Documentation
¶
Overview ¶
Package release is the last mile: a green commit on main becomes a version, a set of binaries, and the same binaries answering for themselves on every bench in the fleet.
It exists because that mile was a shell script. `fleet-install-tools.sh` built the tools, cached them by sha, tarred them to each bench and installed them by rename -- 30 lines of nested ssh quoting with a build, a cache, a copy, an install and a verify all in one `for` loop, and no test of any of it. It was written after a pit stop where benches ran six-hour-old tools while the coordinator believed they were current, and the reason that could happen is that nothing in the loop could SAY what it had done: the install printed one line per bench and the line was composed from the same ssh it was reporting on.
So the four steps are four verbs, each of which can refuse:
cut main is green, here is the version, here is what changed build every cmd/nova-* for one platform, stamped, with a checksum file install verify the checksums, put them in place atomically, skip what is current adopt do the install on every machine in a file, one receipt line each
The three edges to the world outside this process -- the forge, ssh, and the Go toolchain -- are interfaces, so that a test can watch every argument this tool would hand a subprocess and so that the production implementations are short enough to read line by line. No unit test here reaches the network or a real machine.
NOTHING HERE TOUCHES A SECRET. gh carries its own credential, ssh carries its own key, and neither is read, logged or passed by this package.
Index ¶
- Constants
- Variables
- func Annotation(version, sha, sumsDigest string, ciWaiver ...string) string
- func ArtifactDir(root, version, goos, goarch string) string
- func ExeSuffix(goos string) string
- func ExecVersion(ctx context.Context, path string) (string, error)
- func Ldflags(version string) string
- func Main(name string, args []string, stamp string, out, errs io.Writer) int
- func MarkPulled(text, version, note string) (string, error)
- func Platform(flagValue string) (string, string, error)
- func PulledNote(when time.Time, reason string) string
- func ReadDigestFile(path string) (string, error)
- func ReadPathsFile(path, rangeName string) ([]string, error)
- func RemoteFrom(value string) (host, dir string, remote bool)
- func RemotePath(p string) string
- func Run(name string, args []string, out, errs io.Writer, deps Deps) int
- func Sensitive(files []string) []string
- func SumsInAnnotation(message string) string
- func ToolFile(tool, goos string) string
- func Tools(source string) ([]string, error)
- func ValidRemotePathOn(goos, what, p string) error
- func ValidSecurityRead(id string) error
- func ValidVersion(v string) error
- func VerbUsage(verb string) string
- func VerifyArtifacts(dir string, arts []Artifact) (int, error)
- func VersionsUnder(root, goos, goarch string) []string
- func WritePathsFile(path, rangeName string, files []string) error
- type Ansible
- type Artifact
- type CheckRun
- type Commit
- type Deps
- type Dogfood
- type DogfoodVerdict
- type EvidenceHeader
- type ExecAnsible
- type ExecGit
- type ExecSSH
- type ExecSource
- func (ExecSource) Changed(ctx context.Context, dir, base, head string) ([]string, error)
- func (ExecSource) GoVersion(ctx context.Context) (string, error)
- func (ExecSource) Head(ctx context.Context, dir string) (string, bool, error)
- func (ExecSource) Packages(ctx context.Context, dir, goos, goarch string, pkgs []string) (map[string][]string, error)
- type FileReceipts
- type Forge
- type GH
- func (g *GH) CheckRuns(ctx context.Context, repo, sha string) ([]CheckRun, error)
- func (g *GH) Compare(ctx context.Context, repo, base, head string) ([]Commit, error)
- func (g *GH) Files(ctx context.Context, repo, base, head string) ([]string, error)
- func (g *GH) HeadSHA(ctx context.Context, repo, branch string) (string, error)
- func (g *GH) Tag(ctx context.Context, repo, tag, sha, message string) error
- func (g *GH) TagMessage(ctx context.Context, repo, tag string) (string, error)
- func (g *GH) TagTime(ctx context.Context, repo, tag string) (time.Time, error)
- func (g *GH) Tags(ctx context.Context, repo string) ([]string, error)
- type Git
- type GoBuild
- type InstalledReceipt
- type Journey
- type JourneyRecord
- type JourneyResult
- type Machine
- type NoReadout
- type OneMachine
- type OneRun
- type OpenRouterSpend
- type PR
- type ProviderSpend
- type RecordedSpend
- type SSH
- type Source
- type SpendRow
- type SpendSources
- type SpendVerdict
- type SpendWindow
- type TagTimer
- type TokenReceipts
- type Toolchain
Constants ¶
const ( DogfoodWaiveFlag = "--no-dogfood-gate" DogfoodReasonFlag = "--reason" )
DogfoodWaiveFlag and DogfoodReasonFlag are the way past the gate, spelled in one place so the remedy a person is handed is the flag they then type.
const ( JourneysProvenPrefix = "Recovery journeys proven at " JourneysIncompletePrefix = "Recovery journeys incomplete, gate waived: " )
JourneysProvenPrefix and JourneysIncompletePrefix are how the CHANGELOG section names what the gate found.
const ( OpenRouterActivityURL = "https://openrouter.ai/api/v1/activity" OpenRouterActivityEnv = "OPENROUTER_PROVISIONING_KEY" // OpenRouterActivityDays is how far back the activity reaches. OpenRouterActivityDays = 30 )
OpenRouter's readouts: the account's activity (GET with a provisioning key, the usage of each of the last completed UTC days, by model), and the key's own count of today (GET /key, data.usage_daily), the day the activity does not hold yet.
const AdoptNote = "adopt runs FROM the host that has ssh to every machine and fans out from there; it never needs the machines to reach each other. " + "When the release was built elsewhere, --from may name that machine as host:dir and --stage <dir> says where to fetch it first. " + "Such a fetch is verified against a digest that did NOT travel with the bits: --repo <owner/name> reads it off the annotated tag the cut wrote, --expect-sums <sha256> names it outright, or --expect-sums-from <file> reads it out of the " + DigestFile + " this host's own `release build` wrote. " + "A dev build has no tag, which is why the third exists; the file must be a LOCAL one, because a digest computed on the machine holding the bits is that machine vouching for itself. " + "Install the release on this host before adopting it: the nova-update running the fan-out is the one here, and a coordinator older than the release it is adopting refuses and says so. " + "--machines is " + MachinesShape + ". " + RemotePathsNote + ". " + "--retire <dir> removes this release's own nova-* files from a second directory nobody should still be running from (~/go/bin); it refuses to be --bin or the live stamp. " + "--bin, --dest and --retire must be absolute or ~/-rooted and free of shell metacharacters; they are validated before any remote command is composed."
AdoptNote is what a person needs before their first adopt, and every sentence of it is something the first dogfood pass had to find out by failing.
const AnnotationSumsPrefix = "sums="
AnnotationSumsPrefix is how the TAG names the same digest, and it is written on a line of its own so that reading it back is an anchored match rather than a search through prose.
const CIWaiverChecksPrefix = "CI waived checks: "
CIWaiverChecksPrefix opens the line that names the checks the waiver covered.
const CIWaiverPrefix = "CI waived: "
CIWaiverPrefix is how the tag annotation and the CHANGELOG section name a waived CI gate, spelled once so the two records cannot drift. A red CI is let past only by the cut's own --waive-ci, and what it let past outlives the terminal: a person asking in six months why a version shipped on a red commit reads the answer in the tag. docs/SPEC-RELEASE.md section 19.
const CompareFileCap = 300
CompareFileCap is how many files the forge will name for one compare. It is GitHub's own ceiling on the `files` array of a compare response, and it matters here for one reason: A FILE LIST AT THE CEILING IS A LIST THAT MAY BE SHORT. The classification below reads that list, so a range at this number cannot be classified at all, and a gate that reads a truncated list is a gate that passes the one file it did not see. `cut` refuses such a range with the same remedy as a sensitive one rather than quietly deciding on a prefix of the truth.
const CycleNote = "cycle is the fix-land-install cycle from the coordinator in one command: the tools play (<--source>/fleet/tools.yml) with --check, then the play itself, limited to --benches, localhost and the store_deployer group, and the build's schema and function library on the store runs on every cycle. " +
"The play builds every missing platform with `release build --incremental --gate report --reason <why>`, copies only the binaries a bench does not already hold, and installs; " +
"one CYCLE BENCH line per bench names the version it now runs, and both plays' output is kept under <--out>/<version>/cycle-check.log and cycle-apply.log. " +
"--dry-run runs the check alone. It needs the inventory's environment (the store, the seat) exactly as the play does. A release cut keeps the refusing gate: cycle is for a machinery install during a sprint."
CycleNote is the verb said where a person meets it.
const DigestFile = "SUMS.digest"
DigestFile holds the sha256 OF SumsFile, written beside it by `release build`.
`adopt` fetching a release from another machine must check it against a digest that did NOT travel with the bits, and the two ways to have one -- the annotated tag and the CHANGELOG entry -- both belong to a TAGGED release. A dev build has no tag, so the only other place to get a digest is the machine being adopted FROM, which is that machine vouching for its own bytes and is not evidence at all. This file is written where the build ran, on the coordinator, out of the SHA256SUMS the build had just verified; `adopt --expect-sums-from` reads it from there. A digest computed on the machine being adopted from is not evidence about a fetch.
const DogfoodRemedy = "fix the open edges or " + DogfoodWaiveFlag + " " + DogfoodReasonFlag + " <why>"
DogfoodRemedy is what the refusal tells somebody to do about it. Two ways out and both are work: fix the edges, or waive the gate and say why where the waiver will outlive the terminal.
const DogfoodWaiverPrefix = "Dogfood gate waived: "
DogfoodWaiverPrefix is how the CHANGELOG section names a waived gate. The waiver travels with the release, in the file a person reads to find out what a version is, because a waiver that lives only in one terminal's scrollback is a waiver nobody can weigh in six months.
const EvidenceKind = "release-journeys"
EvidenceKind is what the evidence header's "evidence" field says, so a file of some other JSON is refused rather than read as an empty run.
const ExitCodes = "exit codes: 0 the verb did what its line says (a --dry-run printed its plan and changed nothing); " +
"1 it ran and a step failed partway, the FAILED or REFUSED line naming what was done and what to do next; " +
"2 it refused before acting, naming the command to run."
ExitCodes is the release verbs' exit-code line, which each verb's -h prints.
const IncrementalNote = "build writes a record beside each platform directory, <out>/<version>/<goos-goarch>" + RecordSuffix + ": the commit (none when the checkout is dirty), the Go, the stamp shape, the gate and its reason. " +
"--incremental compiles only the tools whose packages, or the packages they import, differ (`git diff --name-only --no-renames`) between the newest such record under --out and the checkout, and copies every other tool from that build, verified against its SHA256SUMS; " +
"a reused tool answers the version it was built at. A dirty checkout, another Go, a go.mod or go.sum change, or no record is a whole build, said on RELEASE BUILD WHOLE; otherwise RELEASE BUILD INCREMENTAL names the base and every tool rebuilt. " +
"--gate report --reason <why> runs the dogfood gate, prints its open edges and RELEASE BUILD DOGFOOD REPORTED, and builds (dogfood=report); it is for a machinery install during a sprint, and cut has no such flag."
IncrementalNote is --incremental and --gate report said where a person meets them.
const JourneyRemedy = "run each promised journey at the release revision and pass --journeys <file>, or " + JourneyWaiveFlag + " " + DogfoodReasonFlag + " <why>"
JourneyRemedy is what the refusal tells somebody to do.
const JourneyWaiveFlag = "--no-journey-gate"
JourneyWaiveFlag is the way past the gate, spelled once.
const KeepBesides = 3
KeepBesides is how many release versions a root keeps BESIDES the ones it must: the version just built or installed, and the version the machine was running before it. A root that keeps every version forever, one directory per dev build, runs a machine out of disk; the measurement below is that root, not a claim every machine reaches it. Measurement (2026-10-01): 36 versions, 9 GB on one root; 34 GB on another.
Three is enough to put back any of the last few builds by re-installing it without a rebuild, which is the only thing an old version directory is for, and it bounds a root at five versions whatever the day's build rate.
const MachinesShape = "" /* 169-byte string literal not displayed */
MachinesShape is the one sentence that says what the --machines file holds. The help prints it and docs/SPEC-UPDATE.md carries it, because a file format discoverable only from a refusal is a format nobody can write correctly the first time, so the shape is stated here rather than learned from the source.
const PathsHeaderPrefix = "# nova-update release cut --local-diff "
PathsHeaderPrefix is the first line of a path list `release cut --local-diff` wrote, and the whole reason --paths-from can be trusted: the rest of the file is a classification gate's INPUT, and a gate reading a hand-written input is a gate whose answer is whatever somebody remembered. The line also names the RANGE, so a list left over from a different pair of commits is refused rather than quietly classifying a release that is not this one.
PlatformUnavailable is how a journey's skip says its platform was not there to run on: `t.Skip(release.PlatformUnavailable + "windows: no windows bench answered")`. It counts only for a platform the journey names as Optional.
const PullNote = "pull withdraws a release that should not have shipped (a leak, a key, a file that was never meant to travel). " + "THE TAG STAYS: a tag that vanishes is a history that cannot be read, so the CHANGELOG section is marked " + PulledPrefix + "<date>** instead, carrying --reason. " + "What is deleted is the ARTIFACTS: the release's own files under --out here, and the same files under --dest on every machine in --machines, by name, never recursively. " + "The names come from that release's own " + SumsFile + " under --out, so --out must still hold the release being pulled. " + "It does not touch an INSTALLED binary: a machine keeps running what it is running until the next release is adopted over it."
PullNote is what a person needs before their first pull, in the help, because every sentence of it is a decision somebody would otherwise have to guess at.
const PulledPrefix = "**PULLED "
PulledPrefix is how a changelog section says the release was withdrawn. It is a prefix rather than a whole sentence because the note carries a date and a reason, and it is one constant because `pull` writes it and `pull` reads it back: a second run must not stack a second note.
const ReceiptsKind = "spend-receipts"
ReceiptsKind is what a receipts file's "evidence" field says.
const RecordSuffix = ".build"
RecordSuffix names the build record written beside each platform directory, <out>/<version>/<goos-goarch>.build. It sits OUTSIDE the platform directory so that it is never in SHA256SUMS, never copied to a bench and never installed: it is this host's note about how the artifacts were made.
const RemotePathsNote = "--bin and --dest are paths on each machine; the remote shell expands a leading ~, so quote it ('~/.local/bin') or the local shell expands it here instead. " +
`A windows target takes the drive form too ('C:\Users\nova\.local\bin'), folded to forward slashes before any command is composed -- the far side's ssh shell is Git Bash (docs/BENCH-WINDOWS.md) and a backslash there is an escape. The drive form is refused for every other target`
RemotePathsNote says the one thing about --bin and --dest that is easy to get wrong and silent when you do. They are paths ON THE MACHINE: the remote shell expands a leading ~, so `--bin '~/.local/bin'` is how three different home directories are named at once -- and the quotes are load-bearing, because an unquoted ~ is expanded by the LOCAL shell into the adopting host's home, which is a path the machine has probably never heard of.
And the windows bench's own form is said here rather than found out at a refusal: it is what docs/BENCH-WINDOWS.md puts in that bench's runner .path, so it is what a person will type.
const SpendGapOver = 0.05
SpendGapOver is the share of the provider's own figure a gap must pass to refuse.
const SpendRemedy = "find the spend the store did not record (nova-sprint cost reconcile; card <id> shows the records), give every provider a readout and the friends their receipts, or " + SpendWaiveFlag + " " + DogfoodReasonFlag + " <why>"
SpendRemedy is what the refusal tells somebody to do.
const SpendWaiveFlag = "--no-spend-gate"
SpendWaiveFlag is the way past the gate, spelled once.
const SpendWaiverPrefix = "Spend gate waived: "
SpendWaiverPrefix is how the CHANGELOG section names a waived spend gate.
const SprintReleaseFlag = "a directory holding one nova-sprint release as `gh release download <tag> -R <owner>/nova-sprint -D <dir>` writes it " +
"(<tool>_<tag>_<goos>_<goarch> and SHA256SUMS_<goos>_<goarch>): its tools (nova-sprint, nova-card, nova-work) are verified against " +
"its checksums and shipped in this release beside the ones built from --source, never compiled here"
SprintReleaseFlag is --sprint-release, said once for build and cycle.
const SumsDigestPrefix = "SHA256SUMS digest: "
SumsDigestPrefix is how the changelog names the digest of a release's SHA256SUMS, in one place so that what `cut` writes and what a person copies into `adopt --expect-sums` are the same string.
const SumsFile = "SHA256SUMS"
SumsFile is the name of the checksum file in every artifact directory, in the format `sha256sum -c` reads, because the person verifying a copy by hand should not need this tool to do it.
const Verbs = `` /* 1655-byte string literal not displayed */
Verbs is the usage block `nova-update help` prints for this verb, and the same five lines docs/SPEC-UPDATE.md carries. Every path is a flag and no flag has a default path: a path guessed from the cwd or from `$HOME` makes a release cut from a laptop and a release cut from a bench mean different things, so the same command is the same release on either host. The one exception is --receipts, and pkg/release/dogfoodgate.go says at length why the gate in front of the definition of done is worth it.
Variables ¶
var CutNote = "cut classifies the range since the previous tag against the sensitive path list in pkg/release/sensitive.go and docs/SPEC-RELEASE.md " + "(" + SensitiveShape + "). A range that touches one of them REFUSES until --security-read names the security reader's read -- a note id or the url of the comment -- " + "and the cut then prints `RELEASE CUT SENSITIVE paths=<n> read=<id>` above its receipt. " + "A range TOO BIG FOR THE FORGE TO LIST is a different refusal and --security-read does not get past it: a read of a list that may be short is a read of a prefix of the truth. " + "Classify such a range from a complete local list instead -- `--local-diff <checkout>` runs `git diff --name-only <previous tag>...<head>` in that checkout, and `--paths-from <file>` writes the answer there for a later cut to read back. " + "The tag is annotated, and the annotation carries `sums=<sha256 of SHA256SUMS>` when --sums names the built checksum file, which is the digest `adopt --repo` reads back. " + "`build` writes one SHA256SUMS per platform, under <out>/<version>/<goos-goarch>/, and --sums takes one of them: the tag and the CHANGELOG section carry THAT platform's digest, and `adopt --repo` verifies that platform only. " + "Every other platform the release built is adopted with --expect-sums-from <out>/<version>/<goos-goarch>/" + DigestFile + " on the host that built it, or --expect-sums <sha256> from the sums= field of its `RELEASE BUILT` line."
CutNote is the gate in front of a tag, said where a person will meet it. It is a var rather than a const because it names the list, and the list has ONE home: composing this from SensitivePaths is why the help cannot fall behind the gate.
var DefaultReceiptsDir = filepath.Join("nova-working", "dogfood")
DefaultReceiptsDir is where this fleet keeps its receipts, relative to the home directory of whoever is cutting. It is the ONLY path in this package with a default, and it is one on purpose: SPEC-UPDATE rule 1 says no path is guessed, and the reason the rule exists is that a guessed path makes two runs mean different things. A receipts directory is the exception because the alternative -- a release lane that silently skips the gate whenever somebody forgets a flag -- fails in the direction that lets a tool ship. It is used only when it EXISTS, and what was used is named on the line.
var DogfoodNote = "cut and build run the dogfood gate FIRST -- `nova-check dogfood gate --cli <reference> --receipts <dir>`, in process -- and refuse on an OPEN EDGE: " + "a verb somebody ran, that did not do what they needed, and that nobody has run since and said it did. " + "A tool is done when it is tested, dogfooded by a non-author on real work, and the feedback is APPLIED; feedback filed is not feedback applied. " + "--cli names the command reference and defaults to docs/CLI.md beside the checkout the verb was already given (--changelog for cut, --source for build). " + "--receipts names the receipts and defaults to ~/" + DefaultReceiptsDir + " when that directory exists. " + "A run with neither is NOT a run that passed: it prints `dogfood-gate=skipped` and names what was missing. " + "The way past an open edge is to fix it, or " + DogfoodWaiveFlag + " " + DogfoodReasonFlag + " <why> -- and the waiver is printed on the line AND written into the CHANGELOG section, because a waiver nobody can find later is a gate nobody has."
DogfoodNote is the gate said where a person will meet it: on `release help`, and on `--help` for the two verbs that run it.
var JourneyNote = "cut runs the journey gate once the head is known: a checkout that ships internal/sprint PROMISES its recovery journeys, and the cut refuses unless --journeys names a `go test -json` run of each, " + "under a first line {\"evidence\":\"" + EvidenceKind + "\",\"revision\":<the sha being tagged>,\"functions\":<v>,\"schema\":<v>,\"installed\":[{\"machine\",\"build\",\"revision\"}]}. " + "Each journey is read on its own and only a pass proves it: owed, skipped, failed and not-run are incomplete, and a green parent over skipped subtests proves nothing. " + "A skip saying `" + PlatformUnavailable + "<platform>` is named, and is not incomplete only for a platform the journey names as optional. " + "The way past is " + JourneyWaiveFlag + " " + DogfoodReasonFlag + " <why>, and every incomplete journey is then written into the CHANGELOG section."
JourneyNote is the gate said where a person meets it.
var PromisedJourneys = []Journey{
{Package: "internal/sprint", Test: "TestEveryFriendFailureShowsWithinItsBound/harness closed: down within 1 minute"},
{Package: "internal/sprint", Test: "TestEveryFriendFailureShowsWithinItsBound/session silent: down within 15 minutes of bus silence"},
{Package: "internal/sprint", Test: "TestEveryFriendFailureShowsWithinItsBound/usage limit: down until the reset, woken after"},
{Package: "internal/sprint", Test: "TestEveryFriendFailureShowsWithinItsBound/bus credential revoked: an alarm on the first failed send"},
{Package: "internal/sprint", Test: "TestEveryFriendFailureShowsWithinItsBound/hold: no card left on him, his cards dealt elsewhere"},
}
PromisedJourneys are the recovery journeys a release that ships the sprint package promises (the chaos suite, friend against the sprint's store, lives with the sprint): each way a friend fails, detected within its bound, his cards dealt elsewhere, recovered (docs/SPEC-FRIEND.md, "Chaos").
var SSHOptions = []string{
"-o", "BatchMode=yes",
"-o", "ConnectTimeout=10",
"-o", "ForwardAgent=no",
}
SSHOptions are the options EVERY invocation carries, in one slice so a test can read the whole policy rather than three call sites (the repository owner, 2026-09-18).
BatchMode so a missing key is a refusal now rather than a password prompt nobody is at the keyboard for. ConnectTimeout so a sleeping bench costs seconds rather than the run. And ForwardAgent=no SAID OUT LOUD rather than left to the default or to whatever ~/.ssh/config on the adopting host says: this verb runs on the one host that holds keys to the whole fleet, and forwarding that agent to a bench would put the fleet's trust inside a machine the release is being pushed TO. A default is not a decision; this is.
There is no -i here and there never will be: a key named on argv is a key in every `ps` on the box. ssh finds its own identity.
var SensitivePaths = []string{
"cmd/nova-sandbox/",
"cmd/nova-secrets/",
"infra/image/",
"pkg/sandbox/",
"pkg/secrets/",
"profiles/",
"tools/sandboxcheck/",
}
SensitivePaths is THE LIST, and this file is the one place in code it exists.
SPEC-RELEASE.md decision 1: a release is the moment work stops being a diff somebody can revert and starts being binaries on every bench in the fleet, so the ranges that touch the parts of this estate a mistake cannot be taken back from -- the secret store, the sandbox that holds a worker and the text of its wall, the image every bench boots -- are not cut on the judgement of whoever is at the keyboard. They are cut after he has read them, and the read is NAMED on the command line so the receipt says who vouched for it.
Each entry is a DIRECTORY PREFIX, trailing slash included, and the slash is load-bearing: `pkg/secrets` without it would also catch `pkg/secrets<sibling>/`, and a list that classifies by accident is a list nobody can reason about. Matching is by prefix and by nothing else -- no guessing from a file name, no substring anywhere in the path.
docs/SPEC-RELEASE.md carries the same list in the same order, and internal/ci's TestTheSensitivePathListIsTheSameInTheCodeAndInTheSpec fails when the two disagree. Two copies of a security list drift, and the copy that drifts is always the one nobody is running; this one is the one that runs, so a path is added HERE and the spec is updated in the same commit.
var SensitiveShape = strings.Join(SensitivePaths, ", ")
SensitiveShape is the list in one phrase, for the help and for a refusal that wants to say what the gate covers. It is COMPOSED from SensitivePaths rather than written out beside it, which is the whole reason the help cannot fall behind the gate.
var SpendNote = "cut runs the spend gate once the previous tag is known: over the release's window (since the previous tag's commit, or --spend-since <RFC3339>, from the start of that UTC day to now), " + "each paid provider's own count of its spend is set beside what the sprint's store (--spend-store <addr>) recorded of it, and each subscription friend's harness receipts (--spend-receipts <file>) beside the tokens the store recorded of them. " + "A gap over 5% of the provider's own figure refuses, printing `SPEND provider=<p> store=<$> provider_usd=<$> gap=<$> share=<%>`; a provider whose readout cannot be read, or a friend with no receipt, refuses naming it, never passes. " + "The providers' keys come from the environment as nova-secrets exec delivers them and are never printed. " + "The way past is " + SpendWaiveFlag + " " + DogfoodReasonFlag + " <why>, and every row that did not pass is then written into the CHANGELOG section."
SpendNote is the gate said where a person meets it.
var SpendStore func(ctx context.Context, addr string, getenv func(string) string) (RecordedSpend, error)
SpendStore reads what the sprint's store at addr recorded, logged in from getenv. The store's tables are nova-sprint's, so this package cannot read them itself: a binary that links nova-sprint's store sets it (cmd/nova-sprint, spend_store.go), and where it is nil (nova-update, since nova-sprint left this repository) the gate is refused as unread, and --no-spend-gate --reason is the way past.
Functions ¶
func Annotation ¶
Annotation is the message the TAG OBJECT carries, composed in one place because it is written by `cut` and read by `adopt` and the two have to agree about where the digest is (the repository owner's decision 2, #1337). A tag is the one thing in this repository that cannot be quietly amended, so what it says about a release is the most durable record the release has. It also carries a waived CI gate, when there was one: the tag is exactly where a person asks why a version shipped red, and the extra lines are appended after the digest so the `sums=` line stays put (docs/SPEC-RELEASE.md section 19).
func ArtifactDir ¶
ArtifactDir is where one platform's binaries for one version live, under the root --out or --from names. The version and the platform are both in the path so that one root can hold several releases and several platforms at once, which is what a build bench serving four benches actually holds.
func ExeSuffix ¶
ExeSuffix is what a tool's FILE is called on one platform: `nova-bus` on unix, `nova-bus.exe` on windows. It takes the TARGET's goos, never the host's, because every one of these names is decided for the machine the binary will run on rather than for the machine deciding it: a release cut for a windows bench names windows files, and a release built ON windows names them the same way.
Reading runtime.GOOS at each of these sites instead is the defect this exists to make impossible, and it is a defect that hides: it is right on the host that happens to match and silently wrong on every other, so the artifacts end up called one thing while everything looking for them asks for another. The product half is `adopt` composing the remote command as a bare `nova-update`, which names a path that does not exist on a windows bench.
func ExecVersion ¶
ExecVersion is the production answer to what the binary at path reports: its own `version` verb, which every tool in this repository answers, under a short deadline because the binary being replaced may be the broken one.
func Ldflags ¶
Ldflags is the stamp a release build carries, the same string tools/ghrelease's ldflags verb composes: one place, so the -X cannot be dropped by an edit to a long build line nobody rereads.
func Main ¶
Main is the production entry: the verb with every seam at its default, and the one thing a seam cannot default to -- what THIS binary is stamped with, which lives in main and is handed down. `adopt` is the reader: a coordinator older than the release it is fanning out cannot run that release's install, and a coordinator far enough behind cannot adopt at all.
func MarkPulled ¶
MarkPulled writes note under version's section and returns the new text. It is pure, so what a withdrawal does to the record is asserted by a test rather than by reading a file somebody edited afterwards.
A section that already carries a note is returned UNCHANGED: a pull run twice -- which is what happens when the first run refused on one machine -- must not stack two notes. A version the changelog does not describe is a refusal: that is somebody pointing --changelog at the wrong file, and writing the note anywhere else would be worse than not writing it.
func Platform ¶
Platform is the goos-goarch an artifact directory is named for. A release built here for this host is the fleet's common case, and --platform is the flag for the other one: cross-compiling to a bench from wherever the release was cut.
func PulledNote ¶
PulledNote is the line a pulled section carries, composed in one place so the mark `pull` writes and the mark it recognises on a second run are the same string.
func ReadDigestFile ¶
ReadDigestFile reads a DigestFile: the sha256 of a release's SHA256SUMS, as `release build` wrote it. The first whitespace-separated token is taken, so a file produced by `sha256sum SHA256SUMS` -- which is `<digest> SHA256SUMS` -- reads too, and a person who made one by hand that way is not punished for it. The path is LOCAL: this function opens a file. A digest computed on the machine holding the bits is not evidence about a fetch.
func ReadPathsFile ¶
ReadPathsFile reads one back, and refuses anything this verb did not write: the wrong first line, the wrong range, a list without the digest line the verb pins its body with, and a body that no longer matches that digest.
func RemoteFrom ¶
RemoteFrom splits a --from that names another machine, as `host:dir`.
THIS IS THE ANSWER TO THE ONE THING THE DOGFOOD PASS COULD NOT DO. adopt was written assuming it runs on the build host and fans out from there; on this fleet it cannot, because no bench has ssh trust to any other bench -- only the the coordinator's machine does, and 3 of 3 machines refused with `Permission denied (publickey)` (receipt 20260918T144929Z, worker-child). The fix that needs NO NEW TRUST is to run adopt from the host that already has it and let it read the artifacts from the host that built them. A jump host (`ssh -J`) would not have helped: -J forwards the connection but still authenticates to the target with the CALLING host's key, so fanning out from a host would still need that host's key on every bench -- new trust between benches, which is the thing we do not want, and the coordinator's machine is a friend's and not ours to hand out keys for.
The host part must be a machine name of at least two characters, so a windows path (`C:\releases`) reads as a local path rather than as a host called C. That is the one ambiguity a colon introduces, resolved in favour of the local path because it is the common case and the mistake is loud either way.
func RemotePath ¶
RemotePath is the form a path takes INSIDE a remote command, and the only place that decision is made: every backslash folded to a forward slash.
THE FAR SIDE PARSES A POSIX COMMAND LINE, on windows as everywhere else. docs/BENCH-WINDOWS.md names the windows bench's ssh shell as Git Bash (`C:\Program Files\Git\bin\bash.exe`), or native OpenSSH with Bash in sshd_config, and the windows checks in pulse/fleetstandard.go at 39e472aa0 are POSIX shell that reach for `powershell.exe -NoProfile -Command '...'` only for the questions only PowerShell can answer. In that shell a backslash is an ESCAPE: `C:\Users\nova` arrives as `C:Usersnova`, silently, and the machine then refuses about a path nobody typed -- which is the worst kind of refusal, because the remedy it suggests is to fix a path that was already right. Windows accepts a forward slash in every API and in every one of its own shells, so the fold costs nothing and removes the whole class.
A unix path can carry no backslash -- remotePathShape has never allowed one -- so this changes nothing about any path that is not a windows one.
func Run ¶
Run is `nova-update release <verb>`. The verb is dispatched here and each of the four validates its own flags, so a missing flag is named by the verb that wanted it rather than by a shared check that knows about all of them.
func Sensitive ¶
Sensitive returns the paths of files that sit under one of SensitivePaths, deduplicated and sorted, so that a refusal naming them reads the same way twice and a receipt counting them counts files rather than mentions.
func SumsInAnnotation ¶
SumsInAnnotation reads the digest back out of a tag's message, or "" when the tag carries none -- which is what a release cut before decision 2, or one cut without --sums, looks like from here.
func ToolFile ¶
ToolFile is the file one tool installs under on the target platform. Every place in this package that turns a tool NAME into a FILE goes through it.
func Tools ¶
Tools lists the shipped set: every cmd/nova-* directory in the source tree, discovered by walking it rather than from a list. release.yml makes the same argument at greater length -- a tool added tomorrow ships on the day it appears rather than on the day somebody remembers a list.
func ValidRemotePathOn ¶
ValidRemotePathOn refuses anything that could be more than a path on the far side. It also refuses a RELATIVE path, because the binary this verb runs there must be named absolutely: a relative path resolves against whatever directory the remote shell happens to start in, and a bare name would resolve against $PATH -- which is how a machine ends up running a nova-update that is not the one just verified and sent.
goos is the TARGET's, and the only thing it decides is whether the drive form is a path at all. `C:\Users\nova\.local\bin` on a linux bench is not a path that bench has; taking it would compose a remote command whose first token cannot exist, and the machine would answer `command not found` for a mistake made on this side. So the drive form is allowed for the windows target and refused, by name, for every other.
func ValidSecurityRead ¶
ValidSecurityRead holds --security-read to the field law before anything is tagged. A read nobody could print is a read nobody could look up, and the whole value of naming it is that a person reading the receipt in six months can go and find what he actually said.
func ValidVersion ¶
ValidVersion holds a release version to the same shape tools/ghrelease's ldflags verb refuses at, and for the same reasons: the string travels into `-X main.version=`, into a printf format, and into a one-line field. Whitespace splits the linker flag, `%` reads a directive that was never supplied, and `=` is what both the stamp assertion and pkg/oneline treat as a separator -- so a version carrying any of them is a version no later step could check. Refused here, before anything is built or tagged.
func VerbUsage ¶
VerbUsage is the one usage line for one release verb, so that `--help` on a verb answers about THAT verb. A person who asked about `adopt` did not ask to re-read `cut`.
func VerifyArtifacts ¶
VerifyArtifacts checks every artifact in dir against the checksums the build recorded. It is one function because two callers need exactly this: `install` before its first rename, and `adopt` after fetching a release from another machine -- a truncated fetch caught once on the adopting host is one refusal rather than one per machine. It returns HOW MANY it checked, and `build` prints that number rather than the length of the list it was given. The two are equal when the check ran and there is no way to print the number without running it -- which is the point: `verified=21` computed from a slice length would be a claim about work that may not have happened, and a claim like that is worse than no claim.
func VersionsUnder ¶
VersionsUnder lists the releases an artifact root holds for one platform: a directory whose <version>/<goos>-<goarch>/SHA256SUMS exists. A directory that is not a release is not a candidate, so a stray folder never becomes one.
func WritePathsFile ¶
WritePathsFile records the complete list, the range it is the list for, and the digest of the list itself. The digest is computed with the package's own sumOf (incremental.go).
Types ¶
type Artifact ¶
Artifact is one shipped binary: the name it installs under and the checksum the build recorded for it.
type CheckRun ¶
type CheckRun struct {
Name string
Status string // queued, in_progress, completed
Conclusion string // success, failure, cancelled, timed_out, skipped, neutral
}
CheckRun is one CI check on a commit, as the forge reports it: the name a person reads in the checks list, the run's status, and its conclusion once it has one. A run that has not completed has an empty Conclusion, which is why both fields are here -- a pending run is not a green one, and reading only the conclusion would make it look like a neutral one.
type Commit ¶
Commit is one commit in a range, with the whole message: the subject carries the pull request number a squash merge writes, and the body of an integration batch carries the members it rolled up. Both are read from this one field, so the whole changelog is one call to the forge rather than one call per pull request.
type Deps ¶
type Deps struct {
Forge Forge
SSH SSH
Toolchain Toolchain
// Git is the local checkout `cut --local-diff` reads the complete path
// list out of when the forge's compare is at its ceiling.
Git Git
// Dogfood is the definition-of-done gate `cut` and `build` run first. A
// nil Dogfood is ReadDogfood, which reads the command reference and the
// receipts off disk and reaches nothing else.
Dogfood Dogfood
// Journeys is the recovery journeys `cut` holds the release to
// (journeygate.go). A nil Journeys is the checkout's own promise:
// PromisedJourneys, for each package the checkout ships.
Journeys []Journey
// Spend is the spend gate's readouts (spendcheck.go): the store's recorded spend,
// each paid provider's own, the subscription friends' receipts. A nil Spend is
// ProductionSpend, from --spend-store and --spend-receipts and the environment.
Spend *SpendSources
Now func() time.Time
// Self answers what the nova-update RUNNING THIS is stamped with. It is a
// seam rather than a constant because this package is a library and the
// stamp lives in main; a nil Self means `adopt` cannot compare its own
// version with the release's and does not pretend to.
Self func() string
// VersionOf answers what the binary at path reports for itself. It is a
// seam because `install`'s skip decision is the one place this package
// runs a binary it is about to replace.
VersionOf func(ctx context.Context, path string) (string, error)
// Source is the checkout `build` records and `--incremental` diffs
// (incremental.go); nil is ExecSource.
Source Source
// Ansible runs the tools play for `cycle` (cycle.go); nil is
// ExecAnsible with --ansible.
Ansible Ansible
}
Deps are the seams. A zero Deps is the production one: the forge is gh, the remote is ssh, the compiler is go, the clock is the machine's. A test fills in what it needs and nothing it fills in can reach the network.
type Dogfood ¶
type Dogfood func(cli, receipts, cmd string) (DogfoodVerdict, error)
Dogfood is the seam. A nil Dogfood in Deps is the production one, which reads the reference, the receipts and the cmd/ directory off disk and reaches nothing else -- no forge, no network, no shell.
type DogfoodVerdict ¶
type DogfoodVerdict struct {
Verbs int
Open int
// Shipped is how many tools the gate judged, and Outside how many
// receipts it set aside because they name a tool the release does not
// ship. Both are said on the line: evidence left out is counted.
Shipped int
Outside int
// Findings are the gate's own lines, one per open edge, in the words
// `nova-check dogfood gate` prints them. The release lane says what the
// gate says rather than paraphrasing it: two spellings of one finding is
// one of them going stale.
Findings []string
}
DogfoodVerdict is what the gate answers: what it read, and what it found.
func ReadDogfood ¶
func ReadDogfood(cli, receipts, cmd string) (DogfoodVerdict, error)
ReadDogfood is that production gate. cmd is the checkout's cmd/ directory: the tools under it are the shipped set, and the gate judges only them. An empty cmd judges every tool the receipts name, which is the stricter read.
type EvidenceHeader ¶
type EvidenceHeader struct {
Evidence string `json:"evidence"`
Revision string `json:"revision"`
Functions string `json:"functions"`
Schema string `json:"schema"`
Installed []InstalledReceipt `json:"installed"`
}
EvidenceHeader is the evidence file's first line. Every field is required: a journey that passed is proof about one revision, one installed build and one set of function and schema versions, and evidence that cannot say which is proof about nothing in particular.
type ExecAnsible ¶
type ExecAnsible struct{ Path string }
ExecAnsible is the production play runner: the named ansible-playbook, stdin closed (no prompt can wait for a person), stdout and stderr together.
type ExecGit ¶
type ExecGit struct{}
ExecGit is the production checkout reader: one `git -C <dir> diff --name-only <base>...<head>`, which is a READ and the only git this package ever runs. It is here rather than in cut.go for the same reason gh and ssh are: every edge to a subprocess is in this file, where it can be read whole.
type ExecSSH ¶
ExecSSH is the production remote. The ssh binary is named by --ssh rather than found on PATH, because "no cwd dependence, every path a flag" applies to the program as much as to the directories: a bench with two ssh binaries should not be a coin toss.
Guard is the per-test host guard this seam consults: a test arms an isolated testguard.NewGuard(true) as a field on the value under test, so it needs neither t.Setenv nor a process-wide reload. The nil default is the production path: the package-level guard, armed from NOVA_TEST_NO_HOST by `make test`.
func (ExecSSH) Fetch ¶
Fetch reads a directory FROM the machine into a local one, the mirror of Send: the remote tars to stdout and the stream is unpacked here, in Go, so the entry names are checked by this process rather than trusted to a local tar. It is what makes `--from host:dir` work -- the host that has the ssh trust adopting a release that lives on the host that has the cores.
func (ExecSSH) Run ¶
Run executes argv on the machine. The arguments are handed to ssh as separate argv entries; the remote's own shell still reassembles them, so every value that reaches here has been checked by its caller (the machine name against machineName, the version against ValidVersion, the paths by the flags that named them).
func (ExecSSH) Send ¶
Send copies a directory to the machine as a tar stream on ssh's stdin. The tar is written HERE, in Go, rather than by a local `tar` piped into a remote one: the thing this replaces was two ssh invocations joined by a shell pipe, where a failure in the first was invisible to the second.
type ExecSource ¶
type ExecSource struct{}
ExecSource is the production Source: git and go in the checkout, each a read.
type FileReceipts ¶
type FileReceipts struct{ Path string }
FileReceipts is the subscription friends' harness receipts as one file: {"evidence":"spend-receipts","from":<RFC3339>,"to":<RFC3339>,"friends":{"<friend>":<tokens>}}, the tokens each friend's harness counted over [from, to). It is read only for a window it covers: from the window's start, to within an hour of its end.
func (FileReceipts) Tokens ¶
func (f FileReceipts) Tokens(_ context.Context, w SpendWindow) (map[string]int64, error)
Tokens is the file's tokens by friend, or why they cannot be read for the window.
type Forge ¶
type Forge interface {
// HeadSHA resolves a branch to the commit it points at.
HeadSHA(ctx context.Context, repo, branch string) (string, error)
// CheckRuns reads every check run recorded against one commit.
CheckRuns(ctx context.Context, repo, sha string) ([]CheckRun, error)
// Tags lists the repository's tag names, unordered; the caller picks.
Tags(ctx context.Context, repo string) ([]string, error)
// Compare lists the commits in base..head, oldest first.
Compare(ctx context.Context, repo, base, head string) ([]Commit, error)
// Files lists the paths base..head touched. It is a separate question
// from Compare because it is asked for a separate reason -- the
// classification `cut` makes against SensitivePaths -- and because its
// answer carries a ceiling of its own (CompareFileCap) that the commit
// list does not.
Files(ctx context.Context, repo, base, head string) ([]string, error)
// Tag creates an ANNOTATED tag at sha: a tag OBJECT carrying message,
// then the ref pointing at that object. It is the one mutation on this
// interface and the only one `cut` performs.
Tag(ctx context.Context, repo, tag, sha, message string) error
// TagMessage reads an existing annotated tag's message back. It is how
// `adopt` learns the digest a release was cut with without anybody
// retyping it: a tag object is a git object, so its message reached the
// adopting host through the repository rather than through the machine
// whose bits are being checked against it.
TagMessage(ctx context.Context, repo, tag string) (string, error)
}
Forge is the edge between this tool and GitHub. Seven questions, one gh invocation each -- Tag is two, for the reason it gives -- and every one of them is a read except Tag.
type GH ¶
GH is the production forge: one `gh` invocation per question, each under the run's deadline. gh carries its own credential; nothing here reads, logs or passes one.
func (*GH) CheckRuns ¶
CheckRuns reads every check run recorded against a commit, paginated, because this repository runs more than a page of them on a self-hosted fleet and a first page read as the whole set is a green nobody checked.
func (*GH) Compare ¶
Compare lists the commits between two revisions. ONE call answers the whole changelog: a squash merge's message carries the pull request's title in its subject and its body underneath, so the alternative -- list the merged pull requests, then read each one -- is a hundred calls for the same text.
func (*GH) Files ¶
Files lists the paths a compare range touched. One call, and the names are deduplicated here because a paginated compare repeats the diff's file list on each page it answers with.
THE ANSWER IS BOUNDED BY THE FORGE at CompareFileCap files, which is why the caller checks for a list at exactly that number rather than trusting a short one; see CompareFileCap for what that means for the classification.
func (*GH) HeadSHA ¶
HeadSHA asks the forge, not a local checkout: the commit a release is cut from is the one the forge believes main is at, and a local clone can be behind it by exactly the merge somebody is about to release.
func (*GH) Tag ¶
Tag creates the annotated tag. It is a create, never a force-move: a tag that can be moved is a tag whose binaries and whose source can disagree, which is the one state release.yml spends thirty lines refusing.
func (*GH) TagMessage ¶
TagMessage reads an annotated tag's message. Two reads: the ref, to learn what it points at, and then the object. A ref pointing at a COMMIT is a lightweight tag -- every tag this tool made before decision 2 -- and it is refused by name rather than answered with an empty message, because "this tag carries no annotation" and "this annotation carries no digest" are two different facts with two different remedies.
func (*GH) TagTime ¶
TagTime is when the tag's commit was made, as the forge says it: the start of the spend window since that tag.
type Git ¶
type Git interface {
// DiffNames is `git -C dir diff --name-only base...head`: the paths the
// range touched, THREE dots, so it is what head carries since the merge
// base rather than every difference between two branches.
DiffNames(ctx context.Context, dir, base, head string) ([]string, error)
}
Git is the edge to a LOCAL CHECKOUT, and it exists for exactly one question: which paths a range touched, when the forge cannot say.
The forge answers a compare with at most CompareFileCap files, and the sensitive-path gate cannot be run on a prefix of the truth. git in a checkout has no such ceiling. The answer is produced BY THIS VERB rather than pasted in by a person, because a classification gate whose input is hand-written is a gate whose input is whatever somebody remembered.
type GoBuild ¶
type GoBuild struct{}
GoBuild is the production toolchain. CGO is off so the artifact runs on a bench whose libc is not this one's, and the arguments the caller composed -- -trimpath and the -ldflags stamp -- are passed through untouched.
func (GoBuild) Build ¶
func (GoBuild) Build(ctx context.Context, source, pkg, out, goos, goarch string, args []string) (string, error)
Build compiles one package to one output path.
type InstalledReceipt ¶
type InstalledReceipt struct {
Machine string `json:"machine"`
Build string `json:"build"`
Revision string `json:"revision"`
}
InstalledReceipt is one machine the journeys ran against and the build it had installed, at the revision that build was made from.
type Journey ¶
type Journey struct {
// Package is the test's package, relative to the module root, such as
// internal/sprint. A checkout without it does not ship the capability, and
// does not promise its journeys.
Package string
// Test is the test's name as the source spells it, Parent/subtest, with the
// spaces `go test` turns into underscores.
Test string
// Optional names the platforms whose absence is a skip rather than a broken
// promise: a journey that needs a real harness on a windows bench may say the
// bench did not answer.
Optional []string
}
Journey is one promised recovery journey: a test that has to have run and passed at the release revision.
type JourneyRecord ¶
type JourneyRecord struct {
State string
Revision string
Header EvidenceHeader
Proven int
Reason string
Incomplete []JourneyResult
}
JourneyRecord is what the gate leaves for the CHANGELOG section: the bound evidence when it passed, the incomplete journeys when it was waived.
type JourneyResult ¶
JourneyResult is what the evidence says about one promised journey.
type Machine ¶
type Machine struct {
Name string
// Bin and Dest override --bin and --dest for this machine. Empty means
// the flag's value, which is the ordinary case.
Bin, Dest string
}
Machine is one line of the --machines file: which machine, and optionally where ITS tools go. The fleet has three different home directories, so a single --bin is right for most machines and wrong for one; the columns are how that one is said in the file rather than by a second run with different flags -- which is a second chance to get the version wrong.
func Machines ¶
Machines reads the machine list. MachinesShape is that format said once: one machine per line, optionally followed by TAB-separated --bin and --dest overrides for that machine, blanks and `#` comments skipped, every name checked before ssh is reached. The file is a flag because the fleet is not a constant -- it was four benches, then five, and the day the iMac Pro joined nothing in a tool should have needed editing.
type NoReadout ¶
type NoReadout struct{ Name, Why string }
NoReadout is a provider whose own count cannot be read: every window is unread, with why.
type OneMachine ¶
type OneMachine struct {
// Version is the release to adopt: the one the coordinator's machine runs.
Version string
// Flags are adopt's flags but --machines, --version and --dry-run, as the
// coordinator would type them (--ssh, --from, --bin, --dest, the stage's
// digest, --no-certify or the certification's three).
Flags []string
// Dir is where the one-machine lists are written; "" is os.TempDir.
Dir string
// Deps are adopt's (Run); the zero value is the real ssh and clock.
Deps Deps
// contains filtered or unexported fields
}
OneMachine adopts one release onto one machine at a time, through adopt itself: the sprint's tick hands it a fleet member back from down (docs/SPEC-SPRINT.md section 5, "Back from down: adopt the latest"; docs/SPEC-RELEASE.md, "Adopting one machine"). An adoption is three runs of adopt with a machine list of that machine alone: --dry-run, which reads the version it has installed (installed= of RELEASE WOULD ADOPT); the adopt; and --dry-run again, which reads the version back. The tick calls Start and Adoption from inside a plan, so the runs go on beside it, and one machine has at most one adoption in flight.
func (*OneMachine) Run ¶
func (o *OneMachine) Run(machine, episode string) (OneRun, bool)
Run is the machine's adoption of the episode, false when there is none.
func (*OneMachine) Start ¶
func (o *OneMachine) Start(machine, version, episode string) bool
Start begins the adoption of version on machine for the episode, beside the caller. It is false, and starts nothing, while an adoption of that machine is in flight, or when the machine already has a run of the episode.
type OneRun ¶
OneRun is a machine's adoption of one episode: running until it ends, the version installed before and read back after, and why it failed.
type OpenRouterSpend ¶
type OpenRouterSpend struct {
RT http.RoundTripper
Getenv func(string) string
}
OpenRouterSpend is openrouter's own count of a window: the activity's days from the window's first through yesterday, and the key's count of today when the window reaches it.
func (OpenRouterSpend) Provider ¶
func (OpenRouterSpend) Provider() string
Provider is "openrouter".
func (OpenRouterSpend) Spend ¶
func (r OpenRouterSpend) Spend(ctx context.Context, w SpendWindow) (float64, error)
Spend is the account's dollars over the window.
type PR ¶
PR is one merged pull request as the changelog prints it. Members is the list of pull requests an integration batch rolled up, read from the batch's own body: a batch line that named only the batch would hide ten pieces of work behind one number.
func PullRequests ¶
PullRequests reads a compare range as a changelog. One commit whose subject ends in `(#n)` is one pull request; a `Merge pull request #n from ...` subject is one too, with its title read from the body. The rest of the message, if it names other pull requests, is that entry's member list.
type ProviderSpend ¶
type ProviderSpend interface {
Provider() string
Spend(ctx context.Context, w SpendWindow) (float64, error)
}
ProviderSpend is one paid provider's own count of what was spent with it over a window, in dollars. An error is a readout that could not be read, and refuses.
func SpendReaders ¶
func SpendReaders(rt http.RoundTripper, getenv func(string) string) []ProviderSpend
SpendReaders is the paid providers' readouts over the transport (nil is http.DefaultTransport), their keys read from getenv: openrouter's account activity, and the providers whose own count no endpoint answers, each an error naming why.
type RecordedSpend ¶
type RecordedSpend interface {
Providers(ctx context.Context, w SpendWindow) ([]string, error)
Spend(ctx context.Context, provider string, w SpendWindow) (float64, error)
Tokens(ctx context.Context, w SpendWindow) (map[string]int64, error)
}
RecordedSpend is what the sprint's store recorded over a window: the paid providers it knows of, its dollars of each, and its tokens of each subscription friend.
type SSH ¶
type SSH interface {
// Run executes argv on machine and returns its combined output.
Run(ctx context.Context, machine string, argv []string) (string, error)
// Send copies the local directory tree at dir to dest on machine.
Send(ctx context.Context, machine, dir, dest string) (string, error)
// Fetch copies the directory dir ON machine into the local directory
// dest. It is how the host that has the trust reads a release built on
// the host that has the cores, without anybody copying it by hand.
Fetch(ctx context.Context, machine, dir, dest string) (string, error)
}
SSH is the edge to another machine: run a command there, or put a directory there. Both take argv rather than a command line, because the thing this package replaces built its remote commands by pasting shell into shell and the quoting was the part nobody could read.
type Source ¶
type Source interface {
// Head is the commit the checkout is at and whether its tree is clean:
// no change to a tracked file and no untracked file.
Head(ctx context.Context, dir string) (commit string, clean bool, err error)
// Changed is every path that differs between two commits' trees.
Changed(ctx context.Context, dir, base, head string) ([]string, error)
// Packages answers, for one platform, each package's directories relative
// to dir: its own and every non-standard package it imports, transitively.
Packages(ctx context.Context, dir, goos, goarch string, pkgs []string) (map[string][]string, error)
// GoVersion is the version of the go that builds the release.
GoVersion(ctx context.Context) (string, error)
}
Source is the edge an incremental build asks: where the checkout is, what changed since a recorded commit, which package directories each tool is built from, and which Go builds it. A nil Source in Deps is ExecSource.
type SpendRow ¶
type SpendRow struct {
Kind string
Name string
Store float64
Own float64
Gap float64 // Own less Store
Unread string
}
SpendRow is one comparison: a paid provider's dollars (Kind "provider") or a subscription friend's tokens (Kind "friend"). Unread says why the provider's own figure could not be read, "" when it was.
type SpendSources ¶
type SpendSources struct {
Store RecordedSpend
Providers []ProviderSpend
Receipts TokenReceipts
}
SpendSources are the gate's three readouts. A nil Receipts is no receipts readout: a subscription friend the store recorded is then a refusal naming it.
func ProductionSpend ¶
func ProductionSpend(ctx context.Context, o options, w SpendWindow) (SpendSources, error)
ProductionSpend is the gate's sources from the cut's flags: the store at --spend-store, the providers' readouts (SpendReaders), the receipts at --spend-receipts.
type SpendVerdict ¶
type SpendVerdict struct {
Window SpendWindow
Rows []SpendRow
}
SpendVerdict is what the gate found over its window.
func CheckSpend ¶
func CheckSpend(ctx context.Context, src SpendSources, w SpendWindow) (SpendVerdict, error)
CheckSpend sets each paid provider's own spend over the window beside the store's, and each subscription friend's receipts beside the store's tokens of them. The providers checked are every one the store knows of and every one a readout is given for; one with no readout is unread. An error is the store unread, which refuses whole.
func (SpendVerdict) Refused ¶
func (v SpendVerdict) Refused() []SpendRow
Refused is the rows that refuse the cut.
type SpendWindow ¶
SpendWindow is the release's window: [From, To), From at the start of its UTC day (the providers count by the UTC day).
func SpendWindowFrom ¶
func SpendWindowFrom(since, now time.Time) SpendWindow
SpendWindowFrom is the window from since to now, since taken back to the start of its UTC day.
func (SpendWindow) String ¶
func (w SpendWindow) String() string
String is the window as a line field says it.
type TagTimer ¶
TagTimer is a forge that can say when a tag's commit was made: the start of the spend window. GH is one.
type TokenReceipts ¶
type TokenReceipts interface {
Tokens(ctx context.Context, w SpendWindow) (map[string]int64, error)
}
TokenReceipts is the subscription friends' harnesses' own counts of the tokens each used over a window, by friend. An error is receipts that could not be read, and refuses.
type Toolchain ¶
type Toolchain interface {
Build(ctx context.Context, source, pkg, out, goos, goarch string, args []string) (string, error)
// Platforms is every `goos/goarch` this toolchain can compile for, as
// `go tool dist list` prints it. It is ASKED rather than written out in
// this file because a list here is a list that is right on the day it is
// written -- and the fourth release dogfood found out what the other kind
// costs: `--platform darwin-arm64,darwin-amd64` reached the compiler
// whole, failed at tool 1 of 21 with the compiler's own `unsupported
// GOOS/GOARCH pair`, and left an empty directory of that name in the
// release tree for somebody to find later.
Platforms(ctx context.Context) ([]string, error)
}
Toolchain is the edge to `go build`. The arguments are handed over whole, so that a test asserting -trimpath and the -ldflags stamp is asserting the exact strings the compiler is given rather than a summary of them.