r10k 1.1.4 → 1.2.0rc1

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 (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
@@ -13,6 +13,7 @@ module Execution
13
13
  # @params [Hash] opts
14
14
  #
15
15
  # @option opts [String] :event An optional log event name. Defaults to cmd.
16
+ # @option opts [String] :cwd The working directory to use when executing the command
16
17
  #
17
18
  # @raise [R10K::ExecutionFailure] If the executed command exited with a
18
19
  # nonzero exit code.
@@ -20,11 +21,13 @@ module Execution
20
21
  # @return [String] the stdout from the command
21
22
  def execute(cmd, opts = {})
22
23
 
23
- event = opts[:event] || cmd
24
+ logger.warn "R10K::Execution#execute is deprecated, use R10K::Util::Subprocess"
25
+
26
+ event = (opts.delete(:event) || cmd)
24
27
 
25
28
  logger.debug1 "Execute: #{event.inspect}"
26
29
 
27
- status, stdout, stderr = systemu(cmd)
30
+ status, stdout, stderr = systemu(cmd, opts)
28
31
 
29
32
  logger.debug2 "[#{event}] STDOUT: #{stdout.chomp}" unless stdout.empty?
30
33
  logger.debug2 "[#{event}] STDERR: #{stderr.chomp}" unless stderr.empty?
@@ -1,15 +1,15 @@
1
1
  require 'r10k/logging'
2
+
3
+ require 'r10k/git'
2
4
  require 'r10k/git/repository'
3
5
 
4
6
  require 'r10k/settings'
5
7
  require 'r10k/registry'
6
8
 
7
- module R10K
8
- module Git
9
- class Cache < R10K::Git::Repository
10
- # Mirror a git repository for use shared git object repositories
11
- #
12
- # @see man git-clone(1)
9
+ # Mirror a git repository for use shared git object repositories
10
+ #
11
+ # @see man git-clone(1)
12
+ class R10K::Git::Cache < R10K::Git::Repository
13
13
 
14
14
  include R10K::Settings::Mixin
15
15
 
@@ -25,28 +25,24 @@ class Cache < R10K::Git::Repository
25
25
 
26
26
  include R10K::Logging
27
27
 
28
- # @!attribute [r] remote
29
- # @return [String] The git repository remote
30
- attr_reader :remote
31
-
32
28
  # @!attribute [r] path
29
+ # @deprecated
33
30
  # @return [String] The path to the git cache repository
34
- attr_reader :path
31
+ def path
32
+ logger.warn "#{self.class}#path is deprecated; use #git_dir"
33
+ @git_dir
34
+ end
35
35
 
36
36
  # @param [String] remote
37
37
  # @param [String] cache_root
38
38
  def initialize(remote)
39
39
  @remote = remote
40
40
 
41
- @path = File.join(settings[:cache_root], sanitized_dirname)
41
+ @git_dir = File.join(settings[:cache_root], sanitized_dirname)
42
42
  end
43
43
 
44
44
  def sync
45
- if @synced
46
- # XXX This gets really spammy. Might be good to turn it on later, but for
47
- # general work it's way much.
48
- #logger.debug "#{@remote} already synced this run, not syncing again"
49
- else
45
+ if not @synced
50
46
  sync!
51
47
  @synced = true
52
48
  end
@@ -54,41 +50,35 @@ class Cache < R10K::Git::Repository
54
50
 
55
51
  def sync!
56
52
  if cached?
57
- # XXX This gets really spammy. Might be good to turn it on later, but for
58
- # general work it's way much.
59
- #logger.debug "Updating existing cache at #{@path}"
60
- git "fetch --prune", :git_dir => @path
53
+ fetch
61
54
  else
62
55
  logger.debug "Creating new git cache for #{@remote.inspect}"
63
56
 
57
+ # TODO extract this to an initialization step
64
58
  unless File.exist? settings[:cache_root]
65
59
  FileUtils.mkdir_p settings[:cache_root]
66
60
  end
67
61
 
68
- git "clone --mirror #{@remote} #{@path}"
62
+ git ['clone', '--mirror', @remote, git_dir]
63
+ end
64
+ rescue R10K::Util::Subprocess::SubprocessError => e
65
+ msg = e.result.stderr.slice(/^fatal: .*$/)
66
+ if msg
67
+ raise R10K::Git::GitError, "Couldn't update git cache for #{@remote}: #{msg.inspect}"
68
+ else
69
+ raise e
69
70
  end
70
71
  end
71
72
 
72
73
  # @return [Array<String>] A list the branches for the git repository
73
74
  def branches
74
- output = git "branch", :git_dir => @path
75
- output.split("\n").map do |str|
76
- # the `git branch` command returns output like this:
77
- # <pre>
78
- # 0.11.x
79
- # 0.12.x
80
- # * master
81
- # passenger_scoping
82
- # </pre>
83
- #
84
- # The string index notation strips off the leading whitespace/asterisk
85
- str[2..-1]
86
- end
75
+ output = git %w[for-each-ref refs/heads --format %(refname)], :git_dir => git_dir
76
+ output.scan(%r[refs/heads/(.*)$]).flatten
87
77
  end
88
78
 
89
79
  # @return [true, false] If the repository has been locally cached
90
80
  def cached?
91
- File.exist? @path
81
+ File.exist? git_dir
92
82
  end
93
83
 
94
84
  private
@@ -97,10 +87,4 @@ class Cache < R10K::Git::Repository
97
87
  def sanitized_dirname
98
88
  @remote.gsub(/[^@\w\.-]/, '-')
99
89
  end
100
-
101
- def git_dir
102
- @path
103
- end
104
- end
105
- end
106
90
  end
@@ -0,0 +1,22 @@
1
+ require 'r10k/git/ref'
2
+ require 'r10k/git/repository'
3
+
4
+ # commit: A 40-byte hex representation of a SHA1 referencing a specific commit
5
+ # @see https://www.kernel.org/pub/software/scm/git/docs/gitglossary.html
6
+ # @api private
7
+ class R10K::Git::Commit < R10K::Git::Ref
8
+
9
+ # @!attribute [r] commit
10
+ # @return [String] The git commit
11
+ attr_reader :commit
12
+ alias :ref :commit
13
+
14
+ def initialize(commit, repository = nil)
15
+ @commit = commit
16
+ @repository = repository
17
+ end
18
+
19
+ def fetch?
20
+ ! resolvable?
21
+ end
22
+ end
@@ -1,34 +1,43 @@
1
+ require 'r10k/errors'
2
+
1
3
  module R10K
2
- module Git
3
- class NonexistentHashError < StandardError
4
- # Raised when a hash was requested that can't be found in the repository
4
+ module Git
5
5
 
6
- attr_reader :hash
7
- attr_reader :git_dir
6
+ class GitError < R10KError
7
+ end
8
8
 
9
- def initialize(msg = nil, git_dir = nil)
10
- super(msg)
9
+ class UnresolvableRefError < GitError
11
10
 
12
- @git_dir = git_dir
13
- end
11
+ attr_reader :ref
12
+ attr_reader :git_dir
14
13
 
15
- HASHLIKE = %r[[A-Fa-f0-9]]
14
+ def initialize(*args)
15
+ super
16
16
 
17
- # Print a friendly error message if an object hash is given as the message
18
- def message
19
- msg = super
20
- if msg and msg.match(HASHLIKE)
21
- msg = "Could not locate hash #{msg.inspect} in repository"
22
- elsif msg.nil?
23
- msg = "Could not locate hash in repository"
17
+ @hash = @options[:ref]
18
+ @git_dir = @options[:git_dir]
24
19
  end
25
20
 
26
- if @git_dir
27
- msg << " at #{@git_dir}. (Does the remote repository need to be updated?)"
28
- end
21
+ HASHLIKE = %r[[A-Fa-f0-9]]
22
+
23
+ # Print a friendly error message if an object hash is given as the message
24
+ def message
25
+ if @mesg
26
+ msg = @mesg
27
+ else
28
+ msg = "Could not locate hash"
29
29
 
30
- msg
30
+ if @hash
31
+ msg << " '#{@hash}'"
32
+ end
33
+ end
34
+
35
+ if @git_dir
36
+ msg << " at #{@git_dir}"
37
+ end
38
+
39
+ msg
40
+ end
31
41
  end
32
42
  end
33
43
  end
34
- end
@@ -0,0 +1,33 @@
1
+ require 'r10k/git'
2
+ require 'r10k/git/ref'
3
+ require 'r10k/git/repository'
4
+
5
+
6
+ # head: A named reference to the commit at the tip of a branch. Heads are
7
+ # stored in a file in $GIT_DIR/refs/heads/ directory. except when using packed
8
+ #
9
+ # @see https://www.kernel.org/pub/software/scm/git/docs/gitglossary.html
10
+ # @api private
11
+ class R10K::Git::Head < R10K::Git::Ref
12
+
13
+ # @!attribute [r] head
14
+ # @return [String] The git head
15
+ attr_reader :head
16
+ alias :ref :head
17
+
18
+ def initialize(head, repository = nil)
19
+ @head = head
20
+ @repository = repository
21
+ end
22
+
23
+ # def sha1
24
+ # TODO ensure that @head is an actual head as opposed to a tag or other
25
+ # hooliganism.
26
+ #end
27
+
28
+ # If we are tracking a branch, we should always try to fetch a newer version
29
+ # of that branch.
30
+ def fetch?
31
+ true
32
+ end
33
+ end
@@ -0,0 +1,63 @@
1
+ require 'r10k/git'
2
+ require 'r10k/git/repository'
3
+
4
+ # ref: A 40-byte hex representation of a SHA1 or a name that denotes a
5
+ # particular object. They may be stored in a file under $GIT_DIR/refs/
6
+ # directory, or in the $GIT_DIR/packed-refs file.
7
+ #
8
+ # @see https://www.kernel.org/pub/software/scm/git/docs/gitglossary.html
9
+ # @api private
10
+ class R10K::Git::Ref
11
+
12
+ # @!attribute [r] ref
13
+ # @return [String] The git reference
14
+ attr_reader :ref
15
+
16
+ # @!attribute [rw] repository
17
+ # @return [R10K::Git::Repository] A git repository that can be used to
18
+ # resolve the git reference to a commit.
19
+ attr_accessor :repository
20
+
21
+ def initialize(ref, repository = nil)
22
+ @ref = ref
23
+ @repository = repository
24
+ end
25
+
26
+ # Can we locate the commit in the related repository?
27
+ def resolvable?
28
+ sha1
29
+ true
30
+ rescue R10K::Git::UnresolvableRefError
31
+ false
32
+ end
33
+
34
+ # Should we try to fetch this ref?
35
+ #
36
+ # Since we don't know the type of this ref, we have to assume that it might
37
+ # be a branch and always update accordingly.
38
+ def fetch?
39
+ true
40
+ end
41
+
42
+ def sha1
43
+ if @repository.nil?
44
+ raise ArgumentError, "Cannot resolve #{self.inspect}: no associated git repository"
45
+ else
46
+ @repository.rev_parse(ref)
47
+ end
48
+ end
49
+
50
+ def ==(other)
51
+ other.sha1 == self.sha1
52
+ rescue ArgumentError, R10K::Git::UnresolvableRefError
53
+ false
54
+ end
55
+
56
+ def to_s
57
+ ref
58
+ end
59
+
60
+ def inspect
61
+ "#<#{self.class}: #{to_s}>"
62
+ end
63
+ end
@@ -1,74 +1,103 @@
1
- require 'r10k/execution'
2
- require 'r10k/git/errors'
1
+ require 'r10k/git'
2
+ require 'r10k/util/subprocess'
3
3
 
4
- module R10K
5
- module Git
6
- class Repository
7
- # Define an abstract base class for git repositories.
8
-
9
- include R10K::Execution
4
+ # Define an abstract base class for git repositories.
5
+ class R10K::Git::Repository
10
6
 
11
7
  # @!attribute [r] remote
12
8
  # @return [String] The URL to the git repository
13
9
  attr_reader :remote
14
10
 
15
11
  # @!attribute [r] basedir
16
- # @return [String] The basedir for the working directory
12
+ # @return [String] The directory containing the repository
17
13
  attr_reader :basedir
18
14
 
19
15
  # @!attribute [r] dirname
20
- # @return [String] The name for the directory
16
+ # @return [String] The name of the directory
21
17
  attr_reader :dirname
22
18
 
23
- # Resolve a ref to a commit hash
19
+ # @!attribute [r] git_dir
20
+ # Set the path to the git directory. For git repositories with working copies
21
+ # this will be `$working_dir/.git`; for bare repositories this will be
22
+ # `bare-repo.git`
23
+ # @return [String] The path to the git directory
24
+ attr_reader :git_dir
25
+
26
+ # Resolve a ref to a git commit. The given pattern can be a commit, tag,
27
+ # or a local or remote branch
28
+ #
29
+ # @param [String] pattern
24
30
  #
25
- # @param [String] ref
31
+ # @return [String] The dereferenced hash of `pattern`
32
+ def resolve_ref(pattern)
33
+ commit = nil
34
+ begin
35
+ all_commits = git ['show-ref', '-s', pattern], :git_dir => git_dir
36
+ commit = all_commits.lines.first
37
+ rescue R10K::Util::Subprocess::SubprocessError
38
+ end
39
+
40
+ if commit.nil?
41
+ begin
42
+ commit = git ['rev-parse', "#{ref}^{commit}"], :git_dir => git_dir
43
+ rescue R10K::Util::Subprocess::SubprocessError
44
+ end
45
+ end
46
+
47
+ if commit
48
+ commit.chomp
49
+ else
50
+ raise R10K::Git::UnresolvableRefError.new(:ref => pattern, :git_dir => git_dir)
51
+ end
52
+ end
53
+ alias rev_parse resolve_ref
54
+
55
+ private
56
+
57
+ # Fetch objects and refs from the given git remote
26
58
  #
27
- # @return [String] The dereferenced hash of `ref`
28
- def rev_parse(ref)
29
- commit = git "rev-parse #{ref}^{commit}", :git_dir => git_dir
30
- commit.chomp
31
- rescue R10K::ExecutionFailure
32
- raise R10K::Git::NonexistentHashError.new(ref, git_dir)
59
+ # @param remote [#to_s] The remote name to fetch from
60
+ def fetch(remote = 'origin')
61
+ git ['fetch', '--prune', remote], :git_dir => @git_dir
33
62
  end
34
63
 
35
64
  # Wrap git commands
36
65
  #
37
- # @param [String] command_line_args The arguments for the git prompt
38
- # @param [Hash] opts
66
+ # @param cmd [Array<String>] cmd The arguments for the git prompt
67
+ # @param opts [Hash] opts
39
68
  #
69
+ # @option opts [String] :path
40
70
  # @option opts [String] :git_dir
41
71
  # @option opts [String] :work_tree
42
- # @option opts [String] :work_tree
43
72
  #
44
73
  # @raise [R10K::ExecutionFailure] If the executed command exited with a
45
74
  # nonzero exit code.
46
75
  #
47
76
  # @return [String] The git command output
48
- def git(command_line_args, opts = {})
49
- args = %w{git}
50
-
51
- log_event = "git #{command_line_args}"
52
- log_event << ", args: #{opts.inspect}" unless opts.empty?
53
-
77
+ def git(cmd, opts = {})
78
+ argv = %w{git}
54
79
 
55
80
  if opts[:path]
56
- args << "--git-dir #{opts[:path]}/.git"
57
- args << "--work-tree #{opts[:path]}"
81
+ argv << "--git-dir" << File.join(opts[:path], '.git')
82
+ argv << "--work-tree" << opts[:path]
58
83
  else
59
84
  if opts[:git_dir]
60
- args << "--git-dir #{opts[:git_dir]}"
85
+ argv << "--git-dir" << opts[:git_dir]
61
86
  end
62
87
  if opts[:work_tree]
63
- args << "--work-tree #{opts[:work_tree]}"
88
+ argv << "--work-tree" << opts[:work_tree]
64
89
  end
65
90
  end
66
91
 
67
- args << command_line_args
68
- cmd = args.join(' ')
92
+ argv.concat(cmd)
93
+
94
+ subproc = R10K::Util::Subprocess.new(argv)
95
+ subproc.raise_on_fail = true
96
+ subproc.logger = self.logger
97
+
98
+ result = subproc.execute
69
99
 
70
- execute(cmd, :event => log_event)
100
+ # todo ensure that logging always occurs even if the command fails to run
101
+ result.stdout
71
102
  end
72
103
  end
73
- end
74
- end