name: CI on: push: branches: - "**" pull_request: concurrency: group: ${{ github.ref }} cancel-in-progress: true jobs: # ─── 1. Generate OpenAPI types ──────────────────────────────────────────── generate-types: name: Generate TypeScript types runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "22" - run: corepack enable - run: cd types && yarn install --frozen-lockfile && yarn openapi-ts - uses: actions/upload-artifact@v3 with: name: types-gen path: types/gen # ─── 2a. Server unit tests ──────────────────────────────────────────────── server-test: name: Server unit tests runs-on: ubuntu-latest needs: generate-types env: NODE_ENV: test JWT_SECRET: test-secret-min-32-chars-aaaaaaa! MOCK_DATA: "true" STORAGE: json steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "22" - run: corepack enable - uses: actions/download-artifact@v3 with: name: types-gen path: types/gen - run: cd server && yarn install --frozen-lockfile && yarn test # ─── 2b. Build server ───────────────────────────────────────────────────── server-build: name: Build server runs-on: ubuntu-latest needs: generate-types steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "22" - run: corepack enable - uses: actions/download-artifact@v3 with: name: types-gen path: types/gen - run: cd types && yarn install --frozen-lockfile - run: cd server && yarn install --frozen-lockfile && yarn build - uses: actions/upload-artifact@v3 with: name: server-dist path: server/dist # ─── 2c. Build client ───────────────────────────────────────────────────── client-build: name: Build client runs-on: ubuntu-latest needs: generate-types steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "22" - run: corepack enable - uses: actions/download-artifact@v3 with: name: types-gen path: types/gen - run: cd types && yarn install --frozen-lockfile - run: cd client && yarn install --frozen-lockfile && yarn build - uses: actions/upload-artifact@v3 with: name: client-dist path: client/dist # ─── 3. Playwright E2E tests ────────────────────────────────────────────── e2e: name: Playwright E2E tests runs-on: ubuntu-latest needs: [ server-build, client-build ] container: mcr.microsoft.com/playwright:v1.59.1-jammy services: redis: image: redis/redis-stack-server:7.4.0-v1 env: REDIS_ARGS: "--save '' --loglevel warning" env: CI: "true" NODE_ENV: test JWT_SECRET: test-secret-min-32-chars-aaaaaaa! MOCK_DATA: "true" STORAGE: redis REDIS_HOST: redis REDIS_PORT: "6379" HTTP_REMOTE_USER_ENABLED: "true" HTTP_REMOTE_USER_HEADER_NAME: remote-user HTTP_REMOTE_TRUSTED_IPS: "127.0.0.1,::1,::ffff:127.0.0.1" steps: - uses: actions/checkout@v4 - uses: actions/download-artifact@v3 with: name: server-dist path: server/dist - uses: actions/download-artifact@v3 with: name: client-dist path: client/dist - name: Install server dependencies run: cd server && yarn install --frozen-lockfile - name: Copy client build into server/public run: cp -r client/dist server/public - name: Install e2e dependencies and browsers run: cd e2e && yarn install --frozen-lockfile && yarn playwright install firefox --with-deps - name: Run Playwright tests run: cd e2e && yarn test - uses: actions/upload-artifact@v3 if: failure() with: name: playwright-report path: | e2e/playwright-report e2e/test-results # ─── 4. Build and push Docker image (master only) ───────────────────────── docker-build: name: Build and push Docker image runs-on: ubuntu-latest needs: [ server-build, client-build, server-test, e2e ] if: github.event_name == 'push' && github.ref == 'refs/heads/master' steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "22" - run: corepack enable - uses: actions/download-artifact@v3 with: name: server-dist path: server/dist - uses: actions/download-artifact@v3 with: name: client-dist path: client/dist - name: Install server production dependencies run: cd server && yarn install --frozen-lockfile --production - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ${{ secrets.REPO_URL }} username: ${{ secrets.REPO_USERNAME }} password: ${{ secrets.REPO_PASSWORD }} - uses: docker/build-push-action@v5 with: context: . file: Dockerfile-Woodpecker platforms: linux/amd64 push: true tags: ${{ secrets.REPO_URL }}/${{ secrets.REPO_NAME }}:latest # ─── 5. Notifications ──────────────────────── notify: name: Notify runs-on: ubuntu-latest needs: [ server-build, client-build, server-test, e2e, docker-build ] if: always() && github.event_name == 'push' steps: - name: Send webhook env: DISCORD_WEBHOOK_ID: ${{ secrets.DISCORD_WEBHOOK_ID }} DISCORD_WEBHOOK_TOKEN: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} NTFY_URL: ${{ secrets.NTFY_URL }} BUILD_RESULT: ${{ needs.docker-build.result }} RUN_NUMBER: ${{ github.run_number }} RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} COMMIT_MESSAGE: ${{ github.event.head_commit.message }} COMMIT_AUTHOR: ${{ github.event.head_commit.author.name }} run: | if [ "$BUILD_RESULT" = "success" ]; then MSG="✅ Sestavení #${RUN_NUMBER} proběhlo úspěšně." NTFY_TAGS="white_check_mark" else MSG="❌ Sestavení #${RUN_NUMBER} selhalo." NTFY_TAGS="x" fi FULL_MSG="$(printf '%s\n\nPipeline: %s\nPoslední commit: %sAutor: %s' \ "$MSG" "$RUN_URL" "$COMMIT_MESSAGE" "$COMMIT_AUTHOR")" curl -s -X POST \ "https://discord.com/api/webhooks/${DISCORD_WEBHOOK_ID}/${DISCORD_WEBHOOK_TOKEN}" \ -H "Content-Type: application/json" \ --data "$(jq -n --arg content "$FULL_MSG" '{content: $content}')" curl -s -X POST "${NTFY_URL}" \ -H "Title: Luncher CI #${RUN_NUMBER}" \ -H "Tags: ${NTFY_TAGS}" \ -H "Click: ${RUN_URL}" \ -d "${FULL_MSG}"