Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* [BUGFIX] Work around issue preventing feature execution on Ruby 3.5.0dev (by [@faisal][])
* [CHANGE] Add changes or suppress warnings for issues found by newer rubocop (by [@faisal][])
* [CHANGE] Update CI checkout action to v4 (by [@faisal][])
* [CHANGE] Run RubyCritic outside of the project without losing the churn value (by [@juanvqz][])

# v4.9.2 / 2025-04-08 [(commits)](https://github.com/whitesmith/rubycritic/compare/v4.9.1...v4.9.2)

Expand Down
21 changes: 20 additions & 1 deletion lib/rubycritic/source_control_systems/git.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,27 @@ def git(arg)
self.class.git(arg)
end

# :reek:DuplicateMethodCall
# :reek:NilCheck
def self.supported?
git('branch 2>&1') && $CHILD_STATUS.success?
Copy link
Contributor Author

@JuanVqz JuanVqz Jul 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JFI: This was the main reason we could not run the RubyCritic command outside of the project; the Git class checks for the existence of git in the current path (pwd), and if it exists, it returns a supported? value as true.

return true if git('branch 2>&1') && $CHILD_STATUS.success?

return false if Config.paths.nil? || Config.paths.empty?

Config.paths.any? do |path|
absolute_path = File.expand_path(path)
check_git_repository?(absolute_path)
end
end

# :reek:DuplicateMethodCall
def self.check_git_repository?(path)
current_path = File.expand_path(path)
while current_path != File.dirname(current_path)
return true if Dir.exist?(File.join(current_path, '.git'))
current_path = File.dirname(current_path)
end
false
end

def self.to_s
Expand Down
79 changes: 68 additions & 11 deletions lib/rubycritic/source_control_systems/git/churn.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@ def current(name)
end
end

# :reek:TooManyInstanceVariables
class Churn
# :reek:TooManyStatements
def initialize(churn_after: nil, paths: ['.'])
@churn_after = churn_after
@paths = Array(paths)
@date = nil
@stats = {}
@git_root = find_git_root

call
end
Expand All @@ -40,24 +43,54 @@ def date_of_last_commit(path)

private

# :reek:DuplicateMethodCall
def find_git_root
@paths.each do |path|
current_path = File.expand_path(path)
while current_path != File.dirname(current_path)
if Dir.exist?(File.join(current_path, '.git'))
return current_path
end
current_path = File.dirname(current_path)
end
end
Dir.pwd
end

def call
git_log_commands.each { |log_command| exec_git_command(log_command) }
end

def exec_git_command(command)
Git
.git(command)
.split("\n")
.reject(&:empty?)
.each { |line| process_line(line) }
# Run git command from the git repository root
Dir.chdir(@git_root) do
Git
.git(command)
.split("\n")
.reject(&:empty?)
.each { |line| process_line(line) }
end
end

def git_log_commands
@paths.map { |path| git_log_command(path) }
end

def git_log_command(path)
"log --all --date=iso --follow --format='format:date:%x09%ad' --name-status #{after_clause}#{path}"
# Convert absolute paths to relative paths from git root
relative_path = make_relative_to_git_root(path)
"log --all --date=iso --follow --format='format:date:%x09%ad' --name-status #{after_clause}#{relative_path}"
end

def make_relative_to_git_root(path)
absolute_path = File.expand_path(path)
if absolute_path.start_with?(@git_root)
# Convert to relative path from git root
absolute_path[@git_root.length + 1..-1] || '.'
else
# If path is not within git root, use as is
path
end
end

def after_clause
Expand Down Expand Up @@ -87,13 +120,18 @@ def process_rename(from, to)
process_file(to)
end

# :reek:DuplicateMethodCall
def filename_for_subdirectory(filename)
git_path = Git.git('rev-parse --show-toplevel')
cd_path = Dir.pwd
if cd_path.length > git_path.length
filename = filename.sub(/^#{Regexp.escape("#{File.basename(cd_path)}/")}/, '')
if @git_root != Dir.pwd
filename
else
git_path = Git.git('rev-parse --show-toplevel')
cd_path = Dir.pwd
if cd_path.length > git_path.length
filename = filename.sub(/^#{Regexp.escape("#{File.basename(cd_path)}/")}/, '')
end
[filename]
end
[filename]
end

def process_file(filename)
Expand All @@ -109,7 +147,26 @@ def renames
@renames ||= Renames.new
end

# :reek:TooManyStatements
def stats(path)
# Try the path as-is first
result = @stats.fetch(path, nil)
return result if result

# If not found, try converting absolute path to relative path from git root
absolute_path = File.expand_path(path)
if absolute_path.start_with?(@git_root)
relative_path = absolute_path[@git_root.length + 1..-1]
return @stats.fetch(relative_path, Stats.new(0))
end

# If still not found, try converting relative path to absolute path
if !path.start_with?('/')
absolute_path = File.expand_path(path, @git_root)
return @stats.fetch(absolute_path, Stats.new(0))
end

# Default fallback
@stats.fetch(path, Stats.new(0))
end
end
Expand Down
Loading