overview 0.0.3.61 → 0.0.4.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.gitignore +21 -0
- data/.travis.yml +3 -3
- data/README.rdoc +2 -2
- data/Rakefile +1 -1
- data/bin/overview +52 -17
- data/lib/{appversion/appversion.rb → appversion.rb} +11 -6
- data/lib/changelog.rb +455 -0
- data/lib/{appversion → helpers}/ci.rb +4 -2
- data/lib/helpers/git.rb +67 -0
- data/lib/helpers/github.rb +81 -0
- data/lib/{appversion → helpers}/string.rb +0 -0
- data/lib/overview/version.rb +1 -1
- data/lib/overview.rb +5 -5
- data/overview.gemspec +8 -3
- data/spec/appversion_spec.rb +17 -48
- data/spec/changelog_spec.rb +12 -0
- data/spec/helpers_spec.rb +196 -0
- metadata +95 -7
- data/lib/appversion/git.rb +0 -41
data/lib/changelog.rb
ADDED
@@ -0,0 +1,455 @@
|
|
1
|
+
module Changelog
|
2
|
+
|
3
|
+
require 'httparty'
|
4
|
+
require 'json'
|
5
|
+
require 'octokit'
|
6
|
+
require 'formatador'
|
7
|
+
require 'pp'
|
8
|
+
require 'yaml'
|
9
|
+
require 'logging'
|
10
|
+
require_relative 'helpers/ci'
|
11
|
+
require_relative 'helpers/git'
|
12
|
+
require_relative 'helpers/github'
|
13
|
+
require 'api_cache'
|
14
|
+
require 'moneta'
|
15
|
+
require 'faraday-http-cache'
|
16
|
+
=begin
|
17
|
+
TODO:
|
18
|
+
|
19
|
+
More than I can comprehend...
|
20
|
+
Keep all incoming data in original format, so that it can be reproccessed. e.g. Make calls. Store Data. Retrieve data. Process data. Display data. Deploy.
|
21
|
+
Mark sprint.ly items as deployed if complete. This will eventually be in a separate script.
|
22
|
+
Sprint.ly status
|
23
|
+
Compelete refactor into a modern ruby CLI app.
|
24
|
+
More info output
|
25
|
+
Color output for terminal
|
26
|
+
HTML output with embedded links
|
27
|
+
Logic to be improved for push and pull rquests
|
28
|
+
Deployment logic to work differently for feature branches... I think
|
29
|
+
=end
|
30
|
+
|
31
|
+
class Base
|
32
|
+
def initialize
|
33
|
+
@logger = Logging.logger[self]
|
34
|
+
@logger.add_appenders \
|
35
|
+
Logging.appenders.stdout,
|
36
|
+
Logging.appenders.file('overview.log')
|
37
|
+
@logger.level = :info
|
38
|
+
|
39
|
+
APICache.store = Moneta.new(:YAML, :file => "#{self.class.name}_cache")
|
40
|
+
APICache.logger.level = Logger::DEBUG
|
41
|
+
|
42
|
+
#Caching for octokit
|
43
|
+
#store = Moneta.new(:YAML, :file => 'changelog_octokit.cache')
|
44
|
+
stack = Faraday::RackBuilder.new do |builder|
|
45
|
+
builder.use Faraday::HttpCache
|
46
|
+
builder.use Octokit::Response::RaiseError
|
47
|
+
#builder.use :store => store
|
48
|
+
builder.adapter Faraday.default_adapter
|
49
|
+
end
|
50
|
+
|
51
|
+
Octokit.middleware = stack
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Release < Base
|
56
|
+
def initialize(name, description, sha, date, status=nil, items=nil)
|
57
|
+
super()
|
58
|
+
status ||= "NA"
|
59
|
+
@name = name.delete("\n")
|
60
|
+
|
61
|
+
@description = description
|
62
|
+
@sha = sha
|
63
|
+
@date = date
|
64
|
+
@status = status
|
65
|
+
@items = items
|
66
|
+
end
|
67
|
+
|
68
|
+
def commits
|
69
|
+
end
|
70
|
+
|
71
|
+
def items
|
72
|
+
@items
|
73
|
+
end
|
74
|
+
|
75
|
+
def inspect
|
76
|
+
str = "<#{@name} at #{@date.strftime("%x")} (#{@items.length} items)>"
|
77
|
+
@items.each { |i| str.concat("<#{i.inspect}>") }
|
78
|
+
return str
|
79
|
+
end
|
80
|
+
|
81
|
+
def name
|
82
|
+
return @name
|
83
|
+
end
|
84
|
+
|
85
|
+
def date
|
86
|
+
return @date
|
87
|
+
end
|
88
|
+
|
89
|
+
def status
|
90
|
+
return @status
|
91
|
+
end
|
92
|
+
|
93
|
+
def released?
|
94
|
+
return self.status == "RELEASED"
|
95
|
+
end
|
96
|
+
|
97
|
+
def display
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class Item
|
103
|
+
def initialize(title, id, description, sha, date, source, type=nil, status=nil, author=nil, environments=nil)
|
104
|
+
type ||="NA"
|
105
|
+
status ||="UNKNOWN"
|
106
|
+
author ||="UNKNOWN"
|
107
|
+
environments ||= []
|
108
|
+
@title = title.delete("\n")
|
109
|
+
@description = description
|
110
|
+
@sha = sha
|
111
|
+
@date = date
|
112
|
+
@id = id
|
113
|
+
@source = source
|
114
|
+
@type = type
|
115
|
+
@status = status
|
116
|
+
@author = author
|
117
|
+
@environments = environments
|
118
|
+
end
|
119
|
+
|
120
|
+
def inspect
|
121
|
+
"<#{@source} #{@type.upcase} ##{@id} : #{self.status} : #{@title}>"
|
122
|
+
end
|
123
|
+
|
124
|
+
def title
|
125
|
+
return @title
|
126
|
+
end
|
127
|
+
|
128
|
+
def date
|
129
|
+
return @date
|
130
|
+
end
|
131
|
+
|
132
|
+
def type
|
133
|
+
return @type
|
134
|
+
end
|
135
|
+
|
136
|
+
def author
|
137
|
+
return @author
|
138
|
+
end
|
139
|
+
|
140
|
+
def environments
|
141
|
+
return @environments.any? ? @environments : nil
|
142
|
+
end
|
143
|
+
|
144
|
+
def sha
|
145
|
+
return @sha
|
146
|
+
end
|
147
|
+
|
148
|
+
def id
|
149
|
+
return @id
|
150
|
+
end
|
151
|
+
|
152
|
+
def status
|
153
|
+
return @status.nil? ? "NA" : @status.upcase
|
154
|
+
end
|
155
|
+
|
156
|
+
def source
|
157
|
+
return @source
|
158
|
+
end
|
159
|
+
|
160
|
+
def sprintly?
|
161
|
+
return self.source.upcase == "SPRINT.LY"
|
162
|
+
end
|
163
|
+
|
164
|
+
def complete?
|
165
|
+
return %w(COMPLETED ACCEPTED).include?(self.status)
|
166
|
+
end
|
167
|
+
|
168
|
+
def built?
|
169
|
+
return self.environments.join.scan(/#(\d+)/).any? unless self.environments.nil?
|
170
|
+
end
|
171
|
+
|
172
|
+
def released?
|
173
|
+
return self.environments.join.scan(/v(\d+)/).any? unless self.environments.nil?
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
class Log < Base
|
178
|
+
def githubReleaseStatus(release=nil)
|
179
|
+
case
|
180
|
+
when release.nil?
|
181
|
+
status = "NA"
|
182
|
+
when release.draft
|
183
|
+
status = "DRAFT"
|
184
|
+
when release.prerelease
|
185
|
+
status = "PRE-RELEASE"
|
186
|
+
else
|
187
|
+
status = "RELEASED"
|
188
|
+
end
|
189
|
+
return status
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
def displayEnvironments(environments)
|
194
|
+
case
|
195
|
+
when environments.nil?
|
196
|
+
environments = ""
|
197
|
+
when environments.compact.length > 0
|
198
|
+
environments = " [" + environments.compact.join(", ") + "]"
|
199
|
+
else
|
200
|
+
environments = ""
|
201
|
+
end
|
202
|
+
return environments
|
203
|
+
end
|
204
|
+
|
205
|
+
def deploy(releases)
|
206
|
+
buildItems = []
|
207
|
+
allReleasedItems = []
|
208
|
+
releases.each { |r|
|
209
|
+
releaseItems = []
|
210
|
+
r.items.each { |i|
|
211
|
+
buildItems << i.id unless (i.built? || !i.complete?) #potential write each build for incomplete tasks or stories...not sure
|
212
|
+
releaseItems << i.id unless (i.released? || !i.complete? || !r.released? || allReleasedItems.include?(i.id))
|
213
|
+
}
|
214
|
+
allReleasedItems.concat(releaseItems)
|
215
|
+
releaseEnvironment = "Release #{r.name}"
|
216
|
+
sprintlyDeploy(releaseItems.uniq, releaseEnvironment) unless (releaseEnvironment.nil? || !releaseItems.any?)
|
217
|
+
}
|
218
|
+
buildEnvironment = "Build ##{ENV['TRAVIS_BUILD_NUMBER']}"
|
219
|
+
sprintlyDeploy(buildItems.uniq, buildEnvironment) unless (buildEnvironment.nil? || !buildItems.any?)
|
220
|
+
end
|
221
|
+
|
222
|
+
#refactor to keep source data separate, and update environments on sprint.ly ticket so future checks return the new environemnt
|
223
|
+
#deployments should also be batched, with one api call for all items of the build or release
|
224
|
+
#@iterations = 0
|
225
|
+
def sprintlyDeploy(ids, environment)
|
226
|
+
#@iterations += 1
|
227
|
+
data = {:environment => environment, :numbers => ids.join(",")}
|
228
|
+
url = "https://sprint.ly/api/products/#{@product_id}/deploys.json"
|
229
|
+
response = HTTParty.post(url, {:basic_auth => @auth, :body => data})
|
230
|
+
item = JSON.parse(response.body)
|
231
|
+
#pp item
|
232
|
+
#exit unless @iterations < 2
|
233
|
+
#curl -u jono@overllc.com:WXKp2h3F38VUm2CmzxpqBYMDZBFvw84f --data "environment=test&numbers=22,8" https://sprint.ly/api/products/20064/deploys.json
|
234
|
+
end
|
235
|
+
|
236
|
+
def displayReleases(releases)
|
237
|
+
f = Formatador.new
|
238
|
+
types = releases.flat_map { |r|
|
239
|
+
r.items.flat_map { |i|
|
240
|
+
i.type
|
241
|
+
}
|
242
|
+
}.uniq
|
243
|
+
environments = releases.flat_map { |r|
|
244
|
+
r.items.flat_map { |i|
|
245
|
+
i.environments.flatten unless i.environments.nil?
|
246
|
+
}
|
247
|
+
}.uniq.compact
|
248
|
+
#puts environments.inspect
|
249
|
+
releases.each { |r|
|
250
|
+
f.display_line("#{r.name} (#{r.status.upcase}, #{displayDate(r.date)})")
|
251
|
+
f.indent {
|
252
|
+
|
253
|
+
types.each { |t|
|
254
|
+
items = r.items.select { |i| i.type == t }
|
255
|
+
f.display_line("#{t.upcase}") if items.length > 0
|
256
|
+
f.indent {
|
257
|
+
items.each { |i|
|
258
|
+
# i.sha[0..6]
|
259
|
+
#TODO fix this
|
260
|
+
bullet = ENV['TRAVIS_COMMIT'] == i.sha[0..6] ? "+" : "•"
|
261
|
+
f.display_line("#{bullet} ##{i.id}: #{i.title} (#{i.status.upcase}; #{i.author}; #{displayDate(i.date)})#{displayEnvironments(i.environments)}")
|
262
|
+
|
263
|
+
}
|
264
|
+
}
|
265
|
+
}
|
266
|
+
}
|
267
|
+
|
268
|
+
}
|
269
|
+
end
|
270
|
+
|
271
|
+
def idsFromCommitMessage(message)
|
272
|
+
commands = %w(close closed closes finish finished finishes fix fixed fixes breaks unfixes reopen reopens re-open re-opens addresses re ref references refs start starts see).collect { |x| "\\b#{x}\\b" }.join("|")
|
273
|
+
prefixes = %w(task issue defect bug item ticket).collect { |x| "\\b#{x}:\\b" }.join("|") + "|#"
|
274
|
+
re = Regexp.new(/(?:#{commands})\s(?:#{prefixes})(\d+)/)
|
275
|
+
#working re = Regexp.new(/(?:#{commands})\s(?:#{prefixes})(\d+)/)
|
276
|
+
#working re = Regexp.new(/(?:\bcloses\b|\bfixes\b)..(\d+)/)
|
277
|
+
#puts re.source
|
278
|
+
|
279
|
+
sprintlyIds = message.scan(re).flatten.compact
|
280
|
+
crashlyticsIds = message.scan(/(c|C):(?<crashlytics>\d+)/).flatten.compact
|
281
|
+
return sprintlyIds, crashlyticsIds
|
282
|
+
end
|
283
|
+
|
284
|
+
def crashlyticsItem(title, id, sha, date, author)
|
285
|
+
#puts " " + item["type"].capitalize + " " + item["number"].to_s + ": " + item["title"]
|
286
|
+
return Item.new(title, id, "NA", sha, date, "Crashlytics", "crash", nil, author)
|
287
|
+
end
|
288
|
+
|
289
|
+
#TODO - consider returning sprint.ly story rather than task
|
290
|
+
#TODO - cache raw response, not item
|
291
|
+
def sprintlyItem(id, sha, date, author)
|
292
|
+
url = "https://sprint.ly/api/products/" + @product_id + "/items/" + id + ".json"
|
293
|
+
APICache.get(url, :timeout => 30, :fail => []) do
|
294
|
+
response = HTTParty.get(url, :basic_auth => @auth)
|
295
|
+
item = JSON.parse(response.body)
|
296
|
+
Item.new(item["title"], item["number"], item["description"], sha, date, "Sprint.ly", item["type"], item["status"], author, item["deployed_to"])
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def commitItem(commit)
|
301
|
+
APICache.get(commit.sha, :fail => []) do
|
302
|
+
Item.new(commit.commit.message.lines.first, commit.sha[0..6], commit.commit.message, commit.sha, commit.commit.author.date, "Github", "commit", "NA", commit.commit.author.name)
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
|
307
|
+
|
308
|
+
def itemsFromCommit(commit)
|
309
|
+
#pp commit
|
310
|
+
items = []
|
311
|
+
sprintlyIds, crashlyticsIds = idsFromCommitMessage(commit.commit.message)
|
312
|
+
#puts sprintlyIds.class
|
313
|
+
#puts commit.commit.message
|
314
|
+
unless (sprintlyIds.nil? || !sprintlyIds.any?) then
|
315
|
+
sprintlyIds.each { |id| items << sprintlyItem(id, commit.sha, commit.commit.author.date, commit.commit.author.name) }
|
316
|
+
end
|
317
|
+
unless (crashlyticsIds.nil? || !crashlyticsIds.any?) then
|
318
|
+
crashlyticsIds.each { |id| items << crashlyticsItem(commit.commit.message.lines.first, id, commit.sha, commit.commit.author.date, commit.commit.author.name) }
|
319
|
+
end
|
320
|
+
items << commitItem(commit) unless (!items.nil? && items.any?)
|
321
|
+
return items
|
322
|
+
end
|
323
|
+
|
324
|
+
|
325
|
+
def displayDate(date)
|
326
|
+
return date.strftime("%-d-%b-%Y")
|
327
|
+
end
|
328
|
+
|
329
|
+
|
330
|
+
#*****
|
331
|
+
#deploy items parsed from travis commit sha (as I can't check what was previosuly undeployed)
|
332
|
+
#awaiting improvements from sprint.ly - due 19 Feb
|
333
|
+
#*****
|
334
|
+
|
335
|
+
#TRAVIS_COMMIT_RANGE
|
336
|
+
|
337
|
+
def getReleasesFromServices
|
338
|
+
|
339
|
+
|
340
|
+
#user = @github.client.user
|
341
|
+
#user.login
|
342
|
+
|
343
|
+
|
344
|
+
result = []
|
345
|
+
|
346
|
+
all_releases = @github.releases
|
347
|
+
#p all_releases.inspect
|
348
|
+
r = all_releases.first
|
349
|
+
#p r
|
350
|
+
p r.tag_name
|
351
|
+
ref = @github.ref(r.tag_name)
|
352
|
+
sha = ref.object.sha
|
353
|
+
commit = @github.commit(sha).commit
|
354
|
+
since = commit.author.date
|
355
|
+
|
356
|
+
commits = @github.commits_since(since)
|
357
|
+
commits.pop #remove commit from last release - not sure why this in necessary and recon that it is not accurate either
|
358
|
+
|
359
|
+
items = []
|
360
|
+
commits.each { |c| items.concat(itemsFromCommit(c)) }
|
361
|
+
|
362
|
+
release = Release.new("Next Version", "NA", sha, since, "NA", items)
|
363
|
+
|
364
|
+
result << release
|
365
|
+
|
366
|
+
releases = all_releases.take(ENV['NO_OF_RELEASES'].to_i)
|
367
|
+
|
368
|
+
releases.each_with_index { |r, index|
|
369
|
+
|
370
|
+
next_r = all_releases[index+1] unless index == all_releases.size - 1 #note we use all_releases for this
|
371
|
+
ref = @github.ref(r.tag_name)
|
372
|
+
sha = ref.object.sha
|
373
|
+
commit = @github.commit(sha).commit
|
374
|
+
|
375
|
+
to = commit.author.date
|
376
|
+
|
377
|
+
if next_r
|
378
|
+
next_ref = @github.ref(next_r.tag_name)
|
379
|
+
next_sha = next_ref.object.sha
|
380
|
+
next_commit = @github.commit(next_sha).commit
|
381
|
+
from = next_commit.author.date
|
382
|
+
commits = @github.client.commits_between(ENV['TRAVIS_REPO_SLUG'], from, to, ENV['TRAVIS_BRANCH'])
|
383
|
+
else
|
384
|
+
commits = @github.client.commits_before(ENV['TRAVIS_REPO_SLUG'], to, ENV['TRAVIS_BRANCH'])
|
385
|
+
end
|
386
|
+
|
387
|
+
items = []
|
388
|
+
commits.each { |c| items.concat(itemsFromCommit(c)) }
|
389
|
+
|
390
|
+
release = Release.new(r.tag_name, r.name, sha, to, githubReleaseStatus(r), items)
|
391
|
+
|
392
|
+
result << release
|
393
|
+
}
|
394
|
+
return result
|
395
|
+
end
|
396
|
+
|
397
|
+
def getReleasesFromYAML
|
398
|
+
return YAML.load_file(Dir.pwd + ENV['YAML_BACKUP']) if File.file?(Dir.pwd + ENV['YAML_BACKUP'])
|
399
|
+
end
|
400
|
+
|
401
|
+
def saveReleasesToYAML(releases)
|
402
|
+
File.open(Dir.pwd + ENV['YAML_BACKUP'], 'w') { |file| file.write(releases.to_yaml) }
|
403
|
+
end
|
404
|
+
|
405
|
+
|
406
|
+
def initialize
|
407
|
+
|
408
|
+
super
|
409
|
+
@commit = CI.commit || Git.commit_sha
|
410
|
+
@repo = CI.repo || Git.repo
|
411
|
+
@build_no = CI.build_no
|
412
|
+
@branch = CI.branch || Git.branch
|
413
|
+
|
414
|
+
warning = 'Unable to determine repo branch' if @branch.nil?
|
415
|
+
warning = 'Unable to determine SHA1' if @commit.nil?
|
416
|
+
warning = 'Unable to determine repo' if @repo.nil?
|
417
|
+
warning = 'Unable to determine GITHUB_TOKEN' if ENV['GITHUB_TOKEN'].nil?
|
418
|
+
warning = 'Unable to determine TRAVIS_TOKEN' if ENV['TRAVIS_TOKEN'].nil?
|
419
|
+
warning = 'Unable to determine SPRINTLY_USER' if ENV['SPRINTLY_USER'].nil?
|
420
|
+
warning = 'Unable to determine SPRINTLY_API_KEY' if ENV['SPRINTLY_API_KEY'].nil?
|
421
|
+
|
422
|
+
@logger.error warning if warning
|
423
|
+
|
424
|
+
ENV['NO_OF_RELEASES'] = '2'
|
425
|
+
ENV['YAML_BACKUP'] = '/releases.yml'
|
426
|
+
ENV['TRAVIS_COMMIT_RANGE'] = nil if ENV['TRAVIS_COMMIT_RANGE'].nil?
|
427
|
+
|
428
|
+
@auth = {:username => ENV['SPRINTLY_USER'], :password => ENV['SPRINTLY_API_KEY']}
|
429
|
+
|
430
|
+
@repo = CI.repo || Git.repo
|
431
|
+
@branch = CI.branch || Git.branch
|
432
|
+
#TODO abstract Github via CI class
|
433
|
+
@github = Github.new(@repo, @branch)
|
434
|
+
|
435
|
+
@product_id = @github.sprintly_product_id
|
436
|
+
if !@product_id
|
437
|
+
@logger.error "Unable to retrieve Sprint.ly product_id for #{repo}"
|
438
|
+
exit 1
|
439
|
+
end
|
440
|
+
|
441
|
+
|
442
|
+
#@releases = getReleasesFromYAML
|
443
|
+
@releases = getReleasesFromServices if @releases.nil?
|
444
|
+
#saveReleasesToYAML @releases unless @releases.nil?
|
445
|
+
end
|
446
|
+
|
447
|
+
def display(audience='production')
|
448
|
+
displayReleases @releases unless @releases.nil?
|
449
|
+
end
|
450
|
+
|
451
|
+
def commit
|
452
|
+
@commit
|
453
|
+
end
|
454
|
+
end
|
455
|
+
end
|
data/lib/helpers/git.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require_relative 'string.rb'
|
2
|
+
|
3
|
+
class Git
|
4
|
+
def self.remote
|
5
|
+
begin
|
6
|
+
`git remote -v show`.lines.first.strip.match(/github\.com[\/|:](.+)\.git/)[1]
|
7
|
+
rescue
|
8
|
+
$stderr.puts 'Unable to retrieve slug from >> git remote -v show'
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
end
|
12
|
+
def self.repo
|
13
|
+
remote
|
14
|
+
end
|
15
|
+
def self.branch
|
16
|
+
`git rev-parse --abbrev-ref HEAD`.strip
|
17
|
+
end
|
18
|
+
def self.tag
|
19
|
+
(`git describe --tags --match 'v*' --abbrev=0 2>/dev/null` || 'HEAD').strip
|
20
|
+
end
|
21
|
+
def self.clean_tag(tag=self.tag)
|
22
|
+
tag.strip.sub('v','').split(/[\.,-]/).select { |e| e.is_number?}.first(3).join('.')
|
23
|
+
end
|
24
|
+
def self.commit_count
|
25
|
+
`git rev-list --count HEAD`.to_i
|
26
|
+
end
|
27
|
+
def self.commit_count_since_tag(tag)
|
28
|
+
`git rev-list --count ${tag}.. 2>/dev/null`.to_i
|
29
|
+
end
|
30
|
+
def self.installed?
|
31
|
+
system 'git --version >>/dev/null 2>&1'
|
32
|
+
end
|
33
|
+
def self.parse_deploy(commitMessage)
|
34
|
+
commands = %w(deploy DEPLOY force_deploy FORCE_DEPLOY).collect{ |x| "\\b#{x}\\b" }.join("|")
|
35
|
+
re = Regexp.new(/<(#{commands}):?\s*(.*?)(?:>)/)
|
36
|
+
deploy = commitMessage.scan(re).flatten.compact
|
37
|
+
return !deploy[0].nil? && !deploy[0].empty?, deploy[1] || ''
|
38
|
+
end
|
39
|
+
def self.parse_crashlytics(commitMessage)
|
40
|
+
return commitMessage.scan(/(c|C):(?<crashlytics>\d+)/).flatten.collect { |x| x.to_i }.uniq
|
41
|
+
end
|
42
|
+
def self.parse_sprintly(commitMessage)
|
43
|
+
commands = %w(close closed closes finish finished finishes fix fixed fixes breaks unfixes reopen reopens re-open re-opens addresses re ref references refs start starts see).collect{ |x| "\\b#{x}\\b" }.join("|")
|
44
|
+
prefixes = %w(task issue defect bug item ticket).collect{ |x| "\\b#{x}:\\b" }.join("|") + "|#"
|
45
|
+
re = Regexp.new(/(?:#{commands})\s(?:#{prefixes})(\d+)/)
|
46
|
+
return commitMessage.downcase.scan(re).flatten.collect{ |x| x.to_i }.uniq
|
47
|
+
end
|
48
|
+
def self.parse_commit_message(commitMessage)
|
49
|
+
h = Hash.new
|
50
|
+
deploy, message = self.parse_deploy(commitMessage)
|
51
|
+
sprintly_tickets = self.parse_sprintly(commitMessage)
|
52
|
+
crashlytics_ids = self.parse_crashlytics(commitMessage)
|
53
|
+
h["deploy?"] = deploy
|
54
|
+
h["message"] = message
|
55
|
+
h["sprintly"] = sprintly_tickets
|
56
|
+
h["crashlytics"] = crashlytics_ids
|
57
|
+
return h
|
58
|
+
end
|
59
|
+
def self.commit_sha
|
60
|
+
`git rev-parse HEAD`.strip
|
61
|
+
end
|
62
|
+
def self.commit_short_sha
|
63
|
+
`git rev-parse --short HEAD`.strip
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'api_cache'
|
2
|
+
require 'moneta'
|
3
|
+
require 'octokit'
|
4
|
+
#TODO - this is actually a github class
|
5
|
+
class Github
|
6
|
+
#TODO make this a singleton
|
7
|
+
#TODO add repo, and branch to cache keys
|
8
|
+
def initialize(repo, branch, github_token=ENV['GITHUB_TOKEN'])
|
9
|
+
#TODO: inherit from base class
|
10
|
+
@github_token = github_token
|
11
|
+
@repo = repo
|
12
|
+
@branch = branch
|
13
|
+
@logger = Logging.logger[self]
|
14
|
+
@logger.add_appenders \
|
15
|
+
Logging.appenders.stdout,
|
16
|
+
Logging.appenders.file('overview.log')
|
17
|
+
@logger.level = :info
|
18
|
+
|
19
|
+
APICache.store = Moneta.new(:YAML, :file => "#{self.class.name}_cache")
|
20
|
+
APICache.logger.level = Logger::INFO
|
21
|
+
#APICache.store = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def client
|
25
|
+
if !@client
|
26
|
+
@client = Octokit::Client.new(:access_token => @github_token)
|
27
|
+
@client.auto_paginate = true
|
28
|
+
@client.default_media_type = "application/vnd.github.moondragon+json"
|
29
|
+
@logger.info "Connected to Github" if @client
|
30
|
+
end
|
31
|
+
@client
|
32
|
+
end
|
33
|
+
|
34
|
+
def hooks
|
35
|
+
@hooks = client.hooks(@repo) unless @hooks
|
36
|
+
@logger.info "Retrieved hooks for #{@repo}" if @hooks
|
37
|
+
@hooks
|
38
|
+
end
|
39
|
+
|
40
|
+
def releases
|
41
|
+
#APICache.get("releases") do
|
42
|
+
client.releases(@repo)
|
43
|
+
#end
|
44
|
+
end
|
45
|
+
|
46
|
+
def sprintly_product_id
|
47
|
+
#TODO this could also return a CLI flag or env variable
|
48
|
+
APICache.get("sprintly_product_id_#{@repo}", :fail => [], :timeout => 30) do
|
49
|
+
@logger.info "Not using cache"
|
50
|
+
product_id = hooks.map { |h| h.config.product_id }.compact.first
|
51
|
+
@logger.info "Sprint.ly product_id = #{product_id}" if product_id
|
52
|
+
product_id
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def ref(tag)
|
57
|
+
#APICache.get("ref_#{tag}") do
|
58
|
+
client.ref(@repo, "tags/" + tag)
|
59
|
+
#end
|
60
|
+
end
|
61
|
+
|
62
|
+
def commit(sha)
|
63
|
+
#APICache.get("commit_#{sha}") do
|
64
|
+
client.commit(@repo, sha)
|
65
|
+
#end
|
66
|
+
end
|
67
|
+
|
68
|
+
def commits_since(since)
|
69
|
+
#APICache.get("commit_since_#{since}") do
|
70
|
+
client.commits_since(@repo, since, @branch)
|
71
|
+
#end
|
72
|
+
end
|
73
|
+
|
74
|
+
def commits_between(from,to)
|
75
|
+
|
76
|
+
end
|
77
|
+
def commits_before(to)
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
File without changes
|
data/lib/overview/version.rb
CHANGED
data/lib/overview.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
require_relative 'overview/version.rb'
|
2
|
-
require_relative '
|
3
|
-
require_relative '
|
4
|
-
require_relative '
|
5
|
-
require_relative 'appversion
|
6
|
-
|
2
|
+
require_relative 'helpers/git.rb'
|
3
|
+
require_relative 'helpers/ci.rb'
|
4
|
+
require_relative 'helpers/string.rb'
|
5
|
+
require_relative 'appversion.rb'
|
6
|
+
require_relative 'changelog.rb'
|
7
7
|
|
8
8
|
# Add requires for other files you add to your project here, so
|
9
9
|
# you just need to require this one file in your bin file
|
data/overview.gemspec
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Ensure we require the local version and not one we might have installed already
|
2
|
-
require File.join([File.dirname(__FILE__),'lib
|
2
|
+
require File.join([File.dirname(__FILE__),'lib/overview','version.rb'])
|
3
3
|
|
4
4
|
spec = Gem::Specification.new do |s|
|
5
5
|
s.name = 'overview'
|
@@ -8,7 +8,7 @@ spec = Gem::Specification.new do |s|
|
|
8
8
|
s.email = 'jono@overllc.com'
|
9
9
|
s.homepage = 'http://www.overllc.com'
|
10
10
|
s.platform = Gem::Platform::RUBY
|
11
|
-
s.summary = "Over's custom CI / CD toolchain, integrating Sprint.ly, Github,
|
11
|
+
s.summary = "Over's custom CI / CD toolchain, integrating Sprint.ly, Github, Github and ITC"
|
12
12
|
s.files = `git ls-files`.split("
|
13
13
|
")
|
14
14
|
s.require_paths << 'lib'
|
@@ -26,5 +26,10 @@ spec = Gem::Specification.new do |s|
|
|
26
26
|
s.add_development_dependency('gem-release')
|
27
27
|
s.add_runtime_dependency('gli','2.13.0')
|
28
28
|
s.add_runtime_dependency('octokit','3.8.0')
|
29
|
-
|
29
|
+
s.add_runtime_dependency('httparty')
|
30
|
+
s.add_runtime_dependency('formatador')
|
31
|
+
s.add_runtime_dependency('moneta')
|
32
|
+
s.add_runtime_dependency('api_cache')
|
33
|
+
s.add_runtime_dependency('logging')
|
34
|
+
s.add_runtime_dependency('faraday-http-cache')
|
30
35
|
end
|