gitingest 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 65bac565322a3c782e5a3da91648fbe1aef905af80344b7ab81d8223e7a39c08
4
- data.tar.gz: ae9c73bc2df91ba61b10a01e36a15c2de7238104627c296408cfcc273851e885
3
+ metadata.gz: fd7a1e5d5ced0b5449fa30671b0d9a536685d37c3d0d34d33437f652df24c199
4
+ data.tar.gz: c49a7c6489f7074e3870a05b5d1e47b0ea3e6b7a6eadce405db3c300d8165434
5
5
  SHA512:
6
- metadata.gz: ba151945b20e7c5b4ce51af89a724f8673ec4b8928700b8922eecc8aeb7089fbe86e172d41289d308caff4ce1e88603be87416b1f743537c6b3f4e64d21515de
7
- data.tar.gz: ce1aaff5e55cca369d8eeb6e3fd68a92e63b5258f35b5edd8698b505c5c6d84363189eedeb483c5553d62ce2b9011ecf9b119f222dbc51e91a28b5f7e5e369e1
6
+ metadata.gz: 64b73ea01bc836a500c82a260c41be6f87e6f0c72bf868bee059407eea45466ad4892d905b7b69f5c6151e40e92d2553c8b8f678bfe1155fef968486648ae871
7
+ data.tar.gz: 7c18261e6fdb279916d2f8bff4557de76eb6ec0c039861d646f5f56dadacbef536b176d2a48fb25d142b70e30e868fb431f9cde6aad69262ee6f8dc37232ea0a
data/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.3.0] - 2025-03-02
6
+ - Added `faraday-retry` gem dependency for better API rate limit handling
7
+ - Implemented thread-safe buffer management with mutex locks
8
+ - Added new `ProgressIndicator` class for better CLI progress reporting (showing percentages)
9
+ - Improved memory efficiency with configurable buffer size
10
+ - Enhanced code organization with dedicated methods for file content formatting
11
+ - Added comprehensive method documentation and parameter descriptions
12
+ - Optimized thread pool size calculation for better performance
13
+ - Improved error handling in concurrent operations
14
+
5
15
  ## [0.2.0] - 2025-03-02
6
16
  - Added support for quiet and verbose modes in the command-line interface
7
17
  - Added the ability to specify a custom output file for the prompt
@@ -67,9 +67,21 @@ module Gitingest
67
67
 
68
68
  # Maximum number of files to process to prevent memory overload
69
69
  MAX_FILES = 1000
70
+ BUFFER_SIZE = 100 # Write every 100 files to reduce I/O operations
70
71
 
71
72
  attr_reader :options, :client, :repo_files, :excluded_patterns, :logger
72
73
 
74
+ # Initialize a new Generator with the given options
75
+ #
76
+ # @param options [Hash] Configuration options
77
+ # @option options [String] :repository GitHub repository in format "username/repo"
78
+ # @option options [String] :token GitHub personal access token
79
+ # @option options [String] :branch Repository branch (default: "main")
80
+ # @option options [String] :output_file Output file path
81
+ # @option options [Array<String>] :exclude Additional patterns to exclude
82
+ # @option options [Boolean] :quiet Reduce logging to errors only
83
+ # @option options [Boolean] :verbose Increase logging verbosity
84
+ # @option options [Logger] :logger Custom logger instance
73
85
  def initialize(options = {})
74
86
  @options = options
75
87
  @repo_files = []
@@ -80,6 +92,15 @@ module Gitingest
80
92
  compile_excluded_patterns
81
93
  end
82
94
 
95
+ # Main execution method
96
+ def run
97
+ fetch_repository_contents
98
+ generate_prompt
99
+ end
100
+
101
+ private
102
+
103
+ # Set up logging based on verbosity options
83
104
  def setup_logger
84
105
  @logger = @options[:logger] || Logger.new($stdout)
85
106
  @logger.level = if @options[:quiet]
@@ -89,11 +110,11 @@ module Gitingest
89
110
  else
90
111
  Logger::INFO
91
112
  end
92
- # Semplifica il formato del logger per la riga di comando
113
+ # Simplify logger format for command line usage
93
114
  @logger.formatter = proc { |severity, _, _, msg| "#{severity == "INFO" ? "" : "[#{severity}] "}#{msg}\n" }
94
115
  end
95
116
 
96
- ### Option Validation
117
+ # Validate and set default options
97
118
  def validate_options
98
119
  raise ArgumentError, "Repository is required" unless @options[:repository]
99
120
 
@@ -103,7 +124,7 @@ module Gitingest
103
124
  @excluded_patterns = DEFAULT_EXCLUDES + @options[:exclude]
104
125
  end
105
126
 
106
- ### Client Configuration
127
+ # Configure the GitHub API client
107
128
  def configure_client
108
129
  @client = @options[:token] ? Octokit::Client.new(access_token: @options[:token]) : Octokit::Client.new
109
130
 
@@ -115,17 +136,16 @@ module Gitingest
115
136
  end
116
137
  end
117
138
 
139
+ # Convert exclusion patterns to regular expressions
118
140
  def compile_excluded_patterns
119
141
  @excluded_patterns = @excluded_patterns.map { |pattern| Regexp.new(pattern) }
120
142
  end
121
143
 
122
- ### Fetch Repository Contents
144
+ # Fetch repository contents and apply exclusion filters
123
145
  def fetch_repository_contents
124
146
  @logger.info "Fetching repository: #{@options[:repository]} (branch: #{@options[:branch]})"
125
147
  begin
126
- # First validate authentication and repository access
127
148
  validate_repository_access
128
-
129
149
  repo_tree = @client.tree(@options[:repository], @options[:branch], recursive: true)
130
150
  @repo_files = repo_tree.tree.select { |item| item.type == "blob" && !excluded_file?(item.path) }
131
151
 
@@ -143,8 +163,8 @@ module Gitingest
143
163
  end
144
164
  end
145
165
 
166
+ # Validate repository and branch access
146
167
  def validate_repository_access
147
- # Check if we can access the repository
148
168
  begin
149
169
  @client.repository(@options[:repository])
150
170
  rescue Octokit::Unauthorized
@@ -153,7 +173,6 @@ module Gitingest
153
173
  raise "Repository '#{@options[:repository]}' not found or is private. Check the repository name or provide a valid token."
154
174
  end
155
175
 
156
- # Check if the branch exists
157
176
  begin
158
177
  @client.branch(@options[:repository], @options[:branch])
159
178
  rescue Octokit::NotFound
@@ -161,47 +180,64 @@ module Gitingest
161
180
  end
162
181
  end
163
182
 
183
+ # Check if a file should be excluded based on its path
164
184
  def excluded_file?(path)
165
185
  return true if path.start_with?(".") || path.split("/").any? { |part| part.start_with?(".") }
166
186
 
167
187
  @excluded_patterns.any? { |pattern| path.match?(pattern) }
168
188
  end
169
189
 
170
- ### Generate Prompt
190
+ # Generate the consolidated prompt file
171
191
  def generate_prompt
172
192
  @logger.info "Generating prompt..."
173
- Concurrent::Array.new(@repo_files)
174
193
  buffer = []
175
- buffer_size = 100 # Write every 100 files to reduce I/O
194
+ progress = ProgressIndicator.new(@repo_files.size, @logger)
176
195
 
177
196
  # Dynamic thread pool based on core count
178
- pool = Concurrent::FixedThreadPool.new([Concurrent.processor_count, 5].max)
197
+ pool = Concurrent::FixedThreadPool.new([Concurrent.processor_count, 5].min)
179
198
 
180
199
  File.open(@options[:output_file], "w") do |file|
181
200
  @repo_files.each_with_index do |repo_file, index|
182
201
  pool.post do
183
202
  content = fetch_file_content_with_retry(repo_file.path)
184
- result = <<~TEXT
185
- ================================================================
186
- File: #{repo_file.path}
187
- ================================================================
188
- #{content}
189
-
190
- TEXT
191
- buffer << result
192
- write_buffer(file, buffer) if buffer.size >= buffer_size
193
- print "\rProcessing: #{index + 1}/#{@repo_files.size} files"
203
+ result = format_file_content(repo_file.path, content)
204
+
205
+ # Thread-safe buffer management
206
+ buffer_mutex.synchronize do
207
+ buffer << result
208
+ write_buffer(file, buffer) if buffer.size >= BUFFER_SIZE
209
+ end
210
+
211
+ progress.update(index + 1)
194
212
  rescue Octokit::Error => e
195
213
  @logger.error "Error fetching #{repo_file.path}: #{e.message}"
196
214
  end
197
215
  end
216
+
198
217
  pool.shutdown
199
218
  pool.wait_for_termination
200
- write_buffer(file, buffer) unless buffer.empty?
219
+
220
+ # Write any remaining files in buffer
221
+ buffer_mutex.synchronize do
222
+ write_buffer(file, buffer) unless buffer.empty?
223
+ end
201
224
  end
202
- @logger.info "\nPrompt generated and saved to #{@options[:output_file]}"
225
+
226
+ @logger.info "Prompt generated and saved to #{@options[:output_file]}"
203
227
  end
204
228
 
229
+ # Format a file's content for the prompt
230
+ def format_file_content(path, content)
231
+ <<~TEXT
232
+ ================================================================
233
+ File: #{path}
234
+ ================================================================
235
+ #{content}
236
+
237
+ TEXT
238
+ end
239
+
240
+ # Fetch file content with retry logic for rate limiting
205
241
  def fetch_file_content_with_retry(path, retries = 3)
206
242
  content = @client.contents(@options[:repository], path: path, ref: @options[:branch])
207
243
  Base64.decode64(content.content)
@@ -214,15 +250,32 @@ module Gitingest
214
250
  fetch_file_content_with_retry(path, retries - 1)
215
251
  end
216
252
 
253
+ # Write buffer contents to file and clear buffer
217
254
  def write_buffer(file, buffer)
218
255
  file.puts(buffer.join)
219
256
  buffer.clear
220
257
  end
221
258
 
222
- ### Main Execution
223
- def run
224
- fetch_repository_contents
225
- generate_prompt
259
+ # Thread-safe mutex for buffer operations
260
+ def buffer_mutex
261
+ @buffer_mutex ||= Mutex.new
262
+ end
263
+ end
264
+
265
+ # Helper class for showing progress in CLI
266
+ class ProgressIndicator
267
+ def initialize(total, logger)
268
+ @total = total
269
+ @logger = logger
270
+ @last_percent = 0
271
+ end
272
+
273
+ def update(current)
274
+ percent = (current.to_f / @total * 100).round
275
+ return unless percent > @last_percent && ((percent % 5).zero? || current == @total)
276
+
277
+ @logger.info "Processing: #{percent}% complete (#{current}/#{@total} files)"
278
+ @last_percent = percent
226
279
  end
227
280
  end
228
281
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gitingest
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitingest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Davide Santangelo
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday-retry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: octokit
29
43
  requirement: !ruby/object:Gem::Requirement