token-injectable-docker-builder 1.8.1 → 1.9.0

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/API.md CHANGED
@@ -135,6 +135,143 @@ A Lambda-compatible DockerImageCode referencing the tag of the built Docker imag
135
135
  ---
136
136
 
137
137
 
138
+ ### TokenInjectableDockerBuilderProvider <a name="TokenInjectableDockerBuilderProvider" id="token-injectable-docker-builder.TokenInjectableDockerBuilderProvider"></a>
139
+
140
+ Shared provider for `TokenInjectableDockerBuilder` instances.
141
+
142
+ Creates the onEvent and isComplete Lambda functions once per stack.
143
+ Each builder instance registers its CodeBuild project ARN so the
144
+ shared Lambdas have permission to start builds and read logs.
145
+
146
+ #### Methods <a name="Methods" id="Methods"></a>
147
+
148
+ | **Name** | **Description** |
149
+ | --- | --- |
150
+ | <code><a href="#token-injectable-docker-builder.TokenInjectableDockerBuilderProvider.toString">toString</a></code> | Returns a string representation of this construct. |
151
+ | <code><a href="#token-injectable-docker-builder.TokenInjectableDockerBuilderProvider.registerProject">registerProject</a></code> | Grant the shared Lambdas permission to start builds for a specific CodeBuild project and pull/push to its ECR repository. |
152
+
153
+ ---
154
+
155
+ ##### `toString` <a name="toString" id="token-injectable-docker-builder.TokenInjectableDockerBuilderProvider.toString"></a>
156
+
157
+ ```typescript
158
+ public toString(): string
159
+ ```
160
+
161
+ Returns a string representation of this construct.
162
+
163
+ ##### `registerProject` <a name="registerProject" id="token-injectable-docker-builder.TokenInjectableDockerBuilderProvider.registerProject"></a>
164
+
165
+ ```typescript
166
+ public registerProject(project: Project, ecrRepo: Repository, encryptionKey?: Key): void
167
+ ```
168
+
169
+ Grant the shared Lambdas permission to start builds for a specific CodeBuild project and pull/push to its ECR repository.
170
+
171
+ ###### `project`<sup>Required</sup> <a name="project" id="token-injectable-docker-builder.TokenInjectableDockerBuilderProvider.registerProject.parameter.project"></a>
172
+
173
+ - *Type:* aws-cdk-lib.aws_codebuild.Project
174
+
175
+ ---
176
+
177
+ ###### `ecrRepo`<sup>Required</sup> <a name="ecrRepo" id="token-injectable-docker-builder.TokenInjectableDockerBuilderProvider.registerProject.parameter.ecrRepo"></a>
178
+
179
+ - *Type:* aws-cdk-lib.aws_ecr.Repository
180
+
181
+ ---
182
+
183
+ ###### `encryptionKey`<sup>Optional</sup> <a name="encryptionKey" id="token-injectable-docker-builder.TokenInjectableDockerBuilderProvider.registerProject.parameter.encryptionKey"></a>
184
+
185
+ - *Type:* aws-cdk-lib.aws_kms.Key
186
+
187
+ ---
188
+
189
+ #### Static Functions <a name="Static Functions" id="Static Functions"></a>
190
+
191
+ | **Name** | **Description** |
192
+ | --- | --- |
193
+ | <code><a href="#token-injectable-docker-builder.TokenInjectableDockerBuilderProvider.isConstruct">isConstruct</a></code> | Checks if `x` is a construct. |
194
+ | <code><a href="#token-injectable-docker-builder.TokenInjectableDockerBuilderProvider.getOrCreate">getOrCreate</a></code> | Get or create the singleton provider for this stack. |
195
+
196
+ ---
197
+
198
+ ##### ~~`isConstruct`~~ <a name="isConstruct" id="token-injectable-docker-builder.TokenInjectableDockerBuilderProvider.isConstruct"></a>
199
+
200
+ ```typescript
201
+ import { TokenInjectableDockerBuilderProvider } from 'token-injectable-docker-builder'
202
+
203
+ TokenInjectableDockerBuilderProvider.isConstruct(x: any)
204
+ ```
205
+
206
+ Checks if `x` is a construct.
207
+
208
+ ###### `x`<sup>Required</sup> <a name="x" id="token-injectable-docker-builder.TokenInjectableDockerBuilderProvider.isConstruct.parameter.x"></a>
209
+
210
+ - *Type:* any
211
+
212
+ Any object.
213
+
214
+ ---
215
+
216
+ ##### `getOrCreate` <a name="getOrCreate" id="token-injectable-docker-builder.TokenInjectableDockerBuilderProvider.getOrCreate"></a>
217
+
218
+ ```typescript
219
+ import { TokenInjectableDockerBuilderProvider } from 'token-injectable-docker-builder'
220
+
221
+ TokenInjectableDockerBuilderProvider.getOrCreate(scope: Construct, props?: TokenInjectableDockerBuilderProviderProps)
222
+ ```
223
+
224
+ Get or create the singleton provider for this stack.
225
+
226
+ All `TokenInjectableDockerBuilder` instances in the same stack
227
+ share a single pair of Lambda functions.
228
+
229
+ ###### `scope`<sup>Required</sup> <a name="scope" id="token-injectable-docker-builder.TokenInjectableDockerBuilderProvider.getOrCreate.parameter.scope"></a>
230
+
231
+ - *Type:* constructs.Construct
232
+
233
+ ---
234
+
235
+ ###### `props`<sup>Optional</sup> <a name="props" id="token-injectable-docker-builder.TokenInjectableDockerBuilderProvider.getOrCreate.parameter.props"></a>
236
+
237
+ - *Type:* <a href="#token-injectable-docker-builder.TokenInjectableDockerBuilderProviderProps">TokenInjectableDockerBuilderProviderProps</a>
238
+
239
+ ---
240
+
241
+ #### Properties <a name="Properties" id="Properties"></a>
242
+
243
+ | **Name** | **Type** | **Description** |
244
+ | --- | --- | --- |
245
+ | <code><a href="#token-injectable-docker-builder.TokenInjectableDockerBuilderProvider.property.node">node</a></code> | <code>constructs.Node</code> | The tree node. |
246
+ | <code><a href="#token-injectable-docker-builder.TokenInjectableDockerBuilderProvider.property.serviceToken">serviceToken</a></code> | <code>string</code> | The service token used by CustomResource instances. |
247
+
248
+ ---
249
+
250
+ ##### `node`<sup>Required</sup> <a name="node" id="token-injectable-docker-builder.TokenInjectableDockerBuilderProvider.property.node"></a>
251
+
252
+ ```typescript
253
+ public readonly node: Node;
254
+ ```
255
+
256
+ - *Type:* constructs.Node
257
+
258
+ The tree node.
259
+
260
+ ---
261
+
262
+ ##### `serviceToken`<sup>Required</sup> <a name="serviceToken" id="token-injectable-docker-builder.TokenInjectableDockerBuilderProvider.property.serviceToken"></a>
263
+
264
+ ```typescript
265
+ public readonly serviceToken: string;
266
+ ```
267
+
268
+ - *Type:* string
269
+
270
+ The service token used by CustomResource instances.
271
+
272
+ ---
273
+
274
+
138
275
  ## Structs <a name="Structs" id="Structs"></a>
139
276
 
140
277
  ### TokenInjectableDockerBuilderProps <a name="TokenInjectableDockerBuilderProps" id="token-injectable-docker-builder.TokenInjectableDockerBuilderProps"></a>
@@ -164,6 +301,7 @@ const tokenInjectableDockerBuilderProps: TokenInjectableDockerBuilderProps = { .
164
301
  | <code><a href="#token-injectable-docker-builder.TokenInjectableDockerBuilderProps.property.installCommands">installCommands</a></code> | <code>string[]</code> | Custom commands to run during the install phase of CodeBuild. |
165
302
  | <code><a href="#token-injectable-docker-builder.TokenInjectableDockerBuilderProps.property.kmsEncryption">kmsEncryption</a></code> | <code>boolean</code> | Whether to enable KMS encryption for the ECR repository. |
166
303
  | <code><a href="#token-injectable-docker-builder.TokenInjectableDockerBuilderProps.property.preBuildCommands">preBuildCommands</a></code> | <code>string[]</code> | Custom commands to run during the pre_build phase of CodeBuild. |
304
+ | <code><a href="#token-injectable-docker-builder.TokenInjectableDockerBuilderProps.property.provider">provider</a></code> | <code><a href="#token-injectable-docker-builder.TokenInjectableDockerBuilderProvider">TokenInjectableDockerBuilderProvider</a></code> | Shared provider for the custom resource Lambdas. |
167
305
  | <code><a href="#token-injectable-docker-builder.TokenInjectableDockerBuilderProps.property.securityGroups">securityGroups</a></code> | <code>aws-cdk-lib.aws_ec2.ISecurityGroup[]</code> | The security groups to attach to the CodeBuild project. |
168
306
  | <code><a href="#token-injectable-docker-builder.TokenInjectableDockerBuilderProps.property.subnetSelection">subnetSelection</a></code> | <code>aws-cdk-lib.aws_ec2.SubnetSelection</code> | The subnet selection to specify which subnets to use within the VPC. |
169
307
  | <code><a href="#token-injectable-docker-builder.TokenInjectableDockerBuilderProps.property.vpc">vpc</a></code> | <code>aws-cdk-lib.aws_ec2.IVpc</code> | The VPC in which the CodeBuild project will be deployed. |
@@ -380,6 +518,24 @@ preBuildCommands: [
380
518
 
381
519
  ---
382
520
 
521
+ ##### `provider`<sup>Optional</sup> <a name="provider" id="token-injectable-docker-builder.TokenInjectableDockerBuilderProps.property.provider"></a>
522
+
523
+ ```typescript
524
+ public readonly provider: TokenInjectableDockerBuilderProvider;
525
+ ```
526
+
527
+ - *Type:* <a href="#token-injectable-docker-builder.TokenInjectableDockerBuilderProvider">TokenInjectableDockerBuilderProvider</a>
528
+ - *Default:* A new provider is created per builder instance
529
+
530
+ Shared provider for the custom resource Lambdas.
531
+
532
+ Use `TokenInjectableDockerBuilderProvider.getOrCreate(this)` to create
533
+ a singleton that is shared across all builders in the same stack.
534
+
535
+ When omitted, each builder creates its own Lambdas (original behavior).
536
+
537
+ ---
538
+
383
539
  ##### `securityGroups`<sup>Optional</sup> <a name="securityGroups" id="token-injectable-docker-builder.TokenInjectableDockerBuilderProps.property.securityGroups"></a>
384
540
 
385
541
  ```typescript
@@ -425,5 +581,38 @@ If provided, the CodeBuild project will be launched within the specified VPC.
425
581
 
426
582
  ---
427
583
 
584
+ ### TokenInjectableDockerBuilderProviderProps <a name="TokenInjectableDockerBuilderProviderProps" id="token-injectable-docker-builder.TokenInjectableDockerBuilderProviderProps"></a>
585
+
586
+ Options for creating a `TokenInjectableDockerBuilderProvider`.
587
+
588
+ #### Initializer <a name="Initializer" id="token-injectable-docker-builder.TokenInjectableDockerBuilderProviderProps.Initializer"></a>
589
+
590
+ ```typescript
591
+ import { TokenInjectableDockerBuilderProviderProps } from 'token-injectable-docker-builder'
592
+
593
+ const tokenInjectableDockerBuilderProviderProps: TokenInjectableDockerBuilderProviderProps = { ... }
594
+ ```
595
+
596
+ #### Properties <a name="Properties" id="Properties"></a>
597
+
598
+ | **Name** | **Type** | **Description** |
599
+ | --- | --- | --- |
600
+ | <code><a href="#token-injectable-docker-builder.TokenInjectableDockerBuilderProviderProps.property.queryInterval">queryInterval</a></code> | <code>aws-cdk-lib.Duration</code> | How often the provider polls for build completion. |
601
+
602
+ ---
603
+
604
+ ##### `queryInterval`<sup>Optional</sup> <a name="queryInterval" id="token-injectable-docker-builder.TokenInjectableDockerBuilderProviderProps.property.queryInterval"></a>
605
+
606
+ ```typescript
607
+ public readonly queryInterval: Duration;
608
+ ```
609
+
610
+ - *Type:* aws-cdk-lib.Duration
611
+ - *Default:* Duration.seconds(30)
612
+
613
+ How often the provider polls for build completion.
614
+
615
+ ---
616
+
428
617
 
429
618
 
package/README.md CHANGED
@@ -16,6 +16,7 @@ For example, a Next.js frontend Docker image may require an API Gateway URL as a
16
16
 
17
17
  - **Build and Push Docker Images**: Automatically builds and pushes Docker images to ECR.
18
18
  - **Token Support**: Supports custom build arguments for Docker builds, including CDK tokens resolved at deployment time.
19
+ - **Shared Provider (Singleton)**: When building multiple Docker images in the same stack, use `TokenInjectableDockerBuilderProvider` to share a single pair of Lambda functions across all builders, reducing resource overhead from ~2 Lambdas per image to 2 Lambdas total.
19
20
  - **Custom Install and Pre-Build Commands**: Allows specifying custom commands to run during the `install` and `pre_build` phases of the CodeBuild build process.
20
21
  - **VPC Configuration**: Supports deploying the CodeBuild project within a VPC, with customizable security groups and subnet selection.
21
22
  - **Docker Login**: Supports Docker login using credentials stored in AWS Secrets Manager.
@@ -48,7 +49,37 @@ pip install token-injectable-docker-builder
48
49
 
49
50
  ---
50
51
 
51
- ## Constructor
52
+ ## API Reference
53
+
54
+ ### `TokenInjectableDockerBuilderProvider`
55
+
56
+ A singleton construct that creates the `onEvent` and `isComplete` Lambda functions once per stack. When building multiple Docker images, share a single provider to avoid creating redundant Lambda functions.
57
+
58
+ #### Static Methods
59
+
60
+ | Method | Description |
61
+ |---|---|
62
+ | `getOrCreate(scope, props?)` | Returns the existing provider for the stack, or creates one if it doesn't exist. |
63
+
64
+ #### Properties in `TokenInjectableDockerBuilderProviderProps`
65
+
66
+ | Property | Type | Required | Description |
67
+ |---|---|---|---|
68
+ | `queryInterval` | `Duration` | No | How often the provider polls for build completion. Defaults to `Duration.seconds(30)`. |
69
+
70
+ #### Instance Properties
71
+
72
+ | Property | Type | Description |
73
+ |---|---|---|
74
+ | `serviceToken` | `string` | The service token used by CustomResource instances. |
75
+
76
+ #### Instance Methods
77
+
78
+ | Method | Description |
79
+ |---|---|
80
+ | `registerProject(project, ecrRepo, encryptionKey?)` | Grants the shared Lambdas permission to start builds and access ECR for a specific CodeBuild project. Called automatically when `provider` is passed to `TokenInjectableDockerBuilder`. |
81
+
82
+ ---
52
83
 
53
84
  ### `TokenInjectableDockerBuilder`
54
85
 
@@ -64,6 +95,7 @@ pip install token-injectable-docker-builder
64
95
  |----------------------------|-----------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
65
96
  | `path` | `string` | Yes | The file path to the Dockerfile or source code directory. |
66
97
  | `buildArgs` | `{ [key: string]: string }` | No | Build arguments to pass to the Docker build process. These are transformed into `--build-arg` flags. To use in Dockerfile, leverage the `ARG` keyword. For more details, please see the [official Docker docs](https://docs.docker.com/build/building/variables/). |
98
+ | `provider` | `TokenInjectableDockerBuilderProvider` | No | Shared provider for the custom resource Lambdas. Use `TokenInjectableDockerBuilderProvider.getOrCreate(this)` to share a single pair of Lambdas across all builders in the same stack. When omitted, each builder creates its own Lambdas (original behavior). |
67
99
  | `dockerLoginSecretArn` | `string` | No | ARN of an AWS Secrets Manager secret for Docker credentials. Skips login if not provided. |
68
100
  | `vpc` | `IVpc` | No | The VPC in which the CodeBuild project will be deployed. If provided, the CodeBuild project will be launched within the specified VPC. |
69
101
  | `securityGroups` | `ISecurityGroup[]` | No | The security groups to attach to the CodeBuild project. These should define the network access rules for the CodeBuild project. |
@@ -71,16 +103,103 @@ pip install token-injectable-docker-builder
71
103
  | `installCommands` | `string[]` | No | Custom commands to run during the `install` phase of the CodeBuild build process. Will be executed before the Docker image is built. Useful for installing necessary dependencies for running pre-build scripts. |
72
104
  | `preBuildCommands` | `string[]` | No | Custom commands to run during the `pre_build` phase of the CodeBuild build process. Will be executed before the Docker image is built. Useful for running pre-build scripts, such as fetching configs. |
73
105
  | `kmsEncryption` | `boolean` | No | Whether to enable KMS encryption for the ECR repository. If `true`, a KMS key will be created for encrypting ECR images; otherwise, AES-256 encryption is used. Defaults to `false`. |
74
- | `completenessQueryInterval`| `Duration` | No | The query interval for checking if the CodeBuild project has completed. This determines how frequently the custom resource polls for build completion. Defaults to `Duration.seconds(30)`. |
106
+ | `completenessQueryInterval`| `Duration` | No | The query interval for checking if the CodeBuild project has completed. This determines how frequently the custom resource polls for build completion. Defaults to `Duration.seconds(30)`. Ignored when `provider` is set (the provider's `queryInterval` is used instead). |
75
107
  | `exclude` | `string[]` | No | A list of file paths in the Docker directory to exclude from the S3 asset bundle. If a `.dockerignore` file is present in the source directory, its contents will be used if this prop is not set. Defaults to an empty list or `.dockerignore` contents. |
76
108
  | `file` | `string` | No | The name of the Dockerfile to use for the build. Passed as `--file` to `docker build`. Useful when a project has multiple Dockerfiles (e.g. `Dockerfile.production`, `Dockerfile.admin`). Defaults to `Dockerfile`. |
77
109
  | `cacheDisabled` | `boolean` | No | When `true`, disables Docker layer caching. Every build runs from scratch. Use for debugging, corrupted cache, or major dependency changes. Defaults to `false`. |
78
110
  | `buildLogGroup` | `ILogGroup` | No | CloudWatch log group for CodeBuild build logs. When provided with RETAIN removal policy, logs survive rollbacks and stack deletion. If not provided, CodeBuild uses default logging (logs are deleted on rollback). |
79
111
 
112
+ #### Instance Properties
113
+
114
+ | Property | Type | Description |
115
+ |---|---|---|
116
+ | `containerImage` | `ContainerImage` | An ECS-compatible container image referencing the built Docker image. |
117
+ | `dockerImageCode` | `DockerImageCode` | A Lambda-compatible Docker image code referencing the built Docker image. |
118
+
80
119
  ---
81
120
 
82
121
  ## Usage Examples
83
122
 
123
+ ### Shared Provider (Recommended for Multiple Images)
124
+
125
+ When building multiple Docker images in the same stack, use a shared provider to avoid creating redundant Lambda functions. Without a shared provider, each builder creates 2 Lambdas + 1 Provider framework Lambda. With 10 images, that's 30 Lambdas. A shared provider reduces this to just 3 Lambdas total.
126
+
127
+ #### TypeScript/NPM Example
128
+
129
+ ```typescript
130
+ import * as cdk from 'aws-cdk-lib';
131
+ import {
132
+ TokenInjectableDockerBuilder,
133
+ TokenInjectableDockerBuilderProvider,
134
+ } from 'token-injectable-docker-builder';
135
+ import * as ecs from 'aws-cdk-lib/aws-ecs';
136
+
137
+ export class MultiImageStack extends cdk.Stack {
138
+ constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
139
+ super(scope, id, props);
140
+
141
+ // Create a shared provider once per stack (singleton)
142
+ const provider = TokenInjectableDockerBuilderProvider.getOrCreate(this);
143
+
144
+ // Build multiple Docker images sharing the same provider
145
+ const apiBuilder = new TokenInjectableDockerBuilder(this, 'ApiImage', {
146
+ path: './src/api',
147
+ provider,
148
+ });
149
+
150
+ const workerBuilder = new TokenInjectableDockerBuilder(this, 'WorkerImage', {
151
+ path: './src/worker',
152
+ provider,
153
+ });
154
+
155
+ const frontendBuilder = new TokenInjectableDockerBuilder(this, 'FrontendImage', {
156
+ path: './src/frontend',
157
+ buildArgs: { API_URL: 'https://api.example.com' },
158
+ provider,
159
+ });
160
+
161
+ // Use in ECS task definitions
162
+ const taskDef = new ecs.FargateTaskDefinition(this, 'TaskDef');
163
+ taskDef.addContainer('api', { image: apiBuilder.containerImage });
164
+ taskDef.addContainer('worker', { image: workerBuilder.containerImage });
165
+ }
166
+ }
167
+ ```
168
+
169
+ #### Python Example
170
+
171
+ ```python
172
+ from aws_cdk import aws_ecs as ecs, core as cdk
173
+ from token_injectable_docker_builder import (
174
+ TokenInjectableDockerBuilder,
175
+ TokenInjectableDockerBuilderProvider,
176
+ )
177
+
178
+ class MultiImageStack(cdk.Stack):
179
+ def __init__(self, scope: cdk.App, id: str, **kwargs):
180
+ super().__init__(scope, id, **kwargs)
181
+
182
+ # Create a shared provider once per stack (singleton)
183
+ provider = TokenInjectableDockerBuilderProvider.get_or_create(self)
184
+
185
+ # Build multiple Docker images sharing the same provider
186
+ api_builder = TokenInjectableDockerBuilder(self, "ApiImage",
187
+ path="./src/api",
188
+ provider=provider,
189
+ )
190
+
191
+ worker_builder = TokenInjectableDockerBuilder(self, "WorkerImage",
192
+ path="./src/worker",
193
+ provider=provider,
194
+ )
195
+
196
+ frontend_builder = TokenInjectableDockerBuilder(self, "FrontendImage",
197
+ path="./src/frontend",
198
+ build_args={"API_URL": "https://api.example.com"},
199
+ provider=provider,
200
+ )
201
+ ```
202
+
84
203
  ### Simple Usage Example
85
204
 
86
205
  This example demonstrates the basic usage of the `TokenInjectableDockerBuilder`, where a Next.js frontend Docker image requires an API Gateway URL as a build argument to create a reference from the UI to the associated API in a given deployment.
@@ -347,13 +466,24 @@ In this advanced example:
347
466
  - Uses the packaged asset and `buildArgs` to build the Docker image.
348
467
  - Executes any custom `installCommands` and `preBuildCommands` during the build process.
349
468
  - Pushes the image to an ECR repository.
469
+ - By default, uses `docker buildx` with ECR registry cache to speed up builds.
350
470
  3. **Custom Resource**:
351
471
  - Triggers the build process using a Lambda function (`onEvent`).
352
472
  - Monitors the build status using another Lambda function (`isComplete`) which polls at the interval specified by `completenessQueryInterval` (defaulting to 30 seconds if not provided).
473
+ - When using a shared `provider`, the same pair of Lambdas handles all builders in the stack.
353
474
  4. **Outputs**:
354
475
  - `.containerImage`: Returns the Docker image for ECS.
355
476
  - `.dockerImageCode`: Returns the Docker image code for Lambda.
356
477
 
478
+ ### Resource Comparison
479
+
480
+ | Scenario | Lambdas Created | CodeBuild Projects | ECR Repos |
481
+ |---|---|---|---|
482
+ | 5 images, no shared provider | 15 (3 per image) | 5 | 5 |
483
+ | 5 images, shared provider | 3 (shared) | 5 | 5 |
484
+ | 10 images, no shared provider | 30 (3 per image) | 10 | 10 |
485
+ | 10 images, shared provider | 3 (shared) | 10 | 10 |
486
+
357
487
  ---
358
488
 
359
489
  ## IAM Permissions
@@ -364,33 +494,37 @@ The construct automatically grants permissions for:
364
494
  - Pull and push images to ECR.
365
495
  - Access to AWS Secrets Manager if `dockerLoginSecretArn` is provided.
366
496
  - Access to the KMS key for encryption.
367
- - **Lambda Functions**:
497
+ - **Lambda Functions** (per-instance or shared provider):
368
498
  - Start and monitor CodeBuild builds.
369
499
  - Access CloudWatch Logs.
370
500
  - Access to the KMS key for encryption.
371
501
  - Pull and push images to ECR.
372
502
 
503
+ When using the shared provider, `registerProject()` incrementally adds IAM permissions for each CodeBuild project and ECR repository.
504
+
373
505
  ---
374
506
 
375
507
  ## Notes
376
508
 
509
+ - **Shared Provider**: Use `TokenInjectableDockerBuilderProvider.getOrCreate(this)` when building multiple images in the same stack. This is the recommended approach for stacks with 2+ Docker images.
377
510
  - **Build Arguments**: Pass custom arguments via `buildArgs` as `--build-arg` flags. CDK tokens can be used to inject dynamic values resolved at deployment time.
378
511
  - **Custom Commands**: Use `installCommands` and `preBuildCommands` to run custom shell commands during the build process. This can be useful for installing dependencies or fetching configuration files.
379
512
  - **VPC Configuration**: If your build process requires access to resources within a VPC, you can specify the VPC, security groups, and subnet selection.
380
513
  - **Docker Login**: If you need to log in to a private Docker registry before building the image, provide the ARN of a secret in AWS Secrets Manager containing the Docker credentials.
381
514
  - **ECR Repository**: Automatically creates an ECR repository with lifecycle rules to manage image retention, encryption with a KMS key, and image scanning on push.
382
- - **Build Query Interval**: The polling frequency for checking build completion can be customized via the `completenessQueryInterval` property.
515
+ - **Build Query Interval**: The polling frequency for checking build completion can be customized via the `completenessQueryInterval` property (per-instance) or `queryInterval` (shared provider).
383
516
  - **Custom Dockerfile**: Use the `file` property to specify a Dockerfile other than the default `Dockerfile`. This is passed as the `--file` flag to `docker build`.
384
517
  - **Docker Layer Caching**: By default, builds use ECR as a remote cache backend (via `docker buildx`), which can reduce build times by up to 25%. Set `cacheDisabled: true` when you need a clean build—for example, when debugging, the cache is corrupted, or after major dependency upgrades.
385
518
  - **Build Log Retention**: Pass `buildLogGroup` with a log group that has RETAIN removal policy to ensure build logs survive CloudFormation rollbacks and stack deletion.
519
+ - **Backward Compatibility**: The `provider` prop is optional. Omitting it preserves the original behavior where each builder creates its own Lambdas. Existing code works without changes.
386
520
 
387
521
  ---
388
522
 
389
523
  ## Troubleshooting
390
524
 
391
525
  1. **Build Errors**: Check the CodeBuild logs in CloudWatch Logs for detailed error messages. If you pass `buildLogGroup` with RETAIN removal policy, logs persist even after rollbacks. Otherwise, logs are deleted when the CodeBuild project is removed during rollback.
392
- 2. **Lambda Errors**: Check the `onEvent` and `isComplete` Lambda function logs in CloudWatch Logs.
393
- 3. **Permissions**: Ensure IAM roles have the required permissions for CodeBuild, ECR, Secrets Manager, and KMS if applicable.
526
+ 2. **Lambda Errors**: Check the `onEvent` and `isComplete` Lambda function logs in CloudWatch Logs. With a shared provider, both builders' events flow through the same Lambdas—filter by `ProjectName` in the logs.
527
+ 3. **Permissions**: Ensure IAM roles have the required permissions for CodeBuild, ECR, Secrets Manager, and KMS if applicable. When using a shared provider, verify that `registerProject()` was called for each builder (this happens automatically when passing the `provider` prop).
394
528
  4. **Network Access**: If the build requires network access (e.g., to download dependencies or access internal APIs), ensure that the VPC configuration allows necessary network connectivity, and adjust security group rules accordingly.
395
529
 
396
530
  ---
@@ -69,12 +69,13 @@ exports.handler = async (event) => {
69
69
  return { IsComplete: false };
70
70
  }
71
71
 
72
- // If build succeeded, retrieve the final artifact with the digest
72
+ // If build succeeded, return the image tag from the custom resource properties
73
73
  if (buildStatus === 'SUCCEEDED') {
74
+ const imageTag = event.ResourceProperties?.ImageTag || process.env.IMAGE_TAG;
74
75
  return {
75
76
  IsComplete: true,
76
77
  Data: {
77
- ImageTag: process.env.IMAGE_TAG,
78
+ ImageTag: imageTag,
78
79
  },
79
80
  };
80
81
  }
package/lib/index.d.ts CHANGED
@@ -1,9 +1,48 @@
1
1
  import { Duration } from 'aws-cdk-lib';
2
+ import { Project } from 'aws-cdk-lib/aws-codebuild';
2
3
  import { IVpc, ISecurityGroup, SubnetSelection } from 'aws-cdk-lib/aws-ec2';
4
+ import { Repository } from 'aws-cdk-lib/aws-ecr';
3
5
  import { ContainerImage } from 'aws-cdk-lib/aws-ecs';
6
+ import { Key } from 'aws-cdk-lib/aws-kms';
4
7
  import { DockerImageCode } from 'aws-cdk-lib/aws-lambda';
5
8
  import { ILogGroup } from 'aws-cdk-lib/aws-logs';
6
9
  import { Construct } from 'constructs';
10
+ /**
11
+ * Options for creating a `TokenInjectableDockerBuilderProvider`.
12
+ */
13
+ export interface TokenInjectableDockerBuilderProviderProps {
14
+ /**
15
+ * How often the provider polls for build completion.
16
+ *
17
+ * @default Duration.seconds(30)
18
+ */
19
+ readonly queryInterval?: Duration;
20
+ }
21
+ /**
22
+ * Shared provider for `TokenInjectableDockerBuilder` instances.
23
+ *
24
+ * Creates the onEvent and isComplete Lambda functions once per stack.
25
+ * Each builder instance registers its CodeBuild project ARN so the
26
+ * shared Lambdas have permission to start builds and read logs.
27
+ */
28
+ export declare class TokenInjectableDockerBuilderProvider extends Construct {
29
+ /**
30
+ * Get or create the singleton provider for this stack.
31
+ * All `TokenInjectableDockerBuilder` instances in the same stack
32
+ * share a single pair of Lambda functions.
33
+ */
34
+ static getOrCreate(scope: Construct, props?: TokenInjectableDockerBuilderProviderProps): TokenInjectableDockerBuilderProvider;
35
+ /** The service token used by CustomResource instances. */
36
+ readonly serviceToken: string;
37
+ private readonly onEventHandlerFunction;
38
+ private readonly isCompleteHandlerFunction;
39
+ private constructor();
40
+ /**
41
+ * Grant the shared Lambdas permission to start builds for a specific
42
+ * CodeBuild project and pull/push to its ECR repository.
43
+ */
44
+ registerProject(project: Project, ecrRepo: Repository, encryptionKey?: Key): void;
45
+ }
7
46
  /**
8
47
  * Properties for the `TokenInjectableDockerBuilder` construct.
9
48
  */
@@ -134,6 +173,16 @@ export interface TokenInjectableDockerBuilderProps {
134
173
  * @default - CodeBuild default logging (logs are deleted on rollback)
135
174
  */
136
175
  readonly buildLogGroup?: ILogGroup;
176
+ /**
177
+ * Shared provider for the custom resource Lambdas.
178
+ * Use `TokenInjectableDockerBuilderProvider.getOrCreate(this)` to create
179
+ * a singleton that is shared across all builders in the same stack.
180
+ *
181
+ * When omitted, each builder creates its own Lambdas (original behavior).
182
+ *
183
+ * @default - A new provider is created per builder instance
184
+ */
185
+ readonly provider?: TokenInjectableDockerBuilderProvider;
137
186
  }
138
187
  /**
139
188
  * A CDK construct to build and push Docker images to an ECR repository using