bundix 2.3.0 → 2.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
- SHA1:
3
- metadata.gz: 623983fdd769dae25b35ce73300b7c15e2e05406
4
- data.tar.gz: 467820098d81bee233d3292e0a26cca9924e5c62
2
+ SHA256:
3
+ metadata.gz: 3fbcd152e7e9e610f2357bd6ee0658d84fa9bbcd497c3ced755d7efafb9547a9
4
+ data.tar.gz: 9f8eb407611278f324960b8393e7452d4b9385434cfbc028db61dbddc7335f3d
5
5
  SHA512:
6
- metadata.gz: 730d8af2547d66f2fb33e1e5dd5c1afafec4f30dbc896de6f942552f485ae187e001f65ea786486553110f813e798e7cb8010f9c4012f9a82b686cc9b77bf359
7
- data.tar.gz: e8e3ffa6148a782fe38c44128e11c86e836681eec0e03fddcccd2800b96a430f8f48f5e0c0923ef4b87787c6ccb14e3e5de18287a1acfe5a36c35f9bf9fa0e62
6
+ metadata.gz: 564b4bf345a72c9556d3a776c7d39f62f1ee060b71c5a9c99d7b29ad43096d1d321a6e860da52124b43a4f9675dfc0f3bdbd1a5fe2dadb0d6561967b3e17f4b3
7
+ data.tar.gz: 0565a326c0babef8e995afb905895c6c591c27926d5b7fb011a59478c89f128eabd3aa18c10dfa1f9f040663f7e48c7c86697a55973ad37a77fb07ed8b693d57
data/bin/bundix CHANGED
@@ -1,117 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'optparse'
4
- require 'tmpdir'
5
- require 'tempfile'
6
- require 'pathname'
3
+ require_relative '../lib/bundix/commandline'
7
4
 
8
- require_relative '../lib/bundix'
9
-
10
- options = {
11
- ruby: 'ruby',
12
- bundle_pack_path: 'vendor/bundle',
13
- gemfile: 'Gemfile',
14
- lockfile: 'Gemfile.lock',
15
- gemset: 'gemset.nix',
16
- }
17
-
18
- op = OptionParser.new do |o|
19
- o.on '-m', '--magic', 'lock, pack, and write dependencies' do
20
- options[:magic] = true
21
- end
22
-
23
- o.on '--ruby=ruby', 'ruby version to use for magic and init, defaults to latest' do |value|
24
- options[:ruby] = value
25
- end
26
-
27
- o.on '--bundle-pack-path=vendor/bundle', "path to pack the magic" do |value|
28
- options[:bundle_pack_path] = value
29
- end
30
-
31
- o.on '-i', '--init', "initialize a new shell.nix for nix-shell (won't overwrite old ones)" do
32
- options[:init] = true
33
- end
34
-
35
- o.on '--gemset=gemset.nix', 'path to the gemset.nix' do |value|
36
- options[:gemset] = File.expand_path(value)
37
- end
38
-
39
- o.on '--lockfile=Gemfile.lock', 'path to the Gemfile.lock' do |value|
40
- options[:lockfile] = File.expand_path(value)
41
- end
42
-
43
- o.on '--gemfile=Gemfile', 'path to the Gemfile' do |value|
44
- options[:gemfile] = File.expand_path(value)
45
- end
46
-
47
- o.on '-d', '--dependencies', 'include gem dependencies (deprecated)' do
48
- warn '--dependencies/-d is deprecated because'
49
- warn 'dependencies will always be fetched'
50
- end
51
-
52
- o.on '-q', '--quiet', 'only output errors' do
53
- options[:quiet] = true
54
- end
55
-
56
- o.on '-l', '--lock', 'generate Gemfile.lock first' do
57
- options[:lock] = true
58
- end
59
-
60
- o.on '-v', '--version', 'show the version of bundix' do
61
- puts Bundix::VERSION
62
- exit
63
- end
64
-
65
- o.on '--env', 'show the environment in bundix' do
66
- system('env')
67
- exit
68
- end
69
- end
70
-
71
- op.parse!
72
- $VERBOSE = !options[:quiet]
73
-
74
- if options[:magic]
75
- fail unless system(
76
- Bundix::NIX_SHELL, '-p', options[:ruby],
77
- "bundler.override { ruby = #{options[:ruby]}; }",
78
- "--command", "bundle lock --lockfile=#{options[:lockfile]}")
79
- fail unless system(
80
- Bundix::NIX_SHELL, '-p', options[:ruby],
81
- "bundler.override { ruby = #{options[:ruby]}; }",
82
- "--command", "bundle pack --all --path #{options[:bundle_pack_path]}")
83
- end
84
-
85
- if options[:init]
86
- if File.file?('shell.nix')
87
- warn "won't override existing shell.nix"
88
- else
89
- shell_nix = File.read(File.expand_path('../template/shell.nix', __dir__))
90
- shell_nix.gsub!('PROJECT', File.basename(Dir.pwd))
91
- shell_nix.gsub!('RUBY', options[:ruby])
92
- shell_nix.gsub!('LOCKFILE', "./#{Pathname(options[:lockfile]).relative_path_from(Pathname('./'))}")
93
- shell_nix.gsub!('GEMSET', "./#{Pathname(options[:gemset]).relative_path_from(Pathname('./'))}")
94
- File.write('shell.nix', shell_nix)
95
- end
96
- end
97
-
98
- if options[:lock]
99
- lock = !File.file?(options[:lockfile])
100
- lock ||= File.mtime(options[:gemfile]) > File.mtime(options[:lockfile])
101
- if lock
102
- system('bundle', 'lock')
103
- fail 'bundle lock failed' unless $?.success?
104
- end
105
- end
106
-
107
- gemset = Bundix.new(options).convert
108
-
109
- tempfile = Tempfile.new('gemset.nix', encoding: 'UTF-8')
110
- begin
111
- Bundix.object2nix(gemset, 2, tempfile)
112
- tempfile.flush
113
- FileUtils.cp(tempfile.path, options[:gemset])
114
- ensure
115
- tempfile.close!
116
- tempfile.unlink
117
- end
5
+ Bundix::CommandLine.run
data/lib/bundix.rb CHANGED
@@ -6,44 +6,92 @@ require 'pp'
6
6
 
7
7
  require_relative 'bundix/version'
8
8
  require_relative 'bundix/source'
9
+ require_relative 'bundix/nixer'
9
10
 
10
11
  class Bundix
11
12
  NIX_INSTANTIATE = 'nix-instantiate'
12
13
  NIX_PREFETCH_URL = 'nix-prefetch-url'
13
14
  NIX_PREFETCH_GIT = 'nix-prefetch-git'
14
- NIX_BUILD = 'nix-build'
15
15
  NIX_HASH = 'nix-hash'
16
16
  NIX_SHELL = 'nix-shell'
17
17
 
18
- FETCHURL_FORCE = File.expand_path('../bundix/fetchurl-force.nix', __FILE__)
19
- FETCHURL_FORCE_CHECK = lambda do |_, out|
20
- out =~ /success! failing outer nix build.../
21
- end
22
-
23
18
  SHA256_32 = %r(^[a-z0-9]{52}$)
24
- SHA256_16 = %r(^[a-f0-9]{64}$)
25
19
 
26
20
  attr_reader :options
27
21
 
22
+ attr_accessor :fetcher
23
+
24
+ class Dependency < Bundler::Dependency
25
+ def initialize(name, version, options={}, &blk)
26
+ super(name, version, options, &blk)
27
+ @bundix_version = version
28
+ end
29
+
30
+ attr_reader :version
31
+ end
32
+
28
33
  def initialize(options)
29
34
  @options = { quiet: false, tempfile: nil }.merge(options)
35
+ @fetcher = Fetcher.new
30
36
  end
31
37
 
32
38
  def convert
33
39
  cache = parse_gemset
34
40
  lock = parse_lockfile
41
+ dep_cache = build_depcache(lock)
35
42
 
36
43
  # reverse so git comes last
37
44
  lock.specs.reverse_each.with_object({}) do |spec, gems|
38
- gem = find_cached_spec(spec, cache) || convert_spec(spec, cache)
45
+ gem = find_cached_spec(spec, cache) || convert_spec(spec, cache, dep_cache)
39
46
  gems.merge!(gem)
40
47
 
41
- gems[spec.name]['dependencies'] = spec.dependencies.map(&:name) - ['bundler']
48
+ if spec.dependencies.any?
49
+ gems[spec.name]['dependencies'] = spec.dependencies.map(&:name) - ['bundler']
50
+ end
51
+ end
52
+ end
53
+
54
+ def groups(spec, dep_cache)
55
+ {groups: dep_cache.fetch(spec.name).groups}
56
+ end
57
+
58
+ PLATFORM_MAPPING = {}
59
+
60
+ {
61
+ "ruby" => [{engine: "ruby"}, {engine:"rbx"}, {engine:"maglev"}],
62
+ "mri" => [{engine: "ruby"}, {engine: "maglev"}],
63
+ "rbx" => [{engine: "rbx"}],
64
+ "jruby" => [{engine: "jruby"}],
65
+ "mswin" => [{engine: "mswin"}],
66
+ "mswin64" => [{engine: "mswin64"}],
67
+ "mingw" => [{engine: "mingw"}],
68
+ "truffleruby" => [{engine: "ruby"}],
69
+ "x64_mingw" => [{engine: "mingw"}],
70
+ }.each do |name, list|
71
+ PLATFORM_MAPPING[name] = list
72
+ %w(1.8 1.9 2.0 2.1 2.2 2.3 2.4 2.5 2.6).each do |version|
73
+ PLATFORM_MAPPING["#{name}_#{version.sub(/[.]/,'')}"] = list.map do |platform|
74
+ platform.merge(:version => version)
75
+ end
42
76
  end
43
77
  end
44
78
 
45
- def convert_spec(spec, cache)
46
- {spec.name => {version: spec.version.to_s, source: Source.new(spec).convert}}
79
+ def platforms(spec, dep_cache)
80
+ # c.f. Bundler::CurrentRuby
81
+ platforms = dep_cache.fetch(spec.name).platforms.map do |platform_name|
82
+ PLATFORM_MAPPING[platform_name.to_s]
83
+ end.flatten
84
+
85
+ {platforms: platforms}
86
+ end
87
+
88
+ def convert_spec(spec, cache, dep_cache)
89
+ {
90
+ spec.name => {
91
+ version: spec.version.to_s,
92
+ source: Source.new(spec, fetcher).convert
93
+ }.merge(platforms(spec, dep_cache)).merge(groups(spec, dep_cache))
94
+ }
47
95
  rescue => ex
48
96
  warn "Skipping #{spec.name}: #{ex}"
49
97
  puts ex.backtrace
@@ -70,12 +118,53 @@ class Bundix
70
118
  {name => cached} if cached
71
119
  end
72
120
 
121
+ def build_depcache(lock)
122
+ definition = Bundler::Definition.build(options[:gemfile], options[:lockfile], false)
123
+ dep_cache = {}
124
+
125
+ definition.dependencies.each do |dep|
126
+ dep_cache[dep.name] = dep
127
+ end
128
+
129
+ lock.specs.each do |spec|
130
+ dep_cache[spec.name] ||= Dependency.new(spec.name, nil, {})
131
+ end
132
+
133
+ begin
134
+ changed = false
135
+ lock.specs.each do |spec|
136
+ as_dep = dep_cache.fetch(spec.name)
137
+
138
+ spec.dependencies.each do |dep|
139
+ cached = dep_cache.fetch(dep.name) do |name|
140
+ if name != "bundler"
141
+ raise KeyError, "Gem dependency '#{name}' not specified in #{lockfile}"
142
+ end
143
+ dep_cache[name] = Dependency.new(name, lock.bundler_version, {})
144
+ end
145
+
146
+ if !((as_dep.groups - cached.groups) - [:default]).empty? or !(as_dep.platforms - cached.platforms).empty?
147
+ changed = true
148
+ dep_cache[cached.name] = (Dependency.new(cached.name, nil, {
149
+ "group" => as_dep.groups | cached.groups,
150
+ "platforms" => as_dep.platforms | cached.platforms
151
+ }))
152
+
153
+ cc = dep_cache[cached.name]
154
+ end
155
+ end
156
+ end
157
+ end while changed
158
+
159
+ return dep_cache
160
+ end
73
161
 
74
162
  def parse_gemset
75
163
  path = File.expand_path(options[:gemset])
76
164
  return {} unless File.file?(path)
77
- json = Bundix.sh(
78
- NIX_INSTANTIATE, '--eval', '-E', "builtins.toJSON(import #{path})")
165
+ json = Bundix.sh(NIX_INSTANTIATE, '--eval', '-E', %(
166
+ builtins.toJSON (import #{Nixer.serialize(path)}))
167
+ )
79
168
  JSON.parse(json.strip.gsub(/\\"/, '"')[1..-2])
80
169
  end
81
170
 
@@ -83,37 +172,8 @@ class Bundix
83
172
  Bundler::LockfileParser.new(File.read(options[:lockfile]))
84
173
  end
85
174
 
86
- def self.object2nix(obj, level = 2, out = '')
87
- case obj
88
- when Hash
89
- out << "{\n"
90
- obj.sort_by{|k, v| k.to_s.downcase }.each do |(k, v)|
91
- out << ' ' * level
92
- if k.to_s =~ /^[a-zA-Z_-]+[a-zA-Z0-9_-]*$/
93
- out << k.to_s
94
- else
95
- object2nix(k, level + 2, out)
96
- end
97
- out << ' = '
98
- object2nix(v, level + 2, out)
99
- out << (v.is_a?(Hash) ? "\n" : ";\n")
100
- end
101
- out << (' ' * (level - 2)) << (level == 2 ? '}' : '};')
102
- when Array
103
- out << '[' << obj.sort.map{|o| o.to_str.dump }.join(' ') << ']'
104
- when String
105
- out << obj.dump
106
- when Symbol
107
- out << obj.to_s.dump
108
- when true, false
109
- out << obj.to_s
110
- else
111
- fail obj.inspect
112
- end
113
- end
114
-
115
175
  def self.sh(*args, &block)
116
- out, status = Open3.capture2e(*args)
176
+ out, status = Open3.capture2(*args)
117
177
  unless block_given? ? block.call(status, out) : status.success?
118
178
  puts "$ #{args.join(' ')}" if $VERBOSE
119
179
  puts out if $VERBOSE
@@ -0,0 +1,169 @@
1
+ require 'optparse'
2
+ require 'tmpdir'
3
+ require 'tempfile'
4
+ require 'pathname'
5
+
6
+ require_relative '../bundix'
7
+ require_relative 'shell_nix_context'
8
+
9
+ class Bundix
10
+ class CommandLine
11
+
12
+ DEFAULT_OPTIONS = {
13
+ ruby: 'ruby',
14
+ bundle_pack_path: 'vendor/bundle',
15
+ gemfile: 'Gemfile',
16
+ lockfile: 'Gemfile.lock',
17
+ gemset: 'gemset.nix',
18
+ project: File.basename(Dir.pwd)
19
+ }
20
+
21
+ def self.run
22
+ self.new.run
23
+ end
24
+
25
+ def initialize
26
+ @options = DEFAULT_OPTIONS.clone
27
+ end
28
+
29
+ attr_accessor :options
30
+
31
+ def run
32
+ parse_options
33
+ handle_magic
34
+ handle_lock
35
+ handle_init
36
+ gemset = build_gemset
37
+ save_gemset(gemset)
38
+ end
39
+
40
+ def parse_options
41
+ op = OptionParser.new do |o|
42
+ o.on '-m', '--magic', 'lock, pack, and write dependencies' do
43
+ options[:magic] = true
44
+ end
45
+
46
+ o.on "--ruby=#{options[:ruby]}", 'ruby version to use for magic and init, defaults to latest' do |value|
47
+ options[:ruby] = value
48
+ end
49
+
50
+ o.on "--bundle-pack-path=#{options[:bundle_pack_path]}", "path to pack the magic" do |value|
51
+ options[:bundle_pack_path] = value
52
+ end
53
+
54
+ o.on '-i', '--init', "initialize a new shell.nix for nix-shell (won't overwrite old ones)" do
55
+ options[:init] = true
56
+ end
57
+
58
+ o.on "--gemset=#{options[:gemset]}", 'path to the gemset.nix' do |value|
59
+ options[:gemset] = File.expand_path(value)
60
+ end
61
+
62
+ o.on "--lockfile=#{options[:lockfile]}", 'path to the Gemfile.lock' do |value|
63
+ options[:lockfile] = File.expand_path(value)
64
+ end
65
+
66
+ o.on "--gemfile=#{options[:gemfile]}", 'path to the Gemfile' do |value|
67
+ options[:gemfile] = File.expand_path(value)
68
+ end
69
+
70
+ o.on '-d', '--dependencies', 'include gem dependencies (deprecated)' do
71
+ warn '--dependencies/-d is deprecated because'
72
+ warn 'dependencies will always be fetched'
73
+ end
74
+
75
+ o.on '-q', '--quiet', 'only output errors' do
76
+ options[:quiet] = true
77
+ end
78
+
79
+ o.on '-l', '--lock', 'generate Gemfile.lock first' do
80
+ options[:lock] = true
81
+ end
82
+
83
+ o.on '-v', '--version', 'show the version of bundix' do
84
+ puts Bundix::VERSION
85
+ exit
86
+ end
87
+
88
+ o.on '--env', 'show the environment in bundix' do
89
+ system('env')
90
+ exit
91
+ end
92
+ end
93
+
94
+ op.parse!
95
+ $VERBOSE = !options[:quiet]
96
+ options
97
+ end
98
+
99
+ def handle_magic
100
+ ENV['BUNDLE_GEMFILE'] = options[:gemfile]
101
+
102
+ if options[:magic]
103
+ fail unless system(
104
+ Bundix::NIX_SHELL, '-p', options[:ruby],
105
+ "bundler.override { ruby = #{options[:ruby]}; }",
106
+ "--command", "bundle lock --lockfile=#{options[:lockfile]}")
107
+ fail unless system(
108
+ Bundix::NIX_SHELL, '-p', options[:ruby],
109
+ "bundler.override { ruby = #{options[:ruby]}; }",
110
+ "--command", "bundle pack --all --path #{options[:bundle_pack_path]}")
111
+ end
112
+ end
113
+
114
+ def shell_nix_context
115
+ ShellNixContext.from_hash(options)
116
+ end
117
+
118
+ def shell_nix_string
119
+ tmpl = ERB.new(File.read(File.expand_path('../../template/shell-nix.erb', __dir__)))
120
+ tmpl.result(shell_nix_context.bind)
121
+ end
122
+
123
+ def handle_init
124
+ if options[:init]
125
+ if File.file?('shell.nix')
126
+ warn "won't override existing shell.nix but here is what it'd look like:"
127
+ puts shell_nix_string
128
+ else
129
+ File.write('shell.nix', shell_nix_string)
130
+ end
131
+ end
132
+ end
133
+
134
+ def handle_lock
135
+ if options[:lock]
136
+ lock = !File.file?(options[:lockfile])
137
+ lock ||= File.mtime(options[:gemfile]) > File.mtime(options[:lockfile])
138
+ if lock
139
+ ENV.delete('BUNDLE_PATH')
140
+ ENV.delete('BUNDLE_FROZEN')
141
+ ENV.delete('BUNDLE_BIN_PATH')
142
+ system('bundle', 'lock')
143
+ fail 'bundle lock failed' unless $?.success?
144
+ end
145
+ end
146
+ end
147
+
148
+ def build_gemset
149
+ Bundix.new(options).convert
150
+ end
151
+
152
+ def object2nix(obj)
153
+ Nixer.serialize(obj)
154
+ end
155
+
156
+ def save_gemset(gemset)
157
+ tempfile = Tempfile.new('gemset.nix', encoding: 'UTF-8')
158
+ begin
159
+ tempfile.write(object2nix(gemset))
160
+ tempfile.flush
161
+ FileUtils.cp(tempfile.path, options[:gemset])
162
+ FileUtils.chmod(0644, options[:gemset])
163
+ ensure
164
+ tempfile.close!
165
+ tempfile.unlink
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,104 @@
1
+ require 'erb'
2
+
3
+ class Hash
4
+ # regretfully, duckpunching
5
+ def <=>(other)
6
+ if other.is_a?(Hash)
7
+ larray = to_a.sort{|l,r| Bundix::Nixer.order(l,r)}
8
+ rarray = other.to_a.sort{|l,r| Bundix::Nixer.order(l,r)}
9
+ larray <=> rarray
10
+ else
11
+ nil
12
+ end
13
+ end
14
+ end
15
+
16
+ class Bundix
17
+ class Nixer
18
+ class << self
19
+ def serialize(obj)
20
+ new(obj).serialize
21
+ end
22
+
23
+ def order(left, right)
24
+ if right.is_a?(left.class)
25
+ if right.respond_to?(:<=>)
26
+ cmp = right <=> left
27
+ return -1 * (cmp) unless cmp.nil?
28
+ end
29
+ end
30
+
31
+ if left.is_a?(right.class)
32
+ if left.respond_to?(:<=>)
33
+ cmp = right <=> left
34
+ if cmp.nil?
35
+ return class_order(left, right)
36
+ else
37
+ return cmp
38
+ end
39
+ end
40
+ end
41
+
42
+ return class_order(left, right)
43
+ end
44
+
45
+ def class_order(left, right)
46
+ left.class.name <=> right.class.name # like Erlang
47
+ end
48
+ end
49
+
50
+ attr_reader :level, :obj
51
+
52
+ SET_T = ERB.new(File.read(File.expand_path("../../template/nixer/set.erb", __dir__)).chomp)
53
+ LIST_T = ERB.new(File.read(File.expand_path("../../template/nixer/list.erb", __dir__)).chomp)
54
+
55
+ def initialize(obj, level = 0)
56
+ @obj = obj
57
+ @level = level
58
+ end
59
+
60
+ def indent
61
+ ' ' * (level + 2)
62
+ end
63
+
64
+ def outdent
65
+ ' ' * level
66
+ end
67
+
68
+ def sub(obj, indent = 0)
69
+ self.class.new(obj, level + indent).serialize
70
+ end
71
+
72
+ def serialize_key(k)
73
+ if k.to_s =~ /^[a-zA-Z_-]+[a-zA-Z0-9_-]*$/
74
+ k.to_s
75
+ else
76
+ sub(k, 2)
77
+ end
78
+ end
79
+
80
+ def serialize
81
+ case obj
82
+ when Hash
83
+ return SET_T.result(binding)
84
+ when Array
85
+ return LIST_T.result(binding)
86
+ when String
87
+ obj.dump
88
+ when Symbol
89
+ obj.to_s.dump
90
+ when Pathname
91
+ str = obj.to_s
92
+ if %r{/} !~ str
93
+ "./"+ str
94
+ else
95
+ str
96
+ end
97
+ when true, false
98
+ obj.to_s
99
+ else
100
+ fail "Cannot convert to nix: #{obj.inspect}"
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,27 @@
1
+ class Bundix
2
+ class ShellNixContext < Struct.new(:project, :ruby, :gemfile, :lockfile, :gemset)
3
+ def self.from_hash(hash)
4
+ new(*hash.values_at(*members))
5
+ end
6
+
7
+ def bind
8
+ binding
9
+ end
10
+
11
+ def path_for(file)
12
+ Nixer.serialize(Pathname(file).relative_path_from(Pathname('./')))
13
+ end
14
+
15
+ def gemfile_path
16
+ path_for(gemfile)
17
+ end
18
+
19
+ def lockfile_path
20
+ path_for(lockfile)
21
+ end
22
+
23
+ def gemset_path
24
+ path_for(gemset)
25
+ end
26
+ end
27
+ end
data/lib/bundix/source.rb CHANGED
@@ -1,17 +1,5 @@
1
1
  class Bundix
2
- class Source < Struct.new(:spec)
3
- def convert
4
- case spec.source
5
- when Bundler::Source::Rubygems
6
- convert_rubygems
7
- when Bundler::Source::Git
8
- convert_git
9
- else
10
- pp spec
11
- fail 'unkown bundler source'
12
- end
13
- end
14
-
2
+ class Fetcher
15
3
  def sh(*args, &block)
16
4
  Bundix.sh(*args, &block)
17
5
  end
@@ -20,50 +8,95 @@ class Bundix
20
8
  warn "Downloading #{file} from #{url}"
21
9
  uri = URI(url)
22
10
  open_options = {}
23
- if uri.user && uri.password
11
+
12
+ unless uri.user
13
+ inject_credentials_from_bundler_settings(uri)
14
+ end
15
+
16
+ if uri.user
24
17
  open_options[:http_basic_authentication] = [uri.user, uri.password]
25
18
  uri.user = nil
26
19
  uri.password = nil
27
20
  end
28
- open(uri, 'r', 0600, open_options) do |net|
29
- File.open(file, 'wb+') { |local|
30
- File.copy_stream(net, local)
31
- }
21
+
22
+ begin
23
+ open(uri.to_s, 'r', 0600, open_options) do |net|
24
+ File.open(file, 'wb+') { |local|
25
+ File.copy_stream(net, local)
26
+ }
27
+ end
28
+ rescue OpenURI::HTTPError => e
29
+ # e.message: "403 Forbidden" or "401 Unauthorized"
30
+ debrief_access_denied(uri.host) if e.message =~ /^40[13] /
31
+ raise
32
32
  end
33
33
  end
34
34
 
35
+ def inject_credentials_from_bundler_settings(uri)
36
+ @bundler_settings ||= Bundler::Settings.new(Bundler.root + '.bundle')
37
+
38
+ if val = @bundler_settings[uri.host]
39
+ uri.user, uri.password = val.split(':', 2)
40
+ end
41
+ end
42
+
43
+ def debrief_access_denied(host)
44
+ print_error(
45
+ "Authentication is required for #{host}.\n" +
46
+ "Please supply credentials for this source. You can do this by running:\n" +
47
+ " bundle config packages.shopify.io username:password"
48
+ )
49
+ end
50
+
51
+ def print_error(msg)
52
+ msg = "\x1b[31m#{msg}\x1b[0m" if $stdout.tty?
53
+ STDERR.puts(msg)
54
+ end
55
+
35
56
  def nix_prefetch_url(url)
36
- dir = File.expand_path('~/.cache/bundix')
57
+ dir = File.join(ENV['XDG_CACHE_HOME'] || "#{ENV['HOME']}/.cache", 'bundix')
37
58
  FileUtils.mkdir_p dir
38
59
  file = File.join(dir, url.gsub(/[^\w-]+/, '_'))
39
60
 
40
- unless File.size?(file)
41
- download(file, url)
42
- end
43
-
61
+ download(file, url) unless File.size?(file)
44
62
  return unless File.size?(file)
45
63
 
46
- sh('nix-prefetch-url', '--type', 'sha256', "file://#{file}")
47
- .force_encoding('UTF-8').strip
64
+ sh(
65
+ Bundix::NIX_PREFETCH_URL,
66
+ '--type', 'sha256',
67
+ '--name', File.basename(url), # --name mygem-1.2.3.gem
68
+ "file://#{file}", # file:///.../https_rubygems_org_gems_mygem-1_2_3_gem
69
+ ).force_encoding('UTF-8').strip
48
70
  rescue => ex
49
71
  puts ex
50
72
  nil
51
73
  end
52
74
 
53
- def nix_prefetch_git(uri, revision)
75
+ def nix_prefetch_git(uri, revision, submodules: false)
54
76
  home = ENV['HOME']
55
77
  ENV['HOME'] = '/homeless-shelter'
56
- sh(NIX_PREFETCH_GIT, '--url', uri, '--rev', revision, '--hash', 'sha256', '--leave-dotGit')
78
+
79
+ args = []
80
+ args << '--url' << uri
81
+ args << '--rev' << revision
82
+ args << '--hash' << 'sha256'
83
+ args << '--fetch-submodules' if submodules
84
+
85
+ sh(NIX_PREFETCH_GIT, *args)
57
86
  ensure
58
87
  ENV['HOME'] = home
59
88
  end
60
89
 
90
+ def format_hash(hash)
91
+ sh(NIX_HASH, '--type', 'sha256', '--to-base32', hash)[SHA256_32]
92
+ end
93
+
61
94
  def fetch_local_hash(spec)
62
95
  spec.source.caches.each do |cache|
63
- path = File.join(cache, "#{spec.name}-#{spec.version}.gem")
96
+ path = File.join(cache, "#{spec.full_name}.gem")
64
97
  next unless File.file?(path)
65
- hash = nix_prefetch_url("file://#{path}")[SHA256_32]
66
- return hash if hash
98
+ hash = nix_prefetch_url(path)[SHA256_32]
99
+ return format_hash(hash) if hash
67
100
  end
68
101
 
69
102
  nil
@@ -72,14 +105,14 @@ class Bundix
72
105
  def fetch_remotes_hash(spec, remotes)
73
106
  remotes.each do |remote|
74
107
  hash = fetch_remote_hash(spec, remote)
75
- return remote, hash if hash
108
+ return remote, format_hash(hash) if hash
76
109
  end
77
110
 
78
111
  nil
79
112
  end
80
113
 
81
114
  def fetch_remote_hash(spec, remote)
82
- uri = "#{remote}/gems/#{spec.name}-#{spec.version}.gem"
115
+ uri = "#{remote}/gems/#{spec.full_name}.gem"
83
116
  result = nix_prefetch_url(uri)
84
117
  return unless result
85
118
  result[SHA256_32]
@@ -88,14 +121,36 @@ class Bundix
88
121
  puts e.backtrace
89
122
  nil
90
123
  end
124
+ end
125
+
126
+ class Source < Struct.new(:spec, :fetcher)
127
+ def convert
128
+ case spec.source
129
+ when Bundler::Source::Rubygems
130
+ convert_rubygems
131
+ when Bundler::Source::Git
132
+ convert_git
133
+ when Bundler::Source::Path
134
+ convert_path
135
+ else
136
+ pp spec
137
+ fail 'unkown bundler source'
138
+ end
139
+ end
140
+
141
+ def convert_path
142
+ {
143
+ type: "path",
144
+ path: spec.source.path
145
+ }
146
+ end
91
147
 
92
148
  def convert_rubygems
93
149
  remotes = spec.source.remotes.map{|remote| remote.to_s.sub(/\/+$/, '') }
94
- hash = fetch_local_hash(spec)
95
- remote, hash = fetch_remotes_hash(spec, remotes) unless hash
96
- fail "couldn't fetch hash for #{spec.name}-#{spec.version}" unless hash
97
- hash = sh(NIX_HASH, '--type', 'sha256', '--to-base32', hash)[SHA256_32]
98
- puts "#{hash} => #{spec.name}-#{spec.version}.gem" if $VERBOSE
150
+ hash = fetcher.fetch_local_hash(spec)
151
+ remote, hash = fetcher.fetch_remotes_hash(spec, remotes) unless hash
152
+ fail "couldn't fetch hash for #{spec.full_name}" unless hash
153
+ puts "#{hash} => #{spec.full_name}.gem" if $VERBOSE
99
154
 
100
155
  { type: 'gem',
101
156
  remotes: (remote ? [remote] : remotes),
@@ -105,17 +160,18 @@ class Bundix
105
160
  def convert_git
106
161
  revision = spec.source.options.fetch('revision')
107
162
  uri = spec.source.options.fetch('uri')
108
- output = nix_prefetch_git(uri, revision)
163
+ submodules = !!spec.source.submodules
164
+ output = fetcher.nix_prefetch_git(uri, revision, submodules: submodules)
109
165
  # FIXME: this is a hack, we should separate $stdout/$stderr in the sh call
110
166
  hash = JSON.parse(output[/({[^}]+})\s*\z/m])['sha256']
111
- fail "couldn't fetch hash for #{spec.name}-#{spec.version}" unless hash
167
+ fail "couldn't fetch hash for #{spec.full_name}" unless hash
112
168
  puts "#{hash} => #{uri}" if $VERBOSE
113
169
 
114
170
  { type: 'git',
115
171
  url: uri.to_s,
116
172
  rev: revision,
117
173
  sha256: hash,
118
- fetchSubmodules: false }
174
+ fetchSubmodules: submodules }
119
175
  end
120
176
  end
121
177
  end
@@ -1,3 +1,3 @@
1
1
  class Bundix
2
- VERSION = '2.3.0'
2
+ VERSION = '2.5.0'
3
3
  end
@@ -0,0 +1 @@
1
+ [<% obj.sort{|l,r| Nixer.order(l,r)}.each_with_index do |o, n|%><%if n > 0%> <%end%><%= sub(o) %><%end%>]
@@ -0,0 +1,3 @@
1
+ {
2
+ <% obj.sort_by{|k,v| k.to_s.downcase}.each do |(k,v)| %><%= indent %><%= serialize_key(k) %> = <%= sub(v,2)%>;
3
+ <%end%><%= outdent%>}
@@ -0,0 +1,13 @@
1
+ with (import <nixpkgs> {});
2
+ let
3
+ env = bundlerEnv {
4
+ name = "<%= project %>-bundler-env";
5
+ inherit <%= ruby %>;
6
+ gemfile = <%= gemfile_path %>;
7
+ lockfile = <%= lockfile_path %>;
8
+ gemset = <%= gemset_path %>;
9
+ };
10
+ in stdenv.mkDerivation {
11
+ name = "<%= project %>";
12
+ buildInputs = [ env ];
13
+ }
metadata CHANGED
@@ -1,27 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bundix
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael 'manveru' Fellinger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-05 00:00:00.000000000 Z
11
+ date: 2019-09-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.11'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.11'
27
27
  description: Creates Nix packages from Gemfiles.
@@ -33,9 +33,14 @@ extra_rdoc_files: []
33
33
  files:
34
34
  - bin/bundix
35
35
  - lib/bundix.rb
36
- - lib/bundix/fetchurl-force.nix
36
+ - lib/bundix/commandline.rb
37
+ - lib/bundix/nixer.rb
38
+ - lib/bundix/shell_nix_context.rb
37
39
  - lib/bundix/source.rb
38
40
  - lib/bundix/version.rb
41
+ - template/nixer/list.erb
42
+ - template/nixer/set.erb
43
+ - template/shell-nix.erb
39
44
  homepage: https://github.com/manveru/bundix
40
45
  licenses:
41
46
  - MIT
@@ -56,7 +61,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
56
61
  version: '0'
57
62
  requirements: []
58
63
  rubyforge_project:
59
- rubygems_version: 2.6.10
64
+ rubygems_version: 2.7.6.2
60
65
  signing_key:
61
66
  specification_version: 4
62
67
  summary: Creates Nix packages from Gemfiles.
@@ -1,27 +0,0 @@
1
- { nixpkgs ? import <nixpkgs> {}
2
- , url
3
- }:
4
-
5
- with nixpkgs;
6
-
7
- # (ab)use fetchurl to download a URL and add it to the nix store
8
- # *without* already knowing its hash.
9
- fetchurl {
10
- inherit url;
11
- sha256 = "0000000000000000000000000000000000000000000000000000";
12
- downloadToTemp = true;
13
- postFetch = ''
14
- PATH="$PATH:${stdenv.lib.makeBinPath [ nix ]}"
15
-
16
- sha256="$(nix-hash --base32 --type sha256 --flat "$downloadedFile")"
17
- printf "%s\n" "$sha256"
18
-
19
- file="$(dirname "$downloadedFile")/$(basename "$url")"
20
- mv "$downloadedFile" "$file"
21
- echo "adding to nix store..."
22
- nix-store --add "$file"
23
-
24
- echo "success! failing outer nix build..."
25
- exit 1
26
- '';
27
- }