Repository navigation
fix(ci): defer a trigger until the commit is checkable - #372
Open
aliasfoxkde wants to merge 2 commits into
Open
aliasfoxkde wants to merge 2 commits into
aliasfoxkde wants to merge 2 commits into
Conversation
The trigger endpoint accepted any non-zero new_hash, recorded it, and published a run. When the commit was not in the repository's storage the run's workspace clone failed with `fatal: unable to read tree <sha>` and the run was persisted as failed — a doomed run created for every push. Two trigger paths race for every push: an external relay that learns of the push before the mirror received the objects, and the mirror's own receive path, which arrives later. The relay's trigger was the one that lost the race, so it produced the doomed run while the mirror's trigger produced the real one behind it. The endpoint now probes the repository's storage before it publishes. The probe decides availability the way the workspace clone does: the commit must exist AND be reachable from a branch or tag, because a clone fetches refs/heads/* and refs/tags/* and nothing else. A commit parked under another namespace is present in the object store and still unreachable at checkout, so object existence alone is not enough. A trigger for a commit that is not available yet is recorded durably and deferred: the response is 202 queued with `deferred: true`, and the durable sweep publishes it once the objects arrive. The mirror's trigger for the same commit dedups onto that row, so exactly one run is created. The deferral is bounded — after 30 minutes the row fails visibly with a cause instead of lingering forever. An undecidable probe (no durable store, an unregistered repository, a git failure) does not defer: the probe decides the one failure mode it can decide, and a probe that cannot answer must not silence a trigger that would have worked. Co-Authored-By: Claude Code <noreply@anthropic.com>
The trigger endpoint accepted any non-zero new_hash and answered 202, so a relay trigger that reached the service before the mirror held the objects created a run whose workspace clone failed with 'unable to read tree'. That run was persisted as failed: terminal, visible, and useless. The endpoint now probes the repository's bare storage before publishing. Availability is decided the way the workspace clone decides it: the commit must be reachable from refs/heads/* or refs/tags/*, because the clone fetches those and nothing else. A commit present in the object store but parked under another namespace is still unreachable at checkout. An undecidable probe (no durable store, unregistered repository, git failure) is not treated as unavailable: the probe decides the one failure mode it can decide and must not silence a trigger that would have worked. A trigger for an unavailable commit is recorded durably and answered 202 with 'deferred': true. The durable sweep probes again and publishes once the objects arrive; the mirror's own trigger for the same commit dedups onto the same row, so exactly one run is created. The deferral is bounded at 30 minutes, after which the row fails visibly instead of waiting forever. Regression: test_trigger_for_an_absent_commit_is_deferred_and_creates_no_run asserts the 202/deferred contract, the pending durable row, and that no pipeline run exists for the absent commit. Co-Authored-By: Claude Code <noreply@anthropic.com>
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars.
|
🤖 AI Code Review ResultsAI review skipped: provider 'anthropic' is not configured; deterministic gates remain authoritative. ✅ No critical issues found. AI Code Review powered by GitForge |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The defect
The CI trigger endpoint accepted any non-zero
new_hashand answered202.Two trigger paths race for every push:
.github/workflows/gitforge.yml) POSTs the triggeras soon as GitHub sees the push — before the mirror has received the
objects;
objects present.
Path 1 created a pipeline run whose workspace clone failed with
fatal: unable to read tree <sha>. That run was persisted as failed:terminal, visible, and useless. Path 2 then created a second, real run for
the same commit — so every push produced one doomed run and one good one.
The fix
The endpoint now probes the repository's bare storage before publishing.
Availability is decided the way the workspace clone decides it: the commit
must be reachable from
refs/heads/*orrefs/tags/*, because the clonefetches exactly those namespaces. A commit present in the object store but
parked under another namespace is still unreachable at checkout — object
existence alone is not enough, and the new
a_commit_only_a_non_fetched_namespace_reaches_is_unreachabletest pinsthat distinction.
An undecidable probe (no durable store, unregistered repository,
gititself failing) is not treated as unavailable. The probe decides the one
failure mode it can decide and must never silence a trigger that would
have worked.
A trigger for an unavailable commit is recorded durably and answered
202:{"status":"queued","deferred":true,"reason":"commit_not_available","trigger_id":"..."}The existing durable sweep then probes again and publishes once the objects
arrive. The mirror's own trigger for the same commit dedups onto the same
row, so exactly one run is created. The deferral is bounded at 30
minutes; after that the row fails visibly instead of waiting forever.
No new subsystem: the durable trigger queue and the sweep that already
exist for restart/ring-loss recovery carry the deferral.
Verification
Fedora,
CARGO_TARGET_DIR=~/gitforge-gf14-target:Regressions
test_trigger_for_an_absent_commit_is_deferred_and_creates_no_run202+deferred: true+reason: commit_not_available; the durable row stayspendingwith no run; nopipeline_runsrow existsa_commit_on_a_branch_is_availablerefs/heads/mainis availablean_unknown_commit_is_missinga_commit_only_a_non_fetched_namespace_reaches_is_unreachabletest_trigger_requires_token_and_runs_committed_pipeline🤖 Generated with Claude Code