Skip to content

Instantly share code, notes, and snippets.

@phryneas
Forked from ottidmes/default.nix
Created December 12, 2018 11:36
Show Gist options
  • Save phryneas/698996d24748d3ce7f61ef818949a28e to your computer and use it in GitHub Desktop.
Save phryneas/698996d24748d3ce7f61ef818949a28e to your computer and use it in GitHub Desktop.
VS Code Live Share extension
# Baseed on previous attempts of others: https://github.com/NixOS/nixpkgs/issues/41189
{ lib, vscode-utils, autoPatchelfHook, bash, file, makeWrapper, dotnet-sdk
, curl, gcc, icu, libkrb5, libsecret, libunwind, libX11, lttng-ust, openssl, utillinux, zlib
, enableDiagnosticsWorkaround ? false, gccStdenv
}:
with lib;
let
# https://docs.microsoft.com/en-us/visualstudio/liveshare/reference/linux#install-prerequisites-manually
libs = [
# .NET Core
openssl
libkrb5
zlib
icu
# Credential Storage
libsecret
# NodeJS
libX11
# https://github.com/flathub/com.visualstudio.code.oss/issues/11#issuecomment-392709170
libunwind
lttng-ust
curl
# General
gcc.cc.lib
utillinux # libuuid
];
vscode-utils' = if enableDiagnosticsWorkaround
then vscode-utils.override { stdenv = gccStdenv; }
else vscode-utils;
in (vscode-utils'.buildVscodeMarketplaceExtension {
mktplcRef = {
name = "vsliveshare";
publisher = "ms-vsliveshare";
version = "0.3.1013";
sha256 = "1a730mpv2jz6ksqrbx13ph33109plx234jrdf5qy2dm2w5cbhvm5";
};
}).overrideAttrs(attrs: {
patches = [ ./extension.js.patch ];
buildInputs = attrs.buildInputs ++ libs ++ [ autoPatchelfHook bash file makeWrapper ];
installPhase = attrs.installPhase + ''
runHook postInstall
'';
postInstall = ''
bash -s <<ENDSUBSHELL
shopt -s extglob
cd $out/share/vscode/extensions/ms-vsliveshare.vsliveshare
# A workaround to prevent the journal filling up due to diagnostic logging.
${optionalString enableDiagnosticsWorkaround ''
gcc -fPIC -shared -ldl -o dotnet_modules/noop-syslog.so ${./noop-syslog.c}
''}
# Normally the copying of the right executables and libraries is done externally at a later time,
# but we want it done at installation time.
# FIXME: Surely there is a better way than copying over the shared .NET libraries.
cp \
${dotnet-sdk}/shared/Microsoft.NETCore.App/*/* \
dotnet_modules/runtimes/linux-x64/!(native) \
dotnet_modules/runtimes/linux-x64/native/* \
dotnet_modules/runtimes/unix/lib/netstandard1.3/* \
dotnet_modules
# Those we need are already copied over, the rest is just a waste of space.
rm -r dotnet_modules/runtimes
# Not all executables and libraries are executable, so make sure that they are.
find . -type f ! -executable -exec file {} + | grep -w ELF | cut -d ':' -f1 | tr '\n' '\0' | xargs -0r -n1 chmod +x
# Not all scripts are executed by passing them to a shell, so they need to be executable as well.
find . -type f -name '*.sh' ! -executable -exec chmod +x {} +
# Lock the extension downloader.
touch install-linux.Lock externalDeps-linux.Lock
ENDSUBSHELL
'';
rpath = makeLibraryPath libs;
postFixup = ''
root=$out/share/vscode/extensions/ms-vsliveshare.vsliveshare
# We cannot use `wrapProgram`, because it will generate a relative path,
# which breaks our workaround that makes the extension directory writable.
mv $root/dotnet_modules/vsls-agent{,-wrapped}
makeWrapper $root/dotnet_modules/vsls-agent{-wrapped,} \
--prefix LD_LIBRARY_PATH : "$rpath" ${optionalString enableDiagnosticsWorkaround ''\
--set LD_PRELOAD "$root/dotnet_modules/noop-syslog.so"
''}
'';
meta = {
description = "Live Share lets you achieve greater confidence at speed by streamlining collaborative editing, debugging, and more in real-time during development";
homepage = https://aka.ms/vsls-docs;
license = licenses.unfree;
maintainers = [ maintainers.msteen ];
platforms = [ "x86_64-linux" ];
};
})
--- a/out/src/extension.js
+++ b/out/src/extension.js
@@ -268,18 +268,7 @@
activationEvent.markTime(telemetryStrings_1.TelemetryPropertyNames.EXTENSION_ACTIVATION_COMPAT_CHECK_COMPLETE);
await extensionUtil_1.ExtensionUtil.InitAsync(context);
const liveShareExtension = vscode.extensions.getExtension('ms-vsliveshare.vsliveshare');
- const installationResult = await downloader_1.ExternalDownloader.ensureRuntimeDependenciesAsync(liveShareExtension);
- const isExtensionUpdated = abTestsUtil_1.isExtensionBeingUpdated();
- activationEvent.addProperty(telemetryStrings_1.TelemetryPropertyNames.IS_EXTENSION_BEING_UPDATED, isExtensionUpdated);
- const isExtensionPackCandidatePresent = extensionUtil_1.ExtensionUtil.isExtensionPackCandidatePresent();
- activationEvent.addProperty(telemetryStrings_1.TelemetryPropertyNames.IS_EXTENSION_PACK_CANDIDATE_PRESENT, isExtensionPackCandidatePresent);
- // failed to install dependencies
- if (installationResult === downloader_1.EnsureRuntimeDependenciesResult.Failure) {
- activationEvent.end(telemetry_1.TelemetryResult.UserFailure, 'Extension activation failed - download runtime dependencies.');
- vscode.window.showErrorMessage(`${config.get(config.Key.name)} was unable to download needed dependencies to finish installation. Ensure you have network connectivity and restart VS Code to retry.`);
- return;
- }
- else if ((installationResult === downloader_1.EnsureRuntimeDependenciesResult.Success) && !isExtensionUpdated && isFirstActivation) {
+ if (isFirstActivation) {
// Show the welcome notification on the first installation
const isSelected = (Math.random() <= 0.5);
activationEvent.addProperty(telemetryStrings_1.TelemetryPropertyNames.INITIAL_INSTALL_INFO_PAGE_EXPERIMENT_OPEN_BY_DEFAULT, isSelected);
@@ -287,7 +276,6 @@
const didShow = (isSelected && !isExtensionPackCandidatePresent) ? await welcomePageUtil_1.showWelcomePage(welcomePageUtil_1.WelcomePageSource.Install) : await welcomePageUtil_1.showWelcomeNotification();
activationEvent.addProperty(telemetryStrings_1.TelemetryPropertyNames.SHOWED_INITIAL_INSTALL_INFO_PAGE, didShow);
}
- await extensionUtil_1.ExtensionUtil.updateExecutablePermissionsAsync();
await launcher_1.Launcher.setup(false, !(await downloader_1.installFileExistsAsync()));
activationEvent.markTime(telemetryStrings_1.TelemetryPropertyNames.EXTENSION_ACTIVATION_LAUNCHER_SETUP_COMPLETE);
const rpcClient = new service_1.RPCClient();
@@ -427,15 +415,7 @@
activationAgentEvent.end(telemetry_1.TelemetryResult.Success, 'Agent activation success.');
}
catch (e) {
- let errorMessage;
- if (await downloader_1.isInstallCorrupt(traceSource_1.traceSource)) {
- errorMessage = 'An update or installation of VS Live Share failed due to a corrupted download. ' +
- 'Please uninstall and reinstall the extension to resolve. ' +
- 'See https://aka.ms/vsls-corrupted-install for more details.';
- }
- else {
- errorMessage = e.message;
- }
+ let errorMessage = e.message;
const telemetryMessage = 'Agent activation failed. ' + errorMessage;
activationAgentEvent.end(telemetry_1.TelemetryResult.Failure, telemetryMessage);
extensionTelemetry_1.ExtensionTelemetry.sendActivateAgentAsyncFault(telemetry_1.FaultType.Error, telemetryMessage, e, activationAgentEvent);
void syslog(int priority, const char *format, ...) { }
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.vsliveshare;
pkg = pkgs.vsliveshare.override { enableDiagnosticsWorkaround = cfg.enableDiagnosticsWorkaround; };
writableWorkaroundScript = pkgs.writeScript "vsliveshare-writable-workaround.sh" ''
#!${pkgs.bash}/bin/bash
out=${pkg}
src=$out/share/vscode/extensions/ms-vsliveshare.vsliveshare
# We do not want to pass any invalid path to `rm`.
if [[ ! -d "${cfg.extensionsDir}" ]]; then
echo "The VS Code extensions directory '${cfg.extensionsDir}' does not exist" >&2
exit 1
fi
dst="${cfg.extensionsDir}"/ms-vsliveshare.vsliveshare-$(basename $out | sed 's/.*vsliveshare-//')
# Only run the script when the build has actually changed.
if [[ $(dirname "$(dirname "$(readlink "''${dst}/dotnet_modules/vsls-agent-wrapped")")") == $src ]]; then
exit 0
fi
# Remove all previous versions of VS Code Live Share.
rm -r "${cfg.extensionsDir}"/ms-vsliveshare.vsliveshare*
# Create the extension directory.
mkdir -p "$dst"
# Symlink files which should remain unchanged.
find $src -type f \( -name \*.a -o -name \*.dll -o -name \*.pdb \) | while read -r src_file; do
dst_file="''${dst}''${src_file#''${src}}"
mkdir -p $(dirname "$dst_file")
ln -s "$src_file" "$dst_file"
done
# Symlink ELF executables and copy over executable files.
find $src -type f -executable | while read -r src_file; do
dst_file="''${dst}''${src_file#''${src}}"
mkdir -p $(dirname "$dst_file")
if file "$src_file" | grep -wq ELF; then
ln -s "$src_file" "$dst_file"
else
cp --no-preserve=mode,ownership,timestamps "$src_file" "$dst_file"
chmod +x "$dst_file"
fi
done
# Copy over the remaining files and directories.
# FIXME: Use a different command that does not warn about files being the same.
cp -r --no-clobber --no-preserve=mode,ownership,timestamps "$src/." "$dst" 2> >(grep -Ev "^cp: '.*' and '.*' are the same file$")
exit 0
'';
in {
options.services.vsliveshare = with types; {
enable = mkEnableOption "VS Code Live Share extension";
enableWritableWorkaround = mkEnableOption "copying the build to the VS Code extension directory to ensure write access";
enableDiagnosticsWorkaround = mkEnableOption "an UNIX socket that filters out the diagnostic logging done by VSLS Agent";
extensionsDir = mkOption {
type = str;
default = "$HOME/.vscode/extensions";
description = ''
The VS Code extensions directory.
CAUTION: The workaround will remove ms-vsliveshare.vsliveshare* inside this directory!
'';
};
};
config = mkIf cfg.enable {
environment.systemPackages = with pkgs; [ bash desktop-file-utils xlibs.xprop ]
++ optional (!cfg.enableWritableWorkaround) pkg;
services.gnome3.gnome-keyring.enable = true;
systemd.user.services.vsliveshare-writable-workaround = mkIf cfg.enableWritableWorkaround {
description = "VS Code Live Share extension writable workaround";
path = with pkgs; [ file ];
unitConfig = {
ConditionGroup = "users";
};
serviceConfig = {
ExecStart = writableWorkaroundScript;
};
wantedBy = [ "default.target" ];
restartTriggers = [ writableWorkaroundScript ];
};
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment