mirror of
https://github.com/microsoft/vscode.git
synced 2026-02-15 07:28:05 +00:00
fix: use legacy server as default with additional warnings (#204377)
* ci: switch to glibc 2.17 remote server * chore: signal user about unsupported connection * chore: address review comments * chore: update nodejs build * chore: bump distro * chore: lower the minimum requirements * fix: glibc version check * chore: remove explicit connection disposal
This commit is contained in:
@@ -1 +1 @@
|
||||
2024-01-29T19:26:27.993Z
|
||||
2024-02-05T09:34:15.476Z
|
||||
|
||||
@@ -34,11 +34,6 @@ if [ "$npm_config_arch" == "x64" ]; then
|
||||
export CXX="$PWD/.build/CR_Clang/bin/clang++ --gcc-toolchain=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu"
|
||||
export CXXFLAGS="-nostdinc++ -D__NO_INLINE__ -I$PWD/.build/libcxx_headers -isystem$PWD/.build/libcxx_headers/include -isystem$PWD/.build/libcxxabi_headers/include -fPIC -flto=thin -fsplit-lto-unit -D_LIBCPP_ABI_NAMESPACE=Cr --sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot"
|
||||
export LDFLAGS="-stdlib=libc++ --sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot -fuse-ld=lld -flto=thin -L$PWD/.build/libcxx-objects -lc++abi -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/usr/lib/x86_64-linux-gnu -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/lib/x86_64-linux-gnu -Wl,--lto-O0"
|
||||
# Set compiler toolchain for remote server
|
||||
export VSCODE_REMOTE_CC=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/bin/x86_64-linux-gnu-gcc
|
||||
export VSCODE_REMOTE_CXX=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/bin/x86_64-linux-gnu-g++
|
||||
export VSCODE_REMOTE_CXXFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot"
|
||||
export VSCODE_REMOTE_LDFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/usr/lib/x86_64-linux-gnu -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/lib/x86_64-linux-gnu"
|
||||
elif [ "$npm_config_arch" == "arm64" ]; then
|
||||
# Set compiler toolchain for client native modules and remote server
|
||||
export CC=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc
|
||||
|
||||
@@ -121,53 +121,23 @@ steps:
|
||||
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
GITHUB_TOKEN: "$(github-distro-mixin-password)"
|
||||
VSCODE_HOST_MOUNT: "/mnt/vss/_work/1/s"
|
||||
${{ if or(eq(parameters.VSCODE_ARCH, 'x64'), eq(parameters.VSCODE_ARCH, 'arm64')) }}:
|
||||
VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME: vscodehub.azurecr.io/vscode-linux-build-agent:centos7-devtoolset8-$(VSCODE_ARCH)
|
||||
${{ if eq(parameters.VSCODE_ARCH, 'armhf') }}:
|
||||
VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME: vscodehub.azurecr.io/vscode-linux-build-agent:bionic-arm32v7
|
||||
displayName: Install dependencies (non-OSS)
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
- ${{ if or(eq(parameters.VSCODE_ARCH, 'x64'), eq(parameters.VSCODE_ARCH, 'arm64')) }}:
|
||||
- script: |
|
||||
set -e
|
||||
|
||||
TRIPLE="x86_64-linux-gnu"
|
||||
if [ "$VSCODE_ARCH" == "arm64" ]; then
|
||||
TRIPLE="aarch64-linux-gnu"
|
||||
elif [ "$VSCODE_ARCH" == "armhf" ]; then
|
||||
TRIPLE="arm-rpi-linux-gnueabihf"
|
||||
fi
|
||||
|
||||
# Get all files with .node extension from remote/node_modules folder
|
||||
files=$(find remote/node_modules -name "*.node")
|
||||
|
||||
# Check if any file has dependency of GLIBC > 2.28 or GLIBCXX > 3.4.25
|
||||
for file in $files; do
|
||||
glibc_version="2.28"
|
||||
glibcxx_version="3.4.25"
|
||||
while IFS= read -r line; do
|
||||
if [[ $line == *"GLIBC_"* ]]; then
|
||||
version=$(echo "$line" | awk '{print $5}' | tr -d '()')
|
||||
version=${version#*_}
|
||||
if [[ $(printf "%s\n%s" "$version" "$glibc_version" | sort -V | tail -n1) == "$version" ]]; then
|
||||
glibc_version=$version
|
||||
fi
|
||||
elif [[ $line == *"GLIBCXX_"* ]]; then
|
||||
version=$(echo "$line" | awk '{print $5}' | tr -d '()')
|
||||
version=${version#*_}
|
||||
if [[ $(printf "%s\n%s" "$version" "$glibcxx_version" | sort -V | tail -n1) == "$version" ]]; then
|
||||
glibcxx_version=$version
|
||||
fi
|
||||
fi
|
||||
done < <("$PWD/.build/sysroots/$TRIPLE/$TRIPLE/bin/objdump" -T "$file")
|
||||
|
||||
if [[ "$glibc_version" != "2.28" ]]; then
|
||||
echo "Error: File $file has dependency on GLIBC > 2.28"
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$glibcxx_version" != "3.4.25" ]]; then
|
||||
echo "Error: File $file has dependency on GLIBCXX > 3.4.25"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
displayName: Check GLIBC and GLIBCXX dependencies in remote/node_modules
|
||||
EXPECTED_GLIBC_VERSION="2.17" \
|
||||
EXPECTED_GLIBCXX_VERSION="3.4.19" \
|
||||
./build/azure-pipelines/linux/verify-glibc-requirements.sh
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
displayName: Check GLIBC and GLIBCXX dependencies in remote/node_modules
|
||||
|
||||
- script: node build/azure-pipelines/distro/mixin-npm
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
@@ -248,6 +218,7 @@ steps:
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
export VSCODE_NODE_GLIBC='-glibc-2.17'
|
||||
yarn gulp vscode-reh-linux-$(VSCODE_ARCH)-min-ci
|
||||
mv ../vscode-reh-linux-$(VSCODE_ARCH) ../vscode-server-linux-$(VSCODE_ARCH) # TODO@joaomoreno
|
||||
ARCHIVE_PATH=".build/linux/server/vscode-server-linux-$(VSCODE_ARCH).tar.gz"
|
||||
@@ -260,6 +231,7 @@ steps:
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
export VSCODE_NODE_GLIBC='-glibc-2.17'
|
||||
yarn gulp vscode-reh-web-linux-$(VSCODE_ARCH)-min-ci
|
||||
mv ../vscode-reh-web-linux-$(VSCODE_ARCH) ../vscode-server-linux-$(VSCODE_ARCH)-web # TODO@joaomoreno
|
||||
ARCHIVE_PATH=".build/linux/web/vscode-server-linux-$(VSCODE_ARCH)-web.tar.gz"
|
||||
|
||||
44
build/azure-pipelines/linux/verify-glibc-requirements.sh
Executable file
44
build/azure-pipelines/linux/verify-glibc-requirements.sh
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
TRIPLE="x86_64-linux-gnu"
|
||||
if [ "$VSCODE_ARCH" == "arm64" ]; then
|
||||
TRIPLE="aarch64-linux-gnu"
|
||||
elif [ "$VSCODE_ARCH" == "armhf" ]; then
|
||||
TRIPLE="arm-rpi-linux-gnueabihf"
|
||||
fi
|
||||
|
||||
# Get all files with .node extension from remote/node_modules folder
|
||||
files=$(find remote/node_modules -name "*.node" -not -path "*prebuilds*")
|
||||
|
||||
echo "Verifying requirements for files: $files"
|
||||
|
||||
for file in $files; do
|
||||
glibc_version="$EXPECTED_GLIBC_VERSION"
|
||||
glibcxx_version="$EXPECTED_GLIBCXX_VERSION"
|
||||
while IFS= read -r line; do
|
||||
if [[ $line == *"GLIBC_"* ]]; then
|
||||
version=$(echo "$line" | awk '{print $5}' | tr -d '()')
|
||||
version=${version#*_}
|
||||
if [[ $(printf "%s\n%s" "$version" "$glibc_version" | sort -V | tail -n1) == "$version" ]]; then
|
||||
glibc_version=$version
|
||||
fi
|
||||
elif [[ $line == *"GLIBCXX_"* ]]; then
|
||||
version=$(echo "$line" | awk '{print $5}' | tr -d '()')
|
||||
version=${version#*_}
|
||||
if [[ $(printf "%s\n%s" "$version" "$glibcxx_version" | sort -V | tail -n1) == "$version" ]]; then
|
||||
glibcxx_version=$version
|
||||
fi
|
||||
fi
|
||||
done < <("$PWD/.build/sysroots/$TRIPLE/$TRIPLE/bin/objdump" -T "$file")
|
||||
|
||||
if [[ "$glibc_version" != "$EXPECTED_GLIBC_VERSION" ]]; then
|
||||
echo "Error: File $file has dependency on GLIBC > $EXPECTED_GLIBC_VERSION"
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$glibcxx_version" != "$EXPECTED_GLIBCXX_VERSION" ]]; then
|
||||
echo "Error: File $file has dependency on GLIBCXX > $EXPECTED_GLIBCXX_VERSION"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
@@ -125,7 +125,7 @@ function getNodeVersion() {
|
||||
return { nodeVersion, internalNodeVersion };
|
||||
}
|
||||
|
||||
function getNodeChecksum(nodeVersion, platform, arch) {
|
||||
function getNodeChecksum(nodeVersion, platform, arch, glibcPrefix) {
|
||||
let expectedName;
|
||||
switch (platform) {
|
||||
case 'win32':
|
||||
@@ -135,7 +135,7 @@ function getNodeChecksum(nodeVersion, platform, arch) {
|
||||
case 'darwin':
|
||||
case 'alpine':
|
||||
case 'linux':
|
||||
expectedName = `node-v${nodeVersion}-${platform}-${arch}.tar.gz`;
|
||||
expectedName = `node-v${nodeVersion}${glibcPrefix}-${platform}-${arch}.tar.gz`;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -193,7 +193,8 @@ function nodejs(platform, arch) {
|
||||
|
||||
log(`Downloading node.js ${nodeVersion} ${platform} ${arch} from ${product.nodejsRepository}...`);
|
||||
|
||||
const checksumSha256 = getNodeChecksum(nodeVersion, platform, arch);
|
||||
const glibcPrefix = process.env['VSCODE_NODE_GLIBC'] ?? '';
|
||||
const checksumSha256 = getNodeChecksum(nodeVersion, platform, arch, glibcPrefix);
|
||||
|
||||
if (checksumSha256) {
|
||||
log(`Using SHA256 checksum for checking integrity: ${checksumSha256}`);
|
||||
@@ -210,7 +211,7 @@ function nodejs(platform, arch) {
|
||||
case 'darwin':
|
||||
case 'linux':
|
||||
return (product.nodejsRepository !== 'https://nodejs.org' ?
|
||||
fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: `node-v${nodeVersion}-${platform}-${arch}.tar.gz`, checksumSha256 }) :
|
||||
fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: `node-v${nodeVersion}${glibcPrefix}-${platform}-${arch}.tar.gz`, checksumSha256 }) :
|
||||
fetchUrls(`/dist/v${nodeVersion}/node-v${nodeVersion}-${platform}-${arch}.tar.gz`, { base: 'https://nodejs.org', checksumSha256 })
|
||||
).pipe(flatmap(stream => stream.pipe(gunzip()).pipe(untar())))
|
||||
.pipe(filter('**/node'))
|
||||
|
||||
@@ -53,10 +53,14 @@ function yarnInstall(dir, opts) {
|
||||
console.log(`Installing dependencies in ${dir} inside container ${process.env['VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME']}...`);
|
||||
|
||||
opts.cwd = root;
|
||||
if (process.env['npm_config_arch'] === 'arm64') {
|
||||
if (process.env['npm_config_arch'] === 'arm64' || process.env['npm_config_arch'] === 'arm') {
|
||||
run('sudo', ['docker', 'run', '--rm', '--privileged', 'multiarch/qemu-user-static', '--reset', '-p', 'yes'], opts);
|
||||
}
|
||||
run('sudo', ['docker', 'run', '-e', 'GITHUB_TOKEN', '-e', 'npm_config_arch', '-v', `${process.env['VSCODE_HOST_MOUNT']}:/root/vscode`, '-v', `${process.env['VSCODE_HOST_MOUNT']}/.build/.netrc:/root/.netrc`, process.env['VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME'], 'yarn', '--cwd', dir, ...args], opts);
|
||||
if (process.env['npm_config_arch'] === 'arm') {
|
||||
run('sudo', ['docker', 'run', '-e', 'GITHUB_TOKEN', '-e', 'npm_config_arch', '-v', `${process.env['VSCODE_HOST_MOUNT']}:/home/builduser`, '-v', `${process.env['VSCODE_HOST_MOUNT']}/.build/.netrc:/home/builduser/.netrc`, process.env['VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME'], 'yarn', '--cwd', dir, ...args], opts);
|
||||
} else {
|
||||
run('sudo', ['docker', 'run', '-e', 'GITHUB_TOKEN', '-e', 'npm_config_arch', '-v', `${process.env['VSCODE_HOST_MOUNT']}:/root/vscode`, '-v', `${process.env['VSCODE_HOST_MOUNT']}/.build/.netrc:/root/.netrc`, process.env['VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME'], 'yarn', '--cwd', dir, ...args], opts);
|
||||
}
|
||||
run('sudo', ['chown', '-R', `${userinfo.uid}:${userinfo.gid}`, `${dir}/node_modules`], opts);
|
||||
} else {
|
||||
console.log(`Installing dependencies in ${dir}...`);
|
||||
@@ -102,8 +106,19 @@ for (let dir of dirs) {
|
||||
if (/^(.build\/distro\/npm\/)?remote$/.test(dir)) {
|
||||
// node modules used by vscode server
|
||||
const env = { ...process.env };
|
||||
if (process.env['VSCODE_REMOTE_CC']) { env['CC'] = process.env['VSCODE_REMOTE_CC']; }
|
||||
if (process.env['VSCODE_REMOTE_CXX']) { env['CXX'] = process.env['VSCODE_REMOTE_CXX']; }
|
||||
if (process.env['VSCODE_REMOTE_CC']) {
|
||||
env['CC'] = process.env['VSCODE_REMOTE_CC'];
|
||||
} else {
|
||||
delete env['CC'];
|
||||
}
|
||||
if (process.env['VSCODE_REMOTE_CXX']) {
|
||||
env['CXX'] = process.env['VSCODE_REMOTE_CXX'];
|
||||
} else {
|
||||
delete env['CXX'];
|
||||
}
|
||||
if (process.env['CXXFLAGS']) { delete env['CXXFLAGS']; }
|
||||
if (process.env['CFLAGS']) { delete env['CFLAGS']; }
|
||||
if (process.env['LDFLAGS']) { delete env['LDFLAGS']; }
|
||||
if (process.env['VSCODE_REMOTE_CXXFLAGS']) { env['CXXFLAGS'] = process.env['VSCODE_REMOTE_CXXFLAGS']; }
|
||||
if (process.env['VSCODE_REMOTE_LDFLAGS']) { env['LDFLAGS'] = process.env['VSCODE_REMOTE_LDFLAGS']; }
|
||||
if (process.env['VSCODE_REMOTE_NODE_GYP']) { env['npm_config_node_gyp'] = process.env['VSCODE_REMOTE_NODE_GYP']; }
|
||||
|
||||
@@ -20,8 +20,8 @@ lazy_static! {
|
||||
static ref GENERIC_VERSION_RE: Regex = Regex::new(r"^([0-9]+)\.([0-9]+)$").unwrap();
|
||||
static ref LIBSTD_CXX_VERSION_RE: BinRegex =
|
||||
BinRegex::new(r"GLIBCXX_([0-9]+)\.([0-9]+)(?:\.([0-9]+))?").unwrap();
|
||||
static ref MIN_CXX_VERSION: SimpleSemver = SimpleSemver::new(3, 4, 25);
|
||||
static ref MIN_LDD_VERSION: SimpleSemver = SimpleSemver::new(2, 28, 0);
|
||||
static ref MIN_CXX_VERSION: SimpleSemver = SimpleSemver::new(3, 4, 19);
|
||||
static ref MIN_LDD_VERSION: SimpleSemver = SimpleSemver::new(2, 17, 0);
|
||||
}
|
||||
|
||||
const NIXOS_TEST_PATH: &str = "/etc/NIXOS";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "code-oss-dev",
|
||||
"version": "1.87.0",
|
||||
"distro": "ddced7c51be56108bf9f4bc3e3481a8673aa5031",
|
||||
"distro": "205c07946b7d7629f85cf332acfcfc8a76f23f6d",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
disturl "https://nodejs.org/dist"
|
||||
target "18.17.1"
|
||||
ms_build_id "252256"
|
||||
ms_build_id "255375"
|
||||
runtime "node"
|
||||
build_from_source "true"
|
||||
|
||||
@@ -126,5 +126,5 @@ fi
|
||||
if [ "$found_required_glibc" = "0" ] || [ "$found_required_glibcxx" = "0" ]; then
|
||||
echo "Error: Missing required dependencies. Please refer to our FAQ https://aka.ms/vscode-remote/faq/old-linux for additional information."
|
||||
# Custom exit code based on https://tldp.org/LDP/abs/html/exitcodes.html
|
||||
exit 99
|
||||
#exit 99
|
||||
fi
|
||||
|
||||
@@ -27,6 +27,7 @@ export interface IRemoteAgentEnvironment {
|
||||
all: IUserDataProfile[];
|
||||
home: URI;
|
||||
};
|
||||
isUnsupportedGlibc: boolean;
|
||||
}
|
||||
|
||||
export interface RemoteAgentConnectionContext {
|
||||
|
||||
@@ -96,6 +96,15 @@ export class RemoteAgentEnvironmentChannel implements IServerChannel {
|
||||
if (profile && !this._userDataProfilesService.profiles.some(p => p.id === profile)) {
|
||||
await this._userDataProfilesService.createProfile(profile, profile);
|
||||
}
|
||||
type ProcessWithGlibc = NodeJS.Process & {
|
||||
glibcVersion?: string;
|
||||
};
|
||||
let isUnsupportedGlibc = false;
|
||||
if (process.platform === 'linux') {
|
||||
const glibcVersion = (process as ProcessWithGlibc).glibcVersion;
|
||||
const minorVersion = glibcVersion ? parseInt(glibcVersion.split('.')[1]) : 28;
|
||||
isUnsupportedGlibc = (minorVersion <= 27);
|
||||
}
|
||||
return {
|
||||
pid: process.pid,
|
||||
connectionToken: (this._connectionToken.type !== ServerConnectionTokenType.None ? this._connectionToken.value : ''),
|
||||
@@ -114,7 +123,8 @@ export class RemoteAgentEnvironmentChannel implements IServerChannel {
|
||||
profiles: {
|
||||
home: this._userDataProfilesService.profilesHome,
|
||||
all: [...this._userDataProfilesService.profiles].map(profile => ({ ...profile }))
|
||||
}
|
||||
},
|
||||
isUnsupportedGlibc
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -222,11 +222,13 @@ export class BannerPart extends Part implements IBannerService {
|
||||
}
|
||||
|
||||
// Action
|
||||
const actionBarContainer = append(this.element, $('div.action-container'));
|
||||
this.actionBar = this._register(new ActionBar(actionBarContainer));
|
||||
const closeAction = this._register(new Action('banner.close', 'Close Banner', ThemeIcon.asClassName(widgetClose), true, () => this.close(item)));
|
||||
this.actionBar.push(closeAction, { icon: true, label: false });
|
||||
this.actionBar.setFocusable(false);
|
||||
if (!item.disableCloseAction) {
|
||||
const actionBarContainer = append(this.element, $('div.action-container'));
|
||||
this.actionBar = this._register(new ActionBar(actionBarContainer));
|
||||
const closeAction = this._register(new Action('banner.close', 'Close Banner', ThemeIcon.asClassName(widgetClose), true, () => this.close(item)));
|
||||
this.actionBar.push(closeAction, { icon: true, label: false });
|
||||
this.actionBar.setFocusable(false);
|
||||
}
|
||||
|
||||
this.setVisibility(true);
|
||||
this.item = item;
|
||||
|
||||
@@ -11,6 +11,7 @@ import { TunnelFactoryContribution } from 'vs/workbench/contrib/remote/browser/t
|
||||
import { RemoteAgentConnectionStatusListener, RemoteMarkers } from 'vs/workbench/contrib/remote/browser/remote';
|
||||
import { RemoteStatusIndicator } from 'vs/workbench/contrib/remote/browser/remoteIndicator';
|
||||
import { AutomaticPortForwarding, ForwardedPortsView, PortRestore } from 'vs/workbench/contrib/remote/browser/remoteExplorer';
|
||||
import { InitialRemoteConnectionHealthContribution } from 'vs/workbench/contrib/remote/browser/remoteConnectionHealth';
|
||||
|
||||
const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
|
||||
registerWorkbenchContribution2(ShowCandidateContribution.ID, ShowCandidateContribution, WorkbenchPhase.BlockRestore);
|
||||
@@ -21,3 +22,4 @@ workbenchContributionsRegistry.registerWorkbenchContribution(ForwardedPortsView,
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(PortRestore, LifecyclePhase.Eventually);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(AutomaticPortForwarding, LifecyclePhase.Eventually);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(RemoteMarkers, LifecyclePhase.Eventually);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(InitialRemoteConnectionHealthContribution, LifecyclePhase.Restored);
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IRemoteAgentService, remoteConnectionLatencyMeasurer } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { localize } from 'vs/nls';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { getRemoteName } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { IBannerService } from 'vs/workbench/services/banner/browser/bannerService';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
|
||||
|
||||
const REMOTE_UNSUPPORTED_CONNECTION_CHOICE_KEY = 'remote.unsupportedConnectionChoice';
|
||||
|
||||
export class InitialRemoteConnectionHealthContribution implements IWorkbenchContribution {
|
||||
|
||||
constructor(
|
||||
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
|
||||
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
||||
@IBannerService private readonly bannerService: IBannerService,
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
@IOpenerService private readonly openerService: IOpenerService,
|
||||
@IHostService private readonly hostService: IHostService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
) {
|
||||
if (this._environmentService.remoteAuthority) {
|
||||
this._checkInitialRemoteConnectionHealth();
|
||||
}
|
||||
}
|
||||
|
||||
private async _confirmConnection(): Promise<boolean> {
|
||||
const enum ConnectionChoice {
|
||||
Allow = 1,
|
||||
LearnMore = 2,
|
||||
Cancel = 0
|
||||
}
|
||||
|
||||
const { result, checkboxChecked } = await this.dialogService.prompt<ConnectionChoice>({
|
||||
type: Severity.Warning,
|
||||
message: localize('unsupportedGlibcWarning', "You are about to connect to an OS that is unsupported by {0}", this.productService.nameLong),
|
||||
buttons: [
|
||||
{
|
||||
label: localize({ key: 'allow', comment: ['&& denotes a mnemonic'] }, "&&Allow"),
|
||||
run: () => ConnectionChoice.Allow
|
||||
},
|
||||
{
|
||||
label: localize({ key: 'learnMore', comment: ['&& denotes a mnemonic'] }, "&&Learn More"),
|
||||
run: async () => { await this.openerService.open('https://aka.ms/vscode-remote/faq/old-linux'); return ConnectionChoice.LearnMore; }
|
||||
}
|
||||
],
|
||||
cancelButton: {
|
||||
run: () => ConnectionChoice.Cancel
|
||||
},
|
||||
checkbox: {
|
||||
label: localize('remember', "Do not ask me again"),
|
||||
}
|
||||
});
|
||||
|
||||
if (result === ConnectionChoice.LearnMore) {
|
||||
return await this._confirmConnection();
|
||||
}
|
||||
|
||||
const allowed = result === ConnectionChoice.Allow;
|
||||
if (checkboxChecked) {
|
||||
this.storageService.store(REMOTE_UNSUPPORTED_CONNECTION_CHOICE_KEY, allowed, StorageScope.PROFILE, StorageTarget.MACHINE);
|
||||
}
|
||||
|
||||
return allowed;
|
||||
}
|
||||
|
||||
private async _checkInitialRemoteConnectionHealth(): Promise<void> {
|
||||
try {
|
||||
const environment = await this._remoteAgentService.getRawEnvironment();
|
||||
|
||||
if (environment && environment.isUnsupportedGlibc) {
|
||||
let allowed = this.storageService.getBoolean(REMOTE_UNSUPPORTED_CONNECTION_CHOICE_KEY, StorageScope.PROFILE);
|
||||
if (allowed === undefined) {
|
||||
allowed = await this._confirmConnection();
|
||||
}
|
||||
if (allowed) {
|
||||
const actions = [
|
||||
{
|
||||
label: localize('unsupportedGlibcBannerLearnMore', "Learn More"),
|
||||
href: 'https://aka.ms/vscode-remote/faq/old-linux'
|
||||
}
|
||||
];
|
||||
this.bannerService.show({
|
||||
id: 'unsupportedGlibcWarning.banner',
|
||||
message: localize('unsupportedGlibcWarning.banner', "You are connected to an OS that is unsupported by {0}.", this.productService.nameLong),
|
||||
actions,
|
||||
icon: Codicon.warning,
|
||||
disableCloseAction: true
|
||||
});
|
||||
} else {
|
||||
this.hostService.openWindow({ forceReuseWindow: true, remoteAuthority: null });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
type RemoteConnectionSuccessClassification = {
|
||||
owner: 'alexdima';
|
||||
comment: 'The initial connection succeeded';
|
||||
web: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Is web ui.' };
|
||||
connectionTimeMs: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Time, in ms, until connected'; isMeasurement: true };
|
||||
remoteName: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The name of the resolver.' };
|
||||
};
|
||||
type RemoteConnectionSuccessEvent = {
|
||||
web: boolean;
|
||||
connectionTimeMs: number | undefined;
|
||||
remoteName: string | undefined;
|
||||
};
|
||||
this._telemetryService.publicLog2<RemoteConnectionSuccessEvent, RemoteConnectionSuccessClassification>('remoteConnectionSuccess', {
|
||||
web: isWeb,
|
||||
connectionTimeMs: await this._remoteAgentService.getConnection()?.getInitialConnectionTimeMs(),
|
||||
remoteName: getRemoteName(this._environmentService.remoteAuthority)
|
||||
});
|
||||
|
||||
await this._measureExtHostLatency();
|
||||
|
||||
} catch (err) {
|
||||
|
||||
type RemoteConnectionFailureClassification = {
|
||||
owner: 'alexdima';
|
||||
comment: 'The initial connection failed';
|
||||
web: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Is web ui.' };
|
||||
remoteName: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The name of the resolver.' };
|
||||
connectionTimeMs: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Time, in ms, until connection failure'; isMeasurement: true };
|
||||
message: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Error message' };
|
||||
};
|
||||
type RemoteConnectionFailureEvent = {
|
||||
web: boolean;
|
||||
remoteName: string | undefined;
|
||||
connectionTimeMs: number | undefined;
|
||||
message: string;
|
||||
};
|
||||
this._telemetryService.publicLog2<RemoteConnectionFailureEvent, RemoteConnectionFailureClassification>('remoteConnectionFailure', {
|
||||
web: isWeb,
|
||||
connectionTimeMs: await this._remoteAgentService.getConnection()?.getInitialConnectionTimeMs(),
|
||||
remoteName: getRemoteName(this._environmentService.remoteAuthority),
|
||||
message: err ? err.message : ''
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private async _measureExtHostLatency() {
|
||||
const measurement = await remoteConnectionLatencyMeasurer.measure(this._remoteAgentService);
|
||||
if (measurement === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
type RemoteConnectionLatencyClassification = {
|
||||
owner: 'connor4312';
|
||||
comment: 'The latency to the remote extension host';
|
||||
web: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Whether this is running on web' };
|
||||
remoteName: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Anonymized remote name' };
|
||||
latencyMs: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Latency to the remote, in milliseconds'; isMeasurement: true };
|
||||
};
|
||||
type RemoteConnectionLatencyEvent = {
|
||||
web: boolean;
|
||||
remoteName: string | undefined;
|
||||
latencyMs: number;
|
||||
};
|
||||
|
||||
this._telemetryService.publicLog2<RemoteConnectionLatencyEvent, RemoteConnectionLatencyClassification>('remoteConnectionLatency', {
|
||||
web: isWeb,
|
||||
remoteName: getRemoteName(this._environmentService.remoteAuthority),
|
||||
latencyMs: measurement.current
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle
|
||||
import { ILabelService, ResourceLabelFormatting } from 'vs/platform/label/common/label';
|
||||
import { OperatingSystem, isWeb, OS } from 'vs/base/common/platform';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IRemoteAgentService, remoteConnectionLatencyMeasurer } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { ILoggerService } from 'vs/platform/log/common/log';
|
||||
import { localize, localize2 } from 'vs/nls';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
@@ -24,8 +24,6 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation
|
||||
import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
|
||||
import { Categories } from 'vs/platform/action/common/actionCommonCategories';
|
||||
import { PersistentConnection } from 'vs/platform/remote/common/remoteAgentConnection';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { getRemoteName } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { IDownloadService } from 'vs/platform/download/common/download';
|
||||
import { DownloadServiceChannel } from 'vs/platform/download/common/downloadIpc';
|
||||
import { RemoteLoggerChannelClient } from 'vs/platform/log/common/logIpc';
|
||||
@@ -144,100 +142,10 @@ class RemoteInvalidWorkspaceDetector extends Disposable implements IWorkbenchCon
|
||||
}
|
||||
}
|
||||
|
||||
class InitialRemoteConnectionHealthContribution implements IWorkbenchContribution {
|
||||
|
||||
constructor(
|
||||
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
|
||||
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
||||
) {
|
||||
if (this._environmentService.remoteAuthority) {
|
||||
this._checkInitialRemoteConnectionHealth();
|
||||
}
|
||||
}
|
||||
|
||||
private async _checkInitialRemoteConnectionHealth(): Promise<void> {
|
||||
try {
|
||||
await this._remoteAgentService.getRawEnvironment();
|
||||
|
||||
type RemoteConnectionSuccessClassification = {
|
||||
owner: 'alexdima';
|
||||
comment: 'The initial connection succeeded';
|
||||
web: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Is web ui.' };
|
||||
connectionTimeMs: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Time, in ms, until connected'; isMeasurement: true };
|
||||
remoteName: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The name of the resolver.' };
|
||||
};
|
||||
type RemoteConnectionSuccessEvent = {
|
||||
web: boolean;
|
||||
connectionTimeMs: number | undefined;
|
||||
remoteName: string | undefined;
|
||||
};
|
||||
this._telemetryService.publicLog2<RemoteConnectionSuccessEvent, RemoteConnectionSuccessClassification>('remoteConnectionSuccess', {
|
||||
web: isWeb,
|
||||
connectionTimeMs: await this._remoteAgentService.getConnection()?.getInitialConnectionTimeMs(),
|
||||
remoteName: getRemoteName(this._environmentService.remoteAuthority)
|
||||
});
|
||||
|
||||
await this._measureExtHostLatency();
|
||||
|
||||
} catch (err) {
|
||||
|
||||
type RemoteConnectionFailureClassification = {
|
||||
owner: 'alexdima';
|
||||
comment: 'The initial connection failed';
|
||||
web: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Is web ui.' };
|
||||
remoteName: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The name of the resolver.' };
|
||||
connectionTimeMs: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Time, in ms, until connection failure'; isMeasurement: true };
|
||||
message: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Error message' };
|
||||
};
|
||||
type RemoteConnectionFailureEvent = {
|
||||
web: boolean;
|
||||
remoteName: string | undefined;
|
||||
connectionTimeMs: number | undefined;
|
||||
message: string;
|
||||
};
|
||||
this._telemetryService.publicLog2<RemoteConnectionFailureEvent, RemoteConnectionFailureClassification>('remoteConnectionFailure', {
|
||||
web: isWeb,
|
||||
connectionTimeMs: await this._remoteAgentService.getConnection()?.getInitialConnectionTimeMs(),
|
||||
remoteName: getRemoteName(this._environmentService.remoteAuthority),
|
||||
message: err ? err.message : ''
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private async _measureExtHostLatency() {
|
||||
const measurement = await remoteConnectionLatencyMeasurer.measure(this._remoteAgentService);
|
||||
if (measurement === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
type RemoteConnectionLatencyClassification = {
|
||||
owner: 'connor4312';
|
||||
comment: 'The latency to the remote extension host';
|
||||
web: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Whether this is running on web' };
|
||||
remoteName: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Anonymized remote name' };
|
||||
latencyMs: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Latency to the remote, in milliseconds'; isMeasurement: true };
|
||||
};
|
||||
type RemoteConnectionLatencyEvent = {
|
||||
web: boolean;
|
||||
remoteName: string | undefined;
|
||||
latencyMs: number;
|
||||
};
|
||||
|
||||
this._telemetryService.publicLog2<RemoteConnectionLatencyEvent, RemoteConnectionLatencyClassification>('remoteConnectionLatency', {
|
||||
web: isWeb,
|
||||
remoteName: getRemoteName(this._environmentService.remoteAuthority),
|
||||
latencyMs: measurement.current
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
|
||||
registerWorkbenchContribution2(LabelContribution.ID, LabelContribution, WorkbenchPhase.BlockStartup);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(RemoteChannelsContribution, LifecyclePhase.Restored);
|
||||
registerWorkbenchContribution2(RemoteInvalidWorkspaceDetector.ID, RemoteInvalidWorkspaceDetector, WorkbenchPhase.BlockStartup);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(InitialRemoteConnectionHealthContribution, LifecyclePhase.Restored);
|
||||
|
||||
const enableDiagnostics = true;
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface IBannerItem {
|
||||
readonly actions?: ILinkDescriptor[];
|
||||
readonly ariaLabel?: string;
|
||||
readonly onClose?: () => void;
|
||||
readonly disableCloseAction?: boolean;
|
||||
}
|
||||
|
||||
export const IBannerService = createDecorator<IBannerService>('bannerService');
|
||||
|
||||
@@ -43,6 +43,7 @@ export interface IRemoteAgentEnvironmentDTO {
|
||||
all: UriDto<IUserDataProfile[]>;
|
||||
home: UriComponents;
|
||||
};
|
||||
isUnsupportedGlibc: boolean;
|
||||
}
|
||||
|
||||
export class RemoteExtensionEnvironmentChannelClient {
|
||||
@@ -70,7 +71,8 @@ export class RemoteExtensionEnvironmentChannelClient {
|
||||
arch: data.arch,
|
||||
marks: data.marks,
|
||||
useHostProxy: data.useHostProxy,
|
||||
profiles: revive(data.profiles)
|
||||
profiles: revive(data.profiles),
|
||||
isUnsupportedGlibc: data.isUnsupportedGlibc
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user