diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index df2ab879..147b270b 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -44,42 +44,16 @@ jobs: debhelper fakeroot - - name: Run black - run: poetry run black src tests --check - - - name: Run isort - run: poetry run isort src tests --check - - - name: Run flake8 - run: poetry run flake8 src tests - - - name: Run mypy - run: poetry run mypy --show-error-codes src - - - name: Run pytest - run: poetry run pytest --cov=src --cov-report=term-missing tests - - - name: Generate coverage report - shell: bash - run: poetry run coverage xml - - - uses: codecov/codecov-action@v2 - with: - files: ./coverage.xml - - - name: Build single binary application + - name: Build distributions run: | - poetry install --extras pyinstaller - poetry run poetry-dynamic-versioning - poetry run pyinstaller --onefile src/ops2deb/__main__.py --name ops2deb -s - dist/ops2deb version - mv dist/ops2deb ops2deb_linux_amd64 + rm -rf dist/ + poetry run poetry build - - name: Upload build artifact + - name: Upload build artifacts uses: actions/upload-artifact@v2 with: - name: ops2deb_linux_amd64 - path: ops2deb_linux_amd64 + name: distributions + path: dist/* retention-days: 2 publish_release: @@ -118,37 +92,11 @@ jobs: publish_pypi: runs-on: ubuntu-18.04 needs: [test_and_build] - if: startsWith(github.ref, 'refs/tags') steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Install Python - uses: actions/setup-python@v2 - with: - python-version: "3.10" - - - name: Load Cached Poetry - uses: actions/cache@v2 - with: - path: ~/.local - key: poetry-dependencies-v2 - - - name: Install Poetry - uses: snok/install-poetry@v1 - - - name: Get Release Version - run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV - - - name: Build Distribution - run: | - poetry version "$RELEASE_VERSION" - poetry build - - - name: Publish distribution 📦 to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + - name: Download build artifacts + uses: actions/download-artifact@v2 with: - password: ${{ secrets.PYPI_API_TOKEN }} + name: distributions publish_images: runs-on: ubuntu-latest diff --git a/src/ops2deb/generator.py b/src/ops2deb/generator.py index 49971916..6f34d43f 100644 --- a/src/ops2deb/generator.py +++ b/src/ops2deb/generator.py @@ -28,12 +28,12 @@ def __init__( self.debian_version = f"{epoch}{blueprint.version}-{blueprint.revision}~ops2deb" self.directory_name = f"{blueprint.name}_{blueprint.version}_{blueprint.arch}" self.package_directory = (output_directory / self.directory_name).absolute() + self.configuration_directory = configuration_directory.absolute() self.debian_directory = self.package_directory / "debian" self.source_directory = self.package_directory / "src" self.fetch_directory = self.package_directory / "fetched" self.blueprint = blueprint self.remote_file = self.blueprint.render_fetch() - self.configuration_directory = configuration_directory def _render_template(self, template_name: str) -> None: template = environment.get_template(f"{template_name}") @@ -47,7 +47,7 @@ def _init(self) -> None: shutil.rmtree(self.fetch_directory, ignore_errors=True) shutil.rmtree(self.source_directory, ignore_errors=True) self.source_directory.mkdir(parents=True) - for path in ["usr/bin", "usr/share", "usr/lib"]: + for path in ["usr/bin", "usr/share", "usr/lib", "etc"]: (self.source_directory / path).mkdir(parents=True) def _populate_with_fetch_result(self, fetch_result: FetchResult) -> None: @@ -152,11 +152,11 @@ def generate(self, fetch_results: dict[str, FetchResult]) -> None: ]: self._render_template(template) - # if blueprint has no fetch instruction, we stay in the directory from which - # ops2deb was called, otherwise we run install and script from the fetch directory - cwd = self.fetch_directory if self.blueprint.fetch else Path(".") - - with working_directory(cwd): + # if blueprint has no fetch instruction, we walk in the directory where ops2deb + # config file is, otherwise we run install and script from the fetch directory + with working_directory( + self.fetch_directory if self.blueprint.fetch else self.configuration_directory + ): # copy files / create here documents self._install_files() # run blueprint script diff --git a/tests/test_generator.py b/tests/test_generator.py index 919cfbd2..848e0359 100644 --- a/tests/test_generator.py +++ b/tests/test_generator.py @@ -202,3 +202,12 @@ def test__install_files_should_render_cwd_and_debian_variable_in_source_destinat blueprint = blueprint_factory(install=["{{cwd}}/test:{{debian}}/test"]) SourcePackage(blueprint, tmp_path, tmp_path)._install_files() assert (tmp_path / "great-app_1.0.0_amd64/debian/test").is_file() + + +def test__init__should_use_absolute_paths(blueprint_factory, tmp_path): + blueprint = blueprint_factory() + package = SourcePackage(blueprint, tmp_path, tmp_path) + assert package.package_directory.is_absolute() is True + assert package.debian_directory.is_absolute() is True + assert package.source_directory.is_absolute() is True + assert package.configuration_directory.is_absolute() is True diff --git a/tests/test_ops2deb.py b/tests/test_ops2deb.py index c803219d..44947e86 100644 --- a/tests/test_ops2deb.py +++ b/tests/test_ops2deb.py @@ -137,18 +137,6 @@ """ -mock_configuration_single_blueprint_without_fetch = """\ -name: cool-app -version: 1.0.0 -arch: all -summary: Cool package -description: | - A detailed description of the cool package -script: - - install -m 755 cool-app.sh {{src}}/usr/bin/cool-app -""" - - mock_configuration_single_blueprint_with_fetch = """\ name: great-app version: 1.0.0 @@ -299,20 +287,27 @@ def test_ops2deb_generate_should_not_generate_packages_already_published_in_debi assert result.exit_code == 0 -def test_ops2deb_generate_should_run_script_from_current_directory_when_blueprint_has_not_fetch_instruction( # noqa: E501 - tmp_path, tmp_working_directory, call_ops2deb +def test_ops2deb_generate_should_run_script_from_config_directory_when_blueprint_has_not_fetch_instruction( # noqa: E501 + tmp_path, call_ops2deb ): + configuration_without_fetch = """\ + name: cool-app + version: 1.0.0 + arch: all + summary: Cool package + description: | + A detailed description of the cool package + script: + - install -m 755 cool-app.sh {{src}}/usr/bin/cool-app + """ (tmp_path / "cool-app.sh").touch() - result = call_ops2deb( - "generate", configuration=mock_configuration_single_blueprint_without_fetch - ) + result = call_ops2deb("generate", configuration=configuration_without_fetch) assert result.exit_code == 0 assert (tmp_path / "cool-app_1.0.0_all/src/usr/bin/cool-app").is_file() def test_ops2deb_generate_should_honor_only_argument(tmp_path, call_ops2deb): result = call_ops2deb("generate", "--only", "great-app") - print(result.stdout) assert list(tmp_path.glob("*_all")) == [tmp_path / "great-app_1.0.0_all"] assert result.exit_code == 0 @@ -323,14 +318,31 @@ def test_ops2deb_generate_should_not_crash_when_archive_contains_a_dangling_syml result = call_ops2deb( "generate", configuration=mock_configuration_with_dangling_symlink_in_archive ) - print(result.stdout) + assert result.exit_code == 0 + + +def test_ops2deb_generate_should_set_cwd_variable_to_config_directory_when_blueprint_has_a_fetch_and_path_to_config_is_relative( # noqa: E501 + tmp_path, call_ops2deb, tmp_working_directory +): + configuration = """\ + name: great-app + version: 1.0.0 + summary: Great package + fetch: + url: http://testserver/{{version}}/great-app.tar.gz + sha256: f1be6dd36b503641d633765655e81cdae1ff8f7f73a2582b7468adceb5e212a9 + script: + - mv great-app {{src}}/usr/bin/great-app + - cp {{cwd}}/test.conf {{src}}/etc/test.conf + """ + (tmp_path / "test.conf").touch() + result = call_ops2deb("generate", "-c", "ops2deb.yml", configuration=configuration) assert result.exit_code == 0 def test_ops2deb_build_should_succeed_with_valid_configuration(tmp_path, call_ops2deb): call_ops2deb("generate") result = call_ops2deb("build") - print(result.stdout) assert result.exit_code == 0 assert (tmp_path / "great-app_1.0.0-2~ops2deb_all.deb").is_file() @@ -352,7 +364,6 @@ def test_ops2deb_default_should_build_and_generate_packages_when_configuration_i def test_ops2deb_update_should_succeed_with_valid_configuration(tmp_path, call_ops2deb): result = call_ops2deb("update") - print(result.stdout) configuration = parse(tmp_path / "ops2deb.yml") assert "great-app can be bumped from 1.0.0 to 1.1.1" in result.stdout assert result.exit_code == 0 diff --git a/tests/test_utils.py b/tests/test_utils.py index 9dbbf59d..61c2953e 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,7 +1,10 @@ +import os +from pathlib import Path + import pytest from ops2deb.exceptions import Ops2debError -from ops2deb.utils import separate_results_from_errors +from ops2deb.utils import separate_results_from_errors, working_directory def test_separate_results_from_errors_should_separate_results_from_ops2deb_exceptions(): @@ -18,3 +21,19 @@ def test_separate_results_from_errors_should_raise_when_exception_is_not_an_ops2 test = {0: error} with pytest.raises(RuntimeError): separate_results_from_errors(test) + + +def test_working_directory__should_set_current_working_directory_within_context(tmp_path): + origin = Path().absolute() + with working_directory(tmp_path): + assert Path(os.getcwd()) != origin + assert Path(os.getcwd()) == tmp_path + + +def test_working_directory__should_restore_current_directory_when_context_is_left( + tmp_path, +): + origin = Path().absolute() + with working_directory(tmp_path): + pass + assert Path(os.getcwd()) == origin