2.1 KB72 lines
Blame
1import { spinner, log } from "@clack/prompts";
2import { execSync } from "node:child_process";
3import { writeFileSync, existsSync } from "node:fs";
4import { join, resolve } from "node:path";
5import { hubRequest } from "../api.js";
6import { getHub } from "../config.js";
7import { buildSlConfig } from "../sl-config.js";
8
9interface Repo {
10 id: number;
11 owner_name: string;
12 name: string;
13 default_branch: string;
14}
15
16export async function clone(args: string[]) {
17 const slug = args.find((a) => !a.startsWith("--"));
18 if (!slug) {
19 log.error("Usage: grove clone <owner/repo> [destination]");
20 process.exit(1);
21 }
22
23 let owner: string;
24 let repoName: string;
25
26 if (slug.includes("/")) {
27 [owner, repoName] = slug.split("/", 2);
28 } else {
29 // Bare repo name — look up owner from API
30 const s = spinner();
31 s.start("Looking up repository");
32 const { repos } = await hubRequest<{ repos: Repo[] }>("/api/repos");
33 const match = repos.find((r) => r.name === slug);
34 if (!match) {
35 s.stop("Not found");
36 log.error(`Repository '${slug}' not found.`);
37 process.exit(1);
38 }
39 owner = match.owner_name;
40 repoName = match.name;
41 s.stop(`Found ${owner}/${repoName}`);
42 }
43
44 // Verify repo exists
45 const s = spinner();
46 s.start(`Fetching ${owner}/${repoName}`);
47 const { repo } = await hubRequest<{ repo: Repo }>(
48 `/api/repos/${owner}/${repoName}`
49 );
50 s.stop(`${owner}/${repoName}`);
51
52 const dest = args.find((a, i) => i > args.indexOf(slug) && !a.startsWith("--")) ?? repoName;
53 const destPath = resolve(dest);
54
55 // Run sl clone
56 log.step(`Cloning into ${dest}`);
57 try {
58 execSync(`sl clone slapi:${repoName} ${dest}`, { stdio: "inherit" });
59 } catch {
60 // sl clone of empty repos exits non-zero — check if .sl was created
61 if (!existsSync(join(destPath, ".sl"))) {
62 // Clone truly failed
63 process.exit(1);
64 }
65 }
66
67 // Write proper .sl/config
68 const hub = await getHub();
69 writeFileSync(join(destPath, ".sl", "config"), buildSlConfig({ name: repoName, owner_name: owner, default_branch: repo.default_branch }, hub));
70 log.success(`Cloned ${owner}/${repoName} into ${dest}`);
71}
72