detached 2f5b15c

fix(remote): address review findings

commit
2f5b15ccb375172ad0defee0cf1f12cabbefdce5
author
brice · · 2026-07-06 21:40 UTC
parent
d737ef0
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
4 files · +17 -9
M internal/adapters/dgram/ssh_dialer.go +4 -0
@@ -113,6 +113,10 @@ func (d RemoteDialer) Dial(ctx context.Context) (ports.Transport, error) {
113113
114114 ready, err := readUDPReady(stdout)
115115 if err != nil {
116+ _ = proc.Kill()
117+ _ = proc.Wait()
118+ waited = true
119+ cleanup = false
116120 return nil, udpUnavailable("read bootstrap readiness", err, &stderr)
117121 }
118122 if err := proc.Wait(); err != nil {
@@ -113,6 +113,10 @@ func (d RemoteDialer) Dial(ctx context.Context) (ports.Transport, error) {
113 113
114 ready, err := readUDPReady(stdout)114 ready, err := readUDPReady(stdout)
115 if err != nil {115 if err != nil {
116+ _ = proc.Kill()
117+ _ = proc.Wait()
118+ waited = true
119+ cleanup = false
116 return nil, udpUnavailable("read bootstrap readiness", err, &stderr)120 return nil, udpUnavailable("read bootstrap readiness", err, &stderr)
117 }121 }
118 if err := proc.Wait(); err != nil {122 if err := proc.Wait(); err != nil {
M internal/adapters/remote/remote_factory.go +2 -0
@@ -13,6 +13,8 @@ import (
1313 // DialerFactory selects the requested remote transport adapter.
1414 type DialerFactory struct{}
1515
16+var _ ports.RemoteDialerFactory = DialerFactory{}
17+
1618 func NewDialerFactory() DialerFactory { return DialerFactory{} }
1719
1820 func (DialerFactory) DialerForRemote(target string, session string, mode ports.RemoteTransportMode, log *slog.Logger) (ports.Dialer, error) {
@@ -13,6 +13,8 @@ import (
13 // DialerFactory selects the requested remote transport adapter.13 // DialerFactory selects the requested remote transport adapter.
14 type DialerFactory struct{}14 type DialerFactory struct{}
15 15
16+var _ ports.RemoteDialerFactory = DialerFactory{}
17+
16 func NewDialerFactory() DialerFactory { return DialerFactory{} }18 func NewDialerFactory() DialerFactory { return DialerFactory{} }
17 19
18 func (DialerFactory) DialerForRemote(target string, session string, mode ports.RemoteTransportMode, log *slog.Logger) (ports.Dialer, error) {20 func (DialerFactory) DialerForRemote(target string, session string, mode ports.RemoteTransportMode, log *slog.Logger) (ports.Dialer, error) {
M internal/app/remote_test.go +1 -0
@@ -20,6 +20,7 @@ func TestParseRemoteAttachTarget(t *testing.T) {
2020 {name: "bracketed ipv6 no session", input: "user@[2001:db8::1]", wantTarget: "user@[2001:db8::1]", wantOK: true},
2121 {name: "bracketed ipv6 with session", input: "user@[2001:db8::1]:work", wantTarget: "user@[2001:db8::1]", wantSession: "work", wantOK: true},
2222 {name: "malformed bracketed ipv6", input: "user@[2001:db8::1"},
23+ {name: "malformed suffix after bracket", input: "user@[2001:db8::1]work"},
2324 {name: "target with at in userinfo", input: "first@user@example.com:work", wantTarget: "first@user@example.com", wantSession: "work", wantOK: true},
2425 }
2526
@@ -20,6 +20,7 @@ func TestParseRemoteAttachTarget(t *testing.T) {
20 {name: "bracketed ipv6 no session", input: "user@[2001:db8::1]", wantTarget: "user@[2001:db8::1]", wantOK: true},20 {name: "bracketed ipv6 no session", input: "user@[2001:db8::1]", wantTarget: "user@[2001:db8::1]", wantOK: true},
21 {name: "bracketed ipv6 with session", input: "user@[2001:db8::1]:work", wantTarget: "user@[2001:db8::1]", wantSession: "work", wantOK: true},21 {name: "bracketed ipv6 with session", input: "user@[2001:db8::1]:work", wantTarget: "user@[2001:db8::1]", wantSession: "work", wantOK: true},
22 {name: "malformed bracketed ipv6", input: "user@[2001:db8::1"},22 {name: "malformed bracketed ipv6", input: "user@[2001:db8::1"},
23+ {name: "malformed suffix after bracket", input: "user@[2001:db8::1]work"},
23 {name: "target with at in userinfo", input: "first@user@example.com:work", wantTarget: "first@user@example.com", wantSession: "work", wantOK: true},24 {name: "target with at in userinfo", input: "first@user@example.com:work", wantTarget: "first@user@example.com", wantSession: "work", wantOK: true},
24 }25 }
25 26
M internal/app/run_test.go +10 -9
@@ -466,7 +466,11 @@ func TestRunAttachWithDepsSelectsRemoteTransport(t *testing.T) {
466466 selectedRemoteTransport: tt.selectedTransport,
467467 clipboard: clip,
468468 runClient: func(_ context.Context, d ports.Dialer, _ ports.Terminal, _ ports.Clock, intent uint8, name string, remote bool, clipboard ports.ClipboardReader, _ *slog.Logger) error {
469- gotDialer = d.(namedDialer).name
469+ nd, ok := d.(namedDialer)
470+ if !ok {
471+ t.Fatalf("dialer type = %T, want namedDialer", d)
472+ }
473+ gotDialer = nd.name
470474 gotRemote = remote
471475 gotClipboard = clipboard
472476 if intent != ports.IntentAttach || name != "work" {
@@ -542,7 +546,11 @@ func TestRunAttachWithDepsBuildsLocalDialer(t *testing.T) {
542546 remoteDialerFactory: factory,
543547 clipboard: &fakeClipboardReader{}, // must NOT reach runClient for a local attach
544548 runClient: func(_ context.Context, d ports.Dialer, _ ports.Terminal, _ ports.Clock, intent uint8, name string, remote bool, clipboard ports.ClipboardReader, _ *slog.Logger) error {
545- gotDialer = d.(namedDialer).name
549+ nd, ok := d.(namedDialer)
550+ if !ok {
551+ t.Fatalf("dialer type = %T, want namedDialer", d)
552+ }
553+ gotDialer = nd.name
546554 gotRemote = remote
547555 gotClipboard = clipboard
548556 if intent != ports.IntentEphemeral || name != "" {
@@ -570,20 +578,13 @@ func TestRunUDPBootstrapForwardsReadinessAndExits(t *testing.T) {
570578 udpBootstrapTimeout = time.Second
571579 t.Cleanup(func() { udpBootstrapTimeout = oldTimeout })
572580 oldCommand := udpProxyCommand
573- r, w, err := os.Pipe()
574- if err != nil {
575- t.Fatal(err)
576- }
577- defer func() { _ = r.Close() }()
578581 var gotCmd *exec.Cmd
579582 udpProxyCommand = func(context.Context, string, ...string) *exec.Cmd {
580583 cmd := exec.Command("/bin/sh", "-c", "printf 'VEV-UDP 4242 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\\n'")
581- cmd.Stdout = w
582584 gotCmd = cmd
583585 return cmd
584586 }
585587 t.Cleanup(func() { udpProxyCommand = oldCommand })
586- defer func() { _ = w.Close() }()
587588
588589 got := captureStdout(t, func() {
589590 if err := runUDPBootstrap(context.Background(), "work"); err != nil {
@@ -466,7 +466,11 @@ func TestRunAttachWithDepsSelectsRemoteTransport(t *testing.T) {
466 selectedRemoteTransport: tt.selectedTransport,466 selectedRemoteTransport: tt.selectedTransport,
467 clipboard: clip,467 clipboard: clip,
468 runClient: func(_ context.Context, d ports.Dialer, _ ports.Terminal, _ ports.Clock, intent uint8, name string, remote bool, clipboard ports.ClipboardReader, _ *slog.Logger) error {468 runClient: func(_ context.Context, d ports.Dialer, _ ports.Terminal, _ ports.Clock, intent uint8, name string, remote bool, clipboard ports.ClipboardReader, _ *slog.Logger) error {
469- gotDialer = d.(namedDialer).name469+ nd, ok := d.(namedDialer)
470+ if !ok {
471+ t.Fatalf("dialer type = %T, want namedDialer", d)
472+ }
473+ gotDialer = nd.name
470 gotRemote = remote474 gotRemote = remote
471 gotClipboard = clipboard475 gotClipboard = clipboard
472 if intent != ports.IntentAttach || name != "work" {476 if intent != ports.IntentAttach || name != "work" {
@@ -542,7 +546,11 @@ func TestRunAttachWithDepsBuildsLocalDialer(t *testing.T) {
542 remoteDialerFactory: factory,546 remoteDialerFactory: factory,
543 clipboard: &fakeClipboardReader{}, // must NOT reach runClient for a local attach547 clipboard: &fakeClipboardReader{}, // must NOT reach runClient for a local attach
544 runClient: func(_ context.Context, d ports.Dialer, _ ports.Terminal, _ ports.Clock, intent uint8, name string, remote bool, clipboard ports.ClipboardReader, _ *slog.Logger) error {548 runClient: func(_ context.Context, d ports.Dialer, _ ports.Terminal, _ ports.Clock, intent uint8, name string, remote bool, clipboard ports.ClipboardReader, _ *slog.Logger) error {
545- gotDialer = d.(namedDialer).name549+ nd, ok := d.(namedDialer)
550+ if !ok {
551+ t.Fatalf("dialer type = %T, want namedDialer", d)
552+ }
553+ gotDialer = nd.name
546 gotRemote = remote554 gotRemote = remote
547 gotClipboard = clipboard555 gotClipboard = clipboard
548 if intent != ports.IntentEphemeral || name != "" {556 if intent != ports.IntentEphemeral || name != "" {
@@ -570,20 +578,13 @@ func TestRunUDPBootstrapForwardsReadinessAndExits(t *testing.T) {
570 udpBootstrapTimeout = time.Second578 udpBootstrapTimeout = time.Second
571 t.Cleanup(func() { udpBootstrapTimeout = oldTimeout })579 t.Cleanup(func() { udpBootstrapTimeout = oldTimeout })
572 oldCommand := udpProxyCommand580 oldCommand := udpProxyCommand
573- r, w, err := os.Pipe()
574- if err != nil {
575- t.Fatal(err)
576- }
577- defer func() { _ = r.Close() }()
578 var gotCmd *exec.Cmd581 var gotCmd *exec.Cmd
579 udpProxyCommand = func(context.Context, string, ...string) *exec.Cmd {582 udpProxyCommand = func(context.Context, string, ...string) *exec.Cmd {
580 cmd := exec.Command("/bin/sh", "-c", "printf 'VEV-UDP 4242 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\\n'")583 cmd := exec.Command("/bin/sh", "-c", "printf 'VEV-UDP 4242 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\\n'")
581- cmd.Stdout = w
582 gotCmd = cmd584 gotCmd = cmd
583 return cmd585 return cmd
584 }586 }
585 t.Cleanup(func() { udpProxyCommand = oldCommand })587 t.Cleanup(func() { udpProxyCommand = oldCommand })
586- defer func() { _ = w.Close() }()
587 588
588 got := captureStdout(t, func() {589 got := captureStdout(t, func() {
589 if err := runUDPBootstrap(context.Background(), "work"); err != nil {590 if err := runUDPBootstrap(context.Background(), "work"); err != nil {

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