GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 5aab0f...5cf02c )
by Fedir
02:53
created

main.uniqSlice   A

Complexity

Conditions 5

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 9
nop 1
dl 0
loc 12
ccs 0
cts 8
cp 0
crap 30
rs 9.3333
c 0
b 0
f 0
1
// Copyright 2018 Fedir RYKHTIK. All rights reserved.
2
// Use of this source code is governed by the GNU GPL 3.0
3
// license that can be found in the LICENSE file.
4
5
// ghstat - statistical multi-criteria decision-making comparator for Github's projects.
6
package main
7
8
import (
9
	"encoding/csv"
10
	"encoding/json"
11
	"flag"
12
	"fmt"
13
	"log"
14
	"os"
15
	"path/filepath"
16
	"strings"
17
	"time"
18
19
	"github.com/fedir/ghstat/github"
20
	"github.com/fedir/ghstat/httpcache"
21
	"github.com/fedir/ghstat/timing"
22
)
23
24
func main() {
25 1
	var (
26
		clearHTTPCache         = flag.Bool("cc", false, "Clear HTTP cache")
27
		clearHTTPCacheDryRun   = flag.Bool("ccdr", false, "Clear HTTP cache (dry run)")
28
		debug                  = flag.Bool("d", false, "Debug mode")
29
		resultFileSavePath     = flag.String("f", "", "File path where result CSV file will be saved")
30
		rateLimitCheck         = flag.Bool("l", false, "Rate limit check")
31
		repositoriesKeysManual = flag.String("r", "", "Repositories keys")
32
		tmpFolder              = flag.String("t", "test_data", "Clear HTTP cache (dry run)")
33
		repositoriesKeys       = []string{}
34
	)
35 1
	flag.Parse()
36 1
	if *clearHTTPCache || *clearHTTPCacheDryRun {
37
		clearHTTPCacheFolder(*tmpFolder, *clearHTTPCacheDryRun)
38
		os.Exit(0)
39
	}
40 1
	if *rateLimitCheck {
41
		checkAndPrintRateLimit()
42
		os.Exit(0)
43
	}
44 1
	if *rateLimitCheck {
45
		checkAndPrintRateLimit()
46
		os.Exit(0)
47
	}
48 1
	if *repositoriesKeysManual != "" {
49
		repositoriesKeys = uniqSlice(strings.Split(*repositoriesKeysManual, ","))
50
	} else {
51 1
		repositoriesKeys = []string{
52
			"astaxie/beego",
53
			"gohugoio/hugo",
54
			"gin-gonic/gin",
55
			"labstack/echo",
56
			"revel/revel",
57
			"gobuffalo/buffalo",
58
			"go-chi/chi",
59
			"kataras/iris",
60
			"zenazn/goji",
61
		}
62
	}
63
64 1
	csvFilePath := ""
65 1
	if *resultFileSavePath != "" {
66
		csvFilePath = *resultFileSavePath
67
	} else {
68 1
		csvFilePath = "result.csv"
69
	}
70 1
	var ghData = [][]string{}
71 1
	headers := []string{
72
		"Name",
73
		"URL",
74
		"Author",
75
		"Language",
76
		"License",
77
		"Author's followers",
78
		"Top 10 contributors followers",
79
		"Created at",
80
		"Age in days",
81
		"Total commits",
82
		"Total additions",
83
		"Total deletions",
84
		"Total code changes",
85
		"Last commit date",
86
		"Commits/day",
87
		"Medium commit size",
88
		"Total releases",
89
		"Stargazers",
90
		"Forks",
91
		"Contributors",
92
		"Active forkers, %",
93
		"Open issues",
94
		"Total issues",
95
		"Issue/day",
96
		"Closed issues, %",
97
		"Place",
98
	}
99 1
	ghDataColumnIndexes := map[string]int{
100
		"nameColumn":                       0,
101
		"urlColumn":                        1,
102
		"authorColumn":                     2,
103
		"languageColumn":                   3,
104
		"licenseColumn":                    4,
105
		"authorsFollowersColumn":           5,
106
		"top10ContributorsFollowersColumn": 6,
107
		"ageColumn":                        8,
108
		"totalCommitsColumn":               9,
109
		"totalAdditionsColumn":             10,
110
		"totalDeletionsColumn":             11,
111
		"totalCodeChangesColumn":           12,
112
		"lastCommitDateColumn":             13,
113
		"commitsByDayColumn":               14,
114
		"mediCommitSizeColumn":             15,
115
		"totalTagsColumn":                  16,
116
		"stargazersColumn":                 17,
117
		"activeForkersColumn":              20,
118
		"issuesByDayColumn":                23,
119
		"closedIssuesPercentageColumn":     24,
120
		"totalPointsColumnIndex":           25,
121
	}
122 1
	dataChan := make(chan []string, len(repositoriesKeys))
123 1
	for _, rKey := range repositoriesKeys {
124 1
		go fillRepositoryStatistics(rKey, *tmpFolder, *debug, dataChan)
125
	}
126 1
	for range repositoriesKeys {
127 1
		ghData = append(ghData, <-dataChan)
128
	}
129 1
	greetings := rateGhData(ghData, ghDataColumnIndexes)
130 1
	fmt.Println(greetings)
131 1
	writeCsv(csvFilePath, headers, ghData)
132
}
133
134
func fillRepositoryStatistics(rKey string, tmpFolder string, debug bool, dataChan chan []string) {
135 1
	repositoryData := github.GetRepositoryStatistics(rKey, tmpFolder, debug)
136 1
	repositoryAge := int(time.Since(repositoryData.CreatedAt).Seconds() / 86400)
137 1
	authorLogin, lastCommitDate := github.GetRepositoryCommitsData(rKey, tmpFolder, debug)
138 1
	authorFollowers := 0
139 1
	if authorLogin != "" {
140 1
		authorFollowers = github.GetUserFollowers(authorLogin, tmpFolder, debug)
141
	}
142
143 1
	closedIssues := 0
144 1
	if repositoryData.HasIssues {
145 1
		closedIssues = github.GetRepositoryClosedIssues(rKey, tmpFolder, debug)
146
	}
147 1
	topContributorsFollowers, totalContributors := github.GetRepositoryContributors(rKey, tmpFolder, debug)
148 1
	totalTags := github.GetRepositoryTagsNumber(rKey, tmpFolder, debug)
149 1
	activeForkersPercentage := github.GetActiveForkersPercentage(totalContributors, repositoryData.Forks)
150 1
	issueByDay := github.GetIssueByDay(closedIssues+repositoryData.OpenIssues, repositoryAge)
151 1
	closedIssuesPercentage := github.GetClosedIssuesPercentage(repositoryData.OpenIssues, int(closedIssues))
152 1
	contributionStatistics := github.GetContributionStatistics(rKey, tmpFolder, debug)
153 1
	commitsByDay := github.GetCommitsByDay(contributionStatistics.TotalCommits, repositoryAge)
154 1
	ghProjectData := []string{
155
		repositoryData.FullName,
156
		fmt.Sprintf("https://github.com/%s", repositoryData.FullName),
157
		fmt.Sprintf("%s", repositoryData.Language),
158
		fmt.Sprintf("%s", func(a string) string {
159 1
			if a == "" {
160 1
				a = "[Account removed]"
161
			}
162 1
			return a
163
		}(authorLogin)),
164
		fmt.Sprintf("%s", func(l string) string {
165 1
			if l == "" {
166 1
				l = "[Unknown]"
167
			}
168 1
			return l
169
		}(repositoryData.License.SPDXID)),
170
		fmt.Sprintf("%d", authorFollowers),
171
		fmt.Sprintf("%d", topContributorsFollowers),
172
		fmt.Sprintf("%d/%02d", repositoryData.CreatedAt.Year(), repositoryData.CreatedAt.Month()),
173
		fmt.Sprintf("%d", repositoryAge),
174
		fmt.Sprintf("%d", contributionStatistics.TotalCommits),
175
		fmt.Sprintf("%d", contributionStatistics.TotalAdditions),
176
		fmt.Sprintf("%d", contributionStatistics.TotalDeletions),
177
		fmt.Sprintf("%d", contributionStatistics.TotalCodeChanges),
178
		fmt.Sprintf(lastCommitDate.Format("2006-01-02 15:04:05")),
0 ignored issues
show
introduced by
can't check non-constant format in call to Sprintf
Loading history...
179
		fmt.Sprintf("%.4f", commitsByDay),
180
		fmt.Sprintf("%d", contributionStatistics.MediumCommitSize),
181
		fmt.Sprintf("%d", totalTags),
182
		fmt.Sprintf("%d", repositoryData.Watchers),
183
		fmt.Sprintf("%d", repositoryData.Forks),
184
		fmt.Sprintf("%d", totalContributors),
185
		fmt.Sprintf("%.2f", activeForkersPercentage),
186
		fmt.Sprintf("%d", repositoryData.OpenIssues),
187
		fmt.Sprintf("%d", closedIssues+repositoryData.OpenIssues),
188
		fmt.Sprintf("%.4f", issueByDay),
189
		fmt.Sprintf("%.2f", closedIssuesPercentage),
190
		"0",
191
	}
192 1
	dataChan <- ghProjectData
193
}
194
195
func clearHTTPCacheFolder(tmpFolderPath string, dryRun bool) error {
196
	d, err := os.Open(tmpFolderPath)
197
	if err != nil {
198
		log.Fatalf("Could not open %s", tmpFolderPath)
199
	}
200
	defer d.Close()
201
	names, err := d.Readdirnames(-1)
202
	if err != nil {
203
		log.Fatalf("Could not read from %s", tmpFolderPath)
204
	}
205
	for _, name := range names {
206
		fp := filepath.Join(tmpFolderPath, name)
207
		if dryRun {
208
			fmt.Printf("Deleted %s\n", fp)
209
		} else {
210
			err = os.RemoveAll(fp)
211
			if err != nil {
212
				log.Fatalf("Could not remove %s", fp)
213
			}
214
			fmt.Printf("Deleted %s\n", fp)
215
		}
216
	}
217
	return nil
218
}
219
220
func checkAndPrintRateLimit() {
221
	type RateLimits struct {
222
		Resources struct {
223
			Core struct {
224
				Limit     int `json:"limit"`
225
				Remaining int `json:"remaining"`
226
				Reset     int `json:"reset"`
227
			} `json:"core"`
228
			Search struct {
229
				Limit     int `json:"limit"`
230
				Remaining int `json:"remaining"`
231
				Reset     int `json:"reset"`
232
			} `json:"search"`
233
			GraphQL struct {
234
				Limit     int `json:"limit"`
235
				Remaining int `json:"remaining"`
236
				Reset     int `json:"reset"`
237
			} `json:"graphql"`
238
		} `json:"resources"`
239
		Rate struct {
240
			Limit     int `json:"limit"`
241
			Remaining int `json:"remaining"`
242
			Reset     int `json:"reset"`
243
		} `json:"rate"`
244
	}
245
	url := "https://api.github.com/rate_limit"
246
	resp, statusCode, err := httpcache.MakeHTTPRequest(url)
247
	if err != nil {
248
		log.Fatalf("Error during checking rate limit : %d %v#", statusCode, err)
249
	}
250
	jsonResponse, _, _ := httpcache.ReadResp(resp)
251
	rateLimits := RateLimits{}
252
	json.Unmarshal(jsonResponse, &rateLimits)
253
	fmt.Printf("Core: %d/%d (reset in %d minutes)\n", rateLimits.Resources.Core.Remaining, rateLimits.Resources.Core.Limit, timing.GetRelativeTime(rateLimits.Resources.Core.Reset))
254
	fmt.Printf("Search: %d/%d (reset in %d minutes)\n", rateLimits.Resources.Search.Remaining, rateLimits.Resources.Search.Limit, timing.GetRelativeTime(rateLimits.Resources.Search.Reset))
255
	fmt.Printf("GraphQL: %d/%d (reset in %d minutes)\n", rateLimits.Resources.GraphQL.Remaining, rateLimits.Resources.GraphQL.Limit, timing.GetRelativeTime(rateLimits.Resources.GraphQL.Reset))
256
	fmt.Printf("Rate: %d/%d (reset in %d minutes)\n", rateLimits.Rate.Remaining, rateLimits.Rate.Limit, timing.GetRelativeTime(rateLimits.Rate.Reset))
257
}
258
259
func writeCsv(csvFilePath string, headers []string, ghData [][]string) {
260 1
	file, err := os.Create(csvFilePath)
261 1
	if err != nil {
262
		log.Fatal("Cannot create file", err)
263
	}
264 1
	defer file.Close()
265 1
	writer := csv.NewWriter(file)
266 1
	defer writer.Flush()
267 1
	err = writer.Write(headers)
268 1
	if err != nil {
269
		log.Fatal("Cannot write to file", err)
270
	}
271 1
	for _, value := range ghData {
272 1
		err := writer.Write(value)
273 1
		if err != nil {
274
			log.Fatal("Cannot write to file", err)
275
		}
276
	}
277
}
278
279
func uniqSlice(s []string) []string {
280
	unique := make(map[string]bool, len(s))
281
	us := make([]string, len(unique))
282
	for _, elem := range s {
283
		if len(elem) != 0 {
284
			if !unique[elem] {
285
				us = append(us, elem)
286
				unique[elem] = true
287
			}
288
		}
289
	}
290
	return us
291
}
292