canopycms-cdk 0.0.66-int.82 → 0.0.66-int.84
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/dist/constructs/asset-support.d.ts +116 -13
- package/dist/constructs/asset-support.js +158 -4
- package/dist/constructs/cms-distribution.d.ts +40 -0
- package/dist/constructs/cms-distribution.js +123 -4
- package/dist/constructs/cms-service.d.ts +50 -1
- package/dist/constructs/cms-service.js +134 -4
- package/package.json +4 -4
- package/worker/dist/index.js +2820 -932
|
@@ -107,16 +107,54 @@ export interface AssetSupportProps {
|
|
|
107
107
|
*/
|
|
108
108
|
readonly transformLogGroupName?: string;
|
|
109
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* The two CloudFront path patterns the asset system's behaviors are keyed
|
|
112
|
+
* under, derived from `PREFIXES` above (the single source of truth for these
|
|
113
|
+
* prefixes in this file) rather than spelled out again. Exported so
|
|
114
|
+
* `cms-distribution.ts`'s synth-time ordering guard can recognize them
|
|
115
|
+
* without a second, driftable copy of the literal strings.
|
|
116
|
+
*/
|
|
117
|
+
export declare const ASSETS_PATH_PATTERN: string;
|
|
118
|
+
export declare const ASSETS_TRANSFORM_PATH_PATTERN: string;
|
|
119
|
+
/**
|
|
120
|
+
* The literal property names of `AssetCloudFrontBehaviors`
|
|
121
|
+
* (`assets`/`assetsTransform`). It is a natural but broken mistake to spread
|
|
122
|
+
* `assetBehaviors()`'s return value directly into `additionalBehaviors` (a
|
|
123
|
+
* `Record<pathPattern, BehaviorOptions>`) - that type-checks and deploys
|
|
124
|
+
* clean, but synthesizes two CloudFront behaviors matching the literal path
|
|
125
|
+
* patterns `assets` and `assetsTransform`, which nothing ever requests,
|
|
126
|
+
* instead of the real `/assets/*` and `/assets/t/*` patterns. Use
|
|
127
|
+
* `attachTo()` (below) or `CanopyCmsDistribution`'s `assetSupport` prop
|
|
128
|
+
* instead. Exported so `cms-distribution.ts`'s synth-time guard can
|
|
129
|
+
* recognize the mistake and name it in its error.
|
|
130
|
+
*/
|
|
131
|
+
export declare const ASSET_BEHAVIOR_SPREAD_MISTAKE_KEYS: readonly ["assets", "assetsTransform"];
|
|
110
132
|
/**
|
|
111
133
|
* The two CloudFront behavior configs the asset system needs, keyed by the
|
|
112
134
|
* path pattern they belong under. Each value is a full `BehaviorOptions`
|
|
113
|
-
* (origin included)
|
|
135
|
+
* (origin included).
|
|
136
|
+
*
|
|
137
|
+
* Prefer `AssetSupport.attachTo(distribution)` or `CanopyCmsDistribution`'s
|
|
138
|
+
* `assetSupport` prop over consuming this directly - both encode the
|
|
139
|
+
* required attachment order in exactly one place instead of asking every
|
|
140
|
+
* caller to reproduce it correctly. This return value remains useful as an
|
|
141
|
+
* ESCAPE HATCH for a bespoke `new cloudfront.Distribution(...)` assembled
|
|
142
|
+
* entirely inline (its `additionalBehaviors` is fixed at construction, so
|
|
143
|
+
* there is no distribution yet to call `addBehavior` on) - but manual use
|
|
144
|
+
* must still preserve the ordering shown below, and
|
|
145
|
+
* `CanopyCmsDistribution`'s synth-time guard (`mergeBehaviors`) actively
|
|
146
|
+
* rejects three ways this goes wrong: `/assets/*` listed before
|
|
147
|
+
* `/assets/t/*`; the literal keys `assets`/`assetsTransform` (see
|
|
148
|
+
* `ASSET_BEHAVIOR_SPREAD_MISTAKE_KEYS`) from spreading this object directly
|
|
149
|
+
* into a `Record`; or an asset pattern listed here at all while
|
|
150
|
+
* `CanopyCmsDistribution`'s `assetSupport` prop is also passed. See
|
|
151
|
+
* `assertNoAssetBehaviorOrderingHazards`'s doc comment for the full list.
|
|
114
152
|
*
|
|
115
153
|
* ```ts
|
|
116
154
|
* const behaviors = assetSupport.assetBehaviors()
|
|
117
155
|
*
|
|
118
|
-
* // Building a new distribution. CloudFront matches path patterns in
|
|
119
|
-
* // order they're listed and stops at the first match, so the more
|
|
156
|
+
* // Building a new distribution inline. CloudFront matches path patterns in
|
|
157
|
+
* // the order they're listed and stops at the first match, so the more
|
|
120
158
|
* // specific '/assets/t/*' MUST come before '/assets/*' - otherwise the
|
|
121
159
|
* // broader S3-only pattern swallows transform requests first and they
|
|
122
160
|
* // 403 with no Lambda fallback.
|
|
@@ -127,12 +165,6 @@ export interface AssetSupportProps {
|
|
|
127
165
|
* '/assets/*': behaviors.assets,
|
|
128
166
|
* },
|
|
129
167
|
* })
|
|
130
|
-
*
|
|
131
|
-
* // Adding to an existing distribution: the same first-match-wins ordering
|
|
132
|
-
* // applies to the resulting CacheBehaviors list, so call addBehavior for
|
|
133
|
-
* // '/assets/t/*' before '/assets/*' here too.
|
|
134
|
-
* distribution.addBehavior('/assets/t/*', behaviors.assetsTransform.origin, behaviors.assetsTransform)
|
|
135
|
-
* distribution.addBehavior('/assets/*', behaviors.assets.origin, behaviors.assets)
|
|
136
168
|
* ```
|
|
137
169
|
*/
|
|
138
170
|
export interface AssetCloudFrontBehaviors {
|
|
@@ -165,9 +197,11 @@ export interface AssetCloudFrontBehaviors {
|
|
|
165
197
|
* (custom name/retention/removal policy instead of the
|
|
166
198
|
* CloudFormation-implicit `/aws/lambda/<function-name>` group), and its
|
|
167
199
|
* OAC-locked Function URL.
|
|
168
|
-
* - `
|
|
169
|
-
* distribution
|
|
170
|
-
*
|
|
200
|
+
* - `attachTo(distribution)`, which attaches the two CloudFront behaviors a
|
|
201
|
+
* consuming distribution needs in the only safe order (see its doc comment
|
|
202
|
+
* for why the order is the whole point); `assetBehaviors()` is the escape
|
|
203
|
+
* hatch for a distribution built entirely by hand (see
|
|
204
|
+
* `AssetCloudFrontBehaviors`'s doc comment).
|
|
171
205
|
* - `grantUploadAccess()`, the exact prefix-scoped grants the CMS/editor
|
|
172
206
|
* principal needs to run `S3AssetStore` (packages/canopycms/src/assets/store-s3.ts).
|
|
173
207
|
*/
|
|
@@ -185,8 +219,77 @@ export declare class AssetSupport extends Construct {
|
|
|
185
219
|
private readonly behaviors;
|
|
186
220
|
constructor(scope: Construct, id: string, props: AssetSupportProps);
|
|
187
221
|
private buildBehaviors;
|
|
188
|
-
/**
|
|
222
|
+
/**
|
|
223
|
+
* The two CloudFront behavior configs this system needs.
|
|
224
|
+
*
|
|
225
|
+
* If you use this rather than `attachTo` -- which now takes an `overrides`
|
|
226
|
+
* parameter, so needing per-behavior options is no longer a reason to fall
|
|
227
|
+
* back here -- you own the ordering, and you should assert it: read the
|
|
228
|
+
* SYNTHESIZED template's `CacheBehaviors` array index, not your own source
|
|
229
|
+
* object, since the property is about emitted order.
|
|
230
|
+
*
|
|
231
|
+
* Prefer `attachTo(distribution)` or `CanopyCmsDistribution`'s
|
|
232
|
+
* `assetSupport` prop, which attach these to a distribution in the only
|
|
233
|
+
* safe order automatically. This method exists as the escape hatch for a
|
|
234
|
+
* distribution assembled entirely by hand - see `AssetCloudFrontBehaviors`'s
|
|
235
|
+
* doc comment for that shape and its ordering requirements.
|
|
236
|
+
*/
|
|
189
237
|
assetBehaviors(): AssetCloudFrontBehaviors;
|
|
238
|
+
/**
|
|
239
|
+
* Attach both asset behaviors to a concrete CloudFront distribution, in the
|
|
240
|
+
* only safe order.
|
|
241
|
+
*
|
|
242
|
+
* THIS METHOD EXISTS BECAUSE THE ORDER IS THE WHOLE POINT.
|
|
243
|
+
* `assetBehaviors()` returns `{ assets, assetsTransform }` with no path
|
|
244
|
+
* pattern attached at all (see that method's and `AssetCloudFrontBehaviors`'s
|
|
245
|
+
* doc comments) - so nothing stops a caller from attaching the two in
|
|
246
|
+
* either order. CloudFront matches path patterns in the order given and
|
|
247
|
+
* stops at the first match. `/assets/*` is a broader, S3-only pattern that
|
|
248
|
+
* also matches every `/assets/t/*` request; `/assets/t/*` is an origin
|
|
249
|
+
* group that fails over to the transform Lambda on a miss. Attach
|
|
250
|
+
* `/assets/*` first and every never-yet-computed transform gets a
|
|
251
|
+
* permanent 403 (an OAC-signed S3 miss reports 403) while already-computed
|
|
252
|
+
* transforms keep working - silent, launch-delayed, and permanent.
|
|
253
|
+
* `'/assets/*'` also sorts BEFORE `'/assets/t/*'` lexicographically (`*` =
|
|
254
|
+
* 0x2A, `t` = 0x74), so alphabetizing the keys reproduces exactly this
|
|
255
|
+
* failure, with no synth or deploy error to catch it.
|
|
256
|
+
*
|
|
257
|
+
* `overrides` is merged into BOTH behaviors, and exists because without it
|
|
258
|
+
* this method is unusable by exactly the adopters who most need the ordering
|
|
259
|
+
* guarantee. A distribution that runs a viewer-request function on every
|
|
260
|
+
* behavior - tier basic-auth, most commonly - needs the asset behaviors to
|
|
261
|
+
* carry that same `functionAssociations`, or `/assets/*` is anonymously
|
|
262
|
+
* readable on an authenticated tier. Before this parameter existed such an
|
|
263
|
+
* adopter had to fall back to `assetBehaviors()` plus two hand-ordered
|
|
264
|
+
* `addBehavior` calls: the precise shape this method was added to eliminate,
|
|
265
|
+
* re-entered while believing ordering was handled upstream, which is worse
|
|
266
|
+
* than never having had the method. (`responseHeadersPolicy` is the same
|
|
267
|
+
* story for a repo with a shared security-headers policy.)
|
|
268
|
+
*
|
|
269
|
+
* Merged into both rather than per-behavior on purpose: applying one set to
|
|
270
|
+
* both is what preserves the ordering guarantee as the only thing this
|
|
271
|
+
* method decides. A caller who genuinely needs the two behaviors to differ
|
|
272
|
+
* has left this method's remit and should use `assetBehaviors()` - and keep
|
|
273
|
+
* their own ordering assertion.
|
|
274
|
+
*
|
|
275
|
+
* Typed `Partial<AddBehaviorOptions>` because that is exactly what
|
|
276
|
+
* `addBehavior(pattern, origin, behaviorOptions?)` accepts - `origin` is a
|
|
277
|
+
* POSITIONAL argument there, so `BehaviorOptions` (which is
|
|
278
|
+
* `AddBehaviorOptions` plus `origin`) would let a caller pass a key the call
|
|
279
|
+
* silently ignores. Measured, because the first version of this comment
|
|
280
|
+
* claimed the opposite: widening the parameter and passing an `origin`
|
|
281
|
+
* override changes nothing in the emitted template, so this narrowing
|
|
282
|
+
* prevents a confusing no-op rather than a broken origin group.
|
|
283
|
+
*
|
|
284
|
+
* Needs a concrete `cloudfront.Distribution` - `addBehavior` is an instance
|
|
285
|
+
* method on that class, not on `IDistribution` (what an imported/looked-up
|
|
286
|
+
* distribution reference gives you). For a distribution built entirely
|
|
287
|
+
* inline (its `additionalBehaviors` fixed at construction, with no
|
|
288
|
+
* distribution yet to call `addBehavior` on), call `assetBehaviors()`
|
|
289
|
+
* directly instead and list `/assets/t/*` before `/assets/*` yourself - see
|
|
290
|
+
* `AssetCloudFrontBehaviors`'s doc comment.
|
|
291
|
+
*/
|
|
292
|
+
attachTo(distribution: cloudfront.Distribution, overrides?: Partial<cloudfront.AddBehaviorOptions>): void;
|
|
190
293
|
/**
|
|
191
294
|
* Grant the CMS/editor principal (the CMS Lambda's role, typically) the
|
|
192
295
|
* exact permissions `S3AssetStore` (packages/canopycms/src/assets/store-s3.ts)
|
|
@@ -111,6 +111,46 @@ const TRANSFORM_OUTPUT_RETENTION = Duration.days(180);
|
|
|
111
111
|
const TRANSFORM_CACHE_MIN_TTL = Duration.seconds(0);
|
|
112
112
|
const TRANSFORM_CACHE_DEFAULT_TTL = Duration.days(1);
|
|
113
113
|
const TRANSFORM_CACHE_MAX_TTL = Duration.days(365);
|
|
114
|
+
/**
|
|
115
|
+
* Distributions that already carry the asset behaviors, tracked at MODULE level
|
|
116
|
+
* rather than per instance.
|
|
117
|
+
*
|
|
118
|
+
* Per-instance (the first version of this) only caught the same `AssetSupport`
|
|
119
|
+
* attaching twice. Two `AssetSupport` constructs attaching to one distribution
|
|
120
|
+
* slipped through and synthesized
|
|
121
|
+
* ['/assets/t/*','/assets/*','/assets/t/*','/assets/*'] - the identical
|
|
122
|
+
* duplicate-path-pattern deploy failure the guard exists to convert into a
|
|
123
|
+
* synth error. There is no legitimate form of that: both instances attach the
|
|
124
|
+
* same two patterns, so the second is always wrong regardless of which
|
|
125
|
+
* construct owns it.
|
|
126
|
+
*
|
|
127
|
+
* A `WeakSet` keyed on the distribution object, so it holds no reference that
|
|
128
|
+
* would outlive the construct tree and cannot leak across CDK apps in a test
|
|
129
|
+
* process.
|
|
130
|
+
*/
|
|
131
|
+
const distributionsWithAssetBehaviors = new WeakSet();
|
|
132
|
+
/**
|
|
133
|
+
* The two CloudFront path patterns the asset system's behaviors are keyed
|
|
134
|
+
* under, derived from `PREFIXES` above (the single source of truth for these
|
|
135
|
+
* prefixes in this file) rather than spelled out again. Exported so
|
|
136
|
+
* `cms-distribution.ts`'s synth-time ordering guard can recognize them
|
|
137
|
+
* without a second, driftable copy of the literal strings.
|
|
138
|
+
*/
|
|
139
|
+
export const ASSETS_PATH_PATTERN = `/${PREFIXES.public}/*`;
|
|
140
|
+
export const ASSETS_TRANSFORM_PATH_PATTERN = `/${PREFIXES.transform}/*`;
|
|
141
|
+
/**
|
|
142
|
+
* The literal property names of `AssetCloudFrontBehaviors`
|
|
143
|
+
* (`assets`/`assetsTransform`). It is a natural but broken mistake to spread
|
|
144
|
+
* `assetBehaviors()`'s return value directly into `additionalBehaviors` (a
|
|
145
|
+
* `Record<pathPattern, BehaviorOptions>`) - that type-checks and deploys
|
|
146
|
+
* clean, but synthesizes two CloudFront behaviors matching the literal path
|
|
147
|
+
* patterns `assets` and `assetsTransform`, which nothing ever requests,
|
|
148
|
+
* instead of the real `/assets/*` and `/assets/t/*` patterns. Use
|
|
149
|
+
* `attachTo()` (below) or `CanopyCmsDistribution`'s `assetSupport` prop
|
|
150
|
+
* instead. Exported so `cms-distribution.ts`'s synth-time guard can
|
|
151
|
+
* recognize the mistake and name it in its error.
|
|
152
|
+
*/
|
|
153
|
+
export const ASSET_BEHAVIOR_SPREAD_MISTAKE_KEYS = ['assets', 'assetsTransform'];
|
|
114
154
|
/**
|
|
115
155
|
* Per-site CDK construct for the asset/media delivery system (see
|
|
116
156
|
* `.claude/future-tasks/assets-media-system.md` for the full design record).
|
|
@@ -123,9 +163,11 @@ const TRANSFORM_CACHE_MAX_TTL = Duration.days(365);
|
|
|
123
163
|
* (custom name/retention/removal policy instead of the
|
|
124
164
|
* CloudFormation-implicit `/aws/lambda/<function-name>` group), and its
|
|
125
165
|
* OAC-locked Function URL.
|
|
126
|
-
* - `
|
|
127
|
-
* distribution
|
|
128
|
-
*
|
|
166
|
+
* - `attachTo(distribution)`, which attaches the two CloudFront behaviors a
|
|
167
|
+
* consuming distribution needs in the only safe order (see its doc comment
|
|
168
|
+
* for why the order is the whole point); `assetBehaviors()` is the escape
|
|
169
|
+
* hatch for a distribution built entirely by hand (see
|
|
170
|
+
* `AssetCloudFrontBehaviors`'s doc comment).
|
|
129
171
|
* - `grantUploadAccess()`, the exact prefix-scoped grants the CMS/editor
|
|
130
172
|
* principal needs to run `S3AssetStore` (packages/canopycms/src/assets/store-s3.ts).
|
|
131
173
|
*/
|
|
@@ -317,10 +359,122 @@ export class AssetSupport extends Construct {
|
|
|
317
359
|
};
|
|
318
360
|
return { assets, assetsTransform };
|
|
319
361
|
}
|
|
320
|
-
/**
|
|
362
|
+
/**
|
|
363
|
+
* The two CloudFront behavior configs this system needs.
|
|
364
|
+
*
|
|
365
|
+
* If you use this rather than `attachTo` -- which now takes an `overrides`
|
|
366
|
+
* parameter, so needing per-behavior options is no longer a reason to fall
|
|
367
|
+
* back here -- you own the ordering, and you should assert it: read the
|
|
368
|
+
* SYNTHESIZED template's `CacheBehaviors` array index, not your own source
|
|
369
|
+
* object, since the property is about emitted order.
|
|
370
|
+
*
|
|
371
|
+
* Prefer `attachTo(distribution)` or `CanopyCmsDistribution`'s
|
|
372
|
+
* `assetSupport` prop, which attach these to a distribution in the only
|
|
373
|
+
* safe order automatically. This method exists as the escape hatch for a
|
|
374
|
+
* distribution assembled entirely by hand - see `AssetCloudFrontBehaviors`'s
|
|
375
|
+
* doc comment for that shape and its ordering requirements.
|
|
376
|
+
*/
|
|
321
377
|
assetBehaviors() {
|
|
322
378
|
return this.behaviors;
|
|
323
379
|
}
|
|
380
|
+
/**
|
|
381
|
+
* Attach both asset behaviors to a concrete CloudFront distribution, in the
|
|
382
|
+
* only safe order.
|
|
383
|
+
*
|
|
384
|
+
* THIS METHOD EXISTS BECAUSE THE ORDER IS THE WHOLE POINT.
|
|
385
|
+
* `assetBehaviors()` returns `{ assets, assetsTransform }` with no path
|
|
386
|
+
* pattern attached at all (see that method's and `AssetCloudFrontBehaviors`'s
|
|
387
|
+
* doc comments) - so nothing stops a caller from attaching the two in
|
|
388
|
+
* either order. CloudFront matches path patterns in the order given and
|
|
389
|
+
* stops at the first match. `/assets/*` is a broader, S3-only pattern that
|
|
390
|
+
* also matches every `/assets/t/*` request; `/assets/t/*` is an origin
|
|
391
|
+
* group that fails over to the transform Lambda on a miss. Attach
|
|
392
|
+
* `/assets/*` first and every never-yet-computed transform gets a
|
|
393
|
+
* permanent 403 (an OAC-signed S3 miss reports 403) while already-computed
|
|
394
|
+
* transforms keep working - silent, launch-delayed, and permanent.
|
|
395
|
+
* `'/assets/*'` also sorts BEFORE `'/assets/t/*'` lexicographically (`*` =
|
|
396
|
+
* 0x2A, `t` = 0x74), so alphabetizing the keys reproduces exactly this
|
|
397
|
+
* failure, with no synth or deploy error to catch it.
|
|
398
|
+
*
|
|
399
|
+
* `overrides` is merged into BOTH behaviors, and exists because without it
|
|
400
|
+
* this method is unusable by exactly the adopters who most need the ordering
|
|
401
|
+
* guarantee. A distribution that runs a viewer-request function on every
|
|
402
|
+
* behavior - tier basic-auth, most commonly - needs the asset behaviors to
|
|
403
|
+
* carry that same `functionAssociations`, or `/assets/*` is anonymously
|
|
404
|
+
* readable on an authenticated tier. Before this parameter existed such an
|
|
405
|
+
* adopter had to fall back to `assetBehaviors()` plus two hand-ordered
|
|
406
|
+
* `addBehavior` calls: the precise shape this method was added to eliminate,
|
|
407
|
+
* re-entered while believing ordering was handled upstream, which is worse
|
|
408
|
+
* than never having had the method. (`responseHeadersPolicy` is the same
|
|
409
|
+
* story for a repo with a shared security-headers policy.)
|
|
410
|
+
*
|
|
411
|
+
* Merged into both rather than per-behavior on purpose: applying one set to
|
|
412
|
+
* both is what preserves the ordering guarantee as the only thing this
|
|
413
|
+
* method decides. A caller who genuinely needs the two behaviors to differ
|
|
414
|
+
* has left this method's remit and should use `assetBehaviors()` - and keep
|
|
415
|
+
* their own ordering assertion.
|
|
416
|
+
*
|
|
417
|
+
* Typed `Partial<AddBehaviorOptions>` because that is exactly what
|
|
418
|
+
* `addBehavior(pattern, origin, behaviorOptions?)` accepts - `origin` is a
|
|
419
|
+
* POSITIONAL argument there, so `BehaviorOptions` (which is
|
|
420
|
+
* `AddBehaviorOptions` plus `origin`) would let a caller pass a key the call
|
|
421
|
+
* silently ignores. Measured, because the first version of this comment
|
|
422
|
+
* claimed the opposite: widening the parameter and passing an `origin`
|
|
423
|
+
* override changes nothing in the emitted template, so this narrowing
|
|
424
|
+
* prevents a confusing no-op rather than a broken origin group.
|
|
425
|
+
*
|
|
426
|
+
* Needs a concrete `cloudfront.Distribution` - `addBehavior` is an instance
|
|
427
|
+
* method on that class, not on `IDistribution` (what an imported/looked-up
|
|
428
|
+
* distribution reference gives you). For a distribution built entirely
|
|
429
|
+
* inline (its `additionalBehaviors` fixed at construction, with no
|
|
430
|
+
* distribution yet to call `addBehavior` on), call `assetBehaviors()`
|
|
431
|
+
* directly instead and list `/assets/t/*` before `/assets/*` yourself - see
|
|
432
|
+
* `AssetCloudFrontBehaviors`'s doc comment.
|
|
433
|
+
*/
|
|
434
|
+
attachTo(distribution, overrides) {
|
|
435
|
+
// `addBehavior` does not dedupe, and these calls bypass
|
|
436
|
+
// `CanopyCmsDistribution`'s own synth-time guard because they run after
|
|
437
|
+
// that distribution is constructed. So attaching twice -- passing the
|
|
438
|
+
// `assetSupport` prop AND calling this yourself, most likely, since the
|
|
439
|
+
// prop's doc comment describes them as equivalent -- synthesizes
|
|
440
|
+
// ['/assets/t/*','/assets/*','/assets/t/*','/assets/*'] and fails at
|
|
441
|
+
// deploy time when CloudFront rejects the duplicate patterns. Refuse it
|
|
442
|
+
// here instead, where the message can say which two routes collided.
|
|
443
|
+
if (distributionsWithAssetBehaviors.has(distribution)) {
|
|
444
|
+
throw new Error(`AssetSupport: attachTo() was already called for this distribution. Each pattern ` +
|
|
445
|
+
`would be attached twice and CloudFront rejects duplicate path patterns at deploy ` +
|
|
446
|
+
`time. This usually means the distribution was given CanopyCmsDistribution's ` +
|
|
447
|
+
`\`assetSupport\` prop (which calls attachTo for you) as well as an explicit ` +
|
|
448
|
+
`attachTo() call -- keep one.`);
|
|
449
|
+
}
|
|
450
|
+
distributionsWithAssetBehaviors.add(distribution);
|
|
451
|
+
// Drop explicitly-`undefined` keys before merging. A spread copies own
|
|
452
|
+
// enumerable keys INCLUDING ones whose value is undefined, so
|
|
453
|
+
// `{ ...transformRest, ...{ cachePolicy: undefined } }` deletes the
|
|
454
|
+
// construct's choice and lets CDK substitute its own default - which is a
|
|
455
|
+
// DIFFERENT default. Measured: `cachePolicy: undefined` swaps this
|
|
456
|
+
// behavior's custom policy (TRANSFORM_CACHE_MIN_TTL = 0, which exists
|
|
457
|
+
// solely to stop the oversized-output redirect loop documented above) for
|
|
458
|
+
// the managed CACHING_OPTIMIZED and its 1-second min TTL; and
|
|
459
|
+
// `viewerProtocolPolicy: undefined` downgrades BOTH behaviors from
|
|
460
|
+
// redirect-to-https to allow-all, serving assets over plain HTTP.
|
|
461
|
+
//
|
|
462
|
+
// This is not a contrived input - it is what forwarding optional props
|
|
463
|
+
// produces: `attachTo(dist, { cachePolicy: props.maybePolicy })` with the
|
|
464
|
+
// prop unset. It typechecks (every AddBehaviorOptions field is already
|
|
465
|
+
// optional), synthesizes and deploys clean.
|
|
466
|
+
const definedOverrides = Object.fromEntries(Object.entries(overrides ?? {}).filter(([, value]) => value !== undefined));
|
|
467
|
+
const { origin: transformOrigin, ...transformRest } = this.behaviors.assetsTransform;
|
|
468
|
+
const { origin: assetsOrigin, ...assetsRest } = this.behaviors.assets;
|
|
469
|
+
distribution.addBehavior(ASSETS_TRANSFORM_PATH_PATTERN, transformOrigin, {
|
|
470
|
+
...transformRest,
|
|
471
|
+
...definedOverrides,
|
|
472
|
+
});
|
|
473
|
+
distribution.addBehavior(ASSETS_PATH_PATTERN, assetsOrigin, {
|
|
474
|
+
...assetsRest,
|
|
475
|
+
...definedOverrides,
|
|
476
|
+
});
|
|
477
|
+
}
|
|
324
478
|
/**
|
|
325
479
|
* Grant the CMS/editor principal (the CMS Lambda's role, typically) the
|
|
326
480
|
* exact permissions `S3AssetStore` (packages/canopycms/src/assets/store-s3.ts)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Construct } from 'constructs';
|
|
2
2
|
import { Duration, aws_cloudfront as cloudfront, aws_certificatemanager as acm, aws_route53 as route53, aws_lambda as lambda } from 'aws-cdk-lib';
|
|
3
|
+
import type { AssetSupport } from './asset-support.js';
|
|
3
4
|
export interface CanopyCmsDistributionProps {
|
|
4
5
|
/** Lambda Function URL from CanopyCmsService */
|
|
5
6
|
functionUrl: lambda.FunctionUrl;
|
|
@@ -49,8 +50,45 @@ export interface CanopyCmsDistributionProps {
|
|
|
49
50
|
* overridden key takes YOUR position in this object, not the position the
|
|
50
51
|
* default held. (A plain object spread would do the opposite; see
|
|
51
52
|
* `mergeBehaviors`.)
|
|
53
|
+
*
|
|
54
|
+
* Prefer the `assetSupport` prop over wiring `AssetSupport`'s behaviors in
|
|
55
|
+
* here by hand — but if you do it by hand anyway, `mergeBehaviors` throws
|
|
56
|
+
* at synth if it sees any of the three AssetSupport footguns: `/assets/*`
|
|
57
|
+
* listed before `/assets/t/*`; the literal keys `assets`/`assetsTransform`
|
|
58
|
+
* (from spreading `assetBehaviors()`'s return value directly into this
|
|
59
|
+
* object instead of keying it by path pattern); or an asset pattern listed
|
|
60
|
+
* here at all while the `assetSupport` prop is also passed (see that
|
|
61
|
+
* prop's own doc comment). See `assertNoAssetBehaviorOrderingHazards`'s
|
|
62
|
+
* doc comment for the full list.
|
|
52
63
|
*/
|
|
53
64
|
additionalBehaviors?: Record<string, cloudfront.BehaviorOptions>;
|
|
65
|
+
/**
|
|
66
|
+
* Attach `AssetSupport`'s CloudFront behaviors (`/assets/*` and
|
|
67
|
+
* `/assets/t/*`) to the distribution this construct builds, in the only
|
|
68
|
+
* safe order - see `AssetSupport.attachTo()`'s doc comment for why the
|
|
69
|
+
* order matters. Calls `assetSupport.attachTo(distribution)` for you right
|
|
70
|
+
* after construction, with nothing to forget.
|
|
71
|
+
*
|
|
72
|
+
* NOT fully equivalent to calling `attachTo` yourself: this path cannot pass
|
|
73
|
+
* that method's `overrides` argument. If the asset behaviors need
|
|
74
|
+
* per-behavior options - a viewer-request function for tier auth being the
|
|
75
|
+
* motivating case, since without it `/assets/*` is anonymously readable on
|
|
76
|
+
* an authenticated tier - drop this prop and call
|
|
77
|
+
* `assetSupport.attachTo(this.distribution, { ... })` after construction.
|
|
78
|
+
*
|
|
79
|
+
* Prefer this over passing `assetSupport.assetBehaviors()` through
|
|
80
|
+
* `additionalBehaviors` by hand - that stays available as an escape hatch
|
|
81
|
+
* (e.g. for behaviors that are not from `AssetSupport` at all), and this
|
|
82
|
+
* construct's synth-time guard (`mergeBehaviors`) still checks it for the
|
|
83
|
+
* three ways that manual wiring is known to go wrong: see
|
|
84
|
+
* `additionalBehaviors`'s own doc comment.
|
|
85
|
+
*
|
|
86
|
+
* No ordering logic lives in this construct beyond calling this method -
|
|
87
|
+
* ordering is encoded exactly once, in `AssetSupport.attachTo()` itself.
|
|
88
|
+
*
|
|
89
|
+
* @default - no asset behaviors are attached
|
|
90
|
+
*/
|
|
91
|
+
assetSupport?: AssetSupport;
|
|
54
92
|
}
|
|
55
93
|
/**
|
|
56
94
|
* Optional CDK construct for CanopyCMS CloudFront distribution.
|
|
@@ -66,6 +104,8 @@ export interface CanopyCmsDistributionProps {
|
|
|
66
104
|
* - Cache policies: no-cache for /api/* and /edit*, cache /_next/static/*
|
|
67
105
|
* - Viewer-request CloudFront Function setting x-forwarded-host (redirect-URL
|
|
68
106
|
* derivation behind the Host-stripping OAC origin)
|
|
107
|
+
* - `AssetSupport`'s `/assets/*` and `/assets/t/*` behaviors, in the required
|
|
108
|
+
* order, when the `assetSupport` prop is passed
|
|
69
109
|
*/
|
|
70
110
|
export declare class CanopyCmsDistribution extends Construct {
|
|
71
111
|
/** The CloudFront distribution */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Construct } from 'constructs';
|
|
2
2
|
import { Duration, Stack, Token, aws_cloudfront as cloudfront, aws_cloudfront_origins as origins, aws_certificatemanager as acm, aws_route53 as route53, aws_route53_targets as targets, } from 'aws-cdk-lib';
|
|
3
3
|
import { DEFAULT_CMS_LAMBDA_TIMEOUT, MAX_CLOUDFRONT_ORIGIN_READ_TIMEOUT } from './cms-service.js';
|
|
4
|
+
import { ASSETS_PATH_PATTERN, ASSETS_TRANSFORM_PATH_PATTERN, ASSET_BEHAVIOR_SPREAD_MISTAKE_KEYS, } from './asset-support.js';
|
|
4
5
|
/**
|
|
5
6
|
* Merge caller behaviors over this construct's own, preserving THE CALLER'S
|
|
6
7
|
* ordering for every key they supply.
|
|
@@ -16,11 +17,120 @@ import { DEFAULT_CMS_LAMBDA_TIMEOUT, MAX_CLOUDFRONT_ORIGIN_READ_TIMEOUT } from '
|
|
|
16
17
|
* Dropping collided defaults FIRST means the caller's own object contributes
|
|
17
18
|
* its keys in its own order.
|
|
18
19
|
*/
|
|
19
|
-
function mergeBehaviors(defaults, caller) {
|
|
20
|
+
function mergeBehaviors(defaults, caller, attachingAssetSupport) {
|
|
20
21
|
if (!caller)
|
|
21
22
|
return defaults;
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
// Compare NORMALIZED keys here too. CloudFront treats a leading '/' as
|
|
24
|
+
// optional, so a caller passing `'_next/static/*'` did not displace this
|
|
25
|
+
// construct's own `'/_next/static/*'` default and both were emitted -- two
|
|
26
|
+
// behaviors CloudFront reads as the same pattern, which it rejects at
|
|
27
|
+
// deploy. Same reason the hazard checks below normalize.
|
|
28
|
+
const callerPatterns = new Set(Object.keys(caller).map(normalizePathPattern));
|
|
29
|
+
const uncollided = Object.fromEntries(Object.entries(defaults).filter(([pattern]) => !callerPatterns.has(normalizePathPattern(pattern))));
|
|
30
|
+
const normalizedCallerKeys = Object.keys(caller).map(normalizePathPattern);
|
|
31
|
+
const duplicateCallerPattern = normalizedCallerKeys.find((pattern, i) => normalizedCallerKeys.indexOf(pattern) !== i);
|
|
32
|
+
if (duplicateCallerPattern !== undefined) {
|
|
33
|
+
throw new Error(`CanopyCmsDistribution: additionalBehaviors has two keys that CloudFront reads as the ` +
|
|
34
|
+
`same path pattern (${JSON.stringify(duplicateCallerPattern)} with and without its ` +
|
|
35
|
+
`leading '/'). CloudFront rejects duplicate path patterns at deploy time. Keep one ` +
|
|
36
|
+
`spelling.`);
|
|
37
|
+
}
|
|
38
|
+
const merged = { ...uncollided, ...caller };
|
|
39
|
+
assertNoAssetBehaviorOrderingHazards(merged, attachingAssetSupport);
|
|
40
|
+
return merged;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Guards against the three AssetSupport CloudFront-behavior hazards a manually
|
|
44
|
+
* assembled `additionalBehaviors` can introduce (see
|
|
45
|
+
* `AssetSupport.attachTo()`'s doc comment for the full mechanism):
|
|
46
|
+
*
|
|
47
|
+
* 1. The literal keys `assets`/`assetsTransform` present in `merged` mean
|
|
48
|
+
* `assetSupport.assetBehaviors()`'s return value was spread directly into
|
|
49
|
+
* `additionalBehaviors` (a `Record<pathPattern, BehaviorOptions>`) instead
|
|
50
|
+
* of being attached via `assetSupport.attachTo(distribution)` or this
|
|
51
|
+
* construct's `assetSupport` prop. This type-checks and deploys clean,
|
|
52
|
+
* synthesizing two behaviors matching the literal path patterns `assets`
|
|
53
|
+
* and `assetsTransform`, which nothing ever requests.
|
|
54
|
+
* 2. `/assets/*` listed before `/assets/t/*` means CloudFront's first-match-
|
|
55
|
+
* wins ordering serves every transform request off the broader, S3-only
|
|
56
|
+
* `/assets/*` behavior, permanently 403ing any derivative that has not
|
|
57
|
+
* already been computed (an OAC-signed S3 miss reports 403).
|
|
58
|
+
* 3. Either asset pattern present in `merged` WHILE the `assetSupport` prop is
|
|
59
|
+
* also passed means both wiring routes are active at once, so each pattern
|
|
60
|
+
* is attached twice. This is the migration mistake specifically - keeping a
|
|
61
|
+
* hand-wired block while adopting the prop - and hazard 2 cannot catch it,
|
|
62
|
+
* because the hand-written order is usually the correct one.
|
|
63
|
+
*
|
|
64
|
+
* `Object.keys` insertion order is spec-guaranteed for string keys like
|
|
65
|
+
* these (none are integer-like array-index strings), so comparing index
|
|
66
|
+
* order here is sound.
|
|
67
|
+
*
|
|
68
|
+
* SCOPE LIMIT: this protects only callers going through
|
|
69
|
+
* `CanopyCmsDistribution`'s own `additionalBehaviors` merge. It cannot help a
|
|
70
|
+
* bespoke `new cloudfront.Distribution(...)` assembled elsewhere -
|
|
71
|
+
* `AssetSupport.attachTo()` is the answer there.
|
|
72
|
+
*/
|
|
73
|
+
/**
|
|
74
|
+
* CloudFront treats the leading `/` on a path pattern as optional -- `assets/*`
|
|
75
|
+
* and `/assets/*` match exactly the same requests, and AWS's own console and
|
|
76
|
+
* docs frequently show the slash-less spelling. The checks below therefore
|
|
77
|
+
* compare NORMALIZED keys: without this, writing `{ 'assets/*': ..., 'assets/t/*': ... }`
|
|
78
|
+
* walked past all three hazards and synthesized the broad-pattern-first order
|
|
79
|
+
* this guard exists to refuse -- silently, which is the worst of the failure
|
|
80
|
+
* modes here.
|
|
81
|
+
*/
|
|
82
|
+
function normalizePathPattern(pattern) {
|
|
83
|
+
return pattern.startsWith('/') ? pattern.slice(1) : pattern;
|
|
84
|
+
}
|
|
85
|
+
/** See the three-hazard list documented above `normalizePathPattern`. */
|
|
86
|
+
function assertNoAssetBehaviorOrderingHazards(merged, attachingAssetSupport) {
|
|
87
|
+
const keys = Object.keys(merged).map(normalizePathPattern);
|
|
88
|
+
const assetsPattern = normalizePathPattern(ASSETS_PATH_PATTERN);
|
|
89
|
+
const transformPattern = normalizePathPattern(ASSETS_TRANSFORM_PATH_PATTERN);
|
|
90
|
+
const spreadMistakeKeys = ASSET_BEHAVIOR_SPREAD_MISTAKE_KEYS.filter((key) => key in merged);
|
|
91
|
+
if (spreadMistakeKeys.length > 0) {
|
|
92
|
+
throw new Error(`CanopyCmsDistribution: additionalBehaviors has literal key(s) ` +
|
|
93
|
+
`${spreadMistakeKeys.map((k) => JSON.stringify(k)).join(', ')}, which is not a ` +
|
|
94
|
+
`CloudFront path pattern. This usually means assetSupport.assetBehaviors()'s return ` +
|
|
95
|
+
`value was spread directly into additionalBehaviors instead of attached via ` +
|
|
96
|
+
`assetSupport.attachTo(distribution) or this construct's \`assetSupport\` prop - either ` +
|
|
97
|
+
`of which attaches the real '${ASSETS_PATH_PATTERN}' and '${ASSETS_TRANSFORM_PATH_PATTERN}' ` +
|
|
98
|
+
`path patterns in the required order.`);
|
|
99
|
+
}
|
|
100
|
+
// Both halves wired at once. `attachTo` runs AFTER the distribution is
|
|
101
|
+
// constructed, so its `addBehavior` calls never pass through this function -
|
|
102
|
+
// meaning a caller who keeps a hand-wired asset block AND adopts the
|
|
103
|
+
// `assetSupport` prop gets each pattern attached twice. Measured: CDK does
|
|
104
|
+
// not object, and synthesizes CacheBehaviors
|
|
105
|
+
// ['/assets/t/*','/assets/*','/assets/t/*','/assets/*'], which CloudFront
|
|
106
|
+
// then rejects at deploy time for the duplicate path patterns. That is a
|
|
107
|
+
// late failure of exactly the kind this guard exists to convert into a synth
|
|
108
|
+
// one, and it is the most likely mistake during migration: the correct
|
|
109
|
+
// hand-written order does NOT trip the check below, so nothing else would
|
|
110
|
+
// catch it.
|
|
111
|
+
if (attachingAssetSupport) {
|
|
112
|
+
const alsoWiredByHand = [ASSETS_PATH_PATTERN, ASSETS_TRANSFORM_PATH_PATTERN].filter((pattern) => keys.includes(normalizePathPattern(pattern)));
|
|
113
|
+
if (alsoWiredByHand.length > 0) {
|
|
114
|
+
throw new Error(`CanopyCmsDistribution: the \`assetSupport\` prop attaches ` +
|
|
115
|
+
`'${ASSETS_TRANSFORM_PATH_PATTERN}' and '${ASSETS_PATH_PATTERN}' itself, but ` +
|
|
116
|
+
`additionalBehaviors already lists ${alsoWiredByHand.map((p) => `'${p}'`).join(' and ')}. ` +
|
|
117
|
+
`Each pattern would be attached twice and CloudFront rejects duplicate path patterns at ` +
|
|
118
|
+
`deploy time. Remove the asset entries from additionalBehaviors and keep the ` +
|
|
119
|
+
`\`assetSupport\` prop, which also guarantees the required order.`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const assetsIndex = keys.indexOf(assetsPattern);
|
|
123
|
+
const transformIndex = keys.indexOf(transformPattern);
|
|
124
|
+
if (assetsIndex !== -1 && transformIndex !== -1 && assetsIndex < transformIndex) {
|
|
125
|
+
throw new Error(`CanopyCmsDistribution: additionalBehaviors lists '${ASSETS_PATH_PATTERN}' before ` +
|
|
126
|
+
`'${ASSETS_TRANSFORM_PATH_PATTERN}'. CloudFront matches path patterns in the order given ` +
|
|
127
|
+
`and stops at the first match, so every '${ASSETS_TRANSFORM_PATH_PATTERN}' request would ` +
|
|
128
|
+
`be served by the broader, S3-only '${ASSETS_PATH_PATTERN}' behavior and never fail over ` +
|
|
129
|
+
`to the transform Lambda - a permanent 403 on any derivative that has not already been ` +
|
|
130
|
+
`computed. List '${ASSETS_TRANSFORM_PATH_PATTERN}' first, or use ` +
|
|
131
|
+
`assetSupport.attachTo(distribution) / this construct's \`assetSupport\` prop, which get ` +
|
|
132
|
+
`the order right automatically.`);
|
|
133
|
+
}
|
|
24
134
|
}
|
|
25
135
|
/**
|
|
26
136
|
* Optional CDK construct for CanopyCMS CloudFront distribution.
|
|
@@ -36,6 +146,8 @@ function mergeBehaviors(defaults, caller) {
|
|
|
36
146
|
* - Cache policies: no-cache for /api/* and /edit*, cache /_next/static/*
|
|
37
147
|
* - Viewer-request CloudFront Function setting x-forwarded-host (redirect-URL
|
|
38
148
|
* derivation behind the Host-stripping OAC origin)
|
|
149
|
+
* - `AssetSupport`'s `/assets/*` and `/assets/t/*` behaviors, in the required
|
|
150
|
+
* order, when the `assetSupport` prop is passed
|
|
39
151
|
*/
|
|
40
152
|
export class CanopyCmsDistribution extends Construct {
|
|
41
153
|
constructor(scope, id, props) {
|
|
@@ -160,8 +272,15 @@ export class CanopyCmsDistribution extends Construct {
|
|
|
160
272
|
cachePolicy: staticCachePolicy,
|
|
161
273
|
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
|
|
162
274
|
},
|
|
163
|
-
}, props.additionalBehaviors),
|
|
275
|
+
}, props.additionalBehaviors, props.assetSupport !== undefined),
|
|
164
276
|
});
|
|
277
|
+
// Attach AssetSupport's behaviors (if provided), in the only safe order -
|
|
278
|
+
// see AssetSupport.attachTo()'s doc comment. This is the ONLY place this
|
|
279
|
+
// construct deals with the asset behaviors' ordering; the actual ordering
|
|
280
|
+
// logic lives exactly once, inside attachTo() itself. Callers who instead
|
|
281
|
+
// pass `assetSupport.assetBehaviors()` through `additionalBehaviors` by
|
|
282
|
+
// hand are covered by `mergeBehaviors`'s synth-time guard above instead.
|
|
283
|
+
props.assetSupport?.attachTo(this.distribution);
|
|
165
284
|
// ========================================================================
|
|
166
285
|
// DNS Records
|
|
167
286
|
// ========================================================================
|
|
@@ -72,8 +72,57 @@ export interface CanopyCmsServiceProps {
|
|
|
72
72
|
githubTokenSecretArn?: string;
|
|
73
73
|
/** Secrets Manager ARN for the Clerk secret key */
|
|
74
74
|
clerkSecretKeySecretArn?: string;
|
|
75
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* The GitHub repository's default branch name (default: 'main').
|
|
77
|
+
*
|
|
78
|
+
* Interpolated straight into a git ref (`refs/heads/{baseBranch}`) and into
|
|
79
|
+
* the worker's `.env` heredoc, so an invalid value is rejected at synth (see
|
|
80
|
+
* `assertValidGitBranchName` above) instead of deploying an instance that
|
|
81
|
+
* boots successfully and then fails. And fail it will, permanently:
|
|
82
|
+
* `verifyBaseBranchExists`
|
|
83
|
+
* (packages/canopycms/src/worker/cms-worker.ts) throws when the named branch
|
|
84
|
+
* does not exist in the cloned `remote.git`, `start()`'s catch records the
|
|
85
|
+
* fatal error, `worker/index.ts` exits 1, and systemd's `Restart=always`
|
|
86
|
+
* repeats that forever — there is no working worker at all until the value
|
|
87
|
+
* is fixed and the instance replaced, and `rebaseActiveBranches` fetches and
|
|
88
|
+
* rebases against the wrong lineage in the meantime.
|
|
89
|
+
*
|
|
90
|
+
* MUST match the shared repo's `canopycms.config.ts`'s `defaultBaseBranch`
|
|
91
|
+
* (both default to 'main' when unset) — the two are resolved by different
|
|
92
|
+
* processes (this stamps the worker's `.env`; the Lambda reads
|
|
93
|
+
* `config.defaultBaseBranch` at request time) with no automatic
|
|
94
|
+
* reconciliation between them. `infrastructure/lib/cms-stack.ts`, as
|
|
95
|
+
* scaffolded by `canopycms init-deploy aws`, derives this prop FROM that same
|
|
96
|
+
* config file at synth time so the two cannot drift for adopters who deploy
|
|
97
|
+
* through the generated stack; a hand-rolled stack must set this explicitly
|
|
98
|
+
* whenever `defaultBaseBranch` is anything other than 'main'.
|
|
99
|
+
*/
|
|
76
100
|
baseBranch?: string;
|
|
101
|
+
/**
|
|
102
|
+
* Explicit settings-branch name, stamped into the worker's
|
|
103
|
+
* `CANOPYCMS_SETTINGS_BRANCH` environment variable (default: unset, in which
|
|
104
|
+
* case the worker falls through to the same computed name the Lambda uses,
|
|
105
|
+
* `canopycms-settings-<deploymentName>` — see `deploymentName` below).
|
|
106
|
+
*
|
|
107
|
+
* Only set this to mirror an adopter-configured `config.settingsBranch` in
|
|
108
|
+
* `canopycms.config.ts`. Leaving both unset (the default) is safe: Lambda and
|
|
109
|
+
* worker then resolve the SAME computed name independently, with nothing to
|
|
110
|
+
* keep in sync. But if the shared config sets `settingsBranch` explicitly
|
|
111
|
+
* and this prop is left unset, the worker keeps resolving the computed name
|
|
112
|
+
* while the Lambda's `getSettingsBranchName` short-circuits on
|
|
113
|
+
* `config.settingsBranch` — so the two own different branches. The PRIMARY
|
|
114
|
+
* settings-push path still reaches GitHub (its task payload carries the
|
|
115
|
+
* Lambda-resolved name), but the worker's per-cycle backstop push
|
|
116
|
+
* (`pushSettingsBranches`) targets the wrong branch and its "foreign settings
|
|
117
|
+
* branch" [SYNC-M3] warning misfires against the deployment's own branch.
|
|
118
|
+
* Validated at synth like `baseBranch` (see `assertValidGitBranchName`),
|
|
119
|
+
* since this is interpolated into a git ref and the worker's `.env` heredoc
|
|
120
|
+
* too. `infrastructure/lib/cms-stack.ts`, as scaffolded by
|
|
121
|
+
* `canopycms init-deploy aws`, derives this prop from the same
|
|
122
|
+
* `canopycms.config.ts` at synth time, so the two cannot drift for adopters
|
|
123
|
+
* who deploy through the generated stack.
|
|
124
|
+
*/
|
|
125
|
+
settingsBranch?: string;
|
|
77
126
|
/**
|
|
78
127
|
* Deployment name (default: 'prod'). Namespaces the settings branch
|
|
79
128
|
* (`canopycms-settings-{deploymentName}`) so this stack's CMS Lambda/worker
|