summaryrefslogtreecommitdiff
path: root/internal/cli
diff options
context:
space:
mode:
Diffstat (limited to 'internal/cli')
-rw-r--r--internal/cli/description_sync.go71
-rw-r--r--internal/cli/description_sync_test.go57
-rw-r--r--internal/cli/sync_handlers.go14
-rw-r--r--internal/cli/sync_handlers_test.go19
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")
+ }
+}