Skip to content

Commit 0a7ce7b

Browse files
committed
feat(cli): git repository support for custom init templates
1 parent e46adaf commit 0a7ce7b

File tree

11 files changed

+944
-84
lines changed

11 files changed

+944
-84
lines changed

packages/@aws-cdk/user-input-gen/lib/yargs-gen.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,17 +105,22 @@ function makeYargs(config: CliConfig, helpers: CliHelpers): Statement {
105105
}
106106
commandCallArgs.push(lit(commandFacts.description));
107107

108-
if (commandFacts.options) {
109-
commandCallArgs.push(optionsExpr);
110-
}
111-
112108
// Add implies calls if present
113109
if (commandFacts.implies) {
114110
for (const [key, value] of Object.entries(commandFacts.implies)) {
115111
optionsExpr = optionsExpr.callMethod('implies', lit(key), lit(value));
116112
}
117113
}
118114

115+
// Add check function if present
116+
if (commandFacts.check) {
117+
optionsExpr = optionsExpr.callMethod('check', code.expr.directCode(commandFacts.check.toString()));
118+
}
119+
120+
if (commandFacts.options || commandFacts.check) {
121+
commandCallArgs.push(optionsExpr);
122+
}
123+
119124
yargsExpr = yargsExpr.callMethod('command', ...commandCallArgs);
120125
}
121126

packages/@aws-cdk/user-input-gen/lib/yargs-types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ interface YargsCommand {
88
export interface CliAction extends YargsCommand {
99
options?: { [optionName: string]: CliOption };
1010
implies?: { [key: string]: string };
11+
check?: (argv: any) => boolean | undefined;
1112
}
1213

1314
interface YargsArg {

packages/@aws-cdk/user-input-gen/test/yargs-gen.test.ts

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,29 @@ describe('render', () => {
105105
return yargs
106106
.env('CDK')
107107
.usage('Usage: cdk -a <cdk-app> COMMAND')
108-
.command(['test', 'spec'], 'the action under test', (yargs: Argv) =>
109-
yargs
110-
.option('one', {
111-
default: undefined,
112-
type: 'boolean',
113-
alias: 'o',
114-
desc: 'text for one',
115-
})
116-
.option('O', { type: 'boolean', hidden: true })
117-
.middleware(helpers.yargsNegativeAlias('O', 'one'), true),
108+
.command(
109+
['test', 'spec'],
110+
'the action under test',
111+
(yargs: Argv) =>
112+
yargs
113+
.option('one', {
114+
default: undefined,
115+
type: 'boolean',
116+
alias: 'o',
117+
desc: 'text for one',
118+
})
119+
.option('O', { type: 'boolean', hidden: true })
120+
.middleware(helpers.yargsNegativeAlias('O', 'one'), true),
121+
(yargs: Argv) =>
122+
yargs
123+
.option('one', {
124+
default: undefined,
125+
type: 'boolean',
126+
alias: 'o',
127+
desc: 'text for one',
128+
})
129+
.option('O', { type: 'boolean', hidden: true })
130+
.middleware(helpers.yargsNegativeAlias('O', 'one'), true),
118131
)
119132
.version(helpers.cliVersion())
120133
.demandCommand(1, '')
@@ -186,20 +199,37 @@ describe('render', () => {
186199
return yargs
187200
.env('CDK')
188201
.usage('Usage: cdk -a <cdk-app> COMMAND')
189-
.command('deploy', 'Notification Arns', (yargs: Argv) =>
190-
yargs
191-
.option('notification-arns', {
192-
type: 'array',
193-
desc: 'Deploy all stacks',
194-
nargs: 1,
195-
requiresArg: true,
196-
})
197-
.option('other-array', {
198-
type: 'array',
199-
desc: 'Other array',
200-
nargs: 1,
201-
requiresArg: true,
202-
}),
202+
.command(
203+
'deploy',
204+
'Notification Arns',
205+
(yargs: Argv) =>
206+
yargs
207+
.option('notification-arns', {
208+
type: 'array',
209+
desc: 'Deploy all stacks',
210+
nargs: 1,
211+
requiresArg: true,
212+
})
213+
.option('other-array', {
214+
type: 'array',
215+
desc: 'Other array',
216+
nargs: 1,
217+
requiresArg: true,
218+
}),
219+
(yargs: Argv) =>
220+
yargs
221+
.option('notification-arns', {
222+
type: 'array',
223+
desc: 'Deploy all stacks',
224+
nargs: 1,
225+
requiresArg: true,
226+
})
227+
.option('other-array', {
228+
type: 'array',
229+
desc: 'Other array',
230+
nargs: 1,
231+
requiresArg: true,
232+
}),
203233
)
204234
.version(helpers.cliVersion())
205235
.demandCommand(1, '')

packages/aws-cdk/lib/cli/cli-config.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ import { getLanguageAlias } from '../commands/language';
88

99
export const YARGS_HELPERS = new CliHelpers('./util/yargs-helpers');
1010

11+
interface InitCommandArgs {
12+
'template-path'?: string;
13+
'from-path'?: string;
14+
'from-git-url'?: string;
15+
[key: string]: unknown;
16+
}
17+
1118
/**
1219
* Source of truth for all CDK CLI commands. `user-input-gen` translates this into:
1320
*
@@ -406,8 +413,22 @@ export async function makeConfig(): Promise<CliConfig> {
406413
'lib-version': { type: 'string', alias: 'V', default: undefined, desc: 'The version of the CDK library (aws-cdk-lib) to initialize built-in templates with. Defaults to the version that was current when this CLI was built.' },
407414
'from-path': { type: 'string', desc: 'Path to a local custom template directory or multi-template repository', requiresArg: true, conflicts: ['lib-version'] },
408415
'template-path': { type: 'string', desc: 'Path to a specific template within a multi-template repository', requiresArg: true },
416+
'from-git-url': { type: 'string', desc: 'Git repository URL to clone custom template from', requiresArg: true, conflicts: ['lib-version', 'from-path'] },
417+
},
418+
check: (argv: InitCommandArgs) => {
419+
const hasTemplatePath = Boolean(argv['template-path']);
420+
const hasValidSource = Boolean(argv['from-path'] || argv['from-git-url']);
421+
422+
if (hasTemplatePath && !hasValidSource) {
423+
const e = new Error(
424+
'--template-path can only be used with --from-path or --from-git-url',
425+
);
426+
e.name = 'ValidationError';
427+
throw e;
428+
}
429+
430+
return true;
409431
},
410-
implies: { 'template-path': 'from-path' },
411432
},
412433
'migrate': {
413434
description: 'Migrate existing AWS resources into a CDK app',

packages/aws-cdk/lib/cli/cli-type-registry.json

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -897,10 +897,16 @@
897897
"type": "string",
898898
"desc": "Path to a specific template within a multi-template repository",
899899
"requiresArg": true
900+
},
901+
"from-git-url": {
902+
"type": "string",
903+
"desc": "Git repository URL to clone custom template from",
904+
"requiresArg": true,
905+
"conflicts": [
906+
"lib-version",
907+
"from-path"
908+
]
900909
}
901-
},
902-
"implies": {
903-
"template-path": "from-path"
904910
}
905911
},
906912
"migrate": {

packages/aws-cdk/lib/cli/cli.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ export async function exec(args: string[], synthesizer?: Synthesizer): Promise<n
547547
return printAvailableTemplates(ioHelper, language);
548548
} else {
549549
// Gate custom template support with unstable flag
550-
if (args['from-path'] && !configuration.settings.get(['unstable']).includes('init')) {
550+
if ((args['from-path'] || args['from-git-url']) && !configuration.settings.get(['unstable']).includes('init')) {
551551
throw new ToolkitError('Unstable feature use: \'init\' with custom templates is unstable. It must be opted in via \'--unstable\', e.g. \'cdk init --from-path=./my-template --unstable=init\'');
552552
}
553553
return cliInit({
@@ -558,6 +558,7 @@ export async function exec(args: string[], synthesizer?: Synthesizer): Promise<n
558558
generateOnly: args.generateOnly,
559559
libVersion: args.libVersion,
560560
fromPath: args['from-path'],
561+
fromGitUrl: args['from-git-url'],
561562
templatePath: args['template-path'],
562563
});
563564
}

packages/aws-cdk/lib/cli/convert-to-user-input.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ export function convertYargsToUserInput(args: any): UserInput {
249249
libVersion: args.libVersion,
250250
fromPath: args.fromPath,
251251
templatePath: args.templatePath,
252+
fromGitUrl: args.fromGitUrl,
252253
TEMPLATE: args.TEMPLATE,
253254
};
254255
break;
@@ -491,6 +492,7 @@ export function convertConfigToUserInput(config: any): UserInput {
491492
libVersion: config.init?.libVersion,
492493
fromPath: config.init?.fromPath,
493494
templatePath: config.init?.templatePath,
495+
fromGitUrl: config.init?.fromGitUrl,
494496
};
495497
const migrateOptions = {
496498
stackName: config.migrate?.stackName,

packages/aws-cdk/lib/cli/parse-command-line-arguments.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,23 @@ export function parseCommandLineArguments(args: Array<string>): any {
888888
type: 'string',
889889
desc: 'Path to a specific template within a multi-template repository',
890890
requiresArg: true,
891+
})
892+
.option('from-git-url', {
893+
default: undefined,
894+
type: 'string',
895+
desc: 'Git repository URL to clone custom template from',
896+
requiresArg: true,
897+
conflicts: ['lib-version', 'from-path'],
898+
})
899+
.check((argv) => {
900+
const hasTemplatePath = Boolean(argv['template-path']);
901+
const hasValidSource = Boolean(argv['from-path'] || argv['from-git-url']);
902+
if (hasTemplatePath && !hasValidSource) {
903+
const e = new Error('--template-path can only be used with --from-path or --from-git-url');
904+
e.name = 'ValidationError';
905+
throw e;
906+
}
907+
return true;
891908
}),
892909
)
893910
.command('migrate', 'Migrate existing AWS resources into a CDK app', (yargs: Argv) =>

packages/aws-cdk/lib/cli/user-input.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1401,6 +1401,13 @@ export interface InitOptions {
14011401
*/
14021402
readonly templatePath?: string;
14031403

1404+
/**
1405+
* Git repository URL to clone custom template from
1406+
*
1407+
* @default - undefined
1408+
*/
1409+
readonly fromGitUrl?: string;
1410+
14041411
/**
14051412
* Positional argument for init
14061413
*/

0 commit comments

Comments
 (0)