r10k 0.0.1rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'r10k/cli'
4
+
5
+ R10K::CLI.command.run(ARGV)
@@ -0,0 +1,6 @@
1
+ module R10K; end
2
+
3
+ require 'r10k/root'
4
+ require 'r10k/synchro/git'
5
+ require 'r10k/librarian'
6
+ require 'r10k/version'
@@ -0,0 +1,33 @@
1
+ require 'r10k'
2
+ require 'cri'
3
+
4
+ module R10K::CLI
5
+ def self.command
6
+ @cmd ||= Cri::Command.define do
7
+ name 'r10k'
8
+ usage 'r10k <subcommand> [options]'
9
+ summary 'Killer robot powered Puppet environment deployment'
10
+ description <<-EOD
11
+ r10k is a suite of commands to help deploy and manage puppet code for
12
+ complex environments.
13
+ EOD
14
+
15
+ flag :h, :help, 'show help for this command' do |value, cmd|
16
+ puts cmd.help
17
+ exit 0
18
+ end
19
+
20
+ required :c, :config, 'Specify a configuration file' do |value, cmd|
21
+ R10K::Deployment.instance.configfile = value
22
+ end
23
+
24
+ run do |opts, args, cmd|
25
+ puts cmd.help
26
+ exit 0
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ require 'r10k/cli/environment'
33
+ require 'r10k/cli/module'
@@ -0,0 +1,26 @@
1
+ require 'r10k/cli'
2
+ require 'cri'
3
+
4
+ module R10K::CLI
5
+ module Environment
6
+ def self.command
7
+ @cmd ||= Cri::Command.define do
8
+ name 'environment'
9
+ usage 'environment <subcommand>'
10
+ summary 'Operate on a specific environment'
11
+
12
+ required :e, :environment, 'Specify a particular environment'
13
+
14
+ run do |opts, args, cmd|
15
+ puts cmd.help
16
+ exit 0
17
+ end
18
+ end
19
+ end
20
+ end
21
+ self.command.add_command(Environment.command)
22
+ end
23
+
24
+ require 'r10k/cli/environment/list'
25
+ require 'r10k/cli/environment/deploy'
26
+ require 'r10k/cli/environment/cache'
@@ -0,0 +1,23 @@
1
+ require 'r10k/cli/environment'
2
+ require 'r10k/synchro/git'
3
+ require 'cri'
4
+
5
+ module R10K::CLI::Environment
6
+ module Cache
7
+ def self.command
8
+ @cmd ||= Cri::Command.define do
9
+ name 'cache'
10
+ usage 'cache'
11
+ summary 'Update cache for all sources'
12
+
13
+ run do |opts, args, cmd|
14
+ R10K::Deployment.instance[:sources].each_pair do |name, source|
15
+ synchro = R10K::Synchro::Git.new(source)
16
+ synchro.cache
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ self.command.add_command(Cache.command)
23
+ end
@@ -0,0 +1,46 @@
1
+ require 'r10k/cli/environment'
2
+ require 'r10k/deployment'
3
+ require 'cri'
4
+
5
+ require 'fileutils'
6
+
7
+ module R10K::CLI::Environment
8
+ module Deploy
9
+ def self.command
10
+ @cmd ||= Cri::Command.define do
11
+ name 'deploy'
12
+ usage 'deploy'
13
+ summary 'Deploy an environment'
14
+
15
+ flag :r, :recurse, 'Recursively update submodules'
16
+
17
+ required :u, :update, "Enable or disable cache updating"
18
+
19
+ run do |opts, args, cmd|
20
+ deployment = R10K::Deployment.instance
21
+ env_list = deployment.environments
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]}
27
+ else
28
+ environments = env_list
29
+ end
30
+
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
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ self.command.add_command(Deploy.command)
46
+ end
@@ -0,0 +1,27 @@
1
+ require 'r10k/cli/environment'
2
+ require 'r10k/deployment'
3
+ require 'cri'
4
+
5
+ module R10K::CLI::Environment
6
+ module List
7
+ def self.command
8
+ @cmd ||= Cri::Command.define do
9
+ name 'list'
10
+ usage 'list'
11
+ summary 'List all available environments'
12
+
13
+ run do |opts, args, cmd|
14
+ deployment = R10K::Deployment.instance
15
+ output = deployment.environments.inject('') do |str, root|
16
+ str << " - "
17
+ str << "#{root.name}: #{root.full_path}"
18
+ str << "\n"
19
+ end
20
+
21
+ puts output
22
+ end
23
+ end
24
+ end
25
+ end
26
+ self.command.add_command(List.command)
27
+ end
@@ -0,0 +1,25 @@
1
+ require 'r10k/cli'
2
+ require 'cri'
3
+
4
+ module R10K::CLI
5
+ module Module
6
+ def self.command
7
+ @cmd ||= Cri::Command.define do
8
+ name 'module'
9
+ usage 'module <subcommand>'
10
+ summary 'Operate on a specific puppet module'
11
+
12
+ required :e, :environment, 'Specify a particular environment'
13
+
14
+ run do |opts, args, cmd|
15
+ puts cmd.help
16
+ exit 0
17
+ end
18
+ end
19
+ end
20
+ end
21
+ self.command.add_command(Module.command)
22
+ end
23
+
24
+ require 'r10k/cli/module/deploy'
25
+ require 'r10k/cli/module/list'
@@ -0,0 +1,55 @@
1
+ require 'r10k/cli/module'
2
+ require 'r10k/deployment'
3
+ require 'cri'
4
+
5
+ require 'fileutils'
6
+
7
+ module R10K::CLI::Module
8
+ module Deploy
9
+ def self.command
10
+ @cmd ||= Cri::Command.define do
11
+ name 'deploy'
12
+ usage 'deploy <module name>'
13
+ summary 'Deploy a module'
14
+
15
+ required :u, :update, "Enable or disable cache updating"
16
+
17
+ run do |opts, args, cmd|
18
+
19
+ unless (module_name = args[0])
20
+ puts cmd.help
21
+ exit 1
22
+ end
23
+
24
+ deployment = R10K::Deployment.instance
25
+ env_list = deployment.environments
26
+
27
+ update_cache = (defined? opts[:update]) ? (opts[:update] == 'true') : false
28
+
29
+ if opts[:environment]
30
+ environments = env_list.select {|env| env.name == opts[:environment]}
31
+ else
32
+ environments = env_list
33
+ end
34
+
35
+ environments.each do |env|
36
+ FileUtils.mkdir_p env.full_path
37
+ env.sync! :update_cache => update_cache
38
+
39
+ mods = env.modules.select { |mod| mod.name == module_name }
40
+
41
+ if mods.empty?
42
+ puts "No modules with name #{module_name} matched in environments #{env.map(&:name).inspect}".red
43
+ exit 1
44
+ end
45
+
46
+ mods.each do |mod|
47
+ mod.sync! :update_cache => update_cache
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ self.command.add_command(Deploy.command)
55
+ end
@@ -0,0 +1,45 @@
1
+ require 'r10k/cli/module'
2
+ require 'r10k/deployment'
3
+ require 'cri'
4
+
5
+ module R10K::CLI::Module
6
+ module List
7
+ def self.command
8
+ @cmd ||= Cri::Command.define do
9
+ name 'list'
10
+ usage 'list'
11
+ summary 'List modules that are instantiated in environments'
12
+
13
+ run do |opts, args, cmd|
14
+ deployment = R10K::Deployment.instance
15
+ env_list = deployment.environments
16
+
17
+ update_cache = (defined? opts[:update]) ? (opts[:update] == 'true') : false
18
+
19
+ if opts[:environment]
20
+ environments = env_list.select {|env| env.name == opts[:environment]}
21
+ else
22
+ environments = env_list
23
+ end
24
+
25
+ printree = {}
26
+
27
+ environments.each do |env|
28
+ module_names = env.modules.map(&:name)
29
+
30
+ printree[env.name] = module_names
31
+ end
32
+
33
+ printree.each_pair do |env_name, mod_list|
34
+ puts " - #{env_name}"
35
+ mod_list.each do |mod|
36
+ puts " #{mod}"
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ self.command.add_command(List.command)
44
+ end
45
+
@@ -0,0 +1,60 @@
1
+ require 'r10k'
2
+ require 'r10k/synchro/git'
3
+ require 'r10k/environment_collection'
4
+ require 'yaml'
5
+
6
+ class R10K::Deployment
7
+ # Model a full installation of module directories and modules.
8
+
9
+ def self.instance
10
+ @myself ||= self.new
11
+ end
12
+
13
+ def initialize
14
+ @configfile = File.join(Dir.getwd, "config.yaml")
15
+ @update_cache = true
16
+ end
17
+
18
+ attr_accessor :configfile
19
+
20
+ # Load up all module roots
21
+ #
22
+ # @return [Array<R10K::Root>]
23
+ def environments
24
+ collection = R10K::EnvironmentCollection.new(config)
25
+ collection.to_a
26
+ end
27
+
28
+ # Serve up the loaded config if it's already been loaded, otherwise try to
29
+ # load a config in the current wd.
30
+ def config
31
+ load_config unless @config
32
+ @config
33
+ end
34
+
35
+ # @return [Object] A top level key from the config hash
36
+ def setting(key)
37
+ self.config[key]
38
+ end
39
+ alias_method :[], :setting
40
+
41
+ private
42
+
43
+ # Load and store a config file, and set relevant options
44
+ #
45
+ # @param [String] configfile The path to the YAML config file
46
+ def load_config
47
+ File.open(@configfile) { |fh| @config = YAML.load(fh.read) }
48
+ apply_config_settings
49
+ @config
50
+ rescue => e
51
+ raise "Couldn't load #{configfile}: #{e}"
52
+ end
53
+
54
+ # Apply config settings to the relevant classes after a config has been loaded.
55
+ def apply_config_settings
56
+ if @config[:cachedir]
57
+ R10K::Synchro::Git.cache_root = @config[:cachedir]
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,53 @@
1
+ require 'r10k'
2
+
3
+ class R10K::EnvironmentCollection
4
+
5
+ attr_reader :update_cache
6
+
7
+ def initialize(config, options = {:update_cache => true})
8
+ @config = config
9
+ @environments = []
10
+
11
+ @update_cache = options.delete(:update_cache)
12
+
13
+ load_all
14
+ end
15
+
16
+ # List subdirectories that aren't associated with an env
17
+ #
18
+ # If a branch associated with an environment is deleted then the associated
19
+ # branch ceases to be tracked. This method will scan a directory for
20
+ # subdirectories and return any subdirectories that don't have an active
21
+ # branch associated.
22
+ #
23
+ # @param [String] basedir The directory to scan
24
+ #
25
+ # @return [Array<String>] A list of filenames
26
+ def untracked_environments(basedir)
27
+ raise NotImplementedError
28
+ end
29
+
30
+ # @return [Array<R10K::Root>]
31
+ def to_a
32
+ @environments
33
+ end
34
+
35
+ private
36
+
37
+ def load_all
38
+ @config[:sources].each_pair do |repo_name, repo_config|
39
+ synchro = R10K::Synchro::Git.new(repo_config['remote'])
40
+ synchro.cache if @update_cache
41
+
42
+ if repo_config['ref']
43
+ @environments << R10K::Root.new(repo_config)
44
+ else
45
+ synchro.branches.each do |branch|
46
+ @environments << R10K::Root.new(repo_config.merge({'ref' => branch}))
47
+ end
48
+ end
49
+ end
50
+
51
+ @environments
52
+ end
53
+ end
@@ -0,0 +1,31 @@
1
+ require 'r10k'
2
+ require 'r10k/module'
3
+
4
+ class R10K::Librarian
5
+
6
+ attr_reader :forge
7
+
8
+ def initialize(puppetfile)
9
+ @puppetfile = puppetfile
10
+ @modules = []
11
+ @forge = 'forge.puppetlabs.com'
12
+ end
13
+
14
+ def load
15
+ dsl = R10K::Librarian::DSL.new(self)
16
+ dsl.instance_eval(File.read(@puppetfile), @puppetfile)
17
+
18
+ @modules
19
+ end
20
+
21
+ # This method only exists because people tried being excessively clever.
22
+ def set_forge(forge)
23
+ @forge = forge
24
+ end
25
+
26
+ def add_module(name, args)
27
+ @modules << [name, args]
28
+ end
29
+ end
30
+
31
+ require 'r10k/librarian/dsl'
@@ -0,0 +1,20 @@
1
+ require 'r10k/librarian'
2
+
3
+ class R10K::Librarian::DSL
4
+
5
+ def initialize(librarian)
6
+ @librarian = librarian
7
+ end
8
+
9
+ def mod(name, args = [])
10
+ @librarian.add_module(name, args)
11
+ end
12
+
13
+ def forge(location)
14
+ @librarian.set_forge(location)
15
+ end
16
+
17
+ def method_missing(method, *args)
18
+ raise NoMethodError, "unrecognized declaration '#{method}'"
19
+ end
20
+ end
@@ -0,0 +1,45 @@
1
+ require 'r10k'
2
+
3
+ class R10K::Module
4
+
5
+ # Register an inheriting class for later generation
6
+ def self.inherited(klass)
7
+ @klasses ||= []
8
+ @klasses << klass
9
+ end
10
+
11
+ # Look up the implementing class and instantiate an object
12
+ #
13
+ # This method takes the arguments for normal object generation and checks all
14
+ # inheriting classes to see if they implement the behavior needed to create
15
+ # the requested object. It selects the first class that can implement an object
16
+ # with `name, args`, and generates an object of that class.
17
+ #
18
+ # @param [String] name The unique name of the module
19
+ # @param [String] path The root path to install the module in
20
+ # @param [Object] args An arbitary value or set of values that specifies the implementation
21
+ #
22
+ # @return [Object < R10K::Module] A member of the implementing subclass
23
+ def self.new(name, path, args)
24
+ if implementation = @klasses.find { |klass| klass.implements(name, args) }
25
+ obj = implementation.send(:allocate)
26
+ obj.send(:initialize, name, path, args)
27
+ obj
28
+ else
29
+ raise "Module #{name} with args #{args.inspect} doesn't have an implementation. (Are you using the right arguments?)"
30
+ end
31
+ end
32
+
33
+ attr_accessor :name, :path
34
+
35
+ def initialize(name, path, args)
36
+ @name, @path, @args = name, path, args
37
+ end
38
+
39
+ def full_path
40
+ File.join(@path, @name)
41
+ end
42
+ end
43
+
44
+ require 'r10k/module/git'
45
+ require 'r10k/module/forge'
@@ -0,0 +1,19 @@
1
+ require 'r10k'
2
+ require 'r10k/module'
3
+
4
+ class R10K::Module::Forge < R10K::Module
5
+
6
+ def self.implements(name, args)
7
+ args.is_a? String and args.match /\d+\.\d+\.\d+/
8
+ end
9
+
10
+ def initialize(name, path, args)
11
+ super
12
+
13
+ @version = @args
14
+ end
15
+
16
+ def sync!(options = {})
17
+ puts "#{self.class.name}#sync! is not implemented. Doing nothing."
18
+ end
19
+ end
@@ -0,0 +1,24 @@
1
+ require 'r10k'
2
+ require 'r10k/module'
3
+ require 'r10k/synchro/git'
4
+
5
+ class R10K::Module::Git < R10K::Module
6
+
7
+ def self.implements(name, args)
8
+ args.is_a? Hash and args.has_key?(:git)
9
+ rescue
10
+ false
11
+ end
12
+
13
+ def initialize(name, path, args)
14
+ super
15
+
16
+ @remote = @args[:git]
17
+ @ref = (@args[:ref] || 'master')
18
+ end
19
+
20
+ def sync!(options = {})
21
+ synchro = R10K::Synchro::Git.new(@remote)
22
+ synchro.sync(full_path, @ref, options)
23
+ end
24
+ end
@@ -0,0 +1,95 @@
1
+ require 'r10k'
2
+ require 'r10k/module'
3
+ require 'r10k/synchro/git'
4
+
5
+ class R10K::Root
6
+
7
+ # @!attribute [r] name
8
+ # The directory name of this root
9
+ attr_reader :name
10
+
11
+ # @!attribute [r] basedir
12
+ # The basedir to clone the root into
13
+ attr_reader :basedir
14
+
15
+ # @!attribute [r] remote
16
+ # The location of the remote git repository
17
+ attr_reader :remote
18
+
19
+ # @!attribute [r] ref
20
+ # The git ref to instantiate into the basedir
21
+ attr_reader :ref
22
+
23
+ def initialize(hash)
24
+ parse_initialize_hash(hash)
25
+ end
26
+
27
+ def sync!(options = {})
28
+ synchro = R10K::Synchro::Git.new(@remote)
29
+ recursive_needed = !(synchro.cloned?(full_path))
30
+ synchro.sync(full_path, @ref, options)
31
+
32
+ sync_modules!(options) if recursive_needed
33
+ end
34
+
35
+ def sync_modules!(options = {})
36
+ modules.each do |mod|
37
+ mod.sync!(options)
38
+ end
39
+ end
40
+
41
+ def modules
42
+ librarian = R10K::Librarian.new("#{full_path}/Puppetfile")
43
+
44
+ module_data = librarian.load
45
+
46
+ @modules = module_data.map do |mod|
47
+ name = mod[0]
48
+ args = mod[1]
49
+ R10K::Module.new(name, "#{full_path}/modules", args)
50
+ end
51
+ rescue Errno::ENOENT
52
+ puts "#{self}: #{full_path} does not exist, cannot enumerate modules."
53
+ []
54
+ end
55
+
56
+ def full_path
57
+ @full_path ||= File.expand_path(File.join @basedir, @name)
58
+ end
59
+
60
+ private
61
+
62
+ def parse_initialize_hash(hash)
63
+ if hash['name']
64
+ @name = hash.delete('name')
65
+ elsif hash['ref']
66
+ @name = hash['ref']
67
+ else
68
+ raise "Unable to resolve directory name from options #{hash.inspect}"
69
+ end
70
+
71
+ # XXX This could be metaprogrammed, but it seems like the road to madness.
72
+
73
+ if hash['basedir']
74
+ @basedir = hash.delete('basedir')
75
+ else
76
+ raise "'basedir' is a required value for #{self.class}.new"
77
+ end
78
+
79
+ if hash['remote']
80
+ @remote = hash.delete('remote')
81
+ else
82
+ raise "'remote' is a required value for #{self.class}.new"
83
+ end
84
+
85
+ if hash['ref']
86
+ @ref = hash.delete('ref')
87
+ else
88
+ raise "'ref' is a required value for #{self.class}.new"
89
+ end
90
+
91
+ unless hash.empty?
92
+ raise "#{self.class}.new only expects keys ['name', 'basedir', 'remote', 'ref'], got #{hash.keys.inspect}"
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,186 @@
1
+ require 'r10k'
2
+ require 'shellter'
3
+ require 'fileutils'
4
+
5
+ module R10K::Synchro; end
6
+
7
+ class R10K::Synchro::Git
8
+ # Define a thingy that can synchronize git repositories.
9
+ #
10
+ # This class is built to be a general purpose mechanism for syncing and
11
+ # caching git repositories.
12
+ #
13
+ # Class instances are memoized based on the git remote path. This way if a
14
+ # single git repository is instantiated multiple times, the object cache
15
+ # will only be updated once.
16
+
17
+ class << self
18
+ attr_accessor :cache_root
19
+
20
+ # @return [Hash<R10K::Synchro::Git>] A hash of memoized class instances
21
+ def synchros
22
+ @synchros ||= {}
23
+ end
24
+
25
+ # Memoize class instances and return existing instances.
26
+ #
27
+ # This allows objects to mark themselves as cached to prevent unnecessary
28
+ # cache refreshes.
29
+ #
30
+ # @param [String] remote A git remote URL
31
+ # @return [R10K::Synchro::Git]
32
+ def new(remote)
33
+ unless synchros[remote]
34
+ obj = self.allocate
35
+ obj.send(:initialize, remote)
36
+ synchros[remote] = obj
37
+ end
38
+ synchros[remote]
39
+ end
40
+ end
41
+
42
+ attr_reader :remote
43
+
44
+ # Instantiates a new git synchro and optionally prepares for caching
45
+ #
46
+ # @param [String] remote A git remote URL
47
+ def initialize(remote)
48
+ @remote = remote
49
+
50
+ if self.class.cache_root
51
+ @cache_path = File.join(self.class.cache_root, @remote.gsub(/[^@\w-]/, '-'))
52
+ end
53
+ end
54
+
55
+ # Synchronize the local git repository.
56
+ #
57
+ # @param [String] path The destination path for the files
58
+ # @param [String] ref The git ref to instantiate at the destination path
59
+ def sync(path, ref, options = {:update_cache => true})
60
+ path = File.expand_path(path)
61
+ cache if options[:update_cache]
62
+
63
+ if self.cloned?(path)
64
+ fetch(path)
65
+ else
66
+ clone(path)
67
+ end
68
+ reset(path, ref)
69
+ end
70
+
71
+ # @return [TrueClass] if the git repository is cached
72
+ def has_cache?
73
+ @cache_path and File.directory? @cache_path
74
+ end
75
+
76
+ # Determine if repo has been cloned into a specific dir
77
+ #
78
+ # @param [String] dirname The directory to check
79
+ #
80
+ # @return [true, false] If the repo has already been cloned
81
+ def cloned?(directory)
82
+ File.directory?(File.join(directory, '.git'))
83
+ end
84
+
85
+ # Update the git object cache repository if it hasn't been done
86
+ #
87
+ # @return [true, nil] If the cache was actually updated
88
+ def cache
89
+ unless @cached
90
+ cache!
91
+ @cached = true
92
+ end
93
+ end
94
+
95
+ # Force a cache refresh
96
+ def cache!
97
+ if has_cache?
98
+ git "--git-dir #{@cache_path} fetch --prune"
99
+ else
100
+ FileUtils.mkdir_p File.dirname(File.join(@cache_path))
101
+ git "clone --mirror #{@remote} #{@cache_path}"
102
+ end
103
+ end
104
+
105
+ # Retrieve a list of cached branches for the git repo associated with this
106
+ # object.
107
+ #
108
+ # @return [Array<String>] A list of all cached remote branches
109
+ def branches
110
+ cache
111
+ output = git "--git-dir #{@cache_path} branch"
112
+ output.split("\n").map { |str| str[2..-1] }
113
+ end
114
+
115
+ private
116
+
117
+ # Perform a non-bare clone of a git repository.
118
+ #
119
+ # If a cachedir is available and the repo is already cached, it will be
120
+ # used as an object reference to speed up the clone.
121
+ #
122
+ # @param [String] path The directory to create the repo working directory
123
+ def clone(path)
124
+ if has_cache?
125
+ git "clone --reference #{@cache_path} #{@remote} #{path}"
126
+ else
127
+ FileUtils.mkdir_p path unless File.directory? path
128
+ git "clone #{@remote} #{path}"
129
+ end
130
+ end
131
+
132
+ def fetch(path)
133
+ if has_cache?
134
+ git "fetch --prune #{@cache_path}", path
135
+ else
136
+ git "fetch --prune", path
137
+ end
138
+ end
139
+
140
+ # Reset a git repo with a working directory to a specific ref
141
+ #
142
+ # @param [String] path The path to the working directory of the git repo
143
+ # @param [String] ref The git reference to reset to.
144
+ def reset(path, ref)
145
+
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
+ begin
151
+ commit = git "--git-dir #{@cache_path} rev-parse #{ref}^{commit}"
152
+ rescue RuntimeError => e
153
+ commit = "origin/#{ref}"
154
+ end
155
+
156
+ git "reset --hard #{commit}", path
157
+ end
158
+
159
+ # Wrap git commands
160
+ #
161
+ # @param [String] command_line_args The arguments for the git prompt
162
+ # @param [String] git_dir An optional git working directory
163
+ #
164
+ # @return [String] The git command output
165
+ def git(command_line_args, git_dir = nil)
166
+ args = []
167
+
168
+ if git_dir
169
+ args << "--work-tree" << git_dir
170
+ args << "--git-dir" << "#{git_dir}/.git"
171
+ end
172
+
173
+ args << command_line_args.split(/\s+/)
174
+
175
+ result = Shellter.run!('git', args.join(' '))
176
+ puts "Execute: #{result.last_command}".green
177
+
178
+ stderr = result.stderr.read
179
+ stdout = result.stdout.read
180
+
181
+ puts stdout.blue unless stdout.empty?
182
+ puts stderr.red unless stderr.empty?
183
+
184
+ stdout
185
+ end
186
+ end
@@ -0,0 +1,25 @@
1
+
2
+ module R10K; end
3
+ module R10K::Util; end
4
+
5
+ module R10K::Util::Interp
6
+
7
+ # Interpolate a string with a given scope
8
+ #
9
+ # @param [String] string
10
+ # @param [Hash] scope
11
+ #
12
+ # @return [String]
13
+ def interpolate_string(string, scope)
14
+
15
+ interp = string.clone
16
+
17
+ while (matchdata = interp.match /%\{.+?\}/)
18
+ var_name = matchdata[1].intern
19
+ var_data = scope[var_name]
20
+ interp.gsub!(/%\{#{var_name}\}/, var_data)
21
+ end
22
+
23
+ interp
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module R10K
2
+ VERSION = '0.0.1rc1'
3
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: r10k
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1rc1
5
+ prerelease: 5
6
+ platform: ruby
7
+ authors:
8
+ - adrien@somethingsinistral.net
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: cri
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.3.0
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: 2.3.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: shellter
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 0.9.6
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.9.6
46
+ - !ruby/object:Gem::Dependency
47
+ name: popen4
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 0.1.2
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 0.1.2
62
+ description: ! " R10K is an implementation of the Dynamic Puppet environments based
63
+ on git repositories\n as described in http://puppetlabs.com/blog/git-workflow-and-puppet-environments/.
64
+ It\n aggressively caches and tries to minimize network activity to ensure that
65
+ interactive\n deployment is as fast as possible.\n"
66
+ email:
67
+ executables:
68
+ - r10k
69
+ extensions: []
70
+ extra_rdoc_files: []
71
+ files:
72
+ - bin/r10k
73
+ - lib/r10k/cli/environment/cache.rb
74
+ - lib/r10k/cli/environment/deploy.rb
75
+ - lib/r10k/cli/environment/list.rb
76
+ - lib/r10k/cli/environment.rb
77
+ - lib/r10k/cli/module/deploy.rb
78
+ - lib/r10k/cli/module/list.rb
79
+ - lib/r10k/cli/module.rb
80
+ - lib/r10k/cli.rb
81
+ - lib/r10k/deployment.rb
82
+ - lib/r10k/environment_collection.rb
83
+ - lib/r10k/librarian/dsl.rb
84
+ - lib/r10k/librarian.rb
85
+ - lib/r10k/module/forge.rb
86
+ - lib/r10k/module/git.rb
87
+ - lib/r10k/module.rb
88
+ - lib/r10k/root.rb
89
+ - lib/r10k/synchro/git.rb
90
+ - lib/r10k/util/interp.rb
91
+ - lib/r10k/version.rb
92
+ - lib/r10k.rb
93
+ homepage: http://github.com/adrienthebo/r10k
94
+ licenses: []
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ! '>'
109
+ - !ruby/object:Gem::Version
110
+ version: 1.3.1
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 1.8.23
114
+ signing_key:
115
+ specification_version: 3
116
+ summary: Dynamic Puppet environments with Git
117
+ test_files: []