prebundler 0.0.4 → 0.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 14809b2ea93fc2442016314d9f173db3be3e383f
4
- data.tar.gz: 4d6c78d6743dc6584adc689ee4f4b3b222084578
3
+ metadata.gz: a8f3573257ed6d3ae187bd8cc9b3e37ce6787373
4
+ data.tar.gz: 7780812efb908e7e533e1e7a3299b529f281bda5
5
5
  SHA512:
6
- metadata.gz: 957cbcdff72652ecdc09a8e29ec6725a5dfe05efef3ee538f21c266dc6a65bd42a2a602079abeeaa6340f0910923c724d43e538ef46ca8f3efc6538be18cda07
7
- data.tar.gz: 288c16b848d8909554198f7bee6fff33f67776303fc7d4890650d9e83326cc5da084342e53434473b702de9094b4a459f6678990486b615a1156d76f81d3bf4f
6
+ metadata.gz: 51e0f4e6e61305cd9535de52ca2bd3366694f63b99a0ea43d20ac084571815be95683a035698983ea0aa7eaeb4f14bf903f97aecb7b32988df1d4d296ba0834d
7
+ data.tar.gz: b1890c8532ddce82b3da898d5aae18d1c5da929b328fd460df090949310d5c70ec2d3cd3f7c98bdb1b2315fd44875e60b4594e2f91de7a87c115bb94683edbab
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ 0.5.0
2
+ ===
3
+ - Works for a repository with a significant number of dependencies (~ 400).
4
+ - Testing on staging server indicates bundle is installed correctly.
5
+
1
6
  0.0.4
2
7
  ===
3
8
  - Pass bundle path to `gem install`.
@@ -1,7 +1,8 @@
1
1
  require 'securerandom'
2
2
  require 'tmpdir'
3
3
  require 'parallel'
4
- require 'bundler'
4
+ require 'fileutils'
5
+ require 'yaml'
5
6
 
6
7
  module Prebundler
7
8
  module Cli
@@ -9,15 +10,34 @@ module Prebundler
9
10
  def run
10
11
  prepare
11
12
  install
13
+ update_bundle_config
14
+ generate_binstubs
12
15
  check
13
16
  end
14
17
 
15
18
  private
16
19
 
17
20
  def prepare
18
- gem_list.each do |_, gem_ref|
21
+ FileUtils.mkdir_p(bundle_path)
22
+
23
+ gem_list.each do |name, gem_ref|
19
24
  next if backend_file_list.include?(gem_ref.tar_file)
20
25
  next unless member_of_installable_group?(gem_ref)
26
+
27
+ # Edge case: installation could fail if dependencies of this
28
+ # gem ref haven't been installed yet. They should have been
29
+ # prepared by this point because of the tsort logic in the
30
+ # gemfile class, but we need to actually install them in
31
+ # order to install this current gem. A good example is nokogiri,
32
+ # which can't build its native extension without mini-portile2.
33
+ gem_ref.dependencies.each do |dep|
34
+ if gem_list.gems[dep]
35
+ install_gem_ref(gem_list.gems[dep])
36
+ else
37
+ out.puts "Oops, couldn't find dependency #{dep}"
38
+ end
39
+ end
40
+
21
41
  install_gem(gem_ref) if gem_ref.installable?
22
42
  end
23
43
  end
@@ -79,17 +99,51 @@ module Prebundler
79
99
  config.storage_backend.store_file(dest_file, gem_ref.tar_file)
80
100
  end
81
101
 
102
+ def update_bundle_config
103
+ file = Bundler.app_config_path.join('config').to_s
104
+ config = File.exist?(file) ? YAML.load_file(file) : {}
105
+ config['BUNDLE_WITH'] = with_groups.join(':') unless with_groups.empty?
106
+ config['BUNDLE_WITHOUT'] = without_groups.join(':') unless without_groups.empty?
107
+ File.write(file, YAML.dump(config))
108
+ end
109
+
110
+ def generate_binstubs
111
+ out.puts 'Generating binstubs...'
112
+
113
+ gems_with_executables = gem_list.gems.values.select do |gem_ref|
114
+ next false unless member_of_installable_group?(gem_ref)
115
+ !gem_ref.executables.empty?
116
+ end
117
+
118
+ return if gems_with_executables.empty?
119
+
120
+ system "bundle binstubs #{gems_with_executables.map(&:name).join(' ')}"
121
+ system "bundle binstubs --force bundler"
122
+ out.puts 'Done generating binstubs'
123
+ end
124
+
82
125
  def check
83
- system 'bundle check'
126
+ system "bundle check --gemfile #{gemfile_path}"
84
127
 
85
128
  if $?.exitstatus != 0
86
129
  out.puts 'Bundle not satisfied, falling back to `bundle install`'
87
- system 'bundle install'
130
+ system "bundle install #{bundle_install_args}"
88
131
  end
89
132
  end
90
133
 
134
+ def bundle_install_args
135
+ [].tap do |args|
136
+ args << "--gemfile #{gemfile_path}"
137
+ args << "--with #{with_groups}" unless with_groups.empty?
138
+ args << "--without #{without_groups}" unless without_groups.empty?
139
+ args << "--jobs #{options[:jobs]}"
140
+ end.join(' ')
141
+ end
142
+
91
143
  def gem_list
92
- @gem_list ||= Prebundler::GemfileInterpreter.interpret(gemfile_path, bundle_path)
144
+ @gem_list ||= Prebundler::GemfileInterpreter.interpret(
145
+ gemfile_path, bundle_path
146
+ )
93
147
  end
94
148
 
95
149
  def backend_file_list
@@ -115,14 +169,18 @@ module Prebundler
115
169
 
116
170
  def groups
117
171
  @groups ||= begin
118
- all_groups = gem_list.flat_map { |_, gem_ref| gem_ref.groups }.uniq
119
- with_groups = (options[:with] || '').split(',').map { |g| g.strip.to_sym }
120
- without_groups = (options[:without] || '').split(',').map { |g| g.strip.to_sym }
121
-
122
- groups = with_groups.empty? ? all_groups : with_groups
123
- groups - without_groups
172
+ all_groups = gem_list.flat_map { |_, gem_ref| gem_ref.groups.to_a }.uniq
173
+ (all_groups + with_groups).uniq - without_groups
124
174
  end
125
175
  end
176
+
177
+ def with_groups
178
+ @with_groups ||= (options[:with] || '').split(/[:, ]/).map { |g| g.strip.to_sym }
179
+ end
180
+
181
+ def without_groups
182
+ @without_groups ||= (options[:without] || '').split(/[:, ]/).map { |g| g.strip.to_sym }
183
+ end
126
184
  end
127
185
  end
128
186
  end
@@ -1,4 +1,5 @@
1
1
  require 'fileutils'
2
+ require 'set'
2
3
 
3
4
  module Prebundler
4
5
  class GemRef
@@ -12,13 +13,13 @@ module Prebundler
12
13
  end
13
14
  end
14
15
 
15
- attr_reader :name, :bundle_path
16
+ attr_reader :name, :bundle_path, :groups
16
17
  attr_accessor :spec, :dependencies
17
18
 
18
19
  def initialize(name, bundle_path, options = {})
19
20
  @name = name
20
21
  @bundle_path = bundle_path
21
- @groups = options[:groups]
22
+ @groups = Set.new(options[:groups])
22
23
  @source = options[:source]
23
24
  @dependencies = options[:dependencies]
24
25
  end
@@ -27,10 +28,6 @@ module Prebundler
27
28
  @dependencies ||= []
28
29
  end
29
30
 
30
- def groups
31
- @groups ||= []
32
- end
33
-
34
31
  def source
35
32
  @source ||= DEFAULT_SOURCE
36
33
  end
@@ -44,8 +41,8 @@ module Prebundler
44
41
  end
45
42
 
46
43
  def install
47
- system "gem install -N -i #{bundle_path} --ignore-dependencies --source #{source} #{name} -v #{version}"
48
- $?.exitstatus == 0
44
+ system({ "GEM_HOME" => bundle_path }, "gem install -N --ignore-dependencies --source #{source} #{name} -v #{version}")
45
+ $?.exitstatus
49
46
  end
50
47
 
51
48
  def install_from_tar(tar_file)
@@ -57,11 +54,28 @@ module Prebundler
57
54
  tar_flags = File.exist?(tar_file) ? '-rf' : '-cf'
58
55
 
59
56
  system "tar -C #{bundle_path} #{tar_flags} #{tar_file} #{relative_gem_dir}"
60
- system "tar -C #{bundle_path} -rf #{tar_file} #{relative_gemspec_dir}"
57
+
58
+ relative_gemspec_files.each do |relative_gemspec_file|
59
+ system "tar -C #{bundle_path} -rf #{tar_file} #{relative_gemspec_file}"
60
+ end
61
61
 
62
62
  if File.directory?(extension_dir)
63
63
  system "tar -C #{bundle_path} -rf #{tar_file} #{relative_extension_dir}"
64
64
  end
65
+
66
+ executables.each do |executable|
67
+ system "tar -C #{bundle_path} -rf #{tar_file} #{File.join('bin', executable)}"
68
+ end
69
+ end
70
+
71
+ def executables
72
+ gemspecs.flat_map(&:executables)
73
+ end
74
+
75
+ def gemspecs
76
+ @gemspecs ||= relative_gemspec_files.map do |relative_gemspec_file|
77
+ Bundler.load_gemspec(File.join(bundle_path, relative_gemspec_file))
78
+ end
65
79
  end
66
80
 
67
81
  def installable?
@@ -96,8 +110,8 @@ module Prebundler
96
110
  File.join('gems', id)
97
111
  end
98
112
 
99
- def relative_gemspec_dir
100
- File.join('specifications', gemspec_file)
113
+ def relative_gemspec_files
114
+ [File.join('specifications', gemspec_file)]
101
115
  end
102
116
 
103
117
  def tar_file
@@ -9,6 +9,10 @@ module Prebundler
9
9
 
10
10
  def initialize(gems)
11
11
  @gems = gems
12
+
13
+ gems.each do |_, gem_ref|
14
+ assign_groups(gem_ref, [])
15
+ end
12
16
  end
13
17
 
14
18
  def each
@@ -23,6 +27,16 @@ module Prebundler
23
27
 
24
28
  private
25
29
 
30
+ # propagate groups to dependencies
31
+ def assign_groups(gem_ref, seen)
32
+ gem_ref.dependencies.each do |dep|
33
+ next if seen.include?(dep)
34
+ next unless gems[dep]
35
+ gem_ref.groups.each { |group| gems[dep].groups << group }
36
+ assign_groups(gems[dep], seen + [dep])
37
+ end
38
+ end
39
+
26
40
  def tsort_each_node(&block)
27
41
  gems.keys.each(&block)
28
42
  end
@@ -1,18 +1,21 @@
1
+ require 'set'
2
+
1
3
  module Prebundler
2
4
  class GemfileInterpreter
3
- def self.interpret(path, bundle_path)
4
- Gemfile.new(new(path, bundle_path).gems)
5
+ def self.interpret(gemfile_path, bundle_path)
6
+ Gemfile.new(new(gemfile_path, bundle_path).gems)
5
7
  end
6
8
 
7
- attr_reader :gems, :bundle_path
9
+ attr_reader :gems, :gemfile_path, :bundle_path
8
10
 
9
- def initialize(path, bundle_path)
11
+ def initialize(gemfile_path, bundle_path)
10
12
  @gems = {}
11
- @current_groups = []
13
+ @current_groups = [:global]
14
+ @gemfile_path = gemfile_path
12
15
  @bundle_path = bundle_path
13
- instance_eval(File.read(path))
16
+ instance_eval(File.read(gemfile_path))
14
17
 
15
- lockfile = Bundler::LockfileParser.new(File.read("#{path}.lock"))
18
+ lockfile = Bundler::LockfileParser.new(File.read("#{gemfile_path}.lock"))
16
19
 
17
20
  lockfile.specs.each do |spec|
18
21
  gems[spec.name] ||= GemRef.create(spec.name, bundle_path)
@@ -35,7 +38,7 @@ module Prebundler
35
38
  end
36
39
 
37
40
  def path(dir)
38
- @current_path = dir
41
+ @current_path = File.join(File.dirname(gemfile_path), dir)
39
42
  yield if block_given?
40
43
  @current_path = nil
41
44
  end
@@ -49,7 +52,7 @@ module Prebundler
49
52
  def group(*groups)
50
53
  @current_groups = groups
51
54
  yield if block_given?
52
- @current_groups = []
55
+ @current_groups = [:global]
53
56
  end
54
57
 
55
58
  def gemspec
@@ -15,10 +15,13 @@ module Prebundler
15
15
  def initialize(name, bundle_path, options = {})
16
16
  super
17
17
  @strategy = options.include?(:git) ? :git : :github
18
- @uri = options[@strategy]
19
18
  end
20
19
 
21
20
  def install
21
+ FileUtils.mkdir_p(spec_path)
22
+ FileUtils.mkdir_p(install_path)
23
+ FileUtils.mkdir_p(cache_path)
24
+
22
25
  return if File.exist?(cache_dir) || File.exist?(install_dir)
23
26
  system "git clone #{uri} \"#{cache_dir}\" --bare --no-hardlinks --quiet"
24
27
  return $? if $?.exitstatus != 0
@@ -55,11 +58,7 @@ module Prebundler
55
58
  end
56
59
 
57
60
  def uri
58
- if strategy == :github
59
- "git://github.com/#{@uri}.git"
60
- else
61
- @uri
62
- end
61
+ spec.source.uri
63
62
  end
64
63
 
65
64
  alias_method :source, :uri
@@ -68,16 +67,26 @@ module Prebundler
68
67
  spec.source.revision
69
68
  end
70
69
 
70
+ def gemspecs
71
+ @gemspecs ||= gemspec_files.map do |gemspec_file|
72
+ Bundler.load_gemspec(gemspec_file)
73
+ end
74
+ end
75
+
71
76
  private
72
77
 
78
+ def gemspec_files
79
+ @gemspec_files ||= Dir[File.join(install_dir, '*.gemspec')]
80
+ end
81
+
73
82
  def copy_gemspecs
74
- FileUtils.cp(Dir[File.join(install_dir, '*.gemspec')], spec_path)
83
+ FileUtils.cp(gemspec_files, spec_path)
75
84
  end
76
85
 
77
86
  # adapted from
78
87
  # https://github.com/bundler/bundler/blob/fea23637886c1b1bde471c98344b8844f82e60ce/lib/bundler/source/git.rb#L237
79
88
  def serialize_gemspecs
80
- Dir[File.join(install_dir, '*.gemspec')].each do |path|
89
+ gemspec_files.each do |path|
81
90
  # Evaluate gemspecs and cache the result. Gemspecs
82
91
  # in git might require git or other dependencies.
83
92
  # The gemspecs we cache should already be evaluated.
@@ -100,7 +109,7 @@ module Prebundler
100
109
  # If there is no URI scheme, assume it is an ssh/git URI
101
110
  input = uri
102
111
  end
103
- Digest::SHA1.hexdigest(input)
112
+ Bundler::SharedHelpers.digest(:SHA1).hexdigest(input)
104
113
  end
105
114
  end
106
115
  end
@@ -10,7 +10,11 @@ module Prebundler
10
10
 
11
11
  def initialize(name, bundle_path, options = {})
12
12
  super
13
- @path = options[:path]
13
+
14
+ # @TODO: Individual gem calls can also specify a path, which
15
+ # we don't currently handle. For now just use the gem's name
16
+ # to complete the path.
17
+ @path = File.join(options[:path], name)
14
18
  end
15
19
 
16
20
  def installable?
@@ -21,6 +25,20 @@ module Prebundler
21
25
  false
22
26
  end
23
27
 
28
+ def gemspecs
29
+ @gemspecs ||= gemspec_files.map do |gemspec_file|
30
+ Dir.chdir(File.dirname(gemspec_file)) do
31
+ Bundler.load_gemspec(File.basename(gemspec_file))
32
+ end
33
+ end
34
+ end
35
+
24
36
  alias_method :source, :path
37
+
38
+ private
39
+
40
+ def gemspec_files
41
+ @gemspec_files ||= Dir[File.join(path, '*.gemspec')]
42
+ end
25
43
  end
26
44
  end
@@ -1,3 +1,3 @@
1
1
  module Prebundler
2
- VERSION = '0.0.4'
2
+ VERSION = '0.5.0'
3
3
  end
data/prebundler.gemspec CHANGED
@@ -17,9 +17,8 @@ Gem::Specification.new do |s|
17
17
  s.add_dependency 'parallel', '~> 1.0'
18
18
  s.add_dependency 'gli', '~> 2.0'
19
19
 
20
- # @TODO: remove these, maybe move s3 support into separate gem?
20
+ # @TODO: move s3 support into separate gem
21
21
  s.add_dependency 'aws-sdk', '~> 2.0'
22
- s.add_dependency 'pry-byebug'
23
22
 
24
23
  s.executables << 'prebundle'
25
24
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prebundler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cameron Dutro
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-29 00:00:00.000000000 Z
11
+ date: 2017-12-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,20 +66,6 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '2.0'
69
- - !ruby/object:Gem::Dependency
70
- name: pry-byebug
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :runtime
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
69
  description: Gem dependency prebuilder
84
70
  email:
85
71
  - camertron@gmail.com