102 lines
5.2 KiB
Markdown
102 lines
5.2 KiB
Markdown
# Agent Instructions — `royalroadupdates`
|
|
|
|
## Project Purpose
|
|
A small, self-contained Go CLI tool that checks RoyalRoad stories from a
|
|
PocketBase bookmarks collection and reports their last-update times.
|
|
|
|
## Repository Layout
|
|
|
|
```
|
|
.
|
|
├── cmd/royalroadupdates/ Entry point and integration tests
|
|
│ ├── main.go - CLI bootstrap, flag parsing (--json)
|
|
│ └── e2e_test.go - End-to-end test: compiles binary, runs it,
|
|
│ asserts JSON shape and entry count
|
|
├── internal/
|
|
│ ├── pocketbase/
|
|
│ │ └── client.go - Queries the public PocketBase collection endpoint.
|
|
│ │ Accepts perPage and field params.
|
|
│ │ Returns a list of structs with URL fields.
|
|
│ ├── royalroad/
|
|
│ │ └── scraper.go - Given a URL, fetches the page, confirms it
|
|
│ │ is a RoyalRoad fiction page, extracts the
|
|
│ │ JSON-LD script block, parses name and
|
|
│ │ dateModified into a struct.
|
|
│ │ Returns zero-value + error for any failure
|
|
│ │ so the caller can skip silently.
|
|
│ ├── formatter/
|
|
│ │ ├── table.go - Renders []Story as a plain-text aligned
|
|
│ │ │ table with relative-time "Last Update"
|
|
│ │ │ column.
|
|
│ │ ├── json.go - Renders []Story as JSON (used with --json).
|
|
│ │ └── relative.go - Computes "X minutes/hours/days/months/years
|
|
│ │ ago" strings from time.Time.
|
|
│ └── app.go - Orchestrates the full pipeline:
|
|
│ 1. Load endpoint URL from compile-time var.
|
|
│ 2. Query PocketBase for URLs.
|
|
│ 3. Iterate URLs sequentially.
|
|
│ 4. Collect valid RoyalRoad results.
|
|
│ 5. Sort by dateModified descending.
|
|
│ 6. Dispatch to appropriate formatter.
|
|
├── .env - Real endpoint URL (gitignored).
|
|
├── .env.example - Template showing required env vars.
|
|
├── .gitignore - Must include .env and the built binary.
|
|
├── Makefile - All build, test, and check targets.
|
|
├── go.mod - Standard Go module file.
|
|
└── go.sum - Standard Go checksum file.
|
|
```
|
|
|
|
## Code Style Rules
|
|
|
|
- **Formatting:** All Go source files must pass `gofmt`. The `make check` target
|
|
will reject any diff.
|
|
- **Imports:** Group standard library first, then third-party (none expected),
|
|
then local/internal. `goimports` ordering is acceptable.
|
|
- **Naming:** Exported identifiers use PascalCase; unexported use camelCase.
|
|
Acronyms like HTTP, URL, JSON keep all-caps when exported (`FetchURL`,
|
|
`ParseJSON`).
|
|
- **Error handling:** Always check errors explicitly. Do not swallow errors
|
|
silently unless the design calls for silent skipping (RoyalRoad per-item
|
|
failures). Return errors wrapped with `fmt.Errorf("...: %w", err)` for
|
|
context.
|
|
- **Comments:** Every exported type and function has a concise GoDoc comment.
|
|
Internal helpers may omit comments if their purpose is obvious from the name.
|
|
- **No globals:** Avoid package-level mutable state. Pass dependencies (HTTP
|
|
client, configuration) as struct fields or constructor parameters.
|
|
- **HTTP client reuse:** Create one `http.Client` with a configured timeout and
|
|
share it across requests. Do not use `http.DefaultClient` unwrapped.
|
|
|
|
## Build Rules
|
|
|
|
- **No local Go installation.** Use `make` targets which wrap
|
|
`docker run --rm -v $(pwd):/src -w /src golang:1.22`.
|
|
- **Endpoint injection:** The Makefile reads `.env` and passes the value as
|
|
`-ldflags '-X main.PBURL=...'`.
|
|
- **Output:** The compiled binary is written to `./royalroadupdates` in the
|
|
repository root.
|
|
- **Targets:**
|
|
- `make build` — compile the binary.
|
|
- `make check` — full quality loop (format, vet, test, e2e test).
|
|
- `make clean` — remove the compiled binary.
|
|
|
|
## Testing Rules
|
|
|
|
- **Unit tests:** Cover pure helpers (e.g., `relative.go`, URL filtering logic)
|
|
with table-driven tests. Use `t.Parallel()` where independent.
|
|
- **End-to-end test (`e2e_test.go`):**
|
|
- Uses the `.env` credentials and hits real services.
|
|
- Compiles the binary via `os/exec` so it tests the actual build path.
|
|
- Runs `./royalroadupdates --json`.
|
|
- Assertions:
|
|
1. Exit code 0.
|
|
2. Stderr is empty.
|
|
3. Stdout is valid JSON.
|
|
4. JSON is a non-empty array.
|
|
5. Array contains at least 3 entries.
|
|
6. Each entry has a non-empty `name` and a parseable `date`.
|
|
- Since this test hits live services, it is inherently non-deterministic in
|
|
data content but deterministic in shape. Treat network/Cloudflare flakiness
|
|
as a signal to tune delays rather than a code defect.
|
|
- **Fix-and-rerun cycle:** If `make check` fails at any step, fix the root cause
|
|
and rerun `make check` until all gates pass.
|