From 58f2b93f9b1d61a0ef17ef72a0903c26cce2a746 Mon Sep 17 00:00:00 2001 From: Satya Patel Date: Sun, 10 May 2026 10:43:26 -0700 Subject: [PATCH] feat(cli): add `projects setup` command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adopt an existing v2 project on a target host without creating a new one — the dedup counterpart to `projects create`. Mirrors the desktop's two-button UX: `create` makes a fresh cloud project, `setup ` attaches an existing one to the host (clone the cloud-stored URL or register an existing folder). Bumps cli to 0.2.14. --- bun.lock | 2 +- packages/cli/cli.config.ts | 2 +- packages/cli/package.json | 2 +- .../src/commands/projects/setup/command.ts | 74 +++++++++++++++++++ 4 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 packages/cli/src/commands/projects/setup/command.ts diff --git a/bun.lock b/bun.lock index 12bf16c9466..9f0e01e5af0 100644 --- a/bun.lock +++ b/bun.lock @@ -684,7 +684,7 @@ }, "packages/cli": { "name": "@superset/cli", - "version": "0.2.13", + "version": "0.2.14", "dependencies": { "@clack/prompts": "^0.10.0", "@superset/cli-framework": "workspace:*", diff --git a/packages/cli/cli.config.ts b/packages/cli/cli.config.ts index 11c13ec57b4..d9829e75d15 100644 --- a/packages/cli/cli.config.ts +++ b/packages/cli/cli.config.ts @@ -1,6 +1,6 @@ import { boolean, defineConfig, string } from "@superset/cli-framework"; -const VERSION = "0.2.13"; +const VERSION = "0.2.14"; export default defineConfig({ name: "superset", diff --git a/packages/cli/package.json b/packages/cli/package.json index 6d28bd0540a..187d7d63230 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@superset/cli", - "version": "0.2.13", + "version": "0.2.14", "private": true, "type": "module", "scripts": { diff --git a/packages/cli/src/commands/projects/setup/command.ts b/packages/cli/src/commands/projects/setup/command.ts new file mode 100644 index 00000000000..416097b9bde --- /dev/null +++ b/packages/cli/src/commands/projects/setup/command.ts @@ -0,0 +1,74 @@ +import { boolean, CLIError, positional, string } from "@superset/cli-framework"; +import { command } from "../../../lib/command"; +import { requireHostTarget, resolveHostTarget } from "../../../lib/host-target"; + +export default command({ + description: + "Adopt an existing project on a host (clone its repo or import a folder)", + args: [positional("id").required().desc("Project UUID to adopt")], + options: { + host: string().desc("Target host machineId"), + local: boolean().desc("Target this machine"), + parentDir: string().desc( + "Parent directory to clone the project's repo into (clone mode)", + ), + import: string().desc( + "Existing local repo path on the target host (import mode)", + ), + allowRelocate: boolean().desc( + "Permit re-importing at a different path if the project is already set up here", + ), + }, + run: async ({ ctx, args, options }) => { + const projectId = args.id as string; + const organizationId = ctx.config.organizationId; + if (!organizationId) { + throw new CLIError("No active organization", "Run: superset auth login"); + } + + if (Boolean(options.parentDir) === Boolean(options.import)) { + throw new CLIError( + "Specify exactly one of --parent-dir or --import", + "Use --parent-dir to clone, or --import to register an existing folder", + ); + } + if (options.allowRelocate && !options.import) { + throw new CLIError( + "--allow-relocate only applies to --import", + "Drop --allow-relocate, or switch to --import ", + ); + } + + const hostId = requireHostTarget({ + host: options.host ?? undefined, + local: options.local ?? undefined, + }); + + const target = resolveHostTarget({ + requestedHostId: hostId, + organizationId, + userJwt: ctx.bearer, + }); + + const mode = options.parentDir + ? { + kind: "clone" as const, + parentDir: options.parentDir, + } + : { + kind: "import" as const, + repoPath: options.import as string, + allowRelocate: options.allowRelocate ?? false, + }; + + const result = await target.client.project.setup.mutate({ + projectId, + mode, + }); + + return { + data: result, + message: `Set up project ${projectId} on host ${target.hostId}`, + }; + }, +});