Query cloud APIs, aggregate resource data, output formatted reports. Applies Modules 1-5.
Build a CLI tool that queries a cloud API, aggregates resource information, and outputs formatted reports in multiple formats.
Your program should:
This is the kind of tool every infra team builds internally. Whether it's querying AWS, GCP, or an internal inventory system, the pattern is always: connect → fetch → aggregate → report. This project exercises HTTP clients, JSON handling, pagination, and data aggregation.
# Report on a GitHub org's repos
cloudreport repos --org kubernetes --format table
# Report with sorting
cloudreport repos --org hashicorp --sort stars --limit 20
# JSON output for piping to jq
cloudreport repos --org prometheus --format json | jq '.repos[].name'
# CSV for spreadsheets
cloudreport repos --org grafana --format csv > repos.csv
=== Repository Report: kubernetes ===
Total repositories: 20 (showing top 20 by stars)
NAME STARS FORKS LANG UPDATED
kubernetes 105841 38420 Go 2024-01-15
minikube 28102 4891 Go 2024-01-14
ingress-nginx 16421 8102 Go 2024-01-15
dashboard 13542 4021 TypeScript 2024-01-13
kops 15520 5610 Go 2024-01-12
Summary:
Total stars: 179,426
Languages: Go (14), TypeScript (3), Shell (2), Python (1)
Updated in last 7 days: 18
Link header or use ?page=N&per_page=100 to get all results.X-RateLimit-Remaining header. If approaching the limit (< 10), warn the user and slow down.type Repo struct {
Name string `json:"name"`
Stars int `json:"stargazers_count"`
Forks int `json:"forks_count"`
Language string `json:"language"`
UpdatedAt time.Time `json:"updated_at"`
Description string `json:"description"`
}
--org — GitHub organization to query (required)--format — output format: table (default), json, csv--sort — sort by: stars (default), name, updated, forks--limit — max repos to display (default: 20, 0 = all)--timeout — HTTP timeout in seconds (default: 30)GET https://api.github.com/orgs/{org}/repos?per_page=100&page=1&sort=updated
Response headers to check:
X-RateLimit-Remaining — requests left in the current windowX-RateLimit-Reset — Unix timestamp when the window resetsLink — pagination links (next, last)cloudreport/
├── main.go ← CLI entry point
├── client.go ← HTTP client, pagination, rate limit handling
├── client_test.go ← Tests using httptest.NewServer
├── models.go ← Repo struct, aggregation types
├── aggregate.go ← Sorting, grouping, statistics
├── output.go ← Table, JSON, CSV formatters
└── output_test.go ← Format output tests
Suggested approach:
- Start by fetching a single page from the GitHub API and parsing the JSON
- Add pagination — fetch all pages into a
[]Repo- Add sorting and limiting
- Build the table formatter first (most useful for debugging)
- Add JSON and CSV formatters
- Add rate limit checking
- Add the aggregation summary
func (c *Client) fetchAllRepos(org string) ([]Repo, error) {
var all []Repo
page := 1
for {
repos, hasNext, err := c.fetchReposPage(org, page)
if err != nil {
return nil, err
}
all = append(all, repos...)
if !hasNext {
break
}
page++
}
return all, nil
}
// Compute column widths from data, then use fmt.Sprintf with padding
fmt.Sprintf("%-*s %-*d %-*d %-*s", nameWidth, name, 8, stars, 8, forks, langWidth, lang)
Use httptest.NewServer to mock the GitHub API:
func TestFetchRepos(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode([]Repo{
{Name: "test-repo", Stars: 42, Language: "Go"},
})
}))
defer server.Close()
client := NewClient(server.URL, 10*time.Second)
repos, err := client.fetchAllRepos("test-org")
// assert...
}
GITHUB_TOKEN env var for authenticated requests (higher rate limits)--org flags and merge resultsSkills Used: HTTP clients, JSON parsing, pagination, struct methods, sorting (sort.Slice), string formatting, multiple output formats, CLI flags, error handling, test mocking with httptest.