From a8e7bc69036547dc5688c4fa7cc3bb9b44c2a34c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 18 Sep 2026 15:53:53 +0000 Subject: [PATCH] feat: dispatch chromium-upgrade workflow for release-branch Chromium rolls Release-branch Chromium roll PRs (roller/chromium/N-x-y) previously never triggered the electron/agent-workflows chromium-upgrade.yml workflow, which only ran for main and always against its default roller/chromium/main base. Every Chromium roll that changes DEPS now dispatches the workflow with the roll's own branch as the base-ref input, so release branches get the same agent-driven upgrade as main. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01SmvfDMwrFWz6GBTVqJjs9h --- src/utils/roll.ts | 40 +++++++++++++--------- tests/utils/roll.spec.ts | 74 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 91 insertions(+), 23 deletions(-) diff --git a/src/utils/roll.ts b/src/utils/roll.ts index bd28237..16903ca 100644 --- a/src/utils/roll.ts +++ b/src/utils/roll.ts @@ -152,13 +152,21 @@ async function updateLabels( return coveredBranches; } -async function triggerChromiumUpgradeWorkflow(octokit: Octokit) { +// Dispatches the agent-driven Chromium upgrade workflow against the given +// roll branch (`roller/chromium/`), for main and release +// branches alike. +async function triggerChromiumUpgradeWorkflow(octokit: Octokit, rollBranchName: string) { const d = debug('roller/chromium:triggerChromiumUpgradeWorkflow()'); try { - await octokit.actions.createWorkflowDispatch(CHROMIUM_UPGRADE_WORKFLOW); - d(`Dispatched ${CHROMIUM_UPGRADE_WORKFLOW.workflow_id}`); + await octokit.actions.createWorkflowDispatch({ + ...CHROMIUM_UPGRADE_WORKFLOW, + inputs: { 'base-ref': rollBranchName }, + }); + d(`Dispatched ${CHROMIUM_UPGRADE_WORKFLOW.workflow_id} for ${rollBranchName}`); } catch (e) { - d(`Failed to dispatch ${CHROMIUM_UPGRADE_WORKFLOW.workflow_id}: ${e.message}`); + d( + `Failed to dispatch ${CHROMIUM_UPGRADE_WORKFLOW.workflow_id} for ${rollBranchName}: ${e.message}`, + ); } } @@ -181,6 +189,10 @@ export async function roll({ let didRoll = false; let coveredBranches: string[] = []; + // The bot only ever rolls into the branch it created itself, which it names + // `roller//` in the electron/electron repo. + const rollBranchName = `roller/${rollTarget.name}/${electronBranch.name}`; + // Look for a pre-existing PR that targets this branch to see if we can update that. const existingPrsForBranch = (await github.paginate('GET /repos/:owner/:repo/pulls', { base: electronBranch.name, @@ -193,11 +205,8 @@ export async function roll({ ); if (prs.length) { - // The bot only ever rolls into the branch it created itself, which it names - // `roller//` in the electron/electron repo. Derive - // the write target solely from this trusted naming rather than from any - // field of the (potentially untrusted) PR. - const rollBranchName = `roller/${rollTarget.name}/${electronBranch.name}`; + // Derive the write target solely from the bot's trusted roll branch naming + // rather than from any field of the (potentially untrusted) PR. const electronRepoFullName = `${REPOS.electron.owner}/${REPOS.electron.repo}`; // Update existing PR(s) @@ -291,8 +300,7 @@ export async function roll({ } else { d(`No existing PR found - raising a new PR`); const sha = electronBranch.commit.sha; - const branchName = `roller/${rollTarget.name}/${electronBranch.name}`; - const shortRef = `heads/${branchName}`; + const shortRef = `heads/${rollBranchName}`; const ref = `refs/${shortRef}`; d(`Checking that no orphan ref exists from a previous roll`); @@ -315,16 +323,16 @@ export async function roll({ const { previousDEPSVersion } = await updateDepsFile({ depName: rollTarget.name, depKey: rollTarget.depsKey, - branch: branchName, + branch: rollBranchName, targetVersion, }); // Raise a PR - d(`Raising a PR for ${branchName} to ${electronBranch.name}`); + d(`Raising a PR for ${rollBranchName} to ${electronBranch.name}`); const newPr = await github.pulls.create({ ...REPOS.electron, base: electronBranch.name, - head: `${REPOS.electron.owner}:${branchName}`, + head: `${REPOS.electron.owner}:${rollBranchName}`, ...getPRText(rollTarget, { previousVersion: previousDEPSVersion, newVersion: targetVersion, @@ -349,8 +357,8 @@ export async function roll({ didRoll = true; } - if (didRoll && rollTarget === ROLL_TARGETS.chromium && electronBranch.name === MAIN_BRANCH) { - await triggerChromiumUpgradeWorkflow(github); + if (didRoll && rollTarget === ROLL_TARGETS.chromium) { + await triggerChromiumUpgradeWorkflow(github, rollBranchName); } return coveredBranches; diff --git a/tests/utils/roll.spec.ts b/tests/utils/roll.spec.ts index d6422fe..05800b2 100644 --- a/tests/utils/roll.spec.ts +++ b/tests/utils/roll.spec.ts @@ -300,9 +300,10 @@ describe('roll()', () => { }); expect(mockOctokit.actions.createWorkflowDispatch).toHaveBeenCalledTimes(1); - expect(mockOctokit.actions.createWorkflowDispatch).toHaveBeenCalledWith( - CHROMIUM_UPGRADE_WORKFLOW, - ); + expect(mockOctokit.actions.createWorkflowDispatch).toHaveBeenCalledWith({ + ...CHROMIUM_UPGRADE_WORKFLOW, + inputs: { 'base-ref': `roller/chromium/${MAIN_BRANCH}` }, + }); }); it('dispatches when updating an existing chromium PR on main', async () => { @@ -328,9 +329,10 @@ describe('roll()', () => { }); expect(mockOctokit.actions.createWorkflowDispatch).toHaveBeenCalledTimes(1); - expect(mockOctokit.actions.createWorkflowDispatch).toHaveBeenCalledWith( - CHROMIUM_UPGRADE_WORKFLOW, - ); + expect(mockOctokit.actions.createWorkflowDispatch).toHaveBeenCalledWith({ + ...CHROMIUM_UPGRADE_WORKFLOW, + inputs: { 'base-ref': `roller/chromium/${MAIN_BRANCH}` }, + }); }); it('does not dispatch for node rolls on main', async () => { @@ -345,7 +347,7 @@ describe('roll()', () => { expect(mockOctokit.actions.createWorkflowDispatch).not.toHaveBeenCalled(); }); - it('does not dispatch for chromium rolls on a release branch', async () => { + it('dispatches for chromium rolls on a release branch with that roll branch as base-ref', async () => { mockOctokit.paginate.mockReturnValue([]); await roll({ @@ -354,6 +356,64 @@ describe('roll()', () => { targetVersion: '120.0.0.0', }); + expect(mockOctokit.actions.createWorkflowDispatch).toHaveBeenCalledTimes(1); + expect(mockOctokit.actions.createWorkflowDispatch).toHaveBeenCalledWith({ + ...CHROMIUM_UPGRADE_WORKFLOW, + inputs: { 'base-ref': `roller/chromium/${branch.name}` }, + }); + }); + + it('does not dispatch when the existing chromium PR is paused', async () => { + mockOctokit.paginate.mockReturnValue([ + { + user: { login: 'electron-roller[bot]' }, + title: `chore: bump ${ROLL_TARGETS.chromium.name} to bar`, + number: 1, + head: { + ref: `roller/${ROLL_TARGETS.chromium.name}/${mainBranch.name}`, + repo: { full_name: `${REPOS.electron.owner}/${REPOS.electron.repo}` }, + }, + body: 'Original-Version: 119.0.0.0', + labels: [{ name: 'roller/pause' }], + created_at: new Date().toISOString(), + }, + ]); + + await roll({ + rollTarget: ROLL_TARGETS.chromium, + electronBranch: mainBranch, + targetVersion: '120.0.0.0', + }); + + expect(mockOctokit.actions.createWorkflowDispatch).not.toHaveBeenCalled(); + }); + + it('does not dispatch when the DEPS version is unchanged', async () => { + mockOctokit.paginate.mockReturnValue([ + { + user: { login: 'electron-roller[bot]' }, + title: `chore: bump ${ROLL_TARGETS.chromium.name} to bar`, + number: 1, + head: { + ref: `roller/${ROLL_TARGETS.chromium.name}/${mainBranch.name}`, + repo: { full_name: `${REPOS.electron.owner}/${REPOS.electron.repo}` }, + }, + body: 'Original-Version: 119.0.0.0', + labels: [], + created_at: new Date().toISOString(), + }, + ]); + vi.mocked(updateDepsFile).mockResolvedValue({ + previousDEPSVersion: '120.0.0.0', + newDEPSVersion: '120.0.0.0', + }); + + await roll({ + rollTarget: ROLL_TARGETS.chromium, + electronBranch: mainBranch, + targetVersion: '120.0.0.0', + }); + expect(mockOctokit.actions.createWorkflowDispatch).not.toHaveBeenCalled(); });