diff --git a/bitrise.yml b/bitrise.yml index 5c8c1b7..45602ee 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -3,17 +3,27 @@ default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git app: envs: - - BITRISE_STEP_GIT_CLONE_URL: https://github.com/bitrise-steplib/steps-sign-apk.git - - - SAMPLE_APP_REPOSITORY_URL: https://github.com/bitrise-samples/sample-apps-android-abi-split.git + - 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_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 + - 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 workflows: # ---------------------------------------------------------------- @@ -28,6 +38,7 @@ workflows: - go-test: after_run: - test_apk + - test_apk_debug - test_bundle test_apk: @@ -36,7 +47,18 @@ workflows: - APK_FILE_INCLUDE_FILTER: "*.apk" after_run: - create_build_artifact - - test + - _jarsigner + - _apksigner + + # Using apksigner zipalign fails to zipalign already zipaligned artifact + test_apk_debug: + envs: + - GRADLE_TASK: assembleDebug + - APK_FILE_INCLUDE_FILTER: "*.apk" + after_run: + - create_build_artifact + - _jarsigner + - _apksigner test_bundle: envs: @@ -44,14 +66,15 @@ workflows: - APK_FILE_INCLUDE_FILTER: "*.aab" after_run: - create_build_artifact - - test + - _jarsigner + - _apksigner create_build_artifact: steps: - script: inputs: - content: |- - #!/bin/bash + #!/usr/bin/env bash set -ex rm -rf ./_tmp - change-workdir: @@ -78,88 +101,124 @@ workflows: git remote add origin "${SAMPLE_APP_REPOSITORY_URL}" git fetch || exit 1 [[ -n "${COMMIT}" ]] && git checkout "${COMMIT}" || git checkout "${BRANCH}" - - install-missing-android-tools@2.3.3: + - install-missing-android-tools: inputs: - ndk_revision: '16' run_if: ".IsCI" + - script: + inputs: + - content: |- + #!/usr/bin/env bash + set -ex + envman unset --key BITRISE_APK_PATH + envman unset --key BITRISE_AAB_PATH - gradle-runner: inputs: - gradle_task: "$GRADLE_TASK" - gradlew_path: "$GRADLEW_PATH" - - apk_file_include_filter: $APK_FILE_INCLUDE_FILTER + - app_file_include_filter: $APK_FILE_INCLUDE_FILTER + - script: + inputs: + - content: |- + #!/usr/bin/env bash + set -ex + if [ -n "$BITRISE_APK_PATH" ]; then + envman add --key ORIG_BITRISE_APK_PATH --value ${BITRISE_APK_PATH} + fi - test: + reset_apk_path: steps: - - path::./: - is_skippable: true - title: Step Test - keystore pass == key pass with Jar signer + - script: 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 + - content: |- + #!/usr/bin/env bash + set -ex + if [ -n "$ORIG_BITRISE_APK_PATH" ]; then + envman add --key BITRISE_APK_PATH --value ${ORIG_BITRISE_APK_PATH} + fi + + _jarsigner: + steps: + - script: + inputs: + - content: |- + echo "jarsigner" + envman add --key APK_SIGNER --value "false" + after_run: + - _tests + + _apksigner: + steps: + - script: + inputs: + - content: |- + echo "apksigner" + envman add --key APK_SIGNER --value "true" + after_run: + - _tests + + _tests: + after_run: + - reset_apk_path + - test1 + - reset_apk_path + - test2 + - reset_apk_path + - test3 + - reset_apk_path + - test4 + - reset_apk_path + - test5 + - reset_apk_path + - test6 + + test1: + steps: - path::./: - is_skippable: true - title: Step Test - keystore pass == key pass with APK signer + 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 - - use_apk_signer: true - - path::./: - is_skippable: true - title: Step Test - keystore pass != key pass with Jar signer - 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 + - use_apk_signer: $APK_SIGNER + + test2: + steps: - path::./: - is_skippable: true - title: Step Test - keystore pass != key pass with APK signer + 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 - - use_apk_signer: true + - use_apk_signer: $APK_SIGNER + + test3: + steps: - path::./: - is_skippable: true 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 + - use_apk_signer: $APK_SIGNER + + test4: + steps: - path::./: - is_skippable: true - title: Step Test - default alias with APK signer - 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 - - use_apk_signer: true - - path::./: - is_skippable: true 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 + - use_apk_signer: $APK_SIGNER + + test5: + steps: - path::./: - is_skippable: true - title: Step Test - android studio generated keystore (jks) with APK signer - 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 - - use_apk_signer: true - - path::./: - is_skippable: true title: Step Test - android studio generated keystore (jks) + custom artifact name inputs: - keystore_url: $BITRISEIO_ANDROID_KEYSTORE_4_URL @@ -167,18 +226,11 @@ workflows: - keystore_alias: $BITRISEIO_ANDROID_KEYSTORE_ALIAS_4 - private_key_password: $BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD_4 - output_name: "test-artifact-name" + - use_apk_signer: $APK_SIGNER + + test6: + steps: - path::./: - is_skippable: true - title: Step Test - android studio generated keystore (jks) + custom artifact name with APK signer - 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 - - output_name: "test-artifact-name" - - use_apk_signer: true - - path::./: - is_skippable: true 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 @@ -186,6 +238,7 @@ workflows: - keystore_alias: $BITRISEIO_ANDROID_KEYSTORE_ALIAS_4 - private_key_password: $BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD_4 - output_name: "test-artifact-name" + - use_apk_signer: $APK_SIGNER _go_tests: steps: @@ -206,7 +259,7 @@ workflows: title: Dependency update inputs: - content: |- - #!/bin/bash + #!/usr/bin/env bash set -ex go get -u -v github.com/golang/dep/cmd/dep dep ensure -v @@ -219,6 +272,6 @@ workflows: - script: inputs: - content: |- - #!/bin/bash + #!/usr/bin/env bash set -ex stepman audit --step-yml ./step.yml diff --git a/main.go b/main.go index de6ed7f..95ea846 100644 --- a/main.go +++ b/main.go @@ -202,21 +202,6 @@ func unsignBuildArtifact(aapt, pth string) error { return removeFilesFromBuildArtifact(aapt, pth, signingFiles) } -func zipalignBuildArtifact(zipalign, pth, dstPth string, pageAlign bool) error { - cmdSlice := []string{zipalign} - - if pageAlign { - cmdSlice = append(cmdSlice, "-p") - } - - cmdSlice = append(cmdSlice, "-f", "4", pth, dstPth) - prinatableCmd := command.PrintableCommandArgs(false, cmdSlice) - log.Printf("=> %s", prinatableCmd) - - _, err := keystore.ExecuteForOutput(cmdSlice) - return err -} - func prettyBuildArtifactBasename(buildArtifactPth string) string { buildArtifactBasenameWithExt := path.Base(buildArtifactPth) buildArtifactExt := filepath.Ext(buildArtifactBasenameWithExt) @@ -422,9 +407,8 @@ func signJarSigner(zipalign, tmpDir string, unsignedBuildArtifactPth string, bui fmt.Println() fullPath, err := zipAlignArtifact(zipalign, unalignedBuildArtifactPth, buildArtifactDir, buildArtifactBasename, artifactExt, "signed", outputName, pageAlignConfig) - if err != nil { - failf("Failed to zip align artifact, error: %s", err) + failf("Failed to zipalign Build Artifact: %s", err) } return fullPath @@ -432,9 +416,8 @@ func signJarSigner(zipalign, tmpDir string, unsignedBuildArtifactPth string, bui func signAPK(zipalign, tmpDir string, unsignedBuildArtifactPth string, buildArtifactDir string, buildArtifactBasename string, artifactExt string, outputName string, apkSigner SignatureConfiguration, pageAlignConfig pageAlignStatus) string { alignedPath, err := zipAlignArtifact(zipalign, unsignedBuildArtifactPth, buildArtifactDir, buildArtifactBasename, artifactExt, "aligned", "", pageAlignConfig) - if err != nil { - failf("Failed to zip align artifact, error: %s", err) + failf("Failed to zipalign Build Artifact, error: %s", err) } signedArtifactName := fmt.Sprintf("%s-bitrise-signed%s", buildArtifactBasename, artifactExt) @@ -461,34 +444,6 @@ func signAPK(zipalign, tmpDir string, unsignedBuildArtifactPth string, buildArti return fullPath } -func zipAlignArtifact(zipalign, unalignedBuildArtifactPth string, buildArtifactDir string, buildArtifactBasename string, artifactExt string, fullPathExt string, outputName string, pageAlignConfig pageAlignStatus) (string, error) { - log.Infof("Zipalign Build Artifact") - signedArtifactName := fmt.Sprintf("%s-bitrise-%s%s", buildArtifactBasename, fullPathExt, artifactExt) - if artifactName := fmt.Sprintf("%s%s", outputName, artifactExt); outputName != "" { - log.Printf("- Exporting (%s) as: %s", signedArtifactName, artifactName) - signedArtifactName = artifactName - } - fullPath := filepath.Join(buildArtifactDir, signedArtifactName) - - pageAlign := pageAlignConfig == pageAlignYes - // Only care about .so memory page alignment for APKs - if !strings.EqualFold(artifactExt, ".aab") && pageAlignConfig == pageAlignAuto { - extractNativeLibs, err := parseAPKextractNativeLibs(unalignedBuildArtifactPth) - if err != nil { - log.Warnf("Failed to parse APK manifest to read extractNativeLibs attribute: %s", err) - pageAlign = true - } else { - pageAlign = !extractNativeLibs - } - } - - if err := zipalignBuildArtifact(zipalign, unalignedBuildArtifactPth, fullPath, pageAlign); err != nil { - failf("Failed to zipalign Build Artifact, error: %s", err) - } - - return fullPath, nil -} - func exportAPK(signedAPKPaths []string, joinedAPKOutputPaths string) { if err := tools.ExportEnvironmentWithEnvman("BITRISE_SIGNED_APK_PATH", signedAPKPaths[len(signedAPKPaths)-1]); err != nil { log.Warnf("Failed to export APK (%s) error: %s", signedAPKPaths[len(signedAPKPaths)-1], err) diff --git a/zipalign.go b/zipalign.go new file mode 100644 index 0000000..02a33db --- /dev/null +++ b/zipalign.go @@ -0,0 +1,50 @@ +package main + +import ( + "fmt" + "path/filepath" + "strings" + + "github.com/bitrise-io/go-utils/command" + "github.com/bitrise-io/go-utils/log" +) + +func zipalignBuildArtifact(zipalignConfig *zipalignConfiguration, artifactPath, dstPath string) error { + aligned, err := zipalignConfig.checkAlignment(artifactPath) + if err != nil { + return err + } + if aligned { + if err := command.CopyFile(artifactPath, dstPath); err != nil { + return fmt.Errorf("failed to copy build artifact: %s", err) + } + return nil + } + + return zipalignConfig.zipalignArtifact(artifactPath, dstPath) +} + +func zipAlignArtifact(zipalignPath, unalignedBuildArtifactPth string, buildArtifactDir string, buildArtifactBasename string, artifactExt string, fullPathExt string, outputName string, pageAlignConfig pageAlignStatus) (string, error) { + log.Infof("Zipalign Build Artifact") + signedArtifactName := fmt.Sprintf("%s-bitrise-%s%s", buildArtifactBasename, fullPathExt, artifactExt) + if artifactName := fmt.Sprintf("%s%s", outputName, artifactExt); outputName != "" { + log.Printf("- Exporting (%s) as: %s", signedArtifactName, artifactName) + signedArtifactName = artifactName + } + fullPath := filepath.Join(buildArtifactDir, signedArtifactName) + + isPageAligned := pageAlignConfig == pageAlignYes + // Only care about .so memory page alignment for APKs + if !strings.EqualFold(artifactExt, ".aab") && pageAlignConfig == pageAlignAuto { + extractNativeLibs, err := parseAPKextractNativeLibs(unalignedBuildArtifactPth) + if err != nil { + log.Warnf("Failed to parse APK manifest to read extractNativeLibs attribute: %s", err) + isPageAligned = true + } else { + isPageAligned = !extractNativeLibs + } + } + + return fullPath, zipalignBuildArtifact(newZipalignConfiguration(zipalignPath, isPageAligned), + unalignedBuildArtifactPth, fullPath) +} diff --git a/zipalign_command.go b/zipalign_command.go new file mode 100644 index 0000000..ca55003 --- /dev/null +++ b/zipalign_command.go @@ -0,0 +1,51 @@ +package main + +import ( + "github.com/bitrise-io/go-utils/command" + "github.com/bitrise-io/go-utils/errorutil" + "github.com/bitrise-io/go-utils/log" + "github.com/bitrise-steplib/steps-sign-apk/keystore" +) + +type zipalignConfiguration struct { + zipalignPath string + pageAlign bool +} + +func newZipalignConfiguration(zipalignPath string, pageAlign bool) *zipalignConfiguration { + return &zipalignConfiguration{ + zipalignPath: zipalignPath, + pageAlign: pageAlign, + } +} + +func (config *zipalignConfiguration) checkAlignment(artifactPath string) (bool, error) { + checkCmdSlice := []string{config.zipalignPath} + if config.pageAlign { + checkCmdSlice = append(checkCmdSlice, "-p") + } + checkCmdSlice = append(checkCmdSlice, "-c", "4", artifactPath) + + err := keystore.Execute(checkCmdSlice) + if err != nil { + if errorutil.IsExitStatusError(err) { + return false, nil + } + return false, err + } + + log.Printf("Artifact alignment confirmed.") + return true, nil +} + +func (config *zipalignConfiguration) zipalignArtifact(artifactPath, dstPath string) error { + cmdSlice := []string{config.zipalignPath} + if config.pageAlign { + cmdSlice = append(cmdSlice, "-p") + } + cmdSlice = append(cmdSlice, "-f", "4", artifactPath, dstPath) + log.Printf("=> %s", command.PrintableCommandArgs(false, cmdSlice)) + + _, err := keystore.ExecuteForOutput(cmdSlice) + return err +}