mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-01 06:24:41 +01:00
* feat(ship-policy): changelog_fragments key; restamp folds fragments; tree receipt accepts the fold Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(ship-queue): land, the per-PR merge step; Version Gate enforces fragments Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(ship-queue): land --repair after drift; hook base sha; docs and CHANGELOG Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: timeout on the land gh shim's git spawn Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(ship): /ship writes a CHANGELOG fragment under changelog_fragments; land checks the depth-checked receipt (#3567) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(land): keeps the base's four-part package.json (#3557) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(ship-queue): land waits for CI through runScriptSync (script-invocation tripwire) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: land, fragment and Version Gate cases from /ship's coverage audit Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(windows): leave test/ship-queue-land.test.ts out of the Windows-safe subset (gh shim cannot launch there) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(ship-policy): rollback lands open fragment PRs first; restamp folds only with the key on Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(land): close pre-landing review findings land without --no-wait now compares the pushed branch head and polls the stamp commit's checks, refuses a PR whose base is not the stamped base, releases the lock on a pre-push failure, keeps --break-lock off drift locks, resumes from a lost journal via the stamp commit's pin, honours history merge-only, and refuses a fragment whose text is already released on the base. Version Gate stops applying open claims to a fragments stamp. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(land): close adversarial and outside-voice findings land reconciles a merge GitHub completed behind an error reply, refuses --break-lock against a holder that just entered a push or a merge, keeps a repair takeover on the lock itself (any checkout can land the repair PR), resumes --repair after a failed PR create, releases the lock on QUEUE_LAND_BASE_BUSY, never writes through a symlinked release file, keeps a package.json version source's other edits, and refuses an empty object id instead of pushing a deletion. A carried-back fragment is matched on whole released lines. Under changelog_fragments Version Gate accepts a stamped head only as a [pin, base] merge-step commit with valid fragments. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(land): refuse a pin without a READY receipt; symlinked release files refuse before restamp Under changelog_fragments, gstack-ship-queue ready records a READY receipt (refs/notes/gstack-trains) once its pregate, full_e2e and /ship receipt checks pass, and land refuses a pin without one (fix: run gstack-ship-queue ready --pr N), --dry-run included (GStack Minder ruling, 2026-10-29). The merged tree's release files may not be symlinks, so restamp never writes outside the scratch worktree. --repair adopts an existing branch only when it holds land's repair commit. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(pregate): clear's stamp-only substitution applies the changelog_fragments fold; docs match land and ready gstack-pregate clear and gstack-tree-receipt now classify through one classifyFiles, so under changelog_fragments a receipt for the fragment head clears the stamped head when the stamp is exactly the fold. The ship-policy reference names ready's receipt push, land's own check polling, the key-off refusal and the /ship changelog_fragments line. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(pregate, ship-policy): the fragment fold's two receipt rules, and clear applies them too Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * ship: write the Steps 12-16 range with an ASCII hyphen so the skeleton fits maxSkeletonBytes Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
96 lines
3.8 KiB
YAML
96 lines
3.8 KiB
YAML
name: Version Gate
|
|
|
|
# Every PR: under changelog_fragments a PR that touches no release file must
|
|
# still prove it carries a fragment (docs/ship-policy.md#changelog-fragments).
|
|
on:
|
|
pull_request:
|
|
branches: [main]
|
|
|
|
concurrency:
|
|
group: version-gate-${{ github.event.pull_request.number }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
check:
|
|
name: Check VERSION is not stale vs queue
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 10
|
|
permissions:
|
|
contents: read
|
|
pull-requests: read
|
|
steps:
|
|
- name: Checkout PR head
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
with:
|
|
fetch-depth: 0
|
|
ref: ${{ github.event.pull_request.head.sha }}
|
|
# The head's release_tool runs inside the gate (lib/version-gate-train.ts);
|
|
# no token may sit in the checkout it stages. The base fetch below is public.
|
|
persist-credentials: false
|
|
|
|
# The gate's own code comes from the PR's merge commit (github.sha): a PR
|
|
# that edits the gate runs its edit, and an open PR picks up a gate fix
|
|
# on main at its next pull_request event (reopen or push), not on a re-run.
|
|
- name: Checkout gate code (merge commit)
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
with:
|
|
ref: ${{ github.sha }}
|
|
path: .version-gate
|
|
persist-credentials: false
|
|
|
|
- name: Setup Bun
|
|
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
|
with:
|
|
bun-version: 1.4.2
|
|
|
|
- name: Read versions
|
|
id: versions
|
|
run: |
|
|
set -euo pipefail
|
|
PR_VERSION=$(cat VERSION | tr -d '[:space:]')
|
|
BASE_REF="${{ github.event.pull_request.base.ref }}"
|
|
git fetch origin "$BASE_REF" --quiet || true
|
|
BASE_VERSION=$(git show "origin/$BASE_REF:VERSION" 2>/dev/null | tr -d '[:space:]' || echo "0.0.0.0")
|
|
{
|
|
echo "pr_version=$PR_VERSION"
|
|
echo "base_version=$BASE_VERSION"
|
|
echo "base_ref=$BASE_REF"
|
|
} >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Detect bump level
|
|
id: bump
|
|
run: |
|
|
LEVEL=$(bun run scripts/detect-bump.ts "${{ steps.versions.outputs.base_version }}" "${{ steps.versions.outputs.pr_version }}")
|
|
echo "level=$LEVEL" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Query queue (util) — fail-open on error
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
set +e
|
|
bun run bin/gstack-next-version \
|
|
--base "${{ steps.versions.outputs.base_ref }}" \
|
|
--bump "${{ steps.bump.outputs.level }}" \
|
|
--current-version "${{ steps.versions.outputs.base_version }}" \
|
|
--workspace-root null \
|
|
--exclude-pr "${{ github.event.pull_request.number }}" \
|
|
> next.json 2> next.err
|
|
RC=$?
|
|
if [ "$RC" != "0" ] || [ ! -s next.json ]; then
|
|
echo '{"offline":true}' > next.json
|
|
echo "::warning::util exit=$RC — failing open. stderr:"
|
|
cat next.err || true
|
|
fi
|
|
|
|
- name: Compare PR VERSION to next free slot
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
PR_VERSION: ${{ steps.versions.outputs.pr_version }}
|
|
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
|
|
BASE_REF: ${{ steps.versions.outputs.base_ref }}
|
|
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
run: |
|
|
if ! PR_CHANGED_FILES=$(gh api --paginate "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" --jq '.[].filename'); then PR_CHANGED_FILES=""; fi
|
|
export PR_CHANGED_FILES
|
|
bun --no-env-file --config=/dev/null run .version-gate/scripts/compare-pr-version.ts next.json "${{ github.event.pull_request.number }}"
|