@codedrifters/configulator 0.0.424 → 0.0.426

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.
package/lib/index.js CHANGED
@@ -195,6 +195,7 @@ __export(index_exports, {
195
195
  AwsDeploymentConfig: () => AwsDeploymentConfig,
196
196
  AwsDeploymentTarget: () => AwsDeploymentTarget,
197
197
  AwsTeardownWorkflow: () => AwsTeardownWorkflow,
198
+ BUILD_ARTIFACT_NAME: () => BUILD_ARTIFACT_NAME,
198
199
  BUILT_IN_BUNDLES: () => BUILT_IN_BUNDLES,
199
200
  BUNDLE_OWNERSHIP: () => BUNDLE_OWNERSHIP,
200
201
  CDK_BOOTSTRAP_DEFAULTS_BY_STAGE: () => CDK_BOOTSTRAP_DEFAULTS_BY_STAGE,
@@ -277,6 +278,7 @@ __export(index_exports, {
277
278
  DEFAULT_UNBLOCK_COMMENT_TEMPLATE: () => DEFAULT_UNBLOCK_COMMENT_TEMPLATE,
278
279
  DEFAULT_UNBLOCK_DEPENDENTS_ENABLED: () => DEFAULT_UNBLOCK_DEPENDENTS_ENABLED,
279
280
  DEFAULT_UPSTREAM_CONFIGULATOR_ENABLED: () => DEFAULT_UPSTREAM_CONFIGULATOR_ENABLED,
281
+ DEPLOY_GATE: () => DEPLOY_GATE,
280
282
  DOCS_SYNC_AUDIT_SCHEMA_VERSION: () => DOCS_SYNC_AUDIT_SCHEMA_VERSION,
281
283
  GITHUB_ISSUE_TYPES: () => GITHUB_ISSUE_TYPES,
282
284
  GITHUB_ISSUE_TYPE_BY_TITLE_PREFIX: () => GITHUB_ISSUE_TYPE_BY_TITLE_PREFIX,
@@ -292,7 +294,9 @@ __export(index_exports, {
292
294
  MONOREPO_LAYOUT: () => MONOREPO_LAYOUT,
293
295
  MonorepoProject: () => MonorepoProject,
294
296
  Nvmrc: () => Nvmrc,
297
+ PERMISSION_BACKUP_FILE: () => PERMISSION_BACKUP_FILE,
295
298
  PHASE_LABEL_TYPE_MAP: () => PHASE_LABEL_TYPE_MAP,
299
+ PLAN_RUN_ID_INPUT: () => PLAN_RUN_ID_INPUT,
296
300
  PROD_DEPLOY_NAME: () => PROD_DEPLOY_NAME,
297
301
  PROGRESS_FILES_FORMAT_VALUES: () => PROGRESS_FILES_FORMAT_VALUES,
298
302
  PnpmWorkspace: () => PnpmWorkspace,
@@ -326,6 +330,7 @@ __export(index_exports, {
326
330
  TypeScriptConfig: () => TypeScriptConfig,
327
331
  TypeScriptProject: () => TypeScriptProject,
328
332
  UNKNOWN_TYPE_FALLBACK_TIER: () => UNKNOWN_TYPE_FALLBACK_TIER,
333
+ VALIDATE_PLAN_JOB_ID: () => VALIDATE_PLAN_JOB_ID,
329
334
  VALID_PRIORITY_VALUES: () => VALID_PRIORITY_VALUES,
330
335
  VALID_STATUS_VALUES: () => VALID_STATUS_VALUES,
331
336
  VERSION: () => VERSION,
@@ -470,6 +475,7 @@ __export(index_exports, {
470
475
  renderNextRequirementIdProcedure: () => renderNextRequirementIdProcedure,
471
476
  renderPhaseTypeInvariantSection: () => renderPhaseTypeInvariantSection,
472
477
  renderPhaseTypeInvariantShellHelpers: () => renderPhaseTypeInvariantShellHelpers,
478
+ renderPlanValidationScript: () => renderPlanValidationScript,
473
479
  renderPriorityRulesSection: () => renderPriorityRulesSection,
474
480
  renderProgressFileName: () => renderProgressFileName,
475
481
  renderProgressFilePath: () => renderProgressFilePath,
@@ -507,6 +513,7 @@ __export(index_exports, {
507
513
  resolveBuildPolicy: () => resolveBuildPolicy,
508
514
  resolveBundleAgentTiers: () => resolveBundleAgentTiers,
509
515
  resolveDefaultAgentTier: () => resolveDefaultAgentTier,
516
+ resolveEnvironmentGate: () => resolveEnvironmentGate,
510
517
  resolveIssueDefaults: () => resolveIssueDefaults,
511
518
  resolveIssueTemplates: () => resolveIssueTemplates,
512
519
  resolveModelAlias: () => resolveModelAlias,
@@ -42295,6 +42302,43 @@ var import_build = require("projen/lib/build");
42295
42302
  var import_github6 = require("projen/lib/github");
42296
42303
  var import_workflows_model5 = require("projen/lib/github/workflows-model");
42297
42304
 
42305
+ // src/workflows/deploy-gate.ts
42306
+ var DEPLOY_GATE = {
42307
+ /**
42308
+ * Hold each deploy job behind a GitHub environment so its protection rules —
42309
+ * required reviewers, wait timers, deployment branch policies — apply before
42310
+ * the job is sent to a runner.
42311
+ */
42312
+ ENVIRONMENT: "environment",
42313
+ /**
42314
+ * Split the workflow in two. A **plan** workflow builds and diffs but never
42315
+ * deploys; a dispatch-only **apply** workflow deploys the exact cloud
42316
+ * assembly a nominated plan run produced.
42317
+ *
42318
+ * Needs no protection rule, so unlike {@link DEPLOY_GATE.ENVIRONMENT} it
42319
+ * works on every GitHub plan. `environmentName` is optional here and layers
42320
+ * an environment onto the apply jobs.
42321
+ */
42322
+ PLAN_APPLY: "plan-apply"
42323
+ };
42324
+ var resolveEnvironmentGate = (gate, environmentName, componentName) => {
42325
+ const trimmed = environmentName?.trim();
42326
+ if (gate === void 0) {
42327
+ if (trimmed) {
42328
+ throw new Error(
42329
+ `${componentName} requires \`gate\` to be set when \`environmentName\` is supplied, got "${environmentName}" with no gate`
42330
+ );
42331
+ }
42332
+ return void 0;
42333
+ }
42334
+ if (gate === DEPLOY_GATE.ENVIRONMENT && !trimmed) {
42335
+ throw new Error(
42336
+ `${componentName} requires a non-empty \`environmentName\` when \`gate\` is "${gate}"`
42337
+ );
42338
+ }
42339
+ return trimmed;
42340
+ };
42341
+
42298
42342
  // src/workflows/home-repository.ts
42299
42343
  var HOME_REPOSITORY_PATTERN = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
42300
42344
  var renderHomeRepositoryCondition = (homeRepository, componentName) => {
@@ -42309,6 +42353,105 @@ var renderHomeRepositoryCondition = (homeRepository, componentName) => {
42309
42353
  return `github.repository == '${homeRepository}'`;
42310
42354
  };
42311
42355
 
42356
+ // src/workflows/plan-apply.ts
42357
+ var VALIDATE_PLAN_JOB_ID = "validate-plan";
42358
+ var PLAN_RUN_ID_INPUT = "plan_run_id";
42359
+ var BUILD_ARTIFACT_NAME = "build-artifact";
42360
+ var PERMISSION_BACKUP_FILE = "permissions-backup.acl";
42361
+ var renderPlanValidationScript = (options) => {
42362
+ const {
42363
+ planWorkflowPath,
42364
+ allowedBranches,
42365
+ requireCurrentHead,
42366
+ verifyArtifactDigest
42367
+ } = options;
42368
+ return [
42369
+ "const raw = (process.env.PLAN_RUN_ID ?? '').trim();",
42370
+ "const runId = Number(raw);",
42371
+ "if (!/^[0-9]+$/.test(raw) || !Number.isSafeInteger(runId) || runId <= 0) {",
42372
+ ' core.setFailed(`plan_run_id must be a positive run id, got "${raw}"`);',
42373
+ " return;",
42374
+ "}",
42375
+ "",
42376
+ "const { owner, repo } = context.repo;",
42377
+ "const { data: run } = await github.rest.actions.getWorkflowRun({",
42378
+ " owner,",
42379
+ " repo,",
42380
+ " run_id: runId,",
42381
+ "});",
42382
+ "",
42383
+ `const expectedPath = ${JSON.stringify(planWorkflowPath)};`,
42384
+ "if (run.path !== expectedPath) {",
42385
+ ' core.setFailed(`Run ${runId} belongs to workflow "${run.path}", expected "${expectedPath}".`);',
42386
+ " return;",
42387
+ "}",
42388
+ "",
42389
+ 'if (run.status !== "completed" || run.conclusion !== "success") {',
42390
+ ' core.setFailed(`Run ${runId} is status "${run.status}" / conclusion "${run.conclusion}", expected a completed successful plan run.`);',
42391
+ " return;",
42392
+ "}",
42393
+ "",
42394
+ `const allowedBranches = ${JSON.stringify(allowedBranches)};`,
42395
+ 'const branch = run.head_branch ?? "";',
42396
+ "const branchAllowed = allowedBranches.some((pattern) =>",
42397
+ ' pattern.includes("*")',
42398
+ ' ? branch.startsWith(pattern.slice(0, pattern.indexOf("*")))',
42399
+ " : branch === pattern,",
42400
+ ");",
42401
+ "if (!branchAllowed) {",
42402
+ ' core.setFailed(`Run ${runId} ran on branch "${branch}", which no deployment target in this workflow accepts (allowed: ${allowedBranches.join(", ")}).`);',
42403
+ " return;",
42404
+ "}",
42405
+ ...requireCurrentHead ? [
42406
+ "",
42407
+ "const { data: liveBranch } = await github.rest.repos.getBranch({",
42408
+ " owner,",
42409
+ " repo,",
42410
+ " branch,",
42411
+ "});",
42412
+ "if (liveBranch.commit.sha !== run.head_sha) {",
42413
+ ' core.setFailed(`Run ${runId} planned ${run.head_sha} but "${branch}" now points at ${liveBranch.commit.sha}. Re-run the plan workflow against the current head.`);',
42414
+ " return;",
42415
+ "}"
42416
+ ] : [],
42417
+ "",
42418
+ "const artifacts = await github.paginate(",
42419
+ " github.rest.actions.listWorkflowRunArtifacts,",
42420
+ " { owner, repo, run_id: runId, per_page: 100 },",
42421
+ ");",
42422
+ `const artifactName = ${JSON.stringify(BUILD_ARTIFACT_NAME)};`,
42423
+ "const artifact = artifacts.find((a) => a.name === artifactName);",
42424
+ "if (!artifact) {",
42425
+ ' core.setFailed(`Run ${runId} published no "${artifactName}" artifact. Only a plan run whose build job completed can be applied.`);',
42426
+ " return;",
42427
+ "}",
42428
+ "if (artifact.expired) {",
42429
+ ' core.setFailed(`The "${artifactName}" artifact on run ${runId} expired at ${artifact.expires_at}. Artifact retention is this gate\'s approval window \u2014 re-run the plan workflow.`);',
42430
+ " return;",
42431
+ "}",
42432
+ ...verifyArtifactDigest ? [
42433
+ "if (!artifact.digest) {",
42434
+ ' core.setFailed(`The "${artifactName}" artifact on run ${runId} reports no digest, so it cannot be verified. Disable planApply.verifyArtifactDigest or re-run the plan workflow.`);',
42435
+ " return;",
42436
+ "}",
42437
+ 'core.setOutput("artifact_digest", artifact.digest);'
42438
+ ] : [],
42439
+ "",
42440
+ 'core.setOutput("head_sha", run.head_sha);',
42441
+ 'core.setOutput("head_branch", branch);',
42442
+ "await core.summary",
42443
+ " .addHeading(`Applying plan run ${runId}`, 2)",
42444
+ " .addTable([",
42445
+ ' [{ data: "Field", header: true }, { data: "Value", header: true }],',
42446
+ ' ["Plan run", `<a href="${run.html_url}">${runId}</a>`],',
42447
+ ' ["Branch", branch],',
42448
+ ' ["Commit", run.head_sha],',
42449
+ ' ["Artifact expires", artifact.expires_at ?? "unknown"],',
42450
+ " ])",
42451
+ " .write();"
42452
+ ].join("\n");
42453
+ };
42454
+
42312
42455
  // src/workflows/aws-deploy-workflow.ts
42313
42456
  var PROD_DEPLOY_NAME = "prod-deploy";
42314
42457
  var DIFF_OUTPUT_FILE = "cdk-diff.txt";
@@ -42380,6 +42523,20 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
42380
42523
  target.region
42381
42524
  ].join("-");
42382
42525
  };
42526
+ /**
42527
+ * Build the deterministic GitHub Actions job name for a target's apply job.
42528
+ * Mirrors {@link buildJobName} with an `apply` verb.
42529
+ */
42530
+ this.buildApplyJobName = (target) => {
42531
+ return [
42532
+ target.awsStageType,
42533
+ target.deploymentTargetRole,
42534
+ "apply",
42535
+ target.project.name,
42536
+ target.account,
42537
+ target.region
42538
+ ].join("-");
42539
+ };
42383
42540
  /**
42384
42541
  * Build the CI artifact name for a target's captured diff.
42385
42542
  *
@@ -42398,6 +42555,163 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
42398
42555
  target.region
42399
42556
  ].join("-");
42400
42557
  };
42558
+ /**
42559
+ * Create the dispatch-only apply workflow and its validation job.
42560
+ *
42561
+ * The workflow file name defaults to the plan workflow's name suffixed with
42562
+ * `-apply`. A name already in use throws rather than silently colliding on
42563
+ * the underlying `.github/workflows/<name>.yml` file — the usual cause is two
42564
+ * `AwsDeployWorkflow`s sharing one build workflow, and the fix is an explicit
42565
+ * `planApply.applyWorkflowName`.
42566
+ */
42567
+ this.createApplyWorkflow = (github, homeRepositoryCondition) => {
42568
+ const { applyWorkflowName, requireCurrentHead, verifyArtifactDigest } = this.planApplyOptions;
42569
+ if (github.tryFindWorkflow(applyWorkflowName)) {
42570
+ throw new Error(
42571
+ `AwsDeployWorkflow cannot create the apply workflow "${applyWorkflowName}" because a workflow with that name already exists. Set \`planApply.applyWorkflowName\` to something unique.`
42572
+ );
42573
+ }
42574
+ const workflow = new import_github6.GithubWorkflow(github, applyWorkflowName);
42575
+ workflow.on({
42576
+ workflowDispatch: {
42577
+ inputs: {
42578
+ [PLAN_RUN_ID_INPUT]: {
42579
+ description: "Run id of the plan workflow run to apply. Its build artifact is deployed verbatim.",
42580
+ required: true,
42581
+ type: "string"
42582
+ }
42583
+ }
42584
+ }
42585
+ });
42586
+ const allowedBranches = Array.from(
42587
+ new Set(
42588
+ this.awsDeploymentTargets.flatMap(
42589
+ (target) => target.branches.map((b) => b.branch)
42590
+ )
42591
+ )
42592
+ );
42593
+ workflow.addJob(VALIDATE_PLAN_JOB_ID, {
42594
+ name: "Validate plan run",
42595
+ runsOn: ["ubuntu-latest"],
42596
+ permissions: {
42597
+ actions: import_workflows_model5.JobPermission.READ,
42598
+ contents: import_workflows_model5.JobPermission.READ
42599
+ },
42600
+ ...homeRepositoryCondition ? { if: `\${{ ${homeRepositoryCondition} }}` } : {},
42601
+ outputs: {
42602
+ head_sha: { stepId: "validate_plan_run", outputName: "head_sha" },
42603
+ head_branch: { stepId: "validate_plan_run", outputName: "head_branch" },
42604
+ ...verifyArtifactDigest ? {
42605
+ artifact_digest: {
42606
+ stepId: "validate_plan_run",
42607
+ outputName: "artifact_digest"
42608
+ }
42609
+ } : {}
42610
+ },
42611
+ steps: [
42612
+ {
42613
+ name: "Validate plan run",
42614
+ id: "validate_plan_run",
42615
+ uses: "actions/github-script@v9",
42616
+ /**
42617
+ * The dispatch input reaches the script through the environment
42618
+ * rather than a `${{ }}` interpolation, so a crafted run id cannot
42619
+ * inject JavaScript into the validation itself.
42620
+ */
42621
+ env: {
42622
+ PLAN_RUN_ID: `\${{ inputs.${PLAN_RUN_ID_INPUT} }}`
42623
+ },
42624
+ with: {
42625
+ script: renderPlanValidationScript({
42626
+ planWorkflowPath: `.github/workflows/${this.buildWorkflow.name}.yml`,
42627
+ allowedBranches,
42628
+ requireCurrentHead,
42629
+ verifyArtifactDigest
42630
+ })
42631
+ }
42632
+ }
42633
+ ]
42634
+ });
42635
+ return workflow;
42636
+ };
42637
+ /**
42638
+ * Register one target's apply job on the apply workflow.
42639
+ *
42640
+ * Differences from the `single`-gate deploy job, all of them forced by the
42641
+ * apply run having no build job of its own:
42642
+ *
42643
+ * - `needs` starts at the validation job rather than `build`, so a plan run
42644
+ * that fails any check skips every apply job instead of deploying.
42645
+ * - `actions: read` is added, which is what lets `actions/download-artifact`
42646
+ * reach across runs.
42647
+ * - No `pull-requests: write`, and no sticky PR comment step: an apply is
42648
+ * always a `workflow_dispatch`, so the comment could never fire.
42649
+ * - No branch filter on `github.ref`. The ref an operator happens to
42650
+ * dispatch from is unrelated to what was planned; the branch that matters
42651
+ * is the plan run's, and the validation job checks that one.
42652
+ *
42653
+ * When `environmentName` is also supplied, it lands here rather than on the
42654
+ * plan's jobs — a second approver on top of whoever dispatched the apply.
42655
+ * `validate-plan` stays ungated so its verdict is available to read *before*
42656
+ * the approval is given.
42657
+ */
42658
+ this.addApplyJob = (workflow, target, homeRepositoryCondition) => {
42659
+ const applyJobName = this.buildApplyJobName(target);
42660
+ const { verifyArtifactDigest } = this.planApplyOptions;
42661
+ const artifactsDirectory = this.rootProject.artifactsDirectory;
42662
+ workflow.addJob(applyJobName, {
42663
+ name: `Apply ${this.project.name} ${target.awsStageType}/${target.deploymentTargetRole}/${target.account}/${target.region}`,
42664
+ needs: [
42665
+ VALIDATE_PLAN_JOB_ID,
42666
+ ...this.deployAfterTargets.map((p) => this.buildApplyJobName(p))
42667
+ ],
42668
+ runsOn: ["ubuntu-latest"],
42669
+ permissions: {
42670
+ actions: import_workflows_model5.JobPermission.READ,
42671
+ contents: import_workflows_model5.JobPermission.READ,
42672
+ idToken: import_workflows_model5.JobPermission.WRITE
42673
+ },
42674
+ ...this.environmentName ? { environment: this.environmentName } : void 0,
42675
+ /**
42676
+ * Shares the deploy job's group name so an apply can never overlap a
42677
+ * same-target deploy. `cancel-in-progress` is spelled out rather than
42678
+ * left to GitHub's default: cancelling a half-finished `cdk deploy`
42679
+ * strands a CloudFormation stack mid-update.
42680
+ */
42681
+ concurrency: {
42682
+ group: this.buildJobName(target),
42683
+ "cancel-in-progress": false
42684
+ },
42685
+ ...homeRepositoryCondition ? { if: `\${{ ${homeRepositoryCondition} }}` } : {},
42686
+ steps: [
42687
+ /**
42688
+ * Cross-run download of the plan's assembly. `run-id` plus an explicit
42689
+ * `github-token` is what makes `actions/download-artifact` look outside
42690
+ * the current run; without both it silently searches this run and finds
42691
+ * nothing.
42692
+ */
42693
+ {
42694
+ name: "Download plan build artifacts",
42695
+ uses: "actions/download-artifact@v8",
42696
+ with: {
42697
+ name: BUILD_ARTIFACT_NAME,
42698
+ path: artifactsDirectory,
42699
+ "run-id": `\${{ inputs.${PLAN_RUN_ID_INPUT} }}`,
42700
+ "github-token": "${{ secrets.GITHUB_TOKEN }}",
42701
+ ...verifyArtifactDigest ? {
42702
+ "artifact-digest": `\${{ needs.${VALIDATE_PLAN_JOB_ID}.outputs.artifact_digest }}`
42703
+ } : {}
42704
+ }
42705
+ },
42706
+ {
42707
+ name: "Restore build artifact permissions",
42708
+ continueOnError: true,
42709
+ run: `cd ${artifactsDirectory} && setfacl --restore=${PERMISSION_BACKUP_FILE}`
42710
+ },
42711
+ ...this.deploySteps(target, { stickyPrComment: false })
42712
+ ]
42713
+ });
42714
+ };
42401
42715
  /**
42402
42716
  * Builds a GitHub Actions condition string that checks if the current branch
42403
42717
  * matches any of the provided branch patterns.
@@ -42426,7 +42740,15 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
42426
42740
  }
42427
42741
  return conditions.join(" || ");
42428
42742
  };
42429
- this.deploySteps = (target) => {
42743
+ /**
42744
+ * Steps for a target's deploy job.
42745
+ *
42746
+ * `stickyPrComment` drops the sticky-PR-comment step when false. Apply jobs
42747
+ * pass false: they only ever run on `workflow_dispatch` and so could never
42748
+ * satisfy that step's own `pull_request` guard.
42749
+ */
42750
+ this.deploySteps = (target, options = {}) => {
42751
+ const { stickyPrComment = true } = options;
42430
42752
  const {
42431
42753
  awsStageType,
42432
42754
  deploymentTargetRole,
@@ -42505,15 +42827,17 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
42505
42827
  * comment is keyed by the deploy job name so dev/stage/prod comments
42506
42828
  * don't collide on the same PR.
42507
42829
  */
42508
- {
42509
- name: "Sticky PR comment with deploy endpoints",
42510
- if: "github.event_name == 'pull_request' && hashFiles('deploy-urls.md') != ''",
42511
- uses: "marocchino/sticky-pull-request-comment@v3",
42512
- with: {
42513
- header: deployJobName,
42514
- path: "deploy-urls.md"
42830
+ ...stickyPrComment ? [
42831
+ {
42832
+ name: "Sticky PR comment with deploy endpoints",
42833
+ if: "github.event_name == 'pull_request' && hashFiles('deploy-urls.md') != ''",
42834
+ uses: "marocchino/sticky-pull-request-comment@v3",
42835
+ with: {
42836
+ header: deployJobName,
42837
+ path: "deploy-urls.md"
42838
+ }
42515
42839
  }
42516
- }
42840
+ ] : []
42517
42841
  ];
42518
42842
  };
42519
42843
  /**
@@ -42657,11 +42981,23 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
42657
42981
  (target) => target.awsStageType === this.awsStageType && target.ciDeployment
42658
42982
  ) ?? [];
42659
42983
  this.deployAfterTargets = options.deployAfterTargets ?? [];
42984
+ this.gate = options.gate;
42985
+ const isPlanApply = this.gate === DEPLOY_GATE.PLAN_APPLY;
42986
+ if (isPlanApply && options.diff?.enabled === false) {
42987
+ throw new Error(
42988
+ "AwsDeployWorkflow cannot disable `diff` under the `plan-apply` gate: the diff jobs are what keep the plan workflow publishing the build artifact the apply workflow deploys."
42989
+ );
42990
+ }
42660
42991
  this.diffOptions = {
42661
- enabled: options.diff?.enabled ?? false,
42992
+ enabled: options.diff?.enabled ?? isPlanApply,
42662
42993
  artifact: options.diff?.artifact ?? true,
42663
42994
  summary: options.diff?.summary ?? true
42664
42995
  };
42996
+ this.environmentName = resolveEnvironmentGate(
42997
+ options.gate,
42998
+ options.environmentName,
42999
+ "AwsDeployWorkflow"
43000
+ );
42665
43001
  this.homeRepository = options.homeRepository;
42666
43002
  const homeRepositoryCondition = renderHomeRepositoryCondition(
42667
43003
  this.homeRepository,
@@ -42739,6 +43075,17 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
42739
43075
  ...buildWorkflowOptions?.preBuildSteps ?? []
42740
43076
  ]
42741
43077
  });
43078
+ this.planApplyOptions = {
43079
+ applyWorkflowName: options.planApply?.applyWorkflowName ?? `${this.buildWorkflow.name}-apply`,
43080
+ requireCurrentHead: options.planApply?.requireCurrentHead ?? false,
43081
+ verifyArtifactDigest: options.planApply?.verifyArtifactDigest ?? false
43082
+ };
43083
+ if (isPlanApply) {
43084
+ this.applyWorkflow = this.createApplyWorkflow(
43085
+ github,
43086
+ homeRepositoryCondition
43087
+ );
43088
+ }
42742
43089
  this.awsDeploymentTargets.forEach((target) => {
42743
43090
  const deployJobName = this.buildJobName(target);
42744
43091
  const branchFilterCondition = this.buildBranchFilterCondition(
@@ -42749,24 +43096,31 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
42749
43096
  ...homeRepositoryCondition ? [homeRepositoryCondition] : [],
42750
43097
  ...branchFilterCondition ? [`(${branchFilterCondition})`] : []
42751
43098
  ].join(" && ") + " }}";
42752
- this.buildWorkflow.addPostBuildJob(deployJobName, {
42753
- name: `Deploy ${this.project.name} ${target.awsStageType}/${target.deploymentTargetRole}/${target.account}/${target.region}`,
42754
- needs: [
42755
- "build",
42756
- ...this.deployAfterTargets.map((p) => {
42757
- return this.buildJobName(p);
42758
- })
42759
- ],
42760
- runsOn: ["ubuntu-latest"],
42761
- permissions: {
42762
- contents: import_workflows_model5.JobPermission.READ,
42763
- idToken: import_workflows_model5.JobPermission.WRITE,
42764
- pullRequests: import_workflows_model5.JobPermission.WRITE
42765
- },
42766
- concurrency: deployJobName,
42767
- if: jobCondition,
42768
- steps: [...this.deploySteps(target)]
42769
- });
43099
+ if (this.applyWorkflow) {
43100
+ this.addApplyJob(this.applyWorkflow, target, homeRepositoryCondition);
43101
+ } else {
43102
+ const gatedOnDiff = !!this.environmentName && this.diffOptions.enabled;
43103
+ this.buildWorkflow.addPostBuildJob(deployJobName, {
43104
+ name: `Deploy ${this.project.name} ${target.awsStageType}/${target.deploymentTargetRole}/${target.account}/${target.region}`,
43105
+ needs: [
43106
+ "build",
43107
+ ...gatedOnDiff ? [this.buildDiffJobName(target)] : [],
43108
+ ...this.deployAfterTargets.map((p) => {
43109
+ return this.buildJobName(p);
43110
+ })
43111
+ ],
43112
+ runsOn: ["ubuntu-latest"],
43113
+ permissions: {
43114
+ contents: import_workflows_model5.JobPermission.READ,
43115
+ idToken: import_workflows_model5.JobPermission.WRITE,
43116
+ pullRequests: import_workflows_model5.JobPermission.WRITE
43117
+ },
43118
+ ...this.environmentName ? { environment: this.environmentName } : void 0,
43119
+ concurrency: deployJobName,
43120
+ if: jobCondition,
43121
+ steps: [...this.deploySteps(target)]
43122
+ });
43123
+ }
42770
43124
  if (this.diffOptions.enabled) {
42771
43125
  const diffJobName = this.buildDiffJobName(target);
42772
43126
  this.buildWorkflow.addPostBuildJob(diffJobName, {
@@ -43676,6 +44030,7 @@ export const collections = {
43676
44030
  AwsDeploymentConfig,
43677
44031
  AwsDeploymentTarget,
43678
44032
  AwsTeardownWorkflow,
44033
+ BUILD_ARTIFACT_NAME,
43679
44034
  BUILT_IN_BUNDLES,
43680
44035
  BUNDLE_OWNERSHIP,
43681
44036
  CDK_BOOTSTRAP_DEFAULTS_BY_STAGE,
@@ -43758,6 +44113,7 @@ export const collections = {
43758
44113
  DEFAULT_UNBLOCK_COMMENT_TEMPLATE,
43759
44114
  DEFAULT_UNBLOCK_DEPENDENTS_ENABLED,
43760
44115
  DEFAULT_UPSTREAM_CONFIGULATOR_ENABLED,
44116
+ DEPLOY_GATE,
43761
44117
  DOCS_SYNC_AUDIT_SCHEMA_VERSION,
43762
44118
  GITHUB_ISSUE_TYPES,
43763
44119
  GITHUB_ISSUE_TYPE_BY_TITLE_PREFIX,
@@ -43773,7 +44129,9 @@ export const collections = {
43773
44129
  MONOREPO_LAYOUT,
43774
44130
  MonorepoProject,
43775
44131
  Nvmrc,
44132
+ PERMISSION_BACKUP_FILE,
43776
44133
  PHASE_LABEL_TYPE_MAP,
44134
+ PLAN_RUN_ID_INPUT,
43777
44135
  PROD_DEPLOY_NAME,
43778
44136
  PROGRESS_FILES_FORMAT_VALUES,
43779
44137
  PnpmWorkspace,
@@ -43807,6 +44165,7 @@ export const collections = {
43807
44165
  TypeScriptConfig,
43808
44166
  TypeScriptProject,
43809
44167
  UNKNOWN_TYPE_FALLBACK_TIER,
44168
+ VALIDATE_PLAN_JOB_ID,
43810
44169
  VALID_PRIORITY_VALUES,
43811
44170
  VALID_STATUS_VALUES,
43812
44171
  VERSION,
@@ -43951,6 +44310,7 @@ export const collections = {
43951
44310
  renderNextRequirementIdProcedure,
43952
44311
  renderPhaseTypeInvariantSection,
43953
44312
  renderPhaseTypeInvariantShellHelpers,
44313
+ renderPlanValidationScript,
43954
44314
  renderPriorityRulesSection,
43955
44315
  renderProgressFileName,
43956
44316
  renderProgressFilePath,
@@ -43988,6 +44348,7 @@ export const collections = {
43988
44348
  resolveBuildPolicy,
43989
44349
  resolveBundleAgentTiers,
43990
44350
  resolveDefaultAgentTier,
44351
+ resolveEnvironmentGate,
43991
44352
  resolveIssueDefaults,
43992
44353
  resolveIssueTemplates,
43993
44354
  resolveModelAlias,