Skip to content
This repository has been archived by the owner on Jan 5, 2022. It is now read-only.

feat: Link new and migrate command with the init functionality #66

Merged
merged 4 commits into from
Feb 17, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ ARGUMENTS
OPTIONS
-h, --help show CLI help
-v, --version show CLI version
--multi-app
--init create the cdk directory before building the app and stack files
--multi-app create the stack files within sub directories as the project defines multiple apps
```

_See code: [src/commands/migrate.ts](https://github.com/guardian/cdk-cli/blob/v0.0.0/src/commands/migrate.ts)_
Expand All @@ -112,7 +113,8 @@ ARGUMENTS
OPTIONS
-h, --help show CLI help
-v, --version show CLI version
--multi-app
--init create the cdk directory before building the app and stack files
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this should be the default behaviour for the new command? Perhaps we could consider that in a future PR though.

--multi-app create the stack files within sub directories as the project defines multiple apps
```

_See code: [src/commands/new.ts](https://github.com/guardian/cdk-cli/blob/v0.0.0/src/commands/new.ts)_
Expand Down
63 changes: 5 additions & 58 deletions src/commands/init.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,8 @@
import {
copyFileSync,
existsSync,
lstatSync,
mkdirSync,
readdirSync,
} from "fs";
import { join } from "path";
import { Command, flags } from "@oclif/command";
import { checkDirectoryIsEmpty } from "../utils/args";
import { buildDirectory } from "../utils/init";
import type { InitConfig } from "../utils/init";

interface InitCommandConfig {
outputDir: string;
}

interface InitCommandArgs {
export interface InitCommandArgs {
output: string;
}

Expand All @@ -37,67 +26,25 @@ export class InitCommand extends Command {
},
];

static getConfig = ({
args,
}: {
args: InitCommandArgs;
}): InitCommandConfig => {
static getConfig = ({ args }: { args: InitCommandArgs }): InitConfig => {
const config = {
outputDir: args.output,
};

InitCommand.validateConfig(config);

return config;
};

static validateConfig = (config: InitCommandConfig): void => {
checkDirectoryIsEmpty(config.outputDir);
};

templateDir = `${__dirname}/../template`;

// eslint-disable-next-line @typescript-eslint/require-await -- The Command class requires Promise<any> but we don't do anything async here
async run(): Promise<void> {
this.log("Starting CDK generator");

const config = InitCommand.getConfig(this.parse(InitCommand));

if (!existsSync(config.outputDir)) {
this.log(`Creating ${config.outputDir}`);
mkdirSync(config.outputDir);
}

this.log("Copying template files");
// TODO: Replace any params in files with .template extensions
this.copyFiles(this.templateDir, config.outputDir);
buildDirectory(config, this);

this.log("Success!");
// TODO: Can we do this here?
this.log("Run ./script/setup to get started");
this.log(
"To migrate existing stacks into your new directory you can run cdk-cli migrate"
);
this.log("To create a new stack run cdk-cli new");
}

copyFiles(sourcePath: string, targetPath: string): void {
for (const file of readdirSync(sourcePath)) {
const path = join(sourcePath, file);

if (path.endsWith(".ignore")) {
continue;
} else if (lstatSync(path).isDirectory()) {
const nestedTargetPath = join(targetPath, file);
if (!existsSync(nestedTargetPath)) {
mkdirSync(nestedTargetPath);
}
this.copyFiles(path, nestedTargetPath);
} else if (path.endsWith(".template")) {
copyFileSync(path, join(targetPath, file.replace(".template", "")));
} else {
copyFileSync(path, join(targetPath, file));
}
}
}
}
7 changes: 6 additions & 1 deletion src/commands/migrate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ describe("The MigrateCommand class", () => {
},
flags: {
"multi-app": false,
init: false,
},
};

Expand Down Expand Up @@ -49,7 +50,10 @@ describe("The MigrateCommand class", () => {

test("pulls outs computed values correctly if multiApp is true", () => {
expect(
MigrateCommand.getConfig({ ...args, flags: { "multi-app": true } })
MigrateCommand.getConfig({
...args,
flags: { "multi-app": true, init: false },
})
).toMatchObject({
cfnFile: "template.ts",
appPath: `/path/to/output/bin/app.ts`,
Expand All @@ -67,6 +71,7 @@ describe("The MigrateCommand class", () => {
},
flags: {
"multi-app": false,
init: false,
},
};

Expand Down
24 changes: 22 additions & 2 deletions src/commands/migrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from "../utils/args";
import { parse } from "../utils/cfn";
import { newAppImports, newTestImports } from "../utils/imports";
import { buildDirectory } from "../utils/init";
import { constructTest } from "../utils/snapshot";
import { constructStack } from "../utils/stack";
import type { Name } from "../utils/utils";
Expand All @@ -24,6 +25,7 @@ interface MigrateCommandConfig {
stackPath: string;
stackName: Name;
testPath: string;
init: boolean;
}

interface MigrateCommandArgs {
Expand All @@ -35,6 +37,7 @@ interface MigrateCommandArgs {

interface MigrateCommandFlags {
"multi-app": boolean;
init: boolean;
}

export class MigrateCommand extends Command {
Expand All @@ -44,7 +47,16 @@ export class MigrateCommand extends Command {
static flags = {
version: flags.version({ char: "v" }),
help: flags.help({ char: "h" }),
"multi-app": flags.boolean(),
"multi-app": flags.boolean({
default: false,
description:
"create the stack files within sub directories as the project defines multiple apps",
}),
init: flags.boolean({
default: false,
description:
"create the cdk directory before building the app and stack files",
}),
};

static args = [
Expand Down Expand Up @@ -106,6 +118,7 @@ export class MigrateCommand extends Command {
testPath: `${cdkDir}/lib/${
flags["multi-app"] ? `${kebabAppName}/` : ""
}${kebabStackName}.test.ts`,
init: flags["init"],
};

MigrateCommand.validateConfig(config);
Expand All @@ -116,7 +129,9 @@ export class MigrateCommand extends Command {
static validateConfig = (config: MigrateCommandConfig): void => {
// TODO: Do some better validation here to make sure that files and directories are what we expect them to be.
checkPathExists(config.cfnPath);
checkPathExists(config.cdkDir); // TODO: Add an option to init the CDK dir at the same time?
if (!config.init) {
checkPathExists(config.cdkDir);
}
checkPathDoesNotExist(config.appPath); // TODO: Update the app file if it already exists
checkPathDoesNotExist(config.stackPath);
};
Expand All @@ -126,6 +141,11 @@ export class MigrateCommand extends Command {

const config = MigrateCommand.getConfig(this.parse(MigrateCommand));

if (config.init) {
this.log("Creating CDK directory");
buildDirectory({ outputDir: config.cdkDir }, this);
}

this.log(`Converting template found at ${config.cfnPath}`);
this.log(
`New app ${config.appName.pascal} will be written to ${config.appPath}`
Expand Down
6 changes: 5 additions & 1 deletion src/commands/new.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ describe("The NewCommand class", () => {
},
flags: {
"multi-app": false,
init: false,
},
};

Expand Down Expand Up @@ -46,7 +47,10 @@ describe("The NewCommand class", () => {

test("pulls outs computed values correctly if multiApp is true", () => {
expect(
NewCommand.getConfig({ ...args, flags: { "multi-app": true } })
NewCommand.getConfig({
...args,
flags: { "multi-app": true, init: false },
})
).toMatchObject({
appPath: `/path/to/output/bin/app.ts`,
stackPath: `/path/to/output/lib/app/stack-name.ts`,
Expand Down
23 changes: 21 additions & 2 deletions src/commands/new.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
newStackImports,
newTestImports,
} from "../utils/imports";
import { buildDirectory } from "../utils/init";
import { cancellablePrompts } from "../utils/prompts";
import { constructTest } from "../utils/snapshot";
import type { StackTemplate } from "../utils/stack";
Expand All @@ -27,6 +28,7 @@ interface NewCommandConfig {
stackPath: string;
stackName: Name;
testPath: string;
init: boolean;
}

interface NewCommandArgs {
Expand All @@ -37,6 +39,7 @@ interface NewCommandArgs {

interface NewCommandFlags {
"multi-app": boolean;
init: boolean;
}

export class NewCommand extends Command {
Expand All @@ -45,7 +48,16 @@ export class NewCommand extends Command {
static flags = {
version: flags.version({ char: "v" }),
help: flags.help({ char: "h" }),
"multi-app": flags.boolean(),
"multi-app": flags.boolean({
default: false,
description:
"create the stack files within sub directories as the project defines multiple apps",
}),
init: flags.boolean({
default: false,
description:
"create the cdk directory before building the app and stack files",
}),
};

static args = [
Expand Down Expand Up @@ -102,6 +114,7 @@ export class NewCommand extends Command {
testPath: `${cdkDir}/lib/${
flags["multi-app"] ? `${kebabAppName}/` : ""
}${kebabStackName}.test.ts`,
init: flags["init"],
};

NewCommand.validateConfig(config);
Expand All @@ -110,7 +123,9 @@ export class NewCommand extends Command {
};

static validateConfig = (config: NewCommandConfig): void => {
checkPathExists(config.cdkDir); // TODO: Add an option to init the CDK dir at the same time?
if (!config.init) {
checkPathExists(config.cdkDir);
}
checkPathDoesNotExist(config.appPath); // TODO: Update the app file if it already exists
checkPathDoesNotExist(config.stackPath);
};
Expand All @@ -120,6 +135,10 @@ export class NewCommand extends Command {

const config = NewCommand.getConfig(this.parse(NewCommand));

if (config.init) {
buildDirectory({ outputDir: config.cdkDir }, this);
}

this.log(
`New app ${config.appName.pascal} will be written to ${config.appPath}`
);
Expand Down
17 changes: 17 additions & 0 deletions src/utils/init.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ProjectBuilder } from "./init";

describe("The ProjectBuilder class", () => {
describe("validateConfig function", () => {
test("throws an error if the directory is not empty", () => {
expect(() =>
ProjectBuilder.validateConfig({ outputDir: "./src" })
).toThrow();
});

test("does not throw an error if the directory is empty", () => {
expect(() =>
ProjectBuilder.validateConfig({ outputDir: "./src/empty" })
).not.toThrow();
});
});
});
73 changes: 73 additions & 0 deletions src/utils/init.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import {
copyFileSync,
existsSync,
lstatSync,
mkdirSync,
readdirSync,
} from "fs";
import { join } from "path";
import type Command from "@oclif/command";
import { checkDirectoryIsEmpty } from "../utils/args";

export interface InitConfig {
outputDir: string;
}

// TODO: Add yarn or npm flag
// TODO: Add project name flag
export class ProjectBuilder {
templateDir = `${__dirname}/../template`;
config: InitConfig;
command: Command;

static validateConfig = (config: InitConfig): void => {
checkDirectoryIsEmpty(config.outputDir);
};

constructor(config: InitConfig, command: Command) {
this.config = config;
this.command = command;
}

buildDirectory(): void {
ProjectBuilder.validateConfig(this.config);

if (!existsSync(this.config.outputDir)) {
this.command.log(`Creating ${this.config.outputDir}`);
mkdirSync(this.config.outputDir);
}

this.command.log("Copying template files");
// TODO: Replace any params in files with .template extensions
this.copyFiles(this.templateDir, this.config.outputDir);

this.command.log("Success!");
// TODO: Can we do this here?
this.command.log("Run ./script/setup to install dependencies");
}

copyFiles(sourcePath: string, targetPath: string): void {
for (const file of readdirSync(sourcePath)) {
const path = join(sourcePath, file);

if (path.endsWith(".ignore")) {
continue;
} else if (lstatSync(path).isDirectory()) {
const nestedTargetPath = join(targetPath, file);
if (!existsSync(nestedTargetPath)) {
mkdirSync(nestedTargetPath);
}
this.copyFiles(path, nestedTargetPath);
} else if (path.endsWith(".template")) {
copyFileSync(path, join(targetPath, file.replace(".template", "")));
} else {
copyFileSync(path, join(targetPath, file));
}
}
}
}

export const buildDirectory = (config: InitConfig, command: Command): void => {
const builder = new ProjectBuilder(config, command);
builder.buildDirectory();
};