pindo 5.10.4 → 5.10.8

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.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pindo/base/funlog.rb +12 -1
  3. data/lib/pindo/base/pindocontext.rb +3 -0
  4. data/lib/pindo/command/android/autobuild.rb +62 -9
  5. data/lib/pindo/command/android/build.rb +6 -6
  6. data/lib/pindo/command/android/debug.rb +9 -9
  7. data/lib/pindo/command/deploy/build.rb +5 -2
  8. data/lib/pindo/command/deploy/cert.rb +6 -7
  9. data/lib/pindo/command/deploy/configproj.rb +3 -3
  10. data/lib/pindo/command/deploy/confusecode.rb +1 -1
  11. data/lib/pindo/command/deploy/confuseproj.rb +1 -1
  12. data/lib/pindo/command/deploy/pem.rb +3 -4
  13. data/lib/pindo/command/dev/autobuild.rb +2 -2
  14. data/lib/pindo/command/dev/build.rb +2 -2
  15. data/lib/pindo/command/dev/feishu.rb +1 -1
  16. data/lib/pindo/command/gplay/pullconfig.rb +48 -0
  17. data/lib/pindo/command/gplay.rb +6 -5
  18. data/lib/pindo/command/ios/adhoc.rb +7 -6
  19. data/lib/pindo/command/ios/autobuild.rb +25 -25
  20. data/lib/pindo/command/ios/build.rb +7 -6
  21. data/lib/pindo/command/ios/debug.rb +2 -1
  22. data/lib/pindo/command/ipa/autoresign.rb +2 -2
  23. data/lib/pindo/command/ipa/import.rb +3 -4
  24. data/lib/pindo/command/ipa/output.rb +3 -4
  25. data/lib/pindo/command/jps/upload.rb +6 -5
  26. data/lib/pindo/command/repo/login.rb +1 -1
  27. data/lib/pindo/command/unity/apk.rb +19 -2
  28. data/lib/pindo/command/unity/autobuild.rb +58 -63
  29. data/lib/pindo/command/unity/initpack.rb +1 -1
  30. data/lib/pindo/command/unity/ipa.rb +17 -14
  31. data/lib/pindo/command/unity/pack.rb +1 -1
  32. data/lib/pindo/command/unity/upload.rb +1 -1
  33. data/lib/pindo/command/unity/web.rb +2 -2
  34. data/lib/pindo/command/utils/device.rb +4 -4
  35. data/lib/pindo/command/utils/icon.rb +1 -1
  36. data/lib/pindo/command/utils/renewcert.rb +2 -3
  37. data/lib/pindo/command/utils/renewproj.rb +9 -10
  38. data/lib/pindo/command/web/autobuild.rb +3 -3
  39. data/lib/pindo/command/web/run.rb +1 -1
  40. data/lib/pindo/command.rb +2 -1
  41. data/lib/pindo/module/android/android_build_config_helper.rb +267 -35
  42. data/lib/pindo/module/android/android_build_helper.rb +300 -0
  43. data/lib/pindo/module/android/android_project_helper.rb +279 -0
  44. data/lib/pindo/module/android/gp_compliance_helper.rb +33 -87
  45. data/lib/pindo/module/android/gradle_helper.rb +524 -255
  46. data/lib/pindo/module/android/java_env_helper.rb +633 -0
  47. data/lib/pindo/module/android/keystore_helper.rb +1118 -0
  48. data/lib/pindo/module/appselect.rb +109 -0
  49. data/lib/pindo/module/appstore/appstore_in_app_purchase.rb +1 -1
  50. data/lib/pindo/module/build/{buildhelper.rb → build_helper.rb} +1 -2
  51. data/lib/pindo/module/build/{commonconfuseproj.rb → confuse_xcodeproj.rb} +2 -2
  52. data/lib/pindo/module/cert/cert_helper.rb +245 -0
  53. data/lib/pindo/module/cert/keychain_helper.rb +152 -0
  54. data/lib/pindo/module/cert/pem_helper.rb +67 -0
  55. data/lib/pindo/module/cert/xcodecerthelper.rb +6 -5
  56. data/lib/pindo/module/pgyer/pgyerhelper.rb +5 -5
  57. data/lib/pindo/module/{build/unityhelper.rb → unity/unity_helper.rb} +0 -17
  58. data/lib/pindo/module/xcode/{xcodereshandler.rb → res/xcode_res_handler.rb} +1 -1
  59. data/lib/pindo/module/xcode/{xcodebuildconfig.rb → xcode_build_config.rb} +7 -6
  60. data/lib/pindo/module/xcode/{xcodebuildhelper.rb → xcode_build_helper.rb} +84 -2
  61. data/lib/pindo/module/xcode/{xcodehelper.rb → xcode_project_helper.rb} +4 -2
  62. data/lib/pindo/module/xcode/{xcodereshelper.rb → xcode_res_helper.rb} +12 -9
  63. data/lib/pindo/version.rb +185 -7
  64. data/lib/pindo.rb +14 -9
  65. metadata +24 -23
  66. data/lib/pindo/module/android/apk_helper.rb +0 -138
  67. data/lib/pindo/module/android/base_helper.rb +0 -964
  68. data/lib/pindo/module/android/build_helper.rb +0 -128
  69. data/lib/pindo/module/android/so_helper.rb +0 -18
  70. data/lib/pindo/module/cert/certhelper.rb +0 -246
  71. data/lib/pindo/module/cert/keychainhelper.rb +0 -150
  72. data/lib/pindo/module/cert/pemhelper.rb +0 -65
  73. /data/lib/pindo/module/android/{androidreshelper.rb → android_res_helper.rb} +0 -0
  74. /data/lib/pindo/module/appstore/{iap_tier.json → appstore_iap_tier.json} +0 -0
  75. /data/lib/pindo/module/build/{swarkhelper.rb → swark_helper.rb} +0 -0
  76. /data/lib/pindo/module/build/{versionhelper.rb → version_helper.rb} +0 -0
  77. /data/lib/pindo/module/cert/{provisioninghelper.rb → provisioning_helper.rb} +0 -0
  78. /data/lib/pindo/module/unity/{nugethelper.rb → nuget_helper.rb} +0 -0
  79. /data/lib/pindo/module/xcode/{xcoderesconstant.rb → res/xcode_res_constant.rb} +0 -0
@@ -1,128 +0,0 @@
1
- require 'singleton'
2
- require_relative 'base_helper'
3
- require_relative 'gradle_helper'
4
- require_relative 'so_helper'
5
- require_relative 'apk_helper'
6
- require 'fileutils'
7
-
8
- module Pindo
9
- class AndroidBuildHelper
10
- include BaseAndroidHelper
11
- include GradleHelper
12
- include SoHelper
13
- include ApkHelper
14
- include Singleton
15
-
16
-
17
-
18
- class << self
19
- def share_instance
20
- instance
21
- end
22
- end
23
-
24
- # 该方法已迁移到 Pindo::AndroidBuildConfigHelper.add_test_scheme
25
- # 保留代理方法以兼容旧调用
26
- def add_test_scheme(project_dir:nil, scheme_name:nil)
27
- puts "Warning: add_test_scheme已迁移,请使用Pindo::AndroidBuildConfigHelper.add_test_scheme"
28
- require_relative 'android_build_config_helper'
29
- Pindo::AndroidBuildConfigHelper.add_test_scheme(project_dir: project_dir, scheme_name: scheme_name)
30
- end
31
-
32
- def auto_build_apk(project_dir, debug = false, ignore_sub = false)
33
- # 检查 gradle.properties 中是否设置了 Java Home
34
- gradle_properties_path = File.join(project_dir, "gradle.properties")
35
- if File.exist?(gradle_properties_path)
36
- puts "检查 gradle.properties 中的 Java Home 设置..."
37
- content = File.read(gradle_properties_path)
38
- java_home_match = content.match(/org\.gradle\.java\.home\s*=\s*(.+)/)
39
- if java_home_match && !java_home_match[1].empty?
40
- java_home_path = java_home_match[1].strip
41
- puts "找到 Java Home 路径: #{java_home_path}"
42
- # 设置环境变量
43
- ENV['JAVA_HOME'] = java_home_path
44
- ENV['PATH'] = "#{java_home_path}/bin:#{ENV['PATH']}"
45
- puts "已设置 JAVA_HOME 环境变量为: #{java_home_path}"
46
- puts "Java 版本信息:"
47
- system("java -version")
48
- end
49
- end
50
-
51
- # 检查
52
- if !ignore_sub
53
- sub_android_dir = find_android_subproject(project_dir)
54
- if sub_android_dir
55
- prepare_proj(sub_android_dir)
56
- # 构建 AAB 文件
57
- unless build_so_library(sub_android_dir)
58
- raise RuntimeError, "编译SO库失败:"
59
- end
60
- copy_so_files(sub_android_dir, project_dir)
61
- end
62
- end
63
-
64
- prepare_proj(project_dir)
65
- build_apk(project_dir, debug)
66
- end
67
-
68
- def get_application_id(project_path)
69
- main_module = get_main_module(project_path)
70
- return nil unless main_module
71
-
72
- # 尝试从 build.gradle 获取
73
- gradle_path = File.join(main_module, "build.gradle")
74
- if File.exist?(gradle_path)
75
- content = File.read(gradle_path)
76
- if content =~ /applicationId\s+['"]([^'"]+)['"]/
77
- return $1
78
- end
79
- end
80
-
81
- # 如果 build.gradle 中没有,尝试从 AndroidManifest.xml 获取
82
- manifest_path = File.join(main_module, "src", "main", "AndroidManifest.xml")
83
- if File.exist?(manifest_path)
84
- require 'nokogiri'
85
- doc = Nokogiri::XML(File.read(manifest_path))
86
- package = doc.at_xpath('//manifest/@package')&.value
87
- return package if package
88
- end
89
-
90
- nil
91
- end
92
-
93
- def dsign(project_path, debug)
94
- keystore_config = get_keystore_config(project_path, debug)
95
-
96
- ks = keystore_config[:store_file]
97
- # puts "读取 keystore path = #{ks}"
98
- ks_pass = keystore_config[:store_password]
99
- # puts "读取 keystore pass = #{ks_pass}"
100
- key_alias = keystore_config[:key_alias]
101
- # puts "读取 key alias = #{key_alias}"
102
- key_pass = keystore_config[:key_password]
103
- # puts "读取 key pass = #{key_pass}"
104
- end
105
-
106
- private
107
-
108
- def prepare_proj(project_dir)
109
- raise ArgumentError, "项目目录不能为空" if project_dir.nil?
110
- raise ArgumentError, "项目目录不存在" unless File.directory?(project_dir)
111
-
112
- check_gradle_files(project_dir)
113
- if unity_android_project?(project_dir)
114
- update_build_gradle(project_dir)
115
- update_gradle_version(project_dir)
116
- modify_il2cpp_config(project_dir)
117
- remove_desktop_google_service(project_dir)
118
- end
119
- rescue StandardError => e
120
- raise Informative, "准备项目失败: #{e.message}"
121
- end
122
-
123
- # 相关辅助方法已迁移到 Pindo::AndroidBuildConfigHelper
124
- # find_manifests, find_best_activity, check_scheme_exists等方法已被移除
125
- # 请使用 AndroidBuildConfigHelper 中的相应方法
126
- end
127
- end
128
-
@@ -1,18 +0,0 @@
1
- module Pindo
2
- module SoHelper
3
-
4
- def build_so_library(project_path)
5
- # 编译so库
6
- Dir.chdir(project_path) do
7
- system("./gradlew unityLibrary:BuildIl2CppTask")
8
- end
9
- end
10
-
11
- def copy_so_files(source_path, target_path)
12
- # 复制so文件到正确的目录
13
- src_dir = File.join(source_path, "unityLibrary/src/main/assets")
14
- dst_dir = File.join(target_path, "unityLibrary/src/main/assets")
15
- FileUtils.cp_r(src_dir, dst_dir) if File.directory?(src_dir)
16
- end
17
- end
18
- end
@@ -1,246 +0,0 @@
1
-
2
-
3
- require 'match'
4
- require 'openssl'
5
- require 'pindo/base/aeshelper'
6
- require 'pindo/module/cert/keychainhelper'
7
- require 'pindo/module/cert/provisioninghelper'
8
-
9
-
10
- module Pindo
11
-
12
- module CertHelper
13
- # 密码缓存,避免重复获取相同URL的密码
14
- @@password_cache = {}
15
-
16
- # 获取密码的辅助方法,使用缓存避免重复获取
17
- def self.get_cached_password(cert_url)
18
- unless @@password_cache[cert_url]
19
- puts "\e[33m[DEBUG] 密码缓存中未找到,从Keychain获取: #{cert_url}\e[0m" if ENV['PINDO_DEBUG']
20
- @@password_cache[cert_url] = AESHelper.fetch_password(keychain_name: cert_url)
21
- puts "\e[32m[DEBUG] 密码已缓存: #{cert_url}\e[0m" if ENV['PINDO_DEBUG']
22
- else
23
- puts "\e[32m[DEBUG] 从密码缓存获取: #{cert_url}\e[0m" if ENV['PINDO_DEBUG']
24
- end
25
- @@password_cache[cert_url]
26
- end
27
-
28
- # 清除密码缓存
29
- def self.clear_password_cache
30
- @@password_cache.clear
31
- end
32
-
33
- # 清除特定URL的密码缓存
34
- def self.clear_password_cache_for_url(cert_url)
35
- @@password_cache.delete(cert_url)
36
- end
37
-
38
- def get_cert_info(cer_certificate)
39
- # can receive a certificate path or the file data
40
- begin
41
- if File.exist?(cer_certificate)
42
- cer_certificate = File.binread(cer_certificate)
43
- end
44
- rescue ArgumentError
45
- # cert strings have null bytes; suppressing output
46
- end
47
-
48
- cert = OpenSSL::X509::Certificate.new(cer_certificate)
49
-
50
- # openssl output:
51
- # subject= /UID={User ID}/CN={Certificate Name}/OU={Certificate User}/O={Organisation}/C={Country}
52
- cert_info = cert.subject.to_s.gsub(/\s*subject=\s*/, "").tr("/", "\n")
53
- out_array = cert_info.split("\n")
54
- openssl_keys_to_readable_keys = {
55
- 'UID' => 'User ID',
56
- 'CN' => 'Common Name',
57
- 'OU' => 'Organisation Unit',
58
- 'O' => 'Organisation',
59
- 'C' => 'Country',
60
- 'notBefore' => 'Start Datetime',
61
- 'notAfter' => 'End Datetime'
62
- }
63
-
64
- return out_array.map { |x| x.split(/=+/) if x.include?("=") }
65
- .compact
66
- .map { |k, v| [openssl_keys_to_readable_keys.fetch(k, k), v] }
67
- .push([openssl_keys_to_readable_keys.fetch("notBefore"), cert.not_before])
68
- .push([openssl_keys_to_readable_keys.fetch("notAfter"), cert.not_after])
69
- rescue => ex
70
- raise Informative, "get_cert_info: #{ex}"
71
- return {}
72
- end
73
-
74
- def select_cert_or_key(paths:)
75
- cert_id_path = ENV['MATCH_CERTIFICATE_ID'] ? paths.find { |path| path.include?(ENV['MATCH_CERTIFICATE_ID']) } : nil
76
- cert_id_path || paths.last
77
- end
78
-
79
- def is_cert_valid?(cer_certificate_path)
80
- cert = OpenSSL::X509::Certificate.new(File.binread(cer_certificate_path))
81
- now = Time.now.utc
82
- return (now <=> cert.not_after) == -1
83
- end
84
-
85
- def isMac?
86
- (/darwin/ =~ RUBY_PLATFORM) != nil
87
- end
88
-
89
- def install_certs(cert_url:nil, certs_dir:nil, cert_type:nil, platform_type:nil)
90
-
91
- cert_git_dir = cert_type.downcase
92
- if platform_type.downcase.eql?("macos")
93
- if cert_type.downcase.include?("development")
94
- cert_git_dir = "development"
95
- elsif cert_type.downcase.eql?("appstore")
96
- cert_git_dir = "distribution"
97
- else
98
- cert_git_dir = "developer_id_application"
99
- end
100
- else
101
- if !cert_type.downcase.include?("development")
102
- cert_git_dir = "distribution"
103
- end
104
- end
105
-
106
- certs = Dir[File.join(certs_dir, "certs", cert_git_dir.to_s, "*.cer")]
107
- keys = Dir[File.join(certs_dir, "certs", cert_git_dir.to_s, "*.p12")]
108
-
109
- if certs.count == 0 || keys.count == 0
110
- raise Informative, "No certificates found in #{certs_dir}"
111
- else
112
- output_dir = Dir.mktmpdir
113
-
114
- decrypt_password = CertHelper.get_cached_password(cert_url)
115
- Funlog.instance.fancyinfo_start("正在安装证书...")
116
-
117
- cert_path = AESHelper.decrypt_specific_file(src_file: certs.first, password:decrypt_password, output_dir: output_dir)
118
- if cert_path.nil? || cert_path.empty? || !File.exist?(cert_path)
119
- AESHelper.delete_password(keychain_name:cert_url)
120
- # 清除内存中的密码缓存,避免重复使用错误密码
121
- @@password_cache.delete(cert_url)
122
- raise Informative, "证书解析失败,密码错误!"
123
- end
124
-
125
- key_path = AESHelper.decrypt_specific_file(src_file: keys.first, password:decrypt_password, output_dir: output_dir)
126
- if key_path.nil? || key_path.empty? || !File.exist?(key_path)
127
- AESHelper.delete_password(keychain_name:cert_url)
128
- # 清除内存中的密码缓存,避免重复使用错误密码
129
- @@password_cache.delete(cert_url)
130
- raise Informative, "证书解析失败,密码错误!"
131
- end
132
-
133
- unless is_cert_valid?(cert_path)
134
- raise Informative, "证书已经过期,请重新生产新证书!"
135
- end
136
-
137
-
138
- if isMac?
139
-
140
- keychain_name = "login.keychain"
141
- if FastlaneCore::CertChecker.installed?(cert_path, in_keychain: nil)
142
- Funlog.instance.fancyinfo_success("证书#{File.basename(cert_path)}已安装,无需重复安装!")
143
- else
144
-
145
- cert_password = Pindoconfig.instance.cert_key_password
146
- keychain = 'login.keychain'
147
- keychain_path = FastlaneCore::Helper.keychain_path(keychain)
148
-
149
- KeychainHelper.import_file(cert_path, keychain_path, keychain_password: cert_password, certificate_password:'' )
150
- KeychainHelper.import_file(key_path, keychain_path, keychain_password: cert_password, certificate_password: '')
151
-
152
- Funlog.instance.fancyinfo_success("证书'#{File.basename(cert_path)}'安装完成!")
153
-
154
- end
155
- else
156
- Funlog.instance.fancyinfo_error("非Mac电脑不支持安装证书!")
157
- end
158
-
159
- end
160
-
161
- def install_provisionfiles(cert_url:nil, certs_dir:nil, bundle_id_map:nil, cert_type:nil, platform_type:nil)
162
-
163
- cert_sub_dir = cert_type.downcase
164
- provision_start_name = "Development"
165
- provision_extension_name = ".mobileprovision"
166
-
167
- if platform_type.downcase.include?("macos")
168
- provision_extension_name = ".provisionprofile"
169
-
170
- if cert_type.downcase.include?("development")
171
- provision_start_name = "Development"
172
- cert_sub_dir = cert_type.downcase
173
- elsif cert_type.downcase.eql?("appstore")
174
- provision_start_name = "AppStore"
175
- cert_sub_dir = "appstore"
176
- else
177
- provision_start_name = "Direct"
178
- cert_sub_dir = "developer_id"
179
- end
180
- else
181
- provision_extension_name = ".mobileprovision"
182
- if cert_type.downcase.include?("development")
183
- provision_start_name = "Development"
184
- cert_sub_dir = cert_type.downcase
185
- elsif cert_type.downcase.include?("adhoc")
186
- provision_start_name = "Adhoc"
187
- cert_sub_dir = "adhoc"
188
- else
189
- provision_start_name = "AppStore"
190
- cert_sub_dir = "appstore"
191
- end
192
- end
193
-
194
-
195
- Funlog.instance.fancyinfo_start("正在安装#{provision_start_name} #{platform_type} Provisioning Profiles...")
196
-
197
- un_exist_files = []
198
- provisioning_info_array = []
199
-
200
- # 在循环外获取密码,避免重复添加到Keychain
201
- decrypt_password = CertHelper.get_cached_password(cert_url)
202
-
203
- bundle_id_map.each do |type, bundle_id_temp|
204
- profile_filename = File.join(certs_dir, "profiles", cert_sub_dir, [provision_start_name.to_s, bundle_id_temp].join('_') + provision_extension_name)
205
- unless File.exist?(profile_filename)
206
- un_exist_files << profile_filename
207
- next
208
- end
209
- # puts "正在安装 #{bundle_id_temp}..."
210
- output_dir = Dir.mktmpdir
211
- file_decrypt = AESHelper.decrypt_specific_file(src_file: profile_filename, password:decrypt_password, output_dir: output_dir)
212
- destpath = Provisioninghelper.install(file_decrypt)
213
- parsed_data = Provisioninghelper.parse(destpath)
214
-
215
- provisioning_info = {}
216
- provisioning_info['type'] = type
217
- provisioning_info['bundle_id'] = bundle_id_temp
218
- provisioning_info['profile_name'] = parsed_data['Name']
219
- provisioning_info['profile_path'] = destpath
220
-
221
-
222
- cert_info = get_cert_info(parsed_data["DeveloperCertificates"].first.string).to_h
223
- provisioning_info['signing_identity'] = cert_info["Common Name"]
224
- provisioning_info['team_id'] = parsed_data["TeamIdentifier"].first
225
-
226
- # puts JSON.pretty_generate(provisioning_info)
227
- provisioning_info_array << provisioning_info
228
- end
229
-
230
- Funlog.instance.fancyinfo_success("#{provision_start_name} #{platform_type} Provisioning Profiles文件安装完成!")
231
-
232
- if un_exist_files.size > 0
233
- Funlog.instance.fancyinfo_error("证书 #{provision_start_name} #{platform_type} Provisioning Profiles文件不存在!")
234
- raise Informative, "The following profiles do not exist: #{un_exist_files.join(', ')}"
235
- end
236
-
237
- return provisioning_info_array
238
-
239
- end
240
-
241
-
242
- end
243
-
244
-
245
- end
246
- end
@@ -1,150 +0,0 @@
1
-
2
- require 'open3'
3
- require 'security'
4
-
5
- module Pindo
6
-
7
- module KeychainHelper
8
-
9
- def self.import_file(path, keychain_path, keychain_password: nil, certificate_password: "", skip_set_partition_list: false, output: false)
10
- # puts path
11
-
12
- # puts "Could not find file '#{path}'" unless File.exist?(path)
13
-
14
- password_part = " -P #{certificate_password.shellescape}"
15
-
16
- command = "security import #{path.shellescape} -k '#{keychain_path.shellescape}'"
17
- command << password_part
18
- command << " -T /usr/bin/codesign" # to not be asked for permission when running a tool like `gym` (before Sierra)
19
- command << " -T /usr/bin/security"
20
- command << " -T /usr/bin/productbuild" # to not be asked for permission when using an installer cert for macOS
21
- command << " -T /usr/bin/productsign" # to not be asked for permission when using an installer cert for macOS
22
- command << " 1> /dev/null" unless output
23
-
24
- # puts command
25
- # sensitive_command = command.gsub(password_part, " -P ********")
26
-
27
- # UI.command(sensitive_command) if output
28
- Open3.popen3(command) do |stdin, stdout, stderr, thrd|
29
- # UI.command_output(stdout.read.to_s) if output
30
- # puts "123"
31
- # Set partition list only if success since it can be a time consuming process if a lot of keys are installed
32
- if thrd.value.success? && !skip_set_partition_list
33
- # puts "安装成功!"
34
- keychain_password ||= resolve_keychain_password(keychain_path)
35
- set_partition_list(path, keychain_path, keychain_password: keychain_password, output: output)
36
- else
37
- # Output verbose if file is already installed since not an error otherwise we will show the whole error
38
- err = stderr.read.to_s.strip
39
-
40
- if err.include?("SecKeychainItemImport") && err.include?("The specified item already exists in the keychain")
41
- # UI.verbose("'}' is already installed on this machine")
42
- # puts "证书key已经存在,安装完成!"
43
- else
44
- # puts err
45
- # raise Informative, err
46
- end
47
- end
48
- end
49
- end
50
-
51
- def self.help_backticks(command, print: true)
52
- # UI.command(command) if print
53
- result = `#{command}`
54
- # UI.command_output(result) if print
55
- return result
56
- end
57
-
58
- def self.set_partition_list(path, keychain_path, keychain_password: nil, output: false)
59
- # When security supports partition lists, also add the partition IDs
60
- # See https://openradar.appspot.com/28524119
61
- if help_backticks('security -h | grep set-key-partition-list', print: false).length > 0
62
- password_part = " -k #{keychain_password.to_s.shellescape}"
63
-
64
- command = "security set-key-partition-list"
65
- command << " -S apple-tool:,apple:,codesign:"
66
- command << " -s" # This is a needed in Catalina to prevent "security: SecKeychainItemCopyAccess: A missing value was detected."
67
- command << password_part
68
- command << " #{keychain_path.shellescape}"
69
- command << " 1> /dev/null" # always disable stdout. This can be very verbose, and leak potentially sensitive info
70
-
71
- # Showing loading indicator as this can take some time if a lot of keys installed
72
- # Helper.show_loading_indicator("Setting key partition list... (this can take a minute if there are a lot of keys installed)")
73
-
74
- # Strip keychain password from command output
75
- # sensitive_command = command.gsub(password_part, " -k ********")
76
- # puts command
77
- # UI.command(sensitive_command) if output
78
- Open3.popen3(command) do |stdin, stdout, stderr, thrd|
79
- unless thrd.value.success?
80
- err = stderr.read.to_s.strip
81
-
82
- # Inform user when no/wrong password was used as its needed to prevent UI permission popup from Xcode when signing
83
- if err.include?("SecKeychainItemSetAccessWithPassword")
84
- keychain_name = File.basename(keychain_path, ".*")
85
- # puts "password #{keychain_password}"
86
- # puts "keychain #{keychain_name}"
87
- # puts "security #{server_name(keychain_name)}"
88
- Security::InternetPassword.delete(server: server_name(keychain_name))
89
-
90
- # UI.important("")
91
- # UI.important("Could not configure imported keychain item (certificate) to prevent UI permission popup when code signing\n" \
92
- # "Check if you supplied the correct `keychain_password` for keychain: `#{keychain_path}`\n" \
93
- # "#{err}")
94
- # UI.important("")
95
- # UI.important("Please look at the following docs to see how to set a keychain password:")
96
- # UI.important(" - https://docs.fastlane.tools/actions/sync_code_signing")
97
- # UI.important(" - https://docs.fastlane.tools/actions/get_certificates")
98
- # puts "证书密码错误!"
99
- else
100
- puts err
101
- end
102
- end
103
- end
104
-
105
- # Hiding after Open3 finishes
106
- # Helper.hide_loading_indicator
107
-
108
- end
109
- end
110
-
111
- # https://github.com/fastlane/fastlane/issues/14196
112
- # Keychain password is needed to set the partition list to
113
- # prevent Xcode from prompting dialog for keychain password when signing
114
- # 1. Uses keychain password from login keychain if found
115
- # 2. Prompts user for keychain password and stores it in login keychain for user later
116
- def self.resolve_keychain_password(keychain_path)
117
- keychain_name = File.basename(keychain_path, ".*")
118
- server = server_name(keychain_name)
119
-
120
- # Attempt to find password in keychain for keychain
121
- item = Security::InternetPassword.find(server: server)
122
- if item
123
- keychain_password = item.password
124
- # UI.important("Using keychain password from keychain item #{server} in #{keychain_path}")
125
- end
126
-
127
- if keychain_password.nil?
128
- # if UI.interactive?
129
- # UI.important("Enter the password for #{keychain_path}")
130
- # UI.important("This passphrase will be stored in your local keychain with the name #{server} and used in future runs")
131
- # UI.important("This prompt can be avoided by specifying the 'keychain_password' option or 'MATCH_KEYCHAIN_PASSWORD' environment variable")
132
- keychain_password = FastlaneCore::Helper.ask_password(message: "Password for #{keychain_name} keychain: ", confirm: true, confirmation_message: "Type password for #{keychain_name} keychain again: ")
133
- Security::InternetPassword.add(server, "", keychain_password)
134
- # else
135
- # UI.important("Keychain password for #{keychain_path} was not specified and not found in your keychain. Specify the 'keychain_password' option to prevent the UI permission popup when code signing")
136
- # keychain_password = ""
137
- # end
138
- end
139
-
140
- return keychain_password
141
- end
142
-
143
- # server name used for accessing the macOS keychain
144
- def self.server_name(keychain_name)
145
- ["pindo", "keychain", keychain_name].join("_")
146
- end
147
-
148
- private_class_method :server_name
149
- end
150
- end
@@ -1,65 +0,0 @@
1
-
2
- module Pindo
3
-
4
- module PemHelper
5
-
6
- def create_certificate(bundle_id:nil, type:"prod", output_path:"")
7
-
8
- if bundle_id.empty? || output_path.empty?
9
- UI.user_error!("bundle id is nil or output_path is nil.")
10
- end
11
-
12
- puts "Creating a new push certificate for app '#{bundle_id}'. "
13
-
14
- csr, pkey = Spaceship::Portal.certificate.create_certificate_signing_request
15
-
16
- cert = nil
17
- begin
18
- if type == "dev"
19
- cert = Spaceship::Portal.certificate.development_push.create!(csr: csr, bundle_id: bundle_id)
20
- else
21
- cert = Spaceship::Portal.certificate.production_push.create!(csr: csr, bundle_id: bundle_id)
22
- end
23
- rescue => ex
24
- if ex.to_s.include?("You already have a current")
25
- # That's the most common failure probably
26
- raise Informative, "You already have 2 active push profiles for this application/environment. You'll need to revoke an old certificate to make room for a new one"
27
- else
28
- raise ex
29
- end
30
- end
31
-
32
-
33
- x509_certificate = cert.download
34
- certificate_type = (type == "dev" ? 'development' : 'production')
35
-
36
- base_base = bundle_id.gsub('.', '_')
37
- puts base_base
38
- filename_base = base_base + '_' + type
39
-
40
-
41
- private_key_path = File.join(output_path, "#{filename_base}.pkey")
42
- File.write(private_key_path, pkey.to_pem)
43
- puts "key: #{private_key_path}"
44
-
45
-
46
- p12_cert_path = File.join(output_path, "#{filename_base}.p12")
47
- p12 = OpenSSL::PKCS12.create('goodcert1', certificate_type, pkey, x509_certificate)
48
- File.write(p12_cert_path, p12.to_der)
49
- puts "p12 : #{p12_cert_path}"
50
-
51
-
52
- x509_cert_path = File.join(output_path, "#{filename_base}.pem")
53
- File.write(x509_cert_path, x509_certificate.to_pem + pkey.to_pem)
54
- puts "pem : #{x509_cert_path}"
55
-
56
- return x509_cert_path
57
- end
58
-
59
- def login(apple_id:nil)
60
- puts apple_id
61
- Spaceship::Portal.login(apple_id.to_s)
62
- Spaceship::Portal.select_team
63
- end
64
- end
65
- end