Skip to content

Commit 8d45ca8

Browse files
authored
Merge branch 'main' into issue-2025-sdk-diff
2 parents 91678e9 + 619cf93 commit 8d45ca8

87 files changed

Lines changed: 5797 additions & 515 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/RENOVATE.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Self-hosted Renovate keeps the project's dependencies up to date across `go.mod`
99

1010
- Configuration: [`.github/renovate.json5`](renovate.json5)
1111
- Workflow: [`.github/workflows/renovate.yaml`](workflows/renovate.yaml)
12-
- Companion script: [`tools/update-chainsaw-checksums`](../tools/update-chainsaw-checksums)
12+
- Companion scripts: [`tools/update-chainsaw-checksums`](../tools/update-chainsaw-checksums), [`tools/update-helmfile-checksums`](../tools/update-helmfile-checksums), [`tools/update-helm-diff-checksums`](../tools/update-helm-diff-checksums)
1313

1414
Policy choices (schedule, cooldown, auto-merge scope, group consolidation) are documented inline in `renovate.json5`. This doc covers what's covered, how to extend coverage, and the known gotchas.
1515

@@ -25,6 +25,8 @@ Policy choices (schedule, cooldown, auto-merge scope, group consolidation) are d
2525
| `.settings.yaml` (28 tool entries) | custom regex manager (`# renovate:` annotations) |
2626
| `.settings.yaml` `nvkind` SHA | dedicated git-refs digest customManager (`# renovate-digest:`) |
2727
| `.settings.yaml` `chainsaw_checksums` | `postUpgradeTasks``tools/update-chainsaw-checksums` |
28+
| `.settings.yaml` `helmfile_checksums` | `postUpgradeTasks``tools/update-helmfile-checksums` |
29+
| `.settings.yaml` `helm_diff_checksums` | `postUpgradeTasks``tools/update-helm-diff-checksums` |
2830
| `.go-version` (Go toolchain) | dedicated `golang-version` customManager (`go-toolchain` group) |
2931

3032
The `go` directive in `go.mod` is intentionally not bumped — the Go toolchain version is owned by `.go-version`. Makefile (`GOTOOLCHAIN`), the `load-versions` composite action, `install-karpenter-kwok`, and validator Dockerfiles (`--build-arg GO_VERSION`) all read from that single file.

.github/renovate.json5

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,16 @@
319319
},
320320
},
321321

322+
// ---- helm-diff bumps refresh per-arch SHA256 checksums via post-upgrade script ----
323+
{
324+
matchDepNames: ["databus23/helm-diff"],
325+
postUpgradeTasks: {
326+
commands: ["./tools/update-helm-diff-checksums {{{newVersion}}}"],
327+
fileFilters: [".settings.yaml"],
328+
executionMode: "update",
329+
},
330+
},
331+
322332
// ---- aiperf-bench base image is capped below python 3.14 ----
323333
// PR #1906 auto-bumped this to 3.14 and broke the UAT validator image
324334
// build (aiperf's pyzmq/uvloop had no cp314 wheels, and the single-stage

.github/workflows/codeql.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
4040
with:
4141
go-version: ${{ steps.versions.outputs.go }}
42-
cache: false # vendor/ provides deps; disable to save disk on constrained runners
42+
cache: true
4343
- uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
4444
with:
4545
languages: go

.github/workflows/fern-docs-preview-build.yml

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,28 @@ jobs:
6868
if git show-ref --verify --quiet "refs/tags/${version}"; then
6969
mkdir -p "fern/versions/${version}-content"
7070
git archive "refs/tags/${version}" -- docs/ | tar -x --strip-components=1 -C "fern/versions/${version}-content"
71-
find "fern/versions/${version}-content" -name '*.md' -print0 | xargs -0 sed -i \
72-
-e 's/{/\\{/g' \
73-
-e 's/}/\\}/g' \
74-
-e 's/</\&lt;/g'
71+
# Escape {, }, < for MDX — but only outside fenced code blocks and inline code spans
72+
find "fern/versions/${version}-content" -name '*.md' -print0 | while IFS= read -r -d '' f; do
73+
awk '
74+
/^````*/ || /^~~~~*/ { fence = !fence; print; next }
75+
fence { print; next }
76+
{
77+
n = split($0, p, "`")
78+
out = ""
79+
for (i = 1; i <= n; i++) {
80+
if (i % 2 == 1) {
81+
gsub(/{/, "\\{", p[i])
82+
gsub(/}/, "\\}", p[i])
83+
gsub(/</, "\\&lt;", p[i])
84+
}
85+
out = out p[i]
86+
if (i < n) out = out "`"
87+
}
88+
print out
89+
}
90+
' "$f" > "${f}.tmp" && mv "${f}.tmp" "$f"
91+
done
92+
tools/check-docs-mdx-parse "fern/versions/${version}-content"
7593
echo "Extracted docs from $version"
7694
else
7795
echo "::warning::Tag $version not found — skipping content checkout"

.github/workflows/merge-gate.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ jobs:
296296
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
297297
with:
298298
go-version: ${{ steps.versions.outputs.go }}
299-
cache: false
299+
cache: true
300300
- uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
301301
with:
302302
languages: go
@@ -528,7 +528,7 @@ jobs:
528528
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
529529
with:
530530
go-version: ${{ steps.versions.outputs.go }}
531-
cache: false
531+
cache: true
532532
- name: Verify committed BOM versions and variants match the recipes
533533
env:
534534
GOFLAGS: -mod=vendor
@@ -579,7 +579,7 @@ jobs:
579579
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
580580
with:
581581
go-version: ${{ steps.versions.outputs.go }}
582-
cache: false
582+
cache: true
583583
- name: Verify committed tuning-status table is up to date
584584
env:
585585
GOFLAGS: -mod=vendor
@@ -661,7 +661,7 @@ jobs:
661661
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
662662
with:
663663
go-version: ${{ steps.versions.outputs.go }}
664-
cache: false
664+
cache: true
665665
- name: Verify committed coverage matrix is up to date
666666
env:
667667
GOFLAGS: -mod=vendor

.github/workflows/publish-fern-docs.yml

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -184,14 +184,28 @@ jobs:
184184
if git show-ref --verify --quiet "refs/tags/${version}"; then
185185
mkdir -p "fern/versions/${version}-content"
186186
git archive "refs/tags/${version}" -- docs/ | tar -x --strip-components=1 -C "fern/versions/${version}-content"
187-
# Sanitize frozen markdown for Fern's MDX parser.
188-
# Versioned content gets strict MDX parsing; escape all bare
189-
# { } and < so nothing is interpreted as JSX. Markdown renders
190-
# &lt; as < and \{ as { so the output is visually identical.
191-
find "fern/versions/${version}-content" -name '*.md' -print0 | xargs -0 sed -i \
192-
-e 's/{/\\{/g' \
193-
-e 's/}/\\}/g' \
194-
-e 's/</\&lt;/g'
187+
# Escape {, }, < for MDX — but only outside fenced code blocks and inline code spans
188+
find "fern/versions/${version}-content" -name '*.md' -print0 | while IFS= read -r -d '' f; do
189+
awk '
190+
/^````*/ || /^~~~~*/ { fence = !fence; print; next }
191+
fence { print; next }
192+
{
193+
n = split($0, p, "`")
194+
out = ""
195+
for (i = 1; i <= n; i++) {
196+
if (i % 2 == 1) {
197+
gsub(/{/, "\\{", p[i])
198+
gsub(/}/, "\\}", p[i])
199+
gsub(/</, "\\&lt;", p[i])
200+
}
201+
out = out p[i]
202+
if (i < n) out = out "`"
203+
}
204+
print out
205+
}
206+
' "$f" > "${f}.tmp" && mv "${f}.tmp" "$f"
207+
done
208+
tools/check-docs-mdx-parse "fern/versions/${version}-content"
195209
echo "Extracted docs from $version"
196210
else
197211
echo "::error::Tag $version not found — cannot pin frozen docs content"

.github/workflows/renovate.yaml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,5 +122,18 @@ jobs:
122122
# checksums getting committed alongside a version bump (the version
123123
# changes but the helmfile_checksums/chainsaw_checksums block does
124124
# not), which then breaks E2E/CLI E2E at the sha256 verify step.
125-
RENOVATE_ALLOWED_POST_UPGRADE_COMMANDS: '["^\\./tools/update-[a-z]+-checksums "]'
125+
# `[a-z-]+` (not `[a-z]+`) so hyphenated tool names match too, e.g.
126+
# tools/update-helm-diff-checksums.
127+
#
128+
# The stale-checksum symptom above is not uniform across the three
129+
# tools. A hook that runs but *fails* (renamed asset, egress blip)
130+
# leaves the new version pinned against the old checksums, and only
131+
# helmfile/chainsaw fail a PR-gating job for it — qualification.yaml
132+
# feeds their sha256 into setup-build-tools, which verifies it.
133+
# helm_diff_checksums is read solely by tests/uat/lib/phases.sh, so a
134+
# stale helm-diff pin stays green on every PR check and first surfaces
135+
# in nightly UAT, across all clouds. Renovate's "Artifact update
136+
# problem" warning in the PR body is the signal to heed; helm-diff is
137+
# deliberately not auto-merged, so a human sees it.
138+
RENOVATE_ALLOWED_POST_UPGRADE_COMMANDS: '["^\\./tools/update-[a-z-]+-checksums "]'
126139
LOG_LEVEL: ${{ inputs.logLevel || 'info' }}

.github/workflows/uat-aws.yaml

Lines changed: 85 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ on:
6868
description: 'Skip UAT test execution (manual runs only; the nightly batch always tests).'
6969
type: boolean
7070
default: false
71+
deployer:
72+
# helmfile = <accelerator>-<intent>-config.yaml (the existing
73+
# filename shape every cell has always used). argocd = insert
74+
# -argocd before -config.yaml (e.g. h100-training-argocd-config.yaml).
75+
# See issue #2194.
76+
description: 'Deployer variant.'
77+
type: string
78+
default: helmfile
7179

7280
permissions:
7381
contents: read
@@ -88,17 +96,28 @@ jobs:
8896
if: always() && github.repository == 'nvidia/aicr'
8997
runs-on: ubuntu-latest
9098
# Budget = uncapped non-UAT steps (build, validator image push, EKS
91-
# provisioning, evidence upload ~30m) + UAT phase steps (prep 15 + install 90
92-
# + validate 40 + the intent-selected CUJ (train 25 OR serve 40) + verify 5
93-
# = up to 190) + ~8 failure-path debug collection (Collect cluster debug
94-
# bundle) + the always()-run Destroy Cluster teardown. Only one of
95-
# train/serve runs per intent, so the budget counts the larger (serve).
96-
# Teardown = up to 3 retries × ~18m real destroy = up to ~54m. The teardown
97-
# MUST fit in this budget: a job-level timeout cancels pending always()
98-
# steps, so an undersized cap would skip teardown and leak the GPU node /
99-
# capacity reservation even after a LATE failure. ~30 setup + 190 UAT + ~8
100-
# debug = 228, + ~54 teardown = 282, so 300 leaves the teardown clear
101-
# headroom on a worst-case run.
99+
# provisioning, evidence upload ~30m) + UAT phase steps + ~8 failure-path
100+
# debug collection + the always()-run Destroy Cluster teardown (up to 3
101+
# retries × ~18m real destroy = up to ~54m). The teardown MUST fit in this
102+
# cap: a job-level timeout cancels pending always() steps, so an undersized
103+
# cap would skip teardown and leak the GPU node / capacity reservation
104+
# even after a LATE failure. Only one of train/serve runs per intent, so
105+
# the UAT phase-step count uses the larger.
106+
#
107+
# UAT-phase-step budgets by (deployer, intent), using the current
108+
# timeout-minutes on each step below:
109+
# helmfile+training: prep 15 + install 90 + validate 40 + train 25 + verify 5 = 175
110+
# helmfile+inference: prep 15 + install 90 + validate 40 + serve 40 + verify 5 = 190
111+
# argocd+training: prep 15 + install 110 + validate 40 + train 25 + verify 5 = 195 (issue #2194, aws-h100 only)
112+
# argocd+inference: not shipped today; would be 15 + 110 + 40 + 40 + 5 = 210 (needs cap bump below)
113+
#
114+
# Total = ~30 setup + <UAT> + ~8 debug + ~54 teardown. On today's
115+
# worst-case (argocd+training or helmfile+inference), ~30 + max(195,190)
116+
# + ~8 + ~54 = ~287, so 300 leaves ~13m of teardown headroom.
117+
# NOTE: the "not shipped today" argocd+inference row would push total to
118+
# ~302, over the cap. If a future PR wires the argocd variant for the
119+
# inference cell (or re-enables phase_serve alongside argocd), raise this
120+
# to ~330 in the same PR.
102121
timeout-minutes: 300
103122
permissions:
104123
contents: read
@@ -139,10 +158,19 @@ jobs:
139158
&& format('aicr-uat-day-{0}-{1}-{2}', inputs.slug, inputs.slot, github.run_id)
140159
|| format('aicr-uat-{0}', github.run_id) }}
141160
CLUSTER_CONFIG: ${{ inputs.cluster_config_path }}
142-
# The AICRConfig is selected by accelerator + intent
143-
# (<accelerator>-<intent>-config.yaml); both intents drive the same
144-
# cluster-config (GPU pool from the reservation, system/CPU pools dynamic).
145-
TEST_CONFIG: ${{ inputs.test_config_dir }}/${{ inputs.accelerator }}-${{ inputs.intent }}-config.yaml
161+
# The AICRConfig is selected by accelerator + intent + deployer:
162+
# deployer=helmfile → <accelerator>-<intent>-config.yaml (the default;
163+
# backwards-compatible with every existing cell)
164+
# deployer=X (X!=helmfile) → <accelerator>-<intent>-X-config.yaml
165+
# (e.g. deployer=argocd → h100-training-argocd-config.yaml, issue #2194)
166+
# Both variants drive the same cluster-config (GPU pool from the
167+
# reservation, system/CPU pools dynamic). The step-level check below asserts
168+
# the resolved file exists so an unwired deployer variant fails early
169+
# (before any provisioning) rather than deep in phase_prep.
170+
TEST_CONFIG: >-
171+
${{ inputs.test_config_dir }}/${{ inputs.accelerator }}-${{ inputs.intent }}${{
172+
inputs.deployer != 'helmfile' && format('-{0}', inputs.deployer) || ''
173+
}}-config.yaml
146174
# v0.4.27 — required for node-group taints in the cluster-config schema
147175
# (GPU pool carries skyhook.nvidia.com=runtime-required:NoSchedule until
148176
# NodeWrite finishes tuning + reboot). Earlier v0.4.x pins predate taint
@@ -190,6 +218,7 @@ jobs:
190218
LIFECYCLE: ${{ inputs.lifecycle }}
191219
SLUG: ${{ inputs.slug }}
192220
SLOT: ${{ inputs.slot }}
221+
DEPLOYER: ${{ inputs.deployer }}
193222
run: |
194223
set -euo pipefail
195224
case "${INTENT}" in
@@ -233,6 +262,15 @@ jobs:
233262
echo "::error::${CLUSTER_CONFIG} has no .deployment.tenancy"; exit 1
234263
fi
235264
echo "AWS_ACCOUNT_ID=${TENANCY}" >> "${GITHUB_ENV}"
265+
# Validate deployer after the account export (which teardown depends on).
266+
# workflow_dispatch constrains this to a choice, but workflow_call passes
267+
# free strings, so a typo from a caller must surface as a clear error here
268+
# rather than as a missing test-config file below or a mysterious dispatch
269+
# deep in phases.sh. Allowlist mirrors uat-run.yaml's choice input.
270+
case "${DEPLOYER}" in
271+
helmfile|argocd) ;;
272+
*) echo "::error::unsupported deployer '${DEPLOYER}' (want helmfile|argocd)"; exit 1 ;;
273+
esac
236274
# The test config only matters for the run phases (nightly / daytime-up
237275
# provision+deploy). daytime-down just tears the held cluster down and
238276
# never reads it, so skip the existence check there — a missing sibling
@@ -685,21 +723,36 @@ jobs:
685723
aws-region: ${{ env.AWS_REGION }}
686724
role-session-name: GitHubActions-UAT-AICR-Install
687725

688-
- name: UAT - install (helmfile apply)
726+
- name: UAT - install (helmfile apply or argocd sync)
689727
id: install
690728
if: steps.prep.outcome == 'success'
691-
# helmfile apply (up to HELMFILE_TIMEOUT_SECONDS, 20m) + the post-install
692-
# readiness gate, which runs `aicr validate --phase deployment` until it
693-
# passes READINESS_CONSECUTIVE_PASSES times (up to READINESS_TIMEOUT_SECONDS,
694-
# 60m, spanning nodewright tuning + reboots, which can run past 30m on a
695-
# cold GPU node). Sized to exceed one helmfile attempt + the full gate
696-
# window (20 + 60 = 80, + margin) so the gate's own fail-closed path runs
697-
# before GitHub Actions kills the step.
698-
timeout-minutes: 90
729+
# Step-level timeout must fit the LARGER of the two deployer branches,
730+
# because both dispatch from `./run install` and share this cap:
731+
# helmfile: HELMFILE_TIMEOUT_SECONDS 20m + gate 60m = 80m
732+
# argocd: helm-install 5m + apply+sync 30m + root-grace 2m + gate 60m = 97m
733+
# ARGOCD_HELM_TIMEOUT_SECONDS + ARGOCD_SYNC_TIMEOUT_SECONDS +
734+
# ARGOCD_ROOT_APP_GRACE_SECONDS + READINESS_TIMEOUT_SECONDS in
735+
# tests/uat/lib/phases.sh drive the argocd side. Sized at 110m so the
736+
# argocd fail-closed paths (each shared-budget retry loop's `::error::`)
737+
# run before GitHub Actions kills the step. The gate's own fail-closed
738+
# path still fires within budget on both branches.
739+
timeout-minutes: 110
699740
shell: bash
700741
env:
701742
AICR_BIN: ${{ github.workspace }}/aicr
702743
RUN_ID: ${{ github.run_id }}
744+
# Consumed only by the argocd deployer branch (install_argocd in
745+
# tests/uat/lib/phases.sh), which provisions an in-cluster ghcr.io
746+
# repo-creds Secret so Argo CD's repo-server can pull the bundle
747+
# pushed by phase_prep. GITHUB_ACTOR is auto-injected by the
748+
# Actions runtime; GITHUB_TOKEN is not, so pass it explicitly.
749+
# The token is scoped to `packages: write` at the job level (see
750+
# permissions block above), which is exactly what the Secret
751+
# needs. Set the token ONLY for the argocd branch so helmfile's
752+
# kubectl/aicr/chart-hook children don't inherit it in-env;
753+
# install_argocd's `:?` guard fires on the empty default, which
754+
# is what we want if a caller misconfigures the deployer input.
755+
GITHUB_TOKEN: ${{ inputs.deployer == 'argocd' && github.token || '' }}
703756
run: ./tests/uat/aws/run install "${TEST_CONFIG}"
704757

705758
# Runs ALL validate phases (deployment + conformance + performance), not
@@ -883,12 +936,20 @@ jobs:
883936
SUMMARY_AICR_VERSION: ${{ inputs.aicr_version }}
884937
SUMMARY_INTENT: ${{ inputs.intent }}
885938
SUMMARY_LIFECYCLE: ${{ inputs.lifecycle }}
939+
SUMMARY_DEPLOYER: ${{ inputs.deployer }}
886940
run: |
887941
{
888942
echo "## UAT Results (AWS)"
889943
echo ""
890944
printf '**Reservation:** `%s` · **Intent:** `%s` · **Lifecycle:** `%s`\n' \
891945
"$SUMMARY_RESERVATION" "$SUMMARY_INTENT" "$SUMMARY_LIFECYCLE"
946+
# Render the deployer only when it deviates from the default
947+
# (helmfile), so existing cells' summaries are unchanged and
948+
# only argocd cells surface the extra line — makes it easy to
949+
# spot which lane a run was in from the Test Summary alone.
950+
if [[ "$SUMMARY_DEPLOYER" != "" && "$SUMMARY_DEPLOYER" != "helmfile" ]]; then
951+
printf '**Deployer:** `%s`\n' "$SUMMARY_DEPLOYER"
952+
fi
892953
printf '**Cluster:** `%s`\n' "$DEPLOYMENT_ID"
893954
printf '**AICR version:** `%s`\n' "${SUMMARY_AICR_VERSION:-main (build from source)}"
894955
echo "**Build:** \`${{ github.sha }}\` (branch: \`${{ github.ref_name }}\`)"

0 commit comments

Comments
 (0)