Files
xdrop/.github/workflows/ci.yml
T

256 lines
6.9 KiB
YAML

name: CI
on:
pull_request:
paths-ignore:
- '**/*.md'
- 'docs/**'
- '.github/ISSUE_TEMPLATE/**'
- '.github/pull_request_template.md'
- 'AGENTS.md'
- 'CODE_OF_CONDUCT.md'
- 'CONTRIBUTING.md'
- 'LICENSE'
- 'SECURITY.md'
- 'SUPPORT.md'
push:
branches:
- main
paths-ignore:
- '**/*.md'
- 'docs/**'
- '.github/ISSUE_TEMPLATE/**'
- '.github/pull_request_template.md'
- 'AGENTS.md'
- 'CODE_OF_CONDUCT.md'
- 'CONTRIBUTING.md'
- 'LICENSE'
- 'SECURITY.md'
- 'SUPPORT.md'
workflow_dispatch:
concurrency:
group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
env:
COMPOSE_FILES: -f docker-compose.yml -f docker-compose.build.yml
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
jobs:
web:
name: Web Checks
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Check out repository
uses: actions/checkout@v6
- name: Set up Bun
uses: oven-sh/setup-bun@v2
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Check Prettier formatting
run: bun run format:check:prettier
- name: Lint web app
run: bun run lint:web
- name: Type-check web app
run: bun run typecheck:web
- name: Run web unit tests with coverage
run: bun run test:web:coverage
- name: Normalize web coverage paths for Codecov
run: |
python - <<'PY'
from pathlib import Path
source = Path('apps/web/coverage/lcov.info')
target = Path('apps/web/coverage/lcov.codecov.info')
normalized_lines = []
for line in source.read_text(encoding='utf-8').splitlines():
if line.startswith('SF:'):
path = line[3:].replace('\\', '/')
marker = '/apps/web/'
if marker in path:
path = 'apps/web/' + path.split(marker, 1)[1]
elif not path.startswith('apps/web/'):
path = 'apps/web/' + path.lstrip('./')
line = f'SF:{path}'
normalized_lines.append(line)
target.write_text('\n'.join(normalized_lines) + '\n', encoding='utf-8')
PY
- name: Preview web coverage source paths
shell: bash
run: |
grep '^SF:' apps/web/coverage/lcov.codecov.info | head -n 20
- name: Upload web coverage artifact
if: always()
uses: actions/upload-artifact@v7
with:
name: web-coverage
path: |
apps/web/coverage/lcov.info
apps/web/coverage/lcov.codecov.info
if-no-files-found: error
- name: Upload web coverage to Codecov
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && env.CODECOV_TOKEN != '' }}
uses: codecov/codecov-action@v6
with:
files: apps/web/coverage/lcov.codecov.info
flags: frontend
disable_search: true
token: ${{ env.CODECOV_TOKEN }}
fail_ci_if_error: false
- name: Build web app
run: bun run build:web
api:
name: API Checks
runs-on: ubuntu-latest
timeout-minutes: 20
defaults:
run:
working-directory: apps/api
steps:
- name: Check out repository
uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: apps/api/go.mod
cache-dependency-path: apps/api/go.sum
- name: Check gofmt formatting
shell: bash
run: |
unformatted="$(gofmt -l .)"
if [ -n "$unformatted" ]; then
echo "The following Go files are not gofmt-formatted:"
echo "$unformatted"
exit 1
fi
- name: Run API tests with coverage
shell: bash
run: go test $(go list ./... | grep -v '/internal/testutil$') -coverprofile=coverage.out -covermode=atomic
- name: Print API coverage summary
run: go tool cover -func=coverage.out
- name: Rewrite API coverage paths for Codecov
shell: bash
run: |
sed 's#^github.com/xdrop/monorepo/#apps/api/#' coverage.out > coverage.codecov.out
- name: Upload API coverage artifact
if: always()
uses: actions/upload-artifact@v7
with:
name: api-coverage
path: |
apps/api/coverage.out
apps/api/coverage.codecov.out
if-no-files-found: error
- name: Upload API coverage to Codecov
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && env.CODECOV_TOKEN != '' }}
uses: codecov/codecov-action@v6
with:
files: apps/api/coverage.codecov.out
flags: backend
disable_search: true
token: ${{ env.CODECOV_TOKEN }}
fail_ci_if_error: false
- name: Build API binary
run: go build -o /tmp/xdrop-api ./cmd/api
e2e:
name: Compose Smoke And E2E
runs-on: ubuntu-latest
timeout-minutes: 35
needs:
- web
- api
steps:
- name: Check out repository
uses: actions/checkout@v6
- name: Set up Bun
uses: oven-sh/setup-bun@v2
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Install Playwright browser
run: bunx playwright install --with-deps chromium
- name: Start Docker stack
run: docker compose ${{ env.COMPOSE_FILES }} up -d --build
- name: Wait for Xdrop endpoints
shell: bash
run: |
for _ in {1..60}; do
if curl -fsS http://localhost:8080/healthz >/dev/null && curl -fsS http://localhost:8080/ >/dev/null; then
exit 0
fi
sleep 5
done
echo "Xdrop endpoints did not become ready in time" >&2
exit 1
- name: Run browser E2E suite
run: bun run test:e2e
- name: Capture Docker logs
if: always()
run: docker compose ${{ env.COMPOSE_FILES }} logs --no-color > compose-logs.txt
- name: Upload Playwright report
if: always()
uses: actions/upload-artifact@v7
with:
name: playwright-report
path: playwright-report
if-no-files-found: ignore
- name: Upload Playwright test results
if: always()
uses: actions/upload-artifact@v7
with:
name: playwright-test-results
path: test-results
if-no-files-found: ignore
- name: Upload Docker logs
if: always()
uses: actions/upload-artifact@v7
with:
name: compose-logs
path: compose-logs.txt
if-no-files-found: ignore
- name: Shut down Docker stack
if: always()
run: docker compose ${{ env.COMPOSE_FILES }} down -v