mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
* feat(providers): Gemini CLI provider — ModelProvider.GEMINI Mirrors the grok blueprint with source-verified divergences (all facts pinned against google-gemini/gemini-cli @ 9681621c): no refresher daemon — Google's refresh tokens are reusable, so the RO host mount is COPIED to a writable container-local ~/.gemini and each container refreshes in-process independently (the write-back crash risk on RO never triggers); settings.json renders security.auth.selectedType 'oauth-personal', experimental.enableAgents=false (subagent ban), autoConfigureMemory=false with a bounded heap; tool scoping rides the tiered TOML Policy Engine (deny-only rules that yolo mode structurally cannot beat); gemini -p with --output-format stream-json; usage parsed from the run's own stdout stats — the adversarial pass caught the parser reading the json-mode nested shape while the entrypoint runs stream-json's FLAT shape (every real run would have priced $0 forever, hidden by fixtures sharing the assumption) — now flat-primary with the nested shape as cited fallback; rate-limit classified from structured error.type only (model-echo immune), native exit 41 auth passthrough; per-model pricing for the three GA models; migrations 084 (enum) + 085 (seed) complete the 082-085 finale chain. V1 excludes interactive intake/secretary. Stack-merge required two behavior-preserving complexity refactors in the shared park/usage plumbing (a park-pair loop; a usage-reader dispatch dict). * fix(providers): route gemini usage read through the containment barrier Mirrors the codex/grok fix — _gemini_usage_json now delegates to _read_usage_json_contained, so CodeQL's path-injection alert on the gemini read is resolved by the same resolve-and-contain guard. --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com>
196 lines
8.2 KiB
YAML
196 lines
8.2 KiB
YAML
name: Release
|
|
|
|
on:
|
|
release:
|
|
types: [published]
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
publish-images:
|
|
name: Build & push all RoboCo images to GHCR + Docker Hub
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
outputs:
|
|
version: ${{ steps.ver.outputs.version }}
|
|
|
|
env:
|
|
GHCR: ghcr.io/rennf93 # GitHub Container Registry namespace
|
|
DOCKERHUB: docker.io/renzof93 # Docker Hub namespace (different username)
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v7
|
|
|
|
- name: Free up runner disk space
|
|
run: |
|
|
# Eleven images on one runner is disk-heavy; drop preinstalled tooling
|
|
# we don't use so the builds don't run out of space.
|
|
sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/lib/android /opt/hostedtoolcache/CodeQL || true
|
|
df -h /
|
|
|
|
- name: Log in to GitHub Container Registry
|
|
uses: docker/login-action@v4
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Log in to Docker Hub
|
|
uses: docker/login-action@v4
|
|
with:
|
|
username: renzof93
|
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
|
|
- name: Derive version tag
|
|
id: ver
|
|
run: |
|
|
# release → the tag (v0.1.0 → 0.1.0). Any manual dispatch runs against a
|
|
# branch whose name can contain "/" (e.g. feature/x) — not a valid image
|
|
# tag — so use the short SHA, which always is one.
|
|
if [ "${{ github.event_name }}" = "release" ]; then
|
|
RAW="${{ github.event.release.tag_name }}"
|
|
VERSION="${RAW#v}"
|
|
else
|
|
VERSION="$(git rev-parse --short HEAD)"
|
|
fi
|
|
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
echo "Release version: $VERSION"
|
|
|
|
- name: Build & push every RoboCo image
|
|
env:
|
|
VERSION: ${{ steps.ver.outputs.version }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
# Tag one built image for both registries at :VERSION and :latest.
|
|
regtags() {
|
|
local name="$1"
|
|
echo "-t ${GHCR}/${name}:${VERSION} -t ${GHCR}/${name}:latest" \
|
|
"-t ${DOCKERHUB}/${name}:${VERSION} -t ${DOCKERHUB}/${name}:latest"
|
|
}
|
|
# Push one image's four tags (both registries, both labels).
|
|
pushall() {
|
|
local name="$1"
|
|
for ref in "${GHCR}/${name}" "${DOCKERHUB}/${name}"; do
|
|
docker push "${ref}:${VERSION}"
|
|
docker push "${ref}:latest"
|
|
done
|
|
}
|
|
|
|
# agent-base MUST build first: the agent images build
|
|
# `FROM roboco-agent-base` — a local tag that has to exist in the daemon
|
|
# before they build. Tag it locally (for the FROM) and for both registries.
|
|
echo "::group::build roboco-agent-base"
|
|
docker build -f docker/agent-base.Dockerfile \
|
|
-t roboco-agent-base $(regtags roboco-agent-base) .
|
|
echo "::endgroup::"
|
|
|
|
# roboco-agent-grok MUST build next, ahead of the loop: the two
|
|
# interactive Grok roles (prompter, secretary) build `FROM
|
|
# roboco-agent-grok` — a local tag that has to exist in the daemon
|
|
# before they build, and bash associative-array iteration order is
|
|
# unspecified, so it can't just be another entry in IMAGES below.
|
|
echo "::group::build roboco-agent-grok"
|
|
docker build -f docker/agent-grok.Dockerfile \
|
|
-t roboco-agent-grok $(regtags roboco-agent-grok) .
|
|
echo "::endgroup::"
|
|
|
|
# Every other image → its Dockerfile. Names mirror docker-compose's
|
|
# `image:` values exactly, so compose can later pull instead of build.
|
|
declare -A IMAGES=(
|
|
[roboco-orchestrator]=docker/orchestrator.Dockerfile
|
|
[roboco-panel]=docker/panel.Dockerfile
|
|
[roboco-video-renderer]=docker/video-renderer.Dockerfile
|
|
[roboco-sandbox-pg]=docker/sandbox-pg.Dockerfile
|
|
[roboco-agent-pm]=docker/agent-pm.Dockerfile
|
|
[roboco-agent-dev-be]=docker/agent-dev-be.Dockerfile
|
|
[roboco-agent-dev-fe]=docker/agent-dev-fe.Dockerfile
|
|
[roboco-agent-qa-be]=docker/agent-qa-be.Dockerfile
|
|
[roboco-agent-qa-fe]=docker/agent-qa-fe.Dockerfile
|
|
[roboco-agent-ux]=docker/agent-ux.Dockerfile
|
|
[roboco-agent-doc]=docker/agent-doc.Dockerfile
|
|
[roboco-agent-prompter]=docker/agent-prompter.Dockerfile
|
|
[roboco-agent-secretary]=docker/agent-secretary.Dockerfile
|
|
[roboco-agent-pr-reviewer]=docker/agent-pr-reviewer.Dockerfile
|
|
[roboco-agent-grok-prompter]=docker/agent-grok-prompter.Dockerfile
|
|
[roboco-agent-grok-secretary]=docker/agent-grok-secretary.Dockerfile
|
|
[roboco-agent-codex]=docker/agent-codex.Dockerfile
|
|
# Gemini (Google, official CLI) — one-shot delivery roles only (V1),
|
|
# no interactive prompter/secretary variant depends FROM it, so
|
|
# (unlike roboco-agent-grok above) it needs no special build-order
|
|
# carve-out and is just another entry here.
|
|
[roboco-agent-gemini]=docker/agent-gemini.Dockerfile
|
|
)
|
|
for name in "${!IMAGES[@]}"; do
|
|
echo "::group::build ${name}"
|
|
docker build -f "${IMAGES[$name]}" $(regtags "${name}") .
|
|
echo "::endgroup::"
|
|
done
|
|
|
|
# Push only after every build succeeds, so a failure never leaves a
|
|
# half-published release. Base first, then the rest.
|
|
echo "::group::push roboco-agent-base"
|
|
pushall roboco-agent-base
|
|
echo "::endgroup::"
|
|
echo "::group::push roboco-agent-grok"
|
|
pushall roboco-agent-grok
|
|
echo "::endgroup::"
|
|
for name in "${!IMAGES[@]}"; do
|
|
echo "::group::push ${name}"
|
|
pushall "${name}"
|
|
echo "::endgroup::"
|
|
done
|
|
|
|
echo "Published roboco-agent-base + roboco-agent-grok + ${#IMAGES[@]} more images to GHCR + Docker Hub at :${VERSION} and :latest"
|
|
|
|
pull-smoke:
|
|
name: Registry pull smoke — verify every published image is pullable
|
|
needs: publish-images
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
packages: read
|
|
|
|
# This is the exact regression class the docs/CHANGELOG already record
|
|
# once ("Release images complete" — the release workflow's build matrix
|
|
# was missing the two Grok sub-images, so docker-compose.registry.yml's
|
|
# pull failed for every fresh user until the next release). Running the
|
|
# literal command a user runs, against the tag that was just published,
|
|
# catches a missing/mistagged image before anyone else does.
|
|
env:
|
|
ROBOCO_REGISTRY: ghcr.io/rennf93
|
|
ROBOCO_VERSION: ${{ needs.publish-images.outputs.version }}
|
|
# docker-compose.registry.yml hard-requires these three non-empty
|
|
# (`${VAR:?...}` on the orchestrator/nginx services) just to
|
|
# INTERPOLATE the file — `pull` never touches their runtime values, so
|
|
# placeholders are enough to get past that gate (see scripts/bootstrap.sh
|
|
# for what a real deploy generates here).
|
|
ROBOCO_ENCRYPTION_KEY: pull-smoke-placeholder-not-a-real-key
|
|
ROBOCO_AGENT_AUTH_SECRET: pull-smoke-placeholder-not-a-real-secret
|
|
ROBOCO_PANEL_AGENT_TOKEN: pull-smoke-placeholder-not-a-real-token
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v7
|
|
|
|
- name: Free up runner disk space
|
|
run: |
|
|
# Same trick as publish-images: pulling ~20 images (roboco's own
|
|
# plus postgres/redis/ollama/nginx) on one runner is disk-heavy.
|
|
sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/lib/android /opt/hostedtoolcache/CodeQL || true
|
|
df -h /
|
|
|
|
- name: Log in to GitHub Container Registry
|
|
uses: docker/login-action@v4
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Pull every image docker-compose.registry.yml references
|
|
run: |
|
|
echo "Pulling ${ROBOCO_REGISTRY}/roboco-*:${ROBOCO_VERSION} (+ upstream deps) — failing on any missing image..."
|
|
docker compose -f docker-compose.registry.yml pull
|