historian 0.0.1

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.
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