@codedrifters/configulator 0.0.429 → 0.0.431

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,35 @@ 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 BRANCH_NAME_EXPRESSION = "${{ github.head_ref || github.ref_name }}";
42932
+ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen29.Component {
42649
42933
  constructor(project, options = {}) {
42650
42934
  super(project);
42651
42935
  this.project = project;
@@ -42657,30 +42941,8 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
42657
42941
  * @default 'primary' (this is the only type supported currently)
42658
42942
  */
42659
42943
  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
- };
42944
+ this.setupNode = () => renderSetupNode();
42945
+ this.setupPnpm = () => renderSetupPnpm(this.rootProject.pnpmVersion);
42684
42946
  /**
42685
42947
  * Build the deterministic GitHub Actions job name for a deploy target.
42686
42948
  *
@@ -42698,45 +42960,65 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
42698
42960
  ].join("-");
42699
42961
  };
42700
42962
  /**
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.
42963
+ * Build a deploy job's `concurrency` setting from its job name.
42964
+ *
42965
+ * Two shapes, deliberately:
42966
+ *
42967
+ * - Unscoped (the default) stays the bare string the job has always carried,
42968
+ * so an existing consumer's workflow is byte-identical.
42969
+ * - Branch-scoped spells out `cancel-in-progress: false` rather than leaning
42970
+ * on GitHub's default, the same way the apply job does. A group keyed on
42971
+ * the branch reads like a "cancel superseded pushes" group, and that is the
42972
+ * one thing it must not be — cancelling a half-finished `cdk deploy`
42973
+ * strands a CloudFormation stack mid-update.
42704
42974
  */
42705
- this.buildDiffJobName = (target) => {
42975
+ this.buildDeployConcurrency = (deployJobName) => {
42976
+ if (!this.branchScopedConcurrency) {
42977
+ return deployJobName;
42978
+ }
42979
+ return {
42980
+ group: `${deployJobName}-${BRANCH_NAME_EXPRESSION}`,
42981
+ "cancel-in-progress": false
42982
+ };
42983
+ };
42984
+ /**
42985
+ * Build the deterministic GitHub Actions job name for a target's apply job.
42986
+ * Mirrors {@link buildJobName} with an `apply` verb.
42987
+ */
42988
+ this.buildApplyJobName = (target) => {
42706
42989
  return [
42707
42990
  target.awsStageType,
42708
42991
  target.deploymentTargetRole,
42709
- "diff",
42992
+ "apply",
42710
42993
  target.project.name,
42711
42994
  target.account,
42712
42995
  target.region
42713
42996
  ].join("-");
42714
42997
  };
42715
42998
  /**
42716
- * Build the deterministic GitHub Actions job name for a target's apply job.
42717
- * Mirrors {@link buildJobName} with an `apply` verb.
42999
+ * Build the deterministic GitHub Actions job name for a target's `cdk diff`
43000
+ * job. Mirrors {@link buildJobName} with a `diff` verb so the diff and deploy
43001
+ * jobs for one target sort next to each other in the run view.
42718
43002
  */
42719
- this.buildApplyJobName = (target) => {
43003
+ this.buildDiffJobName = (target) => {
42720
43004
  return [
42721
43005
  target.awsStageType,
42722
43006
  target.deploymentTargetRole,
42723
- "apply",
43007
+ "diff",
42724
43008
  target.project.name,
42725
43009
  target.account,
42726
43010
  target.region
42727
43011
  ].join("-");
42728
43012
  };
42729
43013
  /**
42730
- * Build the CI artifact name for a target's captured diff.
43014
+ * Build the slug that makes a target unique among every target in the plan
43015
+ * workflow, across every deploy component feeding it.
42731
43016
  *
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.
43017
+ * Includes `deploymentTargetRole`, which two otherwise-identical targets can
43018
+ * differ on.
42736
43019
  */
42737
- this.buildDiffArtifactName = (target) => {
43020
+ this.buildDiffSlug = (target) => {
42738
43021
  return [
42739
- "cdk-diff",
42740
43022
  target.awsStageType,
42741
43023
  target.deploymentTargetRole,
42742
43024
  target.project.name,
@@ -42744,6 +43026,23 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
42744
43026
  target.region
42745
43027
  ].join("-");
42746
43028
  };
43029
+ /**
43030
+ * Build the file a target's captured diff is written to, both in its own diff
43031
+ * job's workspace and in the merged directory the report job assembles.
43032
+ */
43033
+ this.buildDiffOutputFile = (target) => {
43034
+ return `${DIFF_OUTPUT_DIRECTORY}/${this.buildDiffSlug(target)}.txt`;
43035
+ };
43036
+ /**
43037
+ * Build the intermediate artifact name carrying one target's captured diff.
43038
+ *
43039
+ * The diff jobs run in parallel and `actions/upload-artifact` v4+ rejects a
43040
+ * duplicate name within a run, so each target needs its own. The report job
43041
+ * merges them into a single artifact, which is the one meant to be read.
43042
+ */
43043
+ this.buildDiffPartArtifactName = (target) => {
43044
+ return `${DIFF_PART_ARTIFACT_PREFIX}-${this.buildDiffSlug(target)}`;
43045
+ };
42747
43046
  /**
42748
43047
  * Join an apply workflow another `AwsDeployWorkflow` already created, so this
42749
43048
  * component's apply jobs land in that file rather than a second one.
@@ -42801,9 +43100,9 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
42801
43100
  ],
42802
43101
  runsOn: ["ubuntu-latest"],
42803
43102
  permissions: {
42804
- actions: import_workflows_model6.JobPermission.READ,
42805
- contents: import_workflows_model6.JobPermission.READ,
42806
- idToken: import_workflows_model6.JobPermission.WRITE
43103
+ actions: import_workflows_model7.JobPermission.READ,
43104
+ contents: import_workflows_model7.JobPermission.READ,
43105
+ idToken: import_workflows_model7.JobPermission.WRITE
42807
43106
  },
42808
43107
  ...this.environmentName ? { environment: this.environmentName } : void 0,
42809
43108
  /**
@@ -42811,6 +43110,13 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
42811
43110
  * same-target deploy. `cancel-in-progress` is spelled out rather than
42812
43111
  * left to GitHub's default: cancelling a half-finished `cdk deploy`
42813
43112
  * strands a CloudFormation stack mid-update.
43113
+ *
43114
+ * `branchScopedConcurrency` deliberately does not reach here. An apply is
43115
+ * dispatched from whatever ref the operator happens to be on — unrelated
43116
+ * to the branch the nominated plan ran against, which is the only branch
43117
+ * that describes what is about to be deployed. Keying on the dispatch ref
43118
+ * would hand out separate locks to applies of the same target, so the
43119
+ * unsuffixed group stays.
42814
43120
  */
42815
43121
  concurrency: {
42816
43122
  group: this.buildJobName(target),
@@ -42993,9 +43299,7 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
42993
43299
  const { roleArn, stackPattern } = ciDeploymentConfig ?? {};
42994
43300
  const { rootCdkOut, cdkCli } = awsDeploymentConfig;
42995
43301
  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 }}";
43302
+ const outputFile = this.buildDiffOutputFile(target);
42999
43303
  return [
43000
43304
  ...this.setupPnpm(),
43001
43305
  ...this.setupNode(),
@@ -43015,69 +43319,36 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
43015
43319
  /**
43016
43320
  * Run CDK Diff, capturing the output while leaving it visible in the job
43017
43321
  * log. `pipefail` keeps the CLI's exit code from being masked by `tee`.
43322
+ *
43323
+ * The capture lands under the same path the report job will merge it
43324
+ * back to, so a target's file name is identical in both places.
43018
43325
  */
43019
43326
  {
43020
43327
  name: `Diff ${label}`,
43021
43328
  shell: "bash",
43022
43329
  run: [
43023
43330
  "set -o pipefail",
43331
+ `mkdir -p ${DIFF_OUTPUT_DIRECTORY}`,
43024
43332
  `pnpm dlx "aws-cdk@${cdkCli.cliVersion}" ${renderCdkDiff({
43025
43333
  ...cdkCli.diffOptionsFor(target),
43026
43334
  app: rootCdkOut,
43027
43335
  ci: true,
43028
43336
  noColor: true,
43029
43337
  stackPatterns: stackPattern ? [stackPattern] : void 0
43030
- })} 2>&1 | tee ${DIFF_OUTPUT_FILE}`
43338
+ })} 2>&1 | tee ${outputFile}`
43031
43339
  ].join("\n")
43032
43340
  },
43033
43341
  /**
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.
43342
+ * Hand the capture to the report job. Guarded on `!cancelled()` rather
43343
+ * than success, so a diff that exits non-zero — a synth error, or a
43344
+ * consumer who opted into `--fail` still contributes what it captured
43345
+ * instead of leaving a hole in the report.
43070
43346
  */
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
- ] : []
43347
+ renderDiffPartUploadStep({
43348
+ label,
43349
+ artifactName: this.buildDiffPartArtifactName(target),
43350
+ outputFile
43351
+ })
43081
43352
  ];
43082
43353
  };
43083
43354
  if (!(project.root instanceof MonorepoProject)) {
@@ -43086,7 +43357,7 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
43086
43357
  );
43087
43358
  }
43088
43359
  this.rootProject = project.root;
43089
- const github = import_github7.GitHub.of(this.rootProject);
43360
+ const github = import_github8.GitHub.of(this.rootProject);
43090
43361
  if (!github) {
43091
43362
  throw new Error(
43092
43363
  "AwsDeployWorkflow requires a GitHub component in the root project"
@@ -43111,6 +43382,7 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
43111
43382
  artifact: options.diff?.artifact ?? true,
43112
43383
  summary: options.diff?.summary ?? true
43113
43384
  };
43385
+ this.branchScopedConcurrency = options.branchScopedConcurrency ?? false;
43114
43386
  this.environmentName = resolveEnvironmentGate(
43115
43387
  options.gate,
43116
43388
  options.environmentName,
@@ -43143,14 +43415,20 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
43143
43415
  buildTask: this.rootProject.buildTask,
43144
43416
  /**
43145
43417
  * Use push triggers based n the branch config for each environment.
43418
+ *
43419
+ * Deduped: targets in one workflow routinely share a branch — two prod
43420
+ * targets both default to `main` — and a repeated entry is only noise
43421
+ * in the generated file, since GitHub treats this as a filter list.
43146
43422
  */
43147
43423
  workflowTriggers: {
43148
43424
  push: {
43149
- branches: [
43150
- ...this.awsDeploymentTargets.flatMap(
43151
- (t) => t.branches.map((b) => b.branch)
43425
+ branches: Array.from(
43426
+ new Set(
43427
+ this.awsDeploymentTargets.flatMap(
43428
+ (t) => t.branches.map((b) => b.branch)
43429
+ )
43152
43430
  )
43153
- ]
43431
+ )
43154
43432
  },
43155
43433
  workflowDispatch: {},
43156
43434
  ...options.buildWorkflowOptions?.workflowTriggers
@@ -43168,7 +43446,7 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
43168
43446
  * Set GIT_BRANCH_NAME so Turborepo remote cache hashes match between local and CI.
43169
43447
  */
43170
43448
  env: {
43171
- GIT_BRANCH_NAME: "${{ github.head_ref || github.ref_name }}",
43449
+ GIT_BRANCH_NAME: BRANCH_NAME_EXPRESSION,
43172
43450
  ...options.buildWorkflowOptions?.env,
43173
43451
  ...buildWorkflowOptions?.env
43174
43452
  },
@@ -43246,19 +43524,19 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
43246
43524
  name: `Deploy ${this.project.name} ${target.awsStageType}/${target.deploymentTargetRole}/${target.account}/${target.region}`,
43247
43525
  needs: [
43248
43526
  "build",
43249
- ...gatedOnDiff ? [this.buildDiffJobName(target)] : [],
43527
+ ...gatedOnDiff ? [DIFF_REPORT_JOB_ID] : [],
43250
43528
  ...this.deployAfterTargets.map((p) => {
43251
43529
  return this.buildJobName(p);
43252
43530
  })
43253
43531
  ],
43254
43532
  runsOn: ["ubuntu-latest"],
43255
43533
  permissions: {
43256
- contents: import_workflows_model6.JobPermission.READ,
43257
- idToken: import_workflows_model6.JobPermission.WRITE,
43258
- pullRequests: import_workflows_model6.JobPermission.WRITE
43534
+ contents: import_workflows_model7.JobPermission.READ,
43535
+ idToken: import_workflows_model7.JobPermission.WRITE,
43536
+ pullRequests: import_workflows_model7.JobPermission.WRITE
43259
43537
  },
43260
43538
  ...this.environmentName ? { environment: this.environmentName } : void 0,
43261
- concurrency: deployJobName,
43539
+ concurrency: this.buildDeployConcurrency(deployJobName),
43262
43540
  if: jobCondition,
43263
43541
  steps: [...this.deploySteps(target)]
43264
43542
  });
@@ -43270,14 +43548,28 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
43270
43548
  needs: ["build"],
43271
43549
  runsOn: ["ubuntu-latest"],
43272
43550
  permissions: {
43273
- contents: import_workflows_model6.JobPermission.READ,
43274
- idToken: import_workflows_model6.JobPermission.WRITE
43551
+ contents: import_workflows_model7.JobPermission.READ,
43552
+ idToken: import_workflows_model7.JobPermission.WRITE
43275
43553
  },
43276
43554
  if: jobCondition,
43277
43555
  steps: [...this.diffSteps(target)]
43278
43556
  });
43279
43557
  }
43280
43558
  });
43559
+ if (this.diffOptions.enabled && this.awsDeploymentTargets.length > 0) {
43560
+ DiffReportJob.attach(this.rootProject, {
43561
+ buildWorkflow: this.buildWorkflow,
43562
+ homeRepositoryCondition,
43563
+ summary: this.diffOptions.summary,
43564
+ artifact: this.diffOptions.artifact,
43565
+ targets: this.awsDeploymentTargets.map((target) => ({
43566
+ label: `${this.project.name} ${target.awsStageType}/${target.deploymentTargetRole}/${target.account}/${target.region}`,
43567
+ jobName: this.buildDiffJobName(target),
43568
+ outputFile: this.buildDiffOutputFile(target),
43569
+ branchCondition: this.buildBranchFilterCondition(target.branches)
43570
+ }))
43571
+ });
43572
+ }
43281
43573
  addBuildCompleteJob(this.buildWorkflow);
43282
43574
  }
43283
43575
  static of(project, buildWorkflow) {
@@ -43291,7 +43583,7 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
43291
43583
  name: "Build Sub Projects",
43292
43584
  run: `pnpm exec projen ${ROOT_CI_TASK_NAME}`
43293
43585
  },
43294
- import_github7.WorkflowSteps.uploadArtifact({
43586
+ import_github8.WorkflowSteps.uploadArtifact({
43295
43587
  name: "Upload Turbo runs",
43296
43588
  if: "always()",
43297
43589
  continueOnError: true,
@@ -43307,9 +43599,9 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen28.Compone
43307
43599
  };
43308
43600
 
43309
43601
  // 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");
43602
+ var import_projen30 = require("projen");
43603
+ var import_github9 = require("projen/lib/github");
43604
+ var import_workflows_model8 = require("projen/lib/github/workflows-model");
43313
43605
  var DEFAULT_TEARDOWN_BRANCH_PATTERNS = [
43314
43606
  "feat/*",
43315
43607
  "fix/*",
@@ -43329,7 +43621,7 @@ var resolveBranchPatterns = (explicit, targets) => {
43329
43621
  }
43330
43622
  return [...DEFAULT_TEARDOWN_BRANCH_PATTERNS];
43331
43623
  };
43332
- var AwsTeardownWorkflow = class extends import_projen29.Component {
43624
+ var AwsTeardownWorkflow = class extends import_projen30.Component {
43333
43625
  constructor(rootProject, options) {
43334
43626
  super(rootProject);
43335
43627
  this.rootProject = rootProject;
@@ -43359,7 +43651,7 @@ var AwsTeardownWorkflow = class extends import_projen29.Component {
43359
43651
  "AwsTeardownWorkflow requires the root project to be a MonorepoProject"
43360
43652
  );
43361
43653
  }
43362
- const github = import_github8.GitHub.of(this.rootProject);
43654
+ const github = import_github9.GitHub.of(this.rootProject);
43363
43655
  if (!github) {
43364
43656
  throw new Error(
43365
43657
  "AwsTeardownWorkflow requires a GitHub component in the root project"
@@ -43373,7 +43665,7 @@ var AwsTeardownWorkflow = class extends import_projen29.Component {
43373
43665
  homeRepository,
43374
43666
  "AwsTeardownWorkflow"
43375
43667
  );
43376
- const workflow = new import_github8.GithubWorkflow(github, "teardown-dev");
43668
+ const workflow = new import_github9.GithubWorkflow(github, "teardown-dev");
43377
43669
  workflow.on({
43378
43670
  workflowDispatch: {},
43379
43671
  schedule: [
@@ -43401,8 +43693,8 @@ var AwsTeardownWorkflow = class extends import_projen29.Component {
43401
43693
  runsOn: ["ubuntu-latest"],
43402
43694
  ...homeRepositoryCondition ? { if: `\${{ ${homeRepositoryCondition} }}` } : {},
43403
43695
  permissions: {
43404
- contents: import_workflows_model7.JobPermission.READ,
43405
- idToken: import_workflows_model7.JobPermission.WRITE
43696
+ contents: import_workflows_model8.JobPermission.READ,
43697
+ idToken: import_workflows_model8.JobPermission.WRITE
43406
43698
  },
43407
43699
  env: {
43408
43700
  REPO: "${{ github.repository }}",
@@ -43517,7 +43809,7 @@ var AwsTeardownWorkflow = class extends import_projen29.Component {
43517
43809
  };
43518
43810
 
43519
43811
  // src/projects/aws-cdk-project.ts
43520
- var AwsCdkProject = class extends import_projen30.awscdk.AwsCdkTypeScriptApp {
43812
+ var AwsCdkProject = class extends import_projen31.awscdk.AwsCdkTypeScriptApp {
43521
43813
  constructor(userOptions) {
43522
43814
  if (!(userOptions.parent instanceof MonorepoProject)) {
43523
43815
  throw new Error(
@@ -43733,7 +44025,7 @@ var AwsCdkProject = class extends import_projen30.awscdk.AwsCdkTypeScriptApp {
43733
44025
  };
43734
44026
 
43735
44027
  // src/projects/react-vite-site-project.ts
43736
- var import_projen31 = require("projen");
44028
+ var import_projen32 = require("projen");
43737
44029
  var import_ts_deepmerge5 = require("ts-deepmerge");
43738
44030
  var ReactViteSiteProject = class extends TypeScriptProject {
43739
44031
  constructor(userOptions) {
@@ -43772,7 +44064,7 @@ var ReactViteSiteProject = class extends TypeScriptProject {
43772
44064
  };
43773
44065
  super(options);
43774
44066
  this.package.addField("type", "module");
43775
- new import_projen31.TextFile(this, ".nvmrc", { lines: ["v24.11.0"] });
44067
+ new import_projen32.TextFile(this, ".nvmrc", { lines: ["v24.11.0"] });
43776
44068
  this.tsconfig?.file.addOverride("compilerOptions.target", "ES2020");
43777
44069
  this.tsconfig?.file.addOverride("compilerOptions.lib", [
43778
44070
  "ES2020",
@@ -43807,7 +44099,7 @@ var ReactViteSiteProject = class extends TypeScriptProject {
43807
44099
  "vitest/globals",
43808
44100
  "vite/client"
43809
44101
  ]);
43810
- new import_projen31.SampleFile(this, "vite.config.ts", {
44102
+ new import_projen32.SampleFile(this, "vite.config.ts", {
43811
44103
  contents: `import { defineConfig } from 'vite';
43812
44104
  import react from '@vitejs/plugin-react';
43813
44105
  import tailwindcss from '@tailwindcss/vite';
@@ -43819,7 +44111,7 @@ export default defineConfig({
43819
44111
  });
43820
44112
  `
43821
44113
  });
43822
- new import_projen31.SampleFile(this, "src/vite-env.d.ts", {
44114
+ new import_projen32.SampleFile(this, "src/vite-env.d.ts", {
43823
44115
  contents: `/// <reference types="vite/client" />
43824
44116
 
43825
44117
  interface ImportMetaEnv {
@@ -43834,7 +44126,7 @@ interface ImportMeta {
43834
44126
  });
43835
44127
  if (options.testRunner !== TestRunner.JEST) {
43836
44128
  this.tryRemoveFile("vitest.config.ts");
43837
- new import_projen31.SampleFile(this, "vitest.config.ts", {
44129
+ new import_projen32.SampleFile(this, "vitest.config.ts", {
43838
44130
  contents: `import { defineConfig, mergeConfig } from 'vitest/config';
43839
44131
  import react from '@vitejs/plugin-react';
43840
44132
  import viteConfig from './vite.config';
@@ -43978,7 +44270,7 @@ export default mergeConfig(
43978
44270
  }
43979
44271
  if (userOptions.sampleCode === true) {
43980
44272
  const siteName = options.name;
43981
- new import_projen31.SampleFile(this, "index.html", {
44273
+ new import_projen32.SampleFile(this, "index.html", {
43982
44274
  contents: `<!doctype html>
43983
44275
  <html>
43984
44276
  <head>
@@ -43993,7 +44285,7 @@ export default mergeConfig(
43993
44285
  </html>
43994
44286
  `
43995
44287
  });
43996
- new import_projen31.SampleFile(this, "src/routes.tsx", {
44288
+ new import_projen32.SampleFile(this, "src/routes.tsx", {
43997
44289
  contents: `import { createBrowserRouter } from 'react-router-dom';
43998
44290
  import App from './App';
43999
44291
 
@@ -44002,7 +44294,7 @@ export const router = createBrowserRouter([
44002
44294
  ]);
44003
44295
  `
44004
44296
  });
44005
- new import_projen31.SampleFile(this, "src/main.tsx", {
44297
+ new import_projen32.SampleFile(this, "src/main.tsx", {
44006
44298
  contents: `import React from 'react';
44007
44299
  import ReactDOM from 'react-dom/client';
44008
44300
  import { RouterProvider } from 'react-router-dom';
@@ -44016,7 +44308,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
44016
44308
  );
44017
44309
  `
44018
44310
  });
44019
- new import_projen31.SampleFile(this, "src/App.tsx", {
44311
+ new import_projen32.SampleFile(this, "src/App.tsx", {
44020
44312
  contents: `export default function App() {
44021
44313
  return (
44022
44314
  <main className="p-4">
@@ -44026,11 +44318,11 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
44026
44318
  }
44027
44319
  `
44028
44320
  });
44029
- new import_projen31.SampleFile(this, "src/index.css", {
44321
+ new import_projen32.SampleFile(this, "src/index.css", {
44030
44322
  contents: `@import "tailwindcss";
44031
44323
  `
44032
44324
  });
44033
- new import_projen31.SampleFile(this, "src/setupTests.ts", {
44325
+ new import_projen32.SampleFile(this, "src/setupTests.ts", {
44034
44326
  contents: `import '@testing-library/jest-dom';
44035
44327
  `
44036
44328
  });
@@ -44039,7 +44331,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
44039
44331
  };
44040
44332
 
44041
44333
  // src/projects/starlight-project.ts
44042
- var import_projen32 = require("projen");
44334
+ var import_projen33 = require("projen");
44043
44335
  var STARLIGHT_ROLE = {
44044
44336
  DOCS: "docs",
44045
44337
  SITE: "site"
@@ -44081,10 +44373,10 @@ var StarlightProject = class extends AstroProject {
44081
44373
  turbo.compileTask.inputs.push("src/content/**");
44082
44374
  }
44083
44375
  if (userOptions.sampleContent === true) {
44084
- new import_projen32.SampleFile(this, "src/content/docs/index.mdx", {
44376
+ new import_projen33.SampleFile(this, "src/content/docs/index.mdx", {
44085
44377
  contents: DEFAULT_INDEX_MDX
44086
44378
  });
44087
- new import_projen32.SampleFile(this, "src/content.config.ts", {
44379
+ new import_projen33.SampleFile(this, "src/content.config.ts", {
44088
44380
  contents: DEFAULT_CONTENT_CONFIG_TS
44089
44381
  });
44090
44382
  }
@@ -44263,7 +44555,12 @@ export const collections = {
44263
44555
  DEFAULT_UNBLOCK_DEPENDENTS_ENABLED,
44264
44556
  DEFAULT_UPSTREAM_CONFIGULATOR_ENABLED,
44265
44557
  DEPLOY_GATE,
44558
+ DIFF_ARTIFACT_NAME,
44559
+ DIFF_OUTPUT_DIRECTORY,
44560
+ DIFF_PART_ARTIFACT_PREFIX,
44561
+ DIFF_REPORT_JOB_ID,
44266
44562
  DOCS_SYNC_AUDIT_SCHEMA_VERSION,
44563
+ DiffReportJob,
44267
44564
  GITHUB_ISSUE_TYPES,
44268
44565
  GITHUB_ISSUE_TYPE_BY_TITLE_PREFIX,
44269
44566
  ISSUE_TEMPLATES_GENERATED_SUFFIX,
@@ -44443,6 +44740,7 @@ export const collections = {
44443
44740
  renderCheckLinksProcedure,
44444
44741
  renderCustomDocSectionBlock,
44445
44742
  renderCustomDocSections,
44743
+ renderDiffPartUploadStep,
44446
44744
  renderExtractApiProcedure,
44447
44745
  renderFocusSection,
44448
44746
  renderGithubIssueTypeSection,
@@ -44474,6 +44772,8 @@ export const collections = {
44474
44772
  renderScopeGateSection,
44475
44773
  renderScopeGateShellHelpers,
44476
44774
  renderSetIssueTypeFallbackLines,
44775
+ renderSetupNode,
44776
+ renderSetupPnpm,
44477
44777
  renderSharedEditingBundleHook,
44478
44778
  renderSharedEditingHelperScript,
44479
44779
  renderSharedEditingRuleContent,