Compare commits

...

2 Commits

Author SHA1 Message Date
Pat Sukprasert daa9cbf9da ci(oss): make /regen result comment accurate when App is unconfigured
The success comment hard-coded 'CI will re-run on the new commit', which
is false in the GITHUB_TOKEN fallback path (a GITHUB_TOKEN push doesn't
re-trigger checks). Branch the message on whether the App token was
minted: when it wasn't, tell the maintainer to push a commit to run CI.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Pat Sukprasert <pattara.sk127@gmail.com>
2026-06-15 18:17:26 +08:00
Pat Sukprasert 1979361cd3 ci(oss): replace OSS_REGEN_TOKEN PAT with a GitHub App token
Both OSS lockfile-regen workflows used a personal PAT (OSS_REGEN_TOKEN)
only to push/open PRs that re-trigger the regen PR's own CI — which a
GITHUB_TOKEN push deliberately won't do. Swap the PAT for a GitHub App
installation token (vars.OSS_REGEN_APP_ID + secrets.OSS_REGEN_APP_KEY),
a distinct actor that re-triggers checks, without the PAT's
user-binding / expiry / broad-scope downsides.

oss-regen-on-comment: mint the token via actions/create-github-app-token
AFTER 'uv lock' (so untrusted PR build backends never see it) and use it
only in the inline push URL — preserving the existing 'credentials never
on disk' hardening.

oss-regenerate-and-smoke: mint the token before opening the rolling
regen PR; push via the token too so refreshing an already-open PR
re-triggers CI on synchronize.

Both steps are if-guarded on vars.OSS_REGEN_APP_ID and fall back to
GITHUB_TOKEN when the App is unconfigured (push still lands; a maintainer
re-pushes to run CI). OSS_REGEN_TOKEN is now unreferenced.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Pat Sukprasert <pattara.sk127@gmail.com>
2026-06-15 18:09:18 +08:00
2 changed files with 59 additions and 22 deletions
+35 -13
View File
@@ -6,10 +6,13 @@
# place.
#
# Validation is deliberately left to the PR's own CI: the push is made with a
# PAT (secrets.OSS_REGEN_TOKEN), NOT GITHUB_TOKEN, so it re-fires the PR's full
# check suite — including the Docker build — on the new commit. A GITHUB_TOKEN
# push would NOT re-trigger those checks (GitHub suppresses it to avoid loops),
# leaving stale results; that is why the PAT is required here.
# GitHub App installation token (vars.OSS_REGEN_APP_ID + secrets.OSS_REGEN_APP_KEY),
# NOT GITHUB_TOKEN, so it re-fires the PR's full check suite — including the
# Docker build — on the new commit. A GITHUB_TOKEN push would NOT re-trigger
# those checks (GitHub suppresses it to avoid loops), leaving stale results; an
# App token is a distinct actor and does re-trigger. If the App is not
# configured the push falls back to GITHUB_TOKEN: it still lands, but a
# maintainer must re-push the branch to run CI.
#
# Authorization: only maintainers listed in .github/MAINTAINER (read from main's
# tip by merge-ready/load-maintainers.sh) may run it — the action pushes code.
@@ -121,8 +124,8 @@ jobs:
# No token and no persisted credentials: the public repo needs no auth
# to fetch, and `uv lock` below can execute build backends the PR head
# chooses (sdists, [build-system] hooks in pyproject.toml) — nothing it
# runs should find OSS_REGEN_TOKEN on disk. The PAT enters only at the
# push step.
# runs should find a push token on disk. The App token is minted only
# after `uv lock` and enters only at the push step.
- name: Checkout the PR branch
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
@@ -151,17 +154,29 @@ jobs:
uv lock
( cd ap-web && npm install --package-lock-only --no-audit --no-fund )
# Mint the App installation token only AFTER `uv lock` so untrusted PR
# build backends never see it, and never via the checkout (credentials
# stay off disk). Skipped when the App isn't configured — the push then
# falls back to GITHUB_TOKEN and a maintainer must re-push to run CI.
- name: Mint App token
id: app-token
if: vars.OSS_REGEN_APP_ID != ''
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ vars.OSS_REGEN_APP_ID }}
private-key: ${{ secrets.OSS_REGEN_APP_KEY }}
# All ${{ }} values are passed via env: and referenced as "$VAR" rather
# than interpolated into the script body — HEAD_REF is the PR author's
# branch name (user-influenced), so this avoids expression injection.
# The PAT authenticates the push inline (scoped to this step, never
# written to .git/config) so the push re-triggers the PR's CI; Actions
# masks the secret in logs.
# The push token authenticates inline (scoped to this step, never written
# to .git/config) so the push re-triggers the PR's CI; Actions masks the
# secret in logs.
- name: Commit and push to the PR branch
id: push
env:
HEAD_REF: ${{ needs.authorize.outputs.head }}
OSS_REGEN_TOKEN: ${{ secrets.OSS_REGEN_TOKEN }}
PUSH_TOKEN: ${{ steps.app-token.outputs.token || secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
run: |
git config user.name "github-actions[bot]"
@@ -174,7 +189,7 @@ jobs:
fi
git add uv.lock ap-web/package-lock.json
git commit -m "chore(oss): regenerate public lockfiles against public PyPI/npm"
git push "https://x-access-token:${OSS_REGEN_TOKEN}@github.com/${REPO}.git" "HEAD:$HEAD_REF"
git push "https://x-access-token:${PUSH_TOKEN}@github.com/${REPO}.git" "HEAD:$HEAD_REF"
echo "changed=true" >> "$GITHUB_OUTPUT"
- name: Comment the result
@@ -184,10 +199,17 @@ jobs:
ISSUE: ${{ github.event.issue.number }}
REPO: ${{ github.repository }}
CHANGED: ${{ steps.push.outputs.changed }}
# App token used → push re-triggers CI; skipped (GITHUB_TOKEN fallback) → it won't.
APP_USED: ${{ steps.app-token.conclusion == 'success' }}
run: |
if [ "$CHANGED" = "true" ]; then
gh pr comment "$ISSUE" --repo "$REPO" \
--body "✅ Regenerated \`uv.lock\` + \`ap-web/package-lock.json\` against public PyPI/npm and pushed to this PR. CI will re-run on the new commit."
base="✅ Regenerated \`uv.lock\` + \`ap-web/package-lock.json\` against public PyPI/npm and pushed to this PR."
if [ "$APP_USED" = "true" ]; then
body="$base CI will re-run on the new commit."
else
body="$base ⚠️ No regen App configured, so this push won't auto-trigger CI — push any commit (or amend) to re-run checks."
fi
gh pr comment "$ISSUE" --repo "$REPO" --body "$body"
else
gh pr comment "$ISSUE" --repo "$REPO" \
--body "️ Lockfiles already current against public PyPI/npm — nothing to regenerate."
+24 -9
View File
@@ -88,20 +88,33 @@ jobs:
- name: CLI smoke
run: docker run --rm omnigent-smoke omnigent --help
# Mint the App installation token for the push + PR below. A distinct
# actor (not GITHUB_TOKEN), so the regen PR runs its own CI. Skipped when
# the App isn't configured — the step then falls back to GITHUB_TOKEN.
- name: Mint App token
id: app-token
if: vars.OSS_REGEN_APP_ID != ''
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ vars.OSS_REGEN_APP_ID }}
private-key: ${{ secrets.OSS_REGEN_APP_KEY }}
# 5. Persist the validated lockfiles via a PR (only if they changed and
# the build above passed). A PR, not a direct push to main, so it
# works once main is branch-protected. Created with a repo PAT
# (secrets.OSS_REGEN_TOKEN) so `gh pr create` is not blocked by the
# org "Allow Actions to create PRs" restriction and the regen PR runs
# its own CI. No loop: this workflow's push trigger is path-scoped to
# the manifests (pyproject.toml / package.json), and the PR only
# touches lockfiles, so merging it never re-fires this workflow.
# Falls back to GITHUB_TOKEN if the PAT is not configured (the step
# works once main is branch-protected. Created with a GitHub App
# installation token (vars.OSS_REGEN_APP_ID + secrets.OSS_REGEN_APP_KEY)
# so `gh pr create` is not blocked by the org "Allow Actions to create
# PRs" restriction and the regen PR runs its own CI (an App token is a
# distinct actor, so its push/PR re-triggers checks; GITHUB_TOKEN's
# would not). No loop: this workflow is workflow_dispatch-only, and the
# PR only touches lockfiles, so merging it never re-fires this workflow.
# Falls back to GITHUB_TOKEN if the App is not configured (the step
# then degrades gracefully — see the else branch below).
- name: Open lockfile-regen PR
if: github.event_name != 'pull_request'
env:
GH_TOKEN: ${{ secrets.OSS_REGEN_TOKEN || secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ steps.app-token.outputs.token || secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
@@ -120,7 +133,9 @@ jobs:
git checkout -b "$BRANCH"
git add uv.lock ap-web/package-lock.json
git commit -m "chore(oss): regenerate public lockfiles against public PyPI/npm"
git push --force origin "$BRANCH"
# Push via the token (App, else GITHUB_TOKEN) so a refresh of an
# already-open PR re-triggers its CI on the synchronize event.
git push --force "https://x-access-token:${GH_TOKEN}@github.com/${REPO}.git" "$BRANCH"
# An already-open PR just picks up the force-pushed update.
if [ -n "$(gh pr list --head "$BRANCH" --state open --json number --jq '.[0].number // empty')" ]; then
echo "PR already open for $BRANCH — refreshed it with the latest lockfiles."