pindo 4.6.9 → 4.7.0
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/pindo/base/aeshelper.rb +48 -2
- data/lib/pindo/base/funlog.rb +89 -0
- data/lib/pindo/base/githelper.rb +30 -12
- data/lib/pindo/base/plaininformative.rb +3 -0
- data/lib/pindo/base/standarderror.rb +1 -0
- data/lib/pindo/base/xcodeconst.rb +251 -0
- data/lib/pindo/client/applovinclient.rb +6 -3
- data/lib/pindo/client/aws3sclient.rb +33 -46
- data/lib/pindo/client/bossclient.rb +1 -1
- data/lib/pindo/client/pgyerclient.rb +78 -14
- data/lib/pindo/command/appstore/iap.rb +43 -0
- data/lib/pindo/command/appstore/itcapp.rb +41 -0
- data/lib/pindo/command/appstore/metadata.rb +43 -0
- data/lib/pindo/command/appstore/screenshots.rb +43 -0
- data/lib/pindo/command/appstore/upload.rb +40 -0
- data/lib/pindo/command/appstore.rb +17 -0
- data/lib/pindo/command/deploy/build.rb +109 -0
- data/lib/pindo/{deploy → command/deploy}/bundleid.rb +1 -1
- data/lib/pindo/command/deploy/cert.rb +179 -0
- data/lib/pindo/command/deploy/configproj.rb +105 -0
- data/lib/pindo/{deploy → command/deploy}/getitcinfo.rb +1 -1
- data/lib/pindo/{deploy → command/deploy}/iap.rb +30 -9
- data/lib/pindo/{deploy → command/deploy}/itcapp.rb +0 -1
- data/lib/pindo/{deploy → command/deploy}/itcinfo.rb +2 -3
- data/lib/pindo/{deploy → command/deploy}/pem.rb +3 -2
- data/lib/pindo/{deploy → command/deploy}/resign.rb +14 -63
- data/lib/pindo/command/deploy.rb +44 -0
- data/lib/pindo/{dev → command/dev}/autobuild.rb +17 -80
- data/lib/pindo/{dev → command/dev}/autoresign.rb +17 -64
- data/lib/pindo/{dev → command/dev}/createbuild.rb +0 -2
- data/lib/pindo/{dev → command/dev}/debug.rb +6 -2
- data/lib/pindo/command/dev/pgyercert.rb +75 -0
- data/lib/pindo/command/dev.rb +25 -0
- data/lib/pindo/command/env.rb +17 -0
- data/lib/pindo/{ipa → command/ipa}/autoresign.rb +18 -70
- data/lib/pindo/{ipa → command/ipa}/import.rb +47 -102
- data/lib/pindo/{ipa → command/ipa}/output.rb +38 -135
- data/lib/pindo/command/ipa.rb +16 -0
- data/lib/pindo/{lib → command/lib}/update.rb +19 -10
- data/lib/pindo/command/lib.rb +16 -0
- data/lib/pindo/{pgyer → command/pgyer}/apptest.rb +7 -29
- data/lib/pindo/{pgyer → command/pgyer}/comment.rb +7 -30
- data/lib/pindo/{pgyer → command/pgyer}/download.rb +35 -30
- data/lib/pindo/{pgyer → command/pgyer}/login.rb +3 -4
- data/lib/pindo/command/pgyer/resign.rb +111 -0
- data/lib/pindo/command/pgyer/upload.rb +123 -0
- data/lib/pindo/command/pgyer.rb +18 -0
- data/lib/pindo/{repo.rb → command/repo.rb} +4 -4
- data/lib/pindo/{utils → command/utils}/applovin.rb +43 -33
- data/lib/pindo/{utils → command/utils}/boss.rb +3 -3
- data/lib/pindo/command/utils/icon.rb +81 -0
- data/lib/pindo/{utils → command/utils}/renewproj.rb +1 -0
- data/lib/pindo/command/utils.rb +26 -0
- data/lib/pindo/command.rb +23 -26
- data/lib/pindo/module/build/swarkhelper.rb +95 -0
- data/lib/pindo/module/cert/certhelper.rb +176 -0
- data/lib/pindo/module/cert/keychainhelper.rb +138 -0
- data/lib/pindo/module/{pemcreate.rb → cert/pemhelper.rb} +3 -1
- data/lib/pindo/module/cert/provisioninghelper.rb +137 -0
- data/lib/pindo/module/cert/xcodecerthelper.rb +301 -0
- data/lib/pindo/module/{pgyerhelper.rb → pgyer/pgyerhelper.rb} +246 -35
- data/lib/pindo/module/xcode/xcodeappconfig.rb +188 -0
- data/lib/pindo/module/xcode/xcodebuildconfig.rb +12 -0
- data/lib/pindo/module/xcode/xcodebuildhelper.rb +312 -0
- data/lib/pindo/module/xcode/xcoderesconstant.rb +248 -0
- data/lib/pindo/module/xcode/xcodereshandler.rb +198 -0
- data/lib/pindo/module/xcode/xcodereshelper.rb +120 -0
- data/lib/pindo/options/appconfigoptions.rb +1 -0
- data/lib/pindo/options/deployoptions.rb +38 -41
- data/lib/pindo/version.rb +1 -1
- metadata +109 -97
- data/lib/pindo/deploy/Fastfile +0 -233
- data/lib/pindo/deploy/build.rb +0 -167
- data/lib/pindo/deploy/cert.rb +0 -508
- data/lib/pindo/deploy/configproj.rb +0 -89
- data/lib/pindo/deploy.rb +0 -44
- data/lib/pindo/dev.rb +0 -23
- data/lib/pindo/env/flutter.rb +0 -59
- data/lib/pindo/env/flutter.sh +0 -116
- data/lib/pindo/env.rb +0 -17
- data/lib/pindo/ipa.rb +0 -22
- data/lib/pindo/lib.rb +0 -18
- data/lib/pindo/module/buildconfighelper.rb +0 -13
- data/lib/pindo/module/buildhelper.rb +0 -76
- data/lib/pindo/module/config_project.sh +0 -143
- data/lib/pindo/module/configprojhelper.rb +0 -631
- data/lib/pindo/module/icon_contents.json +0 -116
- data/lib/pindo/module/imessage_icon.json +0 -91
- data/lib/pindo/module/imgset_contents.json +0 -21
- data/lib/pindo/module/launchimg_contents.json +0 -21
- data/lib/pindo/module/xcodebuildpre.rb +0 -258
- data/lib/pindo/pgyer/upload.rb +0 -234
- data/lib/pindo/pgyer.rb +0 -17
- data/lib/pindo/utils/icon.rb +0 -91
- data/lib/pindo/utils/icon.sh +0 -133
- data/lib/pindo/utils/podindex.rb +0 -56
- data/lib/pindo/utils/podindex.sh +0 -30
- data/lib/pindo/utils.rb +0 -29
- /data/lib/pindo/{deploy → command/deploy}/check.rb +0 -0
- /data/lib/pindo/{deploy → command/deploy}/confusecode.rb +0 -0
- /data/lib/pindo/{deploy → command/deploy}/confuseproj.rb +0 -0
- /data/lib/pindo/{deploy → command/deploy}/fabric.rb +0 -0
- /data/lib/pindo/{deploy → command/deploy}/initconfig.rb +0 -0
- /data/lib/pindo/{deploy → command/deploy}/pullconfig.rb +0 -0
- /data/lib/pindo/{deploy → command/deploy}/pushconfig.rb +0 -0
- /data/lib/pindo/{deploy → command/deploy}/quswark.rb +0 -0
- /data/lib/pindo/{deploy → command/deploy}/quswauth.rb +0 -0
- /data/lib/pindo/{deploy → command/deploy}/reportbug.rb +0 -0
- /data/lib/pindo/{deploy → command/deploy}/tag.rb +0 -0
- /data/lib/pindo/{deploy → command/deploy}/updateconfig.rb +0 -0
- /data/lib/pindo/{deploy → command/deploy}/uploadipa.rb +0 -0
- /data/lib/pindo/{dev → command/dev}/confusecode.rb +0 -0
- /data/lib/pindo/{dev → command/dev}/confuseproj.rb +0 -0
- /data/lib/pindo/{dev → command/dev}/pub.rb +0 -0
- /data/lib/pindo/{dev → command/dev}/renewcert.rb +0 -0
- /data/lib/pindo/{env → command/env}/dreamstudio.rb +0 -0
- /data/lib/pindo/{env → command/env}/quarkenv.rb +0 -0
- /data/lib/pindo/{env → command/env}/swarkenv.rb +0 -0
- /data/lib/pindo/{env → command/env}/workhard.rb +0 -0
- /data/lib/pindo/{lib → command/lib}/forcepush.rb +0 -0
- /data/lib/pindo/{lib → command/lib}/lint.rb +0 -0
- /data/lib/pindo/{lib → command/lib}/push.rb +0 -0
- /data/lib/pindo/{repo → command/repo}/clone.rb +0 -0
- /data/lib/pindo/{repo → command/repo}/create.rb +0 -0
- /data/lib/pindo/{repo → command/repo}/login.rb +0 -0
- /data/lib/pindo/{repo → command/repo}/search.rb +0 -0
- /data/lib/pindo/{setup.rb → command/setup.rb} +0 -0
- /data/lib/pindo/{upgrade.rb → command/upgrade.rb} +0 -0
- /data/lib/pindo/{utils → command/utils}/clearcert.rb +0 -0
- /data/lib/pindo/{utils → command/utils}/device.rb +0 -0
- /data/lib/pindo/{utils → command/utils}/tgate.rb +0 -0
- /data/lib/pindo/{utils → command/utils}/xcassets.rb +0 -0
- /data/lib/pindo/{utils → command/utils}/xcassets.sh +0 -0
- /data/lib/pindo/module/{appstore_in_app_purchase.rb → appstore/appstore_in_app_purchase.rb} +0 -0
- /data/lib/pindo/module/{appstore_metadata_connect_api_helper.rb → appstore/appstore_metadata_connect_api_helper.rb} +0 -0
- /data/lib/pindo/module/{appstore_metadata_fastlane_helper.rb → appstore/appstore_metadata_fastlane_helper.rb} +0 -0
- /data/lib/pindo/module/{iap_tier.json → appstore/iap_tier.json} +0 -0
- /data/lib/pindo/module/{commonconfuseproj.rb → build/commonconfuseproj.rb} +0 -0
- /data/lib/pindo/module/{xcodehelper.rb → xcode/xcodehelper.rb} +0 -0
|
@@ -0,0 +1,138 @@
|
|
|
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 "Could not find file '#{path}'" unless File.exist?(path)
|
|
11
|
+
|
|
12
|
+
password_part = " -P #{certificate_password.shellescape}"
|
|
13
|
+
|
|
14
|
+
command = "security import #{path.shellescape} -k '#{keychain_path.shellescape}'"
|
|
15
|
+
command << password_part
|
|
16
|
+
command << " -T /usr/bin/codesign" # to not be asked for permission when running a tool like `gym` (before Sierra)
|
|
17
|
+
command << " -T /usr/bin/security"
|
|
18
|
+
command << " -T /usr/bin/productbuild" # to not be asked for permission when using an installer cert for macOS
|
|
19
|
+
command << " -T /usr/bin/productsign" # to not be asked for permission when using an installer cert for macOS
|
|
20
|
+
command << " 1> /dev/null" unless output
|
|
21
|
+
|
|
22
|
+
sensitive_command = command.gsub(password_part, " -P ********")
|
|
23
|
+
UI.command(sensitive_command) if output
|
|
24
|
+
Open3.popen3(command) do |stdin, stdout, stderr, thrd|
|
|
25
|
+
UI.command_output(stdout.read.to_s) if output
|
|
26
|
+
|
|
27
|
+
# Set partition list only if success since it can be a time consuming process if a lot of keys are installed
|
|
28
|
+
if thrd.value.success? && !skip_set_partition_list
|
|
29
|
+
keychain_password ||= resolve_keychain_password(keychain_path)
|
|
30
|
+
set_partition_list(path, keychain_path, keychain_password: keychain_password, output: output)
|
|
31
|
+
else
|
|
32
|
+
# Output verbose if file is already installed since not an error otherwise we will show the whole error
|
|
33
|
+
err = stderr.read.to_s.strip
|
|
34
|
+
if err.include?("SecKeychainItemImport") && err.include?("The specified item already exists in the keychain")
|
|
35
|
+
# UI.verbose("'#{File.basename(path)}' is already installed on this machine")
|
|
36
|
+
else
|
|
37
|
+
raise Informative, err
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.help_backticks(command, print: true)
|
|
44
|
+
# UI.command(command) if print
|
|
45
|
+
result = `#{command}`
|
|
46
|
+
# UI.command_output(result) if print
|
|
47
|
+
return result
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.set_partition_list(path, keychain_path, keychain_password: nil, output: false)
|
|
51
|
+
# When security supports partition lists, also add the partition IDs
|
|
52
|
+
# See https://openradar.appspot.com/28524119
|
|
53
|
+
if help_backticks('security -h | grep set-key-partition-list', print: false).length > 0
|
|
54
|
+
password_part = " -k #{keychain_password.to_s.shellescape}"
|
|
55
|
+
|
|
56
|
+
command = "security set-key-partition-list"
|
|
57
|
+
command << " -S apple-tool:,apple:,codesign:"
|
|
58
|
+
command << " -s" # This is a needed in Catalina to prevent "security: SecKeychainItemCopyAccess: A missing value was detected."
|
|
59
|
+
command << password_part
|
|
60
|
+
command << " #{keychain_path.shellescape}"
|
|
61
|
+
command << " 1> /dev/null" # always disable stdout. This can be very verbose, and leak potentially sensitive info
|
|
62
|
+
|
|
63
|
+
# Showing loading indicator as this can take some time if a lot of keys installed
|
|
64
|
+
# Helper.show_loading_indicator("Setting key partition list... (this can take a minute if there are a lot of keys installed)")
|
|
65
|
+
|
|
66
|
+
# Strip keychain password from command output
|
|
67
|
+
sensitive_command = command.gsub(password_part, " -k ********")
|
|
68
|
+
UI.command(sensitive_command) if output
|
|
69
|
+
Open3.popen3(command) do |stdin, stdout, stderr, thrd|
|
|
70
|
+
unless thrd.value.success?
|
|
71
|
+
err = stderr.read.to_s.strip
|
|
72
|
+
|
|
73
|
+
# Inform user when no/wrong password was used as its needed to prevent UI permission popup from Xcode when signing
|
|
74
|
+
if err.include?("SecKeychainItemSetAccessWithPassword")
|
|
75
|
+
keychain_name = File.basename(keychain_path, ".*")
|
|
76
|
+
Security::InternetPassword.delete(server: server_name(keychain_name))
|
|
77
|
+
|
|
78
|
+
# UI.important("")
|
|
79
|
+
# UI.important("Could not configure imported keychain item (certificate) to prevent UI permission popup when code signing\n" \
|
|
80
|
+
# "Check if you supplied the correct `keychain_password` for keychain: `#{keychain_path}`\n" \
|
|
81
|
+
# "#{err}")
|
|
82
|
+
# UI.important("")
|
|
83
|
+
# UI.important("Please look at the following docs to see how to set a keychain password:")
|
|
84
|
+
# UI.important(" - https://docs.fastlane.tools/actions/sync_code_signing")
|
|
85
|
+
# UI.important(" - https://docs.fastlane.tools/actions/get_certificates")
|
|
86
|
+
puts "密码错误"
|
|
87
|
+
else
|
|
88
|
+
raise Informative, err
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Hiding after Open3 finishes
|
|
94
|
+
# Helper.hide_loading_indicator
|
|
95
|
+
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# https://github.com/fastlane/fastlane/issues/14196
|
|
100
|
+
# Keychain password is needed to set the partition list to
|
|
101
|
+
# prevent Xcode from prompting dialog for keychain password when signing
|
|
102
|
+
# 1. Uses keychain password from login keychain if found
|
|
103
|
+
# 2. Prompts user for keychain password and stores it in login keychain for user later
|
|
104
|
+
def self.resolve_keychain_password(keychain_path)
|
|
105
|
+
keychain_name = File.basename(keychain_path, ".*")
|
|
106
|
+
server = server_name(keychain_name)
|
|
107
|
+
|
|
108
|
+
# Attempt to find password in keychain for keychain
|
|
109
|
+
item = Security::InternetPassword.find(server: server)
|
|
110
|
+
if item
|
|
111
|
+
keychain_password = item.password
|
|
112
|
+
# UI.important("Using keychain password from keychain item #{server} in #{keychain_path}")
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
if keychain_password.nil?
|
|
116
|
+
# if UI.interactive?
|
|
117
|
+
# UI.important("Enter the password for #{keychain_path}")
|
|
118
|
+
# UI.important("This passphrase will be stored in your local keychain with the name #{server} and used in future runs")
|
|
119
|
+
# UI.important("This prompt can be avoided by specifying the 'keychain_password' option or 'MATCH_KEYCHAIN_PASSWORD' environment variable")
|
|
120
|
+
keychain_password = FastlaneCore::Helper.ask_password(message: "Password for #{keychain_name} keychain: ", confirm: true, confirmation_message: "Type password for #{keychain_name} keychain again: ")
|
|
121
|
+
Security::InternetPassword.add(server, "", keychain_password)
|
|
122
|
+
# else
|
|
123
|
+
# 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")
|
|
124
|
+
# keychain_password = ""
|
|
125
|
+
# end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
return keychain_password
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# server name used for accessing the macOS keychain
|
|
132
|
+
def self.server_name(keychain_name)
|
|
133
|
+
["fastlane", "keychain", keychain_name].join("_")
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
private_class_method :server_name
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -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
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
|
|
4
|
+
module Pindo
|
|
5
|
+
|
|
6
|
+
module XcodeCertHelper
|
|
7
|
+
|
|
8
|
+
def get_target_name_map
|
|
9
|
+
return {
|
|
10
|
+
"MainTarget" => "bundle_id",
|
|
11
|
+
"Content" => "bundle_id_pushcontent",
|
|
12
|
+
"Service" => "bundle_id_pushservice",
|
|
13
|
+
"Keyboard" => "bundle_id_keyboard",
|
|
14
|
+
"iMessage" => "bundle_id_imessage",
|
|
15
|
+
"Siri" => "bundle_id_siri",
|
|
16
|
+
"SiriUI" => "bundle_id_siriui",
|
|
17
|
+
"Widget" => "bundle_id_widget",
|
|
18
|
+
"Extension" => "bundle_id_extension",
|
|
19
|
+
"ExtensionAd" => "bundle_id_extensionad",
|
|
20
|
+
"ExtensionPorn" => "bundle_id_extensionporn",
|
|
21
|
+
"WatchApp" => "bundle_id_watchapp",
|
|
22
|
+
"WatchAppComplication" => "bundle_id_watchapp_extension"
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def create_provisioning_info_array(build_type:nil)
|
|
27
|
+
|
|
28
|
+
provisioning_info_array = []
|
|
29
|
+
|
|
30
|
+
bundle_id_map = get_bundle_id_map
|
|
31
|
+
|
|
32
|
+
bundle_id_map.each do |type, bundle_id_temp|
|
|
33
|
+
provisioning_info = {}
|
|
34
|
+
provisioning_info['type'] = type
|
|
35
|
+
provisioning_info['bundle_id'] = bundle_id_temp
|
|
36
|
+
name_temp = Match::Utils.environment_variable_name_profile_name(app_identifier: bundle_id_temp, type: build_type)
|
|
37
|
+
provisioning_info['profile_name'] = ENV[name_temp]
|
|
38
|
+
path_temp = Match::Utils.environment_variable_name_profile_path(app_identifier:bundle_id_temp,type: build_type)
|
|
39
|
+
provisioning_info['profile_path'] = ENV[path_temp]
|
|
40
|
+
signing_identity_key=Match::Utils.environment_variable_name_certificate_name(app_identifier: bundle_id_temp,type: build_type)
|
|
41
|
+
provisioning_info["signing_identity"] = ENV[signing_identity_key]
|
|
42
|
+
team_id_key = Match::Utils.environment_variable_name_team_id(app_identifier: bundle_id_temp,type:build_type)
|
|
43
|
+
provisioning_info["team_id"] = ENV[team_id_key]
|
|
44
|
+
provisioning_info_array << provisioning_info
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
return provisioning_info_array
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def config_project_cert(new_proj_name:nil, new_project_dir:nil, cert_type:nil, team_id_vaule:nil, provisioning_info_array:nil)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
new_proj_fullname = File.join(new_project_dir, new_proj_name) + ".xcodeproj"
|
|
55
|
+
new_project_obj = Xcodeproj::Project.open(new_proj_fullname)
|
|
56
|
+
|
|
57
|
+
new_project_obj.root_object.build_configuration_list.set_setting('CODE_SIGN_IDENTITY[sdk=iphoneos*]', "Apple Distribution")
|
|
58
|
+
new_project_obj.root_object.build_configuration_list.set_setting('CODE_SIGN_IDENTITY*', "Apple Distribution")
|
|
59
|
+
|
|
60
|
+
if cert_type == "development"
|
|
61
|
+
new_project_obj.root_object.build_configuration_list.set_setting('CODE_SIGN_IDENTITY[sdk=iphoneos*]', "Apple Development")
|
|
62
|
+
new_project_obj.root_object.build_configuration_list.set_setting('CODE_SIGN_IDENTITY*', "Apple Development")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
new_project_obj.root_object.attributes['TargetAttributes'] = new_project_obj.root_object.attributes['TargetAttributes'] || {}
|
|
66
|
+
target_atts_obj = new_project_obj.root_object.attributes['TargetAttributes']
|
|
67
|
+
|
|
68
|
+
target_map = get_target_name_map
|
|
69
|
+
|
|
70
|
+
new_project_obj.targets.each do |target|
|
|
71
|
+
|
|
72
|
+
provisioning_info = nil
|
|
73
|
+
|
|
74
|
+
if target.product_type.include?(Xcodeproj::Constants::PRODUCT_TYPE_UTI[:application])
|
|
75
|
+
bundle_id_map_key = target_map["MainTarget"]
|
|
76
|
+
provisioning_info = provisioning_info_array.select { |s| s["type"].to_s.eql?(bundle_id_map_key.to_s) }.first
|
|
77
|
+
elsif
|
|
78
|
+
target_map.each do |k, v|
|
|
79
|
+
if target.name.to_s.end_with?(k)
|
|
80
|
+
bundle_id_map_key = v.to_s
|
|
81
|
+
provisioning_info = provisioning_info_array.select { |s| s["type"].to_s.eql?(bundle_id_map_key.to_s) }.first
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
target.build_configurations.each do |config|
|
|
87
|
+
target_atts_obj[target.uuid] = target_atts_obj[target.uuid] || {}
|
|
88
|
+
target_atts_obj[target.uuid]['DevelopmentTeam'] = team_id_vaule
|
|
89
|
+
target_atts_obj[target.uuid]['ProvisioningStyle'] = "Manual"
|
|
90
|
+
config.build_settings['DEVELOPMENT_TEAM'] = team_id_vaule
|
|
91
|
+
config.build_settings['DEVELOPMENT_TEAM[sdk=iphoneos*]'] = team_id_vaule
|
|
92
|
+
config.build_settings['CODE_SIGN_STYLE'] = "Manual"
|
|
93
|
+
|
|
94
|
+
config.build_settings['CODE_SIGN_IDENTITY'] = "Apple Distribution"
|
|
95
|
+
config.build_settings['CODE_SIGN_IDENTITY[sdk=iphoneos*]'] = "Apple Distribution"
|
|
96
|
+
|
|
97
|
+
if cert_type == "development"
|
|
98
|
+
config.build_settings['CODE_SIGN_IDENTITY'] = "Apple Development"
|
|
99
|
+
config.build_settings['CODE_SIGN_IDENTITY[sdk=iphoneos*]'] = "Apple Development"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
if !provisioning_info.nil?
|
|
103
|
+
config.build_settings['PRODUCT_BUNDLE_IDENTIFIER'] = provisioning_info["bundle_id"]
|
|
104
|
+
config.build_settings['PROVISIONING_PROFILE_SPECIFIER'] = provisioning_info['profile_name']
|
|
105
|
+
config.build_settings['PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]'] = provisioning_info['profile_name']
|
|
106
|
+
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
end
|
|
111
|
+
new_project_obj.save
|
|
112
|
+
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def config_infoplist_cert(new_proj_name:nil, new_project_dir:nil, icloud_id:nil, group_id:nil, provisioning_info_array:nil)
|
|
116
|
+
|
|
117
|
+
new_proj_fullname = File.join(new_project_dir, new_proj_name) + ".xcodeproj"
|
|
118
|
+
project_obj = Xcodeproj::Project.open(new_proj_fullname)
|
|
119
|
+
entitlements_plist_path = File.join(new_project_dir, new_proj_name + ".entitlements")
|
|
120
|
+
|
|
121
|
+
project_obj.targets.each do |target|
|
|
122
|
+
|
|
123
|
+
temp_entitlements_file = target.build_configurations.first.build_settings['CODE_SIGN_ENTITLEMENTS']
|
|
124
|
+
if !temp_entitlements_file.nil? && !temp_entitlements_file.empty?
|
|
125
|
+
entitlements_plist_path = File.join(new_project_dir, temp_entitlements_file)
|
|
126
|
+
|
|
127
|
+
if !File.exist?(entitlements_plist_path)
|
|
128
|
+
raise Informative, "Target: #{target.name.to_s} 找不到文件 #{entitlements_plist_path}"
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
temp_info = target.build_configurations.first.build_settings['INFOPLIST_FILE']
|
|
133
|
+
info_plist_path = File.join(new_project_dir, temp_info)
|
|
134
|
+
if target.product_type.to_s.eql?("com.apple.product-type.application") && !File.exist?(info_plist_path)
|
|
135
|
+
raise Informative, "Missing Target #{target.name.to_s} Info.plist!!! #{info_plist_path} Modify Info.plist Error !!!"
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
if target.product_type.to_s.eql?("com.apple.product-type.application") then
|
|
140
|
+
add_swark_entitlement(entitlements_plist_path:entitlements_plist_path, bundle_id_dict:provisioning_info_array)
|
|
141
|
+
if !icloud_id.nil?
|
|
142
|
+
modify_entitlements_plist(entitlements_plist_path:entitlements_plist_path, icloud_id:icloud_id)
|
|
143
|
+
modify_info_plist_icloud(plist_file_name:info_plist_path, icloud_id:icloud_id)
|
|
144
|
+
else
|
|
145
|
+
modify_info_plist_icloud(plist_file_name:info_plist_path, icloud_id:nil)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
if !group_id.nil?
|
|
149
|
+
modify_entitlements_plist(entitlements_plist_path:entitlements_plist_path, group_id:group_id)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def modify_entitlements_plist(entitlements_plist_path:nil, group_id:nil, icloud_id:nil)
|
|
156
|
+
if File.exist?(entitlements_plist_path)
|
|
157
|
+
|
|
158
|
+
entitlements_plist_dict = Xcodeproj::Plist.read_from_path(entitlements_plist_path)
|
|
159
|
+
|
|
160
|
+
if !group_id.nil? && !entitlements_plist_dict['com.apple.security.application-groups'].nil?
|
|
161
|
+
entitlements_plist_dict['com.apple.security.application-groups'] = [group_id]
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
if !icloud_id.nil?
|
|
165
|
+
entitlements_plist_dict['com.apple.developer.icloud-container-identifiers'] = [icloud_id]
|
|
166
|
+
entitlements_plist_dict['com.apple.developer.ubiquity-container-identifiers'] = [icloud_id]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
Xcodeproj::Plist.write_to_path(entitlements_plist_dict, entitlements_plist_path)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def modify_info_plist_icloud(plist_file_name:nil, icloud_id:nil)
|
|
175
|
+
if ! File.exist? (plist_file_name)
|
|
176
|
+
raise Informative, "修改Info.list 路径错误!!! #{plist_file_name}"
|
|
177
|
+
end
|
|
178
|
+
if File.exist?(plist_file_name)
|
|
179
|
+
info_plist_dict = Xcodeproj::Plist.read_from_path(plist_file_name)
|
|
180
|
+
|
|
181
|
+
if !icloud_id.nil?
|
|
182
|
+
|
|
183
|
+
temp_value = {}
|
|
184
|
+
if !info_plist_dict['NSUbiquitousContainers'].nil? && info_plist_dict['NSUbiquitousContainers'].is_a?(Hash)
|
|
185
|
+
info_plist_dict['NSUbiquitousContainers'].each do |key, value|
|
|
186
|
+
if key.include?("iCloud.")
|
|
187
|
+
temp_value = value
|
|
188
|
+
break;
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
info_plist_dict['NSUbiquitousContainers'] = {}
|
|
193
|
+
info_plist_dict['NSUbiquitousContainers'][icloud_id] = temp_value || {}
|
|
194
|
+
else
|
|
195
|
+
info_plist_dict['NSUbiquitousContainers'] = nil
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
Xcodeproj::Plist.write_to_path(info_plist_dict, plist_file_name)
|
|
199
|
+
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def create_upload_cert_info(apple_id:nil, cert_type:nil, provisioning_info_array:nil)
|
|
204
|
+
|
|
205
|
+
cert_dir = File.join(Dir.pwd, "cert")
|
|
206
|
+
cert_json_file = File.join(cert_dir, "certs.json")
|
|
207
|
+
|
|
208
|
+
if !File.exist?(cert_dir)
|
|
209
|
+
FileUtils.mkdir_p(cert_dir)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
cert_json = []
|
|
213
|
+
begin
|
|
214
|
+
cert_json = JSON.parse(File.read(cert_json_file)) if File.exist?(cert_json_file)
|
|
215
|
+
cert_json = cert_json || []
|
|
216
|
+
rescue StandardError => e
|
|
217
|
+
cert_json = []
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
bundle_id = provisioning_info_array.select { |s| s["type"].to_s.eql?("bundle_id") }.first["bundle_id"]
|
|
221
|
+
team_id_vaule = provisioning_info_array.first["team_id"]
|
|
222
|
+
bundle_id_signing_identity = provisioning_info_array.first["signing_identity"]
|
|
223
|
+
|
|
224
|
+
account_cert_set = {}
|
|
225
|
+
select_result= cert_json.select { |x| x["account_name"].eql?(apple_id) }.first
|
|
226
|
+
if select_result.nil?
|
|
227
|
+
|
|
228
|
+
account_cert_set = {}
|
|
229
|
+
else
|
|
230
|
+
|
|
231
|
+
account_cert_set = select_result
|
|
232
|
+
cert_json.delete_if { |x| x["account_name"].eql?(apple_id) }
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
account_cert_set["account_name"] = apple_id
|
|
236
|
+
account_cert_set["team_id"] = team_id_vaule
|
|
237
|
+
account_cert_set["certs"] = account_cert_set["certs"] || []
|
|
238
|
+
|
|
239
|
+
cert_item = {}
|
|
240
|
+
cert_item_result = account_cert_set["certs"].select { |x| x["cert_id"].eql?(bundle_id_signing_identity)}.first
|
|
241
|
+
if cert_item_result.nil?
|
|
242
|
+
cert_item = {}
|
|
243
|
+
else
|
|
244
|
+
cert_item = cert_item_result
|
|
245
|
+
account_cert_set["certs"].delete_if { |x| x["cert_id"].eql?(bundle_id_signing_identity)}
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
cert_item["cert_id"] = cert_item["cert_id"] || bundle_id_signing_identity
|
|
249
|
+
cert_item["password"] = "goodcert1"
|
|
250
|
+
cert_item["cert_file"] = "#{cert_type}.p12".downcase
|
|
251
|
+
cert_item["cert_type"] = cert_type.downcase
|
|
252
|
+
cert_item["cert_provisioning_group"] = cert_item["cert_provisioning_group"] || []
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
cert_provisioning_group_item = {}
|
|
257
|
+
provisioning_group_id = [cert_type, "group", bundle_id].join("_")
|
|
258
|
+
|
|
259
|
+
provisioning_group_result = cert_item["cert_provisioning_group"].select { |x| x["provisioning_group_id"].eql?(provisioning_group_id)}.first
|
|
260
|
+
if provisioning_group_result.nil?
|
|
261
|
+
cert_provisioning_group_item = {}
|
|
262
|
+
else
|
|
263
|
+
cert_provisioning_group_item = provisioning_group_result
|
|
264
|
+
cert_item["cert_provisioning_group"].delete_if { |x| x["provisioning_group_id"].eql?(provisioning_group_id)}
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
cert_provisioning_group_item["provisioning_group_id"] = provisioning_group_id
|
|
270
|
+
cert_provisioning_group_item["provisioning_main_bundle_id"] = bundle_id
|
|
271
|
+
cert_provisioning_group_item["provisioning_items"] = []
|
|
272
|
+
|
|
273
|
+
group_id = [cert_type, "group", bundle_id].join("_")
|
|
274
|
+
provisioning_info_array.each do |provisioning_info|
|
|
275
|
+
bundle_id_temp = provisioning_info['bundle_id']
|
|
276
|
+
provisioning_id = [cert_type, bundle_id_temp].join("_")
|
|
277
|
+
|
|
278
|
+
cert_provisioning_group_item["provisioning_items"] << {
|
|
279
|
+
"bundle_id" => bundle_id_temp,
|
|
280
|
+
"provisioning_id" => provisioning_id,
|
|
281
|
+
"group_id" => group_id,
|
|
282
|
+
"provisioning_file" => provisioning_id + ".mobileprovision"
|
|
283
|
+
}
|
|
284
|
+
real_path = provisioning_info["profile_path"]
|
|
285
|
+
FileUtils.cp_r(real_path, File.join(cert_dir, provisioning_id + ".mobileprovision"))
|
|
286
|
+
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
cert_item["cert_provisioning_group"] << cert_provisioning_group_item
|
|
290
|
+
account_cert_set["certs"] << cert_item
|
|
291
|
+
cert_json << account_cert_set
|
|
292
|
+
File.open(cert_json_file, "w") do |f|
|
|
293
|
+
f.write(JSON.pretty_generate(cert_json.compact))
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
return account_cert_set
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
end
|
|
301
|
+
end
|