fastlane-plugin-mango 1.1.6 → 1.3.25

Sign up to get free protection for your applications and to get access to all the features.
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: []