fastlane 2.13.0 → 2.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/credentials_manager/lib/credentials_manager.rb +1 -1
- data/fastlane/lib/fastlane/actions/ipa.rb +2 -1
- data/fastlane/lib/fastlane/actions/mailgun.rb +15 -2
- data/fastlane/lib/fastlane/actions/scan.rb +14 -0
- data/fastlane/lib/fastlane/documentation/docs_generator.rb +24 -1
- data/fastlane/lib/fastlane/environment_printer.rb +2 -1
- data/fastlane/lib/fastlane/fast_file.rb +4 -4
- data/fastlane/lib/fastlane/version.rb +1 -1
- data/fastlane_core/lib/fastlane_core.rb +1 -1
- data/fastlane_core/lib/fastlane_core/configuration/config_item.rb +44 -2
- data/fastlane_core/lib/fastlane_core/device_manager.rb +15 -0
- data/fastlane_core/lib/fastlane_core/helper.rb +1 -1
- data/fastlane_core/lib/fastlane_core/ui/disable_colors.rb +4 -4
- data/frameit/lib/frameit/config_parser.rb +8 -13
- data/frameit/lib/frameit/editor.rb +3 -2
- data/gym/lib/gym/options.rb +4 -2
- data/match/README.md +2 -2
- data/match/lib/match.rb +5 -19
- data/match/lib/match/generator.rb +8 -1
- data/match/lib/match/git_helper.rb +3 -1
- data/match/lib/match/nuke.rb +18 -14
- data/match/lib/match/options.rb +13 -2
- data/match/lib/match/runner.rb +20 -8
- data/match/lib/match/table_printer.rb +5 -4
- data/match/lib/match/utils.rb +12 -8
- data/scan/lib/scan/options.rb +24 -1
- data/scan/lib/scan/runner.rb +12 -11
- data/scan/lib/scan/test_command_generator.rb +10 -2
- data/sigh/lib/sigh/download_all.rb +1 -1
- data/sigh/lib/sigh/runner.rb +2 -2
- data/snapshot/lib/snapshot/options.rb +2 -1
- data/snapshot/lib/snapshot/runner.rb +12 -12
- data/spaceship/lib/spaceship.rb +1 -0
- data/spaceship/lib/spaceship/base.rb +27 -4
- data/spaceship/lib/spaceship/client.rb +3 -2
- data/spaceship/lib/spaceship/du/du_client.rb +26 -16
- data/spaceship/lib/spaceship/portal/app.rb +1 -1
- data/spaceship/lib/spaceship/portal/person.rb +53 -0
- data/spaceship/lib/spaceship/portal/persons.rb +49 -0
- data/spaceship/lib/spaceship/portal/portal.rb +2 -0
- data/spaceship/lib/spaceship/portal/portal_client.rb +69 -10
- data/spaceship/lib/spaceship/portal/provisioning_profile.rb +29 -1
- data/spaceship/lib/spaceship/tunes/application.rb +11 -1
- data/spaceship/lib/spaceship/tunes/iap.rb +113 -0
- data/spaceship/lib/spaceship/tunes/iap_detail.rb +185 -0
- data/spaceship/lib/spaceship/tunes/iap_families.rb +62 -0
- data/spaceship/lib/spaceship/tunes/iap_family_details.rb +59 -0
- data/spaceship/lib/spaceship/tunes/iap_family_list.rb +33 -0
- data/spaceship/lib/spaceship/tunes/iap_list.rb +72 -0
- data/spaceship/lib/spaceship/tunes/iap_status.rb +48 -0
- data/spaceship/lib/spaceship/tunes/iap_type.rb +45 -0
- data/spaceship/lib/spaceship/tunes/tunes.rb +1 -0
- data/spaceship/lib/spaceship/tunes/tunes_client.rb +163 -1
- metadata +21 -5
@@ -0,0 +1,185 @@
|
|
1
|
+
module Spaceship
|
2
|
+
module Tunes
|
3
|
+
class IAPDetail < TunesBase
|
4
|
+
# @return (Spaceship::Tunes::Application) A reference to the application
|
5
|
+
attr_accessor :application
|
6
|
+
|
7
|
+
# @return (Integer) the IAP id
|
8
|
+
attr_accessor :purchase_id
|
9
|
+
|
10
|
+
# @return (Bool) if it is a news subscription
|
11
|
+
attr_accessor :is_news_subscription
|
12
|
+
|
13
|
+
# @return (String) the IAP Referencename
|
14
|
+
attr_accessor :reference_name
|
15
|
+
|
16
|
+
# @return (String) the IAP Product-Id
|
17
|
+
attr_accessor :product_id
|
18
|
+
|
19
|
+
# @return (String) free trial period
|
20
|
+
attr_accessor :subscription_free_trial
|
21
|
+
|
22
|
+
# @return (String) subscription duration
|
23
|
+
attr_accessor :subscription_duration
|
24
|
+
|
25
|
+
# @return (Bool) Cleared for sale flag
|
26
|
+
attr_accessor :cleared_for_sale
|
27
|
+
|
28
|
+
attr_accessor :review_screenshot
|
29
|
+
|
30
|
+
# @return (String) the notes for the review team
|
31
|
+
attr_accessor :review_notes
|
32
|
+
|
33
|
+
# @return (Hash) subscription pricing target
|
34
|
+
attr_accessor :subscription_price_target
|
35
|
+
|
36
|
+
attr_mapping({
|
37
|
+
'adamId' => :purchase_id,
|
38
|
+
'referenceName.value' => :reference_name,
|
39
|
+
'productId.value' => :product_id,
|
40
|
+
'isNewsSubscription' => :is_news_subscription,
|
41
|
+
'pricingDurationType.value' => :subscription_duration,
|
42
|
+
'freeTrialDurationType.value' => :subscription_free_trial,
|
43
|
+
'clearedForSale.value' => :cleared_for_sale
|
44
|
+
})
|
45
|
+
|
46
|
+
class << self
|
47
|
+
def factory(attrs)
|
48
|
+
return self.new(attrs)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return (Hash) Hash of languages
|
53
|
+
# @example: {
|
54
|
+
# 'de-DE': {
|
55
|
+
# name: "Name shown in AppStore",
|
56
|
+
# description: "Description of the In app Purchase"
|
57
|
+
#
|
58
|
+
# }
|
59
|
+
# }
|
60
|
+
def versions
|
61
|
+
parsed_versions = {}
|
62
|
+
raw_versions = raw_data["versions"].first["details"]["value"]
|
63
|
+
raw_versions.each do |localized_version|
|
64
|
+
language = localized_version["value"]["localeCode"]
|
65
|
+
parsed_versions[language.to_sym] = {
|
66
|
+
name: localized_version["value"]["name"]["value"],
|
67
|
+
description: localized_version["value"]["description"]["value"]
|
68
|
+
}
|
69
|
+
end
|
70
|
+
return parsed_versions
|
71
|
+
end
|
72
|
+
|
73
|
+
# @return (Array) pricing intervals
|
74
|
+
# @example:
|
75
|
+
# [
|
76
|
+
# {
|
77
|
+
# country: "WW",
|
78
|
+
# begin_date: nil,
|
79
|
+
# end_date: nil,
|
80
|
+
# tier: 1
|
81
|
+
# }
|
82
|
+
# ]
|
83
|
+
def pricing_intervals
|
84
|
+
parsed_intervals = []
|
85
|
+
raw_data["pricingIntervals"].each do |interval|
|
86
|
+
parsed_intervals << {
|
87
|
+
tier: interval["value"]["tierStem"].to_i,
|
88
|
+
begin_date: interval["value"]["priceTierEffectiveDate"],
|
89
|
+
end_date: interval["value"]["priceTierEndDate"],
|
90
|
+
grandfathered: interval["value"]["grandfathered"],
|
91
|
+
country: interval["value"]["country"]
|
92
|
+
}
|
93
|
+
end
|
94
|
+
return parsed_intervals
|
95
|
+
end
|
96
|
+
|
97
|
+
# @return (String) Human Readable type of the purchase
|
98
|
+
def type
|
99
|
+
Tunes::IAPType.get_from_string(raw_data["addOnType"])
|
100
|
+
end
|
101
|
+
|
102
|
+
# @return (String) Human Readable status of the purchase
|
103
|
+
def status
|
104
|
+
Tunes::IAPStatus.get_from_string(raw_data["versions"].first["status"])
|
105
|
+
end
|
106
|
+
|
107
|
+
# Saves the current In-App-Purchase
|
108
|
+
def save!
|
109
|
+
# Transform localization versions back to original format.
|
110
|
+
versions_array = []
|
111
|
+
versions.each do |language, value|
|
112
|
+
versions_array << {
|
113
|
+
value: {
|
114
|
+
description: { value: value[:description] },
|
115
|
+
name: { value: value[:name] },
|
116
|
+
localeCode: language.to_s
|
117
|
+
}
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
raw_data.set(["versions"], [{ reviewNotes: @review_notes, details: { value: versions_array } }])
|
122
|
+
|
123
|
+
# transform pricingDetails
|
124
|
+
intervals_array = []
|
125
|
+
pricing_intervals.each do |interval|
|
126
|
+
intervals_array << {
|
127
|
+
value: {
|
128
|
+
tierStem: interval[:tier],
|
129
|
+
priceTierEffectiveDate: interval[:begin_date],
|
130
|
+
priceTierEndDate: interval[:end_date],
|
131
|
+
country: interval[:country] || "WW",
|
132
|
+
grandfathered: interval[:grandfathered]
|
133
|
+
}
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
if subscription_price_target
|
138
|
+
intervals_array = []
|
139
|
+
pricing_calculator = client.iap_subscription_pricing_target(app_id: application.apple_id, purchase_id: purchase_id, currency: subscription_price_target[:currency], tier: subscription_price_target[:tier])
|
140
|
+
pricing_calculator.each do |language_code, value|
|
141
|
+
intervals_array << {
|
142
|
+
value: {
|
143
|
+
tierStem: value["tierStem"],
|
144
|
+
priceTierEffectiveDate: value["priceTierEffectiveDate"],
|
145
|
+
priceTierEndDate: value["priceTierEndDate"],
|
146
|
+
country: language_code,
|
147
|
+
grandfathered: { value: "FUTURE_NONE" }
|
148
|
+
}
|
149
|
+
}
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
raw_data.set(["pricingIntervals"], intervals_array)
|
155
|
+
|
156
|
+
if @review_screenshot
|
157
|
+
# Upload Screenshot
|
158
|
+
upload_file = UploadFile.from_path @review_screenshot
|
159
|
+
screenshot_data = client.upload_purchase_review_screenshot(application.apple_id, upload_file)
|
160
|
+
new_screenshot = {
|
161
|
+
"value" => {
|
162
|
+
"assetToken" => screenshot_data["token"],
|
163
|
+
"sortOrder" => 0,
|
164
|
+
"type" => "SortedScreenShot",
|
165
|
+
"originalFileName" => upload_file.file_name,
|
166
|
+
"size" => screenshot_data["length"],
|
167
|
+
"height" => screenshot_data["height"],
|
168
|
+
"width" => screenshot_data["width"],
|
169
|
+
"checksum" => screenshot_data["md5"]
|
170
|
+
}
|
171
|
+
}
|
172
|
+
|
173
|
+
raw_data["versions"][0]["reviewScreenshot"] = new_screenshot
|
174
|
+
end
|
175
|
+
# Update the Purchase
|
176
|
+
client.update_iap!(app_id: application.apple_id, purchase_id: self.purchase_id, data: raw_data)
|
177
|
+
end
|
178
|
+
|
179
|
+
# Deletes In-App-Purchase
|
180
|
+
def delete!
|
181
|
+
client.delete_iap!(app_id: application.apple_id, purchase_id: self.purchase_id)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
|
2
|
+
module Spaceship
|
3
|
+
module Tunes
|
4
|
+
class IAPFamilies < TunesBase
|
5
|
+
# @return (Spaceship::Tunes::Application) A reference to the application
|
6
|
+
attr_accessor :application
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def factory(attrs)
|
10
|
+
return self.new(attrs)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Create a new Purchase Family
|
15
|
+
# a freshly created family has to have atleast one product.
|
16
|
+
# the product will be created, and versions/pricing_intervals and so on
|
17
|
+
# should be set by subsequent edit.
|
18
|
+
# @param name (String) Familyname
|
19
|
+
# @param product_id (String) New Product's id
|
20
|
+
# @param reference_name (String) Reference name of the new product
|
21
|
+
# @param versions (Hash) Localized Familie names
|
22
|
+
# @example
|
23
|
+
# versions: {
|
24
|
+
# 'de-DE': {
|
25
|
+
# subscription_name: "Subname German",
|
26
|
+
# name: 'App Name German',
|
27
|
+
# },
|
28
|
+
# 'da': {
|
29
|
+
# subscription_name: "Subname DA",
|
30
|
+
# name: 'App Name DA',
|
31
|
+
# }
|
32
|
+
# }
|
33
|
+
|
34
|
+
def create!(name: nil, product_id: nil, reference_name: nil, versions: {})
|
35
|
+
versions_array = []
|
36
|
+
versions.each do |language_code, value|
|
37
|
+
versions_array << {
|
38
|
+
value: {
|
39
|
+
subscriptionName: { value: value[:subscription_name] },
|
40
|
+
name: { value: value[:name] },
|
41
|
+
localeCode: { value: language_code.to_s }
|
42
|
+
}
|
43
|
+
}
|
44
|
+
end
|
45
|
+
client.create_iap_family(app_id: self.application.apple_id, name: name, product_id: product_id, reference_name: reference_name, versions: versions_array)
|
46
|
+
end
|
47
|
+
|
48
|
+
# returns a list of all families
|
49
|
+
def all
|
50
|
+
r = client.iap_families(app_id: self.application.apple_id)
|
51
|
+
return_families = []
|
52
|
+
r.each do |family|
|
53
|
+
attrs = family
|
54
|
+
attrs[:application] = self.application
|
55
|
+
loaded_family = Tunes::IAPFamilyList.factory(attrs)
|
56
|
+
return_families << loaded_family
|
57
|
+
end
|
58
|
+
return_families
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
|
2
|
+
module Spaceship
|
3
|
+
module Tunes
|
4
|
+
class IAPFamilyDetails < TunesBase
|
5
|
+
# @return (Spaceship::Tunes::Application) A reference to the application
|
6
|
+
attr_accessor :application
|
7
|
+
|
8
|
+
# @return (String) the family name
|
9
|
+
attr_accessor :name
|
10
|
+
|
11
|
+
# @return (Intger) the Family Id
|
12
|
+
attr_accessor :family_id
|
13
|
+
|
14
|
+
attr_mapping({
|
15
|
+
'id' => :family_id,
|
16
|
+
'name.value' => :name
|
17
|
+
})
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def factory(attrs)
|
21
|
+
return self.new(attrs)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return (Hash) localized names
|
26
|
+
def versions
|
27
|
+
parsed_versions = {}
|
28
|
+
raw_versions = raw_data["details"]["value"]
|
29
|
+
raw_versions.each do |version|
|
30
|
+
language = version["value"]["localeCode"]["value"]
|
31
|
+
parsed_versions[language.to_sym] = {
|
32
|
+
subscription_name: version["value"]["subscriptionName"]["value"],
|
33
|
+
name: version["value"]["name"]["value"]
|
34
|
+
}
|
35
|
+
end
|
36
|
+
return parsed_versions
|
37
|
+
end
|
38
|
+
|
39
|
+
# modify existing family
|
40
|
+
def save!
|
41
|
+
# Transform localization versions back to original format.
|
42
|
+
versions_array = []
|
43
|
+
versions.each do |language_code, value|
|
44
|
+
versions_array << {
|
45
|
+
value: {
|
46
|
+
subscriptionName: { value: value[:subscription_name] },
|
47
|
+
name: { value: value[:name] },
|
48
|
+
localeCode: { value: language_code.to_s }
|
49
|
+
}
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
raw_data.set(["details"], { value: versions_array })
|
54
|
+
|
55
|
+
client.update_iap_family!(app_id: application.apple_id, family_id: self.family_id, data: raw_data)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
module Spaceship
|
3
|
+
module Tunes
|
4
|
+
class IAPFamilyList < TunesBase
|
5
|
+
# @return (Spaceship::Tunes::Application) A reference to the application
|
6
|
+
attr_accessor :application
|
7
|
+
|
8
|
+
# @return (String) the family name
|
9
|
+
attr_accessor :name
|
10
|
+
|
11
|
+
# @return (Intger) the Family Id
|
12
|
+
attr_accessor :family_id
|
13
|
+
|
14
|
+
attr_mapping({
|
15
|
+
'id' => :family_id,
|
16
|
+
'name.value' => :name
|
17
|
+
})
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def factory(attrs)
|
21
|
+
return self.new(attrs)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# return a editable family object
|
26
|
+
def edit
|
27
|
+
attrs = client.load_iap_family(app_id: application.apple_id, family_id: self.family_id)
|
28
|
+
attrs[:application] = application
|
29
|
+
Tunes::IAPFamilyDetails.factory(attrs)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
|
2
|
+
module Spaceship
|
3
|
+
module Tunes
|
4
|
+
class IAPList < TunesBase
|
5
|
+
# @return (Spaceship::Tunes::Application) A reference to the application
|
6
|
+
attr_accessor :application
|
7
|
+
|
8
|
+
# @return (String) the IAP Referencename
|
9
|
+
attr_accessor :reference_name
|
10
|
+
|
11
|
+
# @return (String) the IAP Product-Id
|
12
|
+
attr_accessor :product_id
|
13
|
+
|
14
|
+
# @return (String) Family Reference Name
|
15
|
+
attr_accessor :family_reference_name
|
16
|
+
|
17
|
+
attr_accessor :duration_days
|
18
|
+
attr_accessor :versions
|
19
|
+
attr_accessor :purple_apple_id
|
20
|
+
attr_accessor :last_modfied_date
|
21
|
+
attr_accessor :is_news_subscription
|
22
|
+
attr_accessor :number_of_codes
|
23
|
+
attr_accessor :maximum_number_of_codes
|
24
|
+
attr_accessor :app_maximum_number_of_codes
|
25
|
+
attr_accessor :is_editable
|
26
|
+
attr_accessor :is_required
|
27
|
+
attr_accessor :can_delete_addon
|
28
|
+
|
29
|
+
attr_mapping({
|
30
|
+
'adamId' => :purchase_id,
|
31
|
+
'referenceName' => :reference_name,
|
32
|
+
'familyReferenceName' => :family_reference_name,
|
33
|
+
'vendorId' => :product_id,
|
34
|
+
'durationDays' => :duration_days,
|
35
|
+
'versions' => :versions,
|
36
|
+
'purpleSoftwareAdamIds' => :purple_apple_id,
|
37
|
+
'lastModifiedDate' => :last_modfied_date,
|
38
|
+
'isNewsSubscription' => :is_news_subscription,
|
39
|
+
'numberOfCodes' => :number_of_codes,
|
40
|
+
'maximumNumberOfCodes' => :maximum_number_of_codes,
|
41
|
+
'appMaximumNumberOfCodes' => :app_maximum_number_of_codes,
|
42
|
+
'isEditable' => :is_editable,
|
43
|
+
'isRequired' => :is_required,
|
44
|
+
'canDeleteAddOn' => :can_delete_addon
|
45
|
+
})
|
46
|
+
|
47
|
+
class << self
|
48
|
+
def factory(attrs)
|
49
|
+
return self.new(attrs)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def type
|
54
|
+
Tunes::IAPType.get_from_string(raw_data["addOnType"])
|
55
|
+
end
|
56
|
+
|
57
|
+
def status
|
58
|
+
Tunes::IAPStatus.get_from_string(raw_data["iTunesConnectStatus"])
|
59
|
+
end
|
60
|
+
|
61
|
+
def edit
|
62
|
+
attrs = client.load_iap(app_id: application.apple_id, purchase_id: self.purchase_id)
|
63
|
+
attrs[:application] = application
|
64
|
+
Tunes::IAPDetail.factory(attrs)
|
65
|
+
end
|
66
|
+
|
67
|
+
def delete!
|
68
|
+
client.delete_iap!(app_id: application.apple_id, purchase_id: self.purchase_id)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Spaceship
|
2
|
+
module Tunes
|
3
|
+
# Defines the different states of an in-app purchase
|
4
|
+
#
|
5
|
+
# As specified by Apple: https://developer.apple.com/library/content/documentation/LanguagesUtilities/Conceptual/iTunesConnectInAppPurchase_Guide/Chapters/WorkingWithYourProductsStatus.html
|
6
|
+
module IAPStatus
|
7
|
+
# IAP created, but missing screenshot/metadata
|
8
|
+
MISSING_METADATA = "Missing Metadata"
|
9
|
+
|
10
|
+
# You can edit the metadata, change screenshot and more. Need to submit.
|
11
|
+
READY_TO_SUBMIT = "Ready to Submit"
|
12
|
+
|
13
|
+
# Waiting for Apple's Review
|
14
|
+
WAITING_FOR_REVIEW = "Waiting For Review"
|
15
|
+
|
16
|
+
# Currently in Review
|
17
|
+
IN_REVIEW = "In Review"
|
18
|
+
|
19
|
+
# Approved (and currently available)
|
20
|
+
APPROVED = "Approved"
|
21
|
+
|
22
|
+
# Developer deleted
|
23
|
+
DELETED = "Deleted"
|
24
|
+
|
25
|
+
# In-app purchase rejected for whatever reason
|
26
|
+
REJECTED = "Rejected"
|
27
|
+
|
28
|
+
# Get the iap status matching based on a string (given by iTunes Connect)
|
29
|
+
def self.get_from_string(text)
|
30
|
+
mapping = {
|
31
|
+
'missingMetadata' => MISSING_METADATA,
|
32
|
+
'readyToSubmit' => READY_TO_SUBMIT,
|
33
|
+
'waitingForReview' => WAITING_FOR_REVIEW,
|
34
|
+
'inReview' => IN_REVIEW,
|
35
|
+
'readyForSale' => APPROVED,
|
36
|
+
'deleted' => DELETED,
|
37
|
+
'rejected' => REJECTED
|
38
|
+
}
|
39
|
+
|
40
|
+
mapping.each do |itc_status, readable_status|
|
41
|
+
return readable_status if itc_status == text
|
42
|
+
end
|
43
|
+
|
44
|
+
return nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|