fix(docs): diagram update #12239
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # | |
| # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
| # SPDX-License-Identifier: Apache-2.0 | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| # | |
| name: NICo Core CI | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - "pull-request/[0-9]+" | |
| tags: | |
| - "v[0-9]*.[0-9]*.[0-9]*" | |
| - "v[0-9][0-9][0-9][0-9].[0-9][0-9].[0-9][0-9]*" | |
| - "v[0-9].[0-9].[0-9]-rc[0-9]*" | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ${{ github.event_name == 'push' && github.run_attempt == '1' && startsWith(github.ref, 'refs/heads/pull-request/') && format('core-pr-{0}', github.ref_name) || format('core-run-{0}-{1}', github.run_id, github.run_attempt) }} | |
| cancel-in-progress: ${{ github.event_name == 'push' && github.run_attempt == '1' && startsWith(github.ref, 'refs/heads/pull-request/') }} | |
| env: | |
| # Build configuration | |
| CARGO_INCREMENTAL: 0 | |
| CARGO_HOME: ${{ github.workspace }}/cargo | |
| FF_USE_FASTZIP: "true" | |
| GIT_SUBMODULE_STRATEGY: recursive | |
| jobs: | |
| changes: | |
| name: Detect Core CI Gate | |
| runs-on: ubuntu-latest | |
| outputs: | |
| run_core_ci: ${{ steps.gate.outputs.run_core_ci }} | |
| non_rest_changed: ${{ steps.non-rest-changes.outputs.non_rest }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| fetch-depth: 0 | |
| - name: Check Core CI concurrency policy | |
| run: bash scripts/check-ci-concurrency.sh core | |
| - name: Test CI final-gate checker | |
| run: python3 -B .github/ci/test_check_ci_gate.py | |
| - name: Test secret-scan range resolver | |
| run: bash .github/ci/test-resolve-secret-scan-range.sh | |
| - name: Check Core CI final-gate inventory | |
| run: python3 -B .github/ci/check_ci_gate.py inventory --policy core .github/workflows/ci.yaml | |
| - name: Detect non-rest changes | |
| id: non-rest-changes | |
| if: startsWith(github.ref, 'refs/heads/pull-request/') | |
| uses: dorny/paths-filter@v3 | |
| with: | |
| base: main | |
| predicate-quantifier: every | |
| filters: | | |
| non_rest: | |
| - '**' | |
| - '!rest-api/**' | |
| - '!.github/workflows/rest-*.yml' | |
| - '!helm/rest/**' | |
| - '!docs/**' | |
| - '!fern/**' | |
| - name: Decide whether Core CI should run | |
| id: gate | |
| env: | |
| REF: ${{ github.ref }} | |
| COMMIT_MESSAGE: ${{ github.event.head_commit.message || '' }} | |
| NON_REST_CHANGED: ${{ steps.non-rest-changes.outputs.non_rest }} | |
| run: | | |
| run_core_ci=true | |
| if [[ "${REF}" =~ ^refs/heads/pull-request/[0-9]+$ ]]; then | |
| run_core_ci="${NON_REST_CHANGED}" | |
| fi | |
| if [[ "${COMMIT_MESSAGE}" =~ ci-run-complete-pipeline ]]; then | |
| run_core_ci=true | |
| fi | |
| echo "run_core_ci=${run_core_ci}" >> "$GITHUB_OUTPUT" | |
| echo "Core CI gate: ${run_core_ci}" | |
| # ============================================================================ | |
| # PREPARE STAGE | |
| # ============================================================================ | |
| prepare: | |
| needs: | |
| - changes | |
| if: ${{ needs.changes.outputs.run_core_ci == 'true' }} | |
| runs-on: linux-amd64-cpu4 | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| image_registry: ${{ steps.registry.outputs.registry }} | |
| source_registry_host: ${{ steps.registry.outputs.source_registry_host }} | |
| bases_rebuilt: ${{ steps.base-container-gate.outputs.build_container_x86_64_run == 'true' || steps.base-container-gate.outputs.runtime_container_x86_64_run == 'true' || steps.base-container-gate.outputs.build_container_aarch64_run == 'true' || steps.base-container-gate.outputs.runtime_container_aarch64_run == 'true' || steps.base-container-gate.outputs.build_artifacts_container_x86_64_run == 'true' || steps.base-container-gate.outputs.build_artifacts_container_aarch64_run == 'true' }} | |
| publish_images: ${{ steps.release-gate.outputs.publish_built_container }} | |
| lint_container_ref: ${{ steps.release-gate.outputs.lint_container_ref }} | |
| target_ngc_path: ${{ steps.registry.outputs.target_ngc_path }} | |
| registry_host: ${{ steps.registry.outputs.registry_host }} | |
| prod_image_registry: ${{ steps.registry.outputs.prod_registry }} | |
| helm_version: ${{ steps.version.outputs.helm_version }} | |
| short_sha: ${{ steps.version.outputs.short_sha }} | |
| major_minor_version: ${{ steps.base-container-gate.outputs.major_minor_version }} | |
| extras_container_ref: ${{ steps.resolve-extras.outputs.extras_container_ref }} | |
| build_container_x86_64_run: ${{ steps.base-container-gate.outputs.build_container_x86_64_run }} | |
| build_container_x86_64_version: ${{ steps.base-container-gate.outputs.build_container_x86_64_version }} | |
| build_container_x86_64_version_latest: ${{ steps.base-container-gate.outputs.build_container_x86_64_version_latest }} | |
| build_container_x86_64_ref: ${{ steps.base-container-gate.outputs.build_container_x86_64_ref }} | |
| runtime_container_x86_64_ref: ${{ steps.base-container-gate.outputs.runtime_container_x86_64_ref }} | |
| build_container_aarch64_ref: ${{ steps.base-container-gate.outputs.build_container_aarch64_ref }} | |
| runtime_container_aarch64_ref: ${{ steps.base-container-gate.outputs.runtime_container_aarch64_ref }} | |
| build_artifacts_container_x86_64_ref: ${{ steps.base-container-gate.outputs.build_artifacts_container_x86_64_ref }} | |
| build_artifacts_container_aarch64_ref: ${{ steps.base-container-gate.outputs.build_artifacts_container_aarch64_ref }} | |
| runtime_container_x86_64_run: ${{ steps.base-container-gate.outputs.runtime_container_x86_64_run }} | |
| runtime_container_x86_64_version: ${{ steps.base-container-gate.outputs.runtime_container_x86_64_version }} | |
| runtime_container_x86_64_version_latest: ${{ steps.base-container-gate.outputs.runtime_container_x86_64_version_latest }} | |
| build_container_aarch64_run: ${{ steps.base-container-gate.outputs.build_container_aarch64_run }} | |
| build_container_aarch64_version: ${{ steps.base-container-gate.outputs.build_container_aarch64_version }} | |
| build_container_aarch64_version_latest: ${{ steps.base-container-gate.outputs.build_container_aarch64_version_latest }} | |
| build_artifacts_container_x86_64_run: ${{ steps.base-container-gate.outputs.build_artifacts_container_x86_64_run }} | |
| build_artifacts_container_x86_64_version: ${{ steps.base-container-gate.outputs.build_artifacts_container_x86_64_version }} | |
| build_artifacts_container_x86_64_version_latest: ${{ steps.base-container-gate.outputs.build_artifacts_container_x86_64_version_latest }} | |
| build_artifacts_container_aarch64_run: ${{ steps.base-container-gate.outputs.build_artifacts_container_aarch64_run }} | |
| build_artifacts_container_aarch64_version: ${{ steps.base-container-gate.outputs.build_artifacts_container_aarch64_version }} | |
| build_artifacts_container_aarch64_version_latest: ${{ steps.base-container-gate.outputs.build_artifacts_container_aarch64_version_latest }} | |
| runtime_container_aarch64_run: ${{ steps.base-container-gate.outputs.runtime_container_aarch64_run }} | |
| runtime_container_aarch64_version: ${{ steps.base-container-gate.outputs.runtime_container_aarch64_version }} | |
| runtime_container_aarch64_version_latest: ${{ steps.base-container-gate.outputs.runtime_container_aarch64_version_latest }} | |
| proto_files_changed: ${{ steps.base-container-gate.outputs.proto_files_changed }} | |
| core_rpc_proto_files_changed: ${{ steps.base-container-gate.outputs.core_rpc_proto_files_changed }} | |
| source_files_changed: ${{ steps.base-container-gate.outputs.source_files_changed }} | |
| release_build_args: ${{ steps.release-metadata.outputs.build_args }} | |
| release_build_args_aarch64: ${{ steps.release-metadata.outputs.build_args_aarch64 }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Set up Python for CI helpers | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.13' | |
| - name: Check boot output contracts | |
| run: python3 -m unittest discover -s .github/ci -p 'test_validate_boot_outputs.py' | |
| - name: Detect build-container-x86_64 changes | |
| id: build-container-changes | |
| uses: dorny/paths-filter@v3 | |
| with: | |
| filters: | | |
| build_container: | |
| - 'dev/docker/Dockerfile.build-container-x86_64' | |
| runtime_container: | |
| - 'dev/docker/Dockerfile.runtime-container-x86_64' | |
| runtime_container_aarch64: | |
| - 'dev/docker/Dockerfile.runtime-container-aarch64' | |
| build_container_aarch64: | |
| - 'dev/docker/Dockerfile.build-container-aarch64' | |
| build_artifacts_x86_64: | |
| - 'dev/docker/Dockerfile.build-artifacts-container-x86_64' | |
| build_artifacts_aarch64: | |
| - 'dev/docker/Dockerfile.build-artifacts-container-aarch64' | |
| proto_files: | |
| - '**/*.proto' | |
| core_rpc_proto_files: | |
| - 'crates/rpc/proto/**' | |
| source_files: | |
| - 'crates/**' | |
| - 'Cargo.toml' | |
| - 'Cargo.lock' | |
| - 'Makefile.toml' | |
| - 'rust-toolchain.toml' | |
| - '.github/workflows/ci.yaml' | |
| - 'dev/k8s/machine-a-tron-controller/**' | |
| - name: Calculate version | |
| id: version | |
| run: | | |
| set -euo pipefail | |
| SHORT_SHA=$(git rev-parse --short=7 HEAD) | |
| echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT | |
| echo "Using Git describe to extract version as VERSION and HELM_VERSION" | |
| # On a tagged commit this collapses to the clean tag (ex v2.0.0-rc.1); | |
| # otherwise it is the long form (ex v2.0.0-3-gabc1234) for dev builds. | |
| VERSION=$(git describe --tags --first-parent --always) | |
| # Record whether describe returned a long (dev) tag ending in '-g<sha>' or a | |
| # short/clean (pre)release tag. Only long tags need the helm suffix rewrite. | |
| if [[ "$VERSION" =~ -g[0-9a-f]+$ ]]; then | |
| IS_LONG_TAG="true" | |
| else | |
| IS_LONG_TAG="false" | |
| fi | |
| # HELM_VERSION strips leading 'v' for strict SemVer. For long (dev) tags the | |
| # git-describe suffix '-<N>-g<sha>' has its last '-' rewritten to '.'; short | |
| # (pre)release tags (ex v2.0.0, v2.0.0-rc.3) are already valid SemVer as-is. | |
| HELM_VERSION="${VERSION#v}" | |
| if [[ "$IS_LONG_TAG" == "true" ]]; then | |
| HELM_VERSION=$(echo "$HELM_VERSION" | sed 's/\(.*\)-/\1./') | |
| fi | |
| # Strict SemVer validation. Regex adapted to POSIX ERE (non-capturing | |
| # groups '(?:...)' -> '(...)', '\d' -> '[0-9]') from the official suggested | |
| # SemVer regex: | |
| # https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string | |
| SEMVER_STRICT='^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\+[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*)?$' | |
| if [[ ! "$HELM_VERSION" =~ $SEMVER_STRICT ]]; then | |
| echo "::error::HELM_VERSION '${HELM_VERSION}' (derived from VERSION '${VERSION}') failed strict SemVer validation" | |
| exit 1 | |
| fi | |
| echo "version=${VERSION}" >> $GITHUB_OUTPUT | |
| echo "helm_version=${HELM_VERSION}" >> $GITHUB_OUTPUT | |
| echo "Calculated VERSION: ${VERSION}" | |
| echo "Calculated HELM_VERSION: ${HELM_VERSION}" | |
| # Output to GitHub Summary | |
| echo "## 📦 Build Version" >> $GITHUB_STEP_SUMMARY | |
| echo "| Variable | Value |" >> $GITHUB_STEP_SUMMARY | |
| echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY | |
| echo "| VERSION | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| HELM_VERSION | \`${HELM_VERSION}\` |" >> $GITHUB_STEP_SUMMARY | |
| # Single definition point for registries. The canonical repo keeps | |
| # upstream defaults; any other repo must set NICO_TARGET_IMAGE_REGISTRY | |
| # and may not target the canonical registries (fail closed for mirrors). | |
| # Source registry = read-only dependencies (extras, non-rebuilt base | |
| # containers); target registry = everything this run publishes. | |
| - name: Resolve container registries | |
| id: registry | |
| env: | |
| TARGET_OVERRIDE: ${{ vars.NICO_TARGET_IMAGE_REGISTRY }} | |
| SOURCE_OVERRIDE: ${{ vars.NICO_SOURCE_IMAGE_REGISTRY }} | |
| EXTRAS_OVERRIDE: ${{ vars.NICO_EXTRAS_IMAGE }} | |
| NGC_PATH_OVERRIDE: ${{ vars.NICO_TARGET_NGC_PATH }} | |
| HAS_SOURCE_TOKEN: ${{ secrets.NICO_SOURCE_REGISTRY_TOKEN != '' }} | |
| HAS_SOURCE_USERNAME: ${{ secrets.NICO_SOURCE_REGISTRY_USERNAME != '' }} | |
| run: | | |
| set -euo pipefail | |
| CANONICAL_DEV="nvcr.io/0837451325059433/carbide-dev" | |
| CANONICAL_PROD="nvcr.io/0837451325059433/carbide" | |
| for V in "${TARGET_OVERRIDE}" "${SOURCE_OVERRIDE}" "${EXTRAS_OVERRIDE}" "${NGC_PATH_OVERRIDE}"; do | |
| if [[ "${V}" == *$'\n'* || "${V}" == *$'\r'* ]]; then | |
| echo "::error::registry variables must be single-line values" | |
| exit 1 | |
| fi | |
| done | |
| if [[ "${GITHUB_REPOSITORY}" == "NVIDIA/infra-controller" ]]; then | |
| # canonical endpoints are constants: repository variables must not | |
| # be able to redirect canonical credentials without a workflow diff | |
| TARGET="${CANONICAL_DEV}" | |
| PROD="${CANONICAL_PROD}" | |
| else | |
| if [[ -z "${TARGET_OVERRIDE}" ]]; then | |
| echo "::error::NICO_TARGET_IMAGE_REGISTRY repository variable is required outside NVIDIA/infra-controller" | |
| exit 1 | |
| fi | |
| TARGET="${TARGET_OVERRIDE}" | |
| PROD="${TARGET}" | |
| # normalize host (case, default port) before the canonical denylist | |
| normalize() { | |
| local x="${1%/}" h p | |
| h="${x%%/*}"; p="${x#*/}" | |
| h="${h,,}"; h="${h%:443}" | |
| printf '%s/%s' "${h}" "${p,,}" | |
| } | |
| for R in "${TARGET}" "${PROD}"; do | |
| N="$(normalize "${R}")" | |
| if [[ "${N}" == "${CANONICAL_DEV}" || "${N}" == "${CANONICAL_PROD}" ]]; then | |
| echo "::error::this repository must not target the canonical registry ${R}" | |
| exit 1 | |
| fi | |
| done | |
| fi | |
| TARGET="${TARGET%/}" | |
| PROD="${PROD%/}" | |
| if [[ "${GITHUB_REPOSITORY}" == "NVIDIA/infra-controller" ]]; then | |
| SOURCE="${CANONICAL_DEV}" | |
| EXTRAS="${SOURCE}/nvmetal-carbide-extras:latest" | |
| else | |
| SOURCE="${SOURCE_OVERRIDE:-${CANONICAL_DEV}}" | |
| SOURCE="${SOURCE%/}" | |
| EXTRAS="${EXTRAS_OVERRIDE:-${SOURCE}/nvmetal-carbide-extras:latest}" | |
| fi | |
| if [[ "${EXTRAS%%/*}" != "${SOURCE%%/*}" ]]; then | |
| echo "::error::NICO_EXTRAS_IMAGE must live on the source registry host (${SOURCE%%/*}); refusing to send source credentials to ${EXTRAS%%/*}" | |
| exit 1 | |
| fi | |
| if [[ "${GITHUB_REPOSITORY}" == "NVIDIA/infra-controller" ]]; then | |
| NGC_PATH="${TARGET#*/}" | |
| else | |
| NGC_PATH="${NGC_PATH_OVERRIDE:-}" | |
| case "${NGC_PATH%/}" in | |
| "${CANONICAL_DEV#*/}"|"${CANONICAL_PROD#*/}") | |
| echo "::error::this repository must not publish helm/resources to the canonical NGC path ${NGC_PATH}" | |
| exit 1;; | |
| esac | |
| fi | |
| if [[ "${SOURCE%%/*}" != "nvcr.io" && ( "${HAS_SOURCE_TOKEN}" != "true" || "${HAS_SOURCE_USERNAME}" != "true" ) ]]; then | |
| echo "::error::non-NVCR source registry requires NICO_SOURCE_REGISTRY_* secrets (refusing to send NVCR credentials elsewhere)" | |
| exit 1 | |
| fi | |
| { | |
| echo "registry=${TARGET}" | |
| echo "registry_host=${TARGET%%/*}" | |
| echo "source_registry=${SOURCE}" | |
| echo "source_registry_host=${SOURCE%%/*}" | |
| echo "extras_image=${EXTRAS}" | |
| echo "prod_registry=${PROD}" | |
| echo "target_ngc_path=${NGC_PATH}" | |
| } >> "$GITHUB_OUTPUT" | |
| echo "Registries: target=${TARGET} source=${SOURCE} prod=${PROD}" | |
| - name: Login to NVCR | |
| uses: ./.github/actions/docker-auth | |
| with: | |
| registry: ${{ steps.registry.outputs.source_registry_host }} | |
| username: ${{ secrets.NICO_SOURCE_REGISTRY_USERNAME || secrets.NVCR_USERNAME }} | |
| token: ${{ secrets.NICO_SOURCE_REGISTRY_TOKEN || secrets.NVCR_TOKEN }} | |
| - name: Resolve pinned carbide-extras image digest | |
| id: resolve-extras | |
| env: | |
| EXTRAS_IMAGE: ${{ steps.registry.outputs.extras_image }} | |
| run: | | |
| set -euo pipefail | |
| docker pull "${EXTRAS_IMAGE}" | |
| EXTRAS_REF="$(docker inspect --format='{{index .RepoDigests 0}}' "${EXTRAS_IMAGE}")" | |
| if [ -z "${EXTRAS_REF}" ]; then | |
| echo "::error::Failed to resolve digest for ${EXTRAS_IMAGE}" | |
| exit 1 | |
| fi | |
| echo "extras_container_ref=${EXTRAS_REF}" >> "$GITHUB_OUTPUT" | |
| echo "Resolved extras image to pinned digest: ${EXTRAS_REF}" | |
| echo "## 📌 Carbide Extras Image" >> "$GITHUB_STEP_SUMMARY" | |
| echo "| Requested | Pinned |" >> "$GITHUB_STEP_SUMMARY" | |
| echo "|-----------|--------|" >> "$GITHUB_STEP_SUMMARY" | |
| echo "| \`${EXTRAS_IMAGE}\` | \`${EXTRAS_REF}\` |" >> "$GITHUB_STEP_SUMMARY" | |
| - name: Decide if build-container-x86_64 must run | |
| id: base-container-gate | |
| env: | |
| TARGET_REGISTRY: ${{ steps.registry.outputs.registry }} | |
| SOURCE_REGISTRY: ${{ steps.registry.outputs.source_registry }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| COMMIT_MESSAGE: ${{ github.event.head_commit.message || '' }} | |
| VERSION: ${{ steps.version.outputs.version }} | |
| BUILD_CONTAINER_X86_64_DOCKERFILE_CHANGED: ${{ steps.build-container-changes.outputs.build_container }} | |
| RUNTIME_CONTAINER_X86_64_DOCKERFILE_CHANGED: ${{ steps.build-container-changes.outputs.runtime_container }} | |
| RUNTIME_CONTAINER_AARCH64_DOCKERFILE_CHANGED: ${{ steps.build-container-changes.outputs.runtime_container_aarch64 }} | |
| BUILD_CONTAINER_AARCH64_DOCKERFILE_CHANGED: ${{ steps.build-container-changes.outputs.build_container_aarch64 }} | |
| BUILD_ARTIFACTS_X86_64_DOCKERFILE_CHANGED: ${{ steps.build-container-changes.outputs.build_artifacts_x86_64 }} | |
| BUILD_ARTIFACTS_AARCH64_DOCKERFILE_CHANGED: ${{ steps.build-container-changes.outputs.build_artifacts_aarch64 }} | |
| PROTO_FILES_CHANGED: ${{ steps.build-container-changes.outputs.proto_files }} | |
| CORE_RPC_PROTO_FILES_CHANGED: ${{ steps.build-container-changes.outputs.core_rpc_proto_files }} | |
| SOURCE_FILES_CHANGED: ${{ steps.build-container-changes.outputs.source_files }} | |
| run: | | |
| build_container_x86_64_run=false | |
| runtime_container_x86_64_run=false | |
| build_container_aarch64_run=false | |
| runtime_container_aarch64_run=false | |
| build_artifacts_container_x86_64_run=false | |
| build_artifacts_container_aarch64_run=false | |
| proto_files_changed=false | |
| core_rpc_proto_files_changed=false | |
| source_files_changed=false | |
| if [[ "${COMMIT_MESSAGE}" =~ ci-rebuild-base-containers ]]; then | |
| build_container_x86_64_run=true | |
| runtime_container_x86_64_run=true | |
| build_container_aarch64_run=true | |
| runtime_container_aarch64_run=true | |
| build_artifacts_container_x86_64_run=true | |
| build_artifacts_container_aarch64_run=true | |
| elif [[ "${BUILD_CONTAINER_X86_64_DOCKERFILE_CHANGED}" == "true" ]]; then | |
| build_container_x86_64_run=true | |
| fi | |
| if [[ "${PROTO_FILES_CHANGED}" == "true" ]]; then | |
| proto_files_changed=true | |
| fi | |
| if [[ "${CORE_RPC_PROTO_FILES_CHANGED}" == "true" ]]; then | |
| core_rpc_proto_files_changed=true | |
| fi | |
| if [[ "${SOURCE_FILES_CHANGED}" == "true" ]]; then | |
| source_files_changed=true | |
| fi | |
| if [[ "${RUNTIME_CONTAINER_X86_64_DOCKERFILE_CHANGED}" == "true" ]]; then | |
| runtime_container_x86_64_run=true | |
| fi | |
| if [[ "${RUNTIME_CONTAINER_AARCH64_DOCKERFILE_CHANGED}" == "true" ]]; then | |
| runtime_container_aarch64_run=true | |
| fi | |
| if [[ "${BUILD_CONTAINER_AARCH64_DOCKERFILE_CHANGED}" == "true" ]]; then | |
| build_container_aarch64_run=true | |
| fi | |
| if [[ "${BUILD_ARTIFACTS_X86_64_DOCKERFILE_CHANGED}" == "true" ]]; then | |
| build_artifacts_container_x86_64_run=true | |
| fi | |
| if [[ "${BUILD_ARTIFACTS_AARCH64_DOCKERFILE_CHANGED}" == "true" ]]; then | |
| build_artifacts_container_aarch64_run=true | |
| fi | |
| echo "build_container_x86_64_run=${build_container_x86_64_run}" >> "$GITHUB_OUTPUT" | |
| echo "runtime_container_x86_64_run=${runtime_container_x86_64_run}" >> "$GITHUB_OUTPUT" | |
| echo "build_container_aarch64_run=${build_container_aarch64_run}" >> "$GITHUB_OUTPUT" | |
| echo "runtime_container_aarch64_run=${runtime_container_aarch64_run}" >> "$GITHUB_OUTPUT" | |
| echo "build_artifacts_container_x86_64_run=${build_artifacts_container_x86_64_run}" >> "$GITHUB_OUTPUT" | |
| echo "build_artifacts_container_aarch64_run=${build_artifacts_container_aarch64_run}" >> "$GITHUB_OUTPUT" | |
| echo "proto_files_changed=${proto_files_changed}" >> "$GITHUB_OUTPUT" | |
| echo "core_rpc_proto_files_changed=${core_rpc_proto_files_changed}" >> "$GITHUB_OUTPUT" | |
| echo "source_files_changed=${source_files_changed}" >> "$GITHUB_OUTPUT" | |
| MAJOR_MINOR=$(echo "${VERSION}" | cut -d. -f1,2) | |
| echo "major_minor_version=${MAJOR_MINOR}" >> "$GITHUB_OUTPUT" | |
| if [[ "$build_container_x86_64_run" == "true" ]]; then | |
| echo "build_container_x86_64_version=${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "build_container_x86_64_ref=${TARGET_REGISTRY}/build-container-x86_64:${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "build_container_x86_64_version_latest=${TARGET_REGISTRY}/build-container-x86_64:${MAJOR_MINOR}-latest" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "build_container_x86_64_version=latest" >> "$GITHUB_OUTPUT" | |
| echo "build_container_x86_64_ref=${SOURCE_REGISTRY}/build-container-x86_64:latest" >> "$GITHUB_OUTPUT" | |
| echo "build_container_x86_64_version_latest=" >> "$GITHUB_OUTPUT" | |
| fi | |
| if [[ "$runtime_container_x86_64_run" == "true" ]]; then | |
| echo "runtime_container_x86_64_version=${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "runtime_container_x86_64_ref=${TARGET_REGISTRY}/runtime-container-x86_64:${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "runtime_container_x86_64_version_latest=${TARGET_REGISTRY}/runtime-container-x86_64:${MAJOR_MINOR}-latest" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "runtime_container_x86_64_version=latest" >> "$GITHUB_OUTPUT" | |
| echo "runtime_container_x86_64_ref=${SOURCE_REGISTRY}/runtime-container-x86_64:latest" >> "$GITHUB_OUTPUT" | |
| echo "runtime_container_x86_64_version_latest=" >> "$GITHUB_OUTPUT" | |
| fi | |
| if [[ "$build_container_aarch64_run" == "true" ]]; then | |
| echo "build_container_aarch64_version=${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "build_container_aarch64_ref=${TARGET_REGISTRY}/build-container-aarch64:${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "build_container_aarch64_version_latest=${TARGET_REGISTRY}/build-container-aarch64:${MAJOR_MINOR}-latest" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "build_container_aarch64_version=latest" >> "$GITHUB_OUTPUT" | |
| echo "build_container_aarch64_ref=${SOURCE_REGISTRY}/build-container-aarch64:latest" >> "$GITHUB_OUTPUT" | |
| echo "build_container_aarch64_version_latest=" >> "$GITHUB_OUTPUT" | |
| fi | |
| if [[ "$runtime_container_aarch64_run" == "true" ]]; then | |
| echo "runtime_container_aarch64_version=${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "runtime_container_aarch64_ref=${TARGET_REGISTRY}/runtime-container-aarch64:${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "runtime_container_aarch64_version_latest=${TARGET_REGISTRY}/runtime-container-aarch64:${MAJOR_MINOR}-latest" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "runtime_container_aarch64_version=latest" >> "$GITHUB_OUTPUT" | |
| echo "runtime_container_aarch64_ref=${SOURCE_REGISTRY}/runtime-container-aarch64:latest" >> "$GITHUB_OUTPUT" | |
| echo "runtime_container_aarch64_version_latest=" >> "$GITHUB_OUTPUT" | |
| fi | |
| if [[ "$build_artifacts_container_x86_64_run" == "true" ]]; then | |
| echo "build_artifacts_container_x86_64_version=${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "build_artifacts_container_x86_64_ref=${TARGET_REGISTRY}/build-artifacts-container-x86_64:${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "build_artifacts_container_x86_64_version_latest=${TARGET_REGISTRY}/build-artifacts-container-x86_64:${MAJOR_MINOR}-latest" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "build_artifacts_container_x86_64_version=latest" >> "$GITHUB_OUTPUT" | |
| echo "build_artifacts_container_x86_64_ref=${SOURCE_REGISTRY}/build-artifacts-container-x86_64:latest" >> "$GITHUB_OUTPUT" | |
| echo "build_artifacts_container_x86_64_version_latest=" >> "$GITHUB_OUTPUT" | |
| fi | |
| if [[ "$build_artifacts_container_aarch64_run" == "true" ]]; then | |
| echo "build_artifacts_container_aarch64_version=${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "build_artifacts_container_aarch64_ref=${TARGET_REGISTRY}/build-artifacts-container-aarch64:${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "build_artifacts_container_aarch64_version_latest=${TARGET_REGISTRY}/build-artifacts-container-aarch64:${MAJOR_MINOR}-latest" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "build_artifacts_container_aarch64_version=latest" >> "$GITHUB_OUTPUT" | |
| echo "build_artifacts_container_aarch64_ref=${SOURCE_REGISTRY}/build-artifacts-container-aarch64:latest" >> "$GITHUB_OUTPUT" | |
| echo "build_artifacts_container_aarch64_version_latest=" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Decide release container publish strategy | |
| id: release-gate | |
| env: | |
| TARGET_REGISTRY: ${{ steps.registry.outputs.registry }} | |
| HAS_TARGET_TOKEN: ${{ secrets.NICO_TARGET_REGISTRY_TOKEN != '' }} | |
| BUILD_X86_RUN: ${{ steps.base-container-gate.outputs.build_container_x86_64_run }} | |
| BUILD_X86_REF: ${{ steps.base-container-gate.outputs.build_container_x86_64_ref }} | |
| SOURCE_REGISTRY: ${{ steps.registry.outputs.source_registry }} | |
| HAS_TARGET_USERNAME: ${{ secrets.NICO_TARGET_REGISTRY_USERNAME != '' }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| REF: ${{ github.ref }} | |
| COMMIT_MESSAGE: ${{ github.event.head_commit.message || '' }} | |
| run: | | |
| PUBLISH=false | |
| if [[ "${EVENT_NAME}" == "pull_request" ]] || [[ "${REF}" =~ pull-request/[0-9]+ ]] ; then | |
| PUBLISH=false | |
| elif [[ "${REF}" == "refs/heads/main" ]] || [[ "${REF}" =~ ^refs/heads/release/ ]] || [[ "${REF}" =~ ^refs/tags/ ]] || [[ "${COMMIT_MESSAGE}" =~ ci-run-complete-pipeline ]]; then | |
| PUBLISH=true | |
| fi | |
| TARGET_HOST="${TARGET_REGISTRY%%/*}" | |
| if [[ "${PUBLISH}" == "true" && "${TARGET_HOST}" != "nvcr.io" && ( "${HAS_TARGET_TOKEN}" != "true" || "${HAS_TARGET_USERNAME}" != "true" ) ]]; then | |
| echo "::error::publishing to a non-NVCR target requires NICO_TARGET_REGISTRY_* secrets (refusing to send NVCR credentials elsewhere)" | |
| exit 1 | |
| fi | |
| echo "publish_built_container=${PUBLISH}" >> "$GITHUB_OUTPUT" | |
| # job containers log in before any step runs, so the lint job must | |
| # never point at a target ref that was not published (mirror PRs) | |
| if [[ "${BUILD_X86_RUN}" == "true" && "${GITHUB_REPOSITORY}" != "NVIDIA/infra-controller" && "${PUBLISH}" != "true" ]]; then | |
| echo "lint_container_ref=${SOURCE_REGISTRY}/build-container-x86_64:latest" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "lint_container_ref=${BUILD_X86_REF}" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Prepare release container build args | |
| id: release-metadata | |
| env: | |
| BUILD_REF: ${{ steps.base-container-gate.outputs.build_container_x86_64_ref }} | |
| RUNTIME_REF: ${{ steps.base-container-gate.outputs.runtime_container_x86_64_ref }} | |
| BUILD_REF_AARCH64: ${{ steps.base-container-gate.outputs.build_container_aarch64_ref }} | |
| RUNTIME_REF_AARCH64: ${{ steps.base-container-gate.outputs.runtime_container_aarch64_ref }} | |
| VERSION: ${{ steps.version.outputs.version }} | |
| SHORT_SHA: ${{ steps.version.outputs.short_sha }} | |
| HELM_VERSION: ${{ steps.version.outputs.helm_version }} | |
| BUILD_VERSION: ${{ steps.base-container-gate.outputs.build_container_x86_64_version }} | |
| RUNTIME_VERSION: ${{ steps.base-container-gate.outputs.runtime_container_x86_64_version }} | |
| BUILD_VERSION_AARCH64: ${{ steps.base-container-gate.outputs.build_container_aarch64_version }} | |
| RUNTIME_VERSION_AARCH64: ${{ steps.base-container-gate.outputs.runtime_container_aarch64_version }} | |
| run: | | |
| set -euo pipefail | |
| # x86_64 build args | |
| BUILD_ARGS=$(jq -n -c \ | |
| --arg VERSION "$VERSION" \ | |
| --arg SHORT_SHA "$SHORT_SHA" \ | |
| --arg BUILD "$BUILD_REF" \ | |
| --arg RUNTIME "$RUNTIME_REF" \ | |
| --arg HELM_VERSION "$HELM_VERSION" \ | |
| '{VERSION: $VERSION, CI_COMMIT_SHORT_SHA: $SHORT_SHA, CONTAINER_BUILD_X86_64: $BUILD, CONTAINER_RUNTIME_X86_64: $RUNTIME, CARBIDE_BUILD_HELM_VERSION: $HELM_VERSION}') | |
| echo "build_args=${BUILD_ARGS}" >> "$GITHUB_OUTPUT" | |
| # aarch64 build args | |
| BUILD_ARGS_AARCH64=$(jq -n -c \ | |
| --arg VERSION "$VERSION" \ | |
| --arg SHORT_SHA "$SHORT_SHA" \ | |
| --arg BUILD "$BUILD_REF_AARCH64" \ | |
| --arg RUNTIME "$RUNTIME_REF_AARCH64" \ | |
| --arg HELM_VERSION "$HELM_VERSION" \ | |
| '{VERSION: $VERSION, CI_COMMIT_SHORT_SHA: $SHORT_SHA, CONTAINER_BUILD_AARCH64: $BUILD, CONTAINER_RUNTIME_AARCH64: $RUNTIME, CARBIDE_BUILD_HELM_VERSION: $HELM_VERSION}') | |
| echo "build_args_aarch64=${BUILD_ARGS_AARCH64}" >> "$GITHUB_OUTPUT" | |
| # ============================================================================ | |
| # BUILD STAGE - Base Containers | |
| # ============================================================================ | |
| build-container-x86_64: | |
| if: needs.prepare.outputs.build_container_x86_64_run == 'true' | |
| needs: | |
| - prepare | |
| uses: ./.github/workflows/docker-build.yml | |
| with: | |
| source_registry_host: ${{ needs.prepare.outputs.source_registry_host }} | |
| dockerfile_path: dev/docker/Dockerfile.build-container-x86_64 | |
| image_name: ${{ needs.prepare.outputs.image_registry }}/build-container-x86_64 | |
| image_tag: ${{ needs.prepare.outputs.build_container_x86_64_version }} | |
| additional_tags: ${{ needs.prepare.outputs.build_container_x86_64_version_latest }} | |
| platforms: linux/amd64 | |
| runner: linux-amd64-cpu4 | |
| push: ${{ github.repository == 'NVIDIA/infra-controller' || needs.prepare.outputs.publish_images == 'true' }} | |
| load: true | |
| tag_latest: ${{ github.ref == 'refs/heads/main' }} | |
| secrets: inherit | |
| build-container-aarch64: | |
| if: needs.prepare.outputs.build_container_aarch64_run == 'true' | |
| needs: | |
| - prepare | |
| uses: ./.github/workflows/docker-build.yml | |
| with: | |
| source_registry_host: ${{ needs.prepare.outputs.source_registry_host }} | |
| dockerfile_path: dev/docker/Dockerfile.build-container-aarch64 | |
| image_name: ${{ needs.prepare.outputs.image_registry }}/build-container-aarch64 | |
| image_tag: ${{ needs.prepare.outputs.build_container_aarch64_version }} | |
| additional_tags: ${{ needs.prepare.outputs.build_container_aarch64_version_latest }} | |
| platforms: linux/arm64 | |
| runner: linux-arm64-cpu4 | |
| push: ${{ github.repository == 'NVIDIA/infra-controller' || needs.prepare.outputs.publish_images == 'true' }} | |
| load: false | |
| tag_latest: ${{ github.ref == 'refs/heads/main' }} | |
| secrets: inherit | |
| build-runtime-container-x86_64: | |
| if: needs.prepare.outputs.runtime_container_x86_64_run == 'true' | |
| needs: | |
| - prepare | |
| uses: ./.github/workflows/docker-build.yml | |
| with: | |
| source_registry_host: ${{ needs.prepare.outputs.source_registry_host }} | |
| dockerfile_path: dev/docker/Dockerfile.runtime-container-x86_64 | |
| image_name: ${{ needs.prepare.outputs.image_registry }}/runtime-container-x86_64 | |
| image_tag: ${{ needs.prepare.outputs.runtime_container_x86_64_version }} | |
| additional_tags: ${{ needs.prepare.outputs.runtime_container_x86_64_version_latest }} | |
| platforms: linux/amd64 | |
| runner: linux-amd64-cpu4 | |
| push: ${{ github.repository == 'NVIDIA/infra-controller' || needs.prepare.outputs.publish_images == 'true' }} | |
| load: true | |
| tag_latest: ${{ github.ref == 'refs/heads/main' }} | |
| secrets: inherit | |
| build-runtime-container-aarch64: | |
| if: needs.prepare.outputs.runtime_container_aarch64_run == 'true' | |
| needs: | |
| - prepare | |
| uses: ./.github/workflows/docker-build.yml | |
| with: | |
| source_registry_host: ${{ needs.prepare.outputs.source_registry_host }} | |
| dockerfile_path: dev/docker/Dockerfile.runtime-container-aarch64 | |
| image_name: ${{ needs.prepare.outputs.image_registry }}/runtime-container-aarch64 | |
| image_tag: ${{ needs.prepare.outputs.runtime_container_aarch64_version }} | |
| additional_tags: ${{ needs.prepare.outputs.runtime_container_aarch64_version_latest }} | |
| platforms: linux/arm64 | |
| runner: linux-arm64-cpu4 | |
| push: ${{ github.repository == 'NVIDIA/infra-controller' || needs.prepare.outputs.publish_images == 'true' }} | |
| load: false | |
| tag_latest: ${{ github.ref == 'refs/heads/main' }} | |
| secrets: inherit | |
| build-artifacts-container-x86_64: | |
| if: needs.prepare.outputs.build_artifacts_container_x86_64_run == 'true' | |
| needs: | |
| - prepare | |
| uses: ./.github/workflows/docker-build.yml | |
| with: | |
| source_registry_host: ${{ needs.prepare.outputs.source_registry_host }} | |
| dockerfile_path: dev/docker/Dockerfile.build-artifacts-container-x86_64 | |
| image_name: ${{ needs.prepare.outputs.image_registry }}/build-artifacts-container-x86_64 | |
| image_tag: ${{ needs.prepare.outputs.build_artifacts_container_x86_64_version }} | |
| additional_tags: ${{needs.prepare.outputs.build_artifacts_container_x86_64_version_latest }} | |
| platforms: linux/amd64 | |
| runner: linux-amd64-cpu4 | |
| push: ${{ github.repository == 'NVIDIA/infra-controller' || needs.prepare.outputs.publish_images == 'true' }} | |
| load: true | |
| tag_latest: ${{ github.ref == 'refs/heads/main' }} | |
| secrets: inherit | |
| build-artifacts-container-aarch64: | |
| if: needs.prepare.outputs.build_artifacts_container_aarch64_run == 'true' | |
| needs: | |
| - prepare | |
| uses: ./.github/workflows/docker-build.yml | |
| with: | |
| source_registry_host: ${{ needs.prepare.outputs.source_registry_host }} | |
| dockerfile_path: dev/docker/Dockerfile.build-artifacts-container-aarch64 | |
| image_name: ${{ needs.prepare.outputs.image_registry }}/build-artifacts-container-aarch64 | |
| image_tag: ${{ needs.prepare.outputs.build_artifacts_container_aarch64_version }} | |
| additional_tags: ${{ needs.prepare.outputs.build_artifacts_container_aarch64_version_latest }} | |
| platforms: linux/arm64 | |
| runner: linux-arm64-cpu4 | |
| push: ${{ github.repository == 'NVIDIA/infra-controller' || needs.prepare.outputs.publish_images == 'true' }} | |
| load: false | |
| tag_latest: ${{ github.ref == 'refs/heads/main' }} | |
| secrets: inherit | |
| # ============================================================================ | |
| # BUILD STAGE - Release Container | |
| # ============================================================================ | |
| # Deliberately does NOT depend on `lint-police` or | |
| # `check-rest-core-proto-sync`. The container build does not consume | |
| # anything either job produces, and gating on them serialized `~11m` of | |
| # linting (on proto-touching changes, `~5m` of proto-sync checking plus | |
| # queueing) in front of a `~29m` build. Their failures still block the | |
| # merge independently via `core-ci-pass`, so the gate is preserved while | |
| # the jobs run in parallel. | |
| build-release-container-x86_64: | |
| if: >- | |
| ${{ | |
| !cancelled() | |
| && github.event_name != 'schedule' | |
| && needs.prepare.result == 'success' | |
| && contains('success,skipped', needs.build-container-x86_64.result) | |
| && contains('success,skipped', needs.build-runtime-container-x86_64.result) | |
| }} | |
| needs: | |
| - prepare | |
| - build-container-x86_64 | |
| - build-runtime-container-x86_64 | |
| uses: ./.github/workflows/docker-build.yml | |
| with: | |
| source_registry_host: ${{ needs.prepare.outputs.source_registry_host }} | |
| target_prebuild_login: ${{ needs.prepare.outputs.bases_rebuilt == 'true' && needs.prepare.outputs.publish_images == 'true' }} | |
| dockerfile_path: dev/docker/Dockerfile.release-container-x86_64 | |
| context_path: . | |
| # No layer cache: the Dockerfile does `COPY . ./` before the compile, so | |
| # any new commit invalidates the builder layer and the expensive cargo | |
| # step can never be restored. Exporting it anyway measured 7m32s on a main | |
| # run and bought two cached layers, while its multi-GB blobs were the bulk | |
| # of what filled up the Actions quota and evicted the sccache | |
| # entries. | |
| cache: false | |
| build_args: ${{ needs.prepare.outputs.release_build_args }} | |
| image_name: ${{ needs.prepare.outputs.image_registry }}/nvmetal-carbide | |
| image_tag: ${{ needs.prepare.outputs.version }} | |
| additional_tags: ${{ needs.prepare.outputs.image_registry }}/nvmetal-carbide:${{ needs.prepare.outputs.major_minor_version }}-latest | |
| platforms: linux/amd64 | |
| runner: linux-amd64-cpu16 | |
| push: ${{ needs.prepare.outputs.publish_images == 'true' }} | |
| load: true | |
| scan: true | |
| tag_latest: false | |
| timeout_minutes: 120 | |
| secrets: inherit | |
| build-release-container-aarch64: | |
| if: >- | |
| ${{ | |
| !cancelled() | |
| && github.event_name != 'schedule' | |
| && needs.prepare.result == 'success' | |
| && contains('success,skipped', needs.build-container-aarch64.result) | |
| && contains('success,skipped', needs.build-runtime-container-aarch64.result) | |
| }} | |
| needs: | |
| - prepare | |
| - build-container-aarch64 | |
| - build-runtime-container-aarch64 | |
| uses: ./.github/workflows/docker-build.yml | |
| with: | |
| source_registry_host: ${{ needs.prepare.outputs.source_registry_host }} | |
| target_prebuild_login: ${{ needs.prepare.outputs.bases_rebuilt == 'true' && needs.prepare.outputs.publish_images == 'true' }} | |
| dockerfile_path: dev/docker/Dockerfile.release-container-aarch64 | |
| context_path: . | |
| # Off for the same reason as the x86_64 release container above, and this | |
| # is the worst of the four: 8m21s of cache export on a main run for two | |
| # cached layers. | |
| cache: false | |
| build_args: ${{ needs.prepare.outputs.release_build_args_aarch64 }} | |
| image_name: ${{ needs.prepare.outputs.image_registry }}/nvmetal-carbide-aarch64 | |
| image_tag: ${{ needs.prepare.outputs.version }} | |
| additional_tags: ${{ needs.prepare.outputs.image_registry }}/nvmetal-carbide-aarch64:${{ needs.prepare.outputs.major_minor_version }}-latest | |
| platforms: linux/arm64 | |
| runner: linux-arm64-cpu16 | |
| push: ${{ needs.prepare.outputs.publish_images == 'true' }} | |
| load: false | |
| tag_latest: false | |
| secrets: inherit | |
| # Combine the per-arch nvmetal-carbide images into a single multi-arch manifest | |
| # at the bare tag, so the carbide control plane can be scheduled on both amd64 | |
| # and arm64 nodes. The per-arch images are still pushed individually | |
| # (nvmetal-carbide = amd64, nvmetal-carbide-aarch64 = arm64). Sources are | |
| # referenced by digest so re-runs are idempotent. Push refs only. | |
| merge-manifests-nvmetal-carbide: | |
| needs: | |
| - prepare | |
| - build-release-container-x86_64 | |
| - build-release-container-aarch64 | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.outputs.publish_images == 'true' && needs.build-release-container-x86_64.result == 'success' && needs.build-release-container-aarch64.result == 'success' }} | |
| runs-on: linux-amd64-cpu4 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to NVCR | |
| uses: ./.github/actions/docker-auth | |
| with: | |
| registry: ${{ needs.prepare.outputs.registry_host }} | |
| username: ${{ secrets.NICO_TARGET_REGISTRY_USERNAME || secrets.NVCR_USERNAME }} | |
| token: ${{ secrets.NICO_TARGET_REGISTRY_TOKEN || secrets.NVCR_TOKEN }} | |
| - name: Create and push multi-arch manifest | |
| uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 | |
| with: | |
| max_attempts: 3 | |
| timeout_minutes: 10 | |
| retry_wait_seconds: 20 | |
| command: | | |
| docker buildx imagetools create -t \ | |
| ${{ needs.prepare.outputs.image_registry }}/nvmetal-carbide:${{ needs.prepare.outputs.version }} \ | |
| ${{ needs.prepare.outputs.image_registry }}/nvmetal-carbide@${{ needs.build-release-container-x86_64.outputs.image_digest }} \ | |
| ${{ needs.prepare.outputs.image_registry }}/nvmetal-carbide-aarch64@${{ needs.build-release-container-aarch64.outputs.image_digest }} | |
| docker buildx imagetools create -t \ | |
| ${{ needs.prepare.outputs.image_registry }}/nvmetal-carbide:${{ needs.prepare.outputs.major_minor_version }}-latest \ | |
| ${{ needs.prepare.outputs.image_registry }}/nvmetal-carbide@${{ needs.build-release-container-x86_64.outputs.image_digest }} \ | |
| ${{ needs.prepare.outputs.image_registry }}/nvmetal-carbide-aarch64@${{ needs.build-release-container-aarch64.outputs.image_digest }} | |
| build-machine-a-tron: | |
| if: >- | |
| ${{ | |
| !cancelled() | |
| && github.event_name != 'schedule' | |
| && needs.prepare.result == 'success' | |
| && needs.prepare.outputs.source_files_changed == 'true' | |
| }} | |
| needs: | |
| - prepare | |
| uses: ./.github/workflows/docker-build.yml | |
| with: | |
| dockerfile_path: crates/machine-a-tron/Dockerfile | |
| context_path: . | |
| # No layer cache, same reasoning as the release containers above: the | |
| # Dockerfile does `COPY . .` before the cargo build, so any new commit | |
| # invalidates the builder layer and nothing expensive can ever be | |
| # restored. `mode=max` still exported the whole builder stage on every | |
| # main commit, pushing multi-hundred-MB blobs into the Actions cache pool | |
| # that sccache depends on. | |
| cache: false | |
| build_args: >- | |
| ${{ format('{{"VERSION":"{0}","CI_COMMIT_SHORT_SHA":"{1}"}}', | |
| needs.prepare.outputs.version, | |
| needs.prepare.outputs.short_sha) }} | |
| image_name: ${{ needs.prepare.outputs.image_registry }}/machine-a-tron | |
| image_tag: ${{ needs.prepare.outputs.version }} | |
| additional_tags: ${{ needs.prepare.outputs.image_registry }}/machine-a-tron:${{ needs.prepare.outputs.major_minor_version }}-latest | |
| platforms: linux/amd64 | |
| runner: linux-amd64-cpu16 | |
| push: ${{ needs.prepare.outputs.publish_images == 'true' }} | |
| load: true | |
| scan: true | |
| tag_latest: false | |
| timeout_minutes: 60 | |
| secrets: inherit | |
| build-mat-k8s-controller: | |
| if: >- | |
| ${{ | |
| !cancelled() | |
| && github.event_name != 'schedule' | |
| && needs.prepare.result == 'success' | |
| && needs.prepare.outputs.source_files_changed == 'true' | |
| }} | |
| needs: | |
| - prepare | |
| uses: ./.github/workflows/docker-build.yml | |
| with: | |
| dockerfile_path: dev/k8s/machine-a-tron-controller/Dockerfile | |
| context_path: dev/k8s/machine-a-tron-controller | |
| image_name: ${{ needs.prepare.outputs.image_registry }}/mat-k8s-controller | |
| image_tag: ${{ needs.prepare.outputs.version }} | |
| additional_tags: ${{ needs.prepare.outputs.image_registry }}/mat-k8s-controller:${{ needs.prepare.outputs.major_minor_version }}-latest | |
| platforms: linux/amd64,linux/arm64 | |
| runner: linux-amd64-cpu4 | |
| push: ${{ needs.prepare.outputs.publish_images == 'true' }} | |
| load: false | |
| scan: true | |
| tag_latest: false | |
| timeout_minutes: 30 | |
| secrets: inherit | |
| # Like the release-container builds above, deliberately not gated on | |
| # `check-rest-core-proto-sync`: the tests do not read anything it produces, | |
| # and `core-ci-pass` still fails the merge when the protos are out of sync. | |
| test-release-container-services: | |
| if: >- | |
| ${{ | |
| !cancelled() | |
| && github.event_name != 'schedule' | |
| && needs.prepare.result == 'success' | |
| && contains('success,skipped', needs.build-container-x86_64.result) | |
| && contains('success,skipped', needs.build-runtime-container-x86_64.result) | |
| }} | |
| needs: | |
| - prepare | |
| - build-container-x86_64 | |
| - build-runtime-container-x86_64 | |
| runs-on: linux-amd64-cpu16 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Login to NVCR | |
| uses: ./.github/actions/docker-auth | |
| with: | |
| registry: ${{ needs.prepare.outputs.source_registry_host }} | |
| username: ${{ secrets.NICO_SOURCE_REGISTRY_USERNAME || secrets.NVCR_USERNAME }} | |
| token: ${{ secrets.NICO_SOURCE_REGISTRY_TOKEN || secrets.NVCR_TOKEN }} | |
| # base refs may point at the target registry after a rebuild; log in | |
| # there too when it is a different host (same-host uses one credential). | |
| - name: Login to target registry | |
| if: ${{ needs.prepare.outputs.publish_images == 'true' && needs.prepare.outputs.registry_host != needs.prepare.outputs.source_registry_host }} | |
| uses: ./.github/actions/docker-auth | |
| with: | |
| registry: ${{ needs.prepare.outputs.registry_host }} | |
| username: ${{ secrets.NICO_TARGET_REGISTRY_USERNAME || secrets.NVCR_USERNAME }} | |
| token: ${{ secrets.NICO_TARGET_REGISTRY_TOKEN || secrets.NVCR_TOKEN }} | |
| # The build container mounts this directory as its sccache store, so a | |
| # miss here means recompiling the whole workspace before the first test | |
| # runs. restore-keys let a lockfile bump start from the previous entry | |
| # rather than from an empty cache. | |
| - name: Restore sccache | |
| id: sccache | |
| uses: actions/cache/restore@v4 | |
| with: | |
| path: .sccache | |
| key: sccache-test-release-x86_64-${{ hashFiles('Cargo.lock') }} | |
| restore-keys: | | |
| sccache-test-release-x86_64- | |
| - name: Run release-container service tests | |
| env: | |
| BUILD_CONTAINER: ${{ needs.prepare.outputs.build_container_x86_64_ref }} | |
| CI_COMMIT_SHORT_SHA: ${{ needs.prepare.outputs.short_sha }} | |
| VERSION: ${{ needs.prepare.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p "${{ github.workspace }}/.sccache" | |
| docker pull "${BUILD_CONTAINER}" | |
| docker run --rm \ | |
| -v "${{ github.workspace }}:/carbide" \ | |
| -v "${{ github.workspace }}/.sccache:/sccache" \ | |
| -w /carbide \ | |
| -e CARGO_HOME=/carbide/cargo \ | |
| -e CARGO_INCREMENTAL=0 \ | |
| -e CARGO_MAKE_SKIP_CODECOV=true \ | |
| -e DATABASE_URL='postgresql://root@%2Fvar%2Frun%2Fpostgresql/root' \ | |
| -e SCCACHE_DIR=/sccache \ | |
| -e SCCACHE_CACHE_SIZE=5G \ | |
| -e RUSTC_WRAPPER=sccache \ | |
| -e CI_COMMIT_SHORT_SHA="${CI_COMMIT_SHORT_SHA}" \ | |
| -e VERSION="${VERSION}" \ | |
| -e CONTAINER_REPO_ROOT=/carbide \ | |
| -e HOST_OWNER="$(id -u):$(id -g)" \ | |
| "${BUILD_CONTAINER}" \ | |
| bash -c ' | |
| set -euo pipefail | |
| # Everything in here runs as root, so sccache leaves /sccache | |
| # owned by root and the post-job archive step cannot read it back. | |
| # Hand the directory to the runner user on the way out, including | |
| # when the tests fail. | |
| trap "chown -R ${HOST_OWNER} /sccache 2>/dev/null || true" EXIT | |
| git config --global --add safe.directory /carbide | |
| /etc/init.d/postgresql start | |
| sudo -u postgres psql -c "ALTER USER root WITH SUPERUSER;" | |
| createdb root | |
| cargo make test-release-container-services | |
| sccache --show-stats || true | |
| echo "sccache directory holds $(du -sh /sccache | cut -f1)" | |
| ' | |
| # Worth saving even when the tests failed: the entry holds the dependency | |
| # compilation, which is no less valid after a red run and is most of the | |
| # eight minutes spent before the first test starts. Cargo.lock keys it, so | |
| # a run that changes no dependencies hits the primary key and skips the | |
| # upload rather than rewriting an equivalent entry. | |
| # | |
| # Only main saves. A pull request whose Cargo.lock differs from main's | |
| # matches the restore-keys prefix rather than the exact key, so cache-hit | |
| # is false and the save would otherwise fire, storing a large ref-scoped | |
| # copy nothing else can read. The main-only condition blocks that, and the | |
| # prefix match still leaves the pull request warm. | |
| - name: Save sccache | |
| if: ${{ !cancelled() && github.ref == 'refs/heads/main' && steps.sccache.outputs.cache-hit != 'true' }} | |
| uses: actions/cache/save@v4 | |
| with: | |
| path: .sccache | |
| key: ${{ steps.sccache.outputs.cache-primary-key }} | |
| - name: Final repository clean check | |
| run: bash scripts/check-repo-clean.sh "release-container service tests" | |
| # ============================================================================ | |
| # BUILD STAGE - Forge CLI (Multi-arch) | |
| # ============================================================================ | |
| build-forge-cli-x86_64: | |
| needs: | |
| - prepare | |
| - build-artifacts-container-x86_64 | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' }} | |
| uses: ./.github/workflows/docker-build.yml | |
| with: | |
| source_registry_host: ${{ needs.prepare.outputs.source_registry_host }} | |
| target_prebuild_login: ${{ needs.prepare.outputs.bases_rebuilt == 'true' && needs.prepare.outputs.publish_images == 'true' }} | |
| dockerfile_path: dev/docker/Dockerfile.release-forge-cli | |
| # Off for the same reason as the release containers above: 2m00s of cache | |
| # export on a main run for a single cached layer. | |
| cache: false | |
| image_name: ${{ needs.prepare.outputs.image_registry }}/forge-admin-cli-x86_64 | |
| image_tag: ${{ needs.prepare.outputs.version }} | |
| additional_tags: ${{ needs.prepare.outputs.image_registry }}/forge-admin-cli-x86_64:${{ needs.prepare.outputs.major_minor_version }}-latest | |
| build_args: '{"CI_COMMIT_SHORT_SHA":"${{ needs.prepare.outputs.short_sha }}","CONTAINER_BUILD":"${{ needs.prepare.outputs.build_artifacts_container_x86_64_ref }}"}' | |
| platforms: linux/amd64 | |
| runner: linux-amd64-cpu4 | |
| push: ${{ needs.prepare.outputs.publish_images == 'true' }} | |
| load: true | |
| scan: true | |
| secrets: inherit | |
| build-forge-cli-aarch64: | |
| needs: | |
| - prepare | |
| - build-artifacts-container-aarch64 | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' }} | |
| uses: ./.github/workflows/docker-build.yml | |
| with: | |
| source_registry_host: ${{ needs.prepare.outputs.source_registry_host }} | |
| target_prebuild_login: ${{ needs.prepare.outputs.bases_rebuilt == 'true' && needs.prepare.outputs.publish_images == 'true' }} | |
| dockerfile_path: dev/docker/Dockerfile.release-forge-cli | |
| # Off for the same reason as the release containers above: 3m27s of cache | |
| # export on a main run for two cached layers. | |
| cache: false | |
| image_name: ${{ needs.prepare.outputs.image_registry }}/forge-admin-cli-aarch64 | |
| image_tag: ${{ needs.prepare.outputs.version }} | |
| additional_tags: ${{ needs.prepare.outputs.image_registry }}/forge-admin-cli-aarch64:${{ needs.prepare.outputs.major_minor_version }}-latest | |
| build_args: '{"CI_COMMIT_SHORT_SHA":"${{ needs.prepare.outputs.short_sha }}","CONTAINER_BUILD":"${{ needs.prepare.outputs.build_artifacts_container_aarch64_ref }}"}' | |
| platforms: linux/arm64 | |
| runner: linux-arm64-cpu4 | |
| push: ${{ needs.prepare.outputs.publish_images == 'true' }} | |
| load: true | |
| secrets: inherit | |
| merge-manifests-forge-cli: | |
| needs: | |
| - prepare | |
| - build-forge-cli-x86_64 | |
| - build-forge-cli-aarch64 | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.outputs.publish_images == 'true' && needs.build-forge-cli-x86_64.result == 'success' && needs.build-forge-cli-aarch64.result == 'success' }} | |
| runs-on: linux-amd64-cpu4 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to NVCR | |
| uses: ./.github/actions/docker-auth | |
| with: | |
| registry: ${{ needs.prepare.outputs.registry_host }} | |
| username: ${{ secrets.NICO_TARGET_REGISTRY_USERNAME || secrets.NVCR_USERNAME }} | |
| token: ${{ secrets.NICO_TARGET_REGISTRY_TOKEN || secrets.NVCR_TOKEN }} | |
| - name: Create and push multi-arch manifest | |
| uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 | |
| with: | |
| max_attempts: 3 | |
| timeout_minutes: 10 | |
| retry_wait_seconds: 20 | |
| command: | | |
| docker buildx imagetools create -t \ | |
| ${{ needs.prepare.outputs.image_registry }}/forge-admin-cli:${{ needs.prepare.outputs.version }} \ | |
| ${{ needs.prepare.outputs.image_registry }}/forge-admin-cli-x86_64:${{ needs.prepare.outputs.version }} \ | |
| ${{ needs.prepare.outputs.image_registry }}/forge-admin-cli-aarch64:${{ needs.prepare.outputs.version }} | |
| docker buildx imagetools create -t \ | |
| ${{ needs.prepare.outputs.image_registry }}/forge-admin-cli:latest \ | |
| ${{ needs.prepare.outputs.image_registry }}/forge-admin-cli-x86_64:${{ needs.prepare.outputs.version }} \ | |
| ${{ needs.prepare.outputs.image_registry }}/forge-admin-cli-aarch64:${{ needs.prepare.outputs.version }} | |
| docker buildx imagetools create -t \ | |
| ${{ needs.prepare.outputs.image_registry }}/forge-admin-cli:${{ needs.prepare.outputs.major_minor_version }}-latest \ | |
| ${{ needs.prepare.outputs.image_registry }}/forge-admin-cli-x86_64:${{ needs.prepare.outputs.version }} \ | |
| ${{ needs.prepare.outputs.image_registry }}/forge-admin-cli-aarch64:${{ needs.prepare.outputs.version }} | |
| # ============================================================================ | |
| # BUILD STAGE - Boot Artifacts | |
| # ============================================================================ | |
| build-boot-artifacts-x86: | |
| needs: | |
| - prepare | |
| - build-artifacts-container-x86_64 | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' && contains('success,skipped', needs.build-artifacts-container-x86_64.result) }} | |
| uses: ./.github/workflows/build-boot-artifacts.yml | |
| with: | |
| target_login: ${{ needs.prepare.outputs.publish_images == 'true' }} | |
| arch: x86_64 | |
| build_type: boot | |
| cargo_make_task: build-boot-artifacts-x86-host-ci | |
| build_container: ${{ needs.prepare.outputs.build_artifacts_container_x86_64_ref }} | |
| runner: linux-amd64-cpu4 | |
| version: ${{ needs.prepare.outputs.version }} | |
| use_container: true | |
| inject_extras: true | |
| extras_container: ${{ needs.prepare.outputs.extras_container_ref }} | |
| secrets: inherit | |
| build-boot-artifacts-bfb: | |
| needs: | |
| - prepare | |
| - build-artifacts-container-aarch64 | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' && contains('success,skipped', needs.build-artifacts-container-aarch64.result) }} | |
| uses: ./.github/workflows/build-boot-artifacts.yml | |
| with: | |
| target_login: ${{ needs.prepare.outputs.publish_images == 'true' }} | |
| arch: aarch64 | |
| build_type: boot | |
| cargo_make_task: build-boot-artifacts-bfb-ci | |
| build_container: ${{ needs.prepare.outputs.build_artifacts_container_aarch64_ref }} | |
| runner: linux-arm64-cpu16 | |
| version: ${{ needs.prepare.outputs.version }} | |
| use_container: true | |
| inject_extras: true | |
| extras_container: ${{ needs.prepare.outputs.extras_container_ref }} | |
| secrets: inherit | |
| # The aarch64 ephemeral (mkosi) build consumes exactly one file from the | |
| # boot stage: the packaged forge-scout `.deb` (pxe `copy-forge-scout-aarch64`). | |
| # Waiting on the full bfb job serialized `~18m` of BFB assembly in front of | |
| # a `~14m` mkosi build for that one file, which was the longest chain in | |
| # the run. This job packages the deb on its own so the mkosi build starts | |
| # as soon as it exists, while bfb keeps feeding the release-artifacts | |
| # carrier in parallel. | |
| # bfb still compiles scout itself: its apt repo ships the deb inside the | |
| # boot image. | |
| build-package-scout-aarch64: | |
| needs: | |
| - prepare | |
| - build-artifacts-container-aarch64 | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' && contains('success,skipped', needs.build-artifacts-container-aarch64.result) }} | |
| uses: ./.github/workflows/build-boot-artifacts.yml | |
| with: | |
| target_login: ${{ needs.prepare.outputs.publish_images == 'true' }} | |
| arch: aarch64 | |
| build_type: package | |
| cargo_make_task: package-scout-aarch64-ci | |
| build_container: ${{ needs.prepare.outputs.build_artifacts_container_aarch64_ref }} | |
| runner: linux-arm64-cpu16 | |
| version: ${{ needs.prepare.outputs.version }} | |
| use_container: true | |
| # Not for injection (`inject_extras` stays false, so the extras are | |
| # never pulled or extracted). The extras host is what the workflow logs | |
| # into for read-only pulls, and prepare pins this ref to the source | |
| # registry. On mirrors with a non-NVCR source registry, omitting it | |
| # logs into the `nvcr.io` fallback while the build container lives on | |
| # the source host, and non-publishing runs never do the target login | |
| # that could paper over that. | |
| extras_container: ${{ needs.prepare.outputs.extras_container_ref }} | |
| secrets: inherit | |
| # ============================================================================ | |
| # BUILD STAGE - Ephemeral Images | |
| # ============================================================================ | |
| # These builds create bootable ephemeral images (scout.efi, qcow-imager.efi) | |
| # using mkosi. They run on standard Ubuntu runners with mkosi installed. | |
| build-boot-artifacts-ephemeral-image-x86-host: | |
| needs: | |
| - prepare | |
| - build-boot-artifacts-x86 | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' && needs.build-boot-artifacts-x86.result == 'success' }} | |
| uses: ./.github/workflows/build-boot-artifacts.yml | |
| with: | |
| target_login: ${{ needs.prepare.outputs.publish_images == 'true' }} | |
| arch: x86_64 | |
| build_type: ephemeral | |
| cargo_make_task: create-ephemeral-image-x86-host-ci | |
| version: ${{ needs.prepare.outputs.version }} | |
| runner: linux-amd64-cpu8 | |
| use_container: false | |
| sa_enablement: true | |
| inject_extras: true | |
| extras_container: ${{ needs.prepare.outputs.extras_container_ref }} | |
| secrets: inherit | |
| build-boot-artifacts-ephemeral-image-arm-host: | |
| needs: | |
| - prepare | |
| - build-package-scout-aarch64 | |
| # Depends on the scout package, not the full bfb build: the forge-scout | |
| # `.deb` is the only boot-stage input the mkosi build reads. The | |
| # release-artifacts carrier downstream still requires bfb to have | |
| # succeeded, so a bfb failure fails the run exactly as before. | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' && needs.build-package-scout-aarch64.result == 'success' }} | |
| uses: ./.github/workflows/build-boot-artifacts.yml | |
| with: | |
| target_login: ${{ needs.prepare.outputs.publish_images == 'true' }} | |
| arch: aarch64 | |
| build_type: ephemeral | |
| cargo_make_task: create-ephemeral-image-arm-host-ci | |
| version: ${{ needs.prepare.outputs.version }} | |
| runner: linux-arm64-cpu16 | |
| use_container: false | |
| sa_enablement: true | |
| inject_extras: true | |
| extras_container: ${{ needs.prepare.outputs.extras_container_ref }} | |
| # Fetch just the deb archive instead of the `5.4 GB` boot artifact, a | |
| # measured `2m06s`-`2m36s` download for one consumed file. The boot | |
| # blobs the carrier image needs still travel in bfb's own artifact. | |
| prereq_artifact: package-artifacts-aarch64-${{ github.run_id }} | |
| secrets: inherit | |
| # ============================================================================ | |
| # BUILD STAGE - Release Artifacts | |
| # ============================================================================ | |
| build-release-artifacts-x86-host: | |
| needs: | |
| - prepare | |
| - build-boot-artifacts-x86 | |
| - build-boot-artifacts-ephemeral-image-x86-host | |
| # Run only if both boot and ephemeral jobs succeeded (we need those artifacts); skip if failed/cancelled | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' && needs.build-boot-artifacts-x86.result == 'success' && needs.build-boot-artifacts-ephemeral-image-x86-host.result == 'success' }} | |
| uses: ./.github/workflows/docker-build.yml | |
| with: | |
| source_registry_host: ${{ needs.prepare.outputs.source_registry_host }} | |
| target_prebuild_login: ${{ needs.prepare.outputs.bases_rebuilt == 'true' && needs.prepare.outputs.publish_images == 'true' }} | |
| dockerfile_path: dev/docker/Dockerfile.release-artifacts-x86_64 | |
| # No RUN steps, so there are no expensive layers worth reusing. The | |
| # multi-GB blob COPY layer would also take a large bite out of the per-repo | |
| # Actions cache quota shared with the layer and sccache caches the rest of | |
| # the pipeline depends on. | |
| cache: false | |
| image_name: ${{ needs.prepare.outputs.image_registry }}/boot-artifacts-x86_64 | |
| image_tag: ${{ needs.prepare.outputs.version }} | |
| additional_tags: ${{ needs.prepare.outputs.image_registry }}/boot-artifacts-x86_64:${{ needs.prepare.outputs.major_minor_version }}-latest | |
| # Multi-arch on push so this x86_64 boot-artifacts carrier can run as an | |
| # init container on both amd64 and arm64 control-plane nodes; the payload | |
| # it ships is unchanged (x86_64 boot blobs). The Dockerfile has no RUN | |
| # steps (FROM alpine + COPY only), so no QEMU is needed. Single-arch on PR | |
| # so the image can load to the daemon. | |
| platforms: ${{ needs.prepare.outputs.publish_images == 'true' && 'linux/amd64,linux/arm64' || 'linux/amd64' }} | |
| runner: linux-amd64-cpu4 | |
| push: ${{ needs.prepare.outputs.publish_images == 'true' }} | |
| load: true | |
| scan: true | |
| download_artifacts: true # Download boot and ephemeral artifacts for packaging | |
| artifact_pattern: '*-x86_64-${{ github.run_id }}' | |
| build_args: | | |
| { | |
| "VERSION": "${{ needs.prepare.outputs.version }}", | |
| "CI_COMMIT_SHORT_SHA": "${{ needs.prepare.outputs.short_sha }}", | |
| "CONTAINER_RUNTIME_X86_64": "alpine:latest" | |
| } | |
| secrets: inherit | |
| build-release-artifacts-arm-host: | |
| needs: | |
| - prepare | |
| - build-boot-artifacts-bfb | |
| - build-boot-artifacts-ephemeral-image-arm-host | |
| # Run only if both boot and ephemeral jobs succeeded (we need those artifacts); skip if failed/cancelled | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' && needs.build-boot-artifacts-bfb.result == 'success' && needs.build-boot-artifacts-ephemeral-image-arm-host.result == 'success' }} | |
| uses: ./.github/workflows/docker-build.yml | |
| with: | |
| source_registry_host: ${{ needs.prepare.outputs.source_registry_host }} | |
| target_prebuild_login: ${{ needs.prepare.outputs.bases_rebuilt == 'true' && needs.prepare.outputs.publish_images == 'true' }} | |
| dockerfile_path: dev/docker/Dockerfile.release-artifacts-aarch64 | |
| # No RUN steps, so there are no expensive layers worth reusing. The | |
| # multi-GB blob COPY layer would also take a large bite out of the per-repo | |
| # Actions cache quota shared with the layer and sccache caches the rest of | |
| # the pipeline depends on. | |
| cache: false | |
| image_name: ${{ needs.prepare.outputs.image_registry }}/boot-artifacts-aarch64 | |
| image_tag: ${{ needs.prepare.outputs.version }} | |
| additional_tags: ${{ needs.prepare.outputs.image_registry }}/boot-artifacts-aarch64:${{ needs.prepare.outputs.major_minor_version }}-latest | |
| # Container arch is independent of the payload: this carrier always ships | |
| # the aarch64 boot blobs as files. Build it multi-arch on push so it can | |
| # run as an init container on both amd64 and arm64 control-plane nodes. The | |
| # Dockerfile has no RUN steps (FROM alpine + COPY only), so no QEMU is | |
| # needed. Single-arch on PR so the image can load to the daemon. | |
| platforms: ${{ needs.prepare.outputs.publish_images == 'true' && 'linux/amd64,linux/arm64' || 'linux/amd64' }} | |
| runner: linux-amd64-cpu4 | |
| push: ${{ needs.prepare.outputs.publish_images == 'true' }} | |
| load: true | |
| scan: true | |
| download_artifacts: true # Download boot and ephemeral artifacts for packaging | |
| artifact_pattern: '*-aarch64-${{ github.run_id }}' | |
| build_args: | | |
| { | |
| "VERSION": "${{ needs.prepare.outputs.version }}", | |
| "CI_COMMIT_SHORT_SHA": "${{ needs.prepare.outputs.short_sha }}", | |
| "CONTAINER_RUNTIME_AARCH64": "alpine:latest" | |
| } | |
| secrets: inherit | |
| # ============================================================================ | |
| # SECURITY STAGE | |
| # ============================================================================ | |
| security-secret-scan: | |
| name: Secret Scan with TruffleHog | |
| needs: prepare | |
| runs-on: linux-amd64-cpu4 | |
| timeout-minutes: 30 | |
| # PR comments are disabled below and this scan does not upload SARIF, so | |
| # TruffleHog inherits the read-only workflow permission. | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| fetch-depth: 0 # Full history for secret scanning | |
| # Pull requests and tags both need a verified base and head. Resolve those | |
| # ranges in one place; ordinary branch pushes keep using the push event. | |
| - name: Compute TruffleHog scan range | |
| id: scan-range | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| if [[ "$GITHUB_REF" == refs/heads/pull-request/* || "$GITHUB_REF" == refs/tags/* ]]; then | |
| bash .github/ci/resolve-secret-scan-range.sh >> "$GITHUB_OUTPUT" | |
| else | |
| BEFORE="${{ github.event.before }}" | |
| echo "base=$BEFORE" >> "$GITHUB_OUTPUT" | |
| echo "head=$GITHUB_SHA" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Run TruffleHog Scan | |
| uses: dsx-ai-factory/dsx-github-actions/.github/actions/trufflehog-scan@9a9ce3a7770a8b53d2726afa920be3276bc3ddd7 | |
| with: | |
| base: ${{ steps.scan-range.outputs.base }} | |
| head: ${{ steps.scan-range.outputs.head }} | |
| extra-args: '--results=verified,unknown --only-verified' | |
| post-pr-comment: 'false' | |
| fail-on-findings: 'true' | |
| security-codeql-scan: | |
| name: CodeQL Security Analysis | |
| needs: prepare | |
| runs-on: linux-amd64-cpu4 | |
| # The scan runs in about two minutes. It was inheriting the 360-minute | |
| # default, so the one run in twenty that wedges holds a runner for six hours | |
| # and leaves the workflow open long after every other job has finished. | |
| timeout-minutes: 20 | |
| # `upload-sarif` and `post-pr-comment` are both false below, so CodeQL only | |
| # needs to read the workflow metadata and source it analyzes. | |
| permissions: | |
| actions: read | |
| contents: read | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Run CodeQL Scan | |
| # Bounded separately from the job so a hang fails this step by name and | |
| # still lets the post steps run, rather than the runner being killed | |
| # mid-flight with no attribution. | |
| timeout-minutes: 15 | |
| uses: dsx-ai-factory/dsx-github-actions/.github/actions/codeql-scan@20dc10dda4fa9f8f0380a47cd9a7800d3da3dcf3 | |
| with: | |
| languages: 'rust' | |
| build-mode: 'none' | |
| category: '/language:rust' | |
| upload-sarif: 'false' # Disable upload until GHAS is enabled, such as the repo be public | |
| post-pr-comment: 'false' | |
| fail-on-findings: 'true' # Enforce quality gate | |
| fail-on-severity: 'error' # Only fail on critical/high severity issues | |
| # ============================================================================ | |
| # BUILD STAGE - Machine Validation | |
| # ============================================================================ | |
| build-release-machine-validation-runner: | |
| needs: | |
| - prepare | |
| - build-runtime-container-x86_64 | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' && contains('success,skipped', needs.build-runtime-container-x86_64.result) }} | |
| uses: ./.github/workflows/docker-build.yml | |
| with: | |
| source_registry_host: ${{ needs.prepare.outputs.source_registry_host }} | |
| target_prebuild_login: ${{ needs.prepare.outputs.bases_rebuilt == 'true' && needs.prepare.outputs.publish_images == 'true' }} | |
| dockerfile_path: dev/docker/Dockerfile.machine-validation-runner | |
| image_name: ${{ needs.prepare.outputs.image_registry }}/machine-validation-runner | |
| image_tag: ${{ needs.prepare.outputs.version }} | |
| additional_tags: ${{ needs.prepare.outputs.image_registry }}/machine-validation-runner:${{ needs.prepare.outputs.major_minor_version }}-latest | |
| build_args: '{"CI_COMMIT_SHORT_SHA":"${{ needs.prepare.outputs.short_sha }}","CONTAINER_RUNTIME_X86_64":"${{ needs.prepare.outputs.runtime_container_x86_64_ref }}"}' | |
| platforms: linux/amd64 | |
| runner: linux-amd64-cpu4 | |
| push: ${{ needs.prepare.outputs.publish_images == 'true' }} | |
| load: true | |
| scan: true | |
| secrets: inherit | |
| build-release-machine-validation-artifacts-x86-host: | |
| needs: | |
| - prepare | |
| - build-release-machine-validation-runner | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' && needs.build-release-machine-validation-runner.result == 'success' }} | |
| uses: ./.github/workflows/docker-build.yml | |
| with: | |
| source_registry_host: ${{ needs.prepare.outputs.source_registry_host }} | |
| target_prebuild_login: ${{ needs.prepare.outputs.bases_rebuilt == 'true' && needs.prepare.outputs.publish_images == 'true' }} | |
| dockerfile_path: dev/docker/Dockerfile.machine-validation-config | |
| image_name: ${{ needs.prepare.outputs.image_registry }}/machine_validation | |
| image_tag: ${{ needs.prepare.outputs.version }} | |
| additional_tags: ${{ needs.prepare.outputs.image_registry }}/machine_validation:${{ needs.prepare.outputs.major_minor_version }}-latest | |
| build_args: '{"CI_COMMIT_SHORT_SHA":"${{ needs.prepare.outputs.short_sha }}","CONTAINER_RUNTIME_X86_64":"${{ needs.prepare.outputs.runtime_container_x86_64_ref }}"}' | |
| platforms: linux/amd64 | |
| runner: linux-amd64-cpu4 | |
| push: ${{ needs.prepare.outputs.publish_images == 'true' }} | |
| load: true | |
| scan: true | |
| secrets: inherit | |
| # arm64 container build of the machine-validation config carrier, so it can run | |
| # as an init container on arm64 control-plane nodes. FROM the aarch64 runtime | |
| # base; pushed as machine_validation-aarch64 and merged into the bare | |
| # machine_validation tag below. (The bundled validation runner payload staying | |
| # arm64-capable is tracked separately — this only makes the carrier runnable on | |
| # arm64 nodes.) | |
| build-release-machine-validation-artifacts-arm-host: | |
| needs: | |
| - prepare | |
| - build-runtime-container-aarch64 | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' && contains('success,skipped', needs.build-runtime-container-aarch64.result) }} | |
| uses: ./.github/workflows/docker-build.yml | |
| with: | |
| source_registry_host: ${{ needs.prepare.outputs.source_registry_host }} | |
| target_prebuild_login: ${{ needs.prepare.outputs.bases_rebuilt == 'true' && needs.prepare.outputs.publish_images == 'true' }} | |
| dockerfile_path: dev/docker/Dockerfile.machine-validation-config-aarch64 | |
| image_name: ${{ needs.prepare.outputs.image_registry }}/machine_validation-aarch64 | |
| image_tag: ${{ needs.prepare.outputs.version }} | |
| additional_tags: ${{ needs.prepare.outputs.image_registry }}/machine_validation-aarch64:${{ needs.prepare.outputs.major_minor_version }}-latest | |
| build_args: '{"CI_COMMIT_SHORT_SHA":"${{ needs.prepare.outputs.short_sha }}","CONTAINER_RUNTIME_AARCH64":"${{ needs.prepare.outputs.runtime_container_aarch64_ref }}"}' | |
| platforms: linux/arm64 | |
| runner: linux-arm64-cpu4 | |
| push: ${{ needs.prepare.outputs.publish_images == 'true' }} | |
| load: true | |
| scan: true | |
| secrets: inherit | |
| # Combine machine_validation (amd64) and machine_validation-aarch64 into a | |
| # single multi-arch manifest at the bare machine_validation tag. Sources are | |
| # referenced by digest so re-runs are idempotent. Push refs only. | |
| merge-manifests-machine-validation: | |
| needs: | |
| - prepare | |
| - build-release-machine-validation-artifacts-x86-host | |
| - build-release-machine-validation-artifacts-arm-host | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.outputs.publish_images == 'true' && needs.build-release-machine-validation-artifacts-x86-host.result == 'success' && needs.build-release-machine-validation-artifacts-arm-host.result == 'success' }} | |
| runs-on: linux-amd64-cpu4 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to NVCR | |
| uses: ./.github/actions/docker-auth | |
| with: | |
| registry: ${{ needs.prepare.outputs.registry_host }} | |
| username: ${{ secrets.NICO_TARGET_REGISTRY_USERNAME || secrets.NVCR_USERNAME }} | |
| token: ${{ secrets.NICO_TARGET_REGISTRY_TOKEN || secrets.NVCR_TOKEN }} | |
| - name: Create and push multi-arch manifest | |
| uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 | |
| with: | |
| max_attempts: 3 | |
| timeout_minutes: 10 | |
| retry_wait_seconds: 20 | |
| command: | | |
| docker buildx imagetools create -t \ | |
| ${{ needs.prepare.outputs.image_registry }}/machine_validation:${{ needs.prepare.outputs.version }} \ | |
| ${{ needs.prepare.outputs.image_registry }}/machine_validation@${{ needs.build-release-machine-validation-artifacts-x86-host.outputs.image_digest }} \ | |
| ${{ needs.prepare.outputs.image_registry }}/machine_validation-aarch64@${{ needs.build-release-machine-validation-artifacts-arm-host.outputs.image_digest }} | |
| docker buildx imagetools create -t \ | |
| ${{ needs.prepare.outputs.image_registry }}/machine_validation:${{ needs.prepare.outputs.major_minor_version }}-latest \ | |
| ${{ needs.prepare.outputs.image_registry }}/machine_validation@${{ needs.build-release-machine-validation-artifacts-x86-host.outputs.image_digest }} \ | |
| ${{ needs.prepare.outputs.image_registry }}/machine_validation-aarch64@${{ needs.build-release-machine-validation-artifacts-arm-host.outputs.image_digest }} | |
| proto-police: | |
| needs: | |
| - prepare | |
| if: ${{ needs.prepare.outputs.proto_files_changed == 'true' && contains(github.ref, 'pull-request/') }} | |
| runs-on: linux-amd64-cpu4 | |
| container: | |
| image: yoheimuta/protolint | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Run Protolint | |
| run: protolint lint -config_path=.protolint.yaml crates/rpc/proto/ | |
| migration-police: | |
| needs: | |
| - changes | |
| if: ${{ contains(github.ref, 'pull-request/') }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| fetch-depth: 0 | |
| - name: Check migration policy | |
| run: | | |
| git fetch --no-tags origin main:refs/remotes/origin/main | |
| bash scripts/check-migration-filenames.sh --base origin/main | |
| bash scripts/check-migration-immutability.sh --base origin/main | |
| proto-breaking-changes: | |
| name: Proto Breaking Changes Check | |
| needs: | |
| - changes | |
| if: ${{ needs.changes.outputs.run_core_ci == 'true' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| fetch-depth: 0 # Required to compute the merge-base against main | |
| - name: Install Buf CLI | |
| run: | | |
| sudo curl -sSL https://github.com/bufbuild/buf/releases/download/v1.70.0/buf-Linux-x86_64 -o /usr/local/bin/buf | |
| sudo chmod +x /usr/local/bin/buf | |
| buf --version | |
| - name: Check for Breaking Changes | |
| run: | | |
| set -euo pipefail | |
| MERGE_BASE="$(git merge-base HEAD origin/main)" | |
| BASE_PROTO_DIR="$(mktemp -d)" | |
| trap 'rm -rf "${BASE_PROTO_DIR}"' EXIT | |
| echo "Comparing protobuf changes against merge-base ${MERGE_BASE}" | |
| git archive "${MERGE_BASE}" crates/rpc/proto | tar -x -C "${BASE_PROTO_DIR}" | |
| buf breaking crates/rpc/proto \ | |
| --against "${BASE_PROTO_DIR}/crates/rpc/proto" \ | |
| --error-format=github-actions | |
| check-rest-core-proto-sync: | |
| name: Check REST Core Proto Sync | |
| needs: | |
| - prepare | |
| if: ${{ needs.prepare.outputs.core_rpc_proto_files_changed == 'true' }} | |
| runs-on: linux-amd64-cpu4 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: "1.26.4" | |
| cache: true | |
| cache-dependency-path: rest-api/go.sum | |
| - name: Regenerate REST protos from Core protos | |
| run: make -C rest-api core-proto | |
| - name: Check repository is clean | |
| run: >- | |
| bash scripts/check-repo-clean.sh "REST Core protobuf sync" | |
| "make sync-protobuf (from the repository root)" | |
| lint-police: | |
| # Merge validation uses the PR API to resolve `.base.ref`, then fetches | |
| # that branch before creating the local merge result. It never edits the PR. | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| needs: | |
| - prepare | |
| - build-container-x86_64 | |
| if: ${{ !failure() && !cancelled() && needs.prepare.outputs.source_files_changed == 'true' && contains(github.ref, 'pull-request/') }} | |
| runs-on: linux-amd64-cpu16 | |
| container: | |
| image: ${{ needs.prepare.outputs.lint_container_ref }} | |
| credentials: | |
| username: ${{ secrets.NICO_SOURCE_REGISTRY_USERNAME || secrets.NVCR_USERNAME }} | |
| password: ${{ secrets.NICO_SOURCE_REGISTRY_TOKEN || secrets.NVCR_TOKEN }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Build pull request merge result | |
| shell: bash | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: | | |
| set -o pipefail | |
| git config --global --add safe.directory "$GITHUB_WORKSPACE" | |
| PR_NUMBER="${GITHUB_REF_NAME##pull-request/}" | |
| if ! TARGET="$( | |
| curl -sf \ | |
| -H "Authorization: Bearer $GITHUB_TOKEN" \ | |
| -H "Accept: application/vnd.github+json" \ | |
| "https://api.github.com/repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER" \ | |
| | jq -r '.base.ref' | |
| )"; then | |
| echo "Could not query the base ref for PR ${PR_NUMBER}." >&2 | |
| echo "Confirm the lint-police job still declares 'pull-requests: read'." >&2 | |
| exit 1 | |
| fi | |
| if [[ -z "${TARGET}" || "${TARGET}" == "null" ]]; then | |
| echo "Could not resolve the base ref for PR ${PR_NUMBER}." >&2 | |
| exit 1 | |
| fi | |
| git fetch --no-tags origin "${TARGET}:refs/remotes/origin/${TARGET}" | |
| git -c user.name="CI merge validation" \ | |
| -c user.email=ci@example.invalid \ | |
| merge --no-commit --no-ff "origin/${TARGET}" | |
| - name: Validate Cargo metadata | |
| run: cargo metadata --locked --no-deps --format-version 1 >/dev/null | |
| # PostgreSQL is required because sqlx proc macros (sqlx::query!) connect to | |
| # a local database at compile time to validate SQL queries and return types. | |
| # Durability settings are disabled for speed in CI/DC. | |
| - name: Start PostgreSQL | |
| run: | | |
| printf 'fsync = off\nsynchronous_commit = off\nfull_page_writes = off\n' | tee -a /etc/postgresql/*/main/postgresql.conf | |
| /etc/init.d/postgresql start | |
| sudo -u postgres psql -c "ALTER USER root WITH SUPERUSER;" | |
| createdb root | |
| - name: Run clippy | |
| run: cargo make --no-workspace clippy-flow | |
| - name: Run carbide lints | |
| run: cargo make carbide-lints | |
| - name: Check error message style | |
| run: cargo make lint-error-messages | |
| - name: Check TOML formatting | |
| run: taplo fmt --check || echo "Please format toml files" | |
| - name: Check Rust formatting | |
| run: cargo make --no-workspace check-format-nightly | |
| - name: Check workspace deps | |
| run: cargo xtask check-workspace-deps | |
| - name: Check instrumented event names | |
| run: cargo xtask check-event-names | |
| - name: Check metric docs | |
| run: cargo xtask check-metric-docs | |
| - name: Check licenses | |
| run: cargo make --no-workspace check-licenses | |
| - name: Check bans | |
| run: cargo make --no-workspace check-bans | |
| - name: Check repository is clean | |
| run: bash scripts/check-repo-clean.sh "Core pre-build checks" | |
| # ============================================================================ | |
| # BUILD STAGE - Helm Chart | |
| # ============================================================================ | |
| build-validate-helm-chart: | |
| needs: | |
| - prepare | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' }} | |
| runs-on: linux-amd64-cpu4 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Validate Helm chart | |
| uses: dsx-ai-factory/dsx-github-actions/.github/actions/helm-validate@94bde998f5d7965576b0c663db7d5d709c918167 | |
| with: | |
| chart-path: helm | |
| lint: 'true' | |
| template: 'true' | |
| build-push-helm-chart: | |
| needs: | |
| - prepare | |
| - build-validate-helm-chart | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' && needs.build-validate-helm-chart.result == 'success' && needs.prepare.outputs.publish_images == 'true' }} | |
| runs-on: linux-amd64-cpu4 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Package and push Helm chart to NGC | |
| if: ${{ needs.prepare.outputs.target_ngc_path != '' }} | |
| uses: dsx-ai-factory/dsx-github-actions/.github/actions/helm-package-push@7de619729962a6bd5e8355b6fb3582a22517d7e5 | |
| with: | |
| chart-path: helm | |
| chart-version: ${{ needs.prepare.outputs.helm_version }} | |
| app-version: ${{ needs.prepare.outputs.version }} | |
| lint: 'false' | |
| ngc-key: ${{ secrets.NICO_NGC_TOKEN || secrets.NVCR_TOKEN }} | |
| ngc-path: ${{ needs.prepare.outputs.target_ngc_path }} | |
| ngc-duplicate: fail | |
| build-validate-helm-prereqs-chart: | |
| needs: | |
| - prepare | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' }} | |
| runs-on: linux-amd64-cpu4 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Validate Helm prereqs chart | |
| uses: dsx-ai-factory/dsx-github-actions/.github/actions/helm-validate@94bde998f5d7965576b0c663db7d5d709c918167 | |
| with: | |
| chart-path: helm-prereqs | |
| lint: 'true' | |
| template: 'true' | |
| build-push-helm-prereqs-chart: | |
| needs: | |
| - prepare | |
| - build-validate-helm-prereqs-chart | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' && needs.build-validate-helm-prereqs-chart.result == 'success' && needs.prepare.outputs.publish_images == 'true' }} | |
| runs-on: linux-amd64-cpu4 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Package and push Helm prereqs chart to NGC | |
| if: ${{ needs.prepare.outputs.target_ngc_path != '' }} | |
| uses: dsx-ai-factory/dsx-github-actions/.github/actions/helm-package-push@7de619729962a6bd5e8355b6fb3582a22517d7e5 | |
| with: | |
| chart-path: helm-prereqs | |
| chart-version: ${{ needs.prepare.outputs.helm_version }} | |
| app-version: ${{ needs.prepare.outputs.version }} | |
| lint: 'false' | |
| ngc-key: ${{ secrets.NICO_NGC_TOKEN || secrets.NVCR_TOKEN }} | |
| ngc-path: ${{ needs.prepare.outputs.target_ngc_path }} | |
| ngc-duplicate: fail | |
| # ============================================================================ | |
| # BUILD STAGE - Bluefield Images | |
| # ============================================================================ | |
| build-bluefield-binaries: | |
| needs: | |
| - prepare | |
| - build-artifacts-container-aarch64 | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' && contains('success,skipped', needs.build-artifacts-container-aarch64.result) }} | |
| runs-on: linux-arm64-cpu8 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Login to NVCR | |
| uses: ./.github/actions/docker-auth | |
| with: | |
| registry: ${{ needs.prepare.outputs.source_registry_host }} | |
| username: ${{ secrets.NICO_SOURCE_REGISTRY_USERNAME || secrets.NVCR_USERNAME }} | |
| token: ${{ secrets.NICO_SOURCE_REGISTRY_TOKEN || secrets.NVCR_TOKEN }} | |
| # base refs may point at the target registry after a rebuild; log in | |
| # there too when it is a different host (same-host uses one credential). | |
| - name: Login to target registry | |
| if: ${{ needs.prepare.outputs.publish_images == 'true' && needs.prepare.outputs.registry_host != needs.prepare.outputs.source_registry_host }} | |
| uses: ./.github/actions/docker-auth | |
| with: | |
| registry: ${{ needs.prepare.outputs.registry_host }} | |
| username: ${{ secrets.NICO_TARGET_REGISTRY_USERNAME || secrets.NVCR_USERNAME }} | |
| token: ${{ secrets.NICO_TARGET_REGISTRY_TOKEN || secrets.NVCR_TOKEN }} | |
| - name: Pull build container | |
| run: docker pull ${{ needs.prepare.outputs.build_artifacts_container_aarch64_ref }} | |
| - name: Compile bluefield Rust binaries | |
| run: | | |
| docker run --rm \ | |
| -v "${{ github.workspace }}:/workspace" \ | |
| -w /workspace \ | |
| -e CARGO_HOME=/workspace/cargo \ | |
| -e CARGO_INCREMENTAL=0 \ | |
| -e CARGO_BUILD_TARGET=aarch64-unknown-linux-gnu \ | |
| ${{ needs.prepare.outputs.build_artifacts_container_aarch64_ref }} \ | |
| bash -c "git config --global --add safe.directory /workspace && \ | |
| cargo make --cwd bluefield build-dpu-agent-and-dhcp-server-ci && \ | |
| cargo make --cwd bluefield build-fmds-ci && \ | |
| cargo make --cwd bluefield build-dpu-otel-agent-ci" | |
| - name: Stage bluefield binaries for upload | |
| run: | | |
| mkdir -p bluefield-binaries-dir/target/aarch64-unknown-linux-gnu/release | |
| cp target/aarch64-unknown-linux-gnu/release/{forge-dpu-agent,forge-dhcp-server,forge-dpu-otel-agent,carbide-fmds} \ | |
| bluefield-binaries-dir/target/aarch64-unknown-linux-gnu/release/ | |
| - name: Upload bluefield binaries | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: bluefield-binaries-${{ github.run_id }} | |
| path: bluefield-binaries-dir/ | |
| build-bluefield-otelcol-contrib: | |
| needs: | |
| - prepare | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' }} | |
| uses: ./.github/workflows/docker-build.yml | |
| with: | |
| # public bases only: skip source login so anonymous nvcr pulls keep working | |
| source_registry_host: '' | |
| dockerfile_path: bluefield/containers/otelcol-contrib/Dockerfile | |
| context_path: . | |
| image_name: ${{ needs.prepare.outputs.image_registry }}/otelcol-contrib | |
| image_tag: ${{ needs.prepare.outputs.version }} | |
| additional_tags: ${{ needs.prepare.outputs.image_registry }}/otelcol-contrib:${{ needs.prepare.outputs.major_minor_version }}-latest | |
| platforms: linux/arm64 | |
| runner: linux-arm64-cpu4 | |
| push: ${{ needs.prepare.outputs.publish_images == 'true' }} | |
| load: false | |
| secrets: inherit | |
| build-bluefield-transceiver-exporter: | |
| needs: | |
| - prepare | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' }} | |
| uses: ./.github/workflows/docker-build.yml | |
| with: | |
| # public bases only: skip source login so anonymous nvcr pulls keep working | |
| source_registry_host: '' | |
| dockerfile_path: bluefield/containers/transceiver-exporter/Dockerfile | |
| context_path: . | |
| image_name: ${{ needs.prepare.outputs.image_registry }}/transceiver-exporter | |
| image_tag: ${{ needs.prepare.outputs.version }} | |
| additional_tags: ${{ needs.prepare.outputs.image_registry }}/transceiver-exporter:${{ needs.prepare.outputs.major_minor_version }}-latest | |
| platforms: linux/arm64 | |
| runner: linux-arm64-cpu4 | |
| push: ${{ needs.prepare.outputs.publish_images == 'true' }} | |
| load: false | |
| secrets: inherit | |
| download-mft-aarch64: | |
| needs: | |
| - prepare | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' }} | |
| runs-on: linux-arm64-cpu4 | |
| steps: | |
| - name: Download and extract MFT package | |
| run: | | |
| wget https://www.mellanox.com/downloads/MFT/mft-4.35.0-159-aarch64-deb.tgz | |
| tar -zxvf mft-4.35.0-159-aarch64-deb.tgz | |
| rm mft-4.35.0-159-aarch64-deb.tgz | |
| - name: Upload MFT as artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: mft-aarch64-deb-${{ github.run_id }} | |
| path: . | |
| build-bluefield-forge-dpu-agent: | |
| needs: | |
| - prepare | |
| - build-bluefield-binaries | |
| - download-mft-aarch64 | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' && needs.build-bluefield-binaries.result == 'success' && needs.download-mft-aarch64.result == 'success' }} | |
| uses: ./.github/workflows/docker-build.yml | |
| with: | |
| # public bases only: skip source login so anonymous nvcr pulls keep working | |
| source_registry_host: '' | |
| dockerfile_path: bluefield/containers/forge-dpu-agent/Dockerfile | |
| image_name: ${{ needs.prepare.outputs.image_registry }}/forge-dpu-agent | |
| image_tag: ${{ needs.prepare.outputs.version }} | |
| additional_tags: ${{ needs.prepare.outputs.image_registry }}/forge-dpu-agent:${{ needs.prepare.outputs.major_minor_version }}-latest | |
| platforms: linux/arm64 | |
| runner: linux-arm64-cpu4 | |
| push: ${{ needs.prepare.outputs.publish_images == 'true' }} | |
| load: false | |
| download_artifacts: true | |
| artifact_pattern: '{bluefield-binaries,mft-aarch64-deb}-${{ github.run_id }}' | |
| secrets: inherit | |
| build-bluefield-forge-dhcp-server: | |
| needs: | |
| - prepare | |
| - build-bluefield-binaries | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' && needs.build-bluefield-binaries.result == 'success' }} | |
| uses: ./.github/workflows/docker-build.yml | |
| with: | |
| # public bases only: skip source login so anonymous nvcr pulls keep working | |
| source_registry_host: '' | |
| dockerfile_path: bluefield/containers/forge-dhcp-server/Dockerfile | |
| image_name: ${{ needs.prepare.outputs.image_registry }}/forge-dhcp-server | |
| image_tag: ${{ needs.prepare.outputs.version }} | |
| additional_tags: ${{ needs.prepare.outputs.image_registry }}/forge-dhcp-server:${{ needs.prepare.outputs.major_minor_version }}-latest | |
| platforms: linux/arm64 | |
| runner: linux-arm64-cpu4 | |
| push: ${{ needs.prepare.outputs.publish_images == 'true' }} | |
| load: false | |
| download_artifacts: true | |
| artifact_pattern: 'bluefield-binaries-${{ github.run_id }}' | |
| secrets: inherit | |
| build-bluefield-carbide-fmds: | |
| needs: | |
| - prepare | |
| - build-bluefield-binaries | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' && needs.build-bluefield-binaries.result == 'success' }} | |
| uses: ./.github/workflows/docker-build.yml | |
| with: | |
| # public bases only: skip source login so anonymous nvcr pulls keep working | |
| source_registry_host: '' | |
| dockerfile_path: bluefield/containers/carbide-fmds/Dockerfile | |
| image_name: ${{ needs.prepare.outputs.image_registry }}/carbide-fmds | |
| image_tag: ${{ needs.prepare.outputs.version }} | |
| additional_tags: ${{ needs.prepare.outputs.image_registry }}/carbide-fmds:${{ needs.prepare.outputs.major_minor_version }}-latest | |
| platforms: linux/arm64 | |
| runner: linux-arm64-cpu4 | |
| push: ${{ needs.prepare.outputs.publish_images == 'true' }} | |
| load: false | |
| download_artifacts: true | |
| artifact_pattern: 'bluefield-binaries-${{ github.run_id }}' | |
| secrets: inherit | |
| # ============================================================================ | |
| # BUILD STAGE - Bluefield Helm Charts | |
| # ============================================================================ | |
| build-validate-bluefield-helm-charts: | |
| needs: | |
| - prepare | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' }} | |
| runs-on: linux-amd64-cpu4 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Validate nico-otelcol chart | |
| uses: dsx-ai-factory/dsx-github-actions/.github/actions/helm-validate@94bde998f5d7965576b0c663db7d5d709c918167 | |
| with: | |
| chart-path: bluefield/charts/nico-otelcol | |
| lint: 'true' | |
| template: 'true' | |
| - name: Validate nico-dpu-agent chart | |
| uses: dsx-ai-factory/dsx-github-actions/.github/actions/helm-validate@94bde998f5d7965576b0c663db7d5d709c918167 | |
| with: | |
| chart-path: bluefield/charts/nico-dpu-agent | |
| lint: 'true' | |
| template: 'true' | |
| - name: Validate nico-dhcp-server chart | |
| uses: dsx-ai-factory/dsx-github-actions/.github/actions/helm-validate@94bde998f5d7965576b0c663db7d5d709c918167 | |
| with: | |
| chart-path: bluefield/charts/nico-dhcp-server | |
| lint: 'true' | |
| template: 'true' | |
| - name: Validate nico-fmds chart | |
| uses: dsx-ai-factory/dsx-github-actions/.github/actions/helm-validate@94bde998f5d7965576b0c663db7d5d709c918167 | |
| with: | |
| chart-path: bluefield/charts/nico-fmds | |
| lint: 'true' | |
| template: 'true' | |
| build-push-bluefield-helm-charts: | |
| needs: | |
| - prepare | |
| - build-validate-bluefield-helm-charts | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' && needs.build-validate-bluefield-helm-charts.result == 'success' && needs.prepare.outputs.publish_images == 'true' }} | |
| runs-on: linux-amd64-cpu4 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Package and push nico-otelcol chart | |
| if: ${{ needs.prepare.outputs.target_ngc_path != '' }} | |
| uses: dsx-ai-factory/dsx-github-actions/.github/actions/helm-package-push@7de619729962a6bd5e8355b6fb3582a22517d7e5 | |
| with: | |
| chart-path: bluefield/charts/nico-otelcol | |
| chart-version: ${{ needs.prepare.outputs.helm_version }} | |
| app-version: ${{ needs.prepare.outputs.version }} | |
| lint: 'false' | |
| ngc-key: ${{ secrets.NICO_NGC_TOKEN || secrets.NVCR_TOKEN }} | |
| ngc-path: ${{ needs.prepare.outputs.target_ngc_path }} | |
| ngc-duplicate: fail | |
| - name: Package and push nico-dpu-agent chart | |
| if: ${{ needs.prepare.outputs.target_ngc_path != '' }} | |
| uses: dsx-ai-factory/dsx-github-actions/.github/actions/helm-package-push@7de619729962a6bd5e8355b6fb3582a22517d7e5 | |
| with: | |
| chart-path: bluefield/charts/nico-dpu-agent | |
| chart-version: ${{ needs.prepare.outputs.helm_version }} | |
| app-version: ${{ needs.prepare.outputs.version }} | |
| lint: 'false' | |
| ngc-key: ${{ secrets.NICO_NGC_TOKEN || secrets.NVCR_TOKEN }} | |
| ngc-path: ${{ needs.prepare.outputs.target_ngc_path }} | |
| ngc-duplicate: fail | |
| - name: Package and push nico-dhcp-server chart | |
| if: ${{ needs.prepare.outputs.target_ngc_path != '' }} | |
| uses: dsx-ai-factory/dsx-github-actions/.github/actions/helm-package-push@7de619729962a6bd5e8355b6fb3582a22517d7e5 | |
| with: | |
| chart-path: bluefield/charts/nico-dhcp-server | |
| chart-version: ${{ needs.prepare.outputs.helm_version }} | |
| app-version: ${{ needs.prepare.outputs.version }} | |
| lint: 'false' | |
| ngc-key: ${{ secrets.NICO_NGC_TOKEN || secrets.NVCR_TOKEN }} | |
| ngc-path: ${{ needs.prepare.outputs.target_ngc_path }} | |
| ngc-duplicate: fail | |
| - name: Package and push nico-fmds chart | |
| if: ${{ needs.prepare.outputs.target_ngc_path != '' }} | |
| uses: dsx-ai-factory/dsx-github-actions/.github/actions/helm-package-push@7de619729962a6bd5e8355b6fb3582a22517d7e5 | |
| with: | |
| chart-path: bluefield/charts/nico-fmds | |
| chart-version: ${{ needs.prepare.outputs.helm_version }} | |
| app-version: ${{ needs.prepare.outputs.version }} | |
| lint: 'false' | |
| ngc-key: ${{ secrets.NICO_NGC_TOKEN || secrets.NVCR_TOKEN }} | |
| ngc-path: ${{ needs.prepare.outputs.target_ngc_path }} | |
| ngc-duplicate: fail | |
| build-summary: | |
| runs-on: linux-amd64-cpu4 | |
| if: ${{ !cancelled() && github.event_name != 'schedule' && needs.prepare.result == 'success' }} | |
| needs: | |
| - prepare | |
| - build-container-x86_64 | |
| - build-container-aarch64 | |
| - build-runtime-container-x86_64 | |
| - build-runtime-container-aarch64 | |
| - build-artifacts-container-x86_64 | |
| - build-artifacts-container-aarch64 | |
| - build-release-container-x86_64 | |
| - build-release-container-aarch64 | |
| - build-machine-a-tron | |
| - build-mat-k8s-controller | |
| - test-release-container-services | |
| - build-boot-artifacts-x86 | |
| - build-boot-artifacts-bfb | |
| - build-package-scout-aarch64 | |
| - build-boot-artifacts-ephemeral-image-x86-host | |
| - build-boot-artifacts-ephemeral-image-arm-host | |
| - build-forge-cli-x86_64 | |
| - build-forge-cli-aarch64 | |
| - merge-manifests-forge-cli | |
| - build-release-machine-validation-runner | |
| - build-release-machine-validation-artifacts-x86-host | |
| - build-validate-helm-chart | |
| - build-push-helm-chart | |
| - build-validate-helm-prereqs-chart | |
| - build-push-helm-prereqs-chart | |
| - build-release-artifacts-x86-host | |
| - build-release-artifacts-arm-host | |
| - build-bluefield-binaries | |
| - build-bluefield-otelcol-contrib | |
| - build-bluefield-transceiver-exporter | |
| - build-bluefield-forge-dpu-agent | |
| - build-bluefield-forge-dhcp-server | |
| - build-bluefield-carbide-fmds | |
| - build-validate-bluefield-helm-charts | |
| - build-push-bluefield-helm-charts | |
| - lint-police | |
| steps: | |
| - name: Generate summary | |
| run: | | |
| NEEDS='${{ toJson(needs) }}' | |
| echo "## Build Jobs Summary" >> "$GITHUB_STEP_SUMMARY" | |
| echo >> "$GITHUB_STEP_SUMMARY" | |
| echo "| Job | Status |" >> "$GITHUB_STEP_SUMMARY" | |
| echo "|-----|--------|" >> "$GITHUB_STEP_SUMMARY" | |
| echo "$NEEDS" | jq -r ' | |
| to_entries[] | |
| | select((.key | startswith("build-")) or (.key | startswith("test-"))) | |
| | "| \(.key) | \(if .value.result == "failure" then "**\(.value.result)**" else .value.result end) |" | |
| ' >> "$GITHUB_STEP_SUMMARY" | |
| echo >> "$GITHUB_STEP_SUMMARY" | |
| echo "## Docker Images Built" >> "$GITHUB_STEP_SUMMARY" | |
| echo >> "$GITHUB_STEP_SUMMARY" | |
| echo "| Job | Image |" >> "$GITHUB_STEP_SUMMARY" | |
| echo "|-----|-------|" >> "$GITHUB_STEP_SUMMARY" | |
| # Collect image_ref from docker-build jobs | |
| declare -A IMAGES | |
| IMAGES["build-container-x86_64"]="${{ needs.build-container-x86_64.outputs.image_ref }}" | |
| IMAGES["build-container-aarch64"]="${{ needs.build-container-aarch64.outputs.image_ref }}" | |
| IMAGES["build-runtime-container-x86_64"]="${{ needs.build-runtime-container-x86_64.outputs.image_ref }}" | |
| IMAGES["build-runtime-container-aarch64"]="${{ needs.build-runtime-container-aarch64.outputs.image_ref }}" | |
| IMAGES["build-artifacts-container-x86_64"]="${{ needs.build-artifacts-container-x86_64.outputs.image_ref }}" | |
| IMAGES["build-artifacts-container-aarch64"]="${{ needs.build-artifacts-container-aarch64.outputs.image_ref }}" | |
| IMAGES["build-release-container-x86_64"]="${{ needs.build-release-container-x86_64.outputs.image_ref }}" | |
| IMAGES["build-release-container-aarch64"]="${{ needs.build-release-container-aarch64.outputs.image_ref }}" | |
| IMAGES["build-forge-cli-x86_64"]="${{ needs.build-forge-cli-x86_64.outputs.image_ref }}" | |
| IMAGES["build-forge-cli-aarch64"]="${{ needs.build-forge-cli-aarch64.outputs.image_ref }}" | |
| IMAGES["build-release-machine-validation-runner"]="${{ needs.build-release-machine-validation-runner.outputs.image_ref }}" | |
| IMAGES["build-release-machine-validation-artifacts-x86-host"]="${{ needs.build-release-machine-validation-artifacts-x86-host.outputs.image_ref }}" | |
| IMAGES["build-release-artifacts-x86-host"]="${{ needs.build-release-artifacts-x86-host.outputs.image_ref }}" | |
| IMAGES["build-release-artifacts-arm-host"]="${{ needs.build-release-artifacts-arm-host.outputs.image_ref }}" | |
| IMAGES["build-mat-k8s-controller"]="${{ needs.build-mat-k8s-controller.outputs.image_ref }}" | |
| for job in "${!IMAGES[@]}"; do | |
| image="${IMAGES[$job]}" | |
| if [[ -n "$image" ]]; then | |
| echo "| $job | \`$image\` |" >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| done | |
| promote-to-be-scanned-image: | |
| # Skip for pull-request/* branches (main, release/*, and tags are allowed by workflow trigger) | |
| if: ${{ !cancelled() && github.repository == 'NVIDIA/infra-controller' && needs.build-release-container-x86_64.result == 'success' && github.event_name != 'schedule' && needs.prepare.outputs.publish_images == 'true' }} | |
| needs: | |
| - prepare | |
| - build-release-container-x86_64 | |
| uses: dsx-ai-factory/dsx-github-actions/.github/workflows/promote-image.yml@760d2d7964b479fde431cd3e0b980bc6b6a26ccd | |
| with: | |
| source: ${{ needs.prepare.outputs.image_registry }}/nvmetal-carbide | |
| source_tag: ${{ needs.prepare.outputs.version }} | |
| destination: ${{ needs.prepare.outputs.prod_image_registry }}/nvmetal-carbide | |
| destination_tag: "to-be-scanned" | |
| secrets: | |
| SOURCE_USERNAME: ${{ secrets.NVCR_USERNAME }} | |
| SOURCE_PASSWORD: ${{ secrets.NVCR_TOKEN }} | |
| DEST_USERNAME: ${{ secrets.NVCR_PROD_USERNAME }} | |
| DEST_PASSWORD: ${{ secrets.NVCR_PROD_TOKEN }} | |
| # ============================================================================ | |
| # NOTIFICATION STAGE | |
| # ============================================================================ | |
| notify-build-status: | |
| if: ${{ always() && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/') || startsWith(github.ref, 'refs/tags/')) && github.event_name != 'schedule' }} | |
| needs: | |
| - prepare | |
| - build-container-x86_64 | |
| - build-container-aarch64 | |
| - build-runtime-container-x86_64 | |
| - build-runtime-container-aarch64 | |
| - build-artifacts-container-x86_64 | |
| - build-artifacts-container-aarch64 | |
| - build-release-container-x86_64 | |
| - build-release-container-aarch64 | |
| - build-machine-a-tron | |
| - build-mat-k8s-controller | |
| - test-release-container-services | |
| - build-boot-artifacts-x86 | |
| - build-boot-artifacts-bfb | |
| - build-package-scout-aarch64 | |
| - build-boot-artifacts-ephemeral-image-x86-host | |
| - build-boot-artifacts-ephemeral-image-arm-host | |
| - build-forge-cli-x86_64 | |
| - build-forge-cli-aarch64 | |
| - merge-manifests-forge-cli | |
| - build-release-machine-validation-runner | |
| - build-release-machine-validation-artifacts-x86-host | |
| - build-validate-helm-chart | |
| - build-push-helm-chart | |
| - build-validate-helm-prereqs-chart | |
| - build-push-helm-prereqs-chart | |
| - build-release-artifacts-x86-host | |
| - build-release-artifacts-arm-host | |
| - build-bluefield-binaries | |
| - build-bluefield-otelcol-contrib | |
| - build-bluefield-transceiver-exporter | |
| - build-bluefield-forge-dpu-agent | |
| - build-bluefield-forge-dhcp-server | |
| - build-bluefield-carbide-fmds | |
| - build-validate-bluefield-helm-charts | |
| - build-push-bluefield-helm-charts | |
| - security-secret-scan | |
| - security-codeql-scan | |
| - promote-to-be-scanned-image | |
| uses: ./.github/workflows/notify-build-status.yml | |
| with: | |
| version: ${{ needs.prepare.outputs.version }} | |
| workflow-name: "NICo Core CI" | |
| channel-id: C0A0TSKJKSB # #dsx-carbide-feed | |
| needs-context: ${{ toJson(needs) }} | |
| notify-on-success: true | |
| notify-on-partial: true | |
| notify-on-failure: true | |
| secrets: | |
| slack-bot-token: ${{ secrets.CDS_SLACK_BOT_OAUTH_TOKEN }} | |
| # ============================================================================ | |
| # FINAL CORE CI GATE | |
| # ============================================================================ | |
| # Passes only when every required job reports `success` or `skipped`. | |
| # `skipped` counts as pass — that's how rest-only PRs unblock when the | |
| # `changes` gate intentionally skips the core pipeline. | |
| # Any other result, plus missing or malformed data, fails closed. | |
| # Every job that can determine Core CI health is listed so an upstream | |
| # failure cannot be hidden when its downstream jobs become `skipped`. | |
| # Reporting-only jobs are explicitly exempted, and the checker in `changes` | |
| # rejects every job that is neither gated nor exempted. | |
| # The checker also protects `if: always()` so a failed dependency cannot skip | |
| # this required check. | |
| # Branch protection should require only this gate and `rest-ci-pass`. | |
| core-ci-pass: | |
| name: core-ci-pass | |
| runs-on: ubuntu-latest | |
| if: always() | |
| needs: | |
| # Detection and preparation | |
| - changes | |
| - prepare | |
| # Build, runtime, and artifact base images | |
| - build-container-x86_64 | |
| - build-container-aarch64 | |
| - build-runtime-container-x86_64 | |
| - build-runtime-container-aarch64 | |
| - build-artifacts-container-x86_64 | |
| - build-artifacts-container-aarch64 | |
| # Release containers and service builds | |
| - build-release-container-x86_64 | |
| - build-release-container-aarch64 | |
| - merge-manifests-nvmetal-carbide | |
| - build-machine-a-tron | |
| - build-mat-k8s-controller | |
| - test-release-container-services | |
| # CLI images | |
| - build-forge-cli-x86_64 | |
| - build-forge-cli-aarch64 | |
| - merge-manifests-forge-cli | |
| # Boot and release artifacts | |
| - build-boot-artifacts-x86 | |
| - build-boot-artifacts-bfb | |
| - build-package-scout-aarch64 | |
| - build-boot-artifacts-ephemeral-image-x86-host | |
| - build-boot-artifacts-ephemeral-image-arm-host | |
| - build-release-artifacts-x86-host | |
| - build-release-artifacts-arm-host | |
| # Security checks | |
| - security-secret-scan | |
| - security-codeql-scan | |
| # Machine validation images | |
| - build-release-machine-validation-runner | |
| - build-release-machine-validation-artifacts-x86-host | |
| - build-release-machine-validation-artifacts-arm-host | |
| - merge-manifests-machine-validation | |
| # Source and repository policy checks | |
| - proto-police | |
| - lint-police | |
| - migration-police | |
| - proto-breaking-changes | |
| - check-rest-core-proto-sync | |
| # Core Helm charts | |
| - build-validate-helm-chart | |
| - build-push-helm-chart | |
| - build-validate-helm-prereqs-chart | |
| - build-push-helm-prereqs-chart | |
| # BlueField binaries, images, and charts | |
| - build-bluefield-binaries | |
| - build-bluefield-otelcol-contrib | |
| - build-bluefield-transceiver-exporter | |
| - download-mft-aarch64 | |
| - build-bluefield-forge-dpu-agent | |
| - build-bluefield-forge-dhcp-server | |
| - build-bluefield-carbide-fmds | |
| - build-validate-bluefield-helm-charts | |
| - build-push-bluefield-helm-charts | |
| # Post-build publication | |
| - promote-to-be-scanned-image | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Decide pass/fail | |
| env: | |
| NEEDS_JSON: ${{ toJson(needs) }} | |
| run: python3 -B .github/ci/check_ci_gate.py results |