Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

First pass at bbl lock

  • Loading branch information...
commit 88741e63efa54219da9410e71d9530ad436b4097 1 parent 13f4a88
Carlhuda authored
View
11 lib/bubble.rb
@@ -1,4 +1,5 @@
require 'pathname'
+require 'yaml'
require 'bubble/rubygems'
module Bubble
@@ -18,17 +19,23 @@ module Bubble
class GemfileNotFound < StandardError; end
class GemNotFound < StandardError; end
class VersionConflict < StandardError; end
+ class GemfileError < StandardError; end
def self.setup(gemfile = nil)
load(gemfile).setup
end
def self.load(gemfile = nil)
- Environment.new(definition(gemfile))
+ Environment.new definition(gemfile)
end
def self.definition(gemfile = nil)
- Definition.from_gemfile(gemfile)
+ lockfile = Pathname.new(gemfile || Definition.default_gemfile).dirname.join('omg.yml')
+ if lockfile.exist?
+ Definition.from_lock(lockfile)
+ else
+ Definition.from_gemfile(gemfile)
+ end
end
def self.home
View
9 lib/bubble/cli.rb
@@ -8,6 +8,10 @@
module Bubble
class CLI < Thor
+ def self.banner(task)
+ task.formatted_usage(self, false)
+ end
+
desc "init", "Generates a Gemfile into the current working directory"
def init
if File.exist?("Gemfile")
@@ -43,6 +47,11 @@ def install
Installer.install(Bubble.definition)
end
+ desc "lock", "Locks a resolve"
+ def lock
+ Bubble.load.lock
+ end
+
private
def with_rescue
View
74 lib/bubble/definition.rb
@@ -7,9 +7,7 @@ def self.from_gemfile(gemfile)
raise GemfileNotFound, "`#{gemfile}` not found"
end
- definition = new
- Dsl.evaluate(gemfile, definition)
- definition
+ Dsl.evaluate(gemfile)
end
def self.default_gemfile
@@ -24,10 +22,76 @@ def self.default_gemfile
raise GemfileNotFound, "The default Gemfile was not found"
end
+ def self.from_lock(lockfile)
+ gemfile_definition = from_gemfile(nil)
+
+ details = YAML.load_file(lockfile)
+ sources = details["sources"].map do |args|
+ name, options = args.to_a.flatten
+ Bubble::Source.const_get(name).new(options)
+ end
+
+ dependencies = details["dependencies"].map do |args|
+ Gem::Dependency.new(*args.to_a.flatten)
+ end
+
+ specs = details["specs"].map do |args|
+ Gem::Dependency.new(*args.to_a.flatten)
+ end
+
+ locked_definition = new(dependencies, sources, specs)
+
+ raise GemfileError unless gemfile_definition.matches?(locked_definition) &&
+ locked_definition.matches?(gemfile_definition)
+
+ locked_definition
+ end
+
attr_reader :dependencies, :sources
- def initialize
- @dependencies, @sources = [], Gem.sources.map { |s| Source::Rubygems.new(:uri => s) }
+ def initialize(dependencies, sources, resolved_dependencies = nil)
+ @dependencies = dependencies
+ @sources = sources
+
+ if resolved_dependencies
+ @specs = resolved_dependencies.map do |dep|
+ index.search(dep).first
+ end
+ end
+ end
+
+ def matches?(other)
+ dependencies.all? do |dep|
+ dep =~ other.specs.find {|spec| spec.name == dep.name }
+ end
+ end
+
+ def specs
+ @specs ||= Resolver.resolve(dependencies, index)
+ end
+
+ def index
+ @index ||= begin
+ index = Index.new
+ sources.reverse_each do |source|
+ index.merge! source.local_specs
+ end
+ index
+ end
+ end
+
+ def to_yaml(options = {})
+ details.to_yaml(options)
+ end
+
+ private
+
+ def details
+ {}.tap do |det|
+ det["sources"] = sources.map { |s| { s.class.name.split("::").last => s.options} }
+ det["specs"] = specs.map { |s| {s.name => s.version.to_s} }
+ det["dependencies"] = dependencies.map { |d| {d.name => d.version_requirements.to_s} }
+ end
end
end
end
View
19 lib/bubble/dsl.rb
@@ -2,14 +2,15 @@ module Bubble
class DslError < StandardError; end
class Dsl
- def self.evaluate(gemfile, definition)
- builder = new(definition)
+ def self.evaluate(gemfile)
+ builder = new
builder.instance_eval(File.read(gemfile.to_s), gemfile.to_s, 1)
- definition
+ builder.to_definition
end
- def initialize(definition)
- @definition = definition
+ def initialize
+ @sources = Gem.sources.map { |s| Source::Rubygems.new(:uri => s) }
+ @dependencies = []
@git = nil
@git_sources = {}
end
@@ -18,7 +19,7 @@ def gem(name, *args)
options = Hash === args.last ? args.pop : {}
version = args.last || ">= 0"
- @definition.dependencies << Dependency.new(name, version, options)
+ @dependencies << Dependency.new(name, version, options)
end
def source(source)
@@ -28,7 +29,7 @@ def source(source)
else source
end
- @definition.sources << source
+ @sources << source
end
def path(path, options = {})
@@ -39,5 +40,9 @@ def git(uri, options = {})
source Source::Git.new(options.merge(:uri => uri))
end
+ def to_definition
+ Definition.new(@dependencies, @sources)
+ end
+
end
end
View
21 lib/bubble/environment.rb
@@ -1,5 +1,9 @@
module Bubble
class Environment
+ def self.from_gemfile(gemfile)
+ new Definition.from_gemfile(gemfile)
+ end
+
def initialize(definition)
@definition = definition
end
@@ -16,18 +20,19 @@ def dependencies
@definition.dependencies
end
+ def lock
+ yml = @definition.to_yaml
+ File.open("#{Definition.default_gemfile.dirname}/omg.yml", 'w') do |f|
+ f.puts yml
+ end
+ end
+
def specs
- @specs ||= Resolver.resolve(dependencies, index)
+ @definition.specs
end
def index
- @index ||= begin
- index = Index.new
- @definition.sources.reverse_each do |source|
- index.merge! source.local_specs
- end
- index
- end
+ @definition.index
end
end
View
11 lib/bubble/source.rb
@@ -4,9 +4,10 @@
module Bubble
module Source
class Rubygems
- attr_reader :uri
+ attr_reader :uri, :options
def initialize(options = {})
+ @options = options
@uri = options[:uri]
@uri = URI.parse(@uri) unless @uri.is_a?(URI)
raise ArgumentError, "The source must be an absolute URI" unless @uri.absolute?
@@ -52,9 +53,10 @@ def prerelease_specs
end
class Path
- attr_reader :path
+ attr_reader :path, :options
def initialize(options)
+ @options = options
@glob = options[:glob] || "{,*/}*.gemspec"
@path = options[:path]
end
@@ -86,11 +88,16 @@ class Git < Path
attr_reader :uri, :ref
def initialize(options)
+ @options = options
@glob = options[:glob] || "{,*/}*.gemspec"
@uri = options[:uri]
@ref = options[:ref] || options[:branch] || 'master'
end
+ def to_yaml(options = {})
+ { :uri => @uri.to_s, :ref => @ref, :glob => @glob }.to_yaml
+ end
+
def path
Bubble.install_path.join("#{base_name}-#{uri_hash}-#{ref}")
end
View
23 spec/lock/gems_spec.rb
@@ -0,0 +1,23 @@
+require File.expand_path('../../spec_helper', __FILE__)
+
+describe "bbl lock with gems" do
+ before :each do
+ in_app_root
+ end
+
+ it "locks the gemfile resolve to the versions available at the time" do
+ gemfile <<-G
+ gem "rack"
+ G
+
+ system_gems "rack-0.9.1" do
+ bbl :lock
+ end
+
+ system_gems "rack-1.0.0", "rack-0.9.1" do
+ should_be_available "rack 0.9.1"
+ end
+ end
+
+ it "includes the ruby version as a dependency of the lock"
+end
View
84 spec/runtime/load_spec.rb
@@ -37,4 +37,88 @@
Bubble.load("omg.rb")
}.should raise_error(Bubble::GemfileNotFound, /omg\.rb/)
end
+
+ describe "when locked" do
+ before :each do
+ system_gems "rack-1.0.0", "activesupport-2.3.2", "activerecord-2.3.2", "activerecord-2.3.1"
+ end
+
+ it "raises an exception if the Gemfile adds a dependency" do
+ gemfile <<-G
+ gem "rack"
+ G
+
+ bbl :lock
+
+ gemfile <<-G
+ gem "rack"
+ gem "activerecord"
+ G
+
+ lambda { Bubble.load }.should raise_error(Bubble::GemfileError)
+ end
+
+ it "raises an exception if the Gemfile removes a dependency" do
+ gemfile <<-G
+ gem "rack"
+ gem "activerecord"
+ G
+
+ bbl :lock
+
+ gemfile <<-G
+ gem "rack"
+ G
+
+ lambda { Bubble.load }.should raise_error(Bubble::GemfileError)
+ end
+
+ it "raises an exception if the Gemfile changes a dependency in an incompatible way" do
+ gemfile <<-G
+ gem "rack"
+ gem "activerecord"
+ G
+
+ bbl :lock
+
+ gemfile <<-G
+ gem "rack"
+ gem "activerecord", "2.3.1"
+ G
+
+ lambda { Bubble.load }.should raise_error(Bubble::GemfileError)
+ end
+
+ it "raises an exception if the Gemfile replaces a root with a child dep of the root" do
+ gemfile <<-G
+ gem "rack"
+ gem "activerecord"
+ G
+
+ bbl :lock
+
+ gemfile <<-G
+ gem "rack"
+ gem "activesupport"
+ G
+
+ lambda { Bubble.load }.should raise_error(Bubble::GemfileError)
+ end
+
+ it "works if the Gemfile changes in a compatible way" do
+ gemfile <<-G
+ gem "rack"
+ gem "activerecord", "2.3.2"
+ G
+
+ bbl :lock
+
+ gemfile <<-G
+ gem "rack"
+ gem "activerecord", ">= 2.0.0"
+ G
+
+ lambda { Bubble.load }.should_not raise_error(Bubble::GemfileError)
+ end
+ end
end
View
2  spec/support/builders.rb
@@ -23,7 +23,7 @@ def build_repo1
build_gem "actionpack", "2.3.2" do |s|
s.add_dependency "activesupport", "2.3.2"
end
- build_gem "activerecord", "2.3.2" do |s|
+ build_gem "activerecord", ["2.3.1", "2.3.2"] do |s|
s.add_dependency "activesupport", "2.3.2"
end
build_gem "actionmailer", "2.3.2" do |s|
View
2  spec/support/matchers.rb
@@ -22,5 +22,7 @@ def should_be_installed(*names)
Gem::Version.new(out).should == Gem::Version.new(version)
end
end
+
+ alias should_be_available should_be_installed
end
end
Please sign in to comment.
Something went wrong with that request. Please try again.