sessions: gate CI rerun button on valid run ID and add tests (#306362)

sessions: gate rerun button on valid run ID and add tests

Only show the Rerun Check action when the check's detailsUrl contains
a parseable GitHub Actions workflow run ID. Export parseWorkflowRunId
for reuse.

Add unit tests for rerunFailedJobs endpoint and parseWorkflowRunId.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Sandeep Somavarapu
2026-03-30 17:45:00 +02:00
committed by GitHub
parent 76258864c3
commit 9f149cd91b
4 changed files with 44 additions and 4 deletions

View File

@@ -22,7 +22,7 @@ import { ChatViewPaneTarget, IChatWidgetService } from '../../../../workbench/co
import { DEFAULT_LABELS_CONTAINER, IResourceLabel, ResourceLabels } from '../../../../workbench/browser/labels.js';
import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js';
import { GitHubCheckConclusion, GitHubCheckStatus, IGitHubCICheck } from '../../github/common/types.js';
import { GitHubPullRequestCIModel } from '../../github/browser/models/githubPullRequestCIModel.js';
import { GitHubPullRequestCIModel, parseWorkflowRunId } from '../../github/browser/models/githubPullRequestCIModel.js';
import { CICheckGroup, buildFixChecksPrompt, getCheckGroup, getCheckStateLabel, getFailedChecks } from './fixCIChecksAction.js';
const $ = dom.$;
@@ -105,7 +105,7 @@ class CICheckListRenderer implements IListRenderer<ICICheckListItem, ICICheckTem
const actions: Action[] = [];
if (element.group === CICheckGroup.Failed) {
if (element.group === CICheckGroup.Failed && parseWorkflowRunId(element.check.detailsUrl) !== undefined) {
actions.push(templateData.elementDisposables.add(new Action(
'ci.rerunCheck',
localize('ci.rerunCheck', "Rerun Check"),

View File

@@ -107,7 +107,7 @@ export class GitHubPullRequestCIModel extends Disposable {
* Extract the GitHub Actions workflow run ID from a check run's details URL.
* URLs follow the pattern: `https://github.com/{owner}/{repo}/actions/runs/{run_id}/job/{job_id}`
*/
function parseWorkflowRunId(detailsUrl: string | undefined): number | undefined {
export function parseWorkflowRunId(detailsUrl: string | undefined): number | undefined {
if (!detailsUrl) {
return undefined;
}

View File

@@ -304,6 +304,19 @@ suite('GitHubPRCIFetcher', () => {
assert.ok(result.includes('(TS2345)'));
assert.ok(result.includes('[warning] src/b.ts:5-8'));
});
test('rerunFailedJobs sends POST to correct endpoint', async () => {
mockApi.setNextResponse(undefined);
await fetcher.rerunFailedJobs('myOwner', 'myRepo', 12345);
assert.strictEqual(mockApi.requestCalls.length, 1);
assert.deepStrictEqual(mockApi.requestCalls[0], {
method: 'POST',
path: '/repos/myOwner/myRepo/actions/runs/12345/rerun-failed-jobs',
body: undefined,
});
});
});
suite('computeOverallCIStatus', () => {

View File

@@ -8,7 +8,7 @@ import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/tes
import { DisposableStore } from '../../../../../base/common/lifecycle.js';
import { NullLogService } from '../../../../../platform/log/common/log.js';
import { GitHubPullRequestModel } from '../../browser/models/githubPullRequestModel.js';
import { GitHubPullRequestCIModel } from '../../browser/models/githubPullRequestCIModel.js';
import { GitHubPullRequestCIModel, parseWorkflowRunId } from '../../browser/models/githubPullRequestCIModel.js';
import { GitHubRepositoryModel } from '../../browser/models/githubRepositoryModel.js';
import { GitHubPRFetcher } from '../../browser/fetchers/githubPRFetcher.js';
import { GitHubPRCIFetcher } from '../../browser/fetchers/githubPRCIFetcher.js';
@@ -231,6 +231,33 @@ suite('GitHubPullRequestCIModel', () => {
});
});
suite('parseWorkflowRunId', () => {
ensureNoDisposablesAreLeakedInTestSuite();
test('extracts run ID from GitHub Actions URL', () => {
assert.strictEqual(
parseWorkflowRunId('https://github.com/microsoft/vscode/actions/runs/12345/job/67890'),
12345,
);
});
test('extracts run ID from URL without job segment', () => {
assert.strictEqual(
parseWorkflowRunId('https://github.com/owner/repo/actions/runs/99999'),
99999,
);
});
test('returns undefined for non-Actions URL', () => {
assert.strictEqual(parseWorkflowRunId('https://example.com/check/1'), undefined);
});
test('returns undefined for undefined input', () => {
assert.strictEqual(parseWorkflowRunId(undefined), undefined);
});
});
//#region Test Helpers