r10k 1.1.4 → 1.2.0rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +8 -8
  2. data/.gitignore +1 -0
  3. data/.nodeset.yml +7 -0
  4. data/.rspec +1 -0
  5. data/.travis.yml +2 -1
  6. data/CHANGELOG +17 -12
  7. data/Gemfile +8 -0
  8. data/README.markdown +4 -2
  9. data/Rakefile +1 -0
  10. data/doc/dynamic-environments.markdown +206 -0
  11. data/doc/puppetfile.markdown +87 -0
  12. data/lib/r10k/cli.rb +1 -1
  13. data/lib/r10k/errors.rb +30 -3
  14. data/lib/r10k/execution.rb +5 -2
  15. data/lib/r10k/git/cache.rb +26 -42
  16. data/lib/r10k/git/commit.rb +22 -0
  17. data/lib/r10k/git/errors.rb +31 -22
  18. data/lib/r10k/git/head.rb +33 -0
  19. data/lib/r10k/git/ref.rb +63 -0
  20. data/lib/r10k/git/repository.rb +65 -36
  21. data/lib/r10k/git/tag.rb +26 -0
  22. data/lib/r10k/git/working_dir.rb +93 -83
  23. data/lib/r10k/git.rb +14 -0
  24. data/lib/r10k/module/forge.rb +129 -62
  25. data/lib/r10k/module/git.rb +72 -6
  26. data/lib/r10k/module/metadata.rb +47 -0
  27. data/lib/r10k/module/svn.rb +99 -0
  28. data/lib/r10k/module.rb +1 -0
  29. data/lib/r10k/module_repository/forge.rb +64 -0
  30. data/lib/r10k/module_repository.rb +8 -0
  31. data/lib/r10k/semver.rb +1 -1
  32. data/lib/r10k/svn/working_dir.rb +76 -0
  33. data/lib/r10k/task/deployment.rb +21 -28
  34. data/lib/r10k/util/subprocess/io.rb +12 -0
  35. data/lib/r10k/util/subprocess/result.rb +36 -0
  36. data/lib/r10k/util/subprocess/runner.rb +88 -0
  37. data/lib/r10k/util/subprocess.rb +107 -0
  38. data/lib/r10k/version.rb +1 -1
  39. data/r10k.gemspec +11 -1
  40. data/spec/fixtures/vcr/cassettes/R10K_ModuleRepository_Forge/and_the_expected_version_is_latest/can_fetch_all_versions_of_a_given_module.yml +42 -0
  41. data/spec/fixtures/vcr/cassettes/R10K_ModuleRepository_Forge/and_the_expected_version_is_latest/can_fetch_the_latest_version_of_a_given_module.yml +42 -0
  42. data/spec/fixtures/vcr/cassettes/R10K_ModuleRepository_Forge/looking_up_versions/can_fetch_all_versions_of_a_given_module.yml +42 -0
  43. data/spec/fixtures/vcr/cassettes/R10K_ModuleRepository_Forge/looking_up_versions/can_fetch_the_latest_version_of_a_given_module.yml +42 -0
  44. data/spec/fixtures/vcr/cassettes/R10K_ModuleRepository_Forge/looking_up_versions.yml +42 -0
  45. data/spec/fixtures/vcr/cassettes/R10K_Module_Forge/and_the_expected_version_is_latest/sets_the_expected_version_based_on_the_latest_forge_version.yml +42 -0
  46. data/spec/rspec-system-r10k/puppetfile.rb +24 -0
  47. data/spec/rspec-system-r10k/tmpdir.rb +32 -0
  48. data/spec/shared-examples/git-ref.rb +49 -0
  49. data/spec/spec_helper.rb +23 -0
  50. data/spec/system/module/forge/install_spec.rb +51 -0
  51. data/spec/system/module/git/install_spec.rb +117 -0
  52. data/spec/system/module/svn/install_spec.rb +51 -0
  53. data/spec/system/module/svn/update_spec.rb +38 -0
  54. data/spec/system/spec_helper.rb +60 -0
  55. data/spec/system/system-helpers.rb +4 -0
  56. data/spec/system/version_spec.rb +7 -0
  57. data/spec/system-provisioning/el.rb +38 -0
  58. data/spec/unit/deployment/source_spec.rb +1 -1
  59. data/spec/unit/git/cache_spec.rb +38 -0
  60. data/spec/unit/git/commit_spec.rb +33 -0
  61. data/spec/unit/git/head_spec.rb +27 -0
  62. data/spec/unit/git/ref_spec.rb +68 -0
  63. data/spec/unit/git/tag_spec.rb +31 -0
  64. data/spec/unit/module/forge_spec.rb +157 -37
  65. data/spec/unit/module/git_spec.rb +49 -0
  66. data/spec/unit/module/metadata_spec.rb +68 -0
  67. data/spec/unit/module/svn_spec.rb +146 -0
  68. data/spec/unit/module_repository/forge_spec.rb +32 -0
  69. metadata +151 -8
@@ -1,6 +1,5 @@
1
1
  require 'r10k/module'
2
- require 'r10k/git/working_dir'
3
- require 'forwardable'
2
+ require 'r10k/git'
4
3
 
5
4
  class R10K::Module::Git < R10K::Module::Base
6
5
 
@@ -12,14 +11,17 @@ class R10K::Module::Git < R10K::Module::Base
12
11
  false
13
12
  end
14
13
 
15
- extend Forwardable
16
- def_delegator :@working_dir, :sync
14
+ # @!attribute [r] working_dir
15
+ # @api private
16
+ # @return [R10K::Git::WorkingDir]
17
+ attr_reader :working_dir
17
18
 
18
19
  def initialize(name, basedir, args)
19
20
  @name, @basedir, @args = name, basedir, args
20
21
 
21
- @remote = @args[:git]
22
- @ref = (@args[:ref] || 'master')
22
+ parse_options(args)
23
+
24
+ @full_path = Pathname.new(File.join(basedir, name))
23
25
 
24
26
  @working_dir = R10K::Git::WorkingDir.new(@ref, @remote, @basedir, @name)
25
27
  end
@@ -27,4 +29,68 @@ class R10K::Module::Git < R10K::Module::Base
27
29
  def version
28
30
  @ref
29
31
  end
32
+
33
+ def sync
34
+ case status
35
+ when :absent
36
+ install
37
+ when :mismatched
38
+ uninstall
39
+ install
40
+ when :outdated
41
+ @working_dir.sync
42
+ end
43
+ end
44
+
45
+ def status
46
+ if not @working_dir.exist?
47
+ return :absent
48
+ elsif not @working_dir.git?
49
+ return :mismatched
50
+ elsif not @remote == @working_dir.remote
51
+ return :mismatched
52
+ end
53
+
54
+ if @working_dir.outdated?
55
+ return :outdated
56
+ end
57
+
58
+ return :insync
59
+ end
60
+
61
+ private
62
+
63
+ def install
64
+ @working_dir.sync
65
+ end
66
+
67
+ def uninstall
68
+ @full_path.rmtree
69
+ end
70
+
71
+ def parse_options(options)
72
+ @remote = options.delete(:git)
73
+
74
+ if options[:branch]
75
+ @ref = R10K::Git::Head.new(options.delete(:branch))
76
+ end
77
+
78
+ if options[:tag]
79
+ @ref = R10K::Git::Tag.new(options.delete(:tag))
80
+ end
81
+
82
+ if options[:commit]
83
+ @ref = R10K::Git::Commit.new(options.delete(:commit))
84
+ end
85
+
86
+ if options[:ref]
87
+ @ref = R10K::Git::Ref.new(options.delete(:ref))
88
+ end
89
+
90
+ @ref ||= R10K::Git::Ref.new('master')
91
+
92
+ unless options.empty?
93
+ raise ArgumentError, "Unhandled options #{options.keys.inspect} specified for #{self.class}"
94
+ end
95
+ end
30
96
  end
@@ -0,0 +1,47 @@
1
+ require 'r10k/module'
2
+ require 'r10k/semver'
3
+ require 'json'
4
+
5
+ class R10K::Module::Metadata
6
+
7
+ # @!attribute [r] name
8
+ # @return [String] The module name
9
+ attr_reader :name
10
+
11
+ # @!attribute [r] author
12
+ # @return [String] The module author username
13
+ attr_reader :author
14
+
15
+ # @!attribute [r] version
16
+ # @return [R10K::SemVer] The module version
17
+ attr_reader :version
18
+
19
+ # @param metadata_path [Pathname] The file path to the metadata
20
+ def initialize(metadata_path)
21
+ @metadata_path = metadata_path
22
+
23
+ @version = R10K::SemVer::MIN
24
+ end
25
+
26
+ # Does the metadata file itself exist?
27
+ def exist?
28
+ @metadata_path.file? and @metadata_path.readable?
29
+ end
30
+
31
+ # Attempt to read the metadata file
32
+ def read
33
+ if self.exist?
34
+ hash = JSON.parse(@metadata_path.read)
35
+ attributes_from_hash(hash)
36
+ end
37
+ rescue JSON::ParserError
38
+ false
39
+ end
40
+
41
+ private
42
+
43
+ def attributes_from_hash(json)
44
+ @author, _, @name = json['name'].partition('-')
45
+ @version = R10K::SemVer.new(json['version'])
46
+ end
47
+ end
@@ -0,0 +1,99 @@
1
+ require 'r10k/module'
2
+ require 'r10k/execution'
3
+ require 'r10k/logging'
4
+ require 'r10k/svn/working_dir'
5
+
6
+ class R10K::Module::SVN < R10K::Module::Base
7
+
8
+ R10K::Module.register(self)
9
+
10
+ def self.implement?(name, args)
11
+ args.is_a? Hash and args.has_key? :svn
12
+ end
13
+
14
+ # @!attribute [r] expected_revision
15
+ # @return [String] The SVN revision that the repo should have checked out
16
+ attr_reader :expected_revision
17
+ alias expected_version expected_revision
18
+
19
+ # @!attribute [r] svn_path
20
+ # @return [String] The path inside of the SVN repository to have checked out
21
+ attr_reader :svn_path
22
+
23
+ # @!attribute [r] full_path
24
+ # @return [Pathname] The filesystem path to the SVN repo
25
+ attr_reader :full_path
26
+
27
+ def initialize(name, basedir, args)
28
+ @name = name
29
+ @basedir = basedir
30
+
31
+ parse_options(args)
32
+
33
+ @full_path = Pathname.new(File.join(@basedir, @name))
34
+ @working_dir = R10K::SVN::WorkingDir.new(@full_path)
35
+ end
36
+
37
+ def status
38
+ if not self.exist?
39
+ :absent
40
+ elsif not @working_dir.is_svn?
41
+ :mismatched
42
+ elsif not @url == @working_dir.url
43
+ :mismatched
44
+ elsif not @expected_revision == @working_dir.revision
45
+ :outdated
46
+ else
47
+ :insync
48
+ end
49
+ end
50
+
51
+ def sync
52
+ case status
53
+ when :absent
54
+ install
55
+ when :mismatched
56
+ reinstall
57
+ when :outdated
58
+ update
59
+ end
60
+ end
61
+
62
+ def exist?
63
+ @full_path.exist?
64
+ end
65
+
66
+ private
67
+
68
+ def install
69
+ FileUtils.mkdir @basedir unless File.directory? @basedir
70
+
71
+ @working_dir.checkout(@url, @expected_revision)
72
+ end
73
+
74
+ def uninstall
75
+ @full_path.rmtree
76
+ end
77
+
78
+ def reinstall
79
+ uninstall
80
+ install
81
+ end
82
+
83
+ def update
84
+ @working_dir.update(@expected_revision)
85
+ end
86
+
87
+ def parse_options(hash)
88
+ hash.each_pair do |key, value|
89
+ case key
90
+ when :svn
91
+ @url = value
92
+ when :rev, :revision
93
+ @expected_revision = value
94
+ when :svn_path
95
+ @svn_path = value
96
+ end
97
+ end
98
+ end
99
+ end
data/lib/r10k/module.rb CHANGED
@@ -32,4 +32,5 @@ module R10K::Module
32
32
  require 'r10k/module/base'
33
33
  require 'r10k/module/git'
34
34
  require 'r10k/module/forge'
35
+ require 'r10k/module/svn'
35
36
  end
@@ -0,0 +1,64 @@
1
+ require 'r10k/module_repository'
2
+ require 'r10k/version'
3
+
4
+ require 'faraday'
5
+ require 'faraday_middleware/multi_json'
6
+ require 'faraday_middleware'
7
+
8
+ class R10K::ModuleRepository::Forge
9
+
10
+ # @!attribute [r] forge
11
+ # @return [String] The forge hostname to use for requests
12
+ attr_reader :forge
13
+
14
+ # @!attribute [r] :conn
15
+ # @api private
16
+ # @return [Faraday]
17
+ attr_reader :conn
18
+
19
+ def initialize(forge = 'forge.puppetlabs.com')
20
+ @forge = forge
21
+
22
+ @conn = Faraday.new(
23
+ :url => "https://#{@forge}",
24
+ :user_agent => "Ruby/r10k #{R10K::VERSION}"
25
+ ) do |builder|
26
+ builder.request :multi_json
27
+ builder.response :multi_json
28
+
29
+ # This needs to be _after_ request/response configuration for testing
30
+ # purposes. This comment is the result of much consternation.
31
+ builder.adapter Faraday.default_adapter
32
+ end
33
+ end
34
+
35
+ # Query for all published versions of a module
36
+ #
37
+ # @example
38
+ # forge = R10K::ModuleRepository::Forge.new
39
+ # forge.versions('adrien/boolean')
40
+ # #=> ["0.9.0-rc1", "0.9.0", "1.0.0", "1.0.1"]
41
+ #
42
+ # @param module_name [String] The fully qualified module name
43
+ # @return [Array<String>] All published versions of the given module
44
+ def versions(module_name)
45
+ response = @conn.get("/api/v1/releases.json", {'module' => module_name})
46
+
47
+ response.body[module_name].map do |version_info|
48
+ version_info['version']
49
+ end
50
+ end
51
+
52
+ # Query for the newest published version of a module
53
+ #
54
+ # @example
55
+ # forge = R10K::ModuleRepository::Forge.new
56
+ # forge.latest_version('adrien/boolean')
57
+ # #=> "1.0.1"
58
+ #
59
+ # @param module_name [String] The fully qualified module name
60
+ # @return [String] The latest published version of the given module
61
+ def latest_version(module_name)
62
+ versions(module_name).last
63
+ end
64
+ end
@@ -0,0 +1,8 @@
1
+ module R10K
2
+
3
+ # Locations that can be queried for remote module metadata
4
+ module ModuleRepository
5
+
6
+ require 'r10k/module_repository/forge'
7
+ end
8
+ end
data/lib/r10k/semver.rb CHANGED
@@ -112,7 +112,7 @@ class SemVer < Numeric
112
112
  end
113
113
 
114
114
  def inspect
115
- @vstring || "v#{@major}.#{@minor}.#{@tiny}#{@special}"
115
+ @vstring || "#{@major}.#{@minor}.#{@tiny}#{@special}"
116
116
  end
117
117
  alias :to_s :inspect
118
118
 
@@ -0,0 +1,76 @@
1
+ require 'r10k/logging'
2
+ require 'r10k/util/subprocess'
3
+
4
+ module R10K
5
+ module SVN
6
+ class WorkingDir
7
+
8
+ # @param full_path [Pathname]
9
+ def initialize(full_path)
10
+ @full_path = full_path
11
+ end
12
+
13
+ def is_svn?
14
+ dot_svn = @full_path + '.svn'
15
+ dot_svn.exist?
16
+ end
17
+
18
+ def revision
19
+ info.slice(/^Revision: (\d+)$/, 1)
20
+ end
21
+
22
+ def url
23
+ info.slice(/^URL: (.*)$/, 1)
24
+ end
25
+
26
+ def root
27
+ info.slice(/^Repository Root: (.*)$/, 1)
28
+ end
29
+
30
+ def update(revision = nil)
31
+ argv = %w[update]
32
+ argv << '-r' << revision if revision
33
+
34
+ svn argv, :cwd => @full_path
35
+ end
36
+
37
+ def checkout(url, revision = nil)
38
+ argv = ['checkout', url]
39
+ argv << '-r' << revision if revision
40
+ argv << @full_path.basename.to_s
41
+
42
+ svn argv, :cwd => @full_path.parent
43
+ end
44
+
45
+ private
46
+
47
+ def info
48
+ svn ["info"], :cwd => @full_path
49
+ end
50
+
51
+ include R10K::Execution
52
+ include R10K::Logging
53
+
54
+ # Wrap SVN commands
55
+ #
56
+ # @param argv [Array<String>]
57
+ # @param opts [Hash]
58
+ #
59
+ # @option opts [Pathname] :cwd The directory to run the command in
60
+ #
61
+ # @return [String] The stdout from the given command
62
+ def svn(argv, opts = {})
63
+ argv.unshift('svn')
64
+
65
+ subproc = R10K::Util::Subprocess.new(argv)
66
+ subproc.raise_on_fail = true
67
+ subproc.logger = self.logger
68
+
69
+ subproc.cwd = opts[:cwd]
70
+ result = subproc.execute
71
+
72
+ result.stdout
73
+ end
74
+ end
75
+ end
76
+ end
@@ -11,39 +11,32 @@ module Deployment
11
11
 
12
12
  private
13
13
 
14
- def active_environments(names)
15
-
16
- active = []
17
-
18
- all_environments = @deployment.environments
19
- if names.empty?
20
- active = all_environments
21
- else
22
- # This has average case O(N^2) but N should remain relatively small, so
23
- # while this should be optimized that optimization can wait a while.
24
- names.each do |env_name|
25
-
26
- matching = all_environments.select do |env|
27
- env.dirname == env_name
28
- end
29
-
30
- if matching.empty?
31
- logger.warn "Environment #{env_name} not found in any source"
32
- task_runner.succeeded = false
33
- else
34
- active.concat(matching)
35
- end
36
- end
14
+ def load_environments!
15
+ @environments = @deployment.environments.inject({}) do |hash, env|
16
+ hash[env.dirname] = env
17
+ hash
37
18
  end
38
-
39
- active
40
19
  end
41
20
 
42
21
  # @param [Array<String>] names The list of environments to deploy.
43
22
  #
44
- def with_environments(names = [])
45
- active_environments(names).each do |env|
46
- yield env
23
+ def with_environments(names = [], &block)
24
+ load_environments!
25
+
26
+ # If an explicit list of environments were not given, deploy everything
27
+ if names.size > 0
28
+ to_deploy = names
29
+ else
30
+ to_deploy = @environments.keys
31
+ end
32
+
33
+ to_deploy.reverse.each do |env_name|
34
+ if (env = @environments[env_name])
35
+ yield env
36
+ else
37
+ logger.warn "Environment #{env_name} not found in any source"
38
+ task_runner.succeeded = false
39
+ end
47
40
  end
48
41
  end
49
42
  end