forgerpc

package
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Jul 15, 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