ruflet 0.0.11 → 0.0.13
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/lib/ruflet/cli/build_command.rb +266 -17
- data/lib/ruflet/cli/extra_command.rb +11 -1
- data/lib/ruflet/cli/flutter_sdk.rb +16 -1
- data/lib/ruflet/cli/new_command.rb +47 -12
- data/lib/ruflet/cli/run_command.rb +131 -8
- data/lib/ruflet/cli/templates.rb +4 -3
- data/lib/ruflet/cli/update_command.rb +14 -0
- data/lib/ruflet/cli.rb +13 -1
- data/lib/ruflet/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 877c730a76717b1a9623d285666db0cb3b5a47fb08eafe0e9d3bb49c681c0b88
|
|
4
|
+
data.tar.gz: a649878ca4f0a7fc8d752e6ba2e0f3eaa1963213fa96dfee3f4a52d2905e4b9a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a1a15a38fa5973a84e921d3b57fd54a135d5d4090fdd996294991f0fede09a89e1553037388f7a07a9dace52b9b4e753273d4da2b0940ca6f326344b708b0de2
|
|
7
|
+
data.tar.gz: d10353a23bb436679d2a82226f490ad7c2ef1d470b33808ff3d523a94432f871982a1ed998a2c9fc80a9321ed07fbf2ad8ba23633dbe492e2ebc20c1b03fea96
|
|
@@ -46,6 +46,7 @@ module Ruflet
|
|
|
46
46
|
return 1
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
+
ensure_ruflet_build_assets(verbose: !!verbose)
|
|
49
50
|
client_dir = ensure_flutter_client_dir(verbose: !!verbose)
|
|
50
51
|
unless client_dir
|
|
51
52
|
warn "Could not find Flutter client directory."
|
|
@@ -340,6 +341,15 @@ module Ruflet
|
|
|
340
341
|
Dir.exist?(target) ? target : nil
|
|
341
342
|
end
|
|
342
343
|
|
|
344
|
+
def ensure_ruflet_build_assets(force: false, verbose: false)
|
|
345
|
+
return true unless respond_to?(:download_ruflet_assets, true)
|
|
346
|
+
|
|
347
|
+
!!send(:download_ruflet_assets, force: force, verbose: verbose)
|
|
348
|
+
rescue StandardError => e
|
|
349
|
+
build_log(verbose, "ruflet asset bootstrap skipped: #{e.class}: #{e.message}")
|
|
350
|
+
false
|
|
351
|
+
end
|
|
352
|
+
|
|
343
353
|
def hidden_flutter_client_dir(root = Dir.pwd)
|
|
344
354
|
File.join(root, "build", "client")
|
|
345
355
|
end
|
|
@@ -352,6 +362,7 @@ module Ruflet
|
|
|
352
362
|
refresh_managed_client_template_files(client_dir, verbose: verbose)
|
|
353
363
|
sync_client_metadata(client_dir, config, verbose: verbose)
|
|
354
364
|
configure_client_runtime_mode(client_dir, self_contained: self_contained, verbose: verbose)
|
|
365
|
+
@ruflet_self_contained_build = self_contained
|
|
355
366
|
apply_service_extension_config(client_dir, config)
|
|
356
367
|
asset_flags = apply_build_config(client_dir, config)
|
|
357
368
|
if asset_flags[:error]
|
|
@@ -361,6 +372,9 @@ module Ruflet
|
|
|
361
372
|
announce_asset_configuration(asset_flags)
|
|
362
373
|
clear_flutter_build_state(client_dir, verbose: verbose)
|
|
363
374
|
clear_stale_platform_outputs(client_dir, platform, verbose: verbose)
|
|
375
|
+
unless ensure_flutter_platform_artifacts(client_dir, platform, tools[:env], tools[:flutter], verbose: verbose)
|
|
376
|
+
return false
|
|
377
|
+
end
|
|
364
378
|
build_note("Resolving Flutter packages")
|
|
365
379
|
build_log(verbose, "running flutter pub get")
|
|
366
380
|
unless run_external_command(tools[:env], tools[:flutter], "pub", "get", chdir: client_dir, unbundled: true)
|
|
@@ -393,6 +407,38 @@ module Ruflet
|
|
|
393
407
|
true
|
|
394
408
|
end
|
|
395
409
|
|
|
410
|
+
def ensure_flutter_platform_artifacts(client_dir, platform, env, flutter, verbose: false)
|
|
411
|
+
precache_flags = flutter_precache_flags(platform)
|
|
412
|
+
return true if precache_flags.empty?
|
|
413
|
+
|
|
414
|
+
build_note("Preparing Flutter #{platform} platform artifacts")
|
|
415
|
+
build_log(verbose, "running flutter precache #{precache_flags.join(' ')}")
|
|
416
|
+
ok = run_external_command(env, flutter, "precache", *precache_flags, chdir: client_dir, unbundled: true)
|
|
417
|
+
return true if ok
|
|
418
|
+
|
|
419
|
+
warn "Flutter platform artifact setup failed for #{platform}"
|
|
420
|
+
false
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
def flutter_precache_flags(platform)
|
|
424
|
+
case platform
|
|
425
|
+
when "apk", "android", "aab", "appbundle"
|
|
426
|
+
["--android"]
|
|
427
|
+
when "ios"
|
|
428
|
+
["--ios"]
|
|
429
|
+
when "macos"
|
|
430
|
+
["--macos"]
|
|
431
|
+
when "windows"
|
|
432
|
+
["--windows"]
|
|
433
|
+
when "linux"
|
|
434
|
+
["--linux"]
|
|
435
|
+
when "web"
|
|
436
|
+
["--web"]
|
|
437
|
+
else
|
|
438
|
+
[]
|
|
439
|
+
end
|
|
440
|
+
end
|
|
441
|
+
|
|
396
442
|
def ensure_native_build_dependencies(client_dir, platform, env, verbose: false)
|
|
397
443
|
case platform
|
|
398
444
|
when "ios"
|
|
@@ -430,15 +476,7 @@ module Ruflet
|
|
|
430
476
|
end
|
|
431
477
|
|
|
432
478
|
def unbundled_command_env(env)
|
|
433
|
-
|
|
434
|
-
cleared_env = {}
|
|
435
|
-
ENV.each_key do |key|
|
|
436
|
-
next unless key.start_with?("BUNDLE_") || key == "RUBYOPT" || key == "RUBYLIB" || key.start_with?("GEM_")
|
|
437
|
-
|
|
438
|
-
cleared_env[key] = nil
|
|
439
|
-
end
|
|
440
|
-
|
|
441
|
-
cleared_env.merge(sanitized_env)
|
|
479
|
+
env.reject { |key, _value| key.start_with?("BUNDLE_") || key == "RUBYOPT" || key == "RUBYLIB" || key.start_with?("GEM_") }
|
|
442
480
|
end
|
|
443
481
|
|
|
444
482
|
def run_external_command(env, *cmd, chdir:, unbundled: false)
|
|
@@ -654,6 +692,7 @@ module Ruflet
|
|
|
654
692
|
apply_web_metadata(client_dir, metadata)
|
|
655
693
|
apply_windows_metadata(client_dir, metadata)
|
|
656
694
|
apply_linux_metadata(client_dir, metadata)
|
|
695
|
+
apply_dart_metadata(client_dir, metadata)
|
|
657
696
|
build_log(
|
|
658
697
|
verbose,
|
|
659
698
|
"app=#{metadata[:display_name]} package=#{metadata[:package_name]} org=#{metadata[:organization]} bundle=#{metadata[:bundle_identifier]}"
|
|
@@ -851,6 +890,14 @@ module Ruflet
|
|
|
851
890
|
replace_in_file(cmake_path, /^set\(APPLICATION_ID ".*"\)$/, %(set(APPLICATION_ID "#{metadata[:linux_application_id]}")))
|
|
852
891
|
end
|
|
853
892
|
|
|
893
|
+
def apply_dart_metadata(client_dir, metadata)
|
|
894
|
+
title = dart_single_quote_escape(metadata[:display_name])
|
|
895
|
+
client_entrypoint_paths(client_dir).each do |entrypoint|
|
|
896
|
+
replace_in_file(entrypoint, /title: 'Ruflet'/, "title: '#{title}'")
|
|
897
|
+
replace_in_file(entrypoint, /AppBar\(title: const Text\('Ruflet'\)\)/, "AppBar(title: const Text('#{title}'))")
|
|
898
|
+
end
|
|
899
|
+
end
|
|
900
|
+
|
|
854
901
|
def replace_plist_value(path, key, value)
|
|
855
902
|
return unless File.file?(path)
|
|
856
903
|
|
|
@@ -866,7 +913,7 @@ module Ruflet
|
|
|
866
913
|
return unless File.file?(path)
|
|
867
914
|
|
|
868
915
|
content = File.read(path)
|
|
869
|
-
updated = content.gsub(pattern
|
|
916
|
+
updated = content.gsub(pattern) { replacement }
|
|
870
917
|
File.write(path, updated) unless updated == content
|
|
871
918
|
end
|
|
872
919
|
|
|
@@ -933,19 +980,27 @@ module Ruflet
|
|
|
933
980
|
value.to_s.gsub('"', '""')
|
|
934
981
|
end
|
|
935
982
|
|
|
983
|
+
def dart_single_quote_escape(value)
|
|
984
|
+
value.to_s.gsub("\\", "\\\\\\").gsub("'", "\\\\'")
|
|
985
|
+
end
|
|
986
|
+
|
|
936
987
|
def key_defined?(hash, key)
|
|
937
988
|
hash.is_a?(Hash) && (hash.key?(key) || hash.key?(key.to_sym))
|
|
938
989
|
end
|
|
939
990
|
|
|
940
|
-
def apply_service_extension_config(client_dir, config = {})
|
|
991
|
+
def apply_service_extension_config(client_dir, config = {}, self_contained: @ruflet_self_contained_build)
|
|
941
992
|
services = Array(config["services"])
|
|
942
993
|
extension_keys = services.map { |v| normalize_extension_key(v) }.compact.uniq
|
|
943
994
|
extension_packages = extension_keys.filter_map { |key| CLIENT_EXTENSION_MAP[key]&.fetch(:package) }.uniq
|
|
944
995
|
extension_aliases = extension_keys.filter_map { |key| CLIENT_EXTENSION_MAP[key]&.fetch(:alias) }.uniq
|
|
945
996
|
|
|
946
997
|
pubspec_path = File.join(client_dir, "pubspec.yaml")
|
|
947
|
-
|
|
998
|
+
if File.file?(pubspec_path)
|
|
999
|
+
sync_client_extension_dependencies(pubspec_path, extension_packages)
|
|
1000
|
+
prune_client_pubspec(pubspec_path, extension_packages)
|
|
1001
|
+
end
|
|
948
1002
|
client_entrypoint_paths(client_dir).each do |entrypoint|
|
|
1003
|
+
sync_client_main_extensions(entrypoint, extension_aliases) if File.file?(entrypoint)
|
|
949
1004
|
prune_client_main(entrypoint, extension_aliases) if File.file?(entrypoint)
|
|
950
1005
|
end
|
|
951
1006
|
end
|
|
@@ -1005,7 +1060,7 @@ module Ruflet
|
|
|
1005
1060
|
assets = Array(flutter["assets"]).map(&:to_s)
|
|
1006
1061
|
|
|
1007
1062
|
if self_contained
|
|
1008
|
-
dependencies["ruby_runtime"] = "
|
|
1063
|
+
dependencies["ruby_runtime"] = ruby_runtime_dependency(dependencies["ruby_runtime"])
|
|
1009
1064
|
assets.delete("assets/main.rb")
|
|
1010
1065
|
assets.delete("assets/ruby_project/")
|
|
1011
1066
|
project_asset_path = "assets/#{self_contained_project_name}/"
|
|
@@ -1022,6 +1077,23 @@ module Ruflet
|
|
|
1022
1077
|
write_pubspec_yaml(pubspec_path, data)
|
|
1023
1078
|
end
|
|
1024
1079
|
|
|
1080
|
+
def ruby_runtime_dependency(current_dependency = nil)
|
|
1081
|
+
local_path = explicit_local_ruby_runtime_path
|
|
1082
|
+
return { "path" => local_path } if local_path
|
|
1083
|
+
|
|
1084
|
+
current_dependency || "^0.0.3"
|
|
1085
|
+
end
|
|
1086
|
+
|
|
1087
|
+
def explicit_local_ruby_runtime_path
|
|
1088
|
+
env_path = ENV["RUFLET_RUBY_RUNTIME_PATH"].to_s.strip
|
|
1089
|
+
return nil if env_path.empty?
|
|
1090
|
+
|
|
1091
|
+
candidate = Pathname.new(env_path).expand_path
|
|
1092
|
+
return candidate.to_s if candidate.join("pubspec.yaml").file?
|
|
1093
|
+
|
|
1094
|
+
nil
|
|
1095
|
+
end
|
|
1096
|
+
|
|
1025
1097
|
def refresh_managed_client_template_files(client_dir, verbose: false)
|
|
1026
1098
|
template_root =
|
|
1027
1099
|
if Ruflet::CLI.respond_to?(:resolve_ruflet_client_template_root, true)
|
|
@@ -1035,7 +1107,10 @@ module Ruflet
|
|
|
1035
1107
|
"lib/main.server.dart",
|
|
1036
1108
|
"lib/connection_probe.dart",
|
|
1037
1109
|
"lib/connection_probe_io.dart",
|
|
1038
|
-
"lib/connection_probe_stub.dart"
|
|
1110
|
+
"lib/connection_probe_stub.dart",
|
|
1111
|
+
"lib/ruflet_file_picker_service.dart",
|
|
1112
|
+
"macos/Runner/DebugProfile.entitlements",
|
|
1113
|
+
"macos/Runner/Release.entitlements"
|
|
1039
1114
|
]
|
|
1040
1115
|
|
|
1041
1116
|
managed_files.each do |relative_path|
|
|
@@ -1089,13 +1164,100 @@ module Ruflet
|
|
|
1089
1164
|
|
|
1090
1165
|
destination = File.join(destination_root, relative_path)
|
|
1091
1166
|
FileUtils.mkdir_p(File.dirname(destination))
|
|
1092
|
-
|
|
1167
|
+
copy_project_asset_file(source.to_s, destination, project_root: project_root.to_s)
|
|
1093
1168
|
copied += 1
|
|
1094
1169
|
end
|
|
1095
1170
|
|
|
1096
1171
|
build_log(verbose, "copied #{copied} project file#{copied == 1 ? '' : 's'} to assets/#{self_contained_project_name}")
|
|
1097
1172
|
end
|
|
1098
1173
|
|
|
1174
|
+
def copy_project_asset_file(source, destination, project_root: nil)
|
|
1175
|
+
if File.extname(source).downcase == ".rb"
|
|
1176
|
+
ruby_source =
|
|
1177
|
+
if File.basename(source) == "main.rb" && project_root
|
|
1178
|
+
bundled_embedded_ruby_source(source, project_root)
|
|
1179
|
+
else
|
|
1180
|
+
File.read(source)
|
|
1181
|
+
end
|
|
1182
|
+
File.write(destination, normalize_embedded_ruby_source(ruby_source))
|
|
1183
|
+
else
|
|
1184
|
+
FileUtils.cp(source, destination)
|
|
1185
|
+
end
|
|
1186
|
+
end
|
|
1187
|
+
|
|
1188
|
+
def bundled_embedded_ruby_source(source, project_root, visited = {})
|
|
1189
|
+
absolute = File.expand_path(source)
|
|
1190
|
+
return "" if visited[absolute]
|
|
1191
|
+
|
|
1192
|
+
visited[absolute] = true
|
|
1193
|
+
base = File.dirname(absolute)
|
|
1194
|
+
File.read(absolute).lines.map do |line|
|
|
1195
|
+
match = line.match(/^\s*require_relative\s+["']([^"']+)["']\s*$/)
|
|
1196
|
+
next line unless match
|
|
1197
|
+
|
|
1198
|
+
required = File.expand_path(match[1], base)
|
|
1199
|
+
required = "#{required}.rb" unless File.file?(required)
|
|
1200
|
+
next line unless File.file?(required) && required.start_with?(File.expand_path(project_root))
|
|
1201
|
+
|
|
1202
|
+
bundled_embedded_ruby_source(required, project_root, visited)
|
|
1203
|
+
end.join
|
|
1204
|
+
end
|
|
1205
|
+
|
|
1206
|
+
def normalize_embedded_ruby_source(source)
|
|
1207
|
+
source.lines.map do |line|
|
|
1208
|
+
expand_endless_method_line(line)
|
|
1209
|
+
end.join
|
|
1210
|
+
end
|
|
1211
|
+
|
|
1212
|
+
def expand_endless_method_line(line)
|
|
1213
|
+
match = line.match(/^(\s*)def\s+(.+)$/)
|
|
1214
|
+
return line unless match
|
|
1215
|
+
|
|
1216
|
+
indent = match[1]
|
|
1217
|
+
body = match[2].chomp
|
|
1218
|
+
newline = line.end_with?("\n") ? "\n" : ""
|
|
1219
|
+
split = endless_method_split(body)
|
|
1220
|
+
return line unless split
|
|
1221
|
+
|
|
1222
|
+
signature, expression = split
|
|
1223
|
+
"#{indent}def #{signature.rstrip}\n#{indent} #{expression.lstrip}\n#{indent}end#{newline}"
|
|
1224
|
+
end
|
|
1225
|
+
|
|
1226
|
+
def endless_method_split(body)
|
|
1227
|
+
depth = 0
|
|
1228
|
+
quote = nil
|
|
1229
|
+
escape = false
|
|
1230
|
+
|
|
1231
|
+
body.each_char.with_index do |char, index|
|
|
1232
|
+
if quote
|
|
1233
|
+
escape = char == "\\" && !escape
|
|
1234
|
+
if char == quote && !escape
|
|
1235
|
+
quote = nil
|
|
1236
|
+
elsif char != "\\"
|
|
1237
|
+
escape = false
|
|
1238
|
+
end
|
|
1239
|
+
next
|
|
1240
|
+
end
|
|
1241
|
+
|
|
1242
|
+
case char
|
|
1243
|
+
when "'", '"'
|
|
1244
|
+
quote = char
|
|
1245
|
+
when "(", "[", "{"
|
|
1246
|
+
depth += 1
|
|
1247
|
+
when ")", "]", "}"
|
|
1248
|
+
depth -= 1 if depth.positive?
|
|
1249
|
+
when "="
|
|
1250
|
+
next unless depth.zero? && body[index + 1] == " "
|
|
1251
|
+
|
|
1252
|
+
signature = body[0...index]
|
|
1253
|
+
expression = body[(index + 1)..]
|
|
1254
|
+
return [signature, expression] unless signature.strip.empty? || expression.to_s.strip.empty?
|
|
1255
|
+
end
|
|
1256
|
+
end
|
|
1257
|
+
|
|
1258
|
+
nil
|
|
1259
|
+
end
|
|
1260
|
+
|
|
1099
1261
|
def remove_self_contained_project_assets(client_dir, verbose: false)
|
|
1100
1262
|
assets_root = File.join(client_dir, "assets")
|
|
1101
1263
|
legacy_entrypoint = File.join(client_dir, "assets", "main.rb")
|
|
@@ -1226,7 +1388,94 @@ module Ruflet
|
|
|
1226
1388
|
end
|
|
1227
1389
|
|
|
1228
1390
|
data["dependencies"] = deps
|
|
1229
|
-
|
|
1391
|
+
write_pubspec_yaml(path, data)
|
|
1392
|
+
end
|
|
1393
|
+
|
|
1394
|
+
def sync_client_extension_dependencies(path, selected_packages)
|
|
1395
|
+
return if selected_packages.empty?
|
|
1396
|
+
|
|
1397
|
+
template_deps = template_client_pubspec_dependencies
|
|
1398
|
+
return if template_deps.empty?
|
|
1399
|
+
|
|
1400
|
+
data = YAML.safe_load(File.read(path), aliases: true) || {}
|
|
1401
|
+
deps = (data["dependencies"] || {}).dup
|
|
1402
|
+
selected_packages.each do |package_name|
|
|
1403
|
+
deps[package_name] = template_deps[package_name] if template_deps.key?(package_name)
|
|
1404
|
+
end
|
|
1405
|
+
|
|
1406
|
+
data["dependencies"] = deps
|
|
1407
|
+
write_pubspec_yaml(path, data)
|
|
1408
|
+
end
|
|
1409
|
+
|
|
1410
|
+
def template_client_pubspec_dependencies
|
|
1411
|
+
template_root =
|
|
1412
|
+
if Ruflet::CLI.respond_to?(:resolve_ruflet_client_template_root, true)
|
|
1413
|
+
Ruflet::CLI.send(:resolve_ruflet_client_template_root)
|
|
1414
|
+
end
|
|
1415
|
+
return {} unless template_root
|
|
1416
|
+
|
|
1417
|
+
pubspec_path = File.join(template_root, "pubspec.yaml")
|
|
1418
|
+
return {} unless File.file?(pubspec_path)
|
|
1419
|
+
|
|
1420
|
+
data = YAML.safe_load(File.read(pubspec_path), aliases: true) || {}
|
|
1421
|
+
deps = data["dependencies"]
|
|
1422
|
+
deps.is_a?(Hash) ? deps : {}
|
|
1423
|
+
rescue StandardError
|
|
1424
|
+
{}
|
|
1425
|
+
end
|
|
1426
|
+
|
|
1427
|
+
def sync_client_main_extensions(path, selected_aliases)
|
|
1428
|
+
return if selected_aliases.empty?
|
|
1429
|
+
|
|
1430
|
+
template_path = template_client_entrypoint_path(File.basename(path))
|
|
1431
|
+
return unless template_path
|
|
1432
|
+
|
|
1433
|
+
content = File.read(path)
|
|
1434
|
+
template = File.read(template_path)
|
|
1435
|
+
|
|
1436
|
+
selected_aliases.each do |extension_alias|
|
|
1437
|
+
import_line = template.lines.find { |line| line.match?(/\sas #{Regexp.escape(extension_alias)};\s*\z/) }
|
|
1438
|
+
extension_line = template.lines.find { |line| line.match?(/^\s*#{Regexp.escape(extension_alias)}\.Extension\(\),\s*$/) }
|
|
1439
|
+
|
|
1440
|
+
content = insert_missing_import(content, import_line) if import_line && !content.include?(import_line)
|
|
1441
|
+
content = insert_missing_extension(content, extension_line) if extension_line && !content.include?(extension_line)
|
|
1442
|
+
end
|
|
1443
|
+
|
|
1444
|
+
File.write(path, content)
|
|
1445
|
+
end
|
|
1446
|
+
|
|
1447
|
+
def template_client_entrypoint_path(name)
|
|
1448
|
+
template_root =
|
|
1449
|
+
if Ruflet::CLI.respond_to?(:resolve_ruflet_client_template_root, true)
|
|
1450
|
+
Ruflet::CLI.send(:resolve_ruflet_client_template_root)
|
|
1451
|
+
end
|
|
1452
|
+
return nil unless template_root
|
|
1453
|
+
|
|
1454
|
+
path = File.join(template_root, "lib", name)
|
|
1455
|
+
File.file?(path) ? path : nil
|
|
1456
|
+
end
|
|
1457
|
+
|
|
1458
|
+
def insert_missing_import(content, import_line)
|
|
1459
|
+
lines = content.lines
|
|
1460
|
+
last_import_index = lines.rindex { |line| line.start_with?("import ") }
|
|
1461
|
+
if last_import_index
|
|
1462
|
+
lines.insert(last_import_index + 1, import_line)
|
|
1463
|
+
else
|
|
1464
|
+
lines.unshift(import_line)
|
|
1465
|
+
end
|
|
1466
|
+
lines.join
|
|
1467
|
+
end
|
|
1468
|
+
|
|
1469
|
+
def insert_missing_extension(content, extension_line)
|
|
1470
|
+
lines = content.lines
|
|
1471
|
+
marker_index = lines.index { |line| line.include?("// --FAT_CLIENT_START--") }
|
|
1472
|
+
if marker_index
|
|
1473
|
+
lines.insert(marker_index, extension_line)
|
|
1474
|
+
else
|
|
1475
|
+
list_index = lines.index { |line| line.include?("final extensions = <FletExtension>[") }
|
|
1476
|
+
lines.insert(list_index ? list_index + 1 : lines.length, extension_line)
|
|
1477
|
+
end
|
|
1478
|
+
lines.join
|
|
1230
1479
|
end
|
|
1231
1480
|
|
|
1232
1481
|
def prune_client_main(path, selected_aliases)
|
|
@@ -1296,7 +1545,7 @@ module Ruflet
|
|
|
1296
1545
|
if in_block && !replaced
|
|
1297
1546
|
out << "#{block_indent}#{key}: #{value}"
|
|
1298
1547
|
end
|
|
1299
|
-
File.write(path, out.join("\n"))
|
|
1548
|
+
File.write(path, indent_pubspec_sequences(out.join("\n")))
|
|
1300
1549
|
end
|
|
1301
1550
|
|
|
1302
1551
|
def flutter_build_command(platform)
|
|
@@ -23,7 +23,7 @@ module Ruflet
|
|
|
23
23
|
if template_root
|
|
24
24
|
puts " Template: #{template_root}"
|
|
25
25
|
elsif fix
|
|
26
|
-
template_root = ensure_cached_ruflet_client_template!(verbose: !!verbose)
|
|
26
|
+
template_root = ensure_cached_ruflet_client_template!(force: true, verbose: !!verbose)
|
|
27
27
|
unless template_root
|
|
28
28
|
warn " Template: missing"
|
|
29
29
|
warn "Failed to fetch the Ruflet Flutter template from GitHub."
|
|
@@ -34,6 +34,7 @@ module Ruflet
|
|
|
34
34
|
warn " Template: missing"
|
|
35
35
|
warn "Run `ruflet doctor --fix` to fetch the Flutter template from GitHub."
|
|
36
36
|
end
|
|
37
|
+
puts " Ruby runtime: pub.dev package"
|
|
37
38
|
if fix
|
|
38
39
|
tools = ensure_flutter!("doctor", client_dir: client_dir, auto_install: true)
|
|
39
40
|
else
|
|
@@ -144,6 +145,15 @@ module Ruflet
|
|
|
144
145
|
|
|
145
146
|
private
|
|
146
147
|
|
|
148
|
+
def resolve_ruby_runtime_root
|
|
149
|
+
env_path = ENV["RUFLET_RUBY_RUNTIME_PATH"].to_s.strip
|
|
150
|
+
candidates = []
|
|
151
|
+
candidates << File.expand_path(env_path) unless env_path.empty?
|
|
152
|
+
candidates << File.expand_path("../../../../../ruby_runtime", __dir__)
|
|
153
|
+
candidates << cached_ruby_runtime_root
|
|
154
|
+
candidates.find { |path| File.file?(File.join(path, "pubspec.yaml")) }
|
|
155
|
+
end
|
|
156
|
+
|
|
147
157
|
def detect_client_dir
|
|
148
158
|
env_dir = ENV["RUFLET_CLIENT_DIR"]
|
|
149
159
|
return env_dir if env_dir && Dir.exist?(env_dir)
|
|
@@ -47,13 +47,25 @@ module Ruflet
|
|
|
47
47
|
private
|
|
48
48
|
|
|
49
49
|
def flutter_tools(client_dir: nil, auto_install: true)
|
|
50
|
-
#
|
|
50
|
+
# Prefer FVM when available so Flutter/Dart match a pinned SDK.
|
|
51
51
|
fvm_tools = flutter_tools_via_fvm(client_dir: client_dir, auto_install: auto_install)
|
|
52
52
|
return fvm_tools if fvm_tools
|
|
53
53
|
|
|
54
|
+
managed_tools = flutter_tools_via_managed_sdk(client_dir: client_dir, auto_install: auto_install)
|
|
55
|
+
return managed_tools if managed_tools
|
|
56
|
+
|
|
54
57
|
nil
|
|
55
58
|
end
|
|
56
59
|
|
|
60
|
+
def flutter_tools_via_managed_sdk(client_dir: nil, auto_install: true)
|
|
61
|
+
return nil unless auto_install
|
|
62
|
+
|
|
63
|
+
sdk_root = ensure_flutter_sdk_downloaded(client_dir: client_dir)
|
|
64
|
+
return nil unless sdk_root
|
|
65
|
+
|
|
66
|
+
tools_from_flutter_bin(File.join(sdk_root, "bin", flutter_executable_name))
|
|
67
|
+
end
|
|
68
|
+
|
|
57
69
|
def flutter_tools_via_fvm(client_dir: nil, auto_install: true)
|
|
58
70
|
version = desired_flutter_version(client_dir: client_dir)
|
|
59
71
|
return nil if version.to_s.strip.empty?
|
|
@@ -226,6 +238,9 @@ module Ruflet
|
|
|
226
238
|
template_client = File.expand_path("../../../../../templates/ruflet_flutter_template/.metadata", __dir__)
|
|
227
239
|
candidates << repo_client
|
|
228
240
|
candidates << template_client
|
|
241
|
+
if Ruflet::CLI.respond_to?(:cached_ruflet_client_template_root, true)
|
|
242
|
+
candidates << File.join(Ruflet::CLI.send(:cached_ruflet_client_template_root), ".metadata")
|
|
243
|
+
end
|
|
229
244
|
candidates.find { |path| File.file?(path) }
|
|
230
245
|
end
|
|
231
246
|
|
|
@@ -7,6 +7,9 @@ require "yaml"
|
|
|
7
7
|
module Ruflet
|
|
8
8
|
module CLI
|
|
9
9
|
module NewCommand
|
|
10
|
+
TEMPLATE_REPO_URL = ENV.fetch("RUFLET_TEMPLATE_REPO_URL", "https://github.com/AdamMusa/ruflet-template.git")
|
|
11
|
+
RUNTIME_REPO_URL = ENV.fetch("RUFLET_RUNTIME_REPO_URL", "https://github.com/AdamMusa/ruflet.git")
|
|
12
|
+
|
|
10
13
|
CLIENT_EXTENSION_MAP = {
|
|
11
14
|
"ads" => { package: "flet_ads", alias: "ruflet_ads" },
|
|
12
15
|
"audio" => { package: "flet_audio", alias: "ruflet_audio" },
|
|
@@ -74,40 +77,69 @@ module Ruflet
|
|
|
74
77
|
end
|
|
75
78
|
|
|
76
79
|
def resolve_ruflet_client_template_root
|
|
77
|
-
|
|
78
|
-
|
|
80
|
+
[
|
|
81
|
+
File.expand_path("../../../../../templates/ruflet_flutter_template", __dir__)
|
|
82
|
+
].each do |template|
|
|
83
|
+
return template if Dir.exist?(template)
|
|
84
|
+
end
|
|
79
85
|
|
|
80
86
|
cached_template = cached_ruflet_client_template_root
|
|
81
87
|
return cached_template if Dir.exist?(cached_template)
|
|
82
88
|
|
|
83
|
-
|
|
84
|
-
|
|
89
|
+
[
|
|
90
|
+
File.expand_path("../../../ruflet_client", __dir__),
|
|
91
|
+
File.expand_path("../../../../../ruflet_client", __dir__)
|
|
92
|
+
].each do |fallback|
|
|
93
|
+
return fallback if Dir.exist?(fallback)
|
|
94
|
+
end
|
|
85
95
|
|
|
86
96
|
nil
|
|
87
97
|
end
|
|
88
98
|
|
|
89
|
-
def ensure_cached_ruflet_client_template!(verbose: false)
|
|
99
|
+
def ensure_cached_ruflet_client_template!(force: false, verbose: false)
|
|
90
100
|
cached_template = cached_ruflet_client_template_root
|
|
91
|
-
return cached_template if Dir.exist?(cached_template)
|
|
101
|
+
return cached_template if !force && Dir.exist?(cached_template)
|
|
102
|
+
|
|
103
|
+
download_ruflet_template(force: force, verbose: verbose)
|
|
104
|
+
Dir.exist?(cached_template) ? cached_template : nil
|
|
105
|
+
end
|
|
92
106
|
|
|
93
|
-
|
|
107
|
+
def ensure_cached_ruby_runtime!(force: false, verbose: false)
|
|
108
|
+
nil
|
|
94
109
|
end
|
|
95
110
|
|
|
96
111
|
def cached_ruflet_client_template_root
|
|
97
112
|
File.join(template_cache_root, "ruflet_flutter_template")
|
|
98
113
|
end
|
|
99
114
|
|
|
115
|
+
def cached_ruby_runtime_root
|
|
116
|
+
File.join(cache_root, "ruby_runtime")
|
|
117
|
+
end
|
|
118
|
+
|
|
100
119
|
def template_cache_root
|
|
101
|
-
File.join(
|
|
120
|
+
File.join(cache_root, "templates")
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def cache_root
|
|
124
|
+
ENV.fetch("RUFLET_CACHE_DIR", File.join(Dir.home, ".ruflet"))
|
|
102
125
|
end
|
|
103
126
|
|
|
104
|
-
def
|
|
127
|
+
def download_ruflet_assets(force: false, verbose: false)
|
|
128
|
+
template_target = cached_ruflet_client_template_root
|
|
129
|
+
return true if !force && Dir.exist?(template_target)
|
|
130
|
+
|
|
131
|
+
Dir.exist?(template_target) || download_ruflet_template(force: force, verbose: verbose)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def download_ruflet_template(force: false, verbose: false)
|
|
105
135
|
target = cached_ruflet_client_template_root
|
|
136
|
+
return target if !force && Dir.exist?(target)
|
|
137
|
+
|
|
106
138
|
FileUtils.mkdir_p(template_cache_root)
|
|
107
139
|
|
|
108
|
-
Dir.mktmpdir("ruflet-
|
|
140
|
+
Dir.mktmpdir("ruflet-assets") do |tmp|
|
|
109
141
|
repo_dir = File.join(tmp, "Ruflet")
|
|
110
|
-
clone_cmd = ["git", "clone", "--depth", "1", "--filter=blob:none", "--sparse",
|
|
142
|
+
clone_cmd = ["git", "clone", "--depth", "1", "--filter=blob:none", "--sparse", TEMPLATE_REPO_URL, repo_dir]
|
|
111
143
|
return nil unless run_template_command(clone_cmd, verbose: verbose)
|
|
112
144
|
return nil unless run_template_command(["git", "-C", repo_dir, "sparse-checkout", "set", "templates/ruflet_flutter_template"], verbose: verbose)
|
|
113
145
|
|
|
@@ -164,7 +196,10 @@ module Ruflet
|
|
|
164
196
|
backend_url: ""
|
|
165
197
|
|
|
166
198
|
# Source of truth for Flutter client extensions/plugins.
|
|
167
|
-
#
|
|
199
|
+
# To test every extension, list all services:
|
|
200
|
+
# ads, audio, audio_recorder, camera, charts, code_editor, color_pickers,
|
|
201
|
+
# datatable2, flashlight, geolocator, lottie, map, permission_handler,
|
|
202
|
+
# secure_storage, video, webview
|
|
168
203
|
services: []
|
|
169
204
|
|
|
170
205
|
# Build assets configuration consumed by `ruflet build`.
|
|
@@ -12,6 +12,7 @@ require "uri"
|
|
|
12
12
|
require "thread"
|
|
13
13
|
require "io/console"
|
|
14
14
|
require "time"
|
|
15
|
+
require "yaml"
|
|
15
16
|
|
|
16
17
|
module Ruflet
|
|
17
18
|
module CLI
|
|
@@ -285,7 +286,7 @@ module Ruflet
|
|
|
285
286
|
end
|
|
286
287
|
|
|
287
288
|
def launch_desktop_client(url)
|
|
288
|
-
cmd = detect_desktop_client_command(url)
|
|
289
|
+
cmd = detect_project_desktop_client_command(url) || detect_desktop_client_command(url)
|
|
289
290
|
unless cmd
|
|
290
291
|
warn "Desktop client executable not found."
|
|
291
292
|
warn "Set RUFLET_CLIENT_DIR to your client path."
|
|
@@ -306,6 +307,76 @@ module Ruflet
|
|
|
306
307
|
[]
|
|
307
308
|
end
|
|
308
309
|
|
|
310
|
+
def detect_project_desktop_client_command(url)
|
|
311
|
+
return nil unless project_run_requires_managed_client?
|
|
312
|
+
return nil unless respond_to?(:ensure_flutter_client_dir, true)
|
|
313
|
+
return nil unless respond_to?(:prepare_flutter_client, true)
|
|
314
|
+
return nil unless respond_to?(:ensure_flutter!, true)
|
|
315
|
+
|
|
316
|
+
platform = host_platform_name
|
|
317
|
+
return nil unless %w[macos windows linux].include?(platform)
|
|
318
|
+
|
|
319
|
+
ensure_ruflet_build_assets(verbose: false) if respond_to?(:ensure_ruflet_build_assets, true)
|
|
320
|
+
client_dir = ensure_flutter_client_dir(verbose: false)
|
|
321
|
+
return nil unless client_dir
|
|
322
|
+
|
|
323
|
+
config = project_run_config
|
|
324
|
+
tools = ensure_flutter!("run", client_dir: client_dir)
|
|
325
|
+
env = build_tool_env(tools[:env], platform, client_dir)
|
|
326
|
+
return nil unless prepare_flutter_client(
|
|
327
|
+
client_dir,
|
|
328
|
+
platform: platform,
|
|
329
|
+
tools: tools,
|
|
330
|
+
config: config,
|
|
331
|
+
self_contained: false,
|
|
332
|
+
verbose: false
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
[
|
|
336
|
+
env,
|
|
337
|
+
tools[:flutter],
|
|
338
|
+
"run",
|
|
339
|
+
"-d",
|
|
340
|
+
platform,
|
|
341
|
+
"--target",
|
|
342
|
+
"lib/main.server.dart",
|
|
343
|
+
"--dart-define",
|
|
344
|
+
"RUFLET_BACKEND_URL=#{url}"
|
|
345
|
+
]
|
|
346
|
+
rescue StandardError => e
|
|
347
|
+
warn "Project desktop client setup failed: #{e.class}: #{e.message}"
|
|
348
|
+
nil
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
def project_run_requires_managed_client?
|
|
352
|
+
return false unless defined?(Ruflet::CLI::BuildCommand::CLIENT_EXTENSION_MAP)
|
|
353
|
+
|
|
354
|
+
services = Array(project_run_config["services"]).map { |value| normalize_run_extension_key(value) }.compact
|
|
355
|
+
return false if services.empty?
|
|
356
|
+
|
|
357
|
+
extension_keys = Ruflet::CLI::BuildCommand::CLIENT_EXTENSION_MAP.keys
|
|
358
|
+
(services & extension_keys).any?
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def project_run_config
|
|
362
|
+
config_path = ENV["RUFLET_CONFIG"] || (File.file?("ruflet.yaml") ? "ruflet.yaml" : "ruflet.yml")
|
|
363
|
+
return {} unless File.file?(config_path)
|
|
364
|
+
|
|
365
|
+
YAML.safe_load(File.read(config_path), aliases: true) || {}
|
|
366
|
+
rescue StandardError
|
|
367
|
+
{}
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def normalize_run_extension_key(value)
|
|
371
|
+
key = value.to_s.strip.downcase
|
|
372
|
+
return nil if key.empty?
|
|
373
|
+
|
|
374
|
+
key.tr!("-", "_")
|
|
375
|
+
key.gsub!(/\A(flet_)+/, "")
|
|
376
|
+
key.gsub!(/\Aservice_/, "")
|
|
377
|
+
key
|
|
378
|
+
end
|
|
379
|
+
|
|
309
380
|
def detect_desktop_client_command(url)
|
|
310
381
|
root = ENV["RUFLET_CLIENT_DIR"]
|
|
311
382
|
root = File.expand_path("ruflet_client", Dir.pwd) if root.to_s.strip.empty?
|
|
@@ -315,12 +386,15 @@ module Ruflet
|
|
|
315
386
|
|
|
316
387
|
host_os = RbConfig::CONFIG["host_os"]
|
|
317
388
|
if host_os.match?(/darwin/i)
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
389
|
+
app_path = [
|
|
390
|
+
File.join(root, "build", "macos", "Build", "Products", "Release", "ruflet_client.app"),
|
|
391
|
+
File.join(root, "build", "macos", "Build", "Products", "Debug", "ruflet_client.app"),
|
|
392
|
+
File.join(root, "desktop", "ruflet_client.app")
|
|
393
|
+
].find do |candidate|
|
|
394
|
+
bin = File.join(candidate, "Contents", "MacOS", "ruflet_client")
|
|
395
|
+
File.file?(bin) && File.executable?(bin) && ensure_macos_file_picker_entitlement(candidate)
|
|
396
|
+
end
|
|
397
|
+
return [File.join(app_path, "Contents", "MacOS", "ruflet_client"), url] if app_path
|
|
324
398
|
elsif host_os.match?(/mswin|mingw|cygwin/i)
|
|
325
399
|
exe = File.join(root, "build", "windows", "x64", "runner", "Release", "ruflet_client.exe")
|
|
326
400
|
prebuilt = File.join(root, "desktop", "ruflet_client.exe")
|
|
@@ -436,7 +510,9 @@ module Ruflet
|
|
|
436
510
|
|
|
437
511
|
case platform
|
|
438
512
|
when "macos"
|
|
439
|
-
File.
|
|
513
|
+
app_path = File.join(root, "desktop", "ruflet_client.app")
|
|
514
|
+
bin = File.join(app_path, "Contents", "MacOS", "ruflet_client")
|
|
515
|
+
File.file?(bin) && ensure_macos_file_picker_entitlement(app_path)
|
|
440
516
|
when "linux"
|
|
441
517
|
File.file?(File.join(root, "desktop", "ruflet_client"))
|
|
442
518
|
when "windows"
|
|
@@ -492,6 +568,53 @@ module Ruflet
|
|
|
492
568
|
nil
|
|
493
569
|
end
|
|
494
570
|
|
|
571
|
+
def ensure_macos_file_picker_entitlement(app_path)
|
|
572
|
+
return true if macos_app_has_file_picker_entitlement?(app_path)
|
|
573
|
+
|
|
574
|
+
Dir.mktmpdir("ruflet-entitlements-") do |dir|
|
|
575
|
+
entitlements_path = File.join(dir, "ruflet_file_picker.entitlements")
|
|
576
|
+
File.write(entitlements_path, macos_file_picker_entitlements_plist)
|
|
577
|
+
system(
|
|
578
|
+
"/usr/bin/codesign",
|
|
579
|
+
"--force",
|
|
580
|
+
"--deep",
|
|
581
|
+
"--sign",
|
|
582
|
+
"-",
|
|
583
|
+
"--entitlements",
|
|
584
|
+
entitlements_path,
|
|
585
|
+
app_path,
|
|
586
|
+
out: File::NULL,
|
|
587
|
+
err: File::NULL
|
|
588
|
+
)
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
macos_app_has_file_picker_entitlement?(app_path)
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
def macos_app_has_file_picker_entitlement?(app_path)
|
|
595
|
+
output = IO.popen(["/usr/bin/codesign", "-d", "--entitlements", ":-", app_path], err: [:child, :out], &:read)
|
|
596
|
+
$?.success? && output.include?("com.apple.security.files.user-selected.read-write")
|
|
597
|
+
rescue StandardError
|
|
598
|
+
false
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
def macos_file_picker_entitlements_plist
|
|
602
|
+
<<~PLIST
|
|
603
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
604
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
605
|
+
<plist version="1.0">
|
|
606
|
+
<dict>
|
|
607
|
+
\t<key>com.apple.security.app-sandbox</key>
|
|
608
|
+
\t<true/>
|
|
609
|
+
\t<key>com.apple.security.files.user-selected.read-write</key>
|
|
610
|
+
\t<true/>
|
|
611
|
+
\t<key>com.apple.security.network.client</key>
|
|
612
|
+
\t<true/>
|
|
613
|
+
</dict>
|
|
614
|
+
</plist>
|
|
615
|
+
PLIST
|
|
616
|
+
end
|
|
617
|
+
|
|
495
618
|
def fallback_release_asset(assets, wanted)
|
|
496
619
|
kind = wanted[:kind]
|
|
497
620
|
platform = wanted[:platform]
|
data/lib/ruflet/cli/templates.rb
CHANGED
|
@@ -5,7 +5,7 @@ module Ruflet
|
|
|
5
5
|
MAIN_TEMPLATE = <<~RUBY
|
|
6
6
|
require "ruflet"
|
|
7
7
|
Ruflet.run do |page|
|
|
8
|
-
page.title = "
|
|
8
|
+
page.title = "%<app_title>s"
|
|
9
9
|
count = 0
|
|
10
10
|
count_text = text(count.to_s, style: {size: 40})
|
|
11
11
|
page.add(
|
|
@@ -36,8 +36,9 @@ module Ruflet
|
|
|
36
36
|
GEMFILE_TEMPLATE = <<~GEMFILE
|
|
37
37
|
source "https://rubygems.org"
|
|
38
38
|
|
|
39
|
-
gem "
|
|
40
|
-
gem "
|
|
39
|
+
gem "ruflet", ">= #{Ruflet::VERSION}"
|
|
40
|
+
gem "ruflet_core", ">= #{Ruflet::VERSION}"
|
|
41
|
+
gem "ruflet_server", ">= #{Ruflet::VERSION}"
|
|
41
42
|
GEMFILE
|
|
42
43
|
|
|
43
44
|
README_TEMPLATE = <<~MD
|
|
@@ -61,6 +61,9 @@ module Ruflet
|
|
|
61
61
|
|
|
62
62
|
return check_client_updates(targets, platform: platform) if options[:check]
|
|
63
63
|
|
|
64
|
+
ensure_cached_ruflet_assets_for_update(force: options[:force])
|
|
65
|
+
ensure_flutter!("update", client_dir: nil, auto_install: true) if respond_to?(:ensure_flutter!, true)
|
|
66
|
+
|
|
64
67
|
targets.each do |target|
|
|
65
68
|
root =
|
|
66
69
|
if target == :web
|
|
@@ -83,6 +86,17 @@ module Ruflet
|
|
|
83
86
|
|
|
84
87
|
private
|
|
85
88
|
|
|
89
|
+
def ensure_cached_ruflet_assets_for_update(force: false)
|
|
90
|
+
if Ruflet::CLI.respond_to?(:download_ruflet_assets, true)
|
|
91
|
+
Ruflet::CLI.send(:download_ruflet_assets, force: force)
|
|
92
|
+
return
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
if Ruflet::CLI.respond_to?(:ensure_cached_ruflet_client_template!, true)
|
|
96
|
+
Ruflet::CLI.send(:ensure_cached_ruflet_client_template!, force: force)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
86
100
|
def check_client_updates(targets, platform:)
|
|
87
101
|
root = client_cache_root_for(platform)
|
|
88
102
|
manifest = read_client_manifest(root)
|
data/lib/ruflet/cli.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "optparse"
|
|
4
4
|
|
|
5
|
+
require_relative "version"
|
|
5
6
|
require_relative "cli/templates"
|
|
6
7
|
require_relative "cli/new_command"
|
|
7
8
|
require_relative "cli/flutter_sdk"
|
|
@@ -9,7 +10,6 @@ require_relative "cli/run_command"
|
|
|
9
10
|
require_relative "cli/update_command"
|
|
10
11
|
require_relative "cli/build_command"
|
|
11
12
|
require_relative "cli/extra_command"
|
|
12
|
-
require_relative "version"
|
|
13
13
|
|
|
14
14
|
module Ruflet
|
|
15
15
|
module CLI
|
|
@@ -22,6 +22,7 @@ module Ruflet
|
|
|
22
22
|
|
|
23
23
|
def run(argv = ARGV)
|
|
24
24
|
command = (argv.shift || "help").downcase
|
|
25
|
+
ensure_first_run_assets(command)
|
|
25
26
|
|
|
26
27
|
case command
|
|
27
28
|
when "version", "-v", "--version"
|
|
@@ -84,5 +85,16 @@ module Ruflet
|
|
|
84
85
|
def version_string
|
|
85
86
|
"ruflet #{Ruflet::VERSION}"
|
|
86
87
|
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
def ensure_first_run_assets(command)
|
|
92
|
+
return if %w[version -v --version help -h --help].include?(command)
|
|
93
|
+
return unless respond_to?(:download_ruflet_assets, true)
|
|
94
|
+
|
|
95
|
+
send(:download_ruflet_assets)
|
|
96
|
+
rescue StandardError
|
|
97
|
+
nil
|
|
98
|
+
end
|
|
87
99
|
end
|
|
88
100
|
end
|
data/lib/ruflet/version.rb
CHANGED