r10k 0.0.9 → 1.0.0rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/bin/r10k +1 -1
  2. data/lib/r10k.rb +0 -4
  3. data/lib/r10k/cli.rb +9 -5
  4. data/lib/r10k/cli/deploy.rb +108 -0
  5. data/lib/r10k/cli/environment.rb +5 -1
  6. data/lib/r10k/cli/environment/deploy.rb +6 -28
  7. data/lib/r10k/cli/environment/list.rb +6 -10
  8. data/lib/r10k/cli/environment/stale.rb +6 -16
  9. data/lib/r10k/cli/module.rb +5 -1
  10. data/lib/r10k/cli/module/deploy.rb +5 -32
  11. data/lib/r10k/cli/module/list.rb +6 -27
  12. data/lib/r10k/cli/puppetfile.rb +76 -0
  13. data/lib/r10k/cli/synchronize.rb +8 -24
  14. data/lib/r10k/cli/version.rb +22 -0
  15. data/lib/r10k/deployment.rb +55 -26
  16. data/lib/r10k/deployment/config.rb +69 -0
  17. data/lib/r10k/{config → deployment/config}/loader.rb +9 -5
  18. data/lib/r10k/deployment/environment.rb +88 -0
  19. data/lib/r10k/deployment/source.rb +79 -0
  20. data/lib/r10k/errors.rb +3 -5
  21. data/lib/r10k/execution.rb +43 -0
  22. data/lib/r10k/git/cache.rb +131 -0
  23. data/lib/r10k/git/errors.rb +34 -0
  24. data/lib/r10k/git/repository.rb +74 -0
  25. data/lib/r10k/git/working_dir.rb +142 -0
  26. data/lib/r10k/logging.rb +6 -2
  27. data/lib/r10k/module.rb +10 -13
  28. data/lib/r10k/module/forge.rb +35 -22
  29. data/lib/r10k/module/git.rb +18 -8
  30. data/lib/r10k/puppetfile.rb +107 -0
  31. data/lib/r10k/task.rb +13 -0
  32. data/lib/r10k/task/deployment.rb +151 -0
  33. data/lib/r10k/task/environment.rb +29 -0
  34. data/lib/r10k/task/module.rb +18 -0
  35. data/lib/r10k/task/puppetfile.rb +99 -0
  36. data/lib/r10k/task_runner.rb +72 -0
  37. data/lib/r10k/util/purgeable.rb +50 -0
  38. data/lib/r10k/version.rb +1 -1
  39. data/spec/unit/deployment/environment_spec.rb +19 -0
  40. data/spec/unit/git/cache_spec.rb +37 -0
  41. data/spec/unit/git/working_dir_spec.rb +15 -0
  42. data/spec/unit/module/forge_spec.rb +95 -0
  43. data/spec/unit/module_spec.rb +29 -0
  44. metadata +79 -44
  45. data/lib/r10k/action.rb +0 -7
  46. data/lib/r10k/action/environment.rb +0 -74
  47. data/lib/r10k/action/module.rb +0 -36
  48. data/lib/r10k/cli/cache.rb +0 -32
  49. data/lib/r10k/config.rb +0 -46
  50. data/lib/r10k/deployment/environment_collection.rb +0 -75
  51. data/lib/r10k/librarian.rb +0 -31
  52. data/lib/r10k/librarian/dsl.rb +0 -20
  53. data/lib/r10k/root.rb +0 -98
  54. data/lib/r10k/synchro/git.rb +0 -226
@@ -0,0 +1,43 @@
1
+ require 'r10k/logging'
2
+ require 'r10k/errors'
3
+
4
+ require 'systemu'
5
+
6
+ module R10K
7
+ module Execution
8
+ include R10K::Logging
9
+
10
+ # Execute a command and return stdout
11
+ #
12
+ # @params [String] cmd
13
+ # @params [Hash] opts
14
+ #
15
+ # @option opts [String] :event An optional log event name. Defaults to cmd.
16
+ #
17
+ # @raise [R10K::ExecutionFailure] If the executed command exited with a
18
+ # nonzero exit code.
19
+ #
20
+ # @return [String] the stdout from the command
21
+ def execute(cmd, opts = {})
22
+
23
+ event = opts[:event] || cmd
24
+
25
+ logger.debug1 "Execute: #{event.inspect}"
26
+
27
+ status, stdout, stderr = systemu(cmd)
28
+
29
+ logger.debug2 "[#{event}] STDOUT: #{stdout.chomp}" unless stdout.empty?
30
+ logger.debug2 "[#{event}] STDERR: #{stderr.chomp}" unless stderr.empty?
31
+
32
+ unless status == 0
33
+ msg = "#{cmd.inspect} returned with non-zero exit value #{status.exitstatus}"
34
+ e = R10K::ExecutionFailure.new(msg)
35
+ e.exit_code = status
36
+ e.stdout = stdout
37
+ e.stderr = stderr
38
+ raise e
39
+ end
40
+ stdout
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,131 @@
1
+ require 'r10k/logging'
2
+ require 'r10k/git/repository'
3
+
4
+ module R10K
5
+ module Git
6
+ class Cache < R10K::Git::Repository
7
+ # Mirror a git repository for use shared git object repositories
8
+ #
9
+ # @see man git-clone(1)
10
+
11
+ class << self
12
+
13
+ # @!attribute [r] cache_root
14
+ # @return [String] The directory to use as the cache.
15
+ attr_writer :cache_root
16
+
17
+ def cache_root
18
+ @cache_root
19
+ end
20
+
21
+ # Memoize class instances and return existing instances.
22
+ #
23
+ # This allows objects to mark themselves as cached to prevent unnecessary
24
+ # cache refreshes.
25
+ #
26
+ # @param [String] remote A git remote URL
27
+ # @return [R10K::Synchro::Git]
28
+ def new(remote)
29
+ @repos ||= {}
30
+ unless @repos[remote]
31
+ obj = self.allocate
32
+ obj.send(:initialize, remote)
33
+ @repos[remote] = obj
34
+ end
35
+ @repos[remote]
36
+ end
37
+
38
+ def clear!
39
+ @repos = {}
40
+ end
41
+ end
42
+
43
+ include R10K::Logging
44
+
45
+ # @!attribute [r] remote
46
+ # @return [String] The git repository remote
47
+ attr_reader :remote
48
+
49
+ # @!attribute [r] cache_root
50
+ # Where to keep the git object cache. Defaults to ~/.r10k/git if a class
51
+ # level value is not set.
52
+ # @return [String] The directory to use as the cache
53
+ attr_reader :cache_root
54
+
55
+ # @!attribute [r] path
56
+ # @return [String] The path to the git cache repository
57
+ attr_reader :path
58
+
59
+ # @param [String] remote
60
+ # @param [String] cache_root
61
+ def initialize(remote)
62
+ @remote = remote
63
+
64
+ @cache_root = self.class.cache_root || default_cache_root
65
+
66
+ @path = File.join(@cache_root, sanitized_dirname)
67
+ end
68
+
69
+ def sync
70
+ if @synced
71
+ # XXX This gets really spammy. Might be good to turn it on later, but for
72
+ # general work it's way much.
73
+ #logger.debug "#{@remote} already synced this run, not syncing again"
74
+ else
75
+ sync!
76
+ @synced = true
77
+ end
78
+ end
79
+
80
+ def sync!
81
+ if cached?
82
+ # XXX This gets really spammy. Might be good to turn it on later, but for
83
+ # general work it's way much.
84
+ #logger.debug "Updating existing cache at #{@path}"
85
+ git "fetch --prune", :git_dir => @path
86
+ else
87
+ logger.debug "Creating new git cache for #{@remote.inspect}"
88
+ FileUtils.mkdir_p cache_root unless File.exist? @cache_root
89
+ git "clone --mirror #{@remote} #{@path}"
90
+ end
91
+ end
92
+
93
+ # @return [Array<String>] A list the branches for the git repository
94
+ def branches
95
+ output = git "branch", :git_dir => @path
96
+ output.split("\n").map do |str|
97
+ # the `git branch` command returns output like this:
98
+ # <pre>
99
+ # 0.11.x
100
+ # 0.12.x
101
+ # * master
102
+ # passenger_scoping
103
+ # </pre>
104
+ #
105
+ # The string index notation strips off the leading whitespace/asterisk
106
+ str[2..-1]
107
+ end
108
+ end
109
+
110
+ # @return [true, false] If the repository has been locally cached
111
+ def cached?
112
+ File.exist? @path
113
+ end
114
+
115
+ private
116
+
117
+ # Reformat the remote name into something that can be used as a directory
118
+ def sanitized_dirname
119
+ @remote.gsub(/[^@\w\.-]/, '-')
120
+ end
121
+
122
+ def default_cache_root
123
+ File.expand_path('~/.r10k/git')
124
+ end
125
+
126
+ def git_dir
127
+ @path
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,34 @@
1
+ module R10K
2
+ module Git
3
+ class NonexistentHashError < StandardError
4
+ # Raised when a hash was requested that can't be found in the repository
5
+
6
+ attr_reader :hash
7
+ attr_reader :git_dir
8
+
9
+ def initialize(msg = nil, git_dir = nil)
10
+ super(msg)
11
+
12
+ @git_dir = git_dir
13
+ end
14
+
15
+ HASHLIKE = %r[[A-Fa-f0-9]]
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"
24
+ end
25
+
26
+ if @git_dir
27
+ msg << " at #{@git_dir}. (Does the remote repository need to be updated?)"
28
+ end
29
+
30
+ msg
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,74 @@
1
+ require 'r10k/execution'
2
+ require 'r10k/git/errors'
3
+
4
+ module R10K
5
+ module Git
6
+ class Repository
7
+ # Define an abstract base class for git repositories.
8
+
9
+ include R10K::Execution
10
+
11
+ # @!attribute [r] remote
12
+ # @return [String] The URL to the git repository
13
+ attr_reader :remote
14
+
15
+ # @!attribute [r] basedir
16
+ # @return [String] The basedir for the working directory
17
+ attr_reader :basedir
18
+
19
+ # @!attribute [r] dirname
20
+ # @return [String] The name for the directory
21
+ attr_reader :dirname
22
+
23
+ # Resolve a ref to a commit hash
24
+ #
25
+ # @param [String] ref
26
+ #
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)
33
+ end
34
+
35
+ # Wrap git commands
36
+ #
37
+ # @param [String] command_line_args The arguments for the git prompt
38
+ # @param [Hash] opts
39
+ #
40
+ # @option opts [String] :git_dir
41
+ # @option opts [String] :work_tree
42
+ # @option opts [String] :work_tree
43
+ #
44
+ # @raise [R10K::ExecutionFailure] If the executed command exited with a
45
+ # nonzero exit code.
46
+ #
47
+ # @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
+
54
+
55
+ if opts[:path]
56
+ args << "--git-dir #{opts[:path]}/.git"
57
+ args << "--work-tree #{opts[:path]}"
58
+ else
59
+ if opts[:git_dir]
60
+ args << "--git-dir #{opts[:git_dir]}"
61
+ end
62
+ if opts[:work_tree]
63
+ args << "--work-tree #{opts[:work_tree]}"
64
+ end
65
+ end
66
+
67
+ args << command_line_args
68
+ cmd = args.join(' ')
69
+
70
+ execute(cmd, :event => log_event)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,142 @@
1
+ require 'forwardable'
2
+ require 'r10k/logging'
3
+ require 'r10k/git/cache'
4
+
5
+ module R10K
6
+ module Git
7
+ class WorkingDir < R10K::Git::Repository
8
+ # Implements sparse git repositories with shared objects
9
+ #
10
+ # Working directory instances use the git alternatives object store, so that
11
+ # working directories only contain checked out files and all object files are
12
+ # shared.
13
+
14
+ include R10K::Logging
15
+
16
+ extend Forwardable
17
+
18
+ # @!attribute [r] cache
19
+ # @return [R10K::Git::Cache] The object cache backing this working directory
20
+ attr_reader :cache
21
+
22
+ # @!attribute [r] ref
23
+ # @return [String] The git reference to use check out in the given directory
24
+ attr_reader :ref
25
+
26
+ # Instantiates a new git synchro and optionally prepares for caching
27
+ #
28
+ # @param [String] ref
29
+ # @param [String] remote
30
+ # @param [String] basedir
31
+ # @param [String] dirname
32
+ def initialize(ref, remote, basedir, dirname = nil)
33
+ @ref = ref
34
+ @remote = remote
35
+ @basedir = basedir
36
+ @dirname = dirname || ref
37
+
38
+ @full_path = File.join(@basedir, @dirname)
39
+
40
+ @cache = R10K::Git::Cache.new(@remote)
41
+ end
42
+
43
+ # Synchronize the local git repository.
44
+ def sync
45
+ # TODO stop forcing a sync every time.
46
+ @cache.sync
47
+
48
+ if cloned?
49
+ fetch
50
+ else
51
+ clone
52
+ end
53
+ reset
54
+ end
55
+
56
+ # Determine if repo has been cloned into a specific dir
57
+ #
58
+ # @return [true, false] If the repo has already been cloned
59
+ def cloned?
60
+ File.directory? git_dir
61
+ end
62
+
63
+ private
64
+
65
+ def set_cache_remote
66
+ # XXX This is crude but it'll ensure that the right remote is used for
67
+ # the cache.
68
+ if remote_url('cache') == @cache.path
69
+ logger.debug1 "Git repo #{@full_path} cache remote already set correctly"
70
+ else
71
+ git "remote set-url cache #{@cache.path}", :path => @full_path
72
+ end
73
+ end
74
+
75
+ # Perform a non-bare clone of a git repository.
76
+ def clone
77
+ # We do the clone against the target repo using the `--reference` flag so
78
+ # that doing a normal `git pull` on a directory will work.
79
+ git "clone --reference #{@cache.path} #{@remote} #{@full_path}"
80
+ git "remote add cache #{@cache.path}", :path => @full_path
81
+ end
82
+
83
+ def fetch
84
+ set_cache_remote
85
+ git "fetch --prune cache", :path => @full_path
86
+ end
87
+
88
+ # Reset a git repo with a working directory to a specific ref
89
+ def reset
90
+ commit = cache.rev_parse(@ref)
91
+ current = rev_parse('HEAD')
92
+
93
+ if commit == current
94
+ logger.debug1 "Git repo #{@full_path} is already at #{commit}, no need to reset"
95
+ return
96
+ end
97
+
98
+ begin
99
+ git "reset --hard #{commit}", :path => @full_path
100
+ rescue R10K::ExecutionFailure => e
101
+ logger.error "Unable to locate commit object #{commit} in git repo #{@full_path}"
102
+ raise
103
+ end
104
+ end
105
+
106
+ # Resolve a ref to a commit hash
107
+ #
108
+ # @param [String] ref
109
+ #
110
+ # @return [String] The dereferenced hash of `ref`
111
+ def rev_parse(ref)
112
+ commit = git "rev-parse #{ref}^{commit}", :path => @full_path
113
+ commit.chomp
114
+ rescue R10K::ExecutionFailure => e
115
+ logger.error "Could not resolve ref #{ref.inspect} for git repo #{@full_path}"
116
+ raise
117
+ end
118
+
119
+ # @param [String] name The remote to retrieve the URl for
120
+ # @return [String] The git remote URL
121
+ def remote_url(remote_name)
122
+ output = git "remote -v", :path => @full_path
123
+
124
+ remotes = {}
125
+
126
+ output.each_line do |line|
127
+ if mdata = line.match(/^(\S+)\s+(\S+)\s+\(fetch\)/)
128
+ name = mdata[1]
129
+ remote = mdata[2]
130
+ remotes[name] = remote
131
+ end
132
+ end
133
+
134
+ remotes[remote_name]
135
+ end
136
+
137
+ def git_dir
138
+ File.join(@full_path, '.git')
139
+ end
140
+ end
141
+ end
142
+ end
data/lib/r10k/logging.rb CHANGED
@@ -7,17 +7,21 @@ module R10K::Logging
7
7
 
8
8
  include Log4r
9
9
 
10
+ def logger_name
11
+ self.class.to_s
12
+ end
13
+
10
14
  def logger
11
15
  unless @logger
12
- @logger = Log4r::Logger.new(self.class.name)
16
+ @logger = Log4r::Logger.new(self.logger_name)
13
17
  @logger.add R10K::Logging.outputter
14
18
  end
15
19
  @logger
16
20
  end
17
21
 
18
22
  class << self
23
+ include Log4r
19
24
 
20
- include Log4r
21
25
  def included(klass)
22
26
  unless @log4r_loaded
23
27
  Configurator.custom_levels(*%w{DEBUG2 DEBUG1 DEBUG INFO NOTICE WARN ERROR FATAL})