mirror of
https://github.com/actionbook/actionbook.git
synced 2026-09-19 03:28:06 +08:00
731cc340d8
* [packages/cli]test: add comprehensive CLI test suite with E2E browser tests - Add unit tests for wrapper (actionbook.js), postinstall, config, profile, search, get, and sources commands - Add Tier 1 browser tests (arg validation, help output, defaults) - Add Tier 2 browser E2E tests (navigation, interaction, screenshots, cookies, tabs, error handling, JSON output) - Fix E2E test reliability: add --wait for element readiness after navigation, use eval-based form submit for login flow, match actual CLI exit codes - Suppress macOS Keychain dialogs in tests via --use-mock-keychain config - Set testTimeout to 60s for browser E2E operations - Export modules from actionbook.js and postinstall.js for testability Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * [packages/cli]fix: resolve Windows .exe suffix when locating test CLI binary Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * [packages/cli]fix: skip shell-script mock tests on Windows Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
161 lines
4.4 KiB
JavaScript
Executable File
161 lines
4.4 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Cross-platform CLI wrapper for actionbook.
|
|
*
|
|
* Resolves the platform-specific binary package and spawns the native binary.
|
|
* Supports ACTIONBOOK_BINARY_PATH env var for development.
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
const { spawn } = require("child_process");
|
|
const { existsSync, readFileSync } = require("fs");
|
|
const path = require("path");
|
|
|
|
const PLATFORM_PACKAGES = {
|
|
"darwin-arm64": "@actionbookdev/cli-darwin-arm64",
|
|
"darwin-x64": "@actionbookdev/cli-darwin-x64",
|
|
"linux-x64": "@actionbookdev/cli-linux-x64-gnu",
|
|
"linux-arm64": "@actionbookdev/cli-linux-arm64-gnu",
|
|
"win32-x64": "@actionbookdev/cli-win32-x64",
|
|
"win32-arm64": "@actionbookdev/cli-win32-arm64",
|
|
};
|
|
|
|
function main() {
|
|
// Keep CLI version aligned with npm package version.
|
|
if (isVersionOnlyFlag(process.argv.slice(2))) {
|
|
const pkgPath = path.join(__dirname, "..", "package.json");
|
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
console.log(`actionbook ${pkg.version}`);
|
|
process.exit(0);
|
|
}
|
|
|
|
// Allow env var override for development.
|
|
const envPath = process.env.ACTIONBOOK_BINARY_PATH;
|
|
if (envPath) {
|
|
run(envPath);
|
|
return;
|
|
}
|
|
|
|
const platformKey = `${process.platform}-${process.arch}`;
|
|
const binaryPath = getBinaryPath(platformKey);
|
|
run(binaryPath);
|
|
}
|
|
|
|
function isVersionOnlyFlag(args) {
|
|
return args.length === 1 && (args[0] === "--version" || args[0] === "-V");
|
|
}
|
|
|
|
function getBinaryPath(platformKey) {
|
|
const packageName = PLATFORM_PACKAGES[platformKey];
|
|
|
|
if (!packageName) {
|
|
console.error(`Error: Unsupported platform: ${platformKey}`);
|
|
console.error(`Supported: ${Object.keys(PLATFORM_PACKAGES).join(", ")}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
if (process.platform === "linux" && isLikelyMusl()) {
|
|
console.error(`Error: Unsupported libc for ${platformKey}`);
|
|
console.error("This release currently supports Linux glibc only.");
|
|
console.error("musl (for example Alpine Linux) is not supported yet.");
|
|
process.exit(1);
|
|
}
|
|
|
|
const binaryName = process.platform === "win32" ? "actionbook.exe" : "actionbook";
|
|
|
|
try {
|
|
const packageDir = resolvePackageDir(packageName);
|
|
if (!packageDir) {
|
|
throw new Error("package not found");
|
|
}
|
|
const binaryPath = path.join(packageDir, "bin", binaryName);
|
|
|
|
if (!existsSync(binaryPath)) {
|
|
console.error(`Error: No binary found in ${packageName}`);
|
|
console.error(`Expected: ${binaryPath}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
return binaryPath;
|
|
} catch {
|
|
console.error(`Error: Missing native package for ${platformKey}`);
|
|
console.error(`Expected package: ${packageName}`);
|
|
console.error("");
|
|
console.error("This usually happens when optional dependencies are skipped.");
|
|
console.error("Check if you installed with --omit=optional.");
|
|
console.error("");
|
|
console.error("Try reinstalling:");
|
|
console.error(" npm install -g @actionbookdev/cli");
|
|
console.error("");
|
|
console.error("Or install the Rust CLI directly:");
|
|
console.error(" cargo install actionbook");
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
function resolvePackageDir(packageName) {
|
|
try {
|
|
const packageJsonPath = require.resolve(`${packageName}/package.json`);
|
|
return path.dirname(packageJsonPath);
|
|
} catch {
|
|
// Fallback for workspace or non-hoisted layouts.
|
|
const unscoped = packageName.split("/")[1];
|
|
const packageDir = path.join(__dirname, "..", "..", unscoped);
|
|
const packageJsonPath = path.join(packageDir, "package.json");
|
|
if (existsSync(packageJsonPath)) {
|
|
return packageDir;
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function isLikelyMusl() {
|
|
if (process.platform !== "linux") {
|
|
return false;
|
|
}
|
|
|
|
if (!process.report || typeof process.report.getReport !== "function") {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
const report = process.report.getReport();
|
|
const header = report && report.header ? report.header : {};
|
|
return !header.glibcVersionRuntime;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function run(binaryPath) {
|
|
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
stdio: "inherit",
|
|
windowsHide: false,
|
|
});
|
|
|
|
child.on("error", (err) => {
|
|
console.error(`Error executing binary: ${err.message}`);
|
|
process.exit(1);
|
|
});
|
|
|
|
child.on("close", (code) => {
|
|
process.exit(code ?? 0);
|
|
});
|
|
}
|
|
|
|
if (require.main === module) {
|
|
main();
|
|
}
|
|
|
|
module.exports = {
|
|
main,
|
|
isVersionOnlyFlag,
|
|
getBinaryPath,
|
|
resolvePackageDir,
|
|
isLikelyMusl,
|
|
run,
|
|
PLATFORM_PACKAGES,
|
|
};
|