v0.3.0 6800c6a

main.go RawHistoryFolder

1package main
2
3import (
4	"context"
5	"encoding/json"
6	"errors"
7	"flag"
8	"fmt"
9	"io"
10	"os"
11	"os/signal"
12	"path/filepath"
13	"strings"
14	"syscall"
15	"time"
16
17	"github.com/bnema/nefergui/internal/harness"
18)
19
20func main() { os.Exit(run(os.Args[1:])) }
21func run(args []string) int {
22	if len(args) == 0 || args[0] != "run" {
23		fmt.Fprintln(os.Stderr, "usage: nefergui-harness run [--size WxH --scale 1.5 --layout fr --input script --expect expect.json --out dir --keep --allow-unpinned --timeout 15s] -- <client cmd...>")
24		return harness.Usage
25	}
26	f := flag.NewFlagSet("run", flag.ContinueOnError)
27	f.SetOutput(os.Stderr)
28	size := f.String("size", "800x600", "headless size")
29	scale := f.String("scale", "1", "output scale")
30	layout := f.String("layout", "us", "keyboard layout")
31	background := f.String("background", "#111111", "background #RRGGBB")
32	input := f.String("input", "", "input script")
33	expect := f.String("expect", "", "expectations JSON")
34	out := f.String("out", "", "new artifact directory")
35	keep := f.Bool("keep", false, "keep temporary runtime/config")
36	allowUnpinned := f.Bool("allow-unpinned", false, "permit NEFERGUI_NEFERWL override")
37	timeout := f.Duration("timeout", 15*time.Second, "maximum session duration")
38	ready := f.Duration("ready-timeout", 8*time.Second, "socket readiness deadline")
39	if err := f.Parse(args[1:]); err != nil {
40		return harness.Usage
41	}
42	if *out == "" {
43		fmt.Fprintln(os.Stderr, "--out required")
44		return harness.Usage
45	}
46	var ex harness.Expectations
47	if *expect != "" {
48		data, err := os.ReadFile(*expect)
49		if err != nil {
50			fmt.Fprintln(os.Stderr, err)
51			return harness.Usage
52		}
53		d := json.NewDecoder(strings.NewReader(string(data)))
54		d.DisallowUnknownFields()
55		if err = d.Decode(&ex); err != nil {
56			fmt.Fprintln(os.Stderr, err)
57			return harness.Usage
58		}
59		var extra any
60		if err = d.Decode(&extra); !errors.Is(err, io.EOF) {
61			fmt.Fprintln(os.Stderr, "unexpected JSON after expectations:", err)
62			return harness.Usage
63		}
64	}
65	base := "."
66	if *expect != "" {
67		base = filepath.Dir(*expect)
68	}
69	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
70	defer stop()
71	res, code := harness.Run(ctx, harness.Options{Size: *size, Scale: *scale, Layout: *layout, Background: *background, Input: *input, Out: *out, Keep: *keep, AllowUnpinned: *allowUnpinned, Timeout: *timeout, ReadyTimeout: *ready, Client: f.Args(), Expect: ex, GoldenBase: base})
72	b, _ := json.Marshal(res)
73	fmt.Println(string(b))
74	return code
75}

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