All checks were successful
images / hermes (push) Successful in 1m55s
Upstream ships /opt/hermes (app + .venv + scripts) read-only root, which blocks the agent self-modifying and the gateway auto-installing the WhatsApp bridge's node_modules in place. Add a derived Containerfile layer (FROM the upstream build) that chowns/chmods /opt/hermes writable by the runtime hermes user. Done in the image, not a volume: a volume over /opt/hermes copies-up once then freezes the app, silently defeating AutoUpdate=registry. Persistence stays on the /opt/data volume. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011D3YeWKpjg5bT488fVanCH
83 lines
3.5 KiB
YAML
83 lines
3.5 KiB
YAML
name: images
|
|
# Build container images required by lair infra and publish them to the Gitea
|
|
# registry at git.lair.cafe. Convention mirrors gongfoo/.gitea/workflows/images.yml.
|
|
#
|
|
# Hermes is the first image: built directly from NousResearch/hermes-agent's own
|
|
# Dockerfile at the latest upstream release tag, and published as
|
|
# git.lair.cafe/lair/hermes:{<version>,latest}. bob then pulls it via a normal
|
|
# AutoUpdate=registry quadlet.
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
paths:
|
|
- "images/**"
|
|
- ".gitea/workflows/images.yml"
|
|
schedule:
|
|
# Daily poll for new upstream releases (Gitea can't subscribe to GitHub
|
|
# release webhooks, so we poll). Release-triggered in effect: a build only
|
|
# runs when the resolved upstream version isn't already in our registry.
|
|
- cron: "0 7 * * *"
|
|
workflow_dispatch:
|
|
inputs:
|
|
force:
|
|
description: "Rebuild even if the version is already published"
|
|
type: boolean
|
|
default: false
|
|
|
|
jobs:
|
|
hermes:
|
|
runs-on:
|
|
- metal
|
|
- podman
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: resolve latest upstream release
|
|
id: rel
|
|
run: |
|
|
# Prefer a published release; fall back to the newest tag.
|
|
tag=$(curl -fsS 'https://api.github.com/repos/NousResearch/hermes-agent/releases/latest' | jq -r '.tag_name // empty')
|
|
if [ -z "$tag" ]; then
|
|
tag=$(curl -fsS 'https://api.github.com/repos/NousResearch/hermes-agent/tags' | jq -r '.[0].name // empty')
|
|
fi
|
|
if [ -z "$tag" ] || [ "$tag" = "null" ]; then
|
|
echo "ERROR: could not resolve an upstream hermes release/tag"; exit 1
|
|
fi
|
|
echo "upstream latest: $tag"
|
|
echo "tag=$tag" >> "$GITHUB_OUTPUT"
|
|
echo "version=${tag#v}" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: login to registry
|
|
run: podman login -u ${{ gitea.actor }} -p ${{ secrets.REGISTRY_TOKEN }} git.lair.cafe
|
|
|
|
- name: build & push (release-triggered, self-healing)
|
|
env:
|
|
TAG: ${{ steps.rel.outputs.tag }}
|
|
VERSION: ${{ steps.rel.outputs.version }}
|
|
FORCE: ${{ github.event.inputs.force }}
|
|
run: |
|
|
IMAGE=git.lair.cafe/lair/hermes
|
|
# Self-healing: the source of truth is "is this version in the registry?"
|
|
# — not a committed pin that can desync if a prior build failed.
|
|
# NB: when the *build definition* changes (e.g. the writable-tree
|
|
# layer), republish the same version with the `force` dispatch input.
|
|
if [ "$FORCE" != "true" ] && skopeo inspect "docker://${IMAGE}:${VERSION}" >/dev/null 2>&1; then
|
|
echo "${IMAGE}:${VERSION} already published — nothing to build"
|
|
exit 0
|
|
fi
|
|
# Two-stage: (1) build upstream from the git context into a local tag,
|
|
# (2) derive our published image from it via images/hermes/Containerfile
|
|
# (makes /opt/hermes writable by uid 10000 — see that file).
|
|
BASE="localhost/hermes-upstream:${VERSION}"
|
|
echo "[1/2] building upstream ${BASE} from NousResearch/hermes-agent#${TAG}"
|
|
podman build --pull=newer -t "${BASE}" \
|
|
"https://github.com/NousResearch/hermes-agent.git#${TAG}"
|
|
echo "[2/2] building derived (writable /opt/hermes) -> ${IMAGE}:${VERSION}"
|
|
podman build --build-arg BASE="${BASE}" \
|
|
-t "${IMAGE}:${VERSION}" \
|
|
-t "${IMAGE}:latest" \
|
|
images/hermes
|
|
podman push "${IMAGE}:${VERSION}"
|
|
podman push "${IMAGE}:latest"
|
|
echo "published ${IMAGE}:${VERSION} (and :latest)"
|