Revyl Documentation

Builds / Remote builds

Signed store builds

Sign a release build and upload it to App Store Connect or Google Play

Revyl can sign your release build with your own certificate or keystore and upload it to App Store Connect or Google Play. You add a signing step and a deploy step to a build profile. Your xcodebuild or gradlew command does not change.

Signing and deploy steps only run in remote builds. Run the profile with --remote.

iOS

Create an App Store Connect API key

  1. Open App Store Connect and go to Users and Access > Integrations > App Store Connect API.
  2. Click Generate API Key. Give it the App Manager role.
  3. Download the .p8 file. It can only be downloaded once.
  4. Note the Key ID and the Issuer ID shown on the same page.

Export your distribution certificate

  1. Open Keychain Access on the Mac that holds the certificate.
  2. Under My Certificates, right-click the Apple Distribution certificate and click Export.
  3. Save it as a .p12 and set a password. Make sure the private key is included.

If any target in your project uses manual signing in Xcode, also download its App Store provisioning profile from the Apple Developer portal. Targets set to Automatically manage signing do not need one.

Add the credentials to Revyl

base64 -i distribution.p12  | revyl build secret set IOS_DIST_CERT --stdin
base64 -i AuthKey_ABC123.p8 | revyl build secret set ASC_API_KEY --stdin
revyl build secret set IOS_DIST_CERT_PASSWORD

# Only for manually signed targets
base64 -i App.mobileprovision | revyl build secret set IOS_APP_PROFILE --stdin

Add the steps to your build profile

Put ios-signing before your build commands and app-store-connect-deploy after them. Use your own Key ID and Issuer ID.

build:
  profiles:
    release:
      ios:
        app_id: <your iOS app UUID>
        setup_commands:
          - yarn install --immutable
          - npx --yes pod-install
        build_commands:
          - ios-signing:
              certificate: IOS_DIST_CERT
              certificate_password: IOS_DIST_CERT_PASSWORD
              api_key: ASC_API_KEY
              key_id: ABC123DEF4
              issuer_id: 57246542-96fe-1a63-e053-0824d011072a
          - xcodebuild archive -workspace ios/App.xcworkspace -scheme App -configuration Release -destination generic/platform=iOS -archivePath build/App.xcarchive $REVYL_XCODEBUILD_SIGNING_ARGS
          - xcodebuild -exportArchive -archivePath build/App.xcarchive -exportOptionsPlist "$REVYL_EXPORT_OPTIONS" -exportPath build $REVYL_XCODEBUILD_SIGNING_ARGS
          - app-store-connect-deploy:
              api_key: ASC_API_KEY
              key_id: ABC123DEF4
              issuer_id: 57246542-96fe-1a63-e053-0824d011072a
        output_path: build/App.ipa

For manually signed targets, add provisioning_profiles: [IOS_APP_PROFILE] to ios-signing.

Run the build

Increase CFBundleVersion if you have already uploaded this build number, then:

revyl build --profile release --platform ios --remote

The build appears in TestFlight after Apple finishes processing it.

ios-signing inputs

InputRequiredDescription
certificateYesSecret name of the base64 .p12
certificate_passwordNoSecret name of the .p12 password
api_key, key_id, issuer_idYes, unless every target has a profileSecret name of the .p8, plus the Key ID and Issuer ID as plain values
provisioning_profilesOnly for manually signed targetsSecret names of base64 .mobileprovision files
export_methodNoapp-store-connect (default), ad-hoc, enterprise, or development
export_optionsNoExtra keys for ExportOptions.plist, for example uploadSymbols: false

app-store-connect-deploy inputs

InputRequiredDescription
api_key, key_id, issuer_idYesSame values as above
ipaNoPath to the .ipa. Defaults to the profile's output_path
apple_idNoThe app's numeric App Store ID

Android

Get your upload keystore

Use the keystore you already sign releases with. Play Console shows its expected SHA-256 under Setup > App signing.

If you do not have one yet:

keytool -genkeypair -v -keystore upload.jks -alias upload -keyalg RSA -keysize 2048 -validity 10000

Create a Google Play service account

  1. In Google Cloud Console, create a service account and download a JSON key for it.
  2. In Play Console, go to Users and permissions > Invite new users.
  3. Enter the service account's email address and give it release access to your app.

Add the credentials to Revyl

base64 -i upload.jks | revyl build secret set ANDROID_KEYSTORE --stdin
revyl build secret set GOOGLE_PLAY_SERVICE_ACCOUNT --stdin < service-account.json
revyl build secret set ANDROID_KEYSTORE_PASSWORD

Add the steps to your build profile

Put android-signing after your build command and google-play-deploy after that. output_path must point to the one .aab or .apk your build produces.

build:
  framework: android
  profiles:
    release:
      android:
        app_id: <your Android app UUID>
        image: android-ubuntu-24.04-jdk-17-ndk-r27b
        build_commands:
          - ./gradlew --no-daemon :app:bundleRelease
          - android-signing:
              keystore: ANDROID_KEYSTORE
              keystore_password: ANDROID_KEYSTORE_PASSWORD
          - google-play-deploy:
              service_account: GOOGLE_PLAY_SERVICE_ACCOUNT
              package_name: com.example.app
              track: internal
              release_status: draft
        output_path: app/build/outputs/bundle/release/app-release.aab

android-signing signs the file at output_path in place, so nothing in your Gradle config needs to change. If the keystore holds more than one key, add key_alias.

Run the build

Increase versionCode if you have already uploaded this one, then:

revyl build --profile release --platform android --remote

With release_status: draft the build is uploaded but not released. Release it from Play Console, or set release_status: completed to release to the track's testers straight away.

android-signing inputs

InputRequiredDescription
keystoreYesSecret name of the base64 keystore
keystore_passwordYesSecret name of the keystore password
key_aliasOnly if the keystore has more than one keyAlias to sign with
key_passwordNoSecret name of the key password. Defaults to the keystore password

google-play-deploy inputs

InputRequiredDescription
service_accountYesSecret name of the service account JSON
package_nameYesApplication ID, for example com.example.app
trackNointernal (default), alpha, beta, or production
release_statusNocompleted (default), draft, inProgress, or halted
aabNoPath to the .aab. Defaults to the profile's output_path
mapping_fileNoPath to the R8 mapping file to upload with the build

Troubleshooting

Credentials are checked before your build command runs, so a bad credential fails in seconds with the reason in the build log.

ErrorFix
Certificate password rejected, or certificate has no private keyRe-export the .p12 from Keychain Access with the private key included
Profile does not match the certificateRegenerate the profile for this certificate
Keystore password rejected, or alias not foundCheck the values against your current release pipeline
output_path must resolve to exactly one APK or AABPoint output_path at the single file your build produces
Duplicate build number or version codeBump it and run again
Google Play permission deniedInvite the service account to the app in Play Console

Profiles with signing or deploy steps always build from source; cached build results are not reused.

On this page