feat(watch): add opt-in --backfill flag for existing archives

Co-authored-by: Belphemur <197810+Belphemur@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-07-03 15:44:08 +00:00
committed by GitHub
co-authored by Belphemur
parent 0b3c3fa90c
commit 1d981be21b
4 changed files with 59 additions and 14 deletions
+15 -7
View File
@@ -41,6 +41,9 @@ func init() {
// Setup common flags (format, quality, override, split, timeout) with viper binding
setupCommonFlags(command, &converterType, 85, true, false, true)
command.Flags().Bool("backfill", false, "Optimize CBZ/CBR files that already exist in the watched folder at startup, before watching for new changes")
_ = viper.BindPFlag("backfill", command.Flags().Lookup("backfill"))
AddCommand(command)
}
func WatchCommand(_ *cobra.Command, args []string) error {
@@ -64,6 +67,8 @@ func WatchCommand(_ *cobra.Command, args []string) error {
timeout := viper.GetDuration("timeout")
backfill := viper.GetBool("backfill")
converterType := constant.FindConversionFormat(viper.GetString("format"))
chapterConverter, err := converter.Get(converterType)
if err != nil {
@@ -74,7 +79,7 @@ func WatchCommand(_ *cobra.Command, args []string) error {
if err != nil {
return fmt.Errorf("failed to prepare converter: %w", err)
}
log.Info().Str("path", path).Bool("override", override).Uint8("quality", quality).Str("format", converterType.String()).Bool("split", split).Msg("Watching directory")
log.Info().Str("path", path).Bool("override", override).Uint8("quality", quality).Str("format", converterType.String()).Bool("split", split).Bool("backfill", backfill).Msg("Watching directory")
watcher, err := fsnotify.NewWatcher()
if err != nil {
@@ -101,15 +106,18 @@ func WatchCommand(_ *cobra.Command, args []string) error {
defer debouncer.Stop()
// Note: existing archives already present under path when the watch
// starts are intentionally left untouched. Watch mode only reacts to
// filesystem events going forward; use the `optimize` command to process
// a library's existing contents. Archives inside a directory that is
// created/moved into the watched tree *after* startup are still
// back-filled below, since only the directory itself generates an
// fsnotify event.
// starts are left untouched unless --backfill is set. Watch mode only
// reacts to filesystem events going forward by default; use the
// `optimize` command (or pass --backfill) to process a library's
// existing contents. Archives inside a directory that is created/moved
// into the watched tree *after* startup are always back-filled below,
// since only the directory itself generates an fsnotify event.
if err := addRecursiveWatch(watcher, path); err != nil {
return fmt.Errorf("failed to watch path %s: %w", path, err)
}
if backfill {
backfillExistingArchives(path, debouncer.Trigger)
}
for {
select {
@@ -170,3 +170,37 @@ func TestOptimizeQueueSkipsMissingPath(t *testing.T) {
assert.EqualValues(t, 1, atomic.LoadInt32(&calls))
}
func TestWatchCommandBackfillFlagDefaultsToFalse(t *testing.T) {
watchCmd, _, err := rootCmd.Find([]string{"watch"})
require.NoError(t, err)
flag := watchCmd.Flags().Lookup("backfill")
require.NotNil(t, flag, "watch command should register a --backfill flag")
assert.Equal(t, "false", flag.DefValue)
assert.Equal(t, "bool", flag.Value.Type())
}
func TestBackfillExistingArchivesOnlyInvokedWhenRequested(t *testing.T) {
root := t.TempDir()
require.NoError(t, os.WriteFile(filepath.Join(root, "chapter1.cbz"), []byte("data"), 0o644))
runBackfill := func(backfill bool) []string {
var found []string
var mu sync.Mutex
process := func(path string) {
mu.Lock()
defer mu.Unlock()
found = append(found, path)
}
// Mirrors the gating logic in WatchCommand: backfillExistingArchives
// must only run when the --backfill flag is set.
if backfill {
backfillExistingArchives(root, process)
}
return found
}
assert.Empty(t, runBackfill(false), "no pre-existing archive should be processed when backfill is disabled")
assert.Len(t, runBackfill(true), 1, "pre-existing archives should be processed when backfill is enabled")
}