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
36 changes: 30 additions & 6 deletions .github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Either way the outcome is the same and easy to miss: `validate` and `audit` are
**required** status checks on `main` (ruleset `11289515`), and neither reports
on the commit `assign-ids` just pushed. A run stuck in `action_required` does
not show up in `gh pr checks` output at all, so the PR does not go red. It
quietly shows *fewer* checks than it should, every check it does show is green,
quietly shows _fewer_ checks than it should, every check it does show is green,
and the PR is unmergeable with nothing obviously wrong.

Pushing under an App identity makes the resulting `synchronize` event look like
Expand Down Expand Up @@ -46,19 +46,43 @@ exactly as before rather than failing outright.

### Verifying it worked

Open a catalog PR that adds an entry with no `id:` and let `assign-ids` commit.
Open a PR **from a branch in `existential-engineering/catalog` itself** that
adds an entry with no `id:`, and let `assign-ids` commit. The job is gated on
`head.repo.fork == false`, so a PR from a fork never runs it — there is no bot
commit and nothing to verify. (Fork PRs still need their IDs assigned some
other way; that is a separate gap, not a broken App.)

Then confirm the required checks ran on the **bot's** commit, not just yours:

```bash
gh pr checks <PR> --repo existential-engineering/catalog --json name,bucket \
--jq '[.[]|"\(.name)=\(.bucket)"]|join(" ")'
```

`validate=pass` and `audit=pass` must both be present. If they are missing, the
App is not wired up and the run is waiting for approval:
`validate=pass` and `audit=pass` must both be present. Their absence is a
symptom, not a diagnosis — they are also missing while a run is still going,
and on fork PRs. Look at the runs themselves before concluding anything:

```bash
gh run list --repo existential-engineering/catalog --branch <branch> \
--json databaseId,conclusion,headSha \
--jq '.[]|select(.conclusion=="action_required")|.databaseId'
--json databaseId,workflowName,event,status,conclusion,headSha \
--jq '.[]|"\(.workflowName)\t\(.event)\t\(.status)/\(.conclusion)\t\(.headSha[0:7])\t\(.databaseId)"'
```

Read it in this order:

1. **No `Assign IDs` run at all** — the path filter never fired: the diff
touched no `data/**/*.yaml`, so no run was created. (A fork PR looks
different — the run exists, but its `assign-ids` job shows as skipped by
the fork gate.) Nothing to say about the App yet.
2. **`Assign IDs` ran but pushed nothing** — nothing needed assigning: every
entry already had an `id:` and every hardware `io` entry already had its
key. Re-test with an entry missing one of those.
3. **`Assign IDs` pushed, but `validate`/`audit` have no run on that new
`headSha`** — this is the GITHUB_TOKEN recursion guard: the App is not
wired up.
4. **`validate`/`audit` runs exist on that `headSha` with
`conclusion=action_required`** — they are waiting for manual approval,
also a sign the push was not made under the App identity.
5. **`status=queued`/`in_progress`/`waiting`/`pending`** — just still
running. Wait and re-check.
38 changes: 35 additions & 3 deletions .github/workflows/assign-ids.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,41 @@ jobs:
# degrades to the old behaviour rather than failing outright. Set the
# ASSIGN_IDS_APP_ID variable and ASSIGN_IDS_APP_PRIVATE_KEY secret to
# activate it; see .github/README.md.
#
# Both halves have to be present or create-github-app-token hard-fails,
# which would defeat that fallback. Secrets are not readable in `if:`, so
# the key is checked here instead — this step exposes only a boolean and
# keeps the PEM in step-scoped env.
- name: Check app credentials
id: app-creds
if: vars.ASSIGN_IDS_APP_ID != ''
env:
APP_PRIVATE_KEY: ${{ secrets.ASSIGN_IDS_APP_PRIVATE_KEY }}
run: |
if [ -n "$APP_PRIVATE_KEY" ]; then
echo "configured=true" >> "$GITHUB_OUTPUT"
else
echo "configured=false" >> "$GITHUB_OUTPUT"
fi

- name: Mint app token
id: app-token
if: vars.ASSIGN_IDS_APP_ID != ''
if: steps.app-creds.outputs.configured == 'true'
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ vars.ASSIGN_IDS_APP_ID }}
private-key: ${{ secrets.ASSIGN_IDS_APP_PRIVATE_KEY }}
permission-contents: write

# persist-credentials: false — nothing in .git/config for `pnpm install`
# (which runs PR-controlled lifecycle scripts) to read. The push step
# re-authenticates for itself.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: ${{ github.head_ref }}
fetch-depth: 0
token: ${{ steps.app-token.outputs.token || secrets.GITHUB_TOKEN }}
persist-credentials: false

- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6

Expand All @@ -62,10 +84,20 @@ jobs:
- name: Format YAML files
run: pnpm format

# Git hooks are PR-controlled (`pnpm install` ran the repo's `prepare`
# script, which installs husky's hooksPath), so disable them for the
# bot's git operations and keep the token out of the commit step's
# environment entirely — only the push step ever sees it.
- name: Commit changes
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add -A
git diff --staged --quiet || git commit -m "chore: assign IDs to new entries"
git push
git diff --staged --quiet || git -c core.hooksPath=/dev/null commit -m "chore: assign IDs to new entries"

- name: Push changes
env:
GH_TOKEN: ${{ steps.app-token.outputs.token || secrets.GITHUB_TOKEN }}
run: |
gh auth setup-git
git -c core.hooksPath=/dev/null push