diff --git a/CHANGELOG.md b/CHANGELOG.md index f040749..26e7baa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ Exceptions are acceptable depending on the circumstances (critical bug fixes tha - changed `DefaultCommandRunner.RunInteractive` to use `sh -c` for proper shell operator support (redirection, pipes) - changed the Go module dependencies to their latest versions - changed the Go module dependencies to their latest versions +- changed per-repo logging in parallel operations to run inside goroutines for progressive feedback ## [0.4.0] - 2026-03-31 diff --git a/internal/repo/failover.go b/internal/repo/failover.go index 96fb58b..f77e4c8 100644 --- a/internal/repo/failover.go +++ b/internal/repo/failover.go @@ -45,7 +45,12 @@ func RunFailover(cfg FailoverConfig) error { go func(idx int, path string) { defer wg.Done() defer func() { <-sem }() - results[idx] = failoverSingleRepo(path, cfg.RootDir, cfg.Runner) + result := failoverSingleRepo(path, cfg.RootDir, cfg.Runner) + log.WithFields(logger.Fields{ + "repo": result.Name, + "status": result.Status, + }).Info(result.Status) + results[idx] = result }(i, repoPath) } @@ -59,10 +64,6 @@ func RunFailover(cfg FailoverConfig) error { counts := map[string]int{"switched": 0, "skipped": 0, "failed": 0} for _, r := range results { - log.WithFields(logger.Fields{ - "repo": r.Name, - "status": r.Status, - }).Info(r.Status) category, known := failoverStatusCategory[r.Status] if !known { category = "failed" diff --git a/internal/repo/fork_sync.go b/internal/repo/fork_sync.go index 5f63002..9497fcd 100644 --- a/internal/repo/fork_sync.go +++ b/internal/repo/fork_sync.go @@ -114,7 +114,12 @@ func parallelForkSync(forks []globalEntities.Repository, cfg ForkSyncConfig) []F defer wg.Done() defer func() { <-sem }() repoPath := filepath.Join(cfg.RootDir, Key(fork)) - results[idx] = ForkSyncSingleRepo(repoPath, fork, cfg) + result := ForkSyncSingleRepo(repoPath, fork, cfg) + cfg.Output.WithFields(logger.Fields{ + "repo": result.Name, + "status": result.Status, + }).Info("fork-sync result") + results[idx] = result }(i, f) } @@ -307,11 +312,6 @@ func restoreAfterForkSync( func logForkSyncSummary(results []ForkSyncResult, log logger.FieldLogger) { synced, conflicts, failed := 0, 0, 0 for _, r := range results { - log.WithFields(logger.Fields{ - "repo": r.Name, - "status": r.Status, - }).Info("fork-sync result") - switch { case strings.HasPrefix(r.Status, "synced"): synced++ diff --git a/internal/repo/mirror.go b/internal/repo/mirror.go index 30bfa09..7006285 100644 --- a/internal/repo/mirror.go +++ b/internal/repo/mirror.go @@ -86,7 +86,12 @@ func RunMirror(cfg MirrorConfig) error { defer wg.Done() defer func() { <-sem }() time.Sleep(time.Duration(idx) * mirrorAPIDelay / time.Duration(workers)) - results[idx] = mirrorSingleRepo(path, cfg, providerName, owner, mirrorProvider) + result := mirrorSingleRepo(path, cfg, providerName, owner, mirrorProvider) + log.WithFields(logger.Fields{ + "repo": result.Name, + "status": result.Status, + }).Info(result.Status) + results[idx] = result }(i, repoPath) } @@ -100,10 +105,6 @@ func RunMirror(cfg MirrorConfig) error { counts := map[string]int{"mirrored": 0, "skipped": 0, mirrorStatusFailed: 0} for _, r := range results { - log.WithFields(logger.Fields{ - "repo": r.Name, - "status": r.Status, - }).Info(r.Status) category, known := mirrorStatusCategory[r.Status] if !known { category = mirrorStatusFailed diff --git a/internal/repo/prune.go b/internal/repo/prune.go index 0ac9c91..85f3ecb 100644 --- a/internal/repo/prune.go +++ b/internal/repo/prune.go @@ -18,6 +18,11 @@ type PruneResult struct { Status string } +// isPruneFailure returns true if the given status string indicates a failure. +func isPruneFailure(status string) bool { + return strings.HasPrefix(status, "FAIL") || strings.Contains(strings.ToLower(status), "failed") +} + // RunPrune deletes merged branches in all repositories under rootDir in parallel. func RunPrune(rootDir string, runner GitRunner, dryRun bool, output io.Writer) error { log := NewLogger(output) @@ -46,7 +51,14 @@ func RunPrune(rootDir string, runner GitRunner, dryRun bool, output io.Writer) e defer wg.Done() defer func() { <-sem }() repoPath := filepath.Join(rootDir, name) - results[idx] = PruneSingleRepo(repoPath, rootDir, runner, dryRun) + result := PruneSingleRepo(repoPath, rootDir, runner, dryRun) + if len(result.Deleted) > 0 || isPruneFailure(result.Status) { + log.WithFields(logger.Fields{ + "repo": result.Name, + "status": result.Status, + }).Info("prune result") + } + results[idx] = result }(i, repoName) } @@ -54,18 +66,8 @@ func RunPrune(rootDir string, runner GitRunner, dryRun bool, output io.Writer) e pruned, skipped, failed := 0, 0, 0 for _, r := range results { - statusLower := strings.ToLower(r.Status) - hasFailure := strings.HasPrefix(r.Status, "FAIL") || strings.Contains(statusLower, "failed") - - if len(r.Deleted) > 0 || hasFailure { - log.WithFields(logger.Fields{ - "repo": r.Name, - "status": r.Status, - }).Info("prune result") - } - switch { - case hasFailure: + case isPruneFailure(r.Status): failed++ case len(r.Deleted) > 0: pruned += len(r.Deleted) diff --git a/internal/repo/restore.go b/internal/repo/restore.go index b36750f..cb25de0 100644 --- a/internal/repo/restore.go +++ b/internal/repo/restore.go @@ -45,7 +45,12 @@ func RunRestore(cfg RestoreConfig) error { go func(idx int, path string) { defer wg.Done() defer func() { <-sem }() - results[idx] = restoreSingleRepo(path, cfg.RootDir, cfg.Runner) + result := restoreSingleRepo(path, cfg.RootDir, cfg.Runner) + log.WithFields(logger.Fields{ + "repo": result.Name, + "status": result.Status, + }).Info(result.Status) + results[idx] = result }(i, repoPath) } @@ -58,10 +63,6 @@ func RunRestore(cfg RestoreConfig) error { counts := map[string]int{"restored": 0, "skipped": 0, "failed": 0} for _, r := range results { - log.WithFields(logger.Fields{ - "repo": r.Name, - "status": r.Status, - }).Info(r.Status) category, known := restoreStatusCategory[r.Status] if !known { category = "failed" diff --git a/internal/repo/sync.go b/internal/repo/sync.go index 3660bf2..19351bf 100644 --- a/internal/repo/sync.go +++ b/internal/repo/sync.go @@ -41,7 +41,12 @@ func RunSync(rootDir string, runner GitRunner, output io.Writer) error { go func(idx int, path string) { defer wg.Done() defer func() { <-sem }() - results[idx] = SyncSingleRepo(path, rootDir, runner) + result := SyncSingleRepo(path, rootDir, runner) + log.WithFields(logger.Fields{ + "repo": result.Name, + "status": result.Status, + }).Info("sync result") + results[idx] = result }(i, repoPath) } @@ -49,10 +54,6 @@ func RunSync(rootDir string, runner GitRunner, output io.Writer) error { synced, wip, failed := 0, 0, 0 for _, r := range results { - log.WithFields(logger.Fields{ - "repo": r.Name, - "status": r.Status, - }).Info("sync result") switch { case strings.HasPrefix(r.Status, "synced"): synced++