Documentation
¶
Index ¶
- type AppAuth
- type Client
- func (c *Client) CleanStaleWebhooks(ctx context.Context)
- func (c *Client) DeregisterWebhooks(ctx context.Context, hooks []ManagedWebhook)
- func (c *Client) FetchJobImage(ctx context.Context, repo string, runID int64, jobID int64) string
- func (c *Client) IsOrgLevel() bool
- func (c *Client) Owner() string
- func (c *Client) PingWebhook(ctx context.Context, m ManagedWebhook) error
- func (c *Client) PollJobs(ctx context.Context) ([]JobEvent, error)
- func (c *Client) RateSnapshot() (remaining, limit int64, reset, updated time.Time)
- func (c *Client) RegisterJITRunner(ctx context.Context, repo string, name string, labels []string) (*gh.JITRunnerConfig, error)
- func (c *Client) RegisterWebhooks(ctx context.Context, webhookURL, secret string) ([]ManagedWebhook, error)
- func (c *Client) RemoveRunner(ctx context.Context, repo string, runnerID int64) error
- func (c *Client) RunnerBusy(ctx context.Context, repo string, runnerID int64) (bool, error)
- func (c *Client) SetHTTPClient(ghClient *gh.Client)
- func (c *Client) WebhookHandler(secret string) (http.Handler, <-chan JobEvent)
- type Config
- type JobEvent
- type ManagedWebhook
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AppAuth ¶
type AppAuth struct {
// contains filtered or unexported fields
}
AppAuth manages GitHub App installation tokens with automatic refresh. Installation tokens expire after 1 hour; this refreshes at 45 minutes.
func NewAppAuth ¶
NewAppAuth loads the PEM key and returns an AppAuth that mints and refreshes GitHub App installation tokens.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client handles GitHub API interactions and webhook events.
func New ¶
New creates a GitHub client. Uses AppAuth for dynamic token refresh when configured, otherwise a static PAT.
All GitHub API responses flow through rateTrackingTransport, which parses the X-RateLimit-* headers and updates the ephemerd_github_api_rate_* gauges. Operators use ephemerd_github_api_rate_updated_seconds to tell a current-but-exhausted budget from a stale reading.
func (*Client) CleanStaleWebhooks ¶
CleanStaleWebhooks removes any workflow_job webhooks left behind by previous ephemerd instances that crashed or were killed without cleanup. Called on startup before registering new webhooks to avoid hitting GitHub's 20-hook limit.
Skipped entirely in pool mode: this sweep deletes every workflow_job hook it can see, and in a pooled fleet those are live hooks belonging to pool-mates (or other pools). Adoption in RegisterWebhooks makes the sweep unnecessary for pooled nodes — same-URL hooks are reused, not duplicated.
func (*Client) DeregisterWebhooks ¶
func (c *Client) DeregisterWebhooks(ctx context.Context, hooks []ManagedWebhook)
DeregisterWebhooks removes all managed webhooks. Called on shutdown. No-op in pool mode: the hook is shared with pool-mates that are still serving; the pool's lifecycle owner (e.g. mayfly destroying the pool) removes it when the last member goes away.
func (*Client) FetchJobImage ¶
FetchJobImage fetches the workflow run's job definition and reads the container image declared in the job's `container:` field. This requires an extra API call per job but lets users specify the image directly in their workflow:
jobs:
build:
runs-on: [self-hosted, linux, x64]
container:
image: ghcr.io/myorg/custom-build:latest
quick:
runs-on: [self-hosted, linux, x64]
container: ghcr.io/myorg/quick:latest # shorthand
Returns empty string if no container image is set.
func (*Client) IsOrgLevel ¶
IsOrgLevel returns true when no repos are configured, meaning ephemerd registers runners at the organization level (available to all repos).
func (*Client) PingWebhook ¶
func (c *Client) PingWebhook(ctx context.Context, m ManagedWebhook) error
PingWebhook triggers a ping event for a managed webhook.
func (*Client) PollJobs ¶
PollJobs checks for queued workflow jobs targeting self-hosted runners. If repos are configured, polls those repos. Otherwise, polls all repos in the org.
func (*Client) RateSnapshot ¶
RateSnapshot returns the last-observed rate-limit state. The scheduler uses this to bias retry backoff — when remaining==0 and now < reset, the next attempt is scheduled just after reset instead of blindly falling through the exponential-backoff ladder. Fields are zero-valued when the client hasn't yet made a request or was created via the bare test constructor.
func (*Client) RegisterJITRunner ¶
func (c *Client) RegisterJITRunner(ctx context.Context, repo string, name string, labels []string) (*gh.JITRunnerConfig, error)
RegisterJITRunner creates a just-in-time runner. If repos are configured, registers at the repo level. If repos are empty, registers at the org level (available to all repos in the org).
func (*Client) RegisterWebhooks ¶
func (c *Client) RegisterWebhooks(ctx context.Context, webhookURL, secret string) ([]ManagedWebhook, error)
RegisterWebhooks creates workflow_job webhooks on each configured repo (or the org) pointing at the given URL with the given secret. Returns the managed hooks for cleanup.
It is idempotent: before creating a hook it lists the existing hooks and, if one already points at webhookURL, reuses it instead of creating a duplicate. GitHub rejects a duplicate hook config with HTTP 422, so without this the external-tunnel path (which re-registers on every startup) would fail on the second boot.
In pool mode the reuse is upgraded to adopt: the existing hook (registered by a pool-mate) is edited in place so secret/events/active converge on this member's config — pool members must present one shared secret. A create that still races a pool-mate to a 422 resolves by adopting the winner's hook.
func (*Client) RemoveRunner ¶
RemoveRunner removes a self-hosted runner by ID. Uses org-level or repo-level API depending on configuration.
func (*Client) RunnerBusy ¶ added in v0.2.9
RunnerBusy reports whether a self-hosted runner is currently executing a job, from GitHub's own per-runner `busy` flag.
Uses the org-level or repo-level API depending on configuration, the same way RemoveRunner does — a JIT runner registered at the org level is not addressable through the repo endpoint.
A runner that has already deregistered itself (404) is reported as not busy: an ephemeral runner removes itself after finishing its job, so "gone" is the strongest possible evidence that nothing is running on it. Every other error is returned to the caller, which must treat it as "could not determine" rather than as idle.
func (*Client) SetHTTPClient ¶
SetHTTPClient replaces the underlying go-github client. Used by test infrastructure to point at a fake server.
type Config ¶
type Config struct {
Token string
Owner string
Repos []string
Log *slog.Logger
// App auth (used instead of Token when set)
AppAuth *AppAuth
// PoolMode changes webhook lifecycle semantics for nodes sharing one
// public webhook URL: registration adopts an existing same-URL hook,
// deregistration is a no-op, and the stale-hook sweep is skipped.
PoolMode bool
// AllowedRepos is an OPTIONAL dispatch allowlist. When non-empty, only jobs
// whose repository name is in this list may dispatch a runner. Empty (the
// default) preserves the historical behavior of dispatching for any tracked
// repo. Defense-in-depth beneath GitHub's outside-collaborator approval.
AllowedRepos []string
// RequiredLabels is an OPTIONAL dispatch allowlist. When non-empty, an
// incoming job must carry at least one of these labels (in addition to the
// mandatory self-hosted gate) before it dispatches. Empty imposes no extra
// label requirement.
RequiredLabels []string
}
Config for the GitHub client.
type JobEvent ¶
type JobEvent struct {
Action string
Repo string
Job *gh.WorkflowJob
}
JobEvent is emitted when a workflow_job webhook fires.
type ManagedWebhook ¶
ManagedWebhook tracks a webhook created by ephemerd so it can be cleaned up on shutdown.