1{ lib, stdenv, fetchFromGitHub, buildGoModule, makeWrapper, runCommand
2, moreutils, jq, git, zip, rsync, pkg-config, yarn, python2
3, nodejs-12_x, libsecret, xorg, ripgrep
4, AppKit, Cocoa, Security, cctools }:
5
6let
7 system = stdenv.hostPlatform.system;
8
9 nodejs = nodejs-12_x;
10 python = python2;
11 yarn' = yarn.override { inherit nodejs; };
12 defaultYarnOpts = [ "frozen-lockfile" "non-interactive" "no-progress"];
13
14in stdenv.mkDerivation rec {
15 pname = "code-server";
16 version = "3.8.0";
17 commit = "c4610f7829701aadb045d450013b84491c30580d";
18
19 src = fetchFromGitHub {
20 owner = "cdr";
21 repo = "code-server";
22 rev = "v${version}";
23 sha256 = "1snc7dbqfz53337h6av2zhkrn54ypanxljs5by4jqczq96c2v6yk";
24 };
25
26 cloudAgent = buildGoModule rec {
27 pname = "cloud-agent";
28 version = "0.2.1";
29
30 src = fetchFromGitHub {
31 owner = "cdr";
32 repo = "cloud-agent";
33 rev = "v${version}";
34 sha256 = "06fpiwxjz2cgzw4ks9sk3376rprkd02khfnb10hg7dhn3y9gp7x8";
35 };
36
37 vendorSha256 = "0k9v10wkzx53r5syf6bmm81gr4s5dalyaa07y9zvx6vv5r2h0661";
38
39 postPatch = ''
40 # the cloud-agent release tag has an empty version string, so add it back in
41 substituteInPlace internal/version/version.go \
42 --replace 'var Version string' 'var Version string = "v${version}"'
43 '';
44 };
45
46 yarnCache = stdenv.mkDerivation {
47 name = "${pname}-${version}-${system}-yarn-cache";
48 inherit src;
49 phases = ["unpackPhase" "buildPhase"];
50 nativeBuildInputs = [ yarn' git ];
51 buildPhase = ''
52 export HOME=$PWD
53
54 yarn config set yarn-offline-mirror $out
55 find "$PWD" -name "yarn.lock" -printf "%h\n" | \
56 xargs -I {} yarn --cwd {} \
57 --frozen-lockfile --ignore-scripts --ignore-platform \
58 --ignore-engines --no-progress --non-interactive
59 '';
60 outputHashMode = "recursive";
61 outputHashAlgo = "sha256";
62
63 # to get hash values use nix-build -A code-server.prefetchYarnCache
64 outputHash = {
65 x86_64-linux = "0xc1yjz53ydg1mwyc2rp4hq20hg6i4aiirfwsnykjw1zm79qgrgb";
66 aarch64-linux = "0xc1yjz53ydg1mwyc2rp4hq20hg6i4aiirfwsnykjw1zm79qgrgb";
67 x86_64-darwin = "0xc1yjz53ydg1mwyc2rp4hq20hg6i4aiirfwsnykjw1zm79qgrgb";
68 }.${system} or (throw "Unsupported system ${system}");
69 };
70
71 # Extract the Node.js source code which is used to compile packages with
72 # native bindings
73 nodeSources = runCommand "node-sources" {} ''
74 tar --no-same-owner --no-same-permissions -xf ${nodejs.src}
75 mv node-* $out
76 '';
77
78 nativeBuildInputs = [
79 nodejs yarn' python pkg-config zip makeWrapper git rsync jq moreutils
80 ];
81 buildInputs = lib.optionals (!stdenv.isDarwin) [ libsecret ]
82 ++ (with xorg; [ libX11 libxkbfile ])
83 ++ lib.optionals stdenv.isDarwin [
84 AppKit Cocoa Security cctools
85 ];
86
87 patches = [
88 # remove download of coder-cloud agent
89 ./remove-cloud-agent-download.patch
90 ];
91
92 postPatch = ''
93 export HOME=$PWD
94
95 patchShebangs ./ci
96
97 # remove unnecessary git config command
98 substituteInPlace lib/vscode/build/npm/postinstall.js \
99 --replace "cp.execSync('git config pull.rebase true');" ""
100
101 # allow offline install for postinstall scripts in extensions
102 grep -rl "yarn install" --include package.json lib/vscode/extensions \
103 | xargs sed -i 's/yarn install/yarn install --offline/g'
104
105 substituteInPlace ci/dev/postinstall.sh \
106 --replace 'yarn' 'yarn --ignore-scripts'
107
108 # use offline cache when installing release packages
109 substituteInPlace ci/build/npm-postinstall.sh \
110 --replace 'yarn --production' 'yarn --production --offline'
111
112 # disable automatic updates
113 sed -i '/update.mode/,/\}/{s/default:.*/default: "none",/g}' \
114 lib/vscode/src/vs/platform/update/common/update.config.contribution.ts
115
116 # inject git commit
117 substituteInPlace ci/build/build-release.sh \
118 --replace '$(git rev-parse HEAD)' "$commit"
119
120 # remove all built-in extensions, as these are 3rd party extensions that
121 # gets downloaded from vscode marketplace
122 jq --slurp '.[0] * .[1]' "lib/vscode/product.json" <(
123 cat << EOF
124 {
125 "builtInExtensions": []
126 }
127 EOF
128 ) | sponge lib/vscode/product.json
129 '';
130
131 configurePhase = ''
132 # run yarn offline by default
133 echo '--install.offline true' >> .yarnrc
134
135 # set default yarn opts
136 ${lib.concatMapStrings (option: ''
137 yarn --offline config set ${option}
138 '') defaultYarnOpts}
139
140 # set offline mirror to yarn cache we created in previous steps
141 yarn --offline config set yarn-offline-mirror "${yarnCache}"
142
143 # link coder-cloud agent from nix store
144 ln -s "${cloudAgent}/bin/cloud-agent" ./lib/coder-cloud-agent
145
146 # skip unnecessary electron download
147 export ELECTRON_SKIP_BINARY_DOWNLOAD=1
148 '' + lib.optionalString stdenv.isLinux ''
149 # set nodedir, so we can build binaries later
150 npm config set nodedir "${nodeSources}"
151 '';
152
153 buildPhase = ''
154 # install code-server dependencies
155 yarn --offline
156
157 # install vscode dependencies without running script for all vscode packages
158 # that require patching for postinstall scripts to succeed
159 for d in lib/vscode lib/vscode/build; do
160 yarn --offline --cwd $d --offline --ignore-scripts
161 done
162
163 # put ripgrep binary into bin, so postinstall does not try to download it
164 find -name vscode-ripgrep -type d \
165 -execdir mkdir -p {}/bin \; \
166 -execdir ln -s ${ripgrep}/bin/rg {}/bin/rg \;
167
168 # patch shebangs of everything, also cached files, as otherwise postinstall
169 # will not be able to find /usr/bin/env, as it does not exist in sandbox
170 patchShebangs .
171
172 # Playwright is only needed for tests, we can disable it for builds.
173 # There's an environment variable to disable downloads, but the package makes a breaking call to
174 # sw_vers before that variable is checked.
175 patch -p1 -i ${./playwright.patch}
176 '' + lib.optionalString stdenv.isDarwin ''
177 # fsevents build fails on Darwin. It's an optional package that's only installed as part of Darwin
178 # builds, so the patch will fail if run on non-Darwin systems.
179 patch -p1 -i ${./darwin-fsevents.patch}
180 '' + ''
181 # rebuild binaries, we use npm here, as yarn does not provide an alternative
182 # that would not attempt to try to reinstall everything and break our
183 # patching attempts
184 npm rebuild --prefix lib/vscode --update-binary
185
186 # run postinstall scripts, which eventually do yarn install on all
187 # additional requirements
188 yarn --cwd lib/vscode postinstall --frozen-lockfile --offline
189
190 # build code-server
191 yarn build
192
193 # build vscode
194 yarn build:vscode
195
196 # create release
197 yarn release
198 '';
199
200 installPhase = ''
201 mkdir -p $out/libexec/code-server $out/bin
202
203 # copy release to libexec path
204 cp -R -T release "$out/libexec/code-server"
205
206 # install only production dependencies
207 yarn --offline --cwd "$out/libexec/code-server" --production
208
209 # link coder-cloud agent from nix store
210 ln -s "${cloudAgent}/bin/cloud-agent" $out/libexec/code-server/lib/coder-cloud-agent
211
212 # create wrapper
213 makeWrapper "${nodejs-12_x}/bin/node" "$out/bin/code-server" \
214 --add-flags "$out/libexec/code-server/out/node/entry.js"
215 '';
216
217 passthru = {
218 prefetchYarnCache = lib.overrideDerivation yarnCache (d: {
219 outputHash = lib.fakeSha256;
220 });
221 };
222
223 meta = with lib; {
224 description = "Run VS Code on a remote server";
225 longDescription = ''
226 code-server is VS Code running on a remote server, accessible through the
227 browser.
228 '';
229 homepage = "https://github.com/cdr/code-server";
230 license = licenses.mit;
231 maintainers = with maintainers; [ offline ];
232 platforms = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" ];
233 };
234}