Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 70 additions & 18 deletions .github/workflows/grammars-repin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,19 @@ name: Grammars repin
# releases, re-vendors the wasm (`pnpm grammars`), rebuilds the reproducible
# bundle (`pnpm build` — web-tree-sitter's JS is inlined into engine.mjs by
# tsup, so its wasm and its bundled runtime move together), then runs the full
# gate (typecheck / test / reproducible-bundle). Only if every gate is green AND
# something actually changed does it update a dedicated pull request. Grammar
# bytes are part of the engine's versioned output: merging the `fix(grammars)`
# commit therefore cuts a patch release and publishes a matching grammar asset,
# instead of silently changing wasm under an existing ENGINE_VERSION.
# gate (typecheck / test / reproducible-bundle / Node 18 smoke — every check
# ci.yml would run on a pull request). Only if every gate is green AND
# something actually changed does it open a pull request, squash-merge it and
# delete the branch, so the repin lands unattended instead of parking a PR
# nobody reviews. Grammar bytes are part of the engine's versioned output:
# merging the `fix(grammars)` commit therefore cuts a patch release and
# publishes a matching grammar asset, instead of silently changing wasm under
# an existing ENGINE_VERSION.
#
# The pull request is a record, not a review gate: every gate below runs on the
# exact tree being merged, in this job, before the merge. That is what makes
# self-merging safe here — waiting for ci.yml on the branch would only re-run
# the same checks, and a GITHUB_TOKEN-created PR does not start them by itself.

on:
workflow_dispatch:
Expand Down Expand Up @@ -84,7 +92,28 @@ jobs:
if: steps.diff.outputs.changed == 'true'
run: pnpm run check:build

- name: Push automation branch and open or refresh the pull request
# Last gate, mirroring ci.yml's `smoke-node18` job: the published
# artifacts are dependency-free and must stay loadable on Node 18. A
# grammar bump moves web-tree-sitter's inlined runtime too, so this is
# exactly the regression that could otherwise auto-merge unnoticed.
# Node is switched in place (no pnpm here — plain `node` only).
- name: Use Node 18 for the smoke gate
if: steps.diff.outputs.changed == 'true'
uses: actions/setup-node@v4
with:
node-version: 18

- name: Gate — Node 18 smoke
if: steps.diff.outputs.changed == 'true'
run: |
node -e 'import("./scripts/engine.mjs").then(m => { if (typeof m.scanRepo !== "function") process.exit(1) })'
node scripts/cli.mjs scan --repo tests/fixtures/mini-repo --out /tmp/codeindex-node18-scan.json
node scripts/cli.mjs search client --repo tests/fixtures/mini-repo --out /tmp/codeindex-node18-search.json
test -s /tmp/codeindex-node18-scan.json
test -s /tmp/codeindex-node18-search.json

- name: Push automation branch, open the pull request and merge it
id: merge
if: steps.diff.outputs.changed == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand All @@ -95,19 +124,42 @@ jobs:
else
git push origin HEAD:refs/heads/automation/grammars-repin
fi
TITLE="fix(grammars): bump tree-sitter grammars and re-vendor wasm"
BODY="Automated grammar repin. The full test, reproducible-build and Node 18 smoke gates passed in the repin job, on these exact bytes, before this pull request was merged. Merging creates a patch release so ENGINE_VERSION and the downloadable grammar asset remain aligned."
PR=$(gh pr list --head automation/grammars-repin --state open --json number --jq '.[0].number // empty')
if [ -n "$PR" ]; then
gh pr edit "$PR" \
--title "fix(grammars): bump tree-sitter grammars and re-vendor wasm" \
--body "Automated grammar repin. The full test and reproducible-build gates passed; merging creates a patch release so ENGINE_VERSION and the downloadable grammar asset remain aligned."
gh pr edit "$PR" --title "$TITLE" --body "$BODY"
else
gh pr create \
--base main \
--head automation/grammars-repin \
--title "fix(grammars): bump tree-sitter grammars and re-vendor wasm" \
--body "Automated grammar repin. The full test and reproducible-build gates passed; merging creates a patch release so ENGINE_VERSION and the downloadable grammar asset remain aligned."
gh pr create --base main --head automation/grammars-repin --title "$TITLE" --body "$BODY"
PR=$(gh pr list --head automation/grammars-repin --state open --json number --jq '.[0].number // empty')
fi
if [ -z "$PR" ]; then
echo "No open pull request for automation/grammars-repin — refusing to merge blind." >&2
exit 1
fi
# Squash so main gets exactly one conventional `fix(grammars): ...`
# commit — that subject is what semantic-release turns into the patch
# release. Fall back to a merge commit if squashing is disabled on the
# repository (the branch commit carries the same subject either way).
if ! gh pr merge "$PR" --squash --subject "$TITLE" --body ""; then
echo "Squash merge refused — falling back to a merge commit."
gh pr merge "$PR" --merge
fi
# GITHUB_TOKEN-created PR events do not recursively start CI. An
# explicit workflow_dispatch is allowed and attaches the normal CI
# checks (including Node 18 smoke) to this branch's HEAD.
gh workflow run ci.yml --ref automation/grammars-repin
# Deleted here rather than via `gh pr merge --delete-branch`: that flag
# also prunes the local branch, which is the one checked out in this
# job. A failed cleanup must not fail a merge that already landed.
git push origin --delete automation/grammars-repin || true
echo "merged=true" >> "$GITHUB_OUTPUT"

# GitHub's anti-recursion rule: the merge above is performed with
# GITHUB_TOKEN, so the resulting push to main does NOT start release.yml's
# `on: push`. Without this explicit dispatch the re-pinned grammars would
# sit on main unreleased — the exact misalignment between ENGINE_VERSION
# and the published grammar asset this workflow exists to avoid.
# semantic-release is a no-op when nothing releasable landed, so an extra
# dispatch is harmless.
- name: Trigger the release run for the merged commit
if: steps.merge.outputs.merged == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh workflow run release.yml --ref main
7 changes: 7 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@ name: Release
# right after `semantic-release` runs, gated on the `.release-version`
# sentinel file that `.releaserc.json`'s `@semantic-release/exec` publishCmd
# writes only when a release actually happened.
#
# Dispatch note: a merge performed by a workflow with GITHUB_TOKEN (grammars-repin
# self-merging its repin pull request) does not start `on: push` here, by the same
# anti-recursion rule described above. `workflow_dispatch` is what lets that job
# release the commit it just merged; semantic-release is a no-op when the commits
# since the last release warrant none, so dispatching is always safe.
on:
workflow_dispatch:
push:
branches: [main]

Expand Down
Loading