librarian 0.0.14 → 0.0.15

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.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 0.0.15
2
+
3
+ * Rewrite the README.
4
+
5
+ * \#44, \#49. Better updating of cached git sources without using local merges.
6
+
1
7
  ## 0.0.14
2
8
 
3
9
  * \#39 Fixes a regression induced by using `git reset --hard SHA`.
data/README.md CHANGED
@@ -1,42 +1,160 @@
1
1
  Librarian
2
2
  =========
3
3
 
4
- A tool to resolve recursively a set of specifications and fetch and install the fully resolved specifications.
4
+ Librarian is a framework for writing bundlers, which are tools that resolve,
5
+ fetch, install, and isolate a project's dependencies, in Ruby.
5
6
 
6
- Librarian::Mock
7
- ---------------
7
+ Librarian ships with Librarian-Chef, which is a bundler for your Chef-based
8
+ infrastructure repositories. In the future, Librarian-Chef will be a separate
9
+ project.
8
10
 
9
- An adapter for Librarian for unit testing the general features.
10
- The mock source is in-process and in-memory and does not touch the filesystem or the network.
11
+ A bundler written with Librarian will expect you to provide a specfile listing
12
+ your project's declared dependencies, including any version constraints and
13
+ including the upstream sources for finding them. Librarian can resolve the spec,
14
+ write a lockfile listing the full resolution, fetch the resolved dependencies,
15
+ install them, and isolate them in your project. This is what Bundler does for
16
+ projects that depend on Ruby gems.
11
17
 
12
- Librarian::Chef
18
+ Librarian-Chef
13
19
  ---------------
14
20
 
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.
21
+ Librarian-Chef is a bundler for infrastructure repositories using Chef. You can
22
+ use Librarian-Chef to resolve your infrastructure's cookbook dependencies and
23
+ fetch them and install them into your infrastructure.
16
24
 
17
- ## Install librarian:
25
+ Librarian-Chef is for resolving and fetching third-party, publicly-released
26
+ cookbooks, and installing them into your infrastructure repository. It is not
27
+ for dealing with the cookbooks you're actively working on within your
28
+ infrastructure repository.
18
29
 
19
- $ gem install librarian
30
+ Librarian-Chef *takes over* your `cookbooks/` directory and manages it for you
31
+ based on your `Cheffile`. Your `Cheffile` becomes the authoritative source for
32
+ what cookbooks your infrastructure repository depends on. You should not modify
33
+ the contents of your `cookbooks/` directory when using Librarian-Chef. If you
34
+ have custom cookbooks specific to your infrastructure repository that you need,
35
+ those should go in your `site-cookbooks/` directory.
36
+
37
+ ### The Cheffile
38
+
39
+ Every infrastruture repository that uses Librarian-Chef will have a file named
40
+ `Cheffile` in the root directory of the repository. The full specification for
41
+ which third-party, publicly-rleased cookbooks your infrastructure repository
42
+ depends will go here.
43
+
44
+ Here's an example `Cheffile`:
45
+
46
+ site "http://community.opscode.com/api/v1"
47
+
48
+ cookbook "ntp"
49
+ cookbook "timezone", "0.0.1"
50
+
51
+ cookbook "rvm",
52
+ :git => "https://github.com/fnichol/chef-rvm",
53
+ :ref => "v0.7.1"
54
+
55
+ cookbook "cloudera",
56
+ :path => "vendor/cookbooks/cloudera-cookbook"
57
+
58
+ Here's how it works:
59
+
60
+ We start off by declaring the *default source* for this `Cheffile`.
61
+
62
+ site "http://community.opscode.com/api/v1"
63
+
64
+ This default source in this example is the Opscode Community Site API. This is
65
+ most likely what you will want for your default source. However, you can
66
+ certainly set up your own API-compatible HTTP endpoint if you want more control.
67
+
68
+ Any time we declare a cookbook dependency without also declaring a source for
69
+ that cookbook dependency, Librarian-Chef assumes we want it to look for that
70
+ cookbook in the default source.
71
+
72
+ Any time we declare a cookbook dependency that has subsidiary cookbook
73
+ dependencies of its own, Librarian-Chef assumes we want it to look for the
74
+ subsidiary cookbook dependencies in the default source.
75
+
76
+ cookbook "ntp"
77
+
78
+ Our infrastructure repository depends on the `ntp` cookbook from the default
79
+ source. Any version of the `ntp` cookbook will fulfill our requirements.
80
+
81
+ cookbook "timezone", "0.0.1"
82
+
83
+ Our infrastructure repository depends on the `timezone` cookbook from the
84
+ default source. But only version `0.0.1` of that cookbook will do.
85
+
86
+ cookbook "rvm",
87
+ :git => "https://github.com/fnichol/chef-rvm",
88
+ :ref => "v0.7.1"
89
+
90
+ Our infrastructure repository depends on the `rvm` cookbook, but not the one
91
+ from the default source. Instead, the cookbook is to be fetched from the
92
+ specified Git repository and from the specified Git tag only.
93
+
94
+ We do not have to use a `:ref =>`. If we do not, then Librarian-Chef will assume
95
+ we meant the branch `master`. (In the future, this will be changed to whatever
96
+ branch is the default branch according to the Git remote, which may not be
97
+ `master`.)
98
+
99
+ If we use a `:ref =>`, we can use anything that Git will recognize as a ref.
100
+ This includes any branch name, tag name, SHA, or SHA unique prefix. If we use a
101
+ branch, we can later ask Librarian-Chef to update the cookbook by fetching the
102
+ most recent version of the cookbook from that same branch.
103
+
104
+ The Git source also supports a `:path =>` option. If we use the path option,
105
+ Librarian-Chef will navigate down into the Git repository and only use the
106
+ specified subdirectory. Many people have the havit of having a single repository
107
+ with many cookbooks in it. If we need a cookbook from such a repository, we can
108
+ use the `:path =>` option here to help Librarian-Chef drill down and find the
109
+ cookbook subdirectory.
20
110
 
111
+ cookbook "cloudera",
112
+ :path => "vendor/cookbooks/cloudera-cookbook"
21
113
 
22
- __Make sure your cookbooks directory is gitignored__
114
+ Our infrastructure repository depends on the `cloudera` cookbook, which we have
115
+ downloaded and copied into our repository. In this example, `vendor/cookbooks/`
116
+ is only for use with Librarian-Chef. This directory should not appear in the
117
+ `.chef/knife.rb`. Librarian-Chef will, instead, copy this cookbook from where
118
+ we vendored it in our repository into the `cookbooks/` directory for us.
119
+
120
+ The `:path =>` source won't be confused with the `:git =>` source's `:path =>`
121
+ option.
122
+
123
+ ### How to Use
124
+
125
+ Install librarian-chef:
126
+
127
+ $ gem install librarian
128
+
129
+ Prepare your infrastructure repository:
23
130
 
24
131
  $ cd ~/path/to/chef-repo
25
- $ git rm -r cookbooks # if the directory is present
132
+ $ git rm -r cookbooks
26
133
  $ echo cookbooks >> .gitignore
27
134
  $ echo tmp >> .gitignore
28
135
 
29
- Note that librarian *takes over* your cookbooks directory
30
- and manages it for you based on your Cheffile. Your
31
- Cheffile becomes the authoritative source for what
32
- cookbooks you have, rather than the directories in your
33
- cookbooks directory.
136
+ Librarian-Chef takes over your `cookbooks/` directory, and will always reinstall
137
+ the cookbooks listed the `Cheffile.lock` into your `cookbooks/` directory. Hence
138
+ you do not need your `cookbooks/` directory to be tracked in Git. If you
139
+ nevertheless want your `cookbooks/` directory to be tracked in Git, simple don't
140
+ `.gitignore` the directory.
34
141
 
35
- __Make a Cheffile__
142
+ If you are manually tracking/vendoring outside cookbooks within the repository,
143
+ put them in another directory such as `vendor/cookbooks/` and use the `:path =>`
144
+ source when declaring these cookbooks in your `Cheffile`. Most people will
145
+ typically not be manually tracking/vendoring outside cookbooks.
146
+
147
+ Librarian-Chef uses your `tmp/` directory for tempfiles and caches. You do not
148
+ need to track this directory in Git.
149
+
150
+ Make a Cheffile:
36
151
 
37
152
  $ librarian-chef init
38
153
 
39
- __Add dependencies and their sources to Cheffile__
154
+ This creates an empty `Cheffile` with the Opscode Community Site API as the
155
+ default source.
156
+
157
+ Add dependencies and their sources to the `Cheffile`:
40
158
 
41
159
  $ cat Cheffile
42
160
  site 'http://community.opscode.com/api/v1'
@@ -45,17 +163,41 @@ __Add dependencies and their sources to Cheffile__
45
163
  cookbook 'rvm',
46
164
  :git => 'https://github.com/fnichol/chef-rvm',
47
165
  :ref => 'v0.7.1'
166
+ cookbook 'cloudera',
167
+ :path => 'vendor/cookbooks/cloudera-cookbook'
48
168
 
49
- __install dependencies into ./cookbooks__
169
+ This is the same `Cheffile` we saw above.
50
170
 
51
171
  $ librarian-chef install [--clean] [--verbose]
52
172
 
53
- __Check your Cheffile.lock into version control__
173
+ This command looks at each `cookbook` declaration and fetches the cookbook from
174
+ the source specified for that cookbook, or from the default source if none is
175
+ provided for that cookbook.
176
+
177
+ Each cookbook is inspected and its dependencies determined, and each dependency
178
+ is also fetched. For example, if you declare `cookbook 'nagios'`, and that
179
+ cookbook depends on other cookbooks such as `'php'`, then those other cookbooks
180
+ including `'php'` will be fetched. This goes all the way down the chain of
181
+ dependencies.
182
+
183
+ This command writes the complete resolution into `Cheffile.lock`.
54
184
 
185
+ This command then copies all of the fetched cookbooks into your `cookbooks/`
186
+ directory, overwriting whatever was there before. You can then use `knife
187
+ cookbook upload -all` to upload the cookbooks to your chef-server, if you are
188
+ using the client-server model.
189
+
190
+ Check your `Cheffile` and `Cheffile.lock` into version control:
191
+
192
+ $ git add Cheffile
55
193
  $ git add Cheffile.lock
56
194
  $ git commit -m "I want these particular versions of these particular cookbooks from these particular."
57
195
 
58
- __Update your cheffile with new/changed/removed constraints/sources/dependencies__
196
+ Make sure you check your Cheffile.lock into version control. This will ensure
197
+ dependencies do not need to be resolved every run, greatly reducing dependency
198
+ resolution time.
199
+
200
+ Update your cheffile with new/changed/removed constraints/sources/dependencies:
59
201
 
60
202
  $ cat Cheffile
61
203
  site 'http://community.opscode.com/api/v1'
@@ -72,43 +214,56 @@ __Update your cheffile with new/changed/removed constraints/sources/dependencies
72
214
  $ git add Cheffile.lock
73
215
  $ git commit -m "I also want these additional cookbooks."
74
216
 
75
- __Update the version of a dependency__
217
+ Update the version of a dependency:
76
218
 
77
219
  $ librarian-chef update ntp timezone monit [--verbose]
78
220
  $ git diff Cheffile.lock
79
221
  $ git add Cheffile.lock
80
222
  $ git commit -m "I want updated versions of these cookbooks."
81
223
 
82
- __Push your changes to the git repository__
224
+ Push your changes to the git repository:
83
225
 
84
226
  $ git push origin master
85
227
 
86
- __Upload the cookbooks to your chef-server__
228
+ Upload the cookbooks to your chef-server:
87
229
 
88
230
  $ knife cookbook upload --all
89
231
 
90
- You should `.gitignore` your `./cookbooks` directory.
91
- If you are manually tracking/vendoring outside cookbooks within the repository,
92
- put them in another directory such as `./cookbooks-sources` and use the `:path` source.
93
- You should typically not need to do this.
232
+ ### Knife Integration
233
+
234
+ You can integrate your `knife.rb` with Librarian-Chef.
94
235
 
95
- You can integrate your `knife.rb` with Librarian. Stick the following in your `knife.rb`:
236
+ Stick the following in your `knife.rb`:
96
237
 
97
238
  require 'librarian/chef/integration/knife'
98
- cookbook_path Librarian::Chef.install_path, "chef-repo/site-cookbooks"
239
+ cookbook_path Librarian::Chef.install_path,
240
+ "/path/to/chef-repo/site-cookbooks"
241
+
242
+ In the above, do *not* to include the path to your `cookbooks/` directory. If
243
+ you have additional cookbooks directories in your chef-repo that you use for
244
+ vendored cookbooks (where you use the `:path =>` source in your `Cheffile`),
245
+ make sure *not* to include the paths to those additional cookbooks directories
246
+ either.
247
+
248
+ You still need to include your `site-cookbooks/` directory in the above list.
249
+
250
+ What this integration does is whenever you use any `knife` command, it will:
251
+
252
+ * Enforce that your `Cheffile` and `Cheffile.lock` are in sync
253
+ * Install the resolved cookbooks to a temporary directory
254
+ * Configure Knife to look in the temporary directory for the installed cookbooks
255
+ and not in the normal `cookbooks/` directory.
256
+
257
+ When you use this integration, any changes you make to anything in the
258
+ `cookbooks/` directory will be ignored by Knife, because Knife won't look in
259
+ that directory for your cookbooks.
99
260
 
100
- In the above, make sure *not* to include the path to your `chef-repo/cookbooks`. If you
101
- have additional cookbooks directories in your chef-repo that you use for `:path`-sourced
102
- cookbooks in your `Cheffile`, make sure *not* to include the paths to those additional
103
- cookbooks directories either in your chef-repo. Since your `chef-repo/site-cookbooks`
104
- directory is for overrides (monkey-patches) to external cookbooks, and since you should
105
- not have any `:path`-sourced cookbooks in your `Cheffile` sourced from that directory,
106
- you still need to include your `chef-repo/site-cookbooks` directory in the above list.
261
+ Reporting Issues
262
+ ----------------
107
263
 
108
- What this integration does is when you use `knife`, it will enforce that your `Cheffile`
109
- and `Cheffile.lock` are in sync. When you `knife cookbook upload`, it will be sure to
110
- upload the same cookbook as is in your `Cheffile.lock`, regardless of what you've done
111
- to your `chef-repo/cookbooks` directory.
264
+ Please include relevant `Cheffile` and `Cheffile.lock` files. Please run the
265
+ `librarian-chef` commands in verbose mode by using the `--verbose` flag, and
266
+ include the verbose output in the bug report as well.
112
267
 
113
268
  License
114
269
  -------
@@ -117,4 +272,5 @@ Written by Jay Feldblum.
117
272
 
118
273
  Copyright (c) 2011 ApplicationsOnline, LLC.
119
274
 
120
- Released under the terms of the MIT License.
275
+ Released under the terms of the MIT License. For further information, please see
276
+ the file `MIT-LICENSE`.
@@ -79,12 +79,16 @@ module Librarian
79
79
  repository.path.mkpath
80
80
  repository.clone!(uri)
81
81
  end
82
+ repository.reset_hard!
82
83
  unless sha == repository.current_commit_hash
83
- repository.fetch!(:tags => true)
84
- repository.fetch!
85
- repository.merge_all_remote_branches!
86
- repository.checkout!(repository.hash_from(sha || ref), :force => true)
87
- @sha ||= repository.current_commit_hash
84
+ remote = repository.default_remote
85
+ repository.fetch!(remote)
86
+ repository.fetch!(remote, :tags => true)
87
+
88
+ new_sha = repository.hash_from(remote, sha || ref)
89
+ repository.checkout!(new_sha)
90
+
91
+ @sha ||= new_sha
88
92
  end
89
93
  end
90
94
 
@@ -33,6 +33,10 @@ module Librarian
33
33
  path.join('.git').exist?
34
34
  end
35
35
 
36
+ def default_remote
37
+ "origin"
38
+ end
39
+
36
40
  def clone!(repository_url)
37
41
  within do
38
42
  command = "clone #{repository_url} ."
@@ -48,48 +52,61 @@ module Librarian
48
52
  end
49
53
  end
50
54
 
51
- def fetch!(options = { })
55
+ def fetch!(remote, options = { })
52
56
  within do
53
- command = "fetch"
57
+ command = "fetch #{remote}"
54
58
  command << " --tags" if options[:tags]
55
59
  run!(command)
56
60
  end
57
61
  end
58
62
 
59
- def merge!(reference)
63
+ def reset_hard!
60
64
  within do
61
- command = "merge #{reference}"
65
+ command = "reset --hard"
62
66
  run!(command)
63
67
  end
64
68
  end
65
69
 
66
- def hash_from(reference)
70
+ def remote_names
67
71
  within do
68
- command = "rev-parse #{reference}"
69
- run!(command).strip
72
+ command = "remote"
73
+ run!(command, false).strip.lines.map(&:strip)
70
74
  end
71
75
  end
72
76
 
73
- def current_commit_hash
77
+ def remote_branch_names
78
+ remotes = remote_names.sort_by(&:length).reverse
79
+
74
80
  within do
75
- command = "rev-parse HEAD"
76
- run!(command).strip!
81
+ command = "branch -r"
82
+ names = run!(command, false).strip.lines.map(&:strip).to_a
83
+ names.each{|n| n.gsub!(/\s*->.*$/, "")}
84
+ names.reject!{|n| n =~ /\/HEAD$/}
85
+ Hash[remotes.map do |r|
86
+ matching_names = names.select{|n| n.start_with?("#{r}/")}
87
+ matching_names.each{|n| names.delete(n)}
88
+ matching_names.each{|n| n.slice!(0, r.size + 1)}
89
+ [r, matching_names]
90
+ end]
77
91
  end
78
92
  end
79
93
 
80
- def merge_all_remote_branches!
81
- remote_branches.each do |branch|
82
- checkout!(branch.slice(%r{[^/]+$}), :force => true)
83
- merge! branch
94
+ def hash_from(remote, reference)
95
+ branch_names = remote_branch_names[remote]
96
+ if branch_names.include?(reference)
97
+ reference = "#{remote}/#{reference}"
98
+ end
99
+
100
+ within do
101
+ command = "rev-parse #{reference}"
102
+ run!(command).strip
84
103
  end
85
104
  end
86
105
 
87
- def remote_branches
106
+ def current_commit_hash
88
107
  within do
89
- command ="branch -r --no-color"
90
- run!(command, false).split("\n ").reject do |r|
91
- r.include? '->' #delete pointers like origin/HEAD -> origin/master
92
- end.collect {|r|r.strip}
108
+ command = "rev-parse HEAD"
109
+ run!(command).strip!
93
110
  end
94
111
  end
95
112
  private
@@ -1,3 +1,3 @@
1
1
  module Librarian
2
- VERSION = "0.0.14"
2
+ VERSION = "0.0.15"
3
3
  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.14
4
+ version: 0.0.15
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: 2012-02-13 00:00:00.000000000 Z
12
+ date: 2012-04-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: thor
16
- requirement: &15281700 !ruby/object:Gem::Requirement
16
+ requirement: &12899900 !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: *15281700
24
+ version_requirements: *12899900
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &15281180 !ruby/object:Gem::Requirement
27
+ requirement: &12899480 !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: *15281180
35
+ version_requirements: *12899480
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &15280600 !ruby/object:Gem::Requirement
38
+ requirement: &12899060 !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: *15280600
46
+ version_requirements: *12899060
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: cucumber
49
- requirement: &15279900 !ruby/object:Gem::Requirement
49
+ requirement: &12898640 !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: *15279900
57
+ version_requirements: *12898640
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: aruba
60
- requirement: &15278980 !ruby/object:Gem::Requirement
60
+ requirement: &12898220 !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: *15278980
68
+ version_requirements: *12898220
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: webmock
71
- requirement: &15278400 !ruby/object:Gem::Requirement
71
+ requirement: &12897800 !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: *15278400
79
+ version_requirements: *12897800
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: chef
82
- requirement: &15277240 !ruby/object:Gem::Requirement
82
+ requirement: &12897300 !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: *15277240
90
+ version_requirements: *12897300
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: highline
93
- requirement: &15276740 !ruby/object:Gem::Requirement
93
+ requirement: &12896880 !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: *15276740
101
+ version_requirements: *12896880
102
102
  description: Librarian
103
103
  email:
104
104
  - y_feldblum@yahoo.com
@@ -216,7 +216,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
216
216
  version: '0'
217
217
  requirements: []
218
218
  rubyforge_project: librarian
219
- rubygems_version: 1.8.10
219
+ rubygems_version: 1.8.17
220
220
  signing_key:
221
221
  specification_version: 3
222
222
  summary: Librarian