standalone-ruby 1.3 → 1.3.1
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/CHANGELOG.md +5 -1
- data/lib/utils/displayer.rb +8 -5
- data/lib/utils/launcher.rb +56 -14
- data/lib/utils/launcher_handler.rb +25 -4
- data/lib/utils/logger_helper.rb +9 -7
- data/lib/utils/parameter_parser.rb +42 -30
- data/lib/utils/ruby_copy.rb +6 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a7320c98d6c1987f62672c69a0d3925e6da1dd7dc2234ccaddbf754e26fb41e
|
4
|
+
data.tar.gz: e7ee6f01a88ff81457934a2a2c0d2855c59be3f32a38725c82846c002faf0453
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6a754e64422834d615d8e14109487217b1c4ae4a6e6f63ce8bb81eb58fc79a4f07aba7a238f118c9e4e70ea5985c77dfbbc317c155f2ba287fe6bde994cc3b32
|
7
|
+
data.tar.gz: 9a9fcf18c7bd5966e3feab9537281d67f61b44d7ad71452d6c4fb6f9d3e30bd12f28021c365b26c2f4c95ae11157d632d20700b7e7fefcdefc2096658812da60
|
data/CHANGELOG.md
CHANGED
@@ -16,4 +16,8 @@
|
|
16
16
|
- Project text outputs corrected again
|
17
17
|
|
18
18
|
## [1.3] - 2025-04-04
|
19
|
-
- Logger support has been added for testing purposes and will be adjusted later.
|
19
|
+
- Logger support has been added for testing purposes and will be adjusted later.
|
20
|
+
|
21
|
+
## [1.3.1] - 2025-04-04
|
22
|
+
- Added platform analysis function
|
23
|
+
- Logger support fixed
|
data/lib/utils/displayer.rb
CHANGED
@@ -1,12 +1,16 @@
|
|
1
|
+
require_relative 'logger_helper'
|
2
|
+
|
1
3
|
class Displayer
|
2
4
|
def initialize(params)
|
3
5
|
@params = params
|
6
|
+
@logger = LoggerHelper.instance
|
4
7
|
end
|
5
8
|
|
6
9
|
def display_params
|
7
10
|
begin
|
8
11
|
display_text = []
|
9
12
|
|
13
|
+
display_text << "Platform: #{@params[:platform]}" if @params[:platform]
|
10
14
|
display_text << "Project Path: #{@params[:project_path]}" if @params[:project_path]
|
11
15
|
display_text << "Ruby Path: #{@params[:ruby_path]}" if @params[:ruby_path]
|
12
16
|
display_text << "Ruby Folder: #{File.basename(@params[:ruby_path])}" if @params[:ruby_path]
|
@@ -15,22 +19,21 @@ class Displayer
|
|
15
19
|
display_text << "Launcher Type: #{@params[:launcher_type]}" if @params[:launcher_type]
|
16
20
|
display_text << "Template: #{@params[:template]}" if @params[:template]
|
17
21
|
display_text << "Template: Default Template" unless @params[:template]
|
22
|
+
display_text << "Log Path: Users Documents Directory"
|
18
23
|
|
19
24
|
puts display_text.join("\n") unless display_text.empty?
|
20
25
|
|
21
26
|
rescue Exception => e
|
22
27
|
print("Display Error: ".red); puts("#{e.message}".red)
|
28
|
+
@logger.error("Display Error: #{e.message}")
|
23
29
|
end
|
24
30
|
end
|
25
31
|
|
26
32
|
def banner
|
27
33
|
banner = -<<'BANNER'
|
28
|
-
|
29
|
-
┌─┐┌┬┐┌─┐┌┐┌┌┬┐┌─┐┬ ┌─┐┌┐┌┌─┐ ┬─┐┬ ┬┌┐ ┬ ┬
|
30
|
-
└─┐ │ ├─┤│││ ││├─┤│ │ ││││├┤ ├┬┘│ │├┴┐└┬┘
|
31
|
-
└─┘ ┴ ┴ ┴┘└┘─┴┘┴ ┴┴─┘└─┘┘└┘└─┘ ┴└─└─┘└─┘ ┴
|
32
|
-
|
34
|
+
Standalone-Ruby v1.3.1
|
33
35
|
# Github: https://github.com/ardatetikbey/Standalone-Ruby
|
36
|
+
# RubyGems: https://rubygems.org/gems/standalone-ruby
|
34
37
|
|
35
38
|
BANNER
|
36
39
|
|
data/lib/utils/launcher.rb
CHANGED
@@ -1,36 +1,78 @@
|
|
1
1
|
require 'optparse'
|
2
2
|
|
3
3
|
class Launcher
|
4
|
-
|
5
4
|
require_relative 'displayer'
|
6
5
|
require_relative 'parameter_parser'
|
7
6
|
require_relative 'ruby_copy'
|
8
7
|
require_relative 'launcher_handler'
|
9
|
-
|
8
|
+
require_relative 'logger_helper'
|
10
9
|
|
11
10
|
def initialize
|
12
11
|
@parser = ParameterParser.new
|
13
12
|
@parser.parse
|
14
|
-
|
15
13
|
@params = @parser.params
|
14
|
+
|
16
15
|
@displayer = Displayer.new(@params)
|
17
16
|
@ruby_copy = RubyCopy.new(@params)
|
18
17
|
@launcher_handler = LauncherHandler.new(@params)
|
18
|
+
@logger = LoggerHelper.instance
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
def platform_analysis
|
22
|
+
case RUBY_PLATFORM
|
23
|
+
when /win32|mingw|cygwin/
|
24
|
+
@params[:platform] = 'Windows'
|
25
|
+
@logger.info("Working on windows platform.")
|
26
|
+
when /darwin/
|
27
|
+
print("Error: ".red); puts("Mac OS platform is not supported at the moment! Exiting...")
|
28
|
+
@logger.error("Mac OS platform is not supported at the moment! Exiting...")
|
29
|
+
exit!
|
30
|
+
when /linux/
|
31
|
+
print("Error: ".red); puts("Linux platform is not supported at the moment! Exiting...")
|
32
|
+
@logger.error("Linux platform is not supported at the moment! Exiting...")
|
33
|
+
exit!
|
34
|
+
else
|
35
|
+
"#{RUBY_PLATFORM}"
|
25
36
|
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def run
|
40
|
+
begin
|
41
|
+
Signal.trap("INT") do
|
42
|
+
@logger.info("The program was closed because an interrupt command was detected.")
|
43
|
+
puts "\nProgram interrupted. Shutting down..."
|
44
|
+
exit(0)
|
45
|
+
end
|
26
46
|
|
27
|
-
|
28
|
-
@displayer.display_params
|
47
|
+
platform_analysis
|
29
48
|
|
30
|
-
|
31
|
-
|
49
|
+
@logger.info("the executor function is started.")
|
50
|
+
@displayer.banner
|
51
|
+
@logger.info("Banner display was made.")
|
52
|
+
@displayer.display_params
|
53
|
+
@logger.info("The parameters entered by the user are reflected on the screen.")
|
54
|
+
@logger.info("The initiator file creator function has started processing.")
|
55
|
+
@launcher_handler.handle
|
56
|
+
@logger.info("The launcher file creator has finished its function.")
|
57
|
+
@ruby_copy.robocopy_interpreter
|
58
|
+
@logger.info("Ruby interpreter copy function completed.")
|
59
|
+
|
60
|
+
puts "Program Output Path: #{@params[:project_path]}"
|
61
|
+
puts "Thanks for using Standalone-Ruby!"
|
62
|
+
@logger.info("Program finished.")
|
63
|
+
rescue Exception => e
|
64
|
+
@logger.error("Launcher Error: #{e.message}")
|
65
|
+
print("Launcher Error: "); puts("#{e.message}".red)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class String
|
71
|
+
def red
|
72
|
+
"\e[31m#{self}\e[0m"
|
73
|
+
end
|
32
74
|
|
33
|
-
|
34
|
-
|
75
|
+
def yellow
|
76
|
+
"\e[33m#{self}\e[0m"
|
35
77
|
end
|
36
|
-
end
|
78
|
+
end
|
@@ -1,8 +1,11 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
|
3
|
+
require_relative 'logger_helper'
|
4
|
+
|
3
5
|
class LauncherHandler
|
4
6
|
def initialize(params)
|
5
7
|
@params = params
|
8
|
+
@logger = LoggerHelper.instance
|
6
9
|
end
|
7
10
|
|
8
11
|
def handle
|
@@ -18,15 +21,20 @@ class LauncherHandler
|
|
18
21
|
|
19
22
|
user_template = @params[:template].to_s
|
20
23
|
content = File.read(user_template)
|
24
|
+
@logger.info("The content of the user template has been read.")
|
21
25
|
|
22
26
|
content.gsub!("STANDALONE_RUBY_PATH", "#{File.join(File.basename(@params[:ruby_path].to_s), "bin", "#{ruby_file}")}")
|
27
|
+
@logger.info("Placed the defined Ruby interpreter path in the template.")
|
28
|
+
|
23
29
|
content.gsub!("STANDALONE_MAIN_FILE", "#{File.basename(@params[:main_file].to_s)}")
|
30
|
+
@logger.info("The main project file path defined in the template is placed.")
|
24
31
|
|
25
32
|
new_launcher_path = File.join(@params[:project_path].to_s, @params[:launcher_name].to_s)
|
26
|
-
|
27
33
|
File.open(new_launcher_path, "w") do |f_man|
|
28
34
|
f_man.puts content
|
29
35
|
end
|
36
|
+
|
37
|
+
@logger.info("New launcher file directory created as #{new_launcher_path.to_s}")
|
30
38
|
else
|
31
39
|
if @params[:launcher_type] == "vbs"
|
32
40
|
if @params[:gui] == true
|
@@ -41,17 +49,23 @@ class LauncherHandler
|
|
41
49
|
|
42
50
|
if File.exist?(vbs_template)
|
43
51
|
content = File.read(vbs_template)
|
52
|
+
@logger.info("The content of the template has been read.")
|
44
53
|
|
45
54
|
content.gsub!("STANDALONE_RUBY_PATH", "#{File.join(File.basename(@params[:ruby_path].to_s), "bin", "#{ruby_file}")}")
|
55
|
+
@logger.info("Placed the defined Ruby interpreter path in the template.")
|
56
|
+
|
46
57
|
content.gsub!("STANDALONE_MAIN_FILE", "#{File.basename(@params[:main_file].to_s)}")
|
58
|
+
@logger.info("The main project file path defined in the template is placed.")
|
47
59
|
|
48
60
|
new_launcher_path = File.join(@params[:project_path].to_s, @params[:launcher_name].to_s)
|
49
|
-
|
50
61
|
File.open(new_launcher_path, "w") do |f_man|
|
51
62
|
f_man.puts content
|
52
63
|
end
|
64
|
+
|
65
|
+
@logger.info("New launcher file directory created as #{new_launcher_path.to_s}")
|
53
66
|
else
|
54
|
-
print("Handler Error: ".red); puts("
|
67
|
+
print("Handler Error: ".red); puts("The template file could not be found.")
|
68
|
+
@logger.error("Template file not found!")
|
55
69
|
exit!
|
56
70
|
end
|
57
71
|
else
|
@@ -66,23 +80,30 @@ class LauncherHandler
|
|
66
80
|
|
67
81
|
if File.exist?(bat_template)
|
68
82
|
content = File.read(bat_template)
|
83
|
+
@logger.info("The content of the template has been read.")
|
69
84
|
|
70
85
|
content.gsub!("STANDALONE_RUBY_PATH", "#{File.join(File.basename(@params[:ruby_path].to_s), "bin", "#{ruby_file}")}")
|
86
|
+
@logger.info("Placed the defined Ruby interpreter path in the template.")
|
87
|
+
|
71
88
|
content.gsub!("STANDALONE_MAIN_FILE", "#{File.basename(@params[:main_file].to_s)}")
|
89
|
+
@logger.info("The main project file path defined in the template is placed.")
|
72
90
|
|
73
91
|
new_launcher_path = File.join(@params[:project_path].to_s, @params[:launcher_name].to_s)
|
74
|
-
|
75
92
|
File.open(new_launcher_path, "w") do |f_man|
|
76
93
|
f_man.puts content
|
77
94
|
end
|
95
|
+
|
96
|
+
@logger.info("New launcher file directory created as #{new_launcher_path.to_s}")
|
78
97
|
else
|
79
98
|
print("Handler Error: ".red); puts("The template file could not be found.")
|
99
|
+
@logger.error("Handler Error: Template file not found!")
|
80
100
|
exit!
|
81
101
|
end
|
82
102
|
end
|
83
103
|
end
|
84
104
|
rescue Exception => e
|
85
105
|
print("Handler Error: ".red); puts("#{e.message}")
|
106
|
+
@logger.error("Handler Error: #{e.message}")
|
86
107
|
end
|
87
108
|
end
|
88
109
|
end
|
data/lib/utils/logger_helper.rb
CHANGED
@@ -4,19 +4,21 @@ require 'fileutils'
|
|
4
4
|
|
5
5
|
class LoggerHelper
|
6
6
|
def self.instance
|
7
|
-
|
8
|
-
|
7
|
+
documents_path = File.join(ENV['USERPROFILE'], 'Documents', 'standalone-ruby-logs')
|
8
|
+
|
9
|
+
FileUtils.mkdir_p(documents_path) unless Dir.exist?(documents_path)
|
9
10
|
|
10
11
|
timestamp = Time.now.strftime("%Y-%m-%d_%H-%M-%S")
|
11
|
-
log_file = "#{
|
12
|
+
log_file = File.join(documents_path, "standalone-ruby_#{timestamp}.log")
|
12
13
|
|
13
|
-
|
14
|
-
|
14
|
+
logger = Logger.new(log_file)
|
15
|
+
logger.level = Logger::INFO
|
15
16
|
|
16
|
-
|
17
|
+
logger.formatter = proc do |severity, datetime, progname, msg|
|
18
|
+
msg = "(empty message)" if msg.nil? || msg.strip.empty?
|
17
19
|
"#{datetime.strftime('%Y-%m-%d %H:%M:%S')} [#{severity}] #{msg}\n"
|
18
20
|
end
|
19
21
|
|
20
|
-
return
|
22
|
+
return logger
|
21
23
|
end
|
22
24
|
end
|
@@ -1,11 +1,15 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
require 'optparse'
|
3
3
|
|
4
|
+
require_relative 'logger_helper'
|
5
|
+
|
4
6
|
class ParameterParser
|
5
7
|
def initialize
|
6
8
|
@params = {
|
7
9
|
threads: 5
|
8
10
|
}
|
11
|
+
|
12
|
+
@logger = LoggerHelper.instance
|
9
13
|
end
|
10
14
|
|
11
15
|
def normalize_paths!
|
@@ -16,52 +20,51 @@ class ParameterParser
|
|
16
20
|
|
17
21
|
def display_help
|
18
22
|
help_text = <<~EOT
|
19
|
-
|
23
|
+
Standalone-Ruby - Make your projects installation independent!
|
20
24
|
|
21
|
-
|
25
|
+
Usage: standalone-ruby [subcommand] [options]
|
22
26
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
27
|
+
Subcommands:
|
28
|
+
archive - archived default output
|
29
|
+
exe - Compressed output to exe file (will be added soon)
|
30
|
+
setup - Output converted to setup file (will be added soon)
|
31
|
+
zip - Reduced size archive output (will be added soon)
|
28
32
|
|
29
|
-
|
30
|
-
|
31
|
-
|
33
|
+
Options:
|
34
|
+
-p, --project PROJECT_PATH Target Ruby project path.
|
35
|
+
Ensures that the given project path exists. If not, an error is displayed.
|
32
36
|
|
33
|
-
|
34
|
-
|
37
|
+
-r, --ruby RUBY_PATH Path to the Ruby interpreter.
|
38
|
+
Ensures that the given Ruby path exists and contains a 'bin' directory.
|
35
39
|
|
36
|
-
|
37
|
-
|
40
|
+
-m, --main MAIN_FILE Path to the main Ruby file of the project.
|
41
|
+
Ensures that the specified Ruby file exists.
|
38
42
|
|
39
|
-
|
40
|
-
|
43
|
+
-l, --launcher LAUNCHER Launcher file name (either .vbs or .bat-cmd).
|
44
|
+
Ensure the launcher file exists and is of the correct type (either .vbs or .bat-cmd).
|
41
45
|
|
42
|
-
|
43
|
-
|
46
|
+
-t, --template TEMPLATE Template file for launcher.
|
47
|
+
Ensures that the specified template file exists.
|
44
48
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
49
|
+
-c, --threads THREADS Number of threads to use (default is 5).
|
50
|
+
Determines the number of threads used during the Ruby interpreter copy process and for Rubocopy operations.
|
51
|
+
A higher number of threads can speed up the process, but requires more system resources.
|
52
|
+
|
53
|
+
-g, --gui This option allows the rubyw.exe file in the bin folder to be used.
|
54
|
+
You can choose it for projects that include GUI.
|
51
55
|
|
52
|
-
|
56
|
+
-h, --help Show this help message.
|
53
57
|
|
54
|
-
|
58
|
+
--version Show program version.
|
55
59
|
|
56
|
-
|
57
|
-
|
60
|
+
For more details, please visit the documentation at:
|
61
|
+
https://github.com/ardatetikbey/Standalone-Ruby
|
58
62
|
|
59
63
|
EOT
|
60
64
|
|
61
65
|
puts help_text
|
62
66
|
end
|
63
67
|
|
64
|
-
|
65
68
|
def parse
|
66
69
|
begin
|
67
70
|
OptionParser.new do |opts|
|
@@ -74,6 +77,7 @@ class ParameterParser
|
|
74
77
|
@params[:project_path] = project_path
|
75
78
|
else
|
76
79
|
print("Parser Error: ".red); puts("The specified project path #{project_path} could not be found!")
|
80
|
+
@logger.error("Parser Error: The project path #{project_path} could not be found!")
|
77
81
|
exit!
|
78
82
|
end
|
79
83
|
end
|
@@ -86,6 +90,7 @@ class ParameterParser
|
|
86
90
|
end
|
87
91
|
else
|
88
92
|
print("Parser Error: ".red); puts("The specified Ruby path #{ruby_path} could not be found!")
|
93
|
+
@logger.error("Parser Error: The specified Ruby path #{ruby_path} could not be found!")
|
89
94
|
exit!
|
90
95
|
end
|
91
96
|
end
|
@@ -96,6 +101,7 @@ class ParameterParser
|
|
96
101
|
@params[:main_file] = main_file
|
97
102
|
else
|
98
103
|
print("Parser Error: ".red); puts("The specified file #{main_file} could not be found!")
|
104
|
+
@logger.error("Parser Error: The specified file #{main_file} could not be found!")
|
99
105
|
exit!
|
100
106
|
end
|
101
107
|
end
|
@@ -105,6 +111,7 @@ class ParameterParser
|
|
105
111
|
@params[:threads] = threads
|
106
112
|
else
|
107
113
|
print("Parser Error: ".red); puts("Invalid value for threads. Please provide an integer.")
|
114
|
+
@logger.error("Parser Error: Invalid value for threads.")
|
108
115
|
exit!
|
109
116
|
end
|
110
117
|
end
|
@@ -125,6 +132,7 @@ class ParameterParser
|
|
125
132
|
@params[:launcher_name] = File.basename(launcher)
|
126
133
|
else
|
127
134
|
print("Parser Error: ".red); puts("The supported launcher #{launcher} could not be found!")
|
135
|
+
@logger.error("Parser Error: The supported launcher path #{launcher} could not be found!")
|
128
136
|
exit!
|
129
137
|
end
|
130
138
|
end
|
@@ -135,6 +143,7 @@ class ParameterParser
|
|
135
143
|
@params[:template] = template
|
136
144
|
else
|
137
145
|
print("Parser Error: ".red); puts("The specified template file #{template} could not be found!")
|
146
|
+
@logger.error("Parser Error: The specified template file #{template} could not be found!")
|
138
147
|
exit!
|
139
148
|
end
|
140
149
|
end
|
@@ -144,7 +153,7 @@ class ParameterParser
|
|
144
153
|
end
|
145
154
|
|
146
155
|
opts.on("--version") do
|
147
|
-
puts "Standalone Ruby Gem Version 1.3"
|
156
|
+
puts "Standalone Ruby Gem Version 1.3.1"
|
148
157
|
exit!
|
149
158
|
end
|
150
159
|
|
@@ -156,12 +165,15 @@ class ParameterParser
|
|
156
165
|
|
157
166
|
if @params[:project_path].nil? || @params[:ruby_path].nil? || @params[:main_file].nil?
|
158
167
|
print("Error: ".red); puts("Missing required parameters. Please provide the necessary parameters:\n -p, -r, -m.\nYou can use the -h parameter for the help menu.")
|
168
|
+
@logger.error("Parser Error: Missing required parameters.")
|
159
169
|
exit!
|
160
170
|
end
|
161
171
|
|
162
172
|
normalize_paths!
|
163
173
|
rescue Exception => e
|
164
174
|
print("Parser Error: ".red); puts("#{e.message}".red)
|
175
|
+
@logger.error("Parser Error: #{e.message}")
|
176
|
+
exit!
|
165
177
|
end
|
166
178
|
end
|
167
179
|
|
data/lib/utils/ruby_copy.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
+
require_relative 'logger_helper'
|
2
|
+
|
1
3
|
class RubyCopy
|
2
4
|
def initialize(params)
|
3
5
|
@params = params
|
6
|
+
@logger = LoggerHelper.instance
|
4
7
|
end
|
5
8
|
|
6
9
|
def robocopy_interpreter
|
@@ -8,14 +11,17 @@ class RubyCopy
|
|
8
11
|
puts("\nThe Ruby interpreter copying process has been started.")
|
9
12
|
print("WARNING: ".yellow); puts("If you are using a development kit, the copying process may take a long time.")
|
10
13
|
|
14
|
+
@logger.info("Robocopy process started using backticks.")
|
11
15
|
copy_output = `robocopy "#{@params[:ruby_path]}" "#{File.join(@params[:project_path].to_s, File.basename(@params[:ruby_path].to_s))}" /E /MT:#{@params[:threads].to_i} /V /ETA /NFL /NDL /R:1000000 /W:30 /NP`
|
12
16
|
puts "Robocopy Command: robocopy \"#{@params[:ruby_path]}\" \"#{File.join(@params[:project_path], File.basename(@params[:ruby_path]))}\" /E /MT:#{@params[:threads].to_i} /V /ETA /NFL /NDL /R:1000000 /W:30 /NP"
|
13
17
|
|
14
18
|
puts copy_output
|
19
|
+
@logger.info("Robocopy output printed to screen.")
|
15
20
|
|
16
21
|
puts("The Ruby interpreter copying process has been completed.")
|
17
22
|
rescue Exception => e
|
18
23
|
print("Copy Error: ".red); puts("#{e.message}".red)
|
24
|
+
@logger.error("Copy Error: #{e.message}")
|
19
25
|
end
|
20
26
|
end
|
21
27
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: standalone-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arda Tetik
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-04-
|
10
|
+
date: 2025-04-04 00:00:00.000000000 Z
|
11
11
|
dependencies: []
|
12
12
|
description: Make your projects installation independent!
|
13
13
|
email:
|