From 6b8a75f6abd2c82428fb7052acd45999417ce108 Mon Sep 17 00:00:00 2001 From: Marko Vejnovic Date: Tue, 11 Nov 2025 22:39:20 -0800 Subject: [PATCH] chore(ENG-21504): Remove bit-rotted scripts (#24606) ### What does this PR do? Removes some scripts which haven't been tested in a while. ### How did you verify your code works? CI passes --- scripts/google.mjs | 510 ------------------------------------------- scripts/machine.mjs | 10 +- scripts/orbstack.mjs | 195 ----------------- 3 files changed, 2 insertions(+), 713 deletions(-) delete mode 100644 scripts/google.mjs delete mode 100644 scripts/orbstack.mjs diff --git a/scripts/google.mjs b/scripts/google.mjs deleted file mode 100644 index 95c197b119..0000000000 --- a/scripts/google.mjs +++ /dev/null @@ -1,510 +0,0 @@ -import { $, getUsernameForDistro, spawnSafe, which } from "./utils.mjs"; - -export const google = { - get cloud() { - return "google"; - }, - - /** - * @param {string[]} args - * @param {import("./utils.mjs").SpawnOptions} [options] - * @returns {Promise} - */ - async spawn(args, options = {}) { - const gcloud = which("gcloud", { required: true }); - - let env = { ...process.env }; - // if (isCI) { - // env; // TODO: Add Google Cloud credentials - // } else { - // env["TERM"] = "dumb"; - // } - - const { stdout } = await spawnSafe($`${gcloud} ${args} --format json`, { - env, - ...options, - }); - try { - return JSON.parse(stdout); - } catch { - return; - } - }, - - /** - * @param {Record} [options] - * @returns {string[]} - */ - getFilters(options = {}) { - const filter = Object.entries(options) - .filter(([, value]) => value !== undefined) - .map(([key, value]) => [value.includes("*") ? `${key}~${value}` : `${key}=${value}`]) - .join(" AND "); - return filter ? ["--filter", filter] : []; - }, - - /** - * @param {Record} options - * @returns {string[]} - */ - getFlags(options) { - return Object.entries(options) - .filter(([, value]) => value !== undefined) - .flatMap(([key, value]) => { - if (typeof value === "boolean") { - return value ? [`--${key}`] : []; - } - return [`--${key}=${value}`]; - }); - }, - - /** - * @param {Record} options - * @returns {string} - * @link https://cloud.google.com/sdk/gcloud/reference/topic/escaping - */ - getMetadata(options) { - const delimiter = Math.random().toString(36).substring(2, 15); - const entries = Object.entries(options) - .map(([key, value]) => `${key}=${value}`) - .join(delimiter); - return `^${delimiter}^${entries}`; - }, - - /** - * @param {string} name - * @returns {string} - */ - getLabel(name) { - return name.replace(/[^a-z0-9_-]/g, "-").toLowerCase(); - }, - - /** - * @typedef {Object} GoogleImage - * @property {string} id - * @property {string} name - * @property {string} family - * @property {"X86_64" | "ARM64"} architecture - * @property {string} diskSizeGb - * @property {string} selfLink - * @property {"READY"} status - * @property {string} creationTimestamp - */ - - /** - * @param {Partial} [options] - * @returns {Promise} - * @link https://cloud.google.com/sdk/gcloud/reference/compute/images/list - */ - async listImages(options) { - const filters = google.getFilters(options); - const images = await google.spawn($`compute images list ${filters} --preview-images --show-deprecated`); - return images.sort((a, b) => (a.creationTimestamp < b.creationTimestamp ? 1 : -1)); - }, - - /** - * @param {Record} options - * @returns {Promise} - * @link https://cloud.google.com/sdk/gcloud/reference/compute/images/create - */ - async createImage(options) { - const { name, ...otherOptions } = options; - const flags = this.getFlags(otherOptions); - const imageId = name || "i-" + Math.random().toString(36).substring(2, 15); - return this.spawn($`compute images create ${imageId} ${flags}`); - }, - - /** - * @typedef {Object} GoogleInstance - * @property {string} id - * @property {string} name - * @property {"RUNNING"} status - * @property {string} machineType - * @property {string} zone - * @property {GoogleDisk[]} disks - * @property {GoogleNetworkInterface[]} networkInterfaces - * @property {object} [scheduling] - * @property {"STANDARD" | "SPOT"} [scheduling.provisioningModel] - * @property {boolean} [scheduling.preemptible] - * @property {Record} [labels] - * @property {string} selfLink - * @property {string} creationTimestamp - */ - - /** - * @typedef {Object} GoogleDisk - * @property {string} deviceName - * @property {boolean} boot - * @property {"X86_64" | "ARM64"} architecture - * @property {string[]} [licenses] - * @property {number} diskSizeGb - */ - - /** - * @typedef {Object} GoogleNetworkInterface - * @property {"IPV4_ONLY" | "IPV4_IPV6" | "IPV6_ONLY"} stackType - * @property {string} name - * @property {string} network - * @property {string} networkIP - * @property {string} subnetwork - * @property {GoogleAccessConfig[]} accessConfigs - */ - - /** - * @typedef {Object} GoogleAccessConfig - * @property {string} name - * @property {"ONE_TO_ONE_NAT" | "INTERNAL_NAT"} type - * @property {string} [natIP] - */ - - /** - * @param {Record} options - * @returns {Promise} - * @link https://cloud.google.com/sdk/gcloud/reference/compute/instances/create - */ - async createInstance(options) { - const { name, ...otherOptions } = options || {}; - const flags = this.getFlags(otherOptions); - const instanceId = name || "i-" + Math.random().toString(36).substring(2, 15); - const [instance] = await this.spawn($`compute instances create ${instanceId} ${flags}`); - return instance; - }, - - /** - * @param {string} instanceId - * @param {string} zoneId - * @returns {Promise} - * @link https://cloud.google.com/sdk/gcloud/reference/compute/instances/stop - */ - async stopInstance(instanceId, zoneId) { - await this.spawn($`compute instances stop ${instanceId} --zone=${zoneId}`); - }, - - /** - * @param {string} instanceId - * @param {string} zoneId - * @returns {Promise} - * @link https://cloud.google.com/sdk/gcloud/reference/compute/instances/delete - */ - async deleteInstance(instanceId, zoneId) { - await this.spawn($`compute instances delete ${instanceId} --delete-disks=all --zone=${zoneId}`, { - throwOnError: error => !/not found/i.test(inspect(error)), - }); - }, - - /** - * @param {string} instanceId - * @param {string} username - * @param {string} zoneId - * @param {object} [options] - * @param {boolean} [options.wait] - * @returns {Promise} - * @link https://cloud.google.com/sdk/gcloud/reference/compute/reset-windows-password - */ - async resetWindowsPassword(instanceId, username, zoneId, options = {}) { - const attempts = options.wait ? 15 : 1; - for (let i = 0; i < attempts; i++) { - const result = await this.spawn( - $`compute reset-windows-password ${instanceId} --user=${username} --zone=${zoneId}`, - { - throwOnError: error => !/instance may not be ready for use/i.test(inspect(error)), - }, - ); - if (result) { - const { password } = result; - if (password) { - return password; - } - } - await new Promise(resolve => setTimeout(resolve, 60000 * i)); - } - }, - - /** - * @param {Partial} options - * @returns {Promise} - */ - async listInstances(options) { - const filters = this.getFilters(options); - const instances = await this.spawn($`compute instances list ${filters}`); - return instances.sort((a, b) => (a.creationTimestamp < b.creationTimestamp ? 1 : -1)); - }, - - /** - * @param {MachineOptions} options - * @returns {Promise} - */ - async getMachineImage(options) { - const { os, arch, distro, release } = options; - const architecture = arch === "aarch64" ? "ARM64" : "X86_64"; - - /** @type {string | undefined} */ - let family; - if (os === "linux") { - if (!distro || distro === "debian") { - family = `debian-${release || "*"}`; - } else if (distro === "ubuntu") { - family = `ubuntu-${release?.replace(/\./g, "") || "*"}`; - } else if (distro === "fedora") { - family = `fedora-coreos-${release || "*"}`; - } else if (distro === "rhel") { - family = `rhel-${release || "*"}`; - } - } else if (os === "windows" && arch === "x64") { - if (!distro || distro === "server") { - family = `windows-${release || "*"}`; - } - } - - if (family) { - const images = await this.listImages({ family, architecture }); - if (images.length) { - const [image] = images; - return image; - } - } - - throw new Error(`Unsupported platform: ${inspect(options)}`); - }, - - /** - * @param {MachineOptions} options - * @returns {Promise} - */ - async createMachine(options) { - const { name, os, arch, distro, instanceType, tags, preemptible, detached } = options; - const image = await google.getMachineImage(options); - const { selfLink: imageUrl } = image; - - const username = getUsername(distro || os); - const userData = getUserData({ ...options, username }); - - /** @type {Record} */ - let metadata; - if (os === "windows") { - metadata = { - "enable-windows-ssh": "TRUE", - "sysprep-specialize-script-ps1": userData, - }; - } else { - metadata = { - "user-data": userData, - }; - } - - const instance = await google.createInstance({ - "name": name, - "zone": "us-central1-a", - "image": imageUrl, - "machine-type": instanceType || (arch === "aarch64" ? "t2a-standard-2" : "t2d-standard-2"), - "boot-disk-auto-delete": true, - "boot-disk-size": `${getDiskSize(options)}GB`, - "metadata": this.getMetadata(metadata), - "labels": Object.entries(tags || {}) - .filter(([, value]) => value !== undefined) - .map(([key, value]) => `${this.getLabel(key)}=${value}`) - .join(","), - "provisioning-model": preemptible ? "SPOT" : "STANDARD", - "instance-termination-action": preemptible || !detached ? "DELETE" : undefined, - "no-restart-on-failure": true, - "threads-per-core": 1, - "max-run-duration": detached ? undefined : "6h", - }); - - return this.toMachine(instance, options); - }, - - /** - * @param {GoogleInstance} instance - * @param {MachineOptions} [options] - * @returns {Machine} - */ - toMachine(instance, options = {}) { - const { id: instanceId, name, zone: zoneUrl, machineType: machineTypeUrl, labels } = instance; - const machineType = machineTypeUrl.split("/").pop(); - const zoneId = zoneUrl.split("/").pop(); - - let os, arch, distro, release; - const { disks = [] } = instance; - for (const { boot, architecture, licenses = [] } of disks) { - if (!boot) { - continue; - } - - if (architecture === "X86_64") { - arch = "x64"; - } else if (architecture === "ARM64") { - arch = "aarch64"; - } - - for (const license of licenses) { - const linuxMatch = /(debian|ubuntu|fedora|rhel)-(\d+)/i.exec(license); - if (linuxMatch) { - os = "linux"; - [, distro, release] = linuxMatch; - } else { - const windowsMatch = /windows-server-(\d+)-dc-core/i.exec(license); - if (windowsMatch) { - os = "windows"; - distro = "windowsserver"; - [, release] = windowsMatch; - } - } - } - } - - let publicIp; - const { networkInterfaces = [] } = instance; - for (const { accessConfigs = [] } of networkInterfaces) { - for (const { type, natIP } of accessConfigs) { - if (type === "ONE_TO_ONE_NAT" && natIP) { - publicIp = natIP; - } - } - } - - let preemptible; - const { scheduling } = instance; - if (scheduling) { - const { provisioningModel, preemptible: isPreemptible } = scheduling; - preemptible = provisioningModel === "SPOT" || isPreemptible; - } - - /** - * @returns {SshOptions} - */ - const connect = () => { - if (!publicIp) { - throw new Error(`Failed to find public IP for instance: ${name}`); - } - - /** @type {string | undefined} */ - let username; - - const { os, distro } = options; - if (os || distro) { - username = getUsernameForDistro(distro || os); - } - - return { hostname: publicIp, username }; - }; - - const spawn = async (command, options) => { - const connectOptions = connect(); - return spawnSsh({ ...connectOptions, command }, options); - }; - - const spawnSafe = async (command, options) => { - const connectOptions = connect(); - return spawnSshSafe({ ...connectOptions, command }, options); - }; - - const rdp = async () => { - const { hostname, username } = connect(); - const rdpUsername = `${username}-rdp`; - const password = await google.resetWindowsPassword(instanceId, rdpUsername, zoneId, { wait: true }); - return { hostname, username: rdpUsername, password }; - }; - - const attach = async () => { - const connectOptions = connect(); - await spawnSshSafe({ ...connectOptions }); - }; - - const upload = async (source, destination) => { - const connectOptions = connect(); - await spawnScp({ ...connectOptions, source, destination }); - }; - - const snapshot = async name => { - const stopResult = await this.stopInstance(instanceId, zoneId); - console.log(stopResult); - const image = await this.createImage({ - ["source-disk"]: instanceId, - ["zone"]: zoneId, - ["name"]: name || `${instanceId}-snapshot-${Date.now()}`, - }); - console.log(image); - return; - }; - - const terminate = async () => { - await google.deleteInstance(instanceId, zoneId); - }; - - return { - cloud: "google", - os, - arch, - distro, - release, - id: instanceId, - imageId: undefined, - name, - instanceType: machineType, - region: zoneId, - publicIp, - preemptible, - labels, - spawn, - spawnSafe, - rdp, - attach, - upload, - snapshot, - close: terminate, - [Symbol.asyncDispose]: terminate, - }; - }, - - /** - * @param {Record} [labels] - * @returns {Promise} - */ - async getMachines(labels) { - const filters = labels ? this.getFilters({ labels }) : {}; - const instances = await google.listInstances(filters); - return instances.map(instance => this.toMachine(instance)); - }, - - /** - * @param {MachineOptions} options - * @returns {Promise} - */ - async getImage(options) { - const { os, arch, distro, release } = options; - const architecture = arch === "aarch64" ? "ARM64" : "X86_64"; - - let name; - let username; - if (os === "linux") { - if (distro === "debian") { - name = `debian-${release}-*`; - username = "admin"; - } else if (distro === "ubuntu") { - name = `ubuntu-${release.replace(/\./g, "")}-*`; - username = "ubuntu"; - } - } else if (os === "windows" && arch === "x64") { - if (distro === "server") { - name = `windows-server-${release}-dc-core-*`; - username = "administrator"; - } - } - - if (name && username) { - const images = await google.listImages({ name, architecture }); - if (images.length) { - const [image] = images; - const { name, selfLink } = image; - return { - id: selfLink, - name, - username, - }; - } - } - - throw new Error(`Unsupported platform: ${inspect(platform)}`); - }, -}; diff --git a/scripts/machine.mjs b/scripts/machine.mjs index 0179aeac53..c1212d8fee 100755 --- a/scripts/machine.mjs +++ b/scripts/machine.mjs @@ -5,8 +5,6 @@ import { basename, extname, join, relative, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { inspect, parseArgs } from "node:util"; import { docker } from "./docker.mjs"; -import { google } from "./google.mjs"; -import { orbstack } from "./orbstack.mjs"; import { tart } from "./tart.mjs"; import { $, @@ -1064,14 +1062,10 @@ function getCloud(name) { switch (name) { case "docker": return docker; - case "orbstack": - return orbstack; - case "tart": - return tart; case "aws": return aws; - case "google": - return google; + case "tart": + return tart; } throw new Error(`Unsupported cloud: ${name}`); } diff --git a/scripts/orbstack.mjs b/scripts/orbstack.mjs deleted file mode 100644 index 4d5e11fd8d..0000000000 --- a/scripts/orbstack.mjs +++ /dev/null @@ -1,195 +0,0 @@ -import { inspect } from "node:util"; -import { getUserData } from "./machine.mjs"; -import { $, getUsernameForDistro, mkdtemp, rm, setupUserData, spawnSafe, spawnSshSafe, writeFile } from "./utils.mjs"; - -/** - * @link https://docs.orbstack.dev/ - */ -export const orbstack = { - get name() { - return "orbstack"; - }, - - /** - * @typedef {Object} OrbstackImage - * @property {string} distro - * @property {string} version - * @property {string} arch - */ - - /** - * @param {Platform} platform - * @returns {OrbstackImage} - */ - getImage(platform) { - const { os, arch, distro, release } = platform; - if (os !== "linux" || !/^debian|ubuntu|alpine|fedora|centos$/.test(distro)) { - throw new Error(`Unsupported platform: ${inspect(platform)}`); - } - - return { - distro, - version: release, - arch: arch === "aarch64" ? "arm64" : "amd64", - }; - }, - - /** - * @typedef {Object} OrbstackVm - * @property {string} id - * @property {string} name - * @property {"running"} state - * @property {OrbstackImage} image - * @property {OrbstackConfig} config - */ - - /** - * @typedef {Object} OrbstackConfig - * @property {string} default_username - * @property {boolean} isolated - */ - - /** - * @typedef {Object} OrbstackVmOptions - * @property {string} [name] - * @property {OrbstackImage} image - * @property {string} [username] - * @property {string} [password] - * @property {string} [userData] - */ - - /** - * @param {OrbstackVmOptions} options - * @returns {Promise} - */ - async createVm(options) { - const { name, image, username, password, userData } = options; - const { distro, version, arch } = image; - const uniqueId = name || `linux-${distro}-${version}-${arch}-${Math.random().toString(36).slice(2, 11)}`; - - const args = [`--arch=${arch}`, `${distro}:${version}`, uniqueId]; - if (username) { - args.push(`--user=${username}`); - } - if (password) { - args.push(`--set-password=${password}`); - } - - let userDataPath; - if (userData) { - userDataPath = mkdtemp("orbstack-user-data-", "user-data.txt"); - console.log("User data path:", userData); - writeFile(userDataPath, userData); - args.push(`--user-data=${userDataPath}`); - } - - try { - await spawnSafe($`orbctl create ${args}`); - } finally { - if (userDataPath) { - rm(userDataPath); - } - } - - return this.inspectVm(uniqueId); - }, - - /** - * @param {string} name - */ - async deleteVm(name) { - await spawnSafe($`orbctl delete ${name}`, { - throwOnError: error => !/machine not found/i.test(inspect(error)), - }); - }, - - /** - * @param {string} name - * @returns {Promise} - */ - async inspectVm(name) { - const { exitCode, stdout } = await spawnSafe($`orbctl info ${name} --format=json`, { - throwOnError: error => !/machine not found/i.test(inspect(error)), - }); - if (exitCode === 0) { - return JSON.parse(stdout); - } - }, - - /** - * @returns {Promise} - */ - async listVms() { - const { stdout } = await spawnSafe($`orbctl list --format=json`); - return JSON.parse(stdout); - }, - - /** - * @param {MachineOptions} options - * @returns {Promise} - */ - async createMachine(options) { - const { distro } = options; - const username = getUsernameForDistro(distro); - const userData = getUserData({ ...options, username }); - - const image = this.getImage(options); - const vm = await this.createVm({ - image, - username, - userData, - }); - - const machine = this.toMachine(vm, options); - - await setupUserData(machine, options); - - return machine; - }, - - /** - * @param {OrbstackVm} vm - * @returns {Machine} - */ - toMachine(vm) { - const { id, name, config } = vm; - - const { default_username: username } = config; - const connectOptions = { - username, - hostname: `${name}@orb`, - }; - - const exec = async (command, options) => { - return spawnSsh({ ...connectOptions, command }, options); - }; - - const execSafe = async (command, options) => { - return spawnSshSafe({ ...connectOptions, command }, options); - }; - - const attach = async () => { - await spawnSshSafe({ ...connectOptions }); - }; - - const upload = async (source, destination) => { - await spawnSafe(["orbctl", "push", `--machine=${name}`, source, destination]); - }; - - const close = async () => { - await this.deleteVm(name); - }; - - return { - cloud: "orbstack", - id, - name, - spawn: exec, - spawnSafe: execSafe, - upload, - attach, - close, - [Symbol.asyncDispose]: close, - }; - }, -};