Path: blob/main/src/vs/sessions/contrib/changes/browser/checksActions.ts
13401 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Codicon } from '../../../../base/common/codicons.js';6import { Disposable } from '../../../../base/common/lifecycle.js';7import { derived } from '../../../../base/common/observable.js';8import { localize, localize2 } from '../../../../nls.js';9import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js';10import { ContextKeyExpr, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js';11import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';12import { ILogService } from '../../../../platform/log/common/log.js';13import { bindContextKey } from '../../../../platform/observable/common/platformObservableUtils.js';14import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../../workbench/common/contributions.js';15import { IsSessionsWindowContext } from '../../../../workbench/common/contextkeys.js';16import { ChatViewPaneTarget, IChatWidgetService } from '../../../../workbench/contrib/chat/browser/chat.js';17import { ChatContextKeys } from '../../../../workbench/contrib/chat/common/actions/chatContextKeys.js';18import { CHAT_CATEGORY } from '../../../../workbench/contrib/chat/browser/actions/chatActions.js';19import { IGitHubService } from '../../github/browser/githubService.js';20import { GitHubCheckConclusion, GitHubCheckStatus, IGitHubCICheck } from '../../github/common/types.js';21import { ISessionsManagementService } from '../../../services/sessions/common/sessionsManagement.js';2223export const hasActiveSessionFailedCIChecks = new RawContextKey<boolean>('sessions.hasActiveSessionFailedCIChecks', false);2425// --- Shared CI check utilities ------------------------------------------------2627export const enum CICheckGroup {28Running,29Pending,30Failed,31Successful,32}3334export function isFailedConclusion(conclusion: GitHubCheckConclusion | undefined): boolean {35return conclusion === GitHubCheckConclusion.Failure36|| conclusion === GitHubCheckConclusion.TimedOut37|| conclusion === GitHubCheckConclusion.ActionRequired;38}3940export function getCheckGroup(check: IGitHubCICheck): CICheckGroup {41switch (check.status) {42case GitHubCheckStatus.InProgress:43return CICheckGroup.Running;44case GitHubCheckStatus.Queued:45return CICheckGroup.Pending;46case GitHubCheckStatus.Completed:47return isFailedConclusion(check.conclusion) ? CICheckGroup.Failed : CICheckGroup.Successful;48}49}5051export function getCheckStateLabel(check: IGitHubCICheck): string {52switch (getCheckGroup(check)) {53case CICheckGroup.Running:54return localize('ci.runningState', "running");55case CICheckGroup.Pending:56return localize('ci.pendingState', "pending");57case CICheckGroup.Failed:58return localize('ci.failedState', "failed");59case CICheckGroup.Successful:60return localize('ci.successfulState', "successful");61}62}6364export function getFailedChecks(checks: readonly IGitHubCICheck[]): readonly IGitHubCICheck[] {65return checks.filter(check => getCheckGroup(check) === CICheckGroup.Failed);66}6768export function buildFixChecksPrompt(failedChecks: ReadonlyArray<{ check: IGitHubCICheck; annotations: string }>): string {69const sections = failedChecks.map(({ check, annotations }) => {70const parts = [71`Check: ${check.name}`,72`Status: ${getCheckStateLabel(check)}`,73`Conclusion: ${check.conclusion ?? 'unknown'}`,74];7576if (check.detailsUrl) {77parts.push(`Details: ${check.detailsUrl}`);78}7980parts.push('', 'Annotations and output:', annotations || 'No output available for this check run.');81return parts.join('\n');82});8384return [85'Please fix the failed CI checks for this session immediately.',86'Use the failed check information below, including annotations and check output, to identify the root causes and make the necessary code changes.',87'Focus on resolving these CI failures. Avoid unrelated changes unless they are required to fix the checks.',88'',89'Failed CI checks:',90'',91sections.join('\n\n---\n\n'),92].join('\n');93}9495/**96* Sets the `hasActiveSessionFailedCIChecks` context key to true when the97* active session has a PR with CI checks and at least one has failed.98*/99class ActiveSessionFailedCIChecksContextContribution extends Disposable implements IWorkbenchContribution {100101static readonly ID = 'workbench.contrib.activeSessionFailedCIChecksContext';102103constructor(104@IContextKeyService contextKeyService: IContextKeyService,105@ISessionsManagementService sessionManagementService: ISessionsManagementService,106@IGitHubService gitHubService: IGitHubService,107) {108super();109110const ciModelObs = derived(this, reader => {111const session = sessionManagementService.activeSession.read(reader);112if (!session) {113return undefined;114}115const gitHubInfo = session.gitHubInfo.read(reader);116if (!gitHubInfo?.pullRequest) {117return undefined;118}119const prModel = gitHubService.getPullRequest(gitHubInfo.owner, gitHubInfo.repo, gitHubInfo.pullRequest.number);120const pr = prModel.pullRequest.read(reader);121if (!pr) {122return undefined;123}124return gitHubService.getPullRequestCI(gitHubInfo.owner, gitHubInfo.repo, gitHubInfo.pullRequest.number, pr.headSha);125});126127this._register(bindContextKey(hasActiveSessionFailedCIChecks, contextKeyService, reader => {128const ciModel = ciModelObs.read(reader);129if (!ciModel) {130return false;131}132const checks = ciModel.checks.read(reader);133return getFailedChecks(checks).length > 0;134}));135}136}137138class FixCIChecksAction extends Action2 {139140static readonly ID = 'sessions.action.fixCIChecks';141142constructor() {143super({144id: FixCIChecksAction.ID,145title: localize2('fixCIChecks', 'Fix CI Checks'),146icon: Codicon.lightbulbAutofix,147category: CHAT_CATEGORY,148precondition: ContextKeyExpr.and(ChatContextKeys.enabled, hasActiveSessionFailedCIChecks),149menu: [{150id: MenuId.ChatEditingSessionApplySubmenu,151group: 'navigation',152order: 4,153when: ContextKeyExpr.and(IsSessionsWindowContext, hasActiveSessionFailedCIChecks),154}],155});156}157158override async run(accessor: ServicesAccessor): Promise<void> {159const sessionManagementService = accessor.get(ISessionsManagementService);160const gitHubService = accessor.get(IGitHubService);161const chatWidgetService = accessor.get(IChatWidgetService);162const logService = accessor.get(ILogService);163164const activeSession = sessionManagementService.activeSession.get();165if (!activeSession) {166return;167}168169const gitHubInfo = activeSession.gitHubInfo.get();170if (!gitHubInfo?.pullRequest) {171return;172}173174const prModel = gitHubService.getPullRequest(gitHubInfo.owner, gitHubInfo.repo, gitHubInfo.pullRequest.number);175const pr = prModel.pullRequest.get();176if (!pr) {177return;178}179180const ciModel = gitHubService.getPullRequestCI(gitHubInfo.owner, gitHubInfo.repo, gitHubInfo.pullRequest.number, pr.headSha);181const checks = ciModel.checks.get();182const failedChecks = getFailedChecks(checks);183if (failedChecks.length === 0) {184return;185}186187const failedCheckDetails = await Promise.all(failedChecks.map(async check => {188const annotations = await ciModel.getCheckRunAnnotations(check.id);189return { check, annotations };190}));191192const prompt = buildFixChecksPrompt(failedCheckDetails);193const sessionResource = activeSession.resource;194const chatWidget = chatWidgetService.getWidgetBySessionResource(sessionResource)195?? await chatWidgetService.openSession(sessionResource, ChatViewPaneTarget);196if (!chatWidget) {197logService.error('[FixCIChecks] Cannot fix CI checks: no chat widget found for session', sessionResource.toString());198return;199}200201await chatWidget.acceptInput(prompt, { noCommandDetection: true });202}203}204205registerWorkbenchContribution2(ActiveSessionFailedCIChecksContextContribution.ID, ActiveSessionFailedCIChecksContextContribution, WorkbenchPhase.AfterRestored);206registerAction2(FixCIChecksAction);207208209