Skip to content

build(trustroot): resign config under base domains and pin tip digests #15

build(trustroot): resign config under base domains and pin tip digests

build(trustroot): resign config under base domains and pin tip digests #15

Workflow file for this run

name: images
# Build service images, push by commit SHA + branch tag, record digests as an artifact.
# Digests-only pins are consumed by deploy/scripts/promote.sh.
# Registry: GHCR only (never AWS ECR).
on:
push:
branches: [dev]
paths:
- "crates/**"
- "bins/**"
- "deploy/Dockerfile"
- "deploy/attest-helper/**"
- "Cargo.toml"
- "Cargo.lock"
- ".github/workflows/images.yml"
workflow_dispatch:
permissions:
contents: read
packages: write
env:
REGISTRY: ghcr.io
jobs:
build-push-digests:
name: build · push · record digests
runs-on: ubuntu-latest
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
include:
- target: validator
image_suffix: validator
dockerfile: deploy/Dockerfile
- target: gateway
image_suffix: gateway
dockerfile: deploy/Dockerfile
- target: updater
image_suffix: updater
dockerfile: deploy/Dockerfile
- target: agent-challenge
image_suffix: agent-challenge
dockerfile: deploy/Dockerfile
- target: base-agent
image_suffix: base-agent
dockerfile: deploy/Dockerfile
- target: ""
image_suffix: base-attest-helper
dockerfile: deploy/attest-helper/Dockerfile
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Lowercase GHCR image prefix
run: |
set -euo pipefail
# GHCR rejects mixed-case repository paths (e.g. BaseIntelligence).
owner_repo="${GITHUB_REPOSITORY,,}"
echo "IMAGE_PREFIX=ghcr.io/${owner_repo}" >> "$GITHUB_ENV"
echo "IMAGE_PREFIX=ghcr.io/${owner_repo}"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in 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: Build and push
id: build
uses: docker/build-push-action@v6
with:
context: ${{ matrix.image_suffix == 'base-attest-helper' && 'deploy/attest-helper' || '.' }}
file: ${{ matrix.dockerfile }}
target: ${{ matrix.target }}
push: ${{ github.event_name != 'pull_request' }}
tags: |
${{ env.IMAGE_PREFIX }}/${{ matrix.image_suffix }}:${{ github.sha }}
${{ env.IMAGE_PREFIX }}/${{ matrix.image_suffix }}:${{ github.ref_name }}
build-args: |
BUILD_FROM=source
provenance: false
- name: Write digest fragment
run: |
set -euo pipefail
mkdir -p digests-out
DIGEST="${{ steps.build.outputs.digest }}"
test -n "$DIGEST"
test "${DIGEST#sha256:}" != "$DIGEST"
REPO="${{ env.IMAGE_PREFIX }}/${{ matrix.image_suffix }}"
SVC="${{ matrix.image_suffix }}"
cat > "digests-out/${SVC}.json" <<EOF
{
"service": "${SVC}",
"repository": "${REPO}",
"tag_sha": "${{ github.sha }}",
"digest": "${DIGEST}",
"image": "${REPO}@${DIGEST}",
"commit_sha": "${{ github.sha }}"
}
EOF
cat "digests-out/${SVC}.json"
- name: Upload digest fragment
uses: actions/upload-artifact@v4
with:
name: digest-${{ matrix.image_suffix }}
path: digests-out/${{ matrix.image_suffix }}.json
if-no-files-found: error
merge-digests:
name: merge digest manifest
needs: build-push-digests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download fragments
uses: actions/download-artifact@v4
with:
pattern: digest-*
path: digests-in
merge-multiple: true
- name: Merge + validate
run: |
set -euo pipefail
python3 - <<'PY'
import json, pathlib, os
from datetime import datetime, timezone
root = pathlib.Path("digests-in")
images = {}
for p in sorted(root.glob("*.json")):
d = json.loads(p.read_text())
assert d["digest"].startswith("sha256:") and len(d["digest"]) == 71
assert "@sha256:" in d["image"]
images[d["service"]] = d
expected = {
"validator",
"gateway",
"updater",
"agent-challenge",
"base-agent",
"base-attest-helper",
}
assert set(images) == expected, (set(images), expected)
out = {
"commit_sha": os.environ.get("GITHUB_SHA", ""),
"created_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
"registry": "ghcr.io",
"images": images,
}
pathlib.Path("deploy/digests").mkdir(parents=True, exist_ok=True)
path = pathlib.Path("deploy/digests") / f"{out['commit_sha']}.json"
path.write_text(json.dumps(out, indent=2, sort_keys=True) + "\n")
print(path.read_text())
PY
- name: Upload merged digests
uses: actions/upload-artifact@v4
with:
name: base-image-digests-${{ github.sha }}
path: deploy/digests/${{ github.sha }}.json
if-no-files-found: error