detached 6a2bad6

fix(kill): resolve a symlinked working directory like vev does

commit
6a2bad67844b5f27c87cf3ac94b6cc54a2848bc1
author
brice · · 2026-09-23 21:25 UTC
parent
eef302d
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +31 -0
M internal/app/kill_all_linux.go +9 -0
@@ -191,6 +191,15 @@ func processSocketDir(procDir string, uid int) string {
191191 }
192192 }
193193 cwd, _ := os.Readlink(filepath.Join(procDir, "cwd"))
194+ // Like os.Getwd, prefer $PWD when it names the same directory, so a repo
195+ // opened through a symlink resolves to the path vev itself used.
196+ if pwd := env["PWD"]; filepath.IsAbs(pwd) {
197+ pwdInfo, pwdErr := os.Stat(pwd)
198+ cwdInfo, cwdErr := os.Stat(filepath.Join(procDir, "cwd"))
199+ if pwdErr == nil && cwdErr == nil && os.SameFile(pwdInfo, cwdInfo) {
200+ cwd = pwd
201+ }
202+ }
194203 runtime := platform.RuntimeDirFor(func(key string) string { return env[key] }, cwd)
195204 return ipc.SocketDirFor(runtime, uid)
196205 }
@@ -191,6 +191,15 @@ func processSocketDir(procDir string, uid int) string {
191 }191 }
192 }192 }
193 cwd, _ := os.Readlink(filepath.Join(procDir, "cwd"))193 cwd, _ := os.Readlink(filepath.Join(procDir, "cwd"))
194+ // Like os.Getwd, prefer $PWD when it names the same directory, so a repo
195+ // opened through a symlink resolves to the path vev itself used.
196+ if pwd := env["PWD"]; filepath.IsAbs(pwd) {
197+ pwdInfo, pwdErr := os.Stat(pwd)
198+ cwdInfo, cwdErr := os.Stat(filepath.Join(procDir, "cwd"))
199+ if pwdErr == nil && cwdErr == nil && os.SameFile(pwdInfo, cwdInfo) {
200+ cwd = pwd
201+ }
202+ }
194 runtime := platform.RuntimeDirFor(func(key string) string { return env[key] }, cwd)203 runtime := platform.RuntimeDirFor(func(key string) string { return env[key] }, cwd)
195 return ipc.SocketDirFor(runtime, uid)204 return ipc.SocketDirFor(runtime, uid)
196 }205 }
M internal/app/kill_all_linux_test.go +22 -0
@@ -123,6 +123,28 @@ func TestFindVevProcesses(t *testing.T) {
123123 require.Empty(t, none, "another user's processes are never matched")
124124 }
125125
126+// TestProcessSocketDirPrefersSymlinkedPWD pins that a VEV_ENV client started
127+// in a repo reached through a symlink resolves to the same path vev used.
128+func TestProcessSocketDirPrefersSymlinkedPWD(t *testing.T) {
129+ base := t.TempDir()
130+ real := filepath.Join(base, "real")
131+ require.NoError(t, os.Mkdir(real, 0o755))
132+ link := filepath.Join(base, "link")
133+ require.NoError(t, os.Symlink(real, link))
134+
135+ proc := filepath.Join(base, "proc", "20")
136+ require.NoError(t, os.MkdirAll(proc, 0o755))
137+ require.NoError(t, os.Symlink(real, filepath.Join(proc, "cwd")))
138+ require.NoError(t, os.WriteFile(filepath.Join(proc, "environ"), []byte("VEV_ENV=dev\x00PWD="+link+"\x00"), 0o644))
139+
140+ uid := os.Getuid()
141+ require.Equal(t, ipc.SocketDirFor(filepath.Join(link, ".dev", "dev", "runtime"), uid), processSocketDir(proc, uid))
142+
143+ // A stale PWD that names another directory is ignored.
144+ require.NoError(t, os.WriteFile(filepath.Join(proc, "environ"), []byte("VEV_ENV=dev\x00PWD="+base+"\x00"), 0o644))
145+ require.Equal(t, ipc.SocketDirFor(filepath.Join(real, ".dev", "dev", "runtime"), uid), processSocketDir(proc, uid))
146+}
147+
126148 func TestRuntimeDirResolutionMatchesVev(t *testing.T) {
127149 tests := []struct {
128150 name string
@@ -123,6 +123,28 @@ func TestFindVevProcesses(t *testing.T) {
123 require.Empty(t, none, "another user's processes are never matched")123 require.Empty(t, none, "another user's processes are never matched")
124 }124 }
125 125
126+// TestProcessSocketDirPrefersSymlinkedPWD pins that a VEV_ENV client started
127+// in a repo reached through a symlink resolves to the same path vev used.
128+func TestProcessSocketDirPrefersSymlinkedPWD(t *testing.T) {
129+ base := t.TempDir()
130+ real := filepath.Join(base, "real")
131+ require.NoError(t, os.Mkdir(real, 0o755))
132+ link := filepath.Join(base, "link")
133+ require.NoError(t, os.Symlink(real, link))
134+
135+ proc := filepath.Join(base, "proc", "20")
136+ require.NoError(t, os.MkdirAll(proc, 0o755))
137+ require.NoError(t, os.Symlink(real, filepath.Join(proc, "cwd")))
138+ require.NoError(t, os.WriteFile(filepath.Join(proc, "environ"), []byte("VEV_ENV=dev\x00PWD="+link+"\x00"), 0o644))
139+
140+ uid := os.Getuid()
141+ require.Equal(t, ipc.SocketDirFor(filepath.Join(link, ".dev", "dev", "runtime"), uid), processSocketDir(proc, uid))
142+
143+ // A stale PWD that names another directory is ignored.
144+ require.NoError(t, os.WriteFile(filepath.Join(proc, "environ"), []byte("VEV_ENV=dev\x00PWD="+base+"\x00"), 0o644))
145+ require.Equal(t, ipc.SocketDirFor(filepath.Join(real, ".dev", "dev", "runtime"), uid), processSocketDir(proc, uid))
146+}
147+
126 func TestRuntimeDirResolutionMatchesVev(t *testing.T) {148 func TestRuntimeDirResolutionMatchesVev(t *testing.T) {
127 tests := []struct {149 tests := []struct {
128 name string150 name string

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 12

Tags 11

↑↓ moveenter openesc close