@codedrifters/configulator 0.0.428 → 0.0.430

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.mjs CHANGED
@@ -41927,61 +41927,15 @@ import { merge as merge4 } from "ts-deepmerge";
41927
41927
 
41928
41928
  // src/workflows/aws-deploy-workflow.ts
41929
41929
  var import_utils11 = __toESM(require_lib());
41930
- import { Component as Component22 } from "projen";
41930
+ import { Component as Component25 } from "projen";
41931
41931
  import { BuildWorkflow } from "projen/lib/build";
41932
- import { GitHub as GitHub5, GithubWorkflow, WorkflowSteps as WorkflowSteps2 } from "projen/lib/github";
41933
- import { JobPermission as JobPermission5 } from "projen/lib/github/workflows-model";
41932
+ import { GitHub as GitHub6, WorkflowSteps as WorkflowSteps3 } from "projen/lib/github";
41933
+ import { JobPermission as JobPermission7 } from "projen/lib/github/workflows-model";
41934
41934
 
41935
- // src/workflows/deploy-gate.ts
41936
- var DEPLOY_GATE = {
41937
- /**
41938
- * Hold each deploy job behind a GitHub environment so its protection rules —
41939
- * required reviewers, wait timers, deployment branch policies — apply before
41940
- * the job is sent to a runner.
41941
- */
41942
- ENVIRONMENT: "environment",
41943
- /**
41944
- * Split the workflow in two. A **plan** workflow builds and diffs but never
41945
- * deploys; a dispatch-only **apply** workflow deploys the exact cloud
41946
- * assembly a nominated plan run produced.
41947
- *
41948
- * Needs no protection rule, so unlike {@link DEPLOY_GATE.ENVIRONMENT} it
41949
- * works on every GitHub plan. `environmentName` is optional here and layers
41950
- * an environment onto the apply jobs.
41951
- */
41952
- PLAN_APPLY: "plan-apply"
41953
- };
41954
- var resolveEnvironmentGate = (gate, environmentName, componentName) => {
41955
- const trimmed = environmentName?.trim();
41956
- if (gate === void 0) {
41957
- if (trimmed) {
41958
- throw new Error(
41959
- `${componentName} requires \`gate\` to be set when \`environmentName\` is supplied, got "${environmentName}" with no gate`
41960
- );
41961
- }
41962
- return void 0;
41963
- }
41964
- if (gate === DEPLOY_GATE.ENVIRONMENT && !trimmed) {
41965
- throw new Error(
41966
- `${componentName} requires a non-empty \`environmentName\` when \`gate\` is "${gate}"`
41967
- );
41968
- }
41969
- return trimmed;
41970
- };
41971
-
41972
- // src/workflows/home-repository.ts
41973
- var HOME_REPOSITORY_PATTERN = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
41974
- var renderHomeRepositoryCondition = (homeRepository, componentName) => {
41975
- if (homeRepository === void 0) {
41976
- return void 0;
41977
- }
41978
- if (!HOME_REPOSITORY_PATTERN.test(homeRepository)) {
41979
- throw new Error(
41980
- `${componentName} requires \`homeRepository\` to be an \`owner/repo\` slug, got "${homeRepository}"`
41981
- );
41982
- }
41983
- return `github.repository == '${homeRepository}'`;
41984
- };
41935
+ // src/workflows/apply-workflow.ts
41936
+ import { Component as Component22 } from "projen";
41937
+ import { GithubWorkflow } from "projen/lib/github";
41938
+ import { JobPermission as JobPermission5 } from "projen/lib/github/workflows-model";
41985
41939
 
41986
41940
  // src/workflows/plan-apply.ts
41987
41941
  var VALIDATE_PLAN_JOB_ID = "validate-plan";
@@ -42082,46 +42036,532 @@ var renderPlanValidationScript = (options) => {
42082
42036
  ].join("\n");
42083
42037
  };
42084
42038
 
42085
- // src/workflows/aws-deploy-workflow.ts
42086
- var PROD_DEPLOY_NAME = "prod-deploy";
42087
- var DIFF_OUTPUT_FILE = "cdk-diff.txt";
42039
+ // src/workflows/apply-workflow.ts
42040
+ var ApplyWorkflow = class _ApplyWorkflow extends Component22 {
42041
+ constructor(project, github, options) {
42042
+ super(project);
42043
+ /**
42044
+ * Every attached component's targets, in attach order. Held as targets rather
42045
+ * than branches because `AwsDeploymentTarget.branches` is mutable — the
42046
+ * branch union is read at `preSynthesize`, once every component has attached.
42047
+ */
42048
+ this.attachedTargets = [];
42049
+ /**
42050
+ * Attach another component's apply jobs to this workflow.
42051
+ *
42052
+ * Every setting the shared `validate-plan` job bakes in must match what the
42053
+ * workflow was created with — there is one validation job for the whole file,
42054
+ * so a disagreement cannot be honoured and is rejected rather than resolved
42055
+ * in favour of whichever component happened to be constructed first.
42056
+ */
42057
+ this.attach = (options) => {
42058
+ const { componentName } = options;
42059
+ const name = this.workflow.name;
42060
+ if (options.planWorkflowName !== this.contract.planWorkflowName) {
42061
+ throw new Error(
42062
+ `${componentName} cannot attach to the apply workflow "${name}": it plans from build workflow "${options.planWorkflowName}", but that apply workflow only accepts runs of "${this.contract.planWorkflowName}". Every component sharing an apply workflow must share one build workflow.`
42063
+ );
42064
+ }
42065
+ this.requireAgreement(
42066
+ componentName,
42067
+ "requireCurrentHead",
42068
+ options.requireCurrentHead,
42069
+ this.contract.requireCurrentHead
42070
+ );
42071
+ this.requireAgreement(
42072
+ componentName,
42073
+ "verifyArtifactDigest",
42074
+ options.verifyArtifactDigest,
42075
+ this.contract.verifyArtifactDigest
42076
+ );
42077
+ this.attachedTargets.push(...options.awsDeploymentTargets);
42078
+ };
42079
+ /**
42080
+ * Reject a boolean `planApply` setting that differs from the one this
42081
+ * workflow was created with.
42082
+ */
42083
+ this.requireAgreement = (componentName, option, supplied, expected) => {
42084
+ if (supplied === expected) {
42085
+ return;
42086
+ }
42087
+ throw new Error(
42088
+ `${componentName} cannot attach to the apply workflow "${this.workflow.name}": \`planApply.${option}\` is ${supplied} here and ${expected} on the components already attached. The setting is baked into the one shared \`${VALIDATE_PLAN_JOB_ID}\` job, so every component sharing an apply workflow must agree on it.`
42089
+ );
42090
+ };
42091
+ /**
42092
+ * Render the validation job every apply job depends on.
42093
+ *
42094
+ * `allowedBranches` is the union of every attached target's branches, deduped
42095
+ * and left in attach order. The apply run's own `github.ref` says nothing
42096
+ * about what was planned, so the list is checked against the *plan run's*
42097
+ * `head_branch` rather than against a workflow-level branch filter.
42098
+ */
42099
+ this.renderValidateJob = () => {
42100
+ const { planWorkflowName, requireCurrentHead, verifyArtifactDigest } = this.contract;
42101
+ const allowedBranches = Array.from(
42102
+ new Set(
42103
+ this.attachedTargets.flatMap(
42104
+ (target) => target.branches.map((b) => b.branch)
42105
+ )
42106
+ )
42107
+ );
42108
+ return {
42109
+ name: "Validate plan run",
42110
+ runsOn: ["ubuntu-latest"],
42111
+ permissions: {
42112
+ actions: JobPermission5.READ,
42113
+ contents: JobPermission5.READ
42114
+ },
42115
+ ...this.homeRepositoryCondition ? { if: `\${{ ${this.homeRepositoryCondition} }}` } : {},
42116
+ outputs: {
42117
+ head_sha: { stepId: "validate_plan_run", outputName: "head_sha" },
42118
+ head_branch: { stepId: "validate_plan_run", outputName: "head_branch" },
42119
+ ...verifyArtifactDigest ? {
42120
+ artifact_digest: {
42121
+ stepId: "validate_plan_run",
42122
+ outputName: "artifact_digest"
42123
+ }
42124
+ } : {}
42125
+ },
42126
+ steps: [
42127
+ {
42128
+ name: "Validate plan run",
42129
+ id: "validate_plan_run",
42130
+ uses: "actions/github-script@v9",
42131
+ /**
42132
+ * The dispatch input reaches the script through the environment
42133
+ * rather than a `${{ }}` interpolation, so a crafted run id cannot
42134
+ * inject JavaScript into the validation itself.
42135
+ */
42136
+ env: {
42137
+ PLAN_RUN_ID: `\${{ inputs.${PLAN_RUN_ID_INPUT} }}`
42138
+ },
42139
+ with: {
42140
+ script: renderPlanValidationScript({
42141
+ planWorkflowPath: `.github/workflows/${planWorkflowName}.yml`,
42142
+ allowedBranches,
42143
+ requireCurrentHead,
42144
+ verifyArtifactDigest
42145
+ })
42146
+ }
42147
+ }
42148
+ ]
42149
+ };
42150
+ };
42151
+ const { applyWorkflowName } = options;
42152
+ if (github.tryFindWorkflow(applyWorkflowName)) {
42153
+ throw new Error(
42154
+ `${options.componentName} cannot create the apply workflow "${applyWorkflowName}" because a workflow with that name already exists. Set \`planApply.applyWorkflow\` to share the existing one, or \`planApply.applyWorkflowName\` to something unique.`
42155
+ );
42156
+ }
42157
+ this.contract = {
42158
+ planWorkflowName: options.planWorkflowName,
42159
+ requireCurrentHead: options.requireCurrentHead,
42160
+ verifyArtifactDigest: options.verifyArtifactDigest
42161
+ };
42162
+ this.homeRepositoryCondition = options.homeRepositoryCondition;
42163
+ this.attachedTargets.push(...options.awsDeploymentTargets);
42164
+ this.workflow = new GithubWorkflow(github, applyWorkflowName);
42165
+ this.workflow.on({
42166
+ workflowDispatch: {
42167
+ inputs: {
42168
+ [PLAN_RUN_ID_INPUT]: {
42169
+ description: "Run id of the plan workflow run to apply. Its build artifact is deployed verbatim.",
42170
+ required: true,
42171
+ type: "string"
42172
+ }
42173
+ }
42174
+ }
42175
+ });
42176
+ this.workflow.addJob(VALIDATE_PLAN_JOB_ID, this.renderValidateJob());
42177
+ }
42178
+ static of(project, workflow) {
42179
+ const isDefined = (c) => c instanceof _ApplyWorkflow && c.workflow === workflow;
42180
+ return project.components.find(isDefined);
42181
+ }
42182
+ preSynthesize() {
42183
+ this.workflow.updateJob(VALIDATE_PLAN_JOB_ID, this.renderValidateJob());
42184
+ super.preSynthesize();
42185
+ }
42186
+ };
42187
+
42188
+ // src/workflows/deploy-gate.ts
42189
+ var DEPLOY_GATE = {
42190
+ /**
42191
+ * Hold each deploy job behind a GitHub environment so its protection rules —
42192
+ * required reviewers, wait timers, deployment branch policies — apply before
42193
+ * the job is sent to a runner.
42194
+ */
42195
+ ENVIRONMENT: "environment",
42196
+ /**
42197
+ * Split the workflow in two. A **plan** workflow builds and diffs but never
42198
+ * deploys; a dispatch-only **apply** workflow deploys the exact cloud
42199
+ * assembly a nominated plan run produced.
42200
+ *
42201
+ * Needs no protection rule, so unlike {@link DEPLOY_GATE.ENVIRONMENT} it
42202
+ * works on every GitHub plan. `environmentName` is optional here and layers
42203
+ * an environment onto the apply jobs.
42204
+ */
42205
+ PLAN_APPLY: "plan-apply"
42206
+ };
42207
+ var resolveEnvironmentGate = (gate, environmentName, componentName) => {
42208
+ const trimmed = environmentName?.trim();
42209
+ if (gate === void 0) {
42210
+ if (trimmed) {
42211
+ throw new Error(
42212
+ `${componentName} requires \`gate\` to be set when \`environmentName\` is supplied, got "${environmentName}" with no gate`
42213
+ );
42214
+ }
42215
+ return void 0;
42216
+ }
42217
+ if (gate === DEPLOY_GATE.ENVIRONMENT && !trimmed) {
42218
+ throw new Error(
42219
+ `${componentName} requires a non-empty \`environmentName\` when \`gate\` is "${gate}"`
42220
+ );
42221
+ }
42222
+ return trimmed;
42223
+ };
42224
+
42225
+ // src/workflows/diff-report-job.ts
42226
+ import { Component as Component23 } from "projen";
42227
+ import { WorkflowSteps as WorkflowSteps2 } from "projen/lib/github";
42228
+ import { JobPermission as JobPermission6 } from "projen/lib/github/workflows-model";
42229
+ var DIFF_REPORT_JOB_ID = "diff-report";
42230
+ var DIFF_OUTPUT_DIRECTORY = "cdk-diff";
42231
+ var DIFF_PART_ARTIFACT_PREFIX = "cdk-diff-part";
42232
+ var DIFF_ARTIFACT_NAME = "cdk-diff";
42233
+ var DIFF_PART_RETENTION_DAYS = 1;
42088
42234
  var DIFF_SUMMARY_BYTE_LIMIT = 9e5;
42089
- var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component22 {
42090
- constructor(project, options = {}) {
42235
+ var RUN_URL = "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}";
42236
+ var DiffReportJob = class _DiffReportJob extends Component23 {
42237
+ constructor(project, options) {
42091
42238
  super(project);
42092
- this.project = project;
42093
- this.options = options;
42094
42239
  /**
42095
- * AWS environment type, such as primary or secondary.
42240
+ * Every contributed target, in attach order.
42241
+ */
42242
+ this.targets = [];
42243
+ /**
42244
+ * Publish settings are OR-ed rather than first-wins: one report means one
42245
+ * answer, and a component that asked for a summary should get one even when
42246
+ * a sibling did not.
42247
+ */
42248
+ this.summary = false;
42249
+ this.artifact = false;
42250
+ /**
42251
+ * Fold one component's contribution in.
42252
+ */
42253
+ this.record = (options) => {
42254
+ this.targets.push(...options.targets);
42255
+ this.summary = this.summary || options.summary;
42256
+ this.artifact = this.artifact || options.artifact;
42257
+ };
42258
+ /**
42259
+ * Compose the report's own condition.
42096
42260
  *
42097
- * @deprecated Use deployment target role terminology elsewhere. This property is maintained for backward compatibility.
42098
- * @default 'primary' (this is the only type supported currently)
42261
+ * `!cancelled()` leads, and it is load-bearing: a job whose `needs` failed or
42262
+ * were skipped is skipped by default, so without a status function the report
42263
+ * would vanish in exactly the cases it is most wanted — one target's diff
42264
+ * failing, or some targets not applying to this branch.
42099
42265
  */
42100
- this.awsEnvironmentType = import_utils11.DEPLOYMENT_TARGET_ROLE.PRIMARY;
42101
- this.setupNode = () => {
42266
+ this.renderJobCondition = () => {
42267
+ const branchConditions = this.targets.map(
42268
+ (target) => target.branchCondition
42269
+ );
42270
+ const anyBranch = branchConditions.some((condition) => !condition);
42271
+ const branchClause = anyBranch ? void 0 : Array.from(new Set(branchConditions)).join(" || ");
42272
+ return "${{ " + [
42273
+ "!cancelled()",
42274
+ "!needs.build.outputs.self_mutation_happened",
42275
+ ...this.homeRepositoryCondition ? [this.homeRepositoryCondition] : [],
42276
+ ...branchClause ? [`(${branchClause})`] : []
42277
+ ].join(" && ") + " }}";
42278
+ };
42279
+ /**
42280
+ * Pull every target's part into one directory.
42281
+ *
42282
+ * `continueOnError` because a run where every target's diff job was skipped
42283
+ * by its branch filter matches no parts at all, and a report with nothing to
42284
+ * report is not a failure.
42285
+ */
42286
+ this.renderDownloadStep = () => {
42102
42287
  return [
42103
- {
42104
- name: "Setup Node",
42105
- uses: `actions/setup-node@${VERSION.SETUP_NODE_ACTION_VERSION}`,
42288
+ WorkflowSteps2.downloadArtifact({
42289
+ name: "Download target diffs",
42290
+ continueOnError: true,
42106
42291
  with: {
42107
- ["node-version"]: VERSION.NODE_WORKFLOWS
42108
- },
42109
- // occasionally this step fails due to internal issues at github
42110
- timeoutMinutes: 1
42292
+ pattern: `${DIFF_PART_ARTIFACT_PREFIX}-*`,
42293
+ mergeMultiple: true,
42294
+ path: DIFF_OUTPUT_DIRECTORY
42295
+ }
42296
+ })
42297
+ ];
42298
+ };
42299
+ /**
42300
+ * Render every target's diff into the job summary, one collapsed `<details>`
42301
+ * block per target.
42302
+ *
42303
+ * A target whose part is absent renders as "did not run", which is what
42304
+ * distinguishes a target that was skipped or whose diff failed before it
42305
+ * captured anything from one that simply had no changes — the latter has a
42306
+ * file, carrying the CDK CLI's own no-differences wording, rather than being
42307
+ * detected by matching on it.
42308
+ */
42309
+ this.renderSummaryStep = () => {
42310
+ if (!this.summary || this.targets.length === 0) {
42311
+ return [];
42312
+ }
42313
+ const budget = Math.floor(DIFF_SUMMARY_BYTE_LIMIT / this.targets.length);
42314
+ const rows = this.targets.map(
42315
+ (target) => `'${target.label}|${target.outputFile}'`
42316
+ );
42317
+ return [
42318
+ {
42319
+ name: "Render diff report",
42320
+ if: "!cancelled()",
42321
+ shell: "bash",
42322
+ run: [
42323
+ "{",
42324
+ " while IFS='|' read -r label file; do",
42325
+ ' echo "## cdk diff \u2014 $label"',
42326
+ ' echo ""',
42327
+ ' if [ ! -f "$file" ]; then',
42328
+ " echo '_Did not run._'",
42329
+ ' echo ""',
42330
+ " continue",
42331
+ " fi",
42332
+ " echo '<details><summary>cdk diff output</summary>'",
42333
+ ' echo ""',
42334
+ " echo '```'",
42335
+ ` if [ "$(wc -c < "$file")" -gt ${budget} ]; then`,
42336
+ ` head -c ${budget} "$file"`,
42337
+ ` printf '\\n... truncated at ${budget} bytes ...\\n'`,
42338
+ " else",
42339
+ ' cat "$file"',
42340
+ " fi",
42341
+ " echo '```'",
42342
+ ' echo ""',
42343
+ " echo '</details>'",
42344
+ ' echo ""',
42345
+ ` done < <(printf '%s\\n' ${rows.join(" ")})`,
42346
+ ...this.artifact ? [
42347
+ ` echo 'Full diff: the \`${DIFF_ARTIFACT_NAME}\` artifact on [this run](${RUN_URL}).'`
42348
+ ] : [],
42349
+ '} >> "$GITHUB_STEP_SUMMARY"'
42350
+ ].join("\n")
42111
42351
  }
42112
42352
  ];
42113
42353
  };
42114
- this.setupPnpm = () => {
42354
+ /**
42355
+ * Fail the report when any target's diff failed.
42356
+ *
42357
+ * Runs last, so the summary and the artifact are published first — a reviewer
42358
+ * still gets the whole picture, including whatever the failing target managed
42359
+ * to capture. But the report must not end up green: everything gated behind
42360
+ * it `needs` it, and a job whose `needs` failed is skipped, which is what
42361
+ * keeps a failed diff from letting a gated deploy through to *Waiting for
42362
+ * approval*. Without this the report would always succeed and a diff failure
42363
+ * would only soft-block behind a human.
42364
+ *
42365
+ * Only `failure` counts. A target skipped by its branch filter reports
42366
+ * `skipped`, which is normal and must not fail the report.
42367
+ */
42368
+ this.renderFailureStep = () => {
42369
+ const jobNames = Array.from(
42370
+ new Set(this.targets.map((target) => target.jobName))
42371
+ );
42372
+ if (jobNames.length === 0) {
42373
+ return [];
42374
+ }
42115
42375
  return [
42116
42376
  {
42117
- name: "Setup PNPM",
42118
- uses: `pnpm/action-setup@${VERSION.PNPM_ACTION_SETUP_VERSION}`,
42377
+ name: "Fail if any target's diff failed",
42378
+ if: "!cancelled()",
42379
+ shell: "bash",
42380
+ run: [
42381
+ ...jobNames.map(
42382
+ (jobName) => `if [ "\${{ needs.${jobName}.result }}" = "failure" ]; then echo "::error::Diff job ${jobName} failed"; failed=1; fi`
42383
+ ),
42384
+ 'if [ "${failed:-0}" = "1" ]; then exit 1; fi'
42385
+ ].join("\n")
42386
+ }
42387
+ ];
42388
+ };
42389
+ /**
42390
+ * Upload the merged directory as the one artifact meant to be read.
42391
+ */
42392
+ this.renderUploadStep = () => {
42393
+ if (!this.artifact || this.targets.length === 0) {
42394
+ return [];
42395
+ }
42396
+ return [
42397
+ WorkflowSteps2.uploadArtifact({
42398
+ name: "Upload combined diff",
42399
+ if: "!cancelled()",
42119
42400
  with: {
42120
- version: this.rootProject.pnpmVersion
42401
+ name: DIFF_ARTIFACT_NAME,
42402
+ path: DIFF_OUTPUT_DIRECTORY,
42403
+ ifNoFilesFound: "ignore"
42121
42404
  }
42122
- }
42405
+ })
42123
42406
  ];
42124
42407
  };
42408
+ this.buildWorkflow = options.buildWorkflow;
42409
+ this.homeRepositoryCondition = options.homeRepositoryCondition;
42410
+ this.record(options);
42411
+ this.buildWorkflow.workflow.addJob(DIFF_REPORT_JOB_ID, {
42412
+ name: "Diff report",
42413
+ needs: ["build"],
42414
+ runsOn: ["ubuntu-latest"],
42415
+ permissions: {
42416
+ contents: JobPermission6.READ
42417
+ },
42418
+ steps: []
42419
+ });
42420
+ }
42421
+ static of(project, buildWorkflow) {
42422
+ const isDefined = (c) => c instanceof _DiffReportJob && c.buildWorkflow === buildWorkflow;
42423
+ return project.components.find(isDefined);
42424
+ }
42425
+ /**
42426
+ * Add a component's targets to the plan workflow's diff report, creating the
42427
+ * report job the first time it is called for that workflow.
42428
+ */
42429
+ static attach(project, options) {
42430
+ const existing = _DiffReportJob.of(project, options.buildWorkflow);
42431
+ if (existing) {
42432
+ existing.record(options);
42433
+ return existing;
42434
+ }
42435
+ return new _DiffReportJob(project, options);
42436
+ }
42437
+ preSynthesize() {
42438
+ this.buildWorkflow.workflow.updateJob(DIFF_REPORT_JOB_ID, {
42439
+ name: "Diff report",
42440
+ /**
42441
+ * `build` is a direct dependency so the job can read its
42442
+ * self-mutation output, which is otherwise unreachable — the `needs`
42443
+ * context only carries direct dependencies, not transitive ones.
42444
+ */
42445
+ needs: [
42446
+ "build",
42447
+ ...Array.from(new Set(this.targets.map((target) => target.jobName)))
42448
+ ],
42449
+ runsOn: ["ubuntu-latest"],
42450
+ permissions: {
42451
+ contents: JobPermission6.READ
42452
+ },
42453
+ if: this.renderJobCondition(),
42454
+ steps: [
42455
+ ...this.renderDownloadStep(),
42456
+ ...this.renderSummaryStep(),
42457
+ ...this.renderUploadStep(),
42458
+ ...this.renderFailureStep()
42459
+ ]
42460
+ });
42461
+ super.preSynthesize();
42462
+ }
42463
+ };
42464
+ var renderDiffPartUploadStep = (options) => {
42465
+ return WorkflowSteps2.uploadArtifact({
42466
+ name: `Upload diff ${options.label}`,
42467
+ if: "!cancelled()",
42468
+ with: {
42469
+ name: options.artifactName,
42470
+ path: options.outputFile,
42471
+ ifNoFilesFound: "ignore",
42472
+ retentionDays: DIFF_PART_RETENTION_DAYS
42473
+ }
42474
+ });
42475
+ };
42476
+
42477
+ // src/workflows/home-repository.ts
42478
+ import { Component as Component24 } from "projen";
42479
+ var HOME_REPOSITORY_PATTERN = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
42480
+ var renderHomeRepositoryCondition = (homeRepository, componentName) => {
42481
+ if (homeRepository === void 0) {
42482
+ return void 0;
42483
+ }
42484
+ if (!HOME_REPOSITORY_PATTERN.test(homeRepository)) {
42485
+ throw new Error(
42486
+ `${componentName} requires \`homeRepository\` to be an \`owner/repo\` slug, got "${homeRepository}"`
42487
+ );
42488
+ }
42489
+ return `github.repository == '${homeRepository}'`;
42490
+ };
42491
+ var describePin = (homeRepository) => homeRepository === void 0 ? "unset" : `"${homeRepository}"`;
42492
+ var WorkflowHomeRepository = class _WorkflowHomeRepository extends Component24 {
42493
+ static of(project, workflow) {
42494
+ const isDefined = (c) => c instanceof _WorkflowHomeRepository && c.workflow === workflow;
42495
+ return project.components.find(isDefined);
42496
+ }
42497
+ /**
42498
+ * Record a pin against a workflow, throwing when it disagrees with what an
42499
+ * earlier component declared for the same one.
42500
+ *
42501
+ * @param project - Project the record is attached to. Use the root project,
42502
+ * so components on sibling sub-projects find each other.
42503
+ */
42504
+ static require(project, options) {
42505
+ const { workflow, workflowName, homeRepository, componentName } = options;
42506
+ const existing = _WorkflowHomeRepository.of(project, workflow);
42507
+ if (!existing) {
42508
+ return new _WorkflowHomeRepository(project, options);
42509
+ }
42510
+ if (existing.homeRepository !== homeRepository) {
42511
+ throw new Error(
42512
+ `${componentName} requires every component sharing the workflow "${workflowName}" to declare the same \`homeRepository\`, got ${describePin(homeRepository)} here and ${describePin(existing.homeRepository)} already declared. The pin guards every job in one workflow file, so it cannot differ between them.`
42513
+ );
42514
+ }
42515
+ return existing;
42516
+ }
42517
+ constructor(project, options) {
42518
+ super(project);
42519
+ this.workflow = options.workflow;
42520
+ this.homeRepository = options.homeRepository;
42521
+ }
42522
+ };
42523
+
42524
+ // src/workflows/node-setup.ts
42525
+ var renderSetupNode = () => {
42526
+ return [
42527
+ {
42528
+ name: "Setup Node",
42529
+ uses: `actions/setup-node@${VERSION.SETUP_NODE_ACTION_VERSION}`,
42530
+ with: {
42531
+ ["node-version"]: VERSION.NODE_WORKFLOWS
42532
+ },
42533
+ timeoutMinutes: 1
42534
+ }
42535
+ ];
42536
+ };
42537
+ var renderSetupPnpm = (pnpmVersion) => {
42538
+ return [
42539
+ {
42540
+ name: "Setup PNPM",
42541
+ uses: `pnpm/action-setup@${VERSION.PNPM_ACTION_SETUP_VERSION}`,
42542
+ with: {
42543
+ version: pnpmVersion
42544
+ }
42545
+ }
42546
+ ];
42547
+ };
42548
+
42549
+ // src/workflows/aws-deploy-workflow.ts
42550
+ var PROD_DEPLOY_NAME = "prod-deploy";
42551
+ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component25 {
42552
+ constructor(project, options = {}) {
42553
+ super(project);
42554
+ this.project = project;
42555
+ this.options = options;
42556
+ /**
42557
+ * AWS environment type, such as primary or secondary.
42558
+ *
42559
+ * @deprecated Use deployment target role terminology elsewhere. This property is maintained for backward compatibility.
42560
+ * @default 'primary' (this is the only type supported currently)
42561
+ */
42562
+ this.awsEnvironmentType = import_utils11.DEPLOYMENT_TARGET_ROLE.PRIMARY;
42563
+ this.setupNode = () => renderSetupNode();
42564
+ this.setupPnpm = () => renderSetupPnpm(this.rootProject.pnpmVersion);
42125
42565
  /**
42126
42566
  * Build the deterministic GitHub Actions job name for a deploy target.
42127
42567
  *
@@ -42139,45 +42579,43 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component22 {
42139
42579
  ].join("-");
42140
42580
  };
42141
42581
  /**
42142
- * Build the deterministic GitHub Actions job name for a target's `cdk diff`
42143
- * job. Mirrors {@link buildJobName} with a `diff` verb so the diff and deploy
42144
- * jobs for one target sort next to each other in the run view.
42582
+ * Build the deterministic GitHub Actions job name for a target's apply job.
42583
+ * Mirrors {@link buildJobName} with an `apply` verb.
42145
42584
  */
42146
- this.buildDiffJobName = (target) => {
42585
+ this.buildApplyJobName = (target) => {
42147
42586
  return [
42148
42587
  target.awsStageType,
42149
42588
  target.deploymentTargetRole,
42150
- "diff",
42589
+ "apply",
42151
42590
  target.project.name,
42152
42591
  target.account,
42153
42592
  target.region
42154
42593
  ].join("-");
42155
42594
  };
42156
42595
  /**
42157
- * Build the deterministic GitHub Actions job name for a target's apply job.
42158
- * Mirrors {@link buildJobName} with an `apply` verb.
42596
+ * Build the deterministic GitHub Actions job name for a target's `cdk diff`
42597
+ * job. Mirrors {@link buildJobName} with a `diff` verb so the diff and deploy
42598
+ * jobs for one target sort next to each other in the run view.
42159
42599
  */
42160
- this.buildApplyJobName = (target) => {
42600
+ this.buildDiffJobName = (target) => {
42161
42601
  return [
42162
42602
  target.awsStageType,
42163
42603
  target.deploymentTargetRole,
42164
- "apply",
42604
+ "diff",
42165
42605
  target.project.name,
42166
42606
  target.account,
42167
42607
  target.region
42168
42608
  ].join("-");
42169
42609
  };
42170
42610
  /**
42171
- * Build the CI artifact name for a target's captured diff.
42611
+ * Build the slug that makes a target unique among every target in the plan
42612
+ * workflow, across every deploy component feeding it.
42172
42613
  *
42173
- * Carries every component that makes a target unique — including
42174
- * `deploymentTargetRole`, which two otherwise-identical targets can differ
42175
- * on. `actions/upload-artifact` v4+ treats artifacts as immutable and rejects
42176
- * a duplicate name within a run, so parallel diff jobs cannot share one.
42614
+ * Includes `deploymentTargetRole`, which two otherwise-identical targets can
42615
+ * differ on.
42177
42616
  */
42178
- this.buildDiffArtifactName = (target) => {
42617
+ this.buildDiffSlug = (target) => {
42179
42618
  return [
42180
- "cdk-diff",
42181
42619
  target.awsStageType,
42182
42620
  target.deploymentTargetRole,
42183
42621
  target.project.name,
@@ -42186,83 +42624,45 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component22 {
42186
42624
  ].join("-");
42187
42625
  };
42188
42626
  /**
42189
- * Create the dispatch-only apply workflow and its validation job.
42627
+ * Build the file a target's captured diff is written to, both in its own diff
42628
+ * job's workspace and in the merged directory the report job assembles.
42629
+ */
42630
+ this.buildDiffOutputFile = (target) => {
42631
+ return `${DIFF_OUTPUT_DIRECTORY}/${this.buildDiffSlug(target)}.txt`;
42632
+ };
42633
+ /**
42634
+ * Build the intermediate artifact name carrying one target's captured diff.
42190
42635
  *
42191
- * The workflow file name defaults to the plan workflow's name suffixed with
42192
- * `-apply`. A name already in use throws rather than silently colliding on
42193
- * the underlying `.github/workflows/<name>.yml` file the usual cause is two
42194
- * `AwsDeployWorkflow`s sharing one build workflow, and the fix is an explicit
42195
- * `planApply.applyWorkflowName`.
42636
+ * The diff jobs run in parallel and `actions/upload-artifact` v4+ rejects a
42637
+ * duplicate name within a run, so each target needs its own. The report job
42638
+ * merges them into a single artifact, which is the one meant to be read.
42196
42639
  */
42197
- this.createApplyWorkflow = (github, homeRepositoryCondition) => {
42198
- const { applyWorkflowName, requireCurrentHead, verifyArtifactDigest } = this.planApplyOptions;
42199
- if (github.tryFindWorkflow(applyWorkflowName)) {
42640
+ this.buildDiffPartArtifactName = (target) => {
42641
+ return `${DIFF_PART_ARTIFACT_PREFIX}-${this.buildDiffSlug(target)}`;
42642
+ };
42643
+ /**
42644
+ * Join an apply workflow another `AwsDeployWorkflow` already created, so this
42645
+ * component's apply jobs land in that file rather than a second one.
42646
+ *
42647
+ * Rejects a `GithubWorkflow` that is not an apply workflow: attaching to an
42648
+ * arbitrary one would add apply jobs depending on a `validate-plan` job that
42649
+ * does not exist there, which GitHub rejects only at run time.
42650
+ */
42651
+ this.attachApplyWorkflow = (workflow) => {
42652
+ const shared = ApplyWorkflow.of(this.rootProject, workflow);
42653
+ if (!shared) {
42200
42654
  throw new Error(
42201
- `AwsDeployWorkflow cannot create the apply workflow "${applyWorkflowName}" because a workflow with that name already exists. Set \`planApply.applyWorkflowName\` to something unique.`
42655
+ `AwsDeployWorkflow cannot attach to the workflow "${workflow.name}" because it is not an apply workflow. Pass the \`applyWorkflow\` of an AwsDeployWorkflow built with the \`plan-apply\` gate.`
42202
42656
  );
42203
42657
  }
42204
- const workflow = new GithubWorkflow(github, applyWorkflowName);
42205
- workflow.on({
42206
- workflowDispatch: {
42207
- inputs: {
42208
- [PLAN_RUN_ID_INPUT]: {
42209
- description: "Run id of the plan workflow run to apply. Its build artifact is deployed verbatim.",
42210
- required: true,
42211
- type: "string"
42212
- }
42213
- }
42214
- }
42215
- });
42216
- const allowedBranches = Array.from(
42217
- new Set(
42218
- this.awsDeploymentTargets.flatMap(
42219
- (target) => target.branches.map((b) => b.branch)
42220
- )
42221
- )
42222
- );
42223
- workflow.addJob(VALIDATE_PLAN_JOB_ID, {
42224
- name: "Validate plan run",
42225
- runsOn: ["ubuntu-latest"],
42226
- permissions: {
42227
- actions: JobPermission5.READ,
42228
- contents: JobPermission5.READ
42229
- },
42230
- ...homeRepositoryCondition ? { if: `\${{ ${homeRepositoryCondition} }}` } : {},
42231
- outputs: {
42232
- head_sha: { stepId: "validate_plan_run", outputName: "head_sha" },
42233
- head_branch: { stepId: "validate_plan_run", outputName: "head_branch" },
42234
- ...verifyArtifactDigest ? {
42235
- artifact_digest: {
42236
- stepId: "validate_plan_run",
42237
- outputName: "artifact_digest"
42238
- }
42239
- } : {}
42240
- },
42241
- steps: [
42242
- {
42243
- name: "Validate plan run",
42244
- id: "validate_plan_run",
42245
- uses: "actions/github-script@v9",
42246
- /**
42247
- * The dispatch input reaches the script through the environment
42248
- * rather than a `${{ }}` interpolation, so a crafted run id cannot
42249
- * inject JavaScript into the validation itself.
42250
- */
42251
- env: {
42252
- PLAN_RUN_ID: `\${{ inputs.${PLAN_RUN_ID_INPUT} }}`
42253
- },
42254
- with: {
42255
- script: renderPlanValidationScript({
42256
- planWorkflowPath: `.github/workflows/${this.buildWorkflow.name}.yml`,
42257
- allowedBranches,
42258
- requireCurrentHead,
42259
- verifyArtifactDigest
42260
- })
42261
- }
42262
- }
42263
- ]
42658
+ shared.attach({
42659
+ planWorkflowName: this.buildWorkflow.name,
42660
+ requireCurrentHead: this.planApplyOptions.requireCurrentHead,
42661
+ verifyArtifactDigest: this.planApplyOptions.verifyArtifactDigest,
42662
+ awsDeploymentTargets: this.awsDeploymentTargets,
42663
+ componentName: "AwsDeployWorkflow"
42264
42664
  });
42265
- return workflow;
42665
+ return shared;
42266
42666
  };
42267
42667
  /**
42268
42668
  * Register one target's apply job on the apply workflow.
@@ -42297,9 +42697,9 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component22 {
42297
42697
  ],
42298
42698
  runsOn: ["ubuntu-latest"],
42299
42699
  permissions: {
42300
- actions: JobPermission5.READ,
42301
- contents: JobPermission5.READ,
42302
- idToken: JobPermission5.WRITE
42700
+ actions: JobPermission7.READ,
42701
+ contents: JobPermission7.READ,
42702
+ idToken: JobPermission7.WRITE
42303
42703
  },
42304
42704
  ...this.environmentName ? { environment: this.environmentName } : void 0,
42305
42705
  /**
@@ -42489,9 +42889,7 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component22 {
42489
42889
  const { roleArn, stackPattern } = ciDeploymentConfig ?? {};
42490
42890
  const { rootCdkOut, cdkCli } = awsDeploymentConfig;
42491
42891
  const label = `${awsStageType}/${deploymentTargetRole}/${account}/${region}`;
42492
- const artifactName = this.buildDiffArtifactName(target);
42493
- const capturedFileGuard = `always() && hashFiles('${DIFF_OUTPUT_FILE}') != ''`;
42494
- const runUrl = "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}";
42892
+ const outputFile = this.buildDiffOutputFile(target);
42495
42893
  return [
42496
42894
  ...this.setupPnpm(),
42497
42895
  ...this.setupNode(),
@@ -42511,69 +42909,36 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component22 {
42511
42909
  /**
42512
42910
  * Run CDK Diff, capturing the output while leaving it visible in the job
42513
42911
  * log. `pipefail` keeps the CLI's exit code from being masked by `tee`.
42912
+ *
42913
+ * The capture lands under the same path the report job will merge it
42914
+ * back to, so a target's file name is identical in both places.
42514
42915
  */
42515
42916
  {
42516
42917
  name: `Diff ${label}`,
42517
42918
  shell: "bash",
42518
42919
  run: [
42519
42920
  "set -o pipefail",
42921
+ `mkdir -p ${DIFF_OUTPUT_DIRECTORY}`,
42520
42922
  `pnpm dlx "aws-cdk@${cdkCli.cliVersion}" ${renderCdkDiff({
42521
42923
  ...cdkCli.diffOptionsFor(target),
42522
42924
  app: rootCdkOut,
42523
42925
  ci: true,
42524
42926
  noColor: true,
42525
42927
  stackPatterns: stackPattern ? [stackPattern] : void 0
42526
- })} 2>&1 | tee ${DIFF_OUTPUT_FILE}`
42928
+ })} 2>&1 | tee ${outputFile}`
42527
42929
  ].join("\n")
42528
42930
  },
42529
42931
  /**
42530
- * Render the captured diff into the job summary inside a collapsed
42531
- * `<details>` block, truncated below GitHub's 1 MiB per-step cap — a
42532
- * summary that exceeds the cap is dropped in full rather than clipped.
42932
+ * Hand the capture to the report job. Guarded on `!cancelled()` rather
42933
+ * than success, so a diff that exits non-zero — a synth error, or a
42934
+ * consumer who opted into `--fail` still contributes what it captured
42935
+ * instead of leaving a hole in the report.
42533
42936
  */
42534
- ...this.diffOptions.summary ? [
42535
- {
42536
- name: `Render diff summary ${label}`,
42537
- if: capturedFileGuard,
42538
- shell: "bash",
42539
- run: [
42540
- `size=$(wc -c < ${DIFF_OUTPUT_FILE})`,
42541
- "{",
42542
- ` echo '## cdk diff \u2014 ${label}'`,
42543
- ' echo ""',
42544
- " echo '<details><summary>cdk diff output</summary>'",
42545
- ' echo ""',
42546
- " echo '```'",
42547
- ` if [ "$size" -gt ${DIFF_SUMMARY_BYTE_LIMIT} ]; then`,
42548
- ` head -c ${DIFF_SUMMARY_BYTE_LIMIT} ${DIFF_OUTPUT_FILE}`,
42549
- ` printf '\\n... truncated at ${DIFF_SUMMARY_BYTE_LIMIT} bytes ...\\n'`,
42550
- " else",
42551
- ` cat ${DIFF_OUTPUT_FILE}`,
42552
- " fi",
42553
- " echo '```'",
42554
- ' echo ""',
42555
- " echo '</details>'",
42556
- ...this.diffOptions.artifact ? [
42557
- ' echo ""',
42558
- ` echo 'Full diff: the \`${artifactName}\` artifact on [this run](${runUrl}).'`
42559
- ] : [],
42560
- '} >> "$GITHUB_STEP_SUMMARY"'
42561
- ].join("\n")
42562
- }
42563
- ] : [],
42564
- /**
42565
- * Upload the untruncated diff as a per-run artifact.
42566
- */
42567
- ...this.diffOptions.artifact ? [
42568
- WorkflowSteps2.uploadArtifact({
42569
- name: `Upload diff ${label}`,
42570
- if: capturedFileGuard,
42571
- with: {
42572
- name: artifactName,
42573
- path: DIFF_OUTPUT_FILE
42574
- }
42575
- })
42576
- ] : []
42937
+ renderDiffPartUploadStep({
42938
+ label,
42939
+ artifactName: this.buildDiffPartArtifactName(target),
42940
+ outputFile
42941
+ })
42577
42942
  ];
42578
42943
  };
42579
42944
  if (!(project.root instanceof MonorepoProject)) {
@@ -42582,7 +42947,7 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component22 {
42582
42947
  );
42583
42948
  }
42584
42949
  this.rootProject = project.root;
42585
- const github = GitHub5.of(this.rootProject);
42950
+ const github = GitHub6.of(this.rootProject);
42586
42951
  if (!github) {
42587
42952
  throw new Error(
42588
42953
  "AwsDeployWorkflow requires a GitHub component in the root project"
@@ -42639,14 +43004,20 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component22 {
42639
43004
  buildTask: this.rootProject.buildTask,
42640
43005
  /**
42641
43006
  * Use push triggers based n the branch config for each environment.
43007
+ *
43008
+ * Deduped: targets in one workflow routinely share a branch — two prod
43009
+ * targets both default to `main` — and a repeated entry is only noise
43010
+ * in the generated file, since GitHub treats this as a filter list.
42642
43011
  */
42643
43012
  workflowTriggers: {
42644
43013
  push: {
42645
- branches: [
42646
- ...this.awsDeploymentTargets.flatMap(
42647
- (t) => t.branches.map((b) => b.branch)
43014
+ branches: Array.from(
43015
+ new Set(
43016
+ this.awsDeploymentTargets.flatMap(
43017
+ (t) => t.branches.map((b) => b.branch)
43018
+ )
42648
43019
  )
42649
- ]
43020
+ )
42650
43021
  },
42651
43022
  workflowDispatch: {},
42652
43023
  ...options.buildWorkflowOptions?.workflowTriggers
@@ -42689,16 +43060,40 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component22 {
42689
43060
  ...buildWorkflowOptions?.preBuildSteps ?? []
42690
43061
  ]
42691
43062
  });
43063
+ WorkflowHomeRepository.require(this.rootProject, {
43064
+ workflow: this.buildWorkflow,
43065
+ workflowName: this.buildWorkflow.name,
43066
+ homeRepository: this.homeRepository,
43067
+ componentName: "AwsDeployWorkflow"
43068
+ });
43069
+ const sharedApplyWorkflow = options.planApply?.applyWorkflow;
43070
+ if (sharedApplyWorkflow && options.planApply?.applyWorkflowName) {
43071
+ throw new Error(
43072
+ "Cannot provide both planApply.applyWorkflow and planApply.applyWorkflowName: the workflow being joined already has a name."
43073
+ );
43074
+ }
42692
43075
  this.planApplyOptions = {
42693
- applyWorkflowName: options.planApply?.applyWorkflowName ?? `${this.buildWorkflow.name}-apply`,
43076
+ applyWorkflowName: sharedApplyWorkflow?.name ?? options.planApply?.applyWorkflowName ?? `${this.buildWorkflow.name}-apply`,
42694
43077
  requireCurrentHead: options.planApply?.requireCurrentHead ?? false,
42695
43078
  verifyArtifactDigest: options.planApply?.verifyArtifactDigest ?? false
42696
43079
  };
42697
43080
  if (isPlanApply) {
42698
- this.applyWorkflow = this.createApplyWorkflow(
42699
- github,
42700
- homeRepositoryCondition
42701
- );
43081
+ const applyWorkflow = sharedApplyWorkflow ? this.attachApplyWorkflow(sharedApplyWorkflow) : new ApplyWorkflow(this.rootProject, github, {
43082
+ applyWorkflowName: this.planApplyOptions.applyWorkflowName,
43083
+ planWorkflowName: this.buildWorkflow.name,
43084
+ requireCurrentHead: this.planApplyOptions.requireCurrentHead,
43085
+ verifyArtifactDigest: this.planApplyOptions.verifyArtifactDigest,
43086
+ homeRepositoryCondition,
43087
+ awsDeploymentTargets: this.awsDeploymentTargets,
43088
+ componentName: "AwsDeployWorkflow"
43089
+ });
43090
+ this.applyWorkflow = applyWorkflow.workflow;
43091
+ WorkflowHomeRepository.require(this.rootProject, {
43092
+ workflow: applyWorkflow.workflow,
43093
+ workflowName: this.planApplyOptions.applyWorkflowName,
43094
+ homeRepository: this.homeRepository,
43095
+ componentName: "AwsDeployWorkflow"
43096
+ });
42702
43097
  }
42703
43098
  this.awsDeploymentTargets.forEach((target) => {
42704
43099
  const deployJobName = this.buildJobName(target);
@@ -42718,16 +43113,16 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component22 {
42718
43113
  name: `Deploy ${this.project.name} ${target.awsStageType}/${target.deploymentTargetRole}/${target.account}/${target.region}`,
42719
43114
  needs: [
42720
43115
  "build",
42721
- ...gatedOnDiff ? [this.buildDiffJobName(target)] : [],
43116
+ ...gatedOnDiff ? [DIFF_REPORT_JOB_ID] : [],
42722
43117
  ...this.deployAfterTargets.map((p) => {
42723
43118
  return this.buildJobName(p);
42724
43119
  })
42725
43120
  ],
42726
43121
  runsOn: ["ubuntu-latest"],
42727
43122
  permissions: {
42728
- contents: JobPermission5.READ,
42729
- idToken: JobPermission5.WRITE,
42730
- pullRequests: JobPermission5.WRITE
43123
+ contents: JobPermission7.READ,
43124
+ idToken: JobPermission7.WRITE,
43125
+ pullRequests: JobPermission7.WRITE
42731
43126
  },
42732
43127
  ...this.environmentName ? { environment: this.environmentName } : void 0,
42733
43128
  concurrency: deployJobName,
@@ -42742,14 +43137,28 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component22 {
42742
43137
  needs: ["build"],
42743
43138
  runsOn: ["ubuntu-latest"],
42744
43139
  permissions: {
42745
- contents: JobPermission5.READ,
42746
- idToken: JobPermission5.WRITE
43140
+ contents: JobPermission7.READ,
43141
+ idToken: JobPermission7.WRITE
42747
43142
  },
42748
43143
  if: jobCondition,
42749
43144
  steps: [...this.diffSteps(target)]
42750
43145
  });
42751
43146
  }
42752
43147
  });
43148
+ if (this.diffOptions.enabled && this.awsDeploymentTargets.length > 0) {
43149
+ DiffReportJob.attach(this.rootProject, {
43150
+ buildWorkflow: this.buildWorkflow,
43151
+ homeRepositoryCondition,
43152
+ summary: this.diffOptions.summary,
43153
+ artifact: this.diffOptions.artifact,
43154
+ targets: this.awsDeploymentTargets.map((target) => ({
43155
+ label: `${this.project.name} ${target.awsStageType}/${target.deploymentTargetRole}/${target.account}/${target.region}`,
43156
+ jobName: this.buildDiffJobName(target),
43157
+ outputFile: this.buildDiffOutputFile(target),
43158
+ branchCondition: this.buildBranchFilterCondition(target.branches)
43159
+ }))
43160
+ });
43161
+ }
42753
43162
  addBuildCompleteJob(this.buildWorkflow);
42754
43163
  }
42755
43164
  static of(project, buildWorkflow) {
@@ -42763,7 +43172,7 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component22 {
42763
43172
  name: "Build Sub Projects",
42764
43173
  run: `pnpm exec projen ${ROOT_CI_TASK_NAME}`
42765
43174
  },
42766
- WorkflowSteps2.uploadArtifact({
43175
+ WorkflowSteps3.uploadArtifact({
42767
43176
  name: "Upload Turbo runs",
42768
43177
  if: "always()",
42769
43178
  continueOnError: true,
@@ -42779,9 +43188,9 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component22 {
42779
43188
  };
42780
43189
 
42781
43190
  // src/workflows/aws-teardown-workflow.ts
42782
- import { Component as Component23 } from "projen";
42783
- import { GitHub as GitHub6, GithubWorkflow as GithubWorkflow2 } from "projen/lib/github";
42784
- import { JobPermission as JobPermission6 } from "projen/lib/github/workflows-model";
43191
+ import { Component as Component26 } from "projen";
43192
+ import { GitHub as GitHub7, GithubWorkflow as GithubWorkflow3 } from "projen/lib/github";
43193
+ import { JobPermission as JobPermission8 } from "projen/lib/github/workflows-model";
42785
43194
  var DEFAULT_TEARDOWN_BRANCH_PATTERNS = [
42786
43195
  "feat/*",
42787
43196
  "fix/*",
@@ -42801,7 +43210,7 @@ var resolveBranchPatterns = (explicit, targets) => {
42801
43210
  }
42802
43211
  return [...DEFAULT_TEARDOWN_BRANCH_PATTERNS];
42803
43212
  };
42804
- var AwsTeardownWorkflow = class extends Component23 {
43213
+ var AwsTeardownWorkflow = class extends Component26 {
42805
43214
  constructor(rootProject, options) {
42806
43215
  super(rootProject);
42807
43216
  this.rootProject = rootProject;
@@ -42831,7 +43240,7 @@ var AwsTeardownWorkflow = class extends Component23 {
42831
43240
  "AwsTeardownWorkflow requires the root project to be a MonorepoProject"
42832
43241
  );
42833
43242
  }
42834
- const github = GitHub6.of(this.rootProject);
43243
+ const github = GitHub7.of(this.rootProject);
42835
43244
  if (!github) {
42836
43245
  throw new Error(
42837
43246
  "AwsTeardownWorkflow requires a GitHub component in the root project"
@@ -42845,7 +43254,7 @@ var AwsTeardownWorkflow = class extends Component23 {
42845
43254
  homeRepository,
42846
43255
  "AwsTeardownWorkflow"
42847
43256
  );
42848
- const workflow = new GithubWorkflow2(github, "teardown-dev");
43257
+ const workflow = new GithubWorkflow3(github, "teardown-dev");
42849
43258
  workflow.on({
42850
43259
  workflowDispatch: {},
42851
43260
  schedule: [
@@ -42873,8 +43282,8 @@ var AwsTeardownWorkflow = class extends Component23 {
42873
43282
  runsOn: ["ubuntu-latest"],
42874
43283
  ...homeRepositoryCondition ? { if: `\${{ ${homeRepositoryCondition} }}` } : {},
42875
43284
  permissions: {
42876
- contents: JobPermission6.READ,
42877
- idToken: JobPermission6.WRITE
43285
+ contents: JobPermission8.READ,
43286
+ idToken: JobPermission8.WRITE
42878
43287
  },
42879
43288
  env: {
42880
43289
  REPO: "${{ github.repository }}",
@@ -43638,6 +44047,7 @@ export {
43638
44047
  AUDIT_CATEGORY_ORDER,
43639
44048
  AgentConfig,
43640
44049
  ApiExtractor,
44050
+ ApplyWorkflow,
43641
44051
  AstroConfig,
43642
44052
  AstroOutput,
43643
44053
  AstroProject,
@@ -43733,7 +44143,12 @@ export {
43733
44143
  DEFAULT_UNBLOCK_DEPENDENTS_ENABLED,
43734
44144
  DEFAULT_UPSTREAM_CONFIGULATOR_ENABLED,
43735
44145
  DEPLOY_GATE,
44146
+ DIFF_ARTIFACT_NAME,
44147
+ DIFF_OUTPUT_DIRECTORY,
44148
+ DIFF_PART_ARTIFACT_PREFIX,
44149
+ DIFF_REPORT_JOB_ID,
43736
44150
  DOCS_SYNC_AUDIT_SCHEMA_VERSION,
44151
+ DiffReportJob,
43737
44152
  GITHUB_ISSUE_TYPES,
43738
44153
  GITHUB_ISSUE_TYPE_BY_TITLE_PREFIX,
43739
44154
  ISSUE_TEMPLATES_GENERATED_SUFFIX,
@@ -43792,6 +44207,7 @@ export {
43792
44207
  VERSION_NPM_PACKAGES,
43793
44208
  VSCodeConfig,
43794
44209
  Vitest,
44210
+ WorkflowHomeRepository,
43795
44211
  addApproveMergeUpgradeWorkflow,
43796
44212
  addBuildCompleteJob,
43797
44213
  addPlaywright,
@@ -43912,6 +44328,7 @@ export {
43912
44328
  renderCheckLinksProcedure,
43913
44329
  renderCustomDocSectionBlock,
43914
44330
  renderCustomDocSections,
44331
+ renderDiffPartUploadStep,
43915
44332
  renderExtractApiProcedure,
43916
44333
  renderFocusSection,
43917
44334
  renderGithubIssueTypeSection,
@@ -43943,6 +44360,8 @@ export {
43943
44360
  renderScopeGateSection,
43944
44361
  renderScopeGateShellHelpers,
43945
44362
  renderSetIssueTypeFallbackLines,
44363
+ renderSetupNode,
44364
+ renderSetupPnpm,
43946
44365
  renderSharedEditingBundleHook,
43947
44366
  renderSharedEditingHelperScript,
43948
44367
  renderSharedEditingRuleContent,