./notes/

VSCode server из ничего

Собираем опен-сорс версию code-server.

2023.01.23

Собираем VScode-server из исходников

Маленькое овервью, как собрать code-server из исходников.

Что?

Code Server — разработка от Microsoft, позволяющая запускать VScode в “клиент-серверном режиме”: бэкенд работает где угодно, фронтенд доступен из браузера. Пример можно потыкать на vscode.dev или на github, нажав кнопку ..

Зачем?

Как сказано в документации, Microsoft поставляет свою сборку code-server’а, которую можно скачать и использовать почти в любых целях, не нарушая лицензию. Но у MS-сборки есть фатальный недостаток: при запуске она лезет в сеть, чтобы проверить обновления, и отказывается без этого запускаться.

Если собирать code-server из исходников, в нём не будет этого механизма апдейтов, и он сможет работать в любом офлайн-окружении. Ещё не будет многих фич, не выложенных в опен-сорс — маркета расширений, коннекта к github, синхронизации настроек и прочего.

Какие ещё варианты?

Я не первый, кто придумал собирать code-server из исхдодников. Есть как минимум 3 популярных форка, занимающихся тем же самым:

Все форки не совсем чистые, и патчат vscode для своих нужд, чего мне не очень хотелось.

Собираем

Пример репо с пайплайном сборки vscode - kotborealis/code-server-oss.

Большинство этапов сборки описаны в документации VSCode. Но, есть пара проблем:

Зависимости

Конкретно, в air-gapped окружении не устанавливается зависимость @vscode/ripgrep, которая выкачивает себе бинарники с github и не умеет работать с прокси (microsoft/vscode-ripgrep/issues/26).

Временно выпилим @vscode/ripgrep из зависимостей сборки, прихватив с собой утилиты для телеметрии, бесполезные в OSS-сборке:

# Remove telemetry libs
sed -i -e 's#"@vscode/telemetry-extractor": "^1.9.8",##g' package.json

# Remove ripgrep, which, for SOME reason,
# cannot be installed in this case due to proxies.
sed -i -e 's#"@vscode/ripgrep": "^1.14.2",##g' package.json

Затем, перепишем .yarnrc-файлы, в которых VSCode хранит информацию о таргет-платформе. По умолчанию, там прописан Electron версии 19:

disturl "https://electronjs.org/headers"
target "19.1.8"
runtime "electron"
build_from_source "true"

Эти параметры используются для сборки нативных зависимостей, например spdlog, и версия для Электрона не запустится на NodeJS. Electron 19.1.8 использует специфичную для себя версию ABI (NODE_MODULE VERSION 106), которая отсутствует в официальной табличке релизов ноды — значения между 102 и 108 там пропущены.

Перегенерируем параметры платформы скриптом и перезапишем все инстансы .yarnrc:

# Set proper node version in yarnrc
node build/npm/setupBuildYarnrc
cp ./build/.yarnrc ./.yarnrc
cp ./build/.yarnrc ./remote/.yarnrc

Затем мы наконец-то можем поставить все зависимости и вернуть @vscode/rigprep на место — по странным причинам, при установке отдельно он отлично работает с проксями:

# Install node_modules
yarn $@

# Install ripgrep.
# Now it works, no one knows exactly why.
yarn add @vscode/ripgrep

Так же до установки зависимостей полезно выставить флажки, запрещающие выкачивать бинарники Electron’а и Playwright’а — для code-server’а они не потребуются:

export ELECTRON_SKIP_BINARY_DOWNLOAD=1
export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1

product.json

В сборках vscode используется магический файл product.json, в котором описывается “вкус” собираемой версии — иконка, имя, расширения по умолчанию. Ничего трогать не будем, только обрежем список расширений до нуля, которые выкачиваются с github’а, занимают много времени и не всегда нужны:

# Prevent builtin extensions from downloading
cat product.json
node -e 'console.log(JSON.stringify({...require("./product.json"), builtInExtensions: []}))' > product.json.tmp
mv product.json.tmp product.json

Релиз-сборка

Девелоперская версия code-server’а собирается скриптом build/lib/preLaunch.js, который так же собирает электрон, и выкачивает встроенные расширения. Для своих нужд обрежем его до минимума:

cat build/lib/preLaunch.js \
| grep -v "await getElectron();" \
| grep -v "await getBuiltInExtensions();" \
> build/lib/preLaunch.server.js

Но, девелоперская сборка занимает много места и работает очень медленно — первая загрузка страницы занимает примерно в 10 раз больше времени.

Релизный, минифицированный билд собирается отдельной командой, которая явно не упомянута в документации, но её можно откопать, например, в скриптах пайплайнов:

yarn gulp vscode-reh-web-linux-x64-min

Сборка дистрибутива

Остаётся вытащить дистрибутив из сборочной директории:

mkdir /code-server-oss && cd /code-server-oss

mv /vscode/.build ./
mv /vscode/extensions ./
mv /vscode/node_modules ./
mv /vscode/out-vscode-reh-web-min ./out
mv /vscode/product.json ./
mv /vscode/package.json ./

Из билда нам понадобились зависимости, собранные файлы, и .json-манифесты.

Добавим скрипт запуска, который раскрутит code-server в подхдящем окружении:

# Get project root di

ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
export NODE=$(find .build/ -name 'node' -type f -executable)

code_server () {
$NODE $ROOT/out/server-main.js $@
}

echo "Starting server with args: $@"

code_server $@

VScode во время сборки выкачивает для себя нужную версию NodeJS в директорию .build, и мы будем ей пользоваться, так как под неё собраны нативные аддоны.

Затем code-server можно запустить примерно следующей командой:

$ /code-server-oss/entrypoint.sh --host 0.0.0.0 --port 8080 --without-connection-token

Итоги

В итоге, получили релизную OSS-сборку code-server’а из исходников, не требующую доступа в интернет при запуске, не тащущую за собой лишние расширения.

Финальный результат лежит в kotborealis/code-server-oss. Так же есть почти ежедневные сборки docker-образа. “Почти” ежедневные потому что иногда проваливается скачивание @vscode/ripgrep из-за рейтлимитов github’a, и на это напарывается таже microsoft - см. скрипты пайплайнов.