Skip to content

Commit 65b3441

Browse files
authored
chore(ci): parallelize checks and cache CDK Docker images (#30)
* chore(ci): parallelize checks and cache CDK Docker images * fix(ci): isolate assemblies and fingerprint all Docker contexts
1 parent 6650bc0 commit 65b3441

10 files changed

Lines changed: 336 additions & 30 deletions

File tree

‎.gitattributes‎

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎.github/actionlint.yaml‎

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎.github/workflows/cdk-deploy-dev.yml‎

Lines changed: 11 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎.github/workflows/cdk-deploy-test.yml‎

Lines changed: 11 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎.github/workflows/cdk-validate.yml‎

Lines changed: 53 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎.projen/files.json‎

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎.projenrc.py‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,28 @@
122122
pin_github_actions(gh)
123123

124124
# Validate the CDK app offline on every pull request (no AWS credentials required)
125-
cdk_validate_workflow(gh, python_version, cdk_cli_version)
125+
cdk_validate_workflow(
126+
gh,
127+
python_version,
128+
cdk_cli_version,
129+
[env for env, account in target_accounts.items() if account],
130+
)
131+
132+
# Temporary until actionlint supports background/wait steps (rhysd/actionlint#693).
133+
YamlFile(
134+
project,
135+
".github/actionlint.yaml",
136+
obj={
137+
"paths": {
138+
".github/workflows/{cdk-validate,cdk-deploy-*}.yml": {
139+
"ignore": [
140+
'^step must run script with "run" section or run action with "uses" section$',
141+
r'^unexpected key "background" for step to (run shell command|execute action)\. expected one of .+$',
142+
],
143+
},
144+
},
145+
},
146+
)
126147

127148
# Add Dependabot configuration for uv
128149
YamlFile(

‎src/bin/cicd_helper.py‎

Lines changed: 106 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
# render the same versions as the workflows built here.
66
GITHUB_ACTIONS = {
77
"checkout": "actions/checkout@v7",
8+
"cache_restore": "actions/cache/restore@v6",
9+
"cache_save": "actions/cache/save@v6",
810
"configure_aws_credentials": "aws-actions/configure-aws-credentials@v6",
911
"semantic_pull_request": "amannn/action-semantic-pull-request@v6",
1012
"setup_python": "actions/setup-python@v7",
@@ -18,7 +20,7 @@ def pin_github_actions(gh):
1820
gh.actions.set(action.split("@")[0], action)
1921

2022

21-
def cdk_environment_steps(python_version, cdk_cli_version):
23+
def cdk_environment_steps(python_version, cdk_cli_version, aws_credentials=None):
2224
"""Checkout, Python, uv, project dependencies and the CDK CLI."""
2325
return [
2426
{
@@ -38,19 +40,85 @@ def cdk_environment_steps(python_version, cdk_cli_version):
3840
"name": "Setup uv",
3941
"uses": GITHUB_ACTIONS["setup_uv"],
4042
},
43+
*([aws_credentials] if aws_credentials else []),
4144
{
4245
"name": "Install dependencies",
43-
"run": "uv sync",
46+
"run": "uv sync --frozen",
4447
},
4548
{
4649
"name": "Install cdk cli",
4750
"run": f"npm install -g aws-cdk@{cdk_cli_version}",
4851
},
52+
*(
53+
[
54+
{
55+
"name": "Wait for AWS authentication",
56+
"wait": ["configure_aws_credentials"],
57+
}
58+
]
59+
if aws_credentials
60+
else []
61+
),
4962
]
5063

5164

52-
def cdk_validate_workflow(gh, python_version, cdk_cli_version):
65+
def docker_asset_cache_steps():
66+
"""Restore content-addressed bundling images; skip uploads for apps without Docker assets."""
67+
cache_path = "${{ runner.temp }}/docker-assets"
68+
cache_prefix = "docker-assets-v2-${{ runner.os }}-${{ runner.arch }}-${{ steps.docker_cache_epoch.outputs.week }}-"
69+
restore = [
70+
{
71+
"name": "Choose Docker cache refresh week",
72+
"id": "docker_cache_epoch",
73+
"run": 'echo "week=$(date -u +%G-%V)" >> "$GITHUB_OUTPUT"\necho "source=$(git rev-parse "HEAD^{tree}")" >> "$GITHUB_OUTPUT"',
74+
},
75+
{
76+
"name": "Restore Docker asset images",
77+
"id": "docker_cache",
78+
"uses": GITHUB_ACTIONS["cache_restore"],
79+
"with": {
80+
"path": cache_path,
81+
"key": cache_prefix + "${{ steps.docker_cache_epoch.outputs.source }}",
82+
"restore-keys": cache_prefix,
83+
},
84+
},
85+
{
86+
"name": "Load Docker asset images",
87+
"run": 'if [ -f "$RUNNER_TEMP/docker-assets/images.tar" ]; then docker load --input "$RUNNER_TEMP/docker-assets/images.tar"; fi',
88+
},
89+
]
90+
save = [
91+
{
92+
"name": "Export Docker asset images",
93+
"id": "export_docker_images",
94+
"if": "steps.docker_cache.outputs.cache-hit != 'true'",
95+
"shell": "bash",
96+
"run": (
97+
"images=()\n"
98+
'while IFS= read -r image; do images+=("$image"); done < <(docker image ls --filter "reference=cdk-*" --format "{{.Repository}}:{{.Tag}}")\n'
99+
'if [ "${#images[@]}" -gt 0 ]; then\n'
100+
' mkdir -p "$RUNNER_TEMP/docker-assets"\n'
101+
' docker save --output "$RUNNER_TEMP/docker-assets/images.tar" "${images[@]}"\n'
102+
' echo "has-images=true" >> "$GITHUB_OUTPUT"\n'
103+
"fi"
104+
),
105+
},
106+
{
107+
"name": "Save Docker asset images",
108+
"if": "steps.export_docker_images.outputs.has-images == 'true'",
109+
"uses": GITHUB_ACTIONS["cache_save"],
110+
"with": {
111+
"path": cache_path,
112+
"key": "${{ steps.docker_cache.outputs.cache-primary-key }}",
113+
},
114+
},
115+
]
116+
return restore, save
117+
118+
119+
def cdk_validate_workflow(gh, python_version, cdk_cli_version, environments):
53120
"""Validate the CDK app offline on pull requests, without AWS credentials."""
121+
restore_cache, save_cache = docker_asset_cache_steps()
54122
workflow = github.GithubWorkflow(gh, "cdk-validate")
55123
workflow.on(pull_request={}, workflow_dispatch={})
56124
workflow.add_jobs(
@@ -63,10 +131,30 @@ def cdk_validate_workflow(gh, python_version, cdk_cli_version):
63131
},
64132
"steps": [
65133
*cdk_environment_steps(python_version, cdk_cli_version),
134+
*restore_cache,
135+
*[
136+
{
137+
"id": f"validate_{env}",
138+
"name": f"Validate {env} offline",
139+
"background": True,
140+
"run": f'uv run projen {env}:validate --no-online --output "${{{{ runner.temp }}}}/cdk-assemblies/{env}"',
141+
}
142+
for env in environments
143+
],
66144
{
67-
"name": "Validate CDK app against the default rule set",
68-
"run": "uv run projen validate",
145+
"id": "unit_tests",
146+
"name": "Run tests",
147+
"background": True,
148+
"run": "uv run projen test",
69149
},
150+
{
151+
"name": "Wait for validation and tests",
152+
"wait": [
153+
*[f"validate_{env}" for env in environments],
154+
"unit_tests",
155+
],
156+
},
157+
*save_cache,
70158
],
71159
},
72160
}
@@ -93,15 +181,20 @@ def github_cicd(gh, account, env, python_version, aws_region, cdk_cli_version):
93181
"idToken": github.workflows.JobPermission.WRITE,
94182
},
95183
"steps": [
96-
*cdk_environment_steps(python_version, cdk_cli_version),
97-
{
98-
"name": "Configure AWS credentials",
99-
"uses": GITHUB_ACTIONS["configure_aws_credentials"],
100-
"with": {
101-
"role-to-assume": f"arn:aws:iam::{account}:role/GitHubDeployRole",
102-
"aws-region": aws_region,
184+
*cdk_environment_steps(
185+
python_version,
186+
cdk_cli_version,
187+
aws_credentials={
188+
"id": "configure_aws_credentials",
189+
"name": "Configure AWS credentials",
190+
"background": True,
191+
"uses": GITHUB_ACTIONS["configure_aws_credentials"],
192+
"with": {
193+
"role-to-assume": f"arn:aws:iam::{account}:role/GitHubDeployRole",
194+
"aws-region": aws_region,
195+
},
103196
},
104-
},
197+
),
105198
{
106199
"name": f"Validate CDK for the {env.upper()} environment",
107200
"run": f"uv run projen {env}:validate",

0 commit comments

Comments
 (0)