Skip to content

Commit 6675c38

Browse files
committed
feat(cli): show sync paths during up
1 parent dc6db44 commit 6675c38

4 files changed

Lines changed: 80 additions & 9 deletions

File tree

AGENTS.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ This file is the canonical repository guidance for coding agents working in `okd
55
## Workflow
66

77
- Prefer targeted, repo-relevant tests before committing.
8+
- Clean up resources created by ad-hoc e2e tests before finishing the task.
89
- Prefer `rg` and `rg --files` for search.
910
- Keep changes narrow and preserve existing patterns unless the task requires a broader refactor.
1011
- Update user-facing docs when behavior changes.
@@ -31,9 +32,16 @@ This file is the canonical repository guidance for coding agents working in `okd
3132
- When asked to update a local install to a release, prefer the published release asset over a workspace build.
3233
- For release follow-ups, verify the actual published artifact or image rather than assuming the workflow succeeded.
3334

35+
## E2E Testing Rules
36+
37+
- Prefer the smallest realistic end-to-end check that validates the changed behavior.
38+
- Reuse existing local installs, release binaries, and published sidecar images when they are sufficient for the test.
39+
- If an e2e test creates cluster resources, local background processes, SSH config entries, port-forwards, or temporary files, clean them up before finishing unless the user explicitly asks to keep them.
40+
- For `okdev` session tests, prefer tearing down with `okdev down` after verification when the session was created only for the test.
41+
- If cleanup cannot be completed, say exactly what was left behind and where.
42+
3443
## Repo-Specific Expectations
3544

3645
- Keep sidecar, SSH, tmux, and Syncthing behavior aligned; changes in one often require checking the others.
3746
- Be careful with config-loading output paths because many commands share the same helper logic.
3847
- When changing pod or sidecar behavior, consider both local installs and sidecar image rebuild/publish steps.
39-

docs/command-reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
- tmux-backed persistent interactive shells are enabled by default.
2222
- `--tmux`: explicitly enable tmux mode on the sidecar.
2323
- `--no-tmux`: disable tmux mode for this pod.
24+
- When `sync.engine=syncthing`, `okdev up` starts background sync in bidirectional mode by default.
2425
- `spec.ports` is materialized as SSH `LocalForward`.
2526
- `okdev down [--delete-pvc] [--dry-run]`
2627
- `okdev status [--all] [--all-users]`

docs/quickstart.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ Tmux-backed persistent shells are enabled by default. Disable them for a pod wit
5959
- ensures Pod/PVC state
6060
- writes managed `~/.ssh/config` host entry (`okdev-<session>`)
6161
- starts managed SSH/forwarding bootstrap
62-
- starts background Syncthing sync when enabled
62+
- starts background Syncthing sync in bidirectional mode when enabled
6363
- exits after setup (no attach mode)
6464

6565
## Access and Data Movement
@@ -80,6 +80,7 @@ Tmux-backed persistent shells are enabled by default. Disable them for a pod wit
8080

8181
Notes:
8282
- Local Syncthing is auto-installed when `sync.engine=syncthing`.
83+
- `okdev up` starts background sync in bidirectional mode by default.
8384
- Default sidecar image follows binary version: `ghcr.io/<owner>/okdev:<okdev-version>` (`edge` for dev builds).
8485
- `spec.sync.exclude` applies local ignore patterns; `spec.sync.remoteExclude` applies remote-only ignores.
8586
- SSH target is always the merged `okdev-sidecar` container (`sshd` on `22`).

internal/cli/up.go

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,20 @@ import (
66
"io"
77
"log/slog"
88
"os"
9+
"path/filepath"
910
"strings"
1011
"time"
1112

1213
"github.com/acmore/okdev/internal/config"
1314
"github.com/acmore/okdev/internal/kube"
1415
"github.com/acmore/okdev/internal/session"
16+
syncengine "github.com/acmore/okdev/internal/sync"
1517
"github.com/spf13/cobra"
1618
corev1 "k8s.io/api/core/v1"
1719
)
1820

21+
const upDefaultSyncMode = "bi"
22+
1923
func newUpCmd(opts *Options) *cobra.Command {
2024
var waitTimeout time.Duration
2125
var dryRun bool
@@ -66,6 +70,10 @@ func newUpCmd(opts *Options) *cobra.Command {
6670
annotations := annotationsForSession(cfg)
6771
annotations["okdev.io/ssh-mode"] = normalizeSSHMode(sshMode)
6872
volumes := cfg.EffectiveVolumes()
73+
syncPairs, err := syncengine.ParsePairs(cfg.Spec.Sync.Paths, cfg.WorkspaceMountPath())
74+
if err != nil {
75+
return err
76+
}
6977
pod := podName(sn)
7078
if dryRun {
7179
ui.section("Dry Run")
@@ -77,6 +85,9 @@ func newUpCmd(opts *Options) *cobra.Command {
7785
fmt.Fprintf(cmd.OutOrStdout(), "- would apply pod/%s\n", pod)
7886
fmt.Fprintf(cmd.OutOrStdout(), "- would wait for pod readiness (timeout=%s)\n", waitTimeout)
7987
fmt.Fprintln(cmd.OutOrStdout(), "- would setup SSH config + managed SSH/port-forwards")
88+
for _, pair := range syncPairs {
89+
fmt.Fprintf(cmd.OutOrStdout(), "- would sync %s -> %s\n", displayLocalSyncPath(pair.Local), pair.Remote)
90+
}
8091
fmt.Fprintln(cmd.OutOrStdout(), "- would start background sync (when sync.engine=syncthing)")
8192
return nil
8293
}
@@ -202,18 +213,21 @@ func newUpCmd(opts *Options) *cobra.Command {
202213
}
203214

204215
syncSummary := "disabled"
216+
syncModeSymbol := ""
205217
if cfg.Spec.Sync.Engine == "" || cfg.Spec.Sync.Engine == "syncthing" {
206-
logPath, started, err := startDetachedSyncthingSync(opts, "bi", sn, ns, cfgPath)
218+
logPath, started, err := startDetachedSyncthingSync(opts, upDefaultSyncMode, sn, ns, cfgPath)
207219
if err != nil {
208220
ui.warnf("failed to start syncthing background sync: %v", err)
209221
syncSummary = "degraded (start failed)"
210222
} else {
223+
syncModeSymbol = modeSymbol(upDefaultSyncMode)
224+
syncPathSummary := syncPairsSummary(syncPairs, syncModeSymbol)
211225
if started {
212-
syncSummary = "active (bi)"
213-
ui.stepDone("sync", fmt.Sprintf("active (bi), logs: %s", logPath))
226+
syncSummary = "active (" + syncModeSymbol + ")"
227+
ui.stepDone("sync", fmt.Sprintf("active (%s), %s, logs: %s", syncModeSymbol, syncPathSummary, logPath))
214228
} else {
215-
syncSummary = "already active (bi)"
216-
ui.stepDone("sync", fmt.Sprintf("already active (bi), logs: %s", logPath))
229+
syncSummary = "already active (" + syncModeSymbol + ")"
230+
ui.stepDone("sync", fmt.Sprintf("already active (%s), %s, logs: %s", syncModeSymbol, syncPathSummary, logPath))
217231
}
218232
}
219233
}
@@ -231,7 +245,7 @@ func newUpCmd(opts *Options) *cobra.Command {
231245
}
232246

233247
ui.printWarnings()
234-
ui.printReadyCard(sn, ns, pod, sshSummary, syncSummary, cfg.Spec.Ports)
248+
ui.printReadyCard(sn, ns, pod, sshSummary, syncSummary, cfg.Spec.Ports, syncPairs, syncModeSymbol)
235249
return nil
236250
},
237251
}
@@ -383,13 +397,19 @@ func (u *upUI) warnWriter() io.Writer {
383397
return upWarnSink{ui: u}
384398
}
385399

386-
func (u *upUI) printReadyCard(sessionName, namespace, pod, sshSummary, syncSummary string, ports []config.PortMapping) {
400+
func (u *upUI) printReadyCard(sessionName, namespace, pod, sshSummary, syncSummary string, ports []config.PortMapping, syncPairs []syncengine.Pair, syncModeSymbol string) {
387401
fmt.Fprintln(u.out, "\n== Ready ==")
388402
fmt.Fprintf(u.out, "session: %s\n", sessionName)
389403
fmt.Fprintf(u.out, "namespace: %s\n", namespace)
390404
fmt.Fprintf(u.out, "pod: %s\n", pod)
391405
fmt.Fprintf(u.out, "ssh: %s\n", sshSummary)
392406
fmt.Fprintf(u.out, "sync: %s\n", syncSummary)
407+
if len(syncPairs) > 0 {
408+
fmt.Fprintln(u.out, "sync paths:")
409+
for _, pair := range syncPairs {
410+
fmt.Fprintf(u.out, "- %s %s %s\n", displayLocalSyncPath(pair.Local), syncArrow(syncModeSymbol), pair.Remote)
411+
}
412+
}
393413
if len(ports) > 0 {
394414
fmt.Fprintln(u.out, "forwards:")
395415
for _, p := range ports {
@@ -409,6 +429,47 @@ func (u *upUI) printReadyCard(sessionName, namespace, pod, sshSummary, syncSumma
409429
fmt.Fprintln(u.out, "- okdev down")
410430
}
411431

432+
func syncPairsSummary(pairs []syncengine.Pair, syncModeSymbol string) string {
433+
if len(pairs) == 0 {
434+
return "no paths"
435+
}
436+
if len(pairs) == 1 {
437+
pair := pairs[0]
438+
return fmt.Sprintf("%s %s %s", displayLocalSyncPath(pair.Local), syncArrow(syncModeSymbol), pair.Remote)
439+
}
440+
return fmt.Sprintf("%d paths", len(pairs))
441+
}
442+
443+
func displayLocalSyncPath(path string) string {
444+
if strings.TrimSpace(path) == "" {
445+
return path
446+
}
447+
if abs, err := filepath.Abs(path); err == nil {
448+
return abs
449+
}
450+
return path
451+
}
452+
453+
func syncArrow(syncModeSymbol string) string {
454+
if strings.TrimSpace(syncModeSymbol) == "" {
455+
return "->"
456+
}
457+
return syncModeSymbol
458+
}
459+
460+
func modeSymbol(mode string) string {
461+
switch strings.TrimSpace(mode) {
462+
case "up":
463+
return "->"
464+
case "down":
465+
return "<-"
466+
case "bi":
467+
return "<->"
468+
default:
469+
return "->"
470+
}
471+
}
472+
412473
type upWarnSink struct {
413474
ui *upUI
414475
}

0 commit comments

Comments
 (0)