Some checks failed
Build and Release / Create Release (push) Successful in 0s
Build and Release / Unit Tests (push) Successful in 3m19s
Build and Release / Integration Tests (PostgreSQL) (push) Successful in 4m39s
Build and Release / Lint (push) Successful in 4m46s
Build and Release / Build Binary (linux/arm64) (push) Has been cancelled
Build and Release / Build Binaries (amd64, linux, linux-latest) (push) Has been cancelled
Build and Release / Build Binaries (amd64, windows, windows-latest) (push) Has been cancelled
Build and Release / Build Binaries (amd64, darwin, macos) (push) Has been cancelled
Build and Release / Build Binaries (arm64, darwin, macos) (push) Has been cancelled
Add API.md (3200+ lines) with complete REST API documentation covering authentication, repository management, issues, pull requests, organizations, package registry, Actions, and Vault APIs. Includes code examples and error handling. Add GUIDE.md with user-focused documentation for getting started, repository operations, collaboration workflows, and CI/CD setup. Implement documentation tab in repository view with automatic detection and rendering of API.md and GUIDE.md files alongside README. Add locale strings and template for doc tab navigation.
3226 lines
64 KiB
Markdown
3226 lines
64 KiB
Markdown
# GitCaddy API Reference
|
|
|
|
Complete technical reference for developers integrating with or using GitCaddy, a self-hosted Git repository management platform.
|
|
|
|
## Table of Contents
|
|
|
|
- [Introduction](#introduction)
|
|
- [Authentication](#authentication)
|
|
- [Personal Access Tokens](#personal-access-tokens)
|
|
- [OAuth2 Authentication](#oauth2-authentication)
|
|
- [WebAuthn (Passkeys)](#webauthn-passkeys)
|
|
- [REST API Endpoints](#rest-api-endpoints)
|
|
- [Repository APIs](#repository-apis)
|
|
- [Issue APIs](#issue-apis)
|
|
- [Pull Request APIs](#pull-request-apis)
|
|
- [Organization APIs](#organization-apis)
|
|
- [User APIs](#user-apis)
|
|
- [Package Registry APIs](#package-registry-apis)
|
|
- [Actions APIs](#actions-apis)
|
|
- [Vault APIs](#vault-apis)
|
|
- [Git Protocol](#git-protocol)
|
|
- [WebSocket APIs](#websocket-apis)
|
|
- [Frontend JavaScript APIs](#frontend-javascript-apis)
|
|
- [Core Utilities](#core-utilities)
|
|
- [DOM Manipulation](#dom-manipulation)
|
|
- [Web Components](#web-components)
|
|
- [Vue Components](#vue-components)
|
|
- [Error Codes](#error-codes)
|
|
- [Rate Limiting](#rate-limiting)
|
|
- [Webhooks](#webhooks)
|
|
- [Code Examples](#code-examples)
|
|
|
|
## Introduction
|
|
|
|
GitCaddy provides comprehensive REST APIs for repository management, collaboration tools, CI/CD workflows, package registry, and enterprise features. All API endpoints are accessible at:
|
|
|
|
```
|
|
https://your-gitcaddy-instance.com/api/v1/
|
|
```
|
|
|
|
**Base URL Format:**
|
|
```
|
|
https://{instance}/api/v1/{endpoint}
|
|
```
|
|
|
|
**Content Type:**
|
|
All requests and responses use `application/json` unless otherwise specified.
|
|
|
|
**API Versioning:**
|
|
Current stable version: `v1`
|
|
|
|
## Authentication
|
|
|
|
GitCaddy supports multiple authentication methods for API access.
|
|
|
|
### Personal Access Tokens
|
|
|
|
Personal access tokens (PATs) provide programmatic access to the GitCaddy API.
|
|
|
|
**Creating a Token:**
|
|
|
|
1. Navigate to User Settings → Applications → Manage Access Tokens
|
|
2. Click "Generate New Token"
|
|
3. Set token name and scopes
|
|
4. Copy the generated token (shown only once)
|
|
|
|
**Using Tokens:**
|
|
|
|
Include the token in the `Authorization` header:
|
|
|
|
```http
|
|
Authorization: token YOUR_ACCESS_TOKEN
|
|
```
|
|
|
|
**Example Request:**
|
|
|
|
```bash
|
|
curl -H "Authorization: token abc123def456" \
|
|
https://gitcaddy.example.com/api/v1/user
|
|
```
|
|
|
|
**Token Scopes:**
|
|
|
|
| Scope | Description |
|
|
|-------|-------------|
|
|
| `repo` | Full access to repositories |
|
|
| `repo:status` | Read-only access to repository status |
|
|
| `public_repo` | Access to public repositories only |
|
|
| `admin:org` | Full organization administration |
|
|
| `write:org` | Write access to organizations |
|
|
| `read:org` | Read-only organization access |
|
|
| `admin:public_key` | Manage public SSH keys |
|
|
| `admin:repo_hook` | Manage repository webhooks |
|
|
| `admin:org_hook` | Manage organization webhooks |
|
|
| `notification` | Access notifications |
|
|
| `user` | Full user profile access |
|
|
| `read:user` | Read-only user profile access |
|
|
| `user:email` | Access user email addresses |
|
|
| `delete_repo` | Delete repositories |
|
|
| `package` | Access package registry |
|
|
| `admin:gpg_key` | Manage GPG keys |
|
|
| `admin:application` | Manage OAuth applications |
|
|
|
|
### OAuth2 Authentication
|
|
|
|
GitCaddy supports OAuth2 for third-party application integration.
|
|
|
|
**OAuth2 Flow:**
|
|
|
|
1. **Register Application:**
|
|
- User Settings → Applications → OAuth2 Applications
|
|
- Create new OAuth2 application
|
|
- Note `client_id` and `client_secret`
|
|
|
|
2. **Authorization Request:**
|
|
|
|
```http
|
|
GET /login/oauth/authorize?client_id={client_id}&redirect_uri={redirect_uri}&response_type=code&state={state}
|
|
```
|
|
|
|
3. **Token Exchange:**
|
|
|
|
```http
|
|
POST /login/oauth/access_token
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"client_id": "your_client_id",
|
|
"client_secret": "your_client_secret",
|
|
"code": "authorization_code",
|
|
"grant_type": "authorization_code",
|
|
"redirect_uri": "https://your-app.com/callback"
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"access_token": "gho_16C7e42F292c6912E7710c838347Ae178B4a",
|
|
"token_type": "bearer",
|
|
"scope": "repo,user"
|
|
}
|
|
```
|
|
|
|
4. **Using Access Token:**
|
|
|
|
```http
|
|
Authorization: Bearer gho_16C7e42F292c6912E7710c838347Ae178B4a
|
|
```
|
|
|
|
**Supported OAuth2 Providers:**
|
|
|
|
GitCaddy can authenticate users via external OAuth2 providers:
|
|
- GitHub
|
|
- GitLab
|
|
- Bitbucket
|
|
- Google
|
|
- Microsoft Azure AD
|
|
- Custom OpenID Connect providers
|
|
|
|
### WebAuthn (Passkeys)
|
|
|
|
GitCaddy supports WebAuthn for passwordless authentication and two-factor authentication.
|
|
|
|
**Registration API:**
|
|
|
|
```javascript
|
|
// Client-side registration
|
|
const response = await fetch('/user/settings/security/webauthn/register', {
|
|
method: 'GET',
|
|
credentials: 'include'
|
|
});
|
|
|
|
const options = await response.json();
|
|
|
|
// Create credential
|
|
const credential = await navigator.credentials.create({
|
|
publicKey: options
|
|
});
|
|
|
|
// Send credential to server
|
|
await fetch('/user/settings/security/webauthn/register', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
name: 'My Security Key',
|
|
credential: credential
|
|
})
|
|
});
|
|
```
|
|
|
|
**Authentication API:**
|
|
|
|
```javascript
|
|
// Get authentication challenge
|
|
const response = await fetch('/user/webauthn/assertion', {
|
|
method: 'GET'
|
|
});
|
|
|
|
const options = await response.json();
|
|
|
|
// Get credential
|
|
const assertion = await navigator.credentials.get({
|
|
publicKey: options
|
|
});
|
|
|
|
// Verify assertion
|
|
await fetch('/user/webauthn/assertion', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(assertion)
|
|
});
|
|
```
|
|
|
|
## REST API Endpoints
|
|
|
|
### Repository APIs
|
|
|
|
#### List User Repositories
|
|
|
|
```http
|
|
GET /api/v1/user/repos
|
|
```
|
|
|
|
**Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `page` | integer | No | Page number (default: 1) |
|
|
| `limit` | integer | No | Page size (default: 10, max: 50) |
|
|
| `type` | string | No | Filter by type: `owner`, `collaborator`, `member` |
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
[
|
|
{
|
|
"id": 1,
|
|
"owner": {
|
|
"id": 1,
|
|
"login": "username",
|
|
"full_name": "User Name",
|
|
"avatar_url": "https://gitcaddy.example.com/avatars/1"
|
|
},
|
|
"name": "my-repo",
|
|
"full_name": "username/my-repo",
|
|
"description": "Repository description",
|
|
"private": false,
|
|
"fork": false,
|
|
"parent": null,
|
|
"empty": false,
|
|
"mirror": false,
|
|
"size": 1024,
|
|
"html_url": "https://gitcaddy.example.com/username/my-repo",
|
|
"ssh_url": "git@gitcaddy.example.com:username/my-repo.git",
|
|
"clone_url": "https://gitcaddy.example.com/username/my-repo.git",
|
|
"website": "",
|
|
"stars_count": 5,
|
|
"forks_count": 2,
|
|
"watchers_count": 3,
|
|
"open_issues_count": 1,
|
|
"default_branch": "main",
|
|
"archived": false,
|
|
"created_at": "2024-01-01T00:00:00Z",
|
|
"updated_at": "2024-01-15T12:30:00Z",
|
|
"permissions": {
|
|
"admin": true,
|
|
"push": true,
|
|
"pull": true
|
|
}
|
|
}
|
|
]
|
|
```
|
|
|
|
#### Create Repository
|
|
|
|
```http
|
|
POST /api/v1/user/repos
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"name": "new-repo",
|
|
"description": "My new repository",
|
|
"private": false,
|
|
"auto_init": true,
|
|
"gitignores": "Go",
|
|
"license": "MIT",
|
|
"readme": "Default",
|
|
"default_branch": "main"
|
|
}
|
|
```
|
|
|
|
**Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `name` | string | Yes | Repository name |
|
|
| `description` | string | No | Repository description |
|
|
| `private` | boolean | No | Create as private (default: false) |
|
|
| `auto_init` | boolean | No | Initialize with README (default: false) |
|
|
| `gitignores` | string | No | .gitignore template name |
|
|
| `license` | string | No | License template (MIT, Apache-2.0, GPL-3.0, etc.) |
|
|
| `readme` | string | No | README template |
|
|
| `default_branch` | string | No | Default branch name (default: main) |
|
|
| `trust_model` | string | No | Trust model: `default`, `collaborator`, `committer`, `collaboratorcommitter` |
|
|
|
|
**Response:** `201 Created`
|
|
|
|
```json
|
|
{
|
|
"id": 2,
|
|
"name": "new-repo",
|
|
"full_name": "username/new-repo",
|
|
"private": false,
|
|
"html_url": "https://gitcaddy.example.com/username/new-repo",
|
|
"clone_url": "https://gitcaddy.example.com/username/new-repo.git",
|
|
"created_at": "2024-01-20T10:00:00Z"
|
|
}
|
|
```
|
|
|
|
#### Get Repository
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"id": 1,
|
|
"owner": {
|
|
"id": 1,
|
|
"login": "username",
|
|
"full_name": "User Name"
|
|
},
|
|
"name": "my-repo",
|
|
"full_name": "username/my-repo",
|
|
"description": "Repository description",
|
|
"private": false,
|
|
"default_branch": "main",
|
|
"permissions": {
|
|
"admin": true,
|
|
"push": true,
|
|
"pull": true
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Delete Repository
|
|
|
|
```http
|
|
DELETE /api/v1/repos/{owner}/{repo}
|
|
```
|
|
|
|
**Response:** `204 No Content`
|
|
|
|
#### List Repository Branches
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/branches
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
[
|
|
{
|
|
"name": "main",
|
|
"commit": {
|
|
"id": "abc123def456",
|
|
"message": "Initial commit",
|
|
"url": "https://gitcaddy.example.com/username/my-repo/commit/abc123def456"
|
|
},
|
|
"protected": true,
|
|
"user_can_push": true,
|
|
"user_can_merge": true
|
|
}
|
|
]
|
|
```
|
|
|
|
#### Create Branch
|
|
|
|
```http
|
|
POST /api/v1/repos/{owner}/{repo}/branches
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"new_branch_name": "feature-branch",
|
|
"old_branch_name": "main"
|
|
}
|
|
```
|
|
|
|
#### Get Branch Protection
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/branch_protections/{branch}
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"branch_name": "main",
|
|
"enable_push": false,
|
|
"enable_push_whitelist": true,
|
|
"push_whitelist_usernames": ["admin"],
|
|
"push_whitelist_teams": ["core-team"],
|
|
"push_whitelist_deploy_keys": false,
|
|
"enable_merge_whitelist": true,
|
|
"merge_whitelist_usernames": ["maintainer"],
|
|
"merge_whitelist_teams": ["reviewers"],
|
|
"enable_status_check": true,
|
|
"status_check_contexts": ["ci/tests", "ci/lint"],
|
|
"required_approvals": 2,
|
|
"enable_approvals_whitelist": false,
|
|
"block_on_rejected_reviews": true,
|
|
"dismiss_stale_approvals": true,
|
|
"require_signed_commits": true,
|
|
"protected_file_patterns": "*.lock",
|
|
"unprotected_file_patterns": "docs/*"
|
|
}
|
|
```
|
|
|
|
#### List Repository Tags
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/tags
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
[
|
|
{
|
|
"name": "v1.0.0",
|
|
"commit": {
|
|
"sha": "abc123def456",
|
|
"url": "https://gitcaddy.example.com/username/my-repo/commit/abc123def456"
|
|
},
|
|
"zipball_url": "https://gitcaddy.example.com/username/my-repo/archive/v1.0.0.zip",
|
|
"tarball_url": "https://gitcaddy.example.com/username/my-repo/archive/v1.0.0.tar.gz"
|
|
}
|
|
]
|
|
```
|
|
|
|
#### Get File Contents
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/contents/{filepath}?ref={branch}
|
|
```
|
|
|
|
**Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `ref` | string | No | Branch, tag, or commit SHA (default: default branch) |
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"name": "README.md",
|
|
"path": "README.md",
|
|
"sha": "abc123",
|
|
"type": "file",
|
|
"size": 1024,
|
|
"encoding": "base64",
|
|
"content": "IyBNeVJlcG8KClRoaXMgaXMgYSBSRUFETUUgZmlsZS4=",
|
|
"target": null,
|
|
"url": "https://gitcaddy.example.com/api/v1/repos/username/my-repo/contents/README.md",
|
|
"html_url": "https://gitcaddy.example.com/username/my-repo/src/branch/main/README.md",
|
|
"git_url": "https://gitcaddy.example.com/api/v1/repos/username/my-repo/git/blobs/abc123",
|
|
"download_url": "https://gitcaddy.example.com/username/my-repo/raw/branch/main/README.md"
|
|
}
|
|
```
|
|
|
|
#### Create/Update File
|
|
|
|
```http
|
|
POST /api/v1/repos/{owner}/{repo}/contents/{filepath}
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"content": "base64_encoded_content",
|
|
"message": "Create README.md",
|
|
"branch": "main",
|
|
"sha": "abc123def456",
|
|
"author": {
|
|
"name": "Author Name",
|
|
"email": "author@example.com"
|
|
},
|
|
"committer": {
|
|
"name": "Committer Name",
|
|
"email": "committer@example.com"
|
|
},
|
|
"dates": {
|
|
"author": "2024-01-20T10:00:00Z",
|
|
"committer": "2024-01-20T10:00:00Z"
|
|
},
|
|
"signoff": false,
|
|
"new_branch": "feature-branch"
|
|
}
|
|
```
|
|
|
|
**Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `content` | string | Yes | Base64-encoded file content |
|
|
| `message` | string | Yes | Commit message |
|
|
| `branch` | string | No | Branch name (default: default branch) |
|
|
| `sha` | string | No | Blob SHA for update (required when updating) |
|
|
| `new_branch` | string | No | Create new branch for commit |
|
|
|
|
#### Delete File
|
|
|
|
```http
|
|
DELETE /api/v1/repos/{owner}/{repo}/contents/{filepath}
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"message": "Delete file",
|
|
"sha": "abc123def456",
|
|
"branch": "main"
|
|
}
|
|
```
|
|
|
|
#### Fork Repository
|
|
|
|
```http
|
|
POST /api/v1/repos/{owner}/{repo}/forks
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"organization": "my-org",
|
|
"name": "forked-repo"
|
|
}
|
|
```
|
|
|
|
#### List Forks
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/forks
|
|
```
|
|
|
|
#### Mirror Repository
|
|
|
|
```http
|
|
POST /api/v1/repos/migrate
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"clone_addr": "https://github.com/user/repo.git",
|
|
"auth_username": "username",
|
|
"auth_password": "password_or_token",
|
|
"uid": 1,
|
|
"repo_name": "mirrored-repo",
|
|
"mirror": true,
|
|
"private": false,
|
|
"description": "Mirrored repository"
|
|
}
|
|
```
|
|
|
|
### Issue APIs
|
|
|
|
#### List Repository Issues
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/issues
|
|
```
|
|
|
|
**Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `state` | string | No | Filter by state: `open`, `closed`, `all` (default: open) |
|
|
| `labels` | string | No | Comma-separated label IDs or names |
|
|
| `q` | string | No | Search query |
|
|
| `type` | string | No | Filter by type: `issues`, `pulls` |
|
|
| `milestones` | string | No | Comma-separated milestone names |
|
|
| `since` | string | No | Only issues updated after this time (ISO 8601) |
|
|
| `before` | string | No | Only issues updated before this time (ISO 8601) |
|
|
| `created_by` | string | No | Filter by creator username |
|
|
| `assigned_by` | string | No | Filter by assignee username |
|
|
| `mentioned_by` | string | No | Filter by mentioned username |
|
|
| `page` | integer | No | Page number |
|
|
| `limit` | integer | No | Page size |
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
[
|
|
{
|
|
"id": 1,
|
|
"url": "https://gitcaddy.example.com/api/v1/repos/username/my-repo/issues/1",
|
|
"html_url": "https://gitcaddy.example.com/username/my-repo/issues/1",
|
|
"number": 1,
|
|
"user": {
|
|
"id": 1,
|
|
"login": "username",
|
|
"full_name": "User Name"
|
|
},
|
|
"title": "Bug: Application crashes on startup",
|
|
"body": "Detailed description of the issue...",
|
|
"labels": [
|
|
{
|
|
"id": 1,
|
|
"name": "bug",
|
|
"color": "#ee0701",
|
|
"description": "Something isn't working"
|
|
}
|
|
],
|
|
"milestone": {
|
|
"id": 1,
|
|
"title": "v1.0",
|
|
"description": "First release",
|
|
"state": "open",
|
|
"due_on": "2024-12-31T23:59:59Z"
|
|
},
|
|
"assignees": [
|
|
{
|
|
"id": 2,
|
|
"login": "developer"
|
|
}
|
|
],
|
|
"state": "open",
|
|
"is_locked": false,
|
|
"comments": 3,
|
|
"created_at": "2024-01-15T10:00:00Z",
|
|
"updated_at": "2024-01-16T14:30:00Z",
|
|
"closed_at": null,
|
|
"due_date": null,
|
|
"pull_request": null
|
|
}
|
|
]
|
|
```
|
|
|
|
#### Create Issue
|
|
|
|
```http
|
|
POST /api/v1/repos/{owner}/{repo}/issues
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"title": "New issue title",
|
|
"body": "Issue description with **markdown** support",
|
|
"assignees": ["developer1", "developer2"],
|
|
"labels": [1, 2],
|
|
"milestone": 1,
|
|
"closed": false,
|
|
"due_date": "2024-12-31T23:59:59Z",
|
|
"ref": "main"
|
|
}
|
|
```
|
|
|
|
**Response:** `201 Created`
|
|
|
|
#### Get Issue
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/issues/{index}
|
|
```
|
|
|
|
#### Update Issue
|
|
|
|
```http
|
|
PATCH /api/v1/repos/{owner}/{repo}/issues/{index}
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"title": "Updated title",
|
|
"body": "Updated description",
|
|
"state": "closed",
|
|
"assignees": ["user1"],
|
|
"labels": [1, 3],
|
|
"milestone": 2,
|
|
"due_date": "2024-12-31T23:59:59Z",
|
|
"unset_due_date": false
|
|
}
|
|
```
|
|
|
|
#### List Issue Comments
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/issues/{index}/comments
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
[
|
|
{
|
|
"id": 1,
|
|
"html_url": "https://gitcaddy.example.com/username/my-repo/issues/1#issuecomment-1",
|
|
"pull_request_url": "",
|
|
"issue_url": "https://gitcaddy.example.com/api/v1/repos/username/my-repo/issues/1",
|
|
"user": {
|
|
"id": 1,
|
|
"login": "username"
|
|
},
|
|
"original_author": "",
|
|
"original_author_id": 0,
|
|
"body": "This is a comment on the issue",
|
|
"created_at": "2024-01-15T11:00:00Z",
|
|
"updated_at": "2024-01-15T11:00:00Z"
|
|
}
|
|
]
|
|
```
|
|
|
|
#### Create Issue Comment
|
|
|
|
```http
|
|
POST /api/v1/repos/{owner}/{repo}/issues/{index}/comments
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"body": "Comment text with **markdown** support"
|
|
}
|
|
```
|
|
|
|
#### Edit Issue Comment
|
|
|
|
```http
|
|
PATCH /api/v1/repos/{owner}/{repo}/issues/comments/{id}
|
|
```
|
|
|
|
#### Delete Issue Comment
|
|
|
|
```http
|
|
DELETE /api/v1/repos/{owner}/{repo}/issues/comments/{id}
|
|
```
|
|
|
|
#### List Issue Labels
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/labels
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
[
|
|
{
|
|
"id": 1,
|
|
"name": "bug",
|
|
"color": "#ee0701",
|
|
"description": "Something isn't working",
|
|
"url": "https://gitcaddy.example.com/api/v1/repos/username/my-repo/labels/1"
|
|
}
|
|
]
|
|
```
|
|
|
|
#### Create Label
|
|
|
|
```http
|
|
POST /api/v1/repos/{owner}/{repo}/labels
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"name": "enhancement",
|
|
"color": "#a2eeef",
|
|
"description": "New feature or request"
|
|
}
|
|
```
|
|
|
|
#### Track Time on Issue
|
|
|
|
```http
|
|
POST /api/v1/repos/{owner}/{repo}/issues/{index}/times
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"time": 3600,
|
|
"created": "2024-01-15T10:00:00Z",
|
|
"user_name": "username"
|
|
}
|
|
```
|
|
|
|
**Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `time` | integer | Yes | Time in seconds |
|
|
| `created` | string | No | Timestamp (ISO 8601) |
|
|
| `user_name` | string | No | Username (admin only) |
|
|
|
|
#### List Tracked Times
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/issues/{index}/times
|
|
```
|
|
|
|
### Pull Request APIs
|
|
|
|
#### List Pull Requests
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/pulls
|
|
```
|
|
|
|
**Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `state` | string | No | Filter by state: `open`, `closed`, `all` |
|
|
| `sort` | string | No | Sort by: `oldest`, `recentupdate`, `leastupdate`, `mostcomment`, `leastcomment`, `priority` |
|
|
| `milestone` | integer | No | Filter by milestone ID |
|
|
| `labels` | string | No | Comma-separated label IDs |
|
|
| `page` | integer | No | Page number |
|
|
| `limit` | integer | No | Page size |
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
[
|
|
{
|
|
"id": 1,
|
|
"url": "https://gitcaddy.example.com/api/v1/repos/username/my-repo/pulls/1",
|
|
"number": 1,
|
|
"user": {
|
|
"id": 1,
|
|
"login": "contributor"
|
|
},
|
|
"title": "Add new feature",
|
|
"body": "This PR adds a new feature...",
|
|
"labels": [],
|
|
"milestone": null,
|
|
"assignees": [],
|
|
"state": "open",
|
|
"is_locked": false,
|
|
"comments": 2,
|
|
"html_url": "https://gitcaddy.example.com/username/my-repo/pulls/1",
|
|
"diff_url": "https://gitcaddy.example.com/username/my-repo/pulls/1.diff",
|
|
"patch_url": "https://gitcaddy.example.com/username/my-repo/pulls/1.patch",
|
|
"mergeable": true,
|
|
"merged": false,
|
|
"merged_at": null,
|
|
"merge_commit_sha": null,
|
|
"merged_by": null,
|
|
"base": {
|
|
"label": "main",
|
|
"ref": "main",
|
|
"sha": "abc123",
|
|
"repo_id": 1,
|
|
"repo": {
|
|
"id": 1,
|
|
"name": "my-repo",
|
|
"full_name": "username/my-repo"
|
|
}
|
|
},
|
|
"head": {
|
|
"label": "contributor:feature-branch",
|
|
"ref": "feature-branch",
|
|
"sha": "def456",
|
|
"repo_id": 2,
|
|
"repo": {
|
|
"id": 2,
|
|
"name": "my-repo",
|
|
"full_name": "contributor/my-repo"
|
|
}
|
|
},
|
|
"merge_base": "abc123",
|
|
"due_date": null,
|
|
"created_at": "2024-01-15T10:00:00Z",
|
|
"updated_at": "2024-01-16T14:30:00Z",
|
|
"closed_at": null
|
|
}
|
|
]
|
|
```
|
|
|
|
#### Create Pull Request
|
|
|
|
```http
|
|
POST /api/v1/repos/{owner}/{repo}/pulls
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"title": "Add new feature",
|
|
"body": "Detailed description of changes",
|
|
"head": "feature-branch",
|
|
"base": "main",
|
|
"assignees": ["reviewer1"],
|
|
"labels": [1],
|
|
"milestone": 1,
|
|
"due_date": "2024-12-31T23:59:59Z"
|
|
}
|
|
```
|
|
|
|
**Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `title` | string | Yes | PR title |
|
|
| `body` | string | No | PR description |
|
|
| `head` | string | Yes | Branch containing changes |
|
|
| `base` | string | Yes | Branch to merge into |
|
|
| `assignees` | array | No | Array of usernames |
|
|
| `labels` | array | No | Array of label IDs |
|
|
| `milestone` | integer | No | Milestone ID |
|
|
|
|
#### Get Pull Request
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/pulls/{index}
|
|
```
|
|
|
|
#### Update Pull Request
|
|
|
|
```http
|
|
PATCH /api/v1/repos/{owner}/{repo}/pulls/{index}
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"title": "Updated title",
|
|
"body": "Updated description",
|
|
"base": "main",
|
|
"assignees": ["reviewer1", "reviewer2"],
|
|
"labels": [1, 2],
|
|
"milestone": 1,
|
|
"state": "closed",
|
|
"due_date": "2024-12-31T23:59:59Z"
|
|
}
|
|
```
|
|
|
|
#### Merge Pull Request
|
|
|
|
```http
|
|
POST /api/v1/repos/{owner}/{repo}/pulls/{index}/merge
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"Do": "merge",
|
|
"MergeMessageField": "Merge pull request #1",
|
|
"MergeTitleField": "Add new feature",
|
|
"delete_branch_after_merge": true,
|
|
"force_merge": false,
|
|
"head_commit_id": "def456",
|
|
"merge_when_checks_succeed": false
|
|
}
|
|
```
|
|
|
|
**Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `Do` | string | Yes | Merge method: `merge`, `rebase`, `rebase-merge`, `squash`, `manually-merged` |
|
|
| `MergeMessageField` | string | No | Merge commit message |
|
|
| `MergeTitleField` | string | No | Merge commit title |
|
|
| `delete_branch_after_merge` | boolean | No | Delete head branch after merge |
|
|
| `force_merge` | boolean | No | Force merge even if checks fail |
|
|
| `head_commit_id` | string | No | Expected head commit SHA |
|
|
|
|
#### List PR Reviews
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/pulls/{index}/reviews
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
[
|
|
{
|
|
"id": 1,
|
|
"user": {
|
|
"id": 2,
|
|
"login": "reviewer"
|
|
},
|
|
"body": "Looks good to me!",
|
|
"commit_id": "def456",
|
|
"state": "APPROVED",
|
|
"html_url": "https://gitcaddy.example.com/username/my-repo/pulls/1#pullrequestreview-1",
|
|
"submitted_at": "2024-01-16T10:00:00Z"
|
|
}
|
|
]
|
|
```
|
|
|
|
#### Create PR Review
|
|
|
|
```http
|
|
POST /api/v1/repos/{owner}/{repo}/pulls/{index}/reviews
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"body": "Review comment",
|
|
"event": "APPROVED",
|
|
"comments": [
|
|
{
|
|
"path": "src/main.go",
|
|
"body": "Consider refactoring this function",
|
|
"old_position": 0,
|
|
"new_position": 42
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `body` | string | No | Overall review comment |
|
|
| `event` | string | Yes | Review state: `APPROVED`, `REQUEST_CHANGES`, `COMMENT` |
|
|
| `comments` | array | No | Array of line comments |
|
|
|
|
#### Submit PR Review
|
|
|
|
```http
|
|
POST /api/v1/repos/{owner}/{repo}/pulls/{index}/reviews/{id}
|
|
```
|
|
|
|
#### Dismiss PR Review
|
|
|
|
```http
|
|
POST /api/v1/repos/{owner}/{repo}/pulls/{index}/reviews/{id}/dismissals
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"message": "Outdated review"
|
|
}
|
|
```
|
|
|
|
#### Request AI Code Review
|
|
|
|
```http
|
|
POST /api/v1/repos/{owner}/{repo}/pulls/{index}/ai-review
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"provider": "openai",
|
|
"model": "gpt-4"
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"review_id": 42,
|
|
"status": "pending",
|
|
"message": "AI review requested. Results will be posted as comments."
|
|
}
|
|
```
|
|
|
|
### Organization APIs
|
|
|
|
#### List Organizations
|
|
|
|
```http
|
|
GET /api/v1/orgs
|
|
```
|
|
|
|
**Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `page` | integer | No | Page number |
|
|
| `limit` | integer | No | Page size |
|
|
|
|
#### Get Organization
|
|
|
|
```http
|
|
GET /api/v1/orgs/{org}
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"id": 1,
|
|
"username": "my-org",
|
|
"full_name": "My Organization",
|
|
"avatar_url": "https://gitcaddy.example.com/avatars/1",
|
|
"description": "Organization description",
|
|
"website": "https://example.com",
|
|
"location": "San Francisco, CA",
|
|
"visibility": "public",
|
|
"repo_admin_change_team_access": false,
|
|
"username": "my-org"
|
|
}
|
|
```
|
|
|
|
#### Create Organization
|
|
|
|
```http
|
|
POST /api/v1/orgs
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"username": "new-org",
|
|
"full_name": "New Organization",
|
|
"description": "Organization description",
|
|
"website": "https://example.com",
|
|
"location": "San Francisco, CA",
|
|
"visibility": "public",
|
|
"repo_admin_change_team_access": false
|
|
}
|
|
```
|
|
|
|
**Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `username` | string | Yes | Organization username |
|
|
| `full_name` | string | No | Display name |
|
|
| `description` | string | No | Organization description |
|
|
| `website` | string | No | Website URL |
|
|
| `location` | string | No | Location |
|
|
| `visibility` | string | No | Visibility: `public`, `limited`, `private` |
|
|
|
|
#### Edit Organization
|
|
|
|
```http
|
|
PATCH /api/v1/orgs/{org}
|
|
```
|
|
|
|
#### Delete Organization
|
|
|
|
```http
|
|
DELETE /api/v1/orgs/{org}
|
|
```
|
|
|
|
#### List Organization Teams
|
|
|
|
```http
|
|
GET /api/v1/orgs/{org}/teams
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
[
|
|
{
|
|
"id": 1,
|
|
"name": "core-team",
|
|
"description": "Core development team",
|
|
"organization": {
|
|
"id": 1,
|
|
"username": "my-org",
|
|
"full_name": "My Organization"
|
|
},
|
|
"permission": "admin",
|
|
"can_create_org_repo": true,
|
|
"includes_all_repositories": false,
|
|
"units": [
|
|
"repo.code",
|
|
"repo.issues",
|
|
"repo.pulls",
|
|
"repo.releases",
|
|
"repo.wiki"
|
|
]
|
|
}
|
|
]
|
|
```
|
|
|
|
#### Create Team
|
|
|
|
```http
|
|
POST /api/v1/orgs/{org}/teams
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"name": "new-team",
|
|
"description": "Team description",
|
|
"permission": "write",
|
|
"can_create_org_repo": false,
|
|
"includes_all_repositories": false,
|
|
"units": [
|
|
"repo.code",
|
|
"repo.issues",
|
|
"repo.pulls"
|
|
]
|
|
}
|
|
```
|
|
|
|
**Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `name` | string | Yes | Team name |
|
|
| `description` | string | No | Team description |
|
|
| `permission` | string | No | Permission level: `read`, `write`, `admin` |
|
|
| `units` | array | No | Repository unit permissions |
|
|
|
|
#### List Team Members
|
|
|
|
```http
|
|
GET /api/v1/teams/{id}/members
|
|
```
|
|
|
|
#### Add Team Member
|
|
|
|
```http
|
|
PUT /api/v1/teams/{id}/members/{username}
|
|
```
|
|
|
|
#### Remove Team Member
|
|
|
|
```http
|
|
DELETE /api/v1/teams/{id}/members/{username}
|
|
```
|
|
|
|
#### List Team Repositories
|
|
|
|
```http
|
|
GET /api/v1/teams/{id}/repos
|
|
```
|
|
|
|
#### Add Team Repository
|
|
|
|
```http
|
|
PUT /api/v1/teams/{id}/repos/{org}/{repo}
|
|
```
|
|
|
|
### User APIs
|
|
|
|
#### Get Authenticated User
|
|
|
|
```http
|
|
GET /api/v1/user
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"id": 1,
|
|
"login": "username",
|
|
"full_name": "User Name",
|
|
"email": "user@example.com",
|
|
"avatar_url": "https://gitcaddy.example.com/avatars/1",
|
|
"language": "en-US",
|
|
"is_admin": false,
|
|
"last_login": "2024-01-20T10:00:00Z",
|
|
"created": "2023-01-01T00:00:00Z",
|
|
"restricted": false,
|
|
"active": true,
|
|
"prohibit_login": false,
|
|
"location": "San Francisco",
|
|
"website": "https://example.com",
|
|
"description": "User bio",
|
|
"visibility": "public",
|
|
"followers_count": 10,
|
|
"following_count": 5,
|
|
"starred_repos_count": 20
|
|
}
|
|
```
|
|
|
|
#### Get User
|
|
|
|
```http
|
|
GET /api/v1/users/{username}
|
|
```
|
|
|
|
#### List User Emails
|
|
|
|
```http
|
|
GET /api/v1/user/emails
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
[
|
|
{
|
|
"email": "user@example.com",
|
|
"verified": true,
|
|
"primary": true
|
|
},
|
|
{
|
|
"email": "alternate@example.com",
|
|
"verified": false,
|
|
"primary": false
|
|
}
|
|
]
|
|
```
|
|
|
|
#### Add Email
|
|
|
|
```http
|
|
POST /api/v1/user/emails
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"emails": ["new@example.com"]
|
|
}
|
|
```
|
|
|
|
#### Delete Email
|
|
|
|
```http
|
|
DELETE /api/v1/user/emails
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"emails": ["old@example.com"]
|
|
}
|
|
```
|
|
|
|
#### List SSH Keys
|
|
|
|
```http
|
|
GET /api/v1/user/keys
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
[
|
|
{
|
|
"id": 1,
|
|
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...",
|
|
"url": "https://gitcaddy.example.com/api/v1/user/keys/1",
|
|
"title": "Work Laptop",
|
|
"fingerprint": "SHA256:abc123def456",
|
|
"created_at": "2024-01-01T00:00:00Z",
|
|
"read_only": false
|
|
}
|
|
]
|
|
```
|
|
|
|
#### Add SSH Key
|
|
|
|
```http
|
|
POST /api/v1/user/keys
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"title": "My Key",
|
|
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...",
|
|
"read_only": false
|
|
}
|
|
```
|
|
|
|
#### Delete SSH Key
|
|
|
|
```http
|
|
DELETE /api/v1/user/keys/{id}
|
|
```
|
|
|
|
#### List GPG Keys
|
|
|
|
```http
|
|
GET /api/v1/user/gpg_keys
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
[
|
|
{
|
|
"id": 1,
|
|
"primary_key_id": "",
|
|
"key_id": "ABC123DEF456",
|
|
"public_key": "-----BEGIN PGP PUBLIC KEY BLOCK-----\n...",
|
|
"emails": [
|
|
{
|
|
"email": "user@example.com",
|
|
"verified": true
|
|
}
|
|
],
|
|
"subkeys": [],
|
|
"can_sign": true,
|
|
"can_encrypt_comms": true,
|
|
"can_encrypt_storage": true,
|
|
"can_certify": true,
|
|
"created_at": "2024-01-01T00:00:00Z",
|
|
"expires_at": "2025-01-01T00:00:00Z"
|
|
}
|
|
]
|
|
```
|
|
|
|
#### Add GPG Key
|
|
|
|
```http
|
|
POST /api/v1/user/gpg_keys
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"armored_public_key": "-----BEGIN PGP PUBLIC KEY BLOCK-----\n..."
|
|
}
|
|
```
|
|
|
|
#### List Starred Repositories
|
|
|
|
```http
|
|
GET /api/v1/user/starred
|
|
```
|
|
|
|
#### Star Repository
|
|
|
|
```http
|
|
PUT /api/v1/user/starred/{owner}/{repo}
|
|
```
|
|
|
|
#### Unstar Repository
|
|
|
|
```http
|
|
DELETE /api/v1/user/starred/{owner}/{repo}
|
|
```
|
|
|
|
#### List Watched Repositories
|
|
|
|
```http
|
|
GET /api/v1/user/subscriptions
|
|
```
|
|
|
|
#### Watch Repository
|
|
|
|
```http
|
|
PUT /api/v1/repos/{owner}/{repo}/subscription
|
|
```
|
|
|
|
#### Unwatch Repository
|
|
|
|
```http
|
|
DELETE /api/v1/repos/{owner}/{repo}/subscription
|
|
```
|
|
|
|
### Package Registry APIs
|
|
|
|
GitCaddy supports multiple package formats with dedicated API endpoints.
|
|
|
|
#### List Packages
|
|
|
|
```http
|
|
GET /api/v1/packages/{owner}
|
|
```
|
|
|
|
**Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `type` | string | No | Package type: `npm`, `maven`, `docker`, `pypi`, `alpine`, etc. |
|
|
| `q` | string | No | Search query |
|
|
| `page` | integer | No | Page number |
|
|
| `limit` | integer | No | Page size |
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
[
|
|
{
|
|
"id": 1,
|
|
"name": "my-package",
|
|
"version": "1.0.0",
|
|
"type": "npm",
|
|
"owner": {
|
|
"id": 1,
|
|
"login": "username"
|
|
},
|
|
"repository": {
|
|
"id": 1,
|
|
"name": "my-repo",
|
|
"full_name": "username/my-repo"
|
|
},
|
|
"creator": {
|
|
"id": 1,
|
|
"login": "username"
|
|
},
|
|
"html_url": "https://gitcaddy.example.com/username/-/packages/npm/my-package/1.0.0",
|
|
"created_at": "2024-01-15T10:00:00Z"
|
|
}
|
|
]
|
|
```
|
|
|
|
#### Get Package
|
|
|
|
```http
|
|
GET /api/v1/packages/{owner}/{type}/{name}/{version}
|
|
```
|
|
|
|
#### Delete Package
|
|
|
|
```http
|
|
DELETE /api/v1/packages/{owner}/{type}/{name}/{version}
|
|
```
|
|
|
|
#### NPM Package API
|
|
|
|
**Configure npm registry:**
|
|
|
|
```bash
|
|
npm config set registry https://gitcaddy.example.com/api/packages/{owner}/npm/
|
|
npm config set //gitcaddy.example.com/api/packages/{owner}/npm/:_authToken {token}
|
|
```
|
|
|
|
**Publish package:**
|
|
|
|
```bash
|
|
npm publish
|
|
```
|
|
|
|
**Install package:**
|
|
|
|
```bash
|
|
npm install @{owner}/{package}
|
|
```
|
|
|
|
#### Docker Registry API
|
|
|
|
**Login:**
|
|
|
|
```bash
|
|
docker login gitcaddy.example.com
|
|
```
|
|
|
|
**Tag and push:**
|
|
|
|
```bash
|
|
docker tag myimage gitcaddy.example.com/{owner}/{image}:{tag}
|
|
docker push gitcaddy.example.com/{owner}/{image}:{tag}
|
|
```
|
|
|
|
**Pull:**
|
|
|
|
```bash
|
|
docker pull gitcaddy.example.com/{owner}/{image}:{tag}
|
|
```
|
|
|
|
#### Maven Package API
|
|
|
|
**Configure in `pom.xml`:**
|
|
|
|
```xml
|
|
<repositories>
|
|
<repository>
|
|
<id>gitcaddy</id>
|
|
<url>https://gitcaddy.example.com/api/packages/{owner}/maven</url>
|
|
</repository>
|
|
</repositories>
|
|
|
|
<distributionManagement>
|
|
<repository>
|
|
<id>gitcaddy</id>
|
|
<url>https://gitcaddy.example.com/api/packages/{owner}/maven</url>
|
|
</repository>
|
|
</distributionManagement>
|
|
```
|
|
|
|
**Configure authentication in `~/.m2/settings.xml`:**
|
|
|
|
```xml
|
|
<servers>
|
|
<server>
|
|
<id>gitcaddy</id>
|
|
<username>{username}</username>
|
|
<password>{token}</password>
|
|
</server>
|
|
</servers>
|
|
```
|
|
|
|
#### PyPI Package API
|
|
|
|
**Configure pip:**
|
|
|
|
```bash
|
|
pip config set global.index-url https://gitcaddy.example.com/api/packages/{owner}/pypi/simple
|
|
```
|
|
|
|
**Upload with twine:**
|
|
|
|
```bash
|
|
twine upload --repository-url https://gitcaddy.example.com/api/packages/{owner}/pypi dist/*
|
|
```
|
|
|
|
### Actions APIs
|
|
|
|
#### List Workflow Runs
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/actions/runs
|
|
```
|
|
|
|
**Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `status` | string | No | Filter by status: `success`, `failure`, `waiting`, `running` |
|
|
| `event` | string | No | Filter by event: `push`, `pull_request`, `schedule` |
|
|
| `actor` | string | No | Filter by actor username |
|
|
| `branch` | string | No | Filter by branch |
|
|
| `page` | integer | No | Page number |
|
|
| `limit` | integer | No | Page size |
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"total_count": 10,
|
|
"workflow_runs": [
|
|
{
|
|
"id": 1,
|
|
"name": "CI",
|
|
"head_branch": "main",
|
|
"head_sha": "abc123def456",
|
|
"run_number": 42,
|
|
"event": "push",
|
|
"status": "completed",
|
|
"conclusion": "success",
|
|
"workflow_id": 1,
|
|
"url": "https://gitcaddy.example.com/api/v1/repos/username/my-repo/actions/runs/1",
|
|
"html_url": "https://gitcaddy.example.com/username/my-repo/actions/runs/1",
|
|
"created_at": "2024-01-20T10:00:00Z",
|
|
"updated_at": "2024-01-20T10:05:00Z",
|
|
"run_started_at": "2024-01-20T10:00:30Z"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
#### Get Workflow Run
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/actions/runs/{run_id}
|
|
```
|
|
|
|
#### Cancel Workflow Run
|
|
|
|
```http
|
|
POST /api/v1/repos/{owner}/{repo}/actions/runs/{run_id}/cancel
|
|
```
|
|
|
|
#### Rerun Workflow
|
|
|
|
```http
|
|
POST /api/v1/repos/{owner}/{repo}/actions/runs/{run_id}/rerun
|
|
```
|
|
|
|
#### List Workflow Jobs
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/actions/runs/{run_id}/jobs
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"total_count": 3,
|
|
"jobs": [
|
|
{
|
|
"id": 1,
|
|
"run_id": 1,
|
|
"name": "build",
|
|
"status": "completed",
|
|
"conclusion": "success",
|
|
"started_at": "2024-01-20T10:00:30Z",
|
|
"completed_at": "2024-01-20T10:03:00Z",
|
|
"url": "https://gitcaddy.example.com/api/v1/repos/username/my-repo/actions/jobs/1",
|
|
"html_url": "https://gitcaddy.example.com/username/my-repo/actions/runs/1/jobs/1"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
#### Get Job Logs
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/actions/jobs/{job_id}/logs
|
|
```
|
|
|
|
**Response:** Plain text log output
|
|
|
|
#### List Repository Secrets
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/actions/secrets
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"total_count": 2,
|
|
"secrets": [
|
|
{
|
|
"name": "API_KEY",
|
|
"created_at": "2024-01-15T10:00:00Z",
|
|
"updated_at": "2024-01-15T10:00:00Z"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
#### Create/Update Secret
|
|
|
|
```http
|
|
PUT /api/v1/repos/{owner}/{repo}/actions/secrets/{secret_name}
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"value": "secret_value"
|
|
}
|
|
```
|
|
|
|
#### Delete Secret
|
|
|
|
```http
|
|
DELETE /api/v1/repos/{owner}/{repo}/actions/secrets/{secret_name}
|
|
```
|
|
|
|
#### List Runners
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/actions/runners
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"total_count": 1,
|
|
"runners": [
|
|
{
|
|
"id": 1,
|
|
"name": "runner-1",
|
|
"os": "linux",
|
|
"status": "online",
|
|
"busy": false,
|
|
"labels": [
|
|
{
|
|
"id": 1,
|
|
"name": "ubuntu-latest",
|
|
"type": "read-only"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
#### Register Runner
|
|
|
|
```http
|
|
POST /api/v1/repos/{owner}/{repo}/actions/runners/registration-token
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"token": "RUNNER_REGISTRATION_TOKEN",
|
|
"expires_at": "2024-01-20T11:00:00Z"
|
|
}
|
|
```
|
|
|
|
### Vault APIs
|
|
|
|
Enterprise-grade secrets management with encryption and audit logging.
|
|
|
|
#### List Vault Secrets
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/vault/secrets
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"total_count": 5,
|
|
"secrets": [
|
|
{
|
|
"id": "secret-id-123",
|
|
"name": "database_password",
|
|
"description": "Production database password",
|
|
"version": 3,
|
|
"created_at": "2024-01-01T00:00:00Z",
|
|
"updated_at": "2024-01-15T10:00:00Z",
|
|
"created_by": {
|
|
"id": 1,
|
|
"login": "admin"
|
|
},
|
|
"last_accessed_at": "2024-01-20T09:30:00Z",
|
|
"access_count": 42
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
#### Create Vault Secret
|
|
|
|
```http
|
|
POST /api/v1/repos/{owner}/{repo}/vault/secrets
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"name": "api_key",
|
|
"description": "External API key",
|
|
"value": "secret_value_encrypted",
|
|
"metadata": {
|
|
"environment": "production",
|
|
"service": "payment-gateway"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Response:** `201 Created`
|
|
|
|
```json
|
|
{
|
|
"id": "secret-id-456",
|
|
"name": "api_key",
|
|
"version": 1,
|
|
"created_at": "2024-01-20T10:00:00Z"
|
|
}
|
|
```
|
|
|
|
#### Get Vault Secret
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/vault/secrets/{secret_id}
|
|
```
|
|
|
|
**Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `version` | integer | No | Specific version (default: latest) |
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"id": "secret-id-456",
|
|
"name": "api_key",
|
|
"description": "External API key",
|
|
"value": "decrypted_secret_value",
|
|
"version": 1,
|
|
"created_at": "2024-01-20T10:00:00Z",
|
|
"metadata": {
|
|
"environment": "production",
|
|
"service": "payment-gateway"
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Update Vault Secret
|
|
|
|
```http
|
|
PUT /api/v1/repos/{owner}/{repo}/vault/secrets/{secret_id}
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"value": "new_secret_value",
|
|
"description": "Updated description"
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"id": "secret-id-456",
|
|
"version": 2,
|
|
"updated_at": "2024-01-20T11:00:00Z"
|
|
}
|
|
```
|
|
|
|
#### Delete Vault Secret
|
|
|
|
```http
|
|
DELETE /api/v1/repos/{owner}/{repo}/vault/secrets/{secret_id}
|
|
```
|
|
|
|
**Response:** `204 No Content`
|
|
|
|
#### List Secret Versions
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/vault/secrets/{secret_id}/versions
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"total_count": 3,
|
|
"versions": [
|
|
{
|
|
"version": 3,
|
|
"created_at": "2024-01-20T10:00:00Z",
|
|
"created_by": {
|
|
"id": 1,
|
|
"login": "admin"
|
|
}
|
|
},
|
|
{
|
|
"version": 2,
|
|
"created_at": "2024-01-15T10:00:00Z",
|
|
"created_by": {
|
|
"id": 1,
|
|
"login": "admin"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
#### Get Vault Audit Log
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/vault/audit
|
|
```
|
|
|
|
**Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `secret_id` | string | No | Filter by secret ID |
|
|
| `action` | string | No | Filter by action: `create`, `read`, `update`, `delete` |
|
|
| `user` | string | No | Filter by username |
|
|
| `since` | string | No | Start date (ISO 8601) |
|
|
| `before` | string | No | End date (ISO 8601) |
|
|
| `page` | integer | No | Page number |
|
|
| `limit` | integer | No | Page size |
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"total_count": 100,
|
|
"entries": [
|
|
{
|
|
"id": "audit-log-123",
|
|
"secret_id": "secret-id-456",
|
|
"secret_name": "api_key",
|
|
"action": "read",
|
|
"user": {
|
|
"id": 2,
|
|
"login": "developer"
|
|
},
|
|
"ip_address": "192.168.1.100",
|
|
"user_agent": "GitCaddy-Actions/1.0",
|
|
"timestamp": "2024-01-20T10:30:00Z",
|
|
"success": true
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
#### Generate CI/CD Token
|
|
|
|
```http
|
|
POST /api/v1/repos/{owner}/{repo}/vault/tokens
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"name": "ci-token",
|
|
"secret_ids": ["secret-id-456", "secret-id-789"],
|
|
"expires_at": "2024-12-31T23:59:59Z",
|
|
"read_only": true
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
{
|
|
"token": "vault_token_abc123def456",
|
|
"expires_at": "2024-12-31T23:59:59Z"
|
|
}
|
|
```
|
|
|
|
## Git Protocol
|
|
|
|
GitCaddy supports standard Git protocols for repository operations.
|
|
|
|
### HTTP(S) Protocol
|
|
|
|
**Clone:**
|
|
|
|
```bash
|
|
git clone https://gitcaddy.example.com/username/repo.git
|
|
```
|
|
|
|
**Authentication:**
|
|
|
|
Use personal access token as password:
|
|
|
|
```bash
|
|
git clone https://username:token@gitcaddy.example.com/username/repo.git
|
|
```
|
|
|
|
Or configure credential helper:
|
|
|
|
```bash
|
|
git config --global credential.helper store
|
|
git clone https://gitcaddy.example.com/username/repo.git
|
|
# Enter username and token when prompted
|
|
```
|
|
|
|
### SSH Protocol
|
|
|
|
**Clone:**
|
|
|
|
```bash
|
|
git clone git@gitcaddy.example.com:username/repo.git
|
|
```
|
|
|
|
**Setup:**
|
|
|
|
1. Generate SSH key:
|
|
|
|
```bash
|
|
ssh-keygen -t ed25519 -C "your_email@example.com"
|
|
```
|
|
|
|
2. Add public key to GitCaddy:
|
|
- User Settings → SSH/GPG Keys → Add Key
|
|
- Paste contents of `~/.ssh/id_ed25519.pub`
|
|
|
|
3. Configure SSH:
|
|
|
|
```bash
|
|
# ~/.ssh/config
|
|
Host gitcaddy.example.com
|
|
User git
|
|
IdentityFile ~/.ssh/id_ed25519
|
|
```
|
|
|
|
### Git LFS
|
|
|
|
**Enable LFS for repository:**
|
|
|
|
```bash
|
|
git lfs install
|
|
git lfs track "*.psd"
|
|
git add .gitattributes
|
|
git commit -m "Enable LFS for PSD files"
|
|
```
|
|
|
|
**Configuration:**
|
|
|
|
GitCaddy automatically handles LFS objects when pushing/pulling.
|
|
|
|
**LFS API Endpoints:**
|
|
|
|
```http
|
|
POST /api/v1/repos/{owner}/{repo}/lfs/objects/batch
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"operation": "upload",
|
|
"transfers": ["basic"],
|
|
"objects": [
|
|
{
|
|
"oid": "abc123def456...",
|
|
"size": 1048576
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
## WebSocket APIs
|
|
|
|
GitCaddy uses WebSocket connections for real-time features.
|
|
|
|
### EventSource (Server-Sent Events)
|
|
|
|
**Time Tracking Updates:**
|
|
|
|
```javascript
|
|
const eventSource = new EventSource('/user/events');
|
|
|
|
eventSource.addEventListener('timetrack', (event) => {
|
|
const data = JSON.parse(event.data);
|
|
console.log('Time tracking update:', data);
|
|
// Update stopwatch UI
|
|
});
|
|
|
|
eventSource.addEventListener('error', (error) => {
|
|
console.error('EventSource error:', error);
|
|
});
|
|
```
|
|
|
|
**Notification Updates:**
|
|
|
|
```javascript
|
|
const notificationSource = new EventSource('/api/v1/notifications/events');
|
|
|
|
notificationSource.addEventListener('notification', (event) => {
|
|
const notification = JSON.parse(event.data);
|
|
console.log('New notification:', notification);
|
|
// Display notification toast
|
|
});
|
|
```
|
|
|
|
### SharedWorker
|
|
|
|
**Shared stopwatch across tabs:**
|
|
|
|
```javascript
|
|
// Initialize shared worker for time tracking
|
|
const worker = new SharedWorker('/assets/js/stopwatch-worker.js');
|
|
|
|
worker.port.start();
|
|
|
|
worker.port.postMessage({
|
|
action: 'start',
|
|
issueId: 123
|
|
});
|
|
|
|
worker.port.onmessage = (event) => {
|
|
const { action, elapsed } = event.data;
|
|
if (action === 'tick') {
|
|
updateStopwatchDisplay(elapsed);
|
|
}
|
|
};
|
|
```
|
|
|
|
## Frontend JavaScript APIs
|
|
|
|
GitCaddy provides modular JavaScript APIs for frontend functionality.
|
|
|
|
### Core Utilities
|
|
|
|
#### DOM Manipulation
|
|
|
|
```javascript
|
|
import {$, $$} from './modules/utils/dom.js';
|
|
|
|
// Select single element
|
|
const button = $('#submit-button');
|
|
|
|
// Select multiple elements
|
|
const listItems = $$('.list-item');
|
|
|
|
// Event delegation
|
|
$(document).on('click', '.dynamic-button', (event) => {
|
|
console.log('Button clicked:', event.target);
|
|
});
|
|
```
|
|
|
|
#### HTML Escaping
|
|
|
|
```javascript
|
|
import {htmlEscape} from './modules/utils/html.js';
|
|
|
|
const userInput = '<script>alert("xss")</script>';
|
|
const safe = htmlEscape(userInput);
|
|
// Result: <script>alert("xss")</script>
|
|
```
|
|
|
|
#### Date/Time Utilities
|
|
|
|
```javascript
|
|
import {parseISODateTime, formatDatetime} from './modules/utils/time.js';
|
|
|
|
const date = parseISODateTime('2024-01-20T10:00:00Z');
|
|
const formatted = formatDatetime(date, 'short'); // "Jan 20, 2024"
|
|
```
|
|
|
|
#### Color Utilities
|
|
|
|
```javascript
|
|
import {useLightTextOnBackground} from './modules/utils/color.js';
|
|
|
|
const bgColor = '#ff0000';
|
|
const useLight = useLightTextOnBackground(bgColor);
|
|
// Returns true if light text should be used on this background
|
|
```
|
|
|
|
#### Glob Pattern Matching
|
|
|
|
```javascript
|
|
import {matchGlobPattern} from './modules/utils/glob.js';
|
|
|
|
const pattern = '*.js';
|
|
const filename = 'script.js';
|
|
const matches = matchGlobPattern(pattern, filename); // true
|
|
```
|
|
|
|
### DOM Manipulation
|
|
|
|
#### Creating Elements
|
|
|
|
```javascript
|
|
import {createElementFromHTML} from './modules/utils/dom.js';
|
|
|
|
const html = '<div class="alert">Message</div>';
|
|
const element = createElementFromHTML(html);
|
|
document.body.appendChild(element);
|
|
```
|
|
|
|
#### Toggle Classes
|
|
|
|
```javascript
|
|
import {toggleClass} from './modules/utils/dom.js';
|
|
|
|
const element = $('#my-element');
|
|
toggleClass(element, 'active', true); // Add class
|
|
toggleClass(element, 'hidden', false); // Remove class
|
|
```
|
|
|
|
### Web Components
|
|
|
|
#### Absolute Date Component
|
|
|
|
```html
|
|
<absolute-date date="2024-01-20T10:00:00Z" format="short"></absolute-date>
|
|
```
|
|
|
|
**JavaScript API:**
|
|
|
|
```javascript
|
|
const dateElement = document.querySelector('absolute-date');
|
|
dateElement.setAttribute('date', '2024-01-21T12:00:00Z');
|
|
```
|
|
|
|
#### Origin URL Component
|
|
|
|
```html
|
|
<origin-url data-url="https://example.com/page"></origin-url>
|
|
```
|
|
|
|
Displays sanitized origin URL with proper formatting.
|
|
|
|
#### Overflow Menu Component
|
|
|
|
```html
|
|
<overflow-menu>
|
|
<button class="item">Action 1</button>
|
|
<button class="item">Action 2</button>
|
|
<button class="item">Action 3</button>
|
|
</overflow-menu>
|
|
```
|
|
|
|
Automatically creates overflow menu for items that don't fit.
|
|
|
|
### Vue Components
|
|
|
|
#### File Tree Component
|
|
|
|
```javascript
|
|
import {createApp} from 'vue';
|
|
import FileTreeView from './modules/features/repo-file-tree.js';
|
|
|
|
const app = createApp({
|
|
components: {
|
|
FileTreeView
|
|
},
|
|
data() {
|
|
return {
|
|
files: [
|
|
{name: 'src', type: 'dir', children: [...]},
|
|
{name: 'README.md', type: 'file'}
|
|
]
|
|
};
|
|
}
|
|
});
|
|
|
|
app.mount('#file-tree');
|
|
```
|
|
|
|
**Template:**
|
|
|
|
```html
|
|
<file-tree-view :files="files" @file-click="handleFileClick"></file-tree-view>
|
|
```
|
|
|
|
#### Context Popup Component
|
|
|
|
```javascript
|
|
import {createContextPopup} from './modules/features/context-popup.js';
|
|
|
|
const popup = createContextPopup({
|
|
items: [
|
|
{label: 'Edit', action: () => console.log('Edit')},
|
|
{label: 'Delete', action: () => console.log('Delete')}
|
|
],
|
|
position: {x: event.clientX, y: event.clientY}
|
|
});
|
|
|
|
popup.show();
|
|
```
|
|
|
|
### Tooltip System
|
|
|
|
```javascript
|
|
import {createTippy} from './modules/features/tippy.js';
|
|
|
|
// Simple tooltip
|
|
createTippy('#my-element', {
|
|
content: 'Tooltip text'
|
|
});
|
|
|
|
// HTML content
|
|
createTippy('.help-icon', {
|
|
content: '<strong>Help:</strong> This is help text',
|
|
allowHTML: true
|
|
});
|
|
|
|
// Interactive tooltip
|
|
createTippy('.interactive', {
|
|
content: document.querySelector('#tooltip-content'),
|
|
interactive: true,
|
|
trigger: 'click'
|
|
});
|
|
```
|
|
|
|
### Toast Notifications
|
|
|
|
```javascript
|
|
import {showToast} from './modules/features/toast.js';
|
|
|
|
// Success toast
|
|
showToast('Operation completed successfully', 'success');
|
|
|
|
// Error toast
|
|
showToast('An error occurred', 'error');
|
|
|
|
// Warning toast
|
|
showToast('Please review your changes', 'warning');
|
|
|
|
// Info toast
|
|
showToast('New version available', 'info');
|
|
|
|
// Custom duration
|
|
showToast('Auto-closing in 5 seconds', 'info', 5000);
|
|
```
|
|
|
|
### Markdown Rendering
|
|
|
|
```javascript
|
|
import {renderMarkdown} from './modules/features/markup.js';
|
|
|
|
const markdown = '# Heading\n\nParagraph with **bold** text.';
|
|
const html = await renderMarkdown(markdown);
|
|
document.querySelector('#content').innerHTML = html;
|
|
```
|
|
|
|
**Features:**
|
|
- GitHub-flavored markdown
|
|
- Syntax highlighting
|
|
- Math rendering (KaTeX)
|
|
- Mermaid diagrams
|
|
- Task lists
|
|
- Emoji support
|
|
|
|
### Mermaid Diagrams
|
|
|
|
```javascript
|
|
import {renderMermaid} from './modules/features/mermaid.js';
|
|
|
|
const diagramCode = `
|
|
graph TD
|
|
A[Start] --> B[Process]
|
|
B --> C[End]
|
|
`;
|
|
|
|
await renderMermaid('#diagram-container', diagramCode);
|
|
```
|
|
|
|
### SortableJS Integration
|
|
|
|
```javascript
|
|
import Sortable from 'sortablejs';
|
|
|
|
const projectBoard = document.querySelector('#project-board');
|
|
|
|
Sortable.create(projectBoard, {
|
|
animation: 150,
|
|
handle: '.card-handle',
|
|
onEnd: (event) => {
|
|
const cardId = event.item.dataset.cardId;
|
|
const newPosition = event.newIndex;
|
|
|
|
// Update card position via API
|
|
fetch(`/api/v1/projects/cards/${cardId}/move`, {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({position: newPosition})
|
|
});
|
|
}
|
|
});
|
|
```
|
|
|
|
## Error Codes
|
|
|
|
GitCaddy uses standard HTTP status codes with detailed error responses.
|
|
|
|
### HTTP Status Codes
|
|
|
|
| Code | Status | Description |
|
|
|------|--------|-------------|
|
|
| 200 | OK | Request succeeded |
|
|
| 201 | Created | Resource created successfully |
|
|
| 204 | No Content | Request succeeded with no response body |
|
|
| 400 | Bad Request | Invalid request parameters |
|
|
| 401 | Unauthorized | Authentication required |
|
|
| 403 | Forbidden | Insufficient permissions |
|
|
| 404 | Not Found | Resource not found |
|
|
| 409 | Conflict | Resource conflict (e.g., duplicate name) |
|
|
| 422 | Unprocessable Entity | Validation error |
|
|
| 429 | Too Many Requests | Rate limit exceeded |
|
|
| 500 | Internal Server Error | Server error |
|
|
| 503 | Service Unavailable | Service temporarily unavailable |
|
|
|
|
### Error Response Format
|
|
|
|
```json
|
|
{
|
|
"message": "Human-readable error message",
|
|
"errors": [
|
|
{
|
|
"field": "email",
|
|
"message": "Email is required"
|
|
}
|
|
],
|
|
"url": "https://docs.gitcaddy.example.com/api/errors/validation"
|
|
}
|
|
```
|
|
|
|
### Common Error Codes
|
|
|
|
**Authentication Errors:**
|
|
|
|
```json
|
|
{
|
|
"message": "Unauthorized: Invalid or expired token",
|
|
"code": "INVALID_TOKEN"
|
|
}
|
|
```
|
|
|
|
**Validation Errors:**
|
|
|
|
```json
|
|
{
|
|
"message": "Validation failed",
|
|
"errors": [
|
|
{
|
|
"field": "name",
|
|
"message": "Repository name already exists"
|
|
},
|
|
{
|
|
"field": "description",
|
|
"message": "Description must be less than 500 characters"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Permission Errors:**
|
|
|
|
```json
|
|
{
|
|
"message": "Forbidden: You do not have permission to perform this action",
|
|
"required_permission": "admin"
|
|
}
|
|
```
|
|
|
|
**Rate Limit Errors:**
|
|
|
|
```json
|
|
{
|
|
"message": "API rate limit exceeded",
|
|
"limit": 5000,
|
|
"remaining": 0,
|
|
"reset": "2024-01-20T11:00:00Z"
|
|
}
|
|
```
|
|
|
|
**Resource Not Found:**
|
|
|
|
```json
|
|
{
|
|
"message": "Repository not found",
|
|
"resource": "repository",
|
|
"id": "username/nonexistent-repo"
|
|
}
|
|
```
|
|
|
|
## Rate Limiting
|
|
|
|
GitCaddy implements rate limiting to prevent abuse.
|
|
|
|
### Rate Limit Headers
|
|
|
|
All API responses include rate limit information:
|
|
|
|
```http
|
|
X-RateLimit-Limit: 5000
|
|
X-RateLimit-Remaining: 4999
|
|
X-RateLimit-Reset: 1705750800
|
|
```
|
|
|
|
| Header | Description |
|
|
|--------|-------------|
|
|
| `X-RateLimit-Limit` | Maximum requests per hour |
|
|
| `X-RateLimit-Remaining` | Remaining requests in current window |
|
|
| `X-RateLimit-Reset` | Unix timestamp when limit resets |
|
|
|
|
### Rate Limits by Authentication
|
|
|
|
| Authentication | Requests/Hour |
|
|
|----------------|---------------|
|
|
| Unauthenticated | 60 |
|
|
| Authenticated (PAT) | 5,000 |
|
|
| OAuth Application | 5,000 |
|
|
| Enterprise License | 15,000 |
|
|
|
|
### Handling Rate Limits
|
|
|
|
```javascript
|
|
async function makeAPIRequest(url) {
|
|
const response = await fetch(url, {
|
|
headers: {
|
|
'Authorization': 'token YOUR_TOKEN'
|
|
}
|
|
});
|
|
|
|
const remaining = response.headers.get('X-RateLimit-Remaining');
|
|
const reset = response.headers.get('X-RateLimit-Reset');
|
|
|
|
if (response.status === 429) {
|
|
const resetDate = new Date(reset * 1000);
|
|
const waitTime = resetDate - new Date();
|
|
|
|
console.log(`Rate limited. Retry after ${waitTime}ms`);
|
|
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
return makeAPIRequest(url); // Retry
|
|
}
|
|
|
|
if (remaining < 100) {
|
|
console.warn(`Low rate limit: ${remaining} requests remaining`);
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
```
|
|
|
|
## Webhooks
|
|
|
|
GitCaddy can send HTTP POST requests to external URLs when events occur.
|
|
|
|
### Creating Webhooks
|
|
|
|
**Via API:**
|
|
|
|
```http
|
|
POST /api/v1/repos/{owner}/{repo}/hooks
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"type": "gitea",
|
|
"config": {
|
|
"url": "https://example.com/webhook",
|
|
"content_type": "json",
|
|
"secret": "webhook_secret"
|
|
},
|
|
"events": [
|
|
"push",
|
|
"pull_request",
|
|
"issues",
|
|
"issue_comment",
|
|
"release"
|
|
],
|
|
"active": true
|
|
}
|
|
```
|
|
|
|
**Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `type` | string | Yes | Webhook type: `gitea`, `gogs`, `slack`, `discord` |
|
|
| `config.url` | string | Yes | Webhook URL |
|
|
| `config.content_type` | string | No | Content type: `json`, `form` |
|
|
| `config.secret` | string | No | Secret for signature verification |
|
|
| `events` | array | Yes | Events to trigger webhook |
|
|
| `active` | boolean | No | Enable webhook (default: true) |
|
|
|
|
### Webhook Events
|
|
|
|
| Event | Description |
|
|
|-------|-------------|
|
|
| `push` | Git push to repository |
|
|
| `create` | Branch or tag created |
|
|
| `delete` | Branch or tag deleted |
|
|
| `fork` | Repository forked |
|
|
| `issues` | Issue opened, closed, edited |
|
|
| `issue_comment` | Comment on issue |
|
|
| `pull_request` | PR opened, closed, merged, edited |
|
|
| `pull_request_review` | PR review submitted |
|
|
| `pull_request_review_comment` | Comment on PR review |
|
|
| `release` | Release published |
|
|
| `repository` | Repository created, deleted |
|
|
| `wiki` | Wiki page created, edited |
|
|
|
|
### Webhook Payload
|
|
|
|
**Push Event:**
|
|
|
|
```json
|
|
{
|
|
"ref": "refs/heads/main",
|
|
"before": "abc123def456",
|
|
"after": "def456ghi789",
|
|
"compare_url": "https://gitcaddy.example.com/username/repo/compare/abc123...def456",
|
|
"commits": [
|
|
{
|
|
"id": "def456ghi789",
|
|
"message": "Fix bug in authentication",
|
|
"url": "https://gitcaddy.example.com/username/repo/commit/def456ghi789",
|
|
"author": {
|
|
"name": "Developer",
|
|
"email": "dev@example.com",
|
|
"username": "developer"
|
|
},
|
|
"committer": {
|
|
"name": "Developer",
|
|
"email": "dev@example.com",
|
|
"username": "developer"
|
|
},
|
|
"timestamp": "2024-01-20T10:00:00Z",
|
|
"added": ["new-file.js"],
|
|
"removed": [],
|
|
"modified": ["existing-file.js"]
|
|
}
|
|
],
|
|
"repository": {
|
|
"id": 1,
|
|
"name": "repo",
|
|
"full_name": "username/repo",
|
|
"html_url": "https://gitcaddy.example.com/username/repo",
|
|
"private": false,
|
|
"owner": {
|
|
"id": 1,
|
|
"login": "username",
|
|
"full_name": "User Name"
|
|
}
|
|
},
|
|
"pusher": {
|
|
"id": 1,
|
|
"login": "username",
|
|
"full_name": "User Name"
|
|
},
|
|
"sender": {
|
|
"id": 1,
|
|
"login": "username",
|
|
"full_name": "User Name"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Pull Request Event:**
|
|
|
|
```json
|
|
{
|
|
"action": "opened",
|
|
"number": 1,
|
|
"pull_request": {
|
|
"id": 1,
|
|
"number": 1,
|
|
"user": {
|
|
"id": 2,
|
|
"login": "contributor"
|
|
},
|
|
"title": "Add new feature",
|
|
"body": "Description of changes",
|
|
"state": "open",
|
|
"html_url": "https://gitcaddy.example.com/username/repo/pulls/1",
|
|
"diff_url": "https://gitcaddy.example.com/username/repo/pulls/1.diff",
|
|
"patch_url": "https://gitcaddy.example.com/username/repo/pulls/1.patch",
|
|
"merged": false,
|
|
"mergeable": true,
|
|
"base": {
|
|
"label": "main",
|
|
"ref": "main",
|
|
"sha": "abc123"
|
|
},
|
|
"head": {
|
|
"label": "contributor:feature",
|
|
"ref": "feature",
|
|
"sha": "def456"
|
|
},
|
|
"created_at": "2024-01-20T10:00:00Z",
|
|
"updated_at": "2024-01-20T10:00:00Z"
|
|
},
|
|
"repository": {
|
|
"id": 1,
|
|
"name": "repo",
|
|
"full_name": "username/repo"
|
|
},
|
|
"sender": {
|
|
"id": 2,
|
|
"login": "contributor"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Webhook Security
|
|
|
|
**Signature Verification:**
|
|
|
|
GitCaddy signs webhook payloads with HMAC-SHA256 using the configured secret.
|
|
|
|
```http
|
|
X-Gitea-Signature: sha256=abc123def456...
|
|
```
|
|
|
|
**Verification Example (Node.js):**
|
|
|
|
```javascript
|
|
const crypto = require('crypto');
|
|
|
|
function verifyWebhook(payload, signature, secret) {
|
|
const hmac = crypto.createHmac('sha256', secret);
|
|
hmac.update(payload);
|
|
const calculatedSignature = 'sha256=' + hmac.digest('hex');
|
|
|
|
return crypto.timingSafeEqual(
|
|
Buffer.from(signature),
|
|
Buffer.from(calculatedSignature)
|
|
);
|
|
}
|
|
|
|
// Express middleware
|
|
app.post('/webhook', (req, res) => {
|
|
const signature = req.headers['x-gitea-signature'];
|
|
const payload = JSON.stringify(req.body);
|
|
|
|
if (!verifyWebhook(payload, signature, process.env.WEBHOOK_SECRET)) {
|
|
return res.status(401).send('Invalid signature');
|
|
}
|
|
|
|
// Process webhook
|
|
res.status(200).send('OK');
|
|
});
|
|
```
|
|
|
|
### Testing Webhooks
|
|
|
|
**Manual Test:**
|
|
|
|
```bash
|
|
curl -X POST https://gitcaddy.example.com/api/v1/repos/username/repo/hooks/1/test \
|
|
-H "Authorization: token YOUR_TOKEN"
|
|
```
|
|
|
|
**View Webhook Deliveries:**
|
|
|
|
```http
|
|
GET /api/v1/repos/{owner}/{repo}/hooks/{id}/deliveries
|
|
```
|
|
|
|
**Response:**
|
|
|
|
```json
|
|
[
|
|
{
|
|
"id": 1,
|
|
"uuid": "abc-123-def-456",
|
|
"url": "https://example.com/webhook",
|
|
"request": {
|
|
"headers": {
|
|
"Content-Type": "application/json",
|
|
"X-Gitea-Event": "push",
|
|
"X-Gitea-Signature": "sha256=..."
|
|
},
|
|
"body": "{...}"
|
|
},
|
|
"response": {
|
|
"status": 200,
|
|
"headers": {},
|
|
"body": "OK"
|
|
},
|
|
"delivered_at": "2024-01-20T10:00:00Z",
|
|
"duration": 0.123
|
|
}
|
|
]
|
|
```
|
|
|
|
## Code Examples
|
|
|
|
### Complete Repository Workflow
|
|
|
|
```javascript
|
|
// Create repository
|
|
async function createRepository(name, description) {
|
|
const response = await fetch('https://gitcaddy.example.com/api/v1/user/repos', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': 'token YOUR_TOKEN',
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
name: name,
|
|
description: description,
|
|
private: false,
|
|
auto_init: true,
|
|
license: 'MIT'
|
|
})
|
|
});
|
|
|
|
return response.json();
|
|
}
|
|
|
|
// Add file to repository
|
|
async function addFile(owner, repo, path, content, message) {
|
|
const encodedContent = btoa(content);
|
|
|
|
const response = await fetch(
|
|
`https://gitcaddy.example.com/api/v1/repos/${owner}/${repo}/contents/${path}`,
|
|
{
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': 'token YOUR_TOKEN',
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
content: encodedContent,
|
|
message: message,
|
|
branch: 'main'
|
|
})
|
|
}
|
|
);
|
|
|
|
return response.json();
|
|
}
|
|
|
|
// Create pull request
|
|
async function createPullRequest(owner, repo, title, head, base) {
|
|
const response = await fetch(
|
|
`https://gitcaddy.example.com/api/v1/repos/${owner}/${repo}/pulls`,
|
|
{
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': 'token YOUR_TOKEN',
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
title: title,
|
|
head: head,
|
|
base: base,
|
|
body: 'Please review these changes'
|
|
})
|
|
}
|
|
);
|
|
|
|
return response.json();
|
|
}
|
|
|
|
// Usage
|
|
(async () => {
|
|
const repo = await createRepository('my-project', 'My awesome project');
|
|
console.log('Repository created:', repo.html_url);
|
|
|
|
const file = await addFile(
|
|
'username',
|
|
'my-project',
|
|
'src/index.js',
|
|
'console.log("Hello World");',
|
|
'Add index.js'
|
|
);
|
|
console.log('File added:', file.commit.sha);
|
|
|
|
const pr = await createPullRequest(
|
|
'username',
|
|
'my-project',
|
|
'Add feature',
|
|
'feature-branch',
|
|
'main'
|
|
);
|
|
console.log('Pull request created:', pr.html_url);
|
|
})();
|
|
```
|
|
|
|
### Issue Management
|
|
|
|
```python
|
|
import requests
|
|
|
|
class GitCaddyClient:
|
|
def __init__(self, base_url, token):
|
|
self.base_url = base_url
|
|
self.headers = {
|
|
'Authorization': f'token {token}',
|
|
'Content-Type': 'application/json'
|
|
}
|
|
|
|
def create_issue(self, owner, repo, title, body, labels=None):
|
|
url = f'{self.base_url}/api/v1/repos/{owner}/{repo}/issues'
|
|
data = {
|
|
'title': title,
|
|
'body': body
|
|
}
|
|
if labels:
|
|
data['labels'] = labels
|
|
|
|
response = requests.post(url, json=data, headers=self.headers)
|
|
return response.json()
|
|
|
|
def add_comment(self, owner, repo, issue_number, comment):
|
|
url = f'{self.base_url}/api/v1/repos/{owner}/{repo}/issues/{issue_number}/comments'
|
|
data = {'body': comment}
|
|
|
|
response = requests.post(url, json=data, headers=self.headers)
|
|
return response.json()
|
|
|
|
def close_issue(self, owner, repo, issue_number):
|
|
url = f'{self.base_url}/api/v1/repos/{owner}/{repo}/issues/{issue_number}'
|
|
data = {'state': 'closed'}
|
|
|
|
response = requests.patch(url, json=data, headers=self.headers)
|
|
return response.json()
|
|
|
|
def track_time(self, owner, repo, issue_number, seconds):
|
|
url = f'{self.base_url}/api/v1/repos/{owner}/{repo}/issues/{issue_number}/times'
|
|
data = {'time': seconds}
|
|
|
|
response = requests.post(url, json=data, headers=self.headers)
|
|
return response.json()
|
|
|
|
# Usage
|
|
client = GitCaddyClient('https://gitcaddy.example.com', 'YOUR_TOKEN')
|
|
|
|
# Create issue
|
|
issue = client.create_issue(
|
|
'username',
|
|
'my-repo',
|
|
'Bug: Login not working',
|
|
'Users cannot log in with correct credentials',
|
|
labels=[1, 2] # bug, high-priority
|
|
)
|
|
print(f'Issue created: #{issue["number"]}')
|
|
|
|
# Add comment
|
|
comment = client.add_comment(
|
|
'username',
|
|
'my-repo',
|
|
issue['number'],
|
|
'Investigating the issue...'
|
|
)
|
|
|
|
# Track time (2 hours)
|
|
time_entry = client.track_time('username', 'my-repo', issue['number'], 7200)
|
|
print(f'Tracked {time_entry["time"]}s')
|
|
|
|
# Close issue
|
|
closed = client.close_issue('username', 'my-repo', issue['number'])
|
|
print(f'Issue closed: {closed["state"]}')
|
|
```
|
|
|
|
### CI/CD Integration
|
|
|
|
```yaml
|
|
# .gitcaddy/workflows/ci.yml
|
|
name: CI
|
|
|
|
on:
|
|
push:
|
|
branches: [main, develop]
|
|
pull_request:
|
|
branches: [main]
|
|
|
|
jobs:
|
|
test:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v3
|
|
with:
|
|
node-version: '18'
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- name: Run tests
|
|
run: npm test
|
|
|
|
- name: Get secrets from Vault
|
|
id: vault
|
|
uses: actions/vault-secrets@v1
|
|
with:
|
|
secrets: |
|
|
database_password
|
|
api_key
|
|
|
|
- name: Deploy
|
|
if: github.ref == 'refs/heads/main'
|
|
run: |
|
|
echo "Deploying with API key: ${{ steps.vault.outputs.api_key }}"
|
|
npm run deploy
|
|
env:
|
|
DATABASE_PASSWORD: ${{ steps.vault.outputs.database_password }}
|
|
API_KEY: ${{ steps.vault.outputs.api_key }}
|
|
```
|
|
|
|
### Webhook Handler
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/http"
|
|
)
|
|
|
|
type PushEvent struct {
|
|
Ref string `json:"ref"`
|
|
Before string `json:"before"`
|
|
After string `json:"after"`
|
|
Repository struct {
|
|
Name string `json:"name"`
|
|
FullName string `json:"full_name"`
|
|
} `json:"repository"`
|
|
Pusher struct {
|
|
Login string `json:"login"`
|
|
} `json:"pusher"`
|
|
Commits []struct {
|
|
ID string `json:"id"`
|
|
Message string `json:"message"`
|
|
Author struct {
|
|
Name string `json:"name"`
|
|
Email string `json:"email"`
|
|
} `json:"author"`
|
|
} `json:"commits"`
|
|
}
|
|
|
|
func verifySignature(payload []byte, signature, secret string) bool {
|
|
mac := hmac.New(sha256.New, []byte(secret))
|
|
mac.Write(payload)
|
|
expectedMAC := "sha256=" + hex.EncodeToString(mac.Sum(nil))
|
|
return hmac.Equal([]byte(signature), []byte(expectedMAC))
|
|
}
|
|
|
|
func handleWebhook(w http.ResponseWriter, r *http.Request) {
|
|
payload, err := ioutil.ReadAll(r.Body)
|
|
if err != nil {
|
|
http.Error(w, "Error reading request body", http.StatusBadRequest)
|
|
return
|
|
}
|
|
defer r.Body.Close()
|
|
|
|
signature := r.Header.Get("X-Gitea-Signature")
|
|
if !verifySignature(payload, signature, "webhook_secret") {
|
|
http.Error(w, "Invalid signature", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
event := r.Header.Get("X-Gitea-Event")
|
|
|
|
switch event {
|
|
case "push":
|
|
var pushEvent PushEvent
|
|
if err := json.Unmarshal(payload, &pushEvent); err != nil {
|
|
http.Error(w, "Error parsing JSON", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
log.Printf("Push to %s by %s", pushEvent.Repository.FullName, pushEvent.Pusher.Login)
|
|
log.Printf("Commits: %d", len(pushEvent.Commits))
|
|
|
|
for _, commit := range pushEvent.Commits {
|
|
log.Printf(" - %s: %s", commit.ID[:7], commit.Message)
|
|
}
|
|
|
|
case "pull_request":
|
|
log.Printf("Pull request event received")
|
|
|
|
default:
|
|
log.Printf("Unknown event: %s", event)
|
|
}
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
fmt.Fprintf(w, "Webhook processed")
|
|
}
|
|
|
|
func main() {
|
|
http.HandleFunc("/webhook", handleWebhook)
|
|
log.Println("Webhook server listening on :8080")
|
|
log.Fatal(http.ListenAndServe(":8080", nil))
|
|
}
|
|
```
|
|
|
|
### Package Publishing
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# publish-npm.sh - Publish npm package to GitCaddy registry
|
|
|
|
GITCADDY_URL="https://gitcaddy.example.com"
|
|
OWNER="username"
|
|
TOKEN="YOUR_TOKEN"
|
|
|
|
# Configure npm
|
|
npm config set registry "${GITCADDY_URL}/api/packages/${OWNER}/npm/"
|
|
npm config set "//${GITCADDY_URL#https://}/api/packages/${OWNER}/npm/:_authToken" "${TOKEN}"
|
|
|
|
# Update version
|
|
npm version patch
|
|
|
|
# Publish
|
|
npm publish
|
|
|
|
echo "Package published to GitCaddy registry"
|
|
```
|
|
|
|
```dockerfile
|
|
# Dockerfile - Build and push Docker image to GitCaddy registry
|
|
FROM node:18-alpine
|
|
|
|
WORKDIR /app
|
|
|
|
COPY package*.json ./
|
|
RUN npm ci --production
|
|
|
|
COPY . .
|
|
|
|
EXPOSE 3000
|
|
|
|
CMD ["node", "server.js"]
|
|
|
|
# Build and push:
|
|
# docker build -t gitcaddy.example.com/username/myapp:latest .
|
|
# docker login gitcaddy.example.com
|
|
# docker push gitcaddy.example.com/username/myapp:latest
|
|
```
|
|
|
|
---
|
|
|
|
For additional support and documentation, visit the [GitCaddy Documentation](https://docs.gitcaddy.example.com) or join the community forum. |