cobench 0.0.17 → 0.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rultor.yml +2 -4
- data/README.md +2 -0
- data/assets/index.xsl +49 -8
- data/bin/cobench +61 -32
- data/features/cli.feature +1 -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: 944037515588754a9074051087759954ea921346dbf852b234a9507836e4ad4d
|
4
|
+
data.tar.gz: e718f120d970ae6f60cd83c748d4f33b6570fc4befa9ec7801316700951b56bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9bed4c1f78e45b2d7185e0c2bb67b928bb4b642cc70ac49851ed81de577817ce54da6fdd2f85f836fb26c30d27814b68c4d6e6164907e69157be0cd209227751
|
7
|
+
data.tar.gz: 9768224ff1925aef1a9cfa7656c930ae3782e6f5488aa7aa502f5a8032974925da8f64916147609cc0a2f801afd466d1b6b707ba1a6343a9a063fdf49ff9aed8
|
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
@@ -82,11 +82,11 @@ SOFTWARE.
|
|
82
82
|
<xsl:text> on </xsl:text>
|
83
83
|
<xsl:value-of select="cobench/@time"/>
|
84
84
|
<xsl:text>. </xsl:text>
|
85
|
-
<xsl:text>"Commits" is the total number of </xsl:text>
|
85
|
+
<xsl:text>"Commits" is the total number of non-merge </xsl:text>
|
86
86
|
<a href="https://github.com/git-guides/git-commit">
|
87
87
|
<xsl:text>Git commits</xsl:text>
|
88
88
|
</a>
|
89
|
-
<xsl:text> authored by the user. </xsl:text>
|
89
|
+
<xsl:text> to the default branch, authored by the user. </xsl:text>
|
90
90
|
<xsl:text>"HoC" is the total number of user's </xsl:text>
|
91
91
|
<a href="https://www.yegor256.com/2014/11/14/hits-of-code.html">
|
92
92
|
<xsl:text>hits of code</xsl:text>
|
@@ -104,11 +104,34 @@ SOFTWARE.
|
|
104
104
|
<xsl:text> created by the user and already merged. </xsl:text>
|
105
105
|
<xsl:text>"Reviews" is the total number of merged pull requests that were reviewed by the user. </xsl:text>
|
106
106
|
<xsl:text>"Score" is an arithmetic summary of all metrics with multipliers: </xsl:text>
|
107
|
-
<xsl:
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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>
|
112
135
|
</p>
|
113
136
|
<p>
|
114
137
|
<xsl:text>The numbers you see reflect the activity of the last </xsl:text>
|
@@ -164,7 +187,7 @@ SOFTWARE.
|
|
164
187
|
</tr>
|
165
188
|
</xsl:template>
|
166
189
|
<xsl:template match="m">
|
167
|
-
<
|
190
|
+
<xsl:variable name="body">
|
168
191
|
<xsl:choose>
|
169
192
|
<xsl:when test="@href = ''">
|
170
193
|
<xsl:value-of select="."/>
|
@@ -175,6 +198,24 @@ SOFTWARE.
|
|
175
198
|
</a>
|
176
199
|
</xsl:otherwise>
|
177
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>
|
178
219
|
</td>
|
179
220
|
</xsl:template>
|
180
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] * 512 },
|
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
|
-
xml.coder(id: u
|
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,7 @@ 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
15
|
Then Stdout contains "XML saved to"
|
16
16
|
And Exit code is zero
|
17
17
|
|
@@ -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.20
|
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
|