bootboot 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f0ffd98be99f6485f9c7480c1af03185884bc7a3
4
+ data.tar.gz: 9c2f985c296d6c1ea93c06a49437b9fd670a1ad8
5
+ SHA512:
6
+ metadata.gz: 8992b0f398e049de5dc730d49f39ce35e908828168939583ff5d84362b6bc84d3193548c92f83117473a7887a6e5c22bb7d76c59aca0a52b3b9609b3dc518135
7
+ data.tar.gz: c4a6e8932482b1e5dd96f8e3e26e08de41e71f8cc38a0872d696a3702381073ad324440b3e95dde8709910907dfe0fa03937530e9a95287584ae82d6e43c33a3
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ sudo: false
2
+ cache: bundler
3
+ language: ruby
4
+ before_install:
5
+ - gem update bundler
6
+ notifications:
7
+ email: false
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ gemspec
6
+
7
+ group :deployment do
8
+ gem 'package_cloud'
9
+ gem 'rake'
10
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,49 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ bootboot (0.1.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ domain_name (0.5.20180417)
10
+ unf (>= 0.0.5, < 1.0.0)
11
+ highline (1.6.20)
12
+ http-cookie (1.0.3)
13
+ domain_name (~> 0.5)
14
+ json_pure (1.8.1)
15
+ mime-types (3.2.2)
16
+ mime-types-data (~> 3.2015)
17
+ mime-types-data (3.2018.0812)
18
+ minitest (5.11.3)
19
+ netrc (0.11.0)
20
+ package_cloud (0.3.05)
21
+ highline (= 1.6.20)
22
+ json_pure (= 1.8.1)
23
+ rainbow (= 2.2.2)
24
+ rest-client (~> 2.0)
25
+ thor (~> 0.18)
26
+ rainbow (2.2.2)
27
+ rake
28
+ rake (10.5.0)
29
+ rest-client (2.0.2)
30
+ http-cookie (>= 1.0.2, < 2.0)
31
+ mime-types (>= 1.16, < 4.0)
32
+ netrc (~> 0.8)
33
+ thor (0.20.3)
34
+ unf (0.1.4)
35
+ unf_ext
36
+ unf_ext (0.0.7.5)
37
+
38
+ PLATFORMS
39
+ ruby
40
+
41
+ DEPENDENCIES
42
+ bootboot!
43
+ bundler (~> 1.17)
44
+ minitest (~> 5.0)
45
+ package_cloud
46
+ rake
47
+
48
+ BUNDLED WITH
49
+ 1.17.1
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Shopify
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,64 @@
1
+ ## 👢👢 Bootboot
2
+
3
+ Introduction
4
+ ------------
5
+ Bootboot is a [Bundler plugin](https://bundler.io/v1.17/guides/bundler_plugins.html#what-is-a-plugin) meant to help dual boot your ruby application.
6
+
7
+ #### What is "Dual booting"?
8
+ In this context, dual boot is the process of booting your application with a different set of dependencies. This technique has become very popular in the Ruby community to help applications safely upgrade dependencies. If you want to learn more about it, have a look at this [conference talk](https://www.youtube.com/watch?v=I-2Xy3RS1ns&t=368s) by @rafaelfranca.
9
+
10
+ There are two schools on how to dual boot your app, each having advantages and disadvantages.
11
+ 1) Use three Gemfiles. One with current production dependencies, a second with an alternate "next" set of dependencies, and a third containing the dependencies that both Gemfiles have in common.
12
+ ```ruby
13
+ # Gemfile.common
14
+ gem "some_gem"
15
+
16
+ # Gemfile
17
+ gem "rails", "~> 5.1.0"
18
+ eval_gemfile "Gemfile.common"
19
+
20
+ # Gemfile.next
21
+ gem "rails", "~> 5.2.0"
22
+ eval_gemfile "Gemfile.common"
23
+ ```
24
+
25
+ 2) Have a single Gemfile containing dependencies separated by environment sensitive `if` statements.
26
+ ```ruby
27
+ # Gemfile
28
+
29
+ if ENV['DEPENDENCIES_NEXT']
30
+ gem "rails", "~> 5.2.0"
31
+ else
32
+ gem "rails", "~> 5.1.0"
33
+ end
34
+ ```
35
+ -----------------------------
36
+ The former doesn't require any Bundler workaround but you need to deal with three Gemfiles and the [confusion](https://github.com/bundler/bundler/issues/6777#issuecomment-436771340) that comes with it.
37
+ The latter is the approach we decided to take at Shopify and it worked very well for us for multiple years.
38
+
39
+ No matter what approach you decide to take, you'll need to create tooling to ensure that all the lockfiles are in sync whenever a developer updates a dependency.
40
+
41
+ Bootboot is only useful if you decide to follow the second approach. It creates the required tooling as well as the Bundler workaround needed to enable dual booting.
42
+
43
+
44
+
45
+ Installation
46
+ ------------
47
+ 1) In your Gemfile, add this
48
+ ```ruby
49
+ plugin 'bootboot', '~> 0.1.1`
50
+ ```
51
+ 2) Run `bundle install && bundle bootboot`
52
+ 3) You're done. Commit the Gemfile and the Gemfile_next.lock
53
+
54
+ Dual boot it!
55
+ ------------
56
+ If you want to boot using the dependencies from the `Gemfile_next.lock`, run any bundler command prefixed with the `DEPENDENCIES_NEXT=1` ENV variable. I.e. `DEPENDENCIES_NEXT=1 bundle exec irb`.
57
+
58
+
59
+ Keep` the Gemfile_next.lock` in sync
60
+ ------------
61
+ When a developer bumps or adds a dependency, Bootboot will ensure that the `Gemfile_next.lock` snapshot gets updated.
62
+
63
+ **However, this feature is only available if you are on Bundler `>= 1.17`**
64
+ Other versions will trigger a warning message telling them that Bootboot can't automatically keep the `Gemfile_next.lock` in sync.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "bootboot"
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/bootboot.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "bootboot/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "bootboot"
7
+ spec.version = Bootboot::VERSION
8
+ spec.authors = ["Shopify"]
9
+ spec.email = ["rails@shopify.com"]
10
+
11
+ spec.summary = "Dualbooting your ruby app made easy."
12
+ spec.description = <<-EOM.gsub(/\W+/, ' ')
13
+ This gems removes you the overhead of monkeypatching your Gemfile in order to
14
+ dualboot your app using the Gemfile_next.lock strategy.
15
+ It also ensure that dependencies in the Gemfile.lock and Gemfile_next.lock are
16
+ in sync whenever someone updates a gem.
17
+ EOM
18
+ spec.homepage = "https://github.com/shopify/bootboot"
19
+ spec.license = "MIT"
20
+
21
+ spec.metadata["homepage_uri"] = spec.homepage
22
+ spec.metadata["source_code_uri"] = "https://github.com/shopify/bootboot"
23
+ spec.metadata["changelog_uri"] = "https://github.com/Shopify/bootboot/blob/master/CHANGELOG.md"
24
+
25
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
26
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
+ end
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_development_dependency "bundler", "~> 1.17"
31
+ spec.add_development_dependency "rake", "~> 10.0"
32
+ spec.add_development_dependency "minitest", "~> 5.0"
33
+ end
data/lib/bootboot.rb ADDED
@@ -0,0 +1,16 @@
1
+ require "bootboot/version"
2
+ require "bootboot/bundler_patch"
3
+
4
+ module Bootboot
5
+ GEMFILE = Bundler.default_gemfile
6
+ GEMFILE_LOCK = Pathname("#{GEMFILE}.lock")
7
+ GEMFILE_NEXT_LOCK = Pathname("#{GEMFILE}_next.lock")
8
+ DUALBOOT_NEXT = 'DEPENDENCIES_NEXT'
9
+ DUALBOOT_PREVIOUS = 'DEPENDENCIES_PREVIOUS'
10
+
11
+ autoload :GemfileNextAutoSync, 'bootboot/gemfile_next_auto_sync'
12
+ autoload :Command, 'bootboot/command'
13
+ end
14
+
15
+ Bootboot::GemfileNextAutoSync.new.setup
16
+ Bootboot::Command.new.setup
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DefinitionPatch
4
+ def initialize(wrong_lock, *args)
5
+ lockfile = if ENV['SKIP_BUNDLER_PATCH']
6
+ wrong_lock
7
+ else
8
+ Bootboot::GEMFILE_NEXT_LOCK
9
+ end
10
+
11
+ super(lockfile, *args)
12
+ end
13
+ end
14
+
15
+ module SharedHelpersPatch
16
+ def default_lockfile
17
+ Bootboot::GEMFILE_NEXT_LOCK
18
+ end
19
+ end
20
+
21
+ Bundler::Dsl.class_eval do
22
+ def enable_dual_booting
23
+ Bundler::Definition.prepend(DefinitionPatch)
24
+ Bundler::SharedHelpers.singleton_class.prepend(SharedHelpersPatch)
25
+ end
26
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+
5
+ module Bootboot
6
+ class Command < Bundler::Plugin::API
7
+ def setup
8
+ self.class.command('bootboot')
9
+ end
10
+
11
+ def exec(_cmd, _args)
12
+ FileUtils.cp(GEMFILE_LOCK, GEMFILE_NEXT_LOCK)
13
+
14
+ File.open(GEMFILE, 'a+') do |f|
15
+ f.write(<<-EOM)
16
+ Plugin.send(:load_plugin, 'bootboot') if Plugin.installed?('bootboot')
17
+
18
+ if ENV['DEPENDENCIES_NEXT']
19
+ enable_dual_booting if Plugin.installed?('bootboot')
20
+
21
+ # Add any gem you want here, they will be loaded only when running
22
+ # bundler command prefixed with `#{DUALBOOT_NEXT}=1`.
23
+ end
24
+ EOM
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bootboot
4
+ class GemfileNextAutoSync < Bundler::Plugin::API
5
+ def setup
6
+ check_bundler_version
7
+ opt_in
8
+ end
9
+
10
+ private
11
+
12
+ def check_bundler_version
13
+ self.class.hook("before-install-all") do
14
+ next if Bundler::VERSION >= "1.17.0" || !GEMFILE_NEXT_LOCK.exist?
15
+
16
+ Bundler.ui.warn(<<-EOM.gsub(/\s+/, " "))
17
+ Bootboot can't automatically update the Gemfile_next.lock because you are running
18
+ an older version of Bundler.
19
+
20
+ Update Bundler to 1.17.0 to discard this warning.
21
+ EOM
22
+ end
23
+ end
24
+
25
+ def opt_in
26
+ self.class.hook('before-install-all') do
27
+ @previous_lock = Bundler.default_lockfile.read
28
+ end
29
+
30
+ self.class.hook("after-install-all") do
31
+ current_definition = Bundler.definition
32
+
33
+ next if !GEMFILE_NEXT_LOCK.exist? ||
34
+ nothing_changed?(current_definition) ||
35
+ ENV[DUALBOOT_NEXT] ||
36
+ ENV[DUALBOOT_PREVIOUS]
37
+
38
+ update!(current_definition)
39
+ end
40
+ end
41
+
42
+ def nothing_changed?(current_definition)
43
+ current_definition.to_lock == @previous_lock
44
+ end
45
+
46
+ def update!(current_definition)
47
+ env = which_env
48
+ lock = which_lock
49
+
50
+ Bundler.ui.confirm("Updating the #{lock}")
51
+ ENV[env] = '1'
52
+ ENV['SKIP_BUNDLER_PATCH'] = '1'
53
+
54
+ unlock = current_definition.instance_variable_get(:@unlock)
55
+ definition = Bundler::Definition.build(GEMFILE, lock, unlock)
56
+ definition.resolve_remotely!
57
+ definition.lock(lock)
58
+ ensure
59
+ ENV.delete(env)
60
+ ENV.delete('SKIP_BUNDLER_PATCH')
61
+ end
62
+
63
+ def which_env
64
+ if Bundler.default_lockfile.to_s =~ /_next\.lock/
65
+ DUALBOOT_PREVIOUS
66
+ else
67
+ DUALBOOT_NEXT
68
+ end
69
+ end
70
+
71
+ def which_lock
72
+ if Bundler.default_lockfile.to_s =~ /_next\.lock/
73
+ GEMFILE_LOCK
74
+ else
75
+ GEMFILE_NEXT_LOCK
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,3 @@
1
+ module Bootboot
2
+ VERSION = "0.1.1"
3
+ end
data/plugins.rb ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/bootboot"
@@ -0,0 +1 @@
1
+ # Default config
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bootboot
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Shopify
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-11-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.17'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.17'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ description: " This gems removes you the overhead of monkeypatching your Gemfile in
56
+ order to dualboot your app using the Gemfile_next lock strategy It also ensure that
57
+ dependencies in the Gemfile lock and Gemfile_next lock are in sync whenever someone
58
+ updates a gem "
59
+ email:
60
+ - rails@shopify.com
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - ".gitignore"
66
+ - ".travis.yml"
67
+ - Gemfile
68
+ - Gemfile.lock
69
+ - LICENSE.txt
70
+ - README.md
71
+ - Rakefile
72
+ - bin/console
73
+ - bootboot.gemspec
74
+ - lib/bootboot.rb
75
+ - lib/bootboot/bundler_patch.rb
76
+ - lib/bootboot/command.rb
77
+ - lib/bootboot/gemfile_next_auto_sync.rb
78
+ - lib/bootboot/version.rb
79
+ - plugins.rb
80
+ - shipit.rubygems.yml
81
+ homepage: https://github.com/shopify/bootboot
82
+ licenses:
83
+ - MIT
84
+ metadata:
85
+ homepage_uri: https://github.com/shopify/bootboot
86
+ source_code_uri: https://github.com/shopify/bootboot
87
+ changelog_uri: https://github.com/Shopify/bootboot/blob/master/CHANGELOG.md
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 2.6.14
105
+ signing_key:
106
+ specification_version: 4
107
+ summary: Dualbooting your ruby app made easy.
108
+ test_files: []