detached cf474d8

Merge pull request #2 from bnema/feat/packaging

feat(packaging): add Arch PKGBUILDs, PAM service file and example config
commit
cf474d82237d39247dc36adfbaa4d57a697895fe
author
bnema · · 2026-09-30 20:08 UTC
committer
GitHub · · 2026-09-30 20:08 UTC
merge of
07cdc18 + ee2503c · diff against the first parent
signature
GPG UnverifiedB5690EEEBB952194 The key is unknown
9 files · +216 -4
M .gitignore +1 -0
@@ -2,3 +2,4 @@
22 bin/
33 *.pprof
44 *.test
5+/dist/
@@ -2,3 +2,4 @@
2 bin/2 bin/
3 *.pprof3 *.pprof
4 *.test4 *.test
5+/dist/
M Makefile +21 -1
@@ -1,4 +1,4 @@
1-.PHONY: build test vet race mocks mocks-check fakes-check check
1+.PHONY: build test vet race mocks mocks-check fakes-check check pkg install
22
33 build:
44 CGO_ENABLED=0 go build ./...
@@ -18,3 +18,23 @@ fakes-check:
1818 [ $$rc -eq 1 ] || { [ $$rc -eq 0 ] && printf '%s\n' 'handwritten test double: generate with Mockery v3' >&2; exit 1; }
1919 check: build vet test fakes-check
2020 CGO_ENABLED=0 staticcheck ./...
21+
22+# Arch package of the committed HEAD (packaging/arch/PKGBUILD). Go modules
23+# come from the module proxy in prepare(); the build itself runs offline.
24+# pacman-ordered version: 0.0.0.r<commits>.g<hash>; a tag replaces 0.0.0.
25+pkg: SHELL := bash
26+pkg: .SHELLFLAGS := -eo pipefail -c
27+pkg:
28+ @test -z "$$(git status --porcelain)" || echo "warning: uncommitted changes are not packaged" >&2
29+ v=$$(t=$$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//; s/-/_/g'); \
30+ echo "$${t:-0.0.0}.r$$(git rev-list --count HEAD).g$$(git rev-parse --short HEAD)"); \
31+ d=$$(mktemp -d /tmp/neferafk-pkg.XXXXXX); trap 'rm -rf "$$d"' EXIT; \
32+ git archive --prefix=neferafk-$$v/ -o "$$d/neferafk-$$v.tar.gz" HEAD; \
33+ cp packaging/arch/PKGBUILD "$$d/"; \
34+ cd "$$d" && sed -i "s/^pkgver=.*/pkgver=$$v/; s/^sha256sums=.*/sha256sums=('$$(sha256sum *.tar.gz | cut -d' ' -f1)')/" PKGBUILD; \
35+ makepkg -f --noconfirm; mkdir -p $(CURDIR)/dist; rm -f $(CURDIR)/dist/neferafk-*.pkg.tar.zst; mv *.pkg.tar.zst $(CURDIR)/dist/
36+ @ls dist/*.pkg.tar.zst
37+
38+# Build the package, then install it with pacman (asks for sudo).
39+install: pkg
40+ sudo pacman -U dist/neferafk-*.pkg.tar.zst
@@ -1,4 +1,4 @@
1-.PHONY: build test vet race mocks mocks-check fakes-check check1+.PHONY: build test vet race mocks mocks-check fakes-check check pkg install
2 2
3 build:3 build:
4 CGO_ENABLED=0 go build ./...4 CGO_ENABLED=0 go build ./...
@@ -18,3 +18,23 @@ fakes-check:
18 [ $$rc -eq 1 ] || { [ $$rc -eq 0 ] && printf '%s\n' 'handwritten test double: generate with Mockery v3' >&2; exit 1; }18 [ $$rc -eq 1 ] || { [ $$rc -eq 0 ] && printf '%s\n' 'handwritten test double: generate with Mockery v3' >&2; exit 1; }
19 check: build vet test fakes-check19 check: build vet test fakes-check
20 CGO_ENABLED=0 staticcheck ./...20 CGO_ENABLED=0 staticcheck ./...
21+
22+# Arch package of the committed HEAD (packaging/arch/PKGBUILD). Go modules
23+# come from the module proxy in prepare(); the build itself runs offline.
24+# pacman-ordered version: 0.0.0.r<commits>.g<hash>; a tag replaces 0.0.0.
25+pkg: SHELL := bash
26+pkg: .SHELLFLAGS := -eo pipefail -c
27+pkg:
28+ @test -z "$$(git status --porcelain)" || echo "warning: uncommitted changes are not packaged" >&2
29+ v=$$(t=$$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//; s/-/_/g'); \
30+ echo "$${t:-0.0.0}.r$$(git rev-list --count HEAD).g$$(git rev-parse --short HEAD)"); \
31+ d=$$(mktemp -d /tmp/neferafk-pkg.XXXXXX); trap 'rm -rf "$$d"' EXIT; \
32+ git archive --prefix=neferafk-$$v/ -o "$$d/neferafk-$$v.tar.gz" HEAD; \
33+ cp packaging/arch/PKGBUILD "$$d/"; \
34+ cd "$$d" && sed -i "s/^pkgver=.*/pkgver=$$v/; s/^sha256sums=.*/sha256sums=('$$(sha256sum *.tar.gz | cut -d' ' -f1)')/" PKGBUILD; \
35+ makepkg -f --noconfirm; mkdir -p $(CURDIR)/dist; rm -f $(CURDIR)/dist/neferafk-*.pkg.tar.zst; mv *.pkg.tar.zst $(CURDIR)/dist/
36+ @ls dist/*.pkg.tar.zst
37+
38+# Build the package, then install it with pacman (asks for sudo).
39+install: pkg
40+ sudo pacman -U dist/neferafk-*.pkg.tar.zst
M README.md +9 -3
@@ -16,13 +16,17 @@ It uses standard protocols only: `ext-idle-notify-v1`, `ext-session-lock-v1`, `w
1616
1717 - Linux with systemd-logind.
1818 - A compositor that advertises `ext_idle_notifier_v1` (v2), `ext_session_lock_manager_v1`, `zwp_linux_dmabuf_v1` (v4), `wp_linux_drm_syncobj_manager_v1`, and `zwlr_output_power_manager_v1` when screens off is enabled.
19-- Password mode needs a PAM service file, `/etc/pam.d/neferafk`, for example:
19+- Password mode needs a PAM service file, `/etc/pam.d/neferafk`. The Arch package installs it; otherwise create it with:
2020
2121 ```
2222 auth include login
2323 ```
2424
25-## Build
25+## Install
26+
27+Arch Linux: `make pkg` builds a package of the committed HEAD from `packaging/arch/PKGBUILD` into `dist/`, and `make install` installs it with pacman. `packaging/aur/neferafk-git/PKGBUILD` builds the latest `main`.
28+
29+From source:
2630
2731 ```sh
2832 CGO_ENABLED=0 go build -o bin/neferafk ./cmd/neferafk
@@ -34,13 +38,15 @@ CGO_ENABLED=0 go build -o bin/neferafk ./cmd/neferafk
3438 neferafk run [--config FILE] run the daemon
3539 neferafk lock lock now
3640 neferafk status print the daemon state
41+neferafk validate-config FILE check a configuration file
42+neferafk version print the version
3743 ```
3844
3945 Start `neferafk run` from your compositor's startup. The daemon writes its log to `$XDG_STATE_HOME/neferafk/logs/daemon.log` (default `~/.local/state/neferafk/logs/daemon.log`).
4046
4147 ## Configuration
4248
43-The file is `$XDG_CONFIG_HOME/neferafk/config` (default `~/.config/neferafk/config`). It uses flat `key = value` lines, and `#` starts a comment. Changes are applied live. Two cases need a daemon restart: enabling screens off, and enabling lock or sleep when both were off at startup.
49+The file is `$XDG_CONFIG_HOME/neferafk/config` (default `~/.config/neferafk/config`); `examples/config` is a commented starting point. It uses flat `key = value` lines, and `#` starts a comment. Changes are applied live. Two cases need a daemon restart: enabling screens off, and enabling lock or sleep when both were off at startup.
4450
4551 ```
4652 fade.after = 6m
@@ -16,13 +16,17 @@ It uses standard protocols only: `ext-idle-notify-v1`, `ext-session-lock-v1`, `w
16 16
17 - Linux with systemd-logind.17 - Linux with systemd-logind.
18 - A compositor that advertises `ext_idle_notifier_v1` (v2), `ext_session_lock_manager_v1`, `zwp_linux_dmabuf_v1` (v4), `wp_linux_drm_syncobj_manager_v1`, and `zwlr_output_power_manager_v1` when screens off is enabled.18 - A compositor that advertises `ext_idle_notifier_v1` (v2), `ext_session_lock_manager_v1`, `zwp_linux_dmabuf_v1` (v4), `wp_linux_drm_syncobj_manager_v1`, and `zwlr_output_power_manager_v1` when screens off is enabled.
19-- Password mode needs a PAM service file, `/etc/pam.d/neferafk`, for example:19+- Password mode needs a PAM service file, `/etc/pam.d/neferafk`. The Arch package installs it; otherwise create it with:
20 20
21 ```21 ```
22 auth include login22 auth include login
23 ```23 ```
24 24
25-## Build25+## Install
26+
27+Arch Linux: `make pkg` builds a package of the committed HEAD from `packaging/arch/PKGBUILD` into `dist/`, and `make install` installs it with pacman. `packaging/aur/neferafk-git/PKGBUILD` builds the latest `main`.
28+
29+From source:
26 30
27 ```sh31 ```sh
28 CGO_ENABLED=0 go build -o bin/neferafk ./cmd/neferafk32 CGO_ENABLED=0 go build -o bin/neferafk ./cmd/neferafk
@@ -34,13 +38,15 @@ CGO_ENABLED=0 go build -o bin/neferafk ./cmd/neferafk
34 neferafk run [--config FILE] run the daemon38 neferafk run [--config FILE] run the daemon
35 neferafk lock lock now39 neferafk lock lock now
36 neferafk status print the daemon state40 neferafk status print the daemon state
41+neferafk validate-config FILE check a configuration file
42+neferafk version print the version
37 ```43 ```
38 44
39 Start `neferafk run` from your compositor's startup. The daemon writes its log to `$XDG_STATE_HOME/neferafk/logs/daemon.log` (default `~/.local/state/neferafk/logs/daemon.log`).45 Start `neferafk run` from your compositor's startup. The daemon writes its log to `$XDG_STATE_HOME/neferafk/logs/daemon.log` (default `~/.local/state/neferafk/logs/daemon.log`).
40 46
41 ## Configuration47 ## Configuration
42 48
43-The file is `$XDG_CONFIG_HOME/neferafk/config` (default `~/.config/neferafk/config`). It uses flat `key = value` lines, and `#` starts a comment. Changes are applied live. Two cases need a daemon restart: enabling screens off, and enabling lock or sleep when both were off at startup.49+The file is `$XDG_CONFIG_HOME/neferafk/config` (default `~/.config/neferafk/config`); `examples/config` is a commented starting point. It uses flat `key = value` lines, and `#` starts a comment. Changes are applied live. Two cases need a daemon restart: enabling screens off, and enabling lock or sleep when both were off at startup.
44 50
45 ```51 ```
46 fade.after = 6m52 fade.after = 6m
M cmd/neferafk/main.go +28 -0
@@ -7,11 +7,13 @@ import (
77 "flag"
88 "fmt"
99 "io"
10+ "io/fs"
1011 "os"
1112 "os/signal"
1213 "syscall"
1314
1415 "github.com/bnema/neferafk/internal/adapters/auth"
16+ "github.com/bnema/neferafk/internal/adapters/config"
1517 "github.com/bnema/neferafk/internal/adapters/control"
1618 "github.com/bnema/neferafk/internal/adapters/visual"
1719 "github.com/bnema/neferafk/internal/app"
@@ -25,12 +27,17 @@ const (
2527 exitUnsupported = 3
2628 )
2729
30+// version is set at build time with -ldflags "-X main.version=...".
31+var version = "dev"
32+
2833 const usage = `usage: neferafk <command> [flags]
2934
3035 commands:
3136 run [--config FILE] run the absence daemon
3237 lock ask the running daemon to lock now
3338 status print the running daemon's state
39+ validate-config FILE check a configuration file and exit
40+ version print the version
3441 visual internal: fade/lock process (fd 3 commands in, fd 4 events out)
3542 `
3643
@@ -51,6 +58,27 @@ func execute(args []string, stdout, stderr io.Writer) int {
5158 return runVisual(rest, os.NewFile(3, "visual-cmd"), os.NewFile(4, "visual-events"), stderr)
5259 case "auth-worker": // hidden: spawned by the visual process only
5360 return runAuthWorker(rest, os.NewFile(3, "auth-in"), os.NewFile(4, "auth-out"), stderr)
61+ case "version":
62+ if len(rest) != 0 {
63+ fmt.Fprint(stderr, usage)
64+ return exitUsage
65+ }
66+ fmt.Fprintln(stdout, version)
67+ return exitOK
68+ case "validate-config":
69+ if len(rest) != 1 {
70+ fmt.Fprint(stderr, usage)
71+ return exitUsage
72+ }
73+ if _, err := config.Load(rest[0]); err != nil {
74+ if errors.Is(err, fs.ErrNotExist) || errors.Is(err, fs.ErrPermission) {
75+ fmt.Fprintf(stderr, "neferafk: %v\n", err) // already names the file
76+ } else {
77+ fmt.Fprintf(stderr, "neferafk: %s: %v\n", rest[0], err)
78+ }
79+ return exitFailure
80+ }
81+ return exitOK
5482 case "-h", "--help", "help":
5583 fmt.Fprint(stdout, usage)
5684 return exitOK
@@ -7,11 +7,13 @@ import (
7 "flag"7 "flag"
8 "fmt"8 "fmt"
9 "io"9 "io"
10+ "io/fs"
10 "os"11 "os"
11 "os/signal"12 "os/signal"
12 "syscall"13 "syscall"
13 14
14 "github.com/bnema/neferafk/internal/adapters/auth"15 "github.com/bnema/neferafk/internal/adapters/auth"
16+ "github.com/bnema/neferafk/internal/adapters/config"
15 "github.com/bnema/neferafk/internal/adapters/control"17 "github.com/bnema/neferafk/internal/adapters/control"
16 "github.com/bnema/neferafk/internal/adapters/visual"18 "github.com/bnema/neferafk/internal/adapters/visual"
17 "github.com/bnema/neferafk/internal/app"19 "github.com/bnema/neferafk/internal/app"
@@ -25,12 +27,17 @@ const (
25 exitUnsupported = 327 exitUnsupported = 3
26 )28 )
27 29
30+// version is set at build time with -ldflags "-X main.version=...".
31+var version = "dev"
32+
28 const usage = `usage: neferafk <command> [flags]33 const usage = `usage: neferafk <command> [flags]
29 34
30 commands:35 commands:
31 run [--config FILE] run the absence daemon36 run [--config FILE] run the absence daemon
32 lock ask the running daemon to lock now37 lock ask the running daemon to lock now
33 status print the running daemon's state38 status print the running daemon's state
39+ validate-config FILE check a configuration file and exit
40+ version print the version
34 visual internal: fade/lock process (fd 3 commands in, fd 4 events out)41 visual internal: fade/lock process (fd 3 commands in, fd 4 events out)
35 `42 `
36 43
@@ -51,6 +58,27 @@ func execute(args []string, stdout, stderr io.Writer) int {
51 return runVisual(rest, os.NewFile(3, "visual-cmd"), os.NewFile(4, "visual-events"), stderr)58 return runVisual(rest, os.NewFile(3, "visual-cmd"), os.NewFile(4, "visual-events"), stderr)
52 case "auth-worker": // hidden: spawned by the visual process only59 case "auth-worker": // hidden: spawned by the visual process only
53 return runAuthWorker(rest, os.NewFile(3, "auth-in"), os.NewFile(4, "auth-out"), stderr)60 return runAuthWorker(rest, os.NewFile(3, "auth-in"), os.NewFile(4, "auth-out"), stderr)
61+ case "version":
62+ if len(rest) != 0 {
63+ fmt.Fprint(stderr, usage)
64+ return exitUsage
65+ }
66+ fmt.Fprintln(stdout, version)
67+ return exitOK
68+ case "validate-config":
69+ if len(rest) != 1 {
70+ fmt.Fprint(stderr, usage)
71+ return exitUsage
72+ }
73+ if _, err := config.Load(rest[0]); err != nil {
74+ if errors.Is(err, fs.ErrNotExist) || errors.Is(err, fs.ErrPermission) {
75+ fmt.Fprintf(stderr, "neferafk: %v\n", err) // already names the file
76+ } else {
77+ fmt.Fprintf(stderr, "neferafk: %s: %v\n", rest[0], err)
78+ }
79+ return exitFailure
80+ }
81+ return exitOK
54 case "-h", "--help", "help":82 case "-h", "--help", "help":
55 fmt.Fprint(stdout, usage)83 fmt.Fprint(stdout, usage)
56 return exitOK84 return exitOK
M cmd/neferafk/main_test.go +36 -0
@@ -6,6 +6,7 @@ import (
66 "golang.org/x/sys/unix"
77 "os"
88 "os/exec"
9+ "path/filepath"
910 "strings"
1011 "syscall"
1112 "testing"
@@ -86,6 +87,41 @@ func TestRunRejectsInvalidConfig(t *testing.T) {
8687 }
8788 }
8889
90+func TestVersionAndValidateConfig(t *testing.T) {
91+ if code, o, _ := run(t, "version"); code != exitOK || strings.TrimSpace(o) != version {
92+ t.Fatalf("version: %d %q", code, o)
93+ }
94+ if code, _, _ := run(t, "version", "extra"); code != exitUsage {
95+ t.Fatal("version accepted an argument")
96+ }
97+ bad := filepath.Join(t.TempDir(), "config")
98+ if err := writeFile(bad, "lock.after = banana\n"); err != nil {
99+ t.Fatal(err)
100+ }
101+ missing := filepath.Join(t.TempDir(), "missing")
102+ for _, tc := range []struct {
103+ name string
104+ args []string
105+ code int
106+ stderr string
107+ }{
108+ {"example", []string{"validate-config", "../../examples/config"}, exitOK, ""},
109+ {"invalid", []string{"validate-config", bad}, exitFailure, "line 1"},
110+ {"missing", []string{"validate-config", missing}, exitFailure, "no such file"},
111+ {"no file", []string{"validate-config"}, exitUsage, "usage:"},
112+ } {
113+ t.Run(tc.name, func(t *testing.T) {
114+ code, _, e := run(t, tc.args...)
115+ if code != tc.code || !strings.Contains(e, tc.stderr) {
116+ t.Fatalf("%d %q", code, e)
117+ }
118+ if tc.name == "missing" && strings.Count(e, missing) != 1 {
119+ t.Fatalf("path not named exactly once: %q", e)
120+ }
121+ })
122+ }
123+}
124+
89125 func TestServeAuthRunsWorkerOnPipes(t *testing.T) {
90126 var in, out, errb bytes.Buffer
91127 if err := auth.WriteBootstrap(&in, ports.AuthBootstrap{Generation: 3, PINSource: ports.PINSourceEnv, EnvPIN: []byte("123456")}); err != nil {
@@ -6,6 +6,7 @@ import (
6 "golang.org/x/sys/unix"6 "golang.org/x/sys/unix"
7 "os"7 "os"
8 "os/exec"8 "os/exec"
9+ "path/filepath"
9 "strings"10 "strings"
10 "syscall"11 "syscall"
11 "testing"12 "testing"
@@ -86,6 +87,41 @@ func TestRunRejectsInvalidConfig(t *testing.T) {
86 }87 }
87 }88 }
88 89
90+func TestVersionAndValidateConfig(t *testing.T) {
91+ if code, o, _ := run(t, "version"); code != exitOK || strings.TrimSpace(o) != version {
92+ t.Fatalf("version: %d %q", code, o)
93+ }
94+ if code, _, _ := run(t, "version", "extra"); code != exitUsage {
95+ t.Fatal("version accepted an argument")
96+ }
97+ bad := filepath.Join(t.TempDir(), "config")
98+ if err := writeFile(bad, "lock.after = banana\n"); err != nil {
99+ t.Fatal(err)
100+ }
101+ missing := filepath.Join(t.TempDir(), "missing")
102+ for _, tc := range []struct {
103+ name string
104+ args []string
105+ code int
106+ stderr string
107+ }{
108+ {"example", []string{"validate-config", "../../examples/config"}, exitOK, ""},
109+ {"invalid", []string{"validate-config", bad}, exitFailure, "line 1"},
110+ {"missing", []string{"validate-config", missing}, exitFailure, "no such file"},
111+ {"no file", []string{"validate-config"}, exitUsage, "usage:"},
112+ } {
113+ t.Run(tc.name, func(t *testing.T) {
114+ code, _, e := run(t, tc.args...)
115+ if code != tc.code || !strings.Contains(e, tc.stderr) {
116+ t.Fatalf("%d %q", code, e)
117+ }
118+ if tc.name == "missing" && strings.Count(e, missing) != 1 {
119+ t.Fatalf("path not named exactly once: %q", e)
120+ }
121+ })
122+ }
123+}
124+
89 func TestServeAuthRunsWorkerOnPipes(t *testing.T) {125 func TestServeAuthRunsWorkerOnPipes(t *testing.T) {
90 var in, out, errb bytes.Buffer126 var in, out, errb bytes.Buffer
91 if err := auth.WriteBootstrap(&in, ports.AuthBootstrap{Generation: 3, PINSource: ports.PINSourceEnv, EnvPIN: []byte("123456")}); err != nil {127 if err := auth.WriteBootstrap(&in, ports.AuthBootstrap{Generation: 3, PINSource: ports.PINSourceEnv, EnvPIN: []byte("123456")}); err != nil {
A examples/config +18 -0
@@ -0,0 +1,18 @@
1+# NeferAFK configuration: copy to ~/.config/neferafk/config.
2+# Delays count from the last input. `off` disables an action.
3+# Changes apply live, except enabling screens off, or enabling lock or sleep
4+# when both were off at startup: those need a daemon restart.
5+
6+fade.after = 6m
7+fade.duration = 2s
8+lock.after = 5m
9+screens.off-after = 7m
10+sleep.after = 20m
11+
12+# password (PAM service /etc/pam.d/neferafk) or pin.
13+auth.mode = password
14+# PIN mode: 6 to 32 digits from `pass show ENTRY` (or auth.pin-source = env
15+# with auth.pin-env = VARIABLE). Replace auth.mode above with:
16+# auth.mode = pin
17+# auth.pin-source = pass
18+# auth.pin-entry = neferafk/pin
@@ -0,0 +1,18 @@
1+# NeferAFK configuration: copy to ~/.config/neferafk/config.
2+# Delays count from the last input. `off` disables an action.
3+# Changes apply live, except enabling screens off, or enabling lock or sleep
4+# when both were off at startup: those need a daemon restart.
5+
6+fade.after = 6m
7+fade.duration = 2s
8+lock.after = 5m
9+screens.off-after = 7m
10+sleep.after = 20m
11+
12+# password (PAM service /etc/pam.d/neferafk) or pin.
13+auth.mode = password
14+# PIN mode: 6 to 32 digits from `pass show ENTRY` (or auth.pin-source = env
15+# with auth.pin-env = VARIABLE). Replace auth.mode above with:
16+# auth.mode = pin
17+# auth.pin-source = pass
18+# auth.pin-entry = neferafk/pin
A packaging/arch/PKGBUILD +44 -0
@@ -0,0 +1,44 @@
1+# Built by `make pkg` at the repo root, from `git archive HEAD`.
2+pkgname=neferafk
3+pkgver=0.0.0
4+pkgrel=1
5+pkgdesc='Idle daemon for Wayland: fade, session lock, screens off and suspend'
6+arch=('x86_64')
7+url='https://github.com/bnema/neferafk'
8+license=('GPL-3.0-only')
9+# Loaded at runtime (purego, no cgo): not seen by makepkg's library scan.
10+depends=('glibc' 'pam' 'libxkbcommon' 'vulkan-icd-loader' 'vulkan-driver' 'systemd'
11+ 'ttf-dejavu') # lock screen monospace font (DejaVu Sans Mono)
12+makedepends=('go>=2:1.27')
13+optdepends=('pass: PIN source for auth.mode = pin')
14+backup=('etc/pam.d/neferafk')
15+source=("$pkgname-$pkgver.tar.gz")
16+sha256sums=('SKIP')
17+
18+prepare() {
19+ cd "$pkgname-$pkgver"
20+ export GOPATH="$srcdir/gopath" GOFLAGS='-mod=readonly -modcacherw' GOWORK=off GOTOOLCHAIN=local
21+ go mod download
22+ go mod vendor
23+}
24+
25+build() {
26+ cd "$pkgname-$pkgver"
27+ export CGO_ENABLED=0 GOFLAGS='-mod=vendor -trimpath -buildvcs=false -buildmode=pie' GOPROXY=off GOWORK=off GOTOOLCHAIN=local
28+ go build -ldflags "-X main.version=$pkgver" -o neferafk ./cmd/neferafk
29+}
30+
31+check() {
32+ cd "$pkgname-$pkgver"
33+ ./neferafk version
34+ ./neferafk validate-config examples/config
35+}
36+
37+package() {
38+ cd "$pkgname-$pkgver"
39+ install -Dm755 neferafk "$pkgdir/usr/bin/neferafk"
40+ install -Dm644 packaging/neferafk.pam "$pkgdir/etc/pam.d/neferafk"
41+ install -Dm644 examples/config "$pkgdir/usr/share/doc/neferafk/config.example"
42+ install -Dm644 README.md "$pkgdir/usr/share/doc/neferafk/README.md"
43+ install -Dm644 LICENSE "$pkgdir/usr/share/licenses/neferafk/LICENSE"
44+}
@@ -0,0 +1,44 @@
1+# Built by `make pkg` at the repo root, from `git archive HEAD`.
2+pkgname=neferafk
3+pkgver=0.0.0
4+pkgrel=1
5+pkgdesc='Idle daemon for Wayland: fade, session lock, screens off and suspend'
6+arch=('x86_64')
7+url='https://github.com/bnema/neferafk'
8+license=('GPL-3.0-only')
9+# Loaded at runtime (purego, no cgo): not seen by makepkg's library scan.
10+depends=('glibc' 'pam' 'libxkbcommon' 'vulkan-icd-loader' 'vulkan-driver' 'systemd'
11+ 'ttf-dejavu') # lock screen monospace font (DejaVu Sans Mono)
12+makedepends=('go>=2:1.27')
13+optdepends=('pass: PIN source for auth.mode = pin')
14+backup=('etc/pam.d/neferafk')
15+source=("$pkgname-$pkgver.tar.gz")
16+sha256sums=('SKIP')
17+
18+prepare() {
19+ cd "$pkgname-$pkgver"
20+ export GOPATH="$srcdir/gopath" GOFLAGS='-mod=readonly -modcacherw' GOWORK=off GOTOOLCHAIN=local
21+ go mod download
22+ go mod vendor
23+}
24+
25+build() {
26+ cd "$pkgname-$pkgver"
27+ export CGO_ENABLED=0 GOFLAGS='-mod=vendor -trimpath -buildvcs=false -buildmode=pie' GOPROXY=off GOWORK=off GOTOOLCHAIN=local
28+ go build -ldflags "-X main.version=$pkgver" -o neferafk ./cmd/neferafk
29+}
30+
31+check() {
32+ cd "$pkgname-$pkgver"
33+ ./neferafk version
34+ ./neferafk validate-config examples/config
35+}
36+
37+package() {
38+ cd "$pkgname-$pkgver"
39+ install -Dm755 neferafk "$pkgdir/usr/bin/neferafk"
40+ install -Dm644 packaging/neferafk.pam "$pkgdir/etc/pam.d/neferafk"
41+ install -Dm644 examples/config "$pkgdir/usr/share/doc/neferafk/config.example"
42+ install -Dm644 README.md "$pkgdir/usr/share/doc/neferafk/README.md"
43+ install -Dm644 LICENSE "$pkgdir/usr/share/licenses/neferafk/LICENSE"
44+}
A packaging/aur/neferafk-git/PKGBUILD +53 -0
@@ -0,0 +1,53 @@
1+# Maintainer: bnema <b at bnema dot dev>
2+pkgname=neferafk-git
3+pkgver=0.1.0.r0.g07cdc18
4+pkgrel=1
5+pkgdesc='Idle daemon for Wayland: fade, session lock, screens off and suspend (git version)'
6+arch=('x86_64' 'aarch64')
7+url='https://github.com/bnema/neferafk'
8+license=('GPL-3.0-only')
9+# Loaded at runtime (purego, no cgo): not seen by makepkg's library scan.
10+depends=('glibc' 'pam' 'libxkbcommon' 'vulkan-icd-loader' 'vulkan-driver' 'systemd'
11+ 'ttf-dejavu') # lock screen monospace font (DejaVu Sans Mono)
12+makedepends=('go>=2:1.27' 'git')
13+optdepends=('pass: PIN source for auth.mode = pin')
14+provides=('neferafk')
15+conflicts=('neferafk')
16+backup=('etc/pam.d/neferafk')
17+source=('git+https://github.com/bnema/neferafk.git')
18+sha256sums=('SKIP')
19+
20+pkgver() {
21+ cd neferafk
22+ # v0.2.0-5-gabc1234 -> 0.2.0.r5.gabc1234. Pre-release tags (v0.3.0-rc1)
23+ # are skipped: pacman sorts 0.3.0.rc1 after 0.3.0.
24+ git describe --long --tags --abbrev=7 --exclude='*-*' | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g'
25+}
26+
27+prepare() {
28+ cd neferafk
29+ export GOPATH="$srcdir/gopath" GOFLAGS='-mod=readonly -modcacherw' GOTOOLCHAIN=local
30+ go mod download
31+}
32+
33+build() {
34+ cd neferafk
35+ export GOPATH="$srcdir/gopath" CGO_ENABLED=0 GOTOOLCHAIN=local GOPROXY=off
36+ export GOFLAGS='-mod=readonly -modcacherw -trimpath -buildvcs=false -buildmode=pie'
37+ go build -ldflags "-X main.version=$pkgver" -o neferafk ./cmd/neferafk
38+}
39+
40+check() {
41+ cd neferafk
42+ ./neferafk version
43+ ./neferafk validate-config examples/config
44+}
45+
46+package() {
47+ cd neferafk
48+ install -Dm755 neferafk "$pkgdir/usr/bin/neferafk"
49+ install -Dm644 packaging/neferafk.pam "$pkgdir/etc/pam.d/neferafk"
50+ install -Dm644 examples/config "$pkgdir/usr/share/doc/neferafk/config.example"
51+ install -Dm644 README.md "$pkgdir/usr/share/doc/neferafk/README.md"
52+ install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
53+}
@@ -0,0 +1,53 @@
1+# Maintainer: bnema <b at bnema dot dev>
2+pkgname=neferafk-git
3+pkgver=0.1.0.r0.g07cdc18
4+pkgrel=1
5+pkgdesc='Idle daemon for Wayland: fade, session lock, screens off and suspend (git version)'
6+arch=('x86_64' 'aarch64')
7+url='https://github.com/bnema/neferafk'
8+license=('GPL-3.0-only')
9+# Loaded at runtime (purego, no cgo): not seen by makepkg's library scan.
10+depends=('glibc' 'pam' 'libxkbcommon' 'vulkan-icd-loader' 'vulkan-driver' 'systemd'
11+ 'ttf-dejavu') # lock screen monospace font (DejaVu Sans Mono)
12+makedepends=('go>=2:1.27' 'git')
13+optdepends=('pass: PIN source for auth.mode = pin')
14+provides=('neferafk')
15+conflicts=('neferafk')
16+backup=('etc/pam.d/neferafk')
17+source=('git+https://github.com/bnema/neferafk.git')
18+sha256sums=('SKIP')
19+
20+pkgver() {
21+ cd neferafk
22+ # v0.2.0-5-gabc1234 -> 0.2.0.r5.gabc1234. Pre-release tags (v0.3.0-rc1)
23+ # are skipped: pacman sorts 0.3.0.rc1 after 0.3.0.
24+ git describe --long --tags --abbrev=7 --exclude='*-*' | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g'
25+}
26+
27+prepare() {
28+ cd neferafk
29+ export GOPATH="$srcdir/gopath" GOFLAGS='-mod=readonly -modcacherw' GOTOOLCHAIN=local
30+ go mod download
31+}
32+
33+build() {
34+ cd neferafk
35+ export GOPATH="$srcdir/gopath" CGO_ENABLED=0 GOTOOLCHAIN=local GOPROXY=off
36+ export GOFLAGS='-mod=readonly -modcacherw -trimpath -buildvcs=false -buildmode=pie'
37+ go build -ldflags "-X main.version=$pkgver" -o neferafk ./cmd/neferafk
38+}
39+
40+check() {
41+ cd neferafk
42+ ./neferafk version
43+ ./neferafk validate-config examples/config
44+}
45+
46+package() {
47+ cd neferafk
48+ install -Dm755 neferafk "$pkgdir/usr/bin/neferafk"
49+ install -Dm644 packaging/neferafk.pam "$pkgdir/etc/pam.d/neferafk"
50+ install -Dm644 examples/config "$pkgdir/usr/share/doc/neferafk/config.example"
51+ install -Dm644 README.md "$pkgdir/usr/share/doc/neferafk/README.md"
52+ install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
53+}
A packaging/neferafk.pam +6 -0
@@ -0,0 +1,6 @@
1+#
2+# PAM configuration file for the NeferAFK lock screen. By default, it includes
3+# the 'login' configuration file (see /etc/pam.d/login).
4+#
5+
6+auth include login
@@ -0,0 +1,6 @@
1+#
2+# PAM configuration file for the NeferAFK lock screen. By default, it includes
3+# the 'login' configuration file (see /etc/pam.d/login).
4+#
5+
6+auth include login

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