pindo 4.6.9 → 4.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|