fastlane-plugin-mango 1.1.3 → 1.3.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/lib/fastlane/plugin/mango/actions/run_dockerized_task_action.rb +103 -67
- data/lib/fastlane/plugin/mango/helper/cpu_load_handler.rb +40 -0
- data/lib/fastlane/plugin/mango/helper/docker_commander.rb +27 -32
- data/lib/fastlane/plugin/mango/helper/emulator_commander.rb +12 -4
- data/lib/fastlane/plugin/mango/helper/mango_helper.rb +45 -68
- data/lib/fastlane/plugin/mango/version.rb +1 -1
- metadata +12 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c5e9db6561834f87bc0a4c5607f7ca117532d38d35f846ed6a389e16c7da3223
|
4
|
+
data.tar.gz: fbdff88d36c647a249c76c58a9345e5aee75a23bc231ffcd01f86986bad41357
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e015e871f2eceac9afb44618dab0023819f96d3066d709b540f2e7fdd4a830c970897d192f6ac0ac36a73d06fffb7992805a1ccc8f813439d3144d3a7abf569
|
7
|
+
data.tar.gz: 077d01545b646eea107ac015b1d7f847d8ffe41a66f1c33786ae0318d39b71e7ae6321564b9a420121f86c0676ae1150ce42774c57e00240694324fcb912f522
|
data/README.md
CHANGED
@@ -78,6 +78,7 @@ The `mango` action has plenty of options to configure it.
|
|
78
78
|
| `docker_image` | Name of the Docker image, that should be started and used to run your tasks. | butomo1989/docker-android-x86-5.1.1 | ❌ | `String` |
|
79
79
|
| `container_timeout` | Timeout (in seconds) to get a healthy docker container. Depending on your `docker_image` it may take some time until it's started up and ready to use. | 450 (this equals 7.5 minutes) | ❌ | `Integer` |
|
80
80
|
| `android_task` | A generic Android task you want to execute. | - | ❌ | `String` |
|
81
|
+
| `core_amount` | Cpu core amount while starting docker container with limited resource. | - | ✅ | `Integer` |
|
81
82
|
| `sdk_path` | The path to your Android sdk directory. | `ANDROID_HOME` environment variable | ✅ | `String` |
|
82
83
|
| `port_factor` | Base for calculating a unique port for noVNC. We recommend to use the `EXECUTOR_NUMBER` from your Jenkins environment. | - | ✅ | `String` |
|
83
84
|
| `workspace_dir` | Path to the workspace to execute commands. If you want to execute your `android_task` from a different directory you have to specify `workspace_dir`. | `/root/tests/` | ✅ | `String` |
|
@@ -4,7 +4,9 @@ module Fastlane
|
|
4
4
|
module Actions
|
5
5
|
class RunDockerizedTaskAction < Action
|
6
6
|
def self.run(params)
|
7
|
-
UI.important(
|
7
|
+
UI.important('The mango plugin is working!')
|
8
|
+
workspace_dir = params[:workspace_dir]
|
9
|
+
ENV['DOCKER_CONFIG'] = "#{ENV['WORKSPACE']}/.docker"
|
8
10
|
mango_helper = Fastlane::Helper::MangoHelper.new(params)
|
9
11
|
mango_helper.setup_container
|
10
12
|
|
@@ -12,32 +14,45 @@ module Fastlane
|
|
12
14
|
|
13
15
|
failure_buffer_timeout = 5
|
14
16
|
timeout_command = "timeout #{params[:maximal_run_time] - failure_buffer_timeout}m"
|
15
|
-
workspace_dir = params[:workspace_dir]
|
16
17
|
|
17
18
|
android_task = params[:android_task]
|
18
19
|
if android_task
|
19
|
-
UI.success(
|
20
|
+
UI.success('Starting Android Task.')
|
20
21
|
bundle_install = params[:bundle_install] ? '&& bundle install ' : ''
|
21
22
|
|
22
23
|
docker_commander.exec(command: "cd #{workspace_dir} #{bundle_install}&& #{timeout_command} #{android_task} || exit 1")
|
23
24
|
end
|
24
|
-
|
25
|
+
rescue StandardError => e
|
26
|
+
begin
|
27
|
+
Actions.sh("docker logs #{mango_helper.container_name} --tail 200")
|
28
|
+
rescue StandardError
|
29
|
+
# do nothing
|
30
|
+
end
|
31
|
+
docker_commander.exec(command:
|
32
|
+
'cat /var/log/supervisor/docker-android.stderr.log', raise_when_fail: false)
|
33
|
+
docker_commander.exec(command: 'cat /var/log/supervisor/supervisord.log',
|
34
|
+
raise_when_fail: false)
|
35
|
+
raise e
|
25
36
|
ensure
|
26
|
-
|
27
|
-
|
28
|
-
|
37
|
+
begin
|
38
|
+
post_actions = params[:post_actions]
|
39
|
+
if post_actions && !mango_helper.kvm_disabled?
|
40
|
+
docker_commander&.exec(command: "cd #{workspace_dir} && #{post_actions}")
|
41
|
+
end
|
42
|
+
|
43
|
+
UI.important("Cleaning up #{params[:emulator_name]} container")
|
44
|
+
docker_commander.delete_container if mango_helper&.instance_variable_get('@container')
|
45
|
+
rescue StandardError => e
|
46
|
+
puts e
|
29
47
|
end
|
30
|
-
|
31
|
-
UI.important("Cleaning up #{params[:emulator_name]} container")
|
32
|
-
mango_helper.clean_container if mango_helper&.instance_variable_get('@container')
|
33
48
|
end
|
34
49
|
|
35
50
|
def self.description
|
36
|
-
|
51
|
+
'Action that runs Android tasks on a specified Docker image'
|
37
52
|
end
|
38
53
|
|
39
54
|
def self.authors
|
40
|
-
[
|
55
|
+
['Serghei Moret', 'Daniel Hartwich']
|
41
56
|
end
|
42
57
|
|
43
58
|
def self.return_value
|
@@ -50,123 +65,144 @@ module Fastlane
|
|
50
65
|
|
51
66
|
def self.details
|
52
67
|
# Optional:
|
53
|
-
|
68
|
+
''
|
54
69
|
end
|
55
70
|
|
56
71
|
def self.available_options
|
57
72
|
[
|
58
73
|
FastlaneCore::ConfigItem.new(key: :container_name,
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
74
|
+
env_name: 'CONTAINER_NAME',
|
75
|
+
description: 'Name of the docker container. Will be generated randomly if not defined',
|
76
|
+
optional: true,
|
77
|
+
type: String),
|
63
78
|
|
64
79
|
FastlaneCore::ConfigItem.new(key: :no_vnc_port,
|
65
|
-
env_name:
|
66
|
-
description:
|
80
|
+
env_name: 'NO_VNC_PORT',
|
81
|
+
description: 'Port to redirect noVNC. 6080 by default',
|
67
82
|
default_value: 6080,
|
68
83
|
optional: false,
|
69
84
|
type: Integer),
|
70
85
|
|
71
86
|
FastlaneCore::ConfigItem.new(key: :device_name,
|
72
|
-
env_name:
|
73
|
-
description:
|
87
|
+
env_name: 'DEVICE_NAME',
|
88
|
+
description: 'Name of the Android device. Nexus 5X by default',
|
74
89
|
default_value: 'Nexus 5X',
|
75
90
|
optional: false,
|
76
91
|
type: String),
|
77
92
|
|
78
93
|
FastlaneCore::ConfigItem.new(key: :emulator_name,
|
79
|
-
env_name:
|
80
|
-
description:
|
94
|
+
env_name: 'EMULATOR_NAME',
|
95
|
+
description: 'Name of the Android emulator. emulator-5554 by default',
|
81
96
|
default_value: 'emulator-5554',
|
82
97
|
optional: false,
|
83
98
|
type: String),
|
84
99
|
|
85
100
|
FastlaneCore::ConfigItem.new(key: :docker_image,
|
86
|
-
env_name:
|
87
|
-
description:
|
101
|
+
env_name: 'DOCKER_IMAGE',
|
102
|
+
description: 'Name of the Docker image. butomo1989/docker-android-x86-5.1.1 by default',
|
88
103
|
default_value: 'butomo1989/docker-android-x86-5.1.1',
|
89
104
|
optional: false,
|
90
105
|
type: String),
|
91
106
|
|
92
107
|
FastlaneCore::ConfigItem.new(key: :container_timeout,
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
108
|
+
env_name: 'CONTAINER_TIMEOUT',
|
109
|
+
description: 'Timeout (in seconds) to get the healthy docker container. 450 (7.5 minutes) by default',
|
110
|
+
default_value: 450,
|
111
|
+
optional: false,
|
112
|
+
type: Integer),
|
113
|
+
|
114
|
+
FastlaneCore::ConfigItem.new(key: :android_task,
|
115
|
+
env_name: 'ANDROID TASK',
|
116
|
+
description: 'A generic Android task you want to execute',
|
117
|
+
is_string: true,
|
118
|
+
optional: false),
|
104
119
|
|
105
120
|
FastlaneCore::ConfigItem.new(key: :sdk_path,
|
106
|
-
env_name:
|
107
|
-
description:
|
121
|
+
env_name: 'SDK_PATH',
|
122
|
+
description: 'The path to your Android sdk directory (root). ANDROID_HOME by default',
|
108
123
|
default_value: ENV['ANDROID_HOME'],
|
109
124
|
is_string: true,
|
110
125
|
optional: true),
|
111
126
|
|
112
127
|
FastlaneCore::ConfigItem.new(key: :port_factor,
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
128
|
+
env_name: 'PORT_FACTOR',
|
129
|
+
description: 'Base for calculating a unique port for noVNC. You can pass EXECUTOR_NUMBER from Jenkins for example, this will be unique and not clash in case of several instances running on the same machine',
|
130
|
+
optional: true,
|
131
|
+
type: String),
|
132
|
+
|
133
|
+
FastlaneCore::ConfigItem.new(key: :core_amount,
|
134
|
+
env_name: 'CORE_AMOUNT',
|
135
|
+
default_value: 0,
|
136
|
+
description: 'Define if we want to start docker container with the limitation',
|
137
|
+
optional: true,
|
138
|
+
type: Integer),
|
117
139
|
|
118
140
|
FastlaneCore::ConfigItem.new(key: :workspace_dir,
|
119
|
-
env_name:
|
141
|
+
env_name: 'WORKSPACE_DIR',
|
120
142
|
default_value: '/root/tests/',
|
121
|
-
description:
|
143
|
+
description: 'Path to the workspace to execute commands. If you want to execute your `android_task` from a different directory you have to specify `workspace_dir`',
|
122
144
|
optional: true,
|
123
145
|
type: String),
|
124
146
|
|
125
147
|
FastlaneCore::ConfigItem.new(key: :maximal_run_time,
|
126
|
-
env_name:
|
148
|
+
env_name: 'MAXIMAL_RUN_TIME',
|
127
149
|
default_value: 60,
|
128
|
-
description:
|
150
|
+
description: 'Defines the maximal time of your test run. Defaults to 60 minutes',
|
129
151
|
optional: true,
|
130
152
|
type: Integer),
|
131
153
|
|
132
154
|
FastlaneCore::ConfigItem.new(key: :bundle_install,
|
133
|
-
env_name:
|
155
|
+
env_name: 'BUNDLE_INSTALL',
|
134
156
|
default_value: false,
|
135
|
-
description:
|
157
|
+
description: 'Defines if the Android task must execute bundle install before running a build',
|
136
158
|
optional: true,
|
137
159
|
type: Boolean),
|
138
160
|
|
139
161
|
FastlaneCore::ConfigItem.new(key: :is_running_on_emulator,
|
140
|
-
env_name:
|
162
|
+
env_name: 'IS_RUNNING_ON_EMULATOR',
|
141
163
|
default_value: true,
|
142
|
-
description:
|
164
|
+
description: 'Define if we want to run the emulator in the container',
|
143
165
|
optional: true,
|
144
166
|
type: Boolean),
|
145
167
|
|
146
168
|
FastlaneCore::ConfigItem.new(key: :post_actions,
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
169
|
+
env_name: 'POST_ACTIONS',
|
170
|
+
description: 'Actions that will be executed after the main command has been executed',
|
171
|
+
is_string: true,
|
172
|
+
optional: true),
|
151
173
|
|
152
174
|
FastlaneCore::ConfigItem.new(key: :pre_action,
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
175
|
+
env_name: 'PRE_ACTION',
|
176
|
+
description: 'Actions that will be executed before the docker image gets pulled',
|
177
|
+
is_string: true,
|
178
|
+
optional: true),
|
157
179
|
|
158
180
|
FastlaneCore::ConfigItem.new(key: :docker_registry_login,
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
181
|
+
env_name: 'DOCKER_REGISTRY_LOGIN',
|
182
|
+
description: 'Authenticating yourself to a custom Docker registry',
|
183
|
+
type: String,
|
184
|
+
optional: true),
|
163
185
|
|
164
186
|
FastlaneCore::ConfigItem.new(key: :pull_latest_image,
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
187
|
+
env_name: 'PULL_LATEST_IMAGE',
|
188
|
+
description: 'Define if you want to pull the latest image',
|
189
|
+
type: Boolean,
|
190
|
+
default_value: false,
|
191
|
+
optional: true),
|
192
|
+
|
193
|
+
FastlaneCore::ConfigItem.new(key: :environment_variables,
|
194
|
+
env_name: 'ENVIRONMENT_VARIABLES',
|
195
|
+
description: 'Comma seperated list of environment variables which are passed into the docker container',
|
196
|
+
type: Array,
|
197
|
+
default_value: [],
|
198
|
+
optional: true),
|
199
|
+
|
200
|
+
FastlaneCore::ConfigItem.new(key: :vnc_enabled,
|
201
|
+
env_name: 'VNC_ENABLED',
|
202
|
+
description: 'A bool. True for vnc_enabled False for vnc_disabled',
|
203
|
+
type: Boolean,
|
204
|
+
default_value: true,
|
205
|
+
optional: true)
|
170
206
|
]
|
171
207
|
end
|
172
208
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'os'
|
2
|
+
|
3
|
+
module Fastlane
|
4
|
+
module Helper
|
5
|
+
module CpuLoadHandler
|
6
|
+
def self.print_cpu_load(load = cpu_load)
|
7
|
+
UI.important("CPU load is: #{load}") if load
|
8
|
+
end
|
9
|
+
|
10
|
+
# Gets load average of Linux machine
|
11
|
+
def self.cpu_load
|
12
|
+
load = Actions.sh('cat /proc/loadavg')
|
13
|
+
load.split(' ').first.to_f unless load.empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
# Gets amount of the CPU cores
|
17
|
+
def self.cpu_core_amount
|
18
|
+
Actions.sh("cat /proc/cpuinfo | awk '/^processor/{print $3}' | tail -1")
|
19
|
+
end
|
20
|
+
|
21
|
+
# For half an hour waiting until CPU load average is less than number of cores*2 (which considers that CPU is ready)
|
22
|
+
# Raises when 30 minutes timeout exceeds
|
23
|
+
def self.wait_cpu_to_idle
|
24
|
+
if OS.linux?
|
25
|
+
30.times do
|
26
|
+
load = cpu_load
|
27
|
+
return true if load <= cpu_core_amount.to_i * 1.5
|
28
|
+
|
29
|
+
print_cpu_load(load)
|
30
|
+
UI.important('Waiting for available resources..')
|
31
|
+
sleep 60
|
32
|
+
end
|
33
|
+
else
|
34
|
+
return true
|
35
|
+
end
|
36
|
+
raise "CPU was overloaded. Couldn't start emulator"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -1,10 +1,8 @@
|
|
1
1
|
require 'docker'
|
2
|
-
require 'os'
|
3
2
|
|
4
3
|
module Fastlane
|
5
4
|
module Helper
|
6
5
|
class DockerCommander
|
7
|
-
|
8
6
|
attr_accessor :container_name
|
9
7
|
|
10
8
|
def initialize(container_name)
|
@@ -12,66 +10,63 @@ module Fastlane
|
|
12
10
|
end
|
13
11
|
|
14
12
|
def pull_image(docker_image_name:)
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
Actions.sh("docker pull #{docker_image_name}")
|
14
|
+
rescue StandardError => e
|
15
|
+
prune if e.message =~ /Create more free space in thin pool/
|
16
|
+
Actions.sh("docker pull #{docker_image_name}")
|
18
17
|
end
|
19
18
|
|
20
|
-
def start_container(emulator_args:, docker_image:)
|
19
|
+
def start_container(emulator_args:, docker_image:, core_amount:)
|
20
|
+
retries ||= 0
|
21
21
|
docker_name = if container_name
|
22
22
|
"--name #{container_name}"
|
23
23
|
else
|
24
24
|
''
|
25
25
|
end
|
26
|
+
# if core_amount value is defined then limit the container while starting
|
27
|
+
core_amount = if core_amount && core_amount > 0
|
28
|
+
"--cpus=#{core_amount}"
|
29
|
+
else
|
30
|
+
''
|
31
|
+
end
|
26
32
|
|
27
33
|
# Action.sh returns all output that the command produced but we are only
|
28
34
|
# interested in the last line, since it contains the id of the created container.
|
29
35
|
UI.important("Attaching #{ENV['PWD']} to the docker container")
|
30
|
-
|
31
|
-
|
36
|
+
Actions.sh("docker run -v $PWD:/root/tests --privileged -t -d #{core_amount} #{emulator_args} #{docker_name} #{docker_image}").chomp
|
37
|
+
rescue StandardError => e
|
38
|
+
if e.message =~ /Create more free space in thin pool/ && (retries += 1) < 2
|
39
|
+
prune
|
40
|
+
retry
|
32
41
|
end
|
33
42
|
end
|
34
43
|
|
35
|
-
def stop_container
|
36
|
-
Actions.sh("docker stop #{container_name}") if container_name
|
37
|
-
end
|
38
|
-
|
39
44
|
def delete_container
|
40
|
-
Actions.sh("docker rm #{container_name}") if container_name
|
45
|
+
Actions.sh("docker rm -f #{container_name}") if container_name
|
41
46
|
end
|
42
47
|
|
43
48
|
def disconnect_network_bridge
|
49
|
+
UI.important('Disconnecting from the network bridge')
|
44
50
|
Actions.sh("docker network disconnect -f bridge #{container_name}") if container_name
|
45
51
|
rescue StandardError
|
46
52
|
# Do nothing if the network bridge is already gone
|
47
53
|
end
|
48
54
|
|
49
|
-
def exec(command:)
|
55
|
+
def exec(command:, raise_when_fail: true)
|
50
56
|
if container_name
|
51
|
-
|
57
|
+
begin
|
58
|
+
Actions.sh("docker exec #{container_name} bash -l -c \"#{command}\"")
|
59
|
+
rescue StandardError => e
|
60
|
+
raise(e) if raise_when_fail
|
61
|
+
end
|
52
62
|
else
|
53
63
|
raise('Cannot execute docker command because the container name is unknown')
|
54
64
|
end
|
55
65
|
end
|
56
|
-
|
66
|
+
|
57
67
|
def prune
|
58
68
|
Action.sh('docker system prune -f')
|
59
69
|
end
|
60
|
-
|
61
|
-
def handle_thin_pool_exception(&block)
|
62
|
-
begin
|
63
|
-
block.call
|
64
|
-
rescue FastlaneCore::Interface::FastlaneShellError => exception
|
65
|
-
retry_counter = retry_counter.to_i + 1
|
66
|
-
if exception.message =~ /Create more free space in thin pool/ && retry_counter < 2
|
67
|
-
prune
|
68
|
-
retry
|
69
|
-
else
|
70
|
-
raise exception
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
70
|
end
|
76
71
|
end
|
77
|
-
end
|
72
|
+
end
|
@@ -2,9 +2,7 @@ require_relative 'docker_commander'
|
|
2
2
|
|
3
3
|
module Fastlane
|
4
4
|
module Helper
|
5
|
-
|
6
5
|
class EmulatorCommander
|
7
|
-
|
8
6
|
attr_accessor :container_name
|
9
7
|
|
10
8
|
def initialize(container_name)
|
@@ -22,7 +20,7 @@ module Fastlane
|
|
22
20
|
# it recovers after some time, so wait and retry
|
23
21
|
retry_counter = retry_counter.to_i + 1
|
24
22
|
if retry_counter <= 5
|
25
|
-
sleep 10*retry_counter
|
23
|
+
sleep 10 * retry_counter
|
26
24
|
retry
|
27
25
|
else
|
28
26
|
raise e
|
@@ -50,7 +48,17 @@ module Fastlane
|
|
50
48
|
def emulator_is_healthy?
|
51
49
|
list_devices = @docker_commander.exec(command: 'adb devices')
|
52
50
|
list_devices.include? "\tdevice"
|
51
|
+
rescue FastlaneCore::Interface::FastlaneShellError => e
|
52
|
+
# Under weird circumstances it can happen that adb is running but adb is not completely up
|
53
|
+
# it recovers after some time, so wait and retry
|
54
|
+
retry_counter = retry_counter.to_i + 1
|
55
|
+
if retry_counter <= 5
|
56
|
+
sleep 10 * retry_counter
|
57
|
+
retry
|
58
|
+
else
|
59
|
+
raise e
|
60
|
+
end
|
53
61
|
end
|
54
62
|
end
|
55
63
|
end
|
56
|
-
end
|
64
|
+
end
|
@@ -1,14 +1,13 @@
|
|
1
|
-
require 'docker'
|
2
|
-
require 'timeout'
|
3
|
-
require 'os'
|
4
1
|
require 'net/http'
|
5
2
|
require_relative 'docker_commander'
|
6
3
|
require_relative 'emulator_commander'
|
4
|
+
require_relative 'cpu_load_handler'
|
7
5
|
|
8
6
|
module Fastlane
|
9
7
|
module Helper
|
10
8
|
class MangoHelper
|
11
|
-
attr_reader :container_name, :no_vnc_port, :device_name, :docker_image, :timeout, :port_factor,
|
9
|
+
attr_reader :container_name, :no_vnc_port, :device_name, :docker_image, :timeout, :port_factor,
|
10
|
+
:maximal_run_time, :sleep_interval, :is_running_on_emulator, :environment_variables, :vnc_enabled, :core_amount
|
12
11
|
|
13
12
|
def initialize(params)
|
14
13
|
@container_name = params[:container_name]
|
@@ -18,6 +17,7 @@ module Fastlane
|
|
18
17
|
@timeout = params[:container_timeout]
|
19
18
|
@sdk_path = params[:sdk_path]
|
20
19
|
@port_factor = params[:port_factor].to_i
|
20
|
+
@core_amount = params[:core_amount].to_i
|
21
21
|
@maximal_run_time = params[:maximal_run_time]
|
22
22
|
@sleep_interval = 5
|
23
23
|
@container = nil
|
@@ -26,7 +26,8 @@ module Fastlane
|
|
26
26
|
@pre_action = params[:pre_action]
|
27
27
|
@docker_registry_login = params[:docker_registry_login]
|
28
28
|
@pull_latest_image = params[:pull_latest_image]
|
29
|
-
|
29
|
+
@environment_variables = params[:environment_variables]
|
30
|
+
@vnc_enabled = params[:vnc_enabled]
|
30
31
|
@docker_commander = DockerCommander.new(container_name)
|
31
32
|
@emulator_commander = EmulatorCommander.new(container_name)
|
32
33
|
end
|
@@ -40,13 +41,11 @@ module Fastlane
|
|
40
41
|
assign_unique_vnc_port if port_factor && is_running_on_emulator
|
41
42
|
|
42
43
|
if container_available?
|
43
|
-
|
44
|
-
@
|
44
|
+
UI.important('Container was already started. Stopping and removing..')
|
45
|
+
@docker_commander.delete_container
|
45
46
|
end
|
46
47
|
|
47
|
-
handle_ports_allocation if is_running_on_emulator
|
48
|
-
|
49
|
-
execute_pre_action if @pre_action
|
48
|
+
handle_ports_allocation if is_running_on_emulator && vnc_enabled
|
50
49
|
|
51
50
|
pull_from_registry if @pull_latest_image
|
52
51
|
|
@@ -67,14 +66,20 @@ module Fastlane
|
|
67
66
|
end
|
68
67
|
|
69
68
|
unless container_state
|
70
|
-
UI.important("Will retry
|
71
|
-
@
|
72
|
-
@container.delete(force: true)
|
69
|
+
UI.important("Will retry to create a healthy docker container after #{sleep_interval} seconds")
|
70
|
+
@docker_commander.delete_container
|
73
71
|
sleep @sleep_interval
|
74
72
|
create_container
|
75
73
|
|
76
74
|
unless wait_for_healthy_container
|
77
75
|
UI.important('Container is unhealthy. Exiting..')
|
76
|
+
begin
|
77
|
+
Actions.sh("docker logs #{container_name} --tail 200")
|
78
|
+
Actions.sh("docker exec -i #{container_name} cat /var/log/supervisor/docker-android.stderr.log")
|
79
|
+
Actions.sh("docker exec -i #{container_name} cat /var/log/supervisor/supervisord.log")
|
80
|
+
rescue StandardError
|
81
|
+
# do nothing
|
82
|
+
end
|
78
83
|
# We use code "2" as we need something than just standard error code 1, so we can differentiate the next step in CI
|
79
84
|
exit 2
|
80
85
|
end
|
@@ -89,6 +94,8 @@ module Fastlane
|
|
89
94
|
@emulator_commander.disable_animations
|
90
95
|
@emulator_commander.increase_logcat_storage
|
91
96
|
end
|
97
|
+
|
98
|
+
execute_pre_action if @pre_action
|
92
99
|
end
|
93
100
|
|
94
101
|
def kvm_disabled?
|
@@ -100,12 +107,6 @@ module Fastlane
|
|
100
107
|
@docker_commander.exec(command: 'cat kvm-ok.txt').include?('KVM acceleration can NOT be used')
|
101
108
|
end
|
102
109
|
|
103
|
-
# Stops and remove container
|
104
|
-
def clean_container
|
105
|
-
@container.stop
|
106
|
-
@container.delete(force: true)
|
107
|
-
end
|
108
|
-
|
109
110
|
private
|
110
111
|
|
111
112
|
# Sets path to adb
|
@@ -116,7 +117,7 @@ module Fastlane
|
|
116
117
|
# assigns vnc port
|
117
118
|
def assign_unique_vnc_port
|
118
119
|
@no_vnc_port = 6080 + port_factor
|
119
|
-
@host_ip_address = `hostname -
|
120
|
+
@host_ip_address = `hostname -i | head -n1 | awk '{print $1;}'`.delete!("\n")
|
120
121
|
UI.success("Port: #{@no_vnc_port} was chosen for VNC")
|
121
122
|
UI.success("Link to VNC: http://#{@host_ip_address}:#{@no_vnc_port}")
|
122
123
|
end
|
@@ -124,49 +125,55 @@ module Fastlane
|
|
124
125
|
# Creates new container using params
|
125
126
|
def create_container
|
126
127
|
UI.important("Creating container: #{container_name}")
|
127
|
-
print_cpu_load
|
128
|
+
CpuLoadHandler.print_cpu_load
|
128
129
|
begin
|
129
130
|
container = create_container_call
|
130
131
|
set_container_name(container)
|
131
132
|
rescue StandardError
|
132
133
|
UI.important("Something went wrong while creating: #{container_name}, will retry in #{@sleep_interval} seconds")
|
133
|
-
print_cpu_load
|
134
|
-
@docker_commander.stop_container
|
134
|
+
CpuLoadHandler.print_cpu_load
|
135
135
|
@docker_commander.delete_container
|
136
|
-
|
137
136
|
sleep @sleep_interval
|
138
137
|
container = create_container_call
|
139
138
|
set_container_name(container)
|
140
139
|
end
|
141
|
-
get_container_instance(container)
|
140
|
+
@container = get_container_instance(container)
|
141
|
+
|
142
|
+
if @container.nil?
|
143
|
+
sleep 3
|
144
|
+
@container = get_container_instance(container)
|
145
|
+
end
|
142
146
|
end
|
143
147
|
|
144
148
|
# Gets container instance by container ID
|
145
149
|
def get_container_instance(container)
|
146
150
|
Docker::Container.all(all: true).each do |cont|
|
147
|
-
if cont.id == container
|
148
|
-
@container = cont
|
149
|
-
break
|
150
|
-
end
|
151
|
+
return cont if cont.id == container
|
151
152
|
end
|
152
153
|
end
|
153
154
|
|
154
155
|
# Call to create a container. We don't use Docker API here, as it doesn't support --privileged.
|
155
156
|
def create_container_call
|
156
157
|
# When CPU is under load we cannot create a healthy container
|
157
|
-
wait_cpu_to_idle
|
158
|
+
CpuLoadHandler.wait_cpu_to_idle
|
158
159
|
|
160
|
+
additional_env = ''
|
161
|
+
environment_variables.each do |variable|
|
162
|
+
additional_env += " -e #{variable}"
|
163
|
+
end
|
159
164
|
emulator_args = is_running_on_emulator ? "-p #{no_vnc_port}:6080 -e DEVICE='#{device_name}'" : ''
|
160
|
-
|
161
|
-
@docker_commander.start_container(emulator_args: emulator_args, docker_image: docker_image
|
165
|
+
emulator_args = "#{emulator_args}#{additional_env}"
|
166
|
+
@docker_commander.start_container(emulator_args: emulator_args, docker_image: docker_image,
|
167
|
+
core_amount: core_amount)
|
162
168
|
end
|
163
169
|
|
164
170
|
def execute_pre_action
|
165
|
-
|
171
|
+
@docker_commander.exec(command: @pre_action)
|
166
172
|
end
|
167
173
|
|
168
174
|
# Pull the docker images before creating a container
|
169
175
|
def pull_from_registry
|
176
|
+
UI.important('Pulling the :latest image from the registry')
|
170
177
|
docker_image_name = docker_image.gsub(':latest', '')
|
171
178
|
Actions.sh(@docker_registry_login) if @docker_registry_login
|
172
179
|
@docker_commander.pull_image(docker_image_name: docker_image_name)
|
@@ -183,7 +190,6 @@ module Fastlane
|
|
183
190
|
if port_open?('0.0.0.0', @no_vnc_port)
|
184
191
|
UI.important('Something went wrong. VNC port is still busy')
|
185
192
|
sleep @sleep_interval
|
186
|
-
@docker_commander.stop_container
|
187
193
|
@docker_commander.delete_container
|
188
194
|
end
|
189
195
|
end
|
@@ -197,17 +203,14 @@ module Fastlane
|
|
197
203
|
nil
|
198
204
|
end
|
199
205
|
|
200
|
-
def print_cpu_load(load = cpu_load)
|
201
|
-
UI.important("CPU load is: #{load}")
|
202
|
-
end
|
203
|
-
|
204
206
|
# Checks if container is already available
|
205
207
|
def container_available?
|
206
208
|
return false unless container_name
|
209
|
+
|
207
210
|
all_containers = Docker::Container.all(all: true)
|
208
211
|
|
209
212
|
all_containers.each do |container|
|
210
|
-
if container.info['Names'].first[1
|
213
|
+
if container.info['Names'].first[1..] == container_name
|
211
214
|
@container = container
|
212
215
|
return true
|
213
216
|
end
|
@@ -246,6 +249,9 @@ module Fastlane
|
|
246
249
|
end
|
247
250
|
UI.important("The Container failed to load after '#{timeout}' seconds timeout. Reason: '#{@container.json['State']['Status']}'")
|
248
251
|
false
|
252
|
+
rescue StandardError => e
|
253
|
+
puts e
|
254
|
+
false
|
249
255
|
end
|
250
256
|
|
251
257
|
# Checks if port is already openZ
|
@@ -257,41 +263,12 @@ module Fastlane
|
|
257
263
|
false
|
258
264
|
end
|
259
265
|
|
260
|
-
# Gets load average of Linux machine
|
261
|
-
def cpu_load
|
262
|
-
load = `cat /proc/loadavg`
|
263
|
-
load.split(' ').first.to_f
|
264
|
-
end
|
265
|
-
|
266
|
-
# Gets amount of the CPU cores
|
267
|
-
def cpu_core_amount
|
268
|
-
`cat /proc/cpuinfo | awk '/^processor/{print $3}' | tail -1`
|
269
|
-
end
|
270
|
-
|
271
|
-
# For half an hour waiting until CPU load average is less than number of cores*2 (which considers that CPU is ready)
|
272
|
-
# Raises when 30 minutes timeout exceeds
|
273
|
-
def wait_cpu_to_idle
|
274
|
-
if OS.linux?
|
275
|
-
30.times do
|
276
|
-
load = cpu_load
|
277
|
-
return true if load < cpu_core_amount.to_i * 1.5
|
278
|
-
print_cpu_load(load)
|
279
|
-
UI.important('Waiting for available resources..')
|
280
|
-
sleep 60
|
281
|
-
end
|
282
|
-
else
|
283
|
-
return true
|
284
|
-
end
|
285
|
-
raise "CPU was overloaded. Couldn't start emulator"
|
286
|
-
end
|
287
|
-
|
288
266
|
# if we do not have container name, we cane use container ID that we got from create call
|
289
267
|
def set_container_name(container)
|
290
268
|
unless container_name
|
291
269
|
@container_name = @emulator_commander.container_name = @docker_commander.container_name = container
|
292
270
|
end
|
293
271
|
end
|
294
|
-
|
295
272
|
end
|
296
273
|
end
|
297
274
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastlane-plugin-mango
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.23
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Serghei Moret, Daniel Hartwich
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-03-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: docker-api
|
@@ -112,16 +112,16 @@ dependencies:
|
|
112
112
|
name: rubocop
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- -
|
115
|
+
- - ">="
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: 0
|
117
|
+
version: '0'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- -
|
122
|
+
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: 0
|
124
|
+
version: '0'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: simplecov
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -136,7 +136,7 @@ dependencies:
|
|
136
136
|
- - ">="
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0'
|
139
|
-
description:
|
139
|
+
description:
|
140
140
|
email: serghei.moret@xing.com, hartwich.daniel@gmail.com
|
141
141
|
executables: []
|
142
142
|
extensions: []
|
@@ -146,6 +146,7 @@ files:
|
|
146
146
|
- README.md
|
147
147
|
- lib/fastlane/plugin/mango.rb
|
148
148
|
- lib/fastlane/plugin/mango/actions/run_dockerized_task_action.rb
|
149
|
+
- lib/fastlane/plugin/mango/helper/cpu_load_handler.rb
|
149
150
|
- lib/fastlane/plugin/mango/helper/docker_commander.rb
|
150
151
|
- lib/fastlane/plugin/mango/helper/emulator_commander.rb
|
151
152
|
- lib/fastlane/plugin/mango/helper/mango_helper.rb
|
@@ -154,7 +155,7 @@ homepage: https://github.com/xing/mango
|
|
154
155
|
licenses:
|
155
156
|
- MIT
|
156
157
|
metadata: {}
|
157
|
-
post_install_message:
|
158
|
+
post_install_message:
|
158
159
|
rdoc_options: []
|
159
160
|
require_paths:
|
160
161
|
- lib
|
@@ -169,9 +170,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
169
170
|
- !ruby/object:Gem::Version
|
170
171
|
version: '0'
|
171
172
|
requirements: []
|
172
|
-
|
173
|
-
|
174
|
-
signing_key:
|
173
|
+
rubygems_version: 3.1.2
|
174
|
+
signing_key:
|
175
175
|
specification_version: 4
|
176
176
|
summary: This plugin Android tasks on docker images
|
177
177
|
test_files: []
|