Last active
August 29, 2015 14:05
-
-
Save aflatter/5ab21c9b032f84c27361 to your computer and use it in GitHub Desktop.
Writes a gems.nix for use with the nixpkgs/pleasant-ruby
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'bundler' | |
require 'erb' | |
# Generates a Nix expression for bundler-managed dependencies. | |
module Bundix | |
class << self | |
def run(gemfile = './Gemfile', lockfile = 'Gemfile.lock') | |
gems = resolve_deps(gemfile, lockfile) | |
checksums = compute_checksums(gems) | |
write_expr(gems, checksums) | |
end | |
def resolve_deps(gemfile, lockfile) | |
definition = Bundler::Definition.build(gemfile, lockfile, {}) | |
definition.resolve.tsort.map(&Bundix::Gem.method(:new)) | |
end | |
def compute_checksums(gems) | |
Checksummer.run(gems) | |
end | |
def write_expr(gems, checksums) | |
template = File.read(__FILE__).split('__END__').last.strip | |
ERB.new(template, nil, '->').result(binding) | |
end | |
def to_attr_name(str) | |
# TODO: This could lead to naming conflicts. Investigate. | |
str.gsub('.', '_') | |
end | |
end | |
class Dependency | |
def initialize(dependency) | |
@dependency = dependency | |
end | |
def attr_name | |
Bundix.to_attr_name(@dependency.name) | |
end | |
end | |
class Gem | |
def initialize(spec) | |
@spec = spec | |
end | |
def attr_name | |
Bundix.to_attr_name(@spec.name) | |
end | |
def drv_name | |
"#{@spec.name}-#{version}" | |
end | |
def version | |
@spec.version.to_s | |
end | |
def url | |
"http://rubygems.org/downloads/#{filename}" | |
end | |
def filename | |
"#{@spec.name}-#{@spec.version}.gem" | |
end | |
def dependencies | |
@spec.dependencies.map { |dep| Dependency.new(dep) } | |
end | |
end | |
# Uses nix-prefetch-scripts to get the checksums of a set of gems. | |
# The results are cached in order to speed up repeated runs. | |
class Checksummer | |
class << self | |
def run(gems) | |
new(gems).run | |
end | |
end | |
def initialize(gems, cache_path = '.bundix/checksums') | |
@gems = gems | |
@cache_path = cache_path | |
end | |
def run | |
cache = load_cache | |
result = @gems.each_with_object(Hash.new) do |gem, hash| | |
drv_name = gem.drv_name | |
hash[drv_name] = cache[drv_name] || prefetch(gem.url) | |
end | |
write_cache(result) | |
result | |
end | |
def load_cache | |
File.readlines(@cache_path).each_with_object(Hash.new) do |line, hash| | |
drv_name, checksum = line.split(':').map(&:strip) | |
hash[drv_name] = checksum | |
end | |
end | |
def prefetch(url) | |
output = `nix-prefetch-url #{url} 2>/dev/null`.strip | |
raise "Prefetch failed: #{url}" unless $?.success? | |
unless output.length == 52 && output =~ /^[a-z0-9]+$/ | |
raise "Invalid checksum for #{url}: #{output}" | |
end | |
output | |
end | |
def write_cache(checksums) | |
File.open(@cache_path, 'w') do |file| | |
checksums.each do |drv_name, checksum| | |
file.write("#{drv_name}: #{checksum}\n") | |
end | |
end | |
end | |
end | |
end | |
puts Bundix.run | |
__END__ | |
{ }: | |
rec { | |
<%- gems.each do |gem| %> | |
<%= gem.attr_name %> = { | |
name = "<%= gem.drv_name %>"; | |
sha256 = "<%= checksums[gem.drv_name] %>"; | |
<%- if gem.dependencies.any? -%> | |
dependencies = [ | |
<%= gem.dependencies.map { |dep| %Q{"#{dep.attr_name}"} }.join("\n ") %> | |
]; | |
<%- end -%> | |
}; | |
<%- end -%> | |
bundler = { | |
name = "bundler-1.7.1"; | |
sha256 = "144yqbmi89gl933rh8dv58bm7ia14s4a098qdi2z0q09ank9n5h2"; | |
}; | |
buildInputs = [ | |
<%- gems.each do |gem| -%> | |
"<%= gem.attr_name %>" | |
<%- end -%> | |
]; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@aflatter Regarding this comment:
# TODO: This could lead to naming conflicts. Investigate.
An attribute path in nix may contain periods, you just need to do something like:
So you shouldn't need to escape the name, as long as it's quoted.