diff options
Diffstat (limited to 'internal/cli')
| -rw-r--r-- | internal/cli/description_sync.go | 71 | ||||
| -rw-r--r-- | internal/cli/description_sync_test.go | 57 | ||||
| -rw-r--r-- | internal/cli/sync_handlers.go | 14 | ||||
| -rw-r--r-- | internal/cli/sync_handlers_test.go | 19 |
4 files changed, 158 insertions, 3 deletions
diff --git a/internal/cli/description_sync.go b/internal/cli/description_sync.go index 4904b56..317bcdf 100644 --- a/internal/cli/description_sync.go +++ b/internal/cli/description_sync.go @@ -2,6 +2,10 @@ package cli import ( "fmt" + "os" + "os/exec" + "path" + "path/filepath" "strings" "codeberg.org/snonux/gitsyncer/internal/codeberg" @@ -105,8 +109,75 @@ func syncRepoDescriptions(cfg *config.Config, dryRun bool, repoName, knownCBDesc } } + syncBackupDescriptions(cfg, dryRun, repoName, canonical) + // Update cache if cache != nil { cache[repoName] = canonical } } + +func syncBackupDescriptions(cfg *config.Config, dryRun bool, repoName, canonical string) { + if cfg == nil || canonical == "" { + return + } + + for i := range cfg.Organizations { + org := &cfg.Organizations[i] + if !org.BackupLocation { + continue + } + + supported, err := syncBackupDescription(org, repoName, canonical, dryRun) + if err != nil { + fmt.Printf(" Warning: Failed to update backup description on %s: %v\n", org.Host, err) + continue + } + if supported && !dryRun { + fmt.Printf(" Updated backup description for %s on %s\n", repoName, org.Host) + } + } +} + +func syncBackupDescription(org *config.Organization, repoName, description string, dryRun bool) (bool, error) { + if org == nil || !org.BackupLocation { + return false, nil + } + + description = strings.TrimSpace(description) + if description == "" { + return false, nil + } + + if strings.HasPrefix(org.Host, "file://") { + descriptionPath := filepath.Join(strings.TrimPrefix(org.Host, "file://"), repoName+".git", "description") + if dryRun { + fmt.Printf(" [DRY RUN] Would update backup description for %s on %s -> %q\n", repoName, org.Host, description) + return true, nil + } + return true, os.WriteFile(descriptionPath, []byte(description+"\n"), 0644) + } + + if strings.TrimSpace(org.DescriptionSyncHost) == "" || strings.TrimSpace(org.DescriptionSyncRoot) == "" { + return false, nil + } + + descriptionPath := path.Join(org.DescriptionSyncRoot, repoName+".git", "description") + if dryRun { + fmt.Printf(" [DRY RUN] Would update backup description for %s on %s -> %q\n", repoName, org.Host, description) + return true, nil + } + + cmd := exec.Command("ssh", org.DescriptionSyncHost, fmt.Sprintf("cat > %s", shellSingleQuote(descriptionPath))) + cmd.Stdin = strings.NewReader(description + "\n") + output, err := cmd.CombinedOutput() + if err != nil { + return true, fmt.Errorf("ssh write failed: %w\n%s", err, strings.TrimSpace(string(output))) + } + + return true, nil +} + +func shellSingleQuote(value string) string { + return "'" + strings.ReplaceAll(value, "'", `'\''`) + "'" +} diff --git a/internal/cli/description_sync_test.go b/internal/cli/description_sync_test.go new file mode 100644 index 0000000..8f92349 --- /dev/null +++ b/internal/cli/description_sync_test.go @@ -0,0 +1,57 @@ +package cli + +import ( + "os" + "path/filepath" + "testing" + + "codeberg.org/snonux/gitsyncer/internal/config" +) + +func TestSyncBackupDescription_FileURLWritesDescription(t *testing.T) { + t.Parallel() + + rootDir := t.TempDir() + repoDir := filepath.Join(rootDir, "sample.git") + if err := os.MkdirAll(repoDir, 0755); err != nil { + t.Fatalf("mkdir repo dir: %v", err) + } + + org := &config.Organization{ + Host: "file://" + rootDir, + BackupLocation: true, + } + + supported, err := syncBackupDescription(org, "sample", "Sample description", false) + if err != nil { + t.Fatalf("syncBackupDescription() error = %v", err) + } + if !supported { + t.Fatal("expected file backup description sync to be supported") + } + + content, err := os.ReadFile(filepath.Join(repoDir, "description")) + if err != nil { + t.Fatalf("read description: %v", err) + } + if string(content) != "Sample description\n" { + t.Fatalf("description = %q, want %q", string(content), "Sample description\n") + } +} + +func TestSyncBackupDescription_SSHWithoutDescriptionSyncConfigIsUnsupported(t *testing.T) { + t.Parallel() + + org := &config.Organization{ + Host: "ssh://git@example.com/repos", + BackupLocation: true, + } + + supported, err := syncBackupDescription(org, "sample", "Sample description", false) + if err != nil { + t.Fatalf("syncBackupDescription() error = %v", err) + } + if supported { + t.Fatal("expected SSH backup description sync without config to be unsupported") + } +} diff --git a/internal/cli/sync_handlers.go b/internal/cli/sync_handlers.go index 6a96b92..e791b45 100644 --- a/internal/cli/sync_handlers.go +++ b/internal/cli/sync_handlers.go @@ -12,6 +12,14 @@ import ( "codeberg.org/snonux/gitsyncer/internal/sync" ) +func shouldEnableBackupSync(flags *Flags) bool { + if flags == nil { + return false + } + + return flags.Backup || flags.FullSync +} + // HandleSync handles syncing a single repository func HandleSync(cfg *config.Config, flags *Flags) int { stateManager, syncState, err := loadSyncState(flags.WorkDir) @@ -50,7 +58,7 @@ func HandleSync(cfg *config.Config, flags *Flags) int { } syncer := sync.New(cfg, flags.WorkDir) - syncer.SetBackupEnabled(flags.Backup) + syncer.SetBackupEnabled(shouldEnableBackupSync(flags)) if err := syncer.SyncRepository(flags.SyncRepo); err != nil { fmt.Printf("ERROR: Sync failed: %v\n", err) return 1 @@ -107,7 +115,7 @@ func HandleSyncAll(cfg *config.Config, flags *Flags) int { } syncer := sync.New(cfg, flags.WorkDir) - syncer.SetBackupEnabled(flags.Backup) + syncer.SetBackupEnabled(shouldEnableBackupSync(flags)) successCount := 0 // Load descriptions cache descCache := loadDescriptionCache(flags.WorkDir) @@ -421,7 +429,7 @@ func newSyncExecution(cfg *config.Config, flags *Flags) *syncExecution { descCache: loadDescriptionCache(flags.WorkDir), syncer: sync.New(cfg, flags.WorkDir), } - execution.syncer.SetBackupEnabled(flags.Backup) + execution.syncer.SetBackupEnabled(shouldEnableBackupSync(flags)) manager, st, err := loadSyncState(flags.WorkDir) if err != nil { diff --git a/internal/cli/sync_handlers_test.go b/internal/cli/sync_handlers_test.go new file mode 100644 index 0000000..2eaad67 --- /dev/null +++ b/internal/cli/sync_handlers_test.go @@ -0,0 +1,19 @@ +package cli + +import "testing" + +func TestShouldEnableBackupSync_FullSyncImplicitlyEnablesBackup(t *testing.T) { + t.Parallel() + + if !shouldEnableBackupSync(&Flags{FullSync: true}) { + t.Fatal("expected full sync to enable backup sync implicitly") + } + + if !shouldEnableBackupSync(&Flags{Backup: true}) { + t.Fatal("expected explicit --backup to enable backup sync") + } + + if shouldEnableBackupSync(&Flags{}) { + t.Fatal("did not expect backup sync to be enabled by default") + } +} |
