diff --git a/CHANGELOG.mkd b/CHANGELOG.mkd index c3eb6eb26..06cac8f28 100644 --- a/CHANGELOG.mkd +++ b/CHANGELOG.mkd @@ -4,6 +4,8 @@ CHANGELOG Unreleased ---------- +- Add 'strip\_component' environment source configuration setting, to allow deploying Git branches named like "env/production" as Puppet environments named like "production". [#1128](https://github.com/puppetlabs/r10k/pull/1128) + 3.8.0 ----- diff --git a/doc/dynamic-environments/configuration.mkd b/doc/dynamic-environments/configuration.mkd index b25c94bed..25bbfe0a4 100644 --- a/doc/dynamic-environments/configuration.mkd +++ b/doc/dynamic-environments/configuration.mkd @@ -394,6 +394,23 @@ sources: * if `false` (default) environment folder will not be prefixed * if `String` environment folder will be prefixed with the `prefix` value. +### strip\_component + +The 'strip\_component' setting allows parts of environment names from a source to have a transformation applied, removing a part of the name before turning them into Puppet environments. This is primarily useful for VCS sources (e.g. Git), because it allows branch names to use prefixes or organizing name components such as "env/production", "env/development", but deploy Puppet environments from these branches named without the leading "env/" component. E.g. "production", "development". + +```yaml +--- +sources: + mysource: + basedir: '/etc/puppet/environments' + strip_component: 'env/' +``` + +#### strip\_component behaviour + +* if `string` environment names will have this prefix removed, if the prefix is present. Note that when string values are used, names can only have prefix components removed. +* if `/regex/` the regex will be matched against environment names and if a match is found, the matching name component will be removed. + ### ignore_branch_prefixes The 'ignore_branch_prefixes' setting causes environments to be ignored which match in part or whole diff --git a/lib/r10k/environment/name.rb b/lib/r10k/environment/name.rb index 029e623ab..a7cba4cd0 100644 --- a/lib/r10k/environment/name.rb +++ b/lib/r10k/environment/name.rb @@ -12,13 +12,13 @@ class Name INVALID_CHARACTERS = %r[\W] def initialize(name, opts) - @name = name - @opts = opts - @source = opts[:source] @prefix = opts[:prefix] @invalid = opts[:invalid] + @name = derive_name(name, opts[:strip_component]) + @opts = opts + case @invalid when 'correct_and_warn' @validate = true @@ -71,8 +71,26 @@ def dirname private - def derive_prefix(source,prefix) + def derive_name(name, strip_component) + return name unless strip_component + + unless strip_component.is_a?(String) + raise _('Improper configuration value given for strip_component setting in %{src} source. ' \ + 'Value must be a string, a /regex/, false, or omitted. Got "%{val}" (%{type})' \ + % {src: @source, val: strip_component, type: strip_component.class}) + end + if %r{^/.*/$}.match(strip_component) + regex = Regexp.new(strip_component[1..-2]) + name.gsub(regex, '') + elsif name.start_with?(strip_component) + name[strip_component.size..-1] + else + name + end + end + + def derive_prefix(source,prefix) if prefix == true "#{source}_" elsif prefix.is_a? String diff --git a/lib/r10k/source/base.rb b/lib/r10k/source/base.rb index 8882192b2..818180931 100644 --- a/lib/r10k/source/base.rb +++ b/lib/r10k/source/base.rb @@ -31,10 +31,15 @@ class R10K::Source::Base # @option options [Boolean, String] :prefix If a String this becomes the prefix. # If true, will use the source name as the prefix. All sources should respect this option. # Defaults to false for no environment prefix. + # @option options [String] :strip_component If a string, this value will be + # removed from the beginning of each generated environment's name, if + # present. If the string is contained within two "/" characters, it will + # be treated as a regular expression. def initialize(name, basedir, options = {}) @name = name @basedir = Pathname.new(basedir).cleanpath.to_s @prefix = options.delete(:prefix) + @strip_component = options.delete(:strip_component) @puppetfile_name = options.delete(:puppetfile_name) @options = options end diff --git a/lib/r10k/source/git.rb b/lib/r10k/source/git.rb index adbdf17d8..082624329 100644 --- a/lib/r10k/source/git.rb +++ b/lib/r10k/source/git.rb @@ -145,7 +145,10 @@ def filter_branches_by_command(branches, command) private def branch_names - opts = {:prefix => @prefix, :invalid => @invalid_branches, :source => @name} + opts = {prefix: @prefix, + invalid: @invalid_branches, + source: @name, + strip_component: @strip_component} branches = @cache.branches if @ignore_branch_prefixes && !@ignore_branch_prefixes.empty? branches = filter_branches_by_regexp(branches, @ignore_branch_prefixes) diff --git a/lib/r10k/source/hash.rb b/lib/r10k/source/hash.rb index 1f3ed7d16..62df43204 100644 --- a/lib/r10k/source/hash.rb +++ b/lib/r10k/source/hash.rb @@ -152,8 +152,10 @@ def set_environments_hash(hash) R10K::Util::SymbolizeKeys.symbolize_keys!(opts) memo.merge({ name => opts.merge({ - :basedir => @basedir, - :dirname => R10K::Environment::Name.new(name, {prefix: @prefix, source: @name}).dirname + basedir: @basedir, + dirname: R10K::Environment::Name.new(name, {prefix: @prefix, + source: @name, + strip_component: @strip_component}).dirname }) }) end diff --git a/lib/r10k/source/svn.rb b/lib/r10k/source/svn.rb index bd46cc533..4dfd8765e 100644 --- a/lib/r10k/source/svn.rb +++ b/lib/r10k/source/svn.rb @@ -121,7 +121,11 @@ def filter_branches(branches, ignore_prefixes) def names_and_paths branches = [] - opts = {:prefix => @prefix, :correct => false, :validate => false, :source => @name} + opts = {prefix: @prefix, + correct: false, + validate: false, + source: @name, + strip_component: @strip_component} branches << [R10K::Environment::Name.new('production', opts), "#{@remote}/trunk"] additional_branch_names = @svn_remote.branches if @ignore_branch_prefixes && !@ignore_branch_prefixes.empty? diff --git a/spec/unit/environment/name_spec.rb b/spec/unit/environment/name_spec.rb index e38f9ecf0..d335127e4 100644 --- a/spec/unit/environment/name_spec.rb +++ b/spec/unit/environment/name_spec.rb @@ -2,6 +2,34 @@ require 'r10k/environment/name' describe R10K::Environment::Name do + describe "strip_component" do + it "does not modify the given name when no strip_component is given" do + bn = described_class.new('myenv', source: 'source', prefix: false) + expect(bn.dirname).to eq 'myenv' + end + + it "removes the first occurance of a regex match when a regex is given" do + bn = described_class.new('myenv', source: 'source', prefix: false, strip_component: '/env/') + expect(bn.dirname).to eq 'my' + end + + it "does not modify the given name when there is no regex match" do + bn = described_class.new('myenv', source: 'source', prefix: false, strip_component: '/bar/') + expect(bn.dirname).to eq 'myenv' + end + + it "removes the given name's prefix when it matches strip_component" do + bn = described_class.new('env/prod', source: 'source', prefix: false, strip_component: 'env/') + expect(bn.dirname).to eq 'prod' + end + + it "raises an error when given an integer" do + expect { + described_class.new('env/prod', source: 'source', prefix: false, strip_component: 4) + }.to raise_error(%r{Improper.*"4"}) + end + end + describe "prefixing" do it "uses the branch name as the dirname when prefixing is off" do bn = described_class.new('mybranch', :source => 'source', :prefix => false)