diff --git a/API.md b/API.md new file mode 100644 index 0000000000..e0802a1bab --- /dev/null +++ b/API.md @@ -0,0 +1,3226 @@ +# 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 + + + gitcaddy + https://gitcaddy.example.com/api/packages/{owner}/maven + + + + + + gitcaddy + https://gitcaddy.example.com/api/packages/{owner}/maven + + +``` + +**Configure authentication in `~/.m2/settings.xml`:** + +```xml + + + gitcaddy + {username} + {token} + + +``` + +#### 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 = ''; +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 = '
Message
'; +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 + +``` + +**JavaScript API:** + +```javascript +const dateElement = document.querySelector('absolute-date'); +dateElement.setAttribute('date', '2024-01-21T12:00:00Z'); +``` + +#### Origin URL Component + +```html + +``` + +Displays sanitized origin URL with proper formatting. + +#### Overflow Menu Component + +```html + + + + + +``` + +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 + +``` + +#### 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: 'Help: 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. \ No newline at end of file diff --git a/GUIDE.md b/GUIDE.md new file mode 100644 index 0000000000..23100b99ae --- /dev/null +++ b/GUIDE.md @@ -0,0 +1,1343 @@ +# GitCaddy Users Guide + +A comprehensive guide to using GitCaddy, a self-hosted Git repository management platform with AI capabilities, encrypted secrets management, and enterprise features. + +## Table of Contents + +- [Introduction](#introduction) +- [Getting Started](#getting-started) + - [Prerequisites](#prerequisites) + - [Installation](#installation) + - [First-Time Setup](#first-time-setup) +- [Basic Repository Operations](#basic-repository-operations) + - [Creating a Repository](#creating-a-repository) + - [Cloning and Pushing Code](#cloning-and-pushing-code) + - [Managing Branches and Tags](#managing-branches-and-tags) + - [File Operations](#file-operations) +- [Collaboration Workflows](#collaboration-workflows) + - [Working with Issues](#working-with-issues) + - [Pull Request Workflow](#pull-request-workflow) + - [Code Reviews](#code-reviews) + - [Project Boards](#project-boards) +- [Wiki Documentation](#wiki-documentation) +- [CI/CD with GitCaddy Actions](#cicd-with-gitcaddy-actions) + - [Creating Workflows](#creating-workflows) + - [Managing Runners](#managing-runners) + - [Secrets and Variables](#secrets-and-variables) +- [Vault: Encrypted Secrets Management](#vault-encrypted-secrets-management) + - [Creating Secrets](#creating-secrets) + - [Using Secrets in CI/CD](#using-secrets-in-cicd) + - [Audit Logs and Version History](#audit-logs-and-version-history) +- [Package Registry](#package-registry) + - [Publishing Packages](#publishing-packages) + - [Installing Packages](#installing-packages) +- [AI-Powered Features](#ai-powered-features) + - [AI Code Review](#ai-code-review) + - [AI Issue Triage](#ai-issue-triage) + - [Code Explanation](#code-explanation) +- [Organizations and Teams](#organizations-and-teams) + - [Creating Organizations](#creating-organizations) + - [Managing Teams](#managing-teams) + - [Team Permissions](#team-permissions) +- [Landing Pages and Custom Domains](#landing-pages-and-custom-domains) +- [Authentication and Security](#authentication-and-security) + - [Two-Factor Authentication](#two-factor-authentication) + - [SSH and GPG Keys](#ssh-and-gpg-keys) + - [OAuth2 and SSO](#oauth2-and-sso) +- [Customization and Settings](#customization-and-settings) + - [User Preferences](#user-preferences) + - [Repository Settings](#repository-settings) + - [Webhooks and Integrations](#webhooks-and-integrations) +- [Tips and Best Practices](#tips-and-best-practices) +- [Troubleshooting](#troubleshooting) + +--- + +## Introduction + +GitCaddy is a powerful, self-hosted Git repository management platform that provides a complete solution for version control, issue tracking, CI/CD workflows, package registry, and team collaboration. Built as an enhanced fork of Gitea, GitCaddy adds enterprise features including: + +- **AI-powered code review and issue triage** +- **Vault encrypted secrets management** with audit logging +- **Package registry** supporting npm, Maven, Docker, PyPI, Go, Cargo, NuGet, and more +- **Custom landing pages** with SSL/TLS support +- **Comprehensive internationalization** with 18+ supported languages +- **GitCaddy Actions** for CI/CD automation +- **Advanced collaboration tools** including project boards, wikis, and time tracking + +This guide will walk you through everything you need to know to effectively use GitCaddy, from initial setup to advanced workflows. + +--- + +## Getting Started + +### Prerequisites + +Before installing GitCaddy, ensure you have the following: + +- **Go runtime** (version 1.18 or higher) +- **Node.js and npm** (for building frontend assets) +- **Git** (version 2.0 or higher) +- **Database**: PostgreSQL, MySQL, SQLite, or MSSQL +- **Operating System**: Linux, macOS, or Windows + +### Installation + +Follow these steps to install GitCaddy on your server: + +#### 1. Clone the Repository + +```bash +git clone https://github.com/your-org/gitcaddy.git +cd gitcaddy +``` + +#### 2. Build the Backend + +```bash +# Install Go dependencies +go mod download + +# Build the GitCaddy binary +go build -o gitcaddy +``` + +#### 3. Build the Frontend + +```bash +# Install Node.js dependencies +npm install + +# Build frontend assets +npm run build +``` + +#### 4. Configure the Database + +Create a configuration file `custom/conf/app.ini` with your database settings: + +```ini +[database] +DB_TYPE = postgres +HOST = localhost:5432 +NAME = gitcaddy +USER = gitcaddy_user +PASSWD = your_secure_password +``` + +Supported database types: `postgres`, `mysql`, `sqlite3`, `mssql` + +#### 5. Start GitCaddy + +```bash +./gitcaddy web +``` + +By default, GitCaddy runs on port 3000. Access it at `http://localhost:3000` + +### First-Time Setup + +When you first access GitCaddy, you'll be guided through a web-based installation wizard: + +1. **Database Configuration**: Verify or adjust database connection settings +2. **General Settings**: Configure site title, domain, and SSH/HTTP ports +3. **Administrator Account**: Create your admin user account +4. **Optional Settings**: Configure email, server paths, and additional features +5. **Install**: Click "Install GitCaddy" to complete setup + +After installation, you'll be redirected to your dashboard where you can start creating repositories and inviting users. + +--- + +## Basic Repository Operations + +### Creating a Repository + +To create a new repository: + +1. Click the **"+"** icon in the top navigation bar +2. Select **"New Repository"** +3. Fill in the repository details: + - **Owner**: Select your user or an organization + - **Repository Name**: Choose a unique name + - **Description**: Optional description of your project + - **Visibility**: Public or Private + - **Initialize Repository**: Optionally add README, .gitignore, and license + - **Template**: Search for and select a repository template + +4. Click **"Create Repository"** + +**Example**: Creating a Node.js project repository + +``` +Owner: myusername +Repository Name: my-nodejs-app +Description: A sample Node.js application +Visibility: Private +✓ Initialize with README +.gitignore: Node +License: MIT License +``` + +### Cloning and Pushing Code + +After creating a repository, clone it to your local machine: + +```bash +# Clone via HTTPS +git clone https://gitcaddy.example.com/username/my-nodejs-app.git + +# Or clone via SSH +git clone git@gitcaddy.example.com:username/my-nodejs-app.git +``` + +Add files and push your first commit: + +```bash +cd my-nodejs-app +echo "# My Node.js App" > README.md +git add README.md +git commit -m "Initial commit" +git push origin main +``` + +### Managing Branches and Tags + +#### Creating a Branch + +```bash +# Create and switch to a new branch +git checkout -b feature/new-feature + +# Push the branch to GitCaddy +git push origin feature/new-feature +``` + +In the GitCaddy web interface: +1. Navigate to your repository +2. Click the **branch dropdown** (usually shows "main") +3. Type a new branch name and click **"Create branch"** + +#### Branch Protection Rules + +Protect important branches from direct pushes: + +1. Go to **Settings** → **Branches** +2. Click **"Add Rule"** +3. Configure protection settings: + - Require pull request reviews + - Require status checks to pass + - Restrict who can push + - Enable force push protection + +#### Creating Tags + +```bash +# Create an annotated tag +git tag -a v1.0.0 -m "Release version 1.0.0" + +# Push tags to GitCaddy +git push origin --tags +``` + +### File Operations + +GitCaddy provides a web-based file editor for quick changes: + +1. Navigate to a file in your repository +2. Click the **pencil icon** to edit +3. Make your changes in the markdown-aware editor +4. Add a commit message +5. Choose to commit directly or create a new branch +6. Click **"Commit Changes"** + +**Uploading Files:** +1. Navigate to the target directory +2. Click **"Upload file"** +3. Drag and drop files or click to browse +4. Add a commit message and commit + +**Deleting Files:** +1. Navigate to the file +2. Click the **trash icon** +3. Confirm deletion with a commit message + +--- + +## Collaboration Workflows + +### Working with Issues + +Issues are the foundation of project tracking in GitCaddy. They support rich features including labels, milestones, assignments, time tracking, and dependencies. + +#### Creating an Issue + +1. Navigate to your repository +2. Click the **"Issues"** tab +3. Click **"New Issue"** +4. Fill in the details: + - **Title**: Brief description of the issue + - **Description**: Detailed explanation using markdown + - **Labels**: Categorize the issue (bug, enhancement, question, etc.) + - **Milestone**: Associate with a project milestone + - **Assignees**: Assign team members + - **Projects**: Add to a project board + +5. Click **"Create Issue"** + +**Example Issue:** + +```markdown +Title: Add user authentication to API + +Description: +## Problem +The API currently lacks authentication, allowing unauthorized access. + +## Proposed Solution +Implement JWT-based authentication with the following endpoints: +- POST /api/auth/login +- POST /api/auth/register +- POST /api/auth/refresh + +## Acceptance Criteria +- [ ] JWT tokens are generated on successful login +- [ ] Protected endpoints validate tokens +- [ ] Refresh token mechanism is implemented +- [ ] Unit tests cover all auth flows + +Labels: enhancement, security +Milestone: v2.0.0 +Assignees: @developer1, @developer2 +``` + +#### Time Tracking + +Track time spent on issues: + +1. Open an issue +2. In the sidebar, find **"Time Tracking"** +3. Click **"Add Time"** and enter hours/minutes +4. View total time spent and estimates + +#### Issue Dependencies + +Create dependencies between issues: + +1. Open an issue +2. In the description or comments, reference another issue: `Depends on #123` +3. GitCaddy automatically creates a dependency link +4. The dependent issue cannot be closed until dependencies are resolved + +### Pull Request Workflow + +Pull requests (PRs) enable code review and collaborative development. + +#### Creating a Pull Request + +1. Push your feature branch to GitCaddy +2. Navigate to the repository +3. Click **"New Pull Request"** +4. Select the base branch (e.g., `main`) and compare branch (e.g., `feature/new-feature`) +5. Review the diff visualization +6. Fill in PR details: + - **Title**: Descriptive title + - **Description**: Explain changes, link related issues + - **Reviewers**: Request reviews from team members + - **Labels, Milestone, Assignees**: Same as issues + +7. Click **"Create Pull Request"** + +**Example PR Description:** + +```markdown +## Changes +This PR implements user authentication for the API as described in #45. + +## Implementation Details +- Added JWT middleware for token validation +- Created auth controller with login/register/refresh endpoints +- Implemented password hashing with bcrypt +- Added comprehensive unit tests (95% coverage) + +## Testing +- All existing tests pass +- New tests cover authentication flows +- Manual testing completed on staging environment + +Closes #45 +``` + +#### Reviewing Pull Requests + +As a reviewer: + +1. Open the pull request +2. Navigate to the **"Files changed"** tab +3. Review the diff: + - Click on a line number to add inline comments + - Suggest specific code changes + - Mark files as reviewed + +4. Submit your review: + - **Comment**: General feedback without approval + - **Approve**: Approve the changes + - **Request Changes**: Block merging until issues are addressed + +#### Merging Pull Requests + +Once approved and status checks pass: + +1. Click the **"Merge Pull Request"** button +2. Choose a merge strategy: + - **Create a merge commit**: Preserves all commits with a merge commit + - **Squash and merge**: Combines all commits into one + - **Rebase and merge**: Replays commits on top of base branch + +3. Confirm the merge +4. Optionally delete the feature branch + +### Code Reviews + +GitCaddy supports comprehensive code review features: + +- **Inline comments**: Comment on specific lines of code +- **Suggestion blocks**: Propose code changes that can be committed directly +- **Review threads**: Discussions can be marked as resolved +- **Status checks**: Automated checks must pass before merging +- **Required approvals**: Enforce minimum number of approvals + +**Making a code suggestion:** + +```markdown +```suggestion +function calculateTotal(items) { + return items.reduce((sum, item) => sum + item.price, 0); +} +``` +``` + +### Project Boards + +Organize work using Kanban-style project boards: + +1. Navigate to your repository +2. Click the **"Projects"** tab +3. Click **"New Project"** +4. Choose a template or start blank +5. Create columns (e.g., "To Do", "In Progress", "Done") +6. Add issues and pull requests to columns +7. Drag and drop cards between columns + +**Best Practice**: Use automation rules to move cards automatically based on issue/PR status. + +--- + +## Wiki Documentation + +Every repository includes a wiki for documentation. + +### Creating Wiki Pages + +1. Navigate to the **"Wiki"** tab in your repository +2. Click **"New Page"** +3. Enter a page title +4. Write content using the EasyMDE markdown editor with live preview +5. Click **"Save Page"** + +### Wiki Features + +- **Markdown support**: Full markdown syntax including tables, code blocks, and images +- **Mermaid diagrams**: Embed flowcharts, sequence diagrams, and more +- **Math formulas**: Use KaTeX for mathematical notation +- **Page hierarchy**: Organize pages with forward slashes (e.g., `Getting Started/Installation`) +- **History**: View page revisions and revert changes +- **Search**: Full-text search across all wiki pages + +**Example Wiki Page:** + +```markdown +# API Documentation + +## Authentication + +All API requests require a JWT token in the Authorization header: + +```http +GET /api/users +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... +``` + +## Endpoints + +### GET /api/users + +Returns a list of users. + +**Response:** +```json +{ + "users": [ + {"id": 1, "username": "alice"}, + {"id": 2, "username": "bob"} + ] +} +``` + +## Rate Limiting + +API requests are limited to 100 requests per hour per user. +``` + +--- + +## CI/CD with GitCaddy Actions + +GitCaddy Actions provides integrated CI/CD automation similar to GitHub Actions. + +### Creating Workflows + +Workflows are defined in YAML files stored in `.gitea/workflows/` directory. + +#### Example: Node.js Test Workflow + +Create `.gitea/workflows/test.yml`: + +```yaml +name: Node.js Tests + +on: + push: + branches: [main, develop] + pull_request: + branches: [main] + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [14, 16, 18] + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + + - name: Install dependencies + run: npm ci + + - name: Run tests + run: npm test + + - name: Upload coverage + uses: actions/upload-artifact@v3 + with: + name: coverage-report + path: coverage/ +``` + +Commit and push this file to trigger the workflow. + +### Managing Runners + +Runners execute your workflow jobs. GitCaddy supports self-hosted runners. + +#### Registering a Runner + +1. Navigate to **Settings** → **Actions** → **Runners** +2. Click **"Add Runner"** +3. Follow the instructions to download and configure the runner on your machine: + +```bash +# Download runner +wget https://gitcaddy.example.com/actions/runner/download + +# Extract and configure +./config.sh --url https://gitcaddy.example.com --token YOUR_RUNNER_TOKEN + +# Start the runner +./run.sh +``` + +#### Monitoring Runners + +- View runner status (online/offline) in the Runners page +- Check runner tasks and job history +- Run bandwidth tests to verify connectivity + +### Secrets and Variables + +Store sensitive data like API keys and passwords securely. + +#### Adding Secrets + +1. Navigate to **Settings** → **Secrets and Variables** → **Actions** +2. Click **"New repository secret"** +3. Enter a name (e.g., `API_KEY`) and value +4. Click **"Add secret"** + +#### Using Secrets in Workflows + +```yaml +steps: + - name: Deploy to production + run: ./deploy.sh + env: + API_KEY: ${{ secrets.API_KEY }} + DATABASE_URL: ${{ secrets.DATABASE_URL }} +``` + +**Security Note**: Secrets are encrypted and never exposed in logs. + +--- + +## Vault: Encrypted Secrets Management + +Vault is an enterprise feature providing advanced encrypted secrets management with version history and audit logging. + +### Creating Secrets + +1. Navigate to **Settings** → **Vault** +2. Click **"New Secret"** +3. Enter secret details: + - **Key**: Secret identifier (e.g., `production-db-password`) + - **Value**: Secret content (automatically encrypted) + - **Description**: Optional description + +4. Click **"Create Secret"** + +### Using Secrets in CI/CD + +Vault secrets can be accessed in workflows using access tokens. + +#### Generate an Access Token + +1. In Vault settings, click **"Access Tokens"** +2. Click **"Generate Token"** +3. Configure token permissions: + - **Scope**: Select specific secrets or all secrets + - **Expiration**: Set token lifetime + - **Permissions**: Read-only or read-write + +4. Copy the generated token (shown only once) + +#### Use Token in Workflow + +```yaml +steps: + - name: Fetch database credentials + run: | + curl -H "Authorization: Bearer ${{ secrets.VAULT_TOKEN }}" \ + https://gitcaddy.example.com/api/vault/secrets/production-db-password +``` + +### Audit Logs and Version History + +Vault maintains comprehensive audit logs: + +1. Navigate to **Vault** → **Audit Logs** +2. View all operations: + - Secret creation, updates, deletions + - Access token generation and usage + - User who performed each action + - Timestamp of each operation + +3. **Version History**: Click on a secret to view all previous versions +4. **Rollback**: Restore a previous version if needed + +**Vault Licensing Tiers:** +- **Solo**: Basic secrets management +- **Pro**: Advanced access controls +- **Team**: Multi-user collaboration features +- **Enterprise**: Full audit logging and compliance features + +--- + +## Package Registry + +GitCaddy includes a multi-format package registry supporting popular ecosystems. + +### Publishing Packages + +#### npm Packages + +Configure npm to use GitCaddy registry: + +```bash +# Set registry URL +npm config set registry https://gitcaddy.example.com/api/packages/username/npm/ + +# Authenticate +npm login --registry=https://gitcaddy.example.com/api/packages/username/npm/ + +# Publish package +npm publish +``` + +#### Docker Images + +```bash +# Login to registry +docker login gitcaddy.example.com + +# Tag image +docker tag myimage:latest gitcaddy.example.com/username/myimage:latest + +# Push image +docker push gitcaddy.example.com/username/myimage:latest +``` + +#### Maven Packages + +Add to your `pom.xml`: + +```xml + + + gitcaddy + https://gitcaddy.example.com/api/packages/username/maven + + +``` + +Deploy with Maven: + +```bash +mvn deploy +``` + +#### Python Packages (PyPI) + +Configure `.pypirc`: + +```ini +[distutils] +index-servers = gitcaddy + +[gitcaddy] +repository = https://gitcaddy.example.com/api/packages/username/pypi +username = your-username +password = your-token +``` + +Upload package: + +```bash +python setup.py sdist bdist_wheel +twine upload --repository gitcaddy dist/* +``` + +### Installing Packages + +GitCaddy provides installation instructions for each package in the web interface: + +1. Navigate to **Packages** in your repository or user profile +2. Click on a package +3. View ecosystem-specific installation commands + +**Example for npm:** + +```bash +npm install --registry=https://gitcaddy.example.com/api/packages/username/npm/ package-name +``` + +--- + +## AI-Powered Features + +GitCaddy integrates AI capabilities to enhance code quality and productivity. + +### AI Code Review + +Enable AI-assisted code reviews to receive automated feedback on pull requests. + +#### Enabling AI Code Review + +1. Navigate to **Repository Settings** → **AI Features** +2. Enable **"AI Code Review"** +3. Configure review preferences: + - **Review depth**: Quick scan or comprehensive analysis + - **Focus areas**: Security, performance, best practices + - **Auto-comment**: Automatically post suggestions as PR comments + +#### How It Works + +When a pull request is created: +1. AI analyzes the code changes +2. Identifies potential issues: + - Security vulnerabilities + - Performance bottlenecks + - Code smells and anti-patterns + - Style inconsistencies + +3. Posts suggestions as PR comments +4. Provides explanations and recommended fixes + +**Example AI Comment:** + +> 🤖 **AI Code Review** +> +> **Potential Security Issue**: SQL query is vulnerable to injection attacks. +> +> **Location**: `src/database.js:45` +> +> **Recommendation**: Use parameterized queries instead of string concatenation. +> +> ```javascript +> // Instead of: +> const query = `SELECT * FROM users WHERE id = ${userId}`; +> +> // Use: +> const query = 'SELECT * FROM users WHERE id = ?'; +> db.query(query, [userId]); +> ``` + +### AI Issue Triage + +AI automatically categorizes and prioritizes new issues. + +#### Enabling AI Issue Triage + +1. Navigate to **Repository Settings** → **AI Features** +2. Enable **"AI Issue Triage"** +3. Configure triage rules: + - **Auto-label**: Automatically apply labels based on content + - **Priority assignment**: Suggest priority levels + - **Similar issues**: Link to related issues + +#### How It Works + +When an issue is created: +1. AI analyzes the issue title and description +2. Suggests appropriate labels (bug, enhancement, documentation, etc.) +3. Identifies priority based on keywords and context +4. Finds similar existing issues to prevent duplicates + +### Code Explanation + +Get AI-generated explanations for complex code. + +#### Using Code Explanation + +1. Navigate to a file in your repository +2. Select a code block +3. Click the **"Explain with AI"** button +4. View the explanation in plain language + +**Example:** + +```javascript +const memoize = (fn) => { + const cache = new Map(); + return (...args) => { + const key = JSON.stringify(args); + if (cache.has(key)) return cache.get(key); + const result = fn(...args); + cache.set(key, result); + return result; + }; +}; +``` + +**AI Explanation:** +> This code implements a memoization function, which is a performance optimization technique. It creates a cache to store the results of expensive function calls. When the function is called with the same arguments again, it returns the cached result instead of recalculating it. This is useful for pure functions with expensive computations. + +--- + +## Organizations and Teams + +Organizations enable collaborative management of multiple repositories with team-based permissions. + +### Creating Organizations + +1. Click the **"+"** icon in the top navigation +2. Select **"New Organization"** +3. Fill in organization details: + - **Organization Name**: Unique identifier + - **Display Name**: Full name + - **Description**: Organization purpose + - **Website**: Optional URL + - **Location**: Optional location + +4. Click **"Create Organization"** + +### Managing Teams + +Teams group users within an organization and control access to repositories. + +#### Creating a Team + +1. Navigate to your organization +2. Click the **"Teams"** tab +3. Click **"New Team"** +4. Configure team settings: + - **Team Name**: Descriptive name + - **Description**: Team purpose + - **Visibility**: Visible or hidden + +5. Click **"Create Team"** + +#### Adding Team Members + +1. Open the team +2. Click **"Add Member"** +3. Search for users and click **"Add"** +4. Assign roles: + - **Member**: Standard access + - **Admin**: Can manage team settings + +### Team Permissions + +Control repository access per team: + +1. Navigate to **Teams** → Select a team +2. Click **"Repositories"** +3. Click **"Add Repository"** +4. Select repository and set permissions: + - **Read**: View code and issues + - **Write**: Push commits, create issues/PRs + - **Admin**: Full repository management + +**Best Practice**: Use teams to implement the principle of least privilege. Grant only the permissions necessary for each team's responsibilities. + +--- + +## Landing Pages and Custom Domains + +Create professional landing pages for your repositories with custom domains. + +### Enabling Landing Pages + +1. Navigate to **Repository Settings** → **Pages** +2. Enable **"Landing Pages"** +3. Configure page settings: + - **Source**: Select branch and directory (e.g., `main/docs`) + - **Custom Domain**: Enter your domain (e.g., `docs.myproject.com`) + - **SSL/TLS**: Enable HTTPS with automatic certificate + +4. Customize your landing page: + - **Hero Section**: Add title, subtitle, and call-to-action + - **Features**: Highlight key features with icons + - **Pricing**: Display pricing tiers + - **Branding**: Upload logo and customize colors + +5. Click **"Save Settings"** + +### Configuring Custom Domains + +1. In your DNS provider, add a CNAME record: + ``` + docs.myproject.com CNAME gitcaddy.example.com + ``` + +2. In GitCaddy Pages settings, enter the custom domain +3. Click **"Verify Domain"** +4. Enable **"Enforce HTTPS"** for automatic SSL certificate + +Your landing page will be accessible at `https://docs.myproject.com` + +### SEO Optimization + +Optimize landing pages for search engines: + +1. Navigate to **Pages** → **SEO Settings** +2. Configure meta tags: + - **Title**: Page title for search results + - **Description**: Meta description + - **Keywords**: Relevant keywords + - **Open Graph**: Social media preview settings + +--- + +## Authentication and Security + +GitCaddy provides multiple authentication methods and security features. + +### Two-Factor Authentication + +Enhance account security with 2FA. + +#### Enabling 2FA + +1. Navigate to **Settings** → **Security** +2. Click **"Enable Two-Factor Authentication"** +3. Scan the QR code with an authenticator app (Google Authenticator, Authy, etc.) +4. Enter the verification code +5. Save recovery codes in a secure location +6. Click **"Enable 2FA"** + +#### WebAuthn (Hardware Keys) + +Use hardware security keys for passwordless authentication: + +1. Navigate to **Settings** → **Security** → **WebAuthn** +2. Click **"Add Security Key"** +3. Enter a nickname for the key +4. Follow browser prompts to register your key +5. Test the key by logging out and back in + +### SSH and GPG Keys + +#### Adding SSH Keys + +1. Generate an SSH key pair on your machine: + ```bash + ssh-keygen -t ed25519 -C "your_email@example.com" + ``` + +2. Navigate to **Settings** → **SSH / GPG Keys** +3. Click **"Add Key"** +4. Paste your public key (`~/.ssh/id_ed25519.pub`) +5. Give it a descriptive name +6. Click **"Add Key"** + +Test your SSH connection: +```bash +ssh -T git@gitcaddy.example.com +``` + +#### Signing Commits with GPG + +1. Generate a GPG key: + ```bash + gpg --full-generate-key + ``` + +2. Export your public key: + ```bash + gpg --armor --export your_email@example.com + ``` + +3. Navigate to **Settings** → **SSH / GPG Keys** → **GPG Keys** +4. Click **"Add Key"** and paste your public key +5. Configure Git to sign commits: + ```bash + git config --global user.signingkey YOUR_KEY_ID + git config --global commit.gpgsign true + ``` + +Signed commits display a "Verified" badge in GitCaddy. + +### OAuth2 and SSO + +Configure external authentication providers for single sign-on. + +#### Supported Providers + +- GitHub +- GitLab +- Google +- Microsoft Azure AD +- LDAP/Active Directory +- SAML 2.0 +- OpenID Connect + +#### Configuring OAuth2 (Admin) + +1. Navigate to **Site Administration** → **Authentication Sources** +2. Click **"Add Authentication Source"** +3. Select provider type (e.g., **OAuth2**) +4. Configure provider settings: + - **Client ID**: From provider + - **Client Secret**: From provider + - **Authorization URL**: Provider-specific + - **Token URL**: Provider-specific + - **User Info URL**: Provider-specific + +5. Click **"Add Authentication Source"** + +Users can now log in using the external provider. + +--- + +## Customization and Settings + +### User Preferences + +Personalize your GitCaddy experience: + +1. Navigate to **Settings** → **Profile** +2. Configure preferences: + - **Display Name**: Your full name + - **Email**: Primary email address + - **Website**: Personal or company website + - **Location**: Your location + - **Biography**: Brief description + +3. **Appearance Settings**: + - **Language**: Select from 18+ supported languages (English, German, Hindi, Italian, Polish, Portuguese, etc.) + - **Theme**: Light, dark, or auto (system preference) + +4. **Notification Settings**: + - **Email Notifications**: Configure which events trigger emails + - **Web Notifications**: Enable browser notifications + - **Watching**: Auto-watch repositories you create or contribute to + +### Repository Settings + +Configure individual repository settings: + +1. Navigate to your repository → **Settings** +2. **General**: + - Repository name, description, website + - Visibility (public/private) + - Template repository + - Archive repository + +3. **Features**: + - Enable/disable issues, wiki, projects, actions + - External wiki URL + - External issue tracker + +4. **Collaborators**: + - Add users with specific permissions + - Manage access levels + +5. **Branches**: + - Default branch + - Branch protection rules + - Required status checks + +6. **Webhooks**: + - Configure webhooks for external integrations + +### Webhooks and Integrations + +Integrate GitCaddy with external services. + +#### Creating a Webhook + +1. Navigate to **Repository Settings** → **Webhooks** +2. Click **"Add Webhook"** +3. Configure webhook: + - **Payload URL**: Endpoint to receive events + - **Content Type**: JSON or form-encoded + - **Secret**: Optional secret for verification + - **Events**: Select which events trigger the webhook + - Push events + - Pull request events + - Issue events + - Release events + - etc. + +4. Click **"Add Webhook"** + +#### Supported Integrations + +GitCaddy provides pre-configured integrations for: + +- **Slack**: Post notifications to Slack channels +- **Discord**: Send updates to Discord servers +- **Microsoft Teams**: Integrate with Teams channels +- **Matrix**: Connect to Matrix chat rooms +- **Telegram**: Send notifications to Telegram +- **Email**: Custom email notifications + +**Example: Slack Integration** + +1. Create an incoming webhook in Slack +2. In GitCaddy, add a Slack webhook +3. Paste the Slack webhook URL +4. Select events to post (push, PR, issues) +5. Customize message format and channel + +--- + +## Tips and Best Practices + +### Repository Management + +1. **Use Templates**: Create repository templates for common project structures to save time +2. **Branch Protection**: Always enable branch protection on `main` and `production` branches +3. **Required Reviews**: Enforce at least one code review before merging pull requests +4. **Status Checks**: Set up CI/CD workflows as required status checks +5. **CODEOWNERS File**: Create a `CODEOWNERS` file to automatically request reviews from relevant team members + +### Issue Tracking + +1. **Labels**: Establish a consistent labeling system across repositories +2. **Templates**: Create issue templates for bug reports, feature requests, and questions +3. **Milestones**: Use milestones to track progress toward releases +4. **Project Boards**: Visualize work with project boards for sprint planning +5. **Time Tracking**: Estimate and track time for better project planning + +### Pull Requests + +1. **Small PRs**: Keep pull requests focused and small for easier review +2. **Descriptive Titles**: Write clear, descriptive PR titles +3. **Link Issues**: Always reference related issues in PR descriptions +4. **Draft PRs**: Use draft pull requests for work-in-progress code +5. **Review Checklist**: Create a PR template with a review checklist + +### CI/CD Workflows + +1. **Matrix Testing**: Test across multiple versions and platforms +2. **Caching**: Cache dependencies to speed up workflow runs +3. **Artifacts**: Upload build artifacts and test results +4. **Conditional Jobs**: Use conditions to skip unnecessary jobs +5. **Secrets Management**: Use Vault for sensitive credentials + +### Security + +1. **2FA**: Require two-factor authentication for all organization members +2. **Signed Commits**: Enforce GPG-signed commits for critical repositories +3. **Dependency Scanning**: Set up automated dependency vulnerability scanning +4. **Secret Scanning**: Enable secret scanning to prevent credential leaks +5. **Regular Audits**: Review access permissions and audit logs regularly + +### Performance + +1. **LFS for Large Files**: Use Git LFS for binary files and large assets +2. **Shallow Clones**: Use shallow clones in CI/CD to reduce clone time +3. **Archive Inactive Repos**: Archive repositories that are no longer maintained +4. **Cleanup**: Regularly clean up old branches and tags + +--- + +## Troubleshooting + +### Authentication Issues + +**Problem**: Cannot push or pull from repository + +**Solutions**: +1. Verify SSH key is added to your GitCaddy account +2. Test SSH connection: `ssh -T git@gitcaddy.example.com` +3. For HTTPS, ensure you're using a personal access token, not your password +4. Check repository permissions (you may only have read access) + +**Problem**: 2FA code not working + +**Solutions**: +1. Ensure your device clock is synchronized (2FA is time-based) +2. Try using a recovery code instead +3. Contact an administrator to reset 2FA if locked out + +### Workflow Issues + +**Problem**: GitCaddy Actions workflow not triggering + +**Solutions**: +1. Verify the workflow file is in `.gitea/workflows/` directory +2. Check YAML syntax (use a YAML validator) +3. Ensure the trigger events match your actions (e.g., `on: push`) +4. Check if Actions are enabled in repository settings +5. Verify at least one runner is online and available + +**Problem**: Workflow fails with "runner not found" + +**Solutions**: +1. Check runner status in **Settings** → **Actions** → **Runners** +2. Restart the runner service +3. Verify runner labels match workflow requirements +4. Check runner logs for connection issues + +### Repository Issues + +**Problem**: Large repository clone is slow + +**Solutions**: +1. Use shallow clone: `git clone --depth=1 ` +2. Enable Git LFS for large files +3. Consider using sparse checkout for monorepos +4. Check network connectivity and bandwidth + +**Problem**: Cannot merge pull request + +**Solutions**: +1. Resolve merge conflicts locally and push +2. Ensure all required status checks pass +3. Verify you have required approvals +4. Check branch protection rules +5. Ensure you have write permissions + +### Vault Issues + +**Problem**: Cannot access Vault secrets in workflow + +**Solutions**: +1. Verify access token has correct permissions +2. Check token hasn't expired +3. Ensure secret key name matches exactly (case-sensitive) +4. Verify Vault is enabled for the repository/organization + +### Package Registry Issues + +**Problem**: Cannot publish package + +**Solutions**: +1. Verify authentication credentials +2. Check package name doesn't conflict with existing package +3. Ensure you have write permissions to the registry +4. Review package manifest for errors +5. Check registry URL is correct for the package type + +**Problem**: Cannot install package + +**Solutions**: +1. Verify registry URL is configured correctly +2. Check authentication if package is private +3. Ensure package version exists +4. Try clearing package manager cache + +### Performance Issues + +**Problem**: GitCaddy web interface is slow + +**Solutions**: +1. Check server resources (CPU, memory, disk) +2. Review database performance and optimize queries +3. Enable caching in `app.ini` configuration +4. Check for large repositories causing indexing delays +5. Review logs for errors: `./gitcaddy admin logs` + +**Problem**: Git operations are slow + +**Solutions**: +1. Run `git gc` to optimize repository +2. Check if repository needs LFS for large files +3. Verify network latency between client and server +4. Consider using SSH instead of HTTPS (or vice versa) + +### Getting Help + +If you continue to experience issues: + +1. **Documentation**: Check the official GitCaddy documentation +2. **Community Forum**: Search or post in the community forum +3. **Issue Tracker**: Report bugs in the GitCaddy repository +4. **Admin Logs**: Review system logs in **Site Administration** → **Monitoring** +5. **Support**: Contact your GitCaddy administrator or support team + +**Useful Commands**: + +```bash +# Check GitCaddy version +./gitcaddy --version + +# View logs +./gitcaddy admin logs + +# Run diagnostics +./gitcaddy doctor + +# Database maintenance +./gitcaddy admin regenerate hooks +./gitcaddy admin regenerate keys +``` + +--- + +## Conclusion + +GitCaddy provides a comprehensive, self-hosted solution for Git repository management with powerful features for collaboration, automation, and security. This guide has covered the essential workflows and features to help you get started and become productive quickly. + +For advanced topics, enterprise features, and administrative configuration, refer to the official GitCaddy documentation or contact your system administrator. + +Happy coding! 🚀 \ No newline at end of file diff --git a/options/locale/locale_en-US.json b/options/locale/locale_en-US.json index fbeee11c0a..eeb08cffaa 100644 --- a/options/locale/locale_en-US.json +++ b/options/locale/locale_en-US.json @@ -1004,6 +1004,8 @@ "repo.readme": "README", "repo.readme_helper": "Select a README file template.", "repo.readme_helper_desc": "This is the place where you can write a complete description for your project.", + "repo.guide": "User Guide", + "repo.api_doc": "API", "repo.auto_init": "Initialize Repository (Adds .gitignore, License and README)", "repo.trust_model_helper": "Select trust model for signature verification. Possible options are:", "repo.trust_model_helper_collaborator": "Collaborator: Trust signatures by collaborators", diff --git a/routers/web/repo/view_home.go b/routers/web/repo/view_home.go index f9a7fec18b..b3cf52ee5e 100644 --- a/routers/web/repo/view_home.go +++ b/routers/web/repo/view_home.go @@ -14,6 +14,7 @@ import ( "code.gitcaddy.com/server/v3/models/db" git_model "code.gitcaddy.com/server/v3/models/git" + "code.gitcaddy.com/server/v3/models/renderhelper" repo_model "code.gitcaddy.com/server/v3/models/repo" unit_model "code.gitcaddy.com/server/v3/models/unit" user_model "code.gitcaddy.com/server/v3/models/user" @@ -23,6 +24,7 @@ import ( "code.gitcaddy.com/server/v3/modules/httplib" "code.gitcaddy.com/server/v3/modules/json" "code.gitcaddy.com/server/v3/modules/log" + "code.gitcaddy.com/server/v3/modules/markup/markdown" "code.gitcaddy.com/server/v3/modules/plugins" repo_module "code.gitcaddy.com/server/v3/modules/repository" "code.gitcaddy.com/server/v3/modules/setting" @@ -259,6 +261,62 @@ func isGalleryImageFile(filename string) bool { return false } +// prepareHomeDocTab loads a markdown doc file for a named tab on the repo home page. +// keyPrefix is used to namespace context data (e.g. "Guide" → "GuideTabExist", "GuideTabContent"). +func prepareHomeDocTab(ctx *context.Context, filename, keyPrefix string) { + ctx.Data[keyPrefix+"TabExist"] = false + + if ctx.Repo.Repository.IsEmpty { + return + } + + commit := ctx.Repo.Commit + if commit == nil { + return + } + + entry, err := commit.GetTreeEntryByPath(filename) + if err != nil || entry.IsDir() { + return + } + + ctx.Data[keyPrefix+"TabExist"] = true + ctx.Data[keyPrefix+"TabFileName"] = filename + + // Load content + content, err := entry.Blob().GetBlobContent(setting.UI.MaxDisplayFileSize) + if err != nil { + log.Error("prepareHomeDocTab(%s): GetBlobContent: %v", filename, err) + return + } + + // Render markdown using the full markup pipeline + rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{ + CurrentRefPath: ctx.Repo.RefTypeNameSubURL(), + CurrentTreePath: ".", + }).WithMarkupType("markdown").WithRelativePath(filename) + + rendered, err := markdown.RenderString(rctx, content) + if err != nil { + log.Error("prepareHomeDocTab(%s): RenderString: %v", filename, err) + ctx.Data[keyPrefix+"TabContent"] = content + ctx.Data[keyPrefix+"TabIsMarkdown"] = false + return + } + ctx.Data[keyPrefix+"TabContent"] = rendered + ctx.Data[keyPrefix+"TabIsMarkdown"] = true +} + +// prepareHomeGuideTab loads GUIDE.md for the User Guide tab +func prepareHomeGuideTab(ctx *context.Context) { + prepareHomeDocTab(ctx, "GUIDE.md", "Guide") +} + +// prepareHomeAPIDocTab loads API.md for the API tab +func prepareHomeAPIDocTab(ctx *context.Context) { + prepareHomeDocTab(ctx, "API.md", "APIDoc") +} + func prepareToRenderDirectory(ctx *context.Context) { entries := renderDirectoryFiles(ctx, 1*time.Second) if ctx.Written() { @@ -594,6 +652,8 @@ func Home(ctx *context.Context) { prepareUpstreamDivergingInfo, prepareHomeSidebarLicenses, prepareHomeLicenseTab, + prepareHomeGuideTab, + prepareHomeAPIDocTab, prepareHomeGalleryTab, prepareHomeSidebarCitationFile(entry), prepareHomeSidebarLanguageStats, diff --git a/templates/repo/view_content.tmpl b/templates/repo/view_content.tmpl index b6314b46f2..977d4bd10e 100644 --- a/templates/repo/view_content.tmpl +++ b/templates/repo/view_content.tmpl @@ -130,43 +130,64 @@ {{template "repo/code/upstream_diverging_info" .}} {{end}} {{template "repo/view_list" .}} - {{/* Tabs for Readme, License, Gallery - only shown at repo root */}} - {{if and $isTreePathRoot (or (and .ReadmeExist (or .IsMarkup .IsPlainText)) .LicenseTabExist .GalleryTabExist)}} + {{/* Tabs for Readme, Guide, API, License, Gallery - only shown at repo root */}} + {{$hasReadme := and .ReadmeExist (or .IsMarkup .IsPlainText)}} + {{if and $isTreePathRoot (or $hasReadme .GuideTabExist .APIDocTabExist .LicenseTabExist .GalleryTabExist)}}
- {{if and .ReadmeExist (or .IsMarkup .IsPlainText)}} + {{if $hasReadme}}
{{template "repo/view_file" .}}
{{end}} + {{if .GuideTabExist}} +
+ {{template "repo/view_doc_tab" dict "ctxData" . "FileName" .GuideTabFileName "Content" .GuideTabContent "IsMarkdown" .GuideTabIsMarkdown "Icon" "octicon-mortar-board"}} +
+ {{end}} + {{if .APIDocTabExist}} +
+ {{template "repo/view_doc_tab" dict "ctxData" . "FileName" .APIDocTabFileName "Content" .APIDocTabContent "IsMarkdown" .APIDocTabIsMarkdown "Icon" "octicon-plug"}} +
+ {{end}} {{if .LicenseTabExist}} -
+
{{template "repo/view_license_tab" .}}
{{end}} {{if .GalleryTabExist}} -
+
{{template "repo/view_gallery_tab" .}}
{{end}}
- {{else if and .ReadmeExist (or .IsMarkup .IsPlainText)}} + {{else if $hasReadme}} {{/* Fallback: show README without tabs if it's the only content */}} {{template "repo/view_file" .}} {{end}} diff --git a/templates/repo/view_doc_tab.tmpl b/templates/repo/view_doc_tab.tmpl new file mode 100644 index 0000000000..f155f53d0f --- /dev/null +++ b/templates/repo/view_doc_tab.tmpl @@ -0,0 +1,27 @@ +
+ +
+ {{if .IsMarkdown}} +
+ {{.Content}} +
+ {{else}} +
+
{{.Content}}
+
+ {{end}} +
+