Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions .github/actionlint.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 11 additions & 6 deletions .github/workflows/cdk-deploy-dev.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 11 additions & 6 deletions .github/workflows/cdk-deploy-test.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 53 additions & 4 deletions .github/workflows/cdk-validate.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .projen/files.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 22 additions & 1 deletion .projenrc.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,28 @@
pin_github_actions(gh)

# Validate the CDK app offline on every pull request (no AWS credentials required)
cdk_validate_workflow(gh, python_version, cdk_cli_version)
cdk_validate_workflow(
gh,
python_version,
cdk_cli_version,
[env for env, account in target_accounts.items() if account],
)

# Temporary until actionlint supports background/wait steps (rhysd/actionlint#693).
YamlFile(
project,
".github/actionlint.yaml",
obj={
"paths": {
".github/workflows/{cdk-validate,cdk-deploy-*}.yml": {
"ignore": [
'^step must run script with "run" section or run action with "uses" section$',
r'^unexpected key "background" for step to (run shell command|execute action)\. expected one of .+$',
],
},
},
},
)

# Add Dependabot configuration for uv
YamlFile(
Expand Down
119 changes: 106 additions & 13 deletions src/bin/cicd_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
# render the same versions as the workflows built here.
GITHUB_ACTIONS = {
"checkout": "actions/checkout@v7",
"cache_restore": "actions/cache/restore@v6",
"cache_save": "actions/cache/save@v6",
"configure_aws_credentials": "aws-actions/configure-aws-credentials@v6",
"semantic_pull_request": "amannn/action-semantic-pull-request@v6",
"setup_python": "actions/setup-python@v7",
Expand All @@ -18,7 +20,7 @@ def pin_github_actions(gh):
gh.actions.set(action.split("@")[0], action)


def cdk_environment_steps(python_version, cdk_cli_version):
def cdk_environment_steps(python_version, cdk_cli_version, aws_credentials=None):
"""Checkout, Python, uv, project dependencies and the CDK CLI."""
return [
{
Expand All @@ -38,19 +40,85 @@ def cdk_environment_steps(python_version, cdk_cli_version):
"name": "Setup uv",
"uses": GITHUB_ACTIONS["setup_uv"],
},
*([aws_credentials] if aws_credentials else []),
{
"name": "Install dependencies",
"run": "uv sync",
"run": "uv sync --frozen",
},
{
"name": "Install cdk cli",
"run": f"npm install -g aws-cdk@{cdk_cli_version}",
},
*(
[
{
"name": "Wait for AWS authentication",
"wait": ["configure_aws_credentials"],
}
]
if aws_credentials
else []
),
]


def cdk_validate_workflow(gh, python_version, cdk_cli_version):
def docker_asset_cache_steps():
"""Restore content-addressed bundling images; skip uploads for apps without Docker assets."""
cache_path = "${{ runner.temp }}/docker-assets"
cache_prefix = "docker-assets-v2-${{ runner.os }}-${{ runner.arch }}-${{ steps.docker_cache_epoch.outputs.week }}-"
restore = [
{
"name": "Choose Docker cache refresh week",
"id": "docker_cache_epoch",
"run": 'echo "week=$(date -u +%G-%V)" >> "$GITHUB_OUTPUT"\necho "source=$(git rev-parse "HEAD^{tree}")" >> "$GITHUB_OUTPUT"',
},
{
"name": "Restore Docker asset images",
"id": "docker_cache",
"uses": GITHUB_ACTIONS["cache_restore"],
"with": {
"path": cache_path,
"key": cache_prefix + "${{ steps.docker_cache_epoch.outputs.source }}",
"restore-keys": cache_prefix,
},
},
{
"name": "Load Docker asset images",
"run": 'if [ -f "$RUNNER_TEMP/docker-assets/images.tar" ]; then docker load --input "$RUNNER_TEMP/docker-assets/images.tar"; fi',
},
]
save = [
{
"name": "Export Docker asset images",
"id": "export_docker_images",
"if": "steps.docker_cache.outputs.cache-hit != 'true'",
"shell": "bash",
"run": (
"images=()\n"
'while IFS= read -r image; do images+=("$image"); done < <(docker image ls --filter "reference=cdk-*" --format "{{.Repository}}:{{.Tag}}")\n'
'if [ "${#images[@]}" -gt 0 ]; then\n'
' mkdir -p "$RUNNER_TEMP/docker-assets"\n'
' docker save --output "$RUNNER_TEMP/docker-assets/images.tar" "${images[@]}"\n'
' echo "has-images=true" >> "$GITHUB_OUTPUT"\n'
"fi"
),
},
{
"name": "Save Docker asset images",
"if": "steps.export_docker_images.outputs.has-images == 'true'",
"uses": GITHUB_ACTIONS["cache_save"],
"with": {
"path": cache_path,
"key": "${{ steps.docker_cache.outputs.cache-primary-key }}",
},
},
]
return restore, save


def cdk_validate_workflow(gh, python_version, cdk_cli_version, environments):
"""Validate the CDK app offline on pull requests, without AWS credentials."""
restore_cache, save_cache = docker_asset_cache_steps()
workflow = github.GithubWorkflow(gh, "cdk-validate")
workflow.on(pull_request={}, workflow_dispatch={})
workflow.add_jobs(
Expand All @@ -63,10 +131,30 @@ def cdk_validate_workflow(gh, python_version, cdk_cli_version):
},
"steps": [
*cdk_environment_steps(python_version, cdk_cli_version),
*restore_cache,
*[
{
"id": f"validate_{env}",
"name": f"Validate {env} offline",
"background": True,
"run": f'uv run projen {env}:validate --no-online --output "${{{{ runner.temp }}}}/cdk-assemblies/{env}"',
}
for env in environments
],
{
"name": "Validate CDK app against the default rule set",
"run": "uv run projen validate",
"id": "unit_tests",
"name": "Run tests",
"background": True,
"run": "uv run projen test",
},
{
"name": "Wait for validation and tests",
"wait": [
*[f"validate_{env}" for env in environments],
"unit_tests",
],
},
*save_cache,
],
},
}
Expand All @@ -93,15 +181,20 @@ def github_cicd(gh, account, env, python_version, aws_region, cdk_cli_version):
"idToken": github.workflows.JobPermission.WRITE,
},
"steps": [
*cdk_environment_steps(python_version, cdk_cli_version),
{
"name": "Configure AWS credentials",
"uses": GITHUB_ACTIONS["configure_aws_credentials"],
"with": {
"role-to-assume": f"arn:aws:iam::{account}:role/GitHubDeployRole",
"aws-region": aws_region,
*cdk_environment_steps(
python_version,
cdk_cli_version,
aws_credentials={
"id": "configure_aws_credentials",
"name": "Configure AWS credentials",
"background": True,
"uses": GITHUB_ACTIONS["configure_aws_credentials"],
"with": {
"role-to-assume": f"arn:aws:iam::{account}:role/GitHubDeployRole",
"aws-region": aws_region,
},
},
},
),
{
"name": f"Validate CDK for the {env.upper()} environment",
"run": f"uv run projen {env}:validate",
Expand Down
Loading
Loading