From 89b7529852bab510b970e394858a2d65975426da Mon Sep 17 00:00:00 2001 From: Richard Murillo <6811113+rjmurillo@users.noreply.github.com> Date: Fri, 20 Feb 2026 17:37:17 -0800 Subject: [PATCH 1/8] chore: consolidate dependency management workflows and documentation - Disable dependabot for NuGet (renovate handles it; dependabot NuGet PRs failed required "Validate PR title" check due to non-conventional commit format, creating unmergeable duplicates of every renovate PR) - Consolidate 4 overlapping auto-approve/merge workflows into 1: removed dependabot-auto-approve.yml, dependabot-auto-merge.yml, auto-approve-and-merge-renovate.yml; kept and expanded dependabot-approve-and-auto-merge.yml with separate jobs for dependabot (GitHub Actions) and renovate (NuGet) - Update renovate.json: ignore Microsoft.CodeAnalysis.* core packages, group BenchmarkDotNet+Perfolizer as benchmark-tooling requiring manual review, disable System.CommandLine updates until PerfDiff rewrite (#914) - Add docs/dependency-management.md documenting package categories, upgrade policies, the VersionOverride pattern, and lessons from #850 Co-Authored-By: Claude Opus 4.6 --- .github/dependabot.yml | 27 ++--- .../auto-approve-and-merge-renovate.yml | 25 ---- .../dependabot-approve-and-auto-merge.yml | 54 ++++++--- .github/workflows/dependabot-auto-approve.yml | 15 --- .github/workflows/dependabot-auto-merge.yml | 22 ---- docs/dependency-management.md | 110 ++++++++++++++++++ renovate.json | 26 ++++- 7 files changed, 179 insertions(+), 100 deletions(-) delete mode 100644 .github/workflows/auto-approve-and-merge-renovate.yml delete mode 100644 .github/workflows/dependabot-auto-approve.yml delete mode 100644 .github/workflows/dependabot-auto-merge.yml create mode 100644 docs/dependency-management.md diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f885c38cf..bfb467025 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -10,23 +10,10 @@ updates: reviewers: - "rjmurillo" - "mattkotsenas" - - package-ecosystem: nuget - directory: "/" - schedule: - interval: "daily" - time: "08:30" - ignore: - # Microsoft.CodeAnalysis.* packages defined in the analyzer project can impact compatibility with older SDKs for - # our users. We don't want to bump these without first considering the user impact. - # - # We don't wildcard Microsoft.CodeAnalysis.* here though, as there are testing libraries and analyzers that - # can be upgraded without impacting our users. - - dependency-name: "Microsoft.CodeAnalysis.CSharp" - - dependency-name: "Microsoft.CodeAnalysis.CSharp.Workspaces" - - dependency-name: "Microsoft.CodeAnalysis.Common" - - dependency-name: "Microsoft.CodeAnalysis.Workspaces.Common" - # Analyzer-shipped BCL packages must match minimum supported SDK host. - # See: https://github.com/rjmurillo/moq.analyzers/issues/850 - - dependency-name: "System.Collections.Immutable" - - dependency-name: "System.Reflection.Metadata" - - dependency-name: "Microsoft.CodeAnalysis.AnalyzerUtilities" + # NuGet dependency management is handled by Renovate (see renovate.json). + # Dependabot NuGet PRs were disabled because: + # 1. PR titles use "Bump X from A to B" format, which fails the required + # "Validate PR title" check (conventional commits required). + # 2. Both bots created duplicate PRs for every package update. + # 3. Renovate supports better grouping, automerge rules, and conventional + # commit formatting out of the box. diff --git a/.github/workflows/auto-approve-and-merge-renovate.yml b/.github/workflows/auto-approve-and-merge-renovate.yml deleted file mode 100644 index 2ecfa01a9..000000000 --- a/.github/workflows/auto-approve-and-merge-renovate.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Auto-Approve and Auto-Merge Renovate PR -on: - pull_request_target: - types: [opened, synchronize, reopened] - -permissions: - contents: write - pull-requests: write - -jobs: - renovate-approve-and-merge: - runs-on: ubuntu-24.04-arm - if: github.actor == 'renovate[bot]' - steps: - - name: Approve Renovate PR - run: gh pr review --approve "$PR_URL" - env: - GH_TOKEN: ${{ secrets.GH_ACTIONS_PR_WRITE }} - PR_URL: ${{ github.event.pull_request.html_url }} - - - name: Enable Auto-Merge and Squash - run: gh pr merge --auto --squash "$PR_URL" - env: - GH_TOKEN: ${{ secrets.GH_ACTIONS_PR_WRITE }} - PR_URL: ${{ github.event.pull_request.html_url }} diff --git a/.github/workflows/dependabot-approve-and-auto-merge.yml b/.github/workflows/dependabot-approve-and-auto-merge.yml index 93ad3008b..f7ab486d8 100644 --- a/.github/workflows/dependabot-approve-and-auto-merge.yml +++ b/.github/workflows/dependabot-approve-and-auto-merge.yml @@ -1,33 +1,53 @@ -name: Dependabot Pull Request Approve and Merge -on: pull_request_target +name: Auto-Approve and Auto-Merge Bot PRs +on: + pull_request_target: + types: [opened, synchronize, reopened] + permissions: - pull-requests: write contents: write + pull-requests: write + jobs: dependabot: + name: Dependabot (GitHub Actions) runs-on: ubuntu-24.04-arm - # Checking the actor will prevent your Action run failing on non-Dependabot - # PRs but also ensures that it only does work for Dependabot PRs. - if: ${{ github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]' }} + if: github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]' steps: - # This first step will fail if there's no metadata and so the approval - # will not occur. - - name: Dependabot metadata + - name: Fetch metadata id: dependabot-metadata uses: dependabot/fetch-metadata@v2.5.0 with: github-token: "${{ secrets.GH_ACTIONS_PR_WRITE }}" - # Here the PR gets approved. - - name: Approve a PR + + - name: Approve PR + run: gh pr review --approve "$PR_URL" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GH_TOKEN: ${{ secrets.GH_ACTIONS_PR_WRITE }} + + - name: Enable auto-merge for non-major updates + if: steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' + run: gh pr merge --auto --squash "$PR_URL" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GH_TOKEN: ${{ secrets.GH_ACTIONS_PR_WRITE }} + + renovate: + name: Renovate (NuGet and GitHub Actions) + runs-on: ubuntu-24.04-arm + if: github.actor == 'renovate[bot]' + steps: + - name: Approve PR run: gh pr review --approve "$PR_URL" env: + GH_TOKEN: ${{ secrets.GH_ACTIONS_PR_WRITE }} PR_URL: ${{ github.event.pull_request.html_url }} - GITHUB_TOKEN: ${{ secrets.GH_ACTIONS_PR_WRITE }} - # Finally, this sets the PR to allow auto-merging for patch and minor - # updates if all checks pass - - name: Enable auto-merge for Dependabot PRs - if: ${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }} + + # Renovate controls which PRs are auto-mergeable via renovate.json + # packageRules. This step enables GitHub's auto-merge so that once + # all required checks pass, the PR merges automatically. + - name: Enable auto-merge run: gh pr merge --auto --squash "$PR_URL" env: + GH_TOKEN: ${{ secrets.GH_ACTIONS_PR_WRITE }} PR_URL: ${{ github.event.pull_request.html_url }} - GITHUB_TOKEN: ${{ secrets.GH_ACTIONS_PR_WRITE }} diff --git a/.github/workflows/dependabot-auto-approve.yml b/.github/workflows/dependabot-auto-approve.yml deleted file mode 100644 index bda237090..000000000 --- a/.github/workflows/dependabot-auto-approve.yml +++ /dev/null @@ -1,15 +0,0 @@ -# This workflow will approve pull requests from dependabot - -name: Auto approve PRs by dependabot or renovate -on: [pull_request_target] - -jobs: - autoapprove: - name: Auto-Approve a PR by dependabot - runs-on: ubuntu-24.04-arm - steps: - - name: Auto approve - uses: cognitedata/auto-approve-dependabot-action@v3.0.1 - if: github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]' || github.actor == 'renovate[bot]' - with: - github-token: ${{ secrets.GH_ACTIONS_PR_WRITE }} diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml deleted file mode 100644 index 961ca03d4..000000000 --- a/.github/workflows/dependabot-auto-merge.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Auto-merge Dependabot or Renovate -on: pull_request - -permissions: - pull-requests: write - contents: write - -jobs: - automerge: - runs-on: ubuntu-24.04-arm - if: ${{ github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]' || github.actor == 'renovate[bot]' }} - steps: - - uses: peter-evans/enable-pull-request-automerge@v3 - with: - token: ${{ secrets.GH_ACTIONS_PR_WRITE }} - pull-request-number: ${{ github.event.pull_request.number }} - merge-method: squash - - name: Auto approve - if: steps.cpr.outputs.pull-request-operation == 'created' - run: gh pr review --approve "${{ steps.cpr.outputs.pull-request-number }}" - env: - GH_TOKEN: ${{ secrets.GH_ACTIONS_PR_WRITE }} diff --git a/docs/dependency-management.md b/docs/dependency-management.md new file mode 100644 index 000000000..efaac30e5 --- /dev/null +++ b/docs/dependency-management.md @@ -0,0 +1,110 @@ +# Dependency Management + +This project uses **Renovate** as the primary dependency update bot. Dependabot is retained only for GitHub Actions updates. + +## Package Categories + +Dependencies fall into distinct categories with different upgrade policies. + +### Shipped (in analyzer nupkg) - EXTREME CAUTION + +These packages are bundled in the analyzer NuGet package and run inside the **user's** compiler host (Visual Studio, dotnet CLI). Version constraints are critical because users may run older SDKs. + +| Package | Central Pin | Constraint | +|---------|------------|------------| +| Microsoft.CodeAnalysis.CSharp | 4.8 | Minimum supported VS/SDK version | +| Microsoft.CodeAnalysis.CSharp.Workspaces | 4.8 | Same as above | +| Microsoft.CodeAnalysis.AnalyzerUtilities | 3.3.4 | Must reference SCI <= 8.0.0.0 | +| System.Collections.Immutable | 8.0.0 | Must not exceed .NET 8 SDK host assembly version | +| System.Reflection.Metadata | (pinned) | Same as SCI | + +**Why this matters:** In v0.4.0, a transitive dependency bump pushed SCI to 10.0.0.0, causing CS8032 assembly load failures for every user on .NET 8 SDK. See [issue #850](https://github.com/rjmurillo/moq.analyzers/issues/850). + +**Upgrade policy:** +- `Microsoft.CodeAnalysis.*` core packages are **ignored** in both Renovate and Dependabot configs. Update manually when raising the minimum supported SDK version. +- `System.Collections.Immutable`, `System.Reflection.Metadata`, and `AnalyzerUtilities` have `automerge: false` and the `analyzer-compat` label. Every update requires manual validation that assembly versions stay within host bounds. +- The `ValidateAnalyzerHostCompatibility` MSBuild target and `AnalyzerAssemblyCompatibilityTests` enforce this at build and test time. + +### Build-time Code Analysis - SAFE + +These run only during builds and are not shipped. Updates do not affect end users. + +| Package | Location | +|---------|----------| +| Meziantou.Analyzer | build/targets/codeanalysis/Packages.props | +| SonarAnalyzer.CSharp | build/targets/codeanalysis/Packages.props | +| Roslynator.Analyzers | build/targets/codeanalysis/Packages.props | +| StyleCop.Analyzers | build/targets/codeanalysis/Packages.props | +| Microsoft.CodeAnalysis.Analyzers | build/targets/codeanalysis/Packages.props | + +**Upgrade policy:** Automerge minor/patch. Major versions may introduce new warnings that break the build (warnings are errors). Review new rules before merging major bumps. + +### Test Framework - SAFE + +Test-only dependencies with no shipped impact. + +| Package | Location | +|---------|----------| +| Verify.Xunit | build/targets/tests/Packages.props | +| xunit | build/targets/tests/Packages.props | +| Microsoft.NET.Test.Sdk | build/targets/tests/Packages.props | +| coverlet.msbuild | build/targets/tests/Packages.props | + +**Upgrade policy:** Automerge minor/patch. CI validates compatibility. + +### Benchmark Tooling - COORDINATED + +BenchmarkDotNet and Perfolizer have intertwined version requirements. BenchmarkDotNet declares a minimum Perfolizer version and a minimum Microsoft.CodeAnalysis.CSharp version. + +| Package | Central Pin | Notes | +|---------|------------|-------| +| BenchmarkDotNet | Directory.Packages.props | Transitive dep on Perfolizer and Roslyn | +| Perfolizer | Directory.Packages.props | Used directly by PerfDiff tool | + +**Upgrade policy:** Grouped in Renovate as `benchmark-tooling` with `automerge: false`. Both packages must be updated together. The benchmark project uses `VersionOverride` for packages whose central pins are constrained by shipped analyzer compatibility (e.g., `System.Collections.Immutable`, `Microsoft.CodeAnalysis.CSharp`). + +### PerfDiff Tool - SPECIAL HANDLING + +The PerfDiff tool (`src/tools/PerfDiff/`) uses System.CommandLine, which had breaking API changes between beta and stable releases. The `IConsole` interface was removed in 2.0.3. + +| Package | Status | +|---------|--------| +| System.CommandLine | **Disabled** in Renovate until PerfDiff is rewritten | +| System.CommandLine.Rendering | **Disabled** (removed in stable release) | + +**Why disabled:** The perf CI check builds PerfDiff on-demand. It is excluded from the normal build/test matrix. Updates that break PerfDiff only surface as `perf` check failures, which are a required status check. + +### Build Infrastructure - MODERATE CAUTION + +| Package | Location | Notes | +|---------|----------|-------| +| Polyfill | build/targets/compiler/Packages.props | Compiler polyfills | +| DotNet.ReproducibleBuilds | build/targets/reproducible/Packages.props | Build reproducibility | +| DotNet.ReproducibleBuilds.Isolated | global.json (msbuild-sdks) | MSBuild SDK isolation | +| Nerdbank.GitVersioning | Directory.Packages.props | Version calculation | + +**Upgrade policy:** Automerge minor/patch for stable versions. ReproducibleBuilds and Isolated should be updated together (same release cadence). + +## Configuration Files + +| File | Purpose | +|------|---------| +| `renovate.json` | Renovate bot configuration (primary dependency bot) | +| `.github/dependabot.yml` | Dependabot configuration (GitHub Actions only) | +| `Directory.Packages.props` | Central package version management | +| `build/targets/*/Packages.props` | Category-specific package versions | + +## VersionOverride Pattern + +Non-shipped projects (benchmarks, PerfDiff) that need higher versions of centrally pinned packages use `VersionOverride` in their `.csproj`: + +```xml + + +``` + +This allows the central pin (8.0.0) to protect shipped analyzer DLLs while letting tools use newer versions. + +## Workflow + +A single consolidated workflow (`.github/workflows/dependabot-approve-and-auto-merge.yml`) handles auto-approval and auto-merge for both Dependabot and Renovate PRs. Renovate's `packageRules` in `renovate.json` control which updates are eligible for automerge. diff --git a/renovate.json b/renovate.json index e3426367f..62314f554 100644 --- a/renovate.json +++ b/renovate.json @@ -3,6 +3,12 @@ "extends": [ "config:recommended" ], + "ignoreDeps": [ + "Microsoft.CodeAnalysis.CSharp", + "Microsoft.CodeAnalysis.CSharp.Workspaces", + "Microsoft.CodeAnalysis.Common", + "Microsoft.CodeAnalysis.Workspaces.Common" + ], "packageRules": [ { "description": "Automerge non-major updates for development dependencies", @@ -22,7 +28,7 @@ "automerge": true }, { - "description": "Analyzer-shipped BCL packages must match minimum supported SDK host. Manual review required.", + "description": "Analyzer-shipped BCL packages must match minimum supported SDK host. Manual review required. See: https://github.com/rjmurillo/moq.analyzers/issues/850", "matchPackageNames": [ "System.Collections.Immutable", "System.Reflection.Metadata", @@ -30,6 +36,24 @@ ], "automerge": false, "labels": ["analyzer-compat"] + }, + { + "description": "Benchmark tooling (BenchmarkDotNet, Perfolizer) requires coordinated updates due to transitive dependency constraints. See docs/dependency-management.md.", + "matchPackageNames": [ + "BenchmarkDotNet", + "Perfolizer" + ], + "groupName": "benchmark-tooling", + "automerge": false, + "labels": ["benchmark-tooling"] + }, + { + "description": "System.CommandLine requires PerfDiff code changes when updated. See: https://github.com/rjmurillo/moq.analyzers/issues/914", + "matchPackageNames": [ + "System.CommandLine", + "System.CommandLine.Rendering" + ], + "enabled": false } ], "platformAutomerge": true From a87cbf0f07de4da7fc6f986bd5c5c9ebbac6f2b1 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 21 Feb 2026 01:48:47 +0000 Subject: [PATCH 2/8] fix(ci): prevent auto-merge for Renovate PRs requiring manual review Add safeguards to the Renovate job in the auto-merge workflow to skip auto-merge for PRs that require manual review: - analyzer-compat packages (CS8032 risk, see issue #850) - benchmark-tooling packages (coordinated updates required) - major version updates (parity with Dependabot job) The workflow now fetches PR labels and conditionally enables auto-merge only for PRs without these labels. Also adds a packageRule in renovate.json to label major updates for workflow detection. --- .../dependabot-approve-and-auto-merge.yml | 21 ++++++++++++++++--- renovate.json | 5 +++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dependabot-approve-and-auto-merge.yml b/.github/workflows/dependabot-approve-and-auto-merge.yml index f7ab486d8..6b21881af 100644 --- a/.github/workflows/dependabot-approve-and-auto-merge.yml +++ b/.github/workflows/dependabot-approve-and-auto-merge.yml @@ -43,10 +43,25 @@ jobs: GH_TOKEN: ${{ secrets.GH_ACTIONS_PR_WRITE }} PR_URL: ${{ github.event.pull_request.html_url }} - # Renovate controls which PRs are auto-mergeable via renovate.json - # packageRules. This step enables GitHub's auto-merge so that once - # all required checks pass, the PR merges automatically. + - name: Get PR labels + id: pr-labels + run: | + labels=$(gh pr view "$PR_URL" --json labels --jq '.labels[].name' | tr '\n' ',') + echo "labels=$labels" >> "$GITHUB_OUTPUT" + env: + GH_TOKEN: ${{ secrets.GH_ACTIONS_PR_WRITE }} + PR_URL: ${{ github.event.pull_request.html_url }} + + # Enable auto-merge only for PRs that Renovate has marked as auto-mergeable. + # Skip auto-merge for: + # - analyzer-compat packages (CS8032 risk, see issue #850) + # - benchmark-tooling packages (coordinated updates required) + # - major version updates (require manual review) - name: Enable auto-merge + if: | + !contains(steps.pr-labels.outputs.labels, 'analyzer-compat') && + !contains(steps.pr-labels.outputs.labels, 'benchmark-tooling') && + !contains(steps.pr-labels.outputs.labels, 'major') run: gh pr merge --auto --squash "$PR_URL" env: GH_TOKEN: ${{ secrets.GH_ACTIONS_PR_WRITE }} diff --git a/renovate.json b/renovate.json index 62314f554..26b0ed468 100644 --- a/renovate.json +++ b/renovate.json @@ -10,6 +10,11 @@ "Microsoft.CodeAnalysis.Workspaces.Common" ], "packageRules": [ + { + "description": "Label major updates for workflow detection", + "matchUpdateTypes": ["major"], + "labels": ["major"] + }, { "description": "Automerge non-major updates for development dependencies", "matchDepTypes": ["devDependencies"], From 6f9a5f149eb40fd07f38cfb1b305f448c2f8278b Mon Sep 17 00:00:00 2001 From: Richard Murillo <6811113+rjmurillo@users.noreply.github.com> Date: Fri, 20 Feb 2026 17:51:59 -0800 Subject: [PATCH 3/8] docs: fix System.Reflection.Metadata description and workflow docs Address review feedback: - System.Reflection.Metadata is transitive, not explicitly pinned - Update workflow section to document label-based auto-merge gating Co-Authored-By: Claude Opus 4.6 --- docs/dependency-management.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/dependency-management.md b/docs/dependency-management.md index efaac30e5..08853a664 100644 --- a/docs/dependency-management.md +++ b/docs/dependency-management.md @@ -16,7 +16,7 @@ These packages are bundled in the analyzer NuGet package and run inside the **us | Microsoft.CodeAnalysis.CSharp.Workspaces | 4.8 | Same as above | | Microsoft.CodeAnalysis.AnalyzerUtilities | 3.3.4 | Must reference SCI <= 8.0.0.0 | | System.Collections.Immutable | 8.0.0 | Must not exceed .NET 8 SDK host assembly version | -| System.Reflection.Metadata | (pinned) | Same as SCI | +| System.Reflection.Metadata | (transitive) | Must not exceed .NET 8 SDK host assembly version | **Why this matters:** In v0.4.0, a transitive dependency bump pushed SCI to 10.0.0.0, causing CS8032 assembly load failures for every user on .NET 8 SDK. See [issue #850](https://github.com/rjmurillo/moq.analyzers/issues/850). @@ -107,4 +107,4 @@ This allows the central pin (8.0.0) to protect shipped analyzer DLLs while letti ## Workflow -A single consolidated workflow (`.github/workflows/dependabot-approve-and-auto-merge.yml`) handles auto-approval and auto-merge for both Dependabot and Renovate PRs. Renovate's `packageRules` in `renovate.json` control which updates are eligible for automerge. +A single consolidated workflow (`.github/workflows/dependabot-approve-and-auto-merge.yml`) handles auto-approval and auto-merge for both Dependabot and Renovate PRs. The workflow skips auto-merge for Renovate PRs labeled `analyzer-compat`, `benchmark-tooling`, or `major`, which Renovate applies based on `packageRules` in `renovate.json`. This ensures packages requiring manual review are never auto-merged. From 3f8144a513099f7fcda1c029adfdb9b622959150 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 21 Feb 2026 02:02:23 +0000 Subject: [PATCH 4/8] fix(renovate): use addLabels for mergeable label accumulation Change labels to addLabels in packageRules so that labels accumulate across multiple matching rules. This fixes the issue where a major update to an analyzer-compat or benchmark-tooling package would lose the 'major' label because Renovate's labels field is non-mergeable. --- renovate.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/renovate.json b/renovate.json index 26b0ed468..5719ed67b 100644 --- a/renovate.json +++ b/renovate.json @@ -13,7 +13,7 @@ { "description": "Label major updates for workflow detection", "matchUpdateTypes": ["major"], - "labels": ["major"] + "addLabels": ["major"] }, { "description": "Automerge non-major updates for development dependencies", @@ -40,7 +40,7 @@ "Microsoft.CodeAnalysis.AnalyzerUtilities" ], "automerge": false, - "labels": ["analyzer-compat"] + "addLabels": ["analyzer-compat"] }, { "description": "Benchmark tooling (BenchmarkDotNet, Perfolizer) requires coordinated updates due to transitive dependency constraints. See docs/dependency-management.md.", @@ -50,7 +50,7 @@ ], "groupName": "benchmark-tooling", "automerge": false, - "labels": ["benchmark-tooling"] + "addLabels": ["benchmark-tooling"] }, { "description": "System.CommandLine requires PerfDiff code changes when updated. See: https://github.com/rjmurillo/moq.analyzers/issues/914", From 39abd70e843c6416c2c24fa64029dbf8469d177e Mon Sep 17 00:00:00 2001 From: Richard Murillo <6811113+rjmurillo@users.noreply.github.com> Date: Fri, 20 Feb 2026 18:11:43 -0800 Subject: [PATCH 5/8] fix: simplify Renovate job to approval-only and fix doc inaccuracies Remove auto-merge logic from the Renovate workflow job. Renovate already handles auto-merge decisions via platformAutomerge and per-package automerge rules in renovate.json. Duplicating this in the workflow created race conditions (labels not yet applied on PR open), silent failure modes (gh pr view failures bypassing safety checks), and substring matching risks on label names. The workflow now only approves Renovate PRs. Packages with automerge: false (analyzer-compat, benchmark-tooling) will not have auto-merge enabled by Renovate, so they require manual merge. Other fixes: - Add enabledManagers: ["nuget"] to prevent duplicate GitHub Actions PRs - Remove major label rule (only needed for removed workflow logic) - Fix false claim about Microsoft.CodeAnalysis.CSharp VersionOverride - Fix "ignored in both Renovate and Dependabot" (Dependabot has no NuGet) - Clarify System.Reflection.Metadata has no central pin - Fix System.CommandLine.Rendering phrasing (folded, not removed) - Add disclaimers to incomplete package lists with links to source Co-Authored-By: Claude Opus 4.6 --- .../dependabot-approve-and-auto-merge.yml | 31 ++++--------------- docs/dependency-management.md | 21 ++++++++----- renovate.json | 6 +--- 3 files changed, 21 insertions(+), 37 deletions(-) diff --git a/.github/workflows/dependabot-approve-and-auto-merge.yml b/.github/workflows/dependabot-approve-and-auto-merge.yml index 6b21881af..b421028cf 100644 --- a/.github/workflows/dependabot-approve-and-auto-merge.yml +++ b/.github/workflows/dependabot-approve-and-auto-merge.yml @@ -33,36 +33,17 @@ jobs: GH_TOKEN: ${{ secrets.GH_ACTIONS_PR_WRITE }} renovate: - name: Renovate (NuGet and GitHub Actions) + name: Renovate runs-on: ubuntu-24.04-arm if: github.actor == 'renovate[bot]' steps: + # Renovate handles auto-merge decisions via platformAutomerge and + # per-package automerge rules in renovate.json. This workflow only + # provides the required approval. Packages with automerge: false + # (analyzer-compat, benchmark-tooling) will not have auto-merge + # enabled by Renovate, so they require manual merge after review. - name: Approve PR run: gh pr review --approve "$PR_URL" env: GH_TOKEN: ${{ secrets.GH_ACTIONS_PR_WRITE }} PR_URL: ${{ github.event.pull_request.html_url }} - - - name: Get PR labels - id: pr-labels - run: | - labels=$(gh pr view "$PR_URL" --json labels --jq '.labels[].name' | tr '\n' ',') - echo "labels=$labels" >> "$GITHUB_OUTPUT" - env: - GH_TOKEN: ${{ secrets.GH_ACTIONS_PR_WRITE }} - PR_URL: ${{ github.event.pull_request.html_url }} - - # Enable auto-merge only for PRs that Renovate has marked as auto-mergeable. - # Skip auto-merge for: - # - analyzer-compat packages (CS8032 risk, see issue #850) - # - benchmark-tooling packages (coordinated updates required) - # - major version updates (require manual review) - - name: Enable auto-merge - if: | - !contains(steps.pr-labels.outputs.labels, 'analyzer-compat') && - !contains(steps.pr-labels.outputs.labels, 'benchmark-tooling') && - !contains(steps.pr-labels.outputs.labels, 'major') - run: gh pr merge --auto --squash "$PR_URL" - env: - GH_TOKEN: ${{ secrets.GH_ACTIONS_PR_WRITE }} - PR_URL: ${{ github.event.pull_request.html_url }} diff --git a/docs/dependency-management.md b/docs/dependency-management.md index 08853a664..1e05887c0 100644 --- a/docs/dependency-management.md +++ b/docs/dependency-management.md @@ -16,18 +16,20 @@ These packages are bundled in the analyzer NuGet package and run inside the **us | Microsoft.CodeAnalysis.CSharp.Workspaces | 4.8 | Same as above | | Microsoft.CodeAnalysis.AnalyzerUtilities | 3.3.4 | Must reference SCI <= 8.0.0.0 | | System.Collections.Immutable | 8.0.0 | Must not exceed .NET 8 SDK host assembly version | -| System.Reflection.Metadata | (transitive) | Must not exceed .NET 8 SDK host assembly version | +| System.Reflection.Metadata | (transitive, no central pin) | Must not exceed .NET 8 SDK host assembly version | **Why this matters:** In v0.4.0, a transitive dependency bump pushed SCI to 10.0.0.0, causing CS8032 assembly load failures for every user on .NET 8 SDK. See [issue #850](https://github.com/rjmurillo/moq.analyzers/issues/850). **Upgrade policy:** -- `Microsoft.CodeAnalysis.*` core packages are **ignored** in both Renovate and Dependabot configs. Update manually when raising the minimum supported SDK version. +- `Microsoft.CodeAnalysis.*` core packages are **ignored** in the Renovate config (`ignoreDeps`). Dependabot does not manage NuGet packages. Update manually when raising the minimum supported SDK version. - `System.Collections.Immutable`, `System.Reflection.Metadata`, and `AnalyzerUtilities` have `automerge: false` and the `analyzer-compat` label. Every update requires manual validation that assembly versions stay within host bounds. - The `ValidateAnalyzerHostCompatibility` MSBuild target and `AnalyzerAssemblyCompatibilityTests` enforce this at build and test time. ### Build-time Code Analysis - SAFE -These run only during builds and are not shipped. Updates do not affect end users. +These run only during builds and are not shipped. Updates do not affect end users. See [`build/targets/codeanalysis/Packages.props`](../build/targets/codeanalysis/Packages.props) for the full list. + +Representative packages: | Package | Location | |---------|----------| @@ -41,7 +43,9 @@ These run only during builds and are not shipped. Updates do not affect end user ### Test Framework - SAFE -Test-only dependencies with no shipped impact. +Test-only dependencies with no shipped impact. See [`build/targets/tests/Packages.props`](../build/targets/tests/Packages.props) for the full list. + +Representative packages: | Package | Location | |---------|----------| @@ -61,7 +65,7 @@ BenchmarkDotNet and Perfolizer have intertwined version requirements. BenchmarkD | BenchmarkDotNet | Directory.Packages.props | Transitive dep on Perfolizer and Roslyn | | Perfolizer | Directory.Packages.props | Used directly by PerfDiff tool | -**Upgrade policy:** Grouped in Renovate as `benchmark-tooling` with `automerge: false`. Both packages must be updated together. The benchmark project uses `VersionOverride` for packages whose central pins are constrained by shipped analyzer compatibility (e.g., `System.Collections.Immutable`, `Microsoft.CodeAnalysis.CSharp`). +**Upgrade policy:** Grouped in Renovate as `benchmark-tooling` with `automerge: false`. Both packages must be updated together. The benchmark project uses `VersionOverride` for packages whose central pins are constrained by shipped analyzer compatibility (e.g., `System.Collections.Immutable`). ### PerfDiff Tool - SPECIAL HANDLING @@ -70,7 +74,7 @@ The PerfDiff tool (`src/tools/PerfDiff/`) uses System.CommandLine, which had bre | Package | Status | |---------|--------| | System.CommandLine | **Disabled** in Renovate until PerfDiff is rewritten | -| System.CommandLine.Rendering | **Disabled** (removed in stable release) | +| System.CommandLine.Rendering | **Disabled** (folded into main package in stable release) | **Why disabled:** The perf CI check builds PerfDiff on-demand. It is excluded from the normal build/test matrix. Updates that break PerfDiff only surface as `perf` check failures, which are a required status check. @@ -107,4 +111,7 @@ This allows the central pin (8.0.0) to protect shipped analyzer DLLs while letti ## Workflow -A single consolidated workflow (`.github/workflows/dependabot-approve-and-auto-merge.yml`) handles auto-approval and auto-merge for both Dependabot and Renovate PRs. The workflow skips auto-merge for Renovate PRs labeled `analyzer-compat`, `benchmark-tooling`, or `major`, which Renovate applies based on `packageRules` in `renovate.json`. This ensures packages requiring manual review are never auto-merged. +A single consolidated workflow (`.github/workflows/dependabot-approve-and-auto-merge.yml`) handles auto-approval for both Dependabot and Renovate PRs. + +- **Dependabot** (GitHub Actions only): The workflow approves the PR and enables auto-merge for non-major updates. Major version gating uses `dependabot/fetch-metadata` output. +- **Renovate** (NuGet): The workflow only approves the PR. Auto-merge is controlled entirely by Renovate via `platformAutomerge: true` and per-package `automerge` rules in `renovate.json`. Packages with `automerge: false` (e.g., `analyzer-compat`, `benchmark-tooling`) require manual merge after review. diff --git a/renovate.json b/renovate.json index 5719ed67b..fa0e6a23a 100644 --- a/renovate.json +++ b/renovate.json @@ -3,6 +3,7 @@ "extends": [ "config:recommended" ], + "enabledManagers": ["nuget"], "ignoreDeps": [ "Microsoft.CodeAnalysis.CSharp", "Microsoft.CodeAnalysis.CSharp.Workspaces", @@ -10,11 +11,6 @@ "Microsoft.CodeAnalysis.Workspaces.Common" ], "packageRules": [ - { - "description": "Label major updates for workflow detection", - "matchUpdateTypes": ["major"], - "addLabels": ["major"] - }, { "description": "Automerge non-major updates for development dependencies", "matchDepTypes": ["devDependencies"], From 2d5e82f7f645cee5730e84e889d098f7749475c9 Mon Sep 17 00:00:00 2001 From: Richard Murillo <6811113+rjmurillo@users.noreply.github.com> Date: Fri, 20 Feb 2026 18:56:27 -0800 Subject: [PATCH 6/8] fix: move permissions to job-level and fix markdown lint - Move workflow permissions from top-level to per-job for least privilege. Renovate job only needs pull-requests: write (no contents: write since it only approves, not merges). - Add blank line before list in docs for markdown lint compliance. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/dependabot-approve-and-auto-merge.yml | 9 +++++---- docs/dependency-management.md | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dependabot-approve-and-auto-merge.yml b/.github/workflows/dependabot-approve-and-auto-merge.yml index b421028cf..c091603c3 100644 --- a/.github/workflows/dependabot-approve-and-auto-merge.yml +++ b/.github/workflows/dependabot-approve-and-auto-merge.yml @@ -3,13 +3,12 @@ on: pull_request_target: types: [opened, synchronize, reopened] -permissions: - contents: write - pull-requests: write - jobs: dependabot: name: Dependabot (GitHub Actions) + permissions: + contents: write + pull-requests: write runs-on: ubuntu-24.04-arm if: github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]' steps: @@ -34,6 +33,8 @@ jobs: renovate: name: Renovate + permissions: + pull-requests: write runs-on: ubuntu-24.04-arm if: github.actor == 'renovate[bot]' steps: diff --git a/docs/dependency-management.md b/docs/dependency-management.md index 1e05887c0..aa263bdbf 100644 --- a/docs/dependency-management.md +++ b/docs/dependency-management.md @@ -21,6 +21,7 @@ These packages are bundled in the analyzer NuGet package and run inside the **us **Why this matters:** In v0.4.0, a transitive dependency bump pushed SCI to 10.0.0.0, causing CS8032 assembly load failures for every user on .NET 8 SDK. See [issue #850](https://github.com/rjmurillo/moq.analyzers/issues/850). **Upgrade policy:** + - `Microsoft.CodeAnalysis.*` core packages are **ignored** in the Renovate config (`ignoreDeps`). Dependabot does not manage NuGet packages. Update manually when raising the minimum supported SDK version. - `System.Collections.Immutable`, `System.Reflection.Metadata`, and `AnalyzerUtilities` have `automerge: false` and the `analyzer-compat` label. Every update requires manual validation that assembly versions stay within host bounds. - The `ValidateAnalyzerHostCompatibility` MSBuild target and `AnalyzerAssemblyCompatibilityTests` enforce this at build and test time. From 187308db2689861b41ce7ac003387f889944b57f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 21 Feb 2026 04:28:20 +0000 Subject: [PATCH 7/8] fix: add System.Formats.Asn1 to analyzer-compat rule System.Formats.Asn1 is in the Transitive pins section of Directory.Packages.props with the same CAUTION comment about flowing into the shipped analyzer DLL. Without this fix, minor/patch updates would auto-merge without review. --- docs/dependency-management.md | 3 ++- renovate.json | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/dependency-management.md b/docs/dependency-management.md index aa263bdbf..bc3c0668e 100644 --- a/docs/dependency-management.md +++ b/docs/dependency-management.md @@ -16,6 +16,7 @@ These packages are bundled in the analyzer NuGet package and run inside the **us | Microsoft.CodeAnalysis.CSharp.Workspaces | 4.8 | Same as above | | Microsoft.CodeAnalysis.AnalyzerUtilities | 3.3.4 | Must reference SCI <= 8.0.0.0 | | System.Collections.Immutable | 8.0.0 | Must not exceed .NET 8 SDK host assembly version | +| System.Formats.Asn1 | 10.0.0 | Must not exceed .NET 8 SDK host assembly version | | System.Reflection.Metadata | (transitive, no central pin) | Must not exceed .NET 8 SDK host assembly version | **Why this matters:** In v0.4.0, a transitive dependency bump pushed SCI to 10.0.0.0, causing CS8032 assembly load failures for every user on .NET 8 SDK. See [issue #850](https://github.com/rjmurillo/moq.analyzers/issues/850). @@ -23,7 +24,7 @@ These packages are bundled in the analyzer NuGet package and run inside the **us **Upgrade policy:** - `Microsoft.CodeAnalysis.*` core packages are **ignored** in the Renovate config (`ignoreDeps`). Dependabot does not manage NuGet packages. Update manually when raising the minimum supported SDK version. -- `System.Collections.Immutable`, `System.Reflection.Metadata`, and `AnalyzerUtilities` have `automerge: false` and the `analyzer-compat` label. Every update requires manual validation that assembly versions stay within host bounds. +- `System.Collections.Immutable`, `System.Formats.Asn1`, `System.Reflection.Metadata`, and `AnalyzerUtilities` have `automerge: false` and the `analyzer-compat` label. Every update requires manual validation that assembly versions stay within host bounds. - The `ValidateAnalyzerHostCompatibility` MSBuild target and `AnalyzerAssemblyCompatibilityTests` enforce this at build and test time. ### Build-time Code Analysis - SAFE diff --git a/renovate.json b/renovate.json index fa0e6a23a..43e1ba453 100644 --- a/renovate.json +++ b/renovate.json @@ -32,6 +32,7 @@ "description": "Analyzer-shipped BCL packages must match minimum supported SDK host. Manual review required. See: https://github.com/rjmurillo/moq.analyzers/issues/850", "matchPackageNames": [ "System.Collections.Immutable", + "System.Formats.Asn1", "System.Reflection.Metadata", "Microsoft.CodeAnalysis.AnalyzerUtilities" ], From 57f629cabc26dd947a521ecd42c541ba0fe5ee15 Mon Sep 17 00:00:00 2001 From: Richard Murillo <6811113+rjmurillo@users.noreply.github.com> Date: Fri, 20 Feb 2026 20:45:44 -0800 Subject: [PATCH 8/8] docs: fix System.Formats.Asn1 constraint description The previous wording said "Must not exceed .NET 8 SDK host assembly version" but the current pin is 10.0.0, which contradicts that constraint. Updated to accurately reflect it is flagged for host compat review as a transitive pin. Co-Authored-By: Claude Opus 4.6 --- docs/dependency-management.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dependency-management.md b/docs/dependency-management.md index bc3c0668e..de6a4530b 100644 --- a/docs/dependency-management.md +++ b/docs/dependency-management.md @@ -16,7 +16,7 @@ These packages are bundled in the analyzer NuGet package and run inside the **us | Microsoft.CodeAnalysis.CSharp.Workspaces | 4.8 | Same as above | | Microsoft.CodeAnalysis.AnalyzerUtilities | 3.3.4 | Must reference SCI <= 8.0.0.0 | | System.Collections.Immutable | 8.0.0 | Must not exceed .NET 8 SDK host assembly version | -| System.Formats.Asn1 | 10.0.0 | Must not exceed .NET 8 SDK host assembly version | +| System.Formats.Asn1 | 10.0.0 | Transitive pin in shipped section; flagged for host compat review | | System.Reflection.Metadata | (transitive, no central pin) | Must not exceed .NET 8 SDK host assembly version | **Why this matters:** In v0.4.0, a transitive dependency bump pushed SCI to 10.0.0.0, causing CS8032 assembly load failures for every user on .NET 8 SDK. See [issue #850](https://github.com/rjmurillo/moq.analyzers/issues/850).