-
Notifications
You must be signed in to change notification settings - Fork 0
ci: push assign-ids commits under an app identity #622
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| # CI configuration notes | ||
|
|
||
| ## `assign-ids` needs a GitHub App identity | ||
|
|
||
| `assign-ids.yml` assigns IDs to new entries and pushes an auto-commit back onto | ||
| the PR branch. That push has to be made by something other than the default | ||
| `GITHUB_TOKEN`. | ||
|
|
||
| ### Why | ||
|
|
||
| A push made with `GITHUB_TOKEN` does not get treated as a normal contributor | ||
| push. Depending on the day it either triggers **no** workflow runs at all | ||
| (GitHub's guard against workflows re-triggering themselves) or creates runs | ||
| that sit in `action_required` waiting for manual approval. Both behaviours were | ||
| observed on the same afternoon, 2026-08-14, across PRs 582-587. | ||
|
|
||
| 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, | ||
| and the PR is unmergeable with nothing obviously wrong. | ||
|
|
||
| Pushing under an App identity makes the resulting `synchronize` event look like | ||
| any other contributor push, so the required checks run and report normally. | ||
|
|
||
| ### Setup | ||
|
|
||
| 1. Create a GitHub App on the org (Settings → Developer settings → GitHub Apps). | ||
| It needs one permission: **Repository → Contents → Read and write**. | ||
| 2. Install it on `existential-engineering/catalog`. | ||
| 3. Generate a private key and record the App's numeric ID. | ||
| 4. Add them to the repo: | ||
| - variable `ASSIGN_IDS_APP_ID` — the numeric App ID (a variable, not a | ||
| secret, because the workflow tests it for emptiness and secrets are not | ||
| readable in `if:` conditions) | ||
| - secret `ASSIGN_IDS_APP_PRIVATE_KEY` — the full PEM contents | ||
|
|
||
| ```bash | ||
| gh variable set ASSIGN_IDS_APP_ID --repo existential-engineering/catalog --body '<app-id>' | ||
| gh secret set ASSIGN_IDS_APP_PRIVATE_KEY --repo existential-engineering/catalog < app.private-key.pem | ||
| ``` | ||
|
|
||
| Until both exist the workflow falls back to `GITHUB_TOKEN`, so it keeps working | ||
| 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. | ||
| 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: | ||
|
|
||
| ```bash | ||
| gh run list --repo existential-engineering/catalog --branch <branch> \ | ||
| --json databaseId,conclusion,headSha \ | ||
| --jq '.[]|select(.conclusion=="action_required")|.databaseId' | ||
| ``` | ||
|
Comment on lines
+57
to
+64
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- README candidate ---'
cat -n .github/README.md | sed -n '35,75p'
printf '%s\n' '--- assign-ids workflow ---'
cat -n .github/workflows/assign-ids.yml | sed -n '1,140p'
printf '%s\n' '--- related references ---'
rg -n -C 3 'gh run list|assign-ids|validate=pass|audit=pass|action_required|status|workflowName' .github README.md 2>/dev/null || true
printf '%s\n' '--- gh availability/help ---'
if command -v gh >/dev/null 2>&1; then
gh --version
gh run list --help | sed -n '1,180p'
else
echo 'gh is not installed'
fiRepository: existential-engineering/catalog Length of output: 17681 🏁 Script executed: #!/bin/bash
set -eu
cat -n .github/README.md | sed -n '35,75p'
printf '\n--- workflow ---\n'
cat -n .github/workflows/assign-ids.yml | sed -n '1,120p'
printf '\n--- references ---\n'
rg -n -C 2 'gh run list|assign-ids|validate=pass|audit=pass|action_required' .github 2>/dev/null || trueRepository: existential-engineering/catalog Length of output: 8018 🌐 Web query:
💡 Result: The GitHub CLI command Citations:
Make the diagnosis conditional. Missing 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,10 +21,30 @@ jobs: | |
| contents: write | ||
|
|
||
| steps: | ||
| # Push the auto-commit under a GitHub App identity, not GITHUB_TOKEN. | ||
| # A GITHUB_TOKEN push either triggers no workflows at all (GitHub's | ||
| # recursion guard) or creates runs stuck in `action_required`. Both were | ||
| # observed on 2026-08-14. Either way `validate` and `audit` — required | ||
| # contexts on main (ruleset 11289515) — never report on the commit this | ||
| # job pushes, so the PR silently shows fewer checks and cannot be merged. | ||
| # | ||
| # Falls back to GITHUB_TOKEN when the App is not configured, so the job | ||
| # 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. | ||
| - name: Mint app token | ||
| id: app-token | ||
| if: vars.ASSIGN_IDS_APP_ID != '' | ||
| 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 }} | ||
|
Comment on lines
+37
to
+41
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -u
printf '%s\n' '--- workflow ---'
cat -n .github/workflows/assign-ids.yml | sed -n '1,90p'
printf '%s\n' '--- README references ---'
cat -n .github/README.md | sed -n '35,55p'
printf '%s\n' '--- action references ---'
rg -n --hidden --glob '!node_modules' 'create-github-app-token|ASSIGN_IDS_APP_ID|ASSIGN_IDS_APP_PRIVATE_KEY|Mint app token|app-config' .
printf '%s\n' '--- pinned action metadata ---'
if command -v gh >/dev/null 2>&1; then
gh api repos/actions/create-github-app-token/commits/bcd2ba49218906704ab6c1aa796996da409d3eb1 --jq '{sha:.sha,message:.commit.message}'
else
curl -fsSL -H 'Accept: application/vnd.github+json' \
'https://api.github.com/repos/actions/create-github-app-token/commits/bcd2ba49218906704ab6c1aa796996da409d3eb1' |
jq '{sha:.sha,message:.commit.message}'
fiRepository: existential-engineering/catalog Length of output: 6864 🌐 Web query:
💡 Result: In the actions/create-github-app-token GitHub Action, the private-key input is explicitly marked as required in the action.yml configuration file [1]. As of version 3.2.0, the action includes specific validation logic to handle cases where the private-key input resolves to an empty value [2][3]. Previously, an empty input would pass through to the underlying Octokit library, resulting in a generic error message regarding the privateKey option [3]. In version 3.2.0, the action performs an early check to ensure the input is a non-empty string, and if it is empty, the action will now fail with a clear, action-specific error message designed to guide users correctly [3]. This change ensures that users receive immediate feedback that the private key is missing or invalid, rather than encountering an implementation-detail error from the authentication library [3]. Citations:
Gate token minting on both App values. When 🧰 Tools🪛 zizmor (1.29.0)[error] 38-38: dangerous use of GitHub App tokens (github-app): app token inherits blanket installation permissions (github-app) 🤖 Prompt for AI Agents
Comment on lines
+38
to
+41
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- workflow ---'
cat -n .github/workflows/assign-ids.yml
printf '%s\n' '--- related documentation ---'
rg -n -C 4 'ASSIGN_IDS_APP_ID|ASSIGN_IDS_APP_PRIVATE_KEY|fallback|App token|create-github-app-token' README.md .github 2>/dev/null || true
printf '%s\n' '--- repository workflow permission settings ---'
rg -n -C 3 'permissions:|permission-contents|create-github-app-token' .github/workflows .github 2>/dev/null || trueRepository: existential-engineering/catalog Length of output: 21772 🌐 Web query:
💡 Result: In the actions/create-github-app-token action (including version 3.2.0), the permission-contents input is an optional configuration parameter used to explicitly set the access level for repository contents (such as commits, branches, and releases) [1][2]. Key technical details regarding this input and the action's permission inheritance include: 1. Default Behavior: By default, if no permission-*-type inputs are provided, the generated GitHub App installation access token inherits all the permissions currently granted to the GitHub App's installation [3][4]. 2. Purpose of Explicit Permissions: When you provide an explicit permission input like permission-contents (which accepts values of 'read' or 'write'), you are overriding the default behavior [1][2]. This allows you to restrict the token's scope following the principle of least privilege [5]. 3. Important Considerations: - Selecting permissions that are not granted to the app's installation will result in an error [4]. - If you encounter authorization errors (e.g., 422 or 403) after setting specific permission- inputs, it is often because the configured permissions are insufficient for the operations you are performing [6]. Removing these specific inputs allows the token to revert to the full set of inherited installation permissions, which can help diagnose whether the issue is caused by overly restrictive settings [4][6]. The documentation recommends explicitly listing only the required permissions for your use case to ensure better security [4]. This aligns with GitHub's broader best practices for controlling workflow token permissions [4]. Citations:
🏁 Script executed: #!/bin/bash
set -eu
base='https://raw.githubusercontent.com/actions/create-github-app-token/v3.2.0'
printf '%s\n' '--- action inputs at v3.2.0 ---'
curl -fsSL "$base/action.yml" | sed -n '1,180p'
printf '%s\n' '--- permission documentation at v3.2.0 ---'
curl -fsSL "$base/README.md" |
rg -n -C 8 'permission-contents|all permissions|permission inputs|installation permissions|least privilege' |
head -n 140Repository: existential-engineering/catalog Length of output: 13055 Constrain the App token permissions explicitly. Set 🧰 Tools🪛 zizmor (1.29.0)[error] 38-38: dangerous use of GitHub App tokens (github-app): app token inherits blanket installation permissions (github-app) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
|
|
||
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | ||
| with: | ||
| ref: ${{ github.head_ref }} | ||
| fetch-depth: 0 | ||
| token: ${{ steps.app-token.outputs.token || secrets.GITHUB_TOKEN }} | ||
|
Comment on lines
43
to
+47
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- workflow files ---'
git ls-files '.github/workflows/assign-ids.yml' 'README.md' '.github/workflows/*' | sed -n '1,80p'
printf '%s\n' '--- workflow ---'
cat -n .github/workflows/assign-ids.yml | sed -n '1,180p'
printf '%s\n' '--- related token/checkout references ---'
rg -n -C 3 'persist-credentials|app-token|ASSIGN_IDS_APP|git push|gh auth setup-git|checkout@' .github README.md 2>/dev/null | sed -n '1,240p'Repository: existential-engineering/catalog Length of output: 13163 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- repository scripts and install configuration ---'
for f in package.json pnpm-workspace.yaml pnpm-lock.yaml; do
if [ -f "$f" ]; then
echo "### $f"
sed -n '1,180p' "$f"
fi
done
printf '%s\n' '--- workflow action source references ---'
rg -n -C 4 'assign-ids|format|prepare|postinstall|preinstall|scripts' package.json .npmrc pnpm-workspace.yaml .github 2>/dev/null | sed -n '1,240p'
printf '%s\n' '--- checkout credential behavior references in repository ---'
rg -n -C 5 'persist-credentials|extraheader|credential.helper|gh auth setup-git|GH_TOKEN' . --glob '!pnpm-lock.yaml' --glob '!data/**' | sed -n '1,260p'
printf '%s\n' '--- static verifier for the workflow security invariant ---'
python3 - <<'PY'
from pathlib import Path
p = Path(".github/workflows/assign-ids.yml")
text = p.read_text()
checkout = text[text.index(" - uses: actions/checkout@"):text.index(" - uses: pnpm/action-setup@")]
commit = text[text.index(" - name: Commit changes"):]
print("checkout_has_persist_credentials_false=",
"persist-credentials: false" in checkout)
print("checkout_persists_by_default=",
"persist-credentials:" not in checkout)
print("repository_commands_after_checkout_before_push=",
all(marker in text for marker in [
"pnpm install --frozen-lockfile",
"pnpm assign-ids",
"pnpm format",
]))
print("push_authentication_explicitly_configured=",
"gh auth setup-git" in commit or "git config" in commit and "credential" in commit)
print("push_present=", "git push" in commit)
PYRepository: existential-engineering/catalog Length of output: 26157 🌐 Web query:
💡 Result: The actions/checkout action uses Citations:
🏁 Script executed: #!/bin/bash
set -eu
base='https://raw.githubusercontent.com/actions/checkout/3d3c42e5aac5ba805825da76410c181273ba90b1'
printf '%s\n' '--- pinned checkout action metadata ---'
curl -fsSL "$base/action.yml" | sed -n '1,180p'
printf '%s\n' '--- pinned checkout authentication implementation ---'
for f in src/git-auth-helper.ts src/auth.ts; do
if curl -fsSL "$base/$f" > "/tmp/checkout-$(basename "$f")"; then
echo "### $f"
sed -n '1,260p' "/tmp/checkout-$(basename "$f")"
fi
done
printf '%s\n' '--- pinned checkout references to credential persistence ---'
curl -fsSL "$base/src/git-auth-helper.ts" | rg -n -C 5 'persist|extraheader|includeIf|RUNNER_TEMP|credential'
printf '%s\n' '--- create-github-app-token guidance ---'
curl -fsSL 'https://raw.githubusercontent.com/actions/create-github-app-token/main/README.md' | rg -n -C 5 'persist-credentials|checkout|pull request|push|setup-git' | sed -n '1,220p'Repository: existential-engineering/catalog Length of output: 30315 Disable checkout credential persistence before running PR code.
🧰 Tools🪛 zizmor (1.29.0)[warning] 43-47: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false (artipacked) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
|
|
||
| - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6 | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
State that verification requires a same-repository PR.
.github/workflows/assign-ids.ymlLine 18 skips the job whengithub.event.pull_request.head.repo.fork == true. A PR from a fork therefore produces no auto-commit or App-token verification run. Update these instructions to require a branch inexistential-engineering/catalog, or document the expected skip.Suggested wording
📝 Committable suggestion
🤖 Prompt for AI Agents