refactor: extract install scripts from serverSetup.ts into script files (#287)

This commit is contained in:
Simon Sobisch
2026-07-01 19:31:58 +02:00
committed by GitHub
parent 22b4519e9c
commit 3cb82b9a18
5 changed files with 517 additions and 460 deletions
+1
View File
@@ -7,3 +7,4 @@
!README.md
!lib/extension.js
!resources/icon.png
!src/scripts/**
+2 -1
View File
@@ -261,7 +261,8 @@ export class RemoteSSHResolver implements vscode.RemoteAuthorityResolver, vscode
remotePlatformMap[sshDest.hostname],
remoteServerListenOnSocket,
customInstallPath,
this.logger
this.logger,
this.context.extensionPath
);
for (const key of Object.keys(envVariables)) {
+194
View File
@@ -0,0 +1,194 @@
# Server installation script
$TMP_DIR="$env:TEMP\$([System.IO.Path]::GetRandomFileName())"
$ProgressPreference = "SilentlyContinue"
$DISTRO_VERSION="%%DISTRO_VERSION%%"
$DISTRO_COMMIT="%%DISTRO_COMMIT%%"
$DISTRO_QUALITY="%%DISTRO_QUALITY%%"
$DISTRO_VSCODIUM_RELEASE="%%DISTRO_VSCODIUM_RELEASE%%"
$SERVER_APP_NAME="%%SERVER_APP_NAME%%"
$SERVER_INITIAL_EXTENSIONS="%%SERVER_INITIAL_EXTENSIONS%%"
$SERVER_LISTEN_FLAG="%%SERVER_LISTEN_FLAG%%"
$SERVER_DATA_DIR="%%SERVER_DATA_DIR%%"
$SERVER_DATA_DIR_FLAG="%%SERVER_DATA_DIR_FLAG%%"
$SERVER_DIR="$SERVER_DATA_DIR\bin\$DISTRO_COMMIT"
$SERVER_SCRIPT="$SERVER_DIR\bin\$SERVER_APP_NAME.cmd"
$SERVER_LOGFILE="$SERVER_DATA_DIR\.$DISTRO_COMMIT.log"
$SERVER_PIDFILE="$SERVER_DATA_DIR\.$DISTRO_COMMIT.pid"
$SERVER_TOKENFILE="$SERVER_DATA_DIR\.$DISTRO_COMMIT.token"
$SERVER_ARCH=
$SERVER_CONNECTION_TOKEN=
$SERVER_DOWNLOAD_URL="%%SERVER_DOWNLOAD_URL%%"
$SERVER_VALIDATION_FLAG="%%SERVER_VALIDATION_FLAG%%"
$LISTENING_ON=
$OS_RELEASE_ID=
$ARCH=
$PLATFORM="win32"
function printInstallResults($code) {
"%%SCRIPT_ID%%: start"
"exitCode==$code=="
"listeningOn==$LISTENING_ON=="
"connectionToken==$SERVER_CONNECTION_TOKEN=="
"logFile==$SERVER_LOGFILE=="
"osReleaseId==$OS_RELEASE_ID=="
"arch==$ARCH=="
"platform==$PLATFORM=="
"tmpDir==$TMP_DIR=="
%%ENV_VAR_LINES%%
"%%SCRIPT_ID%%: end"
}
# Check machine architecture
$ARCH=$env:PROCESSOR_ARCHITECTURE
# Use x64 version for ARM64, as it's not yet available.
if(($ARCH -eq "AMD64") -or ($ARCH -eq "IA64") -or ($ARCH -eq "ARM64")) {
$SERVER_ARCH="x64"
}
else {
"Error architecture not supported: $ARCH"
printInstallResults 1
exit 0
}
# Create installation folder
if(!(Test-Path $SERVER_DIR)) {
try {
ni -it d $SERVER_DIR -f -ea si
} catch {
"Error creating server install directory - $($_.ToString())"
exit 1
}
if(!(Test-Path $SERVER_DIR)) {
"Error creating server install directory"
exit 1
}
}
cd $SERVER_DIR
# Check if server script is already installed
if(!(Test-Path $SERVER_SCRIPT)) {
del vscode-server.tar.gz
$REQUEST_ARGUMENTS = @{
Uri="$SERVER_DOWNLOAD_URL"
TimeoutSec=20
OutFile="vscode-server.tar.gz"
UseBasicParsing=$True
}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Invoke-RestMethod @REQUEST_ARGUMENTS
if(Test-Path "vscode-server.tar.gz") {
tar -xf vscode-server.tar.gz --strip-components 1
del vscode-server.tar.gz
}
if(!(Test-Path $SERVER_SCRIPT)) {
"Error while installing the server binary"
exit 1
}
}
else {
"Server script already installed in $SERVER_SCRIPT"
}
# Modify the commit in the remote server to match the local value
if(%%MODIFY_PRODUCT_JSON%%) {
echo "Will modify product.json on remote to match the commit value"
(Get-Content -Raw "$SERVER_DIR\product.json") -replace '"commit": "[0-9a-f]+",', ('"commit": "' + $DISTRO_COMMIT + '",') |
Set-Content -NoNewLine "$SERVER_DIR\product.json"
}
# Try to find if server is already running
if(Get-Process node -ErrorAction SilentlyContinue | Where-Object Path -Like "$SERVER_DIR\*") {
echo "Server script is already running $SERVER_SCRIPT"
}
else {
if(Test-Path $SERVER_LOGFILE) {
del $SERVER_LOGFILE
}
if(Test-Path $SERVER_PIDFILE) {
del $SERVER_PIDFILE
}
if(Test-Path $SERVER_TOKENFILE) {
del $SERVER_TOKENFILE
}
$SERVER_CONNECTION_TOKEN="%%SERVER_CONNECTION_TOKEN%%"
[System.IO.File]::WriteAllLines($SERVER_TOKENFILE, $SERVER_CONNECTION_TOKEN)
$SCRIPT_ARGUMENTS="--start-server --host=127.0.0.1 $SERVER_LISTEN_FLAG $SERVER_DATA_DIR_FLAG $SERVER_VALIDATION_FLAG $SERVER_INITIAL_EXTENSIONS --connection-token-file $SERVER_TOKENFILE --telemetry-level off --enable-remote-auto-shutdown --accept-server-license-terms *> '$SERVER_LOGFILE'"
$START_ARGUMENTS = @{
FilePath = "powershell.exe"
WindowStyle = "hidden"
ArgumentList = @(
"-ExecutionPolicy", "Unrestricted", "-NoLogo", "-NoProfile", "-NonInteractive", "-c", "$SERVER_SCRIPT $SCRIPT_ARGUMENTS"
)
PassThru = $True
}
$SERVER_ID = (start @START_ARGUMENTS).ID
if($SERVER_ID) {
[System.IO.File]::WriteAllLines($SERVER_PIDFILE, $SERVER_ID)
}
}
if(Test-Path $SERVER_TOKENFILE) {
$SERVER_CONNECTION_TOKEN="$(cat $SERVER_TOKENFILE)"
}
else {
"Error server token file not found $SERVER_TOKENFILE"
printInstallResults 1
exit 0
}
sleep -Milliseconds 500
$SELECT_ARGUMENTS = @{
Path = $SERVER_LOGFILE
Pattern = "Extension host agent listening on (\d+)"
}
for($I = 1; $I -le 5; $I++) {
if(Test-Path $SERVER_LOGFILE) {
$GROUPS = (Select-String @SELECT_ARGUMENTS).Matches.Groups
if($GROUPS) {
$LISTENING_ON = $GROUPS[1].Value
break
}
}
sleep -Milliseconds 500
}
if(!(Test-Path $SERVER_LOGFILE)) {
"Error server log file not found $SERVER_LOGFILE"
printInstallResults 1
exit 0
}
# Finish server setup
printInstallResults 0
if($SERVER_ID) {
while($True) {
if(!(gps -Id $SERVER_ID)) {
"server died, exit"
exit 0
}
sleep 30
}
}
+255
View File
@@ -0,0 +1,255 @@
# Server installation script
TMP_DIR="${XDG_RUNTIME_DIR:-"/tmp"}"
DISTRO_VERSION="%%DISTRO_VERSION%%"
DISTRO_COMMIT="%%DISTRO_COMMIT%%"
DISTRO_QUALITY="%%DISTRO_QUALITY%%"
DISTRO_VSCODIUM_RELEASE="%%DISTRO_VSCODIUM_RELEASE%%"
SERVER_APP_NAME="%%SERVER_APP_NAME%%"
SERVER_INITIAL_EXTENSIONS="%%SERVER_INITIAL_EXTENSIONS%%"
SERVER_LISTEN_FLAG="%%SERVER_LISTEN_FLAG%%"
SERVER_DATA_DIR="%%SERVER_DATA_DIR%%"
SERVER_DATA_DIR_FLAG="%%SERVER_DATA_DIR_FLAG%%"
SERVER_DIR="$SERVER_DATA_DIR/bin/$DISTRO_COMMIT"
SERVER_SCRIPT="$SERVER_DIR/bin/$SERVER_APP_NAME"
SERVER_LOGFILE="$SERVER_DATA_DIR/.$DISTRO_COMMIT.log"
SERVER_PIDFILE="$SERVER_DATA_DIR/.$DISTRO_COMMIT.pid"
SERVER_TOKENFILE="$SERVER_DATA_DIR/.$DISTRO_COMMIT.token"
SERVER_ARCH=
SERVER_CONNECTION_TOKEN=
SERVER_DOWNLOAD_URL=
SERVER_VALIDATION_FLAG="%%SERVER_VALIDATION_FLAG%%"
LISTENING_ON=
OS_RELEASE_ID=
ARCH=
PLATFORM=
# Mimic output from logs of remote-ssh extension
print_install_results_and_exit() {
echo "%%SCRIPT_ID%%: start"
echo "exitCode==$1=="
echo "listeningOn==$LISTENING_ON=="
echo "connectionToken==$SERVER_CONNECTION_TOKEN=="
echo "logFile==$SERVER_LOGFILE=="
echo "osReleaseId==$OS_RELEASE_ID=="
echo "arch==$ARCH=="
echo "platform==$PLATFORM=="
echo "tmpDir==$TMP_DIR=="
%%ENV_VAR_LINES%%
echo "%%SCRIPT_ID%%: end"
exit 0
}
LOCKFILE="$TMP_DIR/server_install.lock"
if command -v flock >/dev/null 2>&1; then
exec {FD}<>"$LOCKFILE"
# wait 30s to acquire lock, otherwise fail
flock -x -w 30 $FD || print_install_results_and_exit 1
trap "flock -u $FD; trap - EXIT INT HUP; exit" EXIT INT HUP
else
echo "Warning: flock not available, skipping install lock" >&2
fi
# Check if platform is supported
if ! command -v uname; then
echo "Error 'uname' command not found, could not get platform/arch data."
print_install_results_and_exit 1
fi
KERNEL="$(uname -s)"
case $KERNEL in
Darwin)
PLATFORM="darwin"
;;
Linux)
PLATFORM="linux"
;;
FreeBSD)
PLATFORM="freebsd"
;;
DragonFly)
PLATFORM="dragonfly"
;;
"")
echo "Error uname -s yields empty result"
print_install_results_and_exit 1
;;
*)
echo "Error platform not supported: $KERNEL"
print_install_results_and_exit 1
;;
esac
# Check machine architecture
ARCH="$(uname -m)"
case $ARCH in
x86_64 | amd64)
SERVER_ARCH="x64"
;;
armv7l | armv8l)
SERVER_ARCH="armhf"
;;
arm64 | aarch64)
SERVER_ARCH="arm64"
;;
ppc64le)
SERVER_ARCH="ppc64le"
;;
riscv64)
SERVER_ARCH="riscv64"
;;
loongarch64)
SERVER_ARCH="loong64"
;;
s390x)
SERVER_ARCH="s390x"
;;
*)
echo "Error architecture not supported: $ARCH"
print_install_results_and_exit 1
;;
esac
# https://www.freedesktop.org/software/systemd/man/os-release.html
OS_RELEASE_ID="$(grep -i '^ID=' /etc/os-release 2>/dev/null | sed 's/^ID=//gi' | sed 's/"//g')"
if [[ -z $OS_RELEASE_ID ]]; then
OS_RELEASE_ID="$(grep -i '^ID=' /usr/lib/os-release 2>/dev/null | sed 's/^ID=//gi' | sed 's/"//g')"
if [[ -z $OS_RELEASE_ID ]]; then
OS_RELEASE_ID="unknown"
fi
fi
# Create installation folder
if [[ ! -d $SERVER_DIR ]]; then
mkdir -p $SERVER_DIR
if (( $? > 0 )); then
echo "Error creating server install directory"
print_install_results_and_exit 1
fi
fi
# adjust platform for vscodium download, if needed
if [[ $OS_RELEASE_ID = alpine ]]; then
PLATFORM=$OS_RELEASE_ID
fi
SERVER_DOWNLOAD_URL="$(echo "%%SERVER_DOWNLOAD_URL_TEMPLATE%%" | sed "s/\${quality}/$DISTRO_QUALITY/g" | sed "s/\${version}/$DISTRO_VERSION/g" | sed "s/\${commit}/$DISTRO_COMMIT/g" | sed "s/\${os}/$PLATFORM/g" | sed "s/\${arch}/$SERVER_ARCH/g" | sed "s/\${release}/$DISTRO_VSCODIUM_RELEASE/g")"
# Check if server script is already installed
if [[ ! -f $SERVER_SCRIPT ]]; then
case "$PLATFORM" in
darwin | linux | alpine | freebsd )
;;
*)
echo "Error '$PLATFORM' needs manual installation of remote extension host"
print_install_results_and_exit 1
;;
esac
pushd $SERVER_DIR > /dev/null
if command -v wget >/dev/null 2>&1; then
wget --tries=3 --timeout=10 --continue --no-verbose -O vscode-server.tar.gz $SERVER_DOWNLOAD_URL
elif command -v curl >/dev/null 2>&1; then
curl --retry 3 --connect-timeout 10 --location --show-error --silent --output vscode-server.tar.gz $SERVER_DOWNLOAD_URL
elif command -v fetch >/dev/null 2>&1; then
fetch --retry --timeout=10 --quiet --output=vscode-server.tar.gz $SERVER_DOWNLOAD_URL
else
echo "Error no tool to download server binary"
print_install_results_and_exit 1
fi
if (( $? > 0 )); then
echo "Error downloading server from $SERVER_DOWNLOAD_URL"
rm -rf vscode-server.tar.gz
print_install_results_and_exit 1
fi
tar -xf vscode-server.tar.gz --strip-components 1
if (( $? > 0 )); then
echo "Error while extracting server contents"
rm -rf vscode-server.tar.gz
print_install_results_and_exit 1
fi
if [[ ! -f $SERVER_SCRIPT ]]; then
rm -rf $SERVER_DIR/*
echo "Error server contents are corrupted"
print_install_results_and_exit 1
fi
rm -f vscode-server.tar.gz
popd > /dev/null
else
echo "Server script already installed in $SERVER_SCRIPT"
fi
# Modify the commit in the remote server to match the local value
if %%MODIFY_PRODUCT_JSON%%; then
if command -v sed >/dev/null 2>&1; then
echo "Will modify product.json on remote to match the commit value"
sed -i -E 's/"commit": "[0-9a-f]+",/"commit": "'"$DISTRO_COMMIT"'",/' "$SERVER_DIR/product.json";
else
echo "Cannot find the 'sed' command, make sure it is installed to modify product.json with the matching commit."
fi
fi
# Try to find if server is already running
if [[ -f $SERVER_PIDFILE ]]; then
SERVER_PID="$(cat $SERVER_PIDFILE)"
SERVER_RUNNING_PROCESS="$(ps -o pid,args -p $SERVER_PID | grep $SERVER_SCRIPT)"
else
SERVER_RUNNING_PROCESS="$(ps -o pid,args -A | grep $SERVER_SCRIPT | grep -v grep)"
fi
if [[ -z $SERVER_RUNNING_PROCESS ]]; then
if [[ -f $SERVER_LOGFILE ]]; then
rm $SERVER_LOGFILE
fi
if [[ -f $SERVER_TOKENFILE ]]; then
rm $SERVER_TOKENFILE
fi
touch $SERVER_TOKENFILE
chmod 600 $SERVER_TOKENFILE
SERVER_CONNECTION_TOKEN="%%SERVER_CONNECTION_TOKEN%%"
echo $SERVER_CONNECTION_TOKEN > $SERVER_TOKENFILE
$SERVER_SCRIPT --start-server --host=127.0.0.1 $SERVER_LISTEN_FLAG $SERVER_DATA_DIR_FLAG $SERVER_VALIDATION_FLAG $SERVER_INITIAL_EXTENSIONS --connection-token-file $SERVER_TOKENFILE --telemetry-level off --enable-remote-auto-shutdown --accept-server-license-terms &> $SERVER_LOGFILE &
echo $! > $SERVER_PIDFILE
else
echo "Server script is already running $SERVER_SCRIPT"
fi
if [[ -f $SERVER_TOKENFILE ]]; then
SERVER_CONNECTION_TOKEN="$(cat $SERVER_TOKENFILE)"
else
echo "Error server token file not found $SERVER_TOKENFILE"
print_install_results_and_exit 1
fi
if [[ -f $SERVER_LOGFILE ]]; then
for i in {1..35}; do
LISTENING_ON="$(cat $SERVER_LOGFILE | grep -E 'Extension host agent listening on .+' | sed 's/Extension host agent listening on //')"
if [[ -n $LISTENING_ON ]]; then
break
fi
sleep 0.5
done
if [[ -z $LISTENING_ON ]]; then
echo "Error server did not start successfully"
print_install_results_and_exit 1
fi
else
echo "Error server log file not found $SERVER_LOGFILE"
print_install_results_and_exit 1
fi
# Finish server setup
print_install_results_and_exit 0
+65 -459
View File
@@ -1,9 +1,24 @@
import * as crypto from 'crypto';
import * as fs from 'fs';
import * as path from 'path';
import Log from './common/logger';
import { getVSCodeServerConfig, ServerVersion, ServerValidation } from './serverConfig';
import SSHConnection from './ssh/sshConnection';
import { fetchRelease, IRelease } from './fetchRelease';
/**
* Reads a script template from <extensionPath>/scripts/<templateName> and
* replaces every %%KEY%% occurrence with the matching value from `variables`.
*/
function compileTemplate(templateName: string, variables: Record<string, string>, extensionPath: string): string {
const templatePath = path.join(extensionPath, 'src', 'scripts', templateName);
let content = fs.readFileSync(templatePath, 'utf8');
for (const [key, value] of Object.entries(variables)) {
content = content.replace(new RegExp(`%%${key}%%`, 'g'), value);
}
return content;
}
/**
* Matches a hostname against a pattern that may contain wildcards.
* Returns a specificity score: higher scores indicate more specific matches.
@@ -103,7 +118,8 @@ export async function installCodeServer(
platform: string | undefined,
useSocketPath: boolean,
customInstallPath: string | undefined,
logger: Log
logger: Log,
extensionPath: string
): Promise<ServerInstallResult> {
let shell = 'powershell';
@@ -160,7 +176,7 @@ export async function installCodeServer(
let commandOutput: { stdout: string; stderr: string };
if (platform === 'windows') {
const installServerScript = generatePowerShellInstallScript(installOptions);
const installServerScript = generatePowerShellInstallScript(installOptions, extensionPath);
logger.trace('Server install command:', installServerScript);
@@ -207,7 +223,7 @@ export async function installCodeServer(
commandOutput = await conn.execPartial(command, (stdout: string) => endRegex.test(stdout));
} else {
const installServerScript = generateBashInstallScript(installOptions);
const installServerScript = generateBashInstallScript(installOptions, extensionPath);
logger.trace('Server install command:', installServerScript);
// Fish shell does not support heredoc so let's workaround it using -c option,
@@ -275,272 +291,37 @@ function parseServerInstallOutput(str: string, scriptId: string): { [k: string]:
return resultMap;
}
function generateBashInstallScript({ id, quality, version, commit, release, extensionIds, envVariables, useSocketPath, serverApplicationName, serverDataFolderName, serverDownloadUrlTemplate, customInstallPath, serverValidation }: ServerInstallOptions) {
const extensions = extensionIds.map(id => '--install-extension ' + id).join(' ');
function generateBashInstallScript({ id, quality, version, commit, release, extensionIds, envVariables, useSocketPath, serverApplicationName, serverDataFolderName, serverDownloadUrlTemplate, customInstallPath, serverValidation }: ServerInstallOptions, extensionPath: string): string {
const extensions = extensionIds.map(extId => '--install-extension ' + extId).join(' ');
const serverDataDir = customInstallPath
? customInstallPath.replace(/^~(?=\/|$)/, '$HOME')
: `$HOME/${serverDataFolderName}`;
return `
# Server installation script
const listenFlag = useSocketPath
? `--socket-path="$TMP_DIR/vscode-server-sock-${crypto.randomUUID()}"`
: '--port=0';
const envVarLines = envVariables.map(envVar => ` echo "${envVar}==$${envVar}=="`).join('\n');
TMP_DIR="\${XDG_RUNTIME_DIR:-"/tmp"}"
DISTRO_VERSION="${version}"
DISTRO_COMMIT="${commit}"
DISTRO_QUALITY="${quality}"
DISTRO_VSCODIUM_RELEASE="${release ?? ''}"
SERVER_APP_NAME="${serverApplicationName}"
SERVER_INITIAL_EXTENSIONS="${extensions}"
SERVER_LISTEN_FLAG="${useSocketPath ? `--socket-path="$TMP_DIR/vscode-server-sock-${crypto.randomUUID()}"` : '--port=0'}"
SERVER_DATA_DIR="${serverDataDir}"
SERVER_DATA_DIR_FLAG="${customInstallPath ? '--server-data-dir="$SERVER_DATA_DIR"' : ''}"
SERVER_DIR="$SERVER_DATA_DIR/bin/$DISTRO_COMMIT"
SERVER_SCRIPT="$SERVER_DIR/bin/$SERVER_APP_NAME"
SERVER_LOGFILE="$SERVER_DATA_DIR/.$DISTRO_COMMIT.log"
SERVER_PIDFILE="$SERVER_DATA_DIR/.$DISTRO_COMMIT.pid"
SERVER_TOKENFILE="$SERVER_DATA_DIR/.$DISTRO_COMMIT.token"
SERVER_ARCH=
SERVER_CONNECTION_TOKEN=
SERVER_DOWNLOAD_URL=
SERVER_VALIDATION_FLAG="${serverValidation === 'skip' ? '--disable-client-validation' : ''}"
LISTENING_ON=
OS_RELEASE_ID=
ARCH=
PLATFORM=
# Mimic output from logs of remote-ssh extension
print_install_results_and_exit() {
echo "${id}: start"
echo "exitCode==$1=="
echo "listeningOn==$LISTENING_ON=="
echo "connectionToken==$SERVER_CONNECTION_TOKEN=="
echo "logFile==$SERVER_LOGFILE=="
echo "osReleaseId==$OS_RELEASE_ID=="
echo "arch==$ARCH=="
echo "platform==$PLATFORM=="
echo "tmpDir==$TMP_DIR=="
${envVariables.map(envVar => `echo "${envVar}==$${envVar}=="`).join('\n')}
echo "${id}: end"
exit 0
return compileTemplate('server-setup.sh', {
DISTRO_VERSION: version,
DISTRO_COMMIT: commit,
DISTRO_QUALITY: quality,
DISTRO_VSCODIUM_RELEASE: release ?? '',
SERVER_APP_NAME: serverApplicationName,
SERVER_INITIAL_EXTENSIONS: extensions,
SERVER_LISTEN_FLAG: listenFlag,
SERVER_DATA_DIR: serverDataDir,
SERVER_DATA_DIR_FLAG: customInstallPath ? '--server-data-dir="$SERVER_DATA_DIR"' : '',
SERVER_VALIDATION_FLAG: serverValidation === 'skip' ? '--disable-client-validation' : '',
SERVER_DOWNLOAD_URL_TEMPLATE: serverDownloadUrlTemplate.replace(/\$\{/g, '\\${'),
SCRIPT_ID: id,
ENV_VAR_LINES: envVarLines,
MODIFY_PRODUCT_JSON: serverValidation === 'force' ? 'true' : 'false',
SERVER_CONNECTION_TOKEN: crypto.randomUUID(),
}, extensionPath);
}
LOCKFILE="$TMP_DIR/server_install.lock"
if command -v flock >/dev/null 2>&1; then
exec {FD}<>"$LOCKFILE"
# wait 30s to acquire lock, otherwise fail
flock -x -w 30 $FD || print_install_results_and_exit 1
trap "flock -u $FD; trap - EXIT INT HUP; exit" EXIT INT HUP
else
echo "Warning: flock not available, skipping install lock" >&2
fi
# Check if platform is supported
if ! command -v uname; then
echo "Error 'uname' command not found, could not get platform/arch data."
print_install_results_and_exit 1
fi
KERNEL="$(uname -s)"
case $KERNEL in
Darwin)
PLATFORM="darwin"
;;
Linux)
PLATFORM="linux"
;;
FreeBSD)
PLATFORM="freebsd"
;;
DragonFly)
PLATFORM="dragonfly"
;;
"")
echo "Error uname -s yields empty result"
print_install_results_and_exit 1
;;
*)
echo "Error platform not supported: $KERNEL"
print_install_results_and_exit 1
;;
esac
# Check machine architecture
ARCH="$(uname -m)"
case $ARCH in
x86_64 | amd64)
SERVER_ARCH="x64"
;;
armv7l | armv8l)
SERVER_ARCH="armhf"
;;
arm64 | aarch64)
SERVER_ARCH="arm64"
;;
ppc64le)
SERVER_ARCH="ppc64le"
;;
riscv64)
SERVER_ARCH="riscv64"
;;
loongarch64)
SERVER_ARCH="loong64"
;;
s390x)
SERVER_ARCH="s390x"
;;
*)
echo "Error architecture not supported: $ARCH"
print_install_results_and_exit 1
;;
esac
# https://www.freedesktop.org/software/systemd/man/os-release.html
OS_RELEASE_ID="$(grep -i '^ID=' /etc/os-release 2>/dev/null | sed 's/^ID=//gi' | sed 's/"//g')"
if [[ -z $OS_RELEASE_ID ]]; then
OS_RELEASE_ID="$(grep -i '^ID=' /usr/lib/os-release 2>/dev/null | sed 's/^ID=//gi' | sed 's/"//g')"
if [[ -z $OS_RELEASE_ID ]]; then
OS_RELEASE_ID="unknown"
fi
fi
# Create installation folder
if [[ ! -d $SERVER_DIR ]]; then
mkdir -p $SERVER_DIR
if (( $? > 0 )); then
echo "Error creating server install directory"
print_install_results_and_exit 1
fi
fi
# adjust platform for vscodium download, if needed
if [[ $OS_RELEASE_ID = alpine ]]; then
PLATFORM=$OS_RELEASE_ID
fi
SERVER_DOWNLOAD_URL="$(echo "${serverDownloadUrlTemplate.replace(/\$\{/g, '\\${')}" | sed "s/\\\${quality}/$DISTRO_QUALITY/g" | sed "s/\\\${version}/$DISTRO_VERSION/g" | sed "s/\\\${commit}/$DISTRO_COMMIT/g" | sed "s/\\\${os}/$PLATFORM/g" | sed "s/\\\${arch}/$SERVER_ARCH/g" | sed "s/\\\${release}/$DISTRO_VSCODIUM_RELEASE/g")"
# Check if server script is already installed
if [[ ! -f $SERVER_SCRIPT ]]; then
case "$PLATFORM" in
darwin | linux | alpine | freebsd )
;;
*)
echo "Error '$PLATFORM' needs manual installation of remote extension host"
print_install_results_and_exit 1
;;
esac
pushd $SERVER_DIR > /dev/null
if command -v wget >/dev/null 2>&1; then
wget --tries=3 --timeout=10 --continue --no-verbose -O vscode-server.tar.gz $SERVER_DOWNLOAD_URL
elif command -v curl >/dev/null 2>&1; then
curl --retry 3 --connect-timeout 10 --location --show-error --silent --output vscode-server.tar.gz $SERVER_DOWNLOAD_URL
elif command -v fetch >/dev/null 2>&1; then
fetch --retry --timeout=10 --quiet --output=vscode-server.tar.gz $SERVER_DOWNLOAD_URL
else
echo "Error no tool to download server binary"
print_install_results_and_exit 1
fi
if (( $? > 0 )); then
echo "Error downloading server from $SERVER_DOWNLOAD_URL"
rm -rf vscode-server.tar.gz
print_install_results_and_exit 1
fi
tar -xf vscode-server.tar.gz --strip-components 1
if (( $? > 0 )); then
echo "Error while extracting server contents"
rm -rf vscode-server.tar.gz
print_install_results_and_exit 1
fi
if [[ ! -f $SERVER_SCRIPT ]]; then
rm -rf $SERVER_DIR/*
echo "Error server contents are corrupted"
print_install_results_and_exit 1
fi
rm -f vscode-server.tar.gz
popd > /dev/null
else
echo "Server script already installed in $SERVER_SCRIPT"
fi
# Modify the commit in the remote server to match the local value
if ${serverValidation === 'force' ? 'true' : 'false'}; then
if command -v sed >/dev/null 2>&1; then
echo "Will modify product.json on remote to match the commit value"
sed -i -E 's/"commit": "[0-9a-f]+",/"commit": "'"$DISTRO_COMMIT"'",/' "$SERVER_DIR/product.json";
else
echo "Cannot find the 'sed' command, make sure it is installed to modify product.json with the matching commit."
fi
fi
# Try to find if server is already running
if [[ -f $SERVER_PIDFILE ]]; then
SERVER_PID="$(cat $SERVER_PIDFILE)"
SERVER_RUNNING_PROCESS="$(ps -o pid,args -p $SERVER_PID | grep $SERVER_SCRIPT)"
else
SERVER_RUNNING_PROCESS="$(ps -o pid,args -A | grep $SERVER_SCRIPT | grep -v grep)"
fi
if [[ -z $SERVER_RUNNING_PROCESS ]]; then
if [[ -f $SERVER_LOGFILE ]]; then
rm $SERVER_LOGFILE
fi
if [[ -f $SERVER_TOKENFILE ]]; then
rm $SERVER_TOKENFILE
fi
touch $SERVER_TOKENFILE
chmod 600 $SERVER_TOKENFILE
SERVER_CONNECTION_TOKEN="${crypto.randomUUID()}"
echo $SERVER_CONNECTION_TOKEN > $SERVER_TOKENFILE
$SERVER_SCRIPT --start-server --host=127.0.0.1 $SERVER_LISTEN_FLAG $SERVER_DATA_DIR_FLAG $SERVER_VALIDATION_FLAG $SERVER_INITIAL_EXTENSIONS --connection-token-file $SERVER_TOKENFILE --telemetry-level off --enable-remote-auto-shutdown --accept-server-license-terms &> $SERVER_LOGFILE &
echo $! > $SERVER_PIDFILE
else
echo "Server script is already running $SERVER_SCRIPT"
fi
if [[ -f $SERVER_TOKENFILE ]]; then
SERVER_CONNECTION_TOKEN="$(cat $SERVER_TOKENFILE)"
else
echo "Error server token file not found $SERVER_TOKENFILE"
print_install_results_and_exit 1
fi
if [[ -f $SERVER_LOGFILE ]]; then
for i in {1..35}; do
LISTENING_ON="$(cat $SERVER_LOGFILE | grep -E 'Extension host agent listening on .+' | sed 's/Extension host agent listening on //')"
if [[ -n $LISTENING_ON ]]; then
break
fi
sleep 0.5
done
if [[ -z $LISTENING_ON ]]; then
echo "Error server did not start successfully"
print_install_results_and_exit 1
fi
else
echo "Error server log file not found $SERVER_LOGFILE"
print_install_results_and_exit 1
fi
# Finish server setup
print_install_results_and_exit 0
`;
}
function generatePowerShellInstallScript({ id, quality, version, commit, release, extensionIds, envVariables, useSocketPath, serverApplicationName, serverDataFolderName, serverDownloadUrlTemplate, customInstallPath, serverValidation }: ServerInstallOptions) {
const extensions = extensionIds.map(id => '--install-extension ' + id).join(' ');
function generatePowerShellInstallScript({ id, quality, version, commit, release, extensionIds, envVariables, useSocketPath, serverApplicationName, serverDataFolderName, serverDownloadUrlTemplate, customInstallPath, serverValidation }: ServerInstallOptions, extensionPath: string): string {
const extensions = extensionIds.map(extId => '--install-extension ' + extId).join(' ');
const downloadUrl = serverDownloadUrlTemplate
.replace(/\$\{quality\}/g, quality)
.replace(/\$\{version\}/g, version)
@@ -551,201 +332,26 @@ function generatePowerShellInstallScript({ id, quality, version, commit, release
const serverDataDir = customInstallPath
? customInstallPath.replace(/^~(?=[\\/]|$)/, '$(Resolve-Path ~)')
: `$(Resolve-Path ~)\\${serverDataFolderName}`;
const listenFlag = useSocketPath
? `--socket-path="$TMP_DIR/vscode-server-sock-${crypto.randomUUID()}"`
: '--port=0';
const envVarLines = envVariables.map(envVar => ` "$${envVar}==$${envVar}=="`).join('\n');
return `
# Server installation script
$TMP_DIR="$env:TEMP\\$([System.IO.Path]::GetRandomFileName())"
$ProgressPreference = "SilentlyContinue"
$DISTRO_VERSION="${version}"
$DISTRO_COMMIT="${commit}"
$DISTRO_QUALITY="${quality}"
$DISTRO_VSCODIUM_RELEASE="${release ?? ''}"
$SERVER_APP_NAME="${serverApplicationName}"
$SERVER_INITIAL_EXTENSIONS="${extensions}"
$SERVER_LISTEN_FLAG="${useSocketPath ? `--socket-path="$TMP_DIR/vscode-server-sock-${crypto.randomUUID()}"` : '--port=0'}"
$SERVER_DATA_DIR="${serverDataDir}"
$SERVER_DATA_DIR_FLAG="${customInstallPath ? '--server-data-dir=""$SERVER_DATA_DIR""' : ''}"
$SERVER_DIR="$SERVER_DATA_DIR\\bin\\$DISTRO_COMMIT"
$SERVER_SCRIPT="$SERVER_DIR\\bin\\$SERVER_APP_NAME.cmd"
$SERVER_LOGFILE="$SERVER_DATA_DIR\\.$DISTRO_COMMIT.log"
$SERVER_PIDFILE="$SERVER_DATA_DIR\\.$DISTRO_COMMIT.pid"
$SERVER_TOKENFILE="$SERVER_DATA_DIR\\.$DISTRO_COMMIT.token"
$SERVER_ARCH=
$SERVER_CONNECTION_TOKEN=
$SERVER_DOWNLOAD_URL=
$SERVER_VALIDATION_FLAG="${serverValidation === 'skip' ? '--disable-client-validation' : ''}"
$LISTENING_ON=
$OS_RELEASE_ID=
$ARCH=
$PLATFORM="win32"
function printInstallResults($code) {
"${id}: start"
"exitCode==$code=="
"listeningOn==$LISTENING_ON=="
"connectionToken==$SERVER_CONNECTION_TOKEN=="
"logFile==$SERVER_LOGFILE=="
"osReleaseId==$OS_RELEASE_ID=="
"arch==$ARCH=="
"platform==$PLATFORM=="
"tmpDir==$TMP_DIR=="
${envVariables.map(envVar => `"${envVar}==$${envVar}=="`).join('\n')}
"${id}: end"
}
# Check machine architecture
$ARCH=$env:PROCESSOR_ARCHITECTURE
# Use x64 version for ARM64, as it's not yet available.
if(($ARCH -eq "AMD64") -or ($ARCH -eq "IA64") -or ($ARCH -eq "ARM64")) {
$SERVER_ARCH="x64"
}
else {
"Error architecture not supported: $ARCH"
printInstallResults 1
exit 0
}
# Create installation folder
if(!(Test-Path $SERVER_DIR)) {
try {
ni -it d $SERVER_DIR -f -ea si
} catch {
"Error creating server install directory - $($_.ToString())"
exit 1
}
if(!(Test-Path $SERVER_DIR)) {
"Error creating server install directory"
exit 1
}
}
cd $SERVER_DIR
# Check if server script is already installed
if(!(Test-Path $SERVER_SCRIPT)) {
del vscode-server.tar.gz
$REQUEST_ARGUMENTS = @{
Uri="${downloadUrl}"
TimeoutSec=20
OutFile="vscode-server.tar.gz"
UseBasicParsing=$True
}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Invoke-RestMethod @REQUEST_ARGUMENTS
if(Test-Path "vscode-server.tar.gz") {
tar -xf vscode-server.tar.gz --strip-components 1
del vscode-server.tar.gz
}
if(!(Test-Path $SERVER_SCRIPT)) {
"Error while installing the server binary"
exit 1
}
}
else {
"Server script already installed in $SERVER_SCRIPT"
}
# Modify the commit in the remote server to match the local value
if(${serverValidation === 'force' ? '$true' : '$false'}) {
echo "Will modify product.json on remote to match the commit value"
(Get-Content -Raw "$SERVER_DIR\\product.json") -replace '"commit": "[0-9a-f]+",', ('"commit": "' + $DISTRO_COMMIT + '",') |
Set-Content -NoNewLine "$SERVER_DIR\\product.json"
}
# Try to find if server is already running
if(Get-Process node -ErrorAction SilentlyContinue | Where-Object Path -Like "$SERVER_DIR\\*") {
echo "Server script is already running $SERVER_SCRIPT"
}
else {
if(Test-Path $SERVER_LOGFILE) {
del $SERVER_LOGFILE
}
if(Test-Path $SERVER_PIDFILE) {
del $SERVER_PIDFILE
}
if(Test-Path $SERVER_TOKENFILE) {
del $SERVER_TOKENFILE
}
$SERVER_CONNECTION_TOKEN="${crypto.randomUUID()}"
[System.IO.File]::WriteAllLines($SERVER_TOKENFILE, $SERVER_CONNECTION_TOKEN)
$SCRIPT_ARGUMENTS="--start-server --host=127.0.0.1 $SERVER_LISTEN_FLAG $SERVER_DATA_DIR_FLAG $SERVER_VALIDATION_FLAG $SERVER_INITIAL_EXTENSIONS --connection-token-file $SERVER_TOKENFILE --telemetry-level off --enable-remote-auto-shutdown --accept-server-license-terms *> '$SERVER_LOGFILE'"
$START_ARGUMENTS = @{
FilePath = "powershell.exe"
WindowStyle = "hidden"
ArgumentList = @(
"-ExecutionPolicy", "Unrestricted", "-NoLogo", "-NoProfile", "-NonInteractive", "-c", "$SERVER_SCRIPT $SCRIPT_ARGUMENTS"
)
PassThru = $True
}
$SERVER_ID = (start @START_ARGUMENTS).ID
if($SERVER_ID) {
[System.IO.File]::WriteAllLines($SERVER_PIDFILE, $SERVER_ID)
}
}
if(Test-Path $SERVER_TOKENFILE) {
$SERVER_CONNECTION_TOKEN="$(cat $SERVER_TOKENFILE)"
}
else {
"Error server token file not found $SERVER_TOKENFILE"
printInstallResults 1
exit 0
}
sleep -Milliseconds 500
$SELECT_ARGUMENTS = @{
Path = $SERVER_LOGFILE
Pattern = "Extension host agent listening on (\\d+)"
}
for($I = 1; $I -le 5; $I++) {
if(Test-Path $SERVER_LOGFILE) {
$GROUPS = (Select-String @SELECT_ARGUMENTS).Matches.Groups
if($GROUPS) {
$LISTENING_ON = $GROUPS[1].Value
break
}
}
sleep -Milliseconds 500
}
if(!(Test-Path $SERVER_LOGFILE)) {
"Error server log file not found $SERVER_LOGFILE"
printInstallResults 1
exit 0
}
# Finish server setup
printInstallResults 0
if($SERVER_ID) {
while($True) {
if(!(gps -Id $SERVER_ID)) {
"server died, exit"
exit 0
}
sleep 30
}
}
`;
return compileTemplate('server-setup.ps1', {
DISTRO_VERSION: version,
DISTRO_COMMIT: commit,
DISTRO_QUALITY: quality,
DISTRO_VSCODIUM_RELEASE: release ?? '',
SERVER_APP_NAME: serverApplicationName,
SERVER_INITIAL_EXTENSIONS: extensions,
SERVER_LISTEN_FLAG: listenFlag,
SERVER_DATA_DIR: serverDataDir,
SERVER_DATA_DIR_FLAG: customInstallPath ? '--server-data-dir=""$SERVER_DATA_DIR""' : '',
SERVER_VALIDATION_FLAG: serverValidation === 'skip' ? '--disable-client-validation' : '',
SERVER_DOWNLOAD_URL: downloadUrl,
SCRIPT_ID: id,
ENV_VAR_LINES: envVarLines,
MODIFY_PRODUCT_JSON: serverValidation === 'force' ? '$true' : '$false',
SERVER_CONNECTION_TOKEN: crypto.randomUUID(),
}, extensionPath);
}