{"id":51778335,"url":"https://github.com/bborbe/go-version-watcher","last_synced_at":"2026-07-20T08:32:15.270Z","repository":{"id":370490669,"uuid":"1295050234","full_name":"bborbe/go-version-watcher","owner":"bborbe","description":"Go version watcher — polls go.dev for new stable releases and emits one go-version-update task via the agent framework; image docker.io/bborbe/go-version-watcher.","archived":false,"fork":false,"pushed_at":"2026-07-09T19:05:46.000Z","size":100,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-07-20T08:32:14.229Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bborbe.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-07-09T10:15:47.000Z","updated_at":"2026-07-09T19:05:56.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/bborbe/go-version-watcher","commit_stats":null,"previous_names":["bborbe/go-version-watcher"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/bborbe/go-version-watcher","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bborbe%2Fgo-version-watcher","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bborbe%2Fgo-version-watcher/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bborbe%2Fgo-version-watcher/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bborbe%2Fgo-version-watcher/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bborbe","download_url":"https://codeload.github.com/bborbe/go-version-watcher/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bborbe%2Fgo-version-watcher/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35681006,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-07-20T02:08:10.276Z","status":"ssl_error","status_checked_at":"2026-07-20T02:08:09.736Z","response_time":111,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-20T08:32:14.541Z","updated_at":"2026-07-20T08:32:15.252Z","avatar_url":"https://github.com/bborbe.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# go-version-watcher\n\nPolls [`https://go.dev/dl/?mode=json`](https://go.dev/dl/?mode=json) on an interval, computes the maximum `stable:true` Go version, and publishes one deduplicated `CreateTaskCommand` to Kafka per new version so a downstream task system can act on it.\n\nThe emitted task title and body are plain, deployment-agnostic defaults out of the box; set `TASK_TITLE_TEMPLATE` / `TASK_BODY_TEMPLATE` (Go `text/template`) to inject deployment-specific content — e.g. a link to your own update runbook.\n\nThe watcher is the **producer** (Layer 1 detection) half of the pipeline. It never modifies any repo. It emits one global \"new Go released\" signal; deciding which repos need a bump is a future Layer 2 fan-out service.\n\n## How It Works\n\nOn each poll cycle:\n\n1. **Load cursor** (`/data/cursor.json`) — a single `last_seen_version` string.\n2. **Query go.dev** — parse the JSON release list, keep `stable:true` entries, and compute the **max** version by parsed `(major, minor, patch)` integers (`go1.26.10 \u003e go1.26.9`). On query/parse failure the cursor is held and the cycle records `go_dev_error`.\n3. **Cold start** (empty cursor): seed the cursor to the current max and emit nothing — avoids a spurious \"update\" task on first run.\n4. **New version** (`max \u003e cursor`): classify `release_kind` (`minor` if the major/minor differs, else `patch`), publish one `CreateTaskCommand`, and advance the cursor on publish success.\n5. **Unchanged** (`max \u003c= cursor`): record `version_unchanged`, emit nothing.\n\nDeterministic `UUID5(\"go-version:\" + version)` gives controller dedup — re-polling an already-seen version is a no-op.\n\n## Task Contract\n\nEvery emitted `CreateTaskCommand` carries this frontmatter shape:\n\n```yaml\ntask_type: go-version-update\nassignee: human\nphase: planning\nstatus: in_progress\nstage: dev|prod\ntask_identifier: \u003cUUID5(\"go-version:\" + new_version)\u003e\ntitle: Update Go to \u003cX.Y.Z\u003e\nnew_version: go1.27.0\nprevious_version: go1.26.5\nrelease_kind: minor        # minor | patch\nrelease_notes_url: https://go.dev/doc/devel/release#go1.27.0\n```\n\nBody is an operator-readable header only (title + release-notes URL + downloads URL). The built-in default is deployment-agnostic; override it per deployment with `TASK_BODY_TEMPLATE` to add e.g. a runbook link.\n\n## Environment Variables\n\n| Variable | Required | Default | Description |\n|---|---|---|---|\n| `STAGE` | yes | — | Deployment stage (`dev` or `prod`) |\n| `KAFKA_BROKERS` | yes | — | Comma-separated Kafka broker list |\n| `LISTEN` | no | `:9090` | HTTP listen address (`/healthz`, `/readiness`, `/metrics`) |\n| `POLL_INTERVAL` | no | `24h` | Poll interval (Go duration string) |\n| `CURSOR_PATH` | no | `/data/cursor.json` | Cursor persistence path (PVC mount) |\n| `TOPIC_PREFIX` | no | — | Kafka topic prefix for CQRS topic construction |\n| `SENTRY_DSN` | no | — | Sentry DSN for error tracking |\n| `SENTRY_PROXY` | no | — | HTTP proxy URL for Sentry transport |\n\n## HTTP Endpoints\n\n| Path | Method | Purpose |\n|---|---|---|\n| `/healthz` | GET | Liveness probe (always 200 OK) |\n| `/readiness` | GET | Readiness probe (always 200 OK) |\n| `/metrics` | GET | Prometheus metrics |\n\n## Metrics\n\n| Metric | Cardinality | Purpose |\n|---|---|---|\n| `go_version_watcher_poll_cycle_total{result}` | `result=success\\|go_dev_error` | Poll health |\n| `go_version_watcher_published_total{status}` | `status=create\\|error` | Task emission |\n| `go_version_watcher_filter_skipped_total{reason}` | `reason=version_unchanged` | Filter visibility |\n\n## Development\n\n```bash\nmake test          # run unit tests\nmake generate      # regenerate counterfeiter mocks\nmake precommit     # format + lint + test + security checks\n```\n\n## Smoke Test (`cmd/run-once`)\n\nSingle Poll cycle against real dev Kafka, then exits.\n\n```bash\ncd cmd/run-once\nmake run-once KAFKA_BROKERS=\u003cbrokers\u003e\n```\n\n## License\n\nBSD 2-Clause License. See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbborbe%2Fgo-version-watcher","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbborbe%2Fgo-version-watcher","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbborbe%2Fgo-version-watcher/lists"}