makit 0.0.157 → 0.0.158
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 +4 -4
- data/README.md +41 -41
- data/exe/makit +5 -5
- data/lib/makit/apache.rb +28 -28
- data/lib/makit/auto.rb +48 -48
- data/lib/makit/azure/blob_storage.rb +257 -257
- data/lib/makit/azure/cli.rb +284 -284
- data/lib/makit/cli/base.rb +17 -17
- data/lib/makit/cli/build_commands.rb +500 -500
- data/lib/makit/cli/generators/base_generator.rb +74 -74
- data/lib/makit/cli/generators/dotnet_generator.rb +50 -50
- data/lib/makit/cli/generators/generator_factory.rb +49 -49
- data/lib/makit/cli/generators/node_generator.rb +50 -50
- data/lib/makit/cli/generators/ruby_generator.rb +77 -77
- data/lib/makit/cli/generators/rust_generator.rb +50 -50
- data/lib/makit/cli/generators/templates/dotnet_templates.rb +167 -167
- data/lib/makit/cli/generators/templates/node_templates.rb +161 -161
- data/lib/makit/cli/generators/templates/ruby/gemfile.rb +26 -26
- data/lib/makit/cli/generators/templates/ruby/gemspec.rb +41 -41
- data/lib/makit/cli/generators/templates/ruby/main_lib.rb +33 -33
- data/lib/makit/cli/generators/templates/ruby/rakefile.rb +35 -35
- data/lib/makit/cli/generators/templates/ruby/readme.rb +63 -63
- data/lib/makit/cli/generators/templates/ruby/test.rb +39 -39
- data/lib/makit/cli/generators/templates/ruby/test_helper.rb +29 -29
- data/lib/makit/cli/generators/templates/ruby/version.rb +29 -29
- data/lib/makit/cli/generators/templates/rust_templates.rb +128 -128
- data/lib/makit/cli/main.rb +78 -78
- data/lib/makit/cli/pipeline_commands.rb +311 -311
- data/lib/makit/cli/project_commands.rb +868 -868
- data/lib/makit/cli/repository_commands.rb +661 -661
- data/lib/makit/cli/strategy_commands.rb +207 -207
- data/lib/makit/cli/utility_commands.rb +521 -521
- data/lib/makit/commands/factory.rb +359 -359
- data/lib/makit/commands/middleware/base.rb +73 -73
- data/lib/makit/commands/middleware/cache.rb +248 -248
- data/lib/makit/commands/middleware/command_logger.rb +312 -312
- data/lib/makit/commands/middleware/validator.rb +269 -269
- data/lib/makit/commands/request.rb +316 -316
- data/lib/makit/commands/result.rb +323 -323
- data/lib/makit/commands/runner.rb +386 -386
- data/lib/makit/commands/strategies/base.rb +171 -171
- data/lib/makit/commands/strategies/child_process.rb +162 -162
- data/lib/makit/commands/strategies/factory.rb +136 -136
- data/lib/makit/commands/strategies/synchronous.rb +139 -139
- data/lib/makit/commands.rb +50 -50
- data/lib/makit/configuration/dotnet_project.rb +48 -48
- data/lib/makit/configuration/gitlab_helper.rb +61 -61
- data/lib/makit/configuration/project.rb +292 -292
- data/lib/makit/configuration/rakefile_helper.rb +43 -43
- data/lib/makit/configuration/step.rb +34 -34
- data/lib/makit/configuration/timeout.rb +74 -74
- data/lib/makit/configuration.rb +21 -21
- data/lib/makit/content/default_gitignore.rb +7 -7
- data/lib/makit/content/default_gitignore.txt +225 -225
- data/lib/makit/content/default_rakefile.rb +13 -13
- data/lib/makit/content/gem_rakefile.rb +16 -16
- data/lib/makit/context.rb +1 -1
- data/lib/makit/data.rb +49 -49
- data/lib/makit/directories.rb +170 -170
- data/lib/makit/directory.rb +262 -262
- data/lib/makit/docs/files.rb +89 -89
- data/lib/makit/docs/rake.rb +102 -102
- data/lib/makit/dotnet/cli.rb +69 -69
- data/lib/makit/dotnet/project.rb +217 -217
- data/lib/makit/dotnet/solution.rb +38 -38
- data/lib/makit/dotnet/solution_classlib.rb +239 -239
- data/lib/makit/dotnet/solution_console.rb +264 -264
- data/lib/makit/dotnet/solution_maui.rb +354 -354
- data/lib/makit/dotnet/solution_wasm.rb +275 -275
- data/lib/makit/dotnet/solution_wpf.rb +304 -304
- data/lib/makit/dotnet.rb +102 -102
- data/lib/makit/email.rb +90 -90
- data/lib/makit/environment.rb +142 -142
- data/lib/makit/examples/runner.rb +370 -370
- data/lib/makit/exceptions.rb +45 -45
- data/lib/makit/fileinfo.rb +32 -32
- data/lib/makit/files.rb +43 -43
- data/lib/makit/gems.rb +40 -40
- data/lib/makit/git/cli.rb +78 -54
- data/lib/makit/git/repository.rb +100 -100
- data/lib/makit/git.rb +104 -104
- data/lib/makit/gitlab/pipeline.rb +857 -857
- data/lib/makit/gitlab/pipeline_service_impl.rb +1535 -1535
- data/lib/makit/gitlab_runner.rb +59 -59
- data/lib/makit/humanize.rb +218 -218
- data/lib/makit/indexer.rb +47 -47
- data/lib/makit/io/filesystem.rb +111 -111
- data/lib/makit/io/filesystem_service_impl.rb +337 -337
- data/lib/makit/lint.rb +212 -212
- data/lib/makit/logging/configuration.rb +309 -309
- data/lib/makit/logging/format_registry.rb +84 -84
- data/lib/makit/logging/formatters/base.rb +39 -39
- data/lib/makit/logging/formatters/console_formatter.rb +140 -140
- data/lib/makit/logging/formatters/json_formatter.rb +65 -65
- data/lib/makit/logging/formatters/plain_text_formatter.rb +71 -71
- data/lib/makit/logging/formatters/text_formatter.rb +64 -64
- data/lib/makit/logging/log_request.rb +119 -119
- data/lib/makit/logging/logger.rb +199 -199
- data/lib/makit/logging/sinks/base.rb +91 -91
- data/lib/makit/logging/sinks/console.rb +72 -72
- data/lib/makit/logging/sinks/file_sink.rb +92 -92
- data/lib/makit/logging/sinks/structured.rb +123 -123
- data/lib/makit/logging/sinks/unified_file_sink.rb +296 -296
- data/lib/makit/logging.rb +578 -578
- data/lib/makit/markdown.rb +75 -75
- data/lib/makit/mp/basic_object_mp.rb +17 -17
- data/lib/makit/mp/command_mp.rb +13 -13
- data/lib/makit/mp/command_request.mp.rb +17 -17
- data/lib/makit/mp/project_mp.rb +199 -199
- data/lib/makit/mp/string_mp.rb +205 -205
- data/lib/makit/nuget.rb +74 -74
- data/lib/makit/podman/podman.rb +458 -458
- data/lib/makit/podman/podman_service_impl.rb +1081 -1081
- data/lib/makit/port.rb +32 -32
- data/lib/makit/process.rb +377 -377
- data/lib/makit/protoc.rb +112 -112
- data/lib/makit/rake/cli.rb +196 -196
- data/lib/makit/rake/trace_controller.rb +174 -174
- data/lib/makit/rake.rb +81 -81
- data/lib/makit/ruby/cli.rb +185 -185
- data/lib/makit/ruby.rb +25 -25
- data/lib/makit/rubygems.rb +137 -0
- data/lib/makit/secrets/azure_key_vault.rb +322 -322
- data/lib/makit/secrets/azure_secrets.rb +183 -183
- data/lib/makit/secrets/local_secrets.rb +72 -72
- data/lib/makit/secrets/secrets_manager.rb +105 -105
- data/lib/makit/secrets.rb +16 -16
- data/lib/makit/serializer.rb +130 -130
- data/lib/makit/services/builder.rb +186 -186
- data/lib/makit/services/error_handler.rb +226 -226
- data/lib/makit/services/repository_manager.rb +367 -367
- data/lib/makit/services/validator.rb +112 -112
- data/lib/makit/setup/classlib.rb +101 -101
- data/lib/makit/setup/gem.rb +268 -268
- data/lib/makit/setup/pages.rb +11 -11
- data/lib/makit/setup/razorclasslib.rb +101 -101
- data/lib/makit/setup/runner.rb +54 -54
- data/lib/makit/setup.rb +5 -5
- data/lib/makit/show.rb +110 -110
- data/lib/makit/storage.rb +126 -126
- data/lib/makit/symbols.rb +175 -175
- data/lib/makit/task_info.rb +130 -130
- data/lib/makit/tasks/at_exit.rb +15 -15
- data/lib/makit/tasks/build.rb +22 -22
- data/lib/makit/tasks/bump.rb +7 -7
- data/lib/makit/tasks/clean.rb +13 -13
- data/lib/makit/tasks/configure.rb +10 -10
- data/lib/makit/tasks/format.rb +10 -10
- data/lib/makit/tasks/hook_manager.rb +443 -443
- data/lib/makit/tasks/info.rb +368 -368
- data/lib/makit/tasks/init.rb +49 -49
- data/lib/makit/tasks/integrate.rb +60 -56
- data/lib/makit/tasks/pull_incoming.rb +13 -13
- data/lib/makit/tasks/secrets.rb +7 -7
- data/lib/makit/tasks/setup.rb +16 -16
- data/lib/makit/tasks/sync.rb +14 -17
- data/lib/makit/tasks/tag.rb +27 -27
- data/lib/makit/tasks/task_monkey_patch.rb +81 -81
- data/lib/makit/tasks/test.rb +22 -22
- data/lib/makit/tasks/update.rb +18 -18
- data/lib/makit/tasks/version.rb +6 -6
- data/lib/makit/tasks.rb +24 -24
- data/lib/makit/test_cache.rb +239 -239
- data/lib/makit/tree.rb +37 -37
- data/lib/makit/v1/configuration/project_service_impl.rb +370 -370
- data/lib/makit/v1/git/git_repository_service_impl.rb +295 -295
- data/lib/makit/v1/makit.v1_pb.rb +35 -35
- data/lib/makit/v1/makit.v1_services_pb.rb +27 -27
- data/lib/makit/v1/services/repository_manager_service_impl.rb +572 -572
- data/lib/makit/version.rb +661 -503
- data/lib/makit/version_util.rb +21 -21
- data/lib/makit/wix.rb +95 -95
- data/lib/makit/yaml.rb +29 -29
- data/lib/makit/zip.rb +17 -17
- data/lib/makit copy.rb +44 -44
- data/lib/makit.rb +115 -114
- metadata +3 -2
|
@@ -1,337 +1,337 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "fileutils"
|
|
4
|
-
require "mime/types"
|
|
5
|
-
require "digest"
|
|
6
|
-
require "time"
|
|
7
|
-
|
|
8
|
-
module Makit
|
|
9
|
-
module IO
|
|
10
|
-
class FileSystemServiceImpl
|
|
11
|
-
def initialize
|
|
12
|
-
# Initialize any required state
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
# Check if gRPC service is available
|
|
16
|
-
def self.grpc_available?
|
|
17
|
-
defined?(Makit::V1::Io::FileSystemService)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# Get a single file's content and/or metadata
|
|
21
|
-
def get_file(request, _unused_call = nil)
|
|
22
|
-
path = request.respond_to?(:path) ? request.path : request[:path]
|
|
23
|
-
include_metadata = request.respond_to?(:include_metadata) ? request.include_metadata : request[:include_metadata]
|
|
24
|
-
include_content = request.respond_to?(:include_content) ? request.include_content : request[:include_content]
|
|
25
|
-
|
|
26
|
-
# Default to metadata only if not specified
|
|
27
|
-
include_metadata = true if include_metadata.nil?
|
|
28
|
-
include_content = false if include_content.nil?
|
|
29
|
-
|
|
30
|
-
unless File.exist?(path)
|
|
31
|
-
raise Errno::ENOENT, "No such file or directory: #{path}"
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
file_data = {
|
|
35
|
-
path: path
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if include_content
|
|
39
|
-
if binary_file?(path)
|
|
40
|
-
file_data[:binary_data] = File.binread(path)
|
|
41
|
-
else
|
|
42
|
-
file_data[:text_lines] = File.readlines(path).map(&:chomp)
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
if include_metadata
|
|
47
|
-
file_data[:metadata] = get_file_metadata(path)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
if self.class.grpc_available?
|
|
51
|
-
Makit::V1::Io::FileResponse.new(file: Makit::V1::Io::File.new(file_data))
|
|
52
|
-
else
|
|
53
|
-
{ file: file_data }
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Create or update a file
|
|
58
|
-
def save_file(request, _unused_call = nil)
|
|
59
|
-
path = request.respond_to?(:path) ? request.path : request[:path]
|
|
60
|
-
binary_data = request.respond_to?(:binary_data) ? request.binary_data : request[:binary_data]
|
|
61
|
-
text_lines = request.respond_to?(:text_lines) ? request.text_lines : request[:text_lines]
|
|
62
|
-
|
|
63
|
-
# Ensure directory exists
|
|
64
|
-
FileUtils.mkdir_p(File.dirname(path))
|
|
65
|
-
|
|
66
|
-
if binary_data && !binary_data.empty?
|
|
67
|
-
File.binwrite(path, binary_data)
|
|
68
|
-
elsif text_lines && !text_lines.empty?
|
|
69
|
-
File.write(path, text_lines.join("\n") + "\n")
|
|
70
|
-
else
|
|
71
|
-
raise ArgumentError, "Either binary_data or text_lines must be provided"
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
file_data = {
|
|
75
|
-
path: path,
|
|
76
|
-
metadata: get_file_metadata(path)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if binary_data && !binary_data.empty?
|
|
80
|
-
file_data[:binary_data] = binary_data
|
|
81
|
-
elsif text_lines && !text_lines.empty?
|
|
82
|
-
file_data[:text_lines] = text_lines
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
if self.class.grpc_available?
|
|
86
|
-
Makit::V1::Io::FileResponse.new(file: Makit::V1::Io::File.new(file_data))
|
|
87
|
-
else
|
|
88
|
-
{ file: file_data }
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# Delete a file by path
|
|
93
|
-
def delete_file(request, _unused_call = nil)
|
|
94
|
-
path = request.respond_to?(:path) ? request.path : request[:path]
|
|
95
|
-
|
|
96
|
-
unless File.exist?(path)
|
|
97
|
-
raise Errno::ENOENT, "No such file or directory: #{path}"
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
File.delete(path)
|
|
101
|
-
|
|
102
|
-
if self.class.grpc_available?
|
|
103
|
-
Google::Protobuf::Empty.new
|
|
104
|
-
else
|
|
105
|
-
{}
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# List files in a directory according to query flags
|
|
110
|
-
def list_files(request, _unused_call = nil)
|
|
111
|
-
directory = request.respond_to?(:directory) ? request.directory : request[:directory]
|
|
112
|
-
recursive = request.respond_to?(:recursive) ? request.recursive : request[:recursive]
|
|
113
|
-
pattern = request.respond_to?(:pattern) ? request.pattern : request[:pattern]
|
|
114
|
-
respect_gitignore = request.respond_to?(:respect_gitignore) ? request.respect_gitignore : request[:respect_gitignore]
|
|
115
|
-
include_metadata = request.respond_to?(:include_metadata) ? request.include_metadata : request[:include_metadata]
|
|
116
|
-
include_content = request.respond_to?(:include_content) ? request.include_content : request[:include_content]
|
|
117
|
-
|
|
118
|
-
# Default values
|
|
119
|
-
recursive = false if recursive.nil?
|
|
120
|
-
respect_gitignore = false if respect_gitignore.nil?
|
|
121
|
-
include_metadata = true if include_metadata.nil?
|
|
122
|
-
include_content = false if include_content.nil?
|
|
123
|
-
|
|
124
|
-
unless Dir.exist?(directory)
|
|
125
|
-
raise Errno::ENOENT, "No such file or directory: #{directory}"
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
files = []
|
|
129
|
-
|
|
130
|
-
# Get gitignore patterns if requested
|
|
131
|
-
gitignore_patterns = []
|
|
132
|
-
if respect_gitignore
|
|
133
|
-
gitignore_file = File.join(directory, ".gitignore")
|
|
134
|
-
if File.exist?(gitignore_file)
|
|
135
|
-
gitignore_patterns = File.readlines(gitignore_file).map(&:strip).reject(&:empty?)
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
# Find files
|
|
140
|
-
if recursive
|
|
141
|
-
Dir.glob(File.join(directory, "**", "*")).each do |file_path|
|
|
142
|
-
next if File.directory?(file_path)
|
|
143
|
-
next if should_ignore_file?(file_path, gitignore_patterns)
|
|
144
|
-
next if pattern && !File.fnmatch(pattern, File.basename(file_path))
|
|
145
|
-
|
|
146
|
-
files << build_file_data(file_path, include_metadata, include_content)
|
|
147
|
-
end
|
|
148
|
-
else
|
|
149
|
-
Dir.entries(directory).each do |entry|
|
|
150
|
-
next if entry == "." || entry == ".."
|
|
151
|
-
|
|
152
|
-
file_path = File.join(directory, entry)
|
|
153
|
-
next if File.directory?(file_path)
|
|
154
|
-
next if should_ignore_file?(file_path, gitignore_patterns)
|
|
155
|
-
next if pattern && !File.fnmatch(pattern, entry)
|
|
156
|
-
|
|
157
|
-
files << build_file_data(file_path, include_metadata, include_content)
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
if self.class.grpc_available?
|
|
162
|
-
Makit::V1::Io::FileList.new(files: files.map { |f| Makit::V1::Io::File.new(f) })
|
|
163
|
-
else
|
|
164
|
-
{ files: files }
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
# Create a directory
|
|
169
|
-
def create_directory(request, _unused_call = nil)
|
|
170
|
-
path = request.respond_to?(:path) ? request.path : request[:path]
|
|
171
|
-
|
|
172
|
-
FileUtils.mkdir_p(path)
|
|
173
|
-
|
|
174
|
-
if self.class.grpc_available?
|
|
175
|
-
Google::Protobuf::Empty.new
|
|
176
|
-
else
|
|
177
|
-
{}
|
|
178
|
-
end
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
# Delete a directory
|
|
182
|
-
def delete_directory(request, _unused_call = nil)
|
|
183
|
-
path = request.respond_to?(:path) ? request.path : request[:path]
|
|
184
|
-
|
|
185
|
-
unless Dir.exist?(path)
|
|
186
|
-
raise Errno::ENOENT, "No such file or directory: #{path}"
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
Dir.rmdir(path)
|
|
190
|
-
|
|
191
|
-
if self.class.grpc_available?
|
|
192
|
-
Google::Protobuf::Empty.new
|
|
193
|
-
else
|
|
194
|
-
{}
|
|
195
|
-
end
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
# Upload large files in chunks
|
|
199
|
-
def upload(request, _unused_call = nil)
|
|
200
|
-
chunks = request.respond_to?(:chunks) ? request.chunks : request[:chunks]
|
|
201
|
-
|
|
202
|
-
raise ArgumentError, "No chunks provided" if chunks.nil? || chunks.empty?
|
|
203
|
-
|
|
204
|
-
# Group chunks by path
|
|
205
|
-
chunks_by_path = {}
|
|
206
|
-
chunks.each do |chunk|
|
|
207
|
-
path = chunk.respond_to?(:path) ? chunk.path : chunk[:path]
|
|
208
|
-
data = chunk.respond_to?(:data) ? chunk.data : chunk[:data]
|
|
209
|
-
metadata = chunk.respond_to?(:metadata) ? chunk.metadata : chunk[:metadata]
|
|
210
|
-
is_final = chunk.respond_to?(:is_final) ? chunk.is_final : chunk[:is_final]
|
|
211
|
-
|
|
212
|
-
chunks_by_path[path] ||= { data: "", metadata: metadata }
|
|
213
|
-
chunks_by_path[path][:data] += data
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
# Save each file
|
|
217
|
-
results = []
|
|
218
|
-
chunks_by_path.each do |path, file_info|
|
|
219
|
-
File.binwrite(path, file_info[:data])
|
|
220
|
-
|
|
221
|
-
file_data = {
|
|
222
|
-
path: path,
|
|
223
|
-
binary_data: file_info[:data],
|
|
224
|
-
metadata: get_file_metadata(path)
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
results << file_data
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
# Return the first (or only) file result
|
|
231
|
-
result = results.first
|
|
232
|
-
|
|
233
|
-
if self.class.grpc_available?
|
|
234
|
-
Makit::V1::Io::FileResponse.new(file: Makit::V1::Io::File.new(result))
|
|
235
|
-
else
|
|
236
|
-
{ file: result }
|
|
237
|
-
end
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
# Download a file in chunks
|
|
241
|
-
def download(request, _unused_call = nil)
|
|
242
|
-
path = request.respond_to?(:path) ? request.path : request[:path]
|
|
243
|
-
|
|
244
|
-
unless File.exist?(path)
|
|
245
|
-
raise Errno::ENOENT, "No such file or directory: #{path}"
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
content = File.binread(path)
|
|
249
|
-
chunk_size = 1024 # 1KB chunks
|
|
250
|
-
chunks = []
|
|
251
|
-
|
|
252
|
-
(0...content.length).step(chunk_size) do |i|
|
|
253
|
-
chunk_data = content[i, chunk_size]
|
|
254
|
-
is_final = (i + chunk_size >= content.length)
|
|
255
|
-
|
|
256
|
-
chunk = {
|
|
257
|
-
path: path,
|
|
258
|
-
data: chunk_data,
|
|
259
|
-
metadata: is_final ? get_file_metadata(path) : nil,
|
|
260
|
-
is_final: is_final
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
chunks << chunk
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
if self.class.grpc_available?
|
|
267
|
-
chunks.map { |c| Makit::V1::Io::FileChunk.new(c) }
|
|
268
|
-
else
|
|
269
|
-
chunks
|
|
270
|
-
end
|
|
271
|
-
end
|
|
272
|
-
|
|
273
|
-
private
|
|
274
|
-
|
|
275
|
-
def get_file_metadata(path)
|
|
276
|
-
stat = File.stat(path)
|
|
277
|
-
|
|
278
|
-
{
|
|
279
|
-
mime_type: get_mime_type(path),
|
|
280
|
-
size_bytes: stat.size,
|
|
281
|
-
created_at: stat.birthtime.iso8601,
|
|
282
|
-
modified_at: stat.mtime.iso8601,
|
|
283
|
-
owner: stat.uid.to_s,
|
|
284
|
-
is_directory: stat.directory?,
|
|
285
|
-
checksum: calculate_checksum(path)
|
|
286
|
-
}
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
def get_mime_type(path)
|
|
290
|
-
mime_type = MIME::Types.type_for(path).first
|
|
291
|
-
mime_type ? mime_type.content_type : "application/octet-stream"
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
def calculate_checksum(path)
|
|
295
|
-
Digest::SHA256.file(path).hexdigest
|
|
296
|
-
end
|
|
297
|
-
|
|
298
|
-
def binary_file?(path)
|
|
299
|
-
# Simple heuristic: check if file contains null bytes or high bytes
|
|
300
|
-
File.open(path, "rb") do |file|
|
|
301
|
-
chunk = file.read(1024)
|
|
302
|
-
return true if chunk.nil? || chunk.include?("\x00") || chunk.bytes.any? { |b| b > 127 }
|
|
303
|
-
end
|
|
304
|
-
false
|
|
305
|
-
rescue
|
|
306
|
-
true # If we can't read it, assume it's binary
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
def should_ignore_file?(file_path, gitignore_patterns)
|
|
310
|
-
return false if gitignore_patterns.empty?
|
|
311
|
-
|
|
312
|
-
relative_path = file_path
|
|
313
|
-
gitignore_patterns.any? do |pattern|
|
|
314
|
-
File.fnmatch(pattern, relative_path) || File.fnmatch(pattern, File.basename(relative_path))
|
|
315
|
-
end
|
|
316
|
-
end
|
|
317
|
-
|
|
318
|
-
def build_file_data(file_path, include_metadata, include_content)
|
|
319
|
-
file_data = { path: file_path }
|
|
320
|
-
|
|
321
|
-
if include_content
|
|
322
|
-
if binary_file?(file_path)
|
|
323
|
-
file_data[:binary_data] = File.binread(file_path)
|
|
324
|
-
else
|
|
325
|
-
file_data[:text_lines] = File.readlines(file_path).map(&:chomp)
|
|
326
|
-
end
|
|
327
|
-
end
|
|
328
|
-
|
|
329
|
-
if include_metadata
|
|
330
|
-
file_data[:metadata] = get_file_metadata(file_path)
|
|
331
|
-
end
|
|
332
|
-
|
|
333
|
-
file_data
|
|
334
|
-
end
|
|
335
|
-
end
|
|
336
|
-
end
|
|
337
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "mime/types"
|
|
5
|
+
require "digest"
|
|
6
|
+
require "time"
|
|
7
|
+
|
|
8
|
+
module Makit
|
|
9
|
+
module IO
|
|
10
|
+
class FileSystemServiceImpl
|
|
11
|
+
def initialize
|
|
12
|
+
# Initialize any required state
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Check if gRPC service is available
|
|
16
|
+
def self.grpc_available?
|
|
17
|
+
defined?(Makit::V1::Io::FileSystemService)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Get a single file's content and/or metadata
|
|
21
|
+
def get_file(request, _unused_call = nil)
|
|
22
|
+
path = request.respond_to?(:path) ? request.path : request[:path]
|
|
23
|
+
include_metadata = request.respond_to?(:include_metadata) ? request.include_metadata : request[:include_metadata]
|
|
24
|
+
include_content = request.respond_to?(:include_content) ? request.include_content : request[:include_content]
|
|
25
|
+
|
|
26
|
+
# Default to metadata only if not specified
|
|
27
|
+
include_metadata = true if include_metadata.nil?
|
|
28
|
+
include_content = false if include_content.nil?
|
|
29
|
+
|
|
30
|
+
unless File.exist?(path)
|
|
31
|
+
raise Errno::ENOENT, "No such file or directory: #{path}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
file_data = {
|
|
35
|
+
path: path
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if include_content
|
|
39
|
+
if binary_file?(path)
|
|
40
|
+
file_data[:binary_data] = File.binread(path)
|
|
41
|
+
else
|
|
42
|
+
file_data[:text_lines] = File.readlines(path).map(&:chomp)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if include_metadata
|
|
47
|
+
file_data[:metadata] = get_file_metadata(path)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
if self.class.grpc_available?
|
|
51
|
+
Makit::V1::Io::FileResponse.new(file: Makit::V1::Io::File.new(file_data))
|
|
52
|
+
else
|
|
53
|
+
{ file: file_data }
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Create or update a file
|
|
58
|
+
def save_file(request, _unused_call = nil)
|
|
59
|
+
path = request.respond_to?(:path) ? request.path : request[:path]
|
|
60
|
+
binary_data = request.respond_to?(:binary_data) ? request.binary_data : request[:binary_data]
|
|
61
|
+
text_lines = request.respond_to?(:text_lines) ? request.text_lines : request[:text_lines]
|
|
62
|
+
|
|
63
|
+
# Ensure directory exists
|
|
64
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
65
|
+
|
|
66
|
+
if binary_data && !binary_data.empty?
|
|
67
|
+
File.binwrite(path, binary_data)
|
|
68
|
+
elsif text_lines && !text_lines.empty?
|
|
69
|
+
File.write(path, text_lines.join("\n") + "\n")
|
|
70
|
+
else
|
|
71
|
+
raise ArgumentError, "Either binary_data or text_lines must be provided"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
file_data = {
|
|
75
|
+
path: path,
|
|
76
|
+
metadata: get_file_metadata(path)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if binary_data && !binary_data.empty?
|
|
80
|
+
file_data[:binary_data] = binary_data
|
|
81
|
+
elsif text_lines && !text_lines.empty?
|
|
82
|
+
file_data[:text_lines] = text_lines
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
if self.class.grpc_available?
|
|
86
|
+
Makit::V1::Io::FileResponse.new(file: Makit::V1::Io::File.new(file_data))
|
|
87
|
+
else
|
|
88
|
+
{ file: file_data }
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Delete a file by path
|
|
93
|
+
def delete_file(request, _unused_call = nil)
|
|
94
|
+
path = request.respond_to?(:path) ? request.path : request[:path]
|
|
95
|
+
|
|
96
|
+
unless File.exist?(path)
|
|
97
|
+
raise Errno::ENOENT, "No such file or directory: #{path}"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
File.delete(path)
|
|
101
|
+
|
|
102
|
+
if self.class.grpc_available?
|
|
103
|
+
Google::Protobuf::Empty.new
|
|
104
|
+
else
|
|
105
|
+
{}
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# List files in a directory according to query flags
|
|
110
|
+
def list_files(request, _unused_call = nil)
|
|
111
|
+
directory = request.respond_to?(:directory) ? request.directory : request[:directory]
|
|
112
|
+
recursive = request.respond_to?(:recursive) ? request.recursive : request[:recursive]
|
|
113
|
+
pattern = request.respond_to?(:pattern) ? request.pattern : request[:pattern]
|
|
114
|
+
respect_gitignore = request.respond_to?(:respect_gitignore) ? request.respect_gitignore : request[:respect_gitignore]
|
|
115
|
+
include_metadata = request.respond_to?(:include_metadata) ? request.include_metadata : request[:include_metadata]
|
|
116
|
+
include_content = request.respond_to?(:include_content) ? request.include_content : request[:include_content]
|
|
117
|
+
|
|
118
|
+
# Default values
|
|
119
|
+
recursive = false if recursive.nil?
|
|
120
|
+
respect_gitignore = false if respect_gitignore.nil?
|
|
121
|
+
include_metadata = true if include_metadata.nil?
|
|
122
|
+
include_content = false if include_content.nil?
|
|
123
|
+
|
|
124
|
+
unless Dir.exist?(directory)
|
|
125
|
+
raise Errno::ENOENT, "No such file or directory: #{directory}"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
files = []
|
|
129
|
+
|
|
130
|
+
# Get gitignore patterns if requested
|
|
131
|
+
gitignore_patterns = []
|
|
132
|
+
if respect_gitignore
|
|
133
|
+
gitignore_file = File.join(directory, ".gitignore")
|
|
134
|
+
if File.exist?(gitignore_file)
|
|
135
|
+
gitignore_patterns = File.readlines(gitignore_file).map(&:strip).reject(&:empty?)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Find files
|
|
140
|
+
if recursive
|
|
141
|
+
Dir.glob(File.join(directory, "**", "*")).each do |file_path|
|
|
142
|
+
next if File.directory?(file_path)
|
|
143
|
+
next if should_ignore_file?(file_path, gitignore_patterns)
|
|
144
|
+
next if pattern && !File.fnmatch(pattern, File.basename(file_path))
|
|
145
|
+
|
|
146
|
+
files << build_file_data(file_path, include_metadata, include_content)
|
|
147
|
+
end
|
|
148
|
+
else
|
|
149
|
+
Dir.entries(directory).each do |entry|
|
|
150
|
+
next if entry == "." || entry == ".."
|
|
151
|
+
|
|
152
|
+
file_path = File.join(directory, entry)
|
|
153
|
+
next if File.directory?(file_path)
|
|
154
|
+
next if should_ignore_file?(file_path, gitignore_patterns)
|
|
155
|
+
next if pattern && !File.fnmatch(pattern, entry)
|
|
156
|
+
|
|
157
|
+
files << build_file_data(file_path, include_metadata, include_content)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
if self.class.grpc_available?
|
|
162
|
+
Makit::V1::Io::FileList.new(files: files.map { |f| Makit::V1::Io::File.new(f) })
|
|
163
|
+
else
|
|
164
|
+
{ files: files }
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Create a directory
|
|
169
|
+
def create_directory(request, _unused_call = nil)
|
|
170
|
+
path = request.respond_to?(:path) ? request.path : request[:path]
|
|
171
|
+
|
|
172
|
+
FileUtils.mkdir_p(path)
|
|
173
|
+
|
|
174
|
+
if self.class.grpc_available?
|
|
175
|
+
Google::Protobuf::Empty.new
|
|
176
|
+
else
|
|
177
|
+
{}
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Delete a directory
|
|
182
|
+
def delete_directory(request, _unused_call = nil)
|
|
183
|
+
path = request.respond_to?(:path) ? request.path : request[:path]
|
|
184
|
+
|
|
185
|
+
unless Dir.exist?(path)
|
|
186
|
+
raise Errno::ENOENT, "No such file or directory: #{path}"
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
Dir.rmdir(path)
|
|
190
|
+
|
|
191
|
+
if self.class.grpc_available?
|
|
192
|
+
Google::Protobuf::Empty.new
|
|
193
|
+
else
|
|
194
|
+
{}
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Upload large files in chunks
|
|
199
|
+
def upload(request, _unused_call = nil)
|
|
200
|
+
chunks = request.respond_to?(:chunks) ? request.chunks : request[:chunks]
|
|
201
|
+
|
|
202
|
+
raise ArgumentError, "No chunks provided" if chunks.nil? || chunks.empty?
|
|
203
|
+
|
|
204
|
+
# Group chunks by path
|
|
205
|
+
chunks_by_path = {}
|
|
206
|
+
chunks.each do |chunk|
|
|
207
|
+
path = chunk.respond_to?(:path) ? chunk.path : chunk[:path]
|
|
208
|
+
data = chunk.respond_to?(:data) ? chunk.data : chunk[:data]
|
|
209
|
+
metadata = chunk.respond_to?(:metadata) ? chunk.metadata : chunk[:metadata]
|
|
210
|
+
is_final = chunk.respond_to?(:is_final) ? chunk.is_final : chunk[:is_final]
|
|
211
|
+
|
|
212
|
+
chunks_by_path[path] ||= { data: "", metadata: metadata }
|
|
213
|
+
chunks_by_path[path][:data] += data
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Save each file
|
|
217
|
+
results = []
|
|
218
|
+
chunks_by_path.each do |path, file_info|
|
|
219
|
+
File.binwrite(path, file_info[:data])
|
|
220
|
+
|
|
221
|
+
file_data = {
|
|
222
|
+
path: path,
|
|
223
|
+
binary_data: file_info[:data],
|
|
224
|
+
metadata: get_file_metadata(path)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
results << file_data
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# Return the first (or only) file result
|
|
231
|
+
result = results.first
|
|
232
|
+
|
|
233
|
+
if self.class.grpc_available?
|
|
234
|
+
Makit::V1::Io::FileResponse.new(file: Makit::V1::Io::File.new(result))
|
|
235
|
+
else
|
|
236
|
+
{ file: result }
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Download a file in chunks
|
|
241
|
+
def download(request, _unused_call = nil)
|
|
242
|
+
path = request.respond_to?(:path) ? request.path : request[:path]
|
|
243
|
+
|
|
244
|
+
unless File.exist?(path)
|
|
245
|
+
raise Errno::ENOENT, "No such file or directory: #{path}"
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
content = File.binread(path)
|
|
249
|
+
chunk_size = 1024 # 1KB chunks
|
|
250
|
+
chunks = []
|
|
251
|
+
|
|
252
|
+
(0...content.length).step(chunk_size) do |i|
|
|
253
|
+
chunk_data = content[i, chunk_size]
|
|
254
|
+
is_final = (i + chunk_size >= content.length)
|
|
255
|
+
|
|
256
|
+
chunk = {
|
|
257
|
+
path: path,
|
|
258
|
+
data: chunk_data,
|
|
259
|
+
metadata: is_final ? get_file_metadata(path) : nil,
|
|
260
|
+
is_final: is_final
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
chunks << chunk
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
if self.class.grpc_available?
|
|
267
|
+
chunks.map { |c| Makit::V1::Io::FileChunk.new(c) }
|
|
268
|
+
else
|
|
269
|
+
chunks
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
private
|
|
274
|
+
|
|
275
|
+
def get_file_metadata(path)
|
|
276
|
+
stat = File.stat(path)
|
|
277
|
+
|
|
278
|
+
{
|
|
279
|
+
mime_type: get_mime_type(path),
|
|
280
|
+
size_bytes: stat.size,
|
|
281
|
+
created_at: stat.birthtime.iso8601,
|
|
282
|
+
modified_at: stat.mtime.iso8601,
|
|
283
|
+
owner: stat.uid.to_s,
|
|
284
|
+
is_directory: stat.directory?,
|
|
285
|
+
checksum: calculate_checksum(path)
|
|
286
|
+
}
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def get_mime_type(path)
|
|
290
|
+
mime_type = MIME::Types.type_for(path).first
|
|
291
|
+
mime_type ? mime_type.content_type : "application/octet-stream"
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def calculate_checksum(path)
|
|
295
|
+
Digest::SHA256.file(path).hexdigest
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def binary_file?(path)
|
|
299
|
+
# Simple heuristic: check if file contains null bytes or high bytes
|
|
300
|
+
File.open(path, "rb") do |file|
|
|
301
|
+
chunk = file.read(1024)
|
|
302
|
+
return true if chunk.nil? || chunk.include?("\x00") || chunk.bytes.any? { |b| b > 127 }
|
|
303
|
+
end
|
|
304
|
+
false
|
|
305
|
+
rescue
|
|
306
|
+
true # If we can't read it, assume it's binary
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def should_ignore_file?(file_path, gitignore_patterns)
|
|
310
|
+
return false if gitignore_patterns.empty?
|
|
311
|
+
|
|
312
|
+
relative_path = file_path
|
|
313
|
+
gitignore_patterns.any? do |pattern|
|
|
314
|
+
File.fnmatch(pattern, relative_path) || File.fnmatch(pattern, File.basename(relative_path))
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def build_file_data(file_path, include_metadata, include_content)
|
|
319
|
+
file_data = { path: file_path }
|
|
320
|
+
|
|
321
|
+
if include_content
|
|
322
|
+
if binary_file?(file_path)
|
|
323
|
+
file_data[:binary_data] = File.binread(file_path)
|
|
324
|
+
else
|
|
325
|
+
file_data[:text_lines] = File.readlines(file_path).map(&:chomp)
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
if include_metadata
|
|
330
|
+
file_data[:metadata] = get_file_metadata(file_path)
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
file_data
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
end
|