CI/CD Pipeline #42
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI/CD Pipeline | |
| on: | |
| pull_request: | |
| branches: [master, develop, 'release/**'] | |
| types: [opened, synchronize, reopened, ready_for_review] | |
| push: | |
| branches: [master, develop] | |
| tags: ['v*'] | |
| workflow_dispatch: | |
| inputs: | |
| debug_enabled: | |
| type: boolean | |
| description: 'Enable debug logging' | |
| required: false | |
| default: false | |
| schedule: | |
| - cron: '0 2 * * 1' # Weekly dependency check | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: ${{ github.ref != 'refs/heads/master' }} | |
| env: | |
| NODE_ENV: ci | |
| FORCE_COLOR: 3 | |
| BUN_VERSION: 'latest' | |
| NODE_VERSION: '20' | |
| PNPM_VERSION: '8' | |
| permissions: | |
| contents: read | |
| checks: write | |
| pull-requests: write | |
| security-events: write | |
| issues: write | |
| packages: write | |
| jobs: | |
| # ======================================== | |
| # CHANGE DETECTION | |
| # ======================================== | |
| changes: | |
| name: 🔍 Detect Changes | |
| runs-on: ubuntu-latest | |
| outputs: | |
| src: ${{ steps.changes.outputs.src }} | |
| tests: ${{ steps.changes.outputs.tests }} | |
| deps: ${{ steps.changes.outputs.deps }} | |
| docs: ${{ steps.changes.outputs.docs }} | |
| ci: ${{ steps.changes.outputs.ci }} | |
| config: ${{ steps.changes.outputs.config }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 2 | |
| - uses: dorny/paths-filter@v3 | |
| id: changes | |
| with: | |
| base: ${{ github.ref }} | |
| filters: | | |
| src: | |
| - 'src/**' | |
| - '*.ts' | |
| - '*.js' | |
| tests: | |
| - 'tests/**' | |
| - '**/*.test.ts' | |
| - '**/*.spec.ts' | |
| - 'vitest.config.ts' | |
| deps: | |
| - 'package.json' | |
| - 'bun.lockb' | |
| - 'pnpm-lock.yaml' | |
| - 'package-lock.json' | |
| - 'yarn.lock' | |
| docs: | |
| - '**.md' | |
| - 'docs/**' | |
| - 'assets/**' | |
| ci: | |
| - '.github/**' | |
| config: | |
| - 'tsconfig.json' | |
| - 'bunfig.toml' | |
| - '.bun-version' | |
| - '.nvmrc' | |
| - '.prettierrc*' | |
| - '.eslintrc*' | |
| # ======================================== | |
| # SECURITY & COMPLIANCE | |
| # ======================================== | |
| security: | |
| name: 🔒 Security Audit | |
| runs-on: ubuntu-latest | |
| if: github.event_name != 'schedule' || github.event_name == 'workflow_dispatch' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: 🔧 Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: ${{ env.BUN_VERSION }} | |
| - name: 🔧 Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: 📦 Cache dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.bun/install/cache | |
| ~/.npm | |
| ~/.cache | |
| key: ${{ runner.os }}-deps-${{ hashFiles('**/bun.lockb', '**/package-lock.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-deps- | |
| - name: 🔍 Audit Bun dependencies | |
| run: | | |
| if [ -f "bun.lockb" ]; then | |
| bun audit || true | |
| fi | |
| continue-on-error: true | |
| - name: 🔍 Audit npm dependencies | |
| run: | | |
| if [ -f "package-lock.json" ]; then | |
| npm audit --audit-level=moderate || true | |
| fi | |
| continue-on-error: true | |
| - name: 🛡️ Run Trivy vulnerability scanner | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| scan-type: 'fs' | |
| scan-ref: '.' | |
| format: 'sarif' | |
| output: 'trivy-results.sarif' | |
| severity: 'CRITICAL,HIGH' | |
| - name: 📋 Upload Trivy results to GitHub Security | |
| uses: github/codeql-action/upload-sarif@v3 | |
| if: always() | |
| with: | |
| sarif_file: 'trivy-results.sarif' | |
| - name: 📋 Initialize CodeQL | |
| uses: github/codeql-action/init@v3 | |
| with: | |
| languages: typescript, javascript | |
| queries: security-and-quality | |
| - name: 🏗️ Autobuild | |
| uses: github/codeql-action/autobuild@v3 | |
| - name: 🔬 Perform CodeQL Analysis | |
| uses: github/codeql-action/analyze@v3 | |
| with: | |
| category: "/language:typescript" | |
| - name: 🔐 Check for secrets | |
| uses: trufflesecurity/trufflehog@main | |
| with: | |
| path: ./ | |
| base: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }} | |
| head: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} | |
| extra_args: --debug --only-verified --fail | |
| continue-on-error: true | |
| - name: 🔐 Fallback filesystem scan for secrets | |
| if: failure() || github.event_name == 'push' | |
| uses: trufflesecurity/trufflehog@main | |
| with: | |
| path: ./ | |
| extra_args: --debug --only-verified --fail --no-verification | |
| continue-on-error: true | |
| # ======================================== | |
| # CODE QUALITY & LINTING | |
| # ======================================== | |
| lint: | |
| name: 🎨 Code Quality | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| if: | | |
| needs.changes.outputs.src == 'true' || | |
| needs.changes.outputs.tests == 'true' || | |
| needs.changes.outputs.config == 'true' || | |
| github.event_name == 'workflow_dispatch' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: 🔧 Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: ${{ env.BUN_VERSION }} | |
| - name: 📦 Restore dependency cache | |
| uses: actions/cache@v4 | |
| id: cache | |
| with: | |
| path: | | |
| ~/.bun/install/cache | |
| node_modules | |
| key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }}-${{ hashFiles('**/package.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }}- | |
| ${{ runner.os }}-bun- | |
| - name: 📦 Install dependencies | |
| if: steps.cache.outputs.cache-hit != 'true' | |
| run: bun install --frozen-lockfile | |
| - name: 🔍 Run ESLint | |
| if: hashFiles('.eslintrc*') != '' | |
| run: | | |
| if command -v eslint &> /dev/null; then | |
| bun run lint | |
| fi | |
| continue-on-error: true | |
| - name: 🎨 Check code formatting | |
| run: | | |
| if [ -f ".prettierrc" ] || [ -f ".prettierrc.json" ] || [ -f ".prettierrc.js" ]; then | |
| bun run format:check || true | |
| fi | |
| - name: 💪 Type checking | |
| run: | | |
| if [ -f "tsconfig.json" ]; then | |
| bun run test:types || bun tsc --noEmit | |
| fi | |
| - name: 🛠️ Build project | |
| run: bun run build | |
| env: | |
| NODE_ENV: production | |
| - name: 📊 Upload build artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: build-${{ github.sha }} | |
| path: | | |
| dist/ | |
| retention-days: 7 | |
| compression-level: 9 | |
| - name: 📈 SonarCloud Scan | |
| if: github.event_name == 'pull_request' && github.actor != 'dependabot[bot]' | |
| uses: SonarSource/sonarcloud-github-action@master | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| continue-on-error: true | |
| # ======================================== | |
| # TESTING | |
| # ======================================== | |
| test: | |
| name: 🧪 Test (${{ matrix.os }}, ${{ matrix.runtime }}-${{ matrix.version }}) | |
| runs-on: ${{ matrix.os }} | |
| needs: [changes, lint] | |
| if: | | |
| needs.changes.outputs.src == 'true' || | |
| needs.changes.outputs.tests == 'true' || | |
| github.event_name == 'workflow_dispatch' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, windows-latest, macos-latest] | |
| runtime: [bun, node] | |
| version: ['latest', 'lts'] | |
| exclude: | |
| - runtime: bun | |
| version: lts | |
| - os: windows-latest | |
| runtime: bun | |
| include: | |
| - os: ubuntu-latest | |
| runtime: node | |
| version: '18' | |
| coverage: true | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: 🔧 Setup Bun | |
| if: matrix.runtime == 'bun' | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: ${{ matrix.version }} | |
| - name: 🔧 Setup Node.js | |
| if: matrix.runtime == 'node' | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ matrix.version == 'latest' && '21' || matrix.version == 'lts' && '20' || matrix.version }} | |
| - name: 📦 Get package manager | |
| id: pm | |
| run: | | |
| if [ "${{ matrix.runtime }}" == "bun" ]; then | |
| echo "cmd=bun" >> $GITHUB_OUTPUT | |
| echo "cache=~/.bun/install/cache" >> $GITHUB_OUTPUT | |
| else | |
| echo "cmd=npm" >> $GITHUB_OUTPUT | |
| echo "cache=~/.npm" >> $GITHUB_OUTPUT | |
| fi | |
| shell: bash | |
| - name: 📦 Cache dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ${{ steps.pm.outputs.cache }} | |
| node_modules | |
| key: ${{ runner.os }}-${{ matrix.runtime }}-${{ matrix.version }}-${{ hashFiles('**/package.json', '**/bun.lockb', '**/package-lock.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-${{ matrix.runtime }}-${{ matrix.version }}- | |
| ${{ runner.os }}-${{ matrix.runtime }}- | |
| - name: 📦 Install dependencies | |
| run: | | |
| if [ "${{ matrix.runtime }}" == "bun" ]; then | |
| bun install --frozen-lockfile | |
| else | |
| npm ci || npm install | |
| fi | |
| shell: bash | |
| - name: 🛠️ Build project | |
| run: ${{ steps.pm.outputs.cmd }} run build | |
| - name: 🧪 Run unit tests | |
| run: ${{ steps.pm.outputs.cmd }} test | |
| env: | |
| CI: true | |
| NODE_ENV: test | |
| - name: 🧪 Run unit tests with coverage | |
| if: matrix.coverage | |
| run: ${{ steps.pm.outputs.cmd }} run test:coverage | |
| env: | |
| CI: true | |
| - name: 📊 Generate test report | |
| if: always() | |
| run: | | |
| if [ -f "package.json" ] && grep -q "test:junit" package.json; then | |
| ${{ steps.pm.outputs.cmd }} run test:junit || true | |
| fi | |
| shell: bash | |
| continue-on-error: true | |
| - name: 📈 Upload test results | |
| if: always() && matrix.coverage | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: test-results-${{ matrix.os }}-${{ matrix.runtime }}-${{ matrix.version }} | |
| path: | | |
| coverage/ | |
| test-results/ | |
| *.xml | |
| retention-days: 7 | |
| - name: 🟩 Upload coverage to Codecov | |
| if: matrix.coverage && !cancelled() | |
| uses: codecov/codecov-action@v4 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| files: ./coverage/lcov.info,./coverage/coverage-final.json | |
| flags: unittests | |
| name: codecov-${{ matrix.os }}-${{ matrix.runtime }} | |
| fail_ci_if_error: false | |
| verbose: true | |
| slug: ali-master/uuidv47 | |
| # ======================================== | |
| # SUMMARY & NOTIFICATIONS | |
| # ======================================== | |
| summary: | |
| name: 📊 Pipeline Summary | |
| runs-on: ubuntu-latest | |
| needs: [lint, test, security] | |
| if: always() | |
| steps: | |
| - name: 📊 Generate summary | |
| run: | | |
| echo "## 🚀 CI/CD Pipeline Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Status badges | |
| echo "### Status" >> $GITHUB_STEP_SUMMARY | |
| echo "| Job | Status |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-----|--------|" >> $GITHUB_STEP_SUMMARY | |
| echo "| 🎨 Lint | ${{ needs.lint.result }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| 🧪 Tests | ${{ needs.test.result }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| 🔒 Security | ${{ needs.security.result }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Details" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Commit**: ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Branch**: ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Runner**: ${{ runner.os }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Triggered by**: ${{ github.actor }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Event**: ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY | |
| - name: 💬 Comment on PR | |
| if: github.event_name == 'pull_request' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const summary = ` | |
| ## 🚀 CI/CD Pipeline Results | |
| | Check | Status | | |
| |-------|--------| | |
| | 🎨 Code Quality | ${{ needs.lint.result }} | | |
| | 🧪 Tests | ${{ needs.test.result }} | | |
| | 🔒 Security | ${{ needs.security.result }} | | |
| [View full details](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) | |
| `; | |
| github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: summary | |
| }); |