Module: FlossFunding::RakeHelpers

Defined in:
lib/floss_funding/rake_helpers.rb

Class Method Summary collapse

Class Method Details

.ask_continue_on_invalid(invalids, lib_name) ⇒ Object

:nocov:



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/floss_funding/rake_helpers.rb', line 206

def ask_continue_on_invalid(invalids, lib_name)
  env = ENV["FF_BADDATA_CHOICE"].to_s.downcase.strip
  if %w[continue abort].include?(env)
    return env.to_sym
  end
  puts "floss_funding: Detected invalid config values for #{lib_name.inspect}: #{invalids.size} attribute(s)."
  puts "See debug log for details (names only)."
  loop do
    print("Continue without invalid values? [c]ontinue / [a]bort: ")
    $stdout.flush
    ans = ($stdin.gets || "").strip.downcase
    case ans
    when "c", "continue"
      return :continue
    when "a", "abort"
      return :abort
    else
      puts "Please choose c/a."
    end
  end
end

.ask_overwrite(path, default_choice = nil, prompt: nil) ⇒ Object

Simple interactive prompt modeled after common installers. Can be overridden
by setting FF_INSTALL_CHOICE to one of: replace_file, append, skip, abort, diff
Backwards-compat: also accepts “overwrite” but maps it to :replace_file.



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
# File 'lib/floss_funding/rake_helpers.rb', line 13

def ask_overwrite(path, default_choice = nil, prompt: nil)
  env_choice = ENV["FF_INSTALL_CHOICE"].to_s.downcase.strip
  case env_choice
  when "replace_file", "rf", "replace file", "overwrite", "o" then return :replace_file
  when "append", "p" then return :append
  when "skip", "s" then return :skip
  when "abort", "a" then return :abort
  when "diff", "d" then return :diff
  end

  loop do
    prompt ||= "#{path} exists. [d]iff, [r]eplace file, ap[p]end, [s]kip, [a]bort?"
    prompt += " (default: #{default_choice.to_s[0]})" if default_choice
    print(prompt + " ")
    $stdout.flush
    ans_line = $stdin.gets
    ans = ans_line ? ans_line.strip.downcase : nil
    if (ans.nil? || ans == "") && default_choice
      return default_choice
    end
    case ans
    when "d" then return :diff
    when "r" then return :replace_file
    when "o" then return :replace_file # legacy alias
    when "p" then return :append
    when "s" then return :skip
    when "a" then return :abort
    end
    puts "Please choose d/r/p/s/a."
  end
end

.ensure_gitignore_sentinels(path, header = "# Sentinels", lock_line = ".floss_funding.*.lock") ⇒ Object

Ensure a .gitignore contains the Sentinels section and required lock line
Behavior:

  • If ‘# Sentinels’ not found, prompt; on append, add a blank line, then the
    section header and the lock line.
  • If found and the section already contains the lock line, auto-skip silently.
  • If found and missing the lock line, display the section chunk, prompt with
    append as the default, and only add the lock line within that section.
    :nocov:


104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/floss_funding/rake_helpers.rb', line 104

def ensure_gitignore_sentinels(path, header = "# Sentinels", lock_line = ".floss_funding.*.lock")
  header = header.strip
  lock_line = lock_line.strip

  if !File.exist?(path)
    # Create file with header and lock line
    content = "#{header}\n#{lock_line}\n"
    FileUtils.mkdir_p(File.dirname(path))
    File.write(path, content)
    puts "floss_funding: Created #{path} with Sentinels section"
    return :created
  end

  current = File.read(path)
  lines = current.split("\n")
  sentinel_idx = lines.index { |l| l.strip == header }

  if sentinel_idx.nil?
    # No header present: propose to add a new section with a leading blank line
    to_append = String.new
    to_append << "\n" unless current.end_with?("\n")
    to_append << "\n#{header}\n#{lock_line}\n"
    # Show the resulting diff before prompting
    show_diff(current, current + to_append)
    loop do
      choice = ask_overwrite(path)
      case choice
      when :diff
        show_diff(current, current + to_append)
        next
      when :replace_file
        File.write(path, current + to_append)
        puts "floss_funding: Updated #{path} (added Sentinels section)"
        return :updated
      when :append
        File.open(path, "a") { |f| f.write(to_append) }
        puts "floss_funding: Appended Sentinels section to #{path}"
        return :updated
      when :skip
        puts "floss_funding: Skipped updating #{path}"
        return :skipped
      when :abort
        abort("floss_funding: Aborted by user while processing #{path}")
      end
    end
  else
    # Find end of section: next line starting with '#' after sentinel_idx
    next_comment_idx = nil
    ((sentinel_idx + 1)...lines.length).each do |i|
      if lines[i].to_s.strip.start_with?("#")
        next_comment_idx = i
        break
      end
    end
    section_end = next_comment_idx || lines.length
    section_lines = lines[sentinel_idx...section_end]

    # If lock line already present in this section, auto-skip
    if section_lines.any? { |l| l.strip == lock_line }
      # silently skip (no prompt)
      return :skipped
    end

    # Prepare new content by inserting lock line at the end of the section
    new_lines = lines.dup
    insert_at = section_end
    new_lines.insert(insert_at, lock_line)
    new_content = new_lines.join("\n")
    new_content += "\n" unless new_content.end_with?("\n")

    # Show the resulting diff for the proposed change (append) before prompting
    show_diff(current, new_content)

    loop do
      # Custom prompt for this context (no "replace chunk"): default append
      prompt = "#{path} exists. [d]iff, ap[p]end, [s]kip, [a]bort? (default: p)"
      print(prompt + " ")
      $stdout.flush
      ans_line = $stdin.gets
      ans = ans_line ? ans_line.strip.downcase : ""
      ans = "p" if ans.nil? || ans == ""
      case ans
      when "d"
        show_diff(current, new_content)
        next
      when "p"
        File.write(path, new_content)
        puts "floss_funding: Added #{lock_line} to Sentinels section in #{path}"
        return :updated
      when "s"
        puts "floss_funding: Skipped updating #{path}"
        return :skipped
      when "a"
        abort("floss_funding: Aborted by user while processing #{path}")
      else
        puts "Please choose d/p/s/a."
      end
    end
  end
end

.show_diff(old_str, new_str) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/floss_funding/rake_helpers.rb', line 45

def show_diff(old_str, new_str)
  old_lines = (old_str || "").to_s.split("\n")
  new_lines = (new_str || "").to_s.split("\n")
  puts "--- current"
  puts "+++ new"
  max = [old_lines.size, new_lines.size].max
  max.times do |i|
    o = old_lines[i]
    n = new_lines[i]
    if o != n
      puts "- #{o}"
      puts "+ #{n}"
    end
  end
end

.write_with_prompt(path, content) ⇒ Object

Write a file with prompting when destination already exists and content differs.
Returns :created, :updated, :skipped



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
# File 'lib/floss_funding/rake_helpers.rb', line 63

def write_with_prompt(path, content)
  if File.exist?(path)
    current = File.read(path)
    return :skipped if current == content # idempotent
    loop do
      choice = ask_overwrite(path)
      case choice
      when :diff
        show_diff(current, content)
        next
      when :replace_file
        File.write(path, content)
        puts "floss_funding: Updated #{path}"
        return :updated
      when :append
        File.open(path, "a") { |f| f.write(content) }
        puts "floss_funding: Appended to #{path}"
        return :updated
      when :skip
        puts "floss_funding: Skipped #{path}"
        return :skipped
      when :abort
        abort("floss_funding: Aborted by user while processing #{path}")
      end
    end
  else
    FileUtils.mkdir_p(File.dirname(path))
    File.write(path, content)
    puts "floss_funding: Created #{path}"
    :created
  end
end