@codedrifters/configulator 0.0.424 → 0.0.426
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 +240 -2
- package/lib/index.d.ts +241 -3
- package/lib/index.js +389 -28
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +385 -31
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -195,6 +195,7 @@ __export(index_exports, {
|
|
|
195
195
|
AwsDeploymentConfig: () => AwsDeploymentConfig,
|
|
196
196
|
AwsDeploymentTarget: () => AwsDeploymentTarget,
|
|
197
197
|
AwsTeardownWorkflow: () => AwsTeardownWorkflow,
|
|
198
|
+
BUILD_ARTIFACT_NAME: () => BUILD_ARTIFACT_NAME,
|
|
198
199
|
BUILT_IN_BUNDLES: () => BUILT_IN_BUNDLES,
|
|
199
200
|
BUNDLE_OWNERSHIP: () => BUNDLE_OWNERSHIP,
|
|
200
201
|
CDK_BOOTSTRAP_DEFAULTS_BY_STAGE: () => CDK_BOOTSTRAP_DEFAULTS_BY_STAGE,
|
|
@@ -277,6 +278,7 @@ __export(index_exports, {
|
|
|
277
278
|
DEFAULT_UNBLOCK_COMMENT_TEMPLATE: () => DEFAULT_UNBLOCK_COMMENT_TEMPLATE,
|
|
278
279
|
DEFAULT_UNBLOCK_DEPENDENTS_ENABLED: () => DEFAULT_UNBLOCK_DEPENDENTS_ENABLED,
|
|
279
280
|
DEFAULT_UPSTREAM_CONFIGULATOR_ENABLED: () => DEFAULT_UPSTREAM_CONFIGULATOR_ENABLED,
|
|
281
|
+
DEPLOY_GATE: () => DEPLOY_GATE,
|
|
280
282
|
DOCS_SYNC_AUDIT_SCHEMA_VERSION: () => DOCS_SYNC_AUDIT_SCHEMA_VERSION,
|
|
281
283
|
GITHUB_ISSUE_TYPES: () => GITHUB_ISSUE_TYPES,
|
|
282
284
|
GITHUB_ISSUE_TYPE_BY_TITLE_PREFIX: () => GITHUB_ISSUE_TYPE_BY_TITLE_PREFIX,
|
|
@@ -292,7 +294,9 @@ __export(index_exports, {
|
|
|
292
294
|
MONOREPO_LAYOUT: () => MONOREPO_LAYOUT,
|
|
293
295
|
MonorepoProject: () => MonorepoProject,
|
|
294
296
|
Nvmrc: () => Nvmrc,
|
|
297
|
+
PERMISSION_BACKUP_FILE: () => PERMISSION_BACKUP_FILE,
|
|
295
298
|
PHASE_LABEL_TYPE_MAP: () => PHASE_LABEL_TYPE_MAP,
|
|
299
|
+
PLAN_RUN_ID_INPUT: () => PLAN_RUN_ID_INPUT,
|
|
296
300
|
PROD_DEPLOY_NAME: () => PROD_DEPLOY_NAME,
|
|
297
301
|
PROGRESS_FILES_FORMAT_VALUES: () => PROGRESS_FILES_FORMAT_VALUES,
|
|
298
302
|
PnpmWorkspace: () => PnpmWorkspace,
|
|
@@ -326,6 +330,7 @@ __export(index_exports, {
|
|
|
326
330
|
TypeScriptConfig: () => TypeScriptConfig,
|
|
327
331
|
TypeScriptProject: () => TypeScriptProject,
|
|
328
332
|
UNKNOWN_TYPE_FALLBACK_TIER: () => UNKNOWN_TYPE_FALLBACK_TIER,
|
|
333
|
+
VALIDATE_PLAN_JOB_ID: () => VALIDATE_PLAN_JOB_ID,
|
|
329
334
|
VALID_PRIORITY_VALUES: () => VALID_PRIORITY_VALUES,
|
|
330
335
|
VALID_STATUS_VALUES: () => VALID_STATUS_VALUES,
|
|
331
336
|
VERSION: () => VERSION,
|
|
@@ -470,6 +475,7 @@ __export(index_exports, {
|
|
|
470
475
|
renderNextRequirementIdProcedure: () => renderNextRequirementIdProcedure,
|
|
471
476
|
renderPhaseTypeInvariantSection: () => renderPhaseTypeInvariantSection,
|
|
472
477
|
renderPhaseTypeInvariantShellHelpers: () => renderPhaseTypeInvariantShellHelpers,
|
|
478
|
+
renderPlanValidationScript: () => renderPlanValidationScript,
|
|
473
479
|
renderPriorityRulesSection: () => renderPriorityRulesSection,
|
|
474
480
|
renderProgressFileName: () => renderProgressFileName,
|
|
475
481
|
renderProgressFilePath: () => renderProgressFilePath,
|
|
@@ -507,6 +513,7 @@ __export(index_exports, {
|
|
|
507
513
|
resolveBuildPolicy: () => resolveBuildPolicy,
|
|
508
514
|
resolveBundleAgentTiers: () => resolveBundleAgentTiers,
|
|
509
515
|
resolveDefaultAgentTier: () => resolveDefaultAgentTier,
|
|
516
|
+
resolveEnvironmentGate: () => resolveEnvironmentGate,
|
|
510
517
|
resolveIssueDefaults: () => resolveIssueDefaults,
|
|
511
518
|
resolveIssueTemplates: () => resolveIssueTemplates,
|
|
512
519
|
resolveModelAlias: () => resolveModelAlias,
|
|
@@ -42295,6 +42302,43 @@ var import_build = require("projen/lib/build");
|
|
|
42295
42302
|
var import_github6 = require("projen/lib/github");
|
|
42296
42303
|
var import_workflows_model5 = require("projen/lib/github/workflows-model");
|
|
42297
42304
|
|
|
42305
|
+
// src/workflows/deploy-gate.ts
|
|
42306
|
+
var DEPLOY_GATE = {
|
|
42307
|
+
/**
|
|
42308
|
+
* Hold each deploy job behind a GitHub environment so its protection rules —
|
|
42309
|
+
* required reviewers, wait timers, deployment branch policies — apply before
|
|
42310
|
+
* the job is sent to a runner.
|
|
42311
|
+
*/
|
|
42312
|
+
ENVIRONMENT: "environment",
|
|
42313
|
+
/**
|
|
42314
|
+
* Split the workflow in two. A **plan** workflow builds and diffs but never
|
|
42315
|
+
* deploys; a dispatch-only **apply** workflow deploys the exact cloud
|
|
42316
|
+
* assembly a nominated plan run produced.
|
|
42317
|
+
*
|
|
42318
|
+
* Needs no protection rule, so unlike {@link DEPLOY_GATE.ENVIRONMENT} it
|
|
42319
|
+
* works on every GitHub plan. `environmentName` is optional here and layers
|
|
42320
|
+
* an environment onto the apply jobs.
|
|
42321
|
+
*/
|
|
42322
|
+
PLAN_APPLY: "plan-apply"
|
|
42323
|
+
};
|
|
42324
|
+
var resolveEnvironmentGate = (gate, environmentName, componentName) => {
|
|
42325
|
+
const trimmed = environmentName?.trim();
|
|
42326
|
+
if (gate === void 0) {
|
|
42327
|
+
if (trimmed) {
|
|
42328
|
+
throw new Error(
|
|
42329
|
+
`${componentName} requires \`gate\` to be set when \`environmentName\` is supplied, got "${environmentName}" with no gate`
|
|
42330
|
+
);
|
|
42331
|
+
}
|
|
42332
|
+
return void 0;
|
|
42333
|
+
}
|
|
42334
|
+
if (gate === DEPLOY_GATE.ENVIRONMENT && !trimmed) {
|
|
42335
|
+
throw new Error(
|
|
42336
|
+
`${componentName} requires a non-empty \`environmentName\` when \`gate\` is "${gate}"`
|
|
42337
|
+
);
|
|
42338
|
+
}
|
|
42339
|
+
return trimmed;
|
|
42340
|
+
};
|
|
42341
|
+
|
|
42298
42342
|
// src/workflows/home-repository.ts
|
|
42299
42343
|
var HOME_REPOSITORY_PATTERN = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
|
|
42300
42344
|
var renderHomeRepositoryCondition = (homeRepository, componentName) => {
|
|
@@ -42309,6 +42353,105 @@ var renderHomeRepositoryCondition = (homeRepository, componentName) => {
|
|
|
42309
42353
|
return `github.repository == '${homeRepository}'`;
|
|
42310
42354
|
};
|
|
42311
42355
|
|
|
42356
|
+
// src/workflows/plan-apply.ts
|
|
42357
|
+
var VALIDATE_PLAN_JOB_ID = "validate-plan";
|
|
42358
|
+
var PLAN_RUN_ID_INPUT = "plan_run_id";
|
|
42359
|
+
var BUILD_ARTIFACT_NAME = "build-artifact";
|
|
42360
|
+
var PERMISSION_BACKUP_FILE = "permissions-backup.acl";
|
|
42361
|
+
var renderPlanValidationScript = (options) => {
|
|
42362
|
+
const {
|
|
42363
|
+
planWorkflowPath,
|
|
42364
|
+
allowedBranches,
|
|
42365
|
+
requireCurrentHead,
|
|
42366
|
+
verifyArtifactDigest
|
|
42367
|
+
} = options;
|
|
42368
|
+
return [
|
|
42369
|
+
"const raw = (process.env.PLAN_RUN_ID ?? '').trim();",
|
|
42370
|
+
"const runId = Number(raw);",
|
|
42371
|
+
"if (!/^[0-9]+$/.test(raw) || !Number.isSafeInteger(runId) || runId <= 0) {",
|
|
42372
|
+
' core.setFailed(`plan_run_id must be a positive run id, got "${raw}"`);',
|
|
42373
|
+
" return;",
|
|
42374
|
+
"}",
|
|
42375
|
+
"",
|
|
42376
|
+
"const { owner, repo } = context.repo;",
|
|
42377
|
+
"const { data: run } = await github.rest.actions.getWorkflowRun({",
|
|
42378
|
+
" owner,",
|
|
42379
|
+
" repo,",
|
|
42380
|
+
" run_id: runId,",
|
|
42381
|
+
"});",
|
|
42382
|
+
"",
|
|
42383
|
+
`const expectedPath = ${JSON.stringify(planWorkflowPath)};`,
|
|
42384
|
+
"if (run.path !== expectedPath) {",
|
|
42385
|
+
' core.setFailed(`Run ${runId} belongs to workflow "${run.path}", expected "${expectedPath}".`);',
|
|
42386
|
+
" return;",
|
|
42387
|
+
"}",
|
|
42388
|
+
"",
|
|
42389
|
+
'if (run.status !== "completed" || run.conclusion !== "success") {',
|
|
42390
|
+
' core.setFailed(`Run ${runId} is status "${run.status}" / conclusion "${run.conclusion}", expected a completed successful plan run.`);',
|
|
42391
|
+
" return;",
|
|
42392
|
+
"}",
|
|
42393
|
+
"",
|
|
42394
|
+
`const allowedBranches = ${JSON.stringify(allowedBranches)};`,
|
|
42395
|
+
'const branch = run.head_branch ?? "";',
|
|
42396
|
+
"const branchAllowed = allowedBranches.some((pattern) =>",
|
|
42397
|
+
' pattern.includes("*")',
|
|
42398
|
+
' ? branch.startsWith(pattern.slice(0, pattern.indexOf("*")))',
|
|
42399
|
+
" : branch === pattern,",
|
|
42400
|
+
");",
|
|
42401
|
+
"if (!branchAllowed) {",
|
|
42402
|
+
' core.setFailed(`Run ${runId} ran on branch "${branch}", which no deployment target in this workflow accepts (allowed: ${allowedBranches.join(", ")}).`);',
|
|
42403
|
+
" return;",
|
|
42404
|
+
"}",
|
|
42405
|
+
...requireCurrentHead ? [
|
|
42406
|
+
"",
|
|
42407
|
+
"const { data: liveBranch } = await github.rest.repos.getBranch({",
|
|
42408
|
+
" owner,",
|
|
42409
|
+
" repo,",
|
|
42410
|
+
" branch,",
|
|
42411
|
+
"});",
|
|
42412
|
+
"if (liveBranch.commit.sha !== run.head_sha) {",
|
|
42413
|
+
' core.setFailed(`Run ${runId} planned ${run.head_sha} but "${branch}" now points at ${liveBranch.commit.sha}. Re-run the plan workflow against the current head.`);',
|
|
42414
|
+
" return;",
|
|
42415
|
+
"}"
|
|
42416
|
+
] : [],
|
|
42417
|
+
"",
|
|
42418
|
+
"const artifacts = await github.paginate(",
|
|
42419
|
+
" github.rest.actions.listWorkflowRunArtifacts,",
|
|
42420
|
+
" { owner, repo, run_id: runId, per_page: 100 },",
|
|
42421
|
+
");",
|
|
42422
|
+
`const artifactName = ${JSON.stringify(BUILD_ARTIFACT_NAME)};`,
|
|
42423
|
+
"const artifact = artifacts.find((a) => a.name === artifactName);",
|
|
42424
|
+
"if (!artifact) {",
|
|
42425
|
+
' core.setFailed(`Run ${runId} published no "${artifactName}" artifact. Only a plan run whose build job completed can be applied.`);',
|
|
42426
|
+
" return;",
|
|
42427
|
+
"}",
|
|
42428
|
+
"if (artifact.expired) {",
|
|
42429
|
+
' core.setFailed(`The "${artifactName}" artifact on run ${runId} expired at ${artifact.expires_at}. Artifact retention is this gate\'s approval window \u2014 re-run the plan workflow.`);',
|
|
42430
|
+
" return;",
|
|
42431
|
+
"}",
|
|
42432
|
+
...verifyArtifactDigest ? [
|
|
42433
|
+
"if (!artifact.digest) {",
|
|
42434
|
+
' core.setFailed(`The "${artifactName}" artifact on run ${runId} reports no digest, so it cannot be verified. Disable planApply.verifyArtifactDigest or re-run the plan workflow.`);',
|
|
42435
|
+
" return;",
|
|
42436
|
+
"}",
|
|
42437
|
+
'core.setOutput("artifact_digest", artifact.digest);'
|
|
42438
|
+
] : [],
|
|
42439
|
+
"",
|
|
42440
|
+
'core.setOutput("head_sha", run.head_sha);',
|
|
42441
|
+
'core.setOutput("head_branch", branch);',
|
|
42442
|
+
"await core.summary",
|
|
42443
|
+
" .addHeading(`Applying plan run ${runId}`, 2)",
|
|
42444
|
+
" .addTable([",
|
|
42445
|
+
' [{ data: "Field", header: true }, { data: "Value", header: true }],',
|
|
42446
|
+
' ["Plan run", `<a href="${run.html_url}">${runId}</a>`],',
|
|
42447
|
+
' ["Branch", branch],',
|
|
42448
|
+
' ["Commit", run.head_sha],',
|
|
42449
|
+
' ["Artifact expires", artifact.expires_at ?? "unknown"],',
|
|
42450
|
+
" ])",
|
|
42451
|
+
" .write();"
|
|
42452
|
+
].join("\n");
|
|
42453
|
+
};
|
|
42454
|
+
|
|
42312
42455
|
// src/workflows/aws-deploy-workflow.ts
|
|
42313
42456
|
var PROD_DEPLOY_NAME = "prod-deploy";
|
|
42314
42457
|
var DIFF_OUTPUT_FILE = "cdk-diff.txt";
|
|
@@ -42380,6 +42523,20 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
|
|
|
42380
42523
|
target.region
|
|
42381
42524
|
].join("-");
|
|
42382
42525
|
};
|
|
42526
|
+
/**
|
|
42527
|
+
* Build the deterministic GitHub Actions job name for a target's apply job.
|
|
42528
|
+
* Mirrors {@link buildJobName} with an `apply` verb.
|
|
42529
|
+
*/
|
|
42530
|
+
this.buildApplyJobName = (target) => {
|
|
42531
|
+
return [
|
|
42532
|
+
target.awsStageType,
|
|
42533
|
+
target.deploymentTargetRole,
|
|
42534
|
+
"apply",
|
|
42535
|
+
target.project.name,
|
|
42536
|
+
target.account,
|
|
42537
|
+
target.region
|
|
42538
|
+
].join("-");
|
|
42539
|
+
};
|
|
42383
42540
|
/**
|
|
42384
42541
|
* Build the CI artifact name for a target's captured diff.
|
|
42385
42542
|
*
|
|
@@ -42398,6 +42555,163 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
|
|
|
42398
42555
|
target.region
|
|
42399
42556
|
].join("-");
|
|
42400
42557
|
};
|
|
42558
|
+
/**
|
|
42559
|
+
* Create the dispatch-only apply workflow and its validation job.
|
|
42560
|
+
*
|
|
42561
|
+
* The workflow file name defaults to the plan workflow's name suffixed with
|
|
42562
|
+
* `-apply`. A name already in use throws rather than silently colliding on
|
|
42563
|
+
* the underlying `.github/workflows/<name>.yml` file — the usual cause is two
|
|
42564
|
+
* `AwsDeployWorkflow`s sharing one build workflow, and the fix is an explicit
|
|
42565
|
+
* `planApply.applyWorkflowName`.
|
|
42566
|
+
*/
|
|
42567
|
+
this.createApplyWorkflow = (github, homeRepositoryCondition) => {
|
|
42568
|
+
const { applyWorkflowName, requireCurrentHead, verifyArtifactDigest } = this.planApplyOptions;
|
|
42569
|
+
if (github.tryFindWorkflow(applyWorkflowName)) {
|
|
42570
|
+
throw new Error(
|
|
42571
|
+
`AwsDeployWorkflow cannot create the apply workflow "${applyWorkflowName}" because a workflow with that name already exists. Set \`planApply.applyWorkflowName\` to something unique.`
|
|
42572
|
+
);
|
|
42573
|
+
}
|
|
42574
|
+
const workflow = new import_github6.GithubWorkflow(github, applyWorkflowName);
|
|
42575
|
+
workflow.on({
|
|
42576
|
+
workflowDispatch: {
|
|
42577
|
+
inputs: {
|
|
42578
|
+
[PLAN_RUN_ID_INPUT]: {
|
|
42579
|
+
description: "Run id of the plan workflow run to apply. Its build artifact is deployed verbatim.",
|
|
42580
|
+
required: true,
|
|
42581
|
+
type: "string"
|
|
42582
|
+
}
|
|
42583
|
+
}
|
|
42584
|
+
}
|
|
42585
|
+
});
|
|
42586
|
+
const allowedBranches = Array.from(
|
|
42587
|
+
new Set(
|
|
42588
|
+
this.awsDeploymentTargets.flatMap(
|
|
42589
|
+
(target) => target.branches.map((b) => b.branch)
|
|
42590
|
+
)
|
|
42591
|
+
)
|
|
42592
|
+
);
|
|
42593
|
+
workflow.addJob(VALIDATE_PLAN_JOB_ID, {
|
|
42594
|
+
name: "Validate plan run",
|
|
42595
|
+
runsOn: ["ubuntu-latest"],
|
|
42596
|
+
permissions: {
|
|
42597
|
+
actions: import_workflows_model5.JobPermission.READ,
|
|
42598
|
+
contents: import_workflows_model5.JobPermission.READ
|
|
42599
|
+
},
|
|
42600
|
+
...homeRepositoryCondition ? { if: `\${{ ${homeRepositoryCondition} }}` } : {},
|
|
42601
|
+
outputs: {
|
|
42602
|
+
head_sha: { stepId: "validate_plan_run", outputName: "head_sha" },
|
|
42603
|
+
head_branch: { stepId: "validate_plan_run", outputName: "head_branch" },
|
|
42604
|
+
...verifyArtifactDigest ? {
|
|
42605
|
+
artifact_digest: {
|
|
42606
|
+
stepId: "validate_plan_run",
|
|
42607
|
+
outputName: "artifact_digest"
|
|
42608
|
+
}
|
|
42609
|
+
} : {}
|
|
42610
|
+
},
|
|
42611
|
+
steps: [
|
|
42612
|
+
{
|
|
42613
|
+
name: "Validate plan run",
|
|
42614
|
+
id: "validate_plan_run",
|
|
42615
|
+
uses: "actions/github-script@v9",
|
|
42616
|
+
/**
|
|
42617
|
+
* The dispatch input reaches the script through the environment
|
|
42618
|
+
* rather than a `${{ }}` interpolation, so a crafted run id cannot
|
|
42619
|
+
* inject JavaScript into the validation itself.
|
|
42620
|
+
*/
|
|
42621
|
+
env: {
|
|
42622
|
+
PLAN_RUN_ID: `\${{ inputs.${PLAN_RUN_ID_INPUT} }}`
|
|
42623
|
+
},
|
|
42624
|
+
with: {
|
|
42625
|
+
script: renderPlanValidationScript({
|
|
42626
|
+
planWorkflowPath: `.github/workflows/${this.buildWorkflow.name}.yml`,
|
|
42627
|
+
allowedBranches,
|
|
42628
|
+
requireCurrentHead,
|
|
42629
|
+
verifyArtifactDigest
|
|
42630
|
+
})
|
|
42631
|
+
}
|
|
42632
|
+
}
|
|
42633
|
+
]
|
|
42634
|
+
});
|
|
42635
|
+
return workflow;
|
|
42636
|
+
};
|
|
42637
|
+
/**
|
|
42638
|
+
* Register one target's apply job on the apply workflow.
|
|
42639
|
+
*
|
|
42640
|
+
* Differences from the `single`-gate deploy job, all of them forced by the
|
|
42641
|
+
* apply run having no build job of its own:
|
|
42642
|
+
*
|
|
42643
|
+
* - `needs` starts at the validation job rather than `build`, so a plan run
|
|
42644
|
+
* that fails any check skips every apply job instead of deploying.
|
|
42645
|
+
* - `actions: read` is added, which is what lets `actions/download-artifact`
|
|
42646
|
+
* reach across runs.
|
|
42647
|
+
* - No `pull-requests: write`, and no sticky PR comment step: an apply is
|
|
42648
|
+
* always a `workflow_dispatch`, so the comment could never fire.
|
|
42649
|
+
* - No branch filter on `github.ref`. The ref an operator happens to
|
|
42650
|
+
* dispatch from is unrelated to what was planned; the branch that matters
|
|
42651
|
+
* is the plan run's, and the validation job checks that one.
|
|
42652
|
+
*
|
|
42653
|
+
* When `environmentName` is also supplied, it lands here rather than on the
|
|
42654
|
+
* plan's jobs — a second approver on top of whoever dispatched the apply.
|
|
42655
|
+
* `validate-plan` stays ungated so its verdict is available to read *before*
|
|
42656
|
+
* the approval is given.
|
|
42657
|
+
*/
|
|
42658
|
+
this.addApplyJob = (workflow, target, homeRepositoryCondition) => {
|
|
42659
|
+
const applyJobName = this.buildApplyJobName(target);
|
|
42660
|
+
const { verifyArtifactDigest } = this.planApplyOptions;
|
|
42661
|
+
const artifactsDirectory = this.rootProject.artifactsDirectory;
|
|
42662
|
+
workflow.addJob(applyJobName, {
|
|
42663
|
+
name: `Apply ${this.project.name} ${target.awsStageType}/${target.deploymentTargetRole}/${target.account}/${target.region}`,
|
|
42664
|
+
needs: [
|
|
42665
|
+
VALIDATE_PLAN_JOB_ID,
|
|
42666
|
+
...this.deployAfterTargets.map((p) => this.buildApplyJobName(p))
|
|
42667
|
+
],
|
|
42668
|
+
runsOn: ["ubuntu-latest"],
|
|
42669
|
+
permissions: {
|
|
42670
|
+
actions: import_workflows_model5.JobPermission.READ,
|
|
42671
|
+
contents: import_workflows_model5.JobPermission.READ,
|
|
42672
|
+
idToken: import_workflows_model5.JobPermission.WRITE
|
|
42673
|
+
},
|
|
42674
|
+
...this.environmentName ? { environment: this.environmentName } : void 0,
|
|
42675
|
+
/**
|
|
42676
|
+
* Shares the deploy job's group name so an apply can never overlap a
|
|
42677
|
+
* same-target deploy. `cancel-in-progress` is spelled out rather than
|
|
42678
|
+
* left to GitHub's default: cancelling a half-finished `cdk deploy`
|
|
42679
|
+
* strands a CloudFormation stack mid-update.
|
|
42680
|
+
*/
|
|
42681
|
+
concurrency: {
|
|
42682
|
+
group: this.buildJobName(target),
|
|
42683
|
+
"cancel-in-progress": false
|
|
42684
|
+
},
|
|
42685
|
+
...homeRepositoryCondition ? { if: `\${{ ${homeRepositoryCondition} }}` } : {},
|
|
42686
|
+
steps: [
|
|
42687
|
+
/**
|
|
42688
|
+
* Cross-run download of the plan's assembly. `run-id` plus an explicit
|
|
42689
|
+
* `github-token` is what makes `actions/download-artifact` look outside
|
|
42690
|
+
* the current run; without both it silently searches this run and finds
|
|
42691
|
+
* nothing.
|
|
42692
|
+
*/
|
|
42693
|
+
{
|
|
42694
|
+
name: "Download plan build artifacts",
|
|
42695
|
+
uses: "actions/download-artifact@v8",
|
|
42696
|
+
with: {
|
|
42697
|
+
name: BUILD_ARTIFACT_NAME,
|
|
42698
|
+
path: artifactsDirectory,
|
|
42699
|
+
"run-id": `\${{ inputs.${PLAN_RUN_ID_INPUT} }}`,
|
|
42700
|
+
"github-token": "${{ secrets.GITHUB_TOKEN }}",
|
|
42701
|
+
...verifyArtifactDigest ? {
|
|
42702
|
+
"artifact-digest": `\${{ needs.${VALIDATE_PLAN_JOB_ID}.outputs.artifact_digest }}`
|
|
42703
|
+
} : {}
|
|
42704
|
+
}
|
|
42705
|
+
},
|
|
42706
|
+
{
|
|
42707
|
+
name: "Restore build artifact permissions",
|
|
42708
|
+
continueOnError: true,
|
|
42709
|
+
run: `cd ${artifactsDirectory} && setfacl --restore=${PERMISSION_BACKUP_FILE}`
|
|
42710
|
+
},
|
|
42711
|
+
...this.deploySteps(target, { stickyPrComment: false })
|
|
42712
|
+
]
|
|
42713
|
+
});
|
|
42714
|
+
};
|
|
42401
42715
|
/**
|
|
42402
42716
|
* Builds a GitHub Actions condition string that checks if the current branch
|
|
42403
42717
|
* matches any of the provided branch patterns.
|
|
@@ -42426,7 +42740,15 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
|
|
|
42426
42740
|
}
|
|
42427
42741
|
return conditions.join(" || ");
|
|
42428
42742
|
};
|
|
42429
|
-
|
|
42743
|
+
/**
|
|
42744
|
+
* Steps for a target's deploy job.
|
|
42745
|
+
*
|
|
42746
|
+
* `stickyPrComment` drops the sticky-PR-comment step when false. Apply jobs
|
|
42747
|
+
* pass false: they only ever run on `workflow_dispatch` and so could never
|
|
42748
|
+
* satisfy that step's own `pull_request` guard.
|
|
42749
|
+
*/
|
|
42750
|
+
this.deploySteps = (target, options = {}) => {
|
|
42751
|
+
const { stickyPrComment = true } = options;
|
|
42430
42752
|
const {
|
|
42431
42753
|
awsStageType,
|
|
42432
42754
|
deploymentTargetRole,
|
|
@@ -42505,15 +42827,17 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
|
|
|
42505
42827
|
* comment is keyed by the deploy job name so dev/stage/prod comments
|
|
42506
42828
|
* don't collide on the same PR.
|
|
42507
42829
|
*/
|
|
42508
|
-
|
|
42509
|
-
|
|
42510
|
-
|
|
42511
|
-
|
|
42512
|
-
|
|
42513
|
-
|
|
42514
|
-
|
|
42830
|
+
...stickyPrComment ? [
|
|
42831
|
+
{
|
|
42832
|
+
name: "Sticky PR comment with deploy endpoints",
|
|
42833
|
+
if: "github.event_name == 'pull_request' && hashFiles('deploy-urls.md') != ''",
|
|
42834
|
+
uses: "marocchino/sticky-pull-request-comment@v3",
|
|
42835
|
+
with: {
|
|
42836
|
+
header: deployJobName,
|
|
42837
|
+
path: "deploy-urls.md"
|
|
42838
|
+
}
|
|
42515
42839
|
}
|
|
42516
|
-
|
|
42840
|
+
] : []
|
|
42517
42841
|
];
|
|
42518
42842
|
};
|
|
42519
42843
|
/**
|
|
@@ -42657,11 +42981,23 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
|
|
|
42657
42981
|
(target) => target.awsStageType === this.awsStageType && target.ciDeployment
|
|
42658
42982
|
) ?? [];
|
|
42659
42983
|
this.deployAfterTargets = options.deployAfterTargets ?? [];
|
|
42984
|
+
this.gate = options.gate;
|
|
42985
|
+
const isPlanApply = this.gate === DEPLOY_GATE.PLAN_APPLY;
|
|
42986
|
+
if (isPlanApply && options.diff?.enabled === false) {
|
|
42987
|
+
throw new Error(
|
|
42988
|
+
"AwsDeployWorkflow cannot disable `diff` under the `plan-apply` gate: the diff jobs are what keep the plan workflow publishing the build artifact the apply workflow deploys."
|
|
42989
|
+
);
|
|
42990
|
+
}
|
|
42660
42991
|
this.diffOptions = {
|
|
42661
|
-
enabled: options.diff?.enabled ??
|
|
42992
|
+
enabled: options.diff?.enabled ?? isPlanApply,
|
|
42662
42993
|
artifact: options.diff?.artifact ?? true,
|
|
42663
42994
|
summary: options.diff?.summary ?? true
|
|
42664
42995
|
};
|
|
42996
|
+
this.environmentName = resolveEnvironmentGate(
|
|
42997
|
+
options.gate,
|
|
42998
|
+
options.environmentName,
|
|
42999
|
+
"AwsDeployWorkflow"
|
|
43000
|
+
);
|
|
42665
43001
|
this.homeRepository = options.homeRepository;
|
|
42666
43002
|
const homeRepositoryCondition = renderHomeRepositoryCondition(
|
|
42667
43003
|
this.homeRepository,
|
|
@@ -42739,6 +43075,17 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
|
|
|
42739
43075
|
...buildWorkflowOptions?.preBuildSteps ?? []
|
|
42740
43076
|
]
|
|
42741
43077
|
});
|
|
43078
|
+
this.planApplyOptions = {
|
|
43079
|
+
applyWorkflowName: options.planApply?.applyWorkflowName ?? `${this.buildWorkflow.name}-apply`,
|
|
43080
|
+
requireCurrentHead: options.planApply?.requireCurrentHead ?? false,
|
|
43081
|
+
verifyArtifactDigest: options.planApply?.verifyArtifactDigest ?? false
|
|
43082
|
+
};
|
|
43083
|
+
if (isPlanApply) {
|
|
43084
|
+
this.applyWorkflow = this.createApplyWorkflow(
|
|
43085
|
+
github,
|
|
43086
|
+
homeRepositoryCondition
|
|
43087
|
+
);
|
|
43088
|
+
}
|
|
42742
43089
|
this.awsDeploymentTargets.forEach((target) => {
|
|
42743
43090
|
const deployJobName = this.buildJobName(target);
|
|
42744
43091
|
const branchFilterCondition = this.buildBranchFilterCondition(
|
|
@@ -42749,24 +43096,31 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends import_projen26.Compone
|
|
|
42749
43096
|
...homeRepositoryCondition ? [homeRepositoryCondition] : [],
|
|
42750
43097
|
...branchFilterCondition ? [`(${branchFilterCondition})`] : []
|
|
42751
43098
|
].join(" && ") + " }}";
|
|
42752
|
-
this.
|
|
42753
|
-
|
|
42754
|
-
|
|
42755
|
-
|
|
42756
|
-
|
|
42757
|
-
|
|
42758
|
-
|
|
42759
|
-
|
|
42760
|
-
|
|
42761
|
-
|
|
42762
|
-
|
|
42763
|
-
|
|
42764
|
-
|
|
42765
|
-
|
|
42766
|
-
|
|
42767
|
-
|
|
42768
|
-
|
|
42769
|
-
|
|
43099
|
+
if (this.applyWorkflow) {
|
|
43100
|
+
this.addApplyJob(this.applyWorkflow, target, homeRepositoryCondition);
|
|
43101
|
+
} else {
|
|
43102
|
+
const gatedOnDiff = !!this.environmentName && this.diffOptions.enabled;
|
|
43103
|
+
this.buildWorkflow.addPostBuildJob(deployJobName, {
|
|
43104
|
+
name: `Deploy ${this.project.name} ${target.awsStageType}/${target.deploymentTargetRole}/${target.account}/${target.region}`,
|
|
43105
|
+
needs: [
|
|
43106
|
+
"build",
|
|
43107
|
+
...gatedOnDiff ? [this.buildDiffJobName(target)] : [],
|
|
43108
|
+
...this.deployAfterTargets.map((p) => {
|
|
43109
|
+
return this.buildJobName(p);
|
|
43110
|
+
})
|
|
43111
|
+
],
|
|
43112
|
+
runsOn: ["ubuntu-latest"],
|
|
43113
|
+
permissions: {
|
|
43114
|
+
contents: import_workflows_model5.JobPermission.READ,
|
|
43115
|
+
idToken: import_workflows_model5.JobPermission.WRITE,
|
|
43116
|
+
pullRequests: import_workflows_model5.JobPermission.WRITE
|
|
43117
|
+
},
|
|
43118
|
+
...this.environmentName ? { environment: this.environmentName } : void 0,
|
|
43119
|
+
concurrency: deployJobName,
|
|
43120
|
+
if: jobCondition,
|
|
43121
|
+
steps: [...this.deploySteps(target)]
|
|
43122
|
+
});
|
|
43123
|
+
}
|
|
42770
43124
|
if (this.diffOptions.enabled) {
|
|
42771
43125
|
const diffJobName = this.buildDiffJobName(target);
|
|
42772
43126
|
this.buildWorkflow.addPostBuildJob(diffJobName, {
|
|
@@ -43676,6 +44030,7 @@ export const collections = {
|
|
|
43676
44030
|
AwsDeploymentConfig,
|
|
43677
44031
|
AwsDeploymentTarget,
|
|
43678
44032
|
AwsTeardownWorkflow,
|
|
44033
|
+
BUILD_ARTIFACT_NAME,
|
|
43679
44034
|
BUILT_IN_BUNDLES,
|
|
43680
44035
|
BUNDLE_OWNERSHIP,
|
|
43681
44036
|
CDK_BOOTSTRAP_DEFAULTS_BY_STAGE,
|
|
@@ -43758,6 +44113,7 @@ export const collections = {
|
|
|
43758
44113
|
DEFAULT_UNBLOCK_COMMENT_TEMPLATE,
|
|
43759
44114
|
DEFAULT_UNBLOCK_DEPENDENTS_ENABLED,
|
|
43760
44115
|
DEFAULT_UPSTREAM_CONFIGULATOR_ENABLED,
|
|
44116
|
+
DEPLOY_GATE,
|
|
43761
44117
|
DOCS_SYNC_AUDIT_SCHEMA_VERSION,
|
|
43762
44118
|
GITHUB_ISSUE_TYPES,
|
|
43763
44119
|
GITHUB_ISSUE_TYPE_BY_TITLE_PREFIX,
|
|
@@ -43773,7 +44129,9 @@ export const collections = {
|
|
|
43773
44129
|
MONOREPO_LAYOUT,
|
|
43774
44130
|
MonorepoProject,
|
|
43775
44131
|
Nvmrc,
|
|
44132
|
+
PERMISSION_BACKUP_FILE,
|
|
43776
44133
|
PHASE_LABEL_TYPE_MAP,
|
|
44134
|
+
PLAN_RUN_ID_INPUT,
|
|
43777
44135
|
PROD_DEPLOY_NAME,
|
|
43778
44136
|
PROGRESS_FILES_FORMAT_VALUES,
|
|
43779
44137
|
PnpmWorkspace,
|
|
@@ -43807,6 +44165,7 @@ export const collections = {
|
|
|
43807
44165
|
TypeScriptConfig,
|
|
43808
44166
|
TypeScriptProject,
|
|
43809
44167
|
UNKNOWN_TYPE_FALLBACK_TIER,
|
|
44168
|
+
VALIDATE_PLAN_JOB_ID,
|
|
43810
44169
|
VALID_PRIORITY_VALUES,
|
|
43811
44170
|
VALID_STATUS_VALUES,
|
|
43812
44171
|
VERSION,
|
|
@@ -43951,6 +44310,7 @@ export const collections = {
|
|
|
43951
44310
|
renderNextRequirementIdProcedure,
|
|
43952
44311
|
renderPhaseTypeInvariantSection,
|
|
43953
44312
|
renderPhaseTypeInvariantShellHelpers,
|
|
44313
|
+
renderPlanValidationScript,
|
|
43954
44314
|
renderPriorityRulesSection,
|
|
43955
44315
|
renderProgressFileName,
|
|
43956
44316
|
renderProgressFilePath,
|
|
@@ -43988,6 +44348,7 @@ export const collections = {
|
|
|
43988
44348
|
resolveBuildPolicy,
|
|
43989
44349
|
resolveBundleAgentTiers,
|
|
43990
44350
|
resolveDefaultAgentTier,
|
|
44351
|
+
resolveEnvironmentGate,
|
|
43991
44352
|
resolveIssueDefaults,
|
|
43992
44353
|
resolveIssueTemplates,
|
|
43993
44354
|
resolveModelAlias,
|