librarian 0.0.12 → 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 0.0.13
2
+
3
+ * \#36 Fixes an issue where, if a dependency has a git source (the upstream), and the upstream is updated,
4
+ then attempting to update that dependency in the local resolution would not update that dependency so
5
+ long as that git source were cached (@databus23).
6
+
7
+ * More immediate detection of, and better error messages for, cases of blank dependency or manifest names
8
+ or cases of names that are otherwise insensible, such as names that are untrimmed.
9
+
1
10
  ## 0.0.12
2
11
 
3
12
  * Fixes an issue where, if a dependency has a git source with a ref, re-resolving may fail.
data/README.md CHANGED
@@ -12,7 +12,7 @@ The mock source is in-process and in-memory and does not touch the filesystem or
12
12
  Librarian::Chef
13
13
  ---------------
14
14
 
15
- An adapter for Librarian applying to Chef cookbooks in a Chef Repository.
15
+ An adapter for Librarian applying to Chef cookbooks in a Chef Repository. When used with Chef, Librarian is really for pulling in the 50 or so finished third-party cookbooks that you're using, not the 1 or 2 cookbooks you're actively working on.
16
16
 
17
17
  ## Install librarian:
18
18
 
@@ -24,6 +24,7 @@ __Make sure your cookbooks directory is gitignored__
24
24
  $ cd ~/path/to/chef-repo
25
25
  $ git rm -r cookbooks # if the directory is present
26
26
  $ echo cookbooks >> .gitignore
27
+ $ echo tmp >> .gitignore
27
28
 
28
29
  Note that librarian *takes over* your cookbooks directory
29
30
  and manages it for you based on your Cheffile. Your
@@ -40,7 +41,7 @@ __Add dependencies and their sources to Cheffile__
40
41
  $ cat Cheffile
41
42
  site 'http://community.opscode.com/api/v1'
42
43
  cookbook 'ntp'
43
- cookbook 'timezone'
44
+ cookbook 'timezone', '0.0.1'
44
45
  cookbook 'rvm',
45
46
  :git => 'https://github.com/fnichol/chef-rvm',
46
47
  :ref => 'v0.7.1'
@@ -59,7 +60,7 @@ __Update your cheffile with new/changed/removed constraints/sources/dependencies
59
60
  $ cat Cheffile
60
61
  site 'http://community.opscode.com/api/v1'
61
62
  cookbook 'ntp'
62
- cookbook 'timezone'
63
+ cookbook 'timezone', '0.0.1'
63
64
  cookbook 'rvm',
64
65
  :git => 'https://github.com/fnichol/chef-rvm',
65
66
  :ref => 'v0.7.1'
@@ -17,6 +17,8 @@ module Librarian
17
17
  spec = specfile.read(previous_resolution.sources)
18
18
  spec_changes = spec_change_set(spec, previous_resolution)
19
19
  raise Error, "Cannot update when the specfile has been changed." unless spec_changes.same?
20
+ unpinnable_sources = previous_resolution.sources - partial_manifests.map(&:source)
21
+ unpinnable_sources.each(&:unpin!)
20
22
  resolution = resolver.resolve(spec, partial_manifests)
21
23
  unless resolution.correct?
22
24
  raise Error, "Could not resolve the dependencies."
@@ -54,6 +54,13 @@ module Librarian
54
54
  {:remote => uri}
55
55
  end
56
56
 
57
+ def pinned?
58
+ false
59
+ end
60
+
61
+ def unpin!
62
+ end
63
+
57
64
  def cache!(dependencies)
58
65
  cache_path.mkpath
59
66
  dependencies.each do |dependency|
@@ -5,6 +5,8 @@ site 'http://community.opscode.com/api/v1'
5
5
 
6
6
  # cookbook 'chef-client'
7
7
 
8
+ # cookbook 'apache2', '>= 1.0.0'
9
+
8
10
  # cookbook 'rvm',
9
11
  # :git => 'https://github.com/fnichol/chef-rvm'
10
12
 
@@ -10,12 +10,16 @@ module Librarian
10
10
 
11
11
  include Helpers::Debug
12
12
 
13
- attr_reader :name, :requirement, :source
13
+ attr_accessor :name, :requirement, :source
14
+ private :name=, :requirement=, :source=
14
15
 
15
16
  def initialize(name, requirement, source)
16
- @name = name
17
- @requirement = Requirement.create(requirement)
18
- @source = source
17
+ assert_name_valid! name
18
+
19
+ self.name = name
20
+ self.requirement = Requirement.create(requirement)
21
+ self.source = source
22
+
19
23
  @manifests = nil
20
24
  end
21
25
 
@@ -50,5 +54,9 @@ module Librarian
50
54
  source.environment
51
55
  end
52
56
 
57
+ def assert_name_valid!(name)
58
+ raise ArgumentError, "name (#{name.inspect}) must be sensible" unless name =~ /^\S.*\S$/
59
+ end
60
+
53
61
  end
54
62
  end
@@ -12,14 +12,18 @@ module Librarian
12
12
  include Support::AbstractMethod
13
13
  include Helpers::Debug
14
14
 
15
- attr_reader :source, :name
15
+ attr_accessor :source, :name
16
+ private :source=, :name=
16
17
 
17
18
  abstract_method :fetch_version!, :fetch_dependencies!
18
19
  abstract_method :install!
19
20
 
20
21
  def initialize(source, name)
21
- @source = source
22
- @name = name
22
+ assert_name_valid! name
23
+
24
+ self.source = source
25
+ self.name = name
26
+
23
27
  @fetched_version = nil
24
28
  @defined_version = nil
25
29
  @fetched_dependencies = nil
@@ -84,5 +88,9 @@ module Librarian
84
88
  dependencies.sort_by{|d| d.name}
85
89
  end
86
90
 
91
+ def assert_name_valid!(name)
92
+ raise ArgumentError, "name (#{name.inspect}) must be sensible" unless name =~ /^\S.*\S$/
93
+ end
94
+
87
95
  end
88
96
  end
@@ -65,6 +65,14 @@ module Librarian
65
65
  options
66
66
  end
67
67
 
68
+ def pinned?
69
+ !!sha
70
+ end
71
+
72
+ def unpin!
73
+ @sha = nil
74
+ end
75
+
68
76
  def cache!(dependencies)
69
77
  unless repository.git?
70
78
  repository.path.rmtree if repository.path.exist?
@@ -73,7 +81,9 @@ module Librarian
73
81
  end
74
82
  unless sha == repository.current_commit_hash
75
83
  repository.fetch!(:tags => true)
76
- repository.checkout!(sha || ref)
84
+ repository.fetch!
85
+ repository.merge_all_remote_branches!
86
+ repository.reset_hard! repository.hash_from(sha || ref)
77
87
  @sha ||= repository.current_commit_hash
78
88
  end
79
89
  end
@@ -47,6 +47,13 @@ module Librarian
47
47
  end
48
48
  end
49
49
 
50
+ def reset_hard!(reference)
51
+ within do
52
+ command = "reset --hard #{reference}"
53
+ run!(command, false)
54
+ end
55
+ end
56
+
50
57
  def fetch!(options = { })
51
58
  within do
52
59
  command = "fetch"
@@ -55,6 +62,13 @@ module Librarian
55
62
  end
56
63
  end
57
64
 
65
+ def merge!(reference)
66
+ within do
67
+ command = "merge #{reference}"
68
+ run!(command)
69
+ end
70
+ end
71
+
58
72
  def hash_from(reference)
59
73
  within do
60
74
  command = "rev-parse #{reference}"
@@ -69,10 +83,26 @@ module Librarian
69
83
  end
70
84
  end
71
85
 
86
+ def merge_all_remote_branches!
87
+ remote_branches.each do |branch|
88
+ checkout!(branch.slice(%r{[^/]+$}))
89
+ merge! branch
90
+ end
91
+ end
92
+
93
+ def remote_branches
94
+ within do
95
+ command ="branch -r --no-color"
96
+ run!(command, false).split("\n ").reject do |r|
97
+ r.include? '->' #delete pointers like origin/HEAD -> origin/master
98
+ end.collect {|r|r.strip}
99
+ end
100
+ end
72
101
  private
73
102
 
74
- def run!(text)
75
- text = "git #{text} --quiet"
103
+ def run!(text, quiet = true)
104
+ text = "git #{text}"
105
+ text << " --quiet" if quiet
76
106
  debug { "Running `#{text}` in #{relative_path_to(Dir.pwd)}" }
77
107
  out = Open3.popen3(text) do |i, o, e, t|
78
108
  raise StandardError, e.read unless (t ? t.value : $?).success?
@@ -43,6 +43,13 @@ module Librarian
43
43
  {:remote => path}
44
44
  end
45
45
 
46
+ def pinned?
47
+ false
48
+ end
49
+
50
+ def unpin!
51
+ end
52
+
46
53
  def cache!(dependencies)
47
54
  end
48
55
 
@@ -1,10 +1,12 @@
1
1
  module Librarian
2
2
  class Spec
3
3
 
4
- attr_reader :source, :dependencies
4
+ attr_accessor :source, :dependencies
5
+ private :source=, :dependencies=
5
6
 
6
7
  def initialize(source, dependencies)
7
- @source, @dependencies = source, dependencies
8
+ self.source = source
9
+ self.dependencies = dependencies
8
10
  end
9
11
 
10
12
  end
@@ -1,3 +1,3 @@
1
1
  module Librarian
2
- VERSION = "0.0.12"
2
+ VERSION = "0.0.13"
3
3
  end
@@ -6,6 +6,7 @@ require 'librarian/helpers'
6
6
  require 'librarian/error'
7
7
  require 'librarian/action/resolve'
8
8
  require 'librarian/action/install'
9
+ require 'librarian/action/update'
9
10
  require 'librarian/chef'
10
11
 
11
12
  module Librarian
@@ -337,6 +338,94 @@ module Librarian
337
338
  end
338
339
  end
339
340
 
341
+ context "when upstream updates" do
342
+ let(:git_path) { tmp_path.join("upstream-updates-repo") }
343
+ let(:repo_path) { tmp_path.join("repo/resolve-with-upstream-updates") }
344
+
345
+ let(:sample_metadata) do
346
+ Helpers.strip_heredoc(<<-METADATA)
347
+ version "0.6.5"
348
+ METADATA
349
+ end
350
+ before do
351
+
352
+ # set up the git repo as normal, but let's also set up a release-stable branch
353
+ # from which our Cheffile will only pull stable releases
354
+ git_path.rmtree if git_path.exist?
355
+ git_path.mkpath
356
+ git_path.join("metadata.rb").open("w+b"){|f| f.write(sample_metadata)}
357
+
358
+ Dir.chdir(git_path) do
359
+ `git init`
360
+ `git add metadata.rb`
361
+ `git commit -m "Initial Commit."`
362
+ `git checkout -b some-branch --quiet`
363
+ `echo 'hi' > some-file`
364
+ `git add some-file`
365
+ `git commit -m 'Some File.'`
366
+ `git checkout master --quiet`
367
+ end
368
+
369
+ # set up the chef repo as normal, except the Cheffile points to the release-stable
370
+ # branch - we expect when the upstream copy of that branch is changed, then we can
371
+ # fetch & merge those changes when we update
372
+ repo_path.rmtree if repo_path.exist?
373
+ repo_path.mkpath
374
+ repo_path.join("cookbooks").mkpath
375
+ cheffile = Helpers.strip_heredoc(<<-CHEFFILE)
376
+ cookbook "sample",
377
+ :git => #{git_path.to_s.inspect},
378
+ :ref => "some-branch"
379
+ CHEFFILE
380
+ repo_path.join("Cheffile").open("wb") { |f| f.write(cheffile) }
381
+ Action::Resolve.new(env).run
382
+
383
+ # change the upstream copy of that branch: we expect to be able to pull the latest
384
+ # when we re-resolve
385
+ Dir.chdir(git_path) do
386
+ `git checkout some-branch --quiet`
387
+ `echo 'ho' > some-other-file`
388
+ `git add some-other-file`
389
+ `git commit -m 'Some Other File.'`
390
+ `git checkout master --quiet`
391
+ end
392
+ end
393
+
394
+ let(:metadata_file) { repo_path.join("cookbooks/sample/metadata.rb") }
395
+ let(:old_code_file) { repo_path.join("cookbooks/sample/some-file") }
396
+ let(:new_code_file) { repo_path.join("cookbooks/sample/some-other-file") }
397
+
398
+ context "when updating not a cookbook from that source" do
399
+ before do
400
+ Action::Update.new(env).run
401
+ end
402
+
403
+ it "should pull the tip from upstream" do
404
+ Action::Install.new(env).run
405
+
406
+ metadata_file.should exist #sanity
407
+ old_code_file.should exist #sanity
408
+
409
+ new_code_file.should_not exist # the assertion
410
+ end
411
+ end
412
+
413
+ context "when updating a cookbook from that source" do
414
+ before do
415
+ Action::Update.new(env, :names => %w(sample)).run
416
+ end
417
+
418
+ it "should pull the tip from upstream" do
419
+ Action::Install.new(env).run
420
+
421
+ metadata_file.should exist #sanity
422
+ old_code_file.should exist #sanity
423
+
424
+ new_code_file.should exist # the assertion
425
+ end
426
+ end
427
+ end
428
+
340
429
  end
341
430
  end
342
431
  end
@@ -0,0 +1,30 @@
1
+ require "librarian/dependency"
2
+
3
+ describe Librarian::Dependency do
4
+
5
+ describe "validations" do
6
+
7
+ context "when the name is blank" do
8
+ it "raises" do
9
+ expect { described_class.new("", [], nil) }.
10
+ to raise_error(ArgumentError, %{name ("") must be sensible})
11
+ end
12
+ end
13
+
14
+ context "when the name has leading whitespace" do
15
+ it "raises" do
16
+ expect { described_class.new(" the-name", [], nil) }.
17
+ to raise_error(ArgumentError, %{name (" the-name") must be sensible})
18
+ end
19
+ end
20
+
21
+ context "when the name has trailing whitespace" do
22
+ it "raises" do
23
+ expect { described_class.new("the-name ", [], nil) }.
24
+ to raise_error(ArgumentError, %{name ("the-name ") must be sensible})
25
+ end
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -8,6 +8,17 @@ module Librarian
8
8
 
9
9
  let(:env) { Environment.new }
10
10
 
11
+ context "a single source and a single dependency with a blank name" do
12
+ it "should not not run with a blank name" do
13
+ expect do
14
+ env.dsl do
15
+ src 'source-1'
16
+ dep ''
17
+ end
18
+ end.to raise_error(ArgumentError, %{name ("") must be sensible})
19
+ end
20
+ end
21
+
11
22
  context "a single dependency but no applicable source" do
12
23
 
13
24
  it "should not run without any sources" do
@@ -0,0 +1,30 @@
1
+ require "librarian/manifest"
2
+
3
+ describe Librarian::Manifest do
4
+
5
+ describe "validations" do
6
+
7
+ context "when the name is blank" do
8
+ it "raises" do
9
+ expect { described_class.new(nil, "") }.
10
+ to raise_error(ArgumentError, %{name ("") must be sensible})
11
+ end
12
+ end
13
+
14
+ context "when the name has leading whitespace" do
15
+ it "raises" do
16
+ expect { described_class.new(nil, " the-name") }.
17
+ to raise_error(ArgumentError, %{name (" the-name") must be sensible})
18
+ end
19
+ end
20
+
21
+ context "when the name has trailing whitespace" do
22
+ it "raises" do
23
+ expect { described_class.new(nil, "the-name ") }.
24
+ to raise_error(ArgumentError, %{name ("the-name ") must be sensible})
25
+ end
26
+ end
27
+
28
+ end
29
+
30
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: librarian
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.12
4
+ version: 0.0.13
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-11-18 00:00:00.000000000Z
12
+ date: 2012-02-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: thor
16
- requirement: &34503640 !ruby/object:Gem::Requirement
16
+ requirement: &16739740 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *34503640
24
+ version_requirements: *16739740
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &34503220 !ruby/object:Gem::Requirement
27
+ requirement: &16753740 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *34503220
35
+ version_requirements: *16753740
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &34502800 !ruby/object:Gem::Requirement
38
+ requirement: &16751920 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *34502800
46
+ version_requirements: *16751920
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: cucumber
49
- requirement: &34502380 !ruby/object:Gem::Requirement
49
+ requirement: &16749300 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *34502380
57
+ version_requirements: *16749300
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: aruba
60
- requirement: &34501960 !ruby/object:Gem::Requirement
60
+ requirement: &16748440 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *34501960
68
+ version_requirements: *16748440
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: webmock
71
- requirement: &34501540 !ruby/object:Gem::Requirement
71
+ requirement: &16754860 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '0'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *34501540
79
+ version_requirements: *16754860
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: chef
82
- requirement: &34501040 !ruby/object:Gem::Requirement
82
+ requirement: &17524920 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: '0.10'
88
88
  type: :runtime
89
89
  prerelease: false
90
- version_requirements: *34501040
90
+ version_requirements: *17524920
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: highline
93
- requirement: &34500620 !ruby/object:Gem::Requirement
93
+ requirement: &17522700 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,7 +98,7 @@ dependencies:
98
98
  version: '0'
99
99
  type: :runtime
100
100
  prerelease: false
101
- version_requirements: *34500620
101
+ version_requirements: *17522700
102
102
  description: Librarian
103
103
  email:
104
104
  - y_feldblum@yahoo.com
@@ -188,9 +188,11 @@ files:
188
188
  - spec/unit/action/clean_spec.rb
189
189
  - spec/unit/action/ensure_spec.rb
190
190
  - spec/unit/action/install_spec.rb
191
+ - spec/unit/dependency_spec.rb
191
192
  - spec/unit/dsl_spec.rb
192
193
  - spec/unit/environment_spec.rb
193
194
  - spec/unit/lockfile_spec.rb
195
+ - spec/unit/manifest_spec.rb
194
196
  - spec/unit/mock/source/mock.rb
195
197
  - spec/unit/resolver_spec.rb
196
198
  - spec/unit/spec_change_set_spec.rb