fix(ci): use built-in token for runtime pin PRs (#3365)
This commit is contained in:
@@ -10,6 +10,14 @@ REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
WORKFLOW = REPO_ROOT / ".github" / "workflows" / "update-agent-runtime-pins.yml"
|
||||
LINT_WORKFLOW = REPO_ROOT / ".github" / "workflows" / "lint-action-pinning.yml"
|
||||
SCRIPT = REPO_ROOT / "scripts" / "update-agent-runtime-pins.mjs"
|
||||
REQUIRED_CHECK_WORKFLOWS = (
|
||||
"backend-tests.yml",
|
||||
"frontend-tests.yml",
|
||||
"e2e-tests.yml",
|
||||
"architecture-lint.yml",
|
||||
"lint-action-pinning.yml",
|
||||
"lint-harness-files.yml",
|
||||
)
|
||||
|
||||
|
||||
class ManagedRuntimePinWorkflowContractTest(unittest.TestCase):
|
||||
@@ -21,16 +29,15 @@ class ManagedRuntimePinWorkflowContractTest(unittest.TestCase):
|
||||
self.assertRegex(self.workflow, r"cron:\s*[\"']?[0-9]+ [0-9]+ \* \* [0-6][\"']?")
|
||||
self.assertIn("workflow_dispatch:", self.workflow)
|
||||
|
||||
def test_workflow_uses_least_privilege_and_dedicated_app_token(self) -> None:
|
||||
self.assertIn("permissions:\n contents: read", self.workflow)
|
||||
self.assertIn("permission-contents: write", self.workflow)
|
||||
self.assertIn("permission-pull-requests: write", self.workflow)
|
||||
self.assertNotIn("permissions:\n contents: write", self.workflow)
|
||||
self.assertNotIn("permissions:\n pull-requests: write", self.workflow)
|
||||
self.assertIn("MANAGED_RUNTIME_PIN_APP_PRIVATE_KEY", self.workflow)
|
||||
self.assertIn("MANAGED_RUNTIME_PIN_APP_CLIENT_ID", self.workflow)
|
||||
def test_workflow_uses_least_privilege_and_builtin_token(self) -> None:
|
||||
self.assertIn(
|
||||
"permissions:\n contents: write\n pull-requests: write", self.workflow
|
||||
)
|
||||
self.assertIn("GH_TOKEN: ${{ github.token }}", self.workflow)
|
||||
self.assertNotIn("actions/create-github-app-token@", self.workflow)
|
||||
self.assertNotIn("MANAGED_RUNTIME_PIN_APP_PRIVATE_KEY", self.workflow)
|
||||
self.assertNotIn("MANAGED_RUNTIME_PIN_APP_CLIENT_ID", self.workflow)
|
||||
self.assertNotIn("secrets.GITHUB_TOKEN", self.workflow)
|
||||
self.assertNotIn("GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}", self.workflow)
|
||||
|
||||
def test_workflow_groups_one_conventional_commit_pr_on_stable_branch(self) -> None:
|
||||
self.assertIn("update-managed-runtime-pins", self.workflow)
|
||||
@@ -47,7 +54,30 @@ class ManagedRuntimePinWorkflowContractTest(unittest.TestCase):
|
||||
push_start = self.workflow.index("- name: Commit and push one grouped change")
|
||||
pr_start = self.workflow.index("- name: Create or refresh the single grouped PR")
|
||||
push_step = self.workflow[push_start:pr_start]
|
||||
self.assertIn("GH_TOKEN: ${{ steps.app-token.outputs.token }}", push_step)
|
||||
pr_step = self.workflow[pr_start:]
|
||||
self.assertIn("GH_TOKEN: ${{ github.token }}", push_step)
|
||||
self.assertIn("GH_TOKEN: ${{ github.token }}", pr_step)
|
||||
|
||||
def test_workflow_dispatches_required_checks_for_builtin_token_prs(self) -> None:
|
||||
self.assertIn("actions: write", self.workflow)
|
||||
dispatch_start = self.workflow.index(
|
||||
"- name: Dispatch required checks for generated PR"
|
||||
)
|
||||
dispatch_step = self.workflow[dispatch_start:]
|
||||
self.assertIn("gh workflow run", dispatch_step)
|
||||
self.assertIn('--ref "$BOT_BRANCH"', dispatch_step)
|
||||
self.assertIn('--repo "$GITHUB_REPOSITORY"', dispatch_step)
|
||||
|
||||
for workflow_name in REQUIRED_CHECK_WORKFLOWS:
|
||||
self.assertIn(workflow_name, dispatch_step)
|
||||
workflow = (REPO_ROOT / ".github" / "workflows" / workflow_name).read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
self.assertRegex(
|
||||
workflow,
|
||||
r"(?m)^ workflow_dispatch:\s*$",
|
||||
f"{workflow_name} must accept explicit validation dispatches",
|
||||
)
|
||||
|
||||
def test_workflow_validates_before_any_commit_or_push(self) -> None:
|
||||
update_pos = self.workflow.index("node scripts/update-agent-runtime-pins.mjs")
|
||||
|
||||
@@ -7,6 +7,7 @@ on:
|
||||
branches: [main]
|
||||
merge_group:
|
||||
types: [checks_requested]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
# Pull requests can supersede one another safely. Pushes compare each commit
|
||||
@@ -57,6 +58,9 @@ jobs:
|
||||
# group's equivalent of the pull request's base.
|
||||
merge_group) BASE_REF="${MERGE_GROUP_BASE_SHA}" ;;
|
||||
push) BASE_REF="${PUSH_BEFORE}" ;;
|
||||
# The updater dispatches this workflow against its stable bot
|
||||
# branch, so the main branch fork point is the PR baseline.
|
||||
workflow_dispatch) BASE_REF="$(git merge-base HEAD origin/main)" ;;
|
||||
*) BASE_REF="" ;;
|
||||
esac
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ on:
|
||||
branches: [main]
|
||||
merge_group:
|
||||
types: [checks_requested]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
@@ -13,6 +13,7 @@ on:
|
||||
branches: [main]
|
||||
merge_group:
|
||||
types: [checks_requested]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
@@ -18,6 +18,7 @@ on:
|
||||
branches: [main]
|
||||
merge_group:
|
||||
types: [checks_requested]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
@@ -18,6 +18,7 @@ on:
|
||||
branches: [main]
|
||||
merge_group:
|
||||
types: [checks_requested]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
@@ -7,11 +7,13 @@ on:
|
||||
- cron: "17 6 * * 1"
|
||||
workflow_dispatch:
|
||||
|
||||
# The job mints a narrower GitHub App token only after checkout. The default
|
||||
# workflow token is never used to create the generated PR, so its events retain
|
||||
# the repository's normal CI behavior.
|
||||
# The job uses the repository-scoped workflow token for the branch, PR, and
|
||||
# explicit validation-workflow dispatches. A GITHUB_TOKEN-created push or PR
|
||||
# does not recursively trigger push/pull_request workflows.
|
||||
permissions:
|
||||
contents: read
|
||||
contents: write
|
||||
pull-requests: write
|
||||
actions: write
|
||||
|
||||
concurrency:
|
||||
group: managed-runtime-pin-update
|
||||
@@ -21,8 +23,6 @@ jobs:
|
||||
update:
|
||||
name: Open managed runtime pin PR
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
env:
|
||||
BOT_BRANCH: automation/update-managed-runtime-pins
|
||||
|
||||
@@ -42,18 +42,9 @@ jobs:
|
||||
- name: Test pin updater
|
||||
run: node --test scripts/update-agent-runtime-pins.test.mjs
|
||||
|
||||
- name: Create managed runtime pin App token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
with:
|
||||
client-id: ${{ vars.MANAGED_RUNTIME_PIN_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.MANAGED_RUNTIME_PIN_APP_PRIVATE_KEY }}
|
||||
permission-contents: write
|
||||
permission-pull-requests: write
|
||||
|
||||
- name: Configure git authentication
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: gh auth setup-git
|
||||
|
||||
- name: Prepare stable bot branch
|
||||
@@ -74,7 +65,7 @@ jobs:
|
||||
- name: Commit and push one grouped change
|
||||
if: steps.update.outputs.changed == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git config user.name "kandev-runtime-updater[bot]"
|
||||
@@ -89,7 +80,7 @@ jobs:
|
||||
- name: Create or refresh the single grouped PR
|
||||
if: steps.update.outputs.changed == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cat > /tmp/managed-runtime-pin-pr-body.md <<'EOF'
|
||||
@@ -113,3 +104,25 @@ jobs:
|
||||
--title "chore: update managed runtime pins" \
|
||||
--body-file /tmp/managed-runtime-pin-pr-body.md
|
||||
fi
|
||||
|
||||
# GITHUB_TOKEN-created push and pull-request events do not recursively
|
||||
# start validation workflows. Dispatch the six required checks explicitly
|
||||
# against the exact bot-branch commit after the PR exists.
|
||||
- name: Dispatch required checks for generated PR
|
||||
if: steps.update.outputs.changed == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
for workflow in \
|
||||
backend-tests.yml \
|
||||
frontend-tests.yml \
|
||||
e2e-tests.yml \
|
||||
architecture-lint.yml \
|
||||
lint-action-pinning.yml \
|
||||
lint-harness-files.yml
|
||||
do
|
||||
gh workflow run "$workflow" \
|
||||
--repo "$GITHUB_REPOSITORY" \
|
||||
--ref "$BOT_BRANCH"
|
||||
done
|
||||
|
||||
Reference in New Issue
Block a user