mirror of
https://github.com/microsoft/vscode.git
synced 2026-08-21 10:01:35 +01:00
* Add window kind assignment filter Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Document window kind filter in Filters comment Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
201 lines
6.1 KiB
TypeScript
201 lines
6.1 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import { Event } from '../../../base/common/event.js';
|
|
import * as platform from '../../../base/common/platform.js';
|
|
import type { IExperimentationFilterProvider } from 'tas-client';
|
|
|
|
export const ASSIGNMENT_STORAGE_KEY = 'VSCode.ABExp.FeatureData';
|
|
export const ASSIGNMENT_REFETCH_INTERVAL = 60 * 60 * 1000; // 1 hour
|
|
|
|
export interface IAssignmentService {
|
|
readonly _serviceBrand: undefined;
|
|
|
|
readonly onDidRefetchAssignments: Event<void>;
|
|
getTreatment<T extends string | number | boolean>(name: string): Promise<T | undefined>;
|
|
}
|
|
|
|
export enum TargetPopulation {
|
|
Insiders = 'insider',
|
|
Public = 'public',
|
|
Exploration = 'exploration'
|
|
}
|
|
|
|
/*
|
|
Based upon the official VSCode currently existing filters in the
|
|
ExP backend for the VSCode cluster.
|
|
https://experimentation.visualstudio.com/Analysis%20and%20Experimentation/_git/AnE.ExP.TAS.TachyonHost.Configuration?path=%2FConfigurations%2Fvscode%2Fvscode.json&version=GBmaster
|
|
"X-MSEdge-Market": "detection.market",
|
|
"X-FD-Corpnet": "detection.corpnet",
|
|
"X-VSCode-AppVersion": "appversion",
|
|
"X-VSCode-Build": "build",
|
|
"X-MSEdge-ClientId": "clientid",
|
|
"X-VSCode-ExtensionName": "extensionname",
|
|
"X-VSCode-ExtensionVersion": "extensionversion",
|
|
"X-VSCode-TargetPopulation": "targetpopulation",
|
|
"X-VSCode-Language": "language",
|
|
"X-VSCode-Platform": "platform",
|
|
"X-VSCode-ReleaseDate": "releasedate",
|
|
"X-VSCode-WindowKind": "windowkind"
|
|
*/
|
|
export enum Filters {
|
|
/**
|
|
* The market in which the extension is distributed.
|
|
*/
|
|
Market = 'X-MSEdge-Market',
|
|
|
|
/**
|
|
* The corporation network.
|
|
*/
|
|
CorpNet = 'X-FD-Corpnet',
|
|
|
|
/**
|
|
* Version of the application which uses experimentation service.
|
|
*/
|
|
ApplicationVersion = 'X-VSCode-AppVersion',
|
|
|
|
/**
|
|
* Insiders vs Stable.
|
|
*/
|
|
Build = 'X-VSCode-Build',
|
|
|
|
/**
|
|
* Client Id which is used as primary unit for the experimentation.
|
|
*/
|
|
ClientId = 'X-MSEdge-ClientId',
|
|
|
|
/**
|
|
* Developer Device Id which can be used as an alternate unit for experimentation.
|
|
*/
|
|
DeveloperDeviceId = 'X-VSCode-DevDeviceId',
|
|
|
|
/**
|
|
* Extension header.
|
|
*/
|
|
ExtensionName = 'X-VSCode-ExtensionName',
|
|
|
|
/**
|
|
* The version of the extension.
|
|
*/
|
|
ExtensionVersion = 'X-VSCode-ExtensionVersion',
|
|
|
|
/**
|
|
* The language in use by VS Code
|
|
*/
|
|
Language = 'X-VSCode-Language',
|
|
|
|
/**
|
|
* The target population.
|
|
* This is used to separate internal, early preview, GA, etc.
|
|
*/
|
|
TargetPopulation = 'X-VSCode-TargetPopulation',
|
|
|
|
/**
|
|
* The platform (OS) on which VS Code is running.
|
|
*/
|
|
Platform = 'X-VSCode-Platform',
|
|
|
|
/**
|
|
* The release/build date of VS Code (UTC) in the format yyyymmddHH.
|
|
*/
|
|
ReleaseDate = 'X-VSCode-ReleaseDate',
|
|
|
|
/**
|
|
* The kind of window VS Code is running in (`editor` or `agents`).
|
|
*/
|
|
WindowKind = 'X-VSCode-WindowKind',
|
|
}
|
|
|
|
export const enum WindowKind {
|
|
Editor = 'editor',
|
|
Agents = 'agents',
|
|
}
|
|
|
|
export class AssignmentFilterProvider implements IExperimentationFilterProvider {
|
|
constructor(
|
|
private version: string,
|
|
private appName: string,
|
|
private machineId: string,
|
|
private devDeviceId: string,
|
|
private targetPopulation: TargetPopulation,
|
|
private releaseDate: string,
|
|
private windowKind: WindowKind
|
|
) { }
|
|
|
|
/**
|
|
* Returns a version string that can be parsed by the TAS client.
|
|
* The tas client cannot handle suffixes lke "-insider"
|
|
* Ref: https://github.com/microsoft/tas-client/blob/30340d5e1da37c2789049fcf45928b954680606f/vscode-tas-client/src/vscode-tas-client/VSCodeFilterProvider.ts#L35
|
|
*
|
|
* @param version Version string to be trimmed.
|
|
*/
|
|
private static trimVersionSuffix(version: string): string {
|
|
const regex = /\-[a-zA-Z0-9]+$/;
|
|
const result = version.split(regex);
|
|
|
|
return result[0];
|
|
}
|
|
|
|
getFilterValue(filter: string): string | null {
|
|
switch (filter) {
|
|
case Filters.ApplicationVersion:
|
|
return AssignmentFilterProvider.trimVersionSuffix(this.version); // productService.version
|
|
case Filters.Build:
|
|
return this.appName; // productService.nameLong
|
|
case Filters.ClientId:
|
|
return this.machineId;
|
|
case Filters.DeveloperDeviceId:
|
|
return this.devDeviceId;
|
|
case Filters.Language:
|
|
return platform.language;
|
|
case Filters.ExtensionName:
|
|
return 'vscode-core'; // always return vscode-core for exp service
|
|
case Filters.ExtensionVersion:
|
|
return '999999.0'; // always return a very large number for cross-extension experimentation
|
|
case Filters.TargetPopulation:
|
|
return this.targetPopulation;
|
|
case Filters.Platform:
|
|
return platform.PlatformToString(platform.platform);
|
|
case Filters.ReleaseDate:
|
|
return AssignmentFilterProvider.formatReleaseDate(this.releaseDate);
|
|
case Filters.WindowKind:
|
|
return this.windowKind;
|
|
default:
|
|
return '';
|
|
}
|
|
}
|
|
|
|
private static formatReleaseDate(iso: string): string {
|
|
// Expect ISO format, fall back to empty string if not provided
|
|
if (!iso) {
|
|
return '';
|
|
}
|
|
// Remove separators and milliseconds: YYYY-MM-DDTHH:MM:SS.sssZ -> YYYYMMDDHH
|
|
// Trimmed to 10 digits to fit within int32 bounds (ExP requirement)
|
|
const match = /^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2})/.exec(iso);
|
|
if (!match) {
|
|
return '';
|
|
}
|
|
return match.slice(1, 5).join('');
|
|
}
|
|
|
|
getFilters(): Map<string, unknown> {
|
|
const filters: Map<string, unknown> = new Map<string, unknown>();
|
|
const filterValues = Object.values(Filters);
|
|
for (const value of filterValues) {
|
|
filters.set(value, this.getFilterValue(value));
|
|
}
|
|
|
|
return filters;
|
|
}
|
|
}
|
|
|
|
export function getInternalOrg(organisations: string[] | undefined): 'vscode' | 'github' | 'microsoft' | undefined {
|
|
const isVSCodeInternal = organisations?.includes('Visual-Studio-Code');
|
|
const isGitHubInternal = organisations?.includes('github');
|
|
const isMicrosoftInternal = organisations?.includes('microsoft') || organisations?.includes('ms-copilot') || organisations?.includes('MicrosoftCopilot');
|
|
return isVSCodeInternal ? 'vscode' : isGitHubInternal ? 'github' : isMicrosoftInternal ? 'microsoft' : undefined;
|
|
}
|