DSH-Arch-Setup/plugins/dsh-touched-git/scripts/install-editor.mjs
Шурупов Илья Викторович cf9443753e Add one-command Arch DSH setup
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.
2026-09-12 19:13:36 +03:00

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()