2021-01-03 14:25:43 +05:30
|
|
|
#!/usr/bin/env ruby
|
2021-03-11 19:13:27 +05:30
|
|
|
# frozen_string_literal: true
|
2021-01-03 14:25:43 +05:30
|
|
|
|
2021-02-22 17:27:13 +05:30
|
|
|
require 'set'
|
2021-11-11 11:23:49 +05:30
|
|
|
require 'fileutils'
|
2021-02-22 17:27:13 +05:30
|
|
|
|
2021-01-03 14:25:43 +05:30
|
|
|
class String
|
|
|
|
def red
|
|
|
|
"\e[31m#{self}\e[0m"
|
|
|
|
end
|
|
|
|
|
|
|
|
def yellow
|
|
|
|
"\e[33m#{self}\e[0m"
|
|
|
|
end
|
|
|
|
|
|
|
|
def green
|
|
|
|
"\e[32m#{self}\e[0m"
|
|
|
|
end
|
|
|
|
|
|
|
|
def bold
|
|
|
|
"\e[1m#{self}\e[0m"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
flags_paths = [
|
|
|
|
'config/feature_flags/**/*.yml'
|
|
|
|
]
|
|
|
|
|
|
|
|
# For EE additionally process `ee/` feature flags
|
2021-12-11 22:18:48 +05:30
|
|
|
is_ee = File.exist?('ee/app/models/license.rb') && !%w[true 1].include?(ENV['FOSS_ONLY'].to_s)
|
|
|
|
if is_ee
|
2021-01-03 14:25:43 +05:30
|
|
|
flags_paths << 'ee/config/feature_flags/**/*.yml'
|
2021-09-30 23:02:18 +05:30
|
|
|
|
|
|
|
# Geo feature flags are constructed dynamically and there's no explicit checks in the codebase so we mark all
|
|
|
|
# the replicators' derived feature flags as used.
|
|
|
|
# See https://gitlab.com/gitlab-org/gitlab/-/blob/54e802e8fe76b6f93656d75ef9b566bf57b60f41/ee/lib/gitlab/geo/replicator.rb#L183-185
|
|
|
|
Dir.glob('ee/app/replicators/geo/*_replicator.rb').each_with_object(Set.new) do |path, memo|
|
|
|
|
replicator_name = File.basename(path, '.rb')
|
|
|
|
feature_flag_name = "geo_#{replicator_name.delete_suffix('_replicator')}_replication"
|
|
|
|
|
|
|
|
FileUtils.touch(File.join('tmp', 'feature_flags', "#{feature_flag_name}.used"))
|
|
|
|
end
|
2021-01-03 14:25:43 +05:30
|
|
|
end
|
|
|
|
|
2021-12-11 22:18:48 +05:30
|
|
|
# For JH additionally process `jh/` feature flags
|
|
|
|
is_jh = is_ee && Dir.exist?('jh') && !%w[true 1].include?(ENV['EE_ONLY'].to_s)
|
|
|
|
if is_jh
|
|
|
|
flags_paths << 'jh/config/feature_flags/**/*.yml'
|
|
|
|
|
|
|
|
Dir.glob('jh/app/replicators/geo/*_replicator.rb').each_with_object(Set.new) do |path, memo|
|
|
|
|
replicator_name = File.basename(path, '.rb')
|
|
|
|
feature_flag_name = "geo_#{replicator_name.delete_suffix('_replicator')}_replication"
|
|
|
|
|
|
|
|
FileUtils.touch(File.join('tmp', 'feature_flags', "#{feature_flag_name}.used"))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-01-03 14:25:43 +05:30
|
|
|
all_flags = {}
|
|
|
|
additional_flags = Set.new
|
|
|
|
|
|
|
|
# Iterate all defined feature flags
|
|
|
|
# to discover which were used
|
|
|
|
flags_paths.each do |flags_path|
|
|
|
|
puts flags_path
|
|
|
|
Dir.glob(flags_path).each do |path|
|
|
|
|
feature_flag_name = File.basename(path, '.yml')
|
|
|
|
|
2021-02-22 17:27:13 +05:30
|
|
|
# TODO: we need a better way of tracking use of Gitaly FF across Gitaly and GitLab
|
2021-09-30 23:02:18 +05:30
|
|
|
if feature_flag_name.start_with?('gitaly_')
|
|
|
|
puts "Skipping the #{feature_flag_name} feature flag since it starts with 'gitaly_'."
|
|
|
|
next
|
|
|
|
end
|
|
|
|
|
|
|
|
# Dynamic feature flag names for redirect to latest CI templates
|
|
|
|
# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63144/diffs#fa2193ace3f6a02f7ef9995ef9bc519eca92c4ee_57_84
|
|
|
|
if feature_flag_name.start_with?('redirect_to_latest_template_')
|
|
|
|
puts "Skipping the #{feature_flag_name} feature flag since it starts with 'redirect_to_latest_template_'."
|
|
|
|
next
|
|
|
|
end
|
2021-02-22 17:27:13 +05:30
|
|
|
|
2021-01-03 14:25:43 +05:30
|
|
|
all_flags[feature_flag_name] = File.exist?(File.join('tmp', 'feature_flags', feature_flag_name + '.used'))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Iterate all used feature flags
|
|
|
|
# to discover which flags are undefined
|
|
|
|
Dir.glob('tmp/feature_flags/*.used').each do |path|
|
|
|
|
feature_flag_name = File.basename(path, '.used')
|
|
|
|
|
|
|
|
additional_flags.add(feature_flag_name) unless all_flags[feature_flag_name]
|
|
|
|
end
|
|
|
|
|
|
|
|
used_flags = all_flags.select { |name, used| used }
|
|
|
|
unused_flags = all_flags.reject { |name, used| used }
|
|
|
|
|
|
|
|
puts "=========================================".green.bold
|
|
|
|
puts "Feature Flags usage summary:".green.bold
|
|
|
|
puts
|
|
|
|
|
|
|
|
puts "- #{all_flags.count + additional_flags.count} was found"
|
|
|
|
puts "- #{unused_flags.count} appear(s) to be UNUSED".yellow
|
|
|
|
puts "- #{additional_flags.count} appear(s) to be unknown".yellow
|
|
|
|
puts "- #{used_flags.count} appear(s) to be used".green
|
|
|
|
puts
|
|
|
|
|
|
|
|
if additional_flags.count > 0
|
|
|
|
puts "==================================================".green.bold
|
|
|
|
puts "There are feature flags that appears to be unknown".yellow
|
|
|
|
puts
|
|
|
|
puts "They appear to be used by CI, but we do lack their YAML definition".yellow
|
|
|
|
puts "This is likely expected, so feel free to ignore that list:".yellow
|
|
|
|
puts
|
|
|
|
additional_flags.sort.each do |name|
|
|
|
|
puts "- #{name}".yellow
|
|
|
|
end
|
|
|
|
puts
|
|
|
|
end
|
|
|
|
|
|
|
|
if unused_flags.count > 0
|
|
|
|
puts "========================================".green.bold
|
|
|
|
puts "These feature flags appears to be UNUSED".red.bold
|
|
|
|
puts
|
|
|
|
puts "If they are really no longer needed REMOVE their .yml definition".red
|
|
|
|
puts "If they are needed you need to ENSURE that their usage is covered with specs to continue.".red
|
|
|
|
puts
|
|
|
|
unused_flags.keys.sort.each do |name|
|
|
|
|
puts "- #{name}".yellow
|
|
|
|
end
|
|
|
|
puts
|
|
|
|
puts "Feature flag usage check failed.".red.bold
|
|
|
|
exit(1)
|
|
|
|
end
|
|
|
|
|
|
|
|
puts "Everything is fine here!".green
|
|
|
|
puts
|