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..c091603c3 100644 --- a/.github/workflows/dependabot-approve-and-auto-merge.yml +++ b/.github/workflows/dependabot-approve-and-auto-merge.yml @@ -1,33 +1,50 @@ -name: Dependabot Pull Request Approve and Merge -on: pull_request_target -permissions: - pull-requests: write - contents: write +name: Auto-Approve and Auto-Merge Bot PRs +on: + pull_request_target: + types: [opened, synchronize, reopened] + jobs: dependabot: + name: Dependabot (GitHub Actions) + permissions: + contents: write + pull-requests: write 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 }} - 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' }} + 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 }} - GITHUB_TOKEN: ${{ secrets.GH_ACTIONS_PR_WRITE }} + GH_TOKEN: ${{ secrets.GH_ACTIONS_PR_WRITE }} + + renovate: + name: Renovate + permissions: + pull-requests: write + 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 }} 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..de6a4530b --- /dev/null +++ b/docs/dependency-management.md @@ -0,0 +1,119 @@ +# 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.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). + +**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.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 + +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 | +|---------|----------| +| 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. See [`build/targets/tests/Packages.props`](../build/targets/tests/Packages.props) for the full list. + +Representative packages: + +| 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`). + +### 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** (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. + +### 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 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 e3426367f..43e1ba453 100644 --- a/renovate.json +++ b/renovate.json @@ -3,6 +3,13 @@ "extends": [ "config:recommended" ], + "enabledManagers": ["nuget"], + "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,14 +29,33 @@ "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.Formats.Asn1", "System.Reflection.Metadata", "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.", + "matchPackageNames": [ + "BenchmarkDotNet", + "Perfolizer" + ], + "groupName": "benchmark-tooling", + "automerge": false, + "addLabels": ["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