Class: Spaceship::Tunes::BuildTrain

Inherits:
TunesBase show all
Defined in:
lib/spaceship/tunes/build_train.rb

Overview

Represents a build train of builds from iTunes Connect A build train is all builds for a given version number with different build numbers

Instance Attribute Summary collapse

Attributes inherited from Base

#client, #raw_data

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from TunesBase

client

Methods inherited from Base

attr_accessor, attr_mapping, #attributes, attributes, #initialize, #inspect, mapping_module, method_missing, set_client, #to_s

Constructor Details

This class inherits a constructor from Spaceship::Base

Instance Attribute Details

#applicationSpaceship::Tunes::Application

Returns A reference to the application this train is for.

Returns:



7
8
9
# File 'lib/spaceship/tunes/build_train.rb', line 7

def application
  @application
end

#buildsArray (readonly)

Returns An array of all builds that are inside this train (Spaceship::Tunes::Build).

Returns:

  • (Array)

    An array of all builds that are inside this train (Spaceship::Tunes::Build)



14
15
16
# File 'lib/spaceship/tunes/build_train.rb', line 14

def builds
  @builds
end

#external_testing_enabledBool (readonly)

Returns Is external beta testing enabled for this train? Only one train can have enabled testing.

Returns:

  • (Bool)

    Is external beta testing enabled for this train? Only one train can have enabled testing.



23
24
25
# File 'lib/spaceship/tunes/build_train.rb', line 23

def external_testing_enabled
  @external_testing_enabled
end

#internal_testing_enabledBool (readonly)

Returns Is internal beta testing enabled for this train? Only one train can have enabled testing.

Returns:

  • (Bool)

    Is internal beta testing enabled for this train? Only one train can have enabled testing.



26
27
28
# File 'lib/spaceship/tunes/build_train.rb', line 26

def internal_testing_enabled
  @internal_testing_enabled
end

#invalid_buildsArray (readonly)

Returns An array of all invalid builds that are inside this train.

Returns:

  • (Array)

    An array of all invalid builds that are inside this train



34
35
36
# File 'lib/spaceship/tunes/build_train.rb', line 34

def invalid_builds
  @invalid_builds
end

#platformString (readonly)

Returns Platform (e.g. “ios”).

Returns:

  • (String)

    Platform (e.g. “ios”)



20
21
22
# File 'lib/spaceship/tunes/build_train.rb', line 20

def platform
  @platform
end

#processing_buildsArray (readonly)

Does not include invalid builds.

I never got this to work to properly try and debug this

Returns:

  • (Array)

    An array of all processing builds that are inside this train (Spaceship::Tunes::Build)



31
32
33
# File 'lib/spaceship/tunes/build_train.rb', line 31

def processing_builds
  @processing_builds
end

#version_setSpaceship::Tunes::VersionSet

Returns A reference to the version set this train is for.

Returns:



11
12
13
# File 'lib/spaceship/tunes/build_train.rb', line 11

def version_set
  @version_set
end

#version_stringString (readonly)

Returns The version number of this train.

Returns:

  • (String)

    The version number of this train



17
18
19
# File 'lib/spaceship/tunes/build_train.rb', line 17

def version_string
  @version_string
end

Class Method Details

.all(application, app_id, platform: nil) ⇒ Object

Parameters:



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/spaceship/tunes/build_train.rb', line 52

def all(application, app_id, platform: nil)
  trains = []
  trains += client.build_trains(app_id, 'internal', platform: platform)['trains']
  trains += client.build_trains(app_id, 'external', platform: platform)['trains']

  result = {}
  trains.each do |attrs|
    attrs[:application] = application
    current = self.factory(attrs)
    if (!platform.nil? && current.platform == platform) || platform.nil?
      result[current.version_string] = current
    end
  end
  result
end

.factory(attrs) ⇒ Object

Create a new object based on a hash. This is used to create a new object based on the server response.



46
47
48
# File 'lib/spaceship/tunes/build_train.rb', line 46

def factory(attrs)
  self.new(attrs)
end

Instance Method Details

#latest_buildSpaceship::Tunes::Build

Returns The latest build for this train, sorted by upload time.

Returns:



115
116
117
# File 'lib/spaceship/tunes/build_train.rb', line 115

def latest_build
  @builds.max_by(&:upload_date)
end

#setupObject

Setup all the builds and processing builds



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/spaceship/tunes/build_train.rb', line 70

def setup
  super

  @builds = (self.raw_data['builds'] || []).collect do |attrs|
    attrs[:build_train] = self
    Tunes::Build.factory(attrs)
  end

  @invalid_builds = @builds.select do |build|
    build.processing_state == 'processingFailed' || build.processing_state == 'invalidBinary'
  end

  # This step may not be necessary anymore - it seems as if every processing build will be caught by the
  # @builds.each below, but not every processing build makes it to buildsInProcessing, so this is redundant
  @processing_builds = (self.raw_data['buildsInProcessing'] || []).collect do |attrs|
    attrs[:build_train] = self
    Tunes::Build.factory(attrs)
  end

  # since buildsInProcessing appears empty, fallback to also including processing state from @builds
  @builds.each do |build|
    # What combination of attributes constitutes which state is pretty complicated. The table below summarizes
    # what I've observed, but there's no reason to believe there aren't more states I just haven't seen yet.
    # The column headers are qualitative states of a given build, and the first column is the observed attributes
    # of that build.
    # NOTE: Some of the builds in the build_trains.json fixture do not follow these rules. I don't know if that is
    # because those examples are older, and the iTC API has changed, or if their format is still a possibility.
    # The second part of the OR clause in the line below exists so that those suspicious examples continue to be
    # accepted for unit tests.
    # +---------------------+-------------------+-------------------+-----------------+--------------------+---------+
    # |                     | just after upload | normal processing | invalid binary  | processing failed  | success |
    # +---------------------+-------------------+-------------------+-----------------+--------------------+---------+
    # |  build.processing = | true              | true              | true            | true               | false   |
    # |       build.valid = | false             | true              | false           | true               | true    |
    # | .processing_state = | "processing"      | "processing"      | "invalidBinary" | "processingFailed" | nil     |
    # +---------------------+-------------------+-------------------+-----------------+--------------------+---------+
    if build.processing_state == 'processing' || (build.processing && build.processing_state != 'invalidBinary' && build.processing_state != 'processingFailed')
      @processing_builds << build
    end
  end

  self.version_set = self.application.version_set_for_platform(self.platform)
end

#update_testing_status!(new_value, testing_type, build = nil) ⇒ Object

Parameters:

  • internal (testing_type)

    or external



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/spaceship/tunes/build_train.rb', line 120

def update_testing_status!(new_value, testing_type, build = nil)
  build ||= latest_build if testing_type == 'external'
  platform = build ? build.platform : self.application.platform
  testing_key = "#{testing_type}Testing"

  data = client.build_trains(self.application.apple_id, testing_type, platform: platform)

  # Delete the irrelevant trains and update the relevant one to enable testing
  data['trains'].delete_if do |train|
    if train['versionString'] != version_string
      true
    else
      train[testing_key]['value'] = new_value

      # also update the builds
      train['builds'].delete_if do |b|
        if b[testing_key].nil?
          true
        elsif build && b["buildVersion"] == build.build_version
          b[testing_key]['value'] = new_value
          false
        elsif b[testing_key]['value'] == true
          b[testing_key]['value'] = false
          false
        else
          true
        end
      end

      false
    end
  end

  begin
    result = client.update_build_trains!(application.apple_id, testing_type, data)
  rescue Spaceship::TunesClient::ITunesConnectError => ex
    if ex.to_s.include?("You must provide an answer for this question")
      # This is a very common error message that's raised by TestFlight
      # We want to show a nicer error message with instructions on how
      # to resolve the underlying issue
      # https://github.com/fastlane/fastlane/issues/1873
      # https://github.com/fastlane/fastlane/issues/4002
      error_message = [""] # to have a nice new-line in the beginning
      error_message << "TestFlight requires you to provide the answer to the encryption question"
      error_message << "to provide the reply, please add the following to your Info.plist file"
      error_message << ""
      error_message << "<key>ITSAppUsesNonExemptEncryption</key><false/>"
      error_message << ""
      error_message << "Afterwards re-build your app and try again"
      error_message << "iTunes Connect reported: '#{ex}'"
      raise error_message.join("\n")
    else
      raise ex
    end
  end
  self.internal_testing_enabled = new_value if testing_type == 'internal'
  self.external_testing_enabled = new_value if testing_type == 'external'

  result
end