gitingest 0.3.1 → 0.4.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: 14bcb35132327c7725e69a895d56b4e88fb21db78fa5473c9afb4d08b879b7ee
4
- data.tar.gz: f3a5e06bec7566a268342678887bac14989d69d26f10cbb590e1f594c6779b89
3
+ metadata.gz: 114b6d8e954843f88917f556760e7fab89753126cb5fd059ed6cfe55c8f2e6c6
4
+ data.tar.gz: 39873c0d14eff9d6e615f6e2f13e7e30331f0e97db1162b613047782a582ff6c
5
5
  SHA512:
6
- metadata.gz: 1aed7d97acae8b6a1c2b15757cdc9852d802c99b83aa73caf114dfc904d0b05c44b70c853cd453fc12868310c09b15bf6bf71dcac203c01b5dfde241dc7ab0f5
7
- data.tar.gz: 07145ca986675723ad0371c873c176384631da660bed227b31ef4cf2680d72cc98479b4da8fb990fedb3dce0eab74ca5114c1222eea0ca1890a85d73726a8d4b
6
+ metadata.gz: f35ffb41ddd9990cd1c2e8c4ad08018a5bdd63c8ec222bfce021ede2c67dfa9355fbc7f1102f014c555d0df46501877b01f2767aadf674896635bd7fa5bab355
7
+ data.tar.gz: e431693ee45cf65ab3c63931c5c4a69675ddafc8038f90cda3dfb7dacec7449f48a65a41bc12f56996f8e697f80d1a896243e97aa37c96085107491099dabc05
data/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.0] - 2025-03-03
4
+
5
+ ### Added
6
+ - Added `generate_prompt` method for in-memory content generation without file I/O
7
+ - Integrated visual progress bar with file processing rate reporting
8
+ - Added human-readable time formatting for progress estimates
9
+ - Enhanced test coverage for multithreaded operations
10
+
11
+ ### Changed
12
+ - Refactored `process_content_to_output` for better code reuse between file and string output
13
+ - Improved thread management to handle various error conditions more gracefully
14
+ - Enhanced documentation with programmatic usage examples
15
+
16
+ ### Fixed
17
+ - Resolved thread pool shutdown issues in test environment
18
+ - Fixed race conditions in progress indicator updates
19
+ - Addressed timing inconsistencies in multithreaded test scenarios
20
+
21
+ ---
22
+
3
23
  ## [0.3.1] - 2025-03-03
4
24
 
5
25
  ### Added
data/README.md CHANGED
@@ -39,6 +39,15 @@ gitingest --repository user/repo --branch develop
39
39
  # Exclude additional patterns
40
40
  gitingest --repository user/repo --exclude "*.md,docs/"
41
41
 
42
+ # Control the number of threads
43
+ gitingest --repository user/repo -T 4
44
+
45
+ # Set thread pool shutdown timeout
46
+ gitingest --repository user/repo -W 120
47
+
48
+ # Combine threading options
49
+ gitingest --repository user/repo -T 8 -W 90
50
+
42
51
  # Quiet mode
43
52
  gitingest --repository user/repo --quiet
44
53
 
@@ -53,6 +62,10 @@ gitingest --repository user/repo --verbose
53
62
  - `-o, --output FILE`: Output file for the prompt [Default: reponame_prompt.txt]
54
63
  - `-e, --exclude PATTERN`: File patterns to exclude (comma separated)
55
64
  - `-b, --branch BRANCH`: Repository branch [Default: main]
65
+ - `-T, --threads COUNT`: Number of concurrent threads [Default: auto-detected]
66
+ - `-W, --thread-timeout SECONDS`: Thread pool shutdown timeout [Default: 60]
67
+ - `-q, --quiet`: Reduce logging to errors only
68
+ - `-v, --verbose`: Increase logging verbosity
56
69
  - `-h, --help`: Show help message
57
70
 
58
71
  ### As a Library
@@ -60,13 +73,21 @@ gitingest --repository user/repo --verbose
60
73
  ```ruby
61
74
  require "gitingest"
62
75
 
63
- # Basic usage
76
+ # Basic usage - write to a file
64
77
  generator = Gitingest::Generator.new(
65
78
  repository: "user/repo",
66
79
  token: "YOUR_GITHUB_TOKEN" # optional
67
80
  )
81
+
82
+ # Run the full workflow (fetch repository and generate file)
68
83
  generator.run
69
84
 
85
+ # OR generate file only (if you need the output path)
86
+ output_path = generator.generate_file
87
+
88
+ # Get content as a string (for in-memory processing)
89
+ content = generator.generate_prompt
90
+
70
91
  # With custom options
71
92
  generator = Gitingest::Generator.new(
72
93
  repository: "user/repo",
@@ -74,9 +95,10 @@ generator = Gitingest::Generator.new(
74
95
  output_file: "my_prompt.txt",
75
96
  branch: "develop",
76
97
  exclude: ["*.md", "docs/"],
77
- quiet: true # or verbose: true
98
+ threads: 4, # control concurrency
99
+ thread_timeout: 120, # custom thread timeout
100
+ quiet: true # or verbose: true
78
101
  )
79
- generator.run
80
102
 
81
103
  # With custom logger
82
104
  custom_logger = Logger.new("gitingest.log")
@@ -84,7 +106,6 @@ generator = Gitingest::Generator.new(
84
106
  repository: "user/repo",
85
107
  logger: custom_logger
86
108
  )
87
- generator.run
88
109
  ```
89
110
 
90
111
  ## Features
data/bin/gitingest CHANGED
@@ -22,7 +22,7 @@ parser = OptionParser.new do |opts|
22
22
  options[:output_file] = file
23
23
  end
24
24
 
25
- opts.on("-b", "--branch BRANCH", "Repository branch") do |branch|
25
+ opts.on("-b", "--branch BRANCH", "Repository branch [Default: main]") do |branch|
26
26
  options[:branch] = branch
27
27
  end
28
28
 
@@ -30,11 +30,20 @@ parser = OptionParser.new do |opts|
30
30
  options[:exclude] = pattern.split(",")
31
31
  end
32
32
 
33
- opts.on("-q", "--quiet", "Disable all output except errors") do
33
+ opts.on("-T", "--threads COUNT", Integer, "Number of concurrent threads [Default: auto-detected]") do |threads|
34
+ options[:threads] = threads
35
+ end
36
+
37
+ opts.on("-W", "--thread-timeout SECONDS", Integer,
38
+ "Thread pool shutdown timeout in seconds [Default: 60]") do |timeout|
39
+ options[:thread_timeout] = timeout
40
+ end
41
+
42
+ opts.on("-q", "--quiet", "Reduce logging to errors only") do
34
43
  options[:quiet] = true
35
44
  end
36
45
 
37
- opts.on("-v", "--verbose", "Enable verbose output") do
46
+ opts.on("-v", "--verbose", "Increase logging verbosity") do
38
47
  options[:verbose] = true
39
48
  end
40
49
 
@@ -42,6 +51,13 @@ parser = OptionParser.new do |opts|
42
51
  puts opts
43
52
  exit
44
53
  end
54
+
55
+ opts.separator ""
56
+ opts.separator "Examples:"
57
+ opts.separator " gitingest -r user/repo # Basic usage with public repository"
58
+ opts.separator " gitingest -r user/repo -t YOUR_TOKEN # With GitHub token for private repositories"
59
+ opts.separator " gitingest -r user/repo -o custom_prompt.txt # Custom output file"
60
+ opts.separator " gitingest -r user/repo -T 8 # Use 8 threads for processing"
45
61
  end
46
62
 
47
63
  begin
@@ -106,10 +106,42 @@ module Gitingest
106
106
  compile_excluded_patterns
107
107
  end
108
108
 
109
- # Main execution method
109
+ # Main execution method for command line
110
110
  def run
111
111
  fetch_repository_contents
112
- generate_prompt
112
+ generate_file
113
+ end
114
+
115
+ # Generate content and save it to a file
116
+ #
117
+ # @return [String] Path to the generated file
118
+ def generate_file
119
+ fetch_repository_contents if @repo_files.empty?
120
+
121
+ @logger.info "Generating file for #{@options[:repository]}"
122
+ File.open(@options[:output_file], "w") do |file|
123
+ process_content_to_output(file)
124
+ end
125
+
126
+ @logger.info "Prompt generated and saved to #{@options[:output_file]}"
127
+ @options[:output_file]
128
+ end
129
+
130
+ # Generate content and return it as a string
131
+ # Useful for programmatic usage
132
+ #
133
+ # @return [String] The generated repository content
134
+ def generate_prompt
135
+ @logger.info "Generating in-memory prompt for #{@options[:repository]}"
136
+
137
+ fetch_repository_contents if @repo_files.empty?
138
+
139
+ content = StringIO.new
140
+ process_content_to_output(content)
141
+
142
+ result = content.string
143
+ @logger.info "Generated #{result.size} bytes of content in memory"
144
+ result
113
145
  end
114
146
 
115
147
  private
@@ -202,89 +234,82 @@ module Gitingest
202
234
  path.match?(@combined_exclude_regex)
203
235
  end
204
236
 
205
- # Generate the consolidated prompt file with optimized threading
206
- def generate_prompt
207
- @logger.info "Generating prompt..."
237
+ # Common implementation for both file and string output
238
+ def process_content_to_output(output)
208
239
  @logger.debug "Using thread pool with #{@options[:threads]} threads"
209
240
 
210
241
  buffer = []
211
242
  progress = ProgressIndicator.new(@repo_files.size, @logger)
212
243
 
213
- # Optimization: thread-local buffers to reduce mutex contention
244
+ # Thread-local buffers to reduce mutex contention
214
245
  thread_buffers = {}
215
246
  mutex = Mutex.new
216
247
  errors = []
217
248
 
218
- # Dynamic thread pool based on configuration
249
+ # Thread pool based on configuration
219
250
  pool = Concurrent::FixedThreadPool.new(@options[:threads])
220
251
 
221
- # Group files by priority (smaller files first for better parallelism)
252
+ # Group files by priority
222
253
  prioritized_files = prioritize_files(@repo_files)
223
254
 
224
- File.open(@options[:output_file], "w") do |file|
225
- prioritized_files.each_with_index do |repo_file, index|
226
- pool.post do
227
- # Optimization: Use thread-local buffers
228
- thread_id = Thread.current.object_id
229
- thread_buffers[thread_id] ||= []
230
- local_buffer = thread_buffers[thread_id]
231
-
232
- begin
233
- content = fetch_file_content_with_retry(repo_file.path)
234
- result = format_file_content(repo_file.path, content)
235
- local_buffer << result
236
-
237
- # Optimization: Only acquire mutex when local buffer reaches threshold
238
- if local_buffer.size >= LOCAL_BUFFER_THRESHOLD
239
- mutex.synchronize do
240
- buffer.concat(local_buffer)
241
- write_buffer(file, buffer) if buffer.size >= BUFFER_SIZE
242
- local_buffer.clear
243
- end
244
- end
255
+ prioritized_files.each_with_index do |repo_file, index|
256
+ pool.post do
257
+ thread_id = Thread.current.object_id
258
+ thread_buffers[thread_id] ||= []
259
+ local_buffer = thread_buffers[thread_id]
245
260
 
246
- progress.update(index + 1)
247
- rescue Octokit::Error => e
248
- mutex.synchronize do
249
- errors << "Error fetching #{repo_file.path}: #{e.message}"
250
- @logger.error "Error fetching #{repo_file.path}: #{e.message}"
251
- end
252
- rescue StandardError => e
261
+ begin
262
+ content = fetch_file_content_with_retry(repo_file.path)
263
+ result = format_file_content(repo_file.path, content)
264
+ local_buffer << result
265
+
266
+ if local_buffer.size >= LOCAL_BUFFER_THRESHOLD
253
267
  mutex.synchronize do
254
- errors << "Unexpected error processing #{repo_file.path}: #{e.message}"
255
- @logger.error "Unexpected error processing #{repo_file.path}: #{e.message}"
268
+ buffer.concat(local_buffer)
269
+ write_buffer(output, buffer) if buffer.size >= BUFFER_SIZE
270
+ local_buffer.clear
256
271
  end
257
272
  end
273
+
274
+ progress.update(index + 1)
275
+ rescue Octokit::Error => e
276
+ mutex.synchronize do
277
+ errors << "Error fetching #{repo_file.path}: #{e.message}"
278
+ @logger.error "Error fetching #{repo_file.path}: #{e.message}"
279
+ end
280
+ rescue StandardError => e
281
+ mutex.synchronize do
282
+ errors << "Unexpected error processing #{repo_file.path}: #{e.message}"
283
+ @logger.error "Unexpected error processing #{repo_file.path}: #{e.message}"
284
+ end
258
285
  end
259
286
  end
287
+ end
260
288
 
261
- begin
262
- pool.shutdown
263
- wait_success = pool.wait_for_termination(@options[:thread_timeout])
289
+ begin
290
+ pool.shutdown
291
+ wait_success = pool.wait_for_termination(@options[:thread_timeout])
264
292
 
265
- unless wait_success
266
- @logger.warn "Thread pool did not shut down within #{@options[:thread_timeout]} seconds, forcing termination"
267
- pool.kill
268
- end
269
- rescue StandardError => e
270
- @logger.error "Error during thread pool shutdown: #{e.message}"
293
+ unless wait_success
294
+ @logger.warn "Thread pool did not shut down within #{@options[:thread_timeout]} seconds, forcing termination"
295
+ pool.kill
271
296
  end
297
+ rescue StandardError => e
298
+ @logger.error "Error during thread pool shutdown: #{e.message}"
299
+ end
272
300
 
273
- # Process remaining files in thread-local buffers
274
- mutex.synchronize do
275
- thread_buffers.each_value do |local_buffer|
276
- buffer.concat(local_buffer) unless local_buffer.empty?
277
- end
278
- write_buffer(file, buffer) unless buffer.empty?
301
+ # Process remaining files in thread-local buffers
302
+ mutex.synchronize do
303
+ thread_buffers.each_value do |local_buffer|
304
+ buffer.concat(local_buffer) unless local_buffer.empty?
279
305
  end
306
+ write_buffer(output, buffer) unless buffer.empty?
280
307
  end
281
308
 
282
- if errors.any?
283
- @logger.warn "Completed with #{errors.size} errors"
284
- @logger.debug "First few errors: #{errors.first(3).join(", ")}" if @logger.debug?
285
- end
309
+ return unless errors.any?
286
310
 
287
- @logger.info "Prompt generated and saved to #{@options[:output_file]}"
311
+ @logger.warn "Completed with #{errors.size} errors"
312
+ @logger.debug "First few errors: #{errors.first(3).join(", ")}" if @logger.debug?
288
313
  end
289
314
 
290
315
  # Format a file's content for the prompt
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gitingest
4
- VERSION = "0.3.1"
4
+ VERSION = "0.4.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitingest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Davide Santangelo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-03-02 00:00:00.000000000 Z
11
+ date: 2025-03-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby