diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3f63841..3fb3372 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,10 +10,10 @@ jobs: - name: Checkout source uses: actions/checkout@v2 - - name: Set up Python 3.8 + - name: Set up Python 3.10.19 uses: actions/setup-python@v1 with: - python-version: 3.8 + python-version: 3.10.19 - name: Install build dependencies run: python -m pip install build wheel diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4fca68c..2640927 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,7 +23,7 @@ jobs: strategy: fail-fast: true matrix: - python-version: [3.6, 3.7, 3.8, 3.9] + python-version: ['3.8', '3.9', '3.10'] steps: - name: Check out repository code uses: actions/checkout@v2 @@ -40,6 +40,9 @@ jobs: run: tox - name: "Upload coverage to Codecov" - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v5 with: fail_ci_if_error: true + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + diff --git a/nak/command.py b/nak/command.py index 381be97..6dade25 100644 --- a/nak/command.py +++ b/nak/command.py @@ -6,7 +6,7 @@ from nak.gateway import Gateway from nak.settings import (CONFIG_FILE, ENV_FILE, LOG_COLOR, ZIP_DESTINATION_DIRECTORY, ZIP_DESTINATION_PATH) -from nak.utils import (get_all_file, get_error_from_response, hide_variable, +from nak.utils import (get_all_file, get_error_from_response, hide_variable, get_lastest_build_file, progress_bar) logging.basicConfig( @@ -63,6 +63,7 @@ def build(self, parser=None): new_zip.write(file) new_zip.close() + logging.info(LOG_COLOR.INFO.format(message=f'Created build file with {destination_file.split("/")[-1]}.')) logging.info(LOG_COLOR.SUCCESS.format(message='Build successfully.')) def push(self, parser=None): @@ -76,11 +77,10 @@ def push(self, parser=None): raise TypeError(LOG_COLOR.ERROR.format( message=('No build file available. Run "nak build".'))) - zip_file_list = get_all_file(path=f"./{ZIP_DESTINATION_DIRECTORY}", required_extension='.zip') - if not zip_file_list: + latest_build_file = get_lastest_build_file() + if not latest_build_file: raise TypeError(LOG_COLOR.ERROR.format(message='Please run build before push command.')) - latest_build_file = sorted(zip_file_list, key=lambda file_name: file_name, reverse=True)[0] file_name = latest_build_file.split("/")[-1] logging.info(LOG_COLOR.INFO.format(message=f'Pushing to app with client_id {self.config.client_id}')) diff --git a/nak/utils.py b/nak/utils.py index 4adfcab..7dc279f 100644 --- a/nak/utils.py +++ b/nak/utils.py @@ -2,11 +2,18 @@ import time from pathlib import Path -from nak.settings import ALLOW_FILE_EXTENSIONS, ZIP_EXCLUDE_FILES +from nak.settings import ALLOW_FILE_EXTENSIONS, ZIP_DESTINATION_DIRECTORY, ZIP_EXCLUDE_FILES -def get_latest_zip(pathfile): - return Path(os.path.relpath(pathfile)).as_posix() +def get_lastest_build_file(): + path = Path(f"./{ZIP_DESTINATION_DIRECTORY}") + + zip_files = [f for f in path.iterdir() if f.is_file() and f.suffix == ".zip"] + if not zip_files: + return None + + latest_zip = max(zip_files, key=lambda f: f.stat().st_ctime) + return latest_zip.as_posix() def progress_bar(iterable, prefix='', suffix='', decimals=1, length=100, fill='█', printEnd="\r"): @@ -72,7 +79,7 @@ def get_error_from_response(response): result = response.json() error_msg = "" for key, value in result.items(): - if type(value) == list: + if isinstance(value, list): error_msg += f'"{key}" : {" ".join(value)}' else: error_msg += value diff --git a/setup.py b/setup.py index 2494a2d..8ebbd59 100644 --- a/setup.py +++ b/setup.py @@ -1,10 +1,10 @@ from setuptools import find_packages, setup -__version__ = '1.0.3' +__version__ = '1.0.4' tests_require = [ "flake8==3.9.2", - "nose==1.3.7" + "pytest==7.2.2" ] with open('README.md', 'r') as fh: diff --git a/tests/test_command.py b/tests/test_command.py index 3e63224..c476115 100644 --- a/tests/test_command.py +++ b/tests/test_command.py @@ -2,7 +2,7 @@ from unittest.mock import MagicMock, call, mock_open, patch from nak.command import Command -from nak.settings import LOG_COLOR +from nak.settings import LOG_COLOR, ZIP_DESTINATION_PATH class TestCommand(unittest.TestCase): @@ -106,9 +106,12 @@ def test_build_with_correct_directory_should_create_zip_file_correctly( call.write('test2.file'), call.close(), ] + + file_name = ZIP_DESTINATION_PATH.format(app_name="app-kit").split('/')[-1] assert log.output == [ f"INFO:root:{LOG_COLOR.INFO.format(message='file: test1.file')}", f"INFO:root:{LOG_COLOR.INFO.format(message='file: test2.file')}", + f"INFO:root:{LOG_COLOR.INFO.format(message=f'Created build file with {file_name}.')}", f"INFO:root:{LOG_COLOR.SUCCESS.format(message='Build successfully.')}" ] @@ -116,12 +119,10 @@ def test_build_with_correct_directory_should_create_zip_file_correctly( # push ##### @patch("os.path.exists", autospec=True) - @patch("nak.command.get_all_file", autospec=True) @patch("nak.command.Config.write_config", autospec=True) def test_push_with_wrong_directory_should_raise_error_correctly( - self, mock_write_config, mock_get_file, mock_path_exists + self, mock_write_config, mock_path_exists ): - mock_get_file.return_value = ["test1.file", "test2.file"] mock_path_exists.side_effect = [ # check envfile at read_config False, @@ -139,12 +140,13 @@ def test_push_with_wrong_directory_should_raise_error_correctly( @patch("builtins.open", autospec=True) @patch("os.path.exists", autospec=True) - @patch("nak.command.get_all_file", autospec=True) + @patch("nak.command.get_lastest_build_file", autospec=True) @patch("nak.command.Config.write_config", autospec=True) def test_push_with_failed_on_server_should_log_error_correctly( self, mock_write_config, mock_get_file, mock_path_exists, mock_open_file ): - mock_get_file.return_value = ["test1-20200101010101.zip", "test2-20200101010102.zip"] + mock_get_file.return_value = "test1-20200101010101.zip" + mock_path_exists.side_effect = [ # check envfile at read_config True, @@ -165,24 +167,24 @@ def test_push_with_failed_on_server_should_log_error_correctly( self.mock_gateway.return_value.update_app.assert_called_once_with( files={ # send with latest file - 'file': ('test2-20200101010102.zip', file) + 'file': ('test1-20200101010101.zip', file) } ) assert log.output == [ f'INFO:root:{LOG_COLOR.INFO.format(message="Pushing to app with client_id 123456")}', - f'INFO:root:{LOG_COLOR.INFO.format(message="with filename test2-20200101010102.zip")}', + f'INFO:root:{LOG_COLOR.INFO.format(message="with filename test1-20200101010101.zip")}', f'INFO:root:{LOG_COLOR.INFO.format(message="by username test@29next.com")}', f"INFO:root:{LOG_COLOR.ERROR.format(message='Upload file to server failed. file size limit')}" ] @patch("builtins.open", autospec=True) @patch("os.path.exists", autospec=True) - @patch("nak.command.get_all_file", autospec=True) + @patch("nak.command.get_lastest_build_file", autospec=True) @patch("nak.command.Config.write_config", autospec=True) def test_push_with_correct_directory_should_call_gateway_correctly( self, mock_write_config, mock_get_file, mock_path_exists, mock_open_file ): - mock_get_file.return_value = ["test1-20200101010101.zip", "test2-20200101010102.zip"] + mock_get_file.return_value = "test2-20200101010102.zip" mock_path_exists.side_effect = [ # check envfile at read_config True, @@ -233,7 +235,7 @@ def test_push_with_no_directory_should_raise_error( message=('No build file available. Run "nak build".')) @patch("os.path.exists", autospec=True) - @patch("nak.command.get_all_file", autospec=True) + @patch("nak.command.get_lastest_build_file", autospec=True) @patch("nak.command.Config.write_config", autospec=True) def test_push_with_no_files_in_directory_should_raise_error( self, mock_write_config, mock_get_file, mock_path_exists diff --git a/tests/test_utils.py b/tests/test_utils.py index 5bc3d4a..047470c 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -67,3 +67,41 @@ def test_get_all_file_with_required_extension_file_should_return_correctly_file_ actual = utils.get_all_file('.', required_extension='.zip') assert actual == ['./test_zip1.zip', './test_zip2.zip'] + + #### + # get_lastest_build_file + ### + @patch("nak.utils.Path") + def test_get_lastest_build_file_with_no_zip_file_should_return_none(self, mock_path): + mock_dir = MagicMock() + mock_path.return_value = mock_dir + + mock_dir.iterdir.return_value = [ + MagicMock( + is_file=MagicMock(return_value=True), suffix='.txt', as_posix=MagicMock(return_value='file1.txt')), + MagicMock( + is_file=MagicMock(return_value=True), suffix='.txt', as_posix=MagicMock(return_value='file2.txt')) + ] + + result = utils.get_lastest_build_file() + assert result is None + + @patch("nak.utils.Path") + def test_get_lastest_build_file_with_have_zip_fiile_should_return_latest_file(self, mock_path): + mock_dir = MagicMock() + mock_path.return_value = mock_dir + + mock_file1 = MagicMock( + is_file=MagicMock(return_value=True), suffix='.zip', as_posix=MagicMock(return_value='file1.txt')) + mock_file1.stat.return_value.st_ctime = 1000 + mock_file1.as_posix.return_value = 'file1.zip' + + mock_file2 = MagicMock( + is_file=MagicMock(return_value=True), suffix='.zip', as_posix=MagicMock(return_value='file2.txt')) + mock_file2.stat.return_value.st_ctime = 2000 + mock_file2.as_posix.return_value = 'file2.zip' + + mock_dir.iterdir.return_value = [mock_file1, mock_file2] + + result = utils.get_lastest_build_file() + assert result == 'file2.zip' diff --git a/tox.ini b/tox.ini index 0992132..47655b9 100644 --- a/tox.ini +++ b/tox.ini @@ -1,20 +1,20 @@ [tox] -envlist = py36, py37, py38, py39 +envlist = py37, py38, py39, py310 skip_missing_interpreters = true [gh-actions] python = - 3.6: py36 3.7: py37 3.8: py38 3.9: py39 + 3.10: py310 [testenv] allowlist_externals = /usr/bin/test deps = - coverage - codecov + pytest + pytest-cov flake8 - nose + codecov -commands = nosetests -v --with-coverage --cover-inclusive --cover-xml --cover-package=nak +commands = pytest --cov=nak --cov-report xml