Revert "Generate policy data as JSON" (#272362)

This commit is contained in:
Paul
2025-10-20 16:12:10 -07:00
committed by GitHub
parent b09414d102
commit 6025703c6a
31 changed files with 593 additions and 1043 deletions

1
build/.gitignore vendored
View File

@@ -1,2 +1 @@
*.js.map
lib/policies/policyDto.*

View File

@@ -118,7 +118,7 @@ steps:
- template: ../../common/install-builtin-extensions.yml@self
- ${{ if ne(parameters.VSCODE_CIBUILD, true) }}:
- script: node build/lib/policies/policyGenerator build/lib/policies/policyData.jsonc darwin
- script: node build/lib/policies darwin
displayName: Generate policy definitions
retryCountOnTaskFailure: 3

View File

@@ -120,7 +120,7 @@ steps:
- template: ../../common/install-builtin-extensions.yml@self
- ${{ if ne(parameters.VSCODE_CIBUILD, true) }}:
- powershell: node build\lib\policies\policyGenerator build\lib\policies\policyData.jsonc win32
- powershell: node build\lib\policies win32
displayName: Generate Group Policy definitions
retryCountOnTaskFailure: 3

View File

@@ -1,37 +1,4 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
@@ -40,12 +7,24 @@ Object.defineProperty(exports, "__esModule", { value: true });
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const minimist_1 = __importDefault(require("minimist"));
const child_process_1 = require("child_process");
const fs_1 = require("fs");
const path_1 = __importDefault(require("path"));
const JSONC = __importStar(require("jsonc-parser"));
const product = require('../../../product.json');
const packageJson = require('../../../package.json');
const byline_1 = __importDefault(require("byline"));
const ripgrep_1 = require("@vscode/ripgrep");
const tree_sitter_1 = __importDefault(require("tree-sitter"));
const { typescript } = require('tree-sitter-typescript');
const product = require('../../product.json');
const packageJson = require('../../package.json');
function isNlsString(value) {
return value ? typeof value !== 'string' : false;
}
function isStringArray(value) {
return !value.some(s => isNlsString(s));
}
function isNlsStringArray(value) {
return value.every(s => isNlsString(s));
}
var PolicyType;
(function (PolicyType) {
PolicyType["Boolean"] = "boolean";
@@ -128,12 +107,12 @@ ${this.renderProfileManifestValue(translations)}
}
}
class BooleanPolicy extends BasePolicy {
static from(category, policy) {
const { name, minimumVersion, localization, type } = policy;
static from(name, category, minimumVersion, description, moduleName, settingNode) {
const type = getStringProperty(moduleName, settingNode, 'type');
if (type !== 'boolean') {
return undefined;
}
return new BooleanPolicy(name, { moduleName: '', name: { nlsKey: category.name.key, value: category.name.value } }, minimumVersion, { nlsKey: localization.description.key, value: localization.description.value }, '');
return new BooleanPolicy(name, category, minimumVersion, description, moduleName);
}
constructor(name, category, minimumVersion, description, moduleName) {
super(PolicyType.Boolean, name, category, minimumVersion, description, moduleName);
@@ -164,17 +143,23 @@ class BooleanPolicy extends BasePolicy {
<string>boolean</string>`;
}
}
class ParseError extends Error {
constructor(message, moduleName, node) {
super(`${message}. ${moduleName}.ts:${node.startPosition.row + 1}`);
}
}
class NumberPolicy extends BasePolicy {
defaultValue;
static from(category, policy) {
const { type, default: defaultValue, name, minimumVersion, localization } = policy;
static from(name, category, minimumVersion, description, moduleName, settingNode) {
const type = getStringProperty(moduleName, settingNode, 'type');
if (type !== 'number') {
return undefined;
}
if (typeof defaultValue !== 'number') {
throw new Error(`Missing required 'default' property.`);
const defaultValue = getNumberProperty(moduleName, settingNode, 'default');
if (typeof defaultValue === 'undefined') {
throw new ParseError(`Missing required 'default' property.`, moduleName, settingNode);
}
return new NumberPolicy(name, { moduleName: '', name: { nlsKey: category.name.key, value: category.name.value } }, minimumVersion, { nlsKey: localization.description.key, value: localization.description.value }, '', defaultValue);
return new NumberPolicy(name, category, minimumVersion, description, moduleName, defaultValue);
}
constructor(name, category, minimumVersion, description, moduleName, defaultValue) {
super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName);
@@ -206,12 +191,12 @@ class NumberPolicy extends BasePolicy {
}
}
class StringPolicy extends BasePolicy {
static from(category, policy) {
const { type, name, minimumVersion, localization } = policy;
static from(name, category, minimumVersion, description, moduleName, settingNode) {
const type = getStringProperty(moduleName, settingNode, 'type');
if (type !== 'string') {
return undefined;
}
return new StringPolicy(name, { moduleName: '', name: { nlsKey: category.name.key, value: category.name.value } }, minimumVersion, { nlsKey: localization.description.key, value: localization.description.value }, '');
return new StringPolicy(name, category, minimumVersion, description, moduleName);
}
constructor(name, category, minimumVersion, description, moduleName) {
super(PolicyType.String, name, category, minimumVersion, description, moduleName);
@@ -239,12 +224,12 @@ class StringPolicy extends BasePolicy {
}
}
class ObjectPolicy extends BasePolicy {
static from(category, policy) {
const { type, name, minimumVersion, localization } = policy;
static from(name, category, minimumVersion, description, moduleName, settingNode) {
const type = getStringProperty(moduleName, settingNode, 'type');
if (type !== 'object' && type !== 'array') {
return undefined;
}
return new ObjectPolicy(name, { moduleName: '', name: { nlsKey: category.name.key, value: category.name.value } }, minimumVersion, { nlsKey: localization.description.key, value: localization.description.value }, '');
return new ObjectPolicy(name, category, minimumVersion, description, moduleName);
}
constructor(name, category, minimumVersion, description, moduleName) {
super(PolicyType.Object, name, category, minimumVersion, description, moduleName);
@@ -275,20 +260,26 @@ class ObjectPolicy extends BasePolicy {
class StringEnumPolicy extends BasePolicy {
enum_;
enumDescriptions;
static from(category, policy) {
const { type, name, minimumVersion, enum: enumValue, localization } = policy;
static from(name, category, minimumVersion, description, moduleName, settingNode) {
const type = getStringProperty(moduleName, settingNode, 'type');
if (type !== 'string') {
return undefined;
}
const enum_ = enumValue;
const enum_ = getStringArrayProperty(moduleName, settingNode, 'enum');
if (!enum_) {
return undefined;
}
if (!localization.enumDescriptions || !Array.isArray(localization.enumDescriptions) || localization.enumDescriptions.length !== enum_.length) {
throw new Error(`Invalid policy data: enumDescriptions must exist and have the same length as enum_ for policy "${name}".`);
if (!isStringArray(enum_)) {
throw new ParseError(`Property 'enum' should not be localized.`, moduleName, settingNode);
}
const enumDescriptions = localization.enumDescriptions.map((e) => ({ nlsKey: e.key, value: e.value }));
return new StringEnumPolicy(name, { moduleName: '', name: { nlsKey: category.name.key, value: category.name.value } }, minimumVersion, { nlsKey: localization.description.key, value: localization.description.value }, '', enum_, enumDescriptions);
const enumDescriptions = getStringArrayProperty(moduleName, settingNode, 'enumDescriptions');
if (!enumDescriptions) {
throw new ParseError(`Missing required 'enumDescriptions' property.`, moduleName, settingNode);
}
else if (!isNlsStringArray(enumDescriptions)) {
throw new ParseError(`Property 'enumDescriptions' should be localized.`, moduleName, settingNode);
}
return new StringEnumPolicy(name, category, minimumVersion, description, moduleName, enum_, enumDescriptions);
}
constructor(name, category, minimumVersion, description, moduleName, enum_, enumDescriptions) {
super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName);
@@ -331,6 +322,178 @@ class StringEnumPolicy extends BasePolicy {
</array>`;
}
}
const NumberQ = {
Q: `(number) @value`,
value(matches) {
const match = matches[0];
if (!match) {
return undefined;
}
const value = match.captures.filter(c => c.name === 'value')[0]?.node.text;
if (!value) {
throw new Error(`Missing required 'value' property.`);
}
return parseInt(value);
}
};
const StringQ = {
Q: `[
(string (string_fragment) @value)
(call_expression
function: [
(identifier) @localizeFn (#eq? @localizeFn localize)
(member_expression
object: (identifier) @nlsObj (#eq? @nlsObj nls)
property: (property_identifier) @localizeFn (#eq? @localizeFn localize)
)
]
arguments: (arguments (string (string_fragment) @nlsKey) (string (string_fragment) @value))
)
]`,
value(matches) {
const match = matches[0];
if (!match) {
return undefined;
}
const value = match.captures.filter(c => c.name === 'value')[0]?.node.text;
if (!value) {
throw new Error(`Missing required 'value' property.`);
}
const nlsKey = match.captures.filter(c => c.name === 'nlsKey')[0]?.node.text;
if (nlsKey) {
return { value, nlsKey };
}
else {
return value;
}
}
};
const StringArrayQ = {
Q: `(array ${StringQ.Q})`,
value(matches) {
if (matches.length === 0) {
return undefined;
}
return matches.map(match => {
return StringQ.value([match]);
});
}
};
function getProperty(qtype, moduleName, node, key) {
const query = new tree_sitter_1.default.Query(typescript, `(
(pair
key: [(property_identifier)(string)] @key
value: ${qtype.Q}
)
(#any-of? @key "${key}" "'${key}'")
)`);
try {
const matches = query.matches(node).filter(m => m.captures[0].node.parent?.parent === node);
return qtype.value(matches);
}
catch (e) {
throw new ParseError(e.message, moduleName, node);
}
}
function getNumberProperty(moduleName, node, key) {
return getProperty(NumberQ, moduleName, node, key);
}
function getStringProperty(moduleName, node, key) {
return getProperty(StringQ, moduleName, node, key);
}
function getStringArrayProperty(moduleName, node, key) {
return getProperty(StringArrayQ, moduleName, node, key);
}
// TODO: add more policy types
const PolicyTypes = [
BooleanPolicy,
NumberPolicy,
StringEnumPolicy,
StringPolicy,
ObjectPolicy
];
function getPolicy(moduleName, configurationNode, settingNode, policyNode, categories) {
const name = getStringProperty(moduleName, policyNode, 'name');
if (!name) {
throw new ParseError(`Missing required 'name' property`, moduleName, policyNode);
}
else if (isNlsString(name)) {
throw new ParseError(`Property 'name' should be a literal string`, moduleName, policyNode);
}
const categoryName = getStringProperty(moduleName, configurationNode, 'title');
if (!categoryName) {
throw new ParseError(`Missing required 'title' property`, moduleName, configurationNode);
}
else if (!isNlsString(categoryName)) {
throw new ParseError(`Property 'title' should be localized`, moduleName, configurationNode);
}
const categoryKey = `${categoryName.nlsKey}:${categoryName.value}`;
let category = categories.get(categoryKey);
if (!category) {
category = { moduleName, name: categoryName };
categories.set(categoryKey, category);
}
const minimumVersion = getStringProperty(moduleName, policyNode, 'minimumVersion');
if (!minimumVersion) {
throw new ParseError(`Missing required 'minimumVersion' property.`, moduleName, policyNode);
}
else if (isNlsString(minimumVersion)) {
throw new ParseError(`Property 'minimumVersion' should be a literal string.`, moduleName, policyNode);
}
const description = getStringProperty(moduleName, policyNode, 'description') ?? getStringProperty(moduleName, settingNode, 'description');
if (!description) {
throw new ParseError(`Missing required 'description' property.`, moduleName, settingNode);
}
if (!isNlsString(description)) {
throw new ParseError(`Property 'description' should be localized.`, moduleName, settingNode);
}
let result;
for (const policyType of PolicyTypes) {
if (result = policyType.from(name, category, minimumVersion, description, moduleName, settingNode)) {
break;
}
}
if (!result) {
throw new ParseError(`Failed to parse policy '${name}'.`, moduleName, settingNode);
}
return result;
}
function getPolicies(moduleName, node) {
const query = new tree_sitter_1.default.Query(typescript, `
(
(call_expression
function: (member_expression property: (property_identifier) @registerConfigurationFn) (#eq? @registerConfigurationFn registerConfiguration)
arguments: (arguments (object (pair
key: [(property_identifier)(string)] @propertiesKey (#any-of? @propertiesKey "properties" "'properties'")
value: (object (pair
key: [(property_identifier)(string)(computed_property_name)]
value: (object (pair
key: [(property_identifier)(string)] @policyKey (#any-of? @policyKey "policy" "'policy'")
value: (object) @policy
)) @setting
))
)) @configuration)
)
)
`);
const categories = new Map();
return query.matches(node).map(m => {
const configurationNode = m.captures.filter(c => c.name === 'configuration')[0].node;
const settingNode = m.captures.filter(c => c.name === 'setting')[0].node;
const policyNode = m.captures.filter(c => c.name === 'policy')[0].node;
return getPolicy(moduleName, configurationNode, settingNode, policyNode, categories);
});
}
async function getFiles(root) {
return new Promise((c, e) => {
const result = [];
const rg = (0, child_process_1.spawn)(ripgrep_1.rgPath, ['-l', 'registerConfiguration\\(', '-g', 'src/**/*.ts', '-g', '!src/**/test/**', root]);
const stream = (0, byline_1.default)(rg.stdout.setEncoding('utf8'));
stream.on('data', path => result.push(path));
stream.on('error', err => e(err));
stream.on('end', () => c(result));
});
}
function renderADMX(regKey, versions, categories, policies) {
versions = versions.map(v => v.replace(/\./g, '_'));
return `<?xml version="1.0" encoding="utf-8"?>
@@ -594,15 +757,7 @@ async function getSpecificNLS(resourceUrlTemplate, languageId, version) {
throw new Error(`[${res.status}] Error downloading language pack ${languageId}@${version}`);
}
const { contents: result } = await res.json();
// TODO: support module namespacing
// Flatten all moduleName keys to empty string
const flattened = { '': {} };
for (const moduleName in result) {
for (const nlsKey in result[moduleName]) {
flattened[''][nlsKey] = result[moduleName][nlsKey];
}
}
return flattened;
return result;
}
function parseVersion(version) {
const [, major, minor, patch] = /^(\d+)\.(\d+)\.(\d+)/.exec(version);
@@ -646,45 +801,18 @@ async function getNLS(extensionGalleryServiceUrl, resourceUrlTemplate, languageI
}
return await getSpecificNLS(resourceUrlTemplate, languageId, latestCompatibleVersion);
}
// TODO: add more policy types
const PolicyTypes = [
BooleanPolicy,
NumberPolicy,
StringEnumPolicy,
StringPolicy,
ObjectPolicy
];
async function parsePolicies(policyDataFile) {
const contents = JSONC.parse(await fs_1.promises.readFile(policyDataFile, { encoding: 'utf8' }));
const categories = new Map();
for (const category of contents.categories) {
categories.set(category.key, category);
}
async function parsePolicies() {
const parser = new tree_sitter_1.default();
parser.setLanguage(typescript);
const files = await getFiles(process.cwd());
const base = path_1.default.join(process.cwd(), 'src');
const policies = [];
for (const policy of contents.policies) {
const category = categories.get(policy.category);
if (!category) {
throw new Error(`Unknown category: ${policy.category}`);
}
let result;
for (const policyType of PolicyTypes) {
if (result = policyType.from(category, policy)) {
break;
}
}
if (!result) {
throw new Error(`Unsupported policy type: ${policy.type} for policy ${policy.name}`);
}
policies.push(result);
for (const file of files) {
const moduleName = path_1.default.relative(base, file).replace(/\.ts$/i, '').replace(/\\/g, '/');
const contents = await fs_1.promises.readFile(file, { encoding: 'utf8' });
const tree = parser.parse(contents);
policies.push(...getPolicies(moduleName, tree.rootNode));
}
// Sort policies first by category name, then by policy name
policies.sort((a, b) => {
const categoryCompare = a.category.name.value.localeCompare(b.category.name.value);
if (categoryCompare !== 0) {
return categoryCompare;
}
return a.name.localeCompare(b.name);
});
return policies;
}
async function getTranslations() {
@@ -732,14 +860,8 @@ async function darwinMain(policies, translations) {
}
}
async function main() {
const args = (0, minimist_1.default)(process.argv.slice(2));
if (args._.length !== 2) {
console.error(`Usage: node build/lib/policies <policy-data-file> <darwin|win32>`);
process.exit(1);
}
const policyDataFile = args._[0];
const platform = args._[1];
const [policies, translations] = await Promise.all([parsePolicies(policyDataFile), getTranslations()]);
const [policies, translations] = await Promise.all([parsePolicies(), getTranslations()]);
const platform = process.argv[2];
if (platform === 'darwin') {
await darwinMain(policies, translations);
}
@@ -747,14 +869,19 @@ async function main() {
await windowsMain(policies, translations);
}
else {
console.error(`Usage: node build/lib/policies <policy-data-file> <darwin|win32>`);
console.error(`Usage: node build/lib/policies <darwin|win32>`);
process.exit(1);
}
}
if (require.main === module) {
main().catch(err => {
console.error(err);
if (err instanceof ParseError) {
console.error(`Parse Error:`, err.message);
}
else {
console.error(err);
}
process.exit(1);
});
}
//# sourceMappingURL=policyGenerator.js.map
//# sourceMappingURL=policies.js.map

View File

@@ -3,17 +3,30 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import minimist from 'minimist';
import { spawn } from 'child_process';
import { promises as fs } from 'fs';
import path from 'path';
import { CategoryDto, ExportedPolicyDataDto, PolicyDto } from './policyDto';
import * as JSONC from 'jsonc-parser';
const product = require('../../../product.json');
const packageJson = require('../../../package.json');
import byline from 'byline';
import { rgPath } from '@vscode/ripgrep';
import Parser from 'tree-sitter';
const { typescript } = require('tree-sitter-typescript');
const product = require('../../product.json');
const packageJson = require('../../package.json');
type NlsString = { value: string; nlsKey: string };
function isNlsString(value: string | NlsString | undefined): value is NlsString {
return value ? typeof value !== 'string' : false;
}
function isStringArray(value: (string | NlsString)[]): value is string[] {
return !value.some(s => isNlsString(s));
}
function isNlsStringArray(value: (string | NlsString)[]): value is NlsString[] {
return value.every(s => isNlsString(s));
}
interface Category {
readonly moduleName: string;
readonly name: NlsString;
@@ -133,14 +146,21 @@ ${this.renderProfileManifestValue(translations)}
class BooleanPolicy extends BasePolicy {
static from(category: CategoryDto, policy: PolicyDto): BooleanPolicy | undefined {
const { name, minimumVersion, localization, type } = policy;
static from(
name: string,
category: Category,
minimumVersion: string,
description: NlsString,
moduleName: string,
settingNode: Parser.SyntaxNode
): BooleanPolicy | undefined {
const type = getStringProperty(moduleName, settingNode, 'type');
if (type !== 'boolean') {
return undefined;
}
return new BooleanPolicy(name, { moduleName: '', name: { nlsKey: category.name.key, value: category.name.value } }, minimumVersion, { nlsKey: localization.description.key, value: localization.description.value }, '');
return new BooleanPolicy(name, category, minimumVersion, description, moduleName);
}
private constructor(
@@ -183,20 +203,35 @@ class BooleanPolicy extends BasePolicy {
}
}
class ParseError extends Error {
constructor(message: string, moduleName: string, node: Parser.SyntaxNode) {
super(`${message}. ${moduleName}.ts:${node.startPosition.row + 1}`);
}
}
class NumberPolicy extends BasePolicy {
static from(category: CategoryDto, policy: PolicyDto): NumberPolicy | undefined {
const { type, default: defaultValue, name, minimumVersion, localization } = policy;
static from(
name: string,
category: Category,
minimumVersion: string,
description: NlsString,
moduleName: string,
settingNode: Parser.SyntaxNode
): NumberPolicy | undefined {
const type = getStringProperty(moduleName, settingNode, 'type');
if (type !== 'number') {
return undefined;
}
if (typeof defaultValue !== 'number') {
throw new Error(`Missing required 'default' property.`);
const defaultValue = getNumberProperty(moduleName, settingNode, 'default');
if (typeof defaultValue === 'undefined') {
throw new ParseError(`Missing required 'default' property.`, moduleName, settingNode);
}
return new NumberPolicy(name, { moduleName: '', name: { nlsKey: category.name.key, value: category.name.value } }, minimumVersion, { nlsKey: localization.description.key, value: localization.description.value }, '', defaultValue);
return new NumberPolicy(name, category, minimumVersion, description, moduleName, defaultValue);
}
private constructor(
@@ -241,14 +276,21 @@ class NumberPolicy extends BasePolicy {
class StringPolicy extends BasePolicy {
static from(category: CategoryDto, policy: PolicyDto): StringPolicy | undefined {
const { type, name, minimumVersion, localization } = policy;
static from(
name: string,
category: Category,
minimumVersion: string,
description: NlsString,
moduleName: string,
settingNode: Parser.SyntaxNode
): StringPolicy | undefined {
const type = getStringProperty(moduleName, settingNode, 'type');
if (type !== 'string') {
return undefined;
}
return new StringPolicy(name, { moduleName: '', name: { nlsKey: category.name.key, value: category.name.value } }, minimumVersion, { nlsKey: localization.description.key, value: localization.description.value }, '');
return new StringPolicy(name, category, minimumVersion, description, moduleName);
}
private constructor(
@@ -289,14 +331,21 @@ class StringPolicy extends BasePolicy {
class ObjectPolicy extends BasePolicy {
static from(category: CategoryDto, policy: PolicyDto): ObjectPolicy | undefined {
const { type, name, minimumVersion, localization } = policy;
static from(
name: string,
category: Category,
minimumVersion: string,
description: NlsString,
moduleName: string,
settingNode: Parser.SyntaxNode
): ObjectPolicy | undefined {
const type = getStringProperty(moduleName, settingNode, 'type');
if (type !== 'object' && type !== 'array') {
return undefined;
}
return new ObjectPolicy(name, { moduleName: '', name: { nlsKey: category.name.key, value: category.name.value } }, minimumVersion, { nlsKey: localization.description.key, value: localization.description.value }, '');
return new ObjectPolicy(name, category, minimumVersion, description, moduleName);
}
private constructor(
@@ -338,32 +387,39 @@ class ObjectPolicy extends BasePolicy {
class StringEnumPolicy extends BasePolicy {
static from(category: CategoryDto, policy: PolicyDto): StringEnumPolicy | undefined {
const { type, name, minimumVersion, enum: enumValue, localization } = policy;
static from(
name: string,
category: Category,
minimumVersion: string,
description: NlsString,
moduleName: string,
settingNode: Parser.SyntaxNode
): StringEnumPolicy | undefined {
const type = getStringProperty(moduleName, settingNode, 'type');
if (type !== 'string') {
return undefined;
}
const enum_ = enumValue;
const enum_ = getStringArrayProperty(moduleName, settingNode, 'enum');
if (!enum_) {
return undefined;
}
if (!localization.enumDescriptions || !Array.isArray(localization.enumDescriptions) || localization.enumDescriptions.length !== enum_.length) {
throw new Error(`Invalid policy data: enumDescriptions must exist and have the same length as enum_ for policy "${name}".`);
if (!isStringArray(enum_)) {
throw new ParseError(`Property 'enum' should not be localized.`, moduleName, settingNode);
}
const enumDescriptions = localization.enumDescriptions.map((e) => ({ nlsKey: e.key, value: e.value }));
return new StringEnumPolicy(
name,
{ moduleName: '', name: { nlsKey: category.name.key, value: category.name.value } },
minimumVersion,
{ nlsKey: localization.description.key, value: localization.description.value },
'',
enum_,
enumDescriptions
);
const enumDescriptions = getStringArrayProperty(moduleName, settingNode, 'enumDescriptions');
if (!enumDescriptions) {
throw new ParseError(`Missing required 'enumDescriptions' property.`, moduleName, settingNode);
} else if (!isNlsStringArray(enumDescriptions)) {
throw new ParseError(`Property 'enumDescriptions' should be localized.`, moduleName, settingNode);
}
return new StringEnumPolicy(name, category, minimumVersion, description, moduleName, enum_, enumDescriptions);
}
private constructor(
@@ -419,6 +475,226 @@ class StringEnumPolicy extends BasePolicy {
}
}
interface QType<T> {
Q: string;
value(matches: Parser.QueryMatch[]): T | undefined;
}
const NumberQ: QType<number> = {
Q: `(number) @value`,
value(matches: Parser.QueryMatch[]): number | undefined {
const match = matches[0];
if (!match) {
return undefined;
}
const value = match.captures.filter(c => c.name === 'value')[0]?.node.text;
if (!value) {
throw new Error(`Missing required 'value' property.`);
}
return parseInt(value);
}
};
const StringQ: QType<string | NlsString> = {
Q: `[
(string (string_fragment) @value)
(call_expression
function: [
(identifier) @localizeFn (#eq? @localizeFn localize)
(member_expression
object: (identifier) @nlsObj (#eq? @nlsObj nls)
property: (property_identifier) @localizeFn (#eq? @localizeFn localize)
)
]
arguments: (arguments (string (string_fragment) @nlsKey) (string (string_fragment) @value))
)
]`,
value(matches: Parser.QueryMatch[]): string | NlsString | undefined {
const match = matches[0];
if (!match) {
return undefined;
}
const value = match.captures.filter(c => c.name === 'value')[0]?.node.text;
if (!value) {
throw new Error(`Missing required 'value' property.`);
}
const nlsKey = match.captures.filter(c => c.name === 'nlsKey')[0]?.node.text;
if (nlsKey) {
return { value, nlsKey };
} else {
return value;
}
}
};
const StringArrayQ: QType<(string | NlsString)[]> = {
Q: `(array ${StringQ.Q})`,
value(matches: Parser.QueryMatch[]): (string | NlsString)[] | undefined {
if (matches.length === 0) {
return undefined;
}
return matches.map(match => {
return StringQ.value([match]) as string | NlsString;
});
}
};
function getProperty<T>(qtype: QType<T>, moduleName: string, node: Parser.SyntaxNode, key: string): T | undefined {
const query = new Parser.Query(
typescript,
`(
(pair
key: [(property_identifier)(string)] @key
value: ${qtype.Q}
)
(#any-of? @key "${key}" "'${key}'")
)`
);
try {
const matches = query.matches(node).filter(m => m.captures[0].node.parent?.parent === node);
return qtype.value(matches);
} catch (e) {
throw new ParseError(e.message, moduleName, node);
}
}
function getNumberProperty(moduleName: string, node: Parser.SyntaxNode, key: string): number | undefined {
return getProperty(NumberQ, moduleName, node, key);
}
function getStringProperty(moduleName: string, node: Parser.SyntaxNode, key: string): string | NlsString | undefined {
return getProperty(StringQ, moduleName, node, key);
}
function getStringArrayProperty(moduleName: string, node: Parser.SyntaxNode, key: string): (string | NlsString)[] | undefined {
return getProperty(StringArrayQ, moduleName, node, key);
}
// TODO: add more policy types
const PolicyTypes = [
BooleanPolicy,
NumberPolicy,
StringEnumPolicy,
StringPolicy,
ObjectPolicy
];
function getPolicy(
moduleName: string,
configurationNode: Parser.SyntaxNode,
settingNode: Parser.SyntaxNode,
policyNode: Parser.SyntaxNode,
categories: Map<string, Category>
): Policy {
const name = getStringProperty(moduleName, policyNode, 'name');
if (!name) {
throw new ParseError(`Missing required 'name' property`, moduleName, policyNode);
} else if (isNlsString(name)) {
throw new ParseError(`Property 'name' should be a literal string`, moduleName, policyNode);
}
const categoryName = getStringProperty(moduleName, configurationNode, 'title');
if (!categoryName) {
throw new ParseError(`Missing required 'title' property`, moduleName, configurationNode);
} else if (!isNlsString(categoryName)) {
throw new ParseError(`Property 'title' should be localized`, moduleName, configurationNode);
}
const categoryKey = `${categoryName.nlsKey}:${categoryName.value}`;
let category = categories.get(categoryKey);
if (!category) {
category = { moduleName, name: categoryName };
categories.set(categoryKey, category);
}
const minimumVersion = getStringProperty(moduleName, policyNode, 'minimumVersion');
if (!minimumVersion) {
throw new ParseError(`Missing required 'minimumVersion' property.`, moduleName, policyNode);
} else if (isNlsString(minimumVersion)) {
throw new ParseError(`Property 'minimumVersion' should be a literal string.`, moduleName, policyNode);
}
const description = getStringProperty(moduleName, policyNode, 'description') ?? getStringProperty(moduleName, settingNode, 'description');
if (!description) {
throw new ParseError(`Missing required 'description' property.`, moduleName, settingNode);
} if (!isNlsString(description)) {
throw new ParseError(`Property 'description' should be localized.`, moduleName, settingNode);
}
let result: Policy | undefined;
for (const policyType of PolicyTypes) {
if (result = policyType.from(name, category, minimumVersion, description, moduleName, settingNode)) {
break;
}
}
if (!result) {
throw new ParseError(`Failed to parse policy '${name}'.`, moduleName, settingNode);
}
return result;
}
function getPolicies(moduleName: string, node: Parser.SyntaxNode): Policy[] {
const query = new Parser.Query(typescript, `
(
(call_expression
function: (member_expression property: (property_identifier) @registerConfigurationFn) (#eq? @registerConfigurationFn registerConfiguration)
arguments: (arguments (object (pair
key: [(property_identifier)(string)] @propertiesKey (#any-of? @propertiesKey "properties" "'properties'")
value: (object (pair
key: [(property_identifier)(string)(computed_property_name)]
value: (object (pair
key: [(property_identifier)(string)] @policyKey (#any-of? @policyKey "policy" "'policy'")
value: (object) @policy
)) @setting
))
)) @configuration)
)
)
`);
const categories = new Map<string, Category>();
return query.matches(node).map(m => {
const configurationNode = m.captures.filter(c => c.name === 'configuration')[0].node;
const settingNode = m.captures.filter(c => c.name === 'setting')[0].node;
const policyNode = m.captures.filter(c => c.name === 'policy')[0].node;
return getPolicy(moduleName, configurationNode, settingNode, policyNode, categories);
});
}
async function getFiles(root: string): Promise<string[]> {
return new Promise((c, e) => {
const result: string[] = [];
const rg = spawn(rgPath, ['-l', 'registerConfiguration\\(', '-g', 'src/**/*.ts', '-g', '!src/**/test/**', root]);
const stream = byline(rg.stdout.setEncoding('utf8'));
stream.on('data', path => result.push(path));
stream.on('error', err => e(err));
stream.on('end', () => c(result));
});
}
function renderADMX(regKey: string, versions: string[], categories: Category[], policies: Policy[]) {
versions = versions.map(v => v.replace(/\./g, '_'));
@@ -693,7 +969,7 @@ type Translations = { languageId: string; languageTranslations: LanguageTranslat
type Version = [number, number, number];
async function getSpecificNLS(resourceUrlTemplate: string, languageId: string, version: Version): Promise<LanguageTranslations> {
async function getSpecificNLS(resourceUrlTemplate: string, languageId: string, version: Version) {
const resource = {
publisher: 'ms-ceintl',
name: `vscode-language-pack-${languageId}`,
@@ -709,17 +985,7 @@ async function getSpecificNLS(resourceUrlTemplate: string, languageId: string, v
}
const { contents: result } = await res.json() as { contents: LanguageTranslations };
// TODO: support module namespacing
// Flatten all moduleName keys to empty string
const flattened: LanguageTranslations = { '': {} };
for (const moduleName in result) {
for (const nlsKey in result[moduleName]) {
flattened[''][nlsKey] = result[moduleName][nlsKey];
}
}
return flattened;
return result;
}
function parseVersion(version: string): Version {
@@ -768,52 +1034,21 @@ async function getNLS(extensionGalleryServiceUrl: string, resourceUrlTemplate: s
return await getSpecificNLS(resourceUrlTemplate, languageId, latestCompatibleVersion);
}
// TODO: add more policy types
const PolicyTypes = [
BooleanPolicy,
NumberPolicy,
StringEnumPolicy,
StringPolicy,
ObjectPolicy
];
async function parsePolicies(): Promise<Policy[]> {
const parser = new Parser();
parser.setLanguage(typescript);
async function parsePolicies(policyDataFile: string): Promise<Policy[]> {
const contents = JSONC.parse(await fs.readFile(policyDataFile, { encoding: 'utf8' })) as ExportedPolicyDataDto;
const categories = new Map<string, CategoryDto>();
for (const category of contents.categories) {
categories.set(category.key, category);
const files = await getFiles(process.cwd());
const base = path.join(process.cwd(), 'src');
const policies = [];
for (const file of files) {
const moduleName = path.relative(base, file).replace(/\.ts$/i, '').replace(/\\/g, '/');
const contents = await fs.readFile(file, { encoding: 'utf8' });
const tree = parser.parse(contents);
policies.push(...getPolicies(moduleName, tree.rootNode));
}
const policies: Policy[] = [];
for (const policy of contents.policies) {
const category = categories.get(policy.category);
if (!category) {
throw new Error(`Unknown category: ${policy.category}`);
}
let result: Policy | undefined;
for (const policyType of PolicyTypes) {
if (result = policyType.from(category, policy)) {
break;
}
}
if (!result) {
throw new Error(`Unsupported policy type: ${policy.type} for policy ${policy.name}`);
}
policies.push(result);
}
// Sort policies first by category name, then by policy name
policies.sort((a, b) => {
const categoryCompare = a.category.name.value.localeCompare(b.category.name.value);
if (categoryCompare !== 0) {
return categoryCompare;
}
return a.name.localeCompare(b.name);
});
return policies;
}
@@ -877,29 +1112,26 @@ async function darwinMain(policies: Policy[], translations: Translations) {
}
async function main() {
const args = minimist(process.argv.slice(2));
if (args._.length !== 2) {
console.error(`Usage: node build/lib/policies <policy-data-file> <darwin|win32>`);
process.exit(1);
}
const policyDataFile = args._[0];
const platform = args._[1];
const [policies, translations] = await Promise.all([parsePolicies(policyDataFile), getTranslations()]);
const [policies, translations] = await Promise.all([parsePolicies(), getTranslations()]);
const platform = process.argv[2];
if (platform === 'darwin') {
await darwinMain(policies, translations);
} else if (platform === 'win32') {
await windowsMain(policies, translations);
} else {
console.error(`Usage: node build/lib/policies <policy-data-file> <darwin|win32>`);
console.error(`Usage: node build/lib/policies <darwin|win32>`);
process.exit(1);
}
}
if (require.main === module) {
main().catch(err => {
console.error(err);
if (err instanceof ParseError) {
console.error(`Parse Error:`, err.message);
} else {
console.error(err);
}
process.exit(1);
});
}

View File

@@ -1,58 +0,0 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const sourceFile = path.join(__dirname, '../../../src/vs/workbench/contrib/policyExport/common/policyDto.ts');
const destFile = path.join(__dirname, 'policyDto.ts');
try {
// Check if source file exists
if (!fs.existsSync(sourceFile)) {
console.error(`Error: Source file not found: ${sourceFile}`);
console.error('Please ensure policyDto.ts exists in src/vs/base/common/');
process.exit(1);
}
// Copy the file
fs.copyFileSync(sourceFile, destFile);
}
catch (error) {
console.error(`Error copying policyDto.ts: ${error.message}`);
process.exit(1);
}
//# sourceMappingURL=copyPolicyDto.js.map

View File

@@ -1,25 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as fs from 'fs';
import * as path from 'path';
const sourceFile = path.join(__dirname, '../../../src/vs/workbench/contrib/policyExport/common/policyDto.ts');
const destFile = path.join(__dirname, 'policyDto.ts');
try {
// Check if source file exists
if (!fs.existsSync(sourceFile)) {
console.error(`Error: Source file not found: ${sourceFile}`);
console.error('Please ensure policyDto.ts exists in src/vs/base/common/');
process.exit(1);
}
// Copy the file
fs.copyFileSync(sourceFile, destFile);
} catch (error) {
console.error(`Error copying policyDto.ts: ${(error as Error).message}`);
process.exit(1);
}

View File

@@ -1,277 +0,0 @@
/** THIS FILE IS AUTOMATICALLY GENERATED USING `code --export-policy-data`. DO NOT MODIFY IT MANUALLY. **/
{
"categories": [
{
"key": "Extensions",
"name": {
"key": "extensionsConfigurationTitle",
"value": "Extensions"
}
},
{
"key": "IntegratedTerminal",
"name": {
"key": "terminalIntegratedConfigurationTitle",
"value": "Integrated Terminal"
}
},
{
"key": "InteractiveSession",
"name": {
"key": "interactiveSessionConfigurationTitle",
"value": "Chat"
}
},
{
"key": "Telemetry",
"name": {
"key": "telemetryConfigurationTitle",
"value": "Telemetry"
}
},
{
"key": "Update",
"name": {
"key": "updateConfigurationTitle",
"value": "Update"
}
}
],
"policies": [
{
"key": "chat.mcp.gallery.serviceUrl",
"name": "McpGalleryServiceUrl",
"category": "InteractiveSession",
"minimumVersion": "1.101",
"localization": {
"description": {
"key": "mcp.gallery.serviceUrl",
"value": "Configure the MCP Gallery service URL to connect to"
}
},
"type": "string",
"default": ""
},
{
"key": "extensions.gallery.serviceUrl",
"name": "ExtensionGalleryServiceUrl",
"category": "Extensions",
"minimumVersion": "1.99",
"localization": {
"description": {
"key": "extensions.gallery.serviceUrl",
"value": "Configure the Marketplace service URL to connect to"
}
},
"type": "string",
"default": ""
},
{
"key": "extensions.allowed",
"name": "AllowedExtensions",
"category": "Extensions",
"minimumVersion": "1.96",
"localization": {
"description": {
"key": "extensions.allowed.policy",
"value": "Specify a list of extensions that are allowed to use. This helps maintain a secure and consistent development environment by restricting the use of unauthorized extensions. More information: https://code.visualstudio.com/docs/setup/enterprise#_configure-allowed-extensions"
}
},
"type": "object",
"default": "*"
},
{
"key": "chat.tools.global.autoApprove",
"name": "ChatToolsAutoApprove",
"category": "InteractiveSession",
"minimumVersion": "1.99",
"localization": {
"description": {
"key": "autoApprove2.description",
"value": "Global auto approve also known as \"YOLO mode\" disables manual approval completely for all tools in all workspaces, allowing the agent to act fully autonomously. This is extremely dangerous and is *never* recommended, even containerized environments like Codespaces and Dev Containers have user keys forwarded into the container that could be compromised.\n\nThis feature disables critical security protections and makes it much easier for an attacker to compromise the machine."
}
},
"type": "boolean",
"default": false
},
{
"key": "chat.mcp.access",
"name": "ChatMCP",
"category": "InteractiveSession",
"minimumVersion": "1.99",
"localization": {
"description": {
"key": "chat.mcp.access",
"value": "Controls access to installed Model Context Protocol servers."
},
"enumDescriptions": [
{
"key": "chat.mcp.access.none",
"value": "No access to MCP servers."
},
{
"key": "chat.mcp.access.registry",
"value": "Allows access to MCP servers installed from the registry that VS Code is connected to."
},
{
"key": "chat.mcp.access.any",
"value": "Allow access to any installed MCP server."
}
]
},
"type": "string",
"default": "all",
"enum": [
"none",
"registry",
"all"
]
},
{
"key": "chat.extensionTools.enabled",
"name": "ChatAgentExtensionTools",
"category": "InteractiveSession",
"minimumVersion": "1.99",
"localization": {
"description": {
"key": "chat.extensionToolsEnabled",
"value": "Enable using tools contributed by third-party extensions."
}
},
"type": "boolean",
"default": true
},
{
"key": "chat.agent.enabled",
"name": "ChatAgentMode",
"category": "InteractiveSession",
"minimumVersion": "1.99",
"localization": {
"description": {
"key": "chat.agent.enabled.description",
"value": "Enable agent mode for chat. When this is enabled, agent mode can be activated via the dropdown in the view."
}
},
"type": "boolean",
"default": true
},
{
"key": "chat.promptFiles",
"name": "ChatPromptFiles",
"category": "InteractiveSession",
"minimumVersion": "1.99",
"localization": {
"description": {
"key": "chat.promptFiles.policy",
"value": "Enables reusable prompt and instruction files in Chat sessions."
}
},
"type": "boolean",
"default": true
},
{
"key": "chat.tools.terminal.enableAutoApprove",
"name": "ChatToolsTerminalEnableAutoApprove",
"category": "IntegratedTerminal",
"minimumVersion": "1.104",
"localization": {
"description": {
"key": "autoApproveMode.description",
"value": "Controls whether to allow auto approval in the run in terminal tool."
}
},
"type": "boolean",
"default": true
},
{
"key": "update.mode",
"name": "UpdateMode",
"category": "Update",
"minimumVersion": "1.67",
"localization": {
"description": {
"key": "updateMode",
"value": "Configure whether you receive automatic updates. Requires a restart after change. The updates are fetched from a Microsoft online service."
},
"enumDescriptions": [
{
"key": "none",
"value": "Disable updates."
},
{
"key": "manual",
"value": "Disable automatic background update checks. Updates will be available if you manually check for updates."
},
{
"key": "start",
"value": "Check for updates only on startup. Disable automatic background update checks."
},
{
"key": "default",
"value": "Enable automatic update checks. Code will check for updates automatically and periodically."
}
]
},
"type": "string",
"default": "default",
"enum": [
"none",
"manual",
"start",
"default"
]
},
{
"key": "telemetry.telemetryLevel",
"name": "TelemetryLevel",
"category": "Telemetry",
"minimumVersion": "1.99",
"localization": {
"description": {
"key": "telemetry.telemetryLevel.policyDescription",
"value": "Controls the level of telemetry."
},
"enumDescriptions": [
{
"key": "telemetry.telemetryLevel.default",
"value": "Sends usage data, errors, and crash reports."
},
{
"key": "telemetry.telemetryLevel.error",
"value": "Sends general error telemetry and crash reports."
},
{
"key": "telemetry.telemetryLevel.crash",
"value": "Sends OS level crash reports."
},
{
"key": "telemetry.telemetryLevel.off",
"value": "Disables all product telemetry."
}
]
},
"type": "string",
"default": "all",
"enum": [
"all",
"error",
"crash",
"off"
]
},
{
"key": "telemetry.feedback.enabled",
"name": "EnableFeedback",
"category": "Telemetry",
"minimumVersion": "1.99",
"localization": {
"description": {
"key": "telemetry.feedback.enabled",
"value": "Enable feedback mechanisms such as the issue reporter, surveys, and other feedback options."
}
},
"type": "boolean",
"default": true
}
]
}

View File

@@ -64,12 +64,9 @@
},
"type": "commonjs",
"scripts": {
"copy-policy-dto": "node lib/policies/copyPolicyDto.js",
"prebuild-ts": "npm run copy-policy-dto",
"build-ts": "cd .. && npx tsgo --project build/tsconfig.build.json",
"compile": "npm run build-ts",
"watch": "npm run build-ts -- --watch",
"npmCheckJs": "npm run build-ts -- --noEmit"
"compile": "cd .. && npx tsgo --project build/tsconfig.build.json",
"watch": "cd .. && npx tsgo --project build/tsconfig.build.json --watch",
"npmCheckJs": "cd .. && npx tsgo --project build/tsconfig.build.json --noEmit"
},
"optionalDependencies": {
"tree-sitter-typescript": "^0.23.2",

View File

@@ -3,52 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from '../../nls.js';
import { IDefaultAccount } from './defaultAccount.js';
export type PolicyName = string;
export type LocalizedValue = {
key: string;
value: string;
};
export enum PolicyCategory {
Extensions = 'Extensions',
IntegratedTerminal = 'IntegratedTerminal',
InteractiveSession = 'InteractiveSession',
Telemetry = 'Telemetry',
Update = 'Update',
}
export const PolicyCategoryData: {
[key in PolicyCategory]: { name: LocalizedValue }
} = {
[PolicyCategory.Extensions]: {
name: {
key: 'extensionsConfigurationTitle', value: localize('extensionsConfigurationTitle', "Extensions"),
}
},
[PolicyCategory.IntegratedTerminal]: {
name: {
key: 'terminalIntegratedConfigurationTitle', value: localize('terminalIntegratedConfigurationTitle', "Integrated Terminal"),
}
},
[PolicyCategory.InteractiveSession]: {
name: {
key: 'interactiveSessionConfigurationTitle', value: localize('interactiveSessionConfigurationTitle', "Chat"),
}
},
[PolicyCategory.Telemetry]: {
name: {
key: 'telemetryConfigurationTitle', value: localize('telemetryConfigurationTitle', "Telemetry"),
}
},
[PolicyCategory.Update]: {
name: {
key: 'updateConfigurationTitle', value: localize('updateConfigurationTitle', "Update"),
}
}
};
export interface IPolicy {
@@ -57,27 +14,15 @@ export interface IPolicy {
*/
readonly name: PolicyName;
/**
* The policy category.
*/
readonly category: PolicyCategory;
/**
* The Code version in which this policy was introduced.
*/
readonly minimumVersion: `${number}.${number}`;
/**
* Localization info for the policy.
*
* IMPORTANT: the key values for these must be unique to avoid collisions, as during the export time the module information is not available.
* The policy description (optional).
*/
readonly localization: {
/** The localization key or key value pair. If only a key is provided, the default value will fallback to the parent configuration's description property. */
description: LocalizedValue;
/** List of localization key or key value pair. If only a key is provided, the default value will fallback to the parent configuration's enumDescriptions property. */
enumDescriptions?: LocalizedValue[];
};
readonly description?: string;
/**
* The value that an ACCOUNT-based feature will use when its corresponding policy is active.

View File

@@ -7,7 +7,6 @@ import assert from 'assert';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from '../../common/configurationRegistry.js';
import { Registry } from '../../../registry/common/platform.js';
import { PolicyCategory } from '../../../../base/common/policy.js';
suite('ConfigurationRegistry', () => {
@@ -90,18 +89,14 @@ suite('ConfigurationRegistry', () => {
'type': 'object',
policy: {
name: 'policy',
category: PolicyCategory.Extensions,
minimumVersion: '1.0.0',
localization: { description: { key: '', value: '' }, }
minimumVersion: '1.0.0'
}
},
'policy2': {
'type': 'object',
policy: {
name: 'policy',
category: PolicyCategory.Extensions,
minimumVersion: '1.0.0',
localization: { description: { key: '', value: '' }, }
minimumVersion: '1.0.0'
}
}
}

View File

@@ -20,7 +20,6 @@ import { NullLogService } from '../../../log/common/log.js';
import { FilePolicyService } from '../../../policy/common/filePolicyService.js';
import { NullPolicyService } from '../../../policy/common/policy.js';
import { Registry } from '../../../registry/common/platform.js';
import { PolicyCategory } from '../../../../base/common/policy.js';
suite('ConfigurationService.test.ts', () => {
@@ -375,9 +374,7 @@ suite('ConfigurationService.test.ts', () => {
'default': 'isSet',
policy: {
name: 'configurationService.policySetting',
category: PolicyCategory.Extensions,
minimumVersion: '1.0.0',
localization: { description: { key: '', value: '' }, }
}
}
}

View File

@@ -19,7 +19,6 @@ import { IPolicyService } from '../../../policy/common/policy.js';
import { FilePolicyService } from '../../../policy/common/filePolicyService.js';
import { runWithFakedTimers } from '../../../../base/test/common/timeTravelScheduler.js';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
import { PolicyCategory } from '../../../../base/common/policy.js';
suite('PolicyConfiguration', () => {
@@ -40,9 +39,7 @@ suite('PolicyConfiguration', () => {
'default': 'defaultValueA',
policy: {
name: 'PolicySettingA',
category: PolicyCategory.Extensions,
minimumVersion: '1.0.0',
localization: { description: { key: '', value: '' }, }
}
},
'policy.settingB': {
@@ -50,9 +47,7 @@ suite('PolicyConfiguration', () => {
'default': 'defaultValueB',
policy: {
name: 'PolicySettingB',
category: PolicyCategory.Extensions,
minimumVersion: '1.0.0',
localization: { description: { key: '', value: '' }, }
}
},
'policy.objectSetting': {
@@ -60,9 +55,7 @@ suite('PolicyConfiguration', () => {
'default': {},
policy: {
name: 'PolicyObjectSetting',
category: PolicyCategory.Extensions,
minimumVersion: '1.0.0',
localization: { description: { key: '', value: '' }, }
}
},
'policy.arraySetting': {
@@ -70,9 +63,7 @@ suite('PolicyConfiguration', () => {
'default': [],
policy: {
name: 'PolicyArraySetting',
category: PolicyCategory.Extensions,
minimumVersion: '1.0.0',
localization: { description: { key: '', value: '' }, }
}
},
'policy.booleanSetting': {
@@ -80,9 +71,7 @@ suite('PolicyConfiguration', () => {
'default': true,
policy: {
name: 'PolicyBooleanSetting',
category: PolicyCategory.Extensions,
minimumVersion: '1.0.0',
localization: { description: { key: '', value: '' }, }
}
},
'policy.internalSetting': {
@@ -91,9 +80,7 @@ suite('PolicyConfiguration', () => {
included: false,
policy: {
name: 'PolicyInternalSetting',
category: PolicyCategory.Extensions,
minimumVersion: '1.0.0',
localization: { description: { key: '', value: '' }, }
}
},
'nonPolicy.setting': {
@@ -280,9 +267,7 @@ suite('PolicyConfiguration', () => {
'default': 'defaultValueC',
policy: {
name: 'PolicySettingC',
category: PolicyCategory.Extensions,
minimumVersion: '1.0.0',
localization: { description: { key: '', value: '' }, },
}
};
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration(deepClone(policyConfigurationNode));

View File

@@ -105,7 +105,6 @@ export interface NativeParsedArgs {
'skip-welcome'?: boolean;
'disable-telemetry'?: boolean;
'export-default-configuration'?: string;
'export-policy-data'?: string;
'install-source'?: string;
'add-mcp'?: string[];
'disable-updates'?: boolean;

View File

@@ -145,7 +145,6 @@ export interface INativeEnvironmentService extends IEnvironmentService {
useInMemorySecretStorage?: boolean;
crossOriginIsolated?: boolean;
exportPolicyData?: string;
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//

View File

@@ -254,10 +254,6 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron
get editSessionId(): string | undefined { return this.args['editSessionId']; }
get exportPolicyData(): string | undefined {
return this.args['export-policy-data'];
}
get continueOn(): string | undefined {
return this.args['continueOn'];
}

View File

@@ -162,7 +162,6 @@ export const OPTIONS: OptionDescriptions<Required<NativeParsedArgs>> = {
'inspect-sharedprocess': { type: 'string', allowEmptyValue: true },
'inspect-brk-sharedprocess': { type: 'string', allowEmptyValue: true },
'export-default-configuration': { type: 'string' },
'export-policy-data': { type: 'string', allowEmptyValue: true },
'install-source': { type: 'string' },
'enable-smoke-test-driver': { type: 'boolean' },
'logExtensionHostCommunication': { type: 'boolean' },

View File

@@ -9,7 +9,6 @@ import { Event } from '../../../base/common/event.js';
import { IMarkdownString } from '../../../base/common/htmlContent.js';
import { IPager } from '../../../base/common/paging.js';
import { Platform } from '../../../base/common/platform.js';
import { PolicyCategory } from '../../../base/common/policy.js';
import { URI } from '../../../base/common/uri.js';
import { localize, localize2 } from '../../../nls.js';
import { ConfigurationScope, Extensions, IConfigurationRegistry } from '../../configuration/common/configurationRegistry.js';
@@ -735,14 +734,8 @@ Registry.as<IConfigurationRegistry>(Extensions.Configuration)
scope: ConfigurationScope.APPLICATION,
policy: {
name: 'AllowedExtensions',
category: PolicyCategory.Extensions,
minimumVersion: '1.96',
localization: {
description: {
key: 'extensions.allowed.policy',
value: localize('extensions.allowed.policy', "Specify a list of extensions that are allowed to use. This helps maintain a secure and consistent development environment by restricting the use of unauthorized extensions. More information: https://code.visualstudio.com/docs/setup/enterprise#_configure-allowed-extensions"),
}
}
description: localize('extensions.allowed.policy', "Specify a list of extensions that are allowed to use. This helps maintain a secure and consistent development environment by restricting the use of unauthorized extensions. More information: https://code.visualstudio.com/docs/setup/enterprise#_configure-allowed-extensions"),
},
additionalProperties: false,
patternProperties: {

View File

@@ -6,7 +6,6 @@
import { DisposableStore } from '../../../base/common/lifecycle.js';
import { mixin } from '../../../base/common/objects.js';
import { isWeb } from '../../../base/common/platform.js';
import { PolicyCategory } from '../../../base/common/policy.js';
import { escapeRegExpCharacters } from '../../../base/common/strings.js';
import { localize } from '../../../nls.js';
import { IConfigurationService } from '../../configuration/common/configuration.js';
@@ -224,32 +223,8 @@ configurationRegistry.registerConfiguration({
'tags': ['usesOnlineServices', 'telemetry'],
'policy': {
name: 'TelemetryLevel',
category: PolicyCategory.Telemetry,
minimumVersion: '1.99',
localization: {
description: {
key: 'telemetry.telemetryLevel.policyDescription',
value: localize('telemetry.telemetryLevel.policyDescription', "Controls the level of telemetry."),
},
enumDescriptions: [
{
key: 'telemetry.telemetryLevel.default',
value: localize('telemetry.telemetryLevel.default', "Sends usage data, errors, and crash reports."),
},
{
key: 'telemetry.telemetryLevel.error',
value: localize('telemetry.telemetryLevel.error', "Sends general error telemetry and crash reports."),
},
{
key: 'telemetry.telemetryLevel.crash',
value: localize('telemetry.telemetryLevel.crash', "Sends OS level crash reports."),
},
{
key: 'telemetry.telemetryLevel.off',
value: localize('telemetry.telemetryLevel.off', "Disables all product telemetry."),
}
]
}
description: localize('telemetry.telemetryLevel.policyDescription', "Controls the level of telemetry."),
}
},
'telemetry.feedback.enabled': {
@@ -258,9 +233,7 @@ configurationRegistry.registerConfiguration({
description: localize('telemetry.feedback.enabled', "Enable feedback mechanisms such as the issue reporter, surveys, and other feedback options."),
policy: {
name: 'EnableFeedback',
category: PolicyCategory.Telemetry,
minimumVersion: '1.99',
localization: { description: { key: 'telemetry.feedback.enabled', value: localize('telemetry.feedback.enabled', "Enable feedback mechanisms such as the issue reporter, surveys, and other feedback options.") } },
}
},
// Deprecated telemetry setting

View File

@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import { isWeb, isWindows } from '../../../base/common/platform.js';
import { PolicyCategory } from '../../../base/common/policy.js';
import { localize } from '../../../nls.js';
import { ConfigurationScope, Extensions as ConfigurationExtensions, IConfigurationRegistry } from '../../configuration/common/configurationRegistry.js';
import { Registry } from '../../registry/common/platform.js';
@@ -31,29 +30,7 @@ configurationRegistry.registerConfiguration({
],
policy: {
name: 'UpdateMode',
category: PolicyCategory.Update,
minimumVersion: '1.67',
localization: {
description: { key: 'updateMode', value: localize('updateMode', "Configure whether you receive automatic updates. Requires a restart after change. The updates are fetched from a Microsoft online service."), },
enumDescriptions: [
{
key: 'none',
value: localize('none', "Disable updates."),
},
{
key: 'manual',
value: localize('manual', "Disable automatic background update checks. Updates will be available if you manually check for updates."),
},
{
key: 'start',
value: localize('start', "Check for updates only on startup. Disable automatic background update checks."),
},
{
key: 'default',
value: localize('default', "Enable automatic update checks. Code will check for updates automatically and periodically."),
}
]
},
}
},
'update.channel': {

View File

@@ -123,7 +123,6 @@ import { SAVE_TO_PROMPT_ACTION_ID, SAVE_TO_PROMPT_SLASH_COMMAND_NAME } from './p
import { ConfigureToolSets, UserToolSetsContributions } from './tools/toolSetsContribution.js';
import { ChatViewsWelcomeHandler } from './viewsWelcome/chatViewsWelcomeHandler.js';
import { ChatSessionsView } from './chatSessions/view/chatSessionsView.js';
import { PolicyCategory } from '../../../../base/common/policy.js';
// Register configuration
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
@@ -241,21 +240,18 @@ configurationRegistry.registerConfiguration({
},
[ChatConfiguration.GlobalAutoApprove]: {
default: false,
// HACK: Description duplicated for policy parser. See https://github.com/microsoft/vscode/issues/254526
description: nls.localize('autoApprove2.description',
'Global auto approve also known as "YOLO mode" disables manual approval completely for all tools in all workspaces, allowing the agent to act fully autonomously. This is extremely dangerous and is *never* recommended, even containerized environments like Codespaces and Dev Containers have user keys forwarded into the container that could be compromised.\n\nThis feature disables critical security protections and makes it much easier for an attacker to compromise the machine.'
),
markdownDescription: globalAutoApproveDescription.value,
type: 'boolean',
scope: ConfigurationScope.APPLICATION_MACHINE,
tags: ['experimental'],
policy: {
name: 'ChatToolsAutoApprove',
category: PolicyCategory.InteractiveSession,
minimumVersion: '1.99',
value: (account) => account.chat_preview_features_enabled === false ? false : undefined,
localization: {
description: {
key: 'autoApprove2.description',
value: nls.localize('autoApprove2.description', 'Global auto approve also known as "YOLO mode" disables manual approval completely for all tools in all workspaces, allowing the agent to act fully autonomously. This is extremely dangerous and is *never* recommended, even containerized environments like Codespaces and Dev Containers have user keys forwarded into the container that could be compromised.\n\nThis feature disables critical security protections and makes it much easier for an attacker to compromise the machine.')
}
},
}
},
[ChatConfiguration.AutoApproveEdits]: {
@@ -348,7 +344,6 @@ configurationRegistry.registerConfiguration({
default: McpAccessValue.All,
policy: {
name: 'ChatMCP',
category: PolicyCategory.InteractiveSession,
minimumVersion: '1.99',
value: (account) => {
if (account.mcp === false) {
@@ -359,23 +354,6 @@ configurationRegistry.registerConfiguration({
}
return undefined;
},
localization: {
description: {
key: 'chat.mcp.access',
value: nls.localize('chat.mcp.access', "Controls access to installed Model Context Protocol servers.")
},
enumDescriptions: [
{
key: 'chat.mcp.access.none', value: nls.localize('chat.mcp.access.none', "No access to MCP servers."),
},
{
key: 'chat.mcp.access.registry', value: nls.localize('chat.mcp.access.registry', "Allows access to MCP servers installed from the registry that VS Code is connected to."),
},
{
key: 'chat.mcp.access.any', value: nls.localize('chat.mcp.access.any', "Allow access to any installed MCP server.")
}
]
},
}
},
[mcpAutoStartConfig]: {
@@ -441,14 +419,8 @@ configurationRegistry.registerConfiguration({
default: true,
policy: {
name: 'ChatAgentExtensionTools',
category: PolicyCategory.InteractiveSession,
minimumVersion: '1.99',
localization: {
description: {
key: 'chat.extensionToolsEnabled',
value: nls.localize('chat.extensionToolsEnabled', "Enable using tools contributed by third-party extensions.")
}
},
description: nls.localize('chat.extensionToolsPolicy', "Enable using tools contributed by third-party extensions."),
}
},
[ChatConfiguration.AgentEnabled]: {
@@ -457,15 +429,8 @@ configurationRegistry.registerConfiguration({
default: true,
policy: {
name: 'ChatAgentMode',
category: PolicyCategory.InteractiveSession,
minimumVersion: '1.99',
value: (account) => account.chat_agent_enabled === false ? false : undefined,
localization: {
description: {
key: 'chat.agent.enabled.description',
value: nls.localize('chat.agent.enabled.description', "Enable agent mode for chat. When this is enabled, agent mode can be activated via the dropdown in the view."),
}
}
}
},
[ChatConfiguration.EnableMath]: {
@@ -507,15 +472,8 @@ configurationRegistry.registerConfiguration({
included: false,
policy: {
name: 'McpGalleryServiceUrl',
category: PolicyCategory.InteractiveSession,
minimumVersion: '1.101',
value: (account) => account.mcpRegistryUrl,
localization: {
description: {
key: 'mcp.gallery.serviceUrl',
value: nls.localize('mcp.gallery.serviceUrl', "Configure the MCP Gallery service URL to connect to"),
}
}
value: (account) => account.mcpRegistryUrl
},
},
[PromptsConfig.KEY]: {
@@ -537,14 +495,8 @@ configurationRegistry.registerConfiguration({
tags: ['experimental', 'prompts', 'reusable prompts', 'prompt snippets', 'instructions'],
policy: {
name: 'ChatPromptFiles',
category: PolicyCategory.InteractiveSession,
minimumVersion: '1.99',
localization: {
description: {
key: 'chat.promptFiles.policy',
value: nls.localize('chat.promptFiles.policy', "Enables reusable prompt and instruction files in Chat sessions.")
}
}
description: nls.localize('chat.promptFiles.policy', "Enables reusable prompt and instruction files in Chat sessions.")
}
},
[PromptsConfig.INSTRUCTIONS_LOCATION_KEY]: {

View File

@@ -13,7 +13,6 @@ import { mnemonicButtonLabel } from '../../../../base/common/labels.js';
import { Disposable, DisposableStore, IDisposable, isDisposable } from '../../../../base/common/lifecycle.js';
import { Schemas } from '../../../../base/common/network.js';
import { isNative, isWeb } from '../../../../base/common/platform.js';
import { PolicyCategory } from '../../../../base/common/policy.js';
import { URI, UriComponents } from '../../../../base/common/uri.js';
import { MultiCommand } from '../../../../editor/browser/editorExtensions.js';
import { CopyAction, CutAction, PasteAction } from '../../../../editor/contrib/clipboard/browser/clipboard.js';
@@ -287,14 +286,7 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
included: false,
policy: {
name: 'ExtensionGalleryServiceUrl',
category: PolicyCategory.Extensions,
minimumVersion: '1.99',
localization: {
description: {
key: 'extensions.gallery.serviceUrl',
value: localize('extensions.gallery.serviceUrl', "Configure the Marketplace service URL to connect to"),
}
}
},
},
'extensions.supportNodeGlobalNavigator': {

View File

@@ -1,33 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export type LocalizedValueDto = {
key: string;
value: string;
};
export interface CategoryDto {
key: string;
name: LocalizedValueDto;
}
export interface PolicyDto {
key: string;
name: string;
category: string;
minimumVersion: `${number}.${number}`;
localization: {
description: LocalizedValueDto;
enumDescriptions?: LocalizedValueDto[];
};
type?: string | string[];
default?: unknown;
enum?: string[];
}
export interface ExportedPolicyDataDto {
categories: CategoryDto[];
policies: PolicyDto[];
}

View File

@@ -1,117 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js';
import { Disposable } from '../../../../base/common/lifecycle.js';
import { IWorkbenchConfigurationService } from '../../../services/configuration/common/configuration.js';
import { IExtensionService } from '../../../services/extensions/common/extensions.js';
import { INativeEnvironmentService } from '../../../../platform/environment/common/environment.js';
import { ILogService } from '../../../../platform/log/common/log.js';
import { INativeHostService } from '../../../../platform/native/common/native.js';
import { Registry } from '../../../../platform/registry/common/platform.js';
import { Extensions, IConfigurationRegistry } from '../../../../platform/configuration/common/configurationRegistry.js';
import { IProgressService, ProgressLocation } from '../../../../platform/progress/common/progress.js';
import { IFileService } from '../../../../platform/files/common/files.js';
import { URI } from '../../../../base/common/uri.js';
import { VSBuffer } from '../../../../base/common/buffer.js';
import { PolicyCategory, PolicyCategoryData } from '../../../../base/common/policy.js';
import { ExportedPolicyDataDto } from '../common/policyDto.js';
import { join } from '../../../../base/common/path.js';
export class PolicyExportContribution extends Disposable implements IWorkbenchContribution {
static readonly ID = 'workbench.contrib.policyExport';
static readonly DEFAULT_POLICY_EXPORT_PATH = 'build/lib/policies/policyData.jsonc';
constructor(
@INativeEnvironmentService private readonly nativeEnvironmentService: INativeEnvironmentService,
@IExtensionService private readonly extensionService: IExtensionService,
@IFileService private readonly fileService: IFileService,
@IWorkbenchConfigurationService private readonly configurationService: IWorkbenchConfigurationService,
@INativeHostService private readonly nativeHostService: INativeHostService,
@IProgressService private readonly progressService: IProgressService,
@ILogService private readonly logService: ILogService,
) {
super();
// Skip for non-development flows
if (this.nativeEnvironmentService.isBuilt) {
return;
}
const policyDataPath = this.nativeEnvironmentService.exportPolicyData;
if (policyDataPath !== undefined) {
const defaultPath = join(this.nativeEnvironmentService.appRoot, PolicyExportContribution.DEFAULT_POLICY_EXPORT_PATH);
void this.exportPolicyDataAndQuit(policyDataPath ? policyDataPath : defaultPath);
}
}
private log(msg: string | undefined, ...args: unknown[]) {
this.logService.info(`[${PolicyExportContribution.ID}]`, msg, ...args);
}
private async exportPolicyDataAndQuit(policyDataPath: string): Promise<void> {
try {
await this.progressService.withProgress({
location: ProgressLocation.Notification,
title: `Exporting policy data to ${policyDataPath}`
}, async (_progress) => {
this.log('Export started. Waiting for configurations to load.');
await this.extensionService.whenInstalledExtensionsRegistered();
await this.configurationService.whenRemoteConfigurationLoaded();
this.log('Extensions and configuration loaded.');
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
const configurationProperties = {
...configurationRegistry.getExcludedConfigurationProperties(),
...configurationRegistry.getConfigurationProperties(),
};
const policyData: ExportedPolicyDataDto = {
categories: Object.values(PolicyCategory).map(category => ({
key: category,
name: PolicyCategoryData[category].name
})),
policies: []
};
for (const [key, schema] of Object.entries(configurationProperties)) {
// Check for the localization property for now to remain backwards compatible.
if (schema.policy?.localization) {
policyData.policies.push({
key,
name: schema.policy.name,
category: schema.policy.category,
minimumVersion: schema.policy.minimumVersion,
localization: {
description: schema.policy.localization.description,
enumDescriptions: schema.policy.localization.enumDescriptions,
},
type: schema.type,
default: schema.default,
enum: schema.enum,
});
}
}
this.log(`Discovered ${policyData.policies.length} policies to export.`);
const disclaimerComment = `/** THIS FILE IS AUTOMATICALLY GENERATED USING \`code --export-policy-data\`. DO NOT MODIFY IT MANUALLY. **/`;
const policyDataFileContent = `${disclaimerComment}\n${JSON.stringify(policyData, null, 4)}\n`;
await this.fileService.writeFile(URI.file(policyDataPath), VSBuffer.fromString(policyDataFileContent));
this.log(`Successfully exported ${policyData.policies.length} policies to ${policyDataPath}.`);
});
await this.nativeHostService.exit(0);
} catch (error) {
this.log('Failed to export policy', error);
await this.nativeHostService.exit(1);
}
}
}
registerWorkbenchContribution2(
PolicyExportContribution.ID,
PolicyExportContribution,
WorkbenchPhase.Eventually,
);

View File

@@ -1,60 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as cp from 'child_process';
import { promises as fs } from 'fs';
import * as os from 'os';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
import { isWindows } from '../../../../../base/common/platform.js';
import { join } from '../../../../../base/common/path.js';
import { FileAccess } from '../../../../../base/common/network.js';
import * as util from 'util';
const exec = util.promisify(cp.exec);
suite('PolicyExport Integration Tests', () => {
ensureNoDisposablesAreLeakedInTestSuite();
test('exported policy data matches checked-in file', async function () {
// This test launches VS Code with --export-policy-data flag, so it takes longer
this.timeout(60000);
const rootPath = FileAccess.asFileUri('').fsPath.replace(/[\/\\]out[\/\\].*$/, '');
const checkedInFile = join(rootPath, 'build/lib/policies/policyData.jsonc');
const tempFile = join(os.tmpdir(), `policyData-test-${Date.now()}.jsonc`);
try {
// Launch VS Code with --export-policy-data flag
const scriptPath = isWindows
? join(rootPath, 'scripts', 'code.bat')
: join(rootPath, 'scripts', 'code.sh');
await exec(`"${scriptPath}" --export-policy-data="${tempFile}"`, {
cwd: rootPath
});
// Read both files
const [exportedContent, checkedInContent] = await Promise.all([
fs.readFile(tempFile, 'utf-8'),
fs.readFile(checkedInFile, 'utf-8')
]);
// Compare contents
assert.strictEqual(
exportedContent,
checkedInContent,
'Exported policy data should match the checked-in file. If this fails, run: ./scripts/code.sh --export-policy-data'
);
} finally {
// Clean up temp file
try {
await fs.unlink(tempFile);
} catch {
// Ignore cleanup errors
}
}
});
});

View File

@@ -7,7 +7,6 @@ import { Codicon } from '../../../../base/common/codicons.js';
import type { IStringDictionary } from '../../../../base/common/collections.js';
import { IJSONSchemaSnippet } from '../../../../base/common/jsonSchema.js';
import { isMacintosh, isWindows } from '../../../../base/common/platform.js';
import { PolicyCategory } from '../../../../base/common/policy.js';
import { localize } from '../../../../nls.js';
import { ConfigurationScope, Extensions, IConfigurationRegistry, type IConfigurationPropertySchema } from '../../../../platform/configuration/common/configurationRegistry.js';
import product from '../../../../platform/product/common/product.js';
@@ -646,14 +645,7 @@ export async function registerTerminalConfiguration(getFontSnippets: () => Promi
default: true,
policy: {
name: 'ChatToolsTerminalEnableAutoApprove',
category: PolicyCategory.IntegratedTerminal,
minimumVersion: '1.104',
localization: {
description: {
key: 'autoApproveMode.description',
value: localize('autoApproveMode.description', "Controls whether to allow auto approval in the run in terminal tool."),
}
}
}
}
}

View File

@@ -47,7 +47,6 @@ import { UserDataProfileService } from '../../../userDataProfile/common/userData
import { IUserDataProfileService } from '../../../userDataProfile/common/userDataProfile.js';
import { IBrowserWorkbenchEnvironmentService } from '../../../environment/browser/environmentService.js';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
import { PolicyCategory } from '../../../../../base/common/policy.js';
const ROOT = URI.file('tests').with({ scheme: 'vscode-tests' });
@@ -90,9 +89,7 @@ suite('ConfigurationEditing', () => {
'default': 'isSet',
policy: {
name: 'configurationEditing.service.policySetting',
category: PolicyCategory.Extensions,
minimumVersion: '1.0.0',
localization: { description: { key: '', value: '' } }
}
}
}

View File

@@ -52,7 +52,6 @@ import { IUserDataProfileService } from '../../../userDataProfile/common/userDat
import { TasksSchemaProperties } from '../../../../contrib/tasks/common/tasks.js';
import { RemoteSocketFactoryService } from '../../../../../platform/remote/common/remoteSocketFactoryService.js';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
import { PolicyCategory } from '../../../../../base/common/policy.js';
function convertToWorkspacePayload(folder: URI): ISingleFolderWorkspaceIdentifier {
return {
@@ -782,9 +781,7 @@ suite('WorkspaceConfigurationService - Folder', () => {
'default': 'isSet',
policy: {
name: 'configurationService.folder.policySetting',
category: PolicyCategory.Extensions,
minimumVersion: '1.0.0',
localization: { description: { key: '', value: '' } }
}
},
'configurationService.folder.policyObjectSetting': {
@@ -792,9 +789,7 @@ suite('WorkspaceConfigurationService - Folder', () => {
'default': {},
policy: {
name: 'configurationService.folder.policyObjectSetting',
category: PolicyCategory.Extensions,
minimumVersion: '1.0.0',
localization: { description: { key: '', value: '' } }
}
},
}

View File

@@ -12,7 +12,6 @@ import { Registry } from '../../../../../platform/registry/common/platform.js';
import { Extensions, IConfigurationNode, IConfigurationRegistry } from '../../../../../platform/configuration/common/configurationRegistry.js';
import { DefaultConfiguration, PolicyConfiguration } from '../../../../../platform/configuration/common/configurations.js';
import { IDefaultAccount } from '../../../../../base/common/defaultAccount.js';
import { PolicyCategory } from '../../../../../base/common/policy.js';
const BASE_DEFAULT_ACCOUNT: IDefaultAccount = {
enterprise: false,
@@ -39,9 +38,7 @@ suite('AccountPolicyService', () => {
'default': 'defaultValueA',
policy: {
name: 'PolicySettingA',
category: PolicyCategory.Extensions,
minimumVersion: '1.0.0',
localization: { description: { key: '', value: '' } }
}
},
'setting.B': {
@@ -49,9 +46,7 @@ suite('AccountPolicyService', () => {
'default': 'defaultValueB',
policy: {
name: 'PolicySettingB',
category: PolicyCategory.Extensions,
minimumVersion: '1.0.0',
localization: { description: { key: '', value: '' } },
value: account => account.chat_preview_features_enabled === false ? 'policyValueB' : undefined,
}
},
@@ -60,9 +55,7 @@ suite('AccountPolicyService', () => {
'default': ['defaultValueC1', 'defaultValueC2'],
policy: {
name: 'PolicySettingC',
category: PolicyCategory.Extensions,
minimumVersion: '1.0.0',
localization: { description: { key: '', value: '' } },
value: account => account.chat_preview_features_enabled === false ? JSON.stringify(['policyValueC1', 'policyValueC2']) : undefined,
}
},
@@ -71,9 +64,7 @@ suite('AccountPolicyService', () => {
'default': true,
policy: {
name: 'PolicySettingD',
category: PolicyCategory.Extensions,
minimumVersion: '1.0.0',
localization: { description: { key: '', value: '' } },
value: account => account.chat_preview_features_enabled === false ? false : undefined,
}
},

View File

@@ -19,7 +19,6 @@ import { InMemoryFileSystemProvider } from '../../../../../platform/files/common
import { FileService } from '../../../../../platform/files/common/fileService.js';
import { VSBuffer } from '../../../../../base/common/buffer.js';
import { IDefaultAccount } from '../../../../../base/common/defaultAccount.js';
import { PolicyCategory } from '../../../../../base/common/policy.js';
const BASE_DEFAULT_ACCOUNT: IDefaultAccount = {
enterprise: false,
@@ -48,9 +47,7 @@ suite('MultiplexPolicyService', () => {
'default': 'defaultValueA',
policy: {
name: 'PolicySettingA',
category: PolicyCategory.Extensions,
minimumVersion: '1.0.0',
localization: { description: { key: '', value: '' } }
}
},
'setting.B': {
@@ -58,9 +55,7 @@ suite('MultiplexPolicyService', () => {
'default': 'defaultValueB',
policy: {
name: 'PolicySettingB',
category: PolicyCategory.Extensions,
minimumVersion: '1.0.0',
localization: { description: { key: '', value: '' } },
value: account => account.chat_preview_features_enabled === false ? 'policyValueB' : undefined,
}
},
@@ -69,9 +64,7 @@ suite('MultiplexPolicyService', () => {
'default': ['defaultValueC1', 'defaultValueC2'],
policy: {
name: 'PolicySettingC',
category: PolicyCategory.Extensions,
minimumVersion: '1.0.0',
localization: { description: { key: '', value: '' } },
value: account => account.chat_preview_features_enabled === false ? JSON.stringify(['policyValueC1', 'policyValueC2']) : undefined,
}
},
@@ -80,9 +73,7 @@ suite('MultiplexPolicyService', () => {
'default': true,
policy: {
name: 'PolicySettingD',
category: PolicyCategory.Extensions,
minimumVersion: '1.0.0',
localization: { description: { key: '', value: '' } },
value: account => account.chat_preview_features_enabled === false ? false : undefined,
}
},

View File

@@ -182,9 +182,6 @@ import './contrib/emergencyAlert/electron-browser/emergencyAlert.contribution.js
// MCP
import './contrib/mcp/electron-browser/mcp.contribution.js';
// Policy Export
import './contrib/policyExport/electron-browser/policyExport.contribution.js';
//#endregion