Skip to content

Commit

Permalink
feat: add interactive module id selection to "kit import" command
Browse files Browse the repository at this point in the history
  • Loading branch information
JohannesRudolph committed Jul 5, 2023
1 parent cf42711 commit d26b738
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 66 deletions.
28 changes: 24 additions & 4 deletions src/commands/interactive/InteractivePrompts.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import * as colors from "std/fmt/colors";
import { Select } from "x/cliffy/prompt";
import { isWindows } from "../../os.ts";
import { CollieRepository } from "../../model/CollieRepository.ts";
import { FoundationRepository } from "../../model/FoundationRepository.ts";
import { KitModuleRepository } from "../../kit/KitModuleRepository.ts";

export class InteractivePrompts {
static async selectFoundation(kit: CollieRepository) {
return await Select.prompt({
message: "Select a foundation",
options: (await kit.listFoundations()).map((x) => ({
options: (
await kit.listFoundations()
).map((x) => ({
name: x,
value: x,
})),
Expand All @@ -16,9 +21,24 @@ export class InteractivePrompts {
static async selectPlatform(repo: FoundationRepository): Promise<string> {
return await Select.prompt({
message: "Select a platform",
options: (
await repo.platforms
).map((x) => ({ name: x.id, value: x.id })),
options: (await repo.platforms).map((x) => ({ name: x.id, value: x.id })),
});
}

static async selectModule(
moduleRepo: KitModuleRepository,
fromWhereDescription = "your repository",
) {
const options = moduleRepo.all.map((x) => ({
value: x.id,
name: `${x.kitModule.name} ${colors.dim(x.id)}`,
}));

return await Select.prompt({
message: "Select a kit module from " + fromWhereDescription,
options,
info: true,
search: !isWindows, // see https://github.com/c4spar/deno-cliffy/issues/272#issuecomment-1262197264
});
}
}
52 changes: 52 additions & 0 deletions src/commands/kit/KitModuleHub.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import * as fs from "std/fs";
import { GitCliFacade } from "../../api/git/GitCliFacade.ts";
import { CollieRepository } from "../../model/CollieRepository.ts";

export class KitModuleHub {
constructor(
private readonly git: GitCliFacade,
private readonly repo: CollieRepository,
) {}
private readonly hubCacheDirPath = [".collie", "hub"];

// hardcoding this is ok for now
readonly url =
"https://github.com/meshcloud/landing-zone-construction-kit.git";

public async import(id: string, moduleDestDir: string, overwrite?: boolean) {
const moduleSrcDir = this.repo.resolvePath(
...this.hubCacheDirPath,
"kit",
id,
);

await fs.copy(moduleSrcDir, moduleDestDir, { overwrite: overwrite });
}

async updateHubClone() {
const hubCacheDir = this.repo.resolvePath(...this.hubCacheDirPath);

// we do keep a git clone of the repo locally because copying on the local FS is much faster than downloading and
// extracting a huge tarball
await fs.ensureDir(hubCacheDir);

const hubCacheGitDir = this.repo.resolvePath(
...this.hubCacheDirPath,
".git",
);
const hasAlreadyCloned = await this.git.isRepo(hubCacheGitDir);

if (hasAlreadyCloned) {
await this.git.pull(hubCacheDir);
} else {
await this.git.clone(hubCacheDir, this.url);
}

return hubCacheDir;
}

public async cleanHubClone() {
const hubCacheDir = this.repo.resolvePath(...this.hubCacheDirPath);
await Deno.remove(hubCacheDir, { recursive: true });
}
}
17 changes: 2 additions & 15 deletions src/commands/kit/apply.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ export function registerApplyCmd(program: TopLevelCommand) {
logger,
);

moduleId = moduleId || (await selectModule(moduleRepo));
moduleId = moduleId ||
(await InteractivePrompts.selectModule(moduleRepo));
const module = moduleRepo.tryFindById(moduleId);
if (!module) {
throw new CommandOptionError(
Expand Down Expand Up @@ -112,17 +113,3 @@ export function registerApplyCmd(program: TopLevelCommand) {
},
);
}

async function selectModule(moduleRepo: KitModuleRepository) {
const options = moduleRepo.all.map((x) => ({
value: x.id,
name: `${x.kitModule.name} ${colors.dim(x.id)}`,
}));

return await Select.prompt({
message: "Select a kit module from your repository",
options,
info: true,
search: !isWindows, // see https://github.com/c4spar/deno-cliffy/issues/272#issuecomment-1262197264
});
}
66 changes: 19 additions & 47 deletions src/commands/kit/import.command.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import * as fs from "std/fs";
import * as path from "std/path";

import { GitCliFacade } from "../../api/git/GitCliFacade.ts";
import { Logger } from "../../cli/Logger.ts";
import { CollieRepository } from "../../model/CollieRepository.ts";
import { GlobalCommandOptions } from "../GlobalCommandOptions.ts";
import { TopLevelCommand } from "../TopLevelCommand.ts";
import { CliApiFacadeFactory } from "../../api/CliApiFacadeFactory.ts";
import { KitModuleRepository } from "../../kit/KitModuleRepository.ts";
import { ModelValidator } from "../../model/schemas/ModelValidator.ts";
import { InteractivePrompts } from "../interactive/InteractivePrompts.ts";
import { KitModuleHub } from "./KitModuleHub.ts";

interface ImportOptions {
clean?: boolean;
Expand All @@ -15,7 +17,7 @@ interface ImportOptions {

export function registerImportCmd(program: TopLevelCommand) {
program
.command("import <id>")
.command("import [id]")
.option(
"--force",
"overwrite existing kit module files in the local collie repository",
Expand All @@ -27,7 +29,7 @@ export function registerImportCmd(program: TopLevelCommand) {
.description(
"Import a published kit module from the official Landing Zone Construction Kit hub at https://github.com/meshcloud/landing-zone-construction-kit",
)
.action(async (opts: GlobalCommandOptions & ImportOptions, id: string) => {
.action(async (opts: GlobalCommandOptions & ImportOptions, id?: string) => {
const collie = new CollieRepository("./");
const logger = new Logger(collie, opts);

Expand All @@ -40,7 +42,11 @@ export function registerImportCmd(program: TopLevelCommand) {
logger.progress("cleaning local cache of hub modules");
await hub.cleanHubClone();
}

logger.progress("updating local cache of hub modules from " + hub.url);
const hubDir = await hub.updateHubClone();

id = id || (await promptForKitModuleId(logger, hubDir));

const dstPath = collie.resolvePath("kit", id);
try {
Expand Down Expand Up @@ -73,49 +79,15 @@ export function registerImportCmd(program: TopLevelCommand) {
});
}

export class KitModuleHub {
constructor(
private readonly git: GitCliFacade,
private readonly repo: CollieRepository,
) {}
private readonly hubCacheDirPath = [".collie", "hub"];

readonly url =
"https://github.com/meshcloud/landing-zone-construction-kit.git";

// IDEA: should we add a prompt for module id? we can parse modules from LZCK repo...
public async import(id: string, moduleDestDir: string, overwrite?: boolean) {
const hubDir = await this.updateHubClone();

const moduleSrcDir = path.resolve(hubDir, "kit", id);

await fs.copy(moduleSrcDir, moduleDestDir, { overwrite: overwrite });
}

private async updateHubClone() {
const hubCacheDir = this.repo.resolvePath(...this.hubCacheDirPath);

// we do keep a git clone of the repo locally because copying on the local FS is much faster than downloading and
// extracting a huge tarball
await fs.ensureDir(hubCacheDir);

const hubCacheGitDir = this.repo.resolvePath(
...this.hubCacheDirPath,
".git",
);
const hasAlreadyCloned = await this.git.isRepo(hubCacheGitDir);

if (hasAlreadyCloned) {
await this.git.pull(hubCacheDir);
} else {
await this.git.clone(hubCacheDir, this.url);
}
async function promptForKitModuleId(logger: Logger, hubRepoDir: string) {
// note: the hub is a standard collie repository for the most part, so we can just parse it with the same code
const repo = new CollieRepository(hubRepoDir);
const validator = new ModelValidator(logger);

return hubCacheDir;
}
const moduleRepo = await KitModuleRepository.load(repo, validator, logger);

public async cleanHubClone() {
const hubCacheDir = this.repo.resolvePath(...this.hubCacheDirPath);
await Deno.remove(hubCacheDir, { recursive: true });
}
return await InteractivePrompts.selectModule(
moduleRepo,
"official hub modules",
);
}

0 comments on commit d26b738

Please sign in to comment.