Files
SnapOtter/.github/workflows/nightly.yml
T
SnapOtterandGitHub 0c8e2cfcde ci: harden testcontainers against Docker Hub registry flakes (#324)
Disable the redundant ryuk reaper (tests/global-setup.ts stops its containers
explicitly and CI runners are ephemeral) and pre-pull postgres/redis with retry,
so a transient Docker Hub 500 -- as hit pulling ryuk on #320's post-merge run --
cannot fail the suite. Covers ci.yml (unit + integration) and nightly.yml.
2026-06-22 22:26:47 +08:00

370 lines
14 KiB
YAML

name: Nightly
on:
workflow_dispatch:
schedule:
- cron: "0 3 * * *"
permissions:
contents: read
env:
SYSTEM_DEPS: libheif-examples libheif-plugin-x265 libheif-plugin-libde265 libimage-exiftool-perl libraw-bin imagemagick ghostscript libjxl-tools libopenjp2-tools ffmpeg qpdf
# See ci.yml: ryuk's Docker Hub pull is a recurring flake source; disable the reaper
# (tests/global-setup.ts stops its containers explicitly; runners are ephemeral).
TESTCONTAINERS_RYUK_DISABLED: "true"
jobs:
e2e-full:
name: E2E Full (${{ matrix.shard }}/4)
runs-on: ubuntu-latest
timeout-minutes: 60
services:
postgres:
image: postgres:17-alpine
env:
POSTGRES_USER: snapotter
POSTGRES_PASSWORD: snapotter
POSTGRES_DB: snapotter
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U snapotter"
--health-interval 5s
--health-timeout 3s
--health-retries 10
redis:
image: redis:8-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 5s
--health-timeout 3s
--health-retries 10
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Install system dependencies
run: sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends $SYSTEM_DEPS
- uses: ./.github/actions/setup
- name: Get Playwright version
id: pw-version
run: echo "version=$(node -p "require('@playwright/test/package.json').version")" >> "$GITHUB_OUTPUT"
- name: Cache Playwright browsers
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ steps.pw-version.outputs.version }}
- name: Install Playwright Chromium
run: pnpm playwright install --with-deps chromium
- name: Run full e2e suite (shard)
run: pnpm playwright test --project=chromium --shard=${{ matrix.shard }}/4
env:
PW_WORKERS: "2"
- name: Upload report on failure
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: failure()
with:
name: e2e-full-report-shard-${{ matrix.shard }}
path: playwright-report/
retention-days: 7
e2e-serial:
name: E2E Serial Bucket
runs-on: ubuntu-latest
timeout-minutes: 60
services:
postgres:
image: postgres:17-alpine
env:
POSTGRES_USER: snapotter
POSTGRES_PASSWORD: snapotter
POSTGRES_DB: snapotter
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U snapotter"
--health-interval 5s
--health-timeout 3s
--health-retries 10
redis:
image: redis:8-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 5s
--health-timeout 3s
--health-retries 10
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Install system dependencies
run: sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends $SYSTEM_DEPS
- uses: ./.github/actions/setup
- name: Get Playwright version
id: pw-version
run: echo "version=$(node -p "require('@playwright/test/package.json').version")" >> "$GITHUB_OUTPUT"
- name: Cache Playwright browsers
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ steps.pw-version.outputs.version }}
- name: Install Playwright Chromium
run: pnpm playwright install --with-deps chromium
- name: Run serial bucket (global-state specs)
run: pnpm playwright test --project=chromium-serial --workers=1
- name: Upload report on failure
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: failure()
with:
name: e2e-serial-report
path: playwright-report/
retention-days: 7
e2e-cross-browser:
name: E2E Cross-Browser (Firefox + WebKit)
runs-on: ubuntu-latest
timeout-minutes: 30
services:
postgres:
image: postgres:17-alpine
env:
POSTGRES_USER: snapotter
POSTGRES_PASSWORD: snapotter
POSTGRES_DB: snapotter
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U snapotter"
--health-interval 5s
--health-timeout 3s
--health-retries 10
redis:
image: redis:8-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 5s
--health-timeout 3s
--health-retries 10
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: ./.github/actions/setup
- name: Get Playwright version
id: pw-version
run: echo "version=$(node -p "require('@playwright/test/package.json').version")" >> "$GITHUB_OUTPUT"
- name: Cache Playwright browsers
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.cache/ms-playwright
key: playwright-all-${{ runner.os }}-${{ steps.pw-version.outputs.version }}
- name: Install Playwright browsers
run: pnpm playwright install --with-deps chromium firefox webkit
- name: Run cross-browser spec
run: pnpm playwright test --project=firefox --project=webkit
env:
PW_WORKERS: "1"
- name: Upload report on failure
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: failure()
with:
name: e2e-cross-browser-report
path: playwright-report/
retention-days: 7
e2e-device-matrix:
name: E2E Device Matrix (mobile + tablet)
runs-on: ubuntu-latest
timeout-minutes: 30
services:
postgres:
image: postgres:17-alpine
env:
POSTGRES_USER: snapotter
POSTGRES_PASSWORD: snapotter
POSTGRES_DB: snapotter
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U snapotter"
--health-interval 5s
--health-timeout 3s
--health-retries 10
redis:
image: redis:8-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 5s
--health-timeout 3s
--health-retries 10
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Install system dependencies
run: sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends $SYSTEM_DEPS
- uses: ./.github/actions/setup
- name: Get Playwright version
id: pw-version
run: echo "version=$(node -p "require('@playwright/test/package.json').version")" >> "$GITHUB_OUTPUT"
- name: Cache Playwright browsers
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.cache/ms-playwright
key: playwright-all-${{ runner.os }}-${{ steps.pw-version.outputs.version }}
- name: Install Playwright browsers (chromium + webkit)
run: pnpm playwright install --with-deps chromium webkit
- name: Run all device projects
run: pnpm playwright test --project=mobile-chromium --project=mobile-webkit --project=tablet-webkit --project=tablet-chromium
env:
PW_WORKERS: "2"
- name: Upload report on failure
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: failure()
with:
name: e2e-device-matrix-report
path: playwright-report/
retention-days: 7
docker-e2e:
name: Docker Container E2E
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Run container test suite
run: docker compose -f docker/docker-compose.test.yml up --build --exit-code-from test-e2e
extended-matrix:
name: Extended Matrix + Fuzz
runs-on: ubuntu-latest
timeout-minutes: 90
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Install system dependencies
run: sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends $SYSTEM_DEPS
- name: Allow ImageMagick to read EPS/PS via Ghostscript delegate
run: |
POLICY_FILE=$(find /etc/ImageMagick* -name policy.xml 2>/dev/null | head -1)
if [ -n "$POLICY_FILE" ]; then
sudo sed -i 's/<policy domain="coder" rights="none" pattern="EPS"/<policy domain="coder" rights="read" pattern="EPS"/' "$POLICY_FILE"
sudo sed -i 's/<policy domain="coder" rights="none" pattern="PS"/<policy domain="coder" rights="read" pattern="PS"/' "$POLICY_FILE"
fi
- uses: ./.github/actions/setup
- name: Pre-pull testcontainer images (retry transient Docker Hub errors)
run: |
for img in postgres:17-alpine redis:8-alpine; do
ok=
for attempt in 1 2 3 4 5; do
if docker pull "$img"; then ok=1; break; fi
echo "::warning::docker pull $img failed (attempt $attempt/5); retrying in $((attempt * 5))s"
sleep "$((attempt * 5))"
done
[ -n "$ok" ] || { echo "::error::could not pull $img after 5 attempts"; exit 1; }
done
- name: Run integration suite with full matrix and fuzz enabled
run: pnpm vitest run tests/integration/ --reporter=verbose
env:
FULL_MATRIX: "1"
FUZZ: "1"
FUZZ_RUNS: "50"
api-fuzz:
name: Schemathesis API Fuzz
runs-on: ubuntu-latest
timeout-minutes: 45
services:
postgres:
image: postgres:17-alpine
env:
POSTGRES_USER: snapotter
POSTGRES_PASSWORD: snapotter
POSTGRES_DB: snapotter
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U snapotter"
--health-interval 5s
--health-timeout 3s
--health-retries 10
redis:
image: redis:8-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 5s
--health-timeout 3s
--health-retries 10
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Install system dependencies
run: sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends $SYSTEM_DEPS
- uses: ./.github/actions/setup
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- name: Install Schemathesis
run: pip install schemathesis
- name: Start API server
run: |
mkdir -p /tmp/st-data
AUTH_ENABLED=false ANALYTICS_ENABLED=false \
DATABASE_URL=postgres://snapotter:snapotter@localhost:5432/snapotter \
REDIS_URL=redis://localhost:6379 \
WORKSPACE_PATH=/tmp/st-data/workspace DATA_DIR=/tmp/st-data \
pnpm --filter @snapotter/api dev &
for i in $(seq 1 60); do
if curl -fsS http://localhost:13490/api/v1/health > /dev/null 2>&1; then
echo "API up after ${i}s"; exit 0
fi
sleep 1
done
echo "API failed to start"; exit 1
- name: Fuzz tool endpoints from the OpenAPI spec
run: |
schemathesis run http://localhost:13490/api/v1/openapi.yaml \
--url http://localhost:13490 \
--checks not_a_server_error \
--include-path-regex "^/api/v1/(tools|health|info)" \
--max-examples 25 \
--report junit \
--report-dir st-report
- name: Upload fuzz report
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: always()
with:
name: schemathesis-report
path: st-report/
retention-days: 14
coverage-report:
name: Coverage Report
runs-on: ubuntu-latest
timeout-minutes: 90
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Install system dependencies
run: sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends $SYSTEM_DEPS
- name: Allow ImageMagick to read EPS/PS via Ghostscript delegate
run: |
POLICY_FILE=$(find /etc/ImageMagick* -name policy.xml 2>/dev/null | head -1)
if [ -n "$POLICY_FILE" ]; then
sudo sed -i 's/<policy domain="coder" rights="none" pattern="EPS"/<policy domain="coder" rights="read" pattern="EPS"/' "$POLICY_FILE"
sudo sed -i 's/<policy domain="coder" rights="none" pattern="PS"/<policy domain="coder" rights="read" pattern="PS"/' "$POLICY_FILE"
fi
- uses: ./.github/actions/setup
- name: Run tests with coverage
run: pnpm vitest run --coverage tests/unit/ tests/integration/
- name: Upload coverage artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-report
path: coverage/
retention-days: 14