smarter_bundler 0.1.0
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/.gitignore +13 -0
- data/.travis.yml +35 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/Rakefile +1 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/test +61 -0
- data/exe/smarter_bundle +8 -0
- data/lib/smarter_bundler.rb +7 -0
- data/lib/smarter_bundler/bundle.rb +63 -0
- data/lib/smarter_bundler/gemfile.rb +57 -0
- data/lib/smarter_bundler/shell.rb +22 -0
- data/lib/smarter_bundler/version.rb +3 -0
- data/smarter_bundler.gemspec +28 -0
- metadata +118 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
sudo: false
|
2
|
+
language: ruby
|
3
|
+
|
4
|
+
before_install:
|
5
|
+
- gem update --system $RUBYGEMS_VERSION
|
6
|
+
- gem --version
|
7
|
+
- gem install bundler -v ${BUNDLER_VERSION:-1.16.0}
|
8
|
+
- bundle --version
|
9
|
+
- mkdir -p tmp/bundle
|
10
|
+
|
11
|
+
script: ./bin/test
|
12
|
+
|
13
|
+
matrix:
|
14
|
+
fast_finish: true
|
15
|
+
|
16
|
+
matrix:
|
17
|
+
allow_failures:
|
18
|
+
- rvm: 1.8.7
|
19
|
+
gemfile: test/rails_2.3_lts.gemfile
|
20
|
+
env: RUBYGEMS_VERSION=1.8.25 RAILS_VERSION=2.3
|
21
|
+
|
22
|
+
include:
|
23
|
+
- rvm: 1.8.7
|
24
|
+
env: RUBYGEMS_VERSION=1.8.25
|
25
|
+
|
26
|
+
- rvm: 1.9.3
|
27
|
+
|
28
|
+
- rvm: 2.2.1
|
29
|
+
|
30
|
+
- rvm: 2.2.9
|
31
|
+
|
32
|
+
- rvm: 2.3.6
|
33
|
+
|
34
|
+
- rvm: 2.4.3
|
35
|
+
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018 Ian Heggie
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# SmarterBundler
|
2
|
+
|
3
|
+
Enhances bundler by adjusting Gemfile when correctable errors are found
|
4
|
+
|
5
|
+
It is primarily aimed at resolving ruby version conflicts where a gem now requires a later ruby version.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Install it using:
|
10
|
+
|
11
|
+
$ gem install smarter_bundler
|
12
|
+
|
13
|
+
Do not install it via Gemfile, as it needs to execute even if bundle can't install/update the gems in Gemfile
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
Use smarter_bundle instead of the bundle command when installing or upgrading gems.
|
18
|
+
|
19
|
+
## Notes
|
20
|
+
|
21
|
+
The algorithm is simplistic - when an error of the form
|
22
|
+
`Gem::RuntimeRequirementNotMetError: <gem_name> requires Ruby version`
|
23
|
+
is found, as well as
|
24
|
+
`An error occurred while installing <gem_name> (<gem_version>), and Bundler cannot continue`
|
25
|
+
Then it adjusts the Gemfile to require a version of that gem lower than the one that produced the error.
|
26
|
+
|
27
|
+
It will attempt upto 100 times to adjust the Gemfile before giving up.
|
28
|
+
|
29
|
+
Once the Gemfile has been adjusted, commit it into your source repository so that it does not need to be used again.
|
30
|
+
|
31
|
+
## Development
|
32
|
+
|
33
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
34
|
+
|
35
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
36
|
+
|
37
|
+
## Contributing
|
38
|
+
|
39
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/ianheggie/smarter_bundler.
|
40
|
+
|
41
|
+
## License
|
42
|
+
|
43
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "smarter_bundler"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/bin/test
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -e
|
4
|
+
|
5
|
+
ruby_version=`ruby -e 'puts RUBY_VERSION'`
|
6
|
+
psych_ver='< 3.0'
|
7
|
+
case "$ruby_version" in
|
8
|
+
1.8.7)
|
9
|
+
dirs='rails30 rails31 rails32'
|
10
|
+
psych_ver=''
|
11
|
+
;;
|
12
|
+
1.9.2)
|
13
|
+
dirs='rails30 rails31 rails32'
|
14
|
+
;;
|
15
|
+
1.9.3)
|
16
|
+
dirs='rails30 rails31 rails32 rails40 rails41 rails42'
|
17
|
+
;;
|
18
|
+
2.0*)
|
19
|
+
dirs='rails32 rails40 rails41 rails42'
|
20
|
+
;;
|
21
|
+
2.1*)
|
22
|
+
dirs='rails41 rails42'
|
23
|
+
;;
|
24
|
+
2.2.[01]*)
|
25
|
+
dirs='rails42'
|
26
|
+
;;
|
27
|
+
2.2.2)
|
28
|
+
dirs='rails32 rails42 rails50 rails51'
|
29
|
+
psych_ver='> 2.0'
|
30
|
+
;;
|
31
|
+
2.2.[2-9]*|2.[3-9]*)
|
32
|
+
dirs='rails50 rails51'
|
33
|
+
gem install psych
|
34
|
+
psych_ver='> 2.0'
|
35
|
+
;;
|
36
|
+
esac
|
37
|
+
if [ -n "$psych_ver" ]; then
|
38
|
+
if gem list | egrep 'psych.*[^\.0-9][2-9]\.'
|
39
|
+
then
|
40
|
+
echo Psych installed
|
41
|
+
else
|
42
|
+
echo Running: gem install psych -v "$psych_ver"
|
43
|
+
gem install psych -v "$psych_ver"
|
44
|
+
fi
|
45
|
+
fi
|
46
|
+
|
47
|
+
unset BUNDLE_GEMFILE
|
48
|
+
for d in $dirs
|
49
|
+
do
|
50
|
+
(
|
51
|
+
echo "======================================================================"
|
52
|
+
echo "Testing test/$d/Gemfile using ruby $ruby_version ..."
|
53
|
+
export d
|
54
|
+
cd test/$d
|
55
|
+
rm -f Gemfile.lock
|
56
|
+
cp -f Gemfile.default Gemfile
|
57
|
+
# Exclude debugger - it breaks for many varied reasons on travis
|
58
|
+
../../exe/smarter_bundle --without debug
|
59
|
+
bundle exec ruby -e 'puts "Test of #{ENV['"'"'d'"'"']}/Gemfile with ruby #{RUBY_VERSION} passed at #{Time.now}!"'
|
60
|
+
)
|
61
|
+
done
|
data/exe/smarter_bundle
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module SmarterBundler
|
4
|
+
|
5
|
+
class Bundle
|
6
|
+
include SmarterBundler::Shell
|
7
|
+
|
8
|
+
def run(bundle_args)
|
9
|
+
puts "Smarter Bundler will recursively install your gems and output the successful bundler output. This may take a while."
|
10
|
+
count = 0
|
11
|
+
gemfile = SmarterBundler::Gemfile.new
|
12
|
+
previous_failure = [ ]
|
13
|
+
result = nil
|
14
|
+
while count < 100
|
15
|
+
result = call_bundle(bundle_args)
|
16
|
+
failed_gem_and_version = parse_output(result)
|
17
|
+
if failed_gem_and_version
|
18
|
+
if previous_failure == failed_gem_and_version
|
19
|
+
puts "Aborting: Stuck trying to install the same gem and version!"
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
previous_failure = failed_gem_and_version
|
23
|
+
if install_failed_gem failed_gem_and_version
|
24
|
+
puts "Retrying seems to have fixed the problem"
|
25
|
+
elsif ruby_version_clash(result) && gemfile.restrict_gem_version(*failed_gem_and_version)
|
26
|
+
gemfile.save
|
27
|
+
count += 1
|
28
|
+
else
|
29
|
+
puts "Aborting: Unable to fix installation of gems"
|
30
|
+
exit 2
|
31
|
+
end
|
32
|
+
else
|
33
|
+
break
|
34
|
+
end
|
35
|
+
end
|
36
|
+
puts "#{count} adjustments where made to the Gemfile"
|
37
|
+
exit result ? result.status.to_i : 3
|
38
|
+
end
|
39
|
+
|
40
|
+
def call_bundle(bundle_args)
|
41
|
+
shell "bundle #{bundle_args}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def install_failed_gem(failed_gem_and_version)
|
45
|
+
shell? "gem install '#{failed_gem_and_version[0]}' -v '#{failed_gem_and_version[1]}'"
|
46
|
+
end
|
47
|
+
|
48
|
+
def ruby_version_clash(result)
|
49
|
+
result.output.select{|l| l =~ /requires Ruby version/}.any?
|
50
|
+
end
|
51
|
+
|
52
|
+
def parse_output(result)
|
53
|
+
result.output.each do |line|
|
54
|
+
if line =~ /Make sure that `gem install (\S+) -v '(\S+)'`/
|
55
|
+
return [$1, $2]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module SmarterBundler
|
2
|
+
|
3
|
+
class Gemfile
|
4
|
+
|
5
|
+
attr_reader :filename, :contents, :changed
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@filename = ENV['BUNDLE_GEMFILE'] || 'Gemfile'
|
9
|
+
@contents = [ ]
|
10
|
+
File.open(@filename, 'r').each do |line|
|
11
|
+
line.chomp
|
12
|
+
@contents << line
|
13
|
+
end
|
14
|
+
@changed = false
|
15
|
+
end
|
16
|
+
|
17
|
+
def restrict_gem_version gem, version_limit
|
18
|
+
if @contents.select{|line| line =~ /^\s*gem\s+['"]#{gem}['"]/}.empty?
|
19
|
+
@contents << "gem '#{gem}', '>=0'"
|
20
|
+
end
|
21
|
+
adjusted = false
|
22
|
+
@contents.map! do |line|
|
23
|
+
if line =~ /^(\s*gem\s+['"]#{gem}['"])(\s*,\*['"]([^'"]*)['"])?(.*)$/
|
24
|
+
gem_and_name = $1
|
25
|
+
rest_of_line = $4
|
26
|
+
version = $3.to_s
|
27
|
+
new_version = version.sub(/<=?\s*[^,\s]+/, '').sub(/^\s*,\s*/, '').sub(/\s*,\s*$/, '') + (version == '' ? '' : ', ') + "< #{version_limit}"
|
28
|
+
if new_version != version
|
29
|
+
@changed = true
|
30
|
+
rest_of_line.sub(/#.*/, '')
|
31
|
+
rest_of_line << ' # REQUIRED - Added by SmarterBundler'
|
32
|
+
line = "#{gem_and_name}, '#{new_version}'#{rest_of_line}"
|
33
|
+
puts "Changed Gemfile line to: #{line}"
|
34
|
+
line
|
35
|
+
else
|
36
|
+
puts "Unable to change version for #{gem}"
|
37
|
+
line
|
38
|
+
end
|
39
|
+
else
|
40
|
+
line
|
41
|
+
end
|
42
|
+
end
|
43
|
+
@changed
|
44
|
+
end
|
45
|
+
|
46
|
+
def save
|
47
|
+
if @changed
|
48
|
+
File.open("#{@filename}.new", 'w') do |file|
|
49
|
+
file.puts *@contents
|
50
|
+
end
|
51
|
+
FileUtils.move "#{@filename}.new", @filename, :force => true
|
52
|
+
@changed = false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module SmarterBundler
|
2
|
+
module Shell
|
3
|
+
def shell(command)
|
4
|
+
puts '',"+ #{command}"
|
5
|
+
output = [ ]
|
6
|
+
IO.popen("#{command} 2>&1") do |io|
|
7
|
+
while line = io.gets
|
8
|
+
puts line.chomp
|
9
|
+
output << line.chomp
|
10
|
+
end
|
11
|
+
io.close
|
12
|
+
end
|
13
|
+
puts "Command returned status: #{$?.to_i} (#{$?.success? ? 'success' : 'fail'})"
|
14
|
+
Struct.new(:status, :output).new($?, output)
|
15
|
+
end
|
16
|
+
|
17
|
+
def shell?(command)
|
18
|
+
result = shell(command)
|
19
|
+
result.status.success?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path("../lib", __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require "smarter_bundler/version"
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "smarter_bundler"
|
9
|
+
spec.version = SmarterBundler::VERSION
|
10
|
+
spec.authors = ["Ian Heggie"]
|
11
|
+
spec.email = ["ian@heggie.biz"]
|
12
|
+
|
13
|
+
spec.summary = %q{Enhances bundler by adjusting Gemfile when correctable errors are found}
|
14
|
+
spec.description = %q{The smarter_bundle retries installing gems, and if that fails it tries installing an earlier version by adjusting the Gemfile}
|
15
|
+
spec.homepage = "https://github.com/ianheggie/smarter_bundler"
|
16
|
+
spec.license = "MIT"
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
19
|
+
f.match(%r{^(test|spec|features)/})
|
20
|
+
end
|
21
|
+
spec.bindir = "exe"
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
+
spec.require_paths = ["lib"]
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
26
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
27
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: smarter_bundler
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ian Heggie
|
9
|
+
autorequire:
|
10
|
+
bindir: exe
|
11
|
+
cert_chain: []
|
12
|
+
date: 2018-03-12 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.16'
|
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.16'
|
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: '10.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: '10.0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: minitest
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '5.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: '5.0'
|
62
|
+
description: The smarter_bundle retries installing gems, and if that fails it tries
|
63
|
+
installing an earlier version by adjusting the Gemfile
|
64
|
+
email:
|
65
|
+
- ian@heggie.biz
|
66
|
+
executables:
|
67
|
+
- smarter_bundle
|
68
|
+
extensions: []
|
69
|
+
extra_rdoc_files: []
|
70
|
+
files:
|
71
|
+
- .gitignore
|
72
|
+
- .travis.yml
|
73
|
+
- Gemfile
|
74
|
+
- LICENSE.txt
|
75
|
+
- README.md
|
76
|
+
- Rakefile
|
77
|
+
- bin/console
|
78
|
+
- bin/setup
|
79
|
+
- bin/test
|
80
|
+
- exe/smarter_bundle
|
81
|
+
- lib/smarter_bundler.rb
|
82
|
+
- lib/smarter_bundler/bundle.rb
|
83
|
+
- lib/smarter_bundler/gemfile.rb
|
84
|
+
- lib/smarter_bundler/shell.rb
|
85
|
+
- lib/smarter_bundler/version.rb
|
86
|
+
- smarter_bundler.gemspec
|
87
|
+
homepage: https://github.com/ianheggie/smarter_bundler
|
88
|
+
licenses:
|
89
|
+
- MIT
|
90
|
+
post_install_message:
|
91
|
+
rdoc_options: []
|
92
|
+
require_paths:
|
93
|
+
- lib
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
segments:
|
101
|
+
- 0
|
102
|
+
hash: 1256460857645242723
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ! '>='
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
segments:
|
110
|
+
- 0
|
111
|
+
hash: 1256460857645242723
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 1.8.23.2
|
115
|
+
signing_key:
|
116
|
+
specification_version: 3
|
117
|
+
summary: Enhances bundler by adjusting Gemfile when correctable errors are found
|
118
|
+
test_files: []
|