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 ImageChecker
- 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 DefaultDockerHubRegistryURL = "https://registry-1.docker.io/v2/library/golang"
DefaultDockerHubRegistryURL is the Docker Hub registry v2 API base for the golang library image. Manifest path is <registry>/manifests/<tag>.
const DefaultDockerHubTokenURL = "https://auth.docker.io/token" // #nosec G101 -- public Docker Hub auth endpoint, not a credential
DefaultDockerHubTokenURL is Docker Hub's token endpoint used to obtain a registry pull token before manifest requests.
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 ImageChecker ¶ added in v0.6.0
type ImageChecker interface {
// ImageExists reports whether docker.io/library/golang:<version.Number()>
// exists. Returns (false, nil) when the manifest is not yet published (404),
// and an error on transport / auth / unexpected-status failures.
ImageExists(ctx context.Context, version Version) (bool, error)
}
ImageChecker reports whether a golang docker image exists on Docker Hub. Consulted by the watcher before emitting a task for a new Go version: go.dev lists a release before docker.io publishes its image (12h–1day lag), so the gate holds the cursor until the image actually exists (see the goal's architecture decision on the Docker-image availability gate).
func NewImageChecker ¶ added in v0.6.0
func NewImageChecker(httpClient *http.Client, tokenURL, registryURL string) ImageChecker
NewImageChecker returns the production ImageChecker backed by the given HTTP client and Docker Hub endpoints (typically DefaultDockerHubTokenURL + DefaultDockerHubRegistryURL).
type Metrics ¶
type Metrics interface {
// IncPollCycle — result: "success" | "go_dev_error" | "image_check_error" | "build_error"
IncPollCycle(result string)
// IncPublished — status: "create" | "error"
IncPublished(status string)
// IncFilterSkipped — reason: "version_unchanged" | "image_not_ready"
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 ⇒ tasktemplate.DefaultTitle.
TitleTemplate *template.Template
// BodyTemplate overrides the emitted-task body; nil ⇒ tasktemplate.DefaultBody.
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, imageChecker ImageChecker, 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.
imageChecker gates task emission on docker.io actually publishing the golang image for the new version (go.dev releases 12h–1day before the image exists).
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package factory wires concrete dependencies for the go-version-watcher binary.
|
Package factory wires concrete dependencies for the go-version-watcher binary. |
|
Package tasktemplate holds the built-in default title/body templates for the emitted go-version task, authored as embedded markdown files.
|
Package tasktemplate holds the built-in default title/body templates for the emitted go-version task, authored as embedded markdown files. |