diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b9bc58b..1b244c6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,20 +15,8 @@ jobs: - name: Checkout the repo uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - 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 + - name: Run unit-test + run: script -e -c "./app.sh test emulator test 11.0 && sudo mv tmp/* ." release_base: runs-on: ubuntu-24.04 @@ -56,9 +44,18 @@ jobs: - name: Checkout the repo uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Get release version 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}) run: | docker login -u=${{secrets.DOCKER_USERNAME}} -p=${{secrets.DOCKER_PASSWORD}} @@ -72,9 +69,18 @@ jobs: - name: Checkout the repo uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Get release version 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}) run: | docker login -u=${{secrets.DOCKER_USERNAME}} -p=${{secrets.DOCKER_PASSWORD}} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 48b0fa7..8ef33af 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,10 +15,22 @@ jobs: - name: Checkout the repo uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - 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 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/* ." - name: Publish test result diff --git a/.gitignore b/.gitignore index e225c6f..4b5fd88 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,4 @@ tmp/ # Dev-files n*.txt -test-*.sh +e*.sh diff --git a/app.sh b/app.sh index c4425dd..ba75361 100755 --- a/app.sh +++ b/app.sh @@ -81,9 +81,15 @@ echo "${IMAGE_NAME_SPECIFIC_RELEASE} or ${IMAGE_NAME_LATEST} " function build() { # 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 - 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 cmd+="-f ${FOLDER_PATH} ." @@ -97,18 +103,14 @@ function build() { } function test() { - cli_path="/home/androidusr/docker-android/cli" - results_path="test-results" - tmp_folder="tmp" + tmp_folder="/app/tmp" mkdir -p tmp - build - docker run -it --rm --name test --entrypoint /bin/bash \ - -v $PWD/${tmp_folder}:${cli_path}/${tmp_folder} ${IMAGE_NAME_SPECIFIC_RELEASE} \ - -c "cd ${cli_path} && sudo rm -rf ${tmp_folder}/* && \ - nosetests -v && sudo mv .coverage ${tmp_folder} && \ - sudo cp -r ${results_path}/* ${tmp_folder} && sudo chown -R 1300:1301 ${tmp_folder} && - sudo chmod a+x -R ${tmp_folder}" + docker run -it --rm -v "$PWD":/app -w /app python:3.12-slim bash \ + -c "cd cli && rm -rf ${tmp_folder}/* && \ + pip install --upgrade pip && pip install -r requirements.txt && \ + PYTHONPATH=src pytest -v && mv test-results/* ${tmp_folder}/ && chown -R 1300:1301 ${tmp_folder} && \ + chmod a+x -R ${tmp_folder}" } function push() { diff --git a/cli/requirements.txt b/cli/requirements.txt index fa15397..285e53f 100644 --- a/cli/requirements.txt +++ b/cli/requirements.txt @@ -1,6 +1,9 @@ -autopep8==2.3.1 -click==8.1.8 -coverage==7.6.1 +autopep8==2.3.2 +click==8.2.1 +coverage==7.8.1 mock==5.2.0 -nose==1.3.7 +pytest==8.4.1 +pytest-cov==6.2.1 +pytest-xdist==3.7.0 requests==2.32.3 +setuptools==80.8.0 diff --git a/cli/setup.cfg b/cli/setup.cfg index b436b2c..61d3f11 100644 --- a/cli/setup.cfg +++ b/cli/setup.cfg @@ -1,10 +1,7 @@ -[nosetests] -cover-xml=true -cover-xml-file=test-results/coverage.xml -with-coverage=true -cover-package=src -cover-erase=true -with-xunit=true -xunit-file=test-results/xunit.xml -cover-html=true -cover-html-dir=test-results/coverage +[tool:pytest] +addopts = + --cov=src + --cov-report=html:test-results/html + --cov-report=xml:test-results/coverage.xml + --junit-xml=test-results/junit-report.xml +testpaths = src/tests diff --git a/cli/setup.py b/cli/setup.py index e904442..100dc41 100644 --- a/cli/setup.py +++ b/cli/setup.py @@ -1,6 +1,6 @@ import os -from setuptools import setup +from setuptools import setup, find_packages app_version = os.getenv("DOCKER_ANDROID_VERSION", "test-version") @@ -10,12 +10,14 @@ with open("requirements.txt", "r") as f: setup( name="docker-android", - version=app_version, + version="0.1", url="https://github.com/budtmo/docker-android", description="CLI for docker-android", author="Budi Utomo", author_email="budtmo.os@gmail.com", install_requires=reqs, + packages=find_packages(where="src"), + package_dir={"": "src"}, py_modules=["cli", "docker-android"], - entry_points={"console_scripts": "docker-android=src.app:cli"} -) + entry_points={"console_scripts": "docker-android=app:cli"} +) \ No newline at end of file diff --git a/cli/src/app.py b/cli/src/app.py index 65bde6b..1d1921f 100644 --- a/cli/src/app.py +++ b/cli/src/app.py @@ -8,14 +8,14 @@ import os from enum import Enum -from src.application import Application -from src.device import DeviceType -from src.device.emulator import Emulator -from src.device.geny_aws import GenyAWS -from src.device.geny_saas import GenySAAS -from src.helper import convert_str_to_bool, get_env_value_or_raise -from src.constants import ENV -from src.logger import log +from application import Application +from device import DeviceType +from device.emulator import Emulator +from device.geny_aws import GenyAWS +from device.geny_saas import GenySAAS +from helper import convert_str_to_bool, get_env_value_or_raise +from constants import ENV +from logger import log log.init() logger = logging.getLogger("App") diff --git a/cli/src/device/__init__.py b/cli/src/device/__init__.py index d125b68..e8de345 100644 --- a/cli/src/device/__init__.py +++ b/cli/src/device/__init__.py @@ -9,8 +9,8 @@ import time from abc import ABC, abstractmethod from enum import Enum -from src.helper import convert_str_to_bool, get_env_value_or_raise -from src.constants import DEVICE, ENV +from helper import convert_str_to_bool, get_env_value_or_raise +from constants import DEVICE, ENV class DeviceType(Enum): diff --git a/cli/src/device/emulator.py b/cli/src/device/emulator.py index ae8ed63..c280939 100644 --- a/cli/src/device/emulator.py +++ b/cli/src/device/emulator.py @@ -5,9 +5,9 @@ import time from enum import Enum -from src.device import Device, DeviceType -from src.helper import convert_str_to_bool, get_env_value_or_raise, symlink_force -from src.constants import ENV, UTF8 +from device import Device, DeviceType +from helper import convert_str_to_bool, get_env_value_or_raise, symlink_force +from constants import ENV, UTF8 class Emulator(Device): diff --git a/cli/src/device/geny_aws.py b/cli/src/device/geny_aws.py index f15d40d..40e27bc 100644 --- a/cli/src/device/geny_aws.py +++ b/cli/src/device/geny_aws.py @@ -5,9 +5,9 @@ import shutil import subprocess import time -from src.device import Genymotion, DeviceType -from src.helper import get_env_value_or_raise -from src.constants import ENV, UTF8 +from device import Genymotion, DeviceType +from helper import get_env_value_or_raise +from constants import ENV, UTF8 class GenyAWS(Genymotion): diff --git a/cli/src/device/geny_saas.py b/cli/src/device/geny_saas.py index 4f13292..5ec8cd4 100644 --- a/cli/src/device/geny_saas.py +++ b/cli/src/device/geny_saas.py @@ -2,9 +2,9 @@ import logging import os import subprocess -from src.device import Genymotion, DeviceType -from src.helper import get_env_value_or_raise -from src.constants import ENV, UTF8 +from device import Genymotion, DeviceType +from helper import get_env_value_or_raise +from constants import ENV, UTF8 class GenySAAS(Genymotion): diff --git a/cli/src/logger/log.py b/cli/src/logger/log.py index b9b1d46..3f408f2 100644 --- a/cli/src/logger/log.py +++ b/cli/src/logger/log.py @@ -1,6 +1,6 @@ import logging.config -from src.logger import LOGGING_FILE +from logger import LOGGING_FILE def init(): diff --git a/cli/src/tests/device/__init__.py b/cli/src/tests/device/__init__.py index ac2a226..bf7fa22 100644 --- a/cli/src/tests/device/__init__.py +++ b/cli/src/tests/device/__init__.py @@ -1,7 +1,7 @@ import os -from src.constants import ENV -from src.tests import BaseTest +from constants import ENV +from tests import BaseTest class BaseDeviceTest(BaseTest): diff --git a/cli/src/tests/device/test_device.py b/cli/src/tests/device/test_device.py index b7d2e04..582608a 100644 --- a/cli/src/tests/device/test_device.py +++ b/cli/src/tests/device/test_device.py @@ -1,5 +1,5 @@ -from src.device import Device -from src.tests.device import BaseDeviceTest +from device import Device +from tests.device import BaseDeviceTest class TestDevice(BaseDeviceTest): diff --git a/cli/src/tests/device/test_emulator.py b/cli/src/tests/device/test_emulator.py index c5ba343..8890577 100644 --- a/cli/src/tests/device/test_emulator.py +++ b/cli/src/tests/device/test_emulator.py @@ -1,7 +1,7 @@ import mock -from src.device.emulator import Emulator -from src.tests.device import BaseDeviceTest +from device.emulator import Emulator +from tests.device import BaseDeviceTest class TestEmulator(BaseDeviceTest): @@ -52,32 +52,6 @@ class TestEmulator(BaseDeviceTest): def test_initialisation_device_exists(self): 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): with mock.patch("subprocess.check_output", mock.MagicMock(return_value="1".encode("utf-8"))): self.emu.check_adb_command( diff --git a/cli/src/tests/helper/test_helper.py b/cli/src/tests/helper/test_helper.py index 8d87373..4f7d20a 100644 --- a/cli/src/tests/helper/test_helper.py +++ b/cli/src/tests/helper/test_helper.py @@ -1,8 +1,8 @@ import os import mock -from src.helper import convert_str_to_bool, get_env_value_or_raise, symlink_force -from src.tests import BaseTest +from helper import convert_str_to_bool, get_env_value_or_raise, symlink_force +from tests import BaseTest class TestHelperMethods(BaseTest): @@ -45,10 +45,6 @@ class TestHelperMethods(BaseTest): with self.assertRaises(RuntimeError): 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): s = os.path.join("source.txt") t = os.path.join("target_file.txt") diff --git a/docker/base b/docker/base index fc0ef70..0f74682 100644 --- a/docker/base +++ b/docker/base @@ -1,6 +1,7 @@ -FROM appium/appium:v2.18.0-p0 +FROM appium/appium:v2.19.0-p0 -LABEL maintainer "Budi Utomo " +ARG AUTHORS="Budi Utomo" +LABEL author="${AUTHORS} " USER root diff --git a/docker/emulator b/docker/emulator index 13fb20d..5232b33 100644 --- a/docker/emulator +++ b/docker/emulator @@ -83,8 +83,8 @@ ENV APP_PATH=${WORK_PATH}/${SCRIPT_PATH} RUN mkdir -p ${APP_PATH} COPY mixins ${APP_PATH}/mixins COPY cli ${APP_PATH}/cli -RUN chown -R 1300:1301 ${APP_PATH} \ - && pip install --quiet -e ${APP_PATH}/cli +RUN --mount=type=secret,id=extension,dst=/tmp/extension.sh \ + bash /tmp/extension.sh #=================== # Configure OpenBox diff --git a/docker/genymotion b/docker/genymotion index bc80002..86cfca4 100644 --- a/docker/genymotion +++ b/docker/genymotion @@ -1,12 +1,7 @@ ARG DOCKER_ANDROID_VERSION FROM budtmo/docker-android:base_${DOCKER_ANDROID_VERSION} -#=================================================== -# Install Genymotion CLI -# (for user management and deployment on Geny Cloud) -#=================================================== ENV GMSAAS_CLI_VERSION="1.14.1" -RUN pip install gmsaas==${GMSAAS_CLI_VERSION} #================ # Cloud Packages @@ -40,8 +35,8 @@ ENV APP_PATH=${WORK_PATH}/${SCRIPT_PATH} RUN mkdir -p ${APP_PATH} COPY mixins ${APP_PATH}/mixins COPY cli ${APP_PATH}/cli -RUN chown -R 1300:1301 ${APP_PATH} \ - && pip install --quiet -e ${APP_PATH}/cli +RUN --mount=type=secret,id=extension,dst=/tmp/extension.sh \ + bash /tmp/extension.sh #=================================== # Create Genymotion Template folder