smarter_bundler 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ Gemfile.new
2
+ Gemfile.lock
3
+ /.bundle/
4
+ /.yardoc
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
11
+ /test/rails*/.bundle
12
+ /test/rails*/Gemfile
13
+ /test/rails*/Gemfile.lock
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
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in smarter_bundler.gemspec
6
+ gemspec
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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ path = File.expand_path('../../lib', __FILE__)
4
+ $LOAD_PATH.unshift(path) unless $LOAD_PATH.include?(path)
5
+
6
+ require 'smarter_bundler'
7
+
8
+ SmarterBundler::Bundle.new.run ARGV.join(' ')
@@ -0,0 +1,7 @@
1
+ require "smarter_bundler/version"
2
+ require "smarter_bundler/shell"
3
+ require "smarter_bundler/bundle"
4
+ require "smarter_bundler/gemfile"
5
+
6
+ module SmarterBundler
7
+ end
@@ -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,3 @@
1
+ module SmarterBundler
2
+ VERSION = "0.1.0"
3
+ 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: []