Skip to content

Commit 358f600

Browse files
authored
Allow run RubyCritic outside of project (#527)
* Allow run RubyCritic outside of project * Fix linter
1 parent 27f305f commit 358f600

File tree

4 files changed

+103
-22
lines changed

4 files changed

+103
-22
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* [BUGFIX] Work around issue preventing feature execution on Ruby 3.5.0dev (by [@faisal][])
55
* [CHANGE] Add changes or suppress warnings for issues found by newer rubocop (by [@faisal][])
66
* [CHANGE] Update CI checkout action to v4 (by [@faisal][])
7+
* [CHANGE] Run RubyCritic outside of the project without losing the churn value (by [@juanvqz][])
78

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

lib/rubycritic/cli/options/file.rb

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ def to_h
2424
root: root,
2525
coverage_path: coverage_path,
2626
formats: formats,
27-
deduplicate_symlinks: deduplicate_symlinks,
27+
deduplicate_symlinks: deduplicate_symlinks?,
2828
paths: paths,
29-
suppress_ratings: suppress_ratings,
29+
suppress_ratings: suppress_ratings?,
3030
minimum_score: minimum_score,
31-
no_browser: no_browser,
31+
no_browser: no_browser?,
3232
base_branch: base_branch,
3333
feature_branch: feature_branch,
3434
threshold_score: threshold_score
@@ -68,16 +68,16 @@ def threshold_score
6868
options['threshold_score']
6969
end
7070

71-
def deduplicate_symlinks
72-
value_for(options['deduplicate_symlinks'])
71+
def deduplicate_symlinks?
72+
value_for?(options['deduplicate_symlinks'])
7373
end
7474

75-
def suppress_ratings
76-
value_for(options['suppress_ratings'])
75+
def suppress_ratings?
76+
value_for?(options['suppress_ratings'])
7777
end
7878

79-
def no_browser
80-
value_for(options['no_browser'])
79+
def no_browser?
80+
value_for?(options['no_browser'])
8181
end
8282

8383
def formats
@@ -96,7 +96,7 @@ def paths
9696
options['paths']
9797
end
9898

99-
def value_for(value)
99+
def value_for?(value)
100100
value = value.to_s
101101
value == 'true' unless value.empty?
102102
end

lib/rubycritic/source_control_systems/git.rb

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,28 @@ def git(arg)
2121
self.class.git(arg)
2222
end
2323

24+
# :reek:DuplicateMethodCall
25+
# :reek:NilCheck
2426
def self.supported?
25-
git('branch 2>&1') && $CHILD_STATUS.success?
27+
return true if git('branch 2>&1') && $CHILD_STATUS.success?
28+
29+
return false if Config.paths.nil? || Config.paths.empty?
30+
31+
Config.paths.any? do |path|
32+
absolute_path = File.expand_path(path)
33+
check_git_repository?(absolute_path)
34+
end
35+
end
36+
37+
# :reek:DuplicateMethodCall
38+
def self.check_git_repository?(path)
39+
current_path = File.expand_path(path)
40+
while current_path != File.dirname(current_path)
41+
return true if Dir.exist?(File.join(current_path, '.git'))
42+
43+
current_path = File.dirname(current_path)
44+
end
45+
false
2646
end
2747

2848
def self.to_s

lib/rubycritic/source_control_systems/git/churn.rb

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,16 @@ def current(name)
2020
end
2121
end
2222

23+
# :reek:TooManyInstanceVariables
24+
# rubocop:disable Metrics/ClassLength
2325
class Churn
26+
# :reek:TooManyStatements
2427
def initialize(churn_after: nil, paths: ['.'])
2528
@churn_after = churn_after
2629
@paths = Array(paths)
2730
@date = nil
2831
@stats = {}
32+
@git_root = find_git_root
2933

3034
call
3135
end
@@ -40,24 +44,53 @@ def date_of_last_commit(path)
4044

4145
private
4246

47+
# :reek:DuplicateMethodCall
48+
def find_git_root
49+
@paths.each do |path|
50+
current_path = File.expand_path(path)
51+
while current_path != File.dirname(current_path)
52+
return current_path if Dir.exist?(File.join(current_path, '.git'))
53+
54+
current_path = File.dirname(current_path)
55+
end
56+
end
57+
Dir.pwd
58+
end
59+
4360
def call
4461
git_log_commands.each { |log_command| exec_git_command(log_command) }
4562
end
4663

4764
def exec_git_command(command)
48-
Git
49-
.git(command)
50-
.split("\n")
51-
.reject(&:empty?)
52-
.each { |line| process_line(line) }
65+
# Run git command from the git repository root
66+
Dir.chdir(@git_root) do
67+
Git
68+
.git(command)
69+
.split("\n")
70+
.reject(&:empty?)
71+
.each { |line| process_line(line) }
72+
end
5373
end
5474

5575
def git_log_commands
5676
@paths.map { |path| git_log_command(path) }
5777
end
5878

5979
def git_log_command(path)
60-
"log --all --date=iso --follow --format='format:date:%x09%ad' --name-status #{after_clause}#{path}"
80+
# Convert absolute paths to relative paths from git root
81+
relative_path = make_relative_to_git_root(path)
82+
"log --all --date=iso --follow --format='format:date:%x09%ad' --name-status #{after_clause}#{relative_path}"
83+
end
84+
85+
def make_relative_to_git_root(path)
86+
absolute_path = File.expand_path(path)
87+
if absolute_path.start_with?(@git_root)
88+
# Convert to relative path from git root
89+
absolute_path[(@git_root.length + 1)..] || '.'
90+
else
91+
# If path is not within git root, use as is
92+
path
93+
end
6194
end
6295

6396
def after_clause
@@ -87,13 +120,18 @@ def process_rename(from, to)
87120
process_file(to)
88121
end
89122

123+
# :reek:DuplicateMethodCall
90124
def filename_for_subdirectory(filename)
91-
git_path = Git.git('rev-parse --show-toplevel')
92-
cd_path = Dir.pwd
93-
if cd_path.length > git_path.length
94-
filename = filename.sub(/^#{Regexp.escape("#{File.basename(cd_path)}/")}/, '')
125+
if @git_root == Dir.pwd
126+
git_path = Git.git('rev-parse --show-toplevel')
127+
cd_path = Dir.pwd
128+
if cd_path.length > git_path.length
129+
filename = filename.sub(/^#{Regexp.escape("#{File.basename(cd_path)}/")}/, '')
130+
end
131+
[filename]
132+
else
133+
filename
95134
end
96-
[filename]
97135
end
98136

99137
def process_file(filename)
@@ -109,10 +147,32 @@ def renames
109147
@renames ||= Renames.new
110148
end
111149

150+
# :reek:TooManyStatements
151+
# rubocop:disable Metrics/MethodLength
112152
def stats(path)
153+
# Try the path as-is first
154+
result = @stats.fetch(path, nil)
155+
return result if result
156+
157+
# If not found, try converting absolute path to relative path from git root
158+
absolute_path = File.expand_path(path)
159+
if absolute_path.start_with?(@git_root)
160+
relative_path = absolute_path[(@git_root.length + 1)..]
161+
return @stats.fetch(relative_path, Stats.new(0))
162+
end
163+
164+
# If still not found, try converting relative path to absolute path
165+
unless path.start_with?('/')
166+
absolute_path = File.expand_path(path, @git_root)
167+
return @stats.fetch(absolute_path, Stats.new(0))
168+
end
169+
170+
# Default fallback
113171
@stats.fetch(path, Stats.new(0))
114172
end
173+
# rubocop:enable Metrics/MethodLength
115174
end
175+
# rubocop:enable Metrics/ClassLength
116176
end
117177
end
118178
end

0 commit comments

Comments
 (0)