quidproquo-cli 0.1.18 → 0.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/README.md +1 -0
  2. package/lib/commonjs/cli/commandRegistry.js +6 -0
  3. package/lib/commonjs/cli/commandRegistry.js.map +1 -1
  4. package/lib/commonjs/commands/goDevApi.js +54 -8
  5. package/lib/commonjs/commands/goDevApi.js.map +1 -1
  6. package/lib/commonjs/commands/goDevWeb.js +10 -2
  7. package/lib/commonjs/commands/goDevWeb.js.map +1 -1
  8. package/lib/commonjs/commands/setup.d.ts +1 -0
  9. package/lib/commonjs/commands/setup.js +102 -0
  10. package/lib/commonjs/commands/setup.js.map +1 -0
  11. package/lib/commonjs/lib/args.d.ts +1 -0
  12. package/lib/commonjs/lib/args.js +11 -1
  13. package/lib/commonjs/lib/args.js.map +1 -1
  14. package/lib/commonjs/lib/keepOtherDevServersFlag.d.ts +1 -0
  15. package/lib/commonjs/lib/keepOtherDevServersFlag.js +11 -0
  16. package/lib/commonjs/lib/keepOtherDevServersFlag.js.map +1 -0
  17. package/lib/commonjs/lib/killChildWithEscalation.d.ts +10 -0
  18. package/lib/commonjs/lib/killChildWithEscalation.js +33 -0
  19. package/lib/commonjs/lib/killChildWithEscalation.js.map +1 -0
  20. package/lib/commonjs/lib/killStaleListeners.d.ts +14 -0
  21. package/lib/commonjs/lib/killStaleListeners.js +74 -24
  22. package/lib/commonjs/lib/killStaleListeners.js.map +1 -1
  23. package/lib/commonjs/lib/prompts.d.ts +2 -0
  24. package/lib/commonjs/lib/prompts.js +1 -1
  25. package/lib/commonjs/lib/prompts.js.map +1 -1
  26. package/lib/commonjs/lib/setupStep.d.ts +16 -0
  27. package/lib/commonjs/lib/setupStep.js +3 -0
  28. package/lib/commonjs/lib/setupStep.js.map +1 -0
  29. package/lib/commonjs/platforms/aws/index.js +2 -0
  30. package/lib/commonjs/platforms/aws/index.js.map +1 -1
  31. package/lib/commonjs/platforms/aws/setup/awsSetupContext.d.ts +12 -0
  32. package/lib/commonjs/platforms/aws/setup/awsSetupContext.js +23 -0
  33. package/lib/commonjs/platforms/aws/setup/awsSetupContext.js.map +1 -0
  34. package/lib/commonjs/platforms/aws/setup/awsSetupSteps.d.ts +8 -0
  35. package/lib/commonjs/platforms/aws/setup/awsSetupSteps.js +44 -0
  36. package/lib/commonjs/platforms/aws/setup/awsSetupSteps.js.map +1 -0
  37. package/lib/commonjs/platforms/aws/setup/buildBucketNamesStep.d.ts +4 -0
  38. package/lib/commonjs/platforms/aws/setup/buildBucketNamesStep.js +77 -0
  39. package/lib/commonjs/platforms/aws/setup/buildBucketNamesStep.js.map +1 -0
  40. package/lib/commonjs/platforms/aws/setup/buildCdkBootstrapSteps.d.ts +5 -0
  41. package/lib/commonjs/platforms/aws/setup/buildCdkBootstrapSteps.js +38 -0
  42. package/lib/commonjs/platforms/aws/setup/buildCdkBootstrapSteps.js.map +1 -0
  43. package/lib/commonjs/platforms/aws/setup/buildGithubDeployRoleStep.d.ts +4 -0
  44. package/lib/commonjs/platforms/aws/setup/buildGithubDeployRoleStep.js +55 -0
  45. package/lib/commonjs/platforms/aws/setup/buildGithubDeployRoleStep.js.map +1 -0
  46. package/lib/commonjs/platforms/aws/setup/buildGithubOidcProviderStep.d.ts +4 -0
  47. package/lib/commonjs/platforms/aws/setup/buildGithubOidcProviderStep.js +54 -0
  48. package/lib/commonjs/platforms/aws/setup/buildGithubOidcProviderStep.js.map +1 -0
  49. package/lib/commonjs/platforms/aws/setup/buildHostedZonesStep.d.ts +4 -0
  50. package/lib/commonjs/platforms/aws/setup/buildHostedZonesStep.js +63 -0
  51. package/lib/commonjs/platforms/aws/setup/buildHostedZonesStep.js.map +1 -0
  52. package/lib/commonjs/platforms/aws/setup/buildIamCapabilityStep.d.ts +4 -0
  53. package/lib/commonjs/platforms/aws/setup/buildIamCapabilityStep.js +48 -0
  54. package/lib/commonjs/platforms/aws/setup/buildIamCapabilityStep.js.map +1 -0
  55. package/lib/commonjs/platforms/aws/setup/buildIdentityStep.d.ts +4 -0
  56. package/lib/commonjs/platforms/aws/setup/buildIdentityStep.js +39 -0
  57. package/lib/commonjs/platforms/aws/setup/buildIdentityStep.js.map +1 -0
  58. package/lib/commonjs/platforms/aws/setup/buildStackSteps.d.ts +5 -0
  59. package/lib/commonjs/platforms/aws/setup/buildStackSteps.js +32 -0
  60. package/lib/commonjs/platforms/aws/setup/buildStackSteps.js.map +1 -0
  61. package/lib/commonjs/platforms/aws/setup/describeStackStatus.d.ts +4 -0
  62. package/lib/commonjs/platforms/aws/setup/describeStackStatus.js +32 -0
  63. package/lib/commonjs/platforms/aws/setup/describeStackStatus.js.map +1 -0
  64. package/lib/commonjs/platforms/aws/setup/index.d.ts +1 -0
  65. package/lib/commonjs/platforms/aws/setup/index.js +18 -0
  66. package/lib/commonjs/platforms/aws/setup/index.js.map +1 -0
  67. package/lib/commonjs/platforms/types.d.ts +2 -0
  68. package/lib/commonjs/platforms/types.js.map +1 -1
  69. package/lib/esm/cli/commandRegistry.js +6 -0
  70. package/lib/esm/cli/commandRegistry.js.map +1 -1
  71. package/lib/esm/commands/goDevApi.js +55 -9
  72. package/lib/esm/commands/goDevApi.js.map +1 -1
  73. package/lib/esm/commands/goDevWeb.js +10 -2
  74. package/lib/esm/commands/goDevWeb.js.map +1 -1
  75. package/lib/esm/commands/setup.d.ts +1 -0
  76. package/lib/esm/commands/setup.js +88 -0
  77. package/lib/esm/commands/setup.js.map +1 -0
  78. package/lib/esm/lib/args.d.ts +1 -0
  79. package/lib/esm/lib/args.js +9 -0
  80. package/lib/esm/lib/args.js.map +1 -1
  81. package/lib/esm/lib/keepOtherDevServersFlag.d.ts +1 -0
  82. package/lib/esm/lib/keepOtherDevServersFlag.js +8 -0
  83. package/lib/esm/lib/keepOtherDevServersFlag.js.map +1 -0
  84. package/lib/esm/lib/killChildWithEscalation.d.ts +10 -0
  85. package/lib/esm/lib/killChildWithEscalation.js +29 -0
  86. package/lib/esm/lib/killChildWithEscalation.js.map +1 -0
  87. package/lib/esm/lib/killStaleListeners.d.ts +14 -0
  88. package/lib/esm/lib/killStaleListeners.js +72 -23
  89. package/lib/esm/lib/killStaleListeners.js.map +1 -1
  90. package/lib/esm/lib/prompts.d.ts +2 -0
  91. package/lib/esm/lib/prompts.js +1 -1
  92. package/lib/esm/lib/prompts.js.map +1 -1
  93. package/lib/esm/lib/setupStep.d.ts +16 -0
  94. package/lib/esm/lib/setupStep.js +2 -0
  95. package/lib/esm/lib/setupStep.js.map +1 -0
  96. package/lib/esm/platforms/aws/index.js +2 -0
  97. package/lib/esm/platforms/aws/index.js.map +1 -1
  98. package/lib/esm/platforms/aws/setup/awsSetupContext.d.ts +12 -0
  99. package/lib/esm/platforms/aws/setup/awsSetupContext.js +18 -0
  100. package/lib/esm/platforms/aws/setup/awsSetupContext.js.map +1 -0
  101. package/lib/esm/platforms/aws/setup/awsSetupSteps.d.ts +8 -0
  102. package/lib/esm/platforms/aws/setup/awsSetupSteps.js +31 -0
  103. package/lib/esm/platforms/aws/setup/awsSetupSteps.js.map +1 -0
  104. package/lib/esm/platforms/aws/setup/buildBucketNamesStep.d.ts +4 -0
  105. package/lib/esm/platforms/aws/setup/buildBucketNamesStep.js +62 -0
  106. package/lib/esm/platforms/aws/setup/buildBucketNamesStep.js.map +1 -0
  107. package/lib/esm/platforms/aws/setup/buildCdkBootstrapSteps.d.ts +5 -0
  108. package/lib/esm/platforms/aws/setup/buildCdkBootstrapSteps.js +24 -0
  109. package/lib/esm/platforms/aws/setup/buildCdkBootstrapSteps.js.map +1 -0
  110. package/lib/esm/platforms/aws/setup/buildGithubDeployRoleStep.d.ts +4 -0
  111. package/lib/esm/platforms/aws/setup/buildGithubDeployRoleStep.js +40 -0
  112. package/lib/esm/platforms/aws/setup/buildGithubDeployRoleStep.js.map +1 -0
  113. package/lib/esm/platforms/aws/setup/buildGithubOidcProviderStep.d.ts +4 -0
  114. package/lib/esm/platforms/aws/setup/buildGithubOidcProviderStep.js +40 -0
  115. package/lib/esm/platforms/aws/setup/buildGithubOidcProviderStep.js.map +1 -0
  116. package/lib/esm/platforms/aws/setup/buildHostedZonesStep.d.ts +4 -0
  117. package/lib/esm/platforms/aws/setup/buildHostedZonesStep.js +49 -0
  118. package/lib/esm/platforms/aws/setup/buildHostedZonesStep.js.map +1 -0
  119. package/lib/esm/platforms/aws/setup/buildIamCapabilityStep.d.ts +4 -0
  120. package/lib/esm/platforms/aws/setup/buildIamCapabilityStep.js +34 -0
  121. package/lib/esm/platforms/aws/setup/buildIamCapabilityStep.js.map +1 -0
  122. package/lib/esm/platforms/aws/setup/buildIdentityStep.d.ts +4 -0
  123. package/lib/esm/platforms/aws/setup/buildIdentityStep.js +26 -0
  124. package/lib/esm/platforms/aws/setup/buildIdentityStep.js.map +1 -0
  125. package/lib/esm/platforms/aws/setup/buildStackSteps.d.ts +5 -0
  126. package/lib/esm/platforms/aws/setup/buildStackSteps.js +17 -0
  127. package/lib/esm/platforms/aws/setup/buildStackSteps.js.map +1 -0
  128. package/lib/esm/platforms/aws/setup/describeStackStatus.d.ts +4 -0
  129. package/lib/esm/platforms/aws/setup/describeStackStatus.js +17 -0
  130. package/lib/esm/platforms/aws/setup/describeStackStatus.js.map +1 -0
  131. package/lib/esm/platforms/aws/setup/index.d.ts +1 -0
  132. package/lib/esm/platforms/aws/setup/index.js +2 -0
  133. package/lib/esm/platforms/aws/setup/index.js.map +1 -0
  134. package/lib/esm/platforms/types.d.ts +2 -0
  135. package/lib/esm/platforms/types.js.map +1 -1
  136. package/package.json +13 -10
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.buildGithubDeployRoleStep = void 0;
13
+ const quidproquo_actionprocessor_awslambda_1 = require("quidproquo-actionprocessor-awslambda");
14
+ const quidproquo_config_aws_1 = require("quidproquo-config-aws");
15
+ const client_iam_1 = require("@aws-sdk/client-iam");
16
+ // The role itself is deployed by the bootstrap stack; this step tells you whether the config
17
+ // declares it, whether it exists yet, and what to put in the GitHub Environment once it does.
18
+ const checkDeployRole = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
19
+ var _a;
20
+ const config = quidproquo_config_aws_1.qpqConfigAwsUtils.getAwsGithubDeployRoleConfig(ctx.bootstrapQpqConfig);
21
+ if (!config) {
22
+ return { done: false, detail: 'not declared: add defineAwsGithubDeployRole("owner/repo", { ownerId, repositoryId }) to bootstrap.qpq.ts' };
23
+ }
24
+ const roleName = quidproquo_actionprocessor_awslambda_1.awsNamingUtils.getGithubDeployRoleNameFromConfig(ctx.bootstrapQpqConfig);
25
+ const githubEnvironment = (_a = config.githubEnvironment) !== null && _a !== void 0 ? _a : ctx.environment;
26
+ try {
27
+ const { Role } = yield new client_iam_1.IAMClient({ region: ctx.region }).send(new client_iam_1.GetRoleCommand({ RoleName: roleName }));
28
+ return {
29
+ done: true,
30
+ detail: `${Role === null || Role === void 0 ? void 0 : Role.Arn}; GitHub Environment '${githubEnvironment}' needs DEPLOY_ROLE_ARN=<that arn> and AWS_REGION=${ctx.region}`,
31
+ };
32
+ }
33
+ catch (error) {
34
+ if (error instanceof Error && error.name === 'NoSuchEntityException') {
35
+ return { done: false, detail: `${roleName} not deployed yet (the bootstrap stack creates it)` };
36
+ }
37
+ throw error;
38
+ }
39
+ });
40
+ /** Check-only: the GitHub Actions deploy role is declared and deployed, with the values the GitHub Environment needs. */
41
+ const buildGithubDeployRoleStep = (ctx) => ({
42
+ id: 'github-deploy-role',
43
+ name: 'GitHub Actions deploy role',
44
+ check: () => checkDeployRole(ctx),
45
+ run: () => __awaiter(void 0, void 0, void 0, function* () {
46
+ var _a;
47
+ const result = yield checkDeployRole(ctx);
48
+ if (!result.done) {
49
+ throw new Error((_a = result.detail) !== null && _a !== void 0 ? _a : 'deploy role missing');
50
+ }
51
+ console.log(result.detail);
52
+ }),
53
+ });
54
+ exports.buildGithubDeployRoleStep = buildGithubDeployRoleStep;
55
+ //# sourceMappingURL=buildGithubDeployRoleStep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildGithubDeployRoleStep.js","sourceRoot":"","sources":["../../../../../src/platforms/aws/setup/buildGithubDeployRoleStep.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,+FAAsE;AACtE,iEAA0D;AAE1D,oDAAgE;AAKhE,6FAA6F;AAC7F,8FAA8F;AAC9F,MAAM,eAAe,GAAG,CAAO,GAAoB,EAA6B,EAAE;;IAChF,MAAM,MAAM,GAAG,yCAAiB,CAAC,4BAA4B,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IACtF,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,0GAA0G,EAAE,CAAC;IAC7I,CAAC;IAED,MAAM,QAAQ,GAAG,qDAAc,CAAC,iCAAiC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAC1F,MAAM,iBAAiB,GAAG,MAAA,MAAM,CAAC,iBAAiB,mCAAI,GAAG,CAAC,WAAW,CAAC;IACtE,IAAI,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,sBAAS,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,2BAAc,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;QAC9G,OAAO;YACL,IAAI,EAAE,IAAI;YACV,MAAM,EAAE,GAAG,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,GAAG,yBAAyB,iBAAiB,qDAAqD,GAAG,CAAC,MAAM,EAAE;SAChI,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,uBAAuB,EAAE,CAAC;YACrE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,QAAQ,oDAAoD,EAAE,CAAC;QAClG,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC,CAAA,CAAC;AAEF,yHAAyH;AAClH,MAAM,yBAAyB,GAAG,CAAC,GAAoB,EAAa,EAAE,CAAC,CAAC;IAC7E,EAAE,EAAE,oBAAoB;IACxB,IAAI,EAAE,4BAA4B;IAClC,KAAK,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC;IACjC,GAAG,EAAE,GAAS,EAAE;;QACd,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,MAAA,MAAM,CAAC,MAAM,mCAAI,qBAAqB,CAAC,CAAC;QAC1D,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC,CAAA;CACF,CAAC,CAAC;AAXU,QAAA,yBAAyB,6BAWnC","sourcesContent":["import { awsNamingUtils } from 'quidproquo-actionprocessor-awslambda';\nimport { qpqConfigAwsUtils } from 'quidproquo-config-aws';\n\nimport { GetRoleCommand, IAMClient } from '@aws-sdk/client-iam';\n\nimport { SetupCheckResult, SetupStep } from '../../../lib/setupStep';\nimport { AwsSetupContext } from './awsSetupContext';\n\n// The role itself is deployed by the bootstrap stack; this step tells you whether the config\n// declares it, whether it exists yet, and what to put in the GitHub Environment once it does.\nconst checkDeployRole = async (ctx: AwsSetupContext): Promise<SetupCheckResult> => {\n const config = qpqConfigAwsUtils.getAwsGithubDeployRoleConfig(ctx.bootstrapQpqConfig);\n if (!config) {\n return { done: false, detail: 'not declared: add defineAwsGithubDeployRole(\"owner/repo\", { ownerId, repositoryId }) to bootstrap.qpq.ts' };\n }\n\n const roleName = awsNamingUtils.getGithubDeployRoleNameFromConfig(ctx.bootstrapQpqConfig);\n const githubEnvironment = config.githubEnvironment ?? ctx.environment;\n try {\n const { Role } = await new IAMClient({ region: ctx.region }).send(new GetRoleCommand({ RoleName: roleName }));\n return {\n done: true,\n detail: `${Role?.Arn}; GitHub Environment '${githubEnvironment}' needs DEPLOY_ROLE_ARN=<that arn> and AWS_REGION=${ctx.region}`,\n };\n } catch (error) {\n if (error instanceof Error && error.name === 'NoSuchEntityException') {\n return { done: false, detail: `${roleName} not deployed yet (the bootstrap stack creates it)` };\n }\n throw error;\n }\n};\n\n/** Check-only: the GitHub Actions deploy role is declared and deployed, with the values the GitHub Environment needs. */\nexport const buildGithubDeployRoleStep = (ctx: AwsSetupContext): SetupStep => ({\n id: 'github-deploy-role',\n name: 'GitHub Actions deploy role',\n check: () => checkDeployRole(ctx),\n run: async () => {\n const result = await checkDeployRole(ctx);\n if (!result.done) {\n throw new Error(result.detail ?? 'deploy role missing');\n }\n console.log(result.detail);\n },\n});\n"]}
@@ -0,0 +1,4 @@
1
+ import { SetupStep } from '../../../lib/setupStep';
2
+ import { AwsSetupContext } from './awsSetupContext';
3
+ /** Check-only: the account's GitHub Actions OIDC provider is declared in account.qpq.ts and deployed. */
4
+ export declare const buildGithubOidcProviderStep: (ctx: AwsSetupContext) => SetupStep;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.buildGithubOidcProviderStep = void 0;
13
+ const quidproquo_config_aws_1 = require("quidproquo-config-aws");
14
+ const client_iam_1 = require("@aws-sdk/client-iam");
15
+ // The provider is created by the account stack (defineAccountGithubOidcProvider in
16
+ // account.qpq.ts), so this only reports: declared or not, present or not.
17
+ const checkProvider = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
18
+ const declared = quidproquo_config_aws_1.qpqConfigAwsUtils.isAccountGithubOidcProviderDeclared(ctx.accountQpqConfig);
19
+ const arn = quidproquo_config_aws_1.qpqConfigAwsUtils.getGithubOidcProviderArn(ctx.accountId);
20
+ let exists = false;
21
+ try {
22
+ yield new client_iam_1.IAMClient({ region: ctx.region }).send(new client_iam_1.GetOpenIDConnectProviderCommand({ OpenIDConnectProviderArn: arn }));
23
+ exists = true;
24
+ }
25
+ catch (error) {
26
+ if (!(error instanceof Error && error.name === 'NoSuchEntityException')) {
27
+ throw error;
28
+ }
29
+ }
30
+ if (!declared) {
31
+ return {
32
+ done: false,
33
+ detail: exists
34
+ ? `${arn} exists but account.qpq.ts does not declare it: add defineAccountGithubOidcProvider() and delete the hand-made provider before deploying the account stack`
35
+ : 'not declared: add defineAccountGithubOidcProvider() to account.qpq.ts (the account stack creates it)',
36
+ };
37
+ }
38
+ return exists ? { done: true, detail: arn } : { done: false, detail: 'declared, not deployed yet (the account stack creates it)' };
39
+ });
40
+ /** Check-only: the account's GitHub Actions OIDC provider is declared in account.qpq.ts and deployed. */
41
+ const buildGithubOidcProviderStep = (ctx) => ({
42
+ id: 'github-oidc-provider',
43
+ name: 'GitHub Actions OIDC provider',
44
+ check: () => checkProvider(ctx),
45
+ run: () => __awaiter(void 0, void 0, void 0, function* () {
46
+ var _a;
47
+ const result = yield checkProvider(ctx);
48
+ if (!result.done) {
49
+ throw new Error((_a = result.detail) !== null && _a !== void 0 ? _a : 'provider missing');
50
+ }
51
+ }),
52
+ });
53
+ exports.buildGithubOidcProviderStep = buildGithubOidcProviderStep;
54
+ //# sourceMappingURL=buildGithubOidcProviderStep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildGithubOidcProviderStep.js","sourceRoot":"","sources":["../../../../../src/platforms/aws/setup/buildGithubOidcProviderStep.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,iEAA0D;AAE1D,oDAAiF;AAKjF,mFAAmF;AACnF,0EAA0E;AAC1E,MAAM,aAAa,GAAG,CAAO,GAAoB,EAA6B,EAAE;IAC9E,MAAM,QAAQ,GAAG,yCAAiB,CAAC,mCAAmC,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC7F,MAAM,GAAG,GAAG,yCAAiB,CAAC,wBAAwB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAEtE,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,IAAI,sBAAS,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,4CAA+B,CAAC,EAAE,wBAAwB,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;QACzH,MAAM,GAAG,IAAI,CAAC;IAChB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,uBAAuB,CAAC,EAAE,CAAC;YACxE,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO;YACL,IAAI,EAAE,KAAK;YACX,MAAM,EAAE,MAAM;gBACZ,CAAC,CAAC,GAAG,GAAG,4JAA4J;gBACpK,CAAC,CAAC,sGAAsG;SAC3G,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,2DAA2D,EAAE,CAAC;AACrI,CAAC,CAAA,CAAC;AAEF,yGAAyG;AAClG,MAAM,2BAA2B,GAAG,CAAC,GAAoB,EAAa,EAAE,CAAC,CAAC;IAC/E,EAAE,EAAE,sBAAsB;IAC1B,IAAI,EAAE,8BAA8B;IACpC,KAAK,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC;IAC/B,GAAG,EAAE,GAAS,EAAE;;QACd,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,MAAA,MAAM,CAAC,MAAM,mCAAI,kBAAkB,CAAC,CAAC;QACvD,CAAC;IACH,CAAC,CAAA;CACF,CAAC,CAAC;AAVU,QAAA,2BAA2B,+BAUrC","sourcesContent":["import { qpqConfigAwsUtils } from 'quidproquo-config-aws';\n\nimport { GetOpenIDConnectProviderCommand, IAMClient } from '@aws-sdk/client-iam';\n\nimport { SetupCheckResult, SetupStep } from '../../../lib/setupStep';\nimport { AwsSetupContext } from './awsSetupContext';\n\n// The provider is created by the account stack (defineAccountGithubOidcProvider in\n// account.qpq.ts), so this only reports: declared or not, present or not.\nconst checkProvider = async (ctx: AwsSetupContext): Promise<SetupCheckResult> => {\n const declared = qpqConfigAwsUtils.isAccountGithubOidcProviderDeclared(ctx.accountQpqConfig);\n const arn = qpqConfigAwsUtils.getGithubOidcProviderArn(ctx.accountId);\n\n let exists = false;\n try {\n await new IAMClient({ region: ctx.region }).send(new GetOpenIDConnectProviderCommand({ OpenIDConnectProviderArn: arn }));\n exists = true;\n } catch (error) {\n if (!(error instanceof Error && error.name === 'NoSuchEntityException')) {\n throw error;\n }\n }\n\n if (!declared) {\n return {\n done: false,\n detail: exists\n ? `${arn} exists but account.qpq.ts does not declare it: add defineAccountGithubOidcProvider() and delete the hand-made provider before deploying the account stack`\n : 'not declared: add defineAccountGithubOidcProvider() to account.qpq.ts (the account stack creates it)',\n };\n }\n\n return exists ? { done: true, detail: arn } : { done: false, detail: 'declared, not deployed yet (the account stack creates it)' };\n};\n\n/** Check-only: the account's GitHub Actions OIDC provider is declared in account.qpq.ts and deployed. */\nexport const buildGithubOidcProviderStep = (ctx: AwsSetupContext): SetupStep => ({\n id: 'github-oidc-provider',\n name: 'GitHub Actions OIDC provider',\n check: () => checkProvider(ctx),\n run: async () => {\n const result = await checkProvider(ctx);\n if (!result.done) {\n throw new Error(result.detail ?? 'provider missing');\n }\n },\n});\n"]}
@@ -0,0 +1,4 @@
1
+ import { SetupStep } from '../../../lib/setupStep';
2
+ import { AwsSetupContext } from './awsSetupContext';
3
+ /** Check-only: the Route53 zones the deploy will write records into exist in this account. */
4
+ export declare const buildHostedZonesStep: (ctx: AwsSetupContext) => SetupStep;
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.buildHostedZonesStep = void 0;
13
+ const quidproquo_deploy_awscdk_1 = require("quidproquo-deploy-awscdk");
14
+ const quidproquo_webserver_1 = require("quidproquo-webserver");
15
+ const client_route_53_1 = require("@aws-sdk/client-route-53");
16
+ // The zones the environment writes records into: one per root, chosen the way the
17
+ // CDK chooses them (each root's site root under the app's resolver, or the root).
18
+ const getRequiredZones = (ctx) => {
19
+ const config = ctx.bootstrapQpqConfig;
20
+ return [
21
+ ...new Set(quidproquo_webserver_1.qpqWebServerUtils
22
+ .getRootDomains(config)
23
+ .map((rootDomain) => quidproquo_deploy_awscdk_1.qpqDeployAwsCdkUtils.resolveHostedZoneForHost(config, quidproquo_deploy_awscdk_1.qpqDeployAwsCdkUtils.resolveDeployHostForRoot(config, rootDomain, {})))),
24
+ ];
25
+ };
26
+ const zoneExists = (client, zoneName) => __awaiter(void 0, void 0, void 0, function* () {
27
+ const { HostedZones } = yield client.send(new client_route_53_1.ListHostedZonesByNameCommand({ DNSName: zoneName, MaxItems: 1 }));
28
+ return (HostedZones !== null && HostedZones !== void 0 ? HostedZones : []).some((zone) => { var _a; return zone.Name === `${zoneName}.` && !((_a = zone.Config) === null || _a === void 0 ? void 0 : _a.PrivateZone); });
29
+ });
30
+ const checkHostedZones = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
31
+ const required = getRequiredZones(ctx);
32
+ if (required.length === 0) {
33
+ return { done: true, detail: 'no domains declared' };
34
+ }
35
+ const client = new client_route_53_1.Route53Client({ region: ctx.region });
36
+ const missing = [];
37
+ for (const zoneName of required) {
38
+ if (!(yield zoneExists(client, zoneName))) {
39
+ missing.push(zoneName);
40
+ }
41
+ }
42
+ return missing.length === 0
43
+ ? { done: true, detail: required.join(', ') }
44
+ : {
45
+ done: false,
46
+ detail: `missing ${missing.join(', ')}: create each as a public hosted zone here and delegate it (NS record) from its parent zone`,
47
+ };
48
+ });
49
+ /** Check-only: the Route53 zones the deploy will write records into exist in this account. */
50
+ const buildHostedZonesStep = (ctx) => ({
51
+ id: 'hosted-zones',
52
+ name: 'Route53 hosted zones',
53
+ check: () => checkHostedZones(ctx),
54
+ run: () => __awaiter(void 0, void 0, void 0, function* () {
55
+ var _a;
56
+ const result = yield checkHostedZones(ctx);
57
+ if (!result.done) {
58
+ throw new Error((_a = result.detail) !== null && _a !== void 0 ? _a : 'hosted zones missing');
59
+ }
60
+ }),
61
+ });
62
+ exports.buildHostedZonesStep = buildHostedZonesStep;
63
+ //# sourceMappingURL=buildHostedZonesStep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildHostedZonesStep.js","sourceRoot":"","sources":["../../../../../src/platforms/aws/setup/buildHostedZonesStep.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,uEAAgE;AAChE,+DAAyD;AAEzD,8DAAuF;AAKvF,kFAAkF;AAClF,kFAAkF;AAClF,MAAM,gBAAgB,GAAG,CAAC,GAAoB,EAAY,EAAE;IAC1D,MAAM,MAAM,GAAG,GAAG,CAAC,kBAAkB,CAAC;IACtC,OAAO;QACL,GAAG,IAAI,GAAG,CACR,wCAAiB;aACd,cAAc,CAAC,MAAM,CAAC;aACtB,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAClB,+CAAoB,CAAC,wBAAwB,CAAC,MAAM,EAAE,+CAAoB,CAAC,wBAAwB,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC,CAC7H,CACJ;KACF,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,CAAO,MAAqB,EAAE,QAAgB,EAAoB,EAAE;IACrF,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,8CAA4B,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAChH,OAAO,CAAC,WAAW,aAAX,WAAW,cAAX,WAAW,GAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,WAAC,OAAA,IAAI,CAAC,IAAI,KAAK,GAAG,QAAQ,GAAG,IAAI,CAAC,CAAA,MAAA,IAAI,CAAC,MAAM,0CAAE,WAAW,CAAA,CAAA,EAAA,CAAC,CAAC;AACvG,CAAC,CAAA,CAAC;AAEF,MAAM,gBAAgB,GAAG,CAAO,GAAoB,EAA6B,EAAE;IACjF,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACvC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC;IACvD,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,+BAAa,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IACzD,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,QAAQ,IAAI,QAAQ,EAAE,CAAC;QAChC,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC;YAC1C,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC,MAAM,KAAK,CAAC;QACzB,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QAC7C,CAAC,CAAC;YACE,IAAI,EAAE,KAAK;YACX,MAAM,EAAE,WAAW,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,6FAA6F;SACnI,CAAC;AACR,CAAC,CAAA,CAAC;AAEF,8FAA8F;AACvF,MAAM,oBAAoB,GAAG,CAAC,GAAoB,EAAa,EAAE,CAAC,CAAC;IACxE,EAAE,EAAE,cAAc;IAClB,IAAI,EAAE,sBAAsB;IAC5B,KAAK,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC;IAClC,GAAG,EAAE,GAAS,EAAE;;QACd,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,MAAA,MAAM,CAAC,MAAM,mCAAI,sBAAsB,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC,CAAA;CACF,CAAC,CAAC;AAVU,QAAA,oBAAoB,wBAU9B","sourcesContent":["import { qpqDeployAwsCdkUtils } from 'quidproquo-deploy-awscdk';\nimport { qpqWebServerUtils } from 'quidproquo-webserver';\n\nimport { ListHostedZonesByNameCommand, Route53Client } from '@aws-sdk/client-route-53';\n\nimport { SetupCheckResult, SetupStep } from '../../../lib/setupStep';\nimport { AwsSetupContext } from './awsSetupContext';\n\n// The zones the environment writes records into: one per root, chosen the way the\n// CDK chooses them (each root's site root under the app's resolver, or the root).\nconst getRequiredZones = (ctx: AwsSetupContext): string[] => {\n const config = ctx.bootstrapQpqConfig;\n return [\n ...new Set(\n qpqWebServerUtils\n .getRootDomains(config)\n .map((rootDomain) =>\n qpqDeployAwsCdkUtils.resolveHostedZoneForHost(config, qpqDeployAwsCdkUtils.resolveDeployHostForRoot(config, rootDomain, {})),\n ),\n ),\n ];\n};\n\nconst zoneExists = async (client: Route53Client, zoneName: string): Promise<boolean> => {\n const { HostedZones } = await client.send(new ListHostedZonesByNameCommand({ DNSName: zoneName, MaxItems: 1 }));\n return (HostedZones ?? []).some((zone) => zone.Name === `${zoneName}.` && !zone.Config?.PrivateZone);\n};\n\nconst checkHostedZones = async (ctx: AwsSetupContext): Promise<SetupCheckResult> => {\n const required = getRequiredZones(ctx);\n if (required.length === 0) {\n return { done: true, detail: 'no domains declared' };\n }\n\n const client = new Route53Client({ region: ctx.region });\n const missing: string[] = [];\n for (const zoneName of required) {\n if (!(await zoneExists(client, zoneName))) {\n missing.push(zoneName);\n }\n }\n\n return missing.length === 0\n ? { done: true, detail: required.join(', ') }\n : {\n done: false,\n detail: `missing ${missing.join(', ')}: create each as a public hosted zone here and delegate it (NS record) from its parent zone`,\n };\n};\n\n/** Check-only: the Route53 zones the deploy will write records into exist in this account. */\nexport const buildHostedZonesStep = (ctx: AwsSetupContext): SetupStep => ({\n id: 'hosted-zones',\n name: 'Route53 hosted zones',\n check: () => checkHostedZones(ctx),\n run: async () => {\n const result = await checkHostedZones(ctx);\n if (!result.done) {\n throw new Error(result.detail ?? 'hosted zones missing');\n }\n },\n});\n"]}
@@ -0,0 +1,4 @@
1
+ import { SetupStep } from '../../../lib/setupStep';
2
+ import { AwsSetupContext } from './awsSetupContext';
3
+ /** Check-only: the credentials can manage IAM, which every CDK deploy needs. */
4
+ export declare const buildIamCapabilityStep: (ctx: AwsSetupContext) => SetupStep;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.buildIamCapabilityStep = void 0;
13
+ const client_iam_1 = require("@aws-sdk/client-iam");
14
+ // CDK bootstrap and every deploy create IAM roles. PowerUserAccess fails on the
15
+ // first iam:GetRole for a cdk-* role, so probe exactly that: a NoSuchEntity
16
+ // answer means the permission is there, AccessDenied means it is not.
17
+ const checkIamCapability = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
18
+ const roleName = `cdk-hnb659fds-cfn-exec-role-${ctx.accountId}-${ctx.region}`;
19
+ try {
20
+ yield new client_iam_1.IAMClient({ region: ctx.region }).send(new client_iam_1.GetRoleCommand({ RoleName: roleName }));
21
+ return { done: true, detail: 'iam:GetRole allowed' };
22
+ }
23
+ catch (error) {
24
+ const name = error instanceof Error ? error.name : '';
25
+ if (name === 'NoSuchEntityException') {
26
+ return { done: true, detail: 'iam:GetRole allowed' };
27
+ }
28
+ return {
29
+ done: false,
30
+ detail: `iam:GetRole denied (${name}); the permission set needs IAM rights (AdministratorAccess or a custom set adding iam:*)`,
31
+ };
32
+ }
33
+ });
34
+ /** Check-only: the credentials can manage IAM, which every CDK deploy needs. */
35
+ const buildIamCapabilityStep = (ctx) => ({
36
+ id: 'iam',
37
+ name: 'IAM rights on the credentials',
38
+ check: () => checkIamCapability(ctx),
39
+ run: () => __awaiter(void 0, void 0, void 0, function* () {
40
+ var _a;
41
+ const result = yield checkIamCapability(ctx);
42
+ if (!result.done) {
43
+ throw new Error((_a = result.detail) !== null && _a !== void 0 ? _a : 'IAM rights missing');
44
+ }
45
+ }),
46
+ });
47
+ exports.buildIamCapabilityStep = buildIamCapabilityStep;
48
+ //# sourceMappingURL=buildIamCapabilityStep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildIamCapabilityStep.js","sourceRoot":"","sources":["../../../../../src/platforms/aws/setup/buildIamCapabilityStep.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,oDAAgE;AAKhE,gFAAgF;AAChF,4EAA4E;AAC5E,sEAAsE;AACtE,MAAM,kBAAkB,GAAG,CAAO,GAAoB,EAA6B,EAAE;IACnF,MAAM,QAAQ,GAAG,+BAA+B,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;IAC9E,IAAI,CAAC;QACH,MAAM,IAAI,sBAAS,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,2BAAc,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;QAC7F,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC;IACvD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACtD,IAAI,IAAI,KAAK,uBAAuB,EAAE,CAAC;YACrC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC;QACvD,CAAC;QACD,OAAO;YACL,IAAI,EAAE,KAAK;YACX,MAAM,EAAE,uBAAuB,IAAI,2FAA2F;SAC/H,CAAC;IACJ,CAAC;AACH,CAAC,CAAA,CAAC;AAEF,gFAAgF;AACzE,MAAM,sBAAsB,GAAG,CAAC,GAAoB,EAAa,EAAE,CAAC,CAAC;IAC1E,EAAE,EAAE,KAAK;IACT,IAAI,EAAE,+BAA+B;IACrC,KAAK,EAAE,GAAG,EAAE,CAAC,kBAAkB,CAAC,GAAG,CAAC;IACpC,GAAG,EAAE,GAAS,EAAE;;QACd,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,GAAG,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,MAAA,MAAM,CAAC,MAAM,mCAAI,oBAAoB,CAAC,CAAC;QACzD,CAAC;IACH,CAAC,CAAA;CACF,CAAC,CAAC;AAVU,QAAA,sBAAsB,0BAUhC","sourcesContent":["import { GetRoleCommand, IAMClient } from '@aws-sdk/client-iam';\n\nimport { SetupCheckResult, SetupStep } from '../../../lib/setupStep';\nimport { AwsSetupContext } from './awsSetupContext';\n\n// CDK bootstrap and every deploy create IAM roles. PowerUserAccess fails on the\n// first iam:GetRole for a cdk-* role, so probe exactly that: a NoSuchEntity\n// answer means the permission is there, AccessDenied means it is not.\nconst checkIamCapability = async (ctx: AwsSetupContext): Promise<SetupCheckResult> => {\n const roleName = `cdk-hnb659fds-cfn-exec-role-${ctx.accountId}-${ctx.region}`;\n try {\n await new IAMClient({ region: ctx.region }).send(new GetRoleCommand({ RoleName: roleName }));\n return { done: true, detail: 'iam:GetRole allowed' };\n } catch (error) {\n const name = error instanceof Error ? error.name : '';\n if (name === 'NoSuchEntityException') {\n return { done: true, detail: 'iam:GetRole allowed' };\n }\n return {\n done: false,\n detail: `iam:GetRole denied (${name}); the permission set needs IAM rights (AdministratorAccess or a custom set adding iam:*)`,\n };\n }\n};\n\n/** Check-only: the credentials can manage IAM, which every CDK deploy needs. */\nexport const buildIamCapabilityStep = (ctx: AwsSetupContext): SetupStep => ({\n id: 'iam',\n name: 'IAM rights on the credentials',\n check: () => checkIamCapability(ctx),\n run: async () => {\n const result = await checkIamCapability(ctx);\n if (!result.done) {\n throw new Error(result.detail ?? 'IAM rights missing');\n }\n },\n});\n"]}
@@ -0,0 +1,4 @@
1
+ import { SetupStep } from '../../../lib/setupStep';
2
+ import { AwsSetupContext } from './awsSetupContext';
3
+ /** Check-only: the active credentials belong to the environment's account. Catches a stale .env or SSO profile. */
4
+ export declare const buildIdentityStep: (ctx: AwsSetupContext) => SetupStep;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.buildIdentityStep = void 0;
13
+ const client_sts_1 = require("@aws-sdk/client-sts");
14
+ const checkIdentity = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
15
+ try {
16
+ const { Account, Arn } = yield new client_sts_1.STSClient({ region: ctx.region }).send(new client_sts_1.GetCallerIdentityCommand({}));
17
+ if (Account !== ctx.accountId) {
18
+ return { done: false, detail: `credentials are for account ${Account}, environment '${ctx.environment}' deploys to ${ctx.accountId}` };
19
+ }
20
+ return { done: true, detail: `${Arn}` };
21
+ }
22
+ catch (error) {
23
+ return { done: false, detail: `no valid credentials: ${error instanceof Error ? error.message : String(error)}` };
24
+ }
25
+ });
26
+ /** Check-only: the active credentials belong to the environment's account. Catches a stale .env or SSO profile. */
27
+ const buildIdentityStep = (ctx) => ({
28
+ id: 'identity',
29
+ name: `Credentials for account ${ctx.accountId}`,
30
+ check: () => checkIdentity(ctx),
31
+ run: () => __awaiter(void 0, void 0, void 0, function* () {
32
+ const result = yield checkIdentity(ctx);
33
+ if (!result.done) {
34
+ throw new Error(`${result.detail}. Log in to the right account (or fix AWS_DEFAULT_ACCOUNT / deploy.config.json) and rerun.`);
35
+ }
36
+ }),
37
+ });
38
+ exports.buildIdentityStep = buildIdentityStep;
39
+ //# sourceMappingURL=buildIdentityStep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildIdentityStep.js","sourceRoot":"","sources":["../../../../../src/platforms/aws/setup/buildIdentityStep.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,oDAA0E;AAK1E,MAAM,aAAa,GAAG,CAAO,GAAoB,EAA6B,EAAE;IAC9E,IAAI,CAAC;QACH,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,IAAI,sBAAS,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,qCAAwB,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5G,IAAI,OAAO,KAAK,GAAG,CAAC,SAAS,EAAE,CAAC;YAC9B,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,+BAA+B,OAAO,kBAAkB,GAAG,CAAC,WAAW,gBAAgB,GAAG,CAAC,SAAS,EAAE,EAAE,CAAC;QACzI,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,EAAE,EAAE,CAAC;IAC1C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,yBAAyB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;IACpH,CAAC;AACH,CAAC,CAAA,CAAC;AAEF,mHAAmH;AAC5G,MAAM,iBAAiB,GAAG,CAAC,GAAoB,EAAa,EAAE,CAAC,CAAC;IACrE,EAAE,EAAE,UAAU;IACd,IAAI,EAAE,2BAA2B,GAAG,CAAC,SAAS,EAAE;IAChD,KAAK,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC;IAC/B,GAAG,EAAE,GAAS,EAAE;QACd,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,CAAC,MAAM,4FAA4F,CAAC,CAAC;QAChI,CAAC;IACH,CAAC,CAAA;CACF,CAAC,CAAC;AAVU,QAAA,iBAAiB,qBAU3B","sourcesContent":["import { GetCallerIdentityCommand, STSClient } from '@aws-sdk/client-sts';\n\nimport { SetupCheckResult, SetupStep } from '../../../lib/setupStep';\nimport { AwsSetupContext } from './awsSetupContext';\n\nconst checkIdentity = async (ctx: AwsSetupContext): Promise<SetupCheckResult> => {\n try {\n const { Account, Arn } = await new STSClient({ region: ctx.region }).send(new GetCallerIdentityCommand({}));\n if (Account !== ctx.accountId) {\n return { done: false, detail: `credentials are for account ${Account}, environment '${ctx.environment}' deploys to ${ctx.accountId}` };\n }\n return { done: true, detail: `${Arn}` };\n } catch (error) {\n return { done: false, detail: `no valid credentials: ${error instanceof Error ? error.message : String(error)}` };\n }\n};\n\n/** Check-only: the active credentials belong to the environment's account. Catches a stale .env or SSO profile. */\nexport const buildIdentityStep = (ctx: AwsSetupContext): SetupStep => ({\n id: 'identity',\n name: `Credentials for account ${ctx.accountId}`,\n check: () => checkIdentity(ctx),\n run: async () => {\n const result = await checkIdentity(ctx);\n if (!result.done) {\n throw new Error(`${result.detail}. Log in to the right account (or fix AWS_DEFAULT_ACCOUNT / deploy.config.json) and rerun.`);\n }\n },\n});\n"]}
@@ -0,0 +1,5 @@
1
+ import { SetupStep } from '../../../lib/setupStep';
2
+ import { AwsSetupContext } from './awsSetupContext';
3
+ export declare const buildAccountStackStep: (ctx: AwsSetupContext) => SetupStep;
4
+ export declare const buildDomainStackStep: (ctx: AwsSetupContext) => SetupStep;
5
+ export declare const buildBootstrapStackStep: (ctx: AwsSetupContext) => SetupStep;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.buildBootstrapStackStep = exports.buildDomainStackStep = exports.buildAccountStackStep = void 0;
13
+ const quidproquo_deploy_awscdk_1 = require("quidproquo-deploy-awscdk");
14
+ const stacks_1 = require("../stacks");
15
+ const describeStackStatus_1 = require("./describeStackStatus");
16
+ const buildStackStep = (id, name, stackName, region, run) => ({
17
+ id,
18
+ name: `${name} (${stackName})`,
19
+ check: () => __awaiter(void 0, void 0, void 0, function* () {
20
+ const status = yield (0, describeStackStatus_1.describeStackStatus)(stackName, region);
21
+ return status === null ? { done: false, detail: 'not deployed' } : { done: (0, describeStackStatus_1.isStackHealthy)(status), detail: status };
22
+ }),
23
+ run,
24
+ });
25
+ const buildAccountStackStep = (ctx) => buildStackStep('account-stack', 'Account guardrails stack', quidproquo_deploy_awscdk_1.qpqDeployAwsCdkUtils.getAccountStackName(), ctx.region, () => (0, stacks_1.deployAccountStack)(ctx.appName));
26
+ exports.buildAccountStackStep = buildAccountStackStep;
27
+ // The domain stack deploys its cert stacks as dependencies, so one step covers certificates too.
28
+ const buildDomainStackStep = (ctx) => buildStackStep('domain-stack', 'Domain stack: certificates + api domains', quidproquo_deploy_awscdk_1.qpqDeployAwsCdkUtils.getDomainStackName(ctx.bootstrapQpqConfig), ctx.region, () => (0, stacks_1.deployDomainStack)(ctx.appName));
29
+ exports.buildDomainStackStep = buildDomainStackStep;
30
+ const buildBootstrapStackStep = (ctx) => buildStackStep('bootstrap-stack', 'Bootstrap stack: WAF and app-wide resources', quidproquo_deploy_awscdk_1.qpqDeployAwsCdkUtils.getBootstrapStackName(ctx.bootstrapQpqConfig), ctx.region, () => (0, stacks_1.deployBootstrapStack)(ctx.appName));
31
+ exports.buildBootstrapStackStep = buildBootstrapStackStep;
32
+ //# sourceMappingURL=buildStackSteps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildStackSteps.js","sourceRoot":"","sources":["../../../../../src/platforms/aws/setup/buildStackSteps.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,uEAAgE;AAGhE,sCAAwF;AAExF,+DAA4E;AAE5E,MAAM,cAAc,GAAG,CAAC,EAAU,EAAE,IAAY,EAAE,SAAiB,EAAE,MAAc,EAAE,GAAwB,EAAa,EAAE,CAAC,CAAC;IAC5H,EAAE;IACF,IAAI,EAAE,GAAG,IAAI,KAAK,SAAS,GAAG;IAC9B,KAAK,EAAE,GAAS,EAAE;QAChB,MAAM,MAAM,GAAG,MAAM,IAAA,yCAAmB,EAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAC5D,OAAO,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAA,oCAAc,EAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IACtH,CAAC,CAAA;IACD,GAAG;CACJ,CAAC,CAAC;AAEI,MAAM,qBAAqB,GAAG,CAAC,GAAoB,EAAa,EAAE,CACvE,cAAc,CAAC,eAAe,EAAE,0BAA0B,EAAE,+CAAoB,CAAC,mBAAmB,EAAE,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CACvH,IAAA,2BAAkB,EAAC,GAAG,CAAC,OAAO,CAAC,CAChC,CAAC;AAHS,QAAA,qBAAqB,yBAG9B;AAEJ,iGAAiG;AAC1F,MAAM,oBAAoB,GAAG,CAAC,GAAoB,EAAa,EAAE,CACtE,cAAc,CACZ,cAAc,EACd,0CAA0C,EAC1C,+CAAoB,CAAC,kBAAkB,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAC/D,GAAG,CAAC,MAAM,EACV,GAAG,EAAE,CAAC,IAAA,0BAAiB,EAAC,GAAG,CAAC,OAAO,CAAC,CACrC,CAAC;AAPS,QAAA,oBAAoB,wBAO7B;AAEG,MAAM,uBAAuB,GAAG,CAAC,GAAoB,EAAa,EAAE,CACzE,cAAc,CACZ,iBAAiB,EACjB,6CAA6C,EAC7C,+CAAoB,CAAC,qBAAqB,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAClE,GAAG,CAAC,MAAM,EACV,GAAG,EAAE,CAAC,IAAA,6BAAoB,EAAC,GAAG,CAAC,OAAO,CAAC,CACxC,CAAC;AAPS,QAAA,uBAAuB,2BAOhC","sourcesContent":["import { qpqDeployAwsCdkUtils } from 'quidproquo-deploy-awscdk';\n\nimport { SetupStep } from '../../../lib/setupStep';\nimport { deployAccountStack, deployBootstrapStack, deployDomainStack } from '../stacks';\nimport { AwsSetupContext } from './awsSetupContext';\nimport { describeStackStatus, isStackHealthy } from './describeStackStatus';\n\nconst buildStackStep = (id: string, name: string, stackName: string, region: string, run: () => Promise<void>): SetupStep => ({\n id,\n name: `${name} (${stackName})`,\n check: async () => {\n const status = await describeStackStatus(stackName, region);\n return status === null ? { done: false, detail: 'not deployed' } : { done: isStackHealthy(status), detail: status };\n },\n run,\n});\n\nexport const buildAccountStackStep = (ctx: AwsSetupContext): SetupStep =>\n buildStackStep('account-stack', 'Account guardrails stack', qpqDeployAwsCdkUtils.getAccountStackName(), ctx.region, () =>\n deployAccountStack(ctx.appName),\n );\n\n// The domain stack deploys its cert stacks as dependencies, so one step covers certificates too.\nexport const buildDomainStackStep = (ctx: AwsSetupContext): SetupStep =>\n buildStackStep(\n 'domain-stack',\n 'Domain stack: certificates + api domains',\n qpqDeployAwsCdkUtils.getDomainStackName(ctx.bootstrapQpqConfig),\n ctx.region,\n () => deployDomainStack(ctx.appName),\n );\n\nexport const buildBootstrapStackStep = (ctx: AwsSetupContext): SetupStep =>\n buildStackStep(\n 'bootstrap-stack',\n 'Bootstrap stack: WAF and app-wide resources',\n qpqDeployAwsCdkUtils.getBootstrapStackName(ctx.bootstrapQpqConfig),\n ctx.region,\n () => deployBootstrapStack(ctx.appName),\n );\n"]}
@@ -0,0 +1,4 @@
1
+ import { Nullable } from 'quidproquo-core';
2
+ /** A stack's status, or null when it does not exist. */
3
+ export declare const describeStackStatus: (stackName: string, region: string) => Promise<Nullable<string>>;
4
+ export declare const isStackHealthy: (status: Nullable<string>) => boolean;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.isStackHealthy = exports.describeStackStatus = void 0;
13
+ const client_cloudformation_1 = require("@aws-sdk/client-cloudformation");
14
+ /** A stack's status, or null when it does not exist. */
15
+ const describeStackStatus = (stackName, region) => __awaiter(void 0, void 0, void 0, function* () {
16
+ var _a, _b;
17
+ try {
18
+ const { Stacks } = yield new client_cloudformation_1.CloudFormationClient({ region }).send(new client_cloudformation_1.DescribeStacksCommand({ StackName: stackName }));
19
+ return (_b = (_a = Stacks === null || Stacks === void 0 ? void 0 : Stacks[0]) === null || _a === void 0 ? void 0 : _a.StackStatus) !== null && _b !== void 0 ? _b : null;
20
+ }
21
+ catch (error) {
22
+ if (error instanceof Error && /does not exist/.test(error.message)) {
23
+ return null;
24
+ }
25
+ throw error;
26
+ }
27
+ });
28
+ exports.describeStackStatus = describeStackStatus;
29
+ const healthyStatuses = ['CREATE_COMPLETE', 'UPDATE_COMPLETE', 'IMPORT_COMPLETE'];
30
+ const isStackHealthy = (status) => status !== null && healthyStatuses.includes(status);
31
+ exports.isStackHealthy = isStackHealthy;
32
+ //# sourceMappingURL=describeStackStatus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"describeStackStatus.js","sourceRoot":"","sources":["../../../../../src/platforms/aws/setup/describeStackStatus.ts"],"names":[],"mappings":";;;;;;;;;;;;AAEA,0EAA6F;AAE7F,wDAAwD;AACjD,MAAM,mBAAmB,GAAG,CAAO,SAAiB,EAAE,MAAc,EAA6B,EAAE;;IACxG,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,4CAAoB,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,6CAAqB,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;QACxH,OAAO,MAAA,MAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAG,CAAC,CAAC,0CAAE,WAAW,mCAAI,IAAI,CAAC;IAC1C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,KAAK,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YACnE,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC,CAAA,CAAC;AAVW,QAAA,mBAAmB,uBAU9B;AAEF,MAAM,eAAe,GAAG,CAAC,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB,CAAC,CAAC;AAE3E,MAAM,cAAc,GAAG,CAAC,MAAwB,EAAW,EAAE,CAAC,MAAM,KAAK,IAAI,IAAI,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAA5G,QAAA,cAAc,kBAA8F","sourcesContent":["import { Nullable } from 'quidproquo-core';\n\nimport { CloudFormationClient, DescribeStacksCommand } from '@aws-sdk/client-cloudformation';\n\n/** A stack's status, or null when it does not exist. */\nexport const describeStackStatus = async (stackName: string, region: string): Promise<Nullable<string>> => {\n try {\n const { Stacks } = await new CloudFormationClient({ region }).send(new DescribeStacksCommand({ StackName: stackName }));\n return Stacks?.[0]?.StackStatus ?? null;\n } catch (error) {\n if (error instanceof Error && /does not exist/.test(error.message)) {\n return null;\n }\n throw error;\n }\n};\n\nconst healthyStatuses = ['CREATE_COMPLETE', 'UPDATE_COMPLETE', 'IMPORT_COMPLETE'];\n\nexport const isStackHealthy = (status: Nullable<string>): boolean => status !== null && healthyStatuses.includes(status);\n"]}
@@ -0,0 +1 @@
1
+ export * from './awsSetupSteps';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./awsSetupSteps"), exports);
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/platforms/aws/setup/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,kDAAgC","sourcesContent":["export * from './awsSetupSteps';\n"]}
@@ -1,6 +1,7 @@
1
1
  import { QpqAppDeployEnvironment } from 'quidproquo-deploy-awscdk';
2
2
  import { ClearResourcesPlan } from '../lib/clearResourcesPlan';
3
3
  import { DeployPlan, TeardownPlan } from '../lib/deployPrompts';
4
+ import { SetupStep } from '../lib/setupStep';
4
5
  export declare enum QpqDeployPlatform {
5
6
  aws = "aws",
6
7
  docker = "docker"
@@ -11,6 +12,7 @@ export interface QpqPlatformDriver {
11
12
  goDocker?: (appName: string, plan: DeployPlan) => Promise<void>;
12
13
  teardown?: (appName: string, plan: TeardownPlan) => Promise<void>;
13
14
  clearResources?: (appName: string, plan: ClearResourcesPlan) => Promise<void>;
15
+ setupSteps?: (appName: string, environment: string) => Promise<SetupStep[]>;
14
16
  publish: (appName: string, serviceNames: string[]) => Promise<void>;
15
17
  publishBuild: (appName: string, serviceNames: string[]) => Promise<void>;
16
18
  publishUpload: (appName: string, serviceNames: string[]) => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/platforms/types.ts"],"names":[],"mappings":";;;AAKA,IAAY,iBAGX;AAHD,WAAY,iBAAiB;IAC3B,gCAAW,CAAA;IACX,sCAAiB,CAAA;AACnB,CAAC,EAHW,iBAAiB,iCAAjB,iBAAiB,QAG5B","sourcesContent":["import { QpqAppDeployEnvironment } from 'quidproquo-deploy-awscdk';\n\nimport { ClearResourcesPlan } from '../lib/clearResourcesPlan';\nimport { DeployPlan, TeardownPlan } from '../lib/deployPrompts';\n\nexport enum QpqDeployPlatform {\n aws = 'aws',\n docker = 'docker',\n}\n\n// A deploy platform driver — everything that happens after \"what to deploy\"\n// has been decided. `qpq go` resolves the app + environment, prompts the plan,\n// then hands off here; the platform comes from the environment's entry in\n// apps/<app>/deploy.config.json (default aws), so an app can move platforms\n// with a config change instead of a different command.\nexport interface QpqPlatformDriver {\n // Prime process.env with this platform's deploy identity from the resolved\n // environment target (already-set env vars win). Service configs read these\n // vars at load time, so this runs before any infrastructure.ts is required.\n // Returns human-readable descriptions of anything still missing — deploys\n // treat that as fatal, local dev commands ignore it.\n primeDeployIdentity: (target: QpqAppDeployEnvironment | undefined) => string[];\n\n // Sequential deploy (`qpq go`).\n go: (appName: string, plan: DeployPlan) => Promise<void>;\n\n // Parallel containerized deploy (`qpq go:docker`) — optional; drivers that\n // have no docker strategy simply omit it.\n goDocker?: (appName: string, plan: DeployPlan) => Promise<void>;\n\n // Destroy the web/api/inf stacks for selected services (`qpq teardown`) —\n // optional; drivers with no teardown strategy simply omit it.\n teardown?: (appName: string, plan: TeardownPlan) => Promise<void>;\n\n // Empty the stored data of selected resources without touching the stacks\n // (`qpq clear-resources`) — optional; drivers with no strategy omit it.\n clearResources?: (appName: string, plan: ClearResourcesPlan) => Promise<void>;\n\n // Federated remote publishing (`qpq publish[:build|:upload|:deploy]`).\n publish: (appName: string, serviceNames: string[]) => Promise<void>;\n publishBuild: (appName: string, serviceNames: string[]) => Promise<void>;\n publishUpload: (appName: string, serviceNames: string[]) => Promise<void>;\n publishDeploy: (appName: string, serviceNames: string[]) => Promise<void>;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/platforms/types.ts"],"names":[],"mappings":";;;AAMA,IAAY,iBAGX;AAHD,WAAY,iBAAiB;IAC3B,gCAAW,CAAA;IACX,sCAAiB,CAAA;AACnB,CAAC,EAHW,iBAAiB,iCAAjB,iBAAiB,QAG5B","sourcesContent":["import { QpqAppDeployEnvironment } from 'quidproquo-deploy-awscdk';\n\nimport { ClearResourcesPlan } from '../lib/clearResourcesPlan';\nimport { DeployPlan, TeardownPlan } from '../lib/deployPrompts';\nimport { SetupStep } from '../lib/setupStep';\n\nexport enum QpqDeployPlatform {\n aws = 'aws',\n docker = 'docker',\n}\n\n// A deploy platform driver — everything that happens after \"what to deploy\"\n// has been decided. `qpq go` resolves the app + environment, prompts the plan,\n// then hands off here; the platform comes from the environment's entry in\n// apps/<app>/deploy.config.json (default aws), so an app can move platforms\n// with a config change instead of a different command.\nexport interface QpqPlatformDriver {\n // Prime process.env with this platform's deploy identity from the resolved\n // environment target (already-set env vars win). Service configs read these\n // vars at load time, so this runs before any infrastructure.ts is required.\n // Returns human-readable descriptions of anything still missing — deploys\n // treat that as fatal, local dev commands ignore it.\n primeDeployIdentity: (target: QpqAppDeployEnvironment | undefined) => string[];\n\n // Sequential deploy (`qpq go`).\n go: (appName: string, plan: DeployPlan) => Promise<void>;\n\n // Parallel containerized deploy (`qpq go:docker`) — optional; drivers that\n // have no docker strategy simply omit it.\n goDocker?: (appName: string, plan: DeployPlan) => Promise<void>;\n\n // Destroy the web/api/inf stacks for selected services (`qpq teardown`) —\n // optional; drivers with no teardown strategy simply omit it.\n teardown?: (appName: string, plan: TeardownPlan) => Promise<void>;\n\n // Empty the stored data of selected resources without touching the stacks\n // (`qpq clear-resources`) — optional; drivers with no strategy omit it.\n clearResources?: (appName: string, plan: ClearResourcesPlan) => Promise<void>;\n\n // The ordered checklist that takes an empty cloud environment to a deployed\n // one (`qpq setup`) — optional; drivers with nothing to set up omit it.\n setupSteps?: (appName: string, environment: string) => Promise<SetupStep[]>;\n\n // Federated remote publishing (`qpq publish[:build|:upload|:deploy]`).\n publish: (appName: string, serviceNames: string[]) => Promise<void>;\n publishBuild: (appName: string, serviceNames: string[]) => Promise<void>;\n publishUpload: (appName: string, serviceNames: string[]) => Promise<void>;\n publishDeploy: (appName: string, serviceNames: string[]) => Promise<void>;\n}\n"]}
@@ -23,6 +23,12 @@ export const cliCommands = [
23
23
  ],
24
24
  run: (a) => require('../commands/go').goCommand(a),
25
25
  },
26
+ {
27
+ name: 'setup',
28
+ summary: 'Take a cloud environment from empty to deployed: checked steps, run in order (interactive)',
29
+ usageExtra: ["--check prints each step's status; --yes runs them all; --only <ids> runs a subset."],
30
+ run: (a) => require('../commands/setup').setupCommand(a),
31
+ },
26
32
  {
27
33
  name: 'go:docker',
28
34
  summary: 'Same as go (incl. positional args), but deploys in parallel via docker',
@@ -1 +1 @@
1
- {"version":3,"file":"commandRegistry.js","sourceRoot":"","sources":["../../../src/cli/commandRegistry.ts"],"names":[],"mappings":"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","sourcesContent":["// Single source of truth for every qpq command: its name, the one-line\n// summary (shared by `qpq help` and the interactive menu) and a runner.\n//\n// Command modules are required lazily inside each runner so a simple\n// `qpq help` doesn't pay for the whole toolchain, and so ts-node can be\n// registered before any command module (or anything it requires) loads.\nimport { promptText } from '../lib/prompts';\n\nexport type CliCommand = {\n name: string;\n // Argument hint shown in the usage text, e.g. '[svc] [stack]'.\n usageArgs?: string;\n summary: string;\n // Extra usage-only detail lines, indented under the summary.\n usageExtra?: string[];\n // Menu-only: commands that need a positional argument (which the menu has\n // no argv to carry) gather it here before running.\n promptArgs?: () => Promise<string[]>;\n run: (commandArgv: string[]) => Promise<void>;\n};\n\n// `qpq hooks` needs a hook name; when launched from the menu there is no\n// argv to pass it in, so ask.\nconst promptHookName = async (): Promise<string[]> => [\n await promptText('Hook name (runs qpq:<name>, or plain <name>, in every app/package that defines it):'),\n];\n\n/* eslint-disable @typescript-eslint/no-require-imports */\nexport const cliCommands: CliCommand[] = [\n {\n name: 'go',\n usageArgs: '[svc] [stack]',\n summary: 'Deploy services (interactive; platform from deploy.config.json, default aws).',\n usageExtra: [\n 'Pass positional args to skip prompts, e.g. qpq go all all — svc is',\n \"'all' | comma-list | account | domain | bootstrap; stack is all|inf|api|web|views.\",\n \"A full 'all all' (or 'all api') deploy also publishes federated backends.\",\n ],\n run: (a) => require('../commands/go').goCommand(a),\n },\n {\n name: 'go:docker',\n summary: 'Same as go (incl. positional args), but deploys in parallel via docker',\n run: (a) => require('../commands/goDocker').goDockerCommand(a),\n },\n {\n name: 'go:dev',\n summary: 'Run the full local dev stack (api + web) in one process',\n run: (a) => require('../commands/goDev').goDevCommand(a),\n },\n {\n name: 'go:dev:api',\n summary: 'Run the local API dev server (hot reload)',\n run: (a) => require('../commands/goDevApi').goDevApiCommand(a),\n },\n {\n name: 'go:dev:web',\n summary: 'Run every views microfrontend dev server',\n run: (a) => require('../commands/goDevWeb').goDevWebCommand(a),\n },\n {\n name: 'migrate',\n usageArgs: '[app]',\n summary: 'Run pending migrations against the LOCAL dev store, once, then exit.',\n usageExtra: [\n 'Deployed, migrations run on a stack update; nothing local ever deploys, so',\n 'this is how you exercise one before shipping it. Records what it runs, so a',\n 'second call is a no-op.',\n ],\n run: (a) => require('../commands/migrate').migrateCommand(a),\n },\n {\n name: 'teardown',\n summary: 'Destroy web/api/inf stacks for selected services (interactive)',\n run: (a) => require('../commands/teardown').teardownCommand(a),\n },\n {\n name: 'clear-resources',\n summary: 'Empty selected buckets/tables (data only, stacks untouched; interactive)',\n run: (a) => require('../commands/clearResources').clearResourcesCommand(a),\n },\n {\n name: 'synth',\n usageArgs: '[service]',\n summary: 'Synth QPQ configs to dist/apps/<app>/infrastructure',\n run: (a) => require('../commands/synth').synthCommand(a),\n },\n {\n name: 'prep',\n summary: 'Regenerate apps/<app>/tsconfig.federated.json',\n run: (a) => require('../commands/prep').prepCommand(a),\n },\n {\n name: 'publish',\n summary: 'Build + upload + deploy federated remotes',\n run: (a) => require('../commands/publish').publishCommand(a),\n },\n {\n name: 'publish:build',\n summary: 'Build federated remotes locally',\n run: (a) => require('../commands/publish').publishBuildCommand(a),\n },\n {\n name: 'publish:upload',\n summary: 'Upload built version dirs (nothing goes live)',\n run: (a) => require('../commands/publish').publishUploadCommand(a),\n },\n {\n name: 'publish:deploy',\n summary: 'Flip manifests to the uploaded versions',\n run: (a) => require('../commands/publish').publishDeployCommand(a),\n },\n {\n name: 'check:circular',\n summary: 'Scan workspace sources for circular imports (fails on cycles; --warn to only report)',\n run: (a) => require('../commands/checkCircular').checkCircularCommand(a),\n },\n {\n name: 'hooks',\n usageArgs: '<name>',\n summary: 'Run qpq:<name> (or plain <name>) in every app/package that defines it (dependency ordered, parallel; --jobs=N)',\n promptArgs: promptHookName,\n run: (a) => require('../commands/hooks').hooksCommand(a),\n },\n];\n/* eslint-enable @typescript-eslint/no-require-imports */\n\n// Width of the \"name [args]\" column in the usage text.\nconst usageColumnWidth = 17;\n\nexport const buildUsage = (): string => {\n const commandLines = cliCommands.flatMap((command) => {\n const nameAndArgs = command.usageArgs ? `${command.name} ${command.usageArgs}` : command.name;\n const extraLines = (command.usageExtra ?? []).map((line) => ` ${''.padEnd(usageColumnWidth)}${line}`);\n return [` ${nameAndArgs.padEnd(usageColumnWidth)}${command.summary}`, ...extraLines];\n });\n\n return [\n 'qpq — build, dev and deploy orchestration for QPQ apps',\n '',\n 'Usage: qpq <command> [--app <name>] [--env <name>] [--platform <name>]',\n ' qpq (no command opens an interactive menu)',\n '',\n 'Commands:',\n ...commandLines,\n '',\n ].join('\\n');\n};\n"]}
1
+ {"version":3,"file":"commandRegistry.js","sourceRoot":"","sources":["../../../src/cli/commandRegistry.ts"],"names":[],"mappings":"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","sourcesContent":["// Single source of truth for every qpq command: its name, the one-line\n// summary (shared by `qpq help` and the interactive menu) and a runner.\n//\n// Command modules are required lazily inside each runner so a simple\n// `qpq help` doesn't pay for the whole toolchain, and so ts-node can be\n// registered before any command module (or anything it requires) loads.\nimport { promptText } from '../lib/prompts';\n\nexport type CliCommand = {\n name: string;\n // Argument hint shown in the usage text, e.g. '[svc] [stack]'.\n usageArgs?: string;\n summary: string;\n // Extra usage-only detail lines, indented under the summary.\n usageExtra?: string[];\n // Menu-only: commands that need a positional argument (which the menu has\n // no argv to carry) gather it here before running.\n promptArgs?: () => Promise<string[]>;\n run: (commandArgv: string[]) => Promise<void>;\n};\n\n// `qpq hooks` needs a hook name; when launched from the menu there is no\n// argv to pass it in, so ask.\nconst promptHookName = async (): Promise<string[]> => [\n await promptText('Hook name (runs qpq:<name>, or plain <name>, in every app/package that defines it):'),\n];\n\n/* eslint-disable @typescript-eslint/no-require-imports */\nexport const cliCommands: CliCommand[] = [\n {\n name: 'go',\n usageArgs: '[svc] [stack]',\n summary: 'Deploy services (interactive; platform from deploy.config.json, default aws).',\n usageExtra: [\n 'Pass positional args to skip prompts, e.g. qpq go all all — svc is',\n \"'all' | comma-list | account | domain | bootstrap; stack is all|inf|api|web|views.\",\n \"A full 'all all' (or 'all api') deploy also publishes federated backends.\",\n ],\n run: (a) => require('../commands/go').goCommand(a),\n },\n {\n name: 'setup',\n summary: 'Take a cloud environment from empty to deployed: checked steps, run in order (interactive)',\n usageExtra: [\"--check prints each step's status; --yes runs them all; --only <ids> runs a subset.\"],\n run: (a) => require('../commands/setup').setupCommand(a),\n },\n {\n name: 'go:docker',\n summary: 'Same as go (incl. positional args), but deploys in parallel via docker',\n run: (a) => require('../commands/goDocker').goDockerCommand(a),\n },\n {\n name: 'go:dev',\n summary: 'Run the full local dev stack (api + web) in one process',\n run: (a) => require('../commands/goDev').goDevCommand(a),\n },\n {\n name: 'go:dev:api',\n summary: 'Run the local API dev server (hot reload)',\n run: (a) => require('../commands/goDevApi').goDevApiCommand(a),\n },\n {\n name: 'go:dev:web',\n summary: 'Run every views microfrontend dev server',\n run: (a) => require('../commands/goDevWeb').goDevWebCommand(a),\n },\n {\n name: 'migrate',\n usageArgs: '[app]',\n summary: 'Run pending migrations against the LOCAL dev store, once, then exit.',\n usageExtra: [\n 'Deployed, migrations run on a stack update; nothing local ever deploys, so',\n 'this is how you exercise one before shipping it. Records what it runs, so a',\n 'second call is a no-op.',\n ],\n run: (a) => require('../commands/migrate').migrateCommand(a),\n },\n {\n name: 'teardown',\n summary: 'Destroy web/api/inf stacks for selected services (interactive)',\n run: (a) => require('../commands/teardown').teardownCommand(a),\n },\n {\n name: 'clear-resources',\n summary: 'Empty selected buckets/tables (data only, stacks untouched; interactive)',\n run: (a) => require('../commands/clearResources').clearResourcesCommand(a),\n },\n {\n name: 'synth',\n usageArgs: '[service]',\n summary: 'Synth QPQ configs to dist/apps/<app>/infrastructure',\n run: (a) => require('../commands/synth').synthCommand(a),\n },\n {\n name: 'prep',\n summary: 'Regenerate apps/<app>/tsconfig.federated.json',\n run: (a) => require('../commands/prep').prepCommand(a),\n },\n {\n name: 'publish',\n summary: 'Build + upload + deploy federated remotes',\n run: (a) => require('../commands/publish').publishCommand(a),\n },\n {\n name: 'publish:build',\n summary: 'Build federated remotes locally',\n run: (a) => require('../commands/publish').publishBuildCommand(a),\n },\n {\n name: 'publish:upload',\n summary: 'Upload built version dirs (nothing goes live)',\n run: (a) => require('../commands/publish').publishUploadCommand(a),\n },\n {\n name: 'publish:deploy',\n summary: 'Flip manifests to the uploaded versions',\n run: (a) => require('../commands/publish').publishDeployCommand(a),\n },\n {\n name: 'check:circular',\n summary: 'Scan workspace sources for circular imports (fails on cycles; --warn to only report)',\n run: (a) => require('../commands/checkCircular').checkCircularCommand(a),\n },\n {\n name: 'hooks',\n usageArgs: '<name>',\n summary: 'Run qpq:<name> (or plain <name>) in every app/package that defines it (dependency ordered, parallel; --jobs=N)',\n promptArgs: promptHookName,\n run: (a) => require('../commands/hooks').hooksCommand(a),\n },\n];\n/* eslint-enable @typescript-eslint/no-require-imports */\n\n// Width of the \"name [args]\" column in the usage text.\nconst usageColumnWidth = 17;\n\nexport const buildUsage = (): string => {\n const commandLines = cliCommands.flatMap((command) => {\n const nameAndArgs = command.usageArgs ? `${command.name} ${command.usageArgs}` : command.name;\n const extraLines = (command.usageExtra ?? []).map((line) => ` ${''.padEnd(usageColumnWidth)}${line}`);\n return [` ${nameAndArgs.padEnd(usageColumnWidth)}${command.summary}`, ...extraLines];\n });\n\n return [\n 'qpq — build, dev and deploy orchestration for QPQ apps',\n '',\n 'Usage: qpq <command> [--app <name>] [--env <name>] [--platform <name>]',\n ' qpq (no command opens an interactive menu)',\n '',\n 'Commands:',\n ...commandLines,\n '',\n ].join('\\n');\n};\n"]}