-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.rb
More file actions
172 lines (144 loc) · 5.98 KB
/
Copy pathmain.rb
File metadata and controls
172 lines (144 loc) · 5.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# frozen_string_literal: true
require "json"
require "optparse"
ROOT_DIR = File.expand_path(__dir__)
DEFAULT_PORT = 20_300
# Represents a user-facing command failure without a Ruby backtrace.
class CommandError < StandardError; end
# Parses command-line options shared by every ASLM entry point.
def parse_options(arguments)
options = {
port: DEFAULT_PORT,
key: nil,
value: nil,
file: nil,
log: false
}
parser = OptionParser.new do |definition|
definition.banner = "Usage: ruby main.rb <command> [options]"
definition.on("--port PORT", Integer, "Port for runserver") { |value| options[:port] = value }
definition.on("--key KEY", String, "Setting key") { |value| options[:key] = value }
definition.on("--value VALUE", String, "Setting value") { |value| options[:value] = value }
definition.on("--file PATH", String, "Host JSON snapshot path") { |value| options[:file] = value }
definition.on("--log", "Enable verbose output") { options[:log] = true }
definition.on("-h", "--help", "Show command help") { options[:help] = true }
end
parser.parse!(arguments)
[options, parser]
rescue OptionParser::ParseError => error
raise CommandError, error.message
end
# Prints the module name for commands that are not machine-readable hooks.
def print_banner(command)
silent_commands = %w[get_setting set_setting downloads_bridge apply_aslm_host_theme apply_aslm_locale]
return if silent_commands.include?(command)
manifest = JSON.parse(File.read(File.join(ROOT_DIR, "ASLM_Module.json"), encoding: Encoding::UTF_8))
puts "[ASLM-Example] #{manifest.fetch('name', 'Example Ruby Module')} v#{manifest.fetch('version', '')}"
rescue JSON::ParserError, SystemCallError
nil
end
# Prefers CLI port, then ASLM_UI_PORT, then settings.json example-port.
def resolve_runserver_port(requested_port)
return requested_port unless requested_port == DEFAULT_PORT
# Host-injected port when ASLM starts runserver.
environment_port = ENV.fetch("ASLM_UI_PORT", "").strip
return Integer(environment_port, 10) unless environment_port.empty?
# Fall back to persisted settings.
require_relative "Settings/settings"
Integer(Settings::Store.load_settings.fetch("example-port", DEFAULT_PORT))
rescue ArgumentError, TypeError
DEFAULT_PORT
end
# Starts the Rails UI server on the requested port.
def run_server(port, log: false)
puts "[ASLM-Example] Starting server on port #{port}..." if log
require_relative "App/app"
App::Server.run(port: port, log: log)
end
# Runs Settings/first_run.rb (settings.json only; portable runtime is host-managed).
def run_first_run(port)
require_relative "Settings/first_run"
Settings::FirstRun.run(log: true, ui_port: port)
end
# Prints one setting value to stdout for ASLM getExec.
def get_setting(key)
raise CommandError, "--key argument is required." if key.nil? || key.empty?
require_relative "Settings/settings"
value = Settings::Store.get(key)
puts serialize_cli_value(value)
end
# Parses {value} and persists one setting for ASLM setExec.
def set_setting(key, raw_value)
raise CommandError, "--key and --value arguments are required." if key.nil? || key.empty? || raw_value.nil?
require_relative "Settings/settings"
value = Settings::Store.normalize_setting_value(raw_value)
Settings::Store.set(key, value)
puts "[ASLM-Example] Setting '#{key}' updated to #{value.inspect}"
end
# Loads host theme JSON from --file and writes Settings/host_theme.json.
def apply_host_theme(path)
require_relative "Settings/host_theme"
Settings::HostTheme.save_payload(read_json_object(path, "theme"))
puts "[ASLM-Example] Host theme snapshot updated."
end
# Loads host locale JSON from --file and writes Settings/host_locale.json.
def apply_host_locale(path)
require_relative "Settings/host_locale"
Settings::HostLocale.save_payload(read_json_object(path, "locale"))
puts "[ASLM-Example] Host locale snapshot updated."
end
# Reads and validates one host-provided JSON snapshot.
def read_json_object(path, label)
raise CommandError, "--file argument is required." if path.nil? || path.empty?
raise CommandError, "#{label} file not found: #{path}" unless File.file?(path)
raw = File.read(path, encoding: Encoding::UTF_8).delete_prefix("\uFEFF").strip
payload = JSON.parse(raw)
raise CommandError, "host #{label} JSON must be an object." unless payload.is_a?(Hash)
payload
rescue JSON::ParserError => error
raise CommandError, "invalid JSON in #{label} file: #{error.message}"
rescue SystemCallError => error
raise CommandError, "could not read #{label} file: #{error.message}"
end
# Converts Ruby values into stable getExec stdout text.
def serialize_cli_value(value)
case value
when nil then ""
when true then "true"
when false then "false"
when Hash, Array then JSON.generate(value)
else value.to_s
end
end
# Dispatches one bridge request from stdin and prints the JSON response.
def run_downloads_bridge
require_relative "Services/downloads_bridge"
Services::DownloadsBridge.run_cli
end
# Parses argv, dispatches the requested command, and returns its exit code.
def main(arguments = ARGV)
command = arguments.shift.to_s
options, parser = parse_options(arguments)
command = "help" if options[:help]
print_banner(command)
# Hooks must emit only machine-readable stdout and avoid loading Rails.
case command
when "runserver" then run_server(resolve_runserver_port(options[:port]), log: options[:log])
when "first_run" then run_first_run(options[:port])
when "get_setting" then get_setting(options[:key])
when "set_setting" then set_setting(options[:key], options[:value])
when "apply_aslm_host_theme" then apply_host_theme(options[:file])
when "apply_aslm_locale" then apply_host_locale(options[:file])
when "downloads_bridge" then return run_downloads_bridge
when "help", ""
puts parser
else
raise CommandError, "Unknown command '#{command}'. Run 'ruby main.rb help' for usage."
end
0
rescue CommandError => error
warn "Error: #{error.message}"
1
end
$stdout.sync = true
exit(main) if $PROGRAM_NAME == __FILE__