cliver 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.
- data/.githooks/pre-commit/ruby-appraiser +17 -0
- data/.gitignore +17 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +45 -0
- data/Rakefile +8 -0
- data/cliver.gemspec +26 -0
- data/lib/cliver.rb +10 -0
- data/lib/cliver/assertion.rb +63 -0
- data/lib/cliver/version.rb +5 -0
- data/spec/cliver/assertion_spec.rb +89 -0
- data/spec/cliver_spec.rb +6 -0
- metadata +141 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
echo -e "\033[0;36mRuby Appraiser: running\033[0m"
|
3
|
+
bundle exec ruby-appraiser --mode=staged reek rubocop
|
4
|
+
result_code=$?
|
5
|
+
if [ $result_code -gt "0" ]; then
|
6
|
+
echo -en "\033[0;31m" # RED
|
7
|
+
echo "[✘] Ruby Appraiser found newly-created defects and "
|
8
|
+
echo " has blocked your commit."
|
9
|
+
echo " Fix the defects and commit again."
|
10
|
+
echo " To bypass, commit again with --no-verify."
|
11
|
+
echo -en "\033[0m" # RESET
|
12
|
+
exit $result_code
|
13
|
+
else
|
14
|
+
echo -en "\033[0;32m" # GREEN
|
15
|
+
echo "[✔] Ruby Appraiser ok"
|
16
|
+
echo -en "\033[0m" #RESET
|
17
|
+
fi
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Ryan Biesemeyer
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# Cliver
|
2
|
+
|
3
|
+
Sometimes Ruby apps shell out to command-line executables, but there is no
|
4
|
+
standard way to ensure those underlying dependencies are met. Users usually
|
5
|
+
find out via a nasty stack-trace and whatever wasn't captured on stderr.
|
6
|
+
|
7
|
+
`Cliver` is a simple gem that provides an easy way to make assertions about
|
8
|
+
command-line dependencies. Under the covers, it uses [rubygems/requirements][]
|
9
|
+
so it supports the version requirements you're used to providing in your
|
10
|
+
gemspec.
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
Cliver.assert('subl') # no version requirements
|
16
|
+
Cliver.assert('bzip2', '~> 1.0.6') # one version requirement
|
17
|
+
Cliver.assert('racc', '>= 1.0', '< 1.4.9') # many version requirements
|
18
|
+
```
|
19
|
+
|
20
|
+
If the executable can't be found on your path at all, a
|
21
|
+
`Cliver::Assertion::DependencyNotFound` exception is raised; if the version
|
22
|
+
reached does not meet the requirements, a `Cliver::Assertion::VersionMismatch`
|
23
|
+
exception is raised.
|
24
|
+
|
25
|
+
## Advanced Usage:
|
26
|
+
|
27
|
+
Some programs don't provide nice 'version 1.2.3' strings in their `--version`
|
28
|
+
output; `Cliver` lets you provide your own matcher, whose first group is the
|
29
|
+
string version.
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
Cliver.assert('python', '~> 1.7', version_matcher: /Python ([0-9.]+)/)
|
33
|
+
```
|
34
|
+
|
35
|
+
Other programs don't provide a standard `--version`; `Cliver` allows you to
|
36
|
+
provide your own arg:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
Cliver.assert('janky', '~> 10.1.alpha', version_arg: '--release-version')
|
40
|
+
```
|
41
|
+
|
42
|
+
It obeys all the same rules as `Gem::Requirement`, including pre-release
|
43
|
+
semantics.
|
44
|
+
|
45
|
+
[rubygems/requirements]: https://github.com/rubygems/rubygems/blob/master/lib/rubygems/requirement.rb
|
data/Rakefile
ADDED
data/cliver.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cliver/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'cliver'
|
8
|
+
spec.version = Cliver::VERSION
|
9
|
+
spec.authors = ['Ryan Biesemeyer']
|
10
|
+
spec.email = ['ryan@yaauie.com']
|
11
|
+
spec.description = 'Assertions for command-line dependencies'
|
12
|
+
spec.summary = 'Cross-platform version constraints for cli tools'
|
13
|
+
spec.homepage = 'https://www.github.com/yaauie/cliver'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($RS)
|
17
|
+
spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(/^(test|spec|features)\//)
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
spec.add_development_dependency 'rspec'
|
24
|
+
spec.add_development_dependency 'ruby-appraiser-reek'
|
25
|
+
spec.add_development_dependency 'ruby-appraiser-rubocop'
|
26
|
+
end
|
data/lib/cliver.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'open3'
|
3
|
+
require 'rubygems/requirement'
|
4
|
+
|
5
|
+
module Cliver
|
6
|
+
# The core of Cliver, Assertion is responsible for detecting the
|
7
|
+
# installed version of a binary and determining if it meets the requirements
|
8
|
+
class Assertion
|
9
|
+
VersionMismatch = Class.new(ArgumentError)
|
10
|
+
DependencyNotFound = Class.new(ArgumentError)
|
11
|
+
|
12
|
+
EXECUTABLE_PATTERN = /\A[a-z][a-zA-Z0-9\-_]*\z/.freeze
|
13
|
+
|
14
|
+
# Creates a new instance with the args and calls #assert.
|
15
|
+
# @see #assert
|
16
|
+
def self.assert!(*args)
|
17
|
+
new(*args).assert!
|
18
|
+
end
|
19
|
+
|
20
|
+
# @overload initialize(executable, *requirements, options = {})
|
21
|
+
# @param executable [String]
|
22
|
+
# @param requirements [Array<String>, String] splat of strings
|
23
|
+
# whose elements follow the pattern
|
24
|
+
# [<operator>] <version>
|
25
|
+
# Where <operator> is optional (default '='') and in the set
|
26
|
+
# '=', '!=', '>', '<', '>=', '<=', or '~>'
|
27
|
+
# And <version> is dot-separated integers with optional
|
28
|
+
# alphanumeric pre-release suffix
|
29
|
+
# @see Gem::Requirement::new
|
30
|
+
# @param options [Hash<Symbol,Object>]
|
31
|
+
# @options options [#match] :version_matcher
|
32
|
+
# @options options [String] :version_arg
|
33
|
+
def initialize(executable, *args)
|
34
|
+
options = args.last.kind_of?(Hash) ? args.pop : {}
|
35
|
+
raise ArgumentError, 'executable' unless executable[EXECUTABLE_PATTERN]
|
36
|
+
|
37
|
+
@executable = executable.dup.freeze
|
38
|
+
@requirement = Gem::Requirement.new(args)
|
39
|
+
@version_arg = options.fetch(:version_arg, '--version')
|
40
|
+
@version_matcher = options.fetch(:version_matcher,
|
41
|
+
/version ([0-9][.0-9a-z]+)/i)
|
42
|
+
end
|
43
|
+
|
44
|
+
# @raise [VersionMismatch] if installed version does not match requirement
|
45
|
+
# @raise [DependencyNotFound] if no installed version on your path
|
46
|
+
def assert!
|
47
|
+
version = installed_version
|
48
|
+
version || raise(DependencyNotFound, "#{@executable} missing.")
|
49
|
+
unless @requirement.satisfied_by?(version)
|
50
|
+
raise VersionMismatch,
|
51
|
+
"got #{version}, expected #{@requirement}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# @private
|
56
|
+
def installed_version
|
57
|
+
command = "which #{@executable} && #{@executable} #{@version_arg}"
|
58
|
+
command_out, _ = Open3.capture2e(command)
|
59
|
+
match = @version_matcher.match(command_out)
|
60
|
+
match && Gem::Version.new(match[1])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'cliver/assertion'
|
3
|
+
|
4
|
+
describe Cliver::Assertion do
|
5
|
+
let(:mismatch_exception) { Cliver::Assertion::VersionMismatch }
|
6
|
+
let(:missing_exception) { Cliver::Assertion::DependencyNotFound }
|
7
|
+
let(:requirements) { ['6.8'] }
|
8
|
+
let(:executable) { 'fubar' }
|
9
|
+
let(:assertion) { Cliver::Assertion.new(executable, *requirements) }
|
10
|
+
|
11
|
+
context 'when dependency found' do
|
12
|
+
before(:each) { assertion.stub(:installed_version) { version } }
|
13
|
+
|
14
|
+
# sampling of requirements; actual implementation
|
15
|
+
# is supplied by rubygems/requirement and well-tested there.
|
16
|
+
context '~>' do
|
17
|
+
let(:requirements) { ['~> 6.8'] }
|
18
|
+
context 'when version matches exactly' do
|
19
|
+
let(:version) { Gem::Version.new('6.8') }
|
20
|
+
it 'should not raise' do
|
21
|
+
expect { assertion.assert! }.to_not raise_exception
|
22
|
+
end
|
23
|
+
end
|
24
|
+
context 'when major matches, and minor too low' do
|
25
|
+
let(:version) { Gem::Version.new('6.7') }
|
26
|
+
it 'should raise' do
|
27
|
+
expect { assertion.assert! }.to raise_exception mismatch_exception
|
28
|
+
end
|
29
|
+
end
|
30
|
+
context 'when major matches, and minor bumped' do
|
31
|
+
let(:version) { Gem::Version.new('6.13') }
|
32
|
+
it 'should not raise' do
|
33
|
+
expect { assertion.assert! }.to_not raise_exception
|
34
|
+
end
|
35
|
+
end
|
36
|
+
context 'when major too high' do
|
37
|
+
let(:version) { Gem::Version.new('7.0') }
|
38
|
+
it 'should raise' do
|
39
|
+
expect { assertion.assert! }.to raise_exception mismatch_exception
|
40
|
+
end
|
41
|
+
end
|
42
|
+
context 'patch version present' do
|
43
|
+
let(:version) { Gem::Version.new('6.8.1') }
|
44
|
+
it 'should not raise' do
|
45
|
+
expect { assertion.assert! }.to_not raise_exception
|
46
|
+
end
|
47
|
+
end
|
48
|
+
context 'pre-release of version that matches' do
|
49
|
+
let(:version) { Gem::Version.new('6.8.a') }
|
50
|
+
it 'should raise' do
|
51
|
+
version.should be_prerelease
|
52
|
+
expect { assertion.assert! }.to raise_exception mismatch_exception
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'multi [>=,<]' do
|
58
|
+
let(:requirements) { ['>= 1.1.4', '< 3.1'] }
|
59
|
+
context 'matches both' do
|
60
|
+
let(:version) { Gem::Version.new('2.0') }
|
61
|
+
it 'should not raise' do
|
62
|
+
expect { assertion.assert! }.to_not raise_exception
|
63
|
+
end
|
64
|
+
end
|
65
|
+
context 'fails one' do
|
66
|
+
let(:version) { Gem::Version.new('3.1') }
|
67
|
+
it 'should raise' do
|
68
|
+
expect { assertion.assert! }.to raise_exception mismatch_exception
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'none' do
|
74
|
+
let(:requirements) { [] }
|
75
|
+
let(:version) { Gem::Version.new('3.1') }
|
76
|
+
it 'should not raise' do
|
77
|
+
expect { assertion.assert! }.to_not raise_exception
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'when dependency not found' do
|
83
|
+
before(:each) { assertion.stub(:installed_version) { nil } }
|
84
|
+
|
85
|
+
it 'should raise' do
|
86
|
+
expect { assertion.assert! }.to raise_exception missing_exception
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/spec/cliver_spec.rb
ADDED
metadata
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cliver
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ryan Biesemeyer
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-06-18 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.3'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.3'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: ruby-appraiser-reek
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: ruby-appraiser-rubocop
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
description: Assertions for command-line dependencies
|
95
|
+
email:
|
96
|
+
- ryan@yaauie.com
|
97
|
+
executables: []
|
98
|
+
extensions: []
|
99
|
+
extra_rdoc_files: []
|
100
|
+
files:
|
101
|
+
- .githooks/pre-commit/ruby-appraiser
|
102
|
+
- .gitignore
|
103
|
+
- Gemfile
|
104
|
+
- LICENSE.txt
|
105
|
+
- README.md
|
106
|
+
- Rakefile
|
107
|
+
- cliver.gemspec
|
108
|
+
- lib/cliver.rb
|
109
|
+
- lib/cliver/assertion.rb
|
110
|
+
- lib/cliver/version.rb
|
111
|
+
- spec/cliver/assertion_spec.rb
|
112
|
+
- spec/cliver_spec.rb
|
113
|
+
homepage: https://www.github.com/yaauie/cliver
|
114
|
+
licenses:
|
115
|
+
- MIT
|
116
|
+
post_install_message:
|
117
|
+
rdoc_options: []
|
118
|
+
require_paths:
|
119
|
+
- lib
|
120
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
|
+
none: false
|
128
|
+
requirements:
|
129
|
+
- - ! '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
requirements: []
|
133
|
+
rubyforge_project:
|
134
|
+
rubygems_version: 1.8.24
|
135
|
+
signing_key:
|
136
|
+
specification_version: 3
|
137
|
+
summary: Cross-platform version constraints for cli tools
|
138
|
+
test_files:
|
139
|
+
- spec/cliver/assertion_spec.rb
|
140
|
+
- spec/cliver_spec.rb
|
141
|
+
has_rdoc:
|