diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c557df56..e06818eb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,7 +4,7 @@ updates: directory: "/" schedule: interval: "weekly" - target-branch: "master" + target-branch: "develop" ignore: - dependency-name: "Microsoft.Build.Tasks.Core" versions: ["16.*"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 95bd54cb..526fcaa7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,10 +2,10 @@ name: ci on: push: - branches: [master, pre-release, feature/**, fix/**] + branches: [main, develop] paths-ignore: ['**.md', 'docs/**', 'LICENSE*'] pull_request: - branches: [master, pre-release] + branches: [main, develop] paths-ignore: ['**.md', 'docs/**', 'LICENSE*'] concurrency: @@ -89,10 +89,10 @@ jobs: ConfuserEx.zip Confuser.MSBuild.Tasks/bin/Release/*.nupkg - # Pre-release: on push to pre-release branch - pre-release: + # Dev build: on push to develop branch + dev-release: needs: build - if: github.event_name == 'push' && github.ref == 'refs/heads/pre-release' + if: github.event_name == 'push' && github.ref == 'refs/heads/develop' runs-on: windows-2025 timeout-minutes: 5 permissions: @@ -111,25 +111,24 @@ jobs: run: | $ver = nbgv get-version -v NuGetPackageVersion echo "VERSION=$ver" >> $env:GITHUB_OUTPUT - Write-Host "Pre-release version: $ver" - name: Download artifacts uses: actions/download-artifact@v5 with: name: confuserex-packages - - name: Create or update pre-release + - name: Create or update dev release uses: softprops/action-gh-release@v2 with: - tag_name: pre-release - name: "Pre-release v${{ steps.version.outputs.VERSION }}" + tag_name: dev-latest + name: "Dev build v${{ steps.version.outputs.VERSION }}" prerelease: true make_latest: false body: | - **Pre-release build** — for testing only, not production use. + **Development build** — for testing only, not production use. Version: `${{ steps.version.outputs.VERSION }}` - Branch: `pre-release` + Branch: `develop` Commit: ${{ github.sha }} Download the binaries below to test recent fixes and features before they are included in a stable release. @@ -139,10 +138,10 @@ jobs: ConfuserEx.zip *.nupkg - # Release: only on PR merge to master + # Release: only on PR merge to main release: needs: build - if: github.event_name == 'push' && github.ref == 'refs/heads/master' + if: github.event_name == 'push' && github.ref == 'refs/heads/main' runs-on: windows-2025 timeout-minutes: 5 permissions: @@ -161,7 +160,6 @@ jobs: run: | $ver = nbgv get-version -v NuGetPackageVersion echo "VERSION=$ver" >> $env:GITHUB_OUTPUT - Write-Host "Version: $ver" - name: Configure git identity run: | diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index ef22fb1f..2e0c25f9 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,17 +1,13 @@ name: "CodeQL" on: - push: - branches: [master, release/*] - pull_request: - branches: [master] schedule: - - cron: "26 4 * * 0" + - cron: "0 3 * * 1" # Every Monday 3am UTC + workflow_dispatch: # Manual trigger from Actions tab permissions: contents: read security-events: write - pull-requests: read actions: read jobs: @@ -19,29 +15,42 @@ jobs: name: Analyze runs-on: windows-2025 - strategy: - fail-fast: false - steps: - name: Checkout repository uses: actions/checkout@v5 with: fetch-depth: 0 - submodules: recursive + + - name: Check for recent commits + id: check + shell: pwsh + run: | + $count = (git log --oneline --since="7 days ago" | Measure-Object).Count + echo "has_changes=$($count -gt 0)" >> $env:GITHUB_OUTPUT + Write-Host "Commits in last 7 days: $count" - name: Initialize CodeQL + if: steps.check.outputs.has_changes == 'True' || github.event_name == 'workflow_dispatch' uses: github/codeql-action/init@v3 with: languages: csharp - name: Setup MSBuild + if: steps.check.outputs.has_changes == 'True' || github.event_name == 'workflow_dispatch' uses: microsoft/setup-msbuild@v2 - name: Restore + if: steps.check.outputs.has_changes == 'True' || github.event_name == 'workflow_dispatch' run: msbuild Confuser2.sln -t:Restore -verbosity:minimal - name: Build + if: steps.check.outputs.has_changes == 'True' || github.event_name == 'workflow_dispatch' run: msbuild Confuser2.sln -p:Configuration=Release -verbosity:minimal - name: Perform CodeQL Analysis + if: steps.check.outputs.has_changes == 'True' || github.event_name == 'workflow_dispatch' uses: github/codeql-action/analyze@v3 + + - name: Skip notice + if: steps.check.outputs.has_changes != 'True' && github.event_name != 'workflow_dispatch' + run: echo "No commits in the last 7 days — skipping analysis." diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..60a2b6cc --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,145 @@ +name: lint + +on: + push: + paths-ignore: ['**.md', 'docs/**', 'LICENSE*'] + pull_request: + branches: [main, develop] + paths-ignore: ['**.md', 'docs/**', 'LICENSE*'] + +concurrency: + group: lint-${{ github.ref }} + cancel-in-progress: true + +jobs: + lint: + runs-on: windows-2025 + timeout-minutes: 5 + permissions: + contents: read + pull-requests: write + issues: write + env: + NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages + steps: + - uses: actions/checkout@v5 + + - name: Cache NuGet packages + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}/.nuget/packages + key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.vcxproj') }} + restore-keys: | + ${{ runner.os }}-nuget- + + - name: Restore + run: dotnet restore Confuser2.sln + + - name: Check whitespace + id: whitespace + run: dotnet format whitespace Confuser2.sln --verify-no-changes --verbosity minimal 2>&1 | Tee-Object -Variable wsOutput + shell: pwsh + continue-on-error: true + + - name: Check style + id: style + run: dotnet format style Confuser2.sln --verify-no-changes --severity warn 2>&1 | Tee-Object -Variable styleOutput + shell: pwsh + continue-on-error: true + + - name: Check analyzers + id: analyzers + run: dotnet format analyzers Confuser2.sln --verify-no-changes --severity warn 2>&1 | Tee-Object -Variable analyzerOutput + shell: pwsh + continue-on-error: true + + - name: Build lint report + if: always() + id: report + shell: pwsh + run: | + $ws = '${{ steps.whitespace.outcome }}' + $st = '${{ steps.style.outcome }}' + $an = '${{ steps.analyzers.outcome }}' + $anyFailed = ($ws -eq 'failure') -or ($st -eq 'failure') -or ($an -eq 'failure') + + $md = @() + $md += '## Lint Results' + $md += '' + + if (-not $anyFailed) { + $md += '> :white_check_mark: **All checks passed**' + } else { + $md += '> :warning: **Issues found** — run `dotnet format Confuser2.sln` locally to fix' + } + + $md += '' + $md += '| Check | Result |' + $md += '|-------|--------|' + + $icon = if ($ws -eq 'success') { ':white_check_mark:' } else { ':x:' } + $md += "| Whitespace (indentation, line endings) | $icon |" + + $icon = if ($st -eq 'success') { ':white_check_mark:' } else { ':x:' } + $md += "| Style (IDE rules, naming, var usage) | $icon |" + + $icon = if ($an -eq 'success') { ':white_check_mark:' } else { ':x:' } + $md += "| Analyzers (CA*, RCS* rules) | $icon |" + + # Get specific violations if any failed + if ($anyFailed) { + $md += '' + $md += '
Details (click to expand)' + $md += '' + $md += '```' + # Re-run to capture output + $violations = dotnet format Confuser2.sln --verify-no-changes --verbosity diagnostic 2>&1 | Select-String 'error|warning' | Select-Object -First 30 + foreach ($v in $violations) { $md += $v.ToString().Trim() } + if ($violations.Count -ge 30) { $md += '... (truncated, run dotnet format locally for full list)' } + $md += '```' + $md += '' + $md += '
' + } + + $md -join "`n" | Set-Content -Path 'lint-report.md' -Encoding utf8 + $md -join "`n" >> $env:GITHUB_STEP_SUMMARY + + echo "any_failed=$anyFailed" >> $env:GITHUB_OUTPUT + + - name: Post PR comment + if: always() && github.event_name == 'pull_request' + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: lint-report + path: lint-report.md + + - name: Post issue comment + if: always() && github.event_name == 'push' && github.event.pull_request == null + shell: pwsh + run: | + # Extract issue number from branch name (e.g. 54-nuget-packages → 54) + $branch = "${{ github.ref_name }}" + if ($branch -match '^(\d+)-') { + $issueNumber = $Matches[1] + $marker = '' + $body = $marker + "`n" + (Get-Content 'lint-report.md' -Raw) + + # Find existing lint comment + $comments = gh api "repos/${{ github.repository }}/issues/$issueNumber/comments" --jq '.[] | select(.body | startswith("")) | .id' 2>$null + if ($comments) { + # Update existing + gh api "repos/${{ github.repository }}/issues/comments/$comments" -X PATCH -f body="$body" 2>$null + } else { + # Create new + gh issue comment $issueNumber --repo ${{ github.repository }} --body "$body" 2>$null + } + } + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Fail if issues found + if: always() && steps.report.outputs.any_failed == 'True' + shell: pwsh + run: | + Write-Host "::warning::Lint issues found. Run 'dotnet format Confuser2.sln' locally to fix." + exit 1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fd44def1..728b57c4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,11 +1,8 @@ name: test on: - push: - branches: [master, pre-release, feature/**, fix/**] - paths-ignore: ['**.md', 'docs/**', 'LICENSE*'] pull_request: - branches: [master, pre-release] + branches: [main, develop] paths-ignore: ['**.md', 'docs/**', 'LICENSE*'] concurrency: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 42a023fc..b2ffa38e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,12 +5,37 @@ Contributions of any kind are welcome. For bugfixes and unit tests, you can subm ## Getting Started 1. Fork the repository -2. Create a feature branch from `pre-release`: `git checkout -b feat/my-feature pre-release` +2. Create a branch from `develop` (see naming conventions below) 3. Make your changes and ensure CI passes -4. Open a PR targeting `pre-release` +4. Open a PR targeting `develop` See [README.md](README.md#building-from-source) for build prerequisites. +## Branch Naming Convention + +### With an issue (preferred) + +Use GitHub's built-in branch creation: + +```bash +gh issue develop 42 --checkout +# → creates branch: 42-fix-samba-timeout (auto-sanitized by GitHub) +``` + +### Without an issue + +| Scenario | Pattern | Example | +|----------|---------|---------| +| Feature | `feature/{description}` | `feature/add-symbol-server` | +| Chore | `chore/{description}` | `chore/update-dependencies` | +| Documentation | `docs/{description}` | `docs/add-plugin-guide` | +| Hotfix (urgent) | `hotfix/{description}` | `hotfix/crash-on-startup` | +| Experiment | `experiment/{description}` | `experiment/avalonia-port` | + +**Slug rules:** lowercase, words separated by `-`, no special characters, max ~50 chars. + +All branches target `develop` except `hotfix/` which branches from and merges to `main`. + ## Testing Policy Every PR must maintain or improve test coverage. We use a **ratchet strategy** — coverage only goes up, never down. @@ -104,24 +129,47 @@ dotnet test Tests/Confuser.CLI.Test/Confuser.CLI.Test.csproj -c Release --collec Every PR receives an automatic coverage comment showing per-assembly line and branch coverage. The full HTML drill-down report is downloadable as the `coverage-report` artifact from the test workflow. -## Commit Conventions - -We use [Conventional Commits](https://www.conventionalcommits.org/): +## Commit Format ``` -feat(scope): add new feature -fix(scope): fix a bug -test(scope): add or update tests -refactor(scope): code change that doesn't fix a bug or add a feature -chore(scope): build, CI, dependency updates -docs(scope): documentation changes +: + +[optional body — what changed and why] ``` -Always reference the issue number: `fix(renamer): handle FnPtr types (#6)` +**Types** — full words, matching branch naming: + +| Type | When to use | +|------|-------------| +| `feature` | New feature or capability added | +| `fix` | Bug fix | +| `test` | Adding or updating tests | +| `refactor` | Code change that neither fixes a bug nor adds a feature | +| `chore` | Build config, dependencies, tooling, project setup | +| `docs` | Documentation only changes | +| `hotfix` | Urgent fix applied directly to main | +| `experiment` | Exploratory or throwaway work | + +**Rules:** +- Full words only — no abbreviations (`feature` not `feat`) +- No parenthesized scopes — the description should be clear enough +- Reference issue number at end when applicable: `(#42)` +- Lowercase first word after colon + +**Examples:** + +``` +feature: add symbol server support (#54) +fix: handle locked file exception on folder scan (#42) +test: add cross-framework integration tests +chore: update dependencies +docs: add plugin development guide +refactor: extract base repository to reduce duplication +``` ## Pull Request Process -1. PRs target `pre-release`, not `master` +1. PRs target `develop`, not `main` 2. CI must pass (build + tests + coverage) 3. Coverage must not decrease 4. Only include files that are part of your change — no unrelated modifications diff --git a/README.md b/README.md index 0c2c0814..103864ec 100644 --- a/README.md +++ b/README.md @@ -96,9 +96,9 @@ See the [Issues][issues] section. Please check existing issues before filing a n ## Contributing 1. Fork the repository -2. Create a feature branch from `pre-release` +2. Create a branch from `develop` (see [CONTRIBUTING.md](CONTRIBUTING.md) for naming conventions) 3. Make your changes and ensure CI passes -4. Open a PR targeting `pre-release` +4. Open a PR targeting `develop` ## License @@ -127,6 +127,6 @@ Licensed under the MIT license. See [LICENSE.md][license] for details. [license]: LICENSE.md [project_format]: docs/ProjectFormat.md -[img_ci]: https://github.com/mcpolo99/ConfuserExx/actions/workflows/ci.yml/badge.svg?branch=master +[img_ci]: https://github.com/mcpolo99/ConfuserExx/actions/workflows/ci.yml/badge.svg?branch=main [img_codeql]: https://github.com/mcpolo99/ConfuserExx/actions/workflows/codeql-analysis.yml/badge.svg [img_license]: https://img.shields.io/github/license/mcpolo99/ConfuserExx.svg?style=flat