name: CI on: push: branches: [main] paths-ignore: ['**.md', 'docs/**', 'LICENSE'] pull_request: branches: [main] paths-ignore: ['**.md', 'docs/**', 'LICENSE'] workflow_call: concurrency: group: ci-${{ github.ref }} cancel-in-progress: true # ── Reusable workflow orchestration ───────────────────────────────── # Each concern lives in its own workflow file for maintainability: # ci-quality.yml — typecheck (tsc --noEmit) # ci-tests.yml — all tests with coverage (ubuntu) + cross-platform # ci-report.yml — PR comment (workflow_run trigger for fork write access) jobs: quality: uses: ./.github/workflows/ci-quality.yml permissions: contents: read tests: uses: ./.github/workflows/ci-tests.yml permissions: contents: read # ── Unified CI gate ────────────────────────────────────────────── # Single required check for branch protection. ci-status: name: CI Gate needs: [quality, tests] if: always() runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: Check all jobs passed shell: bash env: QUALITY: ${{ needs.quality.result }} TESTS: ${{ needs.tests.result }} run: | echo "Quality: $QUALITY" echo "Tests: $TESTS" if [[ "$QUALITY" != "success" ]] || [[ "$TESTS" != "success" ]]; then echo "::error::One or more CI jobs failed" exit 1 fi # ── PR metadata for ci-report.yml ──────────────────────────────── # Saves PR number and job results so the workflow_run-triggered # report can post comments with a write token (works for forks). save-pr-meta: name: Save PR Metadata if: always() && github.event_name == 'pull_request' needs: [quality, tests] runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: Write PR metadata shell: bash env: PR_NUMBER: ${{ github.event.pull_request.number }} QUALITY: ${{ needs.quality.result }} TESTS: ${{ needs.tests.result }} run: | mkdir -p pr-meta echo "$PR_NUMBER" > pr-meta/pr-number echo "$QUALITY" > pr-meta/quality-result echo "$TESTS" > pr-meta/tests-result - name: Upload PR metadata uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: pr-meta path: pr-meta/ retention-days: 1