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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48f017fb5f4994354ad835b809870f6c804bf3350cd22b443e29f2dfda0f1276
4
- data.tar.gz: a57ba22b48e7760415921e4c80fb52c407e95ad84c1c3d5da4e1b2de76538f5e
3
+ metadata.gz: 221c41b7266e31f56aa5f90f39010bdd6e453b65ab8585ec20f8783f40e89f20
4
+ data.tar.gz: 5c163c8fa858234a5598a84a17ee9aa844b6c034fc85dca737e4365573aefe2e
5
5
  SHA512:
6
- metadata.gz: 845946fc2dfb68de3ef780a35b2063311e38104f337a235067f88d2ab00bf3b309e30025e1bb3dd2091ca2d43af80e18a8332258f6085dedf96f9f3de39889ef
7
- data.tar.gz: 9133dcf17b4d1ca286e70c46b122dfd458c6bce44f1f247bae74a4b5b7604db34c90a74493eb5ac49a05fa74626c5ab5ee14f09751d23ec5143052122173b5e6
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
- <img src="https://raw.githubusercontent.com/yegor256/cobench/master/logo.svg" style="width:64px"/>
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 hits of code. </xsl:text>
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 other numbers with multipliers: </xsl:text>
102
- <xsl:text>one Pull costs 100 points, </xsl:text>
103
- <xsl:text>one Issue 50 points, </xsl:text>
104
- <xsl:text>one Review 40 points, </xsl:text>
105
- <xsl:text>one Commit 5 points, </xsl:text>
106
- <xsl:text>one HoC 1 point.</xsl:text>
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() &gt; 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
- <td class="num">
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
- data = {}
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), href: '' },
115
- { title: 'HoC', total: Random.new.rand(100), href: '' },
116
- { title: 'HoC', total: Random.new.rand(100), href: '' }
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
- score = ms.map do |t, h|
132
- h[:total] * if t == 'HoC'
133
- 1
134
- elsif t == 'Pulls'
135
- 100
136
- elsif t == 'Issues'
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
- File.write(index, xml)
174
- loog.debug("XML saved to #{index} (#{File.size(index)} bytes)")
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 John --verbose --dry --to foo"
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
@@ -23,5 +23,5 @@
23
23
  # Copyright:: Copyright (c) 2022 Yegor Bugayenko
24
24
  # License:: MIT
25
25
  module Cobench
26
- VERSION = '0.0.16'.freeze
26
+ VERSION = '0.0.19'.freeze
27
27
  end
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.16
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-15 00:00:00.000000000 Z
11
+ date: 2022-08-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: backtrace