Skip to content

Commit 8d044ef

Browse files
committed
feat(sync): support remote exclude patterns for syncthing
1 parent fd0aace commit 8d044ef

6 files changed

Lines changed: 51 additions & 7 deletions

File tree

docs/okdev-design.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ spec:
113113
- .git/
114114
- .venv/
115115
- node_modules/
116+
remoteExclude:
117+
- checkpoints/
116118
ports:
117119
- name: api
118120
local: 8080
@@ -241,7 +243,7 @@ Ownership model:
241243
- `okdev sync [--mode=bi|up|down] [--background] [--dry-run]`
242244
- syncthing engine: continuous sync for single path mapping
243245
- supports detached syncthing mode via `--background`
244-
- exclude support, `.stignore` generation for syncthing
246+
- exclude support, `.stignore` generation for local (`exclude`) and remote (`remoteExclude`)
245247

246248
- `okdev ports`
247249
- establish all declared forwards

docs/quickstart.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ For a named session:
6464

6565
`okdev` auto-installs local Syncthing and auto-injects a pod sidecar when `sync.engine=syncthing`.
6666
Default sidecar image tag follows the running `okdev` binary version (`ghcr.io/<repo-owner>/okdev:<okdev-version>`). For dev builds, fallback is `ghcr.io/<repo-owner>/okdev:edge`. Update `spec.sync.syncthing.image` only if you publish to a different registry/repository.
67+
Use `spec.sync.exclude` for local ignore patterns and `spec.sync.remoteExclude` for remote-only ignore patterns.
6768

6869
Preview-only mode (no cluster changes):
6970

internal/cli/syncthing.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ func runSyncthingSync(cmd *cobra.Command, opts *Options, cfg *config.DevEnvironm
7272
if _, err := k.ExecShInContainer(ctx, namespace, pod, syncthingContainerName, fmt.Sprintf("mkdir -p %s", syncengine.ShellEscape(pair.Remote))); err != nil {
7373
return err
7474
}
75+
if err := writeRemoteSTIgnoreInPod(ctx, k, namespace, pod, pair.Remote, cfg.Spec.Sync.RemoteExclude); err != nil {
76+
return err
77+
}
7578

7679
localHome, err := localSyncthingHome(sessionName)
7780
if err != nil {
@@ -144,9 +147,17 @@ func runSyncthingSync(cmd *cobra.Command, opts *Options, cfg *config.DevEnvironm
144147
}
145148

146149
func writeSTIgnore(localPath string, excludes []string) error {
147-
if len(excludes) == 0 {
150+
content, ok := buildSTIgnoreContent(excludes)
151+
if !ok {
148152
return nil
149153
}
154+
return os.WriteFile(filepath.Join(localPath, ".stignore"), []byte(content), 0o644)
155+
}
156+
157+
func buildSTIgnoreContent(excludes []string) (string, bool) {
158+
if len(excludes) == 0 {
159+
return "", false
160+
}
150161
var b strings.Builder
151162
for _, ex := range excludes {
152163
ex = strings.TrimSpace(ex)
@@ -159,9 +170,25 @@ func writeSTIgnore(localPath string, excludes []string) error {
159170
}
160171
}
161172
if b.Len() == 0 {
173+
return "", false
174+
}
175+
return b.String(), true
176+
}
177+
178+
func writeRemoteSTIgnoreInPod(ctx context.Context, k interface {
179+
ExecShInContainer(context.Context, string, string, string, string) ([]byte, error)
180+
}, namespace, pod, remotePath string, excludes []string) error {
181+
content, ok := buildSTIgnoreContent(excludes)
182+
if !ok {
162183
return nil
163184
}
164-
return os.WriteFile(filepath.Join(localPath, ".stignore"), []byte(b.String()), 0o644)
185+
ignorePath := strings.TrimRight(remotePath, "/") + "/.stignore"
186+
if ignorePath == "/.stignore" || strings.TrimSpace(ignorePath) == "" {
187+
return fmt.Errorf("invalid remote sync path %q for remoteExclude", remotePath)
188+
}
189+
cmd := fmt.Sprintf("cat > %s <<'OKDEV_STIGNORE_EOF'\n%sOKDEV_STIGNORE_EOF\n", syncengine.ShellEscape(ignorePath), content)
190+
_, err := k.ExecShInContainer(ctx, namespace, pod, syncthingContainerName, cmd)
191+
return err
165192
}
166193

167194
func localSyncthingHome(session string) (string, error) {

internal/cli/syncthing_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,13 @@ func TestSyncthingCompletion(t *testing.T) {
5757
t.Fatalf("unexpected completion values pct=%v need=%d", pct, need)
5858
}
5959
}
60+
61+
func TestBuildSTIgnoreContent(t *testing.T) {
62+
content, ok := buildSTIgnoreContent([]string{" .git/ ", "", "node_modules/"})
63+
if !ok {
64+
t.Fatal("expected content")
65+
}
66+
if content != ".git/\nnode_modules/\n" {
67+
t.Fatalf("unexpected content %q", content)
68+
}
69+
}

internal/config/config.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,11 @@ type MetadataMap struct {
6868
}
6969

7070
type SyncSpec struct {
71-
Paths []string `yaml:"paths"`
72-
Exclude []string `yaml:"exclude"`
73-
Engine string `yaml:"engine"`
74-
Syncthing SyncthingSpec `yaml:"syncthing"`
71+
Paths []string `yaml:"paths"`
72+
Exclude []string `yaml:"exclude"`
73+
RemoteExclude []string `yaml:"remoteExclude"`
74+
Engine string `yaml:"engine"`
75+
Syncthing SyncthingSpec `yaml:"syncthing"`
7576
}
7677

7778
type SyncthingSpec struct {

internal/config/template.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ spec:
2929
- .git/
3030
- .venv/
3131
- node_modules/
32+
remoteExclude: []
3233
ports:
3334
- name: app
3435
local: 8080
@@ -69,6 +70,7 @@ spec:
6970
- node_modules/
7071
- checkpoints/
7172
- data/
73+
remoteExclude: []
7274
ports:
7375
- name: api
7476
local: 8080
@@ -134,6 +136,7 @@ spec:
134136
- node_modules/
135137
- checkpoints/
136138
- data/
139+
remoteExclude: []
137140
ports:
138141
- name: app
139142
local: 8080

0 commit comments

Comments
 (0)