herodotus 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ tmp/*
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
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in herodotus.gemspec
4
+ gemspec
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
@@ -0,0 +1,12 @@
1
+ require 'bundler'
2
+ require 'bundler/setup'
3
+ require 'herodotus/tasks'
4
+ Bundler::GemHelper.install_tasks
5
+ include Rake::DSL
6
+
7
+ require 'rake/testtask'
8
+ Rake::TestTask.new do |t|
9
+ t.pattern = "spec/*_spec.rb"
10
+ end
11
+
12
+ task :default => :test
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,6 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ require 'grit'
3
+
4
+ require 'herodotus/git'
5
+ require 'herodotus/collector'
6
+ require 'herodotus/reporter'
@@ -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,6 @@
1
+ module Herodotus
2
+ MAJOR = 0
3
+ MINOR = 0
4
+ PATCH = 1
5
+ VERSION = "#{MAJOR}.#{MINOR}.#{PATCH}"
6
+ 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
@@ -0,0 +1,5 @@
1
+ require 'minitest/autorun'
2
+ require 'ruby-debug'
3
+ require 'mocha'
4
+
5
+ require File.expand_path(File.join('lib', 'herodotus'))
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