2021-10-18 20:10:29 -04:00
|
|
|
#!/usr/bin/env ruby
|
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
require 'yaml'
|
|
|
|
require 'optparse'
|
2022-05-15 23:07:37 -04:00
|
|
|
require 'pathname'
|
2021-10-18 20:10:29 -04:00
|
|
|
require_relative 'api/default_options'
|
|
|
|
|
|
|
|
# This script returns the desired feature flag state as a comma-separated string for the feature flags in the specified files.
|
2022-05-15 23:07:37 -04:00
|
|
|
# Each desired feature flag state is specified as 'feature-flag=state'. This allows us to run package-and-qa with the
|
|
|
|
# feature flag set to the desired state.
|
2021-10-18 20:10:29 -04:00
|
|
|
#
|
2022-07-28 14:09:03 -04:00
|
|
|
# For example, if the specified files included `config/feature_flags/development/ci_awesome_feature.yml` and the desired
|
|
|
|
# state as specified by the second argument was enabled, the value returned would be `ci_awesome_feature=enabled`
|
2021-10-18 20:10:29 -04:00
|
|
|
|
|
|
|
class GetFeatureFlagsFromFiles
|
|
|
|
def initialize(options)
|
|
|
|
@files = options.delete(:files)
|
|
|
|
@state = options.delete(:state)
|
2022-05-15 23:07:37 -04:00
|
|
|
|
|
|
|
abort("ERROR: Please specify the directory containing MR diffs.") if @files.to_s.empty?
|
2021-10-18 20:10:29 -04:00
|
|
|
end
|
|
|
|
|
2022-05-15 23:07:37 -04:00
|
|
|
# Gets feature flags from definition files or diffs of deleted defition files
|
|
|
|
#
|
|
|
|
# @return [String] a comma-separated list of feature flags and their desired state
|
2021-10-18 20:10:29 -04:00
|
|
|
def extracted_flags
|
2022-05-15 23:07:37 -04:00
|
|
|
flags_list = diffs_dir.glob('**/*').each_with_object([]) do |file_path, flags|
|
|
|
|
ff_yaml = ff_yaml_for_file(file_path)
|
|
|
|
next if ff_yaml.nil?
|
|
|
|
break [] if ff_yaml.empty?
|
|
|
|
|
|
|
|
flags << ff_yaml['name']
|
|
|
|
end
|
|
|
|
flags_list = flags_list.map { |flag| "#{flag}=#{state}" } unless state.to_s.empty?
|
|
|
|
flags_list.join(',')
|
|
|
|
end
|
2021-10-18 20:10:29 -04:00
|
|
|
|
2022-05-15 23:07:37 -04:00
|
|
|
# Loads the YAML feature flag definition based on a diff of the definition file. The definition is loaded from the
|
|
|
|
# definition file itself, or from a diff of the deleted definition file.
|
|
|
|
#
|
|
|
|
# @param [Pathname] path the path to the diff
|
|
|
|
# @return [Hash] a hash containing the YAML data for the feature flag definition
|
|
|
|
def ff_yaml_for_file(path)
|
|
|
|
return unless File.expand_path(path).to_s =~ %r{/feature_flags/(development|ops)/.*\.yml}
|
2021-10-18 20:10:29 -04:00
|
|
|
|
2022-05-15 23:07:37 -04:00
|
|
|
if path.to_s.end_with?('yml.deleted.diff')
|
|
|
|
# Ignore deleted feature flag definitions if we want to enable/disable existing flags.
|
|
|
|
return if state != 'deleted'
|
|
|
|
|
|
|
|
yaml_from_deleted_diff(path)
|
|
|
|
else
|
|
|
|
# If we want deleted definition files but find one that wasn't deleted, we return immediately to
|
|
|
|
# because non-deleted flags are tested in separate jobs from deleted flags, so we don't need to run
|
|
|
|
# a job with just deleted flags.
|
|
|
|
return [] if state == 'deleted'
|
|
|
|
|
|
|
|
yaml_from_file(path, diffs_dir)
|
|
|
|
end
|
2021-10-18 20:10:29 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
attr_reader :files, :state
|
2022-05-15 23:07:37 -04:00
|
|
|
|
|
|
|
# The absolute path to the directory of diffs
|
|
|
|
#
|
|
|
|
# @return [String]
|
|
|
|
def diffs_dir
|
|
|
|
@diffs_dir ||= Pathname.new(files).expand_path
|
|
|
|
end
|
|
|
|
|
|
|
|
# Loads the YAML feature flag definition from a file corresponding to a diff of the definition file.
|
|
|
|
#
|
|
|
|
# @param [Pathname] file_path the path to the diff
|
|
|
|
# @param [Pathname] diffs_dir the path to the diffs directory
|
|
|
|
# @return [Hash] a hash containing the YAML data from the feature flag definition file corresponding to the diff
|
|
|
|
def yaml_from_file(file_path, diffs_dir)
|
|
|
|
real_file_path = File.join(Dir.pwd, file_path.to_s.delete_prefix(diffs_dir.to_s)).delete_suffix('.diff')
|
|
|
|
YAML.safe_load(File.read(real_file_path))
|
|
|
|
end
|
|
|
|
|
|
|
|
# Loads the YAML feature flag definition from a diff of the deleted feature flag definition file.
|
|
|
|
#
|
|
|
|
# @param [Pathname] file_path the path of the diff
|
|
|
|
# @return [Hash] a hash containing the YAML data for the feature flag definition from the diff
|
|
|
|
def yaml_from_deleted_diff(file_path)
|
|
|
|
cleaned_diff = File.read(file_path).gsub(/^[^a-z]+/, '')
|
|
|
|
YAML.safe_load(cleaned_diff)
|
|
|
|
end
|
2021-10-18 20:10:29 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
if $0 == __FILE__
|
|
|
|
options = API::DEFAULT_OPTIONS.dup
|
|
|
|
|
|
|
|
OptionParser.new do |opts|
|
2022-05-15 23:07:37 -04:00
|
|
|
opts.on("-f", "--files FILES", String, "A directory containing diffs including feature flag definition change diffs") do |value|
|
2021-10-18 20:10:29 -04:00
|
|
|
options[:files] = value
|
|
|
|
end
|
|
|
|
|
|
|
|
opts.on("-s", "--state STATE", String,
|
|
|
|
"The desired state of the feature flags (enabled or disabled). If not specified the output will only list the feature flags."
|
|
|
|
) do |value|
|
|
|
|
options[:state] = value
|
|
|
|
end
|
|
|
|
|
|
|
|
opts.on("-h", "--help", "Prints this help") do
|
|
|
|
puts opts
|
|
|
|
exit
|
|
|
|
end
|
|
|
|
end.parse!
|
|
|
|
|
|
|
|
puts GetFeatureFlagsFromFiles.new(options).extracted_flags
|
|
|
|
end
|