Skip to content

fix(release): fetch tags before reachability guard; fix batch rollup (#6916)#6919

Merged
chubes4 merged 1 commit into
mainfrom
fix/release-tagless-checkout-6916
Jun 28, 2026
Merged

fix(release): fetch tags before reachability guard; fix batch rollup (#6916)#6919
chubes4 merged 1 commit into
mainfrom
fix/release-tagless-checkout-6916

Conversation

@chubes4

@chubes4 chubes4 commented Jun 28, 2026

Copy link
Copy Markdown
Member

Closes #6916

Problem

homeboy release <component> refuses with:

Invalid argument 'release-range': Latest release tag <tag> is not reachable from HEAD. Refusing to plan changelog entries

even when the tag genuinely IS reachable from HEAD in the real repo. The release flow computes a release-range (latest tag..HEAD) and requires the latest release tag be an ancestor of HEAD. When the operating checkout was materialized without tags (or shallow), the tag is either absent or its connecting commits are missing, so git merge-base --is-ancestor <tag> HEAD reports false and the guard refuses — fully blocking releases.

Fix 1 — make tags available before the guard (guard correctness preserved)

The reachability/changelog-range guard lives in validate_latest_release_tag_reachable, called from the single chokepoint resolve_tag_and_commits (which also computes the latest tag and the commit range). I added git::fetch_tags() and call it at the entry of resolve_tag_and_commits, before the guard:

  • Fetches tags from the resolved default remote (git fetch --tags <remote>).
  • Unshallows a shallow checkout (git fetch --unshallow --tags) so ancestry can be computed accurately.
  • Best-effort: an offline/no-remote checkout falls through to the guard against whatever local history exists.

The fix makes tags available so the check is accurate — it does not skip or weaken the check. A tag that is genuinely not an ancestor of HEAD is still refused.

Fix 2 — batch summary rollup

run_batch labeled any non-skipped Ok component as released, even when its authoritative per-component status was failed/missing/partial. That produced the misleading {"released": 1, "failed": 1} summary in the issue. The rollup now reads the real result.status via a new batch_status_bucket: only a clean released counts as released; skipped is its own bucket; every other non-skipped outcome (failed/missing/partial/unknown) counts as failed so the summary never overstates success.

Tests

  • resolve_tag_and_commits_fetches_missing_tag_reachable_on_origin — a tagless checkout (cloned --no-tags from a local bare-ish origin) where the tag is reachable on origin now proceeds past the guard (no network: uses a local fixture origin).
  • resolve_tag_and_commits_still_refuses_genuinely_unreachable_tag_after_fetch — an off-branch tag fetched from origin still fails closed (guard correctness preserved).
  • batch_status_bucket_rolls_up_non_released_outcomes_as_failed — failed/missing/partial/unknown roll up as failed, not released.
  • Pre-existing resolve_tag_and_commits_fails_closed_when_latest_release_tag_is_off_branch still passes (fetch_tags is a no-op without a remote).

cargo build clean; cargo test --lib release:: 304 passed single-threaded. (One env-dependent enterprise-proxy test in github_release is flaky under parallelism on both base and branch — unrelated to this change.)

AI assistance

  • AI assistance: Yes
  • Tool(s): Claude Opus 4.8 via Claude Code
  • Used for: Investigating the release guard/materialization flow, implementing the tag-fetch helper and batch rollup fix, and writing regression tests.

The release flow computes a release-range (latest tag..HEAD) and refuses
when the latest release tag is not an ancestor of HEAD. When the operating
checkout was materialized without tags (or shallow), the tag is absent or
its connecting history is missing, so a genuinely-reachable tag is reported
as "not reachable from HEAD" and release is fully blocked (#6916).

Add git::fetch_tags() and call it at the entry of resolve_tag_and_commits,
before the reachability/changelog-range guard runs. It fetches tags from the
resolved default remote and unshallows a shallow checkout so ancestry can be
computed accurately. It is best-effort: an offline checkout still falls
through to the guard against local history, and the guard correctness is
preserved — a tag that is genuinely not an ancestor of HEAD is still refused.

Secondary: the batch summary rolled up any non-skipped Ok component as
"released", even when its authoritative per-component status was failed/
missing/partial, producing a misleading {released, failed} summary. Roll up
the real per-component status via batch_status_bucket so only a clean
"released" counts as released; every other non-skipped outcome counts as
failed.

Tests: a tagless checkout with the tag reachable on origin now proceeds past
the guard; an off-branch tag still fails closed after fetching tags; the
batch bucket maps failed/missing/partial/unknown to failed.

Closes #6916

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@homeboy-ci

homeboy-ci Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Homeboy Results — homeboy

Lint

lint — passed

ℹ️ Full options: homeboy docs commands/lint
ℹ️ Save lint baseline: homeboy lint homeboy --baseline
Deep dive: homeboy lint homeboy --changed-since fd23417

Artifacts and drill-down
  • CI results artifact: homeboy-ci-results-homeboy-lint-homeboy-Linux contains immediate command JSON for this action invocation.
  • Observation artifact: homeboy-observations-homeboy-lint-homeboy-Linux contains exported Homeboy run history for deeper queries.
  • Drill-down: download the observation artifact, then run homeboy runs import <dir>, homeboy runs list, and homeboy runs findings <run-id>.
  • Artifacts are attached to the workflow run: https://github.com/Extra-Chill/homeboy/actions/runs/28329982972

Test

⚠️ test — baseline red

ℹ️ No tests ran — the runner failed before producing results. See raw_output.stderr_tail / raw_output.stdout_tail for the underlying error (bootstrap failure, missing deps, DB connection, etc.).
ℹ️ To run specific tests: homeboy test homeboy -- --filter=TestName
ℹ️ Auto-fix lint issues: homeboy refactor homeboy --from lint --write
ℹ️ Collect coverage: homeboy test homeboy --coverage
ℹ️ Analyze failures: homeboy test homeboy --analyze
ℹ️ Pass args to test runner: homeboy test -- [args]
ℹ️ Full options: homeboy docs commands/test
Deep dive: homeboy test homeboy --changed-since fd23417

Artifacts and drill-down
  • CI results artifact: homeboy-ci-results-homeboy-test-homeboy-Linux contains immediate command JSON for this action invocation.
  • Observation artifact: homeboy-observations-homeboy-test-homeboy-Linux contains exported Homeboy run history for deeper queries.
  • Drill-down: download the observation artifact, then run homeboy runs import <dir>, homeboy runs list, and homeboy runs findings <run-id>.
  • Artifacts are attached to the workflow run: https://github.com/Extra-Chill/homeboy/actions/runs/28329982972
Tooling versions
  • Homeboy CLI: homeboy 0.269.0+2ef12653d00f+57e3e631c
  • Extension: rust from https://github.com/Extra-Chill/homeboy-extensions
  • Extension revision: dfe9fa31
  • Action: unknown@unknown

@chubes4 chubes4 merged commit 6f83aba into main Jun 28, 2026
3 checks passed
@chubes4 chubes4 deleted the fix/release-tagless-checkout-6916 branch June 28, 2026 17:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

homeboy release: release checkout lacks git tags → 'latest release tag not reachable from HEAD' refusal even when the tag IS reachable

1 participant