Help us shape the future of Go! Take the 2026 Go Developer Survey

forgerpc

package
v0.2.10 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 2, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package forgerpc implements a lightweight ConnectRPC client for the Gitea/Forgejo RunnerService protocol.

Both platforms share the same runner.v1.RunnerService API (Register, Declare, FetchTask) with minor response differences (Forgejo returns a runner UUID). This package uses raw HTTP + JSON encoding to avoid pulling in the full ConnectRPC and protobuf dependency trees.

Wire format: Connect protocol unary RPCs over HTTP/1.1 with JSON.

POST {instanceURL}/api/actions/runner.v1.RunnerService/{Method}
Content-Type: application/json
Connect-Protocol-Version: 1

Auth (post-registration):

x-runner-uuid: {uuid}
x-runner-token: {token}

Reference:

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DeclareLabels

func DeclareLabels(registerLabels []string) []string

DeclareLabels converts Register-format labels ("ubuntu-latest:docker://image") to Declare-format labels ("ubuntu-latest") by stripping the :type://image suffix.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client talks to a Gitea or Forgejo instance's RunnerService via ConnectRPC.

func NewClient

func NewClient(instanceURL string, httpClient *http.Client) *Client

NewClient creates a ConnectRPC client for the given forge instance URL. Pass nil for httpClient to use a default with 30s timeout.

func (*Client) Declare

func (c *Client) Declare(ctx context.Context, labels []string) error

Declare announces the runner's labels to the server. Call after Register to tell the forge which job labels this runner handles.

Labels should be name-only strings (e.g. "ubuntu-latest"), NOT the full docker-mapped format used in Register ("ubuntu-latest:docker://image"). Forgejo matches runs-on: values against Declare labels, so "ubuntu-latest" must match exactly. Use DeclareLabels to convert from Register format.

func (*Client) FetchTask

func (c *Client) FetchTask(ctx context.Context, tasksVersion int64) (*FetchTaskResult, error)

FetchTask polls for an available task. Returns a nil Task if none available. The server may long-poll (~5s) before returning an empty response.

Pass tasksVersion from the previous response for efficient change detection; use 0 for the first call.

func (*Client) Register

func (c *Client) Register(ctx context.Context, name, regToken, version string, labels []string) (*Runner, error)

Register exchanges a registration token for persistent runner credentials. On success the client stores the credentials for subsequent authenticated calls.

Labels are plain strings (e.g. "ubuntu-latest:docker://node:20-bookworm"). The proto field is repeated string, not repeated AgentLabel — use Declare for the structured label announcement after registration.

func (*Client) RegistrationToken

func (c *Client) RegistrationToken(ctx context.Context, apiToken, owner, repo string) (string, error)

RegistrationToken fetches a runner registration token from the forge REST API.

Scope is determined by owner/repo:

  • owner="" repo="" → instance-level (admin)
  • owner="org" repo="" → org-level
  • owner="org" repo="repo" → repo-level

The HTTP method for this endpoint diverged across Gitea versions:

Gitea <1.22:  endpoint does not exist (runners registered via CLI/admin UI)
Gitea 1.22–1.23: GET only (POST returns 405)
Gitea 1.24–1.25: both GET and POST
Gitea 1.26+:     POST only (GET returns 404)
Forgejo (all):   GET

This method tries POST first then falls back to GET, covering all versions.

func (*Client) SetAuth

func (c *Client) SetAuth(uuid, token string)

SetAuth sets the runner credentials for authenticated RPCs. Called automatically by Register; only needed when restoring saved credentials.

func (*Client) Token

func (c *Client) Token() string

Token returns the runner token set after registration.

func (*Client) UUID

func (c *Client) UUID() string

UUID returns the runner UUID set after registration.

func (*Client) UpdateLog

func (c *Client) UpdateLog(ctx context.Context, taskID int64, index int64, rows []LogRow, noMore bool) (int64, error)

UpdateLog streams log lines to the forge. Returns the server's acknowledged line index. Call with noMore=true after the final flush.

func (*Client) UpdateTask

func (c *Client) UpdateTask(ctx context.Context, state *TaskState, outputs map[string]string) (*UpdateTaskResponse, error)

UpdateTask reports the current state of a task (step results, completion).

func (*Client) Version

func (c *Client) Version(ctx context.Context) (string, error)

Version returns the forge instance version string (e.g. "1.26.0" for Gitea, "9.0.3" for Forgejo).

type FetchTaskResult

type FetchTaskResult struct {
	Task         *Task // nil if no task available
	TasksVersion int64 // pass back to next FetchTask for change detection
}

FetchTaskResult is the response from FetchTask.

func (*FetchTaskResult) UnmarshalJSON

func (r *FetchTaskResult) UnmarshalJSON(data []byte) error

type LogRow

type LogRow struct {
	Time    time.Time `json:"time"`
	Content string    `json:"content"`
}

LogRow is a single timestamped log line.

type RPCError

type RPCError struct {
	HTTPStatus int
	Code       string // ConnectRPC error code (e.g. "unauthenticated", "not_found")
	Message    string
}

RPCError is returned when the ConnectRPC endpoint returns a non-200 status.

func (*RPCError) Error

func (e *RPCError) Error() string

type Runner

type Runner struct {
	ID    int64
	UUID  string
	Name  string
	Token string
}

Runner holds runner info returned by Register.

func (*Runner) UnmarshalJSON

func (r *Runner) UnmarshalJSON(data []byte) error

type StepState

type StepState struct {
	Step      int64      `json:"step"`
	Result    TaskResult `json:"result"`
	StartedAt *time.Time `json:"startedAt,omitempty"`
	StoppedAt *time.Time `json:"stoppedAt,omitempty"`
	LogIndex  int64      `json:"logIndex"`
	LogLength int64      `json:"logLength"`
}

StepState reports the outcome of a single workflow step.

type Task

type Task struct {
	ID              int64
	UUID            string          // Forgejo returns this; Gitea may leave empty
	WorkflowPayload string          // base64-encoded workflow YAML
	Context         json.RawMessage // google.protobuf.Struct — repo/ref/secrets context
}

Task is a CI task returned by FetchTask.

func (*Task) ContainerImage

func (t *Task) ContainerImage() string

ContainerImage parses the workflow YAML and returns the first job's `container.image` value, if set. Returns "" if not found.

func (*Task) Repo

func (t *Task) Repo() string

Repo extracts the repository full name (e.g. "owner/repo") from the task context. Gitea/Forgejo nest repo info under a "github" key for Actions compatibility.

func (*Task) UnmarshalJSON

func (t *Task) UnmarshalJSON(data []byte) error

func (*Task) WorkflowYAML

func (t *Task) WorkflowYAML() ([]byte, error)

WorkflowYAML decodes the base64 workflow payload to raw YAML bytes.

type TaskResult

type TaskResult int

TaskResult represents the outcome of a task or step.

const (
	ResultUnspecified TaskResult = 0
	ResultSuccess     TaskResult = 1
	ResultFailure     TaskResult = 2
	ResultCancelled   TaskResult = 3
	ResultSkipped     TaskResult = 4
)

type TaskState

type TaskState struct {
	ID        int64       `json:"id"`
	Result    TaskResult  `json:"result"`
	StartedAt *time.Time  `json:"startedAt,omitempty"`
	StoppedAt *time.Time  `json:"stoppedAt,omitempty"`
	Steps     []StepState `json:"steps,omitempty"`
}

TaskState reports the current state of a task.

type UpdateTaskResponse

type UpdateTaskResponse struct {
	State       *TaskState `json:"state,omitempty"`
	SentOutputs []string   `json:"sentOutputs,omitempty"`
}

UpdateTaskResponse is the response from UpdateTask.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL