# GitHub Actions Integration

Hướng dẫn tích hợp SunLint với GitHub Actions để tự động review Pull Requests.

## Tính năng mới: Auto GitHub Annotation

SunLint giờ đây có thể tự động tạo review comments trên Pull Requests mà **không cần chỉ định `--output` và `--format=json`**.

### 3 Annotation Modes

`--github-annotate` hỗ trợ 3 modes:

#### 1. **`annotate`** - Inline Comments Only
Tạo review comments trực tiếp trên code (inline comments)
```bash
sunlint --all --input=src --github-annotate=annotate
```
- ✅ Comment trên từng dòng code có lỗi
- ✅ Duplicate detection
- ✅ Batch processing (30 comments/review)

#### 2. **`summary`** - Summary Comment Only
Tạo 1 comment tổng hợp (như GitHub Actions summary)
```bash
sunlint --all --input=src --github-annotate=summary
```
- ✅ Thống kê tổng quan (errors, warnings, files)
- ✅ Top 10 files có nhiều lỗi nhất
- ✅ Auto update comment cũ (không spam)

#### 3. **`all`** - Both (Default)
Tạo cả inline comments và summary comment
```bash
sunlint --all --input=src --github-annotate        # default
sunlint --all --input=src --github-annotate=all    # explicit
```
- ✅ Full experience: inline + summary
- ✅ Resilient: nếu 1 task fail, task kia vẫn chạy

### Cách hoạt động

Khi sử dụng flag `--github-annotate`:

1. ✅ **Tự động phát hiện PR event** - Chỉ chạy khi event là `pull_request`
2. ✅ **Tự động tạo JSON report** - Không cần `--output` và `--format=json`
3. ✅ **Tự động cleanup** - Xóa temp file sau khi annotate
4. ✅ **Intelligent error handling** - Báo lỗi chi tiết và gợi ý fix
5. ✅ **Duplicate detection** - Tránh spam comments trùng lặp
6. ✅ **3 flexible modes** - Chọn mode phù hợp với workflow

## Quick Start

### Workflow đơn giản nhất

```yaml
name: SunLint PR Review
on:
  pull_request:
    branches: [main, develop]

jobs:
  sunlint:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
      contents: read

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'

      - name: Install SunLint
        run: npm install -g sunlint

      - name: Run SunLint with Auto Annotation
        run: sunlint --all --input=src --github-annotate
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

**Đó là tất cả!** SunLint sẽ tự động:
- Phân tích code
- Tạo JSON report (tạm thời)
- Comment lên PR
- Xóa file tạm

## Advanced Usage

### 1. Analyze only changed files (Auto-detect PR)

**⭐ Tính năng mới**: Tự động phát hiện PR context, tự động fetch base branch, và sử dụng merge-base để diff chính xác!

```yaml
steps:
  - name: Checkout code
    uses: actions/checkout@v4
    with:
      fetch-depth: 0  # Recommended: fetch full history for accurate diff

  - name: Run SunLint on Changed Files (Auto-detect)
    run: sunlint --all --changed-files --github-annotate
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

**✨ Đơn giản nhất có thể - không cần config gì thêm!**

**Cách hoạt động**:

- ✅ Tự động detect GitHub Actions PR event (`GITHUB_EVENT_NAME=pull_request`)
- ✅ Tự động lấy base branch từ `GITHUB_BASE_REF`
- ✅ **Tự động fetch base branch nếu chưa có** (không cần thêm step!)
- ✅ Detect shallow clone và fetch thêm history nếu cần
- ✅ Sử dụng `git merge-base` để tìm common ancestor
- ✅ So sánh với merge-base thay vì HEAD → Lấy đúng các file thay đổi trong PR

**Không cần:**

- ❌ Chỉ định `--diff-base`
- ❌ Thêm step fetch base branch thủ công
- ❌ Config phức tạp

**Fallback**: Nếu không phát hiện được PR context hoặc không fetch được base branch, sẽ fallback về standard git diff logic

**❓ Có cần `fetch-depth: 0` không?**

**Không bắt buộc!** Nhưng **strongly recommended** vì performance.

| Option | Performance | Checkout Time | Total Time | Accuracy |
|--------|-------------|---------------|------------|----------|
| **With `fetch-depth: 0`** ⭐ | Fast | +2-3s | **Fastest** | 100% |
| Without (shallow clone) | Slower | Fast | Slower | ~95% |

**Với `fetch-depth: 0` (Recommended):**

```yaml
- uses: actions/checkout@v4
  with:
    fetch-depth: 0  # One-time full fetch
```

- ✅ **Fastest total time** - fetch once, use immediately
- ✅ **100% accurate** - full history for merge-base
- ✅ **Simpler** - no runtime fetch needed

**Không có `fetch-depth: 0` (Vẫn work):**

```yaml
- uses: actions/checkout@v4  # Shallow clone (depth=1)
```

- ✅ Fast checkout
- ⚠️ **Slower total time** - SunLint must fetch at runtime:
  - Detect shallow clone
  - `git fetch --deepen=50`
  - `git fetch origin/main --depth=50`
- ⚠️ May miss history for very large PRs

**Recommendation**: Dùng `fetch-depth: 0` trừ khi bạn có lý do đặc biệt (e.g., monorepo cực lớn)

### 2. Save report file + annotate

```yaml
- name: Run SunLint
  run: sunlint --all --input=src --output=sunlint-report.json --github-annotate
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Upload Report
  uses: actions/upload-artifact@v4
  if: always()
  with:
    name: sunlint-report
    path: sunlint-report.json
```

### 3. Run specific rules

```yaml
- name: Run Security Rules Only
  run: sunlint --security --input=src --github-annotate
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

### 4. Multiple languages

```yaml
- name: Run SunLint on TypeScript and Kotlin
  run: sunlint --all --languages=typescript,kotlin --input=. --github-annotate
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

### 5. Với verbose logging

```yaml
- name: Run SunLint (Verbose)
  run: sunlint --all --input=src --github-annotate --verbose
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

## Complete Example Workflow

```yaml
name: Code Quality Check
on:
  pull_request:
    branches: [main, develop, 'release/**']
    paths:
      - '**.ts'
      - '**.tsx'
      - '**.js'
      - '**.jsx'
      - '**.kt'
      - '**.dart'

jobs:
  sunlint:
    name: SunLint Analysis
    runs-on: ubuntu-latest

    permissions:
      contents: read
      pull-requests: write

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0  # For git diff

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Install SunLint
        run: npm install -g sunlint

      - name: Run SunLint on Changed Files
        id: sunlint
        continue-on-error: true  # Don't fail the workflow
        run: |
          sunlint --all \
            --changed-files \
            --diff-base=origin/${{ github.base_ref }} \
            --github-annotate \
            --output=sunlint-report.json \
            --verbose
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Upload Report
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: sunlint-report-${{ github.event.pull_request.number }}
          path: sunlint-report.json
          retention-days: 30

      - name: Comment Summary
        if: always()
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            if (!fs.existsSync('sunlint-report.json')) return;

            const report = JSON.parse(fs.readFileSync('sunlint-report.json', 'utf8'));
            let violations = 0;
            let errors = 0;
            let warnings = 0;

            for (const file of report) {
              violations += file.messages.length;
              errors += file.errorCount;
              warnings += file.warningCount;
            }

            const body = `## SunLint Report

            📊 **Summary:**
            - Total violations: ${violations}
            - Errors: ${errors}
            - Warnings: ${warnings}

            ${violations === 0 ? '✅ No issues found!' : '⚠️ Please check the PR comments for details.'}
            `;

            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: body
            });
```

## Environment Variables

SunLint tự động đọc các biến môi trường từ GitHub Actions:

| Variable | Description | Required |
|----------|-------------|----------|
| `GITHUB_TOKEN` | GitHub token for API access | ✅ Yes |
| `GITHUB_REPOSITORY` | Repository (owner/repo) | Auto |
| `GITHUB_EVENT_NAME` | Event type (pull_request) | Auto |
| `GITHUB_EVENT_PATH` | Path to event payload | Auto |
| `RUNNER_TEMP` | Temp directory | Auto |

## Permissions Required

```yaml
permissions:
  contents: read         # To checkout code
  pull-requests: write   # To create review comments
```

## Troubleshooting

### 1. "GitHub annotation only works on pull_request events"

**Nguyên nhân:** Workflow chạy trên `push` event thay vì `pull_request`

**Giải pháp:**
```yaml
on:
  pull_request:  # Thay vì push
    branches: [main]
```

### 2. "Missing GITHUB_TOKEN"

**Nguyên nhân:** Thiếu GITHUB_TOKEN trong env

**Giải pháp:**
```yaml
env:
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

### 3. "Permission denied"

**Nguyên nhân:** Token không có quyền `pull-requests: write`

**Giải pháp:**
```yaml
permissions:
  pull-requests: write
```

### 4. Annotations không hiển thị

**Nguyên nhân:** File paths không đúng format

**Giải pháp:** Đảm bảo paths là relative từ repo root:
```yaml
- uses: actions/checkout@v4  # Checkout đúng branch
```

### 5. Fork PRs không annotate được

**Nguyên nhân:** `pull_request` event từ fork có quyền hạn chế

**Giải pháp:** Dùng `pull_request_target` (cẩn thận về security!)
```yaml
on:
  pull_request_target:  # Cho fork PRs
```

## Best Practices

### 1. Fail on errors, warn on warnings

```yaml
- name: Run SunLint
  run: sunlint --all --input=src --github-annotate
  continue-on-error: false  # Fail if errors found
```

### 2. Cache SunLint installation

```yaml
- name: Cache SunLint
  uses: actions/cache@v3
  with:
    path: ~/.npm
    key: ${{ runner.os }}-sunlint-${{ hashFiles('**/package-lock.json') }}
```

### 3. Run only on specific files

```yaml
on:
  pull_request:
    paths:
      - 'src/**'
      - '!src/**/*.test.ts'
```

### 4. Matrix strategy for multiple projects

```yaml
jobs:
  sunlint:
    strategy:
      matrix:
        project: [frontend, backend, mobile]
    steps:
      - run: sunlint --all --input=${{ matrix.project }} --github-annotate
```

### 5. Skip on draft PRs

```yaml
jobs:
  sunlint:
    if: github.event.pull_request.draft == false
    steps:
      - run: sunlint --all --github-annotate
```

## Migration Guide

### From old style (with --output)

**Before:**
```yaml
- name: Run SunLint
  run: sunlint --all --input=src --output=report.json --format=json

- name: Annotate PR
  run: node annotate-script.js
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

**After:**
```yaml
- name: Run SunLint with Auto Annotation
  run: sunlint --all --input=src --github-annotate
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

## FAQ

**Q: Có thể dùng cho push events không?**
A: Không. `--github-annotate` chỉ hoạt động cho `pull_request` events. Dùng `--output` để save report cho push events.

**Q: Có bị rate limit không?**
A: SunLint có retry mechanism và batch processing để tránh rate limit. Maximum 30 comments/review, tự động chia batches nếu nhiều hơn.

**Q: Có thể customize comment format không?**
A: Hiện tại format là `[rule-id] message`. Customize format đang được phát triển.

**Q: Có delete được comments cũ không?**
A: SunLint có duplicate detection để tránh spam. Comments cũ được giữ lại cho lịch sử.

**Q: Performance với large PRs?**
A: SunLint optimize cho large PRs với:
- Batch processing
- Only comment on changed files
- Async operations
- Retry mechanism

## Examples Repository

Xem thêm examples tại: [examples/github-actions/](../examples/github-actions/)

## Support

- Issues: [GitHub Issues](https://github.com/sun-asterisk/engineer-excellence/issues)
- Docs: [Documentation](../README.md)
- Community: [Discussions](https://github.com/sun-asterisk/engineer-excellence/discussions)
