// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package lib import ( "context" "fmt" "net/url" "os" "os/exec" "path" "path/filepath" "sync" ) type repoSpec struct { user string name string } type GitRunner struct { ch <-chan repoSpec cfg *WebhookCFG } func (g *GitRunner) Run(ctx context.Context) error { // graceful: exit only if all tasks are done for { select { case repo := <-g.ch: err := g.cfg.download(repo.user, repo.name) if err != nil { fmt.Println("Failed to download repo: ", err) } case <-ctx.Done(): return ctx.Err() } } } func (c *WebhookCFG) pageDir(user, repo string) string { return filepath.Join(c.PageDir, user, repo) } func (c *WebhookCFG) gitDir(user, repo string) string { return filepath.Join(c.GitDir, user, repo) } func (c *WebhookCFG) checkout(user, repo string) (err error) { pageDir := c.pageDir(user, repo) gitDir := c.gitDir(user, repo) git := exec.Command( "git", "--git-dir", gitDir, "--work-tree", pageDir, "checkout", "origin/"+c.Branch, ) output, err := git.CombinedOutput() if err != nil { fmt.Println("git checkout failed: ", err) fmt.Println("=========== Dump output: ============") fmt.Println(string(output)) fmt.Println("=====================================") } return } func (c *WebhookCFG) fetch(user, repo string) (err error) { gitDir := c.gitDir(user, repo) git := exec.Command( "git", "--git-dir", gitDir, "fetch", "origin", c.Branch, ) output, err := git.CombinedOutput() if err != nil { fmt.Println("git fetch failed: ", err) fmt.Println("=========== Dump output: ============") fmt.Println(string(output)) fmt.Println("=====================================") } return } func (c *WebhookCFG) clone(user, repo string) (err error) { pageDir := c.pageDir(user, repo) err = os.MkdirAll(filepath.Dir(pageDir), 0755) if err != nil { return } gitDir := c.gitDir(user, repo) err = os.MkdirAll(filepath.Dir(gitDir), 0700) if err != nil { return } uri := c.Server uri.Path = "/" + path.Join(user, repo) + ".git" uri.User = url.UserPassword(c.GitUser, c.GitPass) git := exec.Command( "git", "clone", "-b", c.Branch, "--single-branch", "--no-tags", "--separate-git-dir", gitDir, uri.String(), pageDir, ) output, err := git.CombinedOutput() if err != nil { fmt.Println("git clone failed: ", err) fmt.Println("=========== Dump output: ============") fmt.Println(string(output)) fmt.Println("=====================================") } os.Remove(filepath.Join(pageDir, ".git")) return } var repoLock = &sync.Map{} func (c *WebhookCFG) lock(user, repo string) func() { key := user + "/" + repo lock, _ := repoLock.LoadOrStore(key, &sync.Mutex{}) m := lock.(*sync.Mutex) m.Lock() return m.Unlock } func (c *WebhookCFG) download(user, repo string) (err error) { unlock := c.lock(user, repo) defer unlock() fmt.Println("Pulling ", user, repo) gitDir := c.gitDir(user, repo) if _, err = os.Stat(gitDir); os.IsNotExist(err) { err = c.clone(user, repo) } else { err = c.fetch(user, repo) } if err != nil { return } err = c.checkout(user, repo) return }