fastlane-plugin-mango 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 75221491bc8b2b1f16e769b0208203efbd55ac56a5577a632e522b1caa5348ba
4
+ data.tar.gz: facf07278586a34970216bf5bef7984bdd0e2cebb9854ae2f75e84d62693f310
5
+ SHA512:
6
+ metadata.gz: be523d5eaaf2475812c9aac88e1a82a8135b06649228d0c6684d3201064d4fceeaf28551dfcef541a5b820c8bb27d11bee76bfdb99e78a05600240585cf2c818
7
+ data.tar.gz: f095125f165f15695d06fb6d11589c57a61be876fb54bf1f9201919289ac93532c1d1f0eafd74af29577f062ab6a210539ec9a2b60fd3b2e22662a57faa1a9b4
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2017 XING SE
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,127 @@
1
+
2
+ # Mango - Fastlane plugin
3
+
4
+ [![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-badge.svg)](https://rubygems.org/gems/fastlane-plugin-mango)
5
+
6
+ A fastlane plugin that runs Android tasks on a specified [Docker](https://www.docker.com/) image
7
+
8
+ <img src="assets/mango_logo.png" alt="Mango Logo" width="256px" height="256px"/>
9
+
10
+ Running Android tests, especially [Espresso](https://developer.android.com/training/testing/espresso/) on a continuous integration environment like [Jenkins](https://jenkins.io/) can be a hassle. You need to boot, manage and destroy an [Android Virtual Device (AVD)](https://developer.android.com/studio/run/managing-avds) during the test run. This is why we, the mobile releases team at [XING](https://www.xing/com), built this plugin. It spins up a specified [Docker](https://www.docker.com/) image and runs a given task on it.
11
+
12
+ Another requirement we had was to run on a clean environment, which is why Mango also helps us to run our unit test and more. For an example check out the [example `Fastfile`](sample-android/fastlane/Fastfile)
13
+
14
+ ## Prerequisites
15
+
16
+ In order to use this plugin you will need to to have [Docker](https://www.docker.com/) installed on the machine you are using.
17
+ Documentation on how to set it up properly on a Linux (ubuntu) machine can be found [here](docs/docker-linux.md).
18
+
19
+ If you need an Android Virtual Device (AVD) to run your tests (for example Espresso, Calabash or Appium), it's necessary to check that your CPU supports kvm virtualisation. We already experienced, that it doesn't fully work on macOS and are using Linux for that.
20
+
21
+ ## Getting Started
22
+
23
+ This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To get started with `fastlane-plugin-mango`, add it to your project by running:
24
+
25
+ ```bash
26
+ fastlane add_plugin mango
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ After installing this plugin you have access to one additional action (`mango`) in your `Fastfile`.
32
+
33
+ So a lane in your `Fastfile` could look similar to this:
34
+ ```ruby
35
+ desc "Run espresso tests on docker images"
36
+ lane :Espresso_Tests do |options|
37
+ mango(
38
+ container_name: "espresso_container",
39
+ docker_image: "thyrlian/android-sdk:latest",
40
+ container_timeout: 120,
41
+ android_task: "./gradlew connectedAndroidTest",
42
+ post_actions: "adb logcat -d > logcat.txt"
43
+ )
44
+ end
45
+ ```
46
+
47
+ Now you can call this new lane by calling `bundle exec fastlane Espresso_Tests`.
48
+
49
+ The Plugin will start up the given `docker_image`, execute the given `android_task` and afterwards execute the `post_actions`.
50
+
51
+ Of
52
+
53
+ ## Configuration options
54
+ The `mango` action has plenty of options to configure it.
55
+
56
+ | Option | Description | Default value | Optional | Type |
57
+ | - |:-|-:| :-:| -:|
58
+ | `container_name`| Name of the docker container. Will be generated randomly if not defined. | - | ✅ | `String` |
59
+ | `no_vnc_port` | Port to redirect noVNC. To observe your docker container from your browser while it's running. | 6080 | ❌ | `Integer` |
60
+ | `device_name` | Name of the Android device. | Nexus 5X | ❌ | `String` |
61
+ | `emulator_name`| Name of the Android emulator. | emulator-5554 | ❌ | `String` |
62
+ | `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` |
63
+ | `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` |
64
+ | `android_task` | A generic Android task you want to execute. | - | ❌ | `String` |
65
+ | `sdk_path` | The path to your Android sdk directory. | `ANDROID_HOME` environment variable | ✅ | `String` |
66
+ | `port_factor` | Base for calculating a unique port for noVNC. We recommend to use the `EXECUTOR_NUMBER` from your Jenkins environment. | - | ✅ | `String` |
67
+ | `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` |
68
+ | `maximal_run_time` | Defines the maximal time of your test run in minutes. This can be helpful if you want to kill hanging processes automatically after a certain time. | 60 | ✅ | `Integer` |
69
+ | `bundle_install` | Defines if the Android task must execute `bundle install` before running a build. This is useful if you want to execute non-gradle tasks. Like [Calabash](https://github.com/calabash/calabash-android), where you need to update/install your [Ruby](https://www.ruby-lang.org) dependencies. | `true` | ✅ | `Boolean` |
70
+ | `is_running_on_emulator` | Define if container boots up an emulator instance inside of it. This will trigger configuration of noVNC, assign necessary ports etc. | `true` | ✅ | `Boolean` |
71
+ | `post_actions` | Actions that will be executed after the main command has been executed. | - | ✅ | `String` |
72
+ | `pre_action` | Actions that will be executed before the docker image gets pulled | - | ✅ | `String` |
73
+ | `docker_registry_login` | Command to authenticate yourself in a custom Docker registry | - | ✅ | `String` |
74
+ | `pull_latest_image` | Defines if you want to pull the `:latest` tag of the given `docker_image` | `false` | ✅ | `Boolean` |
75
+
76
+ ## Contributing
77
+
78
+ 🎁 Bug reports and pull requests for new features are most welcome!
79
+
80
+ 👷🏼 We are looking forward to your pull request, we'd love to help!
81
+
82
+ You can help by doing any of the following:
83
+
84
+ - Reviewing pull requests
85
+ - Bringing ideas for new features
86
+ - Answering questions on issues
87
+ - Improving documentation
88
+
89
+ This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant code](http://contributor-covenant.org/) of conduct.
90
+
91
+ ## Issues and Feedback
92
+
93
+ For any other issues and feedback about this plugin, please submit it to this repository.
94
+
95
+ ## Troubleshooting
96
+
97
+ If you have trouble using plugins, check out the [Plugins Troubleshooting](https://docs.fastlane.tools/plugins/plugins-troubleshooting/) guide.
98
+
99
+ ## License
100
+
101
+ The fastlane plugin is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
102
+
103
+ ## Example
104
+
105
+ Check out the [example `Fastfile`](sample-android/fastlane/Fastfile) to see how to use this plugin.
106
+ Try it by cloning the repo, running `fastlane install_plugins` and `bundle exec fastlane test`.
107
+
108
+ ## Run tests for this plugin
109
+
110
+ To run both the tests and code style validation, run
111
+
112
+ ```
113
+ rake
114
+ ```
115
+
116
+ To automatically fix many of the styling issues, use
117
+ ```
118
+ rubocop -a
119
+ ```
120
+
121
+ ## Using _fastlane_ Plugins
122
+
123
+ For more information about how the `fastlane` plugin system works, check out the [Plugins documentation](https://docs.fastlane.tools/plugins/create-plugin/).
124
+
125
+ ## About _fastlane_
126
+
127
+ _fastlane_ is the easiest way to automate beta deployments and releases for your iOS and Android apps. To learn more, check out [fastlane.tools](https://fastlane.tools).
@@ -0,0 +1,16 @@
1
+ require 'fastlane/plugin/mango/version'
2
+
3
+ module Fastlane
4
+ module Mango
5
+ # Return all .rb files inside the "actions" and "helper" directory
6
+ def self.all_classes
7
+ Dir[File.expand_path('**/{actions,helper}/*.rb', File.dirname(__FILE__))]
8
+ end
9
+ end
10
+ end
11
+
12
+ # By default we want to import all available actions and helpers
13
+ # A plugin can contain any number of actions and plugins
14
+ Fastlane::Mango.all_classes.each do |current|
15
+ require current
16
+ end
@@ -0,0 +1,171 @@
1
+ module Fastlane
2
+ module Actions
3
+ class RunDockerizedTaskAction < Action
4
+ def self.run(params)
5
+ UI.important("The mango plugin is working!")
6
+ emulator_name = params[:emulator_name]
7
+ docker_emulator = Fastlane::Helper::MangoHelper.new(params)
8
+ docker_emulator.setup_container
9
+
10
+ failure_buffer_timeout = 5
11
+ timeout_command = "timeout #{params[:maximal_run_time] - failure_buffer_timeout}m"
12
+ workspace_dir = params[:workspace_dir]
13
+
14
+ android_task = params[:android_task]
15
+ if android_task
16
+ UI.success("Starting Android Task.")
17
+ bundle_install = params[:bundle_install] ? '&& bundle install ' : ''
18
+
19
+ docker_emulator.docker_exec("cd #{workspace_dir} #{bundle_install}&& #{timeout_command} #{android_task} || exit 1")
20
+ end
21
+
22
+ ensure
23
+ post_actions = params[:post_actions]
24
+ if post_actions
25
+ docker_emulator&.docker_exec("cd #{workspace_dir} && #{post_actions}")
26
+ end
27
+
28
+ UI.important("Cleaning up #{emulator_name} container")
29
+ docker_emulator.clean_container if docker_emulator.instance_variable_get('@container')
30
+ end
31
+
32
+ def self.description
33
+ "Action that runs Android tasks on a specified Docker image"
34
+ end
35
+
36
+ def self.authors
37
+ ["Serghei Moret", "Daniel Hartwich"]
38
+ end
39
+
40
+ def self.return_value
41
+ # If your method provides a return value, you can describe here what it does
42
+ end
43
+
44
+ def self.is_supported?(platform)
45
+ platform == :android
46
+ end
47
+
48
+ def self.details
49
+ # Optional:
50
+ ""
51
+ end
52
+
53
+ def self.available_options
54
+ [
55
+ FastlaneCore::ConfigItem.new(key: :container_name,
56
+ env_name: "CONTAINER_NAME",
57
+ description: "Name of the docker container. Will be generated randomly if not defined",
58
+ optional: true,
59
+ type: String),
60
+
61
+ FastlaneCore::ConfigItem.new(key: :no_vnc_port,
62
+ env_name: "NO_VNC_PORT",
63
+ description: "Port to redirect noVNC. 6080 by default",
64
+ default_value: 6080,
65
+ optional: false,
66
+ type: Integer),
67
+
68
+ FastlaneCore::ConfigItem.new(key: :device_name,
69
+ env_name: "DEVICE_NAME",
70
+ description: "Name of the Android device. Nexus 5X by default",
71
+ default_value: 'Nexus 5X',
72
+ optional: false,
73
+ type: String),
74
+
75
+ FastlaneCore::ConfigItem.new(key: :emulator_name,
76
+ env_name: "EMULATOR_NAME",
77
+ description: "Name of the Android emulator. emulator-5554 by default",
78
+ default_value: 'emulator-5554',
79
+ optional: false,
80
+ type: String),
81
+
82
+ FastlaneCore::ConfigItem.new(key: :docker_image,
83
+ env_name: "DOCKER_IMAGE",
84
+ description: "Name of the Docker image. butomo1989/docker-android-x86-5.1.1 by default",
85
+ default_value: 'butomo1989/docker-android-x86-5.1.1',
86
+ optional: false,
87
+ type: String),
88
+
89
+ FastlaneCore::ConfigItem.new(key: :container_timeout,
90
+ env_name: "CONTAINER_TIMEOUT",
91
+ description: "Timeout (in seconds) to get the healthy docker container. 450 (7.5 minutes) by default",
92
+ default_value: 450,
93
+ optional: false,
94
+ type: Integer),
95
+
96
+ FastlaneCore::ConfigItem.new(key: :android_task,
97
+ env_name: "ANDROID TASK",
98
+ description: "A generic Android task you want to execute",
99
+ is_string: true,
100
+ optional: false),
101
+
102
+ FastlaneCore::ConfigItem.new(key: :sdk_path,
103
+ env_name: "SDK_PATH",
104
+ description: "The path to your Android sdk directory (root). ANDROID_HOME by default",
105
+ default_value: ENV['ANDROID_HOME'],
106
+ is_string: true,
107
+ optional: true),
108
+
109
+ FastlaneCore::ConfigItem.new(key: :port_factor,
110
+ env_name: "PORT_FACTOR",
111
+ 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",
112
+ optional: true,
113
+ type: String),
114
+
115
+ FastlaneCore::ConfigItem.new(key: :workspace_dir,
116
+ env_name: "WORKSPACE_DIR",
117
+ default_value: '/root/tests/',
118
+ description: "Path to the workspace to execute commands",
119
+ optional: true,
120
+ type: String),
121
+
122
+ FastlaneCore::ConfigItem.new(key: :maximal_run_time,
123
+ env_name: "MAXIMAL_RUN_TIME",
124
+ default_value: 60,
125
+ description: "Defines the maximal time of your test run. Defaults to 60 minutes",
126
+ optional: true,
127
+ type: Integer),
128
+
129
+ FastlaneCore::ConfigItem.new(key: :bundle_install,
130
+ env_name: "BUNDLE_INSTALL",
131
+ default_value: false,
132
+ description: "Defines if the Android task must execute bundle install before running a build",
133
+ optional: true,
134
+ type: Boolean),
135
+
136
+ FastlaneCore::ConfigItem.new(key: :is_running_on_emulator,
137
+ env_name: "IS_RUNNING_ON_EMULATOR",
138
+ default_value: true,
139
+ description: "Define if we want to run the emulator in the container",
140
+ optional: true,
141
+ type: Boolean),
142
+
143
+ FastlaneCore::ConfigItem.new(key: :post_actions,
144
+ env_name: "POST_ACTIONS",
145
+ description: "Actions that will be executed after the main command has been executed",
146
+ is_string: true,
147
+ optional: true),
148
+
149
+ FastlaneCore::ConfigItem.new(key: :pre_action,
150
+ env_name: "PRE_ACTION",
151
+ description: "Actions that will be executed before the docker image gets pulled",
152
+ is_string: true,
153
+ optional: true),
154
+
155
+ FastlaneCore::ConfigItem.new(key: :docker_registry_login,
156
+ env_name: "DOCKER_REGISTRY_LOGIN",
157
+ description: "Authenticating yourself to a custom Docker registry",
158
+ type: String,
159
+ optional: true),
160
+
161
+ FastlaneCore::ConfigItem.new(key: :pull_latest_image,
162
+ env_name: "PULL_LATEST_IMAGE",
163
+ description: "Define if you want to pull the latest image",
164
+ type: Boolean,
165
+ default_value: false,
166
+ optional: true)
167
+ ]
168
+ end
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,310 @@
1
+ require 'docker'
2
+ require 'socket'
3
+ require 'timeout'
4
+ require 'os'
5
+
6
+ module Fastlane
7
+ module Helper
8
+ class MangoHelper
9
+ attr_reader :container_name, :no_vnc_port, :device_name, :docker_image, :timeout, :port_factor, :maximal_run_time, :sleep_interval, :is_running_on_emulator
10
+
11
+ def initialize(params)
12
+ @container_name = params[:container_name]
13
+ @no_vnc_port = params[:no_vnc_port]
14
+ @device_name = params[:device_name]
15
+ @docker_image = params[:docker_image]
16
+ @timeout = params[:container_timeout]
17
+ @sdk_path = params[:sdk_path]
18
+ @port_factor = params[:port_factor].to_i
19
+ @maximal_run_time = params[:maximal_run_time]
20
+ @sleep_interval = 5
21
+ @container = nil
22
+ @adb_path = adb_path
23
+ @is_running_on_emulator = params[:is_running_on_emulator]
24
+ @pre_action = params[:pre_action]
25
+ @docker_registry_login = params[:docker_registry_login]
26
+ @pull_latest_image = params[:pull_latest_image]
27
+ end
28
+
29
+ # Setting up the container:
30
+ # 1. Checks if ports are already allocated and kill the ones that do
31
+ # 2. Checks if Container we want to create already exist. If it does, restart it and check that the ports are correct
32
+ # 3. Creates container if there wasn't one already created or the created one has incorrect ports
33
+ # 4. Finally, waits until container is up and running (Healthy) using timeout specified in params
34
+ def setup_container
35
+ assign_unique_vnc_port if port_factor && is_running_on_emulator
36
+
37
+ if container_available?
38
+ @container.stop
39
+ @container.delete(force: true)
40
+ end
41
+
42
+ handle_ports_allocation if is_running_on_emulator
43
+
44
+ execute_pre_action if @pre_action
45
+
46
+ pull_from_registry if @pull_latest_image
47
+
48
+ # Make sure that network bridge for the current container is not already used
49
+ disconnect_network_bridge if container_name
50
+
51
+ create_container
52
+
53
+ begin
54
+ wait_for_healthy_container false
55
+ check_emulator_connection if is_running_on_emulator
56
+ rescue StandardError
57
+ UI.important("Will retry checking for a healthy docker container after #{sleep_interval} seconds")
58
+ @container.stop
59
+ @container.delete(force: true)
60
+ sleep @sleep_interval
61
+ create_container
62
+ wait_for_healthy_container
63
+ check_emulator_connection if is_running_on_emulator
64
+ end
65
+ end
66
+
67
+ # Stops and remove container
68
+ def clean_container
69
+ @container.stop
70
+ @container.delete(force: true)
71
+ end
72
+
73
+ # Executes commands inside docker container
74
+ def docker_exec(command)
75
+ Actions.sh("docker exec -i #{container_name} bash -l -c \"#{command}\"")
76
+ end
77
+
78
+ private
79
+
80
+ # Sets path to adb
81
+ def adb_path
82
+ "#{@sdk_path}/platform-tools/adb"
83
+ end
84
+
85
+ # assigns vnc port
86
+ def assign_unique_vnc_port
87
+ @no_vnc_port = 6080 + port_factor
88
+ @host_ip_address = `hostname -I | head -n1 | awk '{print $1;}'`.delete!("\n")
89
+ UI.success("Port: #{@no_vnc_port} was chosen for VNC")
90
+ UI.success("Link to VNC: http://#{@host_ip_address}:#{@no_vnc_port}")
91
+ end
92
+
93
+ # Restarts adb on the separate port and checks if created emulator is connected
94
+ def check_emulator_connection
95
+ UI.success('Checking if emulator is connected to ADB.')
96
+
97
+ if emulator_is_healthy?
98
+ UI.success('Emulator connected successfully')
99
+ else
100
+ raise "Something went wrong. Newly created device couldn't connect to the adb"
101
+ end
102
+
103
+ disable_animations
104
+ increase_logcat_storage
105
+ end
106
+
107
+ def emulator_is_healthy?
108
+ list_devices = docker_exec('adb devices')
109
+ list_devices.include? "\tdevice"
110
+ end
111
+
112
+ # Creates new container using params
113
+ def create_container
114
+ UI.important("Creating container: #{container_name}")
115
+ print_cpu_load
116
+ begin
117
+ container = create_container_call
118
+ @container_name = container unless container_name
119
+ rescue StandardError
120
+ UI.important("Something went wrong while creating: #{container_name}, will retry in #{@sleep_interval} seconds")
121
+ print_cpu_load
122
+ `docker stop #{container_name}` if container_name
123
+ `docker rm #{container_name}` if container_name
124
+ sleep @sleep_interval
125
+ container = create_container_call
126
+ @container_name = container unless container_name
127
+ end
128
+ get_container_instance(container)
129
+ end
130
+
131
+ # Gets container instance by container ID
132
+ def get_container_instance(container)
133
+ Docker::Container.all(all: true).each do |cont|
134
+ if cont.id == container
135
+ @container = cont
136
+ break
137
+ end
138
+ end
139
+ end
140
+
141
+ # Call to create a container. We don't use Docker API here, as it doesn't support --privileged.
142
+ def create_container_call
143
+ # When CPU is under load we cannot create a healthy container
144
+ wait_cpu_to_idle
145
+
146
+ docker_name = if container_name
147
+ "--name #{container_name}"
148
+ else
149
+ ''
150
+ end
151
+
152
+ emulator_args = is_running_on_emulator ? "-p #{no_vnc_port}:6080 -e DEVICE='#{device_name}'" : ''
153
+
154
+ # Action.sh returns all output that the command produced but we are only
155
+ # interested in the last line, since it contains the id of the created container.
156
+ UI.important("Attaching #{ENV['PWD']} to the docker container")
157
+ output = Actions.sh("docker run -v $PWD:/root/tests --privileged -t -d #{emulator_args} #{docker_name} #{docker_image}").chomp
158
+ output.split("\n").last
159
+ end
160
+
161
+ def execute_pre_action
162
+ Actions.sh(@pre_action)
163
+ end
164
+
165
+ # Pull the docker images before creating a container
166
+ def pull_from_registry
167
+ docker_image_name = docker_image.gsub(':latest', '')
168
+ Actions.sh(@docker_registry_login) if @docker_registry_login
169
+ Actions.sh("docker pull #{docker_image_name}")
170
+ end
171
+
172
+ # Checks that chosen ports are not already allocated. If they are, it will stop the allocated container
173
+ def handle_ports_allocation
174
+ vnc_allocated_container = container_of_allocated_port(no_vnc_port)
175
+ if vnc_allocated_container
176
+ UI.important("Port: #{no_vnc_port} was already allocated. Stopping Container.")
177
+ vnc_allocated_container.stop
178
+ end
179
+
180
+ if ports_open?('0.0.0.0', [@no_vnc_port])
181
+ UI.important('Something went wrong. One of the required ports is still busy')
182
+ sleep @sleep_interval
183
+ `docker stop #{container_name}` if container_name
184
+ `docker rm #{container_name}` if container_name
185
+ end
186
+ end
187
+
188
+ # Gets container instance of allocated port
189
+ def container_of_allocated_port(port)
190
+ Docker::Container.all.each do |container|
191
+ public_ports = container.info['Ports'].map { |public_port| public_port['PublicPort'] }
192
+ return container if public_ports.include? port
193
+ end
194
+ nil
195
+ end
196
+
197
+ def print_cpu_load(load = cpu_load)
198
+ UI.important("CPU load is: #{load}")
199
+ end
200
+
201
+ # Checks if container is already available
202
+ def container_available?
203
+ return false unless container_name
204
+ all_containers = Docker::Container.all(all: true)
205
+
206
+ all_containers.each do |container|
207
+ if container.info['Names'].first[1..-1] == container_name
208
+ @container = container
209
+ return true
210
+ end
211
+ end
212
+ false
213
+ end
214
+
215
+ # Checks if container status is 'healthy'
216
+ def container_is_healthy?
217
+ if @container.json['State']['Health']
218
+ @container.json['State']['Health']['Status'] == 'healthy'
219
+ else
220
+ @container.json['State']['Status'] == 'running'
221
+ end
222
+ end
223
+
224
+ # Waits until container is healthy using specified timeout
225
+ def wait_for_healthy_container(will_exit = true)
226
+ UI.important('Waiting for Container to be in the Healthy state.')
227
+
228
+ number_of_tries = timeout / sleep_interval
229
+ number_of_tries.times do
230
+ sleep sleep_interval / 2
231
+ if container_is_healthy?
232
+ UI.success('Your container is ready to work')
233
+ return true
234
+ end
235
+ UI.important("Container status: #{@container.json['State']['Status']}")
236
+ sleep sleep_interval
237
+ end
238
+ UI.important("The Container failed to load after '#{timeout}' seconds timeout. Reason: '#{@container.json['State']['Status']}'")
239
+ # We use code "2" as we need something than just standard error code 1, so we can differentiate the next step in CI
240
+ exit 2 if will_exit
241
+ raise 'Fail'
242
+ end
243
+
244
+ # Checks if port is already open
245
+ def ports_open?(ip, ports)
246
+ raise "'ports' should be an array" unless ports.is_a? Array
247
+ ports.each do |port|
248
+ begin
249
+ Timeout.timeout(1) do
250
+ begin
251
+ s = TCPSocket.new(ip, port)
252
+ s.close
253
+ return true
254
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
255
+ end
256
+ end
257
+ rescue Timeout::Error
258
+ end
259
+ end
260
+ false
261
+ end
262
+
263
+ # Gets load average of Linux machine
264
+ def cpu_load
265
+ load = `cat /proc/loadavg`
266
+ load.split(' ').first.to_f
267
+ end
268
+
269
+ # Gets amount of the CPU cores
270
+ def cpu_core_amount
271
+ `cat /proc/cpuinfo | awk '/^processor/{print $3}' | tail -1`
272
+ end
273
+
274
+ # For half an hour waiting until CPU load average is less than number of cores*2 (which considers that CPU is ready)
275
+ # Raises when 30 minutes timeout exceeds
276
+ def wait_cpu_to_idle
277
+ if OS.linux?
278
+ 30.times do
279
+ load = cpu_load
280
+ return true if load < cpu_core_amount.to_i * 1.5
281
+ print_cpu_load(load)
282
+ UI.important('Waiting for available resources..')
283
+ sleep 60
284
+ end
285
+ else
286
+ return true
287
+ end
288
+ raise "CPU was overloaded. Couldn't start emulator"
289
+ end
290
+
291
+ # Disables animation for faster and stable testing
292
+ def disable_animations
293
+ docker_exec('adb shell settings put global window_animation_scale 0.0')
294
+ docker_exec('adb shell settings put global transition_animation_scale 0.0')
295
+ docker_exec('adb shell settings put global animator_duration_scale 0.0')
296
+ end
297
+
298
+ # Increases logcat storage
299
+ def increase_logcat_storage
300
+ docker_exec('adb logcat -G 16m')
301
+ end
302
+
303
+ def disconnect_network_bridge
304
+ `docker network disconnect -f bridge #{container_name}`
305
+ rescue StandardError
306
+ # Do nothing if the network bridge is already gone
307
+ end
308
+ end
309
+ end
310
+ end
@@ -0,0 +1,5 @@
1
+ module Fastlane
2
+ module Mango
3
+ VERSION = '1.0.0'.freeze
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,175 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fastlane-plugin-mango
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Serghei Moret, Daniel Hartwich
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-08-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: docker-api
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: fastlane
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 2.66.2
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 2.66.2
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec_junit_formatter
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '='
116
+ - !ruby/object:Gem::Version
117
+ version: 0.58.2
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '='
123
+ - !ruby/object:Gem::Version
124
+ version: 0.58.2
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description:
140
+ email: serghei.moret@xing.com, hartwich.daniel@gmail.com
141
+ executables: []
142
+ extensions: []
143
+ extra_rdoc_files: []
144
+ files:
145
+ - LICENSE
146
+ - README.md
147
+ - lib/fastlane/plugin/mango.rb
148
+ - lib/fastlane/plugin/mango/actions/run_dockerized_task_action.rb
149
+ - lib/fastlane/plugin/mango/helper/mango_helper.rb
150
+ - lib/fastlane/plugin/mango/version.rb
151
+ homepage: https://github.com/xing/mango
152
+ licenses:
153
+ - MIT
154
+ metadata: {}
155
+ post_install_message:
156
+ rdoc_options: []
157
+ require_paths:
158
+ - lib
159
+ required_ruby_version: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ required_rubygems_version: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - ">="
167
+ - !ruby/object:Gem::Version
168
+ version: '0'
169
+ requirements: []
170
+ rubyforge_project:
171
+ rubygems_version: 2.7.7
172
+ signing_key:
173
+ specification_version: 4
174
+ summary: This plugin Android tasks on docker images
175
+ test_files: []