From e3e2eec9bb191aaf15f46e5d394cba936106b0b4 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 5 Jan 2026 13:48:19 +0000 Subject: [PATCH 1/5] fix: cancel running CI workflow before re-triggering and allow trusted bots Co-Authored-By: keith@cal.com --- .github/workflows/run-ci.yml | 76 +++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 15 deletions(-) diff --git a/.github/workflows/run-ci.yml b/.github/workflows/run-ci.yml index 452de59ab5e111..27906c73069bd2 100644 --- a/.github/workflows/run-ci.yml +++ b/.github/workflows/run-ci.yml @@ -21,19 +21,26 @@ jobs: const adder = context.payload.sender.login; const pr = context.payload.pull_request; - // Verify label adder has write access - const { data: perm } = await github.rest.repos.getCollaboratorPermissionLevel({ - owner: context.repo.owner, - repo: context.repo.repo, - username: adder, - }); + // Trusted bots that can add labels to trigger CI + const trustedBots = ['graphite-app[bot]']; - if (!['admin', 'maintain', 'write'].includes(perm.permission)) { - core.setFailed(`${adder} does not have write access`); - return; - } + // Verify label adder has write access (skip for trusted bots) + if (!trustedBots.includes(adder)) { + const { data: perm } = await github.rest.repos.getCollaboratorPermissionLevel({ + owner: context.repo.owner, + repo: context.repo.repo, + username: adder, + }); + + if (!['admin', 'maintain', 'write'].includes(perm.permission)) { + core.setFailed(`${adder} does not have write access`); + return; + } - console.log(`Label added by ${adder} (${perm.permission})`); + console.log(`Label added by ${adder} (${perm.permission})`); + } else { + console.log(`Label added by trusted bot: ${adder}`); + } // Find the latest pr.yml run for this PR's head SHA const { data: runs } = await github.rest.actions.listWorkflowRuns({ @@ -56,13 +63,52 @@ jobs: const latestRun = matchingRuns[0]; - // Check if workflow is still running - can't re-run in-progress workflows + // If workflow is still running, cancel it first then re-run if (latestRun.status === 'in_progress' || latestRun.status === 'queued') { - core.setFailed(`Workflow is still running (status: ${latestRun.status}). Wait for it to complete or cancel it first.`); - return; + console.log(`Workflow is running (status: ${latestRun.status}). Cancelling it first...`); + + await github.rest.actions.cancelWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: latestRun.id, + }); + + // Wait for the workflow to be cancelled (poll with timeout) + const maxWaitTime = 60000; // 60 seconds + const pollInterval = 2000; // 2 seconds + const startTime = Date.now(); + + while (Date.now() - startTime < maxWaitTime) { + await new Promise(resolve => setTimeout(resolve, pollInterval)); + + const { data: updatedRun } = await github.rest.actions.getWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: latestRun.id, + }); + + if (updatedRun.status === 'completed') { + console.log(`Workflow cancelled successfully (conclusion: ${updatedRun.conclusion})`); + break; + } + + console.log(`Waiting for workflow to cancel... (status: ${updatedRun.status})`); + } + + // Check final status + const { data: finalRun } = await github.rest.actions.getWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: latestRun.id, + }); + + if (finalRun.status !== 'completed') { + core.setFailed(`Timed out waiting for workflow to cancel (status: ${finalRun.status})`); + return; + } } - console.log(`Re-running workflow ${latestRun.id} (was: ${latestRun.conclusion})`); + console.log(`Re-running workflow ${latestRun.id} (was: ${latestRun.conclusion || latestRun.status})`); // Re-run preserves original context (PR, SHA, etc.) await github.rest.actions.reRunWorkflow({ From 188983111cb0f3242523e9e364fafd235e10d696 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 5 Jan 2026 14:10:11 +0000 Subject: [PATCH 2/5] fix: remove hardcoded bot allowlist, keep only cancel-and-rerun improvement Co-Authored-By: keith@cal.com --- .github/workflows/run-ci.yml | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/.github/workflows/run-ci.yml b/.github/workflows/run-ci.yml index 27906c73069bd2..c629f32929c907 100644 --- a/.github/workflows/run-ci.yml +++ b/.github/workflows/run-ci.yml @@ -21,27 +21,20 @@ jobs: const adder = context.payload.sender.login; const pr = context.payload.pull_request; - // Trusted bots that can add labels to trigger CI - const trustedBots = ['graphite-app[bot]']; - - // Verify label adder has write access (skip for trusted bots) - if (!trustedBots.includes(adder)) { - const { data: perm } = await github.rest.repos.getCollaboratorPermissionLevel({ - owner: context.repo.owner, - repo: context.repo.repo, - username: adder, - }); - - if (!['admin', 'maintain', 'write'].includes(perm.permission)) { - core.setFailed(`${adder} does not have write access`); - return; - } + // Verify label adder has write access + const { data: perm } = await github.rest.repos.getCollaboratorPermissionLevel({ + owner: context.repo.owner, + repo: context.repo.repo, + username: adder, + }); - console.log(`Label added by ${adder} (${perm.permission})`); - } else { - console.log(`Label added by trusted bot: ${adder}`); + if (!['admin', 'maintain', 'write'].includes(perm.permission)) { + core.setFailed(`${adder} does not have write access`); + return; } + console.log(`Label added by ${adder} (${perm.permission})`); + // Find the latest pr.yml run for this PR's head SHA const { data: runs } = await github.rest.actions.listWorkflowRuns({ owner: context.repo.owner, From 0d33e90c09ccea876c31672ca3a47f1ec3be905b Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 5 Jan 2026 14:15:29 +0000 Subject: [PATCH 3/5] fix: add app_id verification for trusted GitHub Apps (Graphite) Co-Authored-By: keith@cal.com --- .github/workflows/run-ci.yml | 51 +++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/.github/workflows/run-ci.yml b/.github/workflows/run-ci.yml index c629f32929c907..ff638b3a234441 100644 --- a/.github/workflows/run-ci.yml +++ b/.github/workflows/run-ci.yml @@ -19,21 +19,52 @@ jobs: with: script: | const adder = context.payload.sender.login; + const senderType = context.payload.sender.type; const pr = context.payload.pull_request; + const installation = context.payload.installation; - // Verify label adder has write access - const { data: perm } = await github.rest.repos.getCollaboratorPermissionLevel({ - owner: context.repo.owner, - repo: context.repo.repo, - username: adder, - }); + // Trusted GitHub App IDs that can add labels to trigger CI + // Graphite App: https://github.com/apps/graphite-app + const GRAPHITE_APP_ID = 15497; + const trustedAppIds = [GRAPHITE_APP_ID]; - if (!['admin', 'maintain', 'write'].includes(perm.permission)) { - core.setFailed(`${adder} does not have write access`); - return; + let isAuthorized = false; + + // Check if this is a GitHub App (Bot) with an installation context + if (senderType === 'Bot' && installation?.id) { + try { + // Get the installation details to verify the app_id + const { data: installationData } = await github.rest.apps.getInstallation({ + installation_id: installation.id, + }); + + if (trustedAppIds.includes(installationData.app_id)) { + console.log(`Label added by trusted GitHub App: ${adder} (app_id: ${installationData.app_id})`); + isAuthorized = true; + } else { + console.log(`GitHub App ${adder} (app_id: ${installationData.app_id}) is not in the trusted list`); + } + } catch (error) { + console.log(`Could not verify GitHub App installation: ${error.message}`); + // Fall through to human permission check + } } - console.log(`Label added by ${adder} (${perm.permission})`); + // If not authorized by app check, verify human has write access + if (!isAuthorized) { + const { data: perm } = await github.rest.repos.getCollaboratorPermissionLevel({ + owner: context.repo.owner, + repo: context.repo.repo, + username: adder, + }); + + if (!['admin', 'maintain', 'write'].includes(perm.permission)) { + core.setFailed(`${adder} does not have write access`); + return; + } + + console.log(`Label added by ${adder} (${perm.permission})`); + } // Find the latest pr.yml run for this PR's head SHA const { data: runs } = await github.rest.actions.listWorkflowRuns({ From caf0771c60147b59c7367fbf7e4fdbaf6ad7f499 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 5 Jan 2026 14:41:24 +0000 Subject: [PATCH 4/5] fix: simplify trusted bot check to use sender type, login, and installation context Co-Authored-By: keith@cal.com --- .github/workflows/run-ci.yml | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/.github/workflows/run-ci.yml b/.github/workflows/run-ci.yml index ff638b3a234441..21298d0271f55d 100644 --- a/.github/workflows/run-ci.yml +++ b/.github/workflows/run-ci.yml @@ -23,31 +23,19 @@ jobs: const pr = context.payload.pull_request; const installation = context.payload.installation; - // Trusted GitHub App IDs that can add labels to trigger CI - // Graphite App: https://github.com/apps/graphite-app - const GRAPHITE_APP_ID = 15497; - const trustedAppIds = [GRAPHITE_APP_ID]; + // Trusted GitHub App bot logins that can add labels to trigger CI + // These are verified by checking sender.type === 'Bot' AND presence of installation context + // The installation object is GitHub-generated and cannot be forged by contributors + const trustedBotLogins = ['graphite-app[bot]']; let isAuthorized = false; - // Check if this is a GitHub App (Bot) with an installation context - if (senderType === 'Bot' && installation?.id) { - try { - // Get the installation details to verify the app_id - const { data: installationData } = await github.rest.apps.getInstallation({ - installation_id: installation.id, - }); - - if (trustedAppIds.includes(installationData.app_id)) { - console.log(`Label added by trusted GitHub App: ${adder} (app_id: ${installationData.app_id})`); - isAuthorized = true; - } else { - console.log(`GitHub App ${adder} (app_id: ${installationData.app_id}) is not in the trusted list`); - } - } catch (error) { - console.log(`Could not verify GitHub App installation: ${error.message}`); - // Fall through to human permission check - } + // Check if this is a trusted GitHub App (Bot) with an installation context + // The combination of sender.type === 'Bot' + installation context + known bot login + // provides sufficient verification since the payload comes from GitHub + if (senderType === 'Bot' && installation && trustedBotLogins.includes(adder)) { + console.log(`Label added by trusted GitHub App: ${adder}`); + isAuthorized = true; } // If not authorized by app check, verify human has write access From 69f938935b7f96a27b0fd5945f68ad0f356253c5 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 5 Jan 2026 14:43:12 +0000 Subject: [PATCH 5/5] chore: remove unnecessary comments Co-Authored-By: keith@cal.com --- .github/workflows/run-ci.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/run-ci.yml b/.github/workflows/run-ci.yml index 21298d0271f55d..1d96a65ce2bc48 100644 --- a/.github/workflows/run-ci.yml +++ b/.github/workflows/run-ci.yml @@ -23,22 +23,14 @@ jobs: const pr = context.payload.pull_request; const installation = context.payload.installation; - // Trusted GitHub App bot logins that can add labels to trigger CI - // These are verified by checking sender.type === 'Bot' AND presence of installation context - // The installation object is GitHub-generated and cannot be forged by contributors const trustedBotLogins = ['graphite-app[bot]']; - let isAuthorized = false; - // Check if this is a trusted GitHub App (Bot) with an installation context - // The combination of sender.type === 'Bot' + installation context + known bot login - // provides sufficient verification since the payload comes from GitHub if (senderType === 'Bot' && installation && trustedBotLogins.includes(adder)) { console.log(`Label added by trusted GitHub App: ${adder}`); isAuthorized = true; } - // If not authorized by app check, verify human has write access if (!isAuthorized) { const { data: perm } = await github.rest.repos.getCollaboratorPermissionLevel({ owner: context.repo.owner,