#!/bin/bash set -euo pipefail function log() { local message="${1}" ; shift local level="${1:-info}" if [[ "${level}" == "error" ]] ; then >&2 echo "${message}" else echo "${message}" fi } function exit-if-ci() { if [[ -n "${ci}" ]] ; then log "Pre-built VS Code ${vscodeVersion}-${target}-${arch} is incorrectly built" "error" exit 1 fi } # Copy code-server into VS Code along with its dependencies. function copy-server() { log "Applying patch" cd "${vscodeSourcePath}" git reset --hard git clean -fd git apply "${rootPath}/scripts/vscode.patch" local serverPath="${vscodeSourcePath}/src/vs/server" rm -rf "${serverPath}" mkdir -p "${serverPath}" log "Copying code-server code" cp -r "${rootPath}/src" "${serverPath}" cp -r "${rootPath}/typings" "${serverPath}" cp "${rootPath}/main.js" "${serverPath}" cp "${rootPath}/package.json" "${serverPath}" cp "${rootPath}/yarn.lock" "${serverPath}" if [[ -d "${rootPath}/node_modules" ]] ; then log "Copying code-server build dependencies" cp -r "${rootPath}/node_modules" "${serverPath}" else log "Installing code-server build dependencies" cd "${serverPath}" # Ignore scripts to avoid also installing VS Code dependencies which has # already been done. yarn --ignore-scripts rm -r node_modules/@types/node # I keep getting type conflicts fi # TODO: Duplicate identifier issue. There must be a better way to fix this. if [[ "${target}" == "darwin" ]] ; then rm "${serverPath}/node_modules/fsevents/node_modules/safe-buffer/index.d.ts" fi } # Prepend the nbin shim which enables finding files within the binary. function prepend-loader() { local filePath="${codeServerBuildPath}/${1}" ; shift cat "${rootPath}/scripts/nbin-shim.js" "${filePath}" > "${filePath}.temp" mv "${filePath}.temp" "${filePath}" # Using : as the delimiter so the escaping here is easier to read. # ${parameter/pattern/string}, so the pattern is /: (if the pattern starts # with / it matches all instances) and the string is \\: (results in \:). if [[ "${target}" == "darwin" ]] ; then sed -i "" -e "s:{{ROOT_PATH}}:${codeServerBuildPath//:/\\:}:g" "${filePath}" else sed -i "s:{{ROOT_PATH}}:${codeServerBuildPath//:/\\:}:g" "${filePath}" fi } # Copy code-server into VS Code then build it. function build-code-server() { copy-server # TODO: look into making it do the full minified build for just our code # (basically just want to skip extensions, target our server code, and get # the same type of build you get with the vscode-linux-x64-min task). # Something like: yarn gulp "vscode-server-${target}-${arch}-min" log "Building code-server" yarn gulp compile-client rm -rf "${codeServerBuildPath}" mkdir -p "${codeServerBuildPath}" local json="{\"codeServerVersion\": \"${codeServerVersion}\"}" cp -r "${vscodeBuildPath}/resources/app/extensions" "${codeServerBuildPath}" node "${rootPath}/scripts/merge.js" "${vscodeBuildPath}/resources/app/package.json" "${rootPath}/scripts/package.json" "${codeServerBuildPath}/package.json" "${json}" node "${rootPath}/scripts/merge.js" "${vscodeBuildPath}/resources/app/product.json" "${rootPath}/scripts/product.json" "${codeServerBuildPath}/product.json" cp -r "${vscodeSourcePath}/out" "${codeServerBuildPath}" rm -rf "${codeServerBuildPath}/out/vs/server/typings" # Rebuild to make sure the native modules work since at the moment all the # pre-built packages are from one Linux system. This means you must build on # the target system. log "Installing remote dependencies" cd "${vscodeSourcePath}/remote" if [[ "${target}" != "linux" ]] ; then yarn --production --force fi cp -r "${vscodeSourcePath}/remote/node_modules" "${codeServerBuildPath}" # Only keep the production dependencies. cd "${codeServerBuildPath}/out/vs/server" yarn --production --ignore-scripts prepend-loader "out/vs/server/main.js" prepend-loader "out/bootstrap-fork.js" log "Final build: ${codeServerBuildPath}" } # Build VS Code if it hasn't already been built. If we're in the CI and it's # not fully built, error and exit. function build-vscode() { if [[ ! -d "${vscodeSourcePath}" ]] ; then exit-if-ci log "${vscodeSourceName} does not exist, cloning" git clone https://github.com/microsoft/vscode --quiet \ --branch "${vscodeVersion}" --single-branch --depth=1 \ "${vscodeSourcePath}" else log "${vscodeSourceName} already exists, skipping clone" fi cd "${vscodeSourcePath}" if [[ ! -d "${vscodeSourcePath}/node_modules" ]] ; then exit-if-ci log "Installing VS Code dependencies" # Not entirely sure why but there seem to be problems with native modules # so rebuild them. yarn --force # Keep just what we need to keep the pre-built archive smaller. rm -rf "${vscodeSourcePath}/test" else log "${vscodeSourceName}/node_modules already exists, skipping install" fi if [[ ! -d "${vscodeBuildPath}" ]] ; then exit-if-ci log "${vscodeBuildName} does not exist, building" local builtPath="${buildPath}/VSCode-${target}-${arch}" rm -rf "${builtPath}" yarn gulp "vscode-${target}-${arch}-min" --max-old-space-size=32384 mkdir -p "${vscodeBuildPath}/resources/app" # Copy just what we need to keep the pre-built archive smaller. mv "${builtPath}/resources/app/extensions" "${vscodeBuildPath}/resources/app" mv "${builtPath}/resources/app/"*.json "${vscodeBuildPath}/resources/app" rm -rf "${builtPath}" else log "${vscodeBuildName} already exists, skipping build" fi } # Download VS Code with either curl or wget depending on which is available. function download-vscode() { cd "${buildPath}" if command -v wget &> /dev/null ; then log "Attempting to download ${tarName} with wget" wget "${vsSourceUrl}" --quiet --output-document "${tarName}" else log "Attempting to download ${tarName} with curl" curl "${vsSourceUrl}" --silent --fail --output "${tarName}" fi } # Download pre-built VS Code if necessary. Build if there is no available # download but not when in the CI. The pre-built package basically just # provides us the dependencies and extensions so we don't have to install and # build them respectively which takes a long time. function prepare-vscode() { if [[ ! -d "${vscodeBuildPath}" || ! -d "${vscodeSourcePath}" ]] ; then mkdir -p "${buildPath}" # TODO: for now everything uses the Linux build and we rebuild the modules. # This means you must build on the target system. local tarName="vstar-${vscodeVersion}-${target}-${arch}.tar.gz" local linuxTarName="vstar-${vscodeVersion}-linux-${arch}.tar.gz" local linuxVscodeBuildName="vscode-${vscodeVersion}-linux-${arch}-built" local vsSourceUrl="https://codesrv-ci.cdr.sh/${linuxTarName}" if download-vscode ; then cd "${buildPath}" rm -rf "${vscodeBuildPath}" tar -xzf "${tarName}" rm "${tarName}" if [[ "${target}" != "linux" ]] ; then mv "${linuxVscodeBuildName}" "${vscodeBuildName}" fi elif [[ -n "${ci}" ]] ; then log "Pre-built VS Code ${vscodeVersion}-${target}-${arch} does not exist" "error" exit 1 else log "${tarName} does not exist, building" build-vscode return fi else log "VS Code is already downloaded or built" fi log "Ensuring VS Code is fully built" build-vscode } function build-task() { prepare-vscode build-code-server } function vstar-task() { local archivePath="${releasePath}/vstar-${vscodeVersion}-${target}-${arch}.tar.gz" rm -f "${archivePath}" mkdir -p "${releasePath}" tar -C "${buildPath}" -czf "${archivePath}" "${vscodeSourceName}" "${vscodeBuildName}" log "Archive: ${archivePath}" } function package-task() { local archivePath="${releasePath}/${binaryName}" rm -rf "${archivePath}" mkdir -p "${archivePath}" cp "${buildPath}/${binaryName}" "${archivePath}/code-server" cp "${rootPath}/README.md" "${archivePath}" cp "${vscodeSourcePath}/LICENSE.txt" "${archivePath}" cp "${vscodeSourcePath}/ThirdPartyNotices.txt" "${archivePath}" cd "${releasePath}" if [[ "${target}" == "darwin" ]] ; then zip -r "${binaryName}.zip" "${binaryName}" log "Archive: ${archivePath}.zip" else tar -czf "${binaryName}.tar.gz" "${binaryName}" log "Archive: ${archivePath}.tar.gz" fi } # Package built code into a binary. function binary-task() { # I had trouble getting VS Code to build with the @coder/nbin dependency due # to the types it installs (tons of conflicts), so for now it's a global # dependency. cd "${rootPath}" npm link @coder/nbin node "${rootPath}/scripts/nbin.js" "${target}" "${arch}" "${codeServerBuildPath}" rm node_modules/@coder/nbin mv "${codeServerBuildPath}/code-server" "${buildPath}/${binaryName}" log "Binary: ${buildPath}/${binaryName}" } # Check if it looks like we are inside VS Code. function in-vscode () { log "Checking if we are inside VS Code" local dir="${1}" ; shift local maybeVscode local dirName maybeVscode="$(realpath "${dir}/../../..")" dirName="$(basename "${maybeVscode}")" if [[ "${dirName}" != "vscode" ]] ; then return 1 fi if [[ ! -f "${maybeVscode}/package.json" ]] ; then return 1 fi if ! grep '"name": "code-oss-dev"' "${maybeVscode}/package.json" --quiet ; then return 1 fi return 0 } function ensure-in-vscode-task() { if ! in-vscode "${rootPath}"; then log "Not in vscode" "error" exit 1 fi exit 0 } function main() { local relativeRootPath local rootPath relativeRootPath="$(dirname "${0}")/.." rootPath="$(realpath "${relativeRootPath}")" local task="${1}" ; shift if [[ "${task}" == "ensure-in-vscode" ]] ; then ensure-in-vscode-task fi local codeServerVersion="${1}" ; shift local vscodeVersion="${1}" ; shift local target="${1}" ; shift local arch="${1}" ; shift local ci="${CI:-}" # This lets you build in a separate directory since building within this # directory while developing makes it hard to keep developing since compiling # will compile everything in the build directory as well. local outPath="${OUT:-${rootPath}}" # If we're inside a vscode directory, assume we want to develop. In that case # we should set an OUT directory and not build in this directory. if in-vscode "${outPath}" ; then log "Set the OUT environment variable to something outside of VS Code" "error" exit 1 fi local releasePath="${outPath}/release" local buildPath="${outPath}/build" local vscodeSourceName="vscode-${vscodeVersion}-source" local vscodeBuildName="vscode-${vscodeVersion}-${target}-${arch}-built" local vscodeSourcePath="${buildPath}/${vscodeSourceName}" local vscodeBuildPath="${buildPath}/${vscodeBuildName}" local codeServerBuildName="code-server${codeServerVersion}-vsc${vscodeVersion}-${target}-${arch}-built" local codeServerBuildPath="${buildPath}/${codeServerBuildName}" local binaryName="code-server${codeServerVersion}-vsc${vscodeVersion}-${target}-${arch}" log "Running ${task} task" log " rootPath: ${rootPath}" log " outPath: ${outPath}" log " codeServerVersion: ${codeServerVersion}" log " vscodeVersion: ${vscodeVersion}" log " target: ${target}" log " arch: ${arch}" if [[ -n "${ci}" ]] ; then log " CI: yes" else log " CI: no" fi "${task}-task" "$@" } main "$@"