feat(updates): add release discovery engine - #841
Conversation
🔐 Codex Security Review
Review SummaryOverall Risk: NONE FindingsNo security, correctness, or reliability findings were identified in the scoped diff. NotesReviewed only Generated by Codex Security Review | |
2cbc5a3 to
c3d2063
Compare
There was a problem hiding this comment.
Pull request overview
Adds a new server/internal/domain/updates package that periodically discovers the newest canonical stable and RC releases from the GitHub Releases API, with bounded streaming JSON decoding and strict tag/url validation. This lays the groundwork for later update-status RPCs and operator UX (stacked PRs), without wiring this engine into fleetd yet.
Changes:
- Introduce a bounded GitHub Releases client with ETag caching, pagination caps, and streaming decode guards.
- Add a checker lifecycle job that selects latest stable + latest RC via strict tag grammar and semver ordering, and publishes an immutable snapshot.
- Add config validation for the Releases API URL and an allowlisted download origin, plus fixtures and extensive unit tests.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| server/internal/domain/updates/github.go | Streaming, bounded GitHub Releases API client with ETag caching and strict decode limits |
| server/internal/domain/updates/checker.go | Release selection logic (stable + RC), snapshot caching, lifecycle + jittered polling |
| server/internal/domain/updates/config.go | Checker configuration and validation (HTTPS API URL + allowlisted download base) |
| server/internal/domain/updates/config_test.go | Config validation tests, including hostile download-base inputs |
| server/internal/domain/updates/checker_test.go | End-to-end unit tests for paging, ETag, malformed/oversized payloads, selection invariants, and lifecycle |
| server/internal/domain/updates/testdata/releases.json | Mixed release list fixture (nightlies, PR builds, drafts, RCs, stable) |
| server/internal/domain/updates/testdata/releases_rc_order.json | RC ordering fixture to ensure numeric semver prerelease comparison |
| server/internal/domain/updates/testdata/releases_nightlies.json | Fixture ensuring RC discovery behind many nightly entries |
| server/internal/domain/updates/testdata/releases_malformed_entry.json | Fixture validating malformed entries are skipped without failing the cycle |
| server/internal/domain/updates/testdata/latest_stable.json | /releases/latest stable fixture |
| server/internal/domain/updates/testdata/latest_misflagged_rc.json | Fixture ensuring prerelease-tag grammar blocks “stable” offer even if misflagged |
| server/internal/domain/updates/testdata/latest_invalid_tag.json | Fixture ensuring invalid/non-semver latest tags are rejected |
| server/go.mod | Promote semver dependency (golang.org/x/mod) (and otel/trace) to direct requirements |
| docs/plans/2026-07-27-001-feat-release-update-notifications-plan.md | Phase plan documenting constraints, sequencing, and security boundaries |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c3d2063451
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
c3d2063 to
8b608b3
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8b608b3786
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
8b608b3 to
4b10f2e
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4b10f2e7bc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
e3bb148 to
f9ef184
Compare
563e62c to
642aeff
Compare
642aeff to
ef0efbf
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ef0efbf8e5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
ef0efbf to
66db0f5
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 66db0f5577
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2896f97f82
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
2896f97 to
dc9f7ea
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dc9f7ea4cf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 04031bed7e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Reviewable diff: +1537/-2 across 13 files (excludes generated, test, and story files).
Summary
Adds a dormant GitHub release-discovery engine that identifies the newest canonical stable and release-candidate builds. Production discovery is fixed to the Proto Fleet repository, runs at most hourly with subtract-only jitter, and exposes no operator experience until downstream PRs wire it into fleetd.
Stack: #841 → #842 → #843 → #844 → #845 → #835 → #836 → #837 → #838 → #839 → #840. This is 1/5 of phase one, relative to
main. Contract/storage, backend delivery, client surfaces, and host execution remain downstream.How it works
Each cycle makes two baseline requests to the fixed
/releases/latestendpoint and page 1 of/releases?per_page=100&page=1. Responses are capped at 8 MiB and the list is stream-decoded, rejecting the 101st entry. Exact tag grammar and semver validation select the maximum stable and RC candidates; prerelease metadata and release-note links are derived from that classification and the fixed repository rather than upstream body fields.Baseline responses are cached by ETag and reused on
304; an optional GitHub token makes unchanged authenticated polls quota-free. A reported rate limit suppresses requests until reset. If a higher cached candidate ages out of the bounded responses, the checker conditionally revalidates it through/releases/tags/{tag}before keeping it eligible. A 404 or channel reclassification drops it; a transient, malformed, or mismatched response retains the data for retry but marks only that channel unavailable, with one warning after three consecutive failures.Stable and RC availability are exposed through invariant-preserving accessors, allowing a fresh RC-only snapshot to remain useful to Stable+RC operators. Startup checks immediately, then repeats every configured hour minus 10–20% jitter (48–54 minutes). Each polling iteration contains panics so a defect in this best-effort feature cannot crash fleetd, and the next scheduled cycle still runs.
Areas of the code involved
server/internal/domain/updates/github.goserver/internal/domain/updates/checker.goserver/internal/domain/updates/config.goserver/internal/domain/updates/*_test.go,testdata/plugin/asicrs/Dockerfile.builddocs/plans/...release-update-notifications-plan.mdKey technical decisions & trade-offs
cargo clean --release -p asicrs-pluginafter dependency warmup so Cargo owns package invalidation while retaining the warmed dependency graph.Testing & validation
go test -race ./internal/domain/updates -count=1golangci-lint run ./internal/domain/updates304reuse, stable/RC preservation, rate-limit reset suppression, and independent channel degradation.