|
1
|
#!/bin/bash |
|
2
|
# ci-bake-rocm.sh - Docker buildx bake wrapper for ROCm CI builds. |
|
3
|
# |
|
4
|
# The wrapper keeps three build concerns separate: |
|
5
|
# * ci_base builds are content-addressed by vllm.ci_base.content_hash. |
|
6
|
# * test image contents record the commit while runtime tags stay build-scoped. |
|
7
|
# * ROCm install artifacts are uploaded only for targets that export wheels. |
|
8
|
# |
|
9
|
# Usage: |
|
10
|
# ci-bake-rocm.sh [TARGET] |
|
11
|
# |
|
12
|
# Set BAKE_PRINT_ONLY=1 to stop after docker buildx bake --print. |
|
13
|
|
|
14
|
set -euo pipefail |
|
15
|
|
|
16
|
DEFAULT_REPO_SLUG="vllm-project/vllm" |
|
17
|
DEFAULT_CI_HCL_SOURCE="docker/ci-rocm.hcl" |
|
18
|
DEFAULT_CI_BASE_CONTENT_FILES=".dockerignore requirements/common.txt requirements/rocm.txt requirements/test/rocm.txt tools/install_torchcodec_rocm.sh rust-toolchain.toml tests/vllm_test_utils" |
|
19
|
DEFAULT_CI_BASE_DOCKERFILE="docker/Dockerfile.rocm" |
|
20
|
DEFAULT_CI_BASE_DOCKERFILE_STAGES="base rust_toolchain_input_0 rust-toolchain-input rust-toolchain build_nixl lmcache_source build_lmcache build_rocshmem build_deepep mori_base ci_base" |
|
21
|
DEFAULT_CI_BASE_METADATA_VERSION="3" |
|
22
|
# ROCm CI forces REMOTE_VLLM=0, so content identity covers only the selected |
|
23
|
# local-source stages rather than unreachable remote-fetch alternatives. |
|
24
|
DEFAULT_ROCM_CSRC_CONTENT_FILES=".dockerignore requirements/common.txt requirements/rocm.txt pyproject.toml setup.py CMakeLists.txt cmake csrc vllm/envs.py vllm/__init__.py tools/build_rust.py" |
|
25
|
DEFAULT_ROCM_CSRC_DOCKERFILE_STAGES="base fetch_vllm_0 fetch_vllm build_vllm_dependencies rocm-triton-kernels csrc-build" |
|
26
|
DEFAULT_ROCM_RUST_CONTENT_FILES=".dockerignore .git_archival.txt pyproject.toml requirements/build/rust.txt rust/Cargo.lock rust/Cargo.toml rust/proto rust/src rust-toolchain.toml tools/build_rust.py tools/build_rust.sh" |
|
27
|
DEFAULT_ROCM_RUST_DOCKERFILE_STAGES="base fetch_vllm_0 fetch_vllm vllm-version rust_toolchain_input_0 rust-toolchain-input rust_input_0 rust-input rust-toolchain rust-build" |
|
28
|
# Docker's 128-character tag limit minus the longest cache prefix |
|
29
|
# ("csrc-rocm-branch-" and "rust-rocm-branch-", both 17 characters). |
|
30
|
ROCM_CACHE_BRANCH_TAG_MAX_LEN=111 |
|
31
|
CI_BASE_WRITE_SCOPE="" |
|
32
|
|
|
33
|
TARGET="" |
|
34
|
CI_HCL_SOURCE="${CI_HCL_SOURCE:-}" |
|
35
|
CI_HCL_PATH="" |
|
36
|
CI_BASE_LABEL_OVERRIDE_PATH="" |
|
37
|
CSRC_CACHE_OVERRIDE_PATH="" |
|
38
|
ROCM_ARG_OVERRIDE_PATH="" |
|
39
|
BUILD_CONTEXT_OVERRIDE_PATH="" |
|
40
|
SCRIPT_TMP_DIR="" |
|
41
|
BAKE_CONFIG_FILE="" |
|
42
|
ROCM_BUILD_CONTEXT_ROOT="" |
|
43
|
ROCM_BUILD_CONTEXT_INDEX="" |
|
44
|
ROCM_BUILD_CONTEXT_COMMIT="" |
|
45
|
BAKE_FILES=() |
|
46
|
BAKE_ALLOW_ARGS=() |
|
47
|
BAKE_TARGETS=() |
|
48
|
DEPENDENCY_CACHE_TARGETS=() |
|
49
|
|
|
50
|
cleanup() { |
|
51
|
if [[ -n "${SCRIPT_TMP_DIR}" && -d "${SCRIPT_TMP_DIR}" ]]; then |
|
52
|
rm -rf "${SCRIPT_TMP_DIR}" |
|
53
|
fi |
|
54
|
} |
|
55
|
trap cleanup EXIT |
|
56
|
|
|
57
|
clean_docker_tag() { |
|
58
|
local input="$1" |
|
59
|
echo "${input}" | sed 's/[^a-zA-Z0-9._-]/_/g' | cut -c1-128 |
|
60
|
} |
|
61
|
|
|
62
|
is_url_like() { |
|
63
|
local value="${1:-}" |
|
64
|
[[ "${value}" =~ ^[a-zA-Z][a-zA-Z0-9+.-]*:// || "${value}" == git@*:* ]] |
|
65
|
} |
|
66
|
|
|
67
|
is_full_git_sha() { |
|
68
|
local value="${1:-}" |
|
69
|
[[ "${value}" =~ ^[0-9a-fA-F]{40}$ ]] |
|
70
|
} |
|
71
|
|
|
72
|
select_cache_branch_name() { |
|
73
|
local candidate="" |
|
74
|
local var="" |
|
75
|
|
|
76
|
for var in \ |
|
77
|
ROCM_CACHE_BRANCH_NAME \ |
|
78
|
BUILDKITE_PULL_REQUEST_HEAD_BRANCH \ |
|
79
|
BUILDKITE_HEAD_BRANCH \ |
|
80
|
BUILDKITE_BRANCH \ |
|
81
|
VLLM_BRANCH; do |
|
82
|
candidate="${!var:-}" |
|
83
|
[[ -n "${candidate}" ]] || continue |
|
84
|
is_url_like "${candidate}" && continue |
|
85
|
is_full_git_sha "${candidate}" && continue |
|
86
|
printf '%s\n' "${candidate}" |
|
87
|
return 0 |
|
88
|
done |
|
89
|
} |
|
90
|
|
|
91
|
cache_scope_suffix() { |
|
92
|
local arch_hash="" |
|
93
|
arch_hash=$(printf '%s' "${PYTORCH_ROCM_ARCH:-default}" | sha256sum | cut -c1-12) |
|
94
|
printf 'arch-%s\n' "${arch_hash}" |
|
95
|
} |
|
96
|
|
|
97
|
compose_cache_branch_tag() { |
|
98
|
local repo_slug="$1" |
|
99
|
local branch="$2" |
|
100
|
local suffix="" |
|
101
|
local prefix="" |
|
102
|
local max_prefix_len=0 |
|
103
|
local max_tag_len="${ROCM_CACHE_BRANCH_TAG_MAX_LEN}" |
|
104
|
|
|
105
|
suffix="$(cache_scope_suffix)" |
|
106
|
prefix="$(clean_docker_tag "${repo_slug}")-$(clean_docker_tag "${branch}")" |
|
107
|
max_prefix_len=$((max_tag_len - ${#suffix} - 1)) |
|
108
|
if (( max_prefix_len < 1 )); then |
|
109
|
max_prefix_len=1 |
|
110
|
fi |
|
111
|
printf '%s-%s\n' "${prefix:0:${max_prefix_len}}" "${suffix}" |
|
112
|
} |
|
113
|
|
|
114
|
parse_repo_slug() { |
|
115
|
local repo_url="${1:-}" |
|
116
|
local repo_slug="" |
|
117
|
|
|
118
|
if [[ -z "${repo_url}" ]]; then |
|
119
|
printf '%s\n' "${DEFAULT_REPO_SLUG}" |
|
120
|
return 0 |
|
121
|
fi |
|
122
|
|
|
123
|
repo_slug=$(echo "${repo_url}" | sed -E 's#(git@|https?://)([^/:]+)[:/]([^/]+/[^/.]+)(\.git)?$#\3#') |
|
124
|
if [[ "${repo_slug}" != */* ]]; then |
|
125
|
repo_slug="${DEFAULT_REPO_SLUG}" |
|
126
|
fi |
|
127
|
printf '%s\n' "${repo_slug}" |
|
128
|
} |
|
129
|
|
|
130
|
normalize_repo_slug() { |
|
131
|
local repo_slug="${1:-}" |
|
132
|
|
|
133
|
repo_slug="${repo_slug%/}" |
|
134
|
repo_slug="${repo_slug%.git}" |
|
135
|
repo_slug="${repo_slug#https://github.com/}" |
|
136
|
repo_slug="${repo_slug#http://github.com/}" |
|
137
|
repo_slug="${repo_slug#ssh://git@github.com/}" |
|
138
|
repo_slug="${repo_slug#git@github.com:}" |
|
139
|
repo_slug="${repo_slug#github.com/}" |
|
140
|
printf '%s\n' "${repo_slug}" |
|
141
|
} |
|
142
|
|
|
143
|
is_trusted_ci_cache_writer() { |
|
144
|
local actual_repo="" |
|
145
|
local trusted_repo="" |
|
146
|
|
|
147
|
[[ "${BUILDKITE:-false}" == "true" ]] || return 1 |
|
148
|
[[ "${BUILDKITE_PULL_REQUEST:-false}" == "false" ]] || return 1 |
|
149
|
[[ "${BUILDKITE_BRANCH:-}" == "${CI_BASE_STABLE_BRANCH:-main}" ]] || return 1 |
|
150
|
actual_repo=$(normalize_repo_slug "${BUILDKITE_REPO:-}") |
|
151
|
trusted_repo=$(normalize_repo_slug \ |
|
152
|
"${CI_BASE_STABLE_REPO_SLUG:-${DEFAULT_REPO_SLUG}}") |
|
153
|
[[ -n "${actual_repo}" && "${actual_repo}" == "${trusted_repo}" ]] |
|
154
|
} |
|
155
|
|
|
156
|
ci_base_write_scope() { |
|
157
|
local identity="" |
|
158
|
local source_repo="${BUILDKITE_PULL_REQUEST_REPO:-${BUILDKITE_REPO:-local}}" |
|
159
|
|
|
160
|
if is_trusted_ci_cache_writer; then |
|
161
|
return 0 |
|
162
|
fi |
|
163
|
identity=$(printf '%s\n' "${source_repo}" | sha256sum | cut -c1-12) |
|
164
|
printf 'preview-%s\n' "${identity}" |
|
165
|
} |
|
166
|
|
|
167
|
configure_ci_base_write_scope() { |
|
168
|
local scope="" |
|
169
|
|
|
170
|
scope=$(ci_base_write_scope) |
|
171
|
if [[ -n "${scope}" ]]; then |
|
172
|
CI_BASE_WRITE_SCOPE=$(clean_docker_tag "${scope}") |
|
173
|
echo "Non-canonical cache writes use source scope: ${CI_BASE_WRITE_SCOPE}" |
|
174
|
else |
|
175
|
CI_BASE_WRITE_SCOPE="" |
|
176
|
echo "Trusted main build: publishing canonical ci_base refs" |
|
177
|
fi |
|
178
|
export CI_BASE_WRITE_SCOPE |
|
179
|
} |
|
180
|
|
|
181
|
get_buildkite_repo_slug() { |
|
182
|
parse_repo_slug "${BUILDKITE_PULL_REQUEST_REPO:-${BUILDKITE_REPO:-}}" |
|
183
|
} |
|
184
|
|
|
185
|
get_buildkite_target_repo_slug() { |
|
186
|
parse_repo_slug "${BUILDKITE_REPO:-}" |
|
187
|
} |
|
188
|
|
|
189
|
get_buildkite_target_repo_url() { |
|
190
|
local repo_url="${BUILDKITE_REPO:-}" |
|
191
|
|
|
192
|
if [[ -n "${repo_url}" ]] && is_url_like "${repo_url}"; then |
|
193
|
printf '%s\n' "${repo_url}" |
|
194
|
return 0 |
|
195
|
fi |
|
196
|
|
|
197
|
printf 'https://github.com/%s.git\n' "${DEFAULT_REPO_SLUG}" |
|
198
|
} |
|
199
|
|
|
200
|
git_fetch_with_timeout() { |
|
201
|
local timeout_secs="${ROCM_CACHE_GIT_FETCH_TIMEOUT:-60}" |
|
202
|
local -a fetch_command=(git fetch --no-auto-maintenance) |
|
203
|
|
|
204
|
# Detached maintenance can race a later shallow fetch on .git/shallow. |
|
205
|
if command -v timeout >/dev/null 2>&1; then |
|
206
|
timeout "${timeout_secs}s" "${fetch_command[@]}" "$@" |
|
207
|
else |
|
208
|
"${fetch_command[@]}" "$@" |
|
209
|
fi |
|
210
|
} |
|
211
|
|
|
212
|
git_fetch_for_cache() { |
|
213
|
git_fetch_with_timeout "$@" 2>/dev/null |
|
214
|
} |
|
215
|
|
|
216
|
hash_string_short() { |
|
217
|
printf '%s' "$1" | sha256sum | cut -c1-16 |
|
218
|
} |
|
219
|
|
|
220
|
list_content_files() { |
|
221
|
# Hash the checkout inputs Docker can intentionally consume, not ignored |
|
222
|
# compiler/test debris left behind on a reused worker. |
|
223
|
if [[ -n "${ROCM_BUILD_CONTEXT_ROOT:-}" ]]; then |
|
224
|
GIT_INDEX_FILE="${ROCM_BUILD_CONTEXT_INDEX}" \ |
|
225
|
git ls-files -z --cached -- "$1" | LC_ALL=C sort -z |
|
226
|
else |
|
227
|
git ls-files -z --cached --others --exclude-standard -- "$1" \ |
|
228
|
| LC_ALL=C sort -z |
|
229
|
fi |
|
230
|
} |
|
231
|
|
|
232
|
content_regular_file() { |
|
233
|
local file="$1" |
|
234
|
local physical_file="" |
|
235
|
|
|
236
|
physical_file="${file}" |
|
237
|
if [[ -n "${ROCM_BUILD_CONTEXT_ROOT:-}" && "${file}" != /* ]]; then |
|
238
|
physical_file="${ROCM_BUILD_CONTEXT_ROOT}/${file}" |
|
239
|
fi |
|
240
|
[[ -f "${physical_file}" ]] |
|
241
|
} |
|
242
|
|
|
243
|
hash_content_file() { |
|
244
|
local file="$1" |
|
245
|
local physical_file="" |
|
246
|
local checksum="" |
|
247
|
local file_mode="" |
|
248
|
|
|
249
|
physical_file="${file}" |
|
250
|
if [[ -n "${ROCM_BUILD_CONTEXT_ROOT:-}" && "${file}" != /* ]]; then |
|
251
|
physical_file="${ROCM_BUILD_CONTEXT_ROOT}/${file}" |
|
252
|
fi |
|
253
|
if [[ -L "${physical_file}" ]]; then |
|
254
|
printf 'symlink:%s\ntarget:' "${file}" |
|
255
|
readlink -n -- "${physical_file}" || return $? |
|
256
|
printf '\n' |
|
257
|
return |
|
258
|
fi |
|
259
|
if [[ ! -f "${physical_file}" ]]; then |
|
260
|
printf 'missing:%s\n' "${file}" |
|
261
|
return |
|
262
|
fi |
|
263
|
file_mode=$(stat -c '%a' "${physical_file}") || return $? |
|
264
|
printf 'file:%s\nmode:%s\n' "${file}" "${file_mode}" |
|
265
|
checksum=$(sha256sum < "${physical_file}") || return $? |
|
266
|
checksum="${checksum%% *}" |
|
267
|
printf '%s %s\n' "${checksum}" "${file}" |
|
268
|
} |
|
269
|
|
|
270
|
hash_content_directory() { |
|
271
|
if ! list_content_files "$1" | while IFS= read -r -d '' file; do |
|
272
|
hash_content_file "${file}" || exit $? |
|
273
|
done; then |
|
274
|
echo "Failed to hash content under $1" >&2 |
|
275
|
return 1 |
|
276
|
fi |
|
277
|
} |
|
278
|
|
|
279
|
compute_content_hash() { |
|
280
|
local path="" |
|
281
|
local physical_path="" |
|
282
|
|
|
283
|
for path in "$@"; do |
|
284
|
physical_path="${path}" |
|
285
|
if [[ -n "${ROCM_BUILD_CONTEXT_ROOT:-}" && "${path}" != /* ]]; then |
|
286
|
physical_path="${ROCM_BUILD_CONTEXT_ROOT}/${path}" |
|
287
|
fi |
|
288
|
if [[ -L "${physical_path}" || -f "${physical_path}" ]]; then |
|
289
|
hash_content_file "${path}" || return $? |
|
290
|
elif [[ -d "${physical_path}" ]]; then |
|
291
|
hash_content_directory "${path}" || return $? |
|
292
|
else |
|
293
|
printf 'missing:%s\n' "${path}" |
|
294
|
fi |
|
295
|
done | sha256sum | cut -d' ' -f1 |
|
296
|
} |
|
297
|
|
|
298
|
validate_ci_build_context_source() { |
|
299
|
local source_root="$1" |
|
300
|
local context_commit="" |
|
301
|
local source_head="" |
|
302
|
local entry="" |
|
303
|
local metadata="" |
|
304
|
local mode="" |
|
305
|
local stage="" |
|
306
|
local path="" |
|
307
|
local worktree_diff_status=0 |
|
308
|
local staged_diff_status=0 |
|
309
|
local index_modes_file="${SCRIPT_TMP_DIR}/git-index-modes" |
|
310
|
local info_attributes="" |
|
311
|
|
|
312
|
if ! context_commit=$(git -C "${source_root}" rev-parse \ |
|
313
|
--verify "${BUILDKITE_COMMIT:-HEAD}^{commit}"); then |
|
314
|
echo "Failed to resolve the CI Docker context revision" >&2 |
|
315
|
return 1 |
|
316
|
fi |
|
317
|
if ! source_head=$(git -C "${source_root}" rev-parse --verify HEAD); then |
|
318
|
echo "Failed to resolve the checked-out CI revision" >&2 |
|
319
|
return 1 |
|
320
|
fi |
|
321
|
if [[ "${context_commit}" != "${source_head}" ]]; then |
|
322
|
echo "BUILDKITE_COMMIT does not match the checked-out CI revision" >&2 |
|
323
|
return 1 |
|
324
|
fi |
|
325
|
ROCM_BUILD_CONTEXT_COMMIT="${context_commit}" |
|
326
|
# Shared AMD workspaces may present tracked files with inflated executable |
|
327
|
# bits. Context modes come from the pinned Git tree, so ignore only that |
|
328
|
# filesystem drift while continuing to reject content changes. |
|
329
|
git -C "${source_root}" -c core.fileMode=false diff \ |
|
330
|
--quiet --no-ext-diff --ignore-submodules=none -- \ |
|
331
|
|| worktree_diff_status=$? |
|
332
|
if (( worktree_diff_status != 0 )); then |
|
333
|
if (( worktree_diff_status > 1 )); then |
|
334
|
printf 'Failed to inspect tracked CI worktree changes (git diff exited %s)\n' \ |
|
335
|
"${worktree_diff_status}" >&2 |
|
336
|
return 1 |
|
337
|
fi |
|
338
|
echo "Tracked worktree changes cannot be omitted from the CI Docker context" \ |
|
339
|
>&2 |
|
340
|
echo "Tracked worktree diff (first 50 entries):" >&2 |
|
341
|
git -C "${source_root}" -c core.fileMode=false diff \ |
|
342
|
--no-ext-diff --ignore-submodules=none --name-status -- \ |
|
343
|
| sed -n '1,50p' >&2 || true |
|
344
|
git -C "${source_root}" -c core.fileMode=false diff \ |
|
345
|
--no-ext-diff --ignore-submodules=none --summary -- \ |
|
346
|
| sed -n '1,50p' >&2 || true |
|
347
|
return 1 |
|
348
|
fi |
|
349
|
git -C "${source_root}" diff --cached \ |
|
350
|
--quiet --no-ext-diff --ignore-submodules=none HEAD -- \ |
|
351
|
|| staged_diff_status=$? |
|
352
|
if (( staged_diff_status != 0 )); then |
|
353
|
if (( staged_diff_status > 1 )); then |
|
354
|
printf 'Failed to inspect staged CI changes (git diff exited %s)\n' \ |
|
355
|
"${staged_diff_status}" >&2 |
|
356
|
return 1 |
|
357
|
fi |
|
358
|
echo "Staged changes cannot be omitted from the CI Docker context" >&2 |
|
359
|
echo "Staged diff (first 50 entries):" >&2 |
|
360
|
git -C "${source_root}" diff --cached \ |
|
361
|
--no-ext-diff --ignore-submodules=none --name-status HEAD -- \ |
|
362
|
| sed -n '1,50p' >&2 || true |
|
363
|
git -C "${source_root}" diff --cached \ |
|
364
|
--no-ext-diff --ignore-submodules=none --summary HEAD -- \ |
|
365
|
| sed -n '1,50p' >&2 || true |
|
366
|
return 1 |
|
367
|
fi |
|
368
|
# Untracked and ignored worker outputs are intentionally absent: the |
|
369
|
# pinned Git tree, rather than mutable checkout contents, is the contract. |
|
370
|
if ! info_attributes=$(git -C "${source_root}" \ |
|
371
|
rev-parse --path-format=absolute --git-path info/attributes); then |
|
372
|
echo "Failed to locate repository-local Git attributes" >&2 |
|
373
|
return 1 |
|
374
|
fi |
|
375
|
if [[ -s "${info_attributes}" ]]; then |
|
376
|
echo "Repository-local Git attributes cannot define the CI Docker context" \ |
|
377
|
>&2 |
|
378
|
return 1 |
|
379
|
fi |
|
380
|
|
|
381
|
ROCM_BUILD_CONTEXT_INDEX="${SCRIPT_TMP_DIR}/docker-context.index" |
|
382
|
if ! GIT_INDEX_FILE="${ROCM_BUILD_CONTEXT_INDEX}" \ |
|
383
|
git -C "${source_root}" -c core.splitIndex=false \ |
|
384
|
read-tree "${context_commit}^{tree}"; then |
|
385
|
echo "Failed to create the CI Docker context index" >&2 |
|
386
|
return 1 |
|
387
|
fi |
|
388
|
if ! GIT_INDEX_FILE="${ROCM_BUILD_CONTEXT_INDEX}" \ |
|
389
|
git -C "${source_root}" ls-files --stage -z \ |
|
390
|
> "${index_modes_file}"; then |
|
391
|
echo "Failed to read the Git index for the CI Docker context" >&2 |
|
392
|
return 1 |
|
393
|
fi |
|
394
|
while IFS= read -r -d '' entry; do |
|
395
|
metadata="${entry%%$'\t'*}" |
|
396
|
mode="${metadata%% *}" |
|
397
|
stage="${metadata##* }" |
|
398
|
path="${entry#*$'\t'}" |
|
399
|
if [[ "${stage}" != "0" ]]; then |
|
400
|
echo "Unmerged Git entry cannot be used as Docker context: ${path}" >&2 |
|
401
|
return 1 |
|
402
|
fi |
|
403
|
case "${mode}" in |
|
404
|
100644|100755|120000) |
|
405
|
;; |
|
406
|
160000) |
|
407
|
echo "Git submodule cannot be materialized in the CI Docker context: ${path}" \ |
|
408
|
>&2 |
|
409
|
return 1 |
|
410
|
;; |
|
411
|
*) |
|
412
|
echo "Unsupported Git mode ${mode} for ${path}" >&2 |
|
413
|
return 1 |
|
414
|
;; |
|
415
|
esac |
|
416
|
done < "${index_modes_file}" |
|
417
|
} |
|
418
|
|
|
419
|
describe_ci_revision() { |
|
420
|
git -C "$1" describe --tags --long --abbrev=10 \ |
|
421
|
--match 'v[0-9]*' "$2" 2>/dev/null |
|
422
|
} |
|
423
|
|
|
424
|
write_ci_git_archival_metadata() { |
|
425
|
local source_root="$1" |
|
426
|
local context_root="$2" |
|
427
|
local commit="${ROCM_BUILD_CONTEXT_COMMIT}" |
|
428
|
local commit_date="" |
|
429
|
local describe="" |
|
430
|
local is_shallow="false" |
|
431
|
|
|
432
|
commit_date=$(git -C "${source_root}" show -s --format=%cI "${commit}") \ |
|
433
|
|| return $? |
|
434
|
is_shallow=$(git -C "${source_root}" rev-parse --is-shallow-repository) \ |
|
435
|
|| return $? |
|
436
|
if git -C "${source_root}" remote get-url origin >/dev/null 2>&1; then |
|
437
|
# AMD agents use shallow, no-tag clones. Reach a release tag so one |
|
438
|
# commit cannot acquire different versions from different depths. |
|
439
|
# Versioning only needs tags and commits, not historical source trees. |
|
440
|
echo "Synchronizing version tags for the canonical CI Docker context" |
|
441
|
(cd "${source_root}" && git_fetch_with_timeout --quiet \ |
|
442
|
--filter=tree:0 --prune origin '+refs/tags/*:refs/tags/*') \ |
|
443
|
|| return $? |
|
444
|
if [[ "${is_shallow}" == "true" ]] \ |
|
445
|
&& ! describe_ci_revision "${source_root}" "${commit}" >/dev/null; then |
|
446
|
echo "Deepening history to reach a version tag" |
|
447
|
(cd "${source_root}" && git_fetch_with_timeout --quiet \ |
|
448
|
--filter=tree:0 --no-tags --deepen=1000 origin "${commit}") \ |
|
449
|
|| return $? |
|
450
|
is_shallow=$(git -C "${source_root}" \ |
|
451
|
rev-parse --is-shallow-repository) || return $? |
|
452
|
if [[ "${is_shallow}" == "true" ]] \ |
|
453
|
&& ! describe_ci_revision "${source_root}" "${commit}" >/dev/null; then |
|
454
|
echo "No version tag within 1,000 commits; fetching full history" |
|
455
|
(cd "${source_root}" && git_fetch_with_timeout --quiet \ |
|
456
|
--filter=tree:0 --no-tags --unshallow origin "${commit}") \ |
|
457
|
|| return $? |
|
458
|
fi |
|
459
|
fi |
|
460
|
fi |
|
461
|
if ! describe=$(describe_ci_revision "${source_root}" "${commit}"); then |
|
462
|
echo "No numeric version tag is reachable from the CI revision" >&2 |
|
463
|
return 1 |
|
464
|
fi |
|
465
|
if [[ -e "${context_root}/.git_archival.txt" \ |
|
466
|
|| -L "${context_root}/.git_archival.txt" ]]; then |
|
467
|
echo "The source tree already contains .git_archival.txt" >&2 |
|
468
|
return 1 |
|
469
|
fi |
|
470
|
printf 'node: %s\nnode-date: %s\ndescribe-name: %s\n' \ |
|
471
|
"${commit}" "${commit_date}" "${describe}" \ |
|
472
|
> "${context_root}/.git_archival.txt" \ |
|
473
|
&& chmod 0644 -- "${context_root}/.git_archival.txt" |
|
474
|
} |
|
475
|
|
|
476
|
write_build_context_override() { |
|
477
|
local escaped_context="" |
|
478
|
|
|
479
|
[[ -n "${ROCM_BUILD_CONTEXT_ROOT:-}" ]] || return 0 |
|
480
|
escaped_context=$(hcl_escape_string "${ROCM_BUILD_CONTEXT_ROOT}") || return $? |
|
481
|
if ! { |
|
482
|
printf 'target "_common-rocm" {\n' |
|
483
|
printf ' context = "%s"\n' "${escaped_context}" |
|
484
|
printf '}\n' |
|
485
|
} > "${BUILD_CONTEXT_OVERRIDE_PATH}"; then |
|
486
|
echo "Failed to write the CI Docker context override" >&2 |
|
487
|
return 1 |
|
488
|
fi |
|
489
|
BAKE_FILES+=(-f "${BUILD_CONTEXT_OVERRIDE_PATH}") |
|
490
|
} |
|
491
|
|
|
492
|
prepare_ci_build_context() { |
|
493
|
local source_root="" |
|
494
|
local context_root="" |
|
495
|
|
|
496
|
[[ "${BUILDKITE:-false}" == "true" ]] || return 0 |
|
497
|
[[ "${REMOTE_VLLM:-0}" == "0" ]] || return 0 |
|
498
|
|
|
499
|
# BuildKit includes file modes in cache keys. Export the pinned revision to |
|
500
|
# an owned context instead of changing modes in the shared checkout. |
|
501
|
if ! source_root=$(git rev-parse --show-toplevel); then |
|
502
|
echo "Failed to locate the CI source checkout" >&2 |
|
503
|
return 1 |
|
504
|
fi |
|
505
|
validate_ci_build_context_source "${source_root}" || return $? |
|
506
|
context_root="${SCRIPT_TMP_DIR}/docker-context" |
|
507
|
if ! mkdir -m 0700 -- "${context_root}"; then |
|
508
|
echo "Failed to create the owned CI Docker context" >&2 |
|
509
|
return 1 |
|
510
|
fi |
|
511
|
if ! ( |
|
512
|
umask 0022 |
|
513
|
unset GIT_LFS_SKIP_SMUDGE |
|
514
|
export GIT_ATTR_NOSYSTEM=1 |
|
515
|
export GIT_INDEX_FILE="${ROCM_BUILD_CONTEXT_INDEX}" |
|
516
|
git -C "${source_root}" \ |
|
517
|
-c core.attributesFile=/dev/null \ |
|
518
|
-c core.autocrlf=false \ |
|
519
|
-c core.eol=lf \ |
|
520
|
-c core.symlinks=true \ |
|
521
|
--work-tree="${context_root}" \ |
|
522
|
checkout-index --all |
|
523
|
); then |
|
524
|
echo "Failed to materialize the CI Docker context from the Git index" >&2 |
|
525
|
return 1 |
|
526
|
fi |
|
527
|
|
|
528
|
# setuptools-scm understands Git's stable archive format, so wheels and |
|
529
|
# Rust artifacts retain their exact version without copying Git history. |
|
530
|
if ! is_ci_base_target; then |
|
531
|
write_ci_git_archival_metadata "${source_root}" "${context_root}" \ |
|
532
|
|| return $? |
|
533
|
fi |
|
534
|
if [[ -e "${context_root}/.git" ]]; then |
|
535
|
echo "Canonical CI Docker context unexpectedly contains .git" >&2 |
|
536
|
return 1 |
|
537
|
fi |
|
538
|
ROCM_BUILD_CONTEXT_ROOT="${context_root}" |
|
539
|
BAKE_ALLOW_ARGS+=(--allow "fs.read=${ROCM_BUILD_CONTEXT_ROOT}") |
|
540
|
echo "Using canonical CI Docker context: ${ROCM_BUILD_CONTEXT_ROOT}" |
|
541
|
} |
|
542
|
|
|
543
|
configure_custom_rocm_stages() { |
|
544
|
using_custom_rocm_dockerfiles || return 0 |
|
545
|
local context_root="${ROCM_BUILD_CONTEXT_ROOT:-.}" |
|
546
|
local dockerfile="${context_root}/${CI_BASE_DOCKERFILE}" |
|
547
|
local stages="" |
|
548
|
|
|
549
|
(cd "${context_root}" && \ |
|
550
|
validate_rocm_dockerfile "${CI_BASE_DOCKERFILE}" \ |
|
551
|
ci_base test export_vllm export_test_smoke csrc-build rust-build) \ |
|
552
|
|| return $? |
|
553
|
# Hash all stages for custom layouts, including additional helper stages. |
|
554
|
stages=$(rocm_dockerfile_stages "${dockerfile}" | paste -sd ' ') |
|
555
|
CI_BASE_DOCKERFILE_STAGES="${CI_BASE_DOCKERFILE_STAGES:-${stages}}" |
|
556
|
ROCM_CSRC_DOCKERFILE_STAGES="${ROCM_CSRC_DOCKERFILE_STAGES:-${stages}}" |
|
557
|
ROCM_RUST_DOCKERFILE_STAGES="${ROCM_RUST_DOCKERFILE_STAGES:-${stages}}" |
|
558
|
if [[ " ${stages} " != *" build_rocshmem "* ]]; then |
|
559
|
unset ROCSHMEM_BRANCH ROCSHMEM_CACHE_KEY DEEPEP_CACHE_KEY |
|
560
|
fi |
|
561
|
} |
|
562
|
|
|
563
|
compose_dependency_cache_key() { |
|
564
|
local prefix="$1" |
|
565
|
local material="$2" |
|
566
|
local cleaned_prefix="" |
|
567
|
|
|
568
|
cleaned_prefix=$(clean_docker_tag "${prefix}" | cut -c1-96) |
|
569
|
printf '%s-%s\n' "${cleaned_prefix}" "$(hash_string_short "${material}")" |
|
570
|
} |
|
571
|
|
|
572
|
hash_dockerfile_stages() { |
|
573
|
local dockerfile="$1" |
|
574
|
local stages="$2" |
|
575
|
local physical_dockerfile="" |
|
576
|
|
|
577
|
physical_dockerfile="${dockerfile}" |
|
578
|
if [[ -n "${ROCM_BUILD_CONTEXT_ROOT:-}" && "${dockerfile}" != /* ]]; then |
|
579
|
physical_dockerfile="${ROCM_BUILD_CONTEXT_ROOT}/${dockerfile}" |
|
580
|
fi |
|
581
|
|
|
582
|
awk -v wanted_stages="${stages}" ' |
|
583
|
BEGIN { |
|
584
|
split(wanted_stages, stage_list, /[[:space:]]+/) |
|
585
|
for (idx in stage_list) { |
|
586
|
if (stage_list[idx] != "") { |
|
587
|
wanted[stage_list[idx]] = 1 |
|
588
|
} |
|
589
|
} |
|
590
|
emit = 0 |
|
591
|
} |
|
592
|
toupper($1) == "FROM" { |
|
593
|
stage = "" |
|
594
|
for (idx = 1; idx <= NF; idx++) { |
|
595
|
if (tolower($idx) == "as" && idx < NF) { |
|
596
|
stage = tolower($(idx + 1)) |
|
597
|
} |
|
598
|
} |
|
599
|
emit = (stage in wanted) |
|
600
|
} |
|
601
|
emit { |
|
602
|
print |
|
603
|
} |
|
604
|
' "${physical_dockerfile}" |
|
605
|
} |
|
606
|
|
|
607
|
discover_dockerfile_stage_args() { |
|
608
|
local dockerfile="$1" |
|
609
|
local stages="$2" |
|
610
|
local physical_dockerfile="" |
|
611
|
|
|
612
|
physical_dockerfile="${dockerfile}" |
|
613
|
if [[ -n "${ROCM_BUILD_CONTEXT_ROOT:-}" && "${dockerfile}" != /* ]]; then |
|
614
|
physical_dockerfile="${ROCM_BUILD_CONTEXT_ROOT}/${dockerfile}" |
|
615
|
fi |
|
616
|
[[ -f "${physical_dockerfile}" ]] || return 0 |
|
617
|
|
|
618
|
awk -v wanted_stages="${stages}" ' |
|
619
|
function add_arg(name) { |
|
620
|
if (name != "" && !(name in seen)) { |
|
621
|
seen[name] = 1 |
|
622
|
args[++arg_count] = name |
|
623
|
} |
|
624
|
} |
|
625
|
BEGIN { |
|
626
|
split(wanted_stages, stage_list, /[[:space:]]+/) |
|
627
|
for (idx in stage_list) { |
|
628
|
if (stage_list[idx] != "") { |
|
629
|
wanted[stage_list[idx]] = 1 |
|
630
|
} |
|
631
|
} |
|
632
|
emit = 1 |
|
633
|
} |
|
634
|
{ |
|
635
|
line = $0 |
|
636
|
if (toupper($1) == "FROM") { |
|
637
|
stage = "" |
|
638
|
for (idx = 1; idx <= NF; idx++) { |
|
639
|
if (tolower($idx) == "as" && idx < NF) { |
|
640
|
stage = tolower($(idx + 1)) |
|
641
|
} |
|
642
|
} |
|
643
|
emit = (stage in wanted) |
|
644
|
} |
|
645
|
if (emit) { |
|
646
|
lines[++line_count] = line |
|
647
|
} |
|
648
|
} |
|
649
|
END { |
|
650
|
for (idx = 1; idx <= line_count; idx++) { |
|
651
|
line = lines[idx] |
|
652
|
arg_name = line |
|
653
|
sub(/^[[:space:]]*[Aa][Rr][Gg][[:space:]]+/, "", arg_name) |
|
654
|
if (arg_name != line) { |
|
655
|
sub(/[=[:space:]].*/, "", arg_name) |
|
656
|
if (arg_name ~ /^[A-Za-z_][A-Za-z0-9_]*$/) { |
|
657
|
add_arg(arg_name) |
|
658
|
} |
|
659
|
} |
|
660
|
} |
|
661
|
|
|
662
|
for (idx = 1; idx <= line_count; idx++) { |
|
663
|
line = lines[idx] |
|
664
|
for (arg_idx = 1; arg_idx <= arg_count; arg_idx++) { |
|
665
|
name = args[arg_idx] |
|
666
|
if (line ~ "\\$\\{" name "([}:][^}]*)?\\}" \ |
|
667
|
|| line ~ "\\$" name "([^A-Za-z0-9_]|$)") { |
|
668
|
used[name] = 1 |
|
669
|
} |
|
670
|
} |
|
671
|
} |
|
672
|
|
|
673
|
for (arg_idx = 1; arg_idx <= arg_count; arg_idx++) { |
|
674
|
name = args[arg_idx] |
|
675
|
if (used[name]) { |
|
676
|
print name |
|
677
|
} |
|
678
|
} |
|
679
|
} |
|
680
|
' "${physical_dockerfile}" |
|
681
|
} |
|
682
|
|
|
683
|
get_content_arg_names() { |
|
684
|
local dockerfile="$1" |
|
685
|
local stages="$2" |
|
686
|
local explicit_args="${3:-}" |
|
687
|
|
|
688
|
if [[ -n "${explicit_args}" ]]; then |
|
689
|
tr ' ' '\n' <<< "${explicit_args}" |
|
690
|
else |
|
691
|
discover_dockerfile_stage_args "${dockerfile}" "${stages}" |
|
692
|
fi | awk 'NF && !seen[$0]++' |
|
693
|
} |
|
694
|
|
|
695
|
compute_ci_base_content_hash() { |
|
696
|
local -a content_paths=() |
|
697
|
local -a content_args=() |
|
698
|
local content_files_hash="" |
|
699
|
local dockerfile="${CI_BASE_DOCKERFILE:-}" |
|
700
|
local stages="${CI_BASE_DOCKERFILE_STAGES:-}" |
|
701
|
|
|
702
|
read -r -a content_paths <<< "${CI_BASE_CONTENT_FILES}" |
|
703
|
mapfile -t content_args < <( |
|
704
|
get_content_arg_names "${dockerfile}" "${stages}" "${CI_BASE_CONTENT_ARGS:-}" |
|
705
|
) |
|
706
|
if ! content_files_hash=$(compute_content_hash "${content_paths[@]}"); then |
|
707
|
echo "Failed to hash ci_base content files" >&2 |
|
708
|
return 1 |
|
709
|
fi |
|
710
|
|
|
711
|
{ |
|
712
|
printf 'content-files-hash:%s\n' "${content_files_hash}" |
|
713
|
if [[ -n "${dockerfile}" ]]; then |
|
714
|
printf 'dockerfile:%s\n' "${dockerfile}" |
|
715
|
printf 'resolved-build-args:\n' |
|
716
|
hash_dockerfile_arg_values "${dockerfile}" "${content_args[@]}" \ |
|
717
|
|| return 1 |
|
718
|
if [[ -n "${stages}" ]]; then |
|
719
|
printf 'dockerfile-stages:%s\n' "${stages}" |
|
720
|
if content_regular_file "${dockerfile}"; then |
|
721
|
hash_dockerfile_stages "${dockerfile}" "${stages}" |
|
722
|
else |
|
723
|
printf 'missing:%s\n' "${dockerfile}" |
|
724
|
fi |
|
725
|
fi |
|
726
|
fi |
|
727
|
} | sha256sum | cut -d' ' -f1 |
|
728
|
} |
|
729
|
|
|
730
|
extract_dockerfile_arg_default() { |
|
731
|
local dockerfile="$1" |
|
732
|
local arg_name="$2" |
|
733
|
local physical_dockerfile="" |
|
734
|
|
|
735
|
physical_dockerfile="${dockerfile}" |
|
736
|
if [[ -n "${ROCM_BUILD_CONTEXT_ROOT:-}" && "${dockerfile}" != /* ]]; then |
|
737
|
physical_dockerfile="${ROCM_BUILD_CONTEXT_ROOT}/${dockerfile}" |
|
738
|
fi |
|
739
|
sed -n -E "s/^[[:space:]]*[Aa][Rr][Gg][[:space:]]+${arg_name}=\"?([^\"[:space:]]+)\"?.*/\\1/p" \ |
|
740
|
"${physical_dockerfile}" | head -1 |
|
741
|
} |
|
742
|
|
|
743
|
resolve_image_digest() { |
|
744
|
local image_ref="$1" |
|
745
|
local attempts="${ROCM_IMAGE_DIGEST_ATTEMPTS:-4}" |
|
746
|
local delay_secs="${ROCM_IMAGE_DIGEST_RETRY_DELAY:-2}" |
|
747
|
local attempt=0 |
|
748
|
local digest="" |
|
749
|
local output="" |
|
750
|
local status=0 |
|
751
|
|
|
752
|
if [[ "${image_ref}" =~ @(sha256:[0-9a-f]{64})$ ]]; then |
|
753
|
printf '%s\n' "${BASH_REMATCH[1]}" |
|
754
|
return |
|
755
|
fi |
|
756
|
if [[ ! "${attempts}" =~ ^[1-9][0-9]*$ \ |
|
757
|
|| ! "${delay_secs}" =~ ^[0-9]+$ ]]; then |
|
758
|
echo "Invalid image digest retry configuration" >&2 |
|
759
|
return 1 |
|
760
|
fi |
|
761
|
|
|
762
|
for ((attempt = 1; attempt <= attempts; attempt++)); do |
|
763
|
status=0 |
|
764
|
output=$(docker buildx imagetools inspect "${image_ref}" 2>&1) || status=$? |
|
765
|
digest=$(awk '$1 == "Digest:" { print $2; exit }' <<< "${output}") |
|
766
|
if ((status == 0)) && [[ "${digest}" =~ ^sha256:[0-9a-f]{64}$ ]]; then |
|
767
|
printf '%s\n' "${digest}" |
|
768
|
return |
|
769
|
fi |
|
770
|
if ((attempt < attempts)); then |
|
771
|
printf \ |
|
772
|
'Image digest lookup failed for %s (%d/%d, status %d); retrying\n' \ |
|
773
|
"${image_ref}" "${attempt}" "${attempts}" "${status}" >&2 |
|
774
|
sleep "${delay_secs}" |
|
775
|
fi |
|
776
|
done |
|
777
|
|
|
778
|
printf 'Failed to resolve digest for %s (status %d)\n%s\n' \ |
|
779
|
"${image_ref}" "${status}" "${output:-<no output>}" >&2 |
|
780
|
return 1 |
|
781
|
} |
|
782
|
|
|
783
|
resolve_dockerfile_arg_value() { |
|
784
|
local dockerfile="$1" |
|
785
|
local arg_name="$2" |
|
786
|
local env_name="${arg_name}" |
|
787
|
local value="" |
|
788
|
|
|
789
|
case "${arg_name}" in |
|
790
|
ARG_PYTORCH_ROCM_ARCH) |
|
791
|
env_name="PYTORCH_ROCM_ARCH" |
|
792
|
;; |
|
793
|
max_jobs) |
|
794
|
env_name="CI_MAX_JOBS" |
|
795
|
;; |
|
796
|
esac |
|
797
|
|
|
798
|
value="${!env_name:-}" |
|
799
|
if [[ -z "${value}" && "${env_name}" != "${arg_name}" ]]; then |
|
800
|
value="${!arg_name:-}" |
|
801
|
fi |
|
802
|
if [[ -z "${value}" ]] && content_regular_file "${dockerfile}"; then |
|
803
|
value=$(extract_dockerfile_arg_default "${dockerfile}" "${arg_name}") |
|
804
|
fi |
|
805
|
|
|
806
|
printf '%s\n' "${value}" |
|
807
|
} |
|
808
|
|
|
809
|
hash_dockerfile_arg_values() { |
|
810
|
local dockerfile="$1" |
|
811
|
local arg_name="" |
|
812
|
local arg_value="" |
|
813
|
local digest="" |
|
814
|
shift || true |
|
815
|
|
|
816
|
for arg_name in "$@"; do |
|
817
|
[[ -n "${arg_name}" ]] || continue |
|
818
|
arg_value=$(resolve_dockerfile_arg_value "${dockerfile}" "${arg_name}") |
|
819
|
if [[ "${arg_name}" == "BASE_IMAGE" && -n "${arg_value}" ]]; then |
|
820
|
if ! digest=$(resolve_image_digest "${arg_value}"); then |
|
821
|
echo "Failed to resolve digest for BASE_IMAGE=${arg_value}" >&2 |
|
822
|
return 1 |
|
823
|
fi |
|
824
|
printf 'arg:%s.digest=%s\n' "${arg_name}" "${digest}" |
|
825
|
else |
|
826
|
printf 'arg:%s=%s\n' "${arg_name}" "${arg_value:-<empty>}" |
|
827
|
fi |
|
828
|
done |
|
829
|
} |
|
830
|
|
|
831
|
pin_base_image() { |
|
832
|
local dockerfile="${CI_BASE_DOCKERFILE:-${DEFAULT_CI_BASE_DOCKERFILE}}" |
|
833
|
local base_image="" |
|
834
|
local digest="" |
|
835
|
|
|
836
|
base_image=$(resolve_dockerfile_arg_value "${dockerfile}" "BASE_IMAGE") |
|
837
|
[[ -n "${base_image}" ]] || return 0 |
|
838
|
if ! digest=$(resolve_image_digest "${base_image}"); then |
|
839
|
echo "Error: could not resolve base image digest for ${base_image}" >&2 |
|
840
|
echo "Refusing to compute content-addressed cache keys from a mutable tag." >&2 |
|
841
|
return 1 |
|
842
|
fi |
|
843
|
BASE_IMAGE="${base_image%@*}@${digest}" |
|
844
|
export BASE_IMAGE |
|
845
|
echo "Pinned base image for this build: ${BASE_IMAGE}" |
|
846
|
} |
|
847
|
|
|
848
|
is_ci_base_target() { |
|
849
|
[[ "${TARGET}" == *"ci-base-rocm"* ]] |
|
850
|
} |
|
851
|
|
|
852
|
is_commit_image_target() { |
|
853
|
[[ -n "${IMAGE_TAG:-}" && -n "${BUILDKITE_COMMIT:-}" ]] || return 1 |
|
854
|
is_ci_base_target && return 1 |
|
855
|
return 0 |
|
856
|
} |
|
857
|
|
|
858
|
should_upload_wheel_artifacts() { |
|
859
|
[[ "${UPLOAD_ROCM_WHEEL_ARTIFACTS:-0}" == "1" ]] && return 0 |
|
860
|
[[ "${TARGET}" == *"with-wheel"* \ |
|
861
|
|| "${TARGET}" == *"export-wheel"* \ |
|
862
|
|| "${TARGET}" == *"artifact"* ]] |
|
863
|
} |
|
864
|
|
|
865
|
should_export_rocm_smoke() { |
|
866
|
[[ "${TARGET}" == "test-rocm-ci-with-wheel" \ |
|
867
|
|| "${TARGET}" == "smoke-test-rocm-ci" ]] |
|
868
|
} |
|
869
|
|
|
870
|
verify_rocm_smoke_export() { |
|
871
|
local marker="./build/rocm-smoke-export/vllm-smoke-ok" |
|
872
|
local expected_smoke_id="${BUILDKITE_BUILD_ID:-local}" |
|
873
|
local actual_smoke_id="" |
|
874
|
|
|
875
|
should_export_rocm_smoke || return 0 |
|
876
|
if [[ ! -f "${marker}" ]]; then |
|
877
|
echo "ROCm BuildKit smoke marker is missing: ${marker}" >&2 |
|
878
|
return 1 |
|
879
|
fi |
|
880
|
actual_smoke_id="$(< "${marker}")" |
|
881
|
if [[ "${actual_smoke_id}" != "${expected_smoke_id}" ]]; then |
|
882
|
echo "ROCm BuildKit smoke marker belongs to ${actual_smoke_id}, not ${expected_smoke_id}" \ |
|
883
|
>&2 |
|
884
|
return 1 |
|
885
|
fi |
|
886
|
} |
|
887
|
|
|
888
|
get_remote_image_label() { |
|
889
|
local image_ref="$1" |
|
890
|
local label_key="$2" |
|
891
|
local format="{{ index .Image.Config.Labels \"${label_key}\" }}" |
|
892
|
|
|
893
|
docker buildx imagetools inspect "${image_ref}" \ |
|
894
|
--format "${format}" 2>/dev/null | awk 'NF { print; exit }' || true |
|
895
|
} |
|
896
|
|
|
897
|
remote_ci_base_identity_is_current_with_retry() { |
|
898
|
local image_ref="$1" |
|
899
|
local attempts="${CI_BASE_LABEL_ATTEMPTS:-4}" |
|
900
|
local delay_secs="${CI_BASE_LABEL_RETRY_DELAY:-2}" |
|
901
|
local identity="" |
|
902
|
local remote_hash="" |
|
903
|
local remote_version="" |
|
904
|
local content_files_hash="" |
|
905
|
local base_digest="" |
|
906
|
local attempt=0 |
|
907
|
local expected_version="${CI_BASE_METADATA_VERSION:-${DEFAULT_CI_BASE_METADATA_VERSION}}" |
|
908
|
local format='{{ index .Image.Config.Labels "vllm.ci_base.content_hash" }}|{{ index .Image.Config.Labels "vllm.ci_base.metadata_version" }}|{{ index .Image.Config.Labels "vllm.ci_base.content_files_hash" }}|{{ index .Image.Config.Labels "vllm.rocm.base_image_digest" }}' |
|
909
|
|
|
910
|
if [[ ! "${attempts}" =~ ^[1-9][0-9]*$ \ |
|
911
|
|| ! "${delay_secs}" =~ ^[0-9]+$ ]]; then |
|
912
|
echo "Invalid ci_base label retry configuration" >&2 |
|
913
|
return 1 |
|
914
|
fi |
|
915
|
|
|
916
|
for ((attempt = 1; attempt <= attempts; attempt++)); do |
|
917
|
identity=$(docker buildx imagetools inspect "${image_ref}" \ |
|
918
|
--format "${format}" 2>/dev/null || true) |
|
919
|
IFS='|' read -r \ |
|
920
|
remote_hash remote_version content_files_hash base_digest <<< "${identity}" |
|
921
|
if [[ "${remote_hash}" == "${CI_BASE_CONTENT_HASH:-}" \ |
|
922
|
&& "${remote_version}" == "${expected_version}" \ |
|
923
|
&& "${content_files_hash}" =~ ^[0-9a-f]{64}$ \ |
|
924
|
&& "${base_digest}" =~ ^sha256:[0-9a-f]{64}$ ]]; then |
|
925
|
return 0 |
|
926
|
fi |
|
927
|
((attempt == attempts)) || sleep "${delay_secs}" |
|
928
|
done |
|
929
|
|
|
930
|
echo "ci_base identity did not match after ${attempts} attempts: ${image_ref}" >&2 |
|
931
|
echo " expected hash/version: ${CI_BASE_CONTENT_HASH:-<missing>}/${expected_version}" >&2 |
|
932
|
echo " observed hash/version: ${remote_hash:-<missing>}/${remote_version:-<missing>}" >&2 |
|
933
|
return 1 |
|
934
|
} |
|
935
|
|
|
936
|
registry_ref_exists_with_retry() { |
|
937
|
local inspect_kind="$1" |
|
938
|
local image_ref="$2" |
|
939
|
local attempts="${ROCM_REGISTRY_PROBE_ATTEMPTS:-2}" |
|
940
|
local delay_secs="${ROCM_REGISTRY_PROBE_RETRY_DELAY:-1}" |
|
941
|
local output="" |
|
942
|
local status=0 |
|
943
|
local attempt=0 |
|
944
|
|
|
945
|
if [[ ! "${attempts}" =~ ^[1-9][0-9]*$ \ |
|
946
|
|| ! "${delay_secs}" =~ ^[0-9]+$ ]]; then |
|
947
|
echo "Invalid registry probe retry configuration" >&2 |
|
948
|
return 1 |
|
949
|
fi |
|
950
|
|
|
951
|
for ((attempt = 1; attempt <= attempts; attempt++)); do |
|
952
|
status=0 |
|
953
|
if [[ "${inspect_kind}" == "manifest" ]]; then |
|
954
|
output=$(docker manifest inspect "${image_ref}" 2>&1) || status=$? |
|
955
|
else |
|
956
|
output=$(docker buildx imagetools inspect "${image_ref}" 2>&1) \ |
|
957
|
|| status=$? |
|
958
|
fi |
|
959
|
if ((status == 0)); then |
|
960
|
return 0 |
|
961
|
fi |
|
962
|
# A definitive registry miss is normal for a new content key. Retry |
|
963
|
# transport/rate-limit failures, but do not add latency to a real 404. |
|
964
|
if grep -Eiq \ |
|
965
|
'manifest unknown|no such manifest|name unknown|not found|does not exist' \ |
|
966
|
<<< "${output}"; then |
|
967
|
return 1 |
|
968
|
fi |
|
969
|
if ((attempt < attempts)); then |
|
970
|
echo "Registry probe failed for ${image_ref} (${attempt}/${attempts}); retrying" >&2 |
|
971
|
sleep "${delay_secs}" |
|
972
|
fi |
|
973
|
done |
|
974
|
|
|
975
|
echo "Registry probe failed after ${attempts} attempts: ${image_ref}" >&2 |
|
976
|
return 1 |
|
977
|
} |
|
978
|
|
|
979
|
remote_image_exists() { |
|
980
|
registry_ref_exists_with_retry manifest "$1" |
|
981
|
} |
|
982
|
|
|
983
|
use_existing_builder() { |
|
984
|
echo "Using existing builder: ${BUILDER_NAME}" |
|
985
|
docker buildx use "${BUILDER_NAME}" |
|
986
|
docker buildx inspect --bootstrap |
|
987
|
} |
|
988
|
|
|
989
|
buildx_driver() { |
|
990
|
local builder="${1:-}" |
|
991
|
|
|
992
|
if [[ -n "${builder}" ]]; then |
|
993
|
docker buildx inspect "${builder}" 2>/dev/null |
|
994
|
else |
|
995
|
docker buildx inspect 2>/dev/null |
|
996
|
fi | awk -F': *' '$1 == "Driver" { print $2; exit }' |
|
997
|
} |
|
998
|
|
|
999
|
builder_supports_registry_cache() { |
|
1000
|
local driver="$1" |
|
1001
|
|
|
1002
|
[[ -n "${driver}" && "${driver}" != "docker" ]] |
|
1003
|
} |
|
1004
|
|
|
1005
|
create_and_bootstrap_builder() { |
|
1006
|
local driver="$1" |
|
1007
|
local endpoint="${2:-}" |
|
1008
|
|
|
1009
|
echo "Creating builder '${BUILDER_NAME}' with ${driver} driver" |
|
1010
|
if [[ -n "${endpoint}" ]]; then |
|
1011
|
docker buildx create \ |
|
1012
|
--name "${BUILDER_NAME}" \ |
|
1013
|
--driver "${driver}" \ |
|
1014
|
--use \ |
|
1015
|
"${endpoint}" |
|
1016
|
else |
|
1017
|
docker buildx create --name "${BUILDER_NAME}" --driver "${driver}" --use |
|
1018
|
fi |
|
1019
|
docker buildx inspect --bootstrap |
|
1020
|
} |
|
1021
|
|
|
1022
|
init_config() { |
|
1023
|
# shellcheck source=.buildkite/scripts/rocm/build-config.sh |
|
1024
|
source "$(dirname "${BASH_SOURCE[0]}")/rocm/build-config.sh" |
|
1025
|
configure_rocm_build |
|
1026
|
|
|
1027
|
TARGET="${1:-test-ci}" |
|
1028
|
if using_custom_rocm_dockerfiles; then |
|
1029
|
if [[ -z "${BASE_IMAGE:-}" ]]; then |
|
1030
|
echo "Custom ROCm builds require BASE_IMAGE from the selected base handoff" >&2 |
|
1031
|
return 1 |
|
1032
|
fi |
|
1033
|
if ! is_ci_base_target && [[ -z "${CI_BASE_IMAGE:-}" ]]; then |
|
1034
|
echo "Custom ROCm builds require CI_BASE_IMAGE from the selected ci_base handoff" >&2 |
|
1035
|
return 1 |
|
1036
|
fi |
|
1037
|
fi |
|
1038
|
BAKE_TARGETS=("${TARGET}") |
|
1039
|
DEPENDENCY_CACHE_TARGETS=() |
|
1040
|
CI_HCL_SOURCE="${CI_HCL_SOURCE:-${CI_HCL_FILE:-${DEFAULT_CI_HCL_SOURCE}}}" |
|
1041
|
VLLM_BAKE_FILE="${VLLM_BAKE_FILE:-docker/docker-bake-rocm.hcl}" |
|
1042
|
BUILDER_NAME="${BUILDER_NAME:-vllm-builder}" |
|
1043
|
BUILDKIT_SOCKET="${BUILDKIT_SOCKET:-/run/buildkit/buildkitd.sock}" |
|
1044
|
PYTORCH_ROCM_ARCH="${PYTORCH_ROCM_ARCH:-gfx90a;gfx942;gfx950}" |
|
1045
|
CI_BASE_CONTENT_FILES="${CI_BASE_CONTENT_FILES:-${DEFAULT_CI_BASE_CONTENT_FILES}}" |
|
1046
|
CI_BASE_DOCKERFILE="${CI_BASE_DOCKERFILE:-${DEFAULT_CI_BASE_DOCKERFILE}}" |
|
1047
|
if ! using_custom_rocm_dockerfiles; then |
|
1048
|
CI_BASE_DOCKERFILE_STAGES="${CI_BASE_DOCKERFILE_STAGES:-${DEFAULT_CI_BASE_DOCKERFILE_STAGES}}" |
|
1049
|
fi |
|
1050
|
CI_BASE_METADATA_VERSION="${CI_BASE_METADATA_VERSION:-${DEFAULT_CI_BASE_METADATA_VERSION}}" |
|
1051
|
CI_BASE_IMAGE_TAG="${CI_BASE_IMAGE_TAG:-rocm/vllm-dev:ci_base}" |
|
1052
|
export PYTORCH_ROCM_ARCH CI_BASE_DOCKERFILE |
|
1053
|
|
|
1054
|
SCRIPT_TMP_DIR=$(mktemp -d -t ci-bake-rocm.XXXXXX) |
|
1055
|
CI_HCL_PATH="${SCRIPT_TMP_DIR}/ci.hcl" |
|
1056
|
CI_BASE_LABEL_OVERRIDE_PATH="${SCRIPT_TMP_DIR}/ci-base-label-override.hcl" |
|
1057
|
CSRC_CACHE_OVERRIDE_PATH="${SCRIPT_TMP_DIR}/rocm-csrc-cache-override.hcl" |
|
1058
|
ROCM_ARG_OVERRIDE_PATH="${SCRIPT_TMP_DIR}/rocm-arg-override.hcl" |
|
1059
|
BUILD_CONTEXT_OVERRIDE_PATH="${SCRIPT_TMP_DIR}/build-context-override.hcl" |
|
1060
|
BAKE_CONFIG_FILE="bake-config-build-${BUILDKITE_BUILD_NUMBER:-local}.json" |
|
1061
|
} |
|
1062
|
|
|
1063
|
print_header() { |
|
1064
|
echo "--- :docker: Setting up Docker buildx bake" |
|
1065
|
echo "Target: ${TARGET}" |
|
1066
|
echo "CI HCL source: ${CI_HCL_SOURCE}" |
|
1067
|
echo "vLLM bake file: ${VLLM_BAKE_FILE}" |
|
1068
|
if is_ci_base_target; then |
|
1069
|
echo "Build mode: ci_base" |
|
1070
|
elif is_commit_image_target; then |
|
1071
|
echo "Build mode: build-scoped commit image" |
|
1072
|
else |
|
1073
|
echo "Build mode: generic" |
|
1074
|
fi |
|
1075
|
if [[ "${USE_SCCACHE:-0}" == "1" ]]; then |
|
1076
|
echo "Compiler cache: sccache enabled" |
|
1077
|
fi |
|
1078
|
} |
|
1079
|
|
|
1080
|
validate_inputs() { |
|
1081
|
if using_custom_rocm_dockerfiles; then |
|
1082
|
validate_rocm_dockerfile "${ROCM_BASE_DOCKERFILE}" || return $? |
|
1083
|
validate_rocm_dockerfile "${CI_BASE_DOCKERFILE}" \ |
|
1084
|
ci_base test export_vllm export_test_smoke csrc-build rust-build || return $? |
|
1085
|
fi |
|
1086
|
if [[ ! -f "${VLLM_BAKE_FILE}" ]]; then |
|
1087
|
echo "Error: vLLM bake file not found at ${VLLM_BAKE_FILE}" |
|
1088
|
echo "Make sure you're running from the vLLM repository root" |
|
1089
|
exit 1 |
|
1090
|
fi |
|
1091
|
|
|
1092
|
if [[ -n "${CI_HCL_SOURCE:-}" ]] && is_url_like "${CI_HCL_SOURCE}"; then |
|
1093
|
echo "Error: remote CI HCL sources are not supported: ${CI_HCL_SOURCE}" |
|
1094
|
echo "Use the vLLM-owned docker/ci-rocm.hcl or set CI_HCL_SOURCE to a local file." |
|
1095
|
exit 1 |
|
1096
|
fi |
|
1097
|
|
|
1098
|
if [[ -n "${CI_HCL_SOURCE:-}" && ! -f "${CI_HCL_SOURCE}" ]]; then |
|
1099
|
echo "Error: CI HCL file not found at ${CI_HCL_SOURCE}" |
|
1100
|
echo "Set CI_HCL_SOURCE to a local file if you need an override." |
|
1101
|
exit 1 |
|
1102
|
fi |
|
1103
|
} |
|
1104
|
|
|
1105
|
load_ci_hcl() { |
|
1106
|
echo "--- :page_facing_up: Loading ci.hcl" |
|
1107
|
cp "${CI_HCL_SOURCE}" "${CI_HCL_PATH}" |
|
1108
|
echo "Copied ${CI_HCL_SOURCE} to ${CI_HCL_PATH}" |
|
1109
|
} |
|
1110
|
|
|
1111
|
init_bake_files() { |
|
1112
|
BAKE_FILES=(-f "${VLLM_BAKE_FILE}" -f "${CI_HCL_PATH}") |
|
1113
|
if using_custom_rocm_dockerfiles; then |
|
1114
|
# Custom stacks reuse local cache without replacing the |
|
1115
|
# standard ROCm registry caches for the same commit or branch. |
|
1116
|
BAKE_FILES+=(--set '*.cache-to=') |
|
1117
|
fi |
|
1118
|
} |
|
1119
|
|
|
1120
|
compute_ci_base_hash_if_needed() { |
|
1121
|
if [[ -z "${CI_BASE_CONTENT_FILES:-}" ]]; then |
|
1122
|
return 0 |
|
1123
|
fi |
|
1124
|
pin_base_image |
|
1125
|
if ! is_ci_base_target; then |
|
1126
|
return 0 |
|
1127
|
fi |
|
1128
|
if [[ "${REMOTE_VLLM:-0}" != "0" ]]; then |
|
1129
|
echo "Error: content-addressed ci_base builds require REMOTE_VLLM=0" >&2 |
|
1130
|
return 1 |
|
1131
|
fi |
|
1132
|
|
|
1133
|
CI_BASE_CONTENT_HASH=$(compute_ci_base_content_hash) |
|
1134
|
export CI_BASE_CONTENT_HASH |
|
1135
|
echo "ci_base content hash: ${CI_BASE_CONTENT_HASH:0:16}..." |
|
1136
|
} |
|
1137
|
|
|
1138
|
wants_stable_ci_base_tag() { |
|
1139
|
if [[ "${BUILDKITE_PULL_REQUEST:-false}" != "false" ]]; then |
|
1140
|
return 1 |
|
1141
|
fi |
|
1142
|
|
|
1143
|
if [[ "${CI_BASE_PUSH_STABLE_TAG:-}" == "1" ]]; then |
|
1144
|
return 0 |
|
1145
|
fi |
|
1146
|
if [[ "${CI_BASE_PUSH_STABLE_TAG:-}" == "0" ]]; then |
|
1147
|
return 1 |
|
1148
|
fi |
|
1149
|
|
|
1150
|
[[ "${NIGHTLY:-0}" == "1" && "${BUILDKITE_BRANCH:-}" == "${CI_BASE_STABLE_BRANCH:-main}" ]] |
|
1151
|
} |
|
1152
|
|
|
1153
|
trusted_ci_base_tip_matches_build() { |
|
1154
|
local branch="${CI_BASE_STABLE_BRANCH:-main}" |
|
1155
|
local build_commit="${BUILDKITE_COMMIT:-}" |
|
1156
|
local remote_tip="" |
|
1157
|
|
|
1158
|
is_trusted_ci_cache_writer || return 1 |
|
1159
|
if [[ ! "${build_commit}" =~ ^[0-9a-fA-F]{40}$ ]]; then |
|
1160
|
echo "Skipping ci_base stable tag: Buildkite commit is missing or invalid" >&2 |
|
1161
|
return 1 |
|
1162
|
fi |
|
1163
|
remote_tip=$(git ls-remote --exit-code "${BUILDKITE_REPO}" \ |
|
1164
|
"refs/heads/${branch}" 2>/dev/null | awk 'NR == 1 { print $1 }') |
|
1165
|
if [[ ! "${remote_tip}" =~ ^[0-9a-fA-F]{40}$ ]]; then |
|
1166
|
echo "Skipping ci_base stable tag: could not resolve remote ${branch} tip" >&2 |
|
1167
|
return 1 |
|
1168
|
fi |
|
1169
|
if [[ "${remote_tip,,}" != "${build_commit,,}" ]]; then |
|
1170
|
echo "Skipping ci_base stable tag: ${branch} advanced from ${build_commit} to ${remote_tip}" >&2 |
|
1171
|
return 1 |
|
1172
|
fi |
|
1173
|
} |
|
1174
|
|
|
1175
|
should_push_stable_ci_base_tag() { |
|
1176
|
wants_stable_ci_base_tag \ |
|
1177
|
&& is_trusted_ci_cache_writer \ |
|
1178
|
&& trusted_ci_base_tip_matches_build |
|
1179
|
} |
|
1180
|
|
|
1181
|
ci_base_tag_with_suffix() { |
|
1182
|
local base_tag="$1" |
|
1183
|
local suffix="$2" |
|
1184
|
|
|
1185
|
printf '%s-%s\n' "${base_tag}" "$(clean_docker_tag "${suffix}")" |
|
1186
|
} |
|
1187
|
|
|
1188
|
configure_ci_base_image_refs() { |
|
1189
|
local stable_tag="${CI_BASE_IMAGE_TAG:-rocm/vllm-dev:ci_base}" |
|
1190
|
local metadata_version="${CI_BASE_METADATA_VERSION:-${DEFAULT_CI_BASE_METADATA_VERSION}}" |
|
1191
|
local scope="${CI_BASE_WRITE_SCOPE:-}" |
|
1192
|
local trusted_content_tag="" |
|
1193
|
local content_tag="" |
|
1194
|
local build_tag="" |
|
1195
|
local primary_tag="" |
|
1196
|
|
|
1197
|
if [[ ! "${metadata_version}" =~ ^[A-Za-z0-9][A-Za-z0-9_.-]{0,15}$ ]]; then |
|
1198
|
echo "Invalid ci_base metadata version: ${metadata_version}" >&2 |
|
1199
|
return 1 |
|
1200
|
fi |
|
1201
|
|
|
1202
|
if [[ "${BUILDKITE:-false}" == "true" ]] \ |
|
1203
|
&& ! is_full_git_sha "${BUILDKITE_COMMIT:-}"; then |
|
1204
|
echo "Invalid Buildkite commit for ci_base handoff: ${BUILDKITE_COMMIT:-<empty>}" >&2 |
|
1205
|
return 1 |
|
1206
|
fi |
|
1207
|
if [[ "${BUILDKITE:-false}" == "true" \ |
|
1208
|
&& -z "${BUILDKITE_BUILD_ID:-}" ]]; then |
|
1209
|
echo "Buildkite build ID is required for the ci_base runtime handoff" >&2 |
|
1210
|
return 1 |
|
1211
|
fi |
|
1212
|
if [[ -n "${BUILDKITE_BUILD_ID:-}" ]]; then |
|
1213
|
build_tag=$(ci_base_tag_with_suffix \ |
|
1214
|
"${stable_tag}" "build-${BUILDKITE_BUILD_ID}") |
|
1215
|
fi |
|
1216
|
CI_BASE_IMAGE_TAG_BUILD_REF="${build_tag}" |
|
1217
|
export CI_BASE_IMAGE_TAG_BUILD_REF |
|
1218
|
|
|
1219
|
if [[ -z "${CI_BASE_CONTENT_HASH:-}" ]]; then |
|
1220
|
if is_ci_base_target; then |
|
1221
|
echo "Error: ci_base builds require a content hash" >&2 |
|
1222
|
return 1 |
|
1223
|
fi |
|
1224
|
CI_BASE_IMAGE="${CI_BASE_IMAGE:-${stable_tag}}" |
|
1225
|
export CI_BASE_IMAGE |
|
1226
|
return 0 |
|
1227
|
fi |
|
1228
|
|
|
1229
|
trusted_content_tag=$(ci_base_tag_with_suffix \ |
|
1230
|
"${stable_tag}" "v${metadata_version}-${CI_BASE_CONTENT_HASH}") |
|
1231
|
if [[ -n "${scope}" ]]; then |
|
1232
|
content_tag=$(ci_base_tag_with_suffix \ |
|
1233
|
"${stable_tag}" "v${metadata_version}-${scope}-${CI_BASE_CONTENT_HASH}") |
|
1234
|
else |
|
1235
|
content_tag="${trusted_content_tag}" |
|
1236
|
fi |
|
1237
|
CI_BASE_IMAGE_TAG_CONTENT_REF="${content_tag}" |
|
1238
|
CI_BASE_TRUSTED_CONTENT_REF="${trusted_content_tag}" |
|
1239
|
primary_tag="${build_tag:-${content_tag}}" |
|
1240
|
|
|
1241
|
# Main writes canonical content refs. Other builds import those refs |
|
1242
|
# read-only and publish into a source-scoped preview namespace. |
|
1243
|
# A fresh CI build first publishes its unique runtime tag. Content and |
|
1244
|
# stable aliases are created only after that image passes identity checks. |
|
1245
|
CI_BASE_STABLE_PROMOTION_REF="${stable_tag}" |
|
1246
|
CI_BASE_IMAGE_TAG="${primary_tag}" |
|
1247
|
export CI_BASE_IMAGE_TAG |
|
1248
|
export CI_BASE_IMAGE_TAG_CONTENT_REF |
|
1249
|
export CI_BASE_TRUSTED_CONTENT_REF |
|
1250
|
export CI_BASE_STABLE_PROMOTION_REF |
|
1251
|
|
|
1252
|
if is_ci_base_target; then |
|
1253
|
IMAGE_TAG="${primary_tag}" |
|
1254
|
CI_BASE_IMAGE="${primary_tag}" |
|
1255
|
export CI_BASE_IMAGE |
|
1256
|
export IMAGE_TAG |
|
1257
|
|
|
1258
|
echo "ci_base primary image tag: ${CI_BASE_IMAGE_TAG}" |
|
1259
|
if [[ -n "${build_tag}" ]]; then |
|
1260
|
echo "ci_base build image tag: ${build_tag}" |
|
1261
|
fi |
|
1262
|
echo "ci_base content image tag: ${content_tag}" |
|
1263
|
if wants_stable_ci_base_tag && is_trusted_ci_cache_writer; then |
|
1264
|
echo "ci_base stable alias is eligible for post-build promotion: ${stable_tag}" |
|
1265
|
else |
|
1266
|
echo "ci_base stable alias will not be pushed for this build" |
|
1267
|
echo "Set NIGHTLY=1 on ${CI_BASE_STABLE_BRANCH:-main} to refresh ${stable_tag}" |
|
1268
|
fi |
|
1269
|
return 0 |
|
1270
|
fi |
|
1271
|
|
|
1272
|
if [[ -z "${CI_BASE_IMAGE:-}" || "${CI_BASE_IMAGE}" == "${stable_tag}" ]]; then |
|
1273
|
CI_BASE_IMAGE="${content_tag}" |
|
1274
|
export CI_BASE_IMAGE |
|
1275
|
echo "Using ci_base image: ${CI_BASE_IMAGE}" |
|
1276
|
else |
|
1277
|
echo "Using provided CI_BASE_IMAGE override: ${CI_BASE_IMAGE}" |
|
1278
|
fi |
|
1279
|
} |
|
1280
|
|
|
1281
|
publish_ci_base_handoff_ref() { |
|
1282
|
local source_ref="${1:-${CI_BASE_IMAGE_TAG_BUILD_REF:-${CI_BASE_IMAGE_TAG_CONTENT_REF:-}}}" |
|
1283
|
local content_ref="${CI_BASE_IMAGE_TAG_CONTENT_REF:-}" |
|
1284
|
local digest="" |
|
1285
|
local handoff_ref="" |
|
1286
|
|
|
1287
|
is_ci_base_target || return 0 |
|
1288
|
if [[ -z "${source_ref}" || -z "${content_ref}" ]]; then |
|
1289
|
echo "Cannot publish ci_base handoff without source and content refs" >&2 |
|
1290
|
return 1 |
|
1291
|
fi |
|
1292
|
if ! digest=$(resolve_image_digest "${source_ref}"); then |
|
1293
|
echo "Could not resolve immutable ci_base handoff: ${source_ref}" >&2 |
|
1294
|
return 1 |
|
1295
|
fi |
|
1296
|
|
|
1297
|
handoff_ref="${content_ref%@*}@${digest}" |
|
1298
|
if ! confirm_remote_image_push "${handoff_ref}"; then |
|
1299
|
echo "Could not validate immutable ci_base handoff: ${handoff_ref}" >&2 |
|
1300
|
return 1 |
|
1301
|
fi |
|
1302
|
if command -v buildkite-agent >/dev/null 2>&1; then |
|
1303
|
if ! buildkite-agent meta-data set "rocm-ci-base-image" "${handoff_ref}"; then |
|
1304
|
echo "Could not publish required ci_base handoff metadata" >&2 |
|
1305
|
return 1 |
|
1306
|
fi |
|
1307
|
elif [[ "${BUILDKITE:-false}" == "true" ]]; then |
|
1308
|
echo "buildkite-agent not found; cannot publish ci_base handoff" >&2 |
|
1309
|
return 1 |
|
1310
|
fi |
|
1311
|
echo "Published immutable ci_base handoff: ${handoff_ref}" |
|
1312
|
} |
|
1313
|
|
|
1314
|
ci_base_output_refs() { |
|
1315
|
printf '%s\n' \ |
|
1316
|
"${CI_BASE_IMAGE_TAG_CONTENT_REF:-}" \ |
|
1317
|
"${CI_BASE_IMAGE_TAG_BUILD_REF:-}" \ |
|
1318
|
| awk 'NF && !seen[$0]++' |
|
1319
|
} |
|
1320
|
|
|
1321
|
ci_base_candidate_refs() { |
|
1322
|
printf '%s\n' \ |
|
1323
|
"${CI_BASE_TRUSTED_CONTENT_REF:-}" \ |
|
1324
|
"${CI_BASE_IMAGE_TAG_CONTENT_REF:-}" \ |
|
1325
|
"${CI_BASE_STABLE_PROMOTION_REF:-}" \ |
|
1326
|
| awk 'NF && !seen[$0]++' |
|
1327
|
} |
|
1328
|
|
|
1329
|
find_matching_ci_base_ref() { |
|
1330
|
local candidate="" |
|
1331
|
local candidate_digest="" |
|
1332
|
local immutable_candidate="" |
|
1333
|
|
|
1334
|
while IFS= read -r candidate; do |
|
1335
|
[[ -n "${candidate}" ]] || continue |
|
1336
|
# Missing content refs are normal after an input change. Avoid paying |
|
1337
|
# the full digest retry budget for each definitely absent candidate. |
|
1338
|
remote_image_exists "${candidate}" || continue |
|
1339
|
if ! candidate_digest=$(resolve_image_digest "${candidate}"); then |
|
1340
|
echo "Could not pin ci_base candidate: ${candidate}" >&2 |
|
1341
|
continue |
|
1342
|
fi |
|
1343
|
immutable_candidate="${candidate%@*}@${candidate_digest}" |
|
1344
|
if remote_ci_base_identity_is_current_with_retry "${immutable_candidate}"; then |
|
1345
|
printf '%s\n' "${immutable_candidate}" |
|
1346
|
return 0 |
|
1347
|
fi |
|
1348
|
done < <(ci_base_candidate_refs) |
|
1349
|
|
|
1350
|
return 1 |
|
1351
|
} |
|
1352
|
|
|
1353
|
refresh_ci_base_tags_from_ref() { |
|
1354
|
local source_ref="$1" |
|
1355
|
local source_digest="" |
|
1356
|
local immutable_source="" |
|
1357
|
local tag="" |
|
1358
|
local tag_digest="" |
|
1359
|
|
|
1360
|
if ! source_digest=$(resolve_image_digest "${source_ref}"); then |
|
1361
|
echo "Could not resolve selected ci_base image: ${source_ref}" >&2 |
|
1362
|
return 1 |
|
1363
|
fi |
|
1364
|
immutable_source="${source_ref%@*}@${source_digest}" |
|
1365
|
|
|
1366
|
while IFS= read -r tag; do |
|
1367
|
[[ -n "${tag}" ]] || continue |
|
1368
|
[[ "${tag}" != "${source_ref}" ]] || continue |
|
1369
|
tag_digest="" |
|
1370
|
if remote_image_exists "${tag}"; then |
|
1371
|
tag_digest=$(resolve_image_digest "${tag}" || true) |
|
1372
|
fi |
|
1373
|
if [[ "${tag_digest}" == "${source_digest}" ]]; then |
|
1374
|
echo "ci_base tag is already current: ${tag}" |
|
1375
|
continue |
|
1376
|
fi |
|
1377
|
echo "Updating ci_base tag ${tag} -> ${immutable_source}" |
|
1378
|
if ! docker buildx imagetools create --prefer-index=false \ |
|
1379
|
-t "${tag}" "${immutable_source}"; then |
|
1380
|
echo "Failed to update ci_base tag ${tag} from ${immutable_source}" >&2 |
|
1381
|
return 1 |
|
1382
|
fi |
|
1383
|
if ! tag_digest=$(resolve_image_digest "${tag}") \ |
|
1384
|
|| [[ "${tag_digest}" != "${source_digest}" ]]; then |
|
1385
|
echo "Updated ci_base tag does not resolve to the selected digest: ${tag}" >&2 |
|
1386
|
return 1 |
|
1387
|
fi |
|
1388
|
done < <(ci_base_output_refs) |
|
1389
|
} |
|
1390
|
|
|
1391
|
promote_stable_ci_base_tag() { |
|
1392
|
local source_ref="${1:-${CI_BASE_IMAGE_TAG_BUILD_REF:-${CI_BASE_IMAGE_TAG_CONTENT_REF:-}}}" |
|
1393
|
local stable_ref="${CI_BASE_STABLE_PROMOTION_REF:-}" |
|
1394
|
local digest="" |
|
1395
|
local immutable_source="" |
|
1396
|
|
|
1397
|
is_ci_base_target || return 0 |
|
1398
|
wants_stable_ci_base_tag || return 0 |
|
1399
|
if ! should_push_stable_ci_base_tag; then |
|
1400
|
echo "Skipping ci_base stable promotion: build is not the trusted current main tip" |
|
1401
|
return 0 |
|
1402
|
fi |
|
1403
|
if [[ -z "${source_ref}" || -z "${stable_ref}" ]]; then |
|
1404
|
echo "Cannot promote ci_base stable tag without source and destination refs" >&2 |
|
1405
|
return 1 |
|
1406
|
fi |
|
1407
|
if ! digest=$(resolve_image_digest "${source_ref}"); then |
|
1408
|
echo "Could not pin ci_base source before stable promotion: ${source_ref}" >&2 |
|
1409
|
return 1 |
|
1410
|
fi |
|
1411
|
immutable_source="${source_ref%@*}@${digest}" |
|
1412
|
echo "Promoting ci_base stable tag from ${immutable_source}" |
|
1413
|
if ! docker buildx imagetools create --prefer-index=false \ |
|
1414
|
-t "${stable_ref}" "${immutable_source}"; then |
|
1415
|
echo "Failed to promote ci_base stable tag" >&2 |
|
1416
|
return 1 |
|
1417
|
fi |
|
1418
|
if ! confirm_remote_image_push "${stable_ref}"; then |
|
1419
|
echo "Promoted ci_base stable tag did not become visible: ${stable_ref}" >&2 |
|
1420
|
return 1 |
|
1421
|
fi |
|
1422
|
} |
|
1423
|
|
|
1424
|
maybe_reuse_matching_ci_base_ref() { |
|
1425
|
local matching_ref="" |
|
1426
|
|
|
1427
|
matching_ref=$(find_matching_ci_base_ref) || return 1 |
|
1428
|
|
|
1429
|
echo "Found existing ci_base image with matching content hash: ${matching_ref}" |
|
1430
|
if ! refresh_ci_base_tags_from_ref "${matching_ref}"; then |
|
1431
|
echo "ci_base tag refresh failed after finding an exact image; aborting" >&2 |
|
1432
|
return 2 |
|
1433
|
fi |
|
1434
|
if ! promote_stable_ci_base_tag "${matching_ref}"; then |
|
1435
|
echo "ci_base stable promotion failed after finding an exact image; aborting" >&2 |
|
1436
|
return 2 |
|
1437
|
fi |
|
1438
|
if ! publish_ci_base_handoff_ref "${matching_ref}"; then |
|
1439
|
echo "ci_base handoff failed after finding an exact image; aborting" >&2 |
|
1440
|
return 2 |
|
1441
|
fi |
|
1442
|
echo "Content hashes match -- ci_base is current" |
|
1443
|
return 0 |
|
1444
|
} |
|
1445
|
|
|
1446
|
maybe_skip_existing_image() { |
|
1447
|
local remote_revision="" |
|
1448
|
local reuse_status=0 |
|
1449
|
|
|
1450
|
if [[ -z "${IMAGE_TAG:-}" ]]; then |
|
1451
|
return 0 |
|
1452
|
fi |
|
1453
|
|
|
1454
|
if [[ "${FORCE_BUILD:-0}" == "1" ]]; then |
|
1455
|
echo "FORCE_BUILD=1 set; skipping existing-image check" |
|
1456
|
return 0 |
|
1457
|
fi |
|
1458
|
if ! is_ci_base_target \ |
|
1459
|
&& { should_upload_wheel_artifacts || should_export_rocm_smoke; }; then |
|
1460
|
echo "Local-output targets always run for the current build" |
|
1461
|
return 0 |
|
1462
|
fi |
|
1463
|
|
|
1464
|
echo "--- :mag: Checking image tag" |
|
1465
|
echo "Image tag: ${IMAGE_TAG}" |
|
1466
|
|
|
1467
|
if is_ci_base_target && [[ -n "${CI_BASE_CONTENT_HASH:-}" ]]; then |
|
1468
|
maybe_reuse_matching_ci_base_ref || reuse_status=$? |
|
1469
|
case "${reuse_status}" in |
|
1470
|
0) |
|
1471
|
echo "Skipping build" |
|
1472
|
exit 0 |
|
1473
|
;; |
|
1474
|
1) |
|
1475
|
echo "No current ci_base image matched the expected content hash" |
|
1476
|
echo "Proceeding with build" |
|
1477
|
return 0 |
|
1478
|
;; |
|
1479
|
*) |
|
1480
|
return "${reuse_status}" |
|
1481
|
;; |
|
1482
|
esac |
|
1483
|
fi |
|
1484
|
|
|
1485
|
if ! remote_image_exists "${IMAGE_TAG}"; then |
|
1486
|
echo "Image not found, proceeding with build" |
|
1487
|
return 0 |
|
1488
|
fi |
|
1489
|
|
|
1490
|
if is_ci_base_target; then |
|
1491
|
echo "ci_base image already exists and no content hash was configured" |
|
1492
|
echo "Skipping build" |
|
1493
|
exit 0 |
|
1494
|
fi |
|
1495
|
|
|
1496
|
if is_commit_image_target; then |
|
1497
|
remote_revision=$(get_remote_image_label "${IMAGE_TAG}" "org.opencontainers.image.revision") |
|
1498
|
if [[ -n "${remote_revision}" && "${remote_revision}" != "${BUILDKITE_COMMIT}" ]]; then |
|
1499
|
echo "Existing image revision does not match ${BUILDKITE_COMMIT}" |
|
1500
|
echo " found revision: ${remote_revision}" |
|
1501
|
echo "Rebuilding image" |
|
1502
|
return 0 |
|
1503
|
fi |
|
1504
|
|
|
1505
|
echo "Commit image already exists: ${IMAGE_TAG}" |
|
1506
|
echo "Skipping build" |
|
1507
|
exit 0 |
|
1508
|
fi |
|
1509
|
|
|
1510
|
echo "Image already exists: ${IMAGE_TAG}" |
|
1511
|
echo "Skipping build" |
|
1512
|
exit 0 |
|
1513
|
} |
|
1514
|
|
|
1515
|
setup_builder() { |
|
1516
|
echo "--- :buildkite: Setting up buildx builder" |
|
1517
|
|
|
1518
|
local setup_mode="${ROCM_SETUP_BUILDX_BUILDER:-auto}" |
|
1519
|
local current_driver="" |
|
1520
|
local named_driver="" |
|
1521
|
|
|
1522
|
if [[ "${setup_mode}" == "0" || "${setup_mode}" == "false" ]]; then |
|
1523
|
echo "Using current Docker buildx builder" |
|
1524
|
echo "ROCM_SETUP_BUILDX_BUILDER=${setup_mode}; cache exporters may fail if the driver is docker" |
|
1525
|
docker buildx inspect --bootstrap |
|
1526
|
echo "Active builder:" |
|
1527
|
docker buildx ls | grep -E '^\*|^NAME' || docker buildx ls |
|
1528
|
return 0 |
|
1529
|
fi |
|
1530
|
|
|
1531
|
current_driver=$(buildx_driver || true) |
|
1532
|
if [[ "${setup_mode}" != "1" ]] && builder_supports_registry_cache "${current_driver}"; then |
|
1533
|
echo "Using current Docker buildx builder with ${current_driver} driver" |
|
1534
|
docker buildx inspect --bootstrap |
|
1535
|
echo "Active builder:" |
|
1536
|
docker buildx ls | grep -E '^\*|^NAME' || docker buildx ls |
|
1537
|
return 0 |
|
1538
|
fi |
|
1539
|
|
|
1540
|
if [[ "${setup_mode}" != "1" ]]; then |
|
1541
|
echo "Current buildx driver '${current_driver:-unknown}' cannot export registry caches" |
|
1542
|
echo "Creating or using a cache-capable builder: ${BUILDER_NAME}" |
|
1543
|
fi |
|
1544
|
|
|
1545
|
if docker buildx inspect "${BUILDER_NAME}" >/dev/null 2>&1; then |
|
1546
|
named_driver=$(buildx_driver "${BUILDER_NAME}" || true) |
|
1547
|
if ! builder_supports_registry_cache "${named_driver}"; then |
|
1548
|
echo "Builder '${BUILDER_NAME}' uses ${named_driver:-unknown} driver; using ${BUILDER_NAME}-cache instead" |
|
1549
|
BUILDER_NAME="${BUILDER_NAME}-cache" |
|
1550
|
fi |
|
1551
|
fi |
|
1552
|
|
|
1553
|
if [[ -S "${BUILDKIT_SOCKET}" ]]; then |
|
1554
|
echo "Found local buildkitd socket at ${BUILDKIT_SOCKET}" |
|
1555
|
echo "Using remote driver to connect to buildkitd" |
|
1556
|
|
|
1557
|
if docker buildx inspect "${BUILDER_NAME}" >/dev/null 2>&1; then |
|
1558
|
use_existing_builder |
|
1559
|
else |
|
1560
|
create_and_bootstrap_builder remote "unix://${BUILDKIT_SOCKET}" |
|
1561
|
fi |
|
1562
|
elif docker buildx inspect "${BUILDER_NAME}" >/dev/null 2>&1; then |
|
1563
|
use_existing_builder |
|
1564
|
else |
|
1565
|
echo "No local buildkitd found, using docker-container driver" |
|
1566
|
create_and_bootstrap_builder docker-container |
|
1567
|
fi |
|
1568
|
|
|
1569
|
echo "Active builder:" |
|
1570
|
docker buildx ls | grep -E '^\*|^NAME' || docker buildx ls |
|
1571
|
} |
|
1572
|
|
|
1573
|
validate_cache_branch_tag() { |
|
1574
|
local name="$1" |
|
1575
|
local value="$2" |
|
1576
|
|
|
1577
|
if [[ -n "${value}" \ |
|
1578
|
&& (! "${value}" =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]*$ \ |
|
1579
|
|| ${#value} -gt ${ROCM_CACHE_BRANCH_TAG_MAX_LEN}) ]]; then |
|
1580
|
echo "Invalid ${name}; expected a Docker tag component of at most ${ROCM_CACHE_BRANCH_TAG_MAX_LEN} characters" >&2 |
|
1581
|
return 1 |
|
1582
|
fi |
|
1583
|
} |
|
1584
|
|
|
1585
|
prepare_git_cache_metadata() { |
|
1586
|
local cache_branch_name="" |
|
1587
|
local cache_base_branch="${BUILDKITE_PULL_REQUEST_BASE_BRANCH:-main}" |
|
1588
|
local target_repo_slug="" |
|
1589
|
local target_repo_url="" |
|
1590
|
local merge_base_ref="" |
|
1591
|
|
|
1592
|
if is_ci_base_target; then |
|
1593
|
echo "Skipping commit-cache ancestry lookup for content-addressed ci_base" |
|
1594
|
return 0 |
|
1595
|
fi |
|
1596
|
|
|
1597
|
if [[ -z "${PARENT_COMMIT:-}" || -z "${VLLM_MERGE_BASE_COMMIT:-}" ]] \ |
|
1598
|
&& git rev-parse --is-shallow-repository 2>/dev/null | grep -q "true"; then |
|
1599
|
echo "Shallow clone detected - deepening for cache key computation" |
|
1600
|
git_fetch_for_cache --filter=tree:0 --no-tags --deepen=1 \ |
|
1601
|
origin "$(git rev-parse HEAD)" || true |
|
1602
|
fi |
|
1603
|
|
|
1604
|
if [[ -z "${PARENT_COMMIT:-}" ]]; then |
|
1605
|
PARENT_COMMIT=$(git rev-parse HEAD~1 2>/dev/null || echo "") |
|
1606
|
if [[ -n "${PARENT_COMMIT}" ]]; then |
|
1607
|
export PARENT_COMMIT |
|
1608
|
echo "Computed parent commit for cache fallback: ${PARENT_COMMIT}" |
|
1609
|
else |
|
1610
|
echo "Could not determine parent commit" |
|
1611
|
fi |
|
1612
|
else |
|
1613
|
echo "Using provided PARENT_COMMIT: ${PARENT_COMMIT}" |
|
1614
|
fi |
|
1615
|
|
|
1616
|
if [[ -z "${ROCM_CACHE_BRANCH_TAG:-}" ]]; then |
|
1617
|
cache_branch_name=$(select_cache_branch_name) |
|
1618
|
if [[ -z "${cache_branch_name}" && "${BUILDKITE_PULL_REQUEST:-false}" != "false" ]]; then |
|
1619
|
cache_branch_name="pr-${BUILDKITE_PULL_REQUEST}" |
|
1620
|
echo "Using pull request number for ROCm branch cache tag: ${cache_branch_name}" |
|
1621
|
fi |
|
1622
|
fi |
|
1623
|
|
|
1624
|
if [[ -z "${ROCM_CACHE_BRANCH_TAG:-}" && -n "${cache_branch_name}" ]]; then |
|
1625
|
ROCM_CACHE_BRANCH_TAG=$( |
|
1626
|
compose_cache_branch_tag "$(get_buildkite_repo_slug)" "${cache_branch_name}" |
|
1627
|
) |
|
1628
|
export ROCM_CACHE_BRANCH_TAG |
|
1629
|
echo "Computed ROCm branch cache tag: ${ROCM_CACHE_BRANCH_TAG} (from ${cache_branch_name})" |
|
1630
|
elif [[ -n "${ROCM_CACHE_BRANCH_TAG:-}" ]]; then |
|
1631
|
echo "Using provided ROCM_CACHE_BRANCH_TAG: ${ROCM_CACHE_BRANCH_TAG}" |
|
1632
|
elif [[ -n "${BUILDKITE_BRANCH:-}" ]]; then |
|
1633
|
echo "Skipping ROCm branch cache tag: no usable branch name found" |
|
1634
|
echo " BUILDKITE_BRANCH=${BUILDKITE_BRANCH}" |
|
1635
|
fi |
|
1636
|
|
|
1637
|
if [[ -z "${ROCM_CACHE_UPSTREAM_BRANCH_TAG:-}" \ |
|
1638
|
&& -n "${BUILDKITE_PULL_REQUEST_BASE_BRANCH:-}" \ |
|
1639
|
&& "${BUILDKITE_PULL_REQUEST:-false}" != "false" ]]; then |
|
1640
|
target_repo_slug=$(get_buildkite_target_repo_slug) |
|
1641
|
ROCM_CACHE_UPSTREAM_BRANCH_TAG=$( |
|
1642
|
compose_cache_branch_tag "${target_repo_slug}" "${BUILDKITE_PULL_REQUEST_BASE_BRANCH}" |
|
1643
|
) |
|
1644
|
export ROCM_CACHE_UPSTREAM_BRANCH_TAG |
|
1645
|
echo "Computed ROCm upstream branch cache tag: ${ROCM_CACHE_UPSTREAM_BRANCH_TAG}" |
|
1646
|
elif [[ -n "${ROCM_CACHE_UPSTREAM_BRANCH_TAG:-}" ]]; then |
|
1647
|
echo "Using provided ROCM_CACHE_UPSTREAM_BRANCH_TAG: ${ROCM_CACHE_UPSTREAM_BRANCH_TAG}" |
|
1648
|
fi |
|
1649
|
|
|
1650
|
validate_cache_branch_tag \ |
|
1651
|
ROCM_CACHE_BRANCH_TAG "${ROCM_CACHE_BRANCH_TAG:-}" |
|
1652
|
validate_cache_branch_tag \ |
|
1653
|
ROCM_CACHE_UPSTREAM_BRANCH_TAG "${ROCM_CACHE_UPSTREAM_BRANCH_TAG:-}" |
|
1654
|
|
|
1655
|
if [[ -z "${VLLM_MERGE_BASE_COMMIT:-}" ]]; then |
|
1656
|
target_repo_url=$(get_buildkite_target_repo_url) |
|
1657
|
merge_base_ref="refs/remotes/vllm-cache-upstream/${cache_base_branch}" |
|
1658
|
git_fetch_for_cache --no-tags --depth=200 "${target_repo_url}" \ |
|
1659
|
"+refs/heads/${cache_base_branch}:${merge_base_ref}" 2>/dev/null || true |
|
1660
|
VLLM_MERGE_BASE_COMMIT=$(git merge-base HEAD "${merge_base_ref}" 2>/dev/null || echo "") |
|
1661
|
if [[ -z "${VLLM_MERGE_BASE_COMMIT}" ]]; then |
|
1662
|
git_fetch_for_cache --no-tags --deepen=1000 "${target_repo_url}" \ |
|
1663
|
"+refs/heads/${cache_base_branch}:${merge_base_ref}" 2>/dev/null || true |
|
1664
|
VLLM_MERGE_BASE_COMMIT=$(git merge-base HEAD "${merge_base_ref}" 2>/dev/null || echo "") |
|
1665
|
fi |
|
1666
|
if [[ -n "${VLLM_MERGE_BASE_COMMIT}" ]]; then |
|
1667
|
export VLLM_MERGE_BASE_COMMIT |
|
1668
|
echo "Computed merge base commit for cache fallback: ${VLLM_MERGE_BASE_COMMIT}" |
|
1669
|
else |
|
1670
|
echo "Could not determine merge base with ${cache_base_branch}" |
|
1671
|
fi |
|
1672
|
else |
|
1673
|
echo "Using provided VLLM_MERGE_BASE_COMMIT: ${VLLM_MERGE_BASE_COMMIT}" |
|
1674
|
fi |
|
1675
|
} |
|
1676
|
|
|
1677
|
ci_base_metadata_pairs() { |
|
1678
|
local dockerfile="${CI_BASE_DOCKERFILE:-${DEFAULT_CI_BASE_DOCKERFILE}}" |
|
1679
|
local stages="${CI_BASE_DOCKERFILE_STAGES:-${DEFAULT_CI_BASE_DOCKERFILE_STAGES}}" |
|
1680
|
local content_files="${CI_BASE_CONTENT_FILES:-${DEFAULT_CI_BASE_CONTENT_FILES}}" |
|
1681
|
local content_files_hash="" |
|
1682
|
local base_image="" |
|
1683
|
local base_image_digest="" |
|
1684
|
local -a content_paths=() |
|
1685
|
local -a content_args=() |
|
1686
|
|
|
1687
|
read -r -a content_paths <<< "${content_files}" |
|
1688
|
if [[ ${#content_paths[@]} -gt 0 ]]; then |
|
1689
|
if ! content_files_hash=$(compute_content_hash "${content_paths[@]}"); then |
|
1690
|
echo "Failed to hash ci_base metadata content files" >&2 |
|
1691
|
return 1 |
|
1692
|
fi |
|
1693
|
fi |
|
1694
|
mapfile -t content_args < <( |
|
1695
|
get_content_arg_names "${dockerfile}" "${stages}" "${CI_BASE_CONTENT_ARGS:-}" |
|
1696
|
) |
|
1697
|
|
|
1698
|
base_image=$(resolve_dockerfile_arg_value "${dockerfile}" "BASE_IMAGE") |
|
1699
|
if [[ -n "${base_image}" ]]; then |
|
1700
|
if ! base_image_digest=$(resolve_image_digest "${base_image}"); then |
|
1701
|
echo "Failed to resolve ci_base metadata digest for ${base_image}" >&2 |
|
1702
|
return 1 |
|
1703
|
fi |
|
1704
|
fi |
|
1705
|
|
|
1706
|
metadata_pair "vllm.ci_base.metadata_version" "${CI_BASE_METADATA_VERSION:-${DEFAULT_CI_BASE_METADATA_VERSION}}" |
|
1707
|
metadata_pair "vllm.ci_base.content_hash" "${CI_BASE_CONTENT_HASH:-}" |
|
1708
|
metadata_pair "vllm.ci_base.content_files_hash" "${content_files_hash}" |
|
1709
|
metadata_pair "vllm.ci_base.content_files" "${content_files}" |
|
1710
|
metadata_pair "vllm.ci_base.content_args" "$(join_words "${content_args[@]}")" |
|
1711
|
metadata_pair "vllm.ci_base.dockerfile" "${dockerfile}" |
|
1712
|
metadata_pair "vllm.ci_base.dockerfile_stages" "${stages}" |
|
1713
|
|
|
1714
|
# The parent identity is digest-only: mutable aliases that resolve to the |
|
1715
|
# same image must produce byte-identical canonical metadata. |
|
1716
|
metadata_pair "vllm.rocm.base_image" "${base_image_digest}" |
|
1717
|
metadata_pair "vllm.rocm.base_image_digest" "${base_image_digest}" |
|
1718
|
metadata_pair "vllm.rocm.pytorch_rocm_arch" "${PYTORCH_ROCM_ARCH:-}" |
|
1719
|
metadata_pair "vllm.rocm.nic_backend" "$(resolve_dockerfile_arg_value "${dockerfile}" "NIC_BACKEND")" |
|
1720
|
metadata_pair "vllm.rocm.ainic_version" "$(resolve_dockerfile_arg_value "${dockerfile}" "AINIC_VERSION")" |
|
1721
|
metadata_pair "vllm.rocm.ubuntu_codename" "$(resolve_dockerfile_arg_value "${dockerfile}" "UBUNTU_CODENAME")" |
|
1722
|
metadata_pair "vllm.rocm.max_jobs" "$(resolve_dockerfile_arg_value "${dockerfile}" "max_jobs")" |
|
1723
|
metadata_pair "vllm.rocm.nixl_repo" "$(resolve_dockerfile_arg_value "${dockerfile}" "NIXL_REPO")" |
|
1724
|
metadata_pair "vllm.rocm.nixl_commit" "${NIXL_BRANCH:-$(resolve_dockerfile_arg_value "${dockerfile}" "NIXL_BRANCH")}" |
|
1725
|
metadata_pair "vllm.rocm.ucx_repo" "$(resolve_dockerfile_arg_value "${dockerfile}" "UCX_REPO")" |
|
1726
|
metadata_pair "vllm.rocm.ucx_commit" "${UCX_BRANCH:-$(resolve_dockerfile_arg_value "${dockerfile}" "UCX_BRANCH")}" |
|
1727
|
metadata_pair "vllm.rocm.rocshmem_repo" "$(resolve_dockerfile_arg_value "${dockerfile}" "ROCSHMEM_REPO")" |
|
1728
|
metadata_pair "vllm.rocm.rocshmem_commit" "${ROCSHMEM_BRANCH:-$(resolve_dockerfile_arg_value "${dockerfile}" "ROCSHMEM_BRANCH")}" |
|
1729
|
metadata_pair "vllm.rocm.deepep_repo" "$(resolve_dockerfile_arg_value "${dockerfile}" "DEEPEP_REPO")" |
|
1730
|
metadata_pair "vllm.rocm.deepep_commit" "${DEEPEP_BRANCH:-$(resolve_dockerfile_arg_value "${dockerfile}" "DEEPEP_BRANCH")}" |
|
1731
|
metadata_pair "vllm.rocm.deepep_nic" "$(resolve_dockerfile_arg_value "${dockerfile}" "DEEPEP_NIC")" |
|
1732
|
metadata_pair "vllm.rocm.deepep_rocm_arch" "$(resolve_dockerfile_arg_value "${dockerfile}" "DEEPEP_ROCM_ARCH")" |
|
1733
|
metadata_pair "vllm.rocm.nixl_cache_key" "${NIXL_CACHE_KEY:-}" |
|
1734
|
metadata_pair "vllm.rocm.rocshmem_cache_key" "${ROCSHMEM_CACHE_KEY:-}" |
|
1735
|
metadata_pair "vllm.rocm.deepep_cache_key" "${DEEPEP_CACHE_KEY:-}" |
|
1736
|
} |
|
1737
|
|
|
1738
|
write_ci_base_metadata_annotations() { |
|
1739
|
local metadata="$1" |
|
1740
|
local key="" |
|
1741
|
local value="" |
|
1742
|
|
|
1743
|
[[ -n "${metadata}" ]] || return 0 |
|
1744
|
while IFS=$'\t' read -r key value; do |
|
1745
|
[[ -n "${key}" && -n "${value}" ]] || continue |
|
1746
|
printf ' "%s",\n' \ |
|
1747
|
"$(hcl_escape_string "manifest:${key}=${value}")" |
|
1748
|
done <<< "${metadata}" |
|
1749
|
} |
|
1750
|
|
|
1751
|
write_ci_base_metadata_labels() { |
|
1752
|
local metadata="$1" |
|
1753
|
local key="" |
|
1754
|
local value="" |
|
1755
|
|
|
1756
|
[[ -n "${metadata}" ]] || return 0 |
|
1757
|
while IFS=$'\t' read -r key value; do |
|
1758
|
[[ -n "${key}" && -n "${value}" ]] || continue |
|
1759
|
printf ' "%s" = "%s"\n' \ |
|
1760
|
"$(hcl_escape_string "${key}")" \ |
|
1761
|
"$(hcl_escape_string "${value}")" |
|
1762
|
done <<< "${metadata}" |
|
1763
|
} |
|
1764
|
|
|
1765
|
write_ci_base_label_override() { |
|
1766
|
local target_name="" |
|
1767
|
local metadata="" |
|
1768
|
local -a ci_base_targets=() |
|
1769
|
|
|
1770
|
if [[ -z "${CI_BASE_CONTENT_HASH:-}" ]]; then |
|
1771
|
return 0 |
|
1772
|
fi |
|
1773
|
|
|
1774
|
mapfile -t ci_base_targets < <( |
|
1775
|
{ |
|
1776
|
printf '%s\n' "ci-base-rocm" |
|
1777
|
sed -n -E 's/^target "(ci-base-rocm[^"]+)".*/\1/p' "${CI_HCL_PATH}" 2>/dev/null || true |
|
1778
|
} | awk '!seen[$0]++' |
|
1779
|
) |
|
1780
|
|
|
1781
|
if [[ ${#ci_base_targets[@]} -eq 0 ]]; then |
|
1782
|
return 0 |
|
1783
|
fi |
|
1784
|
|
|
1785
|
metadata=$(ci_base_metadata_pairs) |
|
1786
|
|
|
1787
|
: > "${CI_BASE_LABEL_OVERRIDE_PATH}" |
|
1788
|
for target_name in "${ci_base_targets[@]}"; do |
|
1789
|
cat >> "${CI_BASE_LABEL_OVERRIDE_PATH}" <<EOF |
|
1790
|
target "${target_name}" { |
|
1791
|
annotations = [ |
|
1792
|
"manifest:org.opencontainers.image.revision=", |
|
1793
|
EOF |
|
1794
|
write_ci_base_metadata_annotations "${metadata}" >> "${CI_BASE_LABEL_OVERRIDE_PATH}" |
|
1795
|
cat >> "${CI_BASE_LABEL_OVERRIDE_PATH}" <<EOF |
|
1796
|
] |
|
1797
|
labels = { |
|
1798
|
"org.opencontainers.image.revision" = "" |
|
1799
|
EOF |
|
1800
|
write_ci_base_metadata_labels "${metadata}" >> "${CI_BASE_LABEL_OVERRIDE_PATH}" |
|
1801
|
cat >> "${CI_BASE_LABEL_OVERRIDE_PATH}" <<EOF |
|
1802
|
} |
|
1803
|
} |
|
1804
|
|
|
1805
|
EOF |
|
1806
|
done |
|
1807
|
|
|
1808
|
BAKE_FILES+=(-f "${CI_BASE_LABEL_OVERRIDE_PATH}") |
|
1809
|
echo "Appended ci_base metadata label override for targets: ${ci_base_targets[*]}" |
|
1810
|
} |
|
1811
|
|
|
1812
|
uses_rocm_csrc_cache() { |
|
1813
|
case "${TARGET}" in |
|
1814
|
csrc-rocm-ci \ |
|
1815
|
| test-rocm-ci \ |
|
1816
|
| test-rocm-ci-with-wheel \ |
|
1817
|
| test-rocm-ci-with-artifacts \ |
|
1818
|
| export-wheel-rocm \ |
|
1819
|
| smoke-test-rocm-ci) |
|
1820
|
return 0 |
|
1821
|
;; |
|
1822
|
*) |
|
1823
|
return 1 |
|
1824
|
;; |
|
1825
|
esac |
|
1826
|
} |
|
1827
|
|
|
1828
|
uses_rocm_rust_cache() { |
|
1829
|
case "${TARGET}" in |
|
1830
|
rust-rocm-ci \ |
|
1831
|
| test-rocm-ci \ |
|
1832
|
| test-rocm-ci-with-wheel \ |
|
1833
|
| test-rocm-ci-with-artifacts \ |
|
1834
|
| export-wheel-rocm \ |
|
1835
|
| smoke-test-rocm-ci) |
|
1836
|
return 0 |
|
1837
|
;; |
|
1838
|
*) |
|
1839
|
return 1 |
|
1840
|
;; |
|
1841
|
esac |
|
1842
|
} |
|
1843
|
|
|
1844
|
compute_rocm_csrc_content_hash() { |
|
1845
|
local bake_dir="" |
|
1846
|
local dockerfile_rocm="" |
|
1847
|
local content_files="${ROCM_CSRC_CONTENT_FILES:-${DEFAULT_ROCM_CSRC_CONTENT_FILES}}" |
|
1848
|
local stages="${ROCM_CSRC_DOCKERFILE_STAGES:-${DEFAULT_ROCM_CSRC_DOCKERFILE_STAGES}}" |
|
1849
|
local -a content_paths=() |
|
1850
|
local -a content_args=() |
|
1851
|
|
|
1852
|
bake_dir=$(dirname "${VLLM_BAKE_FILE}") |
|
1853
|
dockerfile_rocm="${CI_BASE_DOCKERFILE:-${bake_dir}/Dockerfile.rocm}" |
|
1854
|
read -r -a content_paths <<< "${content_files}" |
|
1855
|
mapfile -t content_args < <( |
|
1856
|
get_content_arg_names "${dockerfile_rocm}" "${stages}" "${ROCM_CSRC_CONTENT_ARGS:-}" |
|
1857
|
) |
|
1858
|
|
|
1859
|
{ |
|
1860
|
printf 'csrc-input-files-hash:%s\n' "$(compute_content_hash "${content_paths[@]}")" |
|
1861
|
printf 'dockerfile:%s\n' "${dockerfile_rocm}" |
|
1862
|
printf 'resolved-build-args:\n' |
|
1863
|
hash_dockerfile_arg_values "${dockerfile_rocm}" "${content_args[@]}" |
|
1864
|
printf 'dockerfile-stages:%s\n' "${stages}" |
|
1865
|
if content_regular_file "${dockerfile_rocm}"; then |
|
1866
|
hash_dockerfile_stages "${dockerfile_rocm}" "${stages}" |
|
1867
|
else |
|
1868
|
printf 'missing:%s\n' "${dockerfile_rocm}" |
|
1869
|
fi |
|
1870
|
} | sha256sum | cut -d' ' -f1 |
|
1871
|
} |
|
1872
|
|
|
1873
|
compute_rocm_csrc_content_hash_if_needed() { |
|
1874
|
local cache_repo="${DOCKERHUB_CACHE_REPO:-rocm/vllm-ci-cache}" |
|
1875
|
local write_scope="${CI_BASE_WRITE_SCOPE:-}" |
|
1876
|
|
|
1877
|
if [[ "${ROCM_CSRC_CONTENT_CACHE:-1}" == "0" ]] || ! uses_rocm_csrc_cache; then |
|
1878
|
return 0 |
|
1879
|
fi |
|
1880
|
|
|
1881
|
ROCM_CSRC_CONTENT_HASH=$(compute_rocm_csrc_content_hash) |
|
1882
|
ROCM_CSRC_TRUSTED_CONTENT_CACHE_REF="${cache_repo}:csrc-rocm-input-${ROCM_CSRC_CONTENT_HASH}" |
|
1883
|
ROCM_CSRC_CONTENT_CACHE_REF="${ROCM_CSRC_TRUSTED_CONTENT_CACHE_REF}" |
|
1884
|
if [[ -n "${write_scope}" ]]; then |
|
1885
|
ROCM_CSRC_CONTENT_CACHE_REF="${ROCM_CSRC_CONTENT_CACHE_REF}-${write_scope}" |
|
1886
|
fi |
|
1887
|
export ROCM_CSRC_CONTENT_HASH |
|
1888
|
export ROCM_CSRC_TRUSTED_CONTENT_CACHE_REF |
|
1889
|
export ROCM_CSRC_CONTENT_CACHE_REF |
|
1890
|
echo "ROCm csrc content cache ref: ${ROCM_CSRC_CONTENT_CACHE_REF}" |
|
1891
|
} |
|
1892
|
|
|
1893
|
compute_rocm_rust_content_hash() { |
|
1894
|
local bake_dir="" |
|
1895
|
local dockerfile_rocm="" |
|
1896
|
local content_files="${ROCM_RUST_CONTENT_FILES:-${DEFAULT_ROCM_RUST_CONTENT_FILES}}" |
|
1897
|
local stages="${ROCM_RUST_DOCKERFILE_STAGES:-${DEFAULT_ROCM_RUST_DOCKERFILE_STAGES}}" |
|
1898
|
local -a content_paths=() |
|
1899
|
local -a content_args=() |
|
1900
|
|
|
1901
|
bake_dir=$(dirname "${VLLM_BAKE_FILE}") |
|
1902
|
dockerfile_rocm="${CI_BASE_DOCKERFILE:-${bake_dir}/Dockerfile.rocm}" |
|
1903
|
read -r -a content_paths <<< "${content_files}" |
|
1904
|
mapfile -t content_args < <( |
|
1905
|
get_content_arg_names \ |
|
1906
|
"${dockerfile_rocm}" "${stages}" "${ROCM_RUST_CONTENT_ARGS:-}" |
|
1907
|
) |
|
1908
|
|
|
1909
|
{ |
|
1910
|
printf 'rust-input-files-hash:%s\n' "$(compute_content_hash "${content_paths[@]}")" |
|
1911
|
printf 'dockerfile:%s\n' "${dockerfile_rocm}" |
|
1912
|
printf 'resolved-build-args:\n' |
|
1913
|
hash_dockerfile_arg_values "${dockerfile_rocm}" "${content_args[@]}" |
|
1914
|
printf 'dockerfile-stages:%s\n' "${stages}" |
|
1915
|
if content_regular_file "${dockerfile_rocm}"; then |
|
1916
|
hash_dockerfile_stages "${dockerfile_rocm}" "${stages}" |
|
1917
|
else |
|
1918
|
printf 'missing:%s\n' "${dockerfile_rocm}" |
|
1919
|
fi |
|
1920
|
} | sha256sum | cut -d' ' -f1 |
|
1921
|
} |
|
1922
|
|
|
1923
|
compute_rocm_rust_content_hash_if_needed() { |
|
1924
|
local cache_repo="${DOCKERHUB_CACHE_REPO:-rocm/vllm-ci-cache}" |
|
1925
|
local write_scope="${CI_BASE_WRITE_SCOPE:-}" |
|
1926
|
|
|
1927
|
if [[ "${ROCM_RUST_CONTENT_CACHE:-1}" == "0" ]] || ! uses_rocm_rust_cache; then |
|
1928
|
return 0 |
|
1929
|
fi |
|
1930
|
|
|
1931
|
ROCM_RUST_CONTENT_HASH=$(compute_rocm_rust_content_hash) |
|
1932
|
ROCM_RUST_TRUSTED_CONTENT_CACHE_REF="${cache_repo}:rust-rocm-input-${ROCM_RUST_CONTENT_HASH}" |
|
1933
|
ROCM_RUST_CONTENT_CACHE_REF="${ROCM_RUST_TRUSTED_CONTENT_CACHE_REF}" |
|
1934
|
if [[ -n "${write_scope}" ]]; then |
|
1935
|
ROCM_RUST_CONTENT_CACHE_REF="${ROCM_RUST_CONTENT_CACHE_REF}-${write_scope}" |
|
1936
|
fi |
|
1937
|
export ROCM_RUST_CONTENT_HASH |
|
1938
|
export ROCM_RUST_TRUSTED_CONTENT_CACHE_REF |
|
1939
|
export ROCM_RUST_CONTENT_CACHE_REF |
|
1940
|
echo "ROCm Rust content cache ref: ${ROCM_RUST_CONTENT_CACHE_REF}" |
|
1941
|
} |
|
1942
|
|
|
1943
|
write_hcl_string_list_entries() { |
|
1944
|
local indent="$1" |
|
1945
|
local value="" |
|
1946
|
shift |
|
1947
|
|
|
1948
|
for value in "$@"; do |
|
1949
|
value="${value//\\/\\\\}" |
|
1950
|
value="${value//\"/\\\"}" |
|
1951
|
printf '%s"%s",\n' "${indent}" "${value}" |
|
1952
|
done |
|
1953
|
} |
|
1954
|
|
|
1955
|
hcl_escape_string() { |
|
1956
|
local value="$1" |
|
1957
|
|
|
1958
|
value="${value//\\/\\\\}" |
|
1959
|
value="${value//\"/\\\"}" |
|
1960
|
printf '%s' "${value}" |
|
1961
|
} |
|
1962
|
|
|
1963
|
join_words() { |
|
1964
|
local IFS=" " |
|
1965
|
printf '%s' "$*" |
|
1966
|
} |
|
1967
|
|
|
1968
|
metadata_pair() { |
|
1969
|
local key="$1" |
|
1970
|
local value="${2:-}" |
|
1971
|
|
|
1972
|
printf '%s\t%s\n' "${key}" "${value}" |
|
1973
|
} |
|
1974
|
|
|
1975
|
write_hcl_string_list() { |
|
1976
|
local indent="$1" |
|
1977
|
shift |
|
1978
|
|
|
1979
|
printf '%s[\n' "${indent}" |
|
1980
|
write_hcl_string_list_entries "${indent} " "$@" |
|
1981
|
printf '%s]\n' "${indent}" |
|
1982
|
} |
|
1983
|
|
|
1984
|
write_rocm_build_arg_override() { |
|
1985
|
local bake_dir="" |
|
1986
|
local dockerfile_rocm="" |
|
1987
|
local -a arg_names=() |
|
1988
|
local arg_name="" |
|
1989
|
local arg_value="" |
|
1990
|
|
|
1991
|
bake_dir=$(dirname "${VLLM_BAKE_FILE}") |
|
1992
|
dockerfile_rocm="${CI_BASE_DOCKERFILE:-${bake_dir}/Dockerfile.rocm}" |
|
1993
|
mapfile -t arg_names < <( |
|
1994
|
{ |
|
1995
|
get_content_arg_names \ |
|
1996
|
"${dockerfile_rocm}" \ |
|
1997
|
"${CI_BASE_DOCKERFILE_STAGES:-${DEFAULT_CI_BASE_DOCKERFILE_STAGES}}" \ |
|
1998
|
"${CI_BASE_CONTENT_ARGS:-}" |
|
1999
|
get_content_arg_names \ |
|
2000
|
"${dockerfile_rocm}" \ |
|
2001
|
"${ROCM_CSRC_DOCKERFILE_STAGES:-${DEFAULT_ROCM_CSRC_DOCKERFILE_STAGES}}" \ |
|
2002
|
"${ROCM_CSRC_CONTENT_ARGS:-}" |
|
2003
|
get_content_arg_names \ |
|
2004
|
"${dockerfile_rocm}" \ |
|
2005
|
"${ROCM_RUST_DOCKERFILE_STAGES:-${DEFAULT_ROCM_RUST_DOCKERFILE_STAGES}}" \ |
|
2006
|
"${ROCM_RUST_CONTENT_ARGS:-}" |
|
2007
|
} | awk 'NF && !seen[$0]++' |
|
2008
|
) |
|
2009
|
|
|
2010
|
{ |
|
2011
|
cat <<EOF |
|
2012
|
target "_common-rocm" { |
|
2013
|
args = { |
|
2014
|
EOF |
|
2015
|
for arg_name in "${arg_names[@]}"; do |
|
2016
|
[[ -n "${arg_name}" ]] || continue |
|
2017
|
arg_value=$(resolve_dockerfile_arg_value "${dockerfile_rocm}" "${arg_name}") |
|
2018
|
[[ -n "${arg_value}" ]] || continue |
|
2019
|
printf ' %s = "%s"\n' "${arg_name}" "$(hcl_escape_string "${arg_value}")" |
|
2020
|
done |
|
2021
|
cat <<EOF |
|
2022
|
} |
|
2023
|
} |
|
2024
|
EOF |
|
2025
|
} > "${ROCM_ARG_OVERRIDE_PATH}" |
|
2026
|
|
|
2027
|
BAKE_FILES+=(-f "${ROCM_ARG_OVERRIDE_PATH}") |
|
2028
|
echo "Appended resolved ROCm Docker ARG override" |
|
2029
|
} |
|
2030
|
|
|
2031
|
write_hcl_string_list_attr() { |
|
2032
|
local indent="$1" |
|
2033
|
local attr="$2" |
|
2034
|
shift 2 |
|
2035
|
|
|
2036
|
printf '%s%s = [\n' "${indent}" "${attr}" |
|
2037
|
write_hcl_string_list_entries "${indent} " "$@" |
|
2038
|
printf '%s]\n' "${indent}" |
|
2039
|
} |
|
2040
|
|
|
2041
|
validate_cache_export_mode() { |
|
2042
|
local mode="$1" |
|
2043
|
local env_name="$2" |
|
2044
|
|
|
2045
|
case "${mode}" in |
|
2046
|
min|max) |
|
2047
|
;; |
|
2048
|
*) |
|
2049
|
echo "Error: ${env_name} must be one of: min, max" |
|
2050
|
exit 1 |
|
2051
|
;; |
|
2052
|
esac |
|
2053
|
} |
|
2054
|
|
|
2055
|
validate_content_cache_export_mode() { |
|
2056
|
local mode="$1" |
|
2057
|
local env_name="$2" |
|
2058
|
|
|
2059
|
case "${mode}" in |
|
2060
|
missing|always|never) |
|
2061
|
;; |
|
2062
|
*) |
|
2063
|
echo "Error: ${env_name} must be one of: missing, always, never" |
|
2064
|
exit 1 |
|
2065
|
;; |
|
2066
|
esac |
|
2067
|
} |
|
2068
|
|
|
2069
|
should_export_content_cache_ref() { |
|
2070
|
local cache_ref="$1" |
|
2071
|
local cache_name="$2" |
|
2072
|
local trusted_ref="${3:-${cache_ref}}" |
|
2073
|
local mode="${ROCM_CONTENT_CACHE_EXPORT_MODE:-missing}" |
|
2074
|
|
|
2075
|
case "${mode}" in |
|
2076
|
always) |
|
2077
|
echo "${cache_name} content cache export mode is always" |
|
2078
|
return 0 |
|
2079
|
;; |
|
2080
|
never) |
|
2081
|
echo "${cache_name} content cache export mode is never" |
|
2082
|
return 1 |
|
2083
|
;; |
|
2084
|
missing|"") |
|
2085
|
if registry_ref_exists_with_retry imagetools "${trusted_ref}"; then |
|
2086
|
echo "${cache_name} trusted content cache is visible: ${trusted_ref}" |
|
2087
|
return 1 |
|
2088
|
fi |
|
2089
|
if [[ "${cache_ref}" != "${trusted_ref}" ]] \ |
|
2090
|
&& registry_ref_exists_with_retry imagetools "${cache_ref}"; then |
|
2091
|
echo "${cache_name} scoped content cache is visible: ${cache_ref}" |
|
2092
|
return 1 |
|
2093
|
fi |
|
2094
|
echo "${cache_name} content cache is missing: ${cache_ref}" |
|
2095
|
return 0 |
|
2096
|
;; |
|
2097
|
*) |
|
2098
|
echo "Error: ROCM_CONTENT_CACHE_EXPORT_MODE must be one of: missing, always, never" |
|
2099
|
exit 1 |
|
2100
|
;; |
|
2101
|
esac |
|
2102
|
} |
|
2103
|
|
|
2104
|
write_rocm_cache_override() { |
|
2105
|
local cache_repo="${DOCKERHUB_CACHE_REPO:-rocm/vllm-ci-cache}" |
|
2106
|
local content_cache_export_mode="${ROCM_CONTENT_CACHE_EXPORT_MODE:-missing}" |
|
2107
|
local csrc_cache_to_mode="${ROCM_CSRC_CACHE_TO_MODE:-max}" |
|
2108
|
local rust_cache_to_mode="${ROCM_RUST_CACHE_TO_MODE:-max}" |
|
2109
|
local rocm_cache_to_mode="${ROCM_FINAL_CACHE_TO_MODE:-min}" |
|
2110
|
local -a csrc_content_cache_from=() |
|
2111
|
local -a rust_content_cache_from=() |
|
2112
|
local -a combined_content_cache_from=() |
|
2113
|
local -a csrc_cache_to=() |
|
2114
|
local -a rust_cache_to=() |
|
2115
|
local -a rocm_cache_to=() |
|
2116
|
local -a export_wheel_cache_to=() |
|
2117
|
local export_csrc_cache=1 |
|
2118
|
local export_rust_cache=1 |
|
2119
|
|
|
2120
|
if ! uses_rocm_csrc_cache && ! uses_rocm_rust_cache; then |
|
2121
|
return 0 |
|
2122
|
fi |
|
2123
|
|
|
2124
|
validate_content_cache_export_mode \ |
|
2125
|
"${content_cache_export_mode}" \ |
|
2126
|
"ROCM_CONTENT_CACHE_EXPORT_MODE" |
|
2127
|
validate_cache_export_mode "${csrc_cache_to_mode}" "ROCM_CSRC_CACHE_TO_MODE" |
|
2128
|
validate_cache_export_mode "${rust_cache_to_mode}" "ROCM_RUST_CACHE_TO_MODE" |
|
2129
|
validate_cache_export_mode "${rocm_cache_to_mode}" "ROCM_FINAL_CACHE_TO_MODE" |
|
2130
|
echo "ROCm content cache export mode: ${content_cache_export_mode}" |
|
2131
|
echo "ROCm csrc cache export mode: ${csrc_cache_to_mode}" |
|
2132
|
echo "ROCm Rust fallback cache export mode: ${rust_cache_to_mode}" |
|
2133
|
echo "ROCm final image cache export mode: ${rocm_cache_to_mode}" |
|
2134
|
|
|
2135
|
if [[ -n "${ROCM_CSRC_CONTENT_CACHE_REF:-}" ]]; then |
|
2136
|
csrc_content_cache_from+=( |
|
2137
|
"type=registry,ref=${ROCM_CSRC_TRUSTED_CONTENT_CACHE_REF}" |
|
2138
|
) |
|
2139
|
if [[ "${ROCM_CSRC_CONTENT_CACHE_REF}" != \ |
|
2140
|
"${ROCM_CSRC_TRUSTED_CONTENT_CACHE_REF}" ]]; then |
|
2141
|
csrc_content_cache_from+=( |
|
2142
|
"type=registry,ref=${ROCM_CSRC_CONTENT_CACHE_REF}" |
|
2143
|
) |
|
2144
|
fi |
|
2145
|
if should_export_content_cache_ref \ |
|
2146
|
"${ROCM_CSRC_CONTENT_CACHE_REF}" "ROCm csrc" \ |
|
2147
|
"${ROCM_CSRC_TRUSTED_CONTENT_CACHE_REF}"; then |
|
2148
|
csrc_cache_to+=( |
|
2149
|
"type=registry,ref=${ROCM_CSRC_CONTENT_CACHE_REF},mode=${csrc_cache_to_mode},ignore-error=true" |
|
2150
|
) |
|
2151
|
else |
|
2152
|
export_csrc_cache=0 |
|
2153
|
fi |
|
2154
|
fi |
|
2155
|
|
|
2156
|
if [[ -n "${ROCM_RUST_CONTENT_CACHE_REF:-}" ]]; then |
|
2157
|
rust_content_cache_from+=( |
|
2158
|
"type=registry,ref=${ROCM_RUST_TRUSTED_CONTENT_CACHE_REF}" |
|
2159
|
) |
|
2160
|
if [[ "${ROCM_RUST_CONTENT_CACHE_REF}" != \ |
|
2161
|
"${ROCM_RUST_TRUSTED_CONTENT_CACHE_REF}" ]]; then |
|
2162
|
rust_content_cache_from+=( |
|
2163
|
"type=registry,ref=${ROCM_RUST_CONTENT_CACHE_REF}" |
|
2164
|
) |
|
2165
|
fi |
|
2166
|
# Legacy commit/branch exports are only needed while the exact-input |
|
2167
|
# content ref is absent. The exact ref itself is refreshed below. |
|
2168
|
if ! should_export_content_cache_ref \ |
|
2169
|
"${ROCM_RUST_CONTENT_CACHE_REF}" "ROCm Rust" \ |
|
2170
|
"${ROCM_RUST_TRUSTED_CONTENT_CACHE_REF}"; then |
|
2171
|
export_rust_cache=0 |
|
2172
|
fi |
|
2173
|
if [[ "${content_cache_export_mode}" != "never" ]]; then |
|
2174
|
# Refresh the exact-input ref in the original solve regardless of |
|
2175
|
# which local or remote cache supplied the Rust result. |
|
2176
|
rust_cache_to+=( |
|
2177
|
"type=registry,ref=${ROCM_RUST_CONTENT_CACHE_REF},mode=min,ignore-error=true" |
|
2178
|
) |
|
2179
|
echo "ROCm Rust exact-input cache will be refreshed (mode=min): ${ROCM_RUST_CONTENT_CACHE_REF}" |
|
2180
|
fi |
|
2181
|
fi |
|
2182
|
|
|
2183
|
combined_content_cache_from=("${csrc_content_cache_from[@]}" "${rust_content_cache_from[@]}") |
|
2184
|
|
|
2185
|
# Docker Hub cache exports are best-effort. A cache-only target failure can |
|
2186
|
# otherwise cancel the sibling image target before its manifest is pushed. |
|
2187
|
if [[ -n "${BUILDKITE_COMMIT:-}" ]]; then |
|
2188
|
if [[ ${export_csrc_cache} -eq 1 ]]; then |
|
2189
|
csrc_cache_to+=( |
|
2190
|
"type=registry,ref=${cache_repo}:csrc-rocm-${BUILDKITE_COMMIT},mode=${csrc_cache_to_mode},ignore-error=true" |
|
2191
|
) |
|
2192
|
fi |
|
2193
|
if [[ ${export_rust_cache} -eq 1 ]]; then |
|
2194
|
rust_cache_to+=( |
|
2195
|
"type=registry,ref=${cache_repo}:rust-rocm-${BUILDKITE_COMMIT},mode=${rust_cache_to_mode},ignore-error=true" |
|
2196
|
) |
|
2197
|
fi |
|
2198
|
rocm_cache_to+=( |
|
2199
|
"type=registry,ref=${cache_repo}:rocm-${BUILDKITE_COMMIT},mode=${rocm_cache_to_mode},ignore-error=true" |
|
2200
|
) |
|
2201
|
fi |
|
2202
|
|
|
2203
|
if [[ -n "${ROCM_CACHE_BRANCH_TAG:-}" ]]; then |
|
2204
|
if [[ ${export_csrc_cache} -eq 1 ]]; then |
|
2205
|
csrc_cache_to+=( |
|
2206
|
"type=registry,ref=${cache_repo}:csrc-rocm-branch-${ROCM_CACHE_BRANCH_TAG},mode=${csrc_cache_to_mode},ignore-error=true" |
|
2207
|
) |
|
2208
|
fi |
|
2209
|
if [[ ${export_rust_cache} -eq 1 ]]; then |
|
2210
|
rust_cache_to+=( |
|
2211
|
"type=registry,ref=${cache_repo}:rust-rocm-branch-${ROCM_CACHE_BRANCH_TAG},mode=${rust_cache_to_mode},ignore-error=true" |
|
2212
|
) |
|
2213
|
fi |
|
2214
|
rocm_cache_to+=( |
|
2215
|
"type=registry,ref=${cache_repo}:rocm-branch-${ROCM_CACHE_BRANCH_TAG},mode=${rocm_cache_to_mode},ignore-error=true" |
|
2216
|
) |
|
2217
|
fi |
|
2218
|
|
|
2219
|
# Standalone image/wheel targets reach rust-build but not the cache-only |
|
2220
|
# exporter unless it is requested explicitly. |
|
2221
|
if ((${#rust_cache_to[@]} > 0)); then |
|
2222
|
case "${TARGET}" in |
|
2223
|
test-rocm-ci|export-wheel-rocm|smoke-test-rocm-ci) |
|
2224
|
BAKE_TARGETS=("rust-rocm-ci" "${BAKE_TARGETS[@]}") |
|
2225
|
;; |
|
2226
|
esac |
|
2227
|
fi |
|
2228
|
|
|
2229
|
if [[ "${TARGET}" == "test-rocm-ci-with-wheel" ]]; then |
|
2230
|
export_wheel_cache_to=() |
|
2231
|
else |
|
2232
|
export_wheel_cache_to=("${rocm_cache_to[@]}") |
|
2233
|
fi |
|
2234
|
|
|
2235
|
{ |
|
2236
|
cat <<EOF |
|
2237
|
target "csrc-rocm-ci" { |
|
2238
|
cache-from = concat( |
|
2239
|
get_cache_from_rocm_csrc(), |
|
2240
|
EOF |
|
2241
|
write_hcl_string_list " " "${csrc_content_cache_from[@]}" |
|
2242
|
cat <<EOF |
|
2243
|
) |
|
2244
|
EOF |
|
2245
|
write_hcl_string_list_attr " " "cache-to" "${csrc_cache_to[@]}" |
|
2246
|
cat <<EOF |
|
2247
|
} |
|
2248
|
|
|
2249
|
target "rust-rocm-ci" { |
|
2250
|
cache-from = concat( |
|
2251
|
get_cache_from_rocm_rust(), |
|
2252
|
EOF |
|
2253
|
write_hcl_string_list " " "${rust_content_cache_from[@]}" |
|
2254
|
cat <<EOF |
|
2255
|
) |
|
2256
|
EOF |
|
2257
|
write_hcl_string_list_attr " " "cache-to" "${rust_cache_to[@]}" |
|
2258
|
cat <<EOF |
|
2259
|
} |
|
2260
|
|
|
2261
|
target "test-rocm-ci" { |
|
2262
|
cache-from = concat( |
|
2263
|
get_cache_from_rocm(), |
|
2264
|
EOF |
|
2265
|
write_hcl_string_list " " "${combined_content_cache_from[@]}" |
|
2266
|
cat <<EOF |
|
2267
|
) |
|
2268
|
EOF |
|
2269
|
write_hcl_string_list_attr " " "cache-to" "${rocm_cache_to[@]}" |
|
2270
|
cat <<EOF |
|
2271
|
} |
|
2272
|
|
|
2273
|
target "smoke-test-rocm-ci" { |
|
2274
|
cache-from = concat( |
|
2275
|
get_cache_from_rocm(), |
|
2276
|
EOF |
|
2277
|
write_hcl_string_list " " "${combined_content_cache_from[@]}" |
|
2278
|
cat <<EOF |
|
2279
|
) |
|
2280
|
} |
|
2281
|
|
|
2282
|
target "export-wheel-rocm" { |
|
2283
|
cache-from = concat( |
|
2284
|
get_cache_from_rocm(), |
|
2285
|
EOF |
|
2286
|
write_hcl_string_list " " "${combined_content_cache_from[@]}" |
|
2287
|
cat <<EOF |
|
2288
|
) |
|
2289
|
EOF |
|
2290
|
write_hcl_string_list_attr " " "cache-to" "${export_wheel_cache_to[@]}" |
|
2291
|
cat <<EOF |
|
2292
|
} |
|
2293
|
EOF |
|
2294
|
} > "${CSRC_CACHE_OVERRIDE_PATH}" |
|
2295
|
|
|
2296
|
BAKE_FILES+=(-f "${CSRC_CACHE_OVERRIDE_PATH}") |
|
2297
|
echo "Appended ROCm cache override with non-fatal registry exports" |
|
2298
|
} |
|
2299
|
|
|
2300
|
extract_dependency_pins() { |
|
2301
|
local bake_dir="" |
|
2302
|
local dockerfile_rocm="" |
|
2303
|
local physical_dockerfile="" |
|
2304
|
local var="" |
|
2305
|
local val="" |
|
2306
|
|
|
2307
|
bake_dir=$(dirname "${VLLM_BAKE_FILE}") |
|
2308
|
dockerfile_rocm="${CI_BASE_DOCKERFILE:-${bake_dir}/Dockerfile.rocm}" |
|
2309
|
physical_dockerfile="${dockerfile_rocm}" |
|
2310
|
if [[ -n "${ROCM_BUILD_CONTEXT_ROOT:-}" && "${dockerfile_rocm}" != /* ]]; then |
|
2311
|
physical_dockerfile="${ROCM_BUILD_CONTEXT_ROOT}/${dockerfile_rocm}" |
|
2312
|
fi |
|
2313
|
if [[ ! -f "${physical_dockerfile}" ]]; then |
|
2314
|
return 0 |
|
2315
|
fi |
|
2316
|
|
|
2317
|
for var in NIXL_BRANCH UCX_BRANCH ROCSHMEM_BRANCH DEEPEP_BRANCH; do |
|
2318
|
if [[ -n "${!var:-}" ]]; then |
|
2319
|
echo "Using provided ${var}: ${!var}" |
|
2320
|
continue |
|
2321
|
fi |
|
2322
|
|
|
2323
|
val=$( |
|
2324
|
sed -n -E "s/^[[:space:]]*[Aa][Rr][Gg][[:space:]]+${var}=\"?([^\"[:space:]]+)\"?.*/\\1/p" \ |
|
2325
|
"${physical_dockerfile}" | head -1 |
|
2326
|
) |
|
2327
|
if [[ -n "${val}" ]]; then |
|
2328
|
export "${var}=${val}" |
|
2329
|
echo "Extracted ${var}=${val} from ${dockerfile_rocm}" |
|
2330
|
fi |
|
2331
|
done |
|
2332
|
} |
|
2333
|
|
|
2334
|
compute_dependency_cache_keys() { |
|
2335
|
local bake_dir="" |
|
2336
|
local dockerfile_rocm="" |
|
2337
|
local nixl_branch="" |
|
2338
|
local ucx_branch="" |
|
2339
|
local rocshmem_branch="" |
|
2340
|
local deepep_branch="" |
|
2341
|
local nixl_material="" |
|
2342
|
local rocshmem_material="" |
|
2343
|
local deepep_material="" |
|
2344
|
|
|
2345
|
bake_dir=$(dirname "${VLLM_BAKE_FILE}") |
|
2346
|
dockerfile_rocm="${CI_BASE_DOCKERFILE:-${bake_dir}/Dockerfile.rocm}" |
|
2347
|
nixl_branch=$(resolve_dockerfile_arg_value "${dockerfile_rocm}" "NIXL_BRANCH") |
|
2348
|
ucx_branch=$(resolve_dockerfile_arg_value "${dockerfile_rocm}" "UCX_BRANCH") |
|
2349
|
rocshmem_branch=$(resolve_dockerfile_arg_value "${dockerfile_rocm}" "ROCSHMEM_BRANCH") |
|
2350
|
deepep_branch=$(resolve_dockerfile_arg_value "${dockerfile_rocm}" "DEEPEP_BRANCH") |
|
2351
|
|
|
2352
|
if [[ -n "${nixl_branch}" && -n "${ucx_branch}" ]]; then |
|
2353
|
nixl_material=$(compose_stage_cache_material "${dockerfile_rocm}" "base build_nixl") |
|
2354
|
NIXL_CACHE_KEY=$( |
|
2355
|
compose_dependency_cache_key \ |
|
2356
|
"${nixl_branch}-ucx-${ucx_branch}" \ |
|
2357
|
"${nixl_material}" |
|
2358
|
) |
|
2359
|
export NIXL_CACHE_KEY |
|
2360
|
echo "NIXL dependency cache key: ${NIXL_CACHE_KEY}" |
|
2361
|
fi |
|
2362
|
|
|
2363
|
if [[ -n "${rocshmem_branch}" ]]; then |
|
2364
|
rocshmem_material=$(compose_stage_cache_material "${dockerfile_rocm}" "base build_rocshmem") |
|
2365
|
ROCSHMEM_CACHE_KEY=$( |
|
2366
|
compose_dependency_cache_key \ |
|
2367
|
"${rocshmem_branch}" \ |
|
2368
|
"${rocshmem_material}" |
|
2369
|
) |
|
2370
|
export ROCSHMEM_CACHE_KEY |
|
2371
|
echo "ROCShmem dependency cache key: ${ROCSHMEM_CACHE_KEY}" |
|
2372
|
fi |
|
2373
|
|
|
2374
|
if [[ -n "${deepep_branch}" && -n "${rocshmem_branch}" ]]; then |
|
2375
|
deepep_material=$(compose_stage_cache_material "${dockerfile_rocm}" "base build_rocshmem build_deepep") |
|
2376
|
DEEPEP_CACHE_KEY=$( |
|
2377
|
compose_dependency_cache_key \ |
|
2378
|
"${deepep_branch}-rocshmem-${rocshmem_branch}" \ |
|
2379
|
"${deepep_material}" |
|
2380
|
) |
|
2381
|
export DEEPEP_CACHE_KEY |
|
2382
|
echo "DeepEP dependency cache key: ${DEEPEP_CACHE_KEY}" |
|
2383
|
fi |
|
2384
|
} |
|
2385
|
|
|
2386
|
compose_stage_cache_material() { |
|
2387
|
local dockerfile="$1" |
|
2388
|
local stages="$2" |
|
2389
|
local -a content_args=() |
|
2390
|
|
|
2391
|
mapfile -t content_args < <(get_content_arg_names "${dockerfile}" "${stages}" "") |
|
2392
|
{ |
|
2393
|
printf 'dockerfile:%s\n' "${dockerfile}" |
|
2394
|
printf 'dockerfile-stages:%s\n' "${stages}" |
|
2395
|
hash_dockerfile_stages "${dockerfile}" "${stages}" |
|
2396
|
printf 'resolved-build-args:\n' |
|
2397
|
hash_dockerfile_arg_values "${dockerfile}" "${content_args[@]}" |
|
2398
|
} |
|
2399
|
} |
|
2400
|
|
|
2401
|
dependency_cache_ref_exists() { |
|
2402
|
local cache_ref="$1" |
|
2403
|
registry_ref_exists_with_retry imagetools "${cache_ref}" |
|
2404
|
} |
|
2405
|
|
|
2406
|
dependency_cache_ref_for_target() { |
|
2407
|
local target="$1" |
|
2408
|
local cache_repo="${DOCKERHUB_CACHE_REPO:-rocm/vllm-ci-cache}" |
|
2409
|
|
|
2410
|
case "${target}" in |
|
2411
|
nixl-rocm-ci) |
|
2412
|
if [[ -n "${NIXL_CACHE_KEY:-}" ]]; then |
|
2413
|
printf '%s\n' "${cache_repo}:nixl-rocm-${NIXL_CACHE_KEY}" |
|
2414
|
elif [[ -n "${NIXL_BRANCH:-}" ]]; then |
|
2415
|
printf '%s\n' "${cache_repo}:nixl-rocm-${NIXL_BRANCH}-ucx-${UCX_BRANCH:-}" |
|
2416
|
fi |
|
2417
|
;; |
|
2418
|
rocshmem-rocm-ci) |
|
2419
|
if [[ -n "${ROCSHMEM_CACHE_KEY:-}" ]]; then |
|
2420
|
printf '%s\n' "${cache_repo}:rocshmem-rocm-${ROCSHMEM_CACHE_KEY}" |
|
2421
|
elif [[ -n "${ROCSHMEM_BRANCH:-}" ]]; then |
|
2422
|
printf '%s\n' "${cache_repo}:rocshmem-rocm-${ROCSHMEM_BRANCH}" |
|
2423
|
fi |
|
2424
|
;; |
|
2425
|
deepep-rocm-ci) |
|
2426
|
if [[ -n "${DEEPEP_CACHE_KEY:-}" ]]; then |
|
2427
|
printf '%s\n' "${cache_repo}:deepep-rocm-${DEEPEP_CACHE_KEY}" |
|
2428
|
elif [[ -n "${DEEPEP_BRANCH:-}" ]]; then |
|
2429
|
printf '%s\n' "${cache_repo}:deepep-rocm-${DEEPEP_BRANCH}-rocshmem-${ROCSHMEM_BRANCH:-}" |
|
2430
|
fi |
|
2431
|
;; |
|
2432
|
esac |
|
2433
|
} |
|
2434
|
|
|
2435
|
add_dependency_cache_target() { |
|
2436
|
local target="$1" |
|
2437
|
|
|
2438
|
if printf '%s\n' "${DEPENDENCY_CACHE_TARGETS[@]}" | grep -qx "${target}"; then |
|
2439
|
return 0 |
|
2440
|
fi |
|
2441
|
DEPENDENCY_CACHE_TARGETS+=("${target}") |
|
2442
|
} |
|
2443
|
|
|
2444
|
resolve_ci_base_dependency_targets() { |
|
2445
|
local mode="${ROCM_DEP_CACHE_EXPORT_MODE:-missing}" |
|
2446
|
local nixl_ref="" |
|
2447
|
local rocshmem_ref="" |
|
2448
|
local deepep_ref="" |
|
2449
|
|
|
2450
|
[[ "${TARGET}" == "ci-base-rocm-ci-with-deps" ]] || return 0 |
|
2451
|
|
|
2452
|
case "${mode}" in |
|
2453
|
always) |
|
2454
|
echo "ROCM_DEP_CACHE_EXPORT_MODE=always; exporting all dependency caches serially" |
|
2455
|
for target in nixl-rocm-ci rocshmem-rocm-ci deepep-rocm-ci; do |
|
2456
|
if [[ -n "$(dependency_cache_ref_for_target "${target}")" ]]; then |
|
2457
|
add_dependency_cache_target "${target}" |
|
2458
|
fi |
|
2459
|
done |
|
2460
|
;; |
|
2461
|
never) |
|
2462
|
BAKE_TARGETS=("ci-base-rocm-ci") |
|
2463
|
DEPENDENCY_CACHE_TARGETS=() |
|
2464
|
echo "ROCM_DEP_CACHE_EXPORT_MODE=never; building ci_base without dependency cache exports" |
|
2465
|
return 0 |
|
2466
|
;; |
|
2467
|
missing|"") |
|
2468
|
;; |
|
2469
|
*) |
|
2470
|
echo "Error: ROCM_DEP_CACHE_EXPORT_MODE must be one of: missing, always, never" |
|
2471
|
exit 1 |
|
2472
|
;; |
|
2473
|
esac |
|
2474
|
|
|
2475
|
if [[ "${mode}" != "always" && -n "${NIXL_CACHE_KEY:-}" ]]; then |
|
2476
|
nixl_ref=$(dependency_cache_ref_for_target "nixl-rocm-ci") |
|
2477
|
if dependency_cache_ref_exists "${nixl_ref}"; then |
|
2478
|
echo "NIXL dependency cache exists: ${nixl_ref}" |
|
2479
|
else |
|
2480
|
echo "NIXL dependency cache missing; will seed: ${nixl_ref}" |
|
2481
|
add_dependency_cache_target "nixl-rocm-ci" |
|
2482
|
fi |
|
2483
|
fi |
|
2484
|
|
|
2485
|
if [[ "${mode}" != "always" && -n "${ROCSHMEM_CACHE_KEY:-}" ]]; then |
|
2486
|
rocshmem_ref=$(dependency_cache_ref_for_target "rocshmem-rocm-ci") |
|
2487
|
if dependency_cache_ref_exists "${rocshmem_ref}"; then |
|
2488
|
echo "ROCShmem dependency cache exists: ${rocshmem_ref}" |
|
2489
|
else |
|
2490
|
echo "ROCShmem dependency cache missing; will seed: ${rocshmem_ref}" |
|
2491
|
add_dependency_cache_target "rocshmem-rocm-ci" |
|
2492
|
fi |
|
2493
|
fi |
|
2494
|
|
|
2495
|
if [[ "${mode}" != "always" && -n "${DEEPEP_CACHE_KEY:-}" ]]; then |
|
2496
|
deepep_ref=$(dependency_cache_ref_for_target "deepep-rocm-ci") |
|
2497
|
if dependency_cache_ref_exists "${deepep_ref}"; then |
|
2498
|
echo "DeepEP dependency cache exists: ${deepep_ref}" |
|
2499
|
else |
|
2500
|
echo "DeepEP dependency cache missing; will seed: ${deepep_ref}" |
|
2501
|
add_dependency_cache_target "deepep-rocm-ci" |
|
2502
|
fi |
|
2503
|
fi |
|
2504
|
|
|
2505
|
# DeepEP inherits from ROCShmem. If ROCShmem is being seeded, seed DeepEP too |
|
2506
|
# so the pair stays consistent for future ci_base rebuilds. |
|
2507
|
if printf '%s\n' "${DEPENDENCY_CACHE_TARGETS[@]}" | grep -qx "rocshmem-rocm-ci" \ |
|
2508
|
&& ! printf '%s\n' "${DEPENDENCY_CACHE_TARGETS[@]}" | grep -qx "deepep-rocm-ci" \ |
|
2509
|
&& [[ -n "${DEEPEP_BRANCH:-}" ]]; then |
|
2510
|
echo "ROCShmem cache is missing; also seeding DeepEP cache" |
|
2511
|
add_dependency_cache_target "deepep-rocm-ci" |
|
2512
|
fi |
|
2513
|
|
|
2514
|
BAKE_TARGETS=("ci-base-rocm-ci") |
|
2515
|
if [[ ${#DEPENDENCY_CACHE_TARGETS[@]} -eq 0 ]]; then |
|
2516
|
echo "All dependency caches exist; building ci_base without dependency cache exports" |
|
2517
|
else |
|
2518
|
echo "Resolved dependency cache seed targets: ${DEPENDENCY_CACHE_TARGETS[*]}" |
|
2519
|
echo "Resolved ci_base bake targets: ${BAKE_TARGETS[*]}" |
|
2520
|
fi |
|
2521
|
} |
|
2522
|
|
|
2523
|
bake_config_targets() { |
|
2524
|
printf '%s\n' "${DEPENDENCY_CACHE_TARGETS[@]}" "${BAKE_TARGETS[@]}" \ |
|
2525
|
| awk 'NF && !seen[$0]++' |
|
2526
|
} |
|
2527
|
|
|
2528
|
print_bake_config() { |
|
2529
|
local -a print_targets=() |
|
2530
|
|
|
2531
|
echo "--- :page_facing_up: Resolved bake configuration" |
|
2532
|
mapfile -t print_targets < <(bake_config_targets) |
|
2533
|
docker buildx bake "${BAKE_ALLOW_ARGS[@]}" \ |
|
2534
|
"${BAKE_FILES[@]}" --print "${print_targets[@]}" | tee "${BAKE_CONFIG_FILE}" |
|
2535
|
|
|
2536
|
if command -v buildkite-agent >/dev/null 2>&1 && [[ -n "${BUILDKITE_BUILD_NUMBER:-}" ]]; then |
|
2537
|
buildkite-agent artifact upload "${BAKE_CONFIG_FILE}" || true |
|
2538
|
echo "Uploaded ${BAKE_CONFIG_FILE} as Buildkite artifact" |
|
2539
|
else |
|
2540
|
echo "Saved bake config to ${BAKE_CONFIG_FILE} (not in Buildkite, skipping upload)" |
|
2541
|
fi |
|
2542
|
} |
|
2543
|
|
|
2544
|
confirm_remote_image_push() { |
|
2545
|
local image_ref="$1" |
|
2546
|
|
|
2547
|
if [[ -z "${CI_BASE_CONTENT_HASH:-}" ]]; then |
|
2548
|
remote_image_exists "${image_ref}" |
|
2549
|
return |
|
2550
|
fi |
|
2551
|
|
|
2552
|
if remote_ci_base_identity_is_current_with_retry "${image_ref}"; then |
|
2553
|
return 0 |
|
2554
|
fi |
|
2555
|
|
|
2556
|
echo "Remote image does not have the expected complete ci_base identity." |
|
2557
|
return 1 |
|
2558
|
} |
|
2559
|
|
|
2560
|
verify_dependency_cache_ref() { |
|
2561
|
local cache_ref="$1" |
|
2562
|
local attempts="${ROCM_DEP_CACHE_VERIFY_ATTEMPTS:-6}" |
|
2563
|
local delay_secs="${ROCM_DEP_CACHE_VERIFY_DELAY:-5}" |
|
2564
|
local attempt |
|
2565
|
|
|
2566
|
for ((attempt = 1; attempt <= attempts; attempt++)); do |
|
2567
|
if dependency_cache_ref_exists "${cache_ref}"; then |
|
2568
|
echo "Dependency cache confirmed: ${cache_ref}" |
|
2569
|
return 0 |
|
2570
|
fi |
|
2571
|
if [[ ${attempt} -lt ${attempts} ]]; then |
|
2572
|
echo "Dependency cache not visible yet (${attempt}/${attempts}): ${cache_ref}" |
|
2573
|
sleep "${delay_secs}" |
|
2574
|
fi |
|
2575
|
done |
|
2576
|
|
|
2577
|
echo "ERROR: dependency cache was not confirmed after upload: ${cache_ref}" |
|
2578
|
return 1 |
|
2579
|
} |
|
2580
|
|
|
2581
|
seed_dependency_caches_if_needed() { |
|
2582
|
local target="" |
|
2583
|
local cache_ref="" |
|
2584
|
|
|
2585
|
if [[ "${TARGET}" != "ci-base-rocm-ci-with-deps" ]]; then |
|
2586
|
return 0 |
|
2587
|
fi |
|
2588
|
if [[ ${#DEPENDENCY_CACHE_TARGETS[@]} -eq 0 ]]; then |
|
2589
|
return 0 |
|
2590
|
fi |
|
2591
|
|
|
2592
|
echo "--- :docker: Seeding ROCm dependency caches" |
|
2593
|
echo "Dependency cache uploads are required for this build." |
|
2594
|
echo "Seeding serially to avoid concurrent Docker Hub cache exporters." |
|
2595
|
|
|
2596
|
for target in "${DEPENDENCY_CACHE_TARGETS[@]}"; do |
|
2597
|
cache_ref=$(dependency_cache_ref_for_target "${target}") |
|
2598
|
if [[ -z "${cache_ref}" ]]; then |
|
2599
|
echo "ERROR: could not resolve dependency cache ref for ${target}" |
|
2600
|
return 1 |
|
2601
|
fi |
|
2602
|
|
|
2603
|
echo "--- :docker: Seeding ${target}" |
|
2604
|
echo "Expected cache ref: ${cache_ref}" |
|
2605
|
docker buildx bake \ |
|
2606
|
"${BAKE_ALLOW_ARGS[@]}" \ |
|
2607
|
"${BAKE_FILES[@]}" \ |
|
2608
|
--progress "${BUILDKIT_PROGRESS:-plain}" \ |
|
2609
|
"${target}" |
|
2610
|
verify_dependency_cache_ref "${cache_ref}" |
|
2611
|
done |
|
2612
|
} |
|
2613
|
|
|
2614
|
run_bake() { |
|
2615
|
local confirmation_ref="${IMAGE_TAG:-}" |
|
2616
|
|
|
2617
|
if is_ci_base_target && [[ -n "${CI_BASE_IMAGE_TAG_BUILD_REF:-}" ]]; then |
|
2618
|
confirmation_ref="${CI_BASE_IMAGE_TAG_BUILD_REF}" |
|
2619
|
fi |
|
2620
|
|
|
2621
|
echo "--- :docker: Building ${TARGET}" |
|
2622
|
docker buildx bake \ |
|
2623
|
"${BAKE_ALLOW_ARGS[@]}" \ |
|
2624
|
"${BAKE_FILES[@]}" \ |
|
2625
|
--progress "${BUILDKIT_PROGRESS:-plain}" \ |
|
2626
|
"${BAKE_TARGETS[@]}" |
|
2627
|
|
|
2628
|
if is_ci_base_target; then |
|
2629
|
if ! confirm_remote_image_push "${confirmation_ref}"; then |
|
2630
|
echo "Fresh ci_base image failed identity validation: ${confirmation_ref}" >&2 |
|
2631
|
return 1 |
|
2632
|
fi |
|
2633
|
refresh_ci_base_tags_from_ref "${confirmation_ref}" |
|
2634
|
fi |
|
2635
|
echo "--- :white_check_mark: Build complete" |
|
2636
|
} |
|
2637
|
|
|
2638
|
upload_wheel_artifacts_if_present() { |
|
2639
|
local wheel_dir="./wheel-export" |
|
2640
|
local artifact_dir="artifacts/vllm-rocm-install" |
|
2641
|
local archive_name="vllm-rocm-install.tar.gz" |
|
2642
|
local metadata_dir="${wheel_dir}/.vllm-ci-artifact" |
|
2643
|
local build_base_digest="" |
|
2644
|
local expected_native_base_image="" |
|
2645
|
local native_base_image="" |
|
2646
|
local native_base_digest="" |
|
2647
|
local whl="" |
|
2648
|
local whl_name="" |
|
2649
|
local -a wheels=() |
|
2650
|
|
|
2651
|
if ! should_upload_wheel_artifacts; then |
|
2652
|
return 0 |
|
2653
|
fi |
|
2654
|
|
|
2655
|
if [[ -d "${wheel_dir}" ]]; then |
|
2656
|
mapfile -t wheels < <(find "${wheel_dir}" -maxdepth 1 -type f -name '*.whl' -print) |
|
2657
|
fi |
|
2658
|
if [[ ${#wheels[@]} -ne 1 ]]; then |
|
2659
|
echo "Expected exactly one ROCm wheel in ${wheel_dir}; found ${#wheels[@]}" >&2 |
|
2660
|
return 1 |
|
2661
|
fi |
|
2662
|
whl="${wheels[0]}" |
|
2663
|
whl_name=$(basename "${whl}") |
|
2664
|
native_base_image="${CI_BASE_IMAGE_TAG_BUILD_REF:-${CI_BASE_IMAGE:-}}" |
|
2665
|
if [[ -z "${native_base_image}" ]]; then |
|
2666
|
echo "Native ROCm artifact requires a ci_base image reference" >&2 |
|
2667
|
return 1 |
|
2668
|
fi |
|
2669
|
if [[ "${BUILDKITE:-false}" == "true" ]]; then |
|
2670
|
expected_native_base_image=$(ci_base_tag_with_suffix \ |
|
2671
|
"${CI_BASE_IMAGE_TAG:-rocm/vllm-dev:ci_base}" \ |
|
2672
|
"build-${BUILDKITE_BUILD_ID:-}") |
|
2673
|
if [[ "${native_base_image}" != "${expected_native_base_image}" ]]; then |
|
2674
|
echo "Native ROCm artifact requires the exact ci_base build handoff: ${native_base_image}" >&2 |
|
2675
|
return 1 |
|
2676
|
fi |
|
2677
|
if [[ ! "${CI_BASE_IMAGE:-}" =~ @sha256:[0-9a-f]{64}$ ]]; then |
|
2678
|
echo "ROCm artifact build base must be digest-pinned: ${CI_BASE_IMAGE:-<empty>}" >&2 |
|
2679
|
return 1 |
|
2680
|
fi |
|
2681
|
build_base_digest="${CI_BASE_IMAGE##*@}" |
|
2682
|
if ! native_base_digest=$(resolve_image_digest "${native_base_image}"); then |
|
2683
|
echo "Could not resolve native ci_base handoff: ${native_base_image}" >&2 |
|
2684
|
return 1 |
|
2685
|
fi |
|
2686
|
if [[ "${native_base_digest}" != "${build_base_digest}" ]]; then |
|
2687
|
echo "Native ci_base handoff does not match the artifact build base" >&2 |
|
2688
|
echo " native: ${native_base_image}@${native_base_digest}" >&2 |
|
2689
|
echo " build: ${CI_BASE_IMAGE}" >&2 |
|
2690
|
return 1 |
|
2691
|
fi |
|
2692
|
fi |
|
2693
|
|
|
2694
|
echo "--- :package: Uploading ROCm vLLM install artifact" |
|
2695
|
rm -rf "${artifact_dir}" "${metadata_dir}" |
|
2696
|
mkdir -p "${artifact_dir}" "${metadata_dir}" |
|
2697
|
|
|
2698
|
printf '%s\n' "${BUILDKITE_COMMIT:-local}" > "${metadata_dir}/commit.txt" |
|
2699
|
printf '%s\n' "${native_base_image}" > "${metadata_dir}/native-base-image.txt" |
|
2700
|
printf '%s\n' "${CI_BASE_IMAGE:-}" > "${metadata_dir}/ci-base-image.txt" |
|
2701
|
printf '%s\n' "${IMAGE_TAG:-}" > "${metadata_dir}/fallback-image.txt" |
|
2702
|
printf '%s\n' "${whl_name}" > "${metadata_dir}/wheel-filename.txt" |
|
2703
|
|
|
2704
|
tar -C "${wheel_dir}" -czf "${artifact_dir}/${archive_name}" . |
|
2705
|
( |
|
2706
|
cd "${artifact_dir}" |
|
2707
|
sha256sum "${archive_name}" > "${archive_name}.sha256" |
|
2708
|
) |
|
2709
|
echo "Created ${archive_name}: $(du -sh "${artifact_dir}/${archive_name}" | cut -f1)" |
|
2710
|
cp "${metadata_dir}"/*.txt "${artifact_dir}/" |
|
2711
|
cp "${whl}" "${artifact_dir}/${whl_name}" |
|
2712
|
echo "Copied ${whl_name}: $(du -sh "${artifact_dir}/${whl_name}" | cut -f1)" |
|
2713
|
|
|
2714
|
if command -v buildkite-agent >/dev/null 2>&1; then |
|
2715
|
buildkite-agent artifact upload "${artifact_dir}/*" || return 1 |
|
2716
|
echo "ROCm vLLM install artifacts uploaded to ${artifact_dir}/" |
|
2717
|
elif [[ "${BUILDKITE:-false}" == "true" ]]; then |
|
2718
|
echo "buildkite-agent not found; cannot upload required ROCm artifacts" >&2 |
|
2719
|
return 1 |
|
2720
|
else |
|
2721
|
echo "Not in Buildkite, skipping artifact upload" |
|
2722
|
fi |
|
2723
|
|
|
2724
|
rm -rf "${wheel_dir}" |
|
2725
|
} |
|
2726
|
|
|
2727
|
main() { |
|
2728
|
init_config "$@" |
|
2729
|
configure_ci_base_write_scope |
|
2730
|
print_header |
|
2731
|
validate_inputs |
|
2732
|
load_ci_hcl |
|
2733
|
init_bake_files |
|
2734
|
if is_ci_base_target; then |
|
2735
|
prepare_ci_build_context |
|
2736
|
configure_custom_rocm_stages |
|
2737
|
fi |
|
2738
|
compute_ci_base_hash_if_needed |
|
2739
|
configure_ci_base_image_refs |
|
2740
|
maybe_skip_existing_image |
|
2741
|
setup_builder |
|
2742
|
prepare_git_cache_metadata |
|
2743
|
# Non-ci_base builds may deepen a shallow checkout above. Derive archival |
|
2744
|
# version metadata only after that lookup sees the available tag history. |
|
2745
|
if ! is_ci_base_target; then |
|
2746
|
prepare_ci_build_context |
|
2747
|
configure_custom_rocm_stages |
|
2748
|
fi |
|
2749
|
extract_dependency_pins |
|
2750
|
write_rocm_build_arg_override |
|
2751
|
compute_dependency_cache_keys |
|
2752
|
write_ci_base_label_override |
|
2753
|
compute_rocm_csrc_content_hash_if_needed |
|
2754
|
compute_rocm_rust_content_hash_if_needed |
|
2755
|
write_rocm_cache_override |
|
2756
|
# Keep the context override last so every bake target uses the owned tree. |
|
2757
|
write_build_context_override |
|
2758
|
resolve_ci_base_dependency_targets |
|
2759
|
print_bake_config |
|
2760
|
if [[ "${BAKE_PRINT_ONLY:-0}" == "1" ]]; then |
|
2761
|
echo "BAKE_PRINT_ONLY=1 set; skipping build" |
|
2762
|
return 0 |
|
2763
|
fi |
|
2764
|
if should_upload_wheel_artifacts; then |
|
2765
|
# wheel-export is an output directory, not a BuildKit cache. Starting |
|
2766
|
# clean prevents a failed/retried export from packaging a stale wheel. |
|
2767
|
rm -rf ./wheel-export |
|
2768
|
fi |
|
2769
|
if should_export_rocm_smoke; then |
|
2770
|
# The marker is a build output, not a cache. Never accept stale output |
|
2771
|
# from an earlier build or retry. |
|
2772
|
rm -rf ./build/rocm-smoke-export |
|
2773
|
fi |
|
2774
|
seed_dependency_caches_if_needed |
|
2775
|
run_bake |
|
2776
|
verify_rocm_smoke_export |
|
2777
|
promote_stable_ci_base_tag |
|
2778
|
publish_ci_base_handoff_ref |
|
2779
|
upload_wheel_artifacts_if_present |
|
2780
|
} |
|
2781
|
|
|
2782
|
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then |
|
2783
|
main "$@" |
|
2784
|
fi |