feat: init
Main Release / hadolint (push) Successful in 7s
Main Release / build (push) Successful in 2m34s
Main Release / tag (push) Successful in 9s
Tag Release / hadolint (push) Successful in 12s
Tag Release / build (push) Successful in 2m53s
Tag Release / build-push (push) Successful in 3m29s

This commit is contained in:
Julien Cabillot
2026-06-16 10:58:51 -04:00
parent 94b9d5b1f2
commit 12777914ab
8 changed files with 344 additions and 1 deletions
+52
View File
@@ -0,0 +1,52 @@
name: Nightly Rebuild
on:
schedule:
- cron: '0 0 * * *'
jobs:
hadolint:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: hadolint/hadolint-action@2332a7b74a6de0dda2e2221d575162eba76ba5e5 # v3.3.0
with:
dockerfile: Dockerfile
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
- run: docker build -t ci-image:${{ github.sha }} .
build-push:
needs: [build]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 0
- uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
- uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- id: get-latest-tag
run: |
TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
echo "tag=$TAG" >> $GITHUB_OUTPUT
- id: meta
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6
with:
images: jcabillot/hermes-agent-webui
tags: |
type=raw,value=${{ steps.get-latest-tag.outputs.tag }}-latest,enable=${{ steps.get-latest-tag.outputs.tag != '' }}
- uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
pull: true
+39
View File
@@ -0,0 +1,39 @@
name: Main Release
on:
push:
branches: [main]
jobs:
hadolint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: hadolint/hadolint-action@2332a7b74a6de0dda2e2221d575162eba76ba5e5 # v3.3.0
with:
dockerfile: Dockerfile
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
- run: docker build -t ci-image:${{ github.sha }} .
tag:
needs: [build]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 0
- name: Configure git auth
run: |
git remote set-url origin "https://x-access-token:${{ secrets.SA_TOKEN_ACTION_PUSH_TAGS }}@scm.cabillot.eu/web/hermes-agent-webui.git"
- uses: anothrNick/github-tag-action@4ed44965e0db8dab2b466a16da04aec3cc312fd8 # v1.75.0
env:
GITHUB_TOKEN: ${{ secrets.SA_TOKEN_ACTION_PUSH_TAGS }}
DEFAULT_BUMP: patch
RELEASE_BRANCHES: main
WITH_V: true
GIT_API_TAGGING: false
+21
View File
@@ -0,0 +1,21 @@
name: PR Checks
on:
pull_request:
branches: [main]
jobs:
hadolint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: hadolint/hadolint-action@2332a7b74a6de0dda2e2221d575162eba76ba5e5 # v3.3.0
with:
dockerfile: Dockerfile
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
- run: docker build -t ci-image:${{ github.sha }} .
+47
View File
@@ -0,0 +1,47 @@
name: Tag Release
on:
push:
tags: ['*']
jobs:
hadolint:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: hadolint/hadolint-action@2332a7b74a6de0dda2e2221d575162eba76ba5e5 # v3.3.0
with:
dockerfile: Dockerfile
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
- run: docker build -t ci-image:${{ github.sha }} .
build-push:
needs: [build]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
- uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- id: meta
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6
with:
images: jcabillot/hermes-agent-webui
tags: |
type=ref,event=tag
type=ref,event=tag,suffix=-latest
- uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
pull: true
+39
View File
@@ -0,0 +1,39 @@
# AGENTS.md
## 1. Overview
Container image for Hermes Agent WebUI with a bundled Hermes Agent runtime, distributed via Docker Hub as `jcabillot/hermes-agent-webui`.
## 2. Folder Structure
- `Dockerfile`: Multi-stage image build — clones `nousresearch/hermes-agent` and `nesquena/hermes-webui`, installs both in a shared venv, copies to a slim runtime stage. Exposes port `8787`.
- `.gitea/workflows/docker-build.yaml`: Gitea Actions pipeline — builds and pushes the image to Docker Hub on push to `main` and on a daily cron schedule.
- `renovate.json`: Regex-based custom manager tracking upstream `nousresearch/hermes-agent` and `nesquena/hermes-webui` Docker tag updates.
- `README.md`: Public-facing documentation.
- `AGENTS.md`: This file — contributor guide for AI agents.
## 3. Core Behaviors & Patterns
- **Build & Release**: The Dockerfile pins two upstream repos via `ARG HERMES_AGENT_VERSION` and `ARG HERMES_WEBUI_VERSION`. On push to `main`, the CI pipeline builds the image, bumps the git tag (`patch`), which triggers a tag release that pushes to `jcabillot/hermes-agent-webui`. A daily cron rebuild pushes `<latest-tag>-latest`.
- **Dependency Tracking**: Renovate scans `Dockerfile` for both `ARG` version pins and opens PRs when either upstream publishes a new tag.
- **Multi-stage Build**: Build stage clones repos and creates a venv; runtime stage is a clean `python:3.12-slim` with only the venv and source trees copied over.
- **Health Check**: The container exposes port `8787` with a `/health` endpoint polled every 30s.
## 4. Conventions
- **Version Pinning**: Both upstream versions (`HERMES_AGENT_VERSION`, `HERMES_WEBUI_VERSION`) are pinned via `ARG` at the top of the builder stage, managed by renovate.
- **CI Secrets**: `DOCKERHUB_USERNAME`, `DOCKERHUB_TOKEN`, `SA_TOKEN_ACTION_PUSH_TAGS` required. Login skipped on PR events.
- **Dockerfile Style**: Multi-stage with `AS builder` / runtime split. `apt-get` in a single `RUN` with cleanup. `COPY --from=builder` for artifacts. Comments annotate stage boundaries.
- **Pin Hashing**: All GitHub Action versions pinned to commit SHA hashes with semantic version in trailing comment.
## 5. Working Agreements
- Respond in the user's preferred language (French or English); keep technical terms in English, never translate code blocks
- Create tests/lint only when explicitly requested
- Build context by reviewing related usages and patterns before editing
- Prefer simple solutions; avoid unnecessary abstraction
- Ask for clarification when requirements are ambiguous
- Make minimal changes; preserve public APIs and behavior
- Run type-check after code changes (no type-checker in this project — skip)
- New files: single-purpose, colocated with related concerns
- External dependencies: only when necessary, explain why
+94
View File
@@ -0,0 +1,94 @@
# Build stage: clone and install Hermes Agent + WebUI dependencies
FROM python:3.12-slim AS builder
WORKDIR /build
# hadolint ignore=DL3008
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
curl \
git \
&& rm -rf /var/lib/apt/lists/*
# Install uv system-wide
# hadolint ignore=DL4006
RUN curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=/usr/local/bin sh
# Clone Hermes Agent at a pinned commit
ARG HERMES_AGENT_VERSION=v2026.6.5
RUN git clone --depth 1 --branch ${HERMES_AGENT_VERSION} \
https://github.com/NousResearch/hermes-agent.git /build/hermes-agent
# Clone Hermes WebUI at a pinned commit
ARG HERMES_WEBUI_VERSION=v0.51.350
RUN git clone --depth 1 --branch ${HERMES_WEBUI_VERSION} \
https://github.com/nesquena/hermes-webui.git /build/hermes-webui
# Create a shared venv and install both projects
# hadolint ignore=DL3059
RUN uv venv /build/venv
ENV VIRTUAL_ENV=/build/venv
ENV PATH="/build/venv/bin:$PATH"
# Install hermes-agent with all extras (includes ML/agent deps)
# hadolint ignore=DL3013,DL3059
RUN uv pip install \
"/build/hermes-agent[all]"
# Install hermes-webui deps (pyyaml + cryptography)
# hadolint ignore=DL3059
RUN uv pip install \
-r /build/hermes-webui/requirements.txt
# Install uv in the venv so the webui server can use it for profile/skill management
# hadolint ignore=DL3059
RUN uv pip install \
"uv>=0.6.0"
# Runtime stage
FROM python:3.12-slim
WORKDIR /app
# hadolint ignore=DL3008
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
curl \
git \
openssh-client \
&& rm -rf /var/lib/apt/lists/*
# Copy the virtual environment and source trees from builder
COPY --from=builder /build/venv /opt/venv
COPY --from=builder /build/hermes-agent /opt/hermes-agent
COPY --from=builder /build/hermes-webui /app
# Set environment
ENV PATH="/opt/venv/bin:$PATH" \
VIRTUAL_ENV=/opt/venv \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PYTHONIOENCODING=utf-8
# Tell the WebUI where to find the agent
ENV HERMES_WEBUI_AGENT_DIR=/opt/hermes-agent \
HERMES_WEBUI_HOST=0.0.0.0 \
HERMES_WEBUI_PORT=8787 \
HERMES_WEBUI_STATE_DIR=/home/hermes/.hermes/webui \
HERMES_WEBUI_DEFAULT_WORKSPACE=/workspace \
HERMES_HOME=/home/hermes/.hermes
# Create non-root user
RUN useradd --create-home --shell /bin/bash hermes \
&& mkdir -p /workspace \
&& chown -R hermes:hermes /app /opt/venv /opt/hermes-agent /workspace /home/hermes
USER hermes
EXPOSE 8787
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD curl -f http://localhost:8787/health || exit 1
# Run the WebUI server (which runs Hermes Agent in-process)
CMD ["python", "/app/server.py"]
+27 -1
View File
@@ -1,3 +1,29 @@
# hermes-agent-webui
Hermes Agent WebUI container image
Container image for [Hermes WebUI](https://github.com/nesquena/hermes-webui) with a bundled [Hermes Agent](https://github.com/NousResearch/hermes-agent) runtime.
## Image
`jcabillot/hermes-agent-webui` — [Docker Hub](https://hub.docker.com/r/jcabillot/hermes-agent-webui)
## Usage
```bash
docker pull jcabillot/hermes-agent-webui
docker run -p 8787:8787 jcabillot/hermes-agent-webui
```
Open http://localhost:8787
## Build locally
```bash
docker build -t jcabillot/hermes-agent-webui .
```
## Tags
| Tag | Description |
|-----|-------------|
| `latest` | Latest stable build from `main` |
| `sha-<commit>` | Per-commit build |
+25
View File
@@ -0,0 +1,25 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"customManagers": [
{
"customType": "regex",
"description": "Track nousresearch/hermes-agent Docker tag pinned in Dockerfile ARG",
"managerFilePatterns": ["/^Dockerfile$/"],
"matchStrings": ["ARG HERMES_AGENT_VERSION=v(?<currentValue>[^\\s]+)"],
"depNameTemplate": "nousresearch/hermes-agent",
"datasourceTemplate": "docker",
"versioningTemplate": "docker",
"extractVersionTemplate": "v^(?<version>.*)$"
},
{
"customType": "regex",
"description": "Track nesquena/hermes-webui Docker tag pinned in Dockerfile ARG",
"managerFilePatterns": ["/^Dockerfile$/"],
"matchStrings": ["ARG HERMES_WEBUI_VERSION=v(?<currentValue>[^\\s]+)"],
"depNameTemplate": "nesquena/hermes-webui",
"datasourceTemplate": "docker",
"versioningTemplate": "docker",
"extractVersionTemplate": "v^(?<version>.*)$"
}
]
}