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
@@ -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