diff --git a/.gitignore b/.gitignore index 946405e..64559d2 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .gows* _tmp _keystores +steps-sign-apk diff --git a/README.md b/README.md index 94ac13d..5881cdc 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,133 @@ -# steps-sign-apk +# Android Sign ![Bitrise Build Status](https://app.bitrise.io/app/3b968e65d584db2a.svg?token=Yk1LUEjLZtIjeIW4OOZvKw&branch=master) [![Bitrise Step Version](https://shields.io/github/v/release/bitrise-steplib/steps-sign-apk?include_prereleases)](https://github.com/bitrise-steplib/steps-sign-apk/releases) ![GitHub License](https://img.shields.io/badge/license-MIT-lightgrey.svg) [![Bitrise Community](https://img.shields.io/badge/community-Bitrise%20Discuss-lightgrey)](https://discuss.bitrise.io/) -## How to use this Step +Signs your APK or Android App Bundle before uploading it to Google Play Store. -Can be run directly with the [bitrise CLI](https://github.com/bitrise-io/bitrise), -just `git clone` this repository, `cd` into it's folder in your Terminal/Command Line -and call `bitrise run test`. +Once you have uploaded your keystore file and provided your keystore credentials on the **Code Signing** tab of the Workflow Editor, the **Android Sign** Step signs your APK digitally. +Bitrise assigns Environment Variables to the uploaded file and credentials, and uses those in the respective fields of the **Android Sign** Step. +Once the Step runs, it produces a signed APK or App Bundle which will be used as the input value of the **App file path** field in the **Google Play Deploy** Step. -*Check the `bitrise.yml` file for required inputs which have to be -added to your `.bitrise.secrets.yml` file!* +### Configuring the Step + +1. Add the **Android Sign** Step after a build Step in your deploy workflow. +2. Upload the keystore file to the **Upload file** field on the **Code Signing** tab. +3. Provide your keystore password, keystore alias and private key password to the relevant fields on the **Code Signing** tab. +4. Run your build. + + +### Troubleshooting +Make sure you have the **Android Sign** Step right after a build Steps but before **Deploy to Google Play** Step in your deploy workflow. +If you wish to get your Android project signed automatically, use the **Android Sign** Step and do not set any gradle task for the signing, otherwise, the Step will fail. + + +### Useful links +- [Android code signing using Android Sign Step](https://devcenter.bitrise.io/code-signing/android-code-signing/android-code-signing-using-bitrise-sign-apk-step/) +- [Android deployment](https://devcenter.bitrise.io/deploy/android-deploy/android-deployment-index/) + + +### Related Steps +- [Android Build](https://www.bitrise.io/integrations/steps/android-build) +- [Gradle Runner](https://www.bitrise.io/integrations/steps/gradle-runner) +- [Deploy to Bitrise.io](https://www.bitrise.io/integrations/steps/deploy-to-bitrise-io) + +## Examples + +### Building a bundle and signing it + +1. Build an Android App Bundle: +``` +--- +format_version: '8' +default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git +project_type: android +workflows: + release: + envs: + - PROJECT_LOCATION: . + - MODULE: + - VARIANT: release + # If the Android keystore is configured in the workflow editor, BITRISEIO_ANDROID_KEYSTORE* envs will be set automatically + - BITRISEIO_ANDROID_KEYSTORE_URL: $BITRISEIO_ANDROID_KEYSTORE_URL + - BITRISEIO_ANDROID_KEYSTORE_PASSWORD: $BITRISEIO_ANDROID_KEYSTORE_PASSWORD + - BITRISEIO_ANDROID_KEYSTORE_ALIAS: $BITRISEIO_ANDROID_KEYSTORE_ALIAS + - BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD: $BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD + steps: + - activate-ssh-key: + run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}' + - git-clone: {} + - script: + title: "Select Java 11" + run_if: $.IsCI + inputs: + - content: |- + #!/usr/bin/env bash + set -ex + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + sudo update-alternatives --set javac /usr/lib/jvm/java-11-openjdk-amd64/bin/javac + sudo update-alternatives --set java /usr/lib/jvm/java-11-openjdk-amd64/bin/java + export JAVA_HOME="/usr/lib/jvm/java-11-openjdk-amd64" + envman add --key JAVA_HOME --value "/usr/lib/jvm/java-11-openjdk-amd64" + elif [[ "$OSTYPE" == "darwin"* ]]; then + jenv global 11 + export JAVA_HOME="$(jenv prefix)" + envman add --key JAVA_HOME --value "$(jenv prefix)" + fi + - install-missing-android-tools: + inputs: + - gradlew_path: $PROJECT_LOCATION/gradlew + - android-build: + inputs: + - project_location: $PROJECT_LOCATION + - module: $MODULE + - variant: $VARIANT + - build_type: aab +``` +2. Sign the App Bundle: +```yml + - sign-apk: + inputs: + - use_apk_signer: true +``` +3. Deploy the signed App Bundle to Bitrise: +``` + - deploy-to-bitrise-io: {} +``` + +## Configuration + +### Inputs + +| Parameter | Description | Required | Default | +| --- | --- | --- | --- | +| android_app | Path(s) to the build artifact file to sign (`.aab` or `.apk`). You can provide multiple build artifact file paths separated by `\|` character. Format examples: `/path/to/my/app.apk`; `/path/to/my/app1.apk\|/path/to/my/app2.apk`. | ✔️ | *$BITRISE_APK_PATH\n$BITRISE_AAB_PATH* | +| keystore_url | Keystore URL. For remote keystores you can provide any download location (example: `https://URL/TO/keystore.jks`). For local keystores provide file path url. (example: `file://PATH/TO/keystore.jks`). | ✔️ | *$BITRISEIO_ANDROID_KEYSTORE_URL* | +| keystore_password | Keystore password | ✔️ | *$BITRISEIO_ANDROID_KEYSTORE_PASSWORD* | +| keystore_alias | Key alias | ✔️ | *$BITRISEIO_ANDROID_KEYSTORE_ALIAS* | +| private_key_password | Key password. If key password equals to keystore password (not recommended), you can leave it empty. | - | *$BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD* | +| page_align | If enabled, it tells zipalign to use memory page alignment for stored shared object files. Options: `automatic` (Enable page alignment for .so files, unless attribute *extractNativeLibs="true"* is set in the AndroidManifest.xml); `true`; `false` | ✔️ | `automatic` | +| use_apk_signer | Indicates if the signature should be done using apksigner instead of jarsigner. Options: `true`, `false`. | ✔️ | `false` | +| signer_scheme | APK Signature Scheme. `automatic` uses the values of --min-sdk-version and --max-sdk-version to decide which Signature Scheme to use. Options: `v2`, `v3`, `v4`, `automatic`. | ✔️ | `automatic` | +| debuggable_permitted | Whether to permit signing android:debuggable="true" APKs. Android disables some of its security protections for such apps. Options: `true`, `false`. | ✔️ | `true` | +| output_name | Name of the produced output artifact. By default the output name is *app-release-bitrise-signed*. Else it's the specified name. Do not add extensions. | - | "" | +| verbose_log | Enables verbose logging. Options: `true`, `false`. | ✔️ | `false` | +| apk_path | *deprecated* | - | - | + +### Outputs + +| Environment Variable | Description | +| --- | --- | +| BITRISE_SIGNED_APK_PATH | This output will include the path of the signed APK. If the build generates more than one APK this output will contain the last one's path. | +| BITRISE_SIGNED_APK_PATH_LIST | This output will include the paths of the generated APKs. If multiple APKs are provided for signing the output paths are separated with `\|` character, for example, `app-armeabi-v7a-debug.apk\|app-mips-debug.apk\|app-x86-debug.apk` | +| BITRISE_SIGNED_AAB_PATH | This output will include the path of the signed AAB. If the build generates more than one AAB this output will contain the last one's path. | +| BITRISE_SIGNED_AAB_PATH_LIST | This output will include the paths of the generated AABs. If multiple AABs are provided for signing the output paths are separated with `\|` character, for example, `app-armeabi-v7a-debug.aab\|app-mips-debug.aab\|app-x86-debug.aab` | +| BITRISE_APK_PATH | *deprecated* | +| BITRISE_AAB_PATH | *deprecated* | + +## Contributing + +We welcome [pull requests](https://github.com/bitrise-steplib/steps-sign-apk/pulls) and [issues](https://github.com/bitrise-steplib/steps-sign-apk/issues) against this repository. + +For pull requests, work on your changes in a forked repository and use the bitrise cli to [run your tests locally](https://devcenter.bitrise.io/bitrise-cli/run-your-first-build/). + +### Creating your own steps + +Follow [this guide](https://devcenter.bitrise.io/contributors/create-your-own-step/) if you would like to create your own step. diff --git a/bitrise.yml b/bitrise.yml index 45602ee..e46b142 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -6,28 +6,29 @@ app: - SAMPLE_APP_REPOSITORY_URL: https://github.com/bitrise-io/sample-apps-android-abi-split.git - BRANCH: master - GRADLEW_PATH: "./gradlew" - # define these in your .bitrise.secrets.yml - - BITRISEIO_ANDROID_KEYSTORE_1_URL: $BITRISEIO_ANDROID_KEYSTORE_1_URL - - BITRISEIO_ANDROID_KEYSTORE_PASSWORD_1: $BITRISEIO_ANDROID_KEYSTORE_PASSWORD_1 - - BITRISEIO_ANDROID_KEYSTORE_ALIAS_1: $BITRISEIO_ANDROID_KEYSTORE_ALIAS_1 - - BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD_1: $BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD_1 - - BITRISEIO_ANDROID_KEYSTORE_2_URL: $BITRISEIO_ANDROID_KEYSTORE_2_URL - - BITRISEIO_ANDROID_KEYSTORE_PASSWORD_2: $BITRISEIO_ANDROID_KEYSTORE_PASSWORD_2 - - BITRISEIO_ANDROID_KEYSTORE_ALIAS_2: $BITRISEIO_ANDROID_KEYSTORE_ALIAS_2 - - BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD_2: $BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD_2 - - BITRISEIO_ANDROID_KEYSTORE_3_URL: $BITRISEIO_ANDROID_KEYSTORE_3_URL - - BITRISEIO_ANDROID_KEYSTORE_PASSWORD_3: $BITRISEIO_ANDROID_KEYSTORE_PASSWORD_3 - - BITRISEIO_ANDROID_KEYSTORE_ALIAS_3: $BITRISEIO_ANDROID_KEYSTORE_ALIAS_3 - - BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD_3: $BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD_3 - - BITRISEIO_ANDROID_KEYSTORE_4_URL: $BITRISEIO_ANDROID_KEYSTORE_4_URL - - BITRISEIO_ANDROID_KEYSTORE_PASSWORD_4: $BITRISEIO_ANDROID_KEYSTORE_PASSWORD_4 - - BITRISEIO_ANDROID_KEYSTORE_ALIAS_4: $BITRISEIO_ANDROID_KEYSTORE_ALIAS_4 - - BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD_4: $BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD_4 + # Keystore password == key password + - SAME_PASS_ANDROID_KEYSTORE_URL: $SAME_PASS_ANDROID_KEYSTORE_URL + - SAME_PASS_ANDROID_KEYSTORE_PASSWORD: $SAME_PASS_ANDROID_KEYSTORE_PASSWORD + - SAME_PASS_ANDROID_KEY_ALIAS: $SAME_PASS_ANDROID_KEY_ALIAS + - SAME_PASS_ANDROID_KEY_PASSWORD: $SAME_PASS_ANDROID_KEY_PASSWORD + # Keystore password != key password + - DIFF_PASS_ANDROID_KEYSTORE_URL: $DIFF_PASS_ANDROID_KEYSTORE_URL + - DIFF_PASS_ANDROID_KEYSTORE_PASSWORD: $DIFF_PASS_ANDROID_KEYSTORE_PASSWORD + - DIFF_PASS_ANDROID_KEY_ALIAS: $DIFF_PASS_ANDROID_KEY_ALIAS + - DIFF_PASS_ANDROID_KEY_PASSWORD: $DIFF_PASS_ANDROID_KEY_PASSWORD + # Default alias ('mykey') + - DEFAULT_ALIAS_ANDROID_KEYSTORE_URL: $DEFAULT_ALIAS_ANDROID_KEYSTORE_URL + - DEFAULT_ALIAS_ANDROID_KEYSTORE_PASSWORD: $DEFAULT_ALIAS_ANDROID_KEYSTORE_PASSWORD + - DEFAULT_ALIAS_ANDROID_KEY_ALIAS: $DEFAULT_ALIAS_ANDROID_KEY_ALIAS + - DEFAULT_ALIAS_ANDROID_KEY_PASSWORD: $DEFAULT_ALIAS_ANDROID_KEY_PASSWORD + # Android Studio generated keystore + - STUDIO_GEN_ANDROID_KEYSTORE_URL: $STUDIO_GEN_ANDROID_KEYSTORE_URL + - STUDIO_GEN_ANDROID_KEYSTORE_PASSWORD: $STUDIO_GEN_ANDROID_KEYSTORE_PASSWORD + - STUDIO_GEN_ANDROID_KEY_ALIAS: $STUDIO_GEN_ANDROID_KEY_ALIAS + - STUDIO_GEN_ANDROID_KEY_PASSWORD: $STUDIO_GEN_ANDROID_KEY_PASSWORD workflows: - # ---------------------------------------------------------------- - # --- workflow to Test this Step ci: before_run: - audit-this-step @@ -37,10 +38,51 @@ workflows: - errcheck: - go-test: after_run: + - test + + test: + steps: + - script: + run_if: $.IsCI + inputs: + - content: |- + #!/usr/bin/env bash + set -ex + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + sudo update-alternatives --set javac /usr/lib/jvm/java-11-openjdk-amd64/bin/javac + sudo update-alternatives --set java /usr/lib/jvm/java-11-openjdk-amd64/bin/java + export JAVA_HOME="/usr/lib/jvm/java-11-openjdk-amd64" + envman add --key JAVA_HOME --value "/usr/lib/jvm/java-11-openjdk-amd64" + elif [[ "$OSTYPE" == "darwin"* ]]; then + jenv global 11 + export JAVA_HOME="$(jenv prefix)" + envman add --key JAVA_HOME --value "$(jenv prefix)" + fi + after_run: - test_apk - test_apk_debug - test_bundle + debug: + envs: + - APK_SIGNER: "true" + # Must define these envs in .bitrise.secrets.yml + - BITRISE_APK_PATH: $BITRISE_APK_PATH + - BITRISEIO_ANDROID_KEYSTORE_URL: $BITRISEIO_ANDROID_KEYSTORE_URL + - BITRISEIO_ANDROID_KEYSTORE_PASSWORD: $BITRISEIO_ANDROID_KEYSTORE_PASSWORD + - BITRISEIO_ANDROID_KEYSTORE_ALIAS: $BITRISEIO_ANDROID_KEYSTORE_ALIAS + - BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD: $BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD + steps: + - path::./: + title: Debug sign-apk Step + inputs: + - keystore_url: $BITRISEIO_ANDROID_KEYSTORE_URL + - keystore_password: $BITRISEIO_ANDROID_KEYSTORE_PASSWORD + - keystore_alias: $BITRISEIO_ANDROID_KEYSTORE_ALIAS + - private_key_password: $BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD + - use_apk_signer: $APK_SIGNER + + test_apk: envs: - GRADLE_TASK: assembleRelease @@ -83,24 +125,11 @@ workflows: inputs: - path: ./_tmp - is_create_path: true - - script: + - git::https://github.com/bitrise-steplib/bitrise-step-simple-git-clone.git@master: inputs: - - content: |- - #!/usr/bin/env bash - set -x - - if [[ -z "${SAMPLE_APP_REPOSITORY_URL}" ]]; then - echo "error: there is no SAMPLE_APP_URL env var specified" - exit 1 - elif [[ -z "${COMMIT}" && -z "${BRANCH}" ]]; then - echo "error: can't checkout: there is no BRANCH or COMMIT env var specified" - exit 1 - fi - - git init - git remote add origin "${SAMPLE_APP_REPOSITORY_URL}" - git fetch || exit 1 - [[ -n "${COMMIT}" ]] && git checkout "${COMMIT}" || git checkout "${BRANCH}" + - repository_url: $SAMPLE_APP_REPOSITORY_URL + - clone_into_dir: . + - branch: $BRANCH - install-missing-android-tools: inputs: - ndk_revision: '16' @@ -177,10 +206,10 @@ workflows: - path::./: title: Step Test - keystore pass == key pass inputs: - - keystore_url: $BITRISEIO_ANDROID_KEYSTORE_1_URL - - keystore_password: $BITRISEIO_ANDROID_KEYSTORE_PASSWORD_1 - - keystore_alias: $BITRISEIO_ANDROID_KEYSTORE_ALIAS_1 - - private_key_password: $BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD_1 + - keystore_url: $SAME_PASS_ANDROID_KEYSTORE_URL + - keystore_password: $SAME_PASS_ANDROID_KEYSTORE_PASSWORD + - keystore_alias: $SAME_PASS_ANDROID_KEY_ALIAS + - private_key_password: $SAME_PASS_ANDROID_KEY_PASSWORD - use_apk_signer: $APK_SIGNER test2: @@ -188,10 +217,10 @@ workflows: - path::./: title: Step Test - keystore pass != key pass inputs: - - keystore_url: $BITRISEIO_ANDROID_KEYSTORE_2_URL - - keystore_password: $BITRISEIO_ANDROID_KEYSTORE_PASSWORD_2 - - keystore_alias: $BITRISEIO_ANDROID_KEYSTORE_ALIAS_2 - - private_key_password: $BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD_2 + - keystore_url: $DIFF_PASS_ANDROID_KEYSTORE_URL + - keystore_password: $DIFF_PASS_ANDROID_KEYSTORE_PASSWORD + - keystore_alias: $DIFF_PASS_ANDROID_KEY_ALIAS + - private_key_password: $DIFF_PASS_ANDROID_KEY_PASSWORD - use_apk_signer: $APK_SIGNER test3: @@ -199,10 +228,10 @@ workflows: - path::./: title: Step Test - default alias inputs: - - keystore_url: $BITRISEIO_ANDROID_KEYSTORE_3_URL - - keystore_password: $BITRISEIO_ANDROID_KEYSTORE_PASSWORD_3 - - keystore_alias: $BITRISEIO_ANDROID_KEYSTORE_ALIAS_3 - - private_key_password: $BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD_3 + - keystore_url: $DEFAULT_ALIAS_ANDROID_KEYSTORE_URL + - keystore_password: $DEFAULT_ALIAS_ANDROID_KEYSTORE_PASSWORD + - keystore_alias: $DEFAULT_ALIAS_ANDROID_KEY_ALIAS + - private_key_password: $DEFAULT_ALIAS_ANDROID_KEY_PASSWORD - use_apk_signer: $APK_SIGNER test4: @@ -210,10 +239,10 @@ workflows: - path::./: title: Step Test - android studio generated keystore (jks) inputs: - - keystore_url: $BITRISEIO_ANDROID_KEYSTORE_4_URL - - keystore_password: $BITRISEIO_ANDROID_KEYSTORE_PASSWORD_4 - - keystore_alias: $BITRISEIO_ANDROID_KEYSTORE_ALIAS_4 - - private_key_password: $BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD_4 + - keystore_url: $STUDIO_GEN_ANDROID_KEYSTORE_URL + - keystore_password: $STUDIO_GEN_ANDROID_KEYSTORE_PASSWORD + - keystore_alias: $STUDIO_GEN_ANDROID_KEY_ALIAS + - private_key_password: $STUDIO_GEN_ANDROID_KEY_PASSWORD - use_apk_signer: $APK_SIGNER test5: @@ -221,10 +250,10 @@ workflows: - path::./: title: Step Test - android studio generated keystore (jks) + custom artifact name inputs: - - keystore_url: $BITRISEIO_ANDROID_KEYSTORE_4_URL - - keystore_password: $BITRISEIO_ANDROID_KEYSTORE_PASSWORD_4 - - keystore_alias: $BITRISEIO_ANDROID_KEYSTORE_ALIAS_4 - - private_key_password: $BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD_4 + - keystore_url: $STUDIO_GEN_ANDROID_KEYSTORE_URL + - keystore_password: $STUDIO_GEN_ANDROID_KEYSTORE_PASSWORD + - keystore_alias: $STUDIO_GEN_ANDROID_KEY_ALIAS + - private_key_password: $STUDIO_GEN_ANDROID_KEY_PASSWORD - output_name: "test-artifact-name" - use_apk_signer: $APK_SIGNER @@ -233,37 +262,12 @@ workflows: - path::./: title: Step Test - android studio generated keystore (jks) + custom artifact name second time to see collisions if any inputs: - - keystore_url: $BITRISEIO_ANDROID_KEYSTORE_4_URL - - keystore_password: $BITRISEIO_ANDROID_KEYSTORE_PASSWORD_4 - - keystore_alias: $BITRISEIO_ANDROID_KEYSTORE_ALIAS_4 - - private_key_password: $BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD_4 + - keystore_url: $STUDIO_GEN_ANDROID_KEYSTORE_URL + - keystore_password: $STUDIO_GEN_ANDROID_KEYSTORE_PASSWORD + - keystore_alias: $STUDIO_GEN_ANDROID_KEY_ALIAS + - private_key_password: $STUDIO_GEN_ANDROID_KEY_PASSWORD - output_name: "test-artifact-name" - use_apk_signer: $APK_SIGNER - - _go_tests: - steps: - - go-list: - - golint: - - errcheck: - - go-test: - - # ---------------------------------------------------------------- - # --- Utility workflows - - dep-update: - title: Dep update - description: | - Used for updating bitrise dependencies with dep - steps: - - script: - title: Dependency update - inputs: - - content: |- - #!/usr/bin/env bash - set -ex - go get -u -v github.com/golang/dep/cmd/dep - dep ensure -v - dep ensure -v -update # ---------------------------------------------------------------- # --- workflows to Share this step into a Step Library