cozy-games/scripts/ensure-java.mjs

99 lines
4.3 KiB
JavaScript

/**
* Post-install convenience: make a Java runtime available for the Firebase
* Firestore emulator (used by `pnpm -F mnswpr dev` / `db:start`). The emulator is
* a Java program and firebase-tools does not bundle a JRE.
*
* Installs a Temurin JRE 21 into the user's home (~/.local) WITHOUT sudo and
* symlinks `java` into ~/.local/bin (already on most PATHs). Properties:
* - idempotent: does nothing if `java` is already on PATH
* - non-fatal: never fails `pnpm install` — on any problem it warns, exits 0
* - opt-out: set SKIP_JRE_SETUP=1 (also auto-skips when CI is set)
* Only Linux/macOS on x64/arm64 are auto-handled; anything else prints manual
* instructions (see apps/mnswpr/README.md).
*/
import { spawnSync } from 'node:child_process'
import { existsSync, mkdirSync, readdirSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'
import { homedir, tmpdir, platform, arch } from 'node:os'
import { join } from 'node:path'
const log = msg => console.log(`[ensure-java] ${msg}`)
const warn = msg => console.warn(`[ensure-java] ${msg}`)
// This is a convenience, never a blocker — always exit 0.
try {
await main()
} catch (err) {
warn(`skipped (${err?.message || err}).`)
warn('The Firestore emulator needs Java 11+ — install it manually, see apps/mnswpr/README.md.')
}
process.exit(0)
async function main() {
if (process.env.SKIP_JRE_SETUP) return log('SKIP_JRE_SETUP set — skipping.')
if (process.env.CI) return log('CI detected — skipping JRE setup.')
if (hasJava('java')) return log('Java already on PATH — nothing to do.')
const adoptOs = { linux: 'linux', darwin: 'mac' }[platform()]
const adoptArch = { x64: 'x64', arm64: 'aarch64' }[arch()]
if (!adoptOs || !adoptArch) {
warn(`no auto-install for ${platform()}/${arch()} — install a JRE 21 manually (apps/mnswpr/README.md).`)
return
}
if (!hasTool('tar')) {
warn('`tar` not found — cannot unpack the JRE. Install Java manually (apps/mnswpr/README.md).')
return
}
const prefix = process.env.JAVA_SETUP_PREFIX || join(homedir(), '.local')
const libDir = join(prefix, 'lib')
const binDir = join(prefix, 'bin')
mkdirSync(libDir, { recursive: true })
mkdirSync(binDir, { recursive: true })
const url = `https://api.adoptium.net/v3/binary/latest/21/ga/${adoptOs}/${adoptArch}/jre/hotspot/normal/eclipse`
log(`no Java found — downloading Temurin JRE 21 (${adoptOs}/${adoptArch})…`)
const res = await fetch(url, { redirect: 'follow' })
if (!res.ok) throw new Error(`download failed: HTTP ${res.status}`)
const tgz = join(tmpdir(), `temurin-jre-21-${adoptOs}-${adoptArch}.tar.gz`)
writeFileSync(tgz, Buffer.from(await res.arrayBuffer()))
const before = new Set(readdirSync(libDir))
const untar = spawnSync('tar', ['xzf', tgz, '-C', libDir], { stdio: 'inherit' })
rmSync(tgz, { force: true })
if (untar.status !== 0) throw new Error('tar extraction failed')
// the top-level dir the tarball created (e.g. jdk-21.0.11+10-jre); on re-runs
// it already exists, so fall back to the newest matching dir.
const jreDir = readdirSync(libDir).find(d => !before.has(d) && /jdk.*jre/i.test(d))
|| readdirSync(libDir).filter(d => /jdk.*jre/i.test(d)).sort().pop()
if (!jreDir) throw new Error('could not locate the unpacked JRE directory')
// java is at bin/java (linux) or Contents/Home/bin/java (macOS)
const javaBin = [
join(libDir, jreDir, 'bin', 'java'),
join(libDir, jreDir, 'Contents', 'Home', 'bin', 'java')
].find(existsSync)
if (!javaBin) throw new Error('java binary not found in the unpacked JRE')
for (const tool of ['java', 'keytool']) {
const src = javaBin.replace(/java$/, tool)
const dst = join(binDir, tool)
if (existsSync(src)) { rmSync(dst, { force: true }); symlinkSync(src, dst) }
}
if (!hasJava(join(binDir, 'java'))) throw new Error('the installed java did not run')
log(`installed JRE 21 → ${join(libDir, jreDir)}`)
log(`linked java → ${join(binDir, 'java')}`)
if (!(process.env.PATH || '').split(':').includes(binDir)) {
warn(`${binDir} is not on your PATH — add it (e.g. in ~/.profile) so \`java\` is found.`)
}
}
function hasJava(bin) {
const r = spawnSync(bin, ['-version'], { stdio: 'ignore' })
return !r.error && r.status === 0
}
function hasTool(name) {
const r = spawnSync(name, ['--version'], { stdio: 'ignore' })
return !r.error
}