From 5a8357586705460dc45feea89574824eff0e7182 Mon Sep 17 00:00:00 2001 From: Kelly Abuelsaad Date: Tue, 21 Jul 2026 10:12:37 -0400 Subject: [PATCH 1/2] ci(build): add GHCR image build workflow for exgentic-a2a-runner Automates publishing multi-arch images to ghcr.io/kagenti/workload-harness/exgentic-a2a-runner on tag pushes, pushes to main, and manual dispatch. Modeled after the build.yaml in rossoctl/cortex, with a matrix so future images plug in as extra entries. Also repoint k8s/job.yaml to the new registry path and document the image publishing scheme in the README. Closes #36 Signed-off-by: Kelly Abuelsaad --- .github/workflows/build.yaml | 97 ++++++++++++++++++++++++++++++++ README.md | 42 +++++++++++--- exgentic_a2a_runner/k8s/job.yaml | 4 +- 3 files changed, 133 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/build.yaml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..f64dfb7 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,97 @@ +name: Build-Publish + +on: + push: + # Trigger on version tags like v1.0.0 + tags: + - 'v*' + # Trigger on pushes to main (typically PR merges) + branches: + - main + # Allow manual trigger + workflow_dispatch: + +permissions: + contents: read + packages: write + +# Cancel superseded builds so rapid pushes don't queue duplicates. +# Never cancel tag builds — each tag is a distinct release artifact. +concurrency: + group: build-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref_type != 'tag' }} + +jobs: + build-and-push: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + image_config: + # A2A runner used by the k8s Job in + # exgentic_a2a_runner/k8s/job.yaml. + - name: exgentic-a2a-runner + context: ./exgentic_a2a_runner + dockerfile: ./exgentic_a2a_runner/Dockerfile + + steps: + # 1. Checkout code + - name: Checkout repository + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + + # 2. Set up QEMU for multi-arch builds + - name: Set up QEMU + uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4 + + # 3. Set up Docker Buildx + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4 + + # 4. Log in to GitHub Container Registry + - name: Log in to ghcr.io + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # 5. Resolve the image tag: + # - on v* tag → the tag name (e.g. v0.0.1) + # - otherwise → - + - name: Generate image tag + id: tag + run: | + if [[ "${{ github.ref_type }}" == "tag" ]]; then + echo "tag=${{ github.ref_name }}" >> "$GITHUB_OUTPUT" + else + BRANCH="${{ github.ref_name }}" + BRANCH="${BRANCH//\//-}" + SHORT_SHA="$(echo "${{ github.sha }}" | cut -c1-7)" + echo "tag=${BRANCH}-${SHORT_SHA}" >> "$GITHUB_OUTPUT" + fi + + # 6. Extract Docker metadata. Apply `latest` on v* tag pushes, + # pushes to main, and manual dispatches. + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6 + with: + images: ghcr.io/${{ github.repository }}/${{ matrix.image_config.name }} + tags: | + type=raw,value=${{ steps.tag.outputs.tag }} + type=raw,value=latest,enable=${{ (github.ref_type == 'tag' && startsWith(github.ref_name, 'v')) || github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main' }} + + # 7. Build and push image. Note: no build-args here — the runner + # doesn't need any today, and specifying `build-args:` twice (as + # cortex's build.yaml does) silently drops matrix-level args + # because the second key wins. + - name: Build and push ${{ matrix.image_config.name }} + uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7 + with: + context: ${{ matrix.image_config.context }} + file: ${{ matrix.image_config.dockerfile }} + push: true + platforms: linux/amd64,linux/arm64 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/README.md b/README.md index cb8d29f..7a15e9a 100644 --- a/README.md +++ b/README.md @@ -906,15 +906,11 @@ The runner can execute entirely inside the cluster as a Kubernetes Job — no lo `k8s/job.yaml` is the launch template. It references secrets for credentials and passes benchmark/agent flags as container `args`. The job container uses cluster-internal DNS to reach the Kagenti API, Keycloak, MCP server, and agent — no port-forwarding is required. MLflow tracing switches automatically to HTTP/protobuf when `KUBERNETES_SERVICE_HOST` is set. -### Step 1 — Build and push the runner image +### Step 1 — Pull (or build) the runner image -```bash -cd exgentic_a2a_runner -docker build -t ghcr.io/exgentic/runner:latest . -docker push ghcr.io/exgentic/runner:latest -``` +Prebuilt multi-arch images are published to GHCR on every push to `main` and every `v*` tag — see [Container images](#container-images) below. `k8s/job.yaml` points at `:latest` (which tracks `main`) by default, so no build is required. -Replace `ghcr.io/exgentic/runner:latest` with your own registry path if needed, and update `k8s/job.yaml` → `image:` to match. +To build locally instead — for example, to test unreleased changes — see [Iterating with a locally-built image](#iterating-with-a-locally-built-image). ### Step 2 — Set up required secrets @@ -996,12 +992,40 @@ AGENT PROCESSING LATENCY To test a local image change without pushing to a registry, sync it into the kind cluster first: ```bash -export REMOTE_IMAGE_NAME=ghcr.io/exgentic/runner:dev +cd exgentic_a2a_runner +docker build -t ghcr.io/kagenti/workload-harness/exgentic-a2a-runner:dev . +export REMOTE_IMAGE_NAME=ghcr.io/kagenti/workload-harness/exgentic-a2a-runner:dev export KIND_CLUSTER_NAME=kagenti source ./sync-image-to-cluster.sh ``` -Then set `imagePullPolicy: IfNotPresent` in `k8s/job.yaml` and submit as above. +Update `k8s/job.yaml` → `image:` to match your local tag, set `imagePullPolicy: IfNotPresent`, and submit as above. + +## Container images + +The runner image is built and published by [`.github/workflows/build.yaml`](.github/workflows/build.yaml) on every push to `main`, every `v*` tag, and manual workflow dispatch. Images are multi-arch (`linux/amd64`, `linux/arm64`). + +| Image | Source | +|-------|--------| +| `ghcr.io/kagenti/workload-harness/exgentic-a2a-runner` | [`exgentic_a2a_runner/Dockerfile`](exgentic_a2a_runner/Dockerfile) | + +**Tags:** + +| Ref pushed | Tags produced | +|------------|---------------| +| `v*` tag (e.g. `v0.0.1`) | `v0.0.1`, `latest` | +| Push to `main` | `main-`, `latest` | +| Manual dispatch on any branch | `-`, `latest` | +| Push to any other branch | `-` | + +`latest` tracks `main` — pin to a `v*` or `main-` tag for reproducible deploys. + +**Build locally:** + +```bash +cd exgentic_a2a_runner +docker build -t ghcr.io/kagenti/workload-harness/exgentic-a2a-runner:dev . +``` ## E2E Test Script diff --git a/exgentic_a2a_runner/k8s/job.yaml b/exgentic_a2a_runner/k8s/job.yaml index 70438bd..504fb75 100644 --- a/exgentic_a2a_runner/k8s/job.yaml +++ b/exgentic_a2a_runner/k8s/job.yaml @@ -13,7 +13,9 @@ spec: # All cluster interaction goes through the Kagenti API and Keycloak over HTTP. containers: - name: runner - image: ghcr.io/exgentic/runner:latest + # Published by .github/workflows/build.yaml. `:latest` tracks + # main; use a `main-` or `v*` tag to pin a specific build. + image: ghcr.io/kagenti/workload-harness/exgentic-a2a-runner:latest imagePullPolicy: IfNotPresent args: - "--benchmark" From 14926e2ee6b17d668ce4a2016a95de8d416b5ab8 Mon Sep 17 00:00:00 2001 From: Kelly Abuelsaad Date: Tue, 21 Jul 2026 10:13:49 -0400 Subject: [PATCH 2/2] ci(build): retarget image path to rossoctl/workload-harness Origin was renamed from kagenti/workload-harness to rossoctl/workload-harness. github.repository in the workflow already resolves to the new owner, so this only fixes the hardcoded references in k8s/job.yaml and the README. Signed-off-by: Kelly Abuelsaad --- README.md | 8 ++++---- exgentic_a2a_runner/k8s/job.yaml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7a15e9a..ecc3075 100644 --- a/README.md +++ b/README.md @@ -993,8 +993,8 @@ To test a local image change without pushing to a registry, sync it into the kin ```bash cd exgentic_a2a_runner -docker build -t ghcr.io/kagenti/workload-harness/exgentic-a2a-runner:dev . -export REMOTE_IMAGE_NAME=ghcr.io/kagenti/workload-harness/exgentic-a2a-runner:dev +docker build -t ghcr.io/rossoctl/workload-harness/exgentic-a2a-runner:dev . +export REMOTE_IMAGE_NAME=ghcr.io/rossoctl/workload-harness/exgentic-a2a-runner:dev export KIND_CLUSTER_NAME=kagenti source ./sync-image-to-cluster.sh ``` @@ -1007,7 +1007,7 @@ The runner image is built and published by [`.github/workflows/build.yaml`](.git | Image | Source | |-------|--------| -| `ghcr.io/kagenti/workload-harness/exgentic-a2a-runner` | [`exgentic_a2a_runner/Dockerfile`](exgentic_a2a_runner/Dockerfile) | +| `ghcr.io/rossoctl/workload-harness/exgentic-a2a-runner` | [`exgentic_a2a_runner/Dockerfile`](exgentic_a2a_runner/Dockerfile) | **Tags:** @@ -1024,7 +1024,7 @@ The runner image is built and published by [`.github/workflows/build.yaml`](.git ```bash cd exgentic_a2a_runner -docker build -t ghcr.io/kagenti/workload-harness/exgentic-a2a-runner:dev . +docker build -t ghcr.io/rossoctl/workload-harness/exgentic-a2a-runner:dev . ``` ## E2E Test Script diff --git a/exgentic_a2a_runner/k8s/job.yaml b/exgentic_a2a_runner/k8s/job.yaml index 504fb75..6cbe085 100644 --- a/exgentic_a2a_runner/k8s/job.yaml +++ b/exgentic_a2a_runner/k8s/job.yaml @@ -15,7 +15,7 @@ spec: - name: runner # Published by .github/workflows/build.yaml. `:latest` tracks # main; use a `main-` or `v*` tag to pin a specific build. - image: ghcr.io/kagenti/workload-harness/exgentic-a2a-runner:latest + image: ghcr.io/rossoctl/workload-harness/exgentic-a2a-runner:latest imagePullPolicy: IfNotPresent args: - "--benchmark"