makit 0.0.167 → 0.0.169

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 (179) 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/azure-pipelines.rb +187 -187
  9. data/lib/makit/cli/base.rb +17 -17
  10. data/lib/makit/cli/build_commands.rb +500 -500
  11. data/lib/makit/cli/generators/base_generator.rb +74 -74
  12. data/lib/makit/cli/generators/dotnet_generator.rb +50 -50
  13. data/lib/makit/cli/generators/generator_factory.rb +49 -49
  14. data/lib/makit/cli/generators/node_generator.rb +50 -50
  15. data/lib/makit/cli/generators/ruby_generator.rb +77 -77
  16. data/lib/makit/cli/generators/rust_generator.rb +50 -50
  17. data/lib/makit/cli/generators/templates/dotnet_templates.rb +167 -167
  18. data/lib/makit/cli/generators/templates/node_templates.rb +161 -161
  19. data/lib/makit/cli/generators/templates/ruby/gemfile.rb +26 -26
  20. data/lib/makit/cli/generators/templates/ruby/gemspec.rb +41 -41
  21. data/lib/makit/cli/generators/templates/ruby/main_lib.rb +33 -33
  22. data/lib/makit/cli/generators/templates/ruby/rakefile.rb +35 -35
  23. data/lib/makit/cli/generators/templates/ruby/readme.rb +63 -63
  24. data/lib/makit/cli/generators/templates/ruby/test.rb +39 -39
  25. data/lib/makit/cli/generators/templates/ruby/test_helper.rb +29 -29
  26. data/lib/makit/cli/generators/templates/ruby/version.rb +29 -29
  27. data/lib/makit/cli/generators/templates/rust_templates.rb +128 -128
  28. data/lib/makit/cli/main.rb +78 -78
  29. data/lib/makit/cli/pipeline_commands.rb +311 -311
  30. data/lib/makit/cli/project_commands.rb +868 -868
  31. data/lib/makit/cli/repository_commands.rb +661 -661
  32. data/lib/makit/cli/strategy_commands.rb +207 -207
  33. data/lib/makit/cli/utility_commands.rb +521 -521
  34. data/lib/makit/commands/factory.rb +359 -359
  35. data/lib/makit/commands/middleware/base.rb +73 -73
  36. data/lib/makit/commands/middleware/cache.rb +248 -248
  37. data/lib/makit/commands/middleware/command_logger.rb +312 -312
  38. data/lib/makit/commands/middleware/validator.rb +269 -269
  39. data/lib/makit/commands/request.rb +316 -316
  40. data/lib/makit/commands/result.rb +323 -323
  41. data/lib/makit/commands/runner.rb +386 -386
  42. data/lib/makit/commands/strategies/base.rb +171 -171
  43. data/lib/makit/commands/strategies/child_process.rb +162 -162
  44. data/lib/makit/commands/strategies/factory.rb +136 -136
  45. data/lib/makit/commands/strategies/synchronous.rb +139 -139
  46. data/lib/makit/commands.rb +50 -50
  47. data/lib/makit/configuration/dotnet_project.rb +48 -48
  48. data/lib/makit/configuration/gitlab_helper.rb +61 -61
  49. data/lib/makit/configuration/project.rb +292 -292
  50. data/lib/makit/configuration/rakefile_helper.rb +43 -43
  51. data/lib/makit/configuration/step.rb +34 -34
  52. data/lib/makit/configuration/timeout.rb +74 -74
  53. data/lib/makit/configuration.rb +21 -21
  54. data/lib/makit/content/default_gitignore.rb +7 -7
  55. data/lib/makit/content/default_gitignore.txt +225 -225
  56. data/lib/makit/content/default_rakefile.rb +13 -13
  57. data/lib/makit/content/gem_rakefile.rb +16 -16
  58. data/lib/makit/context.rb +1 -1
  59. data/lib/makit/data.rb +49 -49
  60. data/lib/makit/directories.rb +170 -170
  61. data/lib/makit/directory.rb +262 -262
  62. data/lib/makit/docs/files.rb +89 -89
  63. data/lib/makit/docs/rake.rb +102 -102
  64. data/lib/makit/dotnet/cli.rb +224 -224
  65. data/lib/makit/dotnet/project.rb +217 -217
  66. data/lib/makit/dotnet/solution.rb +38 -38
  67. data/lib/makit/dotnet/solution_classlib.rb +239 -239
  68. data/lib/makit/dotnet/solution_console.rb +264 -264
  69. data/lib/makit/dotnet/solution_maui.rb +354 -354
  70. data/lib/makit/dotnet/solution_wasm.rb +275 -275
  71. data/lib/makit/dotnet/solution_wpf.rb +304 -304
  72. data/lib/makit/dotnet.rb +110 -110
  73. data/lib/makit/email.rb +90 -90
  74. data/lib/makit/environment.rb +142 -142
  75. data/lib/makit/examples/runner.rb +370 -370
  76. data/lib/makit/exceptions.rb +45 -45
  77. data/lib/makit/fileinfo.rb +32 -32
  78. data/lib/makit/files.rb +43 -43
  79. data/lib/makit/gems.rb +49 -49
  80. data/lib/makit/git/cli.rb +103 -103
  81. data/lib/makit/git/repository.rb +100 -100
  82. data/lib/makit/git.rb +104 -104
  83. data/lib/makit/github_actions.rb +202 -0
  84. data/lib/makit/gitlab/pipeline.rb +857 -857
  85. data/lib/makit/gitlab/pipeline_service_impl.rb +1535 -1535
  86. data/lib/makit/gitlab_runner.rb +59 -59
  87. data/lib/makit/humanize.rb +218 -218
  88. data/lib/makit/indexer.rb +47 -47
  89. data/lib/makit/io/filesystem.rb +111 -111
  90. data/lib/makit/io/filesystem_service_impl.rb +337 -337
  91. data/lib/makit/lint.rb +212 -212
  92. data/lib/makit/logging/configuration.rb +309 -309
  93. data/lib/makit/logging/format_registry.rb +84 -84
  94. data/lib/makit/logging/formatters/base.rb +39 -39
  95. data/lib/makit/logging/formatters/console_formatter.rb +140 -140
  96. data/lib/makit/logging/formatters/json_formatter.rb +65 -65
  97. data/lib/makit/logging/formatters/plain_text_formatter.rb +71 -71
  98. data/lib/makit/logging/formatters/text_formatter.rb +64 -64
  99. data/lib/makit/logging/log_request.rb +119 -119
  100. data/lib/makit/logging/logger.rb +199 -199
  101. data/lib/makit/logging/sinks/base.rb +91 -91
  102. data/lib/makit/logging/sinks/console.rb +72 -72
  103. data/lib/makit/logging/sinks/file_sink.rb +92 -92
  104. data/lib/makit/logging/sinks/structured.rb +123 -123
  105. data/lib/makit/logging/sinks/unified_file_sink.rb +296 -296
  106. data/lib/makit/logging.rb +578 -578
  107. data/lib/makit/markdown.rb +75 -75
  108. data/lib/makit/mp/basic_object_mp.rb +17 -17
  109. data/lib/makit/mp/command_mp.rb +13 -13
  110. data/lib/makit/mp/command_request.mp.rb +17 -17
  111. data/lib/makit/mp/project_mp.rb +199 -199
  112. data/lib/makit/mp/string_mp.rb +205 -205
  113. data/lib/makit/nuget.rb +460 -454
  114. data/lib/makit/podman/podman.rb +458 -458
  115. data/lib/makit/podman/podman_service_impl.rb +1081 -1081
  116. data/lib/makit/port.rb +32 -32
  117. data/lib/makit/process.rb +377 -377
  118. data/lib/makit/protoc.rb +112 -112
  119. data/lib/makit/rake/cli.rb +196 -196
  120. data/lib/makit/rake/trace_controller.rb +174 -174
  121. data/lib/makit/rake.rb +81 -81
  122. data/lib/makit/ruby/cli.rb +185 -185
  123. data/lib/makit/ruby.rb +25 -25
  124. data/lib/makit/rubygems.rb +137 -137
  125. data/lib/makit/secrets/azure_key_vault.rb +322 -322
  126. data/lib/makit/secrets/azure_secrets.rb +221 -221
  127. data/lib/makit/secrets/local_secrets.rb +72 -72
  128. data/lib/makit/secrets/secrets_manager.rb +105 -105
  129. data/lib/makit/secrets.rb +96 -96
  130. data/lib/makit/serializer.rb +130 -130
  131. data/lib/makit/services/builder.rb +186 -186
  132. data/lib/makit/services/error_handler.rb +226 -226
  133. data/lib/makit/services/repository_manager.rb +367 -367
  134. data/lib/makit/services/validator.rb +112 -112
  135. data/lib/makit/setup/classlib.rb +101 -101
  136. data/lib/makit/setup/gem.rb +268 -268
  137. data/lib/makit/setup/pages.rb +11 -11
  138. data/lib/makit/setup/razorclasslib.rb +101 -101
  139. data/lib/makit/setup/runner.rb +54 -54
  140. data/lib/makit/setup.rb +5 -5
  141. data/lib/makit/show.rb +110 -110
  142. data/lib/makit/storage.rb +126 -126
  143. data/lib/makit/symbols.rb +175 -175
  144. data/lib/makit/task_info.rb +130 -130
  145. data/lib/makit/tasks/at_exit.rb +15 -15
  146. data/lib/makit/tasks/build.rb +22 -22
  147. data/lib/makit/tasks/bump.rb +7 -7
  148. data/lib/makit/tasks/clean.rb +13 -13
  149. data/lib/makit/tasks/configure.rb +10 -10
  150. data/lib/makit/tasks/format.rb +10 -10
  151. data/lib/makit/tasks/hook_manager.rb +443 -443
  152. data/lib/makit/tasks/info.rb +368 -368
  153. data/lib/makit/tasks/init.rb +49 -49
  154. data/lib/makit/tasks/integrate.rb +60 -60
  155. data/lib/makit/tasks/pull_incoming.rb +13 -13
  156. data/lib/makit/tasks/secrets.rb +7 -7
  157. data/lib/makit/tasks/setup.rb +16 -16
  158. data/lib/makit/tasks/sync.rb +14 -14
  159. data/lib/makit/tasks/tag.rb +27 -27
  160. data/lib/makit/tasks/task_monkey_patch.rb +81 -81
  161. data/lib/makit/tasks/test.rb +22 -22
  162. data/lib/makit/tasks/update.rb +21 -21
  163. data/lib/makit/tasks/version.rb +6 -6
  164. data/lib/makit/tasks.rb +24 -24
  165. data/lib/makit/test_cache.rb +239 -239
  166. data/lib/makit/tree.rb +37 -37
  167. data/lib/makit/v1/configuration/project_service_impl.rb +370 -370
  168. data/lib/makit/v1/git/git_repository_service_impl.rb +295 -295
  169. data/lib/makit/v1/makit.v1_pb.rb +35 -35
  170. data/lib/makit/v1/makit.v1_services_pb.rb +27 -27
  171. data/lib/makit/v1/services/repository_manager_service_impl.rb +572 -572
  172. data/lib/makit/version.rb +661 -661
  173. data/lib/makit/version_util.rb +21 -21
  174. data/lib/makit/wix.rb +95 -95
  175. data/lib/makit/yaml.rb +29 -29
  176. data/lib/makit/zip.rb +17 -17
  177. data/lib/makit copy.rb +44 -44
  178. data/lib/makit.rb +121 -120
  179. metadata +3 -2
@@ -1,309 +1,309 @@
1
- # frozen_string_literal: true
2
-
3
- module Makit
4
- module Logging
5
- # Configuration management for unified logging
6
- #
7
- # Provides schema validation, default configurations, and helper methods
8
- # for setting up the unified file sink with various output configurations.
9
- #
10
- # @example Basic configuration
11
- # config = Configuration.new(
12
- # configurations: [
13
- # { file: $stdout, format: :console },
14
- # { file: "logs/app.log", format: :json }
15
- # ]
16
- # )
17
- #
18
- # @example YAML configuration
19
- # config = Configuration.from_yaml("config/logging.yml")
20
- #
21
- # @example Environment-based configuration
22
- # config = Configuration.for_environment(:production)
23
- class Configuration
24
- # @return [Array<Hash>] list of output configurations
25
- attr_reader :configurations
26
-
27
- # Initialize configuration
28
- #
29
- # @param configurations [Array<Hash>] list of output configurations
30
- def initialize(configurations: [])
31
- @configurations = configurations
32
- end
33
-
34
- # Create configuration from YAML file
35
- #
36
- # @param yaml_file [String] path to YAML configuration file
37
- # @return [Configuration] new configuration instance
38
- def self.from_yaml(yaml_file)
39
- require "yaml"
40
- data = YAML.load_file(yaml_file)
41
- configurations = data["logging"]["sinks"] || []
42
-
43
- # Convert string keys to symbols
44
- configurations = configurations.map do |config|
45
- config.transform_keys(&:to_sym)
46
- end
47
-
48
- new(configurations: configurations)
49
- end
50
-
51
- # Create configuration from JSON file
52
- #
53
- # @param json_file [String] path to JSON configuration file
54
- # @return [Configuration] new configuration instance
55
- def self.from_json(json_file)
56
- require "json"
57
- data = JSON.parse(File.read(json_file))
58
- configurations = data["logging"]["sinks"] || []
59
-
60
- # Convert string keys to symbols
61
- configurations = configurations.map do |config|
62
- config.transform_keys(&:to_sym)
63
- end
64
-
65
- new(configurations: configurations)
66
- end
67
-
68
- # Create environment-specific configuration
69
- #
70
- # @param environment [Symbol] environment name (:development, :production, :test)
71
- # @return [Configuration] new configuration instance
72
- def self.for_environment(environment)
73
- case environment
74
- when :development
75
- development_config
76
- when :production
77
- production_config
78
- when :test
79
- test_config
80
- when :ci
81
- ci_config
82
- else
83
- default_config
84
- end
85
- end
86
-
87
- # Get default configuration
88
- #
89
- # @return [Configuration] default configuration
90
- def self.default_config
91
- require_relative "../logging" unless defined?(Makit::Logging)
92
- new(
93
- configurations: [
94
- { file: $stdout, format: :console },
95
- { file: Makit::Logging.log_file_path("makit.log"), format: :json, append: true },
96
- ],
97
- )
98
- end
99
-
100
- # Get development configuration
101
- #
102
- # @return [Configuration] development configuration
103
- def self.development_config
104
- new(
105
- configurations: [
106
- {
107
- file: $stdout,
108
- format: :console,
109
- show_timestamp: true,
110
- show_level: true,
111
- },
112
- {
113
- file: "logs/development.log",
114
- format: :text,
115
- append: true,
116
- include_context: true,
117
- },
118
- {
119
- file: "logs/debug.log",
120
- format: :json,
121
- append: true,
122
- min_level: :debug,
123
- },
124
- ],
125
- )
126
- end
127
-
128
- # Get production configuration
129
- #
130
- # @return [Configuration] production configuration
131
- def self.production_config
132
- new(
133
- configurations: [
134
- {
135
- file: "logs/application.log",
136
- format: :json,
137
- append: true,
138
- include_metadata: true,
139
- rotation: { max_size: "100MB", max_files: 10 },
140
- },
141
- {
142
- file: "logs/errors.log",
143
- format: :json,
144
- append: true,
145
- min_level: :error,
146
- include_metadata: true,
147
- },
148
- ],
149
- )
150
- end
151
-
152
- # Get test configuration
153
- #
154
- # @return [Configuration] test configuration
155
- def self.test_config
156
- new(
157
- configurations: [
158
- {
159
- file: "logs/test.log",
160
- format: :text,
161
- append: false,
162
- include_context: true,
163
- },
164
- ],
165
- )
166
- end
167
-
168
- # Get CI/CD configuration
169
- #
170
- # @return [Configuration] CI configuration
171
- def self.ci_config
172
- new(
173
- configurations: [
174
- {
175
- file: $stdout,
176
- format: :console,
177
- show_level: true,
178
- },
179
- {
180
- file: "artifacts/build.log",
181
- format: :json,
182
- append: true,
183
- include_metadata: true,
184
- },
185
- ],
186
- )
187
- end
188
-
189
- # Validate configuration
190
- #
191
- # @return [Boolean] true if valid
192
- # @raise [ArgumentError] if configuration is invalid
193
- def validate!
194
- raise ArgumentError, "configurations must be an array" unless @configurations.is_a?(Array)
195
- raise ArgumentError, "at least one configuration is required" if @configurations.empty?
196
-
197
- @configurations.each_with_index do |config, index|
198
- validate_single_configuration(config, index)
199
- end
200
-
201
- true
202
- end
203
-
204
- # Check if configuration is valid
205
- #
206
- # @return [Boolean] true if valid
207
- def valid?
208
- validate!
209
- true
210
- rescue ArgumentError
211
- false
212
- end
213
-
214
- # Get configuration as hash
215
- #
216
- # @return [Hash] configuration as hash
217
- def to_hash
218
- {
219
- logging: {
220
- sinks: @configurations,
221
- },
222
- }
223
- end
224
-
225
- # Save configuration to YAML file
226
- #
227
- # @param yaml_file [String] path to YAML file
228
- # @return [void]
229
- def save_yaml(yaml_file)
230
- require "yaml"
231
- FileUtils.mkdir_p(File.dirname(yaml_file))
232
- File.write(yaml_file, to_hash.to_yaml)
233
- end
234
-
235
- # Save configuration to JSON file
236
- #
237
- # @param json_file [String] path to JSON file
238
- # @return [void]
239
- def save_json(json_file)
240
- require "json"
241
- FileUtils.mkdir_p(File.dirname(json_file))
242
- File.write(json_file, JSON.pretty_generate(to_hash))
243
- end
244
-
245
- private
246
-
247
- # Validate a single configuration
248
- #
249
- # @param config [Hash] configuration to validate
250
- # @param index [Integer] configuration index for error messages
251
- # @raise [ArgumentError] if configuration is invalid
252
- def validate_single_configuration(config, index)
253
- raise ArgumentError, "configuration #{index} must be a hash" unless config.is_a?(Hash)
254
-
255
- required_keys = %i[file format]
256
- missing_keys = required_keys - config.keys
257
- unless missing_keys.empty?
258
- raise ArgumentError,
259
- "configuration #{index} missing required keys: #{missing_keys.join(", ")}"
260
- end
261
-
262
- # Validate file
263
- file = config[:file]
264
- unless file.is_a?(String) || file.is_a?(IO) || file.respond_to?(:write)
265
- raise ArgumentError, "configuration #{index} file must be a String path, IO object, or writable object"
266
- end
267
-
268
- # Validate format
269
- format = config[:format]
270
- unless format.is_a?(Symbol) || format.is_a?(String)
271
- raise ArgumentError, "configuration #{index} format must be a Symbol or String"
272
- end
273
-
274
- # Validate log levels if specified
275
- if config[:min_level] && !valid_log_level?(config[:min_level])
276
- raise ArgumentError, "configuration #{index} min_level must be a valid log level"
277
- end
278
-
279
- if config[:max_level] && !valid_log_level?(config[:max_level])
280
- raise ArgumentError, "configuration #{index} max_level must be a valid log level"
281
- end
282
-
283
- # Validate rotation if specified
284
- return unless config[:rotation] && !valid_rotation_config?(config[:rotation])
285
-
286
- raise ArgumentError, "configuration #{index} rotation must be a valid rotation configuration"
287
- end
288
-
289
- # Check if log level is valid
290
- #
291
- # @param level [Symbol] log level to check
292
- # @return [Boolean] true if valid
293
- def valid_log_level?(level)
294
- %i[debug info warn error fatal success].include?(level)
295
- end
296
-
297
- # Check if rotation configuration is valid
298
- #
299
- # @param rotation [Hash] rotation configuration
300
- # @return [Boolean] true if valid
301
- def valid_rotation_config?(rotation)
302
- return false unless rotation.is_a?(Hash)
303
-
304
- valid_keys = %i[max_size max_files compress pattern]
305
- rotation.keys.all? { |key| valid_keys.include?(key) }
306
- end
307
- end
308
- end
309
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Makit
4
+ module Logging
5
+ # Configuration management for unified logging
6
+ #
7
+ # Provides schema validation, default configurations, and helper methods
8
+ # for setting up the unified file sink with various output configurations.
9
+ #
10
+ # @example Basic configuration
11
+ # config = Configuration.new(
12
+ # configurations: [
13
+ # { file: $stdout, format: :console },
14
+ # { file: "logs/app.log", format: :json }
15
+ # ]
16
+ # )
17
+ #
18
+ # @example YAML configuration
19
+ # config = Configuration.from_yaml("config/logging.yml")
20
+ #
21
+ # @example Environment-based configuration
22
+ # config = Configuration.for_environment(:production)
23
+ class Configuration
24
+ # @return [Array<Hash>] list of output configurations
25
+ attr_reader :configurations
26
+
27
+ # Initialize configuration
28
+ #
29
+ # @param configurations [Array<Hash>] list of output configurations
30
+ def initialize(configurations: [])
31
+ @configurations = configurations
32
+ end
33
+
34
+ # Create configuration from YAML file
35
+ #
36
+ # @param yaml_file [String] path to YAML configuration file
37
+ # @return [Configuration] new configuration instance
38
+ def self.from_yaml(yaml_file)
39
+ require "yaml"
40
+ data = YAML.load_file(yaml_file)
41
+ configurations = data["logging"]["sinks"] || []
42
+
43
+ # Convert string keys to symbols
44
+ configurations = configurations.map do |config|
45
+ config.transform_keys(&:to_sym)
46
+ end
47
+
48
+ new(configurations: configurations)
49
+ end
50
+
51
+ # Create configuration from JSON file
52
+ #
53
+ # @param json_file [String] path to JSON configuration file
54
+ # @return [Configuration] new configuration instance
55
+ def self.from_json(json_file)
56
+ require "json"
57
+ data = JSON.parse(File.read(json_file))
58
+ configurations = data["logging"]["sinks"] || []
59
+
60
+ # Convert string keys to symbols
61
+ configurations = configurations.map do |config|
62
+ config.transform_keys(&:to_sym)
63
+ end
64
+
65
+ new(configurations: configurations)
66
+ end
67
+
68
+ # Create environment-specific configuration
69
+ #
70
+ # @param environment [Symbol] environment name (:development, :production, :test)
71
+ # @return [Configuration] new configuration instance
72
+ def self.for_environment(environment)
73
+ case environment
74
+ when :development
75
+ development_config
76
+ when :production
77
+ production_config
78
+ when :test
79
+ test_config
80
+ when :ci
81
+ ci_config
82
+ else
83
+ default_config
84
+ end
85
+ end
86
+
87
+ # Get default configuration
88
+ #
89
+ # @return [Configuration] default configuration
90
+ def self.default_config
91
+ require_relative "../logging" unless defined?(Makit::Logging)
92
+ new(
93
+ configurations: [
94
+ { file: $stdout, format: :console },
95
+ { file: Makit::Logging.log_file_path("makit.log"), format: :json, append: true },
96
+ ],
97
+ )
98
+ end
99
+
100
+ # Get development configuration
101
+ #
102
+ # @return [Configuration] development configuration
103
+ def self.development_config
104
+ new(
105
+ configurations: [
106
+ {
107
+ file: $stdout,
108
+ format: :console,
109
+ show_timestamp: true,
110
+ show_level: true,
111
+ },
112
+ {
113
+ file: "logs/development.log",
114
+ format: :text,
115
+ append: true,
116
+ include_context: true,
117
+ },
118
+ {
119
+ file: "logs/debug.log",
120
+ format: :json,
121
+ append: true,
122
+ min_level: :debug,
123
+ },
124
+ ],
125
+ )
126
+ end
127
+
128
+ # Get production configuration
129
+ #
130
+ # @return [Configuration] production configuration
131
+ def self.production_config
132
+ new(
133
+ configurations: [
134
+ {
135
+ file: "logs/application.log",
136
+ format: :json,
137
+ append: true,
138
+ include_metadata: true,
139
+ rotation: { max_size: "100MB", max_files: 10 },
140
+ },
141
+ {
142
+ file: "logs/errors.log",
143
+ format: :json,
144
+ append: true,
145
+ min_level: :error,
146
+ include_metadata: true,
147
+ },
148
+ ],
149
+ )
150
+ end
151
+
152
+ # Get test configuration
153
+ #
154
+ # @return [Configuration] test configuration
155
+ def self.test_config
156
+ new(
157
+ configurations: [
158
+ {
159
+ file: "logs/test.log",
160
+ format: :text,
161
+ append: false,
162
+ include_context: true,
163
+ },
164
+ ],
165
+ )
166
+ end
167
+
168
+ # Get CI/CD configuration
169
+ #
170
+ # @return [Configuration] CI configuration
171
+ def self.ci_config
172
+ new(
173
+ configurations: [
174
+ {
175
+ file: $stdout,
176
+ format: :console,
177
+ show_level: true,
178
+ },
179
+ {
180
+ file: "artifacts/build.log",
181
+ format: :json,
182
+ append: true,
183
+ include_metadata: true,
184
+ },
185
+ ],
186
+ )
187
+ end
188
+
189
+ # Validate configuration
190
+ #
191
+ # @return [Boolean] true if valid
192
+ # @raise [ArgumentError] if configuration is invalid
193
+ def validate!
194
+ raise ArgumentError, "configurations must be an array" unless @configurations.is_a?(Array)
195
+ raise ArgumentError, "at least one configuration is required" if @configurations.empty?
196
+
197
+ @configurations.each_with_index do |config, index|
198
+ validate_single_configuration(config, index)
199
+ end
200
+
201
+ true
202
+ end
203
+
204
+ # Check if configuration is valid
205
+ #
206
+ # @return [Boolean] true if valid
207
+ def valid?
208
+ validate!
209
+ true
210
+ rescue ArgumentError
211
+ false
212
+ end
213
+
214
+ # Get configuration as hash
215
+ #
216
+ # @return [Hash] configuration as hash
217
+ def to_hash
218
+ {
219
+ logging: {
220
+ sinks: @configurations,
221
+ },
222
+ }
223
+ end
224
+
225
+ # Save configuration to YAML file
226
+ #
227
+ # @param yaml_file [String] path to YAML file
228
+ # @return [void]
229
+ def save_yaml(yaml_file)
230
+ require "yaml"
231
+ FileUtils.mkdir_p(File.dirname(yaml_file))
232
+ File.write(yaml_file, to_hash.to_yaml)
233
+ end
234
+
235
+ # Save configuration to JSON file
236
+ #
237
+ # @param json_file [String] path to JSON file
238
+ # @return [void]
239
+ def save_json(json_file)
240
+ require "json"
241
+ FileUtils.mkdir_p(File.dirname(json_file))
242
+ File.write(json_file, JSON.pretty_generate(to_hash))
243
+ end
244
+
245
+ private
246
+
247
+ # Validate a single configuration
248
+ #
249
+ # @param config [Hash] configuration to validate
250
+ # @param index [Integer] configuration index for error messages
251
+ # @raise [ArgumentError] if configuration is invalid
252
+ def validate_single_configuration(config, index)
253
+ raise ArgumentError, "configuration #{index} must be a hash" unless config.is_a?(Hash)
254
+
255
+ required_keys = %i[file format]
256
+ missing_keys = required_keys - config.keys
257
+ unless missing_keys.empty?
258
+ raise ArgumentError,
259
+ "configuration #{index} missing required keys: #{missing_keys.join(", ")}"
260
+ end
261
+
262
+ # Validate file
263
+ file = config[:file]
264
+ unless file.is_a?(String) || file.is_a?(IO) || file.respond_to?(:write)
265
+ raise ArgumentError, "configuration #{index} file must be a String path, IO object, or writable object"
266
+ end
267
+
268
+ # Validate format
269
+ format = config[:format]
270
+ unless format.is_a?(Symbol) || format.is_a?(String)
271
+ raise ArgumentError, "configuration #{index} format must be a Symbol or String"
272
+ end
273
+
274
+ # Validate log levels if specified
275
+ if config[:min_level] && !valid_log_level?(config[:min_level])
276
+ raise ArgumentError, "configuration #{index} min_level must be a valid log level"
277
+ end
278
+
279
+ if config[:max_level] && !valid_log_level?(config[:max_level])
280
+ raise ArgumentError, "configuration #{index} max_level must be a valid log level"
281
+ end
282
+
283
+ # Validate rotation if specified
284
+ return unless config[:rotation] && !valid_rotation_config?(config[:rotation])
285
+
286
+ raise ArgumentError, "configuration #{index} rotation must be a valid rotation configuration"
287
+ end
288
+
289
+ # Check if log level is valid
290
+ #
291
+ # @param level [Symbol] log level to check
292
+ # @return [Boolean] true if valid
293
+ def valid_log_level?(level)
294
+ %i[debug info warn error fatal success].include?(level)
295
+ end
296
+
297
+ # Check if rotation configuration is valid
298
+ #
299
+ # @param rotation [Hash] rotation configuration
300
+ # @return [Boolean] true if valid
301
+ def valid_rotation_config?(rotation)
302
+ return false unless rotation.is_a?(Hash)
303
+
304
+ valid_keys = %i[max_size max_files compress pattern]
305
+ rotation.keys.all? { |key| valid_keys.include?(key) }
306
+ end
307
+ end
308
+ end
309
+ end