canopycms-cdk 0.0.67-int.88 → 0.0.67-int.90
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.
|
@@ -755,8 +755,8 @@ export class AssetSupport extends Construct {
|
|
|
755
755
|
code: lambda.Code.fromAsset(transformAssetDir),
|
|
756
756
|
handler: 'handler.handler',
|
|
757
757
|
// nodejs20.x was deprecated 2026-04-30; CDK's CloudFormation validation
|
|
758
|
-
// now fails synth on it. The esbuild bundle
|
|
759
|
-
//
|
|
758
|
+
// now fails synth on it. The esbuild bundle (lambda/asset-transform/
|
|
759
|
+
// build.mjs) targets node22 to match; move the two together.
|
|
760
760
|
runtime: lambda.Runtime.NODEJS_22_X,
|
|
761
761
|
architecture: lambda.Architecture.ARM_64,
|
|
762
762
|
memorySize: TRANSFORM_LAMBDA_MEMORY_MB,
|
|
@@ -34,11 +34,18 @@ export interface CanopyCmsServiceProps {
|
|
|
34
34
|
/** Lambda reserved concurrency cap (default: 10) */
|
|
35
35
|
reservedConcurrency?: number;
|
|
36
36
|
/**
|
|
37
|
-
* Lambda architecture (default: `Architecture.
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
37
|
+
* Lambda architecture (default: `Architecture.ARM_64`, matching the EC2
|
|
38
|
+
* worker and AssetSupport's transform Lambda).
|
|
39
|
+
*
|
|
40
|
+
* This also decides the image's architecture for
|
|
41
|
+
* `DockerImageCode.fromImageAsset`: the construct always passes a resolved
|
|
42
|
+
* architecture to the function, and CDK derives the Docker build platform
|
|
43
|
+
* from it. So omit `platform` on `fromImageAsset`. An explicit `platform`
|
|
44
|
+
* overrides the derived one, and an image built for the other architecture
|
|
45
|
+
* cannot run on the function: an arm64 image on an x86_64 function fails at
|
|
46
|
+
* invoke with `Runtime.InvalidEntrypoint` (see "Where the image is built" in
|
|
47
|
+
* docs/deploying-to-aws.md). A prebuilt image (`DockerImageCode.fromEcr`) has
|
|
48
|
+
* no build for CDK to steer, so it must already be built for this architecture.
|
|
42
49
|
*/
|
|
43
50
|
architecture?: lambda.Architecture;
|
|
44
51
|
/** EC2 spot max price (default: on-demand rate for t4g.nano) */
|
|
@@ -70,8 +77,114 @@ export interface CanopyCmsServiceProps {
|
|
|
70
77
|
githubRepo: string;
|
|
71
78
|
/** Secrets Manager ARN for the GitHub bot token */
|
|
72
79
|
githubTokenSecretArn?: string;
|
|
80
|
+
/**
|
|
81
|
+
* The key within a JSON secret document at `githubTokenSecretArn`; omit when
|
|
82
|
+
* the secret's whole value is the credential.
|
|
83
|
+
*
|
|
84
|
+
* Stamped into the worker's `CANOPYCMS_GITHUB_TOKEN_SECRET_JSON_FIELD`, which
|
|
85
|
+
* `getSecret` (packages/canopycms-cdk/worker/secrets.ts) reads to pull one
|
|
86
|
+
* field out of the document instead of using the whole string. Omitting it is
|
|
87
|
+
* the default and the common case — a secret holding a bare `ghp_…` needs
|
|
88
|
+
* nothing here.
|
|
89
|
+
*
|
|
90
|
+
* This is NOT the ECS/CloudFormation `arn:…:secret:name-AbCdEf:KEY::`
|
|
91
|
+
* convention. The worker calls the `GetSecretValue` API, which does not parse
|
|
92
|
+
* that suffix; a literal ARN carrying one is refused at synth (see
|
|
93
|
+
* `assertSecretArnHasNoFieldSuffix` below, and the known limit recorded on
|
|
94
|
+
* `SECRET_ARN_WITH_EMPTY_VERSION_TAIL` for the one token spelling that gets
|
|
95
|
+
* through) and the field belongs here instead.
|
|
96
|
+
*/
|
|
97
|
+
githubTokenSecretJsonField?: string;
|
|
98
|
+
/**
|
|
99
|
+
* GitHub App ID, to authenticate the worker as a GitHub App installation
|
|
100
|
+
* instead of as a personal access token.
|
|
101
|
+
*
|
|
102
|
+
* **The token is the default and stays first-class.** Registering a GitHub
|
|
103
|
+
* App under an organisation takes an owner of that organisation (or a GitHub
|
|
104
|
+
* App manager for all its Apps), which many adopters are not, so this is the
|
|
105
|
+
* "if your organisation requires it" option, not a direction of travel.
|
|
106
|
+
* Nothing about
|
|
107
|
+
* `githubTokenSecretArn` is deprecated or warned about.
|
|
108
|
+
*
|
|
109
|
+
* All three App props (`githubAppId`, `githubAppInstallationId`,
|
|
110
|
+
* `githubAppPrivateKeySecretArn`) are set together or not at all, and App
|
|
111
|
+
* auth is mutually exclusive with `githubTokenSecretArn` — both are refused
|
|
112
|
+
* at synth. That mirrors `resolveWorkerGitHubAuth` in core
|
|
113
|
+
* (packages/canopycms/src/worker/github-auth.ts), which refuses the same two
|
|
114
|
+
* shapes at boot; checking here turns a 5-second systemd restart loop into a
|
|
115
|
+
* failed `cdk synth`.
|
|
116
|
+
*
|
|
117
|
+
* Stamped into the worker's `CANOPYCMS_GITHUB_APP_ID`.
|
|
118
|
+
*/
|
|
119
|
+
githubAppId?: string;
|
|
120
|
+
/**
|
|
121
|
+
* The App's installation ID on your repository — NOT the App ID above.
|
|
122
|
+
*
|
|
123
|
+
* An App can be installed on several accounts, and a token is minted per
|
|
124
|
+
* installation, so both numbers are needed. It is the trailing number in the
|
|
125
|
+
* URL of the App's install page under your organisation's settings.
|
|
126
|
+
*
|
|
127
|
+
* Stamped into the worker's `CANOPYCMS_GITHUB_APP_INSTALLATION_ID`.
|
|
128
|
+
*/
|
|
129
|
+
githubAppInstallationId?: string;
|
|
130
|
+
/**
|
|
131
|
+
* Secrets Manager ARN for the App's PEM private key.
|
|
132
|
+
*
|
|
133
|
+
* **ARN-only: there is deliberately no plaintext prop for this key**, unlike
|
|
134
|
+
* every other credential the construct knows about, and the reason is
|
|
135
|
+
* mechanical rather than a matter of taste. Every value the construct puts in
|
|
136
|
+
* the worker's environment is written into a `.env` file that systemd reads
|
|
137
|
+
* as `EnvironmentFile=`, where a newline starts a new variable — so
|
|
138
|
+
* `assertEnvSafe` refuses one, and a PEM is inherently multi-line. A
|
|
139
|
+
* plaintext key could not be delivered to the worker intact by this path at
|
|
140
|
+
* all. Passing the PEM itself here is caught by name at synth (see
|
|
141
|
+
* `assertNotInlinePrivateKey`, and the note there about the generated stack
|
|
142
|
+
* reaching CDK's own ARN complaint first) rather than surfacing as a puzzling
|
|
143
|
+
* "an ARN must not contain a newline".
|
|
144
|
+
*
|
|
145
|
+
* The ARN is unioned into the worker's IAM policy alongside the other secret
|
|
146
|
+
* ARN props; you do not need to repeat it in `secretsArns`.
|
|
147
|
+
*/
|
|
148
|
+
githubAppPrivateKeySecretArn?: string;
|
|
149
|
+
/**
|
|
150
|
+
* The key within a JSON secret document at `githubAppPrivateKeySecretArn`;
|
|
151
|
+
* omit when the secret's whole value is the PEM.
|
|
152
|
+
*
|
|
153
|
+
* Stamped into the worker's
|
|
154
|
+
* `CANOPYCMS_GITHUB_APP_PRIVATE_KEY_SECRET_JSON_FIELD`. See
|
|
155
|
+
* `githubTokenSecretJsonField` above — same mechanism, same non-relationship
|
|
156
|
+
* to the ECS `:KEY::` ARN suffix. This is the case that motivated the JSON
|
|
157
|
+
* field support in the first place: an App private key is exactly the kind of
|
|
158
|
+
* material an organisation keeps inside one credential document per
|
|
159
|
+
* environment.
|
|
160
|
+
*
|
|
161
|
+
* A PEM stored as a JSON string value carries its newlines as `\n` escapes,
|
|
162
|
+
* which `JSON.parse` turns back into real newlines — and the worker
|
|
163
|
+
* additionally runs whatever it reads through
|
|
164
|
+
* `normalizeGitHubAppPrivateKey`, which unescapes and base64-unwraps, so a
|
|
165
|
+
* key mangled by a single-line config field still works.
|
|
166
|
+
*/
|
|
167
|
+
githubAppPrivateKeySecretJsonField?: string;
|
|
73
168
|
/** Secrets Manager ARN for the Clerk secret key */
|
|
74
169
|
clerkSecretKeySecretArn?: string;
|
|
170
|
+
/**
|
|
171
|
+
* The key within a JSON secret document at `clerkSecretKeySecretArn`; omit
|
|
172
|
+
* when the secret's whole value is the credential.
|
|
173
|
+
*
|
|
174
|
+
* Stamped into the worker's `CLERK_SECRET_KEY_SECRET_JSON_FIELD`. See
|
|
175
|
+
* `githubTokenSecretJsonField` above — same mechanism, same non-relationship
|
|
176
|
+
* to the ECS `:KEY::` ARN suffix.
|
|
177
|
+
*
|
|
178
|
+
* Note that this covers `CLERK_SECRET_KEY` only. A Clerk JSON document
|
|
179
|
+
* typically also holds `CLERK_JWT_KEY` and the publishable key, and NEITHER
|
|
180
|
+
* can be sourced from Secrets Manager at all: `CLERK_JWT_KEY` reaches the
|
|
181
|
+
* Lambda as a plain value through `environment`, and the publishable key is a
|
|
182
|
+
* Docker build arg inlined into the client bundle. Both are public material
|
|
183
|
+
* (docs/deploying-to-aws.md, "Security Model"), so that is by design rather
|
|
184
|
+
* than an omission — but it means pointing this prop at your document does
|
|
185
|
+
* not relieve you of supplying those two separately.
|
|
186
|
+
*/
|
|
187
|
+
clerkSecretKeySecretJsonField?: string;
|
|
75
188
|
/**
|
|
76
189
|
* The GitHub repository's default branch name (default: 'main').
|
|
77
190
|
*
|
|
@@ -227,10 +340,10 @@ export interface CanopyCmsServiceProps {
|
|
|
227
340
|
* - Lambda function (Docker image, EFS mount, private subnet, no internet)
|
|
228
341
|
* - Lambda Function URL (for CloudFront origin)
|
|
229
342
|
* - EC2 Worker (t4g.nano spot in ASG, public subnet, EFS mount, systemd) -
|
|
230
|
-
* rolled
|
|
231
|
-
* bundle
|
|
232
|
-
* launch template until the next spot interruption (see
|
|
233
|
-
* below)
|
|
343
|
+
* rolled via the ASG's UpdatePolicy by every deploy that changes its launch
|
|
344
|
+
* template, so a changed worker bundle reaches the instance instead of
|
|
345
|
+
* sitting unused in a launch template until the next spot interruption (see
|
|
346
|
+
* the UpdatePolicy below)
|
|
234
347
|
* - Dedicated CloudWatch log groups for the CMS Lambda and the worker's
|
|
235
348
|
* stdout/stderr (the worker's is shipped via the amazon-cloudwatch-agent -
|
|
236
349
|
* journald is not agent-readable), each with a custom name/retention/
|