Help us shape the future of Go! Take the 2026 Go Developer Survey

fleet

package
v0.68.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 1, 2026 License: BSD-3-Clause Imports: 11 Imported by: 0

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

View Source
const Attempts = 5

Attempts is how many times a retryable failure is tried before giving up.

View Source
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.

View Source
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

View Source
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.

View Source
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.

View Source
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.

View Source
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

func BudgetLeft() (left int, tracking bool)

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 GH

func GH(args ...string) ([]byte, error)

GH runs gh with the shared retry policy and returns its stdout.

func IsHelpRequest

func IsHelpRequest(arg string) bool

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 Orgs

func Orgs() ([]string, error)

Orgs lists every organisation the authenticated account belongs to.

func ParseFlags

func ParseFlags(fs *flag.FlagSet, args []string) (code int, ok bool)

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

func Retryable(msg string) bool

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

func StartBudget(floor, reserve int) error

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

func Throttled(msg string) bool

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

func Transient(msg string) bool

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

func WarnIfStale(w io.Writer)

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.

func Repos

func Repos(org string) ([]Repo, error)

Repos lists an organisation's repositories, skipping archived ones and forks: neither is ours to judge, and counting them makes every total disagree with every other total.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL