require 'r10k/git' require 'r10k/environment' require 'r10k/environment/name' # This class implements a source for Git environments. # # A Git source generates environments by locally caching the given Git # repository and enumerating the branches for the Git repository. Branches # are mapped to environments without modification. # # @since 1.3.0 class R10K::Source::Git < R10K::Source::Base R10K::Source.register(:git, self) # Register git as the default source R10K::Source.register(nil, self) # @!attribute [r] remote # @return [String] The URL to the remote git repository attr_reader :remote # @!attribute [r] cache # @api private # @return [R10K::Git::Cache] The git cache associated with this source attr_reader :cache # @!attribute [r] settings # @return [Hash<Symbol, Object>] Additional settings that configure how # the source should behave. attr_reader :settings # @!attribute [r] invalid_branches # @return [String] How Git branch names that cannot be cleanly mapped to # Puppet environments will be handled. attr_reader :invalid_branches # @!attribute [r] ignore_branch_prefixes # @return [Array<String>] Array of strings used to remove repository branches # that will be deployed as environments. attr_reader :ignore_branch_prefixes # @!attribute [r] filter_command # @return [String] Command to run to filter branches attr_reader :filter_command # Initialize the given source. # # @param name [String] The identifier for this source. # @param basedir [String] The base directory where the generated environments will be created. # @param options [Hash] An additional set of options for this source. # # @option options [Boolean, String] :prefix If a String this becomes the prefix. # If true, will use the source name as the prefix. # Defaults to false for no environment prefix. # @option options [String] :remote The URL to the base directory of the SVN repository # @option options [Hash] :remote Additional settings that configure how the # source should behave. def initialize(name, basedir, options = {}) super @environments = [] @remote = options[:remote] @invalid_branches = (options[:invalid_branches] || 'correct_and_warn') @ignore_branch_prefixes = options[:ignore_branch_prefixes] @filter_command = options[:filter_command] @cache = R10K::Git.cache.generate(@remote) end # Update the git cache for this git source to get the latest list of environments. # # @return [void] def preload! logger.debug _("Fetching '%{remote}' to determine current branches.") % {remote: @remote} @cache.sync rescue => e raise R10K::Error.wrap(e, _("Unable to determine current branches for Git source '%{name}' (%{basedir})") % {name: @name, basedir: @basedir}) end alias fetch_remote preload! # Load the git remote and create environments for each branch. If the cache # has not been fetched, this will return an empty list. # # @return [Array<R10K::Environment::Git>] def environments if not @cache.cached? [] elsif @environments.empty? @environments = generate_environments() else @environments end end def generate_environments envs = [] branch_names.each do |bn| if bn.valid? envs << R10K::Environment::Git.new(bn.name, @basedir, bn.dirname, {remote: remote, ref: bn.name, puppetfile_name: puppetfile_name, overrides: @options[:overrides]}) elsif bn.correct? logger.warn _("Environment %{env_name} contained non-word characters, correcting name to %{corrected_env_name}") % {env_name: bn.name.inspect, corrected_env_name: bn.dirname} envs << R10K::Environment::Git.new(bn.name, @basedir, bn.dirname, {remote: remote, ref: bn.name, puppetfile_name: puppetfile_name, overrides: @options[:overrides]}) elsif bn.validate? logger.error _("Environment %{env_name} contained non-word characters, ignoring it.") % {env_name: bn.name.inspect} end end envs end # List all environments that should exist in the basedir for this source # @note This is required by {R10K::Util::Basedir} # @return [Array<String>] def desired_contents environments.map {|env| env.dirname } end def filter_branches_by_regexp(branches, ignore_prefixes) filter = Regexp.new("^#{Regexp.union(ignore_prefixes)}") branches = branches.reject do |branch| result = filter.match(branch) if result logger.warn _("Branch %{branch} filtered out by ignore_branch_prefixes %{ibp}") % {branch: branch, ibp: @ignore_branch_prefixes} end result end branches end def filter_branches_by_command(branches, command) branches.select do |branch| result = system({'GIT_DIR' => @cache.git_dir.to_s, 'R10K_BRANCH' => branch, 'R10K_NAME' => @name.to_s}, command) unless result logger.warn _("Branch `%{name}:%{branch}` filtered out by filter_command %{cmd}") % {name: @name, branch: branch, cmd: command} end result end end private def branch_names 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) end if @filter_command && !@filter_command.empty? branches = filter_branches_by_command(branches, @filter_command) end branches.map do |branch| R10K::Environment::Name.new(branch, opts) end end end