module Spaceship module Tunes # Represents a build which is inside the build train class Build < TunesBase ##################################################### # @!group General metadata ##################################################### # @return (Spaceship::Tunes::BuildTrain) A reference to the build train this build is contained in attr_accessor :build_train # @return (Integer) The ID generated by iTunes Connect attr_accessor :id # @return (Boolean) attr_accessor :valid # @return (String) The build version (not the version number), but also is named `build number` attr_accessor :build_version # @return (String) The version number (e.g. 1.3) attr_accessor :train_version # @return (Boolean) Is this build currently processing? attr_accessor :processing # @return (Integer) The number of ticks since 1970 (e.g. 1413966436000) attr_accessor :upload_date # @return (String) URL to the app icon of this build (150x150px) attr_accessor :icon_url # @return (String) The name of the app this build is for attr_accessor :app_name # @return (String) The platform of this build (e.g. 'ios') attr_accessor :platform # @return (Integer) When is this build going to be invalid attr_accessor :internal_expiry_date # @return (Integer): When is the external build going to expire? attr_accessor :external_expiry_date # @return (Bool) Is external beta testing enabled for this train? Only one train can have enabled testing. attr_reader :external_testing_enabled # @return (Bool) Is internal beta testing enabled for this train? Only one train can have enabled testing. attr_reader :internal_testing_enabled # @return (Bool) Does this build support WatchKit? attr_accessor :watch_kit_enabled # @return (Bool): attr_accessor :ready_to_install ##################################################### # @!group Analytics ##################################################### # @return (Integer) Number of installs of this build attr_accessor :install_count # @return (Integer) Number of installs for this build that come from internal users attr_accessor :internal_install_count # @return (Integer) Number of installs for this build that come from external users attr_accessor :external_install_count # @return (Integer) Might be nil. The number of sessions for this build attr_accessor :session_count # @return (Integer) Might be nil. The number of crashes of this build attr_accessor :crash_count attr_mapping( 'uploadDate' => :upload_date, 'iconUrl' => :icon_url, 'buildVersion' => :build_version, 'trainVersion' => :train_version, 'appName' => :app_name, 'platform' => :platform, 'id' => :id, 'valid' => :valid, 'processing' => :processing, 'installCount' => :install_count, 'internalInstallCount' => :internal_install_count, 'externalInstallCount' => :external_install_count, 'sessionCount' => :session_count, 'crashCount' => :crash_count, 'internalExpiry' => :internal_expiry_date, 'externalExpiry' => :external_expiry_date, 'watchKitEnabled' => :watch_kit_enabled, 'readyToInstall' => :ready_to_install, 'internalTesting.value' => :internal_testing_enabled, 'externalTesting.value' => :external_testing_enabled ) class << self # Create a new object based on a hash. # This is used to create a new object based on the server response. def factory(attrs) self.new(attrs) end end def setup super self.external_expiry_date ||= 0 self.internal_expiry_date ||= 0 end def update_build_information!(whats_new: nil, description: nil, feedback_email: nil) parameters = { app_id: self.build_train.application.apple_id, train: self.build_train.version_string, build_number: self.build_version, platform: self.platform }.merge({ whats_new: whats_new, description: description, feedback_email: feedback_email }) client.update_build_information!(parameters) end # This will submit this build for TestFlight beta review # @param metadata [Hash] A hash containing the following information (keys must be symbols): # { # # Required Metadata: # changelog: "Changelog", # description: "Your Description", # feedback_email: "Email Address for Feedback", # marketing_url: "https://marketing.com", # first_name: "Felix", # last_name: "Krause", # review_email: "Contact email address for Apple", # phone_number: "0128383383", # # # Optional Metadata: # privacy_policy_url: nil, # review_notes: nil, # review_user_name: nil, # review_password: nil, # encryption: false # } def submit_for_beta_review!(metadata) parameters = { app_id: self.build_train.application.apple_id, train: self.build_train.version_string, build_number: self.build_version, platform: self.platform, # Required Metadata: changelog: "No changelog provided", description: "No app description provided", feedback_email: "contact@company.com", marketing_url: "http://marketing.com", first_name: "Felix", last_name: "Krause", review_email: "contact@company.com", phone_number: "0123456789", significant_change: false, # Optional Metadata: privacy_policy_url: nil, review_user_name: nil, review_password: nil, encryption: false }.merge(metadata) client.submit_testflight_build_for_review!(parameters) # Last, enable beta testing for this train (per iTC requirement). This will fail until the app has been approved for beta testing self.build_train.update_testing_status!(true, 'external', self) return parameters end # @return [String] A nicely formatted string about the state of this build # @examples: # External, Internal, Inactive, Expired def testing_status testing ||= "External" if self.external_testing_enabled testing ||= "Internal" if self.internal_testing_enabled if Time.at(self.internal_expiry_date / 1000) > Time.now testing ||= "Inactive" else testing = "Expired" end return testing end # This will cancel the review process for this TestFlight build def cancel_beta_review! client.remove_testflight_build_from_review!(app_id: self.build_train.application.apple_id, train: self.build_train.version_string, build_number: self.build_version, platform: self.platform) end end end end # Example Response # {"sectionErrorKeys"=>[], # "sectionInfoKeys"=>[], # "sectionWarningKeys"=>[], # "buildVersion"=>"1", # "trainVersion"=>"1.0", # "uploadDate"=>1441975590000, # "iconUrl"=> # "https://is2-ssl.mzstatic.com/image/thumb/Newsstand3/v4/a9/f9/8b/a9f98b23-592d-af2e-6e10-a04873bed5df/Icon-76@2x.png.png/150x150bb-80.png", # "iconAssetToken"=> # "Newsstand3/v4/a9/f9/8b/a9f98b23-592d-af2e-6e10-a04873bed5df/Icon-76@2x.png.png", # "appName"=>"Updated by fastlane", # "platform"=>"ios", # "betaEntitled"=>true, # "exceededFileSizeLimit"=>false, # "wentLiveWithVersion"=>false, # "processing"=>false, # "id"=>5298023, # "valid"=>true, # "missingExportCompliance"=>false, # "waitingForExportComplianceApproval"=>false, # "addedInternalUsersCount"=>0, # "addedExternalUsersCount"=>0, # "invitedExternalUsersCount"=>0, # "invitedInternalUsersCount"=>0, # "acceptedInternalUsersCount"=>1, # "acceptedExternalUsersCount"=>0, # "installCount"=>0, # "internalInstallCount"=>0, # "externalInstallCount"=>0, # "sessionCount"=>0, # "internalSessionCount"=>0, # "externalSessionCount"=>0, # "crashCount"=>0, # "internalCrashCount"=>0, # "externalCrashCount"=>0, # "promotedVersion"=>nil, # "internalState"=>"inactive", # "betaState"=>"submitForReview", # "internalExpiry"=>1444567590000, # "externalExpiry"=>0, # "watchKitEnabled"=>false, # "readyToInstall"=>true, # "sdkBuildWhitelisted"=>true, # "internalTesting"=>{"value"=>false, "isEditable"=>true, "isRequired"=>false, "errorKeys"=>nil}, # "externalTesting"=>{"value"=>false, "isEditable"=>true, "isRequired"=>false, "errorKeys"=>nil}