Codex/pr 1217 (#1226)
* 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>
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env node
|
||||
// Download Git for Windows MinGit for Windows builds. Other platforms create
|
||||
// an empty resource directory so electron-builder can use the same resource map.
|
||||
import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { dirname, resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { arch as osArch, platform as osPlatform, tmpdir } from 'node:os'
|
||||
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 OUT_DIR = resolve(ROOT, 'resources', 'git', `${OS_LABEL}-${TARGET_ARCH}`)
|
||||
|
||||
mkdirSync(OUT_DIR, { recursive: true })
|
||||
|
||||
if (TARGET_OS !== 'win32') {
|
||||
writeFileSync(resolve(OUT_DIR, '.placeholder'), 'Git for Windows is only bundled on Windows.\n')
|
||||
console.log(`Git resource placeholder ready at ${OUT_DIR}`)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
if (TARGET_ARCH !== 'x64') {
|
||||
console.error(`Unsupported Git for Windows target: ${TARGET_OS}-${TARGET_ARCH}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (existsSync(resolve(OUT_DIR, 'cmd', 'git.exe'))) {
|
||||
console.log(`Git for Windows already present at ${OUT_DIR}, skipping`)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
async function latestMinGitUrl() {
|
||||
if (process.env.GIT_FOR_WINDOWS_URL?.trim()) return process.env.GIT_FOR_WINDOWS_URL.trim()
|
||||
|
||||
const headers = { 'User-Agent': 'hermes-studio-desktop-build' }
|
||||
const token = process.env.GH_TOKEN || process.env.GITHUB_TOKEN
|
||||
if (token?.trim()) headers.Authorization = `Bearer ${token.trim()}`
|
||||
|
||||
const response = await fetch('https://api.github.com/repos/git-for-windows/git/releases/latest', {
|
||||
headers,
|
||||
})
|
||||
if (!response.ok) {
|
||||
throw new Error(`GitHub API returned ${response.status}`)
|
||||
}
|
||||
const release = await response.json()
|
||||
const asset = release.assets?.find(candidate =>
|
||||
typeof candidate?.name === 'string'
|
||||
&& /^MinGit-.*-64-bit\.zip$/.test(candidate.name)
|
||||
&& typeof candidate.browser_download_url === 'string',
|
||||
)
|
||||
if (!asset) throw new Error('Could not find MinGit 64-bit zip in latest Git for Windows release')
|
||||
return asset.browser_download_url
|
||||
}
|
||||
|
||||
let url
|
||||
try {
|
||||
url = await latestMinGitUrl()
|
||||
} catch (err) {
|
||||
console.error(`Failed to resolve Git for Windows download URL: ${err instanceof Error ? err.message : String(err)}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const file = url.split('/').pop() || 'mingit.zip'
|
||||
const archivePath = resolve(tmpdir(), file)
|
||||
|
||||
console.log(`Fetching ${url}`)
|
||||
const curl = spawnSync('curl', ['-fL', '--retry', '3', '-o', archivePath, url], { stdio: 'inherit' })
|
||||
if (curl.status !== 0) {
|
||||
console.error('curl failed')
|
||||
process.exit(curl.status ?? 1)
|
||||
}
|
||||
|
||||
console.log(`Extracting into ${OUT_DIR}`)
|
||||
const extract = spawnSync('tar', ['-xf', archivePath, '-C', OUT_DIR], { stdio: 'inherit' })
|
||||
if (extract.status !== 0) {
|
||||
console.error('extract failed')
|
||||
process.exit(extract.status ?? 1)
|
||||
}
|
||||
|
||||
rmSync(archivePath, { force: true })
|
||||
console.log(`Git for Windows ready at ${OUT_DIR}`)
|
||||
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env node
|
||||
// Download a portable Node.js runtime for the current (or target) platform/arch
|
||||
// and extract into resources/node/<os>-<arch>/.
|
||||
import { existsSync, mkdirSync, rmSync } from 'node:fs'
|
||||
import { dirname, join, resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { arch as osArch, platform as osPlatform, tmpdir } from 'node:os'
|
||||
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 NODE_VERSION = (process.env.HERMES_DESKTOP_NODE_VERSION || process.env.NODE_VERSION || process.versions.node).replace(/^v/, '')
|
||||
|
||||
const OS_LABEL = TARGET_OS === 'win32' ? 'win' : TARGET_OS === 'darwin' ? 'mac' : TARGET_OS
|
||||
const OUT_DIR = resolve(ROOT, 'resources', 'node', `${OS_LABEL}-${TARGET_ARCH}`)
|
||||
|
||||
const DIST_PLATFORM = TARGET_OS === 'win32' ? 'win' : TARGET_OS === 'darwin' ? 'darwin' : TARGET_OS
|
||||
const DIST_ARCH = TARGET_ARCH === 'x64' ? 'x64' : TARGET_ARCH === 'arm64' ? 'arm64' : ''
|
||||
if (!DIST_ARCH || !['win', 'darwin', 'linux'].includes(DIST_PLATFORM)) {
|
||||
console.error(`Unsupported target: ${TARGET_OS}-${TARGET_ARCH}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const ext = TARGET_OS === 'win32' ? 'zip' : 'tar.gz'
|
||||
const file = `node-v${NODE_VERSION}-${DIST_PLATFORM}-${DIST_ARCH}.${ext}`
|
||||
const baseUrl = (process.env.NODE_DIST_BASE_URL || 'https://nodejs.org/dist').replace(/\/$/, '')
|
||||
const url = `${baseUrl}/v${NODE_VERSION}/${file}`
|
||||
const marker = TARGET_OS === 'win32' ? 'node.exe' : join('bin', 'node')
|
||||
|
||||
if (existsSync(resolve(OUT_DIR, marker))) {
|
||||
console.log(`Node.js already present at ${OUT_DIR}, skipping`)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
mkdirSync(OUT_DIR, { recursive: true })
|
||||
const archivePath = resolve(tmpdir(), file)
|
||||
|
||||
console.log(`Fetching ${url}`)
|
||||
const curl = spawnSync('curl', ['-fL', '--retry', '3', '-o', archivePath, url], { stdio: 'inherit' })
|
||||
if (curl.status !== 0) {
|
||||
console.error('curl failed')
|
||||
process.exit(curl.status ?? 1)
|
||||
}
|
||||
|
||||
console.log(`Extracting into ${OUT_DIR}`)
|
||||
let extract
|
||||
if (TARGET_OS === 'win32') {
|
||||
extract = spawnSync('tar', ['-xf', archivePath, '-C', OUT_DIR, '--strip-components=1'], { stdio: 'inherit' })
|
||||
} else {
|
||||
extract = spawnSync('tar', ['-xzf', archivePath, '-C', OUT_DIR, '--strip-components=1'], { stdio: 'inherit' })
|
||||
}
|
||||
if (extract.status !== 0) {
|
||||
console.error('extract failed')
|
||||
process.exit(extract.status ?? 1)
|
||||
}
|
||||
|
||||
rmSync(archivePath, { force: true })
|
||||
console.log(`Node.js ready at ${OUT_DIR}`)
|
||||
@@ -18,13 +18,14 @@ import { basename, resolve, dirname, join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import { platform as osPlatform, arch as osArch, homedir as osHomedir } from 'node:os'
|
||||
import { hermesVersion } from './runtime-config.mjs'
|
||||
|
||||
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 HERMES_VERSION = process.env.HERMES_VERSION || '0.15.2'
|
||||
const HERMES_VERSION = hermesVersion()
|
||||
// Match the packaged runtime to the channel list exposed at /hermes/channels.
|
||||
// Telegram, Discord, and Slack are covered by "messaging". We intentionally
|
||||
// install Matrix's plaintext deps below instead of using the "matrix" extra:
|
||||
@@ -59,6 +60,7 @@ const SKIP_BROWSER_RUNTIME = process.env.HERMES_SKIP_BROWSER_RUNTIME === '1'
|
||||
|
||||
const OS_LABEL = TARGET_OS === 'win32' ? 'win' : TARGET_OS === 'darwin' ? 'mac' : TARGET_OS
|
||||
const PY_DIR = resolve(ROOT, 'resources', 'python', `${OS_LABEL}-${TARGET_ARCH}`)
|
||||
const NODE_DIR = resolve(ROOT, 'resources', 'node', `${OS_LABEL}-${TARGET_ARCH}`)
|
||||
const NODE_PREFIX = resolve(PY_DIR, 'node')
|
||||
const AGENT_BROWSER_HOME = resolve(PY_DIR, 'agent-browser')
|
||||
const PLAYWRIGHT_BROWSERS_PATH = resolve(PY_DIR, 'ms-playwright')
|
||||
@@ -96,7 +98,8 @@ function optionalRun(command, args, options = {}) {
|
||||
|
||||
function commandInvocation(command) {
|
||||
if (TARGET_OS === 'win32' && command.toLowerCase().endsWith('.cmd')) {
|
||||
return { command: 'cmd.exe', argsPrefix: ['/d', '/s', '/c', command] }
|
||||
const cmdCommand = /[\s&()[\]{}^=;!'+,`~]/.test(command) ? `"${command}"` : command
|
||||
return { command: 'cmd.exe', argsPrefix: ['/d', '/s', '/c', cmdCommand] }
|
||||
}
|
||||
return { command, argsPrefix: [] }
|
||||
}
|
||||
@@ -109,15 +112,25 @@ function optionalRunInvocation(invocation, args, options = {}) {
|
||||
return optionalRun(invocation.command, [...invocation.argsPrefix, ...args], options)
|
||||
}
|
||||
|
||||
function pythonBuildEnv() {
|
||||
if (TARGET_OS !== 'darwin') return process.env
|
||||
|
||||
const env = { ...process.env }
|
||||
if (!env.AR && existsSync('/usr/bin/ar')) env.AR = '/usr/bin/ar'
|
||||
if (!env.RANLIB && existsSync('/usr/bin/ranlib')) env.RANLIB = '/usr/bin/ranlib'
|
||||
return env
|
||||
}
|
||||
|
||||
function installPythonPackages(packages, label) {
|
||||
if (packages.length === 0) return
|
||||
const env = pythonBuildEnv()
|
||||
if (hasUv()) {
|
||||
console.log(`→ Installing ${label} via uv: ${packages.join(' ')}`)
|
||||
run('uv', [
|
||||
'pip', 'install',
|
||||
'--python', pyBin,
|
||||
'pip', 'install',
|
||||
'--python', pyBin,
|
||||
...packages,
|
||||
])
|
||||
], { env })
|
||||
} else {
|
||||
console.log(`→ Installing ${label} via pip: ${packages.join(' ')}`)
|
||||
run(pyBin, [
|
||||
@@ -125,17 +138,21 @@ function installPythonPackages(packages, label) {
|
||||
...packages,
|
||||
'--no-warn-script-location',
|
||||
'--disable-pip-version-check',
|
||||
])
|
||||
], { env })
|
||||
}
|
||||
}
|
||||
|
||||
function npmCommand() {
|
||||
const bundled = TARGET_OS === 'win32'
|
||||
? resolve(NODE_DIR, 'npm.cmd')
|
||||
: resolve(NODE_DIR, 'bin', 'npm')
|
||||
const candidates = TARGET_OS === 'win32'
|
||||
? ['npm.cmd', 'npm.exe', 'npm']
|
||||
: ['npm']
|
||||
? [bundled, 'npm.cmd', 'npm.exe', 'npm']
|
||||
: [bundled, 'npm']
|
||||
for (const candidate of candidates) {
|
||||
if (candidate === bundled && !existsSync(candidate)) continue
|
||||
const invocation = commandInvocation(candidate)
|
||||
const result = optionalRunInvocation(invocation, ['--version'], { stdio: 'ignore' })
|
||||
const result = optionalRunInvocation(invocation, ['--version'], { stdio: 'ignore', env: browserRuntimeEnv(false) })
|
||||
if (result.status === 0) return invocation
|
||||
}
|
||||
return null
|
||||
@@ -148,16 +165,24 @@ function agentBrowserCommand() {
|
||||
return resolve(NODE_PREFIX, 'bin', 'agent-browser')
|
||||
}
|
||||
|
||||
function browserRuntimeEnv() {
|
||||
function browserRuntimeEnv(includeAgentBrowser = true) {
|
||||
const bundledNodeBin = TARGET_OS === 'win32'
|
||||
? NODE_DIR
|
||||
: resolve(NODE_DIR, 'bin')
|
||||
const nodePath = TARGET_OS === 'win32'
|
||||
? NODE_PREFIX
|
||||
: resolve(NODE_PREFIX, 'bin')
|
||||
const inheritedPath = process.env.PATH || process.env.Path || ''
|
||||
const pathKey = TARGET_OS === 'win32' ? 'Path' : 'PATH'
|
||||
const browserExecutable = ensureBundledBrowserExecutable()
|
||||
const browserExecutable = includeAgentBrowser ? ensureBundledBrowserExecutable() : null
|
||||
const pathEntries = includeAgentBrowser
|
||||
? [nodePath, bundledNodeBin, inheritedPath]
|
||||
: [bundledNodeBin, inheritedPath]
|
||||
const env = {
|
||||
...process.env,
|
||||
[pathKey]: [nodePath, inheritedPath].filter(Boolean).join(TARGET_OS === 'win32' ? ';' : ':'),
|
||||
[pathKey]: pathEntries.filter(Boolean).join(TARGET_OS === 'win32' ? ';' : ':'),
|
||||
HERMES_AGENT_NODE: TARGET_OS === 'win32' ? resolve(NODE_DIR, 'node.exe') : resolve(NODE_DIR, 'bin', 'node'),
|
||||
HERMES_AGENT_NODE_ROOT: NODE_DIR,
|
||||
AGENT_BROWSER_HOME,
|
||||
PLAYWRIGHT_BROWSERS_PATH,
|
||||
}
|
||||
@@ -447,4 +472,8 @@ if (!SKIP_BROWSER_RUNTIME) {
|
||||
], { env: browserRuntimeEnv() })
|
||||
}
|
||||
|
||||
console.log('✓ hermes Python, MCP, websockets, agent-browser, and Chromium checks passed')
|
||||
if (SKIP_BROWSER_RUNTIME) {
|
||||
console.log('✓ hermes Python, MCP, and websockets checks passed; browser runtime skipped')
|
||||
} else {
|
||||
console.log('✓ hermes Python, MCP, websockets, agent-browser, and Chromium checks passed')
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
#!/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 })
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env node
|
||||
import { arch as osArch, platform as osPlatform } from 'node:os'
|
||||
import { hermesVersion, runtimeReleaseTag } from './runtime-config.mjs'
|
||||
|
||||
const TARGET_OS = process.env.TARGET_OS || osPlatform()
|
||||
const TARGET_ARCH = process.env.TARGET_ARCH || osArch()
|
||||
const HERMES_VERSION = hermesVersion()
|
||||
const RUNTIME_RELEASE_TAG = runtimeReleaseTag()
|
||||
const OS_LABEL = TARGET_OS === 'win32' ? 'win' : TARGET_OS === 'darwin' ? 'mac' : TARGET_OS
|
||||
|
||||
if (!['win', 'mac', 'linux'].includes(OS_LABEL) || !['x64', 'arm64'].includes(TARGET_ARCH)) {
|
||||
console.error(`Unsupported runtime target: ${TARGET_OS}-${TARGET_ARCH}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const platform = `${OS_LABEL}-${TARGET_ARCH}`
|
||||
const asset = `hermes-runtime-hermes-agent-${HERMES_VERSION}-${platform}.tar.gz`
|
||||
const manifest = `hermes-runtime-${platform}.json`
|
||||
|
||||
if (process.argv.includes('--manifest')) {
|
||||
console.log(manifest)
|
||||
} else if (process.argv.includes('--platform')) {
|
||||
console.log(platform)
|
||||
} else if (process.argv.includes('--release-tag')) {
|
||||
console.log(RUNTIME_RELEASE_TAG)
|
||||
} else {
|
||||
console.log(asset)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
export const DEFAULT_HERMES_VERSION = '0.15.2'
|
||||
|
||||
export function hermesVersion(env = process.env) {
|
||||
return env.HERMES_VERSION || DEFAULT_HERMES_VERSION
|
||||
}
|
||||
|
||||
export function runtimeReleaseTag(env = process.env) {
|
||||
const version = hermesVersion(env)
|
||||
return env.HERMES_DESKTOP_RUNTIME_RELEASE_TAG
|
||||
|| env.RUNTIME_RELEASE_TAG
|
||||
|| `hermes-${version}-runtime`
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env node
|
||||
import { mkdirSync, writeFileSync } from 'node:fs'
|
||||
import { dirname, resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { runtimeReleaseTag } from './runtime-config.mjs'
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
const ROOT = resolve(__dirname, '..')
|
||||
const outFile = resolve(ROOT, 'build', 'runtime-release.json')
|
||||
const tag = runtimeReleaseTag()
|
||||
|
||||
mkdirSync(dirname(outFile), { recursive: true })
|
||||
writeFileSync(outFile, JSON.stringify({ tag }, null, 2) + '\n')
|
||||
console.log(`Runtime release metadata: ${tag}`)
|
||||
Reference in New Issue
Block a user