class Fastlane::Helper::AppcenterHelper

Constants

MAX_REQUEST_RETRIES

Maximum number of retries for a request

RELEASE_UPLOAD_STATUS_POLL_INTERVAL

Time to wait between 2 status polls in seconds

REQUEST_RETRY_INTERVAL

Delay between retries in seconds

Public Class Methods

add_new_app_to_distribution_group(api_token:, owner_name:, app_name:, destination_name:) click to toggle source

add new created app to existing distribution group

# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 957
def self.add_new_app_to_distribution_group(api_token:, owner_name:, app_name:, destination_name:)
  url = URI.escape("/v0.1/orgs/#{owner_name}/distribution_groups/#{destination_name}/apps")
  body = {
    apps: [
      { name: app_name }
    ]
  }

  UI.message("DEBUG: POST #{url}") if ENV['DEBUG']
  UI.message("DEBUG: POST body #{JSON.pretty_generate(body)}\n") if ENV['DEBUG']

  status, message, response = retry_429_and_error do 
    response = connection.post(url) do |req|
      req.headers['X-API-Token'] = api_token
      req.headers['internal-request-source'] = "fastlane"
      req.body = body
    end
  end

  case status
  when 0, 429
    if status == 0
      UI.error("Faraday http adding to distribution group: #{message}")
    else
      UI.error("Retryable error adding to distribution group: #{status}: #{message}")
    end
  when 200...300
    response.body
    UI.success("Added new app #{app_name} to distribution group #{destination_name}")
  when 401
    UI.user_error!("Auth Error, provided invalid token")
  when 404
    UI.error("Not found, invalid distribution group name #{destination_name}")
  when 409
    UI.success("App already added to distribution group #{destination_name}")
  else
    UI.error("Error adding app to distribution group #{response.status}: #{response.body}")
  end
end
add_to_destination(api_token, owner_name, app_name, release_id, destination_type, destination_id, mandatory_update = false, notify_testers = false) click to toggle source

add release to distribution group or store

# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 691
def self.add_to_destination(api_token, owner_name, app_name, release_id, destination_type, destination_id, mandatory_update = false, notify_testers = false)
  connection = self.connection

  url = "v0.1/apps/#{owner_name}/#{app_name}/releases/#{release_id}/#{destination_type}s"
  body = {
    id: destination_id
  }

  if destination_type == "group"
    body["mandatory_update"] = mandatory_update
    body["notify_testers"] = notify_testers
  end

  UI.message("DEBUG: POST #{url}") if ENV['DEBUG']
  UI.message("DEBUG: POST body #{JSON.pretty_generate(body)}\n") if ENV['DEBUG']

  status, message, response = retry_429_and_error do 
    response = connection.post(url) do |req|
      req.headers['X-API-Token'] = api_token
      req.headers['internal-request-source'] = "fastlane"
      req.body = body
    end
  end

  case status
  when 0, 429
    if status == 0
      UI.error("Faraday http exception adding to destination: #{message}")
    else
      UI.error("Retryable error adding to destination: #{status}: #{message}")
    end
    false
  when 200...300
    # get full release info
    release = self.get_release(api_token, owner_name, app_name, release_id)
    return false unless release

    download_url = release['download_url']

    Actions.lane_context[Fastlane::Actions::SharedValues::APPCENTER_DOWNLOAD_LINK] = download_url
    Actions.lane_context[Fastlane::Actions::SharedValues::APPCENTER_BUILD_INFORMATION] = release

    UI.message("Release '#{release_id}' (#{release['short_version']}) was successfully distributed'")

    release
  when 404
    UI.error("Not found, invalid distribution #{destination_type} name")
    false
  when 401
    UI.user_error!("Auth Error, provided invalid token")
    false
  else
    UI.error("Error adding to #{destination_type} #{response.status}: #{response.body}")
    false
  end
end
connection(upload_url = nil, dsym = false, csv = false) click to toggle source

create request

# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 32
def self.connection(upload_url = nil, dsym = false, csv = false)
  require 'faraday'
  require 'faraday_middleware'

  default_api_url = "https://api.appcenter.ms"
  if ENV['APPCENTER_ENV']&.upcase == 'INT'
    default_api_url = "https://api-gateway-core-integration.dev.avalanch.es"
  end
  options = {
    url: upload_url || default_api_url
  }

  UI.message("DEBUG: BASE URL #{options[:url]}") if ENV['DEBUG']

  Faraday.new(options) do |builder|
    if upload_url
      builder.request :multipart unless dsym
      builder.request :url_encoded unless dsym
    else
      builder.request :json
    end
    builder.response :json, content_type: /\bjson$/ unless csv
    builder.use FaradayMiddleware::FollowRedirects
    builder.adapter :net_http
  end
end
create_app(api_token, owner_type, owner_name, app_name, app_display_name, os, platform) click to toggle source

returns true if app exists, false in case of 404 and error otherwise

# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 787
def self.create_app(api_token, owner_type, owner_name, app_name, app_display_name, os, platform)
  connection = self.connection

  url = owner_type == "user" ? "v0.1/apps" : "v0.1/orgs/#{owner_name}/apps"
  body = {
    display_name: app_display_name,
    name: app_name,
    os: os,
    platform: platform
  }

  UI.message("DEBUG: POST #{url}") if ENV['DEBUG']
  UI.message("DEBUG: POST body #{JSON.pretty_generate(body)}\n") if ENV['DEBUG']

  status, message, response = retry_429_and_error do 
    response = connection.post(url) do |req|
      req.headers['X-API-Token'] = api_token
      req.headers['internal-request-source'] = "fastlane"
      req.body = body
    end
  end

  case status
  when 0, 429
    if status == 0
      UI.error("Faraday http exception creating app: #{message}")
    else
      UI.error("Retryable error creating app: #{status}: #{message}")
    end
    false
  when 200...300
    created = response.body
    UI.success("Created #{os}/#{platform} app with name \"#{created['name']}\" and display name \"#{created['display_name']}\" for #{owner_type} \"#{owner_name}\"")
    true
  when 401
    UI.user_error!("Auth Error, provided invalid token")
    false
  else
    UI.error("Error creating app #{response.status}: #{response.body}")
    false
  end
end
create_dsym_upload(api_token, owner_name, app_name) click to toggle source

creates new dSYM upload in appcenter returns: symbol_upload_id upload_url expiration_date

# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 157
def self.create_dsym_upload(api_token, owner_name, app_name)
  connection = self.connection

  url = "v0.1/apps/#{owner_name}/#{app_name}/symbol_uploads"
  body = {
    symbol_type: 'Apple'
  }

  UI.message("DEBUG: POST #{url}") if ENV['DEBUG']
  UI.message("DEBUG: POST body #{JSON.pretty_generate(body)}\n") if ENV['DEBUG']

  status, message, response = retry_429_and_error do 
    response = connection.post(url) do |req|
      req.headers['X-API-Token'] = api_token
      req.headers['internal-request-source'] = "fastlane"
      req.body = body
    end
  end

  case status
  when 0, 429
    if status == 0
      UI.error("Faraday http exception creating dsym upload: #{message}")
    else
      UI.error("Retryable error creating dsym upload #{status}: #{message}")
    end
    false
  when 200...300
    response.body
  when 401
    UI.user_error!("Auth Error, provided invalid token")
    false
  when 404
    UI.error("Not found, invalid owner or application name")
    false
  else
    UI.error("Error #{response.status}: #{response.body}")
    false
  end
end
create_mapping_upload(api_token, owner_name, app_name, file_name, build_number, version) click to toggle source

creates new mapping upload in appcenter returns: symbol_upload_id upload_url expiration_date

# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 108
def self.create_mapping_upload(api_token, owner_name, app_name, file_name, build_number, version)
  connection = self.connection

  url = "v0.1/apps/#{owner_name}/#{app_name}/symbol_uploads"
  body = {
    symbol_type: "AndroidProguard",
    file_name: file_name,
    build: build_number,
    version: version,
  }

  UI.message("DEBUG: POST #{url}") if ENV['DEBUG']
  UI.message("DEBUG: POST body #{JSON.pretty_generate(body)}\n") if ENV['DEBUG']

  status, message, response = retry_429_and_error do 
    response = connection.post(url) do |req|
      req.headers['X-API-Token'] = api_token
      req.headers['internal-request-source'] = "fastlane"
      req.body = body
    end
  end

  case status
  when 0, 429
    if status == 0
      UI.error("Faraday http exception creating mapping upload: #{message}")
    else
      UI.error("Retryable error creating mapping upload #{status}: #{message}")
    end
    false
  when 200...300
    response.body
  when 401
    UI.user_error!("Auth Error, provided invalid token")
    false
  when 404
    UI.error("Not found, invalid owner or application name")
    false
  else
    UI.error("Error #{response.status}: #{response.body}")
    false
  end
end
create_release_upload(api_token, owner_name, app_name, body) click to toggle source

creates new release upload returns: upload_id upload_url

# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 63
def self.create_release_upload(api_token, owner_name, app_name, body)
  connection = self.connection
  url = "v0.1/apps/#{owner_name}/#{app_name}/uploads/releases"
  body ||= {}

  UI.message("DEBUG: POST #{url}") if ENV['DEBUG']
  UI.message("DEBUG: POST body: #{JSON.pretty_generate(body)}\n") if ENV['DEBUG']

  status, message, response = retry_429_and_error do 
    response = connection.post(url) do |req|
      req.headers['X-API-Token'] = api_token
      req.headers['internal-request-source'] = "fastlane"
      req.body = body
    end
  end

  case status
  when 0, 429
    if status == 0
      UI.error("Faraday http exception creating release upload: #{message}")
    else
      UI.error("Retryable error creating release upload #{status}: #{message}")
    end
    false
  when 200...300
    response.body
  when 401
    UI.user_error!("Auth Error, provided invalid token")
    false
  when 404
    UI.error("Not found, invalid owner or application name")
    false
  when 500...600
    UI.abort_with_message!("Internal Service Error, please try again later")
  else
    UI.error("Error #{response.status}: #{response.body}")
    false
  end
end
fetch_devices(api_token:, owner_name:, app_name:, distribution_group:) click to toggle source
# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 866
def self.fetch_devices(api_token:, owner_name:, app_name:, distribution_group:)
  connection = self.connection(nil, false, true)

  url = "/v0.1/apps/#{owner_name}/#{app_name}/distribution_groups/#{ERB::Util.url_encode(distribution_group)}/devices/download_devices_list"

  UI.message("DEBUG: GET #{url}") if ENV['DEBUG']

  status, message, response = retry_429_and_error do 
    response = connection.get(url) do |req|
      req.headers['X-API-Token'] = api_token
      req.headers['internal-request-source'] = "fastlane"
    end
  end

  case status
  when 0, 429
    if status == 0
      UI.error("Faraday http fetching devices: #{message}")
    else
      UI.error("Retryable error fetching devices: #{status}: #{message}")
    end
    false
  when 200...300
    response.body
  when 401
    UI.user_error!("Auth Error, provided invalid token")
    false
  when 404
    UI.error("Not found, invalid owner, application or distribution group name")
    false
  else
    UI.error("Error #{response.status}: #{response.body}")
    false
  end
end
fetch_distribution_groups(api_token:, owner_name:, app_name:) click to toggle source
# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 830
def self.fetch_distribution_groups(api_token:, owner_name:, app_name:)
  connection = self.connection

  url = "/v0.1/apps/#{owner_name}/#{app_name}/distribution_groups"

  UI.message("DEBUG: GET #{url}") if ENV['DEBUG']

  status, message, response = retry_429_and_error do 
    response = connection.get(url) do |req|
      req.headers['X-API-Token'] = api_token
      req.headers['internal-request-source'] = "fastlane"
    end
  end

  case status
  when 0, 429
    if status == 0
      UI.error("Faraday http fetching destribution groups: #{message}")
    else
      UI.error("Retryable error fetching destribution groups: #{status}: #{message}")
    end
    false
  when 200...300
    response.body
  when 401
    UI.user_error!("Auth Error, provided invalid token")
    false
  when 404
    UI.error("Not found, invalid owner or application name")
    false
  else
    UI.error("Error #{response.status}: #{response.body}")
    false
  end
end
fetch_releases(api_token:, owner_name:, app_name:) click to toggle source
# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 902
def self.fetch_releases(api_token:, owner_name:, app_name:)
  connection = self.connection(nil, false, true)

  url = "/v0.1/apps/#{owner_name}/#{app_name}/releases"

  UI.message("DEBUG: GET #{url}") if ENV['DEBUG']

  status, message, response = retry_429_and_error do 
    response = connection.get(url) do |req|
      req.headers['X-API-Token'] = api_token
      req.headers['internal-request-source'] = "fastlane"
    end
  end

 case status
 when 0, 429
   if status == 0
     UI.error("Faraday http fetching releases: #{message}")
   else
     UI.error("Retryable error fetching releases: #{status}: #{message}")
   end
   false
 when 200...300
   JSON.parse(response.body)
 when 401
   UI.user_error!("Auth Error, provided invalid token")
   false
 when 404
   UI.error("Not found, invalid owner or application name")
   false
 else
   UI.error("Error #{response.status}: #{response.body}")
   false
 end
end
file_extname_full(path) click to toggle source

basic utility method to check file types that App Center will accept, accounting for file types that can and should be zip-compressed before they are uploaded

# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 23
def self.file_extname_full(path)
  %w(.app.zip .dSYM.zip).each do |suffix|
    return suffix if path.to_s.downcase.end_with? suffix.downcase
  end

  File.extname path
end
finish_release_upload(finish_url, api_token, owner_name, app_name, upload_id, timeout) click to toggle source

Verifies a successful upload to App Center returns: successful upload response body.

# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 325
def self.finish_release_upload(finish_url, api_token, owner_name, app_name, upload_id, timeout)
  connection = self.connection(finish_url)

  UI.message("DEBUG: POST #{finish_url}") if ENV['DEBUG']

  status, message, response = retry_429_and_error do 
    response = connection.post do |req|
      req.options.timeout = timeout
      req.headers['internal-request-source'] = "fastlane"
    end
  end

  case status
  when 0, 429
    if status == 0
      UI.error("Faraday http exception finishing release upload: #{message}")
    else
      UI.error("Retryable error finishing release upload #{status}: #{message}")
    end
    false
  when 200...300
    if response.body['error'] == false
      UI.message("Upload finished")
      self.update_release_upload(api_token, owner_name, app_name, upload_id, 'uploadFinished')
    else
      UI.error("Error finishing upload: #{response.body['message']}")
      false
    end
  when 401
    UI.user_error!("Auth Error, provided invalid token")
    false
  else
    UI.error("Error finishing upload: #{response.status}: #{response.body}")
    false
  end
end
get_app(api_token, owner_name, app_name) click to toggle source

returns true if app exists, false in case of 404 and error otherwise

# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 749
def self.get_app(api_token, owner_name, app_name)
  connection = self.connection

  url = "v0.1/apps/#{owner_name}/#{app_name}"

  UI.message("DEBUG: GET #{url}") if ENV['DEBUG']

  status, message, response = retry_429_and_error do 
    response = connection.get(url) do |req|
      req.headers['X-API-Token'] = api_token
      req.headers['internal-request-source'] = "fastlane"
    end
  end

  case status
  when 0, 429
    if status == 0
      UI.error("Faraday http exception getting app: #{message}")
    else
      UI.error("Retryable error getting app: #{status}: #{message}")
    end
    false
  when 200...300
    UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
    true
  when 404
    UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
    false
  when 401
    UI.user_error!("Auth Error, provided invalid token")
    false
  else
    UI.error("Error getting app #{owner_name}/#{app_name}, #{response.status}: #{response.body}")
    false
  end
end
get_destination(api_token, owner_name, app_name, destination_type, destination_name) click to toggle source

get distribution group or store

# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 549
def self.get_destination(api_token, owner_name, app_name, destination_type, destination_name)
  connection = self.connection

  url = "v0.1/apps/#{owner_name}/#{app_name}/distribution_#{destination_type}s/#{ERB::Util.url_encode(destination_name)}"

  UI.message("DEBUG: GET #{url}") if ENV['DEBUG']

  status, message, response = retry_429_and_error do 
    response = connection.get(url) do |req|
      req.headers['X-API-Token'] = api_token
      req.headers['internal-request-source'] = "fastlane"
    end
  end

  case status
  when 0, 429
    if status == 0
      UI.error("Faraday http exception getting destination: #{message}")
    else
      UI.error("Retryable error getting destination: #{status}: #{message}")
    end
    false
  when 200...300
    destination = response.body
    destination
  when 404
    UI.error("Not found, invalid distribution #{destination_type} name")
    false
  when 401
    UI.user_error!("Auth Error, provided invalid token")
    false
  else
    UI.error("Error getting #{destination_type} #{response.status}: #{response.body}")
    false
  end
end
get_install_url(owner_type, owner_name, app_name) click to toggle source
# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 947
def self.get_install_url(owner_type, owner_name, app_name)
  owner_path = owner_type == "user" ? "users/#{owner_name}" : "orgs/#{owner_name}"
  if ENV['APPCENTER_ENV']&.upcase == 'INT'
    return "https://install.portal-server-core-integration.dev.avalanch.es/#{owner_path}/apps/#{app_name}"
  end

  return "https://install.appcenter.ms/#{owner_path}/apps/#{app_name}"
end
get_release(api_token, owner_name, app_name, release_id) click to toggle source

get existing release

# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 469
def self.get_release(api_token, owner_name, app_name, release_id)
  connection = self.connection

  url = "v0.1/apps/#{owner_name}/#{app_name}/releases/#{release_id}"

  UI.message("DEBUG: GET #{url}") if ENV['DEBUG']

  status, message, response = retry_429_and_error do 
    response = connection.get(url) do |req|
      req.headers['X-API-Token'] = api_token
      req.headers['internal-request-source'] = "fastlane"
    end
  end

  case status
  when 0, 429
    if status == 0
      UI.error("Faraday http exception getting release: #{message}")
    else
      UI.error("Retryable error getting release: #{status}: #{message}")
    end
    false
  when 200...300
    release = response.body
    release
  when 404
    UI.error("Not found, invalid release url")
    false
  when 401
    UI.user_error!("Auth Error, provided invalid token")
    false
  else
    UI.error("Error fetching information about release #{response.status}: #{response.body}")
    false
  end
end
get_release_url(owner_type, owner_name, app_name, release_id) click to toggle source
# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 938
def self.get_release_url(owner_type, owner_name, app_name, release_id)
  owner_path = owner_type == "user" ? "users/#{owner_name}" : "orgs/#{owner_name}"
  if ENV['APPCENTER_ENV']&.upcase == 'INT'
    return "https://portal-server-core-integration.dev.avalanch.es/#{owner_path}/apps/#{app_name}/distribute/releases/#{release_id}"
  end

  return "https://appcenter.ms/#{owner_path}/apps/#{app_name}/distribute/releases/#{release_id}"
end
poll_for_release_id(api_token, url) click to toggle source

Polls the upload for a release id. When a release is uploaded, we have to check for a successful extraction before we can continue. returns: release_distinct_id

# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 510
def self.poll_for_release_id(api_token, url)
  connection = self.connection

  while true
    UI.message("DEBUG: GET #{url}") if ENV['DEBUG']

    status, message, response = retry_429_and_error do 
      response = connection.get(url) do |req|
        req.headers['X-API-Token'] = api_token
        req.headers['internal-request-source'] = "fastlane"
      end
    end

    case status
    when 0, 429
      if status == 0
        UI.error("Faraday http exception polling for release id: #{message}")
      else
        UI.error("Retryable error polling for release id: #{status}: #{message}")
      end
      return false
    when 200...300
      case response.body['upload_status']
      when "readyToBePublished"
        return response.body['release_distinct_id']
      when "error"
        UI.error("Error fetching release: #{response.body['error_details']}")
        return false
      else
        sleep(RELEASE_UPLOAD_STATUS_POLL_INTERVAL)
      end
    else
      UI.error("Error fetching information about release #{response.status}: #{response.body}")
      return false
    end
  end
end
retry_429_and_error(&block) click to toggle source
# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 997
def self.retry_429_and_error(&block)
  retries = 0
  status = 0

  # status == 0   - Faraday error
  # status == 429 - retryable error code from server
  while ((status == 0) || (status == 429)) && (retries <= MAX_REQUEST_RETRIES)
    begin
      # calling request sending logic
      response = block.call

      # checking reponse
      status = response.status
      message = response.body
      UI.message("DEBUG: #{status} #{JSON.pretty_generate(message)}\n") if ENV['DEBUG']
    rescue Faraday::Error => e
      status = 0
      message = e.message
    end

    # Pause before retrying
    if (status == 0) || (status == 429)
      sleep(REQUEST_RETRY_INTERVAL)
    end
    
    retries += 1
  end

  return status, message, response
end
set_release_upload_metadata(set_metadata_url, api_token, owner_name, app_name, upload_id, timeout) click to toggle source

sets metadata for new upload in App Center returns: chunk size

# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 283
def self.set_release_upload_metadata(set_metadata_url, api_token, owner_name, app_name, upload_id, timeout)
  connection = self.connection(set_metadata_url)

  UI.message("DEBUG: POST #{set_metadata_url}") if ENV['DEBUG']
  UI.message("DEBUG: POST body <data>\n") if ENV['DEBUG']

  status, message, response = retry_429_and_error do 
    response = connection.post do |req|
      req.options.timeout = timeout
      req.headers['internal-request-source'] = "fastlane"
    end
  end

  case status
  when 0, 429
    if status == 0
      UI.error("Faraday http exception releasing upload metadata: #{message}")
    else
      UI.error("Retryable error releasing upload metadata #{status}: #{message}")
    end
    false
  when 200...300
    chunk_size = response.body['chunk_size']
    unless chunk_size.is_a? Integer
      UI.error("Set metadata didn't return chunk size: #{response.status}: #{response.body}")
      false
    else
      UI.message("Metadata set")
      chunk_size
    end
  when 401
    UI.user_error!("Auth Error, provided invalid token")
    false
  else
    UI.error("Error setting metadata: #{response.status}: #{response.body}")
    false
  end
end
update_release(api_token, owner_name, app_name, release_id, release_notes = '') click to toggle source

add release to destination

# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 587
def self.update_release(api_token, owner_name, app_name, release_id, release_notes = '')
  connection = self.connection

  url = "v0.1/apps/#{owner_name}/#{app_name}/releases/#{release_id}"
  body = {
    release_notes: release_notes
  }

  UI.message("DEBUG: PUT #{url}") if ENV['DEBUG']
  UI.message("DEBUG: PUT body #{JSON.pretty_generate(body)}\n") if ENV['DEBUG']

  status, message, response = retry_429_and_error do 
    response = connection.put(url) do |req|
      req.headers['X-API-Token'] = api_token
      req.headers['internal-request-source'] = "fastlane"
      req.body = body
    end
  end

  case status
  when 0, 429
    if status == 0
      UI.error("Faraday http exception updating release: #{message}")
    else
      UI.error("Retryable error updating release: #{status}: #{message}")
    end
    false
  when 200...300
    # get full release info
    release = self.get_release(api_token, owner_name, app_name, release_id)
    return false unless release

    download_url = release['download_url']

    Actions.lane_context[Fastlane::Actions::SharedValues::APPCENTER_DOWNLOAD_LINK] = download_url
    Actions.lane_context[Fastlane::Actions::SharedValues::APPCENTER_BUILD_INFORMATION] = release

    UI.message("Release '#{release_id}' (#{release['short_version']}) was successfully updated")

    release
  when 404
    UI.error("Not found, invalid release id")
    false
  when 401
    UI.user_error!("Auth Error, provided invalid token")
    false
  else
    UI.error("Error adding updating release #{response.status}: #{response.body}")
    false
  end
end
update_release_metadata(api_token, owner_name, app_name, release_id, dsa_signature = '', ed_signature = '') click to toggle source

updates release metadata

# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 640
def self.update_release_metadata(api_token, owner_name, app_name, release_id, dsa_signature = '', ed_signature = '')
  return if dsa_signature.to_s == '' && ed_signature.to_s == ''

  url = "v0.1/apps/#{owner_name}/#{app_name}/releases/#{release_id}"
  body = {
    metadata: {}
  }
  
  if dsa_signature.to_s != ''
    body[:metadata]["dsa_signature"] = dsa_signature
  end
  if ed_signature.to_s != ''
    body[:metadata]["ed_signature"] = ed_signature
  end

  UI.message("DEBUG: PATCH #{url}") if ENV['DEBUG']
  UI.message("DEBUG: PATCH body #{JSON.pretty_generate(body)}\n") if ENV['DEBUG']

  connection = self.connection

  status, message, response = retry_429_and_error do 
    response = connection.patch(url) do |req|
      req.headers['X-API-Token'] = api_token
      req.headers['internal-request-source'] = "fastlane"
      req.body = body
    end
  end

  case status
  when 0, 429
    if status == 0
      UI.error("Faraday http exception updating release metadata: #{message}")
    else
      UI.error("Retryable error updating release metadata: #{status}: #{message}")
    end
    false
  when 200...300
    UI.message("Release Metadata was successfully updated for release '#{release_id}'")
  when 404
    UI.error("Not found, invalid release id")
    false
  when 401
    UI.user_error!("Auth Error, provided invalid token")
    false
  else
    UI.error("Error adding updating release metadata #{response.status}: #{response.body}")
    false
  end
end
update_release_upload(api_token, owner_name, app_name, upload_id, status) click to toggle source

Commits or aborts the upload process for a release

# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 427
def self.update_release_upload(api_token, owner_name, app_name, upload_id, status)
  connection = self.connection

  url = "v0.1/apps/#{owner_name}/#{app_name}/uploads/releases/#{upload_id}"
  body = {
    upload_status: status,
    id: upload_id
  }

  UI.message("DEBUG: PATCH #{url}") if ENV['DEBUG']
  UI.message("DEBUG: PATCH body #{JSON.pretty_generate(body)}\n") if ENV['DEBUG']

  status, message, response = retry_429_and_error do 
    response = connection.patch(url) do |req|
      req.headers['X-API-Token'] = api_token
      req.headers['internal-request-source'] = "fastlane"
      req.body = body
    end
  end

  case status
  when 0, 429
    if status == 0
      UI.error("Faraday http exception updating release upload: #{message}")
    else
      UI.error("Retryable error updating release upload #{status}: #{message}")
    end
    false
  when 200...300
    response.body
  when 401
    UI.user_error!("Auth Error, provided invalid token")
    false
  when 500...600
    UI.abort_with_message!("Internal Service Error, please try again later")
  else
    UI.error("Error #{response.status}: #{response.body}")
    false
  end
end
update_symbol_upload(api_token, owner_name, app_name, symbol_upload_id, status) click to toggle source

commits or aborts symbol upload

# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 199
def self.update_symbol_upload(api_token, owner_name, app_name, symbol_upload_id, status)
  connection = self.connection

  url = "v0.1/apps/#{owner_name}/#{app_name}/symbol_uploads/#{symbol_upload_id}"
  body = {
    status: status
  }

  UI.message("DEBUG: PATCH #{url}") if ENV['DEBUG']
  UI.message("DEBUG: PATCH body #{JSON.pretty_generate(body)}\n") if ENV['DEBUG']

  status, message, response = retry_429_and_error do 
    response = connection.patch(url) do |req|
      req.headers['X-API-Token'] = api_token
      req.headers['internal-request-source'] = "fastlane"
      req.body = body
    end
  end

  case status
  when 0, 429
    if status == 0
      UI.error("Faraday http exception updating symbol upload: #{message}")
    else
      UI.error("Retryable error updating symbol upload #{status}: #{message}")
    end
    false
  when 200...300
    response.body
  when 401
    UI.user_error!("Auth Error, provided invalid token")
    false
  else
    UI.error("Error #{response.status}: #{response.body}")
    false
  end
end
upload_build(api_token, owner_name, app_name, file, upload_id, upload_url, content_type, chunk_size, timeout) click to toggle source

upload binary for specified upload_url if succeed, then commits the release otherwise aborts

# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 365
def self.upload_build(api_token, owner_name, app_name, file, upload_id, upload_url, content_type, chunk_size, timeout)
  block_number = 1

  File.open(file).each_chunk(chunk_size) do |chunk|
    upload_chunk_url = "#{upload_url}&block_number=#{block_number}"
    retries = 0

    while retries <= MAX_REQUEST_RETRIES
      begin
        connection = self.connection(upload_chunk_url, true)

        UI.message("DEBUG: POST #{upload_chunk_url}") if ENV['DEBUG']
        UI.message("DEBUG: POST body <data>\n") if ENV['DEBUG']
        response = connection.post do |req|
          req.options.timeout = timeout
          req.headers['internal-request-source'] = "fastlane"
          req.headers['Content-Length'] = chunk.length.to_s
          req.headers['Content-Type'] = 'application/octet-stream'
          req.body = chunk
        end
        UI.message("DEBUG: #{response.status} #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
        status = response.status
        message = response.body
      rescue Faraday::Error => e

        # Low level HTTP errors, we will retry them
        status = 0
        message = e.message
      end

      case status
      when 200...300
        if response.body['error'] == false
          UI.message("Chunk uploaded")
          block_number += 1
          break
        else
          UI.error("Error uploading binary #{response.body['message']}")
          return false
        end
      when 401
        UI.user_error!("Auth Error, provided invalid token")
        return false
      when 400...407, 409...428, 430...499
        UI.user_error!("Client error: #{response.status}: #{response.body}")
        return false
      else
        if retries < MAX_REQUEST_RETRIES
          UI.message("DEBUG: Retryable error uploading binary #{status}: #{message}")
          retries += 1
          sleep(REQUEST_RETRY_INTERVAL)
        else
          UI.error("Error uploading binary #{status}: #{message}")
          return false
        end
      end
    end
  end
  UI.message("Binary uploaded")
end
upload_symbol(api_token, owner_name, app_name, symbol, symbol_type, symbol_upload_id, upload_url) click to toggle source

upload symbol (dSYM or mapping) files to specified upload url if succeed, then commits the upload otherwise aborts

# File lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb, line 240
def self.upload_symbol(api_token, owner_name, app_name, symbol, symbol_type, symbol_upload_id, upload_url)
  connection = self.connection(upload_url, true)

  UI.message("DEBUG: PUT #{upload_url}") if ENV['DEBUG']
  UI.message("DEBUG: PUT body <data>\n") if ENV['DEBUG']

  log_type = "dSYM" if symbol_type == "Apple"
  log_type = "mapping" if symbol_type == "Android"

  status, message, response = retry_429_and_error do 
    response = connection.put do |req|
      req.headers['x-ms-blob-type'] = "BlockBlob"
      req.headers['Content-Length'] = File.size(symbol).to_s
      req.headers['internal-request-source'] = "fastlane"
      req.body = Faraday::UploadIO.new(symbol, 'application/octet-stream') if symbol && File.exist?(symbol)
    end
  end

  case status
  when 0, 429
    if status == 0
      UI.error("Faraday http exception updating symbol upload: #{message}")
    else
      UI.error("Retryable error updating symbol upload #{status}: #{message}")
    end
    false
  when 200...300
    self.update_symbol_upload(api_token, owner_name, app_name, symbol_upload_id, 'committed')
    UI.success("#{log_type} uploaded")
  when 401
    UI.user_error!("Auth Error, provided invalid token")
    false
  else
    UI.error("Error uploading #{log_type} #{response.status}: #{response.body}")
    self.update_symbol_upload(api_token, owner_name, app_name, symbol_upload_id, 'aborted')
    UI.error("#{log_type} upload aborted")
    false
  end
end