GitHub Release Monitor
Build a Go service that keeps an eye on GitHub repositories you care about and tells you when they publish a new release. It checks each repository on a schedule through GitHub's REST API, stores every release it has seen in PostgreSQL, so it never reports the same release twice, and keeps a searchable history. Other programs can query the history and list new releases through a gRPC API. It's careful with GitHub's rate limits, using conditional requests with ETags, which don't count against the limit when nothing changed. You'll learn working with a real REST API, rate limits and conditional requests, database schemas and migrations, defining a gRPC service with Protocol Buffers, and scheduling background work in Go.
- Estimate
- ~13h
- Steps
- 7
- Completed by
- 0
- Proposed by
- codeseed.app
gRPC · PostgreSQL
Project roadmap
- 01
Set up the project and database
~1.5hRun go mod init and add pgx for PostgreSQL. Write SQL migrations for repositories (owner, name, last ETag, last checked time) and releases (repository, tag, name, published_at, URL, first seen time, with a unique constraint on repository plus tag). Read the database URL and an optional GitHub token from environment variables. Done when: the service starts, connects and runs the migrations on an empty database.
- 02
Fetch releases from GitHub
~2hWrite a GitHub client using net/http that calls GET /repos/{owner}/{repo}/releases with the Accept: application/vnd.github+json header and the token if one is set. Decode the JSON into structs, skipping drafts. Read the X-RateLimit-Remaining and X-RateLimit-Reset headers, and return a clear error for 404 (repository not found) and 403 (rate limited). Done when: fetching the releases of a real repository returns them as Go structs.
- 03
Use conditional requests
~1.5hStore each repository's ETag and send it back in an If-None-Match header on the next check. A 304 Not Modified response means nothing changed and doesn't count against GitHub's rate limit. When the remaining limit is low, wait until the reset time before checking again. Done when: a second check of an unchanged repository gets a 304, and the service logs that nothing changed.
- 04
Store and detect new releases
~2hFor each fetched release, insert it with INSERT ... ON CONFLICT DO NOTHING and use the result to know whether it's new. Log new releases as structured messages (repository, tag and URL). Add CLI commands to add and remove watched repositories. Done when: the first check stores all existing releases, and later checks report only releases that appeared since.
- 05
Schedule the checks
~1.5hRun the checks in a background loop with a time.Ticker (default every 15 minutes, configurable), checking up to 4 repositories at once with a worker pool. Handle SIGINT and SIGTERM with signal.NotifyContext so the service stops cleanly and doesn't leave a check half done. Done when: the service keeps checking on schedule and shuts down cleanly with Ctrl+C.
- 06
Add a gRPC API
~2.5hDefine release_monitor.proto with a ReleaseMonitor service: ListRepositories, ListReleases (for a repository, newest first, with a page size) and ListNewReleases (since a given timestamp). Generate the Go code with protoc and protoc-gen-go-grpc, implement the service, and serve it on its own port. Test it with grpcurl. Done when: grpcurl can list repositories and releases from the running service.
- 07
Write tests
~2hTest the GitHub client against an httptest server that returns fixed release JSON, a 304, a 404 and a rate-limit response. Test release storage against a real PostgreSQL (with testcontainers-go, or a test database), checking that duplicates are ignored and new releases are detected. Done when: go test ./... passes.
Ready to build this?
Get a GitHub repo and start building. Your AI reviewer checks each step as you go.
Tech stack