From a03ad1252413a596dc2c097f6cf269bea222c075 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Wed, 13 May 2026 13:01:30 -0700 Subject: [PATCH] Fix pre-release badges (#83) and develop ruleset auto-merge stall (#82) (#84) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Closes #82 — drop strict "Require branches to be up to date before merging" from the `develop` ruleset (already flipped on the live repo, id 15886282). With strict on, two bot PRs against `develop` landing within the same window left the second OPEN forever: the first merge flipped it to `mergeStateStatus: BEHIND`, GitHub's auto-merge will not fire while strict is on, and the merge-bot only enables auto-merge on `opened`/`reopened` (never auto-updates bot branches). Squash mechanics, linear history, textual-conflict detection, and the required `Check pull request workflow status` still gate merges. - Closes #83 — fix the GitHub Pre-Release shield with `&filter=*-g*` (NBGV pre-release tags carry a `-g` suffix, stable tags don't); drop the NuGet Pre-Release badge because shields.io's `vpre` endpoint accepts no filter parameter and silently returns the latest stable whenever one exists (the steady state after every release). - AGENTS.md "Branching Model" + README.md "Rules / Rulesets" updated in lockstep with the ruleset flip; both rulesets now omit the strict flag for related-but-distinct reasons (documented inline). ## Test plan - [ ] Visual: rendered README badge row shows three pre-release-aware badges (GitHub Release, GitHub Pre-Release, NuGet Release) plus Docker Latest/Develop and PyPI Release — `NuGet Pre-Release` is gone. - [ ] Shield URL spot-check against a downstream consumer with current pre-releases: `https://img.shields.io/github/v/release/ptr727/LanguageTags?include_prereleases&filter=*-g*` renders an NBGV pre-release tag (e.g. `1.2.40-g0f69a1b0a0`), not the latest stable. - [ ] `gh api repos/ptr727/ProjectTemplate/rulesets/15886282 | jq '.rules[] | select(.type=="required_status_checks")'` shows `strict_required_status_checks_policy: false` — already verified. - [ ] Next pair of overlapping Dependabot/codegen PRs against `develop` both auto-merge without one stalling in `BEHIND`. --- AGENTS.md | 5 ++++- README.md | 8 +++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 5015e1ca..4f001e4a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,7 +22,10 @@ Treat this file as authoritative for everything else; don't restate its rules el - `develop` → `main` is **merge-commit only** (no squash, no rebase). Merge commits preserve develop's commit list as a real second-parent reference on main, which is what makes the "release on every push" model attribute releases to the develop commits that produced them. Branch protection enforces this: the develop ruleset allows only `squash`, the main ruleset allows only `merge`. - All commits on both branches must be cryptographically signed (SSH or GPG). Squash and merge commits created via the GitHub UI are signed by GitHub's web-flow key. - **`develop` is forward-only — no `main → develop` back-merges.** The develop ruleset's squash-only setting physically blocks merge commits on develop. Historical back-merge commits visible in `git log` (`b9b0447`, `410ba56`, `ffb9e64`, `5ce95cf`, etc.) predate this rule and must not be repeated. -- **Main ruleset intentionally omits "Require branches to be up to date before merging".** This GitHub branch-protection 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. Other technical workarounds — rebasing develop onto main, or rewriting develop's history — exist but contradict the squash-only develop ruleset and the linearity invariant. The develop ruleset keeps the "up to date" check on (it's normal hygiene for feature → develop merges); only the main ruleset omits it. See [`README.md`](./README.md#template---github-setup) "Rules / Rulesets" for the configured state. +- **Both rulesets intentionally omit "Require branches to be up to date before merging".** The flag is off on `main` and on `develop`, for related but 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. Other technical workarounds — rebasing develop onto main, or rewriting develop's history — exist but contradict the squash-only develop ruleset and the linearity invariant. + - *Develop:* the check stalls bot auto-merge when two bot PRs against develop land within the same window. As soon as the first merges, the second flips to `mergeStateStatus: BEHIND` and GitHub's auto-merge will not fire while strict is on. The merge-bot only *enables* auto-merge on `opened`/`reopened` (see below) and never auto-updates bot branches, and Dependabot's rebase isn't real-time, so the second PR sits OPEN with all checks green indefinitely. Squash mechanics still rebase the diff onto develop's tip on merge, `required_linear_history` still enforces linearity, textual conflicts still block `mergeable: CONFLICTING`, and the required `Check pull request workflow status` still gates merges — the only thing lost is pre-merge detection of *semantic-but-not-textual* conflicts, which the post-merge develop CI run catches anyway. + - See [`README.md`](./README.md#template---github-setup) "Rules / Rulesets" for the configured state. - **Bots (Dependabot and codegen) target both `main` and `develop` in parallel.** [`.github/dependabot.yml`](./.github/dependabot.yml) duplicates every ecosystem entry (one per branch) and [`.github/workflows/run-codegen-pull-request-task.yml`](./.github/workflows/run-codegen-pull-request-task.yml) runs as a matrix over both branches with branch names `codegen-main` and `codegen-develop`. 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). 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. - **Maintainer-pushed commits on a bot PR auto-disable auto-merge.** The merge-bot's `merge-dependabot` and `merge-codegen` jobs only fire on `opened` / `reopened` events (auto-merge is enabled exactly once per PR). When a maintainer pushes commits to a bot's branch (a `synchronize` event with an actor that isn't the same bot), the merge-bot's `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 auto-merge manually (`gh pr merge --auto ` or the GitHub UI) when ready. - **Why parallel dual-target rather than develop-only with eventual flow-through:** push-distribution channels (HACS for Home Assistant integrations, Linux distros that vendor from `main`, etc.) consume `main` directly. A develop-only model would leave `main` running stale code during long-running develop features. Codegen content can also be production-critical (live API-derived data, language lists, build catalogs) rather than just sample/demo content, so both branches need fresh codegen on their own cadence. diff --git a/README.md b/README.md index 5f0378e6..5ea67df0 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,6 @@ C# .NET project template. [![Docker Latest][dockerlatestversion-shield]][docker-link]\ [![Docker Develop][dockerdevelopversion-shield]][docker-link]\ [![NuGet Release][nugetreleaseversion-shield]][nuget-link]\ -[![NuGet Pre-Release][nugetprereleaseversion-shield]][nuget-link]\ [![PyPI Release][pypireleaseversion-shield]][pypi-link] ### Release Notes @@ -478,12 +477,12 @@ Licensed under the [MIT License][license-link]\ - `Allow rebase merging` — disabled (no flow uses it; the develop ruleset forbids it anyway) - `Always suggest updating pull request branches` - `Allow auto-merge` -- Rules / Rulesets — **separate rulesets per branch**. Develop and main intentionally diverge on three rules — allowed merge methods, `Require linear history`, and `Require branches to be up to date before merging`; everything else is shared. +- Rules / Rulesets — **separate rulesets per branch**. Develop and main intentionally diverge on two rules — allowed merge methods and `Require linear history`. `Require branches to be up to date before merging` is **off on both** for related-but-distinct reasons (below); everything else is shared. - "Develop": - Target branches: `develop`. - Allowed merge methods: `Squash` - `Require linear history` (develop is kept linear; main carries merge commits by design, so this setting belongs to develop only) - - `Require status checks to pass` → `Require branches to be up to date before merging` ✓ (feature branches must rebase or merge develop before merging back — standard hygiene) + - `Require status checks to pass` → `Require branches to be up to date before merging` **intentionally OFF**. Leaving it on stalls bot auto-merge when two bot PRs against develop land within the same window — the first merge flips the second to `mergeStateStatus: BEHIND`, and GitHub's auto-merge will not fire while strict is on. The merge-bot in [`.github/workflows/merge-bot-pull-request.yml`](./.github/workflows/merge-bot-pull-request.yml) only enables auto-merge on `opened`/`reopened` and never auto-updates bot branches; Dependabot's rebase isn't real-time. With strict off, squash mechanics still rebase the diff onto develop's tip on merge, `Require linear history` still enforces linearity, textual conflicts still block `mergeable: CONFLICTING`, and the required `Check pull request workflow status` still gates merges. See [AGENTS.md "Branching Model"](./AGENTS.md#branching-model) for the full reasoning. - Plus shared settings (below). - "Main": - Target branches: `main`. @@ -558,9 +557,8 @@ For an example of the manual-release model in production, see [homeassistant-pur [license-link]: ./LICENSE [license-shield]: https://img.shields.io/github/license/ptr727/ProjectTemplate?label=License [nuget-link]: https://www.nuget.org/packages/ptr727.ProjectTemplate.Library/ -[nugetprereleaseversion-shield]: https://img.shields.io/nuget/vpre/ptr727.ProjectTemplate.Library?logo=nuget&label=NuGet%20Pre-Release&color=orange [nugetreleaseversion-shield]: https://img.shields.io/nuget/v/ptr727.ProjectTemplate.Library?logo=nuget&label=NuGet%20Release -[prereleaseversion-shield]: https://img.shields.io/github/v/release/ptr727/ProjectTemplate?include_prereleases&label=GitHub%20Pre-Release&logo=github +[prereleaseversion-shield]: https://img.shields.io/github/v/release/ptr727/ProjectTemplate?include_prereleases&filter=*-g*&label=GitHub%20Pre-Release&logo=github [pypi-link]: https://pypi.org/project/ptr727-projecttemplate-library/ [pypireleaseversion-shield]: https://img.shields.io/pypi/v/ptr727-projecttemplate-library?logo=pypi&label=PyPI%20Release [releasebuildstatus-shield]: https://img.shields.io/github/actions/workflow/status/ptr727/ProjectTemplate/publish-release.yml?logo=github&label=Releases%20Build