Files
pocket-id/.github/workflows/e2e-tests.yml
ItalyPaleAle 2aa13fe06c feat: add FRANCIS_HOST to connect to a standalone Francis runtime
FRANCIS_HOST decides where the Francis actor runtime lives. When it is
empty or set to "embedded" (the default), Pocket ID starts the runtime inside its own process, backed by its own database.

Any other value is the address, or a comma-separated list of addresses, of a standalone Francis runtime. Pocket ID then connects to it as a remote actor host and starts no embedded runtime.

Note: connecting to a standalone runtime also needs FRANCIS_HOST_PSK or FRANCIS_HOST_JWT, and optionally (but recommended) FRANCIS_CA.
2026-08-31 14:56:45 +02:00

223 lines
7.8 KiB
YAML

name: E2E Tests
on:
push:
branches: [ main ]
paths-ignore:
- "docs/**"
- "**.md"
- ".github/**"
pull_request:
branches: [ main, breaking/** ]
paths-ignore:
- "docs/**"
- "**.md"
- ".github/**"
permissions:
contents: read
actions: write
id-token: write
jobs:
test:
if: github.event.pull_request.head.ref != 'i18n_crowdin'
runs-on: depot-ubuntu-24.04-32
strategy:
fail-fast: false
matrix:
# "francis" selects where the actor runtime lives: embedded in Pocket ID, or a standalone runtime it connects to
include:
- db: sqlite
storage: filesystem
francis: embedded
- db: postgres
storage: filesystem
francis: embedded
- db: sqlite
storage: s3
francis: embedded
- db: sqlite
storage: database
francis: embedded
- db: postgres
storage: database
francis: embedded
- db: sqlite
storage: filesystem
francis: remote
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup pnpm
uses: pnpm/action-setup@v6
- name: Setup Node.js
uses: actions/setup-node@v6.5.0
with:
node-version: 24
cache: "pnpm"
- name: Set up Depot CLI
uses: depot/setup-action@v1
- name: Set up Depot Docker builder
run: depot configure-docker
- name: Cache Playwright Browsers
uses: actions/cache@v5
id: playwright-cache
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('pnpm-lock.yaml') }}
- name: Cache PostgreSQL Docker image
uses: actions/cache@v5
id: postgres-cache
with:
path: /tmp/postgres-image.tar
key: postgres-17-${{ runner.os }}
- name: Pull and save PostgreSQL image
if: matrix.db == 'postgres' && steps.postgres-cache.outputs.cache-hit != 'true'
run: |
docker pull postgres:17
docker save postgres:17 > /tmp/postgres-image.tar
- name: Load PostgreSQL image
if: matrix.db == 'postgres' && steps.postgres-cache.outputs.cache-hit == 'true'
run: docker load < /tmp/postgres-image.tar
- name: Cache SCIM Test Server Docker image
uses: actions/cache@v5
id: scim-cache
with:
path: /tmp/scim-test-server-image.tar
key: scim-test-server-${{ runner.os }}
- name: Pull and save SCIM Test Server image
if: steps.scim-cache.outputs.cache-hit != 'true'
run: |
docker pull ghcr.io/pocket-id/scim-test-server
docker save ghcr.io/pocket-id/scim-test-server > /tmp/scim-test-server-image.tar
- name: Load SCIM Test Server image
if: steps.scim-cache.outputs.cache-hit == 'true'
run: docker load < /tmp/scim-test-server-image.tar
- name: Cache Localstack S3 Docker image
if: matrix.storage == 's3'
uses: actions/cache@v5
id: s3-cache
with:
path: /tmp/localstack-s3-image.tar
key: localstack-4.14.0-${{ runner.os }}
- name: Pull and save Localstack S3 image
if: matrix.storage == 's3' && steps.s3-cache.outputs.cache-hit != 'true'
run: |
docker pull localstack/localstack:4.14.0
docker save localstack/localstack:4.14.0 > /tmp/localstack-s3-image.tar
- name: Load Localstack S3 image
if: matrix.storage == 's3' && steps.s3-cache.outputs.cache-hit == 'true'
run: docker load < /tmp/localstack-s3-image.tar
- name: Resolve Francis runtime image
if: matrix.francis == 'remote'
id: francis-image
working-directory: ./tests/setup
# The Compose file is the single source of truth for the version, so the cache key follows it automatically
run: |
IMAGE=$(grep -oP '(?<=image: )ghcr\.io/italypaleale/francis:\S+' docker-compose-francis.yml)
echo "image=$IMAGE" >> "$GITHUB_OUTPUT"
echo "key=$(echo "$IMAGE" | tr '/:' '--')" >> "$GITHUB_OUTPUT"
- name: Cache Francis runtime Docker image
if: matrix.francis == 'remote'
uses: actions/cache@v5
id: francis-cache
with:
path: /tmp/francis-image.tar
key: ${{ steps.francis-image.outputs.key }}-${{ runner.os }}
- name: Pull and save Francis runtime image
if: matrix.francis == 'remote' && steps.francis-cache.outputs.cache-hit != 'true'
run: |
docker pull "${{ steps.francis-image.outputs.image }}"
docker save "${{ steps.francis-image.outputs.image }}" > /tmp/francis-image.tar
- name: Load Francis runtime image
if: matrix.francis == 'remote' && steps.francis-cache.outputs.cache-hit == 'true'
run: docker load < /tmp/francis-image.tar
- name: Install test dependencies
run: pnpm --filter pocket-id-tests install --frozen-lockfile
- name: Install Playwright Browsers
working-directory: ./tests
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: pnpm exec playwright install --with-deps chromium
- name: Run Docker containers
working-directory: ./tests/setup
run: |
DOCKER_COMPOSE_FILE=docker-compose.yml
cat > .env <<EOF
FILE_BACKEND=${{ matrix.storage }}
SCIM_SERVICE_PROVIDER_URL=http://localhost:18123/v2
SCIM_SERVICE_PROVIDER_URL_INTERNAL=http://scim-test-server:8080/v2
EOF
if [ "${{ matrix.francis }}" = "remote" ]; then
DOCKER_COMPOSE_FILE=docker-compose-francis.yml
elif [ "${{ matrix.db }}" = "postgres" ]; then
DOCKER_COMPOSE_FILE=docker-compose-postgres.yml
elif [ "${{ matrix.storage }}" = "s3" ]; then
DOCKER_COMPOSE_FILE=docker-compose-s3.yml
fi
docker compose -f "$DOCKER_COMPOSE_FILE" up -d --build
{
LOG_FILE="/tmp/backend.log"
while true; do
CID=$(docker compose -f "$DOCKER_COMPOSE_FILE" ps -q pocket-id)
if [ -n "$CID" ]; then
echo "[$(date)] Attaching logs for $CID" >> "$LOG_FILE"
docker logs -f --since=0 "$CID" >> "$LOG_FILE" 2>&1
else
echo "[$(date)] Container not yet running…" >> "$LOG_FILE"
fi
sleep 1
done
} &
if [ "${{ matrix.francis }}" = "remote" ]; then
docker compose -f "$DOCKER_COMPOSE_FILE" logs -f --no-log-prefix francis-runtime > /tmp/francis-runtime.log 2>&1 &
fi
- name: Run Playwright tests
working-directory: ./tests
run: pnpm exec playwright test
- name: Upload Test Report
uses: actions/upload-artifact@v7
if: always() && github.event.pull_request.head.ref != 'i18n_crowdin'
with:
name: playwright-report-${{ matrix.db }}-${{ matrix.storage }}-francis-${{ matrix.francis }}
path: tests/.report
include-hidden-files: true
retention-days: 15
- name: Upload Backend Test Report
uses: actions/upload-artifact@v7
if: always() && github.event.pull_request.head.ref != 'i18n_crowdin'
with:
name: backend-${{ matrix.db }}-${{ matrix.storage }}-francis-${{ matrix.francis }}
path: /tmp/backend.log
include-hidden-files: true
retention-days: 15
- name: Upload Francis Runtime Report
uses: actions/upload-artifact@v7
if: always() && matrix.francis == 'remote' && github.event.pull_request.head.ref != 'i18n_crowdin'
with:
name: francis-runtime-${{ matrix.db }}-${{ matrix.storage }}
path: /tmp/francis-runtime.log
include-hidden-files: true
retention-days: 15