fastlane 2.29.0.beta.20170428010037 → 2.29.0.beta.20170429010036

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 91292cdc784ec6198bd0d2a0cbaa343ff7a62fce
4
- data.tar.gz: 4fb2fd2ae65ac1fba0c3015468609d0bc448cb13
3
+ metadata.gz: c3651fba720d62f1e3f1c4ac3a153045521c06eb
4
+ data.tar.gz: 7d86e4038f6bd0f7adb34ed11f3937eb06306e69
5
5
  SHA512:
6
- metadata.gz: 07fbb942966c1500d299723dc9c810861112141456aeb4c2b12a2f7f637e8d72b184f2fb0cb76872a5b0424671d63279675e842995354fea1913468f88d3bf66
7
- data.tar.gz: 49ac45ad4577e1e7be85705b42cb9e125f594eb854f609ad54d35926e38216adb657331147de71fc5b451ce53503bcc86b7b87d08c6592cc16e73187ff2cfe06
6
+ metadata.gz: 0be189bdc70ba539c76274d43ee7289baa549acaa17cd9706eb9c425284d4fe541c895c2ff4e363f3b85e47d18471422a378a5dbb61620488cb1fa9875ac8d54
7
+ data.tar.gz: 8f9cef4e329780ed40035fcd2420e9b77e7a4548a1393efa1076ebac0d66ac6681c8f2ed85d3ea0cab1e85ea92ddd8dc4cadb49558fd7c0c2ebb0c3ed3d3a354
@@ -37,7 +37,7 @@ module Deliver
37
37
  optional: true,
38
38
  env_name: "DELIVER_IPA_PATH",
39
39
  description: "Path to your ipa file",
40
- default_value: Dir["*.ipa"].first,
40
+ default_value: Dir["*.ipa"].sort_by { |x| File.mtime(x) }.last,
41
41
  verify_block: proc do |value|
42
42
  UI.user_error!("Could not find ipa file at path '#{File.expand_path(value)}'") unless File.exist?(value)
43
43
  UI.user_error!("'#{value}' doesn't seem to be an ipa file") unless value.end_with?(".ipa")
@@ -51,7 +51,7 @@ module Deliver
51
51
  optional: true,
52
52
  env_name: "DELIVER_PKG_PATH",
53
53
  description: "Path to your pkg file",
54
- default_value: Dir["*.pkg"].first,
54
+ default_value: Dir["*.pkg"].sort_by { |x| File.mtime(x) }.last,
55
55
  verify_block: proc do |value|
56
56
  UI.user_error!("Could not find pkg file at path '#{File.expand_path(value)}'") unless File.exist?(value)
57
57
  UI.user_error!("'#{value}' doesn't seem to be a pkg file") unless value.end_with?(".pkg")
@@ -65,7 +65,7 @@ module Fastlane
65
65
  platform = Actions.lane_context[Actions::SharedValues::PLATFORM_NAME]
66
66
 
67
67
  if platform == :ios or platform.nil?
68
- ipa_path_default = Dir["*.ipa"].last
68
+ ipa_path_default = Dir["*.ipa"].sort_by { |x| File.mtime(x) }.last
69
69
  end
70
70
 
71
71
  if platform == :android
@@ -1,4 +1,4 @@
1
1
  module Fastlane
2
- VERSION = '2.29.0.beta.20170428010037'.freeze
2
+ VERSION = '2.29.0.beta.20170429010036'.freeze
3
3
  DESCRIPTION = "The easiest way to automate beta deployments and releases for your iOS and Android apps".freeze
4
4
  end
@@ -1,36 +1,54 @@
1
1
  module FastlaneCore
2
2
  class BuildWatcher
3
- # @return The build we waited for. This method will always return a build
4
- def self.wait_for_build_processing_to_be_complete(app_id: nil, platform: nil)
5
- # First, find the train and build version we want to watch for
6
- processing_builds = Spaceship::TestFlight::Build.all_processing_builds(app_id: app_id, platform: platform)
3
+ class << self
4
+ # @return The build we waited for. This method will always return a build
5
+ def wait_for_build_processing_to_be_complete(app_id: nil, platform: nil)
6
+ # First, find the train and build version we want to watch for
7
+ watched_build = watching_build(app_id: app_id, platform: platform)
8
+ UI.crash!("Could not find a build for app: #{app_id} on platform: #{platform}") if watched_build.nil?
7
9
 
8
- watching_build = processing_builds.sort_by(&:upload_date).last # either it's still processing
9
- watching_build ||= Spaceship::TestFlight::Build.latest(app_id: app_id, platform: platform) # or we fallback to the most recent uplaod
10
+ loop do
11
+ matched_build = matching_build(watched_build: watched_build, app_id: app_id, platform: platform)
10
12
 
11
- loop do
12
- UI.message("Waiting for iTunes Connect to finish processing the new build (#{watching_build.train_version} - #{watching_build.build_version})")
13
+ report_status(build: matched_build)
13
14
 
15
+ if matched_build && matched_build.processed?
16
+ return matched_build
17
+ end
18
+
19
+ sleep 10
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def watching_build(app_id: nil, platform: nil)
26
+ processing_builds = Spaceship::TestFlight::Build.all_processing_builds(app_id: app_id, platform: platform)
27
+
28
+ watched_build = processing_builds.sort_by(&:upload_date).last
29
+ watched_build || Spaceship::TestFlight::Build.latest(app_id: app_id, platform: platform)
30
+ end
31
+
32
+ def matching_build(watched_build: nil, app_id: nil, platform: nil)
33
+ matched_builds = Spaceship::TestFlight::Build.builds_for_train(app_id: app_id, platform: platform, train_version: watched_build.train_version)
34
+ matched_builds.find { |build| build.build_version == watched_build.build_version }
35
+ end
36
+
37
+ def report_status(build: nil)
14
38
  # Due to iTunes Connect, builds disappear from the build list alltogether
15
39
  # after they finished processing. Before returning this build, we have to
16
40
  # wait for the build to appear in the build list again
17
41
  # As this method is very often used to wait for a build, and then do something
18
42
  # with it, we have to be sure that the build actually is ready
19
-
20
- matching_builds = Spaceship::TestFlight::Build.builds_for_train(app_id: app_id, platform: platform, train_version: watching_build.train_version)
21
- matching_build = matching_builds.find { |build| build.build_version == watching_build.build_version }
22
-
23
- if matching_build.nil?
43
+ if build.nil?
24
44
  UI.message("Build doesn't show up in the build list any more, waiting for it to appear again")
25
- elsif matching_build.active?
26
- UI.success("Build #{matching_build.train_version} - #{matching_build.build_version} is already being tested")
27
- return matching_build
28
- elsif matching_build.ready_to_submit? || matching_build.export_compliance_missing?
29
- UI.success("Successfully finished processing the build #{matching_build.train_version} - #{matching_build.build_version}")
30
- return matching_build
45
+ elsif build.active?
46
+ UI.success("Build #{build.train_version} - #{build.build_version} is already being tested")
47
+ elsif build.ready_to_submit? || build.export_compliance_missing?
48
+ UI.success("Successfully finished processing the build #{build.train_version} - #{build.build_version}")
49
+ else
50
+ UI.message("Waiting for iTunes Connect to finish processing the new build (#{build.train_version} - #{build.build_version})")
31
51
  end
32
-
33
- sleep 10
34
52
  end
35
53
  end
36
54
  end
@@ -118,11 +118,6 @@ module FastlaneCore
118
118
  FastlaneCore::Env.truthy?("TERM_PROGRAM_VERSION")
119
119
  end
120
120
 
121
- # Does the user use iTerm?
122
- def self.iterm?
123
- FastlaneCore::Env.truthy?("ITERM_SESSION_ID")
124
- end
125
-
126
121
  # Logs base directory
127
122
  def self.buildlog_path
128
123
  return ENV["FL_BUILDLOG_PATH"] || "~/Library/Logs"
@@ -32,7 +32,7 @@ module Pilot
32
32
  optional: true,
33
33
  env_name: "PILOT_IPA",
34
34
  description: "Path to the ipa file to upload",
35
- default_value: Dir["*.ipa"].first,
35
+ default_value: Dir["*.ipa"].sort_by { |x| File.mtime(x) }.last,
36
36
  verify_block: proc do |value|
37
37
  UI.user_error!("Could not find ipa file at path '#{value}'") unless File.exist? value
38
38
  UI.user_error!("'#{value}' doesn't seem to be an ipa file") unless value.end_with? ".ipa"
@@ -13,7 +13,7 @@ module Pilot
13
13
  tester = find_or_create_tester(email: config[:email], first_name: config[:first_name], last_name: config[:last_name])
14
14
 
15
15
  begin
16
- groups = add_tester_to_groups!(tester: tester, app: app, groups: config[:groups])
16
+ groups = Spaceship::TestFlight::Group.add_tester_to_groups!(tester: tester, app: app, groups: config[:groups])
17
17
  if tester.kind_of?(Spaceship::Tunes::Tester::Internal)
18
18
  UI.success("Successfully added tester to app #{app.name}")
19
19
  else
@@ -59,7 +59,7 @@ module Pilot
59
59
  test_flight_tester.remove_from_app!(app_id: app.apple_id)
60
60
  UI.success("Successfully removed tester, #{test_flight_tester.email}, from app: #{app.name}")
61
61
  else
62
- groups = remove_tester_from_groups!(tester: tester, app: app, groups: config[:groups])
62
+ groups = Spaceship::TestFlight::Group.remove_tester_from_groups!(tester: tester, app: app, groups: config[:groups])
63
63
  group_names = groups.map(&:name).join(", ")
64
64
  UI.success("Successfully removed tester #{tester.email} from app #{app.name} in group(s) #{group_names}")
65
65
  end
@@ -109,40 +109,6 @@ module Pilot
109
109
  raise ex
110
110
  end
111
111
 
112
- def perform_for_groups_in_app(app: nil, groups: nil, &block)
113
- if groups.nil?
114
- default_external_group = app.default_external_group
115
- if default_external_group.nil?
116
- UI.user_error!("The app #{app.name} does not have a default external group. Please make sure to pass group names to the `:groups` option.")
117
- end
118
- test_flight_groups = [default_external_group]
119
- else
120
- test_flight_groups = Spaceship::TestFlight::Group.filter_groups(app_id: app.apple_id) do |group|
121
- groups.include?(group.name)
122
- end
123
-
124
- UI.user_error!("There are no groups available matching the names passed to the `:groups` option.") if test_flight_groups.empty?
125
- end
126
-
127
- test_flight_groups.each(&block)
128
- end
129
-
130
- def add_tester_to_groups!(tester: nil, app: nil, groups: nil)
131
- if tester.kind_of?(Spaceship::Tunes::Tester::Internal)
132
- Spaceship::TestFlight::Group.internal_group(app_id: app.apple_id).add_tester!(tester)
133
- else
134
- perform_for_groups_in_app(app: app, groups: groups) { |group| group.add_tester!(tester) }
135
- end
136
- end
137
-
138
- def remove_tester_from_groups!(tester: nil, app: nil, groups: nil)
139
- if tester.kind_of?(Spaceship::Tunes::Tester::Internal)
140
- Spaceship::TestFlight::Group.internal_group(app_id: app.apple_id).remove_tester!(tester)
141
- else
142
- perform_for_groups_in_app(app: app, groups: groups) { |group| group.remove_tester!(tester) }
143
- end
144
- end
145
-
146
112
  def list_testers_by_app(app_filter)
147
113
  app = Spaceship::Application.find(app_filter)
148
114
  UI.user_error!("Couldn't find app with '#{app_filter}'") unless app
@@ -4,6 +4,18 @@ module Spaceship::TestFlight
4
4
  @client ||= Client.client_with_authorization_from(Spaceship::Tunes.client)
5
5
  end
6
6
 
7
+ ##
8
+ # Have subclasses inherit the client from their superclass
9
+ #
10
+ # Essentially, we are making a class-inheritable-accessor as described here:
11
+ # https://apidock.com/rails/v4.2.7/Class/class_attribute
12
+ def self.inherited(subclass)
13
+ this_class = self
14
+ subclass.define_singleton_method(:client) do
15
+ this_class.client
16
+ end
17
+ end
18
+
7
19
  def to_json
8
20
  raw_data.to_json
9
21
  end
@@ -72,12 +72,12 @@ module Spaceship::TestFlight
72
72
  export_compliance_missing: 'testflight.build.state.export.compliance.missing'
73
73
  }
74
74
 
75
- # Find a Build by `build_id`. Returns `nil` if can't find it.
75
+ # Find a Build by `build_id`.
76
76
  #
77
77
  # @return (Spaceship::TestFlight::Build)
78
78
  def self.find(app_id: nil, build_id: nil)
79
79
  attrs = client.get_build(app_id: app_id, build_id: build_id)
80
- self.new(attrs) if attrs
80
+ self.new(attrs)
81
81
  end
82
82
 
83
83
  def self.all(app_id: nil, platform: nil)
@@ -105,7 +105,7 @@ module Spaceship::TestFlight
105
105
  #
106
106
  # Note: this will overwrite any non-saved changes to the object
107
107
  #
108
- # @return (Spaceceship::Base::DataHash) the raw_data of the build.
108
+ # @return (Spaceship::Base::DataHash) the raw_data of the build.
109
109
  def reload
110
110
  self.raw_data = self.class.find(app_id: app_id, build_id: id).raw_data
111
111
  end
@@ -130,6 +130,10 @@ module Spaceship::TestFlight
130
130
  external_state == BUILD_STATES[:export_compliance_missing]
131
131
  end
132
132
 
133
+ def self.processed?
134
+ active? || ready_to_submit? || export_compliance_missing?
135
+ end
136
+
133
137
  # Getting builds from BuildTrains only gets a partial Build object
134
138
  # We are then requesting the full build from iTC when we need to access
135
139
  # any of the variables below, because they are not inlcuded in the partial Build objects
@@ -164,9 +168,9 @@ module Spaceship::TestFlight
164
168
  end
165
169
 
166
170
  def update_build_information!(description: nil, feedback_email: nil, whats_new: nil)
167
- test_info.description = description
168
- test_info.feedback_email = feedback_email
169
- test_info.whats_new = whats_new
171
+ test_info.description = description if description
172
+ test_info.feedback_email = feedback_email if feedback_email
173
+ test_info.whats_new = whats_new if whats_new
170
174
  save!
171
175
  end
172
176
 
@@ -1,20 +1,34 @@
1
1
  module Spaceship::TestFlight
2
2
  class Client < Spaceship::Client
3
+ ##
4
+ # Spaceship HTTP client for the testflight API.
5
+ #
6
+ # This client is solely responsible for the making HTTP requests and
7
+ # parsing their responses. Parameters should be either named parameters, or
8
+ # for large request data bodies, pass in anything that can resond to
9
+ # `to_json`.
10
+ #
11
+ # Each request method should validate the required parameters. A required parameter is one that would result in 400-range response if it is not supplied.
12
+ # Each request method should make only one request. For more high-level logic, put code in the data models.
13
+
3
14
  def self.hostname
4
15
  'https://itunesconnect.apple.com/testflight/v2/'
5
16
  end
6
17
 
18
+ ##
19
+ # @!group Build trains API
20
+ ##
21
+
7
22
  # Returns an array of all available build trains (not the builds they include)
8
- def get_build_trains(app_id: nil, platform: nil)
23
+ def get_build_trains(app_id: nil, platform: "ios")
9
24
  assert_required_params(__method__, binding)
10
- platform ||= "ios"
25
+
11
26
  response = request(:get, "providers/#{team_id}/apps/#{app_id}/platforms/#{platform}/trains")
12
27
  handle_response(response)
13
28
  end
14
29
 
15
- def get_builds_for_train(app_id: nil, platform: nil, train_version: nil)
30
+ def get_builds_for_train(app_id: nil, platform: "ios", train_version: nil)
16
31
  assert_required_params(__method__, binding)
17
- platform ||= "ios"
18
32
 
19
33
  response = request(:get, "providers/#{team_id}/apps/#{app_id}/platforms/#{platform}/trains/#{train_version}/builds")
20
34
  handle_response(response)
@@ -34,8 +48,72 @@ module Spaceship::TestFlight
34
48
  handle_response(response)
35
49
  end
36
50
 
51
+ ##
52
+ # @!group Builds API
53
+ ##
54
+
55
+ def get_build(app_id: nil, build_id: nil)
56
+ assert_required_params(__method__, binding)
57
+
58
+ response = request(:get, "providers/#{team_id}/apps/#{app_id}/builds/#{build_id}")
59
+ handle_response(response)
60
+ end
61
+
62
+ def put_build(app_id: nil, build_id: nil, build: nil)
63
+ assert_required_params(__method__, binding)
64
+
65
+ response = request(:put) do |req|
66
+ req.url "providers/#{team_id}/apps/#{app_id}/builds/#{build_id}"
67
+ req.body = build.to_json
68
+ req.headers['Content-Type'] = 'application/json'
69
+ end
70
+ handle_response(response)
71
+ end
72
+
73
+ def post_for_testflight_review(app_id: nil, build_id: nil, build: nil)
74
+ assert_required_params(__method__, binding)
75
+
76
+ response = request(:post) do |req|
77
+ req.url "providers/#{team_id}/apps/#{app_id}/builds/#{build_id}/review"
78
+ req.body = build.to_json
79
+ req.headers['Content-Type'] = 'application/json'
80
+ end
81
+ handle_response(response)
82
+ end
83
+
84
+ ##
85
+ # @!group Groups API
86
+ ##
87
+
88
+ def get_groups(app_id: nil)
89
+ assert_required_params(__method__, binding)
90
+
91
+ response = request(:get, "/testflight/v2/providers/#{team_id}/apps/#{app_id}/groups")
92
+ handle_response(response)
93
+ end
94
+
95
+ def add_group_to_build(app_id: nil, group_id: nil, build_id: nil)
96
+ assert_required_params(__method__, binding)
97
+
98
+ body = {
99
+ 'groupId' => group_id,
100
+ 'buildId' => build_id
101
+ }
102
+ response = request(:put) do |req|
103
+ req.url "providers/#{team_id}/apps/#{app_id}/groups/#{group_id}/builds/#{build_id}"
104
+ req.body = body.to_json
105
+ req.headers['Content-Type'] = 'application/json'
106
+ end
107
+ handle_response(response)
108
+ end
109
+
110
+ ##
111
+ # @!group Testers API
112
+ ##
113
+
37
114
  def post_tester(app_id: nil, tester: nil)
38
115
  assert_required_params(__method__, binding)
116
+
39
117
  url = "providers/#{team_id}/apps/#{app_id}/testers"
40
118
  response = request(:post) do |req|
41
119
  req.url url
@@ -51,6 +129,7 @@ module Spaceship::TestFlight
51
129
 
52
130
  def put_tester_to_group(app_id: nil, tester_id: nil, group_id: nil)
53
131
  assert_required_params(__method__, binding)
132
+
54
133
  # Then we can add the tester to the group that allows the app to test
55
134
  # This is easy enough, we already have all this data. We don't need any response from the previous request
56
135
  url = "providers/#{team_id}/apps/#{app_id}/groups/#{group_id}/testers/#{tester_id}"
@@ -67,6 +146,7 @@ module Spaceship::TestFlight
67
146
 
68
147
  def delete_tester_from_group(group_id: nil, tester_id: nil, app_id: nil)
69
148
  assert_required_params(__method__, binding)
149
+
70
150
  url = "providers/#{team_id}/apps/#{app_id}/groups/#{group_id}/testers/#{tester_id}"
71
151
  response = request(:delete) do |req|
72
152
  req.url url
@@ -75,53 +155,25 @@ module Spaceship::TestFlight
75
155
  handle_response(response)
76
156
  end
77
157
 
78
- def get_build(app_id: nil, build_id: nil)
79
- assert_required_params(__method__, binding)
80
- response = request(:get, "providers/#{team_id}/apps/#{app_id}/builds/#{build_id}")
81
- handle_response(response)
82
- end
158
+ ##
159
+ # @!group TestInfo
160
+ ##
83
161
 
84
- def put_build(app_id: nil, build_id: nil, build: nil)
162
+ def put_testinfo(app_id: nil, testinfo: nil)
85
163
  assert_required_params(__method__, binding)
86
- response = request(:put) do |req|
87
- req.url "providers/#{team_id}/apps/#{app_id}/builds/#{build_id}"
88
- req.body = build.to_json
89
- req.headers['Content-Type'] = 'application/json'
90
- end
91
- handle_response(response)
92
- end
93
164
 
94
- def post_for_testflight_review(app_id: nil, build_id: nil, build: nil)
95
- assert_required_params(__method__, binding)
96
- response = request(:post) do |req|
97
- req.url "providers/#{team_id}/apps/#{app_id}/builds/#{build_id}/review"
98
- req.body = build.to_json
99
- req.headers['Content-Type'] = 'application/json'
100
- end
101
- handle_response(response)
102
- end
103
-
104
- def get_groups(app_id: nil)
105
- assert_required_params(__method__, binding)
106
- response = request(:get, "/testflight/v2/providers/#{team_id}/apps/#{app_id}/groups")
107
- handle_response(response)
108
- end
109
-
110
- def add_group_to_build(app_id: nil, group_id: nil, build_id: nil)
111
- body = {
112
- 'groupId' => group_id,
113
- 'buildId' => build_id
114
- }
115
165
  response = request(:put) do |req|
116
- req.url "providers/#{team_id}/apps/#{app_id}/groups/#{group_id}/builds/#{build_id}"
117
- req.body = body.to_json
166
+ req.url "providers/#{team_id}/apps/#{app_id}/testInfo"
167
+ req.body = testinfo.to_json
118
168
  req.headers['Content-Type'] = 'application/json'
119
169
  end
120
170
  handle_response(response)
121
171
  end
122
172
 
173
+ protected
174
+
123
175
  def handle_response(response)
124
- if (200..300).cover?(response.status) && response.body.empty?
176
+ if (200...300).cover?(response.status) && (response.body.nil? || response.body.empty?)
125
177
  return
126
178
  end
127
179
 
@@ -11,16 +11,13 @@ module Spaceship::TestFlight
11
11
  'id' => :id,
12
12
  'name' => :name,
13
13
  'isInternalGroup' => :is_internal_group,
14
+ 'appAdamId' => :app_id,
14
15
  'isDefaultExternalGroup' => :is_default_external_group
15
16
  })
16
17
 
17
18
  def self.all(app_id: nil)
18
19
  groups = client.get_groups(app_id: app_id)
19
- groups.map do |g|
20
- current_element = self.new(g)
21
- current_element.app_id = app_id
22
- current_element
23
- end
20
+ groups.map { |g| self.new(g) }
24
21
  end
25
22
 
26
23
  def self.find(app_id: nil, group_name: nil)
@@ -59,6 +56,22 @@ module Spaceship::TestFlight
59
56
  client.delete_tester_from_group(group_id: self.id, tester_id: tester.tester_id, app_id: self.app_id)
60
57
  end
61
58
 
59
+ def self.add_tester_to_groups!(tester: nil, app: nil, groups: nil)
60
+ if tester.kind_of?(Spaceship::Tunes::Tester::Internal)
61
+ self.internal_group(app_id: app.apple_id).add_tester!(tester)
62
+ else
63
+ self.perform_for_groups_in_app(app: app, groups: groups) { |group| group.add_tester!(tester) }
64
+ end
65
+ end
66
+
67
+ def self.remove_tester_from_groups!(tester: nil, app: nil, groups: nil)
68
+ if tester.kind_of?(Spaceship::Tunes::Tester::Internal)
69
+ self.internal_group(app_id: app.apple_id).remove_tester!(tester)
70
+ else
71
+ self.perform_for_groups_in_app(app: app, groups: groups) { |group| group.remove_tester!(tester) }
72
+ end
73
+ end
74
+
62
75
  def default_external_group?
63
76
  is_default_external_group
64
77
  end
@@ -66,5 +79,23 @@ module Spaceship::TestFlight
66
79
  def internal_group?
67
80
  is_internal_group
68
81
  end
82
+
83
+ def self.perform_for_groups_in_app(app: nil, groups: nil, &block)
84
+ if groups.nil?
85
+ default_external_group = app.default_external_group
86
+ if default_external_group.nil?
87
+ UI.user_error!("The app #{app.name} does not have a default external group. Please make sure to pass group names to the `:groups` option.")
88
+ end
89
+ test_flight_groups = [default_external_group]
90
+ else
91
+ test_flight_groups = self.filter_groups(app_id: app.apple_id) do |group|
92
+ groups.include?(group.name)
93
+ end
94
+
95
+ UI.user_error!("There are no groups available matching the names passed to the `:groups` option.") if test_flight_groups.empty?
96
+ end
97
+
98
+ test_flight_groups.each(&block)
99
+ end
69
100
  end
70
101
  end
@@ -30,5 +30,9 @@ module Spaceship::TestFlight
30
30
  def whats_new=(value)
31
31
  raw_data.each { |locale| locale['whatsNew'] = value }
32
32
  end
33
+
34
+ def deep_copy
35
+ TestInfo.new(raw_data.map(&:dup))
36
+ end
33
37
  end
34
38
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.29.0.beta.20170428010037
4
+ version: 2.29.0.beta.20170429010036
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felix Krause
@@ -15,7 +15,7 @@ authors:
15
15
  autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
- date: 2017-04-28 00:00:00.000000000 Z
18
+ date: 2017-04-29 00:00:00.000000000 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: slack-notifier
@@ -743,6 +743,20 @@ dependencies:
743
743
  - - "~>"
744
744
  - !ruby/object:Gem::Version
745
745
  version: 0.8.1
746
+ - !ruby/object:Gem::Dependency
747
+ name: sinatra
748
+ requirement: !ruby/object:Gem::Requirement
749
+ requirements:
750
+ - - "~>"
751
+ - !ruby/object:Gem::Version
752
+ version: 1.4.8
753
+ type: :development
754
+ prerelease: false
755
+ version_requirements: !ruby/object:Gem::Requirement
756
+ requirements:
757
+ - - "~>"
758
+ - !ruby/object:Gem::Version
759
+ version: 1.4.8
746
760
  description: The easiest way to automate beta deployments and releases for your iOS
747
761
  and Android apps
748
762
  email:
@@ -1342,23 +1356,23 @@ metadata:
1342
1356
  post_install_message:
1343
1357
  rdoc_options: []
1344
1358
  require_paths:
1345
- - frameit/lib
1346
- - deliver/lib
1347
- - spaceship/lib
1348
- - supply/lib
1349
- - screengrab/lib
1350
- - cert/lib
1351
- - pilot/lib
1352
- - sigh/lib
1359
+ - fastlane/lib
1353
1360
  - scan/lib
1354
- - credentials_manager/lib
1361
+ - gym/lib
1362
+ - screengrab/lib
1355
1363
  - snapshot/lib
1364
+ - supply/lib
1356
1365
  - match/lib
1357
1366
  - pem/lib
1358
- - fastlane/lib
1367
+ - spaceship/lib
1359
1368
  - produce/lib
1360
- - gym/lib
1369
+ - sigh/lib
1361
1370
  - fastlane_core/lib
1371
+ - frameit/lib
1372
+ - pilot/lib
1373
+ - deliver/lib
1374
+ - credentials_manager/lib
1375
+ - cert/lib
1362
1376
  required_ruby_version: !ruby/object:Gem::Requirement
1363
1377
  requirements:
1364
1378
  - - ">="