cobench 0.0.16 → 0.0.19
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rultor.yml +2 -4
- data/README.md +2 -0
- data/assets/index.xsl +68 -14
- data/bin/cobench +60 -31
- data/features/cli.feature +12 -1
- data/lib/cobench/metrics/commits.rb +2 -1
- data/lib/cobench/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 221c41b7266e31f56aa5f90f39010bdd6e453b65ab8585ec20f8783f40e89f20
|
4
|
+
data.tar.gz: 5c163c8fa858234a5598a84a17ee9aa844b6c034fc85dca737e4365573aefe2e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c58b0cb4b3f7505970b0c79300fedf7731eebda93c9db619bd687720672fa584aaf293a7cbcd22dde52142b7b494861e368942add8fe7807bff8af029d5c78b5
|
7
|
+
data.tar.gz: 89fc4df3d481cf325bc9524110deefe552c542cbabf73d565469e18567924a579cc2d6d65757a1a28ad1dffa5c75f8f670ce86898d0a6f521242d9ead9a56486
|
data/.rultor.yml
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
docker:
|
2
|
+
image: yegor256/rultor-image:1.9.1
|
1
3
|
assets:
|
2
4
|
rubygems.yml: yegor256/home#assets/rubygems.yml
|
3
5
|
install: |
|
@@ -16,7 +18,3 @@ release:
|
|
16
18
|
merge:
|
17
19
|
script: |-
|
18
20
|
bundle exec rake
|
19
|
-
deploy:
|
20
|
-
script: |-
|
21
|
-
echo "There is nothing to deploy"
|
22
|
-
exit -1
|
data/README.md
CHANGED
@@ -26,6 +26,8 @@ Then, run it locally and read its output:
|
|
26
26
|
$ cobench --coder yegor256 --verbose
|
27
27
|
```
|
28
28
|
|
29
|
+
This is how our report [looks like](https://github.com/cqfn/bench).
|
30
|
+
|
29
31
|
## How to contribute
|
30
32
|
|
31
33
|
Read [these guidelines](https://www.yegor256.com/2014/04/15/github-guidelines.html).
|
data/assets/index.xsl
CHANGED
@@ -50,6 +50,9 @@ SOFTWARE.
|
|
50
50
|
header { text-align: center; }
|
51
51
|
footer { text-align: center; font-size: 0.8em; line-height: 1.2em; color: gray; }
|
52
52
|
article { width: 60em; border: 0; }
|
53
|
+
td.avatar { vertical-align: middle; text-align: center; }
|
54
|
+
td.avatar img { width: 1.5em; height: 1.5em; vertical-align: middle; }
|
55
|
+
.subtitle { font-size: 0.8em; line-height: 1em; color: gray; }
|
53
56
|
.sorter { cursor: pointer; }
|
54
57
|
</style>
|
55
58
|
</head>
|
@@ -57,7 +60,9 @@ SOFTWARE.
|
|
57
60
|
<section>
|
58
61
|
<header>
|
59
62
|
<p>
|
60
|
-
<
|
63
|
+
<a href="">
|
64
|
+
<img src="https://raw.githubusercontent.com/yegor256/cobench/master/logo.svg" style="width:64px"/>
|
65
|
+
</a>
|
61
66
|
</p>
|
62
67
|
</header>
|
63
68
|
<article>
|
@@ -77,17 +82,17 @@ SOFTWARE.
|
|
77
82
|
<xsl:text> on </xsl:text>
|
78
83
|
<xsl:value-of select="cobench/@time"/>
|
79
84
|
<xsl:text>. </xsl:text>
|
80
|
-
<xsl:text>"Commits" is the total number of </xsl:text>
|
85
|
+
<xsl:text>"Commits" is the total number of non-merge </xsl:text>
|
81
86
|
<a href="https://github.com/git-guides/git-commit">
|
82
87
|
<xsl:text>Git commits</xsl:text>
|
83
88
|
</a>
|
84
|
-
<xsl:text> authored by the user. </xsl:text>
|
85
|
-
<xsl:text>"HoC" is the total number of user's
|
89
|
+
<xsl:text> to the default branch, authored by the user. </xsl:text>
|
90
|
+
<xsl:text>"HoC" is the total number of user's </xsl:text>
|
86
91
|
<a href="https://www.yegor256.com/2014/11/14/hits-of-code.html">
|
87
92
|
<xsl:text>hits of code</xsl:text>
|
88
93
|
</a>
|
89
94
|
<xsl:text>. </xsl:text>
|
90
|
-
<xsl:text>"Issues" is the total number of issues. </xsl:text>
|
95
|
+
<xsl:text>"Issues" is the total number of issues submitted by the user. </xsl:text>
|
91
96
|
<a href="https://docs.github.com/en/issues">
|
92
97
|
<xsl:text>issues</xsl:text>
|
93
98
|
</a>
|
@@ -96,14 +101,37 @@ SOFTWARE.
|
|
96
101
|
<a href="https://docs.github.com/en/pull-requests">
|
97
102
|
<xsl:text>pull requests</xsl:text>
|
98
103
|
</a>
|
99
|
-
<xsl:text> created by the user and merged. </xsl:text>
|
100
|
-
<xsl:text>"Reviews" is the total number of merged pull requests reviewed by the user. </xsl:text>
|
101
|
-
<xsl:text>"Score" is an arithmetic summary of all
|
102
|
-
<xsl:
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
104
|
+
<xsl:text> created by the user and already merged. </xsl:text>
|
105
|
+
<xsl:text>"Reviews" is the total number of merged pull requests that were reviewed by the user. </xsl:text>
|
106
|
+
<xsl:text>"Score" is an arithmetic summary of all metrics with multipliers: </xsl:text>
|
107
|
+
<xsl:for-each select="cobench/weights/w">
|
108
|
+
<xsl:if test="position() > 1">
|
109
|
+
<xsl:text>, </xsl:text>
|
110
|
+
</xsl:if>
|
111
|
+
<xsl:text>one </xsl:text>
|
112
|
+
<xsl:choose>
|
113
|
+
<xsl:when test="substring(@id, string-length(@id)) = 's'">
|
114
|
+
<xsl:value-of select="substring(@id, 0, string-length(@id))"/>
|
115
|
+
</xsl:when>
|
116
|
+
<xsl:otherwise>
|
117
|
+
<xsl:value-of select="@id"/>
|
118
|
+
</xsl:otherwise>
|
119
|
+
</xsl:choose>
|
120
|
+
<xsl:choose>
|
121
|
+
<xsl:when test="position() = 1">
|
122
|
+
<xsl:text> costs </xsl:text>
|
123
|
+
</xsl:when>
|
124
|
+
<xsl:otherwise>
|
125
|
+
<xsl:text> — </xsl:text>
|
126
|
+
</xsl:otherwise>
|
127
|
+
</xsl:choose>
|
128
|
+
<xsl:value-of select="text()"/>
|
129
|
+
<xsl:text> point</xsl:text>
|
130
|
+
<xsl:if test="text() != '1'">
|
131
|
+
<xsl:text>s</xsl:text>
|
132
|
+
</xsl:if>
|
133
|
+
</xsl:for-each>
|
134
|
+
<xsl:text>.</xsl:text>
|
107
135
|
</p>
|
108
136
|
<p>
|
109
137
|
<xsl:text>The numbers you see reflect the activity of the last </xsl:text>
|
@@ -121,6 +149,7 @@ SOFTWARE.
|
|
121
149
|
<xsl:template match="cobench/titles">
|
122
150
|
<thead>
|
123
151
|
<tr>
|
152
|
+
<th/>
|
124
153
|
<th/>
|
125
154
|
<xsl:for-each select="title[generate-id() = generate-id(key('titles', .)[1])]">
|
126
155
|
<xsl:sort select="."/>
|
@@ -138,11 +167,18 @@ SOFTWARE.
|
|
138
167
|
</xsl:template>
|
139
168
|
<xsl:template match="coder">
|
140
169
|
<tr>
|
170
|
+
<td class="avatar">
|
171
|
+
<img src="https://socatar.com/github/{@id}/64-64"/>
|
172
|
+
</td>
|
141
173
|
<td>
|
142
174
|
<a href="https://github.com/{@id}">
|
143
175
|
<xsl:text>@</xsl:text>
|
144
176
|
<xsl:value-of select="@id"/>
|
145
177
|
</a>
|
178
|
+
<br/>
|
179
|
+
<span class="subtitle">
|
180
|
+
<xsl:value-of select="@details"/>
|
181
|
+
</span>
|
146
182
|
</td>
|
147
183
|
<xsl:for-each select="metrics/m">
|
148
184
|
<xsl:sort select="@id"/>
|
@@ -151,7 +187,7 @@ SOFTWARE.
|
|
151
187
|
</tr>
|
152
188
|
</xsl:template>
|
153
189
|
<xsl:template match="m">
|
154
|
-
<
|
190
|
+
<xsl:variable name="body">
|
155
191
|
<xsl:choose>
|
156
192
|
<xsl:when test="@href = ''">
|
157
193
|
<xsl:value-of select="."/>
|
@@ -162,6 +198,24 @@ SOFTWARE.
|
|
162
198
|
</a>
|
163
199
|
</xsl:otherwise>
|
164
200
|
</xsl:choose>
|
201
|
+
</xsl:variable>
|
202
|
+
<td class="num">
|
203
|
+
<xsl:if test="@actual">
|
204
|
+
<xsl:choose>
|
205
|
+
<xsl:when test="@actual">
|
206
|
+
<span class="firebrick">
|
207
|
+
<xsl:value-of select="$body"/>
|
208
|
+
</span>
|
209
|
+
</xsl:when>
|
210
|
+
<xsl:otherwise>
|
211
|
+
<xsl:value-of select="$body"/>
|
212
|
+
</xsl:otherwise>
|
213
|
+
</xsl:choose>
|
214
|
+
<br/>
|
215
|
+
<span class="subtitle" title="The actual value of the metric was capped">
|
216
|
+
<xsl:value-of select="@actual"/>
|
217
|
+
</span>
|
218
|
+
</xsl:if>
|
165
219
|
</td>
|
166
220
|
</xsl:template>
|
167
221
|
<xsl:template match="node()|@*">
|
data/bin/cobench
CHANGED
@@ -35,7 +35,7 @@ loog = Loog::REGULAR
|
|
35
35
|
def config(path)
|
36
36
|
f = File.expand_path(path)
|
37
37
|
args = []
|
38
|
-
args += File.readlines(f).map(&:strip) if File.exist?(f)
|
38
|
+
args += File.readlines(f).map(&:strip).reject { |a| a.empty? } if File.exist?(f)
|
39
39
|
args
|
40
40
|
end
|
41
41
|
|
@@ -47,6 +47,7 @@ opts = Slop.parse(args, strict: true, help: true) do |o|
|
|
47
47
|
o.bool '--version', 'Show current version'
|
48
48
|
o.bool '--verbose', 'Print as much log messages as possible'
|
49
49
|
o.bool '--dry', 'Make no real round trips to GitHub'
|
50
|
+
o.bool '--reuse', 'Don\'t fetch from GitHub, reuse the existing XML file'
|
50
51
|
o.integer '--days', 'How many days to measure', default: 7
|
51
52
|
o.string '--to', 'Directory where to save all files to', default: './cobench'
|
52
53
|
o.string '--token', 'GitHub authentication token'
|
@@ -73,17 +74,7 @@ end
|
|
73
74
|
Encoding.default_external = Encoding::UTF_8
|
74
75
|
Encoding.default_internal = Encoding::UTF_8
|
75
76
|
|
76
|
-
|
77
|
-
|
78
|
-
begin
|
79
|
-
home = File.absolute_path(opts[:to])
|
80
|
-
loog.debug("All files generated will be saved to #{home}")
|
81
|
-
if File.exist?(home)
|
82
|
-
loog.debug("Directory #{home} exists")
|
83
|
-
else
|
84
|
-
FileUtils.mkdir_p(home)
|
85
|
-
loog.debug("Directory #{home} created")
|
86
|
-
end
|
77
|
+
def build_xml(opts, loog)
|
87
78
|
if opts.token?
|
88
79
|
api = Octokit::Client.new(:access_token => opts[:token])
|
89
80
|
else
|
@@ -94,6 +85,7 @@ begin
|
|
94
85
|
api = Obk.new(api, pause: 2000)
|
95
86
|
loog.info("Reading GitHub data for the last #{opts[:days]} days")
|
96
87
|
titles = {}
|
88
|
+
data = {}
|
97
89
|
opts[:coder].each do |u|
|
98
90
|
user = u.downcase
|
99
91
|
loog.info("Scanning #{user}...")
|
@@ -111,9 +103,10 @@ begin
|
|
111
103
|
if opts.dry?
|
112
104
|
measures = [
|
113
105
|
{ title: 'Issues', total: Random.new.rand(100), href: 'https://github.com/' },
|
114
|
-
{ title: 'Pulls', total: Random.new.rand(100)
|
115
|
-
{ title: '
|
116
|
-
{ title: 'HoC', total: Random.new.rand(100)
|
106
|
+
{ title: 'Pulls', total: Random.new.rand(100) },
|
107
|
+
{ title: 'Commits', total: Random.new.rand(100) },
|
108
|
+
{ title: 'HoC', total: Random.new.rand(100) },
|
109
|
+
{ title: 'HoC', total: Random.new.rand(100) }
|
117
110
|
]
|
118
111
|
else
|
119
112
|
measures = m.take(loog)
|
@@ -127,21 +120,31 @@ begin
|
|
127
120
|
end
|
128
121
|
end
|
129
122
|
end
|
123
|
+
caps = {
|
124
|
+
'HoC' => lambda { |ms| ms['Commits'][:total] * 1000 },
|
125
|
+
}
|
130
126
|
data.each do |u, ms|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
50
|
138
|
-
elsif t == 'Commits'
|
139
|
-
5
|
140
|
-
elsif t == 'Reviews'
|
141
|
-
40
|
142
|
-
else
|
143
|
-
raise "Unknown title '#{t}'"
|
127
|
+
ms.map do |t, h|
|
128
|
+
next unless caps.key?(t)
|
129
|
+
cap = caps[t].call(ms)
|
130
|
+
if h[:total] > cap
|
131
|
+
data[u][t][:actual] = h[:total]
|
132
|
+
data[u][t][:total] = cap
|
144
133
|
end
|
134
|
+
end
|
135
|
+
data[u] = ms
|
136
|
+
end
|
137
|
+
weights = {
|
138
|
+
'HoC' => 1,
|
139
|
+
'Pulls' => 200,
|
140
|
+
'Issues' => 50,
|
141
|
+
'Commits' => 5,
|
142
|
+
'Reviews' => 75
|
143
|
+
}
|
144
|
+
data.each do |u, ms|
|
145
|
+
score = ms.map do |t, h|
|
146
|
+
raise "Unknown title '#{t}'" unless weights.key?(t)
|
147
|
+
h[:total] * weights[t]
|
145
148
|
end.inject(0, :+)
|
146
149
|
data[u]['Score'] = { total: score, href: '' }
|
147
150
|
end
|
@@ -152,12 +155,22 @@ begin
|
|
152
155
|
xml.title t
|
153
156
|
end
|
154
157
|
end
|
158
|
+
xml.weights do
|
159
|
+
weights.each do |t, w|
|
160
|
+
xml.w(id: t) do
|
161
|
+
xml.text w
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
155
165
|
xml.coders do
|
156
166
|
data.each do |u, ms|
|
157
167
|
xml.coder(id: u) do
|
168
|
+
xml.parent.set_attribute('details', api.user(u).name)
|
158
169
|
xml.metrics do
|
159
170
|
ms.each do |k, v|
|
160
171
|
xml.m(id: k, href: v[:href]) do
|
172
|
+
xml.parent.set_attribute('actual', v[:actual]) unless v[:actual].nil?
|
173
|
+
xml.parent.set_attribute('href', v[:href]) unless v[:href].nil?
|
161
174
|
xml.text v[:total]
|
162
175
|
end
|
163
176
|
end
|
@@ -167,11 +180,27 @@ begin
|
|
167
180
|
end
|
168
181
|
end
|
169
182
|
end
|
170
|
-
index = File.join(home, 'index.xml')
|
171
183
|
xml = builder.to_xml
|
172
184
|
loog.debug(xml)
|
173
|
-
|
174
|
-
|
185
|
+
xml
|
186
|
+
end
|
187
|
+
begin
|
188
|
+
home = File.absolute_path(opts[:to])
|
189
|
+
loog.debug("All files generated will be saved to #{home}")
|
190
|
+
if File.exist?(home)
|
191
|
+
loog.debug("Directory #{home} exists")
|
192
|
+
else
|
193
|
+
FileUtils.mkdir_p(home)
|
194
|
+
loog.debug("Directory #{home} created")
|
195
|
+
end
|
196
|
+
index = File.join(home, 'index.xml')
|
197
|
+
if opts[:reuse]
|
198
|
+
xml = File.read(index)
|
199
|
+
else
|
200
|
+
xml = build_xml(opts, loog)
|
201
|
+
File.write(index, xml)
|
202
|
+
loog.debug("XML saved to #{index} (#{File.size(index)} bytes)")
|
203
|
+
end
|
175
204
|
xslt = Nokogiri::XSLT(File.read(File.join(__dir__, '../assets/index.xsl')))
|
176
205
|
html = xslt.transform(Nokogiri::XML(xml), 'version' => "'#{Cobench::VERSION}'")
|
177
206
|
loog.debug(html)
|
data/features/cli.feature
CHANGED
@@ -11,7 +11,18 @@ Feature: Simple Reporting
|
|
11
11
|
Then Exit code is zero
|
12
12
|
|
13
13
|
Scenario: Simple report
|
14
|
-
When I run bin/cobench with "--coder yegor256 --coder
|
14
|
+
When I run bin/cobench with "--coder yegor256 --coder Jeff --verbose --dry --to foo"
|
15
|
+
Then Stdout contains "XML saved to"
|
16
|
+
And Exit code is zero
|
17
|
+
|
18
|
+
Scenario: Simple report with defaults
|
19
|
+
Given I have a ".cobench" file with content:
|
20
|
+
"""
|
21
|
+
--verbose
|
22
|
+
|
23
|
+
--coder=john
|
24
|
+
"""
|
25
|
+
When I run bin/cobench with "--dry"
|
15
26
|
Then Stdout contains "XML saved to"
|
16
27
|
And Exit code is zero
|
17
28
|
|
@@ -34,7 +34,7 @@ class Cobench::Commits
|
|
34
34
|
|
35
35
|
def take(loog)
|
36
36
|
from = (Time.now - (60 * 60 * 24 * @opts[:days])).strftime('%Y-%m-%d')
|
37
|
-
q = "author:#{@user} author-date:>#{from}"
|
37
|
+
q = "author:#{@user} author-date:>#{from} is:public merge:false"
|
38
38
|
json = @api.search_commits(q)
|
39
39
|
loog.debug("Found #{json.total_count} commits")
|
40
40
|
hoc = 0
|
@@ -44,6 +44,7 @@ class Cobench::Commits
|
|
44
44
|
next unless Cobench::Match.new(@opts, loog).matches?(repo)
|
45
45
|
loog.debug("Including #{sha} in #{repo}")
|
46
46
|
json = @api.commit(repo, sha)
|
47
|
+
next unless json
|
47
48
|
hocs = json.stats.total
|
48
49
|
loog.debug("Found #{hocs} HoC in #{sha}")
|
49
50
|
hoc += hocs
|
data/lib/cobench/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cobench
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.19
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yegor Bugayenko
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-08-
|
11
|
+
date: 2022-08-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: backtrace
|