Package the portable DSH profiles, local plugins, desktop integration, Firefox wrapper, and ownership-aware installer lifecycle. Bundle checksum-pinned OpenVSCode, editor extensions, translation assets, and cloudflared so unreliable upstream artifact downloads cannot break a clean installation.
75 lines
3.6 KiB
JavaScript
75 lines
3.6 KiB
JavaScript
import { execFile } from 'node:child_process'
|
|
import { createHash } from 'node:crypto'
|
|
import { createReadStream, constants } from 'node:fs'
|
|
import { access, mkdir, mkdtemp, readFile, rename, rm, writeFile } from 'node:fs/promises'
|
|
import { dirname, join, resolve } from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
import { promisify } from 'node:util'
|
|
import { installEditorExtensions } from './install-editor-extensions.mjs'
|
|
|
|
const execute = promisify(execFile)
|
|
const pluginDirectory = dirname(dirname(fileURLToPath(import.meta.url)))
|
|
const release = JSON.parse(await readFile(join(pluginDirectory, 'editor-release.json'), 'utf8'))
|
|
const runtimeDirectory = join(pluginDirectory, 'runtime')
|
|
const installationDirectory = join(runtimeDirectory, 'current')
|
|
|
|
async function archiveDigest(path) {
|
|
const hash = createHash('sha256')
|
|
for await (const chunk of createReadStream(path)) hash.update(chunk)
|
|
return hash.digest('hex')
|
|
}
|
|
|
|
async function isAlreadyInstalled() {
|
|
let installed
|
|
try {
|
|
installed = JSON.parse(await readFile(join(installationDirectory, 'dsh-release.json'), 'utf8'))
|
|
} catch (error) {
|
|
if (error.code === 'ENOENT') return false
|
|
throw error
|
|
}
|
|
if (installed.sha256 !== release.sha256) {
|
|
throw new Error('A different editor runtime is installed. Remove runtime/current explicitly before changing versions.')
|
|
}
|
|
await access(join(installationDirectory, 'bin', 'openvscode-server'), constants.X_OK)
|
|
return true
|
|
}
|
|
|
|
async function install() {
|
|
const argumentsList = process.argv.slice(2)
|
|
if (argumentsList.length !== 0 && (argumentsList.length !== 2 || argumentsList[0] !== '--archive')) {
|
|
throw new Error('Usage: node scripts/install-editor.mjs [--archive /path/to/editor.tar.gz]')
|
|
}
|
|
const providedArchive = argumentsList.length === 2 ? resolve(argumentsList[1]) : null
|
|
|
|
if (process.platform !== release.platform || process.arch !== release.architecture) {
|
|
throw new Error(`This pinned runtime supports ${release.platform}/${release.architecture}, not ${process.platform}/${process.arch}.`)
|
|
}
|
|
if (await isAlreadyInstalled()) {
|
|
console.log(`OpenVSCode Server ${release.version} is already installed. No server started.`)
|
|
return
|
|
}
|
|
|
|
await mkdir(runtimeDirectory, { recursive: true })
|
|
const stagingDirectory = await mkdtemp(join(runtimeDirectory, '.install-'))
|
|
try {
|
|
const archive = providedArchive || join(stagingDirectory, 'editor.tar.gz')
|
|
if (!providedArchive) {
|
|
console.log(`Downloading OpenVSCode Server ${release.version} from the pinned official release…`)
|
|
await execute('curl', ['--fail', '--location', '--silent', '--show-error', '--header', 'Accept: application/octet-stream', '--max-time', '180', '--output', archive, release.url], { timeout: 190000 })
|
|
}
|
|
if (await archiveDigest(archive) !== release.sha256) {
|
|
throw new Error('Editor archive SHA-256 mismatch; refusing to extract or execute it.')
|
|
}
|
|
await execute('tar', ['--extract', '--gzip', '--file', archive, '--directory', stagingDirectory, '--no-same-owner'], { timeout: 60000 })
|
|
const extractedDirectory = join(stagingDirectory, release.archiveDirectory)
|
|
await access(join(extractedDirectory, 'bin', 'openvscode-server'), constants.X_OK)
|
|
await writeFile(join(extractedDirectory, 'dsh-release.json'), `${JSON.stringify(release, null, 2)}\n`)
|
|
await rename(extractedDirectory, installationDirectory)
|
|
console.log(`Installed and SHA-256 verified OpenVSCode Server ${release.version}. No server started.`)
|
|
} finally {
|
|
await rm(stagingDirectory, { recursive: true, force: true })
|
|
}
|
|
}
|
|
|
|
await install()
|
|
await installEditorExtensions()
|