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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 877c730a76717b1a9623d285666db0cb3b5a47fb08eafe0e9d3bb49c681c0b88
4
- data.tar.gz: a649878ca4f0a7fc8d752e6ba2e0f3eaa1963213fa96dfee3f4a52d2905e4b9a
3
+ metadata.gz: d273c23251a6550ce0d8fa89003051b98f4b92ade780a048bd0c56c41ca14c81
4
+ data.tar.gz: e646f2912853da8f30a6b49b924c04cfb9a00fd8328f391f8a68bea0e7875fcb
5
5
  SHA512:
6
- metadata.gz: a1a15a38fa5973a84e921d3b57fd54a135d5d4090fdd996294991f0fede09a89e1553037388f7a07a9dace52b9b4e753273d4da2b0940ca6f326344b708b0de2
7
- data.tar.gz: d10353a23bb436679d2a82226f490ad7c2ef1d470b33808ff3d523a94432f871982a1ed998a2c9fc80a9321ed07fbf2ad8ba23633dbe492e2ebc20c1b03fea96
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)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ruflet
4
- VERSION = "0.0.13" unless const_defined?(:VERSION)
4
+ VERSION = "0.0.14" unless const_defined?(:VERSION)
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruflet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.13
4
+ version: 0.0.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - AdamMusa