github.com/nilium/gitlab-runner@v12.5.0+incompatible/commands/helpers/file_archiver.go (about) 1 package helpers 2 3 import ( 4 "bufio" 5 "bytes" 6 "errors" 7 "fmt" 8 "io" 9 "os" 10 "os/exec" 11 "path/filepath" 12 "sort" 13 "strings" 14 "time" 15 16 "github.com/sirupsen/logrus" 17 ) 18 19 type fileArchiver struct { 20 Paths []string `long:"path" description:"Add paths to archive"` 21 Untracked bool `long:"untracked" description:"Add git untracked files"` 22 Verbose bool `long:"verbose" description:"Detailed information"` 23 24 wd string 25 files map[string]os.FileInfo 26 } 27 28 func (c *fileArchiver) isChanged(modTime time.Time) bool { 29 for _, info := range c.files { 30 if modTime.Before(info.ModTime()) { 31 return true 32 } 33 } 34 return false 35 } 36 37 func (c *fileArchiver) isFileChanged(fileName string) bool { 38 ai, err := os.Stat(fileName) 39 if ai != nil { 40 if !c.isChanged(ai.ModTime()) { 41 return false 42 } 43 } else if !os.IsNotExist(err) { 44 logrus.Warningln(err) 45 } 46 return true 47 } 48 49 func (c *fileArchiver) sortedFiles() []string { 50 files := make([]string, len(c.files)) 51 52 i := 0 53 for file := range c.files { 54 files[i] = file 55 i++ 56 } 57 58 sort.Strings(files) 59 return files 60 } 61 62 func (c *fileArchiver) add(path string) (err error) { 63 // Always use slashes 64 path = filepath.ToSlash(path) 65 66 // Check if file exist 67 info, err := os.Lstat(path) 68 if err == nil { 69 c.files[path] = info 70 } 71 return 72 } 73 74 func (c *fileArchiver) process(match string) bool { 75 var absolute, relative string 76 var err error 77 78 absolute, err = filepath.Abs(match) 79 if err == nil { 80 // Let's try to find a real relative path to an absolute from working directory 81 relative, err = filepath.Rel(c.wd, absolute) 82 } 83 if err == nil { 84 // Process path only if it lives in our build directory 85 if !strings.HasPrefix(relative, ".."+string(filepath.Separator)) { 86 err = c.add(relative) 87 } else { 88 err = errors.New("not supported: outside build directory") 89 } 90 } 91 if err == nil { 92 return true 93 } else if os.IsNotExist(err) { 94 // We hide the error that file doesn't exist 95 return false 96 } 97 98 logrus.Warningf("%s: %v", match, err) 99 return false 100 } 101 102 func (c *fileArchiver) processPaths() { 103 for _, path := range c.Paths { 104 matches, err := filepath.Glob(path) 105 if err != nil { 106 logrus.Warningf("%s: %v", path, err) 107 continue 108 } 109 110 found := 0 111 112 for _, match := range matches { 113 err := filepath.Walk(match, func(path string, info os.FileInfo, err error) error { 114 if c.process(path) { 115 found++ 116 } 117 return nil 118 }) 119 if err != nil { 120 logrus.Warningln("Walking", match, err) 121 } 122 } 123 124 if found == 0 { 125 logrus.Warningf("%s: no matching files", path) 126 } else { 127 logrus.Infof("%s: found %d matching files", path, found) 128 } 129 } 130 } 131 132 func (c *fileArchiver) processUntracked() { 133 if !c.Untracked { 134 return 135 } 136 137 found := 0 138 139 var output bytes.Buffer 140 cmd := exec.Command("git", "ls-files", "-o", "-z") 141 cmd.Env = os.Environ() 142 cmd.Stdout = &output 143 cmd.Stderr = os.Stderr 144 logrus.Debugln("Executing command:", strings.Join(cmd.Args, " ")) 145 err := cmd.Run() 146 if err == nil { 147 reader := bufio.NewReader(&output) 148 for { 149 line, err := reader.ReadString(0) 150 if err == io.EOF { 151 break 152 } else if err != nil { 153 logrus.Warningln(err) 154 break 155 } 156 if c.process(line[:len(line)-1]) { 157 found++ 158 } 159 } 160 161 if found == 0 { 162 logrus.Warningf("untracked: no files") 163 } else { 164 logrus.Infof("untracked: found %d files", found) 165 } 166 } else { 167 logrus.Warningf("untracked: %v", err) 168 } 169 } 170 171 func (c *fileArchiver) enumerate() error { 172 wd, err := os.Getwd() 173 if err != nil { 174 return fmt.Errorf("Failed to get current working directory: %v", err) 175 } 176 177 c.wd = wd 178 c.files = make(map[string]os.FileInfo) 179 180 c.processPaths() 181 c.processUntracked() 182 return nil 183 }