#!/usr/bin/env node/* eslint-disable @typescript-eslint/explicit-function-return-type */ // Downloads a micromamba binary for one conda subdir and writes it to a destination path.//// node scripts/fetch-micromamba.mjs <subdir> <destPath> [primary|compatibility]// subdir one of: osx-arm64 | osx-64 | linux-64 | linux-aarch64 | win-64// destPath full path to write the binary to (e.g. resources/bin/mac/arm64/micromamba)//// Source is the pinned release in mamba-org/micromamba-releases, the official mirror of conda-forge// micromamba executables. Fetching the release asset directly avoids micro.mamba.pm re-packaging the// same version URL with different archive bytes. The .tar.bz2 binary lives at bin/micromamba (POSIX)// or Library/bin/micromamba.exe (Windows); we extract to a temp dir with the system `tar` (present on// every CI runner — GNU tar on Linux, bsdtar on macOS/Windows, all bzip2-capable) and copy the located// binary to destPath (chmod +x on POSIX).//// The version and the per-subdir SHA256 come from scripts/micromamba-versions.json (MICROMAMBA_VERSION// may override the version for dev). This binary is signed/notarized with the app, so we PIN the// version (never `latest`) and VERIFY the downloaded .tar.bz2 against the pinned digest before copying// anything — a missing pinned digest or a mismatch is fatal, keeping releases reproducible and// tamper-evident. See that file for how to repin/regenerate the digests.import { execFileSync } from 'node:child_process'import { createHash } from 'node:crypto'import { chmodSync, copyFileSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, writeFileSync} from 'node:fs'import { tmpdir } from 'node:os'import { dirname, join } from 'node:path'import { fileURLToPath } from 'node:url' const SUBDIRS = new Set(['osx-arm64', 'osx-64', 'linux-64', 'linux-aarch64', 'win-64']) // Pinned version + digests, loaded from the sibling JSON (kept out of code so repinning is a data edit).const PINNED = JSON.parse( readFileSync(join(dirname(fileURLToPath(import.meta.url)), 'micromamba-versions.json'), 'utf8')) // Recursively finds the micromamba binary basename under a directory.const findBinary = (dir, name) => { for (const entry of readdirSync(dir, { withFileTypes: true })) { const full = join(dir, entry.name) if (entry.isDirectory()) { const hit = findBinary(full, name) if (hit) return hit } else if (entry.name === name) { return full } } return undefined} // Resolves the effective version, honoring MICROMAMBA_VERSION only when it equals the pinned version.// The digests in micromamba-versions.json cover PINNED.version alone, so an override to any other// version would download a different archive that can never match — fail fast with actionable guidance// instead of a confusing "sha256 mismatch" after the download. `env` defaults to process.env for tests.const resolvePin = (variant = 'primary') => { if (variant === 'primary') return PINNED if (variant === 'compatibility') return PINNED.compatibility throw new Error(`unknown micromamba runner variant "${variant}"`)} const resolveVersion = (env = process.env, variant = 'primary') => { const pin = resolvePin(variant) if (variant === 'compatibility') return pin.version const override = env.MICROMAMBA_VERSION if (override && override !== pin.version) { throw new Error( `MICROMAMBA_VERSION=${override} does not match the pinned ${pin.version}; ` + 'update scripts/micromamba-versions.json (version + all 5 sha256 digests) to repin, ' + 'rather than overriding at runtime' ) } return pin.version} const resolveDownloadUrl = (subdir, variant = 'primary') => { const pin = resolvePin(variant) if (!pin.releaseTag) { throw new Error('no pinned releaseTag in micromamba-versions.json') } if (!pin.sha256?.[subdir]) { throw new Error(`no pinned sha256 for subdir "${subdir}" in micromamba-versions.json`) } return ( `https://github.com/mamba-org/micromamba-releases/releases/download/${pin.releaseTag}/` + `micromamba-${subdir}.tar.bz2` )} // Throws unless `buffer` (the downloaded .tar.bz2) matches the pinned SHA256 for `subdir`. A missing// pinned digest is treated as fatal, so an unverifiable subdir can never ship.const verifyArchiveDigest = (buffer, subdir, variant = 'primary') => { const expected = resolvePin(variant).sha256?.[subdir] if (!expected) { throw new Error(`no pinned sha256 for subdir "${subdir}" in micromamba-versions.json`) } const actual = createHash('sha256').update(buffer).digest('hex') if (actual !== expected) { throw new Error( `sha256 mismatch for ${subdir}: expected ${expected}, got ${actual} — refusing to use this binary` ) }} const verifyBinaryDigest = (buffer, subdir, variant = 'primary') => { const expected = resolvePin(variant).binarySha256?.[subdir] if (!expected) { throw new Error(`no pinned binary sha256 for subdir "${subdir}" in micromamba-versions.json`) } const actual = createHash('sha256').update(buffer).digest('hex') if (actual !== expected) { throw new Error( `binary sha256 mismatch for ${subdir}: expected ${expected}, got ${actual} — refusing to use this binary` ) }} const main = async () => { const [subdir, destPath, variant = 'primary'] = process.argv.slice(2) if (!subdir || !destPath) { console.error( 'usage: node scripts/fetch-micromamba.mjs <subdir> <destPath> [primary|compatibility]' ) process.exit(1) } if (!SUBDIRS.has(subdir)) { console.error(`unknown subdir "${subdir}" (expected one of ${[...SUBDIRS].join(', ')})`) process.exit(1) } const binName = subdir === 'win-64' ? 'micromamba.exe' : 'micromamba' const pin = resolvePin(variant) const version = resolveVersion(process.env, variant) const url = resolveDownloadUrl(subdir, variant) console.log(`[fetch-micromamba] ${subdir} ${version} -> ${destPath}`) const res = await fetch(url) if (!res.ok) throw new Error(`download failed ${res.status}: ${url}`) const archive = Buffer.from(await res.arrayBuffer()) // Verify the downloaded archive against the pinned digest BEFORE extracting or copying anything. verifyArchiveDigest(archive, subdir, variant) const staging = mkdtempSync(join(tmpdir(), 'mm-')) const archiveName = 'micromamba.tar.bz2' writeFileSync(join(staging, archiveName), archive) // Extract with cwd=staging and a RELATIVE archive name (no -C absolute path). On Windows, GNU tar // reads a drive-letter path like `C:\Users\...` as a remote `host:path` spec and fails with // "Cannot connect to C: resolve failed"; keeping every path relative avoids the colon entirely and // still works with bsdtar on macOS (which lacks GNU's --force-local). execFileSync('tar', ['-xjf', archiveName], { cwd: staging, stdio: 'ignore' }) const found = findBinary(staging, binName) if (!found) throw new Error(`micromamba binary (${binName}) not found in the downloaded archive`) if (pin.binarySha256?.[subdir]) { verifyBinaryDigest(readFileSync(found), subdir, variant) } mkdirSync(dirname(destPath), { recursive: true }) copyFileSync(found, destPath) if (binName !== 'micromamba.exe') chmodSync(destPath, 0o755) console.log(`[fetch-micromamba] wrote ${destPath}`)} export { PINNED, SUBDIRS, resolveDownloadUrl, resolvePin, resolveVersion, verifyArchiveDigest, verifyBinaryDigest} // Run as a CLI only when invoked directly (importing for tests must not trigger a download).if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) { main().catch((err) => { console.error('[fetch-micromamba] failed:', err) process.exit(1) })}
#!/usr/bin/env node
/* eslint-disable @typescript-eslint/explicit-function-return-type */
// Downloads a micromamba binary for one conda subdir and writes it to a destination path.
//
// node scripts/fetch-micromamba.mjs <subdir> <destPath> [primary|compatibility]
// subdir one of: osx-arm64 | osx-64 | linux-64 | linux-aarch64 | win-64
// destPath full path to write the binary to (e.g. resources/bin/mac/arm64/micromamba)
//
// Source is the pinned release in mamba-org/micromamba-releases, the official mirror of conda-forge
// micromamba executables. Fetching the release asset directly avoids micro.mamba.pm re-packaging the
// same version URL with different archive bytes. The .tar.bz2 binary lives at bin/micromamba (POSIX)
// or Library/bin/micromamba.exe (Windows); we extract to a temp dir with the system `tar` (present on
// every CI runner — GNU tar on Linux, bsdtar on macOS/Windows, all bzip2-capable) and copy the located
// binary to destPath (chmod +x on POSIX).
//
// The version and the per-subdir SHA256 come from scripts/micromamba-versions.json (MICROMAMBA_VERSION
// may override the version for dev). This binary is signed/notarized with the app, so we PIN the
// version (never `latest`) and VERIFY the downloaded .tar.bz2 against the pinned digest before copying
// anything — a missing pinned digest or a mismatch is fatal, keeping releases reproducible and
// tamper-evident. See that file for how to repin/regenerate the digests.
import { execFileSync } from 'node:child_process'
import { createHash } from 'node:crypto'
import {
chmodSync,
copyFileSync,
mkdirSync,
mkdtempSync,
readdirSync,
readFileSync,
writeFileSync
} from 'node:fs'
import { tmpdir } from 'node:os'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
const SUBDIRS = new Set(['osx-arm64', 'osx-64', 'linux-64', 'linux-aarch64', 'win-64'])
// Pinned version + digests, loaded from the sibling JSON (kept out of code so repinning is a data edit).
const PINNED = JSON.parse(
readFileSync(join(dirname(fileURLToPath(import.meta.url)), 'micromamba-versions.json'), 'utf8')
)
// Recursively finds the micromamba binary basename under a directory.
const findBinary = (dir, name) => {
for (const entry of readdirSync(dir, { withFileTypes: true })) {
const full = join(dir, entry.name)
if (entry.isDirectory()) {
const hit = findBinary(full, name)
if (hit) return hit
} else if (entry.name === name) {
return full
}
}
return undefined
}
// Resolves the effective version, honoring MICROMAMBA_VERSION only when it equals the pinned version.
// The digests in micromamba-versions.json cover PINNED.version alone, so an override to any other
// version would download a different archive that can never match — fail fast with actionable guidance
// instead of a confusing "sha256 mismatch" after the download. `env` defaults to process.env for tests.
const resolvePin = (variant = 'primary') => {
if (variant === 'primary') return PINNED
if (variant === 'compatibility') return PINNED.compatibility
throw new Error(`unknown micromamba runner variant "${variant}"`)
}
const resolveVersion = (env = process.env, variant = 'primary') => {
const pin = resolvePin(variant)
if (variant === 'compatibility') return pin.version
const override = env.MICROMAMBA_VERSION
if (override && override !== pin.version) {
throw new Error(
`MICROMAMBA_VERSION=${override} does not match the pinned ${pin.version}; ` +
'update scripts/micromamba-versions.json (version + all 5 sha256 digests) to repin, ' +
'rather than overriding at runtime'
)
}
return pin.version
}
const resolveDownloadUrl = (subdir, variant = 'primary') => {
const pin = resolvePin(variant)
if (!pin.releaseTag) {
throw new Error('no pinned releaseTag in micromamba-versions.json')
}
if (!pin.sha256?.[subdir]) {
throw new Error(`no pinned sha256 for subdir "${subdir}" in micromamba-versions.json`)
}
return (
`https://github.com/mamba-org/micromamba-releases/releases/download/${pin.releaseTag}/` +
`micromamba-${subdir}.tar.bz2`
)
}
// Throws unless `buffer` (the downloaded .tar.bz2) matches the pinned SHA256 for `subdir`. A missing
// pinned digest is treated as fatal, so an unverifiable subdir can never ship.
const verifyArchiveDigest = (buffer, subdir, variant = 'primary') => {
const expected = resolvePin(variant).sha256?.[subdir]
if (!expected) {
throw new Error(`no pinned sha256 for subdir "${subdir}" in micromamba-versions.json`)
}
const actual = createHash('sha256').update(buffer).digest('hex')
if (actual !== expected) {
throw new Error(
`sha256 mismatch for ${subdir}: expected ${expected}, got ${actual} — refusing to use this binary`
)
}
}
const verifyBinaryDigest = (buffer, subdir, variant = 'primary') => {
const expected = resolvePin(variant).binarySha256?.[subdir]
if (!expected) {
throw new Error(`no pinned binary sha256 for subdir "${subdir}" in micromamba-versions.json`)
}
const actual = createHash('sha256').update(buffer).digest('hex')
if (actual !== expected) {
throw new Error(
`binary sha256 mismatch for ${subdir}: expected ${expected}, got ${actual} — refusing to use this binary`
)
}
}
const main = async () => {
const [subdir, destPath, variant = 'primary'] = process.argv.slice(2)
if (!subdir || !destPath) {
console.error(
'usage: node scripts/fetch-micromamba.mjs <subdir> <destPath> [primary|compatibility]'
)
process.exit(1)
}
if (!SUBDIRS.has(subdir)) {
console.error(`unknown subdir "${subdir}" (expected one of ${[...SUBDIRS].join(', ')})`)
process.exit(1)
}
const binName = subdir === 'win-64' ? 'micromamba.exe' : 'micromamba'
const pin = resolvePin(variant)
const version = resolveVersion(process.env, variant)
const url = resolveDownloadUrl(subdir, variant)
console.log(`[fetch-micromamba] ${subdir} ${version} -> ${destPath}`)
const res = await fetch(url)
if (!res.ok) throw new Error(`download failed ${res.status}: ${url}`)
const archive = Buffer.from(await res.arrayBuffer())
// Verify the downloaded archive against the pinned digest BEFORE extracting or copying anything.
verifyArchiveDigest(archive, subdir, variant)
const staging = mkdtempSync(join(tmpdir(), 'mm-'))
const archiveName = 'micromamba.tar.bz2'
writeFileSync(join(staging, archiveName), archive)
// Extract with cwd=staging and a RELATIVE archive name (no -C absolute path). On Windows, GNU tar
// reads a drive-letter path like `C:\Users\...` as a remote `host:path` spec and fails with
// "Cannot connect to C: resolve failed"; keeping every path relative avoids the colon entirely and
// still works with bsdtar on macOS (which lacks GNU's --force-local).
execFileSync('tar', ['-xjf', archiveName], { cwd: staging, stdio: 'ignore' })
const found = findBinary(staging, binName)
if (!found) throw new Error(`micromamba binary (${binName}) not found in the downloaded archive`)
if (pin.binarySha256?.[subdir]) {
verifyBinaryDigest(readFileSync(found), subdir, variant)
}
mkdirSync(dirname(destPath), { recursive: true })
copyFileSync(found, destPath)
if (binName !== 'micromamba.exe') chmodSync(destPath, 0o755)
console.log(`[fetch-micromamba] wrote ${destPath}`)
}
export {
PINNED,
SUBDIRS,
resolveDownloadUrl,
resolvePin,
resolveVersion,
verifyArchiveDigest,
verifyBinaryDigest
}
// Run as a CLI only when invoked directly (importing for tests must not trigger a download).
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
main().catch((err) => {
console.error('[fetch-micromamba] failed:', err)
process.exit(1)
})
}