detached 7abacfb

feat(packaging): Arch package with a display-manager session and vendored dist tarball

commit
7abacfb5501e2b2e164b58e204e8855bd41236a7
author
brice · · 2026-09-26 05:48 UTC
committer
brice · · 2026-09-26 05:50 UTC
parent
0b45e97
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
9 files · +113 -9
M .gitignore +1 -0
@@ -4,3 +4,4 @@
44 *.out
55 coverage/
66 .worktrees/
7+/dist/
@@ -4,3 +4,4 @@
4 *.out4 *.out
5 coverage/5 coverage/
6 .worktrees/6 .worktrees/
7+/dist/
M Makefile +25 -1
@@ -1,4 +1,4 @@
1-.PHONY: build test vet race mocks mocks-check fakes-check arch check bin tty logs
1+.PHONY: build test vet race mocks mocks-check fakes-check arch check bin tty logs dist pkg
22
33 # 0 runs until quit; set e.g. TTY_TIMEOUT=60s for a safety net.
44 TTY_TIMEOUT ?= 0
@@ -45,3 +45,27 @@ fakes-check:
4545 arch:
4646 $(HOME)/go/bin/hexcheck -hexcheck.config .hexcheck.yaml -hexcheck.root . ./...
4747 check: vet test arch fakes-check
48+
49+# Self-contained source tarball for packaging (packaging/arch/PKGBUILD): the
50+# sibling purego-* modules are vendored, so the package builds offline.
51+# pacman-ordered: 0.0.0.r<commits>.g<hash>, a tag replaces 0.0.0 once there is one.
52+VERSION ?= $(shell t=$$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//'); \
53+ echo "$${t:-0.0.0}.r$$(git rev-list --count HEAD).g$$(git rev-parse --short HEAD)")
54+DIST := dist/nefertty-$(VERSION)
55+SIBLINGS := github.com/bnema/purego-libwayland github.com/bnema/purego-vulkan
56+dist:
57+ @rm -rf $(DIST) && mkdir -p $(DIST)
58+ git archive $$(git stash create || echo HEAD) | tar -x -C $(DIST)
59+ cd $(DIST) && go mod edit $$(cd $(CURDIR) && go list -m -f '-replace={{.Path}}={{.Dir}}' $(SIBLINGS)) && go mod vendor && \
60+ for m in $(SIBLINGS); do d=$$(cd $(CURDIR) && go list -m -f '{{.Dir}}' $$m); \
61+ sed -i "s#=> $$d\$$#=> ../$${m##*/}#" go.mod vendor/modules.txt; done && \
62+ ! grep -rq "$(HOME)" go.mod vendor/modules.txt && echo '$(VERSION)' > VERSION
63+ tar -C dist -czf $(DIST).tar.gz nefertty-$(VERSION)
64+ @rm -rf $(DIST) && echo $(DIST).tar.gz
65+
66+# Arch package from the dist tarball; install it with `sudo pacman -U` on
67+# the printed file.
68+pkg: dist
69+ rm -rf dist/pkg && mkdir -p dist/pkg && cp packaging/arch/PKGBUILD $(DIST).tar.gz dist/pkg/
70+ cd dist/pkg && sed -i "s/^pkgver=.*/pkgver=$(VERSION)/" PKGBUILD && makepkg -f --noconfirm
71+ @ls dist/pkg/*.pkg.tar.zst
@@ -1,4 +1,4 @@
1-.PHONY: build test vet race mocks mocks-check fakes-check arch check bin tty logs1+.PHONY: build test vet race mocks mocks-check fakes-check arch check bin tty logs dist pkg
2 2
3 # 0 runs until quit; set e.g. TTY_TIMEOUT=60s for a safety net.3 # 0 runs until quit; set e.g. TTY_TIMEOUT=60s for a safety net.
4 TTY_TIMEOUT ?= 04 TTY_TIMEOUT ?= 0
@@ -45,3 +45,27 @@ fakes-check:
45 arch:45 arch:
46 $(HOME)/go/bin/hexcheck -hexcheck.config .hexcheck.yaml -hexcheck.root . ./...46 $(HOME)/go/bin/hexcheck -hexcheck.config .hexcheck.yaml -hexcheck.root . ./...
47 check: vet test arch fakes-check47 check: vet test arch fakes-check
48+
49+# Self-contained source tarball for packaging (packaging/arch/PKGBUILD): the
50+# sibling purego-* modules are vendored, so the package builds offline.
51+# pacman-ordered: 0.0.0.r<commits>.g<hash>, a tag replaces 0.0.0 once there is one.
52+VERSION ?= $(shell t=$$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//'); \
53+ echo "$${t:-0.0.0}.r$$(git rev-list --count HEAD).g$$(git rev-parse --short HEAD)")
54+DIST := dist/nefertty-$(VERSION)
55+SIBLINGS := github.com/bnema/purego-libwayland github.com/bnema/purego-vulkan
56+dist:
57+ @rm -rf $(DIST) && mkdir -p $(DIST)
58+ git archive $$(git stash create || echo HEAD) | tar -x -C $(DIST)
59+ cd $(DIST) && go mod edit $$(cd $(CURDIR) && go list -m -f '-replace={{.Path}}={{.Dir}}' $(SIBLINGS)) && go mod vendor && \
60+ for m in $(SIBLINGS); do d=$$(cd $(CURDIR) && go list -m -f '{{.Dir}}' $$m); \
61+ sed -i "s#=> $$d\$$#=> ../$${m##*/}#" go.mod vendor/modules.txt; done && \
62+ ! grep -rq "$(HOME)" go.mod vendor/modules.txt && echo '$(VERSION)' > VERSION
63+ tar -C dist -czf $(DIST).tar.gz nefertty-$(VERSION)
64+ @rm -rf $(DIST) && echo $(DIST).tar.gz
65+
66+# Arch package from the dist tarball; install it with `sudo pacman -U` on
67+# the printed file.
68+pkg: dist
69+ rm -rf dist/pkg && mkdir -p dist/pkg && cp packaging/arch/PKGBUILD $(DIST).tar.gz dist/pkg/
70+ cd dist/pkg && sed -i "s/^pkgver=.*/pkgver=$(VERSION)/" PKGBUILD && makepkg -f --noconfirm
71+ @ls dist/pkg/*.pkg.tar.zst
M README.md +9 -0
@@ -14,6 +14,15 @@ Build: `make build` (requires sibling checkouts of purego-libwayland and purego-
1414
1515 Run: `nefertty --backend=headless --timeout 5s`
1616
17+## Install (Arch Linux)
18+
19+```sh
20+make pkg # builds dist/pkg/nefertty-*.pkg.tar.zst
21+sudo pacman -U dist/pkg/nefertty-*.pkg.tar.zst
22+```
23+
24+The package installs `nefertty` and a **NeferTTY** session for display managers (Ly, GDM, SDDM). The session runs `nefertty --session`, which shares `WAYLAND_DISPLAY` and `DISPLAY` with D-Bus and systemd user services, so portals and notification daemons reach it. `make dist` alone builds the self-contained source tarball (Go modules vendored) that the package uses.
25+
1726 ## Configuration
1827
1928 Configuration lives at `$XDG_CONFIG_HOME/nefertty/config` (or `~/.config/nefertty/config`). Use `nefertty --config path` to select another file; `nefertty validate-config [path]` checks it without starting the compositor.
@@ -14,6 +14,15 @@ Build: `make build` (requires sibling checkouts of purego-libwayland and purego-
14 14
15 Run: `nefertty --backend=headless --timeout 5s`15 Run: `nefertty --backend=headless --timeout 5s`
16 16
17+## Install (Arch Linux)
18+
19+```sh
20+make pkg # builds dist/pkg/nefertty-*.pkg.tar.zst
21+sudo pacman -U dist/pkg/nefertty-*.pkg.tar.zst
22+```
23+
24+The package installs `nefertty` and a **NeferTTY** session for display managers (Ly, GDM, SDDM). The session runs `nefertty --session`, which shares `WAYLAND_DISPLAY` and `DISPLAY` with D-Bus and systemd user services, so portals and notification daemons reach it. `make dist` alone builds the self-contained source tarball (Go modules vendored) that the package uses.
25+
17 ## Configuration26 ## Configuration
18 27
19 Configuration lives at `$XDG_CONFIG_HOME/nefertty/config` (or `~/.config/nefertty/config`). Use `nefertty --config path` to select another file; `nefertty validate-config [path]` checks it without starting the compositor.28 Configuration lives at `$XDG_CONFIG_HOME/nefertty/config` (or `~/.config/nefertty/config`). Use `nefertty --config path` to select another file; `nefertty validate-config [path]` checks it without starting the compositor.
M cmd/nefertty/main.go +19 -4
@@ -23,6 +23,13 @@ import (
2323
2424 type usageError struct{ error }
2525
26+// sessionEnvCommand exports the session environment to D-Bus and systemd
27+// user services (--session).
28+var sessionEnvCommand = []string{"dbus-update-activation-environment", "--systemd", "WAYLAND_DISPLAY", "DISPLAY", "XDG_CURRENT_DESKTOP", "XDG_SESSION_TYPE"}
29+
30+// version is set with -ldflags "-X main.version=..." by packaged builds.
31+var version string
32+
2633 func main() {
2734 tuneGC()
2835 os.Exit(runCode())
@@ -99,11 +106,13 @@ func run() error {
99106 fmt.Println("unknown")
100107 return nil
101108 }
102- version := info.Main.Version
103- if version == "" {
104- version = "(devel)"
109+ v := info.Main.Version
110+ if version != "" {
111+ v = version // set by release builds (make dist)
112+ } else if v == "" {
113+ v = "(devel)"
105114 }
106- fmt.Println(version)
115+ fmt.Println(v)
107116 return nil
108117 }
109118 flags := flag.NewFlagSet("nefertty", flag.ContinueOnError)
@@ -116,6 +125,7 @@ func run() error {
116125 timeout := flags.Duration("timeout", 0, "duration before exit (0 disables timeout)")
117126 debugFlag := flags.String("debug", "", "debug components (comma-separated or all)")
118127 configFlag := flags.String("config", "", "config path (empty uses XDG default)")
128+ session := flags.Bool("session", false, "run as the login session: share WAYLAND_DISPLAY and DISPLAY with systemd and D-Bus user services")
119129 if err := flags.Parse(os.Args[1:]); err != nil {
120130 if errors.Is(err, flag.ErrHelp) {
121131 return err
@@ -180,6 +190,11 @@ func run() error {
180190 fmt.Fprintln(os.Stderr, cfgErr)
181191 return cfgErr
182192 }
193+ if *session {
194+ // First startup command: portals and other D-Bus or systemd user
195+ // services started later find the session's displays.
196+ cfg.Startup = append([][]string{sessionEnvCommand}, cfg.Startup...)
197+ }
183198 var script *os.File
184199 if *inputPath != "" {
185200 if *inputPath == "-" {
@@ -23,6 +23,13 @@ import (
23 23
24 type usageError struct{ error }24 type usageError struct{ error }
25 25
26+// sessionEnvCommand exports the session environment to D-Bus and systemd
27+// user services (--session).
28+var sessionEnvCommand = []string{"dbus-update-activation-environment", "--systemd", "WAYLAND_DISPLAY", "DISPLAY", "XDG_CURRENT_DESKTOP", "XDG_SESSION_TYPE"}
29+
30+// version is set with -ldflags "-X main.version=..." by packaged builds.
31+var version string
32+
26 func main() {33 func main() {
27 tuneGC()34 tuneGC()
28 os.Exit(runCode())35 os.Exit(runCode())
@@ -99,11 +106,13 @@ func run() error {
99 fmt.Println("unknown")106 fmt.Println("unknown")
100 return nil107 return nil
101 }108 }
102- version := info.Main.Version109+ v := info.Main.Version
103- if version == "" {110+ if version != "" {
104- version = "(devel)"111+ v = version // set by release builds (make dist)
112+ } else if v == "" {
113+ v = "(devel)"
105 }114 }
106- fmt.Println(version)115+ fmt.Println(v)
107 return nil116 return nil
108 }117 }
109 flags := flag.NewFlagSet("nefertty", flag.ContinueOnError)118 flags := flag.NewFlagSet("nefertty", flag.ContinueOnError)
@@ -116,6 +125,7 @@ func run() error {
116 timeout := flags.Duration("timeout", 0, "duration before exit (0 disables timeout)")125 timeout := flags.Duration("timeout", 0, "duration before exit (0 disables timeout)")
117 debugFlag := flags.String("debug", "", "debug components (comma-separated or all)")126 debugFlag := flags.String("debug", "", "debug components (comma-separated or all)")
118 configFlag := flags.String("config", "", "config path (empty uses XDG default)")127 configFlag := flags.String("config", "", "config path (empty uses XDG default)")
128+ session := flags.Bool("session", false, "run as the login session: share WAYLAND_DISPLAY and DISPLAY with systemd and D-Bus user services")
119 if err := flags.Parse(os.Args[1:]); err != nil {129 if err := flags.Parse(os.Args[1:]); err != nil {
120 if errors.Is(err, flag.ErrHelp) {130 if errors.Is(err, flag.ErrHelp) {
121 return err131 return err
@@ -180,6 +190,11 @@ func run() error {
180 fmt.Fprintln(os.Stderr, cfgErr)190 fmt.Fprintln(os.Stderr, cfgErr)
181 return cfgErr191 return cfgErr
182 }192 }
193+ if *session {
194+ // First startup command: portals and other D-Bus or systemd user
195+ // services started later find the session's displays.
196+ cfg.Startup = append([][]string{sessionEnvCommand}, cfg.Startup...)
197+ }
183 var script *os.File198 var script *os.File
184 if *inputPath != "" {199 if *inputPath != "" {
185 if *inputPath == "-" {200 if *inputPath == "-" {
M internal/adapters/launcher/launcher.go +1 -1
@@ -32,7 +32,7 @@ func ChildEnv(base []string, waylandDisplay, runtimeDir string, cursorSize int)
3232 continue
3333 }
3434 switch {
35- case key == "HOME", key == "USER", key == "LOGNAME", key == "SHELL", key == "PATH", key == "LANG", strings.HasPrefix(key, "LC_"), strings.HasPrefix(key, "XDG_"), strings.HasPrefix(key, "XCURSOR_"):
35+ case key == "HOME", key == "USER", key == "LOGNAME", key == "SHELL", key == "PATH", key == "LANG", key == "DBUS_SESSION_BUS_ADDRESS", strings.HasPrefix(key, "LC_"), strings.HasPrefix(key, "XDG_"), strings.HasPrefix(key, "XCURSOR_"):
3636 values[key] = value
3737 }
3838 }
@@ -32,7 +32,7 @@ func ChildEnv(base []string, waylandDisplay, runtimeDir string, cursorSize int)
32 continue32 continue
33 }33 }
34 switch {34 switch {
35- case key == "HOME", key == "USER", key == "LOGNAME", key == "SHELL", key == "PATH", key == "LANG", strings.HasPrefix(key, "LC_"), strings.HasPrefix(key, "XDG_"), strings.HasPrefix(key, "XCURSOR_"):35+ case key == "HOME", key == "USER", key == "LOGNAME", key == "SHELL", key == "PATH", key == "LANG", key == "DBUS_SESSION_BUS_ADDRESS", strings.HasPrefix(key, "LC_"), strings.HasPrefix(key, "XDG_"), strings.HasPrefix(key, "XCURSOR_"):
36 values[key] = value36 values[key] = value
37 }37 }
38 }38 }
M internal/adapters/launcher/launcher_test.go +2 -2
@@ -14,8 +14,8 @@ import (
1414 )
1515
1616 func TestChildEnv(t *testing.T) {
17- got := ChildEnv([]string{"TERM=x", "DISPLAY=:0", "WAYLAND_SOCKET=4", "WAYLAND_DISPLAY=old", "HOME=/home/me", "PATH=/bin", "LC_ALL=C", "XDG_RUNTIME_DIR=old", "XCURSOR_THEME=Adwaita", "XCURSOR_PATH=/icons", "SECRET=bad"}, "wayland-7", "/run/me", 24)
18- want := []string{"HOME=/home/me", "LC_ALL=C", "PATH=/bin", "WAYLAND_DISPLAY=wayland-7", "XCURSOR_PATH=/icons", "XCURSOR_SIZE=24", "XCURSOR_THEME=Adwaita", "XDG_CURRENT_DESKTOP=nefertty", "XDG_RUNTIME_DIR=/run/me", "XDG_SESSION_TYPE=wayland"}
17+ got := ChildEnv([]string{"TERM=x", "DISPLAY=:0", "WAYLAND_SOCKET=4", "WAYLAND_DISPLAY=old", "HOME=/home/me", "PATH=/bin", "LC_ALL=C", "XDG_RUNTIME_DIR=old", "XCURSOR_THEME=Adwaita", "XCURSOR_PATH=/icons", "SECRET=bad", "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/me/bus"}, "wayland-7", "/run/me", 24)
18+ want := []string{"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/me/bus", "HOME=/home/me", "LC_ALL=C", "PATH=/bin", "WAYLAND_DISPLAY=wayland-7", "XCURSOR_PATH=/icons", "XCURSOR_SIZE=24", "XCURSOR_THEME=Adwaita", "XDG_CURRENT_DESKTOP=nefertty", "XDG_RUNTIME_DIR=/run/me", "XDG_SESSION_TYPE=wayland"}
1919 if !reflect.DeepEqual(got, want) {
2020 t.Fatalf("got %v want %v", got, want)
2121 }
@@ -14,8 +14,8 @@ import (
14 )14 )
15 15
16 func TestChildEnv(t *testing.T) {16 func TestChildEnv(t *testing.T) {
17- got := ChildEnv([]string{"TERM=x", "DISPLAY=:0", "WAYLAND_SOCKET=4", "WAYLAND_DISPLAY=old", "HOME=/home/me", "PATH=/bin", "LC_ALL=C", "XDG_RUNTIME_DIR=old", "XCURSOR_THEME=Adwaita", "XCURSOR_PATH=/icons", "SECRET=bad"}, "wayland-7", "/run/me", 24)17+ got := ChildEnv([]string{"TERM=x", "DISPLAY=:0", "WAYLAND_SOCKET=4", "WAYLAND_DISPLAY=old", "HOME=/home/me", "PATH=/bin", "LC_ALL=C", "XDG_RUNTIME_DIR=old", "XCURSOR_THEME=Adwaita", "XCURSOR_PATH=/icons", "SECRET=bad", "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/me/bus"}, "wayland-7", "/run/me", 24)
18- want := []string{"HOME=/home/me", "LC_ALL=C", "PATH=/bin", "WAYLAND_DISPLAY=wayland-7", "XCURSOR_PATH=/icons", "XCURSOR_SIZE=24", "XCURSOR_THEME=Adwaita", "XDG_CURRENT_DESKTOP=nefertty", "XDG_RUNTIME_DIR=/run/me", "XDG_SESSION_TYPE=wayland"}18+ want := []string{"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/me/bus", "HOME=/home/me", "LC_ALL=C", "PATH=/bin", "WAYLAND_DISPLAY=wayland-7", "XCURSOR_PATH=/icons", "XCURSOR_SIZE=24", "XCURSOR_THEME=Adwaita", "XDG_CURRENT_DESKTOP=nefertty", "XDG_RUNTIME_DIR=/run/me", "XDG_SESSION_TYPE=wayland"}
19 if !reflect.DeepEqual(got, want) {19 if !reflect.DeepEqual(got, want) {
20 t.Fatalf("got %v want %v", got, want)20 t.Fatalf("got %v want %v", got, want)
21 }21 }
M internal/adapters/wayland/layer_test.go +10 -1
@@ -128,7 +128,16 @@ func TestWaybarLayer(t *testing.T) {
128128 if err = os.WriteFile(style, nil, 0600); err != nil {
129129 t.Fatal(err)
130130 }
131- _, done, output := lifecycleClient(t, tool, s, dir, []string{"HOME=" + home}, "-c", config, "-s", style)
131+ // waybar (gio) aborts without a session bus; the user bus is the default.
132+ bus := os.Getenv("DBUS_SESSION_BUS_ADDRESS")
133+ if bus == "" {
134+ sock := os.Getenv("XDG_RUNTIME_DIR") + "/bus"
135+ if _, err := os.Stat(sock); err != nil {
136+ t.Skip("no D-Bus session bus")
137+ }
138+ bus = "unix:path=" + sock
139+ }
140+ _, done, output := lifecycleClient(t, tool, s, dir, []string{"HOME=" + home, "DBUS_SESSION_BUS_ADDRESS=" + bus}, "-c", config, "-s", style)
132141 select {
133142 case ev := <-events:
134143 v, ok := ev.(ports.LayerChanged)
@@ -128,7 +128,16 @@ func TestWaybarLayer(t *testing.T) {
128 if err = os.WriteFile(style, nil, 0600); err != nil {128 if err = os.WriteFile(style, nil, 0600); err != nil {
129 t.Fatal(err)129 t.Fatal(err)
130 }130 }
131- _, done, output := lifecycleClient(t, tool, s, dir, []string{"HOME=" + home}, "-c", config, "-s", style)131+ // waybar (gio) aborts without a session bus; the user bus is the default.
132+ bus := os.Getenv("DBUS_SESSION_BUS_ADDRESS")
133+ if bus == "" {
134+ sock := os.Getenv("XDG_RUNTIME_DIR") + "/bus"
135+ if _, err := os.Stat(sock); err != nil {
136+ t.Skip("no D-Bus session bus")
137+ }
138+ bus = "unix:path=" + sock
139+ }
140+ _, done, output := lifecycleClient(t, tool, s, dir, []string{"HOME=" + home, "DBUS_SESSION_BUS_ADDRESS=" + bus}, "-c", config, "-s", style)
132 select {141 select {
133 case ev := <-events:142 case ev := <-events:
134 v, ok := ev.(ports.LayerChanged)143 v, ok := ev.(ports.LayerChanged)
A packaging/arch/PKGBUILD +40 -0
@@ -0,0 +1,40 @@
1+# Built by `make pkg` at the repo root, from the `make dist` tarball.
2+pkgname=nefertty
3+pkgver=0.0.0
4+pkgrel=1
5+pkgdesc='Minimal Wayland compositor with scrollable columns, for the TTY'
6+arch=('x86_64')
7+url='https://github.com/bnema/nefertty'
8+license=('MIT')
9+# Loaded at runtime (purego, no cgo): not seen by makepkg's library scan.
10+depends=('glibc' 'libinput' 'libxkbcommon' 'seatd' 'systemd-libs' 'vulkan-icd-loader'
11+ 'vulkan-driver')
12+makedepends=('go')
13+optdepends=('xwayland-satellite: X11 apps'
14+ 'foot: default terminal'
15+ 'fuzzel: launcher'
16+ 'xdg-desktop-portal-gtk: file pickers and settings for apps')
17+options=('!debug')
18+source=("$pkgname-$pkgver.tar.gz")
19+sha256sums=('SKIP')
20+
21+build() {
22+ cd "$pkgname-$pkgver"
23+ export CGO_ENABLED=0 GOFLAGS='-mod=vendor -trimpath -buildvcs=false' GOPROXY=off
24+ go build -ldflags "-s -w -X main.version=$pkgver" -o nefertty ./cmd/nefertty
25+}
26+
27+check() {
28+ cd "$pkgname-$pkgver"
29+ ./nefertty version
30+ ./nefertty validate-config examples/config
31+}
32+
33+package() {
34+ cd "$pkgname-$pkgver"
35+ install -Dm755 nefertty "$pkgdir/usr/bin/nefertty"
36+ install -Dm644 packaging/nefertty.desktop "$pkgdir/usr/share/wayland-sessions/nefertty.desktop"
37+ install -Dm644 examples/config "$pkgdir/usr/share/doc/nefertty/config.example"
38+ install -Dm644 README.md "$pkgdir/usr/share/doc/nefertty/README.md"
39+ install -Dm644 LICENSE "$pkgdir/usr/share/licenses/nefertty/LICENSE"
40+}
@@ -0,0 +1,40 @@
1+# Built by `make pkg` at the repo root, from the `make dist` tarball.
2+pkgname=nefertty
3+pkgver=0.0.0
4+pkgrel=1
5+pkgdesc='Minimal Wayland compositor with scrollable columns, for the TTY'
6+arch=('x86_64')
7+url='https://github.com/bnema/nefertty'
8+license=('MIT')
9+# Loaded at runtime (purego, no cgo): not seen by makepkg's library scan.
10+depends=('glibc' 'libinput' 'libxkbcommon' 'seatd' 'systemd-libs' 'vulkan-icd-loader'
11+ 'vulkan-driver')
12+makedepends=('go')
13+optdepends=('xwayland-satellite: X11 apps'
14+ 'foot: default terminal'
15+ 'fuzzel: launcher'
16+ 'xdg-desktop-portal-gtk: file pickers and settings for apps')
17+options=('!debug')
18+source=("$pkgname-$pkgver.tar.gz")
19+sha256sums=('SKIP')
20+
21+build() {
22+ cd "$pkgname-$pkgver"
23+ export CGO_ENABLED=0 GOFLAGS='-mod=vendor -trimpath -buildvcs=false' GOPROXY=off
24+ go build -ldflags "-s -w -X main.version=$pkgver" -o nefertty ./cmd/nefertty
25+}
26+
27+check() {
28+ cd "$pkgname-$pkgver"
29+ ./nefertty version
30+ ./nefertty validate-config examples/config
31+}
32+
33+package() {
34+ cd "$pkgname-$pkgver"
35+ install -Dm755 nefertty "$pkgdir/usr/bin/nefertty"
36+ install -Dm644 packaging/nefertty.desktop "$pkgdir/usr/share/wayland-sessions/nefertty.desktop"
37+ install -Dm644 examples/config "$pkgdir/usr/share/doc/nefertty/config.example"
38+ install -Dm644 README.md "$pkgdir/usr/share/doc/nefertty/README.md"
39+ install -Dm644 LICENSE "$pkgdir/usr/share/licenses/nefertty/LICENSE"
40+}
A packaging/nefertty.desktop +6 -0
@@ -0,0 +1,6 @@
1+[Desktop Entry]
2+Name=NeferTTY
3+Comment=Minimal Wayland compositor with scrollable columns
4+Exec=nefertty --session
5+Type=Application
6+DesktopNames=nefertty
@@ -0,0 +1,6 @@
1+[Desktop Entry]
2+Name=NeferTTY
3+Comment=Minimal Wayland compositor with scrollable columns
4+Exec=nefertty --session
5+Type=Application
6+DesktopNames=nefertty

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 23

Tags 10

↑↓ moveenter openesc close