Sandbox chart was un-deployable end-to-end because three CI-side gaps compounded after PR #1658 wired the mcp-server module to depend on core/controllers + core/services/shared via `replace` directives: 1. **mcp-server Dockerfile built against a too-narrow context**. The workflow passed `context: products/sandbox/mcp-server` and the Dockerfile assumed `COPY . .` could see everything it needed, but the `replace ../../../core/controllers` line in the module's go.mod only resolves when the build can actually reach those paths. Result: every push after #1658 failed at `go build` with `module not found`. Fix mirrors core/controllers/sandbox/Dockerfile (Slice-CC1 layout): COPY the replace targets' module roots + sources, then build with WORKDIR set to the dependent module. Static binary still produced into a distroless/static-debian12:nonroot final stage. 2. **mcp-server workflow had no chart auto-bump step**. Even after a green build, `runtime.mcpImage` in platform/sandbox/chart/values.yaml stayed empty so the chart's `required` guard (deployment.yaml line 72) refused to render. Added the same yq-bump + bot-commit pattern build-sandbox-controller.yaml already uses, targeting `.runtime.mcpImage` and writing a fully-qualified `<repo>:<sha>` string (consumer reads it as one image reference, not a {repository,tag} pair). Also widened paths-filter to include core/controllers/** + core/services/shared/** so changes to the replace targets re-trigger the build. 3. **pty-server workflow had no auto-bump either**. Same surgery: yq-bump `.runtime.ptyServerImage` + commit-and-push. Context stays narrow (pty-server has no cross-tree `replace` directives). 4. **Stop-gap pin values for runtime.{ptyServerImage,mcpImage}** so the next chart roll out doesn't fail-fast before the rebuilt workflows land their first bumps: - ptyServerImage →ad5163e6(current latest pty-server) - mcpImage →1b0e86c(last pre-#1658 green build; the rebuilt workflow will land the next real SHA on the next push to main). Verified locally: - `go build ./products/sandbox/mcp-server/...` clean (43.8 MB static binary at /tmp/openova-sandbox-mcp; `file` confirms statically linked ELF). - `helm template test platform/sandbox/chart --set enabled=true …` renders cleanly; both env vars carry the SHA-pinned image refs. No Chart.yaml bump. Read-only clusters. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
185 lines
7.3 KiB
YAML
185 lines
7.3 KiB
YAML
name: Build sandbox-pty-server
|
|
|
|
# sandbox-pty-server — Wave 2 of the Sandbox product (PR #1618). The
|
|
# in-pod HTTP+WS PTY shim (port 7681) that Wave 2's pty-server
|
|
# StatefulSet runs alongside the agent process. See
|
|
# products/sandbox/docs/architecture.md §2.
|
|
#
|
|
# Per docs/INVIOLABLE-PRINCIPLES.md #4a (GitHub Actions is the only
|
|
# build path) every image that runs on OpenOva infra MUST be produced
|
|
# by a CI workflow from a committed git SHA. Shape mirrors
|
|
# build-sandbox-controller.yaml — same Buildx + cosign keyless sign +
|
|
# SBOM attestation + auto-bump of the chart values.yaml so the next
|
|
# Sovereign install picks up the SHA-pinned image without an operator
|
|
# manually editing the file.
|
|
#
|
|
# Per `feedback_inviolable_principles.md`: event-driven only, NO cron.
|
|
|
|
on:
|
|
push:
|
|
paths:
|
|
- 'products/sandbox/pty-server/**'
|
|
- '.github/workflows/build-sandbox-pty-server.yaml'
|
|
branches: [main]
|
|
pull_request:
|
|
paths:
|
|
- 'products/sandbox/pty-server/**'
|
|
- '.github/workflows/build-sandbox-pty-server.yaml'
|
|
workflow_dispatch:
|
|
|
|
env:
|
|
REGISTRY: ghcr.io
|
|
IMAGE: ghcr.io/openova-io/openova/sandbox-pty-server
|
|
CHART_VALUES: platform/sandbox/chart/values.yaml
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
# contents: write — the deploy step below pushes a values.yaml SHA
|
|
# bump back to main so the platform/sandbox chart picks up the
|
|
# newly-built image without an operator manually editing the file
|
|
# (per `feedback_no_mvp_no_workarounds.md` rule 1: target-state,
|
|
# never "manual follow-up bump").
|
|
contents: write
|
|
packages: write
|
|
# id-token write is required by cosign keyless signing (Sigstore).
|
|
id-token: write
|
|
outputs:
|
|
sha_short: ${{ steps.vars.outputs.sha_short }}
|
|
digest: ${{ steps.build.outputs.digest }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Set short SHA
|
|
id: vars
|
|
run: echo "sha_short=$(echo $GITHUB_SHA | head -c 7)" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Set up Go
|
|
uses: actions/setup-go@v5
|
|
with:
|
|
go-version: '1.22'
|
|
cache-dependency-path: |
|
|
products/sandbox/pty-server/go.sum
|
|
|
|
- name: go vet
|
|
working-directory: products/sandbox/pty-server
|
|
run: go vet ./...
|
|
|
|
- name: Run unit tests
|
|
working-directory: products/sandbox/pty-server
|
|
# Empty `go test ./...` is harmless: prints "no test files" and
|
|
# exits 0. Wave-2 follow-ups will add unit tests under
|
|
# internal/session/.
|
|
run: go test -count=1 -race ./...
|
|
|
|
# On pull_request runs we stop here — image push requires
|
|
# `packages: write` which only main-branch authors hold.
|
|
- name: Login to GHCR
|
|
if: github.event_name != 'pull_request'
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: ${{ env.REGISTRY }}
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Set up Docker Buildx
|
|
if: github.event_name != 'pull_request'
|
|
uses: docker/setup-buildx-action@v3
|
|
|
|
- name: Build and push image
|
|
id: build
|
|
if: github.event_name != 'pull_request'
|
|
uses: docker/build-push-action@v6
|
|
with:
|
|
# pty-server's Dockerfile uses `COPY . .` so the build context
|
|
# is the pty-server directory itself (its own go.mod root —
|
|
# NOT the repo root, unlike core/controllers which share a
|
|
# parent go.mod). pty-server has no cross-tree `replace`
|
|
# directives so a narrow context still resolves cleanly.
|
|
context: products/sandbox/pty-server
|
|
file: products/sandbox/pty-server/Dockerfile
|
|
push: true
|
|
tags: |
|
|
${{ env.IMAGE }}:${{ steps.vars.outputs.sha_short }}
|
|
${{ env.IMAGE }}:latest
|
|
labels: |
|
|
org.opencontainers.image.source=https://github.com/openova-io/openova
|
|
org.opencontainers.image.revision=${{ github.sha }}
|
|
org.opencontainers.image.title=sandbox-pty-server
|
|
org.opencontainers.image.description=In-pod HTTP+WS PTY shim (Wave 2 of Sandbox product, #1618)
|
|
# provenance=false: containerd 1.7.x on k3s mis-resolves the
|
|
# provenance attestation manifest. SBOM attestation handled
|
|
# by the cosign attest step below.
|
|
provenance: false
|
|
sbom: false
|
|
|
|
- name: Install cosign
|
|
if: github.event_name != 'pull_request'
|
|
uses: sigstore/cosign-installer@v3
|
|
|
|
- name: Sign image with cosign (keyless)
|
|
if: github.event_name != 'pull_request'
|
|
env:
|
|
DIGEST: ${{ steps.build.outputs.digest }}
|
|
run: |
|
|
cosign sign --yes "${IMAGE}@${DIGEST}"
|
|
|
|
- name: Generate and attest SBOM
|
|
if: github.event_name != 'pull_request'
|
|
env:
|
|
DIGEST: ${{ steps.build.outputs.digest }}
|
|
run: |
|
|
cosign attest --yes \
|
|
--predicate <(echo '{"sbom":"in-toto-spdx attached at build time"}') \
|
|
--type spdx \
|
|
"${IMAGE}@${DIGEST}"
|
|
|
|
# Auto-bump the chart values.yaml runtime.ptyServerImage so the
|
|
# next Sovereign chart rollout picks up this image without a
|
|
# manual edit. Per `feedback_no_mvp_no_workarounds.md` rule 1
|
|
# (target-state, no operator-action gates) and
|
|
# `feedback_inviolable_principles.md` (event-driven, never cron).
|
|
# The chart's deployment.yaml `required` guard fails-fast when
|
|
# runtime.ptyServerImage is empty (Inviolable Principle #4a), so
|
|
# an un-bumped values.yaml = un-deployable chart. Mirrors the
|
|
# build-sandbox-controller.yaml auto-bump shape, just targeting a
|
|
# different yq path and writing a fully-qualified `<repo>:<sha>`
|
|
# string (the consumer reads runtime.ptyServerImage as a single
|
|
# image reference, not a {repository,tag} pair).
|
|
- name: Install yq
|
|
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
|
|
run: |
|
|
sudo wget -qO /usr/local/bin/yq \
|
|
https://github.com/mikefarah/yq/releases/download/v4.44.3/yq_linux_amd64
|
|
sudo chmod +x /usr/local/bin/yq
|
|
|
|
- name: Bump runtime.ptyServerImage in values.yaml
|
|
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
|
|
env:
|
|
SHA_SHORT: ${{ steps.vars.outputs.sha_short }}
|
|
run: |
|
|
set -euo pipefail
|
|
yq eval -i ".runtime.ptyServerImage = \"${IMAGE}:${SHA_SHORT}\"" "${CHART_VALUES}"
|
|
echo "values.yaml after bump:"
|
|
yq eval '.runtime.ptyServerImage' "${CHART_VALUES}"
|
|
|
|
- name: Commit and push values.yaml bump
|
|
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
|
|
env:
|
|
SHA_SHORT: ${{ steps.vars.outputs.sha_short }}
|
|
run: |
|
|
set -euo pipefail
|
|
git config user.name "github-actions[bot]"
|
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
if git diff --quiet "${CHART_VALUES}"; then
|
|
echo "no values.yaml change — already pinned to ${SHA_SHORT}"
|
|
exit 0
|
|
fi
|
|
git add "${CHART_VALUES}"
|
|
git commit -m "deploy: bump sandbox-pty-server image to ${SHA_SHORT}"
|
|
# Pull-rebase to avoid races with parallel build commits.
|
|
git pull --rebase --autostash origin main || true
|
|
git push origin HEAD:main
|