|
| 1 | +import { register, run } from "@coder/runner"; |
| 2 | +import * as fs from "fs"; |
| 3 | +import * as fse from "fs-extra"; |
| 4 | +import * as path from "path"; |
| 5 | +import * as zlib from "zlib"; |
| 6 | + |
| 7 | +const libPath = path.join(__dirname, "../lib"); |
| 8 | +const vscodePath = path.join(libPath, "vscode"); |
| 9 | +const pkgsPath = path.join(__dirname, "../packages"); |
| 10 | +const defaultExtensionsPath = path.join(libPath, "VSCode-linux-x64/resources/app/extensions"); |
| 11 | + |
| 12 | +const buildServerBinary = register("build:server:binary", async (runner) => { |
| 13 | + await ensureInstalled(); |
| 14 | + await copyForDefaultExtensions(); |
| 15 | + await Promise.all([ |
| 16 | + buildBootstrapFork(), |
| 17 | + buildWeb(), |
| 18 | + buildDefaultExtensions(), |
| 19 | + buildServerBundle(), |
| 20 | + buildAppBrowser(), |
| 21 | + ]); |
| 22 | + |
| 23 | + await buildServerBinaryPackage(); |
| 24 | +}); |
| 25 | + |
| 26 | +const buildServerBinaryPackage = register("build:server:binary:package", async (runner) => { |
| 27 | + const cliPath = path.join(pkgsPath, "server"); |
| 28 | + runner.cwd = cliPath; |
| 29 | + if (!fs.existsSync(path.join(cliPath, "out"))) { |
| 30 | + throw new Error("Cannot build binary without web bundle built"); |
| 31 | + } |
| 32 | + await buildServerBinaryCopy(); |
| 33 | + const resp = await runner.execute("npm", ["run", "build:nexe"]); |
| 34 | + if (resp.exitCode !== 0) { |
| 35 | + throw new Error(`Failed to package binary: ${resp.stderr}`); |
| 36 | + } |
| 37 | +}); |
| 38 | + |
| 39 | +const buildServerBinaryCopy = register("build:server:binary:copy", async (runner) => { |
| 40 | + const cliPath = path.join(pkgsPath, "server"); |
| 41 | + const cliBuildPath = path.join(cliPath, "build"); |
| 42 | + fse.removeSync(cliBuildPath); |
| 43 | + fse.mkdirpSync(path.join(cliBuildPath, "extensions")); |
| 44 | + const bootstrapForkPath = path.join(pkgsPath, "vscode", "bin", "bootstrap-fork.js"); |
| 45 | + const webOutputPath = path.join(pkgsPath, "web", "out"); |
| 46 | + const browserAppOutputPath = path.join(pkgsPath, "app", "browser", "out"); |
| 47 | + const nodePtyModule = path.join(pkgsPath, "protocol", "node_modules", "node-pty", "build", "Release", "pty.node"); |
| 48 | + |
| 49 | + if (!fs.existsSync(nodePtyModule)) { |
| 50 | + throw new Error("Could not find pty.node. Ensure all packages have been installed"); |
| 51 | + } |
| 52 | + if (!fs.existsSync(webOutputPath)) { |
| 53 | + throw new Error("Web bundle must be built"); |
| 54 | + } |
| 55 | + if (!fs.existsSync(defaultExtensionsPath)) { |
| 56 | + throw new Error("Default extensions must be built"); |
| 57 | + } |
| 58 | + if (!fs.existsSync(bootstrapForkPath)) { |
| 59 | + throw new Error("Bootstrap fork must exist"); |
| 60 | + } |
| 61 | + fse.copySync(defaultExtensionsPath, path.join(cliBuildPath, "extensions")); |
| 62 | + fs.writeFileSync(path.join(cliBuildPath, "bootstrap-fork.js.gz"), zlib.gzipSync(fs.readFileSync(bootstrapForkPath))); |
| 63 | + const cpDir = (dir: string, subdir: "auth" | "unauth", rootPath: string): void => { |
| 64 | + const stat = fs.statSync(dir); |
| 65 | + if (stat.isDirectory()) { |
| 66 | + const paths = fs.readdirSync(dir); |
| 67 | + paths.forEach((p) => cpDir(path.join(dir, p), subdir, rootPath)); |
| 68 | + } else if (stat.isFile()) { |
| 69 | + const newPath = path.join(cliBuildPath, "web", subdir, path.relative(rootPath, dir)); |
| 70 | + fse.mkdirpSync(path.dirname(newPath)); |
| 71 | + fs.writeFileSync(newPath + ".gz", zlib.gzipSync(fs.readFileSync(dir))); |
| 72 | + } else { |
| 73 | + // Nothing |
| 74 | + } |
| 75 | + }; |
| 76 | + cpDir(webOutputPath, "auth", webOutputPath); |
| 77 | + cpDir(browserAppOutputPath, "unauth", browserAppOutputPath); |
| 78 | + fse.mkdirpSync(path.join(cliBuildPath, "modules")); |
| 79 | + fse.copySync(nodePtyModule, path.join(cliBuildPath, "modules", "pty.node")); |
| 80 | +}); |
| 81 | + |
| 82 | +const buildServerBundle = register("build:server:bundle", async (runner) => { |
| 83 | + const cliPath = path.join(pkgsPath, "server"); |
| 84 | + runner.cwd = cliPath; |
| 85 | + await runner.execute("npm", ["run", "build:webpack"]); |
| 86 | +}); |
| 87 | + |
| 88 | +const buildBootstrapFork = register("build:bootstrap-fork", async (runner) => { |
| 89 | + await ensureInstalled(); |
| 90 | + await ensurePatched(); |
| 91 | + |
| 92 | + const vscodePkgPath = path.join(pkgsPath, "vscode"); |
| 93 | + runner.cwd = vscodePkgPath; |
| 94 | + await runner.execute("npm", ["run", "build:bootstrap-fork"]); |
| 95 | +}); |
| 96 | + |
| 97 | +const buildAppBrowser = register("build:app:browser", async (runner) => { |
| 98 | + await ensureInstalled(); |
| 99 | + |
| 100 | + const appPath = path.join(pkgsPath, "app/browser"); |
| 101 | + runner.cwd = appPath; |
| 102 | + fse.removeSync(path.join(appPath, "out")); |
| 103 | + await runner.execute("npm", ["run", "build"]); |
| 104 | +}); |
| 105 | + |
| 106 | +const buildWeb = register("build:web", async (runner) => { |
| 107 | + await ensureInstalled(); |
| 108 | + await ensurePatched(); |
| 109 | + |
| 110 | + const webPath = path.join(pkgsPath, "web"); |
| 111 | + runner.cwd = webPath; |
| 112 | + fse.removeSync(path.join(webPath, "out")); |
| 113 | + await runner.execute("npm", ["run", "build"]); |
| 114 | +}); |
| 115 | + |
| 116 | +const extDirPath = path.join("lib", "vscode-default-extensions"); |
| 117 | +const copyForDefaultExtensions = register("build:copy-vscode", async (runner) => { |
| 118 | + if (!fs.existsSync(defaultExtensionsPath)) { |
| 119 | + await ensureClean(); |
| 120 | + fse.removeSync(extDirPath); |
| 121 | + fse.copySync(vscodePath, extDirPath); |
| 122 | + } |
| 123 | +}); |
| 124 | + |
| 125 | +const buildDefaultExtensions = register("build:default-extensions", async (runner) => { |
| 126 | + if (!fs.existsSync(defaultExtensionsPath)) { |
| 127 | + await copyForDefaultExtensions(); |
| 128 | + runner.cwd = extDirPath; |
| 129 | + const resp = await runner.execute("npx", ["gulp", "vscode-linux-x64"]); |
| 130 | + if (resp.exitCode !== 0) { |
| 131 | + throw new Error(`Failed to build default extensions: ${resp.stderr}`); |
| 132 | + } |
| 133 | + } |
| 134 | +}); |
| 135 | + |
| 136 | +const ensureInstalled = register("vscode:install", async (runner) => { |
| 137 | + await ensureCloned(); |
| 138 | + |
| 139 | + runner.cwd = vscodePath; |
| 140 | + const install = await runner.execute("yarn", []); |
| 141 | + if (install.exitCode !== 0) { |
| 142 | + throw new Error(`Failed to install vscode dependencies: ${install.stderr}`); |
| 143 | + } |
| 144 | +}); |
| 145 | + |
| 146 | +const ensureCloned = register("vscode:clone", async (runner) => { |
| 147 | + if (fs.existsSync(vscodePath)) { |
| 148 | + await ensureClean(); |
| 149 | + } else { |
| 150 | + fs.mkdirSync(libPath); |
| 151 | + runner.cwd = libPath; |
| 152 | + const clone = await runner.execute("git", ["clone", "https://github.com/microsoft/vscode"]); |
| 153 | + if (clone.exitCode !== 0) { |
| 154 | + throw new Error(`Failed to clone: ${clone.exitCode}`); |
| 155 | + } |
| 156 | + } |
| 157 | + |
| 158 | + runner.cwd = vscodePath; |
| 159 | + const checkout = await runner.execute("git", ["checkout", "tags/1.31.0"]); |
| 160 | + if (checkout.exitCode !== 0) { |
| 161 | + throw new Error(`Failed to checkout: ${checkout.stderr}`); |
| 162 | + } |
| 163 | +}); |
| 164 | + |
| 165 | +const ensureClean = register("vscode:clean", async (runner) => { |
| 166 | + runner.cwd = vscodePath; |
| 167 | + |
| 168 | + const status = await runner.execute("git", ["status", "--porcelain"]); |
| 169 | + if (status.stdout.trim() !== "") { |
| 170 | + const clean = await runner.execute("git", ["clean", "-f", "-d", "-X"]); |
| 171 | + if (clean.exitCode !== 0) { |
| 172 | + throw new Error(`Failed to clean git repository: ${clean.stderr}`); |
| 173 | + } |
| 174 | + const removeUnstaged = await runner.execute("git", ["checkout", "--", "."]); |
| 175 | + if (removeUnstaged.exitCode !== 0) { |
| 176 | + throw new Error(`Failed to remove unstaged files: ${removeUnstaged.stderr}`); |
| 177 | + } |
| 178 | + } |
| 179 | +}); |
| 180 | + |
| 181 | +const ensurePatched = register("vscode:patch", async (runner) => { |
| 182 | + if (!fs.existsSync(vscodePath)) { |
| 183 | + throw new Error("vscode must be cloned to patch"); |
| 184 | + } |
| 185 | + await ensureClean(); |
| 186 | + |
| 187 | + runner.cwd = vscodePath; |
| 188 | + const patchPath = path.join(__dirname, "../scripts/vscode.patch"); |
| 189 | + const apply = await runner.execute("git", ["apply", "--unidiff-zero", patchPath]); |
| 190 | + if (apply.exitCode !== 0) { |
| 191 | + throw new Error(`Failed to apply patches: ${apply.stderr}`); |
| 192 | + } |
| 193 | +}); |
| 194 | + |
| 195 | +run(); |
0 commit comments