diff --git a/pontos/release/parser.py b/pontos/release/parser.py index 9c4a8f858..d7d5a253d 100644 --- a/pontos/release/parser.py +++ b/pontos/release/parser.py @@ -23,7 +23,11 @@ from typing import Callable, Tuple, Type from pontos.release.helper import ReleaseType -from pontos.version.version import parse_version +from pontos.version.schemes import ( + VERSIONING_SCHEMES, + VersioningScheme, + versioning_scheme_argument_type, +) from .release import release from .sign import sign @@ -81,6 +85,14 @@ def parse_args(args) -> Tuple[str, str, Namespace]: release_parser = subparsers.add_parser("release") release_parser.set_defaults(func=release) + release_parser.add_argument( + "--versioning-scheme", + help="Versioning scheme to use for parsing and handling version " + f"information. Choices are {', '.join(VERSIONING_SCHEMES.keys())}. " + "Default: %(default)s", + default="pep440", + type=versioning_scheme_argument_type, + ) release_parser.add_argument( "--release-type", help="Select the release type for calculating the release version. " @@ -90,20 +102,18 @@ def parse_args(args) -> Tuple[str, str, Namespace]: release_parser.add_argument( "--release-version", help=( - "Will release changelog as version. Must be PEP 440 compliant. " - "default: lookup version in project definition." + "Will release changelog as version. " + "Default: lookup version in project definition." ), - type=parse_version, action=ReleaseVersionAction, ) release_parser.add_argument( "--next-version", help=( - "Sets the next PEP 440 compliant version in project definition " - "after the release. default: set to next dev version" + "Sets the next version in project definition " + "after the release. Default: set to next dev version" ), - type=parse_version, ) release_parser.add_argument( @@ -149,10 +159,17 @@ def parse_args(args) -> Tuple[str, str, Namespace]: default=DEFAULT_SIGNING_KEY, help="The key to sign zip, tarballs of a release. Default %(default)s.", ) + sign_parser.add_argument( + "--versioning-scheme", + help="Versioning scheme to use for parsing and handling version " + f"information. Choices are {', '.join(VERSIONING_SCHEMES.keys())}. " + "Default: %(default)s", + default="pep440", + type=versioning_scheme_argument_type, + ) sign_parser.add_argument( "--release-version", help="Will release changelog as version. Must be PEP 440 compliant.", - type=parse_version, ) sign_parser.add_argument( "--git-tag-prefix", @@ -181,13 +198,15 @@ def parse_args(args) -> Tuple[str, str, Namespace]: ) parsed_args = parser.parse_args(args) + scheme: VersioningScheme = getattr(parsed_args, "versioning_scheme", None) + if parsed_args.func in (release,): # check for release-type - if not getattr(parsed_args, "release_type"): + if not getattr(parsed_args, "release_type", None): parser.error("--release-type is required.") if ( - getattr(parsed_args, "release_version") + getattr(parsed_args, "release_version", None) and parsed_args.release_type != ReleaseType.VERSION ): parser.error( @@ -196,13 +215,21 @@ def parse_args(args) -> Tuple[str, str, Namespace]: ) if parsed_args.release_type == ReleaseType.VERSION and not getattr( - parsed_args, "release_version" + parsed_args, "release_version", None ): parser.error( f"--release-type {ReleaseType.VERSION.value} requires to set " "--release-version" ) + next_version = getattr(parsed_args, "next_version", None) + if next_version: + parsed_args.next_version = scheme.parse_version(next_version) + + release_version = getattr(parsed_args, "release_version", None) + if release_version: + parsed_args.release_version = scheme.parse_version(release_version) + token = os.environ.get("GITHUB_TOKEN") user = os.environ.get("GITHUB_USER") return user, token, parsed_args diff --git a/pontos/release/release.py b/pontos/release/release.py index 9c26416b9..aa0982cfe 100644 --- a/pontos/release/release.py +++ b/pontos/release/release.py @@ -29,11 +29,10 @@ from pontos.git import Git from pontos.github.api import GitHubAsyncRESTApi from pontos.terminal import Terminal -from pontos.version.calculator import VersionCalculator -from pontos.version.errors import VersionError +from pontos.version import Version, VersionCalculator, VersionError from pontos.version.helper import get_last_release_version from pontos.version.project import Project -from pontos.version.version import Version +from pontos.version.schemes import VersioningScheme from .helper import ReleaseType, find_signing_key, get_git_repository_name @@ -68,11 +67,11 @@ def __init__(self, terminal: Terminal) -> None: def _get_release_version( self, project: Project, + calculator: VersionCalculator, release_type: ReleaseType, release_version: Optional[Version], ) -> Version: current_version = project.get_current_version() - calculator = VersionCalculator() if release_type == ReleaseType.CALENDAR: return calculator.next_calendar_version(current_version) @@ -142,6 +141,7 @@ async def run( token: str, space: str, project: Optional[str], + versioning_scheme: VersioningScheme, release_type: ReleaseType, release_version: Optional[Version], next_version: Optional[str], @@ -160,6 +160,8 @@ async def run( links in the changelog. project: Name of the project to release. If not set it will be gathered via the git remote url. + versioning_scheme: The versioning scheme to use for version parsing + and calculation release_type: Type of the release to prepare. Defines the release version. PATCH increments the bugfix version. CALENDAR creates a new CalVer release version. VERSION uses the provided @@ -188,14 +190,16 @@ async def run( self.space = space try: - project = Project.gather_project() + project = Project(versioning_scheme) except PontosError as e: self.terminal.error(f"Unable to determine project settings. {e}") return ReleaseReturnValue.PROJECT_SETTINGS_NOT_FOUND + calculator = versioning_scheme.calculator() + try: release_version = self._get_release_version( - project, release_type, release_version + project, calculator, release_type, release_version ) except VersionError as e: self.terminal.error(f"Unable to determine release version. {e}") @@ -266,7 +270,7 @@ async def run( return ReleaseReturnValue.CREATE_RELEASE_ERROR if not next_version: - next_version = VersionCalculator().next_dev_version(release_version) + next_version = calculator.next_dev_version(release_version) try: updated = project.update_version(next_version) @@ -318,6 +322,7 @@ def release( token=token, space=args.space, project=args.project, + versioning_scheme=args.versioning_scheme, release_type=args.release_type, release_version=args.release_version, next_version=args.next_version, diff --git a/pontos/release/sign.py b/pontos/release/sign.py index a468f772e..5aa1c2a99 100644 --- a/pontos/release/sign.py +++ b/pontos/release/sign.py @@ -33,8 +33,9 @@ from pontos.helper import AsyncDownloadProgressIterable from pontos.terminal import Terminal from pontos.terminal.rich import RichTerminal +from pontos.version import Version from pontos.version.helper import get_last_release_version -from pontos.version.version import Version +from pontos.version.schemes import VersioningScheme from .helper import get_git_repository_name @@ -182,13 +183,33 @@ async def run( *, token: str, dry_run: Optional[bool] = False, - project: Optional[str], space: str, + project: Optional[str], + versioning_scheme: VersioningScheme, git_tag_prefix: Optional[str], release_version: Optional[Version], signing_key: str, passphrase: str, ) -> SignReturnValue: + """ + Sign a release + + Args: + token: A token for creating a release on GitHub + dry_run: True to not upload the signature files + space: GitHub username or organization. Required for generating + links in the changelog. + project: Name of the project to release. If not set it will be + gathered via the git remote url. + versioning_scheme: The versioning scheme to use for version parsing + and calculation + git_tag_prefix: An optional prefix to use for handling a git tag + from the release version. + release_version: Optional release version to use. If not set the + current version will be determined from the project. + signing_key: A GPG key ID to use for creating signatures. + passphrase: Passphrase for the signing key + """ if not token and not dry_run: # dry run doesn't upload assets. therefore a token MAY NOT be # required # for public repositories. @@ -210,7 +231,10 @@ async def run( release_version = ( release_version if release_version is not None - else get_last_release_version(git_tag_prefix) + else get_last_release_version( + versioning_scheme.parse_version, + git_tag_prefix=git_tag_prefix, + ) ) except PontosError as e: self.terminal.error(f"Could not determine release version. {e}") @@ -342,6 +366,7 @@ def sign( dry_run=args.dry_run, project=args.project, space=args.space, + versioning_scheme=args.versioning_scheme, git_tag_prefix=args.git_tag_prefix, release_version=args.release_version, signing_key=args.signing_key,