From 62fe659cba9897c47b78c6d44044304d3b5e208d Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 3 Nov 2026 21:41:47 +0530 Subject: [PATCH 1/2] ci(azure): repoint dev CI + registry to Azure VM (refs azure-migration) - Registry host gitea.* -> git.* (backend base FROM, frontend image, build scripts, base workflow) - deploy-dev: run on the Azure VM host (/opt/sapl/dev, secrets-based registry auth, hermes-owned checkout preserved) - build-base-image: token via REGISTRY_TOKEN secret, clone from git. --- .gitea/workflows/build-base-image.yml | 24 +++++++-------- .gitea/workflows/deploy-dev.yml | 42 +++++++++++++++------------ backend/Dockerfile | 2 +- docker-compose.dev.yml | 6 ++-- docs/uat-team-guide.md | 2 +- scripts/build-frontend-dev.sh | 14 ++++----- 6 files changed, 48 insertions(+), 42 deletions(-) diff --git a/.gitea/workflows/build-base-image.yml b/.gitea/workflows/build-base-image.yml index 61bbb9f5..8e04667f 100644 --- a/.gitea/workflows/build-base-image.yml +++ b/.gitea/workflows/build-base-image.yml @@ -48,15 +48,15 @@ jobs: } docker --version - # Helper container with host Docker + registry token. Mount sources - # resolve on the HOST (the daemon), so no runner config change is - # needed. The repo is cloned FRESH inside the helper (depth 1, - # authenticated via the per-run GITHUB_TOKEN) — never touching the - # shared dokploy checkouts, which a concurrent deploy may be - # building from. + # Helper container with host Docker. Mount sources resolve on the HOST + # (the daemon), so no runner config change is needed. Registry auth + # comes from the REGISTRY_TOKEN repo secret (env). The repo is cloned + # FRESH inside the helper (depth 1, authenticated via the per-run + # GITHUB_TOKEN) — never touching the shared deploy checkouts, which + # a concurrent deploy may be building from. docker run --rm \ -v /var/run/docker.sock:/var/run/docker.sock \ - -v /root/.hermes/registry-token.txt:/run/secrets/registry-token:ro \ + -e REGISTRY_TOKEN=${{ secrets.REGISTRY_TOKEN }} \ -e DISPATCH_REF=${{ github.ref }} \ -e INPUT_REF=${{ github.event.inputs.ref }} \ -e CLONE_TOKEN=${{ secrets.GITHUB_TOKEN }} \ @@ -92,7 +92,7 @@ jobs: echo "FATAL: CLONE_TOKEN empty - secrets.GITHUB_TOKEN unavailable?" >&2 exit 1 fi - git clone --depth 1 --branch "$BRANCH" "https://oauth2:${CLONE_TOKEN}@gitea.ai-automation-hub.tech/sapl/sapl.git" /work/sapl + git clone --depth 1 --branch "$BRANCH" "https://oauth2:${CLONE_TOKEN}@git.ai-automation-hub.tech/sapl/sapl.git" /work/sapl unset CLONE_TOKEN cd /work/sapl @@ -101,15 +101,15 @@ jobs: # tag); the sha tag keeps each base build identifiable. SHA="$(git rev-parse --short=8 HEAD)" TAG="dev-${SHA}" - REGISTRY="gitea.ai-automation-hub.tech" + REGISTRY="git.ai-automation-hub.tech" IMAGE="sapl-deploy-bot/sapl-backend-base" REGISTRY_USER="sapl-deploy-bot" PLATFORM="linux/amd64" echo "==> Building backend base for [$BRANCH] @ [$SHA] -> [$REGISTRY/$IMAGE:$TAG]" - REG_TOKEN="$(cat /run/secrets/registry-token 2>/dev/null || true)" + REG_TOKEN="$(cat /run/secrets/registry-token 2>/dev/null || echo "${REGISTRY_TOKEN:-}")" if [ -z "$REG_TOKEN" ]; then - echo "FATAL: no registry token (/run/secrets/registry-token)" >&2 + echo "FATAL: no registry token (REGISTRY_TOKEN secret or /run/secrets/registry-token)" >&2 exit 1 fi echo "$REG_TOKEN" | docker login "$REGISTRY" -u "$REGISTRY_USER" --password-stdin >/dev/null 2>&1 @@ -117,7 +117,7 @@ jobs: # Context is backend/ (Dockerfile.base COPYs requirements-base.txt # from it). The build runs on the HOST daemon, so the image is - # local for every same-VPS tier build afterwards; the push makes + # local for every same-host tier build afterwards; the push makes # it available to registry-authenticated pulls (dev deploy). docker build --platform "$PLATFORM" \ -t "$REGISTRY/$IMAGE:$TAG" \ diff --git a/.gitea/workflows/deploy-dev.yml b/.gitea/workflows/deploy-dev.yml index 36859b92..7cab4195 100644 --- a/.gitea/workflows/deploy-dev.yml +++ b/.gitea/workflows/deploy-dev.yml @@ -12,7 +12,7 @@ on: # One deploy at a time per branch (added for the CI speed milestone, issue #1151). # Two merges landing close together used to queue two CONCURRENT deploy jobs against the -# one shared checkout (/etc/dokploy/compose/sapl-sapl-dev/code), which produced both of +# one shared checkout (/opt/sapl/dev), which produced both of # our known deploy failure modes: # - "fatal: cannot lock ref 'HEAD': Unable to create '.../refs/heads/dev.lock': File exists" # - "Conflict. The container name '/_sapl-sapl-dev-backend-1' is already in use" @@ -62,7 +62,7 @@ jobs: steps: - name: Deploy to Dev via Host Docker run: | - echo "🚀 Deploying sapl Dev — laptop-first frontend build + registry pull..." + echo "🚀 Deploying sapl Dev on the Azure VM host — frontend build + registry push/pull..." # Install Docker CLI (runner is node:20-bullseye). Debian bullseye is # EOL (Aug 2026) — debian-security AND main both 404 on deb.debian.org @@ -83,20 +83,19 @@ jobs: } docker --version - # Helper container with host Docker + filesystem access + the - # laptop-build SSH key and registry token. Mount sources resolve on - # the HOST (the daemon), so no runner config change is needed. - # Laptop-first builds: LAPTOP_HOST + LAPTOP_SSH_USER enable the - # laptop build path (see scripts/build-frontend-dev.sh) — the script - # falls back to the VPS automatically when the laptop is offline. + # Helper container with host Docker + filesystem access + the registry + # token (Azure VM host). Mount sources resolve on the HOST (the + # daemon), so no runner config change is needed. + # No laptop build path here: LAPTOP_* stay unset so the script + # always builds locally (see scripts/build-frontend-dev.sh). + # Git auth for the checkout sync comes from the hermes credential + # file (host path, read-only); the checkout stays hermes-owned via + # the chown at the end (the helper runs as root). docker run --rm \ -v /var/run/docker.sock:/var/run/docker.sock \ - -v /etc/dokploy/compose/sapl-sapl-dev/code:/code \ - -v /root/.hermes/ssh/laptop_build_ed25519:/root/.ssh/id_ed25519:ro \ - -v /root/.hermes/ssh/known_hosts:/root/.ssh/known_hosts:ro \ - -v /root/.hermes/registry-token.txt:/run/secrets/registry-token:ro \ - -e LAPTOP_HOST=100.67.2.63 \ - -e LAPTOP_SSH_USER=shridharsabat \ + -v /opt/sapl/dev:/code \ + -v /home/hermes/.git-credentials:/root/.git-credentials:ro \ + -e REGISTRY_TOKEN=${{ secrets.REGISTRY_TOKEN }} \ -w /code \ docker:28-cli \ sh -c ' @@ -110,15 +109,22 @@ jobs: timeout 60 apk add --no-cache git openssh-client 2>/dev/null && break || echo "⚠️ apk mirror $AM failed, trying next..." done command -v git >/dev/null || { echo "❌ all apk mirrors failed — failing fast, please re-run"; exit 1; } + # Helper runs as root on a hermes-owned checkout: allow it + use + # the mounted credential file for the authenticated fetch below. + git config --global --add safe.directory /code + git config --global credential.helper store # Bootstrap: sync the host checkout BEFORE running the script — # the script itself re-syncs and self-heals, but must EXIST in the # checkout first (first run after any workflow/script change). git -C /code fetch origin dev 2>/dev/null git -C /code checkout -f dev 2>/dev/null || git -C /code checkout -f FETCH_HEAD git -C /code reset --hard origin/dev - # Builds frontend (laptop-first, VPS fallback), pushes to the - # Gitea registry, then `compose up -d` with FRONTEND_TAG set. + # Builds frontend locally, pushes to the Azure Gitea registry, + # then `compose up -d` with FRONTEND_TAG set. sh /code/scripts/build-frontend-dev.sh + # Restore hermes ownership (helper ran as root; .env.dev is the + # hermes-owned reference) so agent CLI work keeps working. + chown -R --reference=/code/.env.dev /code ' 2>&1 echo "✅ Dev deploy complete!" @@ -130,7 +136,7 @@ jobs: run: | docker run --rm \ -v /var/run/docker.sock:/var/run/docker.sock \ - -v /etc/dokploy/compose/sapl-sapl-dev/code:/code \ + -v /opt/sapl/dev:/code \ docker:28-cli \ sh -c ' HOST_MD5=$(md5sum /code/backend/routers/tna.py | cut -c1-32) @@ -141,7 +147,7 @@ jobs: echo "⚠️ BACKEND CONTAINER STALE vs checkout — compose --build reused the cache." # refs #2034: worker shares the backend built image (no worker # build block), so the rebuild is backend-only below. - echo " Fix: cd /etc/dokploy/compose/sapl-sapl-dev/code && docker compose --env-file .env.dev -f docker-compose.dev.yml -p sapl-sapl-dev build backend && docker compose --env-file .env.dev -f docker-compose.dev.yml -p sapl-sapl-dev up -d backend worker" + echo " Fix: cd /opt/sapl/dev && docker compose --env-file .env.dev -f docker-compose.dev.yml -p sapl-sapl-dev build backend && docker compose --env-file .env.dev -f docker-compose.dev.yml -p sapl-sapl-dev up -d backend worker" fi ' diff --git a/backend/Dockerfile b/backend/Dockerfile index dabb2a7c..f77854fe 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -12,7 +12,7 @@ # ORDERING: the base image MUST exist in the registry (or the local daemon) # before this builds. After any base-source change (Dockerfile.base, # requirements-base.txt), dispatch build-base-image.yml FIRST, then deploy. -FROM gitea.ai-automation-hub.tech/sapl-deploy-bot/sapl-backend-base:dev-latest +FROM git.ai-automation-hub.tech/sapl-deploy-bot/sapl-backend-base:dev-latest WORKDIR /app diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 6efa29fd..0e6cc901 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -187,10 +187,10 @@ services: cpus: '0.75' frontend: - # Image is built by scripts/build-frontend-dev.sh (laptop-first, VPS - # fallback) and pushed to the Gitea container registry. FRONTEND_TAG is + # Image is built by scripts/build-frontend-dev.sh (local build on the VM host) + # and pushed to the Gitea container registry. FRONTEND_TAG is # injected by the deploy script; dev-latest keeps a manual `up -d` working. - image: "gitea.ai-automation-hub.tech/sapl-deploy-bot/sapl-frontend:${FRONTEND_TAG:-dev-latest}" + image: "git.ai-automation-hub.tech/sapl-deploy-bot/sapl-frontend:${FRONTEND_TAG:-dev-latest}" pull_policy: always depends_on: backend: diff --git a/docs/uat-team-guide.md b/docs/uat-team-guide.md index 6e073fb0..28fe9daa 100644 --- a/docs/uat-team-guide.md +++ b/docs/uat-team-guide.md @@ -62,7 +62,7 @@ UAT (User Acceptance Testing) is a **separate, isolated environment** where new | Service | Production | UAT | |---------|:----------:|:----:| | **Web App** | [sapl.ai-automation-hub.tech](https://sapl.ai-automation-hub.tech) | [uat.ai-automation-hub.tech](https://uat.ai-automation-hub.tech) | -| **Gitea** | [gitea.ai-automation-hub.tech](https://gitea.ai-automation-hub.tech) | — | +| **Gitea** | [git.ai-automation-hub.tech](https://git.ai-automation-hub.tech) | — | | **Dokploy** | [dokploy.ai-automation-hub.tech](https://dokploy.ai-automation-hub.tech) | — | --- diff --git a/scripts/build-frontend-dev.sh b/scripts/build-frontend-dev.sh index a788ef39..683fa1cd 100755 --- a/scripts/build-frontend-dev.sh +++ b/scripts/build-frontend-dev.sh @@ -1,20 +1,20 @@ #!/bin/sh # build-frontend-dev.sh — laptop-first SAPL frontend image build + registry push, -# with automatic VPS fallback, then compose deploy of the dev stack. +# with automatic local fallback, then compose deploy of the dev stack. # # Runs inside the deploy job's docker:28-cli helper container (Alpine). Expected mounts: -# /code → host deploy checkout (e.g. /etc/dokploy/compose/sapl-sapl-dev/code) +# /code → host deploy checkout (e.g. /opt/sapl/dev) # /var/run/docker.sock → host docker daemon # /root/.ssh/id_ed25519 → Hermes laptop-build SSH key (read-only) # /root/.ssh/known_hosts → laptop host key (read-only, may be empty) -# /run/secrets/registry-token → Gitea package token (read-only) +# /run/secrets/registry-token → Gitea package token (read-only; or REGISTRY_TOKEN env) # # Flow: # 1. Reset the checkout to origin/ exactly # 2. Login to the Gitea container registry # 3. If LAPTOP_HOST + LAPTOP_SSH_USER are set AND the laptop answers SSH: # sync frontend/ via tar-over-ssh → build + push ON THE LAPTOP -# else: build + push on the VPS (today's behaviour) +# else: build + push on the host (local fallback) # 4. `docker compose up -d` with FRONTEND_TAG=> so the stack pulls # the freshly pushed image (compose frontend service has pull_policy: always). # Also tags/pushes dev-latest so a plain manual `up -d` always works. @@ -25,7 +25,7 @@ BRANCH="${BRANCH:-dev}" PROJECT="${PROJECT:-sapl-sapl-dev}" COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.dev.yml}" ENV_FILE="${ENV_FILE:-.env.dev}" -REGISTRY="${REGISTRY:-gitea.ai-automation-hub.tech}" +REGISTRY="${REGISTRY:-git.ai-automation-hub.tech}" IMAGE="${IMAGE:-sapl-deploy-bot/sapl-frontend}" # bot's user namespace (verified push access) REGISTRY_USER="${REGISTRY_USER:-sapl-deploy-bot}" PLATFORM="${PLATFORM:-linux/amd64}" @@ -92,11 +92,11 @@ if [ -n "$LAPTOP_TARGET" ] && ssh $SSHOPTS "$LAPTOP_TARGET" "command -v podman > "; then echo "==> Laptop build+push OK" else - echo "==> Laptop build failed/timed out (exit $?) — building on VPS (fallback)" + echo "==> Laptop build failed/timed out (exit $?) — building locally (fallback)" build_and_push_local /code/frontend fi else - echo "==> Laptop DOWN/unconfigured — building on VPS (fallback)" + echo "==> Laptop DOWN/unconfigured — building locally (fallback)" build_and_push_local /code/frontend fi -- 2.54.0 From cc641cd8fabff7259141dc2e6fa07c290500643b Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 3 Nov 2026 21:44:01 +0530 Subject: [PATCH 2/2] fix(ci): BusyBox-safe checkout chown (no --reference in docker:28-cli) --- .gitea/workflows/deploy-dev.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/deploy-dev.yml b/.gitea/workflows/deploy-dev.yml index 7cab4195..6db68f66 100644 --- a/.gitea/workflows/deploy-dev.yml +++ b/.gitea/workflows/deploy-dev.yml @@ -122,9 +122,10 @@ jobs: # Builds frontend locally, pushes to the Azure Gitea registry, # then `compose up -d` with FRONTEND_TAG set. sh /code/scripts/build-frontend-dev.sh - # Restore hermes ownership (helper ran as root; .env.dev is the - # hermes-owned reference) so agent CLI work keeps working. - chown -R --reference=/code/.env.dev /code + # Restore checkout ownership (helper runs as root; .env.dev is + # the hermes-owned reference). BusyBox chown has NO --reference: + # resolve uid:gid via stat first (both exist in docker:28-cli). + chown -R "$(stat -c '%u:%g' /code/.env.dev)" /code ' 2>&1 echo "✅ Dev deploy complete!" -- 2.54.0