mirror of
https://github.com/microsoft/vscode.git
synced 2026-02-15 07:28:05 +00:00
feat: create versioned resources for windows setup (#263998)
* feat: create versioned resources for windows setup * chore: use inno_updater to remove old installation * chore: remove old installation as part of setup * chore: update explorer-command * chore: prefer session-end * chore: uninst delete updating_version * chore: make session-ending write synchronous * chore: cleanup updateService.win32.ts * chore: invoke inno_updater gc path for non background update * chore: move session-end path to runtime * chore: use commit for updating_version * chore: fix invalid string * chore: set appUpdate path * chore: update inno_updater * chore: empty commit for testing * chore: some cleanups 1) Check for session-ending flag in appx and tunnel callsites 2) Move gc for background update to cleanup phase in updateservice 3) Set update state to ready when there is a running inno_setup * chore: disallow same version update * chore: disallow application launch in the middle of update * chore: empty commit for testing * chore: bump inno_updater * chore: empty commit for testing * chore: move gc to update startup * chore: move feature behind insider only check * chore: bump inno_updater * chore: bump explorer-command * fix: build * fix: gc for background update in system setup * chore: create separate cli entrypoints for build * fix: check for setup mutex created by inno * chore: remove problematic updatingVersionPath deletion * chore: remove redundant update check * chore: bump inno_updater * chore: fix build * chore: bump inno updater
This commit is contained in:
@@ -43,11 +43,8 @@ async function main() {
|
||||
|
||||
// Package client
|
||||
if (process.env['BUILT_CLIENT']) {
|
||||
// Product version
|
||||
const version = await $`node -p "require('../VSCode-win32-${arch}/resources/app/package.json').version"`;
|
||||
|
||||
printBanner('Package client');
|
||||
const clientArchivePath = `.build/win32-${arch}/VSCode-win32-${arch}-${version}.zip`;
|
||||
const clientArchivePath = `.build/win32-${arch}/VSCode-win32-${arch}.zip`;
|
||||
await $`7z.exe a -tzip ${clientArchivePath} ../VSCode-win32-${arch}/* "-xr!CodeSignSummary*.md"`.pipe(process.stdout);
|
||||
await $`7z.exe l ${clientArchivePath}`.pipe(process.stdout);
|
||||
}
|
||||
|
||||
@@ -192,7 +192,8 @@ steps:
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ArtifactName = (gci -Path "$(Build.ArtifactStagingDirectory)/cli" | Select-Object -last 1).FullName
|
||||
Expand-Archive -Path $ArtifactName -DestinationPath "$(Build.ArtifactStagingDirectory)/cli"
|
||||
$AppProductJson = Get-Content -Raw -Path "$(Agent.BuildDirectory)\VSCode-win32-$(VSCODE_ARCH)\resources\app\product.json" | ConvertFrom-Json
|
||||
$ProductJsonPath = (Get-ChildItem -Path "$(Agent.BuildDirectory)\VSCode-win32-$(VSCODE_ARCH)" -Name "product.json" -Recurse | Select-Object -First 1)
|
||||
$AppProductJson = Get-Content -Raw -Path "$(Agent.BuildDirectory)\VSCode-win32-$(VSCODE_ARCH)\$ProductJsonPath" | ConvertFrom-Json
|
||||
$CliAppName = $AppProductJson.tunnelApplicationName
|
||||
$AppName = $AppProductJson.applicationName
|
||||
Move-Item -Path "$(Build.ArtifactStagingDirectory)/cli/$AppName.exe" -Destination "$(Agent.BuildDirectory)/VSCode-win32-$(VSCODE_ARCH)/bin/$CliAppName.exe"
|
||||
@@ -249,7 +250,8 @@ steps:
|
||||
- powershell: |
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$PackageJson = Get-Content -Raw -Path ..\VSCode-win32-$(VSCODE_ARCH)\resources\app\package.json | ConvertFrom-Json
|
||||
$PackageJsonPath = (Get-ChildItem -Path "..\VSCode-win32-$(VSCODE_ARCH)" -Name "package.json" -Recurse | Select-Object -First 1)
|
||||
$PackageJson = Get-Content -Raw -Path ..\VSCode-win32-$(VSCODE_ARCH)\$PackageJsonPath | ConvertFrom-Json
|
||||
$Version = $PackageJson.version
|
||||
|
||||
mkdir $(Build.ArtifactStagingDirectory)\out\system-setup -Force
|
||||
@@ -259,7 +261,7 @@ steps:
|
||||
mv .build\win32-$(VSCODE_ARCH)\user-setup\VSCodeSetup.exe $(Build.ArtifactStagingDirectory)\out\user-setup\VSCodeUserSetup-$(VSCODE_ARCH)-$Version.exe
|
||||
|
||||
mkdir $(Build.ArtifactStagingDirectory)\out\archive -Force
|
||||
mv .build\win32-$(VSCODE_ARCH)\VSCode-win32-$(VSCODE_ARCH)-$Version.zip $(Build.ArtifactStagingDirectory)\out\archive\VSCode-win32-$(VSCODE_ARCH)-$Version.zip
|
||||
mv .build\win32-$(VSCODE_ARCH)\VSCode-win32-$(VSCODE_ARCH).zip $(Build.ArtifactStagingDirectory)\out\archive\VSCode-win32-$(VSCODE_ARCH)-$Version.zip
|
||||
|
||||
mkdir $(Build.ArtifactStagingDirectory)\out\server -Force
|
||||
mv .build\win32-$(VSCODE_ARCH)\vscode-server-win32-$(VSCODE_ARCH).zip $(Build.ArtifactStagingDirectory)\out\server\vscode-server-win32-$(VSCODE_ARCH).zip
|
||||
|
||||
@@ -76,7 +76,8 @@ steps:
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
$ErrorActionPreference = "Stop"
|
||||
$AppRoot = "$(agent.builddirectory)\test\VSCode-win32-$(VSCODE_ARCH)"
|
||||
$AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json
|
||||
$ProductJsonPath = (Get-ChildItem -Path "$AppRoot" -Name "product.json" -Recurse | Select-Object -First 1)
|
||||
$AppProductJson = Get-Content -Raw -Path "$AppRoot\$ProductJsonPath" | ConvertFrom-Json
|
||||
$AppNameShort = $AppProductJson.nameShort
|
||||
$env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"
|
||||
$env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\test\vscode-server-win32-$(VSCODE_ARCH)"
|
||||
@@ -98,7 +99,8 @@ steps:
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
$ErrorActionPreference = "Stop"
|
||||
$AppRoot = "$(agent.builddirectory)\test\VSCode-win32-$(VSCODE_ARCH)"
|
||||
$AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json
|
||||
$ProductJsonPath = (Get-ChildItem -Path "$AppRoot" -Name "product.json" -Recurse | Select-Object -First 1)
|
||||
$AppProductJson = Get-Content -Raw -Path "$AppRoot\$ProductJsonPath" | ConvertFrom-Json
|
||||
$AppNameShort = $AppProductJson.nameShort
|
||||
$env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"
|
||||
$env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\test\vscode-server-win32-$(VSCODE_ARCH)"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
11b36db4f244693381e52316261ce61678286f6bdfe2614c6352f6fecf3f060d code_explorer_command_arm64.dll
|
||||
bfab3719038ca46bcd8afb9249a00f851dd08aa3cc8d13d01a917111a2a6d7c2 code_explorer_command_x64.dll
|
||||
b5cd79c1e91390bdeefaf35cc5c62a6022220832e145781e5609913fac706ad9 code_insider_explorer_command_arm64.dll
|
||||
f04335cc6fbe8425bd5516e6acbfa05ca706fd7566799a1e22fca1344c25351f code_insider_explorer_command_x64.dll
|
||||
5dbdd08784067e4caf7d119f7bec05b181b155e1e9868dec5a6c5174ce59f8bd code_explorer_command_arm64.dll
|
||||
c7b8dde71f62397fbcd1693e35f25d9ceab51b66e805b9f39efc78e02c6abf3c code_explorer_command_x64.dll
|
||||
968a6fe75c7316d2e2176889dffed8b50e41ee3f1834751cf6387094709b00ef code_insider_explorer_command_arm64.dll
|
||||
da071035467a64fabf8fc3762b52fa8cdb3f216aa2b252df5b25b8bdf96ec594 code_insider_explorer_command_x64.dll
|
||||
|
||||
@@ -45,6 +45,7 @@ const glob = promisify(globCallback);
|
||||
const rcedit = promisify(rceditCallback);
|
||||
const root = path.dirname(import.meta.dirname);
|
||||
const commit = getVersion(root);
|
||||
const versionedResourcesFolder = (product.quality && product.quality === 'insider') ? commit.substring(0, 10) : '';
|
||||
|
||||
// Build
|
||||
const vscodeEntryPoints = [
|
||||
@@ -328,6 +329,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
|
||||
deps
|
||||
);
|
||||
|
||||
let customElectronConfig = {};
|
||||
if (platform === 'win32') {
|
||||
all = es.merge(all, gulp.src([
|
||||
'resources/win32/bower.ico',
|
||||
@@ -360,6 +362,12 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
|
||||
'resources/win32/code_70x70.png',
|
||||
'resources/win32/code_150x150.png'
|
||||
], { base: '.' }));
|
||||
if (quality && quality === 'insider') {
|
||||
customElectronConfig = {
|
||||
createVersionedResources: true,
|
||||
productVersionString: `${versionedResourcesFolder}`,
|
||||
};
|
||||
}
|
||||
} else if (platform === 'linux') {
|
||||
const policyDest = gulp.src('.build/policies/linux/**', { base: '.build/policies/linux' })
|
||||
.pipe(rename(f => f.dirname = `policies/${f.dirname}`));
|
||||
@@ -377,7 +385,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
|
||||
.pipe(util.skipDirectories())
|
||||
.pipe(util.fixWin32DirectoryPermissions())
|
||||
.pipe(filter(['**', '!**/.github/**'], { dot: true })) // https://github.com/microsoft/vscode/issues/116523
|
||||
.pipe(electron({ ...config, platform, arch: arch === 'armhf' ? 'arm' : arch, ffmpegChromium: false }))
|
||||
.pipe(electron({ ...config, platform, arch: arch === 'armhf' ? 'arm' : arch, ffmpegChromium: false, ...customElectronConfig }))
|
||||
.pipe(filter(['**', '!LICENSE', '!version'], { dot: true }));
|
||||
|
||||
if (platform === 'linux') {
|
||||
@@ -393,19 +401,37 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
|
||||
if (platform === 'win32') {
|
||||
result = es.merge(result, gulp.src('resources/win32/bin/code.js', { base: 'resources/win32', allowEmpty: true }));
|
||||
|
||||
result = es.merge(result, gulp.src('resources/win32/bin/code.cmd', { base: 'resources/win32' })
|
||||
.pipe(replace('@@NAME@@', product.nameShort))
|
||||
.pipe(rename(function (f) { f.basename = product.applicationName; })));
|
||||
if (quality && quality === 'insider') {
|
||||
result = es.merge(result, gulp.src('resources/win32/insider/bin/code.cmd', { base: 'resources/win32/insider' })
|
||||
.pipe(replace('@@NAME@@', product.nameShort))
|
||||
.pipe(replace('@@VERSIONFOLDER@@', versionedResourcesFolder))
|
||||
.pipe(rename(function (f) { f.basename = product.applicationName; })));
|
||||
|
||||
result = es.merge(result, gulp.src('resources/win32/bin/code.sh', { base: 'resources/win32' })
|
||||
.pipe(replace('@@NAME@@', product.nameShort))
|
||||
.pipe(replace('@@PRODNAME@@', product.nameLong))
|
||||
.pipe(replace('@@VERSION@@', version))
|
||||
.pipe(replace('@@COMMIT@@', commit))
|
||||
.pipe(replace('@@APPNAME@@', product.applicationName))
|
||||
.pipe(replace('@@SERVERDATAFOLDER@@', product.serverDataFolderName || '.vscode-remote'))
|
||||
.pipe(replace('@@QUALITY@@', quality))
|
||||
.pipe(rename(function (f) { f.basename = product.applicationName; f.extname = ''; })));
|
||||
result = es.merge(result, gulp.src('resources/win32/insider/bin/code.sh', { base: 'resources/win32/insider' })
|
||||
.pipe(replace('@@NAME@@', product.nameShort))
|
||||
.pipe(replace('@@PRODNAME@@', product.nameLong))
|
||||
.pipe(replace('@@VERSION@@', version))
|
||||
.pipe(replace('@@COMMIT@@', commit))
|
||||
.pipe(replace('@@APPNAME@@', product.applicationName))
|
||||
.pipe(replace('@@VERSIONFOLDER@@', versionedResourcesFolder))
|
||||
.pipe(replace('@@SERVERDATAFOLDER@@', product.serverDataFolderName || '.vscode-remote'))
|
||||
.pipe(replace('@@QUALITY@@', quality))
|
||||
.pipe(rename(function (f) { f.basename = product.applicationName; f.extname = ''; })));
|
||||
} else {
|
||||
result = es.merge(result, gulp.src('resources/win32/bin/code.cmd', { base: 'resources/win32' })
|
||||
.pipe(replace('@@NAME@@', product.nameShort))
|
||||
.pipe(rename(function (f) { f.basename = product.applicationName; })));
|
||||
|
||||
result = es.merge(result, gulp.src('resources/win32/bin/code.sh', { base: 'resources/win32' })
|
||||
.pipe(replace('@@NAME@@', product.nameShort))
|
||||
.pipe(replace('@@PRODNAME@@', product.nameLong))
|
||||
.pipe(replace('@@VERSION@@', version))
|
||||
.pipe(replace('@@COMMIT@@', commit))
|
||||
.pipe(replace('@@APPNAME@@', product.applicationName))
|
||||
.pipe(replace('@@SERVERDATAFOLDER@@', product.serverDataFolderName || '.vscode-remote'))
|
||||
.pipe(replace('@@QUALITY@@', quality))
|
||||
.pipe(rename(function (f) { f.basename = product.applicationName; f.extname = ''; })));
|
||||
}
|
||||
|
||||
result = es.merge(result, gulp.src('resources/win32/VisualElementsManifest.xml', { base: 'resources/win32' })
|
||||
.pipe(rename(product.nameShort + '.VisualElementsManifest.xml')));
|
||||
@@ -453,8 +479,8 @@ function patchWin32DependenciesTask(destinationFolderName) {
|
||||
|
||||
return async () => {
|
||||
const deps = await glob('**/*.node', { cwd, ignore: 'extensions/node_modules/@parcel/watcher/**' });
|
||||
const packageJson = JSON.parse(await fs.promises.readFile(path.join(cwd, 'resources', 'app', 'package.json'), 'utf8'));
|
||||
const product = JSON.parse(await fs.promises.readFile(path.join(cwd, 'resources', 'app', 'product.json'), 'utf8'));
|
||||
const packageJson = JSON.parse(await fs.promises.readFile(path.join(cwd, versionedResourcesFolder, 'resources', 'app', 'package.json'), 'utf8'));
|
||||
const product = JSON.parse(await fs.promises.readFile(path.join(cwd, versionedResourcesFolder, 'resources', 'app', 'product.json'), 'utf8'));
|
||||
const baseVersion = packageJson.version.replace(/-.*$/, '');
|
||||
|
||||
await Promise.all(deps.map(async dep => {
|
||||
|
||||
@@ -8,6 +8,7 @@ import * as fs from 'fs';
|
||||
import assert from 'assert';
|
||||
import * as cp from 'child_process';
|
||||
import * as util from './lib/util.ts';
|
||||
import * as getVersionModule from './lib/getVersion.ts';
|
||||
import * as task from './lib/task.ts';
|
||||
import pkg from '../package.json' with { type: 'json' };
|
||||
import product from '../product.json' with { type: 'json' };
|
||||
@@ -15,11 +16,12 @@ import vfs from 'vinyl-fs';
|
||||
import rcedit from 'rcedit';
|
||||
import { createRequire } from 'module';
|
||||
|
||||
const { getVersion } = getVersionModule;
|
||||
const require = createRequire(import.meta.url);
|
||||
const repoPath = path.dirname(import.meta.dirname);
|
||||
const commit = getVersion(repoPath);
|
||||
const buildPath = (/** @type {string} */ arch) => path.join(path.dirname(repoPath), `VSCode-win32-${arch}`);
|
||||
const setupDir = (/** @type {string} */ arch, /** @type {string} */ target) => path.join(repoPath, '.build', `win32-${arch}`, `${target}-setup`);
|
||||
const issPath = path.join(import.meta.dirname, 'win32', 'code.iss');
|
||||
const innoSetupPath = path.join(path.dirname(path.dirname(require.resolve('innosetup'))), 'bin', 'ISCC.exe');
|
||||
const signWin32Path = path.join(repoPath, 'build', 'azure-pipelines', 'common', 'sign-win32.ts');
|
||||
|
||||
@@ -75,19 +77,26 @@ function buildWin32Setup(arch, target) {
|
||||
const outputPath = setupDir(arch, target);
|
||||
fs.mkdirSync(outputPath, { recursive: true });
|
||||
|
||||
const originalProductJsonPath = path.join(sourcePath, 'resources/app/product.json');
|
||||
const quality = product.quality || 'dev';
|
||||
let versionedResourcesFolder = '';
|
||||
let issPath = path.join(import.meta.dirname, 'win32', 'code.iss');
|
||||
if (quality && quality === 'insider') {
|
||||
versionedResourcesFolder = commit.substring(0, 10);
|
||||
issPath = path.join(import.meta.dirname, 'win32', 'code-insider.iss');
|
||||
}
|
||||
const originalProductJsonPath = path.join(sourcePath, versionedResourcesFolder, 'resources/app/product.json');
|
||||
const productJsonPath = path.join(outputPath, 'product.json');
|
||||
const productJson = JSON.parse(fs.readFileSync(originalProductJsonPath, 'utf8'));
|
||||
productJson['target'] = target;
|
||||
fs.writeFileSync(productJsonPath, JSON.stringify(productJson, undefined, '\t'));
|
||||
|
||||
const quality = product.quality || 'dev';
|
||||
const definitions = {
|
||||
NameLong: product.nameLong,
|
||||
NameShort: product.nameShort,
|
||||
DirName: product.win32DirName,
|
||||
Version: pkg.version,
|
||||
RawVersion: pkg.version.replace(/-\w+$/, ''),
|
||||
Commit: commit,
|
||||
NameVersion: product.win32NameVersion + (target === 'user' ? ' (User)' : ''),
|
||||
ExeBasename: product.nameShort,
|
||||
RegValueName: product.win32RegValueName,
|
||||
@@ -108,10 +117,11 @@ function buildWin32Setup(arch, target) {
|
||||
OutputDir: outputPath,
|
||||
InstallTarget: target,
|
||||
ProductJsonPath: productJsonPath,
|
||||
VersionedResourcesFolder: versionedResourcesFolder,
|
||||
Quality: quality
|
||||
};
|
||||
|
||||
if (quality !== 'exploration') {
|
||||
if (quality === 'stable' || quality === 'insider') {
|
||||
definitions['AppxPackage'] = `${quality === 'stable' ? 'code' : 'code_insider'}_${arch}.appx`;
|
||||
definitions['AppxPackageDll'] = `${quality === 'stable' ? 'code' : 'code_insider'}_explorer_command_${arch}.dll`;
|
||||
definitions['AppxPackageName'] = `${product.win32AppUserModelId}`;
|
||||
|
||||
4
build/win32/Cargo.lock
generated
4
build/win32/Cargo.lock
generated
@@ -129,7 +129,7 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "inno_updater"
|
||||
version = "0.16.0"
|
||||
version = "0.18.2"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"crc",
|
||||
@@ -546,4 +546,4 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
]
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "inno_updater"
|
||||
version = "0.16.0"
|
||||
version = "0.18.2"
|
||||
authors = ["Microsoft <monacotools@microsoft.com>"]
|
||||
build = "build.rs"
|
||||
|
||||
|
||||
1740
build/win32/code-insider.iss
Normal file
1740
build/win32/code-insider.iss
Normal file
File diff suppressed because it is too large
Load Diff
@@ -43,12 +43,12 @@ export async function downloadExplorerDll(outDir: string, quality: string = 'sta
|
||||
d(`downloading ${fileName}`);
|
||||
const artifact = await downloadArtifact({
|
||||
isGeneric: true,
|
||||
version: 'v4.0.0-350164',
|
||||
version: 'v5.0.0-377200',
|
||||
artifactName: fileName,
|
||||
checksums,
|
||||
mirrorOptions: {
|
||||
mirror: 'https://github.com/microsoft/vscode-explorer-command/releases/download/',
|
||||
customDir: 'v4.0.0-350164',
|
||||
customDir: 'v5.0.0-377200',
|
||||
customFilename: fileName
|
||||
}
|
||||
});
|
||||
|
||||
Binary file not shown.
7
resources/win32/insider/bin/code.cmd
Normal file
7
resources/win32/insider/bin/code.cmd
Normal file
@@ -0,0 +1,7 @@
|
||||
@echo off
|
||||
setlocal
|
||||
set VSCODE_DEV=
|
||||
set ELECTRON_RUN_AS_NODE=1
|
||||
"%~dp0..\@@NAME@@.exe" "%~dp0..\@@VERSIONFOLDER@@\resources\app\out\cli.js" %*
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /b %ERRORLEVEL%
|
||||
endlocal
|
||||
63
resources/win32/insider/bin/code.sh
Normal file
63
resources/win32/insider/bin/code.sh
Normal file
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env sh
|
||||
#
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
if [ "$VSCODE_WSL_DEBUG_INFO" = true ]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
COMMIT="@@COMMIT@@"
|
||||
APP_NAME="@@APPNAME@@"
|
||||
QUALITY="@@QUALITY@@"
|
||||
NAME="@@NAME@@"
|
||||
SERVERDATAFOLDER="@@SERVERDATAFOLDER@@"
|
||||
VERSIONFOLDER="@@VERSIONFOLDER@@"
|
||||
VSCODE_PATH="$(dirname "$(dirname "$(realpath "$0")")")"
|
||||
ELECTRON="$VSCODE_PATH/$NAME.exe"
|
||||
|
||||
IN_WSL=false
|
||||
if [ -n "$WSL_DISTRO_NAME" ]; then
|
||||
# $WSL_DISTRO_NAME is available since WSL builds 18362, also for WSL2
|
||||
IN_WSL=true
|
||||
else
|
||||
WSL_BUILD=$(uname -r | sed -E 's/^[0-9.]+-([0-9]+)-Microsoft.*|.*/\1/')
|
||||
if [ -n "$WSL_BUILD" ]; then
|
||||
if [ "$WSL_BUILD" -ge 17063 ]; then
|
||||
# WSLPATH is available since WSL build 17046
|
||||
# WSLENV is available since WSL build 17063
|
||||
IN_WSL=true
|
||||
else
|
||||
# If running under older WSL, don't pass cli.js to Electron as
|
||||
# environment vars cannot be transferred from WSL to Windows
|
||||
# See: https://github.com/microsoft/BashOnWindows/issues/1363
|
||||
# https://github.com/microsoft/BashOnWindows/issues/1494
|
||||
"$ELECTRON" "$@"
|
||||
exit $?
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if [ $IN_WSL = true ]; then
|
||||
|
||||
export WSLENV="ELECTRON_RUN_AS_NODE/w:$WSLENV"
|
||||
CLI=$(wslpath -m "$VSCODE_PATH/resources/app/out/cli.js")
|
||||
|
||||
# use the Remote WSL extension if installed
|
||||
WSL_EXT_ID="ms-vscode-remote.remote-wsl"
|
||||
|
||||
ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" --locate-extension $WSL_EXT_ID >/tmp/remote-wsl-loc.txt 2>/dev/null </dev/null
|
||||
WSL_EXT_WLOC=$(cat /tmp/remote-wsl-loc.txt)
|
||||
|
||||
if [ -n "$WSL_EXT_WLOC" ]; then
|
||||
# replace \r\n with \n in WSL_EXT_WLOC
|
||||
WSL_CODE=$(wslpath -u "${WSL_EXT_WLOC%%[[:cntrl:]]}")/scripts/wslCode.sh
|
||||
"$WSL_CODE" "$COMMIT" "$QUALITY" "$ELECTRON" "$APP_NAME" "$SERVERDATAFOLDER" "$@"
|
||||
exit $?
|
||||
fi
|
||||
|
||||
elif [ -x "$(command -v cygpath)" ]; then
|
||||
CLI=$(cygpath -m "$VSCODE_PATH/$VERSIONFOLDER/resources/app/out/cli.js")
|
||||
else
|
||||
CLI="$VSCODE_PATH/$VERSIONFOLDER/resources/app/out/cli.js"
|
||||
fi
|
||||
ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" "$@"
|
||||
exit $?
|
||||
@@ -144,6 +144,14 @@ class CodeMain {
|
||||
evt.join('instanceLockfile', promises.unlink(environmentMainService.mainLockfile).catch(() => { /* ignored */ }));
|
||||
});
|
||||
|
||||
// Check if Inno Setup is running
|
||||
const innoSetupActive = await this.checkInnoSetupMutex(productService);
|
||||
if (innoSetupActive) {
|
||||
const message = `${productService.nameShort} is currently being updated. Please wait for the update to complete before launching.`;
|
||||
instantiationService.invokeFunction(this.quit, new Error(message));
|
||||
return;
|
||||
}
|
||||
|
||||
return instantiationService.createInstance(CodeApplication, mainProcessNodeIpcServer, instanceEnvironment).startup();
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -487,6 +495,21 @@ class CodeMain {
|
||||
lifecycleMainService.kill(exitCode);
|
||||
}
|
||||
|
||||
private async checkInnoSetupMutex(productService: IProductService): Promise<boolean> {
|
||||
if (!isWindows || !productService.win32MutexName || productService.quality !== 'insider') {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const readyMutexName = `${productService.win32MutexName}setup`;
|
||||
const mutex = await import('@vscode/windows-mutex');
|
||||
return mutex.isActive(readyMutexName);
|
||||
} catch (error) {
|
||||
console.error('Failed to check Inno Setup mutex:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//#region Command line arguments utilities
|
||||
|
||||
private resolveArgs(): NativeParsedArgs {
|
||||
|
||||
@@ -13,6 +13,7 @@ import { ILogService } from '../../../../platform/log/common/log.js';
|
||||
import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
|
||||
import { FileOperationResult, IFileService, IFileStat, toFileOperationResult } from '../../../../platform/files/common/files.js';
|
||||
import { getErrorMessage } from '../../../../base/common/errors.js';
|
||||
import { IProductService } from '../../../../platform/product/common/productService.js';
|
||||
|
||||
const defaultExtensionsInitStatusKey = 'initializing-default-extensions';
|
||||
|
||||
@@ -23,6 +24,7 @@ export class DefaultExtensionsInitializer extends Disposable {
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -70,9 +72,15 @@ export class DefaultExtensionsInitializer extends Disposable {
|
||||
}
|
||||
|
||||
private getDefaultExtensionVSIXsLocation(): URI {
|
||||
// appRoot = C:\Users\<name>\AppData\Local\Programs\Microsoft VS Code Insiders\resources\app
|
||||
// extensionsPath = C:\Users\<name>\AppData\Local\Programs\Microsoft VS Code Insiders\bootstrap\extensions
|
||||
return URI.file(join(dirname(dirname(this.environmentService.appRoot)), 'bootstrap', 'extensions'));
|
||||
if (this.productService.quality === 'insider') {
|
||||
// appRoot = C:\Users\<name>\AppData\Local\Programs\Microsoft VS Code Insiders\<version>\resources\app
|
||||
// extensionsPath = C:\Users\<name>\AppData\Local\Programs\Microsoft VS Code Insiders\<version>\bootstrap\extensions
|
||||
return URI.file(join(dirname(dirname(dirname(this.environmentService.appRoot))), 'bootstrap', 'extensions'));
|
||||
} else {
|
||||
// appRoot = C:\Users\<name>\AppData\Local\Programs\Microsoft VS Code Insiders\resources\app
|
||||
// extensionsPath = C:\Users\<name>\AppData\Local\Programs\Microsoft VS Code Insiders\bootstrap\extensions
|
||||
return URI.file(join(dirname(dirname(this.environmentService.appRoot)), 'bootstrap', 'extensions'));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -175,9 +175,17 @@ export class RemoteTunnelService extends Disposable implements IRemoteTunnelServ
|
||||
// appRoot = /Applications/Visual Studio Code - Insiders.app/Contents/Resources/app
|
||||
// bin = /Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/bin
|
||||
binParentLocation = this.environmentService.appRoot;
|
||||
} else if (isWindows) {
|
||||
if (this.productService.quality === 'insider') {
|
||||
// appRoot = C:\Users\<name>\AppData\Local\Programs\Microsoft VS Code Insiders\<version>\resources\app
|
||||
// bin = C:\Users\<name>\AppData\Local\Programs\Microsoft VS Code Insiders\bin
|
||||
binParentLocation = dirname(dirname(dirname(this.environmentService.appRoot)));
|
||||
} else {
|
||||
// appRoot = C:\Users\<name>\AppData\Local\Programs\Microsoft VS Code Insiders\resources\app
|
||||
// bin = C:\Users\<name>\AppData\Local\Programs\Microsoft VS Code Insiders\bin
|
||||
binParentLocation = dirname(dirname(this.environmentService.appRoot));
|
||||
}
|
||||
} else {
|
||||
// appRoot = C:\Users\<name>\AppData\Local\Programs\Microsoft VS Code Insiders\resources\app
|
||||
// bin = C:\Users\<name>\AppData\Local\Programs\Microsoft VS Code Insiders\bin
|
||||
// appRoot = /usr/share/code-insiders/resources/app
|
||||
// bin = /usr/share/code-insiders/bin
|
||||
binParentLocation = dirname(dirname(this.environmentService.appRoot));
|
||||
|
||||
@@ -48,7 +48,7 @@ export abstract class AbstractUpdateService implements IUpdateService {
|
||||
constructor(
|
||||
@ILifecycleMainService protected readonly lifecycleMainService: ILifecycleMainService,
|
||||
@IConfigurationService protected configurationService: IConfigurationService,
|
||||
@IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService,
|
||||
@IEnvironmentMainService protected environmentMainService: IEnvironmentMainService,
|
||||
@IRequestService protected requestService: IRequestService,
|
||||
@ILogService protected logService: ILogService,
|
||||
@IProductService protected readonly productService: IProductService
|
||||
@@ -105,6 +105,8 @@ export abstract class AbstractUpdateService implements IUpdateService {
|
||||
|
||||
this.setState(State.Idle(this.getUpdateType()));
|
||||
|
||||
await this.postInitialize();
|
||||
|
||||
if (updateMode === 'manual') {
|
||||
this.logService.info('update#ctor - manual checks only; automatic updates are disabled by user preference');
|
||||
return;
|
||||
@@ -230,6 +232,10 @@ export abstract class AbstractUpdateService implements IUpdateService {
|
||||
// noop
|
||||
}
|
||||
|
||||
protected async postInitialize(): Promise<void> {
|
||||
// noop
|
||||
}
|
||||
|
||||
protected abstract buildUpdateFeedUrl(quality: string): string | undefined;
|
||||
protected abstract doCheckForUpdates(explicit: boolean): void;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { spawn } from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import { existsSync, unlinkSync } from 'fs';
|
||||
import { mkdir, readFile, unlink } from 'fs/promises';
|
||||
import { tmpdir } from 'os';
|
||||
import { app } from 'electron';
|
||||
import { timeout } from '../../../base/common/async.js';
|
||||
import { CancellationToken } from '../../../base/common/cancellation.js';
|
||||
import { memoize } from '../../../base/common/decorators.js';
|
||||
@@ -40,7 +42,7 @@ interface IAvailableUpdate {
|
||||
let _updateType: UpdateType | undefined = undefined;
|
||||
function getUpdateType(): UpdateType {
|
||||
if (typeof _updateType === 'undefined') {
|
||||
_updateType = fs.existsSync(path.join(path.dirname(process.execPath), 'unins000.exe'))
|
||||
_updateType = existsSync(path.join(path.dirname(process.execPath), 'unins000.exe'))
|
||||
? UpdateType.Setup
|
||||
: UpdateType.Archive;
|
||||
}
|
||||
@@ -55,7 +57,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun
|
||||
@memoize
|
||||
get cachePath(): Promise<string> {
|
||||
const result = path.join(tmpdir(), `vscode-${this.productService.quality}-${this.productService.target}-${process.arch}`);
|
||||
return fs.promises.mkdir(result, { recursive: true }).then(() => result);
|
||||
return mkdir(result, { recursive: true }).then(() => result);
|
||||
}
|
||||
|
||||
constructor(
|
||||
@@ -90,6 +92,14 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun
|
||||
}
|
||||
|
||||
protected override async initialize(): Promise<void> {
|
||||
if (this.environmentMainService.isBuilt) {
|
||||
const cachePath = await this.cachePath;
|
||||
app.setPath('appUpdate', cachePath);
|
||||
try {
|
||||
await unlink(path.join(cachePath, 'session-ending.flag'));
|
||||
} catch { }
|
||||
}
|
||||
|
||||
if (this.productService.target === 'user' && await this.nativeHostMainService.isAdmin(undefined)) {
|
||||
this.setState(State.Disabled(DisablementReason.RunningAsAdmin));
|
||||
this.logService.info('update#ctor - updates are disabled due to running as Admin in user setup');
|
||||
@@ -99,6 +109,49 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun
|
||||
await super.initialize();
|
||||
}
|
||||
|
||||
protected override async postInitialize(): Promise<void> {
|
||||
if (this.productService.quality !== 'insider') {
|
||||
return;
|
||||
}
|
||||
// Check for pending update from previous session
|
||||
// This can happen if the app is quit right after the update has been
|
||||
// downloaded and before the update has been applied.
|
||||
const exePath = app.getPath('exe');
|
||||
const exeDir = path.dirname(exePath);
|
||||
const updatingVersionPath = path.join(exeDir, 'updating_version');
|
||||
if (await pfs.Promises.exists(updatingVersionPath)) {
|
||||
try {
|
||||
const updatingVersion = (await readFile(updatingVersionPath, 'utf8')).trim();
|
||||
this.logService.info(`update#doCheckForUpdates - application was updating to version ${updatingVersion}`);
|
||||
const updatePackagePath = await this.getUpdatePackagePath(updatingVersion);
|
||||
if (await pfs.Promises.exists(updatePackagePath)) {
|
||||
await this._applySpecificUpdate(updatePackagePath);
|
||||
this.logService.info(`update#doCheckForUpdates - successfully applied update to version ${updatingVersion}`);
|
||||
}
|
||||
} catch (e) {
|
||||
this.logService.error(`update#doCheckForUpdates - could not read ${updatingVersionPath}`, e);
|
||||
} finally {
|
||||
// updatingVersionPath will be deleted by inno setup.
|
||||
}
|
||||
} else {
|
||||
const fastUpdatesEnabled = this.configurationService.getValue('update.enableWindowsBackgroundUpdates');
|
||||
// GC for background updates in system setup happens via inno_setup since it requires
|
||||
// elevated permissions.
|
||||
if (fastUpdatesEnabled && this.productService.target === 'user' && this.productService.commit) {
|
||||
const versionedResourcesFolder = this.productService.commit.substring(0, 10);
|
||||
const innoUpdater = path.join(exeDir, versionedResourcesFolder, 'tools', 'inno_updater.exe');
|
||||
await new Promise<void>(resolve => {
|
||||
const child = spawn(innoUpdater, ['--gc', exePath, versionedResourcesFolder], {
|
||||
stdio: ['ignore', 'ignore', 'ignore'],
|
||||
windowsHide: true,
|
||||
timeout: 2 * 60 * 1000
|
||||
});
|
||||
child.once('exit', () => resolve());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected buildUpdateFeedUrl(quality: string): string | undefined {
|
||||
let platform = `win32-${process.arch}`;
|
||||
|
||||
@@ -196,7 +249,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun
|
||||
|
||||
const promises = versions.filter(filter).map(async one => {
|
||||
try {
|
||||
await fs.promises.unlink(path.join(cachePath, one));
|
||||
await unlink(path.join(cachePath, one));
|
||||
} catch (err) {
|
||||
// ignore
|
||||
}
|
||||
@@ -218,11 +271,12 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun
|
||||
this.setState(State.Updating(update));
|
||||
|
||||
const cachePath = await this.cachePath;
|
||||
const sessionEndFlagPath = path.join(cachePath, 'session-ending.flag');
|
||||
|
||||
this.availableUpdate.updateFilePath = path.join(cachePath, `CodeSetup-${this.productService.quality}-${update.version}.flag`);
|
||||
|
||||
await pfs.Promises.writeFile(this.availableUpdate.updateFilePath, 'flag');
|
||||
const child = spawn(this.availableUpdate.packagePath, ['/verysilent', '/log', `/update="${this.availableUpdate.updateFilePath}"`, '/nocloseapplications', '/mergetasks=runcode,!desktopicon,!quicklaunchicon'], {
|
||||
const child = spawn(this.availableUpdate.packagePath, ['/verysilent', '/log', `/update="${this.availableUpdate.updateFilePath}"`, `/sessionend="${sessionEndFlagPath}"`, '/nocloseapplications', '/mergetasks=runcode,!desktopicon,!quicklaunchicon'], {
|
||||
detached: true,
|
||||
stdio: ['ignore', 'ignore', 'ignore'],
|
||||
windowsVerbatimArguments: true
|
||||
@@ -249,7 +303,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun
|
||||
this.logService.trace('update#quitAndInstall(): running raw#quitAndInstall()');
|
||||
|
||||
if (this.availableUpdate.updateFilePath) {
|
||||
fs.unlinkSync(this.availableUpdate.updateFilePath);
|
||||
unlinkSync(this.availableUpdate.updateFilePath);
|
||||
} else {
|
||||
spawn(this.availableUpdate.packagePath, ['/silent', '/log', '/mergetasks=runcode,!desktopicon,!quicklaunchicon'], {
|
||||
detached: true,
|
||||
|
||||
@@ -88,6 +88,28 @@ export async function resolveElectronConfiguration(options: LaunchOptions): Prom
|
||||
};
|
||||
}
|
||||
|
||||
function findFilePath(root: string, path: string): string {
|
||||
// First check if the path exists directly in the root
|
||||
const directPath = join(root, path);
|
||||
if (fs.existsSync(directPath)) {
|
||||
return directPath;
|
||||
}
|
||||
|
||||
// If not found directly, search through subdirectories
|
||||
const entries = fs.readdirSync(root, { withFileTypes: true });
|
||||
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory()) {
|
||||
const found = join(root, entry.name, path);
|
||||
if (fs.existsSync(found)) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Could not find ${path} in any subdirectory`);
|
||||
}
|
||||
|
||||
export function getDevElectronPath(): string {
|
||||
const buildPath = join(root, '.build');
|
||||
const product = require(join(root, 'product.json'));
|
||||
@@ -113,7 +135,8 @@ export function getBuildElectronPath(root: string): string {
|
||||
return join(root, product.applicationName);
|
||||
}
|
||||
case 'win32': {
|
||||
const product = require(join(root, 'resources', 'app', 'product.json'));
|
||||
const productPath = findFilePath(root, join('resources', 'app', 'product.json'));
|
||||
const product = require(productPath);
|
||||
return join(root, `${product.nameShort}.exe`);
|
||||
}
|
||||
default:
|
||||
@@ -125,6 +148,10 @@ export function getBuildVersion(root: string): string {
|
||||
switch (process.platform) {
|
||||
case 'darwin':
|
||||
return require(join(root, 'Contents', 'Resources', 'app', 'package.json')).version;
|
||||
case 'win32': {
|
||||
const packagePath = findFilePath(root, join('resources', 'app', 'package.json'));
|
||||
return require(packagePath).version;
|
||||
}
|
||||
default:
|
||||
return require(join(root, 'resources', 'app', 'package.json')).version;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user