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
@@ -0,0 +1,39 @@
|
|
1
|
+
module CapistranoChangelog
|
2
|
+
class StoriesTracker
|
3
|
+
attr_reader :stories
|
4
|
+
|
5
|
+
def fetch(commits, cache, options)
|
6
|
+
ids_to_fetch = ids(commits) - cache.keys
|
7
|
+
|
8
|
+
return if ids_to_fetch.empty?
|
9
|
+
|
10
|
+
log = ->(msg){ options[:logger].send(:info, msg) if options[:logger] }
|
11
|
+
|
12
|
+
chunks = ids_to_fetch.each_slice(100).to_a.map do |chunk|
|
13
|
+
begin
|
14
|
+
log.call("Changelog: fetching chunk of stories: #{chunk.size}")
|
15
|
+
source.export(chunk)
|
16
|
+
rescue Trackers::BatchError => e
|
17
|
+
log.call("Changelog: got an export error")
|
18
|
+
log.call(e.message)
|
19
|
+
|
20
|
+
chunk.map do |uid|
|
21
|
+
log.call("Changelog: Fetching single story: #{uid}")
|
22
|
+
source.get(uid)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
cache.merge! Hash[chunks.inject(:+)]
|
28
|
+
end
|
29
|
+
|
30
|
+
def source
|
31
|
+
@source ||= Trackers::Pivotal.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def ids(commits)
|
35
|
+
@ids ||= commits.map{ |commit| commit.comment.scan(Trackers::Pivotal::MATCHER) }.flatten.compact.map(&:to_i).uniq
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module CapistranoChangelog
|
2
|
+
Story = Struct.new(:uid, :history, :commits_collection) do
|
3
|
+
def title
|
4
|
+
history.cache.fetch(uid, nil) || history.cache.fetch(uid.to_s, nil)
|
5
|
+
end
|
6
|
+
|
7
|
+
def error
|
8
|
+
'Story has no description'
|
9
|
+
end
|
10
|
+
|
11
|
+
def valid?
|
12
|
+
history.cache.has_key?(uid) || history.cache.has_key?(uid.to_s)
|
13
|
+
end
|
14
|
+
|
15
|
+
def commits
|
16
|
+
commits_collection.select do |commit|
|
17
|
+
commit.comment =~ (/\##{uid}/)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def url
|
22
|
+
"https://www.pivotaltracker.com/story/show/#{uid}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
"#{uid}: #{title}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'oj'
|
2
|
+
require 'faraday'
|
3
|
+
require 'faraday_middleware'
|
4
|
+
|
5
|
+
module CapistranoChangelog
|
6
|
+
module Trackers
|
7
|
+
class Pivotal
|
8
|
+
|
9
|
+
MATCHER = /\#(\d+)/
|
10
|
+
|
11
|
+
def connection
|
12
|
+
@connection ||= Faraday.new 'https://www.pivotaltracker.com/services/v5/' do |faraday|
|
13
|
+
faraday.request :json
|
14
|
+
faraday.response :json, :content_type => /\bjson$/
|
15
|
+
faraday.adapter Faraday.default_adapter
|
16
|
+
faraday.headers['X-TrackerToken'] = CapistranoChangelog.pivotal_tracker
|
17
|
+
faraday.options[:timeout] = 30
|
18
|
+
faraday.options[:open_timeout] = 30
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def export(ids)
|
23
|
+
response = connection.post do |opts|
|
24
|
+
opts.url 'stories/export'
|
25
|
+
opts.headers['Content-Type'] = 'application/json'
|
26
|
+
opts.body = Oj.dump({"ids" => ids})
|
27
|
+
end
|
28
|
+
|
29
|
+
unless response.status == 200
|
30
|
+
raise Trackers::BatchError, response.body.inspect
|
31
|
+
else
|
32
|
+
CSV.parse(response.body, headers: :first_row, skip_blanks: true).map do |row|
|
33
|
+
row.values_at(0, 1)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def get(uid)
|
39
|
+
response = connection.get("stories/#{uid}")
|
40
|
+
|
41
|
+
unless response.status == 200
|
42
|
+
raise Trackers::AccessDenied
|
43
|
+
else
|
44
|
+
response.body.values_at(*%w{ id name })
|
45
|
+
end
|
46
|
+
rescue Trackers::AccessDenied => e
|
47
|
+
[ uid, false ]
|
48
|
+
rescue Faraday::TimeoutError => e
|
49
|
+
sleep(1) and retry
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -1,11 +1,12 @@
|
|
1
|
-
require '
|
1
|
+
require 'json'
|
2
|
+
require 'changelog/git'
|
2
3
|
|
3
4
|
module CapistranoChangelog
|
4
|
-
class
|
5
|
+
class Version
|
5
6
|
def self.generate
|
6
7
|
JSON.dump({
|
7
8
|
restart: ( ENV.fetch('RESTART', 'true') == 'true' ),
|
8
|
-
version:
|
9
|
+
version: CapistranoChangelog::Git.describe,
|
9
10
|
ts: Time.now.to_i
|
10
11
|
})
|
11
12
|
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe CapistranoChangelog::Git do
|
4
|
+
|
5
|
+
describe ".describe" do
|
6
|
+
let(:release) { "v1.2.3-4-g55443322" }
|
7
|
+
|
8
|
+
it "returns last release string" do
|
9
|
+
allow(described_class).to receive(:run).with(described_class::CMD_VERSION).and_return(release)
|
10
|
+
expect(described_class.describe).to eq(release)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe ".first_commit" do
|
15
|
+
let(:first_commit) { 'e5eed9f975008ea59ec5d3b47047088949f6fb41' }
|
16
|
+
let(:git_log) do
|
17
|
+
output =<<OUTPUT
|
18
|
+
e5eed9f975008ea59ec5d3b47047088949f6fb41\t2013-11-22 13:11:48 +0200\tinitial commit
|
19
|
+
OUTPUT
|
20
|
+
end
|
21
|
+
|
22
|
+
it "returns first commit object" do
|
23
|
+
allow(described_class).to receive(:run).with(described_class::CMD_FIRST_COMMIT).and_return(first_commit)
|
24
|
+
allow(described_class).to receive(:run).with(described_class::CMD_LOG + first_commit).and_return(git_log)
|
25
|
+
expect(described_class.first_commit.commit).to eq(first_commit)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe ".last_commit" do
|
30
|
+
let(:last_commit) { '8bbeaac2b114228d27f8464fe2cd2df15adeb8ce' }
|
31
|
+
let(:git_log) do
|
32
|
+
output =<<OUTPUT
|
33
|
+
8bbeaac2b114228d27f8464fe2cd2df15adeb8ce\t2013-11-22 13:11:48 +0200\tinitial commit
|
34
|
+
OUTPUT
|
35
|
+
end
|
36
|
+
|
37
|
+
it "returns first commit object" do
|
38
|
+
allow(described_class).to receive(:run).with(described_class::CMD_LOG + 'HEAD^..HEAD').and_return(git_log)
|
39
|
+
|
40
|
+
expect(described_class.last_commit.commit).to eq(last_commit)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe ".releases" do
|
45
|
+
let(:full_output) do
|
46
|
+
output =<<OUTPUT
|
47
|
+
029bb8a07f62d4579b8265cb10e5ab01b8bed880\t2013-11-20 10:10:26 +0200\tv0.0.1
|
48
|
+
bbc430a085ebff05bf41878336aa688303415f60\t2013-11-20 11:04:37 +0200\tv0.0.2
|
49
|
+
9c909837862ca121189003a7eec6817a646b2163\t2013-11-20 11:07:47 +0200\tv0.1.0
|
50
|
+
8bbeaac2b114228d27f8464fe2cd2df15adeb8ce\t2013-11-20 12:11:21 +0200\tv0.1.1
|
51
|
+
3904d563f7df1332d84606e9b2bf9071ca5552fa\t2013-11-21 14:46:33 +0200\tv0.2.0
|
52
|
+
5efb66c1f863cb0726e013be4f0d015dafdaa4e1\t2013-11-21 15:59:36 +0200\tv0.2.1
|
53
|
+
fb5bffcad9d5809d9675687c1c608173db9a1589\t2013-11-22 13:11:48 +0200\tv0.2.2
|
54
|
+
25fafb66002aef1ba1b3a18015cd3880c1e36694\t2013-11-25 15:35:21 +0200\tv0.3.0
|
55
|
+
OUTPUT
|
56
|
+
end
|
57
|
+
|
58
|
+
let(:first_tag) { described_class.releases.first }
|
59
|
+
|
60
|
+
it "returns array of Commit objects" do
|
61
|
+
allow(described_class).to receive(:run).with(described_class::CMD_TAGS).and_return(full_output)
|
62
|
+
|
63
|
+
expect(first_tag).to be_a(CapistranoChangelog::Git::Commit)
|
64
|
+
expect(first_tag.commit).to eq('029bb8a07f62d4579b8265cb10e5ab01b8bed880')
|
65
|
+
expect(first_tag.comment).to eq('v0.0.1')
|
66
|
+
end
|
67
|
+
|
68
|
+
it "returns empty array if no tags were created" do
|
69
|
+
allow(described_class).to receive(:run).with(described_class::CMD_TAGS).and_return("")
|
70
|
+
expect(described_class.releases).to be_empty
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe ".commits" do
|
75
|
+
let(:git_log) do
|
76
|
+
output =<<OUTPUT
|
77
|
+
e5eed9f975008ea59ec5d3b47047088949f6fb41\t2013-11-22 13:11:48 +0200\tupdated html formatting
|
78
|
+
d4c120c3ecc6abe259489b6ea6a027234422babf\t2013-11-21 15:59:36 +0200\ttime for tags
|
79
|
+
af94b7cac32054f55d041db3359acb23ccb920aa\t2013-11-21 14:46:33 +0200\tchangelog
|
80
|
+
OUTPUT
|
81
|
+
end
|
82
|
+
|
83
|
+
let(:initial_commit) { }
|
84
|
+
let(:head) { }
|
85
|
+
|
86
|
+
let(:first_tag) { CapistranoChangelog::Git::Commit.new({
|
87
|
+
commit: '8bbeaac2b114228d27f8464fe2cd2df15adeb8ce',
|
88
|
+
datetime: '2013-11-20 12:11:21 +0200',
|
89
|
+
title: 'v0.1.1'
|
90
|
+
}) }
|
91
|
+
|
92
|
+
let(:second_tag) { CapistranoChangelog::Git::Commit.new({
|
93
|
+
commit: 'fb5bffcad9d5809d9675687c1c608173db9a1589',
|
94
|
+
datetime: '2013-11-22 13:11:48 +0200',
|
95
|
+
title: 'v0.2.2'
|
96
|
+
}) }
|
97
|
+
|
98
|
+
let(:predecessor) { CapistranoChangelog::Release.new(first_tag) }
|
99
|
+
let(:successor) { CapistranoChangelog::Release.new(second_tag) }
|
100
|
+
|
101
|
+
it "retrieves list between two commits" do
|
102
|
+
allow(described_class).to receive(:run).and_return(git_log)
|
103
|
+
expect(described_class.commits(predecessor, successor)).to satisfy { |collection| collection.size == 3 }
|
104
|
+
end
|
105
|
+
|
106
|
+
it "retrieves empty list between the same commit" do
|
107
|
+
expect(described_class).to_not receive(:run)
|
108
|
+
expect(described_class.commits(predecessor, predecessor)).to be_empty
|
109
|
+
end
|
110
|
+
|
111
|
+
it "retrieves empty list between wrong sequenve of commits" do
|
112
|
+
expect(described_class).to_not receive(:run)
|
113
|
+
expect(described_class.commits(successor, predecessor)).to be_empty
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe CapistranoChangelog::History do
|
4
|
+
|
5
|
+
let(:initial) { CapistranoChangelog::Git::Commit.new(commit: 'theveryfirstcommit', datetime: (Time.now - 30).to_s, comment: '1') }
|
6
|
+
let(:first) { CapistranoChangelog::Git::Commit.new(commit: 'firstcommit123', datetime: (Time.now - 10).to_s, comment: '2') }
|
7
|
+
let(:second) { CapistranoChangelog::Git::Commit.new(commit: 'secondcommit231', datetime: (Time.now ).to_s, comment: '3') }
|
8
|
+
let(:third) { CapistranoChangelog::Git::Commit.new(commit: 'thirdcommit321', datetime: (Time.now + 10).to_s, comment: '4') }
|
9
|
+
let(:head) { CapistranoChangelog::Git::Commit.new(commit: 'theverylastcommit', datetime: (Time.now + 30).to_s, comment: '5') }
|
10
|
+
let(:collection) { [first, second, third] }
|
11
|
+
|
12
|
+
before do
|
13
|
+
allow(CapistranoChangelog::Git).to receive(:last_commit).and_return(head)
|
14
|
+
allow(CapistranoChangelog::Git).to receive(:first_commit).and_return(initial)
|
15
|
+
end
|
16
|
+
|
17
|
+
subject { described_class.new }
|
18
|
+
|
19
|
+
describe ".next_to" do
|
20
|
+
it "have to return next release from history if exists" do
|
21
|
+
allow(CapistranoChangelog::Release).to receive(:all).and_return(collection)
|
22
|
+
expect(subject.next_to(first)).to eq(second)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "have to return HEAD if next release does not exists" do
|
26
|
+
allow(CapistranoChangelog::Release).to receive(:all).and_return(collection)
|
27
|
+
expect(subject.next_to(third).commit).to eq('theverylastcommit')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe ".prev_to" do
|
32
|
+
it "have to return prev release from history if exists" do
|
33
|
+
allow(CapistranoChangelog::Release).to receive(:all).and_return(collection)
|
34
|
+
expect(subject.prev_to(second)).to eq(first)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "have to return FIRST if prev release does not exists" do
|
38
|
+
allow(CapistranoChangelog::Release).to receive(:all).and_return(collection)
|
39
|
+
expect(subject.prev_to(first).commit).to eq(initial.commit)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe ".generate", skip: true do
|
44
|
+
it "generates HTML formated changelog" do
|
45
|
+
expect{ Nokogiri::HTML(described_class.new.generate) }.not_to raise_error
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe CapistranoChangelog::Release do
|
4
|
+
|
5
|
+
let(:first_tag) { CapistranoChangelog::Git::Commit.new commit: 'aabbcczz1', title: 'v0.0.1', datetime: '2013-11-21 10:10:10 +0200' }
|
6
|
+
let(:second_tag) { CapistranoChangelog::Git::Commit.new commit: 'aabbcczz2', title: 'v0.0.2', datetime: '2013-11-22 10:10:10 +0200' }
|
7
|
+
let(:last_tag) { CapistranoChangelog::Git::Commit.new commit: 'aabbcczz3', title: 'v0.0.3', datetime: '2013-11-23 10:10:10 +0200' }
|
8
|
+
|
9
|
+
context "object properties" do
|
10
|
+
|
11
|
+
let(:tag) { first_tag }
|
12
|
+
|
13
|
+
subject { described_class.new(tag) }
|
14
|
+
|
15
|
+
it "has commit object" do
|
16
|
+
expect(subject.commit).to eq('aabbcczz1')
|
17
|
+
end
|
18
|
+
|
19
|
+
it "uses tag name" do
|
20
|
+
expect(subject.title).to eq('v0.0.1')
|
21
|
+
end
|
22
|
+
|
23
|
+
it "uses hash value as value" do
|
24
|
+
expect(subject.date).to eq(Time.utc(2013,11,21,8,10,10))
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
context "comparable" do
|
31
|
+
|
32
|
+
let(:one) { described_class.new(first_tag) }
|
33
|
+
let(:two) { described_class.new(second_tag) }
|
34
|
+
let(:three) { described_class.new(last_tag) }
|
35
|
+
|
36
|
+
it "have to be equal" do
|
37
|
+
expect(one <=> one).to eq(0)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "have to be lower" do
|
41
|
+
expect(one <=> two).to eq(-1)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "have to be greater" do
|
45
|
+
expect(three <=> two).to eq(1)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "have to be sortable" do
|
49
|
+
expect([one, three, two].sort).to eq([one, two, three])
|
50
|
+
end
|
51
|
+
|
52
|
+
it "have to be reversable" do
|
53
|
+
expect([one, two, three].reverse).to eq([three, two, one])
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
describe ".stories" do
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe CapistranoChangelog::Version do
|
4
|
+
|
5
|
+
let(:version) { 'x.y.z-andsomehash' }
|
6
|
+
|
7
|
+
describe ".generate" do
|
8
|
+
it "generates JSON string" do
|
9
|
+
expect{ JSON.parse(described_class.generate) }.not_to raise_error
|
10
|
+
end
|
11
|
+
|
12
|
+
context "JSON content" do
|
13
|
+
let(:time) { Time.mktime(2014,01,01) }
|
14
|
+
|
15
|
+
before do
|
16
|
+
allow(CapistranoChangelog::Git).to receive(:describe).and_return(version)
|
17
|
+
allow(Time).to receive(:now).and_return(time)
|
18
|
+
end
|
19
|
+
|
20
|
+
subject { JSON.parse(described_class.generate) }
|
21
|
+
|
22
|
+
it "contains version string" do
|
23
|
+
expect(subject).to include({'version' => version})
|
24
|
+
end
|
25
|
+
|
26
|
+
it "contains UNIX timestamp" do
|
27
|
+
expect(subject).to include({'ts' => time.to_i})
|
28
|
+
end
|
29
|
+
|
30
|
+
context "restart flag" do
|
31
|
+
it "returns true by default" do
|
32
|
+
allow(ENV).to receive(:fetch).with('RESTART', 'true').and_return('true')
|
33
|
+
expect(subject).to include({'restart' => true})
|
34
|
+
end
|
35
|
+
|
36
|
+
it "returns false if environment variable exists" do
|
37
|
+
allow(ENV).to receive(:fetch).with('RESTART', 'true').and_return('any value or false')
|
38
|
+
expect(subject).to include({'restart' => false})
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
data/spec/spec_helper.rb
CHANGED
data/templates/changelog.erb
CHANGED
@@ -106,35 +106,51 @@
|
|
106
106
|
|
107
107
|
<body>
|
108
108
|
<div class="content">
|
109
|
-
<h1>Change Log @ <%=
|
109
|
+
<h1>Change Log @ <%= format date %></h1>
|
110
110
|
<div class="content-inner">
|
111
111
|
|
112
|
-
<% for
|
113
|
-
<h2><%=
|
112
|
+
<% for release in releases %>
|
113
|
+
<h2><%= release.comment %> <span class="date">(<%= format release.date %>)</span></h2>
|
114
114
|
|
115
|
-
<% for
|
115
|
+
<% for story in release.stories(history) %>
|
116
116
|
<div class="story">
|
117
117
|
|
118
|
-
<% if
|
118
|
+
<% if story.valid? %>
|
119
119
|
<h3>
|
120
|
-
<a href="<%=
|
121
|
-
<span class="name"><%=
|
120
|
+
<a href="<%= story.url %>"><span class="num"><%= story.uid %></span></a>
|
121
|
+
<span class="name"><%= story.title %></span>
|
122
122
|
</h3>
|
123
123
|
<% else %>
|
124
124
|
<h3>
|
125
|
-
<span class="num"><%=
|
126
|
-
<span class="name"><%=
|
125
|
+
<span class="num"><%= story.uid %></span>
|
126
|
+
<span class="name"><%= story.error %></span>
|
127
127
|
</h3>
|
128
128
|
<% end %>
|
129
129
|
|
130
130
|
<ul class="commits">
|
131
|
-
<% for
|
132
|
-
<li><%=
|
131
|
+
<% for commit in story.commits %>
|
132
|
+
<li><%= commit %></li>
|
133
133
|
<% end %>
|
134
134
|
</ul>
|
135
135
|
</div>
|
136
136
|
<% end %>
|
137
137
|
|
138
|
+
<% if release.unreconized?(history) %>
|
139
|
+
<div class="story">
|
140
|
+
|
141
|
+
<h3>
|
142
|
+
<span class="name">Unreconized</span>
|
143
|
+
</h3>
|
144
|
+
|
145
|
+
<ul class="commits">
|
146
|
+
<% for commit in release.unreconized(history) %>
|
147
|
+
<li><%= commit.short %> <%= commit %></li>
|
148
|
+
<% end %>
|
149
|
+
</ul>
|
150
|
+
|
151
|
+
</div>
|
152
|
+
<% end %>
|
153
|
+
|
138
154
|
<% end %>
|
139
155
|
</div>
|
140
156
|
</div>
|