Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 184 additions & 0 deletions .github/workflows/rigel-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# Upstream docker.yml requires Blacksmith runners and publishes to Docker Hub.
# This workflow publishes the fork on GitHub-hosted runners.
name: Rigel image

on:
push:
tags: ['v*-rigel.*']
workflow_dispatch:
inputs:
ref:
description: Rigel release tag to publish
required: true
type: string

permissions:
contents: read

concurrency:
group: rigel-image-${{ inputs.ref || github.ref }}
cancel-in-progress: false

env:
IMAGE: ghcr.io/rigelbuild/hermes-agent

jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runner: ubuntu-24.04
- arch: arm64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
timeout-minutes: 60
permissions:
contents: read
packages: write
outputs:
sha: ${{ steps.commit.outputs.sha }}
steps:
- name: Validate tag
id: tag
env:
REF: ${{ inputs.ref || github.ref_name }}
run: |
if [[ ! "$REF" =~ ^v[0-9][^/]*-rigel\.[^/]+$ || ! "$REF" =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$ ]]; then
echo "::error::Ref must be a Rigel release tag"
exit 1
fi
echo "ref=$REF" >> "$GITHUB_OUTPUT"
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: refs/tags/${{ steps.tag.outputs.ref }}
fetch-depth: 0
- name: Resolve commit
id: commit
env:
REF: ${{ steps.tag.outputs.ref }}
run: |
sha="$(git rev-parse "refs/tags/$REF^{commit}")"
test "$sha" = "$(git rev-parse HEAD)"
echo "sha=$sha" >> "$GITHUB_OUTPUT"
echo "epoch=$(git log -1 --format=%ct "$sha")" >> "$GITHUB_OUTPUT"
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.14'
- name: Write install stamp
run: |
display="$(python3 -m scripts.releases.distance)"
identity=(--commit "$(git rev-parse HEAD)")
if [ -n "$display" ]; then
base="${display%%+*}"
development="${display#*+}"
if [ "$development" = "$display" ]; then distance=0; else distance="${development%%.*}"; fi
identity+=(--base-version "$base" --display-version "$display" --distance "$distance")
fi
python3 scripts/write_install_stamp.py --output install-stamp.json \
--distribution docker --update-mechanism external --source ci "${identity[@]}"
- name: Reject profile exports in build context
run: python3 scripts/ci/check_profile_archive_boundary.py
- uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
- uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push image by digest
id: push
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
with:
context: .
platforms: linux/${{ matrix.arch }}
outputs: type=image,name=ghcr.io/rigelbuild/hermes-agent,push-by-digest=true,name-canonical=true,push=true
labels: |
org.opencontainers.image.source=https://github.com/RigelBuild/hermes-agent
org.opencontainers.image.revision=${{ steps.commit.outputs.sha }}
build-args: SOURCE_DATE_EPOCH=${{ steps.commit.outputs.epoch }}
cache-from: type=gha,scope=rigel-image-${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=rigel-image-${{ matrix.arch }}
- name: Pull image for tests
env:
DIGEST: ${{ steps.push.outputs.digest }}
run: docker pull "$IMAGE@$DIGEST"
- name: Park install stamp
run: mv install-stamp.json "$RUNNER_TEMP/install-stamp.json"
- name: Set up test dependencies
uses: ./.github/actions/setup-pm
with:
extras: '[]'
test-environment: 'true'
prune-python-cache: true
- name: Restore install stamp
run: mv "$RUNNER_TEMP/install-stamp.json" install-stamp.json
- name: Run docker integration tests
env:
HERMES_TEST_IMAGE: ${{ env.IMAGE }}@${{ steps.push.outputs.digest }}
OPENROUTER_API_KEY: ''
OPENAI_API_KEY: ''
NOUS_API_KEY: ''
run: HERMES_TEST_WORKERS=$(nproc) scripts/run_tests.sh tests/docker/
- name: Save digest
env:
DIGEST: ${{ steps.push.outputs.digest }}
run: |
mkdir -p "$RUNNER_TEMP/digests"
touch "$RUNNER_TEMP/digests/${DIGEST#sha256:}"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: digest-${{ matrix.arch }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error

manifest:
needs: build
runs-on: ubuntu-24.04
permissions:
contents: write
packages: write
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: ${{ runner.temp }}/digests
pattern: digest-*
merge-multiple: true
- uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
- uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Publish manifest
id: manifest
env:
REF: ${{ inputs.ref || github.ref_name }}
SHA: ${{ needs.build.outputs.sha }}
run: |
set -euo pipefail
ref="${REF#refs/tags/}"
images=()
for digest in "$RUNNER_TEMP"/digests/*; do
images+=("$IMAGE@sha256:${digest##*/}")
done
docker buildx imagetools create -t "$IMAGE:$ref" -t "$IMAGE:sha-$SHA" "${images[@]}"
digest="$(docker buildx imagetools inspect "$IMAGE:sha-$SHA" --format '{{json .Manifest.Digest}}' | tr -d '"')"
printf '%s@%s\n' "$IMAGE" "$digest" | tee image-digest.txt >> "$GITHUB_STEP_SUMMARY"
echo "ref=$ref" >> "$GITHUB_OUTPUT"
- name: Attach digest to release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REF: ${{ steps.manifest.outputs.ref }}
REPO: ${{ github.repository }}
run: |
set -euo pipefail
if response="$(gh api -i "repos/$REPO/releases/tags/$REF" 2>&1)"; then
gh release upload "$REF" image-digest.txt --clobber -R "$REPO"
# The tag can be pushed before its GitHub release exists.
elif [[ "$response" == HTTP/*' 404 '* ]]; then
echo "::warning::No GitHub release exists for $REF"
else
printf '%s\n' "$response" >&2
exit 1
fi
Loading