# GitHub Integration Setup Guide

This guide walks you through connecting Lathe Studio to GitHub for CI/CD pipeline integration and issue tracking.

* * *

## Prerequisites

- GitHub account (personal or organization)
- GitHub repository with Actions workflows
- Project admin access in Lathe Studio

* * *

## Overview

The GitHub integration enables:

- **CI/CD Webhooks**: Automatically create builds when GitHub Actions workflows complete
- **Issue Sync**: Link test failures to GitHub Issues
- **Pull Request Status**: Display test results directly in PRs

* * *

## Connecting GitHub

### Step 1: Create an API Key

1. Go to **Settings > Organization > API Keys**  
2. Click **Create API Key**  
3. Give it a name (e.g., "GitHub Actions")  
4. Select scopes:
   - `builds:read` \- View builds  
   - `builds:create` \- Create new builds  
5. Copy the generated key (shown only once)

### Step 2: Configure GitHub Webhook

1. In GitHub, go to your repository  
2. Navigate to **Settings > Webhooks**  
3. Click **Add webhook**  
4. Configure:
   - **Payload URL**: `https://lathestudio.dev/api/webhooks/github`
   - **Content type**: `application/json`
   - **Secret**: (optional) Add a webhook secret for signature verification
   - **SSL verification**: Enable  
5. **Events**: Select "Workflow runs" or "Send me everything"  
6. Click **Add webhook**

### Step 3: Add API Key to Headers

In the webhook configuration, add a custom header:

- **Name**: `Authorization`  
- **Value**: `Bearer <your-api-key>`

Alternatively, use the `X-Pt-Org-Id` header with your organization ID.

### Step 4: Test the Integration

1. Trigger a workflow run in GitHub Actions  
2. Go to **Releases** page in Lathe Studio  
3. You should see the build in the **Build Queue** section  
4. Assign it to a release

* * *

## Authentication Methods

### Method 1: API Key (Recommended)

Include the API key in the Authorization header:

```
Authorization: Bearer pt_live_xxxxxxxxxxxxxxxx
```

Benefits:

- Rate limiting per key
- Usage tracking
- Scoped permissions

### Method 2: Organization ID

Include your organization ID in a custom header:

```
X-Pt-Org-Id: your-org-uuid
```

Note: This method has basic rate limiting and is less secure. Use API keys for production.

* * *

## How It Works

1. **Workflow Completes** → GitHub sends webhook to `/api/webhooks/github`
2. **Authentication** → Webhook verifies API key or org ID
3. **Project Matching** → System tries to match repo URL to a project
4. **Queue or Create**:
   - If project + active release found → Build created directly
   - If project found but no release → Queued for release assignment
   - If no project match → Queued for project + release assignment
5. **Assignment** → User assigns queued builds via Releases page

* * *

## Build Queue UI

The Build Queue appears on the Releases page when there are pending builds:

- Shows build name, source (GitHub), project, commit/branch
- **Assign to Release**: Select an active release for the project
- **Select Project**: Navigate to projects if project not auto-detected
- **Reject**: Remove build from queue (no build created)

* * *

## GitHub Actions Example

Add this step to your existing `.github/workflows/test.yml` after your test runner finishes. The `if: always()` ensures results are sent even if tests fail.

```yaml
name: Test and Report

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

- name: Run Tests
        run: npm test -- --reporter=json --outputFile=test-results.json

- name: Upload Results to Lathe Studio
        if: always()
        env:
          LATHE_API_KEY: ${{ secrets.LATHE_STUDIO_API_KEY }}
        run: |
          # Parse your test framework's JSON output into the Lathe Studio format.
          # Below is an example for Jest — adapt the jq filter to your framework.
          RESULTS=$(jq -c '
            [.testResults[] | .assertionResults[] | select(.status != "pending") | {
              test_identifier: .ancestorTitles + [.title] | join(" > "),
              name: .title,
              status: (if .status == "passed" then "passed" elif .status == "failed" then "failed" else "skipped" end),
              duration_ms: .duration,
              error: (if .failureMessages | length > 0 then .failureMessages[0] else null end)
            }]
          ' test-results.json)

curl -X POST https://perpetualtest.dev/api/v1/results/ingest \
            -H "Authorization: Bearer $LATHE_API_KEY" \
            -H "Content-Type: application/json" \
            -d "{
              \"build\": {
                \"identifier\": \"${{ github.ref_name }}-${{ github.run_number }}\",
                \"branch\": \"${{ github.ref_name }}\",
                \"commit_sha\": \"${{ github.sha }}\",
                \"url\": \"${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}\"
              },
              \"source\": {
                \"pipeline\": \"github_actions\",
                \"run_id\": \"${{ github.run_id }}\",
                \"run_url\": \"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\"
              },
              \"results\": $RESULTS
            }"
```

**What this does:**

1. Runs your tests and outputs JSON (Jest example shown)
2. Parses the JSON into Lathe Studio's `IngestResult` format
3. Sends everything to `POST /api/v1/results/ingest`
4. Lathe Studio automatically:
   - Creates a Build (or re-uses an existing one by `identifier`)
   - Creates a completed Test Run linked to that Build
   - Matches results to existing Test Cases by `test_identifier`
   - Auto-creates new Test Cases for unknown identifiers

**Response:**

```json
{
  "build_id": "uuid",
  "build_display_id": "main-1042",
  "test_run_id": "uuid",
  "test_run_display_id": "RUN-MOB-0123",
  "test_run_url": "/projects/.../runs/...",
  "test_cases_created": 3,
  "test_cases_linked": 47,
  "results_imported": 50
}
```

**Adapting to other test frameworks:**

| Framework | Approach |
| --- | --- |
| **Jest** | Use `--json --outputFile=` then `jq` to map fields |
| **Pytest** | Use `pytest-json-report` plugin, map `nodeid` → `test_identifier` |
| **JUnit XML** | Convert XML to JSON with `xq` or a Python script first |
| **Cypress** | Use `cypress-multi-reporters` with `mocha-junit-reporter`, then convert |
| **Playwright** | Use `blob` reporter + `npx playwright merge-reports --reporter=json` |

The only requirement is that each result has a stable `test_identifier` string. Use your existing display codes (`TC-MOB-0042`) or any unique ID — Lathe Studio will match them automatically.

* * *

## Troubleshooting

### Builds not appearing

1. Check webhook delivery in GitHub (Settings > Webhooks > Recent Deliveries)
2. Verify API key is not expired or revoked
3. Ensure the workflow run event is being sent
4. Check that the repository URL matches your project settings

### Wrong project assigned

The system matches by:

1. Repository URL (exact match)
2. Repository name (partial match)

You can manually assign to the correct project from the queue.

### Rate limiting

API keys are limited to 60 requests per minute by default. Contact support to increase limits.

### Webhook signature verification fails

If using webhook secrets:

1. Verify the secret is correctly configured in both GitHub and Lathe Studio
2. Check that the signature header is being sent
3. Ensure the payload hasn't been modified

* * *

## Security

- API keys are hashed using SHA-256 before storage
- Only the first 12 characters are stored for identification
- Keys can be revoked at any time
- Webhook signatures (HMAC-SHA256) are supported for verification
- All webhook communications use HTTPS

* * *

## Unlinking GitHub

To disconnect:

1. Go to your GitHub repository
2. Navigate to **Settings > Webhooks**
3. Find the Lathe Studio webhook
4. Click **Delete**

**Note**: Previously created builds remain in Lathe Studio. New builds will not be created until reconnected.

* * *

## Advanced Configuration

### Custom Build Names

Override the default build name by adding metadata to your workflow:

```yaml
- name: Set Build Name
  run: echo "PT_BUILD_NAME=Release ${{ github.ref_name }}" >> $GITHUB_ENV
```

### Branch Filtering

Configure which branches trigger builds in **Project Settings > CI/CD**:

- Include: `main`, `develop`, `release/*`
- Exclude: `dependabot/*`, `hotfix/*`

* * *

_For additional help, contact support._
