Skip to content

Commit 96b4d87

Browse files
committed
#19 feat: download Bundletool automatically
1 parent 30ca090 commit 96b4d87

File tree

2 files changed

+145
-14
lines changed

2 files changed

+145
-14
lines changed

README.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@ brew install dector/lampa
5757

5858
DX will be improved in the future but currently you need to:
5959

60-
- Have [Java](https://adoptium.net) installed.
61-
- Have [Android SDK](https://developer.android.com/studio) installed - for now we need `aapt2` but I have plans to change it in the future.
62-
- Have [Bundletool](https://github.com/google/bundletool/releases/latest) installed.
60+
- Have [Java](https://adoptium.net) installed (for Bundletool).
61+
- Have [Android SDK](https://developer.android.com/studio) installed - for now we need `aapt2` but I have plans to change it in the future. You need to have `ANDROID_SDK_ROOT` env variable set.
62+
- (Optional) [Bundletool](https://github.com/google/bundletool/releases/latest) is optional. If `BUNDLETOOL_JAR` is not set, it will be downloaded automatically.
6363

6464
## How To Use
6565

@@ -73,7 +73,6 @@ Remember that you can always use `lampa help` if you forget something.
7373
You will need to use this report for comparative HTML report.
7474

7575
``` shell
76-
export BUNDLETOOL_JAR="~/Apps/bundletool-all-1.18.1.jar"
7776
export ANDROID_SDK_ROOT="~/Apps/AndroidSDK"
7877

7978
lampa collect
@@ -102,7 +101,6 @@ Other useful flags are:
102101
### Generate only HTML report for current version
103102

104103
``` shell
105-
export BUNDLETOOL_JAR="~/Apps/bundletool-all-1.18.1.jar"
106104
export ANDROID_SDK_ROOT="~/Apps/AndroidSDK"
107105

108106
lampa collect --format html

cmd/cli/collect/cmd.go

Lines changed: 142 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
pages "lampa/internal/templates/html"
1515
"lampa/internal/utils"
1616
"log"
17+
"net/http"
1718
"os"
1819
"os/exec"
1920
"path"
@@ -33,6 +34,9 @@ import (
3334
. "lampa/internal/globals"
3435
)
3536

37+
const BundletoolUrl = "https://github.com/google/bundletool/releases/download/1.18.1/bundletool-all-1.18.1.jar"
38+
const BundletoolHash = "e105bfd112a86986bb869d94b831c0e1e571a314"
39+
3640
const (
3741
EnvAndroidSdkRoot = "ANDROID_SDK_ROOT"
3842
EnvBundletoolJar = "BUNDLETOOL_JAR"
@@ -114,7 +118,13 @@ func parseExecArgs(c *cli.Command) ExecArgs {
114118
args.GradlewPath = path.Join(args.ProjectDir, "gradlew")
115119

116120
args.AndroidSdkPath = utils.TryResolveFsPath(os.Getenv(EnvAndroidSdkRoot))
117-
args.BundletoolPath = utils.TryResolveFsPath(os.Getenv(EnvBundletoolJar))
121+
122+
bundleToolEnv := strings.TrimSpace(os.Getenv(EnvBundletoolJar))
123+
if bundleToolEnv == "" {
124+
args.NeedToDownloadBundletool = true
125+
} else {
126+
args.BundletoolPath = utils.TryResolveFsPath(bundleToolEnv)
127+
}
118128

119129
return args
120130
}
@@ -178,13 +188,16 @@ func validateExecArgs(args *ExecArgs) error {
178188

179189
// Bundletool
180190
if args.BundletoolPath == "" {
181-
return fmt.Errorf("%s environment variable is not set", EnvBundletoolJar)
182-
}
183-
if !utils.FileExists(args.BundletoolPath) {
184-
return fmt.Errorf("bundletool jar file `%s` does not exist", args.BundletoolPath)
185-
}
186-
if utils.IsDir(args.BundletoolPath) {
187-
return fmt.Errorf("bundletool jar file `%s` is a directory", args.BundletoolPath)
191+
if !args.NeedToDownloadBundletool {
192+
return fmt.Errorf("%s environment variable is not set", EnvBundletoolJar)
193+
}
194+
} else {
195+
if !utils.FileExists(args.BundletoolPath) {
196+
return fmt.Errorf("bundletool jar file `%s` does not exist", args.BundletoolPath)
197+
}
198+
if utils.IsDir(args.BundletoolPath) {
199+
return fmt.Errorf("bundletool jar file `%s` is a directory", args.BundletoolPath)
200+
}
188201
}
189202

190203
// Aapt
@@ -244,6 +257,8 @@ type ExecArgs struct {
244257
AndroidSdkPath string
245258
AaptPath string
246259
GradlewPath string
260+
261+
NeedToDownloadBundletool bool
247262
}
248263

249264
func CmdActionCollect(ctx context.Context, cmd *cli.Command) error {
@@ -282,11 +297,22 @@ func execute(args ExecArgs) error {
282297
}
283298
}
284299
}
300+
if args.NeedToDownloadBundletool {
301+
hasWarningSection = true
302+
out.PrintlnWarn("%s is not set, so it will be downloaded automatically.", EnvBundletoolJar)
303+
}
285304
if hasWarningSection {
286305
fmt.Println()
287306
}
288307

289-
_, err := DynamicSpinner(SpinnerArgs{
308+
var err error
309+
310+
err = StepBundletool(&args)
311+
if err != nil {
312+
return err
313+
}
314+
315+
_, err = DynamicSpinner(SpinnerArgs{
290316
Msg: "Building...",
291317
MsgAfterSuccess: "Building: Done.",
292318
MsgAfterFail: "Building: Failed.",
@@ -368,6 +394,113 @@ func execute(args ExecArgs) error {
368394
return nil
369395
}
370396

397+
func StepBundletool(args *ExecArgs) error {
398+
if !args.NeedToDownloadBundletool {
399+
return nil
400+
}
401+
402+
_, err := DynamicSpinner(
403+
SpinnerArgs{
404+
Msg: "Downloading bundletool...",
405+
MsgAfterSuccess: "Downloading bundletool: Done.",
406+
MsgAfterFail: "Downloading bundletool: Failed.",
407+
},
408+
func() (any, error) {
409+
return nil, stepBundletoolInternal(args)
410+
},
411+
)
412+
413+
return err
414+
}
415+
416+
func stepBundletoolInternal(args *ExecArgs) error {
417+
// Download bundletool-all-1.18.1.jar to ./.lampa/cache
418+
cacheDir := filepath.Join(args.ProjectDir, ".lampa", "cache")
419+
bundletoolFileName := filepath.Base(BundletoolUrl)
420+
bundletoolPath := filepath.Join(cacheDir, bundletoolFileName)
421+
422+
// Ensure cache directory exists
423+
err := os.MkdirAll(cacheDir, 0o755)
424+
if err != nil {
425+
return fmt.Errorf("failed to create cache directory for bundletool: %w", err)
426+
}
427+
428+
// Download if not exists
429+
if !utils.FileExists(bundletoolPath) {
430+
// fmt.Printf("Downloading bundletool from %s...\n", BundletoolUrl)
431+
outFile, err := os.Create(bundletoolPath)
432+
if err != nil {
433+
return fmt.Errorf("failed to create bundletool file: %w", err)
434+
}
435+
defer outFile.Close()
436+
437+
client := &http.Client{}
438+
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
439+
// Allow up to 10 redirects
440+
if len(via) >= 10 {
441+
return fmt.Errorf("stopped after 10 redirects")
442+
}
443+
return nil
444+
}
445+
req, err := http.NewRequestWithContext(context.Background(), "GET", BundletoolUrl, nil)
446+
if err != nil {
447+
return fmt.Errorf("failed to create HTTP request for bundletool: %w", err)
448+
}
449+
resp, err := client.Do(req)
450+
if err != nil {
451+
return fmt.Errorf("failed to download bundletool: %w", err)
452+
}
453+
defer resp.Body.Close()
454+
455+
if resp.StatusCode != 200 {
456+
return fmt.Errorf("failed to download bundletool: HTTP %d", resp.StatusCode)
457+
}
458+
459+
_, err = io.Copy(outFile, resp.Body)
460+
if err != nil {
461+
return fmt.Errorf("failed to save bundletool: %w", err)
462+
}
463+
}
464+
465+
// Verify checksum of downloaded file
466+
expectedChecksum := BundletoolHash
467+
file, err := os.Open(bundletoolPath)
468+
if err != nil {
469+
return fmt.Errorf("failed to open bundletool file for checksum: %w", err)
470+
}
471+
defer file.Close()
472+
hasher := sha1.New()
473+
if _, err := io.Copy(hasher, file); err != nil {
474+
return fmt.Errorf("failed to compute checksum of bundletool: %w", err)
475+
}
476+
actualChecksum := fmt.Sprintf("%x", hasher.Sum(nil))
477+
if actualChecksum != expectedChecksum {
478+
return fmt.Errorf("bundletool checksum mismatch: expected %s, got %s", expectedChecksum, actualChecksum)
479+
}
480+
481+
args.BundletoolPath = utils.TryResolveFsPath(bundletoolPath)
482+
483+
// Ensure .lampa/gitignore exists and ignores all content
484+
gitignorePath := filepath.Join(args.ProjectDir, ".lampa", ".gitignore")
485+
if !utils.FileExists(gitignorePath) {
486+
err := os.MkdirAll(filepath.Dir(gitignorePath), 0o755)
487+
if err != nil {
488+
return fmt.Errorf("failed to create .lampa directory for gitignore: %w", err)
489+
}
490+
f, err := os.Create(gitignorePath)
491+
if err != nil {
492+
return fmt.Errorf("failed to create .lampa/gitignore: %w", err)
493+
}
494+
defer f.Close()
495+
_, err = f.WriteString("*\n")
496+
if err != nil {
497+
return fmt.Errorf("failed to write to .lampa/gitignore: %w", err)
498+
}
499+
}
500+
501+
return nil
502+
}
503+
371504
func StepReport(args ExecArgs) error {
372505
pathToAab, err := findAabFile(args)
373506
if err != nil {

0 commit comments

Comments
 (0)