From f38b05a8dbbc4a90313ed1d837accaa943a44eac Mon Sep 17 00:00:00 2001 From: Ari Nguyen Date: Tue, 29 Sep 2026 09:54:52 -0700 Subject: [PATCH] ci(lint-pr): exempt the SDK automation App by bot user ID, not login The exemption for this repo's SDK automation App matched its bot by login, agentex-sdk-sync[bot]. A GitHub App bot's login follows the App's name, so when the App was renamed its bot became stainless-sync[bot] and the entry silently stopped matching: release pull requests went back to failing "Validate PR base branch" and drawing the "retarget to next" comment. The stale entry was also a small hole. The rename released the old slug, so anyone could register an App under it and that App's bot would have inherited the exemption. Match on the bot user's numeric ID instead, held once in a workflow-level SDK_AUTOMATION_BOT_ID and read by both checks. A user ID never changes and is never reused, so a future rename cannot break this and a new App reusing the name gets nothing. The old login is removed rather than kept alongside. The other exemptions stay login-based: stainless-app, release-please[bot], github-actions[bot] and dependabot[bot] are not ours to rename. Exercised both run blocks under bash -e against every author case: the renamed bot is exempt from both checks; a different App on the freed name is not; dependabot and human pull requests behave as before. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/lint-pr.yaml | 42 ++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/.github/workflows/lint-pr.yaml b/.github/workflows/lint-pr.yaml index 49a894981..bde91e1f3 100644 --- a/.github/workflows/lint-pr.yaml +++ b/.github/workflows/lint-pr.yaml @@ -10,6 +10,16 @@ on: - labeled - unlabeled +env: + # This repo's own SDK automation App's bot, exempt from both checks below. + # + # Matched by the bot user's numeric ID, not its login. A GitHub App bot's login + # follows the App's name, so renaming the App renamed its bot and silently broke + # the previous login-based entry. Worse, the old name was then free to register, + # so any App that took it would have inherited the exemption. A user ID never + # changes and is never reused. + SDK_AUTOMATION_BOT_ID: '333044712' + jobs: validate-pr-title: name: Validate PR title (Conventional Commits) @@ -19,22 +29,28 @@ jobs: env: PR_TITLE: ${{ github.event.pull_request.title }} PR_AUTHOR: ${{ github.event.pull_request.user.login }} + PR_AUTHOR_ID: ${{ github.event.pull_request.user.id }} run: | # Exempt automated PRs (Stainless codegen, release-please, dependabot, etc.). # These bots may not always emit Conventional-Commits-formatted titles # (dependabot's default "Bump foo from 1.0 to 1.1" doesn't match) and we # don't want their PRs blocked by this check. Mirrors validate-pr-base. # - # agentex-sdk-sync[bot] is this repo's own SDK automation. release-please - # runs here as a CLI under that App rather than as the release-please[bot] - # GitHub App, so its release pull requests are authored by - # agentex-sdk-sync[bot] and the entry above never matched them. Their - # titles come from release-please's configured pull-request-title-pattern, - # "release: ", which is not a Conventional Commits type and cannot - # be changed without also changing the string release-please parses back - # when it cuts the release. The same App opens the promote pull requests. + # This repo's own SDK automation App is exempt too, matched by ID through + # SDK_AUTOMATION_BOT_ID at the top of this file. release-please runs here as + # a CLI under that App rather than as the release-please[bot] GitHub App, so + # its release pull requests are authored by the App's bot and the list below + # never matched them. Their titles come from release-please's configured + # pull-request-title-pattern, which is not always a Conventional Commits type + # and cannot be changed without also changing the string release-please + # parses back when it cuts the release. The same App opens the promote pull + # requests. + if [ "$PR_AUTHOR_ID" = "$SDK_AUTOMATION_BOT_ID" ]; then + echo "PR is from this repo's SDK automation ($PR_AUTHOR); skipping title check." + exit 0 + fi case "$PR_AUTHOR" in - stainless-app|stainless-app\[bot\]|release-please\[bot\]|github-actions\[bot\]|dependabot\[bot\]|agentex-sdk-sync\[bot\]) + stainless-app|stainless-app\[bot\]|release-please\[bot\]|github-actions\[bot\]|dependabot\[bot\]) echo "PR is from automation ($PR_AUTHOR); skipping title check." exit 0 ;; @@ -74,6 +90,7 @@ jobs: REPO: ${{ github.repository }} PR_NUMBER: ${{ github.event.pull_request.number }} PR_AUTHOR: ${{ github.event.pull_request.user.login }} + PR_AUTHOR_ID: ${{ github.event.pull_request.user.id }} PR_BASE: ${{ github.event.pull_request.base.ref }} HAS_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'target-main') }} run: | @@ -101,8 +118,13 @@ jobs: fi # Exempt automated PRs (must mirror validate-pr-title's list). + if [ "$PR_AUTHOR_ID" = "$SDK_AUTOMATION_BOT_ID" ]; then + delete_comment + echo "PR is from this repo's SDK automation ($PR_AUTHOR); allowing PR targeting main." + exit 0 + fi case "$PR_AUTHOR" in - stainless-app|stainless-app\[bot\]|release-please\[bot\]|github-actions\[bot\]|dependabot\[bot\]|agentex-sdk-sync\[bot\]) + stainless-app|stainless-app\[bot\]|release-please\[bot\]|github-actions\[bot\]|dependabot\[bot\]) delete_comment echo "PR is from automation ($PR_AUTHOR); allowing PR targeting main." exit 0