Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/backend-pr-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ jobs:
sudo dpkg -i /tmp/tinygo.deb

- name: Build WASM
run: tinygo build -target=wasip1 -buildmode=c-shared -o github.wasm .
run: tinygo build -target=wasip1 -buildmode=c-shared -o gitlab.wasm .

test:
name: Tests
Expand Down
38 changes: 19 additions & 19 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
run: |
set -euo pipefail
mkdir -p ../release/backend
tinygo build -target=wasip1 -buildmode=c-shared -o ../release/backend/github.wasm .
tinygo build -target=wasip1 -buildmode=c-shared -o ../release/backend/gitlab.wasm .

- name: Build frontend
working-directory: frontend
Expand Down Expand Up @@ -74,32 +74,32 @@ jobs:
- name: Create archives
run: |
set -euo pipefail
tar -czf github-backend-wasm.tar.gz -C release/backend github.wasm
tar -czf github-frontend-dist.tar.gz -C release/frontend dist
tar -czf github-mcp-dist.tar.gz -C release/mcp dist
tar -czf github-migrations.tar.gz -C release migrations
tar -czf github-skills-bundle.tar.gz -C release skills
tar -czf github-plugin-manifest.tar.gz -C release plugin.json
tar -czf gitlab-backend-wasm.tar.gz -C release/backend gitlab.wasm
tar -czf gitlab-frontend-dist.tar.gz -C release/frontend dist
tar -czf gitlab-mcp-dist.tar.gz -C release/mcp dist
tar -czf gitlab-migrations.tar.gz -C release migrations
tar -czf gitlab-skills-bundle.tar.gz -C release skills
tar -czf gitlab-plugin-manifest.tar.gz -C release plugin.json

- name: Generate checksums
run: |
set -euo pipefail
sha256sum github-backend-wasm.tar.gz \
github-frontend-dist.tar.gz \
github-mcp-dist.tar.gz \
github-migrations.tar.gz \
github-skills-bundle.tar.gz \
github-plugin-manifest.tar.gz > checksums.txt
sha256sum gitlab-backend-wasm.tar.gz \
gitlab-frontend-dist.tar.gz \
gitlab-mcp-dist.tar.gz \
gitlab-migrations.tar.gz \
gitlab-skills-bundle.tar.gz \
gitlab-plugin-manifest.tar.gz > checksums.txt

- name: Publish GitHub Release assets
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
files: |
github-backend-wasm.tar.gz
github-frontend-dist.tar.gz
github-mcp-dist.tar.gz
github-migrations.tar.gz
github-skills-bundle.tar.gz
github-plugin-manifest.tar.gz
gitlab-backend-wasm.tar.gz
gitlab-frontend-dist.tar.gz
gitlab-mcp-dist.tar.gz
gitlab-migrations.tar.gz
gitlab-skills-bundle.tar.gz
gitlab-plugin-manifest.tar.gz
checksums.txt
270 changes: 84 additions & 186 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,241 +1,139 @@
# com.paca.github
# com.paca.gitlab

First-party Paca plugin that integrates GitHub repositories, pull requests, and branches with Paca projects and tasks.
Plugin first-party Paca che collega **progetti GitLab**, **merge request** e **branch** a progetti e task Paca.

Port del plugin GitHub (`com.paca.github`) verso GitLab. Il **codice e la CI restano su GitHub**; il prodotto parla con GitLab.com (e, nel codice, con Self-Managed via URL opzionale).

Documentazione GitLab di riferimento: [docs.gitlab.com](https://docs.gitlab.com/) · [REST API](https://docs.gitlab.com/ee/api/rest/) · [Project webhooks](https://docs.gitlab.com/user/project/integrations/webhooks/) · [Access token scopes](https://docs.gitlab.com/security/tokens/access_token_scopes/) · [MCP server ufficiale](https://docs.gitlab.com/user/model_context_protocol/mcp_server/)

---

## Architecture
## Cosa fa

The plugin follows the standard three-part plugin structure:
- Salva un access token GitLab (cifrato) per progetto Paca
- Elenca e collega progetti GitLab accessibili dal token
- Crea webhook sul progetto collegato (push, merge request, pipeline)
- Su ogni task Paca: crea/linka branch e merge request, commenti, review, stato CI (pipelines)
- Espone trigger/condizioni/azioni di automazione (`gitlab.mr_*`, `gitlab.branch_linked`, …)
- Bundle MCP + skill `paca-gitlab-workflow` centrati sul *link al task Paca* (non sostituiscono il [MCP ufficiale GitLab](https://docs.gitlab.com/user/model_context_protocol/mcp_server/))

```text
github/
├── backend/ - Go WASM plugin (runs inside the API host)
├── frontend/ - React micro-frontend (Module Federation remote)
└── mcp/ - MCP tools bundle for AI/tooling integrations
```
---

### Backend (backend/)
## Differenze rispetto al plugin GitHub

- Written in Go, compiled to wasip1/wasm for production.
- Registered as com.paca.github in the plugin registry.
- Owns its database schema (plugin_data_com_paca_github) and runs its own migration on startup.
- Stores GitHub PATs and webhook secrets encrypted with AES-256-GCM.
- Creates and verifies GitHub webhooks for linked repositories.
- Handles task/project cleanup on task.deleted and project.deleted events.
| | GitHub (`com.paca.github`) | GitLab (`com.paca.gitlab`) |
|--|---------------------------|----------------------------|
| Oggetto | Pull request | Merge request (`iid`) |
| Auth | PAT | Personal **o** Project **o** Group Access Token |
| Host API | `api.github.com` fisso | `instance_url` (default `https://gitlab.com`) |
| Webhook | `X-Hub-Signature-256` | `signing_token` HMAC (GitLab **19+**, Standard Webhooks) |
| CI | Checks / commit status | Pipelines MR |
| Review | PR reviews | Note + Approvals API (`APPROVE` / `REQUEST_CHANGES` / `COMMENT`) |
| UI | inglese | i18n **en** + **it** |

### Frontend (frontend/)
---

- Vite + React + TanStack Query.
- Exposed as a Module Federation remote (com_paca_github).
- Mounted at:
- project.settings.tab (GitHubSettingsTab)
- task.detail.section (GitHubTaskSection)
- Communicates with backend via plugin API routes under /api/v1/plugins/com.paca.github/projects/:projectId/...
## Setup

### MCP (mcp/)
### Config host Paca

- Built with @paca-ai/plugin-sdk-mcp.
- Published as an ESM bundle (mcp.js).
- Exposes tools for integration management, repository linking, PR linking, and branch operations.
| Chiave | Obbligatoria | Descrizione |
|--------|--------------|-------------|
| `ENCRYPTION_KEY` | sì | 64 caratteri hex (chiave AES-256-GCM per token e signing token webhook) |
| `PUBLIC_URL` | sì per i webhook | URL pubblico del server Paca (callback webhook) |

---
Senza `PUBLIC_URL` il link di un progetto GitLab fallisce: la creazione webhook richiede un URL raggiungibile.

## Required Configuration
Outbound in `plugin.json`: `gitlab.com`. Per un’istanza Self-Managed andrebbe aggiunto l’hostname in `allowedOutboundDomains` prima del build (test Self-Managed non in scope per ora).

The backend uses the following config keys:
### Token GitLab

- ENCRYPTION_KEY: 64 hex chars (32-byte key) used for AES-256-GCM encryption of tokens/secrets.
- PUBLIC_URL: public base URL of the Paca server, used to build the webhook callback URL.
Supportati (obbligatori in prodotto):

Without PUBLIC_URL, linking repositories will fail because webhook creation requires a reachable callback URL.
1. **Personal Access Token**
2. **Project Access Token**
3. **Group Access Token**

---
Scope consigliato: [`api`](https://docs.gitlab.com/security/tokens/access_token_scopes/).
Per creare project webhook serve ruolo **Maintainer** o **Owner** sul progetto GitLab ([webhooks](https://docs.gitlab.com/user/project/integrations/webhooks/)).

## API Endpoints

All routes are prefixed with /projects/:projectId by the host.
The caller must be an authenticated member of the project unless noted otherwise.

| Method | Path | Description |
|--------|------|-------------|
| GET | /github | Get integration status for the current project |
| POST | /github/token | Set or replace GitHub PAT |
| DELETE | /github/token | Delete GitHub PAT and integration |
| GET | /github/repositories | List repositories accessible by the token |
| GET | /github/linked-repositories | List repositories linked to the project |
| POST | /github/linked-repositories | Link a repository and create webhook |
| DELETE | /github/linked-repositories/:repoId | Unlink a repository |
| GET | /tasks/:taskId/github/pull-requests | List pull requests linked to a task |
| POST | /tasks/:taskId/github/pull-requests | Link a pull request to a task |
| DELETE | /tasks/:taskId/github/pull-requests/:prId | Unlink a pull request from a task |
| POST | /tasks/:taskId/github/branches | Create branch and link it to a task |
| GET | /tasks/:taskId/github/branches | List branches linked to a task |
| POST | /webhook (public) | Receive GitHub webhook events |

### Example requests

Set token:

```json
{ "token": "ghp_xxx" }
```
In Project Settings → **GitLab**:

Link repository:
- **Instance URL** (opzionale): default `https://gitlab.com`
- **Token type**: personal / project / group
- **Access token**: valore del token

```json
{ "owner": "Paca-AI", "repo_name": "paca" }
```
Project/Group token vedono solo il proprio scope: la lista “accessible projects” può essere corta o un solo progetto — è normale.

Link PR to task:
### Webhook (GitLab 19+)

```json
{ "repo_id": "550e8400-e29b-41d4-a716-446655440000", "pr_number": 42 }
```
Alla link di un progetto il plugin crea un hook con:

Create branch for task:
- `push_events`, `merge_requests_events`, `pipeline_events`
- **`signing_token`** (`whsec_…`) — path primario di verifica (niente secret token legacy)

```json
{
"repo_id": "550e8400-e29b-41d4-a716-446655440000",
"branch_name": "feat/PROJ-42-github-integration",
"source_branch": "main"
}
```
Header attesi in ingresso: `X-Gitlab-Event`, `webhook-id`, `webhook-timestamp`, `webhook-signature`.

---

## Webhook Behavior
## Architettura

On linked repositories, GitHub webhooks are configured for:
```text
├── backend/ Go → WASM (plugin-sdk-go), schema plugin_data_com_paca_gitlab
├── frontend/ React Module Federation + i18n (en/it)
├── mcp/ Tool MCP Paca-centrici
└── skills/ paca-gitlab-workflow
```

- push
- pull_request
- check_run
Plugin ID: `com.paca.gitlab` · permesso custom: `gitlab.manage`

Current behavior includes:
### Route principali

- Verifying X-Hub-Signature-256 with the stored webhook secret.
- Caching/updating pull request state in plugin tables.
- Auto-linking PRs when opened/reopened from a branch already linked to a task.
- Auto-linking created branches when branch names include task references like PROJ-42.
- Emitting plugin events:
- github.pr_linked
- github.pr_updated
- github.branch_linked
Prefisso host: `/api/v1/plugins/com.paca.gitlab/projects/:projectId/…`

| Metodo | Path | Note |
|--------|------|------|
| GET/POST/DELETE | `/integration`, `/integration/token` | stato + set/remove token (`instance_url`, `token_kind`) |
| GET | `/integration/accessible-repos` | progetti visibili al token |
| GET/POST/DELETE | `/repositories`… | link/unlink + webhook |
| * | `/tasks/:taskId/pull-requests`… | MR (path legacy “pull-requests”, semantica MR) |
| * | `/tasks/:taskId/branches`… | branch |
| POST | `/webhook` | pubblico (firma signing_token) |

---

## Database Schema
## i18n

Tables live in plugin_data_com_paca_github and are created by backend/migrations/0001_create_github_tables.sql.

```text
github_integrations
id UUID PK
project_id UUID UNIQUE -> projects(id) ON DELETE CASCADE
access_token_enc TEXT
created_at TIMESTAMPTZ
updated_at TIMESTAMPTZ

github_repositories
id UUID PK
project_id UUID -> projects(id) ON DELETE CASCADE
integration_id UUID -> github_integrations(id) ON DELETE CASCADE
owner TEXT
repo_name TEXT
full_name TEXT
webhook_id BIGINT
webhook_secret_enc TEXT
default_branch TEXT
created_at TIMESTAMPTZ
updated_at TIMESTAMPTZ

github_pull_requests
id UUID PK
project_id UUID -> projects(id) ON DELETE CASCADE
repo_id UUID -> github_repositories(id) ON DELETE CASCADE
pr_number INT
github_pr_id BIGINT
title TEXT
state TEXT
html_url TEXT
head_branch TEXT
base_branch TEXT
author TEXT
merged_at TIMESTAMPTZ
created_at TIMESTAMPTZ
updated_at TIMESTAMPTZ

github_task_pr_links
id UUID PK
task_id UUID -> tasks(id) ON DELETE CASCADE
pull_request_id UUID -> github_pull_requests(id) ON DELETE CASCADE
created_at TIMESTAMPTZ

github_task_branches
id UUID PK
task_id UUID -> tasks(id) ON DELETE CASCADE
repo_id UUID -> github_repositories(id) ON DELETE CASCADE
branch_name TEXT
created_at TIMESTAMPTZ
```
Frontend: cataloghi `en` e `it` in `frontend/src/i18n/`.
Locale da prop host `locale` oppure `navigator.language` (fallback `en`).

---

## Development
## Sviluppo

### Backend

```bash
cd backend

# Run tests
go test -v ./...

# Build WASM binary (requires TinyGo — see https://tinygo.org/getting-started/install/)
tinygo build -target=wasip1 -buildmode=c-shared -o github.wasm .
go test ./...
tinygo build -target=wasip1 -buildmode=c-shared -o gitlab.wasm .
```

### Frontend
### Frontend / MCP

```bash
cd frontend

# Install dependencies
bun install

# Development build (watch)
bun run dev

# Production build
bun run build
cd frontend && bun install && bun run typecheck && bun run build
cd mcp && bun install && bun run typecheck && bun run build
```

### MCP
### Release

```bash
cd mcp

# Install dependencies
bun install

# Typecheck
bun run typecheck

# Production build (outputs mcp.js)
bun run build
```
Workflow GitHub Actions: `.github/workflows/release.yml` su tag `v*` → artifact `gitlab-*.tar.gz`.

---

## Release (CD)

The release workflow is at .github/workflows/release.yml.

It runs on:

- tag push matching v*
- manual workflow_dispatch
## Note

The workflow builds backend/frontend/mcp assets, packages migrations and plugin.json, generates SHA256 checksums, and uploads all artifacts to a GitHub Release.
- **Self-Managed:** campo `instance_url` presente; smoke/test su istanze private **deferred** finché non c’è un’istanza da usare. Validazione primaria prevista su GitLab.com.
- Coesistenza GitHub+GitLab sullo stesso progetto Paca: **non** in scope.
- Minimo GitLab: **19+** (firma webhook `signing_token`).
Loading
Loading