herodotus 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.rvmrc +1 -0
- data/CHANGES +10 -0
- data/Gemfile +4 -0
- data/README.md +59 -0
- data/Rakefile +12 -0
- data/herodotus.gemspec +26 -0
- data/lib/herodotus.rb +6 -0
- data/lib/herodotus/collector.rb +46 -0
- data/lib/herodotus/git.rb +31 -0
- data/lib/herodotus/reporter.rb +25 -0
- data/lib/herodotus/tasks.rb +22 -0
- data/lib/herodotus/version.rb +6 -0
- data/spec/collector_spec.rb +50 -0
- data/spec/git_spec.rb +40 -0
- data/spec/reporter_spec.rb +44 -0
- data/spec/spec_helper.rb +5 -0
- metadata +136 -0
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.2
|
data/CHANGES
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
v0.0.1 [2011-07-28]:
|
2
|
+
|
3
|
+
2011-07-23 Harold Giménez <hgimenez@thoughtbot.com>
|
4
|
+
Including "Changelog:" in the commit message will mark
|
5
|
+
that commit as available for the changelog, and will use all text
|
6
|
+
below the keyword.
|
7
|
+
|
8
|
+
2011-07-27 Harold Giménez <hgimenez@thoughtbot.com>
|
9
|
+
Add the ability for specifying which revision to write a changelog
|
10
|
+
since. Usually, this will be a tag name.
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
Herodotus
|
2
|
+
---------
|
3
|
+
|
4
|
+
Herodotus, an ancient greek historian and story teller will
|
5
|
+
help you keep a changelog updated.
|
6
|
+
|
7
|
+
Oftentimes you don't think about the changelog when you make changes to your
|
8
|
+
project. Then, when it's time to cut a release, you've really lost momentum
|
9
|
+
of how those changes affect your users. You thought about these things days or
|
10
|
+
weeks ago, when you committed them to the repo.
|
11
|
+
|
12
|
+
Herodotus can help by going over your git history since a particular release
|
13
|
+
and extracting your notes. Obviously, not every commit will contain useful or
|
14
|
+
changelog worthy information, so we'll use the commit message format to
|
15
|
+
communicate what should go in the changelog. Here are the rules:
|
16
|
+
|
17
|
+
* Tag every release, so that it is easy to tell Herodotus how far back to start
|
18
|
+
looking for changes.
|
19
|
+
* To start writing notes for use in the changelog, use the following format on
|
20
|
+
your git commit message:
|
21
|
+
|
22
|
+
|
23
|
+
```
|
24
|
+
Real commit message subject
|
25
|
+
|
26
|
+
Some more info on the commit
|
27
|
+
|
28
|
+
Changelog:
|
29
|
+
Anything below this line will go on the changelog. Tell your users how to
|
30
|
+
upgrade the app, what has been depracated, what will break, etc.
|
31
|
+
```
|
32
|
+
|
33
|
+
It doesn't need to be exact either. Herodotus will simply extract these
|
34
|
+
comments, format them by adding the author and date, and either append them to
|
35
|
+
your CHANGES file, or print them out to standard out. Once it's there you can
|
36
|
+
tweak it at will before pushing. But the important pieces have been thought out
|
37
|
+
at the time when you wrote the software changes - and was written right on the
|
38
|
+
commit message, and so maintaining the changelog could be easier.
|
39
|
+
|
40
|
+
## Installation
|
41
|
+
|
42
|
+
Add herodotus to your Gemfile and run bundle.
|
43
|
+
|
44
|
+
If you're not using bundler, install via `gem install herodotus`
|
45
|
+
|
46
|
+
Then add `require 'herodotus/tasks'` to your `Rakefile`. This will provide the rake tasks with which you interface with herodotus.
|
47
|
+
|
48
|
+
## Usage
|
49
|
+
|
50
|
+
Herodotus provides a couple of rake tasks:
|
51
|
+
|
52
|
+
```
|
53
|
+
rake -T
|
54
|
+
rake herodotus:append[since_ref] # Appends changes to your changelog
|
55
|
+
rake herodotus:print[since_ref] # Prints out the change log from git
|
56
|
+
```
|
57
|
+
|
58
|
+
You can optionally pass the reference (usually a tag) from which herodotus will start to look for changelog messages in your commits.
|
59
|
+
Note that some shells require you to wrap the rake task in double quotes when passing arguments. For example: `rake "herodotus:print[v1]"`
|
data/Rakefile
ADDED
data/herodotus.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "herodotus/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "herodotus"
|
7
|
+
s.version = Herodotus::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Harold Giménez"]
|
10
|
+
s.email = ["harold.gimenez@gmail.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{The father of your project's history}
|
13
|
+
s.description = %q{Reads your git tags and commit messages to keep the changelog updated}
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_dependency 'grit'
|
21
|
+
|
22
|
+
s.add_development_dependency 'redgreen'
|
23
|
+
s.add_development_dependency 'rake'
|
24
|
+
s.add_development_dependency 'ruby-debug19'
|
25
|
+
s.add_development_dependency 'mocha'
|
26
|
+
end
|
data/lib/herodotus.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
module Herodotus
|
2
|
+
class Collector
|
3
|
+
attr_accessor :git, :since_ref, :changes, :changelog_filename
|
4
|
+
CHANGES_REGEX = /changelog.*?\n(.*)/mi.freeze
|
5
|
+
|
6
|
+
def initialize(base_dir, since_ref = nil)
|
7
|
+
@git = Git.new(base_dir)
|
8
|
+
@since_ref = since_ref
|
9
|
+
@changes = []
|
10
|
+
find_changes
|
11
|
+
end
|
12
|
+
|
13
|
+
Change = Struct.new(:author, :time, :message) do
|
14
|
+
def pretty_author
|
15
|
+
"#{author.name} <#{author.email}>"
|
16
|
+
end
|
17
|
+
|
18
|
+
def log_entry
|
19
|
+
"#{time.to_date} #{pretty_author}" +
|
20
|
+
"\n#{message}" +
|
21
|
+
"\n\n"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def print
|
26
|
+
reporter.print
|
27
|
+
end
|
28
|
+
|
29
|
+
def append_to_changelog
|
30
|
+
reporter.append_to_changelog
|
31
|
+
end
|
32
|
+
|
33
|
+
def find_changes
|
34
|
+
git.commits.each do |commit|
|
35
|
+
if commit.message =~ CHANGES_REGEX
|
36
|
+
@changes.unshift Change.new(commit.author, commit.authored_date, $1)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
private :find_changes
|
41
|
+
|
42
|
+
def reporter
|
43
|
+
Herodotus::Reporter.new(self)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Herodotus
|
2
|
+
class Git
|
3
|
+
attr_accessor :repo
|
4
|
+
def initialize(base_dir)
|
5
|
+
@base_dir = base_dir
|
6
|
+
@repo = Grit::Repo.new(guess_repo)
|
7
|
+
end
|
8
|
+
|
9
|
+
def commits(since_ref = nil)
|
10
|
+
if since_ref
|
11
|
+
output = repo.git.rev_list({'pretty' => 'raw'}, "#{since_ref}..")
|
12
|
+
Grit::Commit.list_from_string(repo, output)
|
13
|
+
else
|
14
|
+
Grit::Commit.find_all(@repo, since_ref)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def guess_repo
|
20
|
+
current_dir = File.expand_path(@base_dir)
|
21
|
+
until current_dir == '/' do
|
22
|
+
maybe_repo = File.expand_path(".git", current_dir)
|
23
|
+
if File.directory?(maybe_repo)
|
24
|
+
return maybe_repo
|
25
|
+
else
|
26
|
+
current_dir = File.expand_path('..', current_dir)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Herodotus
|
2
|
+
class Reporter
|
3
|
+
attr_reader :collector
|
4
|
+
attr_accessor :changelog_filename
|
5
|
+
|
6
|
+
def initialize(collector)
|
7
|
+
@collector = collector
|
8
|
+
@changelog_filename = 'CHANGES'
|
9
|
+
end
|
10
|
+
|
11
|
+
def print
|
12
|
+
@collector.changes.each do |change|
|
13
|
+
puts change.log_entry
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def append_to_changelog
|
18
|
+
File.open(changelog_filename, 'a') do |file|
|
19
|
+
@collector.changes.each do |change|
|
20
|
+
file.puts change.log_entry
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'herodotus'
|
3
|
+
|
4
|
+
def default_base_dir
|
5
|
+
File.expand_path(File.dirname(__FILE__))
|
6
|
+
end
|
7
|
+
|
8
|
+
namespace :herodotus do
|
9
|
+
desc 'Prints out the change log from git'
|
10
|
+
task :print, :since_ref do |t, args|
|
11
|
+
base_dir = ENV['BASE_DIR'] || default_base_dir
|
12
|
+
Herodotus::Collector.new(base_dir, args['since_ref']).
|
13
|
+
print
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Appends changes to your changelog'
|
17
|
+
task :append, :since_ref do |t, args|
|
18
|
+
base_dir = ENV['BASE_DIR'] || default_base_dir
|
19
|
+
Herodotus::Collector.new(base_dir, args['since_ref']).
|
20
|
+
append_to_changelog
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require File.expand_path('spec_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe Herodotus::Collector do
|
4
|
+
def collector
|
5
|
+
@collector ||= Herodotus::Collector.new('/tmp/herodotus')
|
6
|
+
end
|
7
|
+
before do
|
8
|
+
FileUtils.mkdir_p '/tmp/herodotus'
|
9
|
+
FileUtils.cd '/tmp/herodotus' do
|
10
|
+
`git init`
|
11
|
+
`touch file1`
|
12
|
+
`git add file1`
|
13
|
+
`git commit -m "this is a change"`
|
14
|
+
`touch file2`
|
15
|
+
`git add file2`
|
16
|
+
`git commit -m "Another\nChAnGeLOg:\nBroke everything again. Don't update to this version."`
|
17
|
+
`touch file3`
|
18
|
+
`git add file3`
|
19
|
+
`git commit -m "Another\nchangelog:\nNevermind, everything is fixed now."`
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
after { FileUtils.rm_rf '/tmp/herodotus' }
|
24
|
+
|
25
|
+
it 'starts off with a default git, changes and a since_ref of nil' do
|
26
|
+
collector.git.wont_be_nil
|
27
|
+
collector.since_ref.must_be_nil
|
28
|
+
collector.changes.wont_be_nil
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'finds commits containing the changelog keyword on the message' do
|
32
|
+
collector.changes.length.must_equal 2
|
33
|
+
collector.changes.first.message.must_equal "Broke everything again. Don't update to this version."
|
34
|
+
collector.changes.last.message.must_equal "Nevermind, everything is fixed now."
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'tells the reporter to print' do
|
38
|
+
reporter = mock('reporter')
|
39
|
+
collector.stubs(:reporter).returns(reporter)
|
40
|
+
reporter.expects(:print).once
|
41
|
+
collector.print
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'tells the reporter to append to changelog' do
|
45
|
+
reporter = mock('reporter')
|
46
|
+
collector.stubs(:reporter).returns(reporter)
|
47
|
+
reporter.expects(:append_to_changelog).once
|
48
|
+
collector.append_to_changelog
|
49
|
+
end
|
50
|
+
end
|
data/spec/git_spec.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.expand_path('spec_helper', File.dirname(__FILE__))
|
2
|
+
require 'ruby-debug'
|
3
|
+
|
4
|
+
describe Herodotus::Git do
|
5
|
+
|
6
|
+
def git
|
7
|
+
@git ||= Herodotus::Git.new('/tmp/herodotus/bacon')
|
8
|
+
end
|
9
|
+
|
10
|
+
before do
|
11
|
+
FileUtils.mkdir_p '/tmp/herodotus'
|
12
|
+
FileUtils.cd '/tmp/herodotus' do
|
13
|
+
`git init`
|
14
|
+
`touch file`
|
15
|
+
`git add file`
|
16
|
+
`git commit -m "Added file"`
|
17
|
+
`git tag -a v1 -m "the MVP"`
|
18
|
+
`touch file2`
|
19
|
+
`git add file2`
|
20
|
+
`git commit -m "Added file2"`
|
21
|
+
FileUtils.mkdir_p 'bacon'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
after do
|
26
|
+
FileUtils.rm_rf '/tmp/herodotus'
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'locates the closest git repo' do
|
30
|
+
git.repo.path.must_equal '/tmp/herodotus/.git'
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'pulls out commits from the repo' do
|
34
|
+
git.commits.map(&:message).must_equal ['Added file2', 'Added file']
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'pulls out commits since a given ref' do
|
38
|
+
git.commits('v1').map(&:message).must_equal ['Added file2']
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require File.expand_path('spec_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe Herodotus::Reporter do
|
4
|
+
def collector
|
5
|
+
@collector ||= Herodotus::Collector.new('/tmp/herodotus')
|
6
|
+
end
|
7
|
+
|
8
|
+
before do
|
9
|
+
FileUtils.mkdir_p '/tmp/herodotus'
|
10
|
+
FileUtils.cd '/tmp/herodotus' do
|
11
|
+
`git init`
|
12
|
+
`touch file1`
|
13
|
+
`git add file1`
|
14
|
+
`git commit -m "this is a change"`
|
15
|
+
`touch file2`
|
16
|
+
`git add file2`
|
17
|
+
`git commit -m "Another\nChAnGeLOg:\nBroke everything again. Don't update to this version."`
|
18
|
+
`touch file3`
|
19
|
+
`git add file3`
|
20
|
+
`git commit -m "Another\nchangelog:\nNevermind, everything is fixed now."`
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
after { FileUtils.rm_rf '/tmp/herodotus' }
|
25
|
+
|
26
|
+
it 'takes a Collector instance' do
|
27
|
+
reporter = Herodotus::Reporter.new(collector)
|
28
|
+
reporter.collector.must_equal collector
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'has a default changelog filename of CHANGES' do
|
32
|
+
reporter = Herodotus::Reporter.new(collector)
|
33
|
+
reporter.changelog_filename.must_equal 'CHANGES'
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'appends changelog entries to the changelog file' do
|
37
|
+
reporter = Herodotus::Reporter.new(collector)
|
38
|
+
reporter.changelog_filename = File.expand_path('tmp/test_changes')
|
39
|
+
reporter.append_to_changelog
|
40
|
+
changelog = IO.read(reporter.changelog_filename)
|
41
|
+
changelog.must_include "Broke everything again. Don't update to this version."
|
42
|
+
changelog.must_include "Nevermind, everything is fixed now."
|
43
|
+
end
|
44
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: herodotus
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- "Harold Gim\xC3\xA9nez"
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-07-28 00:00:00 -04:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: grit
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
type: :runtime
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: redgreen
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "0"
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: rake
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
type: :development
|
47
|
+
prerelease: false
|
48
|
+
version_requirements: *id003
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: ruby-debug19
|
51
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
type: :development
|
58
|
+
prerelease: false
|
59
|
+
version_requirements: *id004
|
60
|
+
- !ruby/object:Gem::Dependency
|
61
|
+
name: mocha
|
62
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: *id005
|
71
|
+
description: Reads your git tags and commit messages to keep the changelog updated
|
72
|
+
email:
|
73
|
+
- harold.gimenez@gmail.com
|
74
|
+
executables: []
|
75
|
+
|
76
|
+
extensions: []
|
77
|
+
|
78
|
+
extra_rdoc_files: []
|
79
|
+
|
80
|
+
files:
|
81
|
+
- .gitignore
|
82
|
+
- .rvmrc
|
83
|
+
- CHANGES
|
84
|
+
- Gemfile
|
85
|
+
- README.md
|
86
|
+
- Rakefile
|
87
|
+
- herodotus.gemspec
|
88
|
+
- lib/herodotus.rb
|
89
|
+
- lib/herodotus/collector.rb
|
90
|
+
- lib/herodotus/git.rb
|
91
|
+
- lib/herodotus/reporter.rb
|
92
|
+
- lib/herodotus/tasks.rb
|
93
|
+
- lib/herodotus/version.rb
|
94
|
+
- spec/collector_spec.rb
|
95
|
+
- spec/git_spec.rb
|
96
|
+
- spec/reporter_spec.rb
|
97
|
+
- spec/spec_helper.rb
|
98
|
+
has_rdoc: true
|
99
|
+
homepage: ""
|
100
|
+
licenses: []
|
101
|
+
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options: []
|
104
|
+
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
hash: -2072332935919534749
|
113
|
+
segments:
|
114
|
+
- 0
|
115
|
+
version: "0"
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
none: false
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
hash: -2072332935919534749
|
122
|
+
segments:
|
123
|
+
- 0
|
124
|
+
version: "0"
|
125
|
+
requirements: []
|
126
|
+
|
127
|
+
rubyforge_project:
|
128
|
+
rubygems_version: 1.6.2
|
129
|
+
signing_key:
|
130
|
+
specification_version: 3
|
131
|
+
summary: The father of your project's history
|
132
|
+
test_files:
|
133
|
+
- spec/collector_spec.rb
|
134
|
+
- spec/git_spec.rb
|
135
|
+
- spec/reporter_spec.rb
|
136
|
+
- spec/spec_helper.rb
|