v0.4.1 122e784

cmdrun.go RawHistoryFolder

1// SPDX-License-Identifier: MIT
2
3// Package cmdrun runs host commands (ip, nft, mkfs.ext4) behind a small
4// interface so that callers can be tested with a fake.
5package cmdrun
6
7import (
8	"bytes"
9	"context"
10	"fmt"
11	"io"
12	"os/exec"
13	"strings"
14)
15
16// Runner runs a command to completion and returns its combined output.
17type Runner interface {
18	Run(ctx context.Context, stdin io.Reader, name string, args ...string) ([]byte, error)
19}
20
21// Exec runs real commands.
22type Exec struct{}
23
24// Run implements Runner. A non-zero exit becomes an error carrying the command
25// line and its output.
26func (Exec) Run(ctx context.Context, stdin io.Reader, name string, args ...string) ([]byte, error) {
27	cmd := exec.CommandContext(ctx, name, args...)
28	cmd.Stdin = stdin
29	var out bytes.Buffer
30	cmd.Stdout = &out
31	cmd.Stderr = &out
32	if err := cmd.Run(); err != nil {
33		return out.Bytes(), fmt.Errorf("%s %s: %w: %s", name, strings.Join(args, " "), err, strings.TrimSpace(out.String()))
34	}
35	return out.Bytes(), nil
36}
37
38// Call is one recorded invocation of a Fake.
39type Call struct {
40	Name  string
41	Args  []string
42	Stdin string
43}
44
45// String renders the call as a shell-like line.
46func (c Call) String() string { return strings.TrimSpace(c.Name + " " + strings.Join(c.Args, " ")) }
47
48// Fake records calls and answers them with Handle (nil means success).
49type Fake struct {
50	Calls  []Call
51	Handle func(Call) ([]byte, error)
52}
53
54// Run implements Runner.
55func (f *Fake) Run(_ context.Context, stdin io.Reader, name string, args ...string) ([]byte, error) {
56	c := Call{Name: name, Args: append([]string(nil), args...)}
57	if stdin != nil {
58		b, _ := io.ReadAll(stdin)
59		c.Stdin = string(b)
60	}
61	f.Calls = append(f.Calls, c)
62	if f.Handle != nil {
63		return f.Handle(c)
64	}
65	return nil, nil
66}
67
68// Lines returns the recorded calls as strings.
69func (f *Fake) Lines() []string {
70	out := make([]string, len(f.Calls))
71	for i, c := range f.Calls {
72		out[i] = c.String()
73	}
74	return out
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 4

Tags 11

↑↓ moveenter openesc close