1{
2 lib,
3 stdenv,
4 fetchFromGitHub,
5 buildGoModule,
6 makeWrapper,
7 cacert,
8 moreutils,
9 jq,
10 git,
11 pkg-config,
12 yarn,
13 python3,
14 esbuild,
15 nodejs,
16 node-gyp,
17 libsecret,
18 libkrb5,
19 xorg,
20 ripgrep,
21 cctools,
22 nixosTests,
23}:
24
25let
26 system = stdenv.hostPlatform.system;
27
28 yarn' = yarn.override { inherit nodejs; };
29 defaultYarnOpts = [
30 "frozen-lockfile"
31 "non-interactive"
32 "no-progress"
33 ];
34
35 vsBuildTarget =
36 {
37 x86_64-linux = "linux-x64";
38 aarch64-linux = "linux-arm64";
39 x86_64-darwin = "darwin-x64";
40 aarch64-darwin = "darwin-arm64";
41 }
42 .${system} or (throw "Unsupported system ${system}");
43
44 esbuild' = esbuild.override {
45 buildGoModule =
46 args:
47 buildGoModule (
48 args
49 // rec {
50 version = "0.17.14";
51 src = fetchFromGitHub {
52 owner = "evanw";
53 repo = "esbuild";
54 rev = "v${version}";
55 hash = "sha256-4TC1d5FOZHUMuEMTcTOBLZZM+sFUswhyblI5HVWyvPA=";
56 };
57 vendorHash = "sha256-+BfxCyg0KkDQpHt/wycy/8CTG6YBA/VJvJFhhzUnSiQ=";
58 }
59 );
60 };
61
62 # replaces esbuild's download script with a binary from nixpkgs
63 patchEsbuild = path: version: ''
64 mkdir -p ${path}/node_modules/esbuild/bin
65 jq "del(.scripts.postinstall)" ${path}/node_modules/esbuild/package.json | sponge ${path}/node_modules/esbuild/package.json
66 sed -i 's/${version}/${esbuild'.version}/g' ${path}/node_modules/esbuild/lib/main.js
67 ln -s -f ${esbuild'}/bin/esbuild ${path}/node_modules/esbuild/bin/esbuild
68 '';
69in
70stdenv.mkDerivation (finalAttrs: {
71 pname = "openvscode-server";
72 version = "1.88.1";
73
74 src = fetchFromGitHub {
75 owner = "gitpod-io";
76 repo = "openvscode-server";
77 rev = "openvscode-server-v${finalAttrs.version}";
78 hash = "sha256-Yc16L13Z8AmsGoSFbvy+4+KBdHxvqLMwZLeU2/dAQVU=";
79 };
80
81 yarnCache = stdenv.mkDerivation {
82 name = "${finalAttrs.pname}-${finalAttrs.version}-${system}-yarn-cache";
83 inherit (finalAttrs) src;
84 nativeBuildInputs = [
85 cacert
86 yarn'
87 git
88 ];
89 buildPhase = ''
90 export HOME=$PWD
91
92 yarn config set yarn-offline-mirror $out
93 find "$PWD" -name "yarn.lock" -printf "%h\n" | \
94 xargs -I {} yarn --cwd {} \
95 --frozen-lockfile --ignore-scripts --ignore-platform \
96 --ignore-engines --no-progress --non-interactive
97 '';
98
99 installPhase = ''
100 echo yarnCache
101 '';
102
103 outputHashMode = "recursive";
104 outputHashAlgo = "sha256";
105 outputHash = "sha256-89c6GYLT2RzHqwxBKegYqB6g5rEJ6/nH53cnfV7b0Ts=";
106 };
107
108 nativeBuildInputs = [
109 nodejs
110 yarn'
111 python3
112 pkg-config
113 makeWrapper
114 git
115 jq
116 moreutils
117 ];
118
119 buildInputs =
120 lib.optionals (!stdenv.hostPlatform.isDarwin) [ libsecret ]
121 ++ (with xorg; [
122 libX11
123 libxkbfile
124 libkrb5
125 ])
126 ++ lib.optionals stdenv.hostPlatform.isDarwin [
127 cctools
128 ];
129
130 patches = [
131 # Patch out remote download of nodejs from build script
132 ./remove-node-download.patch
133 ];
134
135 # Disable NAPI_EXPERIMENTAL to allow to build with Node.js≥18.20.0.
136 env.NIX_CFLAGS_COMPILE = "-DNODE_API_EXPERIMENTAL_NOGC_ENV_OPT_OUT";
137
138 postPatch = ''
139 export HOME=$PWD
140
141 # remove all built-in extensions, as these are 3rd party extensions that
142 # get downloaded from vscode marketplace
143 jq --slurp '.[0] * .[1]' "product.json" <(
144 cat << EOF
145 {
146 "builtInExtensions": []
147 }
148 EOF
149 ) | sponge product.json
150 '';
151
152 configurePhase = ''
153 runHook preConfigure
154
155 # set default yarn opts
156 ${lib.concatMapStrings (option: ''
157 yarn --offline config set ${option}
158 '') defaultYarnOpts}
159
160 # set offline mirror to yarn cache we created in previous steps
161 yarn --offline config set yarn-offline-mirror "${finalAttrs.yarnCache}"
162
163 # set nodedir to prevent node-gyp from downloading headers
164 # taken from https://nixos.org/manual/nixpkgs/stable/#javascript-tool-specific
165 mkdir -p $HOME/.node-gyp/${nodejs.version}
166 echo 9 > $HOME/.node-gyp/${nodejs.version}/installVersion
167 ln -sfv ${nodejs}/include $HOME/.node-gyp/${nodejs.version}
168 export npm_config_nodedir=${nodejs}
169
170 # use updated node-gyp. fixes the following error on Darwin:
171 # PermissionError: [Errno 1] Operation not permitted: '/usr/sbin/pkgutil'
172 export npm_config_node_gyp=${node-gyp}/lib/node_modules/node-gyp/bin/node-gyp.js
173
174 runHook postConfigure
175 '';
176
177 buildPhase =
178 ''
179 runHook preBuild
180
181 # install dependencies
182 yarn --offline --ignore-scripts
183
184 # run yarn install everywhere, skipping postinstall so we can patch esbuild
185 find . -path "*node_modules" -prune -o \
186 -path "./*/*" -name "yarn.lock" -printf "%h\n" | \
187 xargs -I {} yarn --cwd {} \
188 --frozen-lockfile --offline --ignore-scripts --ignore-engines
189
190 ${patchEsbuild "./build" "0.12.6"}
191 ${patchEsbuild "./extensions" "0.11.23"}
192
193 # patch shebangs of node_modules to allow binary packages to build
194 patchShebangs ./remote/node_modules
195
196 # put ripgrep binary into bin so postinstall does not try to download it
197 find -path "*@vscode/ripgrep" -type d \
198 -execdir mkdir -p {}/bin \; \
199 -execdir ln -s ${ripgrep}/bin/rg {}/bin/rg \;
200 ''
201 + lib.optionalString stdenv.hostPlatform.isDarwin ''
202 # use prebuilt binary for @parcel/watcher, which requires macOS SDK 10.13+
203 # (see issue #101229)
204 pushd ./remote/node_modules/@parcel/watcher
205 mkdir -p ./build/Release
206 mv ./prebuilds/darwin-x64/node.napi.glibc.node ./build/Release/watcher.node
207 jq "del(.scripts) | .gypfile = false" ./package.json | sponge ./package.json
208 popd
209 ''
210 + ''
211 export NODE_OPTIONS=--openssl-legacy-provider
212
213 # rebuild binaries, we use npm here, as yarn does not provide an alternative
214 # that would not attempt to try to reinstall everything and break our
215 # patching attempts
216 npm --prefix ./remote rebuild --build-from-source
217
218 # run postinstall scripts after patching
219 find . -path "*node_modules" -prune -o \
220 -path "./*/*" -name "yarn.lock" -printf "%h\n" | \
221 xargs -I {} sh -c 'jq -e ".scripts.postinstall" {}/package.json >/dev/null && yarn --cwd {} postinstall --frozen-lockfile --offline || true'
222
223 # build and minify
224 yarn --offline gulp vscode-reh-web-${vsBuildTarget}-min
225
226 runHook postBuild
227 '';
228
229 installPhase = ''
230 runHook preInstall
231
232 mkdir -p $out
233 cp -R -T ../vscode-reh-web-${vsBuildTarget} $out
234 ln -s ${nodejs}/bin/node $out
235
236 runHook postInstall
237 '';
238
239 passthru.tests = {
240 inherit (nixosTests) openvscode-server;
241 };
242
243 meta = {
244 description = "Run VS Code on a remote machine";
245 longDescription = ''
246 Run upstream VS Code on a remote machine with access through a modern web
247 browser from any device, anywhere.
248 '';
249 homepage = "https://github.com/gitpod-io/openvscode-server";
250 license = lib.licenses.mit;
251 maintainers = with lib.maintainers; [
252 dguenther
253 ghuntley
254 emilytrau
255 ];
256 platforms = [
257 "x86_64-linux"
258 "aarch64-linux"
259 "x86_64-darwin"
260 "aarch64-darwin"
261 ];
262 # Depends on nodejs_18 that has been removed.
263 broken = true;
264 mainProgram = "openvscode-server";
265 };
266})