CDK Shorts #3 – Local Bundling
By being a bit creative, we can force local bundling of CDK assets.
Scenario
We want to make our build process part of the CDK commands. This means that we don’t need to do extra build
steps/scripts before running CDK commands like: diff
, deploy
and watch
.
Solution
There are two ways to do this, the first is hinted within the CDK docs.
This method creates a new class that implements the ILocalBundling
interface. This is a bit of a pain as it requires
us to create a new class for each asset type, or it might be nice if you want to make some abstraction over this bundling.
class MyBundle implements ILocalBundling {
public tryBundle(outputDir: string, options: BundlingOptions) {
/* YOUR LOGIC HERE */
return true;
}
}
new assets.Asset(this, 'BundledAsset', {
path: '/path/to/asset',
bundling: {
image: DockerImage.fromRegistry('local'),/* Reqauired, so just specify a dummy value, it will never be read */
local: new MyBundle(),
},
});
The important parts here are to return true
in the tryBundle
method. This will then always make the CDK use the
local
bundling option, it will never run the docker command. Here we omit any docker related info, except the image,
because that is a required property. We are just giving it a dummy value as it will never be used.
The second method, is similar, but it does not require us to create a new class. We can just pass an object that
satisfies the ILocalBundling
interface.
new assets.Asset(this, 'BundledAsset', {
path: '/path/to/asset',
bundling: {
image: DockerImage.fromRegistry('local'),/* Reqauired, so just specify a dummy value, it will never be read */
local: {
tryBundle(outputDir: string, options: BundlingOptions) {
/* YOUR LOGIC HERE */
return true;
}
},
},
});
Worth noting is that you can specify assetHashType
which defaults to AssetHashType.SOURCE
. The default is great as it
means it will only run the bundling if the source changed. But it is not so great if we are testing and trying to perfect our bundling
code in the tryBundle
function. If you want it to run again the next
time you do a CDK command, you would need to change the source code everytime and that just makes for a tedious testing process.
Instead, you can set assetHashType
to AssetHashType.CUSTOM
and then specify assetHash
to be new everytime. This will
make the CDK think that the source has changed and it will run the bundling every time. Just be sure to change it back by
removing these two properties or setting assetHashType
back to AssetHashType.SOURCE
, which is the default anyway.
new assets.Asset(this, 'BundledAsset', {
path: '/path/to/asset',
assetHashType: AssetHashType.CUSTOM,
assetHash: Date.now().toString(),
bundling: {
image: DockerImage.fromRegistry('local'),/* Reqauired, so just specify a dummy value, it will never be read */
local: {
tryBundle(outputDir: string, options: BundlingOptions) {
/* YOUR LOGIC HERE */
return true;
}
},
},
});
Example usages
AWS Lambda code
Here we can do something like transpile and bundle our TS code to JS.
new lambda.Function(this, 'Lambda', {
code: lambda.Code.fromAsset(path.join(__dirname, 'lambda'), {
bundling: {
image: DockerImage.fromRegistry('local'),/* Reqauired, so just specify a dummy value, it will never be read */
local: {
tryBundle(outputDir: string, options: BundlingOptions) {
// Do your bundling here
return true;
}
},
},
}),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_18_X,
});
S3 bundling
We can build our frontend before we upload it to S3 for hosting.
new s3deploy.BucketDeployment(this, 'DeployWebsite', {
sources: [s3deploy.Source.asset(path.join(__dirname, 'website'), {
bundling: {
image: DockerImage.fromRegistry('local'),/* Reqauired, so just specify a dummy value, it will never be read */
local: {
tryBundle(outputDir: string, options: BundlingOptions) {
// Do your frontend build command here
return true;
}
},
},
})],
destinationBucket: websiteBucket,
distribution: frontendDist,
distributionPaths: [
"/*",
],
});