Module: DocOpsLab::Dev::FileUtilities

Defined in:
lib/docopslab/dev/file_utils.rb

Class Method Summary collapse

Class Method Details

.file_matches_ignore_pattern?(file, pattern) ⇒ Boolean

Returns:

  • (Boolean)


114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/docopslab/dev/file_utils.rb', line 114

def file_matches_ignore_pattern? file, pattern
  if pattern.include?('*') || pattern.include?('?')
    # Handle glob patterns
    # If pattern ends with /*, treat it as recursive (dir/**/*)
    recursive_pattern = if pattern.end_with?('/*')
                          pattern.sub(%r{/\*$}, '/**/*')
                        else
                          pattern
                        end

    File.fnmatch(recursive_pattern, file, File::FNM_PATHNAME | File::FNM_DOTMATCH) ||
      # Also try exact match without modification for explicit patterns
      File.fnmatch(pattern, file, File::FNM_PATHNAME | File::FNM_DOTMATCH)
  else
    # Non-glob patterns: match directory name anywhere in path
    File.fnmatch("**/#{pattern}/**", file, File::FNM_PATHNAME | File::FNM_DOTMATCH) ||
      File.fnmatch("**/#{pattern}", file, File::FNM_PATHNAME | File::FNM_DOTMATCH)
  end
end

.find_asciidoc_files(context) ⇒ Object



110
111
112
# File 'lib/docopslab/dev/file_utils.rb', line 110

def find_asciidoc_files context
  FileUtilities.find_files_to_lint('vale', context)
end

.find_files_to_lint(tool_slug, context) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/docopslab/dev/file_utils.rb', line 61

def find_files_to_lint tool_slug, context
  path_config = context.get_path_config(tool_slug)
  lint_paths = path_config[:lint]
  skip_paths = path_config[:skip]
  exts = path_config[:exts]
  git_tracked_only = path_config[:git_tracked_only]

  return [] unless lint_paths

  pwd = Pathname.pwd
  files = []
  lint_paths.each do |path|
    Sourcerer::Util::Pathifier.match(path).enum.each do |file|
      # Normalize to a relative path for consistent pattern matching.
      # Pathifier may return absolute paths, so we relativize unconditionally.
      normalized = Pathname.new(file).expand_path.relative_path_from(pwd).to_s
      files << normalized
    end
  end

  files.uniq!

  # Filter by extension if exts is provided
  if exts && !exts.empty?
    files.select! do |file|
      ext = File.extname(file).delete_prefix('.')
      exts.include?(ext)
    end
  end

  # Filter out ignored paths
  files.reject! do |file|
    should_skip = skip_paths.any? do |ignored|
      FileUtilities.file_matches_ignore_pattern?(file, ignored)
    end
    should_skip
  end

  # Filter by git tracking status
  if git_tracked_only
    files.select! do |file|
      is_tracked = FileUtilities.git_tracked_or_staged?(file)
      is_tracked
    end
  end

  files.sort
end

.find_shell_scripts(context) ⇒ Object



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/docopslab/dev/file_utils.rb', line 10

def find_shell_scripts context
  # First, try to find files using the new path configuration system
  files = find_files_to_lint('shellcheck', context)
  return files if files && !files.empty?

  # Fallback to old method if no paths are configured for shellcheck
  scripts = []
  patterns = [
    '**/*.sh',
    '**/*.bash',
    '**/.*rc',
    '**/.*profile',
    'scripts/*.sh'
  ]
  patterns.each do |pattern|
    Dir.glob(pattern).each do |file|
      next unless File.file?(file)
      next if file.include?('/.vendor/')
      next if file.include?('/node_modules/')
      next unless FileUtilities.git_tracked_or_staged?(file)

      scripts << file if File.executable?(file) || FileUtilities.shell_shebang?(file)
    end
  end

  # Also pick up extensionless files with a Bash-implying shebang
  Dir.glob('**/*').each do |file|
    next unless File.file?(file)
    next unless File.extname(file).empty?
    next if file.include?('/.vendor/')
    next if file.include?('/node_modules/')
    next unless FileUtilities.git_tracked_or_staged?(file)

    scripts << file if FileUtilities.shell_shebang?(file)
  end

  scripts.uniq.sort
end

.git_tracked_or_staged?(file) ⇒ Boolean

Returns:

  • (Boolean)


134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/docopslab/dev/file_utils.rb', line 134

def git_tracked_or_staged? file
  return true unless Dir.exist?('.git')

  repo_root = `git rev-parse --show-toplevel`.strip
  rel = Pathname.new(file).expand_path.relative_path_from(Pathname.new(repo_root)).to_s

  # Check if the file is tracked
  return true if system('git', 'ls-files', '--error-unmatch', rel, out: File::NULL, err: File::NULL)

  # Check if the file is staged (but not necessarily committed yet)
  return true if system('git', 'diff', '--name-only', '--cached', '--', rel, out: File::NULL, err: File::NULL)

  false
end

.shell_shebang?(file) ⇒ Boolean

Returns:

  • (Boolean)


49
50
51
52
53
54
55
56
57
58
59
# File 'lib/docopslab/dev/file_utils.rb', line 49

def shell_shebang? file
  return false unless File.readable?(file)

  first_line = File.open(file, 'r') do |f|
    f.readline
  rescue StandardError
    ''
  end
  first_line.start_with?('#!') &&
    (first_line.include?('sh') || first_line.include?('bash'))
end