Skip to content

Commit

Permalink
Updated tool lookup paths for new SDK layout (#23)
Browse files Browse the repository at this point in the history
* Updated tool lookup with new SDK layout
* Updated to go modules
* Log emulator output on failure
* Adding sleep after killing emulator in test
* Removed time.Sleep
  • Loading branch information
lpusok authored Jun 25, 2021
1 parent d46b9e1 commit dd09e2e
Show file tree
Hide file tree
Showing 24 changed files with 1,748 additions and 197 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
steps-avd-manager
.bitrise*
_tmp
50 changes: 0 additions & 50 deletions Gopkg.lock

This file was deleted.

15 changes: 0 additions & 15 deletions Gopkg.toml

This file was deleted.

4 changes: 4 additions & 0 deletions bitrise.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ workflows:
#!/bin/bash
set -x
adb -s $BITRISE_EMULATOR_SERIAL emu kill
adb devices
# It takes a bit of time for the simulator to exit
sleep 5
adb devices
# ----------------------------------------------------------------
# --- workflows to Share this step into a Step Library
Expand Down
10 changes: 10 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module github.com/bitrise-steplib/steps-avd-manager

go 1.16

require (
github.com/bitrise-io/go-android v0.0.0-20210527143215-3ad22ad02e2e
github.com/bitrise-io/go-steputils v0.0.0-20210514150206-5b6261447e77
github.com/bitrise-io/go-utils v0.0.0-20210520073355-367fa34178f5
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
)
50 changes: 50 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
github.com/bitrise-io/go-android v0.0.0-20210527143215-3ad22ad02e2e h1:lkJnz+yXbIqFGpDTdRBBshqxeX0UCndJmEOp0yy2GRQ=
github.com/bitrise-io/go-android v0.0.0-20210527143215-3ad22ad02e2e/go.mod h1:gGXmY8hJ1x44AC98TIvZZvxP7o+hs4VI6wgmO4JMfEg=
github.com/bitrise-io/go-steputils v0.0.0-20210514150206-5b6261447e77 h1:+wd+ADdtJCRL9JEghE1RMbR4ywXBYNvIBelAW/UkWr8=
github.com/bitrise-io/go-steputils v0.0.0-20210514150206-5b6261447e77/go.mod h1:H0iZjgsAR5NA6pnlD/zKB6AbxEsskq55pwJ9klVmP8w=
github.com/bitrise-io/go-utils v0.0.0-20210507100250-37de47dfa6ce/go.mod h1:15EZZf02noI5nWFqXMZEoyb1CyqYRXTMz5Fyu4CWFzI=
github.com/bitrise-io/go-utils v0.0.0-20210520073355-367fa34178f5 h1:kclxBfygfNK6kWUB+9xcsfPLBen8Us9gubhitfL/Z6c=
github.com/bitrise-io/go-utils v0.0.0-20210520073355-367fa34178f5/go.mod h1:DRx7oFuAqk0dbKpAKCqWl0TgrowfJUb/MqYPRscxJOQ=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw=
github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
145 changes: 96 additions & 49 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package main

import (
"bufio"
"bytes"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"time"

"github.com/bitrise-io/go-android/sdk"
"github.com/bitrise-io/go-steputils/stepconf"
"github.com/bitrise-io/go-steputils/tools"
"github.com/bitrise-io/go-utils/command"
Expand All @@ -18,7 +20,8 @@ import (

// config ...
type config struct {
AndroidHome string `env:"ANDROID_HOME,required"`
AndroidHome string `env:"ANDROID_HOME"`
AndroidSDKRoot string `env:"ANDROID_SDK_ROOT"`
APILevel int `env:"api_level,required"`
Tag string `env:"tag,opt[google_apis,google_apis_playstore,android-wear,android-tv,default]"`
DeviceProfile string `env:"profile,required"`
Expand All @@ -30,12 +33,15 @@ type config struct {
}

func runningDeviceInfos(androidHome string) (map[string]string, error) {
cmd := command.New(filepath.Join(androidHome, "platform-tools/adb"), "devices")
cmd := command.New(filepath.Join(androidHome, "platform-tools", "adb"), "devices")
out, err := cmd.RunAndReturnTrimmedCombinedOutput()
if err != nil {
return map[string]string{}, fmt.Errorf("command failed, error: %s", err)
}

log.Debugf("$ %s", cmd.PrintableCommandArgs())
log.Debugf("%s", out)

// List of devices attached
// emulator-5554 device
deviceListItemPattern := `^(?P<emulator>emulator-\d*)[\s+](?P<state>.*)`
Expand Down Expand Up @@ -88,6 +94,17 @@ func currentlyStartedDeviceSerial(alreadyRunningDeviceInfos, currentlyRunningDev
return ""
}

func queryNewDeviceSerial(androidHome string, runningDevices map[string]string) (string, error) {
currentRunningDevices, err := runningDeviceInfos(androidHome)
if err != nil {
return "", fmt.Errorf("failed to check running devices: %s", err)
}

serial := currentlyStartedDeviceSerial(runningDevices, currentRunningDevices)

return serial, nil
}

type phase struct {
name string
command *command.Model
Expand All @@ -102,15 +119,31 @@ func main() {
stepconf.Print(cfg)
fmt.Println()

runningDevices, err := runningDeviceInfos(cfg.AndroidHome)
// Initialize Android SDK
log.Printf("Initialize Android SDK")
androidSdk, err := sdk.NewDefaultModel(sdk.Environment{
AndroidHome: cfg.AndroidHome,
AndroidSDKRoot: cfg.AndroidSDKRoot,
})
if err != nil {
failf("Failed to initialize Android SDK: %s", err)
}

androidHome := androidSdk.GetAndroidHome()
runningDevices, err := runningDeviceInfos(androidHome)
if err != nil {
failf("Failed to check running devices, error: %s", err)
}

cmdlineToolsPath, err := androidSdk.CmdlineToolsPath()
if err != nil {
failf("Could not locate Android command-line tools: %v", err)
}

var (
sdkManagerPath = filepath.Join(cfg.AndroidHome, "tools/bin/sdkmanager")
avdManagerPath = filepath.Join(cfg.AndroidHome, "tools/bin/avdmanager")
emulatorPath = filepath.Join(cfg.AndroidHome, "emulator/emulator")
sdkManagerPath = filepath.Join(cmdlineToolsPath, "sdkmanager")
avdManagerPath = filepath.Join(cmdlineToolsPath, "avdmanager")
emulatorPath = filepath.Join(androidHome, "emulator", "emulator")

pkg = fmt.Sprintf("system-images;android-%d;%s;%s", cfg.APILevel, cfg.Tag, cfg.Abi)
yes, no = strings.Repeat("yes\n", 20), strings.Repeat("no\n", 20)
Expand All @@ -127,19 +160,19 @@ func main() {
}

for _, phase := range []phase{
{"Update emulator",
{"Updating emulator",
command.New(sdkManagerPath, "--verbose", "--channel="+cfg.EmulatorChannel, "emulator").
SetStdin(strings.NewReader(yes)), // hitting yes in case it waits for accepting license
nil,
},

{"Update system-image packages",
{"Updating system-image packages",
command.New(sdkManagerPath, "--verbose", pkg).
SetStdin(strings.NewReader(yes)), // hitting yes in case it waits for accepting license
nil,
},

{"Create device",
{"Creating device",
command.New(avdManagerPath, append([]string{
"--verbose", "create", "avd", "--force",
"--name", cfg.ID,
Expand All @@ -150,25 +183,6 @@ func main() {
SetStdin(strings.NewReader(no)), // hitting no in case it asks for creating hw profile
nil,
},

{"Start device",
command.New(emulatorPath, append([]string{
"@" + cfg.ID,
"-verbose",
"-show-kernel",
"-no-audio",
"-no-window",
"-no-boot-anim",
"-netdelay", "none",
"-no-snapshot",
"-wipe-data",
"-gpu", "swiftshader_indirect"}, startCustomFlags...)...),
func(cmd *command.Model) func() (string, error) { // need to start the emlator as a detached process
return func() (string, error) {
return "", cmd.GetCmd().Start()
}
},
},
} {
log.Infof(phase.name)
log.Donef("$ %s", phase.command.PrintableCommandArgs())
Expand All @@ -179,37 +193,70 @@ func main() {
}

if out, err := exec(); err != nil {
failf("Failed to run phase, error: %s, output: %s", err, out)
failf("Failed to run phase: %s, output: %s", err, out)
}

fmt.Println()
}

deviceDetectionStarted := time.Now()
for true {
currentRunningDevices, err := runningDeviceInfos(cfg.AndroidHome)
if err != nil {
failf("Failed to check running devices, error: %s", err)
}

serial := currentlyStartedDeviceSerial(runningDevices, currentRunningDevices)

if serial != "" {
if err := tools.ExportEnvironmentWithEnvman("BITRISE_EMULATOR_SERIAL", serial); err != nil {
log.Warnf("Failed to export environment (BITRISE_EMULATOR_SERIAL), error: %s", err)
var output bytes.Buffer
deviceStartCmd := command.New(emulatorPath, append([]string{
"@" + cfg.ID,
"-verbose",
"-show-kernel",
"-no-audio",
"-no-window",
"-no-boot-anim",
"-netdelay", "none",
"-no-snapshot",
"-wipe-data",
"-gpu", "swiftshader_indirect"}, startCustomFlags...)...,
).SetStdout(&output).SetStderr(&output)

log.Infof("Starting device")
log.Donef("$ %s", deviceStartCmd.PrintableCommandArgs())
// start the emlator as a detached process
emulatorWaitCh := make(chan error, 1)
if err := deviceStartCmd.GetCmd().Start(); err != nil {
failf("Failed to run device start command: %v", err)
}
go func() {
emulatorWaitCh <- deviceStartCmd.GetCmd().Wait()
}()

var serial string
const bootWaitTime = time.Duration(300)
timeout := time.NewTimer(bootWaitTime * time.Second)
deviceCheckTicker := time.NewTicker(5 * time.Second)

waitLoop:
for {
select {
case err := <-emulatorWaitCh:
log.Warnf("Emulator log: %s", output)
if err != nil {
failf("Emulator exited unexpectedly: %v", err)
}
log.Printf("- Device with serial: %s started", serial)
break
}

bootWaitTime := time.Duration(300)

if time.Now().After(deviceDetectionStarted.Add(bootWaitTime * time.Second)) {
failf("Emulator exited early, without error. A possible cause can be the emulator process having received a KILL signal.")
case <-timeout.C:
log.Warnf("Emulator log: %s", output)
failf("Failed to boot emulator device within %d seconds.", bootWaitTime)
case <-deviceCheckTicker.C:
serial, err = queryNewDeviceSerial(androidHome, runningDevices)
if err != nil {
failf("Error: %s", err)
} else if serial != "" {
break waitLoop
}
}
}
timeout.Stop()
deviceCheckTicker.Stop()

time.Sleep(5 * time.Second)
if err := tools.ExportEnvironmentWithEnvman("BITRISE_EMULATOR_SERIAL", serial); err != nil {
log.Warnf("Failed to export environment (BITRISE_EMULATOR_SERIAL), error: %s", err)
}
log.Printf("- Device with serial: %s started", serial)

log.Donef("- Done")
}
Loading

0 comments on commit dd09e2e

Please sign in to comment.