Path: blob/main/src/vs/sessions/common/agentHostSessionWorkspace.ts
13388 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 { match as matchGlob } from '../../base/common/glob.js';7import { extUri, basename } from '../../base/common/resources.js';8import { ThemeIcon } from '../../base/common/themables.js';9import { URI } from '../../base/common/uri.js';10import type { ISessionGitState } from '../../platform/agentHost/common/state/sessionState.js';11import { IConfigurationService } from '../../platform/configuration/common/configuration.js';12import { ISessionWorkspace } from '../services/sessions/common/session.js';1314export interface IAgentHostSessionProjectSummary {15readonly uri: URI;16readonly displayName: string;17}1819export interface IAgentHostSessionWorkspaceOptions {20readonly providerLabel?: string;21readonly fallbackIcon: ThemeIcon;22readonly requiresWorkspaceTrust: boolean;23readonly description?: string;24/**25* Group label used by the workspace picker to bucket the produced26* workspace into a top-level tab (e.g. `"Local"`, `"Remote"`).27*/28readonly group?: string;29/**30* Configured `git.branchProtection` glob patterns. Used to compute31* `baseBranchProtected` on the resulting repository.32*/33readonly branchProtectionPatterns?: readonly string[];34}3536/**37* Returns true when `branchName` matches any of the configured38* `git.branchProtection` glob patterns.39*/40export function matchesAnyBranchProtectionPattern(branchName: string, patterns: readonly string[] | undefined): boolean {41if (!patterns) {42return false;43}44for (const pattern of patterns) {45const trimmed = pattern.trim();46if (trimmed && matchGlob(trimmed, branchName)) {47return true;48}49}50return false;51}5253/**54* Reads `git.branchProtection` from configuration and normalizes the result55* into an array of trimmed, non-empty pattern strings.56*57* The `git.branchProtection` setting is `resource`-scoped, so the value can58* differ between workspace folders. Pass the session's working directory (or59* project URI as a fallback) as `resource` so we read the setting in the60* scope of the folder VS Code actually has loaded rather than the host61* window's active workspace.62*/63export function readBranchProtectionPatterns(configurationService: IConfigurationService, resource?: URI): readonly string[] {64const raw = configurationService.getValue<unknown>('git.branchProtection', { resource }) ?? [];65const list = Array.isArray(raw) ? raw : [raw];66return list67.map(p => typeof p === 'string' ? p.trim() : '')68.filter(p => p !== '');69}7071export function agentHostSessionWorkspaceKey(workspace: ISessionWorkspace | undefined): string | undefined {72const repository = workspace?.repositories[0];73if (!workspace || !repository) {74return undefined;75}76return [77workspace.label,78extUri.getComparisonKey(repository.uri),79repository.workingDirectory ? extUri.getComparisonKey(repository.workingDirectory) : '',80repository.branchName ?? '',81repository.baseBranchName ?? '',82String(repository.baseBranchProtected ?? ''),83String(repository.hasGitHubRemote ?? ''),84repository.upstreamBranchName ?? '',85String(repository.incomingChanges ?? ''),86String(repository.outgoingChanges ?? ''),87String(repository.uncommittedChanges ?? ''),88].join('\n');89}9091export function buildAgentHostSessionWorkspace(project: IAgentHostSessionProjectSummary | undefined, workingDirectory: URI | undefined, options: IAgentHostSessionWorkspaceOptions, gitState?: ISessionGitState): ISessionWorkspace | undefined {92const baseBranchName = gitState?.baseBranchName;93const baseBranchProtected = baseBranchName !== undefined94? matchesAnyBranchProtectionPattern(baseBranchName, options.branchProtectionPatterns)95: undefined;96const hasGitHubRemote = gitState?.hasGitHubRemote;97const upstreamBranchName = gitState?.upstreamBranchName;98const incomingChanges = gitState?.incomingChanges;99const outgoingChanges = gitState?.outgoingChanges;100const uncommittedChanges = gitState?.uncommittedChanges;101const branchName = gitState?.branchName;102const gitFields = { branchName, baseBranchName, baseBranchProtected, hasGitHubRemote, upstreamBranchName, incomingChanges, outgoingChanges, uncommittedChanges };103if (project) {104const repositoryWorkingDirectory = extUri.isEqual(workingDirectory, project.uri) ? undefined : workingDirectory;105return {106label: options.providerLabel ? `${project.displayName} [${options.providerLabel}]` : project.displayName,107description: options.description,108icon: Codicon.repo,109group: options.group,110repositories: [{ uri: project.uri, workingDirectory: repositoryWorkingDirectory, detail: undefined, ...gitFields }],111requiresWorkspaceTrust: options.requiresWorkspaceTrust,112};113}114115if (!workingDirectory) {116return undefined;117}118119const folderName = basename(workingDirectory) || workingDirectory.path;120return {121label: options.providerLabel ? `${folderName} [${options.providerLabel}]` : folderName,122description: options.description,123icon: options.fallbackIcon,124group: options.group,125repositories: [{ uri: workingDirectory, workingDirectory: undefined, detail: undefined, ...gitFields }],126requiresWorkspaceTrust: options.requiresWorkspaceTrust,127};128}129130131