Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 23 additions & 12 deletions .github/workflows/bazel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,19 @@ concurrency:
group: bazel-${{ github.ref }}
cancel-in-progress: true

# NOTE: keep the bazel-ci image tag as a string literal in
# container.image and any tag-bump bookkeeping. GitHub Actions
# evaluates job-level `container.image` before the workflow-level
# `env:` context is reliably available, so referencing
# `${{ env.BAZEL_CI_IMAGE }}` here causes the matrix to expand to
# zero jobs on push events (validated empirically; PR-event runs
# happen to work). The duplication is intentional.
# The bazel-ci job container comes from the repository variable
# BAZEL_CI_IMAGE, so the image is bumped in one place (repo settings) instead
# of being edited in every workflow that runs in it.
#
# It must be `vars`, NOT `env`. GitHub Actions evaluates job-level
# `container.image` before the workflow-level `env:` context is reliably
# available, so `${{ env.BAZEL_CI_IMAGE }}` made the matrix expand to zero jobs
# on push events (validated empirically; PR-event runs happened to work). The
# `vars` context does not have that ordering problem.
#
# The `||` fallback keeps CI working if the variable is unset or unavailable
# (for example on a fork). Keep the fallback in step with the variable; it is a
# safety net, not the source of truth.

jobs:
detect:
Expand Down Expand Up @@ -314,7 +320,7 @@ jobs:
# Bazelisk then selects the root .bazelversion release. Update this tag
# only after the corresponding internal image has been published and
# mirrored.
image: ghcr.io/nvidia/nvcf/bazel-ci:0.13.0
image: ${{ vars.BAZEL_CI_IMAGE || 'ghcr.io/nvidia/nvcf/bazel-ci:0.13.0' }}
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
Expand All @@ -331,9 +337,12 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
# Full history so the root row can diff a PR against its base to
# compute affected targets (see "Determine build targets").
fetch-depth: 0
# Only the root row (path ".") needs full history: it diffs a PR
# against its base to compute affected targets (see "Determine build
# targets"). Every other row builds its whole scope regardless, so a
# shallow checkout there skips a full-history clone. (GHA treats 0 as
# falsy, so the ternary is written non-root-first to return 0 for root.)
fetch-depth: ${{ matrix.subtree.path != '.' && 1 || 0 }}

# A subtree can be selected but not yet have its Bazel scaffold on
# the mirror (its upstream OSS-flip MR is still open, or the
Expand Down Expand Up @@ -717,7 +726,9 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
# docker-host rows never build the root scope, so no git history is
# needed here; a shallow checkout is enough.
fetch-depth: 1
# The Java tree builds with local_jdk (root .bazelrc). Unlike the fast
# matrix, this lane runs on a bare runner rather than the bazel-ci image,
# so provide JDK 25 explicitly for the Java requires-docker tests.
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/chart-push-manual.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
name: package + push chart to ncp-dev
runs-on: ubuntu-latest
container:
image: ghcr.io/nvidia/nvcf/bazel-ci:0.8.0
image: ${{ vars.BAZEL_CI_IMAGE || 'ghcr.io/nvidia/nvcf/bazel-ci:0.13.0' }}
defaults:
run:
# In container jobs Actions falls back to plain `sh` (dash), which
Expand Down
80 changes: 63 additions & 17 deletions .github/workflows/image-push-manual.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,12 @@ on:
workflow_dispatch:
inputs:
service_path:
description: Service subtree to build and push
description: >-
Service subtree to build and push, e.g.
src/invocation-plane-services/grpc-proxy. Any subtree with an
oci_image_index target works; the list is not maintained here.
required: true
type: choice
options:
- src/invocation-plane-services/grpc-proxy
- src/invocation-plane-services/http-invocation
- src/invocation-plane-services/llm-api-gateway
- src/invocation-plane-services/ratelimiter
- src/invocation-plane-services/vanity-gateway
- src/control-plane-services/nats-auth-callout
- src/control-plane-services/function-autoscaler
- src/control-plane-services/helm-reval
- src/compute-plane-services/nvca
- src/compute-plane-services/ess-agent
- src/compute-plane-services/image-credential-helper
type: string

permissions:
contents: read
Expand All @@ -48,7 +39,7 @@ jobs:
name: push to ncp-dev
runs-on: ubuntu-latest
container:
image: ghcr.io/nvidia/nvcf/bazel-ci:0.8.0
image: ${{ vars.BAZEL_CI_IMAGE || 'ghcr.io/nvidia/nvcf/bazel-ci:0.13.0' }}
defaults:
run:
# In container jobs Actions falls back to plain `sh` (dash), which
Expand Down Expand Up @@ -116,28 +107,83 @@ jobs:
printf '{"auths":{"%s":{"auth":"%s"}}}\n' "${REGISTRY}" "${auth}" > "$HOME/.docker/config.json"
chmod 600 "$HOME/.docker/config.json"

# Resolve where Bazel must run and how to scope the query. A subtree with
# its own MODULE.bazel is queried from inside itself; a service that
# lives in the repo-root module (the Java services) is queried from the
# root with a path-scoped pattern. Deriving this rather than hardcoding
# it means a new service needs no change to this workflow.
- name: Resolve Bazel module layout
id: layout
env:
SVC_PATH: ${{ github.event.inputs.service_path }}
run: |
set -euo pipefail
if [ ! -d "$SVC_PATH" ]; then
echo "ERROR: no such subtree: $SVC_PATH" >&2
echo "Subtrees owning a Bazel module:" >&2
find src -maxdepth 4 -name MODULE.bazel -printf ' %h\n' 2>/dev/null | sort >&2
exit 1
fi
if [ -f "$SVC_PATH/MODULE.bazel" ]; then
echo "workdir=$SVC_PATH" >> "$GITHUB_OUTPUT"
echo "scope=//..." >> "$GITHUB_OUTPUT"
echo "layout: standalone module rooted at $SVC_PATH"
else
echo "workdir=." >> "$GITHUB_OUTPUT"
echo "scope=//$SVC_PATH/..." >> "$GITHUB_OUTPUT"
echo "layout: root module, scoped to //$SVC_PATH/..."
fi

- name: Build and push multi-arch image(s)
working-directory: ${{ github.event.inputs.service_path }}
working-directory: ${{ steps.layout.outputs.workdir }}
env:
SVC_PATH: ${{ github.event.inputs.service_path }}
SCOPE: ${{ steps.layout.outputs.scope }}
TAG: ${{ steps.meta.outputs.tag }}
REGISTRY: ${{ secrets.NCP_DEV_REGISTRY }}
run: |
set -euo pipefail
REGISTRY="${REGISTRY%/}"
svc="$(basename "$SVC_PATH")"
export BAZEL_DISK_CACHE="${HOME}/.bazel-disk-cache"
mapfile -t indexes < <(bazel query --remote_cache= 'kind("oci_image_index", //...)')
# Run the query directly rather than inside process substitution.
# With `mapfile < <(bazel query ...)` only stdout reaches mapfile, so a
# failing query (BUILD error, unloadable package) yields an empty array
# and would be misreported below as "no image targets" instead of
# surfacing the real error.
if ! query_out="$(bazel query --remote_cache= "kind(\"oci_image_index\", ${SCOPE})")"; then
echo "ERROR: bazel query failed for scope ${SCOPE}" >&2
exit 1
fi
# Build the array by hand: a here-string of empty output would produce
# a single empty element rather than an empty array.
indexes=()
while IFS= read -r line; do
[ -n "$line" ] && indexes+=("$line")
done <<< "$query_out"
if [ "${#indexes[@]}" -eq 0 ]; then
echo "ERROR: no oci_image_index targets under ${SVC_PATH}" >&2
echo "The subtree must declare an image target (go_oci_image, java_oci_image, ...)." >&2
exit 1
fi
echo "discovered: ${indexes[*]}"
for tgt in "${indexes[@]}"; do
name="${tgt##*:}"; name="${name%_index}"
# Two naming conventions exist in the tree and they mean different
# things, distinguished by the separator:
# image -> the service's sole image; repo is the service
# <component>_image -> a sub-component; repo is <service>-<component>
# (nvcf-unbound webhook, llm-api-gateway
# rate_limit_sync_worker, nvsnap agent/server)
# <image-name>-image -> the target already carries the full image
# name; use it as-is, do NOT prefix the
# service (byoo-otel-collector, cloud-tasks)
# Previously the hyphenated form fell through to the default and
# produced names like byoo-otel-collector-byoo-otel-collector-image.
case "$name" in
image) repo="${svc}" ;;
*_image) sub="$(printf '%s' "${name%_image}" | tr '_' '-')"; repo="${svc}-${sub}" ;;
*-image) repo="${name%-image}" ;;
*) sub="$(printf '%s' "$name" | tr '_' '-')"; repo="${svc}-${sub}" ;;
esac
dest="${REGISTRY}/${repo}"
Expand Down
13 changes: 10 additions & 3 deletions BAZEL.md
Original file line number Diff line number Diff line change
Expand Up @@ -429,13 +429,20 @@ build).

## CI

The public GitHub Bazel matrix in `.github/workflows/bazel.yml` consumes
`ghcr.io/nvidia/nvcf/bazel-ci:0.12.0`. That image is built in the internal
Every workflow that needs the Bazel toolchain runs in the `bazel-ci` job
container, sourced from the repository variable `BAZEL_CI_IMAGE` (currently
Comment thread
coderabbitai[bot] marked this conversation as resolved.
`ghcr.io/nvidia/nvcf/bazel-ci`). Each workflow carries the current pin as a
`||` fallback so CI still runs when the variable is unavailable, for example on
a fork.

That image is built in the internal
[`nvcf/bazel-ci-templates`](https://gitlab-master.nvidia.com/nvcf/bazel-ci-templates)
project, stamped with a version, and mirrored to GHCR. The mirror is currently
manual; automation is planned. To change the image's Bazel, Java, or operating
system tooling, update the internal template first, publish and mirror a new
tag, and only then update the pinned `container.image` in `.github/workflows/bazel.yml`.
tag, then update the `BAZEL_CI_IMAGE` repository variable. Do not hardcode the
tag per workflow: it drifted to three different versions across workflows and
this document before the variable was introduced.

The root `ci/Dockerfile.bazel` and `.github/workflows/bazel-ci-image.yml` were a
stale, divergent copy (no Java, older Bazel) and have been removed. The image is
Expand Down
Loading