ruflet 0.0.13 → 0.0.14
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 +149 -0
- data/lib/ruflet/cli/flutter_sdk.rb +4 -0
- data/lib/ruflet/cli/run_command.rb +27 -0
- 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: d273c23251a6550ce0d8fa89003051b98f4b92ade780a048bd0c56c41ca14c81
|
|
4
|
+
data.tar.gz: e646f2912853da8f30a6b49b924c04cfb9a00fd8328f391f8a68bea0e7875fcb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d6084b2b22d1775ff0849ade08bbc0ac4cb67198816add2f9d02a4c6461fef156936b2e45cd69cde3c4d7f0cc3510e9c942c245d7c2d032def1a39d4d3620c24
|
|
7
|
+
data.tar.gz: 7e73f5b93c8af40c22f7ff5c154bcf4bc40f87595ef7993144ebd7b7eccc7531fab2b72b7e0f19aa002ec2983a4b5e0d0655a5b8946c49b1a98d90185b8831c0
|
|
@@ -31,6 +31,41 @@ module Ruflet
|
|
|
31
31
|
"webview" => { package: "flet_webview", alias: "ruflet_webview" }
|
|
32
32
|
}.freeze
|
|
33
33
|
|
|
34
|
+
SERVICE_NATIVE_REQUIREMENTS = {
|
|
35
|
+
"audio_recorder" => {
|
|
36
|
+
android_permissions: ["android.permission.RECORD_AUDIO"],
|
|
37
|
+
ios_info: {
|
|
38
|
+
"NSMicrophoneUsageDescription" => "Microphone access is required for audio recording."
|
|
39
|
+
},
|
|
40
|
+
macos_info: {
|
|
41
|
+
"NSMicrophoneUsageDescription" => "Microphone access is required for audio recording."
|
|
42
|
+
},
|
|
43
|
+
macos_entitlements: {
|
|
44
|
+
"com.apple.security.device.audio-input" => true
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"barometer" => {
|
|
48
|
+
ios_info: {
|
|
49
|
+
"NSMotionUsageDescription" => "Motion access is required for barometer readings."
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"geolocator" => {
|
|
53
|
+
android_permissions: [
|
|
54
|
+
"android.permission.ACCESS_FINE_LOCATION",
|
|
55
|
+
"android.permission.ACCESS_COARSE_LOCATION"
|
|
56
|
+
],
|
|
57
|
+
ios_info: {
|
|
58
|
+
"NSLocationWhenInUseUsageDescription" => "Location access is required for location-aware experiences."
|
|
59
|
+
},
|
|
60
|
+
macos_info: {
|
|
61
|
+
"NSLocationUsageDescription" => "Location access is required for location-aware experiences."
|
|
62
|
+
},
|
|
63
|
+
macos_entitlements: {
|
|
64
|
+
"com.apple.security.personal-information.location" => true
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}.freeze
|
|
68
|
+
|
|
34
69
|
def command_build(args)
|
|
35
70
|
self_contained = args.delete("--self")
|
|
36
71
|
verbose = args.delete("--verbose") || args.delete("-v")
|
|
@@ -1003,6 +1038,120 @@ module Ruflet
|
|
|
1003
1038
|
sync_client_main_extensions(entrypoint, extension_aliases) if File.file?(entrypoint)
|
|
1004
1039
|
prune_client_main(entrypoint, extension_aliases) if File.file?(entrypoint)
|
|
1005
1040
|
end
|
|
1041
|
+
apply_service_native_requirements(client_dir, extension_keys)
|
|
1042
|
+
end
|
|
1043
|
+
|
|
1044
|
+
def apply_service_native_requirements(client_dir, extension_keys)
|
|
1045
|
+
stale_keys = SERVICE_NATIVE_REQUIREMENTS.keys - extension_keys
|
|
1046
|
+
remove_service_native_requirements(client_dir, stale_keys)
|
|
1047
|
+
|
|
1048
|
+
requirements = merge_service_native_requirements(extension_keys)
|
|
1049
|
+
return if requirements.empty?
|
|
1050
|
+
|
|
1051
|
+
android_manifest = File.join(client_dir, "android", "app", "src", "main", "AndroidManifest.xml")
|
|
1052
|
+
Array(requirements[:android_permissions]).each do |permission|
|
|
1053
|
+
ensure_android_permission(android_manifest, permission)
|
|
1054
|
+
end
|
|
1055
|
+
|
|
1056
|
+
ios_info = File.join(client_dir, "ios", "Runner", "Info.plist")
|
|
1057
|
+
Hash(requirements[:ios_info]).each do |key, value|
|
|
1058
|
+
ensure_plist_string(ios_info, key, value)
|
|
1059
|
+
end
|
|
1060
|
+
|
|
1061
|
+
macos_info = File.join(client_dir, "macos", "Runner", "Info.plist")
|
|
1062
|
+
Hash(requirements[:macos_info]).each do |key, value|
|
|
1063
|
+
ensure_plist_string(macos_info, key, value)
|
|
1064
|
+
end
|
|
1065
|
+
|
|
1066
|
+
%w[DebugProfile Release].each do |name|
|
|
1067
|
+
entitlements_path = File.join(client_dir, "macos", "Runner", "#{name}.entitlements")
|
|
1068
|
+
Hash(requirements[:macos_entitlements]).each do |key, value|
|
|
1069
|
+
ensure_plist_boolean(entitlements_path, key, value)
|
|
1070
|
+
end
|
|
1071
|
+
end
|
|
1072
|
+
end
|
|
1073
|
+
|
|
1074
|
+
def remove_service_native_requirements(client_dir, extension_keys)
|
|
1075
|
+
requirements = merge_service_native_requirements(extension_keys)
|
|
1076
|
+
return if requirements.empty?
|
|
1077
|
+
|
|
1078
|
+
android_manifest = File.join(client_dir, "android", "app", "src", "main", "AndroidManifest.xml")
|
|
1079
|
+
Array(requirements[:android_permissions]).each do |permission|
|
|
1080
|
+
remove_android_permission(android_manifest, permission)
|
|
1081
|
+
end
|
|
1082
|
+
|
|
1083
|
+
[File.join(client_dir, "ios", "Runner", "Info.plist"), File.join(client_dir, "macos", "Runner", "Info.plist")].each do |path|
|
|
1084
|
+
(Hash(requirements[:ios_info]).keys + Hash(requirements[:macos_info]).keys).uniq.each do |key|
|
|
1085
|
+
remove_plist_entry(path, key)
|
|
1086
|
+
end
|
|
1087
|
+
end
|
|
1088
|
+
|
|
1089
|
+
%w[DebugProfile Release].each do |name|
|
|
1090
|
+
entitlements_path = File.join(client_dir, "macos", "Runner", "#{name}.entitlements")
|
|
1091
|
+
Hash(requirements[:macos_entitlements]).each_key do |key|
|
|
1092
|
+
remove_plist_entry(entitlements_path, key)
|
|
1093
|
+
end
|
|
1094
|
+
end
|
|
1095
|
+
end
|
|
1096
|
+
|
|
1097
|
+
def merge_service_native_requirements(extension_keys)
|
|
1098
|
+
extension_keys.each_with_object({}) do |key, memo|
|
|
1099
|
+
requirements = SERVICE_NATIVE_REQUIREMENTS[key]
|
|
1100
|
+
next unless requirements
|
|
1101
|
+
|
|
1102
|
+
memo[:android_permissions] ||= []
|
|
1103
|
+
memo[:android_permissions] |= Array(requirements[:android_permissions])
|
|
1104
|
+
%i[ios_info macos_info macos_entitlements].each do |section|
|
|
1105
|
+
memo[section] ||= {}
|
|
1106
|
+
memo[section].merge!(requirements[section] || {})
|
|
1107
|
+
end
|
|
1108
|
+
end
|
|
1109
|
+
end
|
|
1110
|
+
|
|
1111
|
+
def ensure_android_permission(path, permission)
|
|
1112
|
+
return unless File.file?(path)
|
|
1113
|
+
|
|
1114
|
+
content = File.read(path)
|
|
1115
|
+
return if content.include?(permission)
|
|
1116
|
+
|
|
1117
|
+
permission_line = %( <uses-permission android:name="#{xml_escape(permission)}"/>\n)
|
|
1118
|
+
updated = content.sub(/(<manifest\b[^>]*>\s*)/m) { "#{Regexp.last_match(1)}#{permission_line}" }
|
|
1119
|
+
File.write(path, updated == content ? "#{permission_line}#{content}" : updated)
|
|
1120
|
+
end
|
|
1121
|
+
|
|
1122
|
+
def remove_android_permission(path, permission)
|
|
1123
|
+
return unless File.file?(path)
|
|
1124
|
+
|
|
1125
|
+
content = File.read(path)
|
|
1126
|
+
updated = content.gsub(%r{^\s*<uses-permission\s+android:name="#{Regexp.escape(permission)}"\s*/>\s*\n?}, "")
|
|
1127
|
+
File.write(path, updated) unless updated == content
|
|
1128
|
+
end
|
|
1129
|
+
|
|
1130
|
+
def ensure_plist_string(path, key, value)
|
|
1131
|
+
ensure_plist_entry(path, key, "<string>#{xml_escape(value)}</string>")
|
|
1132
|
+
end
|
|
1133
|
+
|
|
1134
|
+
def ensure_plist_boolean(path, key, value)
|
|
1135
|
+
ensure_plist_entry(path, key, value ? "<true/>" : "<false/>")
|
|
1136
|
+
end
|
|
1137
|
+
|
|
1138
|
+
def ensure_plist_entry(path, key, value_xml)
|
|
1139
|
+
return unless File.file?(path)
|
|
1140
|
+
|
|
1141
|
+
content = File.read(path)
|
|
1142
|
+
return if content.include?("<key>#{key}</key>")
|
|
1143
|
+
|
|
1144
|
+
entry = "\t<key>#{key}</key>\n\t#{value_xml}\n"
|
|
1145
|
+
updated = content.sub(%r{</dict>}, "#{entry}</dict>")
|
|
1146
|
+
File.write(path, updated == content ? "#{content}\n#{entry}" : updated)
|
|
1147
|
+
end
|
|
1148
|
+
|
|
1149
|
+
def remove_plist_entry(path, key)
|
|
1150
|
+
return unless File.file?(path)
|
|
1151
|
+
|
|
1152
|
+
content = File.read(path)
|
|
1153
|
+
updated = content.gsub(%r{\s*<key>#{Regexp.escape(key)}</key>\s*<(?:string>.*?</string|true/|false/)>\s*}m, "\n")
|
|
1154
|
+
File.write(path, updated) unless updated == content
|
|
1006
1155
|
end
|
|
1007
1156
|
|
|
1008
1157
|
def clear_flutter_build_state(client_dir, verbose: false)
|
|
@@ -289,6 +289,8 @@ module Ruflet
|
|
|
289
289
|
return JSON.parse(response.body) if response.is_a?(Net::HTTPSuccess)
|
|
290
290
|
|
|
291
291
|
nil
|
|
292
|
+
rescue OpenSSL::SSL::SSLError
|
|
293
|
+
JSON.parse(curl_get(url, headers: ["User-Agent: ruflet-cli"]))
|
|
292
294
|
end
|
|
293
295
|
|
|
294
296
|
def pick_release(manifest, version: nil, revision: nil, channel: nil)
|
|
@@ -380,6 +382,8 @@ module Ruflet
|
|
|
380
382
|
end
|
|
381
383
|
end
|
|
382
384
|
end
|
|
385
|
+
rescue OpenSSL::SSL::SSLError
|
|
386
|
+
curl_download(url, destination)
|
|
383
387
|
end
|
|
384
388
|
|
|
385
389
|
def extract_archive(archive, destination)
|
|
@@ -653,6 +653,8 @@ module Ruflet
|
|
|
653
653
|
return JSON.parse(response.body) if response.is_a?(Net::HTTPSuccess)
|
|
654
654
|
|
|
655
655
|
raise "GitHub API failed (#{response.code})"
|
|
656
|
+
rescue OpenSSL::SSL::SSLError
|
|
657
|
+
JSON.parse(curl_get(url, headers: ["Accept: application/vnd.github+json", "User-Agent: ruflet-cli"]))
|
|
656
658
|
end
|
|
657
659
|
|
|
658
660
|
def download_file(url, destination, limit: 5)
|
|
@@ -674,6 +676,31 @@ module Ruflet
|
|
|
674
676
|
end
|
|
675
677
|
end
|
|
676
678
|
end
|
|
679
|
+
rescue OpenSSL::SSL::SSLError
|
|
680
|
+
curl_download(url, destination)
|
|
681
|
+
end
|
|
682
|
+
|
|
683
|
+
def curl_get(url, headers: [])
|
|
684
|
+
args = ["curl", "-fsSL"]
|
|
685
|
+
headers.each do |header|
|
|
686
|
+
args += ["-H", header]
|
|
687
|
+
end
|
|
688
|
+
args << url
|
|
689
|
+
output = IO.popen(args, err: File::NULL, &:read)
|
|
690
|
+
return output if $?.success?
|
|
691
|
+
|
|
692
|
+
raise "curl failed while requesting #{url}"
|
|
693
|
+
rescue Errno::ENOENT
|
|
694
|
+
raise "Ruby SSL verification failed and curl is not available"
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
def curl_download(url, destination)
|
|
698
|
+
ok = system("curl", "-fL", "--retry", "2", "--connect-timeout", "20", "-o", destination, url, out: File::NULL, err: File::NULL)
|
|
699
|
+
return destination if ok
|
|
700
|
+
|
|
701
|
+
raise "curl failed while downloading #{url}"
|
|
702
|
+
rescue Errno::ENOENT
|
|
703
|
+
raise "Ruby SSL verification failed and curl is not available"
|
|
677
704
|
end
|
|
678
705
|
|
|
679
706
|
def extract_archive(archive, destination)
|
data/lib/ruflet/version.rb
CHANGED