Documentation
¶
Overview ¶
Package fleet is the one place this repository shells out to gh, and the one place that decides what is worth retrying.
It exists because six commands each had their own. The four spellings were not equivalent, and the difference was not stylistic:
prscan tagscan redscan docscan throttling only prmerge quietscan throttling AND transient network faults
prmerge learned the second list the expensive way, and says so in the comment this package inherits: one pass lost 186 of 460 candidates to "no route to host" while the machine's link flapped, and a later DNS outage produced 109 more that the retry never fired on, because the matching was written against the errors that had been SEEN rather than the ones gh emits. Neither lesson ever reached the other four sweeps, so a network blip still turns any of them into a silent hole -- and a hole in a sweep reads as "nothing to report", which is the one answer a sweep must never give by accident.
⛔ The two rate limits want OPPOSITE treatment and this is the distinction worth keeping. The SECONDARY limit is a burst brake: it lifts in seconds and waiting is the whole remedy. The PRIMARY one is an hourly budget that resets at a fixed time, and no short backoff reaches it -- matching on "rate" alone made a spent budget sleep five minutes and fail anyway, per batch.
Index ¶
- Constants
- Variables
- func BudgetLeft() (left int, tracking bool)
- func GH(args ...string) ([]byte, error)
- func IsHelpRequest(arg string) bool
- func Orgs() ([]string, error)
- func ParseFlags(fs *flag.FlagSet, args []string) (code int, ok bool)
- func Retryable(msg string) bool
- func StartBudget(floor, reserve int) error
- func StopBudget()
- func Throttled(msg string) bool
- func Transient(msg string) bool
- func WarnIfStale(w io.Writer)
- type Repo
- type Vintage
Constants ¶
const Attempts = 5
Attempts is how many times a retryable failure is tried before giving up.
const DefaultReserve = 1000
DefaultReserve is how much of the hourly budget a sweep leaves behind.
⛔ It is a CHOSEN number, not a measured one, and the difference matters: what is measured is the cost of a sweep (docscan ~1395 calls, redscan ~2000) and that the fleet's runners are spread across the day. What is NOT measured is what one Renovate run costs, so this cannot be derived. Refining it means instrumenting a Renovate run; until then the figure is a judgement and says so here rather than looking like arithmetic.
const Repository = "go-fleettools/fleettools"
Repository is where these tools live. The staleness check below asks GitHub about this repository, so a fork that renames it must change this line too.
Variables ¶
var Backoff = func(attempt int) time.Duration { return time.Duration(20*(attempt+1)) * time.Second }
Backoff is how long to wait before the next attempt. A test sets it to nothing.
var ErrBudgetFloor = errors.New("fleet: the hourly REST budget is below the floor this sweep asked for")
ErrBudgetFloor means the budget was already below what the sweep asked for, so it never began. Beginning anyway is what leaves a sweep half done and a Renovate run dead.
var ErrBudgetReserve = errors.New("fleet: stopped at the budget reserve")
ErrBudgetReserve means the sweep ran down to the reserve and stopped. Every sweep here already knows how to report INCOMPLETE, which is the honest answer and the reason stopping is better than failing.
var Guard func(args []string) error
Guard, when set, is consulted before every call and can refuse it. quietscan uses this to keep a watcher read-only: a tool that only looks must not be one edit away from writing.
Functions ¶
func BudgetLeft ¶
BudgetLeft is the projection, and whether anything is tracking it. A caller that prints a figure must also say when there is none: "0 left" and "not watched" are different answers.
func IsHelpRequest ¶
IsHelpRequest reports whether this argument is somebody asking what the command does, in any of the four spellings people actually type.
⛔ IT IS FOR THE COMMANDS ParseFlags CANNOT HELP: the ones that dispatch on a subcommand before any flag set exists. blobpurge answered `blobpurge -h` with 2 for exactly that reason -- `-h` is not "scan" and not "strip", so it fell through to "I do not know that subcommand", and the behavioural measure found it while the text gate was satisfied. The three commands that ask this question by hand each knew a DIFFERENT three of the four spellings: prmerge missed `-help`, pagespublish missed `help`, blobpurge asked nothing at all.
func ParseFlags ¶
ParseFlags parses args and says what the command should do about the result: the status to exit with, and whether there is any work left to do.
if code, ok := fleet.ParseFlags(fs, args); !ok {
return code
}
⛔ IT EXISTS FOR ONE DISTINCTION THE OBVIOUS SPELLING DROPS. The shape every command here was written with --
if err := fs.Parse(args); err != nil {
return 2
}
-- answers `-h` with 2, because the flag package reports a help request as an error (flag.ErrHelp) like any other. Asking a tool what its flags are is not a usage error, and the flag package's own default says so: with flag.ExitOnError it calls os.Exit(0) for ErrHelp and os.Exit(2) for the rest. Every command here uses flag.ContinueOnError instead -- so that run() can be tested rather than exit the process -- and that choice silently gave up the distinction. Measured 2026-10-18: of 36 commands, 13 answered `-h` with 2.
⛔ AND IT IS ONE HELPER RATHER THAN FOURTEEN COPIES because the help branch is the one nobody exercises: thirteen hand-written copies are thirteen branches to cover, and the twelve commands that did get it right prove the shape is easy to type and easy to forget. Here it is covered once, by TestParseFlagsSeparatesAQuestionFromAMistake, and TestEveryCommandAnswersHelpWithZero fails for a command that goes back to the two-line form.
func Retryable ¶
Retryable is the whole policy: wait for a burst brake or a network blip, give up at once on a spent hourly budget or a real refusal.
func StartBudget ¶
StartBudget reads the hourly budget once and refuses to begin below floor.
⛔ It reads rate_limit ONCE and then counts this package's own calls, rather than re-reading per call: a guard that polls its own budget doubles the cost of the thing it is protecting. The count is therefore a PROJECTION, exact in what this process spends and blind to what anything else spends in the same hour -- which is the direction that matters, because it under-estimates what is left rather than over.
func StopBudget ¶
func StopBudget()
StopBudget forgets the projection. A test uses it; a long-lived process that starts a second sweep in a new hour would too.
func Throttled ¶
Throttled reports whether a failure is GitHub braking a burst, which waiting cures.
It is false for the primary hourly budget even though that message also says "rate limit": that budget will not refill inside any backoff this package would sleep, so retrying it spends minutes to fail anyway.
func Transient ¶
Transient reports whether a failure is the network rather than GitHub.
The second half of this list is gh's own wording. Matching only the Go-level strings is what let a DNS outage produce 109 failures with the retry never firing: the errors that had been seen were not the errors the tool emits.
func WarnIfStale ¶
WarnIfStale writes at most one line to w when the binary that is running is not the code this repository currently holds.
⛔ This exists because of a measured failure, not a hunch. On 2026-09-27 a guard landed that sets aside a red belonging to a workflow every push skips. The installed binaries were never rebuilt, so four days of sweeps ran without it and reported the same three repositories as RED default branches each time. Nothing was broken, every number was wrong, and no output said so -- which is the shape of defect this whole repository exists to refuse.
It is silent in the only case that needs no attention: built from the head of the default branch, from a clean tree. Every other case says something, including "I could not tell", because a check that fails open teaches you to trust a silence it did not earn.
Types ¶
type Repo ¶
type Repo struct {
FullName string `json:"full_name"`
DefaultBranch string `json:"default_branch"`
Archived bool `json:"archived"`
Fork bool `json:"fork"`
HasPages bool `json:"has_pages"`
}
Repo is the subset of a repository every sweep here needs.
HasPages comes with the listing, which is why pagescheck can find every published site in an organisation without a call per repository.
type Vintage ¶
type Vintage struct {
Revision string // full commit sha, empty when the binary does not say
Modified bool // the working tree had uncommitted changes
// Module is the main module's version when the binary was built in
// module mode -- `go install github.com/go-fleettools/fleettools/cmd/x@v0.4.0`
// -- and so carries no VCS stamp at all. Such a build is made from the
// module zip the proxy serves and sum.golang.org vouches for, so it is
// clean by construction, and its version (a tag, or a pseudo-version
// ending in a commit) is enough to compare with the default branch.
// Empty for a checkout build, which says "(devel)" or carries stamps.
Module string
// Command is the import path the running binary was built from, e.g.
// github.com/go-fleettools/fleettools/cmd/vulngate. It is what makes the
// staleness question answerable PER COMMAND rather than per module: see
// reachesCommand. debug.BuildInfo.Path is the reliable source for it --
// os.Args[0] is whatever the caller typed, including a copied binary under
// another name, which the git hooks here actually are.
Command string
}
Vintage is what a running binary can say about its own provenance.
It comes from the VCS stamps the Go toolchain embeds when they are there, NOT from the module version: a checkout build may carry both, and only the stamps say whether that tree was clean -- a reading taken from a modified tree is not one anybody can reproduce. The module version is used only when there are no stamps at all, which is a module-mode build (see Module), whose tree is the published zip and cannot have been modified.
func BuildVintage ¶
func BuildVintage() Vintage
BuildVintage reads the stamps out of the running binary.