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:{,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)"