This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
This is a Go CLI tool that checks GitHub release versions against configurable expiry policies. It supports multiple repositories with both time-based (days) and version-based (semantic versioning) policies.
Multi-Repository Support: Checks versions for any GitHub repository including:
- GitHub Actions runners (default, 30-day time-based policy)
- Kubernetes (version-based: 3 minor versions behind)
- Pulumi (version-based: 3 minor versions behind)
- Node.js (version-based: 3 major versions behind)
- Custom repositories via owner/repo format or URLs
Policy Types:
- Days Policy: Time-based expiry (e.g., 30 days for actions/runner)
- Versions Policy: Semantic versioning-based (e.g., N minor versions behind)
# Build for current platform (output: bin/github-release-version-checker)
make build
# Build for all platforms (darwin/linux/windows, amd64/arm64)
make build-all
# Install to GOPATH/bin
make install
# Build Docker image
make docker-buildBuild Optimizations:
CGO_ENABLED=0: Static compilation without C dependencies-trimpath: Removes absolute file paths for reproducible builds-w -s: Strips debug information and symbol table- Binary size: ~9.8MB (darwin/arm64)
# Run all tests with race detection and coverage
make test
# Run tests and open HTML coverage report
make test-coverage
# Run specific test
go test -v ./internal/version -run TestAnalyse_ExpiredVersion
# Run benchmarks
go test -bench=. -benchmem ./internal/version/Test Coverage (as of 2025-11-03):
- Overall: 46.3%
internal/version: 89.6%internal/data: 80.0%internal/github: 46.9%cmd: 31.8%
# Format code and tidy dependencies
make fmt
# Run linter (installs golangci-lint if needed)
make lint
# Clean build artifacts
make clean
# Run example check
make run-
CLI Layer (
cmd/root.go)- Built with Cobra framework
- Handles repository selection (
--repo), policy override (--policy), and cache paths - Three output modes: terminal (human-readable), JSON (automation), CI (GitHub Actions annotations)
- Repository resolution: predefined names, owner/repo format, GitHub URLs
-
Configuration Layer (
internal/config/)repository.go: RepositoryConfig with predefined configs (actions-runner, kubernetes, pulumi, nodejs)- Repository aliases: "k8s" → kubernetes, "runner" → actions/runner, "node" → nodejs
- Policy types: days (time-based) and versions (semantic versioning)
ParseRepositoryString(): Handles all repository input formats
-
Public API Layer (
pkg/)pkg/checker/: Version analysis engine (importable by external applications)pkg/client/: GitHub API client wrapperpkg/policy/: Policy implementations (DaysPolicy, VersionsPolicy)pkg/types/: Shared types for releases
-
Policy Layer (
pkg/policy/)policy.go: Pluggable policy system via VersionPolicy interface- DaysPolicy: Time-based expiry (e.g., 30 days)
- VersionsPolicy: Semantic versioning-based (N minor versions behind)
- PolicyResult: IsExpired, IsCritical, IsWarning, DaysOld, VersionsBehind
-
Cache Layer (
internal/cache/)manager.go: Manages embedded and custom caches- Priority: custom cache > embedded cache > no cache
go:embed data/*.json: Multiple embedded caches per repository- JSON parsing with intermediate types for proper unmarshaling
-
Data Layer (
internal/data/)loader.go: Loads embedded release cache viago:embedreleases.json: Embedded cache file for actions/runner releases- Uses intermediate types to avoid import cycles
-
Internal Adapters (
internal/)internal/version/: Legacy wrapper aroundpkg/checkerfor backward compatibilityinternal/github/: Legacy wrapper aroundpkg/clientinternal/policy/: Config adapter for creating policies from RepositoryConfig
-
Type Layer (
pkg/types/andinternal/types/)pkg/types/release.go: Public Release typeinternal/types/release.go: Internal Release type (aliases pkg/types)- Breaks import cycles between packages
- Public/Private API Split: Clear separation between importable (
pkg/) and internal-only (internal/) packages - Pluggable policies: VersionPolicy interface for different expiry strategies
- Repository abstraction: Single tool for multiple repositories
- Interface-based design: GitHubClient, VersionPolicy interfaces
- Type aliases:
internal/typesaliasespkg/typesfor backward compatibility - Adapter pattern:
internal/packages wrappkg/for CLI use while maintaining public API - Embedded multi-cache: Multiple cache files via go:embed
- No database: Stateless with embedded data
The codebase maintains a clean separation:
-
pkg/: Public, stable API for library consumerspkg/checker: Core version checking logicpkg/client: GitHub API clientpkg/policy: Policy implementationspkg/types: Shared data types
-
internal/: CLI-specific and internal implementationinternal/version: Adapter aroundpkg/checker(legacy compatibility)internal/github: Adapter aroundpkg/client(legacy compatibility)internal/config: Repository configuration managementinternal/data: Embedded cache loadinginternal/cache: Cache manager (not yet used by CLI)internal/policy: Policy factory from config
When adding features:
- New version checking logic → add to
pkg/checker - New policy types → add to
pkg/policy - CLI-specific features → add to
cmd/orinternal/ - Keep
pkg/packages focused and minimal for library users
The checker uses a multi-step analysis with embedded cache optimization:
- Load embedded releases from
internal/data/releases.json(instant, no API call) - Fetch 5 most recent releases from GitHub API (1 API call)
- Validate cache: check if latest embedded release is in top 5
- If current: merge embedded + recent releases (optimal path, 1 API call total)
- If stale (>5 releases behind): fall back to
GetAllReleases()(2 API calls total)
- If comparison version provided, parse it and filter releases
- Filter releases newer than comparison version, sort oldest-first
- Calculate days since FIRST newer release (not latest) - this is the key policy metric
- Determine status based on thresholds:
current: on latest versionwarning: behind but within critical thresholdcritical: within critical age window (default 12-30 days)expired: beyond max age (default 30 days)
Cache Strategy: Reduces API calls from 2 per invocation to 1 in the common case, improving speed and rate limit usage.
When using --ci flag for GitHub Actions:
- Uses
::group::/::endgroup::for collapsible sections - Uses
::error::/::warning::/::notice::for annotations - Writes markdown summary to
$GITHUB_STEP_SUMMARYfile with status table and release links
- Repository: https://github.com/nickromney-org/github-release-version-checker
- Module Path:
github.com/nickromney-org/github-release-version-checker - Binary Name:
github-release-version-checker
Tests use MockGitHubClient to simulate various scenarios (current, warning, critical, expired versions). Test helper newTestRelease() creates releases with specific ages (days ago) for deterministic testing.
spf13/cobra: CLI frameworkMasterminds/semver/v3: Semantic version parsing and comparisongoogle/go-github/v57: GitHub API clientfatih/color: Terminal colourisation (imported ascolour)golang.org/x/oauth2: GitHub authentication
Tool accepts token via -t flag or GITHUB_TOKEN env var to avoid rate limiting (60 req/hour unauthenticated, 5000 req/hour authenticated).
The tool embeds historical release data to minimize API calls during runtime:
- Cache file:
internal/data/releases.json- JSON file with all releases from v2.159.0 onwards - Embedded: Via
go:embeddirective ininternal/data/loader.go - Format:
{
"generated_at": "2025-10-31T12:00:00Z",
"releases": [
{
"version": "2.329.0",
"published_at": "2024-10-14T10:30:00Z",
"url": "https://github.com/actions/runner/releases/tag/v2.329.0"
}
]
}Three commands manage the cache:
cmd/bootstrap-releases: Fetches all releases from GitHub API and generatesinternal/data/releases.json
- Used during initial setup and manual updates
- Run via
scripts/update-releases.sh
cmd/check-releases: Validates cache currency
- Checks if latest embedded release is in top 5 recent releases
- Exit 0 if current, exit 1 if stale
- Used by automation to trigger updates
scripts/update-releases.sh: Shell wrapper for bootstrap command
- Sets up environment and runs bootstrap
- Reports release count after update
GitHub Actions workflow (.github/workflows/update-releases.yml):
- Runs daily at 6 AM UTC (3 hours before runner compliance checks)
- Executes
check-releasesto validate cache - If stale: runs
update-releases.shand commits changes - Commit triggers semantic-release for new binary build
- Includes
[skip ci]to avoid workflow recursion
The Analyse() method in pkg/checker/checker.go:
- Loads embedded cache (instant, no API)
- Fetches 5 most recent releases (1 API call)
- Calls
isEmbeddedCurrent()to validate cache - If current: calls
mergeReleases()to combine datasets - If stale: falls back to
GetAllReleases()(additional API call)
This project uses British English spelling throughout the codebase:
- Method names:
Analyse()(notAnalyze()) - Variable names:
colour(notcolor) - Comments and documentation use British spelling
The golangci-lint configuration enforces British spelling via the misspell linter with locale: UK.