Skip to content

Commit b5b442c

Browse files
committed
fix(cli): default demo-mode file picker to cwd, not ~/Downloads
`micasa demo` wires the document file picker to the system Downloads folder by default. In tape recordings, tutorials, and manual demos, that surfaces whatever happens to live in the recorder's real Downloads — producing non-reproducible captures and risking leaked PII in committed demo videos. In demo mode, prefer the current working directory when no explicit `documents.file_picker_dir` / `MICASA_DOCUMENTS_FILE_PICKER_DIR` is set. Explicit config still wins, and production behavior is unchanged.
1 parent c7c7a55 commit b5b442c

3 files changed

Lines changed: 73 additions & 1 deletion

File tree

cmd/micasa/main.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,16 @@ func launchTUI(dbPath string, seed *seedOpts) error {
207207
return fmt.Errorf("resolve currency: %w", err)
208208
}
209209

210+
pickerDir := cfg.Documents.ResolvedFilePickerDir()
211+
if seed != nil {
212+
// Demo mode: prefer cwd so tapes and tutorials aren't at the
213+
// mercy of whatever's in the recorder's ~/Downloads.
214+
pickerDir = cfg.Documents.ResolvedFilePickerDirForDemo()
215+
}
210216
appOpts := app.Options{
211217
DBPath: dbPath,
212218
ConfigPath: config.Path(),
213-
FilePickerDir: cfg.Documents.ResolvedFilePickerDir(),
219+
FilePickerDir: pickerDir,
214220
AddressAutofill: cfg.Address.IsAutofillEnabled(),
215221
AddressCountry: config.DetectCountry(),
216222
}

internal/config/config.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,30 @@ func (d Documents) ResolvedFilePickerDir() string {
280280
return "."
281281
}
282282

283+
// ResolvedFilePickerDirForDemo is like ResolvedFilePickerDir but prefers
284+
// the current working directory over the system Downloads folder when no
285+
// value is configured. Demo recordings and tutorials stage their fixtures
286+
// in cwd; falling back to ~/Downloads there would surface whatever files
287+
// happen to live in the recorder's real Downloads, making captures
288+
// non-reproducible and risking leaked PII in committed demo videos.
289+
// Explicit config still wins.
290+
func (d Documents) ResolvedFilePickerDirForDemo() string {
291+
if d.FilePickerDir != "" {
292+
if info, err := os.Stat(d.FilePickerDir); err == nil && info.IsDir() {
293+
return d.FilePickerDir
294+
}
295+
}
296+
if dir, err := os.Getwd(); err == nil {
297+
return dir
298+
}
299+
if dir := xdg.UserDirs.Download; dir != "" {
300+
if info, err := os.Stat(dir); err == nil && info.IsDir() {
301+
return dir
302+
}
303+
}
304+
return "."
305+
}
306+
283307
// CacheTTLDuration returns the resolved cache TTL as a time.Duration.
284308
// Returns 0 to disable eviction.
285309
func (d Documents) CacheTTLDuration() time.Duration {

internal/config/config_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,6 +1071,48 @@ func TestResolvedFilePickerDir_EmptyFallsBackToDownloadsOrCwd(t *testing.T) {
10711071
assert.NotEmpty(t, result)
10721072
}
10731073

1074+
func TestResolvedFilePickerDirForDemo_ConfiguredDirWins(t *testing.T) {
1075+
t.Parallel()
1076+
dir := t.TempDir()
1077+
d := Documents{FilePickerDir: dir}
1078+
assert.Equal(t, dir, d.ResolvedFilePickerDirForDemo(),
1079+
"explicit config should win in demo mode just like normal mode")
1080+
}
1081+
1082+
func TestResolvedFilePickerDirForDemo_EmptyPrefersCwdOverDownloads(t *testing.T) {
1083+
// Not parallel: we mutate cwd.
1084+
cwd := t.TempDir()
1085+
t.Chdir(cwd)
1086+
1087+
d := Documents{}
1088+
result := d.ResolvedFilePickerDirForDemo()
1089+
1090+
// Resolve symlinks on both sides: macOS returns /private/var/... for
1091+
// temp dirs via Getwd while t.TempDir reports /var/...
1092+
wantResolved, err := filepath.EvalSymlinks(cwd)
1093+
require.NoError(t, err)
1094+
gotResolved, err := filepath.EvalSymlinks(result)
1095+
require.NoError(t, err)
1096+
assert.Equal(t, wantResolved, gotResolved,
1097+
"demo mode with no config should resolve to cwd, "+
1098+
"not the user's Downloads folder")
1099+
}
1100+
1101+
func TestResolvedFilePickerDirForDemo_ConfiguredMissingFallsBackToCwd(t *testing.T) {
1102+
cwd := t.TempDir()
1103+
t.Chdir(cwd)
1104+
1105+
d := Documents{FilePickerDir: "/nonexistent/path/that/does/not/exist"}
1106+
result := d.ResolvedFilePickerDirForDemo()
1107+
1108+
wantResolved, err := filepath.EvalSymlinks(cwd)
1109+
require.NoError(t, err)
1110+
gotResolved, err := filepath.EvalSymlinks(result)
1111+
require.NoError(t, err)
1112+
assert.Equal(t, wantResolved, gotResolved,
1113+
"when configured dir is invalid, demo mode should fall back to cwd")
1114+
}
1115+
10741116
func TestFilePickerDir_FromTOML(t *testing.T) {
10751117
t.Parallel()
10761118
dir := t.TempDir()

0 commit comments

Comments
 (0)