CDK Shorts #3 – Local Bundling

18 Jun 2023

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: [
    "/*",
  ],
});


AWS Architect Profesional
AWS Developer Associate AWS Architect Associate
AWS Community Hero
Tags
Archive