All checks were successful
CI / Build & test backend (push) Successful in 42m58s
actions/setup-java@v4 installs the JDK but not Maven itself. pi-runner does not have mvn pre-installed. Add an explicit curl install step matching the approach used in backend/Dockerfile.
178 lines
7.2 KiB
YAML
178 lines
7.2 KiB
YAML
# Generated by GitHub Copilot
|
|
#
|
|
# docker.yml — Build and push Docker image to Gitea Container Registry
|
|
#
|
|
# Triggers after CI passes on main (workflow_run) to prevent this heavyweight job
|
|
# from competing with the Maven+test job on the single-capacity Pi runner.
|
|
#
|
|
# Produces images tagged with the short SHA and 'latest'.
|
|
# Immediately deploys to the Pi production stack via docker.sock.
|
|
#
|
|
# Required Gitea repository secrets (Settings → Actions → Secrets):
|
|
# REGISTRY_USERNAME — Gitea username (andrisenins)
|
|
# REGISTRY_PASSWORD — Gitea access token (package:write + repo:write scope)
|
|
|
|
name: Docker
|
|
|
|
on:
|
|
# Only run after CI succeeds — prevents OOM kills on the Pi from parallel jobs.
|
|
workflow_run:
|
|
workflows: ["CI"]
|
|
types: [completed]
|
|
branches: [main]
|
|
workflow_dispatch:
|
|
|
|
# Cancel any in-progress docker build for the same branch so stale BuildKit
|
|
# containers from the previous run are cleaned up before the new build starts.
|
|
concurrency:
|
|
group: docker-${{ gitea.event.workflow_run.head_branch || gitea.ref }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
REGISTRY: git.amlab.dev
|
|
|
|
jobs:
|
|
build-api:
|
|
name: Build & push API image
|
|
runs-on: pi-runner
|
|
# For workflow_run events: only build if CI succeeded.
|
|
# For workflow_dispatch (manual): always run.
|
|
if: >
|
|
github.event_name == 'workflow_dispatch' ||
|
|
github.event.workflow_run.conclusion == 'success'
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
with:
|
|
# In a workflow_run context, checkout the commit that triggered CI.
|
|
ref: ${{ gitea.event.workflow_run.head_sha || gitea.sha }}
|
|
|
|
- name: Log in to Gitea Container Registry
|
|
# Login BEFORE the Maven build while the Pi CPU is idle.
|
|
# When login is attempted after a long Maven build the host Docker daemon
|
|
# exhausts its 30s HTTP timeout on every attempt (confirmed in MYPHOTOS
|
|
# task logs — same issue applies here).
|
|
# NOTE: use 'if' not '&&' — act_runner uses 'bash -e', so 'cmd && break'
|
|
# exits the entire script on the first non-zero cmd result.
|
|
env:
|
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
|
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
|
run: |
|
|
for i in 1 2 3 4 5 6 7 8 9 10; do
|
|
if echo "$REGISTRY_PASSWORD" | docker login --username "$REGISTRY_USERNAME" --password-stdin "$REGISTRY"; then
|
|
break
|
|
fi
|
|
if [ "$i" -eq 10 ]; then
|
|
echo "ERROR: All 10 docker login attempts failed"
|
|
exit 1
|
|
fi
|
|
echo "Login attempt $i failed, retrying in 30s..."
|
|
sleep 30
|
|
done
|
|
|
|
# Build JAR natively (inside the job container, outside BuildKit/QEMU).
|
|
# Without this, Maven runs under QEMU cross-emulation during the Docker build
|
|
# RUN step, causing TLS connections to Maven Central to break after ~20 min.
|
|
- name: Set up Java 21 LTS
|
|
uses: actions/setup-java@v4
|
|
with:
|
|
distribution: temurin
|
|
java-version: '21'
|
|
cache: maven
|
|
|
|
# actions/setup-java installs the JDK but not Maven.
|
|
# pi-runner has no pre-installed mvn — download the same version used
|
|
# in backend/Dockerfile to keep the build environment consistent.
|
|
- name: Install Maven 3.9.9
|
|
run: |
|
|
curl -fsSL https://archive.apache.org/dist/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.tar.gz \
|
|
| tar -xz -C /opt
|
|
echo "/opt/apache-maven-3.9.9/bin" >> $GITHUB_PATH
|
|
|
|
- name: Build JAR (native — avoids QEMU Maven download)
|
|
working-directory: backend
|
|
run: mvn -q -ntp clean package -DskipTests
|
|
|
|
- name: Build and push API image
|
|
# Use plain docker build+push (no buildx, no JS action) to avoid
|
|
# docker/build-push-action@v6 crashing silently in act_runner v0.5.0.
|
|
# DOCKER_BUILDKIT=0 uses legacy builder — no gRPC DeadlineExceeded on RPi.
|
|
env:
|
|
DOCKER_BUILDKIT: "0"
|
|
SHA: ${{ gitea.event.workflow_run.head_sha || gitea.sha }}
|
|
OWNER: ${{ gitea.repository_owner }}
|
|
run: |
|
|
SHORT_SHA="${SHA:0:7}"
|
|
IMAGE="${REGISTRY}/${OWNER}/calorie-counter-api"
|
|
docker build \
|
|
--target ci-image \
|
|
--build-arg JAR_FILE=target/calorie-counter-backend-1.0.0-SNAPSHOT.jar \
|
|
-t "${IMAGE}:${SHORT_SHA}" \
|
|
-t "${IMAGE}:latest" \
|
|
backend/
|
|
docker push "${IMAGE}:${SHORT_SHA}"
|
|
docker push "${IMAGE}:latest"
|
|
|
|
deploy:
|
|
name: Deploy to Pi — pull & restart stack
|
|
runs-on: pi-runner
|
|
needs: [build-api]
|
|
if: >
|
|
github.event_name == 'workflow_dispatch' ||
|
|
github.event.workflow_run.conclusion == 'success'
|
|
|
|
steps:
|
|
- name: Pull and restart production stack
|
|
# Run docker compose against the Pi HOST's homelab directory.
|
|
# Volume paths in a `docker run` issued via the mounted socket are
|
|
# resolved against the Docker DAEMON's filesystem (the Pi host), not
|
|
# the job container's filesystem — standard DinD-via-socket behaviour.
|
|
# The .env file in that directory supplies all production secrets so
|
|
# no secrets need to be passed into this job.
|
|
env:
|
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
|
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
|
run: |
|
|
docker run --rm \
|
|
-v /home/andris/homelab/calorie-counter:/project \
|
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
|
-e REGISTRY_PASSWORD \
|
|
-e REGISTRY_USERNAME \
|
|
-e "REGISTRY=${REGISTRY}" \
|
|
-w /project \
|
|
docker:27.5.1-cli \
|
|
sh -c '
|
|
echo "$REGISTRY_PASSWORD" | docker login \
|
|
--username "$REGISTRY_USERNAME" \
|
|
--password-stdin "$REGISTRY" &&
|
|
docker compose pull &&
|
|
docker compose up -d &&
|
|
echo "=== Stack status ===" &&
|
|
docker compose ps
|
|
'
|
|
|
|
# ── Build failure notification ──────────────────────────────────────────────
|
|
notify-failure:
|
|
name: Notify — build failure
|
|
runs-on: ubuntu-latest
|
|
needs: [build-api, deploy]
|
|
# Only run when something failed; skip on success or cancellation
|
|
if: failure()
|
|
steps:
|
|
- name: Post commit comment on failure
|
|
env:
|
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
|
OWNER: ${{ gitea.repository_owner }}
|
|
SHA: ${{ gitea.event.workflow_run.head_sha || gitea.sha }}
|
|
RUN_URL: ${{ gitea.server_url }}/${{ gitea.repository }}/actions/runs/${{ gitea.run_id }}
|
|
run: |
|
|
BODY="❌ **Docker build failed** for \`${SHA:0:7}\`\n\nSee the failed run: ${RUN_URL}"
|
|
curl -sf -X POST \
|
|
"https://git.amlab.dev/api/v1/repos/${OWNER}/calorie-counter/commits/${SHA}/comments" \
|
|
-H "Authorization: token ${REGISTRY_PASSWORD}" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"body\": \"${BODY}\"}" \
|
|
&& echo "Failure comment posted" \
|
|
|| echo "Warning: could not post comment (non-fatal)"
|