fastlane 2.155.1 → 2.157.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +72 -72
- data/deliver/lib/deliver.rb +1 -0
- data/deliver/lib/deliver/app_screenshot_iterator.rb +95 -0
- data/deliver/lib/deliver/detect_values.rb +4 -1
- data/deliver/lib/deliver/languages.rb +7 -0
- data/deliver/lib/deliver/loader.rb +4 -5
- data/deliver/lib/deliver/queue_worker.rb +64 -0
- data/deliver/lib/deliver/runner.rb +7 -5
- data/deliver/lib/deliver/upload_screenshots.rb +143 -128
- data/fastlane/lib/fastlane/actions/app_store_connect_api_key.rb +120 -0
- data/fastlane/lib/fastlane/actions/commit_version_bump.rb +1 -1
- data/fastlane/lib/fastlane/actions/docs/upload_to_play_store.md +2 -0
- data/fastlane/lib/fastlane/actions/docs/upload_to_testflight.md +17 -1
- data/fastlane/lib/fastlane/actions/set_changelog.rb +2 -2
- data/fastlane/lib/fastlane/actions/sonar.rb +5 -0
- data/fastlane/lib/fastlane/actions/spaceship_stats.rb +73 -0
- data/fastlane/lib/fastlane/actions/upload_to_testflight.rb +4 -0
- data/fastlane/lib/fastlane/version.rb +1 -1
- data/fastlane/swift/Deliverfile.swift +1 -1
- data/fastlane/swift/DeliverfileProtocol.swift +1 -1
- data/fastlane/swift/Fastlane.swift +68 -8
- data/fastlane/swift/Gymfile.swift +1 -1
- data/fastlane/swift/GymfileProtocol.swift +1 -1
- data/fastlane/swift/Matchfile.swift +1 -1
- data/fastlane/swift/MatchfileProtocol.swift +1 -1
- data/fastlane/swift/Precheckfile.swift +1 -1
- data/fastlane/swift/PrecheckfileProtocol.swift +1 -1
- data/fastlane/swift/Scanfile.swift +1 -1
- data/fastlane/swift/ScanfileProtocol.swift +1 -1
- data/fastlane/swift/Screengrabfile.swift +1 -1
- data/fastlane/swift/ScreengrabfileProtocol.swift +1 -1
- data/fastlane/swift/Snapshotfile.swift +1 -1
- data/fastlane/swift/SnapshotfileProtocol.swift +1 -1
- data/fastlane_core/lib/fastlane_core/itunes_transporter.rb +71 -42
- data/fastlane_core/lib/fastlane_core/project.rb +1 -0
- data/gym/lib/gym/error_handler.rb +1 -1
- data/gym/lib/gym/generators/build_command_generator.rb +0 -1
- data/pilot/lib/pilot/build_manager.rb +18 -4
- data/pilot/lib/pilot/manager.rb +15 -5
- data/pilot/lib/pilot/options.rb +16 -0
- data/produce/lib/produce/itunes_connect.rb +2 -2
- data/scan/lib/scan/test_command_generator.rb +3 -1
- data/screengrab/lib/screengrab/runner.rb +8 -7
- data/sigh/lib/sigh/runner.rb +9 -5
- data/snapshot/lib/snapshot/test_command_generator_base.rb +3 -1
- data/spaceship/lib/spaceship.rb +4 -0
- data/spaceship/lib/spaceship/client.rb +2 -0
- data/spaceship/lib/spaceship/connect_api.rb +0 -15
- data/spaceship/lib/spaceship/connect_api/api_client.rb +270 -0
- data/spaceship/lib/spaceship/connect_api/client.rb +139 -213
- data/spaceship/lib/spaceship/connect_api/models/profile.rb +3 -2
- data/spaceship/lib/spaceship/connect_api/provisioning/client.rb +8 -17
- data/spaceship/lib/spaceship/connect_api/provisioning/provisioning.rb +75 -64
- data/spaceship/lib/spaceship/connect_api/spaceship.rb +94 -0
- data/spaceship/lib/spaceship/connect_api/testflight/client.rb +8 -17
- data/spaceship/lib/spaceship/connect_api/testflight/testflight.rb +288 -277
- data/spaceship/lib/spaceship/connect_api/token.rb +46 -5
- data/spaceship/lib/spaceship/connect_api/token_refresh_middleware.rb +24 -0
- data/spaceship/lib/spaceship/connect_api/tunes/client.rb +8 -17
- data/spaceship/lib/spaceship/connect_api/tunes/tunes.rb +717 -706
- data/spaceship/lib/spaceship/connect_api/users/client.rb +8 -17
- data/spaceship/lib/spaceship/connect_api/users/users.rb +28 -17
- data/spaceship/lib/spaceship/stats_middleware.rb +65 -0
- metadata +25 -18
- data/sigh/lib/sigh/.runner.rb.swp +0 -0
- data/spaceship/lib/spaceship/connect_api/models/.device.rb.swp +0 -0
| @@ -1,245 +1,171 @@ | |
| 1 | 
            -
            require_relative ' | 
| 2 | 
            -
            require_relative './ | 
| 1 | 
            +
            require_relative './token'
         | 
| 2 | 
            +
            require_relative './provisioning/provisioning'
         | 
| 3 | 
            +
            require_relative './testflight/testflight'
         | 
| 4 | 
            +
            require_relative './tunes/tunes'
         | 
| 5 | 
            +
            require_relative './users/users'
         | 
| 3 6 |  | 
| 4 7 | 
             
            module Spaceship
         | 
| 5 8 | 
             
              class ConnectAPI
         | 
| 6 | 
            -
                class Client | 
| 9 | 
            +
                class Client
         | 
| 7 10 | 
             
                  attr_accessor :token
         | 
| 11 | 
            +
                  attr_accessor :tunes_client
         | 
| 12 | 
            +
                  attr_accessor :portal_client
         | 
| 8 13 |  | 
| 9 | 
            -
                   | 
| 10 | 
            -
                  # @!group Client Init
         | 
| 11 | 
            -
                  #####################################################
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                  # Instantiates a client with cookie session or a JWT token.
         | 
| 14 | 
            -
                  def initialize(cookie: nil, current_team_id: nil, token: nil)
         | 
| 15 | 
            -
                    if token.nil?
         | 
| 16 | 
            -
                      super(cookie: cookie, current_team_id: current_team_id, timeout: 1200)
         | 
| 17 | 
            -
                    else
         | 
| 18 | 
            -
                      options = {
         | 
| 19 | 
            -
                        request: {
         | 
| 20 | 
            -
                          timeout:       (ENV["SPACESHIP_TIMEOUT"] || 300).to_i,
         | 
| 21 | 
            -
                          open_timeout:  (ENV["SPACESHIP_TIMEOUT"] || 300).to_i
         | 
| 22 | 
            -
                        }
         | 
| 23 | 
            -
                      }
         | 
| 24 | 
            -
                      @token = token
         | 
| 25 | 
            -
                      @current_team_id = current_team_id
         | 
| 26 | 
            -
             | 
| 27 | 
            -
                      hostname = "https://api.appstoreconnect.apple.com/v1/"
         | 
| 28 | 
            -
             | 
| 29 | 
            -
                      @client = Faraday.new(hostname, options) do |c|
         | 
| 30 | 
            -
                        c.response(:json, content_type: /\bjson$/)
         | 
| 31 | 
            -
                        c.response(:plist, content_type: /\bplist$/)
         | 
| 32 | 
            -
                        c.use(FaradayMiddleware::RelsMiddleware)
         | 
| 33 | 
            -
                        c.adapter(Faraday.default_adapter)
         | 
| 34 | 
            -
                        c.headers["Authorization"] = "Bearer #{token.text}"
         | 
| 35 | 
            -
             | 
| 36 | 
            -
                        if ENV['SPACESHIP_DEBUG']
         | 
| 37 | 
            -
                          # for debugging only
         | 
| 38 | 
            -
                          # This enables tracking of networking requests using Charles Web Proxy
         | 
| 39 | 
            -
                          c.proxy = "https://127.0.0.1:8888"
         | 
| 40 | 
            -
                          c.ssl[:verify_mode] = OpenSSL::SSL::VERIFY_NONE
         | 
| 41 | 
            -
                        elsif ENV["SPACESHIP_PROXY"]
         | 
| 42 | 
            -
                          c.proxy = ENV["SPACESHIP_PROXY"]
         | 
| 43 | 
            -
                          c.ssl[:verify_mode] = OpenSSL::SSL::VERIFY_NONE if ENV["SPACESHIP_PROXY_SSL_VERIFY_NONE"]
         | 
| 44 | 
            -
                        end
         | 
| 45 | 
            -
             | 
| 46 | 
            -
                        if ENV["DEBUG"]
         | 
| 47 | 
            -
                          puts("To run spaceship through a local proxy, use SPACESHIP_DEBUG")
         | 
| 48 | 
            -
                        end
         | 
| 49 | 
            -
                      end
         | 
| 50 | 
            -
                    end
         | 
| 51 | 
            -
                  end
         | 
| 52 | 
            -
             | 
| 53 | 
            -
                  def self.hostname
         | 
| 54 | 
            -
                    return nil
         | 
| 55 | 
            -
                  end
         | 
| 56 | 
            -
             | 
| 14 | 
            +
                  # Initializes client with Apple's App Store Connect JWT auth key.
         | 
| 57 15 | 
             
                  #
         | 
| 58 | 
            -
                  #  | 
| 16 | 
            +
                  # This method will automatically use the key id, issuer id, and filepath from environment
         | 
| 17 | 
            +
                  # variables if not given.
         | 
| 59 18 | 
             
                  #
         | 
| 60 | 
            -
             | 
| 61 | 
            -
                   | 
| 62 | 
            -
             | 
| 63 | 
            -
                   | 
| 64 | 
            -
             | 
| 65 | 
            -
                   | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
                     | 
| 71 | 
            -
                     | 
| 72 | 
            -
                    params[:limit] = limit if limit
         | 
| 73 | 
            -
                    params[:sort] = sort if sort
         | 
| 74 | 
            -
                    params[:cursor] = cursor if cursor
         | 
| 75 | 
            -
             | 
| 76 | 
            -
                    return params
         | 
| 19 | 
            +
                  # All three parameters are needed to authenticate.
         | 
| 20 | 
            +
                  #
         | 
| 21 | 
            +
                  # @param key_id (String) (optional): The key id
         | 
| 22 | 
            +
                  # @param issuer_id (String) (optional): The issuer id
         | 
| 23 | 
            +
                  # @param filepath (String) (optional): The filepath
         | 
| 24 | 
            +
                  #
         | 
| 25 | 
            +
                  # @raise InvalidUserCredentialsError: raised if authentication failed
         | 
| 26 | 
            +
                  #
         | 
| 27 | 
            +
                  # @return (Spaceship::ConnectAPI::Client) The client the login method was called for
         | 
| 28 | 
            +
                  def self.auth(key_id: nil, issuer_id: nil, filepath: nil)
         | 
| 29 | 
            +
                    token = Spaceship::ConnectAPI::Token.create(key_id: key_id, issuer_id: issuer_id, filepath: filepath)
         | 
| 30 | 
            +
                    return ConnectAPI::Client.new(token: token)
         | 
| 77 31 | 
             
                  end
         | 
| 78 32 |  | 
| 79 | 
            -
                   | 
| 80 | 
            -
             | 
| 81 | 
            -
             | 
| 82 | 
            -
             | 
| 83 | 
            -
             | 
| 84 | 
            -
             | 
| 85 | 
            -
             | 
| 86 | 
            -
             | 
| 33 | 
            +
                  # Authenticates with Apple's web services. This method has to be called once
         | 
| 34 | 
            +
                  # to generate a valid session.
         | 
| 35 | 
            +
                  #
         | 
| 36 | 
            +
                  # This method will automatically use the username from the Appfile (if available)
         | 
| 37 | 
            +
                  # and fetch the password from the Keychain (if available)
         | 
| 38 | 
            +
                  #
         | 
| 39 | 
            +
                  # @param user (String) (optional): The username (usually the email address)
         | 
| 40 | 
            +
                  # @param password (String) (optional): The password
         | 
| 41 | 
            +
                  # @param team_id (String) (optional): The team id
         | 
| 42 | 
            +
                  # @param team_name (String) (optional): The team name
         | 
| 43 | 
            +
                  #
         | 
| 44 | 
            +
                  # @raise InvalidUserCredentialsError: raised if authentication failed
         | 
| 45 | 
            +
                  #
         | 
| 46 | 
            +
                  # @return (Spaceship::ConnectAPI::Client) The client the login method was called for
         | 
| 47 | 
            +
                  def self.login(user = nil, password = nil, team_id: nil, team_name: nil)
         | 
| 48 | 
            +
                    tunes_client = TunesClient.login(user, password)
         | 
| 49 | 
            +
                    portal_client = PortalClient.login(user, password)
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                    # The clients will automatically select the first team if none is given
         | 
| 52 | 
            +
                    if !team_id.nil? || !team_name.nil?
         | 
| 53 | 
            +
                      tunes_client.select_team(team_id: team_id, team_name: team_name)
         | 
| 54 | 
            +
                      portal_client.select_team(team_id: team_id, team_name: team_name)
         | 
| 87 55 | 
             
                    end
         | 
| 88 | 
            -
                    handle_response(response)
         | 
| 89 | 
            -
                  end
         | 
| 90 56 |  | 
| 91 | 
            -
             | 
| 92 | 
            -
                    response = with_asc_retry(tries) do
         | 
| 93 | 
            -
                      request(:post) do |req|
         | 
| 94 | 
            -
                        req.url(url_or_path)
         | 
| 95 | 
            -
                        req.body = body.to_json
         | 
| 96 | 
            -
                        req.headers['Content-Type'] = 'application/json'
         | 
| 97 | 
            -
                      end
         | 
| 98 | 
            -
                    end
         | 
| 99 | 
            -
                    handle_response(response)
         | 
| 57 | 
            +
                    return ConnectAPI::Client.new(tunes_client: tunes_client, portal_client: portal_client)
         | 
| 100 58 | 
             
                  end
         | 
| 101 59 |  | 
| 102 | 
            -
                  def  | 
| 103 | 
            -
                     | 
| 104 | 
            -
             | 
| 105 | 
            -
             | 
| 106 | 
            -
             | 
| 107 | 
            -
             | 
| 108 | 
            -
             | 
| 109 | 
            -
                     | 
| 110 | 
            -
             | 
| 60 | 
            +
                  def initialize(cookie: nil, current_team_id: nil, token: nil, tunes_client: nil, portal_client: nil)
         | 
| 61 | 
            +
                    @token = token
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                    # If using web session...
         | 
| 64 | 
            +
                    # Spaceship::Tunes is needed for TestFlight::API, Tunes::API, and Users::API
         | 
| 65 | 
            +
                    # Spaceship::Portal is needed for Provisioning::API
         | 
| 66 | 
            +
                    @tunes_client = tunes_client
         | 
| 67 | 
            +
                    @portal_client = portal_client
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                    # Extending this instance to add API endpoints from these modules
         | 
| 70 | 
            +
                    # Each of these modules adds a new setter method for an instance
         | 
| 71 | 
            +
                    # of an ConnectAPI::APIClient
         | 
| 72 | 
            +
                    # These get set in set_indvidual_clients
         | 
| 73 | 
            +
                    self.extend(Spaceship::ConnectAPI::TestFlight::API)
         | 
| 74 | 
            +
                    self.extend(Spaceship::ConnectAPI::Tunes::API)
         | 
| 75 | 
            +
                    self.extend(Spaceship::ConnectAPI::Provisioning::API)
         | 
| 76 | 
            +
                    self.extend(Spaceship::ConnectAPI::Users::API)
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                    set_indvidual_clients(
         | 
| 79 | 
            +
                      cookie: cookie,
         | 
| 80 | 
            +
                      current_team_id: current_team_id,
         | 
| 81 | 
            +
                      token: token,
         | 
| 82 | 
            +
                      tunes_client: @tunes_client,
         | 
| 83 | 
            +
                      portal_client: @portal_client
         | 
| 84 | 
            +
                    )
         | 
| 111 85 | 
             
                  end
         | 
| 112 86 |  | 
| 113 | 
            -
                  def  | 
| 114 | 
            -
                     | 
| 115 | 
            -
                       | 
| 116 | 
            -
                         | 
| 117 | 
            -
             | 
| 118 | 
            -
             | 
| 119 | 
            -
             | 
| 120 | 
            -
             | 
| 87 | 
            +
                  def in_house?
         | 
| 88 | 
            +
                    if token
         | 
| 89 | 
            +
                      if token.in_house.nil?
         | 
| 90 | 
            +
                        message = [
         | 
| 91 | 
            +
                          "Cannot determine if team is App Store or Enterprise via the App Store Connect API (yet)",
         | 
| 92 | 
            +
                          "Set 'in_house' on your Spaceship::ConnectAPI::Token",
         | 
| 93 | 
            +
                          "Or set 'in_house' in your App Store Connect API key JSON file",
         | 
| 94 | 
            +
                          "Or set the 'SPACESHIP_CONNECT_API_IN_HOUSE' environment variable to 'true'",
         | 
| 95 | 
            +
                          "View more info in the docs at https://docs.fastlane.tools/app-store-connect-api/"
         | 
| 96 | 
            +
                        ]
         | 
| 97 | 
            +
                        raise message.join('\n')
         | 
| 121 98 | 
             
                      end
         | 
| 99 | 
            +
                      return !!token.in_house
         | 
| 100 | 
            +
                    elsif @portal_client
         | 
| 101 | 
            +
                      return @portal_client.in_house?
         | 
| 102 | 
            +
                    else
         | 
| 103 | 
            +
                      raise "No App Store Connect API token or Portal Client set"
         | 
| 122 104 | 
             
                    end
         | 
| 123 | 
            -
                    handle_response(response)
         | 
| 124 105 | 
             
                  end
         | 
| 125 106 |  | 
| 126 | 
            -
                   | 
| 127 | 
            -
             | 
| 128 | 
            -
             | 
| 129 | 
            -
             | 
| 130 | 
            -
                     | 
| 107 | 
            +
                  def select_team(team_id: nil, team_name: nil)
         | 
| 108 | 
            +
                    @tunes_client.select_team(team_id: team_id, team_name: team_name)
         | 
| 109 | 
            +
                    @portal_client.select_team(team_id: team_id, team_name: team_name)
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                    # Updating the tunes and portal clients requires resetting
         | 
| 112 | 
            +
                    # of the clients in the API modules
         | 
| 113 | 
            +
                    set_indvidual_clients(
         | 
| 114 | 
            +
                      cookie: nil,
         | 
| 115 | 
            +
                      current_team_id: nil,
         | 
| 116 | 
            +
                      token: nil,
         | 
| 117 | 
            +
                      tunes_client: tunes_client,
         | 
| 118 | 
            +
                      portal_client: portal_client
         | 
| 119 | 
            +
                    )
         | 
| 120 | 
            +
                  end
         | 
| 131 121 |  | 
| 132 | 
            -
             | 
| 122 | 
            +
                  private
         | 
| 133 123 |  | 
| 134 | 
            -
             | 
| 135 | 
            -
             | 
| 136 | 
            -
             | 
| 124 | 
            +
                  def set_indvidual_clients(cookie: nil, current_team_id: nil, token: nil, tunes_client: nil, portal_client: nil)
         | 
| 125 | 
            +
                    # This was added by Spaceship::ConnectAPI::TestFlight::API and is required
         | 
| 126 | 
            +
                    # to be set for API methods to have a client to send request on
         | 
| 127 | 
            +
                    if cookie || token || tunes_client
         | 
| 128 | 
            +
                      self.test_flight_request_client = Spaceship::ConnectAPI::TestFlight::Client.new(
         | 
| 129 | 
            +
                        cookie: cookie,
         | 
| 130 | 
            +
                        current_team_id: current_team_id,
         | 
| 131 | 
            +
                        token: token,
         | 
| 132 | 
            +
                        another_client: tunes_client
         | 
| 133 | 
            +
                      )
         | 
| 137 134 | 
             
                    end
         | 
| 138 135 |  | 
| 139 | 
            -
                     | 
| 140 | 
            -
             | 
| 141 | 
            -
                     | 
| 142 | 
            -
             | 
| 143 | 
            -
             | 
| 144 | 
            -
             | 
| 145 | 
            -
             | 
| 146 | 
            -
             | 
| 136 | 
            +
                    # This was added by Spaceship::ConnectAPI::Tunes::API and is required
         | 
| 137 | 
            +
                    # to be set for API methods to have a client to send request on
         | 
| 138 | 
            +
                    if cookie || token || tunes_client
         | 
| 139 | 
            +
                      self.tunes_request_client = Spaceship::ConnectAPI::Tunes::Client.new(
         | 
| 140 | 
            +
                        cookie: cookie,
         | 
| 141 | 
            +
                        current_team_id: current_team_id,
         | 
| 142 | 
            +
                        token: token,
         | 
| 143 | 
            +
                        another_client: tunes_client
         | 
| 144 | 
            +
                      )
         | 
| 147 145 | 
             
                    end
         | 
| 148 | 
            -
                  end
         | 
| 149 146 |  | 
| 150 | 
            -
             | 
| 151 | 
            -
                     | 
| 152 | 
            -
             | 
| 147 | 
            +
                    # This was added by Spaceship::ConnectAPI::Provisioning::API and is required
         | 
| 148 | 
            +
                    # to be set for API methods to have a client to send request on
         | 
| 149 | 
            +
                    if cookie || token || portal_client
         | 
| 150 | 
            +
                      self.provisioning_request_client = Spaceship::ConnectAPI::Provisioning::Client.new(
         | 
| 151 | 
            +
                        cookie: cookie,
         | 
| 152 | 
            +
                        current_team_id: current_team_id,
         | 
| 153 | 
            +
                        token: token,
         | 
| 154 | 
            +
                        another_client: portal_client
         | 
| 155 | 
            +
                      )
         | 
| 153 156 | 
             
                    end
         | 
| 154 157 |  | 
| 155 | 
            -
                     | 
| 156 | 
            -
             | 
| 157 | 
            -
                     | 
| 158 | 
            -
                       | 
| 158 | 
            +
                    # This was added by Spaceship::ConnectAPI::Users::API and is required
         | 
| 159 | 
            +
                    # to be set for API methods to have a client to send request on
         | 
| 160 | 
            +
                    if cookie || token || tunes_client
         | 
| 161 | 
            +
                      self.users_request_client = Spaceship::ConnectAPI::Users::Client.new(
         | 
| 162 | 
            +
                        cookie: cookie,
         | 
| 163 | 
            +
                        current_team_id: current_team_id,
         | 
| 164 | 
            +
                        token: token,
         | 
| 165 | 
            +
                        another_client: tunes_client
         | 
| 166 | 
            +
                      )
         | 
| 159 167 | 
             
                    end
         | 
| 160 | 
            -
             | 
| 161 | 
            -
                    raise UnexpectedResponse, response.body['error'] if response.body['error']
         | 
| 162 | 
            -
             | 
| 163 | 
            -
                    raise UnexpectedResponse, handle_errors(response) if response.body['errors']
         | 
| 164 | 
            -
             | 
| 165 | 
            -
                    raise UnexpectedResponse, "Temporary App Store Connect error: #{response.body}" if response.body['statusCode'] == 'ERROR'
         | 
| 166 | 
            -
             | 
| 167 | 
            -
                    store_csrf_tokens(response)
         | 
| 168 | 
            -
             | 
| 169 | 
            -
                    return Spaceship::ConnectAPI::Response.new(body: response.body, status: response.status, client: self)
         | 
| 170 | 
            -
                  end
         | 
| 171 | 
            -
             | 
| 172 | 
            -
                  def handle_errors(response)
         | 
| 173 | 
            -
                    # Example error format
         | 
| 174 | 
            -
                    # {
         | 
| 175 | 
            -
                    # "errors":[
         | 
| 176 | 
            -
                    #     {
         | 
| 177 | 
            -
                    #       "id":"cbfd8674-4802-4857-bfe8-444e1ea36e32",
         | 
| 178 | 
            -
                    #       "status":"409",
         | 
| 179 | 
            -
                    #       "code":"STATE_ERROR",
         | 
| 180 | 
            -
                    #       "title":"The request cannot be fulfilled because of the state of another resource.",
         | 
| 181 | 
            -
                    #       "detail":"Submit for review errors found.",
         | 
| 182 | 
            -
                    #       "meta":{
         | 
| 183 | 
            -
                    #           "associatedErrors":{
         | 
| 184 | 
            -
                    #             "/v1/appScreenshots/":[
         | 
| 185 | 
            -
                    #                 {
         | 
| 186 | 
            -
                    #                   "id":"23d1734f-b81f-411a-98e4-6d3e763d54ed",
         | 
| 187 | 
            -
                    #                   "status":"409",
         | 
| 188 | 
            -
                    #                   "code":"STATE_ERROR.SCREENSHOT_REQUIRED.APP_WATCH_SERIES_4",
         | 
| 189 | 
            -
                    #                   "title":"App screenshot missing (APP_WATCH_SERIES_4)."
         | 
| 190 | 
            -
                    #                 },
         | 
| 191 | 
            -
                    #                 {
         | 
| 192 | 
            -
                    #                   "id":"db993030-0a93-48e9-9fd7-7e5676633431",
         | 
| 193 | 
            -
                    #                   "status":"409",
         | 
| 194 | 
            -
                    #                   "code":"STATE_ERROR.SCREENSHOT_REQUIRED.APP_WATCH_SERIES_4",
         | 
| 195 | 
            -
                    #                   "title":"App screenshot missing (APP_WATCH_SERIES_4)."
         | 
| 196 | 
            -
                    #                 }
         | 
| 197 | 
            -
                    #             ],
         | 
| 198 | 
            -
                    #             "/v1/builds/d710b6fa-5235-4fe4-b791-2b80d6818db0":[
         | 
| 199 | 
            -
                    #                 {
         | 
| 200 | 
            -
                    #                   "id":"e421fe6f-0e3b-464b-89dc-ba437e7bb77d",
         | 
| 201 | 
            -
                    #                   "status":"409",
         | 
| 202 | 
            -
                    #                   "code":"ENTITY_ERROR.ATTRIBUTE.REQUIRED",
         | 
| 203 | 
            -
                    #                   "title":"The provided entity is missing a required attribute",
         | 
| 204 | 
            -
                    #                   "detail":"You must provide a value for the attribute 'usesNonExemptEncryption' with this request",
         | 
| 205 | 
            -
                    #                   "source":{
         | 
| 206 | 
            -
                    #                       "pointer":"/data/attributes/usesNonExemptEncryption"
         | 
| 207 | 
            -
                    #                   }
         | 
| 208 | 
            -
                    #                 }
         | 
| 209 | 
            -
                    #             ]
         | 
| 210 | 
            -
                    #           }
         | 
| 211 | 
            -
                    #       }
         | 
| 212 | 
            -
                    #     }
         | 
| 213 | 
            -
                    # ]
         | 
| 214 | 
            -
                    # }
         | 
| 215 | 
            -
             | 
| 216 | 
            -
                    return response.body['errors'].map do |error|
         | 
| 217 | 
            -
                      messages = [[error['title'], error['detail']].compact.join(" - ")]
         | 
| 218 | 
            -
             | 
| 219 | 
            -
                      meta = error["meta"] || {}
         | 
| 220 | 
            -
                      associated_errors = meta["associatedErrors"] || {}
         | 
| 221 | 
            -
             | 
| 222 | 
            -
                      messages + associated_errors.values.flatten.map do |associated_error|
         | 
| 223 | 
            -
                        [[associated_error["title"], associated_error["detail"]].compact.join(" - ")]
         | 
| 224 | 
            -
                      end
         | 
| 225 | 
            -
                    end.flatten.join("\n")
         | 
| 226 | 
            -
                  end
         | 
| 227 | 
            -
             | 
| 228 | 
            -
                  private
         | 
| 229 | 
            -
             | 
| 230 | 
            -
                  def local_variable_get(binding, name)
         | 
| 231 | 
            -
                    if binding.respond_to?(:local_variable_get)
         | 
| 232 | 
            -
                      binding.local_variable_get(name)
         | 
| 233 | 
            -
                    else
         | 
| 234 | 
            -
                      binding.eval(name.to_s)
         | 
| 235 | 
            -
                    end
         | 
| 236 | 
            -
                  end
         | 
| 237 | 
            -
             | 
| 238 | 
            -
                  def provider_id
         | 
| 239 | 
            -
                    return team_id if self.provider.nil?
         | 
| 240 | 
            -
                    self.provider.provider_id
         | 
| 241 168 | 
             
                  end
         | 
| 242 169 | 
             
                end
         | 
| 243 170 | 
             
              end
         | 
| 244 | 
            -
              # rubocop:enable Metrics/ClassLength
         | 
| 245 171 | 
             
            end
         | 
| @@ -69,14 +69,15 @@ module Spaceship | |
| 69 69 | 
             
                    return resps.flat_map(&:to_models)
         | 
| 70 70 | 
             
                  end
         | 
| 71 71 |  | 
| 72 | 
            -
                  def self.create(name: nil, profile_type: nil, bundle_id_id: nil, certificate_ids: nil, device_ids: nil)
         | 
| 72 | 
            +
                  def self.create(name: nil, profile_type: nil, bundle_id_id: nil, certificate_ids: nil, device_ids: nil, template_name: nil)
         | 
| 73 73 | 
             
                    resp = Spaceship::ConnectAPI.post_profiles(
         | 
| 74 74 | 
             
                      bundle_id_id: bundle_id_id,
         | 
| 75 75 | 
             
                      certificates: certificate_ids,
         | 
| 76 76 | 
             
                      devices: device_ids,
         | 
| 77 77 | 
             
                      attributes: {
         | 
| 78 78 | 
             
                        name: name,
         | 
| 79 | 
            -
                        profileType: profile_type
         | 
| 79 | 
            +
                        profileType: profile_type,
         | 
| 80 | 
            +
                        templateName: template_name
         | 
| 80 81 | 
             
                      }
         | 
| 81 82 | 
             
                    )
         | 
| 82 83 | 
             
                    return resp.to_models.first
         | 
| @@ -1,27 +1,18 @@ | |
| 1 | 
            -
            require_relative '../ | 
| 1 | 
            +
            require_relative '../api_client'
         | 
| 2 | 
            +
            require_relative './provisioning'
         | 
| 2 3 | 
             
            require_relative '../../portal/portal_client'
         | 
| 3 4 |  | 
| 4 5 | 
             
            module Spaceship
         | 
| 5 6 | 
             
              class ConnectAPI
         | 
| 6 7 | 
             
                module Provisioning
         | 
| 7 | 
            -
                  class Client < Spaceship::ConnectAPI:: | 
| 8 | 
            -
                    def  | 
| 9 | 
            -
                       | 
| 10 | 
            -
                      if Spaceship::ConnectAPI.token
         | 
| 11 | 
            -
                        if @client.nil? || @client.token != Spaceship::ConnectAPI.token
         | 
| 12 | 
            -
                          @client = Spaceship::ConnectAPI::Provisioning::Client.new(token: Spaceship::ConnectAPI.token)
         | 
| 13 | 
            -
                        end
         | 
| 14 | 
            -
                      elsif Spaceship::Portal.client
         | 
| 15 | 
            -
                        # Initialize new client if new or if team changed
         | 
| 16 | 
            -
                        if @client.nil? || @client.team_id != Spaceship::Portal.client.team_id
         | 
| 17 | 
            -
                          @client = Spaceship::ConnectAPI::Provisioning::Client.client_with_authorization_from(Spaceship::Portal.client)
         | 
| 18 | 
            -
                        end
         | 
| 19 | 
            -
                      end
         | 
| 8 | 
            +
                  class Client < Spaceship::ConnectAPI::APIClient
         | 
| 9 | 
            +
                    def initialize(cookie: nil, current_team_id: nil, token: nil, another_client: nil)
         | 
| 10 | 
            +
                      another_client ||= Spaceship::Portal.client if cookie.nil? && token.nil?
         | 
| 20 11 |  | 
| 21 | 
            -
                       | 
| 22 | 
            -
                      raise "Please login using `Spaceship::Portal.login('user', 'password')`" unless @client
         | 
| 12 | 
            +
                      super(cookie: cookie, current_team_id: current_team_id, token: token, another_client: another_client)
         | 
| 23 13 |  | 
| 24 | 
            -
                       | 
| 14 | 
            +
                      self.extend(Spaceship::ConnectAPI::Provisioning::API)
         | 
| 15 | 
            +
                      self.provisioning_request_client = self
         | 
| 25 16 | 
             
                    end
         | 
| 26 17 |  | 
| 27 18 | 
             
                    def self.hostname
         | 
| @@ -3,86 +3,97 @@ require 'spaceship/connect_api/provisioning/client' | |
| 3 3 | 
             
            module Spaceship
         | 
| 4 4 | 
             
              class ConnectAPI
         | 
| 5 5 | 
             
                module Provisioning
         | 
| 6 | 
            -
                   | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 6 | 
            +
                  module API
         | 
| 7 | 
            +
                    def provisioning_request_client=(provisioning_request_client)
         | 
| 8 | 
            +
                      @provisioning_request_client = provisioning_request_client
         | 
| 9 | 
            +
                    end
         | 
| 9 10 |  | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 11 | 
            +
                    def provisioning_request_client
         | 
| 12 | 
            +
                      return @provisioning_request_client if @provisioning_request_client
         | 
| 13 | 
            +
                      raise TypeError, "You need to instantiate this module with provisioning_request_client"
         | 
| 14 | 
            +
                    end
         | 
| 14 15 |  | 
| 15 | 
            -
             | 
| 16 | 
            -
                     | 
| 17 | 
            -
                     | 
| 18 | 
            -
                  end
         | 
| 16 | 
            +
                    #
         | 
| 17 | 
            +
                    # bundleIds
         | 
| 18 | 
            +
                    #
         | 
| 19 19 |  | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 20 | 
            +
                    def get_bundle_ids(filter: {}, includes: nil, limit: nil, sort: nil)
         | 
| 21 | 
            +
                      params = provisioning_request_client.build_params(filter: filter, includes: includes, limit: limit, sort: sort)
         | 
| 22 | 
            +
                      provisioning_request_client.get("bundleIds", params)
         | 
| 23 | 
            +
                    end
         | 
| 23 24 |  | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 25 | 
            +
                    def get_bundle_id(bundle_id_id: {}, includes: nil)
         | 
| 26 | 
            +
                      params = provisioning_request_client.build_params(filter: nil, includes: includes, limit: nil, sort: nil)
         | 
| 27 | 
            +
                      provisioning_request_client.get("bundleIds/#{bundle_id_id}", params)
         | 
| 28 | 
            +
                    end
         | 
| 28 29 |  | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 30 | 
            +
                    #
         | 
| 31 | 
            +
                    # certificates
         | 
| 32 | 
            +
                    #
         | 
| 32 33 |  | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 34 | 
            +
                    def get_certificates(filter: {}, includes: nil, limit: nil, sort: nil)
         | 
| 35 | 
            +
                      params = provisioning_request_client.build_params(filter: filter, includes: includes, limit: limit, sort: sort)
         | 
| 36 | 
            +
                      provisioning_request_client.get("certificates", params)
         | 
| 37 | 
            +
                    end
         | 
| 37 38 |  | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 39 | 
            +
                    #
         | 
| 40 | 
            +
                    # devices
         | 
| 41 | 
            +
                    #
         | 
| 41 42 |  | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 43 | 
            +
                    def get_devices(filter: {}, includes: nil, limit: nil, sort: nil)
         | 
| 44 | 
            +
                      params = provisioning_request_client.build_params(filter: filter, includes: includes, limit: limit, sort: sort)
         | 
| 45 | 
            +
                      provisioning_request_client.get("devices", params)
         | 
| 46 | 
            +
                    end
         | 
| 46 47 |  | 
| 47 | 
            -
             | 
| 48 | 
            -
                     | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 55 | 
            -
             | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
                           | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
                          },
         | 
| 67 | 
            -
                          devices: {
         | 
| 68 | 
            -
                            data: (devices || []).map do |device|
         | 
| 69 | 
            -
                              {
         | 
| 70 | 
            -
                                type: "devices",
         | 
| 71 | 
            -
                                id: device
         | 
| 48 | 
            +
                    #
         | 
| 49 | 
            +
                    # profiles
         | 
| 50 | 
            +
                    #
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                    def get_profiles(filter: {}, includes: nil, limit: nil, sort: nil)
         | 
| 53 | 
            +
                      params = provisioning_request_client.build_params(filter: filter, includes: includes, limit: limit, sort: sort)
         | 
| 54 | 
            +
                      provisioning_request_client.get("profiles", params)
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                    def post_profiles(bundle_id_id: nil, certificates: nil, devices: nil, attributes: {})
         | 
| 58 | 
            +
                      body = {
         | 
| 59 | 
            +
                        data: {
         | 
| 60 | 
            +
                          attributes: attributes,
         | 
| 61 | 
            +
                          type: "profiles",
         | 
| 62 | 
            +
                          relationships: {
         | 
| 63 | 
            +
                            bundleId: {
         | 
| 64 | 
            +
                              data: {
         | 
| 65 | 
            +
                                type: "bundleIds",
         | 
| 66 | 
            +
                                id: bundle_id_id
         | 
| 72 67 | 
             
                              }
         | 
| 73 | 
            -
                             | 
| 68 | 
            +
                            },
         | 
| 69 | 
            +
                            certificates: {
         | 
| 70 | 
            +
                              data: certificates.map do |certificate|
         | 
| 71 | 
            +
                                {
         | 
| 72 | 
            +
                                  type: "certificates",
         | 
| 73 | 
            +
                                  id: certificate
         | 
| 74 | 
            +
                                }
         | 
| 75 | 
            +
                              end
         | 
| 76 | 
            +
                            },
         | 
| 77 | 
            +
                            devices: {
         | 
| 78 | 
            +
                              data: (devices || []).map do |device|
         | 
| 79 | 
            +
                                {
         | 
| 80 | 
            +
                                  type: "devices",
         | 
| 81 | 
            +
                                  id: device
         | 
| 82 | 
            +
                                }
         | 
| 83 | 
            +
                              end
         | 
| 84 | 
            +
                            }
         | 
| 74 85 | 
             
                          }
         | 
| 75 86 | 
             
                        }
         | 
| 76 87 | 
             
                      }
         | 
| 77 | 
            -
                    }
         | 
| 78 88 |  | 
| 79 | 
            -
             | 
| 80 | 
            -
             | 
| 89 | 
            +
                      provisioning_request_client.post("profiles", body)
         | 
| 90 | 
            +
                    end
         | 
| 81 91 |  | 
| 82 | 
            -
             | 
| 83 | 
            -
             | 
| 92 | 
            +
                    def delete_profile(profile_id: nil)
         | 
| 93 | 
            +
                      raise "Profile id is nil" if profile_id.nil?
         | 
| 84 94 |  | 
| 85 | 
            -
             | 
| 95 | 
            +
                      provisioning_request_client.delete("profiles/#{profile_id}")
         | 
| 96 | 
            +
                    end
         | 
| 86 97 | 
             
                  end
         | 
| 87 98 | 
             
                end
         | 
| 88 99 | 
             
              end
         |