r10k 0.0.1rc1 → 0.0.1

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.
data/bin/r10k CHANGED
@@ -1,5 +1,17 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'r10k/cli'
4
+ require 'colored'
4
5
 
5
- R10K::CLI.command.run(ARGV)
6
+ begin
7
+ R10K::CLI.command.run(ARGV)
8
+ rescue Interrupt
9
+ $stderr.puts "Aborted!".red
10
+ exit(1)
11
+ rescue SystemExit => e
12
+ exit(e.status)
13
+ rescue Exception => e
14
+ $stderr.puts "\nRuntime error: #{e.inspect}".red
15
+ $stderr.puts e.backtrace.join("\n").red if ARGV.include? '--trace'
16
+ exit(1)
17
+ end
data/lib/r10k.rb CHANGED
@@ -4,3 +4,4 @@ require 'r10k/root'
4
4
  require 'r10k/synchro/git'
5
5
  require 'r10k/librarian'
6
6
  require 'r10k/version'
7
+ require 'r10k/logging'
@@ -0,0 +1,7 @@
1
+ require 'r10k'
2
+
3
+ module R10K::Action
4
+ end
5
+
6
+ require 'r10k/action/module'
7
+ require 'r10k/action/environment'
@@ -0,0 +1,74 @@
1
+ require 'r10k/action'
2
+ require 'r10k/errors'
3
+ require 'r10k/action/module'
4
+ require 'r10k/deployment'
5
+ require 'r10k/logging'
6
+
7
+ require 'middleware'
8
+
9
+ module R10K::Action::Environment
10
+
11
+ class Deploy
12
+ # Middleware action to deploy an environment
13
+
14
+ include R10K::Logging
15
+
16
+ # @param [Object] app The next application in the middlware stack
17
+ # @param [R10K::Module] mod The module to deploy
18
+ def initialize(app, root)
19
+ @app, @root = app, root
20
+ end
21
+
22
+ # @param [Hash] env
23
+ #
24
+ # @option env [true, false] :update_cache
25
+ # @option env [true, false] :recurse
26
+ # @option env [true, false] :trace
27
+ def call(env)
28
+ @env = env
29
+
30
+ logger.notice "Deploying environment #{@root.name}"
31
+ FileUtils.mkdir_p @root.full_path
32
+ @root.sync! :update_cache => @env[:update_cache]
33
+
34
+ if @env[:recurse]
35
+ # Build a new middleware chain and run it
36
+ stack = Middleware::Builder.new
37
+ @root.modules.each { |mod| stack.use R10K::Action::Module::Deploy, mod }
38
+ stack.call(@env)
39
+ end
40
+
41
+ @app.call(@env)
42
+ rescue R10K::ExecutionFailure => e
43
+ logger.error "Could not synchronize #{@root.full_path}: #{e}".red
44
+ $stderr.puts e.backtrace.join("\n").red if @env[:trace]
45
+ @app.call(@env)
46
+ end
47
+ end
48
+
49
+ class Purge
50
+ # Middleware action to purge stale environments from a directory
51
+
52
+ include R10K::Logging
53
+
54
+ # @param [Object] app The next application in the middlware stack
55
+ # @param [String] path The directory path to purge
56
+ def initialize(app, path)
57
+ @app, @path = app, path
58
+ end
59
+
60
+ # @param [Hash] env
61
+ def call(env)
62
+ @env = env
63
+
64
+ stale_directories = R10K::Deployment.instance.collection.stale(@path)
65
+
66
+ stale_directories.each do |dir|
67
+ logger.notice "Purging stale environment #{dir.inspect}"
68
+ FileUtils.rm_rf(dir, :secure => true)
69
+ end
70
+
71
+ @app.call(@env)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,36 @@
1
+ require 'r10k/action'
2
+ require 'r10k/errors'
3
+ require 'r10k/logging'
4
+
5
+ require 'middleware'
6
+
7
+ module R10K::Action::Module
8
+
9
+ class R10K::Action::Module::Deploy
10
+ # Middleware to deploy a module
11
+
12
+ include R10K::Logging
13
+
14
+ # @param [Object] app The next application in the middlware stack
15
+ # @param [R10K::Module] mod The module to deploy
16
+ def initialize(app, mod)
17
+ @app, @mod = app, mod
18
+ end
19
+
20
+ # @param [Hash] env
21
+ #
22
+ # @option env [true, false] :update_cache
23
+ def call(env)
24
+ @env = env
25
+
26
+ logger.notice "Deploying module #{@mod.name}"
27
+ @mod.sync! :update_cache => @env[:update_cache]
28
+
29
+ @app.call(@env)
30
+ rescue R10K::ExecutionFailure => e
31
+ logger.error "Could not synchronize #{@mod.full_path}: #{e}".red
32
+ $stderr.puts e.backtrace.join("\n").red if @env[:trace]
33
+ @app.call(@env)
34
+ end
35
+ end
36
+ end
data/lib/r10k/cli.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'r10k'
2
+ require 'r10k/logging'
2
3
  require 'cri'
3
4
 
4
5
  module R10K::CLI
@@ -21,6 +22,12 @@ module R10K::CLI
21
22
  R10K::Deployment.instance.configfile = value
22
23
  end
23
24
 
25
+ required :v, :verbose, 'Set verbosity level' do |value, cmd|
26
+ R10K::Logging.level = Integer(value)
27
+ end
28
+
29
+ flag :t, :trace, 'Display stack traces on application crash'
30
+
24
31
  run do |opts, args, cmd|
25
32
  puts cmd.help
26
33
  exit 0
@@ -31,3 +38,5 @@ end
31
38
 
32
39
  require 'r10k/cli/environment'
33
40
  require 'r10k/cli/module'
41
+ require 'r10k/cli/cache'
42
+ require 'r10k/cli/synchronize'
@@ -1,8 +1,8 @@
1
- require 'r10k/cli/environment'
1
+ require 'r10k/cli'
2
2
  require 'r10k/synchro/git'
3
3
  require 'cri'
4
4
 
5
- module R10K::CLI::Environment
5
+ module R10K::CLI
6
6
  module Cache
7
7
  def self.command
8
8
  @cmd ||= Cri::Command.define do
@@ -11,8 +11,17 @@ module R10K::CLI::Environment
11
11
  summary 'Update cache for all sources'
12
12
 
13
13
  run do |opts, args, cmd|
14
- R10K::Deployment.instance[:sources].each_pair do |name, source|
15
- synchro = R10K::Synchro::Git.new(source)
14
+ sources = R10K::Deployment.instance[:sources]
15
+
16
+ remotes = Set.new
17
+
18
+ sources.each_pair do |name, hash|
19
+ remotes << hash['remote']
20
+ end
21
+
22
+ remotes.each do |remote|
23
+ puts "Synchronizing #{remote}"
24
+ synchro = R10K::Synchro::Git.new(remote)
16
25
  synchro.cache
17
26
  end
18
27
  end
@@ -9,8 +9,6 @@ module R10K::CLI
9
9
  usage 'environment <subcommand>'
10
10
  summary 'Operate on a specific environment'
11
11
 
12
- required :e, :environment, 'Specify a particular environment'
13
-
14
12
  run do |opts, args, cmd|
15
13
  puts cmd.help
16
14
  exit 0
@@ -23,4 +21,4 @@ end
23
21
 
24
22
  require 'r10k/cli/environment/list'
25
23
  require 'r10k/cli/environment/deploy'
26
- require 'r10k/cli/environment/cache'
24
+ require 'r10k/cli/environment/stale'
@@ -1,43 +1,45 @@
1
1
  require 'r10k/cli/environment'
2
2
  require 'r10k/deployment'
3
- require 'cri'
3
+ require 'r10k/action'
4
4
 
5
- require 'fileutils'
5
+ require 'cri'
6
+ require 'middleware'
6
7
 
7
8
  module R10K::CLI::Environment
8
9
  module Deploy
9
10
  def self.command
10
11
  @cmd ||= Cri::Command.define do
11
12
  name 'deploy'
12
- usage 'deploy'
13
+ usage 'deploy <environment> <...>'
13
14
  summary 'Deploy an environment'
14
15
 
15
16
  flag :r, :recurse, 'Recursively update submodules'
16
-
17
- required :u, :update, "Enable or disable cache updating"
17
+ flag :u, :update, "Enable or disable cache updating"
18
18
 
19
19
  run do |opts, args, cmd|
20
20
  deployment = R10K::Deployment.instance
21
21
  env_list = deployment.environments
22
22
 
23
- update_cache = (defined? opts[:update]) ? (opts[:update] == 'true') : false
24
-
25
- if opts[:environment]
26
- environments = env_list.select {|env| env.name == opts[:environment]}
23
+ if not args.empty?
24
+ environments = env_list.select {|env| args.include? env.name }
27
25
  else
28
26
  environments = env_list
29
27
  end
30
28
 
31
- environments.each do |env|
32
- FileUtils.mkdir_p env.full_path
33
- env.sync! :update_cache => update_cache
34
-
35
- if opts[:recurse]
36
- env.modules.each do |mod|
37
- mod.sync! :update_cache => update_cache
38
- end
29
+ stack = Middleware::Builder.new do
30
+ environments.each do |env|
31
+ use R10K::Action::Environment::Deploy, env
39
32
  end
40
33
  end
34
+
35
+ # Prepare middleware environment
36
+ stack_env = {
37
+ :update_cache => opts[:update],
38
+ :recurse => opts[:recurse],
39
+ :trace => opts[:trace],
40
+ }
41
+
42
+ stack.call(stack_env)
41
43
  end
42
44
  end
43
45
  end
@@ -0,0 +1,34 @@
1
+ require 'r10k/cli/environment'
2
+ require 'r10k/deployment'
3
+ require 'cri'
4
+
5
+ module R10K::CLI::Environment
6
+ module Stale
7
+ def self.command
8
+ @cmd ||= Cri::Command.define do
9
+ name 'stale'
10
+ usage 'stale <directory> [directory ...]'
11
+ summary 'List all stale environments'
12
+
13
+ run do |opts, args, cmd|
14
+ deployment = R10K::Deployment.instance
15
+
16
+ if args.empty?
17
+ $stderr.print "ERROR: ".red
18
+ $stderr.puts "#{cmd.name} requires one or more directories"
19
+ $stderr.puts cmd.help
20
+ exit(1)
21
+ end
22
+
23
+ args.each do |dir|
24
+ puts "Stale environments in #{dir}:"
25
+ output = deployment.collection.stale(dir).each do |stale_dir|
26
+ puts " - #{stale_dir}"
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ self.command.add_command(Stale.command)
34
+ end
@@ -9,10 +9,10 @@ module R10K::CLI::Module
9
9
  def self.command
10
10
  @cmd ||= Cri::Command.define do
11
11
  name 'deploy'
12
- usage 'deploy <module name>'
12
+ usage 'deploy [module name] <module name> ...'
13
13
  summary 'Deploy a module'
14
14
 
15
- required :u, :update, "Enable or disable cache updating"
15
+ flag :u, :update, "Update module cache"
16
16
 
17
17
  run do |opts, args, cmd|
18
18
 
@@ -21,10 +21,7 @@ module R10K::CLI::Module
21
21
  exit 1
22
22
  end
23
23
 
24
- deployment = R10K::Deployment.instance
25
- env_list = deployment.environments
26
-
27
- update_cache = (defined? opts[:update]) ? (opts[:update] == 'true') : false
24
+ env_list = R10K::Deployment.instance.environments
28
25
 
29
26
  if opts[:environment]
30
27
  environments = env_list.select {|env| env.name == opts[:environment]}
@@ -33,9 +30,6 @@ module R10K::CLI::Module
33
30
  end
34
31
 
35
32
  environments.each do |env|
36
- FileUtils.mkdir_p env.full_path
37
- env.sync! :update_cache => update_cache
38
-
39
33
  mods = env.modules.select { |mod| mod.name == module_name }
40
34
 
41
35
  if mods.empty?
@@ -43,9 +37,15 @@ module R10K::CLI::Module
43
37
  exit 1
44
38
  end
45
39
 
46
- mods.each do |mod|
47
- mod.sync! :update_cache => update_cache
48
- end
40
+ stack = Middleware::Builder.new
41
+ mods.each { |mod| stack.use R10K::Action::Module::Deploy, mod }
42
+
43
+ stack_env = {
44
+ :update_cache => opts[:update],
45
+ :trace => opts[:trace],
46
+ }
47
+
48
+ stack.call(stack_env)
49
49
  end
50
50
  end
51
51
  end
@@ -0,0 +1,45 @@
1
+ require 'r10k/cli'
2
+ require 'r10k/deployment'
3
+ require 'r10k/action/environment'
4
+
5
+ require 'middleware'
6
+ require 'cri'
7
+
8
+ module R10K::CLI
9
+ module Synchronize
10
+ def self.command
11
+ @cmd ||= Cri::Command.define do
12
+ name 'synchronize'
13
+ usage 'synchronize <options>'
14
+ summary 'Fully synchronize all environments'
15
+
16
+ flag :u, :update, "Update cache before running"
17
+
18
+ run do |opts, args, cmd|
19
+ deployment = R10K::Deployment.instance
20
+ environments = deployment.environments
21
+ directories = (deployment.config[:purgedirs] || [])
22
+
23
+ stack = Middleware::Builder.new do
24
+ environments.each do |env|
25
+ use R10K::Action::Environment::Deploy, env
26
+ end
27
+
28
+ directories.each do |dir|
29
+ use R10K::Action::Environment::Purge, dir
30
+ end
31
+ end
32
+
33
+ stack_env = {
34
+ :update_cache => opts[:update],
35
+ :trace => opts[:trace],
36
+ :recurse => true,
37
+ }
38
+
39
+ stack.call(stack_env)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ self.command.add_command(Synchronize.command)
45
+ end
@@ -1,6 +1,6 @@
1
1
  require 'r10k'
2
2
  require 'r10k/synchro/git'
3
- require 'r10k/environment_collection'
3
+ require 'r10k/deployment/environment_collection'
4
4
  require 'yaml'
5
5
 
6
6
  class R10K::Deployment
@@ -21,10 +21,14 @@ class R10K::Deployment
21
21
  #
22
22
  # @return [Array<R10K::Root>]
23
23
  def environments
24
- collection = R10K::EnvironmentCollection.new(config)
25
24
  collection.to_a
26
25
  end
27
26
 
27
+ def collection
28
+ load_config unless @config
29
+ @collection
30
+ end
31
+
28
32
  # Serve up the loaded config if it's already been loaded, otherwise try to
29
33
  # load a config in the current wd.
30
34
  def config
@@ -47,8 +51,6 @@ class R10K::Deployment
47
51
  File.open(@configfile) { |fh| @config = YAML.load(fh.read) }
48
52
  apply_config_settings
49
53
  @config
50
- rescue => e
51
- raise "Couldn't load #{configfile}: #{e}"
52
54
  end
53
55
 
54
56
  # Apply config settings to the relevant classes after a config has been loaded.
@@ -56,5 +58,6 @@ class R10K::Deployment
56
58
  if @config[:cachedir]
57
59
  R10K::Synchro::Git.cache_root = @config[:cachedir]
58
60
  end
61
+ @collection = R10K::EnvironmentCollection.new(@config)
59
62
  end
60
63
  end
@@ -4,15 +4,22 @@ class R10K::EnvironmentCollection
4
4
 
5
5
  attr_reader :update_cache
6
6
 
7
- def initialize(config, options = {:update_cache => true})
7
+ def initialize(config, options = {:update_cache => false})
8
8
  @config = config
9
9
  @environments = []
10
10
 
11
11
  @update_cache = options.delete(:update_cache)
12
-
13
12
  load_all
14
13
  end
15
14
 
15
+ def current(basedir)
16
+ basedir = File.expand_path(basedir)
17
+ tracked_envs = @environments.select do |env|
18
+ envdir = File.expand_path(env.basedir)
19
+ envdir == basedir
20
+ end
21
+ end
22
+
16
23
  # List subdirectories that aren't associated with an env
17
24
  #
18
25
  # If a branch associated with an environment is deleted then the associated
@@ -23,8 +30,17 @@ class R10K::EnvironmentCollection
23
30
  # @param [String] basedir The directory to scan
24
31
  #
25
32
  # @return [Array<String>] A list of filenames
26
- def untracked_environments(basedir)
27
- raise NotImplementedError
33
+ def stale(basedir)
34
+ basedir = File.expand_path(basedir)
35
+
36
+ all_dirs = Dir.glob("#{basedir}/*").map do |file|
37
+ File.basename(file) if File.directory?(file)
38
+ end.compact
39
+ current_dirs = current(basedir).map(&:name)
40
+
41
+ stale_dirs = all_dirs - current_dirs
42
+
43
+ stale_dirs.map {|dir| File.join(basedir, dir)}
28
44
  end
29
45
 
30
46
  # @return [Array<R10K::Root>]
@@ -0,0 +1,7 @@
1
+ require 'r10k'
2
+
3
+ module R10K
4
+ class ExecutionFailure < Exception
5
+ attr_accessor :exit_code, :stdout, :stderr
6
+ end
7
+ end
@@ -0,0 +1,49 @@
1
+ require 'r10k'
2
+
3
+ require 'log4r'
4
+ require 'log4r/configurator'
5
+
6
+ module R10K::Logging
7
+
8
+ include Log4r
9
+
10
+ def logger
11
+ unless @logger
12
+ @logger = Log4r::Logger.new(self.class.name)
13
+ @logger.add R10K::Logging.outputter
14
+ end
15
+ @logger
16
+ end
17
+
18
+ class << self
19
+
20
+ include Log4r
21
+ def included(klass)
22
+ unless @log4r_loaded
23
+ Configurator.custom_levels(*%w{DEBUG2 DEBUG1 DEBUG INFO NOTICE WARN ERROR FATAL})
24
+ Logger.global.level = Log4r::ALL
25
+ @log4r_loaded = true
26
+ end
27
+ end
28
+
29
+ def level
30
+ @level || Log4r::WARN # Default level is WARN
31
+ end
32
+
33
+ def level=(val)
34
+ outputter.level = val
35
+ @level = val
36
+ end
37
+
38
+ def formatter
39
+ @formatter ||= Log4r::PatternFormatter.new(:pattern => '[%C - %l] %m')
40
+ end
41
+
42
+ def outputter
43
+ @outputter ||= Log4r::StderrOutputter.new('console',
44
+ :level => self.level,
45
+ :formatter => formatter
46
+ )
47
+ end
48
+ end
49
+ end
@@ -1,19 +1,84 @@
1
1
  require 'r10k'
2
2
  require 'r10k/module'
3
+ require 'r10k/errors'
4
+ require 'r10k/logging'
5
+
6
+ require 'systemu'
7
+ require 'semver'
8
+ require 'json'
3
9
 
4
10
  class R10K::Module::Forge < R10K::Module
5
11
 
6
12
  def self.implements(name, args)
7
- args.is_a? String and args.match /\d+\.\d+\.\d+/
13
+ args.is_a? String and SemVer.valid?(args)
8
14
  end
9
15
 
16
+ include R10K::Logging
17
+
10
18
  def initialize(name, path, args)
11
19
  super
12
20
 
13
- @version = @args
21
+ @full_name = name
22
+
23
+ @owner, @name = name.split('/')
24
+ @version = SemVer.new(@args)
14
25
  end
15
26
 
16
27
  def sync!(options = {})
17
- puts "#{self.class.name}#sync! is not implemented. Doing nothing."
28
+ return if insync?
29
+
30
+ if insync?
31
+ logger.debug1 "Module #{@full_name} already matches version #{@version}"
32
+ elsif File.exist? metadata_path
33
+ logger.debug "Module #{@full_name} is installed but doesn't match version #{@version}, upgrading"
34
+ cmd = []
35
+ cmd << 'upgrade'
36
+ cmd << "--version=#{@version}"
37
+ cmd << "--ignore-dependencies"
38
+ cmd << @full_name
39
+ pmt cmd
40
+ else
41
+ logger.debug "Module #{@full_name} is not installed"
42
+ cmd = []
43
+ cmd << 'install'
44
+ cmd << "--version=#{@version}"
45
+ cmd << "--ignore-dependencies"
46
+ cmd << @full_name
47
+ pmt cmd
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def current_version
54
+ SemVer.new(metadata['version'])
55
+ end
56
+
57
+ def insync?
58
+ @version == current_version
59
+ rescue
60
+ false
61
+ end
62
+
63
+ def metadata
64
+ JSON.parse(File.read(metadata_path))
65
+ end
66
+
67
+ def metadata_path
68
+ File.join(full_path, 'metadata.json')
69
+ end
70
+
71
+ def pmt(args)
72
+ cmd = "puppet module --modulepath '#{@path}' #{args.join(' ')}"
73
+ logger.debug1 "Execute: #{cmd}"
74
+ status, stdout, stderr = systemu(cmd)
75
+ unless status == 0
76
+ e = R10K::ExecutionFailure.new("#{cmd.inspect} returned with non-zero exit value #{status.inspect}")
77
+ e.exit_code = status
78
+ e.stdout = stdout
79
+ e.stderr = stderr
80
+ raise e
81
+ end
82
+ stdout
18
83
  end
19
84
  end
@@ -1,5 +1,8 @@
1
1
  require 'r10k'
2
- require 'shellter'
2
+ require 'r10k/errors'
3
+ require 'r10k/logging'
4
+
5
+ require 'systemu'
3
6
  require 'fileutils'
4
7
 
5
8
  module R10K::Synchro; end
@@ -39,6 +42,8 @@ class R10K::Synchro::Git
39
42
  end
40
43
  end
41
44
 
45
+ include R10K::Logging
46
+
42
47
  attr_reader :remote
43
48
 
44
49
  # Instantiates a new git synchro and optionally prepares for caching
@@ -48,7 +53,7 @@ class R10K::Synchro::Git
48
53
  @remote = remote
49
54
 
50
55
  if self.class.cache_root
51
- @cache_path = File.join(self.class.cache_root, @remote.gsub(/[^@\w-]/, '-'))
56
+ @cache_path = File.join(self.class.cache_root, @remote.gsub(/[^@\w\.-]/, '-'))
52
57
  end
53
58
  end
54
59
 
@@ -69,7 +74,7 @@ class R10K::Synchro::Git
69
74
  end
70
75
 
71
76
  # @return [TrueClass] if the git repository is cached
72
- def has_cache?
77
+ def cached?
73
78
  @cache_path and File.directory? @cache_path
74
79
  end
75
80
 
@@ -94,10 +99,13 @@ class R10K::Synchro::Git
94
99
 
95
100
  # Force a cache refresh
96
101
  def cache!
97
- if has_cache?
98
- git "--git-dir #{@cache_path} fetch --prune"
102
+ if cached?
103
+ logger.debug "Updating existing cache at #{@cache_path}"
104
+ git "fetch --prune", :git_dir => @cache_path
99
105
  else
100
- FileUtils.mkdir_p File.dirname(File.join(@cache_path))
106
+ logger.debug "No cache for #{@remote.inspect}, forcing cache build"
107
+ cache_root = self.class.cache_root
108
+ FileUtils.mkdir_p cache_root unless File.exist? cache_root
101
109
  git "clone --mirror #{@remote} #{@cache_path}"
102
110
  end
103
111
  end
@@ -106,9 +114,9 @@ class R10K::Synchro::Git
106
114
  # object.
107
115
  #
108
116
  # @return [Array<String>] A list of all cached remote branches
109
- def branches
110
- cache
111
- output = git "--git-dir #{@cache_path} branch"
117
+ def branches(options = {:update_cache => false})
118
+ cache if (options[:update_cache] or not cached?)
119
+ output = git "branch", :git_dir => @cache_path
112
120
  output.split("\n").map { |str| str[2..-1] }
113
121
  end
114
122
 
@@ -121,8 +129,9 @@ class R10K::Synchro::Git
121
129
  #
122
130
  # @param [String] path The directory to create the repo working directory
123
131
  def clone(path)
124
- if has_cache?
132
+ if cached?
125
133
  git "clone --reference #{@cache_path} #{@remote} #{path}"
134
+ git "remote add cache #{@cache_path}", :path => path
126
135
  else
127
136
  FileUtils.mkdir_p path unless File.directory? path
128
137
  git "clone #{@remote} #{path}"
@@ -130,10 +139,10 @@ class R10K::Synchro::Git
130
139
  end
131
140
 
132
141
  def fetch(path)
133
- if has_cache?
134
- git "fetch --prune #{@cache_path}", path
142
+ if cached?
143
+ git "fetch --prune cache", :path => path
135
144
  else
136
- git "fetch --prune", path
145
+ git "fetch --prune origin", :path => path
137
146
  end
138
147
  end
139
148
 
@@ -142,45 +151,76 @@ class R10K::Synchro::Git
142
151
  # @param [String] path The path to the working directory of the git repo
143
152
  # @param [String] ref The git reference to reset to.
144
153
  def reset(path, ref)
154
+ commit = resolve_commit(ref)
145
155
 
146
- # Helloooo, hackery. Try to parse the ref as a commit object. If that fails
147
- # this probably means that the ref is a remote branch. For the sake of
148
- # brevity this code blindly makes that assumption on the failure of
149
- # `git rev-parse`.
150
156
  begin
151
- commit = git "--git-dir #{@cache_path} rev-parse #{ref}^{commit}"
152
- rescue RuntimeError => e
153
- commit = "origin/#{ref}"
157
+ git "reset --hard #{commit}", :path => path
158
+ rescue R10K::ExecutionFailure => e
159
+ logger.error "Unable to locate commit object #{commit} in git repo #{path}"
160
+ raise
154
161
  end
162
+ end
155
163
 
156
- git "reset --hard #{commit}", path
164
+ # Resolve a ref to a commit hash
165
+ #
166
+ # @param [String] ref
167
+ #
168
+ # @return [String] The dereferenced hash of `ref`
169
+ def resolve_commit(ref)
170
+ commit = git "rev-parse #{ref}^{commit}", :git_dir => @cache_path
171
+ commit.chomp
172
+ rescue R10K::ExecutionFailure => e
173
+ logger.error "Could not resolve ref #{ref.inspect} for git cache #{@cache_path}"
174
+ raise
157
175
  end
158
176
 
159
177
  # Wrap git commands
160
178
  #
161
179
  # @param [String] command_line_args The arguments for the git prompt
162
- # @param [String] git_dir An optional git working directory
180
+ # @param [Hash] opts
181
+ #
182
+ # @option opts [String] :git_dir
183
+ # @option opts [String] :work_tree
184
+ # @option opts [String] :work_tree
163
185
  #
164
186
  # @return [String] The git command output
165
- def git(command_line_args, git_dir = nil)
166
- args = []
187
+ def git(command_line_args, opts = {})
188
+ args = %w{git}
167
189
 
168
- if git_dir
169
- args << "--work-tree" << git_dir
170
- args << "--git-dir" << "#{git_dir}/.git"
190
+ log_event = "git #{command_line_args}"
191
+ log_event << ", args: #{opts.inspect}" unless opts.empty?
192
+
193
+
194
+ if opts[:path]
195
+ args << "--git-dir #{opts[:path]}/.git"
196
+ args << "--work-tree #{opts[:path]}"
197
+ else
198
+ if opts[:git_dir]
199
+ args << "--git-dir #{opts[:git_dir]}"
200
+ end
201
+ if opts[:work_tree]
202
+ args << "--work-tree #{opts[:work_tree]}"
203
+ end
171
204
  end
172
205
 
173
- args << command_line_args.split(/\s+/)
206
+ logger.debug1 "Execute: '#{log_event}'"
174
207
 
175
- result = Shellter.run!('git', args.join(' '))
176
- puts "Execute: #{result.last_command}".green
208
+ args << command_line_args
209
+ cmd = args.join(' ')
177
210
 
178
- stderr = result.stderr.read
179
- stdout = result.stdout.read
211
+ status, stdout, stderr = systemu(cmd)
180
212
 
181
- puts stdout.blue unless stdout.empty?
182
- puts stderr.red unless stderr.empty?
213
+ logger.debug2 "[#{log_event}] STDOUT: #{stdout.chomp}" unless stdout.empty?
214
+ logger.debug2 "[#{log_event}] STDERR: #{stderr.chomp}" unless stderr.empty?
183
215
 
216
+ unless status == 0
217
+ msg = "#{cmd.inspect} returned with non-zero exit value #{status.exitstatus}"
218
+ e = R10K::ExecutionFailure.new(msg)
219
+ e.exit_code = status
220
+ e.stdout = stdout
221
+ e.stderr = stderr
222
+ raise e
223
+ end
184
224
  stdout
185
225
  end
186
226
  end
data/lib/r10k/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module R10K
2
- VERSION = '0.0.1rc1'
2
+ VERSION = '0.0.1'
3
3
  end
data/lib/semver.rb ADDED
@@ -0,0 +1,124 @@
1
+ # Ripped off from puppetlabs/puppet
2
+
3
+ # We need to subclass Numeric to force range comparisons not to try to iterate over SemVer
4
+ # and instead use numeric comparisons (eg >, <, >=, <=)
5
+ # Ruby 1.8 already did this for all ranges, but Ruby 1.9 changed range include behavior
6
+ class SemVer < Numeric
7
+ include Comparable
8
+
9
+ VERSION = /^v?(\d+)\.(\d+)\.(\d+)(-[0-9A-Za-z-]*|)$/
10
+ SIMPLE_RANGE = /^v?(\d+|[xX])(?:\.(\d+|[xX])(?:\.(\d+|[xX]))?)?$/
11
+
12
+ def self.valid?(ver)
13
+ VERSION =~ ver
14
+ end
15
+
16
+ def self.find_matching(pattern, versions)
17
+ versions.select { |v| v.matched_by?("#{pattern}") }.sort.last
18
+ end
19
+
20
+ def self.pre(vstring)
21
+ vstring =~ /-/ ? vstring : vstring + '-'
22
+ end
23
+
24
+ def self.[](range)
25
+ range.gsub(/([><=])\s+/, '\1').split(/\b\s+(?!-)/).map do |r|
26
+ case r
27
+ when SemVer::VERSION
28
+ SemVer.new(pre(r)) .. SemVer.new(r)
29
+ when SemVer::SIMPLE_RANGE
30
+ r += ".0" unless SemVer.valid?(r.gsub(/x/i, '0'))
31
+ SemVer.new(r.gsub(/x/i, '0'))...SemVer.new(r.gsub(/(\d+)\.x/i) { "#{$1.to_i + 1}.0" } + '-')
32
+ when /\s+-\s+/
33
+ a, b = r.split(/\s+-\s+/)
34
+ SemVer.new(pre(a)) .. SemVer.new(b)
35
+ when /^~/
36
+ ver = r.sub(/~/, '').split('.').map(&:to_i)
37
+ start = (ver + [0] * (3 - ver.length)).join('.')
38
+
39
+ ver.pop unless ver.length == 1
40
+ ver[-1] = ver.last + 1
41
+
42
+ finish = (ver + [0] * (3 - ver.length)).join('.')
43
+ SemVer.new(pre(start)) ... SemVer.new(pre(finish))
44
+ when /^>=/
45
+ ver = r.sub(/^>=/, '')
46
+ SemVer.new(pre(ver)) .. SemVer::MAX
47
+ when /^<=/
48
+ ver = r.sub(/^<=/, '')
49
+ SemVer::MIN .. SemVer.new(ver)
50
+ when /^>/
51
+ if r =~ /-/
52
+ ver = [r[1..-1]]
53
+ else
54
+ ver = r.sub(/^>/, '').split('.').map(&:to_i)
55
+ ver[2] = ver.last + 1
56
+ end
57
+ SemVer.new(ver.join('.') + '-') .. SemVer::MAX
58
+ when /^</
59
+ ver = r.sub(/^</, '')
60
+ SemVer::MIN ... SemVer.new(pre(ver))
61
+ else
62
+ (1..1)
63
+ end
64
+ end.inject { |a,e| a & e }
65
+ end
66
+
67
+ attr_reader :major, :minor, :tiny, :special
68
+
69
+ def initialize(ver)
70
+ unless SemVer.valid?(ver)
71
+ raise ArgumentError.new("Invalid version string '#{ver}'!")
72
+ end
73
+
74
+ @major, @minor, @tiny, @special = VERSION.match(ver).captures.map do |x|
75
+ # Because Kernel#Integer tries to interpret hex and octal strings, which
76
+ # we specifically do not want, and which cannot be overridden in 1.8.7.
77
+ Float(x).to_i rescue x
78
+ end
79
+ end
80
+
81
+ def <=>(other)
82
+ other = SemVer.new("#{other}") unless other.is_a? SemVer
83
+ return self.major <=> other.major unless self.major == other.major
84
+ return self.minor <=> other.minor unless self.minor == other.minor
85
+ return self.tiny <=> other.tiny unless self.tiny == other.tiny
86
+
87
+ return 0 if self.special == other.special
88
+ return 1 if self.special == ''
89
+ return -1 if other.special == ''
90
+
91
+ return self.special <=> other.special
92
+ end
93
+
94
+ def matched_by?(pattern)
95
+ # For the time being, this is restricted to exact version matches and
96
+ # simple range patterns. In the future, we should implement some or all of
97
+ # the comparison operators here:
98
+ # https://github.com/isaacs/node-semver/blob/d474801/semver.js#L340
99
+
100
+ case pattern
101
+ when SIMPLE_RANGE
102
+ pattern = SIMPLE_RANGE.match(pattern).captures
103
+ pattern[1] = @minor unless pattern[1] && pattern[1] !~ /x/i
104
+ pattern[2] = @tiny unless pattern[2] && pattern[2] !~ /x/i
105
+ [@major, @minor, @tiny] == pattern.map { |x| x.to_i }
106
+ when VERSION
107
+ self == SemVer.new(pattern)
108
+ else
109
+ false
110
+ end
111
+ end
112
+
113
+ def inspect
114
+ @vstring || "v#{@major}.#{@minor}.#{@tiny}#{@special}"
115
+ end
116
+ alias :to_s :inspect
117
+
118
+ MIN = SemVer.new('0.0.0-')
119
+ MIN.instance_variable_set(:@vstring, 'vMIN')
120
+
121
+ MAX = SemVer.new('8.0.0')
122
+ MAX.instance_variable_set(:@major, (1.0/0)) # => Infinity
123
+ MAX.instance_variable_set(:@vstring, 'vMAX')
124
+ end
metadata CHANGED
@@ -1,16 +1,32 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: r10k
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1rc1
5
- prerelease: 5
4
+ version: 0.0.1
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
- - adrien@somethingsinistral.net
8
+ - Adrien Thebo
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-26 00:00:00.000000000 Z
12
+ date: 2013-01-04 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: colored
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '1.2'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '1.2'
14
30
  - !ruby/object:Gem::Dependency
15
31
  name: cri
16
32
  requirement: !ruby/object:Gem::Requirement
@@ -28,13 +44,13 @@ dependencies:
28
44
  - !ruby/object:Gem::Version
29
45
  version: 2.3.0
30
46
  - !ruby/object:Gem::Dependency
31
- name: shellter
47
+ name: systemu
32
48
  requirement: !ruby/object:Gem::Requirement
33
49
  none: false
34
50
  requirements:
35
51
  - - ~>
36
52
  - !ruby/object:Gem::Version
37
- version: 0.9.6
53
+ version: 2.5.2
38
54
  type: :runtime
39
55
  prerelease: false
40
56
  version_requirements: !ruby/object:Gem::Requirement
@@ -42,15 +58,15 @@ dependencies:
42
58
  requirements:
43
59
  - - ~>
44
60
  - !ruby/object:Gem::Version
45
- version: 0.9.6
61
+ version: 2.5.2
46
62
  - !ruby/object:Gem::Dependency
47
- name: popen4
63
+ name: middleware
48
64
  requirement: !ruby/object:Gem::Requirement
49
65
  none: false
50
66
  requirements:
51
67
  - - ~>
52
68
  - !ruby/object:Gem::Version
53
- version: 0.1.2
69
+ version: 0.1.0
54
70
  type: :runtime
55
71
  prerelease: false
56
72
  version_requirements: !ruby/object:Gem::Requirement
@@ -58,30 +74,69 @@ dependencies:
58
74
  requirements:
59
75
  - - ~>
60
76
  - !ruby/object:Gem::Version
61
- version: 0.1.2
77
+ version: 0.1.0
78
+ - !ruby/object:Gem::Dependency
79
+ name: json
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 1.7.6
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 1.7.6
94
+ - !ruby/object:Gem::Dependency
95
+ name: log4r
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: 1.1.10
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: 1.1.10
62
110
  description: ! " R10K is an implementation of the Dynamic Puppet environments based
63
111
  on git repositories\n as described in http://puppetlabs.com/blog/git-workflow-and-puppet-environments/.
64
112
  It\n aggressively caches and tries to minimize network activity to ensure that
65
113
  interactive\n deployment is as fast as possible.\n"
66
- email:
114
+ email: adrien@somethingsinistral.net
67
115
  executables:
68
116
  - r10k
69
117
  extensions: []
70
118
  extra_rdoc_files: []
71
119
  files:
72
120
  - bin/r10k
73
- - lib/r10k/cli/environment/cache.rb
121
+ - lib/r10k/action/environment.rb
122
+ - lib/r10k/action/module.rb
123
+ - lib/r10k/action.rb
124
+ - lib/r10k/cli/cache.rb
74
125
  - lib/r10k/cli/environment/deploy.rb
75
126
  - lib/r10k/cli/environment/list.rb
127
+ - lib/r10k/cli/environment/stale.rb
76
128
  - lib/r10k/cli/environment.rb
77
129
  - lib/r10k/cli/module/deploy.rb
78
130
  - lib/r10k/cli/module/list.rb
79
131
  - lib/r10k/cli/module.rb
132
+ - lib/r10k/cli/synchronize.rb
80
133
  - lib/r10k/cli.rb
134
+ - lib/r10k/deployment/environment_collection.rb
81
135
  - lib/r10k/deployment.rb
82
- - lib/r10k/environment_collection.rb
136
+ - lib/r10k/errors.rb
83
137
  - lib/r10k/librarian/dsl.rb
84
138
  - lib/r10k/librarian.rb
139
+ - lib/r10k/logging.rb
85
140
  - lib/r10k/module/forge.rb
86
141
  - lib/r10k/module/git.rb
87
142
  - lib/r10k/module.rb
@@ -90,6 +145,7 @@ files:
90
145
  - lib/r10k/util/interp.rb
91
146
  - lib/r10k/version.rb
92
147
  - lib/r10k.rb
148
+ - lib/semver.rb
93
149
  homepage: http://github.com/adrienthebo/r10k
94
150
  licenses: []
95
151
  post_install_message:
@@ -105,9 +161,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
105
161
  required_rubygems_version: !ruby/object:Gem::Requirement
106
162
  none: false
107
163
  requirements:
108
- - - ! '>'
164
+ - - ! '>='
109
165
  - !ruby/object:Gem::Version
110
- version: 1.3.1
166
+ version: '0'
111
167
  requirements: []
112
168
  rubyforge_project:
113
169
  rubygems_version: 1.8.23