@codedrifters/configulator 0.0.429 → 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.js CHANGED
@@ -280,7 +280,12 @@ __export(index_exports, {
280
280
  DEFAULT_UNBLOCK_DEPENDENTS_ENABLED: () => DEFAULT_UNBLOCK_DEPENDENTS_ENABLED,
281
281
  DEFAULT_UPSTREAM_CONFIGULATOR_ENABLED: () => DEFAULT_UPSTREAM_CONFIGULATOR_ENABLED,
282
282
  DEPLOY_GATE: () => DEPLOY_GATE,
283
+ DIFF_ARTIFACT_NAME: () => DIFF_ARTIFACT_NAME,
284
+ DIFF_OUTPUT_DIRECTORY: () => DIFF_OUTPUT_DIRECTORY,
285
+ DIFF_PART_ARTIFACT_PREFIX: () => DIFF_PART_ARTIFACT_PREFIX,
286
+ DIFF_REPORT_JOB_ID: () => DIFF_REPORT_JOB_ID,
283
287
  DOCS_SYNC_AUDIT_SCHEMA_VERSION: () => DOCS_SYNC_AUDIT_SCHEMA_VERSION,
288
+ DiffReportJob: () => DiffReportJob,
284
289
  GITHUB_ISSUE_TYPES: () => GITHUB_ISSUE_TYPES,
285
290
  GITHUB_ISSUE_TYPE_BY_TITLE_PREFIX: () => GITHUB_ISSUE_TYPE_BY_TITLE_PREFIX,
286
291
  ISSUE_TEMPLATES_GENERATED_SUFFIX: () => ISSUE_TEMPLATES_GENERATED_SUFFIX,
@@ -460,6 +465,7 @@ __export(index_exports, {
460
465
  renderCheckLinksProcedure: () => renderCheckLinksProcedure,
461
466
  renderCustomDocSectionBlock: () => renderCustomDocSectionBlock,
462
467
  renderCustomDocSections: () => renderCustomDocSections,
468
+ renderDiffPartUploadStep: () => renderDiffPartUploadStep,
463
469
  renderExtractApiProcedure: () => renderExtractApiProcedure,
464
470
  renderFocusSection: () => renderFocusSection,
465
471
  renderGithubIssueTypeSection: () => renderGithubIssueTypeSection,
@@ -491,6 +497,8 @@ __export(index_exports, {
491
497
  renderScopeGateSection: () => renderScopeGateSection,
492
498
  renderScopeGateShellHelpers: () => renderScopeGateShellHelpers,
493
499
  renderSetIssueTypeFallbackLines: () => renderSetIssueTypeFallbackLines,
500
+ renderSetupNode: () => renderSetupNode,
501
+ renderSetupPnpm: () => renderSetupPnpm,
494
502
  renderSharedEditingBundleHook: () => renderSharedEditingBundleHook,
495
503
  renderSharedEditingHelperScript: () => renderSharedEditingHelperScript,
496
504
  renderSharedEditingRuleContent: () => renderSharedEditingRuleContent,
@@ -42292,17 +42300,17 @@ var DEFAULT_FAVICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0
42292
42300
  `;
42293
42301
 
42294
42302
  // src/projects/aws-cdk-project.ts
42295
- var import_projen30 = require("projen");
42303
+ var import_projen31 = require("projen");
42296
42304
  var import_javascript5 = require("projen/lib/javascript");
42297
42305
  var import_release2 = require("projen/lib/release");
42298
42306
  var import_ts_deepmerge4 = require("ts-deepmerge");
42299
42307
 
42300
42308
  // src/workflows/aws-deploy-workflow.ts
42301
42309
  var import_utils11 = __toESM(require_lib());
42302
- var import_projen28 = require("projen");
42310
+ var import_projen29 = require("projen");
42303
42311
  var import_build = require("projen/lib/build");
42304
- var import_github7 = require("projen/lib/github");
42305
- var import_workflows_model6 = require("projen/lib/github/workflows-model");
42312
+ var import_github8 = require("projen/lib/github");
42313
+ var import_workflows_model7 = require("projen/lib/github/workflows-model");
42306
42314
 
42307
42315
  // src/workflows/apply-workflow.ts
42308
42316
  var import_projen26 = require("projen");
@@ -42594,8 +42602,260 @@ var resolveEnvironmentGate = (gate, environmentName, componentName) => {
42594
42602
  return trimmed;
42595
42603
  };
42596
42604
 
42597
- // src/workflows/home-repository.ts
42605
+ // src/workflows/diff-report-job.ts
42598
42606
  var import_projen27 = require("projen");
42607
+ var import_github7 = require("projen/lib/github");
42608
+ var import_workflows_model6 = require("projen/lib/github/workflows-model");
42609
+ var DIFF_REPORT_JOB_ID = "diff-report";
42610
+ var DIFF_OUTPUT_DIRECTORY = "cdk-diff";
42611
+ var DIFF_PART_ARTIFACT_PREFIX = "cdk-diff-part";
42612
+ var DIFF_ARTIFACT_NAME = "cdk-diff";
42613
+ var DIFF_PART_RETENTION_DAYS = 1;
42614
+ var DIFF_SUMMARY_BYTE_LIMIT = 9e5;
42615
+ var RUN_URL = "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}";
42616
+ var DiffReportJob = class _DiffReportJob extends import_projen27.Component {
42617
+ constructor(project, options) {
42618
+ super(project);
42619
+ /**
42620
+ * Every contributed target, in attach order.
42621
+ */
42622
+ this.targets = [];
42623
+ /**
42624
+ * Publish settings are OR-ed rather than first-wins: one report means one
42625
+ * answer, and a component that asked for a summary should get one even when
42626
+ * a sibling did not.
42627
+ */
42628
+ this.summary = false;
42629
+ this.artifact = false;
42630
+ /**
42631
+ * Fold one component's contribution in.
42632
+ */
42633
+ this.record = (options) => {
42634
+ this.targets.push(...options.targets);
42635
+ this.summary = this.summary || options.summary;
42636
+ this.artifact = this.artifact || options.artifact;
42637
+ };
42638
+ /**
42639
+ * Compose the report's own condition.
42640
+ *
42641
+ * `!cancelled()` leads, and it is load-bearing: a job whose `needs` failed or
42642
+ * were skipped is skipped by default, so without a status function the report
42643
+ * would vanish in exactly the cases it is most wanted — one target's diff
42644
+ * failing, or some targets not applying to this branch.
42645
+ */
42646
+ this.renderJobCondition = () => {
42647
+ const branchConditions = this.targets.map(
42648
+ (target) => target.branchCondition
42649
+ );
42650
+ const anyBranch = branchConditions.some((condition) => !condition);
42651
+ const branchClause = anyBranch ? void 0 : Array.from(new Set(branchConditions)).join(" || ");
42652
+ return "${{ " + [
42653
+ "!cancelled()",
42654
+ "!needs.build.outputs.self_mutation_happened",
42655
+ ...this.homeRepositoryCondition ? [this.homeRepositoryCondition] : [],
42656
+ ...branchClause ? [`(${branchClause})`] : []
42657
+ ].join(" && ") + " }}";
42658
+ };
42659
+ /**
42660
+ * Pull every target's part into one directory.
42661
+ *
42662
+ * `continueOnError` because a run where every target's diff job was skipped
42663
+ * by its branch filter matches no parts at all, and a report with nothing to
42664
+ * report is not a failure.
42665
+ */
42666
+ this.renderDownloadStep = () => {
42667
+ return [
42668
+ import_github7.WorkflowSteps.downloadArtifact({
42669
+ name: "Download target diffs",
42670
+ continueOnError: true,
42671
+ with: {
42672
+ pattern: `${DIFF_PART_ARTIFACT_PREFIX}-*`,
42673
+ mergeMultiple: true,
42674
+ path: DIFF_OUTPUT_DIRECTORY
42675
+ }
42676
+ })
42677
+ ];
42678
+ };
42679
+ /**
42680
+ * Render every target's diff into the job summary, one collapsed `<details>`
42681
+ * block per target.
42682
+ *
42683
+ * A target whose part is absent renders as "did not run", which is what
42684
+ * distinguishes a target that was skipped or whose diff failed before it
42685
+ * captured anything from one that simply had no changes — the latter has a
42686
+ * file, carrying the CDK CLI's own no-differences wording, rather than being
42687
+ * detected by matching on it.
42688
+ */
42689
+ this.renderSummaryStep = () => {
42690
+ if (!this.summary || this.targets.length === 0) {
42691
+ return [];
42692
+ }
42693
+ const budget = Math.floor(DIFF_SUMMARY_BYTE_LIMIT / this.targets.length);
42694
+ const rows = this.targets.map(
42695
+ (target) => `'${target.label}|${target.outputFile}'`
42696
+ );
42697
+ return [
42698
+ {
42699
+ name: "Render diff report",
42700
+ if: "!cancelled()",
42701
+ shell: "bash",
42702
+ run: [
42703
+ "{",
42704
+ " while IFS='|' read -r label file; do",
42705
+ ' echo "## cdk diff \u2014 $label"',
42706
+ ' echo ""',
42707
+ ' if [ ! -f "$file" ]; then',
42708
+ " echo '_Did not run._'",
42709
+ ' echo ""',
42710
+ " continue",
42711
+ " fi",
42712
+ " echo '<details><summary>cdk diff output</summary>'",
42713
+ ' echo ""',
42714
+ " echo '```'",
42715
+ ` if [ "$(wc -c < "$file")" -gt ${budget} ]; then`,
42716
+ ` head -c ${budget} "$file"`,
42717
+ ` printf '\\n... truncated at ${budget} bytes ...\\n'`,
42718
+ " else",
42719
+ ' cat "$file"',
42720
+ " fi",
42721
+ " echo '```'",
42722
+ ' echo ""',
42723
+ " echo '</details>'",
42724
+ ' echo ""',
42725
+ ` done < <(printf '%s\\n' ${rows.join(" ")})`,
42726
+ ...this.artifact ? [
42727
+ ` echo 'Full diff: the \`${DIFF_ARTIFACT_NAME}\` artifact on [this run](${RUN_URL}).'`
42728
+ ] : [],
42729
+ '} >> "$GITHUB_STEP_SUMMARY"'
42730
+ ].join("\n")
42731
+ }
42732
+ ];
42733
+ };
42734
+ /**
42735
+ * Fail the report when any target's diff failed.
42736
+ *
42737
+ * Runs last, so the summary and the artifact are published first — a reviewer
42738
+ * still gets the whole picture, including whatever the failing target managed
42739
+ * to capture. But the report must not end up green: everything gated behind
42740
+ * it `needs` it, and a job whose `needs` failed is skipped, which is what
42741
+ * keeps a failed diff from letting a gated deploy through to *Waiting for
42742
+ * approval*. Without this the report would always succeed and a diff failure
42743
+ * would only soft-block behind a human.
42744
+ *
42745
+ * Only `failure` counts. A target skipped by its branch filter reports
42746
+ * `skipped`, which is normal and must not fail the report.
42747
+ */
42748
+ this.renderFailureStep = () => {
42749
+ const jobNames = Array.from(
42750
+ new Set(this.targets.map((target) => target.jobName))
42751
+ );
42752
+ if (jobNames.length === 0) {
42753
+ return [];
42754
+ }
42755
+ return [
42756
+ {
42757
+ name: "Fail if any target's diff failed",
42758
+ if: "!cancelled()",
42759
+ shell: "bash",
42760
+ run: [
42761
+ ...jobNames.map(
42762
+ (jobName) => `if [ "\${{ needs.${jobName}.result }}" = "failure" ]; then echo "::error::Diff job ${jobName} failed"; failed=1; fi`
42763
+ ),
42764
+ 'if [ "${failed:-0}" = "1" ]; then exit 1; fi'
42765
+ ].join("\n")
42766
+ }
42767
+ ];
42768
+ };
42769
+ /**
42770
+ * Upload the merged directory as the one artifact meant to be read.
42771
+ */
42772
+ this.renderUploadStep = () => {
42773
+ if (!this.artifact || this.targets.length === 0) {
42774
+ return [];
42775
+ }
42776
+ return [
42777
+ import_github7.WorkflowSteps.uploadArtifact({
42778
+ name: "Upload combined diff",
42779
+ if: "!cancelled()",
42780
+ with: {
42781
+ name: DIFF_ARTIFACT_NAME,
42782
+ path: DIFF_OUTPUT_DIRECTORY,
42783
+ ifNoFilesFound: "ignore"
42784
+ }
42785
+ })
42786
+ ];
42787
+ };
42788
+ this.buildWorkflow = options.buildWorkflow;
42789
+ this.homeRepositoryCondition = options.homeRepositoryCondition;
42790
+ this.record(options);
42791
+ this.buildWorkflow.workflow.addJob(DIFF_REPORT_JOB_ID, {
42792
+ name: "Diff report",
42793
+ needs: ["build"],
42794
+ runsOn: ["ubuntu-latest"],
42795
+ permissions: {
42796
+ contents: import_workflows_model6.JobPermission.READ
42797
+ },
42798
+ steps: []
42799
+ });
42800
+ }
42801
+ static of(project, buildWorkflow) {
42802
+ const isDefined = (c) => c instanceof _DiffReportJob && c.buildWorkflow === buildWorkflow;
42803
+ return project.components.find(isDefined);
42804
+ }
42805
+ /**
42806
+ * Add a component's targets to the plan workflow's diff report, creating the
42807
+ * report job the first time it is called for that workflow.
42808
+ */
42809
+ static attach(project, options) {
42810
+ const existing = _DiffReportJob.of(project, options.buildWorkflow);
42811
+ if (existing) {
42812
+ existing.record(options);
42813
+ return existing;
42814
+ }
42815
+ return new _DiffReportJob(project, options);
42816
+ }
42817
+ preSynthesize() {
42818
+ this.buildWorkflow.workflow.updateJob(DIFF_REPORT_JOB_ID, {
42819
+ name: "Diff report",
42820
+ /**
42821
+ * `build` is a direct dependency so the job can read its
42822
+ * self-mutation output, which is otherwise unreachable — the `needs`
42823
+ * context only carries direct dependencies, not transitive ones.
42824
+ */
42825
+ needs: [
42826
+ "build",
42827
+ ...Array.from(new Set(this.targets.map((target) => target.jobName)))
42828
+ ],
42829
+ runsOn: ["ubuntu-latest"],
42830
+ permissions: {
42831
+ contents: import_workflows_model6.JobPermission.READ
42832
+ },
42833
+ if: this.renderJobCondition(),
42834
+ steps: [
42835
+ ...this.renderDownloadStep(),
42836
+ ...this.renderSummaryStep(),
42837
+ ...this.renderUploadStep(),
42838
+ ...this.renderFailureStep()
42839
+ ]
42840
+ });
42841
+ super.preSynthesize();
42842
+ }
42843
+ };
42844
+ var renderDiffPartUploadStep = (options) => {
42845
+ return import_github7.WorkflowSteps.uploadArtifact({
42846
+ name: `Upload diff ${options.label}`,
42847
+ if: "!cancelled()",
42848
+ with: {
42849
+ name: options.artifactName,
42850
+ path: options.outputFile,
42851
+ ifNoFilesFound: "ignore",
42852
+ retentionDays: DIFF_PART_RETENTION_DAYS
42853
+ }
42854
+ });
42855
+ };
42856
+
42857
+ // src/workflows/home-repository.ts
42858
+ var import_projen28 = require("projen");
42599
42859
  var HOME_REPOSITORY_PATTERN = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
42600
42860
  var renderHomeRepositoryCondition = (homeRepository, componentName) => {
42601
42861
  if (homeRepository === void 0) {
@@ -42609,7 +42869,7 @@ var renderHomeRepositoryCondition = (homeRepository, componentName) => {
42609
42869
  return `github.repository == '${homeRepository}'`;
42610
42870
  };
42611
42871
  var describePin = (homeRepository) => homeRepository === void 0 ? "unset" : `"${homeRepository}"`;
42612
- var WorkflowHomeRepository = class _WorkflowHomeRepository extends import_projen27.Component {
42872
+ var WorkflowHomeRepository = class _WorkflowHomeRepository extends import_projen28.Component {
42613
42873
  static of(project, workflow) {
42614
42874
  const isDefined = (c) => c instanceof _WorkflowHomeRepository && c.workflow === workflow;
42615
42875
  return project.components.find(isDefined);
@@ -42641,11 +42901,34 @@ var WorkflowHomeRepository = class _WorkflowHomeRepository extends import_projen
42641
42901
  }
42642
42902
  };
42643
42903
 
42904
+ // src/workflows/node-setup.ts
42905
+ var renderSetupNode = () => {
42906
+ return [
42907
+ {
42908
+ name: "Setup Node",
42909
+ uses: `actions/setup-node@${VERSION.SETUP_NODE_ACTION_VERSION}`,
42910
+ with: {
42911
+ ["node-version"]: VERSION.NODE_WORKFLOWS
42912
+ },
42913
+ timeoutMinutes: 1
42914
+ }
42915
+ ];
42916
+ };
42917
+ var renderSetupPnpm = (pnpmVersion) => {
42918
+ return [
42919
+ {
42920
+ name: "Setup PNPM",
42921
+ uses: `pnpm/action-setup@${VERSION.PNPM_ACTION_SETUP_VERSION}`,
42922
+ with: {
42923
+ version: pnpmVersion
42924
+ }
42925
+ }
42926
+ ];
42927
+ };
42928
+
42644
42929
  // src/workflows/aws-deploy-workflow.ts
42645
42930
  var PROD_DEPLOY_NAME = "prod-deploy";
42646
- var DIFF_OUTPUT_FILE = "cdk-diff.txt";
42647
- var DIFF_SUMMARY_BYTE_LIMIT = 9e5;
42648
- var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Component {
42931
+ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen29.Component {
42649
42932
  constructor(project, options = {}) {
42650
42933
  super(project);
42651
42934
  this.project = project;
@@ -42657,30 +42940,8 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
42657
42940
  * @default 'primary' (this is the only type supported currently)
42658
42941
  */
42659
42942
  this.awsEnvironmentType = import_utils11.DEPLOYMENT_TARGET_ROLE.PRIMARY;
42660
- this.setupNode = () => {
42661
- return [
42662
- {
42663
- name: "Setup Node",
42664
- uses: `actions/setup-node@${VERSION.SETUP_NODE_ACTION_VERSION}`,
42665
- with: {
42666
- ["node-version"]: VERSION.NODE_WORKFLOWS
42667
- },
42668
- // occasionally this step fails due to internal issues at github
42669
- timeoutMinutes: 1
42670
- }
42671
- ];
42672
- };
42673
- this.setupPnpm = () => {
42674
- return [
42675
- {
42676
- name: "Setup PNPM",
42677
- uses: `pnpm/action-setup@${VERSION.PNPM_ACTION_SETUP_VERSION}`,
42678
- with: {
42679
- version: this.rootProject.pnpmVersion
42680
- }
42681
- }
42682
- ];
42683
- };
42943
+ this.setupNode = () => renderSetupNode();
42944
+ this.setupPnpm = () => renderSetupPnpm(this.rootProject.pnpmVersion);
42684
42945
  /**
42685
42946
  * Build the deterministic GitHub Actions job name for a deploy target.
42686
42947
  *
@@ -42698,45 +42959,43 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
42698
42959
  ].join("-");
42699
42960
  };
42700
42961
  /**
42701
- * Build the deterministic GitHub Actions job name for a target's `cdk diff`
42702
- * job. Mirrors {@link buildJobName} with a `diff` verb so the diff and deploy
42703
- * jobs for one target sort next to each other in the run view.
42962
+ * Build the deterministic GitHub Actions job name for a target's apply job.
42963
+ * Mirrors {@link buildJobName} with an `apply` verb.
42704
42964
  */
42705
- this.buildDiffJobName = (target) => {
42965
+ this.buildApplyJobName = (target) => {
42706
42966
  return [
42707
42967
  target.awsStageType,
42708
42968
  target.deploymentTargetRole,
42709
- "diff",
42969
+ "apply",
42710
42970
  target.project.name,
42711
42971
  target.account,
42712
42972
  target.region
42713
42973
  ].join("-");
42714
42974
  };
42715
42975
  /**
42716
- * Build the deterministic GitHub Actions job name for a target's apply job.
42717
- * Mirrors {@link buildJobName} with an `apply` verb.
42976
+ * Build the deterministic GitHub Actions job name for a target's `cdk diff`
42977
+ * job. Mirrors {@link buildJobName} with a `diff` verb so the diff and deploy
42978
+ * jobs for one target sort next to each other in the run view.
42718
42979
  */
42719
- this.buildApplyJobName = (target) => {
42980
+ this.buildDiffJobName = (target) => {
42720
42981
  return [
42721
42982
  target.awsStageType,
42722
42983
  target.deploymentTargetRole,
42723
- "apply",
42984
+ "diff",
42724
42985
  target.project.name,
42725
42986
  target.account,
42726
42987
  target.region
42727
42988
  ].join("-");
42728
42989
  };
42729
42990
  /**
42730
- * Build the CI artifact name for a target's captured diff.
42991
+ * Build the slug that makes a target unique among every target in the plan
42992
+ * workflow, across every deploy component feeding it.
42731
42993
  *
42732
- * Carries every component that makes a target unique — including
42733
- * `deploymentTargetRole`, which two otherwise-identical targets can differ
42734
- * on. `actions/upload-artifact` v4+ treats artifacts as immutable and rejects
42735
- * a duplicate name within a run, so parallel diff jobs cannot share one.
42994
+ * Includes `deploymentTargetRole`, which two otherwise-identical targets can
42995
+ * differ on.
42736
42996
  */
42737
- this.buildDiffArtifactName = (target) => {
42997
+ this.buildDiffSlug = (target) => {
42738
42998
  return [
42739
- "cdk-diff",
42740
42999
  target.awsStageType,
42741
43000
  target.deploymentTargetRole,
42742
43001
  target.project.name,
@@ -42744,6 +43003,23 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
42744
43003
  target.region
42745
43004
  ].join("-");
42746
43005
  };
43006
+ /**
43007
+ * Build the file a target's captured diff is written to, both in its own diff
43008
+ * job's workspace and in the merged directory the report job assembles.
43009
+ */
43010
+ this.buildDiffOutputFile = (target) => {
43011
+ return `${DIFF_OUTPUT_DIRECTORY}/${this.buildDiffSlug(target)}.txt`;
43012
+ };
43013
+ /**
43014
+ * Build the intermediate artifact name carrying one target's captured diff.
43015
+ *
43016
+ * The diff jobs run in parallel and `actions/upload-artifact` v4+ rejects a
43017
+ * duplicate name within a run, so each target needs its own. The report job
43018
+ * merges them into a single artifact, which is the one meant to be read.
43019
+ */
43020
+ this.buildDiffPartArtifactName = (target) => {
43021
+ return `${DIFF_PART_ARTIFACT_PREFIX}-${this.buildDiffSlug(target)}`;
43022
+ };
42747
43023
  /**
42748
43024
  * Join an apply workflow another `AwsDeployWorkflow` already created, so this
42749
43025
  * component's apply jobs land in that file rather than a second one.
@@ -42801,9 +43077,9 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
42801
43077
  ],
42802
43078
  runsOn: ["ubuntu-latest"],
42803
43079
  permissions: {
42804
- actions: import_workflows_model6.JobPermission.READ,
42805
- contents: import_workflows_model6.JobPermission.READ,
42806
- idToken: import_workflows_model6.JobPermission.WRITE
43080
+ actions: import_workflows_model7.JobPermission.READ,
43081
+ contents: import_workflows_model7.JobPermission.READ,
43082
+ idToken: import_workflows_model7.JobPermission.WRITE
42807
43083
  },
42808
43084
  ...this.environmentName ? { environment: this.environmentName } : void 0,
42809
43085
  /**
@@ -42993,9 +43269,7 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
42993
43269
  const { roleArn, stackPattern } = ciDeploymentConfig ?? {};
42994
43270
  const { rootCdkOut, cdkCli } = awsDeploymentConfig;
42995
43271
  const label = `${awsStageType}/${deploymentTargetRole}/${account}/${region}`;
42996
- const artifactName = this.buildDiffArtifactName(target);
42997
- const capturedFileGuard = `always() && hashFiles('${DIFF_OUTPUT_FILE}') != ''`;
42998
- const runUrl = "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}";
43272
+ const outputFile = this.buildDiffOutputFile(target);
42999
43273
  return [
43000
43274
  ...this.setupPnpm(),
43001
43275
  ...this.setupNode(),
@@ -43015,69 +43289,36 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
43015
43289
  /**
43016
43290
  * Run CDK Diff, capturing the output while leaving it visible in the job
43017
43291
  * log. `pipefail` keeps the CLI's exit code from being masked by `tee`.
43292
+ *
43293
+ * The capture lands under the same path the report job will merge it
43294
+ * back to, so a target's file name is identical in both places.
43018
43295
  */
43019
43296
  {
43020
43297
  name: `Diff ${label}`,
43021
43298
  shell: "bash",
43022
43299
  run: [
43023
43300
  "set -o pipefail",
43301
+ `mkdir -p ${DIFF_OUTPUT_DIRECTORY}`,
43024
43302
  `pnpm dlx "aws-cdk@${cdkCli.cliVersion}" ${renderCdkDiff({
43025
43303
  ...cdkCli.diffOptionsFor(target),
43026
43304
  app: rootCdkOut,
43027
43305
  ci: true,
43028
43306
  noColor: true,
43029
43307
  stackPatterns: stackPattern ? [stackPattern] : void 0
43030
- })} 2>&1 | tee ${DIFF_OUTPUT_FILE}`
43308
+ })} 2>&1 | tee ${outputFile}`
43031
43309
  ].join("\n")
43032
43310
  },
43033
43311
  /**
43034
- * Render the captured diff into the job summary inside a collapsed
43035
- * `<details>` block, truncated below GitHub's 1 MiB per-step cap — a
43036
- * summary that exceeds the cap is dropped in full rather than clipped.
43037
- */
43038
- ...this.diffOptions.summary ? [
43039
- {
43040
- name: `Render diff summary ${label}`,
43041
- if: capturedFileGuard,
43042
- shell: "bash",
43043
- run: [
43044
- `size=$(wc -c < ${DIFF_OUTPUT_FILE})`,
43045
- "{",
43046
- ` echo '## cdk diff \u2014 ${label}'`,
43047
- ' echo ""',
43048
- " echo '<details><summary>cdk diff output</summary>'",
43049
- ' echo ""',
43050
- " echo '```'",
43051
- ` if [ "$size" -gt ${DIFF_SUMMARY_BYTE_LIMIT} ]; then`,
43052
- ` head -c ${DIFF_SUMMARY_BYTE_LIMIT} ${DIFF_OUTPUT_FILE}`,
43053
- ` printf '\\n... truncated at ${DIFF_SUMMARY_BYTE_LIMIT} bytes ...\\n'`,
43054
- " else",
43055
- ` cat ${DIFF_OUTPUT_FILE}`,
43056
- " fi",
43057
- " echo '```'",
43058
- ' echo ""',
43059
- " echo '</details>'",
43060
- ...this.diffOptions.artifact ? [
43061
- ' echo ""',
43062
- ` echo 'Full diff: the \`${artifactName}\` artifact on [this run](${runUrl}).'`
43063
- ] : [],
43064
- '} >> "$GITHUB_STEP_SUMMARY"'
43065
- ].join("\n")
43066
- }
43067
- ] : [],
43068
- /**
43069
- * Upload the untruncated diff as a per-run artifact.
43312
+ * Hand the capture to the report job. Guarded on `!cancelled()` rather
43313
+ * than success, so a diff that exits non-zero — a synth error, or a
43314
+ * consumer who opted into `--fail` still contributes what it captured
43315
+ * instead of leaving a hole in the report.
43070
43316
  */
43071
- ...this.diffOptions.artifact ? [
43072
- import_github7.WorkflowSteps.uploadArtifact({
43073
- name: `Upload diff ${label}`,
43074
- if: capturedFileGuard,
43075
- with: {
43076
- name: artifactName,
43077
- path: DIFF_OUTPUT_FILE
43078
- }
43079
- })
43080
- ] : []
43317
+ renderDiffPartUploadStep({
43318
+ label,
43319
+ artifactName: this.buildDiffPartArtifactName(target),
43320
+ outputFile
43321
+ })
43081
43322
  ];
43082
43323
  };
43083
43324
  if (!(project.root instanceof MonorepoProject)) {
@@ -43086,7 +43327,7 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
43086
43327
  );
43087
43328
  }
43088
43329
  this.rootProject = project.root;
43089
- const github = import_github7.GitHub.of(this.rootProject);
43330
+ const github = import_github8.GitHub.of(this.rootProject);
43090
43331
  if (!github) {
43091
43332
  throw new Error(
43092
43333
  "AwsDeployWorkflow requires a GitHub component in the root project"
@@ -43143,14 +43384,20 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
43143
43384
  buildTask: this.rootProject.buildTask,
43144
43385
  /**
43145
43386
  * Use push triggers based n the branch config for each environment.
43387
+ *
43388
+ * Deduped: targets in one workflow routinely share a branch — two prod
43389
+ * targets both default to `main` — and a repeated entry is only noise
43390
+ * in the generated file, since GitHub treats this as a filter list.
43146
43391
  */
43147
43392
  workflowTriggers: {
43148
43393
  push: {
43149
- branches: [
43150
- ...this.awsDeploymentTargets.flatMap(
43151
- (t) => t.branches.map((b) => b.branch)
43394
+ branches: Array.from(
43395
+ new Set(
43396
+ this.awsDeploymentTargets.flatMap(
43397
+ (t) => t.branches.map((b) => b.branch)
43398
+ )
43152
43399
  )
43153
- ]
43400
+ )
43154
43401
  },
43155
43402
  workflowDispatch: {},
43156
43403
  ...options.buildWorkflowOptions?.workflowTriggers
@@ -43246,16 +43493,16 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
43246
43493
  name: `Deploy ${this.project.name} ${target.awsStageType}/${target.deploymentTargetRole}/${target.account}/${target.region}`,
43247
43494
  needs: [
43248
43495
  "build",
43249
- ...gatedOnDiff ? [this.buildDiffJobName(target)] : [],
43496
+ ...gatedOnDiff ? [DIFF_REPORT_JOB_ID] : [],
43250
43497
  ...this.deployAfterTargets.map((p) => {
43251
43498
  return this.buildJobName(p);
43252
43499
  })
43253
43500
  ],
43254
43501
  runsOn: ["ubuntu-latest"],
43255
43502
  permissions: {
43256
- contents: import_workflows_model6.JobPermission.READ,
43257
- idToken: import_workflows_model6.JobPermission.WRITE,
43258
- pullRequests: import_workflows_model6.JobPermission.WRITE
43503
+ contents: import_workflows_model7.JobPermission.READ,
43504
+ idToken: import_workflows_model7.JobPermission.WRITE,
43505
+ pullRequests: import_workflows_model7.JobPermission.WRITE
43259
43506
  },
43260
43507
  ...this.environmentName ? { environment: this.environmentName } : void 0,
43261
43508
  concurrency: deployJobName,
@@ -43270,14 +43517,28 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
43270
43517
  needs: ["build"],
43271
43518
  runsOn: ["ubuntu-latest"],
43272
43519
  permissions: {
43273
- contents: import_workflows_model6.JobPermission.READ,
43274
- idToken: import_workflows_model6.JobPermission.WRITE
43520
+ contents: import_workflows_model7.JobPermission.READ,
43521
+ idToken: import_workflows_model7.JobPermission.WRITE
43275
43522
  },
43276
43523
  if: jobCondition,
43277
43524
  steps: [...this.diffSteps(target)]
43278
43525
  });
43279
43526
  }
43280
43527
  });
43528
+ if (this.diffOptions.enabled && this.awsDeploymentTargets.length > 0) {
43529
+ DiffReportJob.attach(this.rootProject, {
43530
+ buildWorkflow: this.buildWorkflow,
43531
+ homeRepositoryCondition,
43532
+ summary: this.diffOptions.summary,
43533
+ artifact: this.diffOptions.artifact,
43534
+ targets: this.awsDeploymentTargets.map((target) => ({
43535
+ label: `${this.project.name} ${target.awsStageType}/${target.deploymentTargetRole}/${target.account}/${target.region}`,
43536
+ jobName: this.buildDiffJobName(target),
43537
+ outputFile: this.buildDiffOutputFile(target),
43538
+ branchCondition: this.buildBranchFilterCondition(target.branches)
43539
+ }))
43540
+ });
43541
+ }
43281
43542
  addBuildCompleteJob(this.buildWorkflow);
43282
43543
  }
43283
43544
  static of(project, buildWorkflow) {
@@ -43291,7 +43552,7 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
43291
43552
  name: "Build Sub Projects",
43292
43553
  run: `pnpm exec projen ${ROOT_CI_TASK_NAME}`
43293
43554
  },
43294
- import_github7.WorkflowSteps.uploadArtifact({
43555
+ import_github8.WorkflowSteps.uploadArtifact({
43295
43556
  name: "Upload Turbo runs",
43296
43557
  if: "always()",
43297
43558
  continueOnError: true,
@@ -43307,9 +43568,9 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
43307
43568
  };
43308
43569
 
43309
43570
  // src/workflows/aws-teardown-workflow.ts
43310
- var import_projen29 = require("projen");
43311
- var import_github8 = require("projen/lib/github");
43312
- var import_workflows_model7 = require("projen/lib/github/workflows-model");
43571
+ var import_projen30 = require("projen");
43572
+ var import_github9 = require("projen/lib/github");
43573
+ var import_workflows_model8 = require("projen/lib/github/workflows-model");
43313
43574
  var DEFAULT_TEARDOWN_BRANCH_PATTERNS = [
43314
43575
  "feat/*",
43315
43576
  "fix/*",
@@ -43329,7 +43590,7 @@ var resolveBranchPatterns = (explicit, targets) => {
43329
43590
  }
43330
43591
  return [...DEFAULT_TEARDOWN_BRANCH_PATTERNS];
43331
43592
  };
43332
- var AwsTeardownWorkflow = class extends import_projen29.Component {
43593
+ var AwsTeardownWorkflow = class extends import_projen30.Component {
43333
43594
  constructor(rootProject, options) {
43334
43595
  super(rootProject);
43335
43596
  this.rootProject = rootProject;
@@ -43359,7 +43620,7 @@ var AwsTeardownWorkflow = class extends import_projen29.Component {
43359
43620
  "AwsTeardownWorkflow requires the root project to be a MonorepoProject"
43360
43621
  );
43361
43622
  }
43362
- const github = import_github8.GitHub.of(this.rootProject);
43623
+ const github = import_github9.GitHub.of(this.rootProject);
43363
43624
  if (!github) {
43364
43625
  throw new Error(
43365
43626
  "AwsTeardownWorkflow requires a GitHub component in the root project"
@@ -43373,7 +43634,7 @@ var AwsTeardownWorkflow = class extends import_projen29.Component {
43373
43634
  homeRepository,
43374
43635
  "AwsTeardownWorkflow"
43375
43636
  );
43376
- const workflow = new import_github8.GithubWorkflow(github, "teardown-dev");
43637
+ const workflow = new import_github9.GithubWorkflow(github, "teardown-dev");
43377
43638
  workflow.on({
43378
43639
  workflowDispatch: {},
43379
43640
  schedule: [
@@ -43401,8 +43662,8 @@ var AwsTeardownWorkflow = class extends import_projen29.Component {
43401
43662
  runsOn: ["ubuntu-latest"],
43402
43663
  ...homeRepositoryCondition ? { if: `\${{ ${homeRepositoryCondition} }}` } : {},
43403
43664
  permissions: {
43404
- contents: import_workflows_model7.JobPermission.READ,
43405
- idToken: import_workflows_model7.JobPermission.WRITE
43665
+ contents: import_workflows_model8.JobPermission.READ,
43666
+ idToken: import_workflows_model8.JobPermission.WRITE
43406
43667
  },
43407
43668
  env: {
43408
43669
  REPO: "${{ github.repository }}",
@@ -43517,7 +43778,7 @@ var AwsTeardownWorkflow = class extends import_projen29.Component {
43517
43778
  };
43518
43779
 
43519
43780
  // src/projects/aws-cdk-project.ts
43520
- var AwsCdkProject = class extends import_projen30.awscdk.AwsCdkTypeScriptApp {
43781
+ var AwsCdkProject = class extends import_projen31.awscdk.AwsCdkTypeScriptApp {
43521
43782
  constructor(userOptions) {
43522
43783
  if (!(userOptions.parent instanceof MonorepoProject)) {
43523
43784
  throw new Error(
@@ -43733,7 +43994,7 @@ var AwsCdkProject = class extends import_projen30.awscdk.AwsCdkTypeScriptApp {
43733
43994
  };
43734
43995
 
43735
43996
  // src/projects/react-vite-site-project.ts
43736
- var import_projen31 = require("projen");
43997
+ var import_projen32 = require("projen");
43737
43998
  var import_ts_deepmerge5 = require("ts-deepmerge");
43738
43999
  var ReactViteSiteProject = class extends TypeScriptProject {
43739
44000
  constructor(userOptions) {
@@ -43772,7 +44033,7 @@ var ReactViteSiteProject = class extends TypeScriptProject {
43772
44033
  };
43773
44034
  super(options);
43774
44035
  this.package.addField("type", "module");
43775
- new import_projen31.TextFile(this, ".nvmrc", { lines: ["v24.11.0"] });
44036
+ new import_projen32.TextFile(this, ".nvmrc", { lines: ["v24.11.0"] });
43776
44037
  this.tsconfig?.file.addOverride("compilerOptions.target", "ES2020");
43777
44038
  this.tsconfig?.file.addOverride("compilerOptions.lib", [
43778
44039
  "ES2020",
@@ -43807,7 +44068,7 @@ var ReactViteSiteProject = class extends TypeScriptProject {
43807
44068
  "vitest/globals",
43808
44069
  "vite/client"
43809
44070
  ]);
43810
- new import_projen31.SampleFile(this, "vite.config.ts", {
44071
+ new import_projen32.SampleFile(this, "vite.config.ts", {
43811
44072
  contents: `import { defineConfig } from 'vite';
43812
44073
  import react from '@vitejs/plugin-react';
43813
44074
  import tailwindcss from '@tailwindcss/vite';
@@ -43819,7 +44080,7 @@ export default defineConfig({
43819
44080
  });
43820
44081
  `
43821
44082
  });
43822
- new import_projen31.SampleFile(this, "src/vite-env.d.ts", {
44083
+ new import_projen32.SampleFile(this, "src/vite-env.d.ts", {
43823
44084
  contents: `/// <reference types="vite/client" />
43824
44085
 
43825
44086
  interface ImportMetaEnv {
@@ -43834,7 +44095,7 @@ interface ImportMeta {
43834
44095
  });
43835
44096
  if (options.testRunner !== TestRunner.JEST) {
43836
44097
  this.tryRemoveFile("vitest.config.ts");
43837
- new import_projen31.SampleFile(this, "vitest.config.ts", {
44098
+ new import_projen32.SampleFile(this, "vitest.config.ts", {
43838
44099
  contents: `import { defineConfig, mergeConfig } from 'vitest/config';
43839
44100
  import react from '@vitejs/plugin-react';
43840
44101
  import viteConfig from './vite.config';
@@ -43978,7 +44239,7 @@ export default mergeConfig(
43978
44239
  }
43979
44240
  if (userOptions.sampleCode === true) {
43980
44241
  const siteName = options.name;
43981
- new import_projen31.SampleFile(this, "index.html", {
44242
+ new import_projen32.SampleFile(this, "index.html", {
43982
44243
  contents: `<!doctype html>
43983
44244
  <html>
43984
44245
  <head>
@@ -43993,7 +44254,7 @@ export default mergeConfig(
43993
44254
  </html>
43994
44255
  `
43995
44256
  });
43996
- new import_projen31.SampleFile(this, "src/routes.tsx", {
44257
+ new import_projen32.SampleFile(this, "src/routes.tsx", {
43997
44258
  contents: `import { createBrowserRouter } from 'react-router-dom';
43998
44259
  import App from './App';
43999
44260
 
@@ -44002,7 +44263,7 @@ export const router = createBrowserRouter([
44002
44263
  ]);
44003
44264
  `
44004
44265
  });
44005
- new import_projen31.SampleFile(this, "src/main.tsx", {
44266
+ new import_projen32.SampleFile(this, "src/main.tsx", {
44006
44267
  contents: `import React from 'react';
44007
44268
  import ReactDOM from 'react-dom/client';
44008
44269
  import { RouterProvider } from 'react-router-dom';
@@ -44016,7 +44277,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
44016
44277
  );
44017
44278
  `
44018
44279
  });
44019
- new import_projen31.SampleFile(this, "src/App.tsx", {
44280
+ new import_projen32.SampleFile(this, "src/App.tsx", {
44020
44281
  contents: `export default function App() {
44021
44282
  return (
44022
44283
  <main className="p-4">
@@ -44026,11 +44287,11 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
44026
44287
  }
44027
44288
  `
44028
44289
  });
44029
- new import_projen31.SampleFile(this, "src/index.css", {
44290
+ new import_projen32.SampleFile(this, "src/index.css", {
44030
44291
  contents: `@import "tailwindcss";
44031
44292
  `
44032
44293
  });
44033
- new import_projen31.SampleFile(this, "src/setupTests.ts", {
44294
+ new import_projen32.SampleFile(this, "src/setupTests.ts", {
44034
44295
  contents: `import '@testing-library/jest-dom';
44035
44296
  `
44036
44297
  });
@@ -44039,7 +44300,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
44039
44300
  };
44040
44301
 
44041
44302
  // src/projects/starlight-project.ts
44042
- var import_projen32 = require("projen");
44303
+ var import_projen33 = require("projen");
44043
44304
  var STARLIGHT_ROLE = {
44044
44305
  DOCS: "docs",
44045
44306
  SITE: "site"
@@ -44081,10 +44342,10 @@ var StarlightProject = class extends AstroProject {
44081
44342
  turbo.compileTask.inputs.push("src/content/**");
44082
44343
  }
44083
44344
  if (userOptions.sampleContent === true) {
44084
- new import_projen32.SampleFile(this, "src/content/docs/index.mdx", {
44345
+ new import_projen33.SampleFile(this, "src/content/docs/index.mdx", {
44085
44346
  contents: DEFAULT_INDEX_MDX
44086
44347
  });
44087
- new import_projen32.SampleFile(this, "src/content.config.ts", {
44348
+ new import_projen33.SampleFile(this, "src/content.config.ts", {
44088
44349
  contents: DEFAULT_CONTENT_CONFIG_TS
44089
44350
  });
44090
44351
  }
@@ -44263,7 +44524,12 @@ export const collections = {
44263
44524
  DEFAULT_UNBLOCK_DEPENDENTS_ENABLED,
44264
44525
  DEFAULT_UPSTREAM_CONFIGULATOR_ENABLED,
44265
44526
  DEPLOY_GATE,
44527
+ DIFF_ARTIFACT_NAME,
44528
+ DIFF_OUTPUT_DIRECTORY,
44529
+ DIFF_PART_ARTIFACT_PREFIX,
44530
+ DIFF_REPORT_JOB_ID,
44266
44531
  DOCS_SYNC_AUDIT_SCHEMA_VERSION,
44532
+ DiffReportJob,
44267
44533
  GITHUB_ISSUE_TYPES,
44268
44534
  GITHUB_ISSUE_TYPE_BY_TITLE_PREFIX,
44269
44535
  ISSUE_TEMPLATES_GENERATED_SUFFIX,
@@ -44443,6 +44709,7 @@ export const collections = {
44443
44709
  renderCheckLinksProcedure,
44444
44710
  renderCustomDocSectionBlock,
44445
44711
  renderCustomDocSections,
44712
+ renderDiffPartUploadStep,
44446
44713
  renderExtractApiProcedure,
44447
44714
  renderFocusSection,
44448
44715
  renderGithubIssueTypeSection,
@@ -44474,6 +44741,8 @@ export const collections = {
44474
44741
  renderScopeGateSection,
44475
44742
  renderScopeGateShellHelpers,
44476
44743
  renderSetIssueTypeFallbackLines,
44744
+ renderSetupNode,
44745
+ renderSetupPnpm,
44477
44746
  renderSharedEditingBundleHook,
44478
44747
  renderSharedEditingHelperScript,
44479
44748
  renderSharedEditingRuleContent,