@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.d.mts +402 -20
- package/lib/index.d.ts +403 -21
- package/lib/index.js +686 -257
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +661 -242
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
|
41930
|
+
import { Component as Component25 } from "projen";
|
|
41931
41931
|
import { BuildWorkflow } from "projen/lib/build";
|
|
41932
|
-
import { GitHub as
|
|
41933
|
-
import { JobPermission as
|
|
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/
|
|
41936
|
-
|
|
41937
|
-
|
|
41938
|
-
|
|
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/
|
|
42086
|
-
var
|
|
42087
|
-
|
|
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
|
|
42090
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
42098
|
-
*
|
|
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.
|
|
42101
|
-
|
|
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: "
|
|
42105
|
-
|
|
42288
|
+
WorkflowSteps2.downloadArtifact({
|
|
42289
|
+
name: "Download target diffs",
|
|
42290
|
+
continueOnError: true,
|
|
42106
42291
|
with: {
|
|
42107
|
-
|
|
42108
|
-
|
|
42109
|
-
|
|
42110
|
-
|
|
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
|
-
|
|
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: "
|
|
42118
|
-
|
|
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
|
-
|
|
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
|
|
42143
|
-
*
|
|
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.
|
|
42585
|
+
this.buildApplyJobName = (target) => {
|
|
42147
42586
|
return [
|
|
42148
42587
|
target.awsStageType,
|
|
42149
42588
|
target.deploymentTargetRole,
|
|
42150
|
-
"
|
|
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
|
|
42158
|
-
* Mirrors {@link buildJobName} with
|
|
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.
|
|
42600
|
+
this.buildDiffJobName = (target) => {
|
|
42161
42601
|
return [
|
|
42162
42602
|
target.awsStageType,
|
|
42163
42603
|
target.deploymentTargetRole,
|
|
42164
|
-
"
|
|
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
|
|
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
|
-
*
|
|
42174
|
-
*
|
|
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.
|
|
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
|
-
*
|
|
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
|
|
42192
|
-
*
|
|
42193
|
-
*
|
|
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.
|
|
42198
|
-
|
|
42199
|
-
|
|
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
|
|
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
|
-
|
|
42205
|
-
|
|
42206
|
-
|
|
42207
|
-
|
|
42208
|
-
|
|
42209
|
-
|
|
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
|
|
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:
|
|
42301
|
-
contents:
|
|
42302
|
-
idToken:
|
|
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
|
|
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 ${
|
|
42928
|
+
})} 2>&1 | tee ${outputFile}`
|
|
42527
42929
|
].join("\n")
|
|
42528
42930
|
},
|
|
42529
42931
|
/**
|
|
42530
|
-
*
|
|
42531
|
-
*
|
|
42532
|
-
*
|
|
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
|
-
|
|
42535
|
-
|
|
42536
|
-
|
|
42537
|
-
|
|
42538
|
-
|
|
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 =
|
|
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
|
-
|
|
42647
|
-
|
|
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
|
-
|
|
42699
|
-
|
|
42700
|
-
|
|
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 ? [
|
|
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:
|
|
42729
|
-
idToken:
|
|
42730
|
-
pullRequests:
|
|
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:
|
|
42746
|
-
idToken:
|
|
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
|
-
|
|
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
|
|
42783
|
-
import { GitHub as
|
|
42784
|
-
import { JobPermission as
|
|
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
|
|
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 =
|
|
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
|
|
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:
|
|
42877
|
-
idToken:
|
|
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,
|