1{ lib, stdenv, fetchFromGitHub, buildGoModule, makeWrapper
2, cacert, moreutils, jq, git, rsync, pkg-config, yarn, python3
3, esbuild, nodejs_16, node-gyp, libsecret, xorg, ripgrep
4, AppKit, Cocoa, CoreServices, Security, cctools, xcbuild, quilt, nixosTests }:
5
6let
7 system = stdenv.hostPlatform.system;
8
9 nodejs = nodejs_16;
10 python = python3;
11 yarn' = yarn.override { inherit nodejs; };
12 defaultYarnOpts = [ ];
13
14 esbuild' = esbuild.override {
15 buildGoModule = args: buildGoModule (args // rec {
16 version = "0.16.17";
17 src = fetchFromGitHub {
18 owner = "evanw";
19 repo = "esbuild";
20 rev = "v${version}";
21 hash = "sha256-8L8h0FaexNsb3Mj6/ohA37nYLFogo5wXkAhGztGUUsQ=";
22 };
23 vendorHash = "sha256-+BfxCyg0KkDQpHt/wycy/8CTG6YBA/VJvJFhhzUnSiQ=";
24 });
25 };
26
27 # replaces esbuild's download script with a binary from nixpkgs
28 patchEsbuild = path : version : ''
29 mkdir -p ${path}/node_modules/esbuild/bin
30 jq "del(.scripts.postinstall)" ${path}/node_modules/esbuild/package.json | sponge ${path}/node_modules/esbuild/package.json
31 sed -i 's/${version}/${esbuild'.version}/g' ${path}/node_modules/esbuild/lib/main.js
32 ln -s -f ${esbuild'}/bin/esbuild ${path}/node_modules/esbuild/bin/esbuild
33 '';
34
35in stdenv.mkDerivation rec {
36 pname = "code-server";
37 version = "4.12.0";
38
39 src = fetchFromGitHub {
40 owner = "coder";
41 repo = "code-server";
42 rev = "v${version}";
43 fetchSubmodules = true;
44 hash = "sha256-PQp5dji2Ynp+LJRWBka41umwe1/IR76C+at/wyOWGcI=";
45 };
46
47 cloudAgent = buildGoModule rec {
48 pname = "cloud-agent";
49 version = "0.2.6";
50
51 src = fetchFromGitHub {
52 owner = "coder";
53 repo = "cloud-agent";
54 rev = "v${version}";
55 sha256 = "1s3jpgvzizc9skc27c3x35sya2p4ywhvdi3l73927z3j47wszy7f";
56 };
57
58 vendorSha256 = "14xzlbmki8fk8mbcci62q8sklyd0nyga07ww1ap0vdrv7d1g31hn";
59
60 postPatch = ''
61 # the cloud-agent release tag has an empty version string, so add it back in
62 substituteInPlace internal/version/version.go \
63 --replace 'var Version string' 'var Version string = "v${version}"'
64 '';
65 };
66
67 yarnCache = stdenv.mkDerivation {
68 name = "${pname}-${version}-${system}-yarn-cache";
69 inherit src;
70 nativeBuildInputs = [ yarn' git cacert ];
71 buildPhase = ''
72 export HOME=$PWD
73 export GIT_SSL_CAINFO="${cacert}/etc/ssl/certs/ca-bundle.crt"
74
75 yarn --cwd "./vendor" install --modules-folder modules --ignore-scripts --frozen-lockfile
76
77 yarn config set yarn-offline-mirror $out
78 find "$PWD" -name "yarn.lock" -printf "%h\n" | \
79 xargs -I {} yarn --cwd {} \
80 --frozen-lockfile --ignore-scripts --ignore-platform \
81 --ignore-engines --no-progress --non-interactive
82
83 find ./lib/vscode -name "yarn.lock" -printf "%h\n" | \
84 xargs -I {} yarn --cwd {} \
85 --ignore-scripts --ignore-engines
86 '';
87 outputHashMode = "recursive";
88 outputHashAlgo = "sha256";
89
90 # to get hash values use nix-build -A code-server.prefetchYarnCache
91 outputHash = "sha256-4Vr9u3+W/IhbbTc39jyDyDNQODlmdF+M/N8oJn0Z4+w=";
92 };
93
94 nativeBuildInputs = [
95 nodejs yarn' python pkg-config makeWrapper git rsync jq moreutils quilt
96 ];
97 buildInputs = lib.optionals (!stdenv.isDarwin) [ libsecret ]
98 ++ (with xorg; [ libX11 libxkbfile ])
99 ++ lib.optionals stdenv.isDarwin [
100 AppKit Cocoa CoreServices Security cctools xcbuild
101 ];
102
103 patches = [
104 # remove git calls from vscode build script
105 ./build-vscode-nogit.patch
106 ];
107
108 postPatch = ''
109 export HOME=$PWD
110
111 patchShebangs ./ci
112
113 # inject git commit
114 substituteInPlace ci/build/build-release.sh \
115 --replace '$(git rev-parse HEAD)' "$commit"
116 '';
117
118 configurePhase = ''
119 # run yarn offline by default
120 echo '--install.offline true' >> .yarnrc
121
122 # set default yarn opts
123 ${lib.concatMapStrings (option: ''
124 yarn --offline config set ${option}
125 '') defaultYarnOpts}
126
127 # set offline mirror to yarn cache we created in previous steps
128 yarn --offline config set yarn-offline-mirror "${yarnCache}"
129
130 # skip unnecessary electron download
131 export ELECTRON_SKIP_BINARY_DOWNLOAD=1
132
133 # set nodedir to prevent node-gyp from downloading headers
134 # taken from https://nixos.org/manual/nixpkgs/stable/#javascript-tool-specific
135 mkdir -p $HOME/.node-gyp/${nodejs.version}
136 echo 9 > $HOME/.node-gyp/${nodejs.version}/installVersion
137 ln -sfv ${nodejs}/include $HOME/.node-gyp/${nodejs.version}
138 export npm_config_nodedir=${nodejs}
139
140 # use updated node-gyp. fixes the following error on Darwin:
141 # PermissionError: [Errno 1] Operation not permitted: '/usr/sbin/pkgutil'
142 export npm_config_node_gyp=${node-gyp}/lib/node_modules/node-gyp/bin/node-gyp.js
143 '';
144
145 buildPhase = ''
146 # install code-server dependencies
147 yarn --offline --ignore-scripts
148
149 # apply patches
150 quilt push -a
151
152 # patch shebangs of everything to allow binary packages to build
153 patchShebangs .
154
155 export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
156 export SKIP_SUBMODULE_DEPS=1
157 export NODE_OPTIONS=--openssl-legacy-provider
158
159 # rebuild binary packages now that scripts have been patched
160 echo "----- NPM rebuild"
161 npm rebuild --prefer-offline
162
163 # Replicate ci/dev/postinstall.sh
164 echo "----- Replicate ci/dev/postinstall.sh"
165 yarn --cwd "./vendor" install --modules-folder modules --offline --ignore-scripts --frozen-lockfile
166
167 # remove all built-in extensions, as these are 3rd party extensions that
168 # get downloaded from vscode marketplace
169 jq --slurp '.[0] * .[1]' "./lib/vscode/product.json" <(
170 cat << EOF
171 {
172 "builtInExtensions": []
173 }
174 EOF
175 ) | sponge ./lib/vscode/product.json
176
177 # disable automatic updates
178 sed -i '/update.mode/,/\}/{s/default:.*/default: "none",/g}' \
179 lib/vscode/src/vs/platform/update/common/update.config.contribution.ts
180
181 # Patch out remote download of nodejs from build script
182 patch -p1 -i ${./remove-node-download.patch}
183
184 # Fetch packages for vscode
185 find ./lib/vscode -name "yarn.lock" -printf "%h\n" | \
186 xargs -I {} yarn --cwd {} \
187 --frozen-lockfile --ignore-scripts --ignore-engines
188
189 # patch shebangs of everything to allow binary packages to build
190 patchShebangs .
191
192 ${patchEsbuild "./lib/vscode/build" "0.12.6"}
193 ${patchEsbuild "./lib/vscode/extensions" "0.11.23"}
194 '' + lib.optionalString stdenv.isDarwin ''
195 # use prebuilt binary for @parcel/watcher, which requires macOS SDK 10.13+
196 # (see issue #101229)
197 pushd ./lib/vscode/remote/node_modules/@parcel/watcher
198 mkdir -p ./build/Release
199 mv ./prebuilds/darwin-x64/node.napi.glibc.node ./build/Release/watcher.node
200 jq "del(.scripts) | .gypfile = false" ./package.json | sponge ./package.json
201 popd
202 '' + ''
203
204 # put ripgrep binary into bin, so postinstall does not try to download it
205 find -name ripgrep -type d \
206 -execdir mkdir -p {}/bin \; \
207 -execdir ln -s ${ripgrep}/bin/rg {}/bin/rg \;
208
209 # run postinstall scripts after patching
210 find ./lib/vscode -path "*node_modules" -prune -o \
211 -path "./*/*/*/*/*" -name "yarn.lock" -printf "%h\n" | \
212 xargs -I {} sh -c 'jq -e ".scripts.postinstall" {}/package.json >/dev/null && yarn --cwd {} postinstall --frozen-lockfile --offline || true'
213
214 # build code-server
215 yarn build
216
217 # build vscode
218 VERSION=${version} yarn build:vscode
219
220 # create release
221 yarn release
222 '';
223
224 installPhase = ''
225 mkdir -p $out/libexec/code-server $out/bin
226
227 # copy release to libexec path
228 cp -R -T release "$out/libexec/code-server"
229
230 # install only production dependencies
231 yarn --offline --cwd "$out/libexec/code-server" --production
232
233 # link coder-cloud agent from nix store
234 mkdir -p $out/libexec/code-server/lib
235 ln -s "${cloudAgent}/bin/cloud-agent" $out/libexec/code-server/lib/coder-cloud-agent
236
237 # create wrapper
238 makeWrapper "${nodejs_16}/bin/node" "$out/bin/code-server" \
239 --add-flags "$out/libexec/code-server/out/node/entry.js"
240 '';
241
242 passthru = {
243 prefetchYarnCache = lib.overrideDerivation yarnCache (d: {
244 outputHash = lib.fakeSha256;
245 });
246 tests = {
247 inherit (nixosTests) code-server;
248 };
249 };
250
251 meta = with lib; {
252 description = "Run VS Code on a remote server";
253 longDescription = ''
254 code-server is VS Code running on a remote server, accessible through the
255 browser.
256 '';
257 homepage = "https://github.com/coder/code-server";
258 license = licenses.mit;
259 maintainers = with maintainers; [ offline henkery ];
260 platforms = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" ];
261 };
262}