ci: publish the docsgpt-sandbox image

deployment/k8s/deployments/sandbox-deploy.yaml pulls arc53/docsgpt-sandbox,
which has never been pushed anywhere: Compose builds the runner from the
checkout (`build: ./sandbox`), but Kubernetes cannot build, so enabling code
execution on a cluster failed on an image that does not exist.

Build and push it like the other two images: `develop` on a push to main that
touches deployment/sandbox, and `<version>` plus `latest` when the release
workflow calls it. Release and develop live in one file here rather than two,
because the runner changes rarely and the only difference is which tags move.
The tag comes from the inputs and the release payload, not from
`github.event_name`, which is `push` when backend-release calls this.
This commit is contained in:
Alex committed 2026-09-12 22:06:27 +01:00
1 parent 01dfe473d3
commit fc5992c5d4
3 files changed
+189 -2

No files matched your search

+15 -2
View File
@@ -1,8 +1,8 @@
name: Backend release
# A version bump on main tags the commit, creates the GitHub release, then
# publishes the backend and frontend Docker images and the PyPI package by
# calling those workflows.
# publishes the backend, frontend and sandbox Docker images and the PyPI
# package by calling those workflows.
# The release is created with GITHUB_TOKEN, and GitHub never starts workflows
# from events that token produces, so the `release: published` triggers on the
# publish workflows would not fire (0.18.0 got no images that way). Releases
@@ -123,6 +123,19 @@ jobs:
contents: read
packages: write
sandbox:
needs: release
if: needs.release.outputs.created == 'true'
uses: $/.github/workflows/sandbox-image.yml
with:
version: ${{ needs.release.outputs.version }}
secrets:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
permissions:
contents: read
packages: write
pypi:
needs: release
if: needs.release.outputs.created == 'true'
+168
View File
@@ -0,0 +1,168 @@
name: Build and push the docsgpt-sandbox image
# The opt-in code-execution runner (deployment/sandbox). Compose builds it from
# the checkout, but Kubernetes cannot build, so the manifest under
# deployment/k8s/deployments/sandbox-deploy.yaml needs a published image.
#
# Three ways in: a push to main that touches the runner (tagged `develop`), a
# release created by hand (the release event), or a call from the backend-release
# workflow with the version it just tagged — GitHub never starts workflows from
# events GITHUB_TOKEN produces, so a bot-created release does not fire the
# release trigger on its own. `github.event_name` is the event that started the
# whole run, which is `push` when backend-release calls this, so the tag comes
# from the inputs and the release payload instead.
on:
release:
types: [published]
workflow_call:
inputs:
version:
description: Release tag to build and push (the images are tagged with it)
type: string
required: true
secrets:
DOCKER_USERNAME:
required: true
DOCKER_PASSWORD:
required: true
push:
branches: [main]
paths:
- 'deployment/sandbox/**'
- '.github/workflows/sandbox-image.yml'
permissions:
contents: read
env:
# The version being published, or `develop` for a push to main.
RELEASE_TAG: ${{ inputs.version || github.event.release.tag_name || 'develop' }}
# A release also moves `latest`; a push to main moves nothing but `develop`.
MOVING_TAG: ${{ (inputs.version || github.event.release.tag_name) && 'latest' || '' }}
jobs:
build:
if: github.repository == 'arc53/DocsGPT'
# Publishing jobs run in a GitHub Actions environment so the registry
# credentials can be scoped to it and protection rules (required reviewers,
# branch restrictions) applied in the repository settings.
environment: docker-hub
env:
# Public namespace the compose files and manifests pull from; the login
# secret only authenticates the push.
DOCKERHUB_NAMESPACE: arc53
strategy:
matrix:
include:
- platform: linux/amd64
runner: ubuntu-latest
suffix: amd64
- platform: linux/arm64
runner: ubuntu-24.04-arm
suffix: arm64
runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
persist-credentials: false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
with:
driver: docker-container
install: true
- name: Login to DockerHub
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to ghcr.io
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Image metadata (OCI labels)
id: meta
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
with:
images: |
${{ env.DOCKERHUB_NAMESPACE }}/docsgpt-sandbox
ghcr.io/${{ github.repository_owner }}/docsgpt-sandbox
labels: |
org.opencontainers.image.title=DocsGPT sandbox runner
org.opencontainers.image.version=${{ env.RELEASE_TAG }}
- name: Build and push platform-specific images
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
with:
file: './deployment/sandbox/Dockerfile'
platforms: ${{ matrix.platform }}
context: ./deployment/sandbox
push: true
tags: |
${{ env.DOCKERHUB_NAMESPACE }}/docsgpt-sandbox:${{ env.RELEASE_TAG }}-${{ matrix.suffix }}
ghcr.io/${{ github.repository_owner }}/docsgpt-sandbox:${{ env.RELEASE_TAG }}-${{ matrix.suffix }}
labels: ${{ steps.meta.outputs.labels }}
provenance: false
sbom: false
cache-from: type=registry,ref=${{ env.DOCKERHUB_NAMESPACE }}/docsgpt-sandbox:develop
cache-to: type=inline
manifest:
if: github.repository == 'arc53/DocsGPT'
# Publishing jobs run in a GitHub Actions environment so the registry
# credentials can be scoped to it and protection rules (required reviewers,
# branch restrictions) applied in the repository settings.
environment: docker-hub
env:
# Public namespace the compose files and manifests pull from; the login
# secret only authenticates the push.
DOCKERHUB_NAMESPACE: arc53
needs: build
runs-on: ubuntu-latest
permissions:
packages: write
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
with:
driver: docker-container
install: true
- name: Login to DockerHub
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to ghcr.io
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create and push multi-arch manifests
env:
TAG: ${{ env.RELEASE_TAG }}
MOVING: ${{ env.MOVING_TAG }}
run: |
set -e
# $MOVING is deliberately unquoted: it is empty for a push to main,
# and an empty word would create a manifest named "$repo:".
for repo in "$DOCKERHUB_NAMESPACE/docsgpt-sandbox" "ghcr.io/${{ github.repository_owner }}/docsgpt-sandbox"; do
for name in "$TAG" $MOVING; do
docker manifest create "$repo:$name" \
--amend "$repo:$TAG-amd64" \
--amend "$repo:$TAG-arm64"
docker manifest push "$repo:$name"
done
done
@@ -21,6 +21,12 @@
# sibling kernels or bypass the session cap). The gateway fails closed if the
# token is unset.
#
# The image is built from deployment/sandbox and published as
# arc53/docsgpt-sandbox (also ghcr.io/arc53/docsgpt-sandbox) by the release
# workflow, with `develop` tracking main. It is pulled here by the floating
# `latest` tag: pin it to a release tag if you would rather not pick up a new
# runner runtime on a pod restart.
#
# On Linux prod, schedule this onto a gVisor `runsc` RuntimeClass for kernel
# isolation (uncomment `runtimeClassName` once the node has it installed).
#