Skip to content
Merged
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
440 changes: 0 additions & 440 deletions .claude/scripts/enumerate-endpoints.sh

This file was deleted.

347 changes: 37 additions & 310 deletions .github/workflows/on-merge.yml
Original file line number Diff line number Diff line change
@@ -1,346 +1,73 @@
name: "On Merge | Validate Build"
name: "On Merge Validate"

on:
# Use a merge queue to gate the creation and storage
# of these Docker images.
# The merge queue validates each PR against the latest trunk before it lands.
# This fires only when a GitHub merge queue is enabled for the repo; with no
# queue configured it simply never runs.
merge_group:
# Allow this job to be executed manually from the GH UI.
workflow_dispatch:

# No cancel-in-progress here: merge-queue runs must not cancel each other.

env:
CARGO_TERM_COLOR: always
# Container registry
REGISTRY: ghcr.io
# The name of the Docker image
IMAGE_NAME: "__SERVICE_NAME__"

jobs:
# Single required status check. Keep this job's name ("⚡ PR Ready") identical
# to the one in on-push.yml so the same branch-protection check is satisfied
# in both the push and merge-queue contexts.
pr-ready:
if: always()
name: "⚡ PR Ready"
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
needs:
- "format"
- "helm-lint"
- "build"
- "coverage"
- "docker"
- "helm"
- "manifest"
- validate
- cuda-build
steps:
- if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }}
- if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }}
run: |
echo "One or more dependent jobs failed, was skipped, or was cancelled. All jobs must pass for the PR to be ready."
echo "One or more dependent jobs failed, was skipped, or was cancelled. All jobs must pass to merge."
exit 1
- run: echo "OK"

format:
validate:
name: fmt · clippy · build · test
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v5

# Reads the channel + components from rust-toolchain.toml and sets up
# caching for the workspace.
- name: Install Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
components: rustfmt

- name: Configure private git dependencies
run: |
git config --global url."https://x-access-token:${{ secrets.CROSS_REPO_PAT }}@github.com/".insteadOf "https://github.com/"
git config --global --add url."https://x-access-token:${{ secrets.CROSS_REPO_PAT }}@github.com/".insteadOf "ssh://git@github.com/"

- name: Install cargo-make
uses: taiki-e/install-action@v2
with:
tool: cargo-make
components: rustfmt, clippy

- name: Check formatting
run: cargo make check-format
run: cargo fmt --all --check

# Lint Helm chart early to fail fast on chart issues.
# This runs in parallel with format and before the longer build jobs.
helm-lint:
name: Lint Helm Chart
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Clippy (deny warnings)
run: cargo clippy --all-targets --workspace --locked -- -D warnings

- name: Install Helm
uses: azure/setup-helm@v5
with:
version: v3.14.0
- name: Build
run: cargo build --workspace --locked

- name: Lint Helm chart
run: helm lint helm/chart
- name: Test
run: cargo test --workspace --locked

# This job installs Cargo Make and Cargo Nextest before running
# the CI workflow using Cargo Make. Most of the time, it should
# restore Cargo Make and other dependencies from cache.
build:
name: Validate Rust Build
# Compile-check the CUDA (CubeCL) backend. cudarc uses dynamic loading, so this
# builds with no GPU, driver, or CUDA toolkit present — it only validates that
# the `#[cfg(feature = "cuda")]` code still compiles. Actually running the CUDA
# backend needs a GPU runner, which is out of scope here.
cuda-build:
name: build (cuda, compile-only)
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v5

- name: Setup Rust
uses: "wack/gh-actions/setup-rust@trunk"
with:
shared-key: rust-ci
extra-tools: cargo-nextest

- name: Configure private git dependencies
run: |
git config --global url."https://x-access-token:${{ secrets.CROSS_REPO_PAT }}@github.com/".insteadOf "https://github.com/"
git config --global --add url."https://x-access-token:${{ secrets.CROSS_REPO_PAT }}@github.com/".insteadOf "ssh://git@github.com/"

- name: Cargo Make
run: cargo make ci-flow

# Run the test suite under coverage. Shares the `rust-ci` cache namespace with
# the build job so the warmed cargo cache is reused.
coverage:
name: Coverage
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v5

- name: Setup Rust
uses: "wack/gh-actions/setup-rust@trunk"
with:
shared-key: rust-ci
extra-tools: cargo-nextest,cargo-llvm-cov

- name: Configure private git dependencies
run: |
git config --global url."https://x-access-token:${{ secrets.CROSS_REPO_PAT }}@github.com/".insteadOf "https://github.com/"
git config --global --add url."https://x-access-token:${{ secrets.CROSS_REPO_PAT }}@github.com/".insteadOf "ssh://git@github.com/"

- name: Coverage
run: cargo make coverage-flow

# Build Docker images for multiple architectures
docker:
name: Build Docker (${{ matrix.arch }})
runs-on: ${{ matrix.runner }}
needs:
- "build"
permissions:
contents: read
packages: write

strategy:
fail-fast: false
matrix:
arch:
- amd64
- arm64
include:
- arch: amd64
runner: ubuntu-24.04
platform: linux/amd64
- arch: arm64
runner: ubuntu-24.04-arm
platform: linux/arm64

steps:
- name: Checkout repository
uses: actions/checkout@v5

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4

- name: Log in to Container Registry
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Generate image tags
id: tags
run: |
IMAGE="${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}"
SHA_SHORT=$(echo "${{ github.sha }}" | cut -c1-7)

# Architecture-specific tag (always set)
echo "arch_tag=${IMAGE}:${SHA_SHORT}-${{ matrix.arch }}" >> $GITHUB_OUTPUT

# Clean SHA tag (without architecture suffix) for Helm chart reference
echo "sha_tag=${IMAGE}:${SHA_SHORT}" >> $GITHUB_OUTPUT
echo "sha_short=${SHA_SHORT}" >> $GITHUB_OUTPUT

# For merge queue, use the head SHA for branch tagging
# Sanitize branch name: replace / with - for valid Docker tags
if [[ "${{ github.event_name }}" == "merge_group" ]]; then
BRANCH=$(echo "${{ github.base_ref }}" | sed 's/\//-/g')
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
BRANCH=$(echo "${{ github.ref_name }}" | sed 's/\//-/g')
fi

# Only set branch_tag if BRANCH is non-empty
if [[ -n "$BRANCH" ]]; then
echo "branch_tag=${IMAGE}:${BRANCH}-${{ matrix.arch }}" >> $GITHUB_OUTPUT
fi

- name: Build and push
uses: docker/build-push-action@v7
with:
context: .
file: Dockerfile
platforms: ${{ matrix.platform }}
push: true
tags: |
${{ steps.tags.outputs.arch_tag }}
${{ steps.tags.outputs.branch_tag }}
# Build-time provenance plumbed into OpenTelemetry resource
# attributes by build.rs. Names match the OTel CI/CD + VCS
# semantic conventions.
build-args: |
VCS_REF_HEAD_REVISION=${{ github.sha }}
VCS_REF_HEAD_NAME=${{ github.base_ref || github.ref_name }}
VCS_REF_HEAD_TYPE=branch
VCS_REPOSITORY_URL_FULL=${{ github.server_url }}/${{ github.repository }}
CICD_PIPELINE_NAME=${{ github.workflow }}
CICD_PIPELINE_RUN_URL_FULL=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
cache-from: type=gha,scope=${{ env.IMAGE_NAME }}-${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=${{ env.IMAGE_NAME }}-${{ matrix.arch }}
provenance: false
secrets: |
GITHUB_TOKEN=${{ secrets.CROSS_REPO_PAT }}

# Create multi-arch manifest after all builds complete
manifest:
name: Create manifest
runs-on: ubuntu-latest
needs: docker
permissions:
contents: read
packages: write

steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4

- name: Log in to Container Registry
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Create and push manifest
run: |
IMAGE="${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}"
SHA_SHORT=$(echo "${{ github.sha }}" | cut -c1-7)

# Create manifest for SHA tag
docker buildx imagetools create -t "${IMAGE}:${SHA_SHORT}" \
"${IMAGE}:${SHA_SHORT}-amd64" \
"${IMAGE}:${SHA_SHORT}-arm64"

# Determine branch name and sanitize it (replace / with - for valid Docker tags)
if [[ "${{ github.event_name }}" == "merge_group" ]]; then
BRANCH=$(echo "${{ github.base_ref }}" | sed 's/\//-/g')
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
BRANCH=$(echo "${{ github.ref_name }}" | sed 's/\//-/g')
fi

# Create manifest for branch tag only if BRANCH is non-empty
if [[ -n "$BRANCH" ]]; then
docker buildx imagetools create -t "${IMAGE}:${BRANCH}" \
"${IMAGE}:${BRANCH}-amd64" \
"${IMAGE}:${BRANCH}-arm64"
fi

# Create 'latest' tag for trunk branch
if [[ "${{ github.base_ref }}" == "trunk" ]] || [[ "${{ github.ref_name }}" == "trunk" ]]; then
docker buildx imagetools create -t "${IMAGE}:latest" \
"${IMAGE}:${SHA_SHORT}-amd64" \
"${IMAGE}:${SHA_SHORT}-arm64"
fi

# Publish Helm chart after Docker images are pushed.
# This job runs after the Docker job completes successfully, ensuring that
# the Docker image exists before we publish a Helm chart that references it.
# The chart's appVersion is dynamically set to match the Docker image tag (commit SHA),
# guaranteeing that `helm install` will pull the correct container image.
helm:
name: Publish Helm Chart
runs-on: ubuntu-latest
needs: docker
permissions:
contents: read # Required to checkout the repository
packages: write # Required to push to GitHub Container Registry

steps:
# Checkout the repository to access the Helm chart source files.
- name: Checkout repository
- name: Checkout code
uses: actions/checkout@v5

# Install Helm CLI. We pin to a specific version for reproducibility.
- name: Install Helm
uses: azure/setup-helm@v5
with:
version: v3.14.0

# Authenticate to GHCR using the GitHub token.
# This is required for pushing the packaged chart as an OCI artifact.
- name: Log in to Container Registry
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# Set the chart version using date and workflow run number for monotonic versioning.
# Format: YYYY.MM.DD-<run_number> (e.g., 2026.02.04-42)
- name: Set chart version
run: |
VERSION="$(date -u +%Y.%m.%d)-${{ github.run_number }}"
sed -i "s/^version:.*/version: ${VERSION}/" helm/chart/Chart.yaml
echo "Set chart version to: ${VERSION}"

# Update the chart's appVersion to match the Docker image tag.
# This ensures the Helm chart references the exact Docker image built in this workflow.
# The appVersion (commit SHA) becomes the default image tag when the chart is installed.
- name: Update Chart.yaml appVersion with commit SHA
run: |
SHA_SHORT=$(echo "${{ github.sha }}" | cut -c1-7)
sed -i "s/^appVersion:.*/appVersion: \"${SHA_SHORT}\"/" helm/chart/Chart.yaml
echo "Updated appVersion to: ${SHA_SHORT}"
cat helm/chart/Chart.yaml

# Extract the chart version from Chart.yaml for use in subsequent steps.
# The chart version is set dynamically using date and run number (YYYY.MM.DD-<run_number>).
- name: Get chart version
id: chart
run: |
VERSION=$(grep '^version:' helm/chart/Chart.yaml | awk '{print $2}')
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Chart version: $VERSION"

# Validate the Helm chart structure and templates.
# This catches syntax errors and misconfigurations before publishing.
- name: Lint Helm chart
run: helm lint helm/chart

# Package the chart into a versioned .tgz archive.
# The output filename follows the pattern: <chart-name>-<version>.tgz
- name: Package Helm chart
run: helm package helm/chart
- name: Install Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1

# Push the packaged chart to GHCR as an OCI artifact.
# The chart will be available at: oci://ghcr.io/<owner>/charts/__SERVICE_NAME__:<version>
# Users can install with: helm install __SERVICE_NAME__ oci://ghcr.io/<owner>/charts/__SERVICE_NAME__ --version <version>
- name: Push Helm chart to GHCR
run: |
helm push __SERVICE_NAME__-${{ steps.chart.outputs.version }}.tgz oci://${{ env.REGISTRY }}/${{ github.repository_owner }}/charts
- name: Build (cuda feature)
run: cargo build -p wubbie --no-default-features --features cuda --locked
Loading