Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 2 additions & 11 deletions .github/workflows/merge-bot-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ jobs:
merge-dependabot:
name: Merge dependabot pull request job
runs-on: ubuntu-latest
# Dependabot PRs from this repo (not forks). Only on opened/reopened so the disable job stays sticky.
# In-repo Dependabot PRs only, on opened/reopened so the disable job stays sticky. Every tier
# auto-merges, semver-major included: the required checks are the gate, not the version bump.
if: >-
(github.event.action == 'opened' || github.event.action == 'reopened') &&
github.event.pull_request.user.login == 'dependabot[bot]' &&
Expand All @@ -41,17 +42,7 @@ jobs:
client-id: ${{ secrets.CODEGEN_APP_CLIENT_ID }}
private-key: ${{ secrets.CODEGEN_APP_PRIVATE_KEY }}

- name: Get dependabot metadata step
id: metadata
uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"

# Skip semver-major NuGet bumps so they land via human review; other ecosystems' majors auto-merge.
- name: Merge pull request step
if: >-
(steps.metadata.outputs.package-ecosystem != 'nuget') ||
(steps.metadata.outputs.update-type != 'version-update:semver-major')
run: |
set -euo pipefail
case "${{ github.event.pull_request.base.ref }}" in
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ This file is the canonical reference for cross-cutting AI-agent rules. The CI/CD
- **Both rulesets intentionally omit "Require branches to be up to date before merging" (`strict_required_status_checks_policy: false`), for two distinct reasons:**
- *Main* - the check is graph-based; it asks whether main's tip commit is reachable from develop, not whether the two branches have the same content. After any develop -> main release, main's tip is a brand-new merge commit that develop's history doesn't contain. Forward-only develop never adds it (no back-merge of main into develop), so the check would fail on every subsequent release.
- *Develop* - bot auto-merge incompatibility. When two bot PRs against develop land in the same minute (e.g. two grouped Dependabot PRs from the same daily run), the first to merge pushes the second into `mergeStateStatus: BEHIND`. GitHub's auto-merge will not fire while the strict flag is on, and nothing in the workflow set auto-updates a bot branch in that window - the merge-bot enables auto-merge via `gh pr merge --auto` but never rebases a stalled branch onto base (see [`merge-bot-pull-request.yml`](./.github/workflows/merge-bot-pull-request.yml)). Real file-level conflicts are still caught textually (`mergeable: CONFLICTING` blocks merge regardless); semantic-but-not-textual conflicts that combine cleanly are caught by the post-merge develop CI run rather than pre-merge. Do not reintroduce the strict flag on develop thinking it's hygiene - it breaks bot auto-merge.
- **Dependabot targets both `main` and `develop` in parallel.** [`.github/dependabot.yml`](./.github/dependabot.yml) duplicates every ecosystem entry (one per branch). Each branch absorbs its own bot PRs independently, so neither falls behind, and the forward-only rule still holds (nothing is back-merged from main to develop - both branches receive their updates directly). Parallel auto-merge across same-batch bot PRs is race-proof only because both rulesets have the strict "up to date" flag off (see bullet above). The merge-bot ([`.github/workflows/merge-bot-pull-request.yml`](./.github/workflows/merge-bot-pull-request.yml)) dispatches `--squash` or `--merge` from each PR's base ref via a `case` statement so the form matches the ruleset on either base. Dependabot **security** PRs (CVE-driven) always open against the repo default branch (`main`) regardless of `target-branch` - the same `case` statement covers them. Semver-major NuGet bumps gate on human review; everything else auto-merges.
- **Dependabot targets both `main` and `develop` in parallel.** [`.github/dependabot.yml`](./.github/dependabot.yml) duplicates every ecosystem entry (one per branch). Each branch absorbs its own bot PRs independently, so neither falls behind, and the forward-only rule still holds (nothing is back-merged from main to develop - both branches receive their updates directly). Parallel auto-merge across same-batch bot PRs is race-proof only because both rulesets have the strict "up to date" flag off (see bullet above). The merge-bot ([`.github/workflows/merge-bot-pull-request.yml`](./.github/workflows/merge-bot-pull-request.yml)) dispatches `--squash` or `--merge` from each PR's base ref via a `case` statement so the form matches the ruleset on either base. Dependabot **security** PRs (CVE-driven) always open against the repo default branch (`main`) regardless of `target-branch` - the same `case` statement covers them. Every tier auto-merges, semver-major included - the required checks are the gate, not the version bump.
- **Maintainer-pushed commits on a bot PR auto-disable auto-merge.** The merge-bot's `merge-dependabot` job only fires on `opened` / `reopened` events (auto-merge is enabled exactly once per PR, for Dependabot-authored PRs that originate from this repository, not forks). When a maintainer pushes commits to the bot's branch (a `synchronize` event with a non-bot actor), the `disable-auto-merge-on-maintainer-push` job fires and calls `gh pr merge --disable-auto`; the maintainer's commits stay in the PR but won't auto-merge with the bot's content. Re-enable manually (`gh pr merge --auto <PR>`) when ready. The merge-bot is on `pull_request_target` with per-PR concurrency; it carries only `merge-dependabot` + `disable-auto-merge-on-maintainer-push`.
- **App-token workflows use Client ID, not App ID.** `actions/create-github-app-token` deprecated the numeric `app-id` input in v3.0.0; the merge-bot uses `client-id: ${{ secrets.CODEGEN_APP_CLIENT_ID }}` (with `private-key: ${{ secrets.CODEGEN_APP_PRIVATE_KEY }}`). The App token - not `GITHUB_TOKEN` - is required so the merge push is committed by the App and fires downstream workflows (`GITHUB_TOKEN` pushes are blocked from triggering further runs by GitHub's recursion guard). When adding new App-token call sites, use the same form - do not reintroduce `app-id`.
- **Why parallel dual-target rather than develop-only with eventual flow-through:** consumers pull the Docker image and the release executables from `main` directly. A develop-only model would leave `main` running stale code during long-running develop features, so both branches receive their own bot updates on their own cadence and each stays current.
Expand Down
29 changes: 13 additions & 16 deletions WORKFLOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,10 @@ run's artifact set is never blanket-deleted.

### Self-sufficiency: automatic updates

Every Dependabot pull request, any ecosystem and any tier, auto-merges once the required checks pass (the
checks are the safety net), except a **semver-major NuGet** bump, which waits for human review. A merged bump
does not itself publish - it ships in the next weekly publish. There is no codegen and no upstream-version
tracker. A person steps in only for a breaking change (a red check) or to dispatch a release.
Every Dependabot pull request, any ecosystem and any tier including **semver-major**, auto-merges once the
required checks pass - the checks are the gate, not the version bump. A merged bump does not itself publish -
it ships in the next weekly publish. There is no codegen and no upstream-version tracker. A person steps in
only for a breaking change (a red check) or to dispatch a release.

### Flow diagrams

Expand Down Expand Up @@ -248,18 +248,15 @@ flowchart TD
```

**Automation - Dependabot + merge-bot.** Dependabot opens in-repo bot PRs; the merge-bot enables auto-merge
on open (squash on `develop`, merge on `main`), excepting semver-major NuGet, and disables it on a
maintainer push. There is no codegen here; a merged bump does not publish - it ships in the next scheduled
run (D8).
on open (squash on `develop`, merge on `main`) for every tier, and disables it on a maintainer push. There
is no codegen here; a merged bump does not publish - it ships in the next scheduled run (D8).

```mermaid
flowchart TD
DEP(["Dependabot opens PR<br/>(in-repo branch, daily)"]):::trig --> MB
subgraph MBT ["merge-bot-pull-request.yml (pull_request_target, key = PR number)"]
MB{"event / author / in-repo?"}:::gate
MB -- "opened/reopened<br/>dependabot[bot]" --> MAJ{"semver-major NuGet?"}:::gate
MAJ -- "yes" --> HUM(["no auto-merge<br/>human review"]):::stop
MAJ -- "no" --> EN["enable auto-merge<br/>--squash develop / --merge main<br/>(App token, --delete-branch)"]
MB -- "opened/reopened<br/>dependabot[bot]<br/>(every tier)" --> EN["enable auto-merge<br/>--squash develop / --merge main<br/>(App token, --delete-branch)"]
MB -- "synchronize by maintainer<br/>on dependabot branch" --> DIS["disable auto-merge<br/>(App token)"]
end
EN --> CK{"required check passes?"}:::gate
Expand Down Expand Up @@ -397,10 +394,10 @@ Each is a **MUST**, stated as input -> output plus the failure it prevents.
checking out its code. Enables auto-merge on `opened`/`reopened`; squash on `develop`, merge-commit on
`main` by the PR's base ref; disables auto-merge when a maintainer pushes to a bot branch. Concurrency keyed
on PR number.
- **D8.2 Dependabot auto-merges on green, semver-major NuGet excepted.** Output: every Dependabot PR
auto-merges once the required checks pass, except a semver-major NuGet bump (human review). A failing check
blocks the merge. A merged bump does **not** itself publish (dependencies are not shipped inputs); it ships
in the next weekly publish.
- **D8.2 Dependabot auto-merges on green, every tier.** Output: every Dependabot PR, any ecosystem and any
tier including semver-major, auto-merges once the required checks pass - the checks are the gate, not the
version bump. A failing check blocks the merge. A merged bump does **not** itself publish (dependencies are
not shipped inputs); it ships in the next weekly publish.
- **D8.3 No codegen / upstream-version automation.** This repo has neither; the merge-bot carries only
`merge-dependabot` + `disable-auto-merge-on-maintainer-push`.

Expand Down Expand Up @@ -453,7 +450,7 @@ Read the workflow files plus `version.json` and assert the fact behind each appl
- **D7:** the publisher group is ref-independent with `cancel-in-progress: false`; the merge-bot keys on PR
number; CI uses the standard group; reusable jobs declare permissions.
- **D8/D9:** the merge-bot runs on `pull_request_target` with the App token, keyed on PR number; Dependabot
auto-merge excepts semver-major NuGet only; no codegen/upstream-version, date-badge, tool-versions,
auto-merge covers every tier; no codegen/upstream-version, date-badge, tool-versions,
docker-readme task, `PUBLISH_ON_MERGE`, or `dorny/paths-filter`; actions SHA-pinned; names/shells/
conditionals per section 2.

Expand All @@ -472,7 +469,7 @@ Read the workflow files plus `version.json` and assert the fact behind each appl
| S9 | merged GitHub-Actions bump | not a shipped input, merges don't publish -> **no release** | D4.1 |
| S10 | PR with a CSharpier / format / markdown / spelling / workflow-YAML violation | the `lint` job fails -> aggregator blocks the merge | D1.3, D1.5 |
| S11 | `version.json` floor bump merged | merges don't publish -> no immediate release; the new floor ships in the next weekly publish | D3.3, D4.1 |
| S12 | Dependabot semver-major NuGet bump | gated on human review -> does not auto-merge; other majors auto-merge on green | D8.2 |
| S12 | Dependabot semver-major bump (any ecosystem) | auto-merges on green like every other tier; the required checks are the gate | D8.2 |
| S13 | `develop` -> `main` promotion (merge commit) | the merge itself does not publish; `main`'s accumulated changes ship in the next weekly run | D4.1, D8.1 |

### 5C. Live probe (where warranted, never publishing)
Expand Down