Skip to content

fix(runner): wire CP-fetched OIDC token into get_bot_token() for backend API calls #3551

fix(runner): wire CP-fetched OIDC token into get_bot_token() for backend API calls

fix(runner): wire CP-fetched OIDC token into get_bot_token() for backend API calls #3551

name: Build and Push Component Docker Images
on:
push:
branches: [main, alpha]
paths:
- '.github/workflows/components-build-deploy.yml'
- 'components/manifests/**'
- 'components/runners/**'
- 'components/operator/**'
- 'components/backend/**'
- 'components/frontend/**'
- 'components/public-api/**'
- 'components/ambient-api-server/**'
- 'components/ambient-control-plane/**'
- 'components/ambient-mcp/**'
pull_request:
branches: [main, alpha]
paths:
- '.github/workflows/components-build-deploy.yml'
- 'components/manifests/**'
- 'components/runners/**'
- 'components/operator/**'
- 'components/backend/**'
- 'components/frontend/**'
- 'components/public-api/**'
- 'components/ambient-api-server/**'
- 'components/ambient-control-plane/**'
- 'components/ambient-mcp/**'
workflow_dispatch:
inputs:
components:
description: 'Components to build (comma-separated: frontend,backend,operator,ambient-runner,state-sync,public-api,ambient-api-server,ambient-control-plane,ambient-mcp) - leave empty for all'
required: false
type: string
default: ''
concurrency:
group: components-build-deploy-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
build-matrix:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
permissions: {}
outputs:
build-matrix: ${{ steps.matrix.outputs.build-matrix }}
merge-matrix: ${{ steps.matrix.outputs.merge-matrix }}
steps:
- name: Build component matrices
id: matrix
run: |
ALL_COMPONENTS='[
{"name":"frontend","context":"./components/frontend","image":"quay.io/ambient_code/vteam_frontend","dockerfile":"./components/frontend/Dockerfile"},
{"name":"backend","context":"./components/backend","image":"quay.io/ambient_code/vteam_backend","dockerfile":"./components/backend/Dockerfile"},
{"name":"operator","context":"./components/operator","image":"quay.io/ambient_code/vteam_operator","dockerfile":"./components/operator/Dockerfile"},
{"name":"ambient-runner","context":"./components/runners","image":"quay.io/ambient_code/vteam_claude_runner","dockerfile":"./components/runners/ambient-runner/Dockerfile"},
{"name":"state-sync","context":"./components/runners/state-sync","image":"quay.io/ambient_code/vteam_state_sync","dockerfile":"./components/runners/state-sync/Dockerfile"},
{"name":"public-api","context":"./components/public-api","image":"quay.io/ambient_code/vteam_public_api","dockerfile":"./components/public-api/Dockerfile"},
{"name":"ambient-api-server","context":"./components/ambient-api-server","image":"quay.io/ambient_code/vteam_api_server","dockerfile":"./components/ambient-api-server/Dockerfile"},
{"name":"ambient-control-plane","context":"./components","image":"quay.io/ambient_code/vteam_control_plane","dockerfile":"./components/ambient-control-plane/Dockerfile"},
{"name":"ambient-mcp","context":"./components/ambient-mcp","image":"quay.io/ambient_code/vteam_mcp","dockerfile":"./components/ambient-mcp/Dockerfile"}
]'
SELECTED="${{ github.event.inputs.components }}"
if [ -n "$SELECTED" ]; then
FILTERED=$(echo "$ALL_COMPONENTS" | jq -c --arg sel "$SELECTED" '[.[] | select(.name as $n | $sel | split(",") | map(gsub("^\\s+|\\s+$";"")) | index($n))]')
else
FILTERED="$ALL_COMPONENTS"
fi
echo "build-matrix=$(echo "$FILTERED" | jq -c '.')" >> $GITHUB_OUTPUT
echo "merge-matrix=$(echo "$FILTERED" | jq -c '[.[] | {name, image}]')" >> $GITHUB_OUTPUT
echo "Components to build:"
echo "$FILTERED" | jq -r '.[].name'
build:
needs: build-matrix
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
strategy:
fail-fast: false
matrix:
# IMPORTANT: suffix values must match the hardcoded suffixes in
# merge-manifests. Update both together if arches change.
arch:
- runner: ubuntu-latest
platform: linux/amd64
suffix: amd64
- runner: ubuntu-24.04-arm
platform: linux/arm64
suffix: arm64
component: ${{ fromJSON(needs.build-matrix.outputs.build-matrix) }}
runs-on: ${{ matrix.arch.runner }}
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Quay.io
uses: docker/login-action@v3
with:
registry: quay.io
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_PASSWORD }}
- name: Log in to Red Hat Container Registry
uses: docker/login-action@v3
with:
registry: registry.redhat.io
username: ${{ secrets.REDHAT_USERNAME }}
password: ${{ secrets.REDHAT_PASSWORD }}
- name: Build and push ${{ matrix.component.name }} (${{ matrix.arch.suffix }})
if: github.event_name != 'pull_request'
uses: docker/build-push-action@v7
with:
context: ${{ matrix.component.context }}
file: ${{ matrix.component.dockerfile }}
platforms: ${{ matrix.arch.platform }}
push: true
tags: ${{ matrix.component.image }}:${{ github.sha }}-${{ matrix.arch.suffix }}
build-args: AMBIENT_VERSION=${{ github.sha }}
cache-from: type=gha,scope=${{ matrix.component.name }}-${{ matrix.arch.suffix }}
cache-to: type=gha,mode=max,scope=${{ matrix.component.name }}-${{ matrix.arch.suffix }}
- name: Build and push ${{ matrix.component.name }} (${{ matrix.arch.suffix }}) for pull request
if: github.event_name == 'pull_request'
uses: docker/build-push-action@v7
with:
context: ${{ matrix.component.context }}
file: ${{ matrix.component.dockerfile }}
platforms: ${{ matrix.arch.platform }}
push: true
tags: |
${{ matrix.component.image }}:pr-${{ github.event.pull_request.number }}-${{ matrix.arch.suffix }}
${{ matrix.component.image }}:pr-${{ github.event.pull_request.number }}-${{ github.sha }}-${{ matrix.arch.suffix }}
build-args: AMBIENT_VERSION=${{ github.sha }}
cache-from: type=gha,scope=${{ matrix.component.name }}-${{ matrix.arch.suffix }}
cache-to: type=gha,mode=max,scope=${{ matrix.component.name }}-${{ matrix.arch.suffix }}
merge-manifests:
needs: [build-matrix, build]
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
strategy:
fail-fast: false
matrix:
component: ${{ fromJSON(needs.build-matrix.outputs.merge-matrix) }}
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Quay.io
uses: docker/login-action@v3
with:
registry: quay.io
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_PASSWORD }}
- name: Create multi-arch manifest for ${{ matrix.component.name }} (main)
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
# Suffixes (-amd64, -arm64) must match the arch matrix in the build job above.
# Arch-suffixed tags remain in the registry after merging. Clean these up
# via Quay tag expiration policies or a periodic job.
run: |
docker buildx imagetools create \
-t ${{ matrix.component.image }}:latest \
-t ${{ matrix.component.image }}:${{ github.sha }} \
${{ matrix.component.image }}:${{ github.sha }}-amd64 \
${{ matrix.component.image }}:${{ github.sha }}-arm64
- name: Create multi-arch manifest for ${{ matrix.component.name }} (alpha)
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/alpha'
run: |
docker buildx imagetools create \
-t ${{ matrix.component.image }}:alpha \
-t ${{ matrix.component.image }}:${{ github.sha }} \
${{ matrix.component.image }}:${{ github.sha }}-amd64 \
${{ matrix.component.image }}:${{ github.sha }}-arm64
- name: Create multi-arch manifest for ${{ matrix.component.name }} (pull request)
if: github.event_name == 'pull_request'
run: |
docker buildx imagetools create \
-t ${{ matrix.component.image }}:pr-${{ github.event.pull_request.number }} \
${{ matrix.component.image }}:pr-${{ github.event.pull_request.number }}-amd64 \
${{ matrix.component.image }}:pr-${{ github.event.pull_request.number }}-arm64
update-rbac-and-crd:
runs-on: ubuntu-latest
needs: [build-matrix, merge-manifests]
if: always() && !cancelled() && (github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch')
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install oc
uses: redhat-actions/oc-installer@v1
with:
oc_version: 'latest'
- name: Log in to OpenShift Cluster
run: |
oc login ${{ secrets.OPENSHIFT_SERVER }} --token=${{ secrets.OPENSHIFT_TOKEN }} --insecure-skip-tls-verify
- name: Apply RBAC and CRD manifests
run: |
oc apply -k components/manifests/base/crds/
oc apply -k components/manifests/base/rbac/
oc apply -f components/manifests/overlays/production/operator-config-openshift.yaml -n ambient-code
- name: Deploy observability stack
run: |
oc apply -k components/manifests/observability/
deploy-to-openshift:
runs-on: ubuntu-latest
needs: [merge-manifests, update-rbac-and-crd]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install oc
uses: redhat-actions/oc-installer@v1
with:
oc_version: 'latest'
- name: Install kustomize
run: |
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
sudo mv kustomize /usr/local/bin/
kustomize version
- name: Log in to OpenShift Cluster
run: |
oc login ${{ secrets.OPENSHIFT_SERVER }} --token=${{ secrets.OPENSHIFT_TOKEN }} --insecure-skip-tls-verify
- name: Update kustomization with SHA image tags
working-directory: components/manifests/overlays/production
run: |
kustomize edit set image quay.io/ambient_code/vteam_frontend:latest=quay.io/ambient_code/vteam_frontend:${{ github.sha }}
kustomize edit set image quay.io/ambient_code/vteam_backend:latest=quay.io/ambient_code/vteam_backend:${{ github.sha }}
kustomize edit set image quay.io/ambient_code/vteam_operator:latest=quay.io/ambient_code/vteam_operator:${{ github.sha }}
kustomize edit set image quay.io/ambient_code/vteam_claude_runner:latest=quay.io/ambient_code/vteam_claude_runner:${{ github.sha }}
kustomize edit set image quay.io/ambient_code/vteam_state_sync:latest=quay.io/ambient_code/vteam_state_sync:${{ github.sha }}
kustomize edit set image quay.io/ambient_code/vteam_api_server:latest=quay.io/ambient_code/vteam_api_server:${{ github.sha }}
kustomize edit set image quay.io/ambient_code/vteam_control_plane:latest=quay.io/ambient_code/vteam_control_plane:${{ github.sha }}
kustomize edit set image quay.io/ambient_code/vteam_mcp:latest=quay.io/ambient_code/vteam_mcp:${{ github.sha }}
kustomize edit set image quay.io/ambient_code/vteam_public_api:latest=quay.io/ambient_code/vteam_public_api:${{ github.sha }}
- name: Validate kustomization
working-directory: components/manifests/overlays/production
run: |
kustomize build . > /dev/null
echo "✅ Kustomization validation passed"
- name: Apply production overlay with kustomize
working-directory: components/manifests/overlays/production
run: |
oc apply -k . -n ambient-code
- name: Update frontend environment variables
run: |
oc set env deployment/frontend -n ambient-code -c frontend \
GITHUB_APP_SLUG="ambient-code-stage" \
FEEDBACK_URL="https://forms.gle/7XiWrvo6No922DUz6"
- name: Update operator environment variables
run: |
oc set env deployment/agentic-operator -n ambient-code -c agentic-operator \
AMBIENT_CODE_RUNNER_IMAGE="quay.io/ambient_code/vteam_claude_runner:${{ github.sha }}" \
STATE_SYNC_IMAGE="quay.io/ambient_code/vteam_state_sync:${{ github.sha }}"
- name: Pin OPERATOR_IMAGE on backend for scheduled session triggers
run: |
oc set env deployment/agentic-backend -n ambient-code -c agentic-backend \
OPERATOR_IMAGE="quay.io/ambient_code/vteam_operator:${{ github.sha }}"
- name: Update agent registry ConfigMap with pinned image tags
run: |
REGISTRY=$(oc get configmap ambient-agent-registry -n ambient-code \
-o jsonpath='{.data.agent-registry\.json}')
REGISTRY=$(echo "$REGISTRY" | sed \
"s|quay.io/ambient_code/vteam_claude_runner[@:][^\"]*|quay.io/ambient_code/vteam_claude_runner:${{ github.sha }}|g")
REGISTRY=$(echo "$REGISTRY" | sed \
"s|quay.io/ambient_code/vteam_state_sync[@:][^\"]*|quay.io/ambient_code/vteam_state_sync:${{ github.sha }}|g")
oc patch configmap ambient-agent-registry -n ambient-code --type=merge \
-p "{\"data\":{\"agent-registry.json\":$(echo "$REGISTRY" | jq -Rs .)}}"
deploy-with-disptach:
runs-on: ubuntu-latest
needs: [merge-manifests, update-rbac-and-crd]
if: github.event_name == 'workflow_dispatch'
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install oc
uses: redhat-actions/oc-installer@v1
with:
oc_version: 'latest'
- name: Install kustomize
run: |
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
sudo mv kustomize /usr/local/bin/
kustomize version
- name: Log in to OpenShift Cluster
run: |
oc login ${{ secrets.OPENSHIFT_SERVER }} --token=${{ secrets.OPENSHIFT_TOKEN }} --insecure-skip-tls-verify
- name: Update kustomization with SHA image tags
working-directory: components/manifests/overlays/production
run: |
kustomize edit set image quay.io/ambient_code/vteam_frontend:latest=quay.io/ambient_code/vteam_frontend:${{ github.sha }}
kustomize edit set image quay.io/ambient_code/vteam_backend:latest=quay.io/ambient_code/vteam_backend:${{ github.sha }}
kustomize edit set image quay.io/ambient_code/vteam_operator:latest=quay.io/ambient_code/vteam_operator:${{ github.sha }}
kustomize edit set image quay.io/ambient_code/vteam_claude_runner:latest=quay.io/ambient_code/vteam_claude_runner:${{ github.sha }}
kustomize edit set image quay.io/ambient_code/vteam_state_sync:latest=quay.io/ambient_code/vteam_state_sync:${{ github.sha }}
kustomize edit set image quay.io/ambient_code/vteam_api_server:latest=quay.io/ambient_code/vteam_api_server:${{ github.sha }}
kustomize edit set image quay.io/ambient_code/vteam_control_plane:latest=quay.io/ambient_code/vteam_control_plane:${{ github.sha }}
kustomize edit set image quay.io/ambient_code/vteam_mcp:latest=quay.io/ambient_code/vteam_mcp:${{ github.sha }}
kustomize edit set image quay.io/ambient_code/vteam_public_api:latest=quay.io/ambient_code/vteam_public_api:${{ github.sha }}
- name: Validate kustomization
working-directory: components/manifests/overlays/production
run: |
kustomize build . > /dev/null
echo "✅ Kustomization validation passed"
- name: Apply production overlay with kustomize
working-directory: components/manifests/overlays/production
run: |
oc apply -k . -n ambient-code
- name: Update frontend environment variables
run: |
oc set env deployment/frontend -n ambient-code -c frontend \
GITHUB_APP_SLUG="ambient-code-stage" \
FEEDBACK_URL="https://forms.gle/7XiWrvo6No922DUz6"
- name: Update operator environment variables
run: |
oc set env deployment/agentic-operator -n ambient-code -c agentic-operator \
AMBIENT_CODE_RUNNER_IMAGE="quay.io/ambient_code/vteam_claude_runner:${{ github.sha }}" \
STATE_SYNC_IMAGE="quay.io/ambient_code/vteam_state_sync:${{ github.sha }}"
- name: Pin OPERATOR_IMAGE on backend for scheduled session triggers
run: |
oc set env deployment/agentic-backend -n ambient-code -c agentic-backend \
OPERATOR_IMAGE="quay.io/ambient_code/vteam_operator:${{ github.sha }}"
- name: Update agent registry ConfigMap with pinned image tags
run: |
REGISTRY=$(oc get configmap ambient-agent-registry -n ambient-code \
-o jsonpath='{.data.agent-registry\.json}')
REGISTRY=$(echo "$REGISTRY" | sed \
"s|quay.io/ambient_code/vteam_claude_runner[@:][^\"]*|quay.io/ambient_code/vteam_claude_runner:${{ github.sha }}|g")
REGISTRY=$(echo "$REGISTRY" | sed \
"s|quay.io/ambient_code/vteam_state_sync[@:][^\"]*|quay.io/ambient_code/vteam_state_sync:${{ github.sha }}|g")
oc patch configmap ambient-agent-registry -n ambient-code --type=merge \
-p "{\"data\":{\"agent-registry.json\":$(echo "$REGISTRY" | jq -Rs .)}}"