mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 12:10:49 +00:00
Add installer changes
This commit is contained in:
parent
e765ccf303
commit
2158478894
13 changed files with 674 additions and 21 deletions
254
installer/build.js
Normal file
254
installer/build.js
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Build script for creating standalone Safe Chain binaries
|
||||
* Uses esbuild to bundle ES modules, then @yao-pkg/pkg to create executable
|
||||
*/
|
||||
|
||||
import { exec } from 'node:child_process';
|
||||
import { promisify } from 'node:util';
|
||||
import { existsSync, mkdirSync, copyFileSync, writeFileSync, rmSync, readFileSync, readdirSync } from 'node:fs';
|
||||
import { join, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import * as esbuild from 'esbuild';
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const ROOT_DIR = join(__dirname, '..');
|
||||
const DIST_DIR = join(__dirname, 'dist');
|
||||
const BUNDLE_DIR = join(DIST_DIR, 'bundle');
|
||||
const SAFE_CHAIN_DIR = join(ROOT_DIR, 'packages/safe-chain');
|
||||
|
||||
/**
|
||||
* Parse command line arguments
|
||||
*/
|
||||
function parseArgs() {
|
||||
const args = process.argv.slice(2);
|
||||
const platform = args.find(arg => arg.startsWith('--platform='))?.split('=')[1] || 'macos';
|
||||
return { platform };
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure dist directory exists
|
||||
*/
|
||||
function ensureDistDirectory() {
|
||||
if (!existsSync(DIST_DIR)) {
|
||||
mkdirSync(DIST_DIR, { recursive: true });
|
||||
}
|
||||
if (!existsSync(BUNDLE_DIR)) {
|
||||
mkdirSync(BUNDLE_DIR, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bundle ES modules using esbuild
|
||||
* This converts ES modules to CommonJS that pkg can handle
|
||||
*/
|
||||
async function bundleWithEsbuild() {
|
||||
console.log('Bundling with esbuild...');
|
||||
|
||||
const entryPoint = join(SAFE_CHAIN_DIR, 'bin/safe-chain.js');
|
||||
const outputFile = join(BUNDLE_DIR, 'safe-chain-bundled.cjs');
|
||||
|
||||
await esbuild.build({
|
||||
entryPoints: [entryPoint],
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
target: 'node20',
|
||||
format: 'cjs',
|
||||
outfile: outputFile,
|
||||
external: [
|
||||
// Keep node: protocol imports external
|
||||
'node:*',
|
||||
],
|
||||
loader: {
|
||||
'.json': 'json', // Inline JSON files
|
||||
},
|
||||
banner: {
|
||||
js: `// Polyfill for import.meta.url in CommonJS
|
||||
var __filename = __filename || (() => {
|
||||
try {
|
||||
return require('url').fileURLToPath(__filename);
|
||||
} catch (e) {
|
||||
return __filename;
|
||||
}
|
||||
})();
|
||||
var __dirname = __dirname || require('path').dirname(__filename);
|
||||
var import_meta_url = typeof __filename !== 'undefined' ? require('url').pathToFileURL(__filename).href : undefined;
|
||||
`,
|
||||
},
|
||||
define: {
|
||||
'import.meta.url': 'import_meta_url',
|
||||
},
|
||||
minify: false, // Keep readable for debugging
|
||||
sourcemap: false,
|
||||
});
|
||||
|
||||
console.log('✓ Bundle created at:', outputFile);
|
||||
return outputFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build macOS binary and installer
|
||||
*/
|
||||
async function buildMacOS() {
|
||||
console.log('Building macOS binary...');
|
||||
|
||||
// Step 1: Bundle with esbuild
|
||||
const bundledFile = await bundleWithEsbuild();
|
||||
|
||||
// Step 2: Package with pkg
|
||||
const targetPlatform = 'node20-macos-arm64';
|
||||
const outputPath = join(DIST_DIR, 'safe-chain-macos-arm64');
|
||||
|
||||
// Copy shell-integration files to a staging directory with the structure we want in /snapshot
|
||||
const stagingDir = join(DIST_DIR, 'staging');
|
||||
const stagingShellInt = join(stagingDir, 'src/shell-integration');
|
||||
|
||||
// Clean and create staging directory
|
||||
if (existsSync(stagingDir)) {
|
||||
rmSync(stagingDir, { recursive: true });
|
||||
}
|
||||
mkdirSync(stagingShellInt, { recursive: true });
|
||||
|
||||
// Copy shell-integration directory
|
||||
const shellIntegrationSrc = join(SAFE_CHAIN_DIR, 'src/shell-integration');
|
||||
|
||||
// Helper to copy directory recursively
|
||||
const copyDir = (src, dest) => {
|
||||
if (!existsSync(dest)) {
|
||||
mkdirSync(dest, { recursive: true });
|
||||
}
|
||||
const entries = readdirSync(src, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
const srcPath = join(src, entry.name);
|
||||
const destPath = join(dest, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
copyDir(srcPath, destPath);
|
||||
} else {
|
||||
copyFileSync(srcPath, destPath);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
copyDir(shellIntegrationSrc, stagingShellInt);
|
||||
|
||||
const pkgArgs = [
|
||||
bundledFile,
|
||||
'--target', targetPlatform,
|
||||
'--output', outputPath,
|
||||
'--compress', 'GZip',
|
||||
// Include contents of staging/src - files will be at /snapshot/src/shell-integration/
|
||||
`--assets=${join(stagingDir, 'src')}/**/*`,
|
||||
];
|
||||
|
||||
console.log(`Running: npx @yao-pkg/pkg ${pkgArgs.join(' ')}`);
|
||||
|
||||
// Use spawn instead of execFile to avoid issues with glob expansion
|
||||
const { spawn } = await import('node:child_process');
|
||||
|
||||
const pkgProcess = spawn('npx', ['@yao-pkg/pkg', ...pkgArgs], {
|
||||
cwd: __dirname,
|
||||
stdio: 'inherit'
|
||||
});
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
pkgProcess.on('close', (code) => {
|
||||
if (code !== 0) {
|
||||
reject(new Error(`pkg failed with code ${code}`));
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}); console.log('✓ Binary created at:', outputPath);
|
||||
|
||||
// Create installer package
|
||||
await createMacOSInstaller(outputPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create macOS installer with certificate installation
|
||||
*/
|
||||
async function createMacOSInstaller(binaryPath) {
|
||||
console.log('Creating macOS installer package...');
|
||||
|
||||
const installerDir = join(DIST_DIR, 'macos-installer');
|
||||
const scriptsDir = join(installerDir, 'scripts');
|
||||
const resourcesDir = join(installerDir, 'resources');
|
||||
|
||||
// Create directory structure
|
||||
mkdirSync(scriptsDir, { recursive: true });
|
||||
mkdirSync(resourcesDir, { recursive: true });
|
||||
|
||||
// Copy binary to resources
|
||||
const binaryDestination = join(resourcesDir, 'safe-chain');
|
||||
copyFileSync(binaryPath, binaryDestination);
|
||||
|
||||
// Read installer scripts from separate files
|
||||
const scriptsSourceDir = join(__dirname, 'scripts');
|
||||
const preinstallScript = readFileSync(join(scriptsSourceDir, 'darwin_preinstall.sh'), 'utf8');
|
||||
const postinstallScript = readFileSync(join(scriptsSourceDir, 'darwin_postinstall.sh'), 'utf8');
|
||||
const uninstallScript = readFileSync(join(scriptsSourceDir, 'darwin_uninstall.sh'), 'utf8');
|
||||
|
||||
// Write scripts to installer directory
|
||||
writeFileSync(join(scriptsDir, 'preinstall'), preinstallScript, { mode: 0o755 });
|
||||
writeFileSync(join(scriptsDir, 'postinstall'), postinstallScript, { mode: 0o755 });
|
||||
writeFileSync(join(installerDir, 'uninstall.sh'), uninstallScript, { mode: 0o755 });
|
||||
|
||||
console.log('✓ macOS installer package created at:', installerDir);
|
||||
console.log('');
|
||||
console.log('To create a .pkg installer, run:');
|
||||
console.log(` cd ${installerDir}`);
|
||||
console.log(' pkgbuild --root resources --scripts scripts --identifier com.aikido.safe-chain --version 1.0.0 --install-location /tmp/safe-chain-install SafeChain.pkg');
|
||||
}
|
||||
|
||||
/**
|
||||
* Build Linux binary and installer
|
||||
*/
|
||||
async function buildLinux() {
|
||||
// TODO: Implement Linux binary creation
|
||||
}
|
||||
|
||||
/**
|
||||
* Build Windows binary and installer
|
||||
*/
|
||||
async function buildWindows() {
|
||||
// TODO: Implement Windows binary creation
|
||||
}
|
||||
|
||||
/**
|
||||
* Main build function
|
||||
*/
|
||||
async function build() {
|
||||
const { platform } = parseArgs();
|
||||
|
||||
console.log('=== Safe Chain Installer Builder ===');
|
||||
console.log(`Platform: ${platform}`);
|
||||
console.log('');
|
||||
|
||||
ensureDistDirectory();
|
||||
|
||||
try {
|
||||
switch (platform) {
|
||||
case 'macos':
|
||||
await buildMacOS();
|
||||
break;
|
||||
case 'linux':
|
||||
await buildLinux();
|
||||
break;
|
||||
case 'windows':
|
||||
await buildWindows();
|
||||
break;
|
||||
default:
|
||||
console.error(`Unknown platform: ${platform}`);
|
||||
console.error('Valid options: macos, linux, windows');
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (error) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Run the build
|
||||
build();
|
||||
Loading…
Add table
Add a link
Reference in a new issue