deliver 0.8.0 → 0.8.1

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: 9e8b78f5d2bd18344f491a53d2d2097137ca6ea5
4
- data.tar.gz: a30ebf40d66983d4b1aec2a9050ffe3c33a29223
3
+ metadata.gz: 8156d24d9ac47337d5f3d1647c4d79209c4dbd4b
4
+ data.tar.gz: 6c4aa1ceee21c1c4585ca1cc4f1192d023d1d760
5
5
  SHA512:
6
- metadata.gz: e5ef73826eb558bd5c47b7ce1d63af2b1f94a980a72769af7b1145444f403edd0fc1e7dbdd5b0e84ccd88148fd5bf367f7ab5824cd82bbd56591f59f2f9c1844
7
- data.tar.gz: 5f86527190c1e4457203d97652f4b301716e6b435eb746c20333c866dabc8f94f2c070152903c207d4e52130179eba8fce82b0c945511bfa34fbf8ba63857c23
6
+ metadata.gz: bb21bd1e99341f908f37c8f2380a2d84e86f18f1674348eb731411361446734b22b6acf1962d6636075bc255cc89cb7e103c0f997f65a6009985e25aa6ecbcea
7
+ data.tar.gz: 0d5fef2525aee2338ac2defad6fa10839f8e079d0f705f08082c626436659b263058dd37545dc58cb3a7d7ffe30e527a37ba759dd418a1c0963e1660c74188e8
data/README.md CHANGED
@@ -168,6 +168,10 @@ To let the computer create the screenshots for you, checkout [this section of th
168
168
 
169
169
  If you want to have the screenshots inside a device frame, with a background and a fancy label on top, you can use [Sketch to App Store](http://sketchtoappstore.com/).
170
170
 
171
+ #### Update App description, keywords and more
172
+
173
+ If you used `deliver init`, take a look at the `./metadata/` folder, which contains your app description, keywords and app title. If you change the content of the files, it will get updated when you run `deliver`.
174
+
171
175
  #### Upload a new ipa file with a changelog to the App Store
172
176
  This will submit a new update to Apple
173
177
  ```ruby
@@ -185,6 +189,8 @@ If you wish to skip automated submission to review you can provide `--skip-deplo
185
189
 
186
190
  The changelog is only used for App Store submission, not for TestFlight builds.
187
191
 
192
+ It is recommended to use `deliver` in combination with [fastlane](https://github.com/KrauseFx/fastlane) to build the app using `xcodebuild`.
193
+
188
194
  #### Upload a new ipa for TestFlight beta testers
189
195
 
190
196
  In order to upload an `.ipa` file for Apple TestFlight you need to specify `beta_ipa` path in your `Deliverfile`
@@ -218,24 +224,6 @@ error do |information|
218
224
  end
219
225
  ```
220
226
 
221
-
222
- #### Set a default language if you only maintain one language
223
- ```ruby
224
- default_language "en-US"
225
- version "1.2"
226
-
227
- title "Only English Title"
228
- ```
229
- If you do not pass an ipa file, you have to specify the app version you want to edit.
230
-
231
- #### Update the app's keywords
232
- ```ruby
233
- default_language "de-DE"
234
- version "1.2"
235
-
236
- keywords ["keyword1", "something", "else"]
237
- ```
238
-
239
227
  #### Read content from somewhere external (file, web service, ...)
240
228
  ```ruby
241
229
  description({
@@ -275,8 +263,9 @@ All available commands with a short description can be found in [Deliverfile.md]
275
263
  - Ask the script user for a changelog
276
264
  - Deploy a new version just by starting a Jenkins job
277
265
  - Post the deployment status on Slack
278
- - Upload the latest screenshots on your server
279
- - Many more things, be creative and let me know :)
266
+ - Upload the latest screenshots to your server
267
+
268
+ For further actions, check out [fastlane](https://github.com/KrauseFx/fastlane)
280
269
 
281
270
  #### Use the exposed Ruby classes
282
271
  Some examples:
@@ -299,7 +288,7 @@ This project is well documented, check it out on [Rubydoc](http://www.rubydoc.in
299
288
 
300
289
  # Credentials
301
290
 
302
- A detailed description about your credentials is available on a [seperate repo](https://github.com/KrauseFx/CredentialsManager).
291
+ A detailed description about your credentials is available on a [separate repo](https://github.com/KrauseFx/CredentialsManager).
303
292
 
304
293
 
305
294
  # Can I trust `deliver`?
@@ -18,7 +18,7 @@ class FastlaneApplication
18
18
  program :help, 'GitHub', 'https://github.com/krausefx/deliver'
19
19
  program :help_formatter, :compact
20
20
 
21
- global_option '--force', 'Runs a deployment without verifying any information (PDF file). This can be used for build servers.'
21
+ global_option '-f', '--force', 'Runs a deployment without verifying any information (PDF file). This can be used for build servers.'
22
22
  global_option '--beta', 'Upload a beta build to iTunes Connect. This uses the `beta_ipa` block.'
23
23
  global_option '--skip-deploy', 'Skips submission of the build on iTunes Connect. This will only upload the ipa and/or metadata.'
24
24
 
@@ -46,9 +46,12 @@ class FastlaneApplication
46
46
 
47
47
  command :init do |c|
48
48
  c.syntax = 'deliver init'
49
+ c.option '-u', '--username String', String, 'Your Apple ID'
49
50
  c.description = "Creates a new Deliverfile in the current directory"
50
51
 
51
52
  c.action do |args, options|
53
+ set_username(options.username)
54
+
52
55
  path = (Deliver::Helper.fastlane_enabled?? './fastlane' : '.')
53
56
  Deliver::DeliverfileCreator.create(path)
54
57
  end
@@ -1,66 +1,30 @@
1
- # For more information visit the GitHub documentation: https://github.com/krausefx/deliver
2
- # Everything next to a # is a comment and will be ignored
1
+ ###################### App Metadata ######################
2
+ # Update the app description and metadata in the ./metadata folder
3
3
 
4
- # hide_transporter_output # remove the '#' in the beginning of the line, to hide the output while uploading
4
+ ###################### Screenshots ######################
5
+ # Store all screenshots in the ./screenshots folder separated
6
+ # by language. If you use snapshot, this happens automatically
5
7
 
8
+ ###################### IPA File ######################
9
+ # This part is only relevant, if you want to submit a new binary
10
+ # If you don't use fastlane (https://github.com/KrauseFx/fastlane)
11
+ # you can set the path to your ipa file using:
12
+ # ipa "./app.ipa"
6
13
 
7
- ########################################
8
- # App Metadata
9
- ########################################
14
+ # to provide an ipa file for TestFlight distribution, use beta_ipa
15
+ # beta_ipa "./app.ipa"
10
16
 
17
+ # The version of your app - remove this if you provide an ipa file
18
+ version "[[APP_VERSION]]"
11
19
 
12
- # This folder has to include one folder for each language
13
- # More information about automatic screenshot upload:
14
- # https://github.com/KrauseFx/deliver#upload-screenshots-to-itunes-connect
15
- screenshots_path "./screenshots/"
20
+ ###################### More Options ######################
21
+ # If you want to have even more control, check out the documentation
22
+ # https://github.com/KrauseFx/deliver/blob/master/Deliverfile.md
16
23
 
17
24
 
18
- # More information about all available options: https://github.com/KrauseFx/deliver/blob/master/Deliverfile.md
25
+ ###################### Automatically generated ######################
26
+ # Feel free to remove the following lines if you use fastlane
19
27
 
20
- # Both lines are optional if you want to remove them:
21
- app_identifier "[[APP_IDENTIFIER]]"
28
+ app_identifier "[[APP_IDENTIFIER]]" # The bundle identifier of your app
22
29
  apple_id "[[APPLE_ID]]" # This is NOT your Apple login ID, but the App ID of your app
23
- email "[[EMAIL]]" # the login email address
24
-
25
- # version '1.2' # you can pass this if you want to verify the version number with the ipa file
26
-
27
- # To update the app's description, changelog, etc. or to avoid updating it using deliver, edit the files in ./metadata/[language]
28
-
29
- ########################################
30
- # Building and Testing
31
- ########################################
32
-
33
- # Dynamic generation of the ipa file
34
- # I'm using Shenzhen by Mattt, but you can use any build tool you want
35
- # Remove the whole block if you do not want to upload an ipa file
36
- ipa do
37
- # Add any code you want, like incrementing the build
38
- # number or changing the app identifier
39
-
40
- # Attention: When you return a valid ipa file, this file will get uploaded and released
41
- # If you only want to upload app metadata, remove the complete ipa block.
42
-
43
- # system("ipa build --verbose") # build your project using Shenzhen
44
-
45
- # ENV["IPA_OUTPUT_PATH"] # if you're using fastlane uncomment this and remove the line below
46
-
47
- "./[[APP_NAME]].ipa" # Tell 'Deliver' where it can find the finished ipa file
48
- end
49
-
50
- # ipa "./latest.ipa" # this can be used instead of the `do` block, if you prefer manually building the ipa file
51
-
52
- # beta_ipa do
53
- # system("ipa build --verbose") # customize this to build beta version
54
- # "./ad_hoc_build.ipa" # upload ipa file using `deliver --beta`
55
- # end
56
-
57
- # unit_tests do
58
- # If you use fastlane (http://github.com/krausefx/fastlane), run the tests there
59
- # system("xctool test")
60
- # end
61
-
62
- success do
63
- # system("say 'Successfully deployed a new version.'")
64
- end
65
-
66
- # More information about all available options: https://github.com/KrauseFx/deliver/blob/master/Deliverfile.md
30
+ email "[[EMAIL]]" # the login email address
@@ -1,26 +1,4 @@
1
- # This is the example Deliverfile
2
- # For more information about each property, visit the GitHub documentation: https://github.com/krausefx/deliver
3
- #
4
- # You can remove those parts you don't need
5
- #
6
- # A list of available language codes can be found here: https://github.com/krausefx/deliver#available-language-codes
7
- #
8
- # Everything next to a # is a comment and will be ignored
9
-
10
- # hide_transporter_output # remove the '#' in the beginning of the line, to hide the output while uploading
11
-
12
- ########################################
13
- # App Metadata
14
- ########################################
15
-
16
-
17
- # This folder has to include one folder for each language
18
- # More information about automatic screenshot upload:
19
- # https://github.com/KrauseFx/deliver#upload-screenshots-to-itunes-connect
20
- screenshots_path "./screenshots"
21
-
22
-
23
- # version '1.2' # you can pass this if you want to verify the version number with the ipa file
1
+ ###################### App Metadata ######################
24
2
  #
25
3
  # title({
26
4
  # "en-US" => "Your App Name"
@@ -29,42 +7,32 @@ screenshots_path "./screenshots"
29
7
  # changelog({
30
8
  # "en-US" => "iPhone 6 (Plus) Support"
31
9
  # })
10
+ # More options: https://github.com/KrauseFx/deliver/blob/master/Deliverfile.md
32
11
 
12
+ ###################### Screenshots ######################
13
+ # Store all screenshots in the ./screenshots folder separated
14
+ # by language. If you use snapshot, this happens automatically
33
15
 
16
+ ###################### IPA File ######################
17
+ # This part is only relevant, if you want to submit a new binary
18
+ # If you don't use fastlane (https://github.com/KrauseFx/fastlane)
19
+ # you can set the path to your ipa file using:
20
+ # ipa "./app.ipa"
34
21
 
35
- ########################################
36
- # Building and Testing
37
- ########################################
38
-
39
- # Dynamic generation of the ipa file
40
- # I'm using Shenzhen by Mattt, but you can use any build tool you want
41
- # Remove the whole block if you do not want to upload an ipa file
42
- ipa do
43
- # Add any code you want, like incrementing the build
44
- # number or changing the app identifier
45
-
46
- # Attention: When you return a valid ipa file, this file will get uploaded and released
47
- # If you only want to upload app metadata, remove the complete ipa block.
48
-
49
- # system("ipa build --verbose") # build your project using Shenzhen
50
-
51
- # ENV["IPA_OUTPUT_PATH"] # if you're using fastlane uncomment this and remove the line below
22
+ # to provide an ipa file for TestFlight distribution, use beta_ipa
23
+ # beta_ipa "./app.ipa"
52
24
 
53
- "./[[APP_NAME]].ipa" # Tell 'Deliver' where it can find the finished ipa file
54
- end
25
+ # The version of your app - remove this if you provide an ipa file
26
+ version "[[APP_VERSION]]"
55
27
 
56
- # ipa "./latest.ipa" # this can be used instead of the `do` block, if you prefer manually building the ipa file
28
+ ###################### More Options ######################
29
+ # If you want to have even more control, check out the documentation
30
+ # https://github.com/KrauseFx/deliver/blob/master/Deliverfile.md
57
31
 
58
- # beta_ipa do
59
- # system("ipa build --verbose") # customize this to build beta version
60
- # "./ad_hoc_build.ipa" # upload ipa file using `deliver --beta`
61
- # end
62
32
 
63
- # unit_tests do
64
- # If you use fastlane (http://github.com/krausefx/fastlane), run the tests there
65
- # system("xctool test")
66
- # end
33
+ ###################### Automatically generated ######################
34
+ # Feel free to remove the following lines if you use fastlane
67
35
 
68
- success do
69
- # system("say 'Successfully deployed a new version.'")
70
- end
36
+ app_identifier "[[APP_IDENTIFIER]]" # The bundle identifier of your app
37
+ apple_id "[[APPLE_ID]]" # This is NOT your Apple login ID, but the App ID of your app
38
+ email "[[EMAIL]]" # the login email address
@@ -79,10 +79,12 @@ module Deliver
79
79
  @app_version = @deploy_information[Deliverer::ValKey::APP_VERSION]
80
80
  @app_identifier = @deploy_information[Deliverer::ValKey::APP_IDENTIFIER]
81
81
 
82
+ used_ipa_file = ENV["IPA_OUTPUT_PATH"]# if (ENV["IPA_OUTPUT_PATH"] and File.exists?(ENV["IPA_OUTPUT_PATH"]))
83
+
82
84
  if is_release_build?
83
- used_ipa_file = @deploy_information[:ipa]
85
+ used_ipa_file = @deploy_information[:ipa] if @deploy_information[:ipa]
84
86
  elsif is_beta_build?
85
- used_ipa_file = @deploy_information[:beta_ipa]
87
+ used_ipa_file = @deploy_information[:beta_ipa] if @deploy_information[:beta_ipa]
86
88
  end
87
89
 
88
90
  if used_ipa_file.kind_of?Proc
@@ -217,6 +219,8 @@ module Deliver
217
219
  Helper.log.warn "Overwriting screenshots path from config (#{screens_path}) with (#{ENV["DELIVER_SCREENSHOTS_PATH"]})".yellow
218
220
  screens_path = ENV["DELIVER_SCREENSHOTS_PATH"]
219
221
  end
222
+
223
+ screens_path ||= "./screenshots/" # default value
220
224
 
221
225
  if screens_path
222
226
  # Not using Snapfile. Not a good user.
@@ -57,9 +57,11 @@ module Deliver
57
57
  app = Deliver::App.new(app_identifier: identifier)
58
58
  app.set_metadata_directory("/tmp") # we don't want to pollute the current folder
59
59
  app.metadata # this will download the latest app metadata
60
+
61
+ version_number = app.metadata.fetch_value("//x:version").first["string"] # fetch the latest app version
60
62
 
61
63
  file_path = File.join(deliver_path, Deliver::Deliverfile::Deliverfile::FILE_NAME)
62
- json = generate_deliver_file(app, deliver_path, project_name)
64
+ json = generate_deliver_file(app, deliver_path, project_name, version_number)
63
65
  File.write(file_path, json)
64
66
 
65
67
  FileUtils.mkdir_p File.join(deliver_path, 'screenshots')
@@ -81,7 +83,7 @@ module Deliver
81
83
  private
82
84
  # This method takes care of creating a new 'deliver' folder, containg the app metadata
83
85
  # and screenshots folders
84
- def self.generate_deliver_file(app, path, project_name)
86
+ def self.generate_deliver_file(app, path, project_name, version_number)
85
87
  FileUtils.mkdir_p path rescue nil # never mind if it's already there
86
88
 
87
89
  json = create_json_based_on_xml(app, path)
@@ -107,6 +109,7 @@ module Deliver
107
109
  deliver.gsub!("[[APP_NAME]]", project_name)
108
110
  deliver.gsub!("[[APPLE_ID]]", app.apple_id.to_s)
109
111
  deliver.gsub!("[[EMAIL]]", CredentialsManager::PasswordManager.shared_manager.username)
112
+ deliver.gsub!("[[APP_VERSION]]", version_number)
110
113
 
111
114
  return deliver
112
115
  end
@@ -99,7 +99,6 @@ module Deliver
99
99
  # Distribute to beta testers
100
100
  Helper.log.info "Distributing the latest build to Beta Testers."
101
101
  if self.app.itc.put_build_into_beta_testing!(self.app, self.fetch_app_version)
102
- Helper.log.info "Successfully distributed a new beta build of your app.".green
103
102
  return true
104
103
  end
105
104
  return false
@@ -11,53 +11,68 @@ module Deliver
11
11
 
12
12
  Helper.log.info("Choosing the latest build on iTunesConnect for beta distribution")
13
13
 
14
- sleep 3
15
- click_on "Prerelease"
16
- sleep 3
14
+ require 'excon'
17
15
 
18
- wait_for_preprocessing
16
+ # Migrate session
17
+ cookie_string = page.driver.cookies.collect { |key, cookie| "#{cookie.name}=#{cookie.value}" }.join(";")
19
18
 
19
+ trains_url = "https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/ra/apps/#{app.apple_id}/trains/"
20
20
 
21
- # Beta Switches
22
- if all(".switcher.ng-binding.checked").count == 0
23
- raise "Looks like Beta Testing is not yet enabled for this app. Open '#{current_url}' and enable TestFlight Beta Testing.".red
24
- end
25
-
26
- if all(".bt-version > a").count == 0
27
- raise "Couldn't find any builds. Please check the iTunes Conncet page: '#{current_url}'".red
28
- end
21
+ current_build = nil
22
+ current_train = nil
23
+ started = Time.now
29
24
 
30
- # We might have to wait until the second part of "Processing" is finished
31
- while first("tr > td.bt-internal").text == "Processing"
32
- Helper.log.debug "iTC needs some more processing... Waiting even longer..."
33
- sleep 10
34
- visit current_url
35
- sleep 10
36
- end
37
25
 
38
- first(".bt-version > a").click
26
+ loop do
27
+ trains = JSON.parse(Excon.get(trains_url, headers: { "Cookie" => cookie_string } ).body)
28
+ if trains['data']['processingBuilds'].count == 0
29
+ # Make sure the build was successfully processed
30
+ latest_build_time = 0
39
31
 
40
-
41
- # Inside the build now, enabling the 'Save' button
42
- save_button = wait_for_elements(".formActionButtons.btn-actions > button").first
43
-
44
- if save_button
45
- # First, enable the save button
46
- evaluate_script("$('.formActionButtons.btn-actions > button').removeAttr('disabled')")
47
-
48
- # Maybe we have a 'What to Test' to fill in
49
- to_test = ENV["DELIVER_WHAT_TO_TEST"]
50
- if to_test
51
- text_area = first('span[ng-show="testinfo.data.details[currentLoc].whatsNew.isEditable"] > * > textarea')
52
- text_area.set to_test if text_area
32
+ trains['data']['trains'].each do |train|
33
+ train['builds'].each do |build|
34
+ if build['uploadDate'] > latest_build_time
35
+ current_build = build
36
+ current_train = train
37
+ latest_build_time = build['uploadDate']
38
+ end
39
+ end
40
+ end
41
+
42
+ # We got the most recent build, let's see if it's ready
43
+ if current_build['internalState'] == 'inactive' and current_build['readyToInstall'] == true
44
+ Helper.log.info "Build is ready 3.2.1...".green
45
+ break # Success!
46
+ end
53
47
  end
54
48
 
55
- save_button.click
56
- else
57
- raise "Couldn't find the save button, looks like there is an internet connection problem.".red
49
+ Helper.log.info("Sorry, we have to wait for iTunesConnect, since it's still processing the uploaded ipa file\n" +
50
+ "If this takes longer than 45 minutes, you have to re-upload the ipa file again.\n" +
51
+ "You can always open the browser page yourself: '#{current_url}'\n" +
52
+ "Passed time: ~#{((Time.now - started) / 60.0).to_i} minute(s)")
53
+ sleep 60
54
+ end
55
+
56
+
57
+ if not current_train['testing']['value']
58
+ # Beta testing for latest build is not yet enabled
59
+ current_train['testing']['value'] = true
60
+
61
+ Helper.log.info "Beta Testing is disabled for version #{current_train['versionString']}... enabling it now!".green
62
+
63
+ # Update the only train we modify
64
+ Excon.post(trains_url, body: {trains: [current_train]}.to_json, headers: { "Cookie" => cookie_string } )
58
65
  end
59
66
 
60
- Helper.log.info "Successfully enabled latest beta build.".green
67
+ build_url = "https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/ra/apps/#{app.apple_id}/trains/#{current_build['trainVersion']}/builds/#{current_build['buildVersion']}/testInformation"
68
+ build_info = JSON.parse(Excon.get(build_url, headers: { "Cookie" => cookie_string } ).body)['data']
69
+
70
+ build_info['details'][0]['whatsNew']['value'] = ENV["DELIVER_WHAT_TO_TEST"]
71
+ build_info['details'][0]['description']['value'] = ENV["DELIVER_BETA_DESCRIPTION"]
72
+ build_info['details'][0]['feedbackEmail']['value'] = ENV["DELIVER_BETA_FEEDBACK_EMAIL"]
73
+ Excon.post(build_url, body: build_info.to_json, headers: { "Cookie" => cookie_string } )
74
+
75
+ Helper.log.info "Successfully distributed latest beta build 🚀".green
61
76
 
62
77
  return true
63
78
  rescue => ex
@@ -10,7 +10,7 @@ module Deliver
10
10
 
11
11
  app_identifier = IpaFileAnalyser.fetch_app_identifier(ipa_path)
12
12
  app_identifier ||= ENV["TESTFLIGHT_APP_IDENTITIFER"] || ask("Could not automatically find the app identifier, please enter the app's bundle identifier: ")
13
- app_id ||= (Deliver::ItunesSearchApi.fetch_by_identifier(app_identifier)['trackId'] rescue nil)
13
+ app_id ||= (FastlaneCore::ItunesSearchApi.fetch_by_identifier(app_identifier)['trackId'] rescue nil)
14
14
  app_id ||= ENV["TESTFLIGHT_APPLE_ID"] || ask("Could not automatically find the app ID, please enter it here (e.g. 956814360): ")
15
15
  strategy = (skip_deploy ? Deliver::IPA_UPLOAD_STRATEGY_JUST_UPLOAD : Deliver::IPA_UPLOAD_STRATEGY_BETA_BUILD)
16
16
 
@@ -1,3 +1,3 @@
1
1
  module Deliver
2
- VERSION = "0.8.0"
2
+ VERSION = "0.8.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deliver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felix Krause
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-20 00:00:00.000000000 Z
11
+ date: 2015-04-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fastlane_core
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - '>='
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: excon
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: bundler
99
113
  requirement: !ruby/object:Gem::Requirement