makit 0.0.98 → 0.0.111
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +41 -0
- data/exe/makit +5 -0
- data/lib/makit/apache.rb +7 -11
- data/lib/makit/cli/build_commands.rb +500 -0
- data/lib/makit/cli/generators/base_generator.rb +74 -0
- data/lib/makit/cli/generators/dotnet_generator.rb +50 -0
- data/lib/makit/cli/generators/generator_factory.rb +49 -0
- data/lib/makit/cli/generators/node_generator.rb +50 -0
- data/lib/makit/cli/generators/ruby_generator.rb +77 -0
- data/lib/makit/cli/generators/rust_generator.rb +50 -0
- data/lib/makit/cli/generators/templates/dotnet_templates.rb +167 -0
- data/lib/makit/cli/generators/templates/node_templates.rb +161 -0
- data/lib/makit/cli/generators/templates/ruby/gemfile.rb +26 -0
- data/lib/makit/cli/generators/templates/ruby/gemspec.rb +40 -0
- data/lib/makit/cli/generators/templates/ruby/main_lib.rb +33 -0
- data/lib/makit/cli/generators/templates/ruby/rakefile.rb +35 -0
- data/lib/makit/cli/generators/templates/ruby/readme.rb +63 -0
- data/lib/makit/cli/generators/templates/ruby/test.rb +39 -0
- data/lib/makit/cli/generators/templates/ruby/test_helper.rb +29 -0
- data/lib/makit/cli/generators/templates/ruby/version.rb +29 -0
- data/lib/makit/cli/generators/templates/rust_templates.rb +128 -0
- data/lib/makit/cli/main.rb +48 -19
- data/lib/makit/cli/project_commands.rb +868 -0
- data/lib/makit/cli/repository_commands.rb +661 -0
- data/lib/makit/cli/utility_commands.rb +521 -0
- data/lib/makit/command_runner.rb +187 -128
- data/lib/makit/commands/compatibility.rb +365 -0
- data/lib/makit/commands/factory.rb +359 -0
- data/lib/makit/commands/middleware/base.rb +73 -0
- data/lib/makit/commands/middleware/cache.rb +248 -0
- data/lib/makit/commands/middleware/command_logger.rb +323 -0
- data/lib/makit/commands/middleware/unified_logger.rb +243 -0
- data/lib/makit/commands/middleware/validator.rb +269 -0
- data/lib/makit/commands/request.rb +254 -0
- data/lib/makit/commands/result.rb +323 -0
- data/lib/makit/commands/runner.rb +317 -0
- data/lib/makit/commands/strategies/base.rb +160 -0
- data/lib/makit/commands/strategies/synchronous.rb +134 -0
- data/lib/makit/commands.rb +24 -3
- data/lib/makit/configuration/gitlab_helper.rb +60 -0
- data/lib/makit/configuration/project.rb +127 -0
- data/lib/makit/configuration/rakefile_helper.rb +43 -0
- data/lib/makit/configuration/step.rb +34 -0
- data/lib/makit/configuration.rb +14 -0
- data/lib/makit/content/default_gitignore.rb +4 -2
- data/lib/makit/content/default_rakefile.rb +4 -2
- data/lib/makit/content/gem_rakefile.rb +4 -2
- data/lib/makit/context.rb +1 -0
- data/lib/makit/data.rb +9 -10
- data/lib/makit/directories.rb +48 -52
- data/lib/makit/directory.rb +38 -52
- data/lib/makit/docs/files.rb +5 -10
- data/lib/makit/docs/rake.rb +16 -20
- data/lib/makit/dotnet/cli.rb +65 -0
- data/lib/makit/dotnet/project.rb +153 -0
- data/lib/makit/dotnet/solution.rb +38 -0
- data/lib/makit/dotnet/solution_classlib.rb +239 -0
- data/lib/makit/dotnet/solution_console.rb +264 -0
- data/lib/makit/dotnet/solution_maui.rb +354 -0
- data/lib/makit/dotnet/solution_wasm.rb +275 -0
- data/lib/makit/dotnet/solution_wpf.rb +304 -0
- data/lib/makit/dotnet.rb +54 -171
- data/lib/makit/email.rb +46 -17
- data/lib/makit/environment.rb +22 -19
- data/lib/makit/examples/runner.rb +370 -0
- data/lib/makit/exceptions.rb +45 -0
- data/lib/makit/fileinfo.rb +3 -5
- data/lib/makit/files.rb +12 -16
- data/lib/makit/gems.rb +40 -39
- data/lib/makit/git/cli.rb +54 -0
- data/lib/makit/git/repository.rb +90 -0
- data/lib/makit/git.rb +44 -91
- data/lib/makit/gitlab_runner.rb +0 -1
- data/lib/makit/humanize.rb +31 -23
- data/lib/makit/indexer.rb +15 -24
- data/lib/makit/logging/configuration.rb +305 -0
- data/lib/makit/logging/format_registry.rb +84 -0
- data/lib/makit/logging/formatters/base.rb +39 -0
- data/lib/makit/logging/formatters/console_formatter.rb +127 -0
- data/lib/makit/logging/formatters/json_formatter.rb +65 -0
- data/lib/makit/logging/formatters/plain_text_formatter.rb +71 -0
- data/lib/makit/logging/formatters/text_formatter.rb +64 -0
- data/lib/makit/logging/log_request.rb +115 -0
- data/lib/makit/logging/logger.rb +159 -0
- data/lib/makit/logging/sinks/base.rb +91 -0
- data/lib/makit/logging/sinks/console.rb +72 -0
- data/lib/makit/logging/sinks/file_sink.rb +92 -0
- data/lib/makit/logging/sinks/structured.rb +129 -0
- data/lib/makit/logging/sinks/unified_file_sink.rb +303 -0
- data/lib/makit/logging.rb +452 -37
- data/lib/makit/markdown.rb +18 -18
- data/lib/makit/mp/basic_object_mp.rb +5 -4
- data/lib/makit/mp/command_mp.rb +5 -5
- data/lib/makit/mp/command_request.mp.rb +3 -2
- data/lib/makit/mp/project_mp.rb +85 -96
- data/lib/makit/mp/string_mp.rb +245 -73
- data/lib/makit/nuget.rb +27 -25
- data/lib/makit/port.rb +25 -27
- data/lib/makit/process.rb +127 -29
- data/lib/makit/protoc.rb +27 -24
- data/lib/makit/rake/cli.rb +196 -0
- data/lib/makit/rake.rb +6 -6
- data/lib/makit/ruby/cli.rb +185 -0
- data/lib/makit/ruby.rb +25 -0
- data/lib/makit/secrets.rb +18 -18
- data/lib/makit/serializer.rb +29 -27
- data/lib/makit/services/builder.rb +186 -0
- data/lib/makit/services/error_handler.rb +226 -0
- data/lib/makit/services/repository_manager.rb +229 -0
- data/lib/makit/services/validator.rb +112 -0
- data/lib/makit/setup/classlib.rb +53 -0
- data/lib/makit/setup/gem.rb +250 -0
- data/lib/makit/setup/runner.rb +40 -0
- data/lib/makit/show.rb +16 -16
- data/lib/makit/storage.rb +32 -37
- data/lib/makit/symbols.rb +12 -0
- data/lib/makit/task_hooks.rb +125 -0
- data/lib/makit/task_info.rb +63 -21
- data/lib/makit/tasks/at_exit.rb +13 -0
- data/lib/makit/tasks/build.rb +18 -0
- data/lib/makit/tasks/clean.rb +11 -0
- data/lib/makit/tasks/hook_manager.rb +239 -0
- data/lib/makit/tasks/init.rb +47 -0
- data/lib/makit/tasks/integrate.rb +15 -0
- data/lib/makit/tasks/pull_incoming.rb +12 -0
- data/lib/makit/tasks/setup.rb +6 -0
- data/lib/makit/tasks/sync.rb +11 -0
- data/lib/makit/tasks/task_monkey_patch.rb +79 -0
- data/lib/makit/tasks.rb +5 -150
- data/lib/makit/test_cache.rb +239 -0
- data/lib/makit/v1/makit.v1_pb.rb +34 -35
- data/lib/makit/v1/makit.v1_services_pb.rb +2 -0
- data/lib/makit/version.rb +1 -60
- data/lib/makit/wix.rb +23 -23
- data/lib/makit/yaml.rb +18 -6
- data/lib/makit.rb +2 -261
- metadata +109 -145
- data/lib/makit/cli/clean.rb +0 -14
- data/lib/makit/cli/clone.rb +0 -59
- data/lib/makit/cli/init.rb +0 -38
- data/lib/makit/cli/make.rb +0 -54
- data/lib/makit/cli/new.rb +0 -37
- data/lib/makit/cli/nuget_cache.rb +0 -38
- data/lib/makit/cli/pull.rb +0 -31
- data/lib/makit/cli/setup.rb +0 -71
- data/lib/makit/cli/work.rb +0 -21
- data/lib/makit/content/default_gitignore.txt +0 -222
@@ -0,0 +1,160 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "open3"
|
4
|
+
require "timeout"
|
5
|
+
|
6
|
+
module Makit
|
7
|
+
module Commands
|
8
|
+
module Strategies
|
9
|
+
# Base class for command execution strategies.
|
10
|
+
#
|
11
|
+
# Execution strategies define how commands are actually executed -
|
12
|
+
# synchronously, asynchronously, in parallel, etc. This provides
|
13
|
+
# flexibility in execution patterns while maintaining a consistent
|
14
|
+
# interface.
|
15
|
+
#
|
16
|
+
# @example Creating custom strategy
|
17
|
+
# class CustomStrategy < Base
|
18
|
+
# def execute(request)
|
19
|
+
# # Custom execution logic
|
20
|
+
# result = Result.new(command: request.to_shell_command)
|
21
|
+
# # ... perform execution ...
|
22
|
+
# result.finish!(exit_code: 0, stdout: "success")
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
class Base
|
26
|
+
# Execute a command request.
|
27
|
+
#
|
28
|
+
# This method must be implemented by subclasses to provide the actual
|
29
|
+
# command execution logic. The implementation should create a Result
|
30
|
+
# object and populate it with execution details.
|
31
|
+
#
|
32
|
+
# @param request [Request] the command request to execute
|
33
|
+
# @return [Result] the execution result
|
34
|
+
# @raise [NotImplementedError] if not overridden by subclass
|
35
|
+
def execute(request)
|
36
|
+
raise NotImplementedError, "#{self.class.name} must implement #execute"
|
37
|
+
end
|
38
|
+
|
39
|
+
# Execute multiple requests (default: sequential execution).
|
40
|
+
#
|
41
|
+
# Override this method in subclasses to provide optimized batch execution
|
42
|
+
# such as parallel execution.
|
43
|
+
#
|
44
|
+
# @param requests [Array<Request>] requests to execute
|
45
|
+
# @return [Array<Result>] execution results in same order
|
46
|
+
def execute_batch(requests)
|
47
|
+
requests.map { |request| execute(request) }
|
48
|
+
end
|
49
|
+
|
50
|
+
# Check if this strategy can handle the given request.
|
51
|
+
#
|
52
|
+
# Override this method to provide conditional strategy selection
|
53
|
+
# based on request properties.
|
54
|
+
#
|
55
|
+
# @param request [Request] the command request
|
56
|
+
# @return [Boolean] true if strategy can handle the request
|
57
|
+
def supports?(_request)
|
58
|
+
true
|
59
|
+
end
|
60
|
+
|
61
|
+
# Get strategy name for logging and debugging.
|
62
|
+
#
|
63
|
+
# @return [String] strategy name
|
64
|
+
def name
|
65
|
+
self.class.name.split("::").last
|
66
|
+
end
|
67
|
+
|
68
|
+
# Get strategy configuration.
|
69
|
+
#
|
70
|
+
# @return [Hash] strategy configuration
|
71
|
+
def config
|
72
|
+
{}
|
73
|
+
end
|
74
|
+
|
75
|
+
protected
|
76
|
+
|
77
|
+
# Execute command using Open3 for cross-platform compatibility.
|
78
|
+
#
|
79
|
+
# This is a helper method that subclasses can use for actual
|
80
|
+
# system command execution.
|
81
|
+
#
|
82
|
+
# @param request [Request] the command request
|
83
|
+
# @return [Result] execution result
|
84
|
+
def execute_with_open3(request)
|
85
|
+
result = Result.new(
|
86
|
+
command: request.to_shell_command,
|
87
|
+
started_at: Time.now,
|
88
|
+
)
|
89
|
+
|
90
|
+
begin
|
91
|
+
# Change to specified directory if provided
|
92
|
+
Dir.chdir(request.directory) do
|
93
|
+
# Execute command with timeout using Timeout module
|
94
|
+
stdout, stderr, status = if request.timeout&.positive?
|
95
|
+
Timeout.timeout(request.timeout) do
|
96
|
+
Open3.capture3(
|
97
|
+
request.environment,
|
98
|
+
request.command,
|
99
|
+
*request.arguments
|
100
|
+
)
|
101
|
+
end
|
102
|
+
else
|
103
|
+
Open3.capture3(
|
104
|
+
request.environment,
|
105
|
+
request.command,
|
106
|
+
*request.arguments
|
107
|
+
)
|
108
|
+
end
|
109
|
+
|
110
|
+
result.finish!(
|
111
|
+
exit_code: status.exitstatus,
|
112
|
+
stdout: stdout,
|
113
|
+
stderr: stderr,
|
114
|
+
)
|
115
|
+
end
|
116
|
+
rescue Timeout::Error => e
|
117
|
+
result.finish!(
|
118
|
+
exit_code: 124, # timeout exit code
|
119
|
+
stderr: "Command timed out after #{request.timeout} seconds",
|
120
|
+
).add_metadata(:timeout, true)
|
121
|
+
.add_metadata(:error, e.message)
|
122
|
+
rescue StandardError => e
|
123
|
+
result.finish!(
|
124
|
+
exit_code: 1,
|
125
|
+
stderr: e.message,
|
126
|
+
).add_metadata(:error_class, e.class.name)
|
127
|
+
.add_metadata(:error, e.message)
|
128
|
+
end
|
129
|
+
|
130
|
+
result
|
131
|
+
end
|
132
|
+
|
133
|
+
# Validate that the command exists and is executable.
|
134
|
+
#
|
135
|
+
# @param command [String] command to validate
|
136
|
+
# @return [Boolean] true if command is available
|
137
|
+
def command_available?(command)
|
138
|
+
system("which #{command} > /dev/null 2>&1") ||
|
139
|
+
system("where #{command} > nul 2>&1") # Windows
|
140
|
+
end
|
141
|
+
|
142
|
+
# Get the full path to a command.
|
143
|
+
#
|
144
|
+
# @param command [String] command name
|
145
|
+
# @return [String, nil] full path to command or nil if not found
|
146
|
+
def which(command)
|
147
|
+
# Try Unix-style which first
|
148
|
+
path = `which #{command} 2>/dev/null`.strip
|
149
|
+
return path unless path.empty?
|
150
|
+
|
151
|
+
# Try Windows-style where
|
152
|
+
path = `where #{command} 2>nul`.strip
|
153
|
+
return path unless path.empty?
|
154
|
+
|
155
|
+
nil
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
require_relative "../result"
|
5
|
+
|
6
|
+
module Makit
|
7
|
+
module Commands
|
8
|
+
module Strategies
|
9
|
+
# Synchronous command execution strategy.
|
10
|
+
#
|
11
|
+
# This strategy executes commands one at a time, blocking until each
|
12
|
+
# command completes. This is the simplest and most reliable execution
|
13
|
+
# pattern, suitable for most use cases.
|
14
|
+
#
|
15
|
+
# @example Using synchronous execution
|
16
|
+
# strategy = Synchronous.new
|
17
|
+
# request = Request.new(command: "echo", arguments: ["hello"])
|
18
|
+
# result = strategy.execute(request)
|
19
|
+
# puts result.stdout # "hello"
|
20
|
+
class Synchronous < Base
|
21
|
+
# Initialize synchronous execution strategy.
|
22
|
+
#
|
23
|
+
# @param options [Hash] strategy configuration
|
24
|
+
# @option options [Boolean] :validate_commands (true) whether to validate commands exist
|
25
|
+
# @option options [Integer] :max_output_size (1048576) maximum output size in bytes
|
26
|
+
def initialize(**options)
|
27
|
+
@validate_commands = options.fetch(:validate_commands, true)
|
28
|
+
@max_output_size = options.fetch(:max_output_size, 1024 * 1024) # 1MB default
|
29
|
+
end
|
30
|
+
|
31
|
+
# Execute a single command request synchronously.
|
32
|
+
#
|
33
|
+
# @param request [Request] the command request to execute
|
34
|
+
# @return [Result] execution result
|
35
|
+
def execute(request)
|
36
|
+
# Skip command validation for basic commands like echo on Windows
|
37
|
+
# This is a simplified approach for demonstration purposes
|
38
|
+
if @validate_commands && !basic_command?(request.command) && !command_available?(request.command)
|
39
|
+
return Result.failure(
|
40
|
+
command: request.to_shell_command,
|
41
|
+
error: "Command not found: #{request.command}",
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Execute using Open3 for cross-platform support
|
46
|
+
result = execute_with_open3(request)
|
47
|
+
|
48
|
+
# Truncate output if too large
|
49
|
+
if result.stdout.bytesize > @max_output_size
|
50
|
+
original_size = result.stdout.bytesize
|
51
|
+
truncated_stdout = result.stdout.byteslice(0, @max_output_size)
|
52
|
+
result.instance_variable_set(:@stdout, "#{truncated_stdout}\n[OUTPUT TRUNCATED]")
|
53
|
+
result.add_metadata(:output_truncated, true)
|
54
|
+
result.add_metadata(:original_stdout_size, original_size)
|
55
|
+
end
|
56
|
+
|
57
|
+
if result.stderr.bytesize > @max_output_size
|
58
|
+
original_size = result.stderr.bytesize
|
59
|
+
truncated_stderr = result.stderr.byteslice(0, @max_output_size)
|
60
|
+
result.instance_variable_set(:@stderr, "#{truncated_stderr}\n[ERROR OUTPUT TRUNCATED]")
|
61
|
+
result.add_metadata(:stderr_truncated, true)
|
62
|
+
result.add_metadata(:original_stderr_size, original_size)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Add strategy metadata
|
66
|
+
result.add_metadata(:execution_strategy, "synchronous")
|
67
|
+
result.add_metadata(:validated_command, @validate_commands)
|
68
|
+
|
69
|
+
result
|
70
|
+
end
|
71
|
+
|
72
|
+
# Execute multiple requests sequentially.
|
73
|
+
#
|
74
|
+
# @param requests [Array<Request>] requests to execute
|
75
|
+
# @return [Array<Result>] execution results in same order
|
76
|
+
def execute_batch(requests)
|
77
|
+
results = []
|
78
|
+
|
79
|
+
requests.each_with_index do |request, index|
|
80
|
+
result = execute(request)
|
81
|
+
result.add_metadata(:batch_index, index)
|
82
|
+
result.add_metadata(:batch_size, requests.size)
|
83
|
+
results << result
|
84
|
+
|
85
|
+
# Stop on first failure if configured
|
86
|
+
break if result.failure? && fail_fast?
|
87
|
+
end
|
88
|
+
|
89
|
+
results
|
90
|
+
end
|
91
|
+
|
92
|
+
# Check if strategy supports the given request.
|
93
|
+
#
|
94
|
+
# Synchronous strategy supports all requests.
|
95
|
+
#
|
96
|
+
# @param request [Request] the command request
|
97
|
+
# @return [Boolean] always true
|
98
|
+
def supports?(_request)
|
99
|
+
true
|
100
|
+
end
|
101
|
+
|
102
|
+
# Get strategy configuration.
|
103
|
+
#
|
104
|
+
# @return [Hash] strategy configuration
|
105
|
+
def config
|
106
|
+
{
|
107
|
+
name: "synchronous",
|
108
|
+
validate_commands: @validate_commands,
|
109
|
+
max_output_size: @max_output_size,
|
110
|
+
fail_fast: fail_fast?,
|
111
|
+
}
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
# Check if command is a basic system command that should always be available.
|
117
|
+
#
|
118
|
+
# @param command [String] command name
|
119
|
+
# @return [Boolean] true if it's a basic command
|
120
|
+
def basic_command?(command)
|
121
|
+
%w[echo ruby bundle rake git].include?(command)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Check if execution should stop on first failure.
|
125
|
+
#
|
126
|
+
# @return [Boolean] whether to fail fast
|
127
|
+
def fail_fast?
|
128
|
+
# Could be configurable in the future
|
129
|
+
false
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
data/lib/makit/commands.rb
CHANGED
@@ -8,14 +8,35 @@ require "logger"
|
|
8
8
|
|
9
9
|
# This module provides classes for the Makit gem.
|
10
10
|
module Makit
|
11
|
+
# Commands module provides a modern, flexible command execution framework
|
12
|
+
# with middleware support, validation, caching, and structured logging.
|
13
|
+
module Commands
|
14
|
+
# Custom exception for security-related command validation failures
|
15
|
+
class SecurityError < StandardError; end
|
16
|
+
|
17
|
+
# Custom exception for command execution failures
|
18
|
+
class ExecutionError < StandardError
|
19
|
+
attr_reader :exit_code, :stderr
|
20
|
+
|
21
|
+
def initialize(message, exit_code: nil, stderr: nil)
|
22
|
+
@exit_code = exit_code
|
23
|
+
@stderr = stderr
|
24
|
+
super(message)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
11
29
|
# This class provide methods running commands.
|
12
30
|
#
|
13
|
-
class
|
14
|
-
|
31
|
+
class LegacyCommands < Array
|
15
32
|
# Generate the commands based on the current directory.
|
16
33
|
def auto_generate
|
17
34
|
self << Makit::V1::CommandRequest.new(name: "bundle", arguments: ["install"]) if File.exist?("Gemfile")
|
18
|
-
self << Makit::V1::CommandRequest.new(name: "bundle", arguments: [
|
35
|
+
self << Makit::V1::CommandRequest.new(name: "bundle", arguments: %w[exec rake]) if File.exist?("Rakefile")
|
19
36
|
end
|
20
37
|
end
|
21
38
|
end
|
39
|
+
|
40
|
+
# Load command system components (load existing components only)
|
41
|
+
require_relative "commands/result"
|
42
|
+
require_relative "commands/middleware/validator"
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
module Makit
|
6
|
+
module Configuration
|
7
|
+
# Helper for GitLab CI YAML conversion
|
8
|
+
class GitLabHelper
|
9
|
+
def self.to_yaml(project, path)
|
10
|
+
yaml_content = generate_yaml(project)
|
11
|
+
File.write(path, yaml_content)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.from_yaml(path)
|
15
|
+
content = File.read(path)
|
16
|
+
data = YAML.safe_load(content, symbolize_names: true, permitted_classes: [Symbol])
|
17
|
+
|
18
|
+
project = Project.new(name: data[:name] || "Unknown", version: data[:version] || "1.0.0")
|
19
|
+
|
20
|
+
# Extract steps from GitLab CI jobs
|
21
|
+
data.each do |key, value|
|
22
|
+
next unless value.is_a?(Hash) && value[:script]
|
23
|
+
|
24
|
+
step_name = key.to_s.gsub(/_job$/, "")
|
25
|
+
step = Step.new(
|
26
|
+
name: step_name,
|
27
|
+
description: value[:description] || "GitLab CI job: #{step_name}",
|
28
|
+
commands: value[:script],
|
29
|
+
)
|
30
|
+
project.add_step(step)
|
31
|
+
end
|
32
|
+
|
33
|
+
project
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def self.generate_yaml(project)
|
39
|
+
yaml = {
|
40
|
+
stages: project.steps.map(&:name),
|
41
|
+
variables: {
|
42
|
+
project_name: project.name,
|
43
|
+
project_version: project.version,
|
44
|
+
},
|
45
|
+
}
|
46
|
+
|
47
|
+
project.steps.each do |step|
|
48
|
+
job_name = "#{step.name}_job"
|
49
|
+
yaml[job_name.to_sym] = {
|
50
|
+
stage: step.name,
|
51
|
+
script: step.commands,
|
52
|
+
description: step.description,
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
yaml.to_yaml
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
module Makit
|
6
|
+
module Configuration
|
7
|
+
# Project configuration management
|
8
|
+
class Project
|
9
|
+
attr_accessor :git_remote_url, :name, :version, :project_type, :steps
|
10
|
+
|
11
|
+
def initialize(name, version = nil, project_type = nil)
|
12
|
+
# Support both keyword arguments and positional arguments for compatibility
|
13
|
+
if name.is_a?(Hash)
|
14
|
+
# Keyword arguments: Project.new(name: "test", version: "1.0.0")
|
15
|
+
@name = name[:name]
|
16
|
+
@version = name[:version]
|
17
|
+
@project_type = name[:project_type]
|
18
|
+
else
|
19
|
+
# Positional arguments: Project.new("test", "1.0.0", "nuget")
|
20
|
+
@name = name
|
21
|
+
@version = version
|
22
|
+
@project_type = project_type
|
23
|
+
end
|
24
|
+
|
25
|
+
@steps = []
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_step(step)
|
29
|
+
raise ArgumentError, "Step must be a Makit::Configuration::Step instance" unless step.is_a?(Step)
|
30
|
+
@steps << step
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_json(*args)
|
34
|
+
{
|
35
|
+
name: @name,
|
36
|
+
version: @version,
|
37
|
+
project_type: @project_type,
|
38
|
+
steps: @steps.map(&:to_h),
|
39
|
+
}.to_json(*args)
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_json_pretty
|
43
|
+
JSON.pretty_generate({
|
44
|
+
name: @name,
|
45
|
+
version: @version,
|
46
|
+
project_type: @project_type,
|
47
|
+
steps: @steps.map(&:to_h),
|
48
|
+
})
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.from_json(path)
|
52
|
+
content = File.read(path)
|
53
|
+
data = JSON.parse(content, symbolize_names: true)
|
54
|
+
|
55
|
+
project = new(data[:name], data[:version], data[:project_type])
|
56
|
+
data[:steps].each do |step_data|
|
57
|
+
step = Step.new(
|
58
|
+
name: step_data[:name],
|
59
|
+
description: step_data[:description],
|
60
|
+
commands: step_data[:commands],
|
61
|
+
)
|
62
|
+
project.add_step(step)
|
63
|
+
end
|
64
|
+
|
65
|
+
project
|
66
|
+
end
|
67
|
+
|
68
|
+
# Class method for deserializing from JSON string (used by serializer)
|
69
|
+
def self.decode_json(json_string)
|
70
|
+
data = JSON.parse(json_string, symbolize_names: true)
|
71
|
+
|
72
|
+
project = new(data[:name], data[:version], data[:project_type])
|
73
|
+
data[:steps].each do |step_data|
|
74
|
+
step = Step.new(
|
75
|
+
name: step_data[:name],
|
76
|
+
description: step_data[:description],
|
77
|
+
commands: step_data[:commands],
|
78
|
+
)
|
79
|
+
project.add_step(step)
|
80
|
+
end
|
81
|
+
|
82
|
+
project
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_gitlab_ci(path)
|
86
|
+
GitLabHelper.to_yaml(self, path)
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_rakefile
|
90
|
+
RakefileHelper.generate(self)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Save the project to a specific path as pretty JSON
|
94
|
+
def save_as(path)
|
95
|
+
File.write(path, to_json_pretty)
|
96
|
+
end
|
97
|
+
|
98
|
+
def save
|
99
|
+
save_as(".makit.json")
|
100
|
+
end
|
101
|
+
|
102
|
+
# Display the project configuration in YAML format
|
103
|
+
def show
|
104
|
+
require "yaml"
|
105
|
+
|
106
|
+
project_data = {
|
107
|
+
name: @name,
|
108
|
+
version: @version,
|
109
|
+
project_type: @project_type,
|
110
|
+
git_remote_url: @git_remote_url,
|
111
|
+
steps: @steps.map(&:to_h),
|
112
|
+
}
|
113
|
+
|
114
|
+
puts YAML.dump(project_data)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Load a project from the default .makit.json file
|
118
|
+
def self.default
|
119
|
+
if File.exist?(".makit.json")
|
120
|
+
from_json(".makit.json")
|
121
|
+
else
|
122
|
+
new(name: "", version: "0.0.0", project_type: "")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Makit
|
4
|
+
module Configuration
|
5
|
+
# Helper for Rakefile generation
|
6
|
+
class RakefileHelper
|
7
|
+
def self.generate(project)
|
8
|
+
rakefile_content = []
|
9
|
+
|
10
|
+
# Add header comment
|
11
|
+
rakefile_content << "# Generated Rakefile for #{project.name} v#{project.version}"
|
12
|
+
rakefile_content << "# Generated by Makit::Configuration::RakefileHelper"
|
13
|
+
rakefile_content << ""
|
14
|
+
|
15
|
+
# Add default task
|
16
|
+
if project.steps.any?
|
17
|
+
default_tasks = project.steps.map(&:name).join(",")
|
18
|
+
rakefile_content << "desc 'Run all project steps'"
|
19
|
+
rakefile_content << "task :default => [:#{default_tasks}]"
|
20
|
+
rakefile_content << ""
|
21
|
+
end
|
22
|
+
|
23
|
+
# Add individual step tasks
|
24
|
+
project.steps.each do |step|
|
25
|
+
rakefile_content << "desc '#{step.description}'"
|
26
|
+
rakefile_content << "task :#{step.name} do"
|
27
|
+
step.commands.each do |command|
|
28
|
+
rakefile_content << " sh '#{command}'"
|
29
|
+
end
|
30
|
+
rakefile_content << "end"
|
31
|
+
rakefile_content << ""
|
32
|
+
end
|
33
|
+
|
34
|
+
rakefile_content.join("\n")
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.write(project, path)
|
38
|
+
content = generate(project)
|
39
|
+
File.write(path, content)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Makit
|
4
|
+
module Configuration
|
5
|
+
# Individual step in a project configuration
|
6
|
+
class Step
|
7
|
+
attr_accessor :name, :description, :commands
|
8
|
+
|
9
|
+
def initialize(name:, description:, commands:)
|
10
|
+
@name = name
|
11
|
+
@description = description
|
12
|
+
@commands = commands.is_a?(Array) ? commands : [commands]
|
13
|
+
|
14
|
+
validate_commands
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_h
|
18
|
+
{
|
19
|
+
name: @name,
|
20
|
+
description: @description,
|
21
|
+
commands: @commands,
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def validate_commands
|
28
|
+
unless @commands.is_a?(Array) && @commands.all? { |cmd| cmd.is_a?(String) }
|
29
|
+
raise ArgumentError, "Commands must be an array of strings"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Load all configuration classes
|
4
|
+
require_relative "configuration/project"
|
5
|
+
require_relative "configuration/step"
|
6
|
+
require_relative "configuration/gitlab_helper"
|
7
|
+
require_relative "configuration/rakefile_helper"
|
8
|
+
|
9
|
+
# Main configuration module that loads all configuration classes
|
10
|
+
module Makit
|
11
|
+
module Configuration
|
12
|
+
# All configuration classes are now loaded above
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
# frozen_string_literal: true
|