11package jobs
22
33import (
4+ "bytes"
45 "context"
56 "fmt"
67 "path/filepath"
@@ -15,30 +16,47 @@ import (
1516 "github.com/charmbracelet/soft-serve/pkg/lfs"
1617 "github.com/charmbracelet/soft-serve/pkg/store"
1718 "github.com/charmbracelet/soft-serve/pkg/sync"
19+ "github.com/spf13/pflag"
1820)
1921
2022func init () {
2123 Register ("mirror-pull" , mirrorPull {})
2224}
2325
24- type mirrorPull struct {}
26+ type (
27+ mirrorPull struct {}
28+ mirrorPullConfig struct {
29+ baseRunnerConfig
30+ RepoConfig map [string ]string
31+ }
32+ )
2533
26- // Spec derives the spec used for pull mirrors and implements Runner.
27- func (m mirrorPull ) Spec (ctx context.Context ) string {
28- cfg := config .FromContext (ctx )
29- if cfg .Jobs .MirrorPull != "" {
30- return cfg .Jobs .MirrorPull
34+ // Description return the description of (pull) mirror job task and implements Runner.
35+ func (m mirrorPull ) Description () string {
36+ return "fetch upstream for mirror repositories"
37+ }
38+
39+ // Config returns the (pull) mirror cronjob configuration and implements Runner.
40+ func (m mirrorPull ) Config (ctx context.Context ) (RunnerConfig , error ) {
41+ cfg := mirrorPullConfig {
42+ baseRunnerConfig : baseRunnerConfig {CronSpec : "@every 10m" },
43+ RepoConfig : make (map [string ]string ),
44+ }
45+ if spec := config .FromContext (ctx ).Jobs .MirrorPull ; spec != "" {
46+ cfg .CronSpec = spec
3147 }
32- return "@every 10m"
48+
49+ return & cfg , nil
3350}
3451
3552// Func runs the (pull) mirror job task and implements Runner.
36- func (m mirrorPull ) Func (ctx context.Context ) func () {
53+ func (m mirrorPull ) Func (ctx context.Context , cronCfg RunnerConfig ) func () {
3754 cfg := config .FromContext (ctx )
3855 logger := log .FromContext (ctx ).WithPrefix ("jobs.mirror" )
3956 b := backend .FromContext (ctx )
4057 dbx := db .FromContext (ctx )
4158 datastore := store .FromContext (ctx )
59+ jobcfg := cronCfg .(* mirrorPullConfig )
4260 return func () {
4361 repos , err := b .Repositories (ctx )
4462 if err != nil {
@@ -64,13 +82,31 @@ func (m mirrorPull) Func(ctx context.Context) func() {
6482 wq .Add (name , func () {
6583 repo := repo
6684
85+ // buffer and write to stdout/stderr in one go,
86+ // avoiding output confusion through parallel writing.
87+ var (
88+ stdout = bytes .NewBuffer (nil )
89+ stderr = bytes .NewBuffer (nil )
90+ )
91+ defer func () {
92+ jobcfg .Output ().Write (stdout .Bytes ())
93+ jobcfg .Error ().Write (stderr .Bytes ())
94+ }()
95+
6796 cmds := []string {
6897 "fetch --prune" , // fetch prune before updating remote
6998 "remote update --prune" , // update remote and prune remote refs
7099 }
71100
101+ gitFlags := []string {}
102+ for key , val := range jobcfg .RepoConfig {
103+ gitFlags = append (gitFlags , "-c" , key + "=" + val )
104+ }
105+
72106 for _ , c := range cmds {
73107 args := strings .Split (c , " " )
108+ args = append (gitFlags , args ... )
109+
74110 cmd := git .NewCommand (args ... ).WithContext (ctx )
75111 cmd .AddEnvs (
76112 fmt .Sprintf (`GIT_SSH_COMMAND=ssh -o UserKnownHostsFile="%s" -o StrictHostKeyChecking=no -i "%s"` ,
@@ -80,6 +116,7 @@ func (m mirrorPull) Func(ctx context.Context) func() {
80116 )
81117
82118 if _ , err := cmd .RunInDir (r .Path ); err != nil {
119+ fmt .Fprintf (stderr , "[%s]: error running git remote update: %v\n " , name , err )
83120 logger .Error ("error running git remote update" , "repo" , name , "err" , err )
84121 }
85122 }
@@ -88,6 +125,7 @@ func (m mirrorPull) Func(ctx context.Context) func() {
88125 rcfg , err := r .Config ()
89126 if err != nil {
90127 logger .Error ("error getting git config" , "repo" , name , "err" , err )
128+ fmt .Fprintf (stderr , "[%s]: lfs pull: error getting git config: %v" , name , err )
91129 return
92130 }
93131
@@ -101,24 +139,38 @@ func (m mirrorPull) Func(ctx context.Context) func() {
101139 ep , err := lfs .NewEndpoint (lfsEndpoint )
102140 if err != nil {
103141 logger .Error ("error creating LFS endpoint" , "repo" , name , "err" , err )
142+ fmt .Fprintf (stderr , "[%s]: lfs pull: creating LFS endpoint: %v" , name , err )
104143 return
105144 }
106145
107146 client := lfs .NewClient (ep )
108147 if client == nil {
148+ fmt .Fprintf (stderr ,
149+ "[%s]: lfs pull: failed to create lfs client: unsupported endpoint %s" ,
150+ name , lfsEndpoint )
109151 logger .Errorf ("failed to create lfs client: unsupported endpoint %s" , lfsEndpoint )
110152 return
111153 }
112154
113155 if err := backend .StoreRepoMissingLFSObjects (ctx , repo , dbx , datastore , client ); err != nil {
156+ fmt .Fprintf (stderr , "[%s]: lfs pull: failed to store missing lfs objects: %v" , name , err )
114157 logger .Error ("failed to store missing lfs objects" , "err" , err , "path" , r .Path )
115158 return
116159 }
117160 }
161+ fmt .Fprintf (stdout , "[%s]: mirror pull succeed\n " , name )
118162 })
119163 }
120164 }
121165
122166 wq .Run ()
123167 }
124168}
169+
170+ // FlagSet returns the flag set that can modify configuration values and implements RunnerConfig
171+ func (cfg * mirrorPullConfig ) FlagSet () * pflag.FlagSet {
172+ flags := pflag .NewFlagSet ("mirror-pull" , pflag .ContinueOnError )
173+ flags .StringToStringVarP (& cfg .RepoConfig , "config" , "c" , cfg .RepoConfig , "Override values from git repository configuration files" )
174+
175+ return flags
176+ }
0 commit comments