From 8f84dae590499b971f93da703c6628c552ebc009 Mon Sep 17 00:00:00 2001 From: Noah Gift Date: Sat, 18 Apr 2026 14:18:17 +0200 Subject: [PATCH 1/2] feat(ci): reusable auto-merge-on-green workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New workflow `auto-merge.yml` enables GitHub's built-in auto-merge (squash) on authorized PRs. Once all required status checks (org ruleset `gate`) pass, GitHub merges automatically — no manual click required. Escape hatches: draft PRs, `hold` label, `do-not-merge` label. Intended rollout: add `auto-merge` job to each repo's ci.yml (after `authorize`). Prerequisite per repo: `allow_auto_merge: true` in settings. Co-Authored-By: Claude Opus 4.7 --- .github/workflows/auto-merge.yml | 53 ++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 .github/workflows/auto-merge.yml diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml new file mode 100644 index 0000000..6501e2b --- /dev/null +++ b/.github/workflows/auto-merge.yml @@ -0,0 +1,53 @@ +# Auto-merge — enable GitHub's built-in auto-merge for authorized PRs +# +# Reusable workflow called by each repo's ci.yml. Enables SQUASH auto-merge +# on the triggering PR, which tells GitHub to merge the PR automatically once +# all required status checks pass (see org ruleset "Green Main" id 13878864). +# +# Escape hatches (no code change needed): +# - Open the PR as a draft → auto-merge is not enabled +# - Apply the `hold` label → auto-merge is skipped +# - Apply the `do-not-merge` label → auto-merge is skipped +# +# Caller pattern (per-repo ci.yml): +# +# on: +# pull_request_target: +# types: [opened, reopened, synchronize, ready_for_review, labeled, unlabeled] +# +# jobs: +# authorize: +# uses: paiml/.github/.github/workflows/pr-gate.yml@main +# auto-merge: +# needs: authorize +# uses: paiml/.github/.github/workflows/auto-merge.yml@main +# +# Prerequisites per repo: +# - `allow_auto_merge: true` in repo settings (gh api -X PATCH repos/OWNER/REPO -f allow_auto_merge=true) +# - At least one required status check (enforced fleet-wide by org ruleset `gate`) + +name: Auto-merge + +on: + workflow_call: + +jobs: + enable: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + if: | + github.event_name == 'pull_request_target' && + github.event.pull_request.draft == false && + !contains(github.event.pull_request.labels.*.name, 'hold') && + !contains(github.event.pull_request.labels.*.name, 'do-not-merge') + steps: + - name: Enable squash auto-merge + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + # Idempotent: if auto-merge is already enabled, gh exits cleanly. + gh pr merge "$PR_NUMBER" --auto --squash From c3488a6a1dea18c0f7fafba22b5275fd777fa92f Mon Sep 17 00:00:00 2001 From: Noah Gift Date: Sat, 18 Apr 2026 14:32:36 +0200 Subject: [PATCH 2/2] feat(ci): bootstrap gate + roll sovereign-ci to rust 1.95 digest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bundles three bootstrap changes so this PR can flow through its own new gate (no admin bypass needed): 1. New .github/workflows/ci.yml — runs actionlint + yamllint on every PR, produces the 'gate' status check required by org ruleset 13878864 ('Green Main'). Previously PRs to this repo had no checks and got mergeStateStatus=BLOCKED by default. 2. Rolls sovereign-ci.yml container digest from sha256:c23c4533… -> sha256:dd219db7… corresponding to the sovereign-ci:stable image rebuilt on mac-server after bumping the base to rust:1.95-slim-bookworm. 3. Together with the already-proposed auto-merge.yml (previous commit), this establishes the pattern: CI green -> ruleset satisfied -> auto-merge fires. Co-Authored-By: Claude Opus 4.7 --- .github/workflows/ci.yml | 47 ++++++++++++++++++++++++++++++ .github/workflows/sovereign-ci.yml | 8 ++--- 2 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a34e8b9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,47 @@ +# CI for paiml/.github itself — lints reusable workflows so a broken YAML +# or a config mismatch can't ship to the fleet. +# +# This workflow also produces the `gate` status check required by the org +# ruleset "Green Main" (id 13878864). Without this, every PR to this repo +# would be mergeStateStatus=BLOCKED and require an admin bypass. + +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + lint: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - name: Run actionlint + uses: raven-actions/actionlint@3a24062651993d40fed1019b58ac6fbdfbf276cc # v2.0.1 + with: + matcher: true + fail-on-error: true + - name: Install yamllint + run: pip install --quiet yamllint + - name: yamllint (relaxed) + run: yamllint -d relaxed .github/workflows/ + + gate: + name: gate + needs: [lint] + if: always() + runs-on: ubuntu-latest + steps: + - name: Check required jobs + run: | + if [ "${{ needs.lint.result }}" != "success" ]; then + echo "lint failed (result: ${{ needs.lint.result }})" + exit 1 + fi + echo "gate passed" diff --git a/.github/workflows/sovereign-ci.yml b/.github/workflows/sovereign-ci.yml index f3f5229..d34af81 100644 --- a/.github/workflows/sovereign-ci.yml +++ b/.github/workflows/sovereign-ci.yml @@ -70,7 +70,7 @@ jobs: name: test runs-on: [self-hosted, clean-room] container: - image: localhost:5000/sovereign-ci:stable@sha256:c23c453328df86562ba69410810180d205490c6b202342972f65af7050db6686 + image: localhost:5000/sovereign-ci:stable@sha256:dd219db7e10568f56aad0597367af1ba1b011d1a0d1c5c5e97b550501a3e014f timeout-minutes: 30 steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 @@ -170,7 +170,7 @@ jobs: name: lint runs-on: [self-hosted, clean-room] container: - image: localhost:5000/sovereign-ci:stable@sha256:c23c453328df86562ba69410810180d205490c6b202342972f65af7050db6686 + image: localhost:5000/sovereign-ci:stable@sha256:dd219db7e10568f56aad0597367af1ba1b011d1a0d1c5c5e97b550501a3e014f timeout-minutes: 30 steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 @@ -273,7 +273,7 @@ jobs: if: ${{ !inputs.skip_coverage }} runs-on: [self-hosted, clean-room] container: - image: localhost:5000/sovereign-ci:stable@sha256:c23c453328df86562ba69410810180d205490c6b202342972f65af7050db6686 + image: localhost:5000/sovereign-ci:stable@sha256:dd219db7e10568f56aad0597367af1ba1b011d1a0d1c5c5e97b550501a3e014f timeout-minutes: 30 steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 @@ -371,7 +371,7 @@ jobs: if: ${{ inputs.run_benchmarks }} runs-on: [self-hosted, clean-room] container: - image: localhost:5000/sovereign-ci:stable@sha256:c23c453328df86562ba69410810180d205490c6b202342972f65af7050db6686 + image: localhost:5000/sovereign-ci:stable@sha256:dd219db7e10568f56aad0597367af1ba1b011d1a0d1c5c5e97b550501a3e014f timeout-minutes: 60 continue-on-error: true steps: