sharenet/.forgejo/workflows/ci.yml
continuist 0e002cf0bf
Some checks failed
Podman Rootless Demo / test-backend (push) Successful in 12m47s
Podman Rootless Demo / test-frontend (push) Successful in 14s
Podman Rootless Demo / build-backend (push) Successful in 25s
Podman Rootless Demo / build-frontend (push) Failing after 2m24s
Podman Rootless Demo / deploy-prod (push) Has been skipped
Apply all test stages
2025-10-24 22:16:04 -04:00

315 lines
11 KiB
YAML

name: Podman Rootless Demo
on: [push, pull_request]
jobs:
test-backend:
runs-on: [ci]
# if: false
# Point all steps at the host's rootless Podman socket
env:
# Point the client at the mounted socket
CONTAINER_HOST: unix:///run/user/1001/podman/podman.sock
# Make sure podman looks in the correct runtime dir hierarchy
XDG_RUNTIME_DIR: /tmp
RUN_ID: ${{ github.run_id }}
POSTGRES_IMG_DIGEST: ${{ secrets.POSTGRES_IMG_DIGEST }}
RUST_IMG_DIGEST: ${{ secrets.RUST_IMG_DIGEST }}
PREBUILT_BACKEND_TEST_IMAGE: ${{ secrets.REGISTRY_HOST }}/${{ github.repository }}/sharenet-test-rust
CACHE_REPO: ${{ secrets.REGISTRY_HOST }}/${{ github.repository }}/sharenet-test-rust-cache
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Verify socket visibility
run: |
set -euo pipefail
id -u; id -g
ls -ld /run/user/1001/podman
ls -l /run/user/1001/podman/podman.sock
test -S /run/user/1001/podman/podman.sock
- name: Use host rootless Podman
run: |
set -euo pipefail
podman --remote info --format '{{.Host.RemoteSocket.Path}} (remote={{.Host.RemoteSocket.Exists}})'
podman --remote version
podman --remote run --rm alpine:3.20 echo "Hello from host rootless Podman!"
- name: Login to container registry with PAT
run: |
echo "${{ secrets.REGISTRY_TOKEN }}" | podman --remote login \
-u "${{ secrets.REGISTRY_USERNAME }}" \
--password-stdin \
"${{ secrets.REGISTRY_HOST }}"
- name: Create network
run: podman --remote network create integ-${{ env.RUN_ID }}
- name: Generate cache key from lockfile
id: cache-key
run: |
cd "$GITHUB_WORKSPACE/backend"
LOCK_HASH=$(sha256sum Cargo.lock | cut -d' ' -f1)
SHORT_HASH=$(echo "$LOCK_HASH" | cut -c1-12)
echo "cache_key=$SHORT_HASH" >> $GITHUB_OUTPUT
echo "Using cache key: $SHORT_HASH"
- name: Build dependencies cache image
run: |
# Build deps stage with layer caching
podman --remote build \
--layers \
--cache-to "$CACHE_REPO" \
--cache-from "$CACHE_REPO" \
--target deps \
-f Dockerfile.test-rust \
-t "$PREBUILT_BACKEND_TEST_IMAGE:deps-${{ steps.cache-key.outputs.cache_key }}" \
backend
podman --remote push \
"$PREBUILT_BACKEND_TEST_IMAGE:deps-${{ steps.cache-key.outputs.cache_key }}"
- name: Build full test image with cached dependencies
run: |
# Build final image using cached deps
podman --remote build \
--layers \
--cache-from "$CACHE_REPO" \
--target runner \
-f Dockerfile.test-rust \
-t "$PREBUILT_BACKEND_TEST_IMAGE:test-${{ github.sha }}" \
backend
- name: Start PostgreSQL
run: |
podman --remote run -d \
--name test-postgres-${{ env.RUN_ID }} \
--network integ-${{ env.RUN_ID }} \
-e POSTGRES_PASSWORD=password \
-e POSTGRES_USER=postgres \
-e POSTGRES_DB=sharenet_test \
"$POSTGRES_IMG_DIGEST"
- name: Wait for PostgreSQL
run: |
timeout 60 bash -euc '
until podman --remote exec test-postgres-${{ env.RUN_ID }} \
pg_isready -h 127.0.0.1 -p 5432 -U postgres; do
sleep 1
done
'
- name: Ensure host Cargo cache directory exists
run: |
podman --remote run --rm \
-v /home/ci-service/.cache:/c \
alpine:3.20 sh -lc 'mkdir -p /c/cargo'
- name: Run backend tests
run: |
# Run tests in the pre-built test image
podman --remote run --rm \
--network integ-${{ env.RUN_ID }} \
-e DATABASE_URL=postgres://postgres:password@test-postgres-${{ env.RUN_ID }}:5432/sharenet_test \
"$PREBUILT_BACKEND_TEST_IMAGE:test-${{ github.sha }}"
- name: Cleanup
if: always()
run: |
podman --remote rm -f test-postgres-${{ env.RUN_ID }} 2>/dev/null || true
podman --remote network rm integ-${{ env.RUN_ID }} 2>/dev/null || true
- name: Debug DB (on failure)
if: failure()
run: podman --remote logs --tail=200 test-postgres-${{ env.RUN_ID }} || true
test-frontend:
runs-on: [ci]
# if: false
needs: test-backend
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Pass-through (no frontend tests yet)
run: echo "Frontend tests placeholder - no tests implemented yet"
build-backend:
runs-on: [ci]
# if: false
needs: [test-backend, test-frontend]
# needs: [test-frontend]
env:
CONTAINER_HOST: unix:///run/user/1001/podman/podman.sock
XDG_RUNTIME_DIR: /tmp
RUN_ID: ${{ github.run_id }}
BACKEND_IMAGE: ${{ secrets.REGISTRY_HOST }}/${{ github.repository }}/sharenet-backend-api-postgres
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Login to container registry with PAT
run: |
echo "${{ secrets.REGISTRY_TOKEN }}" | podman --remote login \
-u "${{ secrets.REGISTRY_USERNAME }}" \
--password-stdin \
"${{ secrets.REGISTRY_HOST }}"
- name: Build backend container image
run: |
podman --remote build \
-f backend/Dockerfile \
-t "$BACKEND_IMAGE:${{ github.sha }}" \
-t "$BACKEND_IMAGE:latest" \
backend
- name: Push backend container image
run: |
podman --remote push "$BACKEND_IMAGE:${{ github.sha }}"
podman --remote push "$BACKEND_IMAGE:latest"
build-frontend:
runs-on: [ci]
# if: false
needs: [test-backend, test-frontend]
# needs: [test-frontend]
env:
CONTAINER_HOST: unix:///run/user/1001/podman/podman.sock
XDG_RUNTIME_DIR: /tmp
RUN_ID: ${{ github.run_id }}
FRONTEND_IMAGE: ${{ secrets.REGISTRY_HOST }}/${{ github.repository }}/sharenet-frontend
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Login to container registry with PAT
run: |
echo "${{ secrets.REGISTRY_TOKEN }}" | podman --remote login \
-u "${{ secrets.REGISTRY_USERNAME }}" \
--password-stdin \
"${{ secrets.REGISTRY_HOST }}"
- name: Disk preflight diagnostic
run: |
echo "=== Podman storage info ==="
podman --remote system df
echo "=== Podman graph root ==="
podman --remote info --format '{{.Store.GraphRoot}}'
echo "=== Disk space info ==="
df -h /home/ci-service /tmp /var/tmp 2>/dev/null || df -h /tmp /var/tmp
echo "=== Inode info ==="
df -i /home/ci-service /tmp /var/tmp 2>/dev/null || df -i /tmp /var/tmp
- name: Build frontend container image
run: |
# Create temp directory on larger filesystem
TMP_DIR="/home/ci-service/tmp"
if [ ! -d "$TMP_DIR" ]; then
TMP_DIR="$(mktemp -d)"
fi
export TMPDIR="$TMP_DIR"
podman --remote build \
--build-arg NEXT_PUBLIC_API_HOST=${{ secrets.PROD_BACKEND_HOST }} \
--build-arg NEXT_PUBLIC_API_PORT=${{ secrets.PROD_BACKEND_PORT }} \
-f frontend/Dockerfile \
-t "$FRONTEND_IMAGE:${{ github.sha }}" \
-t "$FRONTEND_IMAGE:latest" \
frontend
- name: Cleanup storage
if: always()
run: podman --remote system prune -f
- name: Push frontend container image
run: |
podman --remote push "$FRONTEND_IMAGE:${{ github.sha }}"
podman --remote push "$FRONTEND_IMAGE:latest"
deploy-prod:
runs-on: [prod]
# needs: [build-backend]
# needs: [build-frontend]
needs: [build-backend, build-frontend]
env:
CONTAINER_HOST: unix:///run/user/1001/podman/podman.sock
XDG_RUNTIME_DIR: /tmp
RUN_ID: ${{ github.run_id }}
APP_NAME: ${{ github.repository }}
REGISTRY_HOST: ${{ secrets.REGISTRY_HOST }}
IMAGE_TAG: latest
POSTGRES_DATABASE_NAME: ${{ secrets.PROD_DB_DATABASE_NAME }}
POSTGRES_USERNAME: ${{ secrets.PROD_DB_USERNAME }}
POSTGRES_PASSWORD: ${{ secrets.PROD_DB_PASSWORD }}
POSTGRES_PORT: ${{ secrets.PROD_DB_PORT }}
PROD_BACKEND_PORT: ${{ secrets.PROD_BACKEND_PORT }}
PROD_FRONTEND_PORT: ${{ secrets.PROD_FRONTEND_PORT }}
PROD_BACKEND_HOST: ${{ secrets.PROD_BACKEND_HOST }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Login to container registry with PAT
run: |
echo "${{ secrets.REGISTRY_TOKEN }}" | podman --remote login \
-u "${{ secrets.REGISTRY_USERNAME }}" \
--password-stdin \
"${{ secrets.REGISTRY_HOST }}"
- name: (Optional) Pre-pull images to speed up play
run: |
podman --remote pull "$REGISTRY_HOST/$APP_NAME/sharenet-backend-api-postgres:$IMAGE_TAG"
podman --remote pull "$REGISTRY_HOST/$APP_NAME/sharenet-frontend:$IMAGE_TAG"
# RENDER & COPY NGINX CONF (unchanged except for whitelisted envsubst)
- name: Render nginx.conf and write to host
run: |
set -euo pipefail
apk add --no-cache gettext >/dev/null
envsubst '${PROD_FRONTEND_PORT} ${PROD_BACKEND_PORT}' < nginx/nginx.conf > /tmp/nginx.conf
podman --remote run --rm -i \
--userns=keep-id \
-v /opt/sharenet/nginx:/host-nginx:rw \
alpine:3.20 sh -c 'install -D -m 0644 /dev/stdin /host-nginx/nginx.conf' \
< /tmp/nginx.conf
# LINT NGINX CONF BEFORE APPLYING
- name: Validate nginx.conf with throwaway container
run: |
set -euo pipefail
podman --remote run --rm \
-v /opt/sharenet/nginx:/etc/nginx:ro \
docker.io/nginx:alpine \
sh -lc 'nginx -t -c /etc/nginx/nginx.conf'
# APPLY/RE-APPLY THE POD (no explicit "down"; use --replace)
- name: Apply pod (kube play --replace)
run: |
set -euo pipefail
# If your Podman supports --replace, this is the cleanest:
envsubst < deploy/prod-pod.yml | podman --remote kube play --replace -
# If --replace is NOT supported in your Podman, use this fallback instead:
# - name: Recreate pod (fallback)
# run: |
# set -euo pipefail
# podman --remote pod rm -f sharenet-production-pod 2>/dev/null || true
# envsubst < deploy/prod-pod.yml | podman --remote kube play -
# VERIFY (install curl first)
- name: Verify in-pod Nginx
run: |
set -euo pipefail
apk add --no-cache curl >/dev/null
curl -sS -D- http://127.0.0.1:8080/healthz
curl -sS -I http://127.0.0.1:8080/