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
1 change: 1 addition & 0 deletions .claude/agent-memory/archgate-developer/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Skipping steps 2 or 3 is a workflow violation. The user should NEVER have to inv
- **`Bun.Glob.scan({ dot: false })` silently drops dot-prefixed segments — even on explicit paths** — `dot: false` (the default) skips matches whose path contains a `.`-prefixed segment, including patterns that explicitly name the dir (e.g. `.github/workflows/release.yml`). Behavior also varies across platforms — Windows reliably drops the match while Linux can match the same pattern, so a rule appears to "work in CI" but no-ops locally. For code repos where `.github/`, `.husky/`, `.vscode/` are first-class source dirs, ALWAYS pass `dot: true` to `Bun.Glob.scan()`. Applied in `src/engine/runner.ts` (`ctx.glob`, `ctx.grepFiles`) and `src/engine/git-files.ts` (`resolveScopedFiles`). See archgate/cli#222.
- **PowerShell 5.1 reads BOM-less `.ps1` files as ANSI — never use non-ASCII chars in `install.ps1`** — `install.ps1` has no UTF-8 BOM (first bytes are `# A...`). On Windows PowerShell 5.1, this means the file is decoded as the system codepage (Windows-1252), so multi-byte UTF-8 characters like em-dash (`—`, `\xE2\x80\x94`) get split into garbage bytes that break later string parsing — the parser reports cryptic errors like "string is missing the terminator" on lines that look fine. ALWAYS stick to ASCII (`-`, `--`, straight quotes) in comments and string literals in `install.ps1`. To verify after editing: `[System.Management.Automation.Language.Parser]::ParseFile((Resolve-Path .\install.ps1).Path, [ref]$null, [ref]$errs)`. Same caveat applies to any unsigned `.ps1` file the project distributes.
- **SLSA reusable workflow MUST be tag-pinned, not SHA-pinned** — `slsa-framework/slsa-github-generator/.github/workflows/*` looks like it should follow CI-001 (SHA pin), but the SLSA generator's `generate-builder.sh` reads the workflow ref to download the prebuilt builder from a GitHub release and rejects non-tag refs (`Invalid ref: ... Expected ref of the form refs/tags/vX.Y.Z`, exit 2). Pinning by SHA broke the v0.31.0 release ([run 25107195589](https://github.com/archgate/cli/actions/runs/25107195589)). The CI-001 rule allowlists this path so it does NOT block a SHA repin — meaning a future agent could "fix" the tag pin and the rule would be silent until the next release fails. ALWAYS keep `@v2.x.y` for `release-binaries.yml:165` and read the inline comment + CI-001 "Carved-out exceptions" before changing. Upstream issue: [slsa-framework/slsa-github-generator#150](https://github.com/slsa-framework/slsa-github-generator/issues/150).
- **`GITHUB_TOKEN`-authored pushes do NOT trigger downstream workflows — release.yml MUST use the GH App token** — When an Actions workflow pushes commits or opens PRs using `${{ github.token }}` / `secrets.GITHUB_TOKEN`, GitHub intentionally suppresses the resulting `push` / `pull_request` events to prevent recursion. Symptom on release PRs: the head SHA has no `pull_request`-event check runs, so `Validate Code` / `Lint, Test & Check` / `DCO Sign-off Check` are missing from the PR rollup and branch protection treats the PR as missing required checks. PR [#131](https://github.com/archgate/cli/pull/131) papered over this by manually `gh workflow run` + posting commit statuses, but `workflow_dispatch` runs land on `head_branch: release` with `pull_requests: []` — they are not associated with the PR ref, so `Lint, Test & Check` stayed orphaned and the bug recurred on PR [#251](https://github.com/archgate/cli/pull/251). Root-cause fix: in `release.yml` the `pull-request` job MUST generate a GitHub App installation token via `actions/create-github-app-token` (using `secrets.GH_APP_APP_ID` / `secrets.GH_APP_PRIVATE_KEY`) and pass it to BOTH `actions/checkout` and `simple-release-action`. App-token-authored pushes DO trigger `pull_request` events naturally. Apply the same pattern to any future workflow that pushes to a branch whose downstream CI must run.

## Validation Pipeline

Expand Down
61 changes: 18 additions & 43 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,28 @@ jobs:
timeout-minutes: 10
name: Pull request
permissions:
actions: write
contents: write
pull-requests: write
statuses: write
contents: read
needs: check
if: needs.check.outputs.workflow == 'pull-request'
steps:
# Use a GitHub App installation token (not GITHUB_TOKEN) so the push to the
# release branch triggers downstream `pull_request` events on the release PR.
# GITHUB_TOKEN-authored pushes are intentionally muted by GitHub to prevent
# workflow recursion — that mute is what causes release PRs to land without
# the required `Validate Code` / `Lint, Test & Check` / `DCO Sign-off Check`
# status checks attached to the PR ref. With an App token, the synchronize
# event fires naturally and code-pull-request.yml + dco.yml run against
# refs/pull/N/head, producing checks that branch protection accepts.
- name: Generate token
id: generate_token
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3
with:
app-id: ${{ secrets.GH_APP_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
token: ${{ steps.generate_token.outputs.token }}
- uses: moonrepo/setup-toolchain@261c62cb5b0f580c7be7c8cd0f023a2e96756095 # v0
with:
auto-install: true
Expand Down Expand Up @@ -102,46 +115,8 @@ jobs:
uses: TrigenSoftware/simple-release-action@a796225481a62b5b46b37d4481ef7fb114a6d399 # v1
with:
workflow: pull-request
github-token: ${{ github.token }}
github-token: ${{ steps.generate_token.outputs.token }}
branch: release
- name: Run CI and set status checks on release PR
env:
GH_TOKEN: ${{ github.token }}
run: |
HEAD_SHA=$(gh api repos/${{ github.repository }}/git/ref/heads/release --jq '.object.sha' 2>/dev/null) || true

if [ -z "$HEAD_SHA" ]; then
echo "No release branch found — nothing to validate"
exit 0
fi

# Verify DCO sign-off and set status
COMMIT_MSG=$(gh api repos/${{ github.repository }}/git/commits/$HEAD_SHA --jq '.message')
if echo "$COMMIT_MSG" | grep -qiE '^Signed-off-by: .+ <.+>'; then
DCO_STATE=success
DCO_DESC="All commits are signed off"
else
DCO_STATE=failure
DCO_DESC="Missing DCO Signed-off-by line"
fi

gh api repos/${{ github.repository }}/statuses/$HEAD_SHA \
-f state="$DCO_STATE" \
-f context="DCO Sign-off Check" \
-f description="$DCO_DESC" \
--silent

gh workflow run code-pull-request.yml --ref release
sleep 5
RUN_ID=$(gh run list --branch release --workflow code-pull-request.yml --limit 1 --json databaseId --jq '.[0].databaseId')
echo "Waiting for CI run $RUN_ID..."
gh run watch "$RUN_ID" --exit-status && STATUS=success || STATUS=failure

gh api repos/${{ github.repository }}/statuses/$HEAD_SHA \
-f state="$STATUS" \
-f context="Validate Code" \
-f description="CI $STATUS" \
--silent
release:
runs-on: ubuntu-latest
timeout-minutes: 10
Expand Down
Loading