fix(workflow): hard-coded 'main' at reusable-release.yml:608 breaks non-main default branches #370

Closed
opened 2026-07-04 09:04:33 +02:00 by bosun · 1 comment
Owner

Finding

External cold-read round 3 caught: .forgejo/workflows/reusable-release.yml:608 hard-codes "main" in the gamma manifest PR path. Elsewhere in the same file, base branch is derived carefully.

Impact

Adopters whose default branch is not main (master, trunk, develop, custom names) will silently hit this hard-coded value. The manifest PR will be created against main which either doesn't exist (fail loud) or exists with unrelated content (silent-corruption class).

Scope (widened per operator ratify 2026-07-04)

Not just a hard-coded-value fix — implement proper layered default-branch resolution with config override support.

Resolution order (implement all layers with fallback)

  1. Config override — adopter-explicit (top priority):

    • .release-toolkit.yml default_branch: field
    • OR env var RT_DEFAULT_BRANCH (from repo variables or workflow env)
    • Use case: multi-repo consumers with inconsistent defaults, adopters testing on feature branches, forks
  2. Forgejo Actions native context:

    • ${{ github.event.repository.default_branch }} in workflow YAML
    • Passed as env var to scripts
    • Zero API cost, works reliably in workflow context
  3. Forgejo API fallback:

    • GET /repos/{owner}/{repo} returns authoritative default_branch
    • Needed for local script execution outside workflow context (dry-runs)
    • Requires token
  4. Git ref fallback:

    • git symbolic-ref refs/remotes/origin/HEAD extracts ref name
    • Best-effort, works if actions/checkout preserves it
    • No dependencies
  5. Fail loud if none resolve:

    • Actionable error message naming all 4 fallback layers + how to set them

Two config override forms to support

  • Repo variable form (in Forgejo settings) → vars.RT_DEFAULT_BRANCH in workflow YAML → env to script. More discoverable, shows in workflow_dispatch UI.
  • .release-toolkit.yml default_branch: field. More version-controlled, change tracked in git.

Both should work; adopters pick per their operational preference.

Grep sweep

  • Find + fix all hard-coded 'main' references in workflow YAML + scripts (not just reusable-release.yml:608)
  • Regression test: adopter with default-branch=trunk successfully creates manifest PR
  • Regression test: config override actually overrides forgejo-context

Verification AC

  • No hard-coded 'main' outside explicit test-fixtures
  • Layered resolution logic works: config-override > forgejo-context > API > git-ref > fail-loud
  • Documented in integration.md with example default_branch: config
  • Regression coverage for non-main default branch (both via API-detection AND via config-override)
  • External cold-read round 4 finds no hard-coded-main or default-branch objection

Anchor

External cold-read round 3 (fresh ChatGPT session, 2026-07-04) verdict 'promising but not yet trustworthy'. Reviewer verified via execution. New findings surfaced despite round-2 delta closures. Operator ratified delta plan 2026-07-04. Scope widened per operator directive 2026-07-04 (design discussion in Bosun-operator exchange).

BLOCKING v1.0.0.

## Finding External cold-read round 3 caught: `.forgejo/workflows/reusable-release.yml:608` hard-codes `"main"` in the gamma manifest PR path. Elsewhere in the same file, base branch is derived carefully. ## Impact Adopters whose default branch is not `main` (master, trunk, develop, custom names) will silently hit this hard-coded value. The manifest PR will be created against `main` which either doesn't exist (fail loud) or exists with unrelated content (silent-corruption class). ## Scope (widened per operator ratify 2026-07-04) Not just a hard-coded-value fix — implement proper layered default-branch resolution with config override support. ### Resolution order (implement all layers with fallback) 1. **Config override — adopter-explicit** (top priority): - `.release-toolkit.yml` `default_branch:` field - OR env var `RT_DEFAULT_BRANCH` (from repo variables or workflow env) - Use case: multi-repo consumers with inconsistent defaults, adopters testing on feature branches, forks 2. **Forgejo Actions native context**: - `${{ github.event.repository.default_branch }}` in workflow YAML - Passed as env var to scripts - Zero API cost, works reliably in workflow context 3. **Forgejo API fallback**: - `GET /repos/{owner}/{repo}` returns authoritative `default_branch` - Needed for local script execution outside workflow context (dry-runs) - Requires token 4. **Git ref fallback**: - `git symbolic-ref refs/remotes/origin/HEAD` extracts ref name - Best-effort, works if actions/checkout preserves it - No dependencies 5. **Fail loud** if none resolve: - Actionable error message naming all 4 fallback layers + how to set them ### Two config override forms to support - **Repo variable form** (in Forgejo settings) → `vars.RT_DEFAULT_BRANCH` in workflow YAML → env to script. More discoverable, shows in workflow_dispatch UI. - **.release-toolkit.yml `default_branch:` field**. More version-controlled, change tracked in git. Both should work; adopters pick per their operational preference. ### Grep sweep - Find + fix all hard-coded 'main' references in workflow YAML + scripts (not just reusable-release.yml:608) - Regression test: adopter with default-branch=trunk successfully creates manifest PR - Regression test: config override actually overrides forgejo-context ## Verification AC - No hard-coded 'main' outside explicit test-fixtures - Layered resolution logic works: config-override > forgejo-context > API > git-ref > fail-loud - Documented in integration.md with example `default_branch:` config - Regression coverage for non-main default branch (both via API-detection AND via config-override) - External cold-read round 4 finds no hard-coded-main or default-branch objection ## Anchor External cold-read round 3 (fresh ChatGPT session, 2026-07-04) verdict 'promising but not yet trustworthy'. Reviewer verified via execution. New findings surfaced despite round-2 delta closures. Operator ratified delta plan 2026-07-04. Scope widened per operator directive 2026-07-04 (design discussion in Bosun-operator exchange). BLOCKING v1.0.0.

Delta merged via PR #378. Widened per operator ratify: layered 6-layer default-branch resolution (RT_DEFAULT_BRANCH env → release-toolkit.yml default_branch:TOOLKIT_DEFAULT_BRANCH_HINT workflow context → Forgejo API → git-ref symref → fail-loud with actionable error). Adopters on non-main default branches (trunk/master/develop/custom) no longer hit the silent-corruption class at the manifest follow-up PR path. Includes Surveyor 9a16's must-fix env-var-on-correct-step wiring + config_get_default_branch delegation via declare -F (with mutation-verifiable shim test) + layer-4 docstring correction. Full docs in docs/integration.md § Default branch (#370).

Delta merged via [PR #378](https://git.frankenbit.de/frankenbit/release-toolkit/pulls/378). Widened per operator ratify: layered 6-layer default-branch resolution (RT_DEFAULT_BRANCH env → `release-toolkit.yml` `default_branch:` → `TOOLKIT_DEFAULT_BRANCH_HINT` workflow context → Forgejo API → git-ref symref → fail-loud with actionable error). Adopters on non-`main` default branches (trunk/master/develop/custom) no longer hit the silent-corruption class at the manifest follow-up PR path. Includes Surveyor 9a16's must-fix env-var-on-correct-step wiring + config_get_default_branch delegation via `declare -F` (with mutation-verifiable shim test) + layer-4 docstring correction. Full docs in `docs/integration.md § Default branch (#370)`.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
frankenbit/release-toolkit#370
No description provided.