Path: blob/main/src/vs/workbench/contrib/issue/browser/issueService.ts
3296 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 { getZoomLevel } from '../../../../base/browser/browser.js';6import { mainWindow } from '../../../../base/browser/window.js';7import { userAgent } from '../../../../base/common/platform.js';8import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';9import { IExtensionManagementService } from '../../../../platform/extensionManagement/common/extensionManagement.js';10import { ExtensionType, IExtensionDescription } from '../../../../platform/extensions/common/extensions.js';11import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';12import { normalizeGitHubUrl } from '../common/issueReporterUtil.js';13import { IOpenerService } from '../../../../platform/opener/common/opener.js';14import { IProductService } from '../../../../platform/product/common/productService.js';15import { buttonBackground, buttonForeground, buttonHoverBackground, foreground, inputActiveOptionBorder, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, textLinkActiveForeground, textLinkForeground } from '../../../../platform/theme/common/colorRegistry.js';16import { IColorTheme, IThemeService } from '../../../../platform/theme/common/themeService.js';17import { IWorkspaceTrustManagementService } from '../../../../platform/workspace/common/workspaceTrust.js';18import { SIDE_BAR_BACKGROUND } from '../../../common/theme.js';19import { IIssueFormService, IssueReporterData, IssueReporterExtensionData, IssueReporterStyles, IWorkbenchIssueService } from '../common/issue.js';20import { IWorkbenchAssignmentService } from '../../../services/assignment/common/assignmentService.js';21import { IAuthenticationService } from '../../../services/authentication/common/authentication.js';22import { IWorkbenchExtensionEnablementService } from '../../../services/extensionManagement/common/extensionManagement.js';23import { IExtensionService } from '../../../services/extensions/common/extensions.js';24import { IIntegrityService } from '../../../services/integrity/common/integrity.js';252627export class BrowserIssueService implements IWorkbenchIssueService {28declare readonly _serviceBrand: undefined;2930constructor(31@IExtensionService private readonly extensionService: IExtensionService,32@IProductService private readonly productService: IProductService,33@IIssueFormService private readonly issueFormService: IIssueFormService,34@IThemeService private readonly themeService: IThemeService,35@IWorkbenchAssignmentService private readonly experimentService: IWorkbenchAssignmentService,36@IWorkspaceTrustManagementService private readonly workspaceTrustManagementService: IWorkspaceTrustManagementService,37@IIntegrityService private readonly integrityService: IIntegrityService,38@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,39@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,40@IAuthenticationService private readonly authenticationService: IAuthenticationService,41@IConfigurationService private readonly configurationService: IConfigurationService,42@IOpenerService private readonly openerService: IOpenerService43) { }4445async openReporter(options: Partial<IssueReporterData>): Promise<void> {46// If web reporter setting is false open the old GitHub issue reporter47if (!this.configurationService.getValue<boolean>('issueReporter.experimental.webReporter')) {48const extensionId = options.extensionId;49// If we don't have a extensionId, treat this as a Core issue50if (!extensionId) {51if (this.productService.reportIssueUrl) {52const uri = this.getIssueUriFromStaticContent(this.productService.reportIssueUrl);53await this.openerService.open(uri, { openExternal: true });54return;55}56throw new Error(`No issue reporting URL configured for ${this.productService.nameLong}.`);57}5859const selectedExtension = this.extensionService.extensions.filter(ext => ext.identifier.value === options.extensionId)[0];60const extensionGitHubUrl = this.getExtensionGitHubUrl(selectedExtension);61if (!extensionGitHubUrl) {62throw new Error(`Unable to find issue reporting url for ${extensionId}`);63}64const uri = this.getIssueUriFromStaticContent(`${extensionGitHubUrl}/issues/new`, selectedExtension);65await this.openerService.open(uri, { openExternal: true });66}6768if (this.productService.reportIssueUrl) {69const theme = this.themeService.getColorTheme();70const experiments = await this.experimentService.getCurrentExperiments();7172let githubAccessToken = '';73try {74const githubSessions = await this.authenticationService.getSessions('github');75const potentialSessions = githubSessions.filter(session => session.scopes.includes('repo'));76githubAccessToken = potentialSessions[0]?.accessToken;77} catch (e) {78// Ignore79}8081// air on the side of caution and have false be the default82let isUnsupported = false;83try {84isUnsupported = !(await this.integrityService.isPure()).isPure;85} catch (e) {86// Ignore87}8889const extensionData: IssueReporterExtensionData[] = [];90try {91const extensions = await this.extensionManagementService.getInstalled();92const enabledExtensions = extensions.filter(extension => this.extensionEnablementService.isEnabled(extension) || (options.extensionId && extension.identifier.id === options.extensionId));93extensionData.push(...enabledExtensions.map((extension): IssueReporterExtensionData => {94const { manifest } = extension;95const manifestKeys = manifest.contributes ? Object.keys(manifest.contributes) : [];96const isTheme = !manifest.main && !manifest.browser && manifestKeys.length === 1 && manifestKeys[0] === 'themes';97const isBuiltin = extension.type === ExtensionType.System;98return {99name: manifest.name,100publisher: manifest.publisher,101version: manifest.version,102repositoryUrl: manifest.repository && manifest.repository.url,103bugsUrl: manifest.bugs && manifest.bugs.url,104displayName: manifest.displayName,105id: extension.identifier.id,106data: options.data,107uri: options.uri,108isTheme,109isBuiltin,110extensionData: 'Extensions data loading',111};112}));113} catch (e) {114extensionData.push({115name: 'Workbench Issue Service',116publisher: 'Unknown',117version: 'Unknown',118repositoryUrl: undefined,119bugsUrl: undefined,120extensionData: `Extensions not loaded: ${e}`,121displayName: `Extensions not loaded: ${e}`,122id: 'workbench.issue',123isTheme: false,124isBuiltin: true125});126}127128const issueReporterData: IssueReporterData = Object.assign({129styles: getIssueReporterStyles(theme),130zoomLevel: getZoomLevel(mainWindow),131enabledExtensions: extensionData,132experiments: experiments?.join('\n'),133restrictedMode: !this.workspaceTrustManagementService.isWorkspaceTrusted(),134isUnsupported,135githubAccessToken136}, options);137138return this.issueFormService.openReporter(issueReporterData);139}140throw new Error(`No issue reporting URL configured for ${this.productService.nameLong}.`);141142}143144private getExtensionGitHubUrl(extension: IExtensionDescription): string {145if (extension.isBuiltin && this.productService.reportIssueUrl) {146return normalizeGitHubUrl(this.productService.reportIssueUrl);147}148149let repositoryUrl = '';150151const bugsUrl = extension?.bugs?.url;152const extensionUrl = extension?.repository?.url;153154// If given, try to match the extension's bug url155if (bugsUrl && bugsUrl.match(/^https?:\/\/github\.com\/(.*)/)) {156repositoryUrl = normalizeGitHubUrl(bugsUrl);157} else if (extensionUrl && extensionUrl.match(/^https?:\/\/github\.com\/(.*)/)) {158repositoryUrl = normalizeGitHubUrl(extensionUrl);159}160161return repositoryUrl;162}163164private getIssueUriFromStaticContent(baseUri: string, extension?: IExtensionDescription): string {165const issueDescription = `ADD ISSUE DESCRIPTION HERE166167Version: ${this.productService.version}168Commit: ${this.productService.commit ?? 'unknown'}169User Agent: ${userAgent ?? 'unknown'}170Embedder: ${this.productService.embedderIdentifier ?? 'unknown'}171${extension?.version ? `\nExtension version: ${extension.version}` : ''}172<!-- generated by web issue reporter -->`;173174return `${baseUri}?body=${encodeURIComponent(issueDescription)}&labels=web`;175}176}177178export function getIssueReporterStyles(theme: IColorTheme): IssueReporterStyles {179return {180backgroundColor: getColor(theme, SIDE_BAR_BACKGROUND),181color: getColor(theme, foreground),182textLinkColor: getColor(theme, textLinkForeground),183textLinkActiveForeground: getColor(theme, textLinkActiveForeground),184inputBackground: getColor(theme, inputBackground),185inputForeground: getColor(theme, inputForeground),186inputBorder: getColor(theme, inputBorder),187inputActiveBorder: getColor(theme, inputActiveOptionBorder),188inputErrorBorder: getColor(theme, inputValidationErrorBorder),189inputErrorBackground: getColor(theme, inputValidationErrorBackground),190inputErrorForeground: getColor(theme, inputValidationErrorForeground),191buttonBackground: getColor(theme, buttonBackground),192buttonForeground: getColor(theme, buttonForeground),193buttonHoverBackground: getColor(theme, buttonHoverBackground),194sliderActiveColor: getColor(theme, scrollbarSliderActiveBackground),195sliderBackgroundColor: getColor(theme, scrollbarSliderBackground),196sliderHoverColor: getColor(theme, scrollbarSliderHoverBackground),197};198}199200function getColor(theme: IColorTheme, key: string): string | undefined {201const color = theme.getColor(key);202return color ? color.toString() : undefined;203}204205registerSingleton(IWorkbenchIssueService, BrowserIssueService, InstantiationType.Delayed);206207208