From fdb22392c6b38fc0124075f9154a52c3155a5b33 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 28 Apr 2026 23:02:14 +0000 Subject: [PATCH] feat: trigger chromium-upgrade workflow on chromium main rolls After a successful Chromium roll PR is opened or updated against electron/electron main, dispatch the chromium-upgrade workflow in electron/agent-workflows so the agent can pick up the new roll state. The workflow's concurrency group cancels older runs, so re-dispatching on every roll update is safe. --- src/constants.ts | 7 +++ src/utils/roll.ts | 21 +++++++++ tests/utils/roll.spec.ts | 93 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 120 insertions(+), 1 deletion(-) diff --git a/src/constants.ts b/src/constants.ts index f72711c..6f759e3 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -21,6 +21,13 @@ export const REPO_OWNER = 'electron'; export const MAIN_BRANCH = 'main'; +export const CHROMIUM_UPGRADE_WORKFLOW = { + owner: 'electron', + repo: 'agent-workflows', + workflow_id: 'chromium-upgrade.yml', + ref: 'main', +}; + export const ROLL_TARGETS = { node: { name: 'node', diff --git a/src/utils/roll.ts b/src/utils/roll.ts index d16ccfc..0e9062e 100644 --- a/src/utils/roll.ts +++ b/src/utils/roll.ts @@ -3,6 +3,7 @@ import * as semver from 'semver'; import { BACKPORT_CHECK_SKIP, + CHROMIUM_UPGRADE_WORKFLOW, MAIN_BRANCH, NO_BACKPORT, REPOS, @@ -54,6 +55,16 @@ async function updateLabels( await addLabels(octokit, { prNumber, labels }); } +async function triggerChromiumUpgradeWorkflow(octokit: Octokit) { + const d = debug('roller/chromium:triggerChromiumUpgradeWorkflow()'); + try { + await octokit.actions.createWorkflowDispatch(CHROMIUM_UPGRADE_WORKFLOW); + d(`Dispatched ${CHROMIUM_UPGRADE_WORKFLOW.workflow_id}`); + } catch (e) { + d(`Failed to dispatch ${CHROMIUM_UPGRADE_WORKFLOW.workflow_id}: ${e.message}`); + } +} + export async function roll({ rollTarget, electronBranch, @@ -66,6 +77,8 @@ export async function roll({ `roll triggered for electron branch=${electronBranch.name} ${rollTarget.depsKey}=${targetVersion}`, ); + let didRoll = false; + // 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, @@ -131,6 +144,8 @@ export async function roll({ previousVersion: prVersionText[1], prNumber: pr.number, }); + + didRoll = true; } } else { d(`No existing PR found - raising a new PR`); @@ -185,5 +200,11 @@ export async function roll({ }); d(`New PR: ${newPr.data.html_url}`); + + didRoll = true; + } + + if (didRoll && rollTarget === ROLL_TARGETS.chromium && electronBranch.name === MAIN_BRANCH) { + await triggerChromiumUpgradeWorkflow(github); } } diff --git a/tests/utils/roll.spec.ts b/tests/utils/roll.spec.ts index 97a004f..0e21c56 100644 --- a/tests/utils/roll.spec.ts +++ b/tests/utils/roll.spec.ts @@ -2,7 +2,12 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { roll } from '../../src/utils/roll.js'; import { getOctokit } from '../../src/utils/octokit.js'; -import { ROLL_TARGETS, REPOS } from '../../src/constants.js'; +import { + CHROMIUM_UPGRADE_WORKFLOW, + MAIN_BRANCH, + REPOS, + ROLL_TARGETS, +} from '../../src/constants.js'; import { updateDepsFile } from '../../src/utils/update-deps.js'; vi.mock('../../src/utils/octokit.js'); @@ -44,6 +49,9 @@ describe('roll()', () => { addLabels: vi.fn(), listLabelsOnIssue: vi.fn().mockReturnValue({ data: [] }), }, + actions: { + createWorkflowDispatch: vi.fn(), + }, }; vi.mocked(getOctokit).mockReturnValue(mockOctokit); vi.mocked(updateDepsFile).mockResolvedValue({ @@ -177,6 +185,89 @@ describe('roll()', () => { ); }); + describe('chromium-upgrade workflow dispatch', () => { + const mainBranch = { ...branch, name: MAIN_BRANCH }; + + it('dispatches when creating a new chromium PR on main', async () => { + mockOctokit.paginate.mockReturnValue([]); + + await roll({ + rollTarget: ROLL_TARGETS.chromium, + electronBranch: mainBranch, + targetVersion: '120.0.0.0', + }); + + expect(mockOctokit.actions.createWorkflowDispatch).toHaveBeenCalledTimes(1); + expect(mockOctokit.actions.createWorkflowDispatch).toHaveBeenCalledWith( + CHROMIUM_UPGRADE_WORKFLOW, + ); + }); + + it('dispatches when updating an existing chromium PR on main', async () => { + mockOctokit.paginate.mockReturnValue([ + { + user: { login: 'electron-roller[bot]' }, + title: `chore: bump ${ROLL_TARGETS.chromium.name} to bar`, + number: 1, + head: { ref: 'asd' }, + body: 'Original-Version: 119.0.0.0', + labels: [], + created_at: new Date().toISOString(), + }, + ]); + + await roll({ + rollTarget: ROLL_TARGETS.chromium, + electronBranch: mainBranch, + targetVersion: '120.0.0.0', + }); + + expect(mockOctokit.actions.createWorkflowDispatch).toHaveBeenCalledTimes(1); + expect(mockOctokit.actions.createWorkflowDispatch).toHaveBeenCalledWith( + CHROMIUM_UPGRADE_WORKFLOW, + ); + }); + + it('does not dispatch for node rolls on main', async () => { + mockOctokit.paginate.mockReturnValue([]); + + await roll({ + rollTarget: ROLL_TARGETS.node, + electronBranch: mainBranch, + targetVersion: 'v10.0.0', + }); + + expect(mockOctokit.actions.createWorkflowDispatch).not.toHaveBeenCalled(); + }); + + it('does not dispatch for chromium rolls on a release branch', async () => { + mockOctokit.paginate.mockReturnValue([]); + + await roll({ + rollTarget: ROLL_TARGETS.chromium, + electronBranch: branch, + targetVersion: '120.0.0.0', + }); + + expect(mockOctokit.actions.createWorkflowDispatch).not.toHaveBeenCalled(); + }); + + it('does not throw when dispatch itself fails', async () => { + mockOctokit.paginate.mockReturnValue([]); + mockOctokit.actions.createWorkflowDispatch.mockRejectedValueOnce(new Error('boom')); + + await expect( + roll({ + rollTarget: ROLL_TARGETS.chromium, + electronBranch: mainBranch, + targetVersion: '120.0.0.0', + }), + ).resolves.toBeUndefined(); + + expect(mockOctokit.pulls.create).toHaveBeenCalled(); + }); + }); + it('skips PR if existing one has been paused', async () => { mockOctokit.paginate.mockReturnValue([ {