Skip to content

Commit 6cd6083

Browse files
Merge pull request #19 from Genymobile/dev/improve-json-parsing
Improve json parsing
2 parents 36e8a38 + 79924e3 commit 6cd6083

File tree

3 files changed

+158
-53
lines changed

3 files changed

+158
-53
lines changed

bitrise.yml

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ app:
55
envs:
66
# If you want to share this step into a StepLib
77
- BITRISE_STEP_ID: genymotion-cloud-saas-start
8-
- BITRISE_STEP_VERSION: "0.1.6"
8+
- BITRISE_STEP_VERSION: "0.1.7"
99
- BITRISE_STEP_GIT_CLONE_URL: https://github.com/genymobile/bitrise-step-genymotion-cloud-saas-start.git
1010
- MY_STEPLIB_REPO_FORK_GIT_URL: [email protected]:Genymobile/bitrise-steplib.git
1111
# Define these in your .bitrise.secrets.yml
@@ -21,8 +21,17 @@ workflows:
2121
- errcheck:
2222
- go-test:
2323

24+
run-all-tests:
25+
before_run:
26+
- test-credentials
27+
- test-api-token
28+
- test-adb-serial-port
29+
- test-several-devices
30+
- test-several-devices-with-adb-serial-port
2431

2532
test-credentials:
33+
title: "Test Credentials"
34+
description: "This step tests that the user can login and start a device with credentials."
2635
steps:
2736
- change-workdir:
2837
title: Switch working dir to test / _tmp dir
@@ -56,6 +65,8 @@ workflows:
5665
- instance_uuid: $GMCLOUD_SAAS_INSTANCE_UUID
5766

5867
test-api-token:
68+
title: "Test API Token"
69+
description: "This step tests that the user can login and start a device with the API token."
5970
steps:
6071
- change-workdir:
6172
title: Switch working dir to test / _tmp dir
@@ -79,6 +90,7 @@ workflows:
7990
inputs:
8091
- content: |
8192
#!/bin/bash
93+
gmsaas --version
8294
echo "The value of 'GMCLOUD_SAAS_INSTANCE_UUID' is: $GMCLOUD_SAAS_INSTANCE_UUID
8395
echo "The value of 'GMCLOUD_SAAS_INSTANCE_ADB_SERIAL_PORT' is: $GMCLOUD_SAAS_INSTANCE_ADB_SERIAL_PORT
8496
- git::https://github.com/Genymobile/bitrise-step-genymotion-cloud-saas-stop.git:
@@ -89,6 +101,80 @@ workflows:
89101
- instance_uuid: $GMCLOUD_SAAS_INSTANCE_UUID
90102

91103
test-adb-serial-port:
104+
title: "Test ADB Serial Port"
105+
description: "This step tests the connection to the specified ADB port for Genymotion devices."
106+
steps:
107+
- change-workdir:
108+
title: Switch working dir to test / _tmp dir
109+
description: |-
110+
To prevent step testing issues, like referencing relative
111+
files with just './some-file' in the step's code, which would
112+
work for testing the step from this directory directly
113+
but would break if the step is included in another `bitrise.yml`.
114+
run_if: true
115+
inputs:
116+
- path: ./_tmp
117+
- is_create_path: true
118+
- path::./:
119+
title: Genymotion Cloud SaaS Start
120+
run_if: "true"
121+
inputs:
122+
- email: $GMCLOUD_SAAS_EMAIL
123+
- password: $GMCLOUD_SAAS_PASSWORD
124+
- recipe_uuid:
125+
- adb_serial_port:
126+
- script:
127+
inputs:
128+
- content: |
129+
#!/bin/bash
130+
echo "The value of 'GMCLOUD_SAAS_INSTANCE_UUID' is: $GMCLOUD_SAAS_INSTANCE_UUID
131+
echo "The value of 'GMCLOUD_SAAS_INSTANCE_ADB_SERIAL_PORT' is: $GMCLOUD_SAAS_INSTANCE_ADB_SERIAL_PORT
132+
- git::https://github.com/Genymobile/bitrise-step-genymotion-cloud-saas-stop.git:
133+
title: "Genymotion Cloud SaaS Stop"
134+
description: |-
135+
Stop Genymotion Cloud SaaS Android Devices.
136+
inputs:
137+
- instance_uuid: $GMCLOUD_SAAS_INSTANCE_UUID
138+
139+
test-several-devices:
140+
title: "Test Several Devices"
141+
description: "This step tests that the user can login and start several devices with credentials."
142+
steps:
143+
- change-workdir:
144+
title: Switch working dir to test / _tmp dir
145+
description: |-
146+
To prevent step testing issues, like referencing relative
147+
files with just './some-file' in the step's code, which would
148+
work for testing the step from this directory directly
149+
but would break if the step is included in another `bitrise.yml`.
150+
run_if: true
151+
inputs:
152+
- path: ./_tmp
153+
- is_create_path: true
154+
- path::./:
155+
title: Genymotion Cloud SaaS Start
156+
run_if: "true"
157+
inputs:
158+
- email: $GMCLOUD_SAAS_EMAIL
159+
- password: $GMCLOUD_SAAS_PASSWORD
160+
- recipe_uuid:
161+
- adb_serial_port:
162+
- script:
163+
inputs:
164+
- content: |
165+
#!/bin/bash
166+
echo "The value of 'GMCLOUD_SAAS_INSTANCE_UUID' is: $GMCLOUD_SAAS_INSTANCE_UUID
167+
echo "The value of 'GMCLOUD_SAAS_INSTANCE_ADB_SERIAL_PORT' is: $GMCLOUD_SAAS_INSTANCE_ADB_SERIAL_PORT
168+
- git::https://github.com/Genymobile/bitrise-step-genymotion-cloud-saas-stop.git:
169+
title: "Genymotion Cloud SaaS Stop"
170+
description: |-
171+
Stop Genymotion Cloud SaaS Android Devices.
172+
inputs:
173+
- instance_uuid: $GMCLOUD_SAAS_INSTANCE_UUID
174+
175+
test-several-devices-with-adb-serial-port:
176+
title: "Test Several Devices with ADB Serial Port"
177+
description: "This step tests that the user can login and start several devices with specific ADB serial port."
92178
steps:
93179
- change-workdir:
94180
title: Switch working dir to test / _tmp dir

main.go

Lines changed: 69 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ import (
1111

1212
"github.com/bitrise-io/go-steputils/stepconf"
1313
"github.com/bitrise-io/go-steputils/tools"
14-
"github.com/bitrise-io/go-utils/command"
1514
"github.com/bitrise-io/go-utils/log"
1615
)
1716

1817
// Define Genymotion constants
1918
const (
2019
GMCloudSaaSInstanceUUID = "GMCLOUD_SAAS_INSTANCE_UUID"
2120
GMCloudSaaSInstanceADBSerialPort = "GMCLOUD_SAAS_INSTANCE_ADB_SERIAL_PORT"
21+
GMSaaSBinary = "gmsaas"
2222
)
2323

2424
// Define variable
@@ -98,29 +98,49 @@ func setOperationFailed(format string, args ...interface{}) {
9898
isError = true
9999
}
100100

101+
func executeCLI(binary string, args ...string) ([]byte, []byte, error) {
102+
cmd := exec.Command(binary, args...)
103+
104+
var stdoutBuf, stderrBuf strings.Builder
105+
cmd.Stdout = &stdoutBuf
106+
cmd.Stderr = &stderrBuf
107+
108+
err := cmd.Run()
109+
110+
return []byte(stdoutBuf.String()), []byte(stderrBuf.String()), err
111+
}
112+
113+
func parseJSON(data []byte) (map[string]interface{}, error) {
114+
var result map[string]interface{}
115+
116+
if err := json.Unmarshal(data, &result); err != nil {
117+
setOperationFailed("Issue with JSON parsing : %v", err)
118+
return nil, err
119+
}
120+
return result, nil
121+
}
122+
101123
func getADBSerialFromJSON(jsonData string) string {
102124
var output Output
103-
if err := json.Unmarshal([]byte(jsonData), &output); err != nil {
104-
setOperationFailed("Issue with JSON parsing : %w", err)
105-
}
125+
result, _ := parseJSON([]byte(jsonData))
126+
output.Instance.ADB_SERIAL = result["instance"].(map[string]interface{})["adb_serial"].(string)
106127
return output.Instance.ADB_SERIAL
107128
}
108129

109130
func getInstanceDetails(name string) (string, string) {
110-
cmd := command.New("gmsaas", "--format", "json", "instances", "list")
111-
out, err := cmd.RunAndReturnTrimmedCombinedOutput()
131+
args := buildGMSAASArgs("instances", "list")
132+
stdout, stderr, err := executeCLI(GMSaaSBinary, args...)
112133
if err != nil {
113-
setOperationFailed("Failed to get instances list, error: error: %s | output: %s", cmd.PrintableCommandArgs(), err, out)
134+
setOperationFailed("Failed to get instances list, error: %s | stderr: %s | stdout: %s\n", err, stderr, stdout)
114135
return "", ""
115136
}
116-
var output Output
117-
if err := json.Unmarshal([]byte(out), &output); err != nil {
118-
setOperationFailed("Issue with JSON parsing : %w", err)
119-
}
120-
121-
for _, instance := range output.Instances {
122-
if instance.NAME == name {
123-
return instance.UUID, instance.ADB_SERIAL
137+
138+
// Parse the JSON response to get instance details
139+
result, _ := parseJSON(stdout)
140+
for _, instances := range result["instances"].([]interface{}) {
141+
instance := instances.(map[string]interface{})
142+
if instance["name"] == name {
143+
return instance["uuid"].(string), instance["adb_serial"].(string)
124144
}
125145
}
126146
return "", ""
@@ -131,10 +151,10 @@ func configureAndroidSDKPath() {
131151

132152
value, exists := os.LookupEnv("ANDROID_HOME")
133153
if exists {
134-
cmd := command.New("gmsaas", "config", "set", "android-sdk-path", value)
135-
out, err := cmd.RunAndReturnTrimmedCombinedOutput()
154+
args := buildGMSAASArgs("config", "set", "android-sdk-path", value)
155+
stdout, stderr, err := executeCLI(GMSaaSBinary, args...)
136156
if err != nil {
137-
setOperationFailed("Failed to set android-sdk-path, error: error: %s | output: %s", cmd.PrintableCommandArgs(), err, out)
157+
setOperationFailed("Failed to set android-sdk-path, error: %s | stderr: %s | stdout: %s", err, stderr, stdout)
138158
return
139159
}
140160
log.Infof("Android SDK is configured")
@@ -147,18 +167,18 @@ func configureAndroidSDKPath() {
147167
func login(api_token, username, password string) {
148168
log.Infof("Login Genymotion Account")
149169

150-
var cmd *exec.Cmd
170+
var args []string
151171
if api_token != "" {
152-
cmd = exec.Command("gmsaas", "auth", "token", api_token)
172+
args = buildGMSAASArgs("auth", "token", api_token)
153173
} else if username != "" && password != "" {
154-
cmd = exec.Command("gmsaas", "auth", "login", username, password)
174+
args = buildGMSAASArgs("auth", "login", username, password)
155175
} else {
156176
abortf("Invalid arguments. Must provide either a token or both email and password.")
157177
return
158178
}
159-
160-
if out, err := cmd.CombinedOutput(); err != nil {
161-
abortf("Failed to login with gmsaas, error: error: %s | output: %s", cmd.Args, err, out)
179+
stdout, stderr, err := executeCLI(GMSaaSBinary, args...)
180+
if err != nil {
181+
abortf("Failed to login, error: %s | stderr: %s | stdout: %s\n", err, stderr, stdout)
162182
return
163183
}
164184

@@ -168,42 +188,41 @@ func login(api_token, username, password string) {
168188
func startInstanceAndConnect(wg *sync.WaitGroup, recipeUUID, instanceName, adbSerialPort string) {
169189
var output Output
170190
defer wg.Done()
171-
cmd := command.New("gmsaas", "--format", "json", "instances", "start", recipeUUID, instanceName)
172-
jsonData, err := cmd.RunAndReturnTrimmedCombinedOutput()
191+
args := buildGMSAASArgs("instances", "start", recipeUUID, instanceName)
192+
193+
stdout, stderr, err := executeCLI(GMSaaSBinary, args...)
173194
if err != nil {
174-
setOperationFailed("Failed to start a device, error: %s | output: %s\n", err, jsonData)
195+
setOperationFailed("Failed to start a device, error: %s | stderr: %s | stdout: %s\n", err, stderr, stdout)
175196
return
176197
}
177-
178-
if err := json.Unmarshal([]byte(jsonData), &output); err != nil {
179-
setOperationFailed("Issue with JSON parsing : %s", err)
180-
}
198+
199+
// Parse the JSON response to get instance details
200+
result, _ := parseJSON(stdout)
201+
output.Instance.UUID = result["instance"].(map[string]interface{})["uuid"].(string)
202+
output.Instance.ADB_SERIAL = result["instance"].(map[string]interface{})["adb_serial"].(string)
181203

182204
// Connect to adb with adb-serial-port
205+
var adbArgs []string
183206
if adbSerialPort != "" {
184-
cmd := command.New("gmsaas", "--format", "json", "instances", "adbconnect", output.Instance.UUID, "--adb-serial-port", adbSerialPort)
185-
ADBjsonData, err := cmd.RunAndReturnTrimmedCombinedOutput()
186-
if err != nil {
187-
setOperationFailed("Failed to connect a device, error: error: %s | output: %s", cmd.PrintableCommandArgs(), err, ADBjsonData)
188-
return
189-
}
190-
if err := json.Unmarshal([]byte(ADBjsonData), &output); err != nil {
191-
setOperationFailed("Issue with JSON parsing : %s", err)
192-
}
207+
adbArgs = buildGMSAASArgs("instances", "adbconnect", output.Instance.UUID, "--adb-serial-port", adbSerialPort)
193208
} else {
194-
cmd := command.New("gmsaas", "--format", "json", "instances", "adbconnect", output.Instance.UUID)
195-
ADBjsonData, err := cmd.RunAndReturnTrimmedCombinedOutput()
196-
if err != nil {
197-
setOperationFailed("Failed to connect a device, error: error: %s | output: %s", cmd.PrintableCommandArgs(), err, ADBjsonData)
198-
return
199-
}
200-
if err := json.Unmarshal([]byte(ADBjsonData), &output); err != nil {
201-
setOperationFailed("Issue with JSON parsing : %s", err)
202-
}
209+
adbArgs = buildGMSAASArgs("instances", "adbconnect", output.Instance.UUID)
203210
}
204-
211+
212+
adbStdout, adbStderr, err := executeCLI(GMSaaSBinary, adbArgs...)
213+
if err != nil {
214+
setOperationFailed("Failed to connect a device, error: %s | stderr: %s | stdout: %s\n", err, adbStderr, adbStdout)
215+
return
216+
}
217+
218+
result, _ = parseJSON(adbStdout)
219+
output.Instance.ADB_SERIAL = result["instance"].(map[string]interface{})["adb_serial"].(string)
220+
205221
log.Infof("Genymotion instance UUID : %s has been started and connected with ADB Serial Port : %s", output.Instance.UUID, output.Instance.ADB_SERIAL)
222+
}
206223

224+
func buildGMSAASArgs(args ...string) []string {
225+
return append([]string{"--format", "json"}, args...)
207226
}
208227

209228
func main() {

step.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,12 @@ inputs:
130130
For example:
131131
`4321,4322,4323`
132132
133-
- gmsaas_version: "1.11.0"
133+
- gmsaas_version: ""
134134
opts:
135135
title: gmsaas version
136136
summary: ""
137137
description: |-
138-
Install a specific version of gmsaas, per default it will install the latest compatible gmsaas version : 1.11.0
138+
Install a specific version of gmsaas, per default it will install the latest gmsaas version
139139
140140
141141

0 commit comments

Comments
 (0)