# This file contains the fastlane.tools configuration
# You can find the documentation at https://docs.fastlane.tools
#
# For a list of all available actions, check out
#
#     https://docs.fastlane.tools/actions
#
# Fastlane is updated quite frequently with security patches
# update_fastlane

# There are a few env variables defined in the .env file in
# this directory (fastlane/.env)


# unlocks keychain if KEYCHAIN_PASSWORD variable is present
# (to be used on CI machines)
def unlock_keychain_if_needed
    if ENV["KEYCHAIN_PASSWORD"]
        unlock_keychain(
            path:"login.keychain",
            password:ENV["KEYCHAIN_PASSWORD"],
            set_default: true)
    end
end

def curl_upload(url, file, user, conn_timeout=5, timeout=60, retries=3)
    begin
        return sh(
            "curl",
            "--fail",
            "--silent",
            "--user", user,
            "--request", "POST",
            "--header", "Content-Type: application/octet-stream",
            "--data-binary", "@../#{file}",         # `fastlane` is the cwd so we go one folder up
            # we retry few times if upload doesn't succeed in sensible time
            "--retry-connrefused",                  # consider ECONNREFUSED as error too retry
            "--connect-timeout", conn_timeout.to_s, # max time in sec. for establishing connection 
            "--max-time", timeout.to_s,             # max time in sec. for whole transfer to take
            "--retry", retries.to_s,                # number of retries to attempt
            "--retry-max-time", timeout.to_s,       # same as --max-time but for retries
            "--retry-delay", "0",                   # an exponential backoff algorithm in sec.
            url
        )
    rescue => error
        UI.error "Error: #{error}"
        raise
    end
end

def retry_curl_upload(url, file, user, conn_timeout=5, timeout=60, retries=3)
    # since curl doesn't retry on connection and operation timeouts we roll our own
    try = 0
    begin
        return curl_upload(url, file, user, conn_timeout, timeout, retries)
    rescue => error
        try += 1
        if try <= retries
            UI.important "Warning: Retrying cURL upload! (attempt #{try}/#{retries})"
            retry
        else
            raise
        end
    end
end

# uploads `file` to sauce labs (overwrites if there is anoter file from the
# same commit)
def upload_to_saucelabs(file)
    key = ENV["SAUCE_ACCESS_KEY"]
    username = ENV["SAUCE_USERNAME"]
    unique_name = ENV["SAUCE_LABS_NAME"]

    url = "https://saucelabs.com/rest/v1/storage/#{username}/#{unique_name}?overwrite=true"

    upload_result = retry_curl_upload(url, file, "#{username}:#{key}")

    # fail the lane if upload fails
    UI.user_error!(
        "failed to upload file to saucelabs despite retries: #{upload_result}"
    ) unless upload_result.include? "filename"
end


# builds an ios app with ad-hoc configuration and put it
# to "status-adhoc" output folder
# `readonly`:
#    if true  - only fetch existing certificates and profiles, don't upgrade from AppStoreConnect
#    if false - read list of devices from AppStoreConnect, and upgrade the provisioning profiles from it
def build_ios_adhoc(readonly)
    match(
        type: "adhoc",
        force_for_new_devices: true,
        readonly: readonly,
        keychain_name: "login.keychain"
    )

    build_ios_app(
        scheme: "StatusIm",
        workspace: "ios/StatusIm.xcworkspace",
        configuration: "Release",
        clean: true,
        export_method: "ad-hoc",
        # Temporary fix for Xcode 10.1
        xcargs: "-UseModernBuildSystem=N",
        output_directory: "status-adhoc",
        export_options: {
            "UseModernBuildSystem": "N"
        }
    )
end

# builds an ios app with e2e configuration and put it
# to "status-e2e" output folder
def build_ios_e2e

    # determine a simulator SDK installed
    showsdks_output = sh('xcodebuild', '-showsdks')
    simulator_sdk = showsdks_output.scan(/iphonesimulator\d\d?\.\d\d?/).first

    match(
        type: "adhoc",
        force_for_new_devices: true,
        readonly: true,
        keychain_name: "login.keychain"
    )


    build_ios_app(
        # Creating a build for the iOS Simulator
        # 1. https://medium.com/rocket-fuel/fastlane-to-the-simulator-87549b2601b9
        sdk: simulator_sdk,
        destination: "generic/platform=iOS Simulator",
        # 2. fixing compilations issues as stated in https://stackoverflow.com/a/20505258
        #    it looks like i386 isn't supported by React Native
        # UseModernBuildSystem: Temporary fix for Xcode 10.1
        xcargs: "ARCHS=\"x86_64\" ONLY_ACTIVE_ARCH=NO -UseModernBuildSystem=N",
        # 3. directory where to up StatusIm.app
        derived_data_path: "status-e2e",
        output_name: "StatusIm.app",
        # -------------------------------------
        # Normal stuff
        scheme: "StatusIm",
        workspace: "ios/StatusIm.xcworkspace",
        configuration: "Release",
        # Simulator apps can't be archived...
        skip_archive: true,
        # ...and we don't need an .ipa file for them, because we use .app directly
        skip_package_ipa: true,
        export_options: {
            "UseModernBuildSystem": "N"
        }
    )

    zip(
        path: "status-e2e/Build/Products/Release-iphonesimulator/StatusIm.app",
        output_path: "status-e2e/StatusIm.app.zip",
        verbose: false,
    )
end

def upload_to_diawi(source)
    diawi(
        file: source,
        last_hope_attempts_count: 3,
        token: ENV["DIAWI_TOKEN"]
    )
    # save the URL to a file for use in CI
    File.write("diawi.out", lane_context[SharedValues::UPLOADED_FILE_LINK_TO_DIAWI])
end


platform :ios do
  desc "`fastlane ios adhoc` - ad-hoc lane for iOS."
  desc "This lane is used for PRs, Releases, etc."
  desc "It creates an .ipa that can be used by a list of devices, registeded in the App Store Connect."
  desc "This .ipa is ready to be distibuted through diawi.com"
  lane :adhoc do
    unlock_keychain_if_needed
    build_ios_adhoc(true)
  end

  desc "`fastlane ios e2e` - e2e lane for iOS."
  desc "This lane is used for SauceLabs end-to-end testing."
  desc "It creates an .app that can be used inside of a iPhone simulator."
  lane :e2e do
    unlock_keychain_if_needed
    build_ios_e2e
  end

  desc "`fastlane ios pr` - makes a new pr build"
  desc "This lane builds a new adhoc build and leaves an .ipa that is ad-hoc signed (can be uploaded to diawi)"
  lane :pr do
    unlock_keychain_if_needed
    build_ios_adhoc(false)
  end

  desc "`fastlane ios nightly` - makes a new nightly"
  desc "This lane builds a new nightly and leaves an .ipa that is ad-hoc signed (can be uploaded to diawi)"
  lane :nightly do
    unlock_keychain_if_needed
    build_ios_adhoc(false)
  end

  desc "`fastlane ios release` builds a release & uploads it to TestFlight"
  lane :release do
    match(
        type: "appstore",
        readonly: true,
        keychain_name: "login.keychain"
    )

    build_ios_app(
        scheme: "StatusIm",
        workspace: "ios/StatusIm.xcworkspace",
        configuration: "Release",
        clean: true,
        export_method: "app-store",
        output_directory: "status_appstore",
        include_symbols: false,
        # Temporary fix for Xcode 10.1
        xcargs: "-UseModernBuildSystem=N",
        export_options: {
            "UseModernBuildSystem": "N",
            "combileBitcode": true,
            "uploadBitcode": false,
            "ITSAppUsesNonExemptEncryption": false
        }
    )
    upload_to_testflight(
        ipa: "status_appstore/StatusIm.ipa"
    )
  end

  desc "`fastlane ios clean` - remove inactive TestFlight users"
  desc "uses custom plugin, installed via"
  desc "`sudo get install fastlane-plugin-clean_testflight_testers`"
  lane :clean do
    clean_testflight_testers(username: ENV["FASTLANE_APPLE_ID"])
  end

  desc "`fastlane ios upload-diawi` - upload .ipa to diawi"
  desc "expects to have an .ipa prepared: `status-adhoc/StatusIm.ipa`"
  desc "expects to have a diawi token as DIAWI_TOKEN env variable"
  desc "expects to have a github token as GITHUB_TOKEN env variable"
  desc "will fails if file isn't there"
  desc "---"
  desc "Output: writes `fastlane/diawi.out` file url of the uploded file"
  lane :upload_diawi do
    ipa = ENV["DIAWI_IPA"] || "status-adhoc/StatusIm.ipa"
    upload_to_diawi(ipa)
  end

  desc "`fastlane ios saucelabs` - upload .app to sauce labs"
  desc "also notifies in a GitHub comments"
  desc "expects to have an .apk prepared: `android/app/build/outputs/apk/release/app-release.apk`"
  desc "expects to have a saucelabs access key as SAUCE_ACCESS_KEY env variable"
  desc "expects to have a saucelabs username token as SAUCE_USERNAME env variable"
  desc "expects to have a saucelabs destination name as SAUCE_LABS_NAME env variable"
  desc "will fails if file isn't there"
  lane :saucelabs do
    upload_to_saucelabs(
        "status-e2e/StatusIm.app.zip"
    )
  end

  desc "This fastlane step cleans up XCode DerivedData folder"
  lane :cleanup do
    clear_derived_data
  end
end


platform :android do
  # Optional env variables
  APK_PATH = ENV["APK_PATH"] || "android/app/build/outputs/apk/release/app-release.apk"

  desc "Deploy a new internal build to Google Play"
  desc "expects GOOGLE_PLAY_JSON_KEY environment variable"
  lane :nightly do
    upload_to_play_store(
      track: "internal",
      apk: APK_PATH,
      json_key_data: ENV["GOOGLE_PLAY_JSON_KEY"]
    )

  end

  desc "Deploy a new alpha (public) build to Google Play"
  desc "expects GOOGLE_PLAY_JSON_KEY environment variable"
  lane :release do
    upload_to_play_store(
      track: "alpha",
      apk: APK_PATH,
      json_key_data: ENV["GOOGLE_PLAY_JSON_KEY"]
    )
  end

  desc "Upload metadata to Google Play."
  desc "Metadata is always updated when builds are uploaded,"
  desc "but this action can update metadata without uploading a build."
  desc "expects GOOGLE_PLAY_JSON_KEY environment variable"
  lane :upload_metadata do
    upload_to_play_store(
      skip_upload_apk: true,
      json_key_data: ENV["GOOGLE_PLAY_JSON_KEY"]
    )
  end

  desc "`fastlane android upload_diawi` - upload .apk to diawi"
  desc "expects to have an .apk prepared: `android/app/build/outputs/apk/release/app-release.apk`"
  desc "expects to have a diawi token as DIAWI_TOKEN env variable"
  desc "expects to have a github token as GITHUB_TOKEN env variable"
  desc "will fails if file isn't there"
  desc "---"
  desc "Output: writes `fastlane/diawi.out` file url of the uploded file"
  lane :upload_diawi do
    upload_to_diawi(APK_PATH)
  end

  desc "`fastlane android saucelabs` - upload .apk to sauce labs"
  desc "expects to have an .apk prepared: `android/app/build/outputs/apk/release/app-release.apk`"
  desc "expects to have a saucelabs access key as SAUCE_ACCESS_KEY env variable"
  desc "expects to have a saucelabs username token as SAUCE_USERNAME env variable"
  desc "expects to have a saucelabs destination name as SAUCE_LABS_NAME env variable"
  desc "will fails if file isn't there"
  lane :saucelabs do
    upload_to_saucelabs(APK_PATH)
  end
end