Documentation
¶
Overview ¶
Package pkg provides the core domain types and logic for the go-version-watcher service:
- Version — parsed Go release version with (major, minor, patch) comparison
- GoDevClient — queries https://go.dev/dl/?mode=json for the max stable version
- Cursor — single LastSeenVersion dedup state persisted to disk
- TaskPublisher — sends the CreateTaskCommand for the Go update runbook
- Watcher — the Poll loop tying it all together
See [[Go Version Watcher]] for the design, [[Watcher Writing Guide]] for the producer-side contract and [[Agent Task File Contract]] for the frontmatter/body shape this watcher emits.
Index ¶
- Constants
- func BuildCreateCommand(ctx context.Context, newVersion string, previousVersion string, ...) (task.CreateCommand, error)
- func DeriveTaskID(version string) uuid.UUID
- func NewResetCursorHandler(cursorPath string) http.Handler
- func NewSetCursorHandler(cursorPath string) http.Handler
- func NewTriggerHandler(watcher Watcher) http.Handler
- func ParseTaskTemplate(ctx context.Context, name, text string) (*template.Template, error)
- func SaveCursor(ctx context.Context, path string, c *Cursor) error
- type Cursor
- type GoDevClient
- type Metrics
- type TaskConfig
- type TaskPublisher
- type Version
- type Watcher
Constants ¶
const DefaultCursorPath = "/data/cursor.json"
DefaultCursorPath is the default cursor persistence location. k8s mounts /data as a PVC; main.go binds CURSOR_PATH=DefaultCursorPath.
const DefaultGoDevURL = "https://go.dev/dl/?mode=json"
DefaultGoDevURL is the go.dev release-list endpoint returning the JSON array of releases (including unstable ones — the client filters to stable).
Variables ¶
This section is empty.
Functions ¶
func BuildCreateCommand ¶
func BuildCreateCommand( ctx context.Context, newVersion string, previousVersion string, releaseKind string, cfg TaskConfig, ) (task.CreateCommand, error)
BuildCreateCommand assembles the CreateTaskCommand for a new Go version. newVersion and previousVersion are canonical go-version strings (e.g. "go1.27.0"); releaseKind is "minor" or "patch". The title and body are rendered from cfg.TitleTemplate / cfg.BodyTemplate (or the package defaults when nil); any template-execution failure is returned as a wrapped error.
func DeriveTaskID ¶
DeriveTaskID returns a UUID5 derived deterministically from the Go version string (e.g. "go1.27.0").
Uniqueness set rationale (per [[Watcher Writing Guide]] § Deterministic task_identifier):
- Same version → same task_id → controller dedup makes re-emit a no-op.
- A newer version → new name → new task_id → fresh task.
func NewResetCursorHandler ¶ added in v0.3.0
NewResetCursorHandler returns an HTTP handler that deletes the cursor file at cursorPath, so the next poll cold-starts (re-seeds). A missing file is treated as success (already reset).
Wrap with libhttp.NewDangerousHandlerWrapper at the call site to require a passphrase — the bare handler does not enforce auth.
func NewSetCursorHandler ¶ added in v0.3.0
NewSetCursorHandler returns an HTTP handler that validates the {version} URL variable as a Go version (e.g. go1.26.5) and writes it as the cursor's LastSeenVersion. Setting it to a version lower than the current latest makes the next poll emit a task; setting it to the current latest suppresses emit.
Wrap with libhttp.NewDangerousHandlerWrapper at the call site to require a passphrase — the bare handler does not enforce auth.
Route: /setcursor/{version}. Invalid version → 400.
func NewTriggerHandler ¶ added in v0.3.0
NewTriggerHandler returns an HTTP handler that invokes the watcher's Poll once immediately, so an operator can force a poll cycle without waiting for the interval tick (e.g. for live end-to-end testing after a cursor reset/set).
func ParseTaskTemplate ¶ added in v0.5.0
ParseTaskTemplate parses text as a named Go text/template and validates it by rendering against sample data, so a template referencing an unknown field fails fast at startup rather than silently skipping every emit. Empty text returns (nil, nil) ⇒ the built-in default is used.
Types ¶
type Cursor ¶
type Cursor struct {
LastSeenVersion string `json:"last_seen_version"`
}
Cursor is the single-value dedup state: the last Go version the watcher has seen and acted on. Empty LastSeenVersion means cold start (no prior run).
Concurrency: not safe for concurrent use. The Watcher loads at poll start and saves at poll end (single goroutine).
type GoDevClient ¶
type GoDevClient interface {
// LatestStable returns the maximum stable Go version reported by go.dev.
// It returns an error when the request fails, the response is malformed, or
// no stable, parseable version is present.
LatestStable(ctx context.Context) (Version, error)
}
GoDevClient is the upstream-source surface for the go-version watcher.
func NewGoDevClient ¶
func NewGoDevClient(httpClient *http.Client, url string) GoDevClient
NewGoDevClient returns the production GoDevClient backed by the given HTTP client and URL (typically DefaultGoDevURL).
type Metrics ¶
type Metrics interface {
// IncPollCycle — result: "success" | "go_dev_error" | "build_error"
IncPollCycle(result string)
// IncPublished — status: "create" | "error"
IncPublished(status string)
// IncFilterSkipped — reason: "version_unchanged"
IncFilterSkipped(reason string)
}
Metrics is the observable counter surface required by [[Watcher Writing Guide]] § Required observability.
func NewMetrics ¶
func NewMetrics(registerer prometheus.Registerer) Metrics
NewMetrics returns the Prometheus-backed Metrics implementation registered against the supplied Registerer. Pass nil for the default registry. Pre-initialises every label combination so Prometheus exposes a zero series before the first event fires.
type TaskConfig ¶
type TaskConfig struct {
Stage string // "dev" or "prod" — frontmatter `stage`
Assignee string // frontmatter `assignee` (default "human")
Status string // frontmatter `status` (default "in_progress")
Phase string // frontmatter `phase` (default "todo")
Suffix string // optional title/filename suffix appended as " - <suffix>"; empty = none
// TitleTemplate overrides the emitted-task title; nil ⇒ defaultTitleTemplate.
TitleTemplate *template.Template
// BodyTemplate overrides the emitted-task body; nil ⇒ defaultBodyTemplate.
BodyTemplate *template.Template
}
TaskConfig groups per-task envelope settings (stage routing + emitted-task frontmatter knobs).
type TaskPublisher ¶
type TaskPublisher interface {
PublishCreate(ctx context.Context, cmd task.CreateCommand) bool
}
TaskPublisher sends a pre-built CreateTaskCommand via the supplied CreateCommandSender. Returns true on successful send, false on error.
func NewTaskPublisher ¶
func NewTaskPublisher(sender task.CreateCommandSender, metrics Metrics) TaskPublisher
NewTaskPublisher returns a TaskPublisher that wraps the given sender + metrics.
type Version ¶
Version is a parsed Go release version. Patch defaults to 0 when the source string omits it (e.g. "go1.27" → patch 0). Raw preserves the original string.
func ParseVersion ¶
ParseVersion parses a Go release version string of the form go<major>.<minor>[.<patch>]. A missing patch component defaults to 0. Returns an error if the string does not match the expected shape.
func (Version) Compare ¶
Compare orders two versions by (major, minor, patch). It returns a negative number when v < other, zero when equal, and a positive number when v > other.
type Watcher ¶
type Watcher interface {
// Poll runs one scan cycle. Safe to call repeatedly on an interval.
Poll(ctx context.Context) error
}
Watcher polls go.dev for the max stable Go version and publishes a CreateTaskCommand when it advances beyond the cursor.
func NewWatcher ¶
func NewWatcher( client GoDevClient, publisher TaskPublisher, metrics Metrics, cursorPath string, cfg TaskConfig, seedVersion string, ) Watcher
NewWatcher wires the watcher's collaborators.
seedVersion, when non-empty, is the Go version the cursor is seeded with on cold start instead of the current latest, so the first poll can emit a task for the current latest. Empty means seed to latest and emit nothing.