github.com/developest/gtm-enhanced@v1.0.4-0.20220111132249-cc80a3372c3f/command/report.go (about) 1 // Copyright 2016 Michael Schenk. All rights reserved. 2 // Use of this source code is governed by a MIT-style 3 // license that can be found in the LICENSE file. 4 5 package command 6 7 import ( 8 "bufio" 9 "flag" 10 "fmt" 11 "os" 12 "regexp" 13 "strings" 14 "time" 15 16 "github.com/DEVELOPEST/gtm-core/project" 17 "github.com/DEVELOPEST/gtm-core/report" 18 "github.com/DEVELOPEST/gtm-core/scm" 19 "github.com/DEVELOPEST/gtm-core/util" 20 "github.com/briandowns/spinner" 21 isatty "github.com/mattn/go-isatty" 22 "github.com/mitchellh/cli" 23 ) 24 25 // ReportCmd contains methods for report command 26 type ReportCmd struct { 27 UI cli.Ui 28 } 29 30 // NewReport create new ReportCmd struct 31 func NewReport() (cli.Command, error) { 32 return ReportCmd{}, nil 33 } 34 35 // Help returns help for report command 36 func (c ReportCmd) Help() string { 37 helpText := ` 38 Usage: gtm report [options] <Commit-ID>... 39 40 Display reports for one or more git repositories. 41 42 Options: 43 44 Report Formats: 45 46 -format=commits Specify report format [summary|project|commits|files|timeline-hours|timeline-commits] (default commits) 47 -full-message=false Include full commit message 48 -terminal-off=false Exclude time spent in terminal (Terminal plug-in is required) 49 -app-off=false Exclude time spent in apps 50 -force-color=false Always output color even if no terminal is detected, i.e 'gtm report -color | less -R' 51 -testing=false This is used for automated testing to force default test path 52 53 Commit Limiting: 54 55 -n int=0 Limit output, 0 is no limit 56 -from-date=yyyy-mm-dd Show commits starting from this date 57 -to-date=yyyy-mm-dd Show commits thru the end of this date 58 -author="" Show commits which contain author substring 59 -message="" Show commits which contain message substring 60 -subdir="" Show commits that are in subdirectory 61 -today=false Show commits for today 62 -yesterday=false Show commits for yesterday 63 -this-week=false Show commits for this week 64 -last-week=false Show commits for last week 65 -this-month=false Show commits for this month 66 -last-month=false Show commits for last month 67 -this-year=false Show commits for this year 68 -last-year=false Show commits for last year 69 70 Multi-Project Reporting: 71 72 -tags="" Project tags to report on, i.e --tags tag1,tag2 73 -all=false Show commits for all projects 74 ` 75 return strings.TrimSpace(helpText) 76 } 77 78 // Run executes report command with args 79 func (c ReportCmd) Run(args []string) int { 80 var limit int 81 var color, terminalOff, appOff, fullMessage, testing bool 82 var today, yesterday, thisWeek, lastWeek, thisMonth, lastMonth, thisYear, lastYear, all bool 83 var fromDate, toDate, message, author, subdir, tags, format string 84 cmdFlags := flag.NewFlagSet("report", flag.ContinueOnError) 85 cmdFlags.BoolVar(&color, "force-color", false, "") 86 cmdFlags.BoolVar(&terminalOff, "terminal-off", false, "") 87 cmdFlags.BoolVar(&appOff, "app-off", false, "") 88 cmdFlags.StringVar(&format, "format", "commits", "") 89 cmdFlags.IntVar(&limit, "n", 0, "") 90 cmdFlags.BoolVar(&fullMessage, "full-message", false, "") 91 cmdFlags.StringVar(&fromDate, "from-date", "", "") 92 cmdFlags.StringVar(&toDate, "to-date", "", "") 93 cmdFlags.BoolVar(&today, "today", false, "") 94 cmdFlags.BoolVar(&yesterday, "yesterday", false, "") 95 cmdFlags.BoolVar(&thisWeek, "this-week", false, "") 96 cmdFlags.BoolVar(&lastWeek, "last-week", false, "") 97 cmdFlags.BoolVar(&thisMonth, "this-month", false, "") 98 cmdFlags.BoolVar(&lastMonth, "last-month", false, "") 99 cmdFlags.BoolVar(&thisYear, "this-year", false, "") 100 cmdFlags.BoolVar(&lastYear, "last-year", false, "") 101 cmdFlags.StringVar(&author, "author", "", "") 102 cmdFlags.StringVar(&message, "message", "", "") 103 cmdFlags.StringVar(&subdir, "subdir", "", "") 104 cmdFlags.StringVar(&tags, "tags", "", "") 105 cmdFlags.BoolVar(&all, "all", false, "") 106 cmdFlags.BoolVar(&testing, "testing", false, "") 107 cmdFlags.Usage = func() { c.UI.Output(c.Help()) } 108 if err := cmdFlags.Parse(args); err != nil { 109 return 1 110 } 111 112 if !util.StringInSlice([]string{"summary", "commits", "timeline-hours", "files", "timeline-commits", "project"}, format) { 113 c.UI.Error(fmt.Sprintf("report --format=%s not valid\n", format)) 114 return 1 115 } 116 117 var ( 118 commits []string 119 out string 120 err error 121 ) 122 123 const invalidSHA1 = "\nNot a valid commit SHA-1 %s\n" 124 125 // if running from within a MINGW console isatty detection does not work 126 // https://github.com/mintty/mintty/issues/482 127 isMinGW := strings.HasPrefix(os.Getenv("MSYSTEM"), "MINGW") 128 129 sha1Regex := regexp.MustCompile(`\A([0-9a-f]{40})\z`) 130 131 var projCommits []report.ProjectCommits 132 133 switch { 134 case !testing && !isMinGW && !isatty.IsTerminal(os.Stdin.Fd()): 135 scanner := bufio.NewScanner(os.Stdin) 136 for scanner.Scan() { 137 if !sha1Regex.MatchString(scanner.Text()) { 138 c.UI.Error(fmt.Sprintf("%s %s", invalidSHA1, scanner.Text())) 139 return 1 140 } 141 commits = append(commits, scanner.Text()) 142 } 143 curProjPath, err := scm.GitRepoPath() 144 if err != nil { 145 c.UI.Error(err.Error()) 146 return 1 147 } 148 curProjPath, err = scm.Workdir(curProjPath) 149 if err != nil { 150 c.UI.Error(err.Error()) 151 return 1 152 } 153 154 projCommits = append(projCommits, report.ProjectCommits{Path: curProjPath, Commits: commits}) 155 156 case !testing && len(cmdFlags.Args()) > 0: 157 for _, a := range cmdFlags.Args() { 158 if !sha1Regex.MatchString(a) { 159 c.UI.Error(fmt.Sprintf("%s %s", invalidSHA1, a)) 160 return 1 161 } 162 commits = append(commits, a) 163 } 164 curProjPath, err := scm.GitRepoPath() 165 if err != nil { 166 c.UI.Error(err.Error()) 167 return 1 168 } 169 curProjPath, err = scm.Workdir(curProjPath) 170 if err != nil { 171 c.UI.Error(err.Error()) 172 return 1 173 } 174 175 projCommits = append(projCommits, report.ProjectCommits{Path: curProjPath, Commits: commits}) 176 177 default: 178 index, err := project.NewIndex() 179 if err != nil { 180 c.UI.Error(err.Error()) 181 return 1 182 } 183 184 var tagList []string 185 if tags != "" { 186 tagList = util.Map(strings.Split(tags, ","), strings.TrimSpace) 187 } 188 projects, err := index.Get(tagList, all) 189 if err != nil { 190 c.UI.Error(err.Error()) 191 return 1 192 } 193 194 limiter, err := scm.NewCommitLimiter( 195 limit, fromDate, toDate, author, message, 196 today, yesterday, thisWeek, lastWeek, 197 thisMonth, lastMonth, thisYear, lastYear) 198 199 if err != nil { 200 c.UI.Error(err.Error()) 201 return 1 202 } 203 204 limit = limiter.Max 205 206 for _, p := range projects { 207 commits, err = scm.CommitIDs(limiter, p) 208 if err != nil { 209 c.UI.Error(err.Error()) 210 return 1 211 } 212 projCommits = append(projCommits, report.ProjectCommits{Path: p, Commits: commits}) 213 } 214 } 215 216 options := report.OutputOptions{ 217 FullMessage: fullMessage, 218 TerminalOff: terminalOff, 219 AppOff: appOff, 220 Color: color, 221 Limit: limit, 222 Subdir: subdir} 223 224 s := spinner.New(spinner.CharSets[9], 100*time.Millisecond) 225 s.Start() 226 227 switch format { 228 case "project": 229 out, err = report.ProjectSummary(projCommits, options) 230 case "summary": 231 out, err = report.CommitSummary(projCommits, options) 232 case "commits": 233 out, err = report.Commits(projCommits, options) 234 case "files": 235 out, err = report.Files(projCommits, options) 236 case "timeline-hours": 237 out, err = report.Timeline(projCommits, options) 238 case "timeline-commits": 239 out, err = report.TimelineCommits(projCommits, options) 240 } 241 242 s.Stop() 243 244 if err != nil { 245 c.UI.Error(err.Error()) 246 return 1 247 } 248 c.UI.Output(out) 249 250 return 0 251 } 252 253 // Synopsis return help for report command 254 func (c ReportCmd) Synopsis() string { 255 return "Display reports for git repositories" 256 }