Example 5 is quite a noisy example. At the highest level, it's trying to display how to use GitHub Actions Environments to do both PR builds/deploys and then blue/green deployments.
Within src/
there is a file put_message.py
which contains the function put_message
- this takes an env
which is the environment to deploy to, stack_output_path
which is the path to the CDK output.json made after deploying, and secret
- which is a value to display in the message. This message is uploaded to the deployed stacks bucket as an HTML file to view. The function prints the full URL of the HTML file so that downstream processes can use it.
Within cdk/
there is a very simple AWS CDK app which deploys a public Bucket. This stack has two outputs: message-bucket-name-{identifier}
and message-bucket-url-{identifier}
.
Within tests/
there is a file unit/test_put_message.py
which tests that:
- The message is added to the bucket
- The message contains the correct secret
- The message is public to anyone
- The full URL is printed by the function
Within tests/
there is a file integration/test_put_message.py
which tests that:
- The file is available via a public URL as defined by
DEPLOYED_MESSAGE_URL
in.env
- The contents of the file match what is expected with the values of
IDENTIFIER
andSECRET
in.env
Within `.github/workflows/example-5-cdk-with-environments.yaml, we're defining 4 jobs:
This job is used in all scenarios: pull requests and main builds, it does the following:
- Checks out the code
- Installs Python
- Installs Poetry
- Installs dependencies (
make example-5-install
) - Runs unit tests (
make example-5-unit-tests
)
This job is only used on pull request events, it first needs unit-tests
to have succeeded. This job is set to the environment dev
which we set up in the GitHub UI, the job does the following:
- Checks out the code
- Installs Python
- Installs Poetry
- Sets up Node
- Installs dependencies (
make example-5-install
) - Configures AWS CLI (This uses secrets named
AWS_ACCESS_KEY_ID
andAWS_SECRET_ACCESS_KEY
in thedev
environment on GitHub - These are the same secret names in all environments, but the values are different) - Makes the
.env
file (Populated withIDENTIFIER
,OWNER
,AWS_DEFAULT_REGION
, andSECRET
which is the GitHub SecretHTML_SECRET
defined in thedev
environment) - Deploys the stack (
make example-5-deploy
) - Puts
message.html
in the bucket (make example-5-put-message
) - It also sets the step output to the URL of the file - Puts the URL of the file into
.env
asDEPLOYED_MESSAGE_URL
- Runs integration tests (
make example-5-integration-tests
) - Destroys the stack (
make example-5-destroy
)
This job is only used on main
branch builds, it first needs unit-tests
to have succeeded. This job is set to the environment staging
. The environment URL is set to be the value of steps.put-message
. This will be available when the job succeeds. The job does the following:
- Checks out the code
- Installs Python
- Installs Poetry
- Sets up Node
- Installs dependencies (
make example-5-install
) - Configures AWS CLI (This uses secrets named
AWS_ACCESS_KEY_ID
andAWS_SECRET_ACCESS_KEY
in thestaging
environment on GitHub - These are the same secret names in all environments, but the values are different) - Makes the
.env
file (Populated withIDENTIFIER
,OWNER
,AWS_DEFAULT_REGION
, andSECRET
which is the GitHub SecretHTML_SECRET
defined in thestaging
environment) - Deploys the stack (
make example-5-deploy
) - Puts
message.html
in the bucket (make example-5-put-message
) - It also sets the step output to the URL of the file - Puts the URL of the file into
.env
asDEPLOYED_MESSAGE_URL
- Runs integration tests (
make example-5-integration-tests
)
This job is only used on main
branch builds, it first needs staging-deployment
to have succeeded. This job is set to the environment prod
. The environment URL is set to be the value of steps.put-message
. This will be available when the job succeeds. The job does the following:
- Checks out the code
- Installs Python
- Installs Poetry
- Sets up Node
- Installs dependencies (
make example-5-install
) - Configures AWS CLI (This uses secrets named
AWS_ACCESS_KEY_ID
andAWS_SECRET_ACCESS_KEY
in theprod
environment on GitHub - These are the same secret names in all environments, but the values are different) - Makes the
.env
file (Populated withIDENTIFIER
,OWNER
,AWS_DEFAULT_REGION
, andSECRET
which is the GitHub SecretHTML_SECRET
defined in theprod
environment) - Deploys the stack (
make example-5-deploy
) - Puts
message.html
in the bucket (make example-5-put-message
) - It also sets the step output to the URL of the file
When the workflow succeeds for a PR, you'll see a summary like:
You'll notice that the unit-tests
ran and dev-deployment
did, no deployment remains up and you cannot click on a URL to the deployment.
When the workflow succeeds for a main
build, you'll see a summary like:
You'll notice that the unit-tests
ran, staging-deployment
ran, prod-deployment
ran, and dev-deployment
did not. You can see clickable URLs on staging
and prod
which will take you to HTML files in the separate environments.
GitHub Action Environments allow you to:
- Separate your GitHub Secrets into environments
- Maintain the same names for Secrets so that your code only needs to refer to one set of names
- Dynamically assign a Actions Job to an environment
- Attach a URL to a Actions Job environment (To whatever you deployed)
- Add environment protection - This allows you to require a reviewer to approve a deployment to your environment
You can read more about GitHub Action Environments here, I found this post and accompanying video really useful.
To get setup, run:
$ make install # Installs the dependencies in `pyproject.toml`, sets up a Poetry Virtual Environment, and echoes its location for use in IDEs, it also installs the dependencies in `package-lock.json`
To lint the code in cdk
, src/
, and tests/
, run:
$ make lint # Runs flake8, isort, and black in check only mode and outputs any linting issues
To format the code in cdk
, src/
, and tests/
, run:
$ make format # Runs isort and black, formatting your code
To run the unit tests, run:
$ make unit-tests # Runs the unit test suite in `tests/unit`
To see what you'll deploy, run:
$ make diff # Runs a CDK diff of your stack using your `.env` file
To deploy your stack, run:
$ make deploy # Runs a CDK deploy of your stack using your `.env` file, it creates a file called `example-5-outputs.json` which are the CDK outputs
To put the message in your deployed bucket, run:
$ make put-message # Uploads the message to S3 with the values of `SECRET` and `IDENTIFIER` in `.env` and the outputs in `example-5-outputs.json`. This also outputs the full URL of the message - **you should put this in your `.env` file as `DEPLOYED_MESSAGE_URL`**
To run the integration tests against your deployed and now populated stack, run:
$ make integration-tests # Runs the integration test suite in `tests/integration`
To destroy your stack, run:
$ make destroy # Runs a CDK destroy of your stack using your `.env` file
A .env
file is expected within example-5-cdk-with-environments/
. This file should contain the following before you do any deployments:
IDENTIFIER="<a-unique-value-to-tie-to-the-deployment>" # This is the environment in GitHub Actions
OWNER="<your-name>"
AWS_DEFAULT_PROFILE="<your-preferred-named-aws-cli-profile>"
AWS_DEFAULT_REGION="<your-preferred-aws-region>"
SECRET="<a-secret-value-to-display-in-the-message>" # This is only needed on local deploys, this value is retrieved from `${{ secrets.HTML_SECRET }} in GitHub Actions
You will need to add the variable DEPLOYED_MESSAGE_URL
once you have run make put-message
.