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 all 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();
};