Documentation
¶
Index ¶
- Constants
- func AddOrg(path string, o Org) error
- func AddRepo(path string, r RepoTarget) error
- func CheckBaseURL(base string) error
- func CredentialsPath(agentConfigPath string) string
- func WriteCredentials(path string, c Credentials) error
- type Agent
- type Aging
- type Controller
- type Credentials
- type DriverConfig
- type DriverType
- type Org
- type Policy
- type PolicyType
- type RepoTarget
- type ScaleSet
- type SourceConfig
- type SourceType
- type WebhookConfig
Constants ¶
const DefaultRunnerGroupID int64 = 1
DefaultRunnerGroupID is GitHub's "Default" runner group; used when Org.RunnerGroupID is unset (zero).
const SchemaVersion = "1"
SchemaVersion is the only config schema accepted by this binary. Bump when making backwards-incompatible config changes.
Variables ¶
This section is empty.
Functions ¶
func AddRepo ¶
func AddRepo(path string, r RepoTarget) error
AddRepo appends r to the repos list in the agent config at path.
func CheckBaseURL ¶ added in v0.2.11
CheckBaseURL accepts an empty base URL or the root of a GHES instance. The API and App URLs are derived from it, so it must carry no path, and github.com is spelled by leaving it empty.
func CredentialsPath ¶
CredentialsPath is credentials.yaml in the same directory as the agent config.
func WriteCredentials ¶
func WriteCredentials(path string, c Credentials) error
WriteCredentials writes c to path with 0600 perms (it holds a bearer token).
Types ¶
type Agent ¶
type Agent struct {
Version string `yaml:"version"`
ControllerURL string `yaml:"controller_url"`
Token string `yaml:"token"`
Name string `yaml:"name"`
Labels []string `yaml:"labels"`
Driver DriverConfig `yaml:"driver"`
// The agent holds the GitHub App keys, polls GitHub, and mints JIT configs,
// so the orgs it serves and its poll source are configured here.
Orgs []Org `yaml:"orgs"`
// Repos serves individual repositories (personal-account / per-repo runners).
Repos []RepoTarget `yaml:"repos"`
Source SourceConfig `yaml:"source"`
// MetricsListen serves Prometheus metrics at /metrics on this address, with
// no auth. Empty (the default) serves nothing.
MetricsListen string `yaml:"metrics_listen"`
}
type Controller ¶
type Controller struct {
Version string `yaml:"version"`
Listen string `yaml:"listen"`
Token string `yaml:"token"`
// Orgs is read only by the local `ushr cost` report, which queries GitHub
// directly; the keyless control plane schedules on agent-reported metadata
// and needs no org config of its own.
Orgs []Org `yaml:"orgs"`
Policy Policy `yaml:"policy"`
Webhook WebhookConfig `yaml:"webhook"`
// DispatchDSN, when set, backs the dispatch ledger with Postgres (Neon) for
// the hosted control plane instead of the local JSONL WAL. Empty = JSONL.
DispatchDSN string `yaml:"dispatch_dsn"`
// DispatchLedgerPath overrides where the controller persists in-flight
// dispatch state across restarts. Empty means dispatch.DefaultPath().
DispatchLedgerPath string `yaml:"dispatch_ledger_path"`
}
func LoadController ¶
func LoadController(path string) (*Controller, error)
type Credentials ¶
type Credentials struct {
ControllerURL string `yaml:"controller_url"`
Token string `yaml:"token"`
Name string `yaml:"name"`
Orgs []string `yaml:"orgs"`
}
Credentials are the login-managed connection secrets, written by `ushr login` to credentials.yaml alongside the agent config and overlaid by LoadAgent.
type DriverConfig ¶
type DriverConfig struct {
Type DriverType `yaml:"type"`
Image string `yaml:"image"`
Capacity int `yaml:"capacity"`
// Runtime pins the container CLI for the docker driver ("docker" or
// "podman"). Empty autodetects (docker, then podman). Ignored by other drivers.
Runtime string `yaml:"runtime"`
// MinFreeGB is the free-space floor on the driver's image store (tart's
// home, the container data root). Under it the agent stops accepting
// dispatches, so the work stays schedulable on a host that can run it rather
// than failing on a full disk here. Zero takes the driver's default;
// negative disables the gate but leaves reclaim working to the same mark.
MinFreeGB int `yaml:"min_free_gb"`
// ExclusiveDaemon declares that nothing but ushr's runners uses this host's
// container daemon. Jobs mount the host docker socket, so what they leave
// behind carries none of our labels and can only be collected by pruning
// unowned objects — safe exactly when there is no other user. Docker driver
// only; off by default.
ExclusiveDaemon bool `yaml:"exclusive_daemon"`
// Docker-driver-specific, ignored by other drivers. The load-bearing ones:
// Volumes mounts the host docker socket for buildx, GroupAdd carries the
// host docker GID so the runner user can use it, and Memory (e.g. "4g")
// caps each runner container so a heavy build OOM-kills its own job rather
// than the host.
Network string `yaml:"network"`
Volumes []string `yaml:"volumes"`
ExtraHosts []string `yaml:"extra_hosts"`
GroupAdd []string `yaml:"group_add"`
Memory string `yaml:"memory"`
Entrypoint string `yaml:"entrypoint"`
// BuildCache gives each repo its own persistent buildx builder, so image
// layers and RUN --mount=type=cache survive between that repo's jobs in a
// cache store separate from other repos'. This is cache isolation, not a
// trust boundary (jobs share the host docker socket), and within a repo a
// fork-PR build shares the warm cache with protected builds — keep the usual
// "no untrusted PRs on self-hosted runners" rule. Docker driver only, on by
// default (unset = enabled; needs the host buildx plugin, and each dispatch
// degrades to uncached when buildx is unavailable). BuildkitImage overrides
// the buildkit image; BuildCacheGB caps each repo's cache before a prune
// (default 20). StateDir holds the per-repo buildx configs (default
// ~/.local/share/ushr).
BuildCache *bool `yaml:"build_cache"`
BuildkitImage string `yaml:"buildkit_image"`
BuildCacheGB int `yaml:"build_cache_gb"`
BuildCacheIdleHrs int `yaml:"build_cache_idle_hours"` // reap a repo's builder after this idle (default 168 = 7d)
StateDir string `yaml:"state_dir"`
}
type DriverType ¶
type DriverType string
const ( DriverTypeTart DriverType = "tart" DriverTypeLume DriverType = "lume" DriverTypeDocker DriverType = "docker" DriverTypeK8s DriverType = "k8s" )
type Org ¶
type Org struct {
Name string `yaml:"name"`
AppID int64 `yaml:"app_id"`
PrivateKeyPath string `yaml:"private_key_path"`
Priority int `yaml:"priority,omitempty"`
// BaseURL is the GitHub Enterprise Server root (e.g.
// https://ghe.example.com). Empty means github.com.
BaseURL string `yaml:"base_url,omitempty"`
// RunnerGroupID is the GitHub runner group to register JIT runners into.
// Falls back to DefaultRunnerGroupID if zero.
RunnerGroupID int64 `yaml:"runner_group_id,omitempty"`
// Repos optionally restricts polling to these repo names within the org.
// Empty means poll every repo the App installation can access — which blows
// the API rate limit on a large installation. Set it to the handful that
// actually use the runner; this also skips the list-installation-repos call.
// Poll source only.
Repos []string `yaml:"repos,omitempty"`
// ScaleSets lists the runner scale sets to serve in this org. Scaleset
// source only; repo scoping is enforced GitHub-side by the runner group.
ScaleSets []ScaleSet `yaml:"scale_sets,omitempty"`
}
type Policy ¶
type Policy struct {
Type PolicyType `yaml:"type"`
Aging Aging `yaml:"aging"`
// Priorities sets the priority per scope (an org login or "owner/repo").
// A listed scope overrides the priority its agents report; an unlisted
// scope keeps the agent-reported value.
Priorities map[string]int `yaml:"priorities"`
}
type RepoTarget ¶
type RepoTarget struct {
Owner string `yaml:"owner"`
Repo string `yaml:"repo"`
AppID int64 `yaml:"app_id"`
PrivateKeyPath string `yaml:"private_key_path"`
Priority int `yaml:"priority,omitempty"`
// BaseURL is the GitHub Enterprise Server root; empty means github.com.
BaseURL string `yaml:"base_url,omitempty"`
// ScaleSets lists the repo-level runner scale sets to serve. Scaleset
// source only; a repo scale set serves only this repo.
ScaleSets []ScaleSet `yaml:"scale_sets,omitempty"`
}
RepoTarget serves a single repository's runners (the personal-account path: user accounts have no org-level runner pool, so runners are per-repo). The App must have repository "Administration: write" on the repo.
func (RepoTarget) Scope ¶
func (r RepoTarget) Scope() string
Scope is the tenant scope ("owner/repo") this target serves; matches the enrollment-token grant and domain.Job.Org.
type ScaleSet ¶
type ScaleSet struct {
Name string `yaml:"name"`
// MaxRunners caps outstanding runners for this set. Size it to the
// capacity of the agent(s) labelled to serve it: it is what the controller
// reports to GitHub as the set's max capacity when polling for messages.
MaxRunners int `yaml:"max_runners"`
}
ScaleSet is one runner scale set. Its name doubles as the runs-on label workflows target.
type SourceConfig ¶
type SourceConfig struct {
Type SourceType `yaml:"type"`
Interval time.Duration `yaml:"interval"`
}
type SourceType ¶
type SourceType string
const ( SourceTypePoll SourceType = "poll" SourceTypeScaleSet SourceType = "scaleset" )
type WebhookConfig ¶
WebhookConfig enables the workflow_job receiver that feeds the job ledger. Empty Secret disables it. LedgerPath defaults to ledger.DefaultPath().