okami.mom/albedo

git clone https://git.nkp.pet/okami.mom/albedo

dawndaemon: record a content digest of the running build, so the CLI can prove a difference without labels979f217

main
2.4 KiB80 linesraw
1package terminal
2
3import (
4	"context"
5	"fmt"
6	"strings"
7
8	"albedo/cli/internal/daemon"
9	tea "charm.land/bubbletea/v2"
10)
11
12// ConfirmRestart keeps input cancellation owned by Bubble Tea, rather than
13// leaving a blocked reader behind when the caller stops waiting.
14func (t *Service) ConfirmRestart(ctx context.Context, running daemon.ConnectionSnapshot, selected daemon.BuildIdentity) (bool, error) {
15	if err := ctx.Err(); err != nil {
16		return false, err
17	}
18	if !t.Capable {
19		return false, nil
20	}
21	if _, err := fmt.Fprintf(t.Out, "Albedo is already running (PID %d).\n", running.Pid); err != nil {
22		return false, err
23	}
24	if daemon.BuildMismatch(daemon.BuildIdentity{Build: running.Build, Digest: running.Digest}, selected) {
25		if _, err := fmt.Fprintln(t.Out, "The running daemon comes from a different build."); err != nil {
26			return false, err
27		}
28	}
29	if _, err := fmt.Fprintln(t.Out, "Restarting interrupts active work in all sessions. Keep it running unless you want to restart."); err != nil {
30		return false, err
31	}
32	program := tea.NewProgram(restartConfirmation{}, tea.WithInput(t.In), tea.WithOutput(t.Out), tea.WithoutSignalHandler())
33	// Graceful quit waits for the canceled input reader before closing it.
34	// WithContext kills immediately and can race that reader's shutdown.
35	stopCancellation := context.AfterFunc(ctx, func() { program.Send(tea.Quit()) })
36	defer stopCancellation()
37	final, err := program.Run()
38	if ctx.Err() != nil {
39		return false, ctx.Err()
40	}
41	if err != nil {
42		return false, err
43	}
44	answer := final.(restartConfirmation)
45	return Confirmed(answer.input), answer.err
46}
47
48type restartConfirmation struct {
49	input string
50	err   error
51}
52
53func (m restartConfirmation) Init() tea.Cmd { return nil }
54
55func (m restartConfirmation) Update(message tea.Msg) (tea.Model, tea.Cmd) {
56	switch message := message.(type) {
57	case tea.KeyPressMsg:
58		switch message.String() {
59		case "ctrl+c", "ctrl+d", "esc":
60			m.err = context.Canceled
61			return m, tea.Quit
62		case "enter":
63			return m, tea.Quit
64		case "backspace":
65			characters := []rune(m.input)
66			if len(characters) > 0 {
67				m.input = string(characters[:len(characters)-1])
68			}
69		default:
70			m.input += message.Text
71		}
72	case tea.PasteMsg:
73		m.input += strings.ReplaceAll(strings.ReplaceAll(message.Content, "\r", ""), "\n", "")
74	}
75	return m, nil
76}
77
78func (m restartConfirmation) View() tea.View {
79	return tea.NewView("Restart Albedo? [y/N] " + m.input)
80}