|
| 1 | +# com.paca.github |
| 2 | + |
| 3 | +First-party Paca plugin that integrates GitHub repositories, pull requests, and branches with Paca projects and tasks. |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## Architecture |
| 8 | + |
| 9 | +The plugin follows the standard three-part plugin structure: |
| 10 | + |
| 11 | +```text |
| 12 | +github/ |
| 13 | +├── backend/ - Go WASM plugin (runs inside the API host) |
| 14 | +├── frontend/ - React micro-frontend (Module Federation remote) |
| 15 | +└── mcp/ - MCP tools bundle for AI/tooling integrations |
| 16 | +``` |
| 17 | + |
| 18 | +### Backend (backend/) |
| 19 | + |
| 20 | +- Written in Go, compiled to wasip1/wasm for production. |
| 21 | +- Registered as com.paca.github in the plugin registry. |
| 22 | +- Owns its database schema (plugin_data_com_paca_github) and runs its own migration on startup. |
| 23 | +- Stores GitHub PATs and webhook secrets encrypted with AES-256-GCM. |
| 24 | +- Creates and verifies GitHub webhooks for linked repositories. |
| 25 | +- Handles task/project cleanup on task.deleted and project.deleted events. |
| 26 | + |
| 27 | +### Frontend (frontend/) |
| 28 | + |
| 29 | +- Vite + React + TanStack Query. |
| 30 | +- Exposed as a Module Federation remote (com_paca_github). |
| 31 | +- Mounted at: |
| 32 | + - project.settings.tab (GitHubSettingsTab) |
| 33 | + - task.detail.section (GitHubTaskSection) |
| 34 | +- Communicates with backend via plugin API routes under /api/v1/plugins/com.paca.github/projects/:projectId/... |
| 35 | + |
| 36 | +### MCP (mcp/) |
| 37 | + |
| 38 | +- Built with @paca-ai/plugin-sdk-mcp. |
| 39 | +- Published as an ESM bundle (mcp.js). |
| 40 | +- Exposes tools for integration management, repository linking, PR linking, and branch operations. |
| 41 | + |
| 42 | +--- |
| 43 | + |
| 44 | +## Required Configuration |
| 45 | + |
| 46 | +The backend uses the following config keys: |
| 47 | + |
| 48 | +- ENCRYPTION_KEY: 64 hex chars (32-byte key) used for AES-256-GCM encryption of tokens/secrets. |
| 49 | +- PUBLIC_URL: public base URL of the Paca server, used to build the webhook callback URL. |
| 50 | + |
| 51 | +Without PUBLIC_URL, linking repositories will fail because webhook creation requires a reachable callback URL. |
| 52 | + |
| 53 | +--- |
| 54 | + |
| 55 | +## API Endpoints |
| 56 | + |
| 57 | +All routes are prefixed with /projects/:projectId by the host. |
| 58 | +The caller must be an authenticated member of the project unless noted otherwise. |
| 59 | + |
| 60 | +| Method | Path | Description | |
| 61 | +|--------|------|-------------| |
| 62 | +| GET | /github | Get integration status for the current project | |
| 63 | +| POST | /github/token | Set or replace GitHub PAT | |
| 64 | +| DELETE | /github/token | Delete GitHub PAT and integration | |
| 65 | +| GET | /github/repositories | List repositories accessible by the token | |
| 66 | +| GET | /github/linked-repositories | List repositories linked to the project | |
| 67 | +| POST | /github/linked-repositories | Link a repository and create webhook | |
| 68 | +| DELETE | /github/linked-repositories/:repoId | Unlink a repository | |
| 69 | +| GET | /tasks/:taskId/github/pull-requests | List pull requests linked to a task | |
| 70 | +| POST | /tasks/:taskId/github/pull-requests | Link a pull request to a task | |
| 71 | +| DELETE | /tasks/:taskId/github/pull-requests/:prId | Unlink a pull request from a task | |
| 72 | +| POST | /tasks/:taskId/github/branches | Create branch and link it to a task | |
| 73 | +| GET | /tasks/:taskId/github/branches | List branches linked to a task | |
| 74 | +| POST | /webhook (public) | Receive GitHub webhook events | |
| 75 | + |
| 76 | +### Example requests |
| 77 | + |
| 78 | +Set token: |
| 79 | + |
| 80 | +```json |
| 81 | +{ "token": "ghp_xxx" } |
| 82 | +``` |
| 83 | + |
| 84 | +Link repository: |
| 85 | + |
| 86 | +```json |
| 87 | +{ "owner": "Paca-AI", "repo_name": "paca" } |
| 88 | +``` |
| 89 | + |
| 90 | +Link PR to task: |
| 91 | + |
| 92 | +```json |
| 93 | +{ "repo_id": "550e8400-e29b-41d4-a716-446655440000", "pr_number": 42 } |
| 94 | +``` |
| 95 | + |
| 96 | +Create branch for task: |
| 97 | + |
| 98 | +```json |
| 99 | +{ |
| 100 | + "repo_id": "550e8400-e29b-41d4-a716-446655440000", |
| 101 | + "branch_name": "feat/PROJ-42-github-integration", |
| 102 | + "source_branch": "main" |
| 103 | +} |
| 104 | +``` |
| 105 | + |
| 106 | +--- |
| 107 | + |
| 108 | +## Webhook Behavior |
| 109 | + |
| 110 | +On linked repositories, GitHub webhooks are configured for: |
| 111 | + |
| 112 | +- push |
| 113 | +- pull_request |
| 114 | +- check_run |
| 115 | + |
| 116 | +Current behavior includes: |
| 117 | + |
| 118 | +- Verifying X-Hub-Signature-256 with the stored webhook secret. |
| 119 | +- Caching/updating pull request state in plugin tables. |
| 120 | +- Auto-linking PRs when opened/reopened from a branch already linked to a task. |
| 121 | +- Auto-linking created branches when branch names include task references like PROJ-42. |
| 122 | +- Emitting plugin events: |
| 123 | + - github.pr_linked |
| 124 | + - github.pr_updated |
| 125 | + - github.branch_linked |
| 126 | + |
| 127 | +--- |
| 128 | + |
| 129 | +## Database Schema |
| 130 | + |
| 131 | +Tables live in plugin_data_com_paca_github and are created by backend/migrations/0001_create_github_tables.sql. |
| 132 | + |
| 133 | +```text |
| 134 | +github_integrations |
| 135 | + id UUID PK |
| 136 | + project_id UUID UNIQUE -> projects(id) ON DELETE CASCADE |
| 137 | + access_token_enc TEXT |
| 138 | + created_at TIMESTAMPTZ |
| 139 | + updated_at TIMESTAMPTZ |
| 140 | +
|
| 141 | +github_repositories |
| 142 | + id UUID PK |
| 143 | + project_id UUID -> projects(id) ON DELETE CASCADE |
| 144 | + integration_id UUID -> github_integrations(id) ON DELETE CASCADE |
| 145 | + owner TEXT |
| 146 | + repo_name TEXT |
| 147 | + full_name TEXT |
| 148 | + webhook_id BIGINT |
| 149 | + webhook_secret_enc TEXT |
| 150 | + default_branch TEXT |
| 151 | + created_at TIMESTAMPTZ |
| 152 | + updated_at TIMESTAMPTZ |
| 153 | +
|
| 154 | +github_pull_requests |
| 155 | + id UUID PK |
| 156 | + project_id UUID -> projects(id) ON DELETE CASCADE |
| 157 | + repo_id UUID -> github_repositories(id) ON DELETE CASCADE |
| 158 | + pr_number INT |
| 159 | + github_pr_id BIGINT |
| 160 | + title TEXT |
| 161 | + state TEXT |
| 162 | + html_url TEXT |
| 163 | + head_branch TEXT |
| 164 | + base_branch TEXT |
| 165 | + author TEXT |
| 166 | + merged_at TIMESTAMPTZ |
| 167 | + created_at TIMESTAMPTZ |
| 168 | + updated_at TIMESTAMPTZ |
| 169 | +
|
| 170 | +github_task_pr_links |
| 171 | + id UUID PK |
| 172 | + task_id UUID -> tasks(id) ON DELETE CASCADE |
| 173 | + pull_request_id UUID -> github_pull_requests(id) ON DELETE CASCADE |
| 174 | + created_at TIMESTAMPTZ |
| 175 | +
|
| 176 | +github_task_branches |
| 177 | + id UUID PK |
| 178 | + task_id UUID -> tasks(id) ON DELETE CASCADE |
| 179 | + repo_id UUID -> github_repositories(id) ON DELETE CASCADE |
| 180 | + branch_name TEXT |
| 181 | + created_at TIMESTAMPTZ |
| 182 | +``` |
| 183 | + |
| 184 | +--- |
| 185 | + |
| 186 | +## Development |
| 187 | + |
| 188 | +### Backend |
| 189 | + |
| 190 | +```bash |
| 191 | +cd backend |
| 192 | + |
| 193 | +# Run tests |
| 194 | +go test -v ./... |
| 195 | + |
| 196 | +# Build WASM binary (Go 1.24+) |
| 197 | +GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o github.wasm . |
| 198 | +``` |
| 199 | + |
| 200 | +### Frontend |
| 201 | + |
| 202 | +```bash |
| 203 | +cd frontend |
| 204 | + |
| 205 | +# Install dependencies |
| 206 | +bun install |
| 207 | + |
| 208 | +# Development build (watch) |
| 209 | +bun run dev |
| 210 | + |
| 211 | +# Production build |
| 212 | +bun run build |
| 213 | +``` |
| 214 | + |
| 215 | +### MCP |
| 216 | + |
| 217 | +```bash |
| 218 | +cd mcp |
| 219 | + |
| 220 | +# Install dependencies |
| 221 | +bun install |
| 222 | + |
| 223 | +# Typecheck |
| 224 | +bun run typecheck |
| 225 | + |
| 226 | +# Production build (outputs mcp.js) |
| 227 | +bun run build |
| 228 | +``` |
| 229 | + |
| 230 | +--- |
| 231 | + |
| 232 | +## Release (CD) |
| 233 | + |
| 234 | +The release workflow is at .github/workflows/release.yml. |
| 235 | + |
| 236 | +It runs on: |
| 237 | + |
| 238 | +- tag push matching v* |
| 239 | +- manual workflow_dispatch |
| 240 | + |
| 241 | +The workflow builds backend/frontend/mcp assets, packages migrations and plugin.json, generates SHA256 checksums, and uploads all artifacts to a GitHub Release. |
0 commit comments