Documentation
¶
Overview ¶
Package update finds, installs, and restarts onto codeaf releases.
Index ¶
- Constants
- Variables
- func AutoStatePath(profileDir string) string
- func ChannelTag(channel, date, sha string) (string, error)
- func CleanupOld(string)
- func CleanupOldRunning(func() (string, error))
- func CompareChannelBuilds(running string, runningPublished time.Time, selected string, ...) (int, bool)
- func CompareSemverTags(left, right string) (int, bool)
- func CurlLine(executable, channel string) string
- func DismissRelease(profileDir, tag string) error
- func ExecutableTarget(executable func() (string, error)) (string, error)
- func FollowedChannel(running string) string
- func InstallRefusal(target, curl string) string
- func Kind(tag string) string
- func NextSemverTag(tags []string, channel, component string) (string, error)
- func PackageManaged(target string) string
- func ParseChannel(tag string) (channel, date string, ok bool)
- func Restart(plan Plan) error
- func RestartArgs(original []string, transcript string) []string
- func SaveAutoState(profileDir string, state AutoState) error
- func ShouldOffer(state AutoState, tag string, now time.Time) bool
- func ValidDate(value string) bool
- func ValidSHA(value string) bool
- type AutoState
- type Available
- type CheckOptions
- type Choice
- type Client
- type InstallLock
- type InstallOptions
- type InstallResult
- type Plan
- type RCTag
- type RefusalError
- type Release
- type Version
Constants ¶
const ( // CurlCommand is the independent installation road shown at launch beside // /update and after a failed or unavailable in-place update. IT IS THE // SUPPORTED LINE, NOT THE SCRIPT UNDER IT: agentfield.ai/get/codeaf serves // scripts/install.sh from main, and the address is the one a person can // remember and the one every other place codeaf documents spells, so a // launch line and the README can never hand out two different roads. CurlCommand = "curl -fsSL https://agentfield.ai/get/codeaf | bash" NoUpdateCheckEnv = "CODEAF_NO_UPDATE_CHECK" GitHubAPIEnv = "CODEAF_GITHUB_API" GitHubDownloadEnv = "CODEAF_GITHUB_DOWNLOAD" )
const ( // AutoGrace is how long a launch offer waits before the automatic answer. // Nothing is blocked while it runs. AutoGrace = 10 * time.Second )
The auto updater's durable memory: one small file in the profile holding the release a person dismissed, the release that failed and how often, and the backoff after that failure. An unreadable file reads as empty state, because the worst case of forgetting is one repeated offer.
const ( // CheckTimeout is the whole-exchange budget for a launch check and for // `codeaf update --check`. THE CHECK CLOCK NEVER COVERS AN INSTALL: a // release asset is allowed to make steady progress for much longer. CheckTimeout = 3 * time.Second )
Variables ¶
var ErrInstallInFlight = errors.New("another codeaf is already installing an update")
ErrInstallInFlight means another process holds the target's lock.
Functions ¶
func AutoStatePath ¶
AutoStatePath names the coordinator's file in the profile.
func ChannelTag ¶
ChannelTag forms a dev or staging tag from a date and revision.
func CleanupOld ¶
func CleanupOld(string)
CleanupOld removes nothing on systems where the running executable can be replaced directly.
func CleanupOldRunning ¶
CleanupOldRunning does nothing where replacing a running executable leaves no backup. In particular, it does not resolve the executable during launch.
func CompareChannelBuilds ¶ added in v0.4.0
func CompareChannelBuilds(running string, runningPublished time.Time, selected string, selectedPublished time.Time) (int, bool)
CompareChannelBuilds compares a running build with the release selected by the API from the same channel. A negative result means the selected release is newer; a positive result means the running build is ahead.
func CompareSemverTags ¶
CompareSemverTags orders stable and release-candidate tags. A stable tag follows every release candidate on its own version line.
func CurlLine ¶ added in v0.4.0
CurlLine returns the independent installation road for the running file and release channel. The product remains codeaf; only the destination file name changes when a differently named executable asks for its own road back.
func DismissRelease ¶
DismissRelease records "not now" for one exact release. A lock or write that failed is returned, because the surface may not report a skip it did not save.
func ExecutableTarget ¶
ExecutableTarget resolves the running binary before an installer replaces it.
func FollowedChannel ¶ added in v0.4.0
FollowedChannel names the release channel a build takes its updates from. IT IS NOT ALWAYS THE CHANNEL THE TAG WAS CUT ON: a release candidate follows STABLE, because its launch line, its bare /update and its bare `codeaf update` all speak about the stable release its line is heading for. A build from source follows stable too, so the road it is offered is the ordinary one. Only dev and staging follow themselves. Every place that has to answer "which channel is this build on" asks here, so the launch notice and the curl line under it can never name two different roads.
func InstallRefusal ¶
InstallRefusal names why this target may not be replaced in place — a package manager owns it, or its directory is not writable — or "" when an ordinary install may proceed. The curl line is the road the refusal offers instead.
THE TARGET IS CANONICALIZED FIRST, exactly as Install canonicalizes it: a launcher that hands in a symlink (or a folder reached through one) must answer about the file the replacement would land on, or a managed install answers "ordinary" while the installer refuses it a moment later.
func NextSemverTag ¶
NextSemverTag returns the next stable or release-candidate tag.
func PackageManaged ¶
PackageManaged names the package manager that owns a path, or "" when the path is an ordinary install this program may replace. A brew, distro or nix install must go through its manager, not through an in-place replacement that the next upgrade would quietly undo.
func ParseChannel ¶ added in v0.4.0
ParseChannel returns the channel and calendar date carried by a dev or staging tag. The date is kept as YYYYMMDD because that spelling sorts in the same order as the days it names.
func RestartArgs ¶
RestartArgs keeps the original launch shape and points it at this transcript.
func SaveAutoState ¶
SaveAutoState writes the coordinator's file atomically. It is the raw write and does NOT take the profile lock: a caller with a read-modify-write to make goes through [mutateAutoState], which holds both locks across the read and the write.
func ShouldOffer ¶
ShouldOffer reports whether the automatic updater may offer this release now: not dismissed, attempts not exhausted, and any backoff passed. A manual door ignores all three.
Types ¶
type AutoState ¶
type AutoState struct {
// DismissedRelease is the tag a person said no to, by chord or by command.
DismissedRelease string `json:"dismissed_release,omitempty"`
DismissedAt time.Time `json:"dismissed_at,omitempty"`
// FailedRelease, Failures and LastAttemptAt bound automatic retries.
FailedRelease string `json:"failed_release,omitempty"`
Failures int `json:"failures,omitempty"`
LastAttemptAt time.Time `json:"last_attempt_at,omitempty"`
LastError string `json:"last_error,omitempty"`
}
AutoState is what the background updater remembers between launches.
func AutoFailure ¶
AutoFailure records one failed automatic install of a release. Its signature is unchanged — the caller only wants the note on the surface — but a write that did not land returns what the file really holds rather than a fabricated count, and a failure recorded by another window is not clobbered: the whole state is read and rewritten under the lock.
func AutoSuccess ¶
AutoSuccess clears the failure belonging to a release now installed.
IT DOES NOT CLEAR A DISMISSAL. The person's "not now" for another tag is a separate decision and survives — the read-modify-write reads the whole state and empties only the failure fields — and doing so under the profile lock is what keeps a concurrent skip from being erased by a success that read the file a moment too early.
func LoadAutoState ¶
LoadAutoState reads the coordinator's file; a missing or corrupt one is empty.
func (AutoState) Blocked ¶
Blocked reports whether automatic installation of this release has been tried too many times already.
type Available ¶
type Available struct {
Latest string
Running string
LatestPublished time.Time
RunningPublished time.Time
Curl string
}
Available is the release comparison shown at launch or by --check.
func CheckLaunch ¶
func CheckLaunch(ctx context.Context, options CheckOptions) (Available, bool)
CheckLaunch silently checks the release channel this build follows. Every failure is absence at this surface.
func (Available) Ahead ¶ added in v0.4.0
Ahead reports whether this build is newer than the release selected from its own channel. Source builds and incomparable channels are never called ahead.
type CheckOptions ¶
type CheckOptions struct {
Running string
ProfileDir string
Client *Client
Now func() time.Time
Disabled bool
Executable string
}
CheckOptions supplies the launch policy's disk, clock, and release client.
type Choice ¶
type Choice struct {
Channel string
Version string
Running string
// Published is THE MOMENT THE CALLER ALREADY KNOWS this exact release was
// published, and it means something only beside Version.
//
// A PINNED TAG IS RESOLVED WITHOUT THE API, so the release it answers would
// carry no publish moment at all — and the install record written from it
// could not be ordered against the file on disk, which is how a stale
// same-day dev or staging build gets written over a newer one
// ([recordSupersedes]). The road that pins has the fact already: the launch
// check read it from the API and the offer kept it, so it rides here rather
// than being guessed at under the lock. A caller resolving a CHANNEL leaves
// it zero, because the API's own answer is the fact on that road.
Published time.Time
}
Choice selects one release channel or one exact tag.
type Client ¶
type Client struct {
HTTP *http.Client
APIBase string
DownloadBase string
Token string
Revision string
CheckWindow time.Duration
APIWindow time.Duration
StallWindow time.Duration
DownloadCeiling time.Duration
}
Client talks to the release API and release asset host.
func NewClient ¶
NewClient builds the release client from the same mirror and token inputs as the shell installer. The timeout belongs only to the small release check; every install request carries the clock for the particular thing it reads.
func (*Client) Check ¶
Check selects one release inside the short whole-exchange window shared by the launch notice and the terminal's explicit check. Installation calls Select directly, where each metadata request receives its own API window.
func (*Client) Select ¶
Select resolves a channel with the same timestamp law as the shell installer.
func (*Client) TagPublished ¶
TagPublished asks the release API for ONE exact tag's publish moment, and it is BEST-EFFORT BY CONTRACT. The moment is what the install record carries and what orders two same-day dev or staging builds ([recordSupersedes]), so a NAMED tag — which [Select] answers without the API — is worth one bounded lookup before its install writes that record. A tag the API cannot answer for — offline, rate-limited, deleted, or a release the API has not indexed — leaves the moment UNKNOWN, which is never guessed at and never fails the install: the file is still there to fetch, and a person naming it means it.
type InstallLock ¶
type InstallLock struct {
// contains filtered or unexported fields
}
InstallLock is a held lock on one executable target.
func TryTargetLock ¶
func TryTargetLock(target string) (*InstallLock, error)
TryTargetLock claims the lock for one executable target, returning ErrInstallInFlight when another process holds it. The target is canonicalized first, so an alias of a file already held answers in flight rather than opening a second lock file beside a path nothing else names.
func (*InstallLock) Record ¶
func (l *InstallLock) Record() (installRecord, bool)
Record reads the last install this target recorded, and reports false unless the record still matches the file on disk. A tag written by bytes that are no longer there is not a fact about the file.
THE BYTES COME THROUGH THE HELD HANDLE, and the read does not move its cursor (io.NewSectionReader). A second handle would be a second look-up of a name another process may be replacing, and on Windows it would be unreadable to the process that locked it: LockFileEx's own documentation says that a process which locks a region with one handle — which is what [lockFileExclusive] does — cannot access that region through a second handle until it unlocks it (learn.microsoft.com/windows/win32/api/fileapi/nf-fileapi-lockfileex). A record written by the lock holder would then be the one thing it could not read, so the record is read from the handle that holds the lock.
func (*InstallLock) RecordInstalled ¶
func (l *InstallLock) RecordInstalled(tag, digest string, published time.Time)
RecordInstalled writes what this target now holds. The caller holds the lock and has just written those bytes.
func (*InstallLock) Release ¶
func (l *InstallLock) Release()
Release drops the lock. It is safe on a nil lock and safe to call twice.
type InstallOptions ¶
type InstallOptions struct {
Client *Client
Release Release
Target string
Curl string
GOOS string
GOARCH string
// AllowDowngrade says the PERSON chose this exact release by name — `/update
// <tag>` or `update --version <tag>` — and a rollback is what they asked
// for. An automatic or channel-following update never sets it, so a stale
// caller cannot silently downgrade a file another window advanced.
AllowDowngrade bool
}
InstallOptions describes one in-place replacement.
type InstallResult ¶
type InstallResult struct {
Release Release
Path string
// Already is true when the target was already the requested release: a
// successful no-op, not a failure, and not something to alarm anyone about.
Already bool
}
InstallResult is the release and executable path that are ready to restart.
func Install ¶
func Install(ctx context.Context, options InstallOptions) (InstallResult, error)
Install downloads, checks, and atomically replaces one executable.
type RefusalError ¶
type RefusalError struct {
Reason string
}
RefusalError is a typed refusal: this target will not be replaced in place, and Reason is the sentence to show. It is not a download or write failure, so it is never retried and never counted against a release's automatic attempts.
func (*RefusalError) Error ¶
func (e *RefusalError) Error() string
type Release ¶
type Release struct {
Tag string
Repository string
PublishedAt time.Time
RunningPublishedAt time.Time
}
Release is one selected GitHub release and the repository that answered.