From 7ed3c3b5e4b9e212d23d14a93416a5ed54d9e988 Mon Sep 17 00:00:00 2001 From: continuist Date: Fri, 5 Sep 2025 16:41:23 -0400 Subject: [PATCH] Update .forgejo/workflows/ci.yml copied from wrong file .github/workflows/ci.yml --- .forgejo/workflows/ci.yml | 214 +++++++++++++++++++++++--------------- 1 file changed, 129 insertions(+), 85 deletions(-) diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index 6618521..ecd75fe 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -11,202 +11,246 @@ env: APP_NAME: ${{ secrets.APP_NAME }} IMAGE_TAG: ${{ github.sha }} RUN_ID: ${{ github.run_id }} + # Required pinned digests (fail if missing) + RUST_IMG_DIGEST: ${{ secrets.RUST_IMG_DIGEST }} # e.g., docker.io/library/rust@sha256:... + NODE_IMG_DIGEST: ${{ secrets.NODE_IMG_DIGEST }} # e.g., docker.io/library/node@sha256:... + POSTGRES_IMG_DIGEST: ${{ secrets.POSTGRES_IMG_DIGEST }} # e.g., docker.io/library/postgres@sha256:... + PODMAN_CLIENT_IMG_DIGEST: ${{ secrets.PODMAN_CLIENT_IMG_DIGEST }} # e.g., quay.io/podman/stable@sha256:... + SELINUX_ZLABEL: "" # set to ":z" if SELinux is enforcing jobs: test-backend: runs-on: [self-hosted, ci] steps: - - name: Checkout code - uses: actions/checkout@v4 + - uses: actions/checkout@v4 + + - name: Verify pinned digests provided + run: | + for v in RUST_IMG_DIGEST NODE_IMG_DIGEST POSTGRES_IMG_DIGEST PODMAN_CLIENT_IMG_DIGEST; do + [ -n "${!v}" ] || { echo "Missing $v"; exit 1; } + echo "${!v}" | grep -Eq '^.+@sha256:[0-9a-f]{64}' || { echo "$v must be a digest ref"; exit 1; } + done - name: Setup ephemeral PiP container + env: + PODMAN_CLIENT_IMG_DIGEST: ${{ env.PODMAN_CLIENT_IMG_DIGEST }} run: | chmod +x ./secure_pip_setup.sh ./secure_pip_setup.sh - name: Wait for PiP readiness + env: + PIP_NAME: ci-pip-${{ env.RUN_ID }} run: | chmod +x ./pip_ready.sh ./pip_ready.sh - - name: Setup SSH with pinned known_hosts + - name: Setup SSH with pinned known_hosts (per-step) + env: + GIT_SSH_COMMAND: 'ssh -o StrictHostKeyChecking=yes -o UserKnownHostsFile=$HOME/.ssh/known_hosts' run: | - mkdir -p ~/.ssh - chmod 700 ~/.ssh - echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519 - chmod 600 ~/.ssh/id_ed25519 - echo "${{ secrets.SSH_KNOWN_HOSTS }}" > ~/.ssh/known_hosts - chmod 644 ~/.ssh/known_hosts - git config --global core.sshCommand "ssh -o StrictHostKeyChecking=yes -o UserKnownHostsFile=$HOME/.ssh/known_hosts" + mkdir -p ~/.ssh && chmod 700 ~/.ssh + install -m 600 /dev/null ~/.ssh/id_ed25519 + printf '%s' "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519 + install -m 644 /dev/null ~/.ssh/known_hosts + printf '%s\n' "${{ secrets.SSH_KNOWN_HOSTS }}" > ~/.ssh/known_hosts + # git commands in this step will use $GIT_SSH_COMMAND - - name: Create integration test network - run: | - podman exec ci-pip-$RUN_ID podman network create integ-$RUN_ID + - name: Create internal integration network + run: podman exec ci-pip-${{ env.RUN_ID }} podman network create --internal integ-${{ env.RUN_ID }} - - name: Start PostgreSQL on internal network + - name: Start PostgreSQL on internal network (digest-pinned) run: | - podman exec ci-pip-$RUN_ID podman run -d \ + podman exec ci-pip-${{ env.RUN_ID }} podman run -d \ --name test-postgres \ - --network integ-$RUN_ID \ + --network integ-${{ env.RUN_ID }} \ -e POSTGRES_PASSWORD=testpassword \ -e POSTGRES_USER=testuser \ -e POSTGRES_DB=testdb \ - postgres:15-alpine@sha256:def456abc1237890def456abc1237890def456abc1237890def456abc1237890 + "${POSTGRES_IMG_DIGEST}" - name: Wait for PostgreSQL to be ready run: | - podman exec ci-pip-$RUN_ID sh -lc \ - 'timeout 60 sh -c "until podman exec test-postgres pg_isready -h test-postgres -p 5432 -U testuser; do sleep 1; done"' + podman exec ci-pip-${{ env.RUN_ID }} sh -lc ' + i=0; until podman exec test-postgres pg_isready -h 127.0.0.1 -p 5432 -U testuser >/dev/null 2>&1; do + i=$((i+1)); [ $i -gt 60 ] && { echo "pg not ready"; exit 1; }; sleep 1 + done' - name: Run backend unit tests + env: + WORKSPACE: ${{ github.workspace }} run: | - podman exec -e WORKSPACE="${GITHUB_WORKSPACE}" ci-pip-$RUN_ID sh -lc \ - 'podman run --rm \ - -v "$WORKSPACE":/workspace \ - -w /workspace \ - rust@sha256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef \ - sh -c "cargo test --lib -- --test-threads=1"' + podman exec ci-pip-${{ env.RUN_ID }} sh -lc ' + podman run --rm \ + -v "$WORKSPACE":/workspace${SELINUX_ZLABEL} \ + -w /workspace \ + "'"${RUST_IMG_DIGEST}"'" \ + sh -c "cargo test --lib -- --test-threads=1"' - name: Run backend integration tests + env: + WORKSPACE: ${{ github.workspace }} + RUN_ID: ${{ env.RUN_ID }} run: | - podman exec -e WORKSPACE="${GITHUB_WORKSPACE}" -e RUN_ID="${RUN_ID}" ci-pip-$RUN_ID sh -lc ' + podman exec ci-pip-${{ env.RUN_ID }} sh -lc ' podman run --rm \ - --network integ-$RUN_ID \ - -v "$WORKSPACE":/workspace \ + --network integ-'"$RUN_ID"' \ + -v "$WORKSPACE":/workspace'"${SELINUX_ZLABEL}"' \ -w /workspace \ -e DATABASE_URL=postgres://testuser:testpassword@test-postgres:5432/testdb \ - rust@sha256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef \ + "'"${RUST_IMG_DIGEST}"'" \ sh -c "cargo test --test '"'"'*'"'"' -- --test-threads=1"' - name: Cleanup test resources if: always() run: | - podman exec ci-pip-$RUN_ID podman stop test-postgres 2>/dev/null || true - podman exec ci-pip-$RUN_ID podman rm test-postgres 2>/dev/null || true - podman exec ci-pip-$RUN_ID podman network rm integ-$RUN_ID 2>/dev/null || true + podman exec ci-pip-${{ env.RUN_ID }} podman rm -f test-postgres 2>/dev/null || true + podman exec ci-pip-${{ env.RUN_ID }} podman network rm integ-${{ env.RUN_ID }} 2>/dev/null || true - - name: Per-job cleanup (container only) + - name: Per-job cleanup (PiP container) if: always() - run: | - # Only cleanup the PiP container, socket is managed by systemd service - podman rm -f ci-pip-$RUN_ID 2>/dev/null || true + run: podman rm -f ci-pip-${{ env.RUN_ID }} 2>/dev/null || true test-frontend: runs-on: [self-hosted, ci] needs: test-backend steps: - - name: Checkout code - uses: actions/checkout@v4 + - uses: actions/checkout@v4 + + - name: Verify pinned digests provided + run: | + for v in NODE_IMG_DIGEST PODMAN_CLIENT_IMG_DIGEST; do + [ -n "${!v}" ] || { echo "Missing $v"; exit 1; } + echo "${!v}" | grep -Eq '^.+@sha256:[0-9a-f]{64}' || { echo "$v must be a digest ref"; exit 1; } + done - name: Setup ephemeral PiP container + env: + PODMAN_CLIENT_IMG_DIGEST: ${{ env.PODMAN_CLIENT_IMG_DIGEST }} run: | chmod +x ./secure_pip_setup.sh ./secure_pip_setup.sh - name: Wait for PiP readiness + env: + PIP_NAME: ci-pip-${{ env.RUN_ID }} run: | chmod +x ./pip_ready.sh ./pip_ready.sh - - name: Run frontend tests in PiP + - name: Run frontend tests (digest-pinned) + env: + WORKSPACE: ${{ github.workspace }} run: | - podman exec -e WORKSPACE="${GITHUB_WORKSPACE}" ci-pip-$RUN_ID sh -lc \ - 'podman run --rm \ - -v "$WORKSPACE":/workspace \ - -w /workspace \ - node:20@sha256:7890abcdef1234567890abcdef1234567890abcdef1234567890abcdef123456 \ - sh -c "npm ci && npm run test"' + podman exec ci-pip-${{ env.RUN_ID }} sh -lc ' + podman run --rm \ + -v "$WORKSPACE":/workspace'"${SELINUX_ZLABEL}"' \ + -w /workspace \ + "'"${NODE_IMG_DIGEST}"'" \ + sh -c "npm ci && npm run test"' - - name: Per-job cleanup (container only) + - name: Per-job cleanup (PiP container) if: always() - run: | - # Only cleanup the PiP container, socket is managed by systemd service - podman rm -f ci-pip-$RUN_ID 2>/dev/null || true + run: podman rm -f ci-pip-${{ env.RUN_ID }} 2>/dev/null || true build-backend: runs-on: [self-hosted, ci] needs: test-frontend steps: - - name: Checkout code - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - name: Setup ephemeral PiP container + env: + PODMAN_CLIENT_IMG_DIGEST: ${{ env.PODMAN_CLIENT_IMG_DIGEST }} run: | chmod +x ./secure_pip_setup.sh ./secure_pip_setup.sh - name: Wait for PiP readiness + env: + PIP_NAME: ci-pip-${{ env.RUN_ID }} run: | chmod +x ./pip_ready.sh ./pip_ready.sh - name: Login to Forgejo Container Registry securely + env: + REGISTRY_HOST: ${{ env.REGISTRY }} + REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} + REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} run: | - echo "${{ secrets.REGISTRY_TOKEN }}" | podman exec -i ci-pip-$RUN_ID podman login ${{ secrets.REGISTRY_HOST }} \ - -u ${{ secrets.REGISTRY_USERNAME }} \ - --password-stdin + podman exec -i ci-pip-${{ env.RUN_ID }} sh -lc \ + 'podman login "$REGISTRY_HOST" -u "$REGISTRY_USERNAME" --password-stdin' <<<"${REGISTRY_TOKEN}" - name: Build backend image + env: + REGISTRY: ${{ env.REGISTRY }} + APP_NAME: ${{ env.APP_NAME }} + IMAGE_TAG: ${{ env.IMAGE_TAG }} run: | - podman exec -e REGISTRY="$REGISTRY" -e APP_NAME="$APP_NAME" -e IMAGE_TAG="$IMAGE_TAG" \ - ci-pip-$RUN_ID sh -lc 'cd /workspace/backend && podman build -t "$REGISTRY/$APP_NAME/backend:$IMAGE_TAG" .' + podman exec ci-pip-${{ env.RUN_ID }} sh -lc \ + 'cd /workspace/backend && podman build -t "$REGISTRY/$APP_NAME/backend:$IMAGE_TAG" .' - name: Push backend image + env: + REGISTRY: ${{ env.REGISTRY }} + APP_NAME: ${{ env.APP_NAME }} + IMAGE_TAG: ${{ env.IMAGE_TAG }} run: | - podman exec -e REGISTRY="$REGISTRY" -e APP_NAME="$APP_NAME" -e IMAGE_TAG="$IMAGE_TAG" \ - ci-pip-$RUN_ID sh -lc 'podman push "$REGISTRY/$APP_NAME/backend:$IMAGE_TAG"' + podman exec ci-pip-${{ env.RUN_ID }} sh -lc \ + 'podman push "$REGISTRY/$APP_NAME/backend:$IMAGE_TAG"' - - name: Per-job cleanup (container only) + - name: Per-job cleanup (PiP container) if: always() - run: | - # Only cleanup the PiP container, socket is managed by systemd service - podman rm -f ci-pip-$RUN_ID 2>/dev/null || true + run: podman rm -f ci-pip-${{ env.RUN_ID }} 2>/dev/null || true build-frontend: runs-on: [self-hosted, ci] needs: test-frontend steps: - - name: Checkout code - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - name: Setup ephemeral PiP container + env: + PODMAN_CLIENT_IMG_DIGEST: ${{ env.PODMAN_CLIENT_IMG_DIGEST }} run: | chmod +x ./secure_pip_setup.sh ./secure_pip_setup.sh - name: Wait for PiP readiness + env: + PIP_NAME: ci-pip-${{ env.RUN_ID }} run: | chmod +x ./pip_ready.sh ./pip_ready.sh - name: Login to Forgejo Container Registry securely + env: + REGISTRY_HOST: ${{ env.REGISTRY }} + REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} + REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} run: | - echo "${{ secrets.REGISTRY_TOKEN }}" | podman exec -i ci-pip-$RUN_ID podman login ${{ secrets.REGISTRY_HOST }} \ - -u ${{ secrets.REGISTRY_USERNAME }} \ - --password-stdin + podman exec -i ci-pip-${{ env.RUN_ID }} sh -lc \ + 'podman login "$REGISTRY_HOST" -u "$REGISTRY_USERNAME" --password-stdin' <<<"${REGISTRY_TOKEN}" - name: Build frontend image + env: + REGISTRY: ${{ env.REGISTRY }} + APP_NAME: ${{ env.APP_NAME }} + IMAGE_TAG: ${{ env.IMAGE_TAG }} run: | - podman exec -e REGISTRY="$REGISTRY" -e APP_NAME="$APP_NAME" -e IMAGE_TAG="$IMAGE_TAG" \ - ci-pip-$RUN_ID sh -lc 'cd /workspace/frontend && podman build -t "$REGISTRY/$APP_NAME/frontend:$IMAGE_TAG" .' + podman exec ci-pip-${{ env.RUN_ID }} sh -lc \ + 'cd /workspace/frontend && podman build -t "$REGISTRY/$APP_NAME/frontend:$IMAGE_TAG" .' - name: Push frontend image + env: + REGISTRY: ${{ env.REGISTRY }} + APP_NAME: ${{ env.APP_NAME }} + IMAGE_TAG: ${{ env.IMAGE_TAG }} run: | - podman exec -e REGISTRY="$REGISTRY" -e APP_NAME="$APP_NAME" -e IMAGE_TAG="$IMAGE_TAG" \ - ci-pip-$RUN_ID sh -lc 'podman push "$REGISTRY/$APP_NAME/frontend:$IMAGE_TAG"' + podman exec ci-pip-${{ env.RUN_ID }} sh -lc \ + 'podman push "$REGISTRY/$APP_NAME/frontend:$IMAGE_TAG"' - - name: Per-job cleanup (container only) + - name: Per-job cleanup (PiP container) if: always() - run: | - # Only cleanup the PiP container, socket is managed by systemd service - podman rm -f ci-pip-$RUN_ID 2>/dev/null || true - - cleanup: - runs-on: [self-hosted, ci] - needs: [build-backend, build-frontend] - if: always() - steps: - - name: Cleanup PiP container - run: | - # Only cleanup the PiP container, socket is managed by systemd service - podman rm -f ci-pip-$RUN_ID 2>/dev/null || true \ No newline at end of file + run: podman rm -f ci-pip-${{ env.RUN_ID }} 2>/dev/null || true \ No newline at end of file