Overview
pilot makes it easy to manage your TestFlight builds and testers. Upload builds, invite testers, and manage beta testing for your iOS apps through the App Store Connect API.
pilot is the best way to automate TestFlight deployments and manage beta testers.
Installation
pilot is part of fastlane. Install it with:
Quick Start
CLI Commands
Upload build
Distribute build
List builds
Add testers
List testers
Find tester
Remove tester
Export testers
Import testers
Uploading Builds
Basic Upload
lane :beta do
build_app
pilot
end
Upload with Changelog
lane :beta do
build_app
pilot (
changelog: "Bug fixes and performance improvements"
)
end
Upload and Skip Distribution
pilot (
skip_submission: true ,
skip_waiting_for_build_processing: true
)
Key Options
The bundle identifier of your app. pilot ( app_identifier: "com.example.app" )
Path to the IPA file to upload. pilot ( ipa: "./build/MyApp.ipa" )
Provide the ‘What to Test’ text when uploading a new build. pilot ( changelog: "Added dark mode support" )
Skip the distributing action and only upload the IPA file. pilot ( skip_submission: true )
skip_waiting_for_build_processing
Skip waiting for build processing to complete. pilot ( skip_waiting_for_build_processing: true )
Distribute the build to external testers. Requires groups option. pilot (
distribute_external: true ,
groups: [ "Beta Testers" ]
)
Should external testers be notified? pilot ( notify_external_testers: true )
Associate testers to groups by group name. pilot (
groups: [ "Beta Testers" , "Internal Team" ]
)
Beta app review information for contact info and demo account. pilot (
beta_app_review_info: {
contact_email: "[email protected] " ,
contact_first_name: "John" ,
contact_last_name: "Doe" ,
contact_phone: "+1 555-5555" ,
demo_account_name: "[email protected] " ,
demo_account_password: "password"
}
)
Provide the ‘Beta App Description’ when uploading a new build. pilot ( beta_app_description: "This app helps you manage your tasks" )
Localized ‘What to Test’ text by locale. pilot (
localized_build_info: {
"en-US" => { whats_new: "Bug fixes" },
"de-DE" => { whats_new: "Fehlerbehebungen" }
}
)
The version number to distribute. pilot ( app_version: "1.2.0" )
The build number to distribute. pilot ( build_number: "42" )
Expire previous builds. pilot ( expire_previous_builds: true )
Interval in seconds to wait for App Store Connect processing. pilot ( wait_processing_interval: 60 )
Managing Testers
Add Individual Testers
Or in your Fastfile:
lane :add_tester do
pilot (
email: "[email protected] " ,
first_name: "John" ,
last_name: "Doe" ,
groups: [ "Beta Testers" ]
)
end
Import Testers from CSV
Create a testers.csv file:
First Name, Last Name, Email, Groups
John, Doe, [email protected] ,"Beta Testers"
Jane, Smith, [email protected] ,"Internal Team,Beta Testers"
Then import:
Export Testers to CSV
List All Testers
fastlane pilot list -a com.example.app
Find Specific Tester
Remove Tester
Distribute Existing Build
Distribute a previously uploaded build without uploading a new one:
lane :distribute_beta do
pilot (
distribute_only: true ,
app_version: "1.2.0" ,
build_number: "42" ,
groups: [ "Beta Testers" ],
notify_external_testers: true
)
end
Using with fastlane
Complete Beta Lane
lane :beta do
increment_build_number
build_app ( scheme: "MyApp" )
pilot (
skip_waiting_for_build_processing: false ,
changelog: "Bug fixes and improvements" ,
distribute_external: true ,
groups: [ "Beta Testers" ],
notify_external_testers: true ,
beta_app_review_info: {
contact_email: "[email protected] " ,
contact_first_name: "John" ,
contact_last_name: "Doe" ,
contact_phone: "+1 555-5555"
}
)
end
Upload Only Lane
lane :upload_to_testflight do
build_app
pilot (
skip_submission: true ,
skip_waiting_for_build_processing: true
)
end
Distribute Later Lane
lane :distribute_testflight do
pilot (
distribute_only: true ,
build_number: ENV [ "BUILD_NUMBER" ],
groups: [ "Beta Testers" ],
notify_external_testers: true ,
changelog: "Ready for testing!"
)
end
App Store Connect API
pilot supports the App Store Connect API for authentication:
pilot (
api_key_path: "./AuthKey.json"
)
Or use a hash:
pilot (
api_key: {
key_id: "ABC123" ,
issuer_id: "XYZ789" ,
key_filepath: "./AuthKey_ABC123.p8"
}
)
Environment Variables
The bundle identifier of your app
Skip distributing the build
PILOT_DISTRIBUTE_EXTERNAL
Distribute to external testers
Comma-separated list of group names
Tips & Best Practices
Use groups : Organize your testers into groups for easier management.
External testing requires review : External TestFlight builds must go through Apple’s beta review process.
Skip waiting on CI : Use skip_waiting_for_build_processing: true on CI to save time.
Provide changelog : Always include a changelog to help testers know what to test.
Troubleshooting
Build not found
Make sure the build has finished processing in App Store Connect before trying to distribute it.
Tester already exists
You can safely add existing testers - pilot will just associate them with the specified groups.
Wrong team selected
Specify the team explicitly:
pilot (
team_id: "123456789"
)
gym - Build your iOS app
deliver - Upload to App Store
match - Sync certificates and profiles