Skip to content

Commit 93ded08

Browse files
authored
Implement 48h cooldown to docker tag workflow (#161)
* Implement 48h cooldown for Docker images in the docker tag workflow
1 parent 878af6e commit 93ded08

5 files changed

Lines changed: 79 additions & 5 deletions

File tree

.github/workflows/create-test-mirror-pr.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ jobs:
4040

4141
- name: Install crane
4242
run: |
43-
CRANE_VERSION="0.20.2"
43+
CRANE_VERSION="0.21.5"
4444
curl -fsSL "https://github.com/google/go-containerregistry/releases/download/v${CRANE_VERSION}/go-containerregistry_Linux_x86_64.tar.gz" -o crane.tar.gz
4545
tar -xzf crane.tar.gz crane
4646
sudo mv crane /usr/local/bin/crane

.github/workflows/docker-tag.yml

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ on:
44
# Quarterly schedule, roughly aligned with JDK CPU
55
- cron: '0 0 30 1,4,7,10 *'
66
workflow_dispatch:
7+
inputs:
8+
skip_cooldown:
9+
description: 'Skip the 48h external dependency cooldown check'
10+
required: false
11+
type: boolean
12+
default: false
713

814
jobs:
915
tag-images:
@@ -15,11 +21,79 @@ jobs:
1521
steps:
1622
- name: Checkout repository
1723
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
24+
- name: Install crane
25+
run: |
26+
CRANE_VERSION="0.21.5"
27+
curl -fsSL "https://github.com/google/go-containerregistry/releases/download/v${CRANE_VERSION}/go-containerregistry_Linux_x86_64.tar.gz" -o crane.tar.gz
28+
tar -xzf crane.tar.gz crane
29+
sudo mv crane /usr/local/bin/crane
30+
rm crane.tar.gz
1831
- name: Login to ghcr.io
1932
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # 4.1.0
2033
with:
2134
registry: ghcr.io
2235
username: ${{ github.actor }}
2336
password: ${{ secrets.GITHUB_TOKEN }}
37+
- name: Verify external dependency 48h cooldown period
38+
if: ${{ inputs.skip_cooldown != true }}
39+
run: |
40+
COOLDOWN_HOURS=48
41+
COOLDOWN_SECONDS=$((COOLDOWN_HOURS * 3600))
42+
NOW_EPOCH=$(date +%s)
43+
44+
# Collect local build stage names from the Dockerfile
45+
mapfile -t LOCAL_STAGES < <(grep -i ' AS ' Dockerfile | sed 's/.* [Aa][Ss] //' | awk '{print $1}' | sort -u)
46+
47+
# Collect all image references from COPY --from= and FROM directives
48+
mapfile -t ALL_REFS < <({
49+
sed -n 's/.*--from=\([^ ]*\).*/\1/p' Dockerfile
50+
awk '/^FROM/{print $2}' Dockerfile
51+
})
52+
53+
# Filter to only external images
54+
EXTERNAL_IMAGES=()
55+
for ref in "${ALL_REFS[@]}"; do
56+
[[ "$ref" == "scratch" ]] && continue
57+
[[ "$ref" == *'${'* ]] && continue
58+
is_local=false
59+
for stage in "${LOCAL_STAGES[@]}"; do
60+
[[ "$ref" == "$stage" ]] && { is_local=true; break; }
61+
done
62+
$is_local && continue
63+
EXTERNAL_IMAGES+=("$ref")
64+
done
65+
66+
echo "Checking ${#EXTERNAL_IMAGES[@]} external dependencies for ${COOLDOWN_HOURS}h cooldown..."
67+
FAILED=false
68+
for image in "${EXTERNAL_IMAGES[@]}"; do
69+
echo "---"
70+
echo "Checking: ${image}"
71+
IMAGE_CREATED=$(crane config "${image}" | jq -r '.created // empty')
72+
if [[ -z "$IMAGE_CREATED" ]]; then
73+
echo "::error::Could not determine creation time for ${image}"
74+
FAILED=true
75+
continue
76+
fi
77+
IMAGE_EPOCH=$(date -d "$IMAGE_CREATED" +%s) || {
78+
echo "::error::Could not parse creation time '${IMAGE_CREATED}' for ${image}"
79+
FAILED=true
80+
continue
81+
}
82+
AGE_SECONDS=$((NOW_EPOCH - IMAGE_EPOCH))
83+
AGE_HOURS=$((AGE_SECONDS / 3600))
84+
if [[ $AGE_SECONDS -lt $COOLDOWN_SECONDS ]]; then
85+
REMAINING_HOURS=$(( (COOLDOWN_SECONDS - AGE_SECONDS) / 3600 + 1 ))
86+
echo "::error::${image} is only ${AGE_HOURS}h old (created ${IMAGE_CREATED}). Cooldown requires ${COOLDOWN_HOURS}h. Try again in ~${REMAINING_HOURS}h."
87+
FAILED=true
88+
else
89+
echo "OK: ${image} is ${AGE_HOURS}h old (created ${IMAGE_CREATED})"
90+
fi
91+
done
92+
93+
if $FAILED; then
94+
echo "::error::One or more external dependencies have not met the ${COOLDOWN_HOURS}h cooldown period."
95+
exit 1
96+
fi
97+
echo "All external dependencies have met the ${COOLDOWN_HOURS}h cooldown."
2498
- name: Tag images
2599
run: ./build --tag

.github/workflows/update-mirror-digests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939

4040
- name: Install crane
4141
run: |
42-
CRANE_VERSION="0.20.2"
42+
CRANE_VERSION="0.21.5"
4343
curl -fsSL "https://github.com/google/go-containerregistry/releases/download/v${CRANE_VERSION}/go-containerregistry_Linux_x86_64.tar.gz" -o crane.tar.gz
4444
tar -xzf crane.tar.gz crane
4545
sudo mv crane /usr/local/bin/crane

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ ARG LATEST_VERSION
44
FROM eclipse-temurin:${LATEST_VERSION}-jdk-noble AS temurin-latest
55

66
# Intermediate image used to prune cruft from JDKs and squash them all.
7-
FROM ubuntu:latest AS all-jdk
7+
FROM ubuntu:24.04 AS all-jdk
88
ARG LATEST_VERSION
99

1010
RUN <<-EOT

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Image variants are available on a per JDK basis:
1111
- The `zulu8`, `zulu11`, `oracle8`, `ibm8`, `semeru8`, `semeru11`, `semeru17`, `graalvm17`, `graalvm21`, and `graalvm25` variants each contain the base JDKs in addition to the specific JDK from their name.
1212
- The `latest` variant contains the base JDKs and all of the specific JDKs above.
1313

14-
Images are tagged with `ci-` prefixes via the [Tag new images version](https://github.com/DataDog/dd-trace-java-docker-build/actions/workflows/docker-tag.yml) workflow, which runs quarterly on `master` and when manually triggered. On completion, it automatically triggers the [Update mirror digests for ci-* images](https://github.com/DataDog/dd-trace-java-docker-build/actions/workflows/update-mirror-digests.yml) workflow, which opens a PR in [DataDog/images](https://github.com/DataDog/images) updating the pinned `ci-*` mirror image digests. Once that PR is merged, `dd-trace-java` CI picks up the updated images from `registry.ddbuild.io`. Images are mirrored in `registry.ddbuild.io` to ensure they are signed before use in CI.
14+
Images are tagged with `ci-` prefixes via the [Tag new images version](https://github.com/DataDog/dd-trace-java-docker-build/actions/workflows/docker-tag.yml) workflow, which runs quarterly on `master` and when manually triggered. A **48-hour cooldown** is enforced: the workflow verifies that all external upstream dependencies (Eclipse Temurin, Azul Zulu, IBM Semeru, GraalVM, etc.) referenced in the Dockerfile were built at least 48 hours ago before tagging. This ensures that upstream images have had sufficient time for vulnerability scans and community review before being CI use. On completion, it automatically triggers the [Update mirror digests for ci-* images](https://github.com/DataDog/dd-trace-java-docker-build/actions/workflows/update-mirror-digests.yml) workflow, which opens a PR in [DataDog/images](https://github.com/DataDog/images) updating the pinned `ci-*` mirror image digests. Once that PR is merged, `dd-trace-java` CI picks up the updated images from `registry.ddbuild.io`. Images are mirrored in `registry.ddbuild.io` to ensure they are signed before use in CI.
1515

1616
## Development
1717

@@ -40,4 +40,4 @@ To test these images in `dd-trace-java` CI:
4040
5. When the test images look good and `DataDog/dd-trace-java` CI is green, merge your `DataDog/dd-trace-java-docker-build` PR #N.
4141
6. Close the test `DataDog/dd-trace-java` PR.
4242
7. Run the [Delete test image mirror PR](https://github.com/DataDog/dd-trace-java-docker-build/actions/workflows/delete-test-mirror-pr.yml) workflow with `N` to remove the `N_merge-*` images from `DataDog/images`. Confirm that this PR is approved and merged by the `dd-prapprover` bot.
43-
8. Finally, run the [Tag new images version](https://github.com/DataDog/dd-trace-java-docker-build/actions/workflows/docker-tag.yml) workflow. The [Update mirror digests for ci-* images](https://github.com/DataDog/dd-trace-java-docker-build/actions/workflows/update-mirror-digests.yml) workflow will automatically open a PR in `DataDog/images`, updating the pinned `ci-*` digests. `dd-trace-java` CI should automatically pick up these updated images a few minutes after the PR is merged.
43+
8. Finally, run the [Tag new images version](https://github.com/DataDog/dd-trace-java-docker-build/actions/workflows/docker-tag.yml) workflow. **Note:** The workflow enforces a 48-hour cooldown on external upstream dependencies — it will fail if any upstream image (Eclipse Temurin, Azul Zulu, IBM Semeru, GraalVM, etc.) was rebuilt less than 48 hours ago. If the workflow fails, check the logs to see which dependency is too new, and retry after the cooldown has elapsed. This check can also be skipped when manually triggering the workflow. On success, the [Update mirror digests for ci-* images](https://github.com/DataDog/dd-trace-java-docker-build/actions/workflows/update-mirror-digests.yml) workflow will automatically open a PR in `DataDog/images`, updating the pinned `ci-*` digests. `dd-trace-java` CI should automatically pick up these updated images a few minutes after the PR is merged.

0 commit comments

Comments
 (0)