diff --git a/.github/workflows/overlay-promotion-bot.yml b/.github/workflows/overlay-promotion-bot.yml new file mode 100644 index 000000000..0f1eed89e --- /dev/null +++ b/.github/workflows/overlay-promotion-bot.yml @@ -0,0 +1,115 @@ +# Phase 1 of harmoniqs/amicode#1010 — automate the promotion DIFF, keep the +# human MERGE. +# +# The manual toil this replaces isn't the merge (JJ's own promotion PRs +# — #1009, #1011 — were merged 6-27 seconds after opening, once CI was +# green): it's remembering to run `sync:check`/`sync:apply` at all, and +# typing the exact command. This job runs the SAME script +# (overlay-promotion.mjs) a human would, on a schedule, and opens/updates one +# PR when the fork has drifted. Nothing here has merge authority: a human (or +# required status checks, once branch protection is enabled) still merges. +# +# Deliberately NOT built here, because the actual problem doesn't need it: +# a signed webhook receiver, a GitHub App, compare-and-swap bot-merge +# authority, or exception-approval automation. If the app-bundle overlay +# itself is retired later (see the phases-2-4 note referenced from #1010), +# this workflow is deleted, not migrated. +name: overlay-promotion-bot + +on: + schedule: + - cron: "0 */2 * * *" # every 2 hours; cheap no-op when already in sync + workflow_dispatch: {} # manual/test trigger + +permissions: + contents: write + pull-requests: write + +concurrency: overlay-promotion-bot + +env: + PROMOTION_BRANCH: overlay-promotion + +jobs: + check-and-promote: + runs-on: ubuntu-latest + steps: + - name: Checkout amicode + uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: Checkout harmoniqs/opencode (local/amicode) + uses: actions/checkout@v7 + with: + repository: harmoniqs/opencode + path: .opencode-src + ref: local/amicode + fetch-depth: 0 + token: ${{ secrets.OPENCODE_FETCH_TOKEN }} + + - uses: actions/setup-node@v7 + with: { node-version: 20 } + + - name: Resolve fork tip + declared upstream base + id: rev + run: | + set -euo pipefail + FORK_REV="$(git -C .opencode-src rev-parse HEAD)" + BASE_SHA="$(node -p "require('./packages/app-bundle/manifest.json').upstream_base_sha")" + # overlay-promotion.mjs resolves --base against the source checkout; + # the base commit lives in anomalyco/opencode, not necessarily + # reachable in the fork clone yet. + git -C .opencode-src fetch --no-tags https://github.com/anomalyco/opencode.git "$BASE_SHA" + { echo "fork_rev=$FORK_REV"; echo "base_sha=$BASE_SHA"; } >> "$GITHUB_OUTPUT" + + - name: sync:check — is the overlay already current? + id: check + run: | + set +e + node packages/app-bundle/scripts/overlay-promotion.mjs --check \ + --source "$GITHUB_WORKSPACE/.opencode-src" \ + --revision "${{ steps.rev.outputs.fork_rev }}" \ + --base "${{ steps.rev.outputs.base_sha }}" + echo "status=$?" >> "$GITHUB_OUTPUT" + + - name: sync:apply — promote the drifted revision + if: steps.check.outputs.status != '0' + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + # overlay-promotion.mjs refuses --apply directly on main (#1005) — + # switch to the review branch FIRST, same as a human promoting by hand. + git checkout -B "$PROMOTION_BRANCH" + node packages/app-bundle/scripts/overlay-promotion.mjs --apply \ + --source "$GITHUB_WORKSPACE/.opencode-src" \ + --revision "${{ steps.rev.outputs.fork_rev }}" \ + --base "${{ steps.rev.outputs.base_sha }}" + + - name: Open or update the promotion PR + if: steps.check.outputs.status != '0' + env: + GH_TOKEN: ${{ github.token }} + FORK_REV: ${{ steps.rev.outputs.fork_rev }} + run: | + set -euo pipefail + if git diff --quiet -- packages/app-bundle; then + echo "overlay-promotion.mjs reported drift but produced no diff — refusing to open an empty PR." + exit 1 + fi + git add packages/app-bundle + git commit -m "chore: sync overlay to opencode ${FORK_REV:0:10}" + git push --force origin "$PROMOTION_BRANCH" + + if gh pr list --head "$PROMOTION_BRANCH" --state open --json number --jq '.[0].number' | grep -q .; then + echo "PR already open for $PROMOTION_BRANCH — force-push above updated it." + else + gh pr create \ + --head "$PROMOTION_BRANCH" \ + --base main \ + --title "chore: sync overlay to opencode ${FORK_REV:0:10}" \ + --body "Automated promotion (Phase 1 of #1010) — the fork's \`local/amicode\` moved to \`${FORK_REV}\` and the overlay was stale. Generated by \`overlay-promotion.mjs --apply\`, the same script a human would run. Review the diff and merge; CI gates this PR the same as any other." + fi