Skip to content

Commit c259f41

Browse files
authored
Bug/692 create first release without fail (#693)
* Modify cookiecutter command to reduce errors experienced by users * Fixed bug which prevent new repos from creating releases
1 parent e444c13 commit c259f41

File tree

8 files changed

+103
-13
lines changed

8 files changed

+103
-13
lines changed

doc/changes/unreleased.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@
22

33
## Summary
44

5+
## Bug
6+
7+
* #692: Fixed bug where creating first release failed due to no previous tags
8+
59
## Documentation
610

711
* #585: Added instructions how to ignore sonar issues to the User Guide
12+
* #630: Updated cookiecutter command to reduce errors experienced by users
813

914
## Refactoring
1015

doc/user_guide/getting_started.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,18 @@ Use the following command to create a new project:
3131
.. code-block:: shell
3232
3333
cookiecutter https://github.com/exasol/python-toolbox.git \
34-
--checkout <latest-tag> --directory project-template
34+
--checkout <latest-tag> --directory project-template \
35+
--overwrite-if-exists
3536
3637
.. note::
3738

3839
Without option :code:`--checkout` cookiecutter will use the main branch of the PTB. In order
3940
to get reliable and reproducible results, we recommend using the tag of PTB's latest released
4041
version instead.
4142

43+
Without option :code:`--overwrite-if-exists`, the cookiecutter command will fail if
44+
you already have files in that directory, like from ``git init``.
45+
4246
**2. Follow the interactive project setup prompt**
4347

4448
**3. Bootstrap the development environment**

exasol/toolbox/util/dependencies/shared_models.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
from contextlib import contextmanager
66
from dataclasses import dataclass
77
from pathlib import Path
8+
from subprocess import (
9+
CalledProcessError, # nosec: B404 - risk of using subprocess is acceptable
10+
)
811
from typing import (
912
Annotated,
1013
Final,
@@ -25,6 +28,13 @@
2528
VERSION_TYPE = Annotated[str, AfterValidator(lambda v: Version(v))]
2629

2730

31+
class LatestTagNotFoundError(Exception):
32+
"""Raised when the requested latest tag cannot be found in the repository."""
33+
34+
def __init__(self, *args):
35+
super().__init__("The latest git tag was not found in the repository.", *args)
36+
37+
2838
def normalize_package_name(package_name: str) -> NormalizedPackageStr:
2939
return NormalizedPackageStr(package_name.lower().replace("_", "-"))
3040

@@ -64,7 +74,11 @@ def files(self) -> tuple[str, ...]:
6474
@contextmanager
6575
def poetry_files_from_latest_tag(root_path: Path) -> Generator[Path]:
6676
"""Context manager to set up a temporary directory with poetry files from the latest tag"""
67-
latest_tag = Git.get_latest_tag()
77+
try:
78+
latest_tag = Git.get_latest_tag()
79+
except CalledProcessError:
80+
raise LatestTagNotFoundError()
81+
6882
path = root_path.relative_to(Git.toplevel())
6983
with tempfile.TemporaryDirectory() as tmpdir_str:
7084
tmp_dir = Path(tmpdir_str)

exasol/toolbox/util/release/changelog.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
from collections import OrderedDict
34
from datetime import datetime
45
from inspect import cleandoc
56
from pathlib import Path
@@ -8,6 +9,7 @@
89
get_dependencies,
910
get_dependencies_from_latest_tag,
1011
)
12+
from exasol.toolbox.util.dependencies.shared_models import LatestTagNotFoundError
1113
from exasol.toolbox.util.dependencies.track_changes import DependencyChanges
1214
from exasol.toolbox.util.version import Version
1315

@@ -77,9 +79,15 @@ def _describe_dependency_changes(self) -> str:
7779
Describe the dependency changes between the latest tag and the current version
7880
for use in the versioned changes file.
7981
"""
80-
previous_dependencies_in_groups = get_dependencies_from_latest_tag(
81-
root_path=self.root_path
82-
)
82+
try:
83+
previous_dependencies_in_groups = get_dependencies_from_latest_tag(
84+
root_path=self.root_path
85+
)
86+
except LatestTagNotFoundError:
87+
# In new projects, there is not a pre-existing tag, and all dependencies
88+
# are considered new.
89+
previous_dependencies_in_groups = OrderedDict()
90+
8391
current_dependencies_in_groups = get_dependencies(
8492
working_directory=self.root_path
8593
)

test/integration/project-template/conftest.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,14 @@ def new_project(cwd):
1616
project_name = "project"
1717
repo_name = "repo"
1818
package_name = "package"
19+
project_path = cwd / repo_name
20+
21+
subprocess.run(["mkdir", "-p", project_path])
22+
subprocess.run(["git", "init"], cwd=project_path)
1923

2024
subprocess.run(
21-
["cookiecutter", PROJECT_CONFIG.root_path / "project-template", "-o", cwd, "--no-input",
25+
["cookiecutter", PROJECT_CONFIG.root_path / "project-template",
26+
"-o", cwd, "--no-input", "--overwrite-if-exists",
2227
f"project_name={project_name}", f"repo_name={repo_name}",
2328
f"package_name={package_name}",
2429
], capture_output=True, check=True)

test/integration/project-template/nox_test.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,9 @@ def test_package_check(self, poetry_path, run_command):
5858
output = run_command(package_check)
5959

6060
assert output.returncode == 0
61+
62+
def test_release_prepare(self, poetry_path, run_command):
63+
release_prepare = self._command(poetry_path, task="release:prepare", add_ons=["--type", "minor", "--no-pr", "--no-branch", "--no-add"])
64+
output = run_command(release_prepare)
65+
66+
assert output.returncode == 0

test/unit/util/dependencies/shared_models_test.py

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
from subprocess import CalledProcessError
2+
from unittest import mock
3+
14
import pytest
25
from packaging.version import Version
36
from pydantic import BaseModel
47
from pydantic_core._pydantic_core import ValidationError
58

69
from exasol.toolbox.util.dependencies.shared_models import (
710
VERSION_TYPE,
11+
LatestTagNotFoundError,
812
Package,
913
PoetryFiles,
1014
poetry_files_from_latest_tag,
@@ -56,11 +60,28 @@ def test_coordinates():
5660
assert dep.coordinates == "numpy:0.1.0"
5761

5862

59-
def test_poetry_files_from_latest_tag():
60-
latest_tag = Git.get_latest_tag()
61-
with poetry_files_from_latest_tag(root_path=PROJECT_CONFIG.root_path) as tmp_dir:
62-
for file in PoetryFiles().files:
63-
assert (tmp_dir / file).is_file()
63+
class TestPoetryFilesFromLatestTag:
64+
@staticmethod
65+
def test_works_as_expected():
66+
latest_tag = Git.get_latest_tag()
67+
with poetry_files_from_latest_tag(
68+
root_path=PROJECT_CONFIG.root_path
69+
) as tmp_dir:
70+
for file in PoetryFiles().files:
71+
assert (tmp_dir / file).is_file()
6472

65-
contents = (tmp_dir / PoetryFiles.pyproject_toml).read_text()
66-
assert f'version = "{latest_tag}"' in contents
73+
contents = (tmp_dir / PoetryFiles.pyproject_toml).read_text()
74+
assert f'version = "{latest_tag}"' in contents
75+
76+
@staticmethod
77+
def test_raises_exception_when_latest_tag_not_found():
78+
with pytest.raises(LatestTagNotFoundError):
79+
with mock.patch.object(
80+
Git,
81+
"get_latest_tag",
82+
side_effect=CalledProcessError(
83+
cmd="Mocked subprocess error", returncode=1
84+
),
85+
):
86+
with poetry_files_from_latest_tag(root_path=PROJECT_CONFIG.root_path):
87+
pass

test/unit/util/release/changelog_test.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import pytest
66

7+
from exasol.toolbox.util.dependencies.shared_models import LatestTagNotFoundError
78
from exasol.toolbox.util.release.changelog import (
89
UNRELEASED_INITIAL_CONTENT,
910
Changelogs,
@@ -84,6 +85,18 @@ def mock_dependencies(dependencies, previous_dependencies):
8485
yield
8586

8687

88+
@pytest.fixture(scope="function")
89+
def mock_new_dependencies(dependencies):
90+
mock_latest_tag_not_found_error = mock.Mock(side_effect=LatestTagNotFoundError)
91+
92+
with mock.patch.multiple(
93+
"exasol.toolbox.util.release.changelog",
94+
get_dependencies_from_latest_tag=mock_latest_tag_not_found_error,
95+
get_dependencies=lambda working_directory: dependencies,
96+
):
97+
yield
98+
99+
87100
@pytest.fixture(scope="function")
88101
def mock_no_dependencies():
89102
with mock.patch.multiple(
@@ -145,6 +158,20 @@ def test_describe_dependency_changes(changelogs, mock_dependencies):
145158
"* Added dependency `package2:0.2.0`\n"
146159
)
147160

161+
@staticmethod
162+
def test_describe_dependency_changes_without_latest_version(
163+
changelogs, mock_new_dependencies
164+
):
165+
result = changelogs._describe_dependency_changes()
166+
assert result == (
167+
"\n"
168+
"### `main`\n"
169+
"* Added dependency `package1:0.1.0`\n"
170+
"\n"
171+
"### `dev`\n"
172+
"* Added dependency `package2:0.2.0`\n"
173+
)
174+
148175
@staticmethod
149176
@pytest.mark.parametrize(
150177
"groups,expected",

0 commit comments

Comments
 (0)