makit 0.0.164 → 0.0.165

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 (177) 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/azure/blob_storage.rb +257 -257
  7. data/lib/makit/azure/cli.rb +284 -284
  8. data/lib/makit/cli/base.rb +17 -17
  9. data/lib/makit/cli/build_commands.rb +500 -500
  10. data/lib/makit/cli/generators/base_generator.rb +74 -74
  11. data/lib/makit/cli/generators/dotnet_generator.rb +50 -50
  12. data/lib/makit/cli/generators/generator_factory.rb +49 -49
  13. data/lib/makit/cli/generators/node_generator.rb +50 -50
  14. data/lib/makit/cli/generators/ruby_generator.rb +77 -77
  15. data/lib/makit/cli/generators/rust_generator.rb +50 -50
  16. data/lib/makit/cli/generators/templates/dotnet_templates.rb +167 -167
  17. data/lib/makit/cli/generators/templates/node_templates.rb +161 -161
  18. data/lib/makit/cli/generators/templates/ruby/gemfile.rb +26 -26
  19. data/lib/makit/cli/generators/templates/ruby/gemspec.rb +41 -41
  20. data/lib/makit/cli/generators/templates/ruby/main_lib.rb +33 -33
  21. data/lib/makit/cli/generators/templates/ruby/rakefile.rb +35 -35
  22. data/lib/makit/cli/generators/templates/ruby/readme.rb +63 -63
  23. data/lib/makit/cli/generators/templates/ruby/test.rb +39 -39
  24. data/lib/makit/cli/generators/templates/ruby/test_helper.rb +29 -29
  25. data/lib/makit/cli/generators/templates/ruby/version.rb +29 -29
  26. data/lib/makit/cli/generators/templates/rust_templates.rb +128 -128
  27. data/lib/makit/cli/main.rb +78 -78
  28. data/lib/makit/cli/pipeline_commands.rb +311 -311
  29. data/lib/makit/cli/project_commands.rb +868 -868
  30. data/lib/makit/cli/repository_commands.rb +661 -661
  31. data/lib/makit/cli/strategy_commands.rb +207 -207
  32. data/lib/makit/cli/utility_commands.rb +521 -521
  33. data/lib/makit/commands/factory.rb +359 -359
  34. data/lib/makit/commands/middleware/base.rb +73 -73
  35. data/lib/makit/commands/middleware/cache.rb +248 -248
  36. data/lib/makit/commands/middleware/command_logger.rb +312 -312
  37. data/lib/makit/commands/middleware/validator.rb +269 -269
  38. data/lib/makit/commands/request.rb +316 -316
  39. data/lib/makit/commands/result.rb +323 -323
  40. data/lib/makit/commands/runner.rb +386 -386
  41. data/lib/makit/commands/strategies/base.rb +171 -171
  42. data/lib/makit/commands/strategies/child_process.rb +162 -162
  43. data/lib/makit/commands/strategies/factory.rb +136 -136
  44. data/lib/makit/commands/strategies/synchronous.rb +139 -139
  45. data/lib/makit/commands.rb +50 -50
  46. data/lib/makit/configuration/dotnet_project.rb +48 -48
  47. data/lib/makit/configuration/gitlab_helper.rb +61 -61
  48. data/lib/makit/configuration/project.rb +292 -292
  49. data/lib/makit/configuration/rakefile_helper.rb +43 -43
  50. data/lib/makit/configuration/step.rb +34 -34
  51. data/lib/makit/configuration/timeout.rb +74 -74
  52. data/lib/makit/configuration.rb +21 -21
  53. data/lib/makit/content/default_gitignore.rb +7 -7
  54. data/lib/makit/content/default_gitignore.txt +225 -225
  55. data/lib/makit/content/default_rakefile.rb +13 -13
  56. data/lib/makit/content/gem_rakefile.rb +16 -16
  57. data/lib/makit/context.rb +1 -1
  58. data/lib/makit/data.rb +49 -49
  59. data/lib/makit/directories.rb +170 -170
  60. data/lib/makit/directory.rb +262 -262
  61. data/lib/makit/docs/files.rb +89 -89
  62. data/lib/makit/docs/rake.rb +102 -102
  63. data/lib/makit/dotnet/cli.rb +224 -224
  64. data/lib/makit/dotnet/project.rb +217 -217
  65. data/lib/makit/dotnet/solution.rb +38 -38
  66. data/lib/makit/dotnet/solution_classlib.rb +239 -239
  67. data/lib/makit/dotnet/solution_console.rb +264 -264
  68. data/lib/makit/dotnet/solution_maui.rb +354 -354
  69. data/lib/makit/dotnet/solution_wasm.rb +275 -275
  70. data/lib/makit/dotnet/solution_wpf.rb +304 -304
  71. data/lib/makit/dotnet.rb +110 -110
  72. data/lib/makit/email.rb +90 -90
  73. data/lib/makit/environment.rb +142 -142
  74. data/lib/makit/examples/runner.rb +370 -370
  75. data/lib/makit/exceptions.rb +45 -45
  76. data/lib/makit/fileinfo.rb +32 -32
  77. data/lib/makit/files.rb +43 -43
  78. data/lib/makit/gems.rb +49 -49
  79. data/lib/makit/git/cli.rb +103 -103
  80. data/lib/makit/git/repository.rb +100 -100
  81. data/lib/makit/git.rb +104 -104
  82. data/lib/makit/gitlab/pipeline.rb +857 -857
  83. data/lib/makit/gitlab/pipeline_service_impl.rb +1535 -1535
  84. data/lib/makit/gitlab_runner.rb +59 -59
  85. data/lib/makit/humanize.rb +218 -218
  86. data/lib/makit/indexer.rb +47 -47
  87. data/lib/makit/io/filesystem.rb +111 -111
  88. data/lib/makit/io/filesystem_service_impl.rb +337 -337
  89. data/lib/makit/lint.rb +212 -212
  90. data/lib/makit/logging/configuration.rb +309 -309
  91. data/lib/makit/logging/format_registry.rb +84 -84
  92. data/lib/makit/logging/formatters/base.rb +39 -39
  93. data/lib/makit/logging/formatters/console_formatter.rb +140 -140
  94. data/lib/makit/logging/formatters/json_formatter.rb +65 -65
  95. data/lib/makit/logging/formatters/plain_text_formatter.rb +71 -71
  96. data/lib/makit/logging/formatters/text_formatter.rb +64 -64
  97. data/lib/makit/logging/log_request.rb +119 -119
  98. data/lib/makit/logging/logger.rb +199 -199
  99. data/lib/makit/logging/sinks/base.rb +91 -91
  100. data/lib/makit/logging/sinks/console.rb +72 -72
  101. data/lib/makit/logging/sinks/file_sink.rb +92 -92
  102. data/lib/makit/logging/sinks/structured.rb +123 -123
  103. data/lib/makit/logging/sinks/unified_file_sink.rb +296 -296
  104. data/lib/makit/logging.rb +578 -578
  105. data/lib/makit/markdown.rb +75 -75
  106. data/lib/makit/mp/basic_object_mp.rb +17 -17
  107. data/lib/makit/mp/command_mp.rb +13 -13
  108. data/lib/makit/mp/command_request.mp.rb +17 -17
  109. data/lib/makit/mp/project_mp.rb +199 -199
  110. data/lib/makit/mp/string_mp.rb +205 -205
  111. data/lib/makit/nuget.rb +454 -454
  112. data/lib/makit/podman/podman.rb +458 -458
  113. data/lib/makit/podman/podman_service_impl.rb +1081 -1081
  114. data/lib/makit/port.rb +32 -32
  115. data/lib/makit/process.rb +377 -377
  116. data/lib/makit/protoc.rb +112 -112
  117. data/lib/makit/rake/cli.rb +196 -196
  118. data/lib/makit/rake/trace_controller.rb +174 -174
  119. data/lib/makit/rake.rb +81 -81
  120. data/lib/makit/ruby/cli.rb +185 -185
  121. data/lib/makit/ruby.rb +25 -25
  122. data/lib/makit/rubygems.rb +137 -137
  123. data/lib/makit/secrets/azure_key_vault.rb +322 -322
  124. data/lib/makit/secrets/azure_secrets.rb +221 -183
  125. data/lib/makit/secrets/local_secrets.rb +72 -72
  126. data/lib/makit/secrets/secrets_manager.rb +105 -105
  127. data/lib/makit/secrets.rb +96 -16
  128. data/lib/makit/serializer.rb +130 -130
  129. data/lib/makit/services/builder.rb +186 -186
  130. data/lib/makit/services/error_handler.rb +226 -226
  131. data/lib/makit/services/repository_manager.rb +367 -367
  132. data/lib/makit/services/validator.rb +112 -112
  133. data/lib/makit/setup/classlib.rb +101 -101
  134. data/lib/makit/setup/gem.rb +268 -268
  135. data/lib/makit/setup/pages.rb +11 -11
  136. data/lib/makit/setup/razorclasslib.rb +101 -101
  137. data/lib/makit/setup/runner.rb +54 -54
  138. data/lib/makit/setup.rb +5 -5
  139. data/lib/makit/show.rb +110 -110
  140. data/lib/makit/storage.rb +126 -126
  141. data/lib/makit/symbols.rb +175 -175
  142. data/lib/makit/task_info.rb +130 -130
  143. data/lib/makit/tasks/at_exit.rb +15 -15
  144. data/lib/makit/tasks/build.rb +22 -22
  145. data/lib/makit/tasks/bump.rb +7 -7
  146. data/lib/makit/tasks/clean.rb +13 -13
  147. data/lib/makit/tasks/configure.rb +10 -10
  148. data/lib/makit/tasks/format.rb +10 -10
  149. data/lib/makit/tasks/hook_manager.rb +443 -443
  150. data/lib/makit/tasks/info.rb +368 -368
  151. data/lib/makit/tasks/init.rb +49 -49
  152. data/lib/makit/tasks/integrate.rb +60 -60
  153. data/lib/makit/tasks/pull_incoming.rb +13 -13
  154. data/lib/makit/tasks/secrets.rb +7 -7
  155. data/lib/makit/tasks/setup.rb +16 -16
  156. data/lib/makit/tasks/sync.rb +14 -14
  157. data/lib/makit/tasks/tag.rb +27 -27
  158. data/lib/makit/tasks/task_monkey_patch.rb +81 -81
  159. data/lib/makit/tasks/test.rb +22 -22
  160. data/lib/makit/tasks/update.rb +21 -21
  161. data/lib/makit/tasks/version.rb +6 -6
  162. data/lib/makit/tasks.rb +24 -24
  163. data/lib/makit/test_cache.rb +239 -239
  164. data/lib/makit/tree.rb +37 -37
  165. data/lib/makit/v1/configuration/project_service_impl.rb +370 -370
  166. data/lib/makit/v1/git/git_repository_service_impl.rb +295 -295
  167. data/lib/makit/v1/makit.v1_pb.rb +35 -35
  168. data/lib/makit/v1/makit.v1_services_pb.rb +27 -27
  169. data/lib/makit/v1/services/repository_manager_service_impl.rb +572 -572
  170. data/lib/makit/version.rb +661 -661
  171. data/lib/makit/version_util.rb +21 -21
  172. data/lib/makit/wix.rb +95 -95
  173. data/lib/makit/yaml.rb +29 -29
  174. data/lib/makit/zip.rb +17 -17
  175. data/lib/makit copy.rb +44 -44
  176. data/lib/makit.rb +119 -119
  177. metadata +2 -2
data/lib/makit/nuget.rb CHANGED
@@ -1,454 +1,454 @@
1
- # frozen_string_literal: true
2
-
3
- require "tmpdir"
4
- require "open3"
5
- require "fileutils"
6
- # This module provides classes for the Makit gem.
7
- module Makit
8
- # This class provide methods for working with the Nuget package cache
9
- #
10
- # Example:
11
- #
12
- # Makit::Directory.cache("Google.Protobuf", "3.27.2")
13
- #
14
- # dotnet nuget locals all --list
15
- # dotnet nuget locals all --clear
16
- #
17
- class NuGet
18
- def self.get_cache_dir(package, version)
19
- File.join(Makit::Directories::NUGET_PACKAGE_CACHE, package, version)
20
- end
21
-
22
- def self.cache(package, version)
23
- # if the package is already cached, there is nothing to do
24
- return if Dir.exist?(get_cache_dir(package, version))
25
-
26
- Dir.mktmpdir do |dir|
27
- # Use the temp directory here
28
- Dir.chdir(dir) do
29
- system("dotnet new classlib -n ClassLib")
30
- Dir.chdir("ClassLib") do
31
- # display a list of directories in the current directory
32
- puts Dir.entries(Dir.pwd)
33
- # display a list of files in the current directory
34
- puts Dir.entries(Dir.pwd)
35
- puts "dotnet add ClassLib.csproj package #{package} --version #{version}"
36
- system("dotnet add ClassLib.csproj package #{package} --version #{version}")
37
- end
38
- end
39
- # The directory and its contents will be removed automatically after the block
40
- end
41
- end
42
-
43
- def self.clear_cache(package, version)
44
- return unless Dir.exist?(get_cache_dir(package, version))
45
-
46
- FileUtils.rm_rf(get_cache_dir(package, version))
47
- end
48
-
49
- # get the latest version of the package
50
- def self.get_latest_version(package)
51
- dir = File.join(Makit::Directories::NUGET_PACKAGE_CACHE, package)
52
- if Dir.exist?(dir)
53
- versions = Dir.entries(dir).select do |entry|
54
- File.directory?(File.join(dir, entry)) && ![".", ".."].include?(entry)
55
- end
56
- highest_version = Makit::Version.get_highest_version(versions)
57
- return highest_version
58
- end
59
- nil
60
- end
61
-
62
- # publish a package to a nuget directory feed
63
- def self.add_package(_package, path)
64
- system("dotnet nuget push #{path} --source #{path}")
65
- end
66
-
67
- def self.publish_to_directory(nuget_package_path, directory, package_name, version)
68
- target_package_path = "#{directory}/#{package_name}/#{version}/#{package_name}.#{version}.nupkg".downcase
69
- if File.exist?(target_package_path)
70
- puts " #{target_package_path} already exists".colorize(:grey)
71
- else
72
- "dotnet nuget push #{nuget_package_path} --source #{directory}".run
73
- end
74
- end
75
-
76
- # -----------------------
77
- # NuGet Source Management
78
- # -----------------------
79
-
80
- # Lists all configured NuGet sources
81
- #
82
- # @return [Array<Hash>] Array of source hashes with keys: :name, :url, :enabled
83
- def self.list_sources
84
- stdout, stderr, status = Open3.capture3("dotnet", "nuget", "list", "source")
85
-
86
- unless status.success?
87
- Makit::Logging.default_logger.warn("Failed to list NuGet sources: #{stderr}")
88
- return []
89
- end
90
-
91
- sources = []
92
- current_source = nil
93
- lines = stdout.lines
94
-
95
- lines.each_with_index do |line, index|
96
- line_stripped = line.strip
97
- next if line_stripped.empty?
98
-
99
- # Skip header lines
100
- next if line_stripped.start_with?("Registered") || line_stripped.start_with?("---")
101
-
102
- # Parse numbered source lines like:
103
- # " 11. nuget.org [Enabled]"
104
- # " 1. nuget.org [Disabled]"
105
- match = line_stripped.match(/^\s*\d+\.\s+(.+?)(?:\s+\[(Enabled|Disabled)\])?$/)
106
- if match
107
- name_part = match[1]
108
- enabled_str = match[2]
109
-
110
- next if name_part.nil? || name_part.strip.empty?
111
-
112
- name = name_part.strip
113
-
114
- # Default to enabled if not specified
115
- enabled = enabled_str.nil? ? true : (enabled_str == "Enabled")
116
-
117
- # Create new source entry
118
- current_source = { name: name, url: nil, enabled: enabled }
119
- sources << current_source
120
-
121
- # Check next line for URL or path (indented line)
122
- if index + 1 < lines.length
123
- next_line = lines[index + 1]
124
- next_line_stripped = next_line.strip
125
-
126
- # Check if next line is indented (starts with whitespace) and contains URL or path
127
- if next_line.match?(/^\s+/) && !next_line_stripped.empty?
128
- # Check if it's a URL
129
- if next_line_stripped.match?(/^https?:\/\//)
130
- current_source[:url] = next_line_stripped
131
- # Check if it's a file path (Windows drive letter, UNC path, or Unix path)
132
- elsif next_line_stripped.match?(/^[A-Z]:[\\\/]/) || next_line_stripped.match?(/^\\\\/) || next_line_stripped.match?(/^\/[^\/]/)
133
- # For local sources, use the path as the "URL" for comparison purposes
134
- current_source[:url] = next_line_stripped
135
- end
136
- end
137
- end
138
- end
139
- end
140
-
141
- sources
142
- end
143
-
144
- # Checks if a NuGet source with the given name exists
145
- #
146
- # @param name [String] The source name to check
147
- # @return [Boolean] true if the source exists, false otherwise
148
- def self.has_source?(name)
149
- sources = list_sources
150
- sources.any? { |source| source[:name] == name }
151
- end
152
-
153
- # Adds a NuGet source
154
- #
155
- # @param name [String] The source name
156
- # @param url [String] The source URL
157
- # @param username [String, nil] Optional username for authenticated sources
158
- # @param password [String, nil] Optional password for authenticated sources
159
- # @param store_password_in_clear_text [Boolean] Whether to store password in clear text (default: false)
160
- # @return [Boolean] true if successful, false otherwise
161
- def self.add_source(name, url, username: nil, password: nil, store_password_in_clear_text: false)
162
- raise ArgumentError, "name is required" if name.nil? || name.strip.empty?
163
- raise ArgumentError, "url is required" if url.nil? || url.strip.empty?
164
-
165
- # Check if source already exists
166
- if has_source?(name)
167
- Makit::Logging.default_logger.info("NuGet source '#{name}' already exists")
168
- return false
169
- end
170
-
171
- args = ["dotnet", "nuget", "add", "source", url, "--name", name]
172
- args << "--username" << username if username
173
- args << "--password" << password if password
174
- args << "--store-password-in-clear-text" if store_password_in_clear_text
175
-
176
- stdout, stderr, status = Open3.capture3(*args)
177
-
178
- if status.success?
179
- Makit::Logging.default_logger.info("Added NuGet source '#{name}' (#{url})")
180
- true
181
- else
182
- Makit::Logging.default_logger.error("Failed to add NuGet source '#{name}': #{stderr}")
183
- false
184
- end
185
- end
186
-
187
- # Removes a NuGet source
188
- #
189
- # @param name [String] The source name to remove
190
- # @return [Boolean] true if successful, false otherwise
191
- def self.remove_source(name)
192
- raise ArgumentError, "name is required" if name.nil? || name.strip.empty?
193
-
194
- unless has_source?(name)
195
- Makit::Logging.default_logger.info("NuGet source '#{name}' does not exist")
196
- return false
197
- end
198
-
199
- stdout, stderr, status = Open3.capture3("dotnet", "nuget", "remove", "source", name)
200
-
201
- if status.success?
202
- Makit::Logging.default_logger.info("Removed NuGet source '#{name}'")
203
- true
204
- else
205
- Makit::Logging.default_logger.error("Failed to remove NuGet source '#{name}': #{stderr}")
206
- false
207
- end
208
- end
209
-
210
- # Configures a NuGet source (adds if it doesn't exist, updates if URL differs, no-op if already configured)
211
- #
212
- # @param name [String] The source name
213
- # @param url [String] The source URL
214
- # @param username [String, nil] Optional username for authenticated sources
215
- # @param password [String, nil] Optional password for authenticated sources
216
- # @param store_password_in_clear_text [Boolean] Whether to store password in clear text (default: false)
217
- # @return [Boolean] true if successful or already configured, false otherwise
218
- def self.configure_source(name, url, username: nil, password: nil, store_password_in_clear_text: false)
219
- raise ArgumentError, "name is required" if name.nil? || name.strip.empty?
220
- raise ArgumentError, "url is required" if url.nil? || url.strip.empty?
221
-
222
- # Check if source already exists
223
- if has_source?(name)
224
- # Get existing source details
225
- sources = list_sources
226
- existing_source = sources.find { |s| s[:name] == name }
227
-
228
- # If source exists with the same URL, it's already configured correctly (no-op)
229
- if existing_source && existing_source[:url] == url
230
- Makit::Logging.default_logger.info("NuGet source '#{name}' already exists with URL '#{url}', skipping configuration")
231
- return true
232
- end
233
-
234
- # Source exists but URL is different, update it
235
- Makit::Logging.default_logger.info("NuGet source '#{name}' already exists with different URL, updating...")
236
- remove_source(name)
237
- end
238
-
239
- # Add the source
240
- add_source(name, url, username: username, password: password, store_password_in_clear_text: store_password_in_clear_text)
241
- end
242
-
243
- # Migrates packages from one NuGet source to another, matching a pattern
244
- #
245
- # For local directory feeds, packages are found by scanning the directory structure:
246
- # {source_path}/{package_name}/{version}/{package_name}.{version}.nupkg
247
- #
248
- # @param source_name [String] The name of the source to migrate from
249
- # @param package_pattern [String] The package name pattern to match (e.g., "rep*", "MyPackage.*")
250
- # @param destination_name [String] The name of the destination source
251
- # @return [Integer] The number of packages migrated, or -1 if an error occurred
252
- def self.migrate_packages(source_name, package_pattern, destination_name)
253
- raise ArgumentError, "source_name is required" if source_name.nil? || source_name.strip.empty?
254
- raise ArgumentError, "package_pattern is required" if package_pattern.nil? || package_pattern.strip.empty?
255
- raise ArgumentError, "destination_name is required" if destination_name.nil? || destination_name.strip.empty?
256
-
257
- source_name = source_name.strip
258
- package_pattern = package_pattern.strip
259
- destination_name = destination_name.strip
260
-
261
- # Get source information
262
- sources = list_sources
263
- source_info = sources.find { |s| s[:name] == source_name }
264
- destination_info = sources.find { |s| s[:name] == destination_name }
265
-
266
- unless source_info
267
- Makit::Logging.default_logger.error("Source '#{source_name}' not found")
268
- return -1
269
- end
270
-
271
- unless destination_info
272
- Makit::Logging.default_logger.error("Destination source '#{destination_name}' not found")
273
- return -1
274
- end
275
-
276
- source_path = source_info[:url]
277
- destination_path = destination_info[:url]
278
-
279
- # Source must be a local directory feed (not a URL)
280
- unless source_path && !source_path.match?(/^https?:\/\//)
281
- Makit::Logging.default_logger.error("Source '#{source_name}' is not a local directory feed (URL: #{source_path})")
282
- return -1
283
- end
284
-
285
- # Expand and normalize the source path
286
- source_path = File.expand_path(source_path)
287
-
288
- # Destination can be either local directory feed or remote feed (Azure DevOps, NuGet.org, etc.)
289
- is_remote_destination = destination_path && destination_path.match?(/^https?:\/\//)
290
-
291
- # Ensure source directory exists
292
- unless Dir.exist?(source_path)
293
- Makit::Logging.default_logger.error("Source directory does not exist: #{source_path}")
294
- return -1
295
- end
296
-
297
- Makit::Logging.default_logger.info("Searching for packages matching '#{package_pattern}' in source '#{source_name}' (#{source_path})")
298
- Makit::Logging.default_logger.info("Destination: #{destination_name} (#{destination_path}) - #{is_remote_destination ? 'Remote feed' : 'Local directory feed'}")
299
-
300
- # Ensure destination directory exists (only for local feeds)
301
- unless is_remote_destination
302
- FileUtils.mkdir_p(destination_path) unless Dir.exist?(destination_path)
303
- end
304
-
305
- # Find all .nupkg files matching the pattern
306
- # Simply search recursively for all .nupkg files and match package names
307
- matching_packages = []
308
-
309
- # Glob for all .nupkg files recursively in the source directory
310
- Dir.glob(File.join(source_path, "**", "*.nupkg")) do |nupkg_file|
311
- # Extract package name from the file path
312
- # Path structure is typically: {source_path}/{package_name}/{version}/{package_name}.{version}.nupkg
313
- relative_path = nupkg_file.sub(/^#{Regexp.escape(source_path)}[\/\\]?/, "")
314
- path_parts = relative_path.split(/[\/\\]/)
315
-
316
- # Get package name from directory structure (first directory after source_path)
317
- package_name = path_parts.length >= 1 ? path_parts[0] : nil
318
-
319
- # If we can't get it from path, try to extract from filename
320
- if package_name.nil? || package_name.empty?
321
- filename = File.basename(nupkg_file, ".nupkg")
322
- # Try to extract package name by removing version suffix (e.g., "1.2.3" or "1.2.3-beta")
323
- if filename.match?(/^(.+?)\.(\d+\.\d+.*)$/)
324
- package_name = $1
325
- else
326
- package_name = filename
327
- end
328
- end
329
-
330
- # Check if package name matches pattern
331
- if package_name && File.fnmatch?(package_pattern, package_name, File::FNM_DOTMATCH)
332
- # Extract version from path (second directory) or from filename
333
- version = path_parts.length >= 2 ? path_parts[1] : nil
334
-
335
- # If we couldn't extract version from path, try from filename
336
- if version.nil? || version.empty?
337
- filename = File.basename(nupkg_file, ".nupkg")
338
- if filename.match?(/^.+?\.(\d+\.\d+.*)$/)
339
- version = $1
340
- else
341
- version = "unknown"
342
- end
343
- end
344
-
345
- matching_packages << {
346
- package_name: package_name,
347
- version: version,
348
- nupkg_path: nupkg_file
349
- }
350
- end
351
- end
352
-
353
- if matching_packages.empty?
354
- Makit::Logging.default_logger.info("No packages found matching pattern '#{package_pattern}' in source '#{source_name}'")
355
- return 0
356
- end
357
-
358
- Makit::Logging.default_logger.info("Found #{matching_packages.length} package(s) matching pattern '#{package_pattern}' in source '#{source_name}'")
359
-
360
- # Migrate each package
361
- migrated_count = 0
362
- failed_count = 0
363
-
364
- matching_packages.each do |package_info|
365
- package_name = package_info[:package_name]
366
- version = package_info[:version]
367
- nupkg_path = package_info[:nupkg_path]
368
-
369
- Makit::Logging.default_logger.info("Migrating #{package_name}.#{version} from '#{source_name}' to '#{destination_name}'...")
370
-
371
- if is_remote_destination
372
- # For remote feeds (Azure DevOps, NuGet.org, etc.), use dotnet nuget push
373
- # Azure DevOps requires --api-key az (placeholder) and --skip-duplicate
374
- Makit::Logging.default_logger.info(" Pushing #{package_name}.#{version} to remote feed...")
375
-
376
- args = ["dotnet", "nuget", "push", nupkg_path, "--source", destination_path, "--skip-duplicate"]
377
-
378
- # For Azure DevOps feeds, add --api-key az (required placeholder)
379
- if destination_path.include?("dev.azure.com") || destination_path.include?("pkgs.dev.azure.com")
380
- args << "--api-key" << "az"
381
- end
382
-
383
- stdout, stderr, status = Open3.capture3(*args)
384
-
385
- if status.success?
386
- Makit::Logging.default_logger.info(" Successfully migrated #{package_name}.#{version} to remote feed")
387
- migrated_count += 1
388
- else
389
- # Check if it's a duplicate (which is okay with --skip-duplicate)
390
- if stderr.include?("already exists") || stderr.include?("duplicate")
391
- Makit::Logging.default_logger.info(" Package already exists in destination, skipping: #{package_name}.#{version}")
392
- migrated_count += 1
393
- else
394
- Makit::Logging.default_logger.error(" Failed to migrate #{package_name}.#{version}: #{stderr}")
395
- failed_count += 1
396
- end
397
- end
398
- else
399
- # For local directory feeds, use 'nuget add' which automatically organizes packages
400
- # into the hierarchical structure: {packageID}/{version}/packageID.version.nupkg
401
- target_package_dir = File.join(destination_path, package_name.downcase, version)
402
- target_package_path = File.join(target_package_dir, File.basename(nupkg_path).downcase)
403
-
404
- if File.exist?(target_package_path)
405
- Makit::Logging.default_logger.info(" Package already exists in destination, skipping: #{package_name}.#{version}")
406
- migrated_count += 1
407
- next
408
- end
409
-
410
- stdout, stderr, status = Open3.capture3("nuget", "add", nupkg_path, "-Source", destination_path)
411
-
412
- if status.success?
413
- Makit::Logging.default_logger.info(" Successfully migrated and organized #{package_name}.#{version}")
414
- migrated_count += 1
415
- else
416
- # Fallback to dotnet nuget push if nuget.exe is not available
417
- nuget_exe = Makit::Environment.which("nuget")
418
- if nuget_exe.nil? || nuget_exe.empty?
419
- Makit::Logging.default_logger.info(" nuget.exe not found, falling back to dotnet nuget push")
420
-
421
- # Manually organize package into proper directory structure
422
- Makit::Logging.default_logger.info(" Organizing package into directory structure: #{target_package_dir}")
423
- FileUtils.mkdir_p(target_package_dir)
424
- FileUtils.cp(nupkg_path, target_package_path)
425
-
426
- # Push using dotnet nuget push from the organized location
427
- stdout, stderr, status = Open3.capture3("dotnet", "nuget", "push", target_package_path, "--source", destination_path)
428
-
429
- if status.success?
430
- Makit::Logging.default_logger.info(" Successfully migrated and organized #{package_name}.#{version}")
431
- migrated_count += 1
432
- else
433
- Makit::Logging.default_logger.warn(" Package copied to organized structure but dotnet nuget push failed: #{stderr}")
434
- Makit::Logging.default_logger.warn(" Package is available at #{target_package_path} but may need manual indexing")
435
- migrated_count += 1
436
- end
437
- else
438
- Makit::Logging.default_logger.error(" Failed to migrate #{package_name}.#{version}: #{stderr}")
439
- failed_count += 1
440
- end
441
- end
442
- end
443
- end
444
-
445
- if failed_count > 0
446
- Makit::Logging.default_logger.warn("Migration completed with #{failed_count} failure(s). #{migrated_count} package(s) migrated successfully.")
447
- else
448
- Makit::Logging.default_logger.info("Successfully migrated #{migrated_count} package(s) from '#{source_name}' to '#{destination_name}'")
449
- end
450
-
451
- migrated_count
452
- end
453
- end
454
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "tmpdir"
4
+ require "open3"
5
+ require "fileutils"
6
+ # This module provides classes for the Makit gem.
7
+ module Makit
8
+ # This class provide methods for working with the Nuget package cache
9
+ #
10
+ # Example:
11
+ #
12
+ # Makit::Directory.cache("Google.Protobuf", "3.27.2")
13
+ #
14
+ # dotnet nuget locals all --list
15
+ # dotnet nuget locals all --clear
16
+ #
17
+ class NuGet
18
+ def self.get_cache_dir(package, version)
19
+ File.join(Makit::Directories::NUGET_PACKAGE_CACHE, package, version)
20
+ end
21
+
22
+ def self.cache(package, version)
23
+ # if the package is already cached, there is nothing to do
24
+ return if Dir.exist?(get_cache_dir(package, version))
25
+
26
+ Dir.mktmpdir do |dir|
27
+ # Use the temp directory here
28
+ Dir.chdir(dir) do
29
+ system("dotnet new classlib -n ClassLib")
30
+ Dir.chdir("ClassLib") do
31
+ # display a list of directories in the current directory
32
+ puts Dir.entries(Dir.pwd)
33
+ # display a list of files in the current directory
34
+ puts Dir.entries(Dir.pwd)
35
+ puts "dotnet add ClassLib.csproj package #{package} --version #{version}"
36
+ system("dotnet add ClassLib.csproj package #{package} --version #{version}")
37
+ end
38
+ end
39
+ # The directory and its contents will be removed automatically after the block
40
+ end
41
+ end
42
+
43
+ def self.clear_cache(package, version)
44
+ return unless Dir.exist?(get_cache_dir(package, version))
45
+
46
+ FileUtils.rm_rf(get_cache_dir(package, version))
47
+ end
48
+
49
+ # get the latest version of the package
50
+ def self.get_latest_version(package)
51
+ dir = File.join(Makit::Directories::NUGET_PACKAGE_CACHE, package)
52
+ if Dir.exist?(dir)
53
+ versions = Dir.entries(dir).select do |entry|
54
+ File.directory?(File.join(dir, entry)) && ![".", ".."].include?(entry)
55
+ end
56
+ highest_version = Makit::Version.get_highest_version(versions)
57
+ return highest_version
58
+ end
59
+ nil
60
+ end
61
+
62
+ # publish a package to a nuget directory feed
63
+ def self.add_package(_package, path)
64
+ system("dotnet nuget push #{path} --source #{path}")
65
+ end
66
+
67
+ def self.publish_to_directory(nuget_package_path, directory, package_name, version)
68
+ target_package_path = "#{directory}/#{package_name}/#{version}/#{package_name}.#{version}.nupkg".downcase
69
+ if File.exist?(target_package_path)
70
+ puts " #{target_package_path} already exists".colorize(:grey)
71
+ else
72
+ "dotnet nuget push #{nuget_package_path} --source #{directory}".run
73
+ end
74
+ end
75
+
76
+ # -----------------------
77
+ # NuGet Source Management
78
+ # -----------------------
79
+
80
+ # Lists all configured NuGet sources
81
+ #
82
+ # @return [Array<Hash>] Array of source hashes with keys: :name, :url, :enabled
83
+ def self.list_sources
84
+ stdout, stderr, status = Open3.capture3("dotnet", "nuget", "list", "source")
85
+
86
+ unless status.success?
87
+ Makit::Logging.default_logger.warn("Failed to list NuGet sources: #{stderr}")
88
+ return []
89
+ end
90
+
91
+ sources = []
92
+ current_source = nil
93
+ lines = stdout.lines
94
+
95
+ lines.each_with_index do |line, index|
96
+ line_stripped = line.strip
97
+ next if line_stripped.empty?
98
+
99
+ # Skip header lines
100
+ next if line_stripped.start_with?("Registered") || line_stripped.start_with?("---")
101
+
102
+ # Parse numbered source lines like:
103
+ # " 11. nuget.org [Enabled]"
104
+ # " 1. nuget.org [Disabled]"
105
+ match = line_stripped.match(/^\s*\d+\.\s+(.+?)(?:\s+\[(Enabled|Disabled)\])?$/)
106
+ if match
107
+ name_part = match[1]
108
+ enabled_str = match[2]
109
+
110
+ next if name_part.nil? || name_part.strip.empty?
111
+
112
+ name = name_part.strip
113
+
114
+ # Default to enabled if not specified
115
+ enabled = enabled_str.nil? ? true : (enabled_str == "Enabled")
116
+
117
+ # Create new source entry
118
+ current_source = { name: name, url: nil, enabled: enabled }
119
+ sources << current_source
120
+
121
+ # Check next line for URL or path (indented line)
122
+ if index + 1 < lines.length
123
+ next_line = lines[index + 1]
124
+ next_line_stripped = next_line.strip
125
+
126
+ # Check if next line is indented (starts with whitespace) and contains URL or path
127
+ if next_line.match?(/^\s+/) && !next_line_stripped.empty?
128
+ # Check if it's a URL
129
+ if next_line_stripped.match?(/^https?:\/\//)
130
+ current_source[:url] = next_line_stripped
131
+ # Check if it's a file path (Windows drive letter, UNC path, or Unix path)
132
+ elsif next_line_stripped.match?(/^[A-Z]:[\\\/]/) || next_line_stripped.match?(/^\\\\/) || next_line_stripped.match?(/^\/[^\/]/)
133
+ # For local sources, use the path as the "URL" for comparison purposes
134
+ current_source[:url] = next_line_stripped
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
140
+
141
+ sources
142
+ end
143
+
144
+ # Checks if a NuGet source with the given name exists
145
+ #
146
+ # @param name [String] The source name to check
147
+ # @return [Boolean] true if the source exists, false otherwise
148
+ def self.has_source?(name)
149
+ sources = list_sources
150
+ sources.any? { |source| source[:name] == name }
151
+ end
152
+
153
+ # Adds a NuGet source
154
+ #
155
+ # @param name [String] The source name
156
+ # @param url [String] The source URL
157
+ # @param username [String, nil] Optional username for authenticated sources
158
+ # @param password [String, nil] Optional password for authenticated sources
159
+ # @param store_password_in_clear_text [Boolean] Whether to store password in clear text (default: false)
160
+ # @return [Boolean] true if successful, false otherwise
161
+ def self.add_source(name, url, username: nil, password: nil, store_password_in_clear_text: false)
162
+ raise ArgumentError, "name is required" if name.nil? || name.strip.empty?
163
+ raise ArgumentError, "url is required" if url.nil? || url.strip.empty?
164
+
165
+ # Check if source already exists
166
+ if has_source?(name)
167
+ Makit::Logging.default_logger.info("NuGet source '#{name}' already exists")
168
+ return false
169
+ end
170
+
171
+ args = ["dotnet", "nuget", "add", "source", url, "--name", name]
172
+ args << "--username" << username if username
173
+ args << "--password" << password if password
174
+ args << "--store-password-in-clear-text" if store_password_in_clear_text
175
+
176
+ stdout, stderr, status = Open3.capture3(*args)
177
+
178
+ if status.success?
179
+ Makit::Logging.default_logger.info("Added NuGet source '#{name}' (#{url})")
180
+ true
181
+ else
182
+ Makit::Logging.default_logger.error("Failed to add NuGet source '#{name}': #{stderr}")
183
+ false
184
+ end
185
+ end
186
+
187
+ # Removes a NuGet source
188
+ #
189
+ # @param name [String] The source name to remove
190
+ # @return [Boolean] true if successful, false otherwise
191
+ def self.remove_source(name)
192
+ raise ArgumentError, "name is required" if name.nil? || name.strip.empty?
193
+
194
+ unless has_source?(name)
195
+ Makit::Logging.default_logger.info("NuGet source '#{name}' does not exist")
196
+ return false
197
+ end
198
+
199
+ stdout, stderr, status = Open3.capture3("dotnet", "nuget", "remove", "source", name)
200
+
201
+ if status.success?
202
+ Makit::Logging.default_logger.info("Removed NuGet source '#{name}'")
203
+ true
204
+ else
205
+ Makit::Logging.default_logger.error("Failed to remove NuGet source '#{name}': #{stderr}")
206
+ false
207
+ end
208
+ end
209
+
210
+ # Configures a NuGet source (adds if it doesn't exist, updates if URL differs, no-op if already configured)
211
+ #
212
+ # @param name [String] The source name
213
+ # @param url [String] The source URL
214
+ # @param username [String, nil] Optional username for authenticated sources
215
+ # @param password [String, nil] Optional password for authenticated sources
216
+ # @param store_password_in_clear_text [Boolean] Whether to store password in clear text (default: false)
217
+ # @return [Boolean] true if successful or already configured, false otherwise
218
+ def self.configure_source(name, url, username: nil, password: nil, store_password_in_clear_text: false)
219
+ raise ArgumentError, "name is required" if name.nil? || name.strip.empty?
220
+ raise ArgumentError, "url is required" if url.nil? || url.strip.empty?
221
+
222
+ # Check if source already exists
223
+ if has_source?(name)
224
+ # Get existing source details
225
+ sources = list_sources
226
+ existing_source = sources.find { |s| s[:name] == name }
227
+
228
+ # If source exists with the same URL, it's already configured correctly (no-op)
229
+ if existing_source && existing_source[:url] == url
230
+ Makit::Logging.default_logger.info("NuGet source '#{name}' already exists with URL '#{url}', skipping configuration")
231
+ return true
232
+ end
233
+
234
+ # Source exists but URL is different, update it
235
+ Makit::Logging.default_logger.info("NuGet source '#{name}' already exists with different URL, updating...")
236
+ remove_source(name)
237
+ end
238
+
239
+ # Add the source
240
+ add_source(name, url, username: username, password: password, store_password_in_clear_text: store_password_in_clear_text)
241
+ end
242
+
243
+ # Migrates packages from one NuGet source to another, matching a pattern
244
+ #
245
+ # For local directory feeds, packages are found by scanning the directory structure:
246
+ # {source_path}/{package_name}/{version}/{package_name}.{version}.nupkg
247
+ #
248
+ # @param source_name [String] The name of the source to migrate from
249
+ # @param package_pattern [String] The package name pattern to match (e.g., "rep*", "MyPackage.*")
250
+ # @param destination_name [String] The name of the destination source
251
+ # @return [Integer] The number of packages migrated, or -1 if an error occurred
252
+ def self.migrate_packages(source_name, package_pattern, destination_name)
253
+ raise ArgumentError, "source_name is required" if source_name.nil? || source_name.strip.empty?
254
+ raise ArgumentError, "package_pattern is required" if package_pattern.nil? || package_pattern.strip.empty?
255
+ raise ArgumentError, "destination_name is required" if destination_name.nil? || destination_name.strip.empty?
256
+
257
+ source_name = source_name.strip
258
+ package_pattern = package_pattern.strip
259
+ destination_name = destination_name.strip
260
+
261
+ # Get source information
262
+ sources = list_sources
263
+ source_info = sources.find { |s| s[:name] == source_name }
264
+ destination_info = sources.find { |s| s[:name] == destination_name }
265
+
266
+ unless source_info
267
+ Makit::Logging.default_logger.error("Source '#{source_name}' not found")
268
+ return -1
269
+ end
270
+
271
+ unless destination_info
272
+ Makit::Logging.default_logger.error("Destination source '#{destination_name}' not found")
273
+ return -1
274
+ end
275
+
276
+ source_path = source_info[:url]
277
+ destination_path = destination_info[:url]
278
+
279
+ # Source must be a local directory feed (not a URL)
280
+ unless source_path && !source_path.match?(/^https?:\/\//)
281
+ Makit::Logging.default_logger.error("Source '#{source_name}' is not a local directory feed (URL: #{source_path})")
282
+ return -1
283
+ end
284
+
285
+ # Expand and normalize the source path
286
+ source_path = File.expand_path(source_path)
287
+
288
+ # Destination can be either local directory feed or remote feed (Azure DevOps, NuGet.org, etc.)
289
+ is_remote_destination = destination_path && destination_path.match?(/^https?:\/\//)
290
+
291
+ # Ensure source directory exists
292
+ unless Dir.exist?(source_path)
293
+ Makit::Logging.default_logger.error("Source directory does not exist: #{source_path}")
294
+ return -1
295
+ end
296
+
297
+ Makit::Logging.default_logger.info("Searching for packages matching '#{package_pattern}' in source '#{source_name}' (#{source_path})")
298
+ Makit::Logging.default_logger.info("Destination: #{destination_name} (#{destination_path}) - #{is_remote_destination ? 'Remote feed' : 'Local directory feed'}")
299
+
300
+ # Ensure destination directory exists (only for local feeds)
301
+ unless is_remote_destination
302
+ FileUtils.mkdir_p(destination_path) unless Dir.exist?(destination_path)
303
+ end
304
+
305
+ # Find all .nupkg files matching the pattern
306
+ # Simply search recursively for all .nupkg files and match package names
307
+ matching_packages = []
308
+
309
+ # Glob for all .nupkg files recursively in the source directory
310
+ Dir.glob(File.join(source_path, "**", "*.nupkg")) do |nupkg_file|
311
+ # Extract package name from the file path
312
+ # Path structure is typically: {source_path}/{package_name}/{version}/{package_name}.{version}.nupkg
313
+ relative_path = nupkg_file.sub(/^#{Regexp.escape(source_path)}[\/\\]?/, "")
314
+ path_parts = relative_path.split(/[\/\\]/)
315
+
316
+ # Get package name from directory structure (first directory after source_path)
317
+ package_name = path_parts.length >= 1 ? path_parts[0] : nil
318
+
319
+ # If we can't get it from path, try to extract from filename
320
+ if package_name.nil? || package_name.empty?
321
+ filename = File.basename(nupkg_file, ".nupkg")
322
+ # Try to extract package name by removing version suffix (e.g., "1.2.3" or "1.2.3-beta")
323
+ if filename.match?(/^(.+?)\.(\d+\.\d+.*)$/)
324
+ package_name = $1
325
+ else
326
+ package_name = filename
327
+ end
328
+ end
329
+
330
+ # Check if package name matches pattern
331
+ if package_name && File.fnmatch?(package_pattern, package_name, File::FNM_DOTMATCH)
332
+ # Extract version from path (second directory) or from filename
333
+ version = path_parts.length >= 2 ? path_parts[1] : nil
334
+
335
+ # If we couldn't extract version from path, try from filename
336
+ if version.nil? || version.empty?
337
+ filename = File.basename(nupkg_file, ".nupkg")
338
+ if filename.match?(/^.+?\.(\d+\.\d+.*)$/)
339
+ version = $1
340
+ else
341
+ version = "unknown"
342
+ end
343
+ end
344
+
345
+ matching_packages << {
346
+ package_name: package_name,
347
+ version: version,
348
+ nupkg_path: nupkg_file
349
+ }
350
+ end
351
+ end
352
+
353
+ if matching_packages.empty?
354
+ Makit::Logging.default_logger.info("No packages found matching pattern '#{package_pattern}' in source '#{source_name}'")
355
+ return 0
356
+ end
357
+
358
+ Makit::Logging.default_logger.info("Found #{matching_packages.length} package(s) matching pattern '#{package_pattern}' in source '#{source_name}'")
359
+
360
+ # Migrate each package
361
+ migrated_count = 0
362
+ failed_count = 0
363
+
364
+ matching_packages.each do |package_info|
365
+ package_name = package_info[:package_name]
366
+ version = package_info[:version]
367
+ nupkg_path = package_info[:nupkg_path]
368
+
369
+ Makit::Logging.default_logger.info("Migrating #{package_name}.#{version} from '#{source_name}' to '#{destination_name}'...")
370
+
371
+ if is_remote_destination
372
+ # For remote feeds (Azure DevOps, NuGet.org, etc.), use dotnet nuget push
373
+ # Azure DevOps requires --api-key az (placeholder) and --skip-duplicate
374
+ Makit::Logging.default_logger.info(" Pushing #{package_name}.#{version} to remote feed...")
375
+
376
+ args = ["dotnet", "nuget", "push", nupkg_path, "--source", destination_path, "--skip-duplicate"]
377
+
378
+ # For Azure DevOps feeds, add --api-key az (required placeholder)
379
+ if destination_path.include?("dev.azure.com") || destination_path.include?("pkgs.dev.azure.com")
380
+ args << "--api-key" << "az"
381
+ end
382
+
383
+ stdout, stderr, status = Open3.capture3(*args)
384
+
385
+ if status.success?
386
+ Makit::Logging.default_logger.info(" Successfully migrated #{package_name}.#{version} to remote feed")
387
+ migrated_count += 1
388
+ else
389
+ # Check if it's a duplicate (which is okay with --skip-duplicate)
390
+ if stderr.include?("already exists") || stderr.include?("duplicate")
391
+ Makit::Logging.default_logger.info(" Package already exists in destination, skipping: #{package_name}.#{version}")
392
+ migrated_count += 1
393
+ else
394
+ Makit::Logging.default_logger.error(" Failed to migrate #{package_name}.#{version}: #{stderr}")
395
+ failed_count += 1
396
+ end
397
+ end
398
+ else
399
+ # For local directory feeds, use 'nuget add' which automatically organizes packages
400
+ # into the hierarchical structure: {packageID}/{version}/packageID.version.nupkg
401
+ target_package_dir = File.join(destination_path, package_name.downcase, version)
402
+ target_package_path = File.join(target_package_dir, File.basename(nupkg_path).downcase)
403
+
404
+ if File.exist?(target_package_path)
405
+ Makit::Logging.default_logger.info(" Package already exists in destination, skipping: #{package_name}.#{version}")
406
+ migrated_count += 1
407
+ next
408
+ end
409
+
410
+ stdout, stderr, status = Open3.capture3("nuget", "add", nupkg_path, "-Source", destination_path)
411
+
412
+ if status.success?
413
+ Makit::Logging.default_logger.info(" Successfully migrated and organized #{package_name}.#{version}")
414
+ migrated_count += 1
415
+ else
416
+ # Fallback to dotnet nuget push if nuget.exe is not available
417
+ nuget_exe = Makit::Environment.which("nuget")
418
+ if nuget_exe.nil? || nuget_exe.empty?
419
+ Makit::Logging.default_logger.info(" nuget.exe not found, falling back to dotnet nuget push")
420
+
421
+ # Manually organize package into proper directory structure
422
+ Makit::Logging.default_logger.info(" Organizing package into directory structure: #{target_package_dir}")
423
+ FileUtils.mkdir_p(target_package_dir)
424
+ FileUtils.cp(nupkg_path, target_package_path)
425
+
426
+ # Push using dotnet nuget push from the organized location
427
+ stdout, stderr, status = Open3.capture3("dotnet", "nuget", "push", target_package_path, "--source", destination_path)
428
+
429
+ if status.success?
430
+ Makit::Logging.default_logger.info(" Successfully migrated and organized #{package_name}.#{version}")
431
+ migrated_count += 1
432
+ else
433
+ Makit::Logging.default_logger.warn(" Package copied to organized structure but dotnet nuget push failed: #{stderr}")
434
+ Makit::Logging.default_logger.warn(" Package is available at #{target_package_path} but may need manual indexing")
435
+ migrated_count += 1
436
+ end
437
+ else
438
+ Makit::Logging.default_logger.error(" Failed to migrate #{package_name}.#{version}: #{stderr}")
439
+ failed_count += 1
440
+ end
441
+ end
442
+ end
443
+ end
444
+
445
+ if failed_count > 0
446
+ Makit::Logging.default_logger.warn("Migration completed with #{failed_count} failure(s). #{migrated_count} package(s) migrated successfully.")
447
+ else
448
+ Makit::Logging.default_logger.info("Successfully migrated #{migrated_count} package(s) from '#{source_name}' to '#{destination_name}'")
449
+ end
450
+
451
+ migrated_count
452
+ end
453
+ end
454
+ end