00ea452310
* bundle node and windows git runtimes * split desktop runtime into release package * fix desktop runtime packaging ci * embed desktop runtime release tag * show desktop runtime download progress * fix desktop runtime release handling * refactor desktop runtime version config * fix desktop package license --------- Co-authored-by: xingzhi <chuzihao.czh@alibaba-inc.com> Co-authored-by: ekko <fqsy1416@gmail.com>
122 lines
3.9 KiB
JavaScript
122 lines
3.9 KiB
JavaScript
#!/usr/bin/env node
|
|
// Package prepared Python/Node/Git runtime resources into a release asset.
|
|
import {
|
|
cpSync,
|
|
createReadStream,
|
|
existsSync,
|
|
mkdirSync,
|
|
mkdtempSync,
|
|
rmSync,
|
|
statSync,
|
|
writeFileSync,
|
|
} from 'node:fs'
|
|
import { createHash } from 'node:crypto'
|
|
import { arch as osArch, platform as osPlatform, tmpdir } from 'node:os'
|
|
import { dirname, join, resolve } from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
import { spawnSync } from 'node:child_process'
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
const ROOT = resolve(__dirname, '..')
|
|
const TARGET_OS = process.env.TARGET_OS || osPlatform()
|
|
const TARGET_ARCH = process.env.TARGET_ARCH || osArch()
|
|
const OS_LABEL = TARGET_OS === 'win32' ? 'win' : TARGET_OS === 'darwin' ? 'mac' : TARGET_OS
|
|
const PLATFORM = `${OS_LABEL}-${TARGET_ARCH}`
|
|
const OUT_DIR = resolve(ROOT, 'release', 'runtime')
|
|
|
|
const PY_DIR = resolve(ROOT, 'resources', 'python', PLATFORM)
|
|
const NODE_DIR = resolve(ROOT, 'resources', 'node', PLATFORM)
|
|
const GIT_DIR = resolve(ROOT, 'resources', 'git', PLATFORM)
|
|
const pyBin = TARGET_OS === 'win32'
|
|
? resolve(PY_DIR, 'python.exe')
|
|
: resolve(PY_DIR, 'bin', 'python3')
|
|
|
|
function run(command, args, options = {}) {
|
|
const result = spawnSync(command, args, { stdio: 'inherit', ...options })
|
|
if (result.status !== 0) process.exit(result.status ?? 1)
|
|
return result
|
|
}
|
|
|
|
function output(command, args) {
|
|
const result = spawnSync(command, args, { encoding: 'utf-8' })
|
|
if (result.status !== 0) {
|
|
process.stderr.write(result.stderr || result.stdout || '')
|
|
process.exit(result.status ?? 1)
|
|
}
|
|
return result.stdout.trim()
|
|
}
|
|
|
|
async function sha256File(file) {
|
|
const hash = createHash('sha256')
|
|
await new Promise((resolvePromise, rejectPromise) => {
|
|
const stream = createReadStream(file)
|
|
stream.on('data', chunk => hash.update(chunk))
|
|
stream.on('end', resolvePromise)
|
|
stream.on('error', rejectPromise)
|
|
})
|
|
return hash.digest('hex')
|
|
}
|
|
|
|
for (const dir of [PY_DIR, NODE_DIR]) {
|
|
if (!existsSync(dir)) {
|
|
console.error(`Runtime directory missing: ${dir}`)
|
|
process.exit(1)
|
|
}
|
|
}
|
|
|
|
const hermesAgentVersion = output(pyBin, [
|
|
'-c',
|
|
'import importlib.metadata as m; print(m.version("hermes-agent"))',
|
|
])
|
|
const assetName = `hermes-runtime-hermes-agent-${hermesAgentVersion}-${PLATFORM}.tar.gz`
|
|
const manifestName = `hermes-runtime-${PLATFORM}.json`
|
|
|
|
mkdirSync(OUT_DIR, { recursive: true })
|
|
const stage = mkdtempSync(join(tmpdir(), `hermes-runtime-${PLATFORM}-`))
|
|
|
|
try {
|
|
cpSync(PY_DIR, join(stage, 'python'), { recursive: true, force: true, verbatimSymlinks: true })
|
|
cpSync(NODE_DIR, join(stage, 'node'), { recursive: true, force: true, verbatimSymlinks: true })
|
|
if (existsSync(GIT_DIR)) {
|
|
cpSync(GIT_DIR, join(stage, 'git'), { recursive: true, force: true, verbatimSymlinks: true })
|
|
} else {
|
|
mkdirSync(join(stage, 'git'), { recursive: true })
|
|
writeFileSync(join(stage, 'git', '.placeholder'), 'Git for Windows is only bundled on Windows.\n')
|
|
}
|
|
|
|
const runtimeManifest = {
|
|
schema: 1,
|
|
platform: PLATFORM,
|
|
targetOs: TARGET_OS,
|
|
targetArch: TARGET_ARCH,
|
|
hermesAgentVersion,
|
|
asset: {
|
|
name: assetName,
|
|
},
|
|
}
|
|
writeFileSync(join(stage, 'runtime-manifest.json'), JSON.stringify(runtimeManifest, null, 2) + '\n')
|
|
|
|
const assetPath = resolve(OUT_DIR, assetName)
|
|
rmSync(assetPath, { force: true })
|
|
run('tar', ['-czf', assetPath, '-C', stage, '.'])
|
|
|
|
const sha256 = await sha256File(assetPath)
|
|
writeFileSync(`${assetPath}.sha256`, `${sha256} ${assetName}\n`)
|
|
|
|
const platformManifest = {
|
|
...runtimeManifest,
|
|
createdAt: new Date().toISOString(),
|
|
asset: {
|
|
name: assetName,
|
|
sha256,
|
|
size: statSync(assetPath).size,
|
|
},
|
|
}
|
|
writeFileSync(resolve(OUT_DIR, manifestName), JSON.stringify(platformManifest, null, 2) + '\n')
|
|
|
|
console.log(`Runtime asset: ${assetPath}`)
|
|
console.log(`Runtime manifest: ${resolve(OUT_DIR, manifestName)}`)
|
|
} finally {
|
|
rmSync(stage, { recursive: true, force: true })
|
|
}
|