Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

feat: implement basic pep440 support #1020

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: master
Choose a base branch
Loading
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions 1 action.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ ARGS+=("$(eval_boolean_action_input "push" "$INPUT_PUSH" "--push" "--no-push")")
ARGS+=("$(eval_boolean_action_input "changelog" "$INPUT_CHANGELOG" "--changelog" "--no-changelog")") || exit 1
ARGS+=("$(eval_boolean_action_input "vcs_release" "$INPUT_VCS_RELEASE" "--vcs-release" "--no-vcs-release")") || exit 1
ARGS+=("$(eval_boolean_action_input "build" "$INPUT_BUILD" "" "--skip-build")") || exit 1
ARGS+=("$(eval_boolean_action_input "pep440" "$INPUT_PEP440" "--version-compat=pep440" "--version-compat=semver")") || exit 1

# Handle --patch, --minor, --major
# https://stackoverflow.com/a/47541882
Expand Down
6 changes: 6 additions & 0 deletions 6 action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ inputs:
description: |
Build metadata to append to the new version

pep440:
type: string
required: false
description: |
Whether or not to use PEP440 versioning.

outputs:
is_prerelease:
description: |
Expand Down
2 changes: 1 addition & 1 deletion 2 pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ commands =
[testenv:ruff]
deps = .[dev]
commands =
ruff check . --statistics --output-format=text
ruff check . --statistics --output-format=full
"""

[tool.mypy]
Expand Down
19 changes: 15 additions & 4 deletions 19 src/semantic_release/cli/commands/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,17 @@ def is_forced_prerelease(
)


def last_released(repo_dir: Path, tag_format: str) -> tuple[Tag, Version] | None:
def last_released(repo_dir: Path, tag_format: str, version_compat: str) -> tuple[Tag, Version] | None:
with Repo(str(repo_dir)) as git_repo:
ts_and_vs = tags_and_versions(
git_repo.tags, VersionTranslator(tag_format=tag_format)
git_repo.tags, VersionTranslator(tag_format=tag_format, version_compat=version_compat)
)

return ts_and_vs[0] if ts_and_vs else None


def version_from_forced_level(
repo_dir: Path, forced_level_bump: LevelBump, translator: VersionTranslator
repo_dir: Path, forced_level_bump: LevelBump, translator: VersionTranslator,
) -> Version:
with Repo(str(repo_dir)) as git_repo:
ts_and_vs = tags_and_versions(git_repo.tags, translator)
Expand Down Expand Up @@ -323,6 +323,12 @@ def build_distributions(
default=None,
help="Force the next version to use this prerelease token, if it is a prerelease",
)
@click.option(
"--version-compat",
"version_compat",
default=None,
help="Choose a versioning scheme to use",
)
@click.option(
"--major",
"force_level",
Expand Down Expand Up @@ -399,6 +405,7 @@ def version( # noqa: C901
print_last_released_tag: bool,
as_prerelease: bool,
prerelease_token: str | None,
version_compat: str,
commit_changes: bool,
create_tag: bool,
update_changelog: bool,
Expand Down Expand Up @@ -436,7 +443,7 @@ def version( # noqa: C901
if print_last_released or print_last_released_tag:
# TODO: get tag format a better way
if not (
last_release := last_released(config.repo_dir, tag_format=config.tag_format)
last_release := last_released(config.repo_dir, tag_format=config.tag_format, version_compat=version_compat)
):
log.warning("No release tags found.")
return
Expand Down Expand Up @@ -470,6 +477,10 @@ def version( # noqa: C901
log.info("Forcing use of %s as the prerelease token", prerelease_token)
translator.prerelease_token = prerelease_token

if version_compat:
translator.version_compat = version_compat
log.info("Using %s as the version compatibility scheme", translator.version_compat)

# Only push if we're committing changes
if push_changes and not commit_changes and not create_tag:
log.info("changes will not be pushed because --no-commit disables pushing")
Expand Down
24 changes: 19 additions & 5 deletions 24 src/semantic_release/cli/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,12 @@
ScipyCommitParser,
TagCommitParser,
)
from semantic_release.const import COMMIT_MESSAGE, DEFAULT_COMMIT_AUTHOR, SEMVER_REGEX
from semantic_release.const import (
COMMIT_MESSAGE,
DEFAULT_COMMIT_AUTHOR,
PEP440_REGEX,
SEMVER_REGEX,
)
from semantic_release.errors import (
DetachedHeadGitError,
InvalidConfiguration,
Expand Down Expand Up @@ -239,6 +244,7 @@ class BranchConfig(BaseModel):
match: str = "(main|master)"
prerelease_token: str = "rc" # noqa: S105
prerelease: bool = False
version_compat: str = "semver"

@field_validator("match", mode="after")
@classmethod
Expand Down Expand Up @@ -712,7 +718,9 @@ def from_raw_config( # noqa: C901
try:
path, search_text = decl.split(":", maxsplit=1)
# VersionDeclarationABC handles path existence check
vd = TomlVersionDeclaration(path, search_text)
vd = TomlVersionDeclaration(
path, search_text, version_compat=branch_config.version_compat
)
except ValueError as exc:
log.exception("Invalid TOML declaration %r", decl)
raise InvalidConfiguration(
Expand All @@ -724,6 +732,10 @@ def from_raw_config( # noqa: C901
for decl in () if raw.version_variables is None else raw.version_variables:
try:
path, variable = decl.split(":", maxsplit=1)
if branch_config.version_compat == "pep440":
version_pattern = PEP440_REGEX
else:
version_pattern = SEMVER_REGEX
# VersionDeclarationABC handles path existence check
search_text = str.join(
"",
Expand All @@ -734,10 +746,10 @@ def from_raw_config( # noqa: C901
# Supports walrus, equals sign, or colon as assignment operator ignoring whitespace separation
r"\s*(:=|[:=])\s*",
# Supports optional matching quotations around version number of a SEMVER pattern
f"""(?P<quote2>['"])?(?P<version>{SEMVER_REGEX.pattern})(?P=quote2)?""",
f"""(?P<quote2>['"])?(?P<version>{version_pattern.pattern})(?P=quote2)?""",
],
)
pd = PatternVersionDeclaration(path, search_text)
pd = PatternVersionDeclaration(path, search_text, version_compat=branch_config.version_compat)
except ValueError as exc:
log.exception("Invalid variable declaration %r", decl)
raise InvalidConfiguration(
Expand Down Expand Up @@ -800,7 +812,9 @@ def from_raw_config( # noqa: C901

# version_translator
version_translator = VersionTranslator(
tag_format=raw.tag_format, prerelease_token=branch_config.prerelease_token
tag_format=raw.tag_format,
prerelease_token=branch_config.prerelease_token,
version_compat=branch_config.version_compat,
)

build_cmd_env = {}
Expand Down
50 changes: 50 additions & 0 deletions 50 src/semantic_release/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,56 @@
flags=re.VERBOSE,
)

PEP440_REGEX = re.compile(
r"""
v?
(?:(?P<epoch>[0-9]+)!)?
(?P<major>0|[1-9]\d*)
\.
(?P<minor>0|[1-9]\d*)
\.
(?P<patch>0|[1-9]\d*)
(?:[-_\.]?(?P<prerelease>(?:(a|b|c|rc|alpha|beta|pre|preview|dev|post|rev|r))(?:[-_\.]?(?:[0-9]+))*))?
(?:\+(?P<buildmetadata>[a-z0-9]+(?:[-_\.][a-z0-9]+)*))?
""",
flags=re.VERBOSE | re.IGNORECASE,
)

# PEP440_PATTERN = re.compile(
# r"""
# ^\s*
# v?
# (?:
# (?:(?P<epoch>[0-9]+)!)? # epoch
# (?P<release>[0-9]+(?:\.[0-9]+)*) # release segment
# (?P<pre> # pre-release
# [-_\.]?
# (?P<pre_l>(a|b|c|rc|alpha|beta|pre|preview))
# [-_\.]?
# (?P<pre_n>[0-9]+)?
# )?
# (?P<post> # post release
# (?:-(?P<post_n1>[0-9]+))
# |
# (?:
# [-_\.]?
# (?P<post_l>post|rev|r)
# [-_\.]?
# (?P<post_n2>[0-9]+)?
# )
# )?
# (?P<dev> # dev release
# [-_\.]?
# (?P<dev_l>dev)
# [-_\.]?
# (?P<dev_n>[0-9]+)?
# )?
# )
# (?:\+(?P<local>[a-z0-9]+(?:[-_\.][a-z0-9]+)*))? # local version
# \s*$
# """,
# )

COMMIT_MESSAGE = "{version}\n\nAutomatically generated by python-semantic-release"
DEFAULT_COMMIT_AUTHOR = "semantic-release <semantic-release>"
DEFAULT_VERSION = "0.0.0"
Expand Down
11 changes: 6 additions & 5 deletions 11 src/semantic_release/version/declaration.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ class VersionDeclarationABC(ABC):
within the source tree of the repository
"""

def __init__(self, path: Path | str, search_text: str) -> None:
def __init__(self, path: Path | str, search_text: str, version_compat: str) -> None:
self.path = Path(path)
if not self.path.exists():
raise FileNotFoundError(f"path {self.path.resolve()!r} does not exist")
self.search_text = search_text
self.version_compat = version_compat
self._content: str | None = None

@property
Expand Down Expand Up @@ -106,7 +107,7 @@ def parse(self) -> set[Version]:
self.search_text,
maybe_version,
)
valid_version = Version.parse(maybe_version)
valid_version = Version.parse(maybe_version, version_compat=self.version_compat)
return {valid_version} if valid_version else set()
# Maybe in future raise error if not found?
return set()
Expand Down Expand Up @@ -137,8 +138,8 @@ class PatternVersionDeclaration(VersionDeclarationABC):

_VERSION_GROUP_NAME = "version"

def __init__(self, path: Path | str, search_text: str) -> None:
super().__init__(path, search_text)
def __init__(self, path: Path | str, search_text: str, version_compat: str) -> None:
super().__init__(path, search_text, version_compat)
self.search_re = re.compile(self.search_text, flags=re.MULTILINE)
if self._VERSION_GROUP_NAME not in self.search_re.groupindex:
raise ValueError(
Expand All @@ -159,7 +160,7 @@ def parse(self) -> set[Version]:
to check for this condition.
"""
versions = {
Version.parse(m.group(self._VERSION_GROUP_NAME))
Version.parse(m.group(self._VERSION_GROUP_NAME), version_compat=self.version_compat)
for m in self.search_re.finditer(self.content, re.MULTILINE)
}

Expand Down
6 changes: 3 additions & 3 deletions 6 src/semantic_release/version/translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import logging
import re

from semantic_release.const import SEMVER_REGEX
from semantic_release.helpers import check_tag_format
from semantic_release.version.version import Version

Expand All @@ -16,8 +15,6 @@ class VersionTranslator:
instances.
"""

_VERSION_REGEX = SEMVER_REGEX

@classmethod
def _invert_tag_format_to_re(cls, tag_format: str) -> re.Pattern[str]:
r"""
Expand All @@ -44,10 +41,12 @@ def __init__(
self,
tag_format: str = "v{version}",
prerelease_token: str = "rc", # noqa: S107
version_compat: str = "semver"
) -> None:
check_tag_format(tag_format)
self.tag_format = tag_format
self.prerelease_token = prerelease_token
self.version_compat = version_compat
self.from_tag_re = self._invert_tag_format_to_re(self.tag_format)

def from_string(self, version_str: str) -> Version:
Expand All @@ -59,6 +58,7 @@ def from_string(self, version_str: str) -> Version:
version_str,
tag_format=self.tag_format,
prerelease_token=self.prerelease_token,
version_compat=self.version_compat,
)

def from_tag(self, tag: str) -> Version | None:
Expand Down
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.