From 9c149f3bb311da4609c3b18a4c8bb098039d6fcb Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Fri, 28 Nov 2025 10:51:43 +0100 Subject: [PATCH] Create and run build.js --- .github/workflows/create-artifact.yml | 34 ++----- .gitignore | 6 +- build.js | 90 +++++++++++++++++++ package-lock.json | 1 + package.json | 3 +- packages/safe-chain/bin/safe-chain.js | 25 ++++-- .../safe-chain/src/shell-integration/setup.js | 4 +- 7 files changed, 126 insertions(+), 37 deletions(-) create mode 100644 build.js diff --git a/.github/workflows/create-artifact.yml b/.github/workflows/create-artifact.yml index 44d2e3d..7716312 100644 --- a/.github/workflows/create-artifact.yml +++ b/.github/workflows/create-artifact.yml @@ -3,9 +3,7 @@ name: Create binaries on: pull_request jobs: - create-binaries: - name: Create binary for ${{ matrix.os }}-${{ matrix.arch }} runs-on: ${{ matrix.runner }} @@ -17,32 +15,32 @@ jobs: arch: x64 runner: macos-15-intel target: node22-macos-x64 - extension: '' + extension: "" - os: macos arch: arm64 runner: macos-latest target: node22-macos-arm64 - extension: '' + extension: "" - os: linux arch: x64 runner: ubuntu-latest target: node22-linux-x64 - extension: '' + extension: "" - os: linux arch: arm64 runner: ubuntu-24.04-arm target: node22-linux-arm64 - extension: '' + extension: "" - os: win arch: x64 runner: windows-latest target: node22-win-x64 - extension: '.exe' + extension: ".exe" - os: win arch: arm64 runner: windows-11-arm target: node22-win-arm64 - extension: '.exe' + extension: ".exe" steps: - name: Checkout code @@ -51,10 +49,9 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v3 with: - node-version: "lts/*" + node-version: "22.x" - name: Setup safe-chain - if: matrix.os != 'win' run: | npm i -g @aikidosec/safe-chain safe-chain setup-ci @@ -65,23 +62,10 @@ jobs: - name: Create binary (Unix) if: matrix.os != 'win' run: | - npm i -g esbuild@0.27.0 @yao-pkg/pkg@6.10.1 - mkdir -p "dist/${{ matrix.os }}-${{ matrix.arch }}" - esbuild "./packages/safe-chain/bin/safe-chain.js" --bundle --platform=node --target=node22 > "./dist/safe-chain.cjs" - pkg "./dist/safe-chain.cjs" --targets ${{ matrix.target }} --output "./dist/${{ matrix.os }}-${{ matrix.arch }}/safe-chain${{ matrix.extension }}" - shell: bash - - - name: Create binary (Windows) - if: matrix.os == 'win' - run: | - npm i -g esbuild@0.27.0 @yao-pkg/pkg@6.10.1 - New-Item -ItemType Directory -Force -Path "dist/${{ matrix.os }}-${{ matrix.arch }}" - esbuild "./packages/safe-chain/bin/safe-chain.js" --bundle --platform=node --target=node22 | Out-File -FilePath "./dist/safe-chain.cjs" -Encoding utf8 - pkg "./dist/safe-chain.cjs" --targets ${{ matrix.target }} --output "./dist/${{ matrix.os }}-${{ matrix.arch }}/safe-chain${{ matrix.extension }}" - shell: powershell + node build.js ${{ matrix.target }} - name: Upload binary artifact uses: actions/upload-artifact@v4 with: name: safe-chain-${{ matrix.os }}-${{ matrix.arch }} - path: dist/${{ matrix.os }}-${{ matrix.arch }}/* \ No newline at end of file + path: dist/* diff --git a/.gitignore b/.gitignore index acae695..7c44b34 100644 --- a/.gitignore +++ b/.gitignore @@ -143,4 +143,8 @@ vite.config.ts.timestamp-* # AI Claude.md .claude -.reference \ No newline at end of file +.reference + +# Build files +build/ +dist/ diff --git a/build.js b/build.js new file mode 100644 index 0000000..55ad3ec --- /dev/null +++ b/build.js @@ -0,0 +1,90 @@ +import { build } from "esbuild"; +import { mkdir, cp, rm, readFile, writeFile } from "node:fs/promises"; +import { spawn } from "node:child_process"; + +const target = process.argv[2]; +if (!target) { + // eslint-disable-next-line no-console + console.error("Usage: node build.js "); + // eslint-disable-next-line no-console + console.error("Example: node build.js node22-macos-arm64"); + process.exit(1); +} + +(async function () { + await clearOutputFolder(); + await bundleSafeChain(); + await copyShellScripts(); + await copyAndModifyPackageJson(); + await buildSafeChainBinary(target); +})(); + +async function clearOutputFolder() { + await rm("./build", { recursive: true, force: true }); + await mkdir("./build"); +} + +async function bundleSafeChain() { + await build({ + entryPoints: ["./packages/safe-chain/bin/safe-chain.js"], + bundle: true, + platform: "node", + target: "node22", + outfile: "./build/bin/safe-chain.cjs", + }); +} + +async function copyShellScripts() { + await mkdir("./build/bin/startup-scripts", { recursive: true }); + await cp( + "./packages/safe-chain/src/shell-integration/startup-scripts/", + "./build/bin/startup-scripts", + { recursive: true } + ); +} +async function copyAndModifyPackageJson() { + const packageJsonContent = await readFile( + "./packages/safe-chain/package.json", + "utf-8" + ); + const packageJson = JSON.parse(packageJsonContent); + + delete packageJson.main; + delete packageJson.scripts; + delete packageJson.exports; + delete packageJson.dependencies; + delete packageJson.devDependencies; + + packageJson.bin = { + "safe-chain": "bin/safe-chain.cjs", + }; + packageJson.type = "commonjs"; + packageJson.pkg = { + outputPath: "dist", + assets: ["node_modules/certifi/**/*", "bin/startup-scripts/**/*"], + }; + + await writeFile("./build/package.json", JSON.stringify(packageJson, null, 2)); + + return packageJson; +} + +function buildSafeChainBinary(target) { + return new Promise((resolve, reject) => { + const pkg = spawn( + "npx", + ["pkg", "./build/package.json", `--target=${target}`], + { + stdio: "inherit", + } + ); + + pkg.on("close", (code) => { + if (code !== 0) { + reject(new Error(`pkg process exited with code ${code}`)); + } else { + resolve(); + } + }); + }); +} diff --git a/package-lock.json b/package-lock.json index a720a7f..16f42c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "test/e2e" ], "devDependencies": { + "esbuild": "^0.27.0", "oxlint": "^1.22.0" } }, diff --git a/package.json b/package.json index aa40862..8428fe4 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "author": "Aikido Security", "license": "AGPL-3.0-or-later", "devDependencies": { - "oxlint": "^1.22.0" + "oxlint": "^1.22.0", + "esbuild": "^0.27.0" } } diff --git a/packages/safe-chain/bin/safe-chain.js b/packages/safe-chain/bin/safe-chain.js index 738c42b..667a880 100755 --- a/packages/safe-chain/bin/safe-chain.js +++ b/packages/safe-chain/bin/safe-chain.js @@ -1,7 +1,6 @@ #!/usr/bin/env node import chalk from "chalk"; -import { createRequire } from "module"; import { ui } from "../src/environment/userInteraction.js"; import { setup } from "../src/shell-integration/setup.js"; import { teardown } from "../src/shell-integration/teardown.js"; @@ -10,6 +9,8 @@ import { initializeCliArguments } from "../src/config/cliArguments.js"; import { ECOSYSTEM_JS, setEcoSystem } from "../src/config/settings.js"; import { initializePackageManager } from "../src/packagemanager/currentPackageManager.js"; import { main } from "../src/main.js"; +import path from "path"; +import fs from "fs"; if (process.argv.length < 3) { ui.writeError("No command provided. Please provide a command to execute."); @@ -25,6 +26,7 @@ const command = process.argv[2]; const pkgManagerCommands = ["npm", "npx", "yarn"]; if (pkgManagerCommands.includes(command)) { + ui.writeInformation(process.argv.join(", ")); setEcoSystem(ECOSYSTEM_JS); initializePackageManager(command); (async () => { @@ -34,14 +36,16 @@ if (pkgManagerCommands.includes(command)) { } else if (command === "help" || command === "--help" || command === "-h") { writeHelp(); process.exit(0); -}else if (command === "setup") { +} else if (command === "setup") { setup(); } else if (command === "teardown") { teardown(); } else if (command === "setup-ci") { setupCi(); } else if (command === "--version" || command === "-v" || command === "-v") { - ui.writeInformation(`Current safe-chain version: ${getVersion()}`); + (async () => { + ui.writeInformation(`Current safe-chain version: ${await getVersion()}`); + })(); } else { ui.writeError(`Unknown command: ${command}.`); ui.emptyLine(); @@ -97,8 +101,15 @@ function writeHelp() { ui.emptyLine(); } -function getVersion() { - const require = createRequire(import.meta.url); - const packageJson = require("../package.json"); - return packageJson.version; +async function getVersion() { + const packageJsonPath = path.join(__dirname, "..", "package.json"); + + const data = await fs.promises.readFile(packageJsonPath); + const json = JSON.parse(data.toString("utf8")); + + if (json && json.version) { + return json.version; + } + + return "1.0.0"; } diff --git a/packages/safe-chain/src/shell-integration/setup.js b/packages/safe-chain/src/shell-integration/setup.js index e734858..cd54bb8 100644 --- a/packages/safe-chain/src/shell-integration/setup.js +++ b/packages/safe-chain/src/shell-integration/setup.js @@ -103,9 +103,7 @@ function copyStartupFiles() { } // Use absolute path for source - const __filename = fileURLToPath(import.meta.url); - const __dirname = path.dirname(__filename); - const sourcePath = path.resolve( + const sourcePath = path.join( __dirname, includePython() ? "startup-scripts/include-python" : "startup-scripts", file