#!/usr/bin/env node const { execSync } = require('child_process'); const fs = require('fs'); const path = require('path'); const { pathToFileURL } = require('url'); const { parseArgs } = require('util'); function execCommand(command, description) { try { console.log(`${description}...`); execSync(command, { stdio: 'inherit' }); } catch (error) { console.error(`Error: ${description} failed`); process.exit(1); } } function getContainerTool() { // Check for Docker first (primary tool) try { execSync('docker --version', { stdio: 'ignore' }); return 'docker'; } catch (error) { // Fall back to Apple's container command try { execSync('container --version', { stdio: 'ignore' }); return 'container'; } catch (error) { console.error('Error: No container tool found. Please install Docker or Apple Container Tools to build container images.'); process.exit(1); } } } async function generateVersionFile() { const versionModuleUrl = pathToFileURL(path.join(__dirname, 'version.mjs')).href; const { generateVersionFile: generate } = await import(versionModuleUrl); const versionFilePath = path.join(__dirname, '..', 'src', 'version.js'); generate(versionFilePath); return versionFilePath; } function readVersionFile(versionFilePath) { const contents = fs.readFileSync(versionFilePath, 'utf8'); const versionMatch = contents.match(/export const VERSION = '([^']+)';/); const releaseMatch = contents.match(/export const IS_RELEASE = (true|false);/); if (!versionMatch || !releaseMatch) { throw new Error(`Could not parse version file at ${versionFilePath}`); } return { version: versionMatch[1], isRelease: releaseMatch[1] === 'true' }; } function getHostArchitecture() { // Map Node.js architecture names to container architecture names const archMap = { 'arm64': 'arm64', 'arm': 'arm64', 'x64': 'amd64' }; return archMap[process.arch] || 'arm64'; } function showHelp() { const hostArch = getHostArchitecture(); console.log(`Build multi-architecture container images for JMESPath Playground Usage: build-image.js [OPTIONS] Options: --all-arch Build for both arm64 and amd64 (default: build for host architecture only) --arch Target architecture (arm64 or amd64). Can be specified multiple times. --help, -h Show this help message and exit Examples: build-image.js # Builds for ${hostArch} only (host architecture) build-image.js --all-arch # Builds for both arm64 and amd64 build-image.js --arch arm64 # Builds for arm64 only build-image.js --arch arm64 --arch amd64 # Explicitly specify both build-image.js -h # Show help`); } async function main() { const { values } = parseArgs({ options: { help: { type: 'boolean', short: 'h', description: 'Show help' }, 'all-arch': { type: 'boolean', description: 'Build for both arm64 and amd64' }, arch: { type: 'string', multiple: true, description: 'Target architecture (arm64 or amd64)' } }, strict: true, allowPositionals: false }); if (values.help) { showHelp(); process.exit(0); } const containerTool = getContainerTool(); const versionFilePath = await generateVersionFile(); const { version, isRelease } = readVersionFile(versionFilePath); let architectures; if (values['all-arch']) { architectures = ['arm64', 'amd64']; } else if (values.arch && values.arch.length > 0) { architectures = values.arch; } else { architectures = [getHostArchitecture()]; } console.log(`Building ${isRelease ? 'release' : 'development'} version: ${version}`); console.log(`Target architectures: ${architectures.join(', ')}`); // Build container image const tags = isRelease ? [ `-t skoszewski/jmespath-playground:${version}`, `-t skoszewski/jmespath-playground:latest` ].join(' ') : [ `-t skoszewski/jmespath-playground:dev`, `-t skoszewski/jmespath-playground:latest` ].join(' '); const archFlags = architectures.map(arch => `--arch ${arch}`).join(' '); const buildCommand = `${containerTool} build ${archFlags} --build-arg VERSION="${version}" --build-arg IS_RELEASE="${isRelease}" ${tags} .`; execCommand(buildCommand, 'Building container image'); console.log('Container image build completed successfully!'); // Show usage instructions console.log(`\nUsage examples:`); console.log(` build-image.js # Builds for host architecture only`); console.log(` build-image.js --all-arch # Builds for both arm64 and amd64`); console.log(` build-image.js --arch arm64 # Builds for arm64 only`); console.log(` build-image.js --arch arm64 --arch amd64 # Explicitly specify both`); if (isRelease) { console.log(`\nTo run the container:`); console.log(` ${containerTool} run --arch arm64 --name jmespathpg -p 3000:3000 skoszewski/jmespath-playground:${version}`); if (containerTool === 'docker') { console.log(`\nTo push to Docker Hub:`); console.log(` docker push skoszewski/jmespath-playground:${version}`); console.log(` docker push skoszewski/jmespath-playground:latest`); } } else { console.log(`\nTo run the container:`); console.log(` ${containerTool} run --arch arm64 --name jmespathpg -p 3000:3000 skoszewski/jmespath-playground:dev`); } } if (require.main === module) { main().catch((error) => { console.error(`Error: ${error.message}`); process.exit(1); }); }