mirror of
https://github.com/Cleanuparr/Cleanuparr.git
synced 2026-09-13 05:47:33 -04:00
253 lines
7.5 KiB
YAML
253 lines
7.5 KiB
YAML
name: E2E Tests
|
|
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
ref:
|
|
description: 'Git ref to checkout (branch, tag, or SHA). Defaults to the triggering ref.'
|
|
type: string
|
|
required: false
|
|
default: ''
|
|
workflow_dispatch:
|
|
|
|
concurrency:
|
|
group: E2E Tests-${{ inputs.ref || github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
permissions:
|
|
contents: read
|
|
actions: read
|
|
|
|
jobs:
|
|
suites:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
outputs:
|
|
list: ${{ steps.suites.outputs.list }}
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v7
|
|
timeout-minutes: 1
|
|
with:
|
|
ref: ${{ inputs.ref || github.ref }}
|
|
persist-credentials: false
|
|
|
|
- name: Build the suite matrix
|
|
id: suites
|
|
working-directory: e2e
|
|
run: echo "list=$(bash ./scripts/ci-matrix.sh)" >> "$GITHUB_OUTPUT"
|
|
|
|
build-app:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
variant: [plain, patched]
|
|
|
|
name: build-app (${{ matrix.variant }})
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v7
|
|
timeout-minutes: 1
|
|
with:
|
|
ref: ${{ inputs.ref || github.ref }}
|
|
persist-credentials: false
|
|
|
|
- name: Get vault secrets
|
|
uses: hashicorp/vault-action@v4
|
|
with:
|
|
url: ${{ secrets.VAULT_HOST }}
|
|
method: approle
|
|
roleId: ${{ secrets.VAULT_ROLE_ID }}
|
|
secretId: ${{ secrets.VAULT_SECRET_ID }}
|
|
secrets:
|
|
secrets/data/github packages_pat | PACKAGES_PAT
|
|
|
|
# Nothing reverts these. The runner throws the checkout away.
|
|
- name: Apply the e2e patches
|
|
if: matrix.variant == 'patched'
|
|
run: bash e2e/scripts/with-patches.sh
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v4
|
|
timeout-minutes: 5
|
|
|
|
- name: Build the app image
|
|
uses: docker/build-push-action@v7
|
|
timeout-minutes: 25
|
|
with:
|
|
context: ${{ github.workspace }}/code
|
|
file: ${{ github.workspace }}/code/Dockerfile
|
|
provenance: false
|
|
build-args: |
|
|
PACKAGES_USERNAME=${{ github.repository_owner }}
|
|
secrets: |
|
|
packages_pat=${{ env.PACKAGES_PAT }}
|
|
tags: cleanuparr-e2e-app:${{ matrix.variant }}
|
|
outputs: type=docker,dest=/tmp/app-image.tar
|
|
cache-from: type=gha,scope=e2e-${{ matrix.variant }}
|
|
cache-to: type=gha,scope=e2e-${{ matrix.variant }},mode=max
|
|
|
|
# gzip -1 beats letting upload-artifact deflate a layer tar this size.
|
|
- name: Compress the image
|
|
run: gzip -1 /tmp/app-image.tar
|
|
|
|
- name: Upload the image
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: e2e-app-image-${{ matrix.variant }}
|
|
path: /tmp/app-image.tar.gz
|
|
retention-days: 1
|
|
compression-level: 0
|
|
|
|
e2e:
|
|
needs: suites
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 60
|
|
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
suite: ${{ fromJSON(needs.suites.outputs.list) }}
|
|
|
|
name: e2e (${{ matrix.suite.name }})
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v7
|
|
timeout-minutes: 1
|
|
with:
|
|
ref: ${{ inputs.ref || github.ref }}
|
|
persist-credentials: false
|
|
|
|
- name: Start the stack
|
|
working-directory: e2e
|
|
run: make ${{ matrix.suite.stack-target }}
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v7
|
|
with:
|
|
node-version: 26
|
|
cache: 'npm'
|
|
cache-dependency-path: e2e/package-lock.json
|
|
|
|
- name: Install E2E dependencies
|
|
working-directory: e2e
|
|
run: npm ci
|
|
|
|
- name: Resolve Playwright version
|
|
id: playwright
|
|
working-directory: e2e
|
|
run: echo "version=$(node -p "require('@playwright/test/package.json').version")" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Cache Playwright browsers
|
|
uses: actions/cache@v6
|
|
with:
|
|
path: ~/.cache/ms-playwright
|
|
key: ${{ runner.os }}-playwright-${{ steps.playwright.outputs.version }}
|
|
|
|
- name: Report runner disk space
|
|
run: df -h /
|
|
|
|
- name: Install Playwright browsers
|
|
working-directory: e2e
|
|
timeout-minutes: 10
|
|
env:
|
|
DEBUG: pw:install
|
|
run: npx playwright install --with-deps chromium
|
|
|
|
- name: Wait for the app image
|
|
timeout-minutes: 30
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
VARIANT: ${{ matrix.suite.image-variant }}
|
|
RUN_JOBS: /repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/jobs
|
|
RUN_ARTIFACTS: /repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts
|
|
run: |
|
|
set -euo pipefail
|
|
artifact="e2e-app-image-$VARIANT"
|
|
job="build-app ($VARIANT)"
|
|
grace=12
|
|
|
|
# The artifact appears only once build-app uploads it. Watch that job too,
|
|
# so a build failure ends this leg instead of stalling it until the timeout.
|
|
while :; do
|
|
# Assigned, not piped into grep: under pipefail a grep that exits on the
|
|
# first match can SIGPIPE gh, making a found artifact read as not found.
|
|
id=$(gh api "$RUN_ARTIFACTS" --paginate \
|
|
--jq ".artifacts[] | select(.name == \"$artifact\") | .id" || true)
|
|
|
|
if [ -n "$id" ]; then
|
|
break
|
|
fi
|
|
|
|
# A transient API error must not end the leg, so keep polling.
|
|
conclusion=$(gh api "$RUN_JOBS" --paginate \
|
|
--jq ".jobs[] | select(.name == \"$job\") | .conclusion" || true)
|
|
|
|
case "$conclusion" in
|
|
''|null) ;;
|
|
success)
|
|
grace=$((grace - 1))
|
|
if [ "$grace" -le 0 ]; then
|
|
echo "::error::$job succeeded but $artifact never appeared"
|
|
exit 1
|
|
fi
|
|
;;
|
|
*)
|
|
echo "::error::$job concluded $conclusion, no image to run"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
sleep 10
|
|
done
|
|
|
|
- name: Download the app image
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: e2e-app-image-${{ matrix.suite.image-variant }}
|
|
path: /tmp
|
|
|
|
- name: Load the app image
|
|
run: gunzip -c /tmp/app-image.tar.gz | docker load
|
|
|
|
- name: Start the app
|
|
working-directory: e2e
|
|
env:
|
|
APP_IMAGE: cleanuparr-e2e-app:${{ matrix.suite.image-variant }}
|
|
run: make up-app
|
|
|
|
- name: Run E2E tests
|
|
working-directory: e2e
|
|
run: npx playwright test ${{ matrix.suite.projects }}
|
|
|
|
- name: Dump service logs
|
|
if: failure()
|
|
working-directory: e2e
|
|
run: |
|
|
docker compose -f docker-compose.e2e.yml ps
|
|
# A container can serve fine and still fail its check, so record what the check saw.
|
|
docker compose -f docker-compose.e2e.yml ps -q | xargs -r docker inspect \
|
|
--format '{{.Name}} {{with .State.Health}}{{.Status}}{{range .Log}} [exit {{.ExitCode}}] {{.Output}}{{end}}{{end}}'
|
|
docker compose -f docker-compose.e2e.yml logs --no-color --tail 500
|
|
|
|
- name: Upload test results
|
|
uses: actions/upload-artifact@v7
|
|
if: always()
|
|
with:
|
|
name: e2e-test-results-${{ matrix.suite.name }}
|
|
path: |
|
|
e2e/playwright-report/
|
|
e2e/test-results/
|
|
retention-days: 7
|
|
|
|
- name: Stop services
|
|
if: always()
|
|
working-directory: e2e
|
|
run: make down
|