Contributing
Thank you for your interest in contributing! This guide covers development workflow, coding standards, and our AI-powered development process.
Quick Links
Development Workflow
Branch Strategy
- Main branch (
main) - Always in a releasable state - Feature branches - Created from
mainfor new features or fixes
Creating a Feature Branch
Use descriptive branch names with the following prefixes. Features and fixes use an issue number (for example, feat/025-semantic-diff, fix/026-template-escape), while other types use descriptive names:
| Prefix | Purpose | Example |
|---|---|---|
feat/ | New features | feat/semantic-diff-firewall-rules |
fix/ | Bug fixes | fix/template-rendering-escape |
docs/ | Documentation changes | docs/update-installation-guide |
refactor/ | Code refactoring | refactor/parser-immutable-models |
chore/ | Maintenance tasks | chore/update-dependencies |
workflow/ | Agent/workflow changes | workflow/add-uat-skill |
website/ | Website changes | website/add-feature-page |
Example workflow:
git checkout main
git pull origin main
git checkout -b feat/your-feature-nameTesting Requirements
All changes must include appropriate tests. The project uses a comprehensive testing strategy:
Test Types
- Unit Tests - Test individual components in isolation
- Integration Tests - Test end-to-end workflows, including Docker-based tests
- Invariant Tests - Property-based tests that verify markdown invariants
- Snapshot Tests - Golden file tests that detect unexpected output changes
- Template Isolation Tests - Test each template independently
- Fuzz Tests - Test with edge-case inputs (special characters, Unicode)
- Markdownlint Integration - Docker-based linting with markdownlint-cli2
Running Tests
# Run all tests
dotnet test
# Run specific test categories
dotnet test --filter "FullyQualifiedName~MarkdownInvariantTests"
dotnet test --filter "FullyQualifiedName~MarkdownLintIntegrationTests"Markdown Quality Requirements
All generated markdown must:
- Pass markdownlint validation (MD012 and other rules)
- Parse correctly with Markdig
- Render correctly on GitHub and Azure DevOps
- Have proper table structure (no blank lines between rows)
- Have proper heading spacing (blank lines before/after)
- Have balanced HTML tags (,
)
Commit Messages
This project uses Conventional Commits to automate versioning and changelog generation.
Format
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]Commit Types
| Type | Description | Version Bump |
|---|---|---|
feat | A new feature | Minor (0.x.0) |
fix | A bug fix | Patch (0.0.x) |
docs | Documentation only | None |
style | Code style changes | None |
refactor | Code refactoring | None |
test | Adding or modifying tests | None |
chore | Other maintenance | None |
Breaking Changes
For breaking changes, add BREAKING CHANGE: in the commit footer or use ! after the type:
feat(parser)!: change output format to JSON
BREAKING CHANGE: The output format has changed from plain text to JSON.Commit Cohesion
Each commit should focus on a single topic or change. If you find yourself using "and" repeatedly in a commit message, consider splitting it into multiple commits.
Examples
# Feature
git commit -m "feat(cli): add --output flag for custom output path"
# Bug fix
git commit -m "fix(parser): handle empty resource changes array"
# Documentation
git commit -m "docs: update installation instructions"
# Breaking change
git commit -m "feat(api)!: rename TerraformPlan to PlanResult"Pull Request Process
- Create a feature branch from
main - Make your changes following the coding guidelines
- Ensure all checks pass:
dotnet format --verify-no-changes dotnet build dotnet test - Push your branch and create a Pull Request
- Wait for review - PR validation will run automatically
- Merge using "Rebase and merge" - This project requires a linear history
PR Requirements
- All CI checks must pass (build, test, format, vulnerability scan)
- Code follows the project's style guidelines (enforced by
.editorconfig) - Commit messages follow Conventional Commits format
Project Directory Structure
Understanding the project layout:
src/- Core library and CLI entry pointtests/- All test projects (unit, integration, snapshot tests)tools/- Helper tools (HtmlRenderer, ScreenshotGenerator, TerraformShowRenderer)docs/- Feature specifications, ADRs, architecture documentationexamples/- Demo Terraform plans and output sampleswebsite/- GitHub Pages website (HTML/CSS)scripts/- Build, test, and workflow automation scripts.github/- GitHub Actions workflows and agent skill definitions
Merge Strategy
This project uses rebase and merge to maintain a linear Git history:
- When merging a PR, use the "Rebase and merge" button
- If conflicts occur, rebase onto main:
git pull --rebase origin main - Force-push your branch:
git push --force-with-lease - Do NOT use "Squash and merge" or "Create a merge commit"
Coding Standards
Access Modifiers
tfplan2md is a standalone CLI tool, not a class library. Use the most restrictive access modifier that works:
- ✅
private- Default for class members - ✅
internal- For cross-assembly visibility within the solution - ⚠️
public- Only for main entry points or when absolutely necessary
Never use public just for testing. Instead, use InternalsVisibleTo to expose internal members to test projects.
Code Comments
All code must be thoroughly documented with XML doc comments:
- All members (public, internal, private) require XML doc comments
- Comments must explain "why" not just "what"
- Use standard XML tags:
<summary>,<param>,<returns>,<remarks> - Reference related features/specifications for traceability
Example
/// <summary>
/// Parses Terraform plan JSON and extracts resource changes.
/// </summary>
/// <param name="planFilePath">Absolute path to the plan JSON file.</param>
/// <returns>Collection of resource changes found in the plan.</returns>
/// <remarks>
/// Uses streaming deserialization for memory efficiency on large files.
/// Related feature: docs/features/008-comprehensive-demo/
/// </remarks>
internal async Task<IReadOnlyList<ResourceChange>> ParseAsync(string planFilePath)
{
// Implementation
}Local Development Setup
Prerequisites
- .NET 10 SDK
- Git
- Docker (for running integration tests)
Getting Started
# Clone the repository
git clone https://github.com/oocx/tfplan2md.git
cd tfplan2md
# Restore tools (including Husky for git hooks)
dotnet tool restore
# Install git hooks
dotnet husky install
# Build and test
dotnet build
dotnet testPre-commit Hooks
This project uses Husky.Net for git hooks:
- pre-commit: Runs dotnet format --verify-no-changes and dotnet build
- commit-msg: Validates commit message follows Conventional Commits format
If your commit is rejected:
- Format issues: Run dotnet format to fix formatting
- Build errors: Fix the build errors before committing
- Commit message: Ensure your message follows the format type: description
AI-Powered Development Workflow
This project was built 100% using GitHub Copilot with a multi-agent AI workflow.
Release Process
Releases are automated via GitHub Actions:
- When commits are pushed to main, the CI workflow runs Versionize
- Versionize only runs when Docker-relevant files changed (runtime code, examples, build config)
- If there are feat:, fix:, or BREAKING CHANGE commits, Versionize:
- Bumps the version in .csproj
- Updates CHANGELOG.md
- Creates a git tag (for example, v0.2.0)
- The tag push triggers the Release workflow which:
- Creates a GitHub Release with changelog notes
- Builds and pushes the Docker image to Docker Hub
Note: Changes to tests, documentation, website, scripts, or GitHub workflows do not trigger releases, as they do not affect the published Docker image.
Questions?
If you have questions, feel free to open an issue for discussion.