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: Build frontend container image run: | podman --remote build \ -f frontend/Dockerfile \ -t "$FRONTEND_IMAGE:${{ github.sha }}" \ -t "$FRONTEND_IMAGE:latest" \ frontend - 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, 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" # 1) Render nginx.conf from repo and copy to host (no unshare) - name: Render nginx.conf and write to host run: | set -euo pipefail apk add --no-cache gettext >/dev/null # envsubst # Only substitute the vars we intend to: ${PROD_*_PORT} 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 # 2) Lint nginx.conf BEFORE restarting the pod (avoids crash loops) - 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' # 3) Recreate the pod (down → play). This ensures new images/ports/env + the validated conf. - name: Recreate pod run: | set -euo pipefail podman --remote kube down sharenet-production-pod || true envsubst < deploy/prod-pod.yml | podman --remote kube play - # 4) Verify health on the prod host - name: Verify in-pod Nginx run: | set -euo pipefail curl -sS -D- http://127.0.0.1:18080/healthz