#!/usr/bin/env ruby # frozen_string_literal: true module CI class ChangedFiles FRONTEND_EXTENSIONS = "js|cjs|mjs|vue|graphql" FRONTEND_FILES_FILTER = /\.(#{FRONTEND_EXTENSIONS})$/ ESLINT_TODO_FILES = %r{\.eslint_todo/[a-z-]+\.mjs$} ESLINT_TODO_REMOVED_FILE_PATHS = %r{- +'([a-z/_-]+\.(#{FRONTEND_EXTENSIONS}))'} def initialize(env: ENV, args: ARGV) @env = env @args = args end private attr_reader :env, :args # See: https://gitlab.com/groups/gitlab-org/-/epics/16845#note_2370956250 # for why we use `HEAD~` to compare def get_changed_files_in_merged_results_pipeline `git diff --name-only --diff-filter=d HEAD~..HEAD`.split("\n") end def get_files_removed_from_todo_files(changed_files) changed_todo_files = changed_files.filter { |file| file =~ ESLINT_TODO_FILES } return [] if changed_todo_files.empty? diff = `git diff HEAD~..HEAD -- #{changed_todo_files.join(' ')}` diff.scan(ESLINT_TODO_REMOVED_FILE_PATHS).map(&:first) end def filter_and_get_changed_files_in_mr(filter_pattern: //) get_changed_files_in_merged_results_pipeline.grep(filter_pattern) end def run_eslint_for_changed_files puts 'Running ESLint for changed files...' files = filter_and_get_changed_files_in_mr(filter_pattern: FRONTEND_FILES_FILTER) files += get_files_removed_from_todo_files(files) if files.empty? puts 'No files were changed. Skipping...' return 0 end command = [ "yarn", "run", "lint:eslint", "--no-warn-ignored", "--no-error-on-unmatched-pattern", "--format", "gitlab", *files ] system(*command) last_command_status.exitstatus end def last_command_status $? end def process_command_and_determine_exit_status return 0 if args.empty? command = args.first case command when "eslint" run_eslint_for_changed_files else warn "Unknown command: #{command}" 1 end end end end if __FILE__ == $PROGRAM_NAME runner = CI::ChangedFiles.new exit runner.process_command_and_determine_exit_status end