Solutions Constructs are built in typescript in a rather complex build environment. This is to support all the associated testing, code checking and compilation for Java, Python and .NET client. So that you don't have to recreate the entire toolset on your workstation, all building is done on a publicly available docker container. Using this container has both advantages and drawbacks. This document isn't going to pass judgment on these tradeoffs, it's just going to teach you how to build constructs.
Install Docker on your workstation. We're not going to go into detail on how to do this, you probably already have it installed. If not, you can get it here. We strongly recommend you increase the amount of system resources dedicated to a docker container prior to starting the build - this can make the difference between the build taking several hours and the build taking 45 minutes. You can adjust these in the Resources tab under the Settings page on the Docker Dashboard. A good starting point where we have had success is:
CPUs: 6 Memory: 8 GB Swap: 1 GB Disk image size: 60 GB
Action | |
---|---|
Open up the aws-solutions-constructs repo. | As you are reading this file from the aws-solutions-constructs repo, you are probably already there... |
Using the Fork button at the upper right, Fork the repo into your github account. | While we can give some advice on what github and git commands to use, we will not attempt to be a git tutorial. Some git/github expertise is assumed. |
Clone forked repo to your local development environment | If you wish to work off a branch in your repository, create that branch now and clone that branch. You will create a PR back to Main in aws-solutions-constructs eventually, you can do that from fork/main or fork/branch |
cd aws-solutions-constructs |
This is the home directory of the repo, this is where you will open your text editor, run full builds, etc. |
docker run -u root --rm --net=host -it -v $PWD:$PWD -w $PWD public.ecr.aws/jsii/superchain:1-bookworm-slim |
This command downloads the Docker image (if necessary) and starts the container in which you will do all builds. The container will stay open in terminal mode, allowing you to edit/build repeatedly until your code is ready. |
Optional - Open a second terminal window to the aws-solutions-constructs directory. | The docker container mounts your local drive, so shares the Solutions Constructs source code tree with the host. You may find it advantageous to maintain a second terminal not running the Docker container to run git commands, your text editor, etc. |
./deployment/v2/build-patterns.sh |
This command will build the entire repo, which is required prior to developing constructs. Some of the steps include: Installing the correct version of tools required in the build process, setting up environment variables, running JSII in each construct, and running all integration and unit tests. Commands executed across each construct are managed via yarn, so multiple commands are running in parallel most of the time. It's because of this that adding more resources to your Docker container can greatly reduce the build time (suggested values are discussed above). |
In the top level aws-solutions-constructs folder: ./deployment/v2/align-version.sh revert |
This will reset all the version numbers back to 0.0.0. You need to do this before adding/committing code - you can't submit package.json files with version numbers. It's common to do a full build ./deployment/v2/build-patterns.sh after coding to ensure all tests still pass - if you do this it will revert all the version numbers for you. |
The build-patterns.sh script sets everything up to build constructs, builds everything, then resets all the settings back where it started. What kind of settings? For instance - you may have noticed that the version numbers in package.json are 0.0.0 for all CDK and construct references. build-patterns.sh runs a script (align-version.sh) that sets all version numbers to the current version of the Solutions Constructs library. After the build it runs the script again (align-version.sh revert
) to set the version numbers back to 0.0.0 so the code is back in sync with the repo. The steps below perform the first part of build-patterns.sh so you can compile an individual folder, such as a single construct or /core. We call building a single construct rather than the whole library a partial build.
Action | Explanation |
---|---|
(optional)git checkout -b your-branch-name |
If you're working in a different branch than main in your forked repo and haven't changed your local branch, now is a good time to do so. |
source ./deployment/v2/allow-partial-builds.sh |
This aligns the versions and sets up environment variables. IMPORTANT - build-patterns.sh performs some initialization of the image (installing software, etc.) so must be run at least once before you can start working with partial builds. |
cd your-construct-name |
Change directory to the folder where you want to change code (this might also be cd core ). |
Do all your code editing | Open your code editor and and create the construct or perform your edits on an existing construct (or core). |
npm run build+lint+test |
This is the build command for a construct (or core). It will build, lint and run the unit and integration tests. If you make any substantive changes to the code, you will almost certainly see some or all of the tests fail. The next section will describe how testing works in AWS Solutions Constructs and how to write, refresh and execute tests. In the meantime, you can at least check if your code transpiles correctly without running the tests by running npm run build . If you've edited any modules in /core in addition to a construct, be sure to build /core before building your construct. |
Solutions Constructs use 2 flavors of testing, unit testing and integration testing. Unit testing targets specific aspects of a construct or one of the functions in the core library. It examines the results and confirms the correct resources are there. For instance, it may call the deployLambdaFunction() in the core library and then confirm that AWS_NODEJS_CONNECTION_REUSE_ENABLED environment variable was set correctly. The unit tests check that certain aspects of the results are correct. Unit tests are built on the jest framework with CDK extensions. You can learn more about unit testing CDK constructs here and here. Do not use snapshot testing in your unit testing - it doesn't work with the mechanisms in place to support CDK V1 and V2 with the same codebase.
Integration tests create a full construct, generate a CloudFormation template with CDK synth and compare the entire template with an existing template that was created when the construct was published. The integration tests check that the output of the construct has not changed. The existing template (found in the test/*.expected.json files) is generated by actually launching a stack with the construct, then extracting the template from the CloudFormation API.
All test files can be found in the /test directory under each construct (or core). You'll find 3 types of files in this directory:
- *.test.ts files - these are the unit test files. All the unit tests for a construct are in a single file.
- integ.*.ts files - these the integration test files. Each integration test gets a separate file.
- integ.*.expected.json files - these are the CloudFormation templates expected when generating each integ.*.ts file.
Action | Explanation |
---|---|
cdk-integ |
This will regenerate all the integ.*.expected.json files. It does this by launching each integ.*.ts file as a stack in your AWS account, so you will need to register some credentials in your docker container. You can do this using the AWS CLI (aws configure ) or any other method you choose. Because this launches and tears down each stack, it can take a while - adding more resources to your docker container will have no effect on the duration, the time is all waiting for CloudFormation to finish. If you want to generate a single integ.*.expected.json file you can specify the filename on the command line: cdk-integ integ.no-arguments.js . Note that the test is actually the transpiled .js file, not the source .ts file. Remember that the integration test just confirms the results have not changed, so it's imperative that the original results be correct. If you break integration tests, do not reflexively run cdk-integ , confirm that the test is breaking because of the changes you made and that the new template being generated is correct. Only when you're certain the construct is behaving properly regenerate the expected.json file. Run this command in the construct directory (one level above /test). |
cdk-integ-assert |
This will run all the integration tests and confirm that the output matches the expected.json files. You can run a single test by specifying the test name on the command line just like cdk-integ .. Run this command in the construct directory (one level above /test) |