mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 18:00:54 +00:00
feat: support git plugin installs
This commit is contained in:
@@ -35,6 +35,30 @@ function writePlugin([dir, id, version, method, name]) {
|
||||
writePluginManifest(path.join(dir, "openclaw.plugin.json"), id);
|
||||
}
|
||||
|
||||
function writePluginWithCli([dir, id, version, method, name, cliRoot, cliOutput]) {
|
||||
for (const [value, label] of [
|
||||
[dir, "dir"],
|
||||
[id, "id"],
|
||||
[version, "version"],
|
||||
[method, "method"],
|
||||
[name, "name"],
|
||||
[cliRoot, "cliRoot"],
|
||||
[cliOutput, "cliOutput"],
|
||||
]) {
|
||||
requireArg(value, label);
|
||||
}
|
||||
writeJson(path.join(dir, "package.json"), {
|
||||
name: `@openclaw/${id}`,
|
||||
version,
|
||||
openclaw: { extensions: ["./index.js"] },
|
||||
});
|
||||
write(
|
||||
path.join(dir, "index.js"),
|
||||
`module.exports = { id: ${JSON.stringify(id)}, name: ${JSON.stringify(name)}, register(api) { api.registerGatewayMethod(${JSON.stringify(method)}, async () => ({ ok: true })); api.registerCli(({ program }) => { const root = program.command(${JSON.stringify(cliRoot)}).description(${JSON.stringify(`${name} fixture command`)}); root.command("ping").description("Print fixture ping output").action(() => { console.log(${JSON.stringify(cliOutput)}); }); }, { descriptors: [{ name: ${JSON.stringify(cliRoot)}, description: ${JSON.stringify(`${name} fixture command`)}, hasSubcommands: true }] }); }, };\n`,
|
||||
);
|
||||
writePluginManifest(path.join(dir, "openclaw.plugin.json"), id);
|
||||
}
|
||||
|
||||
function writeClaudeBundle([root]) {
|
||||
root = requireArg(root, "root");
|
||||
writeJson(path.join(root, ".claude-plugin", "plugin.json"), { name: "claude-bundle-e2e" });
|
||||
@@ -75,6 +99,7 @@ function writePluginMarketplace([root]) {
|
||||
export const pluginCommands = {
|
||||
"plugin-demo": writePluginDemo,
|
||||
plugin: writePlugin,
|
||||
"plugin-cli": writePluginWithCli,
|
||||
"plugin-manifest": ([file, id]) =>
|
||||
writePluginManifest(requireArg(file, "file"), requireArg(id, "id")),
|
||||
"claude-bundle": writeClaudeBundle,
|
||||
|
||||
@@ -4,6 +4,20 @@ import path from "node:path";
|
||||
const command = process.argv[2];
|
||||
const readJson = (file) => JSON.parse(fs.readFileSync(file, "utf8"));
|
||||
|
||||
function getInstallRecords() {
|
||||
const indexPath = path.join(process.env.HOME, ".openclaw", "plugins", "installs.json");
|
||||
const index = fs.existsSync(indexPath) ? readJson(indexPath) : {};
|
||||
const configPath = path.join(process.env.HOME, ".openclaw", "openclaw.json");
|
||||
const config = fs.existsSync(configPath) ? readJson(configPath) : {};
|
||||
const allowLegacyCompat = process.env.OPENCLAW_PACKAGE_ACCEPTANCE_LEGACY_COMPAT === "1";
|
||||
if (!allowLegacyCompat && !index.installRecords) {
|
||||
throw new Error("expected modern installRecords in installed plugin index");
|
||||
}
|
||||
return allowLegacyCompat
|
||||
? (index.installRecords ?? index.records ?? config.plugins?.installs ?? {})
|
||||
: (index.installRecords ?? {});
|
||||
}
|
||||
|
||||
function recordFixturePluginTrust() {
|
||||
const pluginId = process.argv[3];
|
||||
const pluginRoot = process.argv[4];
|
||||
@@ -173,17 +187,7 @@ function assertMarketplaceInstalled() {
|
||||
}
|
||||
|
||||
function assertMarketplaceRecords() {
|
||||
const indexPath = path.join(process.env.HOME, ".openclaw", "plugins", "installs.json");
|
||||
const index = readJson(indexPath);
|
||||
const configPath = path.join(process.env.HOME, ".openclaw", "openclaw.json");
|
||||
const config = fs.existsSync(configPath) ? readJson(configPath) : {};
|
||||
const allowLegacyCompat = process.env.OPENCLAW_PACKAGE_ACCEPTANCE_LEGACY_COMPAT === "1";
|
||||
if (!allowLegacyCompat && !index.installRecords) {
|
||||
throw new Error("expected modern installRecords in installed plugin index");
|
||||
}
|
||||
const installRecords = allowLegacyCompat
|
||||
? (index.installRecords ?? index.records ?? config.plugins?.installs ?? {})
|
||||
: (index.installRecords ?? {});
|
||||
const installRecords = getInstallRecords();
|
||||
for (const id of ["marketplace-shortcut", "marketplace-direct"]) {
|
||||
const record = installRecords[id];
|
||||
if (!record) {
|
||||
@@ -205,6 +209,56 @@ function assertMarketplaceRecords() {
|
||||
}
|
||||
}
|
||||
|
||||
function assertGitPlugin() {
|
||||
const repoUrl = process.argv[3];
|
||||
const gitRef = process.argv[4];
|
||||
assertSimplePlugin(
|
||||
"/tmp/plugins-git.json",
|
||||
"/tmp/plugins-git-inspect.json",
|
||||
"demo-plugin-git",
|
||||
"demo.git",
|
||||
);
|
||||
|
||||
const inspect = readJson("/tmp/plugins-git-inspect.json");
|
||||
if (!Array.isArray(inspect.cliCommands) || !inspect.cliCommands.includes("demo-git")) {
|
||||
throw new Error(`expected demo-git cli command, got ${inspect.cliCommands?.join(", ")}`);
|
||||
}
|
||||
|
||||
const cliOutput = fs.readFileSync("/tmp/plugins-git-cli.txt", "utf8");
|
||||
if (!cliOutput.includes("demo-plugin-git:pong")) {
|
||||
throw new Error(`unexpected git plugin cli output: ${cliOutput.trim()}`);
|
||||
}
|
||||
|
||||
const record = getInstallRecords()["demo-plugin-git"];
|
||||
if (!record) {
|
||||
throw new Error("missing git install record for demo-plugin-git");
|
||||
}
|
||||
if (record.source !== "git") {
|
||||
throw new Error(`unexpected git install source: ${record.source}`);
|
||||
}
|
||||
if (record.gitUrl !== repoUrl) {
|
||||
throw new Error(`unexpected git url: ${record.gitUrl}, expected ${repoUrl}`);
|
||||
}
|
||||
if (record.gitRef !== gitRef) {
|
||||
throw new Error(`unexpected git ref: ${record.gitRef}, expected ${gitRef}`);
|
||||
}
|
||||
if (record.gitCommit !== gitRef) {
|
||||
throw new Error(`unexpected git commit: ${record.gitCommit}, expected ${gitRef}`);
|
||||
}
|
||||
if (record.spec !== `git:${repoUrl}@${gitRef}`) {
|
||||
throw new Error(`unexpected git spec: ${record.spec}`);
|
||||
}
|
||||
|
||||
const installPath = record.installPath?.replace(/^~(?=$|\/)/u, process.env.HOME);
|
||||
if (!installPath || !fs.existsSync(installPath)) {
|
||||
throw new Error(`git install path missing on disk: ${installPath}`);
|
||||
}
|
||||
const extensionsRoot = path.join(process.env.HOME, ".openclaw", "extensions");
|
||||
if (!installPath.startsWith(`${extensionsRoot}${path.sep}`)) {
|
||||
throw new Error(`git install path is outside managed extensions root: ${installPath}`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertRealPathInside(parentPath, childPath, label) {
|
||||
const parentRealPath = fs.realpathSync(parentPath);
|
||||
const childRealPath = fs.realpathSync(childPath);
|
||||
@@ -414,6 +468,7 @@ const commands = {
|
||||
"bundle-disabled": assertClaudeBundleDisabled,
|
||||
"bundle-inspect": assertClaudeBundleInspect,
|
||||
"slash-install": assertSlashInstall,
|
||||
"plugin-git": assertGitPlugin,
|
||||
"marketplace-list": assertMarketplaceList,
|
||||
"marketplace-installed": assertMarketplaceInstalled,
|
||||
"marketplace-records": assertMarketplaceRecords,
|
||||
|
||||
@@ -20,6 +20,18 @@ write_fixture_plugin() {
|
||||
node scripts/e2e/lib/fixture.mjs plugin "$dir" "$id" "$version" "$method" "$name"
|
||||
}
|
||||
|
||||
write_fixture_plugin_with_cli() {
|
||||
local dir="$1"
|
||||
local id="$2"
|
||||
local version="$3"
|
||||
local method="$4"
|
||||
local name="$5"
|
||||
local cli_root="$6"
|
||||
local cli_output="$7"
|
||||
|
||||
node scripts/e2e/lib/fixture.mjs plugin-cli "$dir" "$id" "$version" "$method" "$name" "$cli_root" "$cli_output"
|
||||
}
|
||||
|
||||
write_fixture_manifest() {
|
||||
local file="$1"
|
||||
local id="$2"
|
||||
|
||||
@@ -23,8 +23,8 @@ run_plugins_marketplace_scenario() {
|
||||
run_logged install-marketplace-shortcut node "$OPENCLAW_ENTRY" plugins install marketplace-shortcut@claude-fixtures
|
||||
run_logged install-marketplace-direct node "$OPENCLAW_ENTRY" plugins install marketplace-direct --marketplace claude-fixtures
|
||||
node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins-marketplace.json
|
||||
node "$OPENCLAW_ENTRY" plugins inspect marketplace-shortcut --json >/tmp/plugins-marketplace-shortcut-inspect.json
|
||||
node "$OPENCLAW_ENTRY" plugins inspect marketplace-direct --json >/tmp/plugins-marketplace-direct-inspect.json
|
||||
node "$OPENCLAW_ENTRY" plugins inspect marketplace-shortcut --runtime --json >/tmp/plugins-marketplace-shortcut-inspect.json
|
||||
node "$OPENCLAW_ENTRY" plugins inspect marketplace-direct --runtime --json >/tmp/plugins-marketplace-direct-inspect.json
|
||||
|
||||
node scripts/e2e/lib/plugins/assertions.mjs marketplace-installed
|
||||
|
||||
@@ -39,7 +39,7 @@ run_plugins_marketplace_scenario() {
|
||||
run_logged update-marketplace-shortcut-dry-run node "$OPENCLAW_ENTRY" plugins update marketplace-shortcut --dry-run
|
||||
run_logged update-marketplace-shortcut node "$OPENCLAW_ENTRY" plugins update marketplace-shortcut
|
||||
node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins-marketplace-updated.json
|
||||
node "$OPENCLAW_ENTRY" plugins inspect marketplace-shortcut --json >/tmp/plugins-marketplace-updated-inspect.json
|
||||
node "$OPENCLAW_ENTRY" plugins inspect marketplace-shortcut --runtime --json >/tmp/plugins-marketplace-updated-inspect.json
|
||||
|
||||
node scripts/e2e/lib/plugins/assertions.mjs marketplace-updated
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ write_demo_fixture_plugin "$demo_plugin_root"
|
||||
record_fixture_plugin_trust "$demo_plugin_id" "$demo_plugin_root" 1
|
||||
|
||||
node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins.json
|
||||
node "$OPENCLAW_ENTRY" plugins inspect demo-plugin --json >/tmp/plugins-inspect.json
|
||||
node "$OPENCLAW_ENTRY" plugins inspect demo-plugin --runtime --json >/tmp/plugins-inspect.json
|
||||
|
||||
node scripts/e2e/lib/plugins/assertions.mjs demo-plugin
|
||||
|
||||
@@ -32,7 +32,7 @@ pack_fixture_plugin "$pack_dir" /tmp/demo-plugin-tgz.tgz demo-plugin-tgz 0.0.1 d
|
||||
|
||||
run_logged install-tgz node "$OPENCLAW_ENTRY" plugins install /tmp/demo-plugin-tgz.tgz
|
||||
node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins2.json
|
||||
node "$OPENCLAW_ENTRY" plugins inspect demo-plugin-tgz --json >/tmp/plugins2-inspect.json
|
||||
node "$OPENCLAW_ENTRY" plugins inspect demo-plugin-tgz --runtime --json >/tmp/plugins2-inspect.json
|
||||
|
||||
node scripts/e2e/lib/plugins/assertions.mjs plugin-tgz
|
||||
|
||||
@@ -42,7 +42,7 @@ write_fixture_plugin "$dir_plugin" demo-plugin-dir 0.0.1 demo.dir "Demo Plugin D
|
||||
|
||||
run_logged install-dir node "$OPENCLAW_ENTRY" plugins install "$dir_plugin"
|
||||
node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins3.json
|
||||
node "$OPENCLAW_ENTRY" plugins inspect demo-plugin-dir --json >/tmp/plugins3-inspect.json
|
||||
node "$OPENCLAW_ENTRY" plugins inspect demo-plugin-dir --runtime --json >/tmp/plugins3-inspect.json
|
||||
|
||||
node scripts/e2e/lib/plugins/assertions.mjs plugin-dir
|
||||
|
||||
@@ -52,10 +52,29 @@ write_fixture_plugin "$file_pack_dir/package" demo-plugin-file 0.0.1 demo.file "
|
||||
|
||||
run_logged install-file node "$OPENCLAW_ENTRY" plugins install "file:$file_pack_dir/package"
|
||||
node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins4.json
|
||||
node "$OPENCLAW_ENTRY" plugins inspect demo-plugin-file --json >/tmp/plugins4-inspect.json
|
||||
node "$OPENCLAW_ENTRY" plugins inspect demo-plugin-file --runtime --json >/tmp/plugins4-inspect.json
|
||||
|
||||
node scripts/e2e/lib/plugins/assertions.mjs plugin-file
|
||||
|
||||
echo "Testing install from git repo and plugin CLI execution..."
|
||||
git_fixture_root="$(mktemp -d "/tmp/openclaw-plugin-git.XXXXXX")"
|
||||
git_repo="$git_fixture_root/repo"
|
||||
git_repo_url="file://$git_repo"
|
||||
write_fixture_plugin_with_cli "$git_repo" demo-plugin-git 0.0.1 demo.git "Demo Plugin Git" demo-git "demo-plugin-git:pong"
|
||||
git -C "$git_repo" init -q
|
||||
git -C "$git_repo" config user.email "docker-e2e@openclaw.local"
|
||||
git -C "$git_repo" config user.name "OpenClaw Docker E2E"
|
||||
git -C "$git_repo" add -A
|
||||
git -C "$git_repo" commit -qm "test fixture"
|
||||
git_ref="$(git -C "$git_repo" rev-parse HEAD)"
|
||||
|
||||
run_logged install-git node "$OPENCLAW_ENTRY" plugins install "git:$git_repo_url@$git_ref"
|
||||
node "$OPENCLAW_ENTRY" plugins list --json >/tmp/plugins-git.json
|
||||
node "$OPENCLAW_ENTRY" plugins inspect demo-plugin-git --runtime --json >/tmp/plugins-git-inspect.json
|
||||
run_logged exec-git-plugin-cli bash -c 'node "$OPENCLAW_ENTRY" demo-git ping >/tmp/plugins-git-cli.txt'
|
||||
|
||||
node scripts/e2e/lib/plugins/assertions.mjs plugin-git "$git_repo_url" "$git_ref"
|
||||
|
||||
echo "Testing Claude bundle enable and inspect flow..."
|
||||
bundle_plugin_id="claude-bundle-e2e"
|
||||
bundle_root="$OPENCLAW_PLUGIN_HOME/$bundle_plugin_id"
|
||||
@@ -74,7 +93,7 @@ slash_install_dir="$(mktemp -d "/tmp/openclaw-plugin-slash-install.XXXXXX")"
|
||||
write_fixture_plugin "$slash_install_dir" slash-install-plugin 0.0.1 demo.slash.install "Slash Install Plugin"
|
||||
|
||||
run_logged install-slash-plugin node "$OPENCLAW_ENTRY" plugins install "$slash_install_dir"
|
||||
node "$OPENCLAW_ENTRY" plugins inspect slash-install-plugin --json >/tmp/plugin-command-install-show.json
|
||||
node "$OPENCLAW_ENTRY" plugins inspect slash-install-plugin --runtime --json >/tmp/plugin-command-install-show.json
|
||||
node scripts/e2e/lib/plugins/assertions.mjs slash-install
|
||||
|
||||
run_plugins_marketplace_scenario
|
||||
|
||||
Reference in New Issue
Block a user