historian 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/.document +5 -0
  2. data/.gitignore +5 -0
  3. data/.rspec +3 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +35 -0
  6. data/LICENSE +20 -0
  7. data/LICENSE.txt +20 -0
  8. data/README.rdoc +149 -0
  9. data/Rakefile +2 -0
  10. data/VERSION +1 -0
  11. data/autotest/discover.rb +1 -0
  12. data/bin/historian +12 -0
  13. data/historian.gemspec +28 -0
  14. data/lib/historian.rb +12 -0
  15. data/lib/historian/cli.rb +84 -0
  16. data/lib/historian/commit_message.rb +40 -0
  17. data/lib/historian/configuration.rb +10 -0
  18. data/lib/historian/git.rb +189 -0
  19. data/lib/historian/history_file.rb +209 -0
  20. data/lib/historian/version.rb +3 -0
  21. data/spec/example_history.txt +24 -0
  22. data/spec/fixtures/after_0_0_2 +16 -0
  23. data/spec/fixtures/after_0_0_2_changelog +6 -0
  24. data/spec/fixtures/after_0_0_2_history +17 -0
  25. data/spec/fixtures/all_types_history +17 -0
  26. data/spec/fixtures/anonymous_release_log +7 -0
  27. data/spec/fixtures/arbitrary_text +21 -0
  28. data/spec/fixtures/courageous_camel_history +13 -0
  29. data/spec/fixtures/courageous_camel_release_log +7 -0
  30. data/spec/fixtures/empty +0 -0
  31. data/spec/fixtures/invalid_significance +17 -0
  32. data/spec/fixtures/missing_significance +16 -0
  33. data/spec/fixtures/normal +10 -0
  34. data/spec/fixtures/normal_changelog_after_major +11 -0
  35. data/spec/fixtures/normal_changelog_after_minor +8 -0
  36. data/spec/fixtures/normal_history_after_major +17 -0
  37. data/spec/fixtures/normal_history_after_minor +14 -0
  38. data/spec/fixtures/patch_on_nothing +5 -0
  39. data/spec/fixtures/release_on_nothing +1 -0
  40. data/spec/fixtures/second_patch_on_nothing +6 -0
  41. data/spec/historian/cli_spec.rb +210 -0
  42. data/spec/historian/commit_message_spec.rb +95 -0
  43. data/spec/historian/git_spec.rb +199 -0
  44. data/spec/historian/history_file_spec.rb +225 -0
  45. data/spec/spec.opts +3 -0
  46. data/spec/spec_helper.rb +21 -0
  47. data/spec/support/fixture_helper.rb +8 -0
  48. data/spec/support/git_helpers.rb +53 -0
  49. data/spec/support/history_file_matchers.rb +52 -0
  50. metadata +189 -0
@@ -0,0 +1,199 @@
1
+ require 'spec_helper'
2
+
3
+ describe Historian::Git do
4
+ include GitHelpers
5
+
6
+ before do
7
+ create_test_repo
8
+ end
9
+
10
+ subject { @git }
11
+
12
+ describe "#bundle_history_file" do
13
+ before do
14
+ @history = history_for_repo :courageous_camel_history
15
+ @git = Historian::Git.new(repo_directory, @history)
16
+ end
17
+
18
+ subject { @git }
19
+
20
+ it "amends the previous commit to include changes to the history file" do
21
+ modified?(@history_file).should be_true
22
+ lambda { subject.bundle_history_file }.should_not change(self, :commits_count)
23
+ modified?(@history_file).should be_false
24
+ end
25
+ end
26
+
27
+ describe "#install_hook" do
28
+ before do
29
+ @git = Historian::Git.new(repo_directory, nil)
30
+ @hook = "foo-bar"
31
+ @hook_sym = :foo_bar
32
+ @hook_wrapper = File.join(repo_directory, ".git", "hooks", @hook)
33
+ @hook_script = File.join(repo_directory, ".git", "hooks", @hook + ".d", "historian")
34
+ end
35
+
36
+ shared_examples_for "creating the hook sub-script" do
37
+ it "creates the hook script" do
38
+ File.exists?(@hook_script).should be_true
39
+ end
40
+
41
+ it "makes the hook script executable" do
42
+ File.stat(@hook_script).should be_executable
43
+ end
44
+
45
+ describe "contents" do
46
+ before do
47
+ @contents = File.readlines @hook_script
48
+ end
49
+
50
+ it "should be a bash script" do
51
+ @contents.first.start_with?("#!/bin/bash").should be_true
52
+ end
53
+
54
+ it "should invoke the hook exactly once" do
55
+ @contents.grep("historian #{@hook_sym} $@\n").should have(1).match
56
+ end
57
+ end
58
+ end
59
+
60
+ shared_examples_for "creating the hook wrapper" do
61
+ it "creates a directory for hooks scripts" do
62
+ File.directory?(File.dirname @hook_script).should be_true
63
+ end
64
+
65
+ it "creates a wrapper script" do
66
+ File.exists?(@hook_wrapper).should be_true
67
+ end
68
+
69
+ it "makes the hook script executable" do
70
+ File.stat(@hook_wrapper).should be_executable
71
+ end
72
+
73
+ describe "contents" do
74
+ before do
75
+ @contents = File.readlines @hook_wrapper
76
+ end
77
+
78
+ it "should be a bash script" do
79
+ @contents.first.start_with?("#!/bin/bash").should be_true
80
+ end
81
+
82
+ it "should invoke all the scripts in the hook script directory" do
83
+ invoke = /for S in .*#{@hook}.d/
84
+ @contents.grep(invoke).should have(1).match
85
+ end
86
+ end
87
+ end
88
+
89
+ context "with no existing hook" do
90
+ before do
91
+ @result = @git.install_hook(@hook_sym)
92
+ end
93
+
94
+ it "returns :created" do
95
+ @result.should eq(:created)
96
+ end
97
+
98
+ it_behaves_like "creating the hook wrapper"
99
+ it_behaves_like "creating the hook sub-script"
100
+ end
101
+
102
+ context "when the hook is already installed" do
103
+ before do
104
+ @result = @git.install_hook(@hook_sym)
105
+ end
106
+
107
+ it "rewrites the hook file" do
108
+ File.should_receive(:open).with(@hook_script, /w/)
109
+ @git.install_hook @hook_sym
110
+ end
111
+
112
+ it "returns :exists" do
113
+ @git.install_hook(@hook_sym).should eq(:exists)
114
+ end
115
+ end
116
+
117
+ context "when another hook is already installed" do
118
+ before do
119
+ @original_contents = "the original script!"
120
+ File.open(@hook_wrapper, "w") { |f| f.write @original_contents }
121
+ @result = @git.install_hook(@hook_sym)
122
+ end
123
+
124
+ it "returns :adapted" do
125
+ @result.should eq(:adapted)
126
+ end
127
+
128
+ it "copies the original hook file into the hook's script directory" do
129
+ new_location = File.join File.dirname(@hook_script), "original"
130
+ File.read(new_location).should eq(@original_contents)
131
+ end
132
+
133
+ it_behaves_like "creating the hook wrapper"
134
+ it_behaves_like "creating the hook sub-script"
135
+ end
136
+ end
137
+
138
+ describe "#tag_release" do
139
+ shared_examples_for "a tagged release" do
140
+ it "creates a new tag for the latest version" do
141
+ tag = "v" + @history.current_version
142
+ tags.should_not include(tag)
143
+ subject.tag_release
144
+ tags.should include(tag)
145
+ end
146
+
147
+ it "annotates the tag with a short message including the version" do
148
+ subject.tag_release
149
+ commit_message_for_tag("v12.0.0").should match(/tagged v12.0.0 "Courageous Camel"/)
150
+ end
151
+
152
+ it "annotates the tag with the changelog" do
153
+ subject.tag_release
154
+ commit_message_for_tag("v12.0.0").should include(fixture :courageous_camel_release_log)
155
+ end
156
+ end
157
+
158
+ describe "with a dirty history file" do
159
+ before do
160
+ @history = history_for_repo :courageous_camel_history
161
+ @history.update_history :minor => "some little thing"
162
+ @git = Historian::Git.new(repo_directory, @history)
163
+ end
164
+
165
+ subject { @git }
166
+
167
+ it "releases the history file if the changelog isn't empty" do
168
+ @history.should_receive :release
169
+ subject.tag_release
170
+ end
171
+
172
+ it "commits the history file if it has unstaged changes" do
173
+ subject.should_receive(:commit_history_changes).twice
174
+ subject.tag_release
175
+ end
176
+
177
+ it_behaves_like "a tagged release"
178
+ end
179
+
180
+ describe "with a clean history file" do
181
+ before do
182
+ @history = history_for_repo :courageous_camel_history
183
+ run_git "add", @history.path
184
+ run_git "commit", "-m", "clean the history"
185
+ @git = Historian::Git.new(repo_directory, @history)
186
+ end
187
+
188
+ subject { @git }
189
+
190
+ it "does not commit the history file" do
191
+ subject.should_not_receive :commit_history_changes
192
+ subject.tag_release
193
+ end
194
+
195
+ it_behaves_like "a tagged release"
196
+
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,225 @@
1
+ require 'spec_helper'
2
+
3
+ describe Historian::HistoryFile do
4
+ def history(fixture_name)
5
+ StringIO.new(fixture fixture_name).tap do |io|
6
+ io.extend Historian::HistoryFile
7
+ end
8
+ end
9
+
10
+ before do
11
+ @io = StringIO.new ""
12
+ @io.extend Historian::HistoryFile
13
+ end
14
+
15
+ subject { @io }
16
+
17
+ describe "#release" do
18
+ it "should invoke update_history" do
19
+ subject.should_receive(:update_history).with(:release => true)
20
+ subject.release
21
+ end
22
+ end
23
+
24
+ context "with no history" do
25
+ before do
26
+ @io = StringIO.new(fixture :empty)
27
+ @io.extend Historian::HistoryFile
28
+ end
29
+ subject {@io}
30
+
31
+ context "when not adding history" do
32
+ it { should have_current_version "0.0.0" }
33
+ it { should have_next_version "0.0.1" }
34
+ end
35
+
36
+ context "when adding a patch to history" do
37
+ before do
38
+ @io.update_history :patch => "bugfix #1"
39
+ end
40
+
41
+ it { should have_current_version "0.0.0" }
42
+ it { should have_next_version "0.0.1" }
43
+
44
+ it { should have_history_like :patch_on_nothing }
45
+ it { should have_changelog_like :patch_on_nothing }
46
+ end
47
+
48
+ context "when triggering a release" do
49
+ before do
50
+ Time.stub_chain :now, :strftime => "2010/12/12"
51
+ @io.update_history :release => "Addled Adder"
52
+ end
53
+
54
+ it { should have_current_version "0.0.1" }
55
+ it { should have_next_version "0.0.2" }
56
+ it { should have_history_like :release_on_nothing }
57
+ end
58
+ end
59
+
60
+ context "with unreleased history" do
61
+ before do
62
+ @io = StringIO.new(fixture :patch_on_nothing)
63
+ @io.extend Historian::HistoryFile
64
+ end
65
+ subject {@io}
66
+
67
+ context "when adding a patch to history" do
68
+ before do
69
+ @io.update_history :patch => "bugfix #2"
70
+ end
71
+
72
+ it { should have_current_version "0.0.0" }
73
+ it { should have_next_version "0.0.1" }
74
+
75
+ it { should have_history_like :second_patch_on_nothing }
76
+ it { should have_changelog_like :second_patch_on_nothing }
77
+ end
78
+ end
79
+
80
+ context "with unreleased and a 0.0.2 release" do
81
+ before do
82
+ @io = StringIO.new(fixture :after_0_0_2)
83
+ @io.extend Historian::HistoryFile
84
+ end
85
+ subject {@io}
86
+
87
+ it { should have_current_version "0.0.2" }
88
+ it { should have_next_version "0.0.3" }
89
+
90
+
91
+ context "when adding a patch to history" do
92
+ before do
93
+ @io.update_history :patch => "bugfix #2"
94
+ end
95
+
96
+ it { should have_next_version "0.0.3" }
97
+ it { should have_history_like :after_0_0_2_history }
98
+ it { should have_changelog_like :after_0_0_2_changelog }
99
+ end
100
+ end
101
+
102
+ describe do
103
+ before do
104
+ @io = StringIO.new(fixture :normal)
105
+ @io.extend Historian::HistoryFile
106
+ end
107
+ subject {@io}
108
+
109
+ context "after minor changes and bugfixes" do
110
+ before do
111
+ @io.update_history :patch => "bugfix #2",
112
+ :minor => "minor #1"
113
+ end
114
+
115
+ it { should have_current_version "11.22.33" }
116
+ it { should have_next_version "11.23.0" }
117
+
118
+ it { should have_history_like :normal_history_after_minor }
119
+ it { should have_changelog_like :normal_changelog_after_minor }
120
+ end
121
+
122
+ context "after major changes, minor changes and bugfixes" do
123
+ before do
124
+ @io.update_history :patch => "bugfix #2",
125
+ :minor => "minor #1",
126
+ :major => "major #1"
127
+ end
128
+
129
+ it { should have_current_version "11.22.33" }
130
+ it { should have_next_version "12.0.0" }
131
+
132
+ it { should have_history_like :normal_history_after_major }
133
+ it { should have_changelog_like :normal_changelog_after_major }
134
+ end
135
+ end
136
+
137
+ context "when releasing a major change" do
138
+ before do
139
+ Time.stub_chain :now, :strftime => "2010/12/12"
140
+ @io = StringIO.new(fixture :normal)
141
+ @io.extend Historian::HistoryFile
142
+ end
143
+ subject { @io }
144
+
145
+ context "with release name 'Courageous Camel'" do
146
+ before do
147
+ @io.update_history :major => "major #1",
148
+ :release => "Courageous Camel"
149
+ end
150
+ it { should have_current_version "12.0.0" }
151
+ it { should have_next_version "12.0.1" }
152
+ it { should have_history_like :courageous_camel_history }
153
+ it { should have_changelog_like :empty }
154
+ it { should have_release_log_like :courageous_camel_release_log }
155
+ it "returns the release name with #release_name" do
156
+ subject.current_release_name.should eq("Courageous Camel")
157
+ end
158
+ end
159
+
160
+ context "with no release name" do
161
+ before do
162
+ @io.update_history :major => "major #1",
163
+ :release => true
164
+ end
165
+
166
+ it { should have_release_log_like :anonymous_release_log }
167
+ end
168
+ end
169
+
170
+ context "when parsing a history file with a release and no unreleased changelog" do
171
+ before do
172
+ @io = StringIO.new(fixture :courageous_camel_history)
173
+ @io.extend Historian::HistoryFile
174
+ end
175
+ subject { @io }
176
+
177
+ it { should have_current_version "12.0.0" }
178
+ it "returns the release name with #release_name" do
179
+ subject.current_release_name.should eq("Courageous Camel")
180
+ end
181
+ it "returns the changelog for the release" do
182
+ subject.release_log.strip.should eql(fixture :courageous_camel_release_log)
183
+ end
184
+ end
185
+
186
+ context "when parsing a history with all significance categories" do
187
+ before do
188
+ @io = StringIO.new(fixture :all_types_history)
189
+ @io.extend Historian::HistoryFile
190
+ @io.parse
191
+ end
192
+ subject { @io.changes }
193
+
194
+ it "has major changes" do
195
+ subject[:major].should_not be_empty
196
+ end
197
+
198
+ it "has minor changes" do
199
+ subject[:minor].should_not be_empty
200
+ end
201
+
202
+ it "has patch changes" do
203
+ subject[:patch].should_not be_empty
204
+ end
205
+ end
206
+
207
+ # TODO: I've realized too late that the strict format
208
+ # I'm parsing against isn't very practical. I'll need
209
+ # to rework my parsing code to be more versatile.
210
+ context "oddly formated history files" do
211
+ it "raises an error when finding history without significance" do
212
+ lambda { history(:missing_significance).parse }.should raise_error(Historian::ParseError)
213
+ end
214
+ it "raises an error when finding an unknown significance" do
215
+ lambda { history(:invalid_significance).parse }.should raise_error(Historian::ParseError)
216
+ end
217
+ it "raises an error when finding arbitrary text" do
218
+ lambda { history(:arbitrary_text).parse }.should raise_error(Historian::ParseError)
219
+ end
220
+ end
221
+
222
+
223
+
224
+
225
+ end
@@ -0,0 +1,3 @@
1
+ --diff
2
+ --color
3
+ --format progress
@@ -0,0 +1,21 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+
5
+ require File.expand_path("../../lib/historian", __FILE__)
6
+
7
+ begin
8
+ require 'rubygems'
9
+ require 'spackle'
10
+ Spackle.init :with => :rspec_formatter
11
+ rescue LoadError
12
+ puts "spackle gem not found -- continuing"
13
+ end
14
+
15
+ # Requires supporting files with custom matchers and macros, etc,
16
+ # in ./support/ and its subdirectories.
17
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
18
+
19
+ RSpec.configure do |config|
20
+
21
+ end