pindo 4.6.9 → 4.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pindo/base/aeshelper.rb +75 -0
  3. data/lib/pindo/base/funlog.rb +89 -0
  4. data/lib/pindo/base/githelper.rb +35 -18
  5. data/lib/pindo/base/plaininformative.rb +3 -0
  6. data/lib/pindo/base/standarderror.rb +1 -0
  7. data/lib/pindo/base/xcodeconst.rb +251 -0
  8. data/lib/pindo/client/applovinclient.rb +6 -3
  9. data/lib/pindo/client/aws3sclient.rb +33 -46
  10. data/lib/pindo/client/bossclient.rb +1 -1
  11. data/lib/pindo/client/bossconfigclient.rb +3 -3
  12. data/lib/pindo/client/pgyerclient.rb +159 -100
  13. data/lib/pindo/command/appstore/iap.rb +43 -0
  14. data/lib/pindo/command/appstore/itcapp.rb +41 -0
  15. data/lib/pindo/command/appstore/metadata.rb +43 -0
  16. data/lib/pindo/command/appstore/screenshots.rb +43 -0
  17. data/lib/pindo/command/appstore/upload.rb +40 -0
  18. data/lib/pindo/command/appstore.rb +17 -0
  19. data/lib/pindo/command/deploy/build.rb +109 -0
  20. data/lib/pindo/{deploy → command/deploy}/bundleid.rb +1 -1
  21. data/lib/pindo/command/deploy/cert.rb +183 -0
  22. data/lib/pindo/command/deploy/configproj.rb +105 -0
  23. data/lib/pindo/{deploy → command/deploy}/getitcinfo.rb +1 -1
  24. data/lib/pindo/{deploy → command/deploy}/iap.rb +30 -9
  25. data/lib/pindo/{deploy → command/deploy}/itcapp.rb +0 -1
  26. data/lib/pindo/{deploy → command/deploy}/itcinfo.rb +2 -3
  27. data/lib/pindo/{deploy → command/deploy}/pem.rb +3 -2
  28. data/lib/pindo/{deploy → command/deploy}/resign.rb +14 -63
  29. data/lib/pindo/command/deploy.rb +44 -0
  30. data/lib/pindo/{dev → command/dev}/autobuild.rb +28 -76
  31. data/lib/pindo/{dev → command/dev}/autoresign.rb +21 -64
  32. data/lib/pindo/command/dev/build.rb +94 -0
  33. data/lib/pindo/{dev → command/dev}/createbuild.rb +0 -2
  34. data/lib/pindo/{dev → command/dev}/debug.rb +6 -2
  35. data/lib/pindo/command/dev/pgyercert.rb +75 -0
  36. data/lib/pindo/command/dev.rb +26 -0
  37. data/lib/pindo/command/env.rb +17 -0
  38. data/lib/pindo/{ipa → command/ipa}/autoresign.rb +22 -70
  39. data/lib/pindo/{ipa → command/ipa}/import.rb +48 -103
  40. data/lib/pindo/{ipa → command/ipa}/output.rb +43 -135
  41. data/lib/pindo/command/ipa.rb +16 -0
  42. data/lib/pindo/{lib → command/lib}/update.rb +23 -14
  43. data/lib/pindo/command/lib.rb +16 -0
  44. data/lib/pindo/{pgyer → command/pgyer}/apptest.rb +7 -29
  45. data/lib/pindo/{pgyer → command/pgyer}/comment.rb +7 -30
  46. data/lib/pindo/{pgyer → command/pgyer}/download.rb +35 -30
  47. data/lib/pindo/{pgyer → command/pgyer}/login.rb +3 -4
  48. data/lib/pindo/command/pgyer/resign.rb +111 -0
  49. data/lib/pindo/command/pgyer/upload.rb +123 -0
  50. data/lib/pindo/command/pgyer.rb +18 -0
  51. data/lib/pindo/{repo.rb → command/repo.rb} +4 -4
  52. data/lib/pindo/{utils → command/utils}/applovin.rb +43 -33
  53. data/lib/pindo/{utils → command/utils}/boss.rb +3 -3
  54. data/lib/pindo/command/utils/icon.rb +81 -0
  55. data/lib/pindo/{utils → command/utils}/renewproj.rb +1 -0
  56. data/lib/pindo/command/utils.rb +26 -0
  57. data/lib/pindo/command.rb +23 -26
  58. data/lib/pindo/config/pindoconfig.rb +27 -0
  59. data/lib/pindo/module/appselect.rb +9 -8
  60. data/lib/pindo/module/build/swarkhelper.rb +95 -0
  61. data/lib/pindo/module/cert/certhelper.rb +187 -0
  62. data/lib/pindo/module/cert/keychainhelper.rb +150 -0
  63. data/lib/pindo/module/{pemcreate.rb → cert/pemhelper.rb} +3 -1
  64. data/lib/pindo/module/cert/provisioninghelper.rb +137 -0
  65. data/lib/pindo/module/cert/xcodecerthelper.rb +326 -0
  66. data/lib/pindo/module/{pgyerhelper.rb → pgyer/pgyerhelper.rb} +246 -36
  67. data/lib/pindo/module/xcode/xcodeappconfig.rb +188 -0
  68. data/lib/pindo/module/xcode/xcodebuildconfig.rb +12 -0
  69. data/lib/pindo/module/xcode/xcodebuildhelper.rb +312 -0
  70. data/lib/pindo/module/xcode/xcoderesconstant.rb +248 -0
  71. data/lib/pindo/module/xcode/xcodereshandler.rb +198 -0
  72. data/lib/pindo/module/xcode/xcodereshelper.rb +119 -0
  73. data/lib/pindo/options/appconfigoptions.rb +1 -0
  74. data/lib/pindo/options/deployoptions.rb +38 -42
  75. data/lib/pindo/version.rb +1 -1
  76. metadata +110 -97
  77. data/lib/pindo/deploy/Fastfile +0 -233
  78. data/lib/pindo/deploy/build.rb +0 -167
  79. data/lib/pindo/deploy/cert.rb +0 -508
  80. data/lib/pindo/deploy/configproj.rb +0 -89
  81. data/lib/pindo/deploy.rb +0 -44
  82. data/lib/pindo/dev.rb +0 -23
  83. data/lib/pindo/env/flutter.rb +0 -59
  84. data/lib/pindo/env/flutter.sh +0 -116
  85. data/lib/pindo/env.rb +0 -17
  86. data/lib/pindo/ipa.rb +0 -22
  87. data/lib/pindo/lib.rb +0 -18
  88. data/lib/pindo/module/buildconfighelper.rb +0 -13
  89. data/lib/pindo/module/buildhelper.rb +0 -76
  90. data/lib/pindo/module/config_project.sh +0 -143
  91. data/lib/pindo/module/configprojhelper.rb +0 -631
  92. data/lib/pindo/module/icon_contents.json +0 -116
  93. data/lib/pindo/module/imessage_icon.json +0 -91
  94. data/lib/pindo/module/imgset_contents.json +0 -21
  95. data/lib/pindo/module/launchimg_contents.json +0 -21
  96. data/lib/pindo/module/xcodebuildpre.rb +0 -258
  97. data/lib/pindo/pgyer/upload.rb +0 -234
  98. data/lib/pindo/pgyer.rb +0 -17
  99. data/lib/pindo/utils/icon.rb +0 -91
  100. data/lib/pindo/utils/icon.sh +0 -133
  101. data/lib/pindo/utils/podindex.rb +0 -56
  102. data/lib/pindo/utils/podindex.sh +0 -30
  103. data/lib/pindo/utils.rb +0 -29
  104. /data/lib/pindo/{deploy → command/deploy}/check.rb +0 -0
  105. /data/lib/pindo/{deploy → command/deploy}/confusecode.rb +0 -0
  106. /data/lib/pindo/{deploy → command/deploy}/confuseproj.rb +0 -0
  107. /data/lib/pindo/{deploy → command/deploy}/fabric.rb +0 -0
  108. /data/lib/pindo/{deploy → command/deploy}/initconfig.rb +0 -0
  109. /data/lib/pindo/{deploy → command/deploy}/pullconfig.rb +0 -0
  110. /data/lib/pindo/{deploy → command/deploy}/pushconfig.rb +0 -0
  111. /data/lib/pindo/{deploy → command/deploy}/quswark.rb +0 -0
  112. /data/lib/pindo/{deploy → command/deploy}/quswauth.rb +0 -0
  113. /data/lib/pindo/{deploy → command/deploy}/reportbug.rb +0 -0
  114. /data/lib/pindo/{deploy → command/deploy}/tag.rb +0 -0
  115. /data/lib/pindo/{deploy → command/deploy}/updateconfig.rb +0 -0
  116. /data/lib/pindo/{deploy → command/deploy}/uploadipa.rb +0 -0
  117. /data/lib/pindo/{dev → command/dev}/confusecode.rb +0 -0
  118. /data/lib/pindo/{dev → command/dev}/confuseproj.rb +0 -0
  119. /data/lib/pindo/{dev → command/dev}/pub.rb +0 -0
  120. /data/lib/pindo/{dev → command/dev}/renewcert.rb +0 -0
  121. /data/lib/pindo/{env → command/env}/dreamstudio.rb +0 -0
  122. /data/lib/pindo/{env → command/env}/quarkenv.rb +0 -0
  123. /data/lib/pindo/{env → command/env}/swarkenv.rb +0 -0
  124. /data/lib/pindo/{env → command/env}/workhard.rb +0 -0
  125. /data/lib/pindo/{lib → command/lib}/forcepush.rb +0 -0
  126. /data/lib/pindo/{lib → command/lib}/lint.rb +0 -0
  127. /data/lib/pindo/{lib → command/lib}/push.rb +0 -0
  128. /data/lib/pindo/{repo → command/repo}/clone.rb +0 -0
  129. /data/lib/pindo/{repo → command/repo}/create.rb +0 -0
  130. /data/lib/pindo/{repo → command/repo}/login.rb +0 -0
  131. /data/lib/pindo/{repo → command/repo}/search.rb +0 -0
  132. /data/lib/pindo/{setup.rb → command/setup.rb} +0 -0
  133. /data/lib/pindo/{upgrade.rb → command/upgrade.rb} +0 -0
  134. /data/lib/pindo/{utils → command/utils}/clearcert.rb +0 -0
  135. /data/lib/pindo/{utils → command/utils}/device.rb +0 -0
  136. /data/lib/pindo/{utils → command/utils}/tgate.rb +0 -0
  137. /data/lib/pindo/{utils → command/utils}/xcassets.rb +0 -0
  138. /data/lib/pindo/{utils → command/utils}/xcassets.sh +0 -0
  139. /data/lib/pindo/module/{appstore_in_app_purchase.rb → appstore/appstore_in_app_purchase.rb} +0 -0
  140. /data/lib/pindo/module/{appstore_metadata_connect_api_helper.rb → appstore/appstore_metadata_connect_api_helper.rb} +0 -0
  141. /data/lib/pindo/module/{appstore_metadata_fastlane_helper.rb → appstore/appstore_metadata_fastlane_helper.rb} +0 -0
  142. /data/lib/pindo/module/{iap_tier.json → appstore/iap_tier.json} +0 -0
  143. /data/lib/pindo/module/{commonconfuseproj.rb → build/commonconfuseproj.rb} +0 -0
  144. /data/lib/pindo/module/{xcodehelper.rb → xcode/xcodehelper.rb} +0 -0
@@ -281,6 +281,14 @@ module Pindo
281
281
  return url
282
282
  end
283
283
 
284
+ def cert_key_password
285
+ cert_key_password = ''
286
+ if !@pindo_user_config_json.nil? && !@pindo_user_config_json["develop_accout_info"].nil?
287
+ cert_key_password = @pindo_user_config_json["develop_accout_info"]["cert_key_password"]
288
+ end
289
+ return cert_key_password
290
+ end
291
+
284
292
  def deploy_cert_giturl
285
293
  url = ''
286
294
  if !@pindo_user_config_json.nil? && !@pindo_user_config_json["develop_accout_info"].nil?
@@ -288,6 +296,16 @@ module Pindo
288
296
  end
289
297
  return url
290
298
  end
299
+
300
+ def deploy_cert_decrypt_password
301
+ pass = ''
302
+ if !@pindo_user_config_json.nil? && !@pindo_user_config_json["develop_accout_info"].nil?
303
+ pass = @pindo_user_config_json["develop_accout_info"]["deploy_cert_decrypt_password"]
304
+ end
305
+ return pass
306
+ end
307
+
308
+
291
309
 
292
310
  def dev_cert_giturl
293
311
  url = ''
@@ -297,6 +315,15 @@ module Pindo
297
315
  return url
298
316
  end
299
317
 
318
+
319
+ def dev_cert_decrypt_password
320
+ pass = ''
321
+ if !@pindo_user_config_json.nil? && !@pindo_user_config_json["develop_accout_info"].nil?
322
+ pass = @pindo_user_config_json["develop_accout_info"]["dev_cert_decrypt_password"]
323
+ end
324
+ return pass
325
+ end
326
+
300
327
  def demo_apple_id
301
328
  url = ''
302
329
  if !@pindo_user_config_json.nil? && !@pindo_user_config_json["develop_accout_info"].nil?
@@ -45,7 +45,6 @@ module Pindo
45
45
 
46
46
  def all_tool_bundleid
47
47
  setting_file = File.join(pindo_single_config.pindo_env_configdir,'bundleid_config.json')
48
- puts setting_file
49
48
  setting_json = load_setting(setting_file:setting_file)
50
49
  sub_json = []
51
50
  if !setting_json.nil? && !setting_json['all_tool_bundleid'].nil?
@@ -86,10 +85,10 @@ module Pindo
86
85
  puts "具体参考文档: https://tower.im/teams/851356/repository_documents/714/"
87
86
  puts ""
88
87
  puts "App Type:"
89
-
88
+ puts
90
89
  cli.choose do |menu| # you can also use constants like :blue
91
- menu.header = "选用哪个bundle id?"
92
- menu.prompt = "请选择使用的bundle id,请输入选项(1/2/3...):"
90
+ menu.header = "可用的Bundle Id如下:"
91
+ menu.prompt = "请选择使用的Bundle Id,请输入选项(1/2/3...):"
93
92
  menu.index_suffix = ") "
94
93
  if !sub_setting_json.nil?
95
94
  sub_setting_json.each do |key, items|
@@ -115,9 +114,10 @@ module Pindo
115
114
 
116
115
  cli = HighLine.new
117
116
  menu_choice="None"
117
+ puts
118
118
  cli.choose do |menu| # you can also use constants like :blue
119
- menu.header = "选用哪个bundle id?"
120
- menu.prompt = "请选择使用的bundle id,请输入选项(1/2/3...):"
119
+ menu.header = "可用的Bundle Id如下:"
120
+ menu.prompt = "请选择使用的Bundle Id,请输入选项(1/2/3...):"
121
121
  if !all_bundleid.nil? && all_bundleid.length > 0
122
122
  for bunld_id in all_bundleid do
123
123
  menu.choice(bunld_id) do |details|
@@ -140,10 +140,11 @@ module Pindo
140
140
 
141
141
  cli = HighLine.new
142
142
  menu_choice="None"
143
+ puts
143
144
  cli.choose do |menu| # you can also use constants like :blue
144
145
 
145
- menu.header = "选用哪个bundle id?"
146
- menu.prompt = "请选择使用的bundle id,请输入选项(1/2/3...):"
146
+ menu.header = "可用的Bundle Id如下:"
147
+ menu.prompt = "请选择使用的Bundle Id,请输入选项(1/2/3...):"
147
148
  if !all_bundleid.nil? && all_bundleid.length > 0
148
149
  for bunld_id in all_bundleid do
149
150
  menu.choice(bunld_id) do |details|
@@ -0,0 +1,95 @@
1
+ require 'highline/import'
2
+ require 'xcodeproj'
3
+ require 'json'
4
+ require 'yaml'
5
+
6
+
7
+ module Pindo
8
+ module SwarkHelper
9
+
10
+ def check_swark_authorize(config_json:nil, buid_type:nil)
11
+ if !config_json.nil? && config_json['project_info']['xcode_build_type'] && config_json["project_info"]["xcode_build_type"].include?("swark")
12
+
13
+ app_config_dir = File.join(File::expand_path(pindo_single_config.pindo_dir), @deploy_identifier)
14
+ swark_authorize_file = File.join(app_config_dir, "swark_authorize.json")
15
+ if File.exist?(swark_authorize_file)
16
+ swark_authorize_json=JSON.parse(File.read(swark_authorize_file))
17
+ unless swark_authorize_json && swark_authorize_json['swark_authorize_status']
18
+ show_swark_authorize_error(swark_authorize_json:swark_authorize_json , buid_type:buid_type)
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ def show_swark_authorize_error(swark_authorize_json:nil, buid_type:nil)
25
+
26
+ puts "++++ +++ Need authorize !!! ++++"
27
+ puts "+++ Need authorize !!! ++++"
28
+ puts "+++ Need authorize !!! +++++"
29
+ puts " =========================="
30
+ puts
31
+
32
+ puts "方法一:"
33
+ command = " 1. swark authorize "
34
+ command = command + swark_authorize_json["swark_authorize_teamid"] + "." + swark_authorize_json["swark_authorize_bundleid"]
35
+ puts command
36
+ command = " 2. 修改swark_authorize.json 中的 swark_authorize_status = true 并且提交"
37
+ puts
38
+
39
+ puts "方法二:"
40
+ puts " pindo deploy quswark"
41
+ if buid_type.downcase.to_s.include?("appstore")
42
+ raise Informative, "Need swark authorize !!!"
43
+ end
44
+ end
45
+
46
+
47
+ def add_swark_entitlement(entitlements_plist_path:nil, bundle_id_dict:nil)
48
+ if @config_json['project_info']['xcode_build_type'] && @config_json["project_info"]["xcode_build_type"].include?("swark")
49
+ if File.exist?(entitlements_plist_path)
50
+ entitlements_plist_dict = Xcodeproj::Plist.read_from_path(entitlements_plist_path)
51
+
52
+ key_value = @team_id_vaule
53
+ key_value = key_value + "." + @bundle_id
54
+ entitlements_plist_dict['keychain-access-groups'] = [key_value]
55
+
56
+ Xcodeproj::Plist.write_to_path(entitlements_plist_dict, entitlements_plist_path)
57
+ end
58
+ end
59
+ end
60
+
61
+ def add_swark_authorize_json
62
+ if @build_type == "appstore" && @config_json['project_info']['xcode_build_type'] && @config_json["project_info"]["xcode_build_type"].include?("swark")
63
+
64
+ app_config_dir = clong_buildconfig_repo(repo_name: @deploy_repo_name)
65
+ swark_authorize_file = File.join(app_config_dir, "swark_authorize.json")
66
+ if File.exist?(swark_authorize_file)
67
+ swark_authorize_json=JSON.parse(File.read(swark_authorize_file))
68
+ if swark_authorize_json && swark_authorize_json['swark_authorize_status']
69
+ puts "swark already authorize success !!! "
70
+ else
71
+ swark_authorize_json = swark_authorize_json || {}
72
+ swark_authorize_json['swark_authorize_teamid'] = @team_id_vaule
73
+ swark_authorize_json['swark_authorize_bundleid'] = @bundle_id
74
+ swark_authorize_json['swark_authorize_status'] = false
75
+ File.open(swark_authorize_file, "w") do |f|
76
+ f.write(JSON.pretty_generate(swark_authorize_json))
77
+ end
78
+ git_addpush_repo(path:app_config_dir, message:"add swark authorize json")
79
+ end
80
+ else
81
+ swark_authorize_json = swark_authorize_json || {}
82
+ swark_authorize_json['swark_authorize_teamid'] = @team_id_vaule
83
+ swark_authorize_json['swark_authorize_bundleid'] = @bundle_id
84
+ swark_authorize_json['swark_authorize_status'] = false
85
+ File.open(swark_authorize_file, "w") do |f|
86
+ f.write(JSON.pretty_generate(swark_authorize_json))
87
+ end
88
+ git_addpush_repo(path:app_config_dir, message:"add swark authorize json")
89
+ end
90
+
91
+ end
92
+ end
93
+
94
+ end
95
+ end
@@ -0,0 +1,187 @@
1
+
2
+ require 'match'
3
+ require 'openssl'
4
+ require 'pindo/base/aeshelper'
5
+ require 'pindo/module/cert/keychainhelper'
6
+ require 'pindo/module/cert/provisioninghelper'
7
+
8
+
9
+ module Pindo
10
+
11
+ module CertHelper
12
+
13
+ def get_cert_info(cer_certificate)
14
+ # can receive a certificate path or the file data
15
+ begin
16
+ if File.exist?(cer_certificate)
17
+ cer_certificate = File.binread(cer_certificate)
18
+ end
19
+ rescue ArgumentError
20
+ # cert strings have null bytes; suppressing output
21
+ end
22
+
23
+ cert = OpenSSL::X509::Certificate.new(cer_certificate)
24
+
25
+ # openssl output:
26
+ # subject= /UID={User ID}/CN={Certificate Name}/OU={Certificate User}/O={Organisation}/C={Country}
27
+ cert_info = cert.subject.to_s.gsub(/\s*subject=\s*/, "").tr("/", "\n")
28
+ out_array = cert_info.split("\n")
29
+ openssl_keys_to_readable_keys = {
30
+ 'UID' => 'User ID',
31
+ 'CN' => 'Common Name',
32
+ 'OU' => 'Organisation Unit',
33
+ 'O' => 'Organisation',
34
+ 'C' => 'Country',
35
+ 'notBefore' => 'Start Datetime',
36
+ 'notAfter' => 'End Datetime'
37
+ }
38
+
39
+ return out_array.map { |x| x.split(/=+/) if x.include?("=") }
40
+ .compact
41
+ .map { |k, v| [openssl_keys_to_readable_keys.fetch(k, k), v] }
42
+ .push([openssl_keys_to_readable_keys.fetch("notBefore"), cert.not_before])
43
+ .push([openssl_keys_to_readable_keys.fetch("notAfter"), cert.not_after])
44
+ rescue => ex
45
+ raise Informative, "get_cert_info: #{ex}"
46
+ return {}
47
+ end
48
+
49
+ def select_cert_or_key(paths:)
50
+ cert_id_path = ENV['MATCH_CERTIFICATE_ID'] ? paths.find { |path| path.include?(ENV['MATCH_CERTIFICATE_ID']) } : nil
51
+ cert_id_path || paths.last
52
+ end
53
+
54
+ def is_cert_valid?(cer_certificate_path)
55
+ cert = OpenSSL::X509::Certificate.new(File.binread(cer_certificate_path))
56
+ now = Time.now.utc
57
+ return (now <=> cert.not_after) == -1
58
+ end
59
+
60
+ def isMac?
61
+ (/darwin/ =~ RUBY_PLATFORM) != nil
62
+ end
63
+
64
+ def install_certs(cert_url:nil, certs_dir:nil, cert_type:nil)
65
+
66
+
67
+ if !cert_type.downcase.include?("development")
68
+ cert_type = "distribution"
69
+ end
70
+
71
+ certs = Dir[File.join(certs_dir, "certs", cert_type.to_s, "*.cer")]
72
+ keys = Dir[File.join(certs_dir, "certs", cert_type.to_s, "*.p12")]
73
+
74
+ if certs.count == 0 || keys.count == 0
75
+ raise Informative, "No certificates found in #{certs_dir}"
76
+ else
77
+ output_dir = Dir.mktmpdir
78
+
79
+ decrypt_password = AESHelper.fetch_password(keychain_name:cert_url)
80
+ Funlog.instance.fancyinfo_start("正在安装证书...")
81
+
82
+ cert_path = AESHelper.decrypt_specific_file(src_file: certs.first, password:decrypt_password, output_dir: output_dir)
83
+ unless cert_path.nil? || File.exist?(cert_path)
84
+ AESHelper.delete_password(keychain_name:cert_url)
85
+ raise Informative, "证书解析失败,密码错误!"
86
+ end
87
+
88
+ key_path = AESHelper.decrypt_specific_file(src_file: keys.first, password:decrypt_password, output_dir: output_dir)
89
+ unless key_path.nil? || File.exist?(key_path)
90
+ AESHelper.delete_password(keychain_name:cert_url)
91
+ raise Informative, "证书解析失败,密码错误!"
92
+ end
93
+
94
+ unless is_cert_valid?(cert_path)
95
+ raise Informative, "证书已经过期,请重新生产新证书!"
96
+ end
97
+
98
+
99
+ if isMac?
100
+
101
+ keychain_name = "login.keychain"
102
+ if FastlaneCore::CertChecker.installed?(cert_path, in_keychain: nil)
103
+ Funlog.instance.fancyinfo_success("证书#{File.basename(cert_path)}已安装,无需重复安装!")
104
+ else
105
+
106
+ cert_password = Pindoconfig.instance.cert_key_password
107
+ keychain = 'login.keychain'
108
+ keychain_path = FastlaneCore::Helper.keychain_path(keychain)
109
+
110
+ KeychainHelper.import_file(cert_path, keychain_path, keychain_password: cert_password, certificate_password:'' )
111
+ KeychainHelper.import_file(key_path, keychain_path, keychain_password: cert_password, certificate_password: '')
112
+
113
+
114
+ Funlog.instance.fancyinfo_success("证书'#{File.basename(cert_path)}'安装完成!")
115
+ end
116
+ else
117
+ Funlog.instance.fancyinfo_error("非Mac电脑不支持安装证书!")
118
+ end
119
+
120
+ end
121
+
122
+ def install_provisionfiles(cert_url:nil, certs_dir:nil, bundle_id_map:nil, cert_type:nil)
123
+
124
+
125
+ Funlog.instance.fancyinfo_start("正在安装Provisioning Profiles文件...")
126
+
127
+
128
+ if cert_type.downcase.include?("development")
129
+ cert_type = "Development"
130
+ elsif cert_type.downcase.include?("adhoc")
131
+ cert_type = "Adhoc"
132
+ else
133
+ cert_type = "AppStore"
134
+ end
135
+
136
+ un_exist_files = []
137
+
138
+ provisioning_info_array = []
139
+
140
+
141
+
142
+ provisioning_info_array = []
143
+
144
+ bundle_id_map.each do |type, bundle_id_temp|
145
+ profile_filename = File.join(certs_dir, "profiles", cert_type.downcase.to_s,[cert_type.to_s, bundle_id_temp].join('_') + '.mobileprovision')
146
+ unless File.exist?(profile_filename)
147
+ un_exist_files << profile_filename
148
+ next
149
+ end
150
+ # puts "正在安装 #{bundle_id_temp}..."
151
+ decrypt_password = AESHelper.fetch_password(keychain_name:cert_url)
152
+ output_dir = Dir.mktmpdir
153
+ file_decrypt = AESHelper.decrypt_specific_file(src_file: profile_filename, password:decrypt_password, output_dir: output_dir)
154
+ destpath = Provisioninghelper.install(file_decrypt)
155
+ parsed_data = Provisioninghelper.parse(destpath)
156
+
157
+ provisioning_info = {}
158
+ provisioning_info['type'] = type
159
+ provisioning_info['bundle_id'] = bundle_id_temp
160
+ provisioning_info['profile_name'] = parsed_data['Name']
161
+ provisioning_info['profile_path'] = destpath
162
+
163
+
164
+ cert_info = get_cert_info(parsed_data["DeveloperCertificates"].first.string).to_h
165
+ provisioning_info['signing_identity'] = cert_info["Common Name"]
166
+ provisioning_info['team_id'] = parsed_data["TeamIdentifier"].first
167
+
168
+ # puts JSON.pretty_generate(provisioning_info)
169
+ provisioning_info_array << provisioning_info
170
+ end
171
+
172
+ Funlog.instance.fancyinfo_success("Provisioning Profiles文件安装完成!")
173
+
174
+ if un_exist_files.size > 0
175
+ Funlog.instance.fancyinfo_error("证书Provisioning Profiles文件不存在!")
176
+ raise Informative, "The following profiles do not exist: #{un_exist_files.join(', ')}"
177
+ end
178
+
179
+ return provisioning_info_array
180
+ end
181
+
182
+
183
+ end
184
+
185
+
186
+ end
187
+ end
@@ -0,0 +1,150 @@
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,6 +1,8 @@
1
1
 
2
2
  module Pindo
3
- module Pemcreate
3
+
4
+ module PemHelper
5
+
4
6
  def create_certificate(bundle_id:nil, type:"prod", output_path:"")
5
7
 
6
8
  if bundle_id.empty? || output_path.empty?
@@ -0,0 +1,137 @@
1
+ require 'plist'
2
+ require 'json'
3
+
4
+ module Pindo
5
+ class Provisioninghelper
6
+ class << self
7
+ # @return (Hash) The hash with the data of the provisioning profile
8
+ # @example
9
+ # {"AppIDName"=>"My App Name",
10
+ # "ApplicationIdentifierPrefix"=>["5A997XSAAA"],
11
+ # "CreationDate"=>#<DateTime: 2015-05-24T20:38:03+00:00 ((2457167j,74283s,0n),+0s,2299161j)>,
12
+ # "DeveloperCertificates"=>[#<StringIO:0x007f944b9666f8>],
13
+ # "Entitlements"=>
14
+ # {"keychain-access-groups"=>["5A997XSAAA.*"],
15
+ # "get-task-allow"=>false,
16
+ # "application-identifier"=>"5A997XAAA.net.sunapps.192",
17
+ # "com.apple.developer.team-identifier"=>"5A997XAAAA",
18
+ # "aps-environment"=>"production",
19
+ # "beta-reports-active"=>true},
20
+ # "ExpirationDate"=>#<DateTime: 2015-11-25T22:45:50+00:00 ((2457352j,81950s,0n),+0s,2299161j)>,
21
+ # "Name"=>"net.sunapps.192 AppStore",
22
+ # "TeamIdentifier"=>["5A997XSAAA"],
23
+ # "TeamName"=>"SunApps GmbH",
24
+ # "TimeToLive"=>185,
25
+ # "UUID"=>"1752e382-53bd-4910-a393-aaa7de0005ad",
26
+ # "Version"=>1}
27
+ def parse(path, keychain_path = nil)
28
+
29
+
30
+ plist = Plist.parse_xml(decode(path, keychain_path))
31
+ # puts JSON.pretty_generate(plist)
32
+ if (plist || []).count > 5
33
+ plist
34
+ else
35
+ raise Informative, "Error parsing provisioning profile at path '#{path}'"
36
+ end
37
+ end
38
+
39
+ # @return [String] The UUID of the given provisioning profile
40
+ def uuid(path, keychain_path = nil)
41
+ parse(path, keychain_path).fetch("UUID")
42
+ end
43
+
44
+ # @return [String] The Name of the given provisioning profile
45
+ def name(path, keychain_path = nil)
46
+ parse(path, keychain_path).fetch("Name")
47
+ end
48
+
49
+ def bundle_id(path, keychain_path = nil)
50
+ profile = parse(path, keychain_path)
51
+ app_id_prefix = profile["ApplicationIdentifierPrefix"].first
52
+ entitlements = profile["Entitlements"]
53
+ app_identifier = entitlements["application-identifier"] || entitlements["com.apple.application-identifier"]
54
+ bundle_id = app_identifier.gsub("#{app_id_prefix}.", "")
55
+ bundle_id
56
+ rescue
57
+ UI.error("Unable to extract the Bundle Id from the provided provisioning profile '#{path}'.")
58
+ end
59
+
60
+ def mac?(path, keychain_path = nil)
61
+ parse(path, keychain_path).fetch("Platform", []).include?('OSX')
62
+ end
63
+
64
+ def profile_filename(path, keychain_path = nil)
65
+ basename = uuid(path, keychain_path)
66
+ basename + profile_extension(path, keychain_path)
67
+ end
68
+
69
+ def profile_extension(path, keychain_path = nil)
70
+ if mac?(path, keychain_path)
71
+ ".provisionprofile"
72
+ else
73
+ ".mobileprovision"
74
+ end
75
+ end
76
+
77
+ def profiles_path
78
+ path = File.expand_path("~") + "/Library/MobileDevice/Provisioning Profiles/"
79
+ # If the directory doesn't exist, create it first
80
+ unless File.directory?(path)
81
+ FileUtils.mkdir_p(path)
82
+ end
83
+
84
+ return path
85
+ end
86
+
87
+ # Installs a provisioning profile for Xcode to use
88
+ def install(path, keychain_path = nil)
89
+
90
+
91
+ destination = File.join(profiles_path, profile_filename(path, keychain_path))
92
+ # puts "Installing provisioning profile... #{destination}"
93
+
94
+ if path != destination
95
+ # copy to Xcode provisioning profile directory
96
+ FileUtils.copy(path, destination)
97
+ unless File.exist?(destination)
98
+ raise Informative, "Failed installation of provisioning profile at location: '#{destination}'"
99
+ end
100
+ end
101
+ destination
102
+ end
103
+
104
+ private
105
+
106
+ def isMacOS?
107
+ (/darwin/ =~ RUBY_PLATFORM) != nil
108
+ end
109
+
110
+
111
+ def decode(path, keychain_path = nil)
112
+ require 'tmpdir'
113
+ Dir.mktmpdir('fastlane') do |dir|
114
+ err = "#{dir}/cms.err"
115
+ # we want to prevent the error output to mix up with the standard output because of
116
+ # /dev/null: https://github.com/fastlane/fastlane/issues/6387
117
+ if isMacOS?
118
+ if keychain_path.nil?
119
+ decoded = `security cms -D -i "#{path}" 2> #{err}`
120
+ else
121
+ decoded = `security cms -D -i "#{path}" -k "#{keychain_path.shellescape}" 2> #{err}`
122
+ end
123
+ else
124
+ # `security` only works on Mac, fallback to `openssl`
125
+ # via https://stackoverflow.com/a/14379814/252627
126
+ decoded = `openssl smime -inform der -verify -noverify -in #{path.shellescape} 2> #{err}`
127
+ end
128
+
129
+ if $?.exitstatus != 0
130
+ raise Informative, "Failure to decode #{path}"
131
+ end
132
+ decoded
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end