Files
vscode/build/lib/extensionTarget.ts
T
4e6997ae07 build: support platform-specific built-in extensions from GitHub releases (#324206)
* Support platform-specific built-in extensions from GitHub releases

Adds support for downloading platform-specific built-in extension VSIXs
from GitHub releases, keyed by marketplace target platform. Also downloads
the latest pre-release assets for insiders builds, ignoring the pinned
version and checksum.

- extensionTarget.ts: resolve build target + release asset name
- builtInExtensions.ts: platformSpecific checksum map + insiders detection
- extensions.ts/fetch.ts: fromGithub asset selection + prerelease support
- CI: platform-aware cache key and target env plumbing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Insiders downloads the latest release instead of latest pre-release

Insiders builds should always be on the newest published release, so the
GitHub download now resolves the most recently published release (including
pre-releases) rather than filtering to pre-releases only.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address code review feedback for platform-specific extensions

- Sort latest releases by published_at instead of created_at
- Log a warning when skipping checksum validation in latest mode
- Document that platform-specific extensions always download from GitHub
- Skip gracefully on unsupported platforms; keep a clear error for a
  known target missing from the platformSpecific map

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address CCR feedback: restrict targets and harden helpers

- Restrict getExtensionTarget to the supported marketplace targets, returning
  undefined for unsupported OS/arch (e.g. win32-ia32, linux-riscv64) so callers
  skip gracefully instead of failing with a missing-asset error
- Remove the bogus ia32 -> x86 mapping (no win32-x86 target exists)
- Validate the target format in getPlatformSpecificAssetName and throw a clear
  error for malformed targets
- Guard the latest-release sort against NaN timestamps (missing published_at
  sorts to the end deterministically)
- Update tests accordingly

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Align asset naming with VS Code convention and support product.overrides.json

The platform-specific extension this feature targets (typescript-go's
TypeScriptTeam.native-preview) publishes VSIX assets named
<name>-<target>.vsix using the raw marketplace target platform, not the
node-vsce-sign osx/win/arm aliasing. Update getPlatformSpecificAssetName to
the standard <name>-<target>.vsix convention and validate against the
supported target set.

- fetch.ts: in latest mode, select the newest published release that actually
  contains the requested asset, so releases shipping only other artifacts
  (e.g. tarballs) without a matching VSIX are skipped
- builtInExtensions.ts: merge product.overrides.json (gitignored, local) so
  overridden built-in extensions are downloaded, mirroring bootstrap-meta
- Update tests for the new naming convention

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Detect extensions project by path segment, not substring

ESBuildTranspiler decided CJS vs ESM output via configFilePath.includes('extensions').
When the repo is checked out (e.g. as a git worktree) into a folder whose name
merely contains the substring 'extensions', the 'src' project was wrongly treated
as an extension and transpiled to CJS, breaking top-level await in src/cli.ts,
src/server-cli.ts and src/server-main.ts. Match an 'extensions' path segment instead.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Remove product.overrides.json support from built-in extensions download

Revert the download script to read product.json directly, per review feedback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-14 10:26:58 +00:00

117 lines
4.2 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 fs from 'fs';
/**
* Detects whether the current Linux system uses musl libc (Alpine Linux).
* Mirrors the detection used in `node-vsce-sign` and the VS Code extension management.
*/
export function isAlpineLinux(): boolean {
let content: string | undefined;
for (const filePath of ['/etc/os-release', '/usr/lib/os-release']) {
try {
content = fs.readFileSync(filePath, 'utf8');
break;
} catch (err) {
// ignore and try the next file
}
}
return !!content && (content.match(/^ID=([^\u001b\r\n]*)/m) || [])[1] === 'alpine';
}
/**
* The set of platform-specific marketplace target platforms, matching the `TargetPlatform` enum
* in `src/vs/platform/extensions/common/extensions.ts` (excluding the non platform-specific
* `web`/`universal`/`unknown`/`undefined` values).
*/
const supportedTargets = new Set([
'win32-x64', 'win32-arm64',
'linux-x64', 'linux-arm64', 'linux-armhf',
'alpine-x64', 'alpine-arm64',
'darwin-x64', 'darwin-arm64',
]);
/**
* Normalizes an architecture (from `VSCODE_ARCH` or `process.arch`) to the suffix used
* by the marketplace target platform identifiers.
*/
function toTargetArch(arch: string): string {
switch (arch) {
case 'arm': return 'armhf';
default: return arch;
}
}
/**
* Returns the marketplace target platform (e.g. `win32-x64`, `linux-armhf`, `alpine-x64`)
* for the given platform and architecture. Mirrors the `TargetPlatform` enum in
* `src/vs/platform/extensions/common/extensions.ts`.
*
* @returns the target platform string, or `undefined` when the combination is not a supported
* marketplace target (e.g. an unsupported OS or architecture such as `win32-ia32` or `linux-riscv64`).
*/
export function getExtensionTarget(platform: string, arch: string, isAlpine: () => boolean = isAlpineLinux): string | undefined {
const targetArch = toTargetArch(arch);
let target: string | undefined;
switch (platform) {
case 'darwin':
target = `darwin-${targetArch}`;
break;
case 'win32':
target = `win32-${targetArch}`;
break;
case 'linux':
target = isAlpine() ? `alpine-${targetArch}` : `linux-${targetArch}`;
break;
}
return target && supportedTargets.has(target) ? target : undefined;
}
/**
* Reads an environment variable, ignoring empty values and unexpanded Azure Pipelines
* macros (e.g. a literal `$(VSCODE_ARCH)` left in place when the variable is not defined).
*/
function readEnv(name: string): string | undefined {
const value = process.env[name];
if (!value || value.startsWith('$(')) {
return undefined;
}
return value;
}
/**
* Returns the marketplace target platform for the current build.
*
* Resolution order:
* 1. `VSCODE_EXTENSION_TARGET` env, when set — an explicit override for cross-compilation
* scenarios where the target cannot be detected from the host (e.g. building the alpine
* target on a glibc host).
* 2. `process.platform` + (`VSCODE_ARCH` ?? `process.arch`) + runtime alpine detection.
*/
export function getCurrentExtensionTarget(): string | undefined {
const override = readEnv('VSCODE_EXTENSION_TARGET');
if (override) {
return override;
}
const arch = readEnv('VSCODE_ARCH') ?? process.arch;
return getExtensionTarget(process.platform, arch);
}
/**
* Derives the GitHub release asset name for a platform-specific extension from its name and
* marketplace target platform. Platform-specific VS Code extensions are conventionally named
* `<name>-<target>.vsix` where `<target>` is the marketplace target platform (e.g.
* `my-ext-win32-x64.vsix`, `my-ext-linux-armhf.vsix`, `my-ext-darwin-arm64.vsix`).
*
* @throws when `target` is not a supported marketplace target platform.
*/
export function getPlatformSpecificAssetName(name: string, target: string): string {
if (!supportedTargets.has(target)) {
throw new Error(`Invalid target platform '${target}': expected one of [${[...supportedTargets]}]`);
}
return `${name}-${target}.vsix`;
}