From f5f796cd168a87c74540eec01c758ee1d3e94999 Mon Sep 17 00:00:00 2001 From: Snider Date: Sat, 21 Feb 2026 17:04:59 +0000 Subject: [PATCH] docs: wiki documentation for go-forge Seven pages covering Home, Architecture, Services, Code Generation, Configuration, Error Handling, and Development. Co-Authored-By: Virgil Co-Authored-By: Claude Opus 4.6 --- docs/wiki/Architecture.md | 122 +++++++++++++ docs/wiki/Code-Generation.md | 125 +++++++++++++ docs/wiki/Configuration.md | 88 ++++++++++ docs/wiki/Development.md | 123 +++++++++++++ docs/wiki/Error-Handling.md | 87 +++++++++ docs/wiki/Home.md | 69 ++++++++ docs/wiki/Services.md | 329 +++++++++++++++++++++++++++++++++++ 7 files changed, 943 insertions(+) create mode 100644 docs/wiki/Architecture.md create mode 100644 docs/wiki/Code-Generation.md create mode 100644 docs/wiki/Configuration.md create mode 100644 docs/wiki/Development.md create mode 100644 docs/wiki/Error-Handling.md create mode 100644 docs/wiki/Home.md create mode 100644 docs/wiki/Services.md diff --git a/docs/wiki/Architecture.md b/docs/wiki/Architecture.md new file mode 100644 index 0000000..6374021 --- /dev/null +++ b/docs/wiki/Architecture.md @@ -0,0 +1,122 @@ +# Architecture + +## Overview + +go-forge is structured around three core ideas: + +1. **Generic Resource** — a single `Resource[T, C, U]` type that provides CRUD methods for any Forgejo API resource. +2. **Code generation** — all 229 Go types are generated from `swagger.v1.json` via a custom codegen tool. +3. **Service embedding** — each service struct embeds `Resource` for free CRUD, then adds hand-written action methods for non-CRUD endpoints. + +## File Layout + +``` +go-forge/ + client.go HTTP client with auth, context, error handling + pagination.go Generic ListPage[T] and ListAll[T] + params.go Path variable resolution ({owner}/{repo} -> values) + resource.go Generic Resource[T, C, U] for CRUD + config.go Config resolution: flags -> env -> defaults + forge.go Top-level Forge client aggregating all 18 services + + repos.go RepoService (embeds Resource) + issues.go IssueService (embeds Resource) + pulls.go PullService (embeds Resource) + orgs.go OrgService (embeds Resource) + users.go UserService (embeds Resource) + teams.go TeamService (embeds Resource) + branches.go BranchService (embeds Resource) + releases.go ReleaseService (embeds Resource) + webhooks.go WebhookService (embeds Resource) + admin.go AdminService (plain client) + labels.go LabelService (plain client) + contents.go ContentService (plain client) + actions.go ActionsService (plain client) + notifications.go NotificationService (plain client) + packages.go PackageService (plain client) + wiki.go WikiService (plain client) + misc.go MiscService (plain client) + commits.go CommitService (plain client) + + cmd/forgegen/ Code generator + main.go CLI entry point + parser.go Swagger 2.0 spec parser + generator.go Go source code emitter + + types/ Generated Go types (229 types across 36 files) + generate.go go:generate directive + repo.go Repository, CreateRepoOption, EditRepoOption, ... + issue.go Issue, CreateIssueOption, ... + ... + + testdata/ + swagger.v1.json Forgejo Swagger 2.0 specification +``` + +## Generic Resource[T, C, U] + +The core abstraction. `T` is the resource type (e.g. `types.Repository`), `C` is the create options type (e.g. `types.CreateRepoOption`), `U` is the update options type (e.g. `types.EditRepoOption`). + +```go +type Resource[T any, C any, U any] struct { + client *Client + path string // e.g. "/api/v1/repos/{owner}/{repo}" +} +``` + +Provides six methods out of the box: + +| Method | HTTP | Description | +|----------|----------|-----------------------------------| +| `List` | GET | Single page with `ListOptions` | +| `ListAll`| GET | All pages (auto-pagination) | +| `Get` | GET | Single resource by path params | +| `Create` | POST | Create with `*C` body | +| `Update` | PATCH | Update with `*U` body | +| `Delete` | DELETE | Delete by path params | + +Path parameters like `{owner}` and `{repo}` are resolved at call time via `Params` maps: + +```go +repo, err := f.Repos.Get(ctx, forge.Params{"owner": "core", "repo": "go-forge"}) +``` + +## Service Pattern + +Services that fit the CRUD model embed `Resource`: + +```go +type RepoService struct { + Resource[types.Repository, types.CreateRepoOption, types.EditRepoOption] +} +``` + +This gives `RepoService` free `List`, `ListAll`, `Get`, `Create`, `Update`, and `Delete` methods. Action methods like `Fork`, `Transfer`, and `MirrorSync` are added as hand-written methods. + +Services with heterogeneous endpoints (e.g. `AdminService`, `LabelService`) hold a plain `*Client` field instead of embedding `Resource`. + +## Pagination + +All list endpoints use Forgejo's `X-Total-Count` header for total item counts and `page`/`limit` query parameters. + +- `ListPage[T]` — fetches a single page, returns `*PagedResult[T]` with `Items`, `TotalCount`, `Page`, `HasMore`. +- `ListAll[T]` — iterates all pages automatically, returns `[]T`. + +## HTTP Client + +`Client` is a low-level HTTP client that: + +- Adds `Authorization: token ` to every request +- Sets `Accept: application/json` and `Content-Type: application/json` +- Wraps errors as `*APIError` with `StatusCode`, `Message`, `URL` +- Supports `Get`, `Post`, `Patch`, `Put`, `Delete`, `DeleteWithBody`, `GetRaw`, `PostRaw` +- Accepts `context.Context` on all methods + +## Code Generation Pipeline + +``` +swagger.v1.json --> parser.go --> generator.go --> types/*.go + (extract) (emit Go) (229 types) +``` + +The parser reads the Swagger 2.0 spec, extracts type definitions, detects Create/Edit option pairs, and resolves Go types. The generator groups types by domain (repo, issue, PR, etc.) and writes one `.go` file per group with `// Code generated` headers. diff --git a/docs/wiki/Code-Generation.md b/docs/wiki/Code-Generation.md new file mode 100644 index 0000000..5bd9a18 --- /dev/null +++ b/docs/wiki/Code-Generation.md @@ -0,0 +1,125 @@ +# Code Generation + +All Go types in the `types/` package are generated from the Forgejo Swagger 2.0 specification. This page explains how the pipeline works and how to regenerate types. + +## Pipeline + +``` +testdata/swagger.v1.json + | + v + cmd/forgegen/parser.go (parse spec, extract types, detect CRUD pairs) + | + v + cmd/forgegen/generator.go (group types by domain, emit Go source) + | + v + types/*.go (229 types across 36 files) +``` + +## Regenerating Types + +```bash +go generate ./types/... +``` + +This runs the `//go:generate` directive in `types/generate.go`: + +```go +//go:generate go run ../cmd/forgegen/ -spec ../testdata/swagger.v1.json -out . +``` + +You can also run the generator directly: + +```bash +go run ./cmd/forgegen/ -spec testdata/swagger.v1.json -out types/ +``` + +## Upgrading Forgejo Version + +1. Download the new `swagger.v1.json` from your Forgejo instance at `/swagger.json`. +2. Replace `testdata/swagger.v1.json` with the new file. +3. Regenerate types: + ```bash + go generate ./types/... + ``` +4. Verify compilation: + ```bash + go build ./... + ``` +5. Run tests: + ```bash + go test ./... + ``` +6. Review the diff for new, changed, or removed types. + +## How It Works + +### Parser (`cmd/forgegen/parser.go`) + +- **`LoadSpec`** — reads and parses the JSON spec file. +- **`ExtractTypes`** — converts all `definitions` into `GoType` structs with fields, JSON tags, and Go type mappings. +- **`DetectCRUDPairs`** — finds matching `Create*Option`/`Edit*Option` pairs (e.g. `CreateRepoOption` + `EditRepoOption` = base `Repo`). + +Type mapping rules: + +| Swagger Type | Go Type | +|--------------------|----------------| +| `string` | `string` | +| `string` + `date-time` | `time.Time` | +| `string` + `binary` | `[]byte` | +| `integer` + `int64` | `int64` | +| `integer` + `int32` | `int32` | +| `boolean` | `bool` | +| `number` + `float` | `float32` | +| `number` | `float64` | +| `array` | `[]`| +| `object` | `map[string]any`| +| `$ref` | `*` | + +### Generator (`cmd/forgegen/generator.go`) + +- **`classifyType`** — assigns each type to a domain file (e.g. `Repository` -> `repo.go`, `Issue` -> `issue.go`). +- **`Generate`** — groups types by file, sorts alphabetically, and writes each file. +- **`writeFile`** — uses `text/template` to emit valid Go with `package types`, conditional `import "time"`, and `// Code generated` headers. + +### Type Grouping + +Types are grouped by prefix matching against a domain map: + +| Prefix/Name | File | +|------------------|-----------------| +| `Repository`, `Repo` | `repo.go` | +| `Issue` | `issue.go` | +| `PullRequest`, `Pull` | `pr.go` | +| `User` | `user.go` | +| `Organization`, `Org` | `org.go` | +| `Team` | `team.go` | +| `Branch` | `branch.go` | +| `Release` | `release.go` | +| `Hook` | `hook.go` | +| `Commit`, `Git` | `commit.go`/`git.go` | +| `Package` | `package.go` | +| `Action`, `Secret`, `Variable` | `action.go` | +| `Notification` | `notification.go` | +| `Wiki` | `wiki.go` | +| (unmatched) | `misc.go` | + +## Generated File Format + +Each file follows this structure: + +```go +// Code generated by forgegen from swagger.v1.json — DO NOT EDIT. + +package types + +import "time" // only if time.Time fields are present + +// TypeName — description from swagger spec. +type TypeName struct { + FieldName GoType `json:"field_name,omitempty"` +} +``` + +Fields marked as `required` in the spec omit the `,omitempty` JSON tag option. diff --git a/docs/wiki/Configuration.md b/docs/wiki/Configuration.md new file mode 100644 index 0000000..60976e8 --- /dev/null +++ b/docs/wiki/Configuration.md @@ -0,0 +1,88 @@ +# Configuration + +go-forge resolves its Forgejo URL and API token from multiple sources with a clear priority chain. + +## Priority Order + +``` +1. Flags (highest) — passed directly to NewForgeFromConfig or ResolveConfig +2. Environment variables — FORGE_URL, FORGE_TOKEN +3. Defaults (lowest) — http://localhost:3000 for URL, no token default +``` + +## Environment Variables + +| Variable | Description | Default | +|---------------|--------------------------------------|--------------------------| +| `FORGE_URL` | Base URL of the Forgejo instance | `http://localhost:3000` | +| `FORGE_TOKEN` | API token for authentication | _(none — required)_ | + +## Using NewForgeFromConfig + +The simplest way to create a configured client: + +```go +// Reads from FORGE_URL and FORGE_TOKEN environment variables. +f, err := forge.NewForgeFromConfig("", "") +if err != nil { + log.Fatal(err) // "no API token configured" if FORGE_TOKEN is unset +} +``` + +With flag overrides: + +```go +// Flag values take priority over env vars. +f, err := forge.NewForgeFromConfig("https://forge.example.com", "my-token") +``` + +## Using ResolveConfig Directly + +For more control, resolve the config separately: + +```go +url, token, err := forge.ResolveConfig(flagURL, flagToken) +if err != nil { + log.Fatal(err) +} +// Use url and token as needed... +f := forge.NewForge(url, token) +``` + +## Using NewForge Directly + +If you already have the URL and token: + +```go +f := forge.NewForge("https://forge.lthn.ai", "my-api-token") +``` + +## Client Options + +`NewForge` and `NewForgeFromConfig` accept variadic options: + +```go +// Custom HTTP client (e.g. with timeouts, proxies, TLS config) +f := forge.NewForge(url, token, + forge.WithHTTPClient(&http.Client{Timeout: 30 * time.Second}), +) + +// Custom User-Agent header +f := forge.NewForge(url, token, + forge.WithUserAgent("my-app/1.0"), +) +``` + +| Option | Description | Default | +|--------------------|-------------------------------|------------------| +| `WithHTTPClient` | Set a custom `*http.Client` | `http.DefaultClient` | +| `WithUserAgent` | Set the `User-Agent` header | `go-forge/0.1` | + +## Accessing the Low-Level Client + +If you need direct HTTP access for custom endpoints: + +```go +client := f.Client() // returns *forge.Client +err := client.Get(ctx, "/api/v1/custom/endpoint", &result) +``` diff --git a/docs/wiki/Development.md b/docs/wiki/Development.md new file mode 100644 index 0000000..a187a84 --- /dev/null +++ b/docs/wiki/Development.md @@ -0,0 +1,123 @@ +# Development + +## Prerequisites + +- Go 1.21+ (generics support required) +- Access to a Forgejo instance for integration testing (optional) + +## Building + +```bash +go build ./... +``` + +## Running Tests + +```bash +# All tests +go test ./... + +# Verbose output +go test -v ./... + +# Single test +go test -v -run TestClient_Good_Get ./... + +# With race detection +go test -race ./... +``` + +## Test Naming Convention + +Tests use a `_Good`, `_Bad`, `_Ugly` suffix pattern: + +| Suffix | Purpose | +|---------|--------------------------------------| +| `_Good` | Happy path — expected success | +| `_Bad` | Expected errors — invalid input, 404 | +| `_Ugly` | Edge cases — panics, empty data | + +## Regenerating Types + +When the Forgejo Swagger spec is updated: + +```bash +# Using go generate +go generate ./types/... + +# Or directly +go run ./cmd/forgegen/ -spec testdata/swagger.v1.json -out types/ +``` + +Then verify: + +```bash +go build ./... +go test ./... +go vet ./... +``` + +## Adding a New Service + +1. Create `servicename.go` with the service struct: + - If CRUD applies: embed `Resource[T, C, U]` with the appropriate types. + - If endpoints are heterogeneous: use a plain `client *Client` field. + +2. Add a constructor: `func newServiceNameService(c *Client) *ServiceNameService` + +3. Add action methods for non-CRUD endpoints. + +4. Create `servicename_test.go` with tests following the `_Good`/`_Bad`/`_Ugly` convention. + +5. Wire the service in `forge.go`: + - Add the field to the `Forge` struct. + - Add `f.ServiceName = newServiceNameService(c)` in `NewForge`. + +6. Run all tests: `go test ./...` + +## Adding a New Action Method + +Action methods are hand-written methods on service structs for endpoints that don't fit the CRUD pattern. + +```go +func (s *ServiceName) ActionName(ctx context.Context, params...) (returnType, error) { + path := fmt.Sprintf("/api/v1/path/%s/%s", param1, param2) + var out types.ReturnType + if err := s.client.Post(ctx, path, body, &out); err != nil { + return nil, err + } + return &out, nil +} +``` + +All methods must: +- Accept `context.Context` as the first parameter +- Return `error` as the last return value +- Use `fmt.Sprintf` for path construction with parameters + +## Project Conventions + +- **UK English** in comments (organisation, colour, licence) +- **`context.Context`** as the first parameter on all methods +- Errors wrapped as `*APIError` for HTTP responses +- Generated code has `// Code generated` headers and must not be edited manually +- Commit messages use conventional commits (`feat:`, `fix:`, `docs:`) + +## Project Structure + +``` +go-forge/ + *.go Core library (client, pagination, resource, services) + *_test.go Tests for each module + cmd/forgegen/ Code generator + types/ Generated types (do not edit) + testdata/ Swagger specification + docs/ Documentation +``` + +## Forge Remote + +```bash +git remote add forge ssh://git@forge.lthn.ai:2223/core/go-forge.git +git push -u forge main +``` diff --git a/docs/wiki/Error-Handling.md b/docs/wiki/Error-Handling.md new file mode 100644 index 0000000..c0407fe --- /dev/null +++ b/docs/wiki/Error-Handling.md @@ -0,0 +1,87 @@ +# Error Handling + +All go-forge methods return errors wrapped as `*APIError` for HTTP error responses. This page covers the error types and how to handle them. + +## APIError + +When the Forgejo API returns an HTTP status >= 400, go-forge wraps the response as: + +```go +type APIError struct { + StatusCode int // HTTP status code (e.g. 404, 403, 409) + Message string // Error message from the API response body + URL string // The request path +} +``` + +The `Error()` method formats as: `forge: /api/v1/repos/core/missing 404: not found` + +## Sentinel Checks + +Three helper functions test for common HTTP error statuses: + +```go +repo, err := f.Repos.Get(ctx, forge.Params{"owner": "core", "repo": "missing"}) +if err != nil { + if forge.IsNotFound(err) { + // 404 — resource does not exist + fmt.Println("Repository not found") + } else if forge.IsForbidden(err) { + // 403 — insufficient permissions + fmt.Println("Access denied") + } else if forge.IsConflict(err) { + // 409 — conflict (e.g. resource already exists) + fmt.Println("Conflict") + } else { + // Other error (network, 500, etc.) + log.Fatal(err) + } +} +``` + +| Function | Status Code | Typical Cause | +|----------------|-------------|-----------------------------------| +| `IsNotFound` | 404 | Resource does not exist | +| `IsForbidden` | 403 | Insufficient permissions or scope | +| `IsConflict` | 409 | Resource already exists | + +## Extracting APIError + +Use `errors.As` to access the full `APIError`: + +```go +var apiErr *forge.APIError +if errors.As(err, &apiErr) { + fmt.Printf("Status: %d\n", apiErr.StatusCode) + fmt.Printf("Message: %s\n", apiErr.Message) + fmt.Printf("URL: %s\n", apiErr.URL) +} +``` + +## Non-API Errors + +Errors that are not HTTP responses (network failures, JSON decode errors, request construction failures) are returned as plain `error` values with descriptive prefixes: + +| Prefix | Cause | +|-----------------------------|------------------------------------| +| `forge: marshal body:` | Failed to JSON-encode request body | +| `forge: create request:` | Failed to build HTTP request | +| `forge: request GET ...:` | Network error during request | +| `forge: decode response:` | Failed to JSON-decode response | +| `forge: parse url:` | Invalid URL in pagination | +| `forge: read response body:`| Failed to read raw response body | + +## Context Cancellation + +All methods accept `context.Context`. When the context is cancelled or times out, the underlying `http.Client` returns a context error: + +```go +ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) +defer cancel() + +repos, err := f.Repos.ListOrgRepos(ctx, "core") +if err != nil { + // Could be context.DeadlineExceeded or context.Canceled + log.Fatal(err) +} +``` diff --git a/docs/wiki/Home.md b/docs/wiki/Home.md new file mode 100644 index 0000000..fb3dad2 --- /dev/null +++ b/docs/wiki/Home.md @@ -0,0 +1,69 @@ +# go-forge + +Full-coverage Go client for the [Forgejo](https://forgejo.org/) API. + +**Module:** `forge.lthn.ai/core/go-forge` + +## Features + +- 18 service modules covering repositories, issues, pull requests, organisations, teams, users, admin, branches, releases, labels, webhooks, notifications, packages, actions, contents, wiki, commits, and miscellaneous endpoints +- Generic `Resource[T, C, U]` pattern providing free CRUD for most services +- 229 Go types generated from the Forgejo Swagger 2.0 specification +- Automatic pagination with `ListAll` and `ListPage` generics +- Config resolution from flags, environment variables, and defaults +- Typed error handling with `IsNotFound`, `IsForbidden`, `IsConflict` helpers + +## Install + +```bash +go get forge.lthn.ai/core/go-forge +``` + +## Quick Start + +```go +package main + +import ( + "context" + "fmt" + "log" + + "forge.lthn.ai/core/go-forge" +) + +func main() { + // Create a client — reads FORGE_URL and FORGE_TOKEN from environment. + f, err := forge.NewForgeFromConfig("", "") + if err != nil { + log.Fatal(err) + } + + ctx := context.Background() + + // Get the authenticated user. + me, err := f.Users.GetCurrent(ctx) + if err != nil { + log.Fatal(err) + } + fmt.Printf("Hello, %s!\n", me.Login) + + // List repositories for an organisation. + repos, err := f.Repos.ListOrgRepos(ctx, "core") + if err != nil { + log.Fatal(err) + } + for _, r := range repos { + fmt.Println(r.FullName) + } +} +``` + +## Documentation + +- [Architecture](Architecture) — Generic Resource pattern, codegen pipeline, service design +- [Services](Services) — All 18 services with method signatures +- [Code Generation](Code-Generation) — How types are generated from the Swagger spec +- [Configuration](Configuration) — Environment variables, flags, defaults +- [Error Handling](Error-Handling) — APIError, sentinel checks, error wrapping +- [Development](Development) — Building, testing, contributing diff --git a/docs/wiki/Services.md b/docs/wiki/Services.md new file mode 100644 index 0000000..e9da721 --- /dev/null +++ b/docs/wiki/Services.md @@ -0,0 +1,329 @@ +# Services + +go-forge provides 18 services accessible via the top-level `Forge` client. Each service groups related API endpoints. + +```go +f := forge.NewForge("https://forge.lthn.ai", "my-token") + +f.Repos // Repository operations +f.Issues // Issue operations +f.Pulls // Pull request operations +f.Orgs // Organisation operations +f.Users // User operations +f.Teams // Team operations +f.Admin // Site administration +f.Branches // Branch operations +f.Releases // Release operations +f.Labels // Label operations +f.Webhooks // Webhook operations +f.Notifications // Notification operations +f.Packages // Package registry +f.Actions // CI/CD actions +f.Contents // File read/write +f.Wiki // Wiki pages +f.Misc // Markdown, licences, gitignore, server info +f.Commits // Commit statuses and git notes +``` + +--- + +## RepoService + +Embeds `Resource[Repository, CreateRepoOption, EditRepoOption]`. +Path: `/api/v1/repos/{owner}/{repo}` + +**CRUD** (via Resource): `List`, `ListAll`, `Get`, `Create`, `Update`, `Delete` + +**Action methods:** + +| Method | Description | +|------------------|----------------------------------------------| +| `ListOrgRepos` | All repositories for an organisation | +| `ListUserRepos` | All repositories for the authenticated user | +| `Fork` | Fork a repository (optionally into an org) | +| `Transfer` | Initiate a repository transfer | +| `AcceptTransfer` | Accept a pending transfer | +| `RejectTransfer` | Reject a pending transfer | +| `MirrorSync` | Trigger a mirror sync | + +--- + +## IssueService + +Embeds `Resource[Issue, CreateIssueOption, EditIssueOption]`. +Path: `/api/v1/repos/{owner}/{repo}/issues/{index}` + +**Action methods:** + +| Method | Description | +|------------------|------------------------------------| +| `Pin` | Pin an issue | +| `Unpin` | Unpin an issue | +| `SetDeadline` | Set or update a deadline | +| `AddReaction` | Add a reaction emoji | +| `DeleteReaction` | Remove a reaction emoji | +| `StartStopwatch` | Start time tracking | +| `StopStopwatch` | Stop time tracking | +| `AddLabels` | Add labels by ID | +| `RemoveLabel` | Remove a single label | +| `ListComments` | List all comments on an issue | +| `CreateComment` | Create a comment | + +--- + +## PullService + +Embeds `Resource[PullRequest, CreatePullRequestOption, EditPullRequestOption]`. +Path: `/api/v1/repos/{owner}/{repo}/pulls/{index}` + +**Action methods:** + +| Method | Description | +|-------------------|----------------------------------------| +| `Merge` | Merge a pull request (merge, rebase, squash, etc.) | +| `Update` | Update branch with base branch | +| `ListReviews` | List all reviews | +| `SubmitReview` | Submit a new review | +| `DismissReview` | Dismiss a review | +| `UndismissReview` | Undismiss a review | + +--- + +## OrgService + +Embeds `Resource[Organization, CreateOrgOption, EditOrgOption]`. +Path: `/api/v1/orgs/{org}` + +**Action methods:** + +| Method | Description | +|----------------|--------------------------------------------| +| `ListMembers` | List all members of an organisation | +| `AddMember` | Add a user to an organisation | +| `RemoveMember` | Remove a user from an organisation | +| `ListUserOrgs` | List organisations for a given user | +| `ListMyOrgs` | List organisations for the authenticated user | + +--- + +## UserService + +Embeds `Resource[User, struct{}, struct{}]` (read-only via CRUD). +Path: `/api/v1/users/{username}` + +**Action methods:** + +| Method | Description | +|-----------------|--------------------------------------------| +| `GetCurrent` | Get the authenticated user | +| `ListFollowers` | List followers of a user | +| `ListFollowing` | List users a user is following | +| `Follow` | Follow a user | +| `Unfollow` | Unfollow a user | +| `ListStarred` | List starred repositories | +| `Star` | Star a repository | +| `Unstar` | Unstar a repository | + +--- + +## TeamService + +Embeds `Resource[Team, CreateTeamOption, EditTeamOption]`. +Path: `/api/v1/teams/{id}` + +**Action methods:** + +| Method | Description | +|----------------|------------------------------------| +| `ListMembers` | List all team members | +| `AddMember` | Add a user to a team | +| `RemoveMember` | Remove a user from a team | +| `ListRepos` | List repositories managed by team | +| `AddRepo` | Add a repository to a team | +| `RemoveRepo` | Remove a repository from a team | +| `ListOrgTeams` | List all teams in an organisation | + +--- + +## AdminService + +No Resource embedding (heterogeneous endpoints). Requires site admin privileges. + +| Method | Description | +|-----------------------|------------------------------------------| +| `ListUsers` | List all users | +| `CreateUser` | Create a new user | +| `EditUser` | Edit an existing user | +| `DeleteUser` | Delete a user | +| `RenameUser` | Rename a user | +| `ListOrgs` | List all organisations | +| `RunCron` | Run a cron task by name | +| `ListCron` | List all cron tasks | +| `AdoptRepo` | Adopt an unadopted repository | +| `GenerateRunnerToken` | Generate an actions runner registration token | + +--- + +## BranchService + +Embeds `Resource[Branch, CreateBranchRepoOption, struct{}]`. +Path: `/api/v1/repos/{owner}/{repo}/branches/{branch}` + +**Action methods:** + +| Method | Description | +|--------------------------|--------------------------------------| +| `ListBranchProtections` | List all branch protection rules | +| `GetBranchProtection` | Get a single branch protection rule | +| `CreateBranchProtection` | Create a new branch protection rule | +| `EditBranchProtection` | Update a branch protection rule | +| `DeleteBranchProtection` | Delete a branch protection rule | + +--- + +## ReleaseService + +Embeds `Resource[Release, CreateReleaseOption, EditReleaseOption]`. +Path: `/api/v1/repos/{owner}/{repo}/releases/{id}` + +**Action methods:** + +| Method | Description | +|---------------|--------------------------------| +| `GetByTag` | Get a release by tag name | +| `DeleteByTag` | Delete a release by tag name | +| `ListAssets` | List assets for a release | +| `GetAsset` | Get a single release asset | +| `DeleteAsset` | Delete a release asset | + +--- + +## LabelService + +No Resource embedding (heterogeneous repo/org paths). + +| Method | Description | +|------------------|-----------------------------------| +| `ListRepoLabels` | List all labels in a repository | +| `GetRepoLabel` | Get a single repository label | +| `CreateRepoLabel`| Create a label in a repository | +| `EditRepoLabel` | Update a repository label | +| `DeleteRepoLabel`| Delete a repository label | +| `ListOrgLabels` | List all labels in an organisation| +| `CreateOrgLabel` | Create a label in an organisation | + +--- + +## WebhookService + +Embeds `Resource[Hook, CreateHookOption, EditHookOption]`. +Path: `/api/v1/repos/{owner}/{repo}/hooks/{id}` + +**Action methods:** + +| Method | Description | +|---------------|----------------------------------------| +| `TestHook` | Trigger a test delivery for a webhook | +| `ListOrgHooks`| List all webhooks for an organisation | + +--- + +## ContentService + +No Resource embedding (varied operations on file paths). + +| Method | Description | +|--------------|--------------------------------------------| +| `GetFile` | Get metadata and content for a file | +| `CreateFile` | Create a new file | +| `UpdateFile` | Update an existing file | +| `DeleteFile` | Delete a file (DELETE with body) | +| `GetRawFile` | Get raw file content as bytes | + +--- + +## ActionsService + +No Resource embedding (heterogeneous repo/org endpoints). + +| Method | Description | +|----------------------|------------------------------------------| +| `ListRepoSecrets` | List secrets for a repository | +| `CreateRepoSecret` | Create or update a repository secret | +| `DeleteRepoSecret` | Delete a repository secret | +| `ListRepoVariables` | List action variables for a repository | +| `CreateRepoVariable` | Create an action variable | +| `DeleteRepoVariable` | Delete an action variable | +| `ListOrgSecrets` | List secrets for an organisation | +| `ListOrgVariables` | List action variables for an organisation| +| `DispatchWorkflow` | Trigger a workflow run | + +--- + +## NotificationService + +No Resource embedding. + +| Method | Description | +|-----------------|------------------------------------------| +| `List` | List all notifications | +| `ListRepo` | List notifications for a repository | +| `MarkRead` | Mark all notifications as read | +| `GetThread` | Get a single notification thread | +| `MarkThreadRead`| Mark a single thread as read | + +--- + +## PackageService + +No Resource embedding. + +| Method | Description | +|------------|------------------------------------------------| +| `List` | List all packages for an owner | +| `Get` | Get a package by owner, type, name, version | +| `Delete` | Delete a package | +| `ListFiles`| List files for a specific package version | + +--- + +## WikiService + +No Resource embedding. + +| Method | Description | +|--------------|------------------------------| +| `ListPages` | List all wiki page metadata | +| `GetPage` | Get a single wiki page | +| `CreatePage` | Create a new wiki page | +| `EditPage` | Update an existing page | +| `DeletePage` | Delete a wiki page | + +--- + +## MiscService + +No Resource embedding (read-only utility endpoints). + +| Method | Description | +|---------------------------|-------------------------------------| +| `RenderMarkdown` | Render markdown text to HTML | +| `ListLicenses` | List available licence templates | +| `GetLicense` | Get a single licence template | +| `ListGitignoreTemplates` | List gitignore template names | +| `GetGitignoreTemplate` | Get a single gitignore template | +| `GetNodeInfo` | Get Forgejo instance NodeInfo | +| `GetVersion` | Get server version | + +--- + +## CommitService + +No Resource embedding. + +| Method | Description | +|---------------------|----------------------------------------------| +| `GetCombinedStatus` | Get combined status for a ref | +| `ListStatuses` | List all commit statuses for a ref | +| `CreateStatus` | Create a commit status for a SHA | +| `GetNote` | Get the git note for a commit SHA |