@codedrifters/configulator 0.0.425 → 0.0.427

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,
@@ -293,7 +294,9 @@ __export(index_exports, {
293
294
  MONOREPO_LAYOUT: () => MONOREPO_LAYOUT,
294
295
  MonorepoProject: () => MonorepoProject,
295
296
  Nvmrc: () => Nvmrc,
297
+ PERMISSION_BACKUP_FILE: () => PERMISSION_BACKUP_FILE,
296
298
  PHASE_LABEL_TYPE_MAP: () => PHASE_LABEL_TYPE_MAP,
299
+ PLAN_RUN_ID_INPUT: () => PLAN_RUN_ID_INPUT,
297
300
  PROD_DEPLOY_NAME: () => PROD_DEPLOY_NAME,
298
301
  PROGRESS_FILES_FORMAT_VALUES: () => PROGRESS_FILES_FORMAT_VALUES,
299
302
  PnpmWorkspace: () => PnpmWorkspace,
@@ -327,6 +330,7 @@ __export(index_exports, {
327
330
  TypeScriptConfig: () => TypeScriptConfig,
328
331
  TypeScriptProject: () => TypeScriptProject,
329
332
  UNKNOWN_TYPE_FALLBACK_TIER: () => UNKNOWN_TYPE_FALLBACK_TIER,
333
+ VALIDATE_PLAN_JOB_ID: () => VALIDATE_PLAN_JOB_ID,
330
334
  VALID_PRIORITY_VALUES: () => VALID_PRIORITY_VALUES,
331
335
  VALID_STATUS_VALUES: () => VALID_STATUS_VALUES,
332
336
  VERSION: () => VERSION,
@@ -471,6 +475,7 @@ __export(index_exports, {
471
475
  renderNextRequirementIdProcedure: () => renderNextRequirementIdProcedure,
472
476
  renderPhaseTypeInvariantSection: () => renderPhaseTypeInvariantSection,
473
477
  renderPhaseTypeInvariantShellHelpers: () => renderPhaseTypeInvariantShellHelpers,
478
+ renderPlanValidationScript: () => renderPlanValidationScript,
474
479
  renderPriorityRulesSection: () => renderPriorityRulesSection,
475
480
  renderProgressFileName: () => renderProgressFileName,
476
481
  renderProgressFilePath: () => renderProgressFilePath,
@@ -42304,7 +42309,17 @@ var DEPLOY_GATE = {
42304
42309
  * required reviewers, wait timers, deployment branch policies — apply before
42305
42310
  * the job is sent to a runner.
42306
42311
  */
42307
- ENVIRONMENT: "environment"
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"
42308
42323
  };
42309
42324
  var resolveEnvironmentGate = (gate, environmentName, componentName) => {
42310
42325
  const trimmed = environmentName?.trim();
@@ -42316,7 +42331,7 @@ var resolveEnvironmentGate = (gate, environmentName, componentName) => {
42316
42331
  }
42317
42332
  return void 0;
42318
42333
  }
42319
- if (!trimmed) {
42334
+ if (gate === DEPLOY_GATE.ENVIRONMENT && !trimmed) {
42320
42335
  throw new Error(
42321
42336
  `${componentName} requires a non-empty \`environmentName\` when \`gate\` is "${gate}"`
42322
42337
  );
@@ -42338,6 +42353,105 @@ var renderHomeRepositoryCondition = (homeRepository, componentName) => {
42338
42353
  return `github.repository == '${homeRepository}'`;
42339
42354
  };
42340
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
+
42341
42455
  // src/workflows/aws-deploy-workflow.ts
42342
42456
  var PROD_DEPLOY_NAME = "prod-deploy";
42343
42457
  var DIFF_OUTPUT_FILE = "cdk-diff.txt";
@@ -42409,6 +42523,20 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
42409
42523
  target.region
42410
42524
  ].join("-");
42411
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
+ };
42412
42540
  /**
42413
42541
  * Build the CI artifact name for a target's captured diff.
42414
42542
  *
@@ -42427,6 +42555,163 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
42427
42555
  target.region
42428
42556
  ].join("-");
42429
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
+ };
42430
42715
  /**
42431
42716
  * Builds a GitHub Actions condition string that checks if the current branch
42432
42717
  * matches any of the provided branch patterns.
@@ -42455,7 +42740,15 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
42455
42740
  }
42456
42741
  return conditions.join(" || ");
42457
42742
  };
42458
- 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;
42459
42752
  const {
42460
42753
  awsStageType,
42461
42754
  deploymentTargetRole,
@@ -42470,14 +42763,6 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
42470
42763
  return [
42471
42764
  ...this.setupPnpm(),
42472
42765
  ...this.setupNode(),
42473
- /**
42474
- * Install CDK, pinned to the same version that synthesized the cloud
42475
- * assembly being deployed. See {@link CdkCli.cliVersion}.
42476
- */
42477
- {
42478
- name: "Install CDK",
42479
- run: `pnpm add "aws-cdk@${cdkCli.cliVersion}"`
42480
- },
42481
42766
  /**
42482
42767
  * Configure AWS creds.
42483
42768
  */
@@ -42534,23 +42819,25 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
42534
42819
  * comment is keyed by the deploy job name so dev/stage/prod comments
42535
42820
  * don't collide on the same PR.
42536
42821
  */
42537
- {
42538
- name: "Sticky PR comment with deploy endpoints",
42539
- if: "github.event_name == 'pull_request' && hashFiles('deploy-urls.md') != ''",
42540
- uses: "marocchino/sticky-pull-request-comment@v3",
42541
- with: {
42542
- header: deployJobName,
42543
- path: "deploy-urls.md"
42822
+ ...stickyPrComment ? [
42823
+ {
42824
+ name: "Sticky PR comment with deploy endpoints",
42825
+ if: "github.event_name == 'pull_request' && hashFiles('deploy-urls.md') != ''",
42826
+ uses: "marocchino/sticky-pull-request-comment@v3",
42827
+ with: {
42828
+ header: deployJobName,
42829
+ path: "deploy-urls.md"
42830
+ }
42544
42831
  }
42545
- }
42832
+ ] : []
42546
42833
  ];
42547
42834
  };
42548
42835
  /**
42549
42836
  * Steps for a target's optional `cdk diff` job.
42550
42837
  *
42551
42838
  * Mirrors {@link deploySteps}' toolchain and credential setup — same pnpm /
42552
- * Node setup, same version-pinned `aws-cdk` install, same OIDC role — so a
42553
- * diff is computed by the same CLI that will run the deploy.
42839
+ * Node setup, same version-pinned `aws-cdk` CLI, same OIDC role — so a diff
42840
+ * is computed by the same CLI that will run the deploy.
42554
42841
  *
42555
42842
  * The `cdk diff` flags come from the {@link CdkCli} precedence chain via
42556
42843
  * `diffOptionsFor(target)`, so `method`, `securityOnly`, `fail`, and the rest
@@ -42578,14 +42865,6 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
42578
42865
  return [
42579
42866
  ...this.setupPnpm(),
42580
42867
  ...this.setupNode(),
42581
- /**
42582
- * Install CDK, pinned to the same version that synthesized the cloud
42583
- * assembly being diffed. See {@link CdkCli.cliVersion}.
42584
- */
42585
- {
42586
- name: "Install CDK",
42587
- run: `pnpm add "aws-cdk@${cdkCli.cliVersion}"`
42588
- },
42589
42868
  /**
42590
42869
  * Configure AWS creds.
42591
42870
  */
@@ -42686,8 +42965,15 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
42686
42965
  (target) => target.awsStageType === this.awsStageType && target.ciDeployment
42687
42966
  ) ?? [];
42688
42967
  this.deployAfterTargets = options.deployAfterTargets ?? [];
42968
+ this.gate = options.gate;
42969
+ const isPlanApply = this.gate === DEPLOY_GATE.PLAN_APPLY;
42970
+ if (isPlanApply && options.diff?.enabled === false) {
42971
+ throw new Error(
42972
+ "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."
42973
+ );
42974
+ }
42689
42975
  this.diffOptions = {
42690
- enabled: options.diff?.enabled ?? false,
42976
+ enabled: options.diff?.enabled ?? isPlanApply,
42691
42977
  artifact: options.diff?.artifact ?? true,
42692
42978
  summary: options.diff?.summary ?? true
42693
42979
  };
@@ -42773,6 +43059,17 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
42773
43059
  ...buildWorkflowOptions?.preBuildSteps ?? []
42774
43060
  ]
42775
43061
  });
43062
+ this.planApplyOptions = {
43063
+ applyWorkflowName: options.planApply?.applyWorkflowName ?? `${this.buildWorkflow.name}-apply`,
43064
+ requireCurrentHead: options.planApply?.requireCurrentHead ?? false,
43065
+ verifyArtifactDigest: options.planApply?.verifyArtifactDigest ?? false
43066
+ };
43067
+ if (isPlanApply) {
43068
+ this.applyWorkflow = this.createApplyWorkflow(
43069
+ github,
43070
+ homeRepositoryCondition
43071
+ );
43072
+ }
42776
43073
  this.awsDeploymentTargets.forEach((target) => {
42777
43074
  const deployJobName = this.buildJobName(target);
42778
43075
  const branchFilterCondition = this.buildBranchFilterCondition(
@@ -42783,27 +43080,31 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
42783
43080
  ...homeRepositoryCondition ? [homeRepositoryCondition] : [],
42784
43081
  ...branchFilterCondition ? [`(${branchFilterCondition})`] : []
42785
43082
  ].join(" && ") + " }}";
42786
- const gatedOnDiff = !!this.environmentName && this.diffOptions.enabled;
42787
- this.buildWorkflow.addPostBuildJob(deployJobName, {
42788
- name: `Deploy ${this.project.name} ${target.awsStageType}/${target.deploymentTargetRole}/${target.account}/${target.region}`,
42789
- needs: [
42790
- "build",
42791
- ...gatedOnDiff ? [this.buildDiffJobName(target)] : [],
42792
- ...this.deployAfterTargets.map((p) => {
42793
- return this.buildJobName(p);
42794
- })
42795
- ],
42796
- runsOn: ["ubuntu-latest"],
42797
- permissions: {
42798
- contents: import_workflows_model5.JobPermission.READ,
42799
- idToken: import_workflows_model5.JobPermission.WRITE,
42800
- pullRequests: import_workflows_model5.JobPermission.WRITE
42801
- },
42802
- ...this.environmentName ? { environment: this.environmentName } : void 0,
42803
- concurrency: deployJobName,
42804
- if: jobCondition,
42805
- steps: [...this.deploySteps(target)]
42806
- });
43083
+ if (this.applyWorkflow) {
43084
+ this.addApplyJob(this.applyWorkflow, target, homeRepositoryCondition);
43085
+ } else {
43086
+ const gatedOnDiff = !!this.environmentName && this.diffOptions.enabled;
43087
+ this.buildWorkflow.addPostBuildJob(deployJobName, {
43088
+ name: `Deploy ${this.project.name} ${target.awsStageType}/${target.deploymentTargetRole}/${target.account}/${target.region}`,
43089
+ needs: [
43090
+ "build",
43091
+ ...gatedOnDiff ? [this.buildDiffJobName(target)] : [],
43092
+ ...this.deployAfterTargets.map((p) => {
43093
+ return this.buildJobName(p);
43094
+ })
43095
+ ],
43096
+ runsOn: ["ubuntu-latest"],
43097
+ permissions: {
43098
+ contents: import_workflows_model5.JobPermission.READ,
43099
+ idToken: import_workflows_model5.JobPermission.WRITE,
43100
+ pullRequests: import_workflows_model5.JobPermission.WRITE
43101
+ },
43102
+ ...this.environmentName ? { environment: this.environmentName } : void 0,
43103
+ concurrency: deployJobName,
43104
+ if: jobCondition,
43105
+ steps: [...this.deploySteps(target)]
43106
+ });
43107
+ }
42807
43108
  if (this.diffOptions.enabled) {
42808
43109
  const diffJobName = this.buildDiffJobName(target);
42809
43110
  this.buildWorkflow.addPostBuildJob(diffJobName, {
@@ -43713,6 +44014,7 @@ export const collections = {
43713
44014
  AwsDeploymentConfig,
43714
44015
  AwsDeploymentTarget,
43715
44016
  AwsTeardownWorkflow,
44017
+ BUILD_ARTIFACT_NAME,
43716
44018
  BUILT_IN_BUNDLES,
43717
44019
  BUNDLE_OWNERSHIP,
43718
44020
  CDK_BOOTSTRAP_DEFAULTS_BY_STAGE,
@@ -43811,7 +44113,9 @@ export const collections = {
43811
44113
  MONOREPO_LAYOUT,
43812
44114
  MonorepoProject,
43813
44115
  Nvmrc,
44116
+ PERMISSION_BACKUP_FILE,
43814
44117
  PHASE_LABEL_TYPE_MAP,
44118
+ PLAN_RUN_ID_INPUT,
43815
44119
  PROD_DEPLOY_NAME,
43816
44120
  PROGRESS_FILES_FORMAT_VALUES,
43817
44121
  PnpmWorkspace,
@@ -43845,6 +44149,7 @@ export const collections = {
43845
44149
  TypeScriptConfig,
43846
44150
  TypeScriptProject,
43847
44151
  UNKNOWN_TYPE_FALLBACK_TIER,
44152
+ VALIDATE_PLAN_JOB_ID,
43848
44153
  VALID_PRIORITY_VALUES,
43849
44154
  VALID_STATUS_VALUES,
43850
44155
  VERSION,
@@ -43989,6 +44294,7 @@ export const collections = {
43989
44294
  renderNextRequirementIdProcedure,
43990
44295
  renderPhaseTypeInvariantSection,
43991
44296
  renderPhaseTypeInvariantShellHelpers,
44297
+ renderPlanValidationScript,
43992
44298
  renderPriorityRulesSection,
43993
44299
  renderProgressFileName,
43994
44300
  renderProgressFilePath,