diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml new file mode 100644 index 0000000..33355dc --- /dev/null +++ b/.github/workflows/npm-publish.yml @@ -0,0 +1,222 @@ +name: Publish npm Packages + +on: + push: + tags: + - "*" + workflow_dispatch: + inputs: + tag: + description: "Tag to publish from (for example: v1.7.2)" + required: true + type: string + +permissions: + contents: read + +jobs: + prepare: + runs-on: ubuntu-latest + outputs: + release_ref: ${{ steps.resolve.outputs.release_ref }} + tag_name: ${{ steps.resolve.outputs.tag_name }} + version: ${{ steps.resolve.outputs.version }} + steps: + - name: Resolve Tag and Version + id: resolve + shell: bash + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + TAG_NAME="${{ inputs.tag }}" + RELEASE_REF="refs/tags/${{ inputs.tag }}" + else + TAG_NAME="${{ github.ref_name }}" + RELEASE_REF="${{ github.ref }}" + fi + + VERSION="${TAG_NAME#v}" + echo "release_ref=${RELEASE_REF}" >> "${GITHUB_OUTPUT}" + echo "tag_name=${TAG_NAME}" >> "${GITHUB_OUTPUT}" + echo "version=${VERSION}" >> "${GITHUB_OUTPUT}" + + publish-platform-packages: + name: Publish ${{ matrix.package_name }} + needs: + - prepare + - validate-release + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - runner: ubuntu-latest + package_name: corgea-cli-linux-x64 + package_dir: packages/corgea-cli-linux-x64 + target: x86_64-unknown-linux-gnu + binary_name: corgea + - runner: ubuntu-24.04-arm + package_name: corgea-cli-linux-arm64 + package_dir: packages/corgea-cli-linux-arm64 + target: aarch64-unknown-linux-gnu + binary_name: corgea + - runner: macos-13 + package_name: corgea-cli-darwin-x64 + package_dir: packages/corgea-cli-darwin-x64 + target: x86_64-apple-darwin + binary_name: corgea + - runner: macos-14 + package_name: corgea-cli-darwin-arm64 + package_dir: packages/corgea-cli-darwin-arm64 + target: aarch64-apple-darwin + binary_name: corgea + - runner: windows-latest + package_name: corgea-cli-win32-x64 + package_dir: packages/corgea-cli-win32-x64 + target: x86_64-pc-windows-msvc + binary_name: corgea.exe + defaults: + run: + shell: bash + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ needs.prepare.outputs.release_ref }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + registry-url: https://registry.npmjs.org + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Build Binary + run: cargo build --release --target "${{ matrix.target }}" + + - name: Stage Platform Package + run: | + node scripts/npm/prepare-platform-package.js \ + "${{ matrix.package_dir }}" \ + "${{ matrix.target }}" \ + "${{ matrix.binary_name }}" \ + "${{ needs.prepare.outputs.version }}" + + - name: Publish Platform Package + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + PACKAGE_NAME: ${{ matrix.package_name }} + PACKAGE_DIR: ${{ matrix.package_dir }} + PACKAGE_VERSION: ${{ needs.prepare.outputs.version }} + run: | + if npm view "${PACKAGE_NAME}@${PACKAGE_VERSION}" version >/dev/null 2>&1; then + echo "${PACKAGE_NAME}@${PACKAGE_VERSION} already exists on npm, skipping." + exit 0 + fi + + npm publish "${PACKAGE_DIR}" --access public + + publish-main-package: + name: Publish corgea-cli + needs: + - prepare + - validate-release + - publish-platform-packages + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ needs.prepare.outputs.release_ref }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + registry-url: https://registry.npmjs.org + + - name: Validate npm Package Contents + run: npm pack --dry-run + + - name: Publish corgea-cli + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + PACKAGE_VERSION: ${{ needs.prepare.outputs.version }} + run: | + if npm view "corgea-cli@${PACKAGE_VERSION}" version >/dev/null 2>&1; then + echo "corgea-cli@${PACKAGE_VERSION} already exists on npm, skipping." + exit 0 + fi + + npm publish --access public + + validate-release: + name: Validate Release Metadata + needs: prepare + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ needs.prepare.outputs.release_ref }} + + - name: Validate package versions + env: + RELEASE_VERSION: ${{ needs.prepare.outputs.version }} + run: | + node - <<'NODE' + const fs = require("node:fs"); + const path = require("node:path"); + + const releaseVersion = process.env.RELEASE_VERSION; + const rootPackage = JSON.parse(fs.readFileSync("package.json", "utf8")); + const optionalDependencies = rootPackage.optionalDependencies || {}; + + const errors = []; + const platformPackageNames = new Set(); + + if (rootPackage.version !== releaseVersion) { + errors.push(`package.json version ${rootPackage.version} does not match release version ${releaseVersion}`); + } + + const packageRoot = "packages"; + const platformDirs = fs.readdirSync(packageRoot, { withFileTypes: true }) + .filter((entry) => entry.isDirectory()) + .map((entry) => path.join(packageRoot, entry.name)); + + for (const packageDir of platformDirs) { + const packageJsonPath = path.join(packageDir, "package.json"); + const platformPackage = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); + const expectedVersion = optionalDependencies[platformPackage.name]; + platformPackageNames.add(platformPackage.name); + + if (!expectedVersion) { + errors.push(`missing optional dependency entry for ${platformPackage.name}`); + continue; + } + + if (expectedVersion !== releaseVersion) { + errors.push(`optional dependency ${platformPackage.name} has version ${expectedVersion}, expected ${releaseVersion}`); + } + } + + for (const dependencyName of Object.keys(optionalDependencies)) { + if (!platformPackageNames.has(dependencyName)) { + errors.push(`optional dependency ${dependencyName} has no matching package under packages/`); + } + } + + if (errors.length > 0) { + console.error(errors.join("\n")); + process.exit(1); + } + NODE diff --git a/.gitignore b/.gitignore index 894c98c..321685d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ .DS_Store .dccache *.zip +/packages/*/vendor/ diff --git a/README.md b/README.md index 8bae0a2..39463f5 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,12 @@ For full documentation, visit https://docs.corgea.app/cli ## Installation +### Using npm +``` +npm install -g corgea-cli +``` +The npm package resolves a platform-specific optional dependency (for example, `corgea-cli-linux-x64`) that contains the native `corgea` binary. + ### Using pip ``` pip install corgea-cli @@ -63,4 +69,3 @@ corgea login ``` Note: After making changes to Rust code, you'll need to run `maturin develop` again to rebuild the package. - diff --git a/bin/corgea.js b/bin/corgea.js new file mode 100755 index 0000000..5bc2479 --- /dev/null +++ b/bin/corgea.js @@ -0,0 +1,135 @@ +#!/usr/bin/env node +"use strict"; + +const { spawn } = require("node:child_process"); +const { existsSync } = require("node:fs"); +const path = require("node:path"); + +const PLATFORM_PACKAGE_BY_TARGET = { + "x86_64-unknown-linux-gnu": "corgea-cli-linux-x64", + "aarch64-unknown-linux-gnu": "corgea-cli-linux-arm64", + "x86_64-apple-darwin": "corgea-cli-darwin-x64", + "aarch64-apple-darwin": "corgea-cli-darwin-arm64", + "x86_64-pc-windows-msvc": "corgea-cli-win32-x64" +}; + +function resolveTargetTriple() { + switch (process.platform) { + case "linux": + case "android": + if (process.arch === "x64") return "x86_64-unknown-linux-gnu"; + if (process.arch === "arm64") return "aarch64-unknown-linux-gnu"; + return null; + case "darwin": + if (process.arch === "x64") return "x86_64-apple-darwin"; + if (process.arch === "arm64") return "aarch64-apple-darwin"; + return null; + case "win32": + if (process.arch === "x64") return "x86_64-pc-windows-msvc"; + return null; + default: + return null; + } +} + +function detectPackageManager() { + const userAgent = process.env.npm_config_user_agent || ""; + if (/\bbun\//.test(userAgent)) return "bun"; + + const execPath = process.env.npm_execpath || ""; + if (execPath.includes("bun")) return "bun"; + + if (__dirname.includes(".bun/install/global") || __dirname.includes(".bun\\install\\global")) { + return "bun"; + } + + if (/\bpnpm\//.test(userAgent) || execPath.includes("pnpm")) return "pnpm"; + if (/\byarn\//.test(userAgent) || execPath.includes("yarn")) return "yarn"; + return userAgent ? "npm" : null; +} + +function getReinstallCommand() { + const manager = detectPackageManager(); + if (manager === "bun") return "bun install -g corgea-cli@latest"; + if (manager === "pnpm") return "pnpm add -g corgea-cli@latest"; + if (manager === "yarn") return "yarn global add corgea-cli@latest"; + return "npm install -g corgea-cli@latest"; +} + +function getUpdatedPath(newDirs) { + const pathSep = process.platform === "win32" ? ";" : ":"; + const existingPath = process.env.PATH || ""; + return [...newDirs, ...existingPath.split(pathSep).filter(Boolean)].join(pathSep); +} + +const targetTriple = resolveTargetTriple(); +if (!targetTriple) { + throw new Error(`Unsupported platform: ${process.platform} (${process.arch})`); +} + +const platformPackage = PLATFORM_PACKAGE_BY_TARGET[targetTriple]; +if (!platformPackage) { + throw new Error(`Unsupported target triple: ${targetTriple}`); +} + +const binaryName = process.platform === "win32" ? "corgea.exe" : "corgea"; +const localVendorRoot = path.join(__dirname, "..", "vendor"); +const localBinaryPath = path.join(localVendorRoot, targetTriple, "corgea", binaryName); + +let vendorRoot = null; +try { + const packageJsonPath = require.resolve(`${platformPackage}/package.json`); + vendorRoot = path.join(path.dirname(packageJsonPath), "vendor"); +} catch (error) { + if (existsSync(localBinaryPath)) { + vendorRoot = localVendorRoot; + } else { + throw new Error(`Missing optional dependency ${platformPackage}. Reinstall Corgea CLI: ${getReinstallCommand()}`); + } +} + +const archRoot = path.join(vendorRoot, targetTriple); +const binaryPath = path.join(archRoot, "corgea", binaryName); + +if (!existsSync(binaryPath)) { + throw new Error(`Corgea binary not found at ${binaryPath}`); +} + +const additionalDirs = []; +const pathDir = path.join(archRoot, "path"); +if (existsSync(pathDir)) { + additionalDirs.push(pathDir); +} + +const env = { ...process.env, PATH: getUpdatedPath(additionalDirs) }; +const packageManagerEnvVar = detectPackageManager() === "bun" ? "CORGEA_MANAGED_BY_BUN" : "CORGEA_MANAGED_BY_NPM"; +env[packageManagerEnvVar] = "1"; + +const child = spawn(binaryPath, process.argv.slice(2), { stdio: "inherit", env }); + +child.on("error", (error) => { + console.error(error); + process.exit(1); +}); + +const forwardSignal = (signal) => { + if (child.killed) return; + try { + child.kill(signal); + } catch (error) { + // Ignore signal forwarding errors. + } +}; + +["SIGINT", "SIGTERM", "SIGHUP"].forEach((signal) => { + process.on(signal, () => forwardSignal(signal)); +}); + +child.on("exit", (code, signal) => { + if (signal) { + process.kill(process.pid, signal); + return; + } + + process.exit(code === null ? 1 : code); +}); diff --git a/package.json b/package.json new file mode 100644 index 0000000..9bd8871 --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "name": "corgea-cli", + "version": "1.7.2", + "description": "The Corgea CLI. Scan for vulnerabilities, create fixes.", + "license": "LGPL-2.1-only", + "homepage": "https://corgea.com", + "repository": { + "type": "git", + "url": "https://github.com/Corgea/cli.git" + }, + "bugs": { + "url": "https://github.com/Corgea/cli/issues" + }, + "bin": { + "corgea": "bin/corgea.js" + }, + "files": [ + "bin", + "README.md", + "LICENSE" + ], + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "corgea-cli-darwin-arm64": "1.7.2", + "corgea-cli-darwin-x64": "1.7.2", + "corgea-cli-linux-arm64": "1.7.2", + "corgea-cli-linux-x64": "1.7.2", + "corgea-cli-win32-x64": "1.7.2" + } +} diff --git a/packages/README.md b/packages/README.md new file mode 100644 index 0000000..5816382 --- /dev/null +++ b/packages/README.md @@ -0,0 +1,7 @@ +# Platform Packages + +Each subdirectory in this folder is a publishable npm package that contains a single prebuilt native Corgea CLI binary. + +- `corgea-cli` (the main package) declares these as optional dependencies. +- CI builds the binary for each target platform, stages it under `vendor//corgea/`, and publishes the package. +- These packages are implementation details and are not intended to be installed directly. diff --git a/packages/corgea-cli-darwin-arm64/README.md b/packages/corgea-cli-darwin-arm64/README.md new file mode 100644 index 0000000..2a54b45 --- /dev/null +++ b/packages/corgea-cli-darwin-arm64/README.md @@ -0,0 +1,3 @@ +# corgea-cli-darwin-arm64 +Platform-specific native binary package for `corgea-cli` on macOS arm64. +This package is installed automatically as an optional dependency of `corgea-cli`. diff --git a/packages/corgea-cli-darwin-arm64/package.json b/packages/corgea-cli-darwin-arm64/package.json new file mode 100644 index 0000000..35f9d1b --- /dev/null +++ b/packages/corgea-cli-darwin-arm64/package.json @@ -0,0 +1,24 @@ +{ + "name": "corgea-cli-darwin-arm64", + "version": "1.7.2", + "description": "macOS arm64 binary for Corgea CLI.", + "license": "LGPL-2.1-only", + "homepage": "https://corgea.com", + "repository": { + "type": "git", + "url": "https://github.com/Corgea/cli.git" + }, + "os": [ + "darwin" + ], + "cpu": [ + "arm64" + ], + "files": [ + "vendor", + "README.md" + ], + "publishConfig": { + "access": "public" + } +} diff --git a/packages/corgea-cli-darwin-x64/README.md b/packages/corgea-cli-darwin-x64/README.md new file mode 100644 index 0000000..6e49771 --- /dev/null +++ b/packages/corgea-cli-darwin-x64/README.md @@ -0,0 +1,3 @@ +# corgea-cli-darwin-x64 +Platform-specific native binary package for `corgea-cli` on macOS x64. +This package is installed automatically as an optional dependency of `corgea-cli`. diff --git a/packages/corgea-cli-darwin-x64/package.json b/packages/corgea-cli-darwin-x64/package.json new file mode 100644 index 0000000..ca84b0c --- /dev/null +++ b/packages/corgea-cli-darwin-x64/package.json @@ -0,0 +1,24 @@ +{ + "name": "corgea-cli-darwin-x64", + "version": "1.7.2", + "description": "macOS x64 binary for Corgea CLI.", + "license": "LGPL-2.1-only", + "homepage": "https://corgea.com", + "repository": { + "type": "git", + "url": "https://github.com/Corgea/cli.git" + }, + "os": [ + "darwin" + ], + "cpu": [ + "x64" + ], + "files": [ + "vendor", + "README.md" + ], + "publishConfig": { + "access": "public" + } +} diff --git a/packages/corgea-cli-linux-arm64/README.md b/packages/corgea-cli-linux-arm64/README.md new file mode 100644 index 0000000..3c2cb87 --- /dev/null +++ b/packages/corgea-cli-linux-arm64/README.md @@ -0,0 +1,3 @@ +# corgea-cli-linux-arm64 +Platform-specific native binary package for `corgea-cli` on Linux arm64. +This package is installed automatically as an optional dependency of `corgea-cli`. diff --git a/packages/corgea-cli-linux-arm64/package.json b/packages/corgea-cli-linux-arm64/package.json new file mode 100644 index 0000000..11afe1d --- /dev/null +++ b/packages/corgea-cli-linux-arm64/package.json @@ -0,0 +1,24 @@ +{ + "name": "corgea-cli-linux-arm64", + "version": "1.7.2", + "description": "Linux arm64 binary for Corgea CLI.", + "license": "LGPL-2.1-only", + "homepage": "https://corgea.com", + "repository": { + "type": "git", + "url": "https://github.com/Corgea/cli.git" + }, + "os": [ + "linux" + ], + "cpu": [ + "arm64" + ], + "files": [ + "vendor", + "README.md" + ], + "publishConfig": { + "access": "public" + } +} diff --git a/packages/corgea-cli-linux-x64/README.md b/packages/corgea-cli-linux-x64/README.md new file mode 100644 index 0000000..11ed1d5 --- /dev/null +++ b/packages/corgea-cli-linux-x64/README.md @@ -0,0 +1,3 @@ +# corgea-cli-linux-x64 +Platform-specific native binary package for `corgea-cli` on Linux x64. +This package is installed automatically as an optional dependency of `corgea-cli`. diff --git a/packages/corgea-cli-linux-x64/package.json b/packages/corgea-cli-linux-x64/package.json new file mode 100644 index 0000000..facfe08 --- /dev/null +++ b/packages/corgea-cli-linux-x64/package.json @@ -0,0 +1,24 @@ +{ + "name": "corgea-cli-linux-x64", + "version": "1.7.2", + "description": "Linux x64 binary for Corgea CLI.", + "license": "LGPL-2.1-only", + "homepage": "https://corgea.com", + "repository": { + "type": "git", + "url": "https://github.com/Corgea/cli.git" + }, + "os": [ + "linux" + ], + "cpu": [ + "x64" + ], + "files": [ + "vendor", + "README.md" + ], + "publishConfig": { + "access": "public" + } +} diff --git a/packages/corgea-cli-win32-x64/README.md b/packages/corgea-cli-win32-x64/README.md new file mode 100644 index 0000000..c792841 --- /dev/null +++ b/packages/corgea-cli-win32-x64/README.md @@ -0,0 +1,3 @@ +# corgea-cli-win32-x64 +Platform-specific native binary package for `corgea-cli` on Windows x64. +This package is installed automatically as an optional dependency of `corgea-cli`. diff --git a/packages/corgea-cli-win32-x64/package.json b/packages/corgea-cli-win32-x64/package.json new file mode 100644 index 0000000..e2cd1fd --- /dev/null +++ b/packages/corgea-cli-win32-x64/package.json @@ -0,0 +1,24 @@ +{ + "name": "corgea-cli-win32-x64", + "version": "1.7.2", + "description": "Windows x64 binary for Corgea CLI.", + "license": "LGPL-2.1-only", + "homepage": "https://corgea.com", + "repository": { + "type": "git", + "url": "https://github.com/Corgea/cli.git" + }, + "os": [ + "win32" + ], + "cpu": [ + "x64" + ], + "files": [ + "vendor", + "README.md" + ], + "publishConfig": { + "access": "public" + } +} diff --git a/scripts/npm/prepare-platform-package.js b/scripts/npm/prepare-platform-package.js new file mode 100644 index 0000000..a501512 --- /dev/null +++ b/scripts/npm/prepare-platform-package.js @@ -0,0 +1,52 @@ +#!/usr/bin/env node +"use strict"; + +const fs = require("node:fs"); +const path = require("node:path"); + +function fail(message) { + console.error(`[prepare-platform-package] ${message}`); + process.exit(1); +} + +const [packageDirArg, targetTriple, binaryName, version] = process.argv.slice(2); + +if (!packageDirArg || !targetTriple || !binaryName || !version) { + fail("usage: prepare-platform-package "); +} + +const repoRoot = path.resolve(__dirname, "..", ".."); +const packageDir = path.resolve(repoRoot, packageDirArg); +const packageJsonPath = path.join(packageDir, "package.json"); +const sourceBinaryPath = path.join(repoRoot, "target", targetTriple, "release", binaryName); +const vendorRoot = path.join(packageDir, "vendor"); +const destinationDir = path.join(vendorRoot, targetTriple, "corgea"); +const destinationBinaryPath = path.join(destinationDir, binaryName); + +if (!fs.existsSync(packageDir)) { + fail(`package directory not found: ${packageDir}`); +} + +if (!fs.existsSync(packageJsonPath)) { + fail(`package.json not found: ${packageJsonPath}`); +} + +if (!fs.existsSync(sourceBinaryPath)) { + fail(`compiled binary not found: ${sourceBinaryPath}`); +} + +const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); +packageJson.version = version; +fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`); + +fs.rmSync(vendorRoot, { recursive: true, force: true }); +fs.mkdirSync(destinationDir, { recursive: true }); +fs.copyFileSync(sourceBinaryPath, destinationBinaryPath); + +if (binaryName !== "corgea.exe") { + fs.chmodSync(destinationBinaryPath, 0o755); +} + +console.log(`Prepared ${packageJson.name}@${version}`); +console.log(`Binary: ${sourceBinaryPath}`); +console.log(`Output: ${destinationBinaryPath}`); diff --git a/scripts/test-local-npm.sh b/scripts/test-local-npm.sh new file mode 100755 index 0000000..347e034 --- /dev/null +++ b/scripts/test-local-npm.sh @@ -0,0 +1,154 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "${ROOT_DIR}" + +for cmd in cargo node npm; do + if ! command -v "${cmd}" >/dev/null 2>&1; then + echo "Missing required command: ${cmd}" >&2 + exit 1 + fi +done + +detect_platform() { + local os arch + os="$(uname -s)" + arch="$(uname -m)" + + case "${os}" in + Linux) + case "${arch}" in + x86_64) + TARGET_TRIPLE_DEFAULT="x86_64-unknown-linux-gnu" + PLATFORM_PACKAGE_DEFAULT="corgea-cli-linux-x64" + BINARY_NAME_DEFAULT="corgea" + ;; + aarch64|arm64) + TARGET_TRIPLE_DEFAULT="aarch64-unknown-linux-gnu" + PLATFORM_PACKAGE_DEFAULT="corgea-cli-linux-arm64" + BINARY_NAME_DEFAULT="corgea" + ;; + *) + echo "Unsupported Linux architecture: ${arch}" >&2 + exit 1 + ;; + esac + ;; + Darwin) + case "${arch}" in + x86_64) + TARGET_TRIPLE_DEFAULT="x86_64-apple-darwin" + PLATFORM_PACKAGE_DEFAULT="corgea-cli-darwin-x64" + BINARY_NAME_DEFAULT="corgea" + ;; + arm64) + TARGET_TRIPLE_DEFAULT="aarch64-apple-darwin" + PLATFORM_PACKAGE_DEFAULT="corgea-cli-darwin-arm64" + BINARY_NAME_DEFAULT="corgea" + ;; + *) + echo "Unsupported macOS architecture: ${arch}" >&2 + exit 1 + ;; + esac + ;; + MINGW*|MSYS*|CYGWIN*) + TARGET_TRIPLE_DEFAULT="x86_64-pc-windows-msvc" + PLATFORM_PACKAGE_DEFAULT="corgea-cli-win32-x64" + BINARY_NAME_DEFAULT="corgea.exe" + ;; + *) + echo "Unsupported OS: ${os}" >&2 + exit 1 + ;; + esac +} + +detect_platform + +TARGET_TRIPLE="${TARGET_TRIPLE:-${TARGET_TRIPLE_DEFAULT}}" +PLATFORM_PACKAGE="${PLATFORM_PACKAGE:-${PLATFORM_PACKAGE_DEFAULT}}" +BINARY_NAME="${BINARY_NAME:-${BINARY_NAME_DEFAULT}}" +PLATFORM_PACKAGE_DIR="packages/${PLATFORM_PACKAGE}" +VERSION="$(node -p "require('./package.json').version")" + +if [[ ! -d "${PLATFORM_PACKAGE_DIR}" ]]; then + echo "Missing platform package directory: ${PLATFORM_PACKAGE_DIR}" >&2 + exit 1 +fi + +TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/corgea-npm-local-test.XXXXXX")" +NPM_CACHE_DIR="${TMP_DIR}/npm-cache" +TEST_PREFIX="${TEST_PREFIX:-${TMP_DIR}/npm-global}" +KEEP_TMP="${KEEP_TMP:-0}" + +cleanup() { + if [[ "${KEEP_TMP}" == "1" ]]; then + echo "Keeping temp directory: ${TMP_DIR}" + return + fi + rm -rf "${TMP_DIR}" +} +trap cleanup EXIT + +echo "Testing local npm install flow" +echo " version: ${VERSION}" +echo " target triple: ${TARGET_TRIPLE}" +echo " platform package: ${PLATFORM_PACKAGE}" +echo " binary: ${BINARY_NAME}" +echo " test prefix: ${TEST_PREFIX}" + +echo "1/6 Building native binary..." +cargo build --release --target "${TARGET_TRIPLE}" + +echo "2/6 Staging platform package..." +node scripts/npm/prepare-platform-package.js \ + "${PLATFORM_PACKAGE_DIR}" \ + "${TARGET_TRIPLE}" \ + "${BINARY_NAME}" \ + "${VERSION}" + +echo "3/6 Packing platform package..." +( + cd "${PLATFORM_PACKAGE_DIR}" + NPM_CONFIG_CACHE="${NPM_CACHE_DIR}" npm pack --pack-destination "${TMP_DIR}" >/dev/null +) +PLATFORM_TARBALL="${TMP_DIR}/${PLATFORM_PACKAGE}-${VERSION}.tgz" + +echo "4/6 Packing main package..." +NPM_CONFIG_CACHE="${NPM_CACHE_DIR}" npm pack --pack-destination "${TMP_DIR}" >/dev/null +MAIN_TARBALL="${TMP_DIR}/corgea-cli-${VERSION}.tgz" + +if [[ ! -f "${PLATFORM_TARBALL}" ]]; then + echo "Expected tarball not found: ${PLATFORM_TARBALL}" >&2 + exit 1 +fi + +if [[ ! -f "${MAIN_TARBALL}" ]]; then + echo "Expected tarball not found: ${MAIN_TARBALL}" >&2 + exit 1 +fi + +echo "5/6 Installing tarballs into isolated prefix..." +mkdir -p "${TEST_PREFIX}" +NPM_CONFIG_CACHE="${NPM_CACHE_DIR}" npm install -g --prefix "${TEST_PREFIX}" --offline "${PLATFORM_TARBALL}" >/dev/null +# The platform package is installed explicitly above, so omit optional deps +# here to avoid unnecessary registry lookups during local/offline testing. +NPM_CONFIG_CACHE="${NPM_CACHE_DIR}" npm install -g --prefix "${TEST_PREFIX}" --offline --omit=optional "${MAIN_TARBALL}" >/dev/null + +echo "6/6 Running corgea --version..." +CLI_PATH_UNIX="${TEST_PREFIX}/bin/corgea" +CLI_PATH_WIN="${TEST_PREFIX}/corgea.cmd" + +if [[ -x "${CLI_PATH_UNIX}" ]]; then + "${CLI_PATH_UNIX}" --version +elif [[ -f "${CLI_PATH_WIN}" ]]; then + "${CLI_PATH_WIN}" --version +else + echo "Could not find installed CLI entrypoint under ${TEST_PREFIX}" >&2 + exit 1 +fi + +echo +echo "Local npm package test passed."