Skip to content

Commit 5cff40d

Browse files
authoredJun 17, 2019
export universal apk for aabs (bitrise-steplib#91)
* initial test * initial bundletool * extended apk uploading * type changed from apk to file for aab file * added file tag back * changed content type * renaming universal.apk to aab base * changed artifactName handling * changed upload * reverted changes * updated aab info * unzip as binary * verbose log * removed lint error * another extract * wextra log * added signing * added logs * additional logs * added logging * fixed lint error * removed unused flag * finalize
1 parent 79d467e commit 5cff40d

File tree

4 files changed

+233
-1
lines changed

4 files changed

+233
-1
lines changed
 

‎bitrise.yml

+11-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,17 @@ workflows:
7474
- notify_user_groups: $NOTIFY_USER_GROUPS
7575
- notify_email_list: $NOTIFY_EMAIL_LIST
7676
- is_enable_public_page: "true"
77-
77+
- path::./:
78+
title: Android AAB Test uncompressed
79+
run_if: true
80+
inputs:
81+
- build_url: $BITRISE_BUILD_URL
82+
- build_api_token: $BITRISE_BUILD_API_TOKEN
83+
- is_compress: "false"
84+
- deploy_path: ./app-bitrise-signed.aab
85+
- notify_user_groups: $NOTIFY_USER_GROUPS
86+
- notify_email_list: $NOTIFY_EMAIL_LIST
87+
- is_enable_public_page: "true"
7888
- path::./:
7989
title: Android Test uncompressed
8090
run_if: true

‎bundletool/bundletool.go

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package bundletool
2+
3+
import (
4+
"io/ioutil"
5+
"net/http"
6+
"path/filepath"
7+
8+
"github.com/bitrise-io/go-utils/command"
9+
"github.com/bitrise-io/go-utils/pathutil"
10+
)
11+
12+
// Runner ...
13+
type Runner struct {
14+
localPath string
15+
}
16+
17+
// NewRunner ...
18+
func NewRunner() (Runner, error) {
19+
const downloadURL = "https://github.com/google/bundletool/releases/download/0.9.0/bundletool-all-0.9.0.jar"
20+
21+
tmpPth, err := pathutil.NormalizedOSTempDirPath("tool")
22+
if err != nil {
23+
return Runner{}, err
24+
}
25+
26+
resp, err := http.Get(downloadURL)
27+
if err != nil {
28+
return Runner{}, err
29+
}
30+
31+
d, err := ioutil.ReadAll(resp.Body)
32+
if err != nil {
33+
return Runner{}, err
34+
}
35+
36+
if err := resp.Body.Close(); err != nil {
37+
return Runner{}, err
38+
}
39+
40+
toolPath := filepath.Join(tmpPth, filepath.Base(downloadURL))
41+
42+
return Runner{toolPath}, ioutil.WriteFile(toolPath, d, 0777)
43+
}
44+
45+
// Command ...
46+
func (r Runner) Command(cmd string, args ...string) *command.Model {
47+
return command.New("java", append([]string{"-jar", r.localPath, cmd}, args...)...)
48+
}

‎main.go

+11
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,17 @@ func main() {
171171
fail("Deploy failed, error: %s", err)
172172
}
173173

174+
if installPage != "" {
175+
publicInstallPages[filepath.Base(pth)] = installPage
176+
}
177+
case ".aab":
178+
log.Donef("Uploading aab file: %s", pth)
179+
180+
installPage, err := uploaders.DeployAAB(pth, config.BuildURL, config.APIToken, config.NotifyUserGroups, config.NotifyEmailList, config.IsPublicPageEnabled)
181+
if err != nil {
182+
fail("Deploy failed, error: %s", err)
183+
}
184+
174185
if installPage != "" {
175186
publicInstallPages[filepath.Base(pth)] = installPage
176187
}

‎uploaders/aabuploader.go

+163
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package uploaders
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
9+
"github.com/bitrise-io/go-utils/command"
10+
"github.com/bitrise-io/go-utils/pathutil"
11+
12+
"github.com/bitrise-io/go-utils/log"
13+
"github.com/bitrise-steplib/steps-deploy-to-bitrise-io/bundletool"
14+
)
15+
16+
// create debug keystore for signing
17+
func generateKeystore(tmpPth string) (string, error) {
18+
log.Printf("- generating debug keystore")
19+
20+
keystorePath := filepath.Join(tmpPth, "debug.keystore")
21+
cmd := command.New("keytool", "-genkey", "-v", "-keystore", keystorePath, "-storepass", "android", "-alias", "androiddebugkey",
22+
"-keypass", "android", "-keyalg", "RSA", "-keysize", "2048", "-validity", "10000", "-dname", "C=US, O=Android, CN=Android Debug").
23+
SetStdout(os.Stdout).
24+
SetStderr(os.Stderr)
25+
26+
log.Donef("$ %s", cmd.PrintableCommandArgs())
27+
28+
return keystorePath, cmd.Run()
29+
}
30+
31+
// generate `tmpDir/universal.apks` from aab file
32+
func buildApksArchive(r bundletool.Runner, tmpPth, aabPth, keystorePath string) (string, error) {
33+
log.Printf("- generating universal apk")
34+
35+
apksPth := filepath.Join(tmpPth, "universal.apks")
36+
cmd := r.Command("build-apks", "--mode=universal", "--bundle", aabPth, "--output", apksPth, "--ks", keystorePath, "--ks-pass", "pass:android", "--ks-key-alias", "androiddebugkey", "--key-pass", "pass:android").SetStdout(os.Stdout).SetStderr(os.Stderr)
37+
38+
log.Donef("$ %s", cmd.PrintableCommandArgs())
39+
40+
return apksPth, cmd.Run()
41+
}
42+
43+
// DeployAAB ...
44+
func DeployAAB(pth, buildURL, token, notifyUserGroups, notifyEmails, isEnablePublicPage string) (string, error) {
45+
log.Printf("- analyzing aab")
46+
47+
tmpPth, err := pathutil.NormalizedOSTempDirPath("aab-bundle")
48+
if err != nil {
49+
return "", err
50+
}
51+
52+
r, err := bundletool.NewRunner()
53+
if err != nil {
54+
return "", err
55+
}
56+
57+
fmt.Println()
58+
59+
keystorePath, err := generateKeystore(tmpPth)
60+
if err != nil {
61+
return "", err
62+
}
63+
64+
fmt.Println()
65+
66+
apksPth, err := buildApksArchive(r, tmpPth, pth, keystorePath)
67+
if err != nil {
68+
return "", err
69+
}
70+
71+
fmt.Println()
72+
73+
// unzip `tmpDir/universal.apks` to tmpPth to have `tmpDir/universal.apk`
74+
log.Printf("- unzip")
75+
cmd := command.New("unzip", apksPth, "-d", tmpPth).SetStdout(os.Stdout).SetStderr(os.Stderr)
76+
77+
log.Donef("$ %s", cmd.PrintableCommandArgs())
78+
79+
if err := cmd.Run(); err != nil {
80+
return "", err
81+
}
82+
83+
fmt.Println()
84+
log.Printf("- rename")
85+
86+
// rename `tmpDir/universal.apk` to `tmpDir/aab-name.aab.universal.apk`
87+
universalAPKPath, renamedUniversalAPKPath := filepath.Join(tmpPth, "universal.apk"), filepath.Join(tmpPth, filepath.Base(pth)+".universal.apk")
88+
if err := os.Rename(universalAPKPath, renamedUniversalAPKPath); err != nil {
89+
return "", err
90+
}
91+
92+
fmt.Println()
93+
94+
// get aab manifest dump
95+
log.Printf("- fetching info")
96+
97+
cmd = r.Command("dump", "manifest", "--bundle", pth)
98+
99+
log.Donef("$ %s", cmd.PrintableCommandArgs())
100+
101+
out, err := cmd.RunAndReturnTrimmedCombinedOutput()
102+
if err != nil {
103+
return "", err
104+
}
105+
106+
packageName, versionCode, versionName := filterPackageInfos(out)
107+
108+
appInfo := map[string]interface{}{
109+
"package_name": packageName,
110+
"version_code": versionCode,
111+
"version_name": versionName,
112+
}
113+
114+
log.Printf(" aab infos: %v", appInfo)
115+
116+
if packageName == "" {
117+
log.Warnf("Package name is undefined, AndroidManifest.xml package content:\n%s", out)
118+
}
119+
120+
if versionCode == "" {
121+
log.Warnf("Version code is undefined, AndroidManifest.xml package content:\n%s", out)
122+
}
123+
124+
if versionName == "" {
125+
log.Warnf("Version name is undefined, AndroidManifest.xml package content:\n%s", out)
126+
}
127+
128+
// ---
129+
130+
fileSize, err := fileSizeInBytes(pth)
131+
if err != nil {
132+
return "", fmt.Errorf("failed to get apk size, error: %s", err)
133+
}
134+
135+
aabInfoMap := map[string]interface{}{
136+
"file_size_bytes": fmt.Sprintf("%f", fileSize),
137+
"app_info": appInfo,
138+
}
139+
140+
artifactInfoBytes, err := json.Marshal(aabInfoMap)
141+
if err != nil {
142+
return "", fmt.Errorf("failed to marshal apk infos, error: %s", err)
143+
}
144+
145+
// ---
146+
147+
uploadURL, artifactID, err := createArtifact(buildURL, token, pth, "android-apk")
148+
if err != nil {
149+
return "", fmt.Errorf("failed to create apk artifact, error: %s", err)
150+
}
151+
152+
if err := uploadArtifact(uploadURL, pth, ""); err != nil {
153+
return "", fmt.Errorf("failed to upload apk artifact, error: %s", err)
154+
}
155+
156+
if _, err = finishArtifact(buildURL, token, artifactID, string(artifactInfoBytes), "", "", "false"); err != nil {
157+
return "", fmt.Errorf("failed to finish apk artifact, error: %s", err)
158+
}
159+
160+
fmt.Println()
161+
162+
return DeployAPK(renamedUniversalAPKPath, buildURL, token, notifyUserGroups, notifyEmails, isEnablePublicPage)
163+
}

0 commit comments

Comments
 (0)
Please sign in to comment.