Skip to content

Commit d546d5d

Browse files
authored
Add cli next-version (#25)
* add find_latest_version * add cli * update changelog * fix increase minor in the case when patch contains characters * fix typo in test file name
1 parent 60a5bc0 commit d546d5d

7 files changed

Lines changed: 113 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
Anything MAY change at any time. The public API SHOULD NOT be considered stable.").
1111
While in this phase, we will denote breaking changes with a minor increase.
1212

13+
## 0.4.1
14+
15+
### Added
16+
17+
* Introduce `dac next-version` command, that allows to find the next minor release for a given python package and (optionally) a given major version.
18+
1319
## 0.4.0
1420

1521
### Changed

src/dac/_cli.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import sys
33
from importlib.metadata import requires, version
44
from pathlib import Path
5+
from typing import Optional
56

67
import typer
78
from rich.console import Console
@@ -11,6 +12,7 @@
1112
from dac._input.config import PackConfig
1213
from dac._input.pyproject import PyProjectConfig
1314
from dac._packing import pack as py_api_pack
15+
from dac._version_management import find_latest_version, increase_minor
1416

1517
app = typer.Typer()
1618
console = Console()
@@ -69,6 +71,30 @@ def pack(
6971
)
7072

7173

74+
@app.command()
75+
def next_version(
76+
pkg_name: str = typer.Option(
77+
...,
78+
help="Name of the python package",
79+
),
80+
major: Optional[int] = typer.Option(
81+
None,
82+
"--major",
83+
help="Major of the version that should be increased. "
84+
"If not specified just take it from the latest version of the package.",
85+
),
86+
):
87+
"""
88+
Print the next version of the python package.
89+
90+
It assumes that semantic versioning (https://semver.org) is used,
91+
and that the next version corresponds to a minor upgrade.
92+
"""
93+
latest_version = find_latest_version(pkg_name=pkg_name, major=major)
94+
next_version = increase_minor(version=latest_version)
95+
console.print(next_version)
96+
97+
7298
@app.command()
7399
def info():
74100
"""

src/dac/_version_management.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import re
2+
import subprocess
3+
from typing import Optional
4+
5+
6+
def find_latest_version(pkg_name: str, major: Optional[int] = None) -> str:
7+
output = subprocess.check_output(
8+
[
9+
"pip",
10+
"install",
11+
"--no-deps",
12+
"--ignore-installed",
13+
"--no-cache-dir",
14+
"--dry-run",
15+
f"{pkg_name}{f'=={major}.*' if major is not None else ''}",
16+
],
17+
stderr=subprocess.DEVNULL,
18+
)
19+
last_line = output.decode("utf-8").splitlines()[-1]
20+
regex_rule = f"{pkg_name.replace('_', '-')}-{major if major is not None else ''}.[^ ]+"
21+
match = re.search(regex_rule, last_line)
22+
assert match is not None
23+
return match[0][len(f"{pkg_name}-") :]
24+
25+
26+
def increase_minor(version: str) -> str:
27+
major, minor, patch = version.split(".")
28+
assert major.isdigit() and minor.isdigit()
29+
return f"{major}.{int(minor) + 1}.0"

test/cli_utilities.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@
88
from typing import Optional
99

1010
from click.testing import Result
11-
from typer.testing import CliRunner
12-
1311
from dac._cli import app
1412
from dac._input.config import PackConfig
13+
from typer.testing import CliRunner
1514

1615
runner = CliRunner()
1716

@@ -66,5 +65,16 @@ def invoke_dac_pack_from_config(config: PackConfig) -> Result:
6665
)
6766

6867

68+
def invoke_dac_next_version(
69+
pkg_name: str,
70+
major: Optional[int] = None,
71+
) -> Result:
72+
major_option = [] if major is None else ["--major", str(major)]
73+
return runner.invoke(
74+
app,
75+
["next-version", "--pkg-name", pkg_name] + major_option,
76+
)
77+
78+
6979
def invoke_dac_info() -> Result:
7080
return runner.invoke(app, ["info"])
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from test.cli_utilities import invoke_dac_next_version
2+
3+
import pytest
4+
from dac._version_management import find_latest_version
5+
6+
7+
def test_if_find_latest_version_is_called_then_return_latest_version():
8+
assert "0.5" == find_latest_version(pkg_name="rainbow-server")
9+
10+
11+
def test_if_find_latest_version_is_called_with_major_constraint_then_return_latest_major_version():
12+
assert "0.25.3" == find_latest_version(pkg_name="pandas", major=0)
13+
14+
15+
def test_if_pkg_does_not_exist_then_find_package_raises_exception():
16+
with pytest.raises(Exception):
17+
find_latest_version(pkg_name="non-existing-package")
18+
19+
20+
def test_if_next_version_without_major_spec_then_return_latest_version_with_minor_upgrade():
21+
result = invoke_dac_next_version(pkg_name="rainbow-saddle")
22+
assert result.stdout == "0.5.0\n"
23+
24+
25+
def test_if_next_version_with_major_spec_then_return_minor_upgrade_for_that_major():
26+
result = invoke_dac_next_version(pkg_name="pandas", major=0)
27+
assert result.stdout == "0.26.0\n"

test/unit_test/_version_management/__init__.py

Whitespace-only changes.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import pytest
2+
from dac._version_management import increase_minor
3+
4+
5+
def test_increase_minor_then_return_version_with_increased_minor():
6+
assert "0.6.0" == increase_minor(version="0.5.0")
7+
assert "0.6.0" == increase_minor(version="0.5.1rc0")
8+
9+
10+
@pytest.mark.parametrize("version", ["0", "0.5", "guess.what.now", "guess.0.now"])
11+
def test_if_invalid_version_then_increase_minor_raises_exception(version):
12+
with pytest.raises(Exception):
13+
increase_minor(version=version)

0 commit comments

Comments
 (0)