name: Pipeline on: push: branches: [master] tags: ['v*'] pull_request: branches: [master] jobs: # ── UI ──────────────────────────────────────────────────────────────────────── ui-test: name: UI Tests runs-on: ubuntu-latest container: image: node:22-bullseye steps: - uses: actions/checkout@v4 - name: Install Firefox run: apt-get update -qq && apt-get install -y --no-install-recommends firefox-esr - name: Cache node_modules uses: actions/cache@v3 with: path: ui/node_modules key: npm-${{ hashFiles('ui/package-lock.json') }} restore-keys: npm- - name: Install dependencies run: npm ci working-directory: ui - name: Run tests run: FIREFOX_BIN=/usr/bin/firefox-esr npx ng test --watch=false working-directory: ui ui-lint: name: UI Lint runs-on: ubuntu-latest container: image: node:22-bullseye steps: - uses: actions/checkout@v4 - name: Cache node_modules uses: actions/cache@v3 with: path: ui/node_modules key: npm-${{ hashFiles('ui/package-lock.json') }} restore-keys: npm- - name: Install dependencies run: npm ci working-directory: ui - name: Run ESLint run: npm run lint working-directory: ui # ── API ─────────────────────────────────────────────────────────────────────── api-unit: name: API Unit Tests runs-on: ubuntu-latest container: image: ghcr.io/astral-sh/uv:python3.12-bookworm steps: - name: Install Node (for JS actions) run: | apt-get update -qq apt-get install -y --no-install-recommends nodejs ca-certificates curl - uses: actions/checkout@v4 - name: Cache uv store uses: actions/cache@v3 with: path: /root/.cache/uv key: uv-${{ hashFiles('api/uv.lock') }} restore-keys: uv- - name: Install dependencies run: uv sync --group dev working-directory: api - name: Run unit tests run: uv run pytest tests/unit/ -q working-directory: api env: DATABASE_URL: postgresql+asyncpg://u:p@localhost/db S3_ENDPOINT_URL: http://localhost:9000 S3_BUCKET_NAME: test S3_ACCESS_KEY_ID: key S3_SECRET_ACCESS_KEY: secret S3_REGION: us-east-1 API_BASE_URL: http://localhost:8000 JWT_SECRET_KEY: d34db33fc4f3b00bd34db33fc4f3b00b OWNER_USERNAME: testowner OWNER_PASSWORD: testpassword api-lint: name: API Lint runs-on: ubuntu-latest container: image: ghcr.io/astral-sh/uv:python3.12-bookworm steps: - name: Install Node (for JS actions) run: | apt-get update -qq apt-get install -y --no-install-recommends nodejs ca-certificates curl - uses: actions/checkout@v4 - name: Run Ruff run: uvx ruff check . working-directory: api api-integration: name: API Integration Tests runs-on: ubuntu-latest container: image: ghcr.io/astral-sh/uv:python3.12-bookworm services: postgres: image: postgres:16 env: POSTGRES_USER: reactbin POSTGRES_PASSWORD: reactbin POSTGRES_DB: reactbin_test options: >- --health-cmd "pg_isready -U reactbin -d reactbin_test" --health-interval 5s --health-timeout 5s --health-retries 10 minio: image: bitnamilegacy/minio:2025.7.23-debian-12-r5 env: MINIO_ROOT_USER: minioadmin MINIO_ROOT_PASSWORD: minioadmin MINIO_DEFAULT_BUCKETS: reactbin-test options: >- --health-cmd "mc ready local || exit 1" --health-interval 5s --health-timeout 5s --health-retries 10 steps: - name: Install Node and curl (for JS actions and mc) run: | apt-get update -qq apt-get install -y --no-install-recommends nodejs ca-certificates curl - uses: actions/checkout@v4 - name: Cache uv store uses: actions/cache@v3 with: path: /root/.cache/uv key: uv-${{ hashFiles('api/uv.lock') }} restore-keys: uv- - name: Install dependencies run: uv sync --group dev working-directory: api - name: Run integration tests run: uv run pytest tests/integration/ -q working-directory: api env: TEST_DATABASE_URL: postgresql+asyncpg://reactbin:reactbin@postgres/reactbin_test DATABASE_URL: postgresql+asyncpg://reactbin:reactbin@postgres/reactbin_test S3_ENDPOINT_URL: http://minio:9000 S3_BUCKET_NAME: reactbin-test S3_ACCESS_KEY_ID: minioadmin S3_SECRET_ACCESS_KEY: minioadmin S3_REGION: us-east-1 API_BASE_URL: http://localhost:8000 JWT_SECRET_KEY: d34db33fc4f3b00bd34db33fc4f3b00b OWNER_USERNAME: testowner OWNER_PASSWORD: testpassword # ── Image builds (tag-only, gated on all jobs) ──────────────────────────────── build-api: name: Build & Push API Image runs-on: ubuntu-latest needs: [ui-test, ui-lint, api-unit, api-lint, api-integration] if: startsWith(github.ref, 'refs/tags/v') steps: - uses: actions/checkout@v4 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ${{ vars.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.REGISTRY_TOKEN }} - uses: docker/build-push-action@v6 with: context: ./api file: ./api/Dockerfile.prod push: true tags: | ${{ vars.REGISTRY }}/${{ vars.REPOSITORY }}/reactbin-api:${{ github.ref_name }} ${{ vars.REGISTRY }}/${{ vars.REPOSITORY }}/reactbin-api:latest build-ui: name: Build & Push UI Image runs-on: ubuntu-latest needs: [ui-test, ui-lint, api-unit, api-lint, api-integration] if: startsWith(github.ref, 'refs/tags/v') steps: - uses: actions/checkout@v4 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ${{ vars.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.REGISTRY_TOKEN }} - uses: docker/build-push-action@v6 with: context: ./ui file: ./ui/Dockerfile.prod push: true tags: | ${{ vars.REGISTRY }}/${{ vars.REPOSITORY }}/reactbin-ui:${{ github.ref_name }} ${{ vars.REGISTRY }}/${{ vars.REPOSITORY }}/reactbin-ui:latest