Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How Does Bundle Install Work?

How Does Bundle Install Work?

Bundler is part of every ruby developers workflow but have you ever wondered what happens when you run bundle install? We'll take a look at the parts that make up Bundler such as the Bundler Definition, CLI, RubyGems and the version resolver. We'll also take a quick look at the upcoming changes for Bundler 2

Colby Swandale

August 30, 2017
Tweet

More Decks by Colby Swandale

Other Decks in Technology

Transcript

  1. How Does Bundle
    Install Work?

    View Slide

  2. › gem install bundler
    Successfully installed bundler-1.15.4
    Parsing documentation for bundler-1.15.4

    View Slide

  3. › bundle install
    Using bundler 1.15.4
    Using diff-lcs 1.3
    Using rack 2.0.3
    Using rspec-support 3.6.0
    Using rspec-core 3.6.0
    Using rspec-expectations 3.6.0
    Using rspec-mocks 3.6.0
    Using rspec 3.6.0
    Bundle complete! 2 Gemfile dependencies, 8 gems now installed.
    Use `bundle info [gemname]` to see where a bundled gem is
    installed.

    View Slide

  4. lib/bundler/cli.rb

    View Slide

  5. # lib/bundler/cli.rb
    method_option "with", :type => :array..
    def install(options)
    require "bundler/cli/install"
    Install.new(options.dup).run
    end

    View Slide

  6. $ ls lib/bundler/cli
    add.rb common.rb gem.rb issue.rb package.rb update.rb
    binstubs.rb config.rb info.rb list.rb platform.rb viz.rb
    cache.rb console.rb init.rb lock.rb plugin.rb check.rb
    doctor.rb inject.rb open.rb pristine.rb clean.rb exec.rb
    install.rb outdated.rb show.rb

    View Slide

  7. # lib/bundler/cli/install.rb

    module Bundler
    class CLI::Install
    attr_reader :options
    def initialize(options)
    @options = options
    end
    def run
    Bundler.ui.level = "error" if options[:quiet]
    warn_if_root
    normalize_groups
    ..

    View Slide

  8. # lib/bunler/cli/install.rb
    definition = Bundler.definition
    definition.validate_runtime!

    View Slide

  9. Definition

    View Slide

  10. Holds all the information
    and operations about your
    Gemfile & Gemfile.lock
    Definition
    Deps
    Sources
    Groups
    Platforms Runtime

    View Slide

  11. What gems are not
    installed?

    View Slide

  12. Have any new gems been
    added since the last
    bundle install?

    View Slide

  13. Gemfile & Gemfile.lock

    View Slide

  14. # Gemfile
    source "https://rubygems.org"
    gem 'rack', '~> 2.0'

    View Slide

  15. source 'https://rubygems.org'
    gem "rails", "4.2.8"
    gem "addressable", "2.4.0" if RUBY_VERSION < "2.0"
    require 'erb'
    require 'yaml'
    database_file = File.join(File.dirname(__FILE__), "config/database.yml")
    if File.exist?(database_file)
    database_config = YAML::load(ERB.new(IO.read(database_file)).result)
    adapters = database_config.values.map {|c| c['adapter']}.compact.uniq
    if adapters.any?
    adapters.each do |adapter|
    case adapter
    when 'mysql2'
    gem "mysql2", "~> 0.4.6", :platforms => [:mri, :mingw, :x64_mingw]
    when /postgresql/
    gem "pg", "~> 0.18.1", :platforms => [:mri, :mingw, :x64_mingw]
    when /sqlite3/
    gem "sqlite3", (RUBY_VERSION < "2.0" && RUBY_PLATFORM =~ /mingw/ ? "1.3.12" : "~>1.3.12"),
    :platforms => [:mri, :mingw, :x64_mingw]
    when /sqlserver/
    gem "tiny_tds", (RUBY_VERSION >= "2.0" ? "~> 1.0.5" : "~> 0.7.0"), :platforms => [:mri, :mingw, :x64_mingw]
    gem "activerecord-sqlserver-adapter", :platforms => [:mri, :mingw, :x64_mingw]
    else
    warn("Unknown database adapter `#{adapter}` found in config/database.yml, use Gemfile.local to load your own database gems")
    end
    end
    else
    warn("No adapter found in config/database.yml, please configure it first")
    end
    else
    warn("Please configure your config/database.yml first")
    end

    View Slide

  16. DSL

    View Slide

  17. # lib/bundler/dsl.rb
    module Bundler
    class Dsl
    def self.evaluate(gemfile, lockfile, unlock)
    builder = new
    builder.eval_gemfile(gemfile)
    builder.to_definition(lockfile, unlock)
    end
    end
    end

    View Slide

  18. # Gemfile.lock
    GEM
    remote: https://rubygems.org/
    specs:
    rack (2.0.3)
    PLATFORMS
    ruby
    DEPENDENCIES
    rack (~> 2.0)
    BUNDLED WITH
    1.15.3

    View Slide

  19. Lockfile Parser

    View Slide

  20. # lib/bundler/lockfile_parser.rb
    lockfile.split(/(?:\r?\n)+/).each do |line|
    if SOURCE.include?(line)
    @state = :source
    parse_source(line)
    elsif line == DEPENDENCIES
    @state = :dependency
    elsif line == PLATFORMS
    @state = :platform
    elsif line == RUBY
    @state = :ruby
    elsif line == BUNDLED
    @state = :bundled_with
    elsif line =~ /^[^\s]/
    @state = nil
    elsif @state
    send("parse_#{@state}", line)
    end
    end

    View Slide

  21. $ bundle install Install
    run
    Definition
    CLI
    install
    DSL
    evaluate

    View Slide

  22. Installer

    View Slide

  23. Resolver

    View Slide

  24. # Gemfile
    source "https://rubygems.org"
    gem 'rack', '~> 2.0.0’

    View Slide

  25. GEM
    remote: https://rubygems.org/
    specs:
    actioncable (5.1.3)
    actionpack (= 5.1.3)
    nio4r (~> 2.0)
    nio4r (2.1.0)
    actionpack (5.1.3)
    rails (5.1.3)
    actioncable (= 5.1.3)
    DEPENDENCIES
    rails (~> 5.1.3)

    View Slide

  26. Full Resolve Gemfile.lock
    Is there a Gemfile.lock

    and is it up to date?

    View Slide

  27. › curl https://index.rubygems.org/versions | head -n 30
    - 1 05d0116933ba44b0b5d0ee19bfd35ccc
    .cat 0.0.1 631fd60a806eaf5026c86fff3155c289
    0mq 0.1.0,0.1.1,0.1.2,0.2.0,0.2.1,0.3.0,0.4.0,0.4.1,0.5.0,0.5.1,0.5.2,0.5.3 61
    0xffffff 0.0.1,0.1.0 0a4a9aeae24152cdb467be02f40482f9
    10to1-crack 0.1.1,0.1.2,0.1.3 e7218e76477e2137355d2e7ded094925
    1234567890_ 1.0,1.1 233e818c2db65d2dad9f9ea9a27b1a30
    12_hour_time 0.0.2,0.0.3,0.0.4 4e58bc03e301f704950410b713c20b69
    16watts-fluently 0.3.0,0.3.1 555088e2b18e97e0293cab1d90dbb0d2
    189seg 0.0.1 c4d329f7d3eb88b6e602358968be0242
    196demo 0.0.0 e00c558565f7b03a438fbd93d854b7de
    1_as_identity_function 1.0.0,1.0.1 bee2f0fbbc3c5c83008c0b8fc64cb168
    1and1 1.1 1853e4495b036ddc5da2035523d48f0d
    1hdoc 0.1.3,0.2.0,0.2.2,0.2.3,0.2.4 7076f29c196df12047a3700c4d6e5915
    1pass 0.1.0,0.1.1,0.1.2 d209547aae4b8f3d67123f18f738ac99
    21-day-challenge-countdown 0.1.0,0.1.1,0.1.2 57e8873fe713063f4e54e85bbbd709bb
    24games 0.2.1,0.2.2,0.2.3,0.2.4 9e4d164dec466b7fe285653289e8468f
    24point 0.0.1,0.0.2 80d1d0efb3811e31f40160237cc9c115
    2DArray 0.1.0,0.1.2,0.1.3,0.1.5,0.1.6 848ea9e77b596cad24688a44c57a25c3
    2Performant 0.0.1,0.0.2,0.0.3,0.0.4,0.0.5,0.0.6,0.0.7,0.0.8,0.1.0,0.1.1,0.1.3

    View Slide

  28. › curl https://index.rubygems.org/info/activerecord
    5.1.0 activemodel:= 5.1.0,activesupport:= 5.1.0,arel:~> 8.0|checksum:b57713e8d
    5.0.3 activemodel:= 5.0.3,activesupport:= 5.0.3,arel:~> 7.0|checksum:6a38454ba
    5.1.1 activemodel:= 5.1.1,activesupport:= 5.1.1,arel:~> 8.0|checksum:61c0dba29
    4.2.9.rc1 activemodel:= 4.2.9.rc1,activesupport:= 4.2.9.rc1,arel:~> 6.0|checks
    5.0.4.rc1 activemodel:= 5.0.4.rc1,activesupport:= 5.0.4.rc1,arel:~> 7.0|checks
    5.0.4 activemodel:= 5.0.4,activesupport:= 5.0.4,arel:~> 7.0|checksum:d82ba1b35
    4.2.9.rc2 activemodel:= 4.2.9.rc2,activesupport:= 4.2.9.rc2,arel:~> 6.0|checks
    5.1.2.rc1 activemodel:= 5.1.2.rc1,activesupport:= 5.1.2.rc1,arel:~> 8.0|checks
    4.2.9 activemodel:= 4.2.9,activesupport:= 4.2.9,arel:~> 6.0|checksum:0be77a1f7
    5.1.2 activemodel:= 5.1.2,activesupport:= 5.1.2,arel:~> 8.0|checksum:a3757003b
    5.1.3.rc1 activemodel:= 5.1.3.rc1,activesupport:= 5.1.3.rc1,arel:~> 8.0|checks
    5.0.5.rc1 activemodel:= 5.0.5.rc1,activesupport:= 5.0.5.rc1,arel:~> 7.0|checks
    5.1.3.rc2 activemodel:= 5.1.3.rc2,activesupport:= 5.1.3.rc2,arel:~> 8.0|checks
    5.0.5.rc2 activemodel:= 5.0.5.rc2,activesupport:= 5.0.5.rc2,arel:~> 7.0|checks
    5.0.5 activemodel:= 5.0.5,activesupport:= 5.0.5,arel:~> 7.0|checksum:89651a138
    5.1.3.rc3 activemodel:= 5.1.3.rc3,activesupport:= 5.1.3.rc3,arel:~> 8.0|checks
    5.1.3 activemodel:= 5.1.3,activesupport:= 5.1.3,arel:~> 8.0|checksum:e83b04f42
    5.0.6.rc1 activemodel:= 5.0.6.rc1,activesupport:= 5.0.6.rc1,arel:~> 7.0|checks
    5.1.4.rc1 activemodel:= 5.1.4.rc1,activesupport:= 5.1.4.rc1,arel:~> 8.0|checks

    View Slide

  29. CocoaPods/Molinillo

    View Slide

  30. Installing Gems

    View Slide

  31. Git RubyGem Path

    View Slide

  32. Git RubyGem Path

    View Slide

  33. Git RubyGem Path

    View Slide

  34. # lib/bundler/source/rubygems.rb
    installed_spec = Bundler::RubyGemsGemInstaller.at(path,
    :install_dir => install_path.to_s,
    :bin_dir => bin_path.to_s,
    :ignore_dependencies => true,
    :wrappers => true,
    :env_shebang => true,
    :build_args => opts[:build_args],
    :bundler_expected_checksum => spec.respond_to?(:checksum) && spec.checksum,
    :bundler_extension_cache_path => extension_cache_path(spec)
    ).install

    View Slide

  35. Congrats! You’re gems
    are now installed

    View Slide

  36. http://bit.ly/2whDvhB

    View Slide

  37. Bundler 2

    View Slide

  38. Gemfile ➡ gems.rb
    Gemfile.lock ➡ gems.locked

    View Slide

  39. Removed commands
    show, console, viz, package

    View Slide

  40. Ruby Support
    Minimum Ruby 2.3 with RubyGems 2.5.0

    View Slide

  41. Global gem & extension cache
    Much quicker bundle install

    View Slide

  42. And much more!
    https://github.com/bundler/rfcs/pull/6

    View Slide

  43. 0xColby
    colby-swandale
    slack.bundler.io#bundler

    View Slide

  44. bundler.io

    View Slide

  45. bundler/bundler

    View Slide

  46. Bundler ❤ Contributors
    %&'()*$%&()*$%&+'(
    $%&& +)*,
    (*)$&($%&'&'*()*&($
    (*)$'*+)*&$%&($%&'
    $+&'()*$()*$%&'()*
    (*)$'*()*+($%-($%&'
    $'*($%'(*)()*&($%

    View Slide

  47. View Slide

  48. View Slide

  49. Thanks!

    View Slide