mirror of
https://github.com/budtmo/docker-android.git
synced 2026-07-31 04:07:25 +00:00
Compare commits
78
Commits
v2.18.0-p0
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5e31745bf | ||
|
|
64dc5076b7 | ||
|
|
a96247bd06 | ||
|
|
c9fba363db | ||
|
|
28478f3bce | ||
|
|
a043822b04 | ||
|
|
d173cf2ffa | ||
|
|
71da27e6ad | ||
|
|
2012d5e77d | ||
|
|
34656ff759 | ||
|
|
405b0b7308 | ||
|
|
c66f55462c | ||
|
|
7be06eefef | ||
|
|
2ddfba647d | ||
|
|
49540d74fb | ||
|
|
ef717fc8a5 | ||
|
|
a31c2f0301 | ||
|
|
ca2b83faa0 | ||
|
|
ed48e174f6 | ||
|
|
9b6a033133 | ||
|
|
1ea2c74b18 | ||
|
|
c2e376c6f7 | ||
|
|
b21d26e151 | ||
|
|
971300eef3 | ||
|
|
c8efeea3b3 | ||
|
|
4fe7dfdd39 | ||
|
|
c86a077767 | ||
|
|
d4d79d2b7a | ||
|
|
1326fc493f | ||
|
|
a28137f92f | ||
|
|
a46dcf1825 | ||
|
|
4459fffdb2 | ||
|
|
c47e5efdad | ||
|
|
149e55b52d | ||
|
|
f5e57eecef | ||
|
|
621d37d7d1 | ||
|
|
66fab80a5b | ||
|
|
97bd5ae254 | ||
|
|
b69020fc03 | ||
|
|
04da87e3e4 | ||
|
|
80134ce080 | ||
|
|
fbfe4a08d7 | ||
|
|
d03f14dadf | ||
|
|
4b64e87ab2 | ||
|
|
8f4b541d49 | ||
|
|
71f3fe64d6 | ||
|
|
5c6b28b530 | ||
|
|
f847fbb4db | ||
|
|
564a1491ff | ||
|
|
a89d4679be | ||
|
|
9290e90e3c | ||
|
|
136b49fab8 | ||
|
|
c5651e78f9 | ||
|
|
76ae526591 | ||
|
|
46805d6a81 | ||
|
|
6302de6bc4 | ||
|
|
1e1c60fc89 | ||
|
|
2a12e66077 | ||
|
|
f2a418d712 | ||
|
|
44dcc5b166 | ||
|
|
2e88c06124 | ||
|
|
6b34c74a25 | ||
|
|
1fc2c74d8c | ||
|
|
ffbafe87e8 | ||
|
|
62fa6a05c8 | ||
|
|
77542f5b45 | ||
|
|
39df955c26 | ||
|
|
8da596a8f1 | ||
|
|
7f55b248fd | ||
|
|
c434ae7770 | ||
|
|
ff9f9268e3 | ||
|
|
27e95cd197 | ||
|
|
1f1224599e | ||
|
|
846cd2ed09 | ||
|
|
b5fbe9777e | ||
|
|
21ffb20ca6 | ||
|
|
c0368d3d71 | ||
|
|
6e5ed8386a |
@@ -1,5 +1,7 @@
|
|||||||
# Git
|
# Git
|
||||||
.git/
|
.git/
|
||||||
|
.github
|
||||||
|
.gitignore
|
||||||
|
|
||||||
# IDE
|
# IDE
|
||||||
**/*.idea
|
**/*.idea
|
||||||
@@ -17,3 +19,10 @@ backup
|
|||||||
**/xunit.xml
|
**/xunit.xml
|
||||||
**/coverage
|
**/coverage
|
||||||
tmp/
|
tmp/
|
||||||
|
|
||||||
|
docker
|
||||||
|
documentations
|
||||||
|
example
|
||||||
|
images
|
||||||
|
*.md
|
||||||
|
*.sh
|
||||||
|
|||||||
+24
-15
@@ -9,20 +9,10 @@ updates:
|
|||||||
commit-message:
|
commit-message:
|
||||||
prefix: "[docker] "
|
prefix: "[docker] "
|
||||||
target-branch: "master"
|
target-branch: "master"
|
||||||
open-pull-requests-limit: 99
|
open-pull-requests-limit: 10
|
||||||
reviewers:
|
labels:
|
||||||
- "budtmo"
|
- "dependabot"
|
||||||
|
- "docker"
|
||||||
- package-ecosystem: pip
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: daily
|
|
||||||
time: '01:00'
|
|
||||||
timezone: "Europe/Berlin"
|
|
||||||
commit-message:
|
|
||||||
prefix: "[pip] "
|
|
||||||
target-branch: "master"
|
|
||||||
open-pull-requests-limit: 99
|
|
||||||
reviewers:
|
reviewers:
|
||||||
- "budtmo"
|
- "budtmo"
|
||||||
|
|
||||||
@@ -35,6 +25,25 @@ updates:
|
|||||||
commit-message:
|
commit-message:
|
||||||
prefix: "[github-action] "
|
prefix: "[github-action] "
|
||||||
target-branch: "master"
|
target-branch: "master"
|
||||||
open-pull-requests-limit: 99
|
open-pull-requests-limit: 10
|
||||||
|
labels:
|
||||||
|
- "dependabot"
|
||||||
|
- "github-action"
|
||||||
|
reviewers:
|
||||||
|
- "budtmo"
|
||||||
|
|
||||||
|
- package-ecosystem: pip
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
time: '01:00'
|
||||||
|
timezone: "Europe/Berlin"
|
||||||
|
commit-message:
|
||||||
|
prefix: "[pip] "
|
||||||
|
target-branch: "master"
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
labels:
|
||||||
|
- "dependabot"
|
||||||
|
- "pip"
|
||||||
reviewers:
|
reviewers:
|
||||||
- "budtmo"
|
- "budtmo"
|
||||||
|
|||||||
@@ -13,29 +13,17 @@ jobs:
|
|||||||
python-version: ["3.8"]
|
python-version: ["3.8"]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repo
|
- name: Checkout the repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Run unit-test
|
||||||
uses: actions/setup-python@v5
|
run: script -e -c "./app.sh test emulator test 11.0 && sudo mv tmp/* ."
|
||||||
with:
|
|
||||||
python-version: ${{ matrix.python-version }}
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
cd cli
|
|
||||||
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
|
||||||
|
|
||||||
- name: Run test
|
|
||||||
run: |
|
|
||||||
cd cli && nosetests -v
|
|
||||||
|
|
||||||
release_base:
|
release_base:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
needs: run_test
|
needs: run_test
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repo
|
- name: Checkout the repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: Get release version
|
- name: Get release version
|
||||||
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||||
@@ -54,11 +42,20 @@ jobs:
|
|||||||
android: ["9.0", "10.0", "11.0", "12.0", "13.0", "14.0"]
|
android: ["9.0", "10.0", "11.0", "12.0", "13.0", "14.0"]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repo
|
- name: Checkout the repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: Get release version
|
- name: Get release version
|
||||||
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set up extension
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.extension }}" > extension.sh
|
||||||
|
chmod 700 extension.sh
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Build and push emulator image ${{ matrix.android }} (${RELEASE_VERSION})
|
- name: Build and push emulator image ${{ matrix.android }} (${RELEASE_VERSION})
|
||||||
run: |
|
run: |
|
||||||
docker login -u=${{secrets.DOCKER_USERNAME}} -p=${{secrets.DOCKER_PASSWORD}}
|
docker login -u=${{secrets.DOCKER_USERNAME}} -p=${{secrets.DOCKER_PASSWORD}}
|
||||||
@@ -70,11 +67,20 @@ jobs:
|
|||||||
needs: release_base
|
needs: release_base
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repo
|
- name: Checkout the repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: Get release version
|
- name: Get release version
|
||||||
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set up extension
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.extension }}" > extension.sh
|
||||||
|
chmod 700 extension.sh
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Build and push genymotion image (${RELEASE_VERSION})
|
- name: Build and push genymotion image (${RELEASE_VERSION})
|
||||||
run: |
|
run: |
|
||||||
docker login -u=${{secrets.DOCKER_USERNAME}} -p=${{secrets.DOCKER_PASSWORD}}
|
docker login -u=${{secrets.DOCKER_USERNAME}} -p=${{secrets.DOCKER_PASSWORD}}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
name: Run Test on Demand
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_and_test:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout the repo
|
||||||
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
|
- name: Set up extension
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.extension }}" > extension.sh
|
||||||
|
chmod 700 extension.sh
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Build base image
|
||||||
|
run: script -e -c "./app.sh build base test"
|
||||||
|
|
||||||
|
- name: Build sample image
|
||||||
|
run: script -e -c "./app.sh build emulator test 11.0"
|
||||||
|
|
||||||
|
- name: Run unit-test
|
||||||
|
run: script -e -c "./app.sh test emulator test 11.0 && sudo mv tmp/* ."
|
||||||
|
|
||||||
|
- name: Publish test result
|
||||||
|
run: bash <(curl -s https://codecov.io/bash)
|
||||||
@@ -4,21 +4,31 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_and_test:
|
build_and_test:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.actor == 'budtmo'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repo
|
- name: Checkout the repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
|
- name: Set up extension
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.extension }}" > extension.sh
|
||||||
|
chmod 700 extension.sh
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Build base image
|
- name: Build base image
|
||||||
run: script -e -c "./app.sh build base test"
|
run: script -e -c "./app.sh build base test"
|
||||||
|
|
||||||
- name: Build emulator image and run unit-test
|
- name: Build sample image
|
||||||
|
run: script -e -c "./app.sh build emulator test 11.0"
|
||||||
|
|
||||||
|
- name: Run unit-test
|
||||||
run: script -e -c "./app.sh test emulator test 11.0 && sudo mv tmp/* ."
|
run: script -e -c "./app.sh test emulator test 11.0 && sudo mv tmp/* ."
|
||||||
|
|
||||||
- name: Publish test result
|
- name: Publish test result
|
||||||
|
|||||||
+1
-1
@@ -17,4 +17,4 @@ tmp/
|
|||||||
|
|
||||||
# Dev-files
|
# Dev-files
|
||||||
n*.txt
|
n*.txt
|
||||||
test-*.sh
|
e*.sh
|
||||||
|
|||||||
@@ -96,15 +96,19 @@ Credit goes to [Guillaume - The Parallel Interface blog](https://www.paralint.co
|
|||||||
sudo usermod -a -G kvm ${USER}
|
sudo usermod -a -G kvm ${USER}
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Add necessary flags to `/etc/wsl2.conf` to their respective sections.
|
2. Add necessary flags to `/etc/wsl.conf` to their respective sections.
|
||||||
```
|
```
|
||||||
[boot]
|
[boot]
|
||||||
command = /bin/bash -c 'chown -v root:kvm /dev/kvm && chmod 660 /dev/kvm'
|
command = /bin/bash -c 'chown -v root:kvm /dev/kvm && chmod 660 /dev/kvm'
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, using PowerShell, open a notepad on `notepad $env:USERPROFILE\.wslconfig`. Inside, put these other flags:
|
||||||
|
```
|
||||||
[wsl2]
|
[wsl2]
|
||||||
nestedVirtualization=true
|
nestedVirtualization=true
|
||||||
```
|
```
|
||||||
3. Restart WSL2 via CMD prompt or Powershell
|
|
||||||
|
4. Restart WSL2 via CMD prompt or Powershell
|
||||||
```
|
```
|
||||||
wsl --shutdown
|
wsl --shutdown
|
||||||
```
|
```
|
||||||
@@ -114,6 +118,8 @@ Credit goes to [Guillaume - The Parallel Interface blog](https://www.paralint.co
|
|||||||
|
|
||||||
`nestedVirtualization` flag is only available to Windows 11.
|
`nestedVirtualization` flag is only available to Windows 11.
|
||||||
|
|
||||||
|
If this setup does not work, you may have an old WSL version. In that case, ignore `\.wslconfig` and put everything on `/etc/wsl.conf`, including the `[wsl2]` flag.
|
||||||
|
|
||||||
Use-Cases
|
Use-Cases
|
||||||
---------
|
---------
|
||||||
|
|
||||||
@@ -163,7 +169,7 @@ The differences between normal version and pro version are:
|
|||||||
|user-behavior-analytics|Yes|No|-|
|
|user-behavior-analytics|Yes|No|-|
|
||||||
|proxy|No|Yes|Set up company proxy on Android emulator on fly|
|
|proxy|No|Yes|Set up company proxy on Android emulator on fly|
|
||||||
|language|No|Yes|Set up language on Android emulator on fly|
|
|language|No|Yes|Set up language on Android emulator on fly|
|
||||||
|Newer Android version|No|Yes|Support other newer Android version e.g. Android 15, etc|
|
|Newer Android version|No|Yes|Support other newer Android version e.g. Android 15, Android 16, etc|
|
||||||
|root-privileged|No|Yes|Able to run command with security privileged|
|
|root-privileged|No|Yes|Able to run command with security privileged|
|
||||||
|headless-mode|No|Yes|Save resources by using headless mode|
|
|headless-mode|No|Yes|Save resources by using headless mode|
|
||||||
|Selenium 4.x integration|No|Yes|Running Appium UI-Tests againt one (Selenium Hub) endpoint for Android- and iOS emulator(s) / device(s)|
|
|Selenium 4.x integration|No|Yes|Running Appium UI-Tests againt one (Selenium Hub) endpoint for Android- and iOS emulator(s) / device(s)|
|
||||||
|
|||||||
@@ -81,9 +81,15 @@ echo "${IMAGE_NAME_SPECIFIC_RELEASE} or ${IMAGE_NAME_LATEST} "
|
|||||||
|
|
||||||
function build() {
|
function build() {
|
||||||
# autopep8 --recursive --exclude=.git,__pycache__,venv --max-line-length=120 --in-place .
|
# autopep8 --recursive --exclude=.git,__pycache__,venv --max-line-length=120 --in-place .
|
||||||
cmd="docker build -t ${IMAGE_NAME_SPECIFIC_RELEASE} --build-arg DOCKER_ANDROID_VERSION=${r_v} "
|
cmd="docker build --no-cache -t ${IMAGE_NAME_SPECIFIC_RELEASE} --build-arg DOCKER_ANDROID_VERSION=${r_v} "
|
||||||
if [ -n "${a_v}" ]; then
|
if [ -n "${a_v}" ]; then
|
||||||
cmd+="--build-arg EMULATOR_ANDROID_VERSION=${a_v} --build-arg EMULATOR_API_LEVEL=${a_l} "
|
DOCKER_BUILDKIT=1
|
||||||
|
cmd="${cmd} --secret id=extension,src=extension.sh --build-arg EMULATOR_ANDROID_VERSION=${a_v} --build-arg EMULATOR_API_LEVEL=${a_l} "
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${p}" == *"genymotion"* ]]; then
|
||||||
|
DOCKER_BUILDKIT=1
|
||||||
|
cmd="${cmd} --secret id=extension,src=extension.sh "
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cmd+="-f ${FOLDER_PATH} ."
|
cmd+="-f ${FOLDER_PATH} ."
|
||||||
@@ -97,18 +103,14 @@ function build() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function test() {
|
function test() {
|
||||||
cli_path="/home/androidusr/docker-android/cli"
|
tmp_folder="/app/tmp"
|
||||||
results_path="test-results"
|
|
||||||
tmp_folder="tmp"
|
|
||||||
|
|
||||||
mkdir -p tmp
|
mkdir -p tmp
|
||||||
build
|
docker run -it --rm -v "$PWD":/app -w /app python:3.12-slim bash \
|
||||||
docker run -it --rm --name test --entrypoint /bin/bash \
|
-c "cd cli && rm -rf ${tmp_folder}/* && \
|
||||||
-v $PWD/${tmp_folder}:${cli_path}/${tmp_folder} ${IMAGE_NAME_SPECIFIC_RELEASE} \
|
pip install --upgrade pip && pip install -r requirements.txt && \
|
||||||
-c "cd ${cli_path} && sudo rm -rf ${tmp_folder}/* && \
|
PYTHONPATH=src pytest -v && mv test-results/* ${tmp_folder}/ && chown -R 1300:1301 ${tmp_folder} && \
|
||||||
nosetests -v && sudo mv .coverage ${tmp_folder} && \
|
chmod a+x -R ${tmp_folder}"
|
||||||
sudo cp -r ${results_path}/* ${tmp_folder} && sudo chown -R 1300:1301 ${tmp_folder} &&
|
|
||||||
sudo chmod a+x -R ${tmp_folder}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function push() {
|
function push() {
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
autopep8==2.3.1
|
autopep8==2.3.2
|
||||||
click==8.1.8
|
click==8.4.2
|
||||||
coverage==7.6.1
|
coverage==7.15.2
|
||||||
mock==5.2.0
|
mock==5.2.0
|
||||||
nose==1.3.7
|
pytest==9.1.1
|
||||||
requests==2.32.3
|
pytest-cov==7.1.0
|
||||||
|
pytest-xdist==3.8.0
|
||||||
|
requests==2.34.2
|
||||||
|
setuptools==83.0.0
|
||||||
|
|||||||
+7
-10
@@ -1,10 +1,7 @@
|
|||||||
[nosetests]
|
[tool:pytest]
|
||||||
cover-xml=true
|
addopts =
|
||||||
cover-xml-file=test-results/coverage.xml
|
--cov=src
|
||||||
with-coverage=true
|
--cov-report=html:test-results/html
|
||||||
cover-package=src
|
--cov-report=xml:test-results/coverage.xml
|
||||||
cover-erase=true
|
--junit-xml=test-results/junit-report.xml
|
||||||
with-xunit=true
|
testpaths = src/tests
|
||||||
xunit-file=test-results/xunit.xml
|
|
||||||
cover-html=true
|
|
||||||
cover-html-dir=test-results/coverage
|
|
||||||
|
|||||||
+6
-4
@@ -1,6 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from setuptools import setup
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
|
||||||
app_version = os.getenv("DOCKER_ANDROID_VERSION", "test-version")
|
app_version = os.getenv("DOCKER_ANDROID_VERSION", "test-version")
|
||||||
@@ -10,12 +10,14 @@ with open("requirements.txt", "r") as f:
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="docker-android",
|
name="docker-android",
|
||||||
version=app_version,
|
version="0.1",
|
||||||
url="https://github.com/budtmo/docker-android",
|
url="https://github.com/budtmo/docker-android",
|
||||||
description="CLI for docker-android",
|
description="CLI for docker-android",
|
||||||
author="Budi Utomo",
|
author="Budi Utomo",
|
||||||
author_email="budtmo.os@gmail.com",
|
author_email="budtmo.os@gmail.com",
|
||||||
install_requires=reqs,
|
install_requires=reqs,
|
||||||
|
packages=find_packages(where="src"),
|
||||||
|
package_dir={"": "src"},
|
||||||
py_modules=["cli", "docker-android"],
|
py_modules=["cli", "docker-android"],
|
||||||
entry_points={"console_scripts": "docker-android=src.app:cli"}
|
entry_points={"console_scripts": "docker-android=app:cli"}
|
||||||
)
|
)
|
||||||
+9
-9
@@ -8,14 +8,14 @@ import os
|
|||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from src.application import Application
|
from application import Application
|
||||||
from src.device import DeviceType
|
from device import DeviceType
|
||||||
from src.device.emulator import Emulator
|
from device.emulator import Emulator
|
||||||
from src.device.geny_aws import GenyAWS
|
from device.geny_aws import GenyAWS
|
||||||
from src.device.geny_saas import GenySAAS
|
from device.geny_saas import GenySAAS
|
||||||
from src.helper import convert_str_to_bool, get_env_value_or_raise
|
from helper import convert_str_to_bool, get_env_value_or_raise
|
||||||
from src.constants import ENV
|
from constants import ENV
|
||||||
from src.logger import log
|
from logger import log
|
||||||
|
|
||||||
log.init()
|
log.init()
|
||||||
logger = logging.getLogger("App")
|
logger = logging.getLogger("App")
|
||||||
@@ -60,7 +60,7 @@ def cli():
|
|||||||
|
|
||||||
def start_appium() -> None:
|
def start_appium() -> None:
|
||||||
if convert_str_to_bool(os.getenv(ENV.APPIUM)):
|
if convert_str_to_bool(os.getenv(ENV.APPIUM)):
|
||||||
cmd = f"/usr/bin/appium"
|
cmd = f"/usr/local/bin/appium"
|
||||||
app_appium = Application("Appium", cmd,
|
app_appium = Application("Appium", cmd,
|
||||||
os.getenv(ENV.APPIUM_ADDITIONAL_ARGS, ""), False)
|
os.getenv(ENV.APPIUM_ADDITIONAL_ARGS, ""), False)
|
||||||
app_appium.start()
|
app_appium.start()
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ GENYMOTION_TEMPLATE_PATH = "GENYMOTION_TEMPLATE_PATH"
|
|||||||
# Device (Geny_SAAS)
|
# Device (Geny_SAAS)
|
||||||
GENY_SAAS_USER = "GENY_SAAS_USER"
|
GENY_SAAS_USER = "GENY_SAAS_USER"
|
||||||
GENY_SAAS_PASS = "GENY_SAAS_PASS"
|
GENY_SAAS_PASS = "GENY_SAAS_PASS"
|
||||||
|
GENY_AUTH_TOKEN = "GENY_AUTH_TOKEN"
|
||||||
GENY_SAAS_TEMPLATE_FILE_NAME = "saas.json"
|
GENY_SAAS_TEMPLATE_FILE_NAME = "saas.json"
|
||||||
|
|
||||||
# Device (Geny_AWS)
|
# Device (Geny_AWS)
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import time
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from src.helper import convert_str_to_bool, get_env_value_or_raise
|
from helper import convert_str_to_bool, get_env_value_or_raise
|
||||||
from src.constants import DEVICE, ENV
|
from constants import DEVICE, ENV
|
||||||
|
|
||||||
|
|
||||||
class DeviceType(Enum):
|
class DeviceType(Enum):
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import time
|
|||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from src.device import Device, DeviceType
|
from device import Device, DeviceType
|
||||||
from src.helper import convert_str_to_bool, get_env_value_or_raise, symlink_force
|
from helper import convert_str_to_bool, get_env_value_or_raise, symlink_force
|
||||||
from src.constants import ENV, UTF8
|
from constants import ENV, UTF8
|
||||||
|
|
||||||
|
|
||||||
class Emulator(Device):
|
class Emulator(Device):
|
||||||
@@ -23,7 +23,9 @@ class Emulator(Device):
|
|||||||
"Samsung Galaxy S8",
|
"Samsung Galaxy S8",
|
||||||
"Samsung Galaxy S9",
|
"Samsung Galaxy S9",
|
||||||
"Samsung Galaxy S10",
|
"Samsung Galaxy S10",
|
||||||
"Pixel C"
|
"Pixel C",
|
||||||
|
"Pixel 8",
|
||||||
|
"Pixel 9"
|
||||||
)
|
)
|
||||||
|
|
||||||
API_LEVEL = {
|
API_LEVEL = {
|
||||||
@@ -151,7 +153,8 @@ class Emulator(Device):
|
|||||||
creation_cmd = "avdmanager create avd -f -n {n} -b {it}/{si} " \
|
creation_cmd = "avdmanager create avd -f -n {n} -b {it}/{si} " \
|
||||||
"-k 'system-images;android-{al};{it};{si}' " \
|
"-k 'system-images;android-{al};{it};{si}' " \
|
||||||
"-d {d} -p {pe}".format(n=self.name, it=self.img_type, si=self.sys_img,
|
"-d {d} -p {pe}".format(n=self.name, it=self.img_type, si=self.sys_img,
|
||||||
al=self.api_level, d=self.device.replace(" ", "\ "),
|
al=self.api_level,
|
||||||
|
d=self.device.lower().replace(" ", "_") if "pixel" in self.device.lower() else self.device.replace(" ", "\ "),
|
||||||
pe=self.path_emulator)
|
pe=self.path_emulator)
|
||||||
self.logger.info(f"Command to create emulator: '{creation_cmd}'")
|
self.logger.info(f"Command to create emulator: '{creation_cmd}'")
|
||||||
subprocess.check_call(creation_cmd, shell=True)
|
subprocess.check_call(creation_cmd, shell=True)
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import shutil
|
|||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from src.device import Genymotion, DeviceType
|
from device import Genymotion, DeviceType
|
||||||
from src.helper import get_env_value_or_raise
|
from helper import get_env_value_or_raise
|
||||||
from src.constants import ENV, UTF8
|
from constants import ENV, UTF8
|
||||||
|
|
||||||
|
|
||||||
class GenyAWS(Genymotion):
|
class GenyAWS(Genymotion):
|
||||||
|
|||||||
+104
-35
@@ -1,10 +1,12 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import concurrent.futures
|
||||||
|
import uuid
|
||||||
|
|
||||||
from src.device import Genymotion, DeviceType
|
from device import Genymotion, DeviceType
|
||||||
from src.helper import get_env_value_or_raise
|
from helper import get_env_value_or_raise
|
||||||
from src.constants import ENV, UTF8
|
from constants import ENV, UTF8
|
||||||
|
|
||||||
|
|
||||||
class GenySAAS(Genymotion):
|
class GenySAAS(Genymotion):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
@@ -12,20 +14,26 @@ class GenySAAS(Genymotion):
|
|||||||
self.logger = logging.getLogger(self.__class__.__name__)
|
self.logger = logging.getLogger(self.__class__.__name__)
|
||||||
self.device_type = DeviceType.GENY_SAAS.value
|
self.device_type = DeviceType.GENY_SAAS.value
|
||||||
self.created_devices = []
|
self.created_devices = []
|
||||||
|
|
||||||
def login(self) -> None:
|
def login(self) -> None:
|
||||||
user = get_env_value_or_raise(ENV.GENY_SAAS_USER)
|
if os.getenv(ENV.GENY_AUTH_TOKEN):
|
||||||
password = get_env_value_or_raise(ENV.GENY_SAAS_PASS)
|
auth_token = get_env_value_or_raise(ENV.GENY_AUTH_TOKEN)
|
||||||
subprocess.check_call(f"gmsaas auth login {user} {password} > /dev/null 2>&1", shell=True)
|
subprocess.check_call(f"gmsaas auth token {auth_token} > /dev/null 2>&1", shell=True)
|
||||||
|
else:
|
||||||
|
user = get_env_value_or_raise(ENV.GENY_SAAS_USER)
|
||||||
|
password = get_env_value_or_raise(ENV.GENY_SAAS_PASS)
|
||||||
|
subprocess.check_call(f"gmsaas auth login {user} {password} > /dev/null 2>&1", shell=True)
|
||||||
self.logger.info("successfully logged in!")
|
self.logger.info("successfully logged in!")
|
||||||
|
|
||||||
def create(self) -> None:
|
def create(self) -> None:
|
||||||
super().create()
|
super().create()
|
||||||
|
|
||||||
|
# Collect all items first
|
||||||
|
items = []
|
||||||
for item in self.get_data_from_template(ENV.GENY_SAAS_TEMPLATE_FILE_NAME):
|
for item in self.get_data_from_template(ENV.GENY_SAAS_TEMPLATE_FILE_NAME):
|
||||||
name = ""
|
name = ""
|
||||||
template = ""
|
template = ""
|
||||||
local_port = ""
|
local_port = ""
|
||||||
|
|
||||||
# implement like this because local_port param is not a must
|
# implement like this because local_port param is not a must
|
||||||
for k, v in item.items():
|
for k, v in item.items():
|
||||||
if k.lower() == "name":
|
if k.lower() == "name":
|
||||||
@@ -36,37 +44,98 @@ class GenySAAS(Genymotion):
|
|||||||
local_port = v
|
local_port = v
|
||||||
else:
|
else:
|
||||||
self.logger.warning(f"'{k}' is not supported! Please check the documentation!")
|
self.logger.warning(f"'{k}' is not supported! Please check the documentation!")
|
||||||
|
|
||||||
if not name:
|
if not name:
|
||||||
import uuid
|
|
||||||
name = str(uuid.uuid4())
|
name = str(uuid.uuid4())
|
||||||
|
|
||||||
if not template:
|
if not template:
|
||||||
self.shutdown_and_logout()
|
self.shutdown_and_logout()
|
||||||
raise RuntimeError(f"'template' is a must parameter and not given!")
|
raise RuntimeError(f"'template' is a must parameter and not given!")
|
||||||
else:
|
|
||||||
self.logger.info(f"name: {name}, template: {template}")
|
items.append({
|
||||||
creation_cmd = f"gmsaas instances start {template} {name}"
|
'name': name,
|
||||||
try:
|
'template': template,
|
||||||
instance_id = subprocess.check_output(creation_cmd.split()).decode(UTF8).replace("\n", "")
|
'local_port': local_port
|
||||||
created_device = {f"{name}": {instance_id}}
|
})
|
||||||
self.created_devices.append(created_device)
|
|
||||||
additional_args = ""
|
# Start all devices in parallel
|
||||||
if local_port:
|
with concurrent.futures.ThreadPoolExecutor(max_workers=100) as executor:
|
||||||
additional_args = f"--adb-serial-port {local_port}"
|
try:
|
||||||
connect_cmd = f"gmsaas instances adbconnect {instance_id} {additional_args}"
|
# Submit all tasks
|
||||||
subprocess.check_call(f"{connect_cmd}", shell=True)
|
future_to_item = {executor.submit(self.create_instance, item): item for item in items}
|
||||||
except Exception as e:
|
|
||||||
self.shutdown_and_logout()
|
# Collect results as they complete
|
||||||
self.logger.error(e)
|
for future in concurrent.futures.as_completed(future_to_item):
|
||||||
exit(1)
|
item = future_to_item[future]
|
||||||
|
try:
|
||||||
|
created_device = future.result()
|
||||||
|
self.created_devices.append(created_device)
|
||||||
|
self.logger.info(f"Successfully created device: {created_device}")
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Instance creation failed for {item['name']}: {e}")
|
||||||
|
self.shutdown_and_logout()
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.shutdown_and_logout()
|
||||||
|
self.logger.error(f"Parallel execution failed: {e}")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
def create_instance(self, item_data):
|
||||||
|
"""Create a single instance and return the result"""
|
||||||
|
name = item_data['name']
|
||||||
|
template = item_data['template']
|
||||||
|
local_port = item_data['local_port']
|
||||||
|
|
||||||
|
self.logger.info(f"name: {name}, template: {template}")
|
||||||
|
creation_cmd = f"gmsaas instances start {template} {name}"
|
||||||
|
|
||||||
|
try:
|
||||||
|
instance_id = subprocess.check_output(creation_cmd.split()).decode(UTF8).replace("\n", "")
|
||||||
|
|
||||||
|
# Connect to ADB
|
||||||
|
additional_args = ""
|
||||||
|
if local_port:
|
||||||
|
additional_args = f"--adb-serial-port {local_port}"
|
||||||
|
connect_cmd = f"gmsaas instances adbconnect {instance_id} {additional_args}"
|
||||||
|
subprocess.check_call(f"{connect_cmd}", shell=True)
|
||||||
|
|
||||||
|
return {f"{name}": instance_id}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Failed to create instance {name}: {e}")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
def stop_instance(self, device_info):
|
||||||
|
"""Stop a single instance"""
|
||||||
|
for name, instance_id in device_info.items():
|
||||||
|
try:
|
||||||
|
subprocess.check_call(f"gmsaas instances stop {instance_id}", shell=True)
|
||||||
|
self.logger.info(f"device '{name}' is successfully removed!")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Failed to stop device '{name}': {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
def shutdown_and_logout(self) -> None:
|
def shutdown_and_logout(self) -> None:
|
||||||
if bool(self.created_devices):
|
if bool(self.created_devices):
|
||||||
self.logger.info("Created device(s) will be removed!")
|
self.logger.info("Created device(s) will be removed!")
|
||||||
for d in self.created_devices:
|
|
||||||
for n, i in d.items():
|
# Stop all devices in parallel
|
||||||
subprocess.check_call(f"gmsaas instances stop {i}", shell=True)
|
with concurrent.futures.ThreadPoolExecutor(max_workers=100) as executor:
|
||||||
self.logger.info(f"device '{n}' is successfully removed!")
|
# Submit all stop tasks
|
||||||
subprocess.check_call("gmsaas auth logout", shell=True)
|
futures = [executor.submit(self.stop_instance, device) for device in self.created_devices]
|
||||||
self.logger.info("successfully logged out!")
|
|
||||||
|
# Wait for all to complete
|
||||||
|
for future in concurrent.futures.as_completed(futures):
|
||||||
|
try:
|
||||||
|
future.result()
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Error during device shutdown: {e}")
|
||||||
|
|
||||||
|
# Logout after all devices are stopped
|
||||||
|
if os.getenv(ENV.GENY_AUTH_TOKEN):
|
||||||
|
subprocess.check_call("gmsaas auth reset", shell=True)
|
||||||
|
else:
|
||||||
|
subprocess.check_call("gmsaas auth logout", shell=True)
|
||||||
|
self.logger.info("successfully logged out!")
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import logging.config
|
import logging.config
|
||||||
|
|
||||||
from src.logger import LOGGING_FILE
|
from logger import LOGGING_FILE
|
||||||
|
|
||||||
|
|
||||||
def init():
|
def init():
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from src.constants import ENV
|
from constants import ENV
|
||||||
from src.tests import BaseTest
|
from tests import BaseTest
|
||||||
|
|
||||||
|
|
||||||
class BaseDeviceTest(BaseTest):
|
class BaseDeviceTest(BaseTest):
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from src.device import Device
|
from device import Device
|
||||||
from src.tests.device import BaseDeviceTest
|
from tests.device import BaseDeviceTest
|
||||||
|
|
||||||
|
|
||||||
class TestDevice(BaseDeviceTest):
|
class TestDevice(BaseDeviceTest):
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import mock
|
import mock
|
||||||
|
|
||||||
from src.device.emulator import Emulator
|
from device.emulator import Emulator
|
||||||
from src.tests.device import BaseDeviceTest
|
from tests.device import BaseDeviceTest
|
||||||
|
|
||||||
|
|
||||||
class TestEmulator(BaseDeviceTest):
|
class TestEmulator(BaseDeviceTest):
|
||||||
@@ -52,32 +52,6 @@ class TestEmulator(BaseDeviceTest):
|
|||||||
def test_initialisation_device_exists(self):
|
def test_initialisation_device_exists(self):
|
||||||
self.assertEqual(self.emu.is_initialized(), True)
|
self.assertEqual(self.emu.is_initialized(), True)
|
||||||
|
|
||||||
@mock.patch("src.device.Device.set_status")
|
|
||||||
@mock.patch("src.device.emulator.Emulator._add_profile")
|
|
||||||
@mock.patch("subprocess.check_call")
|
|
||||||
@mock.patch("src.device.emulator.Emulator._add_skin")
|
|
||||||
@mock.patch("src.device.emulator.Emulator._use_override_config")
|
|
||||||
@mock.patch("src.device.emulator.Emulator.is_initialized", mock.MagicMock(return_value=False))
|
|
||||||
def test_create_device_not_exist(self, mocked_status, mocked_profile, mocked_subprocess, mocked_skin, mocked_override_config):
|
|
||||||
self.emu.create()
|
|
||||||
self.assertEqual(mocked_status.called, True)
|
|
||||||
self.assertEqual(mocked_profile.called, True)
|
|
||||||
self.assertEqual(mocked_subprocess.called, True)
|
|
||||||
self.assertEqual(mocked_skin.called, True)
|
|
||||||
self.assertEqual(mocked_override_config.called, True)
|
|
||||||
|
|
||||||
@mock.patch("src.device.Device.set_status")
|
|
||||||
@mock.patch("src.device.emulator.Emulator._add_profile")
|
|
||||||
@mock.patch("subprocess.check_call")
|
|
||||||
@mock.patch("src.device.emulator.Emulator._add_skin")
|
|
||||||
@mock.patch("src.device.emulator.Emulator._use_override_config")
|
|
||||||
@mock.patch("src.device.emulator.Emulator.is_initialized", mock.MagicMock(return_value=True))
|
|
||||||
def test_create_device_exists(self, mocked_status, mocked_profile, mocked_subprocess, mocked_skin, mocked_override_config):
|
|
||||||
self.emu.create()
|
|
||||||
self.assertEqual(mocked_status.called, False)
|
|
||||||
self.assertEqual(mocked_profile.called, False)
|
|
||||||
self.assertEqual(mocked_subprocess.called, False)
|
|
||||||
|
|
||||||
def test_check_adb_command(self):
|
def test_check_adb_command(self):
|
||||||
with mock.patch("subprocess.check_output", mock.MagicMock(return_value="1".encode("utf-8"))):
|
with mock.patch("subprocess.check_output", mock.MagicMock(return_value="1".encode("utf-8"))):
|
||||||
self.emu.check_adb_command(
|
self.emu.check_adb_command(
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import os
|
import os
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
from src.helper import convert_str_to_bool, get_env_value_or_raise, symlink_force
|
from helper import convert_str_to_bool, get_env_value_or_raise, symlink_force
|
||||||
from src.tests import BaseTest
|
from tests import BaseTest
|
||||||
|
|
||||||
|
|
||||||
class TestHelperMethods(BaseTest):
|
class TestHelperMethods(BaseTest):
|
||||||
@@ -45,10 +45,6 @@ class TestHelperMethods(BaseTest):
|
|||||||
with self.assertRaises(RuntimeError):
|
with self.assertRaises(RuntimeError):
|
||||||
get_env_value_or_raise("env_key02")
|
get_env_value_or_raise("env_key02")
|
||||||
|
|
||||||
def test_get_env_value_with_invalid_format(self):
|
|
||||||
with mock.patch("src.logger"):
|
|
||||||
get_env_value_or_raise(True)
|
|
||||||
|
|
||||||
def test_symlink(self):
|
def test_symlink(self):
|
||||||
s = os.path.join("source.txt")
|
s = os.path.join("source.txt")
|
||||||
t = os.path.join("target_file.txt")
|
t = os.path.join("target_file.txt")
|
||||||
|
|||||||
+17
-13
@@ -1,33 +1,37 @@
|
|||||||
FROM appium/appium:v2.18.0-p0
|
FROM appium/appium:v3.5.2-p0
|
||||||
|
|
||||||
LABEL maintainer "Budi Utomo <budtmo.os@gmail.com>"
|
ARG AUTHORS="Budi Utomo"
|
||||||
|
LABEL author="${AUTHORS} <budtmo.os@gmail.com>"
|
||||||
|
|
||||||
USER root
|
USER root
|
||||||
|
|
||||||
#================
|
#================
|
||||||
# Basic Packages
|
# Basic Packages
|
||||||
#----------------
|
#----------------
|
||||||
|
# ca-certificates
|
||||||
|
# SSL client
|
||||||
|
# python3-pip
|
||||||
|
# python-pip
|
||||||
# socat
|
# socat
|
||||||
# Port forwarder
|
# Port forwarder
|
||||||
# supervisor
|
# supervisor
|
||||||
# Process manager
|
# Process manager
|
||||||
|
# unzip
|
||||||
|
# Unzip zip file
|
||||||
|
# wget
|
||||||
|
# Network downloader
|
||||||
#================
|
#================
|
||||||
RUN apt-get -qqy update && apt-get -qqy install --no-install-recommends \
|
RUN apt-get -qqy update && apt-get -qqy install --no-install-recommends \
|
||||||
|
ca-certificates \
|
||||||
|
python3-pip \
|
||||||
socat \
|
socat \
|
||||||
supervisor \
|
supervisor \
|
||||||
|
unzip \
|
||||||
|
wget \
|
||||||
&& apt autoremove -y \
|
&& apt autoremove -y \
|
||||||
&& apt clean all \
|
&& apt clean all \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
|
&& update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 1
|
||||||
#==================
|
|
||||||
# Configure Python
|
|
||||||
#==================
|
|
||||||
RUN apt-get -qqy update && \
|
|
||||||
apt-get -qqy --no-install-recommends install \
|
|
||||||
python3-pip \
|
|
||||||
&& apt-get clean \
|
|
||||||
&& rm -rf /var/lib/apt/lists/* \
|
|
||||||
&& update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 1
|
|
||||||
|
|
||||||
#=====================
|
#=====================
|
||||||
# Set release version
|
# Set release version
|
||||||
|
|||||||
+8
-14
@@ -11,7 +11,7 @@ ENV EMULATOR_ANDROID_VERSION=${EMULATOR_ANDROID_VERSION} \
|
|||||||
EMULATOR_SYS_IMG=x86_64 \
|
EMULATOR_SYS_IMG=x86_64 \
|
||||||
EMULATOR_IMG_TYPE=google_apis \
|
EMULATOR_IMG_TYPE=google_apis \
|
||||||
EMULATOR_BROWSER=chrome
|
EMULATOR_BROWSER=chrome
|
||||||
ENV PATH ${PATH}:${ANDROID_HOME}/build-tools
|
ENV PATH=${PATH}:${ANDROID_HOME}/build-tools
|
||||||
RUN yes | sdkmanager --licenses \
|
RUN yes | sdkmanager --licenses \
|
||||||
&& sdkmanager "platforms;android-${EMULATOR_API_LEVEL}" \
|
&& sdkmanager "platforms;android-${EMULATOR_API_LEVEL}" \
|
||||||
"system-images;android-${EMULATOR_API_LEVEL};${EMULATOR_IMG_TYPE};${EMULATOR_SYS_IMG}" "emulator" \
|
"system-images;android-${EMULATOR_API_LEVEL};${EMULATOR_IMG_TYPE};${EMULATOR_SYS_IMG}" "emulator" \
|
||||||
@@ -20,14 +20,10 @@ RUN yes | sdkmanager --licenses \
|
|||||||
#=============
|
#=============
|
||||||
# UI Packages
|
# UI Packages
|
||||||
#-------------
|
#-------------
|
||||||
# ffmpeg
|
|
||||||
# Video recorder
|
|
||||||
# feh
|
# feh
|
||||||
# Screen background
|
# Screen background
|
||||||
# libxcomposite-dev
|
# libxcomposite-dev
|
||||||
# Window System for Emulator
|
# Window System for Emulator
|
||||||
# menu
|
|
||||||
# Debian menu
|
|
||||||
# openbox
|
# openbox
|
||||||
# Windows manager
|
# Windows manager
|
||||||
# x11vnc
|
# x11vnc
|
||||||
@@ -36,10 +32,8 @@ RUN yes | sdkmanager --licenses \
|
|||||||
# Terminal emulator
|
# Terminal emulator
|
||||||
#==================
|
#==================
|
||||||
RUN apt-get -qqy update && apt-get -qqy install --no-install-recommends \
|
RUN apt-get -qqy update && apt-get -qqy install --no-install-recommends \
|
||||||
ffmpeg \
|
|
||||||
feh \
|
feh \
|
||||||
libxcomposite-dev \
|
libxcomposite-dev \
|
||||||
menu \
|
|
||||||
openbox \
|
openbox \
|
||||||
x11vnc \
|
x11vnc \
|
||||||
xterm \
|
xterm \
|
||||||
@@ -50,8 +44,8 @@ RUN apt-get -qqy update && apt-get -qqy install --no-install-recommends \
|
|||||||
#=======
|
#=======
|
||||||
# noVNC
|
# noVNC
|
||||||
#=======
|
#=======
|
||||||
ENV NOVNC_VERSION="1.4.0" \
|
ENV NOVNC_VERSION="1.7.0" \
|
||||||
WEBSOCKIFY_VERSION="0.11.0" \
|
WEBSOCKIFY_VERSION="0.13.0" \
|
||||||
OPT_PATH="/opt"
|
OPT_PATH="/opt"
|
||||||
RUN wget -nv -O noVNC.zip "https://github.com/novnc/noVNC/archive/refs/tags/v${NOVNC_VERSION}.zip" \
|
RUN wget -nv -O noVNC.zip "https://github.com/novnc/noVNC/archive/refs/tags/v${NOVNC_VERSION}.zip" \
|
||||||
&& unzip -x noVNC.zip \
|
&& unzip -x noVNC.zip \
|
||||||
@@ -78,13 +72,13 @@ EXPOSE 5900 6080
|
|||||||
#==========
|
#==========
|
||||||
RUN rm -rf ${SCRIPT_PATH}
|
RUN rm -rf ${SCRIPT_PATH}
|
||||||
ENV SCRIPT_PATH="docker-android"
|
ENV SCRIPT_PATH="docker-android"
|
||||||
ENV WORK_PATH="/home/androidusr"
|
|
||||||
ENV APP_PATH=${WORK_PATH}/${SCRIPT_PATH}
|
ENV APP_PATH=${WORK_PATH}/${SCRIPT_PATH}
|
||||||
RUN mkdir -p ${APP_PATH}
|
RUN mkdir -p ${APP_PATH}
|
||||||
COPY mixins ${APP_PATH}/mixins
|
COPY mixins ${APP_PATH}/mixins
|
||||||
COPY cli ${APP_PATH}/cli
|
COPY cli ${APP_PATH}/cli
|
||||||
RUN chown -R 1300:1301 ${APP_PATH} \
|
RUN --mount=type=secret,id=extension,dst=/tmp/extension.sh \
|
||||||
&& pip install --quiet -e ${APP_PATH}/cli
|
bash /tmp/extension.sh
|
||||||
|
RUN echo "${USERNAME} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
||||||
|
|
||||||
#===================
|
#===================
|
||||||
# Configure OpenBox
|
# Configure OpenBox
|
||||||
@@ -94,7 +88,7 @@ RUN echo ${APP_PATH}/mixins/configs/display/.fehbg >> /etc/xdg/openbox/autostart
|
|||||||
#==================
|
#==================
|
||||||
# Use created user
|
# Use created user
|
||||||
#==================
|
#==================
|
||||||
USER 1300:1301
|
USER ${USERID}:${GROUPID}
|
||||||
ENV LOG_PATH=${WORK_PATH}/logs \
|
ENV LOG_PATH=${WORK_PATH}/logs \
|
||||||
WEB_LOG_PORT=9000
|
WEB_LOG_PORT=9000
|
||||||
EXPOSE 9000
|
EXPOSE 9000
|
||||||
@@ -107,4 +101,4 @@ RUN mkdir -p "${WORK_PATH}/.config/Android Open Source Project" \
|
|||||||
#=========
|
#=========
|
||||||
STOPSIGNAL SIGTERM
|
STOPSIGNAL SIGTERM
|
||||||
ENV DEVICE_TYPE=emulator
|
ENV DEVICE_TYPE=emulator
|
||||||
ENTRYPOINT ["/home/androidusr/docker-android/mixins/scripts/run.sh"]
|
CMD ["${APP_PATH}/mixins/scripts/run.sh"]
|
||||||
|
|||||||
+5
-11
@@ -1,12 +1,7 @@
|
|||||||
ARG DOCKER_ANDROID_VERSION
|
ARG DOCKER_ANDROID_VERSION
|
||||||
FROM budtmo/docker-android:base_${DOCKER_ANDROID_VERSION}
|
FROM budtmo/docker-android:base_${DOCKER_ANDROID_VERSION}
|
||||||
|
|
||||||
#===================================================
|
ENV GMSAAS_CLI_VERSION="1.15.0"
|
||||||
# Install Genymotion CLI
|
|
||||||
# (for user management and deployment on Geny Cloud)
|
|
||||||
#===================================================
|
|
||||||
ENV GMSAAS_CLI_VERSION="1.7.1"
|
|
||||||
RUN pip install gmsaas==${GMSAAS_CLI_VERSION}
|
|
||||||
|
|
||||||
#================
|
#================
|
||||||
# Cloud Packages
|
# Cloud Packages
|
||||||
@@ -35,13 +30,12 @@ RUN wget -nv -O terraform.zip "https://releases.hashicorp.com/terraform/${TERRAF
|
|||||||
#==========
|
#==========
|
||||||
RUN rm -rf ${SCRIPT_PATH}
|
RUN rm -rf ${SCRIPT_PATH}
|
||||||
ENV SCRIPT_PATH="docker-android"
|
ENV SCRIPT_PATH="docker-android"
|
||||||
ENV WORK_PATH="/home/androidusr"
|
|
||||||
ENV APP_PATH=${WORK_PATH}/${SCRIPT_PATH}
|
ENV APP_PATH=${WORK_PATH}/${SCRIPT_PATH}
|
||||||
RUN mkdir -p ${APP_PATH}
|
RUN mkdir -p ${APP_PATH}
|
||||||
COPY mixins ${APP_PATH}/mixins
|
COPY mixins ${APP_PATH}/mixins
|
||||||
COPY cli ${APP_PATH}/cli
|
COPY cli ${APP_PATH}/cli
|
||||||
RUN chown -R 1300:1301 ${APP_PATH} \
|
RUN --mount=type=secret,id=extension,dst=/tmp/extension.sh \
|
||||||
&& pip install --quiet -e ${APP_PATH}/cli
|
bash /tmp/extension.sh
|
||||||
|
|
||||||
#===================================
|
#===================================
|
||||||
# Create Genymotion Template folder
|
# Create Genymotion Template folder
|
||||||
@@ -52,7 +46,7 @@ RUN mkdir -p ${GENYMOTION_TEMPLATE_PATH}
|
|||||||
#==================
|
#==================
|
||||||
# Use created user
|
# Use created user
|
||||||
#==================
|
#==================
|
||||||
USER 1300:1301
|
USER ${USERID}:${GROUPID}
|
||||||
ENV LOG_PATH=${WORK_PATH}/logs \
|
ENV LOG_PATH=${WORK_PATH}/logs \
|
||||||
WEB_LOG_PORT=9000
|
WEB_LOG_PORT=9000
|
||||||
EXPOSE 9000
|
EXPOSE 9000
|
||||||
@@ -63,4 +57,4 @@ RUN mkdir -p ${LOG_PATH}
|
|||||||
#=========
|
#=========
|
||||||
STOPSIGNAL SIGTERM
|
STOPSIGNAL SIGTERM
|
||||||
RUN gmsaas config set android-sdk-path ${ANDROID_HOME}
|
RUN gmsaas config set android-sdk-path ${ANDROID_HOME}
|
||||||
ENTRYPOINT ["/home/androidusr/docker-android/mixins/scripts/run.sh"]
|
CMD ["${APP_PATH}/mixins/scripts/run.sh"]
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ List of Docker-Images
|
|||||||
|13.0|33|Normal|budtmo2/docker-android-pro:emulator_13.0|budtmo2/docker-android-pro:emulator_13.0_<release_version>|
|
|13.0|33|Normal|budtmo2/docker-android-pro:emulator_13.0|budtmo2/docker-android-pro:emulator_13.0_<release_version>|
|
||||||
|14.0|34|Normal|budtmo2/docker-android-pro:emulator_14.0|budtmo2/docker-android-pro:emulator_14.0_<release_version>|
|
|14.0|34|Normal|budtmo2/docker-android-pro:emulator_14.0|budtmo2/docker-android-pro:emulator_14.0_<release_version>|
|
||||||
|15.0|35|Normal|budtmo2/docker-android-pro:emulator_15.0|budtmo2/docker-android-pro:emulator_15.0_<release_version>|
|
|15.0|35|Normal|budtmo2/docker-android-pro:emulator_15.0|budtmo2/docker-android-pro:emulator_15.0_<release_version>|
|
||||||
|
|16.0|36|Normal|budtmo2/docker-android-pro:emulator_16.0|budtmo2/docker-android-pro:emulator_16.0_<release_version>|
|
||||||
|9.0|28|Headless|budtmo2/docker-android-pro:emulator_headless_9.0|budtmo2/docker-android-pro:emulator_headless_9.0_<release_version>|
|
|9.0|28|Headless|budtmo2/docker-android-pro:emulator_headless_9.0|budtmo2/docker-android-pro:emulator_headless_9.0_<release_version>|
|
||||||
|10.0|29|Headless|budtmo2/docker-android-pro:emulator_headless_10.0|budtmo2/docker-android-pro:emulator_headless_10.0_<release_version>|
|
|10.0|29|Headless|budtmo2/docker-android-pro:emulator_headless_10.0|budtmo2/docker-android-pro:emulator_headless_10.0_<release_version>|
|
||||||
|11.0|30|Headless|budtmo2/docker-android-pro:emulator_headless_11.0|budtmo2/docker-android-pro:emulator_headless_11.0_<release_version>|
|
|11.0|30|Headless|budtmo2/docker-android-pro:emulator_headless_11.0|budtmo2/docker-android-pro:emulator_headless_11.0_<release_version>|
|
||||||
@@ -36,6 +37,7 @@ List of Docker-Images
|
|||||||
|13.0|33|Headless|budtmo2/docker-android-pro:emulator_headless_13.0|budtmo2/docker-android-pro:emulator_headless_13.0_<release_version>|
|
|13.0|33|Headless|budtmo2/docker-android-pro:emulator_headless_13.0|budtmo2/docker-android-pro:emulator_headless_13.0_<release_version>|
|
||||||
|14.0|34|Headless|budtmo2/docker-android-pro:emulator_headless_14.0|budtmo2/docker-android-pro:emulator_headless_14.0_<release_version>|
|
|14.0|34|Headless|budtmo2/docker-android-pro:emulator_headless_14.0|budtmo2/docker-android-pro:emulator_headless_14.0_<release_version>|
|
||||||
|15.0|35|Headless|budtmo2/docker-android-pro:emulator_headless_15.0|budtmo2/docker-android-pro:emulator_headless_15.0_<release_version>|
|
|15.0|35|Headless|budtmo2/docker-android-pro:emulator_headless_15.0|budtmo2/docker-android-pro:emulator_headless_15.0_<release_version>|
|
||||||
|
|16.0|36|Headless|budtmo2/docker-android-pro:emulator_headless_16.0|budtmo2/docker-android-pro:emulator_headless_16.0_<release_version>|
|
||||||
|-|-|Selenium|budtmo2/docker-android-pro:selenium|budtmo2/docker-android-pro:selenium_<release_version>|
|
|-|-|Selenium|budtmo2/docker-android-pro:selenium|budtmo2/docker-android-pro:selenium_<release_version>|
|
||||||
|
|
||||||
***Note: Headless mode does not have any Web-UI***
|
***Note: Headless mode does not have any Web-UI***
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
```
|
||||||
|
export USER="xxx"
|
||||||
|
export PASS="xxx"
|
||||||
|
|
||||||
|
docker run -d -p 4723:4723 -v ${PWD}/example/genycloud/saas.json:/home/androidusr/genymotion_template/saas.json -e DEVICE_TYPE=geny_saas -e GENY_SAAS_USER=${USER} -e GENY_SAAS_PASS=${PASS} -e APPIUM=true --name android-container budtmo/docker-android:genymotion
|
||||||
|
```
|
||||||
Genymotion Cloud
|
Genymotion Cloud
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
@@ -9,12 +15,13 @@ You can use Genymotion Android virtual devices in the cloud. They are available
|
|||||||
Use [saas.json](../example/genymotion/saas.json) to define the devices that you want to use. You can specify the port on which the device will start so you don't need to change the device name in your tests every time you need to run those tests. Then run following command
|
Use [saas.json](../example/genymotion/saas.json) to define the devices that you want to use. You can specify the port on which the device will start so you don't need to change the device name in your tests every time you need to run those tests. Then run following command
|
||||||
|
|
||||||
```
|
```
|
||||||
export USER="xxx"
|
export AUTH_TOKEN="xxx"
|
||||||
export PASS="xxx"
|
|
||||||
|
|
||||||
docker run -d -p 4723:4723 -v ${PWD}/example/genycloud/saas.json:/home/androidusr/genymotion_template/saas.json -e DEVICE_TYPE=geny_saas -e GENY_SAAS_USER=${USER} -e GENY_SAAS_PASS=${PASS} -e APPIUM=true --name android-container budtmo/docker-android:genymotion
|
docker run -d -p 4723:4723 -v ${PWD}/example/genycloud/saas.json:/home/androidusr/genymotion_template/saas.json -e DEVICE_TYPE=geny_saas -e GENY_AUTH_TOKEN=${AUTH_TOKEN} -e APPIUM=true --name android-container budtmo/docker-android:genymotion
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Genymotion has deprecated credential based login since gmsaas 1.10.0, but if necessary, you can still provide them using `-e GENY_SAAS_USER=${USER} -e GENY_SAAS_PASS=${PASS}` instead of `-e GENY_AUTH_TOKEN=${AUTH_TOKEN}`.
|
||||||
|
|
||||||
The deployed device(s) are automatically connected with adb inside docker container. Stopping the emulator will remove all deployed device(s) on Genymotion SaaS and user will be logged out at the end.
|
The deployed device(s) are automatically connected with adb inside docker container. Stopping the emulator will remove all deployed device(s) on Genymotion SaaS and user will be logged out at the end.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user