vendorificator 0.2.0 → 0.3.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.
Files changed (36) hide show
  1. data/.gitignore +3 -0
  2. data/CHANGELOG.md +40 -0
  3. data/Gemfile +2 -2
  4. data/README.md +18 -17
  5. data/Rakefile +15 -1
  6. data/features/edgecases.feature +34 -0
  7. data/features/environment.feature +17 -0
  8. data/features/fixtures/git/testrepo/objects/53/ac1a96882e666cee31504179abc21eac522f8d +2 -0
  9. data/features/fixtures/git/testrepo/refs/tags/email-v0 +1 -0
  10. data/features/git.feature +19 -1
  11. data/features/status.feature +1 -1
  12. data/features/step_definitions/basic.rb +11 -0
  13. data/features/step_definitions/git.rb +12 -0
  14. data/features/support/env.rb +3 -4
  15. data/features/support/minigit.rb +4 -0
  16. data/lib/vendorificator/cli.rb +32 -44
  17. data/lib/vendorificator/config.rb +63 -15
  18. data/lib/vendorificator/environment.rb +123 -16
  19. data/lib/vendorificator/errors.rb +3 -0
  20. data/lib/vendorificator/hooks/chef_cookbook.rb +1 -1
  21. data/lib/vendorificator/vendor/archive.rb +69 -65
  22. data/lib/vendorificator/vendor/chef_cookbook.rb +45 -39
  23. data/lib/vendorificator/vendor/download.rb +30 -26
  24. data/lib/vendorificator/vendor/git.rb +43 -32
  25. data/lib/vendorificator/vendor.rb +83 -91
  26. data/lib/vendorificator/version.rb +1 -1
  27. data/lib/vendorificator.rb +1 -0
  28. data/spec/spec_helper.rb +20 -22
  29. data/spec/vendorificator/config_spec.rb +64 -0
  30. data/spec/vendorificator/environment_spec.rb +81 -0
  31. data/spec/vendorificator/fixtures/vendorfiles/empty_vendor.rb +1 -0
  32. data/spec/vendorificator/fixtures/vendorfiles/vendor.rb +15 -0
  33. data/spec/vendorificator/vendor/chef_cookbook_spec.rb +13 -0
  34. data/spec/vendorificator/vendor_spec.rb +29 -33
  35. data/vendorificator.gemspec +3 -3
  36. metadata +45 -30
data/.gitignore CHANGED
@@ -17,4 +17,7 @@ test/tmp
17
17
  test/version_tmp
18
18
  tmp
19
19
  /.rbx
20
+ /.ruby-version
21
+ /.ruby-gemset
20
22
  /log
23
+ .DS_Store
data/CHANGELOG.md ADDED
@@ -0,0 +1,40 @@
1
+ # Changes
2
+
3
+ ## 0.3.0
4
+
5
+ - New command `vendor push` for pushing managed branches and tags to
6
+ remote repository
7
+ - Nicer syntax for mixin hooks
8
+ - Add `:tag` option for `git` submodule
9
+ - Better stashing of local changes when syncing
10
+ - Verbosity tweaks
11
+ - Refactor implementation of configuration, other internal refactors
12
+ - Improved test coverage
13
+
14
+ ## 0.2.0
15
+
16
+ - New vendor type `download` for downloading a single file
17
+ - Support `--version` and `-h` / `--help` switches
18
+ - New `:subdirectory` option for vendor modules
19
+ - Support JRuby
20
+ - Fix error when cleaning empty repository
21
+ - Misc verbosity tweaks
22
+ - Use MiniGit instead of Grit as Git library; other internal refactors
23
+ - Run Cucumber tests with Aruba
24
+
25
+ ## 0.1.1
26
+
27
+ - Add `--update` option to `vendor sync` and `vendor status` to check
28
+ whether upstream version has changed
29
+ - It is now possible to explicitly set module's category to `nil`
30
+ - Ruby 1.8.7 compatibility fix
31
+ - Gem runtime/development dependency fixes
32
+ - Initial minitest specs
33
+ - Make Cucumber tests use Webmock
34
+
35
+ ## 0.1.0
36
+
37
+ Initial release.
38
+
39
+
40
+
data/Gemfile CHANGED
@@ -4,8 +4,8 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  group :development do
7
- git "git://github.com/mpasternacki/wrong.git",
8
- :ref => 'ad025241e5772373264d1bf62168e2bf3780ccf9' do
7
+ git "git://github.com/sconover/wrong.git",
8
+ :ref => '0cbc35a07cb63f6f409bb85da6ad7d107bdab021' do
9
9
  gem 'wrong'
10
10
  end
11
11
  gem 'minitest-ansi'
data/README.md CHANGED
@@ -50,8 +50,10 @@ detail.
50
50
 
51
51
  * `vendor sync` will update all vendor modules that need updating
52
52
  * `vendor status` will list all the modules and their status
53
- * `vendor pull` will pull all vendor branches, tags, and notes from a
54
- Git remote
53
+ * `vendor pull` will pull all vendor branches, tags, and notes from
54
+ a Git remote
55
+ * `vendor push` will push all vendor branches, tags, and notes to
56
+ a Git remote
55
57
  * `vendor diff` will show the differences between vendor module's
56
58
  pristine branch and curent work tree
57
59
  * `vendor log` will show a `git log` of all changes made to a particular
@@ -123,9 +125,9 @@ block).
123
125
  Example:
124
126
 
125
127
  ```ruby
126
- vendor 'generated', :version => '0.23' do |mod|
127
- File.open('README') { |f| f.puts "Hello, World!" }
128
- File.open('VERSION') { |f| f.puts mod.version }
128
+ vendor 'generated', :version => '0.23' do |v|
129
+ File.open('README', 'w') { |f| f.puts "Hello, World!" }
130
+ File.open('VERSION', 'w') { |f| f.puts v.version }
129
131
  end
130
132
  ```
131
133
 
@@ -160,7 +162,7 @@ unpacks it as contents of the module. It takes same options as
160
162
  disables this behaviour.
161
163
  * `:checksum` -- if set to SHA256 checksum of the file, it will be
162
164
  checked on download.
163
-
165
+
164
166
  Archive's `:version` defaults to file name.
165
167
 
166
168
  Example:
@@ -181,10 +183,11 @@ Downloads snapshot of a Git repository. Takes the same options as
181
183
  sets name to its basename then), just like `:url` for `archive`
182
184
  (e.g. `git "git://github.com/github/testrepo.git"` will be cloned
183
185
  from that repository, and named `testrepo`).
184
- * `:branch`, `:revision` -- what to check out when repository is
185
- cloned.
186
+ * `:branch`, `:revision`, `:tag` -- what to check out when repository
187
+ is cloned.
186
188
 
187
- Git module's `:version` defaults to the conjured revision.
189
+ Git module's `:version` defaults to the `:tag` if given, or the
190
+ conjured revision otherwise.
188
191
 
189
192
  Example:
190
193
 
@@ -208,7 +211,7 @@ always enough), plus:
208
211
  completely. If an array, don't download dependencies that are in
209
212
  the array. Default for that is `chef_cookbook_ignore_dependencies`
210
213
  setting.
211
-
214
+
212
215
  Examples:
213
216
 
214
217
  ```ruby
@@ -227,18 +230,16 @@ chef_cookbook 'memcached', ignore_dependencies => ['runit']
227
230
  ```
228
231
 
229
232
  If you get Chef cookbooks from Git or anywhere else than Opscode's
230
- community website, you can still use dependency resolution by mixing
231
- in the hook class:
233
+ community website, you can still use dependency resolution by using a :hooks
234
+ option to add it:
232
235
 
233
236
  ```ruby
234
- class << git 'git://github.com/user/cookbook.git', :category => :cookbooks
235
- include Vendorificator::Hooks::ChefCookbookDependencies
237
+ git 'git://github.com/user/cookbook.git',
238
+ :category => :cookbooks,
239
+ :hooks => 'ChefCookbookDependencies'
236
240
  end
237
241
  ```
238
242
 
239
- This is a bit convoluted, but there will soon be an argument to do
240
- that in a nicer way.
241
-
242
243
  ## Contributing
243
244
 
244
245
  1. Fork it
data/Rakefile CHANGED
@@ -13,6 +13,11 @@ namespace :relish do
13
13
  end
14
14
  end
15
15
 
16
+ task :info do
17
+ sh 'which git'
18
+ sh 'git --version'
19
+ end
20
+
16
21
  begin
17
22
  require 'cucumber'
18
23
  require 'cucumber/rake/task'
@@ -38,4 +43,13 @@ end
38
43
  mkdir_p 'tmp'
39
44
  ENV['TMPDIR'] ||= File.join(Dir.pwd, 'tmp')
40
45
 
41
- task :default => [:spec, :features]
46
+ task :default => [:info, :spec, :features]
47
+
48
+ if ENV['COVERAGE']
49
+ task :clean_coverage do
50
+ rm_rf 'coverage'
51
+ end
52
+
53
+ task :spec => :clean_coverage
54
+ task :features => :clean_coverage
55
+ end
@@ -0,0 +1,34 @@
1
+ Feature: weird edge cases
2
+
3
+ Scenario: module with a .gitignore file
4
+ Given a repository with following Vendorfile:
5
+ """ruby
6
+ vendor 'ignore', :version => 1 do
7
+ File.open('.gitignore', 'w') { |f| f.puts 'ignored.txt' }
8
+ end
9
+ """
10
+ When I successfully run `vendor sync`
11
+ Then the following has been conjured:
12
+ | Name | ignore |
13
+ | With file | .gitignore |
14
+
15
+ When I write to "vendor/ignore/ignored.txt" with:
16
+ """
17
+ whatever
18
+ """
19
+ Then git repository is clean
20
+
21
+ When I change Vendorfile to:
22
+ """ruby
23
+ vendor 'ignore', :version => 2 do
24
+ File.open('files.txt', 'w') { |f| f.puts Dir.entries('.').join("\n") }
25
+ File.open('.gitignore', 'w') { |f| f.puts 'ignored.txt' }
26
+ end
27
+ """
28
+ And I successfully run `vendor sync`
29
+ Then the file "vendor/ignore/files.txt" should not contain "ignored.txt"
30
+ And the file "vendor/ignore/ignored.txt" should contain exactly:
31
+ """
32
+ whatever
33
+ """
34
+ And git repository is clean
@@ -0,0 +1,17 @@
1
+ Feature: Environment management.
2
+
3
+ Scenario:
4
+ Given a repository with following Vendorfile:
5
+ """ruby
6
+ vendor 'generated', :version => '0.23' do |v|
7
+ File.open('README', 'w') { |f| f.puts "Hello, World!" }
8
+ end
9
+ """
10
+ And a remote repository
11
+ When I successfully run `vendor sync`
12
+ And I successfully run `vendor push`
13
+ Then branch "vendor/generated" exists in the remote repo
14
+ And tag "vendor/generated/0.23" exists in the remote repo
15
+
16
+
17
+
@@ -0,0 +1,2 @@
1
+ xE�A
2
+ �0@Q�9��E�IӦ���B2��TӖ:��ڍ��OCT�n���Bj�:;q�| ��m�2c䄱��BM��g੔�FWCJ�������Y����2~d8��]��;�������S�� јM�U���1l
@@ -0,0 +1 @@
1
+ 53ac1a96882e666cee31504179abc21eac522f8d
data/features/git.feature CHANGED
@@ -10,6 +10,7 @@ Scenario: Vendorificating a git repo
10
10
  | Name | testrepo |
11
11
  | Version | 10e9ac58c77bc229d8c59a5b4eb7422916453148 |
12
12
  | With file | test/alias.c |
13
+ And there's a git log message including "at revision 10e9ac58c77bc229d8c59a5b4eb7422916453148"
13
14
 
14
15
  Scenario: Vendorificating a subdirectory from a git repo
15
16
  Given a repository with following Vendorfile:
@@ -23,6 +24,7 @@ Scenario: Vendorificating a subdirectory from a git repo
23
24
  | Version | 10e9ac58c77bc229d8c59a5b4eb7422916453148 |
24
25
  | With file | alias.c |
25
26
  | Without file | test/alias.c |
27
+ And there's a git log message including "at revision 10e9ac58c77bc229d8c59a5b4eb7422916453148"
26
28
 
27
29
  Scenario: Vendorificating a certain branch from a git repo
28
30
  Given a repository with following Vendorfile:
@@ -35,6 +37,22 @@ Scenario: Vendorificating a certain branch from a git repo
35
37
  | Name | testrepo |
36
38
  | Version | ecbfa229ba5f11c05b18bcc4f7c32b8f25d63f8c |
37
39
  | With file | README.md |
40
+ And there's a git log message including "at revision ecbfa229ba5f11c05b18bcc4f7c32b8f25d63f8c"
41
+
42
+ @wip
43
+ Scenario: Vendorificating a certain tag from a git repo
44
+ Given a repository with following Vendorfile:
45
+ """ruby
46
+ git "file://#{ENV['FIXTURES_DIR']}/git/testrepo",
47
+ :tag => 'email-v0'
48
+ """
49
+ When I successfully run `vendor sync`
50
+ Then following has been conjured:
51
+ | Name | testrepo |
52
+ | Version | email-v0 |
53
+ | Without file | README.md |
54
+ | With file | test/alias.c |
55
+ And there's a git log message including "at revision f81247bde4ef7a1c7d280140cc0bcf0b8221a51f"
38
56
 
39
57
  Scenario: Vendorificating a certain revision from a git repo
40
58
  Given a repository with following Vendorfile:
@@ -48,4 +66,4 @@ Scenario: Vendorificating a certain revision from a git repo
48
66
  | Version | 6ff1be9c3819c93a2f41e0ddc09f252fcf154f34 |
49
67
  | With file | alias.c |
50
68
  | Without file | test/alias.c |
51
-
69
+ And there's a git log message including "at revision 6ff1be9c3819c93a2f41e0ddc09f252fcf154f34"
@@ -1,6 +1,6 @@
1
1
  Feature: Module status
2
2
 
3
- The `status` subcommand statuss all known modules and their status.
3
+ The `status` subcommand displays all known modules and their status.
4
4
 
5
5
  Background:
6
6
  Given a repository with following Vendorfile:
@@ -19,6 +19,17 @@ Given /^a repository with following Vendorfile:$/ do |vendorfile_contents|
19
19
  run_simple 'git commit -m "New repo"'
20
20
  end
21
21
 
22
+ Given /^a remote repository$/ do
23
+ create_dir '../remote-repository'
24
+ cd '../remote-repository'
25
+ run_simple 'git init --bare'
26
+ # Configure Git username & email to unclutter console output
27
+ run_simple 'git config user.name Cucumber'
28
+ run_simple 'git config user.email cucumber@`hostname --fqdn`'
29
+ cd '../working-repository'
30
+ run_simple 'git remote add origin ../remote-repository'
31
+ end
32
+
22
33
  When /^I change Vendorfile to:$/ do |vendorfile_contents|
23
34
  write_file('Vendorfile', vendorfile_contents)
24
35
  run_simple 'git commit -m "Updated Vendorfile" Vendorfile'
@@ -37,3 +37,15 @@ end
37
37
  Then /^tag matching (#{PATTERN}) does not exist$/ do |pat|
38
38
  deny { git.tags.any? { |t| t =~ pat } }
39
39
  end
40
+
41
+ Then(/^there's a git log message including "(.*?)"$/) do |message|
42
+ assert { git.log.lines.any? { |ln| ln.include?(message) } }
43
+ end
44
+
45
+ Then /^branch "(.*?)" exists in the remote repo$/ do |branch_name|
46
+ assert { remote_git.heads.include?(branch_name) }
47
+ end
48
+
49
+ Then /^tag "(.*?)" exists in the remote repo$/ do |tag_name|
50
+ assert { remote_git.tags.include?(tag_name) }
51
+ end
@@ -17,9 +17,8 @@ end
17
17
 
18
18
  Before do
19
19
  @aruba_timeout_seconds = case defined?(RUBY_ENGINE) && RUBY_ENGINE
20
- when 'ruby', nil then 3
21
- when 'jruby' then 20
22
- when 'rbx' then 10
23
- else 5
20
+ when 'jruby' then 45
21
+ when 'rbx' then 30
22
+ else 30
24
23
  end
25
24
  end
@@ -24,6 +24,10 @@ module Vendorificator
24
24
  def git
25
25
  @git ||= ::MiniGit::Capturing.new(current_dir)
26
26
  end
27
+
28
+ def remote_git
29
+ @remote_git ||= ::MiniGit::Capturing.new(current_dir + '/../remote-repository')
30
+ end
27
31
  end
28
32
  end
29
33
  end
@@ -1,5 +1,15 @@
1
- require 'thor'
1
+ if ENV['COVERAGE']
2
+ require 'pathname'
3
+ require 'simplecov'
4
+ SimpleCov.start do
5
+ add_group 'Vendors', 'lib/vendorificator/vendor'
6
+ root Pathname.new(__FILE__).dirname.dirname.dirname.realpath.to_s
7
+ use_merging
8
+ end
9
+ SimpleCov.command_name "vendor##{$$}@#{DateTime.now.to_s}"
10
+ end
2
11
 
12
+ require 'thor'
3
13
  require 'vendorificator'
4
14
 
5
15
  module Vendorificator
@@ -24,6 +34,10 @@ module Vendorificator
24
34
  def initialize(args=[], options={}, config={})
25
35
  super
26
36
 
37
+ if self.options[:debug]
38
+ MiniGit.debug = true
39
+ end
40
+
27
41
  if self.options[:version]
28
42
  say "Vendorificator #{Vendorificator::VERSION}"
29
43
  exit
@@ -48,17 +62,9 @@ module Vendorificator
48
62
  desc :sync, "Download new or updated vendor files"
49
63
  method_option :update, :type => :boolean, :default => false
50
64
  def sync
51
- ensure_clean!
52
- environment.config[:use_upstream_version] = options[:update]
53
- Vendorificator::Vendor.each(*modules) do |mod|
54
- say_status :module, mod.name
55
- begin
56
- shell.padding += 1
57
- mod.run!
58
- ensure
59
- shell.padding -= 1
60
- end
61
- end
65
+ environment.sync options.merge(:modules => modules)
66
+ rescue DirtyRepoError
67
+ fail! 'Repository is not clean.'
62
68
  end
63
69
 
64
70
  desc "status", "List known vendor modules and their status"
@@ -66,7 +72,7 @@ module Vendorificator
66
72
  def status
67
73
  say_status 'WARNING', 'Git repository is not clean', :red unless environment.clean?
68
74
  environment.config[:use_upstream_version] = options[:update]
69
- Vendorificator::Vendor.each(*modules) do |mod|
75
+ environment.each_vendor_instance(*modules) do |mod|
70
76
  status_line = mod.to_s
71
77
 
72
78
  updatable = mod.updatable?
@@ -87,14 +93,19 @@ module Vendorificator
87
93
  method_option :remote, :aliases => ['-r'], :default => nil
88
94
  method_option :dry_run, :aliases => ['-n'], :default => false, :type => :boolean
89
95
  def pull
90
- ensure_clean!
91
- remotes = options[:remote] ? options[:remote].split(',') : environment.config[:remotes]
92
- remotes.each do |remote|
93
- indent 'remote', remote do
94
- environment.pull(remote, options)
95
- end
96
- end
96
+ environment.pull_all options
97
+ rescue DirtyRepoError
98
+ fail! 'Repository is not clean.'
97
99
  end
100
+
101
+ desc :push, "Push local changes back to the remote repository"
102
+ method_option :remote, :aliases => ['-r'], :default => nil
103
+ def push
104
+ environment.push options
105
+ rescue DirtyRepoError
106
+ fail! 'Repository is not clean.'
107
+ end
108
+
98
109
  desc "git GIT_COMMAND [GIT_ARGS [...]]",
99
110
  "Run a git command for specified modules"
100
111
  long_desc <<EOF
@@ -110,7 +121,7 @@ module Vendorificator
110
121
  vendor git diff --stat @MERGED@ -- @PATH@ # 'vendor diff', as diffstat
111
122
  EOF
112
123
  def git(command, *args)
113
- Vendorificator::Vendor.each(*modules) do |mod|
124
+ environment.each_vendor_instance(*modules) do |mod|
114
125
  unless mod.merged
115
126
  say_status 'unmerged', mod.to_s, :red unless options[:only_changed]
116
127
  next
@@ -185,28 +196,5 @@ EOF
185
196
  raise Thor::Error, 'I give up.'
186
197
  end
187
198
 
188
- def indent(*args, &block)
189
- say_status *args unless args.empty?
190
- shell.padding += 1
191
- yield
192
- ensure
193
- shell.padding -= 1
194
- end
195
-
196
- def ensure_clean!
197
- unless environment.clean?
198
- fail!('Repository is not clean.')
199
- end
200
- end
201
- end
202
- end
203
-
204
- # Monkey patch over https://github.com/wycats/thor/pull/298
205
- class Thor::Options
206
- alias_method :_orig_current_is_switch?, :current_is_switch?
207
- def current_is_switch?
208
- rv = _orig_current_is_switch?
209
- @parsing_options = false if !rv[0] && @stop_on_unknown && @parsing_options
210
- rv
211
199
  end
212
200
  end
@@ -1,33 +1,81 @@
1
1
  require 'pathname'
2
2
 
3
- require 'mixlib/config'
4
-
5
3
  module Vendorificator
6
4
  class Config
7
- extend Mixlib::Config
8
-
9
5
  attr_accessor :environment
10
6
 
11
- configure do |c|
12
- c[:basedir] = 'vendor'
13
- c[:branch_prefix] = 'vendor'
14
- c[:remotes] = %w(origin)
7
+ @defaults = {}
8
+ @modules = {}
9
+
10
+ def self.defaults
11
+ @defaults
12
+ end
13
+
14
+ def self.modules
15
+ @modules
16
+ end
17
+
18
+ def self.option(name, default = nil, &block)
19
+ define_method name do |*args|
20
+ if args.size == 0
21
+ @configuration[name.to_sym]
22
+ elsif args.size == 1
23
+ @configuration[name.to_sym] = args.first
24
+ else
25
+ raise 'Unsupported number of arguments (expected 0 or 1).'
26
+ end
27
+ end
28
+ @defaults[name.to_sym] = default if default
29
+ end
30
+
31
+ def self.register_module(name, klass)
32
+ @modules[name.to_sym] = klass
33
+ end
34
+
35
+ def initialize(params = {})
36
+ @configuration = self.class.defaults.merge(params)
15
37
  end
16
38
 
17
- def self.from_file(filename)
39
+ def read_file(filename)
18
40
  pathname = Pathname.new(filename).cleanpath.expand_path
19
41
 
20
- self[:vendorfile_path] = pathname
21
- self[:root_dir] =
22
- if ( pathname.basename.to_s == 'vendor.rb' &&
23
- pathname.dirname.basename.to_s == 'config' )
24
- # Correctly recognize root dir if main config is 'config/vendor.rb'
42
+ @configuration[:vendorfile_path] = pathname
43
+ @configuration[:root_dir] = if pathname.basename.to_s == 'vendor.rb' &&
44
+ pathname.dirname.basename.to_s == 'config'
25
45
  pathname.dirname.dirname
26
46
  else
27
47
  pathname.dirname
28
48
  end
29
49
 
30
- super(pathname.to_s)
50
+ instance_eval(IO.read(filename), filename, 1)
51
+ end
52
+
53
+ def configure(&block)
54
+ block.call @configuration
55
+ end
56
+
57
+ def [](key)
58
+ @configuration[key]
59
+ end
60
+
61
+ def []=(key, value)
62
+ @configuration[key] = value
63
+ end
64
+
65
+ def modules
66
+ self.class.modules
31
67
  end
68
+
69
+ def method_missing(method_symbol, *args, &block)
70
+ if modules.keys.include? method_symbol
71
+ modules[method_symbol].new(environment, args.delete_at(0).to_s, *args, &block)
72
+ else
73
+ super
74
+ end
75
+ end
76
+
77
+ option :basedir, 'vendor'
78
+ option :branch_prefix, 'vendor'
79
+ option :remotes, %w(origin)
32
80
  end
33
81
  end