detached 31f302d

worker.go RawHistoryFolder

1package visual
2
3import (
4	"context"
5	"fmt"
6	"io"
7	"os"
8	"os/exec"
9	"syscall"
10)
11
12// execLauncher spawns `<exe> auth-worker` with private pipes: child fd 3 is the
13// worker's input, fd 4 its output. No credential ever appears in argv or env.
14type execLauncher struct{ exe string }
15
16type execWorker struct {
17	cmd     *exec.Cmd
18	in, out *os.File
19}
20
21func (l execLauncher) launch(context.Context) (workerProcess, error) {
22	inR, inW, err := os.Pipe()
23	if err != nil {
24		return nil, fmt.Errorf("create auth worker input pipe: %w", err)
25	}
26	outR, outW, err := os.Pipe()
27	if err != nil {
28		inR.Close()
29		inW.Close()
30		return nil, fmt.Errorf("create auth worker output pipe: %w", err)
31	}
32	cmd := exec.Command(l.exe, "auth-worker")
33	cmd.Stderr = os.Stderr
34	cmd.Env = workerEnvironment(os.LookupEnv)
35	cmd.ExtraFiles = []*os.File{inR, outW}
36	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
37	err = cmd.Start()
38	inR.Close()
39	outW.Close()
40	if err != nil {
41		inW.Close()
42		outR.Close()
43		return nil, fmt.Errorf("start auth worker %s: %w", l.exe, err)
44	}
45	return &execWorker{cmd: cmd, in: inW, out: outR}, nil
46}
47
48// workerEnvAllowlist is everything the worker (and the `pass`/gpg it spawns)
49// may inherit from the visual process; nothing else leaks into it.
50var workerEnvAllowlist = []string{
51	"HOME", "PATH", "USER", "LOGNAME", "LANG", "LC_ALL",
52	"XDG_RUNTIME_DIR", "GNUPGHOME", "PASSWORD_STORE_DIR", "GPG_TTY",
53}
54
55// workerEnvironment builds the worker's environment from the allowlist; only
56// variables that are present are passed.
57func workerEnvironment(lookup func(string) (string, bool)) []string {
58	env := make([]string, 0, len(workerEnvAllowlist))
59	for _, key := range workerEnvAllowlist {
60		if v, ok := lookup(key); ok {
61			env = append(env, key+"="+v)
62		}
63	}
64	return env
65}
66
67func (w *execWorker) pipes() (io.WriteCloser, io.ReadCloser) { return w.in, w.out }
68func (w *execWorker) kill() error {
69	return syscall.Kill(-w.cmd.Process.Pid, syscall.SIGKILL)
70}
71func (w *execWorker) wait() error { return w.cmd.Wait() }

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 2

Tags 9

↑↓ moveenter openesc close