capistrano-changelog 0.3.0 → 0.4.0
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 +7 -0
- data/.rspec +1 -1
- data/Gemfile +5 -4
- data/README.md +10 -5
- data/capistrano-changelog.gemspec +8 -9
- data/lib/capistrano-changelog.rb +16 -22
- data/lib/capistrano/changelog.rb +7 -0
- data/lib/capistrano/v2/hooks.rb +12 -0
- data/lib/capistrano/v2/recipes.rb +47 -0
- data/lib/capistrano/v3/tasks/changelog.rake +22 -0
- data/lib/changelog/git.rb +72 -0
- data/lib/changelog/history.rb +86 -0
- data/lib/changelog/release.rb +47 -0
- data/lib/changelog/stories_tracker.rb +39 -0
- data/lib/changelog/story.rb +29 -0
- data/lib/changelog/trackers/pivotal.rb +54 -0
- data/lib/{capistrano-changelog/release.rb → changelog/version.rb} +4 -3
- data/spec/lib/capistrano-changelog/git_spec.rb +117 -0
- data/spec/lib/capistrano-changelog/history_spec.rb +49 -0
- data/spec/lib/capistrano-changelog/release_spec.rb +65 -0
- data/spec/lib/capistrano-changelog/version_spec.rb +44 -0
- data/spec/spec_helper.rb +1 -1
- data/templates/changelog.erb +27 -11
- metadata +48 -74
- data/lib/capistrano-changelog/capistrno.rb +0 -15
- data/lib/capistrano-changelog/change_log.rb +0 -22
- data/lib/capistrano-changelog/git/lib.rb +0 -10
- data/lib/capistrano-changelog/git/tags_list.rb +0 -29
- data/lib/capistrano-changelog/pivotal/api.rb +0 -44
- data/lib/capistrano-changelog/pivotal/story.rb +0 -37
- data/lib/capistrano-changelog/version.rb +0 -3
- data/lib/capistrano-changelog/wrappers/changelog.rb +0 -21
- data/lib/capistrano-changelog/wrappers/release.rb +0 -35
- data/spec/lib/capistrano-changelog/wrappers/changelog_rspec.rb +0 -16
- data/spec/lib/capistrano-changelog/wrappers/release_spec.rb +0 -29
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9e9bfe6dc308fc927af68f6242733ae2024bb0b0
|
4
|
+
data.tar.gz: d9ec16816df93500be675e00816ca8f12278aa29
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d5f7c87c3c499d56755288df88e9db883defe07df54e290483eca93f5f417ef1e7a8c58db2d24662b856ade729e44187835bb71118f67cbf654b13f8757bb03d
|
7
|
+
data.tar.gz: 169536f050e9bc9529d34919aa7946dc6c601fa48d9e7348d5dbe4e25603b147d783a6ec02181432ca401ad044f51ae4777516af050313a464b19885fc4f3a5c
|
data/.rspec
CHANGED
data/Gemfile
CHANGED
@@ -3,10 +3,11 @@ source 'https://rubygems.org'
|
|
3
3
|
gemspec
|
4
4
|
|
5
5
|
gem 'rake'
|
6
|
-
gem 'rspec'
|
7
|
-
gem 'vcr', '~> 2.7'
|
6
|
+
gem 'rspec', '~> 3.0'
|
8
7
|
gem 'webmock'
|
8
|
+
gem 'nokogiri'
|
9
9
|
|
10
10
|
gem 'git'
|
11
|
-
gem 'faraday', '~> 0.
|
12
|
-
gem '
|
11
|
+
gem 'faraday', '~> 0.9'
|
12
|
+
gem 'faraday_middleware', '~> 0.9'
|
13
|
+
gem 'faraday-http-cache'
|
data/README.md
CHANGED
@@ -20,15 +20,20 @@ Or install it yourself as:
|
|
20
20
|
|
21
21
|
$ gem install capistrano-changelog
|
22
22
|
|
23
|
+
Put into deploy.rb
|
24
|
+
|
25
|
+
require 'capistrano/changelog'
|
26
|
+
|
27
|
+
|
23
28
|
|
24
29
|
## Usage
|
25
30
|
|
26
|
-
|
31
|
+
Use following settings at deploy.rb
|
32
|
+
|
33
|
+
role :changelog, %w{ host.name }, cache: true, version: true
|
27
34
|
|
28
|
-
set :
|
29
|
-
set :
|
30
|
-
set :changelog_output, File.join(release_path, 'public', 'changelog.html')
|
31
|
-
set :changelog_cache, '/tmp/pivotal' # does tickets caching if caching path specified, default: false
|
35
|
+
set :changelog, File.join(current_path, 'public', 'changelog.html')
|
36
|
+
set :changelog_version, File.join(current_path, 'public', 'version.json')
|
32
37
|
|
33
38
|
Hooks
|
34
39
|
|
@@ -1,11 +1,11 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require '
|
4
|
+
require 'changelog/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "capistrano-changelog"
|
8
|
-
spec.version =
|
8
|
+
spec.version = "0.4.0"
|
9
9
|
spec.authors = ["Dmitry Larkin"]
|
10
10
|
spec.email = ["dmitry.larkin@gmail.com"]
|
11
11
|
spec.description = %q{Uses git commits to recognize tracker stories and generates ChangeLog.}
|
@@ -18,12 +18,11 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^spec/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_development_dependency
|
22
|
-
spec.add_development_dependency
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
22
|
+
spec.add_development_dependency 'rake'
|
23
23
|
|
24
|
-
spec.add_runtime_dependency
|
25
|
-
spec.add_runtime_dependency
|
26
|
-
spec.add_runtime_dependency
|
27
|
-
spec.add_runtime_dependency
|
28
|
-
spec.add_runtime_dependency "capistrano", "< 3.0"
|
24
|
+
spec.add_runtime_dependency 'oj'
|
25
|
+
spec.add_runtime_dependency 'capistrano', '< 3.0'
|
26
|
+
spec.add_runtime_dependency 'faraday', '~> 0.9'
|
27
|
+
spec.add_runtime_dependency 'faraday-http-cache', '~> 0.4'
|
29
28
|
end
|
data/lib/capistrano-changelog.rb
CHANGED
@@ -1,29 +1,21 @@
|
|
1
|
-
require "capistrano-changelog/version"
|
2
|
-
require "git"
|
3
|
-
|
4
|
-
|
5
1
|
module CapistranoChangelog
|
6
2
|
class GeneralError < StandardError; end
|
7
3
|
|
8
|
-
module
|
9
|
-
class NetworkError < StandardError; end
|
4
|
+
module Trackers
|
5
|
+
class Trackers::NetworkError < StandardError; end
|
6
|
+
class Trackers::ConfigError < StandardError; end
|
7
|
+
class Trackers::AccessDenied < StandardError; end
|
8
|
+
class Trackers::BatchError < StandardError; end
|
10
9
|
|
11
|
-
autoload :
|
12
|
-
autoload :API, "capistrano-changelog/pivotal/api"
|
10
|
+
autoload :Pivotal, "changelog/trackers/pivotal"
|
13
11
|
end
|
14
12
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
autoload :Release, "capistrano-changelog/wrappers/release"
|
22
|
-
end
|
23
|
-
|
24
|
-
autoload :Release, "capistrano-changelog/release"
|
25
|
-
autoload :ChangeLog, "capistrano-changelog/change_log"
|
26
|
-
autoload :TagList, "capistrano-changelog/git/tags_list"
|
13
|
+
autoload :Version, "changelog/version"
|
14
|
+
autoload :Release, "changelog/release"
|
15
|
+
autoload :Story, "changelog/story"
|
16
|
+
autoload :History, "changelog/history"
|
17
|
+
autoload :Git, "changelog/git"
|
18
|
+
autoload :StoriesTracker, "changelog/stories_tracker"
|
27
19
|
|
28
20
|
def self.root
|
29
21
|
File.expand_path '../..', __FILE__
|
@@ -32,6 +24,8 @@ module CapistranoChangelog
|
|
32
24
|
def self.templates
|
33
25
|
File.join root, 'templates'
|
34
26
|
end
|
35
|
-
end
|
36
27
|
|
37
|
-
|
28
|
+
def self.pivotal_tracker
|
29
|
+
ENV['PIVOTAL_TOKEN'] or raise CapistranoChangelog::GeneralError, "Pivotal Tracker access token missed. Run $ export PIVOTAL_TOKEN='you-access-token'."
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require "capistrano/v2/recipes"
|
2
|
+
|
3
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
4
|
+
# Generate ChangeLog file
|
5
|
+
after "deploy:create_symlink", "deploy:changelog"
|
6
|
+
|
7
|
+
# Generage version.json file
|
8
|
+
after "deploy:create_symlink", "deploy:changelog:version"
|
9
|
+
|
10
|
+
before "deploy:changelog", "deploy:changelog:download_cache"
|
11
|
+
after "deploy:changelog", "deploy:changelog:upload_cache"
|
12
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'capistrano-changelog'
|
2
|
+
|
3
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
4
|
+
# CapistranoChangelog.load_into(self)
|
5
|
+
|
6
|
+
_cset(:changelog_tracker) { :pivotal }
|
7
|
+
_cset(:changelog_cache_local_file) { 'tmp/changelog.json' }
|
8
|
+
_cset(:changelog_cache_remote_file) { File.join(shared_path, 'changelog.json') }
|
9
|
+
_cset(:changelog) { File.join(shared_path, 'changelog.html') }
|
10
|
+
_cset(:changelog_version) { File.join(shared_path, 'version.json') }
|
11
|
+
_cset(:changelog_date_format) { '%I:%M%P %D UTC' }
|
12
|
+
|
13
|
+
namespace :deploy do
|
14
|
+
namespace :changelog do
|
15
|
+
task :download_cache, roles: :changelog, only: {cache: true}, on_no_matching_servers: :continue do
|
16
|
+
begin
|
17
|
+
get fetch(:changelog_cache_remote_file), fetch(:changelog_cache_local_file)
|
18
|
+
rescue Capistrano::TransferError => e
|
19
|
+
logger.info "Changelog cache file does not exists."
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
task :upload_cache, roles: :changelog, only: {cache: true}, on_no_matching_servers: :continue do
|
24
|
+
put IO.read(fetch(:changelog_cache_local_file)), fetch(:changelog_cache_remote_file)
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Generates changelog"
|
28
|
+
task :default, roles: :changelog do
|
29
|
+
begin
|
30
|
+
history = CapistranoChangelog::History.new({
|
31
|
+
changelog_cache_local_file: fetch(:changelog_cache_local_file),
|
32
|
+
logger: logger
|
33
|
+
})
|
34
|
+
rescue CapistranoChangelog::GeneralError => e
|
35
|
+
logger.error e.message
|
36
|
+
end
|
37
|
+
|
38
|
+
put history.generate, fetch(:changelog)
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "Generates version file. RESTART=(true|false) option could be specified (by default is true)"
|
42
|
+
task :version, roles: :changelog, only: {version: true}, on_no_matching_servers: :continue do
|
43
|
+
put CapistranoChangelog::Version.generate, fetch(:changelog_version), mkdir: true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
namespace :deploy do
|
2
|
+
desc "Generates changelog"
|
3
|
+
task :changelog do
|
4
|
+
raise 'Capistrano 3.x does not supported yet'
|
5
|
+
end
|
6
|
+
|
7
|
+
desc "Generates version file. RESTART=(true|false) option could be specified (by default is true)"
|
8
|
+
task :version do
|
9
|
+
raise 'Capistrano 3.x does not supported yet'
|
10
|
+
end
|
11
|
+
|
12
|
+
before "deploy:finalize_update", "deploy:changelog"
|
13
|
+
before "deploy:finalize_update", "deploy:version"
|
14
|
+
end
|
15
|
+
|
16
|
+
namespace :load do
|
17
|
+
task :defaults do
|
18
|
+
set(:changelog_tracker) ->{ :pivotal }
|
19
|
+
set(:changelog_cache) ->{ false }
|
20
|
+
set(:changelog) ->{ File.join(release_path, 'public', 'changelog.html') }
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'csv'
|
2
|
+
|
3
|
+
module CapistranoChangelog
|
4
|
+
module Git
|
5
|
+
extend self
|
6
|
+
|
7
|
+
class Commit < OpenStruct
|
8
|
+
def date
|
9
|
+
Time.parse(self.datetime).utc
|
10
|
+
rescue TypeError => e
|
11
|
+
nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def <=> (other)
|
15
|
+
self.date <=> other.date
|
16
|
+
end
|
17
|
+
|
18
|
+
def short
|
19
|
+
self.commit.slice(0,7)
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
self.comment
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
CMD_VERSION = "describe --always"
|
28
|
+
CMD_FIRST_COMMIT = "rev-list --max-parents=0 HEAD"
|
29
|
+
|
30
|
+
CMD_TAGS = "for-each-ref --sort='*authordate' --format='%(objectname)%09%(*authordate:iso8601)%09%(tag)' refs/tags"
|
31
|
+
CMD_TAGS_HEADERS = [ :commit, :datetime, :comment ]
|
32
|
+
|
33
|
+
CMD_LOG = "log --date=iso8601 --pretty=format:'%H%x09%cd%x09%s' --no-merges "
|
34
|
+
CMD_LOG_HEADERS = [ :commit, :datetime, :comment ]
|
35
|
+
|
36
|
+
def describe
|
37
|
+
run(CMD_VERSION)
|
38
|
+
end
|
39
|
+
|
40
|
+
def first_commit
|
41
|
+
csv_to_commits(run(CMD_LOG + run(CMD_FIRST_COMMIT)), CMD_LOG_HEADERS).first
|
42
|
+
end
|
43
|
+
|
44
|
+
def last_commit
|
45
|
+
csv_to_commits(run(CMD_LOG + 'HEAD^..HEAD'), CMD_LOG_HEADERS).first.tap{ |c| c.comment = 'HEAD' }
|
46
|
+
end
|
47
|
+
|
48
|
+
def releases
|
49
|
+
csv_to_commits(run(CMD_TAGS), CMD_TAGS_HEADERS)
|
50
|
+
end
|
51
|
+
|
52
|
+
def tags
|
53
|
+
releases.push(last_commit)
|
54
|
+
end
|
55
|
+
|
56
|
+
def commits(predecessor, successor)
|
57
|
+
return Array.new if (successor <=> predecessor) < 1
|
58
|
+
|
59
|
+
csv_to_commits run(CMD_LOG + " %s..%s" % [predecessor.commit, successor.commit]), CMD_LOG_HEADERS
|
60
|
+
end
|
61
|
+
|
62
|
+
def run(cmd)
|
63
|
+
(`git #{cmd}`).strip
|
64
|
+
end
|
65
|
+
|
66
|
+
def csv_to_commits(csv, headers)
|
67
|
+
CSV.parse(csv.gsub(/\"/, ''), col_sep: "\t").to_a.map do |a|
|
68
|
+
Commit.new Hash[ headers.zip(a) ]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'oj'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
module CapistranoChangelog
|
7
|
+
class Context < OpenStruct
|
8
|
+
def format(dt)
|
9
|
+
dt.strftime('%I:%M%P %D UTC') if dt.kind_of?(Time)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class History
|
14
|
+
attr_reader :releases, :cache, :options
|
15
|
+
|
16
|
+
def initialize(params = {})
|
17
|
+
@first_commit = CapistranoChangelog::Git.first_commit
|
18
|
+
@last_commit = CapistranoChangelog::Git.last_commit
|
19
|
+
@releases = CapistranoChangelog::Release.all
|
20
|
+
@tracker = CapistranoChangelog::StoriesTracker.new
|
21
|
+
@cache = {}
|
22
|
+
@options = {
|
23
|
+
changelog_cache_local_file: nil,
|
24
|
+
changelog_cache: false,
|
25
|
+
logger: false
|
26
|
+
}.merge(params)
|
27
|
+
end
|
28
|
+
|
29
|
+
def store_cache(filename)
|
30
|
+
return unless filename
|
31
|
+
|
32
|
+
dir = File.dirname(filename)
|
33
|
+
FileUtils.mkdir_p(dir) unless File.exists?(dir)
|
34
|
+
File.open(filename, 'w+') do |f|
|
35
|
+
f << Oj.dump(cache)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def read_cache(filename)
|
40
|
+
return unless filename and File.exists?(filename)
|
41
|
+
|
42
|
+
@cache = ( Oj.load(IO.read(filename)) || {} ) if File.exists?(filename)
|
43
|
+
rescue Oj::ParseError => e
|
44
|
+
File.unlink(filename) if File.exists?(filename)
|
45
|
+
|
46
|
+
return {}
|
47
|
+
end
|
48
|
+
|
49
|
+
def udpate_cache
|
50
|
+
read_cache options.fetch(:changelog_cache_local_file)
|
51
|
+
|
52
|
+
@tracker.fetch CapistranoChangelog::Git.commits(@first_commit, @last_commit), cache, options
|
53
|
+
|
54
|
+
store_cache options.fetch(:changelog_cache_local_file)
|
55
|
+
end
|
56
|
+
|
57
|
+
def next_to(release)
|
58
|
+
if idx = releases.index(release) and releases[idx + 1]
|
59
|
+
releases[idx + 1]
|
60
|
+
else
|
61
|
+
@last_commit
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def prev_to(release)
|
66
|
+
if idx = releases.index(release) and idx > 0 and releases[idx - 1]
|
67
|
+
releases[idx - 1]
|
68
|
+
else
|
69
|
+
@first_commit
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def generate
|
74
|
+
udpate_cache
|
75
|
+
|
76
|
+
context = Context.new({
|
77
|
+
date: Time.new.utc,
|
78
|
+
history: self,
|
79
|
+
releases: releases.reverse
|
80
|
+
})
|
81
|
+
|
82
|
+
template = ERB.new IO.read(File.join(CapistranoChangelog.templates, 'changelog.erb'))
|
83
|
+
template.result context.instance_eval { binding }
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
module CapistranoChangelog
|
4
|
+
class Release
|
5
|
+
attr_reader :tag
|
6
|
+
|
7
|
+
def initialize(tag)
|
8
|
+
@tag = tag
|
9
|
+
end
|
10
|
+
|
11
|
+
def <=> (other)
|
12
|
+
self.date <=> other.date
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(meth, *args)
|
16
|
+
if tag.respond_to?(meth)
|
17
|
+
tag.send(meth, *args)
|
18
|
+
else
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def stories(history)
|
24
|
+
commits = CapistranoChangelog::Git.commits history.prev_to(self), self
|
25
|
+
|
26
|
+
CapistranoChangelog::StoriesTracker.new.ids(commits).map do |uid|
|
27
|
+
CapistranoChangelog::Story.new(uid, history, commits)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def unreconized(history)
|
32
|
+
CapistranoChangelog::Git.commits(history.prev_to(self), self).select do |commit|
|
33
|
+
commit.comment !~ Trackers::Pivotal::MATCHER
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def unreconized?(history)
|
38
|
+
unreconized(history).size > 0
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.all
|
42
|
+
CapistranoChangelog::Git.tags.map do |tag|
|
43
|
+
CapistranoChangelog::Release.new(tag)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|