Deployment and lifecycle management for Nix
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

cli: support multiple upload targets

+68 -6
cli

This is a binary file and will not be displayed.

+64 -2
cmd/cli/builder/main.go
··· 4 4 "bufio" 5 5 "context" 6 6 "encoding/json" 7 + "errors" 7 8 "fmt" 8 9 "io" 9 10 "log/slog" ··· 11 12 "os" 12 13 "os/exec" 13 14 "strings" 15 + "sync" 14 16 15 17 "codeberg.org/adamcstephens/sower/cmd/cli/commands" 16 18 "github.com/golang-queue/queue" ··· 106 108 return nil 107 109 } 108 110 111 + // MultiUploader pushes to multiple targets in parallel 112 + type MultiUploader struct { 113 + Uploaders []Uploader 114 + } 115 + 116 + // Push implements Uploader for MultiUploader 117 + func (m *MultiUploader) Push(outputs []string) error { 118 + var wg sync.WaitGroup 119 + errChan := make(chan error, len(m.Uploaders)) 120 + 121 + for _, u := range m.Uploaders { 122 + wg.Add(1) 123 + go func(uploader Uploader) { 124 + defer wg.Done() 125 + if err := uploader.Push(outputs); err != nil { 126 + slog.Error("Failed to push to target", "error", err) 127 + errChan <- err 128 + } 129 + }(u) 130 + } 131 + 132 + wg.Wait() 133 + close(errChan) 134 + 135 + // Collect all errors 136 + var errs []error 137 + for err := range errChan { 138 + errs = append(errs, err) 139 + } 140 + 141 + if len(errs) > 0 { 142 + return fmt.Errorf("failed to push to %d target(s): %w", len(errs), errors.Join(errs...)) 143 + } 144 + return nil 145 + } 146 + 109 147 // NewUploader creates an Uploader from a target string 110 148 func NewUploader(target string) (Uploader, error) { 111 149 parts := strings.SplitN(target, ":", 2) ··· 147 185 } 148 186 } 149 187 150 - func Push(workers int, system string, uploadTarget string) error { 151 - uploader, err := NewUploader(uploadTarget) 188 + // NewMultiUploader creates an Uploader from multiple target strings 189 + func NewMultiUploader(targets []string) (Uploader, error) { 190 + if len(targets) == 0 { 191 + return nil, fmt.Errorf("at least one upload target is required") 192 + } 193 + 194 + // Single target - return simple uploader (no wrapper overhead) 195 + if len(targets) == 1 { 196 + return NewUploader(targets[0]) 197 + } 198 + 199 + // Multiple targets - create MultiUploader 200 + uploaders := make([]Uploader, 0, len(targets)) 201 + for _, target := range targets { 202 + u, err := NewUploader(target) 203 + if err != nil { 204 + return nil, err 205 + } 206 + uploaders = append(uploaders, u) 207 + } 208 + 209 + return &MultiUploader{Uploaders: uploaders}, nil 210 + } 211 + 212 + func Push(workers int, system string, uploadTargets []string) error { 213 + uploader, err := NewMultiUploader(uploadTargets) 152 214 if err != nil { 153 215 return err 154 216 }
+4 -4
cmd/cli/main.go
··· 52 52 } 53 53 54 54 type builderPushCmd struct { 55 - Workers int `arg:"--workers,-w"` 56 - System string `arg:"--system"` 57 - Target string `arg:"--target,-t,required" help:"Upload target (attic:<cache>[?jobs=N] or nix-copy:<remote>)"` 55 + Workers int `arg:"--workers,-w"` 56 + System string `arg:"--system"` 57 + Targets []string `arg:"--target,-t,required,separate" help:"Upload target (attic:<cache>[?jobs=N] or nix-copy:<remote>). Can be repeated."` 58 58 } 59 59 60 60 type daemonCmd struct { ··· 245 245 os.Exit(1) 246 246 } 247 247 case cfg.Builder.Push != nil: 248 - err := builder.Push(cfg.Builder.Push.Workers, cfg.Builder.Push.System, cfg.Builder.Push.Target) 248 + err := builder.Push(cfg.Builder.Push.Workers, cfg.Builder.Push.System, cfg.Builder.Push.Targets) 249 249 if err != nil { 250 250 slog.Error("Failed to eval", "error", err) 251 251 os.Exit(1)