Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sigh repair fails with "undefined method `id' for nil:NilClass (NoMethodError) certificates.map(&:id)" with Apple Distribution certificate type #21295

Open
4 tasks done
rpitting opened this issue May 22, 2023 · 2 comments

Comments

@rpitting
Copy link

rpitting commented May 22, 2023

New Issue Checklist

Issue Description

sigh repair fails to repair provisioning profiles with a ruby error

Command executed
bundle exec fastlane sigh repair --username [redacted]
Complete output when running fastlane, including the stack trace and command used
buildserver@iosbuildserver03 % bundle exec fastlane sigh repair --username [redacted]         

[✔] 🚀
[12:40:03]: Starting login with user '[redacted]'
[12:40:04]: Successfully logged in
[12:40:05]: Going to repair 32 provisioning profiles
[12:40:05]: Repairing profile '[redacted] Widget DEV'...

Looking for related GitHub issues on fastlane/fastlane...

➡️ when I run cordova run , it have a error
#7467 [closed] 2 💬
14 May 2017

➡️ "Could not find app with app identifier" when init Mac project
#7480 [closed] 5 💬
22 Mar 2017

➡️ How can I turnoff the password re-enter and just get the error when the password is incorrect?
#7484 [closed] 5 💬
24 May 2017

and 19675 more at: https://github.com/fastlane/fastlane/search?q=undefined%20method%20%60id%27%20for%20nil%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20certificates.map%28%26%3Aid%29%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5E%5E%5E%5E&type=Issues&utf8=✓

🔗 You can ⌘ + double-click on links to open them directly in your browser.
bundler: failed to load command: fastlane (/Users/buildserver/.rbenv/versions/3.1.2/bin/fastlane)
/Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/spaceship/lib/spaceship/portal/provisioning_profile.rb:456:in map': \e[31m[!] undefined method id' for nil:NilClass (NoMethodError)

        certificates.map(&:id),
                    ^^^^\e[0m
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/spaceship/lib/spaceship/portal/provisioning_profile.rb:456:in `block in update!'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/spaceship/lib/spaceship/client.rb:688:in `with_retry'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/spaceship/lib/spaceship/portal/provisioning_profile.rb:450:in `update!'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/spaceship/lib/spaceship/portal/provisioning_profile.rb:418:in `repair!'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/sigh/lib/sigh/repair.rb:28:in `block in repair_all'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/sigh/lib/sigh/repair.rb:26:in `each'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/sigh/lib/sigh/repair.rb:26:in `repair_all'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/sigh/lib/sigh/commands_generator.rb:97:in `block (2 levels) in run'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/commander-4.6.0/lib/commander/command.rb:187:in `call'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/commander-4.6.0/lib/commander/command.rb:157:in `run'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/commander-4.6.0/lib/commander/runner.rb:444:in `run_active_command'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/fastlane_core/lib/fastlane_core/ui/fastlane_runner.rb:124:in `run!'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/commander-4.6.0/lib/commander/delegates.rb:18:in `run!'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/sigh/lib/sigh/commands_generator.rb:140:in `run'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/sigh/lib/sigh/commands_generator.rb:17:in `start'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/fastlane/lib/fastlane/cli_tools_distributor.rb:115:in `take_off'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/bin/fastlane:23:in `<top (required)>'
from /Users/buildserver/.rbenv/versions/3.1.2/bin/fastlane:25:in `load'
from /Users/buildserver/.rbenv/versions/3.1.2/bin/fastlane:25:in `<top (required)>'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/lib/bundler/cli/exec.rb:58:in `load'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/lib/bundler/cli/exec.rb:58:in `kernel_load'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/lib/bundler/cli/exec.rb:23:in `run'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/lib/bundler/cli.rb:486:in `exec'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/lib/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/lib/bundler/vendor/thor/lib/thor/invocation.rb:127:in `invoke_command'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/lib/bundler/vendor/thor/lib/thor.rb:392:in `dispatch'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/lib/bundler/cli.rb:31:in `dispatch'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/lib/bundler/vendor/thor/lib/thor/base.rb:485:in `start'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/lib/bundler/cli.rb:25:in `start'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/exe/bundle:48:in `block in <top (required)>'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/lib/bundler/friendly_errors.rb:120:in `with_friendly_errors'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/exe/bundle:36:in `<top (required)>'
from /Users/buildserver/.rbenv/versions/3.1.2/bin/bundle:25:in `load'
from /Users/buildserver/.rbenv/versions/3.1.2/bin/bundle:25:in `<main>'

/Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/spaceship/lib/spaceship/portal/provisioning_profile.rb:456:in map': undefined method id' for nil:NilClass (NoMethodError)

        certificates.map(&:id),
                    ^^^^
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/spaceship/lib/spaceship/portal/provisioning_profile.rb:456:in `block in update!'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/spaceship/lib/spaceship/client.rb:688:in `with_retry'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/spaceship/lib/spaceship/portal/provisioning_profile.rb:450:in `update!'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/spaceship/lib/spaceship/portal/provisioning_profile.rb:418:in `repair!'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/sigh/lib/sigh/repair.rb:28:in `block in repair_all'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/sigh/lib/sigh/repair.rb:26:in `each'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/sigh/lib/sigh/repair.rb:26:in `repair_all'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/sigh/lib/sigh/commands_generator.rb:97:in `block (2 levels) in run'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/commander-4.6.0/lib/commander/command.rb:187:in `call'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/commander-4.6.0/lib/commander/command.rb:157:in `run'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/commander-4.6.0/lib/commander/runner.rb:444:in `run_active_command'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/fastlane_core/lib/fastlane_core/ui/fastlane_runner.rb:124:in `run!'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/commander-4.6.0/lib/commander/delegates.rb:18:in `run!'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/sigh/lib/sigh/commands_generator.rb:140:in `run'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/sigh/lib/sigh/commands_generator.rb:17:in `start'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/fastlane/lib/fastlane/cli_tools_distributor.rb:115:in `take_off'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/fastlane-2.212.2/bin/fastlane:23:in `<top (required)>'
from /Users/buildserver/.rbenv/versions/3.1.2/bin/fastlane:25:in `load'
from /Users/buildserver/.rbenv/versions/3.1.2/bin/fastlane:25:in `<top (required)>'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/lib/bundler/cli/exec.rb:58:in `load'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/lib/bundler/cli/exec.rb:58:in `kernel_load'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/lib/bundler/cli/exec.rb:23:in `run'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/lib/bundler/cli.rb:486:in `exec'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/lib/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/lib/bundler/vendor/thor/lib/thor/invocation.rb:127:in `invoke_command'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/lib/bundler/vendor/thor/lib/thor.rb:392:in `dispatch'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/lib/bundler/cli.rb:31:in `dispatch'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/lib/bundler/vendor/thor/lib/thor/base.rb:485:in `start'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/lib/bundler/cli.rb:25:in `start'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/exe/bundle:48:in `block in <top (required)>'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/lib/bundler/friendly_errors.rb:120:in `with_friendly_errors'
from /Users/buildserver/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bundler-2.3.24/exe/bundle:36:in `<top (required)>'
from /Users/buildserver/.rbenv/versions/3.1.2/bin/bundle:25:in `load'

Environment

✅ fastlane environment ✅

Stack

Key Value
OS 13.3.1
Ruby 3.1.2
Bundler? true
Git git version 2.39.0
Installation Source ~/.rbenv/versions/3.1.2/bin/fastlane
Host macOS 13.3.1 ((a))
Ruby Lib Dir ~/.rbenv/versions/3.1.2/lib
OpenSSL Version OpenSSL 3.0.7 1 Nov 2022
Is contained false
Is homebrew false
Is installed via Fabric.app false
Xcode Path /Applications/Xcode.app/Contents/Developer/
Xcode Version 14.2
Swift Version 5.7.2

System Locale

Variable Value
LANG de_DE.UTF-8
LC_ALL
LANGUAGE

fastlane files:

`./fastlane/Fastfile`
# Customise this file, documentation can be found here:
# https://github.com/fastlane/fastlane/tree/master/fastlane/docs
# All available actions: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Actions.md
# can also be listed using the `fastlane actions` command

# More information about multiple platforms in fastlane: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Platforms.md
# All available actions: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Actions.md

# fastlane reports which actions are used
# No personal data is recorded. Learn more at https://github.com/fastlane/enhancer

# Change the syntax highlighting to Ruby
# All lines starting with a # are ignored when running `fastlane`

# If you want to automatically update fastlane if a new version is available:
# update_fastlane

# This is the minimum version number required.
# Update this, if you use features of a newer version
fastlane_require 'yaml'
fastlane_require './helpers/config.rb'
fastlane_require 'spaceship'

default_platform :ios

# Webhooks configured by rpittinger

# Define global Xcode path to easy test new versions (Xcode.app vs. Xcode-beta.app). xversion alone did not work.
ENV['XCODE_PATH'] = '/Applications/Xcode.app'

before_all do |lane, options|
  # Set Xcode to use
  xcode_select(ENV['XCODE_PATH'])

  lanes_without_load_config = [
    :test,
    :test_nightly,
    :test_exaframeworks,
    :test_nightly_multiple_devices,
    :danger,
    :prepare_release
  ]

  # Load config, except for test lanes and danger
  load_config(brand: options[:brand], flavor: options[:flavor]) unless lanes_without_load_config.include?(lane)
end

######################### GENERAL LANES #########################

error do |lane, _exception|
  platform = lane_context[SharedValues::PLATFORM_NAME]
  config = lane_context[SharedValues::WAIPU_CONFIG]

  if lane == 'build'
    # Switch back to automatic code signing
    # This is useful for when adjust build process to start with the "debug" build settings (that means automatic code signing)
    xcodeproj_path = get_xcodeproj_path(platform.to_s, config)
    update_code_signing_settings(
      path: xcodeproj_path,
      use_automatic_signing: true
    )
    
    slack(
      # message with short human friendly message
      message: exception.to_s, 
      success: false, 
      # Output containing extended log output
      payload: { "Output" => exception.error_info.to_s } 
    )
  end
end

desc 'Makes preparations before running unit tests'
private_lane :prepare_test do
  clear_derived_data

  create_devices(
    devices: [
      test_devices('ios'),
      test_devices('tvos')
    ]
  )
end

def latest_os_version_for_testing(platform)
  case platform
  when "ios"
    '16.2'
  when "tvos"
    '16.1'
  else
    raise "Invalid platform: #{platform}"
  end
end

def primary_test_device(platform)
  device = nil

  case platform
  when "ios"
    device = "iPhone 13"
  when "tvos"
    device = 'Apple TV'
  else
    raise "Invalid platform: #{platform}"
  end

  latest_os_version = latest_os_version_for_testing(platform)

  return "#{device} (#{latest_os_version})"
end

def test_destination(platform)
  case platform
  when "ios"
    return "platform=iOS Simulator,name=#{primary_test_device(platform)},OS=#{latest_os_version_for_testing(platform)}"
  when "tvos"
    return "platform=tvOS Simulator,name=#{primary_test_device(platform)},OS=#{latest_os_version_for_testing(platform)}"
  else
    raise "Invalid platform: #{platform}"
  end
end

def test_devices(platform)
  latest_os_version = latest_os_version_for_testing(platform)

  case platform
  when "ios"
    [
      primary_test_device(platform),
      "iPad Pro (10.5-inch) (#{latest_os_version})",
    ]
  when "tvos"
    [
      primary_test_device(platform)
    ]
  else
    raise "Invalid platform: #{platform}"
  end
end

desc 'Runs all the tests'
lane :test do |options|
  prepare_test unless options[:skip_prepare]

  # tvOS

  # 2022-02-09 rpitting
  # We don't use the device paramter of scan (anymore) because xcodebuild had problems
  # to find the matching simulator, complaining about "multiple destinations",
  # although specified with a clear, unique simulator name. In addition, the command
  # was rereun by fastline multiple times until the actual scan process started.

  tvos_test_schemes = %w(
    ExaBaseTVTests
    o2-tvosTests
    waipu-tvosTests
  )

  tvos_test_schemes.each do |scheme|
    scan(
      scheme: scheme,
      destination: test_destination('tvos'),
      clean: false,
      skip_slack: true
    )
  end

  # iOS

  ios_test_schemes = %w(
    ExaBaseMobileTests
    o2-iosTests
    waipu-iosTests
  )

  ios_test_schemes.each do |scheme|
    scan(
      scheme: scheme,
      destination: test_destination('ios'),
      clean: false,
      skip_slack: true
    )
  end

  test_exaframeworks(
    destination: test_destination('ios'),
  )
end

lane :test_exaframeworks do |options|
  destination = options[:destination]
  configuration = options[:configuration]

  frameworks = %w(
    NoGCDWebServerTests
    ExaCoreTests
    ExaComTests
    ExaServiceTests
  )

  # Set "clean" to true, otherwise you might get compile errors for undefined symbols
  frameworks.each do |framework|
    scan(
      destination: destination,
      scheme: framework,
      configuration: configuration,
      clean: true,
      skip_slack: true
    )
  end
end

desc 'Runs all the nightly tests'
lane :test_nightly do |options|
  prepare_test unless options[:skip_prepare]

  scan(
    destination: test_destination('ios'),
    scheme: 'waipu-iosNightlyTests',
    configuration: options[:build_scheme_config],
    clean: false,
    slack_only_on_failure: true
  )

  scan(
    destination: test_destination('ios'),
    scheme: 'o2-iosNightlyTests',
    configuration: options[:build_scheme_config],
    clean: false,
    slack_only_on_failure: true
  )

  scan(
    destination: test_destination('tvos'),
    scheme: 'waipu-tvosNightlyTests',
    configuration: options[:build_scheme_config],
    clean: false,
    slack_only_on_failure: true
  )

  scan(
    destination: test_destination('tvos'),
    scheme: 'o2-tvosNightlyTests',
    configuration: options[:build_scheme_config],
    clean: false,
    slack_only_on_failure: true
  )
end

desc 'Runs all the  tests on multiple iOS/tvOS devices'
lane :test_nightly_multiple_devices do |options|
  prepare_test unless options[:skip_prepare]
  configuration = options[:build_scheme_config]

  derived_data_path = 'build'

  # Build once for all test devices (build_for_testing)...
  scan(
   build_for_testing: true,
   derived_data_path: derived_data_path,
   scheme: 'ExaBaseTVTests',
   configuration: configuration,
   slack_only_on_failure: true
  )

  # Now, use compiled test and run for mulitple devices
  scan(
   test_without_building: true,
   clean: false,
   derived_data_path: derived_data_path,
   destination: test_destination('tvos'),
   scheme: 'ExaBaseTVTests',
   slack_only_on_failure: true
  )

  scan(
    build_for_testing: true,    
    destination: test_destination('ios'),
    derived_data_path: derived_data_path,
    scheme: 'ExaBaseMobileTests',
    configuration: configuration,
    slack_only_on_failure: true
  )

  latest_os_version = latest_os_version_for_testing('ios')
  ipad_destination = "iPad Pro (10.5-inch) (#{latest_os_version})"

  # Test on iPad
  # Not using devices option as xcodebuild cannot handle installed simulators correctly
  scan(
    test_without_building: true,
    clean: false,
    derived_data_path: derived_data_path,
    destination: "platform=iOS Simulator,name=#{ipad_destination},OS=#{latest_os_version}",
    scheme: 'ExaBaseMobileTests',
    configuration: configuration,
    slack_only_on_failure: true
  )
end

######################### PRIVATE LANES GENERAL #########################
desc 'Builds the project, increments the build number and updates the app identifier according settings'
private_lane :build do |options|
  config = lane_context[SharedValues::WAIPU_CONFIG]
  platform = options[:platform]
  scheme = options[:scheme]
  target = get_target_name(platform, config)

  # Request token to query App Store Connect API
  api_key = load_app_store_connect_api_key

  UI.message "Building branch #{git_branch}"

  # Codesigning Information
  team_id = options[:team_id]
  codesigning_identity = options[:codesigning_identity]

  app_bundle_identifier = options[:app][:identifier]
  app_provisioning_profile_name = options[:app][:provisioning_profile_specifier]

  # Required values
  UI.user_error! 'Must specifiy a development team id' unless team_id
  UI.user_error! 'Must specify a codesigning identity' unless codesigning_identity

  UI.user_error! 'Must specifiy an app_identifier' unless app_bundle_identifier
  UI.user_error! 'Must specifiy a provisioning profile specifier' unless app_provisioning_profile_name

  UI.user_error! 'Must specifiy an export_method' unless options[:export_method]

  UI.user_error! 'Must specifiy a target platform (ios, tvos)' unless platform
  UI.user_error! 'Must specifiy a build scheme (waipu-ios, waipu-tvos)' unless scheme

  plist_path = get_info_plist_path(
    platform: platform,
    target: target,
    use_absolute_path: false
  )

  UI.user_error! 'Invalid plist path' unless plist_path

  clear_derived_data

  # check if selected branch is a feature branch and use it if so
  branch_name = "develop"
  if is_feature_branch_build
    branch_name = jira_ticket()
  end

  change_app_icon(config, platform, branch_name)

  skip_git_tag_check = options[:skip_build_from_git_tag_check]

  UI.message "Skip build from git tag check: #{skip_git_tag_check}"

  # Check if release is build from a git tag
  git_tag_restriction_group = config.get('git_tag_restriction', optional: true)
  if git_tag_restriction_group != nil && skip_git_tag_check == false
    check_if_build_from_git_tag(git_tag_restriction_group, platform, config)
  end

  set_plist_variables(
    platform: platform,
    target: target
  )

  # Optional values
  silent = options[:silent].nil? || false
  configuration = options[:configuration] || 'Release'

  version = get_version(platform, config)
  app_identifier = config.get("app_identifier_#{platform}").to_s

  latest_testflight_build_number = latest_testflight_build_number(
    version: version,
    app_identifier: app_identifier,
    initial_build_number: 0, # because we always add one, use 0 for initial build
    platform: platform
  )

  build_number = latest_testflight_build_number + 1

  UI.message "Build number: #{build_number}"

  # needs full path here
  if is_feature_branch_build
    set_info_plist_value(path: "app-#{platform}/#{plist_path}", key: "BuildInfo", value: "#{branch_name} - (#{build_number})")
  end

  xcodeproj_path = get_xcodeproj_path(platform, config)

  increment_build_number(
    build_number: build_number,
    xcodeproj: xcodeproj_path
  )

  # Disables code signing for *all* targets;
  # maybe use "targets" parameter and specify build target (and all included frameworks?)
  update_code_signing_settings(
    path: xcodeproj_path,
    use_automatic_signing: false
  )

  # Note that your Target must not use $(SRCROOT) in Xcode for the Info.plist path!
  update_app_identifier(
    xcodeproj: xcodeproj_path,
    plist_path: plist_path,
    app_identifier: app_bundle_identifier
  )

  # Do not use option force: true
  # This created _thousands_ of provisioning profiles because the ones configured
  # expire once a year due to the yearly-expiring distribution signing identity
  get_provisioning_profile(
    app_identifier: app_bundle_identifier,
    api_key: api_key,
    platform: platform,
    provisioning_name: app_provisioning_profile_name,
    ignore_profiles_with_different_name: true,
    readonly: true
  )

  update_project_provisioning(
    xcodeproj: xcodeproj_path,
    target_filter: target,
    build_configuration: configuration,
    code_signing_identity: codesigning_identity
  )

  extensions = options[:extensions]

  extensions.each do |extension|
    extension_target = extension['target']
    extension_bundle_identifier = extension['extension_identifier']
    extension_plist_path = extension['info_plist_path']
    extension_provisioning_profile = extension['provisioning_profile']

    UI.user_error! 'Must specify a target for this extension' unless extension_target
    UI.user_error! "Must specify an identifier for extension with target: #{extension_target}" unless extension_bundle_identifier
    UI.user_error! "Must specify a provisioning profile for extension with target: #{extension_target}" unless extension_provisioning_profile
    UI.user_error! "Must specify a plist path for extension with target: #{extension_target}" unless extension_plist_path

    # Absolute path to Info.plist needed here
    set_info_plist_value(path: "app-#{platform}/#{extension_plist_path}", key: 'CFBundleShortVersionString', value: version)

    # Note that your Target must not use $(SRCROOT) in Xcode for the Info.plist path!
    update_app_identifier(
      xcodeproj: xcodeproj_path,
      plist_path: extension_plist_path, # Relative path to .xcodeproj
      app_identifier: extension_bundle_identifier
    )

    # Do not use option force: true
    # This created _thousands_ of provisioning profiles because the ones configured
    # expire once a year due to the yearly-expiring distribution signing identity
    get_provisioning_profile(
      app_identifier: extension_bundle_identifier,
      api_key: api_key,
      platform: platform,
      provisioning_name: extension_provisioning_profile,
      ignore_profiles_with_different_name: true,
      readonly: true
    )

    update_project_provisioning(
      xcodeproj: xcodeproj_path,
      target_filter: extension_target,
      build_configuration: configuration,
      code_signing_identity: codesigning_identity
    )

    UI.message 'Build extentions with target: ' + extension_target
  end

  ipa_name = config.get("ipa_name_prefix_#{platform}") + version

  gym(
    workspace: config.get('workspace'),
    configuration: configuration,
    scheme: scheme,
    silent: silent,
    clean: true,
    output_name: ipa_name,
    output_directory: config.get('output_path'),
    export_options: {
      method: options[:export_method],
      teamID: team_id
    },
    include_symbols: true,
    codesigning_identity: codesigning_identity,
    xcargs: "CODE_SIGN_STYLE=Manual" # needed since Xcode 14, see https://forums.swift.org/t/xcode-14-beta-code-signing-issues-when-spm-targets-include-resources/59685/7
  )

  update_code_signing_settings(
    path: xcodeproj_path,
    use_automatic_signing: true
  )
end

platform :tvos do
  import 'Fastfile-tvOS'
end

platform :ios do
  import 'Fastfile-iOS'
end

lane :deploy_release do | options |
  config = lane_context[SharedValues::WAIPU_CONFIG]

  build_artifacts = get_build_artifacts_paths
  flavor = options[:flavor]

  platform = options[:platform]
  version_number = get_version(platform, config)

  plist_path = get_info_plist_path(
    platform: platform,
    target: config.get("target_#{platform}"),
    use_absolute_path: true
  )
  build_number = get_info_plist_value(path: plist_path, key: 'CFBundleVersion')

  changelog = ""

  begin
    if is_feature_branch_build
      changelog = changelog_entry_for(platform, config)
    else
      changelog = changelog_from_yml(platform, config)
    end
  rescue StandardError => ex
    UI.user_error!(ex)
  end

  app_platform = platform
  slack_platform_playload = ':ios: iOS'
  case platform
  when 'tvos'
    app_platform = 'appletvos' # yes, it really is 'appletvos' and not 'tvos'
    slack_platform_playload = ':tvOS: tvOS'
  end

  api_key = load_app_store_connect_api_key

  pilot(
    api_key: api_key,
    app_platform: app_platform,
    ipa: build_artifacts[:ipa_path],
    changelog: changelog,
    app_identifier: config.get("app_identifier_#{platform}")
  )

  if config.get('notify_about_app_upload')
    slack(
      message: "New Version for #{config.get('name')} uploaded to TestFlight",
      slack_url: ENV['SLACK_URL_APP_RELEASE'],
      payload: {
        'Platform' => slack_platform_playload,
        'Release Type' => "#{flavor}",
        'Version' => "#{version_number} (#{build_number})",
        'Release Notes' => changelog
      },
      attachment_properties: {
        thumb_url: 'https://developer.apple.com/assets/elements/icons/testflight/testflight-64x64.png'
      },
      default_payloads: [],
      fail_on_error: false
    )
  end
end

desc 'Creates all neccessary test devices'
lane :create_devices do |options|
  UI.message 'Will automatically create devices needed for testing'

  devices = options[:devices].flatten

  # Sample input:
  # iPad Pro (9.7-inch) (12.4)
  devices.each do |device|
    system("xcrun simctl list devices | grep \"#{device}\"")

    # Quickfix
    platform_string = device.include?('Apple TV') ? 'tvOS' : 'iOS'

    next unless $CHILD_STATUS.exitstatus != 0

    # Searches sep or pattern (regexp) in the string and returns the part before it, the match, and the part after it.
    # If it is not found, returns two empty strings and str.
    device_version_parts = device.rpartition(' ')

    device_type = device_version_parts.first

    os_runtime_version = device_version_parts.last.gsub(/[\(\)]/, '').strip

    begin
      runtime = sh("xcrun simctl list runtimes | grep \"#{platform_string} #{os_runtime_version}\" | head -n1").split(/-/, 3).last.strip
      devicetype = sh("xcrun simctl list devicetypes | grep \"#{device_type} (com.apple.CoreSimulator.SimDeviceType\" | head -n1").split.last.gsub(/[\(\)]/, '')

      sh("xcrun simctl create \"#{device}\" \"#{devicetype}\" \"#{runtime}\"")
      UI.message "Creating #{device}"
    rescue
      UI.user_error! "Could not create test device: #{platform_string} #{os_runtime_version}"
    end
  end
end

######################### HELPER #########################

desc "Returns if we are building for tvos"
def is_platform_tvos
  platform = lane_context[SharedValues::PLATFORM_NAME]
  return platform == "tvos"
end

desc "Load the App Store Connect API token to use in other fastlane tools and actions"
def load_app_store_connect_api_key
  # http://docs.fastlane.tools/actions/app_store_connect_api_key/#app_store_connect_api_key
  api_key = app_store_connect_api_key(
    key_id: "...",
    issuer_id: "...",
    key_filepath: "./fastlane/app-store-connect-api-key.p8"
  )
  return api_key
end

desc "Returns the jira ticket part of the selected branch if it is a feature branch"
def jira_ticket
  ticket = ENV['JIRA_TICKET']
  if ticket
    return ticket
  end

  result = git_branch[/.*\/(TVFUS-[^_]*)/,1]
  ENV['JIRA_TICKET'] = result
  UI.message "🐌 Jira Ticket: #{result}"
  return result
end

desc "Returns true if current build is from a feature branch"
def is_feature_branch_build
  return (not (jira_ticket.nil? || jira_ticket.strip.empty?))
end

# Reads changelog from ../CHANGELOG.yml for current project version.
# Raises an exception if file could not be found or changelog for project
# version is not available.
# TODO: Should be an action
def changelog_from_yml(platform, config)
  key = platform == 'ios' ? 'changelog_path_ios' : 'changelog_path_tvos'
  path = config.get(key)

  changes = YAML.load_file(path)

  version = get_version(platform, config)
  change = changes.find { |entry| entry['version'] == version }

  UI.user_error! "No release notes available for version: #{version}" unless change

  # changelog entries are reversed so the latest additions are on top in TestFlight build description
  release_notes = change['notes'].reverse().reduce('') { |notes, note| "#{notes}\n- #{note}" } unless changes.nil?
end

desc "Returns the associated changelog message if build is from a feature branch"
def changelog_entry_for(platform, config)

  ticket = jira_ticket.to_s
  return "" unless ticket.length > 5
  key = platform == 'ios' ? 'changelog_path_ios' : 'changelog_path_tvos'
  path = config.get(key)

  changes = YAML.load_file(path)
  version = get_version(platform, config)
  change = changes.find { |entry| entry['version'] == version }
  # return the first message line containing the jira ticket number or else just the ticket number
  result = change['notes'].detect { |line| line.include? ticket }
  return "#{ticket}: #{result.to_s}"
end

def get_target_name(platform, config)
  # TODO: Actually check active target, using LANE_CONTEXT or fastlane shell parameters
  platform == 'ios' ? config.get('target_ios') : config.get('target_tvos')
end

# Gets version number for iOS or tvOS lanes
def get_version(platform, config)
  xcodeproj = get_xcodeproj_path(platform, config)

  get_version_number(
    xcodeproj: xcodeproj,
    target: get_target_name(platform, config)
  )
end

# does all the heavy lifting of changing the app icon appropriately
def change_app_icon(config, platform, branch_name)
  # Check if the AppIcon needs changes
  badge_overlay_path = config.get('badge_overlay_path', optional: true)
  override_app_icon = (not (badge_overlay_path.nil? || badge_overlay_path.empty?))

  if override_app_icon
    # app icon dict key
    app_icon_glob_key = "app_icon_glob_#{platform}"

    # overlay image is found at badge_overlay_path
    app_icon_key = "app_icon_#{platform}"
    app_icon_path = config.get(app_icon_key)

    # replace AppIcon
    if app_icon_path
      target_key = "app_icon_target_#{platform}"
      app_icon_dst = config.get(target_key)
      source_key = "app_icon_#{platform}"
      app_icon_src = config.get(source_key)

      UI.message "Using alternativ Icon"

      # clear original resources
      remove_icon_files = "rm -rf '..#{app_icon_dst}/*'"
      sh(remove_icon_files)

      # copy all resources for alternative Icon
      copy_icon_files = "cp -r './..#{app_icon_src}' './..#{app_icon_dst}/..'"
      sh(copy_icon_files)
    else
      # add custom DEV/PREVIEW badge image overlay for regular dev or preview builds
      add_badge(custom: badge_overlay_path, glob: config.get(app_icon_glob_key))
    end

    # add Jira ticket badge
    if is_feature_branch_build
      add_badge(no_badge: "true",
                glob: config.get(app_icon_glob_key),
                shield: "#{branch_name}-red",
                shield_gravity: "South",
                shield_scale: "0.75",
                shield_parameters: "style=flat&labelColor=FF8300&color=FF8300"
                )
    end
  end
end

# Retrieves version information from the tag that currently points to HEAD
# Params:
# +grouping+:: The group aka. folder the tag belongs to
def get_version_from_tag(grouping, platform)
  tag = sh("git tag -l '#{grouping}/#{platform}/*' --points-at HEAD")
  version = tag.split(%r{/}).last

  raise "Tag: #{tag} is not of scheme [#{grouping}/#{platform}/1.2.3]" if version.nil?

  version.chomp
end

def get_build_artifacts_paths
  ipa_path = nil
  dsym_path = nil

  Dir.chdir('..') do
    ipa_path = Dir['./build_fastlane/*.ipa'].first
    dsym_path = Dir['./build_fastlane/*.zip'].first
  end

  UI.message 'Could not find ipa file for upload' if ipa_path.nil?
  UI.message 'Could not find dsym file for upload' if dsym_path.nil?

  # Return a dictionary with the build artifact paths
  {
    ipa_path: ipa_path,
    dsym_path: dsym_path
  }
end

# Checks if the neccessary plist values are not emtpy and sets them.
# Raises an error if one value is missing: AmazonLogin
# Should prevent further constants refactoring issues, which actually promises to make everything better and saver.
private_lane :set_plist_variables do |options|
  config = lane_context[SharedValues::WAIPU_CONFIG]
  platform = options[:platform]
  target = options[:target]

  plist_path = get_info_plist_path(
    platform: platform,
    target: target,
    use_absolute_path: true
  )

  UI.user_error! 'Invalid Info.plist path' unless plist_path

  environment_variables_for_plist = config.get('environment_variables_for_plist')

  environment_variables_for_plist.each do |hash|
    key = hash['key']
    value = hash['value']

    set_info_plist_value(path: plist_path, key: key, value: value)

    UI.user_error! "Missing plist value for '#{key}' ��" unless value && !value.strip.empty?
  end

  UI.success('Required environment variables are set 🙄')
end

# Checks if current project is build from tag and that tag version
# matches version in Info.plist
# Params:
# +grouping+:: The name of the group that the tag belongs to e.g. appstore
def check_if_build_from_git_tag(grouping, platform, config)
  tag_version = get_version_from_tag(grouping, platform)

  project_version = get_version(platform, config)

  UI.user_error! "Tag version and project version differ [#{tag_version} vs. #{project_version}]" unless project_version.eql? tag_version
end

# TODO: Convert to action?
def get_xcodeproj_path(platform, config)
  platform == 'ios' ? config.get('project_ios') : config.get('project_tvos')
end

private_lane :get_info_plist_path do |options|
  platform = options[:platform]
  target = options[:target] # Not used yet
  use_absolute_path = options[:use_absolute_path]

  UI.user_error! 'Must specify platform' if platform.nil?
  UI.user_error! 'Must specify target' if target.nil?
  UI.user_error! 'Must specify use_absolute_path' if use_absolute_path.nil?

  path = "#{target}/Info.plist"

  path.prepend("app-#{platform}/") if use_absolute_path

  path
end
`./fastlane/Appfile`
# Needed for latest_testflight_build_number to update the App Store Connect session
itc_team_id "2250322"

fastlane gems

Gem Version Update-Status
fastlane 2.212.2 ✅ Up-To-Date

Loaded fastlane plugins:

Plugin Version Update-Status
fastlane-plugin-badge 1.5.0 ✅ Up-To-Date
Loaded gems
Gem Version
error_highlight 0.3.0
did_you_mean 1.6.1
bundler 2.3.24
pathname 0.2.0
rake 13.0.6
rexml 3.2.5
CFPropertyList 3.0.6
public_suffix 5.0.1
addressable 2.8.4
artifactory 3.0.15
atomos 0.1.3
aws-eventstream 1.2.0
aws-partitions 1.765.0
aws-sigv4 1.5.2
jmespath 1.6.2
aws-sdk-core 3.172.0
aws-sdk-kms 1.64.0
aws-sdk-s3 1.122.0
babosa 1.0.4
fastimage 2.2.6
colored 1.2
highline 2.0.3
commander 4.6.0
dotenv 2.8.1
emoji_regex 3.2.3
excon 0.99.0
faraday-em_http 1.0.0
faraday-em_synchrony 1.0.0
faraday-excon 1.1.0
faraday-httpclient 1.0.1
multipart-post 2.0.0
faraday-multipart 1.0.4
faraday-net_http 1.0.1
faraday-net_http_persistent 1.2.0
faraday-patron 1.0.0
faraday-rack 1.0.0
faraday-retry 1.0.3
ruby2_keywords 0.0.5
faraday 1.10.3
unf_ext 0.0.8.2
unf 0.1.4
domain_name 0.5.20190701
http-cookie 1.0.5
faraday-cookie_jar 0.0.7
faraday_middleware 1.2.0
gh_inspector 1.1.3
jwt 2.7.0
memoist 0.16.2
multi_json 1.15.0
os 1.1.4
signet 0.17.0
googleauth 1.5.2
httpclient 2.8.3
mini_mime 1.1.2
declarative 0.0.20
trailblazer-option 0.1.2
uber 0.1.0
representable 3.2.0
retriable 3.1.2
webrick 1.8.1
google-apis-core 0.11.0
google-apis-androidpublisher_v3 0.42.0
google-apis-playcustomapp_v1 0.13.0
digest-crc 0.6.4
google-apis-iamcredentials_v1 0.17.0
google-apis-storage_v1 0.19.0
google-cloud-env 1.6.0
google-cloud-errors 1.3.1
google-cloud-core 1.6.0
google-cloud-storage 1.44.0
json 2.6.3
mini_magick 4.12.0
naturally 2.2.1
optparse 0.1.1
plist 3.7.0
rubyzip 2.3.2
security 0.1.3
simctl 1.6.10
terminal-notifier 2.0.0
unicode-display_width 1.8.0
terminal-table 1.8.0
tty-screen 0.8.1
tty-cursor 0.7.1
tty-spinner 0.9.3
word_wrap 1.0.0
claide 1.1.0
colored2 3.1.2
nanaimo 0.3.0
xcodeproj 1.22.0
rouge 2.0.7
xcpretty 0.3.0
xcpretty-travis-formatter 1.0.1
badge 0.13.0
cork 0.3.0
nap 1.1.0
open4 1.3.4
claide-plugins 0.9.2
faraday-http-cache 2.5.0
rchardet 1.8.0
git 1.13.2
kramdown 2.4.0
kramdown-parser-gfm 1.1.0
no_proxy_fix 0.1.2
sawyer 0.9.2
octokit 5.6.1
danger 9.3.0
multi_xml 0.6.0
httparty 0.21.0
gitlab 4.19.0
danger-gitlab 8.0.0
danger-plugin-api 1.0.0
git_diff_parser 2.3.0
danger-ios_logs 0.1.2
thor 0.20.3
danger-swiftlint 0.33.0
danger-todoist 2.0.1
xcresult 0.2.1
danger-xcode_summary 1.2.0
fastlane-plugin-badge 1.5.0

generated on: 2023-05-22

@fastlane-bot
Copy link

It seems like you have not included the output of fastlane env
To make it easier for us help you resolve this issue, please update the issue to include the output of fastlane env 👍

@rpitting
Copy link
Author

rpitting commented May 24, 2023

I found the reason for the problem:

In the Apple Developer Portal, the suggested certificate type is "Apple Distribution" ("Sign development versions of your iOS, macOS, tvOS, and watchOS apps. For use in Xcode 11 or later.").

However, sigh repair only worked when I created a certificate with the type "iOS Distribution" ("App Store and Ad Hoc").

Therefore, fastlane does not seem to handle the new type "Apple Distribution".

The change might be needed in Spaceship::Portal::Certificate#CERTIFICATE_TYPE_IDS (see https://www.rubydoc.info/gems/spaceship/0.5.1/Spaceship/Portal/Certificate#CERTIFICATE_TYPE_IDS-constant).

@rpitting rpitting changed the title sigh repair fails with "undefined method `id' for nil:NilClass (NoMethodError) certificates.map(&:id), sigh repair fails with "undefined method `id' for nil:NilClass (NoMethodError) certificates.map(&:id)" with Apple Distribution certificate type May 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants