Zebrunner MCloud is a Device Farm for automated test execution (Appium) and remote web access by humans to physical devices (Android and iOS) including phones, tablets, and TVs. It is fully integrated into the Zebrunner (Community Edition) ecosystem and can be used both for manual and automated testing.
This repository is the server side of the farm: the docker compose services of STF (a fork of DeviceFarmer/stf with iOS devices and the Zebrunner sign-in), the Selenium/Appium hub mcloud-grid and the nginx in front of them, managed by zebrunner.sh. The devices are connected by mcloud-agent.
Feel free to support the development with a donation for the next improvements.
| Requirement | Value |
|---|---|
| Operating System | Ubuntu 24.04, Linux CentOS 7+, Amazon Linux 2, macOS |
| CPU | 4+ Cores |
| Memory | 16 Gb RAM |
| Free space | SSD 32Gb+ of free space |
- Docker v27+ with the Docker compose plugin
- git 2.20.0+
-
Clone mcloud and set it up, provide valid protocol, hostname and port:
git clone https://github.com/zebrunner/mcloud.git && cd mcloud && ./zebrunner.sh setup
-
Start the services: the setup offers it, otherwise
./zebrunner.sh start. -
Open
http://hostname:80/stf. -
Log in using any name/email values as
auth-mockis configured by default. -
Go to Settings, Keys and generate a new Access Token.
-
Paste the generated token into
variables.envasSTF_TOKEN. -
Restart the services:
./zebrunner.sh restart. -
Set up the servers with Android and iOS devices according to mcloud-agent, the same on Linux and macOS (mcloud-ios is deprecated). Set
STF_STORAGE_TOKENof mcloud-agent to theSTF_STORAGE_TOKENofvariables.env: the STF storage accepts the uploads of the devices with this token only. -
Use
http://hostname:80/mcloud/grid/consoleto see the connected devices andhttp://hostname:80/mcloud/wd/hubas the Selenium/Appium hub URL for test automation.
Follow the installation and configuration guide in Zebrunner CE to reuse MCloud components effectively for Test Automation.
The commands of ./zebrunner.sh:
| Command | What it does |
|---|---|
setup |
writes .env, variables.env and configuration/stf-proxy/nginx.conf from their *.original files, then offers to start the services; the values of the previous setup are kept |
start |
starts the services, the first start runs setup |
stop |
stops the services, the containers are kept |
restart |
down and start |
down |
stops and removes the containers, the data is kept |
shutdown |
removes the containers, the data and the settings |
backup, restore |
the settings to *.bak and the database to backup/rethinkdb.tar.gz, and back |
version |
the version of MCloud, the tag of the STF image |
./zebrunner.sh setup writes the settings from the *.original files, ./zebrunner.sh restart applies their changes. The next setup writes .env again and keeps the values of the variables of variables.env.original in variables.env (but STF_URL and the ZEBRUNNER_* URLs, which come from the protocol, host and port); the other variables added to variables.env are added again after it.
.env, the settings of docker-compose.yml:
| Variable | Default | Description |
|---|---|---|
STF_URL |
http://localhost:8082 |
the public URL of MCloud: the protocol, host and port of the setup |
STF_PORT |
8082 |
the port of stf-proxy on the host |
TAG_NGINX |
1.25.3 |
the tag of nginx |
TAG_SELENIUM_HUB |
develop |
the tag of public.ecr.aws/zebrunner/mcloud-grid |
TAG_RETHINKDB |
2.4.2 |
the tag of rethinkdb |
TAG_STF |
develop |
the tag of public.ecr.aws/zebrunner/stf |
AUTH_SYSTEM |
auth-mock |
the sign-in unit of STF, auth-zebrunner in Zebrunner CE |
AUTH_URL |
auth/mock/ |
the path of the sign-in, auth/zebrunner/ in Zebrunner CE |
variables.env, the settings of the STF units and of the grid:
| Variable | Default | Description |
|---|---|---|
SECRET |
generated | the secret of the sessions and the access tokens of STF; a new deployment gets a random one, a change of it ends the sessions and invalidates the access tokens (STF_TOKEN too) |
STF_STORAGE_TOKEN |
generated | the token of the STF units for the storage routes, the same value as STF_STORAGE_TOKEN of mcloud-agent |
STF_TOKEN |
the access token of the STF user the grid reserves the devices with | |
STF_URL |
http://localhost:8082 |
the public URL of MCloud |
STF_ROOT_GROUP_NAME |
MCloud |
the root group of STF |
STF_ADMIN_NAME, STF_ADMIN_EMAIL |
admin, admin@zebrunner.com |
the administrator of STF, the owner of the root group |
ZEBRUNNER_LOGIN_URL, ZEBRUNNER_USERINFO_URL |
the endpoints of Zebrunner IAM for auth-zebrunner, set by the setup of Zebrunner CE |
|
RETHINKDB_PORT_28015_TCP |
tcp://rethinkdb:28015 |
the database of STF |
AUTHKEY |
not used by the current images |
More settings of STF (e.g. STF_SESSION_TTL_SECONDS, the lifetime of a session, or STF_STORAGE_TEMP_DOWNLOAD_ALLOW, the private networks the apps may be installed from by URL) are described in ZEBRUNNER.md of STF. The settings of the grid (e.g. NODE_ALLOWED_NETWORKS, MAX_NEW_COMMAND_TIMEOUT, MCLOUD_LOG_LEVEL) are described in mcloud-grid, they go to environment of mcloud-grid in docker-compose.yml.
./zebrunner.sh setup with the https protocol turns on the ssl of stf-proxy with a self-signed certificate; put your certificate and key into configuration/stf-proxy/ssl/ssl.crt and configuration/stf-proxy/ssl/ssl.key instead. stf-proxy passes the scheme of the client to STF (X-Forwarded-Proto, the one of a proxy in front of it when there is one), so the session cookie is Secure behind https. The ssl settings allow TLS 1.2 and 1.3 only.
stf-proxy adds Referrer-Policy: no-referrer, X-Content-Type-Options: nosniff, X-Frame-Options: SAMEORIGIN and Content-Security-Policy: frame-ancestors 'self' to every response and removes X-Powered-By: MCloud is not embedded into the pages of other sites. A site that frames MCloud (e.g. another host of Zebrunner) needs frame-ancestors 'self' https://<host> instead and no X-Frame-Options in configuration/stf-proxy/nginx.conf.
See configuration/stf-proxy/htpasswd.
All the services are in the external docker network infra, which ./zebrunner.sh start creates.
| Service | Image | What it does |
|---|---|---|
stf-proxy |
nginx |
the entry point on STF_PORT: STF, the grid under /mcloud, the static files of the grid console |
mcloud-grid |
public.ecr.aws/zebrunner/mcloud-grid |
the Selenium/Appium hub of the devices, also on the port 4446 of the host |
rethinkdb |
rethinkdb |
the database of STF, the volume stf-db-volume |
stf-migrate |
public.ecr.aws/zebrunner/stf |
creates and updates the tables of the database, the other STF units start after it |
stf-app, stf-auth, stf-api, stf-websocket |
public.ecr.aws/zebrunner/stf |
the UI, the sign-in, the REST API and the websocket of STF |
stf-processor001, stf-reaper, stf-api-groups-engine |
public.ecr.aws/zebrunner/stf |
the device events, the heartbeats and the groups of STF |
stf-triproxy-app, stf-triproxy-dev |
public.ecr.aws/zebrunner/stf |
the message brokers of the STF units and of the devices, the ports 7150-7170 and 7250-7270 of the host |
stf-storage-temp, stf-storage-plugin-apk, stf-storage-plugin-image |
public.ecr.aws/zebrunner/stf |
the storage of the apps and screenshots, the volume stf-temp-volume; protected by STF_STORAGE_TOKEN |
| Endpoint | Content |
|---|---|
/stf |
the UI of STF |
/api/v1/ |
the REST API of STF, with an access token |
/mcloud/wd/hub |
the Selenium/Appium hub URL |
/mcloud/grid/console |
the grid console with the devices |
/mcloud/grid/admin/DevicesServlet |
the devices of the grid as JSON: their status and sessions |
/mcloud/grid/admin/AllSessionsServlet |
the active sessions as JSON |
/mcloud/grid/admin/MetricsServlet |
the Prometheus metrics of the grid |
POST /mcloud/grid/admin/TerminateSessionServlet |
terminates the session of a device, see mcloud-grid |
From the images stf:3.0 and mcloud-grid:2.6:
- Update the repository:
git pull. - Run
./zebrunner.sh setup: it keeps the settings ofvariables.envand generatesSTF_STORAGE_TOKEN. - Set the same
STF_STORAGE_TOKENin mcloud-agent and update it with its mcloud-device image to the versions of the new STF, then restart the devices. - Restart the services:
./zebrunner.sh restart.stf-migratecreates the table of the sessions, the users sign in once more.
The setup warns when SECRET is still the default password: replace it in variables.env with a random value, then generate the access tokens again (STF_TOKEN too), as the old ones are signed with the old secret. The grid logs JSON lines now, see mcloud-grid.
- mcloud-agent - Device Farm agent for connecting physical Android and iOS devices including phones, tablets, and TVs via Linux or macOS.
- mcloud-device - Dockerized STF provider image for Android and iOS devices.
- mcloud-grid - Enhanced Selenium Grid for automating physical devices and emulators/simulators via Appium.
- stf - the STF of MCloud with iOS devices and the Zebrunner sign-in.
- appium - Enhanced Appium image with low-level video recording and local storage for ipa/apk artifacts.
- mcloud-redroid - GPU accelerated Android In Cloud solution.
| Target | What it checks | Needs |
|---|---|---|
make check |
everything below, as CI runs it | |
make lint |
tests/lint.sh: yamllint, actionlint, pymarkdown, shellcheck, shfmt, the text files, docker compose config with the *.original settings |
Python 3; on macOS brew install hadolint |
make test |
tests/zebrunner_test.sh and tests/utility_test.sh: the commands of zebrunner.sh on a copy of the repository with a docker stub |
bash |
make docker |
tests/compose_test.sh: the setup, the start and the endpoints of the services, then their shutdown; TAG_STF=<tag> TAG_SELENIUM_HUB=<tag> make docker checks other images |
Docker, no other MCloud on the machine |
The tests write JUnit XML into $JUNIT_DIR when it is set. CI (.github/workflows/ci.yml) runs the jobs lint, tests (Linux and macOS) and docker on the pushes and pull requests.
The conventions of the style, the formatting and the tooling of this repository are in CONVENTIONS.md, the same in all the MCloud repositories. The rules of this repository:
docker-compose.ymltakes the image tags (TAG_*) and the ports from.env, the settings of the services fromvariables.env;./zebrunner.sh setupwrites both from.env.originalandvariables.env.original, which keep the defaults.make lintchecksdocker compose configwith the*.originalfiles.- Every image is pinned by its
TAG_*in.env.original(checked bymake lint). zebrunner.shandpatch/*.share host scripts: the interface of Zebrunner CE (ZBR_INSTALLER, theZBR_*settings,SHUTDOWN_CONFIRMED,.disabled) and the commands stay compatible.- The static files of the Selenium grid console (
configuration/stf-proxy/grid/) are kept byte for byte:generatedoftests/lint_project.shand unset formatting in.editorconfig. - A test of the services goes to
tests/compose_test.sh(DOCKER_TESTS), a test of the commands ofzebrunner.shwithout docker totests/zebrunner_test.sh.
