Skip to content

Commit 2accf3e

Browse files
committed
Merge pull request #191 from agross/master
Improved the Ruby Gem to make it usable from a Rakefile
2 parents dd63885 + 10a14fa commit 2accf3e

File tree

12 files changed

+302
-15
lines changed

12 files changed

+302
-15
lines changed

GitVersionExe/GemAssets/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Gemfile.lock
2+
!bin/

GitVersionExe/GemAssets/.rspec

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
--require spec_helper
2+
--tag ~@draft

GitVersionExe/GemAssets/Gemfile

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
source 'https://rubygems.org'
2+
3+
gemspec
4+
5+
group :development do
6+
gem 'guard-bundler', require: false
7+
gem 'guard-rspec', require: false
8+
9+
case RbConfig::CONFIG['target_os']
10+
when /windows|bccwin|cygwin|djgpp|mingw|mswin|wince/i
11+
gem 'ruby_gntp', require: false
12+
gem 'wdm', require: false
13+
when /linux/i
14+
gem 'rb-inotify', require: false
15+
when /mac|darwin/i
16+
gem 'rb-fsevent', require: false
17+
gem 'growl', require: false
18+
end
19+
end
20+
21+
group :development, :ci do
22+
gem 'rspec', '~> 3.0.0', require: false
23+
gem 'rspec-its', require: false
24+
end

GitVersionExe/GemAssets/Guardfile

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
case RbConfig::CONFIG['target_os']
2+
when /windows|bccwin|cygwin|djgpp|mingw|mswin|wince/i
3+
notification :gntp, :host => 'localhost'
4+
when /linux/i
5+
notification :notifysend
6+
when /mac|darwin/i
7+
notification :growl
8+
end
9+
10+
guard 'bundler' do
11+
watch('Gemfile')
12+
watch(/^.+\.gemspec/)
13+
end
14+
15+
guard 'rspec',
16+
:all_on_start => true,
17+
:all_after_pass => true,
18+
:notification => true,
19+
:cmd => 'bundle exec rspec' do
20+
watch('.rspec') { 'spec' }
21+
watch(%r{^spec/.+_spec\.rb$})
22+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
23+
watch('spec/spec_helper.rb') { 'spec' }
24+
end
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1+
#! ruby
2+
13
result = system(File.dirname(__FILE__) + "/GitVersion.exe " + ARGV.join(' '))
2-
exit 1 unless result
4+
exit 1 unless result

GitVersionExe/GemAssets/gitversion.gemspec

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@ Gem::Specification.new do |spec|
33
spec.name = 'gitversion'
44
spec.licenses = ['MIT']
55
spec.version = '0.20.0'
6-
spec.files = Dir['bin/**/*']
7-
spec.bindir = 'bin'
8-
spec.executables << 'gitversion'
9-
106
spec.summary = 'Derives SemVer information from a repository following GitFlow or GitHubFlow.'
11-
spec.description = <<-EOF
7+
spec.description = <<-EOF
128
Derives SemVer information from a repository following GitFlow or GitHubFlow.
139
EOF
14-
10+
1511
spec.authors = ['NServiceBus','Simon Cropp']
1612
spec.email = '[email protected]'
1713
spec.homepage = 'http://github.com/Particular/GitVersion'
1814
spec.rubyforge_project = 'GitVersion'
19-
end
15+
16+
spec.files = Dir['bin/**/*', 'lib/**/*', '*.gemspec']
17+
spec.executables = spec.files.grep(%r{^bin/.+(?<!\.exe|\.pdb)$}) { |f| File.basename(f) }
18+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19+
spec.require_paths = ['lib']
20+
end
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
require 'git_version/parser'
2+
3+
module GitVersion
4+
def git_version(args = nil)
5+
parsers.fetch(args) { |a| parsers[a] = Parser.new(a) }
6+
end
7+
8+
private
9+
def parsers
10+
@parsers ||= {}
11+
end
12+
end
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
require 'json'
2+
require 'open3'
3+
4+
module GitVersion
5+
class Parser
6+
attr_reader :args
7+
attr_accessor :gitversion_exe
8+
9+
def initialize(args = [])
10+
@args = args
11+
end
12+
13+
def method_missing(symbol, *args)
14+
keys = [symbol.to_s, pascal_case(symbol.to_s)]
15+
16+
found_key = keys.find { |key| json.has_key?(key) }
17+
return json[found_key] if found_key
18+
19+
super
20+
end
21+
22+
def json
23+
@json ||= run_gitversion
24+
end
25+
26+
def gitversion_exe
27+
@gitversion_exe ||= File.expand_path(File.join(File.dirname(__FILE__), '../../bin/GitVersion.exe'))
28+
end
29+
30+
def inspect
31+
unless @json
32+
33+
return <<EOF
34+
#{to_s}
35+
Will invoke #{cmd_string} when first used.
36+
EOF
37+
38+
else
39+
40+
return <<EOF
41+
#{to_s}
42+
Invoked #{cmd_string} and parsed its output:
43+
#{json.inspect}
44+
EOF
45+
46+
end
47+
end
48+
49+
private
50+
def run_gitversion
51+
stdout_and_stderr, status = Open3.capture2e(*cmd)
52+
53+
raise StandardError.new("Failed running #{cmd_string}, #{status}. We received the following output:\n#{stdout_and_stderr}") unless status.success?
54+
55+
JSON.parse(stdout_and_stderr)
56+
end
57+
58+
def cmd
59+
cmd = [gitversion_exe]
60+
cmd << args
61+
cmd.flatten.reject(&:nil?)
62+
end
63+
64+
def cmd_string
65+
cmd.join(' ')
66+
end
67+
68+
def pascal_case(str)
69+
str
70+
.to_s
71+
.split('_')
72+
.inject([]) { |buffer, e| buffer.push(e.capitalize) }
73+
.join
74+
end
75+
end
76+
end
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
require 'git_version'
2+
3+
describe GitVersion::Parser do
4+
describe 'defaults' do
5+
its(:gitversion_exe) { should match(%r|/bin/GitVersion.exe$|) }
6+
its(:args) { should be_empty }
7+
end
8+
9+
describe 'GitVersion.exe invocation' do
10+
before {
11+
allow(Open3).to receive(:capture2e).and_return(gitversion_exe_return)
12+
}
13+
14+
let(:gitversion_exe_return) { ['{ "Sha": 1234 }', OpenStruct.new(success?: true)] }
15+
16+
describe 'cached results' do
17+
context 'accessing multiple properties' do
18+
it 'should run GitVersion.exe only once' do
19+
subject.sha
20+
subject.sha
21+
22+
expect(Open3).to have_received(:capture2e).once
23+
end
24+
end
25+
end
26+
27+
describe 'arguments' do
28+
subject { described_class.new(args) }
29+
30+
let(:args) { %w(some additional args) }
31+
32+
it 'should pass args to the executable' do
33+
subject.sha
34+
35+
expect(Open3).to have_received(:capture2e).with(*([subject.gitversion_exe] + args))
36+
end
37+
end
38+
39+
context 'GitVersion.exe fails' do
40+
let(:gitversion_exe_return) { ['some error output', OpenStruct.new(success?: false)] }
41+
42+
it 'should fail' do
43+
expect { subject.it_does_not_matter }.to raise_error(StandardError, /Failed running.*some error output/m)
44+
end
45+
end
46+
end
47+
48+
describe 'after the executable has been built' do
49+
before {
50+
subject.gitversion_exe = File.expand_path('../../GemBuild/bin/GitVersion.exe')
51+
}
52+
53+
its(:json) { should_not be_nil }
54+
its(:sha) { should match(/[0-9 a-f]{40}/) }
55+
end
56+
57+
describe 'accessing properties' do
58+
before {
59+
allow(subject).to receive(:json).and_return(json)
60+
}
61+
62+
let(:json) { { 'InformationalVersion' => 'blah' } }
63+
64+
it 'should translate snake case to pascal case' do
65+
expect(subject.informational_version).to eq('blah')
66+
end
67+
68+
it 'should support pascal case' do
69+
pascal = subject.InformationalVersion
70+
snake = subject.informational_version
71+
72+
expect(pascal).to eq(snake)
73+
end
74+
75+
context 'with nil value' do
76+
let(:json) { { 'is_nil' => nil } }
77+
78+
it 'should yield nil' do
79+
expect(subject.is_nil).to be_nil
80+
end
81+
end
82+
end
83+
84+
describe '#inspect' do
85+
context 'no properties accessed yet' do
86+
it 'should write what will happen' do
87+
expect(subject.inspect).to match(/.+GitVersion.+\nWill invoke .+GitVersion.exe when first used./)
88+
end
89+
end
90+
91+
context 'properties accessed' do
92+
before {
93+
allow(Open3).to receive(:capture2e).and_return(['{ "Sha": 1234 }', OpenStruct.new(success?: true)])
94+
}
95+
96+
it 'should write what happened' do
97+
subject.sha
98+
expect(subject.inspect).to match(/.+GitVersion.+\nInvoked .+GitVersion.exe and parsed its output:\n.+/)
99+
end
100+
end
101+
end
102+
end
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
require 'git_version'
2+
3+
describe GitVersion do
4+
include described_class
5+
6+
it 'should create a ' + GitVersion::Parser.to_s do
7+
expect(git_version).to be_an_instance_of(GitVersion::Parser)
8+
end
9+
10+
it 'should create a singleton ' + GitVersion::Parser.to_s do
11+
expect(git_version).to equal(git_version)
12+
end
13+
14+
describe 'passing arguments' do
15+
it 'should yield the same instance per argument' do
16+
expect(git_version('foo')).to equal(git_version('foo'))
17+
end
18+
19+
it 'should yield different instances for different arguments' do
20+
expect(git_version('foo')).not_to equal_no_diff(git_version('bar'))
21+
end
22+
23+
def equal_no_diff(expected)
24+
expected = equal(expected)
25+
26+
# Turn off diffing for this matcher as it calls GitVersion::Parser#to_ary which will fail because GitVersion.exe
27+
# cannot be found.
28+
def expected.diffable?
29+
false
30+
end
31+
32+
expected
33+
end
34+
end
35+
end

0 commit comments

Comments
 (0)