From a615b0aee7c715963981cd60577d012c13ebafe6 Mon Sep 17 00:00:00 2001 From: fabi Date: Sun, 31 May 2026 17:26:58 +0200 Subject: [PATCH 1/2] fix(docker): unblock image builds on this host - backend dep-cache stage stubs only main.rs/lib.rs, but Cargo.toml declares a second [[bin]] crawler at src/bin/crawler.rs, so `cargo build --locked` aborts ("can't find bin crawler"). Stub it too. - runtime was debian:bookworm-slim (glibc 2.36) while rust:1-slim now tracks trixie (glibc 2.41) -> "GLIBC_2.39 not found" at boot. Pin the runtime to debian:trixie-slim so it matches the builder's glibc. - frontend healthcheck probed localhost (-> musl picks IPv6 ::1) but the Node server binds IPv4 0.0.0.0 only -> false "unhealthy". Probe 127.0.0.1. Co-Authored-By: Claude Opus 4.8 (1M context) --- backend/Dockerfile | 8 ++++++-- frontend/Dockerfile | 10 ++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 964cbb9..fd4f87c 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -10,7 +10,8 @@ RUN apt-get update \ # exact crate versions CI tested. Without Cargo.lock + the flag, cargo # would silently resolve fresh on every image build. COPY Cargo.toml Cargo.lock ./ -RUN mkdir src && echo "fn main() {}" > src/main.rs && echo "" > src/lib.rs \ +RUN mkdir -p src/bin && echo "fn main() {}" > src/main.rs && echo "" > src/lib.rs \ + && echo "fn main() {}" > src/bin/crawler.rs \ && cargo build --locked --release \ && rm -rf src @@ -18,7 +19,10 @@ COPY src ./src COPY migrations ./migrations RUN touch src/main.rs src/lib.rs && cargo build --locked --release -FROM debian:bookworm-slim +FROM debian:trixie-slim +# Runtime base must match the builder's Debian release: `rust:1-slim` tracks +# trixie (glibc 2.41), so a bookworm runtime (glibc 2.36) can't run the +# binary ("GLIBC_2.39 not found"). Keep these two in lockstep on bumps. # `curl` is for the container HEALTHCHECK; `ca-certificates` is for # outbound HTTPS (crawler covers/pages). RUN apt-get update \ diff --git a/frontend/Dockerfile b/frontend/Dockerfile index dcca067..33f3824 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -24,10 +24,12 @@ COPY --from=builder --chown=node:node /app/package.json ./ USER node EXPOSE 3000 -# Alpine's busybox `wget` is the canonical lightweight HTTP probe. -# `--spider` doesn't follow redirects; `node build` serves a 200 on -# `/` for the homepage so this works without a dedicated /health. +# Alpine's busybox `wget` is the canonical lightweight HTTP probe. Probe +# 127.0.0.1, not `localhost`: musl resolves `localhost` to IPv6 ::1 first, +# but the Node server binds IPv4 0.0.0.0 only, so a localhost probe gets +# "connection refused" and the container is wrongly marked unhealthy. Use a +# GET (`-O /dev/null`) since `node build` serves 200 on `/`. HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ - CMD wget -q --spider http://localhost:3000/ || exit 1 + CMD wget -q -O /dev/null http://127.0.0.1:3000/ || exit 1 CMD ["node", "build"] -- 2.49.1 From 2a0cc24c07216df1e451174b1d3d2caf1091bc8f Mon Sep 17 00:00:00 2001 From: fabi Date: Sun, 31 May 2026 17:26:58 +0200 Subject: [PATCH 2/2] ci: deploy to the local stack over the runner socket, not SSH The runner lives on the deploy host and shares its docker daemon, so the deploy job runs `docker compose pull && up -d` against the central compose via a bind-mounted compose dir (docker:cli + docker_host: "-") instead of appleboy/ssh-action. Drops the SSH_* secrets and recreates only the two mangalord services at the freshly built SHA. Requires /mnt/ssd/docker-data in the runner's container.valid_volumes. Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitea/workflows/deploy.yml | 41 ++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index b9aaaa7..a85489e 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -124,28 +124,31 @@ jobs: runs-on: ubuntu-latest needs: build-and-push if: github.event_name != 'pull_request' + # Single-host deploy: the runner lives on the same box as the stack, so we + # drive the host docker daemon directly (act_runner shares its socket via + # `docker_host: "-"`) instead of SSHing out. The compose dir is bind-mounted + # at its REAL host path so compose's relative bind-mounts (./mangalord/..., + # ./Caddyfile) resolve; this requires `/mnt/ssd/docker-data` in the runner's + # container.valid_volumes. The central compose references the images as + # registry.mc02.dev/mangalord-*:${MANGALORD_TAG:-latest}, so we only pull + # and recreate the two mangalord services at the freshly built SHA. + container: + image: docker:cli + volumes: + - /mnt/ssd/docker-data:/mnt/ssd/docker-data steps: - - name: SSH deploy - uses: appleboy/ssh-action@v1.0.3 - with: - host: ${{ secrets.SSH_HOST }} - username: ${{ secrets.SSH_USER }} - key: ${{ secrets.SSH_PRIVATE_KEY }} - port: ${{ secrets.SSH_PORT || 22 }} - envs: REGISTRY_URL,REGISTRY_USERNAME,REGISTRY_PASSWORD,IMAGE_TAG,DEPLOY_PATH - script_stop: true - script: | - set -euo pipefail - cd "$DEPLOY_PATH" - echo "$REGISTRY_PASSWORD" | docker login "$REGISTRY_URL" -u "$REGISTRY_USERNAME" --password-stdin - export REGISTRY_URL IMAGE_TAG - docker compose -f docker-compose.yml -f docker-compose.prod.yml pull - docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d - docker image prune -f - docker logout "$REGISTRY_URL" + - name: Deploy to the local stack + working-directory: /mnt/ssd/docker-data env: REGISTRY_URL: ${{ secrets.REGISTRY_URL }} REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} IMAGE_TAG: ${{ needs.build-and-push.outputs.image_tag }} - DEPLOY_PATH: ${{ vars.DEPLOY_PATH }} + run: | + set -eu + echo "$REGISTRY_PASSWORD" | docker login "$REGISTRY_URL" -u "$REGISTRY_USERNAME" --password-stdin + export MANGALORD_TAG="$IMAGE_TAG" + docker compose pull mangalord-backend mangalord-frontend + docker compose up -d mangalord-backend mangalord-frontend + docker image prune -f + docker logout "$REGISTRY_URL" -- 2.49.1