Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,16 @@ jobs:
\`\`\`

EOF
if [ "$CHANNEL" = "dev" ]; then
cat >> install.md <<'EOF'
This installs this dev channel as `devaf` beside codeaf:

```sh
curl -fsSL https://agentfield.ai/get/devaf | bash
```

EOF
fi
if [ "$CHANNEL" = "stable" ]; then
# THE SECTION IS RENDERED FOR THE PAGE, NOT COPIED ONTO IT. GitHub
# refuses a release body over its character ceiling, and a rolled-up
Expand Down
22 changes: 15 additions & 7 deletions cmd/codeaf/chatv3_surface.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ var (
runSurfaceProgram = func(ctx context.Context, options tui3.Options) error {
return tui3.Run(ctx, options)
}
surfaceUpdateClient = codeupdate.NewClient
surfaceExecutable = codeupdate.ExecutableTarget
surfaceRevision = buildinfo.Revision
surfaceArguments = func() []string { return append([]string(nil), os.Args[1:]...) }
surfaceUpdateClient = codeupdate.NewClient
surfaceExecutable = codeupdate.ExecutableTarget
surfaceRunningExecutable = os.Executable
surfaceRevision = buildinfo.Revision
surfaceArguments = func() []string { return append([]string(nil), os.Args[1:]...) }
)

// ── THE ONE WAY THE v3 SURFACE IS RUN ───────────────────────────────────────
Expand All @@ -43,30 +44,37 @@ var (
// reads this package's sources rather than trusting the next door to remember.
func runSurface(ctx context.Context, options tui3.Options) error {
revision := surfaceRevision()
executable, executableErr := surfaceRunningExecutable()
curl := codeupdate.CurlCommand
if executableErr == nil {
curl = codeupdate.CurlLine(executable, codeupdate.FollowedChannel(revision))
}
client := surfaceUpdateClient(revision, codeupdate.CheckTimeout)
restart := options.Restart
if restart == nil {
restart = &codeupdate.Plan{}
options.Restart = restart
}
options.UpdateRunning = revision
options.UpdateCurl = curl
options.UpdateArgs = surfaceArguments()
options.UpdateCheck = func(check context.Context) (codeupdate.Available, bool) {
return codeupdate.CheckLaunch(check, codeupdate.CheckOptions{
Running: revision, ProfileDir: options.ProfileDir, Client: client,
Disabled: internalenv.Get(codeupdate.NoUpdateCheckEnv) == "1",
Disabled: internalenv.Get(codeupdate.NoUpdateCheckEnv) == "1",
Executable: executable,
})
}
options.ResolveUpdate = client.Select
options.InstallUpdate = func(install context.Context, release codeupdate.Release) (codeupdate.InstallResult, error) {
// Resolution belongs to the off-frame installation command. A launch may
// live on a slow mount, but its first frame never has to wait for that
// mount merely because /update exists.
target, err := surfaceExecutable(os.Executable)
target, err := surfaceExecutable(func() (string, error) { return executable, executableErr })
if err != nil {
return codeupdate.InstallResult{}, err
}
return codeupdate.Install(install, codeupdate.InstallOptions{Client: client, Release: release, Target: target})
return codeupdate.Install(install, codeupdate.InstallOptions{Client: client, Release: release, Target: target, Curl: curl})
}
// The byte meter, off unless a developer named a log file (wire.go). It
// measures what this surface DRAWS and is therefore as local as the terminal
Expand Down
7 changes: 5 additions & 2 deletions cmd/codeaf/chatv3_surface_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,12 @@ func TestRunSurfaceWiresTheDeferredLaunchCheckAndInstallerThroughRealInit(t *tes
}
oldRun := runSurfaceProgram
oldClient, oldExecutable := surfaceUpdateClient, surfaceExecutable
oldRunningExecutable := surfaceRunningExecutable
oldRevision, oldArguments := surfaceRevision, surfaceArguments
t.Cleanup(func() {
runSurfaceProgram = oldRun
surfaceUpdateClient, surfaceExecutable = oldClient, oldExecutable
surfaceRunningExecutable = oldRunningExecutable
surfaceRevision, surfaceArguments = oldRevision, oldArguments
})
frame := &surfaceFrameWriter{ready: make(chan struct{})}
Expand All @@ -105,6 +107,7 @@ func TestRunSurfaceWiresTheDeferredLaunchCheckAndInstallerThroughRealInit(t *tes
resolved++
return target, nil
}
surfaceRunningExecutable = func() (string, error) { return target, nil }
surfaceRevision = func() string { return "v0.1.1" }
surfaceArguments = func() []string { return []string{"chat", "--model", "x"} }
profile := t.TempDir()
Expand All @@ -122,9 +125,9 @@ func TestRunSurfaceWiresTheDeferredLaunchCheckAndInstallerThroughRealInit(t *tes
t.Fatal("runSurface did not wire every update door")
}
restartArgs := codeupdate.RestartArgs(seen.UpdateArgs, seen.SessionFile)
if strings.Join(restartArgs, " ") != "chat --model x --session /tmp/this.jsonl" || seen.UpdateRunning != "v0.1.1" {
if strings.Join(restartArgs, " ") != "chat --model x --session /tmp/this.jsonl" || seen.UpdateRunning != "v0.1.1" || seen.UpdateCurl != codeupdate.CurlCommand {
cancel()
t.Fatalf("running %q restart args %q", seen.UpdateRunning, restartArgs)
t.Fatalf("running %q curl %q restart args %q", seen.UpdateRunning, seen.UpdateCurl, restartArgs)
}
select {
case <-requestStarted:
Expand Down
2 changes: 1 addition & 1 deletion cmd/codeaf/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ Look at what happened — read-only, no key, nothing spent
print the build this binary was cut from (--version and -v say the same)
Housekeeping — changes state on disk or on the network
codeaf update [--check] [--stable|--rc|--dev|--staging] [--version tag]
check for or install a release; stable is the default
check or install a release; this build's own channel is the default
codeaf cache
what the shared build cache holds, and how big it is
codeaf cache clean [--yes]
Expand Down
55 changes: 39 additions & 16 deletions cmd/codeaf/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,23 +44,30 @@ func runUpdate(args []string) error {
if chosen > 1 {
return fmt.Errorf("choose one of --stable, --rc, --dev, --staging, or --version")
}
channel := "stable"
running := updateRevision()
channel := codeupdate.FollowedChannel(running)
switch {
case *stable:
channel = "stable"
case *rc:
channel = "rc"
case *dev:
channel = "dev"
case *staging:
channel = "staging"
}
choice := codeupdate.Choice{Channel: channel, Version: strings.TrimSpace(*version)}
running := updateRevision()
choice := codeupdate.Choice{Channel: channel, Version: strings.TrimSpace(*version), Running: running}
var target string
curl := codeupdate.CurlCommand
if !*check {
var err error
target, err = codeupdate.ExecutableTarget(updateExecutable)
executable, err := updateExecutable()
if err != nil {
return updateFailure(err)
return updateFailure(fmt.Errorf("find the running codeaf: %w", err), curl)
}
curl = codeupdate.CurlLine(executable, codeupdate.FollowedChannel(running))
target, err = codeupdate.ExecutableTarget(func() (string, error) { return executable, nil })
if err != nil {
return updateFailure(err, curl)
}
if codeupdate.Kind(running) == "other" {
shown := strings.TrimSpace(running)
Expand All @@ -84,32 +91,37 @@ func runUpdate(args []string) error {
fmt.Fprintln(updateErr, "codeaf: could not check for an update:", err)
return exitStatus(1)
}
return updateFailure(fmt.Errorf("could not select a release: %w", err))
return updateFailure(fmt.Errorf("could not select a release: %w", err), curl)
}
if *check {
return sayUpdateCheck(running, release.Tag, choice)
return sayUpdateCheck(running, release, choice)
}
if choice.Version == "" {
if comparison, comparable := codeupdate.CompareSemverTags(running, release.Tag); comparable && comparison > 0 {
available := codeupdate.Available{
Latest: release.Tag, Running: running,
LatestPublished: release.PublishedAt, RunningPublished: release.RunningPublishedAt,
}
if available.Ahead() {
fmt.Fprintf(updateErr, "this codeaf is %s, ahead of the newest %s %s — pass --version %s to install it anyway\n", running, choice.Channel, release.Tag, release.Tag)
return exitStatus(2)
}
}

result, err := codeupdate.Install(context.Background(), codeupdate.InstallOptions{
Client: client, Release: release, Target: target,
Client: client, Release: release, Target: target, Curl: curl,
})
if err != nil {
return updateFailure(err)
return updateFailure(err, curl)
}
fmt.Fprintf(updateOut, "codeaf: installed %s at %s\n", result.Release.Tag, result.Path)
if err := updateVersionLine(result.Path, updateOut, updateErr); err != nil {
return updateFailure(fmt.Errorf("run the installed codeaf: %w", err))
return updateFailure(fmt.Errorf("run the installed codeaf: %w", err), curl)
}
return nil
}

func sayUpdateCheck(running, selected string, choice codeupdate.Choice) error {
func sayUpdateCheck(running string, release codeupdate.Release, choice codeupdate.Choice) error {
selected := release.Tag
shown := strings.TrimSpace(running)
if shown == "" {
shown = "an unstamped source build"
Expand All @@ -128,6 +140,14 @@ func sayUpdateCheck(running, selected string, choice codeupdate.Choice) error {
fmt.Fprintf(updateOut, "you are on the newest %s codeaf, %s\n", channel, selected)
return nil
}
available := codeupdate.Available{
Latest: selected, Running: running,
LatestPublished: release.PublishedAt, RunningPublished: release.RunningPublishedAt,
}
if codeupdate.Kind(running) == channel && available.Ahead() {
fmt.Fprintf(updateOut, "the newest %s codeaf is %s · this codeaf is %s\n", channel, selected, shown)
return nil
}
fmt.Fprintf(updateOut, "codeaf %s is available · you have %s\n", selected, shown)
return exitStatus(3)
}
Expand All @@ -148,11 +168,14 @@ func sayUpdateCheck(running, selected string, choice codeupdate.Choice) error {
return nil
}

func updateFailure(err error) error {
if strings.Contains(err.Error(), codeupdate.CurlCommand) {
func updateFailure(err error, curl string) error {
if strings.TrimSpace(curl) == "" {
curl = codeupdate.CurlCommand
}
if strings.Contains(err.Error(), curl) {
return err
}
return fmt.Errorf("%w; install a release with: %s", err, codeupdate.CurlCommand)
return fmt.Errorf("%w; install a release with: %s", err, curl)
}

func runInstalledVersion(path string, stdout, stderr io.Writer) error {
Expand Down
Loading
Loading