Skip to content

ci: add batch image validation and publishing #1

ci: add batch image validation and publishing

ci: add batch image validation and publishing #1

name: Batch service image
on:
pull_request:
paths:
- "batch-service/**"
- ".github/workflows/batch-service-image.yaml"
push:
branches:
- main
- dev
paths:
- "batch-service/**"
- ".github/workflows/batch-service-image.yaml"
concurrency:
group: batch-service-image-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
env:
IMAGE_NAME: ghcr.io/phoenixvc/sluice-batch-service
TEST_IMAGE: sluice-batch-service:contract-test
jobs:
validate:
name: Test, build, and smoke image
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"
cache: pip
cache-dependency-path: batch-service/requirements-test.txt
- name: Install test dependencies
working-directory: batch-service
run: python -m pip install --requirement requirements-test.txt
- name: Run mocked contract tests
working-directory: batch-service
env:
BATCH_JOB_ID_SIGNING_KEY: contract-tests-only
run: python -m pytest -q
- name: Build image
run: docker build --tag "${TEST_IMAGE}" batch-service
- name: Verify non-root runtime
shell: bash
run: |
runtime_uid="$(docker run --rm "${TEST_IMAGE}" id -u)"
if [[ "${runtime_uid}" == "0" ]]; then
echo "::error::Batch service image runs as root"
exit 1
fi
echo "Runtime UID: ${runtime_uid}"
- name: Start image
run: |
docker run \
--detach \
--name sluice-batch-service-smoke \
--env BATCH_JOB_ID_SIGNING_KEY=local-image-smoke-only \
--publish 127.0.0.1:8080:8080 \
"${TEST_IMAGE}"
- name: Verify signed-job health
shell: bash
run: |
for attempt in {1..30}; do
if response="$(curl --fail --silent http://127.0.0.1:8080/healthz)"; then
if python -c \
'import json,sys; data=json.load(sys.stdin); sys.exit(0 if data == {"status":"ok","job_id_signing_configured":True} else 1)' \
<<< "${response}"; then
echo "Batch service health check passed"
exit 0
fi
fi
sleep 1
done
docker logs sluice-batch-service-smoke
echo "::error::Batch service did not report signing configured"
exit 1
- name: Stop image
if: always()
run: docker rm --force sluice-batch-service-smoke || true
publish:
name: Publish immutable GHCR image
if: github.event_name == 'push'
needs: validate
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
packages: write
attestations: write
id-token: write
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract OCI metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.IMAGE_NAME }}
tags: |
type=raw,value=${{ github.sha }}
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
- name: Build and publish
id: build
uses: docker/build-push-action@v6
with:
context: batch-service
file: batch-service/Dockerfile
platforms: linux/amd64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
provenance: mode=max
sbom: true
cache-from: type=gha,scope=batch-service
cache-to: type=gha,mode=max,scope=batch-service
- name: Write image summary
shell: bash
run: |
{
echo "## Batch service image published"
echo ""
echo "| Reference | Value |"
echo "| --- | --- |"
echo "| SHA tag | \`${IMAGE_NAME}:${GITHUB_SHA}\` |"
echo "| Digest pin | \`${IMAGE_NAME}@${{ steps.build.outputs.digest }}\` |"
} >> "${GITHUB_STEP_SUMMARY}"