v0.3.0 6800c6a

build.go RawHistoryFolder

1package harness
2
3import (
4	"context"
5	"fmt"
6	"os"
7	"os/exec"
8	"path/filepath"
9	"strings"
10	"syscall"
11)
12
13const NeferWLSHA = "1d16b83bf90f9e8189ad21b27772382c96b9f9a8"
14
15func NeferWL(ctx context.Context) (string, error) {
16	if err := ctx.Err(); err != nil {
17		return "", err
18	}
19	if path := os.Getenv("NEFERGUI_NEFERWL"); path != "" {
20		if fi, err := os.Stat(path); err != nil || fi.IsDir() || fi.Mode()&0111 == 0 {
21			return "", fmt.Errorf("NEFERGUI_NEFERWL is not executable: %s", path)
22		}
23		return path, nil
24	}
25	cache := os.Getenv("XDG_CACHE_HOME")
26	if cache == "" {
27		var err error
28		cache, err = os.UserCacheDir()
29		if err != nil {
30			return "", err
31		}
32	}
33	dir := filepath.Join(cache, "nefergui", "harness", "neferwl-"+NeferWLSHA)
34	bin := filepath.Join(dir, "neferwl")
35	if fi, err := os.Stat(bin); err == nil && fi.Mode().IsRegular() && fi.Mode()&0111 != 0 {
36		return bin, nil
37	}
38	if err := os.MkdirAll(dir, 0700); err != nil {
39		return "", err
40	}
41	if err := ctx.Err(); err != nil {
42		return "", err
43	}
44	stage, err := os.MkdirTemp(dir, "build-")
45	if err != nil {
46		return "", err
47	}
48	defer os.RemoveAll(stage)
49	cmd := exec.Command("go", "install", "github.com/bnema/neferwl/cmd/neferwl@"+NeferWLSHA)
50	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
51	cmd.Env = withEnv(os.Environ(), map[string]string{"GOWORK": "off", "GOBIN": stage})
52	stop := context.AfterFunc(ctx, func() {
53		if cmd.Process != nil {
54			_ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
55		}
56	})
57	output, err := cmd.CombinedOutput()
58	stop()
59	if ctx.Err() != nil {
60		return "", ctx.Err()
61	}
62	if err != nil {
63		return "", fmt.Errorf("neferwl build: %w: %s", err, strings.TrimSpace(string(output)))
64	}
65	if err := os.Rename(filepath.Join(stage, "neferwl"), bin); err != nil {
66		return "", err
67	}
68	return bin, nil
69}
70
71// isolatedEnv drops host display credentials before session-specific values are added.
72func isolatedEnv(base []string) []string {
73	out := make([]string, 0, len(base))
74	for _, e := range base {
75		k, _, _ := strings.Cut(e, "=")
76		if k != "DISPLAY" && k != "WAYLAND_DISPLAY" && k != "WAYLAND_SOCKET" {
77			out = append(out, e)
78		}
79	}
80	return out
81}
82func withEnv(base []string, overrides map[string]string) []string {
83	out := make([]string, 0, len(base)+len(overrides))
84	for _, e := range base {
85		k, _, _ := strings.Cut(e, "=")
86		if _, ok := overrides[k]; ok || k == "WAYLAND_SOCKET" {
87			continue
88		}
89		out = append(out, e)
90	}
91	for k, v := range overrides {
92		out = append(out, k+"="+v)
93	}
94	return out
95}

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 1

Tags 12

↑↓ moveenter openesc close