fastlane-plugin-mango 1.1.6 → 1.3.25

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ed170b21d2657138918bb05cda96147bcec67db16311d04b179f7a897f67b0df
4
- data.tar.gz: c57446317b989992adfc8917a7e413a41f171110be888d38bde576e4bdde1a33
3
+ metadata.gz: e38a84cceed91c04a4ce62698766017ee521d3721d885276087b8b47ec57de73
4
+ data.tar.gz: 9e8569619ff82aa951602614b7aae19a1a1247af2ab5b87ba2ba3fb665130132
5
5
  SHA512:
6
- metadata.gz: 4486ba5047f7a8757bd93cdaed54ceb35a1cc2997da737771cce5adcde318e989e76eed1a957da5bcb2f5a13b1856723e110d621e80d62218ffdfbfe1d218b9f
7
- data.tar.gz: e8ddca2399e990354181dc65ba64d23614763a10183a73e973be87fa5a3eee4b283b2da942adb48b37ee59697d8d94c4d98a4adbf3f8e4d0467c5484d6d50f54
6
+ metadata.gz: e4721cdac4969cdfd8dd7f86143b07edc87ce0853b868ba68d0c3c509f23265300832ff77e6acb3f761c78d8c86c11944768de059c092927cec3f856d95213ae
7
+ data.tar.gz: 4a677f075b53365135d3a498e4393cc37cef755e27dd18cba8299fe602d571562b29c7955c6d3e5dce709cbec54e604615d3e8afee4bb747921c6e89ca5524a9
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-badge.svg)](https://rubygems.org/gems/fastlane-plugin-mango) [![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/xing/mango/blob/master/LICENSE)
5
5
  [![Gem](https://img.shields.io/gem/v/fastlane-plugin-mango.svg?style=flat)](http://rubygems.org/gems/fastlane-plugin-mango)
6
6
 
7
- A fastlane plugin that runs Android tasks on a specified [Docker](https://www.docker.com/) image
7
+ A fastlane plugin that runs Android tasks on a specified [Docker](https://www.docker.com/) image. Can be used for orchestrating android tasks on a CI syste.
8
8
 
9
9
  <img src="assets/mango_logo.png" alt="Mango Logo" width="256px" height="256px"/>
10
10
 
@@ -47,19 +47,26 @@ desc "Run espresso tests on docker images"
47
47
  end
48
48
  ```
49
49
 
50
- or to this for unit tests or other gradle tasks:
50
+ or to this for unit tests or other gradle tasks(some of the variables are optional):
51
51
  ```ruby
52
52
  desc "Run unit tests on docker images"
53
- lane :Unit_Tests do
54
- run_dockerized_task(
55
- container_name: "unit_tests_container",
56
- port_factor: options[:port_factor],
57
- docker_image: "joesss/mango-base:latest",
58
- is_running_on_emulator: false,
59
- android_task: "./gradlew testDebug",
60
- pull_latest_image: true
61
- )
62
- end
53
+ lane :example do
54
+ run_dockerized_task(
55
+ container_name: "emulator_#{options[:port_factor]}",
56
+ port_factor: options[:port_factor],
57
+ docker_image: 'joesss/mango-base:latest',
58
+ android_task: './gradlew testDebug',
59
+ post_actions: 'adb logcat -d > logcat.txt',
60
+ bundle_install: true,
61
+ core_amount: '8',
62
+ workspace_dir: '/root/tests/espresso-tests',
63
+ docker_registry_login: "docker login -u='USER' -p='PASS' some.docker.com",
64
+ pull_latest_image: true,
65
+ pre_action: 'echo $GIT_BRANCH > /root/.branch',
66
+ vnc_enabled: false,
67
+ environment_variables: options[:environment_variables] ? options[:environment_variables].split(' ') : ''
68
+ )
69
+ end
63
70
  ```
64
71
 
65
72
  Now you can call this new lane by calling `bundle exec fastlane Espresso_Tests`.
@@ -78,6 +85,7 @@ The `mango` action has plenty of options to configure it.
78
85
  | `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
86
  | `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
87
  | `android_task` | A generic Android task you want to execute. | - | ❌ | `String` |
88
+ | `core_amount` | Cpu core amount while starting docker container with limited resource. | - | ✅ | `Integer` |
81
89
  | `sdk_path` | The path to your Android sdk directory. | `ANDROID_HOME` environment variable | ✅ | `String` |
82
90
  | `port_factor` | Base for calculating a unique port for noVNC. We recommend to use the `EXECUTOR_NUMBER` from your Jenkins environment. | - | ✅ | `String` |
83
91
  | `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("The mango plugin is working!")
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("Starting Android Task.")
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
- post_actions = params[:post_actions]
27
- if post_actions && !mango_helper.kvm_disabled?
28
- docker_commander&.exec(command: "cd #{workspace_dir} && #{post_actions}")
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
- "Action that runs Android tasks on a specified Docker image"
51
+ 'Action that runs Android tasks on a specified Docker image'
37
52
  end
38
53
 
39
54
  def self.authors
40
- ["Serghei Moret", "Daniel Hartwich"]
55
+ ['Serghei Moret', 'Daniel Hartwich']
41
56
  end
42
57
 
43
58
  def self.return_value
@@ -50,130 +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
- env_name: "CONTAINER_NAME",
60
- description: "Name of the docker container. Will be generated randomly if not defined",
61
- optional: true,
62
- type: String),
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: "NO_VNC_PORT",
66
- description: "Port to redirect noVNC. 6080 by default",
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: "DEVICE_NAME",
73
- description: "Name of the Android device. Nexus 5X by default",
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: "EMULATOR_NAME",
80
- description: "Name of the Android emulator. emulator-5554 by default",
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: "DOCKER_IMAGE",
87
- description: "Name of the Docker image. butomo1989/docker-android-x86-5.1.1 by default",
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
- env_name: "CONTAINER_TIMEOUT",
94
- description: "Timeout (in seconds) to get the healthy docker container. 450 (7.5 minutes) by default",
95
- default_value: 450,
96
- optional: false,
97
- type: Integer),
98
-
99
- FastlaneCore::ConfigItem.new(key: :android_task,
100
- env_name: "ANDROID TASK",
101
- description: "A generic Android task you want to execute",
102
- is_string: true,
103
- optional: false),
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: "SDK_PATH",
107
- description: "The path to your Android sdk directory (root). ANDROID_HOME by default",
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
- env_name: "PORT_FACTOR",
114
- 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",
115
- optional: true,
116
- type: String),
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: "WORKSPACE_DIR",
141
+ env_name: 'WORKSPACE_DIR',
120
142
  default_value: '/root/tests/',
121
- 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`",
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: "MAXIMAL_RUN_TIME",
148
+ env_name: 'MAXIMAL_RUN_TIME',
127
149
  default_value: 60,
128
- description: "Defines the maximal time of your test run. Defaults to 60 minutes",
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: "BUNDLE_INSTALL",
155
+ env_name: 'BUNDLE_INSTALL',
134
156
  default_value: false,
135
- description: "Defines if the Android task must execute bundle install before running a build",
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: "IS_RUNNING_ON_EMULATOR",
162
+ env_name: 'IS_RUNNING_ON_EMULATOR',
141
163
  default_value: true,
142
- description: "Define if we want to run the emulator in the container",
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
- env_name: "POST_ACTIONS",
148
- description: "Actions that will be executed after the main command has been executed",
149
- is_string: true,
150
- optional: true),
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
- env_name: "PRE_ACTION",
154
- description: "Actions that will be executed before the docker image gets pulled",
155
- is_string: true,
156
- optional: true),
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
- env_name: "DOCKER_REGISTRY_LOGIN",
160
- description: "Authenticating yourself to a custom Docker registry",
161
- type: String,
162
- optional: true),
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
- env_name: "PULL_LATEST_IMAGE",
166
- description: "Define if you want to pull the latest image",
167
- type: Boolean,
168
- default_value: false,
169
- optional: true),
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),
170
192
 
171
193
  FastlaneCore::ConfigItem.new(key: :environment_variables,
172
- env_name: "ENVIRONMENT_VARIABLES",
173
- description: "Comma seperated list of environment variables which are passed into the docker container",
174
- type: Array,
175
- default_value: [],
176
- optional: true)
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)
177
206
  ]
178
207
  end
179
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,67 @@ module Fastlane
12
10
  end
13
11
 
14
12
  def pull_image(docker_image_name:)
15
- handle_thin_pool_exception do
16
- Actions.sh("docker pull #{docker_image_name}")
17
- end
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
- handle_thin_pool_exception do
31
- Actions.sh("docker run -v $PWD:/root/tests --privileged -t -d #{emulator_args} #{docker_name} #{docker_image}").chomp
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
46
+ rescue StandardError
47
+ sleep 5
48
+ UI.important("Was not able to delete the container after the first attempt, trying again")
49
+ retry
41
50
  end
42
51
 
43
52
  def disconnect_network_bridge
53
+ UI.important('Disconnecting from the network bridge')
44
54
  Actions.sh("docker network disconnect -f bridge #{container_name}") if container_name
45
55
  rescue StandardError
46
56
  # Do nothing if the network bridge is already gone
47
57
  end
48
58
 
49
- def exec(command:)
59
+ def exec(command:, raise_when_fail: true)
50
60
  if container_name
51
- Actions.sh("docker exec -i #{container_name} bash -l -c \"#{command}\"")
61
+ begin
62
+ Actions.sh("docker exec #{container_name} bash -l -c \"#{command}\"")
63
+ rescue StandardError => e
64
+ raise(e) if raise_when_fail
65
+ end
52
66
  else
53
67
  raise('Cannot execute docker command because the container name is unknown')
54
68
  end
55
69
  end
56
-
70
+
57
71
  def prune
58
72
  Action.sh('docker system prune -f')
59
73
  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
74
  end
76
75
  end
77
- end
76
+ 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
@@ -55,7 +53,7 @@ module Fastlane
55
53
  # it recovers after some time, so wait and retry
56
54
  retry_counter = retry_counter.to_i + 1
57
55
  if retry_counter <= 5
58
- sleep 10*retry_counter
56
+ sleep 10 * retry_counter
59
57
  retry
60
58
  else
61
59
  raise e
@@ -63,4 +61,4 @@ module Fastlane
63
61
  end
64
62
  end
65
63
  end
66
- 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, :maximal_run_time, :sleep_interval, :is_running_on_emulator, :environment_variables
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
@@ -27,7 +27,7 @@ module Fastlane
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
-
30
+ @vnc_enabled = params[:vnc_enabled]
31
31
  @docker_commander = DockerCommander.new(container_name)
32
32
  @emulator_commander = EmulatorCommander.new(container_name)
33
33
  end
@@ -41,11 +41,11 @@ module Fastlane
41
41
  assign_unique_vnc_port if port_factor && is_running_on_emulator
42
42
 
43
43
  if container_available?
44
- @container.stop
45
- @container.delete(force: true)
44
+ UI.important('Container was already started. Stopping and removing..')
45
+ @docker_commander.delete_container
46
46
  end
47
47
 
48
- handle_ports_allocation if is_running_on_emulator
48
+ handle_ports_allocation if is_running_on_emulator && vnc_enabled
49
49
 
50
50
  pull_from_registry if @pull_latest_image
51
51
 
@@ -67,13 +67,19 @@ module Fastlane
67
67
 
68
68
  unless container_state
69
69
  UI.important("Will retry to create a healthy docker container after #{sleep_interval} seconds")
70
- @container.stop
71
- @container.delete(force: true)
70
+ @docker_commander.delete_container
72
71
  sleep @sleep_interval
73
72
  create_container
74
73
 
75
74
  unless wait_for_healthy_container
76
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
77
83
  # We use code "2" as we need something than just standard error code 1, so we can differentiate the next step in CI
78
84
  exit 2
79
85
  end
@@ -101,12 +107,6 @@ module Fastlane
101
107
  @docker_commander.exec(command: 'cat kvm-ok.txt').include?('KVM acceleration can NOT be used')
102
108
  end
103
109
 
104
- # Stops and remove container
105
- def clean_container
106
- @container.stop
107
- @container.delete(force: true)
108
- end
109
-
110
110
  private
111
111
 
112
112
  # Sets path to adb
@@ -117,7 +117,7 @@ module Fastlane
117
117
  # assigns vnc port
118
118
  def assign_unique_vnc_port
119
119
  @no_vnc_port = 6080 + port_factor
120
- @host_ip_address = `hostname -I | head -n1 | awk '{print $1;}'`.delete!("\n")
120
+ @host_ip_address = `hostname -i | head -n1 | awk '{print $1;}'`.delete!("\n")
121
121
  UI.success("Port: #{@no_vnc_port} was chosen for VNC")
122
122
  UI.success("Link to VNC: http://#{@host_ip_address}:#{@no_vnc_port}")
123
123
  end
@@ -125,46 +125,46 @@ module Fastlane
125
125
  # Creates new container using params
126
126
  def create_container
127
127
  UI.important("Creating container: #{container_name}")
128
- print_cpu_load
128
+ CpuLoadHandler.print_cpu_load
129
129
  begin
130
130
  container = create_container_call
131
131
  set_container_name(container)
132
132
  rescue StandardError
133
133
  UI.important("Something went wrong while creating: #{container_name}, will retry in #{@sleep_interval} seconds")
134
- print_cpu_load
135
- @docker_commander.stop_container
134
+ CpuLoadHandler.print_cpu_load
136
135
  @docker_commander.delete_container
137
-
138
136
  sleep @sleep_interval
139
137
  container = create_container_call
140
138
  set_container_name(container)
141
139
  end
142
- 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
143
146
  end
144
147
 
145
148
  # Gets container instance by container ID
146
149
  def get_container_instance(container)
147
150
  Docker::Container.all(all: true).each do |cont|
148
- if cont.id == container
149
- @container = cont
150
- break
151
- end
151
+ return cont if cont.id == container
152
152
  end
153
153
  end
154
154
 
155
155
  # Call to create a container. We don't use Docker API here, as it doesn't support --privileged.
156
156
  def create_container_call
157
157
  # When CPU is under load we cannot create a healthy container
158
- wait_cpu_to_idle
158
+ CpuLoadHandler.wait_cpu_to_idle
159
159
 
160
160
  additional_env = ''
161
161
  environment_variables.each do |variable|
162
- additional_env = additional_env + " -e #{variable}"
162
+ additional_env += " -e #{variable}"
163
163
  end
164
164
  emulator_args = is_running_on_emulator ? "-p #{no_vnc_port}:6080 -e DEVICE='#{device_name}'" : ''
165
165
  emulator_args = "#{emulator_args}#{additional_env}"
166
-
167
- @docker_commander.start_container(emulator_args: emulator_args, docker_image: docker_image)
166
+ @docker_commander.start_container(emulator_args: emulator_args, docker_image: docker_image,
167
+ core_amount: core_amount)
168
168
  end
169
169
 
170
170
  def execute_pre_action
@@ -173,6 +173,7 @@ module Fastlane
173
173
 
174
174
  # Pull the docker images before creating a container
175
175
  def pull_from_registry
176
+ UI.important('Pulling the :latest image from the registry')
176
177
  docker_image_name = docker_image.gsub(':latest', '')
177
178
  Actions.sh(@docker_registry_login) if @docker_registry_login
178
179
  @docker_commander.pull_image(docker_image_name: docker_image_name)
@@ -189,7 +190,6 @@ module Fastlane
189
190
  if port_open?('0.0.0.0', @no_vnc_port)
190
191
  UI.important('Something went wrong. VNC port is still busy')
191
192
  sleep @sleep_interval
192
- @docker_commander.stop_container
193
193
  @docker_commander.delete_container
194
194
  end
195
195
  end
@@ -203,13 +203,10 @@ module Fastlane
203
203
  nil
204
204
  end
205
205
 
206
- def print_cpu_load(load = cpu_load)
207
- UI.important("CPU load is: #{load}")
208
- end
209
-
210
206
  # Checks if container is already available
211
207
  def container_available?
212
208
  return false unless container_name
209
+
213
210
  all_containers = Docker::Container.all(all: true)
214
211
 
215
212
  all_containers.each do |container|
@@ -252,6 +249,9 @@ module Fastlane
252
249
  end
253
250
  UI.important("The Container failed to load after '#{timeout}' seconds timeout. Reason: '#{@container.json['State']['Status']}'")
254
251
  false
252
+ rescue StandardError => e
253
+ puts e
254
+ false
255
255
  end
256
256
 
257
257
  # Checks if port is already openZ
@@ -263,41 +263,12 @@ module Fastlane
263
263
  false
264
264
  end
265
265
 
266
- # Gets load average of Linux machine
267
- def cpu_load
268
- load = `cat /proc/loadavg`
269
- load.split(' ').first.to_f
270
- end
271
-
272
- # Gets amount of the CPU cores
273
- def cpu_core_amount
274
- `cat /proc/cpuinfo | awk '/^processor/{print $3}' | tail -1`
275
- end
276
-
277
- # For half an hour waiting until CPU load average is less than number of cores*2 (which considers that CPU is ready)
278
- # Raises when 30 minutes timeout exceeds
279
- def wait_cpu_to_idle
280
- if OS.linux?
281
- 30.times do
282
- load = cpu_load
283
- return true if load < cpu_core_amount.to_i * 1.5
284
- print_cpu_load(load)
285
- UI.important('Waiting for available resources..')
286
- sleep 60
287
- end
288
- else
289
- return true
290
- end
291
- raise "CPU was overloaded. Couldn't start emulator"
292
- end
293
-
294
266
  # if we do not have container name, we cane use container ID that we got from create call
295
267
  def set_container_name(container)
296
268
  unless container_name
297
269
  @container_name = @emulator_commander.container_name = @docker_commander.container_name = container
298
270
  end
299
271
  end
300
-
301
272
  end
302
273
  end
303
274
  end
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module Mango
3
- VERSION = '1.1.6'.freeze
3
+ VERSION = '1.3.25'.freeze
4
4
  end
5
5
  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.1.6
4
+ version: 1.3.25
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: 2019-01-16 00:00:00.000000000 Z
11
+ date: 2021-07-29 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.58.2
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.58.2
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
- rubyforge_project:
173
- rubygems_version: 2.7.4
174
- signing_key:
173
+ rubygems_version: 3.1.4
174
+ signing_key:
175
175
  specification_version: 4
176
- summary: This plugin Android tasks on docker images
176
+ summary: This plugin orchtestrates Android tasks on docker images
177
177
  test_files: []