docs: wiki documentation for go-forge
Seven pages covering Home, Architecture, Services, Code Generation, Configuration, Error Handling, and Development. Co-Authored-By: Virgil <virgil@lethean.io> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
33319590d4
commit
f5f796cd16
7 changed files with 943 additions and 0 deletions
122
docs/wiki/Architecture.md
Normal file
122
docs/wiki/Architecture.md
Normal file
|
|
@ -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 <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.
|
||||
125
docs/wiki/Code-Generation.md
Normal file
125
docs/wiki/Code-Generation.md
Normal file
|
|
@ -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` | `[]<item type>`|
|
||||
| `object` | `map[string]any`|
|
||||
| `$ref` | `*<TypeName>` |
|
||||
|
||||
### 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.
|
||||
88
docs/wiki/Configuration.md
Normal file
88
docs/wiki/Configuration.md
Normal file
|
|
@ -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)
|
||||
```
|
||||
123
docs/wiki/Development.md
Normal file
123
docs/wiki/Development.md
Normal file
|
|
@ -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
|
||||
```
|
||||
87
docs/wiki/Error-Handling.md
Normal file
87
docs/wiki/Error-Handling.md
Normal file
|
|
@ -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)
|
||||
}
|
||||
```
|
||||
69
docs/wiki/Home.md
Normal file
69
docs/wiki/Home.md
Normal file
|
|
@ -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
|
||||
329
docs/wiki/Services.md
Normal file
329
docs/wiki/Services.md
Normal file
|
|
@ -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 |
|
||||
Loading…
Add table
Reference in a new issue