makit 0.0.139 → 0.0.140

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.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +41 -41
  3. data/exe/makit +5 -5
  4. data/lib/makit/apache.rb +28 -28
  5. data/lib/makit/auto.rb +48 -48
  6. data/lib/makit/cli/build_commands.rb +500 -500
  7. data/lib/makit/cli/generators/base_generator.rb +74 -74
  8. data/lib/makit/cli/generators/dotnet_generator.rb +50 -50
  9. data/lib/makit/cli/generators/generator_factory.rb +49 -49
  10. data/lib/makit/cli/generators/node_generator.rb +50 -50
  11. data/lib/makit/cli/generators/ruby_generator.rb +77 -77
  12. data/lib/makit/cli/generators/rust_generator.rb +50 -50
  13. data/lib/makit/cli/generators/templates/dotnet_templates.rb +167 -167
  14. data/lib/makit/cli/generators/templates/node_templates.rb +161 -161
  15. data/lib/makit/cli/generators/templates/ruby/gemfile.rb +26 -26
  16. data/lib/makit/cli/generators/templates/ruby/gemspec.rb +40 -40
  17. data/lib/makit/cli/generators/templates/ruby/main_lib.rb +33 -33
  18. data/lib/makit/cli/generators/templates/ruby/rakefile.rb +35 -35
  19. data/lib/makit/cli/generators/templates/ruby/readme.rb +63 -63
  20. data/lib/makit/cli/generators/templates/ruby/test.rb +39 -39
  21. data/lib/makit/cli/generators/templates/ruby/test_helper.rb +29 -29
  22. data/lib/makit/cli/generators/templates/ruby/version.rb +29 -29
  23. data/lib/makit/cli/generators/templates/rust_templates.rb +128 -128
  24. data/lib/makit/cli/main.rb +69 -67
  25. data/lib/makit/cli/project_commands.rb +868 -868
  26. data/lib/makit/cli/repository_commands.rb +661 -661
  27. data/lib/makit/cli/strategy_commands.rb +51 -0
  28. data/lib/makit/cli/utility_commands.rb +521 -521
  29. data/lib/makit/commands/factory.rb +359 -359
  30. data/lib/makit/commands/middleware/base.rb +73 -73
  31. data/lib/makit/commands/middleware/cache.rb +248 -248
  32. data/lib/makit/commands/middleware/command_logger.rb +312 -312
  33. data/lib/makit/commands/middleware/validator.rb +269 -269
  34. data/lib/makit/commands/request.rb +316 -316
  35. data/lib/makit/commands/result.rb +323 -323
  36. data/lib/makit/commands/runner.rb +385 -385
  37. data/lib/makit/commands/strategies/base.rb +171 -171
  38. data/lib/makit/commands/strategies/child_process.rb +1 -1
  39. data/lib/makit/commands/strategies/synchronous.rb +139 -139
  40. data/lib/makit/commands.rb +50 -50
  41. data/lib/makit/configuration/dotnet_project.rb +12 -12
  42. data/lib/makit/configuration/gitlab_helper.rb +58 -58
  43. data/lib/makit/configuration/project.rb +168 -168
  44. data/lib/makit/configuration/rakefile_helper.rb +43 -43
  45. data/lib/makit/configuration/step.rb +34 -34
  46. data/lib/makit/configuration/timeout.rb +74 -0
  47. data/lib/makit/configuration.rb +15 -14
  48. data/lib/makit/content/default_gitignore.rb +7 -7
  49. data/lib/makit/content/default_gitignore.txt +225 -225
  50. data/lib/makit/content/default_rakefile.rb +13 -13
  51. data/lib/makit/content/gem_rakefile.rb +16 -16
  52. data/lib/makit/context.rb +1 -1
  53. data/lib/makit/data.rb +49 -49
  54. data/lib/makit/directories.rb +140 -140
  55. data/lib/makit/directory.rb +262 -262
  56. data/lib/makit/docs/files.rb +89 -89
  57. data/lib/makit/docs/rake.rb +102 -102
  58. data/lib/makit/dotnet/cli.rb +69 -69
  59. data/lib/makit/dotnet/project.rb +217 -217
  60. data/lib/makit/dotnet/solution.rb +38 -38
  61. data/lib/makit/dotnet/solution_classlib.rb +239 -239
  62. data/lib/makit/dotnet/solution_console.rb +264 -264
  63. data/lib/makit/dotnet/solution_maui.rb +354 -354
  64. data/lib/makit/dotnet/solution_wasm.rb +275 -275
  65. data/lib/makit/dotnet/solution_wpf.rb +304 -304
  66. data/lib/makit/dotnet.rb +102 -102
  67. data/lib/makit/email.rb +90 -90
  68. data/lib/makit/environment.rb +142 -142
  69. data/lib/makit/examples/runner.rb +370 -370
  70. data/lib/makit/exceptions.rb +45 -45
  71. data/lib/makit/fileinfo.rb +24 -24
  72. data/lib/makit/files.rb +43 -43
  73. data/lib/makit/gems.rb +40 -40
  74. data/lib/makit/git/cli.rb +54 -54
  75. data/lib/makit/git/repository.rb +90 -90
  76. data/lib/makit/git.rb +98 -98
  77. data/lib/makit/gitlab_runner.rb +59 -59
  78. data/lib/makit/humanize.rb +137 -137
  79. data/lib/makit/indexer.rb +47 -47
  80. data/lib/makit/logging/configuration.rb +308 -308
  81. data/lib/makit/logging/format_registry.rb +84 -84
  82. data/lib/makit/logging/formatters/base.rb +39 -39
  83. data/lib/makit/logging/formatters/console_formatter.rb +140 -140
  84. data/lib/makit/logging/formatters/json_formatter.rb +65 -65
  85. data/lib/makit/logging/formatters/plain_text_formatter.rb +71 -71
  86. data/lib/makit/logging/formatters/text_formatter.rb +64 -64
  87. data/lib/makit/logging/log_request.rb +119 -119
  88. data/lib/makit/logging/logger.rb +199 -199
  89. data/lib/makit/logging/sinks/base.rb +91 -91
  90. data/lib/makit/logging/sinks/console.rb +72 -72
  91. data/lib/makit/logging/sinks/file_sink.rb +92 -92
  92. data/lib/makit/logging/sinks/structured.rb +123 -123
  93. data/lib/makit/logging/sinks/unified_file_sink.rb +296 -296
  94. data/lib/makit/logging.rb +565 -565
  95. data/lib/makit/markdown.rb +75 -75
  96. data/lib/makit/mp/basic_object_mp.rb +17 -17
  97. data/lib/makit/mp/command_mp.rb +13 -13
  98. data/lib/makit/mp/command_request.mp.rb +17 -17
  99. data/lib/makit/mp/project_mp.rb +199 -199
  100. data/lib/makit/mp/string_mp.rb +191 -191
  101. data/lib/makit/nuget.rb +74 -74
  102. data/lib/makit/port.rb +32 -32
  103. data/lib/makit/process.rb +163 -163
  104. data/lib/makit/protoc.rb +107 -107
  105. data/lib/makit/rake/cli.rb +196 -196
  106. data/lib/makit/rake.rb +80 -80
  107. data/lib/makit/ruby/cli.rb +185 -185
  108. data/lib/makit/ruby.rb +25 -25
  109. data/lib/makit/secrets.rb +51 -51
  110. data/lib/makit/serializer.rb +130 -130
  111. data/lib/makit/services/builder.rb +186 -186
  112. data/lib/makit/services/error_handler.rb +226 -226
  113. data/lib/makit/services/repository_manager.rb +231 -231
  114. data/lib/makit/services/validator.rb +112 -112
  115. data/lib/makit/setup/classlib.rb +101 -101
  116. data/lib/makit/setup/gem.rb +268 -268
  117. data/lib/makit/setup/razorclasslib.rb +101 -101
  118. data/lib/makit/setup/runner.rb +54 -54
  119. data/lib/makit/setup.rb +5 -5
  120. data/lib/makit/show.rb +110 -110
  121. data/lib/makit/storage.rb +126 -126
  122. data/lib/makit/symbols.rb +170 -170
  123. data/lib/makit/task_info.rb +130 -130
  124. data/lib/makit/tasks/at_exit.rb +15 -15
  125. data/lib/makit/tasks/build.rb +22 -22
  126. data/lib/makit/tasks/clean.rb +13 -13
  127. data/lib/makit/tasks/configure.rb +10 -10
  128. data/lib/makit/tasks/format.rb +10 -10
  129. data/lib/makit/tasks/hook_manager.rb +443 -443
  130. data/lib/makit/tasks/init.rb +49 -49
  131. data/lib/makit/tasks/integrate.rb +29 -29
  132. data/lib/makit/tasks/pull_incoming.rb +13 -13
  133. data/lib/makit/tasks/setup.rb +13 -13
  134. data/lib/makit/tasks/sync.rb +17 -17
  135. data/lib/makit/tasks/tag.rb +16 -16
  136. data/lib/makit/tasks/task_monkey_patch.rb +81 -81
  137. data/lib/makit/tasks/test.rb +22 -22
  138. data/lib/makit/tasks/update.rb +18 -18
  139. data/lib/makit/tasks.rb +20 -20
  140. data/lib/makit/test_cache.rb +239 -239
  141. data/lib/makit/tree.rb +37 -37
  142. data/lib/makit/v1/makit.v1_pb.rb +35 -35
  143. data/lib/makit/v1/makit.v1_services_pb.rb +27 -27
  144. data/lib/makit/version.rb +99 -99
  145. data/lib/makit/version_util.rb +21 -21
  146. data/lib/makit/wix.rb +95 -95
  147. data/lib/makit/yaml.rb +29 -29
  148. data/lib/makit/zip.rb +17 -17
  149. data/lib/makit copy.rb +44 -44
  150. data/lib/makit.rb +42 -42
  151. metadata +2 -1
@@ -1,500 +1,500 @@
1
- # frozen_string_literal: true
2
-
3
- require "clamp"
4
- require "fileutils"
5
- require "json"
6
-
7
- module Makit
8
- module Cli
9
- # Consolidated build operations commands
10
- # Combines: make, clean functionality
11
- class BuildCommand < Clamp::Command
12
- self.description = <<~DESC
13
- Manage project build operations and artifact cleanup.
14
-
15
- Available operations:
16
- make - Build project and generate artifacts
17
- clean - Clean build artifacts and temporary files
18
- DESC
19
- end
20
-
21
- # Build project artifacts
22
- class MakeBuildCommand < Clamp::Command
23
- self.description = <<~DESC
24
- Build project artifacts and execute build tasks.
25
-
26
- When called without arguments, enumerates all tracked repositories
27
- and displays their latest commit IDs.
28
-
29
- Examples:
30
- makit build make # Show all repositories and latest commits
31
- makit build make https://github.com/user/repo # Build specific repository
32
- makit build make --target release # Build current project with release target
33
- makit build make --parallel # Build with parallel execution
34
- DESC
35
-
36
- parameter "[REPOSITORY_URL]", "Git repository URL to build (optional)", attribute_name: :repository_url,
37
- required: false
38
- option ["--commit"], "COMMIT", "Specific commit to build (default: latest)", default: "latest"
39
- option ["--target"], "TARGET", "Build target (debug, release)", default: "debug"
40
- option ["--parallel", "-j"], :flag, "Enable parallel build"
41
- option ["--verbose", "-v"], :flag, "Show verbose build output"
42
- option ["--clean"], :flag, "Clean before building"
43
- option ["--force"], :flag, "Force rebuild even if cached result exists"
44
-
45
- def execute
46
- if repository_url
47
- # Build specific repository
48
- build_repository(repository_url)
49
- else
50
- # No arguments - enumerate all tracked repositories and show latest commits
51
- enumerate_repositories
52
- end
53
- end
54
-
55
- private
56
-
57
- def enumerate_repositories
58
- puts "Enumerating all tracked repositories and their latest commit IDs..."
59
- puts "=" * 80
60
-
61
- repositories = load_tracked_repositories
62
-
63
- if repositories.empty?
64
- puts "No tracked repositories found."
65
- puts "Use 'makit repository add <url>' to add repositories to track."
66
- return
67
- end
68
-
69
- repositories.each_with_index do |repo, index|
70
- puts "\n#{(index + 1).to_s.rjust(3)}. #{repo["url"]}"
71
-
72
- begin
73
- clone_dir = get_clone_directory(repo["url"])
74
-
75
- if Dir.exist?(clone_dir)
76
- latest_commit = get_latest_commit_from_clone(clone_dir)
77
- commit_info = get_commit_info(clone_dir, latest_commit)
78
-
79
- puts " Status: ✅ Cloned"
80
- puts " Latest Commit: #{latest_commit[0..7]}"
81
- puts " Message: #{commit_info[:message]}" if commit_info[:message]
82
- puts " Author: #{commit_info[:author]}" if commit_info[:author]
83
- puts " Date: #{commit_info[:date]}" if commit_info[:date]
84
- else
85
- puts " Status: 📋 Tracked (not cloned)"
86
- puts " Use 'makit repository clone #{repo["url"]}' to clone locally"
87
- end
88
- rescue StandardError => e
89
- puts " Status: ❌ Error: #{e.message}"
90
- end
91
- end
92
-
93
- puts "\n=" * 80
94
- puts "Total repositories: #{repositories.length}"
95
- puts "\nTo build a specific repository: makit build make <repository-url>"
96
- end
97
-
98
- def build_repository(url)
99
- puts "Building repository: #{url}"
100
- puts "Commit: #{commit}"
101
- puts "Force rebuild: #{force?}" if force?
102
- puts "Verbose: #{verbose?}" if verbose?
103
-
104
- begin
105
- make_result = Makit.make(url, commit, force: force?)
106
-
107
- puts "\n✅ Build completed successfully!"
108
- puts "Repository: #{make_result.repository}"
109
- puts "Commit: #{make_result.commit}"
110
- puts "Device: #{make_result.device}"
111
- puts "Runtime: #{make_result.runtime_identifier}"
112
-
113
- if verbose? && make_result.commands.any?
114
- puts "\nCommand Summary:"
115
- make_result.commands.each_with_index do |cmd, index|
116
- status = cmd.exit_code.zero? ? "✅" : "❌"
117
- puts " #{index + 1}. #{status} #{cmd.name} #{cmd.arguments.join(" ")}"
118
- puts " Duration: #{cmd.duration.seconds}s" if cmd.duration
119
- puts " Error: #{cmd.error.strip}" if cmd.exit_code != 0 && cmd.error && !cmd.error.empty?
120
- end
121
- end
122
- rescue Makit::BuildError => e
123
- puts "\n❌ Build failed: #{e.message}"
124
- exit 1
125
- rescue StandardError => e
126
- puts "\n❌ Unexpected error: #{e.message}"
127
- puts e.backtrace.first(5).join("\n") if verbose?
128
- exit 1
129
- end
130
- end
131
-
132
- def load_tracked_repositories
133
- tracked_file = File.join(Makit::Directories::ROOT, "tracked_repositories.json")
134
- return [] unless File.exist?(tracked_file)
135
-
136
- begin
137
- JSON.parse(File.read(tracked_file))
138
- rescue JSON::ParserError => e
139
- puts "Warning: Could not parse tracked repositories file: #{e.message}"
140
- []
141
- end
142
- end
143
-
144
- def get_clone_directory(url)
145
- Makit::Directories.get_clone_directory(url)
146
- end
147
-
148
- def get_latest_commit_from_clone(clone_dir)
149
- Dir.chdir(clone_dir) do
150
- `git rev-parse HEAD`.strip
151
- end
152
- end
153
-
154
- def get_commit_info(clone_dir, commit_hash)
155
- Dir.chdir(clone_dir) do
156
- {
157
- message: `git log -1 --format="%s" #{commit_hash}`.strip,
158
- author: `git log -1 --format="%an" #{commit_hash}`.strip,
159
- date: `git log -1 --format="%ci" #{commit_hash}`.strip,
160
- }
161
- end
162
- rescue StandardError
163
- {}
164
- end
165
-
166
- def detect_project_type(dir)
167
- return "ruby" if File.exist?(File.join(dir, "Rakefile")) || File.exist?(File.join(dir, "Gemfile"))
168
- return "dotnet" if Dir.glob(File.join(dir, "*.csproj")).any? || Dir.glob(File.join(dir, "*.sln")).any?
169
- return "node" if File.exist?(File.join(dir, "package.json"))
170
- return "rust" if File.exist?(File.join(dir, "Cargo.toml"))
171
-
172
- "unknown"
173
- end
174
-
175
- def clean_if_requested
176
- puts "Cleaning before build..." if verbose?
177
-
178
- # Use the clean command internally
179
- clean_cmd = CleanBuildCommand.new("")
180
- clean_cmd.run([])
181
- end
182
-
183
- def build_project(project_type)
184
- case project_type
185
- when "ruby"
186
- build_ruby_project
187
- when "dotnet"
188
- build_dotnet_project
189
- when "node"
190
- build_node_project
191
- when "rust"
192
- build_rust_project
193
- end
194
- end
195
-
196
- def build_ruby_project
197
- puts " Building Ruby gem..." if verbose?
198
-
199
- # Check for Rakefile and run default task
200
- if File.exist?("Rakefile")
201
- cmd = "bundle exec rake"
202
- cmd += " --verbose" if verbose?
203
-
204
- puts " Running: #{cmd}" if verbose?
205
- success = system(cmd)
206
-
207
- unless success
208
- puts "❌ Rake build failed"
209
- exit 1
210
- end
211
- else
212
- # Fallback: just run bundle install and try to build gem
213
- puts " Installing dependencies..." if verbose?
214
- system("bundle install") if File.exist?("Gemfile")
215
-
216
- if Dir.glob("*.gemspec").any?
217
- puts " Building gem..." if verbose?
218
- system("gem build *.gemspec")
219
- end
220
- end
221
-
222
- puts " ✅ Ruby build complete" if verbose?
223
- end
224
-
225
- def build_dotnet_project
226
- puts " Building .NET project..." if verbose?
227
-
228
- build_args = []
229
- build_args << "--configuration #{target.capitalize}"
230
- build_args << "--verbosity detailed" if verbose?
231
-
232
- cmd = "dotnet build #{build_args.join(" ")}"
233
- puts " Running: #{cmd}" if verbose?
234
-
235
- success = system(cmd)
236
- unless success
237
- puts "❌ .NET build failed"
238
- exit 1
239
- end
240
-
241
- puts " ✅ .NET build complete" if verbose?
242
- end
243
-
244
- def build_node_project
245
- puts " Building Node.js project..." if verbose?
246
-
247
- # Install dependencies if needed
248
- if File.exist?("package.json") && !Dir.exist?("node_modules")
249
- puts " Installing dependencies..." if verbose?
250
- install_cmd = File.exist?("package-lock.json") ? "npm ci" : "npm install"
251
- system(install_cmd)
252
- end
253
-
254
- # Check for build script in package.json
255
- if File.exist?("package.json")
256
- package_data = JSON.parse(File.read("package.json"))
257
- scripts = package_data["scripts"] || {}
258
-
259
- if scripts["build"]
260
- puts " Running build script..." if verbose?
261
- cmd = "npm run build"
262
- cmd += " --verbose" if verbose?
263
-
264
- success = system(cmd)
265
- unless success
266
- puts "❌ npm build failed"
267
- exit 1
268
- end
269
- elsif verbose?
270
- puts " No build script found in package.json"
271
- end
272
- end
273
-
274
- puts " ✅ Node.js build complete" if verbose?
275
- end
276
-
277
- def build_rust_project
278
- puts " Building Rust project..." if verbose?
279
-
280
- build_args = []
281
- build_args << "--release" if target == "release"
282
- build_args << "--verbose" if verbose?
283
-
284
- cmd = "cargo build #{build_args.join(" ")}"
285
- puts " Running: #{cmd}" if verbose?
286
-
287
- success = system(cmd)
288
- unless success
289
- puts "❌ Cargo build failed"
290
- exit 1
291
- end
292
-
293
- puts " ✅ Rust build complete" if verbose?
294
- end
295
- end
296
-
297
- # Clean build artifacts and temporary files
298
- class CleanBuildCommand < Clamp::Command
299
- self.description = <<~DESC
300
- Clean build artifacts, temporary files, and caches.
301
-
302
- Examples:
303
- makit build clean
304
- makit build clean --deep
305
- makit build clean --dry-run
306
- DESC
307
-
308
- option ["--deep"], :flag, "Perform deep clean (including dependencies)"
309
- option ["--dry-run"], :flag, "Show what would be cleaned without actually cleaning"
310
- option ["--verbose", "-v"], :flag, "Show verbose output"
311
-
312
- def execute
313
- project_type = detect_project_type(Dir.pwd)
314
-
315
- puts "Cleaning #{project_type} project artifacts..."
316
- puts "Deep clean: #{deep?}" if verbose? && deep?
317
- puts "Dry run: #{dry_run?}" if verbose? && dry_run?
318
-
319
- clean_common_artifacts
320
- clean_project_specific_artifacts(project_type)
321
- clean_dependencies if deep?
322
-
323
- if dry_run?
324
- puts "✅ Dry run completed - no files were actually removed"
325
- else
326
- puts "✅ Clean completed successfully"
327
- end
328
- end
329
-
330
- private
331
-
332
- def detect_project_type(dir)
333
- return "ruby" if File.exist?(File.join(dir, "Rakefile")) || File.exist?(File.join(dir, "Gemfile"))
334
- return "dotnet" if Dir.glob(File.join(dir, "*.csproj")).any? || Dir.glob(File.join(dir, "*.sln")).any?
335
- return "node" if File.exist?(File.join(dir, "package.json"))
336
- return "rust" if File.exist?(File.join(dir, "Cargo.toml"))
337
-
338
- "unknown"
339
- end
340
-
341
- def clean_common_artifacts
342
- common_patterns = [
343
- "**/*.log",
344
- "**/tmp/**/*",
345
- "**/temp/**/*",
346
- "**/.DS_Store",
347
- "**/Thumbs.db",
348
- "**/*.tmp",
349
- "**/*.bak",
350
- "**/*.swp",
351
- "**/*.swo",
352
- ]
353
-
354
- common_patterns.each do |pattern|
355
- clean_by_pattern(pattern, "Common temp files")
356
- end
357
- end
358
-
359
- def clean_project_specific_artifacts(project_type)
360
- case project_type
361
- when "ruby"
362
- clean_ruby_artifacts
363
- when "dotnet"
364
- clean_dotnet_artifacts
365
- when "node"
366
- clean_node_artifacts
367
- when "rust"
368
- clean_rust_artifacts
369
- else
370
- puts " Unknown project type - only cleaning common artifacts" if verbose?
371
- end
372
- end
373
-
374
- def clean_ruby_artifacts
375
- # Clean gem files
376
- clean_by_pattern("*.gem", "Ruby gem files")
377
-
378
- # Clean pkg directory
379
- clean_directory("pkg", "Ruby package directory")
380
-
381
- # Clean coverage directory
382
- clean_directory("coverage", "Ruby coverage reports")
383
-
384
- # Clean bundler cache if deep clean
385
- clean_directory("vendor/bundle", "Bundler cache") if deep?
386
-
387
- puts " ✅ Ruby artifacts cleaned" if verbose?
388
- end
389
-
390
- def clean_dotnet_artifacts
391
- # Clean bin and obj directories
392
- clean_by_pattern("**/bin/**/*", ".NET bin directories")
393
- clean_by_pattern("**/obj/**/*", ".NET obj directories")
394
-
395
- # Clean test results
396
- clean_by_pattern("**/TestResults/**/*", ".NET test results")
397
-
398
- # Clean packages if deep clean
399
- clean_directory("packages", ".NET packages directory") if deep?
400
-
401
- puts " ✅ .NET artifacts cleaned" if verbose?
402
- end
403
-
404
- def clean_node_artifacts
405
- # Clean build directory
406
- clean_directory("build", "Node.js build directory")
407
- clean_directory("dist", "Node.js dist directory")
408
-
409
- # Clean coverage and test reports
410
- clean_directory("coverage", "Node.js coverage reports")
411
- clean_directory("nyc_output", "NYC coverage output")
412
-
413
- # Clean logs
414
- clean_by_pattern("npm-debug.log*", "npm debug logs")
415
- clean_by_pattern("yarn-debug.log*", "Yarn debug logs")
416
- clean_by_pattern("yarn-error.log*", "Yarn error logs")
417
-
418
- # Clean node_modules if deep clean
419
- clean_directory("node_modules", "Node.js dependencies") if deep?
420
-
421
- puts " ✅ Node.js artifacts cleaned" if verbose?
422
- end
423
-
424
- def clean_rust_artifacts
425
- # Clean target directory
426
- clean_directory("target", "Rust target directory")
427
-
428
- # Clean Cargo lock if deep clean
429
- clean_file("Cargo.lock", "Cargo lock file") if deep?
430
-
431
- puts " ✅ Rust artifacts cleaned" if verbose?
432
- end
433
-
434
- def clean_dependencies
435
- puts " Performing deep clean of dependencies..." if verbose?
436
-
437
- project_type = detect_project_type(Dir.pwd)
438
-
439
- case project_type
440
- when "ruby"
441
- clean_directory("vendor/bundle", "Ruby vendor directory")
442
- clean_file("Gemfile.lock", "Ruby Gemfile lock") if File.exist?("Gemfile.lock")
443
- when "dotnet"
444
- clean_directory("packages", ".NET packages")
445
- # NOTE: Don't clean packages.lock.json as it's source controlled
446
- when "node"
447
- clean_directory("node_modules", "Node.js modules")
448
- clean_file("package-lock.json", "npm lock file") if File.exist?("package-lock.json")
449
- clean_file("yarn.lock", "Yarn lock file") if File.exist?("yarn.lock")
450
- when "rust"
451
- # Rust doesn't really have a separate dependency directory to clean
452
- # Dependencies are in target/ which is already cleaned above
453
- puts " Rust dependencies are cleaned with target directory" if verbose?
454
- end
455
- end
456
-
457
- def clean_by_pattern(pattern, description)
458
- matching_files = Dir.glob(pattern)
459
-
460
- if matching_files.any?
461
- puts " Cleaning #{description}: #{matching_files.length} items" if verbose?
462
-
463
- unless dry_run?
464
- matching_files.each do |file|
465
- if File.directory?(file)
466
- FileUtils.rm_rf(file)
467
- else
468
- FileUtils.rm_f(file)
469
- end
470
- end
471
- end
472
- elsif verbose?
473
- puts " No #{description} found"
474
- end
475
- end
476
-
477
- def clean_directory(dir_path, description)
478
- if Dir.exist?(dir_path)
479
- puts " Cleaning #{description}: #{dir_path}" if verbose?
480
- FileUtils.rm_rf(dir_path) unless dry_run?
481
- elsif verbose?
482
- puts " #{description} not found: #{dir_path}"
483
- end
484
- end
485
-
486
- def clean_file(file_path, description)
487
- if File.exist?(file_path)
488
- puts " Cleaning #{description}: #{file_path}" if verbose?
489
- FileUtils.rm_f(file_path) unless dry_run?
490
- elsif verbose?
491
- puts " #{description} not found: #{file_path}"
492
- end
493
- end
494
- end
495
-
496
- # Add subcommand declarations after all classes are defined
497
- BuildCommand.subcommand "make", "Build project artifacts", MakeBuildCommand
498
- BuildCommand.subcommand "clean", "Clean build artifacts", CleanBuildCommand
499
- end
500
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "clamp"
4
+ require "fileutils"
5
+ require "json"
6
+
7
+ module Makit
8
+ module Cli
9
+ # Consolidated build operations commands
10
+ # Combines: make, clean functionality
11
+ class BuildCommand < Clamp::Command
12
+ self.description = <<~DESC
13
+ Manage project build operations and artifact cleanup.
14
+
15
+ Available operations:
16
+ make - Build project and generate artifacts
17
+ clean - Clean build artifacts and temporary files
18
+ DESC
19
+ end
20
+
21
+ # Build project artifacts
22
+ class MakeBuildCommand < Clamp::Command
23
+ self.description = <<~DESC
24
+ Build project artifacts and execute build tasks.
25
+
26
+ When called without arguments, enumerates all tracked repositories
27
+ and displays their latest commit IDs.
28
+
29
+ Examples:
30
+ makit build make # Show all repositories and latest commits
31
+ makit build make https://github.com/user/repo # Build specific repository
32
+ makit build make --target release # Build current project with release target
33
+ makit build make --parallel # Build with parallel execution
34
+ DESC
35
+
36
+ parameter "[REPOSITORY_URL]", "Git repository URL to build (optional)", attribute_name: :repository_url,
37
+ required: false
38
+ option ["--commit"], "COMMIT", "Specific commit to build (default: latest)", default: "latest"
39
+ option ["--target"], "TARGET", "Build target (debug, release)", default: "debug"
40
+ option ["--parallel", "-j"], :flag, "Enable parallel build"
41
+ option ["--verbose", "-v"], :flag, "Show verbose build output"
42
+ option ["--clean"], :flag, "Clean before building"
43
+ option ["--force"], :flag, "Force rebuild even if cached result exists"
44
+
45
+ def execute
46
+ if repository_url
47
+ # Build specific repository
48
+ build_repository(repository_url)
49
+ else
50
+ # No arguments - enumerate all tracked repositories and show latest commits
51
+ enumerate_repositories
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def enumerate_repositories
58
+ puts "Enumerating all tracked repositories and their latest commit IDs..."
59
+ puts "=" * 80
60
+
61
+ repositories = load_tracked_repositories
62
+
63
+ if repositories.empty?
64
+ puts "No tracked repositories found."
65
+ puts "Use 'makit repository add <url>' to add repositories to track."
66
+ return
67
+ end
68
+
69
+ repositories.each_with_index do |repo, index|
70
+ puts "\n#{(index + 1).to_s.rjust(3)}. #{repo["url"]}"
71
+
72
+ begin
73
+ clone_dir = get_clone_directory(repo["url"])
74
+
75
+ if Dir.exist?(clone_dir)
76
+ latest_commit = get_latest_commit_from_clone(clone_dir)
77
+ commit_info = get_commit_info(clone_dir, latest_commit)
78
+
79
+ puts " Status: ✅ Cloned"
80
+ puts " Latest Commit: #{latest_commit[0..7]}"
81
+ puts " Message: #{commit_info[:message]}" if commit_info[:message]
82
+ puts " Author: #{commit_info[:author]}" if commit_info[:author]
83
+ puts " Date: #{commit_info[:date]}" if commit_info[:date]
84
+ else
85
+ puts " Status: 📋 Tracked (not cloned)"
86
+ puts " Use 'makit repository clone #{repo["url"]}' to clone locally"
87
+ end
88
+ rescue StandardError => e
89
+ puts " Status: ❌ Error: #{e.message}"
90
+ end
91
+ end
92
+
93
+ puts "\n=" * 80
94
+ puts "Total repositories: #{repositories.length}"
95
+ puts "\nTo build a specific repository: makit build make <repository-url>"
96
+ end
97
+
98
+ def build_repository(url)
99
+ puts "Building repository: #{url}"
100
+ puts "Commit: #{commit}"
101
+ puts "Force rebuild: #{force?}" if force?
102
+ puts "Verbose: #{verbose?}" if verbose?
103
+
104
+ begin
105
+ make_result = Makit.make(url, commit, force: force?)
106
+
107
+ puts "\n✅ Build completed successfully!"
108
+ puts "Repository: #{make_result.repository}"
109
+ puts "Commit: #{make_result.commit}"
110
+ puts "Device: #{make_result.device}"
111
+ puts "Runtime: #{make_result.runtime_identifier}"
112
+
113
+ if verbose? && make_result.commands.any?
114
+ puts "\nCommand Summary:"
115
+ make_result.commands.each_with_index do |cmd, index|
116
+ status = cmd.exit_code.zero? ? "✅" : "❌"
117
+ puts " #{index + 1}. #{status} #{cmd.name} #{cmd.arguments.join(" ")}"
118
+ puts " Duration: #{cmd.duration.seconds}s" if cmd.duration
119
+ puts " Error: #{cmd.error.strip}" if cmd.exit_code != 0 && cmd.error && !cmd.error.empty?
120
+ end
121
+ end
122
+ rescue Makit::BuildError => e
123
+ puts "\n❌ Build failed: #{e.message}"
124
+ exit 1
125
+ rescue StandardError => e
126
+ puts "\n❌ Unexpected error: #{e.message}"
127
+ puts e.backtrace.first(5).join("\n") if verbose?
128
+ exit 1
129
+ end
130
+ end
131
+
132
+ def load_tracked_repositories
133
+ tracked_file = File.join(Makit::Directories::ROOT, "tracked_repositories.json")
134
+ return [] unless File.exist?(tracked_file)
135
+
136
+ begin
137
+ JSON.parse(File.read(tracked_file))
138
+ rescue JSON::ParserError => e
139
+ puts "Warning: Could not parse tracked repositories file: #{e.message}"
140
+ []
141
+ end
142
+ end
143
+
144
+ def get_clone_directory(url)
145
+ Makit::Directories.get_clone_directory(url)
146
+ end
147
+
148
+ def get_latest_commit_from_clone(clone_dir)
149
+ Dir.chdir(clone_dir) do
150
+ `git rev-parse HEAD`.strip
151
+ end
152
+ end
153
+
154
+ def get_commit_info(clone_dir, commit_hash)
155
+ Dir.chdir(clone_dir) do
156
+ {
157
+ message: `git log -1 --format="%s" #{commit_hash}`.strip,
158
+ author: `git log -1 --format="%an" #{commit_hash}`.strip,
159
+ date: `git log -1 --format="%ci" #{commit_hash}`.strip,
160
+ }
161
+ end
162
+ rescue StandardError
163
+ {}
164
+ end
165
+
166
+ def detect_project_type(dir)
167
+ return "ruby" if File.exist?(File.join(dir, "Rakefile")) || File.exist?(File.join(dir, "Gemfile"))
168
+ return "dotnet" if Dir.glob(File.join(dir, "*.csproj")).any? || Dir.glob(File.join(dir, "*.sln")).any?
169
+ return "node" if File.exist?(File.join(dir, "package.json"))
170
+ return "rust" if File.exist?(File.join(dir, "Cargo.toml"))
171
+
172
+ "unknown"
173
+ end
174
+
175
+ def clean_if_requested
176
+ puts "Cleaning before build..." if verbose?
177
+
178
+ # Use the clean command internally
179
+ clean_cmd = CleanBuildCommand.new("")
180
+ clean_cmd.run([])
181
+ end
182
+
183
+ def build_project(project_type)
184
+ case project_type
185
+ when "ruby"
186
+ build_ruby_project
187
+ when "dotnet"
188
+ build_dotnet_project
189
+ when "node"
190
+ build_node_project
191
+ when "rust"
192
+ build_rust_project
193
+ end
194
+ end
195
+
196
+ def build_ruby_project
197
+ puts " Building Ruby gem..." if verbose?
198
+
199
+ # Check for Rakefile and run default task
200
+ if File.exist?("Rakefile")
201
+ cmd = "bundle exec rake"
202
+ cmd += " --verbose" if verbose?
203
+
204
+ puts " Running: #{cmd}" if verbose?
205
+ success = system(cmd)
206
+
207
+ unless success
208
+ puts "❌ Rake build failed"
209
+ exit 1
210
+ end
211
+ else
212
+ # Fallback: just run bundle install and try to build gem
213
+ puts " Installing dependencies..." if verbose?
214
+ system("bundle install") if File.exist?("Gemfile")
215
+
216
+ if Dir.glob("*.gemspec").any?
217
+ puts " Building gem..." if verbose?
218
+ system("gem build *.gemspec")
219
+ end
220
+ end
221
+
222
+ puts " ✅ Ruby build complete" if verbose?
223
+ end
224
+
225
+ def build_dotnet_project
226
+ puts " Building .NET project..." if verbose?
227
+
228
+ build_args = []
229
+ build_args << "--configuration #{target.capitalize}"
230
+ build_args << "--verbosity detailed" if verbose?
231
+
232
+ cmd = "dotnet build #{build_args.join(" ")}"
233
+ puts " Running: #{cmd}" if verbose?
234
+
235
+ success = system(cmd)
236
+ unless success
237
+ puts "❌ .NET build failed"
238
+ exit 1
239
+ end
240
+
241
+ puts " ✅ .NET build complete" if verbose?
242
+ end
243
+
244
+ def build_node_project
245
+ puts " Building Node.js project..." if verbose?
246
+
247
+ # Install dependencies if needed
248
+ if File.exist?("package.json") && !Dir.exist?("node_modules")
249
+ puts " Installing dependencies..." if verbose?
250
+ install_cmd = File.exist?("package-lock.json") ? "npm ci" : "npm install"
251
+ system(install_cmd)
252
+ end
253
+
254
+ # Check for build script in package.json
255
+ if File.exist?("package.json")
256
+ package_data = JSON.parse(File.read("package.json"))
257
+ scripts = package_data["scripts"] || {}
258
+
259
+ if scripts["build"]
260
+ puts " Running build script..." if verbose?
261
+ cmd = "npm run build"
262
+ cmd += " --verbose" if verbose?
263
+
264
+ success = system(cmd)
265
+ unless success
266
+ puts "❌ npm build failed"
267
+ exit 1
268
+ end
269
+ elsif verbose?
270
+ puts " No build script found in package.json"
271
+ end
272
+ end
273
+
274
+ puts " ✅ Node.js build complete" if verbose?
275
+ end
276
+
277
+ def build_rust_project
278
+ puts " Building Rust project..." if verbose?
279
+
280
+ build_args = []
281
+ build_args << "--release" if target == "release"
282
+ build_args << "--verbose" if verbose?
283
+
284
+ cmd = "cargo build #{build_args.join(" ")}"
285
+ puts " Running: #{cmd}" if verbose?
286
+
287
+ success = system(cmd)
288
+ unless success
289
+ puts "❌ Cargo build failed"
290
+ exit 1
291
+ end
292
+
293
+ puts " ✅ Rust build complete" if verbose?
294
+ end
295
+ end
296
+
297
+ # Clean build artifacts and temporary files
298
+ class CleanBuildCommand < Clamp::Command
299
+ self.description = <<~DESC
300
+ Clean build artifacts, temporary files, and caches.
301
+
302
+ Examples:
303
+ makit build clean
304
+ makit build clean --deep
305
+ makit build clean --dry-run
306
+ DESC
307
+
308
+ option ["--deep"], :flag, "Perform deep clean (including dependencies)"
309
+ option ["--dry-run"], :flag, "Show what would be cleaned without actually cleaning"
310
+ option ["--verbose", "-v"], :flag, "Show verbose output"
311
+
312
+ def execute
313
+ project_type = detect_project_type(Dir.pwd)
314
+
315
+ puts "Cleaning #{project_type} project artifacts..."
316
+ puts "Deep clean: #{deep?}" if verbose? && deep?
317
+ puts "Dry run: #{dry_run?}" if verbose? && dry_run?
318
+
319
+ clean_common_artifacts
320
+ clean_project_specific_artifacts(project_type)
321
+ clean_dependencies if deep?
322
+
323
+ if dry_run?
324
+ puts "✅ Dry run completed - no files were actually removed"
325
+ else
326
+ puts "✅ Clean completed successfully"
327
+ end
328
+ end
329
+
330
+ private
331
+
332
+ def detect_project_type(dir)
333
+ return "ruby" if File.exist?(File.join(dir, "Rakefile")) || File.exist?(File.join(dir, "Gemfile"))
334
+ return "dotnet" if Dir.glob(File.join(dir, "*.csproj")).any? || Dir.glob(File.join(dir, "*.sln")).any?
335
+ return "node" if File.exist?(File.join(dir, "package.json"))
336
+ return "rust" if File.exist?(File.join(dir, "Cargo.toml"))
337
+
338
+ "unknown"
339
+ end
340
+
341
+ def clean_common_artifacts
342
+ common_patterns = [
343
+ "**/*.log",
344
+ "**/tmp/**/*",
345
+ "**/temp/**/*",
346
+ "**/.DS_Store",
347
+ "**/Thumbs.db",
348
+ "**/*.tmp",
349
+ "**/*.bak",
350
+ "**/*.swp",
351
+ "**/*.swo",
352
+ ]
353
+
354
+ common_patterns.each do |pattern|
355
+ clean_by_pattern(pattern, "Common temp files")
356
+ end
357
+ end
358
+
359
+ def clean_project_specific_artifacts(project_type)
360
+ case project_type
361
+ when "ruby"
362
+ clean_ruby_artifacts
363
+ when "dotnet"
364
+ clean_dotnet_artifacts
365
+ when "node"
366
+ clean_node_artifacts
367
+ when "rust"
368
+ clean_rust_artifacts
369
+ else
370
+ puts " Unknown project type - only cleaning common artifacts" if verbose?
371
+ end
372
+ end
373
+
374
+ def clean_ruby_artifacts
375
+ # Clean gem files
376
+ clean_by_pattern("*.gem", "Ruby gem files")
377
+
378
+ # Clean pkg directory
379
+ clean_directory("pkg", "Ruby package directory")
380
+
381
+ # Clean coverage directory
382
+ clean_directory("coverage", "Ruby coverage reports")
383
+
384
+ # Clean bundler cache if deep clean
385
+ clean_directory("vendor/bundle", "Bundler cache") if deep?
386
+
387
+ puts " ✅ Ruby artifacts cleaned" if verbose?
388
+ end
389
+
390
+ def clean_dotnet_artifacts
391
+ # Clean bin and obj directories
392
+ clean_by_pattern("**/bin/**/*", ".NET bin directories")
393
+ clean_by_pattern("**/obj/**/*", ".NET obj directories")
394
+
395
+ # Clean test results
396
+ clean_by_pattern("**/TestResults/**/*", ".NET test results")
397
+
398
+ # Clean packages if deep clean
399
+ clean_directory("packages", ".NET packages directory") if deep?
400
+
401
+ puts " ✅ .NET artifacts cleaned" if verbose?
402
+ end
403
+
404
+ def clean_node_artifacts
405
+ # Clean build directory
406
+ clean_directory("build", "Node.js build directory")
407
+ clean_directory("dist", "Node.js dist directory")
408
+
409
+ # Clean coverage and test reports
410
+ clean_directory("coverage", "Node.js coverage reports")
411
+ clean_directory("nyc_output", "NYC coverage output")
412
+
413
+ # Clean logs
414
+ clean_by_pattern("npm-debug.log*", "npm debug logs")
415
+ clean_by_pattern("yarn-debug.log*", "Yarn debug logs")
416
+ clean_by_pattern("yarn-error.log*", "Yarn error logs")
417
+
418
+ # Clean node_modules if deep clean
419
+ clean_directory("node_modules", "Node.js dependencies") if deep?
420
+
421
+ puts " ✅ Node.js artifacts cleaned" if verbose?
422
+ end
423
+
424
+ def clean_rust_artifacts
425
+ # Clean target directory
426
+ clean_directory("target", "Rust target directory")
427
+
428
+ # Clean Cargo lock if deep clean
429
+ clean_file("Cargo.lock", "Cargo lock file") if deep?
430
+
431
+ puts " ✅ Rust artifacts cleaned" if verbose?
432
+ end
433
+
434
+ def clean_dependencies
435
+ puts " Performing deep clean of dependencies..." if verbose?
436
+
437
+ project_type = detect_project_type(Dir.pwd)
438
+
439
+ case project_type
440
+ when "ruby"
441
+ clean_directory("vendor/bundle", "Ruby vendor directory")
442
+ clean_file("Gemfile.lock", "Ruby Gemfile lock") if File.exist?("Gemfile.lock")
443
+ when "dotnet"
444
+ clean_directory("packages", ".NET packages")
445
+ # NOTE: Don't clean packages.lock.json as it's source controlled
446
+ when "node"
447
+ clean_directory("node_modules", "Node.js modules")
448
+ clean_file("package-lock.json", "npm lock file") if File.exist?("package-lock.json")
449
+ clean_file("yarn.lock", "Yarn lock file") if File.exist?("yarn.lock")
450
+ when "rust"
451
+ # Rust doesn't really have a separate dependency directory to clean
452
+ # Dependencies are in target/ which is already cleaned above
453
+ puts " Rust dependencies are cleaned with target directory" if verbose?
454
+ end
455
+ end
456
+
457
+ def clean_by_pattern(pattern, description)
458
+ matching_files = Dir.glob(pattern)
459
+
460
+ if matching_files.any?
461
+ puts " Cleaning #{description}: #{matching_files.length} items" if verbose?
462
+
463
+ unless dry_run?
464
+ matching_files.each do |file|
465
+ if File.directory?(file)
466
+ FileUtils.rm_rf(file)
467
+ else
468
+ FileUtils.rm_f(file)
469
+ end
470
+ end
471
+ end
472
+ elsif verbose?
473
+ puts " No #{description} found"
474
+ end
475
+ end
476
+
477
+ def clean_directory(dir_path, description)
478
+ if Dir.exist?(dir_path)
479
+ puts " Cleaning #{description}: #{dir_path}" if verbose?
480
+ FileUtils.rm_rf(dir_path) unless dry_run?
481
+ elsif verbose?
482
+ puts " #{description} not found: #{dir_path}"
483
+ end
484
+ end
485
+
486
+ def clean_file(file_path, description)
487
+ if File.exist?(file_path)
488
+ puts " Cleaning #{description}: #{file_path}" if verbose?
489
+ FileUtils.rm_f(file_path) unless dry_run?
490
+ elsif verbose?
491
+ puts " #{description} not found: #{file_path}"
492
+ end
493
+ end
494
+ end
495
+
496
+ # Add subcommand declarations after all classes are defined
497
+ BuildCommand.subcommand "make", "Build project artifacts", MakeBuildCommand
498
+ BuildCommand.subcommand "clean", "Clean build artifacts", CleanBuildCommand
499
+ end
500
+ end