mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-04 03:27:23 -05:00 
			
		
		
		
	Merge pull request #37 from abiosoft/master
git: post pull command. retries after pull failure.
This commit is contained in:
		
						commit
						9df9ad975d
					
				@ -7,6 +7,7 @@
 | 
				
			|||||||
//		branch
 | 
					//		branch
 | 
				
			||||||
//		key
 | 
					//		key
 | 
				
			||||||
//		interval
 | 
					//		interval
 | 
				
			||||||
 | 
					//		then command args
 | 
				
			||||||
//	}
 | 
					//	}
 | 
				
			||||||
//	repo 	- git repository
 | 
					//	repo 	- git repository
 | 
				
			||||||
// 		compulsory. Both ssh (e.g. git@github.com:user/project.git)
 | 
					// 		compulsory. Both ssh (e.g. git@github.com:user/project.git)
 | 
				
			||||||
@ -15,7 +16,6 @@
 | 
				
			|||||||
//
 | 
					//
 | 
				
			||||||
// 	path 	- directory to pull into, relative to site root
 | 
					// 	path 	- directory to pull into, relative to site root
 | 
				
			||||||
//		optional. Defaults to site root.
 | 
					//		optional. Defaults to site root.
 | 
				
			||||||
//		If set, must be a subdirectory to site root to be valid.
 | 
					 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// 	branch 	- git branch or tag
 | 
					// 	branch 	- git branch or tag
 | 
				
			||||||
//		optional. Defaults to master
 | 
					//		optional. Defaults to master
 | 
				
			||||||
@ -26,6 +26,9 @@
 | 
				
			|||||||
// 	interval- interval between git pulls in seconds
 | 
					// 	interval- interval between git pulls in seconds
 | 
				
			||||||
//		optional. Defaults to 3600 (1 Hour).
 | 
					//		optional. Defaults to 3600 (1 Hour).
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
 | 
					//	then	- command to execute after successful pull
 | 
				
			||||||
 | 
					//		optional. If set, will execute only when there are new changes.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
// Examples :
 | 
					// Examples :
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// public repo pulled into site root
 | 
					// public repo pulled into site root
 | 
				
			||||||
@ -34,7 +37,7 @@
 | 
				
			|||||||
// public repo pulled into <root>/mysite
 | 
					// public repo pulled into <root>/mysite
 | 
				
			||||||
//	git https://github.com/user/myproject mysite
 | 
					//	git https://github.com/user/myproject mysite
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// private repo pulled into <root>/mysite with tag v1.0 and interval of 1 day
 | 
					// private repo pulled into <root>/mysite with tag v1.0 and interval of 1 day.
 | 
				
			||||||
//	git {
 | 
					//	git {
 | 
				
			||||||
//		repo 	git@github.com:user/myproject
 | 
					//		repo 	git@github.com:user/myproject
 | 
				
			||||||
//		branch 	v1.0
 | 
					//		branch 	v1.0
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,7 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"runtime"
 | 
						"runtime"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
@ -56,7 +57,7 @@ func parse(c middleware.Controller) (*Repo, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		switch len(args) {
 | 
							switch len(args) {
 | 
				
			||||||
		case 2:
 | 
							case 2:
 | 
				
			||||||
			repo.Path = filepath.Join(c.Root(), args[1])
 | 
								repo.Path = filepath.Clean(c.Root() + string(filepath.Separator) + args[1])
 | 
				
			||||||
			fallthrough
 | 
								fallthrough
 | 
				
			||||||
		case 1:
 | 
							case 1:
 | 
				
			||||||
			repo.Url = args[0]
 | 
								repo.Url = args[0]
 | 
				
			||||||
@ -73,7 +74,7 @@ func parse(c middleware.Controller) (*Repo, error) {
 | 
				
			|||||||
				if !c.NextArg() {
 | 
									if !c.NextArg() {
 | 
				
			||||||
					return nil, c.ArgErr()
 | 
										return nil, c.ArgErr()
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				repo.Path = filepath.Join(c.Root(), c.Val())
 | 
									repo.Path = filepath.Clean(c.Root() + string(filepath.Separator) + c.Val())
 | 
				
			||||||
			case "branch":
 | 
								case "branch":
 | 
				
			||||||
				if !c.NextArg() {
 | 
									if !c.NextArg() {
 | 
				
			||||||
					return nil, c.ArgErr()
 | 
										return nil, c.ArgErr()
 | 
				
			||||||
@ -92,6 +93,12 @@ func parse(c middleware.Controller) (*Repo, error) {
 | 
				
			|||||||
				if t > 0 {
 | 
									if t > 0 {
 | 
				
			||||||
					repo.Interval = time.Duration(t) * time.Second
 | 
										repo.Interval = time.Duration(t) * time.Second
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
								case "then":
 | 
				
			||||||
 | 
									thenArgs := c.RemainingArgs()
 | 
				
			||||||
 | 
									if len(thenArgs) == 0 {
 | 
				
			||||||
 | 
										return nil, c.ArgErr()
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									repo.Then = strings.Join(thenArgs, " ")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -125,7 +132,7 @@ func parse(c middleware.Controller) (*Repo, error) {
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return repo, prepare(repo)
 | 
						return repo, repo.prepare()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// sanitizeHttp cleans up repository url and converts to https format
 | 
					// sanitizeHttp cleans up repository url and converts to https format
 | 
				
			||||||
@ -165,3 +172,11 @@ func sanitizeGit(repoUrl string) (string, string, error) {
 | 
				
			|||||||
	host := hostUrl[:i]
 | 
						host := hostUrl[:i]
 | 
				
			||||||
	return repoUrl, host, nil
 | 
						return repoUrl, host, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// logger is an helper function to retrieve the available logger
 | 
				
			||||||
 | 
					func logger() *log.Logger {
 | 
				
			||||||
 | 
						if Logger == nil {
 | 
				
			||||||
 | 
							Logger = log.New(os.Stderr, "", log.LstdFlags)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return Logger
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,20 +1,25 @@
 | 
				
			|||||||
package git
 | 
					package git
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"log"
 | 
					 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"os/exec"
 | 
						"os/exec"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/mholt/caddy/middleware"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DefaultInterval is the minimum interval to delay before
 | 
					// DefaultInterval is the minimum interval to delay before
 | 
				
			||||||
// requesting another git pull
 | 
					// requesting another git pull
 | 
				
			||||||
const DefaultInterval time.Duration = time.Hour * 1
 | 
					const DefaultInterval time.Duration = time.Hour * 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Number of retries if git pull fails
 | 
				
			||||||
 | 
					const numRetries = 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// gitBinary holds the absolute path to git executable
 | 
					// gitBinary holds the absolute path to git executable
 | 
				
			||||||
var gitBinary string
 | 
					var gitBinary string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -31,12 +36,15 @@ type Repo struct {
 | 
				
			|||||||
	Branch     string        // Git branch
 | 
						Branch     string        // Git branch
 | 
				
			||||||
	KeyPath    string        // Path to private ssh key
 | 
						KeyPath    string        // Path to private ssh key
 | 
				
			||||||
	Interval   time.Duration // Interval between pulls
 | 
						Interval   time.Duration // Interval between pulls
 | 
				
			||||||
 | 
						Then       string        // Command to execute after successful git pull
 | 
				
			||||||
	pulled     bool          // true if there was a successful pull
 | 
						pulled     bool          // true if there was a successful pull
 | 
				
			||||||
	lastPull   time.Time     // time of the last successful pull
 | 
						lastPull   time.Time     // time of the last successful pull
 | 
				
			||||||
 | 
						lastCommit string        // hash for the most recent commit
 | 
				
			||||||
	sync.Mutex
 | 
						sync.Mutex
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Pull performs git clone, or git pull if repository exists
 | 
					// Pull attempts a git clone.
 | 
				
			||||||
 | 
					// It retries at most numRetries times if error occurs
 | 
				
			||||||
func (r *Repo) Pull() error {
 | 
					func (r *Repo) Pull() error {
 | 
				
			||||||
	r.Lock()
 | 
						r.Lock()
 | 
				
			||||||
	defer r.Unlock()
 | 
						defer r.Unlock()
 | 
				
			||||||
@ -45,6 +53,33 @@ func (r *Repo) Pull() error {
 | 
				
			|||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// keep last commit hash for comparison later
 | 
				
			||||||
 | 
						lastCommit := r.lastCommit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						// Attempt to pull at most numRetries times
 | 
				
			||||||
 | 
						for i := 0; i < numRetries; i++ {
 | 
				
			||||||
 | 
							if err = r.pull(); err == nil {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							logger().Println(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// check if there are new changes,
 | 
				
			||||||
 | 
						// then execute post pull command
 | 
				
			||||||
 | 
						if r.lastCommit == lastCommit {
 | 
				
			||||||
 | 
							logger().Println("No new changes.")
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return r.postPullCommand()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Pull performs git clone, or git pull if repository exists
 | 
				
			||||||
 | 
					func (r *Repo) pull() error {
 | 
				
			||||||
	params := []string{"clone", "-b", r.Branch, r.Url, r.Path}
 | 
						params := []string{"clone", "-b", r.Branch, r.Url, r.Path}
 | 
				
			||||||
	if r.pulled {
 | 
						if r.pulled {
 | 
				
			||||||
		params = []string{"pull", "origin", r.Branch}
 | 
							params = []string{"pull", "origin", r.Branch}
 | 
				
			||||||
@ -52,35 +87,27 @@ func (r *Repo) Pull() error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// if key is specified, pull using ssh key
 | 
						// if key is specified, pull using ssh key
 | 
				
			||||||
	if r.KeyPath != "" {
 | 
						if r.KeyPath != "" {
 | 
				
			||||||
		return pullWithKey(r, params)
 | 
							return r.pullWithKey(params)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cmd := exec.Command(gitBinary, params...)
 | 
						dir := ""
 | 
				
			||||||
	cmd.Env = os.Environ()
 | 
					 | 
				
			||||||
	cmd.Stdout = os.Stderr
 | 
					 | 
				
			||||||
	cmd.Stderr = os.Stderr
 | 
					 | 
				
			||||||
	if r.pulled {
 | 
						if r.pulled {
 | 
				
			||||||
		cmd.Dir = r.Path
 | 
							dir = r.Path
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	if err = cmd.Start(); err != nil {
 | 
						if err = runCmd(gitBinary, params, dir); err == nil {
 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err = cmd.Wait(); err == nil {
 | 
					 | 
				
			||||||
		r.pulled = true
 | 
							r.pulled = true
 | 
				
			||||||
		r.lastPull = time.Now()
 | 
							r.lastPull = time.Now()
 | 
				
			||||||
		log.Printf("%v pulled.\n", r.Url)
 | 
							logger().Printf("%v pulled.\n", r.Url)
 | 
				
			||||||
 | 
							r.lastCommit, err = r.getMostRecentCommit()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	return err
 | 
						return err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// pullWithKey performs git clone or git pull if repository exists.
 | 
					// pullWithKey is used for private repositories and requires an ssh key.
 | 
				
			||||||
// It is used for private repositories and requires an ssh key.
 | 
					 | 
				
			||||||
// Note: currently only limited to Linux and OSX.
 | 
					// Note: currently only limited to Linux and OSX.
 | 
				
			||||||
func pullWithKey(r *Repo, params []string) error {
 | 
					func (r *Repo) pullWithKey(params []string) error {
 | 
				
			||||||
	var gitSsh, script *os.File
 | 
						var gitSsh, script *os.File
 | 
				
			||||||
	// ensure temporary files deleted after usage
 | 
						// ensure temporary files deleted after usage
 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
@ -105,30 +132,23 @@ func pullWithKey(r *Repo, params []string) error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// execute the git clone bash script
 | 
						dir := ""
 | 
				
			||||||
	cmd := exec.Command(script.Name())
 | 
					 | 
				
			||||||
	cmd.Env = os.Environ()
 | 
					 | 
				
			||||||
	cmd.Stdout = os.Stderr
 | 
					 | 
				
			||||||
	cmd.Stderr = os.Stderr
 | 
					 | 
				
			||||||
	if r.pulled {
 | 
						if r.pulled {
 | 
				
			||||||
		cmd.Dir = r.Path
 | 
							dir = r.Path
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = cmd.Start(); err != nil {
 | 
						if err = runCmd(script.Name(), nil, dir); err == nil {
 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err = cmd.Wait(); err == nil {
 | 
					 | 
				
			||||||
		r.pulled = true
 | 
							r.pulled = true
 | 
				
			||||||
		r.lastPull = time.Now()
 | 
							r.lastPull = time.Now()
 | 
				
			||||||
		log.Printf("%v pulled.\n", r.Url)
 | 
							logger().Printf("%v pulled.\n", r.Url)
 | 
				
			||||||
 | 
							r.lastCommit, err = r.getMostRecentCommit()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return err
 | 
						return err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// prepare prepares for a git pull
 | 
					// prepare prepares for a git pull
 | 
				
			||||||
// and validates the configured directory
 | 
					// and validates the configured directory
 | 
				
			||||||
func prepare(r *Repo) error {
 | 
					func (r *Repo) prepare() error {
 | 
				
			||||||
	// check if directory exists or is empty
 | 
						// check if directory exists or is empty
 | 
				
			||||||
	// if not, create directory
 | 
						// if not, create directory
 | 
				
			||||||
	fs, err := ioutil.ReadDir(r.Path)
 | 
						fs, err := ioutil.ReadDir(r.Path)
 | 
				
			||||||
@ -148,7 +168,7 @@ func prepare(r *Repo) error {
 | 
				
			|||||||
	if isGit {
 | 
						if isGit {
 | 
				
			||||||
		// check if same repository
 | 
							// check if same repository
 | 
				
			||||||
		var repoUrl string
 | 
							var repoUrl string
 | 
				
			||||||
		if repoUrl, err = getRepoUrl(r.Path); err == nil && repoUrl == r.Url {
 | 
							if repoUrl, err = r.getRepoUrl(); err == nil && repoUrl == r.Url {
 | 
				
			||||||
			r.pulled = true
 | 
								r.pulled = true
 | 
				
			||||||
			return nil
 | 
								return nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -160,23 +180,42 @@ func prepare(r *Repo) error {
 | 
				
			|||||||
	return fmt.Errorf("Cannot git clone into %v, directory not empty.", r.Path)
 | 
						return fmt.Errorf("Cannot git clone into %v, directory not empty.", r.Path)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// getMostRecentCommit gets the hash of the most recent commit to the
 | 
				
			||||||
 | 
					// repository. Useful for checking if changes occur.
 | 
				
			||||||
 | 
					func (r *Repo) getMostRecentCommit() (string, error) {
 | 
				
			||||||
 | 
						command := gitBinary + ` --no-pager log -n 1 --pretty=format:"%H"`
 | 
				
			||||||
 | 
						c, args, err := middleware.SplitCommandAndArgs(command)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return runCmdOutput(c, args, r.Path)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getRepoUrl retrieves remote origin url for the git repository at path
 | 
					// getRepoUrl retrieves remote origin url for the git repository at path
 | 
				
			||||||
func getRepoUrl(path string) (string, error) {
 | 
					func (r *Repo) getRepoUrl() (string, error) {
 | 
				
			||||||
 | 
						_, err := os.Stat(r.Path)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	args := []string{"config", "--get", "remote.origin.url"}
 | 
						args := []string{"config", "--get", "remote.origin.url"}
 | 
				
			||||||
 | 
						return runCmdOutput(gitBinary, args, r.Path)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, err := os.Stat(path)
 | 
					// postPullCommand executes r.Then.
 | 
				
			||||||
 | 
					// It is trigged after successful git pull
 | 
				
			||||||
 | 
					func (r *Repo) postPullCommand() error {
 | 
				
			||||||
 | 
						if r.Then == "" {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						c, args, err := middleware.SplitCommandAndArgs(r.Then)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return "", err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cmd := exec.Command(gitBinary, args...)
 | 
						if err = runCmd(c, args, r.Path); err == nil {
 | 
				
			||||||
	cmd.Dir = path
 | 
							logger().Printf("Command %v successful.\n", r.Then)
 | 
				
			||||||
	output, err := cmd.Output()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return "", err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
	return strings.TrimSpace(string(output)), nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// initGit validates git installation and locates the git executable
 | 
					// initGit validates git installation and locates the git executable
 | 
				
			||||||
@ -199,6 +238,33 @@ func initGit() error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// runCmd is a helper function to run commands.
 | 
				
			||||||
 | 
					// It runs command with args from directory at dir.
 | 
				
			||||||
 | 
					// The executed process outputs to os.Stderr
 | 
				
			||||||
 | 
					func runCmd(command string, args []string, dir string) error {
 | 
				
			||||||
 | 
						cmd := exec.Command(command, args...)
 | 
				
			||||||
 | 
						cmd.Stderr = os.Stderr
 | 
				
			||||||
 | 
						cmd.Stdout = os.Stderr
 | 
				
			||||||
 | 
						cmd.Dir = dir
 | 
				
			||||||
 | 
						if err := cmd.Start(); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return cmd.Wait()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// runCmdOutput is a helper function to run commands and return output.
 | 
				
			||||||
 | 
					// It runs command with args from directory at dir.
 | 
				
			||||||
 | 
					// If successful, returns output and nil error
 | 
				
			||||||
 | 
					func runCmdOutput(command string, args []string, dir string) (string, error) {
 | 
				
			||||||
 | 
						cmd := exec.Command(command, args...)
 | 
				
			||||||
 | 
						cmd.Dir = dir
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						if output, err := cmd.Output(); err == nil {
 | 
				
			||||||
 | 
							return string(bytes.TrimSpace(output)), nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "", err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// writeScriptFile writes content to a temporary file.
 | 
					// writeScriptFile writes content to a temporary file.
 | 
				
			||||||
// It changes the temporary file mode to executable and
 | 
					// It changes the temporary file mode to executable and
 | 
				
			||||||
// closes it to prepare it for execution.
 | 
					// closes it to prepare it for execution.
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user