name: CI - Pull Request on: pull_request: branches: - main - develop types: [opened, synchronize, reopened] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: NODE_VERSION: '20' PNPM_VERSION: '9.15.0' TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_TEAM: ${{ secrets.TURBO_TEAM }} jobs: # Detect which projects have changed detect-changes: name: Detect Changed Projects runs-on: ubuntu-latest outputs: projects: ${{ steps.filter.outputs.changes }} has-changes: ${{ steps.filter.outputs.changes != '[]' }} steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Detect changed projects uses: dorny/paths-filter@v3 id: filter with: filters: | chat: - 'apps/chat/**' - 'packages/**' manacore: - 'apps/manacore/**' - 'packages/**' packages: - 'packages/**' # Lint and format check lint-and-format: name: Lint & Format Check runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.has-changes == 'true' steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup pnpm uses: pnpm/action-setup@v4 with: version: ${{ env.PNPM_VERSION }} - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Run format check run: pnpm run format:check continue-on-error: true - name: Run lint run: pnpm run lint --filter='./apps/chat/**' --filter='./apps/manacore/**' continue-on-error: true # Type checking type-check: name: Type Check runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.has-changes == 'true' steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup pnpm uses: pnpm/action-setup@v4 with: version: ${{ env.PNPM_VERSION }} - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Build shared packages run: pnpm run build:packages - name: Run type check run: pnpm run type-check --filter='./apps/chat/**' --filter='./apps/manacore/**' continue-on-error: true # Build all affected projects build: name: Build Projects runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.has-changes == 'true' strategy: matrix: project: ${{ fromJSON(needs.detect-changes.outputs.projects) }} fail-fast: false steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup pnpm uses: pnpm/action-setup@v4 with: version: ${{ env.PNPM_VERSION }} - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Build shared packages run: pnpm run build:packages - name: Build project - ${{ matrix.project }} run: | if [ "${{ matrix.project }}" == "packages" ]; then pnpm run build --filter=@manacore/* else pnpm run build --filter='./apps/${{ matrix.project }}/**' fi continue-on-error: true - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: build-${{ matrix.project }} path: | apps/${{ matrix.project }}/**/dist apps/${{ matrix.project }}/**/.next apps/${{ matrix.project }}/**/.svelte-kit apps/${{ matrix.project }}/**/.astro services/**/dist retention-days: 7 if-no-files-found: ignore # Run tests test: name: Run Tests runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.has-changes == 'true' strategy: matrix: project: ${{ fromJSON(needs.detect-changes.outputs.projects) }} fail-fast: false steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup pnpm uses: pnpm/action-setup@v4 with: version: ${{ env.PNPM_VERSION }} - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Build shared packages run: pnpm run build:packages - name: Run tests - ${{ matrix.project }} run: | if [ "${{ matrix.project }}" == "packages" ]; then pnpm run test --filter=@manacore/* || echo "No tests found for packages" else pnpm run test --filter='./apps/${{ matrix.project }}/**' || echo "No tests found for ${{ matrix.project }}" fi continue-on-error: true - name: Upload test coverage uses: actions/upload-artifact@v4 with: name: coverage-${{ matrix.project }} path: | apps/${{ matrix.project }}/**/coverage services/**/coverage retention-days: 7 if-no-files-found: ignore # Docker build validation for backend services docker-build-check: name: Docker Build Check runs-on: ubuntu-latest needs: detect-changes if: contains(needs.detect-changes.outputs.projects, 'chat') strategy: matrix: service: - { name: 'chat-backend', path: 'apps/chat/apps/backend' } fail-fast: false steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Check if Dockerfile exists id: check-dockerfile run: | if [ -f "${{ matrix.service.path }}/Dockerfile" ]; then echo "exists=true" >> $GITHUB_OUTPUT else echo "exists=false" >> $GITHUB_OUTPUT fi - name: Build Docker image if: steps.check-dockerfile.outputs.exists == 'true' uses: docker/build-push-action@v5 with: context: . file: ${{ matrix.service.path }}/Dockerfile push: false tags: ${{ matrix.service.name }}:pr-${{ github.event.pull_request.number }} cache-from: type=gha cache-to: type=gha,mode=max build-args: | NODE_ENV=production # Security scanning security-scan: name: Security Scan runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.has-changes == 'true' steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup pnpm uses: pnpm/action-setup@v4 with: version: ${{ env.PNPM_VERSION }} - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Run security audit run: pnpm audit --audit-level=high continue-on-error: true - name: Check for outdated dependencies run: pnpm outdated continue-on-error: true # PR status check (required for merge) pr-checks-complete: name: All PR Checks Complete runs-on: ubuntu-latest needs: [lint-and-format, type-check, build, test, docker-build-check, security-scan] if: always() steps: - name: Check all jobs status run: | if [ "${{ needs.lint-and-format.result }}" == "failure" ] || \ [ "${{ needs.type-check.result }}" == "failure" ] || \ [ "${{ needs.build.result }}" == "failure" ]; then echo "One or more required checks failed" exit 1 fi echo "All required checks passed" - name: PR summary run: | echo "## PR Checks Summary" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| Check | Status |" >> $GITHUB_STEP_SUMMARY echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY echo "| Lint & Format | ${{ needs.lint-and-format.result }} |" >> $GITHUB_STEP_SUMMARY echo "| Type Check | ${{ needs.type-check.result }} |" >> $GITHUB_STEP_SUMMARY echo "| Build | ${{ needs.build.result }} |" >> $GITHUB_STEP_SUMMARY echo "| Tests | ${{ needs.test.result }} |" >> $GITHUB_STEP_SUMMARY echo "| Docker Build | ${{ needs.docker-build-check.result }} |" >> $GITHUB_STEP_SUMMARY echo "| Security Scan | ${{ needs.security-scan.result }} |" >> $GITHUB_STEP_SUMMARY