forked from isaac-sim/IsaacLab
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaction.yml
More file actions
340 lines (293 loc) · 14.1 KB
/
Copy pathaction.yml
File metadata and controls
340 lines (293 loc) · 14.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
name: 'ECR Build-Push-Pull'
description: >
Builds a Docker image and pushes it to ECR, using ECR as the layer cache.
If the image already exists in ECR (same tag), pulls it instead of building.
Drop-in replacement for docker-build/action.yml with ECR-backed caching.
inputs:
image-tag:
description: 'Tag for the Docker image (e.g. my-image:latest).'
required: true
isaacsim-base-image:
description: 'IsaacSim base image (passed as ISAACSIM_BASE_IMAGE_ARG build-arg).'
required: true
isaacsim-version:
description: 'IsaacSim version (passed as ISAACSIM_VERSION_ARG build-arg).'
required: true
dockerfile-path:
description: 'Path to Dockerfile, relative to the repository root'
default: 'docker/Dockerfile.base'
required: false
ecr-url:
description: >
ECR repository URL (e.g. "123456789.dkr.ecr.us-west-2.amazonaws.com/my-repo").
Resolved in the following order:
1. ecr-url input, if provided.
2. ECR_CACHE_URL environment variable on the runner.
3. SSM parameter /github-runner/<instance-id>/ecr-cache-url.
4. If still empty, ECR cache is skipped and the image is built locally.
required: false
default: ''
cache-tag:
description: Tag used for the ECR layer cache image (e.g. "cache-base", "cache-curobo").
required: false
default: 'cache'
verify-test-path:
description: >
Path to a test file or directory asserted against a freshly built image, before it is
tagged or pushed. Tests run with IMAGE_TAG set; a failure fails the action with nothing
published, so the next run rebuilds instead of inheriting the bad image from the cache.
Not run on an exact-tag or deps-cache hit: those serve an image that already passed when it
was built.
required: false
default: ''
pull-on-deps-hit:
description: >
Pull the image locally after a deps-cache hit. Needed by jobs that run
the image in later steps of the same job: the ECR login lives in a
temporary docker config that is removed when this action ends, so
steps after the action cannot pull.
Opt-in, unlike the exact-tag hit in step 4b which always pulls: a
deps-cache hit is common on dependency-stable branches, so pulling
unconditionally would download a multi-gigabyte image for the callers
that only need the tag pushed.
required: false
default: 'false'
runs:
using: composite
steps:
##### 1: Setup docker config + Login to nvcr.io #####
# Create a temp docker config with credsStore disabled before any login.
# The runner's credential store backend is broken ("not implemented") and
# causes all docker login calls to fail unless we bypass it upfront.
# The temp config is exported as DOCKER_CONFIG so all subsequent steps
# (including ECR login in step 3) inherit it automatically.
- name: Setup docker config and login to nvcr.io
uses: ./.github/actions/_lib/setup-docker-config
##### 2: Resolve ECR URL #####
# Tries: explicit input >> ECR_CACHE_URL env var >> SSM parameter on EC2.
# Exports ECR_URL to GITHUB_ENV and sets output `available`.
- name: Resolve ECR URL
id: resolve-ecr
shell: bash
env:
INPUT_ECR_URL: ${{ inputs.ecr-url }}
run: |
ECR_URL="${INPUT_ECR_URL:-}"
if [ -z "${ECR_URL}" ]; then
echo "🔵 ecr-url input not set, trying ECR_CACHE_URL env var..."
ECR_URL="${ECR_CACHE_URL:-}"
[ -n "${ECR_URL}" ] && echo "🟢 Using ECR_CACHE_URL env var: ${ECR_URL}"
fi
if [ -z "${ECR_URL}" ]; then
echo "🔵 ECR_CACHE_URL env var not set, trying SSM..."
IMDS_TOKEN=$(curl -sf -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600") || true
INSTANCE_ID=$(curl -sf -H "X-aws-ec2-metadata-token: ${IMDS_TOKEN}" \
"http://169.254.169.254/latest/meta-data/instance-id") || true
INSTANCE_REGION=$(curl -sf -H "X-aws-ec2-metadata-token: ${IMDS_TOKEN}" \
"http://169.254.169.254/latest/meta-data/placement/region") || true
if [ -n "${INSTANCE_ID}" ]; then
ECR_URL=$(aws ssm get-parameter \
--name "/github-runner/${INSTANCE_ID}/ecr-cache-url" \
--region "${INSTANCE_REGION}" \
--query 'Parameter.Value' --output text 2>/dev/null) || ECR_URL=""
if [ -n "${ECR_URL}" ]; then
echo "🟢 Resolved ECR URL from SSM (/github-runner/${INSTANCE_ID}/ecr-cache-url): ${ECR_URL}"
else
echo "🔵 SSM parameter not found for instance ${INSTANCE_ID}"
fi
else
echo "🔵 Not running on EC2 or IMDS unavailable, skipping SSM lookup"
fi
fi
if [ -n "${ECR_URL}" ]; then
echo "ECR_URL=${ECR_URL}" >> "$GITHUB_ENV"
echo "available=true" >> "$GITHUB_OUTPUT"
else
echo "🟠 ECR URL cannot be resolved. Building locally without ECR cache."
fi
##### 3: Setup ECR authentication #####
# Validates the ECR URL, derives ECR image tags, and logs into ECR.
# DOCKER_CONFIG (with credsStore disabled) is already set by step 1.
- name: Setup ECR authentication
if: steps.resolve-ecr.outputs.available == 'true'
shell: bash
run: |
REGISTRY=$(echo "${ECR_URL}" | cut -d'/' -f1)
AWS_REGION=$(echo "${REGISTRY}" | sed 's/.*\.dkr\.ecr\.\(.*\)\.amazonaws\.com/\1/')
if [ "${AWS_REGION}" = "${REGISTRY}" ]; then
echo "🔴 Invalid ECR URL - cannot extract AWS region: ${ECR_URL}"
echo "🔴 Expected format: <account-id>.dkr.ecr.<region>.amazonaws.com/<repo>"
exit 1
fi
ECR_TAG=$(echo "${{ inputs.image-tag }}" | tr ':/' '--')
ECR_IMAGE="${ECR_URL}:${ECR_TAG}"
CACHE_IMAGE="${ECR_URL}:${{ inputs.cache-tag }}"
echo "ECR_IMAGE=${ECR_IMAGE}" >> "$GITHUB_ENV"
echo "CACHE_IMAGE=${CACHE_IMAGE}" >> "$GITHUB_ENV"
echo "🔵 Logging into ECR registry..."
aws ecr get-login-password --region "${AWS_REGION}" | \
docker login --username AWS --password-stdin "${REGISTRY}"
##### 4: Check if exact image exists in ECR #####
# Lightweight manifest check - fetches only the image manifest (~KB),
# not the actual layers. If the exact per-commit image already exists
# in ECR, sets output `hit: true` to skip all subsequent build/push steps.
- name: Check exact image in ECR
id: pull-exact
if: steps.resolve-ecr.outputs.available == 'true'
shell: bash
run: |
echo "🔵 Checking if commit-tagged image exists in ECR >> ${ECR_IMAGE}"
if docker manifest inspect "${ECR_IMAGE}" >/dev/null 2>&1; then
echo "🟢 Commit-tagged image found in ECR, skipping build!"
echo "hit=true" >> "$GITHUB_OUTPUT"
else
echo "🟠 Image ${ECR_IMAGE} not found in ECR, will try deps-cache strategy..."
fi
# Pull the image when the manifest check succeeded but the image is not
# available locally (test jobs need it for `docker run`). Build jobs
# that only push to ECR will already have the image or don't need it.
- name: Pull exact image from ECR
if: steps.pull-exact.outputs.hit == 'true'
shell: bash
run: |
if docker image inspect "${{ inputs.image-tag }}" >/dev/null 2>&1; then
echo "🟢 Image already available locally, skipping pull"
else
echo "🔵 Pulling ${ECR_IMAGE} from ECR..."
docker pull "${ECR_IMAGE}"
docker tag "${ECR_IMAGE}" "${{ inputs.image-tag }}"
echo "🟢 Image pulled and tagged as ${{ inputs.image-tag }}"
fi
##### 5: Check deps cache #####
# Hashes installation-relevant files + the base image digest to produce a stable
# deps-<hash> ECR tag. If the image exists in ECR, the build job succeeds
# immediately and test jobs pull the deps image with a source volume mount.
# Edit DEPS_FILES or DEPS_MANIFEST_PATTERN when install
# inputs change (new packages, new manifests, etc.).
- name: Compute deps hash
id: deps-hash
if: steps.resolve-ecr.outputs.available == 'true' && steps.pull-exact.outputs.hit != 'true'
uses: ./.github/actions/_lib/compute-deps-hash
with:
dockerfile-path: ${{ inputs.dockerfile-path }}
isaacsim-base-image: ${{ inputs.isaacsim-base-image }}
isaacsim-version: ${{ inputs.isaacsim-version }}
- name: Check deps cache
id: deps-cache
if: steps.resolve-ecr.outputs.available == 'true' && steps.pull-exact.outputs.hit != 'true'
shell: bash
run: |
DEPS_HASH="${{ steps.deps-hash.outputs.hash }}"
DEPS_ECR_IMAGE="${ECR_URL}:deps-${DEPS_HASH}"
echo "🔵 Deps hash: ${DEPS_HASH}"
echo "🔵 Checking if deps image ${DEPS_ECR_IMAGE} exists in ECR..."
# Lightweight manifest check - fetches only the image manifest (~KB),
# not the actual layers, so this completes in seconds.
if docker manifest inspect "${DEPS_ECR_IMAGE}" >/dev/null 2>&1; then
echo "🟢 Deps cache HIT!!! Image exists in ECR: ${DEPS_ECR_IMAGE}"
# Create a commit-tagged alias pointing to the same manifest (registry-side,
# no layer download). Test jobs will pull this tag normally.
echo "🔵 Tagging as commit image ${ECR_IMAGE}..."
docker buildx imagetools create -t "${ECR_IMAGE}" "${DEPS_ECR_IMAGE}"
echo "🟢 Tagged ${ECR_IMAGE} >> ${DEPS_ECR_IMAGE}"
echo "deps-cache-hit=true" >> "$GITHUB_OUTPUT"
else
echo "🟠 Deps cache MISS 😿😿😿 (${DEPS_HASH}). Will build now. 🐢🐢🐢"
echo "DEPS_ECR_IMAGE=${DEPS_ECR_IMAGE}" >> "$GITHUB_ENV"
echo "PUSH_DEPS_IMAGE=true" >> "$GITHUB_ENV"
fi
##### 6: Full build (delegated to docker-build) #####
# Runs when neither the exact image nor the deps cache was available.
# docker-build does the actual buildx invocation; we pass ECR layer-cache
# refs and the ECR-prefixed tag so the push steps below have something to
# push.
- name: Full build
if: steps.pull-exact.outputs.hit != 'true' && steps.deps-cache.outputs.deps-cache-hit != 'true'
uses: ./.github/actions/docker-build
with:
image-tag: ${{ inputs.image-tag }}
isaacsim-base-image: ${{ inputs.isaacsim-base-image }}
isaacsim-version: ${{ inputs.isaacsim-version }}
dockerfile-path: ${{ inputs.dockerfile-path }}
cache-from: ${{ steps.resolve-ecr.outputs.available == 'true' && format('type=registry,ref={0}', env.CACHE_IMAGE) || '' }}
cache-to: ${{ steps.resolve-ecr.outputs.available == 'true' && format('type=registry,ref={0},mode=max', env.CACHE_IMAGE) || '' }}
deps-hash: ${{ steps.deps-hash.outputs.hash }}
# Assert against the image while it is only local: the push steps below publish under both
# the commit tag and the deps tag, and a deps-cache hit later serves that image without
# rebuilding it, so anything published unverified stays unverified.
- name: Verify freshly built image
if: >
inputs.verify-test-path != '' &&
steps.pull-exact.outputs.hit != 'true' &&
steps.deps-cache.outputs.deps-cache-hit != 'true'
shell: bash
env:
IMAGE_TAG: ${{ inputs.image-tag }}
TEST_PATH: ${{ inputs.verify-test-path }}
run: |
set -euo pipefail
uv run --no-project --with pytest \
python -m pytest -q "${TEST_PATH}"
- name: Tag built image with ECR-prefixed name
if: >
steps.resolve-ecr.outputs.available == 'true' &&
steps.pull-exact.outputs.hit != 'true' &&
steps.deps-cache.outputs.deps-cache-hit != 'true'
shell: bash
run: |
docker tag "${{ inputs.image-tag }}" "${ECR_IMAGE}"
echo "🟢 Tagged ${ECR_IMAGE}"
##### 7: Push to ECR #####
# Pushes the per-commit ECR image after a successful full build.
# Skipped if the image was pulled in (4).
- name: Push to ECR
if: >
steps.resolve-ecr.outputs.available == 'true' &&
steps.pull-exact.outputs.hit != 'true' &&
steps.deps-cache.outputs.deps-cache-hit != 'true'
shell: bash
run: |
echo "🔵 Pushing ${ECR_IMAGE} to ECR..."
docker push "${ECR_IMAGE}"
echo "🟢 Pushed ${ECR_IMAGE}"
##### 8: Push deps tag #####
# Tags the freshly built image as deps-<hash> so future runs with identical
# install inputs hit the fast path (step 5) instead of doing a full build.
- name: Push deps tag
if: env.PUSH_DEPS_IMAGE == 'true'
shell: bash
run: |
echo "🔵 Pushing deps image for future cache hits: ${DEPS_ECR_IMAGE}"
docker tag "${{ inputs.image-tag }}" "${DEPS_ECR_IMAGE}"
docker push "${DEPS_ECR_IMAGE}"
##### 8b: Pull deps-cached image for same-job use #####
# On a deps-cache hit step 5 only aliases the commit tag registry-side.
# Jobs that docker-run the image in later steps opt in here, while the
# temporary docker config (and its ECR login) still exists.
- name: Pull deps-cached image
if: >
steps.deps-cache.outputs.deps-cache-hit == 'true' &&
inputs.pull-on-deps-hit == 'true'
shell: bash
run: |
if docker image inspect "${{ inputs.image-tag }}" >/dev/null 2>&1; then
echo "🟢 Image already available locally, skipping pull"
else
echo "🔵 Pulling ${ECR_IMAGE} from ECR..."
docker pull "${ECR_IMAGE}"
docker tag "${ECR_IMAGE}" "${{ inputs.image-tag }}"
echo "🟢 Image pulled and tagged as ${{ inputs.image-tag }}"
fi
##### 9: Cleanup docker config #####
- name: Cleanup docker config
if: always()
shell: bash
run: |
if [ -n "${DOCKER_CONFIG}" ] && [ -d "${DOCKER_CONFIG}" ]; then
rm -rf "${DOCKER_CONFIG}"
fi