From 9bdd626bf8f8359d35725cebe803931063260cac Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 12 Jan 2025 10:52:02 -0700 Subject: [PATCH 001/160] docs(github-actions): update PSR versions in github workflow examples (#1140) --- docs/automatic-releases/github-actions.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/automatic-releases/github-actions.rst b/docs/automatic-releases/github-actions.rst index eaae2711d..1a6be5de7 100644 --- a/docs/automatic-releases/github-actions.rst +++ b/docs/automatic-releases/github-actions.rst @@ -337,7 +337,7 @@ before the :ref:`version ` subcommand. .. code:: yaml - - uses: python-semantic-release/python-semantic-release@v9.12.0 + - uses: python-semantic-release/python-semantic-release@v9.16.0 with: root_options: "-vv --noop" @@ -576,7 +576,7 @@ before the :ref:`publish ` subcommand. .. code:: yaml - - uses: python-semantic-release/publish-action@v9.8.9 + - uses: python-semantic-release/publish-action@v9.16.0 with: root_options: "-vv --noop" @@ -684,7 +684,7 @@ to the GitHub Release Assets as well. - name: Action | Semantic Version Release id: release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.12.0 + uses: python-semantic-release/python-semantic-release@v9.16.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} git_committer_name: "github-actions" @@ -695,7 +695,7 @@ to the GitHub Release Assets as well. if: steps.release.outputs.released == 'true' - name: Publish | Upload to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.8.9 + uses: python-semantic-release/publish-action@v9.16.0 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -744,7 +744,7 @@ The equivalent GitHub Action configuration would be: - name: Action | Semantic Version Release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.12.0 + uses: python-semantic-release/python-semantic-release@v9.16.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} force: patch @@ -772,13 +772,13 @@ Publish Action. .. code:: yaml - name: Release Project 1 - uses: python-semantic-release/python-semantic-release@v9.12.0 + uses: python-semantic-release/python-semantic-release@v9.16.0 with: directory: ./project1 github_token: ${{ secrets.GITHUB_TOKEN }} - name: Release Project 2 - uses: python-semantic-release/python-semantic-release@v9.12.0 + uses: python-semantic-release/python-semantic-release@v9.16.0 with: directory: ./project2 github_token: ${{ secrets.GITHUB_TOKEN }} From 3cf32b82dc709e71ef45aae67436405c32a4edab Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 12 Jan 2025 11:00:00 -0700 Subject: [PATCH 002/160] ci(cicd-wkflow): add version updater for documentation before release (#1141) * chore(config): adjust build command & doc version bump script implementation --- .github/workflows/cicd.yml | 19 ++++++++++++++++--- pyproject.toml | 2 -- scripts/bump_version_in_docs.py | 23 +++++++++++++---------- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 14e662112..7a63e8dea 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -67,9 +67,6 @@ jobs: needs: eval-changes with: python-versions-linux: '["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]' - # Since the test suite takes ~4 minutes to complete on windows, and windows is billed higher - # we are only going to run it on the oldest version of python we support. The older version - # will be the most likely area to fail as newer minor versions maintain compatibility. python-versions-windows: '["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]' files-changed: ${{ needs.eval-changes.outputs.any-file-changes }} build-files-changed: ${{ needs.eval-changes.outputs.build-changes }} @@ -116,6 +113,22 @@ jobs: name: ${{ needs.validate.outputs.distribution-artifacts }} path: dist + - name: Evaluate | Determine Next Version + id: version + uses: ./ + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + root_options: "-v --noop" + + - name: Release | Bump Version in Docs + if: steps.version.outputs.released == 'true' && steps.version.outputs.is_prerelease == 'false' + env: + NEW_VERSION: ${{ steps.version.outputs.version }} + NEW_RELEASE_TAG: ${{ steps.version.outputs.tag }} + run: | + python -m scripts.bump_version_in_docs + git add docs/* + - name: Release | Python Semantic Release id: release uses: ./ diff --git a/pyproject.toml b/pyproject.toml index 91b9d3692..4d62532c9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -400,8 +400,6 @@ ignore_names = ["change_to_ex_proj_dir", "init_example_project"] logging_use_named_masks = true commit_parser = "angular" build_command = """ - python -m scripts.bump_version_in_docs - git add docs/* python -m pip install -e .[build] python -m build . """ diff --git a/scripts/bump_version_in_docs.py b/scripts/bump_version_in_docs.py index 30874d345..8167f0fad 100644 --- a/scripts/bump_version_in_docs.py +++ b/scripts/bump_version_in_docs.py @@ -3,35 +3,38 @@ from os import getenv from pathlib import Path -from re import compile as RegExp # noqa: N812 +from re import compile as regexp PROJ_DIR = Path(__file__).resolve().parent.parent DOCS_DIR = PROJ_DIR / "docs" -def update_github_actions_example(filepath: Path, new_version: str) -> None: - psr_regex = RegExp(r"(uses:.*python-semantic-release)@v\d+\.\d+\.\d+") +def update_github_actions_example(filepath: Path, release_tag: str) -> None: + psr_regex = regexp(r"(uses: python-semantic-release/python-semantic-release)@\S+$") + psr_publish_action_regex = regexp( + r"(uses: python-semantic-release/publish-action)@\S+$" + ) file_content_lines: list[str] = filepath.read_text().splitlines() - for regex in [psr_regex]: + for regex in [psr_regex, psr_publish_action_regex]: file_content_lines = list( map( - lambda line, regex=regex: regex.sub(r"\1@v" + new_version, line), # type: ignore[misc] + lambda line, regex=regex: regex.sub(r"\1@" + release_tag, line), # type: ignore[misc] file_content_lines, ) ) - print(f"Bumping version in {filepath} to", new_version) + print(f"Bumping version in {filepath} to", release_tag) filepath.write_text(str.join("\n", file_content_lines) + "\n") if __name__ == "__main__": - new_version = getenv("NEW_VERSION") + new_release_tag = getenv("NEW_RELEASE_TAG") - if not new_version: - print("NEW_VERSION environment variable is not set") + if not new_release_tag: + print("NEW_RELEASE_TAG environment variable is not set") exit(1) update_github_actions_example( - DOCS_DIR / "automatic-releases" / "github-actions.rst", new_version + DOCS_DIR / "automatic-releases" / "github-actions.rst", new_release_tag ) From c4056fc2e1fb3bddb78728793716ac6fb8522b1a Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 12 Jan 2025 12:49:04 -0700 Subject: [PATCH 003/160] fix(parser-custom): handle relative parent directory paths to module file better (#1142) The dynamic import originally would just replace "/" with "." to make the import module name more pythonic, however this would be problematic in monorepos which would use "../../misc/commit_parser.py" to locate the parser and so the resulting `sys.modules` entry would have numerous periods (.) as a prefix. This removes that possibility. Still always an issue if the imported module name matches an existing module but the likelihood is low. --- src/semantic_release/helpers.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/semantic_release/helpers.py b/src/semantic_release/helpers.py index db82a97eb..83d700bfe 100644 --- a/src/semantic_release/helpers.py +++ b/src/semantic_release/helpers.py @@ -69,7 +69,6 @@ def dynamic_import(import_path: str) -> Any: Dynamically import an object from a conventionally formatted "module:attribute" string """ - log.debug("Trying to import %s", import_path) module_name, attr = import_path.split(":", maxsplit=1) # Check if the module is a file path, if it can be resolved and exists on disk then import as a file @@ -78,10 +77,11 @@ def dynamic_import(import_path: str) -> Any: module_path = ( module_filepath.stem if Path(module_name).is_absolute() - else str(Path(module_name).with_suffix("")).replace(os.sep, ".") + else str(Path(module_name).with_suffix("")).replace(os.sep, ".").lstrip(".") ) if module_path not in sys.modules: + log.debug("Loading '%s' from file '%s'", module_path, module_filepath) spec = importlib.util.spec_from_file_location( module_path, str(module_filepath) ) @@ -96,7 +96,9 @@ def dynamic_import(import_path: str) -> Any: # Otherwise, import as a module try: + log.debug("Importing module '%s'", module_name) module = importlib.import_module(module_name) + log.debug("Loading '%s' from module '%s'", attr, module_name) return getattr(module, attr) except TypeError as err: raise ImportError( From d6a3be3386441a4f3a73df1bdd57ded251941739 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Sun, 12 Jan 2025 20:02:37 +0000 Subject: [PATCH 004/160] 9.16.1 Automatically generated by python-semantic-release --- CHANGELOG.md | 21 +++++++++++++++++++++ docs/automatic-releases/github-actions.rst | 14 +++++++------- pyproject.toml | 2 +- src/semantic_release/__init__.py | 2 +- 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38439a798..2e4a8c2aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,27 @@ # CHANGELOG +## v9.16.1 (2025-01-12) + +### Bug Fixes + +- **parser-custom**: Handle relative parent directory paths to module file better + ([#1142](https://github.com/python-semantic-release/python-semantic-release/pull/1142), + [`c4056fc`](https://github.com/python-semantic-release/python-semantic-release/commit/c4056fc2e1fb3bddb78728793716ac6fb8522b1a)) + +The dynamic import originally would just replace "/" with "." to make the import module name more + pythonic, however this would be problematic in monorepos which would use + "../../misc/commit_parser.py" to locate the parser and so the resulting `sys.modules` entry would + have numerous periods (.) as a prefix. This removes that possibility. Still always an issue if the + imported module name matches an existing module but the likelihood is low. + +### Documentation + +- **github-actions**: Update PSR versions in github workflow examples + ([#1140](https://github.com/python-semantic-release/python-semantic-release/pull/1140), + [`9bdd626`](https://github.com/python-semantic-release/python-semantic-release/commit/9bdd626bf8f8359d35725cebe803931063260cac)) + + ## v9.16.0 (2025-01-12) ### Bug Fixes diff --git a/docs/automatic-releases/github-actions.rst b/docs/automatic-releases/github-actions.rst index 1a6be5de7..2fc8c8721 100644 --- a/docs/automatic-releases/github-actions.rst +++ b/docs/automatic-releases/github-actions.rst @@ -337,7 +337,7 @@ before the :ref:`version ` subcommand. .. code:: yaml - - uses: python-semantic-release/python-semantic-release@v9.16.0 + - uses: python-semantic-release/python-semantic-release@v9.16.1 with: root_options: "-vv --noop" @@ -576,7 +576,7 @@ before the :ref:`publish ` subcommand. .. code:: yaml - - uses: python-semantic-release/publish-action@v9.16.0 + - uses: python-semantic-release/publish-action@v9.16.1 with: root_options: "-vv --noop" @@ -684,7 +684,7 @@ to the GitHub Release Assets as well. - name: Action | Semantic Version Release id: release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.16.0 + uses: python-semantic-release/python-semantic-release@v9.16.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} git_committer_name: "github-actions" @@ -695,7 +695,7 @@ to the GitHub Release Assets as well. if: steps.release.outputs.released == 'true' - name: Publish | Upload to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.16.0 + uses: python-semantic-release/publish-action@v9.16.1 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -744,7 +744,7 @@ The equivalent GitHub Action configuration would be: - name: Action | Semantic Version Release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.16.0 + uses: python-semantic-release/python-semantic-release@v9.16.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} force: patch @@ -772,13 +772,13 @@ Publish Action. .. code:: yaml - name: Release Project 1 - uses: python-semantic-release/python-semantic-release@v9.16.0 + uses: python-semantic-release/python-semantic-release@v9.16.1 with: directory: ./project1 github_token: ${{ secrets.GITHUB_TOKEN }} - name: Release Project 2 - uses: python-semantic-release/python-semantic-release@v9.16.0 + uses: python-semantic-release/python-semantic-release@v9.16.1 with: directory: ./project2 github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/pyproject.toml b/pyproject.toml index 4d62532c9..f7ab040c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.16.0" +version = "9.16.1" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } diff --git a/src/semantic_release/__init__.py b/src/semantic_release/__init__.py index d5a2e8cf6..c39aa0c23 100644 --- a/src/semantic_release/__init__.py +++ b/src/semantic_release/__init__.py @@ -24,7 +24,7 @@ tags_and_versions, ) -__version__ = "9.16.0" +__version__ = "9.16.1" __all__ = [ "CommitParser", From 7edaa902a617139e812fb6e2d5bf8dea5ef0550c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 16:53:36 -0700 Subject: [PATCH 005/160] ci(deps): bump `python-semantic-release/publish-action@v9.15.2` action to 9.16.1 (#1145) --- .github/workflows/cicd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 7a63e8dea..de41ec0aa 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -138,7 +138,7 @@ jobs: build: false - name: Release | Add distribution artifacts to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.15.2 + uses: python-semantic-release/publish-action@v9.16.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} tag: ${{ steps.release.outputs.tag }} From 77923885c585171e8888aacde989837ecbabf3fc Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 17 Jan 2025 16:51:25 -0700 Subject: [PATCH 006/160] feat(changelog): add `sort_numerically` filter function to template environment (#1146) * test(helpers): add unit tests for various prefixed number lists * test(changelog-context): add unit tests to validate use of `sort_numerically` filter * test(release-notes-context): add unit tests to validate use of `sort_numerically` filter * refactor(util): relocate `sort_numerically` function to top level * docs(changelog-templates): add description for new `sort_numerically` filter function --- docs/changelog_templates.rst | 26 +++ src/semantic_release/changelog/context.py | 3 + src/semantic_release/cli/changelog_writer.py | 7 +- src/semantic_release/commit_parser/angular.py | 7 +- src/semantic_release/commit_parser/emoji.py | 3 +- src/semantic_release/commit_parser/util.py | 13 +- src/semantic_release/helpers.py | 75 +++++++- .../changelog/test_changelog_context.py | 88 ++++++++++ .../changelog/test_release_notes.py | 97 ++++++++++ tests/unit/semantic_release/test_helpers.py | 166 +++++++++++++++++- 10 files changed, 468 insertions(+), 17 deletions(-) diff --git a/docs/changelog_templates.rst b/docs/changelog_templates.rst index 1ba78dba9..1200c0159 100644 --- a/docs/changelog_templates.rst +++ b/docs/changelog_templates.rst @@ -831,6 +831,31 @@ The filters provided vary based on the VCS configured and available features: {% set prev_changelog_contents = prev_changelog_file | read_file | safe %} +* ``sort_numerically (Callable[[Iterable[str], bool], list[str]])``: given a + sequence of strings with possibly some non-number characters as a prefix or suffix, + sort the strings as if they were just numbers from lowest to highest. This filter + is useful when you want to sort issue numbers or other strings that have a numeric + component in them but cannot be cast to a number directly to sort them. If you want + to sort the strings in reverse order, you can pass a boolean value of ``True`` as the + second argument. + + *Introduced in v9.16.0.* + + **Example Usage:** + + .. code:: jinja + + {{ ["#222", "#1023", "#444"] | sort_numerically }} + {{ ["#222", "#1023", "#444"] | sort_numerically(True) }} + + **Markdown Output:** + + .. code:: markdown + + ['#222', '#444', '#1023'] + ['#1023', '#444', '#222'] + + Availability of the documented filters can be found in the table below: ====================== ========= ===== ====== ====== @@ -846,6 +871,7 @@ issue_url ❌ ✅ ✅ ✅ merge_request_url ❌ ❌ ❌ ✅ pull_request_url ✅ ✅ ✅ ✅ read_file ✅ ✅ ✅ ✅ +sort_numerically ✅ ✅ ✅ ✅ ====================== ========= ===== ====== ====== .. seealso:: diff --git a/src/semantic_release/changelog/context.py b/src/semantic_release/changelog/context.py index 76f499163..9b8b102fe 100644 --- a/src/semantic_release/changelog/context.py +++ b/src/semantic_release/changelog/context.py @@ -8,6 +8,8 @@ from re import compile as regexp from typing import TYPE_CHECKING, Any, Callable, Literal +from semantic_release.helpers import sort_numerically + if TYPE_CHECKING: # pragma: no cover from jinja2 import Environment @@ -87,6 +89,7 @@ def make_changelog_context( read_file, convert_md_to_rst, autofit_text_width, + sort_numerically, ), ) diff --git a/src/semantic_release/cli/changelog_writer.py b/src/semantic_release/cli/changelog_writer.py index 2a9accab8..5c6ab9f55 100644 --- a/src/semantic_release/cli/changelog_writer.py +++ b/src/semantic_release/cli/changelog_writer.py @@ -24,6 +24,7 @@ ) from semantic_release.cli.util import noop_report from semantic_release.errors import InternalError +from semantic_release.helpers import sort_numerically if TYPE_CHECKING: # pragma: no cover from jinja2 import Environment @@ -254,7 +255,11 @@ def generate_release_notes( version=release["version"], release=release, mask_initial_release=mask_initial_release, - filters=(*hvcs_client.get_changelog_context_filters(), autofit_text_width), + filters=( + *hvcs_client.get_changelog_context_filters(), + autofit_text_width, + sort_numerically, + ), ).bind_to_environment( # Use a new, non-configurable environment for release notes - # not user-configurable at the moment diff --git a/src/semantic_release/commit_parser/angular.py b/src/semantic_release/commit_parser/angular.py index 533c235e2..c22d80f06 100644 --- a/src/semantic_release/commit_parser/angular.py +++ b/src/semantic_release/commit_parser/angular.py @@ -21,13 +21,10 @@ ParseError, ParseResult, ) -from semantic_release.commit_parser.util import ( - breaking_re, - parse_paragraphs, - sort_numerically, -) +from semantic_release.commit_parser.util import breaking_re, parse_paragraphs from semantic_release.enums import LevelBump from semantic_release.errors import InvalidParserOptions +from semantic_release.helpers import sort_numerically if TYPE_CHECKING: # pragma: no cover from git.objects.commit import Commit diff --git a/src/semantic_release/commit_parser/emoji.py b/src/semantic_release/commit_parser/emoji.py index 0cefdbeee..df8aeba38 100644 --- a/src/semantic_release/commit_parser/emoji.py +++ b/src/semantic_release/commit_parser/emoji.py @@ -18,9 +18,10 @@ ParsedMessageResult, ParseResult, ) -from semantic_release.commit_parser.util import parse_paragraphs, sort_numerically +from semantic_release.commit_parser.util import parse_paragraphs from semantic_release.enums import LevelBump from semantic_release.errors import InvalidParserOptions +from semantic_release.helpers import sort_numerically logger = logging.getLogger(__name__) diff --git a/src/semantic_release/commit_parser/util.py b/src/semantic_release/commit_parser/util.py index 996077d28..60ed63b2f 100644 --- a/src/semantic_release/commit_parser/util.py +++ b/src/semantic_release/commit_parser/util.py @@ -4,17 +4,20 @@ from re import MULTILINE, compile as regexp from typing import TYPE_CHECKING +# TODO: remove in v10 +from semantic_release.helpers import ( + sort_numerically, # noqa: F401 # TODO: maintained for compatibility +) + if TYPE_CHECKING: # pragma: no cover from re import Pattern - from typing import Sequence, TypedDict + from typing import TypedDict class RegexReplaceDef(TypedDict): pattern: Pattern repl: str -number_pattern = regexp(r"(\d+)") - breaking_re = regexp(r"BREAKING[ -]CHANGE:\s?(.*)") un_word_wrap: RegexReplaceDef = { @@ -71,7 +74,3 @@ def parse_paragraphs(text: str) -> list[str]: ], ) ) - - -def sort_numerically(iterable: Sequence[str] | set[str]) -> list[str]: - return sorted(iterable, key=lambda x: int((number_pattern.search(x) or [-1])[0])) diff --git a/src/semantic_release/helpers.py b/src/semantic_release/helpers.py index 83d700bfe..0840169ed 100644 --- a/src/semantic_release/helpers.py +++ b/src/semantic_release/helpers.py @@ -1,16 +1,87 @@ +from __future__ import annotations + import importlib.util import logging import os import re import string import sys -from functools import lru_cache, wraps +from functools import lru_cache, reduce, wraps from pathlib import Path, PurePosixPath -from typing import Any, Callable, NamedTuple, TypeVar +from re import IGNORECASE, compile as regexp +from typing import TYPE_CHECKING, Any, Callable, NamedTuple, TypeVar from urllib.parse import urlsplit +if TYPE_CHECKING: # pragma: no cover + from typing import Iterable + + log = logging.getLogger(__name__) +number_pattern = regexp(r"(?P\S*?)(?P\d[\d,]*)\b") +hex_number_pattern = regexp( + r"(?P\S*?)(?:0x)?(?P[0-9a-f]+)\b", IGNORECASE +) + + +def get_number_from_str( + string: str, default: int = -1, interpret_hex: bool = False +) -> int: + if interpret_hex and (match := hex_number_pattern.search(string)): + return abs(int(match.group("number"), 16)) + + if match := number_pattern.search(string): + return int(match.group("number")) + + return default + + +def sort_numerically( + iterable: Iterable[str], reverse: bool = False, allow_hex: bool = False +) -> list[str]: + # Alphabetically sort prefixes first, then sort by number + alphabetized_list = sorted(iterable) + + # Extract prefixes in order to group items by prefix + unmatched_items = [] + prefixes: dict[str, list[str]] = {} + for item in alphabetized_list: + if not ( + pattern_match := ( + (hex_number_pattern.search(item) if allow_hex else None) + or number_pattern.search(item) + ) + ): + unmatched_items.append(item) + continue + + prefix = prefix if (prefix := pattern_match.group("prefix")) else "" + + if prefix not in prefixes: + prefixes[prefix] = [] + + prefixes[prefix].append(item) + + # Sort prefixes and items by number mixing in unmatched items as alphabetized with other prefixes + return reduce( + lambda acc, next_item: acc + next_item, + [ + ( + sorted( + prefixes[prefix], + key=lambda x: get_number_from_str( + x, default=-1, interpret_hex=allow_hex + ), + reverse=reverse, + ) + if prefix in prefixes + else [prefix] + ) + for prefix in sorted([*prefixes.keys(), *unmatched_items]) + ], + [], + ) + def format_arg(value: Any) -> str: """Helper to format an argument an argument for logging""" diff --git a/tests/unit/semantic_release/changelog/test_changelog_context.py b/tests/unit/semantic_release/changelog/test_changelog_context.py index 7dedd0015..c80344fa1 100644 --- a/tests/unit/semantic_release/changelog/test_changelog_context.py +++ b/tests/unit/semantic_release/changelog/test_changelog_context.py @@ -497,3 +497,91 @@ def test_changelog_context_autofit_text_width_w_indent( # Evaluate assert expected_changelog == actual_changelog + + +def test_changelog_context_sort_numerically( + example_git_https_url: str, + artificial_release_history: ReleaseHistory, + changelog_md_file: Path, +): + changelog_tpl = dedent( + """\ + {{ [ + ".. _#5: link", + ".. _PR#3: link", + ".. _PR#10: link", + ".. _#100: link" + ] | sort_numerically | join("\\n") + }} + """ + ) + + expected_changelog = dedent( + """\ + .. _#5: link + .. _#100: link + .. _PR#3: link + .. _PR#10: link + """ + ) + + env = environment(trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True) + context = make_changelog_context( + hvcs_client=Gitlab(example_git_https_url), + release_history=artificial_release_history, + mode=ChangelogMode.UPDATE, + prev_changelog_file=changelog_md_file, + insertion_flag="", + mask_initial_release=False, + ) + context.bind_to_environment(env) + + # Create changelog from template with environment + actual_changelog = env.from_string(changelog_tpl).render() + + # Evaluate + assert expected_changelog == actual_changelog + + +def test_changelog_context_sort_numerically_reverse( + example_git_https_url: str, + artificial_release_history: ReleaseHistory, + changelog_md_file: Path, +): + changelog_tpl = dedent( + """\ + {{ [ + ".. _#5: link", + ".. _PR#3: link", + ".. _PR#10: link", + ".. _#100: link" + ] | sort_numerically(reverse=True) | join("\\n") + }} + """ + ) + + expected_changelog = dedent( + """\ + .. _#100: link + .. _#5: link + .. _PR#10: link + .. _PR#3: link + """ + ) + + env = environment(trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True) + context = make_changelog_context( + hvcs_client=Gitlab(example_git_https_url), + release_history=artificial_release_history, + mode=ChangelogMode.UPDATE, + prev_changelog_file=changelog_md_file, + insertion_flag="", + mask_initial_release=False, + ) + context.bind_to_environment(env) + + # Create changelog from template with environment + actual_changelog = env.from_string(changelog_tpl).render() + + # Evaluate + assert expected_changelog == actual_changelog diff --git a/tests/unit/semantic_release/changelog/test_release_notes.py b/tests/unit/semantic_release/changelog/test_release_notes.py index 6021ee7bb..7158dd670 100644 --- a/tests/unit/semantic_release/changelog/test_release_notes.py +++ b/tests/unit/semantic_release/changelog/test_release_notes.py @@ -2,6 +2,7 @@ import os from pathlib import Path +from textwrap import dedent from typing import TYPE_CHECKING import pytest @@ -17,6 +18,8 @@ if TYPE_CHECKING: from semantic_release.changelog.release_history import ReleaseHistory + from tests.fixtures.example_project import ExProjectDir + @pytest.fixture(scope="module") def release_notes_template() -> str: @@ -450,3 +453,97 @@ def test_default_release_notes_template_first_release_unmasked( ) assert expected_content == actual_content + + +def test_release_notes_context_sort_numerically_filter( + example_git_https_url: str, + single_release_history: ReleaseHistory, + example_project_dir: ExProjectDir, + change_to_ex_proj_dir: None, +): + version = list(single_release_history.released.keys())[-1] + release = single_release_history.released[version] + + example_project_dir.joinpath(".release_notes.md.j2").write_text( + dedent( + """\ + {{ [ + ".. _#5: link", + ".. _PR#3: link", + ".. _PR#10: link", + ".. _#100: link" + ] | sort_numerically | join("\\n") + }} + """ + ) + ) + + expected_content = str.join( + os.linesep, + dedent( + """\ + .. _#5: link + .. _#100: link + .. _PR#3: link + .. _PR#10: link + """ + ).split("\n"), + ) + + actual_content = generate_release_notes( + hvcs_client=Github(remote_url=example_git_https_url), + release=release, + template_dir=example_project_dir, + history=single_release_history, + style="angular", + mask_initial_release=False, + ) + + assert expected_content == actual_content + + +def test_release_notes_context_sort_numerically_filter_reversed( + example_git_https_url: str, + single_release_history: ReleaseHistory, + example_project_dir: ExProjectDir, + change_to_ex_proj_dir: None, +): + version = list(single_release_history.released.keys())[-1] + release = single_release_history.released[version] + + example_project_dir.joinpath(".release_notes.md.j2").write_text( + dedent( + """\ + {{ [ + ".. _#5: link", + ".. _PR#3: link", + ".. _PR#10: link", + ".. _#100: link" + ] | sort_numerically(reverse=True) | join("\\n") + }} + """ + ) + ) + + expected_content = str.join( + os.linesep, + dedent( + """\ + .. _#100: link + .. _#5: link + .. _PR#10: link + .. _PR#3: link + """ + ).split("\n"), + ) + + actual_content = generate_release_notes( + hvcs_client=Github(remote_url=example_git_https_url), + release=release, + template_dir=example_project_dir, + history=single_release_history, + style="angular", + mask_initial_release=False, + ) + + assert expected_content == actual_content diff --git a/tests/unit/semantic_release/test_helpers.py b/tests/unit/semantic_release/test_helpers.py index e7db7adfd..4877d3892 100644 --- a/tests/unit/semantic_release/test_helpers.py +++ b/tests/unit/semantic_release/test_helpers.py @@ -1,6 +1,8 @@ +from typing import Iterable + import pytest -from semantic_release.helpers import ParsedGitUrl, parse_git_url +from semantic_release.helpers import ParsedGitUrl, parse_git_url, sort_numerically @pytest.mark.parametrize( @@ -131,3 +133,165 @@ def test_parse_invalid_git_urls(url: str): """Test that an invalid git remote url throws a ValueError.""" with pytest.raises(ValueError): parse_git_url(url) + + +@pytest.mark.parametrize( + "unsorted_list, sorted_list, reverse, allow_hex", + [ + pytest.param( + unsorted_list, + sorted_list, + reverse, + allow_hex, + id=f"({i}) {test_id}", + ) + for i, (test_id, unsorted_list, sorted_list, reverse, allow_hex) in enumerate( + [ + ( + "Only numbers (with mixed digits, ASC)", + ["5", "3", "10"], + ["3", "5", "10"], + False, + False, + ), + ( + "Only numbers (with mixed digits, DESC)", + ["5", "3", "10"], + ["10", "5", "3"], + True, + False, + ), + ( + "Only PR numbers (ASC)", + ["#5", "#3", "#10"], + ["#3", "#5", "#10"], + False, + False, + ), + ( + "Only PR numbers (DESC)", + ["#5", "#3", "#10"], + ["#10", "#5", "#3"], + True, + False, + ), + ( + "Multiple prefixes (ASC)", + ["#5", "PR#3", "PR#10", "#100"], + ["#5", "#100", "PR#3", "PR#10"], + False, + False, + ), + ( + "Multiple prefixes (DESC)", + ["#5", "PR#3", "PR#10", "#100"], + ["#100", "#5", "PR#10", "PR#3"], + True, + False, + ), + ( + "No numbers mixed with mulitple prefixes (ASC)", + ["word", "#100", "#1000", "PR#45"], + ["#100", "#1000", "PR#45", "word"], + False, + False, + ), + ( + "No numbers mixed with mulitple prefixes (DESC)", + ["word", "#100", "#1000", "PR#45"], + ["#1000", "#100", "PR#45", "word"], + True, + False, + ), + ( + "Commit hash links in RST link format (ASC)", + [".. _8ab43ed:", ".. _7ffed34:", ".. _a3b4c54:"], + [".. _7ffed34:", ".. _8ab43ed:", ".. _a3b4c54:"], + False, + True, + ), + ( + "Commit hash links in RST link format (DESC)", + [".. _8ab43ed:", ".. _7ffed34:", ".. _a3b4c54:"], + [".. _a3b4c54:", ".. _8ab43ed:", ".. _7ffed34:"], + True, + True, + ), + ( + "Mixed numbers, PR numbers, and commit hash links in RST link format (ASC)", + [ + ".. _#5:", + ".. _8ab43ed:", + ".. _PR#3:", + ".. _#20:", + ".. _7ffed34:", + ".. _#100:", + ".. _a3b4c54:", + ], + [ + ".. _7ffed34:", + ".. _8ab43ed:", + ".. _a3b4c54:", + ".. _#5:", + ".. _#20:", + ".. _#100:", + ".. _PR#3:", + ], + False, + True, + ), + ( + "Mixed numbers, PR numbers, and commit hash links in RST link format (DESC)", + [ + ".. _#5:", + ".. _8ab43ed:", + ".. _PR#3:", + ".. _#20:", + ".. _7ffed34:", + ".. _#100:", + ".. _a3b4c54:", + ], + [ + ".. _a3b4c54:", + ".. _8ab43ed:", + ".. _7ffed34:", + ".. _#100:", + ".. _#20:", + ".. _#5:", + ".. _PR#3:", + ], + True, + True, + ), + ( + # No change since the prefixes are always alphabetical, asc/desc only is b/w numbers + "Same numbers with different prefixes (ASC)", + ["PR#5", "#5"], + ["#5", "PR#5"], + False, + False, + ), + ( + "Same numbers with different prefixes (DESC)", + ["#5", "PR#5"], + ["#5", "PR#5"], + True, + False, + ), + ], + start=1, + ) + ], +) +def test_sort_numerically( + unsorted_list: Iterable[str], + sorted_list: Iterable[str], + reverse: bool, + allow_hex: bool, +): + actual_list = sort_numerically( + iterable=unsorted_list, + reverse=reverse, + allow_hex=allow_hex, + ) + assert sorted_list == actual_list From f1ef4ecf5f22684a870b958f87d1ca2650e612db Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 17 Jan 2025 17:37:20 -0700 Subject: [PATCH 007/160] perf(logging): remove irrelevant debug logging statements (#1147) * refactor: adjust logging output --- .../changelog/release_history.py | 18 +++++++++--------- src/semantic_release/changelog/template.py | 1 - src/semantic_release/cli/commands/version.py | 2 +- src/semantic_release/cli/masking_filter.py | 2 +- src/semantic_release/version/algorithm.py | 1 - 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/semantic_release/changelog/release_history.py b/src/semantic_release/changelog/release_history.py index 961ae074c..e50728d4c 100644 --- a/src/semantic_release/changelog/release_history.py +++ b/src/semantic_release/changelog/release_history.py @@ -131,9 +131,9 @@ def from_git_history( # commits included, the true reason for a version bump would be missing. if has_exclusion_match and commit_level_bump == LevelBump.NO_RELEASE: log.info( - "Excluding commit [%s] %s", - commit.hexsha[:8], - commit_message.replace("\n", " ")[:50], + "Excluding commit[%s] %s", + parse_result.short_hash, + commit_message.split("\n", maxsplit=1)[0][:40], ) continue @@ -145,29 +145,29 @@ def from_git_history( str.join( " ", [ - "Excluding commit %s (%s) because parser determined", + "Excluding commit[%s] (%s) because parser determined", "it should not included in the changelog", ], ), - commit.hexsha[:8], + parse_result.short_hash, commit_message.replace("\n", " ")[:20], ) continue if the_version is None: log.info( - "[Unreleased] adding '%s' commit(%s) to list", - commit.hexsha[:8], + "[Unreleased] adding commit[%s] to unreleased '%s'", + parse_result.short_hash, commit_type, ) unreleased[commit_type].append(parse_result) continue log.info( - "[%s] adding '%s' commit(%s) to release", + "[%s] adding commit[%s] to release '%s'", the_version, + parse_result.short_hash, commit_type, - commit.hexsha[:8], ) released[the_version]["elements"][commit_type].append(parse_result) diff --git a/src/semantic_release/changelog/template.py b/src/semantic_release/changelog/template.py index c441788ce..2b80d8f65 100644 --- a/src/semantic_release/changelog/template.py +++ b/src/semantic_release/changelog/template.py @@ -54,7 +54,6 @@ def environment( autoescape_value = dynamic_import(autoescape) else: autoescape_value = autoescape - log.debug("%s", locals()) return ComplexDirectorySandboxedEnvironment( block_start_string=block_start_string, diff --git a/src/semantic_release/cli/commands/version.py b/src/semantic_release/cli/commands/version.py index 6dc2774d2..38609c0ad 100644 --- a/src/semantic_release/cli/commands/version.py +++ b/src/semantic_release/cli/commands/version.py @@ -65,7 +65,7 @@ def is_forced_prerelease( log.debug( "%s: %s", is_forced_prerelease.__name__, - ", ".join(f"{k} = {v}" for k, v in local_vars), + str.join(", ", iter(f"{k} = {v}" for k, v in local_vars)), ) return ( as_prerelease diff --git a/src/semantic_release/cli/masking_filter.py b/src/semantic_release/cli/masking_filter.py index aba7575d6..2c0fdb947 100644 --- a/src/semantic_release/cli/masking_filter.py +++ b/src/semantic_release/cli/masking_filter.py @@ -27,7 +27,7 @@ def __init__( def add_mask_for(self, data: str, name: str = "redacted") -> MaskingFilter: if data and data not in self._UNWANTED: - log.debug("Adding redact pattern %r to _redact_patterns", name) + log.debug("Adding redact pattern '%r' to redact_patterns", name) self._redact_patterns[name].add(data) return self diff --git a/src/semantic_release/version/algorithm.py b/src/semantic_release/version/algorithm.py index cc2a7dfc3..f1fe86253 100644 --- a/src/semantic_release/version/algorithm.py +++ b/src/semantic_release/version/algorithm.py @@ -89,7 +89,6 @@ def dfs(start_commit: Commit, stop_nodes: set[Commit]) -> Sequence[Commit]: # Add all parent commits to the stack from left to right so that the rightmost is popped first # as the left side is generally the merged into branch for parent in node.parents: - logger.debug("queuing parent commit %s", parent.hexsha[:7]) stack.put(parent) return commits From 315ae2176e211b00b13374560d81e127a3065d1a Mon Sep 17 00:00:00 2001 From: Kristof Wevers Date: Fri, 24 Jan 2025 04:30:34 +0100 Subject: [PATCH 008/160] fix(github-action): disable writing python bytecode in action execution (#1152) File permission issues can occur when using the github-action and dynamically loading files from the repository. When importing, python generally will create bytecode files and write to disk as the current user. Because the default user in the github action is root, those files are written as root which means when it returns to the rest of the workflow, those files cannot be modified or deleted. With this change, we disable writing of bytecode files which prevents any failures that may result after the python-semantic-release action is executed. --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index 770236ff3..489b41bee 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,4 +28,6 @@ RUN \ ENV PSR_DOCKER_GITHUB_ACTION=true +ENV PYTHONDONTWRITEBYTECODE=1 + ENTRYPOINT ["/bin/bash", "-l", "/psr/action.sh"] From abe0f9e6a88471f42cbdb87dd8ced5429687fa9c Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 24 Jan 2025 00:58:09 -0500 Subject: [PATCH 009/160] test(cmd-changelog): add changelog validator for 4 channel git flow repo (#1153) --- tests/e2e/cmd_changelog/test_changelog.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/e2e/cmd_changelog/test_changelog.py b/tests/e2e/cmd_changelog/test_changelog.py index 261c99a5d..586c5e961 100644 --- a/tests/e2e/cmd_changelog/test_changelog.py +++ b/tests/e2e/cmd_changelog/test_changelog.py @@ -42,6 +42,9 @@ repo_w_git_flow_w_alpha_prereleases_n_angular_commits, repo_w_git_flow_w_alpha_prereleases_n_emoji_commits, repo_w_git_flow_w_alpha_prereleases_n_scipy_commits, + repo_w_git_flow_w_beta_alpha_rev_prereleases_n_angular_commits, + repo_w_git_flow_w_beta_alpha_rev_prereleases_n_emoji_commits, + repo_w_git_flow_w_beta_alpha_rev_prereleases_n_scipy_commits, repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits, repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits_using_tag_format, repo_w_git_flow_w_rc_n_alpha_prereleases_n_emoji_commits, @@ -208,9 +211,9 @@ def test_changelog_noop_is_noop( repo_w_git_flow_angular_commits.__name__, repo_w_git_flow_emoji_commits.__name__, repo_w_git_flow_scipy_commits.__name__, - # repo_w_git_flow_w_beta_alpha_rev_prereleases_n_angular_commits.__name__, - # repo_w_git_flow_w_beta_alpha_rev_prereleases_n_emoji_commits.__name__, - # repo_w_git_flow_w_beta_alpha_rev_prereleases_n_scipy_commits.__name__, + repo_w_git_flow_w_beta_alpha_rev_prereleases_n_angular_commits.__name__, + repo_w_git_flow_w_beta_alpha_rev_prereleases_n_emoji_commits.__name__, + repo_w_git_flow_w_beta_alpha_rev_prereleases_n_scipy_commits.__name__, repo_w_git_flow_w_alpha_prereleases_n_angular_commits.__name__, repo_w_git_flow_w_alpha_prereleases_n_emoji_commits.__name__, repo_w_git_flow_w_alpha_prereleases_n_scipy_commits.__name__, From 40450375c7951dafddb09bef8001db7180d95f3a Mon Sep 17 00:00:00 2001 From: Lars Kellogg-Stedman Date: Sat, 25 Jan 2025 16:44:38 -0500 Subject: [PATCH 010/160] feat(config): extend support of remote urls aliased using git `insteadOf` configurations (#1151) Resolves: #1150 * refactor(hvcs): add validation of git urls upon vcs client initialization * test(hvcs): refactor unit test to catch validation error immediately of bad git url * test(config): add test case of a git `insteadOf` aliased origin --- src/semantic_release/cli/config.py | 7 ++- src/semantic_release/hvcs/_base.py | 2 +- .../unit/semantic_release/cli/test_config.py | 54 ++++++++++++++++++- .../unit/semantic_release/hvcs/test__base.py | 7 +-- 4 files changed, 61 insertions(+), 9 deletions(-) diff --git a/src/semantic_release/cli/config.py b/src/semantic_release/cli/config.py index 2911ae48f..f09520092 100644 --- a/src/semantic_release/cli/config.py +++ b/src/semantic_release/cli/config.py @@ -602,7 +602,12 @@ def from_raw_config( # noqa: C901 # Retrieve details from repository with Repo(str(raw.repo_dir)) as git_repo: try: - remote_url = raw.remote.url or git_repo.remote(raw.remote.name).url + # Get the remote url by calling out to `git remote get-url`. This returns + # the expanded url, taking into account any insteadOf directives + # in the git configuration. + remote_url = raw.remote.url or git_repo.git.remote( + "get-url", raw.remote.name + ) active_branch = git_repo.active_branch.name except ValueError as err: raise MissingGitRemote( diff --git a/src/semantic_release/hvcs/_base.py b/src/semantic_release/hvcs/_base.py index 505673935..60c6a5f87 100644 --- a/src/semantic_release/hvcs/_base.py +++ b/src/semantic_release/hvcs/_base.py @@ -30,7 +30,7 @@ class HvcsBase(metaclass=ABCMeta): """ def __init__(self, remote_url: str, *args: Any, **kwargs: Any) -> None: - self._remote_url = remote_url + self._remote_url = remote_url if parse_git_url(remote_url) else "" self._name: str | None = None self._owner: str | None = None diff --git a/tests/unit/semantic_release/cli/test_config.py b/tests/unit/semantic_release/cli/test_config.py index d083d01d1..c24781c4c 100644 --- a/tests/unit/semantic_release/cli/test_config.py +++ b/tests/unit/semantic_release/cli/test_config.py @@ -3,7 +3,7 @@ import os import shutil import sys -from pathlib import Path +from pathlib import Path, PurePosixPath from re import compile as regexp from typing import TYPE_CHECKING from unittest import mock @@ -11,6 +11,7 @@ import pytest import tomlkit from pydantic import RootModel, ValidationError +from urllib3.util.url import parse_url import semantic_release from semantic_release.cli.config import ( @@ -21,6 +22,7 @@ HvcsClient, RawConfig, RuntimeContext, + _known_hvcs, ) from semantic_release.cli.util import load_raw_config_file from semantic_release.commit_parser.angular import AngularParserOptions @@ -43,7 +45,7 @@ from typing import Any from tests.fixtures.example_project import ExProjectDir, UpdatePyprojectTomlFn - from tests.fixtures.git_repo import BuildRepoFn, CommitConvention + from tests.fixtures.git_repo import BuildRepoFn, BuiltRepoResult, CommitConvention @pytest.mark.parametrize( @@ -413,3 +415,51 @@ def test_changelog_config_default_insertion_flag( ) assert changelog_config.insertion_flag == insertion_flag + + +@pytest.mark.parametrize( + "hvcs_type", + [k.value for k in _known_hvcs], +) +def test_git_remote_url_w_insteadof_alias( + repo_w_initial_commit: BuiltRepoResult, + example_pyproject_toml: Path, + example_git_https_url: str, + hvcs_type: str, + update_pyproject_toml: UpdatePyprojectTomlFn, +): + expected_url = parse_url(example_git_https_url) + repo_name_suffix = PurePosixPath(expected_url.path or "").name + insteadof_alias = "psr_test_insteadof" + insteadof_value = expected_url.url.replace(repo_name_suffix, "") + repo = repo_w_initial_commit["repo"] + + with repo.config_writer() as cfg: + # Setup: define the insteadOf replacement value + cfg.add_value(f'url "{insteadof_value}"', "insteadof", f"{insteadof_alias}:") + + # Setup: set the remote URL with an insteadOf alias + cfg.set_value('remote "origin"', "url", f"{insteadof_alias}:{repo_name_suffix}") + + # Setup: set each supported HVCS client type + update_pyproject_toml("tool.semantic_release.remote.type", hvcs_type) + + # Act: load the configuration (in clear environment) + with mock.patch.dict(os.environ, {}, clear=True): + # Essentially the same as CliContextObj._init_runtime_ctx() + project_config = tomlkit.loads( + example_pyproject_toml.read_text(encoding="utf-8") + ).unwrap() + + runtime = RuntimeContext.from_raw_config( + raw=RawConfig.model_validate( + project_config.get("tool", {}).get("semantic_release", {}), + ), + global_cli_options=GlobalCommandLineOptions(), + ) + + # Trigger a function that calls helpers.parse_git_url() + actual_url = runtime.hvcs_client.remote_url(use_token=False) + + # Evaluate: the remote URL should be the full URL + assert expected_url.url == actual_url diff --git a/tests/unit/semantic_release/hvcs/test__base.py b/tests/unit/semantic_release/hvcs/test__base.py index e9568f2eb..a7e1365de 100644 --- a/tests/unit/semantic_release/hvcs/test__base.py +++ b/tests/unit/semantic_release/hvcs/test__base.py @@ -57,9 +57,6 @@ def test_get_repository_name(remote_url, owner): "git@gitlab.com/somewhere", ], ) -def test_hvcs_parse_error(bad_url): - client = ArbitraryHvcs(bad_url) +def test_hvcs_parse_error(bad_url: str): with pytest.raises(ValueError): - _ = client.repo_name - with pytest.raises(ValueError): - _ = client.owner + ArbitraryHvcs(bad_url) From cf785ca79a49eb4ee95c148e0ae6a19e230e915c Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 25 Jan 2025 20:06:04 -0500 Subject: [PATCH 011/160] feat(parsers): parse squashed commits individually (#1112) * test(parser-angular): update unit tests for parser return value compatibility * test(parser-scipy): update unit tests for parser return value compatibility * test(parser-emoji): update unit tests for parser return value compatibility * feat(version): parse squashed commits individually adds the functionality to separately parse each commit message within a squashed merge commit to detect combined commit types that could change the version bump * feat(changelog): parse squashed commits individually adds functionality to separately parse each commit message within a squashed merge commit which decouples the commits into their respective type categories in the changelog. * refactor(helpers): centralize utility for applying multiple text substitutions * feat(parser-angular): upgrade angular parser to parse squashed commits individually Resolves: #1085 * feat(parser-angular): apply PR/MR numbers to all parsed commits from a squash merge * feat(parser-emoji): add functionality to interpret scopes from gitmoji commit messages * feat(parser-emoji): upgrade emoji parser to parse squashed commits individually * test(fixtures): adjust parser for squashed commit definitions * test(fixtures): change config of github flow repo to parse squash commits * test(fixtures): add fixture to create gitlab formatted merge commit * refactor(parser-scipy): standardize all category spelling applied to commits * docs(commit-parsing): add description for squash commit evaluation option of default parsers * docs(configuration): update the `commit_parser_options` setting description --- docs/commit_parsing.rst | 107 ++- docs/configuration.rst | 62 +- .../changelog/release_history.py | 122 +-- src/semantic_release/commit_parser/_base.py | 2 +- src/semantic_release/commit_parser/angular.py | 234 +++++- src/semantic_release/commit_parser/emoji.py | 235 +++++- src/semantic_release/commit_parser/scipy.py | 14 +- src/semantic_release/commit_parser/tag.py | 4 +- src/semantic_release/commit_parser/util.py | 47 +- src/semantic_release/helpers.py | 10 + src/semantic_release/version/algorithm.py | 21 +- tests/conftest.py | 16 +- tests/fixtures/git_repo.py | 165 ++++- .../github_flow/repo_w_default_release.py | 3 +- .../commit_parser/test_angular.py | 701 ++++++++++++++++-- .../commit_parser/test_emoji.py | 589 ++++++++++++++- .../commit_parser/test_scipy.py | 591 ++++++++++++++- 17 files changed, 2656 insertions(+), 267 deletions(-) diff --git a/docs/commit_parsing.rst b/docs/commit_parsing.rst index fe1d3f376..1cb17a886 100644 --- a/docs/commit_parsing.rst +++ b/docs/commit_parsing.rst @@ -108,13 +108,13 @@ logic in relation to how PSR's core features: message. If no issue numbers are found, the parser will return an empty tuple. *Feature available in v9.15.0+.* -**Limitations:** +- **Squash Commit Evaluation**: This parser implements PSR's + :ref:`commit_parser-builtin-squash_commit_evaluation` to identify and extract each commit + message as a separate commit message within a single squashed commit. You can toggle this + feature on/off via the :ref:`config-commit_parser_options` setting. *Feature available in + v9.17.0+.* -- Squash commits are not currently supported. This means that the level bump for a squash - commit is only determined by the subject line of the squash commit. Our default changelog - template currently writes out the entire commit message body in the changelog in order to - provide the full detail of the changes. Track the implementation of this feature with - the issues `#733`_, `#1085`_, and `PR#1112`_. +**Limitations**: - Commits with the ``revert`` type are not currently supported. Track the implementation of this feature in the issue `#402`_. @@ -179,6 +179,12 @@ how PSR's core features: enabled by setting the configuration option ``commit_parser_options.parse_linked_issues`` to ``true``. *Feature available in v9.15.0+.* +- **Squash Commit Evaluation**: This parser implements PSR's + :ref:`commit_parser-builtin-squash_commit_evaluation` to identify and extract each commit + message as a separate commit message within a single squashed commit. You can toggle this + feature on/off via the :ref:`config-commit_parser_options` setting. *Feature available in + v9.17.0+.* + If no commit parser options are provided via the configuration, the parser will use PSR's built-in :py:class:`defaults `. @@ -304,6 +310,72 @@ return an empty tuple. ---- +.. _commit_parser-builtin-squash_commit_evaluation: + +Common Squash Commit Evaluation +""""""""""""""""""""""""""""""" + +*Introduced in v9.17.0* + +All of the PSR built-in parsers implement common squash commit evaluation logic to identify +and extract individual commit messages from a single squashed commit. The parsers will +look for common squash commit delimiters and multiple matches of the commit message +format to identify each individual commit message that was squashed. The parsers will +return a list containing each commit message as a separate commit object. Squashed commits +will be evaluated individually for both the level bump and changelog generation. If no +squash commits are found, a list with the single commit object will be returned. + +Currently, PSR has been tested against GitHub, BitBucket, and official ``git`` squash +merge commmit messages. GitLab does not have a default template for squash commit messages +but can be customized per project or server. If you are using GitLab, you will need to +ensure that the squash commit message format is similar to the example below. + +**Example**: + +*The following example will extract three separate commit messages from a single GitHub +formatted squash commit message of conventional commit style:* + +.. code-block:: text + + feat(config): add new config option (#123) + + * refactor(config): change the implementation of config loading + + * docs(configuration): defined new config option for the project + +When parsed with the default angular parser with squash commits toggled on, the version +bump will be determined by the highest level bump of the three commits (in this case, a +minor bump because of the feature commit) and the release notes would look similar to +the following: + +.. code-block:: markdown + + ## Features + + - **config**: add new config option (#123) + + ## Documentation + + - **configuration**: defined new config option for the project (#123) + + ## Refactoring + + - **config**: change the implementation of config loading (#123) + +Merge request numbers and commit hash values will be the same across all extracted +commits. Additionally, any :ref:`config-changelog-exclude_commit_patterns` will be +applied individually to each extracted commit so if you are have an exclusion match +for ignoring ``refactor`` commits, the second commit in the example above would be +excluded from the changelog. + +.. important:: + When squash commit evaluation is enabled, if you squashed a higher level bump commit + into the body of a lower level bump commit, the higher level bump commit will be + evaluated as the level bump for the entire squashed commit. This includes breaking + change descriptions. + +---- + .. _commit_parser-builtin-customization: Customization @@ -429,28 +501,23 @@ available. .. _catching exceptions in Python is slower: https://docs.python.org/3/faq/design.html#how-fast-are-exceptions .. _namedtuple: https://docs.python.org/3/library/typing.html#typing.NamedTuple -.. _commit-parsing-parser-options: +.. _commit_parser-parser-options: Parser Options """""""""""""" -To provide options to the commit parser which is configured in the :ref:`configuration file -`, Python Semantic Release includes a -:py:class:`ParserOptions ` -class. Each parser built into Python Semantic Release has a corresponding "options" class, which -subclasses :py:class:`ParserOptions `. - -The configuration in :ref:`commit_parser_options ` is passed to the -"options" class which is specified by the configured :ref:`commit_parser ` - -more information on how this is specified is below. +When writing your own parser, you should accompany the parser with an "options" class +which accepts the appropriate keyword arguments. This class' ``__init__`` method should +store the values that are needed for parsing appropriately. Python Semantic Release will +pass any configuration options from the configuration file's +:ref:`commit_parser_options `, into your custom parser options +class. To ensure that the configuration options are passed correctly, the options class +should inherit from the +:py:class:`ParserOptions ` class. The "options" class is used to validate the options which are configured in the repository, and to provide default values for these options where appropriate. -If you are writing your own parser, you should accompany it with an "options" class -which accepts the appropriate keyword arguments. This class' ``__init__`` method should -store the values that are needed for parsing appropriately. - .. _commit-parsing-commit-parsers: Commit Parsers diff --git a/docs/configuration.rst b/docs/configuration.rst index fac883479..884a28915 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -811,66 +811,14 @@ For more information see :ref:`commit-parsing`. **Type:** ``dict[str, Any]`` -These options are passed directly to the ``parser_options`` method of -:ref:`the commit parser `, without validation -or transformation. +This set of options are passed directly to the commit parser class specified in +:ref:`the commit parser ` configuration option. -For more information, see :ref:`commit-parsing-parser-options`. - -The default value for this setting depends on what you specify as -:ref:`commit_parser `. The table below outlines -the expections from ``commit_parser`` value to default options value. - -================== == ================================= -``commit_parser`` Default ``commit_parser_options`` -================== == ================================= -``"angular"`` -> .. code-block:: toml - - [semantic_release.commit_parser_options] - allowed_types = [ - "build", "chore", "ci", "docs", "feat", "fix", - "perf", "style", "refactor", "test" - ] - minor_types = ["feat"] - patch_types = ["fix", "perf"] - -``"emoji"`` -> .. code-block:: toml - - [semantic_release.commit_parser_options] - major_tags = [":boom:"] - minor_tags = [ - ":sparkles:", ":children_crossing:", ":lipstick:", - ":iphone:", ":egg:", ":chart_with_upwards_trend:" - ] - patch_tags = [ - ":ambulance:", ":lock:", ":bug:", ":zap:", ":goal_net:", - ":alien:", ":wheelchair:", ":speech_balloon:", ":mag:", - ":apple:", ":penguin:", ":checkered_flag:", ":robot:", - ":green_apple:" - ] - -``"scipy"`` -> .. code-block:: toml - - [semantic_release.commit_parser_options] - allowed_tags = [ - "API", "DEP", "ENH", "REV", "BUG", "MAINT", "BENCH", - "BLD", "DEV", "DOC", "STY", "TST", "REL", "FEAT", "TEST", - ] - major_tags = ["API",] - minor_tags = ["DEP", "DEV", "ENH", "REV", "FEAT"] - patch_tags = ["BLD", "BUG", "MAINT"] - -``"tag"`` -> .. code-block:: toml - - [semantic_release.commit_parser_options] - minor_tag = ":sparkles:" - patch_tag = ":nut_and_bolt:" - -``"module:class"`` -> ``**module:class.parser_options()`` -================== == ================================= +For more information (to include defaults), see +:ref:`commit_parser-builtin-customization`. **Default:** ``ParserOptions { ... }``, where ``...`` depends on -:ref:`config-commit_parser` as indicated above. +:ref:`commit_parser `. ---- diff --git a/src/semantic_release/changelog/release_history.py b/src/semantic_release/changelog/release_history.py index e50728d4c..16a3e9637 100644 --- a/src/semantic_release/changelog/release_history.py +++ b/src/semantic_release/changelog/release_history.py @@ -102,75 +102,85 @@ def from_git_history( released.setdefault(the_version, release) - # mypy will be happy if we make this an explicit string - commit_message = str(commit.message) - log.info( "parsing commit [%s] %s", commit.hexsha[:8], - commit_message.replace("\n", " ")[:54], - ) - parse_result = commit_parser.parse(commit) - commit_type = ( - "unknown" if isinstance(parse_result, ParseError) else parse_result.type - ) - - has_exclusion_match = any( - pattern.match(commit_message) for pattern in exclude_commit_patterns - ) - - commit_level_bump = ( - LevelBump.NO_RELEASE - if isinstance(parse_result, ParseError) - else parse_result.bump + str(commit.message).replace("\n", " ")[:54], ) + # returns a ParseResult or list of ParseResult objects, + # it is usually one, but we split a commit if a squashed merge is detected + parse_results = commit_parser.parse(commit) + if not isinstance(parse_results, list): + parse_results = [parse_results] + + is_squash_commit = bool(len(parse_results) > 1) + + # iterate through parsed commits to add to changelog definition + for parsed_result in parse_results: + commit_message = str(parsed_result.commit.message) + commit_type = ( + "unknown" + if isinstance(parsed_result, ParseError) + else parsed_result.type + ) + log.debug("commit has type '%s'", commit_type) - # Skip excluded commits except for any commit causing a version bump - # Reasoning: if a commit causes a version bump, and no other commits - # are included, then the changelog will be empty. Even if ther was other - # commits included, the true reason for a version bump would be missing. - if has_exclusion_match and commit_level_bump == LevelBump.NO_RELEASE: - log.info( - "Excluding commit[%s] %s", - parse_result.short_hash, - commit_message.split("\n", maxsplit=1)[0][:40], + has_exclusion_match = any( + pattern.match(commit_message) for pattern in exclude_commit_patterns ) - continue - if ( - isinstance(parse_result, ParsedCommit) - and not parse_result.include_in_changelog - ): - log.info( - str.join( - " ", - [ - "Excluding commit[%s] (%s) because parser determined", - "it should not included in the changelog", - ], - ), - parse_result.short_hash, - commit_message.replace("\n", " ")[:20], + commit_level_bump = ( + LevelBump.NO_RELEASE + if isinstance(parsed_result, ParseError) + else parsed_result.bump ) - continue - if the_version is None: + # Skip excluded commits except for any commit causing a version bump + # Reasoning: if a commit causes a version bump, and no other commits + # are included, then the changelog will be empty. Even if ther was other + # commits included, the true reason for a version bump would be missing. + if has_exclusion_match and commit_level_bump == LevelBump.NO_RELEASE: + log.info( + "Excluding %s commit[%s] %s", + "piece of squashed" if is_squash_commit else "", + parsed_result.short_hash, + commit_message.split("\n", maxsplit=1)[0][:20], + ) + continue + + if ( + isinstance(parsed_result, ParsedCommit) + and not parsed_result.include_in_changelog + ): + log.info( + str.join( + " ", + [ + "Excluding commit[%s] because parser determined", + "it should not included in the changelog", + ], + ), + parsed_result.short_hash, + ) + continue + + if the_version is None: + log.info( + "[Unreleased] adding commit[%s] to unreleased '%s'", + parsed_result.short_hash, + commit_type, + ) + unreleased[commit_type].append(parsed_result) + continue + log.info( - "[Unreleased] adding commit[%s] to unreleased '%s'", - parse_result.short_hash, + "[%s] adding commit[%s] to release '%s'", + the_version, + parsed_result.short_hash, commit_type, ) - unreleased[commit_type].append(parse_result) - continue - - log.info( - "[%s] adding commit[%s] to release '%s'", - the_version, - parse_result.short_hash, - commit_type, - ) - released[the_version]["elements"][commit_type].append(parse_result) + released[the_version]["elements"][commit_type].append(parsed_result) return cls(unreleased=unreleased, released=released) diff --git a/src/semantic_release/commit_parser/_base.py b/src/semantic_release/commit_parser/_base.py index d97faa1b8..04d2f56bd 100644 --- a/src/semantic_release/commit_parser/_base.py +++ b/src/semantic_release/commit_parser/_base.py @@ -81,4 +81,4 @@ def get_default_options(self) -> _OPTS: return self.parser_options() # type: ignore[return-value] @abstractmethod - def parse(self, commit: Commit) -> _TT: ... + def parse(self, commit: Commit) -> _TT | list[_TT]: ... diff --git a/src/semantic_release/commit_parser/angular.py b/src/semantic_release/commit_parser/angular.py index c22d80f06..511d73a38 100644 --- a/src/semantic_release/commit_parser/angular.py +++ b/src/semantic_release/commit_parser/angular.py @@ -10,8 +10,10 @@ from functools import reduce from itertools import zip_longest from re import compile as regexp +from textwrap import dedent from typing import TYPE_CHECKING, Tuple +from git.objects.commit import Commit from pydantic.dataclasses import dataclass from semantic_release.commit_parser._base import CommitParser, ParserOptions @@ -21,10 +23,15 @@ ParseError, ParseResult, ) -from semantic_release.commit_parser.util import breaking_re, parse_paragraphs +from semantic_release.commit_parser.util import ( + breaking_re, + deep_copy_commit, + force_str, + parse_paragraphs, +) from semantic_release.enums import LevelBump from semantic_release.errors import InvalidParserOptions -from semantic_release.helpers import sort_numerically +from semantic_release.helpers import sort_numerically, text_reducer if TYPE_CHECKING: # pragma: no cover from git.objects.commit import Commit @@ -90,6 +97,10 @@ class AngularParserOptions(ParserOptions): default_bump_level: LevelBump = LevelBump.NO_RELEASE """The minimum bump level to apply to valid commit message.""" + # TODO: breaking change v10, change default to True + parse_squash_commits: bool = False + """Toggle flag for whether or not to parse squash commits""" + @property def tag_to_level(self) -> dict[str, LevelBump]: """A mapping of commit tags to the level bump they should result in.""" @@ -139,14 +150,23 @@ def __init__(self, options: AngularParserOptions | None = None) -> None: ) ) from err - self.re_parser = regexp( + self.commit_prefix = regexp( str.join( "", [ - r"^" + commit_type_pattern.pattern, + f"^{commit_type_pattern.pattern}", r"(?:\((?P[^\n]+)\))?", # TODO: remove ! support as it is not part of the angular commit spec (its part of conventional commits spec) r"(?P!)?:\s+", + ], + ) + ) + + self.re_parser = regexp( + str.join( + "", + [ + self.commit_prefix.pattern, r"(?P[^\n]+)", r"(?:\n\n(?P.+))?", # commit body ], @@ -168,6 +188,42 @@ def __init__(self, options: AngularParserOptions | None = None) -> None: ), flags=re.MULTILINE | re.IGNORECASE, ) + self.filters = { + "typo-extra-spaces": (regexp(r"(\S) +(\S)"), r"\1 \2"), + "git-header-commit": ( + regexp(r"^[\t ]*commit [0-9a-f]+$\n?", flags=re.MULTILINE), + "", + ), + "git-header-author": ( + regexp(r"^[\t ]*Author: .+$\n?", flags=re.MULTILINE), + "", + ), + "git-header-date": ( + regexp(r"^[\t ]*Date: .+$\n?", flags=re.MULTILINE), + "", + ), + "git-squash-heading": ( + regexp( + r"^[\t ]*Squashed commit of the following:.*$\n?", + flags=re.MULTILINE, + ), + "", + ), + "git-squash-commit-prefix": ( + regexp( + str.join( + "", + [ + r"^(?:[\t ]*[*-][\t ]+|[\t ]+)?", # bullet points or indentation + commit_type_pattern.pattern + r"\b", # prior to commit type + ], + ), + flags=re.MULTILINE, + ), + # move commit type to the start of the line + r"\1", + ), + } @staticmethod def get_default_options() -> AngularParserOptions: @@ -213,7 +269,7 @@ def parse_message(self, message: str) -> ParsedMessageResult | None: return None parsed_break = parsed.group("break") - parsed_scope = parsed.group("scope") + parsed_scope = parsed.group("scope") or "" parsed_subject = parsed.group("subject") parsed_text = parsed.group("text") parsed_type = parsed.group("type") @@ -259,24 +315,170 @@ def parse_message(self, message: str) -> ParsedMessageResult | None: linked_merge_request=linked_merge_request, ) + def parse_commit(self, commit: Commit) -> ParseResult: + if not (parsed_msg_result := self.parse_message(force_str(commit.message))): + return _logged_parse_error( + commit, + f"Unable to parse commit message: {commit.message!r}", + ) + + return ParsedCommit.from_parsed_message_result(commit, parsed_msg_result) + # Maybe this can be cached as an optimization, similar to how # mypy/pytest use their own caching directories, for very large commit # histories? # The problem is the cache likely won't be present in CI environments - def parse(self, commit: Commit) -> ParseResult: + def parse(self, commit: Commit) -> ParseResult | list[ParseResult]: """ - Attempt to parse the commit message with a regular expression into a - ParseResult + Parse a commit message + + If the commit message is a squashed merge commit, it will be split into + multiple commits, each of which will be parsed separately. Single commits + will be returned as a list of a single ParseResult. """ - if not (pmsg_result := self.parse_message(str(commit.message))): - return _logged_parse_error( - commit, f"Unable to parse commit message: {commit.message!r}" + separate_commits: list[Commit] = ( + self.unsquash_commit(commit) + if self.options.parse_squash_commits + else [commit] + ) + + # Parse each commit individually if there were more than one + parsed_commits: list[ParseResult] = list( + map(self.parse_commit, separate_commits) + ) + + def add_linked_merge_request( + parsed_result: ParseResult, mr_number: str + ) -> ParseResult: + return ( + parsed_result + if not isinstance(parsed_result, ParsedCommit) + else ParsedCommit( + **{ + **parsed_result._asdict(), + "linked_merge_request": mr_number, + } + ) + ) + + # TODO: improve this for other VCS systems other than GitHub & BitBucket + # Github works as the first commit in a squash merge commit has the PR number + # appended to the first line of the commit message + lead_commit = next(iter(parsed_commits)) + + if isinstance(lead_commit, ParsedCommit) and lead_commit.linked_merge_request: + # If the first commit has linked merge requests, assume all commits + # are part of the same PR and add the linked merge requests to all + # parsed commits + parsed_commits = [ + lead_commit, + *map( + lambda parsed_result, mr=lead_commit.linked_merge_request: ( # type: ignore[misc] + add_linked_merge_request(parsed_result, mr) + ), + parsed_commits[1:], + ), + ] + + elif isinstance(lead_commit, ParseError) and ( + mr_match := self.mr_selector.search(force_str(lead_commit.message)) + ): + # Handle BitBucket Squash Merge Commits (see #1085), which have non angular commit + # format but include the PR number in the commit subject that we want to extract + linked_merge_request = mr_match.group("mr_number") + + # apply the linked MR to all commits + parsed_commits = [ + add_linked_merge_request(parsed_result, linked_merge_request) + for parsed_result in parsed_commits + ] + + return parsed_commits + + def unsquash_commit(self, commit: Commit) -> list[Commit]: + # GitHub EXAMPLE: + # feat(changelog): add autofit_text_width filter to template environment (#1062) + # + # This change adds an equivalent style formatter that can apply a text alignment + # to a maximum width and also maintain an indent over paragraphs of text + # + # * docs(changelog-templates): add definition & usage of autofit_text_width template filter + # + # * test(changelog-context): add test cases to check autofit_text_width filter use + # + # `git merge --squash` EXAMPLE: + # Squashed commit of the following: + # + # commit 63ec09b9e844e616dcaa7bae35a0b66671b59fbb + # Author: codejedi365 + # Date: Sun Oct 13 12:05:23 2024 -0600 + # + # feat(release-config): some commit subject + # + + # Return a list of artificial commits (each with a single commit message) + return [ + # create a artificial commit object (copy of original but with modified message) + Commit( + **{ + **deep_copy_commit(commit), + "message": commit_msg, + } ) + for commit_msg in self.unsquash_commit_message(force_str(commit.message)) + ] or [commit] + + def unsquash_commit_message(self, message: str) -> list[str]: + normalized_message = message.replace("\r", "").strip() + + # split by obvious separate commits (applies to manual git squash merges) + obvious_squashed_commits = self.filters["git-header-commit"][0].split( + normalized_message + ) - logger.debug( - "commit %s introduces a %s level_bump", - commit.hexsha[:8], - pmsg_result.bump, + separate_commit_msgs: list[str] = reduce( + lambda all_msgs, msgs: all_msgs + msgs, + map(self._find_squashed_commits_in_str, obvious_squashed_commits), + [], ) - return ParsedCommit.from_parsed_message_result(commit, pmsg_result) + return separate_commit_msgs + + def _find_squashed_commits_in_str(self, message: str) -> list[str]: + separate_commit_msgs: list[str] = [] + current_msg = "" + + for paragraph in filter(None, message.strip().split("\n\n")): + # Apply filters to normalize the paragraph + clean_paragraph = reduce(text_reducer, self.filters.values(), paragraph) + + # remove any filtered (and now empty) paragraphs (ie. the git headers) + if not clean_paragraph.strip(): + continue + + # Check if the paragraph is the start of a new angular commit + if not self.commit_prefix.search(clean_paragraph): + if not separate_commit_msgs and not current_msg: + # if there are no separate commit messages and no current message + # then this is the first commit message + current_msg = dedent(clean_paragraph) + continue + + # append the paragraph as part of the previous commit message + if current_msg: + current_msg += f"\n\n{dedent(clean_paragraph)}" + # else: drop the paragraph + continue + + # Since we found the start of the new commit, store any previous commit + # message separately and start the new commit message + if current_msg: + separate_commit_msgs.append(current_msg) + + current_msg = clean_paragraph + + # Store the last commit message (if its not empty) + if current_msg: + separate_commit_msgs.append(current_msg) + + return separate_commit_msgs diff --git a/src/semantic_release/commit_parser/emoji.py b/src/semantic_release/commit_parser/emoji.py index df8aeba38..5b8479f18 100644 --- a/src/semantic_release/commit_parser/emoji.py +++ b/src/semantic_release/commit_parser/emoji.py @@ -7,6 +7,7 @@ from functools import reduce from itertools import zip_longest from re import compile as regexp +from textwrap import dedent from typing import Tuple from git.objects.commit import Commit @@ -18,10 +19,14 @@ ParsedMessageResult, ParseResult, ) -from semantic_release.commit_parser.util import parse_paragraphs +from semantic_release.commit_parser.util import ( + deep_copy_commit, + force_str, + parse_paragraphs, +) from semantic_release.enums import LevelBump from semantic_release.errors import InvalidParserOptions -from semantic_release.helpers import sort_numerically +from semantic_release.helpers import sort_numerically, text_reducer logger = logging.getLogger(__name__) @@ -61,7 +66,7 @@ class EmojiParserOptions(ParserOptions): ) """Commit-type prefixes that should result in a patch release bump.""" - other_allowed_tags: Tuple[str, ...] = () + other_allowed_tags: Tuple[str, ...] = (":memo:", ":checkmark:") """Commit-type prefixes that are allowed but do not result in a version bump.""" allowed_tags: Tuple[str, ...] = ( @@ -75,11 +80,6 @@ class EmojiParserOptions(ParserOptions): default_bump_level: LevelBump = LevelBump.NO_RELEASE """The minimum bump level to apply to valid commit message.""" - @property - def tag_to_level(self) -> dict[str, LevelBump]: - """A mapping of commit tags to the level bump they should result in.""" - return self._tag_to_level - parse_linked_issues: bool = False """ Whether to parse linked issues from the commit message. @@ -93,6 +93,15 @@ def tag_to_level(self) -> dict[str, LevelBump]: a whitespace separator. """ + # TODO: breaking change v10, change default to True + parse_squash_commits: bool = False + """Toggle flag for whether or not to parse squash commits""" + + @property + def tag_to_level(self) -> dict[str, LevelBump]: + """A mapping of commit tags to the level bump they should result in.""" + return self._tag_to_level + def __post_init__(self) -> None: self._tag_to_level: dict[str, LevelBump] = { str(tag): level @@ -133,7 +142,7 @@ def __init__(self, options: EmojiParserOptions | None = None) -> None: emojis_in_precedence_order = list(self.options.tag_to_level.keys())[::-1] try: - self.emoji_selector = regexp( + highest_emoji_pattern = regexp( r"(?P%s)" % str.join("|", emojis_in_precedence_order) ) except re.error as err: @@ -148,6 +157,16 @@ def __init__(self, options: EmojiParserOptions | None = None) -> None: ) ) from err + self.emoji_selector = regexp( + str.join( + "", + [ + f"^{highest_emoji_pattern.pattern}", + r"(?:\((?P[^)]+)\))?:?", + ], + ) + ) + # GitHub & Gitea use (#123), GitLab uses (!123), and BitBucket uses (pull request #123) self.mr_selector = regexp( r"[\t ]+\((?:pull request )?(?P[#!]\d+)\)[\t ]*$" @@ -164,6 +183,44 @@ def __init__(self, options: EmojiParserOptions | None = None) -> None: flags=re.MULTILINE | re.IGNORECASE, ) + self.filters = { + "typo-extra-spaces": (regexp(r"(\S) +(\S)"), r"\1 \2"), + "git-header-commit": ( + regexp(r"^[\t ]*commit [0-9a-f]+$\n?", flags=re.MULTILINE), + "", + ), + "git-header-author": ( + regexp(r"^[\t ]*Author: .+$\n?", flags=re.MULTILINE), + "", + ), + "git-header-date": ( + regexp(r"^[\t ]*Date: .+$\n?", flags=re.MULTILINE), + "", + ), + "git-squash-heading": ( + regexp( + r"^[\t ]*Squashed commit of the following:.*$\n?", + flags=re.MULTILINE, + ), + "", + ), + "git-squash-commit-prefix": ( + regexp( + str.join( + "", + [ + r"^(?:[\t ]*[*-][\t ]+|[\t ]+)?", # bullet points or indentation + highest_emoji_pattern.pattern + + r"(\W)", # prior to commit type + ], + ), + flags=re.MULTILINE, + ), + # move commit type to the start of the line + r"\1\2", + ), + } + @staticmethod def get_default_options() -> EmojiParserOptions: return EmojiParserOptions() @@ -210,11 +267,9 @@ def parse_message(self, message: str) -> ParsedMessageResult: # subject = self.mr_selector.sub("", subject).strip() # Search for emoji of the highest importance in the subject - primary_emoji = ( - match.group("type") - if (match := self.emoji_selector.search(subject)) - else "Other" - ) + match = self.emoji_selector.search(subject) + primary_emoji = match.group("type") if match else "Other" + parsed_scope = (match.group("scope") if match else None) or "" level_bump = self.options.tag_to_level.get( primary_emoji, self.options.default_bump_level @@ -236,7 +291,7 @@ def parse_message(self, message: str) -> ParsedMessageResult: bump=level_bump, type=primary_emoji, category=primary_emoji, - scope="", # TODO: add scope support + scope=parsed_scope, # TODO: breaking change v10, removes breaking change footers from descriptions # descriptions=( # descriptions[:1] if level_bump is LevelBump.MAJOR else descriptions @@ -249,17 +304,149 @@ def parse_message(self, message: str) -> ParsedMessageResult: linked_merge_request=linked_merge_request, ) - def parse(self, commit: Commit) -> ParseResult: + def parse_commit(self, commit: Commit) -> ParseResult: + return ParsedCommit.from_parsed_message_result( + commit, self.parse_message(force_str(commit.message)) + ) + + def parse(self, commit: Commit) -> ParseResult | list[ParseResult]: """ - Attempt to parse the commit message with a regular expression into a - ParseResult + Parse a commit message + + If the commit message is a squashed merge commit, it will be split into + multiple commits, each of which will be parsed separately. Single commits + will be returned as a list of a single ParseResult. """ - pmsg_result = self.parse_message(str(commit.message)) + separate_commits: list[Commit] = ( + self.unsquash_commit(commit) + if self.options.parse_squash_commits + else [commit] + ) + + # Parse each commit individually if there were more than one + parsed_commits: list[ParseResult] = list( + map(self.parse_commit, separate_commits) + ) + + def add_linked_merge_request( + parsed_result: ParseResult, mr_number: str + ) -> ParseResult: + return ( + parsed_result + if not isinstance(parsed_result, ParsedCommit) + else ParsedCommit( + **{ + **parsed_result._asdict(), + "linked_merge_request": mr_number, + } + ) + ) + + # TODO: improve this for other VCS systems other than GitHub & BitBucket + # Github works as the first commit in a squash merge commit has the PR number + # appended to the first line of the commit message + lead_commit = next(iter(parsed_commits)) + + if isinstance(lead_commit, ParsedCommit) and lead_commit.linked_merge_request: + # If the first commit has linked merge requests, assume all commits + # are part of the same PR and add the linked merge requests to all + # parsed commits + parsed_commits = [ + lead_commit, + *map( + lambda parsed_result, mr=lead_commit.linked_merge_request: ( # type: ignore[misc] + add_linked_merge_request(parsed_result, mr) + ), + parsed_commits[1:], + ), + ] - logger.debug( - "commit %s introduces a %s level_bump", - commit.hexsha[:8], - pmsg_result.bump, + return parsed_commits + + def unsquash_commit(self, commit: Commit) -> list[Commit]: + # GitHub EXAMPLE: + # ✨(changelog): add autofit_text_width filter to template environment (#1062) + # + # This change adds an equivalent style formatter that can apply a text alignment + # to a maximum width and also maintain an indent over paragraphs of text + # + # * 🌐 Support Japanese language + # + # * ✅(changelog-context): add test cases to check autofit_text_width filter use + # + # `git merge --squash` EXAMPLE: + # Squashed commit of the following: + # + # commit 63ec09b9e844e616dcaa7bae35a0b66671b59fbb + # Author: codejedi365 + # Date: Sun Oct 13 12:05:23 2024 -0000 + # + # ⚡️ (homepage): Lazyload home screen images + # + # + # Return a list of artificial commits (each with a single commit message) + return [ + # create a artificial commit object (copy of original but with modified message) + Commit( + **{ + **deep_copy_commit(commit), + "message": commit_msg, + } + ) + for commit_msg in self.unsquash_commit_message(force_str(commit.message)) + ] or [commit] + + def unsquash_commit_message(self, message: str) -> list[str]: + normalized_message = message.replace("\r", "").strip() + + # split by obvious separate commits (applies to manual git squash merges) + obvious_squashed_commits = self.filters["git-header-commit"][0].split( + normalized_message ) - return ParsedCommit.from_parsed_message_result(commit, pmsg_result) + separate_commit_msgs: list[str] = reduce( + lambda all_msgs, msgs: all_msgs + msgs, + map(self._find_squashed_commits_in_str, obvious_squashed_commits), + [], + ) + + return separate_commit_msgs + + def _find_squashed_commits_in_str(self, message: str) -> list[str]: + separate_commit_msgs: list[str] = [] + current_msg = "" + + for paragraph in filter(None, message.strip().split("\n\n")): + # Apply filters to normalize the paragraph + clean_paragraph = reduce(text_reducer, self.filters.values(), paragraph) + + # remove any filtered (and now empty) paragraphs (ie. the git headers) + if not clean_paragraph.strip(): + continue + + # Check if the paragraph is the start of a new angular commit + if not self.emoji_selector.search(clean_paragraph): + if not separate_commit_msgs and not current_msg: + # if there are no separate commit messages and no current message + # then this is the first commit message + current_msg = dedent(clean_paragraph) + continue + + # append the paragraph as part of the previous commit message + if current_msg: + current_msg += f"\n\n{dedent(clean_paragraph)}" + # else: drop the paragraph + continue + + # Since we found the start of the new commit, store any previous commit + # message separately and start the new commit message + if current_msg: + separate_commit_msgs.append(current_msg) + + current_msg = clean_paragraph + + # Store the last commit message (if its not empty) + if current_msg: + separate_commit_msgs.append(current_msg) + + return separate_commit_msgs diff --git a/src/semantic_release/commit_parser/scipy.py b/src/semantic_release/commit_parser/scipy.py index 5dd82eead..6234cfdf3 100644 --- a/src/semantic_release/commit_parser/scipy.py +++ b/src/semantic_release/commit_parser/scipy.py @@ -74,21 +74,21 @@ def _logged_parse_error(commit: Commit, error: str) -> ParseError: tag_to_section = { "API": "breaking", - "BENCH": "None", + "BENCH": "none", "BLD": "fix", "BUG": "fix", "DEP": "breaking", - "DEV": "None", + "DEV": "none", "DOC": "documentation", "ENH": "feature", "MAINT": "fix", - "REV": "Other", - "STY": "None", - "TST": "None", - "REL": "None", + "REV": "other", + "STY": "none", + "TST": "none", + "REL": "none", # strictly speaking not part of the standard "FEAT": "feature", - "TEST": "None", + "TEST": "none", } diff --git a/src/semantic_release/commit_parser/tag.py b/src/semantic_release/commit_parser/tag.py index 8a400a036..b9a042cc7 100644 --- a/src/semantic_release/commit_parser/tag.py +++ b/src/semantic_release/commit_parser/tag.py @@ -1,5 +1,7 @@ """Legacy commit parser from Python Semantic Release 1.0""" +from __future__ import annotations + import logging import re @@ -41,7 +43,7 @@ class TagCommitParser(CommitParser[ParseResult, TagParserOptions]): def get_default_options() -> TagParserOptions: return TagParserOptions() - def parse(self, commit: Commit) -> ParseResult: + def parse(self, commit: Commit) -> ParseResult | list[ParseResult]: message = str(commit.message) # Attempt to parse the commit message with a regular expression diff --git a/src/semantic_release/commit_parser/util.py b/src/semantic_release/commit_parser/util.py index 60ed63b2f..e6d768428 100644 --- a/src/semantic_release/commit_parser/util.py +++ b/src/semantic_release/commit_parser/util.py @@ -1,5 +1,7 @@ from __future__ import annotations +from contextlib import suppress +from copy import deepcopy from functools import reduce from re import MULTILINE, compile as regexp from typing import TYPE_CHECKING @@ -11,7 +13,9 @@ if TYPE_CHECKING: # pragma: no cover from re import Pattern - from typing import TypedDict + from typing import Any, TypedDict + + from git import Commit class RegexReplaceDef(TypedDict): pattern: Pattern @@ -74,3 +78,44 @@ def parse_paragraphs(text: str) -> list[str]: ], ) ) + + +def force_str(msg: str | bytes | bytearray | memoryview) -> str: + # This shouldn't be a thing but typing is being weird around what + # git.commit.message returns and the memoryview type won't go away + message = msg.tobytes() if isinstance(msg, memoryview) else msg + return ( + message.decode("utf-8") + if isinstance(message, (bytes, bytearray)) + else str(message) + ) + + +def deep_copy_commit(commit: Commit) -> dict[str, Any]: + keys = [ + "repo", + "binsha", + "author", + "authored_date", + "committer", + "committed_date", + "message", + "tree", + "parents", + "encoding", + "gpgsig", + "author_tz_offset", + "committer_tz_offset", + ] + kwargs = {} + for key in keys: + with suppress(ValueError): + if hasattr(commit, key) and (value := getattr(commit, key)) is not None: + if key in ["parents", "repo", "tree"]: + # These tend to have circular references so don't deepcopy them + kwargs[key] = value + continue + + kwargs[key] = deepcopy(value) + + return kwargs diff --git a/src/semantic_release/helpers.py b/src/semantic_release/helpers.py index 0840169ed..c05635718 100644 --- a/src/semantic_release/helpers.py +++ b/src/semantic_release/helpers.py @@ -13,6 +13,7 @@ from urllib.parse import urlsplit if TYPE_CHECKING: # pragma: no cover + from re import Pattern from typing import Iterable @@ -83,6 +84,15 @@ def sort_numerically( ) +def text_reducer(text: str, filter_pair: tuple[Pattern[str], str]) -> str: + """Reduce function to apply mulitple filters to a string""" + if not text: # abort if the paragraph is empty + return text + + filter_pattern, replacement = filter_pair + return filter_pattern.sub(replacement, text) + + def format_arg(value: Any) -> str: """Helper to format an argument an argument for logging""" if type(value) == str: diff --git a/src/semantic_release/version/algorithm.py b/src/semantic_release/version/algorithm.py index f1fe86253..90face7c8 100644 --- a/src/semantic_release/version/algorithm.py +++ b/src/semantic_release/version/algorithm.py @@ -2,6 +2,7 @@ import logging from contextlib import suppress +from functools import reduce from queue import LifoQueue from typing import TYPE_CHECKING, Iterable @@ -346,11 +347,25 @@ def next_version( ) # Step 5. Parse the commits to determine the bump level that should be applied - parsed_levels: set[LevelBump] = { + parsed_levels: set[LevelBump] = { # type: ignore[var-annotated] # too complex for type checkers parsed_result.bump # type: ignore[union-attr] # too complex for type checkers for parsed_result in filter( - lambda parsed_result: isinstance(parsed_result, ParsedCommit), - map(commit_parser.parse, commits_since_last_release), + # Filter out any non-ParsedCommit results (i.e. ParseErrors) + lambda parsed_result: isinstance(parsed_result, ParsedCommit), # type: ignore[arg-type] + reduce( + # Accumulate all parsed results into a single list + lambda accumulated_results, parsed_results: [ + *accumulated_results, + *( + parsed_results + if isinstance(parsed_results, Iterable) + else [parsed_results] # type: ignore[list-item] + ), + ], + # apply the parser to each commit in the history (could return multiple results per commit) + map(commit_parser.parse, commits_since_last_release), + [], + ), ) } diff --git a/tests/conftest.py b/tests/conftest.py index 858f9efd6..933a0cfd1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -25,6 +25,7 @@ from typing import Any, Callable, Generator, Protocol, Sequence, TypedDict from filelock import AcquireReturnProxy + from git import Actor from tests.fixtures.git_repo import RepoActions @@ -382,9 +383,20 @@ def _teardown_cached_dir(directory: Path | str) -> Path: @pytest.fixture(scope="session") -def make_commit_obj() -> MakeCommitObjFn: +def make_commit_obj( + commit_author: Actor, stable_now_date: GetStableDateNowFn +) -> MakeCommitObjFn: def _make_commit(message: str) -> Commit: - return Commit(repo=Repo(), binsha=Commit.NULL_BIN_SHA, message=message) + commit_timestamp = round(stable_now_date().timestamp()) + return Commit( + repo=Repo(), + binsha=Commit.NULL_BIN_SHA, + message=message, + author=commit_author, + authored_date=commit_timestamp, + committer=commit_author, + committed_date=commit_timestamp, + ) return _make_commit diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 0656ec3e2..8a99ba261 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -13,6 +13,12 @@ from git import Actor, Repo from semantic_release.cli.config import ChangelogOutputFormat +from semantic_release.commit_parser.angular import ( + AngularCommitParser, + AngularParserOptions, +) +from semantic_release.commit_parser.emoji import EmojiCommitParser, EmojiParserOptions +from semantic_release.commit_parser.scipy import ScipyCommitParser, ScipyParserOptions from semantic_release.version.version import Version import tests.conftest @@ -46,9 +52,6 @@ from typing_extensions import NotRequired - from semantic_release.commit_parser.angular import AngularCommitParser - from semantic_release.commit_parser.emoji import EmojiCommitParser - from semantic_release.commit_parser.scipy import ScipyCommitParser from semantic_release.hvcs import HvcsBase from semantic_release.hvcs.bitbucket import Bitbucket from semantic_release.hvcs.gitea import Gitea @@ -208,6 +211,16 @@ def __call__(self, branch_name: str, tgt_branch_name: str) -> str: ... class FormatGitHubMergeCommitMsgFn(Protocol): def __call__(self, pr_number: int, branch_name: str) -> str: ... + class FormatGitLabMergeCommitMsgFn(Protocol): + def __call__( + self, + mr_title: str, + mr_number: int, + source_branch: str, + target_branch: str, + closed_issues: list[str], + ) -> str: ... + class CreateMergeCommitFn(Protocol): def __call__( self, @@ -375,6 +388,9 @@ def __call__( self, build_definition: Sequence[RepoActions], key: str ) -> Any: ... + class SeparateSquashedCommitDefFn(Protocol): + def __call__(self, squashed_commit_def: CommitDef) -> list[CommitDef]: ... + @pytest.fixture(scope="session") def deps_files_4_example_git_project( @@ -605,6 +621,48 @@ def _format_merge_commit_msg_git(pr_number: int, branch_name: str) -> str: return _format_merge_commit_msg_git +@pytest.fixture(scope="session") +def format_merge_commit_msg_gitlab() -> FormatGitLabMergeCommitMsgFn: + def _format_merge_commit_msg( + mr_title: str, + mr_number: int, + source_branch: str, + target_branch: str, + closed_issues: list[str], + ) -> str: + """REF: https://docs.gitlab.com/17.8/ee/user/project/merge_requests/commit_templates.html""" + reference = f"{EXAMPLE_REPO_OWNER}/{EXAMPLE_REPO_NAME}!{mr_number}" + issue_statement = ( + "" + if not closed_issues + else str.join( + " ", + [ + "Closes", + str.join( + " and ", [str.join(", ", closed_issues[:-1]), closed_issues[-1]] + ) + if len(closed_issues) > 1 + else closed_issues[0], + ], + ) + ) + return str.join( + "\n\n", + filter( + None, + [ + f"Merge branch '{source_branch}' into '{target_branch}'", + f"{mr_title}", + f"{issue_statement}", + f"See merge request {reference}", + ], + ), + ) + + return _format_merge_commit_msg + + @pytest.fixture(scope="session") def format_squash_commit_msg_git(commit_author: Actor) -> FormatGitSquashCommitMsgFn: def _format_squash_commit_msg_git( @@ -980,6 +1038,94 @@ def _build_configured_base_repo( # noqa: C901 return _build_configured_base_repo +@pytest.fixture(scope="session") +def separate_squashed_commit_def( + default_angular_parser: AngularCommitParser, + default_emoji_parser: EmojiCommitParser, + default_scipy_parser: ScipyCommitParser, +) -> SeparateSquashedCommitDefFn: + message_parsers: dict[ + CommitConvention, AngularCommitParser | EmojiCommitParser | ScipyCommitParser + ] = { + "angular": AngularCommitParser( + options=AngularParserOptions( + **{ + **default_angular_parser.options.__dict__, + "parse_squash_commits": True, + } + ) + ), + "emoji": EmojiCommitParser( + options=EmojiParserOptions( + **{ + **default_emoji_parser.options.__dict__, + "parse_squash_commits": True, + } + ) + ), + "scipy": ScipyCommitParser( + options=ScipyParserOptions( + **{ + **default_scipy_parser.options.__dict__, + "parse_squash_commits": True, + } + ) + ), + } + + def _separate_squashed_commit_def( + squashed_commit_def: CommitDef, + ) -> list[CommitDef]: + commit_type: CommitConvention = "angular" + for parser_name, parser in message_parsers.items(): + if squashed_commit_def["type"] in parser.options.allowed_tags: + commit_type = parser_name + + parser = message_parsers[commit_type] + if not hasattr(parser, "unsquash_commit_message"): + return [squashed_commit_def] + + unsquashed_messages = parser.unsquash_commit_message( + message=squashed_commit_def["msg"] + ) + + return [ + { + "msg": squashed_message, + "type": parsed_result.type, + "category": parsed_result.category, + "desc": str.join( + "\n\n", + ( + [ + # Strip out any MR references (since v9 doesn't) to prep for changelog generatro + # TODO: remove in v10, as the parser will remove the MR reference + str.join( + "(", parsed_result.descriptions[0].split("(")[:-1] + ).strip(), + *parsed_result.descriptions[1:], + ] + if parsed_result.linked_merge_request + else [*parsed_result.descriptions] + ), + ), + "brking_desc": str.join("\n\n", parsed_result.breaking_descriptions), + "scope": parsed_result.scope, + "mr": parsed_result.linked_merge_request or squashed_commit_def["mr"], + "sha": squashed_commit_def["sha"], + "include_in_changelog": True, + "datetime": squashed_commit_def.get("datetime", ""), + } + for parsed_result, squashed_message in iter( + (parser.parse_message(squashed_msg), squashed_msg) + for squashed_msg in unsquashed_messages + ) + if parsed_result is not None + ] + + return _separate_squashed_commit_def + + @pytest.fixture(scope="session") def convert_commit_spec_to_commit_def( get_commit_def_of_angular_commit: GetCommitDefFn, @@ -1037,6 +1183,7 @@ def build_repo_from_definition( # noqa: C901, its required and its just test co create_merge_commit: CreateMergeCommitFn, simulate_change_commits_n_rtn_changelog_entry: SimulateChangeCommitsNReturnChangelogEntryFn, simulate_default_changelog_creation: SimulateDefaultChangelogCreationFn, + separate_squashed_commit_def: SeparateSquashedCommitDefFn, ) -> BuildRepoFromDefinitionFn: def expand_repo_construction_steps( acc: Sequence[RepoActions], step: RepoActions @@ -1193,7 +1340,11 @@ def _build_repo_from_definition( # noqa: C901, its required and its just test c strategy_option=squash_def["strategy_option"], ) if squash_def["commit_def"]["include_in_changelog"]: - current_commits.append(squash_def["commit_def"]) + current_commits.extend( + separate_squashed_commit_def( + squashed_commit_def=squash_def["commit_def"], + ) + ) elif action == RepoActionStep.GIT_MERGE: this_step: RepoActionGitMerge = step_result # type: ignore[assignment] @@ -1468,7 +1619,8 @@ def build_version_entry_markdown( ) # Add commits to section - section_bullets.append(commit_cl_desc) + if commit_cl_desc not in section_bullets: + section_bullets.append(commit_cl_desc) version_entry.extend(sorted(section_bullets)) @@ -1580,7 +1732,8 @@ def build_version_entry_restructured_text( ) # Add commits to section - section_bullets.append(commit_cl_desc) + if commit_cl_desc not in section_bullets: + section_bullets.append(commit_cl_desc) version_entry.extend(sorted(section_bullets)) diff --git a/tests/fixtures/repos/github_flow/repo_w_default_release.py b/tests/fixtures/repos/github_flow/repo_w_default_release.py index 63e0d6f1b..3e572499b 100644 --- a/tests/fixtures/repos/github_flow/repo_w_default_release.py +++ b/tests/fixtures/repos/github_flow/repo_w_default_release.py @@ -135,6 +135,7 @@ def _get_repo_from_defintion( "prerelease": False, }, "tool.semantic_release.allow_zero_version": False, + "tool.semantic_release.commit_parser_options.parse_squash_commits": True, **(extra_configs or {}), }, }, @@ -244,7 +245,7 @@ def _get_repo_from_defintion( }, { "angular": "docs(cli): add cli documentation", - "emoji": ":books: add cli documentation", + "emoji": ":memo: add cli documentation", "scipy": "DOC: add cli documentation", "datetime": next(commit_timestamp_gen), }, diff --git a/tests/unit/semantic_release/commit_parser/test_angular.py b/tests/unit/semantic_release/commit_parser/test_angular.py index b7bf91aac..1ce75734a 100644 --- a/tests/unit/semantic_release/commit_parser/test_angular.py +++ b/tests/unit/semantic_release/commit_parser/test_angular.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Sequence +from textwrap import dedent +from typing import TYPE_CHECKING, Iterable, Sequence import pytest @@ -17,17 +18,556 @@ from tests.conftest import MakeCommitObjFn +# NOTE: GitLab squash commits are not tested because by default +# they don't have any unique attributes of them and they are also +# fully customizable. +# See https://docs.gitlab.com/ee/user/project/merge_requests/commit_templates.html +# It also depends if Fast-Forward merge is enabled because that will +# define if there is a merge commit or not and with that likely no +# Merge Request Number included unless the user adds it. +# TODO: add the recommendation in the PSR documentation is to set your GitLab templates +# to mirror GitHub like references in the first subject line. Will Not matter +# if fast-forward merge is enabled or not. + + +@pytest.mark.parametrize( + "commit_message", ["", "feat(parser\n): Add new parser pattern"] +) def test_parser_raises_unknown_message_style( - default_angular_parser: AngularCommitParser, make_commit_obj: MakeCommitObjFn + default_angular_parser: AngularCommitParser, + make_commit_obj: MakeCommitObjFn, + commit_message: str, ): - assert isinstance(default_angular_parser.parse(make_commit_obj("")), ParseError) - assert isinstance( - default_angular_parser.parse( - make_commit_obj("feat(parser\n): Add new parser pattern") - ), - ParseError, + parsed_results = default_angular_parser.parse(make_commit_obj(commit_message)) + assert isinstance(parsed_results, Iterable) + for result in parsed_results: + assert isinstance(result, ParseError) + + +@pytest.mark.parametrize( + "commit_message, expected_commit_details", + [ + pytest.param( + commit_message, + expected_commit_details, + id=test_id, + ) + for test_id, commit_message, expected_commit_details in [ + ( + "Single commit squashed via BitBucket PR resolution", + dedent( + """\ + Merged in feat/my-awesome-stuff (pull request #10) + + fix(release-config): some commit subject + + An additional description + + Second paragraph with multiple lines + that will be condensed + + Resolves: #12 + Signed-off-by: author + """ + ), + [ + None, + { + "bump": LevelBump.PATCH, + "type": "bug fixes", + "scope": "release-config", + "descriptions": [ + "some commit subject", + "An additional description", + "Second paragraph with multiple lines that will be condensed", + "Resolves: #12", + "Signed-off-by: author ", + ], + "linked_issues": ("#12",), + "linked_merge_request": "#10", + }, + ], + ), + ( + "Multiple commits squashed via BitBucket PR resolution", + dedent( + """\ + Merged in feat/my-awesome-stuff (pull request #10) + + fix(release-config): some commit subject + + An additional description + + Second paragraph with multiple lines + that will be condensed + + Resolves: #12 + Signed-off-by: author + + feat: implemented searching gizmos by keyword + + docs(parser): add new parser pattern + + fix(cli)!: changed option name + + BREAKING CHANGE: A breaking change description + + Closes: #555 + + invalid non-conventional formatted commit + """ + ), + [ + None, + { + "bump": LevelBump.PATCH, + "type": "bug fixes", + "scope": "release-config", + "descriptions": [ + "some commit subject", + "An additional description", + "Second paragraph with multiple lines that will be condensed", + "Resolves: #12", + "Signed-off-by: author ", + ], + "linked_issues": ("#12",), + "linked_merge_request": "#10", + }, + { + "bump": LevelBump.MINOR, + "type": "features", + "descriptions": ["implemented searching gizmos by keyword"], + "linked_merge_request": "#10", + }, + { + "bump": LevelBump.NO_RELEASE, + "type": "documentation", + "scope": "parser", + "descriptions": [ + "add new parser pattern", + ], + "linked_merge_request": "#10", + }, + { + "bump": LevelBump.MAJOR, + "type": "bug fixes", + "scope": "cli", + "descriptions": [ + "changed option name", + "BREAKING CHANGE: A breaking change description", + "Closes: #555", + # This is a bit unusual but its because there is no identifier that will + # identify this as a separate commit so it gets included in the previous commit + "invalid non-conventional formatted commit", + ], + "breaking_descriptions": [ + "A breaking change description", + ], + "linked_issues": ("#555",), + "linked_merge_request": "#10", + }, + ], + ), + ] + ], +) +def test_parser_squashed_commit_bitbucket_squash_style( + default_angular_parser: AngularCommitParser, + make_commit_obj: MakeCommitObjFn, + commit_message: str, + expected_commit_details: Sequence[dict | None], +): + # Setup: Enable squash commit parsing + parser = AngularCommitParser( + options=AngularParserOptions( + **{ + **default_angular_parser.options.__dict__, + "parse_squash_commits": True, + } + ) ) + # Build the commit object and parse it + the_commit = make_commit_obj(commit_message) + parsed_results = parser.parse(the_commit) + + # Validate the results + assert isinstance(parsed_results, Iterable) + assert ( + len(expected_commit_details) == len(parsed_results) + ), f"Expected {len(expected_commit_details)} parsed results, but got {len(parsed_results)}" + + for result, expected in zip(parsed_results, expected_commit_details): + if expected is None: + assert isinstance(result, ParseError) + continue + + assert isinstance(result, ParsedCommit) + # Required + assert expected["bump"] == result.bump + assert expected["type"] == result.type + # Optional + assert expected.get("scope", "") == result.scope + # TODO: v10 change to tuples + assert expected.get("descriptions", []) == result.descriptions + assert expected.get("breaking_descriptions", []) == result.breaking_descriptions + assert expected.get("linked_issues", ()) == result.linked_issues + assert expected.get("linked_merge_request", "") == result.linked_merge_request + + +@pytest.mark.parametrize( + "commit_message, expected_commit_details", + [ + pytest.param( + commit_message, + expected_commit_details, + id=test_id, + ) + for test_id, commit_message, expected_commit_details in [ + ( + "Single commit squashed via manual Git squash merge", + dedent( + """\ + Squashed commit of the following: + + commit 63ec09b9e844e616dcaa7bae35a0b66671b59fbb + Author: author + Date: Sun Jan 19 12:05:23 2025 +0000 + + fix(release-config): some commit subject + + An additional description + + Second paragraph with multiple lines + that will be condensed + + Resolves: #12 + Signed-off-by: author + + """ + ), + [ + { + "bump": LevelBump.PATCH, + "type": "bug fixes", + "scope": "release-config", + "descriptions": [ + "some commit subject", + "An additional description", + "Second paragraph with multiple lines that will be condensed", + "Resolves: #12", + "Signed-off-by: author ", + ], + "linked_issues": ("#12",), + } + ], + ), + ( + "Multiple commits squashed via manual Git squash merge", + dedent( + """\ + Squashed commit of the following: + + commit 63ec09b9e844e616dcaa7bae35a0b66671b59fbb + Author: author + Date: Sun Jan 19 12:05:23 2025 +0000 + + fix(release-config): some commit subject + + An additional description + + Second paragraph with multiple lines + that will be condensed + + Resolves: #12 + Signed-off-by: author + + commit 1f34769bf8352131ad6f4879b8c47becf3c7aa69 + Author: author + Date: Sat Jan 18 10:13:53 2025 +0000 + + feat: implemented searching gizmos by keyword + + commit b2334a64a11ef745a17a2a4034f651e08e8c45a6 + Author: author + Date: Sat Jan 18 10:13:53 2025 +0000 + + docs(parser): add new parser pattern + + commit 5f0292fb5a88c3a46e4a02bec35b85f5228e8e51 + Author: author + Date: Sat Jan 18 10:13:53 2025 +0000 + + fix(cli)!: changed option name + + BREAKING CHANGE: A breaking change description + + Closes: #555 + + commit 2f314e7924be161cfbf220d3b6e2a6189a3b5609 + Author: author + Date: Sat Jan 18 10:13:53 2025 +0000 + + invalid non-conventional formatted commit + """ + ), + [ + { + "bump": LevelBump.PATCH, + "type": "bug fixes", + "scope": "release-config", + "descriptions": [ + "some commit subject", + "An additional description", + "Second paragraph with multiple lines that will be condensed", + "Resolves: #12", + "Signed-off-by: author ", + ], + "linked_issues": ("#12",), + }, + { + "bump": LevelBump.MINOR, + "type": "features", + "descriptions": ["implemented searching gizmos by keyword"], + }, + { + "bump": LevelBump.NO_RELEASE, + "type": "documentation", + "scope": "parser", + "descriptions": [ + "add new parser pattern", + ], + }, + { + "bump": LevelBump.MAJOR, + "type": "bug fixes", + "scope": "cli", + "descriptions": [ + "changed option name", + "BREAKING CHANGE: A breaking change description", + "Closes: #555", + ], + "breaking_descriptions": [ + "A breaking change description", + ], + "linked_issues": ("#555",), + }, + None, + ], + ), + ] + ], +) +def test_parser_squashed_commit_git_squash_style( + default_angular_parser: AngularCommitParser, + make_commit_obj: MakeCommitObjFn, + commit_message: str, + expected_commit_details: Sequence[dict | None], +): + # Setup: Enable squash commit parsing + parser = AngularCommitParser( + options=AngularParserOptions( + **{ + **default_angular_parser.options.__dict__, + "parse_squash_commits": True, + } + ) + ) + + # Build the commit object and parse it + the_commit = make_commit_obj(commit_message) + parsed_results = parser.parse(the_commit) + + # Validate the results + assert isinstance(parsed_results, Iterable) + assert ( + len(expected_commit_details) == len(parsed_results) + ), f"Expected {len(expected_commit_details)} parsed results, but got {len(parsed_results)}" + + for result, expected in zip(parsed_results, expected_commit_details): + if expected is None: + assert isinstance(result, ParseError) + continue + + assert isinstance(result, ParsedCommit) + # Required + assert expected["bump"] == result.bump + assert expected["type"] == result.type + # Optional + assert expected.get("scope", "") == result.scope + # TODO: v10 change to tuples + assert expected.get("descriptions", []) == result.descriptions + assert expected.get("breaking_descriptions", []) == result.breaking_descriptions + assert expected.get("linked_issues", ()) == result.linked_issues + assert expected.get("linked_merge_request", "") == result.linked_merge_request + + +@pytest.mark.parametrize( + "commit_message, expected_commit_details", + [ + pytest.param( + commit_message, + expected_commit_details, + id=test_id, + ) + for test_id, commit_message, expected_commit_details in [ + ( + "Single commit squashed via GitHub PR resolution", + dedent( + """\ + fix(release-config): some commit subject (#10) + + An additional description + + Second paragraph with multiple lines + that will be condensed + + Resolves: #12 + Signed-off-by: author + """ + ), + [ + { + "bump": LevelBump.PATCH, + "type": "bug fixes", + "scope": "release-config", + "descriptions": [ + # TODO: v10 removal of PR number from subject + "some commit subject (#10)", + "An additional description", + "Second paragraph with multiple lines that will be condensed", + "Resolves: #12", + "Signed-off-by: author ", + ], + "linked_issues": ("#12",), + "linked_merge_request": "#10", + }, + ], + ), + ( + "Multiple commits squashed via GitHub PR resolution", + dedent( + """\ + fix(release-config): some commit subject (#10) + + An additional description + + Second paragraph with multiple lines + that will be condensed + + Resolves: #12 + Signed-off-by: author + + * feat: implemented searching gizmos by keyword + + * docs(parser): add new parser pattern + + * fix(cli)!: changed option name + + BREAKING CHANGE: A breaking change description + + Closes: #555 + + * invalid non-conventional formatted commit + """ + ), + [ + { + "bump": LevelBump.PATCH, + "type": "bug fixes", + "scope": "release-config", + "descriptions": [ + # TODO: v10 removal of PR number from subject + "some commit subject (#10)", + "An additional description", + "Second paragraph with multiple lines that will be condensed", + "Resolves: #12", + "Signed-off-by: author ", + ], + "linked_issues": ("#12",), + "linked_merge_request": "#10", + }, + { + "bump": LevelBump.MINOR, + "type": "features", + "descriptions": ["implemented searching gizmos by keyword"], + "linked_merge_request": "#10", + }, + { + "bump": LevelBump.NO_RELEASE, + "type": "documentation", + "scope": "parser", + "descriptions": [ + "add new parser pattern", + ], + "linked_merge_request": "#10", + }, + { + "bump": LevelBump.MAJOR, + "type": "bug fixes", + "scope": "cli", + "descriptions": [ + "changed option name", + "BREAKING CHANGE: A breaking change description", + "Closes: #555", + # This is a bit unusual but its because there is no identifier that will + # identify this as a separate commit so it gets included in the previous commit + "* invalid non-conventional formatted commit", + ], + "breaking_descriptions": [ + "A breaking change description", + ], + "linked_issues": ("#555",), + "linked_merge_request": "#10", + }, + ], + ), + ] + ], +) +def test_parser_squashed_commit_github_squash_style( + default_angular_parser: AngularCommitParser, + make_commit_obj: MakeCommitObjFn, + commit_message: str, + expected_commit_details: Sequence[dict | None], +): + # Setup: Enable squash commit parsing + parser = AngularCommitParser( + options=AngularParserOptions( + **{ + **default_angular_parser.options.__dict__, + "parse_squash_commits": True, + } + ) + ) + + # Build the commit object and parse it + the_commit = make_commit_obj(commit_message) + parsed_results = parser.parse(the_commit) + + # Validate the results + assert isinstance(parsed_results, Iterable) + assert ( + len(expected_commit_details) == len(parsed_results) + ), f"Expected {len(expected_commit_details)} parsed results, but got {len(parsed_results)}" + + for result, expected in zip(parsed_results, expected_commit_details): + if expected is None: + assert isinstance(result, ParseError) + continue + + assert isinstance(result, ParsedCommit) + # Required + assert expected["bump"] == result.bump + assert expected["type"] == result.type + # Optional + assert expected.get("scope", "") == result.scope + # TODO: v10 change to tuples + assert expected.get("descriptions", []) == result.descriptions + assert expected.get("breaking_descriptions", []) == result.breaking_descriptions + assert expected.get("linked_issues", ()) == result.linked_issues + assert expected.get("linked_merge_request", "") == result.linked_merge_request + @pytest.mark.parametrize( "commit_message, bump", @@ -46,8 +586,8 @@ def test_parser_raises_unknown_message_style( ("feat(parser): Add emoji parser", LevelBump.MINOR), ("fix(parser): Fix regex in angular parser", LevelBump.PATCH), ("test(parser): Add a test for angular parser", LevelBump.NO_RELEASE), - ("feat(parser)!: Edit dat parsing stuff", LevelBump.MAJOR), - ("fix!: Edit dat parsing stuff again", LevelBump.MAJOR), + ("feat(parser)!: Edit data parsing stuff", LevelBump.MAJOR), + ("fix!: Edit data parsing stuff again", LevelBump.MAJOR), ("fix: superfix", LevelBump.PATCH), ], ) @@ -57,7 +597,12 @@ def test_parser_returns_correct_bump_level( bump: LevelBump, make_commit_obj: MakeCommitObjFn, ): - result = default_angular_parser.parse(make_commit_obj(commit_message)) + parsed_results = default_angular_parser.parse(make_commit_obj(commit_message)) + + assert isinstance(parsed_results, Iterable) + assert len(parsed_results) == 1 + + result = next(iter(parsed_results)) assert isinstance(result, ParsedCommit) assert result.bump is bump @@ -80,7 +625,12 @@ def test_parser_return_type_from_commit_message( type_: str, make_commit_obj: MakeCommitObjFn, ): - result = default_angular_parser.parse(make_commit_obj(message)) + parsed_results = default_angular_parser.parse(make_commit_obj(message)) + + assert isinstance(parsed_results, Iterable) + assert len(parsed_results) == 1 + + result = next(iter(parsed_results)) assert isinstance(result, ParsedCommit) assert result.type == type_ @@ -105,7 +655,12 @@ def test_parser_return_scope_from_commit_message( scope: str, make_commit_obj: MakeCommitObjFn, ): - result = default_angular_parser.parse(make_commit_obj(message)) + parsed_results = default_angular_parser.parse(make_commit_obj(message)) + + assert isinstance(parsed_results, Iterable) + assert len(parsed_results) == 1 + + result = next(iter(parsed_results)) assert isinstance(result, ParsedCommit) assert result.scope == scope @@ -139,7 +694,12 @@ def test_parser_return_subject_from_commit_message( descriptions: list[str], make_commit_obj: MakeCommitObjFn, ): - result = default_angular_parser.parse(make_commit_obj(message)) + parsed_results = default_angular_parser.parse(make_commit_obj(message)) + + assert isinstance(parsed_results, Iterable) + assert len(parsed_results) == 1 + + result = next(iter(parsed_results)) assert isinstance(result, ParsedCommit) assert result.descriptions == descriptions @@ -181,7 +741,12 @@ def test_parser_return_linked_merge_request_from_commit_message( merge_request_number: str, make_commit_obj: MakeCommitObjFn, ): - result = default_angular_parser.parse(make_commit_obj(message)) + parsed_results = default_angular_parser.parse(make_commit_obj(message)) + + assert isinstance(parsed_results, Iterable) + assert len(parsed_results) == 1 + + result = next(iter(parsed_results)) assert isinstance(result, ParsedCommit) assert merge_request_number == result.linked_merge_request assert subject == result.descriptions[0] @@ -466,7 +1031,12 @@ def test_parser_return_linked_issues_from_commit_message( linked_issues: Sequence[str], make_commit_obj: MakeCommitObjFn, ): - result = default_angular_parser.parse(make_commit_obj(message)) + parsed_results = default_angular_parser.parse(make_commit_obj(message)) + + assert isinstance(parsed_results, Iterable) + assert len(parsed_results) == 1 + + result = next(iter(parsed_results)) assert isinstance(result, ParsedCommit) assert tuple(linked_issues) == result.linked_issues @@ -476,53 +1046,86 @@ def test_parser_return_linked_issues_from_commit_message( ############################## def test_parser_custom_default_level(make_commit_obj: MakeCommitObjFn): options = AngularParserOptions(default_bump_level=LevelBump.MINOR) - parser = AngularCommitParser(options) - result = parser.parse( + parsed_results = AngularCommitParser(options).parse( make_commit_obj("test(parser): Add a test for angular parser") ) + + assert isinstance(parsed_results, Iterable) + + result = next(iter(parsed_results)) assert isinstance(result, ParsedCommit) assert result.bump is LevelBump.MINOR -def test_parser_custom_allowed_types(make_commit_obj: MakeCommitObjFn): - options = AngularParserOptions( - allowed_tags=( - "custom", - "build", - "chore", - "ci", - "docs", - "fix", - "perf", - "style", - "refactor", - "test", +def test_parser_custom_allowed_types( + default_angular_parser: AngularCommitParser, + make_commit_obj: MakeCommitObjFn, +): + new_tag = "custom" + custom_allowed_tags = [*default_angular_parser.options.allowed_tags, new_tag] + parser = AngularCommitParser( + options=AngularParserOptions( + allowed_tags=tuple(custom_allowed_tags), ) ) - parser = AngularCommitParser(options) - res1 = parser.parse(make_commit_obj("custom: ...")) - assert isinstance(res1, ParsedCommit) - assert res1.bump is LevelBump.NO_RELEASE + for commit_type, commit_msg in [ + (new_tag, f"{new_tag}: ..."), # no scope + (new_tag, f"{new_tag}(parser): ..."), # with scope + ("chores", "chore(parser): ..."), # existing, non-release tag + ]: + parsed_results = parser.parse(make_commit_obj(commit_msg)) + assert isinstance(parsed_results, Iterable) + + result = next(iter(parsed_results)) + assert isinstance(result, ParsedCommit) + assert result.type == commit_type + assert result.bump is LevelBump.NO_RELEASE + + +def test_parser_custom_allowed_types_ignores_non_types( + default_angular_parser: AngularCommitParser, make_commit_obj: MakeCommitObjFn +): + banned_tag = "feat" + custom_allowed_tags = [*default_angular_parser.options.allowed_tags] + custom_allowed_tags.remove(banned_tag) + + parser = AngularCommitParser( + options=AngularParserOptions( + allowed_tags=tuple(custom_allowed_tags), + ) + ) - res2 = parser.parse(make_commit_obj("custom(parser): ...")) - assert isinstance(res2, ParsedCommit) - assert res2.type == "custom" + parsed_results = parser.parse(make_commit_obj(f"{banned_tag}(parser): ...")) + assert isinstance(parsed_results, Iterable) - assert isinstance(parser.parse(make_commit_obj("feat(parser): ...")), ParseError) + result = next(iter(parsed_results)) + assert isinstance(result, ParseError) def test_parser_custom_minor_tags(make_commit_obj: MakeCommitObjFn): - options = AngularParserOptions(minor_tags=("docs",)) - parser = AngularCommitParser(options) - res = parser.parse(make_commit_obj("docs: write some docs")) - assert isinstance(res, ParsedCommit) - assert res.bump is LevelBump.MINOR + custom_minor_tag = "docs" + parser = AngularCommitParser( + options=AngularParserOptions(minor_tags=(custom_minor_tag,)) + ) + + parsed_results = parser.parse(make_commit_obj(f"{custom_minor_tag}: ...")) + assert isinstance(parsed_results, Iterable) + + result = next(iter(parsed_results)) + assert isinstance(result, ParsedCommit) + assert result.bump is LevelBump.MINOR def test_parser_custom_patch_tags(make_commit_obj: MakeCommitObjFn): - options = AngularParserOptions(patch_tags=("test",)) - parser = AngularCommitParser(options) - res = parser.parse(make_commit_obj("test(this): added a test")) - assert isinstance(res, ParsedCommit) - assert res.bump is LevelBump.PATCH + custom_patch_tag = "test" + parser = AngularCommitParser( + options=AngularParserOptions(patch_tags=(custom_patch_tag,)) + ) + + parsed_results = parser.parse(make_commit_obj(f"{custom_patch_tag}: ...")) + assert isinstance(parsed_results, Iterable) + + result = next(iter(parsed_results)) + assert isinstance(result, ParsedCommit) + assert result.bump is LevelBump.PATCH diff --git a/tests/unit/semantic_release/commit_parser/test_emoji.py b/tests/unit/semantic_release/commit_parser/test_emoji.py index 50c78ccf4..30c52da41 100644 --- a/tests/unit/semantic_release/commit_parser/test_emoji.py +++ b/tests/unit/semantic_release/commit_parser/test_emoji.py @@ -1,17 +1,17 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Sequence +from textwrap import dedent +from typing import TYPE_CHECKING, Iterable, Sequence import pytest -from semantic_release.commit_parser.token import ParsedCommit +from semantic_release.commit_parser.emoji import EmojiCommitParser, EmojiParserOptions +from semantic_release.commit_parser.token import ParsedCommit, ParseError from semantic_release.enums import LevelBump from tests.const import SUPPORTED_ISSUE_CLOSURE_PREFIXES if TYPE_CHECKING: - from semantic_release.commit_parser.emoji import EmojiCommitParser - from tests.conftest import MakeCommitObjFn @@ -78,8 +78,10 @@ def test_default_emoji_parser( make_commit_obj: MakeCommitObjFn, ): commit = make_commit_obj(commit_message) - result = default_emoji_parser.parse(commit) + parsed_results = default_emoji_parser.parse(commit) + assert isinstance(parsed_results, Iterable) + result = next(iter(parsed_results)) assert isinstance(result, ParsedCommit) assert bump is result.bump assert type_ == result.type @@ -124,7 +126,10 @@ def test_parser_return_linked_merge_request_from_commit_message( merge_request_number: str, make_commit_obj: MakeCommitObjFn, ): - result = default_emoji_parser.parse(make_commit_obj(message)) + parsed_results = default_emoji_parser.parse(make_commit_obj(message)) + assert isinstance(parsed_results, Iterable) + + result = next(iter(parsed_results)) assert isinstance(result, ParsedCommit) assert merge_request_number == result.linked_merge_request assert subject == result.descriptions[0] @@ -410,11 +415,579 @@ def test_parser_return_linked_issues_from_commit_message( make_commit_obj: MakeCommitObjFn, ): # Setup: Enable parsing of linked issues - default_emoji_parser.options.parse_linked_issues = True + parser = EmojiCommitParser( + options=EmojiParserOptions( + **{ + **default_emoji_parser.options.__dict__, + "parse_linked_issues": True, + } + ) + ) # Action - result = default_emoji_parser.parse(make_commit_obj(message)) + parsed_results = parser.parse(make_commit_obj(message)) + assert isinstance(parsed_results, Iterable) + assert len(parsed_results) == 1 # Evaluate (expected -> actual) + result = next(iter(parsed_results)) assert isinstance(result, ParsedCommit) assert tuple(linked_issues) == result.linked_issues + + +@pytest.mark.parametrize( + "commit_message, expected_commit_details", + [ + pytest.param( + commit_message, + expected_commit_details, + id=test_id, + ) + for test_id, commit_message, expected_commit_details in [ + ( + "Single commit squashed via BitBucket PR resolution", + dedent( + """\ + Merged in feat/my-awesome-stuff (pull request #10) + + :bug:(release-config): some commit subject + + An additional description + + Second paragraph with multiple lines + that will be condensed + + Resolves: #12 + Signed-off-by: author + """ + ), + [ + { + "bump": LevelBump.NO_RELEASE, + "type": "Other", + "descriptions": [ + "Merged in feat/my-awesome-stuff (pull request #10)" + ], + "linked_merge_request": "#10", + }, + { + "bump": LevelBump.PATCH, + "type": ":bug:", + "scope": "release-config", + "descriptions": [ + ":bug:(release-config): some commit subject", + "An additional description", + "Second paragraph with multiple lines that will be condensed", + "Resolves: #12", + "Signed-off-by: author ", + ], + "linked_issues": ("#12",), + "linked_merge_request": "#10", + }, + ], + ), + ( + "Multiple commits squashed via BitBucket PR resolution", + dedent( + """\ + Merged in feat/my-awesome-stuff (pull request #10) + + :bug:(release-config): some commit subject + + An additional description + + Second paragraph with multiple lines + that will be condensed + + Resolves: #12 + Signed-off-by: author + + :sparkles: implemented searching gizmos by keyword + + :memo:(parser): add new parser pattern + + :boom::bug: changed option name + + A breaking change description + + Closes: #555 + + invalid non-conventional formatted commit + """ + ), + [ + { + "bump": LevelBump.NO_RELEASE, + "type": "Other", + "descriptions": [ + "Merged in feat/my-awesome-stuff (pull request #10)" + ], + "linked_merge_request": "#10", + }, + { + "bump": LevelBump.PATCH, + "type": ":bug:", + "scope": "release-config", + "descriptions": [ + ":bug:(release-config): some commit subject", + "An additional description", + "Second paragraph with multiple lines that will be condensed", + "Resolves: #12", + "Signed-off-by: author ", + ], + "linked_issues": ("#12",), + "linked_merge_request": "#10", + }, + { + "bump": LevelBump.MINOR, + "type": ":sparkles:", + "descriptions": [ + ":sparkles: implemented searching gizmos by keyword" + ], + "linked_merge_request": "#10", + }, + { + "bump": LevelBump.NO_RELEASE, + "type": ":memo:", + "scope": "parser", + "descriptions": [ + ":memo:(parser): add new parser pattern", + ], + "linked_merge_request": "#10", + }, + { + "bump": LevelBump.MAJOR, + "type": ":boom:", + "scope": "", + "descriptions": [ + ":boom::bug: changed option name", + "A breaking change description", + "Closes: #555", + # This is a bit unusual but its because there is no identifier that will + # identify this as a separate commit so it gets included in the previous commit + "invalid non-conventional formatted commit", + ], + "breaking_descriptions": [ + "A breaking change description", + "Closes: #555", + # This is a bit unusual but its because there is no identifier that will + # identify this as a separate commit so it gets included in the previous commit + "invalid non-conventional formatted commit", + ], + "linked_issues": ("#555",), + "linked_merge_request": "#10", + }, + ], + ), + ] + ], +) +def test_parser_squashed_commit_bitbucket_squash_style( + default_emoji_parser: EmojiCommitParser, + make_commit_obj: MakeCommitObjFn, + commit_message: str, + expected_commit_details: Sequence[dict | None], +): + # Setup: Enable squash commit parsing + parser = EmojiCommitParser( + options=EmojiParserOptions( + **{ + **default_emoji_parser.options.__dict__, + "parse_squash_commits": True, + "parse_linked_issues": True, + } + ) + ) + + # Build the commit object and parse it + the_commit = make_commit_obj(commit_message) + parsed_results = parser.parse(the_commit) + + # Validate the results + assert isinstance(parsed_results, Iterable) + assert ( + len(expected_commit_details) == len(parsed_results) + ), f"Expected {len(expected_commit_details)} parsed results, but got {len(parsed_results)}" + + for result, expected in zip(parsed_results, expected_commit_details): + if expected is None: + assert isinstance(result, ParseError) + continue + + assert isinstance(result, ParsedCommit) + # Required + assert expected["bump"] == result.bump + assert expected["type"] == result.type + # Optional + assert expected.get("scope", "") == result.scope + # TODO: v10 change to tuples + assert expected.get("descriptions", []) == result.descriptions + assert expected.get("breaking_descriptions", []) == result.breaking_descriptions + assert expected.get("linked_issues", ()) == result.linked_issues + assert expected.get("linked_merge_request", "") == result.linked_merge_request + + +@pytest.mark.parametrize( + "commit_message, expected_commit_details", + [ + pytest.param( + commit_message, + expected_commit_details, + id=test_id, + ) + for test_id, commit_message, expected_commit_details in [ + ( + "Single commit squashed via manual Git squash merge", + dedent( + """\ + Squashed commit of the following: + + commit 63ec09b9e844e616dcaa7bae35a0b66671b59fbb + Author: author + Date: Sun Jan 19 12:05:23 2025 +0000 + + :bug:(release-config): some commit subject + + An additional description + + Second paragraph with multiple lines + that will be condensed + + Resolves: #12 + Signed-off-by: author + + """ + ), + [ + { + "bump": LevelBump.PATCH, + "type": ":bug:", + "scope": "release-config", + "descriptions": [ + ":bug:(release-config): some commit subject", + "An additional description", + "Second paragraph with multiple lines that will be condensed", + "Resolves: #12", + "Signed-off-by: author ", + ], + "linked_issues": ("#12",), + } + ], + ), + ( + "Multiple commits squashed via manual Git squash merge", + dedent( + """\ + Squashed commit of the following: + + commit 63ec09b9e844e616dcaa7bae35a0b66671b59fbb + Author: author + Date: Sun Jan 19 12:05:23 2025 +0000 + + :bug:(release-config): some commit subject + + An additional description + + Second paragraph with multiple lines + that will be condensed + + Resolves: #12 + Signed-off-by: author + + commit 1f34769bf8352131ad6f4879b8c47becf3c7aa69 + Author: author + Date: Sat Jan 18 10:13:53 2025 +0000 + + :sparkles: implemented searching gizmos by keyword + + commit b2334a64a11ef745a17a2a4034f651e08e8c45a6 + Author: author + Date: Sat Jan 18 10:13:53 2025 +0000 + + :memo:(parser): add new parser pattern + + commit 5f0292fb5a88c3a46e4a02bec35b85f5228e8e51 + Author: author + Date: Sat Jan 18 10:13:53 2025 +0000 + + :boom::bug: changed option name + + A breaking change description + + Closes: #555 + + commit 2f314e7924be161cfbf220d3b6e2a6189a3b5609 + Author: author + Date: Sat Jan 18 10:13:53 2025 +0000 + + invalid non-conventional formatted commit + """ + ), + [ + { + "bump": LevelBump.PATCH, + "type": ":bug:", + "scope": "release-config", + "descriptions": [ + ":bug:(release-config): some commit subject", + "An additional description", + "Second paragraph with multiple lines that will be condensed", + "Resolves: #12", + "Signed-off-by: author ", + ], + "linked_issues": ("#12",), + }, + { + "bump": LevelBump.MINOR, + "type": ":sparkles:", + "descriptions": [ + ":sparkles: implemented searching gizmos by keyword" + ], + }, + { + "bump": LevelBump.NO_RELEASE, + "type": ":memo:", + "scope": "parser", + "descriptions": [ + ":memo:(parser): add new parser pattern", + ], + }, + { + "bump": LevelBump.MAJOR, + "type": ":boom:", + "descriptions": [ + ":boom::bug: changed option name", + "A breaking change description", + "Closes: #555", + ], + "breaking_descriptions": [ + "A breaking change description", + "Closes: #555", + ], + "linked_issues": ("#555",), + }, + { + "bump": LevelBump.NO_RELEASE, + "type": "Other", + "descriptions": ["invalid non-conventional formatted commit"], + }, + ], + ), + ] + ], +) +def test_parser_squashed_commit_git_squash_style( + default_emoji_parser: EmojiCommitParser, + make_commit_obj: MakeCommitObjFn, + commit_message: str, + expected_commit_details: Sequence[dict | None], +): + # Setup: Enable squash commit parsing + parser = EmojiCommitParser( + options=EmojiParserOptions( + **{ + **default_emoji_parser.options.__dict__, + "parse_squash_commits": True, + "parse_linked_issues": True, + } + ) + ) + + # Build the commit object and parse it + the_commit = make_commit_obj(commit_message) + parsed_results = parser.parse(the_commit) + + # Validate the results + assert isinstance(parsed_results, Iterable) + assert ( + len(expected_commit_details) == len(parsed_results) + ), f"Expected {len(expected_commit_details)} parsed results, but got {len(parsed_results)}" + + for result, expected in zip(parsed_results, expected_commit_details): + if expected is None: + assert isinstance(result, ParseError) + continue + + assert isinstance(result, ParsedCommit) + # Required + assert expected["bump"] == result.bump + assert expected["type"] == result.type + # Optional + assert expected.get("scope", "") == result.scope + # TODO: v10 change to tuples + assert expected.get("descriptions", []) == result.descriptions + assert expected.get("breaking_descriptions", []) == result.breaking_descriptions + assert expected.get("linked_issues", ()) == result.linked_issues + assert expected.get("linked_merge_request", "") == result.linked_merge_request + + +@pytest.mark.parametrize( + "commit_message, expected_commit_details", + [ + pytest.param( + commit_message, + expected_commit_details, + id=test_id, + ) + for test_id, commit_message, expected_commit_details in [ + ( + "Single commit squashed via GitHub PR resolution", + dedent( + """\ + :bug:(release-config): some commit subject (#10) + + An additional description + + Second paragraph with multiple lines + that will be condensed + + Resolves: #12 + Signed-off-by: author + """ + ), + [ + { + "bump": LevelBump.PATCH, + "type": ":bug:", + "scope": "release-config", + "descriptions": [ + # TODO: v10 removal of PR number from subject + ":bug:(release-config): some commit subject (#10)", + "An additional description", + "Second paragraph with multiple lines that will be condensed", + "Resolves: #12", + "Signed-off-by: author ", + ], + "linked_issues": ("#12",), + "linked_merge_request": "#10", + }, + ], + ), + ( + "Multiple commits squashed via GitHub PR resolution", + dedent( + """\ + :bug:(release-config): some commit subject (#10) + + An additional description + + Second paragraph with multiple lines + that will be condensed + + Resolves: #12 + Signed-off-by: author + + * :sparkles: implemented searching gizmos by keyword + + * :memo:(parser): add new parser pattern + + * :boom::bug: changed option name + + A breaking change description + + Closes: #555 + + * invalid non-conventional formatted commit + """ + ), + [ + { + "bump": LevelBump.PATCH, + "type": ":bug:", + "scope": "release-config", + "descriptions": [ + # TODO: v10 removal of PR number from subject + ":bug:(release-config): some commit subject (#10)", + "An additional description", + "Second paragraph with multiple lines that will be condensed", + "Resolves: #12", + "Signed-off-by: author ", + ], + "linked_issues": ("#12",), + "linked_merge_request": "#10", + }, + { + "bump": LevelBump.MINOR, + "type": ":sparkles:", + "descriptions": [ + ":sparkles: implemented searching gizmos by keyword" + ], + "linked_merge_request": "#10", + }, + { + "bump": LevelBump.NO_RELEASE, + "type": ":memo:", + "scope": "parser", + "descriptions": [ + ":memo:(parser): add new parser pattern", + ], + "linked_merge_request": "#10", + }, + { + "bump": LevelBump.MAJOR, + "type": ":boom:", + "scope": "", + "descriptions": [ + ":boom::bug: changed option name", + "A breaking change description", + "Closes: #555", + # This is a bit unusual but its because there is no identifier that will + # identify this as a separate commit so it gets included in the previous commit + "* invalid non-conventional formatted commit", + ], + "breaking_descriptions": [ + "A breaking change description", + "Closes: #555", + "* invalid non-conventional formatted commit", + ], + "linked_issues": ("#555",), + "linked_merge_request": "#10", + }, + ], + ), + ] + ], +) +def test_parser_squashed_commit_github_squash_style( + default_emoji_parser: EmojiCommitParser, + make_commit_obj: MakeCommitObjFn, + commit_message: str, + expected_commit_details: Sequence[dict | None], +): + # Setup: Enable squash commit parsing + parser = EmojiCommitParser( + options=EmojiParserOptions( + **{ + **default_emoji_parser.options.__dict__, + "parse_squash_commits": True, + "parse_linked_issues": True, + } + ) + ) + + # Build the commit object and parse it + the_commit = make_commit_obj(commit_message) + parsed_results = parser.parse(the_commit) + + # Validate the results + assert isinstance(parsed_results, Iterable) + assert ( + len(expected_commit_details) == len(parsed_results) + ), f"Expected {len(expected_commit_details)} parsed results, but got {len(parsed_results)}" + + for result, expected in zip(parsed_results, expected_commit_details): + if expected is None: + assert isinstance(result, ParseError) + continue + + assert isinstance(result, ParsedCommit) + # Required + assert expected["bump"] == result.bump + assert expected["type"] == result.type + # Optional + assert expected.get("scope", "") == result.scope + # TODO: v10 change to tuples + assert expected.get("descriptions", []) == result.descriptions + assert expected.get("breaking_descriptions", []) == result.breaking_descriptions + assert expected.get("linked_issues", ()) == result.linked_issues + assert expected.get("linked_merge_request", "") == result.linked_merge_request diff --git a/tests/unit/semantic_release/commit_parser/test_scipy.py b/tests/unit/semantic_release/commit_parser/test_scipy.py index 69b95a5d1..8fc64fea4 100644 --- a/tests/unit/semantic_release/commit_parser/test_scipy.py +++ b/tests/unit/semantic_release/commit_parser/test_scipy.py @@ -1,25 +1,42 @@ from __future__ import annotations from re import compile as regexp -from typing import TYPE_CHECKING, Sequence +from textwrap import dedent +from typing import TYPE_CHECKING, Iterable, Sequence import pytest -from semantic_release.commit_parser.scipy import tag_to_section -from semantic_release.commit_parser.token import ParsedCommit +from semantic_release.commit_parser.scipy import ( + ScipyCommitParser, + ScipyParserOptions, + tag_to_section, +) +from semantic_release.commit_parser.token import ParsedCommit, ParseError from semantic_release.enums import LevelBump from tests.const import SUPPORTED_ISSUE_CLOSURE_PREFIXES if TYPE_CHECKING: - from semantic_release.commit_parser.scipy import ScipyCommitParser - from tests.conftest import MakeCommitObjFn unwordwrap = regexp(r"((? + """ + ), + [ + None, + { + "bump": LevelBump.PATCH, + "type": "fix", + "scope": "release-config", + "descriptions": [ + "some commit subject", + "An additional description", + "Second paragraph with multiple lines that will be condensed", + "Resolves: #12", + "Signed-off-by: author ", + ], + "linked_issues": ("#12",), + "linked_merge_request": "#10", + }, + ], + ), + ( + "Multiple commits squashed via BitBucket PR resolution", + dedent( + """\ + Merged in feat/my-awesome-stuff (pull request #10) + + BUG(release-config): some commit subject + + An additional description + + Second paragraph with multiple lines + that will be condensed + + Resolves: #12 + Signed-off-by: author + + ENH: implemented searching gizmos by keyword + + DOC(parser): add new parser pattern + + MAINT(cli)!: changed option name + + BREAKING CHANGE: A breaking change description + + Closes: #555 + + invalid non-conventional formatted commit + """ + ), + [ + None, + { + "bump": LevelBump.PATCH, + "type": "fix", + "scope": "release-config", + "descriptions": [ + "some commit subject", + "An additional description", + "Second paragraph with multiple lines that will be condensed", + "Resolves: #12", + "Signed-off-by: author ", + ], + "linked_issues": ("#12",), + "linked_merge_request": "#10", + }, + { + "bump": LevelBump.MINOR, + "type": "feature", + "descriptions": ["implemented searching gizmos by keyword"], + "linked_merge_request": "#10", + }, + { + "bump": LevelBump.NO_RELEASE, + "type": "documentation", + "scope": "parser", + "descriptions": [ + "add new parser pattern", + ], + "linked_merge_request": "#10", + }, + { + "bump": LevelBump.MAJOR, + "type": "fix", + "scope": "cli", + "descriptions": [ + "changed option name", + "BREAKING CHANGE: A breaking change description", + "Closes: #555", + # This is a bit unusual but its because there is no identifier that will + # identify this as a separate commit so it gets included in the previous commit + "invalid non-conventional formatted commit", + ], + "breaking_descriptions": [ + "A breaking change description", + ], + "linked_issues": ("#555",), + "linked_merge_request": "#10", + }, + ], + ), + ] + ], +) +def test_parser_squashed_commit_bitbucket_squash_style( + default_scipy_parser: ScipyCommitParser, + make_commit_obj: MakeCommitObjFn, + commit_message: str, + expected_commit_details: Sequence[dict | None], +): + # Setup: Enable squash commit parsing + parser = ScipyCommitParser( + options=ScipyParserOptions( + **{ + **default_scipy_parser.options.__dict__, + "parse_squash_commits": True, + } + ) + ) + + # Build the commit object and parse it + the_commit = make_commit_obj(commit_message) + parsed_results = parser.parse(the_commit) + + # Validate the results + assert isinstance(parsed_results, Iterable) + assert ( + len(expected_commit_details) == len(parsed_results) + ), f"Expected {len(expected_commit_details)} parsed results, but got {len(parsed_results)}" + + for result, expected in zip(parsed_results, expected_commit_details): + if expected is None: + assert isinstance(result, ParseError) + continue + + assert isinstance(result, ParsedCommit) + # Required + assert expected["bump"] == result.bump + assert expected["type"] == result.type + # Optional + assert expected.get("scope", "") == result.scope + # TODO: v10 change to tuples + assert expected.get("descriptions", []) == result.descriptions + assert expected.get("breaking_descriptions", []) == result.breaking_descriptions + assert expected.get("linked_issues", ()) == result.linked_issues + assert expected.get("linked_merge_request", "") == result.linked_merge_request + + +@pytest.mark.parametrize( + "commit_message, expected_commit_details", + [ + pytest.param( + commit_message, + expected_commit_details, + id=test_id, + ) + for test_id, commit_message, expected_commit_details in [ + ( + "Single commit squashed via manual Git squash merge", + dedent( + """\ + Squashed commit of the following: + + commit 63ec09b9e844e616dcaa7bae35a0b66671b59fbb + Author: author + Date: Sun Jan 19 12:05:23 2025 +0000 + + BUG(release-config): some commit subject + + An additional description + + Second paragraph with multiple lines + that will be condensed + + Resolves: #12 + Signed-off-by: author + + """ + ), + [ + { + "bump": LevelBump.PATCH, + "type": "fix", + "scope": "release-config", + "descriptions": [ + "some commit subject", + "An additional description", + "Second paragraph with multiple lines that will be condensed", + "Resolves: #12", + "Signed-off-by: author ", + ], + "linked_issues": ("#12",), + } + ], + ), + ( + "Multiple commits squashed via manual Git squash merge", + dedent( + """\ + Squashed commit of the following: + + commit 63ec09b9e844e616dcaa7bae35a0b66671b59fbb + Author: author + Date: Sun Jan 19 12:05:23 2025 +0000 + + BUG(release-config): some commit subject + + An additional description + + Second paragraph with multiple lines + that will be condensed + + Resolves: #12 + Signed-off-by: author + + commit 1f34769bf8352131ad6f4879b8c47becf3c7aa69 + Author: author + Date: Sat Jan 18 10:13:53 2025 +0000 + + ENH: implemented searching gizmos by keyword + + commit b2334a64a11ef745a17a2a4034f651e08e8c45a6 + Author: author + Date: Sat Jan 18 10:13:53 2025 +0000 + + DOC(parser): add new parser pattern + + commit 5f0292fb5a88c3a46e4a02bec35b85f5228e8e51 + Author: author + Date: Sat Jan 18 10:13:53 2025 +0000 + + MAINT(cli): changed option name + + BREAKING CHANGE: A breaking change description + + Closes: #555 + + commit 2f314e7924be161cfbf220d3b6e2a6189a3b5609 + Author: author + Date: Sat Jan 18 10:13:53 2025 +0000 + + invalid non-conventional formatted commit + """ + ), + [ + { + "bump": LevelBump.PATCH, + "type": "fix", + "scope": "release-config", + "descriptions": [ + "some commit subject", + "An additional description", + "Second paragraph with multiple lines that will be condensed", + "Resolves: #12", + "Signed-off-by: author ", + ], + "linked_issues": ("#12",), + }, + { + "bump": LevelBump.MINOR, + "type": "feature", + "descriptions": ["implemented searching gizmos by keyword"], + }, + { + "bump": LevelBump.NO_RELEASE, + "type": "documentation", + "scope": "parser", + "descriptions": [ + "add new parser pattern", + ], + }, + { + "bump": LevelBump.MAJOR, + "type": "fix", + "scope": "cli", + "descriptions": [ + "changed option name", + "BREAKING CHANGE: A breaking change description", + "Closes: #555", + ], + "breaking_descriptions": [ + "A breaking change description", + ], + "linked_issues": ("#555",), + }, + None, + ], + ), + ] + ], +) +def test_parser_squashed_commit_git_squash_style( + default_scipy_parser: ScipyCommitParser, + make_commit_obj: MakeCommitObjFn, + commit_message: str, + expected_commit_details: Sequence[dict | None], +): + # Setup: Enable squash commit parsing + parser = ScipyCommitParser( + options=ScipyParserOptions( + **{ + **default_scipy_parser.options.__dict__, + "parse_squash_commits": True, + } + ) + ) + + # Build the commit object and parse it + the_commit = make_commit_obj(commit_message) + parsed_results = parser.parse(the_commit) + + # Validate the results + assert isinstance(parsed_results, Iterable) + assert ( + len(expected_commit_details) == len(parsed_results) + ), f"Expected {len(expected_commit_details)} parsed results, but got {len(parsed_results)}" + + for result, expected in zip(parsed_results, expected_commit_details): + if expected is None: + assert isinstance(result, ParseError) + continue + + assert isinstance(result, ParsedCommit) + # Required + assert expected["bump"] == result.bump + assert expected["type"] == result.type + # Optional + assert expected.get("scope", "") == result.scope + # TODO: v10 change to tuples + assert expected.get("descriptions", []) == result.descriptions + assert expected.get("breaking_descriptions", []) == result.breaking_descriptions + assert expected.get("linked_issues", ()) == result.linked_issues + assert expected.get("linked_merge_request", "") == result.linked_merge_request + + +@pytest.mark.parametrize( + "commit_message, expected_commit_details", + [ + pytest.param( + commit_message, + expected_commit_details, + id=test_id, + ) + for test_id, commit_message, expected_commit_details in [ + ( + "Single commit squashed via GitHub PR resolution", + dedent( + """\ + BUG(release-config): some commit subject (#10) + + An additional description + + Second paragraph with multiple lines + that will be condensed + + Resolves: #12 + Signed-off-by: author + """ + ), + [ + { + "bump": LevelBump.PATCH, + "type": "fix", + "scope": "release-config", + "descriptions": [ + "some commit subject (#10)", + "An additional description", + "Second paragraph with multiple lines that will be condensed", + "Resolves: #12", + "Signed-off-by: author ", + ], + "linked_issues": ("#12",), + "linked_merge_request": "#10", + }, + ], + ), + ( + "Multiple commits squashed via GitHub PR resolution", + dedent( + """\ + BUG(release-config): some commit subject (#10) + + An additional description + + Second paragraph with multiple lines + that will be condensed + + Resolves: #12 + Signed-off-by: author + + * ENH: implemented searching gizmos by keyword + + * DOC(parser): add new parser pattern + + * MAINT(cli)!: changed option name + + BREAKING CHANGE: A breaking change description + + Closes: #555 + + * invalid non-conventional formatted commit + """ + ), + [ + { + "bump": LevelBump.PATCH, + "type": "fix", + "scope": "release-config", + "descriptions": [ + # TODO: v10 removal of PR number from subject + "some commit subject (#10)", + "An additional description", + "Second paragraph with multiple lines that will be condensed", + "Resolves: #12", + "Signed-off-by: author ", + ], + "linked_issues": ("#12",), + "linked_merge_request": "#10", + }, + { + "bump": LevelBump.MINOR, + "type": "feature", + "descriptions": ["implemented searching gizmos by keyword"], + "linked_merge_request": "#10", + }, + { + "bump": LevelBump.NO_RELEASE, + "type": "documentation", + "scope": "parser", + "descriptions": [ + "add new parser pattern", + ], + "linked_merge_request": "#10", + }, + { + "bump": LevelBump.MAJOR, + "type": "fix", + "scope": "cli", + "descriptions": [ + "changed option name", + "BREAKING CHANGE: A breaking change description", + "Closes: #555", + # This is a bit unusual but its because there is no identifier that will + # identify this as a separate commit so it gets included in the previous commit + "* invalid non-conventional formatted commit", + ], + "breaking_descriptions": [ + "A breaking change description", + ], + "linked_issues": ("#555",), + "linked_merge_request": "#10", + }, + ], + ), + ] + ], +) +def test_parser_squashed_commit_github_squash_style( + default_scipy_parser: ScipyCommitParser, + make_commit_obj: MakeCommitObjFn, + commit_message: str, + expected_commit_details: Sequence[dict | None], +): + # Setup: Enable squash commit parsing + parser = ScipyCommitParser( + options=ScipyParserOptions( + **{ + **default_scipy_parser.options.__dict__, + "parse_squash_commits": True, + } + ) + ) + + # Build the commit object and parse it + the_commit = make_commit_obj(commit_message) + parsed_results = parser.parse(the_commit) + + # Validate the results + assert isinstance(parsed_results, Iterable) + assert ( + len(expected_commit_details) == len(parsed_results) + ), f"Expected {len(expected_commit_details)} parsed results, but got {len(parsed_results)}" + + for result, expected in zip(parsed_results, expected_commit_details): + if expected is None: + assert isinstance(result, ParseError) + continue + + assert isinstance(result, ParsedCommit) + # Required + assert expected["bump"] == result.bump + assert expected["type"] == result.type + # Optional + assert expected.get("scope", "") == result.scope + # TODO: v10 change to tuples + assert expected.get("descriptions", []) == result.descriptions + assert expected.get("breaking_descriptions", []) == result.breaking_descriptions + assert expected.get("linked_issues", ()) == result.linked_issues + assert expected.get("linked_merge_request", "") == result.linked_merge_request + + @pytest.mark.parametrize( "message, linked_issues", # TODO: in v10, we will remove the issue reference footers from the descriptions @@ -465,6 +1021,11 @@ def test_parser_return_linked_issues_from_commit_message( linked_issues: Sequence[str], make_commit_obj: MakeCommitObjFn, ): - result = default_scipy_parser.parse(make_commit_obj(message)) + parsed_results = default_scipy_parser.parse(make_commit_obj(message)) + + assert isinstance(parsed_results, Iterable) + assert len(parsed_results) == 1 + + result = next(iter(parsed_results)) assert isinstance(result, ParsedCommit) assert tuple(linked_issues) == result.linked_issues From cdfda70b6df73f3e4917cfd498f4bf9fed29ea08 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Sun, 26 Jan 2025 01:20:29 +0000 Subject: [PATCH 012/160] 9.17.0 Automatically generated by python-semantic-release --- CHANGELOG.md | 96 ++++++++++++++++++++++ docs/automatic-releases/github-actions.rst | 14 ++-- pyproject.toml | 2 +- src/semantic_release/__init__.py | 2 +- 4 files changed, 105 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e4a8c2aa..10374d99b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,102 @@ # CHANGELOG +## v9.17.0 (2025-01-26) + +### Bug Fixes + +- **github-action**: Disable writing python bytecode in action execution + ([#1152](https://github.com/python-semantic-release/python-semantic-release/pull/1152), + [`315ae21`](https://github.com/python-semantic-release/python-semantic-release/commit/315ae2176e211b00b13374560d81e127a3065d1a)) + +File permission issues can occur when using the github-action and dynamically loading files from the + repository. When importing, python generally will create bytecode files and write to disk as the + current user. Because the default user in the github action is root, those files are written as + root which means when it returns to the rest of the workflow, those files cannot be modified or + deleted. With this change, we disable writing of bytecode files which prevents any failures that + may result after the python-semantic-release action is executed. + +### Features + +- **changelog**: Add `sort_numerically` filter function to template environment + ([#1146](https://github.com/python-semantic-release/python-semantic-release/pull/1146), + [`7792388`](https://github.com/python-semantic-release/python-semantic-release/commit/77923885c585171e8888aacde989837ecbabf3fc)) + +* test(helpers): add unit tests for various prefixed number lists + +* test(changelog-context): add unit tests to validate use of `sort_numerically` filter + +* test(release-notes-context): add unit tests to validate use of `sort_numerically` filter + +* refactor(util): relocate `sort_numerically` function to top level + +* docs(changelog-templates): add description for new `sort_numerically` filter function + +- **config**: Extend support of remote urls aliased using git `insteadOf` configurations + ([#1151](https://github.com/python-semantic-release/python-semantic-release/pull/1151), + [`4045037`](https://github.com/python-semantic-release/python-semantic-release/commit/40450375c7951dafddb09bef8001db7180d95f3a)) + +Resolves: #1150 + +* refactor(hvcs): add validation of git urls upon vcs client initialization + +* test(hvcs): refactor unit test to catch validation error immediately of bad git url + +* test(config): add test case of a git `insteadOf` aliased origin + +- **parsers**: Parse squashed commits individually + ([#1112](https://github.com/python-semantic-release/python-semantic-release/pull/1112), + [`cf785ca`](https://github.com/python-semantic-release/python-semantic-release/commit/cf785ca79a49eb4ee95c148e0ae6a19e230e915c)) + +* test(parser-angular): update unit tests for parser return value compatibility + +* test(parser-scipy): update unit tests for parser return value compatibility + +* test(parser-emoji): update unit tests for parser return value compatibility + +* feat(version): parse squashed commits individually + +adds the functionality to separately parse each commit message within a squashed merge commit to + detect combined commit types that could change the version bump + +* feat(changelog): parse squashed commits individually + +adds functionality to separately parse each commit message within a squashed merge commit which + decouples the commits into their respective type categories in the changelog. + +* refactor(helpers): centralize utility for applying multiple text substitutions + +* feat(parser-angular): upgrade angular parser to parse squashed commits individually + +Resolves: #1085 + +* feat(parser-angular): apply PR/MR numbers to all parsed commits from a squash merge + +* feat(parser-emoji): add functionality to interpret scopes from gitmoji commit messages + +* feat(parser-emoji): upgrade emoji parser to parse squashed commits individually + +* test(fixtures): adjust parser for squashed commit definitions + +* test(fixtures): change config of github flow repo to parse squash commits + +* test(fixtures): add fixture to create gitlab formatted merge commit + +* refactor(parser-scipy): standardize all category spelling applied to commits + +* docs(commit-parsing): add description for squash commit evaluation option of default parsers + +* docs(configuration): update the `commit_parser_options` setting description + +### Performance Improvements + +- **logging**: Remove irrelevant debug logging statements + ([#1147](https://github.com/python-semantic-release/python-semantic-release/pull/1147), + [`f1ef4ec`](https://github.com/python-semantic-release/python-semantic-release/commit/f1ef4ecf5f22684a870b958f87d1ca2650e612db)) + +* refactor: adjust logging output + + ## v9.16.1 (2025-01-12) ### Bug Fixes diff --git a/docs/automatic-releases/github-actions.rst b/docs/automatic-releases/github-actions.rst index 2fc8c8721..792bfc95f 100644 --- a/docs/automatic-releases/github-actions.rst +++ b/docs/automatic-releases/github-actions.rst @@ -337,7 +337,7 @@ before the :ref:`version ` subcommand. .. code:: yaml - - uses: python-semantic-release/python-semantic-release@v9.16.1 + - uses: python-semantic-release/python-semantic-release@v9.17.0 with: root_options: "-vv --noop" @@ -576,7 +576,7 @@ before the :ref:`publish ` subcommand. .. code:: yaml - - uses: python-semantic-release/publish-action@v9.16.1 + - uses: python-semantic-release/publish-action@v9.17.0 with: root_options: "-vv --noop" @@ -684,7 +684,7 @@ to the GitHub Release Assets as well. - name: Action | Semantic Version Release id: release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.16.1 + uses: python-semantic-release/python-semantic-release@v9.17.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} git_committer_name: "github-actions" @@ -695,7 +695,7 @@ to the GitHub Release Assets as well. if: steps.release.outputs.released == 'true' - name: Publish | Upload to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.16.1 + uses: python-semantic-release/publish-action@v9.17.0 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -744,7 +744,7 @@ The equivalent GitHub Action configuration would be: - name: Action | Semantic Version Release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.16.1 + uses: python-semantic-release/python-semantic-release@v9.17.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} force: patch @@ -772,13 +772,13 @@ Publish Action. .. code:: yaml - name: Release Project 1 - uses: python-semantic-release/python-semantic-release@v9.16.1 + uses: python-semantic-release/python-semantic-release@v9.17.0 with: directory: ./project1 github_token: ${{ secrets.GITHUB_TOKEN }} - name: Release Project 2 - uses: python-semantic-release/python-semantic-release@v9.16.1 + uses: python-semantic-release/python-semantic-release@v9.17.0 with: directory: ./project2 github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/pyproject.toml b/pyproject.toml index f7ab040c5..c708736d1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.16.1" +version = "9.17.0" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } diff --git a/src/semantic_release/__init__.py b/src/semantic_release/__init__.py index c39aa0c23..8489a3e60 100644 --- a/src/semantic_release/__init__.py +++ b/src/semantic_release/__init__.py @@ -24,7 +24,7 @@ tags_and_versions, ) -__version__ = "9.16.1" +__version__ = "9.17.0" __all__ = [ "CommitParser", From 7c14f81d69339c2029d98ee027bca91325047765 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Jan 2025 21:17:49 -0500 Subject: [PATCH 013/160] ci(deps): bump `python-semantic-release/publish-action@v9.16.1` action to 9.17.0 (#1154) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/cicd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index de41ec0aa..abffd6fba 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -138,7 +138,7 @@ jobs: build: false - name: Release | Add distribution artifacts to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.16.1 + uses: python-semantic-release/publish-action@v9.17.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} tag: ${{ steps.release.outputs.tag }} From 2f18a6debfa6ef3afcc5611a3e09262998f2d4bf Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 27 Jan 2025 00:25:13 -0500 Subject: [PATCH 014/160] docs(changelog): add formatted changelog into hosted documentation (#1155) * chore(changelog): configure custom release notes & RST changelog for PSR * chore(changelog): convert project changelog to an updating RST --- CHANGELOG.md | 5074 --------------- CHANGELOG.rst | 5415 +++++++++++++++++ .../.components/changelog_1.0.0.rst.j2 | 254 + .../.components/changelog_header.rst.j2 | 10 + .../.components/changelog_init.rst.j2 | 39 + .../.components/changelog_update.rst.j2 | 71 + .../.components/changes.md.j2 | 88 + .../.components/changes.rst.j2 | 133 + .../.components/first_release.rst.j2 | 22 + .../.components/macros.md.j2 | 200 + .../.components/macros.rst.j2 | 253 + .../.components/unreleased_changes.rst.j2 | 10 + .../.components/versioned_changes.md.j2 | 15 + .../.components/versioned_changes.rst.j2 | 25 + config/release-templates/.release_notes.md.j2 | 120 + config/release-templates/CHANGELOG.rst.j2 | 22 + docs/index.rst | 1 + docs/psr_changelog.rst | 1 + pyproject.toml | 5 + 19 files changed, 6684 insertions(+), 5074 deletions(-) delete mode 100644 CHANGELOG.md create mode 100644 CHANGELOG.rst create mode 100644 config/release-templates/.components/changelog_1.0.0.rst.j2 create mode 100644 config/release-templates/.components/changelog_header.rst.j2 create mode 100644 config/release-templates/.components/changelog_init.rst.j2 create mode 100644 config/release-templates/.components/changelog_update.rst.j2 create mode 100644 config/release-templates/.components/changes.md.j2 create mode 100644 config/release-templates/.components/changes.rst.j2 create mode 100644 config/release-templates/.components/first_release.rst.j2 create mode 100644 config/release-templates/.components/macros.md.j2 create mode 100644 config/release-templates/.components/macros.rst.j2 create mode 100644 config/release-templates/.components/unreleased_changes.rst.j2 create mode 100644 config/release-templates/.components/versioned_changes.md.j2 create mode 100644 config/release-templates/.components/versioned_changes.rst.j2 create mode 100644 config/release-templates/.release_notes.md.j2 create mode 100644 config/release-templates/CHANGELOG.rst.j2 create mode 100644 docs/psr_changelog.rst diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 10374d99b..000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,5074 +0,0 @@ -# CHANGELOG - - -## v9.17.0 (2025-01-26) - -### Bug Fixes - -- **github-action**: Disable writing python bytecode in action execution - ([#1152](https://github.com/python-semantic-release/python-semantic-release/pull/1152), - [`315ae21`](https://github.com/python-semantic-release/python-semantic-release/commit/315ae2176e211b00b13374560d81e127a3065d1a)) - -File permission issues can occur when using the github-action and dynamically loading files from the - repository. When importing, python generally will create bytecode files and write to disk as the - current user. Because the default user in the github action is root, those files are written as - root which means when it returns to the rest of the workflow, those files cannot be modified or - deleted. With this change, we disable writing of bytecode files which prevents any failures that - may result after the python-semantic-release action is executed. - -### Features - -- **changelog**: Add `sort_numerically` filter function to template environment - ([#1146](https://github.com/python-semantic-release/python-semantic-release/pull/1146), - [`7792388`](https://github.com/python-semantic-release/python-semantic-release/commit/77923885c585171e8888aacde989837ecbabf3fc)) - -* test(helpers): add unit tests for various prefixed number lists - -* test(changelog-context): add unit tests to validate use of `sort_numerically` filter - -* test(release-notes-context): add unit tests to validate use of `sort_numerically` filter - -* refactor(util): relocate `sort_numerically` function to top level - -* docs(changelog-templates): add description for new `sort_numerically` filter function - -- **config**: Extend support of remote urls aliased using git `insteadOf` configurations - ([#1151](https://github.com/python-semantic-release/python-semantic-release/pull/1151), - [`4045037`](https://github.com/python-semantic-release/python-semantic-release/commit/40450375c7951dafddb09bef8001db7180d95f3a)) - -Resolves: #1150 - -* refactor(hvcs): add validation of git urls upon vcs client initialization - -* test(hvcs): refactor unit test to catch validation error immediately of bad git url - -* test(config): add test case of a git `insteadOf` aliased origin - -- **parsers**: Parse squashed commits individually - ([#1112](https://github.com/python-semantic-release/python-semantic-release/pull/1112), - [`cf785ca`](https://github.com/python-semantic-release/python-semantic-release/commit/cf785ca79a49eb4ee95c148e0ae6a19e230e915c)) - -* test(parser-angular): update unit tests for parser return value compatibility - -* test(parser-scipy): update unit tests for parser return value compatibility - -* test(parser-emoji): update unit tests for parser return value compatibility - -* feat(version): parse squashed commits individually - -adds the functionality to separately parse each commit message within a squashed merge commit to - detect combined commit types that could change the version bump - -* feat(changelog): parse squashed commits individually - -adds functionality to separately parse each commit message within a squashed merge commit which - decouples the commits into their respective type categories in the changelog. - -* refactor(helpers): centralize utility for applying multiple text substitutions - -* feat(parser-angular): upgrade angular parser to parse squashed commits individually - -Resolves: #1085 - -* feat(parser-angular): apply PR/MR numbers to all parsed commits from a squash merge - -* feat(parser-emoji): add functionality to interpret scopes from gitmoji commit messages - -* feat(parser-emoji): upgrade emoji parser to parse squashed commits individually - -* test(fixtures): adjust parser for squashed commit definitions - -* test(fixtures): change config of github flow repo to parse squash commits - -* test(fixtures): add fixture to create gitlab formatted merge commit - -* refactor(parser-scipy): standardize all category spelling applied to commits - -* docs(commit-parsing): add description for squash commit evaluation option of default parsers - -* docs(configuration): update the `commit_parser_options` setting description - -### Performance Improvements - -- **logging**: Remove irrelevant debug logging statements - ([#1147](https://github.com/python-semantic-release/python-semantic-release/pull/1147), - [`f1ef4ec`](https://github.com/python-semantic-release/python-semantic-release/commit/f1ef4ecf5f22684a870b958f87d1ca2650e612db)) - -* refactor: adjust logging output - - -## v9.16.1 (2025-01-12) - -### Bug Fixes - -- **parser-custom**: Handle relative parent directory paths to module file better - ([#1142](https://github.com/python-semantic-release/python-semantic-release/pull/1142), - [`c4056fc`](https://github.com/python-semantic-release/python-semantic-release/commit/c4056fc2e1fb3bddb78728793716ac6fb8522b1a)) - -The dynamic import originally would just replace "/" with "." to make the import module name more - pythonic, however this would be problematic in monorepos which would use - "../../misc/commit_parser.py" to locate the parser and so the resulting `sys.modules` entry would - have numerous periods (.) as a prefix. This removes that possibility. Still always an issue if the - imported module name matches an existing module but the likelihood is low. - -### Documentation - -- **github-actions**: Update PSR versions in github workflow examples - ([#1140](https://github.com/python-semantic-release/python-semantic-release/pull/1140), - [`9bdd626`](https://github.com/python-semantic-release/python-semantic-release/commit/9bdd626bf8f8359d35725cebe803931063260cac)) - - -## v9.16.0 (2025-01-12) - -### Bug Fixes - -- **changelog**: Fixes PSR release commit exclusions for customized commit messages - ([#1139](https://github.com/python-semantic-release/python-semantic-release/pull/1139), - [`f9a2078`](https://github.com/python-semantic-release/python-semantic-release/commit/f9a20787437d0f26074fe2121bf0a29576a96df0)) - -* fix(config-changelog): validate `changelog.exclude_commit_patterns` on config load - -* test(fixtures): relocate sanitize changelog functions - -* test(cmd-version): add test to validate that custom release messages are ignored in changelog - -* test(config): add `changelog.exclude_commit_patterns` validation check - -* style(config): refactor import names of `re` module - -- **cmd-version**: Fix `--print-tag` result to match configured tag format - ([#1134](https://github.com/python-semantic-release/python-semantic-release/pull/1134), - [`a990aa7`](https://github.com/python-semantic-release/python-semantic-release/commit/a990aa7ab0a9d52d295c04d54d20e9c9f2db2ca5)) - -* test(fixtures): add new trunk repo that has a different tag format - -* test(fixtures): add helper to extract config settings from repo action definition - -* test(cmd-version): expand testing of `--print-tag` & `--print-last-released-tag` - -PSR did not have enough testing to demonstrate testing of the tag generation when the tag format was - configured differently than normal. This commit adds a significant portion of testing to exercise - the print tag functionality which must match the configured tag format. - -- **cmd-version**: Fix tag format on default version when force bump for initial release - ([#1138](https://github.com/python-semantic-release/python-semantic-release/pull/1138), - [`007fd00`](https://github.com/python-semantic-release/python-semantic-release/commit/007fd00a3945ed211ece4baab0b79ad93dc018f5)) - -Resolves: #1137 - -* test(fixtures): add new unreleased trunk repo with a different tag format - -* test(cmd-version): ensure forced bump version on initial release follows tag format - -ref: #1137 - -### Features - -- **config**: Expand dynamic parser import to handle a filepath to module - ([#1135](https://github.com/python-semantic-release/python-semantic-release/pull/1135), - [`0418fd8`](https://github.com/python-semantic-release/python-semantic-release/commit/0418fd8d27aac14925aafa50912e751e3aeff2f7)) - -* test(fixtures): remove import checking/loading of custom parser in `use_custom_parser` - -* test(config): extend import parser unit tests to evaluate file paths to modules - -* docs(commit-parsing): add the new custom parser import spec description for direct path imports - -Resolves: #687 - -* docs(configuration): adjust `commit_parser` option definition for direct path imports - - -## v9.15.2 (2024-12-16) - -### Bug Fixes - -- **changelog**: Ensures user rendered files are trimmed to end with a single newline - ([#1118](https://github.com/python-semantic-release/python-semantic-release/pull/1118), - [`6dfbbb0`](https://github.com/python-semantic-release/python-semantic-release/commit/6dfbbb0371aef6b125cbcbf89b80dc343ed97360)) - -- **cli**: Add error message of how to gather full error output - ([#1116](https://github.com/python-semantic-release/python-semantic-release/pull/1116), - [`ba85532`](https://github.com/python-semantic-release/python-semantic-release/commit/ba85532ddd6fcf1a2205f7ce0b88ea5be76cb621)) - -- **cmd-version**: Enable maintenance prereleases - ([#864](https://github.com/python-semantic-release/python-semantic-release/pull/864), - [`b88108e`](https://github.com/python-semantic-release/python-semantic-release/commit/b88108e189e1894e36ae4fdf8ad8a382b5c8c90a)) - -* test(fixtures): improve changelog generator to filter by max version - -* test(fixtures): add repo fixture of a trunk only repo w/ dual version support - -* test(fixtures): add repo fixture of a trunk only repo w/ dual version support & prereleases - -* test(cmd-version): add rebuild repo tests for new dual version support repos - -* test(version-determination): adjust unit tests of increment_version logic - -This clarifies repeated function calls and pytest parameter names included the unclear assert diff. - Adds additional tests to check bad states for failures and refactored to match new function - signature. - -* fix(version-bump): increment based on current commit's history only - -Refactor duplicate logging messages and flow to process out odd cases in a fail fast methodology. - This removes the reliance on any last full release that is not within the history of the current - branch. - -Resolves: #861 - -- **cmd-version**: Fix handling of multiple prerelease token variants & git flow merges - ([#1120](https://github.com/python-semantic-release/python-semantic-release/pull/1120), - [`8784b9a`](https://github.com/python-semantic-release/python-semantic-release/commit/8784b9ad4bc59384f855b5af8f1b8fb294397595)) - -* refactor: define a custom logging level of silly - -* fix(version): remove some excessive log msgs from debug to silly level - -* test(fixtures): refactor builder functions for version file updates - -* test(fixtures): adjust build command to handle versions w/ build metadata - -* test(fixtures): fix gitflow repo that included an invalid build metadata string - -* test(fixtures): fix major_on_zero setting in repos to match expected behavior - -* test(cmd-version): add test cases to run an example repo rebuild w/ psr - -* test(cmd-version): enable git flow repo rebuild w/ psr test cases - -* fix(cmd-version): handle multiple prerelease token variants properly - -In the case where there are alpha and beta releases, we must only consider the previous beta release - even if alpha releases exist due to merging into beta release only branches which have no changes - considerable changes from alphas but must be marked otherwise. - -Resolves: #789 - -* fix(cmd-version): fix version determination algorithm to capture commits across merged branches - -* perf(cmd-version): refactor version determination algorithm for accuracy & speed - -* test(algorithm): refactor test to match new function signature - -* style(algorithm): drop unused functions & imports - -* test(algorithm): adapt test case for new DFS commit traversal implementation - -- **cmd-version**: Forces tag timestamp to be same time as release commit - ([#1117](https://github.com/python-semantic-release/python-semantic-release/pull/1117), - [`7898b11`](https://github.com/python-semantic-release/python-semantic-release/commit/7898b1185fc1ad10e96bf3f5e48d9473b45d2b51)) - -- **config**: Ensure default config loads on network mounted windows environments - ([#1124](https://github.com/python-semantic-release/python-semantic-release/pull/1124), - [`a64cbc9`](https://github.com/python-semantic-release/python-semantic-release/commit/a64cbc96c110e32f1ec5d1a7b61e950472491b87)) - -Resolves: #1123 - -* test(cmd-generate-config): added noop version execution to validate config at runtime - -ref: #1123 - - -## v9.15.1 (2024-12-03) - -### Bug Fixes - -- **changelog-md**: Fix commit sort of breaking descriptions section - ([`75b342e`](https://github.com/python-semantic-release/python-semantic-release/commit/75b342e6259412cb82d8b7663e5ee4536d14f407)) - -- **parser-angular**: Ensure issues are sorted by numeric value rather than text sorted - ([`3858add`](https://github.com/python-semantic-release/python-semantic-release/commit/3858add582fe758dc2ae967d0cd051d43418ecd0)) - -- **parser-emoji**: Ensure issues are sorted by numeric value rather than text sorted - ([`7b8d2d9`](https://github.com/python-semantic-release/python-semantic-release/commit/7b8d2d92e135ab46d1be477073ccccc8c576f121)) - - -## v9.15.0 (2024-12-02) - -### Bug Fixes - -- **cmd-version**: Ensure release utilizes a timezone aware datetime - ([`ca817ed`](https://github.com/python-semantic-release/python-semantic-release/commit/ca817ed9024cf84b306a047675534cc36dc116b2)) - -- **default-changelog**: Alphabetically sort commit descriptions in version type sections - ([`bdaaf5a`](https://github.com/python-semantic-release/python-semantic-release/commit/bdaaf5a460ca77edc40070ee799430122132dc45)) - -### Features - -- **commit-parser**: Enable parsers to flag commit to be ignored for changelog - ([#1108](https://github.com/python-semantic-release/python-semantic-release/pull/1108), - [`0cc668c`](https://github.com/python-semantic-release/python-semantic-release/commit/0cc668c36490401dff26bb2c3141f6120a2c47d0)) - -This adds an attribute to the ParsedCommit object that allows custom parsers to set to false if it - is desired to ignore the commit completely from entry into the changelog. - -Resolves: #778 - -* test(parser-custom): add test w/ parser that toggles if a parsed commit is included in changelog - -- **default-changelog**: Add a separate formatted breaking changes section - ([#1110](https://github.com/python-semantic-release/python-semantic-release/pull/1110), - [`4fde30e`](https://github.com/python-semantic-release/python-semantic-release/commit/4fde30e0936ecd186e448f1caf18d9ba377c55ad)) - -Resolves: #244 - -* test(fixtures): update repo changelog generator to add breaking descriptions - -* test(default-changelog): add unit tests to demonstrate breaking change descriptions - -* test(release-notes): add unit tests to demonstrate breaking change descriptions - -* feat(changelog-md): add a breaking changes section to default Markdown template - -* feat(changelog-rst): add a breaking changes section to default reStructuredText template - -* feat(changelog-md): alphabetize breaking change descriptions in markdown changelog template - -* feat(changelog-rst): alphabetize breaking change descriptions in ReStructuredText template - -- **default-changelog**: Alphabetize commit summaries & scopes in change sections - ([#1111](https://github.com/python-semantic-release/python-semantic-release/pull/1111), - [`8327068`](https://github.com/python-semantic-release/python-semantic-release/commit/83270683fd02b626ed32179d94fa1e3c7175d113)) - -* test(fixtures): force non-alphabetical release history to validate template sorting - -* test(default-changelog): update unit test to enforce sorting of commit desc in version sections - -* test(release-notes): update unit test to enforce sorting of commit desc in version sections - -* feat(changelog-md): alphabetize commit summaries & scopes in markdown changelog template - -* feat(changelog-rst): alphabetize commit summaries & scopes in ReStructuredText template - -- **parsers**: Enable parsers to identify linked issues on a commit - ([#1109](https://github.com/python-semantic-release/python-semantic-release/pull/1109), - [`f90b8dc`](https://github.com/python-semantic-release/python-semantic-release/commit/f90b8dc6ce9f112ef2c98539d155f9de24398301)) - -* refactor(parsers): add parser option validation to commit parsing - -* docs(api-parsers): add option documentation to parser options - -* feat(parsers): add `other_allowed_tags` option for commit parser options - -* feat(parser-custom): enable custom parsers to identify linked issues on a commit - -* test(parser-angular): add unit tests to verify parsing of issue numbers - -* test(parser-emoji): add unit tests to verify parsing of issue numbers - -* test(parser-scipy): add unit tests to verify parsing of issue numbers - -* fix(util): prevent git footers from being collapsed during parse - -* feat(parser-angular): automatically parse angular issue footers from commit messages - -* feat(parser-emoji): parse issue reference footers from commit messages - -* docs(commit-parsing): improve & expand commit parsing w/ parser descriptions - -* docs(changelog-templates): update examples using new `commit.linked_issues` attribute - -* chore(docs): update documentation configuration for team publishing - -- **release-notes**: Add tag comparison link to release notes when supported - ([#1107](https://github.com/python-semantic-release/python-semantic-release/pull/1107), - [`9073344`](https://github.com/python-semantic-release/python-semantic-release/commit/9073344164294360843ef5522e7e4c529985984d)) - -* test(release-notes): adjust test case to include a version compare link - -* test(cmd-changelog): add test to ensure multiple variants of release notes are published - - -## v9.14.0 (2024-11-11) - -### Bug Fixes - -- **release-notes**: Override default wordwrap to non-wrap for in default template - ([`99ab99b`](https://github.com/python-semantic-release/python-semantic-release/commit/99ab99bb0ba350ca1913a2bde9696f4242278972)) - -### Documentation - -- **changelog-templates**: Document new `mask_initial_release` changelog context variable - ([`f294957`](https://github.com/python-semantic-release/python-semantic-release/commit/f2949577dfb2dbf9c2ac952c1bbcc4ab84da080b)) - -- **configuration**: Document new `mask_initial_release` option usage & effect - ([`3cabcdc`](https://github.com/python-semantic-release/python-semantic-release/commit/3cabcdcd9473e008604e74cc2d304595317e921d)) - -- **homepage**: Fix reference to new ci workflow for test status badge - ([`6760069`](https://github.com/python-semantic-release/python-semantic-release/commit/6760069e7489f50635beb5aedbbeb2cb82b7c584)) - -### Features - -- **changelog**: Add md to rst conversion for markdown inline links - ([`cb2af1f`](https://github.com/python-semantic-release/python-semantic-release/commit/cb2af1f17cf6c8ae037c6cd8bb8b4d9c019bb47e)) - -- **changelog**: Define first release w/o change descriptions for default MD template - ([`fa89dec`](https://github.com/python-semantic-release/python-semantic-release/commit/fa89dec239efbae7544b187f624a998fa9ecc309)) - -- **changelog**: Define first release w/o change descriptions for default RST template - ([`e30c94b`](https://github.com/python-semantic-release/python-semantic-release/commit/e30c94bffe62b42e8dc6ed4fed6260e57b4d532b)) - -- **changelog-md**: Add markdown inline link format macro - ([`c6d8211`](https://github.com/python-semantic-release/python-semantic-release/commit/c6d8211c859442df17cb41d2ff19fdb7a81cdb76)) - -- **changelogs**: Prefix scopes on commit descriptions in default template - ([#1093](https://github.com/python-semantic-release/python-semantic-release/pull/1093), - [`560fd2c`](https://github.com/python-semantic-release/python-semantic-release/commit/560fd2c0d58c97318377cb83af899a336d24cfcc)) - -* test(changelog): update default changelog unit tests to handle commit scope - -* test(release-notes): update default release notes unit tests to handle commit scope - -* test(fixtures): update changelog generator fixture to handle scope additions - -* test(cmd-version): update implementation for test resiliency - -* feat(changelog-md): prefix scopes on commit descriptions in Markdown changelog template - -* feat(changelog-rst): prefix scopes on commit descriptions in ReStructuredText template - -- **configuration**: Add `changelog.default_templates.mask_initial_release` option - ([`595a70b`](https://github.com/python-semantic-release/python-semantic-release/commit/595a70bcbc8fea1f8ccf6c5069c41c35ec4efb8d)) - -- **context**: Add `mask_initial_release` setting to changelog context - ([`6f2ee39`](https://github.com/python-semantic-release/python-semantic-release/commit/6f2ee39414b3cf75c0b67dee4db0146bbc1041bb)) - -- **release-notes**: Define first release w/o change descriptions in default template - ([`83167a3`](https://github.com/python-semantic-release/python-semantic-release/commit/83167a3dcceb7db16b790e1b0efd5fc75fee8942)) - - -## v9.13.0 (2024-11-10) - -### Bug Fixes - -- **changelog-rst**: Ignore unknown parsed commit types in default RST changelog - ([`77609b1`](https://github.com/python-semantic-release/python-semantic-release/commit/77609b1917a00b106ce254e6f6d5edcd1feebba7)) - -- **parser-angular**: Drop the `breaking` category but still maintain a major level bump - ([`f1ffa54`](https://github.com/python-semantic-release/python-semantic-release/commit/f1ffa5411892de34cdc842fd55c460a24b6685c6)) - -- **parsers**: Improve reliability of text unwordwrap of descriptions - ([`436374b`](https://github.com/python-semantic-release/python-semantic-release/commit/436374b04128d1550467ae97ba90253f1d1b3878)) - -### Documentation - -- **changelog-templates**: Add `linked_merge_request` field to examples - ([`d4376bc`](https://github.com/python-semantic-release/python-semantic-release/commit/d4376bc2ae4d3708d501d91211ec3ee3a923e9b5)) - -- **changelog-templates**: Fix api class reference links - ([`7a5bdf2`](https://github.com/python-semantic-release/python-semantic-release/commit/7a5bdf29b3df0f9a1346ea5301d2a7fee953667b)) - -- **commit-parsing**: Add `linked_merge_request` field to Parsed Commit definition - ([`ca61889`](https://github.com/python-semantic-release/python-semantic-release/commit/ca61889d4ac73e9864fbf637fb87ab2d5bc053ea)) - -### Features - -- **changelog**: Add PR/MR url linking to default Markdown changelog - ([`cd8d131`](https://github.com/python-semantic-release/python-semantic-release/commit/cd8d1310a4000cc79b529fbbdc58933f4c6373c6)) - -Resolves: #924, #953 - -- **changelog**: Add PR/MR url linking to default reStructuredText template - ([`5f018d6`](https://github.com/python-semantic-release/python-semantic-release/commit/5f018d630b4c625bdf6d329b27fd966eba75b017)) - -Resolves: #924, #953 - -- **parsed-commit**: Add linked merge requests list to the `ParsedCommit` object - ([`9a91062`](https://github.com/python-semantic-release/python-semantic-release/commit/9a9106212d6c240e9d3358e139b4c4694eaf9c4b)) - -- **parser-angular**: Automatically parse PR/MR numbers from subject lines in commits - ([`2ac798f`](https://github.com/python-semantic-release/python-semantic-release/commit/2ac798f92e0c13c1db668747f7e35a65b99ae7ce)) - -- **parser-emoji**: Automatically parse PR/MR numbers from subject lines in commits - ([`bca9909`](https://github.com/python-semantic-release/python-semantic-release/commit/bca9909c1b61fdb1f9ccf823fceb6951cd059820)) - -- **parser-scipy**: Automatically parse PR/MR numbers from subject lines in commits - ([`2b3f738`](https://github.com/python-semantic-release/python-semantic-release/commit/2b3f73801f5760bac29acd93db3ffb2bc790cda0)) - -### Performance Improvements - -- **parser-angular**: Simplify commit parsing type pre-calculation - ([`a86a28c`](https://github.com/python-semantic-release/python-semantic-release/commit/a86a28c5e26ed766cda71d26b9382c392e377c61)) - -- **parser-emoji**: Increase speed of commit parsing - ([`2c9c468`](https://github.com/python-semantic-release/python-semantic-release/commit/2c9c4685a66feb35cd78571cf05f76344dd6d66a)) - -- **parser-scipy**: Increase speed & decrease complexity of commit parsing - ([`2b661ed`](https://github.com/python-semantic-release/python-semantic-release/commit/2b661ed122a6f0357a6b92233ac1351c54c7794e)) - - -## v9.12.2 (2024-11-07) - -### Bug Fixes - -- **cli**: Gracefully capture all exceptions unless in very verbose debug mode - ([#1088](https://github.com/python-semantic-release/python-semantic-release/pull/1088), - [`13ca44f`](https://github.com/python-semantic-release/python-semantic-release/commit/13ca44f4434098331f70e6937684679cf1b4106a)) - -* refactor(cli): consolidate entrypoints into the module execute file - -- **hvcs-***: Add flexibility to issue & MR/PR url jinja filters - ([#1089](https://github.com/python-semantic-release/python-semantic-release/pull/1089), - [`275ec88`](https://github.com/python-semantic-release/python-semantic-release/commit/275ec88e6d1637c47065bb752a60017ceba9876c)) - -* fix(github): fix `issue_url` filter to ignore an issue prefix gracefully - -* fix(github): fix `pull_request_url` filter to ignore an PR prefix gracefully - -* fix(gitlab): fix `issue_url` filter to ignore an issue prefix gracefully - -* fix(gitlab): fix `merge_request_url` filter to ignore an PR prefix gracefully - -* fix(gitea): fix `issue_url` filter to ignore an issue prefix gracefully - -* fix(gitea): fix `pull_request_url` filter to ignore an PR prefix gracefully - -* fix(bitbucket): fix `pull_request_url` filter to ignore an PR prefix gracefully - -* test(bitbucket): add test case for prefixed PR numbers - -* test(gitea): add test case for prefixed PR & issue numbers - -* test(gitlab): add test case for prefixed PR & issue numbers - -* test(github): add test case for prefixed PR & issue numbers - -* style(hvcs): fix logical lint errors - -* docs(changelog-templates): update descriptions of issue & MR/PR url jinja filters - - -## v9.12.1 (2024-11-06) - -### Bug Fixes - -- **changelog**: Fix raw-inline pattern replacement in `convert_md_to_rst` filter - ([`2dc70a6`](https://github.com/python-semantic-release/python-semantic-release/commit/2dc70a6106776106b0fba474b0029071317d639f)) - -- **cmd-version**: Fix `--as-prerelease` when no commit change from last full release - ([#1076](https://github.com/python-semantic-release/python-semantic-release/pull/1076), - [`3b7b772`](https://github.com/python-semantic-release/python-semantic-release/commit/3b7b77246100cedd8cc8f289395f7641187ffdec)) - -- **release-notes**: Add context variable shorthand `ctx` like docs claim & changelog has - ([`d618d83`](https://github.com/python-semantic-release/python-semantic-release/commit/d618d83360c4409fc149f70b97c5fe338fa89968)) - -### Documentation - -- **contributing**: Update local testing instructions - ([`74f03d4`](https://github.com/python-semantic-release/python-semantic-release/commit/74f03d44684b7b2d84f9f5e471425b02f8bf91c3)) - - -## v9.12.0 (2024-10-18) - -### Bug Fixes - -- **changelog**: Ignore commit exclusion when a commit causes a version bump - ([`e8f886e`](https://github.com/python-semantic-release/python-semantic-release/commit/e8f886ef2abe8ceaea0a24a0112b92a167abd6a9)) - -- **parser-angular**: Change `Fixes` commit type heading to `Bug Fixes` - ([#1064](https://github.com/python-semantic-release/python-semantic-release/pull/1064), - [`09e3a4d`](https://github.com/python-semantic-release/python-semantic-release/commit/09e3a4da6237740de8e9932d742b18d990e9d079)) - -* test(fixtures): update expected changelog heading to `Bug Fixes` - -* test(unit): update expected changelog heading to `Bug Fixes` - -- **parser-emoji**: Enable the default bump level option - ([`bc27995`](https://github.com/python-semantic-release/python-semantic-release/commit/bc27995255a96b9d6cc743186e7c35098822a7f6)) - -### Documentation - -- **commit-parsers**: Add deprecation message for the tag parser - ([`af94540`](https://github.com/python-semantic-release/python-semantic-release/commit/af94540f2b1c63bf8a4dc977d5d0f66176962b64)) - -- **configuration**: Add deprecation message for the tag parser - ([`a83b7e4`](https://github.com/python-semantic-release/python-semantic-release/commit/a83b7e43e4eaa99790969a6c85f44e01cde80d0a)) - -### Features - -- **changelog**: Add `autofit_text_width` filter to template environment - ([#1062](https://github.com/python-semantic-release/python-semantic-release/pull/1062), - [`83e4b86`](https://github.com/python-semantic-release/python-semantic-release/commit/83e4b86abd4754c2f95ec2e674f04deb74b9a1e6)) - -This change adds an equivalent style formatter that can apply a text alignment to a maximum width - and also maintain an indent over paragraphs of text - -* docs(changelog-templates): add definition & usage of `autofit_text_width` template filter - -* test(changelog-context): add test cases to check `autofit_text_width` filter use - - -## v9.11.1 (2024-10-15) - -### Bug Fixes - -- **changelog**: Prevent custom template errors when components are in hidden folders - ([#1060](https://github.com/python-semantic-release/python-semantic-release/pull/1060), - [`a7614b0`](https://github.com/python-semantic-release/python-semantic-release/commit/a7614b0db8ce791e4252209e66f42b5b5275dffd)) - - -## v9.11.0 (2024-10-12) - -### Features - -- **changelog**: Add default changelog template in reStructuredText format - ([#1055](https://github.com/python-semantic-release/python-semantic-release/pull/1055), - [`c2e8831`](https://github.com/python-semantic-release/python-semantic-release/commit/c2e883104d3c11e56f229638e988d8b571f86e34)) - -* test(fixtures): update repo generation to create rst & md changelogs - -* test(release-history): refactor fragile test to utilize repo fixture definitions - -* test(changelog-cmd): update tests to evaluate rst changelog generation & updates - -* test(version-cmd): update tests to evaluate rst changelog generation & updates - -* test(version-cmd): update test code to match new commit definition functions - -* test(config): add test to validate `insertion_flag` default determination - -* feat(changelog): add `convert_md_to_rst` filter to changelog environment - -* feat(changelog): add default changelog in re-structured text format - -This change adds the templates to create an equivalent CHANGELOG.RST file in angular changelog - style. It can be enabled via the `output_format` configuration setting. - -Resolves: #399 - -* feat(config): enable target changelog filename to trigger RST output format - -* feat(config): enable default `changelog.insertion_flag` based on output format - -* refactor(config): move `changelog_file` setting under `changelog.default_templates` - -This change adds a secondary `changelog_file` setting under the default_templates section while - deprecating the top level one. Since this is not intended to be a breaking change we provided a - warning message and compatibility code to pass along the current `changelog_file` value to the new - setting location while giving the user a notification to update before the next version. - -* fix(changelog): correct spacing for default markdown template during updates - -* docs(configuration): update details of `insertion_flag`'s dynamic defaults with rst - -* docs(configuration): update `output_format` description for reStructuredText support - -* docs(configuration): update `changelog_file` with deprecation notice of setting relocation - -* docs(changelog): clarify the `convert_md_to_rst` filter added to the template environment - -* docs(changelog): increase detail about configuration options of default changelog creation - - -## v9.10.1 (2024-10-10) - -### Bug Fixes - -- **config**: Handle branch match regex errors gracefully - ([#1054](https://github.com/python-semantic-release/python-semantic-release/pull/1054), - [`4d12251`](https://github.com/python-semantic-release/python-semantic-release/commit/4d12251c678a38de6b71cac5b9c1390eb9dd8ad6)) - -prevents stacktrace error when user provided regex for a branch name match is invalid. Translates - most common failure of a plain wildcard `*` character to the implied proper regex - - -## v9.10.0 (2024-10-08) - -### Documentation - -- **github-actions**: Update primary example with workflow sha controlled pipeline - ([`14f04df`](https://github.com/python-semantic-release/python-semantic-release/commit/14f04dffc7366142faecebb162d4449501cbf1fd)) - -### Features - -- **changelog**: Modify changelog template to support changelog updates - ([#1045](https://github.com/python-semantic-release/python-semantic-release/pull/1045), - [`c18c245`](https://github.com/python-semantic-release/python-semantic-release/commit/c18c245df51a9778af09b9dc7a315e3f11cdcda0)) - -* feat(changelog): add `read_file` function to changelog template context - -This feature adds a filter that will enable jinja templates to read a file from the repository into - memory to then use as output within the template. The primary use for this is to read in a - previous changelog file which then the template can give the illusion of insertion as it re-writes - the entire file. - -* feat(changelog): add `changelog_mode` to changelog template context - -Adds a flag that can be passed to the templating environment to allow for triggering an update mode - of a changelog versions an initialization mode. The usage is up to the template developer but for - PSR it is used to handle changelog generation vs changelog updating. - -* feat(changelog): add `prev_changelog_file` to changelog template context - -This adds a string that represents a filename to a previous changelog file which can be read from - inside the template context. The primary use is for enabling the updating of a changelog through - jinja templating. - -* feat(changelog): add `changelog_insertion_flag` to changelog template context - -This adds a customizable string to the jinja templating context which allows users to use the PSR - configuration to pass a custom insertion flag into the templating context. This is intended for - use with initializing a changelog and then updating it from that point forward. - -* feat(changelog): add shorthand `ctx` variable to changelog template env - -* refactor(changelog): change recursive render to not use file streaming - -It would be nice to maintain file streaming for better memory usage but it prevents the ability to - read the file contents previously from within the template which is a desire in order to insert - into a previous changelog. In this case, the memory usage is likely not a problem for large text - files. - -* fix(config): prevent jinja from autoescaping markdown content by default - -Since this project is generally rendering non-html content such as RST or MD, change the default of - the jinja autoescape parameter to false instead of true. When it was true, it would automatically - convert any `&` ampersands to its htmlentity equivalent `&` which is completely unnecessary - and unreadable in non-html documents. - -* docs(configuration): update `changelog.environment.autoescape` default to `false` to match code - -* docs(configuration): standardize all true/false to lowercase ensuring toml-compatibility - -* feat(config): add `changelog.mode` as configuration option - -* feat(config): add `changelog.insertion_flag` as configuration option - -* refactor(config): use `changelog.changelog_file` as previous changelog file for target for update - -* style(config): alphabetize changelog configuration options - -* docs(configuration): add `changelog.mode` and `changelog.insertion_flag` config definitions - -* fix(changelog): adjust angular heading names for readability - -* feat(changelog): modify changelog template to support changelog updates - -By popular demand, the desire to only prepend new information to the changelog is now possible given - the `changelog.mode = update` configuration option. - -Resolves: #858, #722 - -* refactor(errors): add new generic internal error for tragic improbable flaws - -* fix(changelog): ensure changelog templates can handle complex directory includes - -* feat(config): add `changelog.default_templates.output_format` config option - -* fix(changelog): only render user templates when files exist - -This change ensures that we will use our default even when the user only overrides the release notes - template. It also must have jinja templates in the folder otherwise we will render the default - changelog. - -* refactor(changelog): enable default changelog rendering of multiple template parts - -* refactor(changelog): change rendering of default release notes to new template structure - -* refactor(context): use pathlib instead of context manager to read file - -* test(fixtures): update changelog generator format & angular heading names - -* test(angular): adjust test of commit type to section header - -* test(changelog): update make changelog context function call - -* test(release-notes): update test related to release notes generation - -* test(fixtures): add processing to filter out repo definitions for partial changelogs - -* test(fixtures): update repo generators to update changelogs w/ every version - -* test(fixtures): slow down repo generators to prevent git failures from same timestamps - -* test(fixtures): update changelog generator to include insertion flag - -* refactor(changelog): fix template to handle update when no releases exist - -* refactor(changelog): adjust template to use improved release object - -* refactor(changelog): improve resilence & non-existant initial changelog - -* style(changelog-templates): maintain 2-spaces indentation throughout jinja templates - -* refactor(changelog): ensure cross-platform template includes with jinja compatibility - -* test(changelog-cmd): add tests to evaluate variations of the changelog update mode - -* test(version-cmd): add tests to evaluate variations of the changelog update mode - -* refactor(release-notes): normalize line endings to universal newlines & always end with newline - -* refactor(changelog): ensure default changelog renders w/ universal newlines & writes as - os-specific - -* test(changelog): update changelog testing implementation to be newline aware - -* test: update tests to use cross-platform newlines where appropriate - -* docs(changelog-templates): improve detail & describe new `changelog.mode="update"` - -* docs(configuration): mark version of configuration setting introduction - -* docs(homepage): update custom changelog reference - -* refactor(changelog): adjust read_file filter to read file as os-newline aware - -* refactor(changelog): apply forced universal newline normalizer on default changelog - -* test(changelog): adjust implementation to consistently work on windows - -* test(version): adjust implementation to consistently work on windows - -* refactor(changelog-template): only add insertion flag if in update mode - -* test(changelog): adjust test to handle changelog regeneration in init mode - -* refactor(changelog-templates): adjust init template to clean up extra newlines - -* test(changelog): adjust expected output after cleaned up newlines - -* docs(configuration): define the new `changelog.default_templates.output_format` option - -- **github-actions**: Add an action `build` directive to toggle the `--skip-build` option - ([#1044](https://github.com/python-semantic-release/python-semantic-release/pull/1044), - [`26597e2`](https://github.com/python-semantic-release/python-semantic-release/commit/26597e24a80a37500264aa95a908ba366699099e)) - -* docs(commands): update definition of the version commands `--skip-build` option - -* docs(github-actions): add description of the `build` input directive - - -## v9.9.0 (2024-09-28) - -### Documentation - -- **github-actions**: Clarify & consolidate GitHub Actions usage docs - ([#1011](https://github.com/python-semantic-release/python-semantic-release/pull/1011), - [`2135c68`](https://github.com/python-semantic-release/python-semantic-release/commit/2135c68ccbdad94378809902b52fcad546efd5b3)) - -Resolves: #907 - -* chore(scripts): remove non-existant file from version bump script - -* docs(automatic-releases): drop extrenous github push configuration - -* docs(homepage): remove link to old github config & update token scope config - -* docs(github-actions): expand descriptions & clarity of actions configs - -* docs(github-actions): add configuration & description of publish action - -* docs(github-actions): revert removal of namespace prefix from examples - -### Features - -- **github-actions**: Add `is_prerelease` output to the version action - ([#1038](https://github.com/python-semantic-release/python-semantic-release/pull/1038), - [`6a5d35d`](https://github.com/python-semantic-release/python-semantic-release/commit/6a5d35d0d9124d6a6ee7910711b4154b006b8773)) - -* test(github-actions): add test to ensure `is_prerelease` is a action output - -* docs(github-actions): add description of new `is_prerelease` output for version action - - -## v9.8.9 (2024-09-27) - -### Bug Fixes - -- **version-cmd**: Improve `version_variables` flexibility w/ quotes (ie. json, yaml, etc) - ([#1028](https://github.com/python-semantic-release/python-semantic-release/pull/1028), - [`156915c`](https://github.com/python-semantic-release/python-semantic-release/commit/156915c7d759098f65cf9de7c4e980b40b38d5f1)) - -* fix(version-cmd): increase `version_variable` flexibility with quotations (ie. json, yaml, etc) - -Previously json would not work due to the key being wrapped in quotes, yaml also has issues when it - does not usually use quotes. The regex we created originally only wrapped the version to be - replaced in quotes but now both the key and version can optionally be wrapped in different kind of - quotations. - -Resolves: #601, #706, #962, #1026 - -* docs(configuration): add clarity to `version_variables` usage & limitations - -Ref: #941 - -* fix(version-cmd): ensure `version_variables` do not match partial variable names - -* build(deps-test): add `PyYAML` as a test dependency - -* test(fixtures): refactor location of fixture for global use of cli runner - -* test(stamp-version): add test cases to stamp json, python, & yaml files - -### Documentation - -- Update docstrings to resolve sphinx failures - ([#1030](https://github.com/python-semantic-release/python-semantic-release/pull/1030), - [`d84efc7`](https://github.com/python-semantic-release/python-semantic-release/commit/d84efc7719a8679e6979d513d1c8c60904af7384)) - -set `ignore-module-all` for `autodoc_default_options` to resolve some Sphinx errors about duplicate - / ambiguous references https://github.com/sphinx-doc/sphinx/issues/4961#issuecomment-1543858623 - -Standardize some non-standard (Google-ish) docstrings to Sphinx format, to avoid ruff and Sphinx - arguing about underline length. - -Fix indents and other minor whitespace / formatting changes. - -Fixes #1029 - - -## v9.8.8 (2024-09-01) - -### Bug Fixes - -- **config**: Fix path traversal detection for windows compatibility - ([#1014](https://github.com/python-semantic-release/python-semantic-release/pull/1014), - [`16e6daa`](https://github.com/python-semantic-release/python-semantic-release/commit/16e6daaf851ce1eabf5fbd5aa9fe310a8b0f22b3)) - -The original implementation of the path traversal detection expected that `resolve()` works the same - on windows as it does with Linux/Mac. Windows requires the folder paths to exist to be resolved - and that is not the case when the `template_dir` is not being used. - -Resolves: #994 - -### Documentation - -- **configuration**: Update `build_command` env table for windows to use all capital vars - ([`0e8451c`](https://github.com/python-semantic-release/python-semantic-release/commit/0e8451cf9003c6a3bdcae6878039d7d9a23d6d5b)) - -- **github-actions**: Update version in examples to latest version - ([`3c894ea`](https://github.com/python-semantic-release/python-semantic-release/commit/3c894ea8a555d20b454ebf34785e772959bbb4fe)) - - -## v9.8.7 (2024-08-20) - -### Bug Fixes - -- Provide `context.history` global in release notes templates - ([#1005](https://github.com/python-semantic-release/python-semantic-release/pull/1005), - [`5bd91b4`](https://github.com/python-semantic-release/python-semantic-release/commit/5bd91b4d7ac33ddf10446f3e66d7d11e0724aeb2)) - -* fix(release-notes): provide `context.history` global in release note templates - -Temporarily return the `context.history` variable to release notes generation as many users are - using it in their release documentation. It was never intended to be provided and will be removed - in the future. - -context was removed in `v9.8.3` during a refactor and condensing of changelog and release notes - functionality. - -Resolves: #984 - -* fix(release-notes): fix noop-changelog to print raw release notes - -Some markdown sequences can be interpreted as ansi escape sequences which dilute debugging of - release note templates by the user. This change ensures the raw content is displayed to the - console as expected. - -### Documentation - -- Use pinned version for GHA examples - ([#1004](https://github.com/python-semantic-release/python-semantic-release/pull/1004), - [`5fdf761`](https://github.com/python-semantic-release/python-semantic-release/commit/5fdf7614c036a77ffb051cd30f57d0a63c062c0d)) - -* docs(github-actions): use pinned version for GHA examples - -Fixes #1003 - -* chore(scripts): add auto version bump to non dynamic docs text (i.e. code snippets) - -* docs(github-actions): adjust formatting & version warning in code snippets - -* style(docs-github-actions): adjust formatting for readability - ---------- - -Co-authored-by: codejedi365 - -- **changelog**: Clarify description of the default changelog generation process - ([`399fa65`](https://github.com/python-semantic-release/python-semantic-release/commit/399fa6521d5c6c4397b1d6e9b13ea7945ae92543)) - -- **configuration**: Clarify `changelog_file` vs `template_dir` option usage - ([`a7199c8`](https://github.com/python-semantic-release/python-semantic-release/commit/a7199c8cd6041a9de017694302e49b139bbcb034)) - -Provided additional description that warns about the mutually-exclusive nature of the - `changelog_file` option and the `template_dir` option. - -Resolves: #983 - -- **configuration**: Fix build_command_env table rendering - ([#996](https://github.com/python-semantic-release/python-semantic-release/pull/996), - [`a5eff0b`](https://github.com/python-semantic-release/python-semantic-release/commit/a5eff0bfe41d2fd5d9ead152a132010b718b7772)) - - -## v9.8.6 (2024-07-20) - -### Bug Fixes - -- **version-cmd**: Resolve build command execution in powershell - ([#980](https://github.com/python-semantic-release/python-semantic-release/pull/980), - [`32c8e70`](https://github.com/python-semantic-release/python-semantic-release/commit/32c8e70915634d8e560b470c3cf38c27cebd7ae0)) - -Fixes the command line option for passing a shell command to Powershell. Also included a similar - shell detection result for pwsh (Powershell Core) - -### Documentation - -- **configuration**: Correct GHA parameter name for commit email - ([#981](https://github.com/python-semantic-release/python-semantic-release/pull/981), - [`ce9ffdb`](https://github.com/python-semantic-release/python-semantic-release/commit/ce9ffdb82c2358184b288fa18e83a4075f333277)) - -`git_committer_name` was repeated; replace one instance of it with `git_committer_email` - - -## v9.8.5 (2024-07-06) - -### Bug Fixes - -- Enable `--print-last-released*` when in detached head or non-release branch - ([#926](https://github.com/python-semantic-release/python-semantic-release/pull/926), - [`782c0a6`](https://github.com/python-semantic-release/python-semantic-release/commit/782c0a6109fb49e168c37f279928c0a4959f8ac6)) - -* test(version-cmd): add tests to print when detached or non-release branch - -ref: #900 - -* fix(version-cmd): drop branch restriction for `--print-last-released*` opts - -Resolves: #900 - -### Performance Improvements - -- Improve git history processing for changelog generation - ([#972](https://github.com/python-semantic-release/python-semantic-release/pull/972), - [`bfda159`](https://github.com/python-semantic-release/python-semantic-release/commit/bfda1593af59e9e728c584dd88d7927fc52c879f)) - -* perf(changelog): improve git history parser changelog generation - -This converts the double for-loop (`O(n^2)`) down to `O(n)` using a lookup table to match the - current commit with a known tag rather than iterating through all the tags of the repository every - time. - -* fix(changelog): resolve commit ordering issue when dates are similar - - -## v9.8.4 (2024-07-04) - -### Bug Fixes - -- **changelog-cmd**: Remove usage strings when error occured - ([`348a51d`](https://github.com/python-semantic-release/python-semantic-release/commit/348a51db8a837d951966aff3789aa0c93d473829)) - -Resolves: #810 - -- **changelog-cmd**: Render default changelog when user template directory exist but is empty - ([`bded8de`](https://github.com/python-semantic-release/python-semantic-release/commit/bded8deae6c92f6dde9774802d9f3716a5cb5705)) - -- **config**: Prevent path traversal manipulation of target changelog location - ([`43e35d0`](https://github.com/python-semantic-release/python-semantic-release/commit/43e35d0972e8a29239d18ed079d1e2013342fcbd)) - -- **config**: Prevent path traversal manipulation of target changelog location - ([`3eb3dba`](https://github.com/python-semantic-release/python-semantic-release/commit/3eb3dbafec4223ee463b90e927e551639c69426b)) - -- **publish-cmd**: Prevent error when provided tag does not exist locally - ([`16afbbb`](https://github.com/python-semantic-release/python-semantic-release/commit/16afbbb8fbc3a97243e96d7573f4ad2eba09aab9)) - -- **publish-cmd**: Remove usage strings when error occured - ([`afbb187`](https://github.com/python-semantic-release/python-semantic-release/commit/afbb187d6d405fdf6765082e2a1cecdcd7d357df)) - -Resolves: #810 - -- **version-cmd**: Remove usage strings when error occurred - ([`a7c17c7`](https://github.com/python-semantic-release/python-semantic-release/commit/a7c17c73fd7becb6d0e042e45ff6765605187e2a)) - -Resolves: #810 - - -## v9.8.3 (2024-06-18) - -### Bug Fixes - -- **parser**: Strip DOS carriage-returns in commits - ([#956](https://github.com/python-semantic-release/python-semantic-release/pull/956), - [`0b005df`](https://github.com/python-semantic-release/python-semantic-release/commit/0b005df0a8c7730ee0c71453c9992d7b5d2400a4)) - -The default template can result in mixed (UNIX / DOS style) carriage returns in the generated - changelog. Use a string replace in the commit parser to strip the DOS CRs ("\r"). This is only - needed in the case when we are _not_ byte decoding. - -Fixes #955 - - -## v9.8.2 (2024-06-17) - -### Bug Fixes - -- **templates**: Suppress extra newlines in default changelog - ([#954](https://github.com/python-semantic-release/python-semantic-release/pull/954), - [`7b0079b`](https://github.com/python-semantic-release/python-semantic-release/commit/7b0079bf3e17c0f476bff520b77a571aeac469d0)) - -Suppress extra newlines in default generated changelog output - - -## v9.8.1 (2024-06-05) - -### Bug Fixes - -- Improve build cmd env on windows - ([#942](https://github.com/python-semantic-release/python-semantic-release/pull/942), - [`d911fae`](https://github.com/python-semantic-release/python-semantic-release/commit/d911fae993d41a8cb1497fa8b2a7e823576e0f22)) - -* fix(version-cmd): pass windows specific env vars to build cmd when on windows - -* test(version-cmd): extend build cmd tests to include windows vars - -* docs(configuration): define windows specific env vars for build cmd - -* refactor(version-cmd): only add windows vars when windows is detected - ---------- - -Co-authored-by: Juan Cruz Mencia Naranjo - - -## v9.8.0 (2024-05-27) - -### Documentation - -- **migration-v8**: Update version references in migration instructions - ([#938](https://github.com/python-semantic-release/python-semantic-release/pull/938), - [`d6ba16a`](https://github.com/python-semantic-release/python-semantic-release/commit/d6ba16aa8e01bae1a022a9b06cd0b9162c51c345)) - -### Features - -- Extend gitlab to edit a previous release if exists - ([#934](https://github.com/python-semantic-release/python-semantic-release/pull/934), - [`23e02b9`](https://github.com/python-semantic-release/python-semantic-release/commit/23e02b96dfb2a58f6b4ecf7b7812e4c1bc50573d)) - -* style(hvcs-github): update function docstrings for params - -* feat(hvcs-gitlab): enable gitlab to edit a previous release if found - -* fix(hvcs-gitlab): add tag message to release creation - -* fix(gitlab): adjust release name to mirror other hvcs release names - -* refactor(gitlab): consolidate & simplify usage of gitlab client - -* test(gitlab): neuter test cases that used the internet & add new tests - -* refactor(gitlab): handle errors in release retrieval gracefully - -* refactor(gitlab): update release notes editing implementation - ---------- - -Co-authored-by: bdorsey - -- **gha**: Configure ssh signed tags in GitHub Action - ([#937](https://github.com/python-semantic-release/python-semantic-release/pull/937), - [`dfb76b9`](https://github.com/python-semantic-release/python-semantic-release/commit/dfb76b94b859a7f3fa3ad778eec7a86de2874d68)) - -Resolves: #936 - -- **version-cmd**: Add toggle of `--no-verify` option to `git commit` - ([#927](https://github.com/python-semantic-release/python-semantic-release/pull/927), - [`1de6f78`](https://github.com/python-semantic-release/python-semantic-release/commit/1de6f7834c6d37a74bc53f91609d40793556b52d)) - -* test(version-cmd): add test w/ failing pre-commit hook--preventing version commit - -* feat(version-cmd): add toggle of `--no-verify` option to `git commit` - -This commit adds a configuration option that toggles the addition of `--no-verify` command line - switch on git commit operations that are run with the `version` command. - -* docs(configuration): add `no_git_verify` description to the configuration page - ---------- - -Co-authored-by: bdorsey - - -## v9.7.3 (2024-05-15) - -### Bug Fixes - -- Enabled `prelease-token` parameter in github action - ([#929](https://github.com/python-semantic-release/python-semantic-release/pull/929), - [`1bb26b0`](https://github.com/python-semantic-release/python-semantic-release/commit/1bb26b0762d94efd97c06a3f1b6b10fb76901f6d)) - - -## v9.7.2 (2024-05-13) - -### Bug Fixes - -- Enable user configuration of `build_command` env vars - ([#925](https://github.com/python-semantic-release/python-semantic-release/pull/925), - [`6b5b271`](https://github.com/python-semantic-release/python-semantic-release/commit/6b5b271453874b982fbf2827ec1f6be6db1c2cc7)) - -- test(version): add test of user defined env variables in build command - -ref: #922 - -- fix(version): enable user config of `build_command` env variables - -Resolves: #922 - -- docs(configuration): document `build_command_env` configuration option - -### Documentation - -- **configuration**: Clarify TOC & alphabetize configuration descriptions - ([`19add16`](https://github.com/python-semantic-release/python-semantic-release/commit/19add16dcfdfdb812efafe2d492a933d0856df1d)) - -- **configuration**: Clarify TOC & standardize heading links - ([`3a41995`](https://github.com/python-semantic-release/python-semantic-release/commit/3a4199542d0ea4dbf88fa35e11bec41d0c27dd17)) - - -## v9.7.1 (2024-05-07) - -### Bug Fixes - -- **gha**: Fix missing `git_committer_*` definition in action - ([#919](https://github.com/python-semantic-release/python-semantic-release/pull/919), - [`ccef9d8`](https://github.com/python-semantic-release/python-semantic-release/commit/ccef9d8521be12c0640369b3c3a80b81a7832662)) - -Resolves: #918 - - -## v9.7.0 (2024-05-06) - -### Bug Fixes - -- **gha**: Add missing `tag` option to GitHub Action definition - ([#908](https://github.com/python-semantic-release/python-semantic-release/pull/908), - [`6b24288`](https://github.com/python-semantic-release/python-semantic-release/commit/6b24288a96302cd6982260e46fad128ec4940da9)) - -Resolves: #906 - -### Documentation - -- **configuration**: Add description of build command available env variables - ([`c882dc6`](https://github.com/python-semantic-release/python-semantic-release/commit/c882dc62b860b2aeaa925c21d1524f4ae25ef567)) - -### Features - -- **version-cmd**: Pass `NEW_VERSION` & useful env vars to build command - ([`ee6b246`](https://github.com/python-semantic-release/python-semantic-release/commit/ee6b246df3bb211ab49c8bce075a4c3f6a68ed77)) - - -## v9.6.0 (2024-04-29) - -### Bug Fixes - -- Correct version `--prerelease` use & enable `--as-prerelease` - ([#647](https://github.com/python-semantic-release/python-semantic-release/pull/647), - [`2acb5ac`](https://github.com/python-semantic-release/python-semantic-release/commit/2acb5ac35ae79d7ae25ca9a03fb5c6a4a68b3673)) - -* test(version): add validation of `--as-prerelease` and `--prerelease opts` - -* fix(version-cmd): correct `--prerelease` use - -Prior to this change, `--prerelease` performed the role of converting whichever forced version into - a prerelease version declaration, which was an unintentional breaking change to the CLI compared - to v7. - -`--prerelease` now forces the next version to increment the prerelease revision, which makes it - consistent with `--patch`, `--minor` and `--major`. Temporarily disabled the ability to force a - prerelease. - -Resolves: #639 - -* feat(version-cmd): add `--as-prerelease` option to force the next version to be a prerelease - -Prior to this change, `--prerelease` performed the role that `--as-prerelease` now does, which was - an unintentional breaking change to the CLI compared to v7. - -`--prerelease` is used to force the next version to increment the prerelease revision, which makes - it consistent with `--patch`, `--minor` and `--major`, while `--as-prerelease` forces for the next - version to be converted to a prerelease version type before it is applied to the project - regardless of the bump level. - -* docs(commands): update version command options definition about prereleases - ---------- - -Co-authored-by: codejedi365 - -- **parser-custom**: Gracefully handle custom parser import errors - ([`67f6038`](https://github.com/python-semantic-release/python-semantic-release/commit/67f60389e3f6e93443ea108c0e1b4d30126b8e06)) - -### Features - -- Changelog filters are specialized per vcs type - ([#890](https://github.com/python-semantic-release/python-semantic-release/pull/890), - [`76ed593`](https://github.com/python-semantic-release/python-semantic-release/commit/76ed593ea33c851005994f0d1a6a33cc890fb908)) - -* test(github): sync pr url expectation with GitHub api documentation - -* fix(github): correct changelog filter for pull request urls - -* refactor(hvcs-base): change to an abstract class & simplify interface - -* refactor(remote-hvcs-base): extend the base abstract class with common remote base class - -* refactor(github): adapt to new abstract base class - -* refactor(gitea): adapt to new abstract base class - -* refactor(gitlab): adapt to new abstract base class - -* refactor(bitbucket): adapt to new abstract base class - -* refactor(cmds): prevent hvcs from executing if not remote hosted vcs - -* feat(changelog): changelog filters are hvcs focused - -* test(hvcs): add validation for issue_url generation - -* feat(changelog-github): add issue url filter to changelog context - -* feat(changelog-gitea): add issue url filter to changelog context - -* refactor(cmd-version): consolidate asset uploads with release creation - -* style: resolve ruff errors - -* feat(changelog-context): add flag to jinja env for which hvcs is available - -* test(changelog-context): demonstrate per hvcs filters upon render - -* docs(changelog-context): explain new hvcs specific context filters - -* refactor(config): adjust default token resolution w/ subclasses - - -## v9.5.0 (2024-04-23) - -### Build System - -- **deps**: Bump ruff from 0.3.5 to 0.3.7 - ([#894](https://github.com/python-semantic-release/python-semantic-release/pull/894), - [`6bf2849`](https://github.com/python-semantic-release/python-semantic-release/commit/6bf28496d8631ada9009aec5f1000f68b7f7ee16)) - -### Features - -- Extend support to on-prem GitHub Enterprise Server - ([#896](https://github.com/python-semantic-release/python-semantic-release/pull/896), - [`4fcb737`](https://github.com/python-semantic-release/python-semantic-release/commit/4fcb737958d95d1a3be24db7427e137b46f5075f)) - -* test(github): adjust init test to match the Enterprise Server api url - -* feat(github): extend support to on-prem GitHub Enterprise Server - -Resolves: #895 - - -## v9.4.2 (2024-04-14) - -### Bug Fixes - -- **hvcs**: Allow insecure http connections if configured - ([#886](https://github.com/python-semantic-release/python-semantic-release/pull/886), - [`db13438`](https://github.com/python-semantic-release/python-semantic-release/commit/db1343890f7e0644bc8457f995f2bd62087513d3)) - -* fix(gitlab): allow insecure http connections if configured - -* test(hvcs-gitlab): fix tests for clarity & insecure urls - -* test(conftest): refactor netrc generation into common fixture - -* refactor(hvcsbase): remove extrenous non-common functionality - -* fix(gitea): allow insecure http connections if configured - -* test(hvcs-gitea): fix tests for clarity & insecure urls - -* refactor(gitlab): adjust init function signature - -* fix(github): allow insecure http connections if configured - -* test(hvcs-github): fix tests for clarity & insecure urls - -* fix(bitbucket): allow insecure http connections if configured - -* test(hvcs-bitbucket): fix tests for clarity & insecure urls - -* fix(config): add flag to allow insecure connections - -* fix(version-cmd): handle HTTP exceptions more gracefully - -* style(hvcs): resolve typing issues & mimetype executions - -* test(cli-config): adapt default token test for env resolution - -* test(changelog-cmd): isolate env & correct the expected api url - -* test(fixtures): adapt repo builder for new hvcs init() signature - -* style: update syntax for 3.8 compatiblity & formatting - -* docs(configuration): update `remote` settings section with missing values - -Resolves: #868 - -* style(docs): improve configuration & api readability - -- **hvcs**: Prevent double url schemes urls in changelog - ([#676](https://github.com/python-semantic-release/python-semantic-release/pull/676), - [`5cfdb24`](https://github.com/python-semantic-release/python-semantic-release/commit/5cfdb248c003a2d2be5fe65fb61d41b0d4c45db5)) - -* fix(hvcs): prevent double protocol scheme urls in changelogs - -Due to a typo and conditional stripping of the url scheme the hvcs_domain and hvcs_api_domain values - would contain protocol schemes when a user specified one but the defaults would not. It would - cause the api_url and remote_url to end up as "https://https://domain.com" - -* fix(bitbucket): correct url parsing & prevent double url schemes - -* fix(gitea): correct url parsing & prevent double url schemes - -* fix(github): correct url parsing & prevent double url schemes - -* fix(gitlab): correct url parsing & prevent double url schemes - -* test(hvcs): ensure api domains are derived correctly - ---------- - -Co-authored-by: codejedi365 - -### Build System - -- **deps**: Update rich requirement from ~=12.5 to ~=13.0 - ([#877](https://github.com/python-semantic-release/python-semantic-release/pull/877), - [`4a22a8c`](https://github.com/python-semantic-release/python-semantic-release/commit/4a22a8c1a69bcf7b1ddd6db56e6883c617a892b3)) - -Updates the requirements on [rich](https://github.com/Textualize/rich) to permit the latest version. - - [Release notes](https://github.com/Textualize/rich/releases) - - [Changelog](https://github.com/Textualize/rich/blob/master/CHANGELOG.md) - -Resolves: #888 - -Signed-off-by: dependabot[bot] - -Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> - - -## v9.4.1 (2024-04-06) - -### Bug Fixes - -- **gh-actions-output**: Fixed trailing newline to match GITHUB_OUTPUT format - ([#885](https://github.com/python-semantic-release/python-semantic-release/pull/885), - [`2c7b6ec`](https://github.com/python-semantic-release/python-semantic-release/commit/2c7b6ec85b6e3182463d7b695ee48e9669a25b3b)) - -* test(gh-actions-output): fix unit tests to manage proper whitespace - -tests were adjusted for clarity and to replicate error detailed in #884. - -* fix(gh-actions-output): fixed trailing newline to match GITHUB_OUTPUT format - -Resolves: #884 - - -## v9.4.0 (2024-03-31) - -### Features - -- **gitea**: Derives gitea api domain from base domain when unspecified - ([#675](https://github.com/python-semantic-release/python-semantic-release/pull/675), - [`2ee3f8a`](https://github.com/python-semantic-release/python-semantic-release/commit/2ee3f8a918d2e5ea9ab64df88f52e62a1f589c38)) - -* test(gitea): add test of custom server path & custom api domain - -* feat(gitea): derives gitea api domain from base domain when unspecified - -* refactor(hvcs-gitea): uniformly handle protocol prefixes - ---------- - -Co-authored-by: codejedi365 - - -## v9.3.1 (2024-03-24) - -### Bug Fixes - -- **algorithm**: Handle merge-base errors gracefully - ([`4c998b7`](https://github.com/python-semantic-release/python-semantic-release/commit/4c998b77a3fe5e12783d1ab2d47789a10b83f247)) - -Merge-base errors generally occur from a shallow clone that is primarily used by CI environments and - will cause PSR to explode prior to this change. Now it exits with an appropriate error. - -Resolves: #724 - -- **cli-version**: Change implementation to only push the tag we generated - ([`8a9da4f`](https://github.com/python-semantic-release/python-semantic-release/commit/8a9da4feb8753e3ab9ea752afa25decd2047675a)) - -Restricts the git push command to only push the explicit tag we created which will eliminate the - possibility of pushing another tag that could cause an error. - -Resolves: #803 - -### Performance Improvements - -- **algorithm**: Simplify logs & use lookup when searching for commit & tag match - ([`3690b95`](https://github.com/python-semantic-release/python-semantic-release/commit/3690b9511de633ab38083de4d2505b6d05853346)) - - -## v9.3.0 (2024-03-21) - -### Features - -- **cmd-version**: Changelog available to bundle - ([#779](https://github.com/python-semantic-release/python-semantic-release/pull/779), - [`37fdb28`](https://github.com/python-semantic-release/python-semantic-release/commit/37fdb28e0eb886d682b5dea4cc83a7c98a099422)) - -* test(util): fix overlooked file differences in folder comparison - -* test(version): tracked changelog as changed file on version create - -Removes the temporary release_notes hack to prevent CHANGELOG generation on execution of version - command. Now that it is implemented we can remove the fixture to properly pass the tests. - -* feat(cmd-version): create changelog prior to build enabling doc bundling - - -## v9.2.2 (2024-03-19) - -### Bug Fixes - -- **cli**: Enable subcommand help even if config is invalid - ([`91d221a`](https://github.com/python-semantic-release/python-semantic-release/commit/91d221a01266e5ca6de5c73296b0a90987847494)) - -Refactors configuration loading to use lazy loading by subcommands triggered by the property access - of the runtime_ctx object. Resolves the issues when running `--help` on subcommands when a - configuration is invalid - -Resolves: #840 - - -## v9.2.1 (2024-03-19) - -### Bug Fixes - -- **parse-git-url**: Handle urls with url-safe special characters - ([`27cd93a`](https://github.com/python-semantic-release/python-semantic-release/commit/27cd93a0a65ee3787ca51be4c91c48f6ddb4269c)) - - -## v9.2.0 (2024-03-18) - -### Bug Fixes - -- **changelog**: Make sure default templates render ending in 1 newline - ([`0b4a45e`](https://github.com/python-semantic-release/python-semantic-release/commit/0b4a45e3673d0408016dc8e7b0dce98007a763e3)) - -- **changelog-generation**: Fix incorrect release timezone determination - ([`f802446`](https://github.com/python-semantic-release/python-semantic-release/commit/f802446bd0693c4c9f6bdfdceae8b89c447827d2)) - -### Build System - -- **deps**: Add click-option-group for grouping exclusive flags - ([`bd892b8`](https://github.com/python-semantic-release/python-semantic-release/commit/bd892b89c26df9fccc9335c84e2b3217e3e02a37)) - -### Documentation - -- **configuration**: Add description of `allow-zero-version` configuration option - ([`4028f83`](https://github.com/python-semantic-release/python-semantic-release/commit/4028f8384a0181c8d58c81ae81cf0b241a02a710)) - -- **configuration**: Clarify the `major_on_zero` configuration option - ([`f7753cd`](https://github.com/python-semantic-release/python-semantic-release/commit/f7753cdabd07e276bc001478d605fca9a4b37ec4)) - -### Features - -- **version**: Add new version print flags to display the last released version and tag - ([`814240c`](https://github.com/python-semantic-release/python-semantic-release/commit/814240c7355df95e9be9a6ed31d004b800584bc0)) - -- **version-config**: Add option to disable 0.x.x versions - ([`dedb3b7`](https://github.com/python-semantic-release/python-semantic-release/commit/dedb3b765c8530379af61d3046c3bb9c160d54e5)) - - -## v9.1.1 (2024-02-25) - -### Bug Fixes - -- **parse_git_url**: Fix bad url with dash - ([`1c25b8e`](https://github.com/python-semantic-release/python-semantic-release/commit/1c25b8e6f1e43c15ca7d5a59dca0a13767f9bc33)) - - -## v9.1.0 (2024-02-14) - -### Bug Fixes - -- Remove unofficial environment variables - ([`a5168e4`](https://github.com/python-semantic-release/python-semantic-release/commit/a5168e40b9a14dbd022f62964f382b39faf1e0df)) - -### Build System - -- **deps**: Bump minimum required `tomlkit` to `>=0.11.0` - ([`291aace`](https://github.com/python-semantic-release/python-semantic-release/commit/291aacea1d0429a3b27e92b0a20b598f43f6ea6b)) - -TOMLDocument is missing the `unwrap()` function in `v0.10.2` which causes an AttributeError to occur - when attempting to read a the text in `pyproject.toml` as discovered with #834 - -Resolves: #834 - -### Documentation - -- Add bitbucket authentication - ([`b78a387`](https://github.com/python-semantic-release/python-semantic-release/commit/b78a387d8eccbc1a6a424a183254fc576126199c)) - -- Add bitbucket to token table - ([`56f146d`](https://github.com/python-semantic-release/python-semantic-release/commit/56f146d9f4c0fc7f2a84ad11b21c8c45e9221782)) - -- Fix typo - ([`b240e12`](https://github.com/python-semantic-release/python-semantic-release/commit/b240e129b180d45c1d63d464283b7dfbcb641d0c)) - -### Features - -- Add bitbucket hvcs - ([`bbbbfeb`](https://github.com/python-semantic-release/python-semantic-release/commit/bbbbfebff33dd24b8aed2d894de958d532eac596)) - - -## v9.0.3 (2024-02-08) - -### Bug Fixes - -- **algorithm**: Correct bfs to not abort on previously visited node - ([`02df305`](https://github.com/python-semantic-release/python-semantic-release/commit/02df305db43abfc3a1f160a4a52cc2afae5d854f)) - -### Performance Improvements - -- **algorithm**: Refactor bfs search to use queue rather than recursion - ([`8b742d3`](https://github.com/python-semantic-release/python-semantic-release/commit/8b742d3db6652981a7b5f773a74b0534edc1fc15)) - - -## v9.0.2 (2024-02-08) - -### Bug Fixes - -- **util**: Properly parse windows line-endings in commit messages - ([`70193ba`](https://github.com/python-semantic-release/python-semantic-release/commit/70193ba117c1a6d3690aed685fee8a734ba174e5)) - -Due to windows line-endings `\r\n`, it would improperly split the commit description (it failed to - split at all) and cause detection of Breaking changes to fail. The breaking changes regular - expression looks to the start of the line for the proper syntax. - -Resolves: #820 - -### Documentation - -- Remove duplicate note in configuration.rst - ([#807](https://github.com/python-semantic-release/python-semantic-release/pull/807), - [`fb6f243`](https://github.com/python-semantic-release/python-semantic-release/commit/fb6f243a141642c02469f1080180ecaf4f3cec66)) - - -## v9.0.1 (2024-02-06) - -### Bug Fixes - -- **config**: Set commit parser opt defaults based on parser choice - ([#782](https://github.com/python-semantic-release/python-semantic-release/pull/782), - [`9c594fb`](https://github.com/python-semantic-release/python-semantic-release/commit/9c594fb6efac7e4df2b0bfbd749777d3126d03d7)) - - -## v9.0.0 (2024-02-06) - -### Bug Fixes - -- Drop support for Python 3.7 - ([#828](https://github.com/python-semantic-release/python-semantic-release/pull/828), - [`ad086f5`](https://github.com/python-semantic-release/python-semantic-release/commit/ad086f5993ae4741d6e20fee618d1bce8df394fb)) - - -## v8.7.2 (2024-01-03) - -### Bug Fixes - -- **lint**: Correct linter errors - ([`c9556b0`](https://github.com/python-semantic-release/python-semantic-release/commit/c9556b0ca6df6a61e9ce909d18bc5be8b6154bf8)) - - -## v8.7.1 (2024-01-03) - -### Bug Fixes - -- **cli-generate-config**: Ensure configuration types are always toml parsable - ([#785](https://github.com/python-semantic-release/python-semantic-release/pull/785), - [`758e649`](https://github.com/python-semantic-release/python-semantic-release/commit/758e64975fe46b961809f35977574729b7c44271)) - -### Documentation - -- Add note on default envvar behaviour - ([#780](https://github.com/python-semantic-release/python-semantic-release/pull/780), - [`0b07cae`](https://github.com/python-semantic-release/python-semantic-release/commit/0b07cae71915c5c82d7784898b44359249542a64)) - -- **configuration**: Change defaults definition of token default to table - ([#786](https://github.com/python-semantic-release/python-semantic-release/pull/786), - [`df1df0d`](https://github.com/python-semantic-release/python-semantic-release/commit/df1df0de8bc655cbf8f86ae52aff10efdc66e6d2)) - -- **contributing**: Add docs-build, testing conf, & build instructions - ([#787](https://github.com/python-semantic-release/python-semantic-release/pull/787), - [`011b072`](https://github.com/python-semantic-release/python-semantic-release/commit/011b0729cba3045b4e7291fd970cb17aad7bae60)) - - -## v8.7.0 (2023-12-22) - -### Features - -- **config**: Enable default environment token per hvcs - ([#774](https://github.com/python-semantic-release/python-semantic-release/pull/774), - [`26528eb`](https://github.com/python-semantic-release/python-semantic-release/commit/26528eb8794d00dfe985812269702fbc4c4ec788)) - - -## v8.6.0 (2023-12-22) - -### Documentation - -- Minor correction to commit-parsing documentation - ([#777](https://github.com/python-semantic-release/python-semantic-release/pull/777), - [`245e878`](https://github.com/python-semantic-release/python-semantic-release/commit/245e878f02d5cafec6baf0493c921c1e396b56e8)) - -### Features - -- **utils**: Expand parsable valid git remote url formats - ([#771](https://github.com/python-semantic-release/python-semantic-release/pull/771), - [`cf75f23`](https://github.com/python-semantic-release/python-semantic-release/commit/cf75f237360488ebb0088e5b8aae626e97d9cbdd)) - -Git remote url parsing now supports additional formats (ssh, https, file, git) - - -## v8.5.2 (2023-12-19) - -### Bug Fixes - -- **cli**: Gracefully output configuration validation errors - ([#772](https://github.com/python-semantic-release/python-semantic-release/pull/772), - [`e8c9d51`](https://github.com/python-semantic-release/python-semantic-release/commit/e8c9d516c37466a5dce75a73766d5be0f9e74627)) - -* test(fixtures): update example project workflow & add config modifier - -* test(cli-main): add test for raw config validation error - -* fix(cli): gracefully output configuration validation errors - - -## v8.5.1 (2023-12-12) - -### Bug Fixes - -- **cmd-version**: Handle committing of git-ignored file gracefully - ([#764](https://github.com/python-semantic-release/python-semantic-release/pull/764), - [`ea89fa7`](https://github.com/python-semantic-release/python-semantic-release/commit/ea89fa72885e15da91687172355426a22c152513)) - -* fix(version): only commit non git-ignored files during version commit - -* test(version): set version file as ignored file - -Tweaks tests to use one committed change file and the version file as an ignored change file. This - allows us to verify that our commit mechanism does not crash if a file that is changed is ignored - by user - -- **config**: Gracefully fail when repo is in a detached HEAD state - ([#765](https://github.com/python-semantic-release/python-semantic-release/pull/765), - [`ac4f9aa`](https://github.com/python-semantic-release/python-semantic-release/commit/ac4f9aacb72c99f2479ae33369822faad011a824)) - -* fix(config): cleanly handle repository in detached HEAD state - -* test(cli-main): add detached head cli test - -### Documentation - -- **configuration**: Adjust wording and improve clarity - ([#766](https://github.com/python-semantic-release/python-semantic-release/pull/766), - [`6b2fc8c`](https://github.com/python-semantic-release/python-semantic-release/commit/6b2fc8c156e122ee1b43fdb513b2dc3b8fd76724)) - -* docs(configuration): fix typo in text - -* docs(configuration): adjust wording and improve clarity - - -## v8.5.0 (2023-12-07) - -### Features - -- Allow template directories to contain a '.' at the top-level - ([#762](https://github.com/python-semantic-release/python-semantic-release/pull/762), - [`07b232a`](https://github.com/python-semantic-release/python-semantic-release/commit/07b232a3b34be0b28c6af08aea4852acb1b9bd56)) - - -## v8.4.0 (2023-12-07) - -### Documentation - -- **migration**: Fix comments about publish command - ([#747](https://github.com/python-semantic-release/python-semantic-release/pull/747), - [`90380d7`](https://github.com/python-semantic-release/python-semantic-release/commit/90380d797a734dcca5040afc5fa00e3e01f64152)) - -### Features - -- **cmd-version**: Add `--tag/--no-tag` option to version command - ([#752](https://github.com/python-semantic-release/python-semantic-release/pull/752), - [`de6b9ad`](https://github.com/python-semantic-release/python-semantic-release/commit/de6b9ad921e697b5ea2bb2ea8f180893cecca920)) - -* fix(version): separate push tags from commit push when not committing changes - -* feat(version): add `--no-tag` option to turn off tag creation - -* test(version): add test for `--tag` option & `--no-tag/commit` - -* docs(commands): update `version` subcommand options - - -## v8.3.0 (2023-10-23) - -### Features - -- **action**: Use composite action for semantic release - ([#692](https://github.com/python-semantic-release/python-semantic-release/pull/692), - [`4648d87`](https://github.com/python-semantic-release/python-semantic-release/commit/4648d87bac8fb7e6cc361b765b4391b30a8caef8)) - -Co-authored-by: Bernard Cooke - - -## v8.2.0 (2023-10-23) - -### Documentation - -- Add PYTHONPATH mention for commit parser - ([`3284258`](https://github.com/python-semantic-release/python-semantic-release/commit/3284258b9fa1a3fe165f336181aff831d50fddd3)) - -### Features - -- Allow user customization of release notes template - ([#736](https://github.com/python-semantic-release/python-semantic-release/pull/736), - [`94a1311`](https://github.com/python-semantic-release/python-semantic-release/commit/94a131167e1b867f8bc112a042b9766e050ccfd1)) - -Signed-off-by: Bryant Finney - - -## v8.1.2 (2023-10-13) - -### Bug Fixes - -- Correct lint errors - ([`a13a6c3`](https://github.com/python-semantic-release/python-semantic-release/commit/a13a6c37e180dc422599939a5725835306c18ff2)) - -GitHub.upload_asset now raises ValueError instead of requests.HTTPError - -- Error when running build command on windows systems - ([#732](https://github.com/python-semantic-release/python-semantic-release/pull/732), - [`2553657`](https://github.com/python-semantic-release/python-semantic-release/commit/25536574760b407410f435441da533fafbf94402)) - - -## v8.1.1 (2023-09-19) - -### Bug Fixes - -- Attribute error when logging non-strings - ([#711](https://github.com/python-semantic-release/python-semantic-release/pull/711), - [`75e6e48`](https://github.com/python-semantic-release/python-semantic-release/commit/75e6e48129da8238a62d5eccac1ae55d0fee0f9f)) - - -## v8.1.0 (2023-09-19) - -### Documentation - -- Fix typos ([#708](https://github.com/python-semantic-release/python-semantic-release/pull/708), - [`2698b0e`](https://github.com/python-semantic-release/python-semantic-release/commit/2698b0e006ff7e175430b98450ba248ed523b341)) - -- Update project urls - ([#715](https://github.com/python-semantic-release/python-semantic-release/pull/715), - [`5fd5485`](https://github.com/python-semantic-release/python-semantic-release/commit/5fd54856dfb6774feffc40d36d5bb0f421f04842)) - -### Features - -- Upgrade pydantic to v2 - ([#714](https://github.com/python-semantic-release/python-semantic-release/pull/714), - [`5a5c5d0`](https://github.com/python-semantic-release/python-semantic-release/commit/5a5c5d0ee347750d7c417c3242d52e8ada50b217)) - - -## v8.0.8 (2023-08-26) - -### Bug Fixes - -- Dynamic_import() import path split - ([#686](https://github.com/python-semantic-release/python-semantic-release/pull/686), - [`1007a06`](https://github.com/python-semantic-release/python-semantic-release/commit/1007a06d1e16beef6d18f44ff2e0e09921854b54)) - - -## v8.0.7 (2023-08-16) - -### Bug Fixes - -- Use correct upload url for github - ([#661](https://github.com/python-semantic-release/python-semantic-release/pull/661), - [`8a515ca`](https://github.com/python-semantic-release/python-semantic-release/commit/8a515caf1f993aa653e024beda2fdb9e629cc42a)) - -Co-authored-by: github-actions - - -## v8.0.6 (2023-08-13) - -### Bug Fixes - -- **publish**: Improve error message when no tags found - ([#683](https://github.com/python-semantic-release/python-semantic-release/pull/683), - [`bdc06ea`](https://github.com/python-semantic-release/python-semantic-release/commit/bdc06ea061c19134d5d74bd9f168700dd5d9bcf5)) - - -## v8.0.5 (2023-08-10) - -### Bug Fixes - -- Don't warn about vcs token if ignore_token_for_push is true. - ([#670](https://github.com/python-semantic-release/python-semantic-release/pull/670), - [`f1a54a6`](https://github.com/python-semantic-release/python-semantic-release/commit/f1a54a6c9a05b225b6474d50cd610eca19ec0c34)) - -* fix: don't warn about vcs token if ignore_token_for_push is true. - -* docs: `password` should be `token`. - -### Documentation - -- Fix typo missing 's' in version_variable[s] in configuration.rst - ([#668](https://github.com/python-semantic-release/python-semantic-release/pull/668), - [`879186a`](https://github.com/python-semantic-release/python-semantic-release/commit/879186aa09a3bea8bbe2b472f892cf7c0712e557)) - - -## v8.0.4 (2023-07-26) - -### Bug Fixes - -- **changelog**: Use version as semver tag by default - ([#653](https://github.com/python-semantic-release/python-semantic-release/pull/653), - [`5984c77`](https://github.com/python-semantic-release/python-semantic-release/commit/5984c7771edc37f0d7d57894adecc2591efc414d)) - -### Documentation - -- Add Python 3.11 to classifiers in metadata - ([#651](https://github.com/python-semantic-release/python-semantic-release/pull/651), - [`5a32a24`](https://github.com/python-semantic-release/python-semantic-release/commit/5a32a24bf4128c39903f0c5d3bd0cb1ccba57e18)) - -- Clarify usage of assets config option - ([#655](https://github.com/python-semantic-release/python-semantic-release/pull/655), - [`efa2b30`](https://github.com/python-semantic-release/python-semantic-release/commit/efa2b3019b41eb427f0e1c8faa21ad10664295d0)) - - -## v8.0.3 (2023-07-21) - -### Bug Fixes - -- Skip unparseable versions when calculating next version - ([#649](https://github.com/python-semantic-release/python-semantic-release/pull/649), - [`88f25ea`](https://github.com/python-semantic-release/python-semantic-release/commit/88f25eae62589cdf53dbc3dfcb167a3ae6cba2d3)) - - -## v8.0.2 (2023-07-18) - -### Bug Fixes - -- Handle missing configuration - ([#644](https://github.com/python-semantic-release/python-semantic-release/pull/644), - [`f15753c`](https://github.com/python-semantic-release/python-semantic-release/commit/f15753ce652f36cc03b108c667a26ab74bcbf95d)) - -### Documentation - -- Better description for tag_format usage - ([`2129b72`](https://github.com/python-semantic-release/python-semantic-release/commit/2129b729837eccc41a33dbb49785a8a30ce6b187)) - -- Clarify v8 breaking changes in GitHub action inputs - ([#643](https://github.com/python-semantic-release/python-semantic-release/pull/643), - [`cda050c`](https://github.com/python-semantic-release/python-semantic-release/commit/cda050cd9e789d81458157ee240ff99ec65c6f25)) - -- Correct version_toml example in migrating_from_v7.rst - ([#641](https://github.com/python-semantic-release/python-semantic-release/pull/641), - [`325d5e0`](https://github.com/python-semantic-release/python-semantic-release/commit/325d5e048bd89cb2a94c47029d4878b27311c0f0)) - - -## v8.0.1 (2023-07-17) - -### Bug Fixes - -- Invalid version in Git history should not cause a release failure - ([#632](https://github.com/python-semantic-release/python-semantic-release/pull/632), - [`254430b`](https://github.com/python-semantic-release/python-semantic-release/commit/254430b5cc5f032016b4c73168f0403c4d87541e)) - -### Documentation - -- Reduce readthedocs formats and add entries to migration from v7 guide - ([`9b6ddfe`](https://github.com/python-semantic-release/python-semantic-release/commit/9b6ddfef448f9de30fa2845034f76655d34a9912)) - -- **migration**: Fix hyperlink - ([#631](https://github.com/python-semantic-release/python-semantic-release/pull/631), - [`5fbd52d`](https://github.com/python-semantic-release/python-semantic-release/commit/5fbd52d7de4982b5689651201a0e07b445158645)) - - -## v8.0.0 (2023-07-16) - -### Features - -- V8 ([#619](https://github.com/python-semantic-release/python-semantic-release/pull/619), - [`ec30564`](https://github.com/python-semantic-release/python-semantic-release/commit/ec30564b4ec732c001d76d3c09ba033066d2b6fe)) - -* feat!: 8.0.x (#538) - -Co-authored-by: Johan - -Co-authored-by: U-NEO\johan - -* fix: correct Dockerfile CLI command and GHA fetch - -* fix: resolve branch checkout logic in GHA - -* fix: remove commit amending behaviour - -this was not working when there were no source code changes to be made, as it lead to attempting to - amend a HEAD commit that wasn't produced by PSR - -* 8.0.0-alpha.1 - -Automatically generated by python-semantic-release - -* fix: correct logic for generating release notes (#550) - -* fix: cleanup comments and unused logic - -* fix(action): mark container fs as safe for git to operate on - -* style: beautify 49080c510a68cccd2f6c7a8d540b483751901207 - -* fix(action): quotation for git config command - -* 8.0.0-alpha.2 - -* fix: resolve bug in changelog logic, enable upload to pypi - -* 8.0.0-alpha.3 - -* test: add tests for ReleaseHistory.release - -* fix: resolve loss of tag_format configuration - -* 8.0.0-alpha.4 - -* feat: various improvements - -* Added sorting to test parameterisation, so that pytest-xdist works again - dramatic speedup for - testing * Reworked the CI verification code so it's a bit prettier * Added more testing for the - version CLI command, and split some logic out of the command itself * Removed a redundant - double-regex match in VersionTranslator and Version, for some speedup - -* chore(test): proper env patching for tests in CI - -* style: beautify bcb27a4a8ce4789d083226f088c1810f45cd4c77 - -* refactor!: remove verify-ci command - -* 8.0.0-alpha.5 - -* fix(docs): fixup docs and remove reference to dist publication - -* feat!: remove publication of dists to artefact repository - -* feat: rename 'upload' configuration section to 'publish' - -* feat!: removed build status checking - -* feat: add GitHub Actions output - -* fix(action): remove default for 'force' - -* fix(ci): different workflow for v8 - -* fix(action): correct input parsing - -* fix: correct handling of build commands - -* feat: make it easier to access commit messages in ParsedCommits - -* fix: make additional attributes available for template authors - -* fix: add logging for token auth, use token for push - -* ci: add verbosity - -* fix: caching for repo owner and name - -* ci: contents permission for workflow - -* 8.0.0-alpha.6 - -* docs: update docs with additional required permissions - -* feat: add option to specify tag to publish to in publish command - -* feat: add Strict Mode - -* docs: convert to Furo theme - -* feat: add --skip-build option - -* 8.0.0-alpha.7 - -* test: separate command line tests by stdout and stderr - -* ci: pass tag output and conditionally execute publish steps - -* fix: correct assets type in configuration (#603) - -* change raw config assets type - -* fix: correct assets type-annotation for RuntimeContext - ---------- - -Co-authored-by: Bernard Cooke - -* 8.0.0-alpha.8 - -* fix: pin Debian version in Dockerfile - -* feat: promote to rc - -* 8.0.0-rc.1 - -* ci: fix conditionals in workflow and update documentation - -* ci: correct conditionals - -* fix: only call Github Action output callback once defaults are set - -* 8.0.0-rc.2 - -* fix: create_or_update_release for Gitlab hvcs - -* ci: remove separate v8 workflow - -* chore: tweak issue templates - -* chore: bump docs dependencies - -* 8.0.0-rc.3 - -* fix(deps): add types-click, and downgrade sphinx/furo for 3.7 - -* 8.0.0-rc.4 - -* docs: fix typo (#623) - -* docs: correct typo in docs/changelog_templates.rst - -Co-authored-by: Micael Jarniac - -Co-authored-by: semantic-release - -Co-authored-by: github-actions - -Co-authored-by: smeng9 <38666763+smeng9@users.noreply.github.com> - - -## v7.34.6 (2023-06-17) - -### Bug Fixes - -- Relax invoke dependency constraint - ([`18ea200`](https://github.com/python-semantic-release/python-semantic-release/commit/18ea200633fd67e07f3d4121df5aa4c6dd29d154)) - - -## v7.34.5 (2023-06-17) - -### Bug Fixes - -- Consider empty commits - ([#608](https://github.com/python-semantic-release/python-semantic-release/pull/608), - [`6f2e890`](https://github.com/python-semantic-release/python-semantic-release/commit/6f2e8909636595d3cb5e858f42c63820cda45974)) - - -## v7.34.4 (2023-06-15) - -### Bug Fixes - -- Docker build fails installing git - ([#605](https://github.com/python-semantic-release/python-semantic-release/pull/605), - [`9e3eb97`](https://github.com/python-semantic-release/python-semantic-release/commit/9e3eb979783bc39ca564c2967c6c77eecba682e6)) - -git was installed from bullseye-backports, but base image is referencing latest python:3.10 since - bookworm was recently released, this now points at bookworm and installing the backport of git is - actually trying to downgrade, resulting in this error: - -> E: Packages were downgraded and -y was used without --allow-downgrades. - -> ERROR: failed to solve: process "/bin/sh -c echo \"deb http://deb.debian.org/debian - bullseye-backports main\" >> /etc/apt/sources.list; apt-get update; apt-get install -y - git/bullseye-backports" did not complete successfully: exit code: 100 - - -## v7.34.3 (2023-06-01) - -### Bug Fixes - -- Generate markdown linter compliant changelog headers & lists - ([#597](https://github.com/python-semantic-release/python-semantic-release/pull/597), - [`cc87400`](https://github.com/python-semantic-release/python-semantic-release/commit/cc87400d4a823350de7d02dc3172d2488c9517db)) - -In #594, I missed that there are 2 places where the version header is formatted - - -## v7.34.2 (2023-05-29) - -### Bug Fixes - -- Open all files with explicit utf-8 encoding - ([#596](https://github.com/python-semantic-release/python-semantic-release/pull/596), - [`cb71f35`](https://github.com/python-semantic-release/python-semantic-release/commit/cb71f35c26c1655e675fa735fa880d39a2c8af9c)) - - -## v7.34.1 (2023-05-28) - -### Bug Fixes - -- Generate markdown linter compliant changelog headers & lists - ([#594](https://github.com/python-semantic-release/python-semantic-release/pull/594), - [`9d9d403`](https://github.com/python-semantic-release/python-semantic-release/commit/9d9d40305c499c907335abe313e3ed122db0b154)) - -Add an extra new line after each header and between sections to fix 2 markdownlint errors for - changelogs generated by this package - - -## v7.34.0 (2023-05-28) - -### Features - -- Add option to only parse commits for current working directory - ([#509](https://github.com/python-semantic-release/python-semantic-release/pull/509), - [`cdf8116`](https://github.com/python-semantic-release/python-semantic-release/commit/cdf8116c1e415363b10a01f541873e04ad874220)) - -When running the application from a sub-directory in the VCS, the option use_only_cwd_commits will - filter out commits that does not changes the current working directory, similar to running - commands like `git log -- .` in a sub-directory. - - -## v7.33.5 (2023-05-19) - -### Bug Fixes - -- Update docs and default config for gitmoji changes - ([#590](https://github.com/python-semantic-release/python-semantic-release/pull/590), - [`192da6e`](https://github.com/python-semantic-release/python-semantic-release/commit/192da6e1352298b48630423d50191070a1c5ab24)) - -* fix: update docs and default config for gitmoji changes - -PR #582 updated to the latest Gitmojis release however the documentation and default config options - still referenced old and unsupported Gitmojis. - -* fix: update sphinx dep - -I could only build the documentation locally by updating Sphinx to the latest 1.x version. - -### Documentation - -- Update broken badge and add links - ([#591](https://github.com/python-semantic-release/python-semantic-release/pull/591), - [`0c23447`](https://github.com/python-semantic-release/python-semantic-release/commit/0c234475d27ad887b19170c82deb80293b3a95f1)) - -The "Test Status" badge was updated to address a recent breaking change in the GitHub actions API. - All the badges updated to add links to the appropriate resources for end-user convenience. - - -## v7.33.4 (2023-05-14) - -### Bug Fixes - -- If prerelease, publish prerelease - ([#587](https://github.com/python-semantic-release/python-semantic-release/pull/587), - [`927da9f`](https://github.com/python-semantic-release/python-semantic-release/commit/927da9f8feb881e02bc08b33dc559bd8e7fc41ab)) - -Co-authored-by: Ondrej Winter - - -## v7.33.3 (2023-04-24) - -### Bug Fixes - -- Trim emojis from config - ([#583](https://github.com/python-semantic-release/python-semantic-release/pull/583), - [`02902f7`](https://github.com/python-semantic-release/python-semantic-release/commit/02902f73ee961565c2470c000f00947d9ef06cb1)) - -- Update Gitmojis according to official node module - ([#582](https://github.com/python-semantic-release/python-semantic-release/pull/582), - [`806fcfa`](https://github.com/python-semantic-release/python-semantic-release/commit/806fcfa4cfdd3df4b380afd015a68dc90d54215a)) - -### Documentation - -- Grammar in `docs/troubleshooting.rst` - ([#557](https://github.com/python-semantic-release/python-semantic-release/pull/557), - [`bbe754a`](https://github.com/python-semantic-release/python-semantic-release/commit/bbe754a3db9ce7132749e7902fe118b52f48ee42)) - -- change contraction to a possessive pronoun - -Signed-off-by: Vladislav Doster - -- Spelling and grammar in `travis.rst` - ([#556](https://github.com/python-semantic-release/python-semantic-release/pull/556), - [`3a76e9d`](https://github.com/python-semantic-release/python-semantic-release/commit/3a76e9d7505c421009eb3e953c32cccac2e70e07)) - -- spelling - subject-verb agreement - remove verbiage - -Signed-off-by: Vladislav Doster - -- Update repository name - ([#559](https://github.com/python-semantic-release/python-semantic-release/pull/559), - [`5cdb05e`](https://github.com/python-semantic-release/python-semantic-release/commit/5cdb05e20f17b12890e1487c42d317dcbadd06c8)) - -In order to avoid 'Repository not found: relekang/python-semantic-release.' - - -## v7.33.2 (2023-02-17) - -### Bug Fixes - -- Inconsistent versioning between print-version and publish - ([#524](https://github.com/python-semantic-release/python-semantic-release/pull/524), - [`17d60e9`](https://github.com/python-semantic-release/python-semantic-release/commit/17d60e9bf66f62e5845065486c9d5e450f74839a)) - - -## v7.33.1 (2023-02-01) - -### Bug Fixes - -- **action**: Mark container fs as safe for git - ([#552](https://github.com/python-semantic-release/python-semantic-release/pull/552), - [`2a55f68`](https://github.com/python-semantic-release/python-semantic-release/commit/2a55f68e2b3cb9ffa9204c00ddbf12706af5c070)) - -See https://github.com/actions/runner-images/issues/6775#issuecomment-1409268124 and - https://github.com/actions/runner-images/issues/6775#issuecomment-1410270956 - - -## v7.33.0 (2023-01-15) - -### Bug Fixes - -- Bump Dockerfile to use Python 3.10 image - ([#536](https://github.com/python-semantic-release/python-semantic-release/pull/536), - [`8f2185d`](https://github.com/python-semantic-release/python-semantic-release/commit/8f2185d570b3966b667ac591ae523812e9d2e00f)) - -Fixes #533 - -Co-authored-by: Bernard Cooke - -- Changelog release commit search logic - ([#530](https://github.com/python-semantic-release/python-semantic-release/pull/530), - [`efb3410`](https://github.com/python-semantic-release/python-semantic-release/commit/efb341036196c39b4694ca4bfa56c6b3e0827c6c)) - -* Fixes changelog release commit search logic - -Running `semantic-release changelog` currently fails to identify "the last commit in [a] release" - because the compared commit messages have superfluous whitespace. Likely related to the issue - causing: https://github.com/relekang/python-semantic-release/issues/490 - -* Removes a couple of extra `strip()`s. - -- Fix mypy errors for publish - ([`b40dd48`](https://github.com/python-semantic-release/python-semantic-release/commit/b40dd484387c1b3f78df53ee2d35e281e8e799c8)) - -- Formatting in docs - ([`2e8227a`](https://github.com/python-semantic-release/python-semantic-release/commit/2e8227a8a933683250f8dace019df15fdb35a857)) - -- Update documentaton - ([`5cbdad2`](https://github.com/python-semantic-release/python-semantic-release/commit/5cbdad296034a792c9bf05e3700eac4f847eb469)) - -- **action**: Fix environment variable names - ([`3c66218`](https://github.com/python-semantic-release/python-semantic-release/commit/3c66218640044adf263fcf9b2714cfc4b99c2e90)) - -### Features - -- Add signing options to action - ([`31ad5eb`](https://github.com/python-semantic-release/python-semantic-release/commit/31ad5eb5a25f0ea703afc295351104aefd66cac1)) - -- Update action with configuration options - ([#518](https://github.com/python-semantic-release/python-semantic-release/pull/518), - [`4664afe`](https://github.com/python-semantic-release/python-semantic-release/commit/4664afe5f80a04834e398fefb841b166a51d95b7)) - -Co-authored-by: Kevin Watson - -- **repository**: Add support for TWINE_CERT - ([#522](https://github.com/python-semantic-release/python-semantic-release/pull/522), - [`d56e85d`](https://github.com/python-semantic-release/python-semantic-release/commit/d56e85d1f2ac66fb0b59af2178164ca915dbe163)) - -Fixes #521 - - -## v7.32.2 (2022-10-22) - -### Bug Fixes - -- Fix changelog generation in tag-mode - ([#171](https://github.com/python-semantic-release/python-semantic-release/pull/171), - [`482a62e`](https://github.com/python-semantic-release/python-semantic-release/commit/482a62ec374208b2d57675cb0b7f0ab9695849b9)) - -### Documentation - -- Fix code blocks - ([#506](https://github.com/python-semantic-release/python-semantic-release/pull/506), - [`24b7673`](https://github.com/python-semantic-release/python-semantic-release/commit/24b767339fcef1c843f7dd3188900adab05e03b1)) - -Previously: https://i.imgur.com/XWFhG7a.png - - -## v7.32.1 (2022-10-07) - -### Bug Fixes - -- Corrections for deprecation warnings - ([#505](https://github.com/python-semantic-release/python-semantic-release/pull/505), - [`d47afb6`](https://github.com/python-semantic-release/python-semantic-release/commit/d47afb6516238939e174f946977bf4880062a622)) - -### Documentation - -- Correct spelling mistakes - ([#504](https://github.com/python-semantic-release/python-semantic-release/pull/504), - [`3717e0d`](https://github.com/python-semantic-release/python-semantic-release/commit/3717e0d8810f5d683847c7b0e335eeefebbf2921)) - - -## v7.32.0 (2022-09-25) - -### Documentation - -- Correct documented default behaviour for `commit_version_number` - ([#497](https://github.com/python-semantic-release/python-semantic-release/pull/497), - [`ffae2dc`](https://github.com/python-semantic-release/python-semantic-release/commit/ffae2dc68f7f4bc13c5fd015acd43b457e568ada)) - -### Features - -- Add setting for enforcing textual changelog sections - ([#502](https://github.com/python-semantic-release/python-semantic-release/pull/502), - [`988437d`](https://github.com/python-semantic-release/python-semantic-release/commit/988437d21e40d3e3b1c95ed66b535bdd523210de)) - -Resolves #498 - -Add the `use_textual_changelog_sections` setting flag for enforcing that changelog section headings - will always be regular ASCII when using the Emoji parser. - - -## v7.31.4 (2022-08-23) - -### Bug Fixes - -- Account for trailing newlines in commit messages - ([#495](https://github.com/python-semantic-release/python-semantic-release/pull/495), - [`111b151`](https://github.com/python-semantic-release/python-semantic-release/commit/111b1518e8c8e2bd7535bd4c4b126548da384605)) - -Fixes #490 - - -## v7.31.3 (2022-08-22) - -### Bug Fixes - -- Use `commit_subject` when searching for release commits - ([#488](https://github.com/python-semantic-release/python-semantic-release/pull/488), - [`3849ed9`](https://github.com/python-semantic-release/python-semantic-release/commit/3849ed992c3cff9054b8690bcf59e49768f84f47)) - -Co-authored-by: Dzmitry Ryzhykau - - -## v7.31.2 (2022-07-29) - -### Bug Fixes - -- Add better handling of missing changelog placeholder - ([`e7a0e81`](https://github.com/python-semantic-release/python-semantic-release/commit/e7a0e81c004ade73ed927ba4de8c3e3ccaf0047c)) - -There is still one case where we don't add it, but in those corner cases it would be better to do it - manually than to make it mangled. - -Fixes #454 - -- Add repo=None when not in git repo - ([`40be804`](https://github.com/python-semantic-release/python-semantic-release/commit/40be804c09ab8a036fb135c9c38a63f206d2742c)) - -Fixes #422 - -### Documentation - -- Add example for pyproject.toml - ([`2a4b8af`](https://github.com/python-semantic-release/python-semantic-release/commit/2a4b8af1c2893a769c02476bb92f760c8522bd7a)) - - -## v7.31.1 (2022-07-29) - -### Bug Fixes - -- Update git email in action - ([`0ece6f2`](https://github.com/python-semantic-release/python-semantic-release/commit/0ece6f263ff02a17bb1e00e7ed21c490f72e3d00)) - -Fixes #473 - - -## v7.31.0 (2022-07-29) - -### Bug Fixes - -- :bug: fix get_current_release_version for tag_only version_source - ([`cad09be`](https://github.com/python-semantic-release/python-semantic-release/commit/cad09be9ba067f1c882379c0f4b28115a287fc2b)) - -### Features - -- Add prerelease-patch and no-prerelease-patch flags for whether to auto-bump prereleases - ([`b4e5b62`](https://github.com/python-semantic-release/python-semantic-release/commit/b4e5b626074f969e4140c75fdac837a0625cfbf6)) - -- Override repository_url w REPOSITORY_URL env var - ([#439](https://github.com/python-semantic-release/python-semantic-release/pull/439), - [`cb7578c`](https://github.com/python-semantic-release/python-semantic-release/commit/cb7578cf005b8bd65d9b988f6f773e4c060982e3)) - - -## v7.30.2 (2022-07-26) - -### Bug Fixes - -- Declare additional_options as action inputs - ([#481](https://github.com/python-semantic-release/python-semantic-release/pull/481), - [`cb5d8c7`](https://github.com/python-semantic-release/python-semantic-release/commit/cb5d8c7ce7d013fcfabd7696b5ffb846a8a6f853)) - - -## v7.30.1 (2022-07-25) - -### Bug Fixes - -- Don't use commit_subject for tag pattern matching - ([#480](https://github.com/python-semantic-release/python-semantic-release/pull/480), - [`ac3f11e`](https://github.com/python-semantic-release/python-semantic-release/commit/ac3f11e689f4a290d20b68b9c5c214098eb61b5f)) - - -## v7.30.0 (2022-07-25) - -### Bug Fixes - -- Allow empty additional options - ([#479](https://github.com/python-semantic-release/python-semantic-release/pull/479), - [`c9b2514`](https://github.com/python-semantic-release/python-semantic-release/commit/c9b2514d3e164b20e78b33f60989d78c2587e1df)) - -### Features - -- Add `additional_options` input for GitHub Action - ([#477](https://github.com/python-semantic-release/python-semantic-release/pull/477), - [`aea60e3`](https://github.com/python-semantic-release/python-semantic-release/commit/aea60e3d290c6fe3137bff21e0db1ed936233776)) - - -## v7.29.7 (2022-07-24) - -### Bug Fixes - -- Ignore dependency version bumps when parsing version from commit logs - ([#476](https://github.com/python-semantic-release/python-semantic-release/pull/476), - [`51bcb78`](https://github.com/python-semantic-release/python-semantic-release/commit/51bcb780a9f55fadfaf01612ff65c1f92642c2c1)) - - -## v7.29.6 (2022-07-15) - -### Bug Fixes - -- Allow changing prerelease tag using CLI flags - ([#466](https://github.com/python-semantic-release/python-semantic-release/pull/466), - [`395bf4f`](https://github.com/python-semantic-release/python-semantic-release/commit/395bf4f2de73663c070f37cced85162d41934213)) - -Delay construction of version and release patterns until runtime. This will allow to use non-default - prerelease tag. - -Co-authored-by: Dzmitry Ryzhykau - - -## v7.29.5 (2022-07-14) - -### Bug Fixes - -- Add packaging module requirement - ([#469](https://github.com/python-semantic-release/python-semantic-release/pull/469), - [`b99c9fa`](https://github.com/python-semantic-release/python-semantic-release/commit/b99c9fa88dc25e5ceacb131cd93d9079c4fb2c86)) - -- **publish**: Get version bump for current release - ([#467](https://github.com/python-semantic-release/python-semantic-release/pull/467), - [`dd26888`](https://github.com/python-semantic-release/python-semantic-release/commit/dd26888a923b2f480303c19f1916647de48b02bf)) - -Replicate the behavior of "version" command in version calculation. - -Co-authored-by: Dzmitry Ryzhykau - - -## v7.29.4 (2022-06-29) - -### Bug Fixes - -- Add text for empty ValueError - ([#461](https://github.com/python-semantic-release/python-semantic-release/pull/461), - [`733254a`](https://github.com/python-semantic-release/python-semantic-release/commit/733254a99320d8c2f964d799ac4ec29737867faa)) - - -## v7.29.3 (2022-06-26) - -### Bug Fixes - -- Ensure that assets can be uploaded successfully on custom GitHub servers - ([#458](https://github.com/python-semantic-release/python-semantic-release/pull/458), - [`32b516d`](https://github.com/python-semantic-release/python-semantic-release/commit/32b516d7aded4afcafe4aa56d6a5a329b3fc371d)) - -Signed-off-by: Chris Butler - - -## v7.29.2 (2022-06-20) - -### Bug Fixes - -- Ensure should_bump checks against release version if not prerelease - ([#457](https://github.com/python-semantic-release/python-semantic-release/pull/457), - [`da0606f`](https://github.com/python-semantic-release/python-semantic-release/commit/da0606f0d67ada5f097c704b9423ead3b5aca6b2)) - -Co-authored-by: Sebastian Seith - - -## v7.29.1 (2022-06-01) - -### Bug Fixes - -- Capture correct release version when patch has more than one digit - ([#448](https://github.com/python-semantic-release/python-semantic-release/pull/448), - [`426cdc7`](https://github.com/python-semantic-release/python-semantic-release/commit/426cdc7d7e0140da67f33b6853af71b2295aaac2)) - - -## v7.29.0 (2022-05-27) - -### Bug Fixes - -- Fix and refactor prerelease - ([#435](https://github.com/python-semantic-release/python-semantic-release/pull/435), - [`94c9494`](https://github.com/python-semantic-release/python-semantic-release/commit/94c94942561f85f48433c95fd3467e03e0893ab4)) - -### Features - -- Allow using ssh-key to push version while using token to publish to hvcs - ([#419](https://github.com/python-semantic-release/python-semantic-release/pull/419), - [`7b2dffa`](https://github.com/python-semantic-release/python-semantic-release/commit/7b2dffadf43c77d5e0eea307aefcee5c7744df5c)) - -* feat(config): add ignore_token_for_push param - -Add ignore_token_for_push parameter that allows using the underlying git authentication mechanism - for pushing a new version commit and tags while also using an specified token to upload dists - -* test(config): add test for ignore_token_for_push - -Test push_new_version with token while ignore_token_for_push is True and False - -* docs: add documentation for ignore_token_for_push - -* fix(test): override GITHUB_ACTOR env - -push_new_version is using GITHUB_ACTOR env var but we did not contemplate in our new tests that - actually Github actions running the tests will populate that var and change the test outcome - -Now we control the value of that env var and test for it being present or not - - -## v7.28.1 (2022-04-14) - -### Bug Fixes - -- Fix getting current version when `version_source=tag_only` - ([#437](https://github.com/python-semantic-release/python-semantic-release/pull/437), - [`b247936`](https://github.com/python-semantic-release/python-semantic-release/commit/b247936a81c0d859a34bf9f17ab8ca6a80488081)) - - -## v7.28.0 (2022-04-11) - -### Features - -- Add `tag_only` option for `version_source` - ([#436](https://github.com/python-semantic-release/python-semantic-release/pull/436), - [`cf74339`](https://github.com/python-semantic-release/python-semantic-release/commit/cf743395456a86c62679c2c0342502af043bfc3b)) - -Fixes #354 - - -## v7.27.1 (2022-04-03) - -### Bug Fixes - -- **prerelase**: Pass prerelease option to get_current_version - ([#432](https://github.com/python-semantic-release/python-semantic-release/pull/432), - [`aabab0b`](https://github.com/python-semantic-release/python-semantic-release/commit/aabab0b7ce647d25e0c78ae6566f1132ece9fcb9)) - -The `get_current_version` function accepts a `prerelease` argument which was never passed. - - -## v7.27.0 (2022-03-15) - -### Features - -- Add git-lfs to docker container - ([#427](https://github.com/python-semantic-release/python-semantic-release/pull/427), - [`184e365`](https://github.com/python-semantic-release/python-semantic-release/commit/184e3653932979b82e5a62b497f2a46cbe15ba87)) - - -## v7.26.0 (2022-03-07) - -### Features - -- Add prerelease functionality - ([#413](https://github.com/python-semantic-release/python-semantic-release/pull/413), - [`7064265`](https://github.com/python-semantic-release/python-semantic-release/commit/7064265627a2aba09caa2873d823b594e0e23e77)) - -* chore: add initial todos * feat: add prerelease tag option * feat: add prerelease cli flag * feat: - omit_pattern for previouse and current version getters * feat: print_version with prerelease bump - * feat: make print_version prerelease ready * feat: move prerelease determination to - get_new_version * test: improve get_last_version test * docs: added basic infos about prereleases - * feat: add prerelease flag to version and publish * feat: remove leftover todos - -Co-authored-by: Mario Jäckle - - -## v7.25.2 (2022-02-24) - -### Bug Fixes - -- **gitea**: Use form-data from asset upload - ([#421](https://github.com/python-semantic-release/python-semantic-release/pull/421), - [`e011944`](https://github.com/python-semantic-release/python-semantic-release/commit/e011944987885f75b80fe16a363f4befb2519a91)) - - -## v7.25.1 (2022-02-23) - -### Bug Fixes - -- **gitea**: Build status and asset upload - ([#420](https://github.com/python-semantic-release/python-semantic-release/pull/420), - [`57db81f`](https://github.com/python-semantic-release/python-semantic-release/commit/57db81f4c6b96da8259e3bad9137eaccbcd10f6e)) - -* fix(gitea): handle list build status response * fix(gitea): use form-data for upload_asset - - -## v7.25.0 (2022-02-17) - -### Documentation - -- Document tag_commit - ([`b631ca0`](https://github.com/python-semantic-release/python-semantic-release/commit/b631ca0a79cb2d5499715d43688fc284cffb3044)) - -Fixes #410 - -### Features - -- **hvcs**: Add gitea support - ([#412](https://github.com/python-semantic-release/python-semantic-release/pull/412), - [`b7e7936`](https://github.com/python-semantic-release/python-semantic-release/commit/b7e7936331b7939db09abab235c8866d800ddc1a)) - - -## v7.24.0 (2022-01-24) - -### Features - -- Include additional changes in release commits - ([`3e34f95`](https://github.com/python-semantic-release/python-semantic-release/commit/3e34f957ff5a3ec6e6f984cc4a79a38ce4391ea9)) - -Add new config keys, `pre_commit_command` and `commit_additional_files`, to allow custom file - changes alongside the release commits. - - -## v7.23.0 (2021-11-30) - -### Features - -- Support Github Enterprise server - ([`b4e01f1`](https://github.com/python-semantic-release/python-semantic-release/commit/b4e01f1b7e841263fa84f57f0ac331f7c0b31954)) - - -## v7.22.0 (2021-11-21) - -### Bug Fixes - -- Address PR feedback for `parser_angular.py` - ([`f7bc458`](https://github.com/python-semantic-release/python-semantic-release/commit/f7bc45841e6a5c762f99f936c292cee25fabcd02)) - -- `angular_parser_default_level_bump` should have plain-english settings - rename `TYPES` variable - to `LONG_TYPE_NAMES` - -### Features - -- **parser_angular**: Allow customization in parser - ([`298eebb`](https://github.com/python-semantic-release/python-semantic-release/commit/298eebbfab5c083505036ba1df47a5874a1eed6e)) - -- `parser_angular_allowed_types` controls allowed types - defaults stay the same: build, chore, ci, - docs, feat, fix, perf, style, refactor, test - `parser_angular_default_level_bump` controls the - default level to bump the version by - default stays at 0 - `parser_angular_minor_types` controls - which types trigger a minor version bump - default stays at only 'feat' - - `parser_angular_patch_types` controls which types trigger a patch version - default stays at 'fix' - or 'perf' - - -## v7.21.0 (2021-11-21) - -### Bug Fixes - -- Remove invalid repository exception - ([`746b62d`](https://github.com/python-semantic-release/python-semantic-release/commit/746b62d4e207a5d491eecd4ca96d096eb22e3bed)) - -### Features - -- Use gitlab-ci or github actions env vars - ([`8ca8dd4`](https://github.com/python-semantic-release/python-semantic-release/commit/8ca8dd40f742f823af147928bd75a9577c50d0fd)) - -return owner and project name from Gitlab/Github environment variables if available - -Issue #363 - - -## v7.20.0 (2021-11-21) - -### Bug Fixes - -- Don't use linux commands on windows - ([#393](https://github.com/python-semantic-release/python-semantic-release/pull/393), - [`5bcccd2`](https://github.com/python-semantic-release/python-semantic-release/commit/5bcccd21cc8be3289db260e645fec8dc6a592abd)) - -- Mypy errors in vcs_helpers - ([`13ca0fe`](https://github.com/python-semantic-release/python-semantic-release/commit/13ca0fe650125be2f5e953f6193fdc4d44d3c75a)) - -- Skip removing the build folder if it doesn't exist - ([`8e79fdc`](https://github.com/python-semantic-release/python-semantic-release/commit/8e79fdc107ffd852a91dfb5473e7bd1dfaba4ee5)) - -https://github.com/relekang/python-semantic-release/issues/391#issuecomment-950667599 - -### Documentation - -- Clean typos and add section for repository upload - ([`1efa18a`](https://github.com/python-semantic-release/python-semantic-release/commit/1efa18a3a55134d6bc6e4572ab025e24082476cd)) - -Add more details and external links - -### Features - -- Allow custom environment variable names - ([#392](https://github.com/python-semantic-release/python-semantic-release/pull/392), - [`372cda3`](https://github.com/python-semantic-release/python-semantic-release/commit/372cda3497f16ead2209e6e1377d38f497144883)) - -* GH_TOKEN can now be customized by setting github_token_var * GL_TOKEN can now be customized by - setting gitlab_token_var * PYPI_PASSWORD can now be customized by setting pypi_pass_var * - PYPI_TOKEN can now be customized by setting pypi_token_var * PYPI_USERNAME can now be customized - by setting pypi_user_var - -- Rewrite Twine adapter for uploading to artifact repositories - ([`cfb20af`](https://github.com/python-semantic-release/python-semantic-release/commit/cfb20af79a8e25a77aee9ff72deedcd63cb7f62f)) - -Artifact upload generalised to fully support custom repositories like GitLab. Rewritten to use twine - python api instead of running the executable. No-op mode now respected by artifact upload. - - -## v7.19.2 (2021-09-04) - -### Bug Fixes - -- Fixed ImproperConfig import error - ([#377](https://github.com/python-semantic-release/python-semantic-release/pull/377), - [`b011a95`](https://github.com/python-semantic-release/python-semantic-release/commit/b011a9595df4240cb190bfb1ab5b6d170e430dfc)) - - -## v7.19.1 (2021-08-17) - -### Bug Fixes - -- Add get_formatted_tag helper instead of hardcoded v-prefix in the git tags - ([`1a354c8`](https://github.com/python-semantic-release/python-semantic-release/commit/1a354c86abad77563ebce9a6944256461006f3c7)) - - -## v7.19.0 (2021-08-16) - -### Documentation - -- **parser**: Documentation for scipy-parser - ([`45ee34a`](https://github.com/python-semantic-release/python-semantic-release/commit/45ee34aa21443860a6c2cd44a52da2f353b960bf)) - -### Features - -- Custom git tag format support - ([#373](https://github.com/python-semantic-release/python-semantic-release/pull/373), - [`1d76632`](https://github.com/python-semantic-release/python-semantic-release/commit/1d76632043bf0b6076d214a63c92013624f4b95e)) - -* feat: custom git tag format support * test: add git tag format check * docs: add tag_format config - option - - -## v7.18.0 (2021-08-09) - -### Documentation - -- Clarify second argument of ParsedCommit - ([`086ddc2`](https://github.com/python-semantic-release/python-semantic-release/commit/086ddc28f06522453328f5ea94c873bd202ff496)) - -### Features - -- Add support for non-prefixed tags - ([#366](https://github.com/python-semantic-release/python-semantic-release/pull/366), - [`0fee4dd`](https://github.com/python-semantic-release/python-semantic-release/commit/0fee4ddb5baaddf85ed6b76e76a04474a5f97d0a)) - - -## v7.17.0 (2021-08-07) - -### Features - -- **parser**: Add scipy style parser - ([#369](https://github.com/python-semantic-release/python-semantic-release/pull/369), - [`51a3921`](https://github.com/python-semantic-release/python-semantic-release/commit/51a39213ea120c4bbd7a57b74d4f0cc3103da9f5)) - - -## v7.16.4 (2021-08-03) - -### Bug Fixes - -- Correct rendering of gitlab issue references - ([`07429ec`](https://github.com/python-semantic-release/python-semantic-release/commit/07429ec4a32d32069f25ec77b4bea963bd5d2a00)) - -resolves #358 - - -## v7.16.3 (2021-07-29) - -### Bug Fixes - -- Print right info if token is not set (#360) - ([#361](https://github.com/python-semantic-release/python-semantic-release/pull/361), - [`a275a7a`](https://github.com/python-semantic-release/python-semantic-release/commit/a275a7a17def85ff0b41d254e4ee42772cce1981)) - -Co-authored-by: Laercio Barbosa - - -## v7.16.2 (2021-06-25) - -### Bug Fixes - -- Use release-api for gitlab - ([`1ef5cab`](https://github.com/python-semantic-release/python-semantic-release/commit/1ef5caba2d8dd0f2647bc51ede0ef7152d8b7b8d)) - -### Documentation - -- Recommend setting a concurrency group for GitHub Actions - ([`34b0735`](https://github.com/python-semantic-release/python-semantic-release/commit/34b07357ab3f4f4aa787b71183816ec8aaf334a8)) - -- Update trove classifiers to reflect supported versions - ([#344](https://github.com/python-semantic-release/python-semantic-release/pull/344), - [`7578004`](https://github.com/python-semantic-release/python-semantic-release/commit/7578004ed4b20c2bd553782443dfd77535faa377)) - - -## v7.16.1 (2021-06-08) - -### Bug Fixes - -- Tomlkit should stay at 0.7.0 - ([`769a5f3`](https://github.com/python-semantic-release/python-semantic-release/commit/769a5f31115cdb1f43f19a23fe72b96a8c8ba0fc)) - -See https://github.com/relekang/python-semantic-release/pull/339#discussion_r647629387 - - -## v7.16.0 (2021-06-08) - -### Features - -- Add option to omit tagging - ([#341](https://github.com/python-semantic-release/python-semantic-release/pull/341), - [`20603e5`](https://github.com/python-semantic-release/python-semantic-release/commit/20603e53116d4f05e822784ce731b42e8cbc5d8f)) - - -## v7.15.6 (2021-06-08) - -### Bug Fixes - -- Update click and tomlkit - ([#339](https://github.com/python-semantic-release/python-semantic-release/pull/339), - [`947ea3b`](https://github.com/python-semantic-release/python-semantic-release/commit/947ea3bc0750735941446cf4a87bae20e750ba12)) - - -## v7.15.5 (2021-05-26) - -### Bug Fixes - -- Pin tomlkit to 0.7.0 - ([`2cd0db4`](https://github.com/python-semantic-release/python-semantic-release/commit/2cd0db4537bb9497b72eb496f6bab003070672ab)) - - -## v7.15.4 (2021-04-29) - -### Bug Fixes - -- Change log level of failed toml loading - ([`24bb079`](https://github.com/python-semantic-release/python-semantic-release/commit/24bb079cbeff12e7043dd35dd0b5ae03192383bb)) - -Fixes #235 - - -## v7.15.3 (2021-04-03) - -### Bug Fixes - -- Add venv to path in github action - ([`583c5a1`](https://github.com/python-semantic-release/python-semantic-release/commit/583c5a13e40061fc544b82decfe27a6c34f6d265)) - - -## v7.15.2 (2021-04-03) - -### Bug Fixes - -- Run semantic-release in virtualenv in the github action - ([`b508ea9`](https://github.com/python-semantic-release/python-semantic-release/commit/b508ea9f411c1cd4f722f929aab9f0efc0890448)) - -Fixes #331 - -- Set correct path for venv in action script - ([`aac02b5`](https://github.com/python-semantic-release/python-semantic-release/commit/aac02b5a44a6959328d5879578aa3536bdf856c2)) - -- Use absolute path for venv in github action - ([`d4823b3`](https://github.com/python-semantic-release/python-semantic-release/commit/d4823b3b6b1fcd5c33b354f814643c9aaf85a06a)) - -### Documentation - -- Clarify that HVCS should be lowercase - ([`da0ab0c`](https://github.com/python-semantic-release/python-semantic-release/commit/da0ab0c62c4ce2fa0d815e5558aeec1a1e23bc89)) - -Fixes #330 - - -## v7.15.1 (2021-03-26) - -### Bug Fixes - -- Add support for setting build_command to "false" - ([`520cf1e`](https://github.com/python-semantic-release/python-semantic-release/commit/520cf1eaa7816d0364407dbd17b5bc7c79806086)) - -Fixes #328 - -- Upgrade python-gitlab range - ([`abfacc4`](https://github.com/python-semantic-release/python-semantic-release/commit/abfacc432300941d57488842e41c06d885637e6c)) - -Keeping both 1.x and 2.x since only change that is breaking is dropping python 3.6 support. I hope - that leaving the lower limit will make it still work with python 3.6. - -Fixes #329 - -### Documentation - -- Add common options to documentation - ([`20d79a5`](https://github.com/python-semantic-release/python-semantic-release/commit/20d79a51bffa26d40607c1b77d10912992279112)) - -These can be found by running `semantic-release --help`, but including them in the documentation - will be helpful for CI users who don't have the command installed locally. - -Related to #327. - - -## v7.15.0 (2021-02-18) - -### Documentation - -- Add documentation for releasing on a Jenkins instance - ([#324](https://github.com/python-semantic-release/python-semantic-release/pull/324), - [`77ad988`](https://github.com/python-semantic-release/python-semantic-release/commit/77ad988a2057be59e4559614a234d6871c06ee37)) - -### Features - -- Allow the use of .pypirc for twine uploads - ([#325](https://github.com/python-semantic-release/python-semantic-release/pull/325), - [`6bc56b8`](https://github.com/python-semantic-release/python-semantic-release/commit/6bc56b8aa63069a25a828a2d1a9038ecd09b7d5d)) - - -## v7.14.0 (2021-02-11) - -### Documentation - -- Correct casing on proper nouns - ([#320](https://github.com/python-semantic-release/python-semantic-release/pull/320), - [`d51b999`](https://github.com/python-semantic-release/python-semantic-release/commit/d51b999a245a4e56ff7a09d0495c75336f2f150d)) - -* docs: correcting Semantic Versioning casing - -Semantic Versioning is the name of the specification. Therefore it is a proper noun. This patch - corrects the incorrect casing for Semantic Versioning. - -* docs: correcting Python casing - -This patch corrects the incorrect casing for Python. - -### Features - -- **checks**: Add support for Jenkins CI - ([#322](https://github.com/python-semantic-release/python-semantic-release/pull/322), - [`3e99855`](https://github.com/python-semantic-release/python-semantic-release/commit/3e99855c6bc72b3e9a572c58cc14e82ddeebfff8)) - -Includes a ci check handler to verify jenkins. Unlike other ci systems jenkins doesn't generally - prefix things with `JENKINS` or simply inject `JENKINS=true` Really the only thing that is - immediately identifiable is `JENKINS_URL` - - -## v7.13.2 (2021-01-29) - -### Bug Fixes - -- Fix crash when TOML has no PSR section - ([#319](https://github.com/python-semantic-release/python-semantic-release/pull/319), - [`5f8ab99`](https://github.com/python-semantic-release/python-semantic-release/commit/5f8ab99bf7254508f4b38fcddef2bdde8dd15a4c)) - -* test: reproduce issue with TOML without PSR section - -* fix: crash when TOML has no PSR section - -* chore: remove unused imports - -### Documentation - -- Fix `version_toml` example for Poetry - ([#318](https://github.com/python-semantic-release/python-semantic-release/pull/318), - [`39acb68`](https://github.com/python-semantic-release/python-semantic-release/commit/39acb68bfffe8242040e476893639ba26fa0d6b5)) - - -## v7.13.1 (2021-01-26) - -### Bug Fixes - -- Use multiline version_pattern match in replace - ([#315](https://github.com/python-semantic-release/python-semantic-release/pull/315), - [`1a85af4`](https://github.com/python-semantic-release/python-semantic-release/commit/1a85af434325ce52e11b49895e115f7a936e417e)) - -Fixes #306 - - -## v7.13.0 (2021-01-26) - -### Features - -- Support toml files for version declaration - ([#307](https://github.com/python-semantic-release/python-semantic-release/pull/307), - [`9b62a7e`](https://github.com/python-semantic-release/python-semantic-release/commit/9b62a7e377378667e716384684a47cdf392093fa)) - -This introduce a new `version_toml` configuration property that behaves like `version_pattern` and - `version_variable`. - -For poetry support, user should now set `version_toml = pyproject.toml:tool.poetry.version`. - -This introduce an ABC class, `VersionDeclaration`, that can be implemented to add other version - declarations with ease. - -`toml` dependency has been replaced by `tomlkit`, as this is used the library used by poetry to - generate the `pyproject.toml` file, and is able to keep the ordering of data defined in the file. - -Existing `VersionPattern` class has been renamed to `PatternVersionDeclaration` and now implements - `VersionDeclaration`. - -`load_version_patterns()` function has been renamed to `load_version_declarations()` and now return - a list of `VersionDeclaration` implementations. - -Close #245 Close #275 - - -## v7.12.0 (2021-01-25) - -### Documentation - -- **actions**: Pat must be passed to checkout step too - ([`e2d8e47`](https://github.com/python-semantic-release/python-semantic-release/commit/e2d8e47d2b02860881381318dcc088e150c0fcde)) - -Fixes #311 - -### Features - -- **github**: Retry GitHub API requests on failure - ([#314](https://github.com/python-semantic-release/python-semantic-release/pull/314), - [`ac241ed`](https://github.com/python-semantic-release/python-semantic-release/commit/ac241edf4de39f4fc0ff561a749fa85caaf9e2ae)) - -* refactor(github): use requests.Session to call raise_for_status - -* fix(github): add retries to github API requests - - -## v7.11.0 (2021-01-08) - -### Bug Fixes - -- Add dot to --define option help - ([`eb4107d`](https://github.com/python-semantic-release/python-semantic-release/commit/eb4107d2efdf8c885c8ae35f48f1b908d1fced32)) - -- Avoid Unknown bump level 0 message - ([`8ab624c`](https://github.com/python-semantic-release/python-semantic-release/commit/8ab624cf3508b57a9656a0a212bfee59379d6f8b)) - -This issue occurs when some commits are available but are all to level 0. - -- **actions**: Fix github actions with new main location - ([`6666672`](https://github.com/python-semantic-release/python-semantic-release/commit/6666672d3d97ab7cdf47badfa3663f1a69c2dbdf)) - -### Build System - -- Add __main__.py magic file - ([`e93f36a`](https://github.com/python-semantic-release/python-semantic-release/commit/e93f36a7a10e48afb42c1dc3d860a5e2a07cf353)) - -This file allow to run the package from sources properly with `python -m semantic_release`. - -### Features - -- **print-version**: Add print-version command to output version - ([`512e3d9`](https://github.com/python-semantic-release/python-semantic-release/commit/512e3d92706055bdf8d08b7c82927d3530183079)) - -This new command can be integrated in the build process before the effective release, ie. to rename - some files with the version number. - -Users may invoke `VERSION=$(semantic-release print-version)` to retrieve the version that will be - generated during the release before it really occurs. - - -## v7.10.0 (2021-01-08) - -### Documentation - -- Fix incorrect reference syntax - ([`42027f0`](https://github.com/python-semantic-release/python-semantic-release/commit/42027f0d2bb64f4c9eaec65112bf7b6f67568e60)) - -- Rewrite getting started page - ([`97a9046`](https://github.com/python-semantic-release/python-semantic-release/commit/97a90463872502d1207890ae1d9dd008b1834385)) - -### Features - -- **build**: Allow falsy values for build_command to disable build step - ([`c07a440`](https://github.com/python-semantic-release/python-semantic-release/commit/c07a440f2dfc45a2ad8f7c454aaac180c4651f70)) - - -## v7.9.0 (2020-12-21) - -### Bug Fixes - -- **history**: Coerce version to string - ([#298](https://github.com/python-semantic-release/python-semantic-release/pull/298), - [`d4cdc3d`](https://github.com/python-semantic-release/python-semantic-release/commit/d4cdc3d3cd2d93f2a78f485e3ea107ac816c7d00)) - -The changes in #297 mistakenly omitted coercing the return value to a string. This resulted in - errors like: "can only concatenate str (not "VersionInfo") to str" - -Add test case asserting it's type str - -- **history**: Require semver >= 2.10 - ([`5087e54`](https://github.com/python-semantic-release/python-semantic-release/commit/5087e549399648cf2e23339a037b33ca8b62d954)) - -This resolves deprecation warnings, and updates this to a more 3.x compatible syntax - -### Features - -- **hvcs**: Add hvcs_domain config option - ([`ab3061a`](https://github.com/python-semantic-release/python-semantic-release/commit/ab3061ae93c49d71afca043b67b361e2eb2919e6)) - -While Gitlab already has an env var that should provide the vanity URL, this supports a generic - 'hvcs_domain' parameter that makes the hostname configurable for both GHE and Gitlab. - -This will also use the configured hostname (from either source) in the changelog links - -Fixes: #277 - - -## v7.8.2 (2020-12-19) - -### Bug Fixes - -- **cli**: Skip remove_dist where not needed - ([`04817d4`](https://github.com/python-semantic-release/python-semantic-release/commit/04817d4ecfc693195e28c80455bfbb127485f36b)) - -Skip removing dist files when upload_pypi or upload_release are not set - -### Features - -- **repository**: Add to settings artifact repository - ([`f4ef373`](https://github.com/python-semantic-release/python-semantic-release/commit/f4ef3733b948282fba5a832c5c0af134609b26d2)) - -- Add new config var to set repository (repository_url) - Remove 'Pypi' word when it refers - generically to an artifact repository system - Depreciate 'PYPI_USERNAME' and 'PYPI_PASSWORD' and - prefer 'REPOSITORY_USERNAME' and 'REPOSITORY_PASSWORD' env vars - Depreciate every config key with - 'pypi' and prefer repository - Update doc in accordance with those changes - - -## v7.8.1 (2020-12-18) - -### Bug Fixes - -- Filenames with unknown mimetype are now properly uploaded to github release - ([`f3ece78`](https://github.com/python-semantic-release/python-semantic-release/commit/f3ece78b2913e70f6b99907b192a1e92bbfd6b77)) - -When mimetype can't be guessed, content-type header is set to None. But it's mandatory for the file - upload to work properly. In this case, application/octect-stream is now used as a fallback. - -- **logs**: Fix TypeError when enabling debug logs - ([`2591a94`](https://github.com/python-semantic-release/python-semantic-release/commit/2591a94115114c4a91a48f5b10b3954f6ac932a1)) - -Some logger invocation were raising the following error: TypeError: not all arguments converted - during string formatting. - -This also refactor some other parts to use f-strings as much as possible. - - -## v7.8.0 (2020-12-18) - -### Bug Fixes - -- **changelog**: Use "issues" link vs "pull" - ([`93e48c9`](https://github.com/python-semantic-release/python-semantic-release/commit/93e48c992cb8b763f430ecbb0b7f9c3ca00036e4)) - -While, e.g., https://github.com/owner/repos/pull/123 will work, - https://github.com/owner/repos/issues/123 should be safer / more consistent, and should avoid a - failure if someone adds an issue link at the end of a PR that is merged via rebase merge or merge - commit. - -- **netrc**: Prefer using token defined in GH_TOKEN instead of .netrc file - ([`3af32a7`](https://github.com/python-semantic-release/python-semantic-release/commit/3af32a738f2f2841fd75ec961a8f49a0b1c387cf)) - -.netrc file will only be used when available and no GH_TOKEN environment variable is defined. - -This also add a test to make sure .netrc is used properly when no GH_TOKEN is defined. - -### Features - -- Add `upload_to_pypi_glob_patterns` option - ([`42305ed`](https://github.com/python-semantic-release/python-semantic-release/commit/42305ed499ca08c819c4e7e65fcfbae913b8e6e1)) - - -## v7.7.0 (2020-12-12) - -### Features - -- **changelog**: Add PR links in markdown - ([#282](https://github.com/python-semantic-release/python-semantic-release/pull/282), - [`0448f6c`](https://github.com/python-semantic-release/python-semantic-release/commit/0448f6c350bbbf239a81fe13dc5f45761efa7673)) - -GitHub release notes automagically link to the PR, but changelog markdown doesn't. Replace a PR - number at the end of a message with a markdown link. - - -## v7.6.0 (2020-12-06) - -### Documentation - -- Add documentation for option `major_on_zero` - ([`2e8b26e`](https://github.com/python-semantic-release/python-semantic-release/commit/2e8b26e4ee0316a2cf2a93c09c783024fcd6b3ba)) - -### Features - -- Add `major_on_zero` option - ([`d324154`](https://github.com/python-semantic-release/python-semantic-release/commit/d3241540e7640af911eb24c71e66468feebb0d46)) - -To control if bump major or not when current major version is zero. - - -## v7.5.0 (2020-12-04) - -### Features - -- **logs**: Include scope in changelogs - ([#281](https://github.com/python-semantic-release/python-semantic-release/pull/281), - [`21c96b6`](https://github.com/python-semantic-release/python-semantic-release/commit/21c96b688cc44cc6f45af962ffe6d1f759783f37)) - -When the scope is set, include it in changelogs, e.g. "feat(x): some description" becomes "**x**: - some description". This is similar to how the Node semantic release (and - conventional-changelog-generator) generates changelogs. If scope is not given, it's omitted. - -Add a new config parameter changelog_scope to disable this behavior when set to 'False' - - -## v7.4.1 (2020-12-04) - -### Bug Fixes - -- Add "changelog_capitalize" to flags - ([#279](https://github.com/python-semantic-release/python-semantic-release/pull/279), - [`37716df`](https://github.com/python-semantic-release/python-semantic-release/commit/37716dfa78eb3f848f57a5100d01d93f5aafc0bf)) - -Fixes #278 (or so I hope). - - -## v7.4.0 (2020-11-24) - -### Documentation - -- Fix broken internal references - ([#270](https://github.com/python-semantic-release/python-semantic-release/pull/270), - [`da20b9b`](https://github.com/python-semantic-release/python-semantic-release/commit/da20b9bdd3c7c87809c25ccb2a5993a7ea209a22)) - -- Update links to Github docs - ([#268](https://github.com/python-semantic-release/python-semantic-release/pull/268), - [`c53162e`](https://github.com/python-semantic-release/python-semantic-release/commit/c53162e366304082a3bd5d143b0401da6a16a263)) - -### Features - -- Add changelog_capitalize configuration - ([`7cacca1`](https://github.com/python-semantic-release/python-semantic-release/commit/7cacca1eb436a7166ba8faf643b53c42bc32a6a7)) - -Fixes #260 - - -## v7.3.0 (2020-09-28) - -### Documentation - -- Fix docstring - ([`5a5e2cf`](https://github.com/python-semantic-release/python-semantic-release/commit/5a5e2cfb5e6653fb2e95e6e23e56559953b2c2b4)) - -Stumbled upon this docstring which first line seems copy/pasted from the method above. - -### Features - -- Generate `changelog.md` file - ([#266](https://github.com/python-semantic-release/python-semantic-release/pull/266), - [`2587dfe`](https://github.com/python-semantic-release/python-semantic-release/commit/2587dfed71338ec6c816f58cdf0882382c533598)) - - -## v7.2.5 (2020-09-16) - -### Bug Fixes - -- Add required to inputs in action metadata - ([#264](https://github.com/python-semantic-release/python-semantic-release/pull/264), - [`e76b255`](https://github.com/python-semantic-release/python-semantic-release/commit/e76b255cf7d3d156e3314fc28c54d63fa126e973)) - -According to the documentation, `inputs..required` is a required field. - - -## v7.2.4 (2020-09-14) - -### Bug Fixes - -- Use range for toml dependency - ([`45707e1`](https://github.com/python-semantic-release/python-semantic-release/commit/45707e1b7dcab48103a33de9d7f9fdb5a34dae4a)) - -Fixes #241 - - -## v7.2.3 (2020-09-12) - -### Bug Fixes - -- Support multiline version_pattern matching by default - ([`82f7849`](https://github.com/python-semantic-release/python-semantic-release/commit/82f7849dcf29ba658e0cb3b5d21369af8bf3c16f)) - -### Documentation - -- Create 'getting started' instructions - ([#256](https://github.com/python-semantic-release/python-semantic-release/pull/256), - [`5f4d000`](https://github.com/python-semantic-release/python-semantic-release/commit/5f4d000c3f153d1d23128acf577e389ae879466e)) - -- Link to getting started guide in README - ([`f490e01`](https://github.com/python-semantic-release/python-semantic-release/commit/f490e0194fa818db4d38c185bc5e6245bfde546b)) - - -## v7.2.2 (2020-07-26) - -### Bug Fixes - -- **changelog**: Send changelog to stdout - ([`87e2bb8`](https://github.com/python-semantic-release/python-semantic-release/commit/87e2bb881387ff3ac245ab9923347a5a616e197b)) - -Fixes #250 - -### Documentation - -- Add quotation marks to the pip commands in CONTRIBUTING.rst - ([#253](https://github.com/python-semantic-release/python-semantic-release/pull/253), - [`e20fa43`](https://github.com/python-semantic-release/python-semantic-release/commit/e20fa43098c06f5f585c81b9cd7e287dcce3fb5d)) - - -## v7.2.1 (2020-06-29) - -### Bug Fixes - -- Commit all files with bumped versions - ([#249](https://github.com/python-semantic-release/python-semantic-release/pull/249), - [`b3a1766`](https://github.com/python-semantic-release/python-semantic-release/commit/b3a1766be7edb7d2eb76f2726d35ab8298688b3b)) - -### Documentation - -- Give example of multiple build commands - ([#248](https://github.com/python-semantic-release/python-semantic-release/pull/248), - [`65f1ffc`](https://github.com/python-semantic-release/python-semantic-release/commit/65f1ffcc6cac3bf382f4b821ff2be59d04f9f867)) - -I had a little trouble figuring out how to use a non-setup.py build command, so I thought it would - be helpful to update the docs with an example of how to do this. - - -## v7.2.0 (2020-06-15) - -### Features - -- Bump versions in multiple files - ([#246](https://github.com/python-semantic-release/python-semantic-release/pull/246), - [`0ba2c47`](https://github.com/python-semantic-release/python-semantic-release/commit/0ba2c473c6e44cc326b3299b6ea3ddde833bdb37)) - -- Add the `version_pattern` setting, which allows version numbers to be identified using arbitrary - regular expressions. - Refactor the config system to allow non-string data types to be specified - in `pyproject.toml`. - Multiple files can now be specified by setting `version_variable` or - `version_pattern` to a list in `pyproject.toml`. - -Fixes #175 - - -## v7.1.1 (2020-05-28) - -### Bug Fixes - -- **changelog**: Swap sha and message in table changelog - ([`6741370`](https://github.com/python-semantic-release/python-semantic-release/commit/6741370ab09b1706ff6e19b9fbe57b4bddefc70d)) - - -## v7.1.0 (2020-05-24) - -### Features - -- **changelog**: Add changelog_table component - ([#242](https://github.com/python-semantic-release/python-semantic-release/pull/242), - [`fe6a7e7`](https://github.com/python-semantic-release/python-semantic-release/commit/fe6a7e7fa014ffb827a1430dbcc10d1fc84c886b)) - -Add an alternative changelog component which displays each section as a row in a table. - -Fixes #237 - - -## v7.0.0 (2020-05-22) - -### Documentation - -- Add conda-forge badge - ([`e9536bb`](https://github.com/python-semantic-release/python-semantic-release/commit/e9536bbe119c9e3b90c61130c02468e0e1f14141)) - -### Features - -- **changelog**: Add changelog components - ([#240](https://github.com/python-semantic-release/python-semantic-release/pull/240), - [`3e17a98`](https://github.com/python-semantic-release/python-semantic-release/commit/3e17a98d7fa8468868a87e62651ac2c010067711)) - -* feat(changelog): add changelog components - -Add the ability to configure sections of the changelog using a `changelog_components` option. - Component outputs are separated by a blank line and appear in the same order as they were - configured. - -It is possible to create your own custom components. Each component is a function which returns - either some text to be added, or None in which case it will be skipped. - -BREAKING CHANGE: The `compare_url` option has been removed in favor of using `changelog_components`. - This functionality is now available as the `semantic_release.changelog.compare_url` component. - -* docs: add documentation for changelog_components - -* feat: pass changelog_sections to components - -Changelog components may now receive the value of `changelog_sections`, split and ready to use. - -### BREAKING CHANGES - -- **changelog**: The `compare_url` option has been removed in favor of using `changelog_components`. - This functionality is now available as the `semantic_release.changelog.compare_url` component. - - -## v6.4.1 (2020-05-15) - -### Bug Fixes - -- Convert \r\n to \n in commit messages - ([`34acbbc`](https://github.com/python-semantic-release/python-semantic-release/commit/34acbbcd25320a9d18dcd1a4f43e1ce1837b2c9f)) - -Fixes #239 - - -## v6.4.0 (2020-05-15) - -### Features - -- **history**: Create emoji parser - ([#238](https://github.com/python-semantic-release/python-semantic-release/pull/238), - [`2e1c50a`](https://github.com/python-semantic-release/python-semantic-release/commit/2e1c50a865628b372f48945a039a3edb38a7cdf0)) - -Add a commit parser which uses emojis from https://gitmoji.carloscuesta.me/ to determine the type of - change. - -* fix: add emojis to default changelog_sections - -* fix: include all parsed types in changelog - -This allows emojis to appear in the changelog, as well as configuring other types to appear with the - Angular parser (I remember someone asking for that feature a while ago). All filtering is now done - in the markdown_changelog function. - -* refactor(history): get breaking changes in parser - -Move the task of detecting breaking change descriptions into the commit parser function, instead of - during changelog generation. - -This has allowed the emoji parser to also return the regular descriptions as breaking change - descriptions for commits with :boom:. - -BREAKING CHANGE: Custom commit parser functions are now required to pass a fifth argument to - `ParsedCommit`, which is a list of breaking change descriptions. - -* docs: add documentation for emoji parser - -### BREAKING CHANGES - -- **history**: Custom commit parser functions are now required to pass a fifth argument to - `ParsedCommit`, which is a list of breaking change descriptions. - - -## v6.3.1 (2020-05-11) - -### Bug Fixes - -- Use getboolean for commit_version_number - ([`a60e0b4`](https://github.com/python-semantic-release/python-semantic-release/commit/a60e0b4e3cadf310c3e0ad67ebeb4e69d0ee50cb)) - -Fixes #186 - - -## v6.3.0 (2020-05-09) - -### Documentation - -- Document compare_link option - ([`e52c355`](https://github.com/python-semantic-release/python-semantic-release/commit/e52c355c0d742ddd2cfa65d42888296942e5bec5)) - -- Rewrite commit-log-parsing.rst - ([`4c70f4f`](https://github.com/python-semantic-release/python-semantic-release/commit/4c70f4f2aa3343c966d1b7ab8566fcc782242ab9)) - -### Features - -- **history**: Support linking compare page in changelog - ([`79a8e02`](https://github.com/python-semantic-release/python-semantic-release/commit/79a8e02df82fbc2acecaad9e9ff7368e61df3e54)) - -Fixes #218 - - -## v6.2.0 (2020-05-02) - -### Documentation - -- Add = to verbosity option - ([`a0f4c9c`](https://github.com/python-semantic-release/python-semantic-release/commit/a0f4c9cd397fcb98f880097319c08160adb3c3e6)) - -Fixes #227 - -- Use references where possible - ([`f38e5d4`](https://github.com/python-semantic-release/python-semantic-release/commit/f38e5d4a1597cddb69ce47a4d79b8774e796bf41)) - -Fixes #221 - -### Features - -- **history**: Check all paragraphs for breaking changes - ([`fec08f0`](https://github.com/python-semantic-release/python-semantic-release/commit/fec08f0dbd7ae15f95ca9c41a02c9fe6d448ede0)) - -Check each paragraph of the commit's description for breaking changes, instead of only a body and - footer. This ensures that breaking changes are detected when squashing commits together. - -Fixes #200 - - -## v6.1.0 (2020-04-26) - -### Documentation - -- Add documentation for PYPI_TOKEN - ([`a8263a0`](https://github.com/python-semantic-release/python-semantic-release/commit/a8263a066177d1d42f2844e4cb42a76a23588500)) - -### Features - -- **actions**: Support PYPI_TOKEN on GitHub Actions - ([`df2c080`](https://github.com/python-semantic-release/python-semantic-release/commit/df2c0806f0a92186e914cfc8cc992171d74422df)) - -Add support for the new PYPI_TOKEN environment variable to be used on GitHub Actions. - -- **pypi**: Support easier use of API tokens - ([`bac135c`](https://github.com/python-semantic-release/python-semantic-release/commit/bac135c0ae7a6053ecfc7cdf2942c3c89640debf)) - -Allow setting the environment variable PYPI_TOKEN to automatically fill the username as __token__. - -Fixes #213 - - -## v6.0.1 (2020-04-15) - -### Bug Fixes - -- **hvcs**: Convert get_hvcs to use LoggedFunction - ([`3084249`](https://github.com/python-semantic-release/python-semantic-release/commit/308424933fd3375ca3730d9eaf8abbad2435830b)) - -This was missed in 213530fb0c914e274b81d1dacf38ea7322b5b91f - - -## v6.0.0 (2020-04-15) - -### Documentation - -- Create Read the Docs config file - ([`aa5a1b7`](https://github.com/python-semantic-release/python-semantic-release/commit/aa5a1b700a1c461c81c6434686cb6f0504c4bece)) - -- Include README.rst in index.rst - ([`8673a9d`](https://github.com/python-semantic-release/python-semantic-release/commit/8673a9d92a9bf348bb3409e002a830741396c8ca)) - -These files were very similar so it makes sense to simply include one inside the other. - -- Move action.rst into main documentation - ([`509ccaf`](https://github.com/python-semantic-release/python-semantic-release/commit/509ccaf307a0998eced69ad9fee1807132babe28)) - -- Rewrite README.rst - ([`e049772`](https://github.com/python-semantic-release/python-semantic-release/commit/e049772cf14cdd49538cf357db467f0bf3fe9587)) - -- Rewrite troubleshooting page - ([`0285de2`](https://github.com/python-semantic-release/python-semantic-release/commit/0285de215a8dac3fcc9a51f555fa45d476a56dff)) - -### Refactoring - -- **debug**: Use logging and click_log instead of ndebug - ([`15b1f65`](https://github.com/python-semantic-release/python-semantic-release/commit/15b1f650f29761e1ab2a91b767cbff79b2057a4c)) - -BREAKING CHANGE: `DEBUG="*"` no longer has an effect, instead use `--verbosity DEBUG`. - -### BREAKING CHANGES - -- **debug**: `debug="*"` no longer has an effect, instead use `--verbosity DEBUG`. - - -## v5.2.0 (2020-04-09) - -### Documentation - -- Automate API docs - ([`7d4fea2`](https://github.com/python-semantic-release/python-semantic-release/commit/7d4fea266cc75007de51609131eb6d1e324da608)) - -Automatically create pages in the API docs section using sphinx-autodoc. This is added as an event - handler in conf.py. - -### Features - -- **github**: Add tag as default release name - ([`2997908`](https://github.com/python-semantic-release/python-semantic-release/commit/2997908f80f4fcec56917d237a079b961a06f990)) - - -## v5.1.0 (2020-04-04) - -### Documentation - -- Improve formatting of configuration page - ([`9a8e22e`](https://github.com/python-semantic-release/python-semantic-release/commit/9a8e22e838d7dbf3bfd941397c3b39560aca6451)) - -- Improve formatting of envvars page - ([`b376a56`](https://github.com/python-semantic-release/python-semantic-release/commit/b376a567bfd407a507ce0752614b0ca75a0f2973)) - -- Update index.rst - ([`b27c26c`](https://github.com/python-semantic-release/python-semantic-release/commit/b27c26c66e7e41843ab29076f7e724908091b46e)) - -### Features - -- **history**: Allow customizing changelog_sections - ([#207](https://github.com/python-semantic-release/python-semantic-release/pull/207), - [`d5803d5`](https://github.com/python-semantic-release/python-semantic-release/commit/d5803d5c1668d86482a31ac0853bac7ecfdc63bc)) - - -## v5.0.3 (2020-03-26) - -### Bug Fixes - -- Bump dependencies and fix Windows issues on Development - ([#173](https://github.com/python-semantic-release/python-semantic-release/pull/173), - [`0a6f8c3`](https://github.com/python-semantic-release/python-semantic-release/commit/0a6f8c3842b05f5f424dad5ce1fa5e3823c7e688)) - -* Bump dependencies and fix windows issues - -* Correctly pass temp dir to test settings - -* Remove print call on test settings - -* chore: remove py34 and py35 classifiers - -* chore: bump twine, requests and python-gitlab - -* chore: update tox config to be more granular - -* fix: missing mime types on Windows - -* chore: bump circleCI and tox python to 3.8 - -* chore: remove py36 from tox envlist - -* chore: isort errors - - -## v5.0.2 (2020-03-22) - -### Bug Fixes - -- **history**: Leave case of other characters unchanged - ([`96ba94c`](https://github.com/python-semantic-release/python-semantic-release/commit/96ba94c4b4593997343ec61ecb6c823c1494d0e2)) - -Previously, use of str.capitalize() would capitalize the first letter as expected, but all - subsequent letters became lowercase. Now, the other letters remain unchanged. - - -## v5.0.1 (2020-03-22) - -### Bug Fixes - -- Make action use current version of semantic-release - ([`123984d`](https://github.com/python-semantic-release/python-semantic-release/commit/123984d735181c622f3d99088a1ad91321192a11)) - -This gives two benefits: * In this repo it will work as a smoketest * In other repos when they - specify version int the github workflow they will get the version they specify. - - -## v5.0.0 (2020-03-22) - -### Bug Fixes - -- Rename default of build_command config - ([`d5db22f`](https://github.com/python-semantic-release/python-semantic-release/commit/d5db22f9f7acd05d20fd60a8b4b5a35d4bbfabb8)) - -### Documentation - -- **pypi**: Update docstings in pypi.py - ([`6502d44`](https://github.com/python-semantic-release/python-semantic-release/commit/6502d448fa65e5dc100e32595e83fff6f62a881a)) - -### Features - -- **build**: Allow config setting for build command - ([#195](https://github.com/python-semantic-release/python-semantic-release/pull/195), - [`740f4bd`](https://github.com/python-semantic-release/python-semantic-release/commit/740f4bdb26569362acfc80f7e862fc2c750a46dd)) - -* feat(build): allow config setting for build command - -BREAKING CHANGE: Previously the build_commands configuration variable set the types of bundles sent - to `python setup.py`. It has been replaced by the configuration variable `build_command` which - takes the full command e.g. `python setup.py sdist` or `poetry build`. - -Closes #188 - -### BREAKING CHANGES - -- **build**: Previously the build_commands configuration variable set the types of bundles sent to - `python setup.py`. It has been replaced by the configuration variable `build_command` which takes - the full command e.g. `python setup.py sdist` or `poetry build`. - - -## v4.11.0 (2020-03-22) - -### Documentation - -- Make AUTHORS.rst dynamic - ([`db2e076`](https://github.com/python-semantic-release/python-semantic-release/commit/db2e0762f3189d0f1a6ba29aad32bdefb7e0187f)) - -- **readme**: Fix minor typo - ([`c22f69f`](https://github.com/python-semantic-release/python-semantic-release/commit/c22f69f62a215ff65e1ab6dcaa8e7e9662692e64)) - -### Features - -- **actions**: Create GitHub Action - ([`350245d`](https://github.com/python-semantic-release/python-semantic-release/commit/350245dbfb07ed6a1db017b1d9d1072b368b1497)) - - -## v4.10.0 (2020-03-03) - -### Features - -- Make commit message configurable - ([#184](https://github.com/python-semantic-release/python-semantic-release/pull/184), - [`eb0762c`](https://github.com/python-semantic-release/python-semantic-release/commit/eb0762ca9fea5cecd5c7b182504912a629be473b)) - - -## v4.9.0 (2020-03-02) - -### Bug Fixes - -- **pypi**: Change bdist_wheels to bdist_wheel - ([`c4db509`](https://github.com/python-semantic-release/python-semantic-release/commit/c4db50926c03f3d551c8331932c567c7bdaf4f3d)) - -Change the incorrect command bdist_wheels to bdist_wheel. - -### Features - -- **pypi**: Add build_commands config - ([`22146ea`](https://github.com/python-semantic-release/python-semantic-release/commit/22146ea4b94466a90d60b94db4cc65f46da19197)) - -Add a config option to set the commands passed to setup.py when building distributions. This allows - for things like adding custom commands to the build process. - - -## v4.8.0 (2020-02-28) - -### Features - -- **git**: Add a new config for commit author - ([`aa2c22c`](https://github.com/python-semantic-release/python-semantic-release/commit/aa2c22c469448fe57f02bea67a02f998ce519ac3)) - - -## v4.7.1 (2020-02-28) - -### Bug Fixes - -- Repair parsing of remotes in the gitlab ci format - ([`0fddbe2`](https://github.com/python-semantic-release/python-semantic-release/commit/0fddbe2fb70d24c09ceddb789a159162a45942dc)) - -Format is: "https://gitlab-ci-token:MySuperToken@gitlab.example.com/group/project.git" - -Problem was due to the regex modification for #179 - -Fixes #181 - - -## v4.7.0 (2020-02-28) - -### Bug Fixes - -- Support repository owner names containing dots - ([`a6c4da4`](https://github.com/python-semantic-release/python-semantic-release/commit/a6c4da4c0e6bd8a37f64544f7813fa027f5054ed)) - -Fixes #179 - -- **github**: Use application/octet-stream for .whl files - ([`90a7e47`](https://github.com/python-semantic-release/python-semantic-release/commit/90a7e476a04d26babc88002e9035cad2ed485b07)) - -application/octet-stream is more generic, but it is better than using a non-official MIME type. - -### Features - -- Upload distribution files to GitHub Releases - ([#177](https://github.com/python-semantic-release/python-semantic-release/pull/177), - [`e427658`](https://github.com/python-semantic-release/python-semantic-release/commit/e427658e33abf518191498c3142a0f18d3150e07)) - -* refactor(github): create upload_asset function - -Create a function to call the asset upload API. This will soon be used to upload assets specified by - the user. - -* refactor(github): infer Content-Type from file extension - -Infer the Content-Type header based on the file extension instead of setting it manually. - -* refactor(pypi): move building of dists to cli.py - -Refactor to have the building/removal of distributions in cli.py instead of within the - upload_to_pypi function. This makes way for uploading to other locations, such as GitHub Releases, - too. - -* feat(github): upload dists to release - -Upload Python wheels to the GitHub release. Configured with the option upload_to_release, on by - default if using GitHub. - -* docs: document upload_to_release config option - -* test(github): add tests for Github.upload_dists - -* fix(github): fix upload of .whl files - -Fix uploading of .whl files due to a missing MIME type (defined custom type as - application/x-wheel+zip). Additionally, continue with other uploads even if one fails. - -* refactor(cli): additional output during publish - -Add some additional output during the publish command. - -* refactor(github): move api calls to separate methods - -Move each type of GitHub API request into its own method to improve readability. - -Re-implementation of #172 - -* fix: post changelog after PyPI upload - -Post the changelog in-between uploading to PyPI and uploading to GitHub Releases. This is so that if - the PyPI upload fails, GitHub users will not be notified. GitHub uploads still need to be - processed after creating the changelog as the release notes must be published to upload assets to - them. - - -## v4.6.0 (2020-02-19) - -### Bug Fixes - -- Add more debug statements in logs - ([`bc931ec`](https://github.com/python-semantic-release/python-semantic-release/commit/bc931ec46795fde4c1ccee004eec83bf73d5de7a)) - -- Only overwrite with patch if bump is None - ([`1daa4e2`](https://github.com/python-semantic-release/python-semantic-release/commit/1daa4e23ec2dd40c6b490849276524264787e24e)) - -Fixes #159 - -### Features - -- **history**: Capitalize changelog messages - ([`1a8e306`](https://github.com/python-semantic-release/python-semantic-release/commit/1a8e3060b8f6d6362c27903dcfc69d17db5f1d36)) - -Capitalize the first letter of messages in the changelog regardless of whether they are capitalized - in the commit itself. - - -## v4.5.1 (2020-02-16) - -### Bug Fixes - -- **github**: Send token in request header - ([`be9972a`](https://github.com/python-semantic-release/python-semantic-release/commit/be9972a7b1fb183f738fb31bd370adb30281e4d5)) - -Use an Authorization header instead of deprecated query parameter authorization. - -Fixes relekang/python-semantic-release#167 - -### Documentation - -- Add note about automatic releases in readme - ([`e606e75`](https://github.com/python-semantic-release/python-semantic-release/commit/e606e7583a30167cf7679c6bcada2f9e768b3abe)) - -- Fix broken list in readme - ([`7aa572b`](https://github.com/python-semantic-release/python-semantic-release/commit/7aa572b2a323ddbc69686309226395f40c52b469)) - -Fix the syntax of a broken bullet-point list in README.rst. - -- Update readme and getting started docs - ([`07b3208`](https://github.com/python-semantic-release/python-semantic-release/commit/07b3208ff64301e544c4fdcb48314e49078fc479)) - - -## v4.5.0 (2020-02-08) - -### Bug Fixes - -- Remove erroneous submodule - ([`762bfda`](https://github.com/python-semantic-release/python-semantic-release/commit/762bfda728c266b8cd14671d8da9298fc99c63fb)) - -- **cli**: --noop flag works when before command - ([`4fcc781`](https://github.com/python-semantic-release/python-semantic-release/commit/4fcc781d1a3f9235db552f0f4431c9f5e638d298)) - -The entry point of the app is changed from main() to entry(). Entry takes any arguments before - commands and moves them to after commands, then calls main() - -Closes #73 - -### Features - -- **history**: Enable colon defined version - ([`7837f50`](https://github.com/python-semantic-release/python-semantic-release/commit/7837f5036269328ef29996b9ea63cccd5a6bc2d5)) - -The get_current_version_by_config_file and the replace_version_string methods now check for both - variables defined as "variable= version" and "variable: version" This allows for using a yaml file - to store the version. - -Closes #165 - - -## v4.4.1 (2020-01-18) - -### Bug Fixes - -- Add quotes around twine arguments - ([`46a83a9`](https://github.com/python-semantic-release/python-semantic-release/commit/46a83a94b17c09d8f686c3ae7b199d7fd0e0e5e5)) - -Fixes #163 - - -## v4.4.0 (2020-01-17) - -### Bug Fixes - -- **github**: Add check for GITHUB_ACTOR for git push - ([#162](https://github.com/python-semantic-release/python-semantic-release/pull/162), - [`c41e9bb`](https://github.com/python-semantic-release/python-semantic-release/commit/c41e9bb986d01b92d58419cbdc88489d630a11f1)) - -### Features - -- **parser**: Add support for exclamation point for breaking changes - ([`a4f8a10`](https://github.com/python-semantic-release/python-semantic-release/commit/a4f8a10afcc358a8fbef83be2041129480350be2)) - -According to the documentation for conventional commits, breaking changes can be described using - exclamation points, just before the colon between type/scope and subject. In that case, the - breaking change footer is optional, and the subject is used as description of the breaking change. - If the footer exists, it is used for the description. - -Fixes #156 - -- **parser**: Make BREAKING-CHANGE synonymous with BREAKING CHANGE - ([`beedccf`](https://github.com/python-semantic-release/python-semantic-release/commit/beedccfddfb360aeebef595342ee980446012ec7)) - -According to point 16 in the conventional commit specification, this should be implemented. They - especially mention the footer, but I kept the body for backwards compatibility. This should - probably be removed one day. The regex is in the helpers to make it easier to re-use, but I didn't - updated parser_tag since it looks like a legacy parser. - - -## v4.3.4 (2019-12-17) - -### Bug Fixes - -- Fallback to whole log if correct tag is not available - ([#157](https://github.com/python-semantic-release/python-semantic-release/pull/157), - [`252bffd`](https://github.com/python-semantic-release/python-semantic-release/commit/252bffd3be7b6dfcfdb384d24cb1cd83d990fc9a)) - -The method getting all commits to consider for the release will now test whether the version in - input is a valid reference. If it is not, it will consider the whole log for the repository. - -evaluate_version_bump will still consider a message starting with the version number as a breaking - condition to stop analyzing. - -Fixes #51 - - -## v4.3.3 (2019-11-06) - -### Bug Fixes - -- Set version of click to >=2.0,<8.0. - ([#155](https://github.com/python-semantic-release/python-semantic-release/pull/155), - [`f07c7f6`](https://github.com/python-semantic-release/python-semantic-release/commit/f07c7f653be1c018e443f071d9a196d9293e9521)) - -* fix: Upgrade to click 7.0. - -Fixes #117 - -* fix: Instead of requiring click 7.0, looks like all tests will pass with at least 2.0. - -* Upstream is at ~=7.0, so let's set the range to less than 8.0. - -* The string template has no variables, so remove the call to .format() - - -## v4.3.2 (2019-10-05) - -### Bug Fixes - -- Update regex to get repository owner and name for project with dots - ([`2778e31`](https://github.com/python-semantic-release/python-semantic-release/commit/2778e316a0c0aa931b1012cb3862d04659c05e73)) - -Remove the dot from the second capture group to allow project names containing dots to be matched. - Instead of a greedy '+' operator, use '*?' to allow the second group to give back the '.git' (to - avoid including it in the project name) - -Fixes #151 - - -## v4.3.1 (2019-09-29) - -### Bug Fixes - -- Support repo urls without git terminator - ([`700e9f1`](https://github.com/python-semantic-release/python-semantic-release/commit/700e9f18dafde1833f482272a72bb80b54d56bb3)) - - -## v4.3.0 (2019-09-06) - -### Bug Fixes - -- Manage subgroups in git remote url - ([`4b11875`](https://github.com/python-semantic-release/python-semantic-release/commit/4b118754729094e330389712cf863e1c6cefee69)) - -This is a necessary fix for gitlab integration. For an illustration of the need and use for this - fix, test was edited. - -Fixes #139 Fixes #140 - -- Update list of commit types to include build, ci and perf - ([`41ea12f`](https://github.com/python-semantic-release/python-semantic-release/commit/41ea12fa91f97c0046178806bce3be57c3bc2308)) - -Also added perf to the types that trigger a patch update - -Fixes #145 - -### Features - -- Add the possibility to load configuration from pyproject.toml - ([`35f8bfe`](https://github.com/python-semantic-release/python-semantic-release/commit/35f8bfef443c8b69560c918f4b13bc766fb3daa2)) - -Adds the toml library to base requirements. Also adds the related tests and documentation. Also adds - the description of the version_source configuration option - -Relates to #119 - -- Allow the override of configuration options from cli - ([`f0ac82f`](https://github.com/python-semantic-release/python-semantic-release/commit/f0ac82fe59eb59a768a73a1bf2ea934b9d448c58)) - -config can now be overriden with the "-D" flag. Also adds the related tests and documentation. - -Also introduces a fixture in tests/__init__.py that reloads module using config. It is necessary - since all tests run in the same environment. A better way would be to box the execution of tests - (using the --forked option of pytest for example) but it does not work in non-unix systems. Also - some tests should not break if config is changed, but it is outside of the scope of this issue. - -Fixes #119 - -- Allow users to get version from tag and write/commit bump to file - ([`1f9fe1c`](https://github.com/python-semantic-release/python-semantic-release/commit/1f9fe1cc7666d47cc0c348c4705b63c39bf10ecc)) - -Before this commit, version was bumped in the file, but only committed if version was obtained from - `version_variable` (version_source == `commit`). Also added a relevant test and a description for - this new option. - -Fixes #104 - -- Make the vcs functionalities work with gitlab - ([`82d555d`](https://github.com/python-semantic-release/python-semantic-release/commit/82d555d45b9d9e295ef3f9546a6ca2a38ca4522e)) - -Adds python-gitlab as requirement. Refactored github specific methods while keeping default - behavior. Also removed an unused return value for post_release_changelog. Also refactored the - secret filtering method. Updated the related tests. - -Fixes #121 - - -## v4.2.0 (2019-08-05) - -### Bug Fixes - -- Add commit hash when generating breaking changes - ([`0c74faf`](https://github.com/python-semantic-release/python-semantic-release/commit/0c74fafdfa81cf2e13db8f4dcf0a6f7347552504)) - -Fixes #120 - -- Kept setting new version for tag source - ([`0e24a56`](https://github.com/python-semantic-release/python-semantic-release/commit/0e24a5633f8f94b48da97b011634d4f9d84f7b4b)) - -- Remove deletion of build folder - ([`b45703d`](https://github.com/python-semantic-release/python-semantic-release/commit/b45703dad38c29b28575060b21e5fb0f8482c6b1)) - -Fixes #115 - -- Updated the tag tests - ([`3303eef`](https://github.com/python-semantic-release/python-semantic-release/commit/3303eefa49a0474bbd85df10ae186ccbf9090ec1)) - -- Upgrade click to 7.0 - ([`2c5dd80`](https://github.com/python-semantic-release/python-semantic-release/commit/2c5dd809b84c2157a5e6cdcc773c43ec864f0328)) - -### Features - -- Add configuration to customize handling of dists - ([`2af6f41`](https://github.com/python-semantic-release/python-semantic-release/commit/2af6f41b21205bdd192514a434fca2feba17725a)) - -Relates to #115 - -- Add support for configuring branch - ([`14abb05`](https://github.com/python-semantic-release/python-semantic-release/commit/14abb05e7f878e88002f896812d66b4ea5c219d4)) - -Fixes #43 - -- Add support for showing unreleased changelog - ([`41ef794`](https://github.com/python-semantic-release/python-semantic-release/commit/41ef7947ad8a07392c96c7540980476e989c1d83)) - -Fixes #134 - - -## v4.1.2 (2019-08-04) - -### Bug Fixes - -- Correct isort build fail - ([`0037210`](https://github.com/python-semantic-release/python-semantic-release/commit/00372100b527ff9308d9e43fe5c65cdf179dc4dc)) - -build fail: https://circleci.com/gh/relekang/python-semantic-release/379 - -- Make sure the history only breaks loop for version commit - ([`5dc6cfc`](https://github.com/python-semantic-release/python-semantic-release/commit/5dc6cfc634254f09997bb3cb0f17abd296e2c01f)) - -Fixes #135 - -- **vcs**: Allow cli to be run from subdirectory - ([`fb7bb14`](https://github.com/python-semantic-release/python-semantic-release/commit/fb7bb14300e483626464795b8ff4f033a194cf6f)) - -### Documentation - -- **circleci**: Point badge to master branch - ([`9c7302e`](https://github.com/python-semantic-release/python-semantic-release/commit/9c7302e184a1bd88f39b3039691b55cd77f0bb07)) - - -## v4.1.1 (2019-02-15) - -### Documentation - -- Correct usage of changelog - ([`f4f59b0`](https://github.com/python-semantic-release/python-semantic-release/commit/f4f59b08c73700c6ee04930221bfcb1355cbc48d)) - -- Debug usage and related - ([`f08e594`](https://github.com/python-semantic-release/python-semantic-release/commit/f08e5943a9876f2d17a7c02f468720995c7d9ffd)) - -Debug functionality lack documentation. Thoubleshooting is helped by documenting other environment - variables as well. - -- Describing the commands - ([`b6fa04d`](https://github.com/python-semantic-release/python-semantic-release/commit/b6fa04db3044525a1ee1b5952fb175a706842238)) - -The commands is lacking from the documentation. - -- Update url for commit guidelinesThe guidelines can now be found in theDEVELOPERS.md in angular. - ([`90c1b21`](https://github.com/python-semantic-release/python-semantic-release/commit/90c1b217f86263301b91d19d641c7b348e37d960)) - - -## v4.1.0 (2019-01-31) - -### Bug Fixes - -- Initialize git Repo from current folder - ([`c7415e6`](https://github.com/python-semantic-release/python-semantic-release/commit/c7415e634c0affbe6396e0aa2bafe7c1b3368914)) - -This allows to run the program also from inner repository folders - -- Maintain version variable formatting on bump - ([#103](https://github.com/python-semantic-release/python-semantic-release/pull/103), - [`bf63156`](https://github.com/python-semantic-release/python-semantic-release/commit/bf63156f60340614fae94c255fb2f097cf317b2b)) - -Small change to the way the version is written to the config file it is read from. This allows the - formatting to be the same as before semantic-release changed it. - -Prior behavior `my_version_var="1.2.3"` => `my_version_var = '1.2.4'` - -New behavior `my_version_var="1.2.3"` => `my_version_var="1.2.4"` - -I am using python-semantic-release with a Julia project and this change will allow for consistent - formatting in my Project.toml file where the version is maintained - -- Use same changelog code for command as post - ([`248f622`](https://github.com/python-semantic-release/python-semantic-release/commit/248f62283c59182868c43ff105a66d85c923a894)) - -See #27 for background. - -### Documentation - -- Add installation instructions for development - ([#106](https://github.com/python-semantic-release/python-semantic-release/pull/106), - [`9168d0e`](https://github.com/python-semantic-release/python-semantic-release/commit/9168d0ea56734319a5d77e890f23ff6ba51cc97d)) - -- **readme**: Add testing instructions - ([`bb352f5`](https://github.com/python-semantic-release/python-semantic-release/commit/bb352f5b6616cc42c9f2f2487c51dedda1c68295)) - -### Features - -- **ci_checks**: Add support for bitbucket - ([`9fc120d`](https://github.com/python-semantic-release/python-semantic-release/commit/9fc120d1a7e4acbbca609628e72651685108b364)) - - -## v4.0.1 (2019-01-12) - -### Bug Fixes - -- Add better error message when pypi credentials are empty - ([`c4e5dcb`](https://github.com/python-semantic-release/python-semantic-release/commit/c4e5dcbeda0ce8f87d25faefb4d9ae3581029a8f)) - -Fixes #96 - -- Clean out dist and build before building - ([`b628e46`](https://github.com/python-semantic-release/python-semantic-release/commit/b628e466f86bc27cbe45ec27a02d4774a0efd3bb)) - -This should fix the problem with uploading old versions. - -Fixes #86 - -- Filter out pypi secrets from exceptions - ([`5918371`](https://github.com/python-semantic-release/python-semantic-release/commit/5918371c1e82b06606087c9945d8eaf2604a0578)) - -Fixes #41 - -- Unfreeze dependencies - ([`847833b`](https://github.com/python-semantic-release/python-semantic-release/commit/847833bf48352a4935f906d0c3f75e1db596ca1c)) - -This uses ~= for most dependencies instead of pinning them. - -Fixes #100 - -- Use correct syntax to exclude tests in package - ([`3e41e91`](https://github.com/python-semantic-release/python-semantic-release/commit/3e41e91c318663085cd28c8165ece21d7e383475)) - -This implements #92 without deleting __init__.py files. - -- **parser_angular**: Fix non-match when special chars in scope - ([`8a33123`](https://github.com/python-semantic-release/python-semantic-release/commit/8a331232621b26767e4268079f9295bf695047ab)) - -### Documentation - -- Remove reference to gitter - ([`896e37b`](https://github.com/python-semantic-release/python-semantic-release/commit/896e37b95cc43218e8f593325dd4ea63f8b895d9)) - -Fixes #90 - - -## v4.0.0 (2018-11-22) - -### Bug Fixes - -- Add check of credentials - ([`7d945d4`](https://github.com/python-semantic-release/python-semantic-release/commit/7d945d44b36b3e8c0b7771570cb2305e9e09d0b2)) - -- Add credentials check - ([`0694604`](https://github.com/python-semantic-release/python-semantic-release/commit/0694604f3b3d2159a4037620605ded09236cdef5)) - -- Add dists to twine call - ([`1cec2df`](https://github.com/python-semantic-release/python-semantic-release/commit/1cec2df8bcb7f877c813d6470d454244630b050a)) - -- Change requests from fixed version to version range - ([#93](https://github.com/python-semantic-release/python-semantic-release/pull/93), - [`af3ad59`](https://github.com/python-semantic-release/python-semantic-release/commit/af3ad59f018876e11cc3acdda0b149f8dd5606bd)) - -* Change requests version to be more flexible to aid in using this with dev requirements for a - release. - -* revert changes to vcs helpers - -- Re-add skip-existing - ([`366e9c1`](https://github.com/python-semantic-release/python-semantic-release/commit/366e9c1d0b9ffcde755407a1de18e8295f6ad3a1)) - -- Remove repository argument in twine - ([`e24543b`](https://github.com/python-semantic-release/python-semantic-release/commit/e24543b96adb208897f4ce3eaab96b2f4df13106)) - -- Remove support for python 2 - ([`85fe638`](https://github.com/python-semantic-release/python-semantic-release/commit/85fe6384c15db317bc7142f4c8bbf2da58cece58)) - -BREAKING CHANGE: This will only work with python 3 after this commit. - -- Remove universal from setup config - ([`18b2402`](https://github.com/python-semantic-release/python-semantic-release/commit/18b24025e397aace03dd5bb9eed46cfdd13491bd)) - -- Update twine - ([`c4ae7b8`](https://github.com/python-semantic-release/python-semantic-release/commit/c4ae7b8ecc682855a8568b247690eaebe62d2d26)) - -- Use new interface for twine - ([`c04872d`](https://github.com/python-semantic-release/python-semantic-release/commit/c04872d00a26e9bf0f48eeacb360b37ce0fba01e)) - -- Use twine through cli call - ([`ab84beb`](https://github.com/python-semantic-release/python-semantic-release/commit/ab84beb8f809e39ae35cd3ce5c15df698d8712fd)) - -### Documentation - -- Add type hints and more complete docstrings - ([`a6d5e9b`](https://github.com/python-semantic-release/python-semantic-release/commit/a6d5e9b1ccbe75d59e7240528593978a19d8d040)) - -Includes a few style changes suggested by pylint and type safety checks suggested by mypy - -re #81 - -- Fix typo in documentation index - ([`da6844b`](https://github.com/python-semantic-release/python-semantic-release/commit/da6844bce0070a0020bf13950bd136fe28262602)) - -The word role -- 'an actor's part in a play, movie, etc.' does not fit in this context. "ready to - roll" is a phrase meaning "fully prepared to start functioning or moving" or simply "ready". I - believe this is what was meant to be written. - -### Features - -- Add support for commit_message config variable - ([`4de5400`](https://github.com/python-semantic-release/python-semantic-release/commit/4de540011ab10483ee1865f99c623526cf961bb9)) - -This variable can allow you to skip CI pipelines in CI tools like GitLab CI by adding [CI skip] in - the body. There are likely many uses for this beyond that particular example... - -BREAKING CHANGE: If you rely on the commit message to be the version number only, this will break - your code - -re #88 #32 - -- **CI checks**: Add support for GitLab CI checks - ([`8df5e2b`](https://github.com/python-semantic-release/python-semantic-release/commit/8df5e2bdd33a620e683f3adabe174e94ceaa88d9)) - -Check `GITLAB_CI` environment variable and then verify `CI_COMMIT_REF_NAME` matches the given - branch. - -Includes tests - -Closes #88 re #32 - -### BREAKING CHANGES - -- If you rely on the commit message to be the version number only, this will break your code - - -## v3.11.2 (2018-06-10) - -### Bug Fixes - -- Upgrade twine - ([`9722313`](https://github.com/python-semantic-release/python-semantic-release/commit/9722313eb63c7e2c32c084ad31bed7ee1c48a928)) - - -## v3.11.1 (2018-06-06) - -### Bug Fixes - -- Change Gitpython version number - ([`23c9d4b`](https://github.com/python-semantic-release/python-semantic-release/commit/23c9d4b6a1716e65605ed985881452898d5cf644)) - -Change the Gitpython version number to fix a bug described in #80. - -### Documentation - -- Add retry option to cli docs - ([`021da50`](https://github.com/python-semantic-release/python-semantic-release/commit/021da5001934f3199c98d7cf29f62a3ad8c2e56a)) - - -## v3.11.0 (2018-04-12) - -### Bug Fixes - -- Add pytest cache to gitignore - ([`b8efd5a`](https://github.com/python-semantic-release/python-semantic-release/commit/b8efd5a6249c79c8378bffea3e245657e7094ec9)) - -- Make repo non if it is not a git repository - ([`1dc306b`](https://github.com/python-semantic-release/python-semantic-release/commit/1dc306b9b1db2ac360211bdc61fd815302d0014c)) - -Fixes #74 - -### Documentation - -- Remove old notes about trello board - ([`7f50c52`](https://github.com/python-semantic-release/python-semantic-release/commit/7f50c521a522bb0c4579332766248778350e205b)) - -- Update status badges - ([`cfa13b8`](https://github.com/python-semantic-release/python-semantic-release/commit/cfa13b8260e3f3b0bfcb395f828ad63c9c5e3ca5)) - -### Features - -- Add --retry cli option - ([#78](https://github.com/python-semantic-release/python-semantic-release/pull/78), - [`3e312c0`](https://github.com/python-semantic-release/python-semantic-release/commit/3e312c0ce79a78d25016a3b294b772983cfb5e0f)) - -* Add --retry cli option * Post changelog correctly * Add comments * Add --retry to the docs - -- Add support to finding previous version from tags if not using commit messages - ([#68](https://github.com/python-semantic-release/python-semantic-release/pull/68), - [`6786487`](https://github.com/python-semantic-release/python-semantic-release/commit/6786487ebf4ab481139ef9f43cd74e345debb334)) - -* feat: Be a bit more forgiving to find previous tags - -Now grabs the previous version from tag names if it can't find it in the commit - -* quantifiedcode and flake8 fixes - -* Update cli.py - -* Switch to ImproperConfigurationError - - -## v3.10.3 (2018-01-29) - -### Bug Fixes - -- Error when not in git repository - ([#75](https://github.com/python-semantic-release/python-semantic-release/pull/75), - [`251b190`](https://github.com/python-semantic-release/python-semantic-release/commit/251b190a2fd5df68892346926d447cbc1b32475a)) - -Fix an error when the program was run in a non-git repository. It would not allow the help options - to be run. - -issue #74 - - -## v3.10.2 (2017-08-03) - -### Bug Fixes - -- Update call to upload to work with twine 1.9.1 - ([#72](https://github.com/python-semantic-release/python-semantic-release/pull/72), - [`8f47643`](https://github.com/python-semantic-release/python-semantic-release/commit/8f47643c54996e06c358537115e7e17b77cb02ca)) - - -## v3.10.1 (2017-07-22) - -### Bug Fixes - -- Update Twine ([#69](https://github.com/python-semantic-release/python-semantic-release/pull/69), - [`9f268c3`](https://github.com/python-semantic-release/python-semantic-release/commit/9f268c373a932621771abbe9607b739b1e331409)) - -The publishing API is under development and older versions of Twine have problems to deal with newer - versions of the API. Namely the logic of register/upload has changed (it was simplified). - - -## v3.10.0 (2017-05-05) - -### Bug Fixes - -- Make changelog problems not fail whole publish - ([`b5a68cf`](https://github.com/python-semantic-release/python-semantic-release/commit/b5a68cf6177dc0ed80eda722605db064f3fe2062)) - -Can be fixed with changelog command later. - -### Documentation - -- Fix typo in cli.py docstring - ([#64](https://github.com/python-semantic-release/python-semantic-release/pull/64), - [`0d13985`](https://github.com/python-semantic-release/python-semantic-release/commit/0d139859cd71f2d483f4360f196d6ef7c8726c18)) - -### Features - -- Add git hash to the changelog - ([#65](https://github.com/python-semantic-release/python-semantic-release/pull/65), - [`628170e`](https://github.com/python-semantic-release/python-semantic-release/commit/628170ebc440fc6abf094dd3e393f40576dedf9b)) - -* feat(*): add git hash to the changelog - -Add git hash to the changelog to ease finding the specific commit. The hash now is also easily - viewable in Github's tag. see #63 for more information. - -* chore(test_history): fix test errors - -Fix the test errors that would happen after the modification of get_commit_log. - - -## v3.9.0 (2016-07-03) - -### Bug Fixes - -- Can't get the proper last tag from commit history - ([`5a0e681`](https://github.com/python-semantic-release/python-semantic-release/commit/5a0e681e256ec511cd6c6a8edfee9d905891da10)) - -repo.tags returns a list sorted by the name rather than date, fix it by sorting them before - iteration - -### Features - -- Add option for choosing between versioning by commit or tag - ([`c0cd1f5`](https://github.com/python-semantic-release/python-semantic-release/commit/c0cd1f5b2e0776d7b636c3dd9e5ae863125219e6)) - -default versioning behaviour is commiting - -- Don't use file to track version, only tag to commit for versioning - ([`cd25862`](https://github.com/python-semantic-release/python-semantic-release/commit/cd258623ee518c009ae921cd6bb3119dafae43dc)) - -- Get repo version from historical tags instead of config file - ([`a45a9bf`](https://github.com/python-semantic-release/python-semantic-release/commit/a45a9bfb64538efeb7f6f42bb6e7ede86a4ddfa8)) - -repo version will get from historical tags. init 0.0.0 if fail of find any version tag - - -## v3.8.1 (2016-04-17) - -### Bug Fixes - -- Add search_parent_directories option to gitpython - ([#62](https://github.com/python-semantic-release/python-semantic-release/pull/62), - [`8bf9ce1`](https://github.com/python-semantic-release/python-semantic-release/commit/8bf9ce11137399906f18bc8b25698b6e03a65034)) - - -## v3.8.0 (2016-03-21) - -### Bug Fixes - -- Add git fetch to frigg after success - ([`74a6cae`](https://github.com/python-semantic-release/python-semantic-release/commit/74a6cae2b46c5150e63136fde0599d98b9486e36)) - -- Make tag parser work correctly with breaking changes - ([`9496f6a`](https://github.com/python-semantic-release/python-semantic-release/commit/9496f6a502c79ec3acb4e222e190e76264db02cf)) - -The tag parser did not work correctly, this went undiscovered for a while because the tests was not - ran by pytest. - -- Refactoring cli.py to improve --help and error messages - ([`c79fc34`](https://github.com/python-semantic-release/python-semantic-release/commit/c79fc3469fb99bf4c7f52434fa9c0891bca757f9)) - -### Documentation - -- Add info about correct commit guidelines - ([`af35413`](https://github.com/python-semantic-release/python-semantic-release/commit/af35413fae80889e2c5fc6b7d28f77f34b3b4c02)) - -- Add info about trello board in readme - ([`5229557`](https://github.com/python-semantic-release/python-semantic-release/commit/5229557099d76b3404ea3677292332442a57ae2e)) - -- Fix badges in readme - ([`7f4e549`](https://github.com/python-semantic-release/python-semantic-release/commit/7f4e5493edb6b3fb3510d0bb78fcc8d23434837f)) - -- Update info about releases in contributing.md - ([`466f046`](https://github.com/python-semantic-release/python-semantic-release/commit/466f0460774cad86e7e828ffb50c7d1332b64e7b)) - -### Features - -- Add ci checks for circle ci - ([`151d849`](https://github.com/python-semantic-release/python-semantic-release/commit/151d84964266c8dca206cef8912391cb73c8f206)) - - -## v3.7.2 (2016-03-19) - -### Bug Fixes - -- Move code around a bit to make flake8 happy - ([`41463b4`](https://github.com/python-semantic-release/python-semantic-release/commit/41463b49b5d44fd94c11ab6e0a81e199510fabec)) - - -## v3.7.1 (2016-03-15) - -### Documentation - -- **configuration**: Fix typo in setup.cfg section - ([`725d87d`](https://github.com/python-semantic-release/python-semantic-release/commit/725d87dc45857ef2f9fb331222845ac83a3af135)) - - -## v3.7.0 (2016-01-10) - -### Features - -- Add ci_checks for Frigg CI - ([`577c374`](https://github.com/python-semantic-release/python-semantic-release/commit/577c374396fe303b6fe7d64630d2959998d3595c)) - - -## v3.6.1 (2016-01-10) - -### Bug Fixes - -- Add requests as dependency - ([`4525a70`](https://github.com/python-semantic-release/python-semantic-release/commit/4525a70d5520b44720d385b0307e46fae77a7463)) - - -## v3.6.0 (2015-12-28) - -### Documentation - -- Add documentation for configuring on CI - ([`7806940`](https://github.com/python-semantic-release/python-semantic-release/commit/7806940ae36cb0d6ac0f966e5d6d911bd09a7d11)) - -- Add note about node semantic release - ([`0d2866c`](https://github.com/python-semantic-release/python-semantic-release/commit/0d2866c528098ecaf1dd81492f28d3022a2a54e0)) - -- Add step by step guide for configuring travis ci - ([`6f23414`](https://github.com/python-semantic-release/python-semantic-release/commit/6f2341442f61f0284b1119a2c49e96f0be678929)) - -- Move automatic-releases to subfolder - ([`ed68e5b`](https://github.com/python-semantic-release/python-semantic-release/commit/ed68e5b8d3489463e244b078ecce8eab2cba2bb1)) - -- Remove duplicate readme - ([`42a9421`](https://github.com/python-semantic-release/python-semantic-release/commit/42a942131947cd1864c1ba29b184caf072408742)) - -It was created by pandoc earlier when the original readme was written in markdown. - -### Features - -- Add checks for semaphore - ([`2d7ef15`](https://github.com/python-semantic-release/python-semantic-release/commit/2d7ef157b1250459060e99601ec53a00942b6955)) - -Fixes #44 - - -## v3.5.0 (2015-12-22) - -### Bug Fixes - -- Remove " from git push command - ([`031318b`](https://github.com/python-semantic-release/python-semantic-release/commit/031318b3268bc37e6847ec049b37425650cebec8)) - -### Documentation - -- Convert readme to rst - ([`e8a8d26`](https://github.com/python-semantic-release/python-semantic-release/commit/e8a8d265aa2147824f18065b39a8e7821acb90ec)) - -### Features - -- Add author in commit - ([`020efaa`](https://github.com/python-semantic-release/python-semantic-release/commit/020efaaadf588e3fccd9d2f08a273c37e4158421)) - -Fixes #40 - -- Checkout master before publishing - ([`dc4077a`](https://github.com/python-semantic-release/python-semantic-release/commit/dc4077a2d07e0522b625336dcf83ee4e0e1640aa)) - -Related to #39 - - -## v3.4.0 (2015-12-22) - -### Features - -- Add travis environment checks - ([`f386db7`](https://github.com/python-semantic-release/python-semantic-release/commit/f386db75b77acd521d2f5bde2e1dde99924dc096)) - -These checks will ensure that semantic release only runs against master and not in a pull-request. - - -## v3.3.3 (2015-12-22) - -### Bug Fixes - -- Do git push and git push --tags instead of --follow-tags - ([`8bc70a1`](https://github.com/python-semantic-release/python-semantic-release/commit/8bc70a183fd72f595c72702382bc0b7c3abe99c8)) - - -## v3.3.2 (2015-12-21) - -### Bug Fixes - -- Change build badge - ([`0dc068f`](https://github.com/python-semantic-release/python-semantic-release/commit/0dc068fff2f8c6914f4abe6c4e5fb2752669159e)) - -### Documentation - -- Update docstrings for generate_changelog - ([`987c6a9`](https://github.com/python-semantic-release/python-semantic-release/commit/987c6a96d15997e38c93a9d841c618c76a385ce7)) - - -## v3.3.1 (2015-12-21) - -### Bug Fixes - -- Add pandoc to travis settings - ([`17d40a7`](https://github.com/python-semantic-release/python-semantic-release/commit/17d40a73062ffa774542d0abc0f59fc16b68be37)) - -- Only list commits from the last version tag - ([`191369e`](https://github.com/python-semantic-release/python-semantic-release/commit/191369ebd68526e5b1afcf563f7d13e18c8ca8bf)) - -Fixes #28 - - -## v3.3.0 (2015-12-20) - -### Bug Fixes - -- Add missing parameters to twine.upload - ([`4bae22b`](https://github.com/python-semantic-release/python-semantic-release/commit/4bae22bae9b9d9abf669b028ea3af4b3813a1df0)) - -- Better filtering of github token in push error - ([`9b31da4`](https://github.com/python-semantic-release/python-semantic-release/commit/9b31da4dc27edfb01f685e6036ddbd4c715c9f60)) - -- Downgrade twine to version 1.5.0 - ([`66df378`](https://github.com/python-semantic-release/python-semantic-release/commit/66df378330448a313aff7a7c27067adda018904f)) - -- Make sure the github token is not in the output - ([`55356b7`](https://github.com/python-semantic-release/python-semantic-release/commit/55356b718f74d94dd92e6c2db8a15423a6824eb5)) - -- Push to master by default - ([`a0bb023`](https://github.com/python-semantic-release/python-semantic-release/commit/a0bb023438a1503f9fdb690d976d71632f19a21f)) - -### Features - -- Add support for environment variables for pypi credentials - ([`3b383b9`](https://github.com/python-semantic-release/python-semantic-release/commit/3b383b92376a7530e89b11de481c4dfdfa273f7b)) - - -## v3.2.1 (2015-12-20) - -### Bug Fixes - -- Add requirements to manifest - ([`ed25ecb`](https://github.com/python-semantic-release/python-semantic-release/commit/ed25ecbaeec0e20ad3040452a5547bb7d6faf6ad)) - -- **pypi**: Add sdist as default in addition to bdist_wheel - ([`a1a35f4`](https://github.com/python-semantic-release/python-semantic-release/commit/a1a35f43175187091f028474db2ebef5bfc77bc0)) - -There are a lot of outdated pip installations around which leads to confusions if a package have had - an sdist release at some point and then suddenly is only available as wheel packages, because old - pip clients will then download the latest sdist package available. - - -## v3.2.0 (2015-12-20) - -### Bug Fixes - -- **deps**: Use one file for requirements - ([`4868543`](https://github.com/python-semantic-release/python-semantic-release/commit/486854393b24803bb2356324e045ccab17510d46)) - -### Features - -- **angular-parser**: Remove scope requirement - ([`90c9d8d`](https://github.com/python-semantic-release/python-semantic-release/commit/90c9d8d4cd6d43be094cda86579e00b507571f98)) - -- **git**: Add push to GH_TOKEN@github-url - ([`546b5bf`](https://github.com/python-semantic-release/python-semantic-release/commit/546b5bf15466c6f5dfe93c1c03ca34604b0326f2)) - - -## v3.1.0 (2015-08-31) - -### Features - -- **pypi**: Add option to disable pypi upload - ([`f5cd079`](https://github.com/python-semantic-release/python-semantic-release/commit/f5cd079edb219de5ad03a71448d578f5f477da9c)) - - -## v3.0.0 (2015-08-25) - -### Bug Fixes - -- **errors**: Add exposing of errors in package - ([`3662d76`](https://github.com/python-semantic-release/python-semantic-release/commit/3662d7663291859dd58a91b4b4ccde4f0edc99b2)) - -- **version**: Parse file instead for version - ([`005dba0`](https://github.com/python-semantic-release/python-semantic-release/commit/005dba0094eeb4098315ef383a746e139ffb504d)) - -This makes it possible to use the version command without a setup.py file. - -### Features - -- **parser**: Add tag parser - ([`a7f392f`](https://github.com/python-semantic-release/python-semantic-release/commit/a7f392fd4524cc9207899075631032e438e2593c)) - -This parser is based on the same commit style as 1.x.x of python-semantic-release. However, it - requires "BREAKING CHANGE: for a breaking change - - -## v2.1.4 (2015-08-24) - -### Bug Fixes - -- **github**: Fix property calls - ([`7ecdeb2`](https://github.com/python-semantic-release/python-semantic-release/commit/7ecdeb22de96b6b55c5404ebf54a751911c4d8cd)) - -Properties can only be used from instances. - - -## v2.1.3 (2015-08-22) - -### Bug Fixes - -- **hvcs**: Make Github.token an property - ([`37d5e31`](https://github.com/python-semantic-release/python-semantic-release/commit/37d5e3110397596a036def5f1dccf0860964332c)) - -### Documentation - -- **api**: Update apidocs - ([`6185380`](https://github.com/python-semantic-release/python-semantic-release/commit/6185380babedbbeab2a2a342f17b4ff3d4df6768)) - -- **parsers**: Add documentation about commit parsers - ([`9b55422`](https://github.com/python-semantic-release/python-semantic-release/commit/9b554222768036024a133153a559cdfc017c1d91)) - -- **readme**: Update readme with information about the changelog command - ([`56a745e`](https://github.com/python-semantic-release/python-semantic-release/commit/56a745ef6fa4edf6f6ba09c78fcc141102cf2871)) - - -## v2.1.2 (2015-08-20) - -### Bug Fixes - -- **cli**: Fix call to generate_changelog in publish - ([`5f8bce4`](https://github.com/python-semantic-release/python-semantic-release/commit/5f8bce4cbb5e1729e674efd6c651e2531aea2a16)) - - -## v2.1.1 (2015-08-20) - -### Bug Fixes - -- **history**: Fix issue in get_previous_version - ([`f961786`](https://github.com/python-semantic-release/python-semantic-release/commit/f961786aa3eaa3a620f47cc09243340fd329b9c2)) - - -## v2.1.0 (2015-08-20) - -### Bug Fixes - -- **cli**: Fix check of token in changelog command - ([`cc6e6ab`](https://github.com/python-semantic-release/python-semantic-release/commit/cc6e6abe1e91d3aa24e8d73e704829669bea5fd7)) - -- **github**: Fix the github releases integration - ([`f0c3c1d`](https://github.com/python-semantic-release/python-semantic-release/commit/f0c3c1db97752b71f2153ae9f623501b0b8e2c98)) - -- **history**: Fix changelog generation - ([`f010272`](https://github.com/python-semantic-release/python-semantic-release/commit/f01027203a8ca69d21b4aff689e60e8c8d6f9af5)) - -This enables regeneration of a given versions changelog. - -### Features - -- **cli**: Add the possibility to repost the changelog - ([`4d028e2`](https://github.com/python-semantic-release/python-semantic-release/commit/4d028e21b9da01be8caac8f23f2c11e0c087e485)) - - -## v2.0.0 (2015-08-19) - -### Bug Fixes - -- **cli**: Change output indentation on changelog - ([`2ca41d3`](https://github.com/python-semantic-release/python-semantic-release/commit/2ca41d3bd1b8b9d9fe7e162772560e3defe2a41e)) - -- **history**: Fix level id's in angular parser - ([`2918d75`](https://github.com/python-semantic-release/python-semantic-release/commit/2918d759bf462082280ede971a5222fe01634ed8)) - -- **history**: Fix regex in angular parser - ([`974ccda`](https://github.com/python-semantic-release/python-semantic-release/commit/974ccdad392d768af5e187dabc184be9ac3e133d)) - -This fixes a problem where multiline commit messages where not correctly parsed. - -- **history**: Support unexpected types in changelog generator - ([`13deacf`](https://github.com/python-semantic-release/python-semantic-release/commit/13deacf5d33ed500e4e94ea702a2a16be2aa7c48)) - -### Features - -- **cli**: Add command for printing the changelog - ([`336b8bc`](https://github.com/python-semantic-release/python-semantic-release/commit/336b8bcc01fc1029ff37a79c92935d4b8ea69203)) - -Usage: `semantic_release changelog` - -- **github**: Add github release changelog helper - ([`da18795`](https://github.com/python-semantic-release/python-semantic-release/commit/da187951af31f377ac57fe17462551cfd776dc6e)) - -- **history**: Add angular parser - ([`91e4f0f`](https://github.com/python-semantic-release/python-semantic-release/commit/91e4f0f4269d01b255efcd6d7121bbfd5a682e12)) - -This adds a parser that follows the angular specification. The parser is not hooked into the history - evaluation yet. However, it will become the default parser of commit messages when the evaluator - starts using exchangeable parsers. - -Related to #17 - -- **history**: Add generate_changelog function - ([`347f21a`](https://github.com/python-semantic-release/python-semantic-release/commit/347f21a1f8d655a71a0e7d58b64d4c6bc6d0bf31)) - -It generates a dict with changelog information to each of the given section types. - -- **history**: Add markdown changelog formatter - ([`d77b58d`](https://github.com/python-semantic-release/python-semantic-release/commit/d77b58db4b66aec94200dccab94f483def4dacc9)) - -- **history**: Set angular parser as the default - ([`c2cf537`](https://github.com/python-semantic-release/python-semantic-release/commit/c2cf537a42beaa60cd372c7c9f8fb45db8085917)) - -BREAKING CHANGE: This changes the default parser. Thus, the default behaviour of the commit log - evaluator will change. From now on it will use the angular commit message spec to determine the - new version. - -- **publish**: Add publishing of changelog to github - ([`74324ba`](https://github.com/python-semantic-release/python-semantic-release/commit/74324ba2749cdbbe80a92b5abbecfeab04617699)) - -- **settings**: Add loading of current parser - ([`7bd0916`](https://github.com/python-semantic-release/python-semantic-release/commit/7bd0916f87a1f9fe839c853eab05cae1af420cd2)) - - -## v1.0.0 (2015-08-04) - - -## v0.9.1 (2015-08-04) - - -## v0.9.0 (2015-08-03) - - -## v0.8.0 (2015-08-03) - - -## v0.7.0 (2015-08-02) - - -## v0.6.0 (2015-08-02) - - -## v0.5.4 (2015-07-29) - - -## v0.5.3 (2015-07-28) - - -## v0.5.2 (2015-07-28) - - -## v0.5.1 (2015-07-28) - - -## v0.5.0 (2015-07-28) - - -## v0.4.0 (2015-07-28) - - -## v0.3.2 (2015-07-28) - - -## v0.3.1 (2015-07-28) - - -## v0.3.0 (2015-07-27) - - -## v0.2.0 (2015-07-27) - - -## v0.1.1 (2015-07-27) - - -## v0.1.0 (2015-07-27) diff --git a/CHANGELOG.rst b/CHANGELOG.rst new file mode 100644 index 000000000..02b76ffcd --- /dev/null +++ b/CHANGELOG.rst @@ -0,0 +1,5415 @@ +.. _changelog: + +========= +CHANGELOG +========= + +.. _changelog-v9.17.0: + +v9.17.0 (2025-01-26) +==================== + +✨ Features +----------- + +* **changelog**: Add ``sort_numerically`` filter function to template environment (`PR#1146`_, + `7792388`_) + +* **changelog**: Parse squashed commits individually (`PR#1112`_, `cf785ca`_) + +* **config**: Extend support of remote urls aliased using git ``insteadOf`` configurations, closes + `#1150`_ (`PR#1151`_, `4045037`_) + +* **parsers**: Parse squashed commits individually (`PR#1112`_, `cf785ca`_) + +* **parser-angular**: Apply PR/MR numbers to all parsed commits from a squash merge (`PR#1112`_, + `cf785ca`_) + +* **parser-angular**: Upgrade angular parser to parse squashed commits individually, closes `#1085`_ + (`PR#1112`_, `cf785ca`_) + +* **parser-emoji**: Add functionality to interpret scopes from gitmoji commit messages (`PR#1112`_, + `cf785ca`_) + +* **parser-emoji**: Upgrade emoji parser to parse squashed commits individually (`PR#1112`_, + `cf785ca`_) + +* **version**: Parse squashed commits individually (`PR#1112`_, `cf785ca`_) + +🪲 Bug Fixes +------------ + +* **github-action**: Disable writing python bytecode in action execution (`PR#1152`_, `315ae21`_) + +⚡ Performance Improvements +--------------------------- + +* **logging**: Remove irrelevant debug logging statements (`PR#1147`_, `f1ef4ec`_) + +📖 Documentation +---------------- + +* **changelog-templates**: Add description for new ``sort_numerically`` filter function (`PR#1146`_, + `7792388`_) + +* **commit-parsing**: Add description for squash commit evaluation option of default parsers + (`PR#1112`_, `cf785ca`_) + +* **configuration**: Update the ``commit_parser_options`` setting description (`PR#1112`_, + `cf785ca`_) + +.. _#1085: https://github.com/python-semantic-release/python-semantic-release/issues/1085 +.. _#1150: https://github.com/python-semantic-release/python-semantic-release/issues/1150 +.. _315ae21: https://github.com/python-semantic-release/python-semantic-release/commit/315ae2176e211b00b13374560d81e127a3065d1a +.. _4045037: https://github.com/python-semantic-release/python-semantic-release/commit/40450375c7951dafddb09bef8001db7180d95f3a +.. _7792388: https://github.com/python-semantic-release/python-semantic-release/commit/77923885c585171e8888aacde989837ecbabf3fc +.. _cf785ca: https://github.com/python-semantic-release/python-semantic-release/commit/cf785ca79a49eb4ee95c148e0ae6a19e230e915c +.. _f1ef4ec: https://github.com/python-semantic-release/python-semantic-release/commit/f1ef4ecf5f22684a870b958f87d1ca2650e612db +.. _PR#1112: https://github.com/python-semantic-release/python-semantic-release/pull/1112 +.. _PR#1146: https://github.com/python-semantic-release/python-semantic-release/pull/1146 +.. _PR#1147: https://github.com/python-semantic-release/python-semantic-release/pull/1147 +.. _PR#1151: https://github.com/python-semantic-release/python-semantic-release/pull/1151 +.. _PR#1152: https://github.com/python-semantic-release/python-semantic-release/pull/1152 + + +.. _changelog-v9.16.1: + +v9.16.1 (2025-01-12) +==================== + +🪲 Bug Fixes +------------ + +* **parser-custom**: Handle relative parent directory paths to module file better (`PR#1142`_, + `c4056fc`_) + +📖 Documentation +---------------- + +* **github-actions**: Update PSR versions in github workflow examples (`PR#1140`_, `9bdd626`_) + +.. _9bdd626: https://github.com/python-semantic-release/python-semantic-release/commit/9bdd626bf8f8359d35725cebe803931063260cac +.. _c4056fc: https://github.com/python-semantic-release/python-semantic-release/commit/c4056fc2e1fb3bddb78728793716ac6fb8522b1a +.. _PR#1140: https://github.com/python-semantic-release/python-semantic-release/pull/1140 +.. _PR#1142: https://github.com/python-semantic-release/python-semantic-release/pull/1142 + + +.. _changelog-v9.16.0: + +v9.16.0 (2025-01-12) +==================== + +✨ Features +----------- + +* **config**: Expand dynamic parser import to handle a filepath to module (`PR#1135`_, `0418fd8`_) + +🪲 Bug Fixes +------------ + +* **changelog**: Fixes PSR release commit exclusions for customized commit messages (`PR#1139`_, + `f9a2078`_) + +* **cmd-version**: Fixes ``--print-tag`` result to match configured tag format (`PR#1134`_, + `a990aa7`_) + +* **cmd-version**: Fixes tag format on default version when force bump for initial release, closes + `#1137`_ (`PR#1138`_, `007fd00`_) + +* **config-changelog**: Validate ``changelog.exclude_commit_patterns`` on config load (`PR#1139`_, + `f9a2078`_) + +📖 Documentation +---------------- + +* **commit-parsing**: Add the new custom parser import spec description for direct path imports, + closes `#687`_ (`PR#1135`_, `0418fd8`_) + +* **configuration**: Adjust ``commit_parser`` option definition for direct path imports (`PR#1135`_, + `0418fd8`_) + +.. _#687: https://github.com/python-semantic-release/python-semantic-release/issues/687 +.. _#1137: https://github.com/python-semantic-release/python-semantic-release/issues/1137 +.. _007fd00: https://github.com/python-semantic-release/python-semantic-release/commit/007fd00a3945ed211ece4baab0b79ad93dc018f5 +.. _0418fd8: https://github.com/python-semantic-release/python-semantic-release/commit/0418fd8d27aac14925aafa50912e751e3aeff2f7 +.. _a990aa7: https://github.com/python-semantic-release/python-semantic-release/commit/a990aa7ab0a9d52d295c04d54d20e9c9f2db2ca5 +.. _f9a2078: https://github.com/python-semantic-release/python-semantic-release/commit/f9a20787437d0f26074fe2121bf0a29576a96df0 +.. _PR#1134: https://github.com/python-semantic-release/python-semantic-release/pull/1134 +.. _PR#1135: https://github.com/python-semantic-release/python-semantic-release/pull/1135 +.. _PR#1138: https://github.com/python-semantic-release/python-semantic-release/pull/1138 +.. _PR#1139: https://github.com/python-semantic-release/python-semantic-release/pull/1139 + + +.. _changelog-v9.15.2: + +v9.15.2 (2024-12-16) +==================== + +🪲 Bug Fixes +------------ + +* **changelog**: Ensures user rendered files are trimmed to end with a single newline (`PR#1118`_, + `6dfbbb0`_) + +* **cli**: Add error message of how to gather full error output (`PR#1116`_, `ba85532`_) + +* **cmd-version**: Enable maintenance prereleases (`PR#864`_, `b88108e`_) + +* **cmd-version**: Fix handling of multiple prerelease token variants & git flow merges (`PR#1120`_, + `8784b9a`_) + +* **cmd-version**: Fix version determination algorithm to capture commits across merged branches + (`PR#1120`_, `8784b9a`_) + +* **cmd-version**: Forces tag timestamp to be same time as release commit (`PR#1117`_, `7898b11`_) + +* **cmd-version**: Handle multiple prerelease token variants properly, closes `#789`_ (`PR#1120`_, + `8784b9a`_) + +* **config**: Ensure default config loads on network mounted windows environments, closes `#1123`_ + (`PR#1124`_, `a64cbc9`_) + +* **version**: Remove some excessive log msgs from debug to silly level (`PR#1120`_, `8784b9a`_) + +* **version-bump**: Increment based on current commit's history only, closes `#861`_ (`PR#864`_, + `b88108e`_) + +⚡ Performance Improvements +--------------------------- + +* **cmd-version**: Refactor version determination algorithm for accuracy & speed (`PR#1120`_, + `8784b9a`_) + +.. _#789: https://github.com/python-semantic-release/python-semantic-release/issues/789 +.. _#861: https://github.com/python-semantic-release/python-semantic-release/issues/861 +.. _#1123: https://github.com/python-semantic-release/python-semantic-release/issues/1123 +.. _6dfbbb0: https://github.com/python-semantic-release/python-semantic-release/commit/6dfbbb0371aef6b125cbcbf89b80dc343ed97360 +.. _7898b11: https://github.com/python-semantic-release/python-semantic-release/commit/7898b1185fc1ad10e96bf3f5e48d9473b45d2b51 +.. _8784b9a: https://github.com/python-semantic-release/python-semantic-release/commit/8784b9ad4bc59384f855b5af8f1b8fb294397595 +.. _a64cbc9: https://github.com/python-semantic-release/python-semantic-release/commit/a64cbc96c110e32f1ec5d1a7b61e950472491b87 +.. _b88108e: https://github.com/python-semantic-release/python-semantic-release/commit/b88108e189e1894e36ae4fdf8ad8a382b5c8c90a +.. _ba85532: https://github.com/python-semantic-release/python-semantic-release/commit/ba85532ddd6fcf1a2205f7ce0b88ea5be76cb621 +.. _PR#864: https://github.com/python-semantic-release/python-semantic-release/pull/864 +.. _PR#1116: https://github.com/python-semantic-release/python-semantic-release/pull/1116 +.. _PR#1117: https://github.com/python-semantic-release/python-semantic-release/pull/1117 +.. _PR#1118: https://github.com/python-semantic-release/python-semantic-release/pull/1118 +.. _PR#1120: https://github.com/python-semantic-release/python-semantic-release/pull/1120 +.. _PR#1124: https://github.com/python-semantic-release/python-semantic-release/pull/1124 + + +.. _changelog-v9.15.1: + +v9.15.1 (2024-12-03) +==================== + +🪲 Bug Fixes +------------ + +* **changelog-md**: Fix commit sort of breaking descriptions section (`75b342e`_) + +* **parser-angular**: Ensure issues are sorted by numeric value rather than text sorted (`3858add`_) + +* **parser-emoji**: Ensure issues are sorted by numeric value rather than text sorted (`7b8d2d9`_) + +.. _3858add: https://github.com/python-semantic-release/python-semantic-release/commit/3858add582fe758dc2ae967d0cd051d43418ecd0 +.. _75b342e: https://github.com/python-semantic-release/python-semantic-release/commit/75b342e6259412cb82d8b7663e5ee4536d14f407 +.. _7b8d2d9: https://github.com/python-semantic-release/python-semantic-release/commit/7b8d2d92e135ab46d1be477073ccccc8c576f121 + + +.. _changelog-v9.15.0: + +v9.15.0 (2024-12-02) +==================== + +✨ Features +----------- + +* **changelog-md**: Add a breaking changes section to default Markdown template, closes `#244`_ + (`PR#1110`_, `4fde30e`_) + +* **changelog-md**: Alphabetize breaking change descriptions in markdown changelog template + (`PR#1110`_, `4fde30e`_) + +* **changelog-md**: Alphabetize commit summaries & scopes in markdown changelog template + (`PR#1111`_, `8327068`_) + +* **changelog-rst**: Add a breaking changes section to default reStructuredText template, closes + `#244`_ (`PR#1110`_, `4fde30e`_) + +* **changelog-rst**: Alphabetize breaking change descriptions in ReStructuredText template + (`PR#1110`_, `4fde30e`_) + +* **changelog-rst**: Alphabetize commit summaries & scopes in ReStructuredText template (`PR#1111`_, + `8327068`_) + +* **commit-parser**: Enable parsers to flag commit to be ignored for changelog, closes `#778`_ + (`PR#1108`_, `0cc668c`_) + +* **default-changelog**: Add a separate formatted breaking changes section, closes `#244`_ + (`PR#1110`_, `4fde30e`_) + +* **default-changelog**: Alphabetize commit summaries & scopes in change sections (`PR#1111`_, + `8327068`_) + +* **parsers**: Add ``other_allowed_tags`` option for commit parser options (`PR#1109`_, `f90b8dc`_) + +* **parsers**: Enable parsers to identify linked issues on a commit (`PR#1109`_, `f90b8dc`_) + +* **parser-angular**: Automatically parse angular issue footers from commit messages (`PR#1109`_, + `f90b8dc`_) + +* **parser-custom**: Enable custom parsers to identify linked issues on a commit (`PR#1109`_, + `f90b8dc`_) + +* **parser-emoji**: Parse issue reference footers from commit messages (`PR#1109`_, `f90b8dc`_) + +* **release-notes**: Add tag comparison link to release notes when supported (`PR#1107`_, + `9073344`_) + +🪲 Bug Fixes +------------ + +* **cmd-version**: Ensure release utilizes a timezone aware datetime (`ca817ed`_) + +* **default-changelog**: Alphabetically sort commit descriptions in version type sections + (`bdaaf5a`_) + +* **util**: Prevent git footers from being collapsed during parse (`PR#1109`_, `f90b8dc`_) + +📖 Documentation +---------------- + +* **api-parsers**: Add option documentation to parser options (`PR#1109`_, `f90b8dc`_) + +* **changelog-templates**: Update examples using new ``commit.linked_issues`` attribute (`PR#1109`_, + `f90b8dc`_) + +* **commit-parsing**: Improve & expand commit parsing w/ parser descriptions (`PR#1109`_, + `f90b8dc`_) + +.. _#244: https://github.com/python-semantic-release/python-semantic-release/issues/244 +.. _#778: https://github.com/python-semantic-release/python-semantic-release/issues/778 +.. _0cc668c: https://github.com/python-semantic-release/python-semantic-release/commit/0cc668c36490401dff26bb2c3141f6120a2c47d0 +.. _4fde30e: https://github.com/python-semantic-release/python-semantic-release/commit/4fde30e0936ecd186e448f1caf18d9ba377c55ad +.. _8327068: https://github.com/python-semantic-release/python-semantic-release/commit/83270683fd02b626ed32179d94fa1e3c7175d113 +.. _9073344: https://github.com/python-semantic-release/python-semantic-release/commit/9073344164294360843ef5522e7e4c529985984d +.. _bdaaf5a: https://github.com/python-semantic-release/python-semantic-release/commit/bdaaf5a460ca77edc40070ee799430122132dc45 +.. _ca817ed: https://github.com/python-semantic-release/python-semantic-release/commit/ca817ed9024cf84b306a047675534cc36dc116b2 +.. _f90b8dc: https://github.com/python-semantic-release/python-semantic-release/commit/f90b8dc6ce9f112ef2c98539d155f9de24398301 +.. _PR#1107: https://github.com/python-semantic-release/python-semantic-release/pull/1107 +.. _PR#1108: https://github.com/python-semantic-release/python-semantic-release/pull/1108 +.. _PR#1109: https://github.com/python-semantic-release/python-semantic-release/pull/1109 +.. _PR#1110: https://github.com/python-semantic-release/python-semantic-release/pull/1110 +.. _PR#1111: https://github.com/python-semantic-release/python-semantic-release/pull/1111 + + +.. _changelog-v9.14.0: + +v9.14.0 (2024-11-11) +==================== + +✨ Features +----------- + +* **changelog**: Add md to rst conversion for markdown inline links (`cb2af1f`_) + +* **changelog**: Define first release w/o change descriptions for default MD template (`fa89dec`_) + +* **changelog**: Define first release w/o change descriptions for default RST template (`e30c94b`_) + +* **changelog**: Prefix scopes on commit descriptions in default template (`PR#1093`_, `560fd2c`_) + +* **changelog-md**: Add markdown inline link format macro (`c6d8211`_) + +* **changelog-md**: Prefix scopes on commit descriptions in Markdown changelog template (`PR#1093`_, + `560fd2c`_) + +* **changelog-rst**: Prefix scopes on commit descriptions in ReStructuredText template (`PR#1093`_, + `560fd2c`_) + +* **configuration**: Add ``changelog.default_templates.mask_initial_release`` option (`595a70b`_) + +* **context**: Add ``mask_initial_release`` setting to changelog context (`6f2ee39`_) + +* **release-notes**: Define first release w/o change descriptions in default template (`83167a3`_) + +🪲 Bug Fixes +------------ + +* **release-notes**: Override default wordwrap to non-wrap for in default template (`99ab99b`_) + +📖 Documentation +---------------- + +* **changelog-templates**: Document new ``mask_initial_release`` changelog context variable + (`f294957`_) + +* **configuration**: Document new ``mask_initial_release`` option usage & effect (`3cabcdc`_) + +* **homepage**: Fix reference to new ci workflow for test status badge (`6760069`_) + +.. _3cabcdc: https://github.com/python-semantic-release/python-semantic-release/commit/3cabcdcd9473e008604e74cc2d304595317e921d +.. _560fd2c: https://github.com/python-semantic-release/python-semantic-release/commit/560fd2c0d58c97318377cb83af899a336d24cfcc +.. _595a70b: https://github.com/python-semantic-release/python-semantic-release/commit/595a70bcbc8fea1f8ccf6c5069c41c35ec4efb8d +.. _6760069: https://github.com/python-semantic-release/python-semantic-release/commit/6760069e7489f50635beb5aedbbeb2cb82b7c584 +.. _6f2ee39: https://github.com/python-semantic-release/python-semantic-release/commit/6f2ee39414b3cf75c0b67dee4db0146bbc1041bb +.. _83167a3: https://github.com/python-semantic-release/python-semantic-release/commit/83167a3dcceb7db16b790e1b0efd5fc75fee8942 +.. _99ab99b: https://github.com/python-semantic-release/python-semantic-release/commit/99ab99bb0ba350ca1913a2bde9696f4242278972 +.. _c6d8211: https://github.com/python-semantic-release/python-semantic-release/commit/c6d8211c859442df17cb41d2ff19fdb7a81cdb76 +.. _cb2af1f: https://github.com/python-semantic-release/python-semantic-release/commit/cb2af1f17cf6c8ae037c6cd8bb8b4d9c019bb47e +.. _e30c94b: https://github.com/python-semantic-release/python-semantic-release/commit/e30c94bffe62b42e8dc6ed4fed6260e57b4d532b +.. _f294957: https://github.com/python-semantic-release/python-semantic-release/commit/f2949577dfb2dbf9c2ac952c1bbcc4ab84da080b +.. _fa89dec: https://github.com/python-semantic-release/python-semantic-release/commit/fa89dec239efbae7544b187f624a998fa9ecc309 +.. _PR#1093: https://github.com/python-semantic-release/python-semantic-release/pull/1093 + + +.. _changelog-v9.13.0: + +v9.13.0 (2024-11-10) +==================== + +✨ Features +----------- + +* **changelog**: Add PR/MR url linking to default Markdown changelog, closes `#924`_, `#953`_ + (`cd8d131`_) + +* **changelog**: Add PR/MR url linking to default reStructuredText template, closes `#924`_, `#953`_ + (`5f018d6`_) + +* **parsed-commit**: Add linked merge requests list to the ``ParsedCommit`` object (`9a91062`_) + +* **parser-angular**: Automatically parse PR/MR numbers from subject lines in commits (`2ac798f`_) + +* **parser-emoji**: Automatically parse PR/MR numbers from subject lines in commits (`bca9909`_) + +* **parser-scipy**: Automatically parse PR/MR numbers from subject lines in commits (`2b3f738`_) + +🪲 Bug Fixes +------------ + +* **changelog-rst**: Ignore unknown parsed commit types in default RST changelog (`77609b1`_) + +* **parser-angular**: Drop the ``breaking`` category but still maintain a major level bump + (`f1ffa54`_) + +* **parsers**: Improve reliability of text unwordwrap of descriptions (`436374b`_) + +⚡ Performance Improvements +--------------------------- + +* **parser-angular**: Simplify commit parsing type pre-calculation (`a86a28c`_) + +* **parser-emoji**: Increase speed of commit parsing (`2c9c468`_) + +* **parser-scipy**: Increase speed & decrease complexity of commit parsing (`2b661ed`_) + +📖 Documentation +---------------- + +* **changelog-templates**: Add ``linked_merge_request`` field to examples (`d4376bc`_) + +* **changelog-templates**: Fix api class reference links (`7a5bdf2`_) + +* **commit-parsing**: Add ``linked_merge_request`` field to Parsed Commit definition (`ca61889`_) + +.. _#924: https://github.com/python-semantic-release/python-semantic-release/issues/924 +.. _#953: https://github.com/python-semantic-release/python-semantic-release/issues/953 +.. _2ac798f: https://github.com/python-semantic-release/python-semantic-release/commit/2ac798f92e0c13c1db668747f7e35a65b99ae7ce +.. _2b3f738: https://github.com/python-semantic-release/python-semantic-release/commit/2b3f73801f5760bac29acd93db3ffb2bc790cda0 +.. _2b661ed: https://github.com/python-semantic-release/python-semantic-release/commit/2b661ed122a6f0357a6b92233ac1351c54c7794e +.. _2c9c468: https://github.com/python-semantic-release/python-semantic-release/commit/2c9c4685a66feb35cd78571cf05f76344dd6d66a +.. _436374b: https://github.com/python-semantic-release/python-semantic-release/commit/436374b04128d1550467ae97ba90253f1d1b3878 +.. _5f018d6: https://github.com/python-semantic-release/python-semantic-release/commit/5f018d630b4c625bdf6d329b27fd966eba75b017 +.. _77609b1: https://github.com/python-semantic-release/python-semantic-release/commit/77609b1917a00b106ce254e6f6d5edcd1feebba7 +.. _7a5bdf2: https://github.com/python-semantic-release/python-semantic-release/commit/7a5bdf29b3df0f9a1346ea5301d2a7fee953667b +.. _9a91062: https://github.com/python-semantic-release/python-semantic-release/commit/9a9106212d6c240e9d3358e139b4c4694eaf9c4b +.. _a86a28c: https://github.com/python-semantic-release/python-semantic-release/commit/a86a28c5e26ed766cda71d26b9382c392e377c61 +.. _bca9909: https://github.com/python-semantic-release/python-semantic-release/commit/bca9909c1b61fdb1f9ccf823fceb6951cd059820 +.. _ca61889: https://github.com/python-semantic-release/python-semantic-release/commit/ca61889d4ac73e9864fbf637fb87ab2d5bc053ea +.. _cd8d131: https://github.com/python-semantic-release/python-semantic-release/commit/cd8d1310a4000cc79b529fbbdc58933f4c6373c6 +.. _d4376bc: https://github.com/python-semantic-release/python-semantic-release/commit/d4376bc2ae4d3708d501d91211ec3ee3a923e9b5 +.. _f1ffa54: https://github.com/python-semantic-release/python-semantic-release/commit/f1ffa5411892de34cdc842fd55c460a24b6685c6 + + +.. _changelog-v9.12.2: + +v9.12.2 (2024-11-07) +==================== + +🪲 Bug Fixes +------------ + +* **bitbucket**: Fix ``pull_request_url`` filter to ignore an PR prefix gracefully (`PR#1089`_, + `275ec88`_) + +* **cli**: Gracefully capture all exceptions unless in very verbose debug mode (`PR#1088`_, + `13ca44f`_) + +* **gitea**: Fix ``issue_url`` filter to ignore an issue prefix gracefully (`PR#1089`_, `275ec88`_) + +* **gitea**: Fix ``pull_request_url`` filter to ignore an PR prefix gracefully (`PR#1089`_, + `275ec88`_) + +* **github**: Fix ``issue_url`` filter to ignore an issue prefix gracefully (`PR#1089`_, `275ec88`_) + +* **github**: Fix ``pull_request_url`` filter to ignore an PR prefix gracefully (`PR#1089`_, + `275ec88`_) + +* **gitlab**: Fix ``issue_url`` filter to ignore an issue prefix gracefully (`PR#1089`_, `275ec88`_) + +* **gitlab**: Fix ``merge_request_url`` filter to ignore an PR prefix gracefully (`PR#1089`_, + `275ec88`_) + +* **hvcs**: Add flexibility to issue & MR/PR url jinja filters (`PR#1089`_, `275ec88`_) + +📖 Documentation +---------------- + +* **changelog-templates**: Update descriptions of issue & MR/PR url jinja filters (`PR#1089`_, + `275ec88`_) + +.. _13ca44f: https://github.com/python-semantic-release/python-semantic-release/commit/13ca44f4434098331f70e6937684679cf1b4106a +.. _275ec88: https://github.com/python-semantic-release/python-semantic-release/commit/275ec88e6d1637c47065bb752a60017ceba9876c +.. _PR#1088: https://github.com/python-semantic-release/python-semantic-release/pull/1088 +.. _PR#1089: https://github.com/python-semantic-release/python-semantic-release/pull/1089 + + +.. _changelog-v9.12.1: + +v9.12.1 (2024-11-06) +==================== + +🪲 Bug Fixes +------------ + +* **changelog**: Fix raw-inline pattern replacement in ``convert_md_to_rst`` filter (`2dc70a6`_) + +* **cmd-version**: Fix ``--as-prerelease`` when no commit change from last full release (`PR#1076`_, + `3b7b772`_) + +* **release-notes**: Add context variable shorthand ``ctx`` like docs claim & changelog has + (`d618d83`_) + +📖 Documentation +---------------- + +* **contributing**: Update local testing instructions (`74f03d4`_) + +.. _2dc70a6: https://github.com/python-semantic-release/python-semantic-release/commit/2dc70a6106776106b0fba474b0029071317d639f +.. _3b7b772: https://github.com/python-semantic-release/python-semantic-release/commit/3b7b77246100cedd8cc8f289395f7641187ffdec +.. _74f03d4: https://github.com/python-semantic-release/python-semantic-release/commit/74f03d44684b7b2d84f9f5e471425b02f8bf91c3 +.. _d618d83: https://github.com/python-semantic-release/python-semantic-release/commit/d618d83360c4409fc149f70b97c5fe338fa89968 +.. _PR#1076: https://github.com/python-semantic-release/python-semantic-release/pull/1076 + + +.. _changelog-v9.12.0: + +v9.12.0 (2024-10-18) +==================== + +✨ Features +----------- + +* **changelog**: Add ``autofit_text_width`` filter to template environment (`PR#1062`_, `83e4b86`_) + +🪲 Bug Fixes +------------ + +* **changelog**: Ignore commit exclusion when a commit causes a version bump (`e8f886e`_) + +* **parser-angular**: Change ``Fixes`` commit type heading to ``Bug Fixes`` (`PR#1064`_, `09e3a4d`_) + +* **parser-emoji**: Enable the default bump level option (`bc27995`_) + +📖 Documentation +---------------- + +* **changelog-templates**: Add definition & usage of ``autofit_text_width`` template filter + (`PR#1062`_, `83e4b86`_) + +* **commit-parsers**: Add deprecation message for the tag parser (`af94540`_) + +* **configuration**: Add deprecation message for the tag parser (`a83b7e4`_) + +.. _09e3a4d: https://github.com/python-semantic-release/python-semantic-release/commit/09e3a4da6237740de8e9932d742b18d990e9d079 +.. _83e4b86: https://github.com/python-semantic-release/python-semantic-release/commit/83e4b86abd4754c2f95ec2e674f04deb74b9a1e6 +.. _a83b7e4: https://github.com/python-semantic-release/python-semantic-release/commit/a83b7e43e4eaa99790969a6c85f44e01cde80d0a +.. _af94540: https://github.com/python-semantic-release/python-semantic-release/commit/af94540f2b1c63bf8a4dc977d5d0f66176962b64 +.. _bc27995: https://github.com/python-semantic-release/python-semantic-release/commit/bc27995255a96b9d6cc743186e7c35098822a7f6 +.. _e8f886e: https://github.com/python-semantic-release/python-semantic-release/commit/e8f886ef2abe8ceaea0a24a0112b92a167abd6a9 +.. _PR#1062: https://github.com/python-semantic-release/python-semantic-release/pull/1062 +.. _PR#1064: https://github.com/python-semantic-release/python-semantic-release/pull/1064 + + +.. _changelog-v9.11.1: + +v9.11.1 (2024-10-15) +==================== + +🪲 Bug Fixes +------------ + +* **changelog**: Prevent custom template errors when components are in hidden folders (`PR#1060`_, + `a7614b0`_) + +.. _a7614b0: https://github.com/python-semantic-release/python-semantic-release/commit/a7614b0db8ce791e4252209e66f42b5b5275dffd +.. _PR#1060: https://github.com/python-semantic-release/python-semantic-release/pull/1060 + + +.. _changelog-v9.11.0: + +v9.11.0 (2024-10-12) +==================== + +✨ Features +----------- + +* **changelog**: Add ``convert_md_to_rst`` filter to changelog environment (`PR#1055`_, `c2e8831`_) + +* **changelog**: Add default changelog in re-structured text format, closes `#399`_ (`PR#1055`_, + `c2e8831`_) + +* **changelog**: Add default changelog template in reStructuredText format (`PR#1055`_, `c2e8831`_) + +* **config**: Enable default ``changelog.insertion_flag`` based on output format (`PR#1055`_, + `c2e8831`_) + +* **config**: Enable target changelog filename to trigger RST output format, closes `#399`_ + (`PR#1055`_, `c2e8831`_) + +🪲 Bug Fixes +------------ + +* **changelog**: Correct spacing for default markdown template during updates (`PR#1055`_, + `c2e8831`_) + +📖 Documentation +---------------- + +* **changelog**: Clarify the ``convert_md_to_rst`` filter added to the template environment + (`PR#1055`_, `c2e8831`_) + +* **changelog**: Increase detail about configuration options of default changelog creation + (`PR#1055`_, `c2e8831`_) + +* **configuration**: Update ``changelog_file`` with deprecation notice of setting relocation + (`PR#1055`_, `c2e8831`_) + +* **configuration**: Update ``output_format`` description for reStructuredText support (`PR#1055`_, + `c2e8831`_) + +* **configuration**: Update details of ``insertion_flag``'s dynamic defaults with rst (`PR#1055`_, + `c2e8831`_) + +.. _#399: https://github.com/python-semantic-release/python-semantic-release/issues/399 +.. _c2e8831: https://github.com/python-semantic-release/python-semantic-release/commit/c2e883104d3c11e56f229638e988d8b571f86e34 +.. _PR#1055: https://github.com/python-semantic-release/python-semantic-release/pull/1055 + + +.. _changelog-v9.10.1: + +v9.10.1 (2024-10-10) +==================== + +🪲 Bug Fixes +------------ + +* **config**: Handle branch match regex errors gracefully (`PR#1054`_, `4d12251`_) + +.. _4d12251: https://github.com/python-semantic-release/python-semantic-release/commit/4d12251c678a38de6b71cac5b9c1390eb9dd8ad6 +.. _PR#1054: https://github.com/python-semantic-release/python-semantic-release/pull/1054 + + +.. _changelog-v9.10.0: + +v9.10.0 (2024-10-08) +==================== + +✨ Features +----------- + +* **changelog**: Add ``changelog_insertion_flag`` to changelog template context (`PR#1045`_, + `c18c245`_) + +* **changelog**: Add ``changelog_mode`` to changelog template context (`PR#1045`_, `c18c245`_) + +* **changelog**: Add ``prev_changelog_file`` to changelog template context (`PR#1045`_, `c18c245`_) + +* **changelog**: Add ``read_file`` function to changelog template context (`PR#1045`_, `c18c245`_) + +* **changelog**: Add shorthand ``ctx`` variable to changelog template env (`PR#1045`_, `c18c245`_) + +* **changelog**: Modify changelog template to support changelog updates, closes `#858`_ + (`PR#1045`_, `c18c245`_) + +* **config**: Add ``changelog.default_templates.output_format`` config option (`PR#1045`_, + `c18c245`_) + +* **config**: Add ``changelog.insertion_flag`` as configuration option (`PR#1045`_, `c18c245`_) + +* **config**: Add ``changelog.mode`` as configuration option (`PR#1045`_, `c18c245`_) + +* **github-actions**: Add an action ``build`` directive to toggle the ``--skip-build`` option + (`PR#1044`_, `26597e2`_) + +🪲 Bug Fixes +------------ + +* **changelog**: Adjust angular heading names for readability (`PR#1045`_, `c18c245`_) + +* **changelog**: Ensure changelog templates can handle complex directory includes (`PR#1045`_, + `c18c245`_) + +* **changelog**: Only render user templates when files exist (`PR#1045`_, `c18c245`_) + +* **config**: Prevent jinja from autoescaping markdown content by default (`PR#1045`_, `c18c245`_) + +📖 Documentation +---------------- + +* **changelog-templates**: Improve detail & describe new ``changelog.mode="update"`` (`PR#1045`_, + `c18c245`_) + +* **commands**: Update definition of the version commands ``--skip-build`` option (`PR#1044`_, + `26597e2`_) + +* **configuration**: Add ``changelog.mode`` and ``changelog.insertion_flag`` config definitions + (`PR#1045`_, `c18c245`_) + +* **configuration**: Define the new ``changelog.default_templates.output_format`` option + (`PR#1045`_, `c18c245`_) + +* **configuration**: Mark version of configuration setting introduction (`PR#1045`_, `c18c245`_) + +* **configuration**: Standardize all true/false to lowercase ensuring toml-compatibility + (`PR#1045`_, `c18c245`_) + +* **configuration**: Update ``changelog.environment.autoescape`` default to ``false`` to match code + (`PR#1045`_, `c18c245`_) + +* **github-actions**: Add description of the ``build`` input directive (`PR#1044`_, `26597e2`_) + +* **github-actions**: Update primary example with workflow sha controlled pipeline (`14f04df`_) + +* **homepage**: Update custom changelog reference (`PR#1045`_, `c18c245`_) + +.. _#722: https://github.com/python-semantic-release/python-semantic-release/issues/722 +.. _#858: https://github.com/python-semantic-release/python-semantic-release/issues/858 +.. _14f04df: https://github.com/python-semantic-release/python-semantic-release/commit/14f04dffc7366142faecebb162d4449501cbf1fd +.. _26597e2: https://github.com/python-semantic-release/python-semantic-release/commit/26597e24a80a37500264aa95a908ba366699099e +.. _c18c245: https://github.com/python-semantic-release/python-semantic-release/commit/c18c245df51a9778af09b9dc7a315e3f11cdcda0 +.. _PR#1044: https://github.com/python-semantic-release/python-semantic-release/pull/1044 +.. _PR#1045: https://github.com/python-semantic-release/python-semantic-release/pull/1045 + + +.. _changelog-v9.9.0: + +v9.9.0 (2024-09-28) +=================== + +✨ Features +----------- + +* **github-actions**: Add ``is_prerelease`` output to the version action (`PR#1038`_, `6a5d35d`_) + +📖 Documentation +---------------- + +* **automatic-releases**: Drop extrenous github push configuration (`PR#1011`_, `2135c68`_) + +* **github-actions**: Add configuration & description of publish action (`PR#1011`_, `2135c68`_) + +* **github-actions**: Add description of new ``is_prerelease`` output for version action + (`PR#1038`_, `6a5d35d`_) + +* **github-actions**: Clarify & consolidate GitHub Actions usage docs, closes `#907`_ (`PR#1011`_, + `2135c68`_) + +* **github-actions**: Expand descriptions & clarity of actions configs (`PR#1011`_, `2135c68`_) + +* **github-actions**: Revert removal of namespace prefix from examples (`PR#1011`_, `2135c68`_) + +* **homepage**: Remove link to old github config & update token scope config (`PR#1011`_, + `2135c68`_) + +.. _#907: https://github.com/python-semantic-release/python-semantic-release/issues/907 +.. _2135c68: https://github.com/python-semantic-release/python-semantic-release/commit/2135c68ccbdad94378809902b52fcad546efd5b3 +.. _6a5d35d: https://github.com/python-semantic-release/python-semantic-release/commit/6a5d35d0d9124d6a6ee7910711b4154b006b8773 +.. _PR#1011: https://github.com/python-semantic-release/python-semantic-release/pull/1011 +.. _PR#1038: https://github.com/python-semantic-release/python-semantic-release/pull/1038 + + +.. _changelog-v9.8.9: + +v9.8.9 (2024-09-27) +=================== + +🪲 Bug Fixes +------------ + +* **version-cmd**: Ensure ``version_variables`` do not match partial variable names (`PR#1028`_, + `156915c`_) + +* **version-cmd**: Improve ``version_variables`` flexibility w/ quotes (ie. json, yaml, etc) + (`PR#1028`_, `156915c`_) + +* **version-cmd**: Increase ``version_variable`` flexibility with quotations (ie. json, yaml, etc), + closes `#601`_, `#706`_, `#962`_, `#1026`_ (`PR#1028`_, `156915c`_) + +📖 Documentation +---------------- + +* Update docstrings to resolve sphinx failures, closes `#1029`_ (`PR#1030`_, `d84efc7`_) + +* **configuration**: Add clarity to ``version_variables`` usage & limitations (`PR#1028`_, + `156915c`_) + +* **homepage**: Re-structure homepage to be separate from project readme (`PR#1032`_, `2307ed2`_) + +* **README**: Simplify README to point at official docs (`PR#1032`_, `2307ed2`_) + +.. _#1026: https://github.com/python-semantic-release/python-semantic-release/issues/1026 +.. _#1029: https://github.com/python-semantic-release/python-semantic-release/issues/1029 +.. _#601: https://github.com/python-semantic-release/python-semantic-release/issues/601 +.. _#706: https://github.com/python-semantic-release/python-semantic-release/issues/706 +.. _#962: https://github.com/python-semantic-release/python-semantic-release/issues/962 +.. _156915c: https://github.com/python-semantic-release/python-semantic-release/commit/156915c7d759098f65cf9de7c4e980b40b38d5f1 +.. _2307ed2: https://github.com/python-semantic-release/python-semantic-release/commit/2307ed29d9990bf1b6821403a4b8db3365ef8bb5 +.. _d84efc7: https://github.com/python-semantic-release/python-semantic-release/commit/d84efc7719a8679e6979d513d1c8c60904af7384 +.. _PR#1028: https://github.com/python-semantic-release/python-semantic-release/pull/1028 +.. _PR#1030: https://github.com/python-semantic-release/python-semantic-release/pull/1030 +.. _PR#1032: https://github.com/python-semantic-release/python-semantic-release/pull/1032 + + +.. _changelog-v9.8.8: + +v9.8.8 (2024-09-01) +=================== + +🪲 Bug Fixes +------------ + +* **config**: Fix path traversal detection for windows compatibility, closes `#994`_ (`PR#1014`_, + `16e6daa`_) + +📖 Documentation +---------------- + +* **configuration**: Update ``build_command`` env table for windows to use all capital vars + (`0e8451c`_) + +* **github-actions**: Update version in examples to latest version (`3c894ea`_) + +.. _#994: https://github.com/python-semantic-release/python-semantic-release/issues/994 +.. _0e8451c: https://github.com/python-semantic-release/python-semantic-release/commit/0e8451cf9003c6a3bdcae6878039d7d9a23d6d5b +.. _16e6daa: https://github.com/python-semantic-release/python-semantic-release/commit/16e6daaf851ce1eabf5fbd5aa9fe310a8b0f22b3 +.. _3c894ea: https://github.com/python-semantic-release/python-semantic-release/commit/3c894ea8a555d20b454ebf34785e772959bbb4fe +.. _PR#1014: https://github.com/python-semantic-release/python-semantic-release/pull/1014 + + +.. _changelog-v9.8.7: + +v9.8.7 (2024-08-20) +=================== + +🪲 Bug Fixes +------------ + +* Provide ``context.history`` global in release notes templates (`PR#1005`_, `5bd91b4`_) + +* **release-notes**: Fix noop-changelog to print raw release notes (`PR#1005`_, `5bd91b4`_) + +* **release-notes**: Provide ``context.history`` global in release note templates, closes `#984`_ + (`PR#1005`_, `5bd91b4`_) + +📖 Documentation +---------------- + +* Use pinned version for GHA examples (`PR#1004`_, `5fdf761`_) + +* **changelog**: Clarify description of the default changelog generation process (`399fa65`_) + +* **configuration**: Clarify ``changelog_file`` vs ``template_dir`` option usage, closes `#983`_ + (`a7199c8`_) + +* **configuration**: Fix build_command_env table rendering (`PR#996`_, `a5eff0b`_) + +* **github-actions**: Adjust formatting & version warning in code snippets (`PR#1004`_, `5fdf761`_) + +* **github-actions**: Use pinned version for GHA examples, closes `#1003`_ (`PR#1004`_, `5fdf761`_) + +.. _#1003: https://github.com/python-semantic-release/python-semantic-release/issues/1003 +.. _#983: https://github.com/python-semantic-release/python-semantic-release/issues/983 +.. _#984: https://github.com/python-semantic-release/python-semantic-release/issues/984 +.. _399fa65: https://github.com/python-semantic-release/python-semantic-release/commit/399fa6521d5c6c4397b1d6e9b13ea7945ae92543 +.. _5bd91b4: https://github.com/python-semantic-release/python-semantic-release/commit/5bd91b4d7ac33ddf10446f3e66d7d11e0724aeb2 +.. _5fdf761: https://github.com/python-semantic-release/python-semantic-release/commit/5fdf7614c036a77ffb051cd30f57d0a63c062c0d +.. _a5eff0b: https://github.com/python-semantic-release/python-semantic-release/commit/a5eff0bfe41d2fd5d9ead152a132010b718b7772 +.. _a7199c8: https://github.com/python-semantic-release/python-semantic-release/commit/a7199c8cd6041a9de017694302e49b139bbcb034 +.. _PR#1004: https://github.com/python-semantic-release/python-semantic-release/pull/1004 +.. _PR#1005: https://github.com/python-semantic-release/python-semantic-release/pull/1005 +.. _PR#996: https://github.com/python-semantic-release/python-semantic-release/pull/996 + + +.. _changelog-v9.8.6: + +v9.8.6 (2024-07-20) +=================== + +🪲 Bug Fixes +------------ + +* **version-cmd**: Resolve build command execution in powershell (`PR#980`_, `32c8e70`_) + +📖 Documentation +---------------- + +* **configuration**: Correct GHA parameter name for commit email (`PR#981`_, `ce9ffdb`_) + +.. _32c8e70: https://github.com/python-semantic-release/python-semantic-release/commit/32c8e70915634d8e560b470c3cf38c27cebd7ae0 +.. _ce9ffdb: https://github.com/python-semantic-release/python-semantic-release/commit/ce9ffdb82c2358184b288fa18e83a4075f333277 +.. _PR#980: https://github.com/python-semantic-release/python-semantic-release/pull/980 +.. _PR#981: https://github.com/python-semantic-release/python-semantic-release/pull/981 + + +.. _changelog-v9.8.5: + +v9.8.5 (2024-07-06) +=================== + +🪲 Bug Fixes +------------ + +* Enable ``--print-last-released*`` when in detached head or non-release branch (`PR#926`_, + `782c0a6`_) + +* **changelog**: Resolve commit ordering issue when dates are similar (`PR#972`_, `bfda159`_) + +* **version-cmd**: Drop branch restriction for ``--print-last-released*`` opts, closes `#900`_ + (`PR#926`_, `782c0a6`_) + +⚡ Performance Improvements +--------------------------- + +* Improve git history processing for changelog generation (`PR#972`_, `bfda159`_) + +* **changelog**: Improve git history parser changelog generation (`PR#972`_, `bfda159`_) + +.. _#900: https://github.com/python-semantic-release/python-semantic-release/issues/900 +.. _782c0a6: https://github.com/python-semantic-release/python-semantic-release/commit/782c0a6109fb49e168c37f279928c0a4959f8ac6 +.. _bfda159: https://github.com/python-semantic-release/python-semantic-release/commit/bfda1593af59e9e728c584dd88d7927fc52c879f +.. _PR#926: https://github.com/python-semantic-release/python-semantic-release/pull/926 +.. _PR#972: https://github.com/python-semantic-release/python-semantic-release/pull/972 + + +.. _changelog-v9.8.4: + +v9.8.4 (2024-07-04) +=================== + +🪲 Bug Fixes +------------ + +* **changelog-cmd**: Remove usage strings when error occured, closes `#810`_ (`348a51d`_) + +* **changelog-cmd**: Render default changelog when user template directory exist but is empty + (`bded8de`_) + +* **config**: Prevent path traversal manipulation of target changelog location (`43e35d0`_) + +* **config**: Prevent path traversal manipulation of target changelog location (`3eb3dba`_) + +* **publish-cmd**: Prevent error when provided tag does not exist locally (`16afbbb`_) + +* **publish-cmd**: Remove usage strings when error occured, closes `#810`_ (`afbb187`_) + +* **version-cmd**: Remove usage strings when error occurred, closes `#810`_ (`a7c17c7`_) + +.. _#810: https://github.com/python-semantic-release/python-semantic-release/issues/810 +.. _16afbbb: https://github.com/python-semantic-release/python-semantic-release/commit/16afbbb8fbc3a97243e96d7573f4ad2eba09aab9 +.. _348a51d: https://github.com/python-semantic-release/python-semantic-release/commit/348a51db8a837d951966aff3789aa0c93d473829 +.. _3eb3dba: https://github.com/python-semantic-release/python-semantic-release/commit/3eb3dbafec4223ee463b90e927e551639c69426b +.. _43e35d0: https://github.com/python-semantic-release/python-semantic-release/commit/43e35d0972e8a29239d18ed079d1e2013342fcbd +.. _a7c17c7: https://github.com/python-semantic-release/python-semantic-release/commit/a7c17c73fd7becb6d0e042e45ff6765605187e2a +.. _afbb187: https://github.com/python-semantic-release/python-semantic-release/commit/afbb187d6d405fdf6765082e2a1cecdcd7d357df +.. _bded8de: https://github.com/python-semantic-release/python-semantic-release/commit/bded8deae6c92f6dde9774802d9f3716a5cb5705 + + +.. _changelog-v9.8.3: + +v9.8.3 (2024-06-18) +=================== + +🪲 Bug Fixes +------------ + +* **parser**: Strip DOS carriage-returns in commits, closes `#955`_ (`PR#956`_, `0b005df`_) + +.. _#955: https://github.com/python-semantic-release/python-semantic-release/issues/955 +.. _0b005df: https://github.com/python-semantic-release/python-semantic-release/commit/0b005df0a8c7730ee0c71453c9992d7b5d2400a4 +.. _PR#956: https://github.com/python-semantic-release/python-semantic-release/pull/956 + + +.. _changelog-v9.8.2: + +v9.8.2 (2024-06-17) +=================== + +🪲 Bug Fixes +------------ + +* **templates**: Suppress extra newlines in default changelog (`PR#954`_, `7b0079b`_) + +.. _7b0079b: https://github.com/python-semantic-release/python-semantic-release/commit/7b0079bf3e17c0f476bff520b77a571aeac469d0 +.. _PR#954: https://github.com/python-semantic-release/python-semantic-release/pull/954 + + +.. _changelog-v9.8.1: + +v9.8.1 (2024-06-05) +=================== + +🪲 Bug Fixes +------------ + +* Improve build cmd env on windows (`PR#942`_, `d911fae`_) + +* **version-cmd**: Pass windows specific env vars to build cmd when on windows (`PR#942`_, + `d911fae`_) + +📖 Documentation +---------------- + +* **configuration**: Define windows specific env vars for build cmd (`PR#942`_, `d911fae`_) + +.. _d911fae: https://github.com/python-semantic-release/python-semantic-release/commit/d911fae993d41a8cb1497fa8b2a7e823576e0f22 +.. _PR#942: https://github.com/python-semantic-release/python-semantic-release/pull/942 + + +.. _changelog-v9.8.0: + +v9.8.0 (2024-05-27) +=================== + +✨ Features +----------- + +* Extend gitlab to edit a previous release if exists (`PR#934`_, `23e02b9`_) + +* **gha**: Configure ssh signed tags in GitHub Action, closes `#936`_ (`PR#937`_, `dfb76b9`_) + +* **hvcs-gitlab**: Enable gitlab to edit a previous release if found (`PR#934`_, `23e02b9`_) + +* **version-cmd**: Add toggle of ``--no-verify`` option to ``git commit`` (`PR#927`_, `1de6f78`_) + +🪲 Bug Fixes +------------ + +* **gitlab**: Adjust release name to mirror other hvcs release names (`PR#934`_, `23e02b9`_) + +* **hvcs-gitlab**: Add tag message to release creation (`PR#934`_, `23e02b9`_) + +📖 Documentation +---------------- + +* **configuration**: Add ``no_git_verify`` description to the configuration page (`PR#927`_, + `1de6f78`_) + +* **migration-v8**: Update version references in migration instructions (`PR#938`_, `d6ba16a`_) + +.. _#936: https://github.com/python-semantic-release/python-semantic-release/issues/936 +.. _1de6f78: https://github.com/python-semantic-release/python-semantic-release/commit/1de6f7834c6d37a74bc53f91609d40793556b52d +.. _23e02b9: https://github.com/python-semantic-release/python-semantic-release/commit/23e02b96dfb2a58f6b4ecf7b7812e4c1bc50573d +.. _d6ba16a: https://github.com/python-semantic-release/python-semantic-release/commit/d6ba16aa8e01bae1a022a9b06cd0b9162c51c345 +.. _dfb76b9: https://github.com/python-semantic-release/python-semantic-release/commit/dfb76b94b859a7f3fa3ad778eec7a86de2874d68 +.. _PR#927: https://github.com/python-semantic-release/python-semantic-release/pull/927 +.. _PR#934: https://github.com/python-semantic-release/python-semantic-release/pull/934 +.. _PR#937: https://github.com/python-semantic-release/python-semantic-release/pull/937 +.. _PR#938: https://github.com/python-semantic-release/python-semantic-release/pull/938 + + +.. _changelog-v9.7.3: + +v9.7.3 (2024-05-15) +=================== + +🪲 Bug Fixes +------------ + +* Enabled ``prelease-token`` parameter in github action (`PR#929`_, `1bb26b0`_) + +.. _1bb26b0: https://github.com/python-semantic-release/python-semantic-release/commit/1bb26b0762d94efd97c06a3f1b6b10fb76901f6d +.. _PR#929: https://github.com/python-semantic-release/python-semantic-release/pull/929 + + +.. _changelog-v9.7.2: + +v9.7.2 (2024-05-13) +=================== + +🪲 Bug Fixes +------------ + +* Enable user configuration of ``build_command`` env vars (`PR#925`_, `6b5b271`_) + +* **version**: Enable user config of ``build_command`` env variables, closes `#922`_ (`PR#925`_, + `6b5b271`_) + +📖 Documentation +---------------- + +* **configuration**: Clarify TOC & alphabetize configuration descriptions (`19add16`_) + +* **configuration**: Clarify TOC & standardize heading links (`3a41995`_) + +* **configuration**: Document ``build_command_env`` configuration option (`PR#925`_, `6b5b271`_) + +* **CONTRIBUTING**: Update build command definition for developers (`PR#921`_, `b573c4d`_) + +.. _#922: https://github.com/python-semantic-release/python-semantic-release/issues/922 +.. _19add16: https://github.com/python-semantic-release/python-semantic-release/commit/19add16dcfdfdb812efafe2d492a933d0856df1d +.. _3a41995: https://github.com/python-semantic-release/python-semantic-release/commit/3a4199542d0ea4dbf88fa35e11bec41d0c27dd17 +.. _6b5b271: https://github.com/python-semantic-release/python-semantic-release/commit/6b5b271453874b982fbf2827ec1f6be6db1c2cc7 +.. _b573c4d: https://github.com/python-semantic-release/python-semantic-release/commit/b573c4d4a2c212be9bdee918501bb5e046c6a806 +.. _PR#921: https://github.com/python-semantic-release/python-semantic-release/pull/921 +.. _PR#925: https://github.com/python-semantic-release/python-semantic-release/pull/925 + + +.. _changelog-v9.7.1: + +v9.7.1 (2024-05-07) +=================== + +🪲 Bug Fixes +------------ + +* **gha**: Fix missing ``git_committer_*`` definition in action, closes `#918`_ (`PR#919`_, + `ccef9d8`_) + +.. _#918: https://github.com/python-semantic-release/python-semantic-release/issues/918 +.. _ccef9d8: https://github.com/python-semantic-release/python-semantic-release/commit/ccef9d8521be12c0640369b3c3a80b81a7832662 +.. _PR#919: https://github.com/python-semantic-release/python-semantic-release/pull/919 + + +.. _changelog-v9.7.0: + +v9.7.0 (2024-05-06) +=================== + +✨ Features +----------- + +* **version-cmd**: Pass ``NEW_VERSION`` & useful env vars to build command (`ee6b246`_) + +🪲 Bug Fixes +------------ + +* **gha**: Add missing ``tag`` option to GitHub Action definition, closes `#906`_ (`PR#908`_, + `6b24288`_) + +* **gha**: Correct use of ``prerelease`` option for GitHub Action (`PR#914`_, `85e27b7`_) + +📖 Documentation +---------------- + +* **configuration**: Add description of build command available env variables (`c882dc6`_) + +* **gha**: Update GitHub Actions doc with all available options (`PR#914`_, `85e27b7`_) + +⚙️ Build System +---------------- + +* **deps**: Bump GitHub Action container to use ``python3.12``, closes `#801`_ (`PR#914`_, + `85e27b7`_) + +.. _#801: https://github.com/python-semantic-release/python-semantic-release/issues/801 +.. _#906: https://github.com/python-semantic-release/python-semantic-release/issues/906 +.. _6b24288: https://github.com/python-semantic-release/python-semantic-release/commit/6b24288a96302cd6982260e46fad128ec4940da9 +.. _85e27b7: https://github.com/python-semantic-release/python-semantic-release/commit/85e27b7f486e6b0e6cc9e85e101a97e676bc3d60 +.. _c882dc6: https://github.com/python-semantic-release/python-semantic-release/commit/c882dc62b860b2aeaa925c21d1524f4ae25ef567 +.. _ee6b246: https://github.com/python-semantic-release/python-semantic-release/commit/ee6b246df3bb211ab49c8bce075a4c3f6a68ed77 +.. _PR#908: https://github.com/python-semantic-release/python-semantic-release/pull/908 +.. _PR#914: https://github.com/python-semantic-release/python-semantic-release/pull/914 + + +.. _changelog-v9.6.0: + +v9.6.0 (2024-04-29) +=================== + +✨ Features +----------- + +* Changelog filters are specialized per vcs type (`PR#890`_, `76ed593`_) + +* **changelog**: Changelog filters are hvcs focused (`PR#890`_, `76ed593`_) + +* **changelog-context**: Add flag to jinja env for which hvcs is available (`PR#890`_, `76ed593`_) + +* **changelog-gitea**: Add issue url filter to changelog context (`PR#890`_, `76ed593`_) + +* **changelog-github**: Add issue url filter to changelog context (`PR#890`_, `76ed593`_) + +* **version-cmd**: Add ``--as-prerelease`` option to force the next version to be a prerelease, + closes `#639`_ (`PR#647`_, `2acb5ac`_) + +🪲 Bug Fixes +------------ + +* Correct version ``--prerelease`` use & enable ``--as-prerelease`` (`PR#647`_, `2acb5ac`_) + +* **github**: Correct changelog filter for pull request urls (`PR#890`_, `76ed593`_) + +* **parser-custom**: Gracefully handle custom parser import errors (`67f6038`_) + +* **version-cmd**: Correct ``--prerelease`` use, closes `#639`_ (`PR#647`_, `2acb5ac`_) + +📖 Documentation +---------------- + +* **changelog-context**: Explain new hvcs specific context filters (`PR#890`_, `76ed593`_) + +* **commands**: Update version command options definition about prereleases (`PR#647`_, `2acb5ac`_) + +.. _#639: https://github.com/python-semantic-release/python-semantic-release/issues/639 +.. _2acb5ac: https://github.com/python-semantic-release/python-semantic-release/commit/2acb5ac35ae79d7ae25ca9a03fb5c6a4a68b3673 +.. _67f6038: https://github.com/python-semantic-release/python-semantic-release/commit/67f60389e3f6e93443ea108c0e1b4d30126b8e06 +.. _76ed593: https://github.com/python-semantic-release/python-semantic-release/commit/76ed593ea33c851005994f0d1a6a33cc890fb908 +.. _PR#647: https://github.com/python-semantic-release/python-semantic-release/pull/647 +.. _PR#890: https://github.com/python-semantic-release/python-semantic-release/pull/890 + + +.. _changelog-v9.5.0: + +v9.5.0 (2024-04-23) +=================== + +✨ Features +----------- + +* Extend support to on-prem GitHub Enterprise Server (`PR#896`_, `4fcb737`_) + +* **github**: Extend support to on-prem GitHub Enterprise Server, closes `#895`_ (`PR#896`_, + `4fcb737`_) + +.. _#895: https://github.com/python-semantic-release/python-semantic-release/issues/895 +.. _4fcb737: https://github.com/python-semantic-release/python-semantic-release/commit/4fcb737958d95d1a3be24db7427e137b46f5075f +.. _PR#896: https://github.com/python-semantic-release/python-semantic-release/pull/896 + + +.. _changelog-v9.4.2: + +v9.4.2 (2024-04-14) +=================== + +🪲 Bug Fixes +------------ + +* **bitbucket**: Allow insecure http connections if configured (`PR#886`_, `db13438`_) + +* **bitbucket**: Correct url parsing & prevent double url schemes (`PR#676`_, `5cfdb24`_) + +* **config**: Add flag to allow insecure connections (`PR#886`_, `db13438`_) + +* **gitea**: Allow insecure http connections if configured (`PR#886`_, `db13438`_) + +* **gitea**: Correct url parsing & prevent double url schemes (`PR#676`_, `5cfdb24`_) + +* **github**: Allow insecure http connections if configured (`PR#886`_, `db13438`_) + +* **github**: Correct url parsing & prevent double url schemes (`PR#676`_, `5cfdb24`_) + +* **gitlab**: Allow insecure http connections if configured (`PR#886`_, `db13438`_) + +* **gitlab**: Correct url parsing & prevent double url schemes (`PR#676`_, `5cfdb24`_) + +* **hvcs**: Allow insecure http connections if configured (`PR#886`_, `db13438`_) + +* **hvcs**: Prevent double protocol scheme urls in changelogs (`PR#676`_, `5cfdb24`_) + +* **version-cmd**: Handle HTTP exceptions more gracefully (`PR#886`_, `db13438`_) + +📖 Documentation +---------------- + +* **configuration**: Update ``remote`` settings section with missing values, closes `#868`_ + (`PR#886`_, `db13438`_) + +⚙️ Build System +---------------- + +* **deps**: Update rich requirement from ~=12.5 to ~=13.0, closes `#888`_ (`PR#877`_, `4a22a8c`_) + +.. _#868: https://github.com/python-semantic-release/python-semantic-release/issues/868 +.. _#888: https://github.com/python-semantic-release/python-semantic-release/issues/888 +.. _4a22a8c: https://github.com/python-semantic-release/python-semantic-release/commit/4a22a8c1a69bcf7b1ddd6db56e6883c617a892b3 +.. _5cfdb24: https://github.com/python-semantic-release/python-semantic-release/commit/5cfdb248c003a2d2be5fe65fb61d41b0d4c45db5 +.. _db13438: https://github.com/python-semantic-release/python-semantic-release/commit/db1343890f7e0644bc8457f995f2bd62087513d3 +.. _PR#676: https://github.com/python-semantic-release/python-semantic-release/pull/676 +.. _PR#877: https://github.com/python-semantic-release/python-semantic-release/pull/877 +.. _PR#886: https://github.com/python-semantic-release/python-semantic-release/pull/886 + + +.. _changelog-v9.4.1: + +v9.4.1 (2024-04-06) +=================== + +🪲 Bug Fixes +------------ + +* **gh-actions-output**: Fixed trailing newline to match GITHUB_OUTPUT format (`PR#885`_, + `2c7b6ec`_) + +* **gh-actions-output**: Fixed trailing newline to match GITHUB_OUTPUT format, closes `#884`_ + (`PR#885`_, `2c7b6ec`_) + +.. _#884: https://github.com/python-semantic-release/python-semantic-release/issues/884 +.. _2c7b6ec: https://github.com/python-semantic-release/python-semantic-release/commit/2c7b6ec85b6e3182463d7b695ee48e9669a25b3b +.. _PR#885: https://github.com/python-semantic-release/python-semantic-release/pull/885 + + +.. _changelog-v9.4.0: + +v9.4.0 (2024-03-31) +=================== + +✨ Features +----------- + +* **gitea**: Derives gitea api domain from base domain when unspecified (`PR#675`_, `2ee3f8a`_) + +.. _2ee3f8a: https://github.com/python-semantic-release/python-semantic-release/commit/2ee3f8a918d2e5ea9ab64df88f52e62a1f589c38 +.. _PR#675: https://github.com/python-semantic-release/python-semantic-release/pull/675 + + +.. _changelog-v9.3.1: + +v9.3.1 (2024-03-24) +=================== + +🪲 Bug Fixes +------------ + +* **algorithm**: Handle merge-base errors gracefully, closes `#724`_ (`4c998b7`_) + +* **cli-version**: Change implementation to only push the tag we generated, closes `#803`_ + (`8a9da4f`_) + +⚡ Performance Improvements +--------------------------- + +* **algorithm**: Simplify logs & use lookup when searching for commit & tag match (`3690b95`_) + +.. _#724: https://github.com/python-semantic-release/python-semantic-release/issues/724 +.. _#803: https://github.com/python-semantic-release/python-semantic-release/issues/803 +.. _3690b95: https://github.com/python-semantic-release/python-semantic-release/commit/3690b9511de633ab38083de4d2505b6d05853346 +.. _4c998b7: https://github.com/python-semantic-release/python-semantic-release/commit/4c998b77a3fe5e12783d1ab2d47789a10b83f247 +.. _8a9da4f: https://github.com/python-semantic-release/python-semantic-release/commit/8a9da4feb8753e3ab9ea752afa25decd2047675a + + +.. _changelog-v9.3.0: + +v9.3.0 (2024-03-21) +=================== + +✨ Features +----------- + +* **cmd-version**: Changelog available to bundle (`PR#779`_, `37fdb28`_) + +* **cmd-version**: Create changelog prior to build enabling doc bundling (`PR#779`_, `37fdb28`_) + +.. _37fdb28: https://github.com/python-semantic-release/python-semantic-release/commit/37fdb28e0eb886d682b5dea4cc83a7c98a099422 +.. _PR#779: https://github.com/python-semantic-release/python-semantic-release/pull/779 + + +.. _changelog-v9.2.2: + +v9.2.2 (2024-03-19) +=================== + +🪲 Bug Fixes +------------ + +* **cli**: Enable subcommand help even if config is invalid, closes `#840`_ (`91d221a`_) + +.. _#840: https://github.com/python-semantic-release/python-semantic-release/issues/840 +.. _91d221a: https://github.com/python-semantic-release/python-semantic-release/commit/91d221a01266e5ca6de5c73296b0a90987847494 + + +.. _changelog-v9.2.1: + +v9.2.1 (2024-03-19) +=================== + +🪲 Bug Fixes +------------ + +* **parse-git-url**: Handle urls with url-safe special characters (`27cd93a`_) + +.. _27cd93a: https://github.com/python-semantic-release/python-semantic-release/commit/27cd93a0a65ee3787ca51be4c91c48f6ddb4269c + + +.. _changelog-v9.2.0: + +v9.2.0 (2024-03-18) +=================== + +✨ Features +----------- + +* **version**: Add new version print flags to display the last released version and tag (`814240c`_) + +* **version-config**: Add option to disable 0.x.x versions (`dedb3b7`_) + +🪲 Bug Fixes +------------ + +* **changelog**: Make sure default templates render ending in 1 newline (`0b4a45e`_) + +* **changelog-generation**: Fix incorrect release timezone determination (`f802446`_) + +📖 Documentation +---------------- + +* **configuration**: Add description of ``allow-zero-version`` configuration option (`4028f83`_) + +* **configuration**: Clarify the ``major_on_zero`` configuration option (`f7753cd`_) + +⚙️ Build System +---------------- + +* **deps**: Add click-option-group for grouping exclusive flags (`bd892b8`_) + +.. _0b4a45e: https://github.com/python-semantic-release/python-semantic-release/commit/0b4a45e3673d0408016dc8e7b0dce98007a763e3 +.. _4028f83: https://github.com/python-semantic-release/python-semantic-release/commit/4028f8384a0181c8d58c81ae81cf0b241a02a710 +.. _814240c: https://github.com/python-semantic-release/python-semantic-release/commit/814240c7355df95e9be9a6ed31d004b800584bc0 +.. _bd892b8: https://github.com/python-semantic-release/python-semantic-release/commit/bd892b89c26df9fccc9335c84e2b3217e3e02a37 +.. _dedb3b7: https://github.com/python-semantic-release/python-semantic-release/commit/dedb3b765c8530379af61d3046c3bb9c160d54e5 +.. _f7753cd: https://github.com/python-semantic-release/python-semantic-release/commit/f7753cdabd07e276bc001478d605fca9a4b37ec4 +.. _f802446: https://github.com/python-semantic-release/python-semantic-release/commit/f802446bd0693c4c9f6bdfdceae8b89c447827d2 + + +.. _changelog-v9.1.1: + +v9.1.1 (2024-02-25) +=================== + +🪲 Bug Fixes +------------ + +* **parse_git_url**: Fix bad url with dash (`1c25b8e`_) + +.. _1c25b8e: https://github.com/python-semantic-release/python-semantic-release/commit/1c25b8e6f1e43c15ca7d5a59dca0a13767f9bc33 + + +.. _changelog-v9.1.0: + +v9.1.0 (2024-02-14) +=================== + +✨ Features +----------- + +* Add bitbucket hvcs (`bbbbfeb`_) + +🪲 Bug Fixes +------------ + +* Remove unofficial environment variables (`a5168e4`_) + +📖 Documentation +---------------- + +* Add bitbucket authentication (`b78a387`_) + +* Add bitbucket to token table (`56f146d`_) + +* Fix typo (`b240e12`_) + +⚙️ Build System +---------------- + +* **deps**: Bump minimum required ``tomlkit`` to ``>=0.11.0``, closes `#834`_ (`291aace`_) + +.. _#834: https://github.com/python-semantic-release/python-semantic-release/issues/834 +.. _291aace: https://github.com/python-semantic-release/python-semantic-release/commit/291aacea1d0429a3b27e92b0a20b598f43f6ea6b +.. _56f146d: https://github.com/python-semantic-release/python-semantic-release/commit/56f146d9f4c0fc7f2a84ad11b21c8c45e9221782 +.. _a5168e4: https://github.com/python-semantic-release/python-semantic-release/commit/a5168e40b9a14dbd022f62964f382b39faf1e0df +.. _b240e12: https://github.com/python-semantic-release/python-semantic-release/commit/b240e129b180d45c1d63d464283b7dfbcb641d0c +.. _b78a387: https://github.com/python-semantic-release/python-semantic-release/commit/b78a387d8eccbc1a6a424a183254fc576126199c +.. _bbbbfeb: https://github.com/python-semantic-release/python-semantic-release/commit/bbbbfebff33dd24b8aed2d894de958d532eac596 + + +.. _changelog-v9.0.3: + +v9.0.3 (2024-02-08) +=================== + +🪲 Bug Fixes +------------ + +* **algorithm**: Correct bfs to not abort on previously visited node (`02df305`_) + +⚡ Performance Improvements +--------------------------- + +* **algorithm**: Refactor bfs search to use queue rather than recursion (`8b742d3`_) + +.. _02df305: https://github.com/python-semantic-release/python-semantic-release/commit/02df305db43abfc3a1f160a4a52cc2afae5d854f +.. _8b742d3: https://github.com/python-semantic-release/python-semantic-release/commit/8b742d3db6652981a7b5f773a74b0534edc1fc15 + + +.. _changelog-v9.0.2: + +v9.0.2 (2024-02-08) +=================== + +🪲 Bug Fixes +------------ + +* **util**: Properly parse windows line-endings in commit messages, closes `#820`_ (`70193ba`_) + +📖 Documentation +---------------- + +* Remove duplicate note in configuration.rst (`PR#807`_, `fb6f243`_) + +.. _#820: https://github.com/python-semantic-release/python-semantic-release/issues/820 +.. _70193ba: https://github.com/python-semantic-release/python-semantic-release/commit/70193ba117c1a6d3690aed685fee8a734ba174e5 +.. _fb6f243: https://github.com/python-semantic-release/python-semantic-release/commit/fb6f243a141642c02469f1080180ecaf4f3cec66 +.. _PR#807: https://github.com/python-semantic-release/python-semantic-release/pull/807 + + +.. _changelog-v9.0.1: + +v9.0.1 (2024-02-06) +=================== + +🪲 Bug Fixes +------------ + +* **config**: Set commit parser opt defaults based on parser choice (`PR#782`_, `9c594fb`_) + +.. _9c594fb: https://github.com/python-semantic-release/python-semantic-release/commit/9c594fb6efac7e4df2b0bfbd749777d3126d03d7 +.. _PR#782: https://github.com/python-semantic-release/python-semantic-release/pull/782 + + +.. _changelog-v9.0.0: + +v9.0.0 (2024-02-06) +=================== + +♻️ Refactoring +--------------- + +* Drop support for Python 3.7 (`PR#828`_, `ad086f5`_) + +💥 BREAKING CHANGES +-------------------- + +* Removed Python 3.7 specific control flows and made more modern implementations the default + control flow without a bypass or workaround. Will break on Python 3.7 now. If you require Python + 3.7, you should lock your major version at v8. Since we only have enough manpower to maintain the + latest major release, unfortunately there will not be any more updates to v8. + +* We decided to remove support for Python 3.7 because it has been officially deprecated by the + Python Foundation over a year ago and our codebase is starting to have limitations and custom + implementations just to maintain support for 3.7. + +.. _ad086f5: https://github.com/python-semantic-release/python-semantic-release/commit/ad086f5993ae4741d6e20fee618d1bce8df394fb +.. _PR#828: https://github.com/python-semantic-release/python-semantic-release/pull/828 + + +.. _changelog-v8.7.2: + +v8.7.2 (2024-01-03) +=================== + +🪲 Bug Fixes +------------ + +* **lint**: Correct linter errors (`c9556b0`_) + +.. _c9556b0: https://github.com/python-semantic-release/python-semantic-release/commit/c9556b0ca6df6a61e9ce909d18bc5be8b6154bf8 + + +.. _changelog-v8.7.1: + +v8.7.1 (2024-01-03) +=================== + +🪲 Bug Fixes +------------ + +* **cli-generate-config**: Ensure configuration types are always toml parsable (`PR#785`_, + `758e649`_) + +📖 Documentation +---------------- + +* Add note on default envvar behaviour (`PR#780`_, `0b07cae`_) + +* **configuration**: Change defaults definition of token default to table (`PR#786`_, `df1df0d`_) + +* **contributing**: Add docs-build, testing conf, & build instructions (`PR#787`_, `011b072`_) + +.. _011b072: https://github.com/python-semantic-release/python-semantic-release/commit/011b0729cba3045b4e7291fd970cb17aad7bae60 +.. _0b07cae: https://github.com/python-semantic-release/python-semantic-release/commit/0b07cae71915c5c82d7784898b44359249542a64 +.. _758e649: https://github.com/python-semantic-release/python-semantic-release/commit/758e64975fe46b961809f35977574729b7c44271 +.. _df1df0d: https://github.com/python-semantic-release/python-semantic-release/commit/df1df0de8bc655cbf8f86ae52aff10efdc66e6d2 +.. _PR#780: https://github.com/python-semantic-release/python-semantic-release/pull/780 +.. _PR#785: https://github.com/python-semantic-release/python-semantic-release/pull/785 +.. _PR#786: https://github.com/python-semantic-release/python-semantic-release/pull/786 +.. _PR#787: https://github.com/python-semantic-release/python-semantic-release/pull/787 + + +.. _changelog-v8.7.0: + +v8.7.0 (2023-12-22) +=================== + +✨ Features +----------- + +* **config**: Enable default environment token per hvcs (`PR#774`_, `26528eb`_) + +.. _26528eb: https://github.com/python-semantic-release/python-semantic-release/commit/26528eb8794d00dfe985812269702fbc4c4ec788 +.. _PR#774: https://github.com/python-semantic-release/python-semantic-release/pull/774 + + +.. _changelog-v8.6.0: + +v8.6.0 (2023-12-22) +=================== + +✨ Features +----------- + +* **utils**: Expand parsable valid git remote url formats (`PR#771`_, `cf75f23`_) + +📖 Documentation +---------------- + +* Minor correction to commit-parsing documentation (`PR#777`_, `245e878`_) + +.. _245e878: https://github.com/python-semantic-release/python-semantic-release/commit/245e878f02d5cafec6baf0493c921c1e396b56e8 +.. _cf75f23: https://github.com/python-semantic-release/python-semantic-release/commit/cf75f237360488ebb0088e5b8aae626e97d9cbdd +.. _PR#771: https://github.com/python-semantic-release/python-semantic-release/pull/771 +.. _PR#777: https://github.com/python-semantic-release/python-semantic-release/pull/777 + + +.. _changelog-v8.5.2: + +v8.5.2 (2023-12-19) +=================== + +🪲 Bug Fixes +------------ + +* **cli**: Gracefully output configuration validation errors (`PR#772`_, `e8c9d51`_) + +.. _e8c9d51: https://github.com/python-semantic-release/python-semantic-release/commit/e8c9d516c37466a5dce75a73766d5be0f9e74627 +.. _PR#772: https://github.com/python-semantic-release/python-semantic-release/pull/772 + + +.. _changelog-v8.5.1: + +v8.5.1 (2023-12-12) +=================== + +🪲 Bug Fixes +------------ + +* **cmd-version**: Handle committing of git-ignored file gracefully (`PR#764`_, `ea89fa7`_) + +* **config**: Cleanly handle repository in detached HEAD state (`PR#765`_, `ac4f9aa`_) + +* **config**: Gracefully fail when repo is in a detached HEAD state (`PR#765`_, `ac4f9aa`_) + +* **version**: Only commit non git-ignored files during version commit (`PR#764`_, `ea89fa7`_) + +📖 Documentation +---------------- + +* **configuration**: Adjust wording and improve clarity (`PR#766`_, `6b2fc8c`_) + +* **configuration**: Fix typo in text (`PR#766`_, `6b2fc8c`_) + +.. _6b2fc8c: https://github.com/python-semantic-release/python-semantic-release/commit/6b2fc8c156e122ee1b43fdb513b2dc3b8fd76724 +.. _ac4f9aa: https://github.com/python-semantic-release/python-semantic-release/commit/ac4f9aacb72c99f2479ae33369822faad011a824 +.. _ea89fa7: https://github.com/python-semantic-release/python-semantic-release/commit/ea89fa72885e15da91687172355426a22c152513 +.. _PR#764: https://github.com/python-semantic-release/python-semantic-release/pull/764 +.. _PR#765: https://github.com/python-semantic-release/python-semantic-release/pull/765 +.. _PR#766: https://github.com/python-semantic-release/python-semantic-release/pull/766 + + +.. _changelog-v8.5.0: + +v8.5.0 (2023-12-07) +=================== + +✨ Features +----------- + +* Allow template directories to contain a '.' at the top-level (`PR#762`_, `07b232a`_) + +.. _07b232a: https://github.com/python-semantic-release/python-semantic-release/commit/07b232a3b34be0b28c6af08aea4852acb1b9bd56 +.. _PR#762: https://github.com/python-semantic-release/python-semantic-release/pull/762 + + +.. _changelog-v8.4.0: + +v8.4.0 (2023-12-07) +=================== + +✨ Features +----------- + +* **cmd-version**: Add ``--tag/--no-tag`` option to version command (`PR#752`_, `de6b9ad`_) + +* **version**: Add ``--no-tag`` option to turn off tag creation (`PR#752`_, `de6b9ad`_) + +🪲 Bug Fixes +------------ + +* **version**: Separate push tags from commit push when not committing changes (`PR#752`_, + `de6b9ad`_) + +📖 Documentation +---------------- + +* **commands**: Update ``version`` subcommand options (`PR#752`_, `de6b9ad`_) + +* **migration**: Fix comments about publish command (`PR#747`_, `90380d7`_) + +.. _90380d7: https://github.com/python-semantic-release/python-semantic-release/commit/90380d797a734dcca5040afc5fa00e3e01f64152 +.. _de6b9ad: https://github.com/python-semantic-release/python-semantic-release/commit/de6b9ad921e697b5ea2bb2ea8f180893cecca920 +.. _PR#747: https://github.com/python-semantic-release/python-semantic-release/pull/747 +.. _PR#752: https://github.com/python-semantic-release/python-semantic-release/pull/752 + + +.. _changelog-v8.3.0: + +v8.3.0 (2023-10-23) +=================== + +✨ Features +----------- + +* **action**: Use composite action for semantic release (`PR#692`_, `4648d87`_) + +.. _4648d87: https://github.com/python-semantic-release/python-semantic-release/commit/4648d87bac8fb7e6cc361b765b4391b30a8caef8 +.. _PR#692: https://github.com/python-semantic-release/python-semantic-release/pull/692 + + +.. _changelog-v8.2.0: + +v8.2.0 (2023-10-23) +=================== + +✨ Features +----------- + +* Allow user customization of release notes template (`PR#736`_, `94a1311`_) + +📖 Documentation +---------------- + +* Add PYTHONPATH mention for commit parser (`3284258`_) + +.. _3284258: https://github.com/python-semantic-release/python-semantic-release/commit/3284258b9fa1a3fe165f336181aff831d50fddd3 +.. _94a1311: https://github.com/python-semantic-release/python-semantic-release/commit/94a131167e1b867f8bc112a042b9766e050ccfd1 +.. _PR#736: https://github.com/python-semantic-release/python-semantic-release/pull/736 + + +.. _changelog-v8.1.2: + +v8.1.2 (2023-10-13) +=================== + +🪲 Bug Fixes +------------ + +* Correct lint errors (`a13a6c3`_) + +* Error when running build command on windows systems (`PR#732`_, `2553657`_) + +.. _2553657: https://github.com/python-semantic-release/python-semantic-release/commit/25536574760b407410f435441da533fafbf94402 +.. _a13a6c3: https://github.com/python-semantic-release/python-semantic-release/commit/a13a6c37e180dc422599939a5725835306c18ff2 +.. _PR#732: https://github.com/python-semantic-release/python-semantic-release/pull/732 + + +.. _changelog-v8.1.1: + +v8.1.1 (2023-09-19) +=================== + +🪲 Bug Fixes +------------ + +* Attribute error when logging non-strings (`PR#711`_, `75e6e48`_) + +.. _75e6e48: https://github.com/python-semantic-release/python-semantic-release/commit/75e6e48129da8238a62d5eccac1ae55d0fee0f9f +.. _PR#711: https://github.com/python-semantic-release/python-semantic-release/pull/711 + + +.. _changelog-v8.1.0: + +v8.1.0 (2023-09-19) +=================== + +✨ Features +----------- + +* Upgrade pydantic to v2 (`PR#714`_, `5a5c5d0`_) + +📖 Documentation +---------------- + +* Fix typos (`PR#708`_, `2698b0e`_) + +* Update project urls (`PR#715`_, `5fd5485`_) + +.. _2698b0e: https://github.com/python-semantic-release/python-semantic-release/commit/2698b0e006ff7e175430b98450ba248ed523b341 +.. _5a5c5d0: https://github.com/python-semantic-release/python-semantic-release/commit/5a5c5d0ee347750d7c417c3242d52e8ada50b217 +.. _5fd5485: https://github.com/python-semantic-release/python-semantic-release/commit/5fd54856dfb6774feffc40d36d5bb0f421f04842 +.. _PR#708: https://github.com/python-semantic-release/python-semantic-release/pull/708 +.. _PR#714: https://github.com/python-semantic-release/python-semantic-release/pull/714 +.. _PR#715: https://github.com/python-semantic-release/python-semantic-release/pull/715 + + +.. _changelog-v8.0.8: + +v8.0.8 (2023-08-26) +=================== + +🪲 Bug Fixes +------------ + +* Dynamic_import() import path split (`PR#686`_, `1007a06`_) + +.. _1007a06: https://github.com/python-semantic-release/python-semantic-release/commit/1007a06d1e16beef6d18f44ff2e0e09921854b54 +.. _PR#686: https://github.com/python-semantic-release/python-semantic-release/pull/686 + + +.. _changelog-v8.0.7: + +v8.0.7 (2023-08-16) +=================== + +🪲 Bug Fixes +------------ + +* Use correct upload url for github (`PR#661`_, `8a515ca`_) + +.. _8a515ca: https://github.com/python-semantic-release/python-semantic-release/commit/8a515caf1f993aa653e024beda2fdb9e629cc42a +.. _PR#661: https://github.com/python-semantic-release/python-semantic-release/pull/661 + + +.. _changelog-v8.0.6: + +v8.0.6 (2023-08-13) +=================== + +🪲 Bug Fixes +------------ + +* **publish**: Improve error message when no tags found (`PR#683`_, `bdc06ea`_) + +.. _bdc06ea: https://github.com/python-semantic-release/python-semantic-release/commit/bdc06ea061c19134d5d74bd9f168700dd5d9bcf5 +.. _PR#683: https://github.com/python-semantic-release/python-semantic-release/pull/683 + + +.. _changelog-v8.0.5: + +v8.0.5 (2023-08-10) +=================== + +🪲 Bug Fixes +------------ + +* Don't warn about vcs token if ignore_token_for_push is true. (`PR#670`_, `f1a54a6`_) + +📖 Documentation +---------------- + +* ``password`` should be ``token``. (`PR#670`_, `f1a54a6`_) + +* Fix typo missing 's' in version_variable[s] in configuration.rst (`PR#668`_, `879186a`_) + +.. _879186a: https://github.com/python-semantic-release/python-semantic-release/commit/879186aa09a3bea8bbe2b472f892cf7c0712e557 +.. _f1a54a6: https://github.com/python-semantic-release/python-semantic-release/commit/f1a54a6c9a05b225b6474d50cd610eca19ec0c34 +.. _PR#668: https://github.com/python-semantic-release/python-semantic-release/pull/668 +.. _PR#670: https://github.com/python-semantic-release/python-semantic-release/pull/670 + + +.. _changelog-v8.0.4: + +v8.0.4 (2023-07-26) +=================== + +🪲 Bug Fixes +------------ + +* **changelog**: Use version as semver tag by default (`PR#653`_, `5984c77`_) + +📖 Documentation +---------------- + +* Add Python 3.11 to classifiers in metadata (`PR#651`_, `5a32a24`_) + +* Clarify usage of assets config option (`PR#655`_, `efa2b30`_) + +.. _5984c77: https://github.com/python-semantic-release/python-semantic-release/commit/5984c7771edc37f0d7d57894adecc2591efc414d +.. _5a32a24: https://github.com/python-semantic-release/python-semantic-release/commit/5a32a24bf4128c39903f0c5d3bd0cb1ccba57e18 +.. _efa2b30: https://github.com/python-semantic-release/python-semantic-release/commit/efa2b3019b41eb427f0e1c8faa21ad10664295d0 +.. _PR#651: https://github.com/python-semantic-release/python-semantic-release/pull/651 +.. _PR#653: https://github.com/python-semantic-release/python-semantic-release/pull/653 +.. _PR#655: https://github.com/python-semantic-release/python-semantic-release/pull/655 + + +.. _changelog-v8.0.3: + +v8.0.3 (2023-07-21) +=================== + +🪲 Bug Fixes +------------ + +* Skip unparseable versions when calculating next version (`PR#649`_, `88f25ea`_) + +.. _88f25ea: https://github.com/python-semantic-release/python-semantic-release/commit/88f25eae62589cdf53dbc3dfcb167a3ae6cba2d3 +.. _PR#649: https://github.com/python-semantic-release/python-semantic-release/pull/649 + + +.. _changelog-v8.0.2: + +v8.0.2 (2023-07-18) +=================== + +🪲 Bug Fixes +------------ + +* Handle missing configuration (`PR#644`_, `f15753c`_) + +📖 Documentation +---------------- + +* Better description for tag_format usage (`2129b72`_) + +* Clarify v8 breaking changes in GitHub action inputs (`PR#643`_, `cda050c`_) + +* Correct version_toml example in migrating_from_v7.rst (`PR#641`_, `325d5e0`_) + +.. _2129b72: https://github.com/python-semantic-release/python-semantic-release/commit/2129b729837eccc41a33dbb49785a8a30ce6b187 +.. _325d5e0: https://github.com/python-semantic-release/python-semantic-release/commit/325d5e048bd89cb2a94c47029d4878b27311c0f0 +.. _cda050c: https://github.com/python-semantic-release/python-semantic-release/commit/cda050cd9e789d81458157ee240ff99ec65c6f25 +.. _f15753c: https://github.com/python-semantic-release/python-semantic-release/commit/f15753ce652f36cc03b108c667a26ab74bcbf95d +.. _PR#641: https://github.com/python-semantic-release/python-semantic-release/pull/641 +.. _PR#643: https://github.com/python-semantic-release/python-semantic-release/pull/643 +.. _PR#644: https://github.com/python-semantic-release/python-semantic-release/pull/644 + + +.. _changelog-v8.0.1: + +v8.0.1 (2023-07-17) +=================== + +🪲 Bug Fixes +------------ + +* Invalid version in Git history should not cause a release failure (`PR#632`_, `254430b`_) + +📖 Documentation +---------------- + +* Reduce readthedocs formats and add entries to migration from v7 guide (`9b6ddfe`_) + +* **migration**: Fix hyperlink (`PR#631`_, `5fbd52d`_) + +.. _254430b: https://github.com/python-semantic-release/python-semantic-release/commit/254430b5cc5f032016b4c73168f0403c4d87541e +.. _5fbd52d: https://github.com/python-semantic-release/python-semantic-release/commit/5fbd52d7de4982b5689651201a0e07b445158645 +.. _9b6ddfe: https://github.com/python-semantic-release/python-semantic-release/commit/9b6ddfef448f9de30fa2845034f76655d34a9912 +.. _PR#631: https://github.com/python-semantic-release/python-semantic-release/pull/631 +.. _PR#632: https://github.com/python-semantic-release/python-semantic-release/pull/632 + + +.. _changelog-v8.0.0: + +v8.0.0 (2023-07-16) +=================== + +✨ Features +----------- + +* **publish-cmd**: Add ``--post-to-release-tag`` option to control where to publish (`PR#619`_, + `ec30564`_) + +* Make it easier to access commit messages in ParsedCommits (`PR#619`_, `ec30564`_) + +* Remove publication of dists to artifact repository (`PR#619`_, `ec30564`_) + +* Rename 'upload' configuration section to 'publish' (`PR#619`_, `ec30564`_) + +* **github-action**: Add GitHub Actions output variables (`PR#619`_, `ec30564`_) + +* **version-cmd**: Add ``--skip-build`` option (`PR#619`_, `ec30564`_) + +* **version-cmd** Add ``--strict`` version mode (`PR#619`_, `ec30564`_) + +🪲 Bug Fixes +------------ + +* Add logging for token auth, use token for push (`PR#619`_, `ec30564`_) + +* Caching for repo owner and name (`PR#619`_, `ec30564`_) + +* Correct assets type in configuration (`PR#619`_, `ec30564`_) + +* Correct assets type-annotation for RuntimeContext (`PR#619`_, `ec30564`_) + +* Correct Dockerfile CLI command and GHA fetch (`PR#619`_, `ec30564`_) + +* Correct handling of build commands (`PR#619`_, `ec30564`_) + +* Correct logic for generating release notes (`PR#619`_, `ec30564`_) + +* Create_or_update_release for Gitlab hvcs (`PR#619`_, `ec30564`_) + +* Make additional attributes available for template authors (`PR#619`_, `ec30564`_) + +* Only call Github Action output callback once defaults are set (`PR#619`_, `ec30564`_) + +* Remove commit amending behaviour (`PR#619`_, `ec30564`_) + +* Resolve branch checkout logic in GHA (`PR#619`_, `ec30564`_) + +* Resolve bug in changelog logic, enable upload to pypi (`PR#619`_, `ec30564`_) + +* Resolve loss of tag_format configuration (`PR#619`_, `ec30564`_) + +* **github-action**: Pin Debian version in Dockerfile (`PR#619`_, `ec30564`_) + +* **github-action**: Correct input parsing (`PR#619`_, `ec30564`_) + +* **github-action**: Mark container fs as safe for git to operate on (`PR#619`_, `ec30564`_) + +* **github-action**: Quotation for git config command (`PR#619`_, `ec30564`_) + +* **github-action**: Remove default for 'force' (`PR#619`_, `ec30564`_) + +📖 Documentation +---------------- + +* Convert to Furo theme (`PR#619`_, `ec30564`_) + +* Fix typo (`PR#619`_, `ec30564`_) + +* Remove reference to dist publication (`PR#619`_, `ec30564`_) + +* Update docs with additional required permissions (`PR#619`_, `ec30564`_) + +* **changelog-templates**: fix typo (`PR#619`_, `ec30564`_) + +♻️ Refactoring +--------------- + +* Remove verify-ci command (`PR#619`_, `ec30564`_) + +💥 BREAKING CHANGES +-------------------- + +* numerous breaking changes, see :ref:`migrating-from-v7` for more information + +.. _ec30564: https://github.com/python-semantic-release/python-semantic-release/commit/ec30564b4ec732c001d76d3c09ba033066d2b6fe +.. _PR#619: https://github.com/python-semantic-release/python-semantic-release/pull/619 + + +.. _changelog-v7.34.6: + +v7.34.6 (2023-06-17) +==================== + +🪲 Bug Fixes +------------ + +* Relax invoke dependency constraint (`18ea200`_) + +.. _18ea200: https://github.com/python-semantic-release/python-semantic-release/commit/18ea200633fd67e07f3d4121df5aa4c6dd29d154 + + +.. _changelog-v7.34.5: + +v7.34.5 (2023-06-17) +==================== + +🪲 Bug Fixes +------------ + +* Consider empty commits (`PR#608`_, `6f2e890`_) + +.. _6f2e890: https://github.com/python-semantic-release/python-semantic-release/commit/6f2e8909636595d3cb5e858f42c63820cda45974 +.. _PR#608: https://github.com/python-semantic-release/python-semantic-release/pull/608 + + +.. _changelog-v7.34.4: + +v7.34.4 (2023-06-15) +==================== + +🪲 Bug Fixes +------------ + +* Docker build fails installing git (`PR#605`_, `9e3eb97`_) + +.. _9e3eb97: https://github.com/python-semantic-release/python-semantic-release/commit/9e3eb979783bc39ca564c2967c6c77eecba682e6 +.. _PR#605: https://github.com/python-semantic-release/python-semantic-release/pull/605 + + +.. _changelog-v7.34.3: + +v7.34.3 (2023-06-01) +==================== + +🪲 Bug Fixes +------------ + +* Generate markdown linter compliant changelog headers & lists (`PR#597`_, `cc87400`_) + +.. _cc87400: https://github.com/python-semantic-release/python-semantic-release/commit/cc87400d4a823350de7d02dc3172d2488c9517db +.. _PR#597: https://github.com/python-semantic-release/python-semantic-release/pull/597 + + +.. _changelog-v7.34.2: + +v7.34.2 (2023-05-29) +==================== + +🪲 Bug Fixes +------------ + +* Open all files with explicit utf-8 encoding (`PR#596`_, `cb71f35`_) + +.. _cb71f35: https://github.com/python-semantic-release/python-semantic-release/commit/cb71f35c26c1655e675fa735fa880d39a2c8af9c +.. _PR#596: https://github.com/python-semantic-release/python-semantic-release/pull/596 + + +.. _changelog-v7.34.1: + +v7.34.1 (2023-05-28) +==================== + +🪲 Bug Fixes +------------ + +* Generate markdown linter compliant changelog headers & lists (`PR#594`_, `9d9d403`_) + +.. _9d9d403: https://github.com/python-semantic-release/python-semantic-release/commit/9d9d40305c499c907335abe313e3ed122db0b154 +.. _PR#594: https://github.com/python-semantic-release/python-semantic-release/pull/594 + + +.. _changelog-v7.34.0: + +v7.34.0 (2023-05-28) +==================== + +✨ Features +----------- + +* Add option to only parse commits for current working directory (`PR#509`_, `cdf8116`_) + +.. _cdf8116: https://github.com/python-semantic-release/python-semantic-release/commit/cdf8116c1e415363b10a01f541873e04ad874220 +.. _PR#509: https://github.com/python-semantic-release/python-semantic-release/pull/509 + + +.. _changelog-v7.33.5: + +v7.33.5 (2023-05-19) +==================== + +🪲 Bug Fixes +------------ + +* Update docs and default config for gitmoji changes (`PR#590`_, `192da6e`_) + +* Update sphinx dep (`PR#590`_, `192da6e`_) + +📖 Documentation +---------------- + +* Update broken badge and add links (`PR#591`_, `0c23447`_) + +.. _0c23447: https://github.com/python-semantic-release/python-semantic-release/commit/0c234475d27ad887b19170c82deb80293b3a95f1 +.. _192da6e: https://github.com/python-semantic-release/python-semantic-release/commit/192da6e1352298b48630423d50191070a1c5ab24 +.. _PR#590: https://github.com/python-semantic-release/python-semantic-release/pull/590 +.. _PR#591: https://github.com/python-semantic-release/python-semantic-release/pull/591 + + +.. _changelog-v7.33.4: + +v7.33.4 (2023-05-14) +==================== + +🪲 Bug Fixes +------------ + +* If prerelease, publish prerelease (`PR#587`_, `927da9f`_) + +.. _927da9f: https://github.com/python-semantic-release/python-semantic-release/commit/927da9f8feb881e02bc08b33dc559bd8e7fc41ab +.. _PR#587: https://github.com/python-semantic-release/python-semantic-release/pull/587 + + +.. _changelog-v7.33.3: + +v7.33.3 (2023-04-24) +==================== + +🪲 Bug Fixes +------------ + +* Trim emojis from config (`PR#583`_, `02902f7`_) + +* Update Gitmojis according to official node module (`PR#582`_, `806fcfa`_) + +📖 Documentation +---------------- + +* Grammar in ``docs/troubleshooting.rst`` (`PR#557`_, `bbe754a`_) + +* Spelling and grammar in ``travis.rst`` (`PR#556`_, `3a76e9d`_) + +* Update repository name (`PR#559`_, `5cdb05e`_) + +.. _02902f7: https://github.com/python-semantic-release/python-semantic-release/commit/02902f73ee961565c2470c000f00947d9ef06cb1 +.. _3a76e9d: https://github.com/python-semantic-release/python-semantic-release/commit/3a76e9d7505c421009eb3e953c32cccac2e70e07 +.. _5cdb05e: https://github.com/python-semantic-release/python-semantic-release/commit/5cdb05e20f17b12890e1487c42d317dcbadd06c8 +.. _806fcfa: https://github.com/python-semantic-release/python-semantic-release/commit/806fcfa4cfdd3df4b380afd015a68dc90d54215a +.. _bbe754a: https://github.com/python-semantic-release/python-semantic-release/commit/bbe754a3db9ce7132749e7902fe118b52f48ee42 +.. _PR#556: https://github.com/python-semantic-release/python-semantic-release/pull/556 +.. _PR#557: https://github.com/python-semantic-release/python-semantic-release/pull/557 +.. _PR#559: https://github.com/python-semantic-release/python-semantic-release/pull/559 +.. _PR#582: https://github.com/python-semantic-release/python-semantic-release/pull/582 +.. _PR#583: https://github.com/python-semantic-release/python-semantic-release/pull/583 + + +.. _changelog-v7.33.2: + +v7.33.2 (2023-02-17) +==================== + +🪲 Bug Fixes +------------ + +* Inconsistent versioning between print-version and publish (`PR#524`_, `17d60e9`_) + +.. _17d60e9: https://github.com/python-semantic-release/python-semantic-release/commit/17d60e9bf66f62e5845065486c9d5e450f74839a +.. _PR#524: https://github.com/python-semantic-release/python-semantic-release/pull/524 + + +.. _changelog-v7.33.1: + +v7.33.1 (2023-02-01) +==================== + +🪲 Bug Fixes +------------ + +* **action**: Mark container fs as safe for git (`PR#552`_, `2a55f68`_) + +.. _2a55f68: https://github.com/python-semantic-release/python-semantic-release/commit/2a55f68e2b3cb9ffa9204c00ddbf12706af5c070 +.. _PR#552: https://github.com/python-semantic-release/python-semantic-release/pull/552 + + +.. _changelog-v7.33.0: + +v7.33.0 (2023-01-15) +==================== + +✨ Features +----------- + +* Add signing options to action (`31ad5eb`_) + +* Update action with configuration options (`PR#518`_, `4664afe`_) + +* **repository**: Add support for TWINE_CERT, closes `#521`_ (`PR#522`_, `d56e85d`_) + +🪲 Bug Fixes +------------ + +* Changelog release commit search logic (`PR#530`_, `efb3410`_) + +* **github-actions**: Bump Dockerfile to use Python 3.10 image, closes `#533`_ (`PR#536`_, + `8f2185d`_) + +* **action**: Fix environment variable names (`3c66218`_) + +📖 Documentation +---------------- + +* Update documentaton (`5cbdad2`_) + +.. _#521: https://github.com/python-semantic-release/python-semantic-release/issues/521 +.. _#533: https://github.com/python-semantic-release/python-semantic-release/issues/533 +.. _31ad5eb: https://github.com/python-semantic-release/python-semantic-release/commit/31ad5eb5a25f0ea703afc295351104aefd66cac1 +.. _3c66218: https://github.com/python-semantic-release/python-semantic-release/commit/3c66218640044adf263fcf9b2714cfc4b99c2e90 +.. _4664afe: https://github.com/python-semantic-release/python-semantic-release/commit/4664afe5f80a04834e398fefb841b166a51d95b7 +.. _5cbdad2: https://github.com/python-semantic-release/python-semantic-release/commit/5cbdad296034a792c9bf05e3700eac4f847eb469 +.. _8f2185d: https://github.com/python-semantic-release/python-semantic-release/commit/8f2185d570b3966b667ac591ae523812e9d2e00f +.. _d56e85d: https://github.com/python-semantic-release/python-semantic-release/commit/d56e85d1f2ac66fb0b59af2178164ca915dbe163 +.. _efb3410: https://github.com/python-semantic-release/python-semantic-release/commit/efb341036196c39b4694ca4bfa56c6b3e0827c6c +.. _PR#518: https://github.com/python-semantic-release/python-semantic-release/pull/518 +.. _PR#522: https://github.com/python-semantic-release/python-semantic-release/pull/522 +.. _PR#530: https://github.com/python-semantic-release/python-semantic-release/pull/530 +.. _PR#536: https://github.com/python-semantic-release/python-semantic-release/pull/536 +.. _PR#541: https://github.com/python-semantic-release/python-semantic-release/pull/541 + + +.. _changelog-v7.32.2: + +v7.32.2 (2022-10-22) +==================== + +🪲 Bug Fixes +------------ + +* Fix changelog generation in tag-mode (`PR#171`_, `482a62e`_) + +📖 Documentation +---------------- + +* Fix code blocks (`PR#506`_, `24b7673`_) + +.. _24b7673: https://github.com/python-semantic-release/python-semantic-release/commit/24b767339fcef1c843f7dd3188900adab05e03b1 +.. _482a62e: https://github.com/python-semantic-release/python-semantic-release/commit/482a62ec374208b2d57675cb0b7f0ab9695849b9 +.. _PR#171: https://github.com/python-semantic-release/python-semantic-release/pull/171 +.. _PR#506: https://github.com/python-semantic-release/python-semantic-release/pull/506 + + +.. _changelog-v7.32.1: + +v7.32.1 (2022-10-07) +==================== + +🪲 Bug Fixes +------------ + +* Corrections for deprecation warnings (`PR#505`_, `d47afb6`_) + +📖 Documentation +---------------- + +* Correct spelling mistakes (`PR#504`_, `3717e0d`_) + +.. _3717e0d: https://github.com/python-semantic-release/python-semantic-release/commit/3717e0d8810f5d683847c7b0e335eeefebbf2921 +.. _d47afb6: https://github.com/python-semantic-release/python-semantic-release/commit/d47afb6516238939e174f946977bf4880062a622 +.. _PR#504: https://github.com/python-semantic-release/python-semantic-release/pull/504 +.. _PR#505: https://github.com/python-semantic-release/python-semantic-release/pull/505 + + +.. _changelog-v7.32.0: + +v7.32.0 (2022-09-25) +==================== + +✨ Features +----------- + +* Add setting for enforcing textual changelog sections, closes `#498`_ (`PR#502`_, `988437d`_) + +📖 Documentation +---------------- + +* Correct documented default behaviour for ``commit_version_number`` (`PR#497`_, `ffae2dc`_) + +.. _#498: https://github.com/python-semantic-release/python-semantic-release/issues/498 +.. _988437d: https://github.com/python-semantic-release/python-semantic-release/commit/988437d21e40d3e3b1c95ed66b535bdd523210de +.. _ffae2dc: https://github.com/python-semantic-release/python-semantic-release/commit/ffae2dc68f7f4bc13c5fd015acd43b457e568ada +.. _PR#497: https://github.com/python-semantic-release/python-semantic-release/pull/497 +.. _PR#502: https://github.com/python-semantic-release/python-semantic-release/pull/502 + + +.. _changelog-v7.31.4: + +v7.31.4 (2022-08-23) +==================== + +🪲 Bug Fixes +------------ + +* Account for trailing newlines in commit messages, closes `#490`_ (`PR#495`_, `111b151`_) + +.. _#490: https://github.com/python-semantic-release/python-semantic-release/issues/490 +.. _111b151: https://github.com/python-semantic-release/python-semantic-release/commit/111b1518e8c8e2bd7535bd4c4b126548da384605 +.. _PR#495: https://github.com/python-semantic-release/python-semantic-release/pull/495 + + +.. _changelog-v7.31.3: + +v7.31.3 (2022-08-22) +==================== + +🪲 Bug Fixes +------------ + +* Use ``commit_subject`` when searching for release commits (`PR#488`_, `3849ed9`_) + +.. _3849ed9: https://github.com/python-semantic-release/python-semantic-release/commit/3849ed992c3cff9054b8690bcf59e49768f84f47 +.. _PR#488: https://github.com/python-semantic-release/python-semantic-release/pull/488 + + +.. _changelog-v7.31.2: + +v7.31.2 (2022-07-29) +==================== + +🪲 Bug Fixes +------------ + +* Add better handling of missing changelog placeholder, closes `#454`_ (`e7a0e81`_) + +* Add repo=None when not in git repo, closes `#422`_ (`40be804`_) + +📖 Documentation +---------------- + +* Add example for pyproject.toml (`2a4b8af`_) + +.. _#422: https://github.com/python-semantic-release/python-semantic-release/issues/422 +.. _#454: https://github.com/python-semantic-release/python-semantic-release/issues/454 +.. _2a4b8af: https://github.com/python-semantic-release/python-semantic-release/commit/2a4b8af1c2893a769c02476bb92f760c8522bd7a +.. _40be804: https://github.com/python-semantic-release/python-semantic-release/commit/40be804c09ab8a036fb135c9c38a63f206d2742c +.. _e7a0e81: https://github.com/python-semantic-release/python-semantic-release/commit/e7a0e81c004ade73ed927ba4de8c3e3ccaf0047c + + +.. _changelog-v7.31.1: + +v7.31.1 (2022-07-29) +==================== + +🪲 Bug Fixes +------------ + +* Update git email in action, closes `#473`_ (`0ece6f2`_) + +.. _#473: https://github.com/python-semantic-release/python-semantic-release/issues/473 +.. _0ece6f2: https://github.com/python-semantic-release/python-semantic-release/commit/0ece6f263ff02a17bb1e00e7ed21c490f72e3d00 + + +.. _changelog-v7.31.0: + +v7.31.0 (2022-07-29) +==================== + +✨ Features +----------- + +* Add prerelease-patch and no-prerelease-patch flags for whether to auto-bump prereleases + (`b4e5b62`_) + +* Override repository_url w REPOSITORY_URL env var (`PR#439`_, `cb7578c`_) + +🪲 Bug Fixes +------------ + +* :bug: fix get_current_release_version for tag_only version_source (`cad09be`_) + +.. _b4e5b62: https://github.com/python-semantic-release/python-semantic-release/commit/b4e5b626074f969e4140c75fdac837a0625cfbf6 +.. _cad09be: https://github.com/python-semantic-release/python-semantic-release/commit/cad09be9ba067f1c882379c0f4b28115a287fc2b +.. _cb7578c: https://github.com/python-semantic-release/python-semantic-release/commit/cb7578cf005b8bd65d9b988f6f773e4c060982e3 +.. _PR#439: https://github.com/python-semantic-release/python-semantic-release/pull/439 + + +.. _changelog-v7.30.2: + +v7.30.2 (2022-07-26) +==================== + +🪲 Bug Fixes +------------ + +* Declare additional_options as action inputs (`PR#481`_, `cb5d8c7`_) + +.. _cb5d8c7: https://github.com/python-semantic-release/python-semantic-release/commit/cb5d8c7ce7d013fcfabd7696b5ffb846a8a6f853 +.. _PR#481: https://github.com/python-semantic-release/python-semantic-release/pull/481 + + +.. _changelog-v7.30.1: + +v7.30.1 (2022-07-25) +==================== + +🪲 Bug Fixes +------------ + +* Don't use commit_subject for tag pattern matching (`PR#480`_, `ac3f11e`_) + +.. _ac3f11e: https://github.com/python-semantic-release/python-semantic-release/commit/ac3f11e689f4a290d20b68b9c5c214098eb61b5f +.. _PR#480: https://github.com/python-semantic-release/python-semantic-release/pull/480 + + +.. _changelog-v7.30.0: + +v7.30.0 (2022-07-25) +==================== + +✨ Features +----------- + +* Add ``additional_options`` input for GitHub Action (`PR#477`_, `aea60e3`_) + +🪲 Bug Fixes +------------ + +* Allow empty additional options (`PR#479`_, `c9b2514`_) + +.. _aea60e3: https://github.com/python-semantic-release/python-semantic-release/commit/aea60e3d290c6fe3137bff21e0db1ed936233776 +.. _c9b2514: https://github.com/python-semantic-release/python-semantic-release/commit/c9b2514d3e164b20e78b33f60989d78c2587e1df +.. _PR#477: https://github.com/python-semantic-release/python-semantic-release/pull/477 +.. _PR#479: https://github.com/python-semantic-release/python-semantic-release/pull/479 + + +.. _changelog-v7.29.7: + +v7.29.7 (2022-07-24) +==================== + +🪲 Bug Fixes +------------ + +* Ignore dependency version bumps when parsing version from commit logs (`PR#476`_, `51bcb78`_) + +.. _51bcb78: https://github.com/python-semantic-release/python-semantic-release/commit/51bcb780a9f55fadfaf01612ff65c1f92642c2c1 +.. _PR#476: https://github.com/python-semantic-release/python-semantic-release/pull/476 + + +.. _changelog-v7.29.6: + +v7.29.6 (2022-07-15) +==================== + +🪲 Bug Fixes +------------ + +* Allow changing prerelease tag using CLI flags (`PR#466`_, `395bf4f`_) + +.. _395bf4f: https://github.com/python-semantic-release/python-semantic-release/commit/395bf4f2de73663c070f37cced85162d41934213 +.. _PR#466: https://github.com/python-semantic-release/python-semantic-release/pull/466 + + +.. _changelog-v7.29.5: + +v7.29.5 (2022-07-14) +==================== + +🪲 Bug Fixes +------------ + +* Add packaging module requirement (`PR#469`_, `b99c9fa`_) + +* **publish**: Get version bump for current release (`PR#467`_, `dd26888`_) + +.. _b99c9fa: https://github.com/python-semantic-release/python-semantic-release/commit/b99c9fa88dc25e5ceacb131cd93d9079c4fb2c86 +.. _dd26888: https://github.com/python-semantic-release/python-semantic-release/commit/dd26888a923b2f480303c19f1916647de48b02bf +.. _PR#467: https://github.com/python-semantic-release/python-semantic-release/pull/467 +.. _PR#469: https://github.com/python-semantic-release/python-semantic-release/pull/469 + + +.. _changelog-v7.29.4: + +v7.29.4 (2022-06-29) +==================== + +🪲 Bug Fixes +------------ + +* Add text for empty ValueError (`PR#461`_, `733254a`_) + +.. _733254a: https://github.com/python-semantic-release/python-semantic-release/commit/733254a99320d8c2f964d799ac4ec29737867faa +.. _PR#461: https://github.com/python-semantic-release/python-semantic-release/pull/461 + + +.. _changelog-v7.29.3: + +v7.29.3 (2022-06-26) +==================== + +🪲 Bug Fixes +------------ + +* Ensure that assets can be uploaded successfully on custom GitHub servers (`PR#458`_, `32b516d`_) + +.. _32b516d: https://github.com/python-semantic-release/python-semantic-release/commit/32b516d7aded4afcafe4aa56d6a5a329b3fc371d +.. _PR#458: https://github.com/python-semantic-release/python-semantic-release/pull/458 + + +.. _changelog-v7.29.2: + +v7.29.2 (2022-06-20) +==================== + +🪲 Bug Fixes +------------ + +* Ensure should_bump checks against release version if not prerelease (`PR#457`_, `da0606f`_) + +.. _da0606f: https://github.com/python-semantic-release/python-semantic-release/commit/da0606f0d67ada5f097c704b9423ead3b5aca6b2 +.. _PR#457: https://github.com/python-semantic-release/python-semantic-release/pull/457 + + +.. _changelog-v7.29.1: + +v7.29.1 (2022-06-01) +==================== + +🪲 Bug Fixes +------------ + +* Capture correct release version when patch has more than one digit (`PR#448`_, `426cdc7`_) + +.. _426cdc7: https://github.com/python-semantic-release/python-semantic-release/commit/426cdc7d7e0140da67f33b6853af71b2295aaac2 +.. _PR#448: https://github.com/python-semantic-release/python-semantic-release/pull/448 + + +.. _changelog-v7.29.0: + +v7.29.0 (2022-05-27) +==================== + +✨ Features +----------- + +* Allow using ssh-key to push version while using token to publish to hvcs (`PR#419`_, `7b2dffa`_) + +* **config**: Add ignore_token_for_push param (`PR#419`_, `7b2dffa`_) + +🪲 Bug Fixes +------------ + +* Fix and refactor prerelease (`PR#435`_, `94c9494`_) + +* **test**: Override GITHUB_ACTOR env (`PR#419`_, `7b2dffa`_) + +📖 Documentation +---------------- + +* Add documentation for ignore_token_for_push (`PR#419`_, `7b2dffa`_) + +.. _7b2dffa: https://github.com/python-semantic-release/python-semantic-release/commit/7b2dffadf43c77d5e0eea307aefcee5c7744df5c +.. _94c9494: https://github.com/python-semantic-release/python-semantic-release/commit/94c94942561f85f48433c95fd3467e03e0893ab4 +.. _PR#419: https://github.com/python-semantic-release/python-semantic-release/pull/419 +.. _PR#435: https://github.com/python-semantic-release/python-semantic-release/pull/435 + + +.. _changelog-v7.28.1: + +v7.28.1 (2022-04-14) +==================== + +🪲 Bug Fixes +------------ + +* Fix getting current version when ``version_source=tag_only`` (`PR#437`_, `b247936`_) + +.. _b247936: https://github.com/python-semantic-release/python-semantic-release/commit/b247936a81c0d859a34bf9f17ab8ca6a80488081 +.. _PR#437: https://github.com/python-semantic-release/python-semantic-release/pull/437 + + +.. _changelog-v7.28.0: + +v7.28.0 (2022-04-11) +==================== + +✨ Features +----------- + +* Add ``tag_only`` option for ``version_source``, closes `#354`_ (`PR#436`_, `cf74339`_) + +.. _#354: https://github.com/python-semantic-release/python-semantic-release/issues/354 +.. _cf74339: https://github.com/python-semantic-release/python-semantic-release/commit/cf743395456a86c62679c2c0342502af043bfc3b +.. _PR#436: https://github.com/python-semantic-release/python-semantic-release/pull/436 + + +.. _changelog-v7.27.1: + +v7.27.1 (2022-04-03) +==================== + +🪲 Bug Fixes +------------ + +* **prerelase**: Pass prerelease option to get_current_version (`PR#432`_, `aabab0b`_) + +.. _aabab0b: https://github.com/python-semantic-release/python-semantic-release/commit/aabab0b7ce647d25e0c78ae6566f1132ece9fcb9 +.. _PR#432: https://github.com/python-semantic-release/python-semantic-release/pull/432 + + +.. _changelog-v7.27.0: + +v7.27.0 (2022-03-15) +==================== + +✨ Features +----------- + +* Add git-lfs to docker container (`PR#427`_, `184e365`_) + +.. _184e365: https://github.com/python-semantic-release/python-semantic-release/commit/184e3653932979b82e5a62b497f2a46cbe15ba87 +.. _PR#427: https://github.com/python-semantic-release/python-semantic-release/pull/427 + + +.. _changelog-v7.26.0: + +v7.26.0 (2022-03-07) +==================== + +✨ Features +----------- + +* **publish-cmd**: add ``--prerelease`` cli flag to enable prerelease versioning (`PR#413`_, + `7064265`_) + +* **version-cmd**: add ``--prerelease`` cli flag to enable prerelease versioning (`PR#413`_, + `7064265`_) + +📖 Documentation +---------------- + +* Added basic info about prerelease versioning (`PR#413`_, `7064265`_) + +.. _7064265: https://github.com/python-semantic-release/python-semantic-release/commit/7064265627a2aba09caa2873d823b594e0e23e77 +.. _PR#413: https://github.com/python-semantic-release/python-semantic-release/pull/413 + + +.. _changelog-v7.25.2: + +v7.25.2 (2022-02-24) +==================== + +🪲 Bug Fixes +------------ + +* **gitea**: Use form-data from asset upload (`PR#421`_, `e011944`_) + +.. _e011944: https://github.com/python-semantic-release/python-semantic-release/commit/e011944987885f75b80fe16a363f4befb2519a91 +.. _PR#421: https://github.com/python-semantic-release/python-semantic-release/pull/421 + + +.. _changelog-v7.25.1: + +v7.25.1 (2022-02-23) +==================== + +🪲 Bug Fixes +------------ + +* **gitea**: Build status and asset upload (`PR#420`_, `57db81f`_) + +* **gitea**: Handle list build status response (`PR#420`_, `57db81f`_) + +.. _57db81f: https://github.com/python-semantic-release/python-semantic-release/commit/57db81f4c6b96da8259e3bad9137eaccbcd10f6e +.. _PR#420: https://github.com/python-semantic-release/python-semantic-release/pull/420 + + +.. _changelog-v7.25.0: + +v7.25.0 (2022-02-17) +==================== + +✨ Features +----------- + +* **hvcs**: Add gitea support (`PR#412`_, `b7e7936`_) + +📖 Documentation +---------------- + +* Document tag_commit, closes `#410`_ (`b631ca0`_) + + +.. _#410: https://github.com/python-semantic-release/python-semantic-release/issues/410 +.. _b631ca0: https://github.com/python-semantic-release/python-semantic-release/commit/b631ca0a79cb2d5499715d43688fc284cffb3044 +.. _b7e7936: https://github.com/python-semantic-release/python-semantic-release/commit/b7e7936331b7939db09abab235c8866d800ddc1a +.. _PR#412: https://github.com/python-semantic-release/python-semantic-release/pull/412 + + +.. _changelog-v7.24.0: + +v7.24.0 (2022-01-24) +==================== + +✨ Features +----------- + +* Include additional changes in release commits (`3e34f95`_) + +.. _3e34f95: https://github.com/python-semantic-release/python-semantic-release/commit/3e34f957ff5a3ec6e6f984cc4a79a38ce4391ea9 + + +.. _changelog-v7.23.0: + +v7.23.0 (2021-11-30) +==================== + +✨ Features +----------- + +* Support Github Enterprise server (`b4e01f1`_) + +.. _b4e01f1: https://github.com/python-semantic-release/python-semantic-release/commit/b4e01f1b7e841263fa84f57f0ac331f7c0b31954 + + +.. _changelog-v7.22.0: + +v7.22.0 (2021-11-21) +==================== + +✨ Features +----------- + +* **parser_angular**: Allow customization in parser (`298eebb`_) + +🪲 Bug Fixes +------------ + +* Address PR feedback for ``parser_angular.py`` (`f7bc458`_) + +.. _298eebb: https://github.com/python-semantic-release/python-semantic-release/commit/298eebbfab5c083505036ba1df47a5874a1eed6e +.. _f7bc458: https://github.com/python-semantic-release/python-semantic-release/commit/f7bc45841e6a5c762f99f936c292cee25fabcd02 + + +.. _changelog-v7.21.0: + +v7.21.0 (2021-11-21) +==================== + +✨ Features +----------- + +* Use gitlab-ci or github actions env vars, closes `#363`_ (`8ca8dd4`_) + +🪲 Bug Fixes +------------ + +* Remove invalid repository exception (`746b62d`_) + +.. _#363: https://github.com/python-semantic-release/python-semantic-release/issues/363 +.. _746b62d: https://github.com/python-semantic-release/python-semantic-release/commit/746b62d4e207a5d491eecd4ca96d096eb22e3bed +.. _8ca8dd4: https://github.com/python-semantic-release/python-semantic-release/commit/8ca8dd40f742f823af147928bd75a9577c50d0fd + + +.. _changelog-v7.20.0: + +v7.20.0 (2021-11-21) +==================== + +✨ Features +----------- + +* Allow custom environment variable names (`PR#392`_, `372cda3`_) + +* Rewrite Twine adapter for uploading to artifact repositories (`cfb20af`_) + +🪲 Bug Fixes +------------ + +* Don't use linux commands on windows (`PR#393`_, `5bcccd2`_) + +* Mypy errors in vcs_helpers (`13ca0fe`_) + +* Skip removing the build folder if it doesn't exist (`8e79fdc`_) + +📖 Documentation +---------------- + +* Clean typos and add section for repository upload (`1efa18a`_) + +.. _13ca0fe: https://github.com/python-semantic-release/python-semantic-release/commit/13ca0fe650125be2f5e953f6193fdc4d44d3c75a +.. _1efa18a: https://github.com/python-semantic-release/python-semantic-release/commit/1efa18a3a55134d6bc6e4572ab025e24082476cd +.. _372cda3: https://github.com/python-semantic-release/python-semantic-release/commit/372cda3497f16ead2209e6e1377d38f497144883 +.. _5bcccd2: https://github.com/python-semantic-release/python-semantic-release/commit/5bcccd21cc8be3289db260e645fec8dc6a592abd +.. _8e79fdc: https://github.com/python-semantic-release/python-semantic-release/commit/8e79fdc107ffd852a91dfb5473e7bd1dfaba4ee5 +.. _cfb20af: https://github.com/python-semantic-release/python-semantic-release/commit/cfb20af79a8e25a77aee9ff72deedcd63cb7f62f +.. _PR#392: https://github.com/python-semantic-release/python-semantic-release/pull/392 +.. _PR#393: https://github.com/python-semantic-release/python-semantic-release/pull/393 + + +.. _changelog-v7.19.2: + +v7.19.2 (2021-09-04) +==================== + +🪲 Bug Fixes +------------ + +* Fixed ImproperConfig import error (`PR#377`_, `b011a95`_) + +.. _b011a95: https://github.com/python-semantic-release/python-semantic-release/commit/b011a9595df4240cb190bfb1ab5b6d170e430dfc +.. _PR#377: https://github.com/python-semantic-release/python-semantic-release/pull/377 + + +.. _changelog-v7.19.1: + +v7.19.1 (2021-08-17) +==================== + +🪲 Bug Fixes +------------ + +* Add get_formatted_tag helper instead of hardcoded v-prefix in the git tags (`1a354c8`_) + +.. _1a354c8: https://github.com/python-semantic-release/python-semantic-release/commit/1a354c86abad77563ebce9a6944256461006f3c7 + + +.. _changelog-v7.19.0: + +v7.19.0 (2021-08-16) +==================== + +✨ Features +----------- + +* Custom git tag format support (`PR#373`_, `1d76632`_) + +📖 Documentation +---------------- + +* **configuration**: define ``tag_format`` usage & resulting effect (`PR#373`_, `1d76632`_) + +* **parser**: Documentation for scipy-parser (`45ee34a`_) + +.. _1d76632: https://github.com/python-semantic-release/python-semantic-release/commit/1d76632043bf0b6076d214a63c92013624f4b95e +.. _45ee34a: https://github.com/python-semantic-release/python-semantic-release/commit/45ee34aa21443860a6c2cd44a52da2f353b960bf +.. _PR#373: https://github.com/python-semantic-release/python-semantic-release/pull/373 + + +.. _changelog-v7.18.0: + +v7.18.0 (2021-08-09) +==================== + +✨ Features +----------- + +* Add support for non-prefixed tags (`PR#366`_, `0fee4dd`_) + +📖 Documentation +---------------- + +* Clarify second argument of ParsedCommit (`086ddc2`_) + +.. _086ddc2: https://github.com/python-semantic-release/python-semantic-release/commit/086ddc28f06522453328f5ea94c873bd202ff496 +.. _0fee4dd: https://github.com/python-semantic-release/python-semantic-release/commit/0fee4ddb5baaddf85ed6b76e76a04474a5f97d0a +.. _PR#366: https://github.com/python-semantic-release/python-semantic-release/pull/366 + + +.. _changelog-v7.17.0: + +v7.17.0 (2021-08-07) +==================== + +✨ Features +----------- + +* **parser**: Add scipy style parser (`PR#369`_, `51a3921`_) + +.. _51a3921: https://github.com/python-semantic-release/python-semantic-release/commit/51a39213ea120c4bbd7a57b74d4f0cc3103da9f5 +.. _PR#369: https://github.com/python-semantic-release/python-semantic-release/pull/369 + + +.. _changelog-v7.16.4: + +v7.16.4 (2021-08-03) +==================== + +🪲 Bug Fixes +------------ + +* Correct rendering of gitlab issue references, closes `#358`_ (`07429ec`_) + +.. _#358: https://github.com/python-semantic-release/python-semantic-release/issues/358 +.. _07429ec: https://github.com/python-semantic-release/python-semantic-release/commit/07429ec4a32d32069f25ec77b4bea963bd5d2a00 + + +.. _changelog-v7.16.3: + +v7.16.3 (2021-07-29) +==================== + +🪲 Bug Fixes +------------ + +* Print right info if token is not set, closes `#360`_ (`PR#361`_, `a275a7a`_) + +.. _#360: https://github.com/python-semantic-release/python-semantic-release/issues/360 +.. _a275a7a: https://github.com/python-semantic-release/python-semantic-release/commit/a275a7a17def85ff0b41d254e4ee42772cce1981 +.. _PR#361: https://github.com/python-semantic-release/python-semantic-release/pull/361 + + +.. _changelog-v7.16.2: + +v7.16.2 (2021-06-25) +==================== + +🪲 Bug Fixes +------------ + +* Use release-api for gitlab (`1ef5cab`_) + +📖 Documentation +---------------- + +* Recommend setting a concurrency group for GitHub Actions (`34b0735`_) + +* Update trove classifiers to reflect supported versions (`PR#344`_, `7578004`_) + +.. _1ef5cab: https://github.com/python-semantic-release/python-semantic-release/commit/1ef5caba2d8dd0f2647bc51ede0ef7152d8b7b8d +.. _34b0735: https://github.com/python-semantic-release/python-semantic-release/commit/34b07357ab3f4f4aa787b71183816ec8aaf334a8 +.. _7578004: https://github.com/python-semantic-release/python-semantic-release/commit/7578004ed4b20c2bd553782443dfd77535faa377 +.. _PR#344: https://github.com/python-semantic-release/python-semantic-release/pull/344 + + +.. _changelog-v7.16.1: + +v7.16.1 (2021-06-08) +==================== + +🪲 Bug Fixes +------------ + +* Tomlkit should stay at 0.7.0 (`769a5f3`_) + +.. _769a5f3: https://github.com/python-semantic-release/python-semantic-release/commit/769a5f31115cdb1f43f19a23fe72b96a8c8ba0fc + + +.. _changelog-v7.16.0: + +v7.16.0 (2021-06-08) +==================== + +✨ Features +----------- + +* Add option to omit tagging (`PR#341`_, `20603e5`_) + +.. _20603e5: https://github.com/python-semantic-release/python-semantic-release/commit/20603e53116d4f05e822784ce731b42e8cbc5d8f +.. _PR#341: https://github.com/python-semantic-release/python-semantic-release/pull/341 + + +.. _changelog-v7.15.6: + +v7.15.6 (2021-06-08) +==================== + +🪲 Bug Fixes +------------ + +* Update click and tomlkit (`PR#339`_, `947ea3b`_) + +.. _947ea3b: https://github.com/python-semantic-release/python-semantic-release/commit/947ea3bc0750735941446cf4a87bae20e750ba12 +.. _PR#339: https://github.com/python-semantic-release/python-semantic-release/pull/339 + + +.. _changelog-v7.15.5: + +v7.15.5 (2021-05-26) +==================== + +🪲 Bug Fixes +------------ + +* Pin tomlkit to 0.7.0 (`2cd0db4`_) + +.. _2cd0db4: https://github.com/python-semantic-release/python-semantic-release/commit/2cd0db4537bb9497b72eb496f6bab003070672ab + + +.. _changelog-v7.15.4: + +v7.15.4 (2021-04-29) +==================== + +🪲 Bug Fixes +------------ + +* Change log level of failed toml loading, closes `#235`_ (`24bb079`_) + +.. _#235: https://github.com/python-semantic-release/python-semantic-release/issues/235 +.. _24bb079: https://github.com/python-semantic-release/python-semantic-release/commit/24bb079cbeff12e7043dd35dd0b5ae03192383bb + + +.. _changelog-v7.15.3: + +v7.15.3 (2021-04-03) +==================== + +🪲 Bug Fixes +------------ + +* Add venv to path in github action (`583c5a1`_) + +.. _583c5a1: https://github.com/python-semantic-release/python-semantic-release/commit/583c5a13e40061fc544b82decfe27a6c34f6d265 + + +.. _changelog-v7.15.2: + +v7.15.2 (2021-04-03) +==================== + +🪲 Bug Fixes +------------ + +* Run semantic-release in virtualenv in the github action, closes `#331`_ (`b508ea9`_) + +* Set correct path for venv in action script (`aac02b5`_) + +* Use absolute path for venv in github action (`d4823b3`_) + +📖 Documentation +---------------- + +* Clarify that HVCS should be lowercase, closes `#330`_ (`da0ab0c`_) + +.. _#330: https://github.com/python-semantic-release/python-semantic-release/issues/330 +.. _#331: https://github.com/python-semantic-release/python-semantic-release/issues/331 +.. _aac02b5: https://github.com/python-semantic-release/python-semantic-release/commit/aac02b5a44a6959328d5879578aa3536bdf856c2 +.. _b508ea9: https://github.com/python-semantic-release/python-semantic-release/commit/b508ea9f411c1cd4f722f929aab9f0efc0890448 +.. _d4823b3: https://github.com/python-semantic-release/python-semantic-release/commit/d4823b3b6b1fcd5c33b354f814643c9aaf85a06a +.. _da0ab0c: https://github.com/python-semantic-release/python-semantic-release/commit/da0ab0c62c4ce2fa0d815e5558aeec1a1e23bc89 + + +.. _changelog-v7.15.1: + +v7.15.1 (2021-03-26) +==================== + +🪲 Bug Fixes +------------ + +* Add support for setting build_command to "false", closes `#328`_ (`520cf1e`_) + +* Upgrade python-gitlab range, closes `#329`_ (`abfacc4`_) + +📖 Documentation +---------------- + +* Add common options to documentation, closes `#327`_ (`20d79a5`_) + +.. _#327: https://github.com/python-semantic-release/python-semantic-release/issues/327 +.. _#328: https://github.com/python-semantic-release/python-semantic-release/issues/328 +.. _#329: https://github.com/python-semantic-release/python-semantic-release/issues/329 +.. _20d79a5: https://github.com/python-semantic-release/python-semantic-release/commit/20d79a51bffa26d40607c1b77d10912992279112 +.. _520cf1e: https://github.com/python-semantic-release/python-semantic-release/commit/520cf1eaa7816d0364407dbd17b5bc7c79806086 +.. _abfacc4: https://github.com/python-semantic-release/python-semantic-release/commit/abfacc432300941d57488842e41c06d885637e6c + + +.. _changelog-v7.15.0: + +v7.15.0 (2021-02-18) +==================== + +✨ Features +----------- + +* Allow the use of .pypirc for twine uploads (`PR#325`_, `6bc56b8`_) + +📖 Documentation +---------------- + +* Add documentation for releasing on a Jenkins instance (`PR#324`_, `77ad988`_) + +.. _6bc56b8: https://github.com/python-semantic-release/python-semantic-release/commit/6bc56b8aa63069a25a828a2d1a9038ecd09b7d5d +.. _77ad988: https://github.com/python-semantic-release/python-semantic-release/commit/77ad988a2057be59e4559614a234d6871c06ee37 +.. _PR#324: https://github.com/python-semantic-release/python-semantic-release/pull/324 +.. _PR#325: https://github.com/python-semantic-release/python-semantic-release/pull/325 + + +.. _changelog-v7.14.0: + +v7.14.0 (2021-02-11) +==================== + +✨ Features +----------- + +* **checks**: Add support for Jenkins CI (`PR#322`_, `3e99855`_) + +📖 Documentation +---------------- + +* Correct casing on proper nouns (`PR#320`_, `d51b999`_) + +* Correcting Python casing (`PR#320`_, `d51b999`_) + +* Correcting Semantic Versioning casing (`PR#320`_, `d51b999`_) + +.. _3e99855: https://github.com/python-semantic-release/python-semantic-release/commit/3e99855c6bc72b3e9a572c58cc14e82ddeebfff8 +.. _d51b999: https://github.com/python-semantic-release/python-semantic-release/commit/d51b999a245a4e56ff7a09d0495c75336f2f150d +.. _PR#320: https://github.com/python-semantic-release/python-semantic-release/pull/320 +.. _PR#322: https://github.com/python-semantic-release/python-semantic-release/pull/322 + + +.. _changelog-v7.13.2: + +v7.13.2 (2021-01-29) +==================== + +🪲 Bug Fixes +------------ + +* Crash when TOML has no PSR section (`PR#319`_, `5f8ab99`_) + +* Fix crash when TOML has no PSR section (`PR#319`_, `5f8ab99`_) + +📖 Documentation +---------------- + +* Fix ``version_toml`` example for Poetry (`PR#318`_, `39acb68`_) + +.. _39acb68: https://github.com/python-semantic-release/python-semantic-release/commit/39acb68bfffe8242040e476893639ba26fa0d6b5 +.. _5f8ab99: https://github.com/python-semantic-release/python-semantic-release/commit/5f8ab99bf7254508f4b38fcddef2bdde8dd15a4c +.. _PR#318: https://github.com/python-semantic-release/python-semantic-release/pull/318 +.. _PR#319: https://github.com/python-semantic-release/python-semantic-release/pull/319 + + +.. _changelog-v7.13.1: + +v7.13.1 (2021-01-26) +==================== + +🪲 Bug Fixes +------------ + +* Use multiline version_pattern match in replace, closes `#306`_ (`PR#315`_, `1a85af4`_) + +.. _#306: https://github.com/python-semantic-release/python-semantic-release/issues/306 +.. _1a85af4: https://github.com/python-semantic-release/python-semantic-release/commit/1a85af434325ce52e11b49895e115f7a936e417e +.. _PR#315: https://github.com/python-semantic-release/python-semantic-release/pull/315 + + +.. _changelog-v7.13.0: + +v7.13.0 (2021-01-26) +==================== + +✨ Features +----------- + +* Support toml files for version declaration, closes `#245`_, `#275`_ (`PR#307`_, `9b62a7e`_) + +.. _#245: https://github.com/python-semantic-release/python-semantic-release/issues/245 +.. _#275: https://github.com/python-semantic-release/python-semantic-release/issues/275 +.. _9b62a7e: https://github.com/python-semantic-release/python-semantic-release/commit/9b62a7e377378667e716384684a47cdf392093fa +.. _PR#307: https://github.com/python-semantic-release/python-semantic-release/pull/307 + + +.. _changelog-v7.12.0: + +v7.12.0 (2021-01-25) +==================== + +✨ Features +----------- + +* **github**: Retry GitHub API requests on failure (`PR#314`_, `ac241ed`_) + +🪲 Bug Fixes +------------ + +* **github**: Add retries to github API requests (`PR#314`_, `ac241ed`_) + +📖 Documentation +---------------- + +* **actions**: Pat must be passed to checkout step too, closes `#311`_ (`e2d8e47`_) + +.. _#311: https://github.com/python-semantic-release/python-semantic-release/issues/311 +.. _ac241ed: https://github.com/python-semantic-release/python-semantic-release/commit/ac241edf4de39f4fc0ff561a749fa85caaf9e2ae +.. _e2d8e47: https://github.com/python-semantic-release/python-semantic-release/commit/e2d8e47d2b02860881381318dcc088e150c0fcde +.. _PR#314: https://github.com/python-semantic-release/python-semantic-release/pull/314 + + +.. _changelog-v7.11.0: + +v7.11.0 (2021-01-08) +==================== + +✨ Features +----------- + +* **print-version**: Add print-version command to output version (`512e3d9`_) + +🪲 Bug Fixes +------------ + +* Add dot to --define option help (`eb4107d`_) + +* Avoid Unknown bump level 0 message (`8ab624c`_) + +* **actions**: Fix github actions with new main location (`6666672`_) + +⚙️ Build System +---------------- + +* Add __main__.py magic file (`e93f36a`_) + +.. _512e3d9: https://github.com/python-semantic-release/python-semantic-release/commit/512e3d92706055bdf8d08b7c82927d3530183079 +.. _6666672: https://github.com/python-semantic-release/python-semantic-release/commit/6666672d3d97ab7cdf47badfa3663f1a69c2dbdf +.. _8ab624c: https://github.com/python-semantic-release/python-semantic-release/commit/8ab624cf3508b57a9656a0a212bfee59379d6f8b +.. _e93f36a: https://github.com/python-semantic-release/python-semantic-release/commit/e93f36a7a10e48afb42c1dc3d860a5e2a07cf353 +.. _eb4107d: https://github.com/python-semantic-release/python-semantic-release/commit/eb4107d2efdf8c885c8ae35f48f1b908d1fced32 + + +.. _changelog-v7.10.0: + +v7.10.0 (2021-01-08) +==================== + +✨ Features +----------- + +* **build**: Allow falsy values for build_command to disable build step (`c07a440`_) + +📖 Documentation +---------------- + +* Fix incorrect reference syntax (`42027f0`_) + +* Rewrite getting started page (`97a9046`_) + +.. _42027f0: https://github.com/python-semantic-release/python-semantic-release/commit/42027f0d2bb64f4c9eaec65112bf7b6f67568e60 +.. _97a9046: https://github.com/python-semantic-release/python-semantic-release/commit/97a90463872502d1207890ae1d9dd008b1834385 +.. _c07a440: https://github.com/python-semantic-release/python-semantic-release/commit/c07a440f2dfc45a2ad8f7c454aaac180c4651f70 + + +.. _changelog-v7.9.0: + +v7.9.0 (2020-12-21) +=================== + +✨ Features +----------- + +* **hvcs**: Add hvcs_domain config option, closes `#277`_ (`ab3061a`_) + +🪲 Bug Fixes +------------ + +* **history**: Coerce version to string (`PR#298`_, `d4cdc3d`_) + +* **history**: Require semver >= 2.10 (`5087e54`_) + +.. _#277: https://github.com/python-semantic-release/python-semantic-release/issues/277 +.. _5087e54: https://github.com/python-semantic-release/python-semantic-release/commit/5087e549399648cf2e23339a037b33ca8b62d954 +.. _ab3061a: https://github.com/python-semantic-release/python-semantic-release/commit/ab3061ae93c49d71afca043b67b361e2eb2919e6 +.. _d4cdc3d: https://github.com/python-semantic-release/python-semantic-release/commit/d4cdc3d3cd2d93f2a78f485e3ea107ac816c7d00 +.. _PR#298: https://github.com/python-semantic-release/python-semantic-release/pull/298 + + +.. _changelog-v7.8.2: + +v7.8.2 (2020-12-19) +=================== + +✨ Features +----------- + +* **repository**: Add to settings artifact repository (`f4ef373`_) + +🪲 Bug Fixes +------------ + +* **cli**: Skip remove_dist where not needed (`04817d4`_) + +.. _04817d4: https://github.com/python-semantic-release/python-semantic-release/commit/04817d4ecfc693195e28c80455bfbb127485f36b +.. _f4ef373: https://github.com/python-semantic-release/python-semantic-release/commit/f4ef3733b948282fba5a832c5c0af134609b26d2 + + +.. _changelog-v7.8.1: + +v7.8.1 (2020-12-18) +=================== + +🪲 Bug Fixes +------------ + +* Filenames with unknown mimetype are now properly uploaded to github release (`f3ece78`_) + +* **logs**: Fix TypeError when enabling debug logs (`2591a94`_) + +.. _2591a94: https://github.com/python-semantic-release/python-semantic-release/commit/2591a94115114c4a91a48f5b10b3954f6ac932a1 +.. _f3ece78: https://github.com/python-semantic-release/python-semantic-release/commit/f3ece78b2913e70f6b99907b192a1e92bbfd6b77 + + +.. _changelog-v7.8.0: + +v7.8.0 (2020-12-18) +=================== + +✨ Features +----------- + +* Add ``upload_to_pypi_glob_patterns`` option (`42305ed`_) + +🪲 Bug Fixes +------------ + +* **changelog**: Use "issues" link vs "pull" (`93e48c9`_) + +* **netrc**: Prefer using token defined in GH_TOKEN instead of .netrc file (`3af32a7`_) + +.. _3af32a7: https://github.com/python-semantic-release/python-semantic-release/commit/3af32a738f2f2841fd75ec961a8f49a0b1c387cf +.. _42305ed: https://github.com/python-semantic-release/python-semantic-release/commit/42305ed499ca08c819c4e7e65fcfbae913b8e6e1 +.. _93e48c9: https://github.com/python-semantic-release/python-semantic-release/commit/93e48c992cb8b763f430ecbb0b7f9c3ca00036e4 + + +.. _changelog-v7.7.0: + +v7.7.0 (2020-12-12) +=================== + +✨ Features +----------- + +* **changelog**: Add PR links in markdown (`PR#282`_, `0448f6c`_) + +.. _0448f6c: https://github.com/python-semantic-release/python-semantic-release/commit/0448f6c350bbbf239a81fe13dc5f45761efa7673 +.. _PR#282: https://github.com/python-semantic-release/python-semantic-release/pull/282 + + +.. _changelog-v7.6.0: + +v7.6.0 (2020-12-06) +=================== + +✨ Features +----------- + +* Add ``major_on_zero`` option (`d324154`_) + +📖 Documentation +---------------- + +* Add documentation for option ``major_on_zero`` (`2e8b26e`_) + +.. _2e8b26e: https://github.com/python-semantic-release/python-semantic-release/commit/2e8b26e4ee0316a2cf2a93c09c783024fcd6b3ba +.. _d324154: https://github.com/python-semantic-release/python-semantic-release/commit/d3241540e7640af911eb24c71e66468feebb0d46 + + +.. _changelog-v7.5.0: + +v7.5.0 (2020-12-04) +=================== + +✨ Features +----------- + +* **logs**: Include scope in changelogs (`PR#281`_, `21c96b6`_) + +.. _21c96b6: https://github.com/python-semantic-release/python-semantic-release/commit/21c96b688cc44cc6f45af962ffe6d1f759783f37 +.. _PR#281: https://github.com/python-semantic-release/python-semantic-release/pull/281 + + +.. _changelog-v7.4.1: + +v7.4.1 (2020-12-04) +=================== + +🪲 Bug Fixes +------------ + +* Add "changelog_capitalize" to flags, closes `#278`_ (`PR#279`_, `37716df`_) + +.. _#278: https://github.com/python-semantic-release/python-semantic-release/issues/278 +.. _37716df: https://github.com/python-semantic-release/python-semantic-release/commit/37716dfa78eb3f848f57a5100d01d93f5aafc0bf +.. _PR#279: https://github.com/python-semantic-release/python-semantic-release/pull/279 + + +.. _changelog-v7.4.0: + +v7.4.0 (2020-11-24) +=================== + +✨ Features +----------- + +* Add changelog_capitalize configuration, closes `#260`_ (`7cacca1`_) + +📖 Documentation +---------------- + +* Fix broken internal references (`PR#270`_, `da20b9b`_) + +* Update links to Github docs (`PR#268`_, `c53162e`_) + +.. _#260: https://github.com/python-semantic-release/python-semantic-release/issues/260 +.. _7cacca1: https://github.com/python-semantic-release/python-semantic-release/commit/7cacca1eb436a7166ba8faf643b53c42bc32a6a7 +.. _c53162e: https://github.com/python-semantic-release/python-semantic-release/commit/c53162e366304082a3bd5d143b0401da6a16a263 +.. _da20b9b: https://github.com/python-semantic-release/python-semantic-release/commit/da20b9bdd3c7c87809c25ccb2a5993a7ea209a22 +.. _PR#268: https://github.com/python-semantic-release/python-semantic-release/pull/268 +.. _PR#270: https://github.com/python-semantic-release/python-semantic-release/pull/270 + + +.. _changelog-v7.3.0: + +v7.3.0 (2020-09-28) +=================== + +✨ Features +----------- + +* Generate ``changelog.md`` file (`PR#266`_, `2587dfe`_) + +📖 Documentation +---------------- + +* Fix docstring (`5a5e2cf`_) + +.. _2587dfe: https://github.com/python-semantic-release/python-semantic-release/commit/2587dfed71338ec6c816f58cdf0882382c533598 +.. _5a5e2cf: https://github.com/python-semantic-release/python-semantic-release/commit/5a5e2cfb5e6653fb2e95e6e23e56559953b2c2b4 +.. _PR#266: https://github.com/python-semantic-release/python-semantic-release/pull/266 + + +.. _changelog-v7.2.5: + +v7.2.5 (2020-09-16) +=================== + +🪲 Bug Fixes +------------ + +* Add required to inputs in action metadata (`PR#264`_, `e76b255`_) + +.. _e76b255: https://github.com/python-semantic-release/python-semantic-release/commit/e76b255cf7d3d156e3314fc28c54d63fa126e973 +.. _PR#264: https://github.com/python-semantic-release/python-semantic-release/pull/264 + + +.. _changelog-v7.2.4: + +v7.2.4 (2020-09-14) +=================== + +🪲 Bug Fixes +------------ + +* Use range for toml dependency, closes `#241`_ (`45707e1`_) + +.. _#241: https://github.com/python-semantic-release/python-semantic-release/issues/241 +.. _45707e1: https://github.com/python-semantic-release/python-semantic-release/commit/45707e1b7dcab48103a33de9d7f9fdb5a34dae4a + + +.. _changelog-v7.2.3: + +v7.2.3 (2020-09-12) +=================== + +🪲 Bug Fixes +------------ + +* Support multiline version_pattern matching by default (`82f7849`_) + +📖 Documentation +---------------- + +* Create 'getting started' instructions (`PR#256`_, `5f4d000`_) + +* Link to getting started guide in README (`f490e01`_) + +.. _5f4d000: https://github.com/python-semantic-release/python-semantic-release/commit/5f4d000c3f153d1d23128acf577e389ae879466e +.. _82f7849: https://github.com/python-semantic-release/python-semantic-release/commit/82f7849dcf29ba658e0cb3b5d21369af8bf3c16f +.. _f490e01: https://github.com/python-semantic-release/python-semantic-release/commit/f490e0194fa818db4d38c185bc5e6245bfde546b +.. _PR#256: https://github.com/python-semantic-release/python-semantic-release/pull/256 + + +.. _changelog-v7.2.2: + +v7.2.2 (2020-07-26) +=================== + +🪲 Bug Fixes +------------ + +* **changelog**: Send changelog to stdout, closes `#250`_ (`87e2bb8`_) + +📖 Documentation +---------------- + +* Add quotation marks to the pip commands in CONTRIBUTING.rst (`PR#253`_, `e20fa43`_) + +.. _#250: https://github.com/python-semantic-release/python-semantic-release/issues/250 +.. _87e2bb8: https://github.com/python-semantic-release/python-semantic-release/commit/87e2bb881387ff3ac245ab9923347a5a616e197b +.. _e20fa43: https://github.com/python-semantic-release/python-semantic-release/commit/e20fa43098c06f5f585c81b9cd7e287dcce3fb5d +.. _PR#253: https://github.com/python-semantic-release/python-semantic-release/pull/253 + + +.. _changelog-v7.2.1: + +v7.2.1 (2020-06-29) +=================== + +🪲 Bug Fixes +------------ + +* Commit all files with bumped versions (`PR#249`_, `b3a1766`_) + +📖 Documentation +---------------- + +* Give example of multiple build commands (`PR#248`_, `65f1ffc`_) + +.. _65f1ffc: https://github.com/python-semantic-release/python-semantic-release/commit/65f1ffcc6cac3bf382f4b821ff2be59d04f9f867 +.. _b3a1766: https://github.com/python-semantic-release/python-semantic-release/commit/b3a1766be7edb7d2eb76f2726d35ab8298688b3b +.. _PR#248: https://github.com/python-semantic-release/python-semantic-release/pull/248 +.. _PR#249: https://github.com/python-semantic-release/python-semantic-release/pull/249 + + +.. _changelog-v7.2.0: + +v7.2.0 (2020-06-15) +=================== + +✨ Features +----------- + +* Bump versions in multiple files, closes `#175`_ (`PR#246`_, `0ba2c47`_) + +.. _#175: https://github.com/python-semantic-release/python-semantic-release/issues/175 +.. _0ba2c47: https://github.com/python-semantic-release/python-semantic-release/commit/0ba2c473c6e44cc326b3299b6ea3ddde833bdb37 +.. _PR#246: https://github.com/python-semantic-release/python-semantic-release/pull/246 + + +.. _changelog-v7.1.1: + +v7.1.1 (2020-05-28) +=================== + +🪲 Bug Fixes +------------ + +* **changelog**: Swap sha and message in table changelog (`6741370`_) + +.. _6741370: https://github.com/python-semantic-release/python-semantic-release/commit/6741370ab09b1706ff6e19b9fbe57b4bddefc70d + + +.. _changelog-v7.1.0: + +v7.1.0 (2020-05-24) +=================== + +✨ Features +----------- + +* **changelog**: Add changelog_table component, closes `#237`_ (`PR#242`_, `fe6a7e7`_) + +.. _#237: https://github.com/python-semantic-release/python-semantic-release/issues/237 +.. _fe6a7e7: https://github.com/python-semantic-release/python-semantic-release/commit/fe6a7e7fa014ffb827a1430dbcc10d1fc84c886b +.. _PR#242: https://github.com/python-semantic-release/python-semantic-release/pull/242 + + +.. _changelog-v7.0.0: + +v7.0.0 (2020-05-22) +=================== + +✨ Features +----------- + +* Pass changelog_sections to components (`PR#240`_, `3e17a98`_) + +* **changelog**: Add changelog components (`PR#240`_, `3e17a98`_) + +📖 Documentation +---------------- + +* Add conda-forge badge (`e9536bb`_) + +* Add documentation for changelog_components (`PR#240`_, `3e17a98`_) + +💥 BREAKING CHANGES +------------------- + +* **changelog**: The ``compare_url`` option has been removed in favor of using + ``changelog_components``. This functionality is now available as the + ``semantic_release.changelog.compare_url`` component. + +.. _3e17a98: https://github.com/python-semantic-release/python-semantic-release/commit/3e17a98d7fa8468868a87e62651ac2c010067711 +.. _e9536bb: https://github.com/python-semantic-release/python-semantic-release/commit/e9536bbe119c9e3b90c61130c02468e0e1f14141 +.. _PR#240: https://github.com/python-semantic-release/python-semantic-release/pull/240 + + +.. _changelog-v6.4.1: + +v6.4.1 (2020-05-15) +=================== + +🪲 Bug Fixes +------------ + +* Convert ``\r\n`` to ``\n`` in commit messages, closes `#239`_ (`34acbbc`_) + +.. _#239: https://github.com/python-semantic-release/python-semantic-release/issues/239 +.. _34acbbc: https://github.com/python-semantic-release/python-semantic-release/commit/34acbbcd25320a9d18dcd1a4f43e1ce1837b2c9f + + +.. _changelog-v6.4.0: + +v6.4.0 (2020-05-15) +=================== + +✨ Features +----------- + +* **history**: Create emoji parser (`PR#238`_, `2e1c50a`_) + +🪲 Bug Fixes +------------ + +* Add emojis to default changelog_sections (`PR#238`_, `2e1c50a`_) + +* Include all parsed types in changelog (`PR#238`_, `2e1c50a`_) + +📖 Documentation +---------------- + +* Add documentation for emoji parser (`PR#238`_, `2e1c50a`_) + +♻️ Refactoring +--------------- + +* **history**: Get breaking changes in parser (`PR#238`_, `2e1c50a`_) + +.. _2e1c50a: https://github.com/python-semantic-release/python-semantic-release/commit/2e1c50a865628b372f48945a039a3edb38a7cdf0 +.. _PR#238: https://github.com/python-semantic-release/python-semantic-release/pull/238 + + +.. _changelog-v6.3.1: + +v6.3.1 (2020-05-11) +=================== + +🪲 Bug Fixes +------------ + +* Use getboolean for commit_version_number, closes `#186`_ (`a60e0b4`_) + +.. _#186: https://github.com/python-semantic-release/python-semantic-release/issues/186 +.. _a60e0b4: https://github.com/python-semantic-release/python-semantic-release/commit/a60e0b4e3cadf310c3e0ad67ebeb4e69d0ee50cb + + +.. _changelog-v6.3.0: + +v6.3.0 (2020-05-09) +=================== + +✨ Features +----------- + +* **history**: Support linking compare page in changelog, closes `#218`_ (`79a8e02`_) + +📖 Documentation +---------------- + +* Document compare_link option (`e52c355`_) + +* Rewrite commit-log-parsing.rst (`4c70f4f`_) + +.. _#218: https://github.com/python-semantic-release/python-semantic-release/issues/218 +.. _4c70f4f: https://github.com/python-semantic-release/python-semantic-release/commit/4c70f4f2aa3343c966d1b7ab8566fcc782242ab9 +.. _79a8e02: https://github.com/python-semantic-release/python-semantic-release/commit/79a8e02df82fbc2acecaad9e9ff7368e61df3e54 +.. _e52c355: https://github.com/python-semantic-release/python-semantic-release/commit/e52c355c0d742ddd2cfa65d42888296942e5bec5 + + +.. _changelog-v6.2.0: + +v6.2.0 (2020-05-02) +=================== + +✨ Features +----------- + +* **history**: Check all paragraphs for breaking changes, closes `#200`_ (`fec08f0`_) + +📖 Documentation +---------------- + +* Add = to verbosity option, closes `#227`_ (`a0f4c9c`_) + +* Use references where possible, closes `#221`_ (`f38e5d4`_) + + +.. _#200: https://github.com/python-semantic-release/python-semantic-release/issues/200 +.. _#221: https://github.com/python-semantic-release/python-semantic-release/issues/221 +.. _#227: https://github.com/python-semantic-release/python-semantic-release/issues/227 +.. _a0f4c9c: https://github.com/python-semantic-release/python-semantic-release/commit/a0f4c9cd397fcb98f880097319c08160adb3c3e6 +.. _f38e5d4: https://github.com/python-semantic-release/python-semantic-release/commit/f38e5d4a1597cddb69ce47a4d79b8774e796bf41 +.. _fec08f0: https://github.com/python-semantic-release/python-semantic-release/commit/fec08f0dbd7ae15f95ca9c41a02c9fe6d448ede0 + + +.. _changelog-v6.1.0: + +v6.1.0 (2020-04-26) +=================== + +✨ Features +----------- + +* **actions**: Support PYPI_TOKEN on GitHub Actions (`df2c080`_) + +* **pypi**: Support easier use of API tokens, closes `#213`_ (`bac135c`_) + +📖 Documentation +---------------- + +* Add documentation for PYPI_TOKEN (`a8263a0`_) + +.. _#213: https://github.com/python-semantic-release/python-semantic-release/issues/213 +.. _a8263a0: https://github.com/python-semantic-release/python-semantic-release/commit/a8263a066177d1d42f2844e4cb42a76a23588500 +.. _bac135c: https://github.com/python-semantic-release/python-semantic-release/commit/bac135c0ae7a6053ecfc7cdf2942c3c89640debf +.. _df2c080: https://github.com/python-semantic-release/python-semantic-release/commit/df2c0806f0a92186e914cfc8cc992171d74422df + + +.. _changelog-v6.0.1: + +v6.0.1 (2020-04-15) +=================== + +🪲 Bug Fixes +------------ + +* **hvcs**: Convert get_hvcs to use LoggedFunction (`3084249`_) + +.. _3084249: https://github.com/python-semantic-release/python-semantic-release/commit/308424933fd3375ca3730d9eaf8abbad2435830b + + +.. _changelog-v6.0.0: + +v6.0.0 (2020-04-15) +=================== + +📖 Documentation +---------------- + +* Create Read the Docs config file (`aa5a1b7`_) + +* Include README.rst in index.rst (`8673a9d`_) + +* Move action.rst into main documentation (`509ccaf`_) + +* Rewrite README.rst (`e049772`_) + +* Rewrite troubleshooting page (`0285de2`_) + +♻️ Refactoring +--------------- + +* **debug**: Use logging and click_log instead of ndebug (`15b1f65`_) + +💥 BREAKING CHANGES +------------------- + +* **debug**: ``debug="*"`` no longer has an effect, instead use ``--verbosity DEBUG``. + +.. _0285de2: https://github.com/python-semantic-release/python-semantic-release/commit/0285de215a8dac3fcc9a51f555fa45d476a56dff +.. _15b1f65: https://github.com/python-semantic-release/python-semantic-release/commit/15b1f650f29761e1ab2a91b767cbff79b2057a4c +.. _509ccaf: https://github.com/python-semantic-release/python-semantic-release/commit/509ccaf307a0998eced69ad9fee1807132babe28 +.. _8673a9d: https://github.com/python-semantic-release/python-semantic-release/commit/8673a9d92a9bf348bb3409e002a830741396c8ca +.. _aa5a1b7: https://github.com/python-semantic-release/python-semantic-release/commit/aa5a1b700a1c461c81c6434686cb6f0504c4bece +.. _e049772: https://github.com/python-semantic-release/python-semantic-release/commit/e049772cf14cdd49538cf357db467f0bf3fe9587 + + +.. _changelog-v5.2.0: + +v5.2.0 (2020-04-09) +=================== + +✨ Features +----------- + +* **github**: Add tag as default release name (`2997908`_) + +📖 Documentation +---------------- + +* Automate API docs (`7d4fea2`_) + +.. _2997908: https://github.com/python-semantic-release/python-semantic-release/commit/2997908f80f4fcec56917d237a079b961a06f990 +.. _7d4fea2: https://github.com/python-semantic-release/python-semantic-release/commit/7d4fea266cc75007de51609131eb6d1e324da608 + + +.. _changelog-v5.1.0: + +v5.1.0 (2020-04-04) +=================== + +✨ Features +----------- + +* **history**: Allow customizing changelog_sections (`PR#207`_, `d5803d5`_) + +📖 Documentation +---------------- + +* Improve formatting of configuration page (`9a8e22e`_) + +* Improve formatting of envvars page (`b376a56`_) + +* Update index.rst (`b27c26c`_) + +.. _9a8e22e: https://github.com/python-semantic-release/python-semantic-release/commit/9a8e22e838d7dbf3bfd941397c3b39560aca6451 +.. _b27c26c: https://github.com/python-semantic-release/python-semantic-release/commit/b27c26c66e7e41843ab29076f7e724908091b46e +.. _b376a56: https://github.com/python-semantic-release/python-semantic-release/commit/b376a567bfd407a507ce0752614b0ca75a0f2973 +.. _d5803d5: https://github.com/python-semantic-release/python-semantic-release/commit/d5803d5c1668d86482a31ac0853bac7ecfdc63bc +.. _PR#207: https://github.com/python-semantic-release/python-semantic-release/pull/207 + + +.. _changelog-v5.0.3: + +v5.0.3 (2020-03-26) +=================== + +🪲 Bug Fixes +------------ + +* Bump dependencies and fix Windows issues on Development (`PR#173`_, `0a6f8c3`_) + +* Missing mime types on Windows (`PR#173`_, `0a6f8c3`_) + +.. _0a6f8c3: https://github.com/python-semantic-release/python-semantic-release/commit/0a6f8c3842b05f5f424dad5ce1fa5e3823c7e688 +.. _PR#173: https://github.com/python-semantic-release/python-semantic-release/pull/173 + + +.. _changelog-v5.0.2: + +v5.0.2 (2020-03-22) +=================== + +🪲 Bug Fixes +------------ + +* **history**: Leave case of other characters unchanged (`96ba94c`_) + +.. _96ba94c: https://github.com/python-semantic-release/python-semantic-release/commit/96ba94c4b4593997343ec61ecb6c823c1494d0e2 + + +.. _changelog-v5.0.1: + +v5.0.1 (2020-03-22) +=================== + +🪲 Bug Fixes +------------ + +* Make action use current version of semantic-release (`123984d`_) + +.. _123984d: https://github.com/python-semantic-release/python-semantic-release/commit/123984d735181c622f3d99088a1ad91321192a11 + + +.. _changelog-v5.0.0: + +v5.0.0 (2020-03-22) +=================== + +✨ Features +----------- + +* **build**: Allow config setting for build command, closes `#188`_ (`PR#195`_, `740f4bd`_) + +🪲 Bug Fixes +------------ + +* Rename default of build_command config (`d5db22f`_) + +📖 Documentation +---------------- + +* **pypi**: Update docstings in pypi.py (`6502d44`_) + +💥 BREAKING CHANGES +------------------- + +* **build**: Previously the build_commands configuration variable set the types of bundles sent to + ``python setup.py``. It has been replaced by the configuration variable ``build_command`` which + takes the full command e.g. ``python setup.py sdist`` or ``poetry build``. + +.. _#188: https://github.com/python-semantic-release/python-semantic-release/issues/188 +.. _6502d44: https://github.com/python-semantic-release/python-semantic-release/commit/6502d448fa65e5dc100e32595e83fff6f62a881a +.. _740f4bd: https://github.com/python-semantic-release/python-semantic-release/commit/740f4bdb26569362acfc80f7e862fc2c750a46dd +.. _d5db22f: https://github.com/python-semantic-release/python-semantic-release/commit/d5db22f9f7acd05d20fd60a8b4b5a35d4bbfabb8 +.. _PR#195: https://github.com/python-semantic-release/python-semantic-release/pull/195 + + +.. _changelog-v4.11.0: + +v4.11.0 (2020-03-22) +==================== + +✨ Features +----------- + +* **actions**: Create GitHub Action (`350245d`_) + +📖 Documentation +---------------- + +* Make AUTHORS.rst dynamic (`db2e076`_) + +* **readme**: Fix minor typo (`c22f69f`_) + +.. _350245d: https://github.com/python-semantic-release/python-semantic-release/commit/350245dbfb07ed6a1db017b1d9d1072b368b1497 +.. _c22f69f: https://github.com/python-semantic-release/python-semantic-release/commit/c22f69f62a215ff65e1ab6dcaa8e7e9662692e64 +.. _db2e076: https://github.com/python-semantic-release/python-semantic-release/commit/db2e0762f3189d0f1a6ba29aad32bdefb7e0187f + + +.. _changelog-v4.10.0: + +v4.10.0 (2020-03-03) +==================== + +✨ Features +----------- + +* Make commit message configurable (`PR#184`_, `eb0762c`_) + +.. _eb0762c: https://github.com/python-semantic-release/python-semantic-release/commit/eb0762ca9fea5cecd5c7b182504912a629be473b +.. _PR#184: https://github.com/python-semantic-release/python-semantic-release/pull/184 + + +.. _changelog-v4.9.0: + +v4.9.0 (2020-03-02) +=================== + +✨ Features +----------- + +* **pypi**: Add build_commands config (`22146ea`_) + +🪲 Bug Fixes +------------ + +* **pypi**: Change bdist_wheels to bdist_wheel (`c4db509`_) + +.. _22146ea: https://github.com/python-semantic-release/python-semantic-release/commit/22146ea4b94466a90d60b94db4cc65f46da19197 +.. _c4db509: https://github.com/python-semantic-release/python-semantic-release/commit/c4db50926c03f3d551c8331932c567c7bdaf4f3d + + +.. _changelog-v4.8.0: + +v4.8.0 (2020-02-28) +=================== + +✨ Features +----------- + +* **git**: Add a new config for commit author (`aa2c22c`_) + +.. _aa2c22c: https://github.com/python-semantic-release/python-semantic-release/commit/aa2c22c469448fe57f02bea67a02f998ce519ac3 + + +.. _changelog-v4.7.1: + +v4.7.1 (2020-02-28) +=================== + +🪲 Bug Fixes +------------ + +* Repair parsing of remotes in the gitlab ci format, closes `#181`_ (`0fddbe2`_) + +.. _#181: https://github.com/python-semantic-release/python-semantic-release/issues/181 +.. _0fddbe2: https://github.com/python-semantic-release/python-semantic-release/commit/0fddbe2fb70d24c09ceddb789a159162a45942dc + + +.. _changelog-v4.7.0: + +v4.7.0 (2020-02-28) +=================== + +✨ Features +----------- + +* Upload distribution files to GitHub Releases (`PR#177`_, `e427658`_) + +* **github**: Upload dists to release (`PR#177`_, `e427658`_) + +🪲 Bug Fixes +------------ + +* Post changelog after PyPI upload (`PR#177`_, `e427658`_) + +* Support repository owner names containing dots, closes `#179`_ (`a6c4da4`_) + +* **github**: Fix upload of .whl files (`PR#177`_, `e427658`_) + +* **github**: Use application/octet-stream for .whl files (`90a7e47`_) + +📖 Documentation +---------------- + +* Document upload_to_release config option (`PR#177`_, `e427658`_) + +.. _#179: https://github.com/python-semantic-release/python-semantic-release/issues/179 +.. _90a7e47: https://github.com/python-semantic-release/python-semantic-release/commit/90a7e476a04d26babc88002e9035cad2ed485b07 +.. _a6c4da4: https://github.com/python-semantic-release/python-semantic-release/commit/a6c4da4c0e6bd8a37f64544f7813fa027f5054ed +.. _e427658: https://github.com/python-semantic-release/python-semantic-release/commit/e427658e33abf518191498c3142a0f18d3150e07 +.. _PR#177: https://github.com/python-semantic-release/python-semantic-release/pull/177 + + +.. _changelog-v4.6.0: + +v4.6.0 (2020-02-19) +=================== + +✨ Features +----------- + +* **history**: Capitalize changelog messages (`1a8e306`_) + +🪲 Bug Fixes +------------ + +* Add more debug statements in logs (`bc931ec`_) + +* Only overwrite with patch if bump is None, closes `#159`_ (`1daa4e2`_) + +.. _#159: https://github.com/python-semantic-release/python-semantic-release/issues/159 +.. _1a8e306: https://github.com/python-semantic-release/python-semantic-release/commit/1a8e3060b8f6d6362c27903dcfc69d17db5f1d36 +.. _1daa4e2: https://github.com/python-semantic-release/python-semantic-release/commit/1daa4e23ec2dd40c6b490849276524264787e24e +.. _bc931ec: https://github.com/python-semantic-release/python-semantic-release/commit/bc931ec46795fde4c1ccee004eec83bf73d5de7a + + +.. _changelog-v4.5.1: + +v4.5.1 (2020-02-16) +=================== + +🪲 Bug Fixes +------------ + +* **github**: Send token in request header, closes `#167`_ (`be9972a`_) + +📖 Documentation +---------------- + +* Add note about automatic releases in readme (`e606e75`_) + +* Fix broken list in readme (`7aa572b`_) + +* Update readme and getting started docs (`07b3208`_) + +.. _#167: https://github.com/python-semantic-release/python-semantic-release/issues/167 +.. _07b3208: https://github.com/python-semantic-release/python-semantic-release/commit/07b3208ff64301e544c4fdcb48314e49078fc479 +.. _7aa572b: https://github.com/python-semantic-release/python-semantic-release/commit/7aa572b2a323ddbc69686309226395f40c52b469 +.. _be9972a: https://github.com/python-semantic-release/python-semantic-release/commit/be9972a7b1fb183f738fb31bd370adb30281e4d5 +.. _e606e75: https://github.com/python-semantic-release/python-semantic-release/commit/e606e7583a30167cf7679c6bcada2f9e768b3abe + + +.. _changelog-v4.5.0: + +v4.5.0 (2020-02-08) +=================== + +✨ Features +----------- + +* **history**: Enable colon defined version, closes `#165`_ (`7837f50`_) + +🪲 Bug Fixes +------------ + +* Remove erroneous submodule (`762bfda`_) + +* **cli**: --noop flag works when before command, closes `#73`_ (`4fcc781`_) + +.. _#73: https://github.com/python-semantic-release/python-semantic-release/issues/73 +.. _#165: https://github.com/python-semantic-release/python-semantic-release/issues/165 +.. _4fcc781: https://github.com/python-semantic-release/python-semantic-release/commit/4fcc781d1a3f9235db552f0f4431c9f5e638d298 +.. _762bfda: https://github.com/python-semantic-release/python-semantic-release/commit/762bfda728c266b8cd14671d8da9298fc99c63fb +.. _7837f50: https://github.com/python-semantic-release/python-semantic-release/commit/7837f5036269328ef29996b9ea63cccd5a6bc2d5 + + +.. _changelog-v4.4.1: + +v4.4.1 (2020-01-18) +=================== + +🪲 Bug Fixes +------------ + +* Add quotes around twine arguments, closes `#163`_ (`46a83a9`_) + +.. _#163: https://github.com/python-semantic-release/python-semantic-release/issues/163 +.. _46a83a9: https://github.com/python-semantic-release/python-semantic-release/commit/46a83a94b17c09d8f686c3ae7b199d7fd0e0e5e5 + + +.. _changelog-v4.4.0: + +v4.4.0 (2020-01-17) +=================== + +✨ Features +----------- + +* **parser**: Add support for exclamation point for breaking changes, closes `#156`_ (`a4f8a10`_) + +* **parser**: Make BREAKING-CHANGE synonymous with BREAKING CHANGE (`beedccf`_) + +🪲 Bug Fixes +------------ + +* **github**: Add check for GITHUB_ACTOR for git push (`PR#162`_, `c41e9bb`_) + +.. _#156: https://github.com/python-semantic-release/python-semantic-release/issues/156 +.. _a4f8a10: https://github.com/python-semantic-release/python-semantic-release/commit/a4f8a10afcc358a8fbef83be2041129480350be2 +.. _beedccf: https://github.com/python-semantic-release/python-semantic-release/commit/beedccfddfb360aeebef595342ee980446012ec7 +.. _c41e9bb: https://github.com/python-semantic-release/python-semantic-release/commit/c41e9bb986d01b92d58419cbdc88489d630a11f1 +.. _PR#162: https://github.com/python-semantic-release/python-semantic-release/pull/162 + + +.. _changelog-v4.3.4: + +v4.3.4 (2019-12-17) +=================== + +🪲 Bug Fixes +------------ + +* Fallback to whole log if correct tag is not available, closes `#51`_ (`PR#157`_, `252bffd`_) + +.. _#51: https://github.com/python-semantic-release/python-semantic-release/issues/51 +.. _252bffd: https://github.com/python-semantic-release/python-semantic-release/commit/252bffd3be7b6dfcfdb384d24cb1cd83d990fc9a +.. _PR#157: https://github.com/python-semantic-release/python-semantic-release/pull/157 + + +.. _changelog-v4.3.3: + +v4.3.3 (2019-11-06) +=================== + +🪲 Bug Fixes +------------ + +* Instead of requiring click 7.0, looks like all tests will pass with at least 2.0. (`PR#155`_, + `f07c7f6`_) + +* Set version of click to >=2.0,<8.0. (`PR#155`_, `f07c7f6`_) + +* Upgrade to click 7.0, closes `#117`_ (`PR#155`_, `f07c7f6`_) + +.. _#117: https://github.com/python-semantic-release/python-semantic-release/issues/117 +.. _f07c7f6: https://github.com/python-semantic-release/python-semantic-release/commit/f07c7f653be1c018e443f071d9a196d9293e9521 +.. _PR#155: https://github.com/python-semantic-release/python-semantic-release/pull/155 + + +.. _changelog-v4.3.2: + +v4.3.2 (2019-10-05) +=================== + +🪲 Bug Fixes +------------ + +* Update regex to get repository owner and name for project with dots, closes `#151`_ (`2778e31`_) + +.. _#151: https://github.com/python-semantic-release/python-semantic-release/issues/151 +.. _2778e31: https://github.com/python-semantic-release/python-semantic-release/commit/2778e316a0c0aa931b1012cb3862d04659c05e73 + + +.. _changelog-v4.3.1: + +v4.3.1 (2019-09-29) +=================== + +🪲 Bug Fixes +------------ + +* Support repo urls without git terminator (`700e9f1`_) + +.. _700e9f1: https://github.com/python-semantic-release/python-semantic-release/commit/700e9f18dafde1833f482272a72bb80b54d56bb3 + + +.. _changelog-v4.3.0: + +v4.3.0 (2019-09-06) +=================== + +✨ Features +----------- + +* Add the possibility to load configuration from pyproject.toml (`35f8bfe`_) + +* Allow the override of configuration options from cli, closes `#119`_ (`f0ac82f`_) + +* Allow users to get version from tag and write/commit bump to file, closes `#104`_ (`1f9fe1c`_) + +* Make the vcs functionalities work with gitlab, closes `#121`_ (`82d555d`_) + +🪲 Bug Fixes +------------ + +* Manage subgroups in git remote url, closes `#139`_, `#140`_ (`4b11875`_) + +* Update list of commit types to include build, ci and perf, closes `#145`_ (`41ea12f`_) + +.. _#104: https://github.com/python-semantic-release/python-semantic-release/issues/104 +.. _#119: https://github.com/python-semantic-release/python-semantic-release/issues/119 +.. _#121: https://github.com/python-semantic-release/python-semantic-release/issues/121 +.. _#139: https://github.com/python-semantic-release/python-semantic-release/issues/139 +.. _#140: https://github.com/python-semantic-release/python-semantic-release/issues/140 +.. _#145: https://github.com/python-semantic-release/python-semantic-release/issues/145 +.. _1f9fe1c: https://github.com/python-semantic-release/python-semantic-release/commit/1f9fe1cc7666d47cc0c348c4705b63c39bf10ecc +.. _35f8bfe: https://github.com/python-semantic-release/python-semantic-release/commit/35f8bfef443c8b69560c918f4b13bc766fb3daa2 +.. _41ea12f: https://github.com/python-semantic-release/python-semantic-release/commit/41ea12fa91f97c0046178806bce3be57c3bc2308 +.. _4b11875: https://github.com/python-semantic-release/python-semantic-release/commit/4b118754729094e330389712cf863e1c6cefee69 +.. _82d555d: https://github.com/python-semantic-release/python-semantic-release/commit/82d555d45b9d9e295ef3f9546a6ca2a38ca4522e +.. _f0ac82f: https://github.com/python-semantic-release/python-semantic-release/commit/f0ac82fe59eb59a768a73a1bf2ea934b9d448c58 + + +.. _changelog-v4.2.0: + +v4.2.0 (2019-08-05) +=================== + +✨ Features +----------- + +* Add configuration to customize handling of dists, closes `#115`_ (`2af6f41`_) + +* Add support for configuring branch, closes `#43`_ (`14abb05`_) + +* Add support for showing unreleased changelog, closes `#134`_ (`41ef794`_) + +🪲 Bug Fixes +------------ + +* Add commit hash when generating breaking changes, closes `#120`_ (`0c74faf`_) + +* Kept setting new version for tag source (`0e24a56`_) + +* Remove deletion of build folder, closes `#115`_ (`b45703d`_) + +* Updated the tag tests (`3303eef`_) + +* Upgrade click to 7.0 (`2c5dd80`_) + +.. _#43: https://github.com/python-semantic-release/python-semantic-release/issues/43 +.. _#115: https://github.com/python-semantic-release/python-semantic-release/issues/115 +.. _#120: https://github.com/python-semantic-release/python-semantic-release/issues/120 +.. _#134: https://github.com/python-semantic-release/python-semantic-release/issues/134 +.. _0c74faf: https://github.com/python-semantic-release/python-semantic-release/commit/0c74fafdfa81cf2e13db8f4dcf0a6f7347552504 +.. _0e24a56: https://github.com/python-semantic-release/python-semantic-release/commit/0e24a5633f8f94b48da97b011634d4f9d84f7b4b +.. _14abb05: https://github.com/python-semantic-release/python-semantic-release/commit/14abb05e7f878e88002f896812d66b4ea5c219d4 +.. _2af6f41: https://github.com/python-semantic-release/python-semantic-release/commit/2af6f41b21205bdd192514a434fca2feba17725a +.. _2c5dd80: https://github.com/python-semantic-release/python-semantic-release/commit/2c5dd809b84c2157a5e6cdcc773c43ec864f0328 +.. _3303eef: https://github.com/python-semantic-release/python-semantic-release/commit/3303eefa49a0474bbd85df10ae186ccbf9090ec1 +.. _41ef794: https://github.com/python-semantic-release/python-semantic-release/commit/41ef7947ad8a07392c96c7540980476e989c1d83 +.. _b45703d: https://github.com/python-semantic-release/python-semantic-release/commit/b45703dad38c29b28575060b21e5fb0f8482c6b1 + + +.. _changelog-v4.1.2: + +v4.1.2 (2019-08-04) +=================== + +🪲 Bug Fixes +------------ + +* Correct isort build fail (`0037210`_) + +* Make sure the history only breaks loop for version commit, closes `#135`_ (`5dc6cfc`_) + +* **vcs**: Allow cli to be run from subdirectory (`fb7bb14`_) + +📖 Documentation +---------------- + +* **circleci**: Point badge to master branch (`9c7302e`_) + +.. _#135: https://github.com/python-semantic-release/python-semantic-release/issues/135 +.. _0037210: https://github.com/python-semantic-release/python-semantic-release/commit/00372100b527ff9308d9e43fe5c65cdf179dc4dc +.. _5dc6cfc: https://github.com/python-semantic-release/python-semantic-release/commit/5dc6cfc634254f09997bb3cb0f17abd296e2c01f +.. _9c7302e: https://github.com/python-semantic-release/python-semantic-release/commit/9c7302e184a1bd88f39b3039691b55cd77f0bb07 +.. _fb7bb14: https://github.com/python-semantic-release/python-semantic-release/commit/fb7bb14300e483626464795b8ff4f033a194cf6f + + +.. _changelog-v4.1.1: + +v4.1.1 (2019-02-15) +=================== + +📖 Documentation +---------------- + +* Correct usage of changelog (`f4f59b0`_) + +* Debug usage and related (`f08e594`_) + +* Describing the commands (`b6fa04d`_) + +* Update url for commit guidelinesThe guidelines can now be found in theDEVELOPERS.md in angular. + (`90c1b21`_) + +.. _90c1b21: https://github.com/python-semantic-release/python-semantic-release/commit/90c1b217f86263301b91d19d641c7b348e37d960 +.. _b6fa04d: https://github.com/python-semantic-release/python-semantic-release/commit/b6fa04db3044525a1ee1b5952fb175a706842238 +.. _f08e594: https://github.com/python-semantic-release/python-semantic-release/commit/f08e5943a9876f2d17a7c02f468720995c7d9ffd +.. _f4f59b0: https://github.com/python-semantic-release/python-semantic-release/commit/f4f59b08c73700c6ee04930221bfcb1355cbc48d + + +.. _changelog-v4.1.0: + +v4.1.0 (2019-01-31) +=================== + +✨ Features +----------- + +* **ci_checks**: Add support for bitbucket (`9fc120d`_) + +🪲 Bug Fixes +------------ + +* Initialize git Repo from current folder (`c7415e6`_) + +* Maintain version variable formatting on bump (`PR#103`_, `bf63156`_) + +* Use same changelog code for command as post (`248f622`_) + +📖 Documentation +---------------- + +* Add installation instructions for development (`PR#106`_, `9168d0e`_) + +* **readme**: Add testing instructions (`bb352f5`_) + +.. _248f622: https://github.com/python-semantic-release/python-semantic-release/commit/248f62283c59182868c43ff105a66d85c923a894 +.. _9168d0e: https://github.com/python-semantic-release/python-semantic-release/commit/9168d0ea56734319a5d77e890f23ff6ba51cc97d +.. _9fc120d: https://github.com/python-semantic-release/python-semantic-release/commit/9fc120d1a7e4acbbca609628e72651685108b364 +.. _bb352f5: https://github.com/python-semantic-release/python-semantic-release/commit/bb352f5b6616cc42c9f2f2487c51dedda1c68295 +.. _bf63156: https://github.com/python-semantic-release/python-semantic-release/commit/bf63156f60340614fae94c255fb2f097cf317b2b +.. _c7415e6: https://github.com/python-semantic-release/python-semantic-release/commit/c7415e634c0affbe6396e0aa2bafe7c1b3368914 +.. _PR#103: https://github.com/python-semantic-release/python-semantic-release/pull/103 +.. _PR#106: https://github.com/python-semantic-release/python-semantic-release/pull/106 + + +.. _changelog-v4.0.1: + +v4.0.1 (2019-01-12) +=================== + +🪲 Bug Fixes +------------ + +* Add better error message when pypi credentials are empty, closes `#96`_ (`c4e5dcb`_) + +* Clean out dist and build before building, closes `#86`_ (`b628e46`_) + +* Filter out pypi secrets from exceptions, closes `#41`_ (`5918371`_) + +* Unfreeze dependencies, closes `#100`_ (`847833b`_) + +* Use correct syntax to exclude tests in package, closes `#92`_ (`3e41e91`_) + +* **parser_angular**: Fix non-match when special chars in scope (`8a33123`_) + +📖 Documentation +---------------- + +* Remove reference to gitter, closes `#90`_ (`896e37b`_) + +.. _#41: https://github.com/python-semantic-release/python-semantic-release/issues/41 +.. _#86: https://github.com/python-semantic-release/python-semantic-release/issues/86 +.. _#90: https://github.com/python-semantic-release/python-semantic-release/issues/90 +.. _#92: https://github.com/python-semantic-release/python-semantic-release/issues/92 +.. _#96: https://github.com/python-semantic-release/python-semantic-release/issues/96 +.. _#100: https://github.com/python-semantic-release/python-semantic-release/issues/100 +.. _3e41e91: https://github.com/python-semantic-release/python-semantic-release/commit/3e41e91c318663085cd28c8165ece21d7e383475 +.. _5918371: https://github.com/python-semantic-release/python-semantic-release/commit/5918371c1e82b06606087c9945d8eaf2604a0578 +.. _847833b: https://github.com/python-semantic-release/python-semantic-release/commit/847833bf48352a4935f906d0c3f75e1db596ca1c +.. _896e37b: https://github.com/python-semantic-release/python-semantic-release/commit/896e37b95cc43218e8f593325dd4ea63f8b895d9 +.. _8a33123: https://github.com/python-semantic-release/python-semantic-release/commit/8a331232621b26767e4268079f9295bf695047ab +.. _b628e46: https://github.com/python-semantic-release/python-semantic-release/commit/b628e466f86bc27cbe45ec27a02d4774a0efd3bb +.. _c4e5dcb: https://github.com/python-semantic-release/python-semantic-release/commit/c4e5dcbeda0ce8f87d25faefb4d9ae3581029a8f + + +.. _changelog-v4.0.0: + +v4.0.0 (2018-11-22) +=================== + +✨ Features +----------- + +* Add support for commit_message config variable (`4de5400`_) + +* **CI checks**: Add support for GitLab CI checks, closes `#88`_ (`8df5e2b`_) + +🪲 Bug Fixes +------------ + +* Add check of credentials (`7d945d4`_) + +* Add credentials check (`0694604`_) + +* Add dists to twine call (`1cec2df`_) + +* Change requests from fixed version to version range (`PR#93`_, `af3ad59`_) + +* Re-add skip-existing (`366e9c1`_) + +* Remove repository argument in twine (`e24543b`_) + +* Remove universal from setup config (`18b2402`_) + +* Update twine (`c4ae7b8`_) + +* Use new interface for twine (`c04872d`_) + +* Use twine through cli call (`ab84beb`_) + +📖 Documentation +---------------- + +* Add type hints and more complete docstrings, closes `#81`_ (`a6d5e9b`_) + +* Fix typo in documentation index (`da6844b`_) + +♻️ Refactoring +--------------- + +* Remove support for python 2 (`85fe638`_) + +💥 BREAKING CHANGES +------------------- + +* If you rely on the commit message to be the version number only, this will break your code + +* This will only work with python 3 after this commit. + +.. _#81: https://github.com/python-semantic-release/python-semantic-release/issues/81 +.. _#88: https://github.com/python-semantic-release/python-semantic-release/issues/88 +.. _0694604: https://github.com/python-semantic-release/python-semantic-release/commit/0694604f3b3d2159a4037620605ded09236cdef5 +.. _18b2402: https://github.com/python-semantic-release/python-semantic-release/commit/18b24025e397aace03dd5bb9eed46cfdd13491bd +.. _1cec2df: https://github.com/python-semantic-release/python-semantic-release/commit/1cec2df8bcb7f877c813d6470d454244630b050a +.. _366e9c1: https://github.com/python-semantic-release/python-semantic-release/commit/366e9c1d0b9ffcde755407a1de18e8295f6ad3a1 +.. _4de5400: https://github.com/python-semantic-release/python-semantic-release/commit/4de540011ab10483ee1865f99c623526cf961bb9 +.. _7d945d4: https://github.com/python-semantic-release/python-semantic-release/commit/7d945d44b36b3e8c0b7771570cb2305e9e09d0b2 +.. _85fe638: https://github.com/python-semantic-release/python-semantic-release/commit/85fe6384c15db317bc7142f4c8bbf2da58cece58 +.. _8df5e2b: https://github.com/python-semantic-release/python-semantic-release/commit/8df5e2bdd33a620e683f3adabe174e94ceaa88d9 +.. _a6d5e9b: https://github.com/python-semantic-release/python-semantic-release/commit/a6d5e9b1ccbe75d59e7240528593978a19d8d040 +.. _ab84beb: https://github.com/python-semantic-release/python-semantic-release/commit/ab84beb8f809e39ae35cd3ce5c15df698d8712fd +.. _af3ad59: https://github.com/python-semantic-release/python-semantic-release/commit/af3ad59f018876e11cc3acdda0b149f8dd5606bd +.. _c04872d: https://github.com/python-semantic-release/python-semantic-release/commit/c04872d00a26e9bf0f48eeacb360b37ce0fba01e +.. _c4ae7b8: https://github.com/python-semantic-release/python-semantic-release/commit/c4ae7b8ecc682855a8568b247690eaebe62d2d26 +.. _da6844b: https://github.com/python-semantic-release/python-semantic-release/commit/da6844bce0070a0020bf13950bd136fe28262602 +.. _e24543b: https://github.com/python-semantic-release/python-semantic-release/commit/e24543b96adb208897f4ce3eaab96b2f4df13106 +.. _PR#93: https://github.com/python-semantic-release/python-semantic-release/pull/93 + + +.. _changelog-v3.11.2: + +v3.11.2 (2018-06-10) +==================== + +🪲 Bug Fixes +------------ + +* Upgrade twine (`9722313`_) + +.. _9722313: https://github.com/python-semantic-release/python-semantic-release/commit/9722313eb63c7e2c32c084ad31bed7ee1c48a928 + + +.. _changelog-v3.11.1: + +v3.11.1 (2018-06-06) +==================== + +🪲 Bug Fixes +------------ + +* Change Gitpython version number, closes `#80`_ (`23c9d4b`_) + +📖 Documentation +---------------- + +* Add retry option to cli docs (`021da50`_) + +.. _#80: https://github.com/python-semantic-release/python-semantic-release/issues/80 +.. _021da50: https://github.com/python-semantic-release/python-semantic-release/commit/021da5001934f3199c98d7cf29f62a3ad8c2e56a +.. _23c9d4b: https://github.com/python-semantic-release/python-semantic-release/commit/23c9d4b6a1716e65605ed985881452898d5cf644 + + +.. _changelog-v3.11.0: + +v3.11.0 (2018-04-12) +==================== + +✨ Features +----------- + +* Add --retry cli option (`PR#78`_, `3e312c0`_) + +* Add support to finding previous version from tags if not using commit messages (`PR#68`_, + `6786487`_) + +* Be a bit more forgiving to find previous tags (`PR#68`_, `6786487`_) + +🪲 Bug Fixes +------------ + +* Add pytest cache to gitignore (`b8efd5a`_) + +* Make repo non if it is not a git repository, closes `#74`_ (`1dc306b`_) + +📖 Documentation +---------------- + +* Define ``--retry`` usage (`3e312c0`_) + +* Remove old notes about trello board (`7f50c52`_) + +* Update status badges (`cfa13b8`_) + +.. _#74: https://github.com/python-semantic-release/python-semantic-release/issues/74 +.. _1dc306b: https://github.com/python-semantic-release/python-semantic-release/commit/1dc306b9b1db2ac360211bdc61fd815302d0014c +.. _3e312c0: https://github.com/python-semantic-release/python-semantic-release/commit/3e312c0ce79a78d25016a3b294b772983cfb5e0f +.. _6786487: https://github.com/python-semantic-release/python-semantic-release/commit/6786487ebf4ab481139ef9f43cd74e345debb334 +.. _7f50c52: https://github.com/python-semantic-release/python-semantic-release/commit/7f50c521a522bb0c4579332766248778350e205b +.. _b8efd5a: https://github.com/python-semantic-release/python-semantic-release/commit/b8efd5a6249c79c8378bffea3e245657e7094ec9 +.. _cfa13b8: https://github.com/python-semantic-release/python-semantic-release/commit/cfa13b8260e3f3b0bfcb395f828ad63c9c5e3ca5 +.. _PR#68: https://github.com/python-semantic-release/python-semantic-release/pull/68 +.. _PR#78: https://github.com/python-semantic-release/python-semantic-release/pull/78 + + +.. _changelog-v3.10.3: + +v3.10.3 (2018-01-29) +==================== + +🪲 Bug Fixes +------------ + +* Error when not in git repository, closes `#74`_ (`PR#75`_, `251b190`_) + +.. _#74: https://github.com/python-semantic-release/python-semantic-release/issues/74 +.. _251b190: https://github.com/python-semantic-release/python-semantic-release/commit/251b190a2fd5df68892346926d447cbc1b32475a +.. _PR#75: https://github.com/python-semantic-release/python-semantic-release/pull/75 + + +.. _changelog-v3.10.2: + +v3.10.2 (2017-08-03) +==================== + +🪲 Bug Fixes +------------ + +* Update call to upload to work with twine 1.9.1 (`PR#72`_, `8f47643`_) + +.. _8f47643: https://github.com/python-semantic-release/python-semantic-release/commit/8f47643c54996e06c358537115e7e17b77cb02ca +.. _PR#72: https://github.com/python-semantic-release/python-semantic-release/pull/72 + + +.. _changelog-v3.10.1: + +v3.10.1 (2017-07-22) +==================== + +🪲 Bug Fixes +------------ + +* Update Twine (`PR#69`_, `9f268c3`_) + +.. _9f268c3: https://github.com/python-semantic-release/python-semantic-release/commit/9f268c373a932621771abbe9607b739b1e331409 +.. _PR#69: https://github.com/python-semantic-release/python-semantic-release/pull/69 + + +.. _changelog-v3.10.0: + +v3.10.0 (2017-05-05) +==================== + +✨ Features +----------- + +* Add git hash to the changelog (`PR#65`_, `628170e`_) + +🪲 Bug Fixes +------------ + +* Make changelog problems not fail whole publish (`b5a68cf`_) + +📖 Documentation +---------------- + +* Fix typo in cli.py docstring (`PR#64`_, `0d13985`_) + +.. _0d13985: https://github.com/python-semantic-release/python-semantic-release/commit/0d139859cd71f2d483f4360f196d6ef7c8726c18 +.. _628170e: https://github.com/python-semantic-release/python-semantic-release/commit/628170ebc440fc6abf094dd3e393f40576dedf9b +.. _b5a68cf: https://github.com/python-semantic-release/python-semantic-release/commit/b5a68cf6177dc0ed80eda722605db064f3fe2062 +.. _PR#64: https://github.com/python-semantic-release/python-semantic-release/pull/64 +.. _PR#65: https://github.com/python-semantic-release/python-semantic-release/pull/65 + + +.. _changelog-v3.9.0: + +v3.9.0 (2016-07-03) +=================== + +✨ Features +----------- + +* Add option for choosing between versioning by commit or tag (`c0cd1f5`_) + +* Don't use file to track version, only tag to commit for versioning (`cd25862`_) + +* Get repo version from historical tags instead of config file (`a45a9bf`_) + +🪲 Bug Fixes +------------ + +* Can't get the proper last tag from commit history (`5a0e681`_) + +.. _5a0e681: https://github.com/python-semantic-release/python-semantic-release/commit/5a0e681e256ec511cd6c6a8edfee9d905891da10 +.. _a45a9bf: https://github.com/python-semantic-release/python-semantic-release/commit/a45a9bfb64538efeb7f6f42bb6e7ede86a4ddfa8 +.. _c0cd1f5: https://github.com/python-semantic-release/python-semantic-release/commit/c0cd1f5b2e0776d7b636c3dd9e5ae863125219e6 +.. _cd25862: https://github.com/python-semantic-release/python-semantic-release/commit/cd258623ee518c009ae921cd6bb3119dafae43dc + + +.. _changelog-v3.8.1: + +v3.8.1 (2016-04-17) +=================== + +🪲 Bug Fixes +------------ + +* Add search_parent_directories option to gitpython (`PR#62`_, `8bf9ce1`_) + +.. _8bf9ce1: https://github.com/python-semantic-release/python-semantic-release/commit/8bf9ce11137399906f18bc8b25698b6e03a65034 +.. _PR#62: https://github.com/python-semantic-release/python-semantic-release/pull/62 + + +.. _changelog-v3.8.0: + +v3.8.0 (2016-03-21) +=================== + +✨ Features +----------- + +* Add ci checks for circle ci (`151d849`_) + +🪲 Bug Fixes +------------ + +* Add git fetch to frigg after success (`74a6cae`_) + +* Make tag parser work correctly with breaking changes (`9496f6a`_) + +* Refactoring cli.py to improve --help and error messages (`c79fc34`_) + +📖 Documentation +---------------- + +* Add info about correct commit guidelines (`af35413`_) + +* Add info about trello board in readme (`5229557`_) + +* Fix badges in readme (`7f4e549`_) + +* Update info about releases in contributing.md (`466f046`_) + +.. _151d849: https://github.com/python-semantic-release/python-semantic-release/commit/151d84964266c8dca206cef8912391cb73c8f206 +.. _466f046: https://github.com/python-semantic-release/python-semantic-release/commit/466f0460774cad86e7e828ffb50c7d1332b64e7b +.. _5229557: https://github.com/python-semantic-release/python-semantic-release/commit/5229557099d76b3404ea3677292332442a57ae2e +.. _74a6cae: https://github.com/python-semantic-release/python-semantic-release/commit/74a6cae2b46c5150e63136fde0599d98b9486e36 +.. _7f4e549: https://github.com/python-semantic-release/python-semantic-release/commit/7f4e5493edb6b3fb3510d0bb78fcc8d23434837f +.. _9496f6a: https://github.com/python-semantic-release/python-semantic-release/commit/9496f6a502c79ec3acb4e222e190e76264db02cf +.. _af35413: https://github.com/python-semantic-release/python-semantic-release/commit/af35413fae80889e2c5fc6b7d28f77f34b3b4c02 +.. _c79fc34: https://github.com/python-semantic-release/python-semantic-release/commit/c79fc3469fb99bf4c7f52434fa9c0891bca757f9 + + +.. _changelog-v3.7.2: + +v3.7.2 (2016-03-19) +=================== + +🪲 Bug Fixes +------------ + +* Move code around a bit to make flake8 happy (`41463b4`_) + +.. _41463b4: https://github.com/python-semantic-release/python-semantic-release/commit/41463b49b5d44fd94c11ab6e0a81e199510fabec + + +.. _changelog-v3.7.1: + +v3.7.1 (2016-03-15) +=================== + +📖 Documentation +---------------- + +* **configuration**: Fix typo in setup.cfg section (`725d87d`_) + +.. _725d87d: https://github.com/python-semantic-release/python-semantic-release/commit/725d87dc45857ef2f9fb331222845ac83a3af135 + + +.. _changelog-v3.7.0: + +v3.7.0 (2016-01-10) +=================== + +✨ Features +----------- + +* Add ci_checks for Frigg CI (`577c374`_) + +.. _577c374: https://github.com/python-semantic-release/python-semantic-release/commit/577c374396fe303b6fe7d64630d2959998d3595c + + +.. _changelog-v3.6.1: + +v3.6.1 (2016-01-10) +=================== + +🪲 Bug Fixes +------------ + +* Add requests as dependency (`4525a70`_) + +.. _4525a70: https://github.com/python-semantic-release/python-semantic-release/commit/4525a70d5520b44720d385b0307e46fae77a7463 + + +.. _changelog-v3.6.0: + +v3.6.0 (2015-12-28) +=================== + +✨ Features +----------- + +* Add checks for semaphore, closes `#44`_ (`2d7ef15`_) + +📖 Documentation +---------------- + +* Add documentation for configuring on CI (`7806940`_) + +* Add note about node semantic release (`0d2866c`_) + +* Add step by step guide for configuring travis ci (`6f23414`_) + +* Move automatic-releases to subfolder (`ed68e5b`_) + +* Remove duplicate readme (`42a9421`_) + +.. _#44: https://github.com/python-semantic-release/python-semantic-release/issues/44 +.. _0d2866c: https://github.com/python-semantic-release/python-semantic-release/commit/0d2866c528098ecaf1dd81492f28d3022a2a54e0 +.. _2d7ef15: https://github.com/python-semantic-release/python-semantic-release/commit/2d7ef157b1250459060e99601ec53a00942b6955 +.. _42a9421: https://github.com/python-semantic-release/python-semantic-release/commit/42a942131947cd1864c1ba29b184caf072408742 +.. _6f23414: https://github.com/python-semantic-release/python-semantic-release/commit/6f2341442f61f0284b1119a2c49e96f0be678929 +.. _7806940: https://github.com/python-semantic-release/python-semantic-release/commit/7806940ae36cb0d6ac0f966e5d6d911bd09a7d11 +.. _ed68e5b: https://github.com/python-semantic-release/python-semantic-release/commit/ed68e5b8d3489463e244b078ecce8eab2cba2bb1 + + +.. _changelog-v3.5.0: + +v3.5.0 (2015-12-22) +=================== + +✨ Features +----------- + +* Add author in commit, closes `#40`_ (`020efaa`_) + +* Checkout master before publishing (`dc4077a`_) + +🪲 Bug Fixes +------------ + +* Remove " from git push command (`031318b`_) + +📖 Documentation +---------------- + +* Convert readme to rst (`e8a8d26`_) + +.. _#40: https://github.com/python-semantic-release/python-semantic-release/issues/40 +.. _020efaa: https://github.com/python-semantic-release/python-semantic-release/commit/020efaaadf588e3fccd9d2f08a273c37e4158421 +.. _031318b: https://github.com/python-semantic-release/python-semantic-release/commit/031318b3268bc37e6847ec049b37425650cebec8 +.. _dc4077a: https://github.com/python-semantic-release/python-semantic-release/commit/dc4077a2d07e0522b625336dcf83ee4e0e1640aa +.. _e8a8d26: https://github.com/python-semantic-release/python-semantic-release/commit/e8a8d265aa2147824f18065b39a8e7821acb90ec + + +.. _changelog-v3.4.0: + +v3.4.0 (2015-12-22) +=================== + +✨ Features +----------- + +* Add travis environment checks (`f386db7`_) + +.. _f386db7: https://github.com/python-semantic-release/python-semantic-release/commit/f386db75b77acd521d2f5bde2e1dde99924dc096 + + +.. _changelog-v3.3.3: + +v3.3.3 (2015-12-22) +=================== + +🪲 Bug Fixes +------------ + +* Do git push and git push --tags instead of --follow-tags (`8bc70a1`_) + +.. _8bc70a1: https://github.com/python-semantic-release/python-semantic-release/commit/8bc70a183fd72f595c72702382bc0b7c3abe99c8 + + +.. _changelog-v3.3.2: + +v3.3.2 (2015-12-21) +=================== + +🪲 Bug Fixes +------------ + +* Change build badge (`0dc068f`_) + +📖 Documentation +---------------- + +* Update docstrings for generate_changelog (`987c6a9`_) + +.. _0dc068f: https://github.com/python-semantic-release/python-semantic-release/commit/0dc068fff2f8c6914f4abe6c4e5fb2752669159e +.. _987c6a9: https://github.com/python-semantic-release/python-semantic-release/commit/987c6a96d15997e38c93a9d841c618c76a385ce7 + + +.. _changelog-v3.3.1: + +v3.3.1 (2015-12-21) +=================== + +🪲 Bug Fixes +------------ + +* Add pandoc to travis settings (`17d40a7`_) + +* Only list commits from the last version tag, closes `#28`_ (`191369e`_) + +.. _#28: https://github.com/python-semantic-release/python-semantic-release/issues/28 +.. _17d40a7: https://github.com/python-semantic-release/python-semantic-release/commit/17d40a73062ffa774542d0abc0f59fc16b68be37 +.. _191369e: https://github.com/python-semantic-release/python-semantic-release/commit/191369ebd68526e5b1afcf563f7d13e18c8ca8bf + + +.. _changelog-v3.3.0: + +v3.3.0 (2015-12-20) +=================== + +✨ Features +----------- + +* Add support for environment variables for pypi credentials (`3b383b9`_) + +🪲 Bug Fixes +------------ + +* Add missing parameters to twine.upload (`4bae22b`_) + +* Better filtering of github token in push error (`9b31da4`_) + +* Downgrade twine to version 1.5.0 (`66df378`_) + +* Make sure the github token is not in the output (`55356b7`_) + +* Push to master by default (`a0bb023`_) + +.. _3b383b9: https://github.com/python-semantic-release/python-semantic-release/commit/3b383b92376a7530e89b11de481c4dfdfa273f7b +.. _4bae22b: https://github.com/python-semantic-release/python-semantic-release/commit/4bae22bae9b9d9abf669b028ea3af4b3813a1df0 +.. _55356b7: https://github.com/python-semantic-release/python-semantic-release/commit/55356b718f74d94dd92e6c2db8a15423a6824eb5 +.. _66df378: https://github.com/python-semantic-release/python-semantic-release/commit/66df378330448a313aff7a7c27067adda018904f +.. _9b31da4: https://github.com/python-semantic-release/python-semantic-release/commit/9b31da4dc27edfb01f685e6036ddbd4c715c9f60 +.. _a0bb023: https://github.com/python-semantic-release/python-semantic-release/commit/a0bb023438a1503f9fdb690d976d71632f19a21f + + +.. _changelog-v3.2.1: + +v3.2.1 (2015-12-20) +=================== + +🪲 Bug Fixes +------------ + +* Add requirements to manifest (`ed25ecb`_) + +* **pypi**: Add sdist as default in addition to bdist_wheel (`a1a35f4`_) + +.. _a1a35f4: https://github.com/python-semantic-release/python-semantic-release/commit/a1a35f43175187091f028474db2ebef5bfc77bc0 +.. _ed25ecb: https://github.com/python-semantic-release/python-semantic-release/commit/ed25ecbaeec0e20ad3040452a5547bb7d6faf6ad + + +.. _changelog-v3.2.0: + +v3.2.0 (2015-12-20) +=================== + +✨ Features +----------- + +* **angular-parser**: Remove scope requirement (`90c9d8d`_) + +* **git**: Add push to GH_TOKEN@github-url (`546b5bf`_) + +🪲 Bug Fixes +------------ + +* **deps**: Use one file for requirements (`4868543`_) + +.. _4868543: https://github.com/python-semantic-release/python-semantic-release/commit/486854393b24803bb2356324e045ccab17510d46 +.. _546b5bf: https://github.com/python-semantic-release/python-semantic-release/commit/546b5bf15466c6f5dfe93c1c03ca34604b0326f2 +.. _90c9d8d: https://github.com/python-semantic-release/python-semantic-release/commit/90c9d8d4cd6d43be094cda86579e00b507571f98 + + +.. _changelog-v3.1.0: + +v3.1.0 (2015-08-31) +=================== + +✨ Features +----------- + +* **pypi**: Add option to disable pypi upload (`f5cd079`_) + +.. _f5cd079: https://github.com/python-semantic-release/python-semantic-release/commit/f5cd079edb219de5ad03a71448d578f5f477da9c + + +.. _changelog-v3.0.0: + +v3.0.0 (2015-08-25) +=================== + +✨ Features +----------- + +* **parser**: Add tag parser (`a7f392f`_) + +🪲 Bug Fixes +------------ + +* **errors**: Add exposing of errors in package (`3662d76`_) + +* **version**: Parse file instead for version (`005dba0`_) + +.. _005dba0: https://github.com/python-semantic-release/python-semantic-release/commit/005dba0094eeb4098315ef383a746e139ffb504d +.. _3662d76: https://github.com/python-semantic-release/python-semantic-release/commit/3662d7663291859dd58a91b4b4ccde4f0edc99b2 +.. _a7f392f: https://github.com/python-semantic-release/python-semantic-release/commit/a7f392fd4524cc9207899075631032e438e2593c + + +.. _changelog-v2.1.4: + +v2.1.4 (2015-08-24) +=================== + +🪲 Bug Fixes +------------ + +* **github**: Fix property calls (`7ecdeb2`_) + +.. _7ecdeb2: https://github.com/python-semantic-release/python-semantic-release/commit/7ecdeb22de96b6b55c5404ebf54a751911c4d8cd + + +.. _changelog-v2.1.3: + +v2.1.3 (2015-08-22) +=================== + +🪲 Bug Fixes +------------ + +* **hvcs**: Make Github.token an property (`37d5e31`_) + +📖 Documentation +---------------- + +* **api**: Update apidocs (`6185380`_) + +* **parsers**: Add documentation about commit parsers (`9b55422`_) + +* **readme**: Update readme with information about the changelog command (`56a745e`_) + +.. _37d5e31: https://github.com/python-semantic-release/python-semantic-release/commit/37d5e3110397596a036def5f1dccf0860964332c +.. _56a745e: https://github.com/python-semantic-release/python-semantic-release/commit/56a745ef6fa4edf6f6ba09c78fcc141102cf2871 +.. _6185380: https://github.com/python-semantic-release/python-semantic-release/commit/6185380babedbbeab2a2a342f17b4ff3d4df6768 +.. _9b55422: https://github.com/python-semantic-release/python-semantic-release/commit/9b554222768036024a133153a559cdfc017c1d91 + + +.. _changelog-v2.1.2: + +v2.1.2 (2015-08-20) +=================== + +🪲 Bug Fixes +------------ + +* **cli**: Fix call to generate_changelog in publish (`5f8bce4`_) + +.. _5f8bce4: https://github.com/python-semantic-release/python-semantic-release/commit/5f8bce4cbb5e1729e674efd6c651e2531aea2a16 + + +.. _changelog-v2.1.1: + +v2.1.1 (2015-08-20) +=================== + +🪲 Bug Fixes +------------ + +* **history**: Fix issue in get_previous_version (`f961786`_) + +.. _f961786: https://github.com/python-semantic-release/python-semantic-release/commit/f961786aa3eaa3a620f47cc09243340fd329b9c2 + + +.. _changelog-v2.1.0: + +v2.1.0 (2015-08-20) +=================== + +✨ Features +----------- + +* **cli**: Add the possibility to repost the changelog (`4d028e2`_) + +🪲 Bug Fixes +------------ + +* **cli**: Fix check of token in changelog command (`cc6e6ab`_) + +* **github**: Fix the github releases integration (`f0c3c1d`_) + +* **history**: Fix changelog generation (`f010272`_) + +.. _4d028e2: https://github.com/python-semantic-release/python-semantic-release/commit/4d028e21b9da01be8caac8f23f2c11e0c087e485 +.. _cc6e6ab: https://github.com/python-semantic-release/python-semantic-release/commit/cc6e6abe1e91d3aa24e8d73e704829669bea5fd7 +.. _f010272: https://github.com/python-semantic-release/python-semantic-release/commit/f01027203a8ca69d21b4aff689e60e8c8d6f9af5 +.. _f0c3c1d: https://github.com/python-semantic-release/python-semantic-release/commit/f0c3c1db97752b71f2153ae9f623501b0b8e2c98 + + +.. _changelog-v2.0.0: + +v2.0.0 (2015-08-19) +=================== + +✨ Features +----------- + +* **cli**: Add command for printing the changelog (`336b8bc`_) + +* **github**: Add github release changelog helper (`da18795`_) + +* **history**: Add angular parser (`91e4f0f`_) + +* **history**: Add generate_changelog function (`347f21a`_) + +* **history**: Add markdown changelog formatter (`d77b58d`_) + +* **history**: Set angular parser as the default (`c2cf537`_) + +* **publish**: Add publishing of changelog to github (`74324ba`_) + +* **settings**: Add loading of current parser (`7bd0916`_) + +🪲 Bug Fixes +------------ + +* **cli**: Change output indentation on changelog (`2ca41d3`_) + +* **history**: Fix level id's in angular parser (`2918d75`_) + +* **history**: Fix regex in angular parser (`974ccda`_) + +* **history**: Support unexpected types in changelog generator (`13deacf`_) + +💥 BREAKING CHANGES +------------------- + +* **history**: The default parser is now angular. Thus, the default behaviour of the commit log + evaluator will change. From now on it will use the angular commit message spec to determine the + new version. + +.. _13deacf: https://github.com/python-semantic-release/python-semantic-release/commit/13deacf5d33ed500e4e94ea702a2a16be2aa7c48 +.. _2918d75: https://github.com/python-semantic-release/python-semantic-release/commit/2918d759bf462082280ede971a5222fe01634ed8 +.. _2ca41d3: https://github.com/python-semantic-release/python-semantic-release/commit/2ca41d3bd1b8b9d9fe7e162772560e3defe2a41e +.. _336b8bc: https://github.com/python-semantic-release/python-semantic-release/commit/336b8bcc01fc1029ff37a79c92935d4b8ea69203 +.. _347f21a: https://github.com/python-semantic-release/python-semantic-release/commit/347f21a1f8d655a71a0e7d58b64d4c6bc6d0bf31 +.. _74324ba: https://github.com/python-semantic-release/python-semantic-release/commit/74324ba2749cdbbe80a92b5abbecfeab04617699 +.. _7bd0916: https://github.com/python-semantic-release/python-semantic-release/commit/7bd0916f87a1f9fe839c853eab05cae1af420cd2 +.. _91e4f0f: https://github.com/python-semantic-release/python-semantic-release/commit/91e4f0f4269d01b255efcd6d7121bbfd5a682e12 +.. _974ccda: https://github.com/python-semantic-release/python-semantic-release/commit/974ccdad392d768af5e187dabc184be9ac3e133d +.. _c2cf537: https://github.com/python-semantic-release/python-semantic-release/commit/c2cf537a42beaa60cd372c7c9f8fb45db8085917 +.. _d77b58d: https://github.com/python-semantic-release/python-semantic-release/commit/d77b58db4b66aec94200dccab94f483def4dacc9 +.. _da18795: https://github.com/python-semantic-release/python-semantic-release/commit/da187951af31f377ac57fe17462551cfd776dc6e + + +.. _changelog-v1.0.0: + +v1.0.0 (2015-08-04) +=================== + +💥 Breaking +----------- + +* Restructure helpers into history and pypi (`00f64e6`_) + +📖 Documentation +---------------- + +* Add automatic publishing documentation, resolves `#18`_ (`58076e6`_) + +.. _#18: https://github.com/python-semantic-release/python-semantic-release/issues/18 +.. _00f64e6: https://github.com/python-semantic-release/python-semantic-release/commit/00f64e623db0e21470d55488c5081e12d6c11fd3 +.. _58076e6: https://github.com/python-semantic-release/python-semantic-release/commit/58076e60bf20a5835b112b5e99a86c7425ffe7d9 + + +.. _changelog-v0.9.1: + +v0.9.1 (2015-08-04) +=================== + +🪲 Bug Fixes +------------ + +* Fix ``get_current_head_hash`` to ensure it only returns the hash (`7c28832`_) + +.. _7c28832: https://github.com/python-semantic-release/python-semantic-release/commit/7c2883209e5bf4a568de60dbdbfc3741d34f38b4 + + +.. _changelog-v0.9.0: + +v0.9.0 (2015-08-03) +=================== + +✨ Features +----------- + +* Add Python 2.7 support, resolves `#10`_ (`c05e13f`_) + +.. _#10: https://github.com/python-semantic-release/python-semantic-release/issues/10 +.. _c05e13f: https://github.com/python-semantic-release/python-semantic-release/commit/c05e13f22163237e963c493ffeda7e140f0202c6 + + +.. _changelog-v0.8.0: + +v0.8.0 (2015-08-03) +=================== + +✨ Features +----------- + +* Add ``check_build_status`` option, resolves `#5`_ (`310bb93`_) + +* Add ``get_current_head_hash`` in git helpers (`d864282`_) + +* Add git helper to get owner and name of repo (`f940b43`_) + +.. _#5: https://github.com/python-semantic-release/python-semantic-release/issues/5 +.. _310bb93: https://github.com/python-semantic-release/python-semantic-release/commit/310bb9371673fcf9b7b7be48422b89ab99753f04 +.. _d864282: https://github.com/python-semantic-release/python-semantic-release/commit/d864282c498f0025224407b3eeac69522c2a7ca0 +.. _f940b43: https://github.com/python-semantic-release/python-semantic-release/commit/f940b435537a3c93ab06170d4a57287546bd8d3b + + +.. _changelog-v0.7.0: + +v0.7.0 (2015-08-02) +=================== + +✨ Features +----------- + +* Add ``patch_without_tag`` option, resolves `#6`_ (`3734a88`_) + +📖 Documentation +---------------- + +* Set up sphinx based documentation, resolves `#1`_ (`41fba78`_) + +.. _#1: https://github.com/python-semantic-release/python-semantic-release/issues/1 +.. _#6: https://github.com/python-semantic-release/python-semantic-release/issues/6 +.. _3734a88: https://github.com/python-semantic-release/python-semantic-release/commit/3734a889f753f1b9023876e100031be6475a90d1 +.. _41fba78: https://github.com/python-semantic-release/python-semantic-release/commit/41fba78a389a8d841316946757a23a7570763c39 + + +.. _changelog-v0.6.0: + +v0.6.0 (2015-08-02) +=================== + +✨ Features +----------- + +* Add twine for uploads to pypi, resolves `#13`_ (`eec2561`_) + +.. _#13: https://github.com/python-semantic-release/python-semantic-release/issues/13 +.. _eec2561: https://github.com/python-semantic-release/python-semantic-release/commit/eec256115b28b0a18136a26d74cfc3232502f1a6 + + +.. _changelog-v0.5.4: + +v0.5.4 (2015-07-29) +=================== + +🪲 Bug Fixes +------------ + +* Add python2 not supported warning (`e84c4d8`_) + +.. _e84c4d8: https://github.com/python-semantic-release/python-semantic-release/commit/e84c4d8b6f212aec174baccd188185627b5039b6 + + +.. _changelog-v0.5.3: + +v0.5.3 (2015-07-28) +=================== + +⚙️ Build System +--------------- + +* Add ``wheel`` as a dependency (`971e479`_) + +.. _971e479: https://github.com/python-semantic-release/python-semantic-release/commit/971e4795a8b8fea371fcc02dc9221f58a0559f32 + + +.. _changelog-v0.5.2: + +v0.5.2 (2015-07-28) +=================== + +🪲 Bug Fixes +------------ + +* Fix python wheel tag (`f9ac163`_) + +.. _f9ac163: https://github.com/python-semantic-release/python-semantic-release/commit/f9ac163491666022c809ad49846f3c61966e10c1 + + +.. _changelog-v0.5.1: + +v0.5.1 (2015-07-28) +=================== + +🪲 Bug Fixes +------------ + +* Fix push commands (`8374ef6`_) + +.. _8374ef6: https://github.com/python-semantic-release/python-semantic-release/commit/8374ef6bd78eb564a6d846b882c99a67e116394e + + +.. _changelog-v0.5.0: + +v0.5.0 (2015-07-28) +=================== + +✨ Features +----------- + +* Add setup.py hook for the cli interface (`c363bc5`_) + +.. _c363bc5: https://github.com/python-semantic-release/python-semantic-release/commit/c363bc5d3cb9e9a113de3cd0c49dd54a5ea9cf35 + + +.. _changelog-v0.4.0: + +v0.4.0 (2015-07-28) +=================== + +✨ Features +----------- + +* Add publish command (`d8116c9`_) + +.. _d8116c9: https://github.com/python-semantic-release/python-semantic-release/commit/d8116c9dec472d0007973939363388d598697784 + + +.. _changelog-v0.3.2: + +v0.3.2 (2015-07-28) +=================== + +* No change + + +.. _changelog-v0.3.1: + +v0.3.1 (2015-07-28) +=================== + +🪲 Bug Fixes +------------ + +* Fix wheel settings (`1e860e8`_) + +.. _1e860e8: https://github.com/python-semantic-release/python-semantic-release/commit/1e860e8a4d9ec580449a0b87be9660a9482fa2a4 + + +.. _changelog-v0.3.0: + +v0.3.0 (2015-07-27) +=================== + +✨ Features +----------- + +* Add support for tagging releases (`5f4736f`_) + +🪲 Bug Fixes +------------ + +* Fix issue when version should not change (`441798a`_) + +.. _441798a: https://github.com/python-semantic-release/python-semantic-release/commit/441798a223195138c0d3d2c51fc916137fef9a6c +.. _5f4736f: https://github.com/python-semantic-release/python-semantic-release/commit/5f4736f4e41bc96d36caa76ca58be0e1e7931069 + + +.. _changelog-v0.2.0: + +v0.2.0 (2015-07-27) +=================== + +✨ Features +----------- + +* added no-operation (``--noop``) mode (`44c2039`_) + +⚙️ Build System +--------------- + +* Swapped pygit2 with gitpython to avoid libgit2 dependency (`8165a2e`_) + +.. _44c2039: https://github.com/python-semantic-release/python-semantic-release/commit/44c203989aabc9366ba42ed2bc40eaccd7ac891c +.. _8165a2e: https://github.com/python-semantic-release/python-semantic-release/commit/8165a2eef2c6eea88bfa52e6db37abc7374cccba + + +.. _changelog-v0.1.1: + +v0.1.1 (2015-07-27) +=================== + +🪲 Bug Fixes +------------ + +* Fix entry point (`bd7ce7f`_) + +.. _bd7ce7f: https://github.com/python-semantic-release/python-semantic-release/commit/bd7ce7f47c49e2027767fb770024a0d4033299fa + + +.. _changelog-v0.1.0: + +v0.1.0 (2015-07-27) +=================== + +* Initial Release diff --git a/config/release-templates/.components/changelog_1.0.0.rst.j2 b/config/release-templates/.components/changelog_1.0.0.rst.j2 new file mode 100644 index 000000000..303d6b183 --- /dev/null +++ b/config/release-templates/.components/changelog_1.0.0.rst.j2 @@ -0,0 +1,254 @@ +{# + This file overrides what would be generated normally because the commits are + not conformative to the standard commit message format. +#} +.. _changelog-v1.0.0: + +v1.0.0 (2015-08-04) +=================== + +💥 Breaking +----------- + +* Restructure helpers into history and pypi (`00f64e6`_) + +📖 Documentation +---------------- + +* Add automatic publishing documentation, resolves `#18`_ (`58076e6`_) + +.. _#18: https://github.com/python-semantic-release/python-semantic-release/issues/18 +.. _00f64e6: https://github.com/python-semantic-release/python-semantic-release/commit/00f64e623db0e21470d55488c5081e12d6c11fd3 +.. _58076e6: https://github.com/python-semantic-release/python-semantic-release/commit/58076e60bf20a5835b112b5e99a86c7425ffe7d9 + + +.. _changelog-v0.9.1: + +v0.9.1 (2015-08-04) +=================== + +🪲 Bug Fixes +------------ + +* Fix ``get_current_head_hash`` to ensure it only returns the hash (`7c28832`_) + +.. _7c28832: https://github.com/python-semantic-release/python-semantic-release/commit/7c2883209e5bf4a568de60dbdbfc3741d34f38b4 + + +.. _changelog-v0.9.0: + +v0.9.0 (2015-08-03) +=================== + +✨ Features +----------- + +* Add Python 2.7 support, resolves `#10`_ (`c05e13f`_) + +.. _#10: https://github.com/python-semantic-release/python-semantic-release/issues/10 +.. _c05e13f: https://github.com/python-semantic-release/python-semantic-release/commit/c05e13f22163237e963c493ffeda7e140f0202c6 + + +.. _changelog-v0.8.0: + +v0.8.0 (2015-08-03) +=================== + +✨ Features +----------- + +* Add ``check_build_status`` option, resolves `#5`_ (`310bb93`_) + +* Add ``get_current_head_hash`` in git helpers (`d864282`_) + +* Add git helper to get owner and name of repo (`f940b43`_) + +.. _#5: https://github.com/python-semantic-release/python-semantic-release/issues/5 +.. _310bb93: https://github.com/python-semantic-release/python-semantic-release/commit/310bb9371673fcf9b7b7be48422b89ab99753f04 +.. _d864282: https://github.com/python-semantic-release/python-semantic-release/commit/d864282c498f0025224407b3eeac69522c2a7ca0 +.. _f940b43: https://github.com/python-semantic-release/python-semantic-release/commit/f940b435537a3c93ab06170d4a57287546bd8d3b + + +.. _changelog-v0.7.0: + +v0.7.0 (2015-08-02) +=================== + +✨ Features +----------- + +* Add ``patch_without_tag`` option, resolves `#6`_ (`3734a88`_) + +📖 Documentation +---------------- + +* Set up sphinx based documentation, resolves `#1`_ (`41fba78`_) + +.. _#1: https://github.com/python-semantic-release/python-semantic-release/issues/1 +.. _#6: https://github.com/python-semantic-release/python-semantic-release/issues/6 +.. _3734a88: https://github.com/python-semantic-release/python-semantic-release/commit/3734a889f753f1b9023876e100031be6475a90d1 +.. _41fba78: https://github.com/python-semantic-release/python-semantic-release/commit/41fba78a389a8d841316946757a23a7570763c39 + + +.. _changelog-v0.6.0: + +v0.6.0 (2015-08-02) +=================== + +✨ Features +----------- + +* Add twine for uploads to pypi, resolves `#13`_ (`eec2561`_) + +.. _#13: https://github.com/python-semantic-release/python-semantic-release/issues/13 +.. _eec2561: https://github.com/python-semantic-release/python-semantic-release/commit/eec256115b28b0a18136a26d74cfc3232502f1a6 + + +.. _changelog-v0.5.4: + +v0.5.4 (2015-07-29) +=================== + +🪲 Bug Fixes +------------ + +* Add python2 not supported warning (`e84c4d8`_) + +.. _e84c4d8: https://github.com/python-semantic-release/python-semantic-release/commit/e84c4d8b6f212aec174baccd188185627b5039b6 + + +.. _changelog-v0.5.3: + +v0.5.3 (2015-07-28) +=================== + +⚙️ Build System +--------------- + +* Add ``wheel`` as a dependency (`971e479`_) + +.. _971e479: https://github.com/python-semantic-release/python-semantic-release/commit/971e4795a8b8fea371fcc02dc9221f58a0559f32 + + +.. _changelog-v0.5.2: + +v0.5.2 (2015-07-28) +=================== + +🪲 Bug Fixes +------------ + +* Fix python wheel tag (`f9ac163`_) + +.. _f9ac163: https://github.com/python-semantic-release/python-semantic-release/commit/f9ac163491666022c809ad49846f3c61966e10c1 + + +.. _changelog-v0.5.1: + +v0.5.1 (2015-07-28) +=================== + +🪲 Bug Fixes +------------ + +* Fix push commands (`8374ef6`_) + +.. _8374ef6: https://github.com/python-semantic-release/python-semantic-release/commit/8374ef6bd78eb564a6d846b882c99a67e116394e + + +.. _changelog-v0.5.0: + +v0.5.0 (2015-07-28) +=================== + +✨ Features +----------- + +* Add setup.py hook for the cli interface (`c363bc5`_) + +.. _c363bc5: https://github.com/python-semantic-release/python-semantic-release/commit/c363bc5d3cb9e9a113de3cd0c49dd54a5ea9cf35 + + +.. _changelog-v0.4.0: + +v0.4.0 (2015-07-28) +=================== + +✨ Features +----------- + +* Add publish command (`d8116c9`_) + +.. _d8116c9: https://github.com/python-semantic-release/python-semantic-release/commit/d8116c9dec472d0007973939363388d598697784 + + +.. _changelog-v0.3.2: + +v0.3.2 (2015-07-28) +=================== + +* No change + + +.. _changelog-v0.3.1: + +v0.3.1 (2015-07-28) +=================== + +🪲 Bug Fixes +------------ + +* Fix wheel settings (`1e860e8`_) + +.. _1e860e8: https://github.com/python-semantic-release/python-semantic-release/commit/1e860e8a4d9ec580449a0b87be9660a9482fa2a4 + + +.. _changelog-v0.3.0: + +v0.3.0 (2015-07-27) +=================== + +✨ Features +----------- + +* Add support for tagging releases (`5f4736f`_) + +🪲 Bug Fixes +------------ + +* Fix issue when version should not change (`441798a`_) + +.. _441798a: https://github.com/python-semantic-release/python-semantic-release/commit/441798a223195138c0d3d2c51fc916137fef9a6c +.. _5f4736f: https://github.com/python-semantic-release/python-semantic-release/commit/5f4736f4e41bc96d36caa76ca58be0e1e7931069 + + +.. _changelog-v0.2.0: + +v0.2.0 (2015-07-27) +=================== + +✨ Features +----------- + +* added no-operation (``--noop``) mode (`44c2039`_) + +⚙️ Build System +--------------- + +* Swapped pygit2 with gitpython to avoid libgit2 dependency (`8165a2e`_) + +.. _44c2039: https://github.com/python-semantic-release/python-semantic-release/commit/44c203989aabc9366ba42ed2bc40eaccd7ac891c +.. _8165a2e: https://github.com/python-semantic-release/python-semantic-release/commit/8165a2eef2c6eea88bfa52e6db37abc7374cccba + + +.. _changelog-v0.1.1: + +v0.1.1 (2015-07-27) +=================== + +🪲 Bug Fixes +------------ + +* Fix entry point (`bd7ce7f`_) + +.. _bd7ce7f: https://github.com/python-semantic-release/python-semantic-release/commit/bd7ce7f47c49e2027767fb770024a0d4033299fa diff --git a/config/release-templates/.components/changelog_header.rst.j2 b/config/release-templates/.components/changelog_header.rst.j2 new file mode 100644 index 000000000..829078cb0 --- /dev/null +++ b/config/release-templates/.components/changelog_header.rst.j2 @@ -0,0 +1,10 @@ +.. _changelog: + +{% if ctx.changelog_mode == "update" +%}{# # Modified insertion flag to insert a changelog header directly + # which convienently puts the insertion flag incognito when reading raw RST +#}{{ + insertion_flag ~ "\n" + +}}{% endif +%} diff --git a/config/release-templates/.components/changelog_init.rst.j2 b/config/release-templates/.components/changelog_init.rst.j2 new file mode 100644 index 000000000..5cfa3b008 --- /dev/null +++ b/config/release-templates/.components/changelog_init.rst.j2 @@ -0,0 +1,39 @@ +{# +This changelog template initializes a full changelog for the project, +it follows the following logic: + 1. Header + 2. Any Unreleased Details (uncommon) + 3. all previous releases except the very first release + 4. the first release + +#}{# + # # Header +#}{% include "changelog_header.rst.j2" +-%}{# + # # Any Unreleased Details (uncommon) +#}{% include "unreleased_changes.rst.j2" +-%}{# + # # Since this is initialization, we are generating all the previous + # # release notes per version. The very first release notes is specialized. + # # We also have non-conformative commits, so insert manual write-ups. +#}{% if releases | length > 0 +%}{% for release in releases +%}{% if loop.last +%}{{ "\n" +}}{% include "first_release.rst.j2" +-%}{{ "\n" +}}{# +#}{% elif release.version == "1.0.0" +%}{# # Append 0.1.1 through 1.0.0 non-generated changelog only once +#}{{ "\n" +}}{% include "changelog_1.0.0.rst.j2" +-%}{{ "\n\n" +}}{# +#}{% elif release.version > "1.0.0" +%}{{ "\n" +}}{% include "versioned_changes.rst.j2" +-%}{{ "\n" +}}{% endif +%}{% endfor +%}{% endif +%} diff --git a/config/release-templates/.components/changelog_update.rst.j2 b/config/release-templates/.components/changelog_update.rst.j2 new file mode 100644 index 000000000..002c45d50 --- /dev/null +++ b/config/release-templates/.components/changelog_update.rst.j2 @@ -0,0 +1,71 @@ +{# +This Update changelog template uses the following logic: + + 1. Read previous changelog file (ex. project_root/CHANGELOG.md) + 2. Split on insertion flag (ex. ) + 3. Print top half of previous changelog + 3. New Changes (unreleased commits & newly released) + 4. Print bottom half of previous changelog + + Note: if a previous file was not found, it does not write anything at the bottom + but render does NOT fail + +#}{% set prev_changelog_contents = prev_changelog_file | read_file | safe +%}{% set changelog_parts = prev_changelog_contents.split(insertion_flag, maxsplit=1) +%}{# +#}{% if changelog_parts | length < 2 +%}{# # insertion flag was not found, check if the file was empty or did not exist +#}{% if prev_changelog_contents | length > 0 +%}{# # File has content but no insertion flag, therefore, file will not be updated +#}{{ changelog_parts[0] +}}{% else +%}{# # File was empty or did not exist, therefore, it will be created from scratch +#}{% include "changelog_init.rst.j2" +%}{% endif +%}{% else +%}{# + # Previous Changelog Header + # - Depending if there is header content, then it will separate the insertion flag + # with a newline from header content, otherwise it will just print the insertion flag +#}{% set prev_changelog_top = changelog_parts[0] | trim +%}{% if prev_changelog_top | length > 0 +%}{{ + "%s\n\n%s\n" | format(prev_changelog_top, insertion_flag | trim) + +}}{% else +%}{{ + "%s\n" | format(insertion_flag | trim) + +}}{% endif +%}{# + # Any Unreleased Details (uncommon) +#}{% include "unreleased_changes.rst.j2" +-%}{# +#}{% if releases | length > 0 +%}{# # Latest Release Details +#}{% set release = releases[0] +%}{# +#}{% if releases | length == 1 and ctx.mask_initial_release +%}{# # First Release detected +#}{{ "\n" +}}{%- include "first_release.rst.j2" +-%}{{ "\n" +}}{# +#}{% elif release.version.as_semver_tag() ~ " (" not in changelog_parts[1] +%}{# # The release version is not already in the changelog so we add it +#}{{ "\n" +}}{%- include "versioned_changes.rst.j2" +-%}{{ "\n" +}}{# +#}{% endif +%}{% endif +%}{# + # Previous Changelog Footer + # - skips printing footer if empty, which happens when the insertion_flag + # was at the end of the file (ignoring whitespace) +#}{% set previous_changelog_bottom = changelog_parts[1] | trim +%}{% if previous_changelog_bottom | length > 0 +%}{{ "\n%s\n" | format(previous_changelog_bottom) +}}{% endif +%}{% endif +%} diff --git a/config/release-templates/.components/changes.md.j2 b/config/release-templates/.components/changes.md.j2 new file mode 100644 index 000000000..8a5f38f3c --- /dev/null +++ b/config/release-templates/.components/changes.md.j2 @@ -0,0 +1,88 @@ +{% from 'macros.md.j2' import apply_alphabetical_ordering_by_brk_descriptions +%}{% from 'macros.md.j2' import apply_alphabetical_ordering_by_descriptions +%}{% from 'macros.md.j2' import emoji_map, format_breaking_changes_description +%}{% from 'macros.md.j2' import format_commit_summary_line +%}{% from 'macros.md.j2' import section_heading_order, section_heading_translations +%}{# +EXAMPLE: + +### ✨ Features + +- Add new feature ([#10](https://domain.com/namespace/repo/pull/10), + [`abcdef0`](https://domain.com/namespace/repo/commit/HASH)) + +- **scope**: Add new feature ([`abcdef0`](https://domain.com/namespace/repo/commit/HASH)) + +### 🪲 Bug Fixes + +- Fix bug ([#11](https://domain.com/namespace/repo/pull/11), + [`abcdef1`](https://domain.com/namespace/repo/commit/HASH)) + +### 💥 BREAKING CHANGES + +- With the change _____, the change causes ___ effect. Ultimately, this section + it is a more detailed description of the breaking change. With an optional + scope prefix like the commit messages above. + +- **scope**: this breaking change has a scope to identify the part of the code that + this breaking change applies to for better context. + +#}{% set max_line_width = max_line_width | default(100) +%}{% set hanging_indent = hanging_indent | default(2) +%}{# +#}{% for type_ in section_heading_order if type_ in commit_objects +%}{# PREPROCESS COMMITS (order by description & format description line) +#}{% set ns = namespace(commits=commit_objects[type_]) +%}{% set _ = apply_alphabetical_ordering_by_descriptions(ns) +%}{# +#}{% set commit_descriptions = [] +%}{# +#}{% for commit in ns.commits +%}{# # Generate the commit summary line and format it for Markdown +#}{% set description = "- %s" | format(format_commit_summary_line(commit)) +%}{% set description = description | autofit_text_width(max_line_width, hanging_indent) +%}{% set _ = commit_descriptions.append(description) +%}{% endfor +%}{# + # # PRINT SECTION (header & commits) +#}{{ "\n" +}}{{ "### %s %s\n" | format(emoji_map[type_], type_ | title) +}}{{ "\n" +}}{{ "%s\n" | format(commit_descriptions | unique | join("\n\n")) +}}{% endfor +%}{# + # # Determine if any commits have a breaking change description + # # commit_objects is a dictionary of strings to a list of commits { "features", [ParsedCommit(), ...] } +#}{% set breaking_commits = [] +%}{% for commits in commit_objects.values() +%}{% set valid_commits = commits | rejectattr("error", "defined") +%}{# # Filter out breaking change commits that have no breaking descriptions +#}{% set _ = breaking_commits.extend( + valid_commits | selectattr("breaking_descriptions.0") + ) +%}{% endfor +%}{# +#}{% if breaking_commits | length > 0 +%}{# PREPROCESS COMMITS +#}{% set brk_ns = namespace(commits=breaking_commits) +%}{% set _ = apply_alphabetical_ordering_by_brk_descriptions(brk_ns) +%}{# +#}{% set brking_descriptions = [] +%}{# +#}{% for commit in brk_ns.commits +%}{% set full_description = "- %s" | format( + format_breaking_changes_description(commit).split("\n\n") | join("\n\n- ") + ) +%}{% set _ = brking_descriptions.append( + full_description | autofit_text_width(max_line_width, hanging_indent) + ) +%}{% endfor +%}{# + # # PRINT BREAKING CHANGE DESCRIPTIONS (header & descriptions) +#}{{ "\n" +}}{{ "### %s BREAKING CHANGES\n" | format(emoji_map["breaking"]) +}}{{ + "\n%s\n" | format(brking_descriptions | unique | join("\n\n")) +}}{# +#}{% endif +%} diff --git a/config/release-templates/.components/changes.rst.j2 b/config/release-templates/.components/changes.rst.j2 new file mode 100644 index 000000000..e0931376d --- /dev/null +++ b/config/release-templates/.components/changes.rst.j2 @@ -0,0 +1,133 @@ +{% from 'macros.rst.j2' import apply_alphabetical_ordering_by_brk_descriptions +%}{% from 'macros.rst.j2' import apply_alphabetical_ordering_by_descriptions +%}{% from 'macros.rst.j2' import emoji_map, extract_issue_link_references, extract_pr_link_reference +%}{% from 'macros.rst.j2' import format_breaking_changes_description, format_commit_summary_line +%}{% from 'macros.rst.j2' import format_link_reference +%}{% from 'macros.rst.j2' import generate_heading_underline, section_heading_order +%}{% from 'macros.rst.j2' import section_heading_translations +%}{# + +✨ Features +----------- + +* Add new feature (`#10`_, `8a7b8ec`_) + +* **scope**: Add another feature (`abcdef0`_) + +🪲 Bug Fixes +------------ + +* Fix bug (`#11`_, `8a7b8ec`_) + +💥 BREAKING CHANGES +------------------- + +* With the change _____, the change causes ___ effect. Ultimately, this section + it is a more detailed description of the breaking change. With an optional + scope prefix like the commit messages above. + +* **scope**: this breaking change has a scope to identify the part of the code that + this breaking change applies to for better context. + +.. _8a7B8ec: https://domain.com/owner/repo/commit/8a7b8ec +.. _abcdef0: https://domain.com/owner/repo/commit/abcdef0 +.. _PR#10: https://domain.com/namespace/repo/pull/10 +.. _PR#11: https://domain.com/namespace/repo/pull/11 + +#}{% set max_line_width = max_line_width | default(100) +%}{% set hanging_indent = hanging_indent | default(2) +%}{# +#}{% set post_paragraph_links = [] +%}{# +#}{% for type_ in section_heading_order if type_ in commit_objects +%}{# # PREPARE SECTION HEADER +#}{% set section_header = "%s %s" | format( + emoji_map[type_], type_ | title + ) +%}{# + # # PREPROCESS COMMITS +#}{% set ns = namespace(commits=commit_objects[type_]) +%}{% set _ = apply_alphabetical_ordering_by_descriptions(ns) +%}{# +#}{% set commit_descriptions = [] +%}{# +#}{% for commit in ns.commits +%}{# # Extract PR/MR reference if it exists and store it for later +#}{% set pr_link_reference = extract_pr_link_reference(commit) | default("", true) +%}{% if pr_link_reference != "" +%}{% set _ = post_paragraph_links.append(pr_link_reference) +%}{% endif +%}{# + # # Extract Issue references if they exists and store it for later +#}{% set issue_urls_ns = namespace(urls=[]) +%}{% set _ = extract_issue_link_references(issue_urls_ns, commit) +%}{% set _ = post_paragraph_links.extend(issue_urls_ns.urls) +%}{# + # # Always generate a commit hash reference link and store it for later +#}{% set commit_hash_link_reference = format_link_reference( + commit.hexsha | commit_hash_url, + commit.short_hash + ) +%}{% set _ = post_paragraph_links.append(commit_hash_link_reference) +%}{# + # # Generate the commit summary line and format it for RST +#}{% set description = "* %s" | format(format_commit_summary_line(commit)) +%}{% set description = description | convert_md_to_rst +%}{% set description = description | autofit_text_width(max_line_width, hanging_indent) +%}{% set _ = commit_descriptions.append(description) +%}{% endfor +%}{# + # # PRINT SECTION (Header & Commits) + # Note: Must add an additional character to the section header when determining the underline because of + # the emoji character which can serve as 2 characters in length. +#}{{ "\n" +}}{{ section_header ~ "\n" +}}{{ generate_heading_underline(section_header ~ " ", '-') ~ "\n" +}}{{ + "\n%s\n" | format(commit_descriptions | unique | join("\n\n")) + +}}{% endfor +%}{# + # # Determine if any commits have a breaking change description + # # commit_objects is a dictionary of strings to a list of commits { "features", [ParsedCommit(), ...] } +#}{% set breaking_commits = [] +%}{% for commits in commit_objects.values() +%}{% set valid_commits = commits | rejectattr("error", "defined") +%}{# # Filter out breaking change commits that have no breaking descriptions +#}{% set _ = breaking_commits.extend( + valid_commits | selectattr("breaking_descriptions.0") + ) +%}{% endfor +%}{# +#}{% if breaking_commits | length > 0 +%}{# # PREPROCESS COMMITS +#}{% set brk_ns = namespace(commits=breaking_commits) +%}{% set _ = apply_alphabetical_ordering_by_brk_descriptions(brk_ns) +%}{# +#}{% set brking_descriptions = [] +%}{# +#}{% for commit in brk_ns.commits +%}{% set full_description = "* %s" | format( + format_breaking_changes_description(commit).split("\n\n") | join("\n\n* ") + ) +%}{% set _ = brking_descriptions.append( + full_description | convert_md_to_rst | autofit_text_width(max_line_width, hanging_indent) + ) +%}{% endfor +%}{# + # # PRINT BREAKING CHANGE DESCRIPTIONS (header & descriptions) +#}{{ "\n" +}}{{ "%s BREAKING CHANGES\n" | format(emoji_map["breaking"]) +}}{{ '-------------------\n' +}}{{ + "\n%s\n" | format(brking_descriptions | unique | join("\n\n")) +}}{# +#}{% endif +%}{# + # + # # PRINT POST PARAGRAPH LINKS +#}{% if post_paragraph_links | length > 0 +%}{# # Print out any PR/MR or Issue URL references that were found in the commit messages +#}{{ "\n%s\n" | format(post_paragraph_links | unique | sort | join("\n")) +}}{% endif +%} diff --git a/config/release-templates/.components/first_release.rst.j2 b/config/release-templates/.components/first_release.rst.j2 new file mode 100644 index 000000000..5c08066f7 --- /dev/null +++ b/config/release-templates/.components/first_release.rst.j2 @@ -0,0 +1,22 @@ +{% from "macros.rst.j2" import generate_heading_underline +%}{# + +.. _changelog-vX.X.X: + +vX.X.X (YYYY-MMM-DD) +==================== + +* Initial Release + +#}{% set version_header = "%s (%s)" | format( + release.version.as_semver_tag(), + release.tagged_date.strftime("%Y-%m-%d") + ) +%} + +{{- ".. _changelog-%s:" | format(release.version.as_semver_tag()) }} + +{{ version_header }} +{{ generate_heading_underline(version_header, "=") }} + +* Initial Release diff --git a/config/release-templates/.components/macros.md.j2 b/config/release-templates/.components/macros.md.j2 new file mode 100644 index 000000000..01e931fc3 --- /dev/null +++ b/config/release-templates/.components/macros.md.j2 @@ -0,0 +1,200 @@ +{% set section_heading_translations = { + 'feat': 'features', + 'fix': 'bug fixes', + 'perf': 'performance improvements', + 'docs': 'documentation', + 'build': 'build system', + 'refactor': 'refactoring', + 'test': 'testing', + 'ci': 'continuous integration', + 'chore': 'chores', + 'style': 'code style', +} %} + +{% set section_heading_order = section_heading_translations.values() %} + +{% set emoji_map = { + 'breaking': '💥', + 'features': '✨', + 'bug fixes': '🪲', + 'performance improvements': '⚡', + 'documentation': '📖', + 'build system': '⚙️', + 'refactoring': '♻️', + 'testing': '✅', + 'continuous integration': '🤖', + 'chores': '🧹', + 'code style': '🎨', + 'unknown': '❗', + 'release_note': '💡', +} %} + + +{# + MACRO: format a inline link reference in Markdown +#}{% macro format_link(link, label) +%}{{ "[%s](%s)" | format(label, link) +}}{% endmacro +%} + + +{# + MACRO: Capitalize the first letter of a string only +#}{% macro capitalize_first_letter_only(sentence) +%}{{ (sentence[0] | upper) ~ sentence[1:] +}}{% endmacro +%} + + +{# + MACRO: commit message links or PR/MR links of commit +#}{% macro commit_msg_links(commit) +%}{% if commit.error is undefined +%}{# + # # Initialize variables +#}{% set link_references = [] +%}{% set summary_line = capitalize_first_letter_only( + commit.descriptions[0] | safe + ) +%}{# +#}{% if commit.linked_merge_request != "" +%}{% set pr_num = commit.linked_merge_request +%}{# # TODO: breaking change v10, remove summary line replacers as PSR will do it for us +#}{% set summary_line = summary_line | replace("(pull request ", "(") | replace("(" ~ pr_num ~ ")", "") | trim +%}{# + # # Add PR references with a link to the PR +#}{% set _ = link_references.append( + format_link(pr_num | pull_request_url, "PR" ~ pr_num) + ) +%}{% endif +%}{# + # # DEFAULT: Always include the commit hash as a link +#}{% set _ = link_references.append( + format_link( + commit.hexsha | commit_hash_url, + "`%s`" | format(commit.short_hash) + ) + ) +%}{# +#}{% set formatted_links = "" +%}{% if link_references | length > 0 +%}{% set formatted_links = " (%s)" | format(link_references | join(", ")) +%}{% endif +%}{# + # Return the modified summary_line +#}{{ summary_line ~ formatted_links +}}{% endif +%}{% endmacro +%} + + +{# + MACRO: format commit summary line +#}{% macro format_commit_summary_line(commit) +%}{# # Check for Parsing Error +#}{% if commit.error is undefined +%}{# + # # Add any message links to the commit summary line +#}{% set summary_line = commit_msg_links(commit) +%}{# +#}{% if commit.scope +%}{% set summary_line = "**%s**: %s" | format(commit.scope, summary_line) +%}{% endif +%}{# + # # Return the modified summary_line +#}{{ summary_line +}}{# +#}{% else +%}{# # Return the first line of the commit if there was a Parsing Error +#}{{ (commit.commit.message | string).split("\n", maxsplit=1)[0] +}}{% endif +%}{% endmacro +%} + + +{# + MACRO: format the breaking changes description by: + - Capitalizing the description + - Adding an optional scope prefix +#}{% macro format_breaking_changes_description(commit) +%}{% set ns = namespace(full_description="") +%}{# +#}{% if commit.error is undefined +%}{% for paragraph in commit.breaking_descriptions +%}{% if paragraph | trim | length > 0 +%}{# +#}{% set paragraph_text = capitalize_first_letter_only(paragraph) | trim | safe +%}{# +#}{% set ns.full_description = [ + ns.full_description, + paragraph_text + ] | join("\n\n") +%}{# +#}{% endif +%}{% endfor +%}{# +#}{% set ns.full_description = ns.full_description | trim +%}{# +#}{% if commit.scope +%}{% set ns.full_description = "**%s**: %s" | format( + commit.scope, ns.full_description + ) +%}{% endif +%}{% endif +%}{# +#}{{ ns.full_description +}}{% endmacro +%} + + +{# + MACRO: order commits alphabetically by scope and attribute + - Commits are sorted based on scope and then the attribute alphabetically + - Commits without scope are placed first and sorted alphabetically by the attribute + - parameter: ns (namespace) object with a commits list + - parameter: attr (string) attribute to sort by + - returns None but modifies the ns.commits list in place +#}{% macro order_commits_alphabetically_by_scope_and_attr(ns, attr) +%}{% set ordered_commits = [] +%}{# + # # Eliminate any ParseError commits from input set +#}{% set filtered_commits = ns.commits | rejectattr("error", "defined") | list +%}{# + # # grab all commits with no scope and sort alphabetically by attr +#}{% for commit in filtered_commits | rejectattr("scope") | sort(attribute=attr) +%}{% set _ = ordered_commits.append(commit) +%}{% endfor +%}{# + # # grab all commits with a scope and sort alphabetically by the scope and then attr +#}{% for commit in filtered_commits | selectattr("scope") | sort(attribute=(['scope', attr] | join(","))) +%}{% set _ = ordered_commits.append(commit) +%}{% endfor +%}{# + # # Return the ordered commits +#}{% set ns.commits = ordered_commits +%}{% endmacro +%} + + +{# + MACRO: apply smart ordering of commits objects based on alphabetized summaries and then scopes + - Commits are sorted based on the commit type and the commit message + - Commits are grouped by the commit type + - parameter: ns (namespace) object with a commits list + - returns None but modifies the ns.commits list in place +#}{% macro apply_alphabetical_ordering_by_descriptions(ns) +%}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'descriptions.0') +%}{% endmacro +%} + + +{# + MACRO: apply smart ordering of commits objects based on alphabetized breaking changes and then scopes + - Commits are sorted based on the commit type and the commit message + - Commits are grouped by the commit type + - parameter: ns (namespace) object with a commits list + - returns None but modifies the ns.commits list in place +#}{% macro apply_alphabetical_ordering_by_brk_descriptions(ns) +%}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'breaking_descriptions.0') +%}{% endmacro +%} diff --git a/config/release-templates/.components/macros.rst.j2 b/config/release-templates/.components/macros.rst.j2 new file mode 100644 index 000000000..6a9955afc --- /dev/null +++ b/config/release-templates/.components/macros.rst.j2 @@ -0,0 +1,253 @@ +{# TODO: move to configuration for user to modify #} +{% set section_heading_translations = { + 'feat': 'features', + 'fix': 'bug fixes', + 'perf': 'performance improvements', + 'docs': 'documentation', + 'build': 'build system', + 'refactor': 'refactoring', + 'test': 'testing', + 'ci': 'continuous integration', + 'chore': 'chores', + 'style': 'code style', + } +%} + +{% set section_heading_order = section_heading_translations.values() %} + +{% set emoji_map = { + 'breaking': '💥', + 'features': '✨', + 'bug fixes': '🪲', + 'performance improvements': '⚡', + 'documentation': '📖', + 'build system': '⚙️', + 'refactoring': '♻️', + 'testing': '✅', + 'continuous integration': '🤖', + 'chores': '🧹', + 'code style': '🎨', + 'unknown': '❗', + 'release_note': '💡', +} %} + +{# + MACRO: format a post-paragraph link reference in RST +#}{% macro format_link_reference(link, label) +%}{{ ".. _%s: %s" | format(label, link) +}}{% endmacro +%} + + +{# + MACRO: Capitalize the first letter of a string only +#}{% macro capitalize_first_letter_only(sentence) +%}{{ (sentence[0] | upper) ~ sentence[1:] +}}{% endmacro +%} + + +{# + MACRO: format commit summary line +#}{% macro format_commit_summary_line(commit) +%}{# # Check for Parsing Error +#}{% if commit.error is undefined +%}{# + # # Add any message links to the commit summary line +#}{% set summary_line = commit_msg_links(commit) +%}{# +#}{% if commit.scope +%}{% set summary_line = "**%s**: %s" | format(commit.scope, summary_line) +%}{% endif +%}{# + # # Return the modified summary_line +#}{{ summary_line +}}{# +#}{% else +%}{# # Return the first line of the commit if there was a Parsing Error +#}{{ (commit.commit.message | string).split("\n", maxsplit=1)[0] +}}{% endif +%}{% endmacro +%} + + +{# + MACRO: Create & return an non-inline RST link from a commit message + - Returns empty string if no PR/MR identifier is found +#}{% macro extract_pr_link_reference(commit) +%}{% if commit.error is undefined +%}{% set summary_line = commit.descriptions[0] +%}{# +#}{% if commit.linked_merge_request != "" +%}{# # Create a PR/MR reference url +#}{{ format_link_reference( + commit.linked_merge_request | pull_request_url, + "PR" ~ commit.linked_merge_request, + ) +}}{% endif +%}{% endif +%}{% endmacro +%} + +{# + MACRO: Extract issue references from a parsed commit object + - Stores the issue urls in the namespace object +#}{% macro extract_issue_link_references(ns, commit) +%}{% set issue_urls = [] +%}{# +#}{% if commit.linked_issues is defined and commit.linked_issues | length > 0 +%}{% for issue_num in commit.linked_issues +%}{# # Create an issue reference url +#}{% set _ = issue_urls.append( + format_link_reference( + issue_num | issue_url, + issue_num, + ) + ) +%}{% endfor +%}{% endif +%}{# + # # Store the issue urls in the namespace object +#}{% set ns.urls = issue_urls +%}{% endmacro +%} + +{# + MACRO: formats a commit message for a non-inline RST link for a commit hash and/or PR/MR +#}{% macro commit_msg_links(commit) +%}{% if commit.error is undefined +%}{# + # # Initialize variables +#}{% set closes_statement = "" +%}{% set link_references = [] +%}{% set summary_line = capitalize_first_letter_only( + commit.descriptions[0] | safe + ) +%}{# +#}{% if commit.linked_issues | length > 0 +%}{% set closes_statement = ", closes `%s`_" | format( + commit.linked_issues | join("`_, `") + ) +%}{% endif +%}{# +#}{% if commit.linked_merge_request != "" +%}{# # TODO: breaking change v10, remove summary line replacers as PSR will do it for us +#}{% set summary_line = summary_line | replace("(pull request ", "(") | replace("(" ~ commit.linked_merge_request ~ ")", "") | trim +%}{# + # # Add PR references with a link to the PR +#}{% set _ = link_references.append("`PR%s`_" | format(commit.linked_merge_request)) +%}{% endif +%}{# + # DEFAULT: Always include the commit hash as a link +#}{% set _ = link_references.append("`%s`_" | format(commit.short_hash)) +%}{# +#}{% set formatted_links = "" +%}{% if link_references | length > 0 +%}{% set formatted_links = " (%s)" | format(link_references | join(", ")) +%}{% endif +%}{# + # Return the modified summary_line +#}{{ summary_line ~ closes_statement ~ formatted_links +}}{% endif +%}{% endmacro +%} + + +{# MACRO: generate a heading underline that matches the exact length of the header #} +{% macro generate_heading_underline(header, underline_char) +%}{% set header_underline = [] +%}{% for _ in header +%}{% set __ = header_underline.append(underline_char) +%}{% endfor +%}{# # Print out the header underline +#}{{ header_underline | join +}}{% endmacro +%} + + +{# + MACRO: format the breaking changes description by: + - Capitalizing the description + - Adding an optional scope prefix +#}{% macro format_breaking_changes_description(commit) +%}{% set ns = namespace(full_description="") +%}{# +#}{% if commit.error is undefined +%}{% for paragraph in commit.breaking_descriptions +%}{% if paragraph | trim | length > 0 +%}{# +#}{% set paragraph_text = capitalize_first_letter_only(paragraph) | trim | safe +%}{# +#}{% set ns.full_description = [ + ns.full_description, + paragraph_text + ] | join("\n\n") +%}{# +#}{% endif +%}{% endfor +%}{# +#}{% set ns.full_description = ns.full_description | trim +%}{# +#}{% if commit.scope +%}{% set ns.full_description = "**%s**: %s" | format( + commit.scope, ns.full_description + ) +%}{% endif +%}{% endif +%}{# +#}{{ ns.full_description +}}{% endmacro +%} + + +{# + MACRO: order commits alphabetically by scope and attribute + - Commits are sorted based on scope and then the attribute alphabetically + - Commits without scope are placed first and sorted alphabetically by the attribute + - parameter: ns (namespace) object with a commits list + - parameter: attr (string) attribute to sort by + - returns None but modifies the ns.commits list in place +#}{% macro order_commits_alphabetically_by_scope_and_attr(ns, attr) +%}{% set ordered_commits = [] +%}{# + # # Eliminate any ParseError commits from input set +#}{% set filtered_commits = ns.commits | rejectattr("error", "defined") | list +%}{# + # # grab all commits with no scope and sort alphabetically by attr +#}{% for commit in filtered_commits | rejectattr("scope") | sort(attribute=attr) +%}{% set _ = ordered_commits.append(commit) +%}{% endfor +%}{# + # # grab all commits with a scope and sort alphabetically by the scope and then attr +#}{% for commit in filtered_commits | selectattr("scope") | sort(attribute=(['scope', attr] | join(","))) +%}{% set _ = ordered_commits.append(commit) +%}{% endfor +%}{# + # # Return the ordered commits +#}{% set ns.commits = ordered_commits +%}{% endmacro +%} + + +{# + MACRO: apply smart ordering of commits objects based on alphabetized summaries and then scopes + - Commits are sorted based on the commit type and the commit message + - Commits are grouped by the commit type + - parameter: ns (namespace) object with a commits list + - returns None but modifies the ns.commits list in place +#}{% macro apply_alphabetical_ordering_by_descriptions(ns) +%}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'descriptions.0') +%}{% endmacro +%} + + +{# + MACRO: apply smart ordering of commits objects based on alphabetized breaking changes and then scopes + - Commits are sorted based on the commit type and the commit message + - Commits are grouped by the commit type + - parameter: ns (namespace) object with a commits list + - returns None but modifies the ns.commits list in place +#}{% macro apply_alphabetical_ordering_by_brk_descriptions(ns) +%}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'breaking_descriptions.0') +%}{% endmacro +%} diff --git a/config/release-templates/.components/unreleased_changes.rst.j2 b/config/release-templates/.components/unreleased_changes.rst.j2 new file mode 100644 index 000000000..79414655c --- /dev/null +++ b/config/release-templates/.components/unreleased_changes.rst.j2 @@ -0,0 +1,10 @@ +{% if unreleased_commits | length > 0 %} +.. _changelog-unreleased: + +Unreleased +========== +{% set commit_objects = unreleased_commits +%}{% include "changes.rst.j2" +-%}{{ "\n" +}}{% endif +%} diff --git a/config/release-templates/.components/versioned_changes.md.j2 b/config/release-templates/.components/versioned_changes.md.j2 new file mode 100644 index 000000000..bd770150b --- /dev/null +++ b/config/release-templates/.components/versioned_changes.md.j2 @@ -0,0 +1,15 @@ +{# EXAMPLE: + +## vX.X.X (YYYY-MMM-DD) + +{{ change_sections }} + +#}{{ +"## %s (%s)\n" | format( + release.version.as_semver_tag(), + release.tagged_date.strftime("%Y-%m-%d") +) +}}{# +#}{% set commit_objects = release["elements"] +%}{% include "changes.md.j2" +-%} diff --git a/config/release-templates/.components/versioned_changes.rst.j2 b/config/release-templates/.components/versioned_changes.rst.j2 new file mode 100644 index 000000000..7ec187797 --- /dev/null +++ b/config/release-templates/.components/versioned_changes.rst.j2 @@ -0,0 +1,25 @@ +{% from 'macros.rst.j2' import generate_heading_underline %}{# + +.. _changelog-X.X.X: + +vX.X.X (YYYY-MMM-DD) +==================== + +{{ change_sections }} + +#}{% set version_header = "%s (%s)" | format( + release.version.as_semver_tag(), + release.tagged_date.strftime("%Y-%m-%d") + ) +%}{# +#}{{ + +".. _changelog-%s:" | format(release.version.as_semver_tag()) }} + +{{ version_header }} +{{ generate_heading_underline(version_header, "=") }} +{# + +#}{% set commit_objects = release["elements"] +%}{% include "changes.rst.j2" +-%} diff --git a/config/release-templates/.release_notes.md.j2 b/config/release-templates/.release_notes.md.j2 new file mode 100644 index 000000000..39d1b6729 --- /dev/null +++ b/config/release-templates/.release_notes.md.j2 @@ -0,0 +1,120 @@ +{% from ".components/macros.md.j2" import format_link +%}{# EXAMPLE: + +## v1.0.0 (2020-01-01) + +### ✨ Features + +- Add new feature ([PR#10](https://domain.com/namespace/repo/pull/10), [`abcdef0`](https://domain.com/namespace/repo/commit/HASH)) + +- **scope**: Add new feature ([`abcdef0`](https://domain.com/namespace/repo/commit/HASH)) + +### 🪲 Bug Fixes + +- Fix bug ([PR#11](https://domain.com/namespace/repo/pull/11), [`abcdef1`](https://domain.com/namespace/repo/commit/HASH)) + +### 💥 BREAKING CHANGES + +- With the change _____, the change causes ___ effect. Ultimately, this section it is a more detailed description of the breaking change. With an optional scope prefix like the commit messages above. + +- **scope**: this breaking change has a scope to identify the part of the code that this breaking change applies to for better context. + +### ✅ Resolved Issues + +- [#000](https://domain.com/namespace/repo/issues/000): _Title_ + +--- + +**Detailed Changes**: [vX.X.X...vX.X.X](https://domain.com/namespace/repo/compare/vX.X.X...vX.X.X) + +--- + +**Installable artifacts are available from**: + +- [PyPi Registry](https://pypi.org/project/package_name/x.x.x) +- [GitHub Release Assets](https://github.com/namespace/repo/releases/tag/vX.X.X) + +#}{# # Set line width to 1000 to avoid wrapping as GitHub will handle it +#}{% set max_line_width = max_line_width | default(1000) +%}{% set hanging_indent = hanging_indent | default(2) +%}{% set releases = context.history.released.values() | list +%}{% set curr_release_index = releases.index(release) +%}{# +#}{% if mask_initial_release and curr_release_index == releases | length - 1 +%}{# # On a first release, generate our special message +#}{% include ".components/first_release.md.j2" +%}{% else +%}{# # Not the first release so generate notes normally +#}{% include ".components/versioned_changes.md.j2" +-%}{# + # # If there are any commits that resolve issues, list out the issues with links +#}{% set issue_resolving_commits = [] +%}{% for commits in release["elements"].values() +%}{% set _ = issue_resolving_commits.extend( + commits | rejectattr("error", "defined") | selectattr("linked_issues") + ) +%}{% endfor +%}{% if issue_resolving_commits | length > 0 +%}{{ + "\n### ✅ Resolved Issues\n" +}}{# +#}{% set issue_numbers = [] +%}{% for linked_issues in issue_resolving_commits | map(attribute="linked_issues") +%}{% set _ = issue_numbers.extend(linked_issues) +%}{% endfor +%}{% for issue_num in issue_numbers | unique | sort +%}{{ + "\n- %s: _Title_\n" | format(format_link(issue_num | issue_url, issue_num)) +}}{# +#}{% endfor +%}{% endif +%}{# +#}{% set prev_release_index = curr_release_index + 1 +%}{# +#}{% if 'compare_url' is filter and prev_release_index < releases | length +%}{% set prev_version_tag = releases[prev_release_index].version.as_tag() +%}{% set new_version_tag = release.version.as_tag() +%}{% set version_compare_url = prev_version_tag | compare_url(new_version_tag) +%}{% set detailed_changes_link = '[{}...{}]({})'.format( + prev_version_tag, new_version_tag, version_compare_url + ) +%}{{ "\n" +}}{{ "---\n" +}}{{ "\n" +}}{{ "**Detailed Changes**: %s" | format(detailed_changes_link) +}}{{ "\n" +}}{% endif +%}{% endif +%}{# +#} +--- + +**Installable artifacts are available from**: + +{{ + "- %s" | format( + format_link( + [ + "https://pypi.org/project", + repo_name, + release.version | string, + ] | join("/"), + "PyPi Registry", + ) + ) +}} + +{{ + "- %s" | format( + format_link( + [ + "https://github.com", + repo_name, + repo_name, + "releases/tag", + release.version.as_tag(), + ] | join("/"), + "GitHub Release Assets", + ) + ) +}} diff --git a/config/release-templates/CHANGELOG.rst.j2 b/config/release-templates/CHANGELOG.rst.j2 new file mode 100644 index 000000000..5f8553a92 --- /dev/null +++ b/config/release-templates/CHANGELOG.rst.j2 @@ -0,0 +1,22 @@ +{# + This changelog template controls which changelog creation occurs + based on which mode is provided. + + Modes: + - init: Initialize a full changelog from scratch + - update: Insert new version details where the placeholder exists in the current changelog + +#}{% set this_file = "CHANGELOG.rst" +%}{% set insertion_flag = ctx.changelog_insertion_flag +%}{% set unreleased_commits = ctx.history.unreleased +%}{% set releases = ctx.history.released.values() | list +%}{# +#}{% if ctx.changelog_mode == "init" +%}{% include ".components/changelog_init.rst.j2" +%}{# +#}{% elif ctx.changelog_mode == "update" +%}{% set prev_changelog_file = this_file +%}{% include ".components/changelog_update.rst.j2" +%}{# +#}{% endif +%} diff --git a/docs/index.rst b/docs/index.rst index e8deabd5c..2b2228939 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -69,6 +69,7 @@ Documentation Contents Migrating from Python Semantic Release v7 Internal API Algorithm + Changelog View on GitHub Getting Started diff --git a/docs/psr_changelog.rst b/docs/psr_changelog.rst new file mode 100644 index 000000000..565b0521d --- /dev/null +++ b/docs/psr_changelog.rst @@ -0,0 +1 @@ +.. include:: ../CHANGELOG.rst diff --git a/pyproject.toml b/pyproject.toml index c708736d1..dac59175b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -399,6 +399,7 @@ ignore_names = ["change_to_ex_proj_dir", "init_example_project"] [tool.semantic_release] logging_use_named_masks = true commit_parser = "angular" +commit_parser_options = { parse_squash_commits = true } build_command = """ python -m pip install -e .[build] python -m build . @@ -408,6 +409,7 @@ version_variables = ["src/semantic_release/__init__.py:__version__"] version_toml = ["pyproject.toml:project.version"] [tool.semantic_release.changelog] +# default_templates = { changelog_file = "CHANGELOG.rst" } exclude_commit_patterns = [ '''chore(?:\([^)]*?\))?: .+''', '''ci(?:\([^)]*?\))?: .+''', @@ -420,6 +422,9 @@ exclude_commit_patterns = [ # Old semantic-release version commits '''^\d+\.\d+\.\d+''', ] +insertion_flag = "=========\nCHANGELOG\n=========" +mode = "update" +template_dir = "config/release-templates" [tool.semantic_release.branches.main] match = "(main|master)" From 4e11a31d88e8538df4c75575fb9b805f7521f8ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jan 2025 17:39:28 -0500 Subject: [PATCH 015/160] ci(deps): bump `mikepenz/action-junit-report@v5.2.0` action to 5.3.0 (#1157) --- .github/workflows/validate.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 62859ad06..63778a3f2 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -157,7 +157,7 @@ jobs: --junit-xml=tests/reports/pytest-results.xml - name: Report | Upload Test Results - uses: mikepenz/action-junit-report@v5.2.0 + uses: mikepenz/action-junit-report@v5.3.0 if: ${{ always() && steps.tests.outcome != 'skipped' }} with: report_paths: ./tests/reports/*.xml @@ -245,7 +245,7 @@ jobs: retention-days: 1 - name: Report | Upload Test Results - uses: mikepenz/action-junit-report@v5.2.0 + uses: mikepenz/action-junit-report@v5.3.0 if: ${{ always() && steps.tests.outcome != 'skipped' }} with: report_paths: ./tests/reports/*.xml @@ -340,7 +340,7 @@ jobs: retention-days: 1 - name: Report | Upload Test Results - uses: mikepenz/action-junit-report@v5.2.0 + uses: mikepenz/action-junit-report@v5.3.0 if: ${{ always() && steps.tests.outcome != 'skipped' }} with: report_paths: ./tests/reports/*.xml From e33b66842b17e4599378ddb5cfb39c38a12d4405 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 27 Jan 2025 18:48:30 -0500 Subject: [PATCH 016/160] refactor(release-history): refactor type casting for commit msg value exclusion matching (#1158) --- src/semantic_release/changelog/release_history.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/semantic_release/changelog/release_history.py b/src/semantic_release/changelog/release_history.py index 16a3e9637..2d9361f75 100644 --- a/src/semantic_release/changelog/release_history.py +++ b/src/semantic_release/changelog/release_history.py @@ -9,6 +9,7 @@ from semantic_release.commit_parser import ParseError from semantic_release.commit_parser.token import ParsedCommit +from semantic_release.commit_parser.util import force_str from semantic_release.enums import LevelBump from semantic_release.version.algorithm import tags_and_versions @@ -117,7 +118,7 @@ def from_git_history( # iterate through parsed commits to add to changelog definition for parsed_result in parse_results: - commit_message = str(parsed_result.commit.message) + commit_message = force_str(parsed_result.commit.message) commit_type = ( "unknown" if isinstance(parsed_result, ParseError) From 29cdfdb2d4a869d8b01ada162994f7dc53f30172 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 27 Jan 2025 21:03:49 -0500 Subject: [PATCH 017/160] chore(scripts): extend stamping to all docs with $NEW_VERSION & $NEW_RELEASE_TAG vars (#1159) --- scripts/bump_version_in_docs.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/scripts/bump_version_in_docs.py b/scripts/bump_version_in_docs.py index 8167f0fad..f8c67331a 100644 --- a/scripts/bump_version_in_docs.py +++ b/scripts/bump_version_in_docs.py @@ -5,8 +5,11 @@ from pathlib import Path from re import compile as regexp +# Constants PROJ_DIR = Path(__file__).resolve().parent.parent DOCS_DIR = PROJ_DIR / "docs" +version_replace_pattern = regexp(r"\$(NEW_VERSION|{NEW_VERSION})") +tag_replace_pattern = regexp(r"\$(NEW_RELEASE_TAG|{NEW_RELEASE_TAG})") def update_github_actions_example(filepath: Path, release_tag: str) -> None: @@ -28,13 +31,37 @@ def update_github_actions_example(filepath: Path, release_tag: str) -> None: filepath.write_text(str.join("\n", file_content_lines) + "\n") +def envsubst(filepath: Path, version: str, release_tag: str) -> None: + file_content = filepath.read_text() + + found = False + for pattern, replacement in [ + (version_replace_pattern, version), + (tag_replace_pattern, release_tag), + ]: + if not found and (found := bool(pattern.search(file_content))): + print(f"Applying envsubst to {filepath}") + + file_content = pattern.sub(replacement, file_content) + + filepath.write_text(file_content) + + if __name__ == "__main__": new_release_tag = getenv("NEW_RELEASE_TAG") + new_version = getenv("NEW_VERSION") if not new_release_tag: print("NEW_RELEASE_TAG environment variable is not set") exit(1) + if not new_version: + print("NEW_VERSION environment variable is not set") + exit(1) + update_github_actions_example( DOCS_DIR / "automatic-releases" / "github-actions.rst", new_release_tag ) + + for doc_file in DOCS_DIR.rglob("*.rst"): + envsubst(filepath=doc_file, version=new_version, release_tag=new_release_tag) From 45d49c3da75a7f08c86fc9bab5d232a9b37d9e72 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 27 Jan 2025 21:09:04 -0500 Subject: [PATCH 018/160] feat(changelog): add `create_pypi_url` filter to jinja template render context (#1160) * test(release-notes-context): add unit test to validate use of `create_pypi_url` filter * test(changelog-context): add unit test to validate use of `create_pypi_url` filter * docs(changelog-templates): add description for new `create_pypi_url` filter function * chore(release-notes): refactor to use new pypi url filter --- config/release-templates/.release_notes.md.j2 | 6 +- docs/changelog_templates.rst | 21 ++++++ src/semantic_release/changelog/context.py | 17 ++++- src/semantic_release/cli/changelog_writer.py | 2 + src/semantic_release/const.py | 2 + .../changelog/test_changelog_context.py | 74 +++++++++++++++++++ .../changelog/test_release_notes.py | 54 ++++++++++++++ 7 files changed, 170 insertions(+), 6 deletions(-) diff --git a/config/release-templates/.release_notes.md.j2 b/config/release-templates/.release_notes.md.j2 index 39d1b6729..b1e132587 100644 --- a/config/release-templates/.release_notes.md.j2 +++ b/config/release-templates/.release_notes.md.j2 @@ -94,11 +94,7 @@ {{ "- %s" | format( format_link( - [ - "https://pypi.org/project", - repo_name, - release.version | string, - ] | join("/"), + repo_name | create_pypi_url(release.version | string), "PyPi Registry", ) ) diff --git a/docs/changelog_templates.rst b/docs/changelog_templates.rst index 1200c0159..1d86376f9 100644 --- a/docs/changelog_templates.rst +++ b/docs/changelog_templates.rst @@ -658,6 +658,27 @@ The filters provided vary based on the VCS configured and available features: ) }} +* ``create_pypi_url(package_name: str, version: str = "")``: given a package name and an optional + version, return a URL to the PyPI page for the package. If a version is provided, the URL will + point to the specific version page. If no version is provided, the URL will point to the package + page. + + *Introduced in ${NEW_RELEASE_TAG}.* + + **Example Usage:** + + .. code:: jinja + + {{ "example-package" | create_pypi_url }} + {{ "example-package" | create_pypi_url("1.0.0") }} + + **Markdown Output:** + + .. code:: markdown + + https://pypi.org/project/example-package + https://pypi.org/project/example-package/1.0.0 + * ``create_server_url (Callable[[PathStr, AuthStr | None, QueryStr | None, FragmentStr | None], UrlStr])``: when given a path, prepend the configured vcs server host and url scheme. Optionally you can provide, a auth string, a query string or a url fragment to be normalized into the diff --git a/src/semantic_release/changelog/context.py b/src/semantic_release/changelog/context.py index 9b8b102fe..449ed2f1a 100644 --- a/src/semantic_release/changelog/context.py +++ b/src/semantic_release/changelog/context.py @@ -4,10 +4,13 @@ import os from dataclasses import dataclass from enum import Enum -from pathlib import Path +from pathlib import Path, PurePosixPath from re import compile as regexp from typing import TYPE_CHECKING, Any, Callable, Literal +from urllib3.util import Url + +from semantic_release.const import PYPI_WEB_DOMAIN from semantic_release.helpers import sort_numerically if TYPE_CHECKING: # pragma: no cover @@ -86,6 +89,7 @@ def make_changelog_context( hvcs_type=hvcs_client.__class__.__name__.lower(), filters=( *hvcs_client.get_changelog_context_filters(), + create_pypi_url, read_file, convert_md_to_rst, autofit_text_width, @@ -94,6 +98,17 @@ def make_changelog_context( ) +def create_pypi_url(package_name: str, version: str = "") -> str: + project_name = package_name.strip("/").strip() + if not project_name: + raise ValueError("package_name must not be empty!") + return Url( + scheme="https", + host=PYPI_WEB_DOMAIN, + path=str(PurePosixPath("project", project_name, version.strip("/").strip())), + ).url.rstrip("/") + + def read_file(filepath: str) -> str: try: if not filepath: diff --git a/src/semantic_release/cli/changelog_writer.py b/src/semantic_release/cli/changelog_writer.py index 5c6ab9f55..01f28f315 100644 --- a/src/semantic_release/cli/changelog_writer.py +++ b/src/semantic_release/cli/changelog_writer.py @@ -13,6 +13,7 @@ from semantic_release.changelog.context import ( ReleaseNotesContext, autofit_text_width, + create_pypi_url, make_changelog_context, ) from semantic_release.changelog.template import environment, recursive_render @@ -257,6 +258,7 @@ def generate_release_notes( mask_initial_release=mask_initial_release, filters=( *hvcs_client.get_changelog_context_filters(), + create_pypi_url, autofit_text_width, sort_numerically, ), diff --git a/src/semantic_release/const.py b/src/semantic_release/const.py index 5166186a1..231e30466 100644 --- a/src/semantic_release/const.py +++ b/src/semantic_release/const.py @@ -3,6 +3,8 @@ import os import re +PYPI_WEB_DOMAIN = "pypi.org" + # https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string SEMVER_REGEX = re.compile( r""" diff --git a/tests/unit/semantic_release/changelog/test_changelog_context.py b/tests/unit/semantic_release/changelog/test_changelog_context.py index c80344fa1..8204575f6 100644 --- a/tests/unit/semantic_release/changelog/test_changelog_context.py +++ b/tests/unit/semantic_release/changelog/test_changelog_context.py @@ -585,3 +585,77 @@ def test_changelog_context_sort_numerically_reverse( # Evaluate assert expected_changelog == actual_changelog + + +def test_changelog_context_pypi_url_filter( + example_git_https_url: str, + artificial_release_history: ReleaseHistory, + changelog_md_file: Path, +): + changelog_tpl = dedent( + """\ + {{ "example-package" | create_pypi_url }} + """ + ) + + expected_changelog = dedent( + """\ + https://pypi.org/project/example-package + """ + ) + + env = environment(trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True) + context = make_changelog_context( + hvcs_client=Gitlab(example_git_https_url), + release_history=artificial_release_history, + mode=ChangelogMode.UPDATE, + prev_changelog_file=changelog_md_file, + insertion_flag="", + mask_initial_release=False, + ) + context.bind_to_environment(env) + + # Create changelog from template with environment + actual_changelog = env.from_string(changelog_tpl).render() + + # Evaluate + assert expected_changelog == actual_changelog + + +def test_changelog_context_pypi_url_filter_tagged( + example_git_https_url: str, + artificial_release_history: ReleaseHistory, + changelog_md_file: Path, +): + version = "1.0.0" + changelog_tpl = dedent( + """\ + {% set release = context.history.released.values() | first + %}{{ + "example-package" | create_pypi_url(release.version | string) + }} + """ + ) + + expected_changelog = dedent( + f"""\ + https://pypi.org/project/example-package/{version} + """ + ) + + env = environment(trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True) + context = make_changelog_context( + hvcs_client=Gitlab(example_git_https_url), + release_history=artificial_release_history, + mode=ChangelogMode.UPDATE, + prev_changelog_file=changelog_md_file, + insertion_flag="", + mask_initial_release=False, + ) + context.bind_to_environment(env) + + # Create changelog from template with environment + actual_changelog = env.from_string(changelog_tpl).render() + + # Evaluate + assert expected_changelog == actual_changelog diff --git a/tests/unit/semantic_release/changelog/test_release_notes.py b/tests/unit/semantic_release/changelog/test_release_notes.py index 7158dd670..9f506d332 100644 --- a/tests/unit/semantic_release/changelog/test_release_notes.py +++ b/tests/unit/semantic_release/changelog/test_release_notes.py @@ -547,3 +547,57 @@ def test_release_notes_context_sort_numerically_filter_reversed( ) assert expected_content == actual_content + + +def test_release_notes_context_pypi_url_filter( + example_git_https_url: str, + single_release_history: ReleaseHistory, + example_project_dir: ExProjectDir, + change_to_ex_proj_dir: None, +): + version = list(single_release_history.released.keys())[-1] + release = single_release_history.released[version] + + example_project_dir.joinpath(".release_notes.md.j2").write_text( + """{{ "example-package" | create_pypi_url }}""" + ) + + expected_content = f"https://pypi.org/project/example-package{os.linesep}" + + actual_content = generate_release_notes( + hvcs_client=Github(remote_url=example_git_https_url), + release=release, + template_dir=example_project_dir, + history=single_release_history, + style="angular", + mask_initial_release=False, + ) + + assert expected_content == actual_content + + +def test_release_notes_context_pypi_url_filter_tagged( + example_git_https_url: str, + single_release_history: ReleaseHistory, + example_project_dir: ExProjectDir, + change_to_ex_proj_dir: None, +): + version = list(single_release_history.released.keys())[-1] + release = single_release_history.released[version] + + example_project_dir.joinpath(".release_notes.md.j2").write_text( + """{{ "example-package" | create_pypi_url(release.version | string) }}""" + ) + + expected_content = f"https://pypi.org/project/example-package/{version}{os.linesep}" + + actual_content = generate_release_notes( + hvcs_client=Github(remote_url=example_git_https_url), + release=release, + template_dir=example_project_dir, + history=single_release_history, + style="angular", + mask_initial_release=False, + ) + + assert expected_content == actual_content From f853cf059b3323d7888b06fde09142184e7964e8 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 28 Jan 2025 00:17:27 -0500 Subject: [PATCH 019/160] feat: add `create_release_url` & `format_w_official_vcs_name` filters (#1161) * test(release-notes-context): add unit test to validate use `create_release_url` filter * test(release-notes-context): add unit test to validate use `format_w_official_vcs_name` filter * test(changelog-context): add unit test to validate use `create_release_url` filter * test(changelog-context): add unit test to validate use `format_w_official_vcs_name` filter * feat(vcs-github): add `create_release_url` & `format_w_official_vcs_name` filter functions * feat(vcs-gitlab): add `create_release_url` & `format_w_official_vcs_name` filter functiions * feat(vcs-gitea): add `create_release_url` & `format_w_official_vcs_name` filter functions * feat(vcs-bitbucket): add `format_w_official_vcs_name` filter function * docs(changelog-templates): define `create_release_url` & `format_w_official_vcs_name` filters * chore(release-notes): refactor to use latest filters from vcs --- config/release-templates/.release_notes.md.j2 | 10 +- docs/changelog_templates.rst | 74 ++++++++++++--- src/semantic_release/hvcs/bitbucket.py | 15 +++ src/semantic_release/hvcs/gitea.py | 21 +++++ src/semantic_release/hvcs/github.py | 21 +++++ src/semantic_release/hvcs/gitlab.py | 20 ++++ .../changelog/test_changelog_context.py | 91 +++++++++++++++++++ .../changelog/test_release_notes.py | 89 ++++++++++++++++++ 8 files changed, 318 insertions(+), 23 deletions(-) diff --git a/config/release-templates/.release_notes.md.j2 b/config/release-templates/.release_notes.md.j2 index b1e132587..a58588538 100644 --- a/config/release-templates/.release_notes.md.j2 +++ b/config/release-templates/.release_notes.md.j2 @@ -103,14 +103,8 @@ {{ "- %s" | format( format_link( - [ - "https://github.com", - repo_name, - repo_name, - "releases/tag", - release.version.as_tag(), - ] | join("/"), - "GitHub Release Assets", + release.version.as_tag() | create_release_url, + "{vcs_name} Release Assets" | format_w_official_vcs_name, ) ) }} diff --git a/docs/changelog_templates.rst b/docs/changelog_templates.rst index 1d86376f9..08dfc5edd 100644 --- a/docs/changelog_templates.rst +++ b/docs/changelog_templates.rst @@ -679,6 +679,24 @@ The filters provided vary based on the VCS configured and available features: https://pypi.org/project/example-package https://pypi.org/project/example-package/1.0.0 +* ``create_release_url (Callable[[TagStr], UrlStr])``: given a tag, return a URL to the release + page on the remote vcs. This filter is useful when you want to link to the release page on the + remote vcs. + + *Introduced in ${NEW_RELEASE_TAG}.* + + **Example Usage:** + + .. code:: jinja + + {{ "v1.0.0" | create_release_url }} + + **Markdown Output:** + + .. code:: markdown + + https://example.com/example/repo/releases/tag/v1.0.0 + * ``create_server_url (Callable[[PathStr, AuthStr | None, QueryStr | None, FragmentStr | None], UrlStr])``: when given a path, prepend the configured vcs server host and url scheme. Optionally you can provide, a auth string, a query string or a url fragment to be normalized into the @@ -838,6 +856,29 @@ The filters provided vary based on the VCS configured and available features: [#29](https://example.com/example/repo/pull/29) +* ``format_w_official_vcs_name (Callable[[str], str])``: given a format string, insert + the official VCS type name into the string and return. This filter is useful when you want to + display the proper name of the VCS type in a changelog or release notes. The filter supports + three different replace formats: ``%s``, ``{}``, and ``{vcs_name}``. + + *Introduced in ${NEW_RELEASE_TAG}.* + + **Example Usage:** + + .. code:: jinja + + {{ "%s Releases" | format_w_official_vcs_name }} + {{ "{} Releases" | format_w_official_vcs_name }} + {{ "{vcs_name} Releases" | format_w_official_vcs_name }} + + **Markdown Output:** + + .. code:: markdown + + GitHub Releases + GitHub Releases + GitHub Releases + * ``read_file (Callable[[str], str])``: given a file path, read the file and return the contents as a string. This function was added specifically to enable the changelog update feature where it would load the existing changelog @@ -879,21 +920,24 @@ The filters provided vary based on the VCS configured and available features: Availability of the documented filters can be found in the table below: -====================== ========= ===== ====== ====== -**filter - hvcs_type** bitbucket gitea github gitlab -====================== ========= ===== ====== ====== -autofit_text_width ✅ ✅ ✅ ✅ -convert_md_to_rst ✅ ✅ ✅ ✅ -create_server_url ✅ ✅ ✅ ✅ -create_repo_url ✅ ✅ ✅ ✅ -commit_hash_url ✅ ✅ ✅ ✅ -compare_url ✅ ❌ ✅ ✅ -issue_url ❌ ✅ ✅ ✅ -merge_request_url ❌ ❌ ❌ ✅ -pull_request_url ✅ ✅ ✅ ✅ -read_file ✅ ✅ ✅ ✅ -sort_numerically ✅ ✅ ✅ ✅ -====================== ========= ===== ====== ====== +========================== ========= ===== ====== ====== +**filter - hvcs_type** bitbucket gitea github gitlab +========================== ========= ===== ====== ====== +autofit_text_width ✅ ✅ ✅ ✅ +convert_md_to_rst ✅ ✅ ✅ ✅ +create_pypi_url ✅ ✅ ✅ ✅ +create_server_url ✅ ✅ ✅ ✅ +create_release_url ❌ ✅ ✅ ✅ +create_repo_url ✅ ✅ ✅ ✅ +commit_hash_url ✅ ✅ ✅ ✅ +compare_url ✅ ❌ ✅ ✅ +format_w_official_vcs_name ✅ ✅ ✅ ✅ +issue_url ❌ ✅ ✅ ✅ +merge_request_url ❌ ❌ ❌ ✅ +pull_request_url ✅ ✅ ✅ ✅ +read_file ✅ ✅ ✅ ✅ +sort_numerically ✅ ✅ ✅ ✅ +========================== ========= ===== ====== ====== .. seealso:: * `Filters `_ diff --git a/src/semantic_release/hvcs/bitbucket.py b/src/semantic_release/hvcs/bitbucket.py index 6501cacef..e0f9e8656 100644 --- a/src/semantic_release/hvcs/bitbucket.py +++ b/src/semantic_release/hvcs/bitbucket.py @@ -45,6 +45,7 @@ class Bitbucket(RemoteHvcsBase): `server.domain/rest/api/1.0` based on the documentation in April 2024. """ + OFFICIAL_NAME = "Bitbucket" DEFAULT_DOMAIN = "bitbucket.org" DEFAULT_API_SUBDOMAIN_PREFIX = "api" DEFAULT_API_PATH_CLOUD = "/2.0" @@ -216,6 +217,19 @@ def pull_request_url(self, pr_number: str | int) -> str: return "" + @staticmethod + def format_w_official_vcs_name(format_str: str) -> str: + if "%s" in format_str: + return format_str % Bitbucket.OFFICIAL_NAME + + if "{}" in format_str: + return format_str.format(Bitbucket.OFFICIAL_NAME) + + if "{vcs_name}" in format_str: + return format_str.format(vcs_name=Bitbucket.OFFICIAL_NAME) + + return format_str + def get_changelog_context_filters(self) -> tuple[Callable[..., Any], ...]: return ( self.create_server_url, @@ -223,6 +237,7 @@ def get_changelog_context_filters(self) -> tuple[Callable[..., Any], ...]: self.commit_hash_url, self.compare_url, self.pull_request_url, + self.format_w_official_vcs_name, ) def upload_dists(self, tag: str, dist_glob: str) -> int: diff --git a/src/semantic_release/hvcs/gitea.py b/src/semantic_release/hvcs/gitea.py index fa21d1870..c8e241122 100644 --- a/src/semantic_release/hvcs/gitea.py +++ b/src/semantic_release/hvcs/gitea.py @@ -34,6 +34,7 @@ class Gitea(RemoteHvcsBase): """Gitea helper class""" + OFFICIAL_NAME = "Gitea" DEFAULT_DOMAIN = "gitea.com" DEFAULT_API_PATH = "/api/v1" DEFAULT_ENV_TOKEN_NAME = "GITEA_TOKEN" # noqa: S105 @@ -380,6 +381,24 @@ def pull_request_url(self, pr_number: str | int) -> str: return "" + def create_release_url(self, tag: str = "") -> str: + tag_str = tag.strip() + tag_path = f"tag/{tag_str}" if tag_str else "" + return self.create_repo_url(repo_path=f"releases/{tag_path}") + + @staticmethod + def format_w_official_vcs_name(format_str: str) -> str: + if "%s" in format_str: + return format_str % Gitea.OFFICIAL_NAME + + if "{}" in format_str: + return format_str.format(Gitea.OFFICIAL_NAME) + + if "{vcs_name}" in format_str: + return format_str.format(vcs_name=Gitea.OFFICIAL_NAME) + + return format_str + def get_changelog_context_filters(self) -> tuple[Callable[..., Any], ...]: return ( self.create_server_url, @@ -387,6 +406,8 @@ def get_changelog_context_filters(self) -> tuple[Callable[..., Any], ...]: self.commit_hash_url, self.issue_url, self.pull_request_url, + self.create_release_url, + self.format_w_official_vcs_name, ) diff --git a/src/semantic_release/hvcs/github.py b/src/semantic_release/hvcs/github.py index 5ab58527f..016643267 100644 --- a/src/semantic_release/hvcs/github.py +++ b/src/semantic_release/hvcs/github.py @@ -73,6 +73,7 @@ class Github(RemoteHvcsBase): `server.domain/api/v3` based on the documentation in April 2024. """ + OFFICIAL_NAME = "GitHub" DEFAULT_DOMAIN = "github.com" DEFAULT_API_SUBDOMAIN_PREFIX = "api" DEFAULT_API_DOMAIN = f"{DEFAULT_API_SUBDOMAIN_PREFIX}.{DEFAULT_DOMAIN}" @@ -531,6 +532,24 @@ def pull_request_url(self, pr_number: str | int) -> str: return "" + def create_release_url(self, tag: str = "") -> str: + tag_str = tag.strip() + tag_path = f"tag/{tag_str}" if tag_str else "" + return self.create_repo_url(repo_path=f"releases/{tag_path}") + + @staticmethod + def format_w_official_vcs_name(format_str: str) -> str: + if "%s" in format_str: + return format_str % Github.OFFICIAL_NAME + + if "{}" in format_str: + return format_str.format(Github.OFFICIAL_NAME) + + if "{vcs_name}" in format_str: + return format_str.format(vcs_name=Github.OFFICIAL_NAME) + + return format_str + def get_changelog_context_filters(self) -> tuple[Callable[..., Any], ...]: return ( self.create_server_url, @@ -539,6 +558,8 @@ def get_changelog_context_filters(self) -> tuple[Callable[..., Any], ...]: self.compare_url, self.issue_url, self.pull_request_url, + self.create_release_url, + self.format_w_official_vcs_name, ) diff --git a/src/semantic_release/hvcs/gitlab.py b/src/semantic_release/hvcs/gitlab.py index 005a6f4dc..67b8e7512 100644 --- a/src/semantic_release/hvcs/gitlab.py +++ b/src/semantic_release/hvcs/gitlab.py @@ -41,6 +41,7 @@ class Gitlab(RemoteHvcsBase): # purposefully not CI_JOB_TOKEN as it is not a personal access token, # It is missing the permission to push to the repository, but has all others (releases, packages, etc.) + OFFICIAL_NAME = "GitLab" DEFAULT_DOMAIN = "gitlab.com" def __init__( @@ -276,6 +277,23 @@ def pull_request_url(self, pr_number: str | int) -> str: def upload_dists(self, tag: str, dist_glob: str) -> int: return super().upload_dists(tag, dist_glob) + def create_release_url(self, tag: str = "") -> str: + tag_str = tag.strip() + return self.create_repo_url(repo_path=f"/-/releases/{tag_str}") + + @staticmethod + def format_w_official_vcs_name(format_str: str) -> str: + if "%s" in format_str: + return format_str % Gitlab.OFFICIAL_NAME + + if "{}" in format_str: + return format_str.format(Gitlab.OFFICIAL_NAME) + + if "{vcs_name}" in format_str: + return format_str.format(vcs_name=Gitlab.OFFICIAL_NAME) + + return format_str + def get_changelog_context_filters(self) -> tuple[Callable[..., Any], ...]: return ( self.create_server_url, @@ -285,6 +303,8 @@ def get_changelog_context_filters(self) -> tuple[Callable[..., Any], ...]: self.issue_url, self.merge_request_url, self.pull_request_url, + self.create_release_url, + self.format_w_official_vcs_name, ) diff --git a/tests/unit/semantic_release/changelog/test_changelog_context.py b/tests/unit/semantic_release/changelog/test_changelog_context.py index 8204575f6..f665d78aa 100644 --- a/tests/unit/semantic_release/changelog/test_changelog_context.py +++ b/tests/unit/semantic_release/changelog/test_changelog_context.py @@ -4,6 +4,7 @@ from datetime import datetime from textwrap import dedent from typing import TYPE_CHECKING +from unittest import mock import pytest from git import Commit, Object, Repo @@ -659,3 +660,93 @@ def test_changelog_context_pypi_url_filter_tagged( # Evaluate assert expected_changelog == actual_changelog + + +@pytest.mark.parametrize("hvcs_client_class", [Github, Gitlab, Gitea]) +def test_changelog_context_release_url_filter( + example_git_https_url: str, + hvcs_client_class: type[Github | Gitlab | Gitea], + artificial_release_history: ReleaseHistory, + changelog_md_file: Path, +): + version = list(artificial_release_history.released.keys())[-1] + + changelog_tpl = dedent( + """\ + {% set release = context.history.released.values() | first + %}{{ + "[%s](%s)" | format( + release.version.as_tag(), + release.version.as_tag() | create_release_url, + ) + }} + """ + ) + + with mock.patch.dict(os.environ, {}, clear=True): + hvcs_client = hvcs_client_class(remote_url=example_git_https_url) + expected_changelog = dedent( + f"""\ + [{version.as_tag()}]({hvcs_client.create_release_url(version.as_tag())}) + """ + ) + + env = environment(trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True) + context = make_changelog_context( + hvcs_client=hvcs_client, + release_history=artificial_release_history, + mode=ChangelogMode.UPDATE, + prev_changelog_file=changelog_md_file, + insertion_flag="", + mask_initial_release=False, + ) + context.bind_to_environment(env) + + # Create changelog from template with environment + actual_changelog = env.from_string(changelog_tpl).render() + + # Evaluate + assert expected_changelog == actual_changelog + + +@pytest.mark.parametrize("hvcs_client_class", [Github, Gitlab, Gitea, Bitbucket]) +def test_changelog_context_format_w_official_name_filter( + example_git_https_url: str, + hvcs_client_class: type[Github | Gitlab | Gitea], + artificial_release_history: ReleaseHistory, + changelog_md_file: Path, +): + changelog_tpl = dedent( + """\ + {{ "%s" | format_w_official_vcs_name }} + {{ "{}" | format_w_official_vcs_name }} + {{ "{vcs_name}" | format_w_official_vcs_name }} + """ + ) + + with mock.patch.dict(os.environ, {}, clear=True): + hvcs_client = hvcs_client_class(remote_url=example_git_https_url) + expected_changelog = dedent( + f"""\ + {hvcs_client.OFFICIAL_NAME} + {hvcs_client.OFFICIAL_NAME} + {hvcs_client.OFFICIAL_NAME} + """ + ) + + env = environment(trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True) + context = make_changelog_context( + hvcs_client=hvcs_client, + release_history=artificial_release_history, + mode=ChangelogMode.UPDATE, + prev_changelog_file=changelog_md_file, + insertion_flag="", + mask_initial_release=False, + ) + context.bind_to_environment(env) + + # Create changelog from template with environment + actual_changelog = env.from_string(changelog_tpl).render() + + # Evaluate + assert expected_changelog == actual_changelog diff --git a/tests/unit/semantic_release/changelog/test_release_notes.py b/tests/unit/semantic_release/changelog/test_release_notes.py index 9f506d332..ec51bffcd 100644 --- a/tests/unit/semantic_release/changelog/test_release_notes.py +++ b/tests/unit/semantic_release/changelog/test_release_notes.py @@ -4,6 +4,7 @@ from pathlib import Path from textwrap import dedent from typing import TYPE_CHECKING +from unittest import mock import pytest @@ -601,3 +602,91 @@ def test_release_notes_context_pypi_url_filter_tagged( ) assert expected_content == actual_content + + +@pytest.mark.parametrize("hvcs_client_class", [Github, Gitlab, Gitea]) +def test_release_notes_context_release_url_filter( + example_git_https_url: str, + hvcs_client_class: type[Github | Gitlab | Gitea], + single_release_history: ReleaseHistory, + example_project_dir: ExProjectDir, + change_to_ex_proj_dir: None, +): + version = list(single_release_history.released.keys())[-1] + release = single_release_history.released[version] + + example_project_dir.joinpath(".release_notes.md.j2").write_text( + dedent( + """\ + {{ + "[%s](%s)" | format( + release.version.as_tag(), + release.version.as_tag() | create_release_url, + ) + }} + """ + ) + ) + + with mock.patch.dict(os.environ, {}, clear=True): + hvcs_client = hvcs_client_class(remote_url=example_git_https_url) + + expected_content = dedent( + f"""\ + [{version.as_tag()}]({hvcs_client.create_release_url(version.as_tag())}) + """ + ) + + actual_content = generate_release_notes( + hvcs_client=hvcs_client, + release=release, + template_dir=example_project_dir, + history=single_release_history, + style="angular", + mask_initial_release=False, + ) + + assert expected_content == actual_content + + +@pytest.mark.parametrize("hvcs_client_class", [Github, Gitlab, Gitea, Bitbucket]) +def test_release_notes_context_format_w_official_name_filter( + example_git_https_url: str, + hvcs_client_class: type[Github | Gitlab | Gitea], + single_release_history: ReleaseHistory, + example_project_dir: ExProjectDir, + change_to_ex_proj_dir: None, +): + version = list(single_release_history.released.keys())[-1] + release = single_release_history.released[version] + + example_project_dir.joinpath(".release_notes.md.j2").write_text( + dedent( + """\ + {{ "%s" | format_w_official_vcs_name }} + {{ "{}" | format_w_official_vcs_name }} + {{ "{vcs_name}" | format_w_official_vcs_name }} + """ + ) + ) + + with mock.patch.dict(os.environ, {}, clear=True): + hvcs_client = hvcs_client_class(remote_url=example_git_https_url) + expected_content = dedent( + f"""\ + {hvcs_client.OFFICIAL_NAME} + {hvcs_client.OFFICIAL_NAME} + {hvcs_client.OFFICIAL_NAME} + """ + ) + + actual_content = generate_release_notes( + hvcs_client=hvcs_client, + release=release, + template_dir=example_project_dir, + history=single_release_history, + style="angular", + mask_initial_release=False, + ) + + assert expected_content == actual_content From b71357682b371120a087219e70bd109828858177 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Thu, 30 Jan 2025 09:57:10 -0500 Subject: [PATCH 020/160] refactor(parser): ensure commit messages are converted to strings properly (#1163) --- src/semantic_release/commit_parser/token.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/semantic_release/commit_parser/token.py b/src/semantic_release/commit_parser/token.py index 021b43dc2..c100bad84 100644 --- a/src/semantic_release/commit_parser/token.py +++ b/src/semantic_release/commit_parser/token.py @@ -2,6 +2,7 @@ from typing import TYPE_CHECKING, NamedTuple, NoReturn, TypeVar, Union +from semantic_release.commit_parser.util import force_str from semantic_release.errors import CommitParseError if TYPE_CHECKING: # pragma: no cover @@ -109,9 +110,7 @@ def message(self) -> str: If the message is of type ``bytes`` then it will be decoded to a ``UTF-8`` string. """ - m = self.commit.message - message_str = m.decode("utf-8") if isinstance(m, bytes) else m - return message_str.replace("\r", "") + return force_str(self.commit.message).replace("\r", "") @property def hexsha(self) -> str: @@ -170,9 +169,7 @@ def message(self) -> str: If the message is of type ``bytes`` then it will be decoded to a ``UTF-8`` string. """ - m = self.commit.message - message_str = m.decode("utf-8") if isinstance(m, bytes) else m - return message_str.replace("\r", "") + return force_str(self.commit.message).replace("\r", "") @property def hexsha(self) -> str: From cf340c5256dea58aedad71a6bdf50b17eee53d2f Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 31 Jan 2025 00:18:56 -0500 Subject: [PATCH 021/160] fix: refactor parsing compatibility function to support older custom parsers (#1165) * fix(version): fix parsing compatibility w/ custom parsers resolves the error where the casting to a list was incorrectly implemented causing single results back from custom parsers (legacy return value) to be mis-interpreted which meant no parse level would be determined. Resolves: #1162 * fix(changelog): fix parsing compatibility w/ custom parsers resolves the error where the casting to a list was incorrectly implemented causing single results back from custom parsers (legacy return value) to be mis-interpreted which meant no parse level would be determined. Resolves: #1162 --- .../changelog/release_history.py | 31 +++++++- src/semantic_release/helpers.py | 9 ++- src/semantic_release/version/algorithm.py | 75 ++++++++++++------- 3 files changed, 83 insertions(+), 32 deletions(-) diff --git a/src/semantic_release/changelog/release_history.py b/src/semantic_release/changelog/release_history.py index 2d9361f75..f9e90221e 100644 --- a/src/semantic_release/changelog/release_history.py +++ b/src/semantic_release/changelog/release_history.py @@ -11,6 +11,7 @@ from semantic_release.commit_parser.token import ParsedCommit from semantic_release.commit_parser.util import force_str from semantic_release.enums import LevelBump +from semantic_release.helpers import validate_types_in_sequence from semantic_release.version.algorithm import tags_and_versions if TYPE_CHECKING: # pragma: no cover @@ -111,13 +112,35 @@ def from_git_history( # returns a ParseResult or list of ParseResult objects, # it is usually one, but we split a commit if a squashed merge is detected parse_results = commit_parser.parse(commit) - if not isinstance(parse_results, list): - parse_results = [parse_results] - is_squash_commit = bool(len(parse_results) > 1) + if not any( + ( + isinstance(parse_results, (ParseError, ParsedCommit)), + ( + ( + isinstance(parse_results, list) + or type(parse_results) == tuple + ) + and validate_types_in_sequence( + parse_results, (ParseError, ParsedCommit) + ) + ), + ) + ): + raise TypeError("Unexpected type returned from commit_parser.parse") + + results: list[ParseResult] = [ + *( + [parse_results] + if isinstance(parse_results, (ParseError, ParsedCommit)) + else parse_results + ), + ] + + is_squash_commit = bool(len(results) > 1) # iterate through parsed commits to add to changelog definition - for parsed_result in parse_results: + for parsed_result in results: commit_message = force_str(parsed_result.commit.message) commit_type = ( "unknown" diff --git a/src/semantic_release/helpers.py b/src/semantic_release/helpers.py index c05635718..46358b8d0 100644 --- a/src/semantic_release/helpers.py +++ b/src/semantic_release/helpers.py @@ -9,7 +9,7 @@ from functools import lru_cache, reduce, wraps from pathlib import Path, PurePosixPath from re import IGNORECASE, compile as regexp -from typing import TYPE_CHECKING, Any, Callable, NamedTuple, TypeVar +from typing import TYPE_CHECKING, Any, Callable, NamedTuple, Sequence, TypeVar from urllib.parse import urlsplit if TYPE_CHECKING: # pragma: no cover @@ -93,6 +93,13 @@ def text_reducer(text: str, filter_pair: tuple[Pattern[str], str]) -> str: return filter_pattern.sub(replacement, text) +def validate_types_in_sequence( + sequence: Sequence, types: type | tuple[type, ...] +) -> bool: + """Validate that all elements in a sequence are of a specific type""" + return all(isinstance(item, types) for item in sequence) + + def format_arg(value: Any) -> str: """Helper to format an argument an argument for logging""" if type(value) == str: diff --git a/src/semantic_release/version/algorithm.py b/src/semantic_release/version/algorithm.py index 90face7c8..c738e6581 100644 --- a/src/semantic_release/version/algorithm.py +++ b/src/semantic_release/version/algorithm.py @@ -7,9 +7,11 @@ from typing import TYPE_CHECKING, Iterable from semantic_release.commit_parser import ParsedCommit +from semantic_release.commit_parser.token import ParseError from semantic_release.const import DEFAULT_VERSION from semantic_release.enums import LevelBump, SemanticReleaseLogLevels from semantic_release.errors import InternalError, InvalidVersion +from semantic_release.helpers import validate_types_in_sequence if TYPE_CHECKING: # pragma: no cover from typing import Sequence @@ -95,7 +97,7 @@ def dfs(start_commit: Commit, stop_nodes: set[Commit]) -> Sequence[Commit]: return commits # Run a Depth First Search to find all the commits since the last release - commits_since_last_release = dfs( + return dfs( start_commit=head_commit, stop_nodes=set( head_commit.repo.iter_commits(latest_release_tag_str) @@ -104,15 +106,6 @@ def dfs(start_commit: Commit, stop_nodes: set[Commit]) -> Sequence[Commit]: ), ) - log_msg = ( - f"Found {len(commits_since_last_release)} commits since the last release!" - if len(commits_since_last_release) > 0 - else "No commits found since the last release!" - ) - logger.info(log_msg) - - return commits_since_last_release - def _increment_version( latest_version: Version, @@ -346,26 +339,54 @@ def next_version( ), ) - # Step 5. Parse the commits to determine the bump level that should be applied - parsed_levels: set[LevelBump] = { # type: ignore[var-annotated] # too complex for type checkers + logger.info( + f"Found {len(commits_since_last_release)} commits since the last release!" + if len(commits_since_last_release) > 0 + else "No commits found since the last release!" + ) + + # Step 5. apply the parser to each commit in the history (could return multiple results per commit) + parsed_results = list(map(commit_parser.parse, commits_since_last_release)) + + # Step 5A. Validation type check for the parser results (important because of possible custom parsers) + for parsed_result in parsed_results: + if not any( + ( + isinstance(parsed_result, (ParseError, ParsedCommit)), + type(parsed_result) == list + and validate_types_in_sequence( + parsed_result, (ParseError, ParsedCommit) + ), + type(parsed_result) == tuple + and validate_types_in_sequence( + parsed_result, (ParseError, ParsedCommit) + ), + ) + ): + raise TypeError("Unexpected type returned from commit_parser.parse") + + # Step 5B. Accumulate all parsed results into a single list accounting for possible multiple results per commit + consolidated_results: list[ParseResult] = reduce( + lambda accumulated_results, p_results: [ + *accumulated_results, + *( + # Cast to list if not already a list + p_results + if isinstance(p_results, list) or type(p_results) == tuple + else [p_results] + ), + ], + parsed_results, + [], + ) + + # Step 5C. Parse the commits to determine the bump level that should be applied + parsed_levels: set[LevelBump] = { parsed_result.bump # type: ignore[union-attr] # too complex for type checkers for parsed_result in filter( # Filter out any non-ParsedCommit results (i.e. ParseErrors) - lambda parsed_result: isinstance(parsed_result, ParsedCommit), # type: ignore[arg-type] - reduce( - # Accumulate all parsed results into a single list - lambda accumulated_results, parsed_results: [ - *accumulated_results, - *( - parsed_results - if isinstance(parsed_results, Iterable) - else [parsed_results] # type: ignore[list-item] - ), - ], - # apply the parser to each commit in the history (could return multiple results per commit) - map(commit_parser.parse, commits_since_last_release), - [], - ), + lambda parsed_result: isinstance(parsed_result, ParsedCommit), + consolidated_results, ) } From 463e43b897ee80dfaf7ce9d88d22ea8e652bcf55 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 31 Jan 2025 00:48:33 -0500 Subject: [PATCH 022/160] feat(parsers): add option `ignore_merge_commits` to discard parsing merge commits (#1164) * test(parser-angular): add unit test for ignoring merge commits * test(parser-scipy): add unit test for ignoring merge commits * test(parser-emoji): add unit test for ignoring merge commits * test(cmd-changelog): add validation of ignoring merge commits for changelog * refactor(parsed-commit): add merge commit detection function * feat(parser-angular): add a `ignore_merge_commits` option to discard parsing merge commits * feat(parser-emoji): add a `ignore_merge_commits` option to discard parsing merge commits * refactor(history): drop merge commits from history object completely * fix(changelog-templates): adjust default templates to avoid empty version sections * chore(config): set project to ignore merge commits by default --- pyproject.toml | 2 +- .../changelog/release_history.py | 10 ++ src/semantic_release/commit_parser/angular.py | 13 ++ src/semantic_release/commit_parser/emoji.py | 14 ++ src/semantic_release/commit_parser/token.py | 6 + .../angular/md/.components/changes.md.j2 | 12 +- .../angular/rst/.components/changes.rst.j2 | 15 +- .../cmd_changelog/test_changelog_parsing.py | 138 ++++++++++++++++++ .../commit_parser/test_angular.py | 28 ++++ .../commit_parser/test_emoji.py | 28 ++++ .../commit_parser/test_scipy.py | 28 ++++ 11 files changed, 281 insertions(+), 13 deletions(-) create mode 100644 tests/e2e/cmd_changelog/test_changelog_parsing.py diff --git a/pyproject.toml b/pyproject.toml index dac59175b..87efb1502 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -399,7 +399,7 @@ ignore_names = ["change_to_ex_proj_dir", "init_example_project"] [tool.semantic_release] logging_use_named_masks = true commit_parser = "angular" -commit_parser_options = { parse_squash_commits = true } +commit_parser_options = { parse_squash_commits = true, ignore_merge_commits = true } build_command = """ python -m pip install -e .[build] python -m build . diff --git a/src/semantic_release/changelog/release_history.py b/src/semantic_release/changelog/release_history.py index f9e90221e..887fa5492 100644 --- a/src/semantic_release/changelog/release_history.py +++ b/src/semantic_release/changelog/release_history.py @@ -52,6 +52,12 @@ def from_git_history( for tag, version in all_git_tags_and_versions } + ignore_merge_commits = bool( + hasattr(commit_parser, "options") + and hasattr(commit_parser.options, "ignore_merge_commits") + and getattr(commit_parser.options, "ignore_merge_commits") # noqa: B009 + ) + # Strategy: # Loop through commits in history, parsing as we go. # Add these commits to `unreleased` as a key-value mapping @@ -159,6 +165,10 @@ def from_git_history( else parsed_result.bump ) + if ignore_merge_commits and parsed_result.is_merge_commit(): + log.info("Excluding merge commit[%s]", parsed_result.short_hash) + continue + # Skip excluded commits except for any commit causing a version bump # Reasoning: if a commit causes a version bump, and no other commits # are included, then the changelog will be empty. Even if ther was other diff --git a/src/semantic_release/commit_parser/angular.py b/src/semantic_release/commit_parser/angular.py index 511d73a38..80f76571a 100644 --- a/src/semantic_release/commit_parser/angular.py +++ b/src/semantic_release/commit_parser/angular.py @@ -101,6 +101,10 @@ class AngularParserOptions(ParserOptions): parse_squash_commits: bool = False """Toggle flag for whether or not to parse squash commits""" + # TODO: breaking change v10, change default to True + ignore_merge_commits: bool = False + """Toggle flag for whether or not to ignore merge commits""" + @property def tag_to_level(self) -> dict[str, LevelBump]: """A mapping of commit tags to the level bump they should result in.""" @@ -315,6 +319,10 @@ def parse_message(self, message: str) -> ParsedMessageResult | None: linked_merge_request=linked_merge_request, ) + @staticmethod + def is_merge_commit(commit: Commit) -> bool: + return len(commit.parents) > 1 + def parse_commit(self, commit: Commit) -> ParseResult: if not (parsed_msg_result := self.parse_message(force_str(commit.message))): return _logged_parse_error( @@ -336,6 +344,11 @@ def parse(self, commit: Commit) -> ParseResult | list[ParseResult]: multiple commits, each of which will be parsed separately. Single commits will be returned as a list of a single ParseResult. """ + if self.options.ignore_merge_commits and self.is_merge_commit(commit): + return _logged_parse_error( + commit, "Ignoring merge commit: %s" % commit.hexsha[:8] + ) + separate_commits: list[Commit] = ( self.unsquash_commit(commit) if self.options.parse_squash_commits diff --git a/src/semantic_release/commit_parser/emoji.py b/src/semantic_release/commit_parser/emoji.py index 5b8479f18..fa0ad2304 100644 --- a/src/semantic_release/commit_parser/emoji.py +++ b/src/semantic_release/commit_parser/emoji.py @@ -17,6 +17,7 @@ from semantic_release.commit_parser.token import ( ParsedCommit, ParsedMessageResult, + ParseError, ParseResult, ) from semantic_release.commit_parser.util import ( @@ -97,6 +98,10 @@ class EmojiParserOptions(ParserOptions): parse_squash_commits: bool = False """Toggle flag for whether or not to parse squash commits""" + # TODO: breaking change v10, change default to True + ignore_merge_commits: bool = False + """Toggle flag for whether or not to ignore merge commits""" + @property def tag_to_level(self) -> dict[str, LevelBump]: """A mapping of commit tags to the level bump they should result in.""" @@ -304,6 +309,10 @@ def parse_message(self, message: str) -> ParsedMessageResult: linked_merge_request=linked_merge_request, ) + @staticmethod + def is_merge_commit(commit: Commit) -> bool: + return len(commit.parents) > 1 + def parse_commit(self, commit: Commit) -> ParseResult: return ParsedCommit.from_parsed_message_result( commit, self.parse_message(force_str(commit.message)) @@ -317,6 +326,11 @@ def parse(self, commit: Commit) -> ParseResult | list[ParseResult]: multiple commits, each of which will be parsed separately. Single commits will be returned as a list of a single ParseResult. """ + if self.options.ignore_merge_commits and self.is_merge_commit(commit): + err_msg = "Ignoring merge commit: %s" % commit.hexsha[:8] + logger.debug(err_msg) + return ParseError(commit, err_msg) + separate_commits: list[Commit] = ( self.unsquash_commit(commit) if self.options.parse_squash_commits diff --git a/src/semantic_release/commit_parser/token.py b/src/semantic_release/commit_parser/token.py index c100bad84..dae8d6f59 100644 --- a/src/semantic_release/commit_parser/token.py +++ b/src/semantic_release/commit_parser/token.py @@ -131,6 +131,9 @@ def linked_pull_request(self) -> str: """An alias to the linked_merge_request attribute.""" return self.linked_merge_request + def is_merge_commit(self) -> bool: + return bool(len(self.commit.parents) > 1) + @staticmethod def from_parsed_message_result( commit: Commit, parsed_message_result: ParsedMessageResult @@ -185,6 +188,9 @@ def short_hash(self) -> str: """A short representation of the hash value (in hex) of the commit.""" return self.hexsha[:7] + def is_merge_commit(self) -> bool: + return bool(len(self.commit.parents) > 1) + def raise_error(self) -> NoReturn: """A convience method to raise a CommitParseError with the error message.""" raise CommitParseError(self.error) diff --git a/src/semantic_release/data/templates/angular/md/.components/changes.md.j2 b/src/semantic_release/data/templates/angular/md/.components/changes.md.j2 index fe1069447..2bade3b74 100644 --- a/src/semantic_release/data/templates/angular/md/.components/changes.md.j2 +++ b/src/semantic_release/data/templates/angular/md/.components/changes.md.j2 @@ -51,11 +51,13 @@ EXAMPLE: }}{% endfor %}{# # # PRINT SECTION (header & commits) -#}{{ "\n" -}}{{ "### %s\n" | format(type_ | title) -}}{{ "\n" -}}{{ "%s\n" | format(commit_descriptions | unique | join("\n\n")) -}}{% endfor +#}{% if commit_descriptions | length > 0 +%}{{ "\n" +}}{{ "### %s\n" | format(type_ | title) +}}{{ "\n" +}}{{ "%s\n" | format(commit_descriptions | unique | join("\n\n")) +}}{% endif +%}{% endfor %}{# # Determine if there are any breaking change commits by filtering the list by breaking descriptions # commit_objects is a list of tuples [("Features", [ParsedCommit(), ...]), ("Bug Fixes", [ParsedCommit(), ...])] diff --git a/src/semantic_release/data/templates/angular/rst/.components/changes.rst.j2 b/src/semantic_release/data/templates/angular/rst/.components/changes.rst.j2 index d064838f6..07d6729d3 100644 --- a/src/semantic_release/data/templates/angular/rst/.components/changes.rst.j2 +++ b/src/semantic_release/data/templates/angular/rst/.components/changes.rst.j2 @@ -78,13 +78,14 @@ BREAKING CHANGES }}{% endfor %}{# # # PRINT SECTION (Header & Commits) -#}{{ "\n" -}}{{ section_header ~ "\n" -}}{{ generate_heading_underline(section_header, '-') ~ "\n" -}}{{ - "\n%s\n" | format(commit_descriptions | unique | join("\n\n")) - -}}{% endfor +#}{% if commit_descriptions | length > 0 +%}{{ "\n" +}}{{ section_header ~ "\n" +}}{{ generate_heading_underline(section_header, '-') +}}{{ "\n" +}}{{ "\n%s\n" | format(commit_descriptions | unique | join("\n\n")) +}}{% endif +%}{% endfor %}{# # Determine if there are any breaking change commits by filtering the list by breaking descriptions # commit_objects is a list of tuples [("Features", [ParsedCommit(), ...]), ("Bug Fixes", [ParsedCommit(), ...])] diff --git a/tests/e2e/cmd_changelog/test_changelog_parsing.py b/tests/e2e/cmd_changelog/test_changelog_parsing.py new file mode 100644 index 000000000..3f8dc052a --- /dev/null +++ b/tests/e2e/cmd_changelog/test_changelog_parsing.py @@ -0,0 +1,138 @@ +from __future__ import annotations + +import os +import shutil +from pathlib import Path +from re import MULTILINE, compile as regexp +from typing import TYPE_CHECKING + +import pytest +from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture + +from semantic_release.changelog.context import ChangelogMode +from semantic_release.cli.commands.main import main +from semantic_release.cli.const import JINJA2_EXTENSION + +from tests.const import CHANGELOG_SUBCMD, MAIN_PROG_NAME +from tests.fixtures.example_project import ( + default_changelog_md_template, + default_changelog_rst_template, + default_md_changelog_insertion_flag, + default_rst_changelog_insertion_flag, + example_changelog_md, + example_changelog_rst, +) +from tests.fixtures.repos.git_flow import ( + repo_w_git_flow_angular_commits, + repo_w_git_flow_scipy_commits, +) +from tests.util import assert_successful_exit_code + +if TYPE_CHECKING: + from click.testing import CliRunner + + from tests.fixtures.example_project import UpdatePyprojectTomlFn + from tests.fixtures.git_repo import BuiltRepoResult + + +@pytest.mark.parametrize( + "changelog_file, insertion_flag, default_changelog_template, changes_tpl_file", + [ + ( + # ChangelogOutputFormat.MARKDOWN + lazy_fixture(example_changelog_md.__name__), + lazy_fixture(default_md_changelog_insertion_flag.__name__), + lazy_fixture(default_changelog_md_template.__name__), + Path(".components", "changes.md.j2"), + ), + ( + # ChangelogOutputFormat.RESTRUCTURED_TEXT + lazy_fixture(example_changelog_rst.__name__), + lazy_fixture(default_rst_changelog_insertion_flag.__name__), + lazy_fixture(default_changelog_rst_template.__name__), + Path(".components", "changes.rst.j2"), + ), + ], +) +@pytest.mark.parametrize( + "repo_result", + [ + pytest.param( + lazy_fixture(repo_fixture_name), + marks=pytest.mark.comprehensive, + ) + for repo_fixture_name in [ + repo_w_git_flow_angular_commits.__name__, + repo_w_git_flow_scipy_commits.__name__, + ] + ], +) +def test_changelog_parsing_ignore_merge_commits( + cli_runner: CliRunner, + repo_result: BuiltRepoResult, + update_pyproject_toml: UpdatePyprojectTomlFn, + example_project_template_dir: Path, + changelog_file: Path, + insertion_flag: str, + default_changelog_template: Path, + changes_tpl_file: Path, +): + repo = repo_result["repo"] + expected_changelog_content = changelog_file.read_text() + + update_pyproject_toml( + "tool.semantic_release.commit_parser_options.ignore_merge_commits", True + ) + update_pyproject_toml( + "tool.semantic_release.changelog.mode", ChangelogMode.UPDATE.value + ) + update_pyproject_toml( + "tool.semantic_release.changelog.insertion_flag", + insertion_flag, + ) + update_pyproject_toml( + "tool.semantic_release.changelog.template_dir", + str(example_project_template_dir.relative_to(repo.working_dir)), + ) + update_pyproject_toml( + "tool.semantic_release.changelog.exclude_commit_patterns", + [ + r"""Initial Commit.*""", + ], + ) + + # Force custom changelog to be a copy of the default changelog + shutil.copytree( + src=default_changelog_template.parent, + dst=example_project_template_dir, + dirs_exist_ok=True, + ) + + # Remove the "unknown" filter from the changelog template to enable Merge commits + patch = regexp( + r'^(#}{% *for type_, commits in commit_objects) if type_ != "unknown"', + MULTILINE, + ) + changes_file = example_project_template_dir.joinpath(changes_tpl_file) + changes_file.write_text(patch.sub(r"\1", changes_file.read_text())) + + # Make sure the prev_changelog_file is the same as the current changelog + changelog_tpl_file = example_project_template_dir.joinpath( + changelog_file.name + ).with_suffix(str.join("", [changelog_file.suffix, JINJA2_EXTENSION])) + changelog_tpl_file.write_text( + regexp(r"= ctx.prev_changelog_file").sub( + rf'= "{changelog_file.name}"', changelog_tpl_file.read_text() + ) + ) + + # Remove the changelog to force re-generation with new configurations + os.remove(str(changelog_file.resolve())) + + # Act + cli_cmd = [MAIN_PROG_NAME, CHANGELOG_SUBCMD] + result = cli_runner.invoke(main, cli_cmd[1:]) + + # Evaluate + assert_successful_exit_code(result, cli_cmd) + assert expected_changelog_content == changelog_file.read_text() diff --git a/tests/unit/semantic_release/commit_parser/test_angular.py b/tests/unit/semantic_release/commit_parser/test_angular.py index 1ce75734a..5fc69feb1 100644 --- a/tests/unit/semantic_release/commit_parser/test_angular.py +++ b/tests/unit/semantic_release/commit_parser/test_angular.py @@ -1129,3 +1129,31 @@ def test_parser_custom_patch_tags(make_commit_obj: MakeCommitObjFn): result = next(iter(parsed_results)) assert isinstance(result, ParsedCommit) assert result.bump is LevelBump.PATCH + + +def test_parser_ignore_merge_commit( + default_angular_parser: AngularCommitParser, + make_commit_obj: MakeCommitObjFn, +): + # Setup: Enable parsing of linked issues + parser = AngularCommitParser( + options=AngularParserOptions( + **{ + **default_angular_parser.options.__dict__, + "ignore_merge_commits": True, + } + ) + ) + + base_commit = make_commit_obj("Merge branch 'fix/fix-feature' into 'main'") + incomming_commit = make_commit_obj("feat: add a new feature") + + # Setup: Create a merge commit + merge_commit = make_commit_obj("Merge branch 'feat/add-new-feature' into 'main'") + merge_commit.parents = [base_commit, incomming_commit] + + # Action + parsed_result = parser.parse(merge_commit) + + assert isinstance(parsed_result, ParseError) + assert "Ignoring merge commit" in parsed_result.error diff --git a/tests/unit/semantic_release/commit_parser/test_emoji.py b/tests/unit/semantic_release/commit_parser/test_emoji.py index 30c52da41..b5e172c52 100644 --- a/tests/unit/semantic_release/commit_parser/test_emoji.py +++ b/tests/unit/semantic_release/commit_parser/test_emoji.py @@ -991,3 +991,31 @@ def test_parser_squashed_commit_github_squash_style( assert expected.get("breaking_descriptions", []) == result.breaking_descriptions assert expected.get("linked_issues", ()) == result.linked_issues assert expected.get("linked_merge_request", "") == result.linked_merge_request + + +def test_parser_ignore_merge_commit( + default_emoji_parser: EmojiCommitParser, + make_commit_obj: MakeCommitObjFn, +): + # Setup: Enable parsing of linked issues + parser = EmojiCommitParser( + options=EmojiParserOptions( + **{ + **default_emoji_parser.options.__dict__, + "ignore_merge_commits": True, + } + ) + ) + + base_commit = make_commit_obj("Merge branch 'fix/fix-feature' into 'main'") + incomming_commit = make_commit_obj("feat: add a new feature") + + # Setup: Create a merge commit + merge_commit = make_commit_obj("Merge branch 'feat/add-new-feature' into 'main'") + merge_commit.parents = [base_commit, incomming_commit] + + # Action + parsed_result = parser.parse(merge_commit) + + assert isinstance(parsed_result, ParseError) + assert "Ignoring merge commit" in parsed_result.error diff --git a/tests/unit/semantic_release/commit_parser/test_scipy.py b/tests/unit/semantic_release/commit_parser/test_scipy.py index 8fc64fea4..7682b1c69 100644 --- a/tests/unit/semantic_release/commit_parser/test_scipy.py +++ b/tests/unit/semantic_release/commit_parser/test_scipy.py @@ -1029,3 +1029,31 @@ def test_parser_return_linked_issues_from_commit_message( result = next(iter(parsed_results)) assert isinstance(result, ParsedCommit) assert tuple(linked_issues) == result.linked_issues + + +def test_parser_ignore_merge_commit( + default_scipy_parser: ScipyCommitParser, + make_commit_obj: MakeCommitObjFn, +): + # Setup: Enable parsing of linked issues + parser = ScipyCommitParser( + options=ScipyParserOptions( + **{ + **default_scipy_parser.options.__dict__, + "ignore_merge_commits": True, + } + ) + ) + + base_commit = make_commit_obj("Merge branch 'fix/fix-feature' into 'main'") + incomming_commit = make_commit_obj("feat: add a new feature") + + # Setup: Create a merge commit + merge_commit = make_commit_obj("Merge branch 'feat/add-new-feature' into 'main'") + merge_commit.parents = [base_commit, incomming_commit] + + # Action + parsed_result = parser.parse(merge_commit) + + assert isinstance(parsed_result, ParseError) + assert "Ignoring merge commit" in parsed_result.error From 834ce323007c58229abf115ef2016a348de9ee66 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 2 Feb 2025 00:45:33 -0500 Subject: [PATCH 023/160] feat(changelog): add additional release info to changeling from commit `NOTICE`'s (#1166) * test(parser-angular): add unit test for extracting release notices from commits * test(parser-angular): refactor test params to match valid commit messages * test(parser-scipy): add unit test for extracting release notices from commits * test(parser-emoji): add unit test for extracting release notices from commits * test(release-notes): add test of default template for additional release info section * test(changelog): add test of default template for additional release info section * feat(commit-parser): enable parsers to identify additional release notices from commit msgs * feat(parser-angular): add functionality to parse out `NOTICE:` prefixed statements in commits Resolves: #223 * feat(parser-emoji): add functionality to parse out `NOTICE:` prefixed statements in commits Resolves: #223 * feat(changelog-md): add additional release info section to default markdown template Resolves: #223 * feat(changelog-rst): add additional release info section to default ReStructuredText template Resolves: #223 * fix(parser-angular): adjust parser to prevent empty message extractions * fix(parser-emoji): adjust parser to prevent empty message extractions * refactor(util): tweak parse paragraphs for git footers & multi-line descriptions * chore(changelog): update psr templates to include an additional release info section --- .../.components/changes.md.j2 | 42 +- .../.components/changes.rst.j2 | 44 +- .../.components/macros.md.j2 | 47 ++ .../.components/macros.rst.j2 | 47 ++ config/release-templates/.release_notes.md.j2 | 6 + src/semantic_release/commit_parser/angular.py | 33 +- src/semantic_release/commit_parser/emoji.py | 32 +- src/semantic_release/commit_parser/token.py | 10 + src/semantic_release/commit_parser/util.py | 14 +- .../angular/md/.components/changes.md.j2 | 43 ++ .../angular/md/.components/macros.md.j2 | 66 +++ .../templates/angular/md/.release_notes.md.j2 | 19 +- .../angular/rst/.components/changes.rst.j2 | 50 +- .../angular/rst/.components/macros.rst.j2 | 66 +++ .../semantic_release/changelog/conftest.py | 178 +++++++ .../changelog/test_default_changelog.py | 447 ++++++++++++++++++ .../changelog/test_release_notes.py | 323 +++++++++++++ .../commit_parser/test_angular.py | 119 ++++- .../commit_parser/test_emoji.py | 82 ++++ .../commit_parser/test_scipy.py | 82 ++++ 20 files changed, 1689 insertions(+), 61 deletions(-) diff --git a/config/release-templates/.components/changes.md.j2 b/config/release-templates/.components/changes.md.j2 index 8a5f38f3c..87d600924 100644 --- a/config/release-templates/.components/changes.md.j2 +++ b/config/release-templates/.components/changes.md.j2 @@ -1,7 +1,8 @@ {% from 'macros.md.j2' import apply_alphabetical_ordering_by_brk_descriptions %}{% from 'macros.md.j2' import apply_alphabetical_ordering_by_descriptions +%}{% from 'macros.md.j2' import apply_alphabetical_ordering_by_release_notices %}{% from 'macros.md.j2' import emoji_map, format_breaking_changes_description -%}{% from 'macros.md.j2' import format_commit_summary_line +%}{% from 'macros.md.j2' import format_commit_summary_line, format_release_notice %}{% from 'macros.md.j2' import section_heading_order, section_heading_translations %}{# EXAMPLE: @@ -27,6 +28,14 @@ EXAMPLE: - **scope**: this breaking change has a scope to identify the part of the code that this breaking change applies to for better context. +### 💡 ADDITIONAL RELEASE INFORMATION + +- This is a release note that provides additional information about the release + that is not a breaking change or a feature/bug fix. + +- **scope**: this release note has a scope to identify the part of the code that + this release note applies to for better context. + #}{% set max_line_width = max_line_width | default(100) %}{% set hanging_indent = hanging_indent | default(2) %}{# @@ -51,15 +60,20 @@ EXAMPLE: }}{{ "%s\n" | format(commit_descriptions | unique | join("\n\n")) }}{% endfor %}{# - # # Determine if any commits have a breaking change description + # # Determine if any commits have a breaking change or release notice # # commit_objects is a dictionary of strings to a list of commits { "features", [ParsedCommit(), ...] } #}{% set breaking_commits = [] +%}{% set notice_commits = [] %}{% for commits in commit_objects.values() %}{% set valid_commits = commits | rejectattr("error", "defined") %}{# # Filter out breaking change commits that have no breaking descriptions #}{% set _ = breaking_commits.extend( valid_commits | selectattr("breaking_descriptions.0") ) +%}{# # Filter out ParsedCommits commits that have no release notices +#}{% set _ = notice_commits.extend( + valid_commits | selectattr("release_notices.0") + ) %}{% endfor %}{# #}{% if breaking_commits | length > 0 @@ -85,4 +99,28 @@ EXAMPLE: "\n%s\n" | format(brking_descriptions | unique | join("\n\n")) }}{# #}{% endif +%}{# +#}{% if notice_commits | length > 0 +%}{# PREPROCESS COMMITS +#}{% set notice_ns = namespace(commits=notice_commits) +%}{% set _ = apply_alphabetical_ordering_by_release_notices(notice_ns) +%}{# +#}{% set release_notices = [] +%}{# +#}{% for commit in notice_ns.commits +%}{% set full_description = "- %s" | format( + format_release_notice(commit).split("\n\n") | join("\n\n- ") + ) +%}{% set _ = release_notices.append( + full_description | autofit_text_width(max_line_width, hanging_indent) + ) +%}{% endfor +%}{# + # # PRINT RELEASE NOTICE INFORMATION (header & descriptions) +#}{{ "\n" +}}{{ "### %s ADDITIONAL RELEASE INFORMATION\n" | format(emoji_map["release_note"]) +}}{{ + "\n%s\n" | format(release_notices | unique | join("\n\n")) +}}{# +#}{% endif %} diff --git a/config/release-templates/.components/changes.rst.j2 b/config/release-templates/.components/changes.rst.j2 index e0931376d..a0c6e4037 100644 --- a/config/release-templates/.components/changes.rst.j2 +++ b/config/release-templates/.components/changes.rst.j2 @@ -1,8 +1,9 @@ {% from 'macros.rst.j2' import apply_alphabetical_ordering_by_brk_descriptions %}{% from 'macros.rst.j2' import apply_alphabetical_ordering_by_descriptions +%}{% from 'macros.rst.j2' import apply_alphabetical_ordering_by_release_notices %}{% from 'macros.rst.j2' import emoji_map, extract_issue_link_references, extract_pr_link_reference %}{% from 'macros.rst.j2' import format_breaking_changes_description, format_commit_summary_line -%}{% from 'macros.rst.j2' import format_link_reference +%}{% from 'macros.rst.j2' import format_link_reference, format_release_notice %}{% from 'macros.rst.j2' import generate_heading_underline, section_heading_order %}{% from 'macros.rst.j2' import section_heading_translations %}{# @@ -29,6 +30,15 @@ * **scope**: this breaking change has a scope to identify the part of the code that this breaking change applies to for better context. +💡 ADDITIONAL RELEASE INFORMATION +--------------------------------- + +* This is a release note that provides additional information about the release + that is not a breaking change or a feature/bug fix. + +* **scope**: this release note has a scope to identify the part of the code that + this release note applies to for better context. + .. _8a7B8ec: https://domain.com/owner/repo/commit/8a7b8ec .. _abcdef0: https://domain.com/owner/repo/commit/abcdef0 .. _PR#10: https://domain.com/namespace/repo/pull/10 @@ -88,15 +98,20 @@ }}{% endfor %}{# - # # Determine if any commits have a breaking change description + # # Determine if any commits have a breaking change or release notice # # commit_objects is a dictionary of strings to a list of commits { "features", [ParsedCommit(), ...] } #}{% set breaking_commits = [] +%}{% set notice_commits = [] %}{% for commits in commit_objects.values() %}{% set valid_commits = commits | rejectattr("error", "defined") %}{# # Filter out breaking change commits that have no breaking descriptions #}{% set _ = breaking_commits.extend( valid_commits | selectattr("breaking_descriptions.0") ) +%}{# # Filter out ParsedCommits commits that have no release notices +#}{% set _ = notice_commits.extend( + valid_commits | selectattr("release_notices.0") + ) %}{% endfor %}{# #}{% if breaking_commits | length > 0 @@ -123,6 +138,31 @@ "\n%s\n" | format(brking_descriptions | unique | join("\n\n")) }}{# #}{% endif +%}{# +#}{% if notice_commits | length > 0 +%}{# PREPROCESS COMMITS +#}{% set notice_ns = namespace(commits=notice_commits) +%}{% set _ = apply_alphabetical_ordering_by_release_notices(notice_ns) +%}{# +#}{% set release_notices = [] +%}{# +#}{% for commit in notice_ns.commits +%}{% set full_description = "* %s" | format( + format_release_notice(commit).split("\n\n") | join("\n\n* ") + ) +%}{% set _ = release_notices.append( + full_description | convert_md_to_rst | autofit_text_width(max_line_width, hanging_indent) + ) +%}{% endfor +%}{# + # # PRINT RELEASE NOTICE INFORMATION (header & descriptions) +#}{{ "\n" +}}{{ "%s ADDITIONAL RELEASE INFORMATION\n" | format(emoji_map["release_note"]) +}}{{ "---------------------------------\n" +}}{{ + "\n%s\n" | format(release_notices | unique | join("\n\n")) +}}{# +#}{% endif %}{# # # # PRINT POST PARAGRAPH LINKS diff --git a/config/release-templates/.components/macros.md.j2 b/config/release-templates/.components/macros.md.j2 index 01e931fc3..7332fc741 100644 --- a/config/release-templates/.components/macros.md.j2 +++ b/config/release-templates/.components/macros.md.j2 @@ -147,6 +147,41 @@ %} +{# + MACRO: format the release notice by: + - Capitalizing the description + - Adding an optional scope prefix +#}{% macro format_release_notice(commit) +%}{% set ns = namespace(full_description="") +%}{# +#}{% if commit.error is undefined +%}{% for paragraph in commit.release_notices +%}{% if paragraph | trim | length > 0 +%}{# +#}{% set paragraph_text = capitalize_first_letter_only(paragraph) | trim | safe +%}{# +#}{% set ns.full_description = [ + ns.full_description, + paragraph_text + ] | join("\n\n") +%}{# +#}{% endif +%}{% endfor +%}{# +#}{% set ns.full_description = ns.full_description | trim +%}{# +#}{% if commit.scope +%}{% set ns.full_description = "**%s**: %s" | format( + commit.scope, ns.full_description + ) +%}{% endif +%}{% endif +%}{# +#}{{ ns.full_description +}}{% endmacro +%} + + {# MACRO: order commits alphabetically by scope and attribute - Commits are sorted based on scope and then the attribute alphabetically @@ -198,3 +233,15 @@ %}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'breaking_descriptions.0') %}{% endmacro %} + + +{# + MACRO: apply smart ordering of commits objects based on alphabetized release notices and then scopes + - Commits are sorted based on the commit type and the commit message + - Commits are grouped by the commit type + - parameter: ns (namespace) object with a commits list + - returns None but modifies the ns.commits list in place +#}{% macro apply_alphabetical_ordering_by_release_notices(ns) +%}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'release_notices.0') +%}{% endmacro +%} diff --git a/config/release-templates/.components/macros.rst.j2 b/config/release-templates/.components/macros.rst.j2 index 6a9955afc..a0621c252 100644 --- a/config/release-templates/.components/macros.rst.j2 +++ b/config/release-templates/.components/macros.rst.j2 @@ -200,6 +200,41 @@ %} +{# + MACRO: format the release notice by: + - Capitalizing the description + - Adding an optional scope prefix +#}{% macro format_release_notice(commit) +%}{% set ns = namespace(full_description="") +%}{# +#}{% if commit.error is undefined +%}{% for paragraph in commit.release_notices +%}{% if paragraph | trim | length > 0 +%}{# +#}{% set paragraph_text = capitalize_first_letter_only(paragraph) | trim | safe +%}{# +#}{% set ns.full_description = [ + ns.full_description, + paragraph_text + ] | join("\n\n") +%}{# +#}{% endif +%}{% endfor +%}{# +#}{% set ns.full_description = ns.full_description | trim +%}{# +#}{% if commit.scope +%}{% set ns.full_description = "**%s**: %s" | format( + commit.scope, ns.full_description + ) +%}{% endif +%}{% endif +%}{# +#}{{ ns.full_description +}}{% endmacro +%} + + {# MACRO: order commits alphabetically by scope and attribute - Commits are sorted based on scope and then the attribute alphabetically @@ -251,3 +286,15 @@ %}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'breaking_descriptions.0') %}{% endmacro %} + + +{# + MACRO: apply smart ordering of commits objects based on alphabetized release notices and then scopes + - Commits are sorted based on the commit type and the commit message + - Commits are grouped by the commit type + - parameter: ns (namespace) object with a commits list + - returns None but modifies the ns.commits list in place +#}{% macro apply_alphabetical_ordering_by_release_notices(ns) +%}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'release_notices.0') +%}{% endmacro +%} diff --git a/config/release-templates/.release_notes.md.j2 b/config/release-templates/.release_notes.md.j2 index a58588538..147bc12e1 100644 --- a/config/release-templates/.release_notes.md.j2 +++ b/config/release-templates/.release_notes.md.j2 @@ -19,6 +19,12 @@ - **scope**: this breaking change has a scope to identify the part of the code that this breaking change applies to for better context. +### 💡 ADDITIONAL RELEASE INFORMATION + +- This is a release note that provides additional information about the release that is not a breaking change or a feature/bug fix. + +- **scope**: this release note has a scope to identify the part of the code that this release note applies to for better context. + ### ✅ Resolved Issues - [#000](https://domain.com/namespace/repo/issues/000): _Title_ diff --git a/src/semantic_release/commit_parser/angular.py b/src/semantic_release/commit_parser/angular.py index 80f76571a..ca739cc91 100644 --- a/src/semantic_release/commit_parser/angular.py +++ b/src/semantic_release/commit_parser/angular.py @@ -192,6 +192,7 @@ def __init__(self, options: AngularParserOptions | None = None) -> None: ), flags=re.MULTILINE | re.IGNORECASE, ) + self.notice_selector = regexp(r"^NOTICE: (?P.+)$") self.filters = { "typo-extra-spaces": (regexp(r"(\S) +(\S)"), r"\1 \2"), "git-header-commit": ( @@ -236,11 +237,18 @@ def get_default_options() -> AngularParserOptions: def commit_body_components_separator( self, accumulator: dict[str, list[str]], text: str ) -> dict[str, list[str]]: - if match := breaking_re.match(text): - accumulator["breaking_descriptions"].append(match.group(1) or "") + if (match := breaking_re.match(text)) and (brk_desc := match.group(1)): + accumulator["breaking_descriptions"].append(brk_desc) # TODO: breaking change v10, removes breaking change footers from descriptions # return accumulator + elif (match := self.notice_selector.match(text)) and ( + notice := match.group("notice") + ): + accumulator["notices"].append(notice) + # TODO: breaking change v10, removes notice footers from descriptions + # return accumulator + elif match := self.issue_selector.search(text): # if match := self.issue_selector.search(text): predicate = regexp(r",? and | *[,;/& ] *").sub( @@ -256,11 +264,12 @@ def commit_body_components_separator( predicate.split(","), ) ) - accumulator["linked_issues"] = sort_numerically( - set(accumulator["linked_issues"]).union(new_issue_refs) - ) - # TODO: breaking change v10, removes resolution footers from descriptions - # return accumulator + if new_issue_refs: + accumulator["linked_issues"] = sort_numerically( + set(accumulator["linked_issues"]).union(new_issue_refs) + ) + # TODO: breaking change v10, removes resolution footers from descriptions + # return accumulator # Prevent appending duplicate descriptions if text not in accumulator["descriptions"]: @@ -295,6 +304,7 @@ def parse_message(self, message: str) -> ParsedMessageResult | None: { "breaking_descriptions": [], "descriptions": [], + "notices": [], "linked_issues": [], }, ) @@ -315,6 +325,7 @@ def parse_message(self, message: str) -> ParsedMessageResult | None: scope=parsed_scope, descriptions=tuple(body_components["descriptions"]), breaking_descriptions=tuple(body_components["breaking_descriptions"]), + release_notices=tuple(body_components["notices"]), linked_issues=tuple(body_components["linked_issues"]), linked_merge_request=linked_merge_request, ) @@ -455,7 +466,7 @@ def unsquash_commit_message(self, message: str) -> list[str]: [], ) - return separate_commit_msgs + return list(filter(None, separate_commit_msgs)) def _find_squashed_commits_in_str(self, message: str) -> list[str]: separate_commit_msgs: list[str] = [] @@ -490,8 +501,4 @@ def _find_squashed_commits_in_str(self, message: str) -> list[str]: current_msg = clean_paragraph - # Store the last commit message (if its not empty) - if current_msg: - separate_commit_msgs.append(current_msg) - - return separate_commit_msgs + return [*separate_commit_msgs, current_msg] diff --git a/src/semantic_release/commit_parser/emoji.py b/src/semantic_release/commit_parser/emoji.py index fa0ad2304..bb830b395 100644 --- a/src/semantic_release/commit_parser/emoji.py +++ b/src/semantic_release/commit_parser/emoji.py @@ -188,6 +188,8 @@ def __init__(self, options: EmojiParserOptions | None = None) -> None: flags=re.MULTILINE | re.IGNORECASE, ) + self.notice_selector = regexp(r"^NOTICE: (?P.+)$") + self.filters = { "typo-extra-spaces": (regexp(r"(\S) +(\S)"), r"\1 \2"), "git-header-commit": ( @@ -233,7 +235,14 @@ def get_default_options() -> EmojiParserOptions: def commit_body_components_separator( self, accumulator: dict[str, list[str]], text: str ) -> dict[str, list[str]]: - if self.options.parse_linked_issues and ( + if (match := self.notice_selector.match(text)) and ( + notice := match.group("notice") + ): + accumulator["notices"].append(notice) + # TODO: breaking change v10, removes notice footers from descriptions + # return accumulator + + elif self.options.parse_linked_issues and ( match := self.issue_selector.search(text) ): predicate = regexp(r",? and | *[,;/& ] *").sub( @@ -249,11 +258,12 @@ def commit_body_components_separator( predicate.split(","), ) ) - accumulator["linked_issues"] = sort_numerically( - set(accumulator["linked_issues"]).union(new_issue_refs) - ) - # TODO: breaking change v10, removes resolution footers from descriptions - # return accumulator + if new_issue_refs: + accumulator["linked_issues"] = sort_numerically( + set(accumulator["linked_issues"]).union(new_issue_refs) + ) + # TODO: breaking change v10, removes resolution footers from descriptions + # return accumulator # Prevent appending duplicate descriptions if text not in accumulator["descriptions"]: @@ -286,6 +296,7 @@ def parse_message(self, message: str) -> ParsedMessageResult: parse_paragraphs(message), { "descriptions": [], + "notices": [], "linked_issues": [], }, ) @@ -305,6 +316,7 @@ def parse_message(self, message: str) -> ParsedMessageResult: breaking_descriptions=( descriptions[1:] if level_bump is LevelBump.MAJOR else () ), + release_notices=tuple(body_components["notices"]), linked_issues=tuple(body_components["linked_issues"]), linked_merge_request=linked_merge_request, ) @@ -424,7 +436,7 @@ def unsquash_commit_message(self, message: str) -> list[str]: [], ) - return separate_commit_msgs + return list(filter(None, separate_commit_msgs)) def _find_squashed_commits_in_str(self, message: str) -> list[str]: separate_commit_msgs: list[str] = [] @@ -459,8 +471,4 @@ def _find_squashed_commits_in_str(self, message: str) -> list[str]: current_msg = clean_paragraph - # Store the last commit message (if its not empty) - if current_msg: - separate_commit_msgs.append(current_msg) - - return separate_commit_msgs + return [*separate_commit_msgs, current_msg] diff --git a/src/semantic_release/commit_parser/token.py b/src/semantic_release/commit_parser/token.py index dae8d6f59..a9bb254de 100644 --- a/src/semantic_release/commit_parser/token.py +++ b/src/semantic_release/commit_parser/token.py @@ -28,6 +28,7 @@ class ParsedMessageResult(NamedTuple): scope: str descriptions: tuple[str, ...] breaking_descriptions: tuple[str, ...] = () + release_notices: tuple[str, ...] = () linked_issues: tuple[str, ...] = () linked_merge_request: str = "" include_in_changelog: bool = True @@ -75,6 +76,14 @@ class ParsedCommit(NamedTuple): commit: Commit """The original commit object (a class defined by GitPython) that was parsed""" + release_notices: tuple[str, ...] = () + """ + A tuple of release notices, which are additional information about the changes that affect the user. + + An example would be a paragraph which begins with the text ``NOTICE:`` in the commit message but + the parser generally strips the prefix and includes the rest of the paragraph in this list. + """ + linked_issues: tuple[str, ...] = () """ A tuple of issue numbers as strings, if the commit is contains issue references. @@ -147,6 +156,7 @@ def from_parsed_message_result( descriptions=list(parsed_message_result.descriptions), breaking_descriptions=list(parsed_message_result.breaking_descriptions), commit=commit, + release_notices=parsed_message_result.release_notices, linked_issues=parsed_message_result.linked_issues, linked_merge_request=parsed_message_result.linked_merge_request, include_in_changelog=parsed_message_result.include_in_changelog, diff --git a/src/semantic_release/commit_parser/util.py b/src/semantic_release/commit_parser/util.py index e6d768428..9c1322b41 100644 --- a/src/semantic_release/commit_parser/util.py +++ b/src/semantic_release/commit_parser/util.py @@ -45,8 +45,8 @@ class RegexReplaceDef(TypedDict): # Match a git footer line, and add an extra newline after it # only be flexible enough for a double space indent (otherwise its probably on purpose) # - found collision with dependabot's yaml in a commit message with a git footer (its unusal but possible) - "pattern": regexp(r"^ {0,2}([\w-]*: .+)$\n?(?!\n)", MULTILINE), - "repl": r"\1\n\n", + "pattern": regexp(r"^ {0,2}([\w-]*: .+)$\n?((?!\n) *[^:\n]+:)", MULTILINE), + "repl": r"\1\n\n\2", } @@ -65,10 +65,18 @@ def parse_paragraphs(text: str) -> list[str]: """ adjusted_text = reduce( lambda txt, adj: adj["pattern"].sub(adj["repl"], txt), - [trim_line_endings, un_word_wrap_hyphen, spread_out_git_footers], + [trim_line_endings, un_word_wrap_hyphen], text, ) + # Repeat replacements until no more changes are made + prev_iteration = "" + while prev_iteration != adjusted_text: + prev_iteration = adjusted_text + adjusted_text = spread_out_git_footers["pattern"].sub( + spread_out_git_footers["repl"], adjusted_text + ) + return list( filter( None, diff --git a/src/semantic_release/data/templates/angular/md/.components/changes.md.j2 b/src/semantic_release/data/templates/angular/md/.components/changes.md.j2 index 2bade3b74..0cdbf3b6f 100644 --- a/src/semantic_release/data/templates/angular/md/.components/changes.md.j2 +++ b/src/semantic_release/data/templates/angular/md/.components/changes.md.j2 @@ -1,6 +1,8 @@ {% from 'macros.md.j2' import apply_alphabetical_ordering_by_brk_descriptions %}{% from 'macros.md.j2' import apply_alphabetical_ordering_by_descriptions +%}{% from 'macros.md.j2' import apply_alphabetical_ordering_by_release_notices %}{% from 'macros.md.j2' import format_breaking_changes_description, format_commit_summary_line +%}{% from 'macros.md.j2' import format_release_notice %}{# EXAMPLE: @@ -25,6 +27,14 @@ EXAMPLE: - **scope**: this breaking change has a scope to identify the part of the code that this breaking change applies to for better context. +### ADDITIONAL RELEASE INFORMATION + +- This is a release note that provides additional information about the release + that is not a breaking change or a feature/bug fix. + +- **scope**: this release note has a scope to identify the part of the code that + this release note applies to for better context. + #}{% set max_line_width = max_line_width | default(100) %}{% set hanging_indent = hanging_indent | default(2) %}{# @@ -91,4 +101,37 @@ EXAMPLE: "\n%s\n" | format(brking_descriptions | unique | join("\n\n")) }}{# #}{% endif +%}{# + # Determine if there are any commits with release notice information by filtering the list by release_notices + # commit_objects is a list of tuples [("Features", [ParsedCommit(), ...]), ("Bug Fixes", [ParsedCommit(), ...])] + # HOW: Filter out commits that have no release notices + # 1. Re-map the list to only the list of commits from the list of tuples + # 2. Peel off the outer list to get a list of ParsedCommit objects + # 3. Filter the list of ParsedCommits to only those with a release notice +#}{% set notice_commits = commit_objects | map(attribute="1.0") +%}{% set notice_commits = notice_commits | rejectattr("error", "defined") | selectattr("release_notices.0") | list +%}{# +#}{% if notice_commits | length > 0 +%}{# PREPROCESS COMMITS +#}{% set notice_ns = namespace(commits=notice_commits) +%}{{ apply_alphabetical_ordering_by_release_notices(notice_ns) | default("", true) +}}{# +#}{% set release_notices = [] +%}{# +#}{% for commit in notice_ns.commits +%}{% set full_description = "- %s" | format( + format_release_notice(commit).split("\n\n") | join("\n\n- ") + ) +%}{{ release_notices.append( + full_description | autofit_text_width(max_line_width, hanging_indent) + ) | default("", true) +}}{% endfor +%}{# + # # PRINT RELEASE NOTICE INFORMATION (header & descriptions) +#}{{ "\n" +}}{{ "### ADDITIONAL RELEASE INFORMATION\n" +}}{{ + "\n%s\n" | format(release_notices | unique | join("\n\n")) +}}{# +#}{% endif %} diff --git a/src/semantic_release/data/templates/angular/md/.components/macros.md.j2 b/src/semantic_release/data/templates/angular/md/.components/macros.md.j2 index 7513ba728..8bb56ea79 100644 --- a/src/semantic_release/data/templates/angular/md/.components/macros.md.j2 +++ b/src/semantic_release/data/templates/angular/md/.components/macros.md.j2 @@ -108,6 +108,44 @@ %} +{# + MACRO: format the release notice by: + - Capitalizing the description + - Adding an optional scope prefix +#}{% macro format_release_notice(commit) +%}{% set ns = namespace(full_description="") +%}{# +#}{% if commit.error is undefined +%}{% for paragraph in commit.release_notices +%}{% if paragraph | trim | length > 0 +%}{# +#}{% set paragraph_text = [ + paragraph.split(" ", maxsplit=1)[0] | capitalize, + paragraph.split(" ", maxsplit=1)[1] + ] | join(" ") | trim | safe +%}{# +#}{% set ns.full_description = [ + ns.full_description, + paragraph_text + ] | join("\n\n") +%}{# +#}{% endif +%}{% endfor +%}{# +#}{% set ns.full_description = ns.full_description | trim +%}{# +#}{% if commit.scope +%}{% set ns.full_description = "**%s**: %s" | format( + commit.scope, ns.full_description + ) +%}{% endif +%}{% endif +%}{# +#}{{ ns.full_description +}}{% endmacro +%} + + {# MACRO: apply smart ordering of commits objects based on alphabetized summaries and then scopes - Commits are sorted based on the commit type and the commit message @@ -162,3 +200,31 @@ #}{% set ns.commits = ordered_commits %}{% endmacro %} + + +{# + MACRO: apply smart ordering of commits objects based on alphabetized release notices and then scopes + - Commits are sorted based on the commit type and the commit message + - Commits are grouped by the commit type + - parameter: ns (namespace) object with a commits list + - returns None but modifies the ns.commits list in place +#}{% macro apply_alphabetical_ordering_by_release_notices(ns) +%}{% set ordered_commits = [] +%}{# + # # Eliminate any ParseError commits from input set +#}{% set filtered_commits = ns.commits | rejectattr("error", "defined") | list +%}{# + # # grab all commits with no scope and sort alphabetically by the first line of the commit message +#}{% for commit in filtered_commits | rejectattr("scope") | sort(attribute='release_notices.0') +%}{{ ordered_commits.append(commit) | default("", true) +}}{% endfor +%}{# + # # grab all commits with a scope and sort alphabetically by the scope and then the first line of the commit message +#}{% for commit in filtered_commits | selectattr("scope") | sort(attribute='scope,release_notices.0') +%}{{ ordered_commits.append(commit) | default("", true) +}}{% endfor +%}{# + # # Return the ordered commits +#}{% set ns.commits = ordered_commits +%}{% endmacro +%} diff --git a/src/semantic_release/data/templates/angular/md/.release_notes.md.j2 b/src/semantic_release/data/templates/angular/md/.release_notes.md.j2 index 3276ae7a0..a5ad54832 100644 --- a/src/semantic_release/data/templates/angular/md/.release_notes.md.j2 +++ b/src/semantic_release/data/templates/angular/md/.release_notes.md.j2 @@ -2,11 +2,9 @@ ### Features -- Add new feature ([#10](https://domain.com/namespace/repo/pull/10), - [`abcdef0`](https://domain.com/namespace/repo/commit/HASH)) +- Add new feature ([#10](https://domain.com/namespace/repo/pull/10), [`abcdef0`](https://domain.com/namespace/repo/commit/HASH)) -- **scope**: Add new feature - ([`abcdef0`](https://domain.com/namespace/repo/commit/HASH)) +- **scope**: Add new feature ([`abcdef0`](https://domain.com/namespace/repo/commit/HASH)) ### Bug Fixes @@ -14,12 +12,15 @@ ### BREAKING CHANGES -- With the change _____, the change causes ___ effect. Ultimately, this section - it is a more detailed description of the breaking change. With an optional - scope prefix like the commit messages above. +- With the change _____, the change causes ___ effect. Ultimately, this section it is a more detailed description of the breaking change. With an optional scope prefix like the commit messages above. -- **scope**: this breaking change has a scope to identify the part of the code that - this breaking change applies to for better context. +- **scope**: this breaking change has a scope to identify the part of the code that this breaking change applies to for better context. + +### ADDITIONAL RELEASE INFORMATION + +- This is a release note that provides additional information about the release that is not a breaking change or a feature/bug fix. + +- **scope**: this release note has a scope to identify the part of the code that this release note applies to for better context. --- diff --git a/src/semantic_release/data/templates/angular/rst/.components/changes.rst.j2 b/src/semantic_release/data/templates/angular/rst/.components/changes.rst.j2 index 07d6729d3..565446884 100644 --- a/src/semantic_release/data/templates/angular/rst/.components/changes.rst.j2 +++ b/src/semantic_release/data/templates/angular/rst/.components/changes.rst.j2 @@ -1,8 +1,9 @@ {% from 'macros.rst.j2' import apply_alphabetical_ordering_by_brk_descriptions %}{% from 'macros.rst.j2' import apply_alphabetical_ordering_by_descriptions +%}{% from 'macros.rst.j2' import apply_alphabetical_ordering_by_release_notices %}{% from 'macros.rst.j2' import extract_pr_link_reference, format_breaking_changes_description %}{% from 'macros.rst.j2' import format_commit_summary_line, format_link_reference -%}{% from 'macros.rst.j2' import generate_heading_underline +%}{% from 'macros.rst.j2' import format_release_notice, generate_heading_underline %}{# Features @@ -27,10 +28,19 @@ BREAKING CHANGES * **scope**: this breaking change has a scope to identify the part of the code that this breaking change applies to for better context. -.. _10: https://domain.com/namespace/repo/pull/10 +ADDITIONAL RELEASE INFORMATION +------------------------------ + +* This is a release note that provides additional information about the release + that is not a breaking change or a feature/bug fix. + +* **scope**: this release note has a scope to identify the part of the code that + this release note applies to for better context. + +.. _#10: https://domain.com/namespace/repo/pull/10 +.. _#11: https://domain.com/namespace/repo/pull/11 .. _8a7B8ec: https://domain.com/owner/repo/commit/8a7b8ec .. _abcdef0: https://domain.com/owner/repo/commit/abcdef0 -.. _11: https://domain.com/namespace/repo/pull/11 #}{% set max_line_width = max_line_width | default(100) %}{% set hanging_indent = hanging_indent | default(2) @@ -120,6 +130,40 @@ BREAKING CHANGES "\n%s\n" | format(brking_descriptions | unique | join("\n\n")) }}{# #}{% endif +%}{# + # Determine if there are any commits with release notice information by filtering the list by release_notices + # commit_objects is a list of tuples [("Features", [ParsedCommit(), ...]), ("Bug Fixes", [ParsedCommit(), ...])] + # HOW: Filter out commits that have no release notices + # 1. Re-map the list to only the list of commits from the list of tuples + # 2. Peel off the outer list to get a list of ParsedCommit objects + # 3. Filter the list of ParsedCommits to only those with a release notice +#}{% set notice_commits = commit_objects | map(attribute="1.0") +%}{% set notice_commits = notice_commits | rejectattr("error", "defined") | selectattr("release_notices.0") | list +%}{# +#}{% if notice_commits | length > 0 +%}{# PREPROCESS COMMITS +#}{% set notice_ns = namespace(commits=notice_commits) +%}{{ apply_alphabetical_ordering_by_release_notices(notice_ns) | default("", true) +}}{# +#}{% set release_notices = [] +%}{# +#}{% for commit in notice_ns.commits +%}{% set full_description = "* %s" | format( + format_release_notice(commit).split("\n\n") | join("\n\n* ") + ) +%}{{ release_notices.append( + full_description | convert_md_to_rst | autofit_text_width(max_line_width, hanging_indent) + ) | default("", true) +}}{% endfor +%}{# + # # PRINT RELEASE NOTICE INFORMATION (header & descriptions) +#}{{ "\n" +}}{{ "ADDITIONAL RELEASE INFORMATION\n" +}}{{ "------------------------------\n" +}}{{ + "\n%s\n" | format(release_notices | unique | join("\n\n")) +}}{# +#}{% endif %}{# # # # PRINT POST PARAGRAPH LINKS diff --git a/src/semantic_release/data/templates/angular/rst/.components/macros.rst.j2 b/src/semantic_release/data/templates/angular/rst/.components/macros.rst.j2 index 4869ff799..f70043036 100644 --- a/src/semantic_release/data/templates/angular/rst/.components/macros.rst.j2 +++ b/src/semantic_release/data/templates/angular/rst/.components/macros.rst.j2 @@ -135,6 +135,44 @@ %} +{# + MACRO: format the release notice by: + - Capitalizing the description + - Adding an optional scope prefix +#}{% macro format_release_notice(commit) +%}{% set ns = namespace(full_description="") +%}{# +#}{% if commit.error is undefined +%}{% for paragraph in commit.release_notices +%}{% if paragraph | trim | length > 0 +%}{# +#}{% set paragraph_text = [ + paragraph.split(" ", maxsplit=1)[0] | capitalize, + paragraph.split(" ", maxsplit=1)[1] + ] | join(" ") | trim | safe +%}{# +#}{% set ns.full_description = [ + ns.full_description, + paragraph_text + ] | join("\n\n") +%}{# +#}{% endif +%}{% endfor +%}{# +#}{% set ns.full_description = ns.full_description | trim +%}{# +#}{% if commit.scope +%}{% set ns.full_description = "**%s**: %s" | format( + commit.scope, ns.full_description + ) +%}{% endif +%}{% endif +%}{# +#}{{ ns.full_description +}}{% endmacro +%} + + {# MACRO: apply smart ordering of commits objects based on alphabetized summaries and then scopes - Commits are sorted based on the commit type and the commit message @@ -189,3 +227,31 @@ #}{% set ns.commits = ordered_commits %}{% endmacro %} + + +{# + MACRO: apply smart ordering of commits objects based on alphabetized release notices and then scopes + - Commits are sorted based on the commit type and the commit message + - Commits are grouped by the commit type + - parameter: ns (namespace) object with a commits list + - returns None but modifies the ns.commits list in place +#}{% macro apply_alphabetical_ordering_by_release_notices(ns) +%}{% set ordered_commits = [] +%}{# + # # Eliminate any ParseError commits from input set +#}{% set filtered_commits = ns.commits | rejectattr("error", "defined") | list +%}{# + # # grab all commits with no scope and sort alphabetically by the first line of the commit message +#}{% for commit in filtered_commits | rejectattr("scope") | sort(attribute='release_notices.0') +%}{{ ordered_commits.append(commit) | default("", true) +}}{% endfor +%}{# + # # grab all commits with a scope and sort alphabetically by the scope and then the first line of the commit message +#}{% for commit in filtered_commits | selectattr("scope") | sort(attribute='scope,release_notices.0') +%}{{ ordered_commits.append(commit) | default("", true) +}}{% endfor +%}{# + # # Return the ordered commits +#}{% set ns.commits = ordered_commits +%}{% endmacro +%} diff --git a/tests/unit/semantic_release/changelog/conftest.py b/tests/unit/semantic_release/changelog/conftest.py index 18cacaa61..a5e09813e 100644 --- a/tests/unit/semantic_release/changelog/conftest.py +++ b/tests/unit/semantic_release/changelog/conftest.py @@ -1,6 +1,7 @@ from __future__ import annotations from datetime import timedelta +from textwrap import dedent from typing import TYPE_CHECKING import pytest @@ -246,3 +247,180 @@ def single_release_history( version: artificial_release_history.released[version], }, ) + + +@pytest.fixture +def release_history_w_a_notice( + artificial_release_history: ReleaseHistory, + stable_now_date: GetStableDateNowFn, +) -> ReleaseHistory: + current_datetime = stable_now_date() + latest_version = next(iter(artificial_release_history.released.keys())) + next_version = latest_version.bump(LevelBump.PATCH) + notice_commit_subject = "deprecate a type" + notice_commit_type = "refactor" + notice_commit_scope = "cli" + release_notice = dedent( + """\ + This is a multline release notice that is made + up of two lines. + """ + ) + + notice_commit = Commit( + Repo("."), + Object.NULL_BIN_SHA, + message=str.join( + "\n\n", + [ + f"{notice_commit_type}({notice_commit_scope}): {notice_commit_subject}", + f"NOTICE: {release_notice}", + ], + ), + ) + + notice_commit_parsed = ParsedCommit( + bump=LevelBump.NO_RELEASE, + type=notice_commit_type, + scope=notice_commit_scope, + descriptions=[notice_commit_subject], + breaking_descriptions=[], + release_notices=(release_notice.replace("\n", " ").strip(),), + commit=notice_commit, + ) + + return ReleaseHistory( + unreleased={}, + released={ + next_version: Release( + tagger=artificial_release_history.released[latest_version]["tagger"], + committer=artificial_release_history.released[latest_version][ + "committer" + ], + tagged_date=current_datetime, + elements={"Refactoring": [notice_commit_parsed]}, + version=next_version, + ), + **artificial_release_history.released, + }, + ) + + +@pytest.fixture +def release_history_w_notice_n_brk_change( + artificial_release_history: ReleaseHistory, + release_history_w_a_notice: ReleaseHistory, + stable_now_date: GetStableDateNowFn, +) -> ReleaseHistory: + current_datetime = stable_now_date() + latest_version = next(iter(artificial_release_history.released.keys())) + next_version = latest_version.bump(LevelBump.MAJOR) + brk_commit_subject = "fix a problem" + brk_commit_type = "fix" + brk_commit_scope = "cli" + brk_change_msg = "this is a breaking change" + + brk_commit = Commit( + Repo("."), + Object.NULL_BIN_SHA, + message=str.join( + "\n\n", + [ + f"{brk_commit_type}({brk_commit_scope}): {brk_commit_subject}", + f"BREAKING CHANGE: {brk_change_msg}", + ], + ), + ) + + brk_commit_parsed = ParsedCommit( + bump=LevelBump.MAJOR, + type=brk_commit_type, + scope=brk_commit_scope, + descriptions=[brk_commit_subject], + breaking_descriptions=[brk_change_msg], + commit=brk_commit, + ) + + last_notice_release = next(iter(release_history_w_a_notice.released.keys())) + + return ReleaseHistory( + unreleased={}, + released={ + next_version: Release( + tagger=artificial_release_history.released[latest_version]["tagger"], + committer=artificial_release_history.released[latest_version][ + "committer" + ], + tagged_date=current_datetime, + elements={ + "Bug Fixes": [brk_commit_parsed], + **release_history_w_a_notice.released[last_notice_release][ + "elements" + ], + }, + version=next_version, + ), + **artificial_release_history.released, + }, + ) + + +@pytest.fixture +def release_history_w_multiple_notices( + release_history_w_a_notice: ReleaseHistory, + stable_now_date: GetStableDateNowFn, +) -> ReleaseHistory: + current_datetime = stable_now_date() + latest_version = next(iter(release_history_w_a_notice.released.keys())) + + notice_commit_subject = "add a configurable feature" + notice_commit_type = "feat" + notice_commit_scope = "cli-config" + release_notice = dedent( + """\ + This is a multline release notice that is its own + paragraph to detail the configurable feature. + """ + ) + + notice_commit = Commit( + Repo("."), + Object.NULL_BIN_SHA, + message=str.join( + "\n\n", + [ + f"{notice_commit_type}({notice_commit_scope}): {notice_commit_subject}", + f"NOTICE: {release_notice}", + ], + ), + ) + + notice_commit_parsed = ParsedCommit( + bump=LevelBump.MINOR, + type=notice_commit_type, + scope=notice_commit_scope, + descriptions=[notice_commit_subject], + breaking_descriptions=[], + release_notices=(release_notice.replace("\n", " ").strip(),), + commit=notice_commit, + ) + + return ReleaseHistory( + unreleased={}, + released={ + **release_history_w_a_notice.released, + # Replaces and inserts a new commit of different type with breaking changes + latest_version: Release( + tagger=release_history_w_a_notice.released[latest_version]["tagger"], + committer=release_history_w_a_notice.released[latest_version][ + "committer" + ], + tagged_date=current_datetime, + elements={ + "Features": [notice_commit_parsed], + **release_history_w_a_notice.released[latest_version]["elements"], + }, + version=latest_version, + ), + }, + ) diff --git a/tests/unit/semantic_release/changelog/test_default_changelog.py b/tests/unit/semantic_release/changelog/test_default_changelog.py index de30e7e15..e3c01b7d7 100644 --- a/tests/unit/semantic_release/changelog/test_default_changelog.py +++ b/tests/unit/semantic_release/changelog/test_default_changelog.py @@ -576,3 +576,450 @@ def test_default_changelog_template_w_unreleased_changes( ) assert expected_changelog == actual_changelog + + +@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea, Bitbucket]) +def test_default_changelog_template_w_a_notice( + hvcs_client: type[Github | Gitlab | Gitea | Bitbucket], + example_git_https_url: str, + release_history_w_a_notice: ReleaseHistory, + changelog_md_file: Path, + today_date_str: str, +): + hvcs = hvcs_client(example_git_https_url) + released_versions = iter(release_history_w_a_notice.released.keys()) + + latest_version = next(released_versions) + prev_version_1 = next(released_versions) + prev_version_2 = next(released_versions) + + latest_release = release_history_w_a_notice.released[latest_version] + prev_release_1 = release_history_w_a_notice.released[prev_version_1] + + notice_commit_obj = next(iter(latest_release["elements"].values()))[0] + feat_commit_obj = prev_release_1["elements"]["feature"][0] + fix_commit_obj_1 = prev_release_1["elements"]["fix"][0] + fix_commit_obj_2 = prev_release_1["elements"]["fix"][1] + fix_commit_obj_3 = prev_release_1["elements"]["fix"][2] + + assert isinstance(notice_commit_obj, ParsedCommit) + assert isinstance(feat_commit_obj, ParsedCommit) + assert isinstance(fix_commit_obj_1, ParsedCommit) + assert isinstance(fix_commit_obj_2, ParsedCommit) + assert isinstance(fix_commit_obj_3, ParsedCommit) + + notice_commit_url = hvcs.commit_hash_url(notice_commit_obj.commit.hexsha) + notice_commit_description = str.join("\n", notice_commit_obj.descriptions) + notice_description = str.join("\n", notice_commit_obj.release_notices) + + feat_commit_url = hvcs.commit_hash_url(feat_commit_obj.commit.hexsha) + feat_description = str.join("\n", feat_commit_obj.descriptions) + + fix_commit_1_url = hvcs.commit_hash_url(fix_commit_obj_1.commit.hexsha) + fix_commit_1_description = str.join("\n", fix_commit_obj_1.descriptions) + + fix_commit_2_url = hvcs.commit_hash_url(fix_commit_obj_2.commit.hexsha) + fix_commit_2_description = str.join("\n", fix_commit_obj_2.descriptions) + + fix_commit_3_url = hvcs.commit_hash_url(fix_commit_obj_3.commit.hexsha) + fix_commit_3_description = str.join("\n", fix_commit_obj_3.descriptions) + + expected_changelog = str.join( + "\n", + [ + "# CHANGELOG", + "", + "", + f"## v{latest_version} ({today_date_str})", + "", + "### Refactoring", + "", + # Due to the 100 character limit, hash url will be on the second line + f"- **{notice_commit_obj.scope}**: {notice_commit_description.capitalize().rstrip()}", + f" ([`{notice_commit_obj.commit.hexsha[:7]}`]({notice_commit_url}))", + "", + "### ADDITIONAL RELEASE INFORMATION", + "", + "- {commit_scope}{change_desc}".format( + commit_scope=( + f"**{notice_commit_obj.scope}**: " + if notice_commit_obj.scope + else "" + ), + change_desc=notice_description.capitalize().rstrip(), + ), + "", + "", + f"## v{prev_version_1} ({today_date_str})", + "", + "### Feature", + "", + # Due to the 100 character limit, hash url will be on the second line + f"- **{feat_commit_obj.scope}**: {feat_description.capitalize()}", + f" ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", + "", + "### Fix", + "", + # Commit 2 is first because it has no scope + # Due to the 100 character limit, hash url will be on the second line + f"- {fix_commit_2_description.capitalize()}", + f" ([`{fix_commit_obj_2.commit.hexsha[:7]}`]({fix_commit_2_url}))", + "", + # Commit 3 is second because it starts with an A even though it has the same scope as 1 + # Due to the 100 character limit, hash url will be on the second line + f"- **{fix_commit_obj_3.scope}**: {fix_commit_3_description.capitalize()}", + f" ([`{fix_commit_obj_3.commit.hexsha[:7]}`]({fix_commit_3_url}))", + "", + # Due to the 100 character limit, hash url will be on the second line + f"- **{fix_commit_obj_1.scope}**: {fix_commit_1_description.capitalize()}", + f" ([`{fix_commit_obj_1.commit.hexsha[:7]}`]({fix_commit_1_url}))", + "", + "", + f"## v{prev_version_2} ({today_date_str})", + "", + "### Feature", + "", + # Due to the 100 character limit, hash url will be on the second line + f"- **{feat_commit_obj.scope}**: {feat_description.capitalize()}", + f" ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", + ], + ) + + actual_changelog = render_default_changelog_file( + output_format=ChangelogOutputFormat.MARKDOWN, + changelog_context=make_changelog_context( + hvcs_client=hvcs, + release_history=release_history_w_a_notice, + mode=ChangelogMode.INIT, + prev_changelog_file=changelog_md_file, + insertion_flag="", + mask_initial_release=False, + ), + changelog_style="angular", + ) + + assert expected_changelog == actual_changelog + + +@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea, Bitbucket]) +def test_default_changelog_template_w_a_notice_n_brk_change( + hvcs_client: type[Github | Gitlab | Gitea | Bitbucket], + example_git_https_url: str, + release_history_w_notice_n_brk_change: ReleaseHistory, + changelog_md_file: Path, + today_date_str: str, +): + hvcs = hvcs_client(example_git_https_url) + released_versions = iter(release_history_w_notice_n_brk_change.released.keys()) + + latest_version = next(released_versions) + prev_version_1 = next(released_versions) + prev_version_2 = next(released_versions) + + latest_release = release_history_w_notice_n_brk_change.released[latest_version] + prev_release_1 = release_history_w_notice_n_brk_change.released[prev_version_1] + + brk_fix_commit_obj = latest_release["elements"]["Bug Fixes"][0] + notice_commit_obj = latest_release["elements"]["Refactoring"][0] + feat_commit_obj = prev_release_1["elements"]["feature"][0] + fix_commit_obj_1 = prev_release_1["elements"]["fix"][0] + fix_commit_obj_2 = prev_release_1["elements"]["fix"][1] + fix_commit_obj_3 = prev_release_1["elements"]["fix"][2] + + assert isinstance(brk_fix_commit_obj, ParsedCommit) + assert isinstance(notice_commit_obj, ParsedCommit) + assert isinstance(feat_commit_obj, ParsedCommit) + assert isinstance(fix_commit_obj_1, ParsedCommit) + assert isinstance(fix_commit_obj_2, ParsedCommit) + assert isinstance(fix_commit_obj_3, ParsedCommit) + + brk_fix_commit_url = hvcs.commit_hash_url(brk_fix_commit_obj.commit.hexsha) + brk_fix_description = str.join("\n", brk_fix_commit_obj.descriptions) + brk_fix_brking_description = str.join( + "\n", brk_fix_commit_obj.breaking_descriptions + ) + + notice_commit_url = hvcs.commit_hash_url(notice_commit_obj.commit.hexsha) + notice_commit_description = str.join("\n", notice_commit_obj.descriptions) + notice_description = str.join("\n", notice_commit_obj.release_notices) + + feat_commit_url = hvcs.commit_hash_url(feat_commit_obj.commit.hexsha) + feat_description = str.join("\n", feat_commit_obj.descriptions) + + fix_commit_1_url = hvcs.commit_hash_url(fix_commit_obj_1.commit.hexsha) + fix_commit_1_description = str.join("\n", fix_commit_obj_1.descriptions) + + fix_commit_2_url = hvcs.commit_hash_url(fix_commit_obj_2.commit.hexsha) + fix_commit_2_description = str.join("\n", fix_commit_obj_2.descriptions) + + fix_commit_3_url = hvcs.commit_hash_url(fix_commit_obj_3.commit.hexsha) + fix_commit_3_description = str.join("\n", fix_commit_obj_3.descriptions) + + expected_changelog = str.join( + "\n", + [ + "# CHANGELOG", + "", + "", + f"## v{latest_version} ({today_date_str})", + "", + "### Bug Fixes", + "", + "- {commit_scope}{commit_desc}".format( + commit_scope=( + f"**{brk_fix_commit_obj.scope}**: " + if brk_fix_commit_obj.scope + else "" + ), + commit_desc=brk_fix_description.capitalize().rstrip(), + ), + f" ([`{brk_fix_commit_obj.commit.hexsha[:7]}`]({brk_fix_commit_url}))", + "", + "### Refactoring", + "", + "- {commit_scope}{commit_desc}".format( + commit_scope=( + f"**{notice_commit_obj.scope}**: " + if notice_commit_obj.scope + else "" + ), + commit_desc=notice_commit_description.capitalize().rstrip(), + ), + f" ([`{notice_commit_obj.commit.hexsha[:7]}`]({notice_commit_url}))", + "", + "### BREAKING CHANGES", + "", + "- {commit_scope}{change_desc}".format( + commit_scope=( + f"**{brk_fix_commit_obj.scope}**: " + if brk_fix_commit_obj.scope + else "" + ), + change_desc=brk_fix_brking_description.capitalize().rstrip(), + ), + "", + "### ADDITIONAL RELEASE INFORMATION", + "", + "- {commit_scope}{change_desc}".format( + commit_scope=( + f"**{notice_commit_obj.scope}**: " + if notice_commit_obj.scope + else "" + ), + change_desc=notice_description.capitalize().rstrip(), + ), + "", + "", + f"## v{prev_version_1} ({today_date_str})", + "", + "### Feature", + "", + # Due to the 100 character limit, hash url will be on the second line + f"- **{feat_commit_obj.scope}**: {feat_description.capitalize()}", + f" ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", + "", + "### Fix", + "", + # Commit 2 is first because it has no scope + # Due to the 100 character limit, hash url will be on the second line + f"- {fix_commit_2_description.capitalize()}", + f" ([`{fix_commit_obj_2.commit.hexsha[:7]}`]({fix_commit_2_url}))", + "", + # Commit 3 is second because it starts with an A even though it has the same scope as 1 + # Due to the 100 character limit, hash url will be on the second line + f"- **{fix_commit_obj_3.scope}**: {fix_commit_3_description.capitalize()}", + f" ([`{fix_commit_obj_3.commit.hexsha[:7]}`]({fix_commit_3_url}))", + "", + # Due to the 100 character limit, hash url will be on the second line + f"- **{fix_commit_obj_1.scope}**: {fix_commit_1_description.capitalize()}", + f" ([`{fix_commit_obj_1.commit.hexsha[:7]}`]({fix_commit_1_url}))", + "", + "", + f"## v{prev_version_2} ({today_date_str})", + "", + "### Feature", + "", + # Due to the 100 character limit, hash url will be on the second line + f"- **{feat_commit_obj.scope}**: {feat_description.capitalize()}", + f" ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", + ], + ) + + actual_changelog = render_default_changelog_file( + output_format=ChangelogOutputFormat.MARKDOWN, + changelog_context=make_changelog_context( + hvcs_client=hvcs, + release_history=release_history_w_notice_n_brk_change, + mode=ChangelogMode.INIT, + prev_changelog_file=changelog_md_file, + insertion_flag="", + mask_initial_release=False, + ), + changelog_style="angular", + ) + + assert expected_changelog == actual_changelog + + +@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea, Bitbucket]) +def test_default_changelog_template_w_multiple_notices( + hvcs_client: type[Github | Gitlab | Gitea | Bitbucket], + example_git_https_url: str, + release_history_w_multiple_notices: ReleaseHistory, + changelog_md_file: Path, + today_date_str: str, +): + hvcs = hvcs_client(example_git_https_url) + released_versions = iter(release_history_w_multiple_notices.released.keys()) + + latest_version = next(released_versions) + prev_version_1 = next(released_versions) + prev_version_2 = next(released_versions) + + latest_release = release_history_w_multiple_notices.released[latest_version] + prev_release_1 = release_history_w_multiple_notices.released[prev_version_1] + + feat_notice_commit_obj = latest_release["elements"]["Features"][0] + refactor_notice_commit_obj = latest_release["elements"]["Refactoring"][0] + feat_commit_obj = prev_release_1["elements"]["feature"][0] + fix_commit_obj_1 = prev_release_1["elements"]["fix"][0] + fix_commit_obj_2 = prev_release_1["elements"]["fix"][1] + fix_commit_obj_3 = prev_release_1["elements"]["fix"][2] + + assert isinstance(feat_notice_commit_obj, ParsedCommit) + assert isinstance(refactor_notice_commit_obj, ParsedCommit) + assert isinstance(feat_commit_obj, ParsedCommit) + assert isinstance(fix_commit_obj_1, ParsedCommit) + assert isinstance(fix_commit_obj_2, ParsedCommit) + assert isinstance(fix_commit_obj_3, ParsedCommit) + + refactor_commit_url = hvcs.commit_hash_url(refactor_notice_commit_obj.commit.hexsha) + refactor_commit_desc = str.join("\n", refactor_notice_commit_obj.descriptions) + refactor_commit_notice_desc = str.join( + "\n", refactor_notice_commit_obj.release_notices + ) + + feat_notice_commit_url = hvcs.commit_hash_url(feat_notice_commit_obj.commit.hexsha) + feat_notice_description = str.join("\n", feat_notice_commit_obj.descriptions) + feat_commit_notice_desc = str.join("\n", feat_notice_commit_obj.release_notices) + + feat_commit_url = hvcs.commit_hash_url(feat_commit_obj.commit.hexsha) + feat_description = str.join("\n", feat_commit_obj.descriptions) + + fix_commit_1_url = hvcs.commit_hash_url(fix_commit_obj_1.commit.hexsha) + fix_commit_1_description = str.join("\n", fix_commit_obj_1.descriptions) + + fix_commit_2_url = hvcs.commit_hash_url(fix_commit_obj_2.commit.hexsha) + fix_commit_2_description = str.join("\n", fix_commit_obj_2.descriptions) + + fix_commit_3_url = hvcs.commit_hash_url(fix_commit_obj_3.commit.hexsha) + fix_commit_3_description = str.join("\n", fix_commit_obj_3.descriptions) + + expected_changelog = str.join( + "\n", + [ + "# CHANGELOG", + "", + "", + f"## v{latest_version} ({today_date_str})", + "", + "### Features", + "", + "- {commit_scope}{commit_desc}".format( + commit_scope=( + f"**{feat_notice_commit_obj.scope}**: " + if feat_notice_commit_obj.scope + else "" + ), + commit_desc=feat_notice_description.capitalize().rstrip(), + ), + f" ([`{feat_notice_commit_obj.commit.hexsha[:7]}`]({feat_notice_commit_url}))", + "", + "### Refactoring", + "", + "- {commit_scope}{commit_desc}".format( + commit_scope=( + f"**{refactor_notice_commit_obj.scope}**: " + if refactor_notice_commit_obj.scope + else "" + ), + commit_desc=refactor_commit_desc.capitalize().rstrip(), + ), + f" ([`{refactor_notice_commit_obj.commit.hexsha[:7]}`]({refactor_commit_url}))", + "", + "### ADDITIONAL RELEASE INFORMATION", + "", + "- {commit_scope}{change_desc}".format( + commit_scope=( + f"**{refactor_notice_commit_obj.scope}**: " + if refactor_notice_commit_obj.scope + else "" + ), + change_desc=refactor_commit_notice_desc.capitalize().rstrip(), + ), + "", + "- {commit_scope}{change_desc}".format( + commit_scope=( + f"**{feat_notice_commit_obj.scope}**: " + if feat_notice_commit_obj.scope + else "" + ), + change_desc=str.join( + "\n", + [ + feat_commit_notice_desc.capitalize()[:73].rstrip(), + " " + feat_commit_notice_desc[73:].strip(), + ], + ), + ), + "", + "", + f"## v{prev_version_1} ({today_date_str})", + "", + "### Feature", + "", + # Due to the 100 character limit, hash url will be on the second line + f"- **{feat_commit_obj.scope}**: {feat_description.capitalize()}", + f" ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", + "", + "### Fix", + "", + # Commit 2 is first because it has no scope + # Due to the 100 character limit, hash url will be on the second line + f"- {fix_commit_2_description.capitalize()}", + f" ([`{fix_commit_obj_2.commit.hexsha[:7]}`]({fix_commit_2_url}))", + "", + # Commit 3 is second because it starts with an A even though it has the same scope as 1 + # Due to the 100 character limit, hash url will be on the second line + f"- **{fix_commit_obj_3.scope}**: {fix_commit_3_description.capitalize()}", + f" ([`{fix_commit_obj_3.commit.hexsha[:7]}`]({fix_commit_3_url}))", + "", + # Due to the 100 character limit, hash url will be on the second line + f"- **{fix_commit_obj_1.scope}**: {fix_commit_1_description.capitalize()}", + f" ([`{fix_commit_obj_1.commit.hexsha[:7]}`]({fix_commit_1_url}))", + "", + "", + f"## v{prev_version_2} ({today_date_str})", + "", + "### Feature", + "", + # Due to the 100 character limit, hash url will be on the second line + f"- **{feat_commit_obj.scope}**: {feat_description.capitalize()}", + f" ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", + ], + ) + + actual_changelog = render_default_changelog_file( + output_format=ChangelogOutputFormat.MARKDOWN, + changelog_context=make_changelog_context( + hvcs_client=hvcs, + release_history=release_history_w_multiple_notices, + mode=ChangelogMode.INIT, + prev_changelog_file=changelog_md_file, + insertion_flag="", + mask_initial_release=False, + ), + changelog_style="angular", + ) + + assert expected_changelog == actual_changelog diff --git a/tests/unit/semantic_release/changelog/test_release_notes.py b/tests/unit/semantic_release/changelog/test_release_notes.py index ec51bffcd..62c909ba8 100644 --- a/tests/unit/semantic_release/changelog/test_release_notes.py +++ b/tests/unit/semantic_release/changelog/test_release_notes.py @@ -690,3 +690,326 @@ def test_release_notes_context_format_w_official_name_filter( ) assert expected_content == actual_content + + +@pytest.mark.parametrize("mask_initial_release", [True, False]) +@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea, Bitbucket]) +def test_default_release_notes_template_w_a_notice( + example_git_https_url: str, + hvcs_client: type[Github | Gitlab | Gitea | Bitbucket], + release_history_w_a_notice: ReleaseHistory, + mask_initial_release: bool, + today_date_str: str, +): + """ + Unit test goal: just make sure it renders the release notes template without error. + + Scenarios are better suited for all the variations (commit types). + """ + released_versions = iter(release_history_w_a_notice.released.keys()) + version = next(released_versions) + prev_version = next(released_versions) + hvcs = hvcs_client(example_git_https_url) + release = release_history_w_a_notice.released[version] + + notice_commit_obj = next(iter(release["elements"].values()))[0] + assert isinstance(notice_commit_obj, ParsedCommit) + + notice_commit_url = hvcs.commit_hash_url(notice_commit_obj.commit.hexsha) + notice_commit_description = str.join("\n", notice_commit_obj.descriptions) + notice_description = str.join("\n", notice_commit_obj.release_notices) + + expected_content = str.join( + os.linesep, + [ + f"## v{version} ({today_date_str})", + "", + "### Refactoring", + "", + "- {commit_scope}{commit_desc} ([`{short_hash}`]({url}))".format( + commit_scope=( + f"**{notice_commit_obj.scope}**: " + if notice_commit_obj.scope + else "" + ), + commit_desc=notice_commit_description.capitalize().rstrip(), + short_hash=notice_commit_obj.commit.hexsha[:7], + url=notice_commit_url, + ), + "", + "### ADDITIONAL RELEASE INFORMATION", + "", + "- {commit_scope}{change_desc}".format( + commit_scope=( + f"**{notice_commit_obj.scope}**: " + if notice_commit_obj.scope + else "" + ), + change_desc=notice_description.capitalize().rstrip(), + ), + "", + ], + ) + + if not isinstance(hvcs, Gitea): + expected_content += str.join( + os.linesep, + [ + "", + "---", + "", + "**Detailed Changes**: [{prev_version}...{new_version}]({version_compare_url})".format( + prev_version=prev_version.as_tag(), + new_version=version.as_tag(), + version_compare_url=hvcs.compare_url( + prev_version.as_tag(), version.as_tag() + ), + ), + "", + ], + ) + + actual_content = generate_release_notes( + hvcs_client=hvcs_client(remote_url=example_git_https_url), + release=release, + template_dir=Path(""), + history=release_history_w_a_notice, + style="angular", + mask_initial_release=mask_initial_release, + ) + + assert expected_content == actual_content + + +@pytest.mark.parametrize("mask_initial_release", [True, False]) +@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea, Bitbucket]) +def test_default_release_notes_template_w_a_notice_n_brk_change( + example_git_https_url: str, + hvcs_client: type[Github | Gitlab | Gitea | Bitbucket], + release_history_w_notice_n_brk_change: ReleaseHistory, + mask_initial_release: bool, + today_date_str: str, +): + """ + Unit test goal: just make sure it renders the release notes template without error. + + Scenarios are better suited for all the variations (commit types). + """ + released_versions = iter(release_history_w_notice_n_brk_change.released.keys()) + version = next(released_versions) + prev_version = next(released_versions) + hvcs = hvcs_client(example_git_https_url) + release = release_history_w_notice_n_brk_change.released[version] + + brk_fix_commit_obj = release["elements"]["Bug Fixes"][0] + notice_commit_obj = release["elements"]["Refactoring"][0] + assert isinstance(brk_fix_commit_obj, ParsedCommit) + assert isinstance(notice_commit_obj, ParsedCommit) + + brk_fix_commit_url = hvcs.commit_hash_url(brk_fix_commit_obj.commit.hexsha) + brk_fix_description = str.join("\n", brk_fix_commit_obj.descriptions) + brk_fix_brking_description = str.join( + "\n", brk_fix_commit_obj.breaking_descriptions + ) + + notice_commit_url = hvcs.commit_hash_url(notice_commit_obj.commit.hexsha) + notice_commit_description = str.join("\n", notice_commit_obj.descriptions) + notice_description = str.join("\n", notice_commit_obj.release_notices) + + expected_content = str.join( + os.linesep, + [ + f"## v{version} ({today_date_str})", + "", + "### Bug Fixes", + "", + "- {commit_scope}{commit_desc} ([`{short_hash}`]({url}))".format( + commit_scope=( + f"**{brk_fix_commit_obj.scope}**: " + if brk_fix_commit_obj.scope + else "" + ), + commit_desc=brk_fix_description.capitalize().rstrip(), + short_hash=brk_fix_commit_obj.commit.hexsha[:7], + url=brk_fix_commit_url, + ), + "", + "### Refactoring", + "", + "- {commit_scope}{commit_desc} ([`{short_hash}`]({url}))".format( + commit_scope=( + f"**{notice_commit_obj.scope}**: " + if notice_commit_obj.scope + else "" + ), + commit_desc=notice_commit_description.capitalize().rstrip(), + short_hash=notice_commit_obj.commit.hexsha[:7], + url=notice_commit_url, + ), + "", + "### BREAKING CHANGES", + "", + "- {commit_scope}{change_desc}".format( + commit_scope=( + f"**{brk_fix_commit_obj.scope}**: " + if brk_fix_commit_obj.scope + else "" + ), + change_desc=brk_fix_brking_description.capitalize().rstrip(), + ), + "", + "### ADDITIONAL RELEASE INFORMATION", + "", + "- {commit_scope}{change_desc}".format( + commit_scope=( + f"**{notice_commit_obj.scope}**: " + if notice_commit_obj.scope + else "" + ), + change_desc=notice_description.capitalize().rstrip(), + ), + "", + ], + ) + + if not isinstance(hvcs, Gitea): + expected_content += str.join( + os.linesep, + [ + "", + "---", + "", + "**Detailed Changes**: [{prev_version}...{new_version}]({version_compare_url})".format( + prev_version=prev_version.as_tag(), + new_version=version.as_tag(), + version_compare_url=hvcs.compare_url( + prev_version.as_tag(), version.as_tag() + ), + ), + "", + ], + ) + + actual_content = generate_release_notes( + hvcs_client=hvcs_client(remote_url=example_git_https_url), + release=release, + template_dir=Path(""), + history=release_history_w_notice_n_brk_change, + style="angular", + mask_initial_release=mask_initial_release, + ) + + assert expected_content == actual_content + + +@pytest.mark.parametrize("mask_initial_release", [True, False]) +@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea, Bitbucket]) +def test_default_release_notes_template_w_multiple_notices( + example_git_https_url: str, + hvcs_client: type[Github | Gitlab | Gitea | Bitbucket], + release_history_w_multiple_notices: ReleaseHistory, + mask_initial_release: bool, + today_date_str: str, +): + """ + Unit test goal: just make sure it renders the release notes template without error. + + Scenarios are better suited for all the variations (commit types). + """ + released_versions = iter(release_history_w_multiple_notices.released.keys()) + version = next(released_versions) + prev_version = next(released_versions) + hvcs = hvcs_client(example_git_https_url) + release = release_history_w_multiple_notices.released[version] + + feat_commit_obj = release["elements"]["Features"][0] + refactor_commit_obj = release["elements"]["Refactoring"][0] + assert isinstance(refactor_commit_obj, ParsedCommit) + assert isinstance(feat_commit_obj, ParsedCommit) + + refactor_commit_url = hvcs.commit_hash_url(refactor_commit_obj.commit.hexsha) + refactor_commit_desc = str.join("\n", refactor_commit_obj.descriptions) + refactor_commit_notice_desc = str.join("\n", refactor_commit_obj.release_notices) + + feat_commit_url = hvcs.commit_hash_url(feat_commit_obj.commit.hexsha) + feat_description = str.join("\n", feat_commit_obj.descriptions) + feat_commit_notice_desc = str.join("\n", feat_commit_obj.release_notices) + + expected_content = str.join( + os.linesep, + [ + f"## v{version} ({today_date_str})", + "", + "### Features", + "", + "- {commit_scope}{commit_desc} ([`{short_hash}`]({url}))".format( + commit_scope=( + f"**{feat_commit_obj.scope}**: " if feat_commit_obj.scope else "" + ), + commit_desc=feat_description.capitalize().rstrip(), + short_hash=feat_commit_obj.commit.hexsha[:7], + url=feat_commit_url, + ), + "", + "### Refactoring", + "", + "- {commit_scope}{commit_desc} ([`{short_hash}`]({url}))".format( + commit_scope=( + f"**{refactor_commit_obj.scope}**: " + if refactor_commit_obj.scope + else "" + ), + commit_desc=refactor_commit_desc.capitalize().rstrip(), + short_hash=refactor_commit_obj.commit.hexsha[:7], + url=refactor_commit_url, + ), + "", + "### ADDITIONAL RELEASE INFORMATION", + "", + "- {commit_scope}{change_desc}".format( + commit_scope=( + f"**{refactor_commit_obj.scope}**: " + if refactor_commit_obj.scope + else "" + ), + change_desc=refactor_commit_notice_desc.capitalize().rstrip(), + ), + "", + "- {commit_scope}{change_desc}".format( + commit_scope=( + f"**{feat_commit_obj.scope}**: " if feat_commit_obj.scope else "" + ), + change_desc=feat_commit_notice_desc.capitalize().rstrip(), + ), + "", + ], + ) + + if not isinstance(hvcs, Gitea): + expected_content += str.join( + os.linesep, + [ + "", + "---", + "", + "**Detailed Changes**: [{prev_version}...{new_version}]({version_compare_url})".format( + prev_version=prev_version.as_tag(), + new_version=version.as_tag(), + version_compare_url=hvcs.compare_url( + prev_version.as_tag(), version.as_tag() + ), + ), + "", + ], + ) + + actual_content = generate_release_notes( + hvcs_client=hvcs_client(remote_url=example_git_https_url), + release=release, + template_dir=Path(""), + history=release_history_w_multiple_notices, + style="angular", + mask_initial_release=mask_initial_release, + ) + + assert expected_content == actual_content diff --git a/tests/unit/semantic_release/commit_parser/test_angular.py b/tests/unit/semantic_release/commit_parser/test_angular.py index 5fc69feb1..ef6ed9eb4 100644 --- a/tests/unit/semantic_release/commit_parser/test_angular.py +++ b/tests/unit/semantic_release/commit_parser/test_angular.py @@ -572,22 +572,25 @@ def test_parser_squashed_commit_github_squash_style( @pytest.mark.parametrize( "commit_message, bump", [ - ("feat(parsers): Add new parser pattern\n\nBREAKING CHANGE: ", LevelBump.MAJOR), - ("feat(parsers)!: Add new parser pattern", LevelBump.MAJOR), ( - "feat(parsers): Add new parser pattern\n\nNew pattern is awesome\n\n" - "BREAKING CHANGE: \n", + "feat(parsers): add new parser pattern\n\nBREAKING CHANGE: change", LevelBump.MAJOR, ), + ("feat(parsers)!: add new parser pattern", LevelBump.MAJOR), ( - "feat(parsers): Add new parser pattern\n\nBREAKING-CHANGE: change !", + "feat(parsers): add new parser pattern\n\nNew pattern is awesome\n\n" + "BREAKING CHANGE: change \n", LevelBump.MAJOR, ), - ("feat(parser): Add emoji parser", LevelBump.MINOR), - ("fix(parser): Fix regex in angular parser", LevelBump.PATCH), - ("test(parser): Add a test for angular parser", LevelBump.NO_RELEASE), - ("feat(parser)!: Edit data parsing stuff", LevelBump.MAJOR), - ("fix!: Edit data parsing stuff again", LevelBump.MAJOR), + ( + "feat(parsers): add new parser pattern\n\nBREAKING-CHANGE: change !", + LevelBump.MAJOR, + ), + ("feat(parser): add emoji parser", LevelBump.MINOR), + ("fix(parser): fix regex in angular parser", LevelBump.PATCH), + ("test(parser): add a test for angular parser", LevelBump.NO_RELEASE), + ("feat(parser)!: edit data parsing stuff", LevelBump.MAJOR), + ("fix!: edit data parsing stuff again", LevelBump.MAJOR), ("fix: superfix", LevelBump.PATCH), ], ) @@ -675,15 +678,15 @@ def test_parser_return_scope_from_commit_message( @pytest.mark.parametrize( "message, descriptions", [ - ("feat(parser): Add emoji parser", ["Add emoji parser"]), - ("fix(parser): Fix regex in angular parser", ["Fix regex in angular parser"]), + ("feat(parser): add emoji parser", ["add emoji parser"]), + ("fix(parser): fix regex in angular parser", ["fix regex in angular parser"]), ( - "test(parser): Add a test for angular parser", - ["Add a test for angular parser"], + "test(parser): add a test for angular parser", + ["add a test for angular parser"], ), ( - f"fix(tox): Fix env \n\n{_long_text}\n\n{_footer}", - ["Fix env ", _long_text, _footer], + f"fix(tox): fix env \n\n{_long_text}\n\n{_footer}", + ["fix env ", _long_text, _footer], ), ("fix: superfix", ["superfix"]), ], @@ -1041,13 +1044,95 @@ def test_parser_return_linked_issues_from_commit_message( assert tuple(linked_issues) == result.linked_issues +@pytest.mark.parametrize( + "message, notices", + [ + pytest.param( + message, + notices, + id=test_id, + ) + for test_id, message, notices in [ + ( + "single notice", + dedent( + """\ + fix(parser): fix regex in angular parser + + NOTICE: This is a notice + """ + ), + ["This is a notice"], + ), + ( + "multiline notice", + dedent( + """\ + fix(parser): fix regex in angular parser + + NOTICE: This is a notice that is longer than + other notices + """ + ), + ["This is a notice that is longer than other notices"], + ), + ( + "multiple notices", + dedent( + """\ + fix(parser): fix regex in angular parser + + NOTICE: This is a notice + + NOTICE: This is a second notice + """ + ), + ["This is a notice", "This is a second notice"], + ), + ( + "notice with other footer", + dedent( + """\ + fix(parser): fix regex in angular parser + + BREAKING CHANGE: This is a breaking change + + NOTICE: This is a notice + """ + ), + ["This is a notice"], + ), + ] + ], +) +def test_parser_return_release_notices_from_commit_message( + default_angular_parser: AngularCommitParser, + message: str, + notices: Sequence[str], + make_commit_obj: MakeCommitObjFn, +): + parsed_results = default_angular_parser.parse(make_commit_obj(message)) + + assert isinstance(parsed_results, Iterable) + assert len(parsed_results) == 1 + + result = next(iter(parsed_results)) + assert isinstance(result, ParsedCommit) + assert tuple(notices) == result.release_notices + + # TODO: v10, remove this + # full_description = str.join("\n\n", result.descriptions) + # full_notice = str.join("\n\n", result.release_notices) + # assert full_notice not in full_description + + ############################## # test custom parser options # ############################## def test_parser_custom_default_level(make_commit_obj: MakeCommitObjFn): options = AngularParserOptions(default_bump_level=LevelBump.MINOR) parsed_results = AngularCommitParser(options).parse( - make_commit_obj("test(parser): Add a test for angular parser") + make_commit_obj("test(parser): add a test for angular parser") ) assert isinstance(parsed_results, Iterable) diff --git a/tests/unit/semantic_release/commit_parser/test_emoji.py b/tests/unit/semantic_release/commit_parser/test_emoji.py index b5e172c52..84f390773 100644 --- a/tests/unit/semantic_release/commit_parser/test_emoji.py +++ b/tests/unit/semantic_release/commit_parser/test_emoji.py @@ -435,6 +435,88 @@ def test_parser_return_linked_issues_from_commit_message( assert tuple(linked_issues) == result.linked_issues +@pytest.mark.parametrize( + "message, notices", + [ + pytest.param( + message, + notices, + id=test_id, + ) + for test_id, message, notices in [ + ( + "single notice", + dedent( + """\ + :bug:(parser): fix regex in angular parser + + NOTICE: This is a notice + """ + ), + ["This is a notice"], + ), + ( + "multiline notice", + dedent( + """\ + :bug:(parser): fix regex in angular parser + + NOTICE: This is a notice that is longer than + other notices + """ + ), + ["This is a notice that is longer than other notices"], + ), + ( + "multiple notices", + dedent( + """\ + :bug:(parser): fix regex in angular parser + + NOTICE: This is a notice + + NOTICE: This is a second notice + """ + ), + ["This is a notice", "This is a second notice"], + ), + ( + "notice with other footer", + dedent( + """\ + :bug:(parser): fix regex in angular parser + + BREAKING CHANGE: This is a breaking change + + NOTICE: This is a notice + """ + ), + ["This is a notice"], + ), + ] + ], +) +def test_parser_return_release_notices_from_commit_message( + default_emoji_parser: EmojiCommitParser, + message: str, + notices: Sequence[str], + make_commit_obj: MakeCommitObjFn, +): + parsed_results = default_emoji_parser.parse(make_commit_obj(message)) + + assert isinstance(parsed_results, Iterable) + assert len(parsed_results) == 1 + + result = next(iter(parsed_results)) + assert isinstance(result, ParsedCommit) + assert tuple(notices) == result.release_notices + + # TODO: v10, remove this + # full_description = str.join("\n\n", result.descriptions) + # full_notice = str.join("\n\n", result.release_notices) + # assert full_notice not in full_description + + @pytest.mark.parametrize( "commit_message, expected_commit_details", [ diff --git a/tests/unit/semantic_release/commit_parser/test_scipy.py b/tests/unit/semantic_release/commit_parser/test_scipy.py index 7682b1c69..a48fc418a 100644 --- a/tests/unit/semantic_release/commit_parser/test_scipy.py +++ b/tests/unit/semantic_release/commit_parser/test_scipy.py @@ -1031,6 +1031,88 @@ def test_parser_return_linked_issues_from_commit_message( assert tuple(linked_issues) == result.linked_issues +@pytest.mark.parametrize( + "message, notices", + [ + pytest.param( + message, + notices, + id=test_id, + ) + for test_id, message, notices in [ + ( + "single notice", + dedent( + """\ + BUG(parser): fix regex in angular parser + + NOTICE: This is a notice + """ + ), + ["This is a notice"], + ), + ( + "multiline notice", + dedent( + """\ + BUG(parser): fix regex in angular parser + + NOTICE: This is a notice that is longer than + other notices + """ + ), + ["This is a notice that is longer than other notices"], + ), + ( + "multiple notices", + dedent( + """\ + BUG(parser): fix regex in angular parser + + NOTICE: This is a notice + + NOTICE: This is a second notice + """ + ), + ["This is a notice", "This is a second notice"], + ), + ( + "notice with other footer", + dedent( + """\ + BUG(parser): fix regex in angular parser + + BREAKING CHANGE: This is a breaking change + + NOTICE: This is a notice + """ + ), + ["This is a notice"], + ), + ] + ], +) +def test_parser_return_release_notices_from_commit_message( + default_scipy_parser: ScipyCommitParser, + message: str, + notices: Sequence[str], + make_commit_obj: MakeCommitObjFn, +): + parsed_results = default_scipy_parser.parse(make_commit_obj(message)) + + assert isinstance(parsed_results, Iterable) + assert len(parsed_results) == 1 + + result = next(iter(parsed_results)) + assert isinstance(result, ParsedCommit) + assert tuple(notices) == result.release_notices + + # TODO: v10, remove this + # full_description = str.join("\n\n", result.descriptions) + # full_notice = str.join("\n\n", result.release_notices) + # assert full_notice not in full_description + + def test_parser_ignore_merge_commit( default_scipy_parser: ScipyCommitParser, make_commit_obj: MakeCommitObjFn, From ebb4c67d46b86fdf79e32edf744a2ec2b09d6a93 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 2 Feb 2025 23:04:53 -0500 Subject: [PATCH 024/160] docs(changelog-templates): document special separate sections of commit descriptions --- docs/changelog_templates.rst | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/docs/changelog_templates.rst b/docs/changelog_templates.rst index 08dfc5edd..44abb8447 100644 --- a/docs/changelog_templates.rst +++ b/docs/changelog_templates.rst @@ -246,6 +246,11 @@ Configuration Examples '''Merged? .*''', ] +If identified or supported by the parser, the default changelog templates will include +a separate section of breaking changes and additional release information. Refer to the +:ref:`commit parsing ` section to see how to write commit messages that +will be properly parsed and displayed in these sections. + .. _changelog-templates-default_release_notes: @@ -260,12 +265,27 @@ default built-in template out-of-the-box for generating release notes. The difference between the changelog and release notes is that the release notes only contain the changes for the current release. Due to the modularity of the -PSR templates, the format is identical to an individual version of the default -changelog. +PSR templates, the format is similar to an individual version of the default +changelog but may include other version specific information. At this time, the default template for version release notes is only available in Markdown format for all VCS types. +If you want to review what the default release notes look like you can use the +following command to print the release notes to the console (remove any configuration +for defining a custom template directory): + +.. code:: console + + # Create a current tag + git tag v1.0.0 + semantic-release --noop changelog --post-to-release-tag v1.0.0 + +The default template provided by PSR will respect the +:ref:`` setting and +will also add a comparison link to the previous release if one exists without +customization. + .. seealso:: - To personalize your release notes, see the :ref:`changelog-templates-custom_release_notes` section. From cd14e9209d4e54f0876e737d1f802dded294a48c Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 2 Feb 2025 23:06:53 -0500 Subject: [PATCH 025/160] docs(commit-parsing): document new release notice footer detection feature of built-in parsers --- docs/commit_parsing.rst | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/docs/commit_parsing.rst b/docs/commit_parsing.rst index 1cb17a886..5dfcf8cbb 100644 --- a/docs/commit_parsing.rst +++ b/docs/commit_parsing.rst @@ -114,6 +114,12 @@ logic in relation to how PSR's core features: feature on/off via the :ref:`config-commit_parser_options` setting. *Feature available in v9.17.0+.* +- **Release Notice Footer Detection**: This parser implements PSR's + :ref:`commit_parser-builtin-release_notice_footer_detection`, which is a custom extension + to traditional `Angular Commit Style Guidelines`_ to use the ``NOTICE`` keyword as a git + footer to document additional release information that is not considered a breaking change. + *Feature available in ${NEW_RELEASE_TAG}+.* + **Limitations**: - Commits with the ``revert`` type are not currently supported. Track the implementation @@ -185,6 +191,11 @@ how PSR's core features: feature on/off via the :ref:`config-commit_parser_options` setting. *Feature available in v9.17.0+.* +- **Release Notice Footer Detection**: This parser implements PSR's + :ref:`commit_parser-builtin-release_notice_footer_detection`, which is a custom extension + that uses the ``NOTICE`` keyword as a git footer to document additional release information + that is not considered a breaking change. *Feature available in ${NEW_RELEASE_TAG}+.* + If no commit parser options are provided via the configuration, the parser will use PSR's built-in :py:class:`defaults `. @@ -310,6 +321,33 @@ return an empty tuple. ---- +.. _commit_parser-builtin-release_notice_footer_detection: + +Common Release Notice Footer Detection +"""""""""""""""""""""""""""""""""""""" + +*Introduced in ${NEW_RELEASE_TAG}** + +All of the PSR built-in parsers implement common release notice footer detection logic +to identify and extract a ``NOTICE`` git trailer that documents any additional release +information the developer wants to provide to the software consumer. The idea extends +from the concept of the ``BREAKING CHANGE:`` git trailer to document any breaking change +descriptions but the ``NOTICE`` trailer is intended to document any information that is +below the threshold of a breaking change while still important for the software consumer +to be aware of. Common uses would be to provide deprecation warnings or more detailed +change usage information for that release. Parsers will collapse single newlines after +the ``NOTICE`` trailer into a single line paragraph. Commits may have more than one +``NOTICE`` trailer in a single commit message. Each +:py:class:`ParsedCommit ` will have +a ``release_notices`` attribute that is a tuple of string paragraphs to identify each +release notice. + +In the default changelog and release notes template, these release notices will be +formatted into their own section called **ADDITIONAL RELEASE INFORMATION**. Each will +include any commit scope defined and each release notice in alphabetical order. + +---- + .. _commit_parser-builtin-squash_commit_evaluation: Common Squash Commit Evaluation From 41172c1272a402e94e3c68571d013cbdcb5b9023 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Wed, 5 Feb 2025 00:49:01 -0500 Subject: [PATCH 026/160] feat(release-notes): add license information to default release notes template (#1167) Adds a one-line notice to the version release notes about the current license the version is released under at that time. Resolves: #228 * test(release-notes): update unit tests to include license statement * test(fixtures): add release notes generator fixture from repo definition * test(cmd-version): add test to evaluate release notes generation w/ license statement * test(cmd-changelog): add test to evaluate release notes generation w/ license statement * docs(changelog-templates): add details about license specification in the release notes * refactor(changelog-templates): simplify markdown template processing * refactor(changelog-templates): simplify reStructuredText template processing * chore(release-notes): add license information to each version release for psr --- .../.components/versioned_changes.md.j2 | 7 +- config/release-templates/.release_notes.md.j2 | 3 + docs/changelog_templates.rst | 6 + src/semantic_release/changelog/context.py | 1 + src/semantic_release/cli/changelog_writer.py | 2 + .../cli/commands/changelog.py | 46 ++- src/semantic_release/cli/commands/version.py | 22 +- src/semantic_release/cli/config.py | 21 +- .../angular/md/.components/changes.md.j2 | 28 +- .../md/.components/first_release.md.j2 | 15 +- .../md/.components/versioned_changes.md.j2 | 20 +- .../templates/angular/md/.release_notes.md.j2 | 27 +- .../angular/rst/.components/changes.rst.j2 | 36 +- .../rst/.components/first_release.rst.j2 | 2 + .../rst/.components/versioned_changes.rst.j2 | 2 + tests/const.py | 6 +- .../test_changelog_release_notes.py | 332 ++++++++++++++++++ .../cmd_version/test_version_release_notes.py | 105 +++++- tests/fixtures/example_project.py | 6 +- tests/fixtures/git_repo.py | 272 +++++++++++++- .../changelog/test_release_notes.py | 39 +- 21 files changed, 921 insertions(+), 77 deletions(-) create mode 100644 tests/e2e/cmd_changelog/test_changelog_release_notes.py diff --git a/config/release-templates/.components/versioned_changes.md.j2 b/config/release-templates/.components/versioned_changes.md.j2 index bd770150b..f63b59961 100644 --- a/config/release-templates/.components/versioned_changes.md.j2 +++ b/config/release-templates/.components/versioned_changes.md.j2 @@ -2,6 +2,8 @@ ## vX.X.X (YYYY-MMM-DD) +_This release is published under the MIT License._ # Release Notes Only + {{ change_sections }} #}{{ @@ -9,7 +11,10 @@ release.version.as_semver_tag(), release.tagged_date.strftime("%Y-%m-%d") ) -}}{# +}}{% if license_name is defined and license_name +%}{{ "\n_This release is published under the %s License._\n" | format(license_name) +}}{% endif +%}{# #}{% set commit_objects = release["elements"] %}{% include "changes.md.j2" -%} diff --git a/config/release-templates/.release_notes.md.j2 b/config/release-templates/.release_notes.md.j2 index 147bc12e1..2fa05eecb 100644 --- a/config/release-templates/.release_notes.md.j2 +++ b/config/release-templates/.release_notes.md.j2 @@ -3,6 +3,8 @@ ## v1.0.0 (2020-01-01) +_This release is published under the MIT License._ + ### ✨ Features - Add new feature ([PR#10](https://domain.com/namespace/repo/pull/10), [`abcdef0`](https://domain.com/namespace/repo/commit/HASH)) @@ -43,6 +45,7 @@ #}{# # Set line width to 1000 to avoid wrapping as GitHub will handle it #}{% set max_line_width = max_line_width | default(1000) %}{% set hanging_indent = hanging_indent | default(2) +%}{% set license_name = license_name | default("", True) %}{% set releases = context.history.released.values() | list %}{% set curr_release_index = releases.index(release) %}{# diff --git a/docs/changelog_templates.rst b/docs/changelog_templates.rst index 44abb8447..e8193aa84 100644 --- a/docs/changelog_templates.rst +++ b/docs/changelog_templates.rst @@ -286,10 +286,16 @@ The default template provided by PSR will respect the will also add a comparison link to the previous release if one exists without customization. +As of ``${NEW_RELEASE_TAG}``, the default release notes will also include a statement to +declare which license the project was released under. PSR determines which license +to declare based on the value of ``project.license-expression`` in the ``pyproject.toml`` +file as defined in the `PEP 639`_ specification. + .. seealso:: - To personalize your release notes, see the :ref:`changelog-templates-custom_release_notes` section. +.. _PEP 639: https://peps.python.org/pep-0639/ .. _changelog-templates-template-rendering: diff --git a/src/semantic_release/changelog/context.py b/src/semantic_release/changelog/context.py index 449ed2f1a..fe16ca2c2 100644 --- a/src/semantic_release/changelog/context.py +++ b/src/semantic_release/changelog/context.py @@ -29,6 +29,7 @@ class ReleaseNotesContext: version: Version release: Release mask_initial_release: bool + license_name: str filters: tuple[Callable[..., Any], ...] = () def bind_to_environment(self, env: Environment) -> Environment: diff --git a/src/semantic_release/cli/changelog_writer.py b/src/semantic_release/cli/changelog_writer.py index 01f28f315..5ee86457e 100644 --- a/src/semantic_release/cli/changelog_writer.py +++ b/src/semantic_release/cli/changelog_writer.py @@ -230,6 +230,7 @@ def generate_release_notes( history: ReleaseHistory, style: str, mask_initial_release: bool, + license_name: str = "", ) -> str: users_tpl_file = template_dir / DEFAULT_RELEASE_NOTES_TPL_FILE @@ -256,6 +257,7 @@ def generate_release_notes( version=release["version"], release=release, mask_initial_release=mask_initial_release, + license_name=license_name, filters=( *hvcs_client.get_changelog_context_filters(), create_pypi_url, diff --git a/src/semantic_release/cli/commands/changelog.py b/src/semantic_release/cli/commands/changelog.py index d2f4ec345..316b44450 100644 --- a/src/semantic_release/cli/commands/changelog.py +++ b/src/semantic_release/cli/commands/changelog.py @@ -1,10 +1,13 @@ from __future__ import annotations import logging +from contextlib import suppress +from pathlib import Path from typing import TYPE_CHECKING import click -from git import Repo +import tomlkit +from git import GitCommandError, Repo from semantic_release.changelog.release_history import ReleaseHistory from semantic_release.cli.changelog_writer import ( @@ -21,6 +24,43 @@ log = logging.getLogger(__name__) +def get_license_name_for_release(tag_name: str, project_root: Path) -> str: + # Retrieve the license name at the time of the specific release tag + project_metadata: dict[str, str] = {} + curr_dir = Path.cwd().resolve() + allowed_directories = [ + dir_path + for dir_path in [curr_dir, *curr_dir.parents] + if str(project_root) in str(dir_path) + ] + for allowed_dir in allowed_directories: + proj_toml = allowed_dir.joinpath("pyproject.toml") + with Repo(project_root) as git_repo, suppress(GitCommandError): + toml_contents = git_repo.git.show( + f"{tag_name}:{proj_toml.relative_to(project_root)}" + ) + config_toml = tomlkit.parse(toml_contents) + project_metadata = config_toml.unwrap().get("project", project_metadata) + break + + license_cfg = project_metadata.get( + "license-expression", + project_metadata.get( + "license", + "", + ), + ) + + if not isinstance(license_cfg, (str, dict)) or license_cfg is None: + return "" + + return ( + license_cfg.get("text", "") # type: ignore[attr-defined] + if isinstance(license_cfg, dict) + else license_cfg or "" + ) + + def post_release_notes( release_tag: str, release_notes: str, @@ -120,6 +160,10 @@ def changelog(cli_ctx: CliContextObj, release_tag: str | None) -> None: release_history, style=runtime.changelog_style, mask_initial_release=runtime.changelog_mask_initial_release, + license_name=get_license_name_for_release( + tag_name=release_tag, + project_root=runtime.repo_dir, + ), ) try: diff --git a/src/semantic_release/cli/commands/version.py b/src/semantic_release/cli/commands/version.py index 38609c0ad..104faa9fe 100644 --- a/src/semantic_release/cli/commands/version.py +++ b/src/semantic_release/cli/commands/version.py @@ -699,13 +699,31 @@ def version( # noqa: C901 log.info("Remote does not support releases. Skipping release creation...") return + license_cfg = runtime.project_metadata.get( + "license-expression", + runtime.project_metadata.get( + "license", + "", + ), + ) + + if not isinstance(license_cfg, (str, dict)) or license_cfg is None: + license_cfg = "" + + license_name = ( + license_cfg.get("text", "") + if isinstance(license_cfg, dict) + else license_cfg or "" + ) + release_notes = generate_release_notes( hvcs_client, - release_history.released[new_version], - runtime.template_dir, + release=release_history.released[new_version], + template_dir=runtime.template_dir, history=release_history, style=runtime.changelog_style, mask_initial_release=runtime.changelog_mask_initial_release, + license_name=license_name, ) exception: Exception | None = None diff --git a/src/semantic_release/cli/config.py b/src/semantic_release/cli/config.py index f09520092..7e5c31131 100644 --- a/src/semantic_release/cli/config.py +++ b/src/semantic_release/cli/config.py @@ -15,6 +15,8 @@ ) from typing import Any, ClassVar, Dict, List, Literal, Optional, Tuple, Type, Union +# typing_extensions is for Python 3.8, 3.9, 3.10 compatibility +import tomlkit from git import Actor, InvalidGitRepositoryError from git.repo.base import Repo from jinja2 import Environment @@ -26,8 +28,6 @@ field_validator, model_validator, ) - -# typing_extensions is for Python 3.8, 3.9, 3.10 compatibility from typing_extensions import Annotated, Self from urllib3.util.url import parse_url @@ -523,6 +523,7 @@ def _recursive_getattr(obj: Any, path: str) -> Any: class RuntimeContext: _mask_attrs_: ClassVar[List[str]] = ["hvcs_client.token"] + project_metadata: dict[str, Any] repo_dir: Path commit_parser: CommitParser[ParseResult, ParserOptions] version_translator: VersionTranslator @@ -599,6 +600,21 @@ def from_raw_config( # noqa: C901 # credentials masking for logging masker = MaskingFilter(_use_named_masks=raw.logging_use_named_masks) + # TODO: move to config if we change how the generated config is constructed + # Retrieve project metadata from pyproject.toml + project_metadata: dict[str, str] = {} + curr_dir = Path.cwd().resolve() + allowed_directories = [ + dir_path + for dir_path in [curr_dir, *curr_dir.parents] + if str(raw.repo_dir) in str(dir_path) + ] + for allowed_dir in allowed_directories: + if (proj_toml := allowed_dir.joinpath("pyproject.toml")).exists(): + config_toml = tomlkit.parse(proj_toml.read_text()) + project_metadata = config_toml.unwrap().get("project", project_metadata) + break + # Retrieve details from repository with Repo(str(raw.repo_dir)) as git_repo: try: @@ -825,6 +841,7 @@ def from_raw_config( # noqa: C901 # ) self = cls( + project_metadata=project_metadata, repo_dir=raw.repo_dir, commit_parser=commit_parser, version_translator=version_translator, diff --git a/src/semantic_release/data/templates/angular/md/.components/changes.md.j2 b/src/semantic_release/data/templates/angular/md/.components/changes.md.j2 index 0cdbf3b6f..bf4cd98b5 100644 --- a/src/semantic_release/data/templates/angular/md/.components/changes.md.j2 +++ b/src/semantic_release/data/templates/angular/md/.components/changes.md.j2 @@ -41,8 +41,8 @@ EXAMPLE: #}{% for type_, commits in commit_objects if type_ != "unknown" %}{# PREPROCESS COMMITS (order by description & format description line) #}{% set ns = namespace(commits=commits) -%}{{ apply_alphabetical_ordering_by_descriptions(ns) | default("", true) -}}{# +%}{% set _ = apply_alphabetical_ordering_by_descriptions(ns) +%}{# #}{% set commit_descriptions = [] %}{# #}{% for commit in ns.commits @@ -57,8 +57,8 @@ EXAMPLE: ) %}{% endif %}{% set description = description | autofit_text_width(max_line_width, hanging_indent) -%}{{ commit_descriptions.append(description) | default("", true) -}}{% endfor +%}{% set _ = commit_descriptions.append(description) +%}{% endfor %}{# # # PRINT SECTION (header & commits) #}{% if commit_descriptions | length > 0 @@ -81,18 +81,18 @@ EXAMPLE: #}{% if breaking_commits | length > 0 %}{# PREPROCESS COMMITS #}{% set brk_ns = namespace(commits=breaking_commits) -%}{{ apply_alphabetical_ordering_by_brk_descriptions(brk_ns) | default("", true) -}}{# +%}{% set _ = apply_alphabetical_ordering_by_brk_descriptions(brk_ns) +%}{# #}{% set brking_descriptions = [] %}{# #}{% for commit in brk_ns.commits %}{% set full_description = "- %s" | format( format_breaking_changes_description(commit).split("\n\n") | join("\n\n- ") ) -%}{{ brking_descriptions.append( +%}{% set _ = brking_descriptions.append( full_description | autofit_text_width(max_line_width, hanging_indent) - ) | default("", true) -}}{% endfor + ) +%}{% endfor %}{# # # PRINT BREAKING CHANGE DESCRIPTIONS (header & descriptions) #}{{ "\n" @@ -114,18 +114,18 @@ EXAMPLE: #}{% if notice_commits | length > 0 %}{# PREPROCESS COMMITS #}{% set notice_ns = namespace(commits=notice_commits) -%}{{ apply_alphabetical_ordering_by_release_notices(notice_ns) | default("", true) -}}{# +%}{% set _ = apply_alphabetical_ordering_by_release_notices(notice_ns) +%}{# #}{% set release_notices = [] %}{# #}{% for commit in notice_ns.commits %}{% set full_description = "- %s" | format( format_release_notice(commit).split("\n\n") | join("\n\n- ") ) -%}{{ release_notices.append( +%}{% set _ = release_notices.append( full_description | autofit_text_width(max_line_width, hanging_indent) - ) | default("", true) -}}{% endfor + ) +%}{% endfor %}{# # # PRINT RELEASE NOTICE INFORMATION (header & descriptions) #}{{ "\n" diff --git a/src/semantic_release/data/templates/angular/md/.components/first_release.md.j2 b/src/semantic_release/data/templates/angular/md/.components/first_release.md.j2 index 668092a9d..d0e44f7cc 100644 --- a/src/semantic_release/data/templates/angular/md/.components/first_release.md.j2 +++ b/src/semantic_release/data/templates/angular/md/.components/first_release.md.j2 @@ -1,11 +1,18 @@ -{# +{# EXAMPLE: ## vX.X.X (YYYY-MMM-DD) +_This release is published under the MIT License._ # Release Notes Only + +- Initial Release + #}{{ -"## %s (%s)" | format( +"## %s (%s)\n" | format( release.version.as_semver_tag(), release.tagged_date.strftime("%Y-%m-%d") -)}} - +) +}}{% if license_name is defined and license_name +%}{{ "\n_This release is published under the %s License._\n" | format(license_name) +}}{% endif +%} - Initial Release diff --git a/src/semantic_release/data/templates/angular/md/.components/versioned_changes.md.j2 b/src/semantic_release/data/templates/angular/md/.components/versioned_changes.md.j2 index 489570a37..de2b160ce 100644 --- a/src/semantic_release/data/templates/angular/md/.components/versioned_changes.md.j2 +++ b/src/semantic_release/data/templates/angular/md/.components/versioned_changes.md.j2 @@ -1,14 +1,20 @@ -{# +{# EXAMPLE: ## vX.X.X (YYYY-MMM-DD) -#}{{ +_This release is published under the MIT License._ # Release Notes Only - "## %s (%s)\n" | format( - release.version.as_semver_tag(), - release.tagged_date.strftime("%Y-%m-%d") - ) +{{ change_sections }} -}}{% set commit_objects = release["elements"] | dictsort +#}{{ +"## %s (%s)\n" | format( + release.version.as_semver_tag(), + release.tagged_date.strftime("%Y-%m-%d") +) +}}{% if license_name is defined and license_name +%}{{ "\n_This release is published under the %s License._\n" | format(license_name) +}}{% endif +%}{# +#}{% set commit_objects = release["elements"] | dictsort %}{% include "changes.md.j2" -%} diff --git a/src/semantic_release/data/templates/angular/md/.release_notes.md.j2 b/src/semantic_release/data/templates/angular/md/.release_notes.md.j2 index a5ad54832..8bfd679cc 100644 --- a/src/semantic_release/data/templates/angular/md/.release_notes.md.j2 +++ b/src/semantic_release/data/templates/angular/md/.release_notes.md.j2 @@ -1,5 +1,9 @@ {# EXAMPLE: +## v1.0.0 (2020-01-01) + +_This release is published under the MIT License._ + ### Features - Add new feature ([#10](https://domain.com/namespace/repo/pull/10), [`abcdef0`](https://domain.com/namespace/repo/commit/HASH)) @@ -29,27 +33,26 @@ #}{# # Set line width to 1000 to avoid wrapping as GitHub will handle it #}{% set max_line_width = max_line_width | default(1000) %}{% set hanging_indent = hanging_indent | default(2) +%}{% set license_name = license_name | default("", True) %}{% set releases = context.history.released.values() | list %}{% set curr_release_index = releases.index(release) -%}{% set prev_release_index = curr_release_index + 1 %}{# -#}{% if 'compare_url' is filter and prev_release_index < releases | length -%}{% set prev_version_tag = releases[prev_release_index].version.as_tag() -%}{% set new_version_tag = release.version.as_tag() -%}{% set version_compare_url = prev_version_tag | compare_url(new_version_tag) -%}{% set detailed_changes_link = '[{}...{}]({})'.format( - prev_version_tag, new_version_tag, version_compare_url - ) -%}{% endif -%}{# -#}{% if releases | length == 1 and mask_initial_release +#}{% if mask_initial_release and curr_release_index == releases | length - 1 %}{# # On a first release, generate our special message #}{% include ".components/first_release.md.j2" %}{% else %}{# # Not the first release so generate notes normally #}{% include ".components/versioned_changes.md.j2" -%}{# -#}{% if detailed_changes_link is defined +#}{% set prev_release_index = curr_release_index + 1 +%}{# +#}{% if 'compare_url' is filter and prev_release_index < releases | length +%}{% set prev_version_tag = releases[prev_release_index].version.as_tag() +%}{% set new_version_tag = release.version.as_tag() +%}{% set version_compare_url = prev_version_tag | compare_url(new_version_tag) +%}{% set detailed_changes_link = '[{}...{}]({})'.format( + prev_version_tag, new_version_tag, version_compare_url + ) %}{{ "\n" }}{{ "---\n" }}{{ "\n" diff --git a/src/semantic_release/data/templates/angular/rst/.components/changes.rst.j2 b/src/semantic_release/data/templates/angular/rst/.components/changes.rst.j2 index 565446884..2ff88c892 100644 --- a/src/semantic_release/data/templates/angular/rst/.components/changes.rst.j2 +++ b/src/semantic_release/data/templates/angular/rst/.components/changes.rst.j2 @@ -53,24 +53,24 @@ ADDITIONAL RELEASE INFORMATION %}{# # # PREPROCESS COMMITS #}{% set ns = namespace(commits=commits) -%}{{ apply_alphabetical_ordering_by_descriptions(ns) | default("", true) -}}{# +%}{% set _ = apply_alphabetical_ordering_by_descriptions(ns) +%}{# #}{% set commit_descriptions = [] %}{# #}{% for commit in ns.commits %}{# # Extract PR/MR reference if it exists and store it for later #}{% set pr_link_reference = extract_pr_link_reference(commit) | default("", true) %}{% if pr_link_reference != "" -%}{{ post_paragraph_links.append(pr_link_reference) | default("", true) -}}{% endif +%}{% set _ = post_paragraph_links.append(pr_link_reference) +%}{% endif %}{# # # Always generate a commit hash reference link and store it for later #}{% set commit_hash_link_reference = format_link_reference( commit.hexsha | commit_hash_url, commit.short_hash ) -%}{{ post_paragraph_links.append(commit_hash_link_reference) | default("", true) -}}{# +%}{% set _ = post_paragraph_links.append(commit_hash_link_reference) +%}{# # Generate the commit summary line and format it for RST # Update the first line with reference links and if commit description # has more than one line, add the rest of the lines @@ -84,8 +84,8 @@ ADDITIONAL RELEASE INFORMATION %}{% endif %}{% set description = description | convert_md_to_rst %}{% set description = description | autofit_text_width(max_line_width, hanging_indent) -%}{{ commit_descriptions.append(description) | default("", true) -}}{% endfor +%}{% set _ = commit_descriptions.append(description) +%}{% endfor %}{# # # PRINT SECTION (Header & Commits) #}{% if commit_descriptions | length > 0 @@ -109,18 +109,18 @@ ADDITIONAL RELEASE INFORMATION #}{% if breaking_commits | length > 0 %}{# # PREPROCESS COMMITS #}{% set brk_ns = namespace(commits=breaking_commits) -%}{{ apply_alphabetical_ordering_by_brk_descriptions(brk_ns) | default("", true) -}}{# +%}{% set _ = apply_alphabetical_ordering_by_brk_descriptions(brk_ns) +%}{# #}{% set brking_descriptions = [] %}{# #}{% for commit in brk_ns.commits %}{% set full_description = "* %s" | format( format_breaking_changes_description(commit).split("\n\n") | join("\n\n* ") ) -%}{{ brking_descriptions.append( +%}{% set _ = brking_descriptions.append( full_description | convert_md_to_rst | autofit_text_width(max_line_width, hanging_indent) - ) | default("", true) -}}{% endfor + ) +%}{% endfor %}{# # # PRINT BREAKING CHANGE DESCRIPTIONS (header & descriptions) #}{{ "\n" @@ -143,18 +143,18 @@ ADDITIONAL RELEASE INFORMATION #}{% if notice_commits | length > 0 %}{# PREPROCESS COMMITS #}{% set notice_ns = namespace(commits=notice_commits) -%}{{ apply_alphabetical_ordering_by_release_notices(notice_ns) | default("", true) -}}{# +%}{% set _ = apply_alphabetical_ordering_by_release_notices(notice_ns) +%}{# #}{% set release_notices = [] %}{# #}{% for commit in notice_ns.commits %}{% set full_description = "* %s" | format( format_release_notice(commit).split("\n\n") | join("\n\n* ") ) -%}{{ release_notices.append( +%}{% set _ = release_notices.append( full_description | convert_md_to_rst | autofit_text_width(max_line_width, hanging_indent) - ) | default("", true) -}}{% endfor + ) +%}{% endfor %}{# # # PRINT RELEASE NOTICE INFORMATION (header & descriptions) #}{{ "\n" diff --git a/src/semantic_release/data/templates/angular/rst/.components/first_release.rst.j2 b/src/semantic_release/data/templates/angular/rst/.components/first_release.rst.j2 index 15c8c5301..5c08066f7 100644 --- a/src/semantic_release/data/templates/angular/rst/.components/first_release.rst.j2 +++ b/src/semantic_release/data/templates/angular/rst/.components/first_release.rst.j2 @@ -6,6 +6,8 @@ vX.X.X (YYYY-MMM-DD) ==================== +* Initial Release + #}{% set version_header = "%s (%s)" | format( release.version.as_semver_tag(), release.tagged_date.strftime("%Y-%m-%d") diff --git a/src/semantic_release/data/templates/angular/rst/.components/versioned_changes.rst.j2 b/src/semantic_release/data/templates/angular/rst/.components/versioned_changes.rst.j2 index 6229b2671..1297deb54 100644 --- a/src/semantic_release/data/templates/angular/rst/.components/versioned_changes.rst.j2 +++ b/src/semantic_release/data/templates/angular/rst/.components/versioned_changes.rst.j2 @@ -5,6 +5,8 @@ vX.X.X (YYYY-MMM-DD) ==================== +{{ change_sections }} + #}{% set version_header = "%s (%s)" | format( release.version.as_semver_tag(), release.tagged_date.strftime("%Y-%m-%d") diff --git a/tests/const.py b/tests/const.py index e9da2d43d..5bc12340f 100644 --- a/tests/const.py +++ b/tests/const.py @@ -104,16 +104,20 @@ class RepoActionStep(str, Enum): EXAMPLE_PROJECT_NAME = "example" EXAMPLE_PROJECT_VERSION = "0.0.0" +EXAMPLE_PROJECT_LICENSE = "MIT" # Uses the internal defaults of semantic-release unless otherwise needed for testing # modify the pyproject toml as necessary for the test using update_pyproject_toml() # and derivative fixtures EXAMPLE_PYPROJECT_TOML_CONTENT = rf""" +[project] +license-expression = "{EXAMPLE_PROJECT_LICENSE}" + [tool.poetry] name = "{EXAMPLE_PROJECT_NAME}" version = "{EXAMPLE_PROJECT_VERSION}" description = "Just an example" -license = "MIT" +license = "{EXAMPLE_PROJECT_LICENSE}" authors = ["semantic-release "] readme = "README.md" classifiers = [ diff --git a/tests/e2e/cmd_changelog/test_changelog_release_notes.py b/tests/e2e/cmd_changelog/test_changelog_release_notes.py new file mode 100644 index 000000000..7cde9b13f --- /dev/null +++ b/tests/e2e/cmd_changelog/test_changelog_release_notes.py @@ -0,0 +1,332 @@ +from __future__ import annotations + +from datetime import datetime +from typing import TYPE_CHECKING + +import pytest +from pytest_lazy_fixtures import lf as lazy_fixture + +from semantic_release.cli.commands.main import main +from semantic_release.version.version import Version + +from tests.const import CHANGELOG_SUBCMD, EXAMPLE_PROJECT_LICENSE, MAIN_PROG_NAME +from tests.fixtures.repos import ( + repo_w_github_flow_w_default_release_channel_angular_commits, + repo_w_github_flow_w_feature_release_channel_angular_commits, + repo_w_trunk_only_angular_commits, + repo_w_trunk_only_emoji_commits, + repo_w_trunk_only_scipy_commits, +) +from tests.util import assert_successful_exit_code + +if TYPE_CHECKING: + from click.testing import CliRunner + from requests_mock import Mocker + + from tests.conftest import GetStableDateNowFn + from tests.fixtures.example_project import UpdatePyprojectTomlFn + from tests.fixtures.git_repo import ( + BuiltRepoResult, + GenerateDefaultReleaseNotesFromDefFn, + GetCfgValueFromDefFn, + GetHvcsClientFromRepoDefFn, + GetVersionsFromRepoBuildDefFn, + SplitRepoActionsByReleaseTagsFn, + ) + + +@pytest.mark.parametrize( + "repo_result", + [ + lazy_fixture(repo_fixture_name) + for repo_fixture_name in [ + repo_w_trunk_only_angular_commits.__name__, + ] + ], +) +def test_changelog_latest_release_notes( + repo_result: BuiltRepoResult, + get_cfg_value_from_def: GetCfgValueFromDefFn, + get_versions_from_repo_build_def: GetVersionsFromRepoBuildDefFn, + get_hvcs_client_from_repo_def: GetHvcsClientFromRepoDefFn, + cli_runner: CliRunner, + post_mocker: Mocker, + split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, + generate_default_release_notes_from_def: GenerateDefaultReleaseNotesFromDefFn, +): + # Setup + repo_def = repo_result["definition"] + tag_format_str: str = get_cfg_value_from_def(repo_def, "tag_format_str") # type: ignore[assignment] + repo_actions_per_version = split_repo_actions_by_release_tags( + repo_definition=repo_def, + tag_format_str=tag_format_str, + ) + all_versions = get_versions_from_repo_build_def(repo_def) + latest_release_version = all_versions[-1] + release_tag = tag_format_str.format(version=latest_release_version) + + expected_release_notes = generate_default_release_notes_from_def( + version_actions=repo_actions_per_version[release_tag], + hvcs=get_hvcs_client_from_repo_def(repo_def), + previous_version=( + Version.parse(all_versions[-2]) if len(all_versions) > 1 else None + ), + license_name=EXAMPLE_PROJECT_LICENSE, + mask_initial_release=get_cfg_value_from_def(repo_def, "mask_initial_release"), + ) + + # Act + cli_cmd = [MAIN_PROG_NAME, CHANGELOG_SUBCMD, "--post-to-release-tag", release_tag] + result = cli_runner.invoke(main, cli_cmd[1:]) + + # Evaluate + assert_successful_exit_code(result, cli_cmd) + assert post_mocker.call_count == 1 + assert post_mocker.last_request is not None + request_body = post_mocker.last_request.json() + + assert "body" in request_body + actual_posted_notes = request_body["body"] + + assert expected_release_notes == actual_posted_notes + + +@pytest.mark.parametrize( + "repo_result, mask_initial_release", + [ + ( + lazy_fixture(repo_w_trunk_only_angular_commits.__name__), + True, + ), + pytest.param( + lazy_fixture(repo_w_trunk_only_angular_commits.__name__), + False, + marks=pytest.mark.comprehensive, + ), + *[ + pytest.param( + lazy_fixture(repo_fixture_name), + mask_initial_release, + marks=pytest.mark.comprehensive, + ) + for mask_initial_release in [True, False] + for repo_fixture_name in [ + repo_w_github_flow_w_default_release_channel_angular_commits.__name__, + ] + ], + ], +) +def test_changelog_previous_release_notes( + repo_result: BuiltRepoResult, + mask_initial_release: bool, + get_cfg_value_from_def: GetCfgValueFromDefFn, + get_versions_from_repo_build_def: GetVersionsFromRepoBuildDefFn, + get_hvcs_client_from_repo_def: GetHvcsClientFromRepoDefFn, + cli_runner: CliRunner, + post_mocker: Mocker, + split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, + generate_default_release_notes_from_def: GenerateDefaultReleaseNotesFromDefFn, + update_pyproject_toml: UpdatePyprojectTomlFn, +): + # Setup + repo_def = repo_result["definition"] + tag_format_str: str = get_cfg_value_from_def(repo_def, "tag_format_str") # type: ignore[assignment] + repo_actions_per_version = split_repo_actions_by_release_tags( + repo_definition=repo_def, + tag_format_str=tag_format_str, + ) + # Extract all versions except for the latest one + all_prev_versions = get_versions_from_repo_build_def(repo_def)[:-1] + latest_release_version = all_prev_versions[-1] + release_tag = tag_format_str.format(version=latest_release_version) + + expected_release_notes = generate_default_release_notes_from_def( + version_actions=repo_actions_per_version[release_tag], + hvcs=get_hvcs_client_from_repo_def(repo_def), + previous_version=( + Version.parse(all_prev_versions[-2]) if len(all_prev_versions) > 1 else None + ), + license_name=EXAMPLE_PROJECT_LICENSE, + mask_initial_release=mask_initial_release, + ) + + update_pyproject_toml( + "tool.semantic_release.changelog.default_templates.mask_initial_release", + mask_initial_release, + ) + + # Act + cli_cmd = [MAIN_PROG_NAME, CHANGELOG_SUBCMD, "--post-to-release-tag", release_tag] + result = cli_runner.invoke(main, cli_cmd[1:]) + + # Evaluate + assert_successful_exit_code(result, cli_cmd) + assert post_mocker.call_count == 1 + assert post_mocker.last_request is not None + request_body = post_mocker.last_request.json() + + assert "body" in request_body + actual_posted_notes = request_body["body"] + + assert expected_release_notes == actual_posted_notes + + +@pytest.mark.parametrize( + "repo_result, cache_key, mask_initial_release, license_name", + [ + ( + lazy_fixture(repo_w_trunk_only_angular_commits.__name__), + f"psr/repos/{repo_w_trunk_only_angular_commits.__name__}", + True, + "BSD-3-Clause", + ), + pytest.param( + lazy_fixture(repo_w_trunk_only_angular_commits.__name__), + f"psr/repos/{repo_w_trunk_only_angular_commits.__name__}", + False, + "BSD-3-Clause", + marks=pytest.mark.comprehensive, + ), + *[ + pytest.param( + lazy_fixture(repo_fixture_name), + f"psr/repos/{repo_fixture_name}", + mask_initial_release, + "BSD-3-Clause", + marks=pytest.mark.comprehensive, + ) + for mask_initial_release in [True, False] + for repo_fixture_name in [ + repo_w_trunk_only_emoji_commits.__name__, + repo_w_trunk_only_scipy_commits.__name__, + # Add more repos here if needed + # github_flow had issues as its hard to generate the release notes from squash commits + repo_w_github_flow_w_feature_release_channel_angular_commits.__name__, + ] + ], + ], +) +def test_changelog_release_notes_license_change( + repo_result: BuiltRepoResult, + license_name: str, + mask_initial_release: bool, + get_cfg_value_from_def: GetCfgValueFromDefFn, + get_versions_from_repo_build_def: GetVersionsFromRepoBuildDefFn, + get_hvcs_client_from_repo_def: GetHvcsClientFromRepoDefFn, + cli_runner: CliRunner, + post_mocker: Mocker, + split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, + generate_default_release_notes_from_def: GenerateDefaultReleaseNotesFromDefFn, + update_pyproject_toml: UpdatePyprojectTomlFn, + cache: pytest.Cache, + cache_key: str, + stable_now_date: GetStableDateNowFn, +): + # Setup + repo_def = repo_result["definition"] + tag_format_str: str = get_cfg_value_from_def(repo_def, "tag_format_str") # type: ignore[assignment] + + if not (repo_build_data := cache.get(cache_key, None)): + pytest.fail("Repo build date not found in cache") + + repo_build_datetime = datetime.strptime(repo_build_data["build_date"], "%Y-%m-%d") + now_datetime = stable_now_date().replace( + year=repo_build_datetime.year, + month=repo_build_datetime.month, + day=repo_build_datetime.day, + ) + + repo_actions_per_version = split_repo_actions_by_release_tags( + repo_definition=repo_def, + tag_format_str=tag_format_str, + ) + # Extract all versions + all_versions = get_versions_from_repo_build_def(repo_def) + assert len(all_versions) > 1 + latest_release_version = all_versions[-1] + previous_release_version = all_versions[-2] + latest_release_tag = tag_format_str.format(version=latest_release_version) + prev_release_tag = tag_format_str.format(version=previous_release_version) + + expected_release_notes = generate_default_release_notes_from_def( + version_actions=repo_actions_per_version[latest_release_tag], + hvcs=get_hvcs_client_from_repo_def(repo_def), + previous_version=( + Version.parse(previous_release_version) if len(all_versions) > 1 else None + ), + license_name=license_name, + mask_initial_release=mask_initial_release, + ) + + expected_prev_release_notes = generate_default_release_notes_from_def( + version_actions=repo_actions_per_version[prev_release_tag], + hvcs=get_hvcs_client_from_repo_def(repo_def), + previous_version=( + Version.parse(all_versions[-3]) if len(all_versions) > 2 else None + ), + license_name=EXAMPLE_PROJECT_LICENSE, + mask_initial_release=mask_initial_release, + ) + + update_pyproject_toml( + "tool.semantic_release.changelog.default_templates.mask_initial_release", + mask_initial_release, + ) + update_pyproject_toml("project.license-expression", license_name) + + git_repo = repo_result["repo"] + + git_repo.git.commit( + amend=True, + a=True, + no_edit=True, + date=now_datetime.isoformat(timespec="seconds"), + ) + + with git_repo.git.custom_environment( + GIT_COMMITTER_DATE=now_datetime.isoformat(timespec="seconds"), + ): + git_repo.git.tag(latest_release_tag, d=True) + git_repo.git.tag(latest_release_tag, a=True, m=latest_release_tag) + + # Act + cli_cmd = [ + MAIN_PROG_NAME, + CHANGELOG_SUBCMD, + "--post-to-release-tag", + latest_release_tag, + ] + result = cli_runner.invoke(main, cli_cmd[1:]) + + # Evaluate + assert_successful_exit_code(result, cli_cmd) + assert post_mocker.call_count == 1 + assert post_mocker.last_request is not None + request_body = post_mocker.last_request.json() + + assert "body" in request_body + actual_new_posted_notes = request_body["body"] + + assert expected_release_notes == actual_new_posted_notes + + # Generate the previous release notes + cli_cmd = [ + MAIN_PROG_NAME, + CHANGELOG_SUBCMD, + "--post-to-release-tag", + prev_release_tag, + ] + result = cli_runner.invoke(main, cli_cmd[1:]) + + # Evaluate + assert_successful_exit_code(result, cli_cmd) + assert post_mocker.call_count == 2 + assert post_mocker.last_request is not None + request_body = post_mocker.last_request.json() + + assert "body" in request_body + actual_prev_posted_notes = request_body["body"] + + assert expected_prev_release_notes == actual_prev_posted_notes + + assert actual_prev_posted_notes != actual_new_posted_notes diff --git a/tests/e2e/cmd_version/test_version_release_notes.py b/tests/e2e/cmd_version/test_version_release_notes.py index 562bd88ca..36fdf0e63 100644 --- a/tests/e2e/cmd_version/test_version_release_notes.py +++ b/tests/e2e/cmd_version/test_version_release_notes.py @@ -9,8 +9,17 @@ from semantic_release.cli.commands.main import main from semantic_release.version.version import Version -from tests.const import EXAMPLE_RELEASE_NOTES_TEMPLATE, MAIN_PROG_NAME, VERSION_SUBCMD +from tests.const import ( + EXAMPLE_RELEASE_NOTES_TEMPLATE, + MAIN_PROG_NAME, + VERSION_SUBCMD, + RepoActionStep, +) from tests.fixtures.repos import repo_w_no_tags_angular_commits +from tests.fixtures.repos.trunk_based_dev.repo_w_no_tags import ( + repo_w_no_tags_emoji_commits, + repo_w_no_tags_scipy_commits, +) from tests.util import assert_successful_exit_code, get_release_history_from_context if TYPE_CHECKING: @@ -19,9 +28,19 @@ from click.testing import CliRunner from requests_mock import Mocker - from tests.e2e.conftest import RetrieveRuntimeContextFn - from tests.fixtures.example_project import UseReleaseNotesTemplateFn - from tests.fixtures.git_repo import BuiltRepoResult + from tests.conftest import GetStableDateNowFn + from tests.e2e.conftest import ( + RetrieveRuntimeContextFn, + ) + from tests.fixtures.example_project import ( + UpdatePyprojectTomlFn, + UseReleaseNotesTemplateFn, + ) + from tests.fixtures.git_repo import ( + BuiltRepoResult, + GenerateDefaultReleaseNotesFromDefFn, + GetHvcsClientFromRepoDefFn, + ) @pytest.mark.parametrize( @@ -76,3 +95,81 @@ def test_custom_release_notes_template( actual_notes = post_mocker.last_request.json()["body"] assert expected_release_notes == actual_notes + + +@pytest.mark.parametrize( + "repo_result, license_name, license_setting", + [ + pytest.param( + lazy_fixture(repo_fixture_name), + license_name, + license_setting, + marks=pytest.mark.comprehensive, + ) + for license_name in ["", "MIT", "GPL-3.0"] + for license_setting in [ + "project.license-expression", + "project.license", # deprecated + "project.license.text", # deprecated + ] + for repo_fixture_name in [ + repo_w_no_tags_angular_commits.__name__, + repo_w_no_tags_emoji_commits.__name__, + repo_w_no_tags_scipy_commits.__name__, + ] + ], +) +def test_default_release_notes_license_statement( + repo_result: BuiltRepoResult, + cli_runner: CliRunner, + license_name: str, + license_setting: str, + update_pyproject_toml: UpdatePyprojectTomlFn, + mocked_git_push: MagicMock, + post_mocker: Mocker, + stable_now_date: GetStableDateNowFn, + get_hvcs_client_from_repo_def: GetHvcsClientFromRepoDefFn, + generate_default_release_notes_from_def: GenerateDefaultReleaseNotesFromDefFn, +): + new_version = "0.1.0" + + # Setup + repo_def = list(repo_result["definition"]) + repo_def.append( + { + "action": RepoActionStep.RELEASE, + "details": { + "version": new_version, + "datetime": stable_now_date().isoformat(timespec="seconds"), + }, + } + ) + # Setup: Overwrite the default setting (defined in test.const) + update_pyproject_toml("project.license-expression", None) + + # Setup: set the license for the test + update_pyproject_toml(license_setting, license_name) + + expected_release_notes = generate_default_release_notes_from_def( + version_actions=repo_def, + hvcs=get_hvcs_client_from_repo_def(repo_def), + previous_version=None, + license_name=license_name, + mask_initial_release=False, + ) + + # Act + cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--no-changelog", "--vcs-release"] + result = cli_runner.invoke(main, cli_cmd[1:]) + + # Evaluate + assert_successful_exit_code(result, cli_cmd) + assert mocked_git_push.call_count == 2 # 1 for commit, 1 for tag + assert post_mocker.call_count == 1 + assert post_mocker.last_request is not None + request_body = post_mocker.last_request.json() + + assert "body" in request_body + actual_notes = request_body["body"] + + assert expected_release_notes == actual_notes diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index 2d9783cdf..b1dc9858d 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -414,7 +414,11 @@ def _update_pyproject_toml(setting: str, value: Any) -> None: if pointer.get(part, None) is None: pointer.add(part, tomlkit.table()) pointer = pointer.get(part, {}) - pointer.update(new_setting) + + if value is None: + pointer.pop(new_setting_key) + else: + pointer.update(new_setting) with open(cwd_pyproject_toml, "w") as wfd: tomlkit.dump(pyproject_toml, wfd) diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 8a99ba261..c5f224c8c 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -1,5 +1,6 @@ from __future__ import annotations +import os import sys from copy import deepcopy from datetime import datetime, timedelta @@ -8,6 +9,7 @@ from textwrap import dedent from time import sleep from typing import TYPE_CHECKING +from unittest import mock import pytest from git import Actor, Repo @@ -19,6 +21,10 @@ ) from semantic_release.commit_parser.emoji import EmojiCommitParser, EmojiParserOptions from semantic_release.commit_parser.scipy import ScipyCommitParser, ScipyParserOptions +from semantic_release.hvcs.bitbucket import Bitbucket +from semantic_release.hvcs.gitea import Gitea +from semantic_release.hvcs.github import Github +from semantic_release.hvcs.gitlab import Gitlab from semantic_release.version.version import Version import tests.conftest @@ -53,10 +59,6 @@ from typing_extensions import NotRequired from semantic_release.hvcs import HvcsBase - from semantic_release.hvcs.bitbucket import Bitbucket - from semantic_release.hvcs.gitea import Gitea - from semantic_release.hvcs.github import Github - from semantic_release.hvcs.gitlab import Gitlab from tests.conftest import ( BuildRepoOrCopyCacheFn, @@ -162,7 +164,9 @@ def __call__(self) -> list[VersionStr]: ... class GetCommitsFromRepoBuildDefFn(Protocol): def __call__( - self, build_definition: Sequence[RepoActions] + self, + build_definition: Sequence[RepoActions], + filter_4_changelog: bool = False, ) -> RepoDefinition: ... RepoDefinition: TypeAlias = dict[VersionStr, RepoVersionDef] # type: ignore[misc] # mypy is thoroughly confused @@ -391,6 +395,23 @@ def __call__( class SeparateSquashedCommitDefFn(Protocol): def __call__(self, squashed_commit_def: CommitDef) -> list[CommitDef]: ... + class GenerateDefaultReleaseNotesFromDefFn(Protocol): + def __call__( + self, + version_actions: Sequence[RepoActions], + hvcs: Github | Gitlab | Gitea | Bitbucket, + previous_version: Version | None = None, + license_name: str = "", + dest_file: Path | None = None, + mask_initial_release: bool = False, + ) -> str: ... + + class GetHvcsClientFromRepoDefFn(Protocol): + def __call__( + self, + repo_def: Sequence[RepoActions], + ) -> Github | Gitlab | Gitea | Bitbucket: ... + @pytest.fixture(scope="session") def deps_files_4_example_git_project( @@ -926,6 +947,34 @@ def _simulate_change_commits_n_rtn_changelog_entry( return _simulate_change_commits_n_rtn_changelog_entry +@pytest.fixture(scope="session") +def get_hvcs_client_from_repo_def( + example_git_https_url: str, + get_cfg_value_from_def: GetCfgValueFromDefFn, +) -> GetHvcsClientFromRepoDefFn: + hvcs_client_classes = { + Bitbucket.__name__.lower(): Bitbucket, + Github.__name__.lower(): Github, + Gitea.__name__.lower(): Gitea, + Gitlab.__name__.lower(): Gitlab, + } + + def _get_hvcs_client_from_repo_def( + repo_def: Sequence[RepoActions], + ) -> Github | Gitlab | Gitea | Bitbucket: + hvcs_type = get_cfg_value_from_def(repo_def, "hvcs_client_name") + hvcs_client_class = hvcs_client_classes[hvcs_type] + + # Prevent the HVCS client from using the environment variables + with mock.patch.dict(os.environ, {}, clear=True): + return hvcs_client_class( + example_git_https_url, + hvcs_domain=get_cfg_value_from_def(repo_def, "hvcs_domain"), + ) + + return _get_hvcs_client_from_repo_def + + @pytest.fixture(scope="session") def build_configured_base_repo( # noqa: C901 cached_example_git_project: Path, @@ -1258,6 +1307,9 @@ def _build_repo_from_definition( # noqa: C901, its required and its just test c # Save configuration details for later steps mask_initial_release = cfg_def["mask_initial_release"] + # Make sure the resulting build definition is complete with the default + cfg_def["tag_format_str"] = tag_format_str + elif action == RepoActionStep.MAKE_COMMITS: mk_cmts_def: RepoActionMakeCommitsDetails = step_result["details"] # type: ignore[assignment] @@ -1432,9 +1484,19 @@ def _get_commits( ) commits.extend(commits_made) - elif build_step["action"] == RepoActionStep.GIT_MERGE: + elif any( + ( + build_step["action"] == RepoActionStep.GIT_SQUASH, + build_step["action"] == RepoActionStep.GIT_MERGE, + ) + ): if "commit_def" in build_step["details"]: - commits.append(build_step["details"]["commit_def"]) # type: ignore[typeddict-item] + commit_def = build_step["details"]["commit_def"] # type: ignore[typeddict-item] + + if filter_4_changelog and not commit_def["include_in_changelog"]: + continue + + commits.append(commit_def) elif build_step["action"] == RepoActionStep.RELEASE: version = build_step["details"]["version"] @@ -1894,6 +1956,202 @@ def _mimic_semantic_release_default_changelog( return _mimic_semantic_release_default_changelog +@pytest.fixture(scope="session") +def generate_default_release_notes_from_def( # noqa: C901 + today_date_str: str, + get_commits_from_repo_build_def: GetCommitsFromRepoBuildDefFn, +) -> GenerateDefaultReleaseNotesFromDefFn: + def build_version_entry_markdown( + version: VersionStr, + version_def: RepoVersionDef, + hvcs: Github | Gitlab | Gitea | Bitbucket, + license_name: str, + ) -> str: + version_entry = [ + f"## v{version} ({today_date_str})", + *( + [""] + if not license_name + else [ + "", + f"_This release is published under the {license_name} License._", + "", + ] + ), + ] + + changelog_sections = sorted( + {commit["category"] for commit in version_def["commits"]} + ) + + brking_descriptions = [] + + for section in changelog_sections: + # Create Markdown section heading + section_title = section.title() if not section.startswith(":") else section + version_entry.append(f"### {section_title}\n") + + commits: list[CommitDef] = list( + filter( + lambda commit, section=section: ( # type: ignore[arg-type] + commit["category"] == section + ), + version_def["commits"], + ) + ) + + section_bullets = [] + + # format each commit + for commit_def in commits: + descriptions = commit_def["desc"].split("\n\n") + if commit_def["brking_desc"]: + brking_descriptions.append( + "- {commit_scope}{brk_desc}".format( + commit_scope=( + f"**{commit_def['scope']}**: " + if commit_def["scope"] + else "" + ), + brk_desc=commit_def["brking_desc"].capitalize(), + ) + ) + + # NOTE: During release notes, we make the line length very large as the VCS + # will handle the line wrapping for us so here we don't have to worry about it + max_line_length = 1000 + + subject_line = "- {commit_scope}{commit_desc}".format( + commit_desc=descriptions[0].capitalize(), + commit_scope=( + f"**{commit_def['scope']}**: " if commit_def["scope"] else "" + ), + ) + + mr_link = ( + "" + if not commit_def["mr"] + else "([{mr}]({mr_url}),".format( + mr=commit_def["mr"], + mr_url=hvcs.pull_request_url(commit_def["mr"]), + ) + ) + + sha_link = "[`{short_sha}`]({commit_url}))".format( + short_sha=commit_def["sha"][:7], + commit_url=hvcs.commit_hash_url(commit_def["sha"]), + ) + # Add opening parenthesis if no MR link + sha_link = sha_link if mr_link else f"({sha_link}" + + commit_cl_desc = f"{subject_line} {mr_link}".rstrip() + if len(commit_cl_desc) > max_line_length: + commit_cl_desc = f"{subject_line}\n {mr_link}".rstrip() + + if len(f"{commit_cl_desc} {sha_link}") > max_line_length: + commit_cl_desc = f"{commit_cl_desc}\n {sha_link}\n" + else: + commit_cl_desc = f"{commit_cl_desc} {sha_link}\n" + + # NOTE: remove this when we no longer are writing the whole commit msg (squash commits enabled) + # if len(descriptions) > 1: + # commit_cl_desc += ( + # "\n" + str.join("\n\n", [*descriptions[1:]]) + "\n" + # ) + + # Add commits to section + section_bullets.append(commit_cl_desc) + + version_entry.extend(sorted(section_bullets)) + + # Add breaking changes to the end of the version entry + if brking_descriptions: + version_entry.append("### BREAKING CHANGES\n") + version_entry.extend([*sorted(brking_descriptions), ""]) + + return str.join("\n", version_entry) + + def build_initial_version_entry_markdown( + version: VersionStr, + license_name: str = "", + ) -> str: + return str.join( + "\n", + [ + f"## v{version} ({today_date_str})", + *( + [""] + if not license_name + else [ + "", + f"_This release is published under the {license_name} License._", + "", + ] + ), + "- Initial Release", + "", + ], + ) + + def _generate_default_release_notes( + version_actions: Sequence[RepoActions], + hvcs: Github | Gitlab | Gitea | Bitbucket, + previous_version: Version | None = None, + license_name: str = "", + dest_file: Path | None = None, + # TODO: Breaking v10, when default is toggled to true, also change this to True + mask_initial_release: bool = False, + ) -> str: + limited_repo_def: RepoDefinition = get_commits_from_repo_build_def( + build_definition=version_actions, + filter_4_changelog=True, + ) + version: Version = Version.parse(next(iter(limited_repo_def.keys()))) + version_def: RepoVersionDef = limited_repo_def[str(version)] + + release_notes_content = ( + str.join( + "\n" * 2, + [ + ( + build_initial_version_entry_markdown(str(version), license_name) + if mask_initial_release and not previous_version + else build_version_entry_markdown( + str(version), version_def, hvcs, license_name + ) + ).rstrip(), + *( + [ + "---", + "**Detailed Changes**: [{prev_version}...{new_version}]({version_compare_url})".format( + prev_version=previous_version.as_tag(), + new_version=version.as_tag(), + version_compare_url=hvcs.compare_url( + previous_version.as_tag(), version.as_tag() + ), + ), + ] + if previous_version and not isinstance(hvcs, Gitea) + else [] + ), + ], + ).rstrip() + + "\n" + ) + + if dest_file is not None: + # Converts universal newlines to the OS-specific upon write + dest_file.write_text(release_notes_content) + + # match the line endings of the current OS + return ( + str.join(os.linesep, release_notes_content.splitlines(keepends=False)) + + os.linesep + ) + + return _generate_default_release_notes + + @pytest.fixture def git_repo_for_directory() -> Generator[GetGitRepo4DirFn, None, None]: repos: list[Repo] = [] diff --git a/tests/unit/semantic_release/changelog/test_release_notes.py b/tests/unit/semantic_release/changelog/test_release_notes.py index 62c909ba8..f0c959c27 100644 --- a/tests/unit/semantic_release/changelog/test_release_notes.py +++ b/tests/unit/semantic_release/changelog/test_release_notes.py @@ -32,10 +32,12 @@ def release_notes_template() -> str: @pytest.mark.parametrize("mask_initial_release", [True, False]) +@pytest.mark.parametrize("license_name", ["", "MIT"]) @pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea, Bitbucket]) def test_default_release_notes_template( example_git_https_url: str, hvcs_client: type[Github | Gitlab | Gitea | Bitbucket], + license_name: str, artificial_release_history: ReleaseHistory, mask_initial_release: bool, today_date_str: str, @@ -76,7 +78,15 @@ def test_default_release_notes_template( os.linesep, [ f"## v{version} ({today_date_str})", - "", + *( + [""] + if not license_name + else [ + "", + f"_This release is published under the {license_name} License._", + "", + ] + ), "### Feature", "", "- {commit_scope}{commit_desc} ([`{short_hash}`]({url}))".format( @@ -148,6 +158,7 @@ def test_default_release_notes_template( history=artificial_release_history, style="angular", mask_initial_release=mask_initial_release, + license_name=license_name, ) assert expected_content == actual_content @@ -365,10 +376,12 @@ def test_default_release_notes_template_w_multiple_brk_changes( assert expected_content == actual_content +@pytest.mark.parametrize("license_name", ["", "MIT"]) @pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea, Bitbucket]) def test_default_release_notes_template_first_release_masked( example_git_https_url: str, hvcs_client: type[Bitbucket | Gitea | Github | Gitlab], + license_name: str, single_release_history: ReleaseHistory, today_date_str: str, ): @@ -385,7 +398,15 @@ def test_default_release_notes_template_first_release_masked( os.linesep, [ f"## v{version} ({today_date_str})", - "", + *( + [""] + if not license_name + else [ + "", + f"_This release is published under the {license_name} License._", + "", + ] + ), "- Initial Release", "", ], @@ -398,15 +419,18 @@ def test_default_release_notes_template_first_release_masked( history=single_release_history, style="angular", mask_initial_release=True, + license_name=license_name, ) assert expected_content == actual_content +@pytest.mark.parametrize("license_name", ["", "MIT"]) @pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea, Bitbucket]) def test_default_release_notes_template_first_release_unmasked( example_git_https_url: str, hvcs_client: type[Bitbucket | Gitea | Github | Gitlab], + license_name: str, single_release_history: ReleaseHistory, today_date_str: str, ): @@ -429,7 +453,15 @@ def test_default_release_notes_template_first_release_unmasked( os.linesep, [ f"## v{version} ({today_date_str})", - "", + *( + [""] + if not license_name + else [ + "", + f"_This release is published under the {license_name} License._", + "", + ] + ), "### Feature", "", "- {commit_scope}{commit_desc} ([`{short_hash}`]({url}))".format( @@ -451,6 +483,7 @@ def test_default_release_notes_template_first_release_unmasked( history=single_release_history, style="angular", mask_initial_release=False, + license_name=license_name, ) assert expected_content == actual_content From 433f4c90a7464fb1787b8a34d0d9a0b457849d8a Mon Sep 17 00:00:00 2001 From: semantic-release Date: Wed, 5 Feb 2025 06:01:48 +0000 Subject: [PATCH 027/160] 9.18.0 Automatically generated by python-semantic-release --- CHANGELOG.rst | 111 +++++++++++++++++++++ docs/automatic-releases/github-actions.rst | 14 +-- docs/changelog_templates.rst | 8 +- docs/commit_parsing.rst | 6 +- pyproject.toml | 2 +- src/semantic_release/__init__.py | 2 +- 6 files changed, 127 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 02b76ffcd..26e5edf5d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,117 @@ CHANGELOG ========= +.. _changelog-v9.18.0: + +v9.18.0 (2025-02-05) +==================== + +✨ Features +----------- + +* Add ``create_release_url`` & ``format_w_official_vcs_name`` filters (`PR#1161`_, `f853cf0`_) + +* **changelog**: Add ``create_pypi_url`` filter to jinja template render context (`PR#1160`_, + `45d49c3`_) + +* **changelog**: Add additional release info to changeling from commit ``NOTICE``'s (`PR#1166`_, + `834ce32`_) + +* **changelog-md**: Add additional release info section to default markdown template, closes `#223`_ + (`PR#1166`_, `834ce32`_) + +* **changelog-rst**: Add additional release info section to default ReStructuredText template, + closes `#223`_ (`PR#1166`_, `834ce32`_) + +* **commit-parser**: Enable parsers to identify additional release notices from commit msgs + (`PR#1166`_, `834ce32`_) + +* **parser-angular**: Add a ``ignore_merge_commits`` option to discard parsing merge commits + (`PR#1164`_, `463e43b`_) + +* **parser-angular**: Add functionality to parse out ``NOTICE:`` prefixed statements in commits, + closes `#223`_ (`PR#1166`_, `834ce32`_) + +* **parser-emoji**: Add a ``ignore_merge_commits`` option to discard parsing merge commits + (`PR#1164`_, `463e43b`_) + +* **parser-emoji**: Add functionality to parse out ``NOTICE:`` prefixed statements in commits, + closes `#223`_ (`PR#1166`_, `834ce32`_) + +* **parsers**: Add option ``ignore_merge_commits`` to discard parsing merge commits (`PR#1164`_, + `463e43b`_) + +* **release-notes**: Add license information to default release notes template, closes `#228`_ + (`PR#1167`_, `41172c1`_) + +* **vcs-bitbucket**: Add ``format_w_official_vcs_name`` filter function (`PR#1161`_, `f853cf0`_) + +* **vcs-gitea**: Add ``create_release_url`` & ``format_w_official_vcs_name`` filter functions + (`PR#1161`_, `f853cf0`_) + +* **vcs-github**: Add ``create_release_url`` & ``format_w_official_vcs_name`` filter functions + (`PR#1161`_, `f853cf0`_) + +* **vcs-gitlab**: Add ``create_release_url`` & ``format_w_official_vcs_name`` filter functiions + (`PR#1161`_, `f853cf0`_) + +🪲 Bug Fixes +------------ + +* Refactor parsing compatibility function to support older custom parsers (`PR#1165`_, `cf340c5`_) + +* **changelog**: Fix parsing compatibility w/ custom parsers, closes `#1162`_ (`PR#1165`_, + `cf340c5`_) + +* **changelog-templates**: Adjust default templates to avoid empty version sections (`PR#1164`_, + `463e43b`_) + +* **parser-angular**: Adjust parser to prevent empty message extractions (`PR#1166`_, `834ce32`_) + +* **parser-emoji**: Adjust parser to prevent empty message extractions (`PR#1166`_, `834ce32`_) + +* **version**: Fix parsing compatibility w/ custom parsers, closes `#1162`_ (`PR#1165`_, `cf340c5`_) + +📖 Documentation +---------------- + +* **changelog**: Add formatted changelog into hosted documentation (`PR#1155`_, `2f18a6d`_) + +* **changelog-templates**: Add description for new ``create_pypi_url`` filter function (`PR#1160`_, + `45d49c3`_) + +* **changelog-templates**: Add details about license specification in the release notes (`PR#1167`_, + `41172c1`_) + +* **changelog-templates**: Define ``create_release_url`` & ``format_w_official_vcs_name`` filters + (`PR#1161`_, `f853cf0`_) + +* **changelog-templates**: Document special separate sections of commit descriptions (`ebb4c67`_) + +* **commit-parsing**: Document new release notice footer detection feature of built-in parsers + (`cd14e92`_) + +.. _#1162: https://github.com/python-semantic-release/python-semantic-release/issues/1162 +.. _#223: https://github.com/python-semantic-release/python-semantic-release/issues/223 +.. _#228: https://github.com/python-semantic-release/python-semantic-release/issues/228 +.. _2f18a6d: https://github.com/python-semantic-release/python-semantic-release/commit/2f18a6debfa6ef3afcc5611a3e09262998f2d4bf +.. _41172c1: https://github.com/python-semantic-release/python-semantic-release/commit/41172c1272a402e94e3c68571d013cbdcb5b9023 +.. _45d49c3: https://github.com/python-semantic-release/python-semantic-release/commit/45d49c3da75a7f08c86fc9bab5d232a9b37d9e72 +.. _463e43b: https://github.com/python-semantic-release/python-semantic-release/commit/463e43b897ee80dfaf7ce9d88d22ea8e652bcf55 +.. _834ce32: https://github.com/python-semantic-release/python-semantic-release/commit/834ce323007c58229abf115ef2016a348de9ee66 +.. _cd14e92: https://github.com/python-semantic-release/python-semantic-release/commit/cd14e9209d4e54f0876e737d1f802dded294a48c +.. _cf340c5: https://github.com/python-semantic-release/python-semantic-release/commit/cf340c5256dea58aedad71a6bdf50b17eee53d2f +.. _ebb4c67: https://github.com/python-semantic-release/python-semantic-release/commit/ebb4c67d46b86fdf79e32edf744a2ec2b09d6a93 +.. _f853cf0: https://github.com/python-semantic-release/python-semantic-release/commit/f853cf059b3323d7888b06fde09142184e7964e8 +.. _PR#1155: https://github.com/python-semantic-release/python-semantic-release/pull/1155 +.. _PR#1160: https://github.com/python-semantic-release/python-semantic-release/pull/1160 +.. _PR#1161: https://github.com/python-semantic-release/python-semantic-release/pull/1161 +.. _PR#1164: https://github.com/python-semantic-release/python-semantic-release/pull/1164 +.. _PR#1165: https://github.com/python-semantic-release/python-semantic-release/pull/1165 +.. _PR#1166: https://github.com/python-semantic-release/python-semantic-release/pull/1166 +.. _PR#1167: https://github.com/python-semantic-release/python-semantic-release/pull/1167 + + .. _changelog-v9.17.0: v9.17.0 (2025-01-26) diff --git a/docs/automatic-releases/github-actions.rst b/docs/automatic-releases/github-actions.rst index 792bfc95f..384df5e64 100644 --- a/docs/automatic-releases/github-actions.rst +++ b/docs/automatic-releases/github-actions.rst @@ -337,7 +337,7 @@ before the :ref:`version ` subcommand. .. code:: yaml - - uses: python-semantic-release/python-semantic-release@v9.17.0 + - uses: python-semantic-release/python-semantic-release@v9.18.0 with: root_options: "-vv --noop" @@ -576,7 +576,7 @@ before the :ref:`publish ` subcommand. .. code:: yaml - - uses: python-semantic-release/publish-action@v9.17.0 + - uses: python-semantic-release/publish-action@v9.18.0 with: root_options: "-vv --noop" @@ -684,7 +684,7 @@ to the GitHub Release Assets as well. - name: Action | Semantic Version Release id: release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.17.0 + uses: python-semantic-release/python-semantic-release@v9.18.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} git_committer_name: "github-actions" @@ -695,7 +695,7 @@ to the GitHub Release Assets as well. if: steps.release.outputs.released == 'true' - name: Publish | Upload to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.17.0 + uses: python-semantic-release/publish-action@v9.18.0 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -744,7 +744,7 @@ The equivalent GitHub Action configuration would be: - name: Action | Semantic Version Release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.17.0 + uses: python-semantic-release/python-semantic-release@v9.18.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} force: patch @@ -772,13 +772,13 @@ Publish Action. .. code:: yaml - name: Release Project 1 - uses: python-semantic-release/python-semantic-release@v9.17.0 + uses: python-semantic-release/python-semantic-release@v9.18.0 with: directory: ./project1 github_token: ${{ secrets.GITHUB_TOKEN }} - name: Release Project 2 - uses: python-semantic-release/python-semantic-release@v9.17.0 + uses: python-semantic-release/python-semantic-release@v9.18.0 with: directory: ./project2 github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/docs/changelog_templates.rst b/docs/changelog_templates.rst index e8193aa84..b9bc3f0e9 100644 --- a/docs/changelog_templates.rst +++ b/docs/changelog_templates.rst @@ -286,7 +286,7 @@ The default template provided by PSR will respect the will also add a comparison link to the previous release if one exists without customization. -As of ``${NEW_RELEASE_TAG}``, the default release notes will also include a statement to +As of ``v9.18.0``, the default release notes will also include a statement to declare which license the project was released under. PSR determines which license to declare based on the value of ``project.license-expression`` in the ``pyproject.toml`` file as defined in the `PEP 639`_ specification. @@ -689,7 +689,7 @@ The filters provided vary based on the VCS configured and available features: point to the specific version page. If no version is provided, the URL will point to the package page. - *Introduced in ${NEW_RELEASE_TAG}.* + *Introduced in v9.18.0.* **Example Usage:** @@ -709,7 +709,7 @@ The filters provided vary based on the VCS configured and available features: page on the remote vcs. This filter is useful when you want to link to the release page on the remote vcs. - *Introduced in ${NEW_RELEASE_TAG}.* + *Introduced in v9.18.0.* **Example Usage:** @@ -887,7 +887,7 @@ The filters provided vary based on the VCS configured and available features: display the proper name of the VCS type in a changelog or release notes. The filter supports three different replace formats: ``%s``, ``{}``, and ``{vcs_name}``. - *Introduced in ${NEW_RELEASE_TAG}.* + *Introduced in v9.18.0.* **Example Usage:** diff --git a/docs/commit_parsing.rst b/docs/commit_parsing.rst index 5dfcf8cbb..b77b358d8 100644 --- a/docs/commit_parsing.rst +++ b/docs/commit_parsing.rst @@ -118,7 +118,7 @@ logic in relation to how PSR's core features: :ref:`commit_parser-builtin-release_notice_footer_detection`, which is a custom extension to traditional `Angular Commit Style Guidelines`_ to use the ``NOTICE`` keyword as a git footer to document additional release information that is not considered a breaking change. - *Feature available in ${NEW_RELEASE_TAG}+.* + *Feature available in v9.18.0+.* **Limitations**: @@ -194,7 +194,7 @@ how PSR's core features: - **Release Notice Footer Detection**: This parser implements PSR's :ref:`commit_parser-builtin-release_notice_footer_detection`, which is a custom extension that uses the ``NOTICE`` keyword as a git footer to document additional release information - that is not considered a breaking change. *Feature available in ${NEW_RELEASE_TAG}+.* + that is not considered a breaking change. *Feature available in v9.18.0+.* If no commit parser options are provided via the configuration, the parser will use PSR's built-in :py:class:`defaults `. @@ -326,7 +326,7 @@ return an empty tuple. Common Release Notice Footer Detection """""""""""""""""""""""""""""""""""""" -*Introduced in ${NEW_RELEASE_TAG}** +*Introduced in v9.18.0** All of the PSR built-in parsers implement common release notice footer detection logic to identify and extract a ``NOTICE`` git trailer that documents any additional release diff --git a/pyproject.toml b/pyproject.toml index 87efb1502..1ea076196 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.17.0" +version = "9.18.0" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } diff --git a/src/semantic_release/__init__.py b/src/semantic_release/__init__.py index 8489a3e60..f64b8170c 100644 --- a/src/semantic_release/__init__.py +++ b/src/semantic_release/__init__.py @@ -24,7 +24,7 @@ tags_and_versions, ) -__version__ = "9.17.0" +__version__ = "9.18.0" __all__ = [ "CommitParser", From bf9b28456766f0f25fed9fc472966ed356d3dadf Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Wed, 5 Feb 2025 23:26:05 -0500 Subject: [PATCH 028/160] Revert "9.18.0" This reverts commit 433f4c90a7464fb1787b8a34d0d9a0b457849d8a. Revert commit because release failed unexpectedly in CI. Attempting release again. --- CHANGELOG.rst | 111 --------------------- docs/automatic-releases/github-actions.rst | 14 +-- docs/changelog_templates.rst | 8 +- docs/commit_parsing.rst | 6 +- pyproject.toml | 2 +- src/semantic_release/__init__.py | 2 +- 6 files changed, 16 insertions(+), 127 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 26e5edf5d..02b76ffcd 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,117 +4,6 @@ CHANGELOG ========= -.. _changelog-v9.18.0: - -v9.18.0 (2025-02-05) -==================== - -✨ Features ------------ - -* Add ``create_release_url`` & ``format_w_official_vcs_name`` filters (`PR#1161`_, `f853cf0`_) - -* **changelog**: Add ``create_pypi_url`` filter to jinja template render context (`PR#1160`_, - `45d49c3`_) - -* **changelog**: Add additional release info to changeling from commit ``NOTICE``'s (`PR#1166`_, - `834ce32`_) - -* **changelog-md**: Add additional release info section to default markdown template, closes `#223`_ - (`PR#1166`_, `834ce32`_) - -* **changelog-rst**: Add additional release info section to default ReStructuredText template, - closes `#223`_ (`PR#1166`_, `834ce32`_) - -* **commit-parser**: Enable parsers to identify additional release notices from commit msgs - (`PR#1166`_, `834ce32`_) - -* **parser-angular**: Add a ``ignore_merge_commits`` option to discard parsing merge commits - (`PR#1164`_, `463e43b`_) - -* **parser-angular**: Add functionality to parse out ``NOTICE:`` prefixed statements in commits, - closes `#223`_ (`PR#1166`_, `834ce32`_) - -* **parser-emoji**: Add a ``ignore_merge_commits`` option to discard parsing merge commits - (`PR#1164`_, `463e43b`_) - -* **parser-emoji**: Add functionality to parse out ``NOTICE:`` prefixed statements in commits, - closes `#223`_ (`PR#1166`_, `834ce32`_) - -* **parsers**: Add option ``ignore_merge_commits`` to discard parsing merge commits (`PR#1164`_, - `463e43b`_) - -* **release-notes**: Add license information to default release notes template, closes `#228`_ - (`PR#1167`_, `41172c1`_) - -* **vcs-bitbucket**: Add ``format_w_official_vcs_name`` filter function (`PR#1161`_, `f853cf0`_) - -* **vcs-gitea**: Add ``create_release_url`` & ``format_w_official_vcs_name`` filter functions - (`PR#1161`_, `f853cf0`_) - -* **vcs-github**: Add ``create_release_url`` & ``format_w_official_vcs_name`` filter functions - (`PR#1161`_, `f853cf0`_) - -* **vcs-gitlab**: Add ``create_release_url`` & ``format_w_official_vcs_name`` filter functiions - (`PR#1161`_, `f853cf0`_) - -🪲 Bug Fixes ------------- - -* Refactor parsing compatibility function to support older custom parsers (`PR#1165`_, `cf340c5`_) - -* **changelog**: Fix parsing compatibility w/ custom parsers, closes `#1162`_ (`PR#1165`_, - `cf340c5`_) - -* **changelog-templates**: Adjust default templates to avoid empty version sections (`PR#1164`_, - `463e43b`_) - -* **parser-angular**: Adjust parser to prevent empty message extractions (`PR#1166`_, `834ce32`_) - -* **parser-emoji**: Adjust parser to prevent empty message extractions (`PR#1166`_, `834ce32`_) - -* **version**: Fix parsing compatibility w/ custom parsers, closes `#1162`_ (`PR#1165`_, `cf340c5`_) - -📖 Documentation ----------------- - -* **changelog**: Add formatted changelog into hosted documentation (`PR#1155`_, `2f18a6d`_) - -* **changelog-templates**: Add description for new ``create_pypi_url`` filter function (`PR#1160`_, - `45d49c3`_) - -* **changelog-templates**: Add details about license specification in the release notes (`PR#1167`_, - `41172c1`_) - -* **changelog-templates**: Define ``create_release_url`` & ``format_w_official_vcs_name`` filters - (`PR#1161`_, `f853cf0`_) - -* **changelog-templates**: Document special separate sections of commit descriptions (`ebb4c67`_) - -* **commit-parsing**: Document new release notice footer detection feature of built-in parsers - (`cd14e92`_) - -.. _#1162: https://github.com/python-semantic-release/python-semantic-release/issues/1162 -.. _#223: https://github.com/python-semantic-release/python-semantic-release/issues/223 -.. _#228: https://github.com/python-semantic-release/python-semantic-release/issues/228 -.. _2f18a6d: https://github.com/python-semantic-release/python-semantic-release/commit/2f18a6debfa6ef3afcc5611a3e09262998f2d4bf -.. _41172c1: https://github.com/python-semantic-release/python-semantic-release/commit/41172c1272a402e94e3c68571d013cbdcb5b9023 -.. _45d49c3: https://github.com/python-semantic-release/python-semantic-release/commit/45d49c3da75a7f08c86fc9bab5d232a9b37d9e72 -.. _463e43b: https://github.com/python-semantic-release/python-semantic-release/commit/463e43b897ee80dfaf7ce9d88d22ea8e652bcf55 -.. _834ce32: https://github.com/python-semantic-release/python-semantic-release/commit/834ce323007c58229abf115ef2016a348de9ee66 -.. _cd14e92: https://github.com/python-semantic-release/python-semantic-release/commit/cd14e9209d4e54f0876e737d1f802dded294a48c -.. _cf340c5: https://github.com/python-semantic-release/python-semantic-release/commit/cf340c5256dea58aedad71a6bdf50b17eee53d2f -.. _ebb4c67: https://github.com/python-semantic-release/python-semantic-release/commit/ebb4c67d46b86fdf79e32edf744a2ec2b09d6a93 -.. _f853cf0: https://github.com/python-semantic-release/python-semantic-release/commit/f853cf059b3323d7888b06fde09142184e7964e8 -.. _PR#1155: https://github.com/python-semantic-release/python-semantic-release/pull/1155 -.. _PR#1160: https://github.com/python-semantic-release/python-semantic-release/pull/1160 -.. _PR#1161: https://github.com/python-semantic-release/python-semantic-release/pull/1161 -.. _PR#1164: https://github.com/python-semantic-release/python-semantic-release/pull/1164 -.. _PR#1165: https://github.com/python-semantic-release/python-semantic-release/pull/1165 -.. _PR#1166: https://github.com/python-semantic-release/python-semantic-release/pull/1166 -.. _PR#1167: https://github.com/python-semantic-release/python-semantic-release/pull/1167 - - .. _changelog-v9.17.0: v9.17.0 (2025-01-26) diff --git a/docs/automatic-releases/github-actions.rst b/docs/automatic-releases/github-actions.rst index 384df5e64..792bfc95f 100644 --- a/docs/automatic-releases/github-actions.rst +++ b/docs/automatic-releases/github-actions.rst @@ -337,7 +337,7 @@ before the :ref:`version ` subcommand. .. code:: yaml - - uses: python-semantic-release/python-semantic-release@v9.18.0 + - uses: python-semantic-release/python-semantic-release@v9.17.0 with: root_options: "-vv --noop" @@ -576,7 +576,7 @@ before the :ref:`publish ` subcommand. .. code:: yaml - - uses: python-semantic-release/publish-action@v9.18.0 + - uses: python-semantic-release/publish-action@v9.17.0 with: root_options: "-vv --noop" @@ -684,7 +684,7 @@ to the GitHub Release Assets as well. - name: Action | Semantic Version Release id: release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.18.0 + uses: python-semantic-release/python-semantic-release@v9.17.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} git_committer_name: "github-actions" @@ -695,7 +695,7 @@ to the GitHub Release Assets as well. if: steps.release.outputs.released == 'true' - name: Publish | Upload to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.18.0 + uses: python-semantic-release/publish-action@v9.17.0 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -744,7 +744,7 @@ The equivalent GitHub Action configuration would be: - name: Action | Semantic Version Release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.18.0 + uses: python-semantic-release/python-semantic-release@v9.17.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} force: patch @@ -772,13 +772,13 @@ Publish Action. .. code:: yaml - name: Release Project 1 - uses: python-semantic-release/python-semantic-release@v9.18.0 + uses: python-semantic-release/python-semantic-release@v9.17.0 with: directory: ./project1 github_token: ${{ secrets.GITHUB_TOKEN }} - name: Release Project 2 - uses: python-semantic-release/python-semantic-release@v9.18.0 + uses: python-semantic-release/python-semantic-release@v9.17.0 with: directory: ./project2 github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/docs/changelog_templates.rst b/docs/changelog_templates.rst index b9bc3f0e9..e8193aa84 100644 --- a/docs/changelog_templates.rst +++ b/docs/changelog_templates.rst @@ -286,7 +286,7 @@ The default template provided by PSR will respect the will also add a comparison link to the previous release if one exists without customization. -As of ``v9.18.0``, the default release notes will also include a statement to +As of ``${NEW_RELEASE_TAG}``, the default release notes will also include a statement to declare which license the project was released under. PSR determines which license to declare based on the value of ``project.license-expression`` in the ``pyproject.toml`` file as defined in the `PEP 639`_ specification. @@ -689,7 +689,7 @@ The filters provided vary based on the VCS configured and available features: point to the specific version page. If no version is provided, the URL will point to the package page. - *Introduced in v9.18.0.* + *Introduced in ${NEW_RELEASE_TAG}.* **Example Usage:** @@ -709,7 +709,7 @@ The filters provided vary based on the VCS configured and available features: page on the remote vcs. This filter is useful when you want to link to the release page on the remote vcs. - *Introduced in v9.18.0.* + *Introduced in ${NEW_RELEASE_TAG}.* **Example Usage:** @@ -887,7 +887,7 @@ The filters provided vary based on the VCS configured and available features: display the proper name of the VCS type in a changelog or release notes. The filter supports three different replace formats: ``%s``, ``{}``, and ``{vcs_name}``. - *Introduced in v9.18.0.* + *Introduced in ${NEW_RELEASE_TAG}.* **Example Usage:** diff --git a/docs/commit_parsing.rst b/docs/commit_parsing.rst index b77b358d8..5dfcf8cbb 100644 --- a/docs/commit_parsing.rst +++ b/docs/commit_parsing.rst @@ -118,7 +118,7 @@ logic in relation to how PSR's core features: :ref:`commit_parser-builtin-release_notice_footer_detection`, which is a custom extension to traditional `Angular Commit Style Guidelines`_ to use the ``NOTICE`` keyword as a git footer to document additional release information that is not considered a breaking change. - *Feature available in v9.18.0+.* + *Feature available in ${NEW_RELEASE_TAG}+.* **Limitations**: @@ -194,7 +194,7 @@ how PSR's core features: - **Release Notice Footer Detection**: This parser implements PSR's :ref:`commit_parser-builtin-release_notice_footer_detection`, which is a custom extension that uses the ``NOTICE`` keyword as a git footer to document additional release information - that is not considered a breaking change. *Feature available in v9.18.0+.* + that is not considered a breaking change. *Feature available in ${NEW_RELEASE_TAG}+.* If no commit parser options are provided via the configuration, the parser will use PSR's built-in :py:class:`defaults `. @@ -326,7 +326,7 @@ return an empty tuple. Common Release Notice Footer Detection """""""""""""""""""""""""""""""""""""" -*Introduced in v9.18.0** +*Introduced in ${NEW_RELEASE_TAG}** All of the PSR built-in parsers implement common release notice footer detection logic to identify and extract a ``NOTICE`` git trailer that documents any additional release diff --git a/pyproject.toml b/pyproject.toml index 1ea076196..87efb1502 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.18.0" +version = "9.17.0" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } diff --git a/src/semantic_release/__init__.py b/src/semantic_release/__init__.py index f64b8170c..8489a3e60 100644 --- a/src/semantic_release/__init__.py +++ b/src/semantic_release/__init__.py @@ -24,7 +24,7 @@ tags_and_versions, ) -__version__ = "9.18.0" +__version__ = "9.17.0" __all__ = [ "CommitParser", From d33da260d1bda06d49bbc6440f45806e9bf8ddc9 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Thu, 6 Feb 2025 04:40:00 +0000 Subject: [PATCH 029/160] 9.18.0 Automatically generated by python-semantic-release --- CHANGELOG.rst | 111 +++++++++++++++++++++ docs/automatic-releases/github-actions.rst | 14 +-- docs/changelog_templates.rst | 8 +- docs/commit_parsing.rst | 6 +- pyproject.toml | 2 +- src/semantic_release/__init__.py | 2 +- 6 files changed, 127 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 02b76ffcd..8c23d7de1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,117 @@ CHANGELOG ========= +.. _changelog-v9.18.0: + +v9.18.0 (2025-02-06) +==================== + +✨ Features +----------- + +* Add ``create_release_url`` & ``format_w_official_vcs_name`` filters (`PR#1161`_, `f853cf0`_) + +* **changelog**: Add ``create_pypi_url`` filter to jinja template render context (`PR#1160`_, + `45d49c3`_) + +* **changelog**: Add additional release info to changeling from commit ``NOTICE``'s (`PR#1166`_, + `834ce32`_) + +* **changelog-md**: Add additional release info section to default markdown template, closes `#223`_ + (`PR#1166`_, `834ce32`_) + +* **changelog-rst**: Add additional release info section to default ReStructuredText template, + closes `#223`_ (`PR#1166`_, `834ce32`_) + +* **commit-parser**: Enable parsers to identify additional release notices from commit msgs + (`PR#1166`_, `834ce32`_) + +* **parser-angular**: Add a ``ignore_merge_commits`` option to discard parsing merge commits + (`PR#1164`_, `463e43b`_) + +* **parser-angular**: Add functionality to parse out ``NOTICE:`` prefixed statements in commits, + closes `#223`_ (`PR#1166`_, `834ce32`_) + +* **parser-emoji**: Add a ``ignore_merge_commits`` option to discard parsing merge commits + (`PR#1164`_, `463e43b`_) + +* **parser-emoji**: Add functionality to parse out ``NOTICE:`` prefixed statements in commits, + closes `#223`_ (`PR#1166`_, `834ce32`_) + +* **parsers**: Add option ``ignore_merge_commits`` to discard parsing merge commits (`PR#1164`_, + `463e43b`_) + +* **release-notes**: Add license information to default release notes template, closes `#228`_ + (`PR#1167`_, `41172c1`_) + +* **vcs-bitbucket**: Add ``format_w_official_vcs_name`` filter function (`PR#1161`_, `f853cf0`_) + +* **vcs-gitea**: Add ``create_release_url`` & ``format_w_official_vcs_name`` filter functions + (`PR#1161`_, `f853cf0`_) + +* **vcs-github**: Add ``create_release_url`` & ``format_w_official_vcs_name`` filter functions + (`PR#1161`_, `f853cf0`_) + +* **vcs-gitlab**: Add ``create_release_url`` & ``format_w_official_vcs_name`` filter functiions + (`PR#1161`_, `f853cf0`_) + +🪲 Bug Fixes +------------ + +* Refactor parsing compatibility function to support older custom parsers (`PR#1165`_, `cf340c5`_) + +* **changelog**: Fix parsing compatibility w/ custom parsers, closes `#1162`_ (`PR#1165`_, + `cf340c5`_) + +* **changelog-templates**: Adjust default templates to avoid empty version sections (`PR#1164`_, + `463e43b`_) + +* **parser-angular**: Adjust parser to prevent empty message extractions (`PR#1166`_, `834ce32`_) + +* **parser-emoji**: Adjust parser to prevent empty message extractions (`PR#1166`_, `834ce32`_) + +* **version**: Fix parsing compatibility w/ custom parsers, closes `#1162`_ (`PR#1165`_, `cf340c5`_) + +📖 Documentation +---------------- + +* **changelog**: Add formatted changelog into hosted documentation (`PR#1155`_, `2f18a6d`_) + +* **changelog-templates**: Add description for new ``create_pypi_url`` filter function (`PR#1160`_, + `45d49c3`_) + +* **changelog-templates**: Add details about license specification in the release notes (`PR#1167`_, + `41172c1`_) + +* **changelog-templates**: Define ``create_release_url`` & ``format_w_official_vcs_name`` filters + (`PR#1161`_, `f853cf0`_) + +* **changelog-templates**: Document special separate sections of commit descriptions (`ebb4c67`_) + +* **commit-parsing**: Document new release notice footer detection feature of built-in parsers + (`cd14e92`_) + +.. _#1162: https://github.com/python-semantic-release/python-semantic-release/issues/1162 +.. _#223: https://github.com/python-semantic-release/python-semantic-release/issues/223 +.. _#228: https://github.com/python-semantic-release/python-semantic-release/issues/228 +.. _2f18a6d: https://github.com/python-semantic-release/python-semantic-release/commit/2f18a6debfa6ef3afcc5611a3e09262998f2d4bf +.. _41172c1: https://github.com/python-semantic-release/python-semantic-release/commit/41172c1272a402e94e3c68571d013cbdcb5b9023 +.. _45d49c3: https://github.com/python-semantic-release/python-semantic-release/commit/45d49c3da75a7f08c86fc9bab5d232a9b37d9e72 +.. _463e43b: https://github.com/python-semantic-release/python-semantic-release/commit/463e43b897ee80dfaf7ce9d88d22ea8e652bcf55 +.. _834ce32: https://github.com/python-semantic-release/python-semantic-release/commit/834ce323007c58229abf115ef2016a348de9ee66 +.. _cd14e92: https://github.com/python-semantic-release/python-semantic-release/commit/cd14e9209d4e54f0876e737d1f802dded294a48c +.. _cf340c5: https://github.com/python-semantic-release/python-semantic-release/commit/cf340c5256dea58aedad71a6bdf50b17eee53d2f +.. _ebb4c67: https://github.com/python-semantic-release/python-semantic-release/commit/ebb4c67d46b86fdf79e32edf744a2ec2b09d6a93 +.. _f853cf0: https://github.com/python-semantic-release/python-semantic-release/commit/f853cf059b3323d7888b06fde09142184e7964e8 +.. _PR#1155: https://github.com/python-semantic-release/python-semantic-release/pull/1155 +.. _PR#1160: https://github.com/python-semantic-release/python-semantic-release/pull/1160 +.. _PR#1161: https://github.com/python-semantic-release/python-semantic-release/pull/1161 +.. _PR#1164: https://github.com/python-semantic-release/python-semantic-release/pull/1164 +.. _PR#1165: https://github.com/python-semantic-release/python-semantic-release/pull/1165 +.. _PR#1166: https://github.com/python-semantic-release/python-semantic-release/pull/1166 +.. _PR#1167: https://github.com/python-semantic-release/python-semantic-release/pull/1167 + + .. _changelog-v9.17.0: v9.17.0 (2025-01-26) diff --git a/docs/automatic-releases/github-actions.rst b/docs/automatic-releases/github-actions.rst index 792bfc95f..384df5e64 100644 --- a/docs/automatic-releases/github-actions.rst +++ b/docs/automatic-releases/github-actions.rst @@ -337,7 +337,7 @@ before the :ref:`version ` subcommand. .. code:: yaml - - uses: python-semantic-release/python-semantic-release@v9.17.0 + - uses: python-semantic-release/python-semantic-release@v9.18.0 with: root_options: "-vv --noop" @@ -576,7 +576,7 @@ before the :ref:`publish ` subcommand. .. code:: yaml - - uses: python-semantic-release/publish-action@v9.17.0 + - uses: python-semantic-release/publish-action@v9.18.0 with: root_options: "-vv --noop" @@ -684,7 +684,7 @@ to the GitHub Release Assets as well. - name: Action | Semantic Version Release id: release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.17.0 + uses: python-semantic-release/python-semantic-release@v9.18.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} git_committer_name: "github-actions" @@ -695,7 +695,7 @@ to the GitHub Release Assets as well. if: steps.release.outputs.released == 'true' - name: Publish | Upload to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.17.0 + uses: python-semantic-release/publish-action@v9.18.0 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -744,7 +744,7 @@ The equivalent GitHub Action configuration would be: - name: Action | Semantic Version Release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.17.0 + uses: python-semantic-release/python-semantic-release@v9.18.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} force: patch @@ -772,13 +772,13 @@ Publish Action. .. code:: yaml - name: Release Project 1 - uses: python-semantic-release/python-semantic-release@v9.17.0 + uses: python-semantic-release/python-semantic-release@v9.18.0 with: directory: ./project1 github_token: ${{ secrets.GITHUB_TOKEN }} - name: Release Project 2 - uses: python-semantic-release/python-semantic-release@v9.17.0 + uses: python-semantic-release/python-semantic-release@v9.18.0 with: directory: ./project2 github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/docs/changelog_templates.rst b/docs/changelog_templates.rst index e8193aa84..b9bc3f0e9 100644 --- a/docs/changelog_templates.rst +++ b/docs/changelog_templates.rst @@ -286,7 +286,7 @@ The default template provided by PSR will respect the will also add a comparison link to the previous release if one exists without customization. -As of ``${NEW_RELEASE_TAG}``, the default release notes will also include a statement to +As of ``v9.18.0``, the default release notes will also include a statement to declare which license the project was released under. PSR determines which license to declare based on the value of ``project.license-expression`` in the ``pyproject.toml`` file as defined in the `PEP 639`_ specification. @@ -689,7 +689,7 @@ The filters provided vary based on the VCS configured and available features: point to the specific version page. If no version is provided, the URL will point to the package page. - *Introduced in ${NEW_RELEASE_TAG}.* + *Introduced in v9.18.0.* **Example Usage:** @@ -709,7 +709,7 @@ The filters provided vary based on the VCS configured and available features: page on the remote vcs. This filter is useful when you want to link to the release page on the remote vcs. - *Introduced in ${NEW_RELEASE_TAG}.* + *Introduced in v9.18.0.* **Example Usage:** @@ -887,7 +887,7 @@ The filters provided vary based on the VCS configured and available features: display the proper name of the VCS type in a changelog or release notes. The filter supports three different replace formats: ``%s``, ``{}``, and ``{vcs_name}``. - *Introduced in ${NEW_RELEASE_TAG}.* + *Introduced in v9.18.0.* **Example Usage:** diff --git a/docs/commit_parsing.rst b/docs/commit_parsing.rst index 5dfcf8cbb..b77b358d8 100644 --- a/docs/commit_parsing.rst +++ b/docs/commit_parsing.rst @@ -118,7 +118,7 @@ logic in relation to how PSR's core features: :ref:`commit_parser-builtin-release_notice_footer_detection`, which is a custom extension to traditional `Angular Commit Style Guidelines`_ to use the ``NOTICE`` keyword as a git footer to document additional release information that is not considered a breaking change. - *Feature available in ${NEW_RELEASE_TAG}+.* + *Feature available in v9.18.0+.* **Limitations**: @@ -194,7 +194,7 @@ how PSR's core features: - **Release Notice Footer Detection**: This parser implements PSR's :ref:`commit_parser-builtin-release_notice_footer_detection`, which is a custom extension that uses the ``NOTICE`` keyword as a git footer to document additional release information - that is not considered a breaking change. *Feature available in ${NEW_RELEASE_TAG}+.* + that is not considered a breaking change. *Feature available in v9.18.0+.* If no commit parser options are provided via the configuration, the parser will use PSR's built-in :py:class:`defaults `. @@ -326,7 +326,7 @@ return an empty tuple. Common Release Notice Footer Detection """""""""""""""""""""""""""""""""""""" -*Introduced in ${NEW_RELEASE_TAG}** +*Introduced in v9.18.0** All of the PSR built-in parsers implement common release notice footer detection logic to identify and extract a ``NOTICE`` git trailer that documents any additional release diff --git a/pyproject.toml b/pyproject.toml index 87efb1502..1ea076196 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.17.0" +version = "9.18.0" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } diff --git a/src/semantic_release/__init__.py b/src/semantic_release/__init__.py index 8489a3e60..f64b8170c 100644 --- a/src/semantic_release/__init__.py +++ b/src/semantic_release/__init__.py @@ -24,7 +24,7 @@ tags_and_versions, ) -__version__ = "9.17.0" +__version__ = "9.18.0" __all__ = [ "CommitParser", From 28b32a9f3a5c817db39a7cd4a7bbe3e26cf2792e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Feb 2025 00:37:45 -0500 Subject: [PATCH 030/160] ci(deps): bump `python-semantic-release` & `tj-actions/changed-files` action (#1171) * ci(deps): bump `python-semantic-release/python-semantic-release@v9.17.0` action to 9.18.0 * ci(deps): bump `tj-actions/changed-files@v45.0.6` action to 45.0.7 --- .github/workflows/ci.yml | 4 ++-- .github/workflows/cicd.yml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 89619f2e4..36b693d01 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,13 +41,13 @@ jobs: - name: Evaluate | Check common file types for changes id: core-changed-files - uses: tj-actions/changed-files@v45.0.6 + uses: tj-actions/changed-files@v45.0.7 with: files_yaml_from_source_file: .github/changed-files-spec.yml - name: Evaluate | Check specific file types for changes id: ci-changed-files - uses: tj-actions/changed-files@v45.0.6 + uses: tj-actions/changed-files@v45.0.7 with: files_yaml: | ci: diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index abffd6fba..03c537ccc 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -26,14 +26,14 @@ jobs: - name: Evaluate | Check common file types for changes id: core-changed-files - uses: tj-actions/changed-files@v45.0.6 + uses: tj-actions/changed-files@v45.0.7 with: base_sha: ${{ github.event.push.before }} files_yaml_from_source_file: .github/changed-files-spec.yml - name: Evaluate | Check specific file types for changes id: ci-changed-files - uses: tj-actions/changed-files@v45.0.6 + uses: tj-actions/changed-files@v45.0.7 with: base_sha: ${{ github.event.push.before }} files_yaml: | @@ -138,7 +138,7 @@ jobs: build: false - name: Release | Add distribution artifacts to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.17.0 + uses: python-semantic-release/publish-action@v9.18.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} tag: ${{ steps.release.outputs.tag }} From 7bc25d198a8ca00e64f5a3008179a904e85c7027 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Thu, 6 Feb 2025 08:36:09 -0500 Subject: [PATCH 031/160] chore(release-notes): update template to order issue references numerically (#1172) --- config/release-templates/.release_notes.md.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/release-templates/.release_notes.md.j2 b/config/release-templates/.release_notes.md.j2 index 2fa05eecb..584269ead 100644 --- a/config/release-templates/.release_notes.md.j2 +++ b/config/release-templates/.release_notes.md.j2 @@ -71,7 +71,7 @@ _This release is published under the MIT License._ %}{% for linked_issues in issue_resolving_commits | map(attribute="linked_issues") %}{% set _ = issue_numbers.extend(linked_issues) %}{% endfor -%}{% for issue_num in issue_numbers | unique | sort +%}{% for issue_num in issue_numbers | unique | sort_numerically %}{{ "\n- %s: _Title_\n" | format(format_link(issue_num | issue_url, issue_num)) }}{# From 37db2581620ad02e66716a4b3b365aa28abe65f8 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 7 Feb 2025 19:08:04 -0500 Subject: [PATCH 032/160] fix(config): refactors default token resolution to prevent pre-mature insecure URL error (#1173) Due to the previous default token loading, PSR configuration load would initate the default RemoteConfig. During validation of the remote config, the HVCS object was initialized and upon initialization the URL is evaluated for insecure protocol causing errors when the URL was pulled from the env. This change removes the need for initialization which means the URL validation will not occur pre-maturely Resolves: #1169, #1074 --- src/semantic_release/cli/config.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/semantic_release/cli/config.py b/src/semantic_release/cli/config.py index 7e5c31131..cd2d1bf13 100644 --- a/src/semantic_release/cli/config.py +++ b/src/semantic_release/cli/config.py @@ -54,7 +54,6 @@ ParserLoadError, ) from semantic_release.helpers import dynamic_import -from semantic_release.hvcs.remote_hvcs_base import RemoteHvcsBase from semantic_release.version.declaration import ( PatternVersionDeclaration, TomlVersionDeclaration, @@ -287,15 +286,18 @@ def set_default_token(self) -> Self: def _get_default_token(self) -> str | None: hvcs_client_class = _known_hvcs[self.type] - default_hvcs_instance = hvcs_client_class("git@example.com:owner/project.git") - if not isinstance(default_hvcs_instance, RemoteHvcsBase): - return None - default_token_name = default_hvcs_instance.DEFAULT_ENV_TOKEN_NAME - if not default_token_name: - return None + default_token_name = ( + getattr(hvcs_client_class, "DEFAULT_ENV_TOKEN_NAME") # noqa: B009 + if hasattr(hvcs_client_class, "DEFAULT_ENV_TOKEN_NAME") + else "" + ) - return EnvConfigVar(env=default_token_name).getvalue() + return ( + EnvConfigVar(env=default_token_name).getvalue() + if default_token_name + else None + ) @model_validator(mode="after") def check_url_scheme(self) -> Self: From f1e7f858d7951a054daed67880136da814c86969 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Sat, 8 Feb 2025 00:20:43 +0000 Subject: [PATCH 033/160] 9.18.1 Automatically generated by python-semantic-release --- CHANGELOG.rst | 17 +++++++++++++++++ docs/automatic-releases/github-actions.rst | 14 +++++++------- pyproject.toml | 2 +- src/semantic_release/__init__.py | 2 +- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 8c23d7de1..fb1e8ebfb 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,23 @@ CHANGELOG ========= +.. _changelog-v9.18.1: + +v9.18.1 (2025-02-08) +==================== + +🪲 Bug Fixes +------------ + +* **config**: Refactors default token resolution to prevent pre-mature insecure URL error, closes + `#1074`_, `#1169`_ (`PR#1173`_, `37db258`_) + +.. _#1074: https://github.com/python-semantic-release/python-semantic-release/issues/1074 +.. _#1169: https://github.com/python-semantic-release/python-semantic-release/issues/1169 +.. _37db258: https://github.com/python-semantic-release/python-semantic-release/commit/37db2581620ad02e66716a4b3b365aa28abe65f8 +.. _PR#1173: https://github.com/python-semantic-release/python-semantic-release/pull/1173 + + .. _changelog-v9.18.0: v9.18.0 (2025-02-06) diff --git a/docs/automatic-releases/github-actions.rst b/docs/automatic-releases/github-actions.rst index 384df5e64..c8cfd4098 100644 --- a/docs/automatic-releases/github-actions.rst +++ b/docs/automatic-releases/github-actions.rst @@ -337,7 +337,7 @@ before the :ref:`version ` subcommand. .. code:: yaml - - uses: python-semantic-release/python-semantic-release@v9.18.0 + - uses: python-semantic-release/python-semantic-release@v9.18.1 with: root_options: "-vv --noop" @@ -576,7 +576,7 @@ before the :ref:`publish ` subcommand. .. code:: yaml - - uses: python-semantic-release/publish-action@v9.18.0 + - uses: python-semantic-release/publish-action@v9.18.1 with: root_options: "-vv --noop" @@ -684,7 +684,7 @@ to the GitHub Release Assets as well. - name: Action | Semantic Version Release id: release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.18.0 + uses: python-semantic-release/python-semantic-release@v9.18.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} git_committer_name: "github-actions" @@ -695,7 +695,7 @@ to the GitHub Release Assets as well. if: steps.release.outputs.released == 'true' - name: Publish | Upload to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.18.0 + uses: python-semantic-release/publish-action@v9.18.1 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -744,7 +744,7 @@ The equivalent GitHub Action configuration would be: - name: Action | Semantic Version Release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.18.0 + uses: python-semantic-release/python-semantic-release@v9.18.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} force: patch @@ -772,13 +772,13 @@ Publish Action. .. code:: yaml - name: Release Project 1 - uses: python-semantic-release/python-semantic-release@v9.18.0 + uses: python-semantic-release/python-semantic-release@v9.18.1 with: directory: ./project1 github_token: ${{ secrets.GITHUB_TOKEN }} - name: Release Project 2 - uses: python-semantic-release/python-semantic-release@v9.18.0 + uses: python-semantic-release/python-semantic-release@v9.18.1 with: directory: ./project2 github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/pyproject.toml b/pyproject.toml index 1ea076196..b3cbed3ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.18.0" +version = "9.18.1" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } diff --git a/src/semantic_release/__init__.py b/src/semantic_release/__init__.py index f64b8170c..9e823e6de 100644 --- a/src/semantic_release/__init__.py +++ b/src/semantic_release/__init__.py @@ -24,7 +24,7 @@ tags_and_versions, ) -__version__ = "9.18.0" +__version__ = "9.18.1" __all__ = [ "CommitParser", From 154d17c1c810b58eeb14e64be0ab157c1380b5f6 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 7 Feb 2025 23:01:54 -0500 Subject: [PATCH 034/160] chore(config): set release branch to generate dev variants (#1174) --- pyproject.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index b3cbed3ce..65aa99d94 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -436,6 +436,11 @@ match = "^(feat|fix|perf)/.+" prerelease = true prerelease_token = "alpha" +[tool.semantic_release.branches.dev] +match = ".+" +prerelease = true +prerelease_token = "dev" + [tool.semantic_release.remote] type = "github" token = { env = "GH_TOKEN" } From 7b09eb04686f96d6226edcd85c9545955709ba0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Feb 2025 23:18:52 -0500 Subject: [PATCH 035/160] ci(deps): bump `python-semantic-release/publish-action@v9.18.0` to 9.18.1 (#1175) --- .github/workflows/cicd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 03c537ccc..8ad586c6a 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -138,7 +138,7 @@ jobs: build: false - name: Release | Add distribution artifacts to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.18.0 + uses: python-semantic-release/publish-action@v9.18.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} tag: ${{ steps.release.outputs.tag }} From 600777c465a87620579fc8ad15e394640a2d9faf Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 9 Feb 2025 09:26:50 -0700 Subject: [PATCH 036/160] chore(pr): add completion checklist to PR template (#1176) --- .github/PULL_REQUEST_TEMPLATE.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 88a8d5807..85b471448 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -20,7 +20,7 @@ also more difficult to evaluate for edge cases. @@ -29,3 +29,21 @@ mention the rationale here. ## How to Verify + + +--- + +## PR Completion Checklist + +- [ ] Reviewed & followed the [Contributor Guidelines](https://python-semantic-release.readthedocs.io/en/latest/contributing.html) + +- [ ] Changes Implemented & Validation pipeline succeeds + +- [ ] Commits follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) standard + and are separated into the proper commit type and scope (recommended order: test, build, feat/fix, docs) + +- [ ] Appropriate Unit tests added/updated + +- [ ] Appropriate End-to-End tests added/updated + +- [ ] Appropriate Documentation added/updated and syntax validated for sphinx build (see Contributor Guidelines) From 27ddf840f8c812361c60bac9cf0b110d401f33d6 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 9 Feb 2025 20:42:06 -0700 Subject: [PATCH 037/160] feat(parser-conventional): add official `conventional-commits` parser (#1177) NOTICE: The 'angular' commit parser has been renamed to 'conventional' to match the official conventional-commits standard for which the 'angular' parser has evolved into. Please update your configurations to specify 'conventional' as the 'commit_parser' value in place of 'angular'. The 'angular' type will be removed in v11. * test: refactor angular references to conventional * docs: update references to Angular parser to Conventional Commit Parser * chore(config): change psr parser over to conventional-commits * chore(psr-changelog-tpls): correct error preventing additional info section --- CONTRIBUTING.rst | 12 +- .../.components/changes.rst.j2 | 2 +- docs/changelog_templates.rst | 8 +- docs/commit_parsing.rst | 118 +++++++-- docs/configuration.rst | 5 +- docs/index.rst | 7 +- docs/migrating_from_v7.rst | 30 +-- pyproject.toml | 2 +- src/semantic_release/cli/config.py | 21 +- .../commit_parser/__init__.py | 4 + .../commit_parser/conventional.py | 31 +++ tests/const.py | 14 +- tests/e2e/cmd_changelog/test_changelog.py | 88 +++---- .../test_changelog_custom_parser.py | 12 +- .../cmd_changelog/test_changelog_parsing.py | 4 +- .../test_changelog_release_notes.py | 24 +- tests/e2e/cmd_config/test_generate_config.py | 8 +- tests/e2e/cmd_publish/test_publish.py | 8 +- .../git_flow/test_repo_1_channel.py | 4 +- .../git_flow/test_repo_2_channels.py | 4 +- .../git_flow/test_repo_3_channels.py | 8 +- .../git_flow/test_repo_4_channels.py | 4 +- .../github_flow/test_repo_1_channel.py | 4 +- .../github_flow/test_repo_2_channels.py | 4 +- .../trunk_based_dev/test_repo_trunk.py | 4 +- .../test_repo_trunk_dual_version_support.py | 4 +- ...runk_dual_version_support_w_prereleases.py | 4 +- .../test_repo_trunk_w_prereleases.py | 4 +- tests/e2e/cmd_version/test_version.py | 14 +- tests/e2e/cmd_version/test_version_build.py | 10 +- tests/e2e/cmd_version/test_version_bump.py | 234 +++++++++--------- .../e2e/cmd_version/test_version_changelog.py | 62 ++--- ...est_version_changelog_custom_commit_msg.py | 8 +- .../test_version_github_actions.py | 8 +- tests/e2e/cmd_version/test_version_print.py | 70 +++--- .../cmd_version/test_version_release_notes.py | 6 +- tests/e2e/cmd_version/test_version_stamp.py | 14 +- tests/e2e/cmd_version/test_version_strict.py | 6 +- tests/e2e/test_help.py | 8 +- tests/e2e/test_main.py | 24 +- tests/fixtures/commit_parsers.py | 40 +-- tests/fixtures/example_project.py | 14 +- tests/fixtures/git_repo.py | 45 ++-- .../git_flow/repo_w_1_release_channel.py | 32 +-- .../git_flow/repo_w_2_release_channels.py | 34 +-- .../git_flow/repo_w_3_release_channels.py | 38 +-- .../git_flow/repo_w_4_release_channels.py | 28 ++- .../github_flow/repo_w_default_release.py | 20 +- .../github_flow/repo_w_release_channels.py | 20 +- tests/fixtures/repos/repo_initial_commit.py | 4 +- .../repo_w_dual_version_support.py | 14 +- ...po_w_dual_version_support_w_prereleases.py | 18 +- .../repos/trunk_based_dev/repo_w_no_tags.py | 16 +- .../trunk_based_dev/repo_w_prereleases.py | 14 +- .../repos/trunk_based_dev/repo_w_tags.py | 14 +- .../changelog/test_release_history.py | 76 +++--- .../unit/semantic_release/cli/test_config.py | 14 +- .../{test_angular.py => test_conventional.py} | 126 +++++----- .../commit_parser/test_emoji.py | 8 +- .../commit_parser/test_scipy.py | 8 +- tests/util.py | 4 +- 61 files changed, 827 insertions(+), 666 deletions(-) create mode 100644 src/semantic_release/commit_parser/conventional.py rename tests/unit/semantic_release/commit_parser/{test_angular.py => test_conventional.py} (91%) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 27522ea0d..e546295cf 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -13,11 +13,15 @@ Commit messages ~~~~~~~~~~~~~~~ Since python-semantic-release is released with python-semantic-release we need the commit messages -to adhere to the `angular commit guidelines`_. If you are unsure how to describe the change correctly -just try and ask about it in your pr. If we think it should be something else or there is a -pull-request without tags we will help out in adding or changing them. +to adhere to the `Conventional Commits Specification`_. Although scopes are optional, scopes are +expected where applicable. Changes should be committed separately with the commit type they represent, +do not combine them all into one commit. -.. _angular commit guidelines: https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits +If you are unsure how to describe the change correctly just try and ask about it in your pr. If we +think it should be something else or there is a pull-request without tags we will help out in +adding or changing them. + +.. _Conventional Commits Specification: https://www.conventionalcommits.org/en/v1.0.0 Releases ~~~~~~~~ diff --git a/config/release-templates/.components/changes.rst.j2 b/config/release-templates/.components/changes.rst.j2 index a0c6e4037..ba9180e87 100644 --- a/config/release-templates/.components/changes.rst.j2 +++ b/config/release-templates/.components/changes.rst.j2 @@ -103,7 +103,7 @@ #}{% set breaking_commits = [] %}{% set notice_commits = [] %}{% for commits in commit_objects.values() -%}{% set valid_commits = commits | rejectattr("error", "defined") +%}{% set valid_commits = commits | rejectattr("error", "defined") | list %}{# # Filter out breaking change commits that have no breaking descriptions #}{% set _ = breaking_commits.extend( valid_commits | selectattr("breaking_descriptions.0") diff --git a/docs/changelog_templates.rst b/docs/changelog_templates.rst index b9bc3f0e9..08f5eec78 100644 --- a/docs/changelog_templates.rst +++ b/docs/changelog_templates.rst @@ -71,8 +71,8 @@ A common and *highly-recommended* configuration option is the :ref:`changelog.exclude_commit_patterns ` setting which allows the user to define regular expressions that will exclude commits from the changelog output. This is useful to filter out change messages that are not -relevant to your external consumers (ex. ``ci`` and ``test`` in the angular commit -convention) and only include the important changes that impact the consumer of your +relevant to your external consumers (ex. ``ci`` and ``test`` in the conventional commit +standard) and only include the important changes that impact the consumer of your software. Another important configuration option is the :ref:`changelog.mode ` @@ -228,7 +228,7 @@ Configuration Examples } } -3. Goal: Configure an initializing reStructuredText changelog with filtered angular +3. Goal: Configure an initializing reStructuredText changelog with filtered conventional commits patterns and merge commits within a custom config file ``releaserc.toml``. .. code:: toml @@ -282,7 +282,7 @@ for defining a custom template directory): semantic-release --noop changelog --post-to-release-tag v1.0.0 The default template provided by PSR will respect the -:ref:`` setting and +:ref:`config-changelog-default_templates-mask_initial_release` setting and will also add a comparison link to the previous release if one exists without customization. diff --git a/docs/commit_parsing.rst b/docs/commit_parsing.rst index b77b358d8..30cdf32b1 100644 --- a/docs/commit_parsing.rst +++ b/docs/commit_parsing.rst @@ -33,7 +33,7 @@ to handle your specific commit message style. features are added beyond the scope of the original commit message style guidelines. Other tools may not follow the same conventions as PSR's guideline extensions, so - if you plan to use any similar programs in tadem with PSR, you should be aware of the + if you plan to use any similar programs in tandem with PSR, you should be aware of the differences in feature support and fall back to the official format guidelines if necessary. @@ -48,27 +48,110 @@ Built-in Commit Parsers The following parsers are built in to Python Semantic Release: -- :ref:`AngularCommitParser ` +- :ref:`ConventionalCommitParser ` +- :ref:`AngularCommitParser ` *(deprecated in ${NEW_RELEASE_TAG})* - :ref:`EmojiCommitParser ` - :ref:`ScipyCommitParser ` - :ref:`TagCommitParser ` *(deprecated in v9.12.0)* ---- +.. _commit_parser-builtin-conventional: + +Conventional Commits Parser +""""""""""""""""""""""""""" + +*Introduced in ${NEW_RELEASE_TAG}* + +A parser that is designed to parse commits formatted according to the +`Conventional Commits Specification`_. The parser is implemented with the following +logic in relation to how PSR's core features: + +- **Version Bump Determination**: This parser extracts the commit type from the subject + line of the commit (the first line of a commit message). This type is matched against + the configuration mapping to determine the level bump for the specific commit. If the + commit type is not found in the configuration mapping, the commit is considered a + non-parsable commit and will return it as a ParseError object and ultimately a commit + of type ``"unknown"``. The configuration mapping contains lists of commit types that + correspond to the level bump for each commit type. Some commit types are still valid + do not trigger a level bump, such as ``"chore"`` or ``"docs"``. You can also configure + the default level bump + :ref:`commit_parser_options.default_level_bump ` if desired. + To trigger a major release, the commit message body must contain a paragraph that begins + with ``BREAKING CHANGE:``. This will override the level bump determined by the commit type. + +- **Changelog Generation**: PSR will group commits in the changelog by the commit type used + in the commit message. The commit type shorthand is converted to a more human-friendly + section heading and then used as the version section title of the changelog and release + notes. Under the section title, the parsed commit descriptions are listed out in full. If + the commit includes an optional scope, then the scope is prefixed on to the first line of + the commit description. If a commit has any breaking change prefixed paragraphs in the + commit message body, those paragraphs are separated out into a "Breaking Changes" section + in the changelog (Breaking Changes section is available from the default changelog in + v9.15.0). Each breaking change paragraph is listed in a bulleted list format across the + entire version. A single commit is allowed to have more than one breaking change + prefixed paragraph (as opposed to the `Conventional Commits Specification`_). Commits + with an optional scope and a breaking change will have the scope prefixed on to the + breaking change paragraph. Parsing errors will return a ParseError object and ultimately + a commit of type ``"unknown"``. Unknown commits are consolidated into an "Unknown" section + in the changelog by the default template. To remove unwanted commits from the changelog + that normally are placed in the "unknown" section, consider the use of the configuration + option :ref:`changelog.exclude_commit_patterns ` + to ignore those commit styles. + +- **Pull/Merge Request Identifier Detection**: This parser implements PSR's + :ref:`commit_parser-builtin-linked_merge_request_detection` to identify and extract + pull/merge request numbers. The parser will return a string value if a pull/merge + request number is found in the commit message. If no pull/merge request number is + found, the parser will return an empty string. + +- **Linked Issue Identifier Detection**: This parser implements PSR's + :ref:`commit_parser-builtin-issue_number_detection` to identify and extract issue numbers. + The parser will return a tuple of issue numbers as strings if any are found in the commit + message. If no issue numbers are found, the parser will return an empty tuple. + +- **Squash Commit Evaluation**: This parser implements PSR's + :ref:`commit_parser-builtin-squash_commit_evaluation` to identify and extract each commit + message as a separate commit message within a single squashed commit. You can toggle this + feature on/off via the :ref:`config-commit_parser_options` setting. + +- **Release Notice Footer Detection**: This parser implements PSR's + :ref:`commit_parser-builtin-release_notice_footer_detection`, which is a custom extension + to traditional `Conventional Commits Specification`_ to use the ``NOTICE`` keyword as a git + footer to document additional release information that is not considered a breaking change. + +**Limitations**: + +- Commits with the ``revert`` type are not currently supported. Track the implementation + of this feature in the issue `#402`_. + +If no commit parser options are provided via the configuration, the parser will use PSR's +built-in +:py:class:`defaults `. + +.. _#402: https://github.com/python-semantic-release/python-semantic-release/issues/402 +.. _Conventional Commits Specification: https://www.conventionalcommits.org/en/v1.0.0 + +---- + .. _commit_parser-builtin-angular: Angular Commit Parser """"""""""""""""""""" +.. warning:: + This parser was deprecated in ``${NEW_RELEASE_TAG}``. It will be removed in a future release. + This parser is being replaced by the :ref:`commit_parser-builtin-conventional`. + A parser that is designed to parse commits formatted according to the `Angular Commit Style Guidelines`_. The parser is implemented with the following logic in relation to how PSR's core features: - **Version Bump Determination**: This parser extracts the commit type from the subject - line of the commit (the first line of a commit messsage). This type is matched against + line of the commit (the first line of a commit message). This type is matched against the configuration mapping to determine the level bump for the specific commit. If the commit type is not found in the configuration mapping, the commit is considered a - non-conformative commit and will return it as a ParseError object and ultimately a commit + non-parsable commit and will return it as a ParseError object and ultimately a commit of type ``"unknown"``. The configuration mapping contains lists of commit types that correspond to the level bump for each commit type. Some commit types are still valid do not trigger a level bump, such as ``"chore"`` or ``"docs"``. You can also configure @@ -129,10 +212,7 @@ If no commit parser options are provided via the configuration, the parser will built-in :py:class:`defaults `. .. _#402: https://github.com/python-semantic-release/python-semantic-release/issues/402 -.. _#733: https://github.com/python-semantic-release/python-semantic-release/issues/733 -.. _#1085: https://github.com/python-semantic-release/python-semantic-release/issues/1085 .. _Angular Commit Style Guidelines: https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits -.. _PR#1112: https://github.com/python-semantic-release/python-semantic-release/pull/1112 ---- @@ -151,7 +231,7 @@ commit messages. The parser is implemented with the following logic in relation how PSR's core features: - **Version Bump Determination**: This parser only looks for emojis in the subject - line of the commit (the first line of a commit messsage). If more than one emoji is + line of the commit (the first line of a commit message). If more than one emoji is found, the emoji configured with the highest priority is selected for the change impact for the specific commit. The emoji with the highest priority is the one configured in the ``major`` configuration option, followed by the ``minor``, and ``patch`` in descending @@ -209,7 +289,7 @@ Scipy Commit Parser """"""""""""""""""" A parser that is designed to parse commits formatted according to the -`Scipy Commit Style Guidlines`_. This is essentially a variation of the `Angular Commit Style +`Scipy Commit Style Guidelines`_. This is essentially a variation of the `Angular Commit Style Guidelines`_ with all different commit types. Because of this small variance, this parser only extends our :ref:`commit_parser-builtin-angular` parser with pre-defined scipy commit types in the default Scipy Parser Options and all other features are inherited. @@ -217,7 +297,7 @@ in the default Scipy Parser Options and all other features are inherited. If no commit parser options are provided via the configuration, the parser will use PSR's built-in :py:class:`defaults `. -.. _Scipy Commit Style Guidlines: https://scipy.github.io/devdocs/dev/contributor/development_workflow.html#writing-the-commit-message +.. _Scipy Commit Style Guidelines: https://scipy.github.io/devdocs/dev/contributor/development_workflow.html#writing-the-commit-message ---- @@ -246,7 +326,7 @@ Common Linked Merge Request Detection All of the PSR built-in parsers implement common pull/merge request identifier detection logic to extract pull/merge request numbers from the commit message regardless of the -VCS platform. The parsers evaluate the subject line for a paranthesis-enclosed number +VCS platform. The parsers evaluate the subject line for a parenthesis-enclosed number at the end of the line. PSR's parsers will return a string value if a pull/merge request number is found in the commit message. If no pull/merge request number is found, the parsers will return an empty string. @@ -291,7 +371,7 @@ for your VCS. PSR supports the following case-insensitive prefixes and their con - implement (implements, implementing, implemented) PSR also allows for a more flexible approach to identifying more than one issue number without -the need of extra git trailors (although PSR does support multiple git trailors). PSR support +the need of extra git trailers (although PSR does support multiple git trailers). PSR support various list formats which can be used to identify more than one issue in a list. This format will not necessarily work on your VCS. PSR currently support the following list formats: @@ -364,7 +444,7 @@ will be evaluated individually for both the level bump and changelog generation. squash commits are found, a list with the single commit object will be returned. Currently, PSR has been tested against GitHub, BitBucket, and official ``git`` squash -merge commmit messages. GitLab does not have a default template for squash commit messages +merge commit messages. GitLab does not have a default template for squash commit messages but can be customized per project or server. If you are using GitLab, you will need to ensure that the squash commit message format is similar to the example below. @@ -381,10 +461,10 @@ formatted squash commit message of conventional commit style:* * docs(configuration): defined new config option for the project -When parsed with the default angular parser with squash commits toggled on, the version -bump will be determined by the highest level bump of the three commits (in this case, a -minor bump because of the feature commit) and the release notes would look similar to -the following: +When parsed with the default conventional-commit parser with squash commits toggled on, +the version bump will be determined by the highest level bump of the three commits (in +this case, a minor bump because of the feature commit) and the release notes would look +similar to the following: .. code-block:: markdown @@ -582,7 +662,8 @@ A subclass must implement the following: :py:class:`ParseResult `, or a subclass of this. -By default, the constructor for :py:class:`CommitParser ` +By default, the constructor for +:py:class:`CommitParser ` will set the ``options`` parameter on the ``options`` attribute of the parser, so there is no need to override this in order to access ``self.options`` during the ``parse`` method. However, if you have any parsing logic that needs to be done only once, it may @@ -610,5 +691,4 @@ Therefore, a custom commit parser could be implemented via: def parse(self, commit: git.objects.commit.Commit) -> semantic_release.ParseResult: ... -.. _angular commit guidelines: https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits .. _gitpython-commit-object: https://gitpython.readthedocs.io/en/stable/reference.html#module-git.objects.commit diff --git a/docs/configuration.rst b/docs/configuration.rst index 884a28915..35a3b821f 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -790,7 +790,8 @@ Specify which commit parser Python Semantic Release should use to parse the comm within the Git repository. Built-in parsers: - * ``angular`` - :ref:`AngularCommitParser ` + * ``angular`` - :ref:`AngularCommitParser ` *(deprecated in ${NEW_RELEASE_TAG})* + * ``conventional`` - :ref:`ConventionalCommitParser ` *(available in ${NEW_RELEASE_TAG}+)* * ``emoji`` - :ref:`EmojiCommitParser ` * ``scipy`` - :ref:`ScipyCommitParser ` * ``tag`` - :ref:`TagCommitParser ` *(deprecated in v9.12.0)* @@ -800,7 +801,7 @@ your own commit parser in ``path/to/module_file.py:Class`` or ``module:Class`` f For more information see :ref:`commit-parsing`. -**Default:** ``"angular"`` +**Default:** ``"conventional"`` ---- diff --git a/docs/index.rst b/docs/index.rst index 2b2228939..16eaabec0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -140,9 +140,8 @@ Setting up commit parsing ------------------------- We rely on commit messages to detect when a version bump is needed. -By default, Python Semantic Release uses the -`Angular style `_. -You can find out more about this in :ref:`commit-parsing`. +By default, Python Semantic Release uses the `Conventional Commits Specification`_ +to parse commit messages. You can find out more about this in :ref:`commit-parsing`. .. seealso:: - :ref:`config-branches` - Adding configuration for releases from multiple branches. @@ -150,6 +149,8 @@ You can find out more about this in :ref:`commit-parsing`. For example, Python Semantic Release also ships with emoji and scipy-style parsers. - :ref:`remote.type ` - specify the type of your remote VCS. +.. _Conventional Commits Specification: https://www.conventionalcommits.org/en/v1.0.0 + Setting up the changelog ------------------------ diff --git a/docs/migrating_from_v7.rst b/docs/migrating_from_v7.rst index b69038035..be4cbc14a 100644 --- a/docs/migrating_from_v7.rst +++ b/docs/migrating_from_v7.rst @@ -8,7 +8,7 @@ The internals have been changed significantly to better support highly-requested features and to streamline the maintenance of the project. As a result, certain things have been removed, reimplemented differently, or now -exhibit different behaviour to earlier versions of Python Semantic Release. This +exhibit different behavior to earlier versions of Python Semantic Release. This page is a guide to help projects to ``pip install python-semantic-release>=8.0.0`` with fewer surprises. @@ -19,12 +19,12 @@ Python Semantic Release GitHub Action .. _breaking-removed-artefact-upload: -GitHub Action no longer publishes artefacts to PyPI or GitHub Releases +GitHub Action no longer publishes artifacts to PyPI or GitHub Releases """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" Python Semantic Release no longer uploads distributions to PyPI - see :ref:`breaking-commands-repurposed-version-and-publish`. If you are -using Python Semantic Release to publish release notes and artefacts to +using Python Semantic Release to publish release notes and artifacts to GitHub releases, there is a new GitHub Action `upload-to-gh-release`_ which will perform this action for you. @@ -143,7 +143,7 @@ Repurposing of ``version`` and ``publish`` commands """"""""""""""""""""""""""""""""""""""""""""""""""" Python Semantic Release's primary purpose is to enable automation of correct semantic versioning for software projects. Over the years, this automation has been extended to -include other actions such as building/publishing the project and its artefacts to +include other actions such as building/publishing the project and its artifacts to artefact repositories, creating releases in remote version control systems, and writing changelogs. @@ -152,9 +152,9 @@ performing every piece of automation provided. This has been changed - the ``ver command now handles determining the next version, applying the changes to the project metadata according to the configuration, writing a changelog, and committing/pushing changes to the remote Git repository. It also handles creating a release in the remote -VCS. It does *not* publish software artefacts to remote repositories such as PyPI; +VCS. It does *not* publish software artifacts to remote repositories such as PyPI; the rationale behind this decision is simply that under the hood, Python Semantic Release -used `twine`_ to upload artefacts to package indexes such as PyPI, and it's recommended +used `twine`_ to upload artifacts to package indexes such as PyPI, and it's recommended to use twine directly via the command-line. From the twine `documentation `_: @@ -162,7 +162,7 @@ to use twine directly via the command-line. From the twine As a result Python Semantic Release no longer depends on twine internals. -The ``publish`` command now handles publishing software artefacts to releases in the +The ``publish`` command now handles publishing software artifacts to releases in the remote version control system. .. _twine: https://twine.readthedocs.io/en/stable @@ -477,17 +477,17 @@ tag ``v1.1.4``, you should run:: semantic-release changelog --post-to-release-tag v1.1.4 -.. _breaking-changelog-customisation: +.. _breaking-changelog-customization: -Changelog customisation +Changelog customization """"""""""""""""""""""" -A number of options relevant to customising the changelog have been removed. This is +A number of options relevant to customizing the changelog have been removed. This is because Python Semantic Release now supports authoring a completely custom `Jinja`_ template with the contents of your changelog. Historically, the number of options added to Python Semantic Release in order to -allow this customisation has grown significantly; it now uses templates in order to -fully open up customising the changelog's appearance. +allow this customization has grown significantly; it now uses templates in order to +fully open up customizing the changelog's appearance. .. _Jinja: https://jinja.palletsprojects.com/en/3.1.x/ @@ -499,7 +499,7 @@ Configuration The configuration structure has been completely reworked, so you should read :ref:`configuration` carefully during the process of upgrading to v8+. However, -some common pitfalls and potential sources of confusion are summarised here. +some common pitfalls and potential sources of confusion are summarized here. .. _breaking-configuration-setup-cfg-unsupported: @@ -520,7 +520,7 @@ needs. .. warning:: If you don't already have a ``pyproject.toml`` configuration file, ``pip`` can - change its behaviour once you add one, as a result of `PEP-517`_. If you find + change its behavior once you add one, as a result of `PEP-517`_. If you find that this breaks your packaging, you can add your Python Semantic Release configuration to a separate file such as ``semantic-release.toml``, and use the :ref:`--config ` option to reference this alternative @@ -539,7 +539,7 @@ Commit parser options Options such as ``major_emoji``, ``parser_angular_patch_types`` or ``parser_angular_default_level_bump`` have been removed. Instead, these have been -replaced with a single set of recognised commit parser options, ``allowed_tags``, +replaced with a single set of recognized commit parser options, ``allowed_tags``, ``major_tags``, ``minor_tags``, and ``patch_tags``, though the interpretation of these is up to the specific parsers in use. You can read more detail about using commit parser options in :ref:`commit_parser_options `, diff --git a/pyproject.toml b/pyproject.toml index 65aa99d94..5666b1a86 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -398,7 +398,7 @@ ignore_names = ["change_to_ex_proj_dir", "init_example_project"] [tool.semantic_release] logging_use_named_masks = true -commit_parser = "angular" +commit_parser = "conventional" commit_parser_options = { parse_squash_commits = true, ignore_merge_commits = true } build_command = """ python -m pip install -e .[build] diff --git a/src/semantic_release/cli/config.py b/src/semantic_release/cli/config.py index cd2d1bf13..496e97428 100644 --- a/src/semantic_release/cli/config.py +++ b/src/semantic_release/cli/config.py @@ -39,6 +39,7 @@ from semantic_release.commit_parser import ( AngularCommitParser, CommitParser, + ConventionalCommitParser, EmojiCommitParser, ParseResult, ParserOptions, @@ -73,6 +74,7 @@ class HvcsClient(str, Enum): _known_commit_parsers: Dict[str, type[CommitParser]] = { + "conventional": ConventionalCommitParser, "angular": AngularCommitParser, "emoji": EmojiCommitParser, "scipy": ScipyCommitParser, @@ -355,7 +357,7 @@ class RawConfig(BaseModel): env="GIT_COMMIT_AUTHOR", default=DEFAULT_COMMIT_AUTHOR ) commit_message: str = COMMIT_MESSAGE - commit_parser: NonEmptyString = "angular" + commit_parser: NonEmptyString = "conventional" # It's up to the parser_options() method to validate these commit_parser_options: Dict[str, Any] = {} logging_use_named_masks: bool = False @@ -414,6 +416,22 @@ def tag_commit_parser_deprecation_warning(cls, val: str) -> str: ) return val + @field_validator("commit_parser", mode="after") + @classmethod + def angular_commit_parser_deprecation_warning(cls, val: str) -> str: + if val == "angular": + log.warning( + str.join( + " ", + [ + "The 'angular' parser is deprecated and will be removed in v11.", + "The Angular parser is being renamed to the conventional commit parser,", + "which is selected by switching the 'commit_parser' value to 'conventional'.", + ], + ) + ) + return val + @field_validator("build_command_env", mode="after") @classmethod def remove_whitespace(cls, val: list[str]) -> list[str]: @@ -863,6 +881,7 @@ def from_raw_config( # noqa: C901 changelog_excluded_commit_patterns=changelog_excluded_commit_patterns, # TODO: change when we have other styles per parser # changelog_style=changelog_style, + # TODO: Breaking Change v10, change to conventional changelog_style="angular", changelog_output_format=raw.changelog.default_templates.output_format, prerelease=branch_config.prerelease, diff --git a/src/semantic_release/commit_parser/__init__.py b/src/semantic_release/commit_parser/__init__.py index 7e0d9b0fd..740f4ae7f 100644 --- a/src/semantic_release/commit_parser/__init__.py +++ b/src/semantic_release/commit_parser/__init__.py @@ -6,6 +6,10 @@ AngularCommitParser, AngularParserOptions, ) +from semantic_release.commit_parser.conventional import ( + ConventionalCommitParser, + ConventionalCommitParserOptions, +) from semantic_release.commit_parser.emoji import ( EmojiCommitParser, EmojiParserOptions, diff --git a/src/semantic_release/commit_parser/conventional.py b/src/semantic_release/commit_parser/conventional.py new file mode 100644 index 000000000..9ee0b27fe --- /dev/null +++ b/src/semantic_release/commit_parser/conventional.py @@ -0,0 +1,31 @@ +from __future__ import annotations + +from pydantic.dataclasses import dataclass + +from semantic_release.commit_parser.angular import ( + AngularCommitParser, + AngularParserOptions, +) + + +@dataclass +class ConventionalCommitParserOptions(AngularParserOptions): + """Options dataclass for the ConventionalCommitParser.""" + + +class ConventionalCommitParser(AngularCommitParser): + """ + A commit parser for projects conforming to the conventional commits specification. + + See https://www.conventionalcommits.org/en/v1.0.0/ + """ + + # TODO: Deprecate in lieu of get_default_options() + parser_options = ConventionalCommitParserOptions + + def __init__(self, options: ConventionalCommitParserOptions | None = None) -> None: + super().__init__(options) + + @staticmethod + def get_default_options() -> ConventionalCommitParserOptions: + return ConventionalCommitParserOptions() diff --git a/tests/const.py b/tests/const.py index 5bc12340f..88f5677eb 100644 --- a/tests/const.py +++ b/tests/const.py @@ -63,19 +63,19 @@ class RepoActionStep(str, Enum): "Implementing", ] -ANGULAR_COMMITS_CHORE = ("ci: added a commit lint job\n",) +CONVENTIONAL_COMMITS_CHORE = ("ci: added a commit lint job\n",) # Different in-scope commits that produce a certain release type -ANGULAR_COMMITS_PATCH = ( - *ANGULAR_COMMITS_CHORE, +CONVENTIONAL_COMMITS_PATCH = ( + *CONVENTIONAL_COMMITS_CHORE, "fix: fixed voltage in the flux capacitor\n", ) -ANGULAR_COMMITS_MINOR = ( - *ANGULAR_COMMITS_PATCH, +CONVENTIONAL_COMMITS_MINOR = ( + *CONVENTIONAL_COMMITS_PATCH, "feat: last minute rush order\n", ) # Take previous commits and insert a breaking change -ANGULAR_COMMITS_MAJOR = ( - *ANGULAR_COMMITS_MINOR, +CONVENTIONAL_COMMITS_MAJOR = ( + *CONVENTIONAL_COMMITS_MINOR, "fix!: big change\n\nBREAKING CHANGE: reworked something for previous feature\n", ) diff --git a/tests/e2e/cmd_changelog/test_changelog.py b/tests/e2e/cmd_changelog/test_changelog.py index 586c5e961..d717df497 100644 --- a/tests/e2e/cmd_changelog/test_changelog.py +++ b/tests/e2e/cmd_changelog/test_changelog.py @@ -36,31 +36,31 @@ example_changelog_rst, ) from tests.fixtures.repos import ( - repo_w_git_flow_angular_commits, + repo_w_git_flow_conventional_commits, repo_w_git_flow_emoji_commits, repo_w_git_flow_scipy_commits, - repo_w_git_flow_w_alpha_prereleases_n_angular_commits, + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits, repo_w_git_flow_w_alpha_prereleases_n_emoji_commits, repo_w_git_flow_w_alpha_prereleases_n_scipy_commits, - repo_w_git_flow_w_beta_alpha_rev_prereleases_n_angular_commits, + repo_w_git_flow_w_beta_alpha_rev_prereleases_n_conventional_commits, repo_w_git_flow_w_beta_alpha_rev_prereleases_n_emoji_commits, repo_w_git_flow_w_beta_alpha_rev_prereleases_n_scipy_commits, - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits, - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits_using_tag_format, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits_using_tag_format, repo_w_git_flow_w_rc_n_alpha_prereleases_n_emoji_commits, repo_w_git_flow_w_rc_n_alpha_prereleases_n_scipy_commits, - repo_w_github_flow_w_default_release_channel_angular_commits, + repo_w_github_flow_w_default_release_channel_conventional_commits, repo_w_github_flow_w_default_release_channel_emoji_commits, repo_w_github_flow_w_default_release_channel_scipy_commits, - repo_w_github_flow_w_feature_release_channel_angular_commits, + repo_w_github_flow_w_feature_release_channel_conventional_commits, repo_w_github_flow_w_feature_release_channel_emoji_commits, repo_w_github_flow_w_feature_release_channel_scipy_commits, - repo_w_no_tags_angular_commits, + repo_w_no_tags_conventional_commits, repo_w_no_tags_emoji_commits, repo_w_no_tags_scipy_commits, - repo_w_trunk_only_angular_commits, + repo_w_trunk_only_conventional_commits, repo_w_trunk_only_emoji_commits, - repo_w_trunk_only_n_prereleases_angular_commits, + repo_w_trunk_only_n_prereleases_conventional_commits, repo_w_trunk_only_n_prereleases_emoji_commits, repo_w_trunk_only_n_prereleases_scipy_commits, repo_w_trunk_only_scipy_commits, @@ -98,7 +98,7 @@ ) class Commit2Section(TypedDict): - angular: Commit2SectionCommit + conventional: Commit2SectionCommit emoji: Commit2SectionCommit scipy: Commit2SectionCommit @@ -115,8 +115,8 @@ class Commit2SectionCommit(TypedDict): for repo_fixture in ( # Only need to test when it has tags or no tags # DO NOT need to consider all repo types as it doesn't change no-op behavior - repo_w_no_tags_angular_commits.__name__, - repo_w_trunk_only_angular_commits.__name__, + repo_w_no_tags_conventional_commits.__name__, + repo_w_trunk_only_conventional_commits.__name__, ) ], ) @@ -184,8 +184,8 @@ def test_changelog_noop_is_noop( lazy_fixture(repo_fixture) for repo_fixture in [ # All commit types and one without a release - repo_w_no_tags_angular_commits.__name__, - repo_w_trunk_only_angular_commits.__name__, + repo_w_no_tags_conventional_commits.__name__, + repo_w_trunk_only_conventional_commits.__name__, repo_w_trunk_only_emoji_commits.__name__, repo_w_trunk_only_scipy_commits.__name__, ] @@ -193,34 +193,34 @@ def test_changelog_noop_is_noop( *[ pytest.param(lazy_fixture(repo_fixture), marks=pytest.mark.comprehensive) for repo_fixture in [ - # repo_w_no_tags_angular_commits.__name__, + # repo_w_no_tags_conventional_commits.__name__, repo_w_no_tags_emoji_commits.__name__, repo_w_no_tags_scipy_commits.__name__, - # repo_w_trunk_only_angular_commits.__name__, + # repo_w_trunk_only_conventional_commits.__name__, # repo_w_trunk_only_emoji_commits.__name__, # repo_w_trunk_only_scipy_commits.__name__, - repo_w_trunk_only_n_prereleases_angular_commits.__name__, + repo_w_trunk_only_n_prereleases_conventional_commits.__name__, repo_w_trunk_only_n_prereleases_emoji_commits.__name__, repo_w_trunk_only_n_prereleases_scipy_commits.__name__, - repo_w_github_flow_w_default_release_channel_angular_commits.__name__, + repo_w_github_flow_w_default_release_channel_conventional_commits.__name__, repo_w_github_flow_w_default_release_channel_emoji_commits.__name__, repo_w_github_flow_w_default_release_channel_scipy_commits.__name__, - repo_w_github_flow_w_feature_release_channel_angular_commits.__name__, + repo_w_github_flow_w_feature_release_channel_conventional_commits.__name__, repo_w_github_flow_w_feature_release_channel_emoji_commits.__name__, repo_w_github_flow_w_feature_release_channel_scipy_commits.__name__, - repo_w_git_flow_angular_commits.__name__, + repo_w_git_flow_conventional_commits.__name__, repo_w_git_flow_emoji_commits.__name__, repo_w_git_flow_scipy_commits.__name__, - repo_w_git_flow_w_beta_alpha_rev_prereleases_n_angular_commits.__name__, + repo_w_git_flow_w_beta_alpha_rev_prereleases_n_conventional_commits.__name__, repo_w_git_flow_w_beta_alpha_rev_prereleases_n_emoji_commits.__name__, repo_w_git_flow_w_beta_alpha_rev_prereleases_n_scipy_commits.__name__, - repo_w_git_flow_w_alpha_prereleases_n_angular_commits.__name__, + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__, repo_w_git_flow_w_alpha_prereleases_n_emoji_commits.__name__, repo_w_git_flow_w_alpha_prereleases_n_scipy_commits.__name__, - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits.__name__, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits.__name__, repo_w_git_flow_w_rc_n_alpha_prereleases_n_emoji_commits.__name__, repo_w_git_flow_w_rc_n_alpha_prereleases_n_scipy_commits.__name__, - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits_using_tag_format.__name__, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits_using_tag_format.__name__, ] ], ], @@ -294,7 +294,7 @@ def test_changelog_content_regenerated_masked_initial_release( insertion_flag: str, ): build_definition = get_repo_definition_4_trunk_only_repo_w_tags( - commit_type="angular", + commit_type="conventional", mask_initial_release=True, extra_configs={ "tool.semantic_release.changelog.default_templates.changelog_file": str( @@ -345,7 +345,7 @@ def test_changelog_content_regenerated_masked_initial_release( [ lazy_fixture(repo_fixture) for repo_fixture in [ - repo_w_trunk_only_angular_commits.__name__, + repo_w_trunk_only_conventional_commits.__name__, repo_w_trunk_only_emoji_commits.__name__, repo_w_trunk_only_scipy_commits.__name__, ] @@ -402,10 +402,10 @@ def test_changelog_update_mode_unchanged( [ lazy_fixture(repo_fixture) for repo_fixture in [ - repo_w_no_tags_angular_commits.__name__, + repo_w_no_tags_conventional_commits.__name__, repo_w_no_tags_emoji_commits.__name__, repo_w_no_tags_scipy_commits.__name__, - repo_w_trunk_only_angular_commits.__name__, + repo_w_trunk_only_conventional_commits.__name__, repo_w_trunk_only_emoji_commits.__name__, repo_w_trunk_only_scipy_commits.__name__, ] @@ -473,7 +473,7 @@ def test_changelog_update_mode_no_prev_changelog( [ lazy_fixture(repo_fixture) for repo_fixture in [ - repo_w_trunk_only_angular_commits.__name__, + repo_w_trunk_only_conventional_commits.__name__, repo_w_trunk_only_emoji_commits.__name__, repo_w_trunk_only_scipy_commits.__name__, ] @@ -547,7 +547,7 @@ def test_changelog_update_mode_no_flag( lazy_fixture(repo_fixture) for repo_fixture in [ # MUST HAVE at least 2 tags! - repo_w_trunk_only_angular_commits.__name__, + repo_w_trunk_only_conventional_commits.__name__, repo_w_trunk_only_emoji_commits.__name__, repo_w_trunk_only_scipy_commits.__name__, ] @@ -649,7 +649,7 @@ def test_changelog_update_mode_no_header( lazy_fixture(repo_fixture) for repo_fixture in [ # MUST HAVE at least 2 tags! - repo_w_trunk_only_angular_commits.__name__, + repo_w_trunk_only_conventional_commits.__name__, repo_w_trunk_only_emoji_commits.__name__, repo_w_trunk_only_scipy_commits.__name__, ] @@ -753,7 +753,7 @@ def test_changelog_update_mode_no_footer( lazy_fixture(repo_fixture) for repo_fixture in [ # Must not have a single release/tag - repo_w_no_tags_angular_commits.__name__, + repo_w_no_tags_conventional_commits.__name__, repo_w_no_tags_emoji_commits.__name__, repo_w_no_tags_scipy_commits.__name__, ] @@ -852,7 +852,7 @@ def test_changelog_update_mode_no_releases( [ (lazy_fixture(repo_fixture), repo_fixture.split("_")[-2]) for repo_fixture in [ - repo_w_trunk_only_angular_commits.__name__, + repo_w_trunk_only_conventional_commits.__name__, repo_w_trunk_only_emoji_commits.__name__, repo_w_trunk_only_scipy_commits.__name__, ] @@ -869,7 +869,7 @@ def test_changelog_update_mode_unreleased_n_released( commit_n_rtn_changelog_entry: CommitNReturnChangelogEntryFn, changelog_file: Path, insertion_flag: str, - get_commit_def_of_angular_commit: GetCommitDefFn, + get_commit_def_of_conventional_commit: GetCommitDefFn, get_commit_def_of_emoji_commit: GetCommitDefFn, get_commit_def_of_scipy_commit: GetCommitDefFn, ): @@ -890,8 +890,8 @@ def test_changelog_update_mode_unreleased_n_released( ) commit_n_section: Commit2Section = { - "angular": { - "commit": get_commit_def_of_angular_commit( + "conventional": { + "commit": get_commit_def_of_conventional_commit( "perf: improve the performance of the application" ), "section": "Performance Improvements", @@ -1009,7 +1009,7 @@ def test_changelog_update_mode_unreleased_n_released( # Just need to test that it works for "a" project, not all -@pytest.mark.usefixtures(repo_w_trunk_only_n_prereleases_angular_commits.__name__) +@pytest.mark.usefixtures(repo_w_trunk_only_n_prereleases_conventional_commits.__name__) @pytest.mark.parametrize( "args", [("--post-to-release-tag", "v1.99.91910000000000000000000000000")] ) @@ -1026,7 +1026,7 @@ def test_changelog_release_tag_not_in_history( assert "not in release history" in result.stderr.lower() -@pytest.mark.usefixtures(repo_w_trunk_only_n_prereleases_angular_commits.__name__) +@pytest.mark.usefixtures(repo_w_trunk_only_n_prereleases_conventional_commits.__name__) @pytest.mark.parametrize( "args", [ @@ -1119,7 +1119,7 @@ def test_changelog_post_to_release(args: list[str], cli_runner: CliRunner): @pytest.mark.parametrize( "repo_result", - [lazy_fixture(repo_w_trunk_only_n_prereleases_angular_commits.__name__)], + [lazy_fixture(repo_w_trunk_only_n_prereleases_conventional_commits.__name__)], ) def test_custom_release_notes_template( repo_result: BuiltRepoResult, @@ -1168,7 +1168,7 @@ def test_custom_release_notes_template( assert expected_release_notes == actual_notes -@pytest.mark.usefixtures(repo_w_trunk_only_angular_commits.__name__) +@pytest.mark.usefixtures(repo_w_trunk_only_conventional_commits.__name__) def test_changelog_default_on_empty_template_dir( example_changelog_md: Path, changelog_template_dir: Path, @@ -1199,7 +1199,7 @@ def test_changelog_default_on_empty_template_dir( assert example_changelog_md.exists() -@pytest.mark.usefixtures(repo_w_trunk_only_angular_commits.__name__) +@pytest.mark.usefixtures(repo_w_trunk_only_conventional_commits.__name__) def test_changelog_default_on_incorrect_config_template_file( example_changelog_md: Path, changelog_template_dir: Path, @@ -1232,7 +1232,7 @@ def test_changelog_default_on_incorrect_config_template_file( @pytest.mark.parametrize("bad_changelog_file_str", ("/etc/passwd", "../../.ssh/id_rsa")) -@pytest.mark.usefixtures(repo_w_trunk_only_angular_commits.__name__) +@pytest.mark.usefixtures(repo_w_trunk_only_conventional_commits.__name__) def test_changelog_prevent_malicious_path_traversal_file( update_pyproject_toml: UpdatePyprojectTomlFn, bad_changelog_file_str: str, @@ -1257,7 +1257,7 @@ def test_changelog_prevent_malicious_path_traversal_file( @pytest.mark.parametrize("template_dir_path", ("~/.ssh", "../../.ssh")) -@pytest.mark.usefixtures(repo_w_trunk_only_angular_commits.__name__) +@pytest.mark.usefixtures(repo_w_trunk_only_conventional_commits.__name__) def test_changelog_prevent_external_path_traversal_dir( update_pyproject_toml: UpdatePyprojectTomlFn, template_dir_path: str, diff --git a/tests/e2e/cmd_changelog/test_changelog_custom_parser.py b/tests/e2e/cmd_changelog/test_changelog_custom_parser.py index d59044b69..3c6d88f7a 100644 --- a/tests/e2e/cmd_changelog/test_changelog_custom_parser.py +++ b/tests/e2e/cmd_changelog/test_changelog_custom_parser.py @@ -10,9 +10,9 @@ from semantic_release.cli.commands.main import main from tests.const import CHANGELOG_SUBCMD, MAIN_PROG_NAME -from tests.fixtures.repos import repo_w_no_tags_angular_commits +from tests.fixtures.repos import repo_w_no_tags_conventional_commits from tests.util import ( - CustomAngularParserWithIgnorePatterns, + CustomConventionalParserWithIgnorePatterns, assert_successful_exit_code, ) @@ -26,14 +26,14 @@ @pytest.mark.parametrize( - "repo_result", [lazy_fixture(repo_w_no_tags_angular_commits.__name__)] + "repo_result", [lazy_fixture(repo_w_no_tags_conventional_commits.__name__)] ) def test_changelog_custom_parser_remove_from_changelog( repo_result: BuiltRepoResult, cli_runner: CliRunner, update_pyproject_toml: UpdatePyprojectTomlFn, use_custom_parser: UseCustomParserFn, - get_commit_def_of_angular_commit: GetCommitDefFn, + get_commit_def_of_conventional_commit: GetCommitDefFn, changelog_md_file: Path, default_md_changelog_insertion_flag: str, ): @@ -42,7 +42,7 @@ def test_changelog_custom_parser_remove_from_changelog( When provided a commit message that matches the ignore syntax Then the commit message is not included in the resulting changelog """ - ignored_commit_def = get_commit_def_of_angular_commit( + ignored_commit_def = get_commit_def_of_conventional_commit( "chore: do not include me in the changelog" ) @@ -62,7 +62,7 @@ def test_changelog_custom_parser_remove_from_changelog( "tool.semantic_release.changelog.mode", ChangelogMode.INIT.value ) use_custom_parser( - f"{CustomAngularParserWithIgnorePatterns.__module__}:{CustomAngularParserWithIgnorePatterns.__name__}" + f"{CustomConventionalParserWithIgnorePatterns.__module__}:{CustomConventionalParserWithIgnorePatterns.__name__}" ) # Setup: add the commit to be ignored diff --git a/tests/e2e/cmd_changelog/test_changelog_parsing.py b/tests/e2e/cmd_changelog/test_changelog_parsing.py index 3f8dc052a..40b6923cc 100644 --- a/tests/e2e/cmd_changelog/test_changelog_parsing.py +++ b/tests/e2e/cmd_changelog/test_changelog_parsing.py @@ -23,7 +23,7 @@ example_changelog_rst, ) from tests.fixtures.repos.git_flow import ( - repo_w_git_flow_angular_commits, + repo_w_git_flow_conventional_commits, repo_w_git_flow_scipy_commits, ) from tests.util import assert_successful_exit_code @@ -62,7 +62,7 @@ marks=pytest.mark.comprehensive, ) for repo_fixture_name in [ - repo_w_git_flow_angular_commits.__name__, + repo_w_git_flow_conventional_commits.__name__, repo_w_git_flow_scipy_commits.__name__, ] ], diff --git a/tests/e2e/cmd_changelog/test_changelog_release_notes.py b/tests/e2e/cmd_changelog/test_changelog_release_notes.py index 7cde9b13f..e585f8b63 100644 --- a/tests/e2e/cmd_changelog/test_changelog_release_notes.py +++ b/tests/e2e/cmd_changelog/test_changelog_release_notes.py @@ -11,9 +11,9 @@ from tests.const import CHANGELOG_SUBCMD, EXAMPLE_PROJECT_LICENSE, MAIN_PROG_NAME from tests.fixtures.repos import ( - repo_w_github_flow_w_default_release_channel_angular_commits, - repo_w_github_flow_w_feature_release_channel_angular_commits, - repo_w_trunk_only_angular_commits, + repo_w_github_flow_w_default_release_channel_conventional_commits, + repo_w_github_flow_w_feature_release_channel_conventional_commits, + repo_w_trunk_only_conventional_commits, repo_w_trunk_only_emoji_commits, repo_w_trunk_only_scipy_commits, ) @@ -40,7 +40,7 @@ [ lazy_fixture(repo_fixture_name) for repo_fixture_name in [ - repo_w_trunk_only_angular_commits.__name__, + repo_w_trunk_only_conventional_commits.__name__, ] ], ) @@ -95,11 +95,11 @@ def test_changelog_latest_release_notes( "repo_result, mask_initial_release", [ ( - lazy_fixture(repo_w_trunk_only_angular_commits.__name__), + lazy_fixture(repo_w_trunk_only_conventional_commits.__name__), True, ), pytest.param( - lazy_fixture(repo_w_trunk_only_angular_commits.__name__), + lazy_fixture(repo_w_trunk_only_conventional_commits.__name__), False, marks=pytest.mark.comprehensive, ), @@ -111,7 +111,7 @@ def test_changelog_latest_release_notes( ) for mask_initial_release in [True, False] for repo_fixture_name in [ - repo_w_github_flow_w_default_release_channel_angular_commits.__name__, + repo_w_github_flow_w_default_release_channel_conventional_commits.__name__, ] ], ], @@ -175,14 +175,14 @@ def test_changelog_previous_release_notes( "repo_result, cache_key, mask_initial_release, license_name", [ ( - lazy_fixture(repo_w_trunk_only_angular_commits.__name__), - f"psr/repos/{repo_w_trunk_only_angular_commits.__name__}", + lazy_fixture(repo_w_trunk_only_conventional_commits.__name__), + f"psr/repos/{repo_w_trunk_only_conventional_commits.__name__}", True, "BSD-3-Clause", ), pytest.param( - lazy_fixture(repo_w_trunk_only_angular_commits.__name__), - f"psr/repos/{repo_w_trunk_only_angular_commits.__name__}", + lazy_fixture(repo_w_trunk_only_conventional_commits.__name__), + f"psr/repos/{repo_w_trunk_only_conventional_commits.__name__}", False, "BSD-3-Clause", marks=pytest.mark.comprehensive, @@ -201,7 +201,7 @@ def test_changelog_previous_release_notes( repo_w_trunk_only_scipy_commits.__name__, # Add more repos here if needed # github_flow had issues as its hard to generate the release notes from squash commits - repo_w_github_flow_w_feature_release_channel_angular_commits.__name__, + repo_w_github_flow_w_feature_release_channel_conventional_commits.__name__, ] ], ], diff --git a/tests/e2e/cmd_config/test_generate_config.py b/tests/e2e/cmd_config/test_generate_config.py index bfabaa1de..3d49a3136 100644 --- a/tests/e2e/cmd_config/test_generate_config.py +++ b/tests/e2e/cmd_config/test_generate_config.py @@ -10,7 +10,7 @@ from semantic_release.cli.config import RawConfig from tests.const import GENERATE_CONFIG_SUBCMD, MAIN_PROG_NAME, VERSION_SUBCMD -from tests.fixtures.repos import repo_w_no_tags_angular_commits +from tests.fixtures.repos import repo_w_no_tags_conventional_commits from tests.util import assert_successful_exit_code if TYPE_CHECKING: @@ -28,7 +28,7 @@ def raw_config_dict() -> dict[str, Any]: @pytest.mark.parametrize("args", [(), ("--format", "toml"), ("--format", "TOML")]) -@pytest.mark.usefixtures(repo_w_no_tags_angular_commits.__name__) +@pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_generate_config_toml( cli_runner: CliRunner, args: tuple[str], @@ -70,7 +70,7 @@ def test_generate_config_toml( @pytest.mark.parametrize("args", [("--format", "json"), ("--format", "JSON")]) -@pytest.mark.usefixtures(repo_w_no_tags_angular_commits.__name__) +@pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_generate_config_json( cli_runner: CliRunner, args: tuple[str], @@ -111,7 +111,7 @@ def test_generate_config_json( assert_successful_exit_code(result, cli_cmd) -@pytest.mark.usefixtures(repo_w_no_tags_angular_commits.__name__) +@pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_generate_config_pyproject_toml( cli_runner: CliRunner, raw_config_dict: dict[str, Any], diff --git a/tests/e2e/cmd_publish/test_publish.py b/tests/e2e/cmd_publish/test_publish.py index 51cbfe0d4..ba5307fec 100644 --- a/tests/e2e/cmd_publish/test_publish.py +++ b/tests/e2e/cmd_publish/test_publish.py @@ -10,7 +10,7 @@ from semantic_release.hvcs import Github from tests.const import MAIN_PROG_NAME, PUBLISH_SUBCMD -from tests.fixtures.repos import repo_w_trunk_only_angular_commits +from tests.fixtures.repos import repo_w_trunk_only_conventional_commits from tests.util import assert_exit_code, assert_successful_exit_code if TYPE_CHECKING: @@ -23,7 +23,7 @@ @pytest.mark.parametrize("cmd_args", [(), ("--tag", "latest")]) @pytest.mark.parametrize( - "repo_result", [lazy_fixture(repo_w_trunk_only_angular_commits.__name__)] + "repo_result", [lazy_fixture(repo_w_trunk_only_conventional_commits.__name__)] ) def test_publish_latest_uses_latest_tag( repo_result: BuiltRepoResult, @@ -49,7 +49,7 @@ def test_publish_latest_uses_latest_tag( @pytest.mark.parametrize( - "repo_result", [lazy_fixture(repo_w_trunk_only_angular_commits.__name__)] + "repo_result", [lazy_fixture(repo_w_trunk_only_conventional_commits.__name__)] ) def test_publish_to_tag_uses_tag( repo_result: BuiltRepoResult, @@ -73,7 +73,7 @@ def test_publish_to_tag_uses_tag( ) -@pytest.mark.usefixtures(repo_w_trunk_only_angular_commits.__name__) +@pytest.mark.usefixtures(repo_w_trunk_only_conventional_commits.__name__) def test_publish_fails_on_nonexistant_tag(cli_runner: CliRunner): non_existant_tag = "nonexistant-tag" diff --git a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_1_channel.py b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_1_channel.py index 0c753051c..74cd8c7e4 100644 --- a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_1_channel.py +++ b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_1_channel.py @@ -14,7 +14,7 @@ VERSION_SUBCMD, ) from tests.fixtures.repos.git_flow import ( - repo_w_git_flow_angular_commits, + repo_w_git_flow_conventional_commits, repo_w_git_flow_emoji_commits, repo_w_git_flow_scipy_commits, ) @@ -45,7 +45,7 @@ @pytest.mark.parametrize( "repo_fixture_name", [ - repo_w_git_flow_angular_commits.__name__, + repo_w_git_flow_conventional_commits.__name__, *[ pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) for repo_fixture_name in [ diff --git a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_2_channels.py b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_2_channels.py index 004206ad4..31e041770 100644 --- a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_2_channels.py +++ b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_2_channels.py @@ -14,7 +14,7 @@ VERSION_SUBCMD, ) from tests.fixtures.repos.git_flow import ( - repo_w_git_flow_w_alpha_prereleases_n_angular_commits, + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits, repo_w_git_flow_w_alpha_prereleases_n_emoji_commits, repo_w_git_flow_w_alpha_prereleases_n_scipy_commits, ) @@ -45,7 +45,7 @@ @pytest.mark.parametrize( "repo_fixture_name", [ - repo_w_git_flow_w_alpha_prereleases_n_angular_commits.__name__, + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__, *[ pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) for repo_fixture_name in [ diff --git a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_3_channels.py b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_3_channels.py index 2bfa71f03..c3ee44cac 100644 --- a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_3_channels.py +++ b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_3_channels.py @@ -14,8 +14,8 @@ VERSION_SUBCMD, ) from tests.fixtures.repos.git_flow import ( - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits, - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits_using_tag_format, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits_using_tag_format, repo_w_git_flow_w_rc_n_alpha_prereleases_n_emoji_commits, repo_w_git_flow_w_rc_n_alpha_prereleases_n_scipy_commits, ) @@ -46,11 +46,11 @@ @pytest.mark.parametrize( "repo_fixture_name", [ - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits.__name__, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits.__name__, *[ pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) for repo_fixture_name in [ - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits_using_tag_format.__name__, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits_using_tag_format.__name__, repo_w_git_flow_w_rc_n_alpha_prereleases_n_emoji_commits.__name__, repo_w_git_flow_w_rc_n_alpha_prereleases_n_scipy_commits.__name__, ] diff --git a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_4_channels.py b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_4_channels.py index 95464cb0f..3e2b6c14c 100644 --- a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_4_channels.py +++ b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_4_channels.py @@ -14,7 +14,7 @@ VERSION_SUBCMD, ) from tests.fixtures.repos.git_flow import ( - repo_w_git_flow_w_beta_alpha_rev_prereleases_n_angular_commits, + repo_w_git_flow_w_beta_alpha_rev_prereleases_n_conventional_commits, repo_w_git_flow_w_beta_alpha_rev_prereleases_n_emoji_commits, repo_w_git_flow_w_beta_alpha_rev_prereleases_n_scipy_commits, ) @@ -45,7 +45,7 @@ @pytest.mark.parametrize( "repo_fixture_name", [ - repo_w_git_flow_w_beta_alpha_rev_prereleases_n_angular_commits.__name__, + repo_w_git_flow_w_beta_alpha_rev_prereleases_n_conventional_commits.__name__, *[ pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) for repo_fixture_name in [ diff --git a/tests/e2e/cmd_version/bump_version/github_flow/test_repo_1_channel.py b/tests/e2e/cmd_version/bump_version/github_flow/test_repo_1_channel.py index 365672843..0ce3dae7c 100644 --- a/tests/e2e/cmd_version/bump_version/github_flow/test_repo_1_channel.py +++ b/tests/e2e/cmd_version/bump_version/github_flow/test_repo_1_channel.py @@ -14,7 +14,7 @@ VERSION_SUBCMD, ) from tests.fixtures.repos.github_flow import ( - repo_w_github_flow_w_default_release_channel_angular_commits, + repo_w_github_flow_w_default_release_channel_conventional_commits, repo_w_github_flow_w_default_release_channel_emoji_commits, repo_w_github_flow_w_default_release_channel_scipy_commits, ) @@ -45,7 +45,7 @@ @pytest.mark.parametrize( "repo_fixture_name", [ - repo_w_github_flow_w_default_release_channel_angular_commits.__name__, + repo_w_github_flow_w_default_release_channel_conventional_commits.__name__, *[ pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) for repo_fixture_name in [ diff --git a/tests/e2e/cmd_version/bump_version/github_flow/test_repo_2_channels.py b/tests/e2e/cmd_version/bump_version/github_flow/test_repo_2_channels.py index 45b29f082..bf197bc3d 100644 --- a/tests/e2e/cmd_version/bump_version/github_flow/test_repo_2_channels.py +++ b/tests/e2e/cmd_version/bump_version/github_flow/test_repo_2_channels.py @@ -14,7 +14,7 @@ VERSION_SUBCMD, ) from tests.fixtures.repos.github_flow import ( - repo_w_github_flow_w_feature_release_channel_angular_commits, + repo_w_github_flow_w_feature_release_channel_conventional_commits, repo_w_github_flow_w_feature_release_channel_emoji_commits, repo_w_github_flow_w_feature_release_channel_scipy_commits, ) @@ -45,7 +45,7 @@ @pytest.mark.parametrize( "repo_fixture_name", [ - repo_w_github_flow_w_feature_release_channel_angular_commits.__name__, + repo_w_github_flow_w_feature_release_channel_conventional_commits.__name__, *[ pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) for repo_fixture_name in [ diff --git a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk.py b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk.py index fbb876761..fac01bdff 100644 --- a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk.py +++ b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk.py @@ -14,7 +14,7 @@ VERSION_SUBCMD, ) from tests.fixtures.repos.trunk_based_dev import ( - repo_w_trunk_only_angular_commits, + repo_w_trunk_only_conventional_commits, repo_w_trunk_only_emoji_commits, repo_w_trunk_only_scipy_commits, ) @@ -45,7 +45,7 @@ @pytest.mark.parametrize( "repo_fixture_name", [ - repo_w_trunk_only_angular_commits.__name__, + repo_w_trunk_only_conventional_commits.__name__, *[ pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) for repo_fixture_name in [ diff --git a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support.py b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support.py index e81ba67ef..ecc4e3990 100644 --- a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support.py +++ b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support.py @@ -15,7 +15,7 @@ VERSION_SUBCMD, ) from tests.fixtures.repos.trunk_based_dev import ( - repo_w_trunk_only_dual_version_spt_angular_commits, + repo_w_trunk_only_dual_version_spt_conventional_commits, repo_w_trunk_only_dual_version_spt_emoji_commits, repo_w_trunk_only_dual_version_spt_scipy_commits, ) @@ -46,7 +46,7 @@ @pytest.mark.parametrize( "repo_fixture_name", [ - repo_w_trunk_only_dual_version_spt_angular_commits.__name__, + repo_w_trunk_only_dual_version_spt_conventional_commits.__name__, *[ pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) for repo_fixture_name in [ diff --git a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support_w_prereleases.py b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support_w_prereleases.py index 53544a058..720bdfc5c 100644 --- a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support_w_prereleases.py +++ b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support_w_prereleases.py @@ -15,7 +15,7 @@ VERSION_SUBCMD, ) from tests.fixtures.repos.trunk_based_dev import ( - repo_w_trunk_only_dual_version_spt_w_prereleases_angular_commits, + repo_w_trunk_only_dual_version_spt_w_prereleases_conventional_commits, repo_w_trunk_only_dual_version_spt_w_prereleases_emoji_commits, repo_w_trunk_only_dual_version_spt_w_prereleases_scipy_commits, ) @@ -46,7 +46,7 @@ @pytest.mark.parametrize( "repo_fixture_name", [ - repo_w_trunk_only_dual_version_spt_w_prereleases_angular_commits.__name__, + repo_w_trunk_only_dual_version_spt_w_prereleases_conventional_commits.__name__, *[ pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) for repo_fixture_name in [ diff --git a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_w_prereleases.py b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_w_prereleases.py index e907fb2f0..958cac4e9 100644 --- a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_w_prereleases.py +++ b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_w_prereleases.py @@ -14,7 +14,7 @@ VERSION_SUBCMD, ) from tests.fixtures.repos.trunk_based_dev import ( - repo_w_trunk_only_n_prereleases_angular_commits, + repo_w_trunk_only_n_prereleases_conventional_commits, repo_w_trunk_only_n_prereleases_emoji_commits, repo_w_trunk_only_n_prereleases_scipy_commits, ) @@ -45,7 +45,7 @@ @pytest.mark.parametrize( "repo_fixture_name", [ - repo_w_trunk_only_n_prereleases_angular_commits.__name__, + repo_w_trunk_only_n_prereleases_conventional_commits.__name__, *[ pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) for repo_fixture_name in [ diff --git a/tests/e2e/cmd_version/test_version.py b/tests/e2e/cmd_version/test_version.py index e9285bb42..9586073c3 100644 --- a/tests/e2e/cmd_version/test_version.py +++ b/tests/e2e/cmd_version/test_version.py @@ -14,8 +14,8 @@ VERSION_SUBCMD, ) from tests.fixtures.repos import ( - repo_w_no_tags_angular_commits, - repo_w_trunk_only_angular_commits, + repo_w_no_tags_conventional_commits, + repo_w_trunk_only_conventional_commits, ) from tests.util import assert_successful_exit_code @@ -35,7 +35,7 @@ "repo_result, next_release_version", # must use a repo that is ready for a release to prevent no release # logic from being triggered before the noop logic - [(lazy_fixture(repo_w_no_tags_angular_commits.__name__), "0.1.0")], + [(lazy_fixture(repo_w_no_tags_conventional_commits.__name__), "0.1.0")], ) def test_version_noop_is_noop( repo_result: BuiltRepoResult, @@ -84,7 +84,7 @@ def test_version_noop_is_noop( @pytest.mark.parametrize( "repo_result", - [lazy_fixture(repo_w_trunk_only_angular_commits.__name__)], + [lazy_fixture(repo_w_trunk_only_conventional_commits.__name__)], ) def test_version_no_git_verify( repo_result: BuiltRepoResult, @@ -144,7 +144,7 @@ def test_version_no_git_verify( @pytest.mark.parametrize( - "repo_result", [lazy_fixture(repo_w_trunk_only_angular_commits.__name__)] + "repo_result", [lazy_fixture(repo_w_trunk_only_conventional_commits.__name__)] ) def test_version_on_nonrelease_branch( repo_result: BuiltRepoResult, @@ -188,7 +188,7 @@ def test_version_on_nonrelease_branch( @pytest.mark.parametrize( "repo_result", - [lazy_fixture(repo_w_trunk_only_angular_commits.__name__)], + [lazy_fixture(repo_w_trunk_only_conventional_commits.__name__)], ) def test_version_on_last_release( repo_result: BuiltRepoResult, @@ -240,7 +240,7 @@ def test_version_on_last_release( @pytest.mark.parametrize( - "repo_result", [lazy_fixture(repo_w_no_tags_angular_commits.__name__)] + "repo_result", [lazy_fixture(repo_w_no_tags_conventional_commits.__name__)] ) def test_version_only_tag_push( repo_result: BuiltRepoResult, diff --git a/tests/e2e/cmd_version/test_version_build.py b/tests/e2e/cmd_version/test_version_build.py index af81337de..e2b42045f 100644 --- a/tests/e2e/cmd_version/test_version_build.py +++ b/tests/e2e/cmd_version/test_version_build.py @@ -16,7 +16,7 @@ from semantic_release.cli.commands.main import main from tests.const import MAIN_PROG_NAME, VERSION_SUBCMD -from tests.fixtures.repos import repo_w_trunk_only_angular_commits +from tests.fixtures.repos import repo_w_trunk_only_conventional_commits from tests.util import assert_successful_exit_code, get_func_qual_name if TYPE_CHECKING: @@ -57,7 +57,7 @@ "repo_result, cli_args, next_release_version", [ ( - lazy_fixture(repo_w_trunk_only_angular_commits.__name__), + lazy_fixture(repo_w_trunk_only_conventional_commits.__name__), ["--patch"], "0.1.2", ) @@ -137,7 +137,7 @@ def test_version_runs_build_command( "repo_result, cli_args, next_release_version", [ ( - lazy_fixture(repo_w_trunk_only_angular_commits.__name__), + lazy_fixture(repo_w_trunk_only_conventional_commits.__name__), ["--patch"], "0.1.2", ) @@ -278,7 +278,7 @@ def test_version_runs_build_command_windows( "repo_result, cli_args, next_release_version", [ ( - lazy_fixture(repo_w_trunk_only_angular_commits.__name__), + lazy_fixture(repo_w_trunk_only_conventional_commits.__name__), ["--patch"], "0.1.2", ) @@ -382,7 +382,7 @@ def test_version_runs_build_command_w_user_env( ) -@pytest.mark.usefixtures(repo_w_trunk_only_angular_commits.__name__) +@pytest.mark.usefixtures(repo_w_trunk_only_conventional_commits.__name__) def test_version_skips_build_command_with_skip_build( cli_runner: CliRunner, mocked_git_push: mock.MagicMock, diff --git a/tests/e2e/cmd_version/test_version_bump.py b/tests/e2e/cmd_version/test_version_bump.py index 0d9b17ca8..6efb10a96 100644 --- a/tests/e2e/cmd_version/test_version_bump.py +++ b/tests/e2e/cmd_version/test_version_bump.py @@ -12,34 +12,34 @@ from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture from semantic_release.cli.commands.main import main -from semantic_release.commit_parser.angular import AngularCommitParser +from semantic_release.commit_parser.conventional import ConventionalCommitParser from semantic_release.commit_parser.emoji import EmojiCommitParser from semantic_release.commit_parser.scipy import ScipyCommitParser from tests.const import EXAMPLE_PROJECT_NAME, MAIN_PROG_NAME, VERSION_SUBCMD from tests.fixtures import ( - angular_chore_commits, - angular_major_commits, - angular_minor_commits, - angular_patch_commits, + conventional_chore_commits, + conventional_major_commits, + conventional_minor_commits, + conventional_patch_commits, emoji_chore_commits, emoji_major_commits, emoji_minor_commits, emoji_patch_commits, - repo_w_git_flow_w_alpha_prereleases_n_angular_commits, + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits, repo_w_git_flow_w_alpha_prereleases_n_emoji_commits, repo_w_git_flow_w_alpha_prereleases_n_scipy_commits, - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits, repo_w_git_flow_w_rc_n_alpha_prereleases_n_emoji_commits, repo_w_git_flow_w_rc_n_alpha_prereleases_n_scipy_commits, - repo_w_github_flow_w_feature_release_channel_angular_commits, + repo_w_github_flow_w_feature_release_channel_conventional_commits, repo_w_initial_commit, - repo_w_no_tags_angular_commits, + repo_w_no_tags_conventional_commits, repo_w_no_tags_emoji_commits, repo_w_no_tags_scipy_commits, - repo_w_trunk_only_angular_commits, + repo_w_trunk_only_conventional_commits, repo_w_trunk_only_emoji_commits, - repo_w_trunk_only_n_prereleases_angular_commits, + repo_w_trunk_only_n_prereleases_conventional_commits, repo_w_trunk_only_n_prereleases_emoji_commits, repo_w_trunk_only_n_prereleases_scipy_commits, repo_w_trunk_only_scipy_commits, @@ -71,7 +71,7 @@ [ *( ( - lazy_fixture(repo_w_no_tags_angular_commits.__name__), + lazy_fixture(repo_w_no_tags_conventional_commits.__name__), cli_args, next_release_version, ) @@ -118,7 +118,7 @@ marks=pytest.mark.comprehensive, ) for repo_fixture_name, values in { - repo_w_trunk_only_angular_commits.__name__: [ + repo_w_trunk_only_conventional_commits.__name__: [ # New build-metadata forces a new release (["--build-metadata", "build.12345"], "0.1.1+build.12345"), # Forced version bump @@ -155,7 +155,7 @@ "0.1.2-beta.1+build.12345", ), ], - repo_w_trunk_only_n_prereleases_angular_commits.__name__: [ + repo_w_trunk_only_n_prereleases_conventional_commits.__name__: [ # New build-metadata forces a new release (["--build-metadata", "build.12345"], "0.2.0+build.12345"), # Forced version bump @@ -193,7 +193,7 @@ "0.2.1-beta.1+build.12345", ), ], - repo_w_github_flow_w_feature_release_channel_angular_commits.__name__: [ + repo_w_github_flow_w_feature_release_channel_conventional_commits.__name__: [ # New build-metadata forces a new release (["--build-metadata", "build.12345"], "1.1.0+build.12345"), # Forced version bump @@ -230,7 +230,7 @@ "1.1.1-beta.1+build.12345", ), ], - repo_w_git_flow_w_alpha_prereleases_n_angular_commits.__name__: [ + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__: [ # New build-metadata forces a new release (["--build-metadata", "build.12345"], "1.2.0-alpha.2+build.12345"), # Forced version bump @@ -267,7 +267,7 @@ "1.2.1-beta.1+build.12345", ), ], - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits.__name__: [ + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits.__name__: [ # New build-metadata forces a new release (["--build-metadata", "build.12345"], "1.1.0+build.12345"), # Forced version bump @@ -421,9 +421,9 @@ def test_version_force_level( # Default case should be a minor bump since last full release was 1.1.1 # last tag is a prerelease 1.2.0-rc.2 lazy_fixture( - repo_w_git_flow_w_alpha_prereleases_n_angular_commits.__name__ + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__ ), - lazy_fixture(angular_minor_commits.__name__), + lazy_fixture(conventional_minor_commits.__name__), False, "alpha", "1.2.0", @@ -444,25 +444,25 @@ def test_version_force_level( # The last full release version was 1.1.1, so it's had a minor # prerelease ( - repo_w_git_flow_w_alpha_prereleases_n_angular_commits.__name__, + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__, "alpha", ): [ - (angular_patch_commits.__name__, False, "1.1.2", None), + (conventional_patch_commits.__name__, False, "1.1.2", None), ( - angular_patch_commits.__name__, + conventional_patch_commits.__name__, True, "1.1.2-alpha.1", None, ), ( - angular_minor_commits.__name__, + conventional_minor_commits.__name__, True, "1.2.0-alpha.3", "feat/feature-4", # branch ), - (angular_major_commits.__name__, False, "2.0.0", None), + (conventional_major_commits.__name__, False, "2.0.0", None), ( - angular_major_commits.__name__, + conventional_major_commits.__name__, True, "2.0.0-alpha.1", None, @@ -471,26 +471,26 @@ def test_version_force_level( # Latest version for repo_with_git_flow_and_release_channels is # currently 1.1.0 ( - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits.__name__, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits.__name__, "alpha", ): [ - (angular_patch_commits.__name__, False, "1.1.1", None), + (conventional_patch_commits.__name__, False, "1.1.1", None), ( - angular_patch_commits.__name__, + conventional_patch_commits.__name__, True, "1.1.1-alpha.1", None, ), - (angular_minor_commits.__name__, False, "1.2.0", None), + (conventional_minor_commits.__name__, False, "1.2.0", None), ( - angular_minor_commits.__name__, + conventional_minor_commits.__name__, True, "1.2.0-alpha.1", None, ), - (angular_major_commits.__name__, False, "2.0.0", None), + (conventional_major_commits.__name__, False, "2.0.0", None), ( - angular_major_commits.__name__, + conventional_major_commits.__name__, True, "2.0.0-alpha.1", None, @@ -508,7 +508,7 @@ def test_version_force_level( ), ) # TODO: add a github flow test case -def test_version_next_greater_than_version_one_angular( +def test_version_next_greater_than_version_one_conventional( repo_result: BuiltRepoResult, commit_messages: list[str], prerelease: bool, @@ -603,28 +603,28 @@ def test_version_next_greater_than_version_one_angular( # The last full release version was 1.1.1, so it's had a minor # prerelease ( - repo_w_git_flow_w_alpha_prereleases_n_angular_commits.__name__, + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__, "alpha", ): [ *( (commits, True, "1.2.0-alpha.2", "feat/feature-4") for commits in ( None, - angular_chore_commits.__name__, + conventional_chore_commits.__name__, ) ), *( (commits, False, "1.1.1", None) for commits in ( None, - angular_chore_commits.__name__, + conventional_chore_commits.__name__, ) ), ], # Latest version for repo_with_git_flow_and_release_channels is # currently 1.1.0 ( - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits.__name__, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits.__name__, "alpha", ): [ *( @@ -632,7 +632,7 @@ def test_version_next_greater_than_version_one_angular( for prerelease in (True, False) for commits in ( None, - angular_chore_commits.__name__, + conventional_chore_commits.__name__, ) ), ], @@ -647,7 +647,7 @@ def test_version_next_greater_than_version_one_angular( ] ), ) -def test_version_next_greater_than_version_one_no_bump_angular( +def test_version_next_greater_than_version_one_no_bump_conventional( repo_result: BuiltRepoResult, commit_messages: list[str], prerelease: bool, @@ -1351,8 +1351,8 @@ def test_version_next_greater_than_version_one_no_bump_scipy( # It's biggest change type is minor, so the next version should be 0.1.0 # Given the major_on_zero is False and the version is starting at 0.0.0, # the major level commits are limited to only causing a minor level bump - lazy_fixture(repo_w_no_tags_angular_commits.__name__), - lazy_fixture(angular_major_commits.__name__), + lazy_fixture(repo_w_no_tags_conventional_commits.__name__), + lazy_fixture(conventional_major_commits.__name__), False, "rc", False, @@ -1376,7 +1376,7 @@ def test_version_next_greater_than_version_one_no_bump_scipy( # Latest version for repo_with_no_tags is currently 0.0.0 (default) # It's biggest change type is minor, so the next version should be 0.1.0 ( - repo_w_no_tags_angular_commits.__name__, + repo_w_no_tags_conventional_commits.__name__, None, ): [ *( @@ -1389,21 +1389,21 @@ def test_version_next_greater_than_version_one_no_bump_scipy( # will be a minor change and thus the version will be bumped to 0.1.0 None, # Non version bumping commits are absorbed into the previously detected minor bump - lazy_fixture(angular_chore_commits.__name__), + lazy_fixture(conventional_chore_commits.__name__), # Patch commits are absorbed into the previously detected minor bump - lazy_fixture(angular_patch_commits.__name__), + lazy_fixture(conventional_patch_commits.__name__), # Minor level commits are absorbed into the previously detected minor bump - lazy_fixture(angular_minor_commits.__name__), + lazy_fixture(conventional_minor_commits.__name__), # Given the major_on_zero is False and the version is starting at 0.0.0, # the major level commits are limited to only causing a minor level bump - # lazy_fixture(angular_major_commits.__name__), # used as default + # lazy_fixture(conventional_major_commits.__name__), # used as default ) ), # when prerelease is False, & major_on_zero is False, & allow_zero_version is True, # the version should only be minor bumped when provided major commits because # of the major_on_zero value ( - lazy_fixture(angular_major_commits.__name__), + lazy_fixture(conventional_major_commits.__name__), False, False, True, @@ -1414,7 +1414,7 @@ def test_version_next_greater_than_version_one_no_bump_scipy( # the version should be major bumped when provided major commits because # of the major_on_zero value ( - lazy_fixture(angular_major_commits.__name__), + lazy_fixture(conventional_major_commits.__name__), False, True, True, @@ -1429,17 +1429,17 @@ def test_version_next_greater_than_version_one_no_bump_scipy( for major_on_zero in (True, False) for commits in ( None, - lazy_fixture(angular_chore_commits.__name__), - lazy_fixture(angular_patch_commits.__name__), - lazy_fixture(angular_minor_commits.__name__), - lazy_fixture(angular_major_commits.__name__), + lazy_fixture(conventional_chore_commits.__name__), + lazy_fixture(conventional_patch_commits.__name__), + lazy_fixture(conventional_minor_commits.__name__), + lazy_fixture(conventional_major_commits.__name__), ) ), ], # Latest version for repo_with_single_branch is currently 0.1.1 # Note repo_with_single_branch isn't modelled with prereleases ( - repo_w_trunk_only_angular_commits.__name__, + repo_w_trunk_only_conventional_commits.__name__, None, ): [ *( @@ -1447,7 +1447,7 @@ def test_version_next_greater_than_version_one_no_bump_scipy( # the version is patch bumped because of the patch level commits # regardless of the major_on_zero value ( - lazy_fixture(angular_patch_commits.__name__), + lazy_fixture(conventional_patch_commits.__name__), False, major_on_zero, True, @@ -1461,15 +1461,15 @@ def test_version_next_greater_than_version_one_no_bump_scipy( # the version is minor bumped because of the major_on_zero value=False (commits, False, False, True, "0.2.0", None) for commits in ( - lazy_fixture(angular_minor_commits.__name__), - lazy_fixture(angular_major_commits.__name__), + lazy_fixture(conventional_minor_commits.__name__), + lazy_fixture(conventional_major_commits.__name__), ) ), # when prerelease must be False, and allow_zero_version is True, # but the major_on_zero is True, then when a major level commit is given, # the version should be bumped to the next major version ( - lazy_fixture(angular_major_commits.__name__), + lazy_fixture(conventional_major_commits.__name__), False, True, True, @@ -1483,24 +1483,24 @@ def test_version_next_greater_than_version_one_no_bump_scipy( for major_on_zero in (True, False) for commits in ( None, - lazy_fixture(angular_chore_commits.__name__), - lazy_fixture(angular_patch_commits.__name__), - lazy_fixture(angular_minor_commits.__name__), - lazy_fixture(angular_major_commits.__name__), + lazy_fixture(conventional_chore_commits.__name__), + lazy_fixture(conventional_patch_commits.__name__), + lazy_fixture(conventional_minor_commits.__name__), + lazy_fixture(conventional_major_commits.__name__), ) ), ], # Latest version for repo_with_single_branch_and_prereleases is # currently 0.2.0 ( - repo_w_trunk_only_n_prereleases_angular_commits.__name__, + repo_w_trunk_only_n_prereleases_conventional_commits.__name__, None, ): [ # when allow_zero_version is True, # prerelease is False, & major_on_zero is False, the version should be # patch bumped as a prerelease version, when given patch level commits ( - lazy_fixture(angular_patch_commits.__name__), + lazy_fixture(conventional_patch_commits.__name__), True, False, True, @@ -1511,7 +1511,7 @@ def test_version_next_greater_than_version_one_no_bump_scipy( # prerelease is False, & major_on_zero is False, the version should be # patch bumped, when given patch level commits ( - lazy_fixture(angular_patch_commits.__name__), + lazy_fixture(conventional_patch_commits.__name__), False, False, True, @@ -1524,8 +1524,8 @@ def test_version_next_greater_than_version_one_no_bump_scipy( # minor bumped as a prerelease version, when given commits of a minor or major level (commits, True, False, True, "0.3.0-rc.1", None) for commits in ( - lazy_fixture(angular_minor_commits.__name__), - lazy_fixture(angular_major_commits.__name__), + lazy_fixture(conventional_minor_commits.__name__), + lazy_fixture(conventional_major_commits.__name__), ) ), *( @@ -1534,15 +1534,15 @@ def test_version_next_greater_than_version_one_no_bump_scipy( # minor or major level because major_on_zero = False (commits, False, False, True, "0.3.0", None) for commits in ( - lazy_fixture(angular_minor_commits.__name__), - lazy_fixture(angular_major_commits.__name__), + lazy_fixture(conventional_minor_commits.__name__), + lazy_fixture(conventional_major_commits.__name__), ) ), # when prerelease is True, & major_on_zero is True, and allow_zero_version # is True, the version should be bumped to 1.0.0 as a prerelease version, when # given major level commits ( - lazy_fixture(angular_major_commits.__name__), + lazy_fixture(conventional_major_commits.__name__), True, True, True, @@ -1552,7 +1552,7 @@ def test_version_next_greater_than_version_one_no_bump_scipy( # when prerelease is False, & major_on_zero is True, and allow_zero_version # is True, the version should be bumped to 1.0.0, when given major level commits ( - lazy_fixture(angular_major_commits.__name__), + lazy_fixture(conventional_major_commits.__name__), False, True, True, @@ -1567,10 +1567,10 @@ def test_version_next_greater_than_version_one_no_bump_scipy( for major_on_zero in (True, False) for commits in ( None, - lazy_fixture(angular_chore_commits.__name__), - lazy_fixture(angular_patch_commits.__name__), - lazy_fixture(angular_minor_commits.__name__), - lazy_fixture(angular_major_commits.__name__), + lazy_fixture(conventional_chore_commits.__name__), + lazy_fixture(conventional_patch_commits.__name__), + lazy_fixture(conventional_minor_commits.__name__), + lazy_fixture(conventional_major_commits.__name__), ) ), *( @@ -1580,9 +1580,9 @@ def test_version_next_greater_than_version_one_no_bump_scipy( (commits, False, major_on_zero, False, "1.0.0", None) for major_on_zero in (True, False) for commits in ( - lazy_fixture(angular_patch_commits.__name__), - lazy_fixture(angular_minor_commits.__name__), - lazy_fixture(angular_major_commits.__name__), + lazy_fixture(conventional_patch_commits.__name__), + lazy_fixture(conventional_minor_commits.__name__), + lazy_fixture(conventional_major_commits.__name__), ) ), ], @@ -1599,7 +1599,7 @@ def test_version_next_greater_than_version_one_no_bump_scipy( ], ), ) -def test_version_next_w_zero_dot_versions_angular( +def test_version_next_w_zero_dot_versions_conventional( repo_result: BuiltRepoResult, commit_messages: list[str], prerelease: bool, @@ -1706,7 +1706,7 @@ def test_version_next_w_zero_dot_versions_angular( # Latest version for repo_with_single_branch is currently 0.1.1 # Note repo_with_single_branch isn't modelled with prereleases ( - repo_w_trunk_only_angular_commits.__name__, + repo_w_trunk_only_conventional_commits.__name__, None, ): [ *( @@ -1717,14 +1717,14 @@ def test_version_next_w_zero_dot_versions_angular( for major_on_zero in (True, False) for commits in ( None, - lazy_fixture(angular_chore_commits.__name__), + lazy_fixture(conventional_chore_commits.__name__), ) ), ], # Latest version for repo_with_single_branch_and_prereleases is # currently 0.2.0 ( - repo_w_trunk_only_n_prereleases_angular_commits.__name__, + repo_w_trunk_only_n_prereleases_conventional_commits.__name__, None, ): [ *( @@ -1736,7 +1736,7 @@ def test_version_next_w_zero_dot_versions_angular( for major_on_zero in (True, False) for commits in ( None, - lazy_fixture(angular_chore_commits.__name__), + lazy_fixture(conventional_chore_commits.__name__), ) ), ], @@ -1753,7 +1753,7 @@ def test_version_next_w_zero_dot_versions_angular( ], ), ) -def test_version_next_w_zero_dot_versions_no_bump_angular( +def test_version_next_w_zero_dot_versions_no_bump_conventional( repo_result: BuiltRepoResult, commit_messages: list[str], prerelease: bool, @@ -2839,7 +2839,7 @@ def test_version_next_w_zero_dot_versions_no_bump_scipy( # Latest version for repo_w_initial_commit is currently 0.0.0 # with no changes made it should be 0.0.0 lazy_fixture(repo_w_initial_commit.__name__), - AngularCommitParser.__name__.replace("CommitParser", "").lower(), + ConventionalCommitParser.__name__.replace("CommitParser", "").lower(), None, False, "rc", @@ -2874,11 +2874,11 @@ def test_version_next_w_zero_dot_versions_no_bump_scipy( for major_on_zero in (True, False) for commits, parser in ( # No commits added, so base is just initial commit at 0.0.0 - (None, AngularCommitParser.__name__), + (None, ConventionalCommitParser.__name__), # Chore like commits also don't trigger a version bump so it stays 0.0.0 ( - lazy_fixture(angular_chore_commits.__name__), - AngularCommitParser.__name__, + lazy_fixture(conventional_chore_commits.__name__), + ConventionalCommitParser.__name__, ), ( lazy_fixture(emoji_chore_commits.__name__), @@ -2898,8 +2898,8 @@ def test_version_next_w_zero_dot_versions_no_bump_scipy( # a patch bump as a prerelease version, because of the patch level commits # major_on_zero is irrelevant here as we are only applying patch commits ( - lazy_fixture(angular_patch_commits.__name__), - AngularCommitParser.__name__, + lazy_fixture(conventional_patch_commits.__name__), + ConventionalCommitParser.__name__, ), ( lazy_fixture(emoji_patch_commits.__name__), @@ -2919,8 +2919,8 @@ def test_version_next_w_zero_dot_versions_no_bump_scipy( # a patch bump because of the patch commits added # major_on_zero is irrelevant here as we are only applying patch commits ( - lazy_fixture(angular_patch_commits.__name__), - AngularCommitParser.__name__, + lazy_fixture(conventional_patch_commits.__name__), + ConventionalCommitParser.__name__, ), ( lazy_fixture(emoji_patch_commits.__name__), @@ -2938,8 +2938,8 @@ def test_version_next_w_zero_dot_versions_no_bump_scipy( # when prerelease is False, & major_on_zero is False, the version should be # a minor bump because of the minor commits added ( - lazy_fixture(angular_minor_commits.__name__), - AngularCommitParser.__name__, + lazy_fixture(conventional_minor_commits.__name__), + ConventionalCommitParser.__name__, ), ( lazy_fixture(emoji_minor_commits.__name__), @@ -2952,8 +2952,8 @@ def test_version_next_w_zero_dot_versions_no_bump_scipy( # Given the major_on_zero is False and the version is starting at 0.0.0, # the major level commits are limited to only causing a minor level bump ( - lazy_fixture(angular_major_commits.__name__), - AngularCommitParser.__name__, + lazy_fixture(conventional_major_commits.__name__), + ConventionalCommitParser.__name__, ), ( lazy_fixture(emoji_major_commits.__name__), @@ -2973,8 +2973,8 @@ def test_version_next_w_zero_dot_versions_no_bump_scipy( # the version should be a minor bump of 0.0.0 # because of the minor commits added and zero version is allowed ( - lazy_fixture(angular_minor_commits.__name__), - AngularCommitParser.__name__, + lazy_fixture(conventional_minor_commits.__name__), + ConventionalCommitParser.__name__, ), ( lazy_fixture(emoji_minor_commits.__name__), @@ -2987,8 +2987,8 @@ def test_version_next_w_zero_dot_versions_no_bump_scipy( # Given the major_on_zero is False and the version is starting at 0.0.0, # the major level commits are limited to only causing a minor level bump ( - lazy_fixture(angular_major_commits.__name__), - AngularCommitParser.__name__, + lazy_fixture(conventional_major_commits.__name__), + ConventionalCommitParser.__name__, ), ( lazy_fixture(emoji_major_commits.__name__), @@ -3009,22 +3009,22 @@ def test_version_next_w_zero_dot_versions_no_bump_scipy( for major_on_zero in (True, False) for commits, parser in ( # parser doesn't matter here as long as it detects a NO_RELEASE on Initial Commit - (None, AngularCommitParser.__name__), + (None, ConventionalCommitParser.__name__), ( - lazy_fixture(angular_chore_commits.__name__), - AngularCommitParser.__name__, + lazy_fixture(conventional_chore_commits.__name__), + ConventionalCommitParser.__name__, ), ( - lazy_fixture(angular_patch_commits.__name__), - AngularCommitParser.__name__, + lazy_fixture(conventional_patch_commits.__name__), + ConventionalCommitParser.__name__, ), ( - lazy_fixture(angular_minor_commits.__name__), - AngularCommitParser.__name__, + lazy_fixture(conventional_minor_commits.__name__), + ConventionalCommitParser.__name__, ), ( - lazy_fixture(angular_major_commits.__name__), - AngularCommitParser.__name__, + lazy_fixture(conventional_major_commits.__name__), + ConventionalCommitParser.__name__, ), ( lazy_fixture(emoji_chore_commits.__name__), @@ -3068,22 +3068,22 @@ def test_version_next_w_zero_dot_versions_no_bump_scipy( (commits, parser, False, major_on_zero, False, "1.0.0") for major_on_zero in (True, False) for commits, parser in ( - (None, AngularCommitParser.__name__), + (None, ConventionalCommitParser.__name__), ( - lazy_fixture(angular_chore_commits.__name__), - AngularCommitParser.__name__, + lazy_fixture(conventional_chore_commits.__name__), + ConventionalCommitParser.__name__, ), ( - lazy_fixture(angular_patch_commits.__name__), - AngularCommitParser.__name__, + lazy_fixture(conventional_patch_commits.__name__), + ConventionalCommitParser.__name__, ), ( - lazy_fixture(angular_minor_commits.__name__), - AngularCommitParser.__name__, + lazy_fixture(conventional_minor_commits.__name__), + ConventionalCommitParser.__name__, ), ( - lazy_fixture(angular_major_commits.__name__), - AngularCommitParser.__name__, + lazy_fixture(conventional_major_commits.__name__), + ConventionalCommitParser.__name__, ), ( lazy_fixture(emoji_chore_commits.__name__), diff --git a/tests/e2e/cmd_version/test_version_changelog.py b/tests/e2e/cmd_version/test_version_changelog.py index 7d4bbecbb..19a3bb3ba 100644 --- a/tests/e2e/cmd_version/test_version_changelog.py +++ b/tests/e2e/cmd_version/test_version_changelog.py @@ -20,25 +20,25 @@ example_changelog_rst, ) from tests.fixtures.repos import ( - repo_w_git_flow_w_alpha_prereleases_n_angular_commits, + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits, repo_w_git_flow_w_alpha_prereleases_n_emoji_commits, repo_w_git_flow_w_alpha_prereleases_n_scipy_commits, - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits, - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits_using_tag_format, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits_using_tag_format, repo_w_git_flow_w_rc_n_alpha_prereleases_n_emoji_commits, repo_w_git_flow_w_rc_n_alpha_prereleases_n_scipy_commits, - repo_w_github_flow_w_default_release_channel_angular_commits, + repo_w_github_flow_w_default_release_channel_conventional_commits, repo_w_github_flow_w_default_release_channel_emoji_commits, repo_w_github_flow_w_default_release_channel_scipy_commits, - repo_w_github_flow_w_feature_release_channel_angular_commits, + repo_w_github_flow_w_feature_release_channel_conventional_commits, repo_w_github_flow_w_feature_release_channel_emoji_commits, repo_w_github_flow_w_feature_release_channel_scipy_commits, - repo_w_no_tags_angular_commits, + repo_w_no_tags_conventional_commits, repo_w_no_tags_emoji_commits, repo_w_no_tags_scipy_commits, - repo_w_trunk_only_angular_commits, + repo_w_trunk_only_conventional_commits, repo_w_trunk_only_emoji_commits, - repo_w_trunk_only_n_prereleases_angular_commits, + repo_w_trunk_only_n_prereleases_conventional_commits, repo_w_trunk_only_n_prereleases_emoji_commits, repo_w_trunk_only_n_prereleases_scipy_commits, repo_w_trunk_only_scipy_commits, @@ -79,8 +79,8 @@ "repo_result, cache_key, tag_format", [ ( - lazy_fixture(repo_w_trunk_only_angular_commits.__name__), - f"psr/repos/{repo_w_trunk_only_angular_commits.__name__}", + lazy_fixture(repo_w_trunk_only_conventional_commits.__name__), + f"psr/repos/{repo_w_trunk_only_conventional_commits.__name__}", "v{version}", ), *[ @@ -98,7 +98,7 @@ None, ) for repo_fixture_name in [ - # repo_with_single_branch_angular_commits.__name__, # default + # repo_with_single_branch_conventional_commits.__name__, # default repo_w_trunk_only_emoji_commits.__name__, repo_w_trunk_only_scipy_commits.__name__, ] @@ -109,7 +109,7 @@ None, ) for repo_fixture_name in [ - repo_w_trunk_only_n_prereleases_angular_commits.__name__, + repo_w_trunk_only_n_prereleases_conventional_commits.__name__, repo_w_trunk_only_n_prereleases_emoji_commits.__name__, repo_w_trunk_only_n_prereleases_scipy_commits.__name__, ] @@ -120,7 +120,7 @@ None, ) for repo_fixture_name in [ - repo_w_github_flow_w_default_release_channel_angular_commits.__name__, + repo_w_github_flow_w_default_release_channel_conventional_commits.__name__, repo_w_github_flow_w_default_release_channel_emoji_commits.__name__, repo_w_github_flow_w_default_release_channel_scipy_commits.__name__, ] @@ -131,7 +131,7 @@ None, ) for repo_fixture_name in [ - repo_w_github_flow_w_feature_release_channel_angular_commits.__name__, + repo_w_github_flow_w_feature_release_channel_conventional_commits.__name__, repo_w_github_flow_w_feature_release_channel_emoji_commits.__name__, repo_w_github_flow_w_feature_release_channel_scipy_commits.__name__, ] @@ -142,7 +142,7 @@ None, ) for repo_fixture_name in [ - repo_w_git_flow_w_alpha_prereleases_n_angular_commits.__name__, + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__, repo_w_git_flow_w_alpha_prereleases_n_emoji_commits.__name__, repo_w_git_flow_w_alpha_prereleases_n_scipy_commits.__name__, ] @@ -153,7 +153,7 @@ None, ) for repo_fixture_name in [ - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits.__name__, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits.__name__, repo_w_git_flow_w_rc_n_alpha_prereleases_n_emoji_commits.__name__, repo_w_git_flow_w_rc_n_alpha_prereleases_n_scipy_commits.__name__, ] @@ -164,7 +164,7 @@ "submod-v{version}", ) for repo_fixture_name in [ - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits_using_tag_format.__name__, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits_using_tag_format.__name__, ] ], ] @@ -285,8 +285,8 @@ def test_version_updates_changelog_w_new_version( "repo_result, cache_key", [ ( - lazy_fixture(repo_w_no_tags_angular_commits.__name__), - f"psr/repos/{repo_w_no_tags_angular_commits.__name__}", + lazy_fixture(repo_w_no_tags_conventional_commits.__name__), + f"psr/repos/{repo_w_no_tags_conventional_commits.__name__}", ), *[ pytest.param( @@ -296,7 +296,7 @@ def test_version_updates_changelog_w_new_version( ) for repo_fixture in [ # Must not have a single release/tag - # repo_with_no_tags_angular_commits.__name__, # default + # repo_with_no_tags_conventional_commits.__name__, # default repo_w_no_tags_emoji_commits.__name__, repo_w_no_tags_scipy_commits.__name__, ] @@ -435,8 +435,8 @@ def test_version_updates_changelog_wo_prev_releases( "repo_result, cache_key, tag_format", [ ( - lazy_fixture(repo_w_trunk_only_angular_commits.__name__), - f"psr/repos/{repo_w_trunk_only_angular_commits.__name__}", + lazy_fixture(repo_w_trunk_only_conventional_commits.__name__), + f"psr/repos/{repo_w_trunk_only_conventional_commits.__name__}", "v{version}", ), *[ @@ -454,7 +454,7 @@ def test_version_updates_changelog_wo_prev_releases( None, ) for repo_fixture_name in [ - # repo_with_single_branch_angular_commits.__name__, # default + # repo_with_single_branch_conventional_commits.__name__, # default repo_w_trunk_only_emoji_commits.__name__, repo_w_trunk_only_scipy_commits.__name__, ] @@ -465,7 +465,7 @@ def test_version_updates_changelog_wo_prev_releases( None, ) for repo_fixture_name in [ - repo_w_trunk_only_n_prereleases_angular_commits.__name__, + repo_w_trunk_only_n_prereleases_conventional_commits.__name__, repo_w_trunk_only_n_prereleases_emoji_commits.__name__, repo_w_trunk_only_n_prereleases_scipy_commits.__name__, ] @@ -476,7 +476,7 @@ def test_version_updates_changelog_wo_prev_releases( None, ) for repo_fixture_name in [ - repo_w_github_flow_w_default_release_channel_angular_commits.__name__, + repo_w_github_flow_w_default_release_channel_conventional_commits.__name__, repo_w_github_flow_w_default_release_channel_emoji_commits.__name__, repo_w_github_flow_w_default_release_channel_scipy_commits.__name__, ] @@ -487,7 +487,7 @@ def test_version_updates_changelog_wo_prev_releases( None, ) for repo_fixture_name in [ - repo_w_github_flow_w_feature_release_channel_angular_commits.__name__, + repo_w_github_flow_w_feature_release_channel_conventional_commits.__name__, repo_w_github_flow_w_feature_release_channel_emoji_commits.__name__, repo_w_github_flow_w_feature_release_channel_scipy_commits.__name__, ] @@ -498,7 +498,7 @@ def test_version_updates_changelog_wo_prev_releases( None, ) for repo_fixture_name in [ - repo_w_git_flow_w_alpha_prereleases_n_angular_commits.__name__, + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__, repo_w_git_flow_w_alpha_prereleases_n_emoji_commits.__name__, repo_w_git_flow_w_alpha_prereleases_n_scipy_commits.__name__, ] @@ -509,7 +509,7 @@ def test_version_updates_changelog_wo_prev_releases( None, ) for repo_fixture_name in [ - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits.__name__, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits.__name__, repo_w_git_flow_w_rc_n_alpha_prereleases_n_emoji_commits.__name__, repo_w_git_flow_w_rc_n_alpha_prereleases_n_scipy_commits.__name__, ] @@ -520,7 +520,7 @@ def test_version_updates_changelog_wo_prev_releases( "submod-v{version}", ) for repo_fixture_name in [ - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits_using_tag_format.__name__, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits_using_tag_format.__name__, ] ], ] @@ -608,7 +608,7 @@ def test_version_initializes_changelog_in_update_mode_w_no_prev_changelog( ), ], ) -@pytest.mark.usefixtures(repo_w_trunk_only_angular_commits.__name__) +@pytest.mark.usefixtures(repo_w_trunk_only_conventional_commits.__name__) def test_version_maintains_changelog_in_update_mode_w_no_flag( changelog_file: Path, cli_runner: CliRunner, @@ -675,7 +675,7 @@ def test_version_maintains_changelog_in_update_mode_w_no_flag( ) for repo_fixture in [ # Must have a previous release/tag - repo_w_trunk_only_angular_commits.__name__, + repo_w_trunk_only_conventional_commits.__name__, ] ], ) diff --git a/tests/e2e/cmd_version/test_version_changelog_custom_commit_msg.py b/tests/e2e/cmd_version/test_version_changelog_custom_commit_msg.py index 0351f5dfb..d7ee1d08f 100644 --- a/tests/e2e/cmd_version/test_version_changelog_custom_commit_msg.py +++ b/tests/e2e/cmd_version/test_version_changelog_custom_commit_msg.py @@ -25,7 +25,7 @@ changelog_rst_file, ) from tests.fixtures.repos import ( - repo_w_trunk_only_angular_commits, + repo_w_trunk_only_conventional_commits, ) from tests.util import ( assert_successful_exit_code, @@ -51,7 +51,7 @@ ) class Commit2Section(TypedDict): - angular: Commit2SectionCommit + conventional: Commit2SectionCommit emoji: Commit2SectionCommit scipy: Commit2SectionCommit @@ -97,12 +97,12 @@ class Commit2SectionCommit(TypedDict): *[ ( # Repos: Must have at least 2 releases - repo_w_trunk_only_angular_commits.__name__, + repo_w_trunk_only_conventional_commits.__name__, commit_msg, ) for commit_msg in [ dedent( - # Angular compliant prefix with skip-ci idicator + # Conventional compliant prefix with skip-ci idicator """\ chore(release): v{version} [skip ci] diff --git a/tests/e2e/cmd_version/test_version_github_actions.py b/tests/e2e/cmd_version/test_version_github_actions.py index 1c7ba4aaa..c79e34b15 100644 --- a/tests/e2e/cmd_version/test_version_github_actions.py +++ b/tests/e2e/cmd_version/test_version_github_actions.py @@ -7,7 +7,9 @@ from semantic_release.cli.commands.main import main from tests.const import MAIN_PROG_NAME, VERSION_SUBCMD -from tests.fixtures.repos import repo_w_git_flow_w_alpha_prereleases_n_angular_commits +from tests.fixtures.repos import ( + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits, +) from tests.util import actions_output_to_dict, assert_successful_exit_code if TYPE_CHECKING: @@ -16,7 +18,9 @@ from click.testing import CliRunner -@pytest.mark.usefixtures(repo_w_git_flow_w_alpha_prereleases_n_angular_commits.__name__) +@pytest.mark.usefixtures( + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__ +) def test_version_writes_github_actions_output( cli_runner: CliRunner, monkeypatch: pytest.MonkeyPatch, diff --git a/tests/e2e/cmd_version/test_version_print.py b/tests/e2e/cmd_version/test_version_print.py index b0226387d..a259036cb 100644 --- a/tests/e2e/cmd_version/test_version_print.py +++ b/tests/e2e/cmd_version/test_version_print.py @@ -11,16 +11,16 @@ MAIN_PROG_NAME, VERSION_SUBCMD, ) -from tests.fixtures.commit_parsers import angular_minor_commits -from tests.fixtures.git_repo import get_commit_def_of_angular_commit +from tests.fixtures.commit_parsers import conventional_minor_commits +from tests.fixtures.git_repo import get_commit_def_of_conventional_commit from tests.fixtures.repos import ( - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits_using_tag_format, - repo_w_no_tags_angular_commits, - repo_w_trunk_only_angular_commits, - repo_w_trunk_only_angular_commits_using_tag_format, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits_using_tag_format, + repo_w_no_tags_conventional_commits, + repo_w_trunk_only_conventional_commits, + repo_w_trunk_only_conventional_commits_using_tag_format, ) from tests.fixtures.repos.trunk_based_dev.repo_w_no_tags import ( - repo_w_no_tags_angular_commits_using_tag_format, + repo_w_no_tags_conventional_commits_using_tag_format, ) from tests.util import ( add_text_to_file, @@ -47,8 +47,8 @@ "repo_result, commits, force_args, next_release_version", [ ( - lazy_fixture(repo_w_trunk_only_angular_commits.__name__), - lazy_fixture(angular_minor_commits.__name__), + lazy_fixture(repo_w_trunk_only_conventional_commits.__name__), + lazy_fixture(conventional_minor_commits.__name__), cli_args, next_release_version, ) @@ -155,15 +155,15 @@ def test_version_print_next_version( *[ pytest.param( lazy_fixture(repo_fixture_name), - lazy_fixture(angular_minor_commits.__name__), + lazy_fixture(conventional_minor_commits.__name__), cli_args, next_release_version, marks=marks if marks else [], ) for repo_fixture_name, marks in ( - (repo_w_trunk_only_angular_commits.__name__, None), + (repo_w_trunk_only_conventional_commits.__name__, None), ( - repo_w_trunk_only_angular_commits_using_tag_format.__name__, + repo_w_trunk_only_conventional_commits_using_tag_format.__name__, pytest.mark.comprehensive, ), ) @@ -213,8 +213,8 @@ def test_version_print_next_version( marks=pytest.mark.comprehensive, ) for repo_fixture_name in ( - repo_w_no_tags_angular_commits.__name__, - repo_w_no_tags_angular_commits_using_tag_format.__name__, + repo_w_no_tags_conventional_commits.__name__, + repo_w_no_tags_conventional_commits_using_tag_format.__name__, ) for cli_args, next_release_version in ( # Dynamic version bump determination (based on commits) @@ -321,7 +321,7 @@ def test_version_print_tag_prints_next_tag( @pytest.mark.parametrize( "repo_result", - [lazy_fixture(repo_w_trunk_only_angular_commits.__name__)], + [lazy_fixture(repo_w_trunk_only_conventional_commits.__name__)], ) def test_version_print_last_released_prints_version( repo_result: BuiltRepoResult, @@ -367,8 +367,8 @@ def test_version_print_last_released_prints_version( "repo_result, commits", [ ( - lazy_fixture(repo_w_trunk_only_angular_commits.__name__), - lazy_fixture(angular_minor_commits.__name__), + lazy_fixture(repo_w_trunk_only_conventional_commits.__name__), + lazy_fixture(conventional_minor_commits.__name__), ) ], ) @@ -420,7 +420,7 @@ def test_version_print_last_released_prints_released_if_commits( @pytest.mark.parametrize( "repo_result", - [lazy_fixture(repo_w_no_tags_angular_commits.__name__)], + [lazy_fixture(repo_w_no_tags_conventional_commits.__name__)], ) def test_version_print_last_released_prints_nothing_if_no_tags( repo_result: BuiltRepoResult, @@ -464,7 +464,7 @@ def test_version_print_last_released_prints_nothing_if_no_tags( @pytest.mark.parametrize( "repo_result", - [lazy_fixture(repo_w_trunk_only_angular_commits.__name__)], + [lazy_fixture(repo_w_trunk_only_conventional_commits.__name__)], ) def test_version_print_last_released_on_detached_head( repo_result: BuiltRepoResult, @@ -511,7 +511,7 @@ def test_version_print_last_released_on_detached_head( @pytest.mark.parametrize( "repo_result", - [lazy_fixture(repo_w_trunk_only_angular_commits.__name__)], + [lazy_fixture(repo_w_trunk_only_conventional_commits.__name__)], ) def test_version_print_last_released_on_nonrelease_branch( repo_result: BuiltRepoResult, @@ -559,10 +559,10 @@ def test_version_print_last_released_on_nonrelease_branch( @pytest.mark.parametrize( "repo_result", [ - lazy_fixture(repo_w_trunk_only_angular_commits.__name__), + lazy_fixture(repo_w_trunk_only_conventional_commits.__name__), pytest.param( lazy_fixture( - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits_using_tag_format.__name__ + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits_using_tag_format.__name__ ), marks=pytest.mark.comprehensive, ), @@ -614,14 +614,14 @@ def test_version_print_last_released_tag_prints_correct_tag( "repo_result, commits", [ ( - lazy_fixture(repo_w_trunk_only_angular_commits.__name__), - lazy_fixture(angular_minor_commits.__name__), + lazy_fixture(repo_w_trunk_only_conventional_commits.__name__), + lazy_fixture(conventional_minor_commits.__name__), ), pytest.param( lazy_fixture( - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits_using_tag_format.__name__ + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits_using_tag_format.__name__ ), - lazy_fixture(angular_minor_commits.__name__), + lazy_fixture(conventional_minor_commits.__name__), marks=pytest.mark.comprehensive, ), ], @@ -676,7 +676,7 @@ def test_version_print_last_released_tag_prints_released_if_commits( @pytest.mark.parametrize( "repo_result", - [lazy_fixture(repo_w_no_tags_angular_commits.__name__)], + [lazy_fixture(repo_w_no_tags_conventional_commits.__name__)], ) def test_version_print_last_released_tag_prints_nothing_if_no_tags( repo_result: BuiltRepoResult, @@ -721,10 +721,10 @@ def test_version_print_last_released_tag_prints_nothing_if_no_tags( @pytest.mark.parametrize( "repo_result", [ - lazy_fixture(repo_w_trunk_only_angular_commits.__name__), + lazy_fixture(repo_w_trunk_only_conventional_commits.__name__), pytest.param( lazy_fixture( - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits_using_tag_format.__name__ + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits_using_tag_format.__name__ ), marks=pytest.mark.comprehensive, ), @@ -778,10 +778,10 @@ def test_version_print_last_released_tag_on_detached_head( @pytest.mark.parametrize( "repo_result", [ - lazy_fixture(repo_w_trunk_only_angular_commits.__name__), + lazy_fixture(repo_w_trunk_only_conventional_commits.__name__), pytest.param( lazy_fixture( - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits_using_tag_format.__name__ + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits_using_tag_format.__name__ ), marks=pytest.mark.comprehensive, ), @@ -836,8 +836,8 @@ def test_version_print_last_released_tag_on_nonrelease_branch( "repo_result, get_commit_def_fn", [ ( - lazy_fixture(repo_w_trunk_only_angular_commits.__name__), - lazy_fixture(get_commit_def_of_angular_commit.__name__), + lazy_fixture(repo_w_trunk_only_conventional_commits.__name__), + lazy_fixture(get_commit_def_of_conventional_commit.__name__), ) ], ) @@ -895,8 +895,8 @@ def test_version_print_next_version_fails_on_detached_head( "repo_result, get_commit_def_fn", [ ( - lazy_fixture(repo_w_trunk_only_angular_commits.__name__), - lazy_fixture(get_commit_def_of_angular_commit.__name__), + lazy_fixture(repo_w_trunk_only_conventional_commits.__name__), + lazy_fixture(get_commit_def_of_conventional_commit.__name__), ) ], ) diff --git a/tests/e2e/cmd_version/test_version_release_notes.py b/tests/e2e/cmd_version/test_version_release_notes.py index 36fdf0e63..c185b76f2 100644 --- a/tests/e2e/cmd_version/test_version_release_notes.py +++ b/tests/e2e/cmd_version/test_version_release_notes.py @@ -15,7 +15,7 @@ VERSION_SUBCMD, RepoActionStep, ) -from tests.fixtures.repos import repo_w_no_tags_angular_commits +from tests.fixtures.repos import repo_w_no_tags_conventional_commits from tests.fixtures.repos.trunk_based_dev.repo_w_no_tags import ( repo_w_no_tags_emoji_commits, repo_w_no_tags_scipy_commits, @@ -46,7 +46,7 @@ @pytest.mark.parametrize( "repo_result, next_release_version", [ - (lazy_fixture(repo_w_no_tags_angular_commits.__name__), "0.1.0"), + (lazy_fixture(repo_w_no_tags_conventional_commits.__name__), "0.1.0"), ], ) def test_custom_release_notes_template( @@ -113,7 +113,7 @@ def test_custom_release_notes_template( "project.license.text", # deprecated ] for repo_fixture_name in [ - repo_w_no_tags_angular_commits.__name__, + repo_w_no_tags_conventional_commits.__name__, repo_w_no_tags_emoji_commits.__name__, repo_w_no_tags_scipy_commits.__name__, ] diff --git a/tests/e2e/cmd_version/test_version_stamp.py b/tests/e2e/cmd_version/test_version_stamp.py index d052fcc9c..c95c6f813 100644 --- a/tests/e2e/cmd_version/test_version_stamp.py +++ b/tests/e2e/cmd_version/test_version_stamp.py @@ -14,10 +14,10 @@ from tests.const import EXAMPLE_PROJECT_NAME, MAIN_PROG_NAME, VERSION_SUBCMD from tests.fixtures.repos.trunk_based_dev.repo_w_no_tags import ( - repo_w_no_tags_angular_commits, + repo_w_no_tags_conventional_commits, ) from tests.fixtures.repos.trunk_based_dev.repo_w_prereleases import ( - repo_w_trunk_only_n_prereleases_angular_commits, + repo_w_trunk_only_n_prereleases_conventional_commits, ) from tests.util import ( assert_successful_exit_code, @@ -48,7 +48,7 @@ "repo_result, expected_new_version", [ ( - lazy_fixture(repo_w_trunk_only_n_prereleases_angular_commits.__name__), + lazy_fixture(repo_w_trunk_only_n_prereleases_conventional_commits.__name__), "0.3.0", ) ], @@ -141,7 +141,7 @@ def test_version_only_stamp_version( # ============================================================================== # -@pytest.mark.usefixtures(repo_w_no_tags_angular_commits.__name__) +@pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_stamp_version_variables_python( cli_runner: CliRunner, update_pyproject_toml: UpdatePyprojectTomlFn, @@ -174,7 +174,7 @@ def test_stamp_version_variables_python( assert new_version == version_py_after -@pytest.mark.usefixtures(repo_w_no_tags_angular_commits.__name__) +@pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_stamp_version_variables_yaml( cli_runner: CliRunner, update_pyproject_toml: UpdatePyprojectTomlFn, @@ -217,7 +217,7 @@ def test_stamp_version_variables_yaml( assert yaml.safe_load(orig_yaml) == resulting_yaml_obj -@pytest.mark.usefixtures(repo_w_no_tags_angular_commits.__name__) +@pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_stamp_version_variables_yaml_cff( cli_runner: CliRunner, update_pyproject_toml: UpdatePyprojectTomlFn, @@ -267,7 +267,7 @@ def test_stamp_version_variables_yaml_cff( assert yaml.safe_load(orig_yaml) == resulting_yaml_obj -@pytest.mark.usefixtures(repo_w_no_tags_angular_commits.__name__) +@pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_stamp_version_variables_json( cli_runner: CliRunner, update_pyproject_toml: UpdatePyprojectTomlFn, diff --git a/tests/e2e/cmd_version/test_version_strict.py b/tests/e2e/cmd_version/test_version_strict.py index 438adf571..c8dcb56a5 100644 --- a/tests/e2e/cmd_version/test_version_strict.py +++ b/tests/e2e/cmd_version/test_version_strict.py @@ -8,7 +8,7 @@ from semantic_release.cli.commands.main import main from tests.const import MAIN_PROG_NAME, VERSION_SUBCMD -from tests.fixtures.repos import repo_w_trunk_only_angular_commits +from tests.fixtures.repos import repo_w_trunk_only_conventional_commits from tests.util import assert_exit_code if TYPE_CHECKING: @@ -22,7 +22,7 @@ @pytest.mark.parametrize( "repo_result", - [lazy_fixture(repo_w_trunk_only_angular_commits.__name__)], + [lazy_fixture(repo_w_trunk_only_conventional_commits.__name__)], ) def test_version_already_released_when_strict( repo_result: BuiltRepoResult, @@ -71,7 +71,7 @@ def test_version_already_released_when_strict( @pytest.mark.parametrize( - "repo_result", [lazy_fixture(repo_w_trunk_only_angular_commits.__name__)] + "repo_result", [lazy_fixture(repo_w_trunk_only_conventional_commits.__name__)] ) def test_version_on_nonrelease_branch_when_strict( repo_result: BuiltRepoResult, diff --git a/tests/e2e/test_help.py b/tests/e2e/test_help.py index 4a5d7909a..a31454efd 100644 --- a/tests/e2e/test_help.py +++ b/tests/e2e/test_help.py @@ -12,7 +12,7 @@ from semantic_release.cli.commands.version import version from tests.const import MAIN_PROG_NAME, SUCCESS_EXIT_CODE -from tests.fixtures.repos import repo_w_trunk_only_angular_commits +from tests.fixtures.repos import repo_w_trunk_only_conventional_commits from tests.util import assert_exit_code if TYPE_CHECKING: @@ -85,7 +85,7 @@ def test_help_no_repo( (main, changelog, generate_config, publish, version), ids=lambda cmd: cmd.name, ) -@pytest.mark.usefixtures(repo_w_trunk_only_angular_commits.__name__) +@pytest.mark.usefixtures(repo_w_trunk_only_conventional_commits.__name__) def test_help_valid_config( help_option: str, command: Command, @@ -137,7 +137,7 @@ def test_help_invalid_config( help_option: str, command: Command, cli_runner: CliRunner, - repo_w_trunk_only_angular_commits: Repo, + repo_w_trunk_only_conventional_commits: Repo, update_pyproject_toml: UpdatePyprojectTomlFn, ): """ @@ -187,7 +187,7 @@ def test_help_invalid_config( ids=lambda cmd: cmd.name, ) @pytest.mark.parametrize( - "repo_result", [lazy_fixture(repo_w_trunk_only_angular_commits.__name__)] + "repo_result", [lazy_fixture(repo_w_trunk_only_conventional_commits.__name__)] ) def test_help_non_release_branch( help_option: str, diff --git a/tests/e2e/test_main.py b/tests/e2e/test_main.py index 6ada12a1f..361061021 100644 --- a/tests/e2e/test_main.py +++ b/tests/e2e/test_main.py @@ -14,8 +14,8 @@ from tests.const import MAIN_PROG_NAME, VERSION_SUBCMD from tests.fixtures import ( - repo_w_git_flow_w_alpha_prereleases_n_angular_commits, - repo_w_no_tags_angular_commits, + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits, + repo_w_no_tags_conventional_commits, ) from tests.util import assert_exit_code, assert_successful_exit_code @@ -46,7 +46,7 @@ def test_main_no_args_prints_help_text(cli_runner: CliRunner): @pytest.mark.parametrize( "repo_result", - [lazy_fixture(repo_w_git_flow_w_alpha_prereleases_n_angular_commits.__name__)], + [lazy_fixture(repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__)], ) def test_not_a_release_branch_exit_code( repo_result: BuiltRepoResult, cli_runner: CliRunner @@ -64,7 +64,7 @@ def test_not_a_release_branch_exit_code( @pytest.mark.parametrize( "repo_result", - [lazy_fixture(repo_w_git_flow_w_alpha_prereleases_n_angular_commits.__name__)], + [lazy_fixture(repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__)], ) def test_not_a_release_branch_exit_code_with_strict( repo_result: BuiltRepoResult, @@ -83,7 +83,7 @@ def test_not_a_release_branch_exit_code_with_strict( @pytest.mark.parametrize( "repo_result", - [lazy_fixture(repo_w_git_flow_w_alpha_prereleases_n_angular_commits.__name__)], + [lazy_fixture(repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__)], ) def test_not_a_release_branch_detached_head_exit_code( repo_result: BuiltRepoResult, @@ -129,7 +129,9 @@ def json_file_with_no_configuration_for_psr(tmp_path: Path) -> Path: return path -@pytest.mark.usefixtures(repo_w_git_flow_w_alpha_prereleases_n_angular_commits.__name__) +@pytest.mark.usefixtures( + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__ +) def test_default_config_is_used_when_none_in_toml_config_file( cli_runner: CliRunner, toml_file_with_no_configuration_for_psr: Path, @@ -149,7 +151,9 @@ def test_default_config_is_used_when_none_in_toml_config_file( assert_successful_exit_code(result, cli_cmd) -@pytest.mark.usefixtures(repo_w_git_flow_w_alpha_prereleases_n_angular_commits.__name__) +@pytest.mark.usefixtures( + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__ +) def test_default_config_is_used_when_none_in_json_config_file( cli_runner: CliRunner, json_file_with_no_configuration_for_psr: Path, @@ -169,7 +173,9 @@ def test_default_config_is_used_when_none_in_json_config_file( assert_successful_exit_code(result, cli_cmd) -@pytest.mark.usefixtures(repo_w_git_flow_w_alpha_prereleases_n_angular_commits.__name__) +@pytest.mark.usefixtures( + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__ +) def test_errors_when_config_file_does_not_exist_and_passed_explicitly( cli_runner: CliRunner, ): @@ -189,7 +195,7 @@ def test_errors_when_config_file_does_not_exist_and_passed_explicitly( assert "does not exist" in result.stderr -@pytest.mark.usefixtures(repo_w_no_tags_angular_commits.__name__) +@pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_errors_when_config_file_invalid_configuration( cli_runner: CliRunner, update_pyproject_toml: UpdatePyprojectTomlFn ): diff --git a/tests/fixtures/commit_parsers.py b/tests/fixtures/commit_parsers.py index ac0658d67..ff2357cd3 100644 --- a/tests/fixtures/commit_parsers.py +++ b/tests/fixtures/commit_parsers.py @@ -1,17 +1,17 @@ import pytest from semantic_release.commit_parser import ( - AngularCommitParser, - AngularParserOptions, + ConventionalCommitParser, + ConventionalCommitParserOptions, EmojiCommitParser, EmojiParserOptions, ) from tests.const import ( - ANGULAR_COMMITS_CHORE, - ANGULAR_COMMITS_MAJOR, - ANGULAR_COMMITS_MINOR, - ANGULAR_COMMITS_PATCH, + CONVENTIONAL_COMMITS_CHORE, + CONVENTIONAL_COMMITS_MAJOR, + CONVENTIONAL_COMMITS_MINOR, + CONVENTIONAL_COMMITS_PATCH, EMOJI_COMMITS_CHORE, EMOJI_COMMITS_MAJOR, EMOJI_COMMITS_MINOR, @@ -22,15 +22,15 @@ @pytest.fixture(scope="session") -def default_angular_parser() -> AngularCommitParser: - return AngularCommitParser() +def default_conventional_parser() -> ConventionalCommitParser: + return ConventionalCommitParser() @pytest.fixture(scope="session") -def default_angular_parser_options( - default_angular_parser: AngularCommitParser, -) -> AngularParserOptions: - return default_angular_parser.get_default_options() +def default_conventional_parser_options( + default_conventional_parser: ConventionalCommitParser, +) -> ConventionalCommitParserOptions: + return default_conventional_parser.get_default_options() @pytest.fixture(scope="session") @@ -46,23 +46,23 @@ def default_emoji_parser_options( @pytest.fixture(scope="session") -def angular_major_commits(): - return ANGULAR_COMMITS_MAJOR +def conventional_major_commits(): + return CONVENTIONAL_COMMITS_MAJOR @pytest.fixture(scope="session") -def angular_minor_commits(): - return ANGULAR_COMMITS_MINOR +def conventional_minor_commits(): + return CONVENTIONAL_COMMITS_MINOR @pytest.fixture(scope="session") -def angular_patch_commits(): - return ANGULAR_COMMITS_PATCH +def conventional_patch_commits(): + return CONVENTIONAL_COMMITS_PATCH @pytest.fixture(scope="session") -def angular_chore_commits(): - return ANGULAR_COMMITS_CHORE +def conventional_chore_commits(): + return CONVENTIONAL_COMMITS_CHORE @pytest.fixture(scope="session") diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index b1dc9858d..7575ec96f 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -13,7 +13,7 @@ import semantic_release from semantic_release.commit_parser import ( - AngularCommitParser, + ConventionalCommitParser, EmojiCommitParser, ScipyCommitParser, ) @@ -452,17 +452,17 @@ def _set_allow_zero_version(flag: bool) -> None: @pytest.fixture(scope="session") -def use_angular_parser( +def use_conventional_parser( update_pyproject_toml: UpdatePyprojectTomlFn, pyproject_toml_config_option_parser: str, ) -> UseParserFn: - """Modify the configuration file to use the Angular parser.""" + """Modify the configuration file to use the Conventional parser.""" - def _use_angular_parser() -> type[CommitParser]: - update_pyproject_toml(pyproject_toml_config_option_parser, "angular") - return AngularCommitParser + def _use_conventional_parser() -> type[CommitParser]: + update_pyproject_toml(pyproject_toml_config_option_parser, "conventional") + return ConventionalCommitParser - return _use_angular_parser + return _use_conventional_parser @pytest.fixture(scope="session") diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index c5f224c8c..0c8faeeb5 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -15,9 +15,9 @@ from git import Actor, Repo from semantic_release.cli.config import ChangelogOutputFormat -from semantic_release.commit_parser.angular import ( - AngularCommitParser, - AngularParserOptions, +from semantic_release.commit_parser.conventional import ( + ConventionalCommitParser, + ConventionalCommitParserOptions, ) from semantic_release.commit_parser.emoji import EmojiCommitParser, EmojiParserOptions from semantic_release.commit_parser.scipy import ScipyCommitParser, ScipyParserOptions @@ -74,7 +74,7 @@ UseParserFn, ) - CommitConvention = Literal["angular", "emoji", "scipy"] + CommitConvention = Literal["conventional", "emoji", "scipy"] VersionStr = str CommitMsg = str DatetimeISOStr = str @@ -244,7 +244,7 @@ def __call__( ) -> CommitDef: ... class CommitSpec(TypedDict): - angular: str + conventional: str emoji: str scipy: str datetime: NotRequired[DatetimeISOStr] @@ -516,11 +516,11 @@ def example_git_https_url(): @pytest.fixture(scope="session") -def get_commit_def_of_angular_commit( - default_angular_parser: AngularCommitParser, +def get_commit_def_of_conventional_commit( + default_conventional_parser: ConventionalCommitParser, ) -> GetCommitDefFn: - def _get_commit_def_of_angular_commit(msg: str) -> CommitDef: - if not (parsed_result := default_angular_parser.parse_message(msg)): + def _get_commit_def_of_conventional_commit(msg: str) -> CommitDef: + if not (parsed_result := default_conventional_parser.parse_message(msg)): return { "msg": msg, "type": "unknown", @@ -549,7 +549,7 @@ def _get_commit_def_of_angular_commit(msg: str) -> CommitDef: "include_in_changelog": True, } - return _get_commit_def_of_angular_commit + return _get_commit_def_of_conventional_commit @pytest.fixture(scope="session") @@ -982,7 +982,7 @@ def build_configured_base_repo( # noqa: C901 use_gitlab_hvcs: UseHvcsFn, use_gitea_hvcs: UseHvcsFn, use_bitbucket_hvcs: UseHvcsFn, - use_angular_parser: UseParserFn, + use_conventional_parser: UseParserFn, use_emoji_parser: UseParserFn, use_scipy_parser: UseParserFn, use_custom_parser: UseCustomParserFn, @@ -999,7 +999,7 @@ def build_configured_base_repo( # noqa: C901 def _build_configured_base_repo( # noqa: C901 dest_dir: Path | str, - commit_type: str = "angular", + commit_type: str = "conventional", hvcs_client_name: str = "github", hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, @@ -1015,8 +1015,8 @@ def _build_configured_base_repo( # noqa: C901 # Make sure we are in the dest directory with temporary_working_directory(dest_dir): # Set parser configuration - if commit_type == "angular": - use_angular_parser() + if commit_type == "conventional": + use_conventional_parser() elif commit_type == "emoji": use_emoji_parser() elif commit_type == "scipy": @@ -1089,17 +1089,18 @@ def _build_configured_base_repo( # noqa: C901 @pytest.fixture(scope="session") def separate_squashed_commit_def( - default_angular_parser: AngularCommitParser, + default_conventional_parser: ConventionalCommitParser, default_emoji_parser: EmojiCommitParser, default_scipy_parser: ScipyCommitParser, ) -> SeparateSquashedCommitDefFn: message_parsers: dict[ - CommitConvention, AngularCommitParser | EmojiCommitParser | ScipyCommitParser + CommitConvention, + ConventionalCommitParser | EmojiCommitParser | ScipyCommitParser, ] = { - "angular": AngularCommitParser( - options=AngularParserOptions( + "conventional": ConventionalCommitParser( + options=ConventionalCommitParserOptions( **{ - **default_angular_parser.options.__dict__, + **default_conventional_parser.options.__dict__, "parse_squash_commits": True, } ) @@ -1125,7 +1126,7 @@ def separate_squashed_commit_def( def _separate_squashed_commit_def( squashed_commit_def: CommitDef, ) -> list[CommitDef]: - commit_type: CommitConvention = "angular" + commit_type: CommitConvention = "conventional" for parser_name, parser in message_parsers.items(): if squashed_commit_def["type"] in parser.options.allowed_tags: commit_type = parser_name @@ -1177,13 +1178,13 @@ def _separate_squashed_commit_def( @pytest.fixture(scope="session") def convert_commit_spec_to_commit_def( - get_commit_def_of_angular_commit: GetCommitDefFn, + get_commit_def_of_conventional_commit: GetCommitDefFn, get_commit_def_of_emoji_commit: GetCommitDefFn, get_commit_def_of_scipy_commit: GetCommitDefFn, stable_now_date: datetime, ) -> ConvertCommitSpecToCommitDefFn: message_parsers: dict[CommitConvention, GetCommitDefFn] = { - "angular": get_commit_def_of_angular_commit, + "conventional": get_commit_def_of_conventional_commit, "emoji": get_commit_def_of_emoji_commit, "scipy": get_commit_def_of_scipy_commit, } diff --git a/tests/fixtures/repos/git_flow/repo_w_1_release_channel.py b/tests/fixtures/repos/git_flow/repo_w_1_release_channel.py index 75b614245..c624a7965 100644 --- a/tests/fixtures/repos/git_flow/repo_w_1_release_channel.py +++ b/tests/fixtures/repos/git_flow/repo_w_1_release_channel.py @@ -143,7 +143,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=DEV_BRANCH_NAME, tgt_branch_name=DEFAULT_BRANCH_NAME, ), @@ -200,7 +200,7 @@ def _get_repo_from_defintion( # only one commit to start the main branch convert_commit_spec_to_commit_def( { - "angular": INITIAL_COMMIT_MESSAGE, + "conventional": INITIAL_COMMIT_MESSAGE, "emoji": INITIAL_COMMIT_MESSAGE, "scipy": INITIAL_COMMIT_MESSAGE, "datetime": next(commit_timestamp_gen), @@ -237,7 +237,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "feat: add new feature", + "conventional": "feat: add new feature", "emoji": ":sparkles: add new feature", "scipy": "ENH: add new feature", "datetime": next(commit_timestamp_gen), @@ -259,7 +259,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=FEAT_BRANCH_1_NAME, tgt_branch_name=DEV_BRANCH_NAME, ), @@ -324,7 +324,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "feat: add a new feature", + "conventional": "feat: add a new feature", "emoji": ":sparkles: add a new feature", "scipy": "ENH: add a new feature", "datetime": next(commit_timestamp_gen), @@ -346,7 +346,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=FEAT_BRANCH_2_NAME, tgt_branch_name=DEV_BRANCH_NAME, ), @@ -411,7 +411,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": str.join( + "conventional": str.join( "\n\n", [ "feat: add revolutionary feature", @@ -451,7 +451,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=FEAT_BRANCH_3_NAME, tgt_branch_name=DEV_BRANCH_NAME, ), @@ -516,7 +516,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "fix: correct a bug", + "conventional": "fix: correct a bug", "emoji": ":bug: correct a bug", "scipy": "BUG: correct a bug", "datetime": next(commit_timestamp_gen), @@ -538,7 +538,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=FIX_BRANCH_1_NAME, tgt_branch_name=DEV_BRANCH_NAME, ), @@ -603,7 +603,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "fix: correct another bug", + "conventional": "fix: correct another bug", "emoji": ":bug: correct another bug", "scipy": "BUG: correct another bug", "datetime": next(commit_timestamp_gen), @@ -625,7 +625,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=FIX_BRANCH_2_NAME, tgt_branch_name=DEV_BRANCH_NAME, ), @@ -659,7 +659,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "feat(cli): add new config cli command", + "conventional": "feat(cli): add new config cli command", "emoji": ":sparkles: (cli) add new config cli command", "scipy": "ENH(cli): add new config cli command", "datetime": next(commit_timestamp_gen), @@ -681,7 +681,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=FEAT_BRANCH_4_NAME, tgt_branch_name=DEV_BRANCH_NAME, ), @@ -771,13 +771,13 @@ def _build_repo(cached_repo_path: Path) -> Sequence[RepoActions]: @pytest.fixture -def repo_w_git_flow_angular_commits( +def repo_w_git_flow_conventional_commits( build_git_flow_repo_w_1_release_channels: BuildSpecificRepoFn, example_project_git_repo: ExProjectGitRepoFn, example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> BuiltRepoResult: - repo_name = repo_w_git_flow_angular_commits.__name__ + repo_name = repo_w_git_flow_conventional_commits.__name__ commit_type: CommitConvention = repo_name.split("_")[-2] # type: ignore[assignment] return { diff --git a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py index 52e8cdb36..f4a6005bc 100644 --- a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py @@ -143,7 +143,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=DEV_BRANCH_NAME, tgt_branch_name=DEFAULT_BRANCH_NAME, ), @@ -206,7 +206,7 @@ def _get_repo_from_defintion( # only one commit to start the main branch convert_commit_spec_to_commit_def( { - "angular": INITIAL_COMMIT_MESSAGE, + "conventional": INITIAL_COMMIT_MESSAGE, "emoji": INITIAL_COMMIT_MESSAGE, "scipy": INITIAL_COMMIT_MESSAGE, "datetime": next(commit_timestamp_gen), @@ -243,7 +243,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "feat: add new feature", + "conventional": "feat: add new feature", "emoji": ":sparkles: add new feature", "scipy": "ENH: add new feature", "datetime": next(commit_timestamp_gen), @@ -265,7 +265,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=FEAT_BRANCH_1_NAME, tgt_branch_name=DEV_BRANCH_NAME, ), @@ -330,7 +330,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "feat: add a new feature", + "conventional": "feat: add a new feature", "emoji": ":sparkles: add a new feature", "scipy": "ENH: add a new feature", "datetime": next(commit_timestamp_gen), @@ -370,7 +370,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": str.join( + "conventional": str.join( "\n\n", [ "feat: add revolutionary feature", @@ -428,7 +428,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "feat: add some more text", + "conventional": "feat: add some more text", "emoji": ":sparkles: add some more text", "scipy": "ENH: add some more text", "datetime": next(commit_timestamp_gen), @@ -450,7 +450,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=FEAT_BRANCH_2_NAME, tgt_branch_name=DEV_BRANCH_NAME, ), @@ -515,7 +515,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "feat(cli): add new config cli command", + "conventional": "feat(cli): add new config cli command", "emoji": ":sparkles: (cli) add new config cli command", "scipy": "ENH(cli): add new config cli command", "datetime": next(commit_timestamp_gen), @@ -537,7 +537,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=FEAT_BRANCH_3_NAME, tgt_branch_name=DEV_BRANCH_NAME, ), @@ -602,7 +602,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "fix(config): fixed configuration generation", + "conventional": "fix(config): fixed configuration generation", "emoji": ":bug: (config) fixed configuration generation", "scipy": "MAINT(config): fixed configuration generation", "datetime": next(commit_timestamp_gen), @@ -624,7 +624,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=FIX_BRANCH_1_NAME, tgt_branch_name=DEV_BRANCH_NAME, ), @@ -689,7 +689,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "feat: add some more text", + "conventional": "feat: add some more text", "emoji": ":sparkles: add some more text", "scipy": "ENH: add some more text", "datetime": next(commit_timestamp_gen), @@ -729,14 +729,14 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "fix(scope): correct some text", + "conventional": "fix(scope): correct some text", "emoji": ":bug: (scope) correct some text", "scipy": "MAINT(scope): correct some text", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, { - "angular": "feat(scope): add some more text", + "conventional": "feat(scope): add some more text", "emoji": ":sparkles:(scope) add some more text", "scipy": "ENH(scope): add some more text", "datetime": next(commit_timestamp_gen), @@ -811,13 +811,13 @@ def _build_repo(cached_repo_path: Path) -> Sequence[RepoActions]: @pytest.fixture -def repo_w_git_flow_w_alpha_prereleases_n_angular_commits( +def repo_w_git_flow_w_alpha_prereleases_n_conventional_commits( build_git_flow_repo_w_2_release_channels: BuildSpecificRepoFn, example_project_git_repo: ExProjectGitRepoFn, example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> BuiltRepoResult: - repo_name = repo_w_git_flow_w_alpha_prereleases_n_angular_commits.__name__ + repo_name = repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__ commit_type: CommitConvention = repo_name.split("_")[-2] # type: ignore[assignment] return { diff --git a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py index 32f22a7c6..10cc98ff8 100644 --- a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py @@ -145,7 +145,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=DEV_BRANCH_NAME, tgt_branch_name=DEFAULT_BRANCH_NAME, ), @@ -214,7 +214,7 @@ def _get_repo_from_defintion( # only one commit to start the main branch convert_commit_spec_to_commit_def( { - "angular": INITIAL_COMMIT_MESSAGE, + "conventional": INITIAL_COMMIT_MESSAGE, "emoji": INITIAL_COMMIT_MESSAGE, "scipy": INITIAL_COMMIT_MESSAGE, "datetime": next(commit_timestamp_gen), @@ -251,7 +251,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "feat: add new feature", + "conventional": "feat: add new feature", "emoji": ":sparkles: add new feature", "scipy": "ENH: add new feature", "datetime": next(commit_timestamp_gen), @@ -273,7 +273,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=FEAT_BRANCH_1_NAME, tgt_branch_name=DEV_BRANCH_NAME, ), @@ -338,7 +338,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "feat: add a new feature", + "conventional": "feat: add a new feature", "emoji": ":sparkles: add a new feature", "scipy": "ENH: add a new feature", "datetime": next(commit_timestamp_gen), @@ -378,7 +378,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": str.join( + "conventional": str.join( "\n\n", [ "feat: add revolutionary feature", @@ -441,7 +441,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=FEAT_BRANCH_2_NAME, tgt_branch_name=DEV_BRANCH_NAME, ), @@ -529,7 +529,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "feat(cli): add new config cli command", + "conventional": "feat(cli): add new config cli command", "emoji": ":sparkles: (cli) add new config cli command", "scipy": "ENH(cli): add new config cli command", "datetime": next(commit_timestamp_gen), @@ -569,7 +569,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "feat(config): add new config option", + "conventional": "feat(config): add new config option", "emoji": ":sparkles: (config) add new config option", "scipy": "ENH(config): add new config option", "datetime": next(commit_timestamp_gen), @@ -614,7 +614,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=FEAT_BRANCH_3_NAME, tgt_branch_name=DEV_BRANCH_NAME, ), @@ -648,7 +648,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "fix(cli): fix config cli command", + "conventional": "fix(cli): fix config cli command", "emoji": ":bug: (cli) fix config cli command", "scipy": "BUG(cli): fix config cli command", "datetime": next(commit_timestamp_gen), @@ -670,7 +670,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=FIX_BRANCH_1_NAME, tgt_branch_name=DEV_BRANCH_NAME, ), @@ -727,7 +727,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "fix(config): fix config option", + "conventional": "fix(config): fix config option", "emoji": ":bug: (config) fix config option", "scipy": "BUG(config): fix config option", "datetime": next(commit_timestamp_gen), @@ -749,7 +749,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=FIX_BRANCH_2_NAME, tgt_branch_name=DEV_BRANCH_NAME, ), @@ -862,7 +862,7 @@ def _build_repo(cached_repo_path: Path) -> Sequence[RepoActions]: @pytest.fixture -def repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits_using_tag_format( +def repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits_using_tag_format( build_repo_from_definition: BuildRepoFromDefinitionFn, get_repo_definition_4_git_flow_repo_w_3_release_channels: GetRepoDefinitionFn, get_cached_repo_data: GetCachedRepoDataFn, @@ -875,13 +875,13 @@ def repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits_using_tag_format( def _build_repo(cached_repo_path: Path) -> Sequence[RepoActions]: repo_construction_steps = ( get_repo_definition_4_git_flow_repo_w_3_release_channels( - commit_type="angular", + commit_type="conventional", tag_format_str="submod-v{version}", ) ) return build_repo_from_definition(cached_repo_path, repo_construction_steps) - repo_name = repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits_using_tag_format.__name__ + repo_name = repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits_using_tag_format.__name__ build_repo_or_copy_cache( repo_name=repo_name, @@ -900,13 +900,13 @@ def _build_repo(cached_repo_path: Path) -> Sequence[RepoActions]: @pytest.fixture -def repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits( +def repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits( build_git_flow_repo_w_3_release_channels: BuildSpecificRepoFn, example_project_git_repo: ExProjectGitRepoFn, example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> BuiltRepoResult: - repo_name = repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits.__name__ + repo_name = repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits.__name__ commit_type: CommitConvention = repo_name.split("_")[-2] # type: ignore[assignment] return { diff --git a/tests/fixtures/repos/git_flow/repo_w_4_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_4_release_channels.py index 204a41ea3..d6abbb5df 100644 --- a/tests/fixtures/repos/git_flow/repo_w_4_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_4_release_channels.py @@ -169,7 +169,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=DEV_BRANCH_NAME, tgt_branch_name=BETA_BRANCH_NAME, ), @@ -196,7 +196,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=BETA_BRANCH_NAME, tgt_branch_name=DEFAULT_BRANCH_NAME, ), @@ -270,7 +270,7 @@ def _get_repo_from_defintion( # only one commit to start the main branch convert_commit_spec_to_commit_def( { - "angular": INITIAL_COMMIT_MESSAGE, + "conventional": INITIAL_COMMIT_MESSAGE, "emoji": INITIAL_COMMIT_MESSAGE, "scipy": INITIAL_COMMIT_MESSAGE, "datetime": next(commit_timestamp_gen), @@ -316,7 +316,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "feat: add new feature", + "conventional": "feat: add new feature", "emoji": ":sparkles: add new feature", "scipy": "ENH: add new feature", "datetime": next(commit_timestamp_gen), @@ -338,7 +338,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=FEAT_BRANCH_1_NAME, tgt_branch_name=DEV_BRANCH_NAME, ), @@ -411,7 +411,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "fix(cli): fix config cli command", + "conventional": "fix(cli): fix config cli command", "emoji": ":bug: (cli) fix config cli command", "scipy": "BUG(cli): fix config cli command", "datetime": next(commit_timestamp_gen), @@ -433,7 +433,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=FIX_BRANCH_1_NAME, tgt_branch_name=DEV_BRANCH_NAME, ), @@ -521,7 +521,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "fix(config): fix config option", + "conventional": "fix(config): fix config option", "emoji": ":bug: (config) fix config option", "scipy": "BUG(config): fix config option", "datetime": next(commit_timestamp_gen), @@ -543,7 +543,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=FIX_BRANCH_2_NAME, tgt_branch_name=DEV_BRANCH_NAME, ), @@ -632,7 +632,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "feat(feat-2): add another primary feature", + "conventional": "feat(feat-2): add another primary feature", "emoji": ":sparkles: (feat-2) add another primary feature", "scipy": "ENH(feat-2): add another primary feature", "datetime": next(commit_timestamp_gen), @@ -677,7 +677,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_git( + "conventional": format_merge_commit_msg_git( branch_name=FEAT_BRANCH_2_NAME, tgt_branch_name=DEV_BRANCH_NAME, ), @@ -820,13 +820,15 @@ def _build_repo(cached_repo_path: Path) -> Sequence[RepoActions]: @pytest.fixture -def repo_w_git_flow_w_beta_alpha_rev_prereleases_n_angular_commits( +def repo_w_git_flow_w_beta_alpha_rev_prereleases_n_conventional_commits( build_git_flow_repo_w_4_release_channels: BuildSpecificRepoFn, example_project_git_repo: ExProjectGitRepoFn, example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> BuiltRepoResult: - repo_name = repo_w_git_flow_w_beta_alpha_rev_prereleases_n_angular_commits.__name__ + repo_name = ( + repo_w_git_flow_w_beta_alpha_rev_prereleases_n_conventional_commits.__name__ + ) commit_type: CommitConvention = repo_name.split("_")[-2] # type: ignore[assignment] return { diff --git a/tests/fixtures/repos/github_flow/repo_w_default_release.py b/tests/fixtures/repos/github_flow/repo_w_default_release.py index 3e572499b..ce8877dfe 100644 --- a/tests/fixtures/repos/github_flow/repo_w_default_release.py +++ b/tests/fixtures/repos/github_flow/repo_w_default_release.py @@ -152,7 +152,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": INITIAL_COMMIT_MESSAGE, + "conventional": INITIAL_COMMIT_MESSAGE, "emoji": INITIAL_COMMIT_MESSAGE, "scipy": INITIAL_COMMIT_MESSAGE, "datetime": next(commit_timestamp_gen), @@ -161,7 +161,7 @@ def _get_repo_from_defintion( ), }, { - "angular": "feat: add new feature", + "conventional": "feat: add new feature", "emoji": ":sparkles: add new feature", "scipy": "ENH: add new feature", "datetime": next(commit_timestamp_gen), @@ -193,7 +193,7 @@ def _get_repo_from_defintion( fix_branch_1_commits: Sequence[CommitSpec] = [ { - "angular": "fix(cli): add missing text", + "conventional": "fix(cli): add missing text", "emoji": ":bug: add missing text", "scipy": "MAINT: add missing text", "datetime": next(commit_timestamp_gen), @@ -232,19 +232,19 @@ def _get_repo_from_defintion( # simulate separate work by another person at same time as the fix branch feat_branch_1_commits: Sequence[CommitSpec] = [ { - "angular": "feat(cli): add cli interface", + "conventional": "feat(cli): add cli interface", "emoji": ":sparkles: add cli interface", "scipy": "ENH: add cli interface", "datetime": next(commit_timestamp_gen), }, { - "angular": "test(cli): add cli tests", + "conventional": "test(cli): add cli tests", "emoji": ":checkmark: add cli tests", "scipy": "TST: add cli tests", "datetime": next(commit_timestamp_gen), }, { - "angular": "docs(cli): add cli documentation", + "conventional": "docs(cli): add cli documentation", "emoji": ":memo: add cli documentation", "scipy": "DOC: add cli documentation", "datetime": next(commit_timestamp_gen), @@ -282,7 +282,7 @@ def _get_repo_from_defintion( new_version = "1.0.1" - all_commit_types: list[CommitConvention] = ["angular", "emoji", "scipy"] + all_commit_types: list[CommitConvention] = ["conventional", "emoji", "scipy"] fix_branch_pr_number = next(pr_num_gen) fix_branch_squash_commit_spec: CommitSpec = { **{ # type: ignore[typeddict-item] @@ -431,13 +431,15 @@ def _build_repo(cached_repo_path: Path) -> Sequence[RepoActions]: @pytest.fixture -def repo_w_github_flow_w_default_release_channel_angular_commits( +def repo_w_github_flow_w_default_release_channel_conventional_commits( build_repo_w_github_flow_w_default_release_channel: BuildSpecificRepoFn, example_project_git_repo: ExProjectGitRepoFn, example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> BuiltRepoResult: - repo_name = repo_w_github_flow_w_default_release_channel_angular_commits.__name__ + repo_name = ( + repo_w_github_flow_w_default_release_channel_conventional_commits.__name__ + ) commit_type: CommitConvention = repo_name.split("_")[-2] # type: ignore[assignment] return { diff --git a/tests/fixtures/repos/github_flow/repo_w_release_channels.py b/tests/fixtures/repos/github_flow/repo_w_release_channels.py index 843aba506..07be6eb5a 100644 --- a/tests/fixtures/repos/github_flow/repo_w_release_channels.py +++ b/tests/fixtures/repos/github_flow/repo_w_release_channels.py @@ -158,7 +158,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": INITIAL_COMMIT_MESSAGE, + "conventional": INITIAL_COMMIT_MESSAGE, "emoji": INITIAL_COMMIT_MESSAGE, "scipy": INITIAL_COMMIT_MESSAGE, "datetime": next(commit_timestamp_gen), @@ -167,7 +167,7 @@ def _get_repo_from_defintion( ), }, { - "angular": "feat: add new feature", + "conventional": "feat: add new feature", "emoji": ":sparkles: add new feature", "scipy": "ENH: add new feature", "datetime": next(commit_timestamp_gen), @@ -216,7 +216,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "fix: correct some text", + "conventional": "fix: correct some text", "emoji": ":bug: correct some text", "scipy": "MAINT: correct some text", "datetime": next(commit_timestamp_gen), @@ -256,7 +256,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "fix: adjust text to resolve", + "conventional": "fix: adjust text to resolve", "emoji": ":bug: adjust text to resolve", "scipy": "MAINT: adjust text to resolve", "datetime": next(commit_timestamp_gen), @@ -302,7 +302,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_github( + "conventional": format_merge_commit_msg_github( pr_number=fix_branch_pr_number, branch_name=FIX_BRANCH_1_NAME, ), @@ -360,7 +360,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "feat(cli): add cli interface", + "conventional": "feat(cli): add cli interface", "emoji": ":sparkles: add cli interface", "scipy": "ENH: add cli interface", "datetime": next(commit_timestamp_gen), @@ -406,7 +406,7 @@ def _get_repo_from_defintion( "fast_forward": False, "commit_def": convert_commit_spec_to_commit_def( { - "angular": format_merge_commit_msg_github( + "conventional": format_merge_commit_msg_github( pr_number=feat_branch_pr_number, branch_name=FEAT_BRANCH_1_NAME, ), @@ -489,13 +489,15 @@ def _build_repo(cached_repo_path: Path) -> Sequence[RepoActions]: @pytest.fixture -def repo_w_github_flow_w_feature_release_channel_angular_commits( +def repo_w_github_flow_w_feature_release_channel_conventional_commits( build_repo_w_github_flow_w_feature_release_channel: BuildSpecificRepoFn, example_project_git_repo: ExProjectGitRepoFn, example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> BuiltRepoResult: - repo_name = repo_w_github_flow_w_feature_release_channel_angular_commits.__name__ + repo_name = ( + repo_w_github_flow_w_feature_release_channel_conventional_commits.__name__ + ) commit_type: CommitConvention = repo_name.split("_")[-2] # type: ignore[assignment] return { diff --git a/tests/fixtures/repos/repo_initial_commit.py b/tests/fixtures/repos/repo_initial_commit.py index 92a64cf1e..c6ffd952b 100644 --- a/tests/fixtures/repos/repo_initial_commit.py +++ b/tests/fixtures/repos/repo_initial_commit.py @@ -106,7 +106,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": INITIAL_COMMIT_MESSAGE, + "conventional": INITIAL_COMMIT_MESSAGE, "emoji": INITIAL_COMMIT_MESSAGE, "scipy": INITIAL_COMMIT_MESSAGE, "datetime": stable_now_date().isoformat( @@ -194,7 +194,7 @@ def repo_w_initial_commit( return { "definition": build_repo_w_initial_commit( repo_name=repo_name, - commit_type="angular", # not used but required + commit_type="conventional", # not used but required dest_dir=example_project_dir, ), "repo": example_project_git_repo(), diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support.py b/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support.py index 3353024c0..c7a33cc16 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support.py @@ -149,7 +149,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": INITIAL_COMMIT_MESSAGE, + "conventional": INITIAL_COMMIT_MESSAGE, "emoji": INITIAL_COMMIT_MESSAGE, "scipy": INITIAL_COMMIT_MESSAGE, "datetime": next(commit_timestamp_gen), @@ -158,7 +158,7 @@ def _get_repo_from_defintion( ), }, { - "angular": "feat: add new feature", + "conventional": "feat: add new feature", "emoji": ":sparkles: add new feature", "scipy": "ENH: add new feature", "datetime": next(commit_timestamp_gen), @@ -198,7 +198,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "fix: correct some text", + "conventional": "fix: correct some text", "emoji": ":bug: correct some text", "scipy": "MAINT: correct some text", "datetime": next(commit_timestamp_gen), @@ -251,7 +251,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": str.join( + "conventional": str.join( "\n\n", [ "feat: add revolutionary feature", @@ -313,7 +313,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "fix: correct critical bug", + "conventional": "fix: correct critical bug", "emoji": ":bug: correct critical bug", "scipy": "MAINT: correct critical bug", "datetime": next(commit_timestamp_gen), @@ -401,13 +401,13 @@ def _build_repo(cached_repo_path: Path) -> Sequence[RepoActions]: @pytest.fixture -def repo_w_trunk_only_dual_version_spt_angular_commits( +def repo_w_trunk_only_dual_version_spt_conventional_commits( build_trunk_only_repo_w_dual_version_support: BuildSpecificRepoFn, example_project_git_repo: ExProjectGitRepoFn, example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> BuiltRepoResult: - repo_name = repo_w_trunk_only_dual_version_spt_angular_commits.__name__ + repo_name = repo_w_trunk_only_dual_version_spt_conventional_commits.__name__ commit_type: CommitConvention = repo_name.split("_")[-2] # type: ignore[assignment] return { diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support_w_prereleases.py b/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support_w_prereleases.py index 33a827dab..2576ec510 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support_w_prereleases.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support_w_prereleases.py @@ -149,7 +149,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": INITIAL_COMMIT_MESSAGE, + "conventional": INITIAL_COMMIT_MESSAGE, "emoji": INITIAL_COMMIT_MESSAGE, "scipy": INITIAL_COMMIT_MESSAGE, "datetime": next(commit_timestamp_gen), @@ -158,7 +158,7 @@ def _get_repo_from_defintion( ), }, { - "angular": "feat: add new feature", + "conventional": "feat: add new feature", "emoji": ":sparkles: add new feature", "scipy": "ENH: add new feature", "datetime": next(commit_timestamp_gen), @@ -198,7 +198,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "fix: correct some text", + "conventional": "fix: correct some text", "emoji": ":bug: correct some text", "scipy": "MAINT: correct some text", "datetime": next(commit_timestamp_gen), @@ -251,7 +251,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": str.join( + "conventional": str.join( "\n\n", [ "feat: add revolutionary feature", @@ -314,7 +314,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "fix: correct critical bug", + "conventional": "fix: correct critical bug", "emoji": ":bug: correct critical bug", "scipy": "MAINT: correct critical bug", "datetime": next(commit_timestamp_gen), @@ -355,7 +355,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "fix: resolve critical bug", + "conventional": "fix: resolve critical bug", "emoji": ":bug: resolve critical bug", "scipy": "MAINT: resolve critical bug", "datetime": next(commit_timestamp_gen), @@ -396,7 +396,7 @@ def _get_repo_from_defintion( # "commits": convert_commit_specs_to_commit_defs( # [ # { - # "angular": "docs: update documentation regarding critical bug", + # "conventional": "docs: update documentation regarding critical bug", # "emoji": ":books: update documentation regarding critical bug", # "scipy": "DOC: update documentation regarding critical bug", # "datetime": next(commit_timestamp_gen), @@ -484,14 +484,14 @@ def _build_repo(cached_repo_path: Path) -> Sequence[RepoActions]: @pytest.fixture -def repo_w_trunk_only_dual_version_spt_w_prereleases_angular_commits( +def repo_w_trunk_only_dual_version_spt_w_prereleases_conventional_commits( build_trunk_only_repo_w_dual_version_spt_w_prereleases: BuildSpecificRepoFn, example_project_git_repo: ExProjectGitRepoFn, example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> BuiltRepoResult: repo_name = ( - repo_w_trunk_only_dual_version_spt_w_prereleases_angular_commits.__name__ + repo_w_trunk_only_dual_version_spt_w_prereleases_conventional_commits.__name__ ) commit_type: CommitConvention = repo_name.split("_")[-2] # type: ignore[assignment] diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py index 45c3b40dd..65acc4d50 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py @@ -119,7 +119,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": INITIAL_COMMIT_MESSAGE, + "conventional": INITIAL_COMMIT_MESSAGE, "emoji": INITIAL_COMMIT_MESSAGE, "scipy": INITIAL_COMMIT_MESSAGE, "datetime": next(commit_timestamp_gen), @@ -128,21 +128,21 @@ def _get_repo_from_defintion( ), }, { - "angular": "feat: add new feature", + "conventional": "feat: add new feature", "emoji": ":sparkles: add new feature", "scipy": "ENH: add new feature", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, { - "angular": "fix: correct some text", + "conventional": "fix: correct some text", "emoji": ":bug: correct some text", "scipy": "MAINT: correct some text", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, { - "angular": "fix: correct more text", + "conventional": "fix: correct more text", "emoji": ":bug: correct more text", "scipy": "MAINT: correct more text", "datetime": next(commit_timestamp_gen), @@ -215,7 +215,7 @@ def _build_repo(cached_repo_path: Path) -> Sequence[RepoActions]: @pytest.fixture -def repo_w_no_tags_angular_commits_using_tag_format( +def repo_w_no_tags_conventional_commits_using_tag_format( build_repo_from_definition: BuildRepoFromDefinitionFn, get_repo_definition_4_trunk_only_repo_w_no_tags: GetRepoDefinitionFn, get_cached_repo_data: GetCachedRepoDataFn, @@ -230,7 +230,7 @@ def repo_w_no_tags_angular_commits_using_tag_format( Follows tag format defined in python-semantic-release#1137 """ - repo_name = repo_w_no_tags_angular_commits_using_tag_format.__name__ + repo_name = repo_w_no_tags_conventional_commits_using_tag_format.__name__ commit_type: CommitConvention = ( repo_name.split("_commits", maxsplit=1)[0].split("_")[-1] # type: ignore[assignment] ) @@ -259,13 +259,13 @@ def _build_repo(cached_repo_path: Path) -> Sequence[RepoActions]: @pytest.fixture -def repo_w_no_tags_angular_commits( +def repo_w_no_tags_conventional_commits( build_trunk_only_repo_w_no_tags: BuildSpecificRepoFn, example_project_git_repo: ExProjectGitRepoFn, example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> BuiltRepoResult: - repo_name = repo_w_no_tags_angular_commits.__name__ + repo_name = repo_w_no_tags_conventional_commits.__name__ commit_type: CommitConvention = repo_name.split("_")[-2] # type: ignore[assignment] return { diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py index 1d57218ba..a2c133d21 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py @@ -139,7 +139,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": INITIAL_COMMIT_MESSAGE, + "conventional": INITIAL_COMMIT_MESSAGE, "emoji": INITIAL_COMMIT_MESSAGE, "scipy": INITIAL_COMMIT_MESSAGE, "datetime": next(commit_timestamp_gen), @@ -148,7 +148,7 @@ def _get_repo_from_defintion( ), }, { - "angular": "feat: add new feature", + "conventional": "feat: add new feature", "emoji": ":sparkles: add new feature", "scipy": "ENH: add new feature", "datetime": next(commit_timestamp_gen), @@ -188,7 +188,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "fix: correct some text", + "conventional": "fix: correct some text", "emoji": ":bug: correct some text", "scipy": "MAINT: correct some text", "datetime": next(commit_timestamp_gen), @@ -228,7 +228,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "feat: add some more text", + "conventional": "feat: add some more text", "emoji": ":sparkles: add some more text", "scipy": "ENH: add some more text", "datetime": next(commit_timestamp_gen), @@ -268,7 +268,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "feat(cli): add cli command", + "conventional": "feat(cli): add cli command", "emoji": ":sparkles:(cli) add cli command", "scipy": "ENH(cli): add cli command", "datetime": next(commit_timestamp_gen), @@ -343,13 +343,13 @@ def _build_repo(cached_repo_path: Path) -> Sequence[RepoActions]: @pytest.fixture -def repo_w_trunk_only_n_prereleases_angular_commits( +def repo_w_trunk_only_n_prereleases_conventional_commits( build_trunk_only_repo_w_prerelease_tags: BuildSpecificRepoFn, example_project_git_repo: ExProjectGitRepoFn, example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> BuiltRepoResult: - repo_name = repo_w_trunk_only_n_prereleases_angular_commits.__name__ + repo_name = repo_w_trunk_only_n_prereleases_conventional_commits.__name__ commit_type: CommitConvention = repo_name.split("_")[-2] # type: ignore[assignment] return { diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py index a79bd11dc..9d080ed7a 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py @@ -141,7 +141,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": INITIAL_COMMIT_MESSAGE, + "conventional": INITIAL_COMMIT_MESSAGE, "emoji": INITIAL_COMMIT_MESSAGE, "scipy": INITIAL_COMMIT_MESSAGE, "datetime": next(commit_timestamp_gen), @@ -150,7 +150,7 @@ def _get_repo_from_defintion( ), }, { - "angular": "feat: add new feature", + "conventional": "feat: add new feature", "emoji": ":sparkles: add new feature", "scipy": "ENH: add new feature", "datetime": next(commit_timestamp_gen), @@ -190,7 +190,7 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "angular": "fix: correct some text", + "conventional": "fix: correct some text", "emoji": ":bug: correct some text", "scipy": "MAINT: correct some text", "datetime": next(commit_timestamp_gen), @@ -263,7 +263,7 @@ def _build_repo(cached_repo_path: Path) -> Sequence[RepoActions]: @pytest.fixture -def repo_w_trunk_only_angular_commits_using_tag_format( +def repo_w_trunk_only_conventional_commits_using_tag_format( build_repo_from_definition: BuildRepoFromDefinitionFn, get_repo_definition_4_trunk_only_repo_w_tags: GetRepoDefinitionFn, get_cached_repo_data: GetCachedRepoDataFn, @@ -273,7 +273,7 @@ def repo_w_trunk_only_angular_commits_using_tag_format( example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> BuiltRepoResult: - repo_name = repo_w_trunk_only_angular_commits_using_tag_format.__name__ + repo_name = repo_w_trunk_only_conventional_commits_using_tag_format.__name__ commit_type: CommitConvention = ( repo_name.split("_commits", maxsplit=1)[0].split("_")[-1] # type: ignore[assignment] ) @@ -302,13 +302,13 @@ def _build_repo(cached_repo_path: Path) -> Sequence[RepoActions]: @pytest.fixture -def repo_w_trunk_only_angular_commits( +def repo_w_trunk_only_conventional_commits( build_trunk_only_repo_w_tags: BuildSpecificRepoFn, example_project_git_repo: ExProjectGitRepoFn, example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> BuiltRepoResult: - repo_name = repo_w_trunk_only_angular_commits.__name__ + repo_name = repo_w_trunk_only_conventional_commits.__name__ commit_type: CommitConvention = repo_name.split("_")[-2] # type: ignore[assignment] return { diff --git a/tests/unit/semantic_release/changelog/test_release_history.py b/tests/unit/semantic_release/changelog/test_release_history.py index 3f9908fcf..17b327cfa 100644 --- a/tests/unit/semantic_release/changelog/test_release_history.py +++ b/tests/unit/semantic_release/changelog/test_release_history.py @@ -11,21 +11,21 @@ from semantic_release.version.translator import VersionTranslator from semantic_release.version.version import Version -from tests.const import ANGULAR_COMMITS_MINOR, COMMIT_MESSAGE +from tests.const import COMMIT_MESSAGE, CONVENTIONAL_COMMITS_MINOR from tests.fixtures import ( - repo_w_git_flow_w_alpha_prereleases_n_angular_commits, - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits, - repo_w_github_flow_w_feature_release_channel_angular_commits, - repo_w_no_tags_angular_commits, - repo_w_trunk_only_angular_commits, - repo_w_trunk_only_n_prereleases_angular_commits, + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits, + repo_w_github_flow_w_feature_release_channel_conventional_commits, + repo_w_no_tags_conventional_commits, + repo_w_trunk_only_conventional_commits, + repo_w_trunk_only_n_prereleases_conventional_commits, ) from tests.util import add_text_to_file if TYPE_CHECKING: from typing import Protocol - from semantic_release.commit_parser.angular import AngularCommitParser + from semantic_release.commit_parser.conventional import ConventionalCommitParser from tests.fixtures.git_repo import ( BuiltRepoResult, @@ -39,7 +39,7 @@ def __call__(self, repo_def: RepoDefinition) -> FakeReleaseHistoryElements: ... # NOTE: not testing parser correctness here, just that the right commits end up # in the right places. So we only compare that the commits with the messages # we anticipate are in the right place, rather than by hash -# So we are only using the angular parser +# So we are only using the conventional parser # We are also currently only testing that the "elements" key of the releases @@ -103,21 +103,21 @@ def _create_release_history_from_repo_def( @pytest.mark.parametrize( "repo_result", [ - # ANGULAR parser - lazy_fixture(repo_w_no_tags_angular_commits.__name__), + # CONVENTIONAL parser + lazy_fixture(repo_w_no_tags_conventional_commits.__name__), *[ pytest.param( lazy_fixture(repo_fixture_name), marks=pytest.mark.comprehensive, ) for repo_fixture_name in [ - repo_w_trunk_only_angular_commits.__name__, - repo_w_trunk_only_n_prereleases_angular_commits.__name__, + repo_w_trunk_only_conventional_commits.__name__, + repo_w_trunk_only_n_prereleases_conventional_commits.__name__, # This is not tested because currently unable to disern the commits that were squashed or not - # repo_w_github_flow_w_default_release_channel_angular_commits.__name__, - repo_w_github_flow_w_feature_release_channel_angular_commits.__name__, - repo_w_git_flow_w_alpha_prereleases_n_angular_commits.__name__, - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits.__name__, + # repo_w_github_flow_w_default_release_channel_conventional_commits.__name__, + repo_w_github_flow_w_feature_release_channel_conventional_commits.__name__, + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits.__name__, ] ], ], @@ -125,7 +125,7 @@ def _create_release_history_from_repo_def( @pytest.mark.order("last") def test_release_history( repo_result: BuiltRepoResult, - default_angular_parser: AngularCommitParser, + default_conventional_parser: ConventionalCommitParser, file_in_repo: str, create_release_history_from_repo_def: CreateReleaseHistoryFromRepoDefFn, get_commits_from_repo_build_def: GetCommitsFromRepoBuildDefFn, @@ -143,7 +143,7 @@ def test_release_history( history = ReleaseHistory.from_git_history( repo, translator, - default_angular_parser, # type: ignore[arg-type] + default_conventional_parser, # type: ignore[arg-type] ) released = history.released @@ -171,7 +171,7 @@ def test_release_history( # PART 2: add some commits to the repo and check that they are in the right place - for commit_message in ANGULAR_COMMITS_MINOR: + for commit_message in CONVENTIONAL_COMMITS_MINOR: add_text_to_file(repo, file_in_repo) repo.git.commit(m=commit_message) @@ -181,7 +181,7 @@ def test_release_history( [ msg for bucket in [ - ANGULAR_COMMITS_MINOR[::-1], + CONVENTIONAL_COMMITS_MINOR[::-1], *expected_release_history.unreleased.values(), ] for msg in bucket @@ -193,7 +193,7 @@ def test_release_history( new_history = ReleaseHistory.from_git_history( repo, translator, - default_angular_parser, # type: ignore[arg-type] + default_conventional_parser, # type: ignore[arg-type] ) new_unreleased = new_history.unreleased new_released = new_history.released @@ -218,32 +218,32 @@ def test_release_history( @pytest.mark.parametrize( "repo_result", [ - lazy_fixture(repo_w_no_tags_angular_commits.__name__), + lazy_fixture(repo_w_no_tags_conventional_commits.__name__), *[ pytest.param( lazy_fixture(repo_fixture_name), marks=pytest.mark.comprehensive, ) for repo_fixture_name in [ - repo_w_trunk_only_angular_commits.__name__, - repo_w_trunk_only_n_prereleases_angular_commits.__name__, - repo_w_github_flow_w_feature_release_channel_angular_commits.__name__, - repo_w_git_flow_w_alpha_prereleases_n_angular_commits.__name__, - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits.__name__, + repo_w_trunk_only_conventional_commits.__name__, + repo_w_trunk_only_n_prereleases_conventional_commits.__name__, + repo_w_github_flow_w_feature_release_channel_conventional_commits.__name__, + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits.__name__, ] ], ], ) @pytest.mark.order("last") def test_release_history_releases( - repo_result: BuiltRepoResult, default_angular_parser: AngularCommitParser + repo_result: BuiltRepoResult, default_conventional_parser: ConventionalCommitParser ): new_version = Version.parse("100.10.1") actor = Actor("semantic-release", "semantic-release") release_history = ReleaseHistory.from_git_history( repo=repo_result["repo"], translator=VersionTranslator(), - commit_parser=default_angular_parser, # type: ignore[arg-type] + commit_parser=default_conventional_parser, # type: ignore[arg-type] ) tagged_date = datetime.now() new_rh = release_history.release( @@ -270,32 +270,32 @@ def test_release_history_releases( @pytest.mark.parametrize( "repo_result", [ - lazy_fixture(repo_w_no_tags_angular_commits.__name__), + lazy_fixture(repo_w_no_tags_conventional_commits.__name__), *[ pytest.param( lazy_fixture(repo_fixture_name), marks=pytest.mark.comprehensive, ) for repo_fixture_name in [ - repo_w_trunk_only_angular_commits.__name__, - repo_w_trunk_only_n_prereleases_angular_commits.__name__, - repo_w_github_flow_w_feature_release_channel_angular_commits.__name__, - repo_w_git_flow_w_alpha_prereleases_n_angular_commits.__name__, - repo_w_git_flow_w_rc_n_alpha_prereleases_n_angular_commits.__name__, + repo_w_trunk_only_conventional_commits.__name__, + repo_w_trunk_only_n_prereleases_conventional_commits.__name__, + repo_w_github_flow_w_feature_release_channel_conventional_commits.__name__, + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits.__name__, ] ], ], ) @pytest.mark.order("last") def test_all_matching_repo_tags_are_released( - repo_result: BuiltRepoResult, default_angular_parser: AngularCommitParser + repo_result: BuiltRepoResult, default_conventional_parser: ConventionalCommitParser ): repo = repo_result["repo"] translator = VersionTranslator() release_history = ReleaseHistory.from_git_history( repo=repo, translator=translator, - commit_parser=default_angular_parser, # type: ignore[arg-type] + commit_parser=default_conventional_parser, # type: ignore[arg-type] ) for tag in repo.tags: diff --git a/tests/unit/semantic_release/cli/test_config.py b/tests/unit/semantic_release/cli/test_config.py index c24781c4c..343748187 100644 --- a/tests/unit/semantic_release/cli/test_config.py +++ b/tests/unit/semantic_release/cli/test_config.py @@ -25,7 +25,7 @@ _known_hvcs, ) from semantic_release.cli.util import load_raw_config_file -from semantic_release.commit_parser.angular import AngularParserOptions +from semantic_release.commit_parser.conventional import ConventionalCommitParserOptions from semantic_release.commit_parser.emoji import EmojiParserOptions from semantic_release.commit_parser.scipy import ScipyParserOptions from semantic_release.commit_parser.tag import TagParserOptions @@ -33,7 +33,7 @@ from semantic_release.enums import LevelBump from semantic_release.errors import ParserLoadError -from tests.fixtures.repos import repo_w_no_tags_angular_commits +from tests.fixtures.repos import repo_w_no_tags_conventional_commits from tests.util import ( CustomParserOpts, CustomParserWithNoOpts, @@ -110,9 +110,9 @@ def test_invalid_hvcs_type(remote_config: dict[str, Any]): [ ( None, - RootModel(AngularParserOptions()).model_dump(), - ), # default not provided -> means angular - ("angular", RootModel(AngularParserOptions()).model_dump()), + RootModel(ConventionalCommitParserOptions()).model_dump(), + ), # default not provided -> means conventional + ("conventional", RootModel(ConventionalCommitParserOptions()).model_dump()), ("emoji", RootModel(EmojiParserOptions()).model_dump()), ("scipy", RootModel(ScipyParserOptions()).model_dump()), ("tag", RootModel(TagParserOptions()).model_dump()), @@ -142,7 +142,7 @@ def test_load_user_defined_parser_opts(): } raw_config = RawConfig.model_validate( { - "commit_parser": "angular", + "commit_parser": "conventional", "commit_parser_options": user_defined_opts, } ) @@ -185,7 +185,7 @@ def test_default_toml_config_valid(example_project_dir: ExProjectDir): ({"GIT_COMMIT_AUTHOR": "foo "}, "foo "), ], ) -@pytest.mark.usefixtures(repo_w_no_tags_angular_commits.__name__) +@pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_commit_author_configurable( example_pyproject_toml: Path, mock_env: dict[str, str], diff --git a/tests/unit/semantic_release/commit_parser/test_angular.py b/tests/unit/semantic_release/commit_parser/test_conventional.py similarity index 91% rename from tests/unit/semantic_release/commit_parser/test_angular.py rename to tests/unit/semantic_release/commit_parser/test_conventional.py index ef6ed9eb4..078e1ecd5 100644 --- a/tests/unit/semantic_release/commit_parser/test_angular.py +++ b/tests/unit/semantic_release/commit_parser/test_conventional.py @@ -5,9 +5,9 @@ import pytest -from semantic_release.commit_parser.angular import ( - AngularCommitParser, - AngularParserOptions, +from semantic_release.commit_parser.conventional import ( + ConventionalCommitParser, + ConventionalCommitParserOptions, ) from semantic_release.commit_parser.token import ParsedCommit, ParseError from semantic_release.enums import LevelBump @@ -34,11 +34,11 @@ "commit_message", ["", "feat(parser\n): Add new parser pattern"] ) def test_parser_raises_unknown_message_style( - default_angular_parser: AngularCommitParser, + default_conventional_parser: ConventionalCommitParser, make_commit_obj: MakeCommitObjFn, commit_message: str, ): - parsed_results = default_angular_parser.parse(make_commit_obj(commit_message)) + parsed_results = default_conventional_parser.parse(make_commit_obj(commit_message)) assert isinstance(parsed_results, Iterable) for result in parsed_results: assert isinstance(result, ParseError) @@ -172,16 +172,16 @@ def test_parser_raises_unknown_message_style( ], ) def test_parser_squashed_commit_bitbucket_squash_style( - default_angular_parser: AngularCommitParser, + default_conventional_parser: ConventionalCommitParser, make_commit_obj: MakeCommitObjFn, commit_message: str, expected_commit_details: Sequence[dict | None], ): # Setup: Enable squash commit parsing - parser = AngularCommitParser( - options=AngularParserOptions( + parser = ConventionalCommitParser( + options=ConventionalCommitParserOptions( **{ - **default_angular_parser.options.__dict__, + **default_conventional_parser.options.__dict__, "parse_squash_commits": True, } ) @@ -359,16 +359,16 @@ def test_parser_squashed_commit_bitbucket_squash_style( ], ) def test_parser_squashed_commit_git_squash_style( - default_angular_parser: AngularCommitParser, + default_conventional_parser: ConventionalCommitParser, make_commit_obj: MakeCommitObjFn, commit_message: str, expected_commit_details: Sequence[dict | None], ): # Setup: Enable squash commit parsing - parser = AngularCommitParser( - options=AngularParserOptions( + parser = ConventionalCommitParser( + options=ConventionalCommitParserOptions( **{ - **default_angular_parser.options.__dict__, + **default_conventional_parser.options.__dict__, "parse_squash_commits": True, } ) @@ -526,16 +526,16 @@ def test_parser_squashed_commit_git_squash_style( ], ) def test_parser_squashed_commit_github_squash_style( - default_angular_parser: AngularCommitParser, + default_conventional_parser: ConventionalCommitParser, make_commit_obj: MakeCommitObjFn, commit_message: str, expected_commit_details: Sequence[dict | None], ): # Setup: Enable squash commit parsing - parser = AngularCommitParser( - options=AngularParserOptions( + parser = ConventionalCommitParser( + options=ConventionalCommitParserOptions( **{ - **default_angular_parser.options.__dict__, + **default_conventional_parser.options.__dict__, "parse_squash_commits": True, } ) @@ -587,20 +587,20 @@ def test_parser_squashed_commit_github_squash_style( LevelBump.MAJOR, ), ("feat(parser): add emoji parser", LevelBump.MINOR), - ("fix(parser): fix regex in angular parser", LevelBump.PATCH), - ("test(parser): add a test for angular parser", LevelBump.NO_RELEASE), + ("fix(parser): fix regex in conventional parser", LevelBump.PATCH), + ("test(parser): add a test for conventional parser", LevelBump.NO_RELEASE), ("feat(parser)!: edit data parsing stuff", LevelBump.MAJOR), ("fix!: edit data parsing stuff again", LevelBump.MAJOR), ("fix: superfix", LevelBump.PATCH), ], ) def test_parser_returns_correct_bump_level( - default_angular_parser: AngularCommitParser, + default_conventional_parser: ConventionalCommitParser, commit_message: str, bump: LevelBump, make_commit_obj: MakeCommitObjFn, ): - parsed_results = default_angular_parser.parse(make_commit_obj(commit_message)) + parsed_results = default_conventional_parser.parse(make_commit_obj(commit_message)) assert isinstance(parsed_results, Iterable) assert len(parsed_results) == 1 @@ -623,12 +623,12 @@ def test_parser_returns_correct_bump_level( ], ) def test_parser_return_type_from_commit_message( - default_angular_parser: AngularCommitParser, + default_conventional_parser: ConventionalCommitParser, message: str, type_: str, make_commit_obj: MakeCommitObjFn, ): - parsed_results = default_angular_parser.parse(make_commit_obj(message)) + parsed_results = default_conventional_parser.parse(make_commit_obj(message)) assert isinstance(parsed_results, Iterable) assert len(parsed_results) == 1 @@ -653,12 +653,12 @@ def test_parser_return_type_from_commit_message( ], ) def test_parser_return_scope_from_commit_message( - default_angular_parser: AngularCommitParser, + default_conventional_parser: ConventionalCommitParser, message: str, scope: str, make_commit_obj: MakeCommitObjFn, ): - parsed_results = default_angular_parser.parse(make_commit_obj(message)) + parsed_results = default_conventional_parser.parse(make_commit_obj(message)) assert isinstance(parsed_results, Iterable) assert len(parsed_results) == 1 @@ -679,10 +679,13 @@ def test_parser_return_scope_from_commit_message( "message, descriptions", [ ("feat(parser): add emoji parser", ["add emoji parser"]), - ("fix(parser): fix regex in angular parser", ["fix regex in angular parser"]), ( - "test(parser): add a test for angular parser", - ["add a test for angular parser"], + "fix(parser): fix regex in conventional parser", + ["fix regex in conventional parser"], + ), + ( + "test(parser): add a test for conventional parser", + ["add a test for conventional parser"], ), ( f"fix(tox): fix env \n\n{_long_text}\n\n{_footer}", @@ -692,12 +695,12 @@ def test_parser_return_scope_from_commit_message( ], ) def test_parser_return_subject_from_commit_message( - default_angular_parser: AngularCommitParser, + default_conventional_parser: ConventionalCommitParser, message: str, descriptions: list[str], make_commit_obj: MakeCommitObjFn, ): - parsed_results = default_angular_parser.parse(make_commit_obj(message)) + parsed_results = default_conventional_parser.parse(make_commit_obj(message)) assert isinstance(parsed_results, Iterable) assert len(parsed_results) == 1 @@ -719,8 +722,8 @@ def test_parser_return_subject_from_commit_message( ), # GitLab style ( - "fix(parser): fix regex in angular parser (!456)", - "fix regex in angular parser (!456)", + "fix(parser): fix regex in conventional parser (!456)", + "fix regex in conventional parser (!456)", "!456", ), # BitBucket style @@ -738,13 +741,13 @@ def test_parser_return_subject_from_commit_message( ], ) def test_parser_return_linked_merge_request_from_commit_message( - default_angular_parser: AngularCommitParser, + default_conventional_parser: ConventionalCommitParser, message: str, subject: str, merge_request_number: str, make_commit_obj: MakeCommitObjFn, ): - parsed_results = default_angular_parser.parse(make_commit_obj(message)) + parsed_results = default_conventional_parser.parse(make_commit_obj(message)) assert isinstance(parsed_results, Iterable) assert len(parsed_results) == 1 @@ -1029,12 +1032,12 @@ def test_parser_return_linked_merge_request_from_commit_message( ], ) def test_parser_return_linked_issues_from_commit_message( - default_angular_parser: AngularCommitParser, + default_conventional_parser: ConventionalCommitParser, message: str, linked_issues: Sequence[str], make_commit_obj: MakeCommitObjFn, ): - parsed_results = default_angular_parser.parse(make_commit_obj(message)) + parsed_results = default_conventional_parser.parse(make_commit_obj(message)) assert isinstance(parsed_results, Iterable) assert len(parsed_results) == 1 @@ -1057,7 +1060,7 @@ def test_parser_return_linked_issues_from_commit_message( "single notice", dedent( """\ - fix(parser): fix regex in angular parser + fix(parser): fix regex in conventional parser NOTICE: This is a notice """ @@ -1068,7 +1071,7 @@ def test_parser_return_linked_issues_from_commit_message( "multiline notice", dedent( """\ - fix(parser): fix regex in angular parser + fix(parser): fix regex in conventional parser NOTICE: This is a notice that is longer than other notices @@ -1080,7 +1083,7 @@ def test_parser_return_linked_issues_from_commit_message( "multiple notices", dedent( """\ - fix(parser): fix regex in angular parser + fix(parser): fix regex in conventional parser NOTICE: This is a notice @@ -1093,7 +1096,7 @@ def test_parser_return_linked_issues_from_commit_message( "notice with other footer", dedent( """\ - fix(parser): fix regex in angular parser + fix(parser): fix regex in conventional parser BREAKING CHANGE: This is a breaking change @@ -1106,12 +1109,12 @@ def test_parser_return_linked_issues_from_commit_message( ], ) def test_parser_return_release_notices_from_commit_message( - default_angular_parser: AngularCommitParser, + default_conventional_parser: ConventionalCommitParser, message: str, notices: Sequence[str], make_commit_obj: MakeCommitObjFn, ): - parsed_results = default_angular_parser.parse(make_commit_obj(message)) + parsed_results = default_conventional_parser.parse(make_commit_obj(message)) assert isinstance(parsed_results, Iterable) assert len(parsed_results) == 1 @@ -1130,9 +1133,9 @@ def test_parser_return_release_notices_from_commit_message( # test custom parser options # ############################## def test_parser_custom_default_level(make_commit_obj: MakeCommitObjFn): - options = AngularParserOptions(default_bump_level=LevelBump.MINOR) - parsed_results = AngularCommitParser(options).parse( - make_commit_obj("test(parser): add a test for angular parser") + options = ConventionalCommitParserOptions(default_bump_level=LevelBump.MINOR) + parsed_results = ConventionalCommitParser(options).parse( + make_commit_obj("test(parser): add a test for conventional parser") ) assert isinstance(parsed_results, Iterable) @@ -1143,13 +1146,13 @@ def test_parser_custom_default_level(make_commit_obj: MakeCommitObjFn): def test_parser_custom_allowed_types( - default_angular_parser: AngularCommitParser, + default_conventional_parser: ConventionalCommitParser, make_commit_obj: MakeCommitObjFn, ): new_tag = "custom" - custom_allowed_tags = [*default_angular_parser.options.allowed_tags, new_tag] - parser = AngularCommitParser( - options=AngularParserOptions( + custom_allowed_tags = [*default_conventional_parser.options.allowed_tags, new_tag] + parser = ConventionalCommitParser( + options=ConventionalCommitParserOptions( allowed_tags=tuple(custom_allowed_tags), ) ) @@ -1169,14 +1172,15 @@ def test_parser_custom_allowed_types( def test_parser_custom_allowed_types_ignores_non_types( - default_angular_parser: AngularCommitParser, make_commit_obj: MakeCommitObjFn + default_conventional_parser: ConventionalCommitParser, + make_commit_obj: MakeCommitObjFn, ): banned_tag = "feat" - custom_allowed_tags = [*default_angular_parser.options.allowed_tags] + custom_allowed_tags = [*default_conventional_parser.options.allowed_tags] custom_allowed_tags.remove(banned_tag) - parser = AngularCommitParser( - options=AngularParserOptions( + parser = ConventionalCommitParser( + options=ConventionalCommitParserOptions( allowed_tags=tuple(custom_allowed_tags), ) ) @@ -1190,8 +1194,8 @@ def test_parser_custom_allowed_types_ignores_non_types( def test_parser_custom_minor_tags(make_commit_obj: MakeCommitObjFn): custom_minor_tag = "docs" - parser = AngularCommitParser( - options=AngularParserOptions(minor_tags=(custom_minor_tag,)) + parser = ConventionalCommitParser( + options=ConventionalCommitParserOptions(minor_tags=(custom_minor_tag,)) ) parsed_results = parser.parse(make_commit_obj(f"{custom_minor_tag}: ...")) @@ -1204,8 +1208,8 @@ def test_parser_custom_minor_tags(make_commit_obj: MakeCommitObjFn): def test_parser_custom_patch_tags(make_commit_obj: MakeCommitObjFn): custom_patch_tag = "test" - parser = AngularCommitParser( - options=AngularParserOptions(patch_tags=(custom_patch_tag,)) + parser = ConventionalCommitParser( + options=ConventionalCommitParserOptions(patch_tags=(custom_patch_tag,)) ) parsed_results = parser.parse(make_commit_obj(f"{custom_patch_tag}: ...")) @@ -1217,14 +1221,14 @@ def test_parser_custom_patch_tags(make_commit_obj: MakeCommitObjFn): def test_parser_ignore_merge_commit( - default_angular_parser: AngularCommitParser, + default_conventional_parser: ConventionalCommitParser, make_commit_obj: MakeCommitObjFn, ): # Setup: Enable parsing of linked issues - parser = AngularCommitParser( - options=AngularParserOptions( + parser = ConventionalCommitParser( + options=ConventionalCommitParserOptions( **{ - **default_angular_parser.options.__dict__, + **default_conventional_parser.options.__dict__, "ignore_merge_commits": True, } ) diff --git a/tests/unit/semantic_release/commit_parser/test_emoji.py b/tests/unit/semantic_release/commit_parser/test_emoji.py index 84f390773..ec2d83a3e 100644 --- a/tests/unit/semantic_release/commit_parser/test_emoji.py +++ b/tests/unit/semantic_release/commit_parser/test_emoji.py @@ -448,7 +448,7 @@ def test_parser_return_linked_issues_from_commit_message( "single notice", dedent( """\ - :bug:(parser): fix regex in angular parser + :bug:(parser): fix regex in emoji parser NOTICE: This is a notice """ @@ -459,7 +459,7 @@ def test_parser_return_linked_issues_from_commit_message( "multiline notice", dedent( """\ - :bug:(parser): fix regex in angular parser + :bug:(parser): fix regex in emoji parser NOTICE: This is a notice that is longer than other notices @@ -471,7 +471,7 @@ def test_parser_return_linked_issues_from_commit_message( "multiple notices", dedent( """\ - :bug:(parser): fix regex in angular parser + :bug:(parser): fix regex in emoji parser NOTICE: This is a notice @@ -484,7 +484,7 @@ def test_parser_return_linked_issues_from_commit_message( "notice with other footer", dedent( """\ - :bug:(parser): fix regex in angular parser + :bug:(parser): fix regex in emoji parser BREAKING CHANGE: This is a breaking change diff --git a/tests/unit/semantic_release/commit_parser/test_scipy.py b/tests/unit/semantic_release/commit_parser/test_scipy.py index a48fc418a..2c15fca6f 100644 --- a/tests/unit/semantic_release/commit_parser/test_scipy.py +++ b/tests/unit/semantic_release/commit_parser/test_scipy.py @@ -1044,7 +1044,7 @@ def test_parser_return_linked_issues_from_commit_message( "single notice", dedent( """\ - BUG(parser): fix regex in angular parser + BUG(parser): fix regex in scipy parser NOTICE: This is a notice """ @@ -1055,7 +1055,7 @@ def test_parser_return_linked_issues_from_commit_message( "multiline notice", dedent( """\ - BUG(parser): fix regex in angular parser + BUG(parser): fix regex in scipy parser NOTICE: This is a notice that is longer than other notices @@ -1067,7 +1067,7 @@ def test_parser_return_linked_issues_from_commit_message( "multiple notices", dedent( """\ - BUG(parser): fix regex in angular parser + BUG(parser): fix regex in scipy parser NOTICE: This is a notice @@ -1080,7 +1080,7 @@ def test_parser_return_linked_issues_from_commit_message( "notice with other footer", dedent( """\ - BUG(parser): fix regex in angular parser + BUG(parser): fix regex in scipy parser BREAKING CHANGE: This is a breaking change diff --git a/tests/util.py b/tests/util.py index 97dc18b5c..63d7679ac 100644 --- a/tests/util.py +++ b/tests/util.py @@ -17,7 +17,7 @@ from semantic_release.changelog.context import ChangelogMode, make_changelog_context from semantic_release.changelog.release_history import ReleaseHistory from semantic_release.commit_parser._base import CommitParser, ParserOptions -from semantic_release.commit_parser.angular import AngularCommitParser +from semantic_release.commit_parser.conventional import ConventionalCommitParser from semantic_release.commit_parser.token import ( ParsedCommit, ParsedMessageResult, @@ -289,7 +289,7 @@ class IncompleteCustomParser(CommitParser): pass -class CustomAngularParserWithIgnorePatterns(AngularCommitParser): +class CustomConventionalParserWithIgnorePatterns(ConventionalCommitParser): def parse(self, commit: Commit) -> ParsedCommit | ParseError: if not (parse_msg_result := super().parse_message(str(commit.message))): return ParseError(commit, "Unable to parse commit") From 212683d1d33a85146b33a7add4c29893df579fb2 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Mon, 10 Feb 2025 03:53:50 +0000 Subject: [PATCH 038/160] 9.19.0 Automatically generated by python-semantic-release --- CHANGELOG.rst | 27 ++++++++++++++++++++++ docs/automatic-releases/github-actions.rst | 14 +++++------ docs/commit_parsing.rst | 6 ++--- docs/configuration.rst | 4 ++-- pyproject.toml | 2 +- src/semantic_release/__init__.py | 2 +- 6 files changed, 41 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index fb1e8ebfb..303e0d149 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,33 @@ CHANGELOG ========= +.. _changelog-v9.19.0: + +v9.19.0 (2025-02-10) +==================== + +✨ Features +----------- + +* **parser-conventional**: Add official ``conventional-commits`` parser (`PR#1177`_, `27ddf84`_) + +📖 Documentation +---------------- + +* Update references to Angular parser to Conventional Commit Parser (`PR#1177`_, `27ddf84`_) + +💡 ADDITIONAL RELEASE INFORMATION +--------------------------------- + +* **parser-conventional**: The 'angular' commit parser has been renamed to 'conventional' to match + the official conventional-commits standard for which the 'angular' parser has evolved into. Please + update your configurations to specify 'conventional' as the 'commit_parser' value in place of + 'angular'. The 'angular' type will be removed in v11. + +.. _27ddf84: https://github.com/python-semantic-release/python-semantic-release/commit/27ddf840f8c812361c60bac9cf0b110d401f33d6 +.. _PR#1177: https://github.com/python-semantic-release/python-semantic-release/pull/1177 + + .. _changelog-v9.18.1: v9.18.1 (2025-02-08) diff --git a/docs/automatic-releases/github-actions.rst b/docs/automatic-releases/github-actions.rst index c8cfd4098..2ea4ba2ff 100644 --- a/docs/automatic-releases/github-actions.rst +++ b/docs/automatic-releases/github-actions.rst @@ -337,7 +337,7 @@ before the :ref:`version ` subcommand. .. code:: yaml - - uses: python-semantic-release/python-semantic-release@v9.18.1 + - uses: python-semantic-release/python-semantic-release@v9.19.0 with: root_options: "-vv --noop" @@ -576,7 +576,7 @@ before the :ref:`publish ` subcommand. .. code:: yaml - - uses: python-semantic-release/publish-action@v9.18.1 + - uses: python-semantic-release/publish-action@v9.19.0 with: root_options: "-vv --noop" @@ -684,7 +684,7 @@ to the GitHub Release Assets as well. - name: Action | Semantic Version Release id: release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.18.1 + uses: python-semantic-release/python-semantic-release@v9.19.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} git_committer_name: "github-actions" @@ -695,7 +695,7 @@ to the GitHub Release Assets as well. if: steps.release.outputs.released == 'true' - name: Publish | Upload to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.18.1 + uses: python-semantic-release/publish-action@v9.19.0 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -744,7 +744,7 @@ The equivalent GitHub Action configuration would be: - name: Action | Semantic Version Release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.18.1 + uses: python-semantic-release/python-semantic-release@v9.19.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} force: patch @@ -772,13 +772,13 @@ Publish Action. .. code:: yaml - name: Release Project 1 - uses: python-semantic-release/python-semantic-release@v9.18.1 + uses: python-semantic-release/python-semantic-release@v9.19.0 with: directory: ./project1 github_token: ${{ secrets.GITHUB_TOKEN }} - name: Release Project 2 - uses: python-semantic-release/python-semantic-release@v9.18.1 + uses: python-semantic-release/python-semantic-release@v9.19.0 with: directory: ./project2 github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/docs/commit_parsing.rst b/docs/commit_parsing.rst index 30cdf32b1..25e70eb45 100644 --- a/docs/commit_parsing.rst +++ b/docs/commit_parsing.rst @@ -49,7 +49,7 @@ Built-in Commit Parsers The following parsers are built in to Python Semantic Release: - :ref:`ConventionalCommitParser ` -- :ref:`AngularCommitParser ` *(deprecated in ${NEW_RELEASE_TAG})* +- :ref:`AngularCommitParser ` *(deprecated in v9.19.0)* - :ref:`EmojiCommitParser ` - :ref:`ScipyCommitParser ` - :ref:`TagCommitParser ` *(deprecated in v9.12.0)* @@ -61,7 +61,7 @@ The following parsers are built in to Python Semantic Release: Conventional Commits Parser """"""""""""""""""""""""""" -*Introduced in ${NEW_RELEASE_TAG}* +*Introduced in v9.19.0* A parser that is designed to parse commits formatted according to the `Conventional Commits Specification`_. The parser is implemented with the following @@ -140,7 +140,7 @@ Angular Commit Parser """"""""""""""""""""" .. warning:: - This parser was deprecated in ``${NEW_RELEASE_TAG}``. It will be removed in a future release. + This parser was deprecated in ``v9.19.0``. It will be removed in a future release. This parser is being replaced by the :ref:`commit_parser-builtin-conventional`. A parser that is designed to parse commits formatted according to the diff --git a/docs/configuration.rst b/docs/configuration.rst index 35a3b821f..9163f11d8 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -790,8 +790,8 @@ Specify which commit parser Python Semantic Release should use to parse the comm within the Git repository. Built-in parsers: - * ``angular`` - :ref:`AngularCommitParser ` *(deprecated in ${NEW_RELEASE_TAG})* - * ``conventional`` - :ref:`ConventionalCommitParser ` *(available in ${NEW_RELEASE_TAG}+)* + * ``angular`` - :ref:`AngularCommitParser ` *(deprecated in v9.19.0)* + * ``conventional`` - :ref:`ConventionalCommitParser ` *(available in v9.19.0+)* * ``emoji`` - :ref:`EmojiCommitParser ` * ``scipy`` - :ref:`ScipyCommitParser ` * ``tag`` - :ref:`TagCommitParser ` *(deprecated in v9.12.0)* diff --git a/pyproject.toml b/pyproject.toml index 5666b1a86..31eef872b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.18.1" +version = "9.19.0" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } diff --git a/src/semantic_release/__init__.py b/src/semantic_release/__init__.py index 9e823e6de..790c3034f 100644 --- a/src/semantic_release/__init__.py +++ b/src/semantic_release/__init__.py @@ -24,7 +24,7 @@ tags_and_versions, ) -__version__ = "9.18.1" +__version__ = "9.19.0" __all__ = [ "CommitParser", From d25c19f4f48150a177b1ddc812cfe886819e9663 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 9 Feb 2025 21:00:38 -0700 Subject: [PATCH 039/160] Revert "9.19.0" This reverts commit 212683d1d33a85146b33a7add4c29893df579fb2. Revert commit because release & deploy process failed in CI. Must revert. --- CHANGELOG.rst | 27 ---------------------- docs/automatic-releases/github-actions.rst | 14 +++++------ docs/commit_parsing.rst | 6 ++--- docs/configuration.rst | 4 ++-- pyproject.toml | 4 ++-- src/semantic_release/__init__.py | 2 +- 6 files changed, 15 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 303e0d149..fb1e8ebfb 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,33 +4,6 @@ CHANGELOG ========= -.. _changelog-v9.19.0: - -v9.19.0 (2025-02-10) -==================== - -✨ Features ------------ - -* **parser-conventional**: Add official ``conventional-commits`` parser (`PR#1177`_, `27ddf84`_) - -📖 Documentation ----------------- - -* Update references to Angular parser to Conventional Commit Parser (`PR#1177`_, `27ddf84`_) - -💡 ADDITIONAL RELEASE INFORMATION ---------------------------------- - -* **parser-conventional**: The 'angular' commit parser has been renamed to 'conventional' to match - the official conventional-commits standard for which the 'angular' parser has evolved into. Please - update your configurations to specify 'conventional' as the 'commit_parser' value in place of - 'angular'. The 'angular' type will be removed in v11. - -.. _27ddf84: https://github.com/python-semantic-release/python-semantic-release/commit/27ddf840f8c812361c60bac9cf0b110d401f33d6 -.. _PR#1177: https://github.com/python-semantic-release/python-semantic-release/pull/1177 - - .. _changelog-v9.18.1: v9.18.1 (2025-02-08) diff --git a/docs/automatic-releases/github-actions.rst b/docs/automatic-releases/github-actions.rst index 2ea4ba2ff..c8cfd4098 100644 --- a/docs/automatic-releases/github-actions.rst +++ b/docs/automatic-releases/github-actions.rst @@ -337,7 +337,7 @@ before the :ref:`version ` subcommand. .. code:: yaml - - uses: python-semantic-release/python-semantic-release@v9.19.0 + - uses: python-semantic-release/python-semantic-release@v9.18.1 with: root_options: "-vv --noop" @@ -576,7 +576,7 @@ before the :ref:`publish ` subcommand. .. code:: yaml - - uses: python-semantic-release/publish-action@v9.19.0 + - uses: python-semantic-release/publish-action@v9.18.1 with: root_options: "-vv --noop" @@ -684,7 +684,7 @@ to the GitHub Release Assets as well. - name: Action | Semantic Version Release id: release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.19.0 + uses: python-semantic-release/python-semantic-release@v9.18.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} git_committer_name: "github-actions" @@ -695,7 +695,7 @@ to the GitHub Release Assets as well. if: steps.release.outputs.released == 'true' - name: Publish | Upload to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.19.0 + uses: python-semantic-release/publish-action@v9.18.1 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -744,7 +744,7 @@ The equivalent GitHub Action configuration would be: - name: Action | Semantic Version Release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.19.0 + uses: python-semantic-release/python-semantic-release@v9.18.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} force: patch @@ -772,13 +772,13 @@ Publish Action. .. code:: yaml - name: Release Project 1 - uses: python-semantic-release/python-semantic-release@v9.19.0 + uses: python-semantic-release/python-semantic-release@v9.18.1 with: directory: ./project1 github_token: ${{ secrets.GITHUB_TOKEN }} - name: Release Project 2 - uses: python-semantic-release/python-semantic-release@v9.19.0 + uses: python-semantic-release/python-semantic-release@v9.18.1 with: directory: ./project2 github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/docs/commit_parsing.rst b/docs/commit_parsing.rst index 25e70eb45..30cdf32b1 100644 --- a/docs/commit_parsing.rst +++ b/docs/commit_parsing.rst @@ -49,7 +49,7 @@ Built-in Commit Parsers The following parsers are built in to Python Semantic Release: - :ref:`ConventionalCommitParser ` -- :ref:`AngularCommitParser ` *(deprecated in v9.19.0)* +- :ref:`AngularCommitParser ` *(deprecated in ${NEW_RELEASE_TAG})* - :ref:`EmojiCommitParser ` - :ref:`ScipyCommitParser ` - :ref:`TagCommitParser ` *(deprecated in v9.12.0)* @@ -61,7 +61,7 @@ The following parsers are built in to Python Semantic Release: Conventional Commits Parser """"""""""""""""""""""""""" -*Introduced in v9.19.0* +*Introduced in ${NEW_RELEASE_TAG}* A parser that is designed to parse commits formatted according to the `Conventional Commits Specification`_. The parser is implemented with the following @@ -140,7 +140,7 @@ Angular Commit Parser """"""""""""""""""""" .. warning:: - This parser was deprecated in ``v9.19.0``. It will be removed in a future release. + This parser was deprecated in ``${NEW_RELEASE_TAG}``. It will be removed in a future release. This parser is being replaced by the :ref:`commit_parser-builtin-conventional`. A parser that is designed to parse commits formatted according to the diff --git a/docs/configuration.rst b/docs/configuration.rst index 9163f11d8..35a3b821f 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -790,8 +790,8 @@ Specify which commit parser Python Semantic Release should use to parse the comm within the Git repository. Built-in parsers: - * ``angular`` - :ref:`AngularCommitParser ` *(deprecated in v9.19.0)* - * ``conventional`` - :ref:`ConventionalCommitParser ` *(available in v9.19.0+)* + * ``angular`` - :ref:`AngularCommitParser ` *(deprecated in ${NEW_RELEASE_TAG})* + * ``conventional`` - :ref:`ConventionalCommitParser ` *(available in ${NEW_RELEASE_TAG}+)* * ``emoji`` - :ref:`EmojiCommitParser ` * ``scipy`` - :ref:`ScipyCommitParser ` * ``tag`` - :ref:`TagCommitParser ` *(deprecated in v9.12.0)* diff --git a/pyproject.toml b/pyproject.toml index 31eef872b..65aa99d94 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.19.0" +version = "9.18.1" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } @@ -398,7 +398,7 @@ ignore_names = ["change_to_ex_proj_dir", "init_example_project"] [tool.semantic_release] logging_use_named_masks = true -commit_parser = "conventional" +commit_parser = "angular" commit_parser_options = { parse_squash_commits = true, ignore_merge_commits = true } build_command = """ python -m pip install -e .[build] diff --git a/src/semantic_release/__init__.py b/src/semantic_release/__init__.py index 790c3034f..9e823e6de 100644 --- a/src/semantic_release/__init__.py +++ b/src/semantic_release/__init__.py @@ -24,7 +24,7 @@ tags_and_versions, ) -__version__ = "9.19.0" +__version__ = "9.18.1" __all__ = [ "CommitParser", From c02663855a94e46eb021441cd6fb85beb25abdf7 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Mon, 10 Feb 2025 04:17:42 +0000 Subject: [PATCH 040/160] 9.19.0 Automatically generated by python-semantic-release --- CHANGELOG.rst | 27 ++++++++++++++++++++++ docs/automatic-releases/github-actions.rst | 14 +++++------ docs/commit_parsing.rst | 6 ++--- docs/configuration.rst | 4 ++-- pyproject.toml | 2 +- src/semantic_release/__init__.py | 2 +- 6 files changed, 41 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index fb1e8ebfb..303e0d149 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,33 @@ CHANGELOG ========= +.. _changelog-v9.19.0: + +v9.19.0 (2025-02-10) +==================== + +✨ Features +----------- + +* **parser-conventional**: Add official ``conventional-commits`` parser (`PR#1177`_, `27ddf84`_) + +📖 Documentation +---------------- + +* Update references to Angular parser to Conventional Commit Parser (`PR#1177`_, `27ddf84`_) + +💡 ADDITIONAL RELEASE INFORMATION +--------------------------------- + +* **parser-conventional**: The 'angular' commit parser has been renamed to 'conventional' to match + the official conventional-commits standard for which the 'angular' parser has evolved into. Please + update your configurations to specify 'conventional' as the 'commit_parser' value in place of + 'angular'. The 'angular' type will be removed in v11. + +.. _27ddf84: https://github.com/python-semantic-release/python-semantic-release/commit/27ddf840f8c812361c60bac9cf0b110d401f33d6 +.. _PR#1177: https://github.com/python-semantic-release/python-semantic-release/pull/1177 + + .. _changelog-v9.18.1: v9.18.1 (2025-02-08) diff --git a/docs/automatic-releases/github-actions.rst b/docs/automatic-releases/github-actions.rst index c8cfd4098..2ea4ba2ff 100644 --- a/docs/automatic-releases/github-actions.rst +++ b/docs/automatic-releases/github-actions.rst @@ -337,7 +337,7 @@ before the :ref:`version ` subcommand. .. code:: yaml - - uses: python-semantic-release/python-semantic-release@v9.18.1 + - uses: python-semantic-release/python-semantic-release@v9.19.0 with: root_options: "-vv --noop" @@ -576,7 +576,7 @@ before the :ref:`publish ` subcommand. .. code:: yaml - - uses: python-semantic-release/publish-action@v9.18.1 + - uses: python-semantic-release/publish-action@v9.19.0 with: root_options: "-vv --noop" @@ -684,7 +684,7 @@ to the GitHub Release Assets as well. - name: Action | Semantic Version Release id: release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.18.1 + uses: python-semantic-release/python-semantic-release@v9.19.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} git_committer_name: "github-actions" @@ -695,7 +695,7 @@ to the GitHub Release Assets as well. if: steps.release.outputs.released == 'true' - name: Publish | Upload to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.18.1 + uses: python-semantic-release/publish-action@v9.19.0 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -744,7 +744,7 @@ The equivalent GitHub Action configuration would be: - name: Action | Semantic Version Release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.18.1 + uses: python-semantic-release/python-semantic-release@v9.19.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} force: patch @@ -772,13 +772,13 @@ Publish Action. .. code:: yaml - name: Release Project 1 - uses: python-semantic-release/python-semantic-release@v9.18.1 + uses: python-semantic-release/python-semantic-release@v9.19.0 with: directory: ./project1 github_token: ${{ secrets.GITHUB_TOKEN }} - name: Release Project 2 - uses: python-semantic-release/python-semantic-release@v9.18.1 + uses: python-semantic-release/python-semantic-release@v9.19.0 with: directory: ./project2 github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/docs/commit_parsing.rst b/docs/commit_parsing.rst index 30cdf32b1..25e70eb45 100644 --- a/docs/commit_parsing.rst +++ b/docs/commit_parsing.rst @@ -49,7 +49,7 @@ Built-in Commit Parsers The following parsers are built in to Python Semantic Release: - :ref:`ConventionalCommitParser ` -- :ref:`AngularCommitParser ` *(deprecated in ${NEW_RELEASE_TAG})* +- :ref:`AngularCommitParser ` *(deprecated in v9.19.0)* - :ref:`EmojiCommitParser ` - :ref:`ScipyCommitParser ` - :ref:`TagCommitParser ` *(deprecated in v9.12.0)* @@ -61,7 +61,7 @@ The following parsers are built in to Python Semantic Release: Conventional Commits Parser """"""""""""""""""""""""""" -*Introduced in ${NEW_RELEASE_TAG}* +*Introduced in v9.19.0* A parser that is designed to parse commits formatted according to the `Conventional Commits Specification`_. The parser is implemented with the following @@ -140,7 +140,7 @@ Angular Commit Parser """"""""""""""""""""" .. warning:: - This parser was deprecated in ``${NEW_RELEASE_TAG}``. It will be removed in a future release. + This parser was deprecated in ``v9.19.0``. It will be removed in a future release. This parser is being replaced by the :ref:`commit_parser-builtin-conventional`. A parser that is designed to parse commits formatted according to the diff --git a/docs/configuration.rst b/docs/configuration.rst index 35a3b821f..9163f11d8 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -790,8 +790,8 @@ Specify which commit parser Python Semantic Release should use to parse the comm within the Git repository. Built-in parsers: - * ``angular`` - :ref:`AngularCommitParser ` *(deprecated in ${NEW_RELEASE_TAG})* - * ``conventional`` - :ref:`ConventionalCommitParser ` *(available in ${NEW_RELEASE_TAG}+)* + * ``angular`` - :ref:`AngularCommitParser ` *(deprecated in v9.19.0)* + * ``conventional`` - :ref:`ConventionalCommitParser ` *(available in v9.19.0+)* * ``emoji`` - :ref:`EmojiCommitParser ` * ``scipy`` - :ref:`ScipyCommitParser ` * ``tag`` - :ref:`TagCommitParser ` *(deprecated in v9.12.0)* diff --git a/pyproject.toml b/pyproject.toml index 65aa99d94..ee97f49ea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.18.1" +version = "9.19.0" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } diff --git a/src/semantic_release/__init__.py b/src/semantic_release/__init__.py index 9e823e6de..790c3034f 100644 --- a/src/semantic_release/__init__.py +++ b/src/semantic_release/__init__.py @@ -24,7 +24,7 @@ tags_and_versions, ) -__version__ = "9.18.1" +__version__ = "9.19.0" __all__ = [ "CommitParser", From 9ca7fe2cf5b72151c7e36433bb7949a947e323ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 9 Feb 2025 22:26:38 -0700 Subject: [PATCH 041/160] ci(deps): bump `python-semantic-release/publish-action@v9.18.1` to 9.19.0 (#1178) --- .github/workflows/cicd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 8ad586c6a..016c553a4 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -138,7 +138,7 @@ jobs: build: false - name: Release | Add distribution artifacts to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.18.1 + uses: python-semantic-release/publish-action@v9.19.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} tag: ${{ steps.release.outputs.tag }} From 249f37acc20a7334ff7a216b92b3970deabbae7a Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 9 Feb 2025 23:55:24 -0700 Subject: [PATCH 042/160] chore(config): change psr parser over to conventional-commits (#1179) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ee97f49ea..31eef872b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -398,7 +398,7 @@ ignore_names = ["change_to_ex_proj_dir", "init_example_project"] [tool.semantic_release] logging_use_named_masks = true -commit_parser = "angular" +commit_parser = "conventional" commit_parser_options = { parse_squash_commits = true, ignore_merge_commits = true } build_command = """ python -m pip install -e .[build] From ca42dbfffe54c2f2b96dae595012259a92893f38 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 23:57:06 -0700 Subject: [PATCH 043/160] build(deps-dev): bump `mypy` from 1.14.1 to 1.15.0 (#1181) * chore(pre-commit): bump `mypy-plugin` from 1.14.0 to 1.15.0 --- .pre-commit-config.yaml | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 70e871f44..f80f12a05 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -50,7 +50,7 @@ repos: name: ruff (format) - repo: https://github.com/pre-commit/mirrors-mypy - rev: "v1.14.0" + rev: "v1.15.0" hooks: - id: mypy additional_dependencies: diff --git a/pyproject.toml b/pyproject.toml index 31eef872b..c6e99d898 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -82,7 +82,7 @@ dev = [ "ruff == 0.6.1" ] mypy = [ - "mypy == 1.14.1", + "mypy == 1.15.0", "types-requests ~= 2.32.0", "types-pyyaml ~= 6.0", ] From e8343eeb38d3b4e18953ac0f97538df396d22b76 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 6 Oct 2024 23:00:38 -0600 Subject: [PATCH 044/160] docs(automatic-releases): declutter the table of contents for automatic release guides --- docs/automatic-releases/index.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/automatic-releases/index.rst b/docs/automatic-releases/index.rst index 8fd44b1bf..c5e6d3453 100644 --- a/docs/automatic-releases/index.rst +++ b/docs/automatic-releases/index.rst @@ -13,6 +13,8 @@ Guides ^^^^^^ .. toctree:: + :maxdepth: 2 + travis github-actions cronjobs From 55d4a05ff56321cf9874f8f302fbe7e5163ad4f7 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 9 Feb 2025 20:48:33 -0700 Subject: [PATCH 045/160] docs: fix spelling errors & inaccurate descriptions --- CHANGELOG.rst | 34 +++++++++++++++++----------------- docs/algorithm.rst | 2 +- docs/changelog_templates.rst | 6 +++--- docs/commands.rst | 9 +++++---- docs/configuration.rst | 15 +++++++-------- docs/index.rst | 2 +- docs/multibranch_releases.rst | 6 +++--- docs/strict_mode.rst | 2 +- docs/troubleshooting.rst | 5 ++--- 9 files changed, 40 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 303e0d149..f2bc511ae 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -99,7 +99,7 @@ v9.18.0 (2025-02-06) * **vcs-github**: Add ``create_release_url`` & ``format_w_official_vcs_name`` filter functions (`PR#1161`_, `f853cf0`_) -* **vcs-gitlab**: Add ``create_release_url`` & ``format_w_official_vcs_name`` filter functiions +* **vcs-gitlab**: Add ``create_release_url`` & ``format_w_official_vcs_name`` filter functions (`PR#1161`_, `f853cf0`_) 🪲 Bug Fixes @@ -491,7 +491,7 @@ v9.14.0 (2024-11-11) 🪲 Bug Fixes ------------ -* **release-notes**: Override default wordwrap to non-wrap for in default template (`99ab99b`_) +* **release-notes**: Override default word-wrap to non-wrap for in default template (`99ab99b`_) 📖 Documentation ---------------- @@ -548,7 +548,7 @@ v9.13.0 (2024-11-10) * **parser-angular**: Drop the ``breaking`` category but still maintain a major level bump (`f1ffa54`_) -* **parsers**: Improve reliability of text unwordwrap of descriptions (`436374b`_) +* **parsers**: Improve reliability of descriptions after reverse word-wrap (`436374b`_) ⚡ Performance Improvements --------------------------- @@ -871,7 +871,7 @@ v9.9.0 (2024-09-28) 📖 Documentation ---------------- -* **automatic-releases**: Drop extrenous github push configuration (`PR#1011`_, `2135c68`_) +* **automatic-releases**: Drop extraneous github push configuration (`PR#1011`_, `2135c68`_) * **github-actions**: Add configuration & description of publish action (`PR#1011`_, `2135c68`_) @@ -1066,7 +1066,7 @@ v9.8.4 (2024-07-04) 🪲 Bug Fixes ------------ -* **changelog-cmd**: Remove usage strings when error occured, closes `#810`_ (`348a51d`_) +* **changelog-cmd**: Remove usage strings when error occurred, closes `#810`_ (`348a51d`_) * **changelog-cmd**: Render default changelog when user template directory exist but is empty (`bded8de`_) @@ -1077,7 +1077,7 @@ v9.8.4 (2024-07-04) * **publish-cmd**: Prevent error when provided tag does not exist locally (`16afbbb`_) -* **publish-cmd**: Remove usage strings when error occured, closes `#810`_ (`afbb187`_) +* **publish-cmd**: Remove usage strings when error occurred, closes `#810`_ (`afbb187`_) * **version-cmd**: Remove usage strings when error occurred, closes `#810`_ (`a7c17c7`_) @@ -1192,7 +1192,7 @@ v9.7.3 (2024-05-15) 🪲 Bug Fixes ------------ -* Enabled ``prelease-token`` parameter in github action (`PR#929`_, `1bb26b0`_) +* Enabled ``prerelease-token`` parameter in github action (`PR#929`_, `1bb26b0`_) .. _1bb26b0: https://github.com/python-semantic-release/python-semantic-release/commit/1bb26b0762d94efd97c06a3f1b6b10fb76901f6d .. _PR#929: https://github.com/python-semantic-release/python-semantic-release/pull/929 @@ -1705,7 +1705,7 @@ v8.7.1 (2024-01-03) 📖 Documentation ---------------- -* Add note on default envvar behaviour (`PR#780`_, `0b07cae`_) +* Add note on default envvar behavior (`PR#780`_, `0b07cae`_) * **configuration**: Change defaults definition of token default to table (`PR#786`_, `df1df0d`_) @@ -2034,7 +2034,7 @@ v8.0.3 (2023-07-21) 🪲 Bug Fixes ------------ -* Skip unparseable versions when calculating next version (`PR#649`_, `88f25ea`_) +* Skip non-parsable versions when calculating next version (`PR#649`_, `88f25ea`_) .. _88f25ea: https://github.com/python-semantic-release/python-semantic-release/commit/88f25eae62589cdf53dbc3dfcb167a3ae6cba2d3 .. _PR#649: https://github.com/python-semantic-release/python-semantic-release/pull/649 @@ -2105,7 +2105,7 @@ v8.0.0 (2023-07-16) * Make it easier to access commit messages in ParsedCommits (`PR#619`_, `ec30564`_) -* Remove publication of dists to artifact repository (`PR#619`_, `ec30564`_) +* Remove publication of ``dists/`` to artifact repository (`PR#619`_, `ec30564`_) * Rename 'upload' configuration section to 'publish' (`PR#619`_, `ec30564`_) @@ -2138,7 +2138,7 @@ v8.0.0 (2023-07-16) * Only call Github Action output callback once defaults are set (`PR#619`_, `ec30564`_) -* Remove commit amending behaviour (`PR#619`_, `ec30564`_) +* Remove commit amending behavior (`PR#619`_, `ec30564`_) * Resolve branch checkout logic in GHA (`PR#619`_, `ec30564`_) @@ -2405,7 +2405,7 @@ v7.33.0 (2023-01-15) 📖 Documentation ---------------- -* Update documentaton (`5cbdad2`_) +* Update documentation (`5cbdad2`_) .. _#521: https://github.com/python-semantic-release/python-semantic-release/issues/521 .. _#533: https://github.com/python-semantic-release/python-semantic-release/issues/533 @@ -2478,7 +2478,7 @@ v7.32.0 (2022-09-25) 📖 Documentation ---------------- -* Correct documented default behaviour for ``commit_version_number`` (`PR#497`_, `ffae2dc`_) +* Correct documented default behavior for ``commit_version_number`` (`PR#497`_, `ffae2dc`_) .. _#498: https://github.com/python-semantic-release/python-semantic-release/issues/498 .. _988437d: https://github.com/python-semantic-release/python-semantic-release/commit/988437d21e40d3e3b1c95ed66b535bdd523210de @@ -2796,7 +2796,7 @@ v7.27.1 (2022-04-03) 🪲 Bug Fixes ------------ -* **prerelase**: Pass prerelease option to get_current_version (`PR#432`_, `aabab0b`_) +* **prerelease**: Pass prerelease option to get_current_version (`PR#432`_, `aabab0b`_) .. _aabab0b: https://github.com/python-semantic-release/python-semantic-release/commit/aabab0b7ce647d25e0c78ae6566f1132ece9fcb9 .. _PR#432: https://github.com/python-semantic-release/python-semantic-release/pull/432 @@ -4079,7 +4079,7 @@ v5.0.0 (2020-03-22) 📖 Documentation ---------------- -* **pypi**: Update docstings in pypi.py (`6502d44`_) +* **pypi**: Update docstrings in pypi.py (`6502d44`_) 💥 BREAKING CHANGES ------------------- @@ -5237,7 +5237,7 @@ v2.1.0 (2015-08-20) ✨ Features ----------- -* **cli**: Add the possibility to repost the changelog (`4d028e2`_) +* **cli**: Add the possibility to re-post the changelog (`4d028e2`_) 🪲 Bug Fixes ------------ @@ -5292,7 +5292,7 @@ v2.0.0 (2015-08-19) 💥 BREAKING CHANGES ------------------- -* **history**: The default parser is now angular. Thus, the default behaviour of the commit log +* **history**: The default parser is now angular. Thus, the default behavior of the commit log evaluator will change. From now on it will use the angular commit message spec to determine the new version. diff --git a/docs/algorithm.rst b/docs/algorithm.rst index b6797bb4f..4ea9fa6dd 100644 --- a/docs/algorithm.rst +++ b/docs/algorithm.rst @@ -35,7 +35,7 @@ Implementation 1. Parse all the Git tags of the repository into semantic versions, and **sort** in descending (most recent first) order according to `semver precedence`_. - Ignore any tags which do not correspond to valid semantic vesrions according + Ignore any tags which do not correspond to valid semantic versions according to ``tag_format``. diff --git a/docs/changelog_templates.rst b/docs/changelog_templates.rst index 08f5eec78..1b6e5fc7d 100644 --- a/docs/changelog_templates.rst +++ b/docs/changelog_templates.rst @@ -49,7 +49,7 @@ PSR provides two default changelog output formats: Both formats are kept in sync with one another to display the equivalent information in the respective format. The default changelog template is located in the ``data/templates/`` directory within the PSR package. The templates are written in -modular style (ie. multiple files) and during the render proccess are ultimately +modular style (ie. multiple files) and during the render process are ultimately combined together to render the final changelog output. The rendering start point is the ``CHANGELOG.{FORMAT_EXT}.j2`` underneath the respective format directory. @@ -144,7 +144,7 @@ newly created changelog file. .. tip:: We have accomplished changelog updating through the use of the `Jinja`_ templating - and addtional context filters and context variables. This is notable because + and additional context filters and context variables. This is notable because in the case that you want to customize your changelog template, you now can use the same logic to enable changelog updates of your custom template! @@ -1220,7 +1220,7 @@ __ https://github.com/python-semantic-release/python-semantic-release/tree/maste maintain any content that should be included before the new release information. See ``data/templates/*/md/.components/changelog_update.md.j2`` for reference. -5. **Print your insertion flag.** This is impartive to ensure that the resulting +5. **Print your insertion flag.** This is imperative to ensure that the resulting changelog can be updated in the future. See ``data/templates/*/md/.components/changelog_update.md.j2`` for reference. diff --git a/docs/commands.rst b/docs/commands.rst index 66c01eff2..e7e8e5f21 100644 --- a/docs/commands.rst +++ b/docs/commands.rst @@ -272,8 +272,8 @@ the flag. This can be useful when making a single prerelease on a branch that would typically release normal versions. -If not specified in :ref:`cmd-version-option-prerelease-token`, the prerelease token is idenitified using the -:ref:`Multibranch Release Configuration ` +If not specified in :ref:`cmd-version-option-prerelease-token`, the prerelease token is identified +using the :ref:`Multibranch Release Configuration ` See the examples alongside :ref:`cmd-version-option-force-level` for how to use this flag. @@ -282,8 +282,9 @@ See the examples alongside :ref:`cmd-version-option-force-level` for how to use ``--prerelease-token [VALUE]`` ****************************** -Force the next version to use the value as the prerelease token. This overrides the configured value if one is -present. If not used during a release producing a prerelease version, this option has no effect. +Force the next version to use the value as the prerelease token. This overrides the configured +value if one is present. If not used during a release producing a prerelease version, this +option has no effect. .. _cmd-version-option-build-metadata: diff --git a/docs/configuration.rst b/docs/configuration.rst index 9163f11d8..a777c8f21 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -88,9 +88,12 @@ environment variable is: default = "default value" In this structure: + * ``env`` represents the environment variable that Python Semantic Release will search for + * ``default_env`` is a fallback environment variable to read in case the variable specified by ``env`` is not set. This is optional - if not specified then no fallback will be used. + * ``default`` is a default value to use in case the environment variable specified by ``env`` is not set. This is optional - if ``default`` is not specified then the environment variable specified by ``env`` is considered required. @@ -105,7 +108,7 @@ configuration setting. If there are type mis-matches, PSR will throw validation If a setting is not provided, than PSR will fill in the value with the default value. Python Semantic Release expects a root level key to start the configuration definition. Make -sure to use the correct root key dependending on the configuration format you are using. +sure to use the correct root key depending on the configuration format you are using. .. note:: If you are using ``pyproject.toml``, this heading should include the ``tool`` prefix as specified within PEP 517, resulting in ``[tool.semantic_release]``. @@ -405,7 +408,7 @@ This setting is used to specify the output format the default changelog template will use when rendering the changelog. PSR supports both Markdown (``md``) and reStructuredText (``rst``) formats. -This setting will take presendence over the file extension of the +This setting will take precedence over the file extension of the :ref:`config-changelog-default_templates-changelog_file` setting. If this setting is omitted, the file extension of the :ref:`config-changelog-default_templates-changelog_file` setting will be used to determine the output format. If the file extension is not recognized, @@ -695,10 +698,6 @@ A string that will be used to identify where the new version should be inserted changelog file (as defined by :ref:`config-changelog-changelog_file`) when the changelog mode is set to ``update``. -When the changelog mode is set to ``init``, this string will be included as part of the -header of the changelog file to initialize the changelog with a format that will be condusive -for future version insertions. - If you modify this value in your config, you will need to manually update any saved changelog file to match the new insertion flag if you use the ``update`` mode. In ``init`` mode, the changelog file will be overwritten as normal. @@ -965,7 +964,7 @@ calls rather than the primary domain (ex. ``api.github.com``). **Most on-premise HVCS installations will NOT use this setting!** Whether or not this value is used depends on the HVCS configured (and your server administration) -in the :ref:`remote.type ` setting and used in tadem with the +in the :ref:`remote.type ` setting and used in tandem with the :ref:`remote.domain ` setting. When using a custom :ref:`remote.domain ` and a HVCS @@ -1058,7 +1057,7 @@ used for the connection. If the protocol scheme is provided in the field value, match this setting or it will throw an error. The purpose of this flag is to prevent any typos in provided ``domain`` and ``api_domain`` -values that accidently specify an insecure connection but allow users to toggle the protection +values that accidentally specify an insecure connection but allow users to toggle the protection scheme off when desired. **Default:** ``false`` diff --git a/docs/index.rst b/docs/index.rst index 16eaabec0..f8273c5b7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -219,7 +219,7 @@ and changelogs. The user has three options to push changes to the repository: - :ref:`Changelog ` - customize your project's changelog. - :ref:`changelog-templates-custom_release_notes` - customize the published release notes - :ref:`upload_to_vcs_release ` - - enable/disable uploading artefacts to VCS releases + enable/disable uploading artifacts to VCS releases - :ref:`version --vcs-release/--no-vcs-release ` - enable/disable VCS release creation. - `upload-to-gh-release`_, a GitHub Action for running ``semantic-release publish`` diff --git a/docs/multibranch_releases.rst b/docs/multibranch_releases.rst index 97574be6d..276561a35 100644 --- a/docs/multibranch_releases.rst +++ b/docs/multibranch_releases.rst @@ -45,12 +45,12 @@ a situation may occur in the following scenario: Suppose that Python Semantic Release has been configured to use the same prerelease token ``"alpha"`` for all ``feature-*`` branches, and the default tag format ``"v{version}"``. In this case, running a pre-release from branch ``feature-1`` -will recognise that since the last release, ``1.1.0``, a **feature** has been +will recognize that since the last release, ``1.1.0``, a **feature** has been introduced and therefore the next tag to be applied to ``feature-1`` will be ``v1.2.0-alpha.1``. However, suppose we then try to run a release against ``feature-2``. This will also -recognise that a **feature** has been introduced against the last released version of +recognize that a **feature** has been introduced against the last released version of ``v1.1.0`` and therefore will try to create the tag ``v1.2.0-alpha.1``, leading to an error as this tag was already created against ``feature-1``. @@ -91,7 +91,7 @@ Each release group is configured as a nested mapping under the ``tool.semantic_release.branches`` key in ``pyproject.toml``, or the equivalent structure in other formats. the mapping requires a single key that is used as a name for the release group, which can help to identify it in log messages but has -no effect on the behaviour of the release. For example, Python Semantic Release has +no effect on the behavior of the release. For example, Python Semantic Release has only one release group by default with the name ``main``. Inside each release group, the following key-value pairs can be set: diff --git a/docs/strict_mode.rst b/docs/strict_mode.rst index 4172855b6..475398244 100644 --- a/docs/strict_mode.rst +++ b/docs/strict_mode.rst @@ -4,7 +4,7 @@ Strict Mode =========== Strict Mode is enabled by use of the :ref:`strict ` parameter -to the main command for Python Semantic Release. Strict Mode alters the behaviour of +to the main command for Python Semantic Release. Strict Mode alters the behavior of Python Semantic Release when certain conditions are encountered that prevent Python Semantic Release from performing an action. Typically, this will result in a warning becoming an error, or a different exit code (0 vs non-zero) being produced when Python diff --git a/docs/troubleshooting.rst b/docs/troubleshooting.rst index 04e9c6331..d001104d4 100644 --- a/docs/troubleshooting.rst +++ b/docs/troubleshooting.rst @@ -10,7 +10,8 @@ Troubleshooting .. _troubleshooting-verbosity: Increasing Verbosity -==================== +-------------------- + If you are having trouble with Python Semantic Release or would like to see additional information about the actions that it is taking, you can use the top-level :ref:`cmd-main-option-verbosity` option. This can be supplied multiple times to increase @@ -24,12 +25,10 @@ For example:: semantic-release -vv version --print - .. note:: The :ref:`cmd-main-option-verbosity` option must be supplied to the top-level ``semantic-release`` command, before the name of any sub-command. - .. warning:: The volume of logs when using ``DEBUG`` verbosity may be significantly increased, compared to ``INFO`` or the default ``WARNING``, and as a result executing commands From 903c8ba68d797f7cd9e5025c9a3a3ad471c805ae Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 11 Feb 2025 00:03:33 -0700 Subject: [PATCH 046/160] fix(config): handle invalid `commit_parser` type gracefully (#1180) * refactor(cli): improve global error logging --- src/semantic_release/__main__.py | 10 +++++++--- src/semantic_release/cli/commands/main.py | 9 +++------ src/semantic_release/cli/config.py | 11 +++++++++++ src/semantic_release/globals.py | 6 ++++-- src/semantic_release/helpers.py | 6 ++++++ 5 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/semantic_release/__main__.py b/src/semantic_release/__main__.py index 7ec9adac7..5743bcace 100644 --- a/src/semantic_release/__main__.py +++ b/src/semantic_release/__main__.py @@ -8,6 +8,7 @@ from semantic_release import globals from semantic_release.cli.commands.main import main as cli_main +from semantic_release.enums import SemanticReleaseLogLevels def main() -> None: @@ -18,7 +19,7 @@ def main() -> None: print("\n-- User Abort! --", file=sys.stderr) sys.exit(127) except Exception as err: # noqa: BLE001, graceful error handling across application - if globals.debug: + if globals.log_level <= SemanticReleaseLogLevels.DEBUG: print(f"{err.__class__.__name__}: {err}\n", file=sys.stderr) etype, value, traceback = sys.exc_info() print( @@ -35,9 +36,12 @@ def main() -> None: file=sys.stderr, ) - print(f"::ERROR:: {err}", file=sys.stderr) + print( + str.join("\n", [f"::ERROR:: {line}" for line in str(err).splitlines()]), + file=sys.stderr, + ) - if not globals.debug: + if globals.log_level > SemanticReleaseLogLevels.DEBUG: print( "Run semantic-release in very verbose mode (-vv) to see the full traceback.", file=sys.stderr, diff --git a/src/semantic_release/cli/commands/main.py b/src/semantic_release/cli/commands/main.py index 885898dfb..7f0d170e2 100644 --- a/src/semantic_release/cli/commands/main.py +++ b/src/semantic_release/cli/commands/main.py @@ -116,10 +116,10 @@ def main( SemanticReleaseLogLevels.SILLY, ] - log_level = log_levels[verbosity] + globals.log_level = log_levels[verbosity] logging.basicConfig( - level=log_level, + level=globals.log_level, format=FORMAT, datefmt="[%X]", handlers=[ @@ -130,10 +130,7 @@ def main( ) logger = logging.getLogger(__name__) - logger.debug("logging level set to: %s", logging.getLevelName(log_level)) - - if log_level <= logging.DEBUG: - globals.debug = True + logger.debug("logging level set to: %s", logging.getLevelName(globals.log_level)) if noop: rprint( diff --git a/src/semantic_release/cli/config.py b/src/semantic_release/cli/config.py index 496e97428..721028281 100644 --- a/src/semantic_release/cli/config.py +++ b/src/semantic_release/cli/config.py @@ -665,6 +665,17 @@ def from_raw_config( # noqa: C901 if raw.commit_parser in _known_commit_parsers else dynamic_import(raw.commit_parser) ) + except ValueError as err: + raise ParserLoadError( + str.join( + "\n", + [ + f"Unrecognized commit parser value: {raw.commit_parser!r}.", + str(err), + "Unable to load the given parser! Check your configuration!", + ], + ) + ) from err except ModuleNotFoundError as err: raise ParserLoadError( str.join( diff --git a/src/semantic_release/globals.py b/src/semantic_release/globals.py index 3780b84bb..a0ac61ddb 100644 --- a/src/semantic_release/globals.py +++ b/src/semantic_release/globals.py @@ -2,5 +2,7 @@ from __future__ import annotations -debug: bool = False -"""bool: Enable debug level logging and runtime actions.""" +from semantic_release.enums import SemanticReleaseLogLevels + +log_level: SemanticReleaseLogLevels = SemanticReleaseLogLevels.WARNING +"""int: Logging level for semantic-release""" diff --git a/src/semantic_release/helpers.py b/src/semantic_release/helpers.py index 46358b8d0..5f6723ec4 100644 --- a/src/semantic_release/helpers.py +++ b/src/semantic_release/helpers.py @@ -157,6 +157,12 @@ def dynamic_import(import_path: str) -> Any: Dynamically import an object from a conventionally formatted "module:attribute" string """ + if ":" not in import_path: + raise ValueError( + f"Invalid import path {import_path!r}, must use 'module:Class' format" + ) + + # Split the import path into module and attribute module_name, attr = import_path.split(":", maxsplit=1) # Check if the module is a file path, if it can be resolved and exists on disk then import as a file From 81f9e80c3df185ef5e553e024b903ce153e14304 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 11 Feb 2025 00:15:26 -0700 Subject: [PATCH 047/160] fix(changelog): standardize heading format for across all version sections (#1182) * test(fixtures): update changelog generator to match adjusted default template * test(release-notes): update test cases to match adjusted default template * test(changelog): update test cases to match adjusted default template * fix(changelog-rst): standardize heading format for extra release information * fix(changelog-md): standardize heading format for extra release information * fix(release-notes): standardize heading format for extra release information * docs(commit-parsing): update reference to section name of additional release info * chore(psr-changelog-tpls): standardize all headings & resolve additnl release info usage --- config/release-templates/.components/changes.md.j2 | 10 +++++----- config/release-templates/.components/changes.rst.j2 | 8 ++++---- config/release-templates/.release_notes.md.j2 | 4 ++-- docs/commit_parsing.rst | 2 +- .../templates/angular/md/.components/changes.md.j2 | 8 ++++---- .../data/templates/angular/md/.release_notes.md.j2 | 4 ++-- .../templates/angular/rst/.components/changes.rst.j2 | 8 ++++---- tests/fixtures/git_repo.py | 6 +++--- .../changelog/test_default_changelog.py | 12 ++++++------ .../semantic_release/changelog/test_release_notes.py | 12 ++++++------ 10 files changed, 37 insertions(+), 37 deletions(-) diff --git a/config/release-templates/.components/changes.md.j2 b/config/release-templates/.components/changes.md.j2 index 87d600924..d2a062d9a 100644 --- a/config/release-templates/.components/changes.md.j2 +++ b/config/release-templates/.components/changes.md.j2 @@ -19,7 +19,7 @@ EXAMPLE: - Fix bug ([#11](https://domain.com/namespace/repo/pull/11), [`abcdef1`](https://domain.com/namespace/repo/commit/HASH)) -### 💥 BREAKING CHANGES +### 💥 Breaking Changes - With the change _____, the change causes ___ effect. Ultimately, this section it is a more detailed description of the breaking change. With an optional @@ -28,7 +28,7 @@ EXAMPLE: - **scope**: this breaking change has a scope to identify the part of the code that this breaking change applies to for better context. -### 💡 ADDITIONAL RELEASE INFORMATION +### 💡 Additional Release Information - This is a release note that provides additional information about the release that is not a breaking change or a feature/bug fix. @@ -65,7 +65,7 @@ EXAMPLE: #}{% set breaking_commits = [] %}{% set notice_commits = [] %}{% for commits in commit_objects.values() -%}{% set valid_commits = commits | rejectattr("error", "defined") +%}{% set valid_commits = commits | rejectattr("error", "defined") | list %}{# # Filter out breaking change commits that have no breaking descriptions #}{% set _ = breaking_commits.extend( valid_commits | selectattr("breaking_descriptions.0") @@ -94,7 +94,7 @@ EXAMPLE: %}{# # # PRINT BREAKING CHANGE DESCRIPTIONS (header & descriptions) #}{{ "\n" -}}{{ "### %s BREAKING CHANGES\n" | format(emoji_map["breaking"]) +}}{{ "### %s Breaking Changes\n" | format(emoji_map["breaking"]) }}{{ "\n%s\n" | format(brking_descriptions | unique | join("\n\n")) }}{# @@ -118,7 +118,7 @@ EXAMPLE: %}{# # # PRINT RELEASE NOTICE INFORMATION (header & descriptions) #}{{ "\n" -}}{{ "### %s ADDITIONAL RELEASE INFORMATION\n" | format(emoji_map["release_note"]) +}}{{ "### %s Additional Release Information\n" | format(emoji_map["release_note"]) }}{{ "\n%s\n" | format(release_notices | unique | join("\n\n")) }}{# diff --git a/config/release-templates/.components/changes.rst.j2 b/config/release-templates/.components/changes.rst.j2 index ba9180e87..90434bfdb 100644 --- a/config/release-templates/.components/changes.rst.j2 +++ b/config/release-templates/.components/changes.rst.j2 @@ -20,7 +20,7 @@ * Fix bug (`#11`_, `8a7b8ec`_) -💥 BREAKING CHANGES +💥 Breaking Changes ------------------- * With the change _____, the change causes ___ effect. Ultimately, this section @@ -30,7 +30,7 @@ * **scope**: this breaking change has a scope to identify the part of the code that this breaking change applies to for better context. -💡 ADDITIONAL RELEASE INFORMATION +💡 Additional Release Information --------------------------------- * This is a release note that provides additional information about the release @@ -132,7 +132,7 @@ %}{# # # PRINT BREAKING CHANGE DESCRIPTIONS (header & descriptions) #}{{ "\n" -}}{{ "%s BREAKING CHANGES\n" | format(emoji_map["breaking"]) +}}{{ "%s Breaking Changes\n" | format(emoji_map["breaking"]) }}{{ '-------------------\n' }}{{ "\n%s\n" | format(brking_descriptions | unique | join("\n\n")) @@ -157,7 +157,7 @@ %}{# # # PRINT RELEASE NOTICE INFORMATION (header & descriptions) #}{{ "\n" -}}{{ "%s ADDITIONAL RELEASE INFORMATION\n" | format(emoji_map["release_note"]) +}}{{ "%s Additional Release Information\n" | format(emoji_map["release_note"]) }}{{ "---------------------------------\n" }}{{ "\n%s\n" | format(release_notices | unique | join("\n\n")) diff --git a/config/release-templates/.release_notes.md.j2 b/config/release-templates/.release_notes.md.j2 index 584269ead..ee9fde4c4 100644 --- a/config/release-templates/.release_notes.md.j2 +++ b/config/release-templates/.release_notes.md.j2 @@ -15,13 +15,13 @@ _This release is published under the MIT License._ - Fix bug ([PR#11](https://domain.com/namespace/repo/pull/11), [`abcdef1`](https://domain.com/namespace/repo/commit/HASH)) -### 💥 BREAKING CHANGES +### 💥 Breaking Changes - With the change _____, the change causes ___ effect. Ultimately, this section it is a more detailed description of the breaking change. With an optional scope prefix like the commit messages above. - **scope**: this breaking change has a scope to identify the part of the code that this breaking change applies to for better context. -### 💡 ADDITIONAL RELEASE INFORMATION +### 💡 Additional Release Information - This is a release note that provides additional information about the release that is not a breaking change or a feature/bug fix. diff --git a/docs/commit_parsing.rst b/docs/commit_parsing.rst index 25e70eb45..65e523d98 100644 --- a/docs/commit_parsing.rst +++ b/docs/commit_parsing.rst @@ -423,7 +423,7 @@ a ``release_notices`` attribute that is a tuple of string paragraphs to identify release notice. In the default changelog and release notes template, these release notices will be -formatted into their own section called **ADDITIONAL RELEASE INFORMATION**. Each will +formatted into their own section called **Additional Release Information**. Each will include any commit scope defined and each release notice in alphabetical order. ---- diff --git a/src/semantic_release/data/templates/angular/md/.components/changes.md.j2 b/src/semantic_release/data/templates/angular/md/.components/changes.md.j2 index bf4cd98b5..b2b89ff12 100644 --- a/src/semantic_release/data/templates/angular/md/.components/changes.md.j2 +++ b/src/semantic_release/data/templates/angular/md/.components/changes.md.j2 @@ -18,7 +18,7 @@ EXAMPLE: - Fix bug ([#11](https://domain.com/namespace/repo/pull/11), [`abcdef1`](https://domain.com/namespace/repo/commit/HASH)) -### BREAKING CHANGES +### Breaking Changes - With the change _____, the change causes ___ effect. Ultimately, this section it is a more detailed description of the breaking change. With an optional @@ -27,7 +27,7 @@ EXAMPLE: - **scope**: this breaking change has a scope to identify the part of the code that this breaking change applies to for better context. -### ADDITIONAL RELEASE INFORMATION +### Additional Release Information - This is a release note that provides additional information about the release that is not a breaking change or a feature/bug fix. @@ -96,7 +96,7 @@ EXAMPLE: %}{# # # PRINT BREAKING CHANGE DESCRIPTIONS (header & descriptions) #}{{ "\n" -}}{{ "### BREAKING CHANGES\n" +}}{{ "### Breaking Changes\n" }}{{ "\n%s\n" | format(brking_descriptions | unique | join("\n\n")) }}{# @@ -129,7 +129,7 @@ EXAMPLE: %}{# # # PRINT RELEASE NOTICE INFORMATION (header & descriptions) #}{{ "\n" -}}{{ "### ADDITIONAL RELEASE INFORMATION\n" +}}{{ "### Additional Release Information\n" }}{{ "\n%s\n" | format(release_notices | unique | join("\n\n")) }}{# diff --git a/src/semantic_release/data/templates/angular/md/.release_notes.md.j2 b/src/semantic_release/data/templates/angular/md/.release_notes.md.j2 index 8bfd679cc..7fe7d9228 100644 --- a/src/semantic_release/data/templates/angular/md/.release_notes.md.j2 +++ b/src/semantic_release/data/templates/angular/md/.release_notes.md.j2 @@ -14,13 +14,13 @@ _This release is published under the MIT License._ - Fix bug (#11, [`abcdef1`](https://domain.com/namespace/repo/commit/HASH)) -### BREAKING CHANGES +### Breaking Changes - With the change _____, the change causes ___ effect. Ultimately, this section it is a more detailed description of the breaking change. With an optional scope prefix like the commit messages above. - **scope**: this breaking change has a scope to identify the part of the code that this breaking change applies to for better context. -### ADDITIONAL RELEASE INFORMATION +### Additional Release Information - This is a release note that provides additional information about the release that is not a breaking change or a feature/bug fix. diff --git a/src/semantic_release/data/templates/angular/rst/.components/changes.rst.j2 b/src/semantic_release/data/templates/angular/rst/.components/changes.rst.j2 index 2ff88c892..c6ef1cced 100644 --- a/src/semantic_release/data/templates/angular/rst/.components/changes.rst.j2 +++ b/src/semantic_release/data/templates/angular/rst/.components/changes.rst.j2 @@ -18,7 +18,7 @@ Bug Fixes * Fix bug (`#11`_, `8a7b8ec`_) -BREAKING CHANGES +Breaking Changes ---------------- * With the change _____, the change causes ___ effect. Ultimately, this section @@ -28,7 +28,7 @@ BREAKING CHANGES * **scope**: this breaking change has a scope to identify the part of the code that this breaking change applies to for better context. -ADDITIONAL RELEASE INFORMATION +Additional Release Information ------------------------------ * This is a release note that provides additional information about the release @@ -124,7 +124,7 @@ ADDITIONAL RELEASE INFORMATION %}{# # # PRINT BREAKING CHANGE DESCRIPTIONS (header & descriptions) #}{{ "\n" -}}{{ "BREAKING CHANGES\n" +}}{{ "Breaking Changes\n" }}{{ '----------------\n' }}{{ "\n%s\n" | format(brking_descriptions | unique | join("\n\n")) @@ -158,7 +158,7 @@ ADDITIONAL RELEASE INFORMATION %}{# # # PRINT RELEASE NOTICE INFORMATION (header & descriptions) #}{{ "\n" -}}{{ "ADDITIONAL RELEASE INFORMATION\n" +}}{{ "Additional Release Information\n" }}{{ "------------------------------\n" }}{{ "\n%s\n" | format(release_notices | unique | join("\n\n")) diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 0c8faeeb5..048f77b3a 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -1689,7 +1689,7 @@ def build_version_entry_markdown( # Add breaking changes to the end of the version entry if brking_descriptions: - version_entry.append("### BREAKING CHANGES\n") + version_entry.append("### Breaking Changes\n") version_entry.extend([*sorted(brking_descriptions), ""]) return str.join("\n", version_entry) @@ -1822,7 +1822,7 @@ def build_version_entry_restructured_text( # Add breaking changes to the end of the version entry if brking_descriptions: - version_entry.append("BREAKING CHANGES") + version_entry.append("Breaking Changes") version_entry.append("-" * len(version_entry[-1]) + "\n") version_entry.extend([*sorted(brking_descriptions), ""]) @@ -2067,7 +2067,7 @@ def build_version_entry_markdown( # Add breaking changes to the end of the version entry if brking_descriptions: - version_entry.append("### BREAKING CHANGES\n") + version_entry.append("### Breaking Changes\n") version_entry.extend([*sorted(brking_descriptions), ""]) return str.join("\n", version_entry) diff --git a/tests/unit/semantic_release/changelog/test_default_changelog.py b/tests/unit/semantic_release/changelog/test_default_changelog.py index e3c01b7d7..adbbdc241 100644 --- a/tests/unit/semantic_release/changelog/test_default_changelog.py +++ b/tests/unit/semantic_release/changelog/test_default_changelog.py @@ -180,7 +180,7 @@ def test_default_changelog_template_w_a_brk_change( f"- **{brk_fix_commit_obj.scope}**: {brk_fix_description.capitalize()}", f" ([`{brk_fix_commit_obj.commit.hexsha[:7]}`]({brk_fix_commit_url}))", "", - "### BREAKING CHANGES", + "### Breaking Changes", "", # Currently does not consider the 100 character limit because the current # descriptions are short enough to fit in one line @@ -317,7 +317,7 @@ def test_default_changelog_template_w_multiple_brk_changes( f"- {brk_feat_description.capitalize()}", f" ([`{brk_feat_commit_obj.commit.hexsha[:7]}`]({brk_feat_commit_url}))", "", - "### BREAKING CHANGES", + "### Breaking Changes", "", # Currently does not consider the 100 character limit because the current # descriptions are short enough to fit in one line @@ -638,7 +638,7 @@ def test_default_changelog_template_w_a_notice( f"- **{notice_commit_obj.scope}**: {notice_commit_description.capitalize().rstrip()}", f" ([`{notice_commit_obj.commit.hexsha[:7]}`]({notice_commit_url}))", "", - "### ADDITIONAL RELEASE INFORMATION", + "### Additional Release Information", "", "- {commit_scope}{change_desc}".format( commit_scope=( @@ -787,7 +787,7 @@ def test_default_changelog_template_w_a_notice_n_brk_change( ), f" ([`{notice_commit_obj.commit.hexsha[:7]}`]({notice_commit_url}))", "", - "### BREAKING CHANGES", + "### Breaking Changes", "", "- {commit_scope}{change_desc}".format( commit_scope=( @@ -798,7 +798,7 @@ def test_default_changelog_template_w_a_notice_n_brk_change( change_desc=brk_fix_brking_description.capitalize().rstrip(), ), "", - "### ADDITIONAL RELEASE INFORMATION", + "### Additional Release Information", "", "- {commit_scope}{change_desc}".format( commit_scope=( @@ -947,7 +947,7 @@ def test_default_changelog_template_w_multiple_notices( ), f" ([`{refactor_notice_commit_obj.commit.hexsha[:7]}`]({refactor_commit_url}))", "", - "### ADDITIONAL RELEASE INFORMATION", + "### Additional Release Information", "", "- {commit_scope}{change_desc}".format( commit_scope=( diff --git a/tests/unit/semantic_release/changelog/test_release_notes.py b/tests/unit/semantic_release/changelog/test_release_notes.py index f0c959c27..2b95ec827 100644 --- a/tests/unit/semantic_release/changelog/test_release_notes.py +++ b/tests/unit/semantic_release/changelog/test_release_notes.py @@ -211,7 +211,7 @@ def test_default_release_notes_template_w_a_brk_description( url=brk_fix_commit_url, ), "", - "### BREAKING CHANGES", + "### Breaking Changes", "", "- {commit_scope}{change_desc}".format( commit_scope=( @@ -323,7 +323,7 @@ def test_default_release_notes_template_w_multiple_brk_changes( url=brk_feat_commit_url, ), "", - "### BREAKING CHANGES", + "### Breaking Changes", "", "- {commit_scope}{change_desc}".format( commit_scope=( @@ -770,7 +770,7 @@ def test_default_release_notes_template_w_a_notice( url=notice_commit_url, ), "", - "### ADDITIONAL RELEASE INFORMATION", + "### Additional Release Information", "", "- {commit_scope}{change_desc}".format( commit_scope=( @@ -880,7 +880,7 @@ def test_default_release_notes_template_w_a_notice_n_brk_change( url=notice_commit_url, ), "", - "### BREAKING CHANGES", + "### Breaking Changes", "", "- {commit_scope}{change_desc}".format( commit_scope=( @@ -891,7 +891,7 @@ def test_default_release_notes_template_w_a_notice_n_brk_change( change_desc=brk_fix_brking_description.capitalize().rstrip(), ), "", - "### ADDITIONAL RELEASE INFORMATION", + "### Additional Release Information", "", "- {commit_scope}{change_desc}".format( commit_scope=( @@ -997,7 +997,7 @@ def test_default_release_notes_template_w_multiple_notices( url=refactor_commit_url, ), "", - "### ADDITIONAL RELEASE INFORMATION", + "### Additional Release Information", "", "- {commit_scope}{change_desc}".format( commit_scope=( From 0a6013c422396066ede0383ca2c443859397252d Mon Sep 17 00:00:00 2001 From: semantic-release Date: Tue, 11 Feb 2025 07:27:52 +0000 Subject: [PATCH 048/160] 9.19.1 Automatically generated by python-semantic-release --- CHANGELOG.rst | 39 ++++++++++++++++++++++ docs/automatic-releases/github-actions.rst | 14 ++++---- pyproject.toml | 2 +- src/semantic_release/__init__.py | 2 +- 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f2bc511ae..db8c86e47 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,45 @@ CHANGELOG ========= +.. _changelog-v9.19.1: + +v9.19.1 (2025-02-11) +==================== + +🪲 Bug Fixes +------------ + +* **changelog**: Standardize heading format for across all version sections (`PR#1182`_, `81f9e80`_) + +* **changelog-md**: Standardize heading format for extra release information (`PR#1182`_, + `81f9e80`_) + +* **changelog-rst**: Standardize heading format for extra release information (`PR#1182`_, + `81f9e80`_) + +* **config**: Handle invalid ``commit_parser`` type gracefully (`PR#1180`_, `903c8ba`_) + +* **release-notes**: Standardize heading format for extra release information (`PR#1182`_, + `81f9e80`_) + +📖 Documentation +---------------- + +* Fix spelling errors & inaccurate descriptions (`55d4a05`_) + +* **automatic-releases**: Declutter the table of contents for automatic release guides (`e8343ee`_) + +* **commit-parsing**: Update reference to section name of additional release info (`PR#1182`_, + `81f9e80`_) + +.. _55d4a05: https://github.com/python-semantic-release/python-semantic-release/commit/55d4a05ff56321cf9874f8f302fbe7e5163ad4f7 +.. _81f9e80: https://github.com/python-semantic-release/python-semantic-release/commit/81f9e80c3df185ef5e553e024b903ce153e14304 +.. _903c8ba: https://github.com/python-semantic-release/python-semantic-release/commit/903c8ba68d797f7cd9e5025c9a3a3ad471c805ae +.. _e8343ee: https://github.com/python-semantic-release/python-semantic-release/commit/e8343eeb38d3b4e18953ac0f97538df396d22b76 +.. _PR#1180: https://github.com/python-semantic-release/python-semantic-release/pull/1180 +.. _PR#1182: https://github.com/python-semantic-release/python-semantic-release/pull/1182 + + .. _changelog-v9.19.0: v9.19.0 (2025-02-10) diff --git a/docs/automatic-releases/github-actions.rst b/docs/automatic-releases/github-actions.rst index 2ea4ba2ff..b192eb6f9 100644 --- a/docs/automatic-releases/github-actions.rst +++ b/docs/automatic-releases/github-actions.rst @@ -337,7 +337,7 @@ before the :ref:`version ` subcommand. .. code:: yaml - - uses: python-semantic-release/python-semantic-release@v9.19.0 + - uses: python-semantic-release/python-semantic-release@v9.19.1 with: root_options: "-vv --noop" @@ -576,7 +576,7 @@ before the :ref:`publish ` subcommand. .. code:: yaml - - uses: python-semantic-release/publish-action@v9.19.0 + - uses: python-semantic-release/publish-action@v9.19.1 with: root_options: "-vv --noop" @@ -684,7 +684,7 @@ to the GitHub Release Assets as well. - name: Action | Semantic Version Release id: release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.19.0 + uses: python-semantic-release/python-semantic-release@v9.19.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} git_committer_name: "github-actions" @@ -695,7 +695,7 @@ to the GitHub Release Assets as well. if: steps.release.outputs.released == 'true' - name: Publish | Upload to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.19.0 + uses: python-semantic-release/publish-action@v9.19.1 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -744,7 +744,7 @@ The equivalent GitHub Action configuration would be: - name: Action | Semantic Version Release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.19.0 + uses: python-semantic-release/python-semantic-release@v9.19.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} force: patch @@ -772,13 +772,13 @@ Publish Action. .. code:: yaml - name: Release Project 1 - uses: python-semantic-release/python-semantic-release@v9.19.0 + uses: python-semantic-release/python-semantic-release@v9.19.1 with: directory: ./project1 github_token: ${{ secrets.GITHUB_TOKEN }} - name: Release Project 2 - uses: python-semantic-release/python-semantic-release@v9.19.0 + uses: python-semantic-release/python-semantic-release@v9.19.1 with: directory: ./project2 github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/pyproject.toml b/pyproject.toml index c6e99d898..f0d1eb911 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.19.0" +version = "9.19.1" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } diff --git a/src/semantic_release/__init__.py b/src/semantic_release/__init__.py index 790c3034f..cad1767ec 100644 --- a/src/semantic_release/__init__.py +++ b/src/semantic_release/__init__.py @@ -24,7 +24,7 @@ tags_and_versions, ) -__version__ = "9.19.0" +__version__ = "9.19.1" __all__ = [ "CommitParser", From eccdb59ccc2a14e4a2c8bb76b33ea6031f70ecae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 00:45:21 -0700 Subject: [PATCH 049/160] ci(deps): bump `python-semantic-release/publish-action@v9.19.0` to 9.19.1 (#1184) --- .github/workflows/cicd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 016c553a4..d38b53f6c 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -138,7 +138,7 @@ jobs: build: false - name: Release | Add distribution artifacts to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.19.0 + uses: python-semantic-release/publish-action@v9.19.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} tag: ${{ steps.release.outputs.tag }} From a900b2b21318a8a59cdb25c3d99de732340b77bb Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 16 Feb 2025 16:23:17 -0700 Subject: [PATCH 050/160] ci(tests-e2e): mark long running tests to prevent windows execution --- .../git_flow/test_repo_1_channel.py | 14 +- .../git_flow/test_repo_2_channels.py | 14 +- .../git_flow/test_repo_3_channels.py | 16 +- .../git_flow/test_repo_4_channels.py | 14 +- .../github_flow/test_repo_1_channel.py | 14 +- .../github_flow/test_repo_2_channels.py | 14 +- .../test_repo_trunk_dual_version_support.py | 14 +- ...runk_dual_version_support_w_prereleases.py | 14 +- .../test_repo_trunk_w_prereleases.py | 14 +- tests/e2e/cmd_version/test_version_bump.py | 1218 ++++++++--------- 10 files changed, 640 insertions(+), 706 deletions(-) diff --git a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_1_channel.py b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_1_channel.py index 74cd8c7e4..e12ca31e6 100644 --- a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_1_channel.py +++ b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_1_channel.py @@ -45,14 +45,12 @@ @pytest.mark.parametrize( "repo_fixture_name", [ - repo_w_git_flow_conventional_commits.__name__, - *[ - pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) - for repo_fixture_name in [ - repo_w_git_flow_emoji_commits.__name__, - repo_w_git_flow_scipy_commits.__name__, - ] - ], + pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) + for repo_fixture_name in [ + repo_w_git_flow_conventional_commits.__name__, + repo_w_git_flow_emoji_commits.__name__, + repo_w_git_flow_scipy_commits.__name__, + ] ], ) def test_gitflow_repo_rebuild_1_channel( diff --git a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_2_channels.py b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_2_channels.py index 31e041770..035f679bd 100644 --- a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_2_channels.py +++ b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_2_channels.py @@ -45,14 +45,12 @@ @pytest.mark.parametrize( "repo_fixture_name", [ - repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__, - *[ - pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) - for repo_fixture_name in [ - repo_w_git_flow_w_alpha_prereleases_n_emoji_commits.__name__, - repo_w_git_flow_w_alpha_prereleases_n_scipy_commits.__name__, - ] - ], + pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) + for repo_fixture_name in [ + repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__, + repo_w_git_flow_w_alpha_prereleases_n_emoji_commits.__name__, + repo_w_git_flow_w_alpha_prereleases_n_scipy_commits.__name__, + ] ], ) def test_gitflow_repo_rebuild_2_channels( diff --git a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_3_channels.py b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_3_channels.py index c3ee44cac..825b7f7c3 100644 --- a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_3_channels.py +++ b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_3_channels.py @@ -46,15 +46,13 @@ @pytest.mark.parametrize( "repo_fixture_name", [ - repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits.__name__, - *[ - pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) - for repo_fixture_name in [ - repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits_using_tag_format.__name__, - repo_w_git_flow_w_rc_n_alpha_prereleases_n_emoji_commits.__name__, - repo_w_git_flow_w_rc_n_alpha_prereleases_n_scipy_commits.__name__, - ] - ], + pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) + for repo_fixture_name in [ + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits.__name__, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits_using_tag_format.__name__, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_emoji_commits.__name__, + repo_w_git_flow_w_rc_n_alpha_prereleases_n_scipy_commits.__name__, + ] ], ) def test_gitflow_repo_rebuild_3_channels( diff --git a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_4_channels.py b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_4_channels.py index 3e2b6c14c..e1cadb5a6 100644 --- a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_4_channels.py +++ b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_4_channels.py @@ -45,14 +45,12 @@ @pytest.mark.parametrize( "repo_fixture_name", [ - repo_w_git_flow_w_beta_alpha_rev_prereleases_n_conventional_commits.__name__, - *[ - pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) - for repo_fixture_name in [ - repo_w_git_flow_w_beta_alpha_rev_prereleases_n_emoji_commits.__name__, - repo_w_git_flow_w_beta_alpha_rev_prereleases_n_scipy_commits.__name__, - ] - ], + pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) + for repo_fixture_name in [ + repo_w_git_flow_w_beta_alpha_rev_prereleases_n_conventional_commits.__name__, + repo_w_git_flow_w_beta_alpha_rev_prereleases_n_emoji_commits.__name__, + repo_w_git_flow_w_beta_alpha_rev_prereleases_n_scipy_commits.__name__, + ] ], ) def test_gitflow_repo_rebuild_4_channels( diff --git a/tests/e2e/cmd_version/bump_version/github_flow/test_repo_1_channel.py b/tests/e2e/cmd_version/bump_version/github_flow/test_repo_1_channel.py index 0ce3dae7c..e836716d6 100644 --- a/tests/e2e/cmd_version/bump_version/github_flow/test_repo_1_channel.py +++ b/tests/e2e/cmd_version/bump_version/github_flow/test_repo_1_channel.py @@ -45,14 +45,12 @@ @pytest.mark.parametrize( "repo_fixture_name", [ - repo_w_github_flow_w_default_release_channel_conventional_commits.__name__, - *[ - pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) - for repo_fixture_name in [ - repo_w_github_flow_w_default_release_channel_emoji_commits.__name__, - repo_w_github_flow_w_default_release_channel_scipy_commits.__name__, - ] - ], + pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) + for repo_fixture_name in [ + repo_w_github_flow_w_default_release_channel_conventional_commits.__name__, + repo_w_github_flow_w_default_release_channel_emoji_commits.__name__, + repo_w_github_flow_w_default_release_channel_scipy_commits.__name__, + ] ], ) def test_githubflow_repo_rebuild_1_channel( diff --git a/tests/e2e/cmd_version/bump_version/github_flow/test_repo_2_channels.py b/tests/e2e/cmd_version/bump_version/github_flow/test_repo_2_channels.py index bf197bc3d..03054ac6a 100644 --- a/tests/e2e/cmd_version/bump_version/github_flow/test_repo_2_channels.py +++ b/tests/e2e/cmd_version/bump_version/github_flow/test_repo_2_channels.py @@ -45,14 +45,12 @@ @pytest.mark.parametrize( "repo_fixture_name", [ - repo_w_github_flow_w_feature_release_channel_conventional_commits.__name__, - *[ - pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) - for repo_fixture_name in [ - repo_w_github_flow_w_feature_release_channel_emoji_commits.__name__, - repo_w_github_flow_w_feature_release_channel_scipy_commits.__name__, - ] - ], + pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) + for repo_fixture_name in [ + repo_w_github_flow_w_feature_release_channel_conventional_commits.__name__, + repo_w_github_flow_w_feature_release_channel_emoji_commits.__name__, + repo_w_github_flow_w_feature_release_channel_scipy_commits.__name__, + ] ], ) def test_githubflow_repo_rebuild_2_channels( diff --git a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support.py b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support.py index ecc4e3990..6c15f2bd8 100644 --- a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support.py +++ b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support.py @@ -46,14 +46,12 @@ @pytest.mark.parametrize( "repo_fixture_name", [ - repo_w_trunk_only_dual_version_spt_conventional_commits.__name__, - *[ - pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) - for repo_fixture_name in [ - repo_w_trunk_only_dual_version_spt_emoji_commits.__name__, - repo_w_trunk_only_dual_version_spt_scipy_commits.__name__, - ] - ], + pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) + for repo_fixture_name in [ + repo_w_trunk_only_dual_version_spt_conventional_commits.__name__, + repo_w_trunk_only_dual_version_spt_emoji_commits.__name__, + repo_w_trunk_only_dual_version_spt_scipy_commits.__name__, + ] ], ) def test_trunk_repo_rebuild_dual_version_spt_official_releases_only( diff --git a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support_w_prereleases.py b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support_w_prereleases.py index 720bdfc5c..74d5f361f 100644 --- a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support_w_prereleases.py +++ b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support_w_prereleases.py @@ -46,14 +46,12 @@ @pytest.mark.parametrize( "repo_fixture_name", [ - repo_w_trunk_only_dual_version_spt_w_prereleases_conventional_commits.__name__, - *[ - pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) - for repo_fixture_name in [ - repo_w_trunk_only_dual_version_spt_w_prereleases_emoji_commits.__name__, - repo_w_trunk_only_dual_version_spt_w_prereleases_scipy_commits.__name__, - ] - ], + pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) + for repo_fixture_name in [ + repo_w_trunk_only_dual_version_spt_w_prereleases_conventional_commits.__name__, + repo_w_trunk_only_dual_version_spt_w_prereleases_emoji_commits.__name__, + repo_w_trunk_only_dual_version_spt_w_prereleases_scipy_commits.__name__, + ] ], ) def test_trunk_repo_rebuild_dual_version_spt_w_official_n_prereleases( diff --git a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_w_prereleases.py b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_w_prereleases.py index 958cac4e9..9d1171e59 100644 --- a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_w_prereleases.py +++ b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_w_prereleases.py @@ -45,14 +45,12 @@ @pytest.mark.parametrize( "repo_fixture_name", [ - repo_w_trunk_only_n_prereleases_conventional_commits.__name__, - *[ - pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) - for repo_fixture_name in [ - repo_w_trunk_only_n_prereleases_emoji_commits.__name__, - repo_w_trunk_only_n_prereleases_scipy_commits.__name__, - ] - ], + pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) + for repo_fixture_name in [ + repo_w_trunk_only_n_prereleases_conventional_commits.__name__, + repo_w_trunk_only_n_prereleases_emoji_commits.__name__, + repo_w_trunk_only_n_prereleases_scipy_commits.__name__, + ] ], ) def test_trunk_repo_rebuild_w_prereleases( diff --git a/tests/e2e/cmd_version/test_version_bump.py b/tests/e2e/cmd_version/test_version_bump.py index 6efb10a96..245c05505 100644 --- a/tests/e2e/cmd_version/test_version_bump.py +++ b/tests/e2e/cmd_version/test_version_bump.py @@ -725,93 +725,85 @@ def test_version_next_greater_than_version_one_no_bump_conventional( ), xdist_sort_hack( [ - ( - # Default case should be a minor bump since last full release was 1.1.1 - # last tag is a prerelease 1.2.0-rc.2 - lazy_fixture( - repo_w_git_flow_w_alpha_prereleases_n_emoji_commits.__name__ - ), - lazy_fixture(emoji_minor_commits.__name__), - False, - "alpha", - "1.2.0", - "main", - ), - *[ - pytest.param( - lazy_fixture(repo_fixture_name), - [] if commit_messages is None else lazy_fixture(commit_messages), - prerelease, - prerelease_token, - expected_new_version, - "main" if branch_name is None else branch_name, - marks=pytest.mark.comprehensive, - ) - for (repo_fixture_name, prerelease_token), values in { - # Latest version for repo_with_git_flow is currently 1.2.0-alpha.2 - # The last full release version was 1.1.1, so it's had a minor - # prerelease + pytest.param( + lazy_fixture(repo_fixture_name), + [] if commit_messages is None else lazy_fixture(commit_messages), + prerelease, + prerelease_token, + expected_new_version, + "main" if branch_name is None else branch_name, + marks=pytest.mark.comprehensive, + ) + for (repo_fixture_name, prerelease_token), values in { + # Latest version for repo_with_git_flow is currently 1.2.0-alpha.2 + # The last full release version was 1.1.1, so it's had a minor + # prerelease + ( + repo_w_git_flow_w_alpha_prereleases_n_emoji_commits.__name__, + "alpha", + ): [ + (emoji_patch_commits.__name__, False, "1.1.2", None), ( - repo_w_git_flow_w_alpha_prereleases_n_emoji_commits.__name__, - "alpha", - ): [ - (emoji_patch_commits.__name__, False, "1.1.2", None), - ( - emoji_patch_commits.__name__, - True, - "1.1.2-alpha.1", - None, - ), - ( - emoji_minor_commits.__name__, - True, - "1.2.0-alpha.3", - "feat/feature-4", # branch - ), - (emoji_major_commits.__name__, False, "2.0.0", None), - ( - emoji_major_commits.__name__, - True, - "2.0.0-alpha.1", - None, - ), - ], - # Latest version for repo_with_git_flow_and_release_channels is - # currently 1.1.0 + emoji_patch_commits.__name__, + True, + "1.1.2-alpha.1", + None, + ), ( - repo_w_git_flow_w_rc_n_alpha_prereleases_n_emoji_commits.__name__, - "alpha", - ): [ - (emoji_patch_commits.__name__, False, "1.1.1", None), - ( - emoji_patch_commits.__name__, - True, - "1.1.1-alpha.1", - None, - ), - (emoji_minor_commits.__name__, False, "1.2.0", None), - ( - emoji_minor_commits.__name__, - True, - "1.2.0-alpha.1", - None, - ), - (emoji_major_commits.__name__, False, "2.0.0", None), - ( - emoji_major_commits.__name__, - True, - "2.0.0-alpha.1", - None, - ), - ], - }.items() - for ( - commit_messages, - prerelease, - expected_new_version, - branch_name, - ) in values # type: ignore[attr-defined] - ], + emoji_minor_commits.__name__, + False, + "1.2.0", + None, + ), + ( + emoji_minor_commits.__name__, + True, + "1.2.0-alpha.3", + "feat/feature-4", # branch + ), + (emoji_major_commits.__name__, False, "2.0.0", None), + ( + emoji_major_commits.__name__, + True, + "2.0.0-alpha.1", + None, + ), + ], + # Latest version for repo_with_git_flow_and_release_channels is + # currently 1.1.0 + ( + repo_w_git_flow_w_rc_n_alpha_prereleases_n_emoji_commits.__name__, + "alpha", + ): [ + (emoji_patch_commits.__name__, False, "1.1.1", None), + ( + emoji_patch_commits.__name__, + True, + "1.1.1-alpha.1", + None, + ), + (emoji_minor_commits.__name__, False, "1.2.0", None), + ( + emoji_minor_commits.__name__, + True, + "1.2.0-alpha.1", + None, + ), + (emoji_major_commits.__name__, False, "2.0.0", None), + ( + emoji_major_commits.__name__, + True, + "2.0.0-alpha.1", + None, + ), + ], + }.items() + for ( + commit_messages, + prerelease, + expected_new_version, + branch_name, + ) in values # type: ignore[attr-defined] ] ), ) @@ -1032,94 +1024,86 @@ def test_version_next_greater_than_version_one_no_bump_emoji( ), xdist_sort_hack( [ - ( - # Default case should be a minor bump since last full release was 1.1.1 - # last tag is a prerelease 1.2.0-rc.2 - lazy_fixture( - repo_w_git_flow_w_alpha_prereleases_n_scipy_commits.__name__ - ), - lazy_fixture(scipy_minor_commits.__name__), - False, - "alpha", - "1.2.0", - "main", - ), - *[ - pytest.param( - lazy_fixture(repo_fixture_name), - [] if commit_messages is None else lazy_fixture(commit_messages), - prerelease, - prerelease_token, - expected_new_version, - "main" if branch_name is None else branch_name, - marks=pytest.mark.comprehensive, - ) - for (repo_fixture_name, prerelease_token), values in { - # Latest version for repo_with_git_flow is currently 1.2.0-alpha.2 - # The last full release version was 1.1.1, so it's had a minor - # prerelease + pytest.param( + lazy_fixture(repo_fixture_name), + [] if commit_messages is None else lazy_fixture(commit_messages), + prerelease, + prerelease_token, + expected_new_version, + "main" if branch_name is None else branch_name, + marks=pytest.mark.comprehensive, + ) + for (repo_fixture_name, prerelease_token), values in { + # Latest version for repo_with_git_flow is currently 1.2.0-alpha.2 + # The last full release version was 1.1.1, so it's had a minor + # prerelease + ( + repo_w_git_flow_w_alpha_prereleases_n_scipy_commits.__name__, + "alpha", + ): [ + (scipy_patch_commits.__name__, False, "1.1.2", None), ( - repo_w_git_flow_w_alpha_prereleases_n_scipy_commits.__name__, - "alpha", - ): [ - (scipy_patch_commits.__name__, False, "1.1.2", None), - ( - scipy_patch_commits.__name__, - True, - "1.1.2-alpha.1", - None, - ), - ( - scipy_minor_commits.__name__, - True, - "1.2.0-alpha.3", - "feat/feature-4", # branch - ), - (scipy_major_commits.__name__, False, "2.0.0", None), - ( - scipy_major_commits.__name__, - True, - "2.0.0-alpha.1", - None, - ), - ], - # Latest version for repo_with_git_flow_and_release_channels is - # currently 1.1.0 + scipy_patch_commits.__name__, + True, + "1.1.2-alpha.1", + None, + ), ( - repo_w_git_flow_w_rc_n_alpha_prereleases_n_scipy_commits.__name__, - "alpha", - ): [ - (scipy_patch_commits.__name__, False, "1.1.1", None), - ( - scipy_patch_commits.__name__, - True, - "1.1.1-alpha.1", - None, - ), - (scipy_minor_commits.__name__, False, "1.2.0", None), - ( - scipy_minor_commits.__name__, - True, - "1.2.0-alpha.1", - None, - ), - (scipy_major_commits.__name__, False, "2.0.0", None), - ( - scipy_major_commits.__name__, - True, - "2.0.0-alpha.1", - None, - ), - ], - }.items() - for ( - commit_messages, - prerelease, - expected_new_version, - branch_name, - ) in values # type: ignore[attr-defined] - ], - ] + scipy_minor_commits.__name__, + False, + "1.2.0", + None, + ), + ( + scipy_minor_commits.__name__, + True, + "1.2.0-alpha.3", + "feat/feature-4", # branch + ), + (scipy_major_commits.__name__, False, "2.0.0", None), + ( + scipy_major_commits.__name__, + True, + "2.0.0-alpha.1", + None, + ), + ], + # Latest version for repo_with_git_flow_and_release_channels is + # currently 1.1.0 + ( + repo_w_git_flow_w_rc_n_alpha_prereleases_n_scipy_commits.__name__, + "alpha", + ): [ + (scipy_patch_commits.__name__, False, "1.1.1", None), + ( + scipy_patch_commits.__name__, + True, + "1.1.1-alpha.1", + None, + ), + (scipy_minor_commits.__name__, False, "1.2.0", None), + ( + scipy_minor_commits.__name__, + True, + "1.2.0-alpha.1", + None, + ), + (scipy_major_commits.__name__, False, "2.0.0", None), + ( + scipy_major_commits.__name__, + True, + "2.0.0-alpha.1", + None, + ), + ], + }.items() + for ( + commit_messages, + prerelease, + expected_new_version, + branch_name, + ) in values # type: ignore[attr-defined] + ], ), ) def test_version_next_greater_than_version_one_scipy( @@ -1842,256 +1826,240 @@ def test_version_next_w_zero_dot_versions_no_bump_conventional( ), xdist_sort_hack( [ - ( + pytest.param( + lazy_fixture(repo_fixture_name), + commit_messages, + prerelease, + "rc" if prerelease_token is None else prerelease_token, + major_on_zero, + allow_zero_version, + next_release_version, + "main" if branch_name is None else branch_name, + marks=pytest.mark.comprehensive, + ) + for (repo_fixture_name, prerelease_token), values in { # Latest version for repo_with_no_tags is currently 0.0.0 (default) # It's biggest change type is minor, so the next version should be 0.1.0 - # Given the major_on_zero is False and the version is starting at 0.0.0, - # the major level commits are limited to only causing a minor level bump - lazy_fixture(repo_w_no_tags_emoji_commits.__name__), - lazy_fixture(emoji_major_commits.__name__), - False, - "rc", - False, - True, - "0.1.0", - "main", - ), - *[ - pytest.param( - lazy_fixture(repo_fixture_name), - commit_messages, - prerelease, - "rc" if prerelease_token is None else prerelease_token, - major_on_zero, - allow_zero_version, - next_release_version, - "main" if branch_name is None else branch_name, - marks=pytest.mark.comprehensive, - ) - for (repo_fixture_name, prerelease_token), values in { - # Latest version for repo_with_no_tags is currently 0.0.0 (default) - # It's biggest change type is minor, so the next version should be 0.1.0 + ( + repo_w_no_tags_emoji_commits.__name__, + None, + ): [ + *( + # when prerelease is False, & major_on_zero is False & + # allow_zero_version is True, the version should be + # 0.1.0, with the given commits + (commits, False, False, True, "0.1.0", None) + for commits in ( + # Even when this test does not change anything, the base modification + # will be a minor change and thus the version will be bumped to 0.1.0 + None, + # Non version bumping commits are absorbed into the previously detected minor bump + lazy_fixture(emoji_chore_commits.__name__), + # Patch commits are absorbed into the previously detected minor bump + lazy_fixture(emoji_patch_commits.__name__), + # Minor level commits are absorbed into the previously detected minor bump + lazy_fixture(emoji_minor_commits.__name__), + # Given the major_on_zero is False and the version is starting at 0.0.0, + # the major level commits are limited to only causing a minor level bump + lazy_fixture(emoji_major_commits.__name__), + ) + ), + # when prerelease is False, & major_on_zero is False, & allow_zero_version is True, + # the version should only be minor bumped when provided major commits because + # of the major_on_zero value ( - repo_w_no_tags_emoji_commits.__name__, + lazy_fixture(emoji_major_commits.__name__), + False, + False, + True, + "0.1.0", None, - ): [ - *( - # when prerelease is False, & major_on_zero is False & - # allow_zero_version is True, the version should be - # 0.1.0, with the given commits - (commits, False, False, True, "0.1.0", None) - for commits in ( - # Even when this test does not change anything, the base modification - # will be a minor change and thus the version will be bumped to 0.1.0 - None, - # Non version bumping commits are absorbed into the previously detected minor bump - lazy_fixture(emoji_chore_commits.__name__), - # Patch commits are absorbed into the previously detected minor bump - lazy_fixture(emoji_patch_commits.__name__), - # Minor level commits are absorbed into the previously detected minor bump - lazy_fixture(emoji_minor_commits.__name__), - # Given the major_on_zero is False and the version is starting at 0.0.0, - # the major level commits are limited to only causing a minor level bump - # lazy_fixture(emoji_major_commits.__name__), # used as default - ) - ), - # when prerelease is False, & major_on_zero is False, & allow_zero_version is True, - # the version should only be minor bumped when provided major commits because - # of the major_on_zero value - ( - lazy_fixture(emoji_major_commits.__name__), - False, - False, - True, - "0.1.0", + ), + # when prerelease is False, & major_on_zero is True & allow_zero_version is True, + # the version should be major bumped when provided major commits because + # of the major_on_zero value + ( + lazy_fixture(emoji_major_commits.__name__), + False, + True, + True, + "1.0.0", + None, + ), + *( + # when prerelease is False, & allow_zero_version is False, the version should be + # 1.0.0, across the board because 0 is not a valid major version. + # major_on_zero is ignored as it is not relevant but tested for completeness + (commits, False, major_on_zero, False, "1.0.0", None) + for major_on_zero in (True, False) + for commits in ( None, - ), - # when prerelease is False, & major_on_zero is True & allow_zero_version is True, - # the version should be major bumped when provided major commits because - # of the major_on_zero value - ( + lazy_fixture(emoji_chore_commits.__name__), + lazy_fixture(emoji_patch_commits.__name__), + lazy_fixture(emoji_minor_commits.__name__), lazy_fixture(emoji_major_commits.__name__), + ) + ), + ], + # Latest version for repo_with_single_branch is currently 0.1.1 + # Note repo_with_single_branch isn't modelled with prereleases + ( + repo_w_trunk_only_emoji_commits.__name__, + None, + ): [ + *( + # when prerelease must be False, and allow_zero_version is True, + # the version is patch bumped because of the patch level commits + # regardless of the major_on_zero value + ( + lazy_fixture(emoji_patch_commits.__name__), False, + major_on_zero, True, - True, - "1.0.0", + "0.1.2", None, - ), - *( - # when prerelease is False, & allow_zero_version is False, the version should be - # 1.0.0, across the board because 0 is not a valid major version. - # major_on_zero is ignored as it is not relevant but tested for completeness - (commits, False, major_on_zero, False, "1.0.0", None) - for major_on_zero in (True, False) - for commits in ( - None, - lazy_fixture(emoji_chore_commits.__name__), - lazy_fixture(emoji_patch_commits.__name__), - lazy_fixture(emoji_minor_commits.__name__), - lazy_fixture(emoji_major_commits.__name__), - ) - ), - ], - # Latest version for repo_with_single_branch is currently 0.1.1 - # Note repo_with_single_branch isn't modelled with prereleases - ( - repo_w_trunk_only_emoji_commits.__name__, - None, - ): [ - *( - # when prerelease must be False, and allow_zero_version is True, - # the version is patch bumped because of the patch level commits - # regardless of the major_on_zero value - ( - lazy_fixture(emoji_patch_commits.__name__), - False, - major_on_zero, - True, - "0.1.2", - None, - ) - for major_on_zero in (True, False) - ), - *( - # when prerelease must be False, and allow_zero_version is True, - # the version is minor bumped because of the major_on_zero value=False - (commits, False, False, True, "0.2.0", None) - for commits in ( - lazy_fixture(emoji_minor_commits.__name__), - lazy_fixture(emoji_major_commits.__name__), - ) - ), + ) + for major_on_zero in (True, False) + ), + *( # when prerelease must be False, and allow_zero_version is True, - # but the major_on_zero is True, then when a major level commit is given, - # the version should be bumped to the next major version - ( + # the version is minor bumped because of the major_on_zero value=False + (commits, False, False, True, "0.2.0", None) + for commits in ( + lazy_fixture(emoji_minor_commits.__name__), lazy_fixture(emoji_major_commits.__name__), - False, - True, - True, - "1.0.0", + ) + ), + # when prerelease must be False, and allow_zero_version is True, + # but the major_on_zero is True, then when a major level commit is given, + # the version should be bumped to the next major version + ( + lazy_fixture(emoji_major_commits.__name__), + False, + True, + True, + "1.0.0", + None, + ), + *( + # when prerelease must be False, & allow_zero_version is False, the version should be + # 1.0.0, with any change regardless of major_on_zero + (commits, False, major_on_zero, False, "1.0.0", None) + for major_on_zero in (True, False) + for commits in ( None, - ), - *( - # when prerelease must be False, & allow_zero_version is False, the version should be - # 1.0.0, with any change regardless of major_on_zero - (commits, False, major_on_zero, False, "1.0.0", None) - for major_on_zero in (True, False) - for commits in ( - None, - lazy_fixture(emoji_chore_commits.__name__), - lazy_fixture(emoji_patch_commits.__name__), - lazy_fixture(emoji_minor_commits.__name__), - lazy_fixture(emoji_major_commits.__name__), - ) - ), - ], - # Latest version for repo_with_single_branch_and_prereleases is - # currently 0.2.0 + lazy_fixture(emoji_chore_commits.__name__), + lazy_fixture(emoji_patch_commits.__name__), + lazy_fixture(emoji_minor_commits.__name__), + lazy_fixture(emoji_major_commits.__name__), + ) + ), + ], + # Latest version for repo_with_single_branch_and_prereleases is + # currently 0.2.0 + ( + repo_w_trunk_only_n_prereleases_emoji_commits.__name__, + None, + ): [ + # when allow_zero_version is True, + # prerelease is False, & major_on_zero is False, the version should be + # patch bumped as a prerelease version, when given patch level commits ( - repo_w_trunk_only_n_prereleases_emoji_commits.__name__, + lazy_fixture(emoji_patch_commits.__name__), + True, + False, + True, + "0.2.1-rc.1", None, - ): [ + ), + # when allow_zero_version is True, + # prerelease is False, & major_on_zero is False, the version should be + # patch bumped, when given patch level commits + ( + lazy_fixture(emoji_patch_commits.__name__), + False, + False, + True, + "0.2.1", + None, + ), + *( # when allow_zero_version is True, - # prerelease is False, & major_on_zero is False, the version should be - # patch bumped as a prerelease version, when given patch level commits - ( - lazy_fixture(emoji_patch_commits.__name__), - True, - False, - True, - "0.2.1-rc.1", + # prerelease is True, & major_on_zero is False, the version should be + # minor bumped as a prerelease version, when given commits of a minor or major level + (commits, True, False, True, "0.3.0-rc.1", None) + for commits in ( + lazy_fixture(emoji_minor_commits.__name__), + lazy_fixture(emoji_major_commits.__name__), + ) + ), + *( + # when allow_zero_version is True, prerelease is True, & major_on_zero + # is False, the version should be minor bumped, when given commits of a + # minor or major level because major_on_zero = False + (commits, False, False, True, "0.3.0", None) + for commits in ( + lazy_fixture(emoji_minor_commits.__name__), + lazy_fixture(emoji_major_commits.__name__), + ) + ), + # when prerelease is True, & major_on_zero is True, and allow_zero_version + # is True, the version should be bumped to 1.0.0 as a prerelease version, when + # given major level commits + ( + lazy_fixture(emoji_major_commits.__name__), + True, + True, + True, + "1.0.0-rc.1", + None, + ), + # when prerelease is False, & major_on_zero is True, and allow_zero_version + # is True, the version should be bumped to 1.0.0, when given major level commits + ( + lazy_fixture(emoji_major_commits.__name__), + False, + True, + True, + "1.0.0", + None, + ), + *( + # when prerelease is True, & allow_zero_version is False, the version should be + # bumped to 1.0.0 as a prerelease version, when given any/none commits + # because 0.x is no longer a valid version regardless of the major_on_zero value + (commits, True, major_on_zero, False, "1.0.0-rc.1", None) + for major_on_zero in (True, False) + for commits in ( None, - ), - # when allow_zero_version is True, - # prerelease is False, & major_on_zero is False, the version should be - # patch bumped, when given patch level commits - ( + lazy_fixture(emoji_chore_commits.__name__), lazy_fixture(emoji_patch_commits.__name__), - False, - False, - True, - "0.2.1", - None, - ), - *( - # when allow_zero_version is True, - # prerelease is True, & major_on_zero is False, the version should be - # minor bumped as a prerelease version, when given commits of a minor or major level - (commits, True, False, True, "0.3.0-rc.1", None) - for commits in ( - lazy_fixture(emoji_minor_commits.__name__), - lazy_fixture(emoji_major_commits.__name__), - ) - ), - *( - # when allow_zero_version is True, prerelease is True, & major_on_zero - # is False, the version should be minor bumped, when given commits of a - # minor or major level because major_on_zero = False - (commits, False, False, True, "0.3.0", None) - for commits in ( - lazy_fixture(emoji_minor_commits.__name__), - lazy_fixture(emoji_major_commits.__name__), - ) - ), - # when prerelease is True, & major_on_zero is True, and allow_zero_version - # is True, the version should be bumped to 1.0.0 as a prerelease version, when - # given major level commits - ( + lazy_fixture(emoji_minor_commits.__name__), lazy_fixture(emoji_major_commits.__name__), - True, - True, - True, - "1.0.0-rc.1", - None, - ), - # when prerelease is False, & major_on_zero is True, and allow_zero_version - # is True, the version should be bumped to 1.0.0, when given major level commits - ( + ) + ), + *( + # when prerelease is True, & allow_zero_version is False, the version should be + # bumped to 1.0.0, when given any/none commits + # because 0.x is no longer a valid version regardless of the major_on_zero value + (commits, False, major_on_zero, False, "1.0.0", None) + for major_on_zero in (True, False) + for commits in ( + lazy_fixture(emoji_patch_commits.__name__), + lazy_fixture(emoji_minor_commits.__name__), lazy_fixture(emoji_major_commits.__name__), - False, - True, - True, - "1.0.0", - None, - ), - *( - # when prerelease is True, & allow_zero_version is False, the version should be - # bumped to 1.0.0 as a prerelease version, when given any/none commits - # because 0.x is no longer a valid version regardless of the major_on_zero value - (commits, True, major_on_zero, False, "1.0.0-rc.1", None) - for major_on_zero in (True, False) - for commits in ( - None, - lazy_fixture(emoji_chore_commits.__name__), - lazy_fixture(emoji_patch_commits.__name__), - lazy_fixture(emoji_minor_commits.__name__), - lazy_fixture(emoji_major_commits.__name__), - ) - ), - *( - # when prerelease is True, & allow_zero_version is False, the version should be - # bumped to 1.0.0, when given any/none commits - # because 0.x is no longer a valid version regardless of the major_on_zero value - (commits, False, major_on_zero, False, "1.0.0", None) - for major_on_zero in (True, False) - for commits in ( - lazy_fixture(emoji_patch_commits.__name__), - lazy_fixture(emoji_minor_commits.__name__), - lazy_fixture(emoji_major_commits.__name__), - ) - ), - ], - }.items() - for ( - commit_messages, - prerelease, - major_on_zero, - allow_zero_version, - next_release_version, - branch_name, - ) in values # type: ignore[attr-defined] - ], + ) + ), + ], + }.items() + for ( + commit_messages, + prerelease, + major_on_zero, + allow_zero_version, + next_release_version, + branch_name, + ) in values # type: ignore[attr-defined] ], ), ) @@ -2338,256 +2306,240 @@ def test_version_next_w_zero_dot_versions_no_bump_emoji( ), xdist_sort_hack( [ - ( + pytest.param( + lazy_fixture(repo_fixture_name), + commit_messages, + prerelease, + "rc" if prerelease_token is None else prerelease_token, + major_on_zero, + allow_zero_version, + next_release_version, + "main" if branch_name is None else branch_name, + marks=pytest.mark.comprehensive, + ) + for (repo_fixture_name, prerelease_token), values in { # Latest version for repo_with_no_tags is currently 0.0.0 (default) # It's biggest change type is minor, so the next version should be 0.1.0 - # Given the major_on_zero is False and the version is starting at 0.0.0, - # the major level commits are limited to only causing a minor level bump - lazy_fixture(repo_w_no_tags_scipy_commits.__name__), - lazy_fixture(scipy_major_commits.__name__), - False, - "rc", - False, - True, - "0.1.0", - "main", - ), - *[ - pytest.param( - lazy_fixture(repo_fixture_name), - commit_messages, - prerelease, - "rc" if prerelease_token is None else prerelease_token, - major_on_zero, - allow_zero_version, - next_release_version, - "main" if branch_name is None else branch_name, - marks=pytest.mark.comprehensive, - ) - for (repo_fixture_name, prerelease_token), values in { - # Latest version for repo_with_no_tags is currently 0.0.0 (default) - # It's biggest change type is minor, so the next version should be 0.1.0 + ( + repo_w_no_tags_scipy_commits.__name__, + None, + ): [ + *( + # when prerelease is False, & major_on_zero is False & + # allow_zero_version is True, the version should be + # 0.1.0, with the given commits + (commits, False, False, True, "0.1.0", None) + for commits in ( + # Even when this test does not change anything, the base modification + # will be a minor change and thus the version will be bumped to 0.1.0 + None, + # Non version bumping commits are absorbed into the previously detected minor bump + lazy_fixture(scipy_chore_commits.__name__), + # Patch commits are absorbed into the previously detected minor bump + lazy_fixture(scipy_patch_commits.__name__), + # Minor level commits are absorbed into the previously detected minor bump + lazy_fixture(scipy_minor_commits.__name__), + # Given the major_on_zero is False and the version is starting at 0.0.0, + # the major level commits are limited to only causing a minor level bump + lazy_fixture(scipy_major_commits.__name__), + ) + ), + # when prerelease is False, & major_on_zero is False, & allow_zero_version is True, + # the version should only be minor bumped when provided major commits because + # of the major_on_zero value ( - repo_w_no_tags_scipy_commits.__name__, + lazy_fixture(scipy_major_commits.__name__), + False, + False, + True, + "0.1.0", None, - ): [ - *( - # when prerelease is False, & major_on_zero is False & - # allow_zero_version is True, the version should be - # 0.1.0, with the given commits - (commits, False, False, True, "0.1.0", None) - for commits in ( - # Even when this test does not change anything, the base modification - # will be a minor change and thus the version will be bumped to 0.1.0 - None, - # Non version bumping commits are absorbed into the previously detected minor bump - lazy_fixture(scipy_chore_commits.__name__), - # Patch commits are absorbed into the previously detected minor bump - lazy_fixture(scipy_patch_commits.__name__), - # Minor level commits are absorbed into the previously detected minor bump - lazy_fixture(scipy_minor_commits.__name__), - # Given the major_on_zero is False and the version is starting at 0.0.0, - # the major level commits are limited to only causing a minor level bump - # lazy_fixture(scipy_major_commits.__name__), # used as default - ) - ), - # when prerelease is False, & major_on_zero is False, & allow_zero_version is True, - # the version should only be minor bumped when provided major commits because - # of the major_on_zero value - ( - lazy_fixture(scipy_major_commits.__name__), - False, - False, - True, - "0.1.0", + ), + # when prerelease is False, & major_on_zero is True & allow_zero_version is True, + # the version should be major bumped when provided major commits because + # of the major_on_zero value + ( + lazy_fixture(scipy_major_commits.__name__), + False, + True, + True, + "1.0.0", + None, + ), + *( + # when prerelease is False, & allow_zero_version is False, the version should be + # 1.0.0, across the board because 0 is not a valid major version. + # major_on_zero is ignored as it is not relevant but tested for completeness + (commits, False, major_on_zero, False, "1.0.0", None) + for major_on_zero in (True, False) + for commits in ( None, - ), - # when prerelease is False, & major_on_zero is True & allow_zero_version is True, - # the version should be major bumped when provided major commits because - # of the major_on_zero value - ( + lazy_fixture(scipy_chore_commits.__name__), + lazy_fixture(scipy_patch_commits.__name__), + lazy_fixture(scipy_minor_commits.__name__), lazy_fixture(scipy_major_commits.__name__), + ) + ), + ], + # Latest version for repo_with_single_branch is currently 0.1.1 + # Note repo_with_single_branch isn't modelled with prereleases + ( + repo_w_trunk_only_scipy_commits.__name__, + None, + ): [ + *( + # when prerelease must be False, and allow_zero_version is True, + # the version is patch bumped because of the patch level commits + # regardless of the major_on_zero value + ( + lazy_fixture(scipy_patch_commits.__name__), False, + major_on_zero, True, - True, - "1.0.0", + "0.1.2", None, - ), - *( - # when prerelease is False, & allow_zero_version is False, the version should be - # 1.0.0, across the board because 0 is not a valid major version. - # major_on_zero is ignored as it is not relevant but tested for completeness - (commits, False, major_on_zero, False, "1.0.0", None) - for major_on_zero in (True, False) - for commits in ( - None, - lazy_fixture(scipy_chore_commits.__name__), - lazy_fixture(scipy_patch_commits.__name__), - lazy_fixture(scipy_minor_commits.__name__), - lazy_fixture(scipy_major_commits.__name__), - ) - ), - ], - # Latest version for repo_with_single_branch is currently 0.1.1 - # Note repo_with_single_branch isn't modelled with prereleases - ( - repo_w_trunk_only_scipy_commits.__name__, - None, - ): [ - *( - # when prerelease must be False, and allow_zero_version is True, - # the version is patch bumped because of the patch level commits - # regardless of the major_on_zero value - ( - lazy_fixture(scipy_patch_commits.__name__), - False, - major_on_zero, - True, - "0.1.2", - None, - ) - for major_on_zero in (True, False) - ), - *( - # when prerelease must be False, and allow_zero_version is True, - # the version is minor bumped because of the major_on_zero value=False - (commits, False, False, True, "0.2.0", None) - for commits in ( - lazy_fixture(scipy_minor_commits.__name__), - lazy_fixture(scipy_major_commits.__name__), - ) - ), + ) + for major_on_zero in (True, False) + ), + *( # when prerelease must be False, and allow_zero_version is True, - # but the major_on_zero is True, then when a major level commit is given, - # the version should be bumped to the next major version - ( + # the version is minor bumped because of the major_on_zero value=False + (commits, False, False, True, "0.2.0", None) + for commits in ( + lazy_fixture(scipy_minor_commits.__name__), lazy_fixture(scipy_major_commits.__name__), - False, - True, - True, - "1.0.0", + ) + ), + # when prerelease must be False, and allow_zero_version is True, + # but the major_on_zero is True, then when a major level commit is given, + # the version should be bumped to the next major version + ( + lazy_fixture(scipy_major_commits.__name__), + False, + True, + True, + "1.0.0", + None, + ), + *( + # when prerelease must be False, & allow_zero_version is False, the version should be + # 1.0.0, with any change regardless of major_on_zero + (commits, False, major_on_zero, False, "1.0.0", None) + for major_on_zero in (True, False) + for commits in ( None, - ), - *( - # when prerelease must be False, & allow_zero_version is False, the version should be - # 1.0.0, with any change regardless of major_on_zero - (commits, False, major_on_zero, False, "1.0.0", None) - for major_on_zero in (True, False) - for commits in ( - None, - lazy_fixture(scipy_chore_commits.__name__), - lazy_fixture(scipy_patch_commits.__name__), - lazy_fixture(scipy_minor_commits.__name__), - lazy_fixture(scipy_major_commits.__name__), - ) - ), - ], - # Latest version for repo_with_single_branch_and_prereleases is - # currently 0.2.0 + lazy_fixture(scipy_chore_commits.__name__), + lazy_fixture(scipy_patch_commits.__name__), + lazy_fixture(scipy_minor_commits.__name__), + lazy_fixture(scipy_major_commits.__name__), + ) + ), + ], + # Latest version for repo_with_single_branch_and_prereleases is + # currently 0.2.0 + ( + repo_w_trunk_only_n_prereleases_scipy_commits.__name__, + None, + ): [ + # when allow_zero_version is True, + # prerelease is False, & major_on_zero is False, the version should be + # patch bumped as a prerelease version, when given patch level commits ( - repo_w_trunk_only_n_prereleases_scipy_commits.__name__, + lazy_fixture(scipy_patch_commits.__name__), + True, + False, + True, + "0.2.1-rc.1", None, - ): [ + ), + # when allow_zero_version is True, + # prerelease is False, & major_on_zero is False, the version should be + # patch bumped, when given patch level commits + ( + lazy_fixture(scipy_patch_commits.__name__), + False, + False, + True, + "0.2.1", + None, + ), + *( # when allow_zero_version is True, - # prerelease is False, & major_on_zero is False, the version should be - # patch bumped as a prerelease version, when given patch level commits - ( - lazy_fixture(scipy_patch_commits.__name__), - True, - False, - True, - "0.2.1-rc.1", + # prerelease is True, & major_on_zero is False, the version should be + # minor bumped as a prerelease version, when given commits of a minor or major level + (commits, True, False, True, "0.3.0-rc.1", None) + for commits in ( + lazy_fixture(scipy_minor_commits.__name__), + lazy_fixture(scipy_major_commits.__name__), + ) + ), + *( + # when allow_zero_version is True, prerelease is True, & major_on_zero + # is False, the version should be minor bumped, when given commits of a + # minor or major level because major_on_zero = False + (commits, False, False, True, "0.3.0", None) + for commits in ( + lazy_fixture(scipy_minor_commits.__name__), + lazy_fixture(scipy_major_commits.__name__), + ) + ), + # when prerelease is True, & major_on_zero is True, and allow_zero_version + # is True, the version should be bumped to 1.0.0 as a prerelease version, when + # given major level commits + ( + lazy_fixture(scipy_major_commits.__name__), + True, + True, + True, + "1.0.0-rc.1", + None, + ), + # when prerelease is False, & major_on_zero is True, and allow_zero_version + # is True, the version should be bumped to 1.0.0, when given major level commits + ( + lazy_fixture(scipy_major_commits.__name__), + False, + True, + True, + "1.0.0", + None, + ), + *( + # when prerelease is True, & allow_zero_version is False, the version should be + # bumped to 1.0.0 as a prerelease version, when given any/none commits + # because 0.x is no longer a valid version regardless of the major_on_zero value + (commits, True, major_on_zero, False, "1.0.0-rc.1", None) + for major_on_zero in (True, False) + for commits in ( None, - ), - # when allow_zero_version is True, - # prerelease is False, & major_on_zero is False, the version should be - # patch bumped, when given patch level commits - ( + lazy_fixture(scipy_chore_commits.__name__), lazy_fixture(scipy_patch_commits.__name__), - False, - False, - True, - "0.2.1", - None, - ), - *( - # when allow_zero_version is True, - # prerelease is True, & major_on_zero is False, the version should be - # minor bumped as a prerelease version, when given commits of a minor or major level - (commits, True, False, True, "0.3.0-rc.1", None) - for commits in ( - lazy_fixture(scipy_minor_commits.__name__), - lazy_fixture(scipy_major_commits.__name__), - ) - ), - *( - # when allow_zero_version is True, prerelease is True, & major_on_zero - # is False, the version should be minor bumped, when given commits of a - # minor or major level because major_on_zero = False - (commits, False, False, True, "0.3.0", None) - for commits in ( - lazy_fixture(scipy_minor_commits.__name__), - lazy_fixture(scipy_major_commits.__name__), - ) - ), - # when prerelease is True, & major_on_zero is True, and allow_zero_version - # is True, the version should be bumped to 1.0.0 as a prerelease version, when - # given major level commits - ( + lazy_fixture(scipy_minor_commits.__name__), lazy_fixture(scipy_major_commits.__name__), - True, - True, - True, - "1.0.0-rc.1", - None, - ), - # when prerelease is False, & major_on_zero is True, and allow_zero_version - # is True, the version should be bumped to 1.0.0, when given major level commits - ( + ) + ), + *( + # when prerelease is True, & allow_zero_version is False, the version should be + # bumped to 1.0.0, when given any/none commits + # because 0.x is no longer a valid version regardless of the major_on_zero value + (commits, False, major_on_zero, False, "1.0.0", None) + for major_on_zero in (True, False) + for commits in ( + lazy_fixture(scipy_patch_commits.__name__), + lazy_fixture(scipy_minor_commits.__name__), lazy_fixture(scipy_major_commits.__name__), - False, - True, - True, - "1.0.0", - None, - ), - *( - # when prerelease is True, & allow_zero_version is False, the version should be - # bumped to 1.0.0 as a prerelease version, when given any/none commits - # because 0.x is no longer a valid version regardless of the major_on_zero value - (commits, True, major_on_zero, False, "1.0.0-rc.1", None) - for major_on_zero in (True, False) - for commits in ( - None, - lazy_fixture(scipy_chore_commits.__name__), - lazy_fixture(scipy_patch_commits.__name__), - lazy_fixture(scipy_minor_commits.__name__), - lazy_fixture(scipy_major_commits.__name__), - ) - ), - *( - # when prerelease is True, & allow_zero_version is False, the version should be - # bumped to 1.0.0, when given any/none commits - # because 0.x is no longer a valid version regardless of the major_on_zero value - (commits, False, major_on_zero, False, "1.0.0", None) - for major_on_zero in (True, False) - for commits in ( - lazy_fixture(scipy_patch_commits.__name__), - lazy_fixture(scipy_minor_commits.__name__), - lazy_fixture(scipy_major_commits.__name__), - ) - ), - ], - }.items() - for ( - commit_messages, - prerelease, - major_on_zero, - allow_zero_version, - next_release_version, - branch_name, - ) in values # type: ignore[attr-defined] - ], + ) + ), + ], + }.items() + for ( + commit_messages, + prerelease, + major_on_zero, + allow_zero_version, + next_release_version, + branch_name, + ) in values # type: ignore[attr-defined] ], ), ) From 0363ea30bb9fcfc8b5747fea5a8ba1502bd1c4c6 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 16 Feb 2025 16:42:52 -0700 Subject: [PATCH 051/160] test(cmd-version): fix release notes test implementation to avoid date change error --- tests/e2e/cmd_version/test_version_release_notes.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/e2e/cmd_version/test_version_release_notes.py b/tests/e2e/cmd_version/test_version_release_notes.py index c185b76f2..ccd82dc77 100644 --- a/tests/e2e/cmd_version/test_version_release_notes.py +++ b/tests/e2e/cmd_version/test_version_release_notes.py @@ -1,9 +1,11 @@ from __future__ import annotations import os +from datetime import timezone from typing import TYPE_CHECKING import pytest +from freezegun import freeze_time from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture from semantic_release.cli.commands.main import main @@ -134,13 +136,14 @@ def test_default_release_notes_license_statement( new_version = "0.1.0" # Setup + now_datetime = stable_now_date() repo_def = list(repo_result["definition"]) repo_def.append( { "action": RepoActionStep.RELEASE, "details": { "version": new_version, - "datetime": stable_now_date().isoformat(timespec="seconds"), + "datetime": now_datetime.isoformat(timespec="seconds"), }, } ) @@ -159,8 +162,9 @@ def test_default_release_notes_license_statement( ) # Act - cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--no-changelog", "--vcs-release"] - result = cli_runner.invoke(main, cli_cmd[1:]) + with freeze_time(now_datetime.astimezone(timezone.utc)): + cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--no-changelog", "--vcs-release"] + result = cli_runner.invoke(main, cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) From 84b203f75d30f3047705bc669dbeae90f54e2cef Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 16 Feb 2025 17:27:12 -0700 Subject: [PATCH 052/160] test(main): use easiest & common repo for non-comprehensive tests --- tests/e2e/test_main.py | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/tests/e2e/test_main.py b/tests/e2e/test_main.py index 361061021..42c04b118 100644 --- a/tests/e2e/test_main.py +++ b/tests/e2e/test_main.py @@ -13,10 +13,7 @@ from semantic_release.cli.commands.main import main from tests.const import MAIN_PROG_NAME, VERSION_SUBCMD -from tests.fixtures import ( - repo_w_git_flow_w_alpha_prereleases_n_conventional_commits, - repo_w_no_tags_conventional_commits, -) +from tests.fixtures.repos import repo_w_no_tags_conventional_commits from tests.util import assert_exit_code, assert_successful_exit_code if TYPE_CHECKING: @@ -46,7 +43,7 @@ def test_main_no_args_prints_help_text(cli_runner: CliRunner): @pytest.mark.parametrize( "repo_result", - [lazy_fixture(repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__)], + [lazy_fixture(repo_w_no_tags_conventional_commits.__name__)], ) def test_not_a_release_branch_exit_code( repo_result: BuiltRepoResult, cli_runner: CliRunner @@ -64,7 +61,7 @@ def test_not_a_release_branch_exit_code( @pytest.mark.parametrize( "repo_result", - [lazy_fixture(repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__)], + [lazy_fixture(repo_w_no_tags_conventional_commits.__name__)], ) def test_not_a_release_branch_exit_code_with_strict( repo_result: BuiltRepoResult, @@ -83,7 +80,7 @@ def test_not_a_release_branch_exit_code_with_strict( @pytest.mark.parametrize( "repo_result", - [lazy_fixture(repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__)], + [lazy_fixture(repo_w_no_tags_conventional_commits.__name__)], ) def test_not_a_release_branch_detached_head_exit_code( repo_result: BuiltRepoResult, @@ -129,9 +126,7 @@ def json_file_with_no_configuration_for_psr(tmp_path: Path) -> Path: return path -@pytest.mark.usefixtures( - repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__ -) +@pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_default_config_is_used_when_none_in_toml_config_file( cli_runner: CliRunner, toml_file_with_no_configuration_for_psr: Path, @@ -151,9 +146,7 @@ def test_default_config_is_used_when_none_in_toml_config_file( assert_successful_exit_code(result, cli_cmd) -@pytest.mark.usefixtures( - repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__ -) +@pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_default_config_is_used_when_none_in_json_config_file( cli_runner: CliRunner, json_file_with_no_configuration_for_psr: Path, @@ -173,9 +166,7 @@ def test_default_config_is_used_when_none_in_json_config_file( assert_successful_exit_code(result, cli_cmd) -@pytest.mark.usefixtures( - repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__ -) +@pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_errors_when_config_file_does_not_exist_and_passed_explicitly( cli_runner: CliRunner, ): From 8906d8e70467af1489d797ec8cb09b1f95e5d409 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 17 Feb 2025 00:20:49 -0700 Subject: [PATCH 053/160] feat(cmd-version): enable stamping of tag formatted versions into files (#1190) Resolves: #846 * build(deps): add `deprecated~=1.2` for deprecation notices & sphinx documentation * refactor(noop): simplify text output during `--noop` execution * chore(mypy): set mypy configuration to ignore `dotty_dict` missing types * test(declarations): update unit tests for full file modification w/ tag formatted versions * test(cmd-version): add version stamp test to validate tag formatted version stamping * docs(configuration): add usage information for tag format version stamping --- docs/configuration.rst | 132 ++++- pyproject.toml | 6 + src/semantic_release/cli/commands/version.py | 45 +- src/semantic_release/cli/config.py | 79 ++- src/semantic_release/cli/util.py | 2 +- src/semantic_release/version/declaration.py | 163 ++----- .../version/declarations/__init__.py | 0 .../version/declarations/enum.py | 12 + .../declarations/i_version_replacer.py | 67 +++ .../version/declarations/pattern.py | 241 ++++++++++ .../version/declarations/toml.py | 148 ++++++ tests/e2e/cmd_version/test_version_stamp.py | 130 ++++- .../version/declarations/__init__.py | 0 .../declarations/test_pattern_declaration.py | 454 ++++++++++++++++++ .../declarations/test_toml_declaration.py | 350 ++++++++++++++ .../version/test_declaration.py | 138 ------ 16 files changed, 1627 insertions(+), 340 deletions(-) create mode 100644 src/semantic_release/version/declarations/__init__.py create mode 100644 src/semantic_release/version/declarations/enum.py create mode 100644 src/semantic_release/version/declarations/i_version_replacer.py create mode 100644 src/semantic_release/version/declarations/pattern.py create mode 100644 src/semantic_release/version/declarations/toml.py create mode 100644 tests/unit/semantic_release/version/declarations/__init__.py create mode 100644 tests/unit/semantic_release/version/declarations/test_pattern_declaration.py create mode 100644 tests/unit/semantic_release/version/declarations/test_toml_declaration.py delete mode 100644 tests/unit/semantic_release/version/test_declaration.py diff --git a/docs/configuration.rst b/docs/configuration.rst index a777c8f21..7fc4838c4 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -1201,17 +1201,61 @@ Tags which do not match this format will not be considered as versions of your p **Type:** ``list[str]`` -Similar to :ref:`config-version_variables`, but allows the version number to be -identified safely in a toml file like ``pyproject.toml``, with each entry using -dotted notation to indicate the key for which the value represents the version: +This configuration option is similar to :ref:`config-version_variables`, but it uses +a TOML parser to interpret the data structure before, inserting the version. This +allows users to use dot-notation to specify the version via the logical structure +within the TOML file, which is more accurate than a pattern replace. + +The ``version_toml`` option is commonly used to update the version number in the project +definition file: ``pyproject.toml`` as seen in the example below. + +As of ${NEW_RELEASE_TAG}, the ``version_toml`` option accepts a colon-separated definition +with either 2 or 3 parts. The 2-part definition includes the file path and the version +parameter (in dot-notation). Newly with ${NEW_RELEASE_TAG}, it also accepts an optional +3rd part to allow configuration of the format type. + +**Available Format Types** + +- ``nf``: Number format (ex. ``1.2.3``) +- ``tf``: :ref:`Tag Format ` (ex. ``v1.2.3``) + +If the format type is not specified, it will default to the number format. + +**Example** .. code-block:: toml [semantic_release] version_toml = [ - "pyproject.toml:tool.poetry.version", + # "file:variable:[format_type]" + "pyproject.toml:tool.poetry.version", # Implied Default: Number format + "definition.toml:project.version:nf", # Number format + "definition.toml:project.release:tf", # Tag format ] +This configuration will result in the following changes: + +.. code-block:: diff + + diff a/pyproject.toml b/pyproject.toml + + [tool.poetry] + - version = "0.1.0" + + version = "0.2.0" + +.. code-block:: diff + + diff a/definition.toml b/definition.toml + + [project] + name = "example" + + - version = "0.1.0" + + version = "0.1.0" + + - release = "v0.1.0" + + release = "v0.2.0" + **Default:** ``[]`` ---- @@ -1223,17 +1267,74 @@ dotted notation to indicate the key for which the value represents the version: **Type:** ``list[str]`` -Each entry represents a location where the version is stored in the source code, -specified in ``file:variable`` format. For example: +The ``version_variables`` configuration option is a list of string definitions +that defines where the version number should be updated in the repository, when +a new version is released. + +As of ${NEW_RELEASE_TAG}, the ``version_variables`` option accepts a +colon-separated definition with either 2 or 3 parts. The 2-part definition includes +the file path and the variable name. Newly with ${NEW_RELEASE_TAG}, it also accepts +an optional 3rd part to allow configuration of the format type. + +**Available Format Types** + +- ``nf``: Number format (ex. ``1.2.3``) +- ``tf``: :ref:`Tag Format ` (ex. ``v1.2.3``) + +If the format type is not specified, it will default to the number format. + +Prior to ${NEW_RELEASE_TAG}, PSR only supports entries with the first 2-parts +as the tag format type was not available and would only replace numeric +version numbers. + +**Example** .. code-block:: toml [semantic_release] + tag_format = "v{version}" version_variables = [ - "src/semantic_release/__init__.py:__version__", - "docs/conf.py:version", + # "file:variable:format_type" + "src/semantic_release/__init__.py:__version__", # Implied Default: Number format + "docs/conf.py:version:nf", # Number format for sphinx docs + "kustomization.yml:newTag:tf", # Tag format ] +First, the ``__version__`` variable in ``src/semantic_release/__init__.py`` will be updated +with the next version using the `SemVer`_ number format. + +.. code-block:: diff + + diff a/src/semantic_release/__init__.py b/src/semantic_release/__init__.py + + - __version__ = "0.1.0" + + __version__ = "0.2.0" + +Then, the ``version`` variable in ``docs/conf.py`` will be updated with the next version +with the next version using the `SemVer`_ number format because of the explicit ``nf``. + +.. code-block:: diff + + diff a/docs/conf.py b/docs/conf.py + + - version = "0.1.0" + + version = "0.2.0" + +Lastly, the ``newTag`` variable in ``kustomization.yml`` will be updated with the next version +with the next version using the configured :ref:`config-tag_format` because the definition +included ``tf``. + +.. code-block:: diff + + diff a/kustomization.yml b/kustomization.yml + + images: + - name: repo/image + - newTag: v0.1.0 + + newTag: v0.2.0 + +**How It works** + Each version variable will be transformed into a Regular Expression that will be used to substitute the version number in the file. The replacement algorithm is **ONLY** a pattern match and replace. It will **NOT** evaluate the code nor will PSR understand @@ -1242,16 +1343,17 @@ any internal object structures (ie. ``file:object.version`` will not work). .. important:: The Regular Expression expects a version value to exist in the file to be replaced. It cannot be an empty string or a non-semver compliant string. If this is the very - first time you are using PSR, we recommend you set the version to ``0.0.0``. This - may become more flexible in the future with resolution of issue `#941`_. + first time you are using PSR, we recommend you set the version to ``0.0.0``. + + This may become more flexible in the future with resolution of issue `#941`_. .. _#941: https://github.com/python-semantic-release/python-semantic-release/issues/941 Given the pattern matching nature of this feature, the Regular Expression is able to -support most file formats as a variable declaration in most languages is very similar. -We specifically support Python, YAML, and JSON as these have been the most common -requests. This configuration option will also work regardless of file extension -because its only a pattern match. +support most file formats because of the similarity of variable declaration across +programming languages. PSR specifically supports Python, YAML, and JSON as these have +been the most commonly requested formats. This configuration option will also work +regardless of file extension because it looks for a matching pattern string. .. note:: This will also work for TOML but we recommend using :ref:`config-version_toml` for @@ -1264,3 +1366,5 @@ because its only a pattern match. both. This is a limitation of the pattern matching and not a bug. **Default:** ``[]`` + +.. _SemVer: https://semver.org/ diff --git a/pyproject.toml b/pyproject.toml index f0d1eb911..b11f2d7f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,6 +35,7 @@ dependencies = [ "pydantic ~= 2.0", "rich ~= 13.0", "shellingham ~= 1.5", + "Deprecated ~= 1.2", # Backport of deprecated decorator for python 3.8 ] [project.scripts] @@ -83,6 +84,7 @@ dev = [ ] mypy = [ "mypy == 1.15.0", + "types-Deprecated ~= 1.2", "types-requests ~= 2.32.0", "types-pyyaml ~= 6.0", ] @@ -201,6 +203,10 @@ ignore_missing_imports = true module = "shellingham" ignore_missing_imports = true +[[tool.mypy.overrides]] +module = "dotty_dict" +ignore_missing_imports = true + [tool.ruff] line-length = 88 target-version = "py38" diff --git a/src/semantic_release/cli/commands/version.py b/src/semantic_release/cli/commands/version.py index 104faa9fe..86d209937 100644 --- a/src/semantic_release/cli/commands/version.py +++ b/src/semantic_release/cli/commands/version.py @@ -39,12 +39,12 @@ if TYPE_CHECKING: # pragma: no cover from pathlib import Path - from typing import Iterable, Mapping + from typing import Mapping, Sequence from git.refs.tag import Tag from semantic_release.cli.cli_context import CliContextObj - from semantic_release.version.declaration import VersionDeclarationABC + from semantic_release.version.declaration import IVersionReplacer from semantic_release.version.version import Version @@ -135,28 +135,43 @@ def version_from_forced_level( def apply_version_to_source_files( repo_dir: Path, - version_declarations: Iterable[VersionDeclarationABC], + version_declarations: Sequence[IVersionReplacer], version: Version, noop: bool = False, ) -> list[str]: - paths = [ - str(declaration.path.resolve().relative_to(repo_dir)) - for declaration in version_declarations + if len(version_declarations) < 1: + return [] + + if not noop: + log.debug("Updating version %s in repository files...", version) + + paths = list( + map( + lambda decl, new_version=version, noop=noop: ( # type: ignore[misc] + decl.update_file_w_version(new_version=new_version, noop=noop) + ), + version_declarations, + ) + ) + + repo_filepaths = [ + str(updated_file.relative_to(repo_dir)) + for updated_file in paths + if updated_file is not None ] if noop: noop_report( - "would have updated versions in the following paths:" - + "".join(f"\n {path}" for path in paths) + str.join( + "", + [ + "would have updated versions in the following paths:", + *[f"\n {filepath}" for filepath in repo_filepaths], + ], + ) ) - return paths - - log.debug("writing version %s to source paths %s", version, paths) - for declaration in version_declarations: - new_content = declaration.replace(new_version=version) - declaration.path.write_text(new_content) - return paths + return repo_filepaths def shell( diff --git a/src/semantic_release/cli/config.py b/src/semantic_release/cli/config.py index 721028281..41ac02058 100644 --- a/src/semantic_release/cli/config.py +++ b/src/semantic_release/cli/config.py @@ -46,7 +46,7 @@ ScipyCommitParser, TagCommitParser, ) -from semantic_release.const import COMMIT_MESSAGE, DEFAULT_COMMIT_AUTHOR, SEMVER_REGEX +from semantic_release.const import COMMIT_MESSAGE, DEFAULT_COMMIT_AUTHOR from semantic_release.errors import ( DetachedHeadGitError, InvalidConfiguration, @@ -55,11 +55,9 @@ ParserLoadError, ) from semantic_release.helpers import dynamic_import -from semantic_release.version.declaration import ( - PatternVersionDeclaration, - TomlVersionDeclaration, - VersionDeclarationABC, -) +from semantic_release.version.declarations.i_version_replacer import IVersionReplacer +from semantic_release.version.declarations.pattern import PatternVersionDeclaration +from semantic_release.version.declarations.toml import TomlVersionDeclaration from semantic_release.version.translator import VersionTranslator log = logging.getLogger(__name__) @@ -555,7 +553,7 @@ class RuntimeContext: commit_author: Actor commit_message: str changelog_excluded_commit_patterns: Tuple[Pattern[str], ...] - version_declarations: Tuple[VersionDeclarationABC, ...] + version_declarations: Tuple[IVersionReplacer, ...] hvcs_client: hvcs.HvcsBase changelog_insertion_flag: str changelog_mask_initial_release: bool @@ -738,44 +736,41 @@ def from_raw_config( # noqa: C901 commit_author = Actor(*_commit_author_valid.groups()) - version_declarations: list[VersionDeclarationABC] = [] - for decl in () if raw.version_toml is None else raw.version_toml: - try: - path, search_text = decl.split(":", maxsplit=1) - # VersionDeclarationABC handles path existence check - vd = TomlVersionDeclaration(path, search_text) - except ValueError as exc: - log.exception("Invalid TOML declaration %r", decl) - raise InvalidConfiguration( - f"Invalid TOML declaration {decl!r}" - ) from exc - - version_declarations.append(vd) - - for decl in () if raw.version_variables is None else raw.version_variables: - try: - path, variable = decl.split(":", maxsplit=1) - # VersionDeclarationABC handles path existence check - search_text = str.join( - "", + version_declarations: list[IVersionReplacer] = [] + + try: + version_declarations.extend( + TomlVersionDeclaration.from_string_definition(definition) + for definition in iter(raw.version_toml or ()) + ) + except ValueError as err: + raise InvalidConfiguration( + str.join( + "\n", [ - # Supports optional matching quotations around variable name - # Negative lookbehind to ensure we don't match part of a variable name - f"""(?x)(?P['"])?(?['"])?(?P{SEMVER_REGEX.pattern})(?P=quote2)?""", + "Invalid 'version_toml' configuration", + str(err), ], ) - pd = PatternVersionDeclaration(path, search_text) - except ValueError as exc: - log.exception("Invalid variable declaration %r", decl) - raise InvalidConfiguration( - f"Invalid variable declaration {decl!r}" - ) from exc - - version_declarations.append(pd) + ) from err + + try: + version_declarations.extend( + PatternVersionDeclaration.from_string_definition( + definition, raw.tag_format + ) + for definition in iter(raw.version_variables or ()) + ) + except ValueError as err: + raise InvalidConfiguration( + str.join( + "\n", + [ + "Invalid 'version_variables' configuration", + str(err), + ], + ) + ) from err # Provide warnings if the token is missing if not raw.remote.token: diff --git a/src/semantic_release/cli/util.py b/src/semantic_release/cli/util.py index c1c01b79c..97d264b02 100644 --- a/src/semantic_release/cli/util.py +++ b/src/semantic_release/cli/util.py @@ -28,7 +28,7 @@ def noop_report(msg: str) -> None: Rich-prints a msg with a standard prefix to report when an action is not being taken due to a "noop" flag """ - fullmsg = "[bold cyan]:shield: semantic-release 'noop' mode is enabled! " + msg + fullmsg = "[bold cyan][:shield: NOP] " + msg rprint(fullmsg) diff --git a/src/semantic_release/version/declaration.py b/src/semantic_release/version/declaration.py index 89f310d8e..92da001a1 100644 --- a/src/semantic_release/version/declaration.py +++ b/src/semantic_release/version/declaration.py @@ -1,19 +1,45 @@ from __future__ import annotations -import logging -import re +# TODO: Remove v10 from abc import ABC, abstractmethod +from logging import getLogger from pathlib import Path -from typing import Any, Dict, cast - -import tomlkit -from dotty_dict import Dotty # type: ignore[import] - -from semantic_release.version.version import Version - -log = logging.getLogger(__name__) - - +from typing import TYPE_CHECKING + +from deprecated.sphinx import deprecated + +from semantic_release.version.declarations.enum import VersionStampType +from semantic_release.version.declarations.i_version_replacer import IVersionReplacer +from semantic_release.version.declarations.pattern import PatternVersionDeclaration +from semantic_release.version.declarations.toml import TomlVersionDeclaration + +if TYPE_CHECKING: # pragma: no cover + from typing import Any + + from semantic_release.version.version import Version + + +# Globals +__all__ = [ + "IVersionReplacer", + "VersionStampType", + "PatternVersionDeclaration", + "TomlVersionDeclaration", + "VersionDeclarationABC", +] +log = getLogger(__name__) + + +@deprecated( + version="9.20.0", + reason=str.join( + " ", + [ + "Refactored to composition paradigm using the new IVersionReplacer interface.", + "This class will be removed in a future release", + ], + ), +) class VersionDeclarationABC(ABC): """ ABC for classes representing a location in which a version is declared somewhere @@ -86,116 +112,3 @@ def write(self, content: str) -> None: log.debug("writing content to %r", self.path.resolve()) self.path.write_text(content) self._content = None - - -class TomlVersionDeclaration(VersionDeclarationABC): - """VersionDeclarationABC implementation which manages toml-format source files.""" - - def _load(self) -> Dotty: - """Load the content of the source file into a Dotty for easier searching""" - loaded = tomlkit.loads(self.content) - return Dotty(loaded) - - def parse(self) -> set[Version]: - """Look for the version in the source content""" - content = self._load() - maybe_version: str = content.get(self.search_text) # type: ignore[return-value] - if maybe_version is not None: - log.debug( - "Found a key %r that looks like a version (%r)", - self.search_text, - maybe_version, - ) - valid_version = Version.parse(maybe_version) - return {valid_version} if valid_version else set() - # Maybe in future raise error if not found? - return set() - - def replace(self, new_version: Version) -> str: - """ - Replace the version in the source content with `new_version`, and return the - updated content. - """ - content = self._load() - if self.search_text in content: - log.info( - "found %r in source file contents, replacing with %s", - self.search_text, - new_version, - ) - content[self.search_text] = str(new_version) - - return tomlkit.dumps(cast(Dict[str, Any], content)) - - -class PatternVersionDeclaration(VersionDeclarationABC): - """ - VersionDeclarationABC implementation representing a version number in a particular - file. The version number is identified by a regular expression, which should be - provided in `search_text`. - """ - - _VERSION_GROUP_NAME = "version" - - def __init__(self, path: Path | str, search_text: str) -> None: - super().__init__(path, search_text) - self.search_re = re.compile(self.search_text, flags=re.MULTILINE) - if self._VERSION_GROUP_NAME not in self.search_re.groupindex: - raise ValueError( - f"Invalid search text {self.search_text!r}; must use 'version' as a " - "named group, for example (?P...) . For more info on named " - "groups see https://docs.python.org/3/library/re.html" - ) - - # The pattern should be a regular expression with a single group, - # containing the version to replace. - def parse(self) -> set[Version]: - """ - Return the versions matching this pattern. - Because a pattern can match in multiple places, this method returns a - set of matches. Generally, there should only be one element in this - set (i.e. even if the version is specified in multiple places, it - should be the same version in each place), but it falls on the caller - to check for this condition. - """ - versions = { - Version.parse(m.group(self._VERSION_GROUP_NAME)) - for m in self.search_re.finditer(self.content, re.MULTILINE) - } - - log.debug( - "Parsing current version: path=%r pattern=%r num_matches=%s", - self.path.resolve(), - self.search_text, - len(versions), - ) - return versions - - def replace(self, new_version: Version) -> str: - """ - Update the versions. - This method reads the underlying file, replaces each occurrence of the - matched pattern, then writes the updated file. - :param new_version: The new version number as a `Version` instance - """ - n = 0 - - def swap_version(m: re.Match[str]) -> str: - nonlocal n - n += 1 - s = m.string - i, j = m.span() - log.debug("match spans characters %s:%s", i, j) - ii, jj = m.span(self._VERSION_GROUP_NAME) - log.debug("version group spans characters %s:%s", ii, jj) - return s[i:ii] + str(new_version) + s[jj:j] - - new_content, n_matches = self.search_re.subn( - swap_version, self.content, re.MULTILINE - ) - - log.debug( - "path=%r pattern=%r num_matches=%r", self.path, self.search_text, n_matches - ) - - return new_content diff --git a/src/semantic_release/version/declarations/__init__.py b/src/semantic_release/version/declarations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/semantic_release/version/declarations/enum.py b/src/semantic_release/version/declarations/enum.py new file mode 100644 index 000000000..848430f22 --- /dev/null +++ b/src/semantic_release/version/declarations/enum.py @@ -0,0 +1,12 @@ +from __future__ import annotations + +from enum import Enum + + +class VersionStampType(str, Enum): + """Enum for the type of version declaration""" + + # The version is a number format, e.g. 1.2.3 + NUMBER_FORMAT = "nf" + + TAG_FORMAT = "tf" diff --git a/src/semantic_release/version/declarations/i_version_replacer.py b/src/semantic_release/version/declarations/i_version_replacer.py new file mode 100644 index 000000000..fcee56564 --- /dev/null +++ b/src/semantic_release/version/declarations/i_version_replacer.py @@ -0,0 +1,67 @@ +from __future__ import annotations + +from abc import ABCMeta, abstractmethod +from typing import TYPE_CHECKING + +if TYPE_CHECKING: # pragma: no cover + from pathlib import Path + + from semantic_release.version.version import Version + + +class IVersionReplacer(metaclass=ABCMeta): + """ + Interface for subclasses that replace a version string in a source file. + + Methods generally have a base implementation are implemented here but + likely just provide a not-supported message but return gracefully + + This class cannot be instantiated directly but must be inherited from + and implement the designated abstract methods. + """ + + @classmethod + def __subclasshook__(cls, subclass: type) -> bool: + # Validate that the subclass implements all of the abstract methods. + # This supports isinstance and issubclass checks. + return bool( + cls is IVersionReplacer + and all( + bool(hasattr(subclass, method) and callable(getattr(subclass, method))) + for method in IVersionReplacer.__abstractmethods__ + ) + ) + + @abstractmethod + def parse(self) -> set[Version]: + """ + Return a set of the versions which can be parsed from the file. + Because a source can match in multiple places, this method returns a + set of matches. Generally, there should only be one element in this + set (i.e. even if the version is specified in multiple places, it + should be the same version in each place), but enforcing that condition + is not mandatory or expected. + """ + raise NotImplementedError # pragma: no cover + + @abstractmethod + def replace(self, new_version: Version) -> str: + """ + Replace the version in the source content with `new_version`, and return + the updated content. + + :param new_version: The new version number as a `Version` instance + """ + raise NotImplementedError # pragma: no cover + + @abstractmethod + def update_file_w_version( + self, new_version: Version, noop: bool = False + ) -> Path | None: + """ + This method reads the underlying file, replaces each occurrence of the + matched pattern, then writes the updated file. + + :param new_version: The new version number as a `Version` instance + """ + raise NotImplementedError # pragma: no cover diff --git a/src/semantic_release/version/declarations/pattern.py b/src/semantic_release/version/declarations/pattern.py new file mode 100644 index 000000000..73f67b465 --- /dev/null +++ b/src/semantic_release/version/declarations/pattern.py @@ -0,0 +1,241 @@ +from __future__ import annotations + +from logging import getLogger +from pathlib import Path +from re import ( + MULTILINE, + compile as regexp, + error as RegExpError, # noqa: N812 + escape as regex_escape, +) +from typing import TYPE_CHECKING + +from deprecated.sphinx import deprecated + +from semantic_release.cli.util import noop_report +from semantic_release.const import SEMVER_REGEX +from semantic_release.version.declarations.enum import VersionStampType +from semantic_release.version.declarations.i_version_replacer import IVersionReplacer +from semantic_release.version.version import Version + +if TYPE_CHECKING: # pragma: no cover + from re import Match + + +log = getLogger(__name__) + + +class VersionSwapper: + """Callable to replace a version number in a string with a new version number.""" + + def __init__(self, new_version_str: str, group_match_name: str) -> None: + self.version_str = new_version_str + self.group_match_name = group_match_name + + def __call__(self, match: Match[str]) -> str: + i, j = match.span() + ii, jj = match.span(self.group_match_name) + return f"{match.string[i:ii]}{self.version_str}{match.string[jj:j]}" + + +class PatternVersionDeclaration(IVersionReplacer): + """ + VersionDeclarationABC implementation representing a version number in a particular + file. The version number is identified by a regular expression, which should be + provided in `search_text`. + """ + + _VERSION_GROUP_NAME = "version" + + def __init__( + self, path: Path | str, search_text: str, stamp_format: VersionStampType + ) -> None: + self._content: str | None = None + self._path = Path(path).resolve() + self._stamp_format = stamp_format + + try: + self._search_pattern = regexp(search_text, flags=MULTILINE) + except RegExpError as err: + raise ValueError( + f"Invalid regular expression for search text: {search_text!r}" + ) from err + + if self._VERSION_GROUP_NAME not in self._search_pattern.groupindex: + raise ValueError( + str.join( + " ", + [ + f"Invalid search text {search_text!r}; must use", + f"'{self._VERSION_GROUP_NAME}' as a named group, for example", + f"(?P<{self._VERSION_GROUP_NAME}>...) . For more info on named", + "groups see https://docs.python.org/3/library/re.html", + ], + ) + ) + + @property + def content(self) -> str: + """A cached property that stores the content of the configured source file.""" + if self._content is None: + log.debug("No content stored, reading from source file %s", self._path) + + if not self._path.exists(): + raise FileNotFoundError(f"path {self._path!r} does not exist") + + self._content = self._path.read_text() + + return self._content + + @content.deleter + def content(self) -> None: + self._content = None + + @deprecated( + version="9.20.0", + reason="Function is unused and will be removed in a future release", + ) + def parse(self) -> set[Version]: # pragma: no cover + """ + Return the versions matching this pattern. + Because a pattern can match in multiple places, this method returns a + set of matches. Generally, there should only be one element in this + set (i.e. even if the version is specified in multiple places, it + should be the same version in each place), but it falls on the caller + to check for this condition. + """ + versions = { + Version.parse(m.group(self._VERSION_GROUP_NAME)) + for m in self._search_pattern.finditer(self.content) + } + + log.debug( + "Parsing current version: path=%r pattern=%r num_matches=%s", + self._path.resolve(), + self._search_pattern, + len(versions), + ) + return versions + + def replace(self, new_version: Version) -> str: + """ + Replace the version in the source content with `new_version`, and return + the updated content. + + :param new_version: The new version number as a `Version` instance + """ + new_content, n_matches = self._search_pattern.subn( + VersionSwapper( + new_version_str=( + new_version.as_tag() + if self._stamp_format == VersionStampType.TAG_FORMAT + else str(new_version) + ), + group_match_name=self._VERSION_GROUP_NAME, + ), + self.content, + ) + + log.debug( + "path=%r pattern=%r num_matches=%r", + self._path, + self._search_pattern, + n_matches, + ) + + return new_content + + def update_file_w_version( + self, new_version: Version, noop: bool = False + ) -> Path | None: + if noop: + if not self._path.exists(): + noop_report( + f"FILE NOT FOUND: cannot stamp version in non-existent file {self._path}", + ) + return None + + if len(self._search_pattern.findall(self.content)) < 1: + noop_report( + f"VERSION PATTERN NOT FOUND: no version to stamp in file {self._path}", + ) + return None + + return self._path + + new_content = self.replace(new_version) + if new_content == self.content: + return None + + self._path.write_text(new_content) + del self.content + + return self._path + + @classmethod + def from_string_definition( + cls, replacement_def: str, tag_format: str + ) -> PatternVersionDeclaration: + """ + create an instance of self from a string representing one item + of the "version_variables" list in the configuration + """ + parts = replacement_def.split(":", maxsplit=2) + + if len(parts) <= 1: + raise ValueError( + f"Invalid replacement definition {replacement_def!r}, missing ':'" + ) + + if len(parts) == 2: + # apply default version_type of "number_format" (ie. "1.2.3") + parts = [*parts, VersionStampType.NUMBER_FORMAT.value] + + path, variable, version_type = parts + + try: + stamp_type = VersionStampType(version_type) + except ValueError as err: + raise ValueError( + str.join( + " ", + [ + "Invalid stamp type, must be one of:", + str.join(", ", [e.value for e in VersionStampType]), + ], + ) + ) from err + + # DEFAULT: naked (no v-prefixed) semver version + value_replace_pattern_str = ( + f"(?P<{cls._VERSION_GROUP_NAME}>{SEMVER_REGEX.pattern})" + ) + + if version_type == VersionStampType.TAG_FORMAT.value: + tag_parts = tag_format.strip().split(r"{version}", maxsplit=1) + value_replace_pattern_str = str.join( + "", + [ + f"(?P<{cls._VERSION_GROUP_NAME}>", + regex_escape(tag_parts[0]), + SEMVER_REGEX.pattern, + (regex_escape(tag_parts[1]) if len(tag_parts) > 1 else ""), + ")", + ], + ) + + search_text = str.join( + "", + [ + # Supports optional matching quotations around variable name + # Negative lookbehind to ensure we don't match part of a variable name + f"""(?x)(?P['"])?(?['"])?{value_replace_pattern_str}(?P=quote2)?""", + ], + ) + + return cls(path, search_text, stamp_type) diff --git a/src/semantic_release/version/declarations/toml.py b/src/semantic_release/version/declarations/toml.py new file mode 100644 index 000000000..ed9542870 --- /dev/null +++ b/src/semantic_release/version/declarations/toml.py @@ -0,0 +1,148 @@ +from __future__ import annotations + +from logging import getLogger +from pathlib import Path +from typing import Any, Dict, cast + +import tomlkit +from deprecated.sphinx import deprecated +from dotty_dict import Dotty + +from semantic_release.cli.util import noop_report +from semantic_release.version.declarations.enum import VersionStampType +from semantic_release.version.declarations.i_version_replacer import IVersionReplacer +from semantic_release.version.version import Version + +# globals +log = getLogger(__name__) + + +class TomlVersionDeclaration(IVersionReplacer): + def __init__( + self, path: Path | str, search_text: str, stamp_format: VersionStampType + ) -> None: + self._content: str | None = None + self._path = Path(path).resolve() + self._stamp_format = stamp_format + self._search_text = search_text + + @property + def content(self) -> str: + """A cached property that stores the content of the configured source file.""" + if self._content is None: + log.debug("No content stored, reading from source file %s", self._path) + + if not self._path.exists(): + raise FileNotFoundError(f"path {self._path!r} does not exist") + + self._content = self._path.read_text() + + return self._content + + @content.deleter + def content(self) -> None: + self._content = None + + @deprecated( + version="9.20.0", + reason="Function is unused and will be removed in a future release", + ) + def parse(self) -> set[Version]: # pragma: no cover + """Look for the version in the source content""" + content = self._load() + maybe_version: str = content.get(self._search_text) # type: ignore[return-value] + if maybe_version is not None: + log.debug( + "Found a key %r that looks like a version (%r)", + self._search_text, + maybe_version, + ) + valid_version = Version.parse(maybe_version) + return {valid_version} if valid_version else set() + # Maybe in future raise error if not found? + return set() + + def replace(self, new_version: Version) -> str: + """ + Replace the version in the source content with `new_version`, and return the + updated content. + """ + content = self._load() + if self._search_text in content: + log.info( + "found %r in source file contents, replacing with %s", + self._search_text, + new_version, + ) + content[self._search_text] = ( + new_version.as_tag() + if self._stamp_format == VersionStampType.TAG_FORMAT + else str(new_version) + ) + + return tomlkit.dumps(cast(Dict[str, Any], content)) + + def _load(self) -> Dotty: + """Load the content of the source file into a Dotty for easier searching""" + return Dotty(tomlkit.loads(self.content)) + + def update_file_w_version( + self, new_version: Version, noop: bool = False + ) -> Path | None: + if noop: + if not self._path.exists(): + noop_report( + f"FILE NOT FOUND: cannot stamp version in non-existent file {self._path!r}", + ) + return None + + if self._search_text not in self._load(): + noop_report( + f"VERSION PATTERN NOT FOUND: no version to stamp in file {self._path!r}", + ) + return None + + return self._path + + new_content = self.replace(new_version) + if new_content == self.content: + return None + + self._path.write_text(new_content) + del self.content + + return self._path + + @classmethod + def from_string_definition(cls, replacement_def: str) -> TomlVersionDeclaration: + """ + create an instance of self from a string representing one item + of the "version_toml" list in the configuration + """ + parts = replacement_def.split(":", maxsplit=2) + + if len(parts) <= 1: + raise ValueError( + f"Invalid TOML replacement definition {replacement_def!r}, missing ':'" + ) + + if len(parts) == 2: + # apply default version_type of "number_format" (ie. "1.2.3") + parts = [*parts, VersionStampType.NUMBER_FORMAT.value] + + path, search_text, version_type = parts + + try: + stamp_type = VersionStampType(version_type) + except ValueError as err: + raise ValueError( + str.join( + " ", + [ + "Invalid stamp type, must be one of:", + str.join(", ", [e.value for e in VersionStampType]), + ], + ) + ) from err + + return cls(path, search_text, stamp_type) diff --git a/tests/e2e/cmd_version/test_version_stamp.py b/tests/e2e/cmd_version/test_version_stamp.py index c95c6f813..c63d5b66c 100644 --- a/tests/e2e/cmd_version/test_version_stamp.py +++ b/tests/e2e/cmd_version/test_version_stamp.py @@ -8,9 +8,11 @@ import pytest import tomlkit import yaml +from dotty_dict import Dotty from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture from semantic_release.cli.commands.main import main +from semantic_release.version.declarations.enum import VersionStampType from tests.const import EXAMPLE_PROJECT_NAME, MAIN_PROG_NAME, VERSION_SUBCMD from tests.fixtures.repos.trunk_based_dev.repo_w_no_tags import ( @@ -122,7 +124,7 @@ def test_version_only_stamp_version( # no push as it should be turned off automatically assert mocked_git_push.call_count == 0 - assert post_mocker.call_count == 0 # no vcs release creation occured + assert post_mocker.call_count == 0 # no vcs release creation occurred # Files that should receive version change assert expected_changed_files == differing_files @@ -174,6 +176,62 @@ def test_stamp_version_variables_python( assert new_version == version_py_after +@pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) +def test_stamp_version_toml( + cli_runner: CliRunner, + update_pyproject_toml: UpdatePyprojectTomlFn, + default_tag_format_str: str, +) -> None: + orig_version = "0.0.0" + new_version = "0.1.0" + orig_release = default_tag_format_str.format(version=orig_version) + new_release = default_tag_format_str.format(version=new_version) + target_file = Path("example.toml") + orig_toml = dedent( + f"""\ + [package] + name = "example" + version = "{orig_version}" + release = "{orig_release}" + date-released = "1970-01-01" + """ + ) + + orig_toml_obj = Dotty(tomlkit.parse(orig_toml)) + + # Write initial text in file + target_file.write_text(orig_toml) + + # Set configuration to modify the yaml file + update_pyproject_toml( + "tool.semantic_release.version_toml", + [ + f"{target_file}:package.version:{VersionStampType.NUMBER_FORMAT.value}", + f"{target_file}:package.release:{VersionStampType.TAG_FORMAT.value}", + ], + ) + + # Act + cli_cmd = VERSION_STAMP_CMD + result = cli_runner.invoke(main, cli_cmd[1:]) + + # Check the result + assert_successful_exit_code(result, cli_cmd) + + # Read content + resulting_toml_obj = Dotty(tomlkit.parse(target_file.read_text())) + + # Check the version was updated + assert new_version == resulting_toml_obj["package.version"] + assert new_release == resulting_toml_obj["package.release"] + + # Check the rest of the content is the same (by resetting the version & comparing) + resulting_toml_obj["package.version"] = orig_version + resulting_toml_obj["package.release"] = orig_release + + assert orig_toml_obj == resulting_toml_obj + + @pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_stamp_version_variables_yaml( cli_runner: CliRunner, @@ -211,7 +269,7 @@ def test_stamp_version_variables_yaml( # Check the version was updated assert new_version == resulting_yaml_obj["version"] - # Check the rest of the content is the same (by reseting the version & comparing) + # Check the rest of the content is the same (by resetting the version & comparing) resulting_yaml_obj["version"] = orig_version assert yaml.safe_load(orig_yaml) == resulting_yaml_obj @@ -222,10 +280,16 @@ def test_stamp_version_variables_yaml_cff( cli_runner: CliRunner, update_pyproject_toml: UpdatePyprojectTomlFn, ) -> None: + """ + Given a yaml file with a top level version directive, + When the version command is run, + Then the version is updated in the file and the rest of the content is unchanged & parsable + + Based on https://github.com/python-semantic-release/python-semantic-release/issues/962 + """ orig_version = "0.0.0" new_version = "0.1.0" target_file = Path("CITATION.cff") - # Derived format from python-semantic-release/python-semantic-release#962 orig_yaml = dedent( f"""\ --- @@ -261,7 +325,7 @@ def test_stamp_version_variables_yaml_cff( # Check the version was updated assert new_version == resulting_yaml_obj["version"] - # Check the rest of the content is the same (by reseting the version & comparing) + # Check the rest of the content is the same (by resetting the version & comparing) resulting_yaml_obj["version"] = orig_version assert yaml.safe_load(orig_yaml) == resulting_yaml_obj @@ -303,7 +367,63 @@ def test_stamp_version_variables_json( # Check the version was updated assert new_version == resulting_json_obj["version"] - # Check the rest of the content is the same (by reseting the version & comparing) + # Check the rest of the content is the same (by resetting the version & comparing) resulting_json_obj["version"] = orig_version assert orig_json == resulting_json_obj + + +@pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) +def test_stamp_version_variables_yaml_kustomization_container_spec( + cli_runner: CliRunner, + update_pyproject_toml: UpdatePyprojectTomlFn, + default_tag_format_str: str, +) -> None: + """ + Given a yaml file with directives that expect a vX.Y.Z version tag declarations, + When a version is stamped and configured to stamp the version using the tag format, + Then the file is updated with the new version in the tag format + + Based on https://github.com/python-semantic-release/python-semantic-release/issues/846 + """ + orig_version = "0.0.0" + new_version = "0.1.0" + target_file = Path("kustomization.yaml") + orig_yaml = dedent( + f"""\ + images: + - name: repo/image + newTag: {default_tag_format_str.format(version=orig_version)} + """ + ) + expected_new_tag_value = default_tag_format_str.format(version=new_version) + + # Setup: Write initial text in file + target_file.write_text(orig_yaml) + + # Setup: Set configuration to modify the yaml file + update_pyproject_toml( + "tool.semantic_release.version_variables", + [ + f"{target_file}:newTag:{VersionStampType.TAG_FORMAT.value}", + ], + ) + + # Act + cli_cmd = VERSION_STAMP_CMD + result = cli_runner.invoke(main, cli_cmd[1:]) + + # Check the result + assert_successful_exit_code(result, cli_cmd) + + # Read content + resulting_yaml_obj = yaml.safe_load(target_file.read_text()) + + # Check the version was updated + assert expected_new_tag_value == resulting_yaml_obj["images"][0]["newTag"] + + # Check the rest of the content is the same (by resetting the version & comparing) + original_yaml_obj = yaml.safe_load(orig_yaml) + resulting_yaml_obj["images"][0]["newTag"] = original_yaml_obj["images"][0]["newTag"] + + assert original_yaml_obj == resulting_yaml_obj diff --git a/tests/unit/semantic_release/version/declarations/__init__.py b/tests/unit/semantic_release/version/declarations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/semantic_release/version/declarations/test_pattern_declaration.py b/tests/unit/semantic_release/version/declarations/test_pattern_declaration.py new file mode 100644 index 000000000..b49f87fa0 --- /dev/null +++ b/tests/unit/semantic_release/version/declarations/test_pattern_declaration.py @@ -0,0 +1,454 @@ +from __future__ import annotations + +from pathlib import Path +from re import compile as regexp +from textwrap import dedent +from typing import TYPE_CHECKING + +import pytest +from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture + +from semantic_release.version.declarations.enum import VersionStampType +from semantic_release.version.declarations.i_version_replacer import IVersionReplacer +from semantic_release.version.declarations.pattern import PatternVersionDeclaration +from semantic_release.version.version import Version + +from tests.fixtures.git_repo import default_tag_format_str + +if TYPE_CHECKING: + from re import Pattern + + +def test_pattern_declaration_is_version_replacer(): + """ + Given the class PatternVersionDeclaration or an instance of it, + When the class is evaluated as a subclass or an instance of, + Then the evaluation is true + """ + assert issubclass(PatternVersionDeclaration, IVersionReplacer) + + pattern_instance = PatternVersionDeclaration( + "file", r"^version = (?P.*)", VersionStampType.NUMBER_FORMAT + ) + assert isinstance(pattern_instance, IVersionReplacer) + + +@pytest.mark.parametrize( + str.join( + ", ", + [ + "replacement_def", + "tag_format", + "starting_contents", + "resulting_contents", + "next_version", + "test_file", + ], + ), + [ + pytest.param( + replacement_def, + tag_format, + starting_contents, + resulting_contents, + next_version, + test_file, + id=test_id, + ) + for test_file in ["test_file"] + for next_version in ["1.2.3"] + for test_id, replacement_def, tag_format, starting_contents, resulting_contents in [ + ( + "Default number format for python string variable", + f"{test_file}:__version__", + # irrelevant for this case + lazy_fixture(default_tag_format_str.__name__), + # Uses equals separator with single quotes + """__version__ = '1.0.0'""", + f"""__version__ = '{next_version}'""", + ), + ( + "Explicit number format for python string variable", + f"{test_file}:__version__:{VersionStampType.NUMBER_FORMAT.value}", + # irrelevant for this case + lazy_fixture(default_tag_format_str.__name__), + # Uses equals separator with single quotes + """__version__ = '1.0.0'""", + f"""__version__ = '{next_version}'""", + ), + ( + "Using default tag format for python string variable", + f"{test_file}:__version__:{VersionStampType.TAG_FORMAT.value}", + lazy_fixture(default_tag_format_str.__name__), + # Uses equals separator with single quotes + """__version__ = 'v1.0.0'""", + f"""__version__ = 'v{next_version}'""", + ), + ( + "Using custom tag format for python string variable", + f"{test_file}:__version__:{VersionStampType.TAG_FORMAT.value}", + "module-v{version}", + # Uses equals separator with double quotes + '''__version__ = "module-v1.0.0"''', + f'''__version__ = "module-v{next_version}"''', + ), + ( + # Based on https://github.com/python-semantic-release/python-semantic-release/issues/846 + "Using default tag format for multi-line yaml", + f"{test_file}:newTag:{VersionStampType.TAG_FORMAT.value}", + lazy_fixture(default_tag_format_str.__name__), + # Uses colon separator without quotes + dedent( + """\ + # kustomization.yaml + images: + - name: repo/image + newTag: v1.0.0 + """ + ), + dedent( + f"""\ + # kustomization.yaml + images: + - name: repo/image + newTag: v{next_version} + """ + ), + ), + ( + # Based on https://github.com/python-semantic-release/python-semantic-release/issues/846 + "Using custom tag format for multi-line yaml", + f"{test_file}:newTag:{VersionStampType.TAG_FORMAT.value}", + "module-v{version}", + # Uses colon separator without quotes + dedent( + """\ + # kustomization.yaml + images: + - name: repo/image + newTag: module-v1.0.0 + """ + ), + dedent( + f"""\ + # kustomization.yaml + images: + - name: repo/image + newTag: module-v{next_version} + """ + ), + ), + ( + "Explicit number format for python walrus string variable", + f"{test_file}:version:{VersionStampType.NUMBER_FORMAT.value}", + # irrelevant for this case + lazy_fixture(default_tag_format_str.__name__), + # Uses walrus separator with single quotes + """if version := '1.0.0': """, + f"""if version := '{next_version}': """, + ), + ( + "Using default number format for multi-line & quoted json", + f"{test_file}:version:{VersionStampType.NUMBER_FORMAT.value}", + # irrelevant for this case + lazy_fixture(default_tag_format_str.__name__), + # Uses colon separator with double quotes + dedent( + """\ + { + "version": "1.0.0" + } + """ + ), + dedent( + f"""\ + {{ + "version": "{next_version}" + }} + """ + ), + ), + ( + "Using default tag format for multi-line & quoted json", + f"{test_file}:version:{VersionStampType.TAG_FORMAT.value}", + lazy_fixture(default_tag_format_str.__name__), + # Uses colon separator with double quotes + dedent( + """\ + { + "version": "v1.0.0" + } + """ + ), + dedent( + f"""\ + {{ + "version": "v{next_version}" + }} + """ + ), + ), + ] + ], +) +def test_pattern_declaration_from_definition( + replacement_def: str, + tag_format: str, + starting_contents: str, + resulting_contents: str, + next_version: str, + test_file: str, + change_to_ex_proj_dir: None, +): + """ + Given a file with a formatted version string, + When update_file_w_version() is called with a new version, + Then the file is updated with the new version string in the specified tag or number format + + Version variables can be separated by either "=", ":", or ':=' with optional whitespace + between operator and variable name. The variable name or values can also be wrapped in either + single or double quotes. + """ + # Setup: create file with initial contents + expected_filepath = Path(test_file).resolve() + expected_filepath.write_text(starting_contents) + + # Create Pattern Replacer + version_replacer = PatternVersionDeclaration.from_string_definition( + replacement_def, + tag_format, + ) + + # Act: apply version change + actual_file_modified = version_replacer.update_file_w_version( + new_version=Version.parse(next_version, tag_format=tag_format), + noop=False, + ) + + # Evaluate + actual_contents = Path(test_file).read_text() + assert resulting_contents == actual_contents + assert expected_filepath == actual_file_modified + + +def test_pattern_declaration_no_file_change( + default_tag_format_str: str, + change_to_ex_proj_dir: None, +): + """ + Given a configured stamp file is already up-to-date, + When update_file_w_version() is called with the same version, + Then the file is not modified and no path is returned + """ + test_file = "test_file" + expected_filepath = Path(test_file).resolve() + next_version = Version.parse("1.2.3", tag_format=default_tag_format_str) + starting_contents = f"""__version__ = '{next_version}'\n""" + + # Setup: create file with initial contents + expected_filepath.write_text(starting_contents) + + # Create Pattern Replacer + version_replacer = PatternVersionDeclaration.from_string_definition( + f"{test_file}:__version__:{VersionStampType.NUMBER_FORMAT.value}", + tag_format=default_tag_format_str, + ) + + # Act: apply version change + file_modified = version_replacer.update_file_w_version( + new_version=next_version, + noop=False, + ) + + # Evaluate + actual_contents = expected_filepath.read_text() + assert starting_contents == actual_contents + assert file_modified is None + + +def test_pattern_declaration_error_on_missing_file( + default_tag_format_str: str, +): + # Initialization should not fail or do anything intensive + version_replacer = PatternVersionDeclaration.from_string_definition( + "nonexistent_file:__version__", + tag_format=default_tag_format_str, + ) + + with pytest.raises(FileNotFoundError): + version_replacer.update_file_w_version( + new_version=Version.parse("1.2.3", tag_format=default_tag_format_str), + noop=False, + ) + + +def test_pattern_declaration_no_version_in_file( + default_tag_format_str: str, + change_to_ex_proj_dir: None, +): + test_file = "test_file" + expected_filepath = Path(test_file).resolve() + starting_contents = """other content\n""" + + # Setup: create file with initial contents + expected_filepath.write_text(starting_contents) + + # Create Pattern Replacer + version_replacer = PatternVersionDeclaration.from_string_definition( + f"{test_file}:__version__:{VersionStampType.NUMBER_FORMAT.value}", + tag_format=default_tag_format_str, + ) + + file_modified = version_replacer.update_file_w_version( + new_version=Version.parse("1.2.3", tag_format=default_tag_format_str), + noop=False, + ) + + # Evaluate + actual_contents = expected_filepath.read_text() + assert file_modified is None + assert starting_contents == actual_contents + + +def test_pattern_declaration_noop_is_noop( + default_tag_format_str: str, + change_to_ex_proj_dir: None, +): + test_file = "test_file" + expected_filepath = Path(test_file).resolve() + starting_contents = """__version__ = '1.0.0'\n""" + + # Setup: create file with initial contents + expected_filepath.write_text(starting_contents) + + # Create Pattern Replacer + version_replacer = PatternVersionDeclaration.from_string_definition( + f"{test_file}:__version__:{VersionStampType.NUMBER_FORMAT.value}", + tag_format=default_tag_format_str, + ) + + # Act: apply version change + file_modified = version_replacer.update_file_w_version( + new_version=Version.parse("1.2.3", tag_format=default_tag_format_str), + noop=True, + ) + + # Evaluate + actual_contents = Path(test_file).read_text() + assert starting_contents == actual_contents + assert expected_filepath == file_modified + + +def test_pattern_declaration_noop_warning_on_missing_file( + default_tag_format_str: str, + capsys: pytest.CaptureFixture[str], +): + version_replacer = PatternVersionDeclaration.from_string_definition( + "nonexistent_file:__version__", + tag_format=default_tag_format_str, + ) + + file_to_modify = version_replacer.update_file_w_version( + new_version=Version.parse("1.2.3", tag_format=default_tag_format_str), + noop=True, + ) + + # Evaluate + assert file_to_modify is None + assert ( + "FILE NOT FOUND: cannot stamp version in non-existent file" + in capsys.readouterr().err + ) + + +def test_pattern_declaration_noop_warning_on_no_version_in_file( + default_tag_format_str: str, + capsys: pytest.CaptureFixture[str], + change_to_ex_proj_dir: None, +): + test_file = "test_file" + starting_contents = """other content\n""" + + # Setup: create file with initial contents + Path(test_file).write_text(starting_contents) + + # Create Pattern Replacer + version_replacer = PatternVersionDeclaration.from_string_definition( + f"{test_file}:__version__:{VersionStampType.NUMBER_FORMAT.value}", + tag_format=default_tag_format_str, + ) + + file_to_modify = version_replacer.update_file_w_version( + new_version=Version.parse("1.2.3", tag_format=default_tag_format_str), + noop=True, + ) + + # Evaluate + assert file_to_modify is None + assert ( + "VERSION PATTERN NOT FOUND: no version to stamp in file" + in capsys.readouterr().err + ) + + +@pytest.mark.parametrize( + "search_text, error_msg", + [ + ( + search_text, + error_msg, + ) + for error_msg, search_text in [ + *[ + ("must use 'version' as a named group", s_text) + for s_text in [ + r"^version = (.*)$", + r"^version = (?P.*)", + r"(?P.*)", + ] + ], + ("Invalid regular expression", r"*"), + ] + ], +) +def test_bad_version_regex_fails(search_text: str, error_msg: Pattern[str] | str): + with pytest.raises(ValueError, match=error_msg): + PatternVersionDeclaration( + "doesn't matter", search_text, VersionStampType.NUMBER_FORMAT + ) + + +@pytest.mark.parametrize( + "replacement_def, error_msg", + [ + pytest.param( + replacement_def, + error_msg, + id=str(error_msg), + ) + for replacement_def, error_msg in [ + ( + f"{Path(__file__)!s}", + regexp(r"Invalid replacement definition .*, missing ':'"), + ), + ( + f"{Path(__file__)!s}:__version__:not_a_valid_version_type", + "Invalid stamp type, must be one of:", + ), + ] + ], +) +def test_pattern_declaration_w_invalid_definition( + default_tag_format_str: str, + replacement_def: str, + error_msg: Pattern[str] | str, +): + """ + check if PatternVersionDeclaration raises ValueError when loaded + from invalid strings given in the config file + """ + with pytest.raises(ValueError, match=error_msg): + PatternVersionDeclaration.from_string_definition( + replacement_def, + default_tag_format_str, + ) diff --git a/tests/unit/semantic_release/version/declarations/test_toml_declaration.py b/tests/unit/semantic_release/version/declarations/test_toml_declaration.py new file mode 100644 index 000000000..a768b6cd3 --- /dev/null +++ b/tests/unit/semantic_release/version/declarations/test_toml_declaration.py @@ -0,0 +1,350 @@ +from __future__ import annotations + +from pathlib import Path +from re import compile as regexp +from textwrap import dedent +from typing import TYPE_CHECKING + +import pytest +from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture + +from semantic_release.version.declarations.enum import VersionStampType +from semantic_release.version.declarations.i_version_replacer import IVersionReplacer +from semantic_release.version.declarations.toml import TomlVersionDeclaration +from semantic_release.version.version import Version + +from tests.fixtures.git_repo import default_tag_format_str + +if TYPE_CHECKING: + from re import Pattern + + +def test_toml_declaration_is_version_replacer(): + """ + Given the class TomlVersionDeclaration or an instance of it, + When the class is evaluated as a subclass or an instance of, + Then the evaluation is true + """ + assert issubclass(TomlVersionDeclaration, IVersionReplacer) + + toml_instance = TomlVersionDeclaration( + "file", "project.version", VersionStampType.NUMBER_FORMAT + ) + assert isinstance(toml_instance, IVersionReplacer) + + +@pytest.mark.parametrize( + str.join( + ", ", + [ + "replacement_def", + "tag_format", + "starting_contents", + "resulting_contents", + "next_version", + "test_file", + ], + ), + [ + pytest.param( + replacement_def, + tag_format, + starting_contents, + resulting_contents, + next_version, + test_file, + id=test_id, + ) + for test_file in ["test_file.toml"] + for next_version in ["1.2.3"] + for test_id, replacement_def, tag_format, starting_contents, resulting_contents in [ + ( + "Default number format for project.version", + f"{test_file}:project.version", + # irrelevant for this case + lazy_fixture(default_tag_format_str.__name__), + # Uses equals separator with single quotes + dedent( + """\ + [project] + version = '1.0.0' + """ + ), + dedent( + f"""\ + [project] + version = "{next_version}" + """ + ), + ), + ( + "Explicit number format for project.version", + f"{test_file}:project.version:{VersionStampType.NUMBER_FORMAT.value}", + # irrelevant for this case + lazy_fixture(default_tag_format_str.__name__), + # Uses equals separator with double quotes + dedent( + """\ + [project] + version = "1.0.0" + """ + ), + dedent( + f"""\ + [project] + version = "{next_version}" + """ + ), + ), + ( + "Using default tag format for toml string variable", + f"{test_file}:version:{VersionStampType.TAG_FORMAT.value}", + lazy_fixture(default_tag_format_str.__name__), + # Uses equals separator with single quotes + '''version = "v1.0.0"''', + f'''version = "v{next_version}"''', + ), + ( + "Using custom tag format for toml string variable", + f"{test_file}:version:{VersionStampType.TAG_FORMAT.value}", + "module-v{version}", + # Uses equals separator with double quotes + '''version = "module-v1.0.0"''', + f'''version = "module-v{next_version}"''', + ), + ] + ], +) +def test_toml_declaration_from_definition( + replacement_def: str, + tag_format: str, + starting_contents: str, + resulting_contents: str, + next_version: str, + test_file: str, + change_to_ex_proj_dir: None, +): + """ + Given a file with a formatted version string, + When update_file_w_version() is called with a new version, + Then the file is updated with the new version string in the specified tag or number format + + Version variables can be separated by either "=", ":", "@", or ':=' with optional whitespace + between operator and variable name. The variable name or values can also be wrapped in either + single or double quotes. + """ + # Setup: create file with initial contents + expected_filepath = Path(test_file).resolve() + expected_filepath.write_text(starting_contents) + + # Create Pattern Replacer + version_replacer = TomlVersionDeclaration.from_string_definition(replacement_def) + + # Act: apply version change + actual_file_modified = version_replacer.update_file_w_version( + new_version=Version.parse(next_version, tag_format=tag_format), + noop=False, + ) + + # Evaluate + actual_contents = Path(test_file).read_text() + assert resulting_contents == actual_contents + assert expected_filepath == actual_file_modified + + +def test_toml_declaration_no_file_change( + change_to_ex_proj_dir: None, +): + """ + Given a configured stamp file is already up-to-date, + When update_file_w_version() is called with the same version, + Then the file is not modified and no path is returned + """ + test_file = "test_file" + next_version = Version.parse("1.2.3") + starting_contents = dedent( + f"""\ + [project] + version = "{next_version}" + """ + ) + + # Setup: create file with initial contents + Path(test_file).write_text(starting_contents) + + # Create Pattern Replacer + version_replacer = TomlVersionDeclaration.from_string_definition( + f"{test_file}:project.version:{VersionStampType.NUMBER_FORMAT.value}", + ) + + # Act: apply version change + file_modified = version_replacer.update_file_w_version( + new_version=next_version, + noop=False, + ) + + # Evaluate + actual_contents = Path(test_file).read_text() + assert starting_contents == actual_contents + assert file_modified is None + + +def test_toml_declaration_error_on_missing_file(): + # Initialization should not fail or do anything intensive + version_replacer = TomlVersionDeclaration.from_string_definition( + "nonexistent_file:version", + ) + + with pytest.raises(FileNotFoundError): + version_replacer.update_file_w_version( + new_version=Version.parse("1.2.3"), + noop=False, + ) + + +def test_toml_declaration_no_version_in_file( + change_to_ex_proj_dir: None, +): + test_file = "test_file" + expected_filepath = Path(test_file).resolve() + starting_contents = dedent( + """\ + [project] + name = "example" + """ + ) + + # Setup: create file with initial contents + expected_filepath.write_text(starting_contents) + + # Create Pattern Replacer + version_replacer = TomlVersionDeclaration.from_string_definition( + f"{test_file}:project.version:{VersionStampType.NUMBER_FORMAT.value}", + ) + + file_modified = version_replacer.update_file_w_version( + new_version=Version.parse("1.2.3"), + noop=False, + ) + + # Evaluate + actual_contents = expected_filepath.read_text() + assert file_modified is None + assert starting_contents == actual_contents + + +def test_toml_declaration_noop_is_noop( + change_to_ex_proj_dir: None, +): + test_file = "test_file" + expected_filepath = Path(test_file).resolve() + starting_contents = dedent( + """\ + [project] + version = '1.0.0' + """ + ) + + # Setup: create file with initial contents + expected_filepath.write_text(starting_contents) + + # Create Pattern Replacer + version_replacer = TomlVersionDeclaration.from_string_definition( + f"{test_file}:project.version:{VersionStampType.NUMBER_FORMAT.value}", + ) + + # Act: apply version change + file_modified = version_replacer.update_file_w_version( + new_version=Version.parse("1.2.3"), + noop=True, + ) + + # Evaluate + actual_contents = Path(test_file).read_text() + assert starting_contents == actual_contents + assert expected_filepath == file_modified + + +def test_toml_declaration_noop_warning_on_missing_file( + capsys: pytest.CaptureFixture[str], +): + version_replacer = TomlVersionDeclaration.from_string_definition( + "nonexistent_file:version", + ) + + file_to_modify = version_replacer.update_file_w_version( + new_version=Version.parse("1.2.3"), + noop=True, + ) + + # Evaluate + assert file_to_modify is None + assert ( + "FILE NOT FOUND: cannot stamp version in non-existent file" + in capsys.readouterr().err + ) + + +def test_toml_declaration_noop_warning_on_no_version_in_file( + capsys: pytest.CaptureFixture[str], + change_to_ex_proj_dir: None, +): + test_file = "test_file" + starting_contents = dedent( + """\ + [project] + name = "example" + """ + ) + + # Setup: create file with initial contents + Path(test_file).write_text(starting_contents) + + # Create Pattern Replacer + version_replacer = TomlVersionDeclaration.from_string_definition( + f"{test_file}:project.version:{VersionStampType.NUMBER_FORMAT.value}", + ) + + file_to_modify = version_replacer.update_file_w_version( + new_version=Version.parse("1.2.3"), + noop=True, + ) + + # Evaluate + assert file_to_modify is None + assert ( + "VERSION PATTERN NOT FOUND: no version to stamp in file" + in capsys.readouterr().err + ) + + +@pytest.mark.parametrize( + "replacement_def, error_msg", + [ + pytest.param( + replacement_def, + error_msg, + id=str(error_msg), + ) + for replacement_def, error_msg in [ + ( + f"{Path(__file__)!s}", + regexp(r"Invalid TOML replacement definition .*, missing ':'"), + ), + ( + f"{Path(__file__)!s}:tool.poetry.version:not_a_valid_version_type", + "Invalid stamp type, must be one of:", + ), + ] + ], +) +def test_toml_declaration_w_invalid_definition( + replacement_def: str, + error_msg: Pattern[str] | str, +): + """ + check if TomlVersionDeclaration raises ValueError when loaded + from invalid strings given in the config file + """ + with pytest.raises(ValueError, match=error_msg): + TomlVersionDeclaration.from_string_definition(replacement_def) diff --git a/tests/unit/semantic_release/version/test_declaration.py b/tests/unit/semantic_release/version/test_declaration.py deleted file mode 100644 index f39d8f3be..000000000 --- a/tests/unit/semantic_release/version/test_declaration.py +++ /dev/null @@ -1,138 +0,0 @@ -import difflib -from pathlib import Path -from textwrap import dedent -from unittest import mock - -import pytest -from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture - -from semantic_release.version.declaration import ( - PatternVersionDeclaration, - TomlVersionDeclaration, -) -from semantic_release.version.version import Version - -from tests.const import EXAMPLE_PROJECT_VERSION -from tests.fixtures.example_project import ( - example_pyproject_toml, - example_setup_cfg, - init_example_project, -) - - -@pytest.mark.usefixtures(init_example_project.__name__) -def test_pyproject_toml_version_found(example_pyproject_toml: Path): - decl = TomlVersionDeclaration( - example_pyproject_toml.resolve(), "tool.poetry.version" - ) - versions = decl.parse() - assert len(versions) == 1 - assert versions.pop() == Version.parse(EXAMPLE_PROJECT_VERSION) - - -@pytest.mark.usefixtures(init_example_project.__name__) -def test_setup_cfg_version_found(example_setup_cfg: Path): - decl = PatternVersionDeclaration( - example_setup_cfg.resolve(), r"^version *= *(?P.*)$" - ) - versions = decl.parse() - assert len(versions) == 1 - assert versions.pop() == Version.parse(EXAMPLE_PROJECT_VERSION) - - -@pytest.mark.parametrize( - "decl_cls, config_file, search_text", - [ - ( - TomlVersionDeclaration, - lazy_fixture(example_pyproject_toml.__name__), - "tool.poetry.version", - ), - ( - PatternVersionDeclaration, - lazy_fixture(example_setup_cfg.__name__), - r"^version = (?P.*)$", - ), - ], -) -@pytest.mark.usefixtures(init_example_project.__name__) -def test_version_replace(decl_cls, config_file, search_text): - new_version = Version(1, 0, 0) - decl = decl_cls(config_file.resolve(), search_text) - orig_content = decl.content - new_content = decl.replace(new_version=new_version) - decl.write(new_content) - - new_decl = decl_cls(config_file.resolve(), search_text) - assert new_decl.parse() == {new_version} - - d = difflib.Differ() - diff = list( - d.compare( - orig_content.splitlines(keepends=True), - new_decl.content.splitlines(keepends=True), - ) - ) - added = [line[2:] for line in diff if line.startswith("+ ")] - removed = [line[2:] for line in diff if line.startswith("- ")] - - assert len(removed) == 1 - assert len(added) == 1 - - (removed_line,) = removed - (added_line,) = added - - # Line is unchanged apart from new version added - assert removed_line.replace(EXAMPLE_PROJECT_VERSION, str(new_version)) == added_line - - -@pytest.mark.parametrize( - "search_text", - [r"^version = (.*)$", r"^version = (?P.*)", r"(?P.*)"], -) -def test_bad_version_regex_fails(search_text): - with mock.patch.object(Path, "exists") as mock_path_exists, pytest.raises( - ValueError, match="must use 'version'" - ): - mock_path_exists.return_value = True - PatternVersionDeclaration("doesn't matter", search_text) - - -def test_pyproject_toml_no_version(tmp_path): - pyproject_toml = tmp_path / "pyproject.toml" - pyproject_toml.write_text( - dedent( - """ - [tool.isort] - profile = "black" - """ - ) - ) - - decl = TomlVersionDeclaration(pyproject_toml.resolve(), "tool.poetry.version") - assert decl.parse() == set() - - -def test_setup_cfg_no_version(tmp_path): - setup_cfg = tmp_path / "setup.cfg" - setup_cfg.write_text( - dedent( - """ - [tool:isort] - profile = black - """ - ) - ) - - decl = PatternVersionDeclaration( - setup_cfg.resolve(), r"^version = (?P.*)$" - ) - assert decl.parse() == set() - - -@pytest.mark.parametrize( - "decl_cls", (TomlVersionDeclaration, PatternVersionDeclaration) -) -def test_version_decl_error_on_missing_file(decl_cls): - with pytest.raises(FileNotFoundError): - decl_cls("/this/is/definitely/a/missing/path/asdfghjkl", "random search text") From 23f69b6ac206d111b1e566367f9b2f033df5c87a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benedikt=20He=C3=9F?= <85877249+benedikt-hess-km@users.noreply.github.com> Date: Mon, 17 Feb 2025 08:47:40 +0100 Subject: [PATCH 054/160] feat(cmd-version): extend `version_variables` to stamp versions with `@` symbol separator (#1185) Resolves: #1156 * test(declaration): add unit tests of user-defined version stamping patterns * test(cmd-version): add test to demonstrate github actions yaml version tag stamping * docs(configuration): clarify `version_variables` config description & `@` separator usage --------- Co-authored-by: codejedi365 --- docs/configuration.rst | 61 ++++++++++++++-- src/semantic_release/version/declaration.py | 11 +-- .../version/declarations/pattern.py | 6 +- tests/e2e/cmd_version/test_version_stamp.py | 72 +++++++++++++++++++ .../declarations/test_pattern_declaration.py | 20 +++++- 5 files changed, 151 insertions(+), 19 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index 7fc4838c4..a94591ff1 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -1340,14 +1340,22 @@ to substitute the version number in the file. The replacement algorithm is **ONL pattern match and replace. It will **NOT** evaluate the code nor will PSR understand any internal object structures (ie. ``file:object.version`` will not work). -.. important:: - The Regular Expression expects a version value to exist in the file to be replaced. - It cannot be an empty string or a non-semver compliant string. If this is the very - first time you are using PSR, we recommend you set the version to ``0.0.0``. +The regular expression generated from the ``version_variables`` definition will: - This may become more flexible in the future with resolution of issue `#941`_. +1. Look for the specified ``variable`` name in the ``file``. The variable name can be + enclosed by single (``'``) or double (``"``) quotation marks but they must match. -.. _#941: https://github.com/python-semantic-release/python-semantic-release/issues/941 +2. The variable name defined by ``variable`` and the version must be separated by + an operand symbol (``=``, ``:``, ``:=``, or ``@``). Whitespace is optional around + the symbol. + +3. The value of the variable must match a `SemVer`_ regular expression and can be + enclosed by single (``'``) or double (``"``) quotation marks but they must match. However, + the enclosing quotes of the value do not have to match the quotes surrounding the variable + name. + +4. If the format type is set to ``tf`` then the variable value must have the matching prefix + and suffix of the :ref:`config-tag_format` setting around the `SemVer`_ version number. Given the pattern matching nature of this feature, the Regular Expression is able to support most file formats because of the similarity of variable declaration across @@ -1360,6 +1368,47 @@ regardless of file extension because it looks for a matching pattern string. TOML files as it actually will interpret the TOML file and replace the version number before writing the file back to disk. +This is a comprehensive list (but not all variations) of examples where the following versions +will be matched and replaced by the new version: + +.. code-block:: + + # Common variable declaration formats + version='1.2.3' + version = "1.2.3" + release = "v1.2.3" # if tag_format is set + + # YAML + version: 1.2.3 + + # JSON + "version": "1.2.3" + + # NPM & GitHub Actions YAML + version@1.2.3 + version@v1.2.3 # if tag_format is set + + # Walrus Operator + version := "1.2.3" + + # Excessive whitespace + version = '1.2.3' + + # Mixed Quotes + "version" = '1.2.3' + + # Custom Tag Format with tag_format set (monorepos) + __release__ = "module-v1.2.3" + +.. important:: + The Regular Expression expects a version value to exist in the file to be replaced. + It cannot be an empty string or a non-semver compliant string. If this is the very + first time you are using PSR, we recommend you set the version to ``0.0.0``. + + This may become more flexible in the future with resolution of issue `#941`_. + +.. _#941: https://github.com/python-semantic-release/python-semantic-release/issues/941 + .. warning:: If the file (ex. JSON) you are replacing has two of the same variable name in it, this pattern match will not be able to differentiate between the two and will replace diff --git a/src/semantic_release/version/declaration.py b/src/semantic_release/version/declaration.py index 92da001a1..3c225d1b5 100644 --- a/src/semantic_release/version/declaration.py +++ b/src/semantic_release/version/declaration.py @@ -14,8 +14,6 @@ from semantic_release.version.declarations.toml import TomlVersionDeclaration if TYPE_CHECKING: # pragma: no cover - from typing import Any - from semantic_release.version.version import Version @@ -66,13 +64,8 @@ def content(self) -> str: self._content = self.path.read_text() return self._content - # mypy doesn't like properties? - @content.setter # type: ignore[attr-defined] - def _(self, _: Any) -> None: - raise AttributeError("'content' cannot be set directly") - - @content.deleter # type: ignore[attr-defined] - def _(self) -> None: + @content.deleter + def content(self) -> None: log.debug("resetting instance-stored source file contents") self._content = None diff --git a/src/semantic_release/version/declarations/pattern.py b/src/semantic_release/version/declarations/pattern.py index 73f67b465..55873ce0a 100644 --- a/src/semantic_release/version/declarations/pattern.py +++ b/src/semantic_release/version/declarations/pattern.py @@ -230,9 +230,9 @@ def from_string_definition( # Supports optional matching quotations around variable name # Negative lookbehind to ensure we don't match part of a variable name f"""(?x)(?P['"])?(?['"])?{value_replace_pattern_str}(?P=quote2)?""", ], diff --git a/tests/e2e/cmd_version/test_version_stamp.py b/tests/e2e/cmd_version/test_version_stamp.py index c63d5b66c..9d45b6019 100644 --- a/tests/e2e/cmd_version/test_version_stamp.py +++ b/tests/e2e/cmd_version/test_version_stamp.py @@ -373,6 +373,78 @@ def test_stamp_version_variables_json( assert orig_json == resulting_json_obj +@pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) +def test_stamp_version_variables_yaml_github_actions( + cli_runner: CliRunner, + update_pyproject_toml: UpdatePyprojectTomlFn, + default_tag_format_str: str, +) -> None: + """ + Given a yaml file with github actions 'uses:' directives which use @vX.Y.Z version declarations, + When a version is stamped and configured to stamp the version using the tag format, + Then the file is updated with the new version in the tag format + + Based on https://github.com/python-semantic-release/python-semantic-release/issues/1156 + """ + orig_version = "0.0.0" + new_version = "0.1.0" + target_file = Path("combined.yml") + action1_yaml_filepath = "my-org/my-actions/.github/workflows/action1.yml" + action2_yaml_filepath = "my-org/my-actions/.github/workflows/action2.yml" + orig_yaml = dedent( + f"""\ + --- + on: + workflow_call: + + jobs: + action1: + uses: {action1_yaml_filepath}@{default_tag_format_str.format(version=orig_version)} + action2: + uses: {action2_yaml_filepath}@{default_tag_format_str.format(version=orig_version)} + """ + ) + expected_action1_value = ( + f"{action1_yaml_filepath}@{default_tag_format_str.format(version=new_version)}" + ) + expected_action2_value = ( + f"{action2_yaml_filepath}@{default_tag_format_str.format(version=new_version)}" + ) + + # Setup: Write initial text in file + target_file.write_text(orig_yaml) + + # Setup: Set configuration to modify the yaml file + update_pyproject_toml( + "tool.semantic_release.version_variables", + [ + f"{target_file}:{action1_yaml_filepath}:{VersionStampType.TAG_FORMAT.value}", + f"{target_file}:{action2_yaml_filepath}:{VersionStampType.TAG_FORMAT.value}", + ], + ) + + # Act + cli_cmd = VERSION_STAMP_CMD + result = cli_runner.invoke(main, cli_cmd[1:]) + + # Check the result + assert_successful_exit_code(result, cli_cmd) + + # Read content + resulting_yaml_obj = yaml.safe_load(target_file.read_text()) + + # Check the version was updated + assert expected_action1_value == resulting_yaml_obj["jobs"]["action1"]["uses"] + assert expected_action2_value == resulting_yaml_obj["jobs"]["action2"]["uses"] + + # Check the rest of the content is the same (by setting the version & comparing) + original_yaml_obj = yaml.safe_load(orig_yaml) + original_yaml_obj["jobs"]["action1"]["uses"] = expected_action1_value + original_yaml_obj["jobs"]["action2"]["uses"] = expected_action2_value + + assert original_yaml_obj == resulting_yaml_obj + + @pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_stamp_version_variables_yaml_kustomization_container_spec( cli_runner: CliRunner, diff --git a/tests/unit/semantic_release/version/declarations/test_pattern_declaration.py b/tests/unit/semantic_release/version/declarations/test_pattern_declaration.py index b49f87fa0..fd7cb7dad 100644 --- a/tests/unit/semantic_release/version/declarations/test_pattern_declaration.py +++ b/tests/unit/semantic_release/version/declarations/test_pattern_declaration.py @@ -92,6 +92,24 @@ def test_pattern_declaration_is_version_replacer(): '''__version__ = "module-v1.0.0"''', f'''__version__ = "module-v{next_version}"''', ), + ( + # Based on https://github.com/python-semantic-release/python-semantic-release/issues/1156 + "Using default tag format for github actions uses-directive", + f"{test_file}:repo/action-name:{VersionStampType.TAG_FORMAT.value}", + lazy_fixture(default_tag_format_str.__name__), + # Uses @ symbol separator without quotes or spaces + """ uses: repo/action-name@v1.0.0""", + f""" uses: repo/action-name@v{next_version}""", + ), + ( + # Based on https://github.com/python-semantic-release/python-semantic-release/issues/1156 + "Using custom tag format for github actions uses-directive", + f"{test_file}:repo/action-name:{VersionStampType.TAG_FORMAT.value}", + "module-v{version}", + # Uses @ symbol separator without quotes or spaces + """ uses: repo/action-name@module-v1.0.0""", + f""" uses: repo/action-name@module-v{next_version}""", + ), ( # Based on https://github.com/python-semantic-release/python-semantic-release/issues/846 "Using default tag format for multi-line yaml", @@ -205,7 +223,7 @@ def test_pattern_declaration_from_definition( When update_file_w_version() is called with a new version, Then the file is updated with the new version string in the specified tag or number format - Version variables can be separated by either "=", ":", or ':=' with optional whitespace + Version variables can be separated by either "=", ":", "@", or ':=' with optional whitespace between operator and variable name. The variable name or values can also be wrapped in either single or double quotes. """ From 3b7466302c07c543377ec0c79bf178291d51f7ca Mon Sep 17 00:00:00 2001 From: semantic-release Date: Mon, 17 Feb 2025 07:56:42 +0000 Subject: [PATCH 055/160] 9.20.0 Automatically generated by python-semantic-release --- CHANGELOG.rst | 36 ++++++++++++++++++++++ docs/automatic-releases/github-actions.rst | 14 ++++----- docs/configuration.rst | 10 +++--- pyproject.toml | 2 +- src/semantic_release/__init__.py | 2 +- 5 files changed, 50 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index db8c86e47..2e21e4312 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,42 @@ CHANGELOG ========= +.. _changelog-v9.20.0: + +v9.20.0 (2025-02-17) +==================== + +✨ Features +----------- + +* **cmd-version**: Enable stamping of tag formatted versions into files, closes `#846`_ (`PR#1190`_, + `8906d8e`_) + +* **cmd-version**: Extend ``version_variables`` to stamp versions with ``@`` symbol separator, + closes `#1156`_ (`PR#1185`_, `23f69b6`_) + +📖 Documentation +---------------- + +* **configuration**: Add usage information for tag format version stamping (`PR#1190`_, `8906d8e`_) + +* **configuration**: Clarify ``version_variables`` config description & ``@`` separator usage + (`PR#1185`_, `23f69b6`_) + +⚙️ Build System +---------------- + +* **deps**: Add ``deprecated~=1.2`` for deprecation notices & sphinx documentation (`PR#1190`_, + `8906d8e`_) + +.. _#1156: https://github.com/python-semantic-release/python-semantic-release/issues/1156 +.. _#846: https://github.com/python-semantic-release/python-semantic-release/issues/846 +.. _23f69b6: https://github.com/python-semantic-release/python-semantic-release/commit/23f69b6ac206d111b1e566367f9b2f033df5c87a +.. _8906d8e: https://github.com/python-semantic-release/python-semantic-release/commit/8906d8e70467af1489d797ec8cb09b1f95e5d409 +.. _PR#1185: https://github.com/python-semantic-release/python-semantic-release/pull/1185 +.. _PR#1190: https://github.com/python-semantic-release/python-semantic-release/pull/1190 + + .. _changelog-v9.19.1: v9.19.1 (2025-02-11) diff --git a/docs/automatic-releases/github-actions.rst b/docs/automatic-releases/github-actions.rst index b192eb6f9..6d80189c7 100644 --- a/docs/automatic-releases/github-actions.rst +++ b/docs/automatic-releases/github-actions.rst @@ -337,7 +337,7 @@ before the :ref:`version ` subcommand. .. code:: yaml - - uses: python-semantic-release/python-semantic-release@v9.19.1 + - uses: python-semantic-release/python-semantic-release@v9.20.0 with: root_options: "-vv --noop" @@ -576,7 +576,7 @@ before the :ref:`publish ` subcommand. .. code:: yaml - - uses: python-semantic-release/publish-action@v9.19.1 + - uses: python-semantic-release/publish-action@v9.20.0 with: root_options: "-vv --noop" @@ -684,7 +684,7 @@ to the GitHub Release Assets as well. - name: Action | Semantic Version Release id: release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.19.1 + uses: python-semantic-release/python-semantic-release@v9.20.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} git_committer_name: "github-actions" @@ -695,7 +695,7 @@ to the GitHub Release Assets as well. if: steps.release.outputs.released == 'true' - name: Publish | Upload to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.19.1 + uses: python-semantic-release/publish-action@v9.20.0 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -744,7 +744,7 @@ The equivalent GitHub Action configuration would be: - name: Action | Semantic Version Release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.19.1 + uses: python-semantic-release/python-semantic-release@v9.20.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} force: patch @@ -772,13 +772,13 @@ Publish Action. .. code:: yaml - name: Release Project 1 - uses: python-semantic-release/python-semantic-release@v9.19.1 + uses: python-semantic-release/python-semantic-release@v9.20.0 with: directory: ./project1 github_token: ${{ secrets.GITHUB_TOKEN }} - name: Release Project 2 - uses: python-semantic-release/python-semantic-release@v9.19.1 + uses: python-semantic-release/python-semantic-release@v9.20.0 with: directory: ./project2 github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/docs/configuration.rst b/docs/configuration.rst index a94591ff1..78e20d183 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -1209,9 +1209,9 @@ within the TOML file, which is more accurate than a pattern replace. The ``version_toml`` option is commonly used to update the version number in the project definition file: ``pyproject.toml`` as seen in the example below. -As of ${NEW_RELEASE_TAG}, the ``version_toml`` option accepts a colon-separated definition +As of v9.20.0, the ``version_toml`` option accepts a colon-separated definition with either 2 or 3 parts. The 2-part definition includes the file path and the version -parameter (in dot-notation). Newly with ${NEW_RELEASE_TAG}, it also accepts an optional +parameter (in dot-notation). Newly with v9.20.0, it also accepts an optional 3rd part to allow configuration of the format type. **Available Format Types** @@ -1271,9 +1271,9 @@ The ``version_variables`` configuration option is a list of string definitions that defines where the version number should be updated in the repository, when a new version is released. -As of ${NEW_RELEASE_TAG}, the ``version_variables`` option accepts a +As of v9.20.0, the ``version_variables`` option accepts a colon-separated definition with either 2 or 3 parts. The 2-part definition includes -the file path and the variable name. Newly with ${NEW_RELEASE_TAG}, it also accepts +the file path and the variable name. Newly with v9.20.0, it also accepts an optional 3rd part to allow configuration of the format type. **Available Format Types** @@ -1283,7 +1283,7 @@ an optional 3rd part to allow configuration of the format type. If the format type is not specified, it will default to the number format. -Prior to ${NEW_RELEASE_TAG}, PSR only supports entries with the first 2-parts +Prior to v9.20.0, PSR only supports entries with the first 2-parts as the tag format type was not available and would only replace numeric version numbers. diff --git a/pyproject.toml b/pyproject.toml index b11f2d7f7..35f8a8d04 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.19.1" +version = "9.20.0" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } diff --git a/src/semantic_release/__init__.py b/src/semantic_release/__init__.py index cad1767ec..dd5648aaa 100644 --- a/src/semantic_release/__init__.py +++ b/src/semantic_release/__init__.py @@ -24,7 +24,7 @@ tags_and_versions, ) -__version__ = "9.19.1" +__version__ = "9.20.0" __all__ = [ "CommitParser", From 2e868255e9de7550f19996018d8825cb254ba7a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 01:31:18 -0700 Subject: [PATCH 056/160] ci(deps): bump `python-semantic-release/publish-action@v9.19.1` to 9.20.0 (#1191) --- .github/workflows/cicd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index d38b53f6c..0eb3e61c7 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -138,7 +138,7 @@ jobs: build: false - name: Release | Add distribution artifacts to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.19.1 + uses: python-semantic-release/publish-action@v9.20.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} tag: ${{ steps.release.outputs.tag }} From 1ac97bc74c69ce61cec98242c19bf8adc1d37fb9 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 22 Feb 2025 11:25:41 -0700 Subject: [PATCH 057/160] feat: add package name variant, `python-semantic-release`, project script (#1199) Adds support for default implementation of `uvx` which matches script name with package name. Resolves: #1195 * test(cli): add entrypoint script validation --- pyproject.toml | 1 + tests/e2e/test_main.py | 27 ++++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 35f8a8d04..8c12ebe05 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ dependencies = [ ] [project.scripts] +python-semantic-release = "semantic_release.__main__:main" semantic-release = "semantic_release.__main__:main" psr = "semantic_release.__main__:main" diff --git a/tests/e2e/test_main.py b/tests/e2e/test_main.py index 42c04b118..fc65c7f21 100644 --- a/tests/e2e/test_main.py +++ b/tests/e2e/test_main.py @@ -1,6 +1,7 @@ from __future__ import annotations import json +import subprocess from pathlib import Path from textwrap import dedent from typing import TYPE_CHECKING @@ -12,7 +13,7 @@ from semantic_release import __version__ from semantic_release.cli.commands.main import main -from tests.const import MAIN_PROG_NAME, VERSION_SUBCMD +from tests.const import MAIN_PROG_NAME, SUCCESS_EXIT_CODE, VERSION_SUBCMD from tests.fixtures.repos import repo_w_no_tags_conventional_commits from tests.util import assert_exit_code, assert_successful_exit_code @@ -25,6 +26,30 @@ from tests.fixtures.git_repo import BuiltRepoResult +@pytest.mark.parametrize( + "project_script_name", + [ + "python-semantic-release", + "semantic-release", + "psr", + ], +) +def test_entrypoint_scripts(project_script_name: str): + # Setup + command = str.join(" ", [project_script_name, "--version"]) + expected_output = f"semantic-release, version {__version__}\n" + + # Act + proc = subprocess.run( # noqa: S602, PLW1510 + command, shell=True, text=True, capture_output=True + ) + + # Evaluate + assert SUCCESS_EXIT_CODE == proc.returncode # noqa: SIM300 + assert expected_output == proc.stdout + assert not proc.stderr + + def test_main_prints_version_and_exits(cli_runner: CliRunner): cli_cmd = [MAIN_PROG_NAME, "--version"] From 1a4116af4b999144998cf94cf84c9c23ff2e352f Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 23 Feb 2025 13:37:13 -0700 Subject: [PATCH 058/160] ci(release): improve concurrency restrictions to prevent release collisions (#1200) Resolves: #1201 * docs(github-actions): update example workflow to handle rapid merges * ci(release): reduce extra steps in deploy job * ci(validate): simplify build job to use psr action * ci(release): reduce extra steps in release job --- .github/workflows/cicd.yml | 69 +++++++++++----------- .github/workflows/validate.yml | 46 +++++++++++---- .github/workflows/verify_upstream.sh | 33 +++++++++++ docs/automatic-releases/github-actions.rst | 68 ++++++++++++++++++--- 4 files changed, 162 insertions(+), 54 deletions(-) create mode 100644 .github/workflows/verify_upstream.sh diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 0eb3e61c7..bcf298dde 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -65,6 +65,9 @@ jobs: validate: uses: ./.github/workflows/validate.yml needs: eval-changes + concurrency: + group: ${{ github.workflow }}-validate-${{ github.ref_name }} + cancel-in-progress: true with: python-versions-linux: '["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]' python-versions-windows: '["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]' @@ -81,10 +84,13 @@ jobs: release: name: Semantic Release runs-on: ubuntu-latest - concurrency: push needs: validate if: ${{ needs.validate.outputs.new-release-detected == 'true' }} + concurrency: + group: ${{ github.workflow }}-release-${{ github.ref_name }} + cancel-in-progress: false + permissions: contents: write @@ -93,18 +99,21 @@ jobs: GITHUB_ACTIONS_AUTHOR_EMAIL: actions@users.noreply.github.com steps: - # Note: we need to checkout the repository at the workflow sha in case during the workflow - # the branch was updated. To keep PSR working with the configured release branches, - # we force a checkout of the desired release branch but at the workflow sha HEAD. - - name: Setup | Checkout Repository at workflow sha + # Note: We checkout the repository at the branch that triggered the workflow + # with the entire history to ensure to match PSR's release branch detection + # and history evaluation. + # However, we forcefully reset the branch to the workflow sha because it is + # possible that the branch was updated while the workflow was running. This + # prevents accidentally releasing un-evaluated changes. + - name: Setup | Checkout Repository on Release Branch uses: actions/checkout@v4 with: + ref: ${{ github.ref_name }} fetch-depth: 0 - ref: ${{ github.sha }} - - name: Setup | Force correct release branch on workflow sha + - name: Setup | Force release branch to be at workflow sha run: | - git checkout -B ${{ github.ref_name }} + git reset --hard ${{ github.sha }} - name: Setup | Download Build Artifacts uses: actions/download-artifact@v4 @@ -113,25 +122,24 @@ jobs: name: ${{ needs.validate.outputs.distribution-artifacts }} path: dist - - name: Evaluate | Determine Next Version - id: version - uses: ./ - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - root_options: "-v --noop" - - name: Release | Bump Version in Docs - if: steps.version.outputs.released == 'true' && steps.version.outputs.is_prerelease == 'false' + if: needs.validate.outputs.new-release-is-prerelease == 'false' env: - NEW_VERSION: ${{ steps.version.outputs.version }} - NEW_RELEASE_TAG: ${{ steps.version.outputs.tag }} + NEW_VERSION: ${{ needs.validate.outputs.new-release-version }} + NEW_RELEASE_TAG: ${{ needs.validate.outputs.new-release-tag }} run: | python -m scripts.bump_version_in_docs git add docs/* + - name: Evaluate | Verify upstream has NOT changed + # Last chance to abort before causing an error as another PR/push was applied to the upstream branch + # while this workflow was running. This is important because we are committing a version change + shell: bash + run: bash .github/workflows/verify_upstream.sh + - name: Release | Python Semantic Release id: release - uses: ./ + uses: python-semantic-release/python-semantic-release@v9.20.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} root_options: "-v" @@ -139,6 +147,7 @@ jobs: - name: Release | Add distribution artifacts to GitHub Release Assets uses: python-semantic-release/publish-action@v9.20.0 + if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} tag: ${{ steps.release.outputs.tag }} @@ -166,8 +175,9 @@ jobs: git push -u origin "$MAJOR_VERSION_TAG" --force outputs: - released: ${{ steps.release.outputs.released }} - tag: ${{ steps.release.outputs.tag }} + released: ${{ steps.release.outputs.released || 'false' }} + new-release-version: ${{ steps.release.outputs.version }} + new-release-tag: ${{ steps.release.outputs.tag }} deploy: @@ -187,19 +197,6 @@ jobs: id-token: write # needed for PyPI upload steps: - # Note: we need to checkout the repository at the workflow sha in case during the workflow - # the branch was updated. To keep PSR working with the configured release branches, - # we force a checkout of the desired release branch but at the workflow sha HEAD. - - name: Setup | Checkout Repository at workflow sha - uses: actions/checkout@v4 - with: - fetch-depth: 1 - ref: ${{ github.sha }} - - - name: Setup | Force correct release branch on workflow sha - run: | - git checkout -B ${{ github.ref_name }} - - name: Setup | Download Build Artifacts uses: actions/download-artifact@v4 id: artifact-download @@ -210,6 +207,8 @@ jobs: # see https://docs.pypi.org/trusted-publishers/ - name: Publish package distributions to PyPI id: pypi-publish - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@v1.12.4 with: + packages-dir: dist + print-hash: true verbose: true diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 63778a3f2..6f4b50d5f 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -47,6 +47,15 @@ on: new-release-detected: description: Boolean string result for if new release is available value: ${{ jobs.build.outputs.new-release-detected }} + new-release-version: + description: Version string for the new release + value: ${{ jobs.build.outputs.new-release-version }} + new-release-tag: + description: Tag string for the new release + value: ${{ jobs.build.outputs.new-release-tag }} + new-release-is-prerelease: + description: Boolean string result for if new release is a pre-release + value: ${{ jobs.build.outputs.new-release-is-prerelease }} distribution-artifacts: description: Artifact Download name for the distribution artifacts value: ${{ jobs.build.outputs.distribution-artifacts }} @@ -91,17 +100,31 @@ jobs: python -m pip install --upgrade pip setuptools wheel pip install -e .[build] - - name: Build | Create the distribution artifacts + - name: Build | Build next version artifacts + id: version + uses: python-semantic-release/python-semantic-release@v9.20.0 + with: + github_token: "" + root_options: "-v" + build: true + changelog: true + commit: false + push: false + tag: false + vcs_release: false + + - name: Build | Annotate next version + if: steps.version.outputs.released == 'true' + run: | + printf '%s\n' "::notice::Next release will be '${{ steps.version.outputs.tag }}'" + + - name: Build | Create non-versioned distribution artifact + if: steps.version.outputs.released == 'false' + run: python -m build . + + - name: Build | Set distribution artifact variables id: build run: | - if new_version="$(semantic-release --strict version --print)"; then - printf '%s\n' "::notice::Next version will be '$new_version'" - printf '%s\n' "new_release_detected=true" >> $GITHUB_OUTPUT - semantic-release version --changelog --no-commit --no-tag - else - printf '%s\n' "new_release_detected=false" >> $GITHUB_OUTPUT - python -m build . - fi printf '%s\n' "dist_dir=dist/*" >> $GITHUB_OUTPUT printf '%s\n' "artifacts_name=dist" >> $GITHUB_OUTPUT @@ -114,7 +137,10 @@ jobs: retention-days: 2 outputs: - new-release-detected: ${{ steps.build.outputs.new_release_detected }} + new-release-detected: ${{ steps.version.outputs.released }} + new-release-version: ${{ steps.version.outputs.version }} + new-release-tag: ${{ steps.version.outputs.tag }} + new-release-is-prerelease: ${{ steps.version.outputs.is_prerelease }} distribution-artifacts: ${{ steps.build.outputs.artifacts_name }} diff --git a/.github/workflows/verify_upstream.sh b/.github/workflows/verify_upstream.sh new file mode 100644 index 000000000..3e8a38ac2 --- /dev/null +++ b/.github/workflows/verify_upstream.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -eu +o pipefail + +# Example output of `git status -sb`: +# ## master...origin/master [behind 1] +# M .github/workflows/verify_upstream.sh +UPSTREAM_BRANCH_NAME="$(git status -sb | head -n 1 | cut -d' ' -f2 | grep -E '\.{3}' | cut -d'.' -f4)" +printf '%s\n' "Upstream branch name: $UPSTREAM_BRANCH_NAME" + +set -o pipefail + +if [ -z "$UPSTREAM_BRANCH_NAME" ]; then + printf >&2 '%s\n' "::error::Unable to determine upstream branch name!" + exit 1 +fi + +git fetch "${UPSTREAM_BRANCH_NAME%%/*}" + +if ! UPSTREAM_SHA="$(git rev-parse "$UPSTREAM_BRANCH_NAME")"; then + printf >&2 '%s\n' "::error::Unable to determine upstream branch sha!" + exit 1 +fi + +HEAD_SHA="$(git rev-parse HEAD)" + +if [ "$HEAD_SHA" != "$UPSTREAM_SHA" ]; then + printf >&2 '%s\n' "[HEAD SHA] $HEAD_SHA != $UPSTREAM_SHA [UPSTREAM SHA]" + printf >&2 '%s\n' "::error::Upstream has changed, aborting release..." + exit 1 +fi + +printf '%s\n' "Verified upstream branch has not changed, continuing with release..." diff --git a/docs/automatic-releases/github-actions.rst b/docs/automatic-releases/github-actions.rst index 6d80189c7..dde065ba2 100644 --- a/docs/automatic-releases/github-actions.rst +++ b/docs/automatic-releases/github-actions.rst @@ -643,7 +643,7 @@ Examples Common Workflow Example ----------------------- -The following is a common workflow example that uses both the Python Semantic Release Action +The following is a simple common workflow example that uses both the Python Semantic Release Action and the Python Semantic Release Publish Action. This workflow will run on every push to the ``main`` branch and will create a new release upon a successful version determination. If a version is released, the workflow will then publish the package to PyPI and upload the package @@ -661,25 +661,69 @@ to the GitHub Release Assets as well. jobs: release: runs-on: ubuntu-latest - concurrency: release + concurrency: + group: ${{ github.workflow }}-release-${{ github.ref_name }} + cancel-in-progress: false permissions: id-token: write contents: write steps: - # Note: we need to checkout the repository at the workflow sha in case during the workflow - # the branch was updated. To keep PSR working with the configured release branches, - # we force a checkout of the desired release branch but at the workflow sha HEAD. - - name: Setup | Checkout Repository at workflow sha + # Note: We checkout the repository at the branch that triggered the workflow + # with the entire history to ensure to match PSR's release branch detection + # and history evaluation. + # However, we forcefully reset the branch to the workflow sha because it is + # possible that the branch was updated while the workflow was running. This + # prevents accidentally releasing un-evaluated changes. + - name: Setup | Checkout Repository on Release Branch uses: actions/checkout@v4 with: + ref: ${{ github.ref_name }} fetch-depth: 0 - ref: ${{ github.sha }} - - name: Setup | Force correct release branch on workflow sha + - name: Setup | Force release branch to be at workflow sha run: | - git checkout -B ${{ github.ref_name }} ${{ github.sha }} + git reset --hard ${{ github.sha }} + + - name: Evaluate | Verify upstream has NOT changed + # Last chance to abort before causing an error as another PR/push was applied to + # the upstream branch while this workflow was running. This is important + # because we are committing a version change (--commit). You may omit this step + # if you have 'commit: false' in your configuration. + # + # You may consider moving this to a repo script and call it from this step instead + # of writing it in-line. + shell: bash + run: | + set +o pipefail + + UPSTREAM_BRANCH_NAME="$(git status -sb | head -n 1 | cut -d' ' -f2 | grep -E '\.{3}' | cut -d'.' -f4)" + printf '%s\n' "Upstream branch name: $UPSTREAM_BRANCH_NAME" + + set -o pipefail + + if [ -z "$UPSTREAM_BRANCH_NAME" ]; then + printf >&2 '%s\n' "::error::Unable to determine upstream branch name!" + exit 1 + fi + + git fetch "${UPSTREAM_BRANCH_NAME%%/*}" + + if ! UPSTREAM_SHA="$(git rev-parse "$UPSTREAM_BRANCH_NAME")"; then + printf >&2 '%s\n' "::error::Unable to determine upstream branch sha!" + exit 1 + fi + + HEAD_SHA="$(git rev-parse HEAD)" + + if [ "$HEAD_SHA" != "$UPSTREAM_SHA" ]; then + printf >&2 '%s\n' "[HEAD SHA] $HEAD_SHA != $UPSTREAM_SHA [UPSTREAM SHA]" + printf >&2 '%s\n' "::error::Upstream has changed, aborting release..." + exit 1 + fi + + printf '%s\n' "Verified upstream branch has not changed, continuing with release..." - name: Action | Semantic Version Release id: release @@ -706,6 +750,11 @@ to the GitHub Release Assets as well. one release job in the case if there are multiple pushes to ``main`` in a short period of time. + Secondly the *Evaluate | Verify upstream has NOT changed* step is used to ensure that the + upstream branch has not changed while the workflow was running. This is important because + we are committing a version change (``commit: true``) and there might be a push collision + that would cause undesired behavior. Review Issue `#1201`_ for more detailed information. + .. warning:: You must set ``fetch-depth`` to 0 when using ``actions/checkout@v4``, since Python Semantic Release needs access to the full history to build a changelog @@ -721,6 +770,7 @@ to the GitHub Release Assets as well. case, you will also need to pass the new token to ``actions/checkout`` (as the ``token`` input) in order to gain push access. +.. _#1201: https://github.com/python-semantic-release/python-semantic-release/issues/1201 .. _concurrency: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idconcurrency Version Overrides Example From 26bb37cfab71a5a372e3db0f48a6eac57519a4a6 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Sun, 23 Feb 2025 20:45:30 +0000 Subject: [PATCH 059/160] 9.21.0 Automatically generated by python-semantic-release --- CHANGELOG.rst | 23 ++++++++++++++++++++++ docs/automatic-releases/github-actions.rst | 14 ++++++------- pyproject.toml | 2 +- src/semantic_release/__init__.py | 2 +- 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 2e21e4312..0e07247dc 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,29 @@ CHANGELOG ========= +.. _changelog-v9.21.0: + +v9.21.0 (2025-02-23) +==================== + +✨ Features +----------- + +* Add package name variant, ``python-semantic-release``, project script, closes `#1195`_ + (`PR#1199`_, `1ac97bc`_) + +📖 Documentation +---------------- + +* **github-actions**: Update example workflow to handle rapid merges (`PR#1200`_, `1a4116a`_) + +.. _#1195: https://github.com/python-semantic-release/python-semantic-release/issues/1195 +.. _1a4116a: https://github.com/python-semantic-release/python-semantic-release/commit/1a4116af4b999144998cf94cf84c9c23ff2e352f +.. _1ac97bc: https://github.com/python-semantic-release/python-semantic-release/commit/1ac97bc74c69ce61cec98242c19bf8adc1d37fb9 +.. _PR#1199: https://github.com/python-semantic-release/python-semantic-release/pull/1199 +.. _PR#1200: https://github.com/python-semantic-release/python-semantic-release/pull/1200 + + .. _changelog-v9.20.0: v9.20.0 (2025-02-17) diff --git a/docs/automatic-releases/github-actions.rst b/docs/automatic-releases/github-actions.rst index dde065ba2..d67f6c590 100644 --- a/docs/automatic-releases/github-actions.rst +++ b/docs/automatic-releases/github-actions.rst @@ -337,7 +337,7 @@ before the :ref:`version ` subcommand. .. code:: yaml - - uses: python-semantic-release/python-semantic-release@v9.20.0 + - uses: python-semantic-release/python-semantic-release@v9.21.0 with: root_options: "-vv --noop" @@ -576,7 +576,7 @@ before the :ref:`publish ` subcommand. .. code:: yaml - - uses: python-semantic-release/publish-action@v9.20.0 + - uses: python-semantic-release/publish-action@v9.21.0 with: root_options: "-vv --noop" @@ -728,7 +728,7 @@ to the GitHub Release Assets as well. - name: Action | Semantic Version Release id: release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.20.0 + uses: python-semantic-release/python-semantic-release@v9.21.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} git_committer_name: "github-actions" @@ -739,7 +739,7 @@ to the GitHub Release Assets as well. if: steps.release.outputs.released == 'true' - name: Publish | Upload to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.20.0 + uses: python-semantic-release/publish-action@v9.21.0 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -794,7 +794,7 @@ The equivalent GitHub Action configuration would be: - name: Action | Semantic Version Release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.20.0 + uses: python-semantic-release/python-semantic-release@v9.21.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} force: patch @@ -822,13 +822,13 @@ Publish Action. .. code:: yaml - name: Release Project 1 - uses: python-semantic-release/python-semantic-release@v9.20.0 + uses: python-semantic-release/python-semantic-release@v9.21.0 with: directory: ./project1 github_token: ${{ secrets.GITHUB_TOKEN }} - name: Release Project 2 - uses: python-semantic-release/python-semantic-release@v9.20.0 + uses: python-semantic-release/python-semantic-release@v9.21.0 with: directory: ./project2 github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/pyproject.toml b/pyproject.toml index 8c12ebe05..64f665d0b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.20.0" +version = "9.21.0" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } diff --git a/src/semantic_release/__init__.py b/src/semantic_release/__init__.py index dd5648aaa..fa54ef662 100644 --- a/src/semantic_release/__init__.py +++ b/src/semantic_release/__init__.py @@ -24,7 +24,7 @@ tags_and_versions, ) -__version__ = "9.20.0" +__version__ = "9.21.0" __all__ = [ "CommitParser", From 98287c9019325131b6ccc33e1a4159478e2c6c0c Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 23 Feb 2025 14:05:41 -0700 Subject: [PATCH 060/160] ci(deps): bump `python-semantic-release@v9.20.0` action to 9.21.0 (#1202) --- .github/workflows/cicd.yml | 2 +- .github/workflows/validate.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index bcf298dde..2deb632e2 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -139,7 +139,7 @@ jobs: - name: Release | Python Semantic Release id: release - uses: python-semantic-release/python-semantic-release@v9.20.0 + uses: python-semantic-release/python-semantic-release@v9.21.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} root_options: "-v" diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 6f4b50d5f..aab73b6d3 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -102,7 +102,7 @@ jobs: - name: Build | Build next version artifacts id: version - uses: python-semantic-release/python-semantic-release@v9.20.0 + uses: python-semantic-release/python-semantic-release@v9.21.0 with: github_token: "" root_options: "-v" From 84a752b5031c8c06727a1c6d92bcf83081649443 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 23 Feb 2025 14:12:24 -0700 Subject: [PATCH 061/160] ci(deps): bump `mikepenz/action-junit-report@v5.3.0` action to 5.4.0 (#1202) --- .github/workflows/validate.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index aab73b6d3..7ef1e6b85 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -183,7 +183,7 @@ jobs: --junit-xml=tests/reports/pytest-results.xml - name: Report | Upload Test Results - uses: mikepenz/action-junit-report@v5.3.0 + uses: mikepenz/action-junit-report@v5.4.0 if: ${{ always() && steps.tests.outcome != 'skipped' }} with: report_paths: ./tests/reports/*.xml @@ -271,7 +271,7 @@ jobs: retention-days: 1 - name: Report | Upload Test Results - uses: mikepenz/action-junit-report@v5.3.0 + uses: mikepenz/action-junit-report@v5.4.0 if: ${{ always() && steps.tests.outcome != 'skipped' }} with: report_paths: ./tests/reports/*.xml @@ -366,7 +366,7 @@ jobs: retention-days: 1 - name: Report | Upload Test Results - uses: mikepenz/action-junit-report@v5.3.0 + uses: mikepenz/action-junit-report@v5.4.0 if: ${{ always() && steps.tests.outcome != 'skipped' }} with: report_paths: ./tests/reports/*.xml From 5093f30da0746a9a5302fb37c62d6aae028a930c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 23 Feb 2025 14:32:26 -0700 Subject: [PATCH 062/160] ci(deps): bump `python-semantic-release/publish-action@v9.20.0` to 9.21.0 (#1203) --- .github/workflows/cicd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 2deb632e2..0dda5e88c 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -146,7 +146,7 @@ jobs: build: false - name: Release | Add distribution artifacts to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.20.0 + uses: python-semantic-release/publish-action@v9.21.0 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} From e81a78ae6e21ff8d1adfad470510c7ade03cf496 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 22 Mar 2025 21:31:09 -0600 Subject: [PATCH 063/160] ci(workflows): set 3rd party actions to specific commit hash (#1219) --- .github/workflows/ci.yml | 6 +++--- .github/workflows/cicd.yml | 4 ++-- .github/workflows/manual.yml | 2 +- .github/workflows/validate.yml | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 36b693d01..92631bc35 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: with: fetch-depth: 0 - - uses: wagoid/commitlint-github-action@v6 + - uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 eval-changes: @@ -41,13 +41,13 @@ jobs: - name: Evaluate | Check common file types for changes id: core-changed-files - uses: tj-actions/changed-files@v45.0.7 + uses: tj-actions/changed-files@a284dc1814e3fd07f2e34267fc8f81227ed29fb8 #v45.0.9 with: files_yaml_from_source_file: .github/changed-files-spec.yml - name: Evaluate | Check specific file types for changes id: ci-changed-files - uses: tj-actions/changed-files@v45.0.7 + uses: tj-actions/changed-files@a284dc1814e3fd07f2e34267fc8f81227ed29fb8 #v45.0.9 with: files_yaml: | ci: diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 0dda5e88c..bc314a0ba 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -26,14 +26,14 @@ jobs: - name: Evaluate | Check common file types for changes id: core-changed-files - uses: tj-actions/changed-files@v45.0.7 + uses: tj-actions/changed-files@a284dc1814e3fd07f2e34267fc8f81227ed29fb8 #v45.0.9 with: base_sha: ${{ github.event.push.before }} files_yaml_from_source_file: .github/changed-files-spec.yml - name: Evaluate | Check specific file types for changes id: ci-changed-files - uses: tj-actions/changed-files@v45.0.7 + uses: tj-actions/changed-files@a284dc1814e3fd07f2e34267fc8f81227ed29fb8 #v45.0.9 with: base_sha: ${{ github.event.push.before }} files_yaml: | diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml index 4e54606dd..64263f37e 100644 --- a/.github/workflows/manual.yml +++ b/.github/workflows/manual.yml @@ -65,7 +65,7 @@ jobs: python-version: ${{ env.COMMON_PYTHON_VERSION }} - name: Setup | Write file - uses: DamianReeves/write-file-action@v1.3 + uses: DamianReeves/write-file-action@6929a9a6d1807689191dcc8bbe62b54d70a32b42 #v1.3 with: path: .github/manual_eval_input.py write-mode: overwrite diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 7ef1e6b85..ac4ec3e72 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -183,7 +183,7 @@ jobs: --junit-xml=tests/reports/pytest-results.xml - name: Report | Upload Test Results - uses: mikepenz/action-junit-report@v5.4.0 + uses: mikepenz/action-junit-report@b14027d33d3a745ccc4d6a12f649e83110b5a373 # v5.4.0 if: ${{ always() && steps.tests.outcome != 'skipped' }} with: report_paths: ./tests/reports/*.xml @@ -271,7 +271,7 @@ jobs: retention-days: 1 - name: Report | Upload Test Results - uses: mikepenz/action-junit-report@v5.4.0 + uses: mikepenz/action-junit-report@b14027d33d3a745ccc4d6a12f649e83110b5a373 # v5.4.0 if: ${{ always() && steps.tests.outcome != 'skipped' }} with: report_paths: ./tests/reports/*.xml @@ -366,7 +366,7 @@ jobs: retention-days: 1 - name: Report | Upload Test Results - uses: mikepenz/action-junit-report@v5.4.0 + uses: mikepenz/action-junit-report@b14027d33d3a745ccc4d6a12f649e83110b5a373 # v5.4.0 if: ${{ always() && steps.tests.outcome != 'skipped' }} with: report_paths: ./tests/reports/*.xml From a0cd1be4e3aa283cbdc544785e5f895c8391dfb8 Mon Sep 17 00:00:00 2001 From: xakepnz Date: Thu, 17 Apr 2025 13:47:07 +1200 Subject: [PATCH 064/160] build(deps): expand `python-gitlab` dependency to include `v5.0.0` (#1228) The primary change from `v4` to `v5` in this dependency was the removal of identified support for `Python 3.8`. Since we still support `Python 3.8`, then we need to maintain the flexibility for 3.8 package managers to install `v4` while allowing more modern projects to use the new features of `v5` along side `python-semantic-release`. Co-authored-by: Neil McDonald --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 64f665d0b..0b3fa17dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ dependencies = [ "gitpython ~= 3.0", "requests ~= 2.25", "jinja2 ~= 3.1", - "python-gitlab ~= 4.0", + "python-gitlab >= 4.0.0, < 6.0.0", "tomlkit ~= 0.11", "dotty-dict ~= 1.3", "importlib-resources ~= 6.0", From ca6645793937b9ee792d135c91af6ab8c6c245b0 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Wed, 16 Apr 2025 19:43:11 -0600 Subject: [PATCH 065/160] ci(deps): bump `tj-actions/changed-files@v45.0.9` to `v46.0.2` --- .github/workflows/ci.yml | 4 ++-- .github/workflows/cicd.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 92631bc35..7a2bda688 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,13 +41,13 @@ jobs: - name: Evaluate | Check common file types for changes id: core-changed-files - uses: tj-actions/changed-files@a284dc1814e3fd07f2e34267fc8f81227ed29fb8 #v45.0.9 + uses: tj-actions/changed-files@26a38635fc1173cc5820336ce97be6188d0de9f5 #v46.0.2 with: files_yaml_from_source_file: .github/changed-files-spec.yml - name: Evaluate | Check specific file types for changes id: ci-changed-files - uses: tj-actions/changed-files@a284dc1814e3fd07f2e34267fc8f81227ed29fb8 #v45.0.9 + uses: tj-actions/changed-files@26a38635fc1173cc5820336ce97be6188d0de9f5 #v46.0.2 with: files_yaml: | ci: diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index bc314a0ba..955aafa9f 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -26,14 +26,14 @@ jobs: - name: Evaluate | Check common file types for changes id: core-changed-files - uses: tj-actions/changed-files@a284dc1814e3fd07f2e34267fc8f81227ed29fb8 #v45.0.9 + uses: tj-actions/changed-files@26a38635fc1173cc5820336ce97be6188d0de9f5 #v46.0.2 with: base_sha: ${{ github.event.push.before }} files_yaml_from_source_file: .github/changed-files-spec.yml - name: Evaluate | Check specific file types for changes id: ci-changed-files - uses: tj-actions/changed-files@a284dc1814e3fd07f2e34267fc8f81227ed29fb8 #v45.0.9 + uses: tj-actions/changed-files@26a38635fc1173cc5820336ce97be6188d0de9f5 #v46.0.2 with: base_sha: ${{ github.event.push.before }} files_yaml: | From d274f0c049f14ec9e8786b4fb1e7a9ac9404f45e Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Wed, 16 Apr 2025 19:45:14 -0600 Subject: [PATCH 066/160] ci(deps): bump `mikepenz/action-junit-report@v5.4.0` to `v5.5.0` --- .github/workflows/validate.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index ac4ec3e72..df23da85a 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -183,7 +183,7 @@ jobs: --junit-xml=tests/reports/pytest-results.xml - name: Report | Upload Test Results - uses: mikepenz/action-junit-report@b14027d33d3a745ccc4d6a12f649e83110b5a373 # v5.4.0 + uses: mikepenz/action-junit-report@97744eca465b8df9e6e33271cb155003f85327f1 # v5.5.0 if: ${{ always() && steps.tests.outcome != 'skipped' }} with: report_paths: ./tests/reports/*.xml @@ -271,7 +271,7 @@ jobs: retention-days: 1 - name: Report | Upload Test Results - uses: mikepenz/action-junit-report@b14027d33d3a745ccc4d6a12f649e83110b5a373 # v5.4.0 + uses: mikepenz/action-junit-report@97744eca465b8df9e6e33271cb155003f85327f1 # v5.5.0 if: ${{ always() && steps.tests.outcome != 'skipped' }} with: report_paths: ./tests/reports/*.xml @@ -366,7 +366,7 @@ jobs: retention-days: 1 - name: Report | Upload Test Results - uses: mikepenz/action-junit-report@b14027d33d3a745ccc4d6a12f649e83110b5a373 # v5.4.0 + uses: mikepenz/action-junit-report@97744eca465b8df9e6e33271cb155003f85327f1 # v5.5.0 if: ${{ always() && steps.tests.outcome != 'skipped' }} with: report_paths: ./tests/reports/*.xml From 691536e98f311d0fc6d29a72c41ce5a65f1f4b6c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Apr 2025 19:07:52 -0700 Subject: [PATCH 067/160] build(deps): bump `rich` dependency from `13.0` to `14.0` (#1224) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0b3fa17dc..db54dde31 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ dependencies = [ "dotty-dict ~= 1.3", "importlib-resources ~= 6.0", "pydantic ~= 2.0", - "rich ~= 13.0", + "rich ~= 14.0", "shellingham ~= 1.5", "Deprecated ~= 1.2", # Backport of deprecated decorator for python 3.8 ] From 550e85f5ec2695d5aa680014127846d58c680e31 Mon Sep 17 00:00:00 2001 From: Timon Date: Fri, 18 Apr 2025 07:40:38 +0200 Subject: [PATCH 068/160] docs(github-actions): expound on monorepo example to include publishing actions (#1229) Co-authored-by: codejedi365 --- docs/automatic-releases/github-actions.rst | 95 ++++++++++++++++++---- 1 file changed, 80 insertions(+), 15 deletions(-) diff --git a/docs/automatic-releases/github-actions.rst b/docs/automatic-releases/github-actions.rst index d67f6c590..e8d0ce636 100644 --- a/docs/automatic-releases/github-actions.rst +++ b/docs/automatic-releases/github-actions.rst @@ -734,10 +734,6 @@ to the GitHub Release Assets as well. git_committer_name: "github-actions" git_committer_email: "actions@users.noreply.github.com" - - name: Publish | Upload package to PyPI - uses: pypa/gh-action-pypi-publish@v1 - if: steps.release.outputs.released == 'true' - - name: Publish | Upload to GitHub Release Assets uses: python-semantic-release/publish-action@v9.21.0 if: steps.release.outputs.released == 'true' @@ -745,6 +741,10 @@ to the GitHub Release Assets as well. github_token: ${{ secrets.GITHUB_TOKEN }} tag: ${{ steps.release.outputs.tag }} + - name: Publish | Upload package to PyPI + uses: pypa/gh-action-pypi-publish@SHA1_HASH # vX.X.X + if: steps.release.outputs.released == 'true' + .. important:: The `concurrency`_ directive is used on the job to prevent race conditions of more than one release job in the case if there are multiple pushes to ``main`` in a short period @@ -816,19 +816,84 @@ For multiple packages, you would need to run the action multiple times, to relea each project. The following example demonstrates how to release two projects in a monorepo. +Remember that for each release of each submodule you will then need to handle publishing +each package separately as well. This is dependent on the result of your build commands. +In the example below, we assume a simple ``build`` module command to build a ``sdist`` +and wheel artifacts into the submodule's ``dist`` directory. + The ``directory`` input directive is also available for the Python Semantic Release Publish Action. .. code:: yaml - - name: Release Project 1 - uses: python-semantic-release/python-semantic-release@v9.21.0 - with: - directory: ./project1 - github_token: ${{ secrets.GITHUB_TOKEN }} - - - name: Release Project 2 - uses: python-semantic-release/python-semantic-release@v9.21.0 - with: - directory: ./project2 - github_token: ${{ secrets.GITHUB_TOKEN }} + jobs: + + release: + + env: + SUBMODULE_1_DIR: project1 + SUBMODULE_2_DIR: project2 + + steps: + + # ------------------------------------------------------------------- # + # Note the use of different IDs to distinguish which submodule was # + # identified to be released. The subsequent actions then reference # + # their specific release ID to determine if a release occurred. # + # ------------------------------------------------------------------- # + + - name: Release submodule 1 + id: release-submod-1 + uses: python-semantic-release/python-semantic-release@v9.21.0 + with: + directory: ${{ env.SUBMODULE_1_DIR }} + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Release submodule 2 + id: release-submod-2 + uses: python-semantic-release/python-semantic-release@v9.21.0 + with: + directory: ${{ env.SUBMODULE_2_DIR }} + github_token: ${{ secrets.GITHUB_TOKEN }} + + # ------------------------------------------------------------------- # + # For each submodule, you will have to publish the package separately # + # and only attempt to publish if the release for that submodule was # + # deemed a release (and the release was successful). # + # ------------------------------------------------------------------- # + + - name: Publish | Upload package 1 to GitHub Release Assets + uses: python-semantic-release/publish-action@v9.21.0 + if: steps.release-submod-1.outputs.released == 'true' + with: + directory: ${{ env.SUBMODULE_1_DIR }} + github_token: ${{ secrets.GITHUB_TOKEN }} + tag: ${{ steps.release-submod-1.outputs.tag }} + + - name: Publish | Upload package 2 to GitHub Release Assets + uses: python-semantic-release/publish-action@v9.21.0 + if: steps.release-submod-2.outputs.released == 'true' + with: + directory: ${{ env.SUBMODULE_2_DIR }} + github_token: ${{ secrets.GITHUB_TOKEN }} + tag: ${{ steps.release-submod-2.outputs.tag }} + + # ------------------------------------------------------------------- # + # Python Semantic Release is not responsible for publishing your # + # python artifacts to PyPI. Use the official PyPA publish action # + # instead. The following steps are an example but is not guaranteed # + # to work as the action is not maintained by the # + # python-semantic-release team. # + # ------------------------------------------------------------------- # + + - name: Publish | Upload package 1 to PyPI + uses: pypa/gh-action-pypi-publish@SHA1_HASH # vX.X.X + if: steps.release-submod-1.outputs.released == 'true' + with: + packages-dir: ${{ format('{}/dist', env.SUBMODULE_1_DIR) }} + + - name: Publish | Upload package 2 to PyPI + uses: pypa/gh-action-pypi-publish@SHA1_HASH # vX.X.X + if: steps.release-submod-2.outputs.released == 'true' + with: + packages-dir: ${{ format('{}/dist', env.SUBMODULE_2_DIR) }} From f7acbf3d7920489bb1ccef0ddd4ebbf726831620 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Apr 2025 20:35:26 -0700 Subject: [PATCH 069/160] build(deps-test): expand `pytest-cov` dependency to include `v6` updates (#1231) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index db54dde31..46a885779 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,7 +68,7 @@ test = [ "pyyaml ~= 6.0", "pytest ~= 8.3", "pytest-clarity ~= 1.0", - "pytest-cov ~= 5.0", + "pytest-cov >= 5.0.0, < 7.0.0", "pytest-env ~= 1.0", "pytest-lazy-fixtures ~= 1.1.1", "pytest-mock ~= 3.0", From 6e3678a948b62b9af7c4639fa7e199bee5fe8a2e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Apr 2025 20:39:25 -0700 Subject: [PATCH 070/160] build(deps-build): expand `setuptools` dependency to include `v76`, `v77`, & `v78` (#1225) This expansion maintains support for python 3.8 while accepting the new features of setuptools with more modern python versions. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 46a885779..59b7613d6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ # Ref: https://packaging.python.org/en/latest/specifications/declaring-project-metadata/ # and https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html [build-system] -requires = ["setuptools ~= 75.3.0", "wheel ~= 0.42"] +requires = ["setuptools >= 75.3.0, < 79.0.0", "wheel ~= 0.42"] build-backend = "setuptools.build_meta" [project] From 493b90666cf8a0b32865c239acf80c22017ca576 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 4 May 2025 21:09:30 -0600 Subject: [PATCH 071/160] ci(deps): bump `tj-actions/changed-files@v46.0.2` to `v46.0.5` --- .github/workflows/ci.yml | 4 ++-- .github/workflows/cicd.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7a2bda688..53d5c83c0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,13 +41,13 @@ jobs: - name: Evaluate | Check common file types for changes id: core-changed-files - uses: tj-actions/changed-files@26a38635fc1173cc5820336ce97be6188d0de9f5 #v46.0.2 + uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c #v46.0.5 with: files_yaml_from_source_file: .github/changed-files-spec.yml - name: Evaluate | Check specific file types for changes id: ci-changed-files - uses: tj-actions/changed-files@26a38635fc1173cc5820336ce97be6188d0de9f5 #v46.0.2 + uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c #v46.0.5 with: files_yaml: | ci: diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 955aafa9f..870e06e25 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -26,14 +26,14 @@ jobs: - name: Evaluate | Check common file types for changes id: core-changed-files - uses: tj-actions/changed-files@26a38635fc1173cc5820336ce97be6188d0de9f5 #v46.0.2 + uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c #v46.0.5 with: base_sha: ${{ github.event.push.before }} files_yaml_from_source_file: .github/changed-files-spec.yml - name: Evaluate | Check specific file types for changes id: ci-changed-files - uses: tj-actions/changed-files@26a38635fc1173cc5820336ce97be6188d0de9f5 #v46.0.2 + uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c #v46.0.5 with: base_sha: ${{ github.event.push.before }} files_yaml: | From ef7d4ff2e2e9452a21c016bee9632db8b37d35e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Apr 2025 18:49:31 +0000 Subject: [PATCH 072/160] ci(deps): bump `mikepenz/action-junit-report@5.5.0` to `v5.5.1` --- .github/workflows/validate.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index df23da85a..91848725a 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -183,7 +183,7 @@ jobs: --junit-xml=tests/reports/pytest-results.xml - name: Report | Upload Test Results - uses: mikepenz/action-junit-report@97744eca465b8df9e6e33271cb155003f85327f1 # v5.5.0 + uses: mikepenz/action-junit-report@cf701569b05ccdd861a76b8607a66d76f6fd4857 # v5.5.1 if: ${{ always() && steps.tests.outcome != 'skipped' }} with: report_paths: ./tests/reports/*.xml @@ -271,7 +271,7 @@ jobs: retention-days: 1 - name: Report | Upload Test Results - uses: mikepenz/action-junit-report@97744eca465b8df9e6e33271cb155003f85327f1 # v5.5.0 + uses: mikepenz/action-junit-report@cf701569b05ccdd861a76b8607a66d76f6fd4857 # v5.5.1 if: ${{ always() && steps.tests.outcome != 'skipped' }} with: report_paths: ./tests/reports/*.xml @@ -366,7 +366,7 @@ jobs: retention-days: 1 - name: Report | Upload Test Results - uses: mikepenz/action-junit-report@97744eca465b8df9e6e33271cb155003f85327f1 # v5.5.0 + uses: mikepenz/action-junit-report@cf701569b05ccdd861a76b8607a66d76f6fd4857 # v5.5.1 if: ${{ always() && steps.tests.outcome != 'skipped' }} with: report_paths: ./tests/reports/*.xml From f7ef0e41308342ddbf68112ed6133b95707bcfa1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 4 May 2025 20:25:10 -0700 Subject: [PATCH 073/160] build(deps-build): expand setuptools dependency to include `v79` & `v80` (#1237) Co-authored-by: codejedi365 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 59b7613d6..49e05922e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ # Ref: https://packaging.python.org/en/latest/specifications/declaring-project-metadata/ # and https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html [build-system] -requires = ["setuptools >= 75.3.0, < 79.0.0", "wheel ~= 0.42"] +requires = ["setuptools >= 75.3.0, < 81.0.0", "wheel ~= 0.42"] build-backend = "setuptools.build_meta" [project] From 71ab2cff88ae9f0ad9dbdcb77bff448aee36cfb0 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 4 May 2025 22:05:27 -0600 Subject: [PATCH 074/160] refactor: pull project version variable from package metadata (#1240) * chore(project-config): remove the source code version stamp during release --- pyproject.toml | 1 - src/semantic_release/__init__.py | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 49e05922e..7379b0af3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -412,7 +412,6 @@ build_command = """ python -m build . """ major_on_zero = true -version_variables = ["src/semantic_release/__init__.py:__version__"] version_toml = ["pyproject.toml:project.version"] [tool.semantic_release.changelog] diff --git a/src/semantic_release/__init__.py b/src/semantic_release/__init__.py index fa54ef662..25deef686 100644 --- a/src/semantic_release/__init__.py +++ b/src/semantic_release/__init__.py @@ -2,6 +2,8 @@ from __future__ import annotations +import importlib.metadata + from semantic_release.commit_parser import ( CommitParser, ParsedCommit, @@ -24,7 +26,7 @@ tags_and_versions, ) -__version__ = "9.21.0" +__version__ = importlib.metadata.version(f"python_{__package__}".replace("_", "-")) __all__ = [ "CommitParser", From f61f8a38a1a3f44a7a56cf9dcb7dde748f90ca1e Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 4 May 2025 22:20:02 -0600 Subject: [PATCH 075/160] fix(changelog-filters): fixes url resolution when prefix & path share letters (#1239) The change adjusts the url normalization computation to remove any path prefix and only the path prefix even when letters are shared with the rest of the url path. Resolves: #1204 * test(hvcs-bitbucket): add validation for self hosted server commit url --- src/semantic_release/hvcs/remote_hvcs_base.py | 18 +++++++++++---- .../semantic_release/hvcs/test_bitbucket.py | 23 +++++++++++++++++++ .../unit/semantic_release/hvcs/test_gitea.py | 23 +++++++++++++++++++ .../unit/semantic_release/hvcs/test_github.py | 23 +++++++++++++++++++ .../unit/semantic_release/hvcs/test_gitlab.py | 23 +++++++++++++++++++ 5 files changed, 106 insertions(+), 4 deletions(-) diff --git a/src/semantic_release/hvcs/remote_hvcs_base.py b/src/semantic_release/hvcs/remote_hvcs_base.py index e7cd93ab3..d26b01881 100644 --- a/src/semantic_release/hvcs/remote_hvcs_base.py +++ b/src/semantic_release/hvcs/remote_hvcs_base.py @@ -95,10 +95,15 @@ def create_server_url( query: str | None = None, fragment: str | None = None, ) -> str: - # Ensure any path prefix is transfered but not doubled up on the derived url + # Ensure any path prefix is transferred but not doubled up on the derived url + normalized_path = ( + f"{self.hvcs_domain.path}/{path}" + if self.hvcs_domain.path and not path.startswith(self.hvcs_domain.path) + else path + ) return self._derive_url( self.hvcs_domain, - path=f"{self.hvcs_domain.path or ''}/{path.lstrip(self.hvcs_domain.path)}", + path=normalized_path, auth=auth, query=query, fragment=fragment, @@ -123,10 +128,15 @@ def create_api_url( query: str | None = None, fragment: str | None = None, ) -> str: - # Ensure any api path prefix is transfered but not doubled up on the derived api url + # Ensure any api path prefix is transferred but not doubled up on the derived api url + normalized_endpoint = ( + f"{self.api_url.path}/{endpoint}" + if self.api_url.path and not endpoint.startswith(self.api_url.path) + else endpoint + ) return self._derive_url( self.api_url, - path=f"{self.api_url.path or ''}/{endpoint.lstrip(self.api_url.path)}", + path=normalized_endpoint, auth=auth, query=query, fragment=fragment, diff --git a/tests/unit/semantic_release/hvcs/test_bitbucket.py b/tests/unit/semantic_release/hvcs/test_bitbucket.py index 85f1d7e46..16d77fb87 100644 --- a/tests/unit/semantic_release/hvcs/test_bitbucket.py +++ b/tests/unit/semantic_release/hvcs/test_bitbucket.py @@ -301,6 +301,29 @@ def test_commit_hash_url(default_bitbucket_client: Bitbucket): assert expected_url == default_bitbucket_client.commit_hash_url(sha) +def test_commit_hash_url_w_custom_server(): + """ + Test the commit hash URL generation for a self-hosted Bitbucket server with prefix. + + ref: https://github.com/python-semantic-release/python-semantic-release/issues/1204 + """ + sha = "244f7e11bcb1e1ce097db61594056bc2a32189a0" + expected_url = "{server}/{owner}/{repo}/commits/{sha}".format( + server=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo", + owner="foo", + repo=EXAMPLE_REPO_NAME, + sha=sha, + ) + + with mock.patch.dict(os.environ, {}, clear=True): + actual_url = Bitbucket( + remote_url=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo/foo/{EXAMPLE_REPO_NAME}.git", + hvcs_domain=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo", + ).commit_hash_url(sha) + + assert expected_url == actual_url + + @pytest.mark.parametrize("pr_number", (666, "666", "#666")) def test_pull_request_url(default_bitbucket_client: Bitbucket, pr_number: int | str): expected_url = "{server}/{owner}/{repo}/pull-requests/{pr_number}".format( diff --git a/tests/unit/semantic_release/hvcs/test_gitea.py b/tests/unit/semantic_release/hvcs/test_gitea.py index 710b01b08..d98be8e96 100644 --- a/tests/unit/semantic_release/hvcs/test_gitea.py +++ b/tests/unit/semantic_release/hvcs/test_gitea.py @@ -203,6 +203,29 @@ def test_commit_hash_url(default_gitea_client: Gitea): assert expected_url == default_gitea_client.commit_hash_url(sha) +def test_commit_hash_url_w_custom_server(): + """ + Test the commit hash URL generation for a self-hosted Bitbucket server with prefix. + + ref: https://github.com/python-semantic-release/python-semantic-release/issues/1204 + """ + sha = "244f7e11bcb1e1ce097db61594056bc2a32189a0" + expected_url = "{server}/{owner}/{repo}/commit/{sha}".format( + server=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo", + owner="foo", + repo=EXAMPLE_REPO_NAME, + sha=sha, + ) + + with mock.patch.dict(os.environ, {}, clear=True): + actual_url = Gitea( + remote_url=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo/foo/{EXAMPLE_REPO_NAME}.git", + hvcs_domain=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo", + ).commit_hash_url(sha) + + assert expected_url == actual_url + + @pytest.mark.parametrize("issue_number", (666, "666", "#666")) def test_issue_url(default_gitea_client: Gitea, issue_number: int | str): expected_url = "{server}/{owner}/{repo}/issues/{issue_number}".format( diff --git a/tests/unit/semantic_release/hvcs/test_github.py b/tests/unit/semantic_release/hvcs/test_github.py index f52482ebf..e7f69a5ea 100644 --- a/tests/unit/semantic_release/hvcs/test_github.py +++ b/tests/unit/semantic_release/hvcs/test_github.py @@ -375,6 +375,29 @@ def test_commit_hash_url(default_gh_client: Github): assert expected_url == default_gh_client.commit_hash_url(sha) +def test_commit_hash_url_w_custom_server(): + """ + Test the commit hash URL generation for a self-hosted Bitbucket server with prefix. + + ref: https://github.com/python-semantic-release/python-semantic-release/issues/1204 + """ + sha = "244f7e11bcb1e1ce097db61594056bc2a32189a0" + expected_url = "{server}/{owner}/{repo}/commit/{sha}".format( + server=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo", + owner="foo", + repo=EXAMPLE_REPO_NAME, + sha=sha, + ) + + with mock.patch.dict(os.environ, {}, clear=True): + actual_url = Github( + remote_url=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo/foo/{EXAMPLE_REPO_NAME}.git", + hvcs_domain=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo", + ).commit_hash_url(sha) + + assert expected_url == actual_url + + @pytest.mark.parametrize("issue_number", (666, "666", "#666")) def test_issue_url(default_gh_client: Github, issue_number: str | int): expected_url = "{server}/{owner}/{repo}/issues/{issue_num}".format( diff --git a/tests/unit/semantic_release/hvcs/test_gitlab.py b/tests/unit/semantic_release/hvcs/test_gitlab.py index c4a0979fe..3011de8bb 100644 --- a/tests/unit/semantic_release/hvcs/test_gitlab.py +++ b/tests/unit/semantic_release/hvcs/test_gitlab.py @@ -260,6 +260,29 @@ def test_commit_hash_url(default_gl_client: Gitlab): assert expected_url == default_gl_client.commit_hash_url(REF) +def test_commit_hash_url_w_custom_server(): + """ + Test the commit hash URL generation for a self-hosted Bitbucket server with prefix. + + ref: https://github.com/python-semantic-release/python-semantic-release/issues/1204 + """ + sha = "244f7e11bcb1e1ce097db61594056bc2a32189a0" + expected_url = "{server}/{owner}/{repo}/-/commit/{sha}".format( + server=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo", + owner="foo", + repo=EXAMPLE_REPO_NAME, + sha=sha, + ) + + with mock.patch.dict(os.environ, {}, clear=True): + actual_url = Gitlab( + remote_url=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo/foo/{EXAMPLE_REPO_NAME}.git", + hvcs_domain=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo", + ).commit_hash_url(sha) + + assert expected_url == actual_url + + @pytest.mark.parametrize("issue_number", (666, "666", "#666")) def test_issue_url(default_gl_client: Gitlab, issue_number: int | str): expected_url = "{server}/{owner}/{repo}/-/issues/{issue_num}".format( From 0dc72ac9058a62054a45f6344c83a423d7f906a8 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Mon, 5 May 2025 04:28:43 +0000 Subject: [PATCH 076/160] 9.21.1 Automatically generated by python-semantic-release --- CHANGELOG.rst | 35 ++++++++++++++++++++++ docs/automatic-releases/github-actions.rst | 18 +++++------ pyproject.toml | 2 +- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0e07247dc..e802155f2 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,41 @@ CHANGELOG ========= +.. _changelog-v9.21.1: + +v9.21.1 (2025-05-05) +==================== + +🪲 Bug Fixes +------------ + +* **changelog-filters**: Fixes url resolution when prefix & path share letters, closes `#1204`_ + (`PR#1239`_, `f61f8a3`_) + +📖 Documentation +---------------- + +* **github-actions**: Expound on monorepo example to include publishing actions (`PR#1229`_, + `550e85f`_) + +⚙️ Build System +---------------- + +* **deps**: Bump ``rich`` dependency from ``13.0`` to ``14.0`` (`PR#1224`_, `691536e`_) + +* **deps**: Expand ``python-gitlab`` dependency to include ``v5.0.0`` (`PR#1228`_, `a0cd1be`_) + +.. _#1204: https://github.com/python-semantic-release/python-semantic-release/issues/1204 +.. _550e85f: https://github.com/python-semantic-release/python-semantic-release/commit/550e85f5ec2695d5aa680014127846d58c680e31 +.. _691536e: https://github.com/python-semantic-release/python-semantic-release/commit/691536e98f311d0fc6d29a72c41ce5a65f1f4b6c +.. _a0cd1be: https://github.com/python-semantic-release/python-semantic-release/commit/a0cd1be4e3aa283cbdc544785e5f895c8391dfb8 +.. _f61f8a3: https://github.com/python-semantic-release/python-semantic-release/commit/f61f8a38a1a3f44a7a56cf9dcb7dde748f90ca1e +.. _PR#1224: https://github.com/python-semantic-release/python-semantic-release/pull/1224 +.. _PR#1228: https://github.com/python-semantic-release/python-semantic-release/pull/1228 +.. _PR#1229: https://github.com/python-semantic-release/python-semantic-release/pull/1229 +.. _PR#1239: https://github.com/python-semantic-release/python-semantic-release/pull/1239 + + .. _changelog-v9.21.0: v9.21.0 (2025-02-23) diff --git a/docs/automatic-releases/github-actions.rst b/docs/automatic-releases/github-actions.rst index e8d0ce636..219e0273c 100644 --- a/docs/automatic-releases/github-actions.rst +++ b/docs/automatic-releases/github-actions.rst @@ -337,7 +337,7 @@ before the :ref:`version ` subcommand. .. code:: yaml - - uses: python-semantic-release/python-semantic-release@v9.21.0 + - uses: python-semantic-release/python-semantic-release@v9.21.1 with: root_options: "-vv --noop" @@ -576,7 +576,7 @@ before the :ref:`publish ` subcommand. .. code:: yaml - - uses: python-semantic-release/publish-action@v9.21.0 + - uses: python-semantic-release/publish-action@v9.21.1 with: root_options: "-vv --noop" @@ -728,14 +728,14 @@ to the GitHub Release Assets as well. - name: Action | Semantic Version Release id: release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.21.0 + uses: python-semantic-release/python-semantic-release@v9.21.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} git_committer_name: "github-actions" git_committer_email: "actions@users.noreply.github.com" - name: Publish | Upload to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.21.0 + uses: python-semantic-release/publish-action@v9.21.1 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -794,7 +794,7 @@ The equivalent GitHub Action configuration would be: - name: Action | Semantic Version Release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.21.0 + uses: python-semantic-release/python-semantic-release@v9.21.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} force: patch @@ -844,14 +844,14 @@ Publish Action. - name: Release submodule 1 id: release-submod-1 - uses: python-semantic-release/python-semantic-release@v9.21.0 + uses: python-semantic-release/python-semantic-release@v9.21.1 with: directory: ${{ env.SUBMODULE_1_DIR }} github_token: ${{ secrets.GITHUB_TOKEN }} - name: Release submodule 2 id: release-submod-2 - uses: python-semantic-release/python-semantic-release@v9.21.0 + uses: python-semantic-release/python-semantic-release@v9.21.1 with: directory: ${{ env.SUBMODULE_2_DIR }} github_token: ${{ secrets.GITHUB_TOKEN }} @@ -863,7 +863,7 @@ Publish Action. # ------------------------------------------------------------------- # - name: Publish | Upload package 1 to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.21.0 + uses: python-semantic-release/publish-action@v9.21.1 if: steps.release-submod-1.outputs.released == 'true' with: directory: ${{ env.SUBMODULE_1_DIR }} @@ -871,7 +871,7 @@ Publish Action. tag: ${{ steps.release-submod-1.outputs.tag }} - name: Publish | Upload package 2 to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.21.0 + uses: python-semantic-release/publish-action@v9.21.1 if: steps.release-submod-2.outputs.released == 'true' with: directory: ${{ env.SUBMODULE_2_DIR }} diff --git a/pyproject.toml b/pyproject.toml index 7379b0af3..074ab8c4b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.21.0" +version = "9.21.1" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } From e39ceeb7955f2439a5e6ffe45a10feb92e09e295 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 4 May 2025 23:19:53 -0600 Subject: [PATCH 077/160] ci(deps): bump `python-semantic-release@v9.21.0` action to `v9.21.1` --- .github/workflows/cicd.yml | 2 +- .github/workflows/validate.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 870e06e25..cc5c2c4f2 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -139,7 +139,7 @@ jobs: - name: Release | Python Semantic Release id: release - uses: python-semantic-release/python-semantic-release@v9.21.0 + uses: python-semantic-release/python-semantic-release@v9.21.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} root_options: "-v" diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 91848725a..08cbb4c91 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -102,7 +102,7 @@ jobs: - name: Build | Build next version artifacts id: version - uses: python-semantic-release/python-semantic-release@v9.21.0 + uses: python-semantic-release/python-semantic-release@v9.21.1 with: github_token: "" root_options: "-v" From 31abbc7e4555949a93ac1bd428c8f998277b37c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 05:15:17 +0000 Subject: [PATCH 078/160] ci(deps): bump `python-semantic-release/publish-action@v9.21.0` action to `v9.21.1` --- .github/workflows/cicd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index cc5c2c4f2..13dc13bda 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -146,7 +146,7 @@ jobs: build: false - name: Release | Add distribution artifacts to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.21.0 + uses: python-semantic-release/publish-action@v9.21.1 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} From 4aa6a6edbff75889e09f32f7cba52cb90c9fb626 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Thu, 15 May 2025 22:50:49 -0600 Subject: [PATCH 079/160] build(deps): prevent update to `click@8.2.0` (#1245) click 8.2 has a breaking change in the test suite where it removes a parameter from the cli runner. There is additional deprecation warnings included as well so for now since it will break our tests limit this to 8.1.0 only. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 074ab8c4b..ca6945374 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ classifiers = [ readme = "README.rst" authors = [{ name = "Rolf Erik Lekang", email = "me@rolflekang.com" }] dependencies = [ - "click ~= 8.0", + "click ~= 8.1.0", "click-option-group ~= 0.5", "gitpython ~= 3.0", "requests ~= 2.25", From 080e4bcb14048a2dd10445546a7ee3159b3ab85c Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Thu, 15 May 2025 23:24:02 -0600 Subject: [PATCH 080/160] feat(cmd-version): enable `version_variables` version stamp of vars with double-equals (#1244) * test(stamp-version): add test case for stamping a `requirements.txt` file with double-equals * docs(configuration): update `version_variables` section to include double-equals operand support --- docs/configuration.rst | 6 +++++- src/semantic_release/version/declarations/pattern.py | 4 ++-- .../version/declarations/test_pattern_declaration.py | 9 +++++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index 78e20d183..aa904dc9d 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -1347,7 +1347,8 @@ The regular expression generated from the ``version_variables`` definition will: 2. The variable name defined by ``variable`` and the version must be separated by an operand symbol (``=``, ``:``, ``:=``, or ``@``). Whitespace is optional around - the symbol. + the symbol. As of $NEW_VERSION, a double-equals (``==``) operator is also supported + as a valid operand symbol. 3. The value of the variable must match a `SemVer`_ regular expression and can be enclosed by single (``'``) or double (``"``) quotation marks but they must match. However, @@ -1400,6 +1401,9 @@ will be matched and replaced by the new version: # Custom Tag Format with tag_format set (monorepos) __release__ = "module-v1.2.3" + # requirements.txt + my-package == 1.2.3 + .. important:: The Regular Expression expects a version value to exist in the file to be replaced. It cannot be an empty string or a non-semver compliant string. If this is the very diff --git a/src/semantic_release/version/declarations/pattern.py b/src/semantic_release/version/declarations/pattern.py index 55873ce0a..e96234117 100644 --- a/src/semantic_release/version/declarations/pattern.py +++ b/src/semantic_release/version/declarations/pattern.py @@ -230,9 +230,9 @@ def from_string_definition( # Supports optional matching quotations around variable name # Negative lookbehind to ensure we don't match part of a variable name f"""(?x)(?P['"])?(?['"])?{value_replace_pattern_str}(?P=quote2)?""", ], diff --git a/tests/unit/semantic_release/version/declarations/test_pattern_declaration.py b/tests/unit/semantic_release/version/declarations/test_pattern_declaration.py index fd7cb7dad..8280e95bb 100644 --- a/tests/unit/semantic_release/version/declarations/test_pattern_declaration.py +++ b/tests/unit/semantic_release/version/declarations/test_pattern_declaration.py @@ -165,6 +165,15 @@ def test_pattern_declaration_is_version_replacer(): """if version := '1.0.0': """, f"""if version := '{next_version}': """, ), + ( + "Explicit number format for requirements.txt file with double equals", + f"{test_file}:my-package:{VersionStampType.NUMBER_FORMAT.value}", + # irrelevant for this case + lazy_fixture(default_tag_format_str.__name__), + # Uses double equals separator + """my-package == 1.0.0""", + f"""my-package == {next_version}""", + ), ( "Using default number format for multi-line & quoted json", f"{test_file}:version:{VersionStampType.NUMBER_FORMAT.value}", From fb3da27650ff15bcdb3b7badc919bd8a9a73238d Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 12 May 2025 22:50:05 -0600 Subject: [PATCH 081/160] fix(github-action)!: resolve command injection vulnerability in action script Prevents the malicious execution of arbitrary code when a command injection is defined in the action yaml as part of the action parameter specification. Low impact exploitation if proper least privilege is implemented and pull request code is reviewed before merging to a release branch. BREAKING CHANGE: The `root_options` action input parameter has been removed because it created a command injection vulernability for arbitrary code to execute within the container context of the GitHub action if a command injection code was provided as part of the `root_options` parameter string. To eliminate the vulnerability, each relevant option that can be provided to `semantic-release` has been individually added as its own parameter and will be processed individually to prevent command injection. Please review our `Github Actions Configuration`__ page on the Python Semantic Release Documentation website to review the newly available configuration options that replace the `root_options` parameter. __ https://python-semantic-release.readthedocs.io/en/stable/automatic-releases/github-actions.html --- action.sh | 61 +++++++++++++++++++++++++++++++++++++++++++++++++----- action.yml | 49 +++++++++++++++++++++++++++---------------- 2 files changed, 87 insertions(+), 23 deletions(-) diff --git a/action.sh b/action.sh index 48ab72c35..e70e39d4a 100644 --- a/action.sh +++ b/action.sh @@ -2,6 +2,12 @@ set -e +explicit_run_cmd() { + local cmd="$*" + printf '%s\n' "$> $cmd" + eval "$cmd" +} + # Convert "true"/"false" into command line args, returns "" if not defined eval_boolean_action_input() { local -r input_name="$1" @@ -13,11 +19,11 @@ eval_boolean_action_input() { local -r if_false="$1" if [ -z "$flag_value" ]; then - echo "" + printf "" elif [ "$flag_value" = "true" ]; then - echo "$if_true" + printf '%s\n' "$if_true" elif [ "$flag_value" = "false" ]; then - echo "$if_false" + printf '%s\n' "$if_false" else printf 'Error: Invalid value for input %s: %s is not "true" or "false\n"' \ "$input_name" "$flag_value" >&2 @@ -25,8 +31,53 @@ eval_boolean_action_input() { fi } +# Convert string input into command line args, returns "" if undefined +eval_string_input() { + local -r input_name="$1" + shift + local -r if_defined="$1" + shift + local value + value="$(printf '%s' "$1" | tr -d ' ')" + + if [ -z "$value" ]; then + printf "" + return 0 + fi + + printf '%s' "${if_defined/\%s/$value}" +} + # Convert inputs to command line arguments -export ARGS=() +ROOT_OPTIONS=() + +if ! printf '%s\n' "$INPUT_VERBOSITY" | grep -qE '^[0-9]+$'; then + printf "Error: Input 'verbosity' must be a positive integer\n" >&2 + exit 1 +fi + +VERBOSITY_OPTIONS="" +for ((i = 0; i < INPUT_VERBOSITY; i++)); do + [ "$i" -eq 0 ] && VERBOSITY_OPTIONS="-" + VERBOSITY_OPTIONS+="v" +done + +ROOT_OPTIONS+=("$VERBOSITY_OPTIONS") + +if [ -n "$INPUT_CONFIG_FILE" ]; then + # Check if the file exists + if [ ! -f "$INPUT_CONFIG_FILE" ]; then + printf "Error: Input 'config_file' does not exist: %s\n" "$INPUT_CONFIG_FILE" >&2 + exit 1 + fi + + ROOT_OPTIONS+=("$(eval_string_input "config_file" "--config %s" "$INPUT_CONFIG_FILE")") || exit 1 +fi + +ROOT_OPTIONS+=("$(eval_boolean_action_input "strict" "$INPUT_STRICT" "--strict" "")") || exit 1 +ROOT_OPTIONS+=("$(eval_boolean_action_input "no_operation_mode" "$INPUT_NO_OPERATION_MODE" "--noop" "")") || exit 1 + +ARGS=() # v10 Breaking change as prerelease should be as_prerelease to match ARGS+=("$(eval_boolean_action_input "prerelease" "$INPUT_PRERELEASE" "--as-prerelease" "")") || exit 1 ARGS+=("$(eval_boolean_action_input "commit" "$INPUT_COMMIT" "--commit" "--no-commit")") || exit 1 @@ -111,4 +162,4 @@ fi export GH_TOKEN="${INPUT_GITHUB_TOKEN}" # Run Semantic Release (explicitly use the GitHub action version) -eval "/psr/.venv/bin/semantic-release $INPUT_ROOT_OPTIONS version ${ARGS[*]}" +explicit_run_cmd "/psr/.venv/bin/semantic-release ${ROOT_OPTIONS[*]} version ${ARGS[*]}" diff --git a/action.yml b/action.yml index 992b5d05d..7b9e3c654 100644 --- a/action.yml +++ b/action.yml @@ -7,11 +7,14 @@ branding: color: orange inputs: - root_options: - default: "-v" + + config_file: + default: "" required: false description: | - Additional options for the main command. Example: -vv --noop + Path to a custom semantic-release configuration file. By default, an empty + string will look for a pyproject.toml file in the current directory. This is the same + as passing the `-c` or `--config` parameter to semantic-release. directory: default: "." @@ -19,64 +22,78 @@ inputs: description: Sub-directory to cd into before running semantic-release github_token: - type: string required: true description: GitHub token used to push release notes and new commits/tags git_committer_name: - type: string required: false description: The human name for the “committer” field git_committer_email: - type: string required: false description: The email address for the “committer” field + no_operation_mode: + default: "false" + required: false + description: | + If set to true, the github action will pass the `--noop` parameter to + semantic-release. This will cause semantic-release to run in "no operation" + mode. See the documentation for more information on this parameter. Note that, + this parameter will not affect the output of the action, so you will still get + the version determination decision as output values. + ssh_public_signing_key: - type: string required: false description: The ssh public key used to sign commits ssh_private_signing_key: - type: string required: false description: The ssh private key used to sign commits + strict: + default: "false" + required: false + description: | + If set to true, the github action will pass the `--strict` parameter to + semantic-release. See the documentation for more information on this parameter. + + verbosity: + default: "1" + required: false + description: | + Set the verbosity level of the output as the number of -v's to pass to + semantic-release. 0 is no extra output, 1 is info level output, 2 is + debug output, and 3 is silly debug level of output. + # `semantic-release version` command line options prerelease: - type: string required: false description: | Force the next version to be a prerelease. Set to "true" or "false". prerelease_token: - type: string required: false description: "Force the next version to use this prerelease token, if it is a prerelease" force: - type: string required: false description: | Force the next version to be a major release. Must be set to one of "prerelease", "patch", "minor", or "major". commit: - type: string required: false description: Whether or not to commit changes locally. Defaults are handled by python-semantic-release internal version command. tag: - type: string required: false description: | Whether or not to make a local version tag. Defaults are handled by python-semantic-release internal version command. push: - type: string required: false description: | Whether or not to push local commits to the Git repository. See @@ -84,26 +101,22 @@ inputs: for how the default is determined between push, tag, & commit. changelog: - type: string required: false description: | Whether or not to update the changelog. vcs_release: - type: string required: false description: | Whether or not to create a release in the remote VCS, if supported build: - type: string required: false description: | Whether or not to run the build_command for the project. Defaults are handled by python-semantic-release internal version command. build_metadata: - type: string required: false description: | Build metadata to append to the new version From a08289693085153effdafe3c6ff235a1777bb1fa Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 13 May 2025 00:38:41 -0600 Subject: [PATCH 082/160] docs(github-actions): update PSR action parameter documenation --- docs/automatic-releases/github-actions.rst | 78 ++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/docs/automatic-releases/github-actions.rst b/docs/automatic-releases/github-actions.rst index 219e0273c..4b1b7ca17 100644 --- a/docs/automatic-releases/github-actions.rst +++ b/docs/automatic-releases/github-actions.rst @@ -163,6 +163,25 @@ to the index during the build command. This option is equivalent to adding eithe ---- +.. _gh_actions-psr-inputs-config_file: + +``config_file`` +""""""""""""""" + +Path to a custom semantic-release configuration file. By default, an empty +string will look for to the ``pyproject.toml`` file in the current directory. +This is the same as passing the ``-c`` or ``--config`` parameter to semantic-release. + +**Required:** ``false`` + +**Default:** ``""`` + +.. seealso:: + + - :ref:`cmd-main-option-config` for the :ref:`semantic-release ` command + +---- + .. _gh_actions-psr-inputs-directory: ``directory`` @@ -248,6 +267,27 @@ The token should have the following `permissions`_: ---- +.. _gh_actions-psr-inputs-noop: + +``no_operation_mode`` +""""""""""""""""""""" + +If set to true, the github action will pass the ``--noop`` parameter to +semantic-release. This will cause semantic-release to run in "no operation" +mode. + +This is useful for testing the action without making any permanent changes to the repository. + +**Required:** ``false`` + +**Default:** ``false`` + +.. seealso:: + + - :ref:`cmd-main-option-noop` option for the :ref:`semantic-release ` command + +---- + .. _gh_actions-psr-inputs-prerelease: ``prerelease`` @@ -330,6 +370,11 @@ to the remote repository. This option is equivalent to adding either ``--push`` ``root_options`` """""""""""""""" +.. important:: + This option has been removed in version $NEW_VERSION and newer because of a + command injection vulnerability. Please update as to $NEW_RELEASE_TAG as soon + as possible. + Additional options for the main ``semantic-release`` command, which will come before the :ref:`version ` subcommand. @@ -377,6 +422,20 @@ The private key used to sign a commit and tag. ---- +.. _gh_actions-psr-inputs-strict: + +``strict`` +"""""""""" + +If set to true, the github action will pass the `--strict` parameter to +``semantic-release``. + +.. seealso:: + + - :ref:`cmd-main-option-strict` option for the :ref:`semantic-release ` command + +---- + .. _gh_actions-psr-inputs-tag: ``tag`` @@ -424,6 +483,25 @@ equivalent to adding either ``--vcs-release`` (on ``true``) or ``--no-vcs-releas ---- +.. _gh_actions-psr-inputs-verbosity: + +``verbosity`` +""""""""""""" + +Set the verbosity level of the output as the number of ``-v``'s to pass to +``semantic-release``. 0 is no extra output, 1 is info level output, 2 is debug output, and +3 is a silly amount of debug output. + +**Required:** ``false`` + +**Default:** ``"1"`` + +.. seealso:: + + - :ref:`cmd-main-option-verbosity` for the :ref:`semantic-release ` command + +---- + .. _gh_actions-psr-outputs: Outputs From c4d45ec46dfa81f645c25ea18ffffe9635922603 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 13 May 2025 00:40:08 -0600 Subject: [PATCH 083/160] docs(github-actions): update `python-semantic-release/publish-action` parameter notes --- docs/automatic-releases/github-actions.rst | 64 ++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/docs/automatic-releases/github-actions.rst b/docs/automatic-releases/github-actions.rst index 4b1b7ca17..19476e861 100644 --- a/docs/automatic-releases/github-actions.rst +++ b/docs/automatic-releases/github-actions.rst @@ -609,6 +609,25 @@ supported input and its purpose. ---- +.. _gh_actions-publish-inputs-config_file: + +``config_file`` +""""""""""""""" + +Path to a custom semantic-release configuration file. By default, an empty +string will look for to the ``pyproject.toml`` file in the current directory. +This is the same as passing the ``-c`` or ``--config`` parameter to semantic-release. + +**Required:** ``false`` + +**Default:** ``""`` + +.. seealso:: + + - :ref:`cmd-main-option-config` for the :ref:`semantic-release ` command + +---- + .. _gh_actions-publish-inputs-directory: ``directory`` @@ -642,11 +661,37 @@ The token should have the following `permissions`_: ---- +.. _gh_actions-publish-inputs-noop: + +``no_operation_mode`` +""""""""""""""""""""" + +If set to true, the github action will pass the ``--noop`` parameter to +semantic-release. This will cause semantic-release to run in "no operation" +mode. + +This is useful for testing the action without actually publishing anything. + +**Required:** ``false`` + +**Default:** ``false`` + +.. seealso:: + + - :ref:`cmd-main-option-noop` option for the :ref:`semantic-release ` command + +---- + .. _gh_actions-publish-inputs-root_options: ``root_options`` """""""""""""""" +.. important:: + This option has been removed in version $NEW_VERSION and newer because of a + command injection vulnerability. Please update as to $NEW_RELEASE_TAG as soon + as possible. + Additional options for the main ``semantic-release`` command, which will come before the :ref:`publish ` subcommand. @@ -698,6 +743,25 @@ Python Semantic Release will automatically determine the latest release if no ---- +.. _gh_actions-publish-inputs-verbosity: + +``verbosity`` +""""""""""""" + +Set the verbosity level of the output as the number of ``-v``'s to pass to +``semantic-release``. 0 is no extra output, 1 is info level output, 2 is debug output, and +3 is a silly amount of debug output. + +**Required:** ``false`` + +**Default:** ``"1"`` + +.. seealso:: + + - :ref:`cmd-main-option-verbosity` for the :ref:`semantic-release ` command + +---- + .. _gh_actions-publish-outputs: Outputs From 67b2ae0050cce540a4126fe280cca6dc4bcf5d3f Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 13 May 2025 21:22:56 -0600 Subject: [PATCH 084/160] docs(github-actions): change recommended workflow to separate release from deploy --- docs/automatic-releases/github-actions.rst | 51 ++++++++++++++++++++-- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/docs/automatic-releases/github-actions.rst b/docs/automatic-releases/github-actions.rst index 19476e861..621a13067 100644 --- a/docs/automatic-releases/github-actions.rst +++ b/docs/automatic-releases/github-actions.rst @@ -800,6 +800,10 @@ to the GitHub Release Assets as well. branches: - main + # default: least privileged permissions across all jobs + permissions: + contents: read + jobs: release: runs-on: ubuntu-latest @@ -808,7 +812,6 @@ to the GitHub Release Assets as well. cancel-in-progress: false permissions: - id-token: write contents: write steps: @@ -883,9 +886,49 @@ to the GitHub Release Assets as well. github_token: ${{ secrets.GITHUB_TOKEN }} tag: ${{ steps.release.outputs.tag }} - - name: Publish | Upload package to PyPI - uses: pypa/gh-action-pypi-publish@SHA1_HASH # vX.X.X - if: steps.release.outputs.released == 'true' + - name: Upload | Distribution Artifacts + uses: actions/upload-artifact@v4 + with: + name: distribution-artifacts + path: dist + if-no-files-found: error + + deploy: + # 1. Separate out the deploy step from the publish step to run each step at + # the least amount of token privilege + # 2. Also, deployments can fail, and its better to have a separate job if you need to retry + # and it won't require reversing the release. + runs-on: ubuntu-latest + needs: release + if: ${{ needs.release.outputs.released == 'true' }} + + permissions: + contents: read + id-token: write + + steps: + - name: Setup | Download Build Artifacts + uses: actions/download-artifact@v4 + id: artifact-download + with: + name: distribution-artifacts + path: dist + + # ------------------------------------------------------------------- # + # Python Semantic Release is not responsible for publishing your # + # python artifacts to PyPI. Use the official PyPA publish action # + # instead. The following steps are an example but is not guaranteed # + # to work as the action is not maintained by the # + # python-semantic-release team. # + # ------------------------------------------------------------------- # + + # see https://docs.pypi.org/trusted-publishers/ + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@@SHA1_HASH # vX.X.X + with: + packages-dir: dist + print-hash: true + verbose: true .. important:: The `concurrency`_ directive is used on the job to prevent race conditions of more than From 47d1b74780a04bfb0bb0f31f0bdc19a094bc77e7 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 13 May 2025 22:08:03 -0600 Subject: [PATCH 085/160] refactor(github-actions): consolidate code & simplify github action build --- Dockerfile | 33 ---------------------- MANIFEST.in | 8 +++++- action.yml | 2 +- src/gh_action/.dockerignore | 6 ++++ src/gh_action/Dockerfile | 42 ++++++++++++++++++++++++++++ action.sh => src/gh_action/action.sh | 8 ++++-- src/gh_action/requirements.txt | 1 + 7 files changed, 63 insertions(+), 37 deletions(-) delete mode 100644 Dockerfile create mode 100644 src/gh_action/.dockerignore create mode 100644 src/gh_action/Dockerfile rename action.sh => src/gh_action/action.sh (94%) create mode 100644 src/gh_action/requirements.txt diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 489b41bee..000000000 --- a/Dockerfile +++ /dev/null @@ -1,33 +0,0 @@ -# This Dockerfile is only for GitHub Actions -FROM python:3.13-bookworm - -# Copy python-semantic-release source code into container -COPY . /psr - -RUN \ - # Install desired packages - apt update && apt install -y --no-install-recommends \ - # install git with git-lfs support - git git-lfs \ - # install python cmodule / binary module build utilities - python3-dev gcc make cmake cargo \ - # Configure global pip - && { \ - printf '%s\n' "[global]"; \ - printf '%s\n' "no-cache-dir = true"; \ - printf '%s\n' "disable-pip-version-check = true"; \ - } > /etc/pip.conf \ - # Create virtual environment for python-semantic-release - && python3 -m venv /psr/.venv \ - # Update core utilities in the virtual environment - && /psr/.venv/bin/pip install -U pip setuptools wheel \ - # Install psr & its dependencies from source into virtual environment - && /psr/.venv/bin/pip install /psr \ - # Cleanup - && apt clean -y - -ENV PSR_DOCKER_GITHUB_ACTION=true - -ENV PYTHONDONTWRITEBYTECODE=1 - -ENTRYPOINT ["/bin/bash", "-l", "/psr/action.sh"] diff --git a/MANIFEST.in b/MANIFEST.in index 9953cfd42..c6e688b9f 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,5 +2,11 @@ graft src/**/data/ # include docs & testing into sdist, ignored for wheel build -graft tests/ graft docs/ +prune docs/_build/ +graft tests/ +prune tests/gh_action/ + +# Remove any generated files +prune **/__pycache__/ +prune src/*.egg-info diff --git a/action.yml b/action.yml index 7b9e3c654..2cf7cf5ec 100644 --- a/action.yml +++ b/action.yml @@ -140,4 +140,4 @@ outputs: runs: using: docker - image: Dockerfile + image: src/gh_action/Dockerfile diff --git a/src/gh_action/.dockerignore b/src/gh_action/.dockerignore new file mode 100644 index 000000000..e9783640d --- /dev/null +++ b/src/gh_action/.dockerignore @@ -0,0 +1,6 @@ +# Default, ignore everything +* + +# Except +!requirements.txt +!action.sh diff --git a/src/gh_action/Dockerfile b/src/gh_action/Dockerfile new file mode 100644 index 000000000..138b95a29 --- /dev/null +++ b/src/gh_action/Dockerfile @@ -0,0 +1,42 @@ +# This Dockerfile is only for GitHub Actions +FROM python:3.13-bookworm +ARG WORK_DIR="/opt/psr" + +WORKDIR ${WORK_DIR} + +ENV PSR_DOCKER_GITHUB_ACTION=true \ + PYTHONDONTWRITEBYTECODE=1 \ + PSR_VENV_BIN="${WORK_DIR}/.venv/bin" + +# Copy action utilities into container +COPY . ./ + +RUN \ + # Install desired packages + apt update && apt install -y --no-install-recommends \ + # install git with git-lfs support + git git-lfs \ + # install python cmodule / binary module build utilities + python3-dev gcc make cmake cargo \ + # Configure global pip + && { \ + printf '%s\n' "[global]"; \ + printf '%s\n' "disable-pip-version-check = true"; \ + } > /etc/pip.conf \ + # Create virtual environment for python-semantic-release + && python3 -m venv "$(dirname "${PSR_VENV_BIN}")" \ + # Update core utilities in the virtual environment + && "${PSR_VENV_BIN}/pip" install --upgrade pip setuptools wheel \ + # Install psr & its dependencies from source into virtual environment + && "${PSR_VENV_BIN}/pip" install --pre -r requirements.txt \ + # Validate binary availability + && bash -c "${PSR_VENV_BIN}/semantic-release --help" \ + # make action script executable + && chmod +x "${WORK_DIR}/action.sh" \ + # Put action script in PATH + && ln -s "${WORK_DIR}/action.sh" /usr/local/bin/action-entrypoint \ + # Clean up + && apt clean && rm -rf /var/lib/apt/lists/* \ + && find /tmp -mindepth 1 -delete + +ENTRYPOINT ["/usr/local/bin/action-entrypoint"] diff --git a/action.sh b/src/gh_action/action.sh similarity index 94% rename from action.sh rename to src/gh_action/action.sh index e70e39d4a..cd862d9a9 100644 --- a/action.sh +++ b/src/gh_action/action.sh @@ -3,7 +3,8 @@ set -e explicit_run_cmd() { - local cmd="$*" + local cmd="" + cmd="$(printf '%s' "$*" | sed 's/^ *//g' | sed 's/ *$//g')" printf '%s\n' "$> $cmd" eval "$cmd" } @@ -161,5 +162,8 @@ fi # Copy inputs into correctly-named environment variables export GH_TOKEN="${INPUT_GITHUB_TOKEN}" +# normalize extra spaces into single spaces as you combine the arguments +CMD_ARGS="$(printf '%s' "${ROOT_OPTIONS[*]} version ${ARGS[*]}" | sed 's/ [ ]*/ /g' | sed 's/^ *//g')" + # Run Semantic Release (explicitly use the GitHub action version) -explicit_run_cmd "/psr/.venv/bin/semantic-release ${ROOT_OPTIONS[*]} version ${ARGS[*]}" +explicit_run_cmd "$PSR_VENV_BIN/semantic-release $CMD_ARGS" diff --git a/src/gh_action/requirements.txt b/src/gh_action/requirements.txt new file mode 100644 index 000000000..1aae03384 --- /dev/null +++ b/src/gh_action/requirements.txt @@ -0,0 +1 @@ +python-semantic-release == 9.21.1 From 8d2b874dd43b24cbe57251581a938d28bb7171fa Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 16 May 2025 23:21:37 -0600 Subject: [PATCH 086/160] test(github-action): add github Docker action test suite --- .../gh_action/example_project/pyproject.toml | 4 + .../gh_action/example_project/releaserc.toml | 2 + tests/gh_action/run.sh | 133 ++++++++++++++++++ tests/gh_action/suite/test_version.sh | 86 +++++++++++ tests/gh_action/suite/test_version_strict.sh | 48 +++++++ tests/gh_action/utils.sh | 67 +++++++++ 6 files changed, 340 insertions(+) create mode 100644 tests/gh_action/example_project/pyproject.toml create mode 100644 tests/gh_action/example_project/releaserc.toml create mode 100644 tests/gh_action/run.sh create mode 100644 tests/gh_action/suite/test_version.sh create mode 100644 tests/gh_action/suite/test_version_strict.sh create mode 100644 tests/gh_action/utils.sh diff --git a/tests/gh_action/example_project/pyproject.toml b/tests/gh_action/example_project/pyproject.toml new file mode 100644 index 000000000..97a158c8d --- /dev/null +++ b/tests/gh_action/example_project/pyproject.toml @@ -0,0 +1,4 @@ +[project] +name = "example" +version = "0.0.0" +description = "Example project" diff --git a/tests/gh_action/example_project/releaserc.toml b/tests/gh_action/example_project/releaserc.toml new file mode 100644 index 000000000..c5c259a34 --- /dev/null +++ b/tests/gh_action/example_project/releaserc.toml @@ -0,0 +1,2 @@ +[semantic-release] +commit_parser = "emoji" diff --git a/tests/gh_action/run.sh b/tests/gh_action/run.sh new file mode 100644 index 000000000..3d92e0d88 --- /dev/null +++ b/tests/gh_action/run.sh @@ -0,0 +1,133 @@ +#!/bin/bash + +set -eu + +if ! command -v realpath &>/dev/null; then + realpath() { + readlink -f "$1" + } +fi + +TEST_DIR="$(realpath "$(dirname "${BASH_SOURCE[0]}")")" +PROJ_DIR="$(realpath "$(dirname "$TEST_DIR")/..")" +EXAMPLE_PROJECT_BASE_DIR="${EXAMPLE_PROJECT_BASE_DIR:-"$TEST_DIR/example_project"}" + +if [ -z "${UTILS_LOADED:-}" ]; then + # shellcheck source=tests/utils.sh + source "$TEST_DIR/utils.sh" +fi + +create_example_project() { + local EXAMPLE_PROJECT_DIR="$1" + + log "Creating example project in: $EXAMPLE_PROJECT_DIR" + mkdir -vp "$(dirname "$EXAMPLE_PROJECT_DIR")" + cp -r "${EXAMPLE_PROJECT_BASE_DIR}" "$EXAMPLE_PROJECT_DIR" + + log "Constructing git history in repository" + pushd "$EXAMPLE_PROJECT_DIR" >/dev/null || return 1 + + # Initialize and configure git (remove any signature requirements) + git init + git config --local user.email "developer@users.noreply.github.com" + git config --local user.name "developer" + git config --local commit.gpgSign false + git config --local tag.gpgSign false + git remote add origin "https://github.com/python-semantic-release/example-project.git" + + # Create initial commit and tag + git add . + git commit -m "Initial commit" + + # set default branch to main + git branch -m main + + # Create the first release (with commit & tag) + cat <pyproject.toml +[project] +name = "example" +version = "1.0.0" +description = "Example project" +EOF + git commit -am '1.0.0' + git tag -a v1.0.0 -m "v1.0.0" + + popd >/dev/null || return 1 + log "Example project created successfully" +} + +# ------------------------------ +# TEST SUITE DRIVER +# ------------------------------ + +run_test_suite() { + local ALL_TEST_FNS + + # Dynamically import all test scripts + for test_script in "$TEST_DIR"/suite/test_*.sh; do + if [ -f "$test_script" ]; then + if ! source "$test_script"; then + error "Failed to load test script: $test_script" + fi + fi + done + + # Extract all test functions + tests_in_env="$(compgen -A function | grep "^test_")" + read -r -a ALL_TEST_FNS <<< "$(printf '%s' "$tests_in_env" | tr '\n' ' ')" + + log "" + log "************************" + log "* Running test suite *" + log "************************" + + # Incrementally run all test functions and flag if any fail + local test_index=1 + local test_failures=0 + for test_fn in "${ALL_TEST_FNS[@]}"; do + if command -v "$test_fn" &>/dev/null; then + if ! "$test_fn" "$test_index"; then + ((test_failures++)) + fi + fi + log "--------------------------------------------------------------------------------" + ((test_index++)) + done + + log "" + log "************************" + log "* Test Summary *" + log "************************" + log "" + log "Total tests executed: ${#ALL_TEST_FNS[@]}" + log "Successes: $((${#ALL_TEST_FNS[@]} - test_failures))" + log "Failures: $test_failures" + + if [ "$test_failures" -gt 0 ]; then + return 1 + fi +} + +# ------------------------------ +# MAIN +# ------------------------------ + +log "================================================================================" +log "|| PSR Version Action Test Runner ||" +log "================================================================================" +log "Initializing..." + +# Make absolute path to project directory +PROJECT_MOUNT_DIR="${PROJ_DIR:?}/${PROJECT_MOUNT_DIR:?}" + +log "" +log "******************************" +log "* Running test suite setup *" +log "******************************" +log "" + +# Setup project environment +create_example_project "$PROJECT_MOUNT_DIR" +trap 'rm -rf "${PROJECT_MOUNT_DIR:?}"' EXIT + +run_test_suite diff --git a/tests/gh_action/suite/test_version.sh b/tests/gh_action/suite/test_version.sh new file mode 100644 index 000000000..a853bf99f --- /dev/null +++ b/tests/gh_action/suite/test_version.sh @@ -0,0 +1,86 @@ +#!/bin/bash + +__file__="$(realpath "${BASH_SOURCE[0]}")" +__directory__="$(dirname "${__file__}")" + +if ! [ "${UTILS_LOADED}" = "true" ]; then + # shellcheck source=tests/utils.sh + source "$__directory__/../utils.sh" +fi + +test_version() { + # Using default configuration within PSR with no modifications + # triggering the NOOP mode to prevent errors since the repo doesn't exist + # We are just trying to test that the root options & tag arguments are + # passed to the action without a fatal error + local index="${1:?Index not provided}" + local test_name="${FUNCNAME[0]}" + + # Create expectations & set env variables that will be passed in for Docker command + local WITH_VAR_GITHUB_TOKEN="ghp_1x2x3x4x5x6x7x8x9x0x1x2x3x4x5x6x7x8x9x0" + local WITH_VAR_NO_OPERATION_MODE="true" + local WITH_VAR_VERBOSITY="2" + local expected_psr_cmd=".*/bin/semantic-release -vv --noop version" + + # Execute the test & capture output + # Fatal errors if exit code is not 0 + local output="" + if ! output="$(run_test "$index. $test_name" 2>&1)"; then + # Log the output for debugging purposes + log "$output" + error "fatal error occurred!" + error "::error:: $test_name failed!" + return 1 + fi + + # Evaluate the output to ensure the expected command is present + if ! printf '%s' "$output" | grep -q -E "$expected_psr_cmd"; then + # Log the output for debugging purposes + log "$output" + error "Failed to find the expected command in the output!" + error "\tExpected Command: $expected_psr_cmd" + error "::error:: $test_name failed!" + return 1 + fi + + log "\n$index. $test_name: PASSED!" +} + +test_version_w_custom_config() { + # Using default configuration within PSR with no modifications + # triggering the NOOP mode to prevent errors since the repo doesn't exist + # We are just trying to test that the root options & tag arguments are + # passed to the action without a fatal error + local index="${1:?Index not provided}" + local test_name="${FUNCNAME[0]}" + + # Create expectations & set env variables that will be passed in for Docker command + local WITH_VAR_GITHUB_TOKEN="ghp_1x2x3x4x5x6x7x8x9x0x1x2x3x4x5x6x7x8x9x0" + local WITH_VAR_NO_OPERATION_MODE="true" + local WITH_VAR_VERBOSITY="0" + local WITH_VAR_CONFIG_FILE="releaserc.toml" + local expected_psr_cmd=".*/bin/semantic-release --config $WITH_VAR_CONFIG_FILE --noop version" + + # Execute the test & capture output + # Fatal errors if exit code is not 0 + local output="" + if ! output="$(run_test "$index. $test_name" 2>&1)"; then + # Log the output for debugging purposes + log "$output" + error "fatal error occurred!" + error "::error:: $test_name failed!" + return 1 + fi + + # Evaluate the output to ensure the expected command is present + if ! printf '%s' "$output" | grep -q "$expected_psr_cmd"; then + # Log the output for debugging purposes + log "$output" + error "Failed to find the expected command in the output!" + error "\tExpected Command: $expected_psr_cmd" + error "::error:: $test_name failed!" + return 1 + fi + + log "\n$index. $test_name: PASSED!" +} diff --git a/tests/gh_action/suite/test_version_strict.sh b/tests/gh_action/suite/test_version_strict.sh new file mode 100644 index 000000000..cd9a2b512 --- /dev/null +++ b/tests/gh_action/suite/test_version_strict.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +__file__="$(realpath "${BASH_SOURCE[0]}")" +__directory__="$(dirname "${__file__}")" + +if ! [ "${UTILS_LOADED:-false}" = "true" ]; then + # shellcheck source=tests/utils.sh + source "$__directory__/../utils.sh" +fi + +test_version_strict() { + # Using default configuration within PSR with no modifications + # triggering the NOOP mode to prevent errors since the repo doesn't exist + # We are just trying to test that the root options & tag arguments are + # passed to the action without a fatal error + local index="${1:?Index not provided}" + local test_name="${FUNCNAME[0]}" + + # Create expectations & set env variables that will be passed in for Docker command + local WITH_VAR_GITHUB_TOKEN="ghp_1x2x3x4x5x6x7x8x9x0x1x2x3x4x5x6x7x8x9x0" + local WITH_VAR_NO_OPERATION_MODE="true" + local WITH_VAR_STRICT="true" + local expected_psr_cmd=".*/bin/semantic-release -v --strict --noop version" + # Since the example project is at the latest release, we expect strict mode + # to fail with a non-zero exit code + + # Execute the test & capture output + local output="" + if output="$(run_test "$index. $test_name" 2>&1)"; then + # Log the output for debugging purposes + log "$output" + error "Strict mode should of exited with a non-zero exit code but didn't!" + error "::error:: $test_name failed!" + return 1 + fi + + # Evaluate the output to ensure the expected command is present + if ! printf '%s' "$output" | grep -q "$expected_psr_cmd"; then + # Log the output for debugging purposes + log "$output" + error "Failed to find the expected command in the output!" + error "\tExpected Command: $expected_psr_cmd" + error "::error:: $test_name failed!" + return 1 + fi + + log "\n$index. $test_name: PASSED!" +} \ No newline at end of file diff --git a/tests/gh_action/utils.sh b/tests/gh_action/utils.sh new file mode 100644 index 000000000..a08f25e73 --- /dev/null +++ b/tests/gh_action/utils.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +# ------------------------------ +# UTILS +# ------------------------------ +IMAGE_TAG="${TEST_CONTAINER_TAG:?TEST_CONTAINER_TAG not set}" +PROJECT_MOUNT_DIR="${PROJECT_MOUNT_DIR:-"tmp/project"}" +GITHUB_ACTIONS_CWD="/github/workspace" + +log() { + printf '%b\n' "$*" +} + +error() { + log >&2 "\033[31m$*\033[0m" +} + +explicit_run_cmd() { + local cmd="$*" + log "$> $cmd\n" + eval "$cmd" +} + +run_test() { + local test_name="${1:?Test name not provided}" + test_name="${test_name//_/ }" + test_name="$(tr "[:lower:]" "[:upper:]" <<< "${test_name:0:1}")${test_name:1}" + + # Set Defaults based on action.yml + [ -z "${WITH_VAR_DIRECTORY:-}" ] && local WITH_VAR_DIRECTORY="." + [ -z "${WITH_VAR_CONFIG_FILE:-}" ] && local WITH_VAR_CONFIG_FILE="" + [ -z "${WITH_VAR_NO_OPERATION_MODE:-}" ] && local WITH_VAR_NO_OPERATION_MODE="false" + [ -z "${WITH_VAR_VERBOSITY:-}" ] && local WITH_VAR_VERBOSITY="1" + + # Extract all WITH_VAR_ variables dynamically from environment + local ENV_ARGS=() + args_in_env="$(compgen -A variable | grep "^WITH_VAR_")" + read -r -a ENV_ARGS <<< "$(printf '%s' "$args_in_env" | tr '\n' ' ')" + + # Set Docker arguments (default: always remove the container after execution) + local DOCKER_ARGS=("--rm") + + # Add all WITH_VAR_ variables to the Docker command + local actions_input_var_name="" + for input in "${ENV_ARGS[@]}"; do + # Convert WITH_VAR_ to INPUT_ to simulate GitHub Actions input syntax + actions_input_var_name="INPUT_${input#WITH_VAR_}" + + # Add the environment variable to the Docker command + DOCKER_ARGS+=("-e ${actions_input_var_name}='${!input}'") + done + + # Add the project directory to the Docker command + DOCKER_ARGS+=("-v ${PROJECT_MOUNT_DIR}:${GITHUB_ACTIONS_CWD}") + + # Set the working directory to the project directory + DOCKER_ARGS+=("-w ${GITHUB_ACTIONS_CWD}") + + # Run the test + log "\n$test_name" + log "--------------------------------------------------------------------------------" + if ! explicit_run_cmd "docker run ${DOCKER_ARGS[*]} $IMAGE_TAG"; then + return 1 + fi +} + +export UTILS_LOADED="true" From d664428e8aa7261b08b9aab526d35312d6e4b2e4 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 16 May 2025 23:23:01 -0600 Subject: [PATCH 087/160] ci(validate): add integration testing of PSR github docker action --- .github/changed-files-spec.yml | 13 +++++--- .github/workflows/ci.yml | 10 ++++-- .github/workflows/cicd.yml | 10 ++++-- .github/workflows/manual.yml | 2 ++ .github/workflows/validate.yml | 60 ++++++++++++++++++++++++++++++++++ 5 files changed, 87 insertions(+), 8 deletions(-) diff --git a/.github/changed-files-spec.yml b/.github/changed-files-spec.yml index 7f8e40353..8d479d1db 100644 --- a/.github/changed-files-spec.yml +++ b/.github/changed-files-spec.yml @@ -2,8 +2,6 @@ build: - MANIFEST.in - - Dockerfile - - .dockerignore - scripts/** docs: - docs/** @@ -11,8 +9,15 @@ docs: - AUTHORS.rst - CONTRIBUTING.rst - CHANGELOG.rst +gha_src: + - src/gh_action/** src: - - src/** + - src/semantic_release/** - pyproject.toml +gha_tests: + - tests/gh_action/** tests: - - tests/** + - tests/e2e/** + - tests/fixtures/** + - tests/unit/** + - tests/*.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 53d5c83c0..8658260eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,8 +62,10 @@ jobs: [ "${{ steps.ci-changed-files.outputs.ci_any_changed }}" == "true" ] || \ [ "${{ steps.core-changed-files.outputs.docs_any_changed }}" == "true" ] || \ [ "${{ steps.core-changed-files.outputs.src_any_changed }}" == "true" ] || \ - [ "${{ steps.core-changed-files.outputs.tests_any_changed }}" == "true" ]; then - printf '%s\n' "any_changed=true" >> $GITHUB_OUTPUT + [ "${{ steps.core-changed-files.outputs.tests_any_changed }}" == "true" ] || \ + [ "${{ steps.core-changed-files.outputs.gha_src_any_changed }}" == "true" ] || \ + [ "${{ steps.core-changed-files.outputs.gha_tests_any_changed }}" == "true" ]; then + printf '%s\n' "any_changed=true" >> $GITHUB_OUTPUT fi outputs: @@ -74,6 +76,8 @@ jobs: doc-changes: ${{ steps.core-changed-files.outputs.docs_any_changed }} src-changes: ${{ steps.core-changed-files.outputs.src_any_changed }} test-changes: ${{ steps.core-changed-files.outputs.tests_any_changed }} + gha-src-changes: ${{ steps.core-changed-files.outputs.gha_src_any_changed }} + gha-test-changes: ${{ steps.core-changed-files.outputs.gha_tests_any_changed }} validate: @@ -91,5 +95,7 @@ jobs: doc-files-changed: ${{ needs.eval-changes.outputs.doc-changes }} src-files-changed: ${{ needs.eval-changes.outputs.src-changes }} test-files-changed: ${{ needs.eval-changes.outputs.test-changes }} + gha-src-files-changed: ${{ needs.eval-changes.outputs.gha-src-changes }} + gha-test-files-changed: ${{ needs.eval-changes.outputs.gha-test-changes }} permissions: {} secrets: {} diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 13dc13bda..358dff3ab 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -49,8 +49,10 @@ jobs: [ "${{ steps.ci-changed-files.outputs.ci_any_changed }}" == "true" ] || \ [ "${{ steps.core-changed-files.outputs.docs_any_changed }}" == "true" ] || \ [ "${{ steps.core-changed-files.outputs.src_any_changed }}" == "true" ] || \ - [ "${{ steps.core-changed-files.outputs.tests_any_changed }}" == "true" ]; then - printf '%s\n' "any_changed=true" >> $GITHUB_OUTPUT + [ "${{ steps.core-changed-files.outputs.tests_any_changed }}" == "true" ] || \ + [ "${{ steps.core-changed-files.outputs.gha_src_any_changed }}" == "true" ] || \ + [ "${{ steps.core-changed-files.outputs.gha_tests_any_changed }}" == "true" ]; then + printf '%s\n' "any_changed=true" >> $GITHUB_OUTPUT fi outputs: @@ -60,6 +62,8 @@ jobs: doc-changes: ${{ steps.core-changed-files.outputs.docs_any_changed }} src-changes: ${{ steps.core-changed-files.outputs.src_any_changed }} test-changes: ${{ steps.core-changed-files.outputs.tests_any_changed }} + gha-src-changes: ${{ steps.core-changed-files.outputs.gha_src_any_changed }} + gha-test-changes: ${{ steps.core-changed-files.outputs.gha_tests_any_changed }} validate: @@ -77,6 +81,8 @@ jobs: doc-files-changed: ${{ needs.eval-changes.outputs.doc-changes }} src-files-changed: ${{ needs.eval-changes.outputs.src-changes }} test-files-changed: ${{ needs.eval-changes.outputs.test-changes }} + gha-src-files-changed: ${{ needs.eval-changes.outputs.gha-src-changes }} + gha-test-files-changed: ${{ needs.eval-changes.outputs.gha-test-changes }} permissions: {} secrets: {} diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml index 64263f37e..e1f292d66 100644 --- a/.github/workflows/manual.yml +++ b/.github/workflows/manual.yml @@ -133,6 +133,8 @@ jobs: doc-files-changed: true src-files-changed: true test-files-changed: true + gha-src-files-changed: true + gha-test-files-changed: true files-changed: true permissions: {} secrets: {} diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 08cbb4c91..e17483728 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -43,6 +43,16 @@ on: type: string required: false default: 'false' + gha-src-files-changed: + description: 'Boolean string result for if GitHub Action source files have changed' + type: string + required: false + default: 'false' + gha-test-files-changed: + description: 'Boolean string result for if GitHub Action test files have changed' + type: string + required: false + default: 'false' outputs: new-release-detected: description: Boolean string result for if new release is available @@ -373,6 +383,56 @@ jobs: annotate_only: true + test-gh-action: + name: Validate Action Build & Execution + runs-on: ubuntu-latest + if: inputs.gha-src-files-changed == 'true' || inputs.gha-test-files-changed == 'true' || inputs.ci-files-changed == 'true' + + needs: + - build + - unit-test + + env: + TEST_CONTAINER_TAG: psr-action:latest + ACTION_SRC_DIR: src/gh_action + + steps: + - name: Setup | Checkout Repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 1 + ref: ${{ github.sha }} + + - name: Setup | Download Distribution Artifacts + uses: actions/download-artifact@v4 + with: + name: ${{ needs.build.outputs.distribution-artifacts }} + path: ${{ env.ACTION_SRC_DIR }} + + - name: Setup | Update Dependency list with latest version + working-directory: ${{ env.ACTION_SRC_DIR }} + run: | + find . -name '*.whl' > requirements.txt + + - name: Setup | Allow Docker build to include wheel files + working-directory: ${{ env.ACTION_SRC_DIR }} + run: | + printf '%s\n' "!*.whl" >> .dockerignore + + - name: Build | Action Container + id: container-builder + uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0 + with: + context: ${{ env.ACTION_SRC_DIR }} + load: true # add to `docker images` + push: false + platforms: linux/amd64 + tags: ${{ env.TEST_CONTAINER_TAG }} + + - name: Test | Action Container + run: bash tests/gh_action/run.sh + + lint: name: Lint if: ${{ inputs.files-changed == 'true' }} From 5c5ca2aaf8ecce443546bf1337755069a0b2f442 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 17 May 2025 12:31:31 -0600 Subject: [PATCH 088/160] chore(conf): configure project to stamp the ci action's `requirements.txt` w/ new version (#1248) --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index ca6945374..7c1ae30c9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -412,6 +412,9 @@ build_command = """ python -m build . """ major_on_zero = true +version_variables = [ + "src/gh_action/requirements.txt:python-semantic-release:nf", +] version_toml = ["pyproject.toml:project.version"] [tool.semantic_release.changelog] From 726026be58de046e09188b50315ff19dadef71c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 24 May 2025 23:28:11 -0700 Subject: [PATCH 089/160] ci(deps): bump `docker/build-push-action@v6.16.0` to `v6.17.0` (#1250) --- .github/workflows/validate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index e17483728..e5a9b842d 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -421,7 +421,7 @@ jobs: - name: Build | Action Container id: container-builder - uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0 + uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0 with: context: ${{ env.ACTION_SRC_DIR }} load: true # add to `docker images` From ed5a2da7fbed42f2fdcdf6a002aae80466300a26 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 17 May 2025 13:46:29 -0600 Subject: [PATCH 090/160] refactor(changelog-templates): move templates from `angular/` to `conventional/` --- docs/changelog_templates.rst | 2 +- src/semantic_release/cli/config.py | 7 ++--- src/semantic_release/commit_parser/emoji.py | 2 +- .../md/.components/changelog_header.md.j2 | 0 .../md/.components/changelog_init.md.j2 | 0 .../md/.components/changelog_update.md.j2 | 0 .../md/.components/changes.md.j2 | 0 .../md/.components/first_release.md.j2 | 0 .../md/.components/macros.md.j2 | 0 .../md/.components/unreleased_changes.md.j2 | 0 .../md/.components/versioned_changes.md.j2 | 0 .../md/.release_notes.md.j2 | 0 .../md/CHANGELOG.md.j2 | 0 .../rst/.components/changelog_header.rst.j2 | 0 .../rst/.components/changelog_init.rst.j2 | 0 .../rst/.components/changelog_update.rst.j2 | 0 .../rst/.components/changes.rst.j2 | 0 .../rst/.components/first_release.rst.j2 | 0 .../rst/.components/macros.rst.j2 | 0 .../rst/.components/unreleased_changes.rst.j2 | 0 .../rst/.components/versioned_changes.rst.j2 | 0 .../rst/CHANGELOG.rst.j2 | 0 tests/fixtures/example_project.py | 4 +-- .../changelog/test_default_changelog.py | 18 +++++------ .../changelog/test_release_notes.py | 30 +++++++++---------- 25 files changed, 31 insertions(+), 32 deletions(-) rename src/semantic_release/data/templates/{angular => conventional}/md/.components/changelog_header.md.j2 (100%) rename src/semantic_release/data/templates/{angular => conventional}/md/.components/changelog_init.md.j2 (100%) rename src/semantic_release/data/templates/{angular => conventional}/md/.components/changelog_update.md.j2 (100%) rename src/semantic_release/data/templates/{angular => conventional}/md/.components/changes.md.j2 (100%) rename src/semantic_release/data/templates/{angular => conventional}/md/.components/first_release.md.j2 (100%) rename src/semantic_release/data/templates/{angular => conventional}/md/.components/macros.md.j2 (100%) rename src/semantic_release/data/templates/{angular => conventional}/md/.components/unreleased_changes.md.j2 (100%) rename src/semantic_release/data/templates/{angular => conventional}/md/.components/versioned_changes.md.j2 (100%) rename src/semantic_release/data/templates/{angular => conventional}/md/.release_notes.md.j2 (100%) rename src/semantic_release/data/templates/{angular => conventional}/md/CHANGELOG.md.j2 (100%) rename src/semantic_release/data/templates/{angular => conventional}/rst/.components/changelog_header.rst.j2 (100%) rename src/semantic_release/data/templates/{angular => conventional}/rst/.components/changelog_init.rst.j2 (100%) rename src/semantic_release/data/templates/{angular => conventional}/rst/.components/changelog_update.rst.j2 (100%) rename src/semantic_release/data/templates/{angular => conventional}/rst/.components/changes.rst.j2 (100%) rename src/semantic_release/data/templates/{angular => conventional}/rst/.components/first_release.rst.j2 (100%) rename src/semantic_release/data/templates/{angular => conventional}/rst/.components/macros.rst.j2 (100%) rename src/semantic_release/data/templates/{angular => conventional}/rst/.components/unreleased_changes.rst.j2 (100%) rename src/semantic_release/data/templates/{angular => conventional}/rst/.components/versioned_changes.rst.j2 (100%) rename src/semantic_release/data/templates/{angular => conventional}/rst/CHANGELOG.rst.j2 (100%) diff --git a/docs/changelog_templates.rst b/docs/changelog_templates.rst index 1b6e5fc7d..e950db3d1 100644 --- a/docs/changelog_templates.rst +++ b/docs/changelog_templates.rst @@ -331,7 +331,7 @@ be copied to its target location without being rendered by the template engine. When initially starting out at customizing your own changelog templates, you should reference the default template embedded within PSR. The template directory is located at ``data/templates/`` within the PSR package. Within our templates - directory we separate out each type of commit parser (e.g. angular) and the + directory we separate out each type of commit parser (e.g. conventional) and the content format type (e.g. markdown). You can copy this directory to your repository's templates directory and then customize the templates to your liking. diff --git a/src/semantic_release/cli/config.py b/src/semantic_release/cli/config.py index 41ac02058..be8ad4a5e 100644 --- a/src/semantic_release/cli/config.py +++ b/src/semantic_release/cli/config.py @@ -859,11 +859,11 @@ def from_raw_config( # noqa: C901 # Here we just assume the desired changelog style matches the parser name # as we provide templates specific to each parser type. Unfortunately if the user has # provided a custom parser, it would be up to the user to provide custom templates - # but we just assume the base template is angular + # but we just assume the base template is conventional # changelog_style = ( # raw.commit_parser # if raw.commit_parser in _known_commit_parsers - # else "angular" + # else "conventional" # ) self = cls( @@ -887,8 +887,7 @@ def from_raw_config( # noqa: C901 changelog_excluded_commit_patterns=changelog_excluded_commit_patterns, # TODO: change when we have other styles per parser # changelog_style=changelog_style, - # TODO: Breaking Change v10, change to conventional - changelog_style="angular", + changelog_style="conventional", changelog_output_format=raw.changelog.default_templates.output_format, prerelease=branch_config.prerelease, ignore_token_for_push=raw.remote.ignore_token_for_push, diff --git a/src/semantic_release/commit_parser/emoji.py b/src/semantic_release/commit_parser/emoji.py index bb830b395..5be4f1ec3 100644 --- a/src/semantic_release/commit_parser/emoji.py +++ b/src/semantic_release/commit_parser/emoji.py @@ -450,7 +450,7 @@ def _find_squashed_commits_in_str(self, message: str) -> list[str]: if not clean_paragraph.strip(): continue - # Check if the paragraph is the start of a new angular commit + # Check if the paragraph is the start of a new emoji commit if not self.emoji_selector.search(clean_paragraph): if not separate_commit_msgs and not current_msg: # if there are no separate commit messages and no current message diff --git a/src/semantic_release/data/templates/angular/md/.components/changelog_header.md.j2 b/src/semantic_release/data/templates/conventional/md/.components/changelog_header.md.j2 similarity index 100% rename from src/semantic_release/data/templates/angular/md/.components/changelog_header.md.j2 rename to src/semantic_release/data/templates/conventional/md/.components/changelog_header.md.j2 diff --git a/src/semantic_release/data/templates/angular/md/.components/changelog_init.md.j2 b/src/semantic_release/data/templates/conventional/md/.components/changelog_init.md.j2 similarity index 100% rename from src/semantic_release/data/templates/angular/md/.components/changelog_init.md.j2 rename to src/semantic_release/data/templates/conventional/md/.components/changelog_init.md.j2 diff --git a/src/semantic_release/data/templates/angular/md/.components/changelog_update.md.j2 b/src/semantic_release/data/templates/conventional/md/.components/changelog_update.md.j2 similarity index 100% rename from src/semantic_release/data/templates/angular/md/.components/changelog_update.md.j2 rename to src/semantic_release/data/templates/conventional/md/.components/changelog_update.md.j2 diff --git a/src/semantic_release/data/templates/angular/md/.components/changes.md.j2 b/src/semantic_release/data/templates/conventional/md/.components/changes.md.j2 similarity index 100% rename from src/semantic_release/data/templates/angular/md/.components/changes.md.j2 rename to src/semantic_release/data/templates/conventional/md/.components/changes.md.j2 diff --git a/src/semantic_release/data/templates/angular/md/.components/first_release.md.j2 b/src/semantic_release/data/templates/conventional/md/.components/first_release.md.j2 similarity index 100% rename from src/semantic_release/data/templates/angular/md/.components/first_release.md.j2 rename to src/semantic_release/data/templates/conventional/md/.components/first_release.md.j2 diff --git a/src/semantic_release/data/templates/angular/md/.components/macros.md.j2 b/src/semantic_release/data/templates/conventional/md/.components/macros.md.j2 similarity index 100% rename from src/semantic_release/data/templates/angular/md/.components/macros.md.j2 rename to src/semantic_release/data/templates/conventional/md/.components/macros.md.j2 diff --git a/src/semantic_release/data/templates/angular/md/.components/unreleased_changes.md.j2 b/src/semantic_release/data/templates/conventional/md/.components/unreleased_changes.md.j2 similarity index 100% rename from src/semantic_release/data/templates/angular/md/.components/unreleased_changes.md.j2 rename to src/semantic_release/data/templates/conventional/md/.components/unreleased_changes.md.j2 diff --git a/src/semantic_release/data/templates/angular/md/.components/versioned_changes.md.j2 b/src/semantic_release/data/templates/conventional/md/.components/versioned_changes.md.j2 similarity index 100% rename from src/semantic_release/data/templates/angular/md/.components/versioned_changes.md.j2 rename to src/semantic_release/data/templates/conventional/md/.components/versioned_changes.md.j2 diff --git a/src/semantic_release/data/templates/angular/md/.release_notes.md.j2 b/src/semantic_release/data/templates/conventional/md/.release_notes.md.j2 similarity index 100% rename from src/semantic_release/data/templates/angular/md/.release_notes.md.j2 rename to src/semantic_release/data/templates/conventional/md/.release_notes.md.j2 diff --git a/src/semantic_release/data/templates/angular/md/CHANGELOG.md.j2 b/src/semantic_release/data/templates/conventional/md/CHANGELOG.md.j2 similarity index 100% rename from src/semantic_release/data/templates/angular/md/CHANGELOG.md.j2 rename to src/semantic_release/data/templates/conventional/md/CHANGELOG.md.j2 diff --git a/src/semantic_release/data/templates/angular/rst/.components/changelog_header.rst.j2 b/src/semantic_release/data/templates/conventional/rst/.components/changelog_header.rst.j2 similarity index 100% rename from src/semantic_release/data/templates/angular/rst/.components/changelog_header.rst.j2 rename to src/semantic_release/data/templates/conventional/rst/.components/changelog_header.rst.j2 diff --git a/src/semantic_release/data/templates/angular/rst/.components/changelog_init.rst.j2 b/src/semantic_release/data/templates/conventional/rst/.components/changelog_init.rst.j2 similarity index 100% rename from src/semantic_release/data/templates/angular/rst/.components/changelog_init.rst.j2 rename to src/semantic_release/data/templates/conventional/rst/.components/changelog_init.rst.j2 diff --git a/src/semantic_release/data/templates/angular/rst/.components/changelog_update.rst.j2 b/src/semantic_release/data/templates/conventional/rst/.components/changelog_update.rst.j2 similarity index 100% rename from src/semantic_release/data/templates/angular/rst/.components/changelog_update.rst.j2 rename to src/semantic_release/data/templates/conventional/rst/.components/changelog_update.rst.j2 diff --git a/src/semantic_release/data/templates/angular/rst/.components/changes.rst.j2 b/src/semantic_release/data/templates/conventional/rst/.components/changes.rst.j2 similarity index 100% rename from src/semantic_release/data/templates/angular/rst/.components/changes.rst.j2 rename to src/semantic_release/data/templates/conventional/rst/.components/changes.rst.j2 diff --git a/src/semantic_release/data/templates/angular/rst/.components/first_release.rst.j2 b/src/semantic_release/data/templates/conventional/rst/.components/first_release.rst.j2 similarity index 100% rename from src/semantic_release/data/templates/angular/rst/.components/first_release.rst.j2 rename to src/semantic_release/data/templates/conventional/rst/.components/first_release.rst.j2 diff --git a/src/semantic_release/data/templates/angular/rst/.components/macros.rst.j2 b/src/semantic_release/data/templates/conventional/rst/.components/macros.rst.j2 similarity index 100% rename from src/semantic_release/data/templates/angular/rst/.components/macros.rst.j2 rename to src/semantic_release/data/templates/conventional/rst/.components/macros.rst.j2 diff --git a/src/semantic_release/data/templates/angular/rst/.components/unreleased_changes.rst.j2 b/src/semantic_release/data/templates/conventional/rst/.components/unreleased_changes.rst.j2 similarity index 100% rename from src/semantic_release/data/templates/angular/rst/.components/unreleased_changes.rst.j2 rename to src/semantic_release/data/templates/conventional/rst/.components/unreleased_changes.rst.j2 diff --git a/src/semantic_release/data/templates/angular/rst/.components/versioned_changes.rst.j2 b/src/semantic_release/data/templates/conventional/rst/.components/versioned_changes.rst.j2 similarity index 100% rename from src/semantic_release/data/templates/angular/rst/.components/versioned_changes.rst.j2 rename to src/semantic_release/data/templates/conventional/rst/.components/versioned_changes.rst.j2 diff --git a/src/semantic_release/data/templates/angular/rst/CHANGELOG.rst.j2 b/src/semantic_release/data/templates/conventional/rst/CHANGELOG.rst.j2 similarity index 100% rename from src/semantic_release/data/templates/angular/rst/CHANGELOG.rst.j2 rename to src/semantic_release/data/templates/conventional/rst/CHANGELOG.rst.j2 diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index 7575ec96f..5d0c60886 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -248,7 +248,7 @@ def default_changelog_md_template() -> Path: return Path( str( files(semantic_release.__name__).joinpath( - Path("data", "templates", "angular", "md", "CHANGELOG.md.j2") + Path("data", "templates", "conventional", "md", "CHANGELOG.md.j2") ) ) ).resolve() @@ -260,7 +260,7 @@ def default_changelog_rst_template() -> Path: return Path( str( files(semantic_release.__name__).joinpath( - Path("data", "templates", "angular", "rst", "CHANGELOG.rst.j2") + Path("data", "templates", "conventional", "rst", "CHANGELOG.rst.j2") ) ) ).resolve() diff --git a/tests/unit/semantic_release/changelog/test_default_changelog.py b/tests/unit/semantic_release/changelog/test_default_changelog.py index adbbdc241..6e42f7fad 100644 --- a/tests/unit/semantic_release/changelog/test_default_changelog.py +++ b/tests/unit/semantic_release/changelog/test_default_changelog.py @@ -23,7 +23,7 @@ def default_changelog_template() -> str: """Retrieve the semantic-release default changelog template.""" version_notes_template = files(semantic_release.__name__).joinpath( - Path("data", "templates", "angular", "md", "CHANGELOG.md.j2") + Path("data", "templates", "conventional", "md", "CHANGELOG.md.j2") ) return version_notes_template.read_text(encoding="utf-8") @@ -112,7 +112,7 @@ def test_default_changelog_template( insertion_flag="", mask_initial_release=True, ), - changelog_style="angular", + changelog_style="conventional", ) assert expected_changelog == actual_changelog @@ -235,7 +235,7 @@ def test_default_changelog_template_w_a_brk_change( insertion_flag="", mask_initial_release=True, ), - changelog_style="angular", + changelog_style="conventional", ) assert expected_changelog == actual_changelog @@ -381,7 +381,7 @@ def test_default_changelog_template_w_multiple_brk_changes( insertion_flag="", mask_initial_release=True, ), - changelog_style="angular", + changelog_style="conventional", ) assert expected_changelog == actual_changelog @@ -475,7 +475,7 @@ def test_default_changelog_template_no_initial_release_mask( insertion_flag="", mask_initial_release=False, ), - changelog_style="angular", + changelog_style="conventional", ) assert expected_changelog == actual_changelog @@ -572,7 +572,7 @@ def test_default_changelog_template_w_unreleased_changes( insertion_flag="", mask_initial_release=True, ), - changelog_style="angular", + changelog_style="conventional", ) assert expected_changelog == actual_changelog @@ -695,7 +695,7 @@ def test_default_changelog_template_w_a_notice( insertion_flag="", mask_initial_release=False, ), - changelog_style="angular", + changelog_style="conventional", ) assert expected_changelog == actual_changelog @@ -855,7 +855,7 @@ def test_default_changelog_template_w_a_notice_n_brk_change( insertion_flag="", mask_initial_release=False, ), - changelog_style="angular", + changelog_style="conventional", ) assert expected_changelog == actual_changelog @@ -1019,7 +1019,7 @@ def test_default_changelog_template_w_multiple_notices( insertion_flag="", mask_initial_release=False, ), - changelog_style="angular", + changelog_style="conventional", ) assert expected_changelog == actual_changelog diff --git a/tests/unit/semantic_release/changelog/test_release_notes.py b/tests/unit/semantic_release/changelog/test_release_notes.py index 2b95ec827..02f217bf1 100644 --- a/tests/unit/semantic_release/changelog/test_release_notes.py +++ b/tests/unit/semantic_release/changelog/test_release_notes.py @@ -26,7 +26,7 @@ def release_notes_template() -> str: """Retrieve the semantic-release default release notes template.""" version_notes_template = files(semantic_release.__name__).joinpath( - Path("data", "templates", "angular", "md", ".release_notes.md.j2") + Path("data", "templates", "conventional", "md", ".release_notes.md.j2") ) return version_notes_template.read_text(encoding="utf-8") @@ -156,7 +156,7 @@ def test_default_release_notes_template( release=release, template_dir=Path(""), history=artificial_release_history, - style="angular", + style="conventional", mask_initial_release=mask_initial_release, license_name=license_name, ) @@ -248,7 +248,7 @@ def test_default_release_notes_template_w_a_brk_description( release=release, template_dir=Path(""), history=release_history_w_brk_change, - style="angular", + style="conventional", mask_initial_release=mask_initial_release, ) @@ -369,7 +369,7 @@ def test_default_release_notes_template_w_multiple_brk_changes( release=release, template_dir=Path(""), history=release_history_w_multiple_brk_changes, - style="angular", + style="conventional", mask_initial_release=mask_initial_release, ) @@ -417,7 +417,7 @@ def test_default_release_notes_template_first_release_masked( release=release, template_dir=Path(""), history=single_release_history, - style="angular", + style="conventional", mask_initial_release=True, license_name=license_name, ) @@ -481,7 +481,7 @@ def test_default_release_notes_template_first_release_unmasked( release=release, template_dir=Path(""), history=single_release_history, - style="angular", + style="conventional", mask_initial_release=False, license_name=license_name, ) @@ -529,7 +529,7 @@ def test_release_notes_context_sort_numerically_filter( release=release, template_dir=example_project_dir, history=single_release_history, - style="angular", + style="conventional", mask_initial_release=False, ) @@ -576,7 +576,7 @@ def test_release_notes_context_sort_numerically_filter_reversed( release=release, template_dir=example_project_dir, history=single_release_history, - style="angular", + style="conventional", mask_initial_release=False, ) @@ -603,7 +603,7 @@ def test_release_notes_context_pypi_url_filter( release=release, template_dir=example_project_dir, history=single_release_history, - style="angular", + style="conventional", mask_initial_release=False, ) @@ -630,7 +630,7 @@ def test_release_notes_context_pypi_url_filter_tagged( release=release, template_dir=example_project_dir, history=single_release_history, - style="angular", + style="conventional", mask_initial_release=False, ) @@ -675,7 +675,7 @@ def test_release_notes_context_release_url_filter( release=release, template_dir=example_project_dir, history=single_release_history, - style="angular", + style="conventional", mask_initial_release=False, ) @@ -718,7 +718,7 @@ def test_release_notes_context_format_w_official_name_filter( release=release, template_dir=example_project_dir, history=single_release_history, - style="angular", + style="conventional", mask_initial_release=False, ) @@ -807,7 +807,7 @@ def test_default_release_notes_template_w_a_notice( release=release, template_dir=Path(""), history=release_history_w_a_notice, - style="angular", + style="conventional", mask_initial_release=mask_initial_release, ) @@ -928,7 +928,7 @@ def test_default_release_notes_template_w_a_notice_n_brk_change( release=release, template_dir=Path(""), history=release_history_w_notice_n_brk_change, - style="angular", + style="conventional", mask_initial_release=mask_initial_release, ) @@ -1041,7 +1041,7 @@ def test_default_release_notes_template_w_multiple_notices( release=release, template_dir=Path(""), history=release_history_w_multiple_notices, - style="angular", + style="conventional", mask_initial_release=mask_initial_release, ) From e7ac155a91fc2e735d3cbf9b66fb4e5ff40a1466 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Thu, 19 Dec 2024 23:44:58 -0700 Subject: [PATCH 091/160] fix(changelog-md)!: change to 1-line descriptions in markdown template Remove the code that writes out the rest of the commit message from the default template. BREAKING CHANGE: The default Markdown changelog template and release notes template will no longer print out the entire commit message contents, instead, it will only print the commit subject line. This comes to meet the high demand of better formatted changelogs and requests for subject line only. Originally, it was a decision to not hide commit subjects that were included in the commit body via the `git merge --squash` command and PSR did not have another alternative. At this point, all the built-in parsers have the ability to parse squashed commits and separate them out into their own entry on the changelog. Therefore, the default template no longer needs to write out the full commit body. See the commit parser options if you want to enable/disable parsing squash commits. Resolves: #733 --- .../conventional/md/.components/changes.md.j2 | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/semantic_release/data/templates/conventional/md/.components/changes.md.j2 b/src/semantic_release/data/templates/conventional/md/.components/changes.md.j2 index b2b89ff12..c81b0faa1 100644 --- a/src/semantic_release/data/templates/conventional/md/.components/changes.md.j2 +++ b/src/semantic_release/data/templates/conventional/md/.components/changes.md.j2 @@ -46,16 +46,8 @@ EXAMPLE: #}{% set commit_descriptions = [] %}{# #}{% for commit in ns.commits -%}{# # Update the first line with reference links and if commit description - # has more than one line, add the rest of the lines - # NOTE: This is specifically to make sure to not hide contents - # of squash commits (until parse support is added) +%}{# # Add reference links to the commit summary line #}{% set description = "- %s" | format(format_commit_summary_line(commit)) -%}{% if commit.descriptions | length > 1 -%}{% set description = "%s\n\n%s" | format( - description, commit.descriptions[1:] | join("\n\n") - ) -%}{% endif %}{% set description = description | autofit_text_width(max_line_width, hanging_indent) %}{% set _ = commit_descriptions.append(description) %}{% endfor From 731466fec4e06fe71f6c4addd4ae2ec2182ae9c1 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Thu, 19 Dec 2024 23:45:24 -0700 Subject: [PATCH 092/160] fix(changelog-rst)!: change to 1-line descriptions in the default ReStructuredText template Remove the code that writes out the rest of the commit message from the default template. BREAKING CHANGE: The default ReStructured changelog template will no longer print out the entire commit message contents, instead, it will only print the commit subject line. This comes to meet the high demand of better formatted changelogs and requests for subject line only. Originally, it was a decision to not hide commit subjects that were included in the commit body via the `git merge --squash` command and PSR did not have another alternative. At this point, all the built-in parsers have the ability to parse squashed commits and separate them out into their own entry on the changelog. Therefore, the default template no longer needs to write out the full commit body. See the commit parser options if you want to enable/disable parsing squash commits. Resolves: #733 --- .../conventional/rst/.components/changes.rst.j2 | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/semantic_release/data/templates/conventional/rst/.components/changes.rst.j2 b/src/semantic_release/data/templates/conventional/rst/.components/changes.rst.j2 index c6ef1cced..7498aa787 100644 --- a/src/semantic_release/data/templates/conventional/rst/.components/changes.rst.j2 +++ b/src/semantic_release/data/templates/conventional/rst/.components/changes.rst.j2 @@ -72,16 +72,8 @@ Additional Release Information %}{% set _ = post_paragraph_links.append(commit_hash_link_reference) %}{# # Generate the commit summary line and format it for RST - # Update the first line with reference links and if commit description - # has more than one line, add the rest of the lines - # NOTE: This is specifically to make sure to not hide contents - # of squash commits (until parse support is added) + # autoformatting the reference links #}{% set description = "* %s" | format(format_commit_summary_line(commit)) -%}{% if commit.descriptions | length > 1 -%}{% set description = "%s\n\n%s" | format( - description, commit.descriptions[1:] | join("\n\n") | trim - ) -%}{% endif %}{% set description = description | convert_md_to_rst %}{% set description = description | autofit_text_width(max_line_width, hanging_indent) %}{% set _ = commit_descriptions.append(description) From 615d8d22698cdfd0dfdffb5b65279c6b8bc09da3 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 17 May 2025 15:37:22 -0600 Subject: [PATCH 093/160] refactor(parser-conventional): update parser code to separate from the deprecating angular parser --- .../commit_parser/conventional.py | 477 +++++++++++++++++- 1 file changed, 472 insertions(+), 5 deletions(-) diff --git a/src/semantic_release/commit_parser/conventional.py b/src/semantic_release/commit_parser/conventional.py index 9ee0b27fe..9a90c3ff4 100644 --- a/src/semantic_release/commit_parser/conventional.py +++ b/src/semantic_release/commit_parser/conventional.py @@ -1,19 +1,126 @@ from __future__ import annotations +import re +from functools import reduce +from itertools import zip_longest +from logging import getLogger +from re import compile as regexp +from textwrap import dedent +from typing import TYPE_CHECKING, Tuple + +from git.objects.commit import Commit from pydantic.dataclasses import dataclass -from semantic_release.commit_parser.angular import ( - AngularCommitParser, - AngularParserOptions, +from semantic_release.commit_parser._base import CommitParser, ParserOptions +from semantic_release.commit_parser.token import ( + ParsedCommit, + ParsedMessageResult, + ParseError, + ParseResult, +) +from semantic_release.commit_parser.util import ( + breaking_re, + deep_copy_commit, + force_str, + parse_paragraphs, ) +from semantic_release.enums import LevelBump +from semantic_release.errors import InvalidParserOptions +from semantic_release.helpers import sort_numerically, text_reducer + +if TYPE_CHECKING: # pragma: no cover + from git.objects.commit import Commit + + +def _logged_parse_error(commit: Commit, error: str) -> ParseError: + getLogger(__name__).debug(error) + return ParseError(commit, error=error) + + +# TODO: Remove from here, allow for user customization instead via options +# types with long names in changelog +LONG_TYPE_NAMES = { + "build": "build system", + "ci": "continuous integration", + "chore": "chores", + "docs": "documentation", + "feat": "features", + "fix": "bug fixes", + "perf": "performance improvements", + "refactor": "refactoring", + "style": "code style", + "test": "testing", +} @dataclass -class ConventionalCommitParserOptions(AngularParserOptions): +class ConventionalCommitParserOptions(ParserOptions): """Options dataclass for the ConventionalCommitParser.""" + minor_tags: Tuple[str, ...] = ("feat",) + """Commit-type prefixes that should result in a minor release bump.""" + + patch_tags: Tuple[str, ...] = ("fix", "perf") + """Commit-type prefixes that should result in a patch release bump.""" -class ConventionalCommitParser(AngularCommitParser): + other_allowed_tags: Tuple[str, ...] = ( + "build", + "chore", + "ci", + "docs", + "style", + "refactor", + "test", + ) + """Commit-type prefixes that are allowed but do not result in a version bump.""" + + allowed_tags: Tuple[str, ...] = ( + *minor_tags, + *patch_tags, + *other_allowed_tags, + ) + """ + All commit-type prefixes that are allowed. + + These are used to identify a valid commit message. If a commit message does not start with + one of these prefixes, it will not be considered a valid commit message. + """ + + default_bump_level: LevelBump = LevelBump.NO_RELEASE + """The minimum bump level to apply to valid commit message.""" + + # TODO: breaking change v10, change default to True + parse_squash_commits: bool = False + """Toggle flag for whether or not to parse squash commits""" + + # TODO: breaking change v10, change default to True + ignore_merge_commits: bool = False + """Toggle flag for whether or not to ignore merge commits""" + + @property + def tag_to_level(self) -> dict[str, LevelBump]: + """A mapping of commit tags to the level bump they should result in.""" + return self._tag_to_level + + def __post_init__(self) -> None: + self._tag_to_level: dict[str, LevelBump] = { + str(tag): level + for tag, level in [ + # we have to do a type ignore as zip_longest provides a type that is not specific enough + # for our expected output. Due to the empty second array, we know the first is always longest + # and that means no values in the first entry of the tuples will ever be a LevelBump. We + # apply a str() to make mypy happy although it will never happen. + *zip_longest(self.allowed_tags, (), fillvalue=self.default_bump_level), + *zip_longest(self.patch_tags, (), fillvalue=LevelBump.PATCH), + *zip_longest(self.minor_tags, (), fillvalue=LevelBump.MINOR), + ] + if "|" not in str(tag) + } + + +class ConventionalCommitParser( + CommitParser[ParseResult, ConventionalCommitParserOptions] +): """ A commit parser for projects conforming to the conventional commits specification. @@ -26,6 +133,366 @@ class ConventionalCommitParser(AngularCommitParser): def __init__(self, options: ConventionalCommitParserOptions | None = None) -> None: super().__init__(options) + try: + commit_type_pattern = regexp( + r"(?P%s)" % str.join("|", self.options.allowed_tags) + ) + except re.error as err: + raise InvalidParserOptions( + str.join( + "\n", + [ + f"Invalid options for {self.__class__.__name__}", + "Unable to create regular expression from configured commit-types.", + "Please check the configured commit-types and remove or escape any regular expression characters.", + ], + ) + ) from err + + self.commit_prefix = regexp( + str.join( + "", + [ + f"^{commit_type_pattern.pattern}", + r"(?:\((?P[^\n]+)\))?", + # TODO: remove ! support as it is not part of the angular commit spec (its part of conventional commits spec) + r"(?P!)?:\s+", + ], + ) + ) + + self.re_parser = regexp( + str.join( + "", + [ + self.commit_prefix.pattern, + r"(?P[^\n]+)", + r"(?:\n\n(?P.+))?", # commit body + ], + ), + flags=re.DOTALL, + ) + + # GitHub & Gitea use (#123), GitLab uses (!123), and BitBucket uses (pull request #123) + self.mr_selector = regexp( + r"[\t ]+\((?:pull request )?(?P[#!]\d+)\)[\t ]*$" + ) + self.issue_selector = regexp( + str.join( + "", + [ + r"^(?:clos(?:e|es|ed|ing)|fix(?:es|ed|ing)?|resolv(?:e|es|ed|ing)|implement(?:s|ed|ing)?):", + r"[\t ]+(?P.+)[\t ]*$", + ], + ), + flags=re.MULTILINE | re.IGNORECASE, + ) + self.notice_selector = regexp(r"^NOTICE: (?P.+)$") + self.filters = { + "typo-extra-spaces": (regexp(r"(\S) +(\S)"), r"\1 \2"), + "git-header-commit": ( + regexp(r"^[\t ]*commit [0-9a-f]+$\n?", flags=re.MULTILINE), + "", + ), + "git-header-author": ( + regexp(r"^[\t ]*Author: .+$\n?", flags=re.MULTILINE), + "", + ), + "git-header-date": ( + regexp(r"^[\t ]*Date: .+$\n?", flags=re.MULTILINE), + "", + ), + "git-squash-heading": ( + regexp( + r"^[\t ]*Squashed commit of the following:.*$\n?", + flags=re.MULTILINE, + ), + "", + ), + "git-squash-commit-prefix": ( + regexp( + str.join( + "", + [ + r"^(?:[\t ]*[*-][\t ]+|[\t ]+)?", # bullet points or indentation + commit_type_pattern.pattern + r"\b", # prior to commit type + ], + ), + flags=re.MULTILINE, + ), + # move commit type to the start of the line + r"\1", + ), + } + @staticmethod def get_default_options() -> ConventionalCommitParserOptions: return ConventionalCommitParserOptions() + + def commit_body_components_separator( + self, accumulator: dict[str, list[str]], text: str + ) -> dict[str, list[str]]: + if (match := breaking_re.match(text)) and (brk_desc := match.group(1)): + accumulator["breaking_descriptions"].append(brk_desc) + # TODO: breaking change v10, removes breaking change footers from descriptions + # return accumulator + + elif (match := self.notice_selector.match(text)) and ( + notice := match.group("notice") + ): + accumulator["notices"].append(notice) + # TODO: breaking change v10, removes notice footers from descriptions + # return accumulator + + elif match := self.issue_selector.search(text): + # if match := self.issue_selector.search(text): + predicate = regexp(r",? and | *[,;/& ] *").sub( + ",", match.group("issue_predicate") or "" + ) + # Almost all issue trackers use a number to reference an issue so + # we use a simple regexp to validate the existence of a number which helps filter out + # any non-issue references that don't fit our expected format + has_number = regexp(r"\d+") + new_issue_refs: set[str] = set( + filter( + lambda issue_str, validator=has_number: validator.search(issue_str), # type: ignore[arg-type] + predicate.split(","), + ) + ) + if new_issue_refs: + accumulator["linked_issues"] = sort_numerically( + set(accumulator["linked_issues"]).union(new_issue_refs) + ) + # TODO: breaking change v10, removes resolution footers from descriptions + # return accumulator + + # Prevent appending duplicate descriptions + if text not in accumulator["descriptions"]: + accumulator["descriptions"].append(text) + + return accumulator + + def parse_message(self, message: str) -> ParsedMessageResult | None: + if not (parsed := self.re_parser.match(message)): + return None + + parsed_break = parsed.group("break") + parsed_scope = parsed.group("scope") or "" + parsed_subject = parsed.group("subject") + parsed_text = parsed.group("text") + parsed_type = parsed.group("type") + + linked_merge_request = "" + if mr_match := self.mr_selector.search(parsed_subject): + linked_merge_request = mr_match.group("mr_number") + # TODO: breaking change v10, removes PR number from subject/descriptions + # expects changelog template to format the line accordingly + # parsed_subject = self.pr_selector.sub("", parsed_subject).strip() + + body_components: dict[str, list[str]] = reduce( + self.commit_body_components_separator, + [ + # Insert the subject before the other paragraphs + parsed_subject, + *parse_paragraphs(parsed_text or ""), + ], + { + "breaking_descriptions": [], + "descriptions": [], + "notices": [], + "linked_issues": [], + }, + ) + + level_bump = ( + LevelBump.MAJOR + if body_components["breaking_descriptions"] or parsed_break + else self.options.tag_to_level.get( + parsed_type, self.options.default_bump_level + ) + ) + + return ParsedMessageResult( + bump=level_bump, + type=parsed_type, + category=LONG_TYPE_NAMES.get(parsed_type, parsed_type), + scope=parsed_scope, + descriptions=tuple(body_components["descriptions"]), + breaking_descriptions=tuple(body_components["breaking_descriptions"]), + release_notices=tuple(body_components["notices"]), + linked_issues=tuple(body_components["linked_issues"]), + linked_merge_request=linked_merge_request, + ) + + @staticmethod + def is_merge_commit(commit: Commit) -> bool: + return len(commit.parents) > 1 + + def parse_commit(self, commit: Commit) -> ParseResult: + if not (parsed_msg_result := self.parse_message(force_str(commit.message))): + return _logged_parse_error( + commit, + f"Unable to parse commit message: {commit.message!r}", + ) + + return ParsedCommit.from_parsed_message_result(commit, parsed_msg_result) + + # Maybe this can be cached as an optimization, similar to how + # mypy/pytest use their own caching directories, for very large commit + # histories? + # The problem is the cache likely won't be present in CI environments + def parse(self, commit: Commit) -> ParseResult | list[ParseResult]: + """ + Parse a commit message + + If the commit message is a squashed merge commit, it will be split into + multiple commits, each of which will be parsed separately. Single commits + will be returned as a list of a single ParseResult. + """ + if self.options.ignore_merge_commits and self.is_merge_commit(commit): + return _logged_parse_error( + commit, "Ignoring merge commit: %s" % commit.hexsha[:8] + ) + + separate_commits: list[Commit] = ( + self.unsquash_commit(commit) + if self.options.parse_squash_commits + else [commit] + ) + + # Parse each commit individually if there were more than one + parsed_commits: list[ParseResult] = list( + map(self.parse_commit, separate_commits) + ) + + def add_linked_merge_request( + parsed_result: ParseResult, mr_number: str + ) -> ParseResult: + return ( + parsed_result + if not isinstance(parsed_result, ParsedCommit) + else ParsedCommit( + **{ + **parsed_result._asdict(), + "linked_merge_request": mr_number, + } + ) + ) + + # TODO: improve this for other VCS systems other than GitHub & BitBucket + # Github works as the first commit in a squash merge commit has the PR number + # appended to the first line of the commit message + lead_commit = next(iter(parsed_commits)) + + if isinstance(lead_commit, ParsedCommit) and lead_commit.linked_merge_request: + # If the first commit has linked merge requests, assume all commits + # are part of the same PR and add the linked merge requests to all + # parsed commits + parsed_commits = [ + lead_commit, + *map( + lambda parsed_result, mr=lead_commit.linked_merge_request: ( # type: ignore[misc] + add_linked_merge_request(parsed_result, mr) + ), + parsed_commits[1:], + ), + ] + + elif isinstance(lead_commit, ParseError) and ( + mr_match := self.mr_selector.search(force_str(lead_commit.message)) + ): + # Handle BitBucket Squash Merge Commits (see #1085), which have non angular commit + # format but include the PR number in the commit subject that we want to extract + linked_merge_request = mr_match.group("mr_number") + + # apply the linked MR to all commits + parsed_commits = [ + add_linked_merge_request(parsed_result, linked_merge_request) + for parsed_result in parsed_commits + ] + + return parsed_commits + + def unsquash_commit(self, commit: Commit) -> list[Commit]: + # GitHub EXAMPLE: + # feat(changelog): add autofit_text_width filter to template environment (#1062) + # + # This change adds an equivalent style formatter that can apply a text alignment + # to a maximum width and also maintain an indent over paragraphs of text + # + # * docs(changelog-templates): add definition & usage of autofit_text_width template filter + # + # * test(changelog-context): add test cases to check autofit_text_width filter use + # + # `git merge --squash` EXAMPLE: + # Squashed commit of the following: + # + # commit 63ec09b9e844e616dcaa7bae35a0b66671b59fbb + # Author: codejedi365 + # Date: Sun Oct 13 12:05:23 2024 -0600 + # + # feat(release-config): some commit subject + # + + # Return a list of artificial commits (each with a single commit message) + return [ + # create a artificial commit object (copy of original but with modified message) + Commit( + **{ + **deep_copy_commit(commit), + "message": commit_msg, + } + ) + for commit_msg in self.unsquash_commit_message(force_str(commit.message)) + ] or [commit] + + def unsquash_commit_message(self, message: str) -> list[str]: + normalized_message = message.replace("\r", "").strip() + + # split by obvious separate commits (applies to manual git squash merges) + obvious_squashed_commits = self.filters["git-header-commit"][0].split( + normalized_message + ) + + separate_commit_msgs: list[str] = reduce( + lambda all_msgs, msgs: all_msgs + msgs, + map(self._find_squashed_commits_in_str, obvious_squashed_commits), + [], + ) + + return list(filter(None, separate_commit_msgs)) + + def _find_squashed_commits_in_str(self, message: str) -> list[str]: + separate_commit_msgs: list[str] = [] + current_msg = "" + + for paragraph in filter(None, message.strip().split("\n\n")): + # Apply filters to normalize the paragraph + clean_paragraph = reduce(text_reducer, self.filters.values(), paragraph) + + # remove any filtered (and now empty) paragraphs (ie. the git headers) + if not clean_paragraph.strip(): + continue + + # Check if the paragraph is the start of a new angular commit + if not self.commit_prefix.search(clean_paragraph): + if not separate_commit_msgs and not current_msg: + # if there are no separate commit messages and no current message + # then this is the first commit message + current_msg = dedent(clean_paragraph) + continue + + # append the paragraph as part of the previous commit message + if current_msg: + current_msg += f"\n\n{dedent(clean_paragraph)}" + # else: drop the paragraph + continue + + # Since we found the start of the new commit, store any previous commit + # message separately and start the new commit message + if current_msg: + separate_commit_msgs.append(current_msg) + + current_msg = clean_paragraph + + return [*separate_commit_msgs, current_msg] From f703a757abc5b343f61e9a4cc09bbd6a92b7b7f4 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 17 May 2025 15:58:11 -0600 Subject: [PATCH 094/160] refactor(parser-scipy): update parser code to separate from the deprecating angular parser --- src/semantic_release/commit_parser/scipy.py | 429 ++++++++++++++++++-- 1 file changed, 404 insertions(+), 25 deletions(-) diff --git a/src/semantic_release/commit_parser/scipy.py b/src/semantic_release/commit_parser/scipy.py index 6234cfdf3..1e014b6f8 100644 --- a/src/semantic_release/commit_parser/scipy.py +++ b/src/semantic_release/commit_parser/scipy.py @@ -46,29 +46,39 @@ from __future__ import annotations -import logging +import re +from functools import reduce +from itertools import zip_longest +from logging import getLogger +from re import compile as regexp +from textwrap import dedent from typing import TYPE_CHECKING, Tuple +from git.objects.commit import Commit from pydantic.dataclasses import dataclass -from semantic_release.commit_parser.angular import ( - AngularCommitParser, - AngularParserOptions, -) +from semantic_release.commit_parser._base import CommitParser, ParserOptions from semantic_release.commit_parser.token import ( + ParsedCommit, ParsedMessageResult, ParseError, + ParseResult, +) +from semantic_release.commit_parser.util import ( + deep_copy_commit, + force_str, + parse_paragraphs, ) from semantic_release.enums import LevelBump +from semantic_release.errors import InvalidParserOptions +from semantic_release.helpers import sort_numerically, text_reducer if TYPE_CHECKING: # pragma: no cover from git.objects.commit import Commit -logger = logging.getLogger(__name__) - def _logged_parse_error(commit: Commit, error: str) -> ParseError: - logger.debug(error) + getLogger(__name__).debug(error) return ParseError(commit, error=error) @@ -93,7 +103,7 @@ def _logged_parse_error(commit: Commit, error: str) -> ParseError: @dataclass -class ScipyParserOptions(AngularParserOptions): +class ScipyParserOptions(ParserOptions): """ Options dataclass for ScipyCommitParser @@ -110,10 +120,7 @@ class ScipyParserOptions(AngularParserOptions): patch_tags: Tuple[str, ...] = ("BLD", "BUG", "MAINT") """Commit-type prefixes that should result in a patch release bump.""" - allowed_tags: Tuple[str, ...] = ( - *major_tags, - *minor_tags, - *patch_tags, + other_allowed_tags: Tuple[str, ...] = ( "BENCH", "DOC", "STY", @@ -121,6 +128,14 @@ class ScipyParserOptions(AngularParserOptions): "REL", "TEST", ) + """Commit-type prefixes that are allowed but do not result in a version bump.""" + + allowed_tags: Tuple[str, ...] = ( + *major_tags, + *minor_tags, + *patch_tags, + *other_allowed_tags, + ) """ All commit-type prefixes that are allowed. @@ -132,15 +147,39 @@ class ScipyParserOptions(AngularParserOptions): default_level_bump: LevelBump = LevelBump.NO_RELEASE """The minimum bump level to apply to valid commit message.""" + # TODO: breaking change v10, change default to True + parse_squash_commits: bool = False + """Toggle flag for whether or not to parse squash commits""" + + # TODO: breaking change v10, change default to True + ignore_merge_commits: bool = False + """Toggle flag for whether or not to ignore merge commits""" + + @property + def tag_to_level(self) -> dict[str, LevelBump]: + """A mapping of commit tags to the level bump they should result in.""" + return self._tag_to_level + def __post_init__(self) -> None: # TODO: breaking v10, remove as the name is now consistent self.default_bump_level = self.default_level_bump - super().__post_init__() - for tag in self.major_tags: - self._tag_to_level[tag] = LevelBump.MAJOR - - -class ScipyCommitParser(AngularCommitParser): + self._tag_to_level: dict[str, LevelBump] = { + str(tag): level + for tag, level in [ + # we have to do a type ignore as zip_longest provides a type that is not specific enough + # for our expected output. Due to the empty second array, we know the first is always longest + # and that means no values in the first entry of the tuples will ever be a LevelBump. We + # apply a str() to make mypy happy although it will never happen. + *zip_longest(self.allowed_tags, (), fillvalue=self.default_bump_level), + *zip_longest(self.patch_tags, (), fillvalue=LevelBump.PATCH), + *zip_longest(self.minor_tags, (), fillvalue=LevelBump.MINOR), + *zip_longest(self.major_tags, (), fillvalue=LevelBump.MAJOR), + ] + if "|" not in str(tag) + } + + +class ScipyCommitParser(CommitParser[ParseResult, ScipyParserOptions]): """Parser for scipy-style commit messages""" # TODO: Deprecate in lieu of get_default_options() @@ -149,18 +188,358 @@ class ScipyCommitParser(AngularCommitParser): def __init__(self, options: ScipyParserOptions | None = None) -> None: super().__init__(options) + try: + commit_type_pattern = regexp( + r"(?P%s)" % str.join("|", self.options.allowed_tags) + ) + except re.error as err: + raise InvalidParserOptions( + str.join( + "\n", + [ + f"Invalid options for {self.__class__.__name__}", + "Unable to create regular expression from configured commit-types.", + "Please check the configured commit-types and remove or escape any regular expression characters.", + ], + ) + ) from err + + self.commit_prefix = regexp( + str.join( + "", + [ + f"^{commit_type_pattern.pattern}", + r"(?:\((?P[^\n]+)\))?", + r":\s+", + ], + ) + ) + + self.re_parser = regexp( + str.join( + "", + [ + self.commit_prefix.pattern, + r"(?P[^\n]+)", + r"(?:\n\n(?P.+))?", # commit body + ], + ), + flags=re.DOTALL, + ) + + # GitHub & Gitea use (#123), GitLab uses (!123), and BitBucket uses (pull request #123) + self.mr_selector = regexp( + r"[\t ]+\((?:pull request )?(?P[#!]\d+)\)[\t ]*$" + ) + self.issue_selector = regexp( + str.join( + "", + [ + r"^(?:clos(?:e|es|ed|ing)|fix(?:es|ed|ing)?|resolv(?:e|es|ed|ing)|implement(?:s|ed|ing)?):", + r"[\t ]+(?P.+)[\t ]*$", + ], + ), + flags=re.MULTILINE | re.IGNORECASE, + ) + self.notice_selector = regexp(r"^NOTICE: (?P.+)$") + self.filters = { + "typo-extra-spaces": (regexp(r"(\S) +(\S)"), r"\1 \2"), + "git-header-commit": ( + regexp(r"^[\t ]*commit [0-9a-f]+$\n?", flags=re.MULTILINE), + "", + ), + "git-header-author": ( + regexp(r"^[\t ]*Author: .+$\n?", flags=re.MULTILINE), + "", + ), + "git-header-date": ( + regexp(r"^[\t ]*Date: .+$\n?", flags=re.MULTILINE), + "", + ), + "git-squash-heading": ( + regexp( + r"^[\t ]*Squashed commit of the following:.*$\n?", + flags=re.MULTILINE, + ), + "", + ), + "git-squash-commit-prefix": ( + regexp( + str.join( + "", + [ + r"^(?:[\t ]*[*-][\t ]+|[\t ]+)?", # bullet points or indentation + commit_type_pattern.pattern + r"\b", # prior to commit type + ], + ), + flags=re.MULTILINE, + ), + # move commit type to the start of the line + r"\1", + ), + } + @staticmethod def get_default_options() -> ScipyParserOptions: return ScipyParserOptions() + def commit_body_components_separator( + self, accumulator: dict[str, list[str]], text: str + ) -> dict[str, list[str]]: + if (match := self.notice_selector.match(text)) and ( + notice := match.group("notice") + ): + accumulator["notices"].append(notice) + # TODO: breaking change v10, removes notice footers from descriptions + # return accumulator + + elif match := self.issue_selector.search(text): + # if match := self.issue_selector.search(text): + predicate = regexp(r",? and | *[,;/& ] *").sub( + ",", match.group("issue_predicate") or "" + ) + # Almost all issue trackers use a number to reference an issue so + # we use a simple regexp to validate the existence of a number which helps filter out + # any non-issue references that don't fit our expected format + has_number = regexp(r"\d+") + new_issue_refs: set[str] = set( + filter( + lambda issue_str, validator=has_number: validator.search(issue_str), # type: ignore[arg-type] + predicate.split(","), + ) + ) + if new_issue_refs: + accumulator["linked_issues"] = sort_numerically( + set(accumulator["linked_issues"]).union(new_issue_refs) + ) + # TODO: breaking change v10, removes resolution footers from descriptions + # return accumulator + + # Prevent appending duplicate descriptions + if text not in accumulator["descriptions"]: + accumulator["descriptions"].append(text) + + return accumulator + def parse_message(self, message: str) -> ParsedMessageResult | None: - return ( - None - if not (pmsg_result := super().parse_message(message)) - else ParsedMessageResult( + if not (parsed := self.re_parser.match(message)): + return None + + parsed_scope = parsed.group("scope") or "" + parsed_subject = parsed.group("subject") + parsed_text = parsed.group("text") + parsed_type = parsed.group("type") + + linked_merge_request = "" + if mr_match := self.mr_selector.search(parsed_subject): + linked_merge_request = mr_match.group("mr_number") + # TODO: breaking change v10, removes PR number from subject/descriptions + # expects changelog template to format the line accordingly + # parsed_subject = self.pr_selector.sub("", parsed_subject).strip() + + body_components: dict[str, list[str]] = reduce( + self.commit_body_components_separator, + [ + # Insert the subject before the other paragraphs + parsed_subject, + *parse_paragraphs(parsed_text or ""), + ], + { + "descriptions": [], + "notices": [], + "linked_issues": [], + }, + ) + + level_bump = self.options.tag_to_level.get( + parsed_type, self.options.default_bump_level + ) + + return ParsedMessageResult( + bump=level_bump, + type=parsed_type, + category=tag_to_section.get(parsed_type, "None"), + scope=parsed_scope, + descriptions=tuple( + body_components["descriptions"] + if level_bump != LevelBump.MAJOR + else [parsed_subject] + ), + breaking_descriptions=tuple( + body_components["descriptions"][1:] + if level_bump == LevelBump.MAJOR + else [] + ), + release_notices=tuple(body_components["notices"]), + linked_issues=tuple(body_components["linked_issues"]), + linked_merge_request=linked_merge_request, + ) + + @staticmethod + def is_merge_commit(commit: Commit) -> bool: + return len(commit.parents) > 1 + + def parse_commit(self, commit: Commit) -> ParseResult: + if not (parsed_msg_result := self.parse_message(force_str(commit.message))): + return _logged_parse_error( + commit, + f"Unable to parse commit message: {commit.message!r}", + ) + + return ParsedCommit.from_parsed_message_result(commit, parsed_msg_result) + + def parse(self, commit: Commit) -> ParseResult | list[ParseResult]: + """ + Parse a commit message + + If the commit message is a squashed merge commit, it will be split into + multiple commits, each of which will be parsed separately. Single commits + will be returned as a list of a single ParseResult. + """ + if self.options.ignore_merge_commits and self.is_merge_commit(commit): + return _logged_parse_error( + commit, "Ignoring merge commit: %s" % commit.hexsha[:8] + ) + + separate_commits: list[Commit] = ( + self.unsquash_commit(commit) + if self.options.parse_squash_commits + else [commit] + ) + + # Parse each commit individually if there were more than one + parsed_commits: list[ParseResult] = list( + map(self.parse_commit, separate_commits) + ) + + def add_linked_merge_request( + parsed_result: ParseResult, mr_number: str + ) -> ParseResult: + return ( + parsed_result + if not isinstance(parsed_result, ParsedCommit) + else ParsedCommit( + **{ + **parsed_result._asdict(), + "linked_merge_request": mr_number, + } + ) + ) + + # TODO: improve this for other VCS systems other than GitHub & BitBucket + # Github works as the first commit in a squash merge commit has the PR number + # appended to the first line of the commit message + lead_commit = next(iter(parsed_commits)) + + if isinstance(lead_commit, ParsedCommit) and lead_commit.linked_merge_request: + # If the first commit has linked merge requests, assume all commits + # are part of the same PR and add the linked merge requests to all + # parsed commits + parsed_commits = [ + lead_commit, + *map( + lambda parsed_result, mr=lead_commit.linked_merge_request: ( # type: ignore[misc] + add_linked_merge_request(parsed_result, mr) + ), + parsed_commits[1:], + ), + ] + + elif isinstance(lead_commit, ParseError) and ( + mr_match := self.mr_selector.search(force_str(lead_commit.message)) + ): + # Handle BitBucket Squash Merge Commits (see #1085), which have non angular commit + # format but include the PR number in the commit subject that we want to extract + linked_merge_request = mr_match.group("mr_number") + + # apply the linked MR to all commits + parsed_commits = [ + add_linked_merge_request(parsed_result, linked_merge_request) + for parsed_result in parsed_commits + ] + + return parsed_commits + + def unsquash_commit(self, commit: Commit) -> list[Commit]: + # GitHub EXAMPLE: + # feat(changelog): add autofit_text_width filter to template environment (#1062) + # + # This change adds an equivalent style formatter that can apply a text alignment + # to a maximum width and also maintain an indent over paragraphs of text + # + # * docs(changelog-templates): add definition & usage of autofit_text_width template filter + # + # * test(changelog-context): add test cases to check autofit_text_width filter use + # + # `git merge --squash` EXAMPLE: + # Squashed commit of the following: + # + # commit 63ec09b9e844e616dcaa7bae35a0b66671b59fbb + # Author: codejedi365 + # Date: Sun Oct 13 12:05:23 2024 -0600 + # + # feat(release-config): some commit subject + # + + # Return a list of artificial commits (each with a single commit message) + return [ + # create a artificial commit object (copy of original but with modified message) + Commit( **{ - **pmsg_result._asdict(), - "category": tag_to_section.get(pmsg_result.type, "None"), + **deep_copy_commit(commit), + "message": commit_msg, } ) + for commit_msg in self.unsquash_commit_message(force_str(commit.message)) + ] or [commit] + + def unsquash_commit_message(self, message: str) -> list[str]: + normalized_message = message.replace("\r", "").strip() + + # split by obvious separate commits (applies to manual git squash merges) + obvious_squashed_commits = self.filters["git-header-commit"][0].split( + normalized_message + ) + + separate_commit_msgs: list[str] = reduce( + lambda all_msgs, msgs: all_msgs + msgs, + map(self._find_squashed_commits_in_str, obvious_squashed_commits), + [], ) + + return list(filter(None, separate_commit_msgs)) + + def _find_squashed_commits_in_str(self, message: str) -> list[str]: + separate_commit_msgs: list[str] = [] + current_msg = "" + + for paragraph in filter(None, message.strip().split("\n\n")): + # Apply filters to normalize the paragraph + clean_paragraph = reduce(text_reducer, self.filters.values(), paragraph) + + # remove any filtered (and now empty) paragraphs (ie. the git headers) + if not clean_paragraph.strip(): + continue + + # Check if the paragraph is the start of a new angular commit + if not self.commit_prefix.search(clean_paragraph): + if not separate_commit_msgs and not current_msg: + # if there are no separate commit messages and no current message + # then this is the first commit message + current_msg = dedent(clean_paragraph) + continue + + # append the paragraph as part of the previous commit message + if current_msg: + current_msg += f"\n\n{dedent(clean_paragraph)}" + # else: drop the paragraph + continue + + # Since we found the start of the new commit, store any previous commit + # message separately and start the new commit message + if current_msg: + separate_commit_msgs.append(current_msg) + + current_msg = clean_paragraph + + return [*separate_commit_msgs, current_msg] From b1bb0e55910715754eebef6cb5b21ebed5ee8d68 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Wed, 23 Oct 2024 23:51:41 -0600 Subject: [PATCH 095/160] fix(parser-conventional)!: remove issue footer messages from commit descriptions BREAKING CHANGE: Any issue resolution footers that the parser detects will now be removed from the `commit.descriptions[]` list. Previously, the descriptions included all text from the commit message but now that the parser pulls out the issue numbers the numbers will be included in the `commit.linked_issues` tuple for user extraction in any changelog generation. --- src/semantic_release/commit_parser/conventional.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/semantic_release/commit_parser/conventional.py b/src/semantic_release/commit_parser/conventional.py index 9a90c3ff4..25b60642a 100644 --- a/src/semantic_release/commit_parser/conventional.py +++ b/src/semantic_release/commit_parser/conventional.py @@ -263,8 +263,7 @@ def commit_body_components_separator( accumulator["linked_issues"] = sort_numerically( set(accumulator["linked_issues"]).union(new_issue_refs) ) - # TODO: breaking change v10, removes resolution footers from descriptions - # return accumulator + return accumulator # Prevent appending duplicate descriptions if text not in accumulator["descriptions"]: From 3cfee76032662bda6fbdd7e2585193213e4f9da2 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 17 May 2025 16:03:23 -0600 Subject: [PATCH 096/160] fix(parser-scipy)!: remove issue footer messages from commit descriptions BREAKING CHANGE: Any issue resolution footers that the parser detects will now be removed from the commit.descriptions[] list. Previously, the descriptions included all text from the commit message but now that the parser pulls out the issue numbers the numbers will be included in the commit.linked_issues tuple for user extraction in any changelog generation. --- src/semantic_release/commit_parser/scipy.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/semantic_release/commit_parser/scipy.py b/src/semantic_release/commit_parser/scipy.py index 1e014b6f8..a21223944 100644 --- a/src/semantic_release/commit_parser/scipy.py +++ b/src/semantic_release/commit_parser/scipy.py @@ -312,8 +312,7 @@ def commit_body_components_separator( accumulator["linked_issues"] = sort_numerically( set(accumulator["linked_issues"]).union(new_issue_refs) ) - # TODO: breaking change v10, removes resolution footers from descriptions - # return accumulator + return accumulator # Prevent appending duplicate descriptions if text not in accumulator["descriptions"]: From b757603e77ebe26d8a14758d78fd21163a9059b2 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 18 May 2025 17:04:24 -0600 Subject: [PATCH 097/160] fix(parser-emoji)!: remove issue footer messages from commit descriptions BREAKING CHANGE: Any issue resolution footers that the parser detects will now be removed from the `commit.descriptions[]` list. Previously, the descriptions included all text from the commit message but now that the parser pulls out the issue numbers the numbers will be included in the `commit.linked_issues` tuple for user extraction in any changelog generation. --- src/semantic_release/commit_parser/emoji.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/semantic_release/commit_parser/emoji.py b/src/semantic_release/commit_parser/emoji.py index 5be4f1ec3..d5ec89dd1 100644 --- a/src/semantic_release/commit_parser/emoji.py +++ b/src/semantic_release/commit_parser/emoji.py @@ -262,8 +262,7 @@ def commit_body_components_separator( accumulator["linked_issues"] = sort_numerically( set(accumulator["linked_issues"]).union(new_issue_refs) ) - # TODO: breaking change v10, removes resolution footers from descriptions - # return accumulator + return accumulator # Prevent appending duplicate descriptions if text not in accumulator["descriptions"]: From b271cbb2d3e8b86d07d1358b2e7424ccff6ae186 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Wed, 23 Oct 2024 23:52:50 -0600 Subject: [PATCH 098/160] fix(parser-conventional)!: remove breaking change footer messages from commit descriptions BREAKING CHANGE: Any breaking change footer messages that the conventional commit parser detects will now be removed from the `commit.descriptions[]` list but maintained in and only in the `commit.breaking_descriptions[]` list. Previously, the descriptions included all text from the commit message but that was redundant as the default changelog now handles breaking change footers in its own section. --- src/semantic_release/commit_parser/conventional.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/semantic_release/commit_parser/conventional.py b/src/semantic_release/commit_parser/conventional.py index 25b60642a..a7a3fadff 100644 --- a/src/semantic_release/commit_parser/conventional.py +++ b/src/semantic_release/commit_parser/conventional.py @@ -234,10 +234,9 @@ def commit_body_components_separator( ) -> dict[str, list[str]]: if (match := breaking_re.match(text)) and (brk_desc := match.group(1)): accumulator["breaking_descriptions"].append(brk_desc) - # TODO: breaking change v10, removes breaking change footers from descriptions - # return accumulator + return accumulator - elif (match := self.notice_selector.match(text)) and ( + if (match := self.notice_selector.match(text)) and ( notice := match.group("notice") ): accumulator["notices"].append(notice) From 7e8dc13c0b048a95d01f7aecfbe4eeedcddec9a4 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 18 May 2025 17:45:27 -0600 Subject: [PATCH 099/160] fix(parser-conventional)!: remove release notice footer messages from commit descriptions BREAKING CHANGE: Any release notice footer messages that the commit parser detects will now be removed from the `commit.descriptions[]` list but maintained in and only in the `commit.notices[]` list. Previously, the descriptions included all text from the commit message but that was redundant as the default changelog now handles release notice footers in its own section. --- src/semantic_release/commit_parser/conventional.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/semantic_release/commit_parser/conventional.py b/src/semantic_release/commit_parser/conventional.py index a7a3fadff..3e1a73cf3 100644 --- a/src/semantic_release/commit_parser/conventional.py +++ b/src/semantic_release/commit_parser/conventional.py @@ -240,10 +240,9 @@ def commit_body_components_separator( notice := match.group("notice") ): accumulator["notices"].append(notice) - # TODO: breaking change v10, removes notice footers from descriptions - # return accumulator + return accumulator - elif match := self.issue_selector.search(text): + if match := self.issue_selector.search(text): # if match := self.issue_selector.search(text): predicate = regexp(r",? and | *[,;/& ] *").sub( ",", match.group("issue_predicate") or "" From 58308e31bb6306aac3a985af01eb779dc923d3f0 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Wed, 23 Oct 2024 23:52:50 -0600 Subject: [PATCH 100/160] fix(parser-scipy)!: remove release notice footer messages from commit descriptions BREAKING CHANGE: Any release notice footer messages that the commit parser detects will now be removed from the `commit.descriptions[]` list but maintained in and only in the `commit.notices[]` list. Previously, the descriptions included all text from the commit message but that was redundant as the default changelog now handles release notice footers in its own section. --- src/semantic_release/commit_parser/scipy.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/semantic_release/commit_parser/scipy.py b/src/semantic_release/commit_parser/scipy.py index a21223944..a6484fe20 100644 --- a/src/semantic_release/commit_parser/scipy.py +++ b/src/semantic_release/commit_parser/scipy.py @@ -290,10 +290,9 @@ def commit_body_components_separator( notice := match.group("notice") ): accumulator["notices"].append(notice) - # TODO: breaking change v10, removes notice footers from descriptions - # return accumulator + return accumulator - elif match := self.issue_selector.search(text): + if match := self.issue_selector.search(text): # if match := self.issue_selector.search(text): predicate = regexp(r",? and | *[,;/& ] *").sub( ",", match.group("issue_predicate") or "" From b6307cb649043bbcc7ad9f15ac5ac6728914f443 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 18 May 2025 17:07:07 -0600 Subject: [PATCH 101/160] fix(parser-emoji)!: remove release notice footer messages from commit descriptions BREAKING CHANGE: Any release notice footer messages that the emoji commit parser detects will now be removed from the `commit.descriptions[]` list but maintained in and only in the `commit.notices[]` list. Previously, the descriptions included all text from the commit message but that was redundant as the default changelog now handles release notice footers in its own section. --- src/semantic_release/commit_parser/emoji.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/semantic_release/commit_parser/emoji.py b/src/semantic_release/commit_parser/emoji.py index d5ec89dd1..2333f8273 100644 --- a/src/semantic_release/commit_parser/emoji.py +++ b/src/semantic_release/commit_parser/emoji.py @@ -239,10 +239,9 @@ def commit_body_components_separator( notice := match.group("notice") ): accumulator["notices"].append(notice) - # TODO: breaking change v10, removes notice footers from descriptions - # return accumulator + return accumulator - elif self.options.parse_linked_issues and ( + if self.options.parse_linked_issues and ( match := self.issue_selector.search(text) ): predicate = regexp(r",? and | *[,;/& ] *").sub( From eed63fa9f6e762f55700fc85ef3ebdc0d3144f21 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Wed, 23 Oct 2024 23:56:21 -0600 Subject: [PATCH 102/160] fix(parser-conventional)!: remove PR/MR references from commit subject line BREAKING CHANGE: Generally, a pull request or merge request number reference is included in the subject line at the end within parentheses on some common VCS's like GitHub. PSR now looks for this reference and extracts it into the `commit.linked_merge_request` and the `commit.linked_pull_request` attributes of a commit object. Since this is now pulled out individually, it is cleaner to remove this from the first line of the `commit.descriptions` list (ie. the subject line) so that changelog macros do not have to replace the text but instead only append a PR/MR link to the end of the line. The reference does maintain the PR/MR prefix indicator (`#` or `!`). --- src/semantic_release/commit_parser/conventional.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/semantic_release/commit_parser/conventional.py b/src/semantic_release/commit_parser/conventional.py index 3e1a73cf3..067caf3c7 100644 --- a/src/semantic_release/commit_parser/conventional.py +++ b/src/semantic_release/commit_parser/conventional.py @@ -282,9 +282,7 @@ def parse_message(self, message: str) -> ParsedMessageResult | None: linked_merge_request = "" if mr_match := self.mr_selector.search(parsed_subject): linked_merge_request = mr_match.group("mr_number") - # TODO: breaking change v10, removes PR number from subject/descriptions - # expects changelog template to format the line accordingly - # parsed_subject = self.pr_selector.sub("", parsed_subject).strip() + parsed_subject = self.mr_selector.sub("", parsed_subject).strip() body_components: dict[str, list[str]] = reduce( self.commit_body_components_separator, From da4140f3e3a2ed03c05064f35561b4584f517105 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Wed, 23 Oct 2024 23:56:21 -0600 Subject: [PATCH 103/160] fix(parser-scipy)!: remove PR/MR references from commit subject line BREAKING CHANGE: Generally, a pull request or merge request number reference is included in the subject line at the end within parentheses on some common VCS's like GitHub. PSR now looks for this reference and extracts it into the `commit.linked_merge_request` and the `commit.linked_pull_request` attributes of a commit object. Since this is now pulled out individually, it is cleaner to remove this from the first line of the `commit.descriptions` list (ie. the subject line) so that changelog macros do not have to replace the text but instead only append a PR/MR link to the end of the line. The reference does maintain the PR/MR prefix indicator (`#` or `!`). --- src/semantic_release/commit_parser/scipy.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/semantic_release/commit_parser/scipy.py b/src/semantic_release/commit_parser/scipy.py index a6484fe20..e19725a0b 100644 --- a/src/semantic_release/commit_parser/scipy.py +++ b/src/semantic_release/commit_parser/scipy.py @@ -331,9 +331,7 @@ def parse_message(self, message: str) -> ParsedMessageResult | None: linked_merge_request = "" if mr_match := self.mr_selector.search(parsed_subject): linked_merge_request = mr_match.group("mr_number") - # TODO: breaking change v10, removes PR number from subject/descriptions - # expects changelog template to format the line accordingly - # parsed_subject = self.pr_selector.sub("", parsed_subject).strip() + parsed_subject = self.mr_selector.sub("", parsed_subject).strip() body_components: dict[str, list[str]] = reduce( self.commit_body_components_separator, From 16465f133386b09627d311727a6f8d24dd8f174f Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 18 May 2025 17:12:36 -0600 Subject: [PATCH 104/160] fix(parser-emoji)!: remove PR/MR references from commit subject line BREAKING CHANGE: Generally, a pull request or merge request number reference is included in the subject line at the end within parentheses on some common VCS's (e.g. GitHub). PSR now looks for these references and extract it into the `commit.linked_merge_request` field of a commit object. Since this is now pulled out individually, it is cleaner to remove this from the first line of the `commit.descriptions` list (ie. the subject line) so that changelog macros do not have to replace the text but instead only append a PR/MR link to the end of the line. The reference will maintain the PR/MR prefix indicator (e.g. `#` or `!`). --- src/semantic_release/commit_parser/emoji.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/semantic_release/commit_parser/emoji.py b/src/semantic_release/commit_parser/emoji.py index 2333f8273..dabef9e5f 100644 --- a/src/semantic_release/commit_parser/emoji.py +++ b/src/semantic_release/commit_parser/emoji.py @@ -275,9 +275,7 @@ def parse_message(self, message: str) -> ParsedMessageResult: linked_merge_request = "" if mr_match := self.mr_selector.search(subject): linked_merge_request = mr_match.group("mr_number") - # TODO: breaking change v10, removes PR number from subject/descriptions - # expects changelog template to format the line accordingly - # subject = self.mr_selector.sub("", subject).strip() + subject = self.mr_selector.sub("", subject).strip() # Search for emoji of the highest importance in the subject match = self.emoji_selector.search(subject) From 6fcdc99e9462b1186ea9488fc14e4e18f8c7fdb3 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 11 Nov 2024 14:39:31 -0700 Subject: [PATCH 105/160] feat(parser-conventional)!: set parser to evaluate all squashed commits by default BREAKING CHANGE: The configuration setting `commit_parser_options.parse_squash_commits` is now set to `true` by default. The feature to parse squash commits was introduced in `v9.17.0` and was originally set to `false` to prevent unexpected results on a non-breaking update. The parse squash commits feature attempts to find additional commits of the same commit type within the body of a single commit message. When squash commits are found, Python Semantic Release will separate out each commit into its own artificial commit object and parse them individually. This potentially can change the resulting version bump if a larger bump was detected within the squashed components. It also allows for the changelog and release notes to separately order and display each commit as originally written. If this is not desired, you will need to update your configuration to change the new setting to `false`. --- src/semantic_release/commit_parser/conventional.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/semantic_release/commit_parser/conventional.py b/src/semantic_release/commit_parser/conventional.py index 067caf3c7..267a505d3 100644 --- a/src/semantic_release/commit_parser/conventional.py +++ b/src/semantic_release/commit_parser/conventional.py @@ -89,8 +89,7 @@ class ConventionalCommitParserOptions(ParserOptions): default_bump_level: LevelBump = LevelBump.NO_RELEASE """The minimum bump level to apply to valid commit message.""" - # TODO: breaking change v10, change default to True - parse_squash_commits: bool = False + parse_squash_commits: bool = True """Toggle flag for whether or not to parse squash commits""" # TODO: breaking change v10, change default to True From 634fffea29157e9b6305b21802c78ac245454265 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 11 Nov 2024 14:39:31 -0700 Subject: [PATCH 106/160] feat(parser-scipy)!: set parser to evaluate all squashed commits by default BREAKING CHANGE: The configuration setting `commit_parser_options.parse_squash_commits` is now set to `true` by default. The feature to parse squash commits was introduced in `v9.17.0` and was originally set to `false` to prevent unexpected results on a non-breaking update. The parse squash commits feature attempts to find additional commits of the same commit type within the body of a single commit message. When squash commits are found, Python Semantic Release will separate out each commit into its own artificial commit object and parse them individually. This potentially can change the resulting version bump if a larger bump was detected within the squashed components. It also allows for the changelog and release notes to separately order and display each commit as originally written. If this is not desired, you will need to update your configuration to change the new setting to `false`. --- src/semantic_release/commit_parser/scipy.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/semantic_release/commit_parser/scipy.py b/src/semantic_release/commit_parser/scipy.py index e19725a0b..3615fff94 100644 --- a/src/semantic_release/commit_parser/scipy.py +++ b/src/semantic_release/commit_parser/scipy.py @@ -147,8 +147,7 @@ class ScipyParserOptions(ParserOptions): default_level_bump: LevelBump = LevelBump.NO_RELEASE """The minimum bump level to apply to valid commit message.""" - # TODO: breaking change v10, change default to True - parse_squash_commits: bool = False + parse_squash_commits: bool = True """Toggle flag for whether or not to parse squash commits""" # TODO: breaking change v10, change default to True From 514a922fa87721e2500062dcae841bedd84dc1fe Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 18 May 2025 17:17:17 -0600 Subject: [PATCH 107/160] feat(parser-emoji)!: set parser to evaluate all squashed commits by default BREAKING CHANGE: The configuration setting `commit_parser_options.parse_squash_commits` is now set to `true` by default. The feature to parse squash commits was introduced in `v9.17.0` and was originally set to `false` to prevent unexpected results on a non-breaking update. The parse squash commits feature attempts to find additional commits of the same commit type within the body of a single commit message. When squash commits are found, Python Semantic Release will separate out each commit into its own artificial commit object and parse them individually. This potentially can change the resulting version bump if a larger bump was detected within the squashed components. It also allows for the changelog and release notes to separately order and display each commit as originally written. If this is not desired, you will need to update your configuration to change the new setting to `false`. --- src/semantic_release/commit_parser/emoji.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/semantic_release/commit_parser/emoji.py b/src/semantic_release/commit_parser/emoji.py index dabef9e5f..6fe965b02 100644 --- a/src/semantic_release/commit_parser/emoji.py +++ b/src/semantic_release/commit_parser/emoji.py @@ -94,8 +94,7 @@ class EmojiParserOptions(ParserOptions): a whitespace separator. """ - # TODO: breaking change v10, change default to True - parse_squash_commits: bool = False + parse_squash_commits: bool = True """Toggle flag for whether or not to parse squash commits""" # TODO: breaking change v10, change default to True From 59bf08440a15269afaac81d78dd03ee418f9fd6b Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 11 Nov 2024 14:41:22 -0700 Subject: [PATCH 108/160] feat(parser-conventional)!: set parser to ignore merge commits by default BREAKING CHANGE: The configuration setting `commit_parser_options.ignore_merge_commits` is now set to `true` by default. The feature to ignore squash commits was introduced in `v9.18.0` and was originally set to `false` to prevent unexpected results on a non-breaking update. The ignore merge commits feature prevents additional unnecessary processing on a commit message that likely will not match a commit message syntax. Most merge commits are syntactically pre-defined by Git or Remote Version Control System (ex. GitHub, etc.) and do not follow a commit convention (nor should they). The larger issue with merge commits is that they ultimately are a full copy of all the changes that were previously created and committed. The merge commit itself ensures that the previous commit tree is maintained in history, therefore the commit message always exists. If merge commits are parsed, it generally creates duplicate messages that will end up in your changelog, which is less than desired in most cases. If you have previously used the `changelog.exclude_commit_patterns` functionality to ignore merge commit messages then you will want this setting set to `true` to improve parsing speed. You can also now remove the merge commit exclude pattern from the list as well to improve parsing speed. If this functionality is not desired, you will need to update your configuration to change the new setting to `false`. --- src/semantic_release/commit_parser/conventional.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/semantic_release/commit_parser/conventional.py b/src/semantic_release/commit_parser/conventional.py index 267a505d3..25c0ae207 100644 --- a/src/semantic_release/commit_parser/conventional.py +++ b/src/semantic_release/commit_parser/conventional.py @@ -92,8 +92,7 @@ class ConventionalCommitParserOptions(ParserOptions): parse_squash_commits: bool = True """Toggle flag for whether or not to parse squash commits""" - # TODO: breaking change v10, change default to True - ignore_merge_commits: bool = False + ignore_merge_commits: bool = True """Toggle flag for whether or not to ignore merge commits""" @property From d4f128e75e33256c0163fbb475c7c41e18f65147 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 11 Nov 2024 14:41:22 -0700 Subject: [PATCH 109/160] feat(parser-scipy)!: set parser to ignore merge commits by default BREAKING CHANGE: The configuration setting `commit_parser_options.ignore_merge_commits` is now set to `true` by default. The feature to ignore squash commits was introduced in `v9.18.0` and was originally set to `false` to prevent unexpected results on a non-breaking update. The ignore merge commits feature prevents additional unnecessary processing on a commit message that likely will not match a commit message syntax. Most merge commits are syntactically pre-defined by Git or Remote Version Control System (ex. GitHub, etc.) and do not follow a commit convention (nor should they). The larger issue with merge commits is that they ultimately are a full copy of all the changes that were previously created and committed. The merge commit itself ensures that the previous commit tree is maintained in history, therefore the commit message always exists. If merge commits are parsed, it generally creates duplicate messages that will end up in your changelog, which is less than desired in most cases. If you have previously used the `changelog.exclude_commit_patterns` functionality to ignore merge commit messages then you will want this setting set to `true` to improve parsing speed. You can also now remove the merge commit exclude pattern from the list as well to improve parsing speed. If this functionality is not desired, you will need to update your configuration to change the new setting to `false`. --- src/semantic_release/commit_parser/scipy.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/semantic_release/commit_parser/scipy.py b/src/semantic_release/commit_parser/scipy.py index 3615fff94..ba5a2f358 100644 --- a/src/semantic_release/commit_parser/scipy.py +++ b/src/semantic_release/commit_parser/scipy.py @@ -150,8 +150,7 @@ class ScipyParserOptions(ParserOptions): parse_squash_commits: bool = True """Toggle flag for whether or not to parse squash commits""" - # TODO: breaking change v10, change default to True - ignore_merge_commits: bool = False + ignore_merge_commits: bool = True """Toggle flag for whether or not to ignore merge commits""" @property From 8a5152573b9175f01be06d0c4531ea0ca4de8dd4 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 18 May 2025 17:20:36 -0600 Subject: [PATCH 110/160] feat(parser-emoji)!: set parser to ignore merge commits by default BREAKING CHANGE: The configuration setting `commit_parser_options.ignore_merge_commits` is now set to `true` by default. The feature to ignore squash commits was introduced in `v9.18.0` and was originally set to `false` to prevent unexpected results on a non-breaking update. The ignore merge commits feature prevents additional unnecessary processing on a commit message that likely will not match a commit message syntax. Most merge commits are syntactically pre-defined by Git or Remote Version Control System (ex. GitHub, etc.) and do not follow a commit convention (nor should they). The larger issue with merge commits is that they ultimately are a full copy of all the changes that were previously created and committed. The merge commit itself ensures that the previous commit tree is maintained in history, therefore the commit message always exists. If merge commits are parsed, it generally creates duplicate messages that will end up in your changelog, which is less than desired in most cases. If you have previously used the `changelog.exclude_commit_patterns` functionality to ignore merge commit messages then you will want this setting set to `true` to improve parsing speed. You can also now remove the merge commit exclude pattern from the list as well to improve parsing speed. If this functionality is not desired, you will need to update your configuration to change the new setting to `false`. --- src/semantic_release/commit_parser/emoji.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/semantic_release/commit_parser/emoji.py b/src/semantic_release/commit_parser/emoji.py index 6fe965b02..81afaf4d7 100644 --- a/src/semantic_release/commit_parser/emoji.py +++ b/src/semantic_release/commit_parser/emoji.py @@ -97,8 +97,7 @@ class EmojiParserOptions(ParserOptions): parse_squash_commits: bool = True """Toggle flag for whether or not to parse squash commits""" - # TODO: breaking change v10, change default to True - ignore_merge_commits: bool = False + ignore_merge_commits: bool = True """Toggle flag for whether or not to ignore merge commits""" @property From 92b42febba1a69f7bac2bacf465862b7c1106d32 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 18 May 2025 10:59:53 -0600 Subject: [PATCH 111/160] ci(validate): increase defined terminal width for pytest results --- .github/workflows/validate.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index e5a9b842d..a55e468fd 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -180,6 +180,8 @@ jobs: - name: Test | Run pytest -m unit --comprehensive id: tests + env: + COLUMNS: 150 run: | pytest \ -vv \ @@ -248,6 +250,8 @@ jobs: - name: Test | Run pytest -m e2e --comprehensive id: tests + env: + COLUMNS: 150 run: | pytest \ -vv \ @@ -340,12 +344,15 @@ jobs: - name: Test | Run pytest -m e2e id: tests shell: pwsh + # env: # Required for GitPython to work on Windows because of getpass.getuser() # USERNAME: "runneradmin" + # COLUMNS: 150 # Because GHA is currently broken on Windows to pass these varables, we do it manually run: | $env:USERNAME = "runneradmin" + $env:COLUMNS = 150 pytest ` -vv ` -nauto ` From 2cbd3f13d54d1ee9b7f11f522613f0b90106cbe5 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 17 May 2025 21:31:33 -0600 Subject: [PATCH 112/160] refactor(parser-conventional): ensures squash commits are interpreted correctly refactored to avoid interpreting a `fix: #123` with the start of another commit since the prefix is essentially the same. --- .../commit_parser/conventional.py | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/semantic_release/commit_parser/conventional.py b/src/semantic_release/commit_parser/conventional.py index 25c0ae207..b4eac746c 100644 --- a/src/semantic_release/commit_parser/conventional.py +++ b/src/semantic_release/commit_parser/conventional.py @@ -147,24 +147,23 @@ def __init__(self, options: ConventionalCommitParserOptions | None = None) -> No ) ) from err - self.commit_prefix = regexp( + self.commit_subject = regexp( str.join( "", [ f"^{commit_type_pattern.pattern}", r"(?:\((?P[^\n]+)\))?", - # TODO: remove ! support as it is not part of the angular commit spec (its part of conventional commits spec) r"(?P!)?:\s+", + r"(?P[^\n]+)", ], ) ) - self.re_parser = regexp( + self.commit_msg_pattern = regexp( str.join( "", [ - self.commit_prefix.pattern, - r"(?P[^\n]+)", + self.commit_subject.pattern, r"(?:\n\n(?P.+))?", # commit body ], ), @@ -268,7 +267,7 @@ def commit_body_components_separator( return accumulator def parse_message(self, message: str) -> ParsedMessageResult | None: - if not (parsed := self.re_parser.match(message)): + if not (parsed := self.commit_msg_pattern.match(message)): return None parsed_break = parsed.group("break") @@ -467,25 +466,31 @@ def _find_squashed_commits_in_str(self, message: str) -> list[str]: if not clean_paragraph.strip(): continue - # Check if the paragraph is the start of a new angular commit - if not self.commit_prefix.search(clean_paragraph): - if not separate_commit_msgs and not current_msg: - # if there are no separate commit messages and no current message - # then this is the first commit message - current_msg = dedent(clean_paragraph) - continue - - # append the paragraph as part of the previous commit message + # Check if the paragraph is the start of a new conventional commit + # Note: that we check that the subject has more than one word to differentiate from + # a closing footer (e.g. "fix: #123", or "fix: ABC-123") + if (match := self.commit_subject.search(clean_paragraph)) and len( + match.group("subject").split(" ") + ) > 1: + # Since we found the start of the new commit, store any previous commit + # message separately and start the new commit message if current_msg: - current_msg += f"\n\n{dedent(clean_paragraph)}" - # else: drop the paragraph + separate_commit_msgs.append(current_msg) + + current_msg = clean_paragraph + continue + + if not separate_commit_msgs and not current_msg: + # if there are no separate commit messages and no current message + # then this is the first commit message + current_msg = dedent(clean_paragraph) continue - # Since we found the start of the new commit, store any previous commit - # message separately and start the new commit message + # append the paragraph as part of the previous commit message if current_msg: - separate_commit_msgs.append(current_msg) + current_msg += f"\n\n{dedent(clean_paragraph)}" - current_msg = clean_paragraph + # else: drop the paragraph + continue return [*separate_commit_msgs, current_msg] From 8c50c4f603a3479ca366a63556c0c35f0c84cf09 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 18 May 2025 00:28:32 -0600 Subject: [PATCH 113/160] refactor(parser-scipy): ensure that scopes are properly extracted from commit subjects --- src/semantic_release/commit_parser/scipy.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/semantic_release/commit_parser/scipy.py b/src/semantic_release/commit_parser/scipy.py index ba5a2f358..6083d7359 100644 --- a/src/semantic_release/commit_parser/scipy.py +++ b/src/semantic_release/commit_parser/scipy.py @@ -111,16 +111,18 @@ class ScipyParserOptions(ParserOptions): just with different tag names. """ - major_tags: Tuple[str, ...] = ("API",) + major_tags: Tuple[str, ...] = ("API", "DEP") """Commit-type prefixes that should result in a major release bump.""" - minor_tags: Tuple[str, ...] = ("DEP", "DEV", "ENH", "REV", "FEAT") + minor_tags: Tuple[str, ...] = ("ENH", "FEAT") """Commit-type prefixes that should result in a minor release bump.""" patch_tags: Tuple[str, ...] = ("BLD", "BUG", "MAINT") """Commit-type prefixes that should result in a patch release bump.""" other_allowed_tags: Tuple[str, ...] = ( + # "REV", # Revert commits are NOT Currently Supported + "DEV", "BENCH", "DOC", "STY", @@ -207,13 +209,13 @@ def __init__(self, options: ScipyParserOptions | None = None) -> None: "", [ f"^{commit_type_pattern.pattern}", - r"(?:\((?P[^\n]+)\))?", - r":\s+", + r"(?::[\t ]*(?P[^:\n]+))?", + r":[\t ]+", ], ) ) - self.re_parser = regexp( + self.commit_msg_pattern = regexp( str.join( "", [ @@ -318,7 +320,7 @@ def commit_body_components_separator( return accumulator def parse_message(self, message: str) -> ParsedMessageResult | None: - if not (parsed := self.re_parser.match(message)): + if not (parsed := self.commit_msg_pattern.match(message)): return None parsed_scope = parsed.group("scope") or "" From 48b3de0af2ac4f0f0d258a5d39c5cc960c314df1 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 18 May 2025 18:26:20 -0600 Subject: [PATCH 114/160] refactor(parser-emoji): ensures that merge request numbers are removed from subject lines --- src/semantic_release/commit_parser/emoji.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/semantic_release/commit_parser/emoji.py b/src/semantic_release/commit_parser/emoji.py index 81afaf4d7..2199c6948 100644 --- a/src/semantic_release/commit_parser/emoji.py +++ b/src/semantic_release/commit_parser/emoji.py @@ -268,7 +268,9 @@ def commit_body_components_separator( return accumulator def parse_message(self, message: str) -> ParsedMessageResult: - subject = message.split("\n", maxsplit=1)[0] + msg_parts = message.split("\n", maxsplit=1) + subject = msg_parts[0] + msg_body = msg_parts[1] if len(msg_parts) > 1 else "" linked_merge_request = "" if mr_match := self.mr_selector.search(subject): @@ -287,7 +289,10 @@ def parse_message(self, message: str) -> ParsedMessageResult: # All emojis will remain part of the returned description body_components: dict[str, list[str]] = reduce( self.commit_body_components_separator, - parse_paragraphs(message), + [ + subject, + *parse_paragraphs(msg_body), + ], { "descriptions": [], "notices": [], @@ -302,11 +307,9 @@ def parse_message(self, message: str) -> ParsedMessageResult: type=primary_emoji, category=primary_emoji, scope=parsed_scope, - # TODO: breaking change v10, removes breaking change footers from descriptions - # descriptions=( - # descriptions[:1] if level_bump is LevelBump.MAJOR else descriptions - # ) - descriptions=descriptions, + descriptions=( + descriptions[:1] if level_bump is LevelBump.MAJOR else descriptions + ), breaking_descriptions=( descriptions[1:] if level_bump is LevelBump.MAJOR else () ), From eb0a9eb8af6a4af6f5ff17b135be5a01e722d95a Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 17 May 2025 20:27:58 -0600 Subject: [PATCH 115/160] test(conftest): update commit object creation to prevent test failures --- tests/conftest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/conftest.py b/tests/conftest.py index 933a0cfd1..2897bbac7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -396,6 +396,7 @@ def _make_commit(message: str) -> Commit: authored_date=commit_timestamp, committer=commit_author, committed_date=commit_timestamp, + parents=[], ) return _make_commit From 802c1902ac4856ee470cf3d7526db27c71142b19 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 17 May 2025 21:32:32 -0600 Subject: [PATCH 116/160] test(parser-conventional): update unit testing to match expected output of modified parser --- .../commit_parser/test_conventional.py | 32 ++++++------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/tests/unit/semantic_release/commit_parser/test_conventional.py b/tests/unit/semantic_release/commit_parser/test_conventional.py index 078e1ecd5..02cd4f5de 100644 --- a/tests/unit/semantic_release/commit_parser/test_conventional.py +++ b/tests/unit/semantic_release/commit_parser/test_conventional.py @@ -1,3 +1,4 @@ +# ruff: noqa: SIM300 from __future__ import annotations from textwrap import dedent @@ -80,7 +81,6 @@ def test_parser_raises_unknown_message_style( "some commit subject", "An additional description", "Second paragraph with multiple lines that will be condensed", - "Resolves: #12", "Signed-off-by: author ", ], "linked_issues": ("#12",), @@ -127,7 +127,6 @@ def test_parser_raises_unknown_message_style( "some commit subject", "An additional description", "Second paragraph with multiple lines that will be condensed", - "Resolves: #12", "Signed-off-by: author ", ], "linked_issues": ("#12",), @@ -154,8 +153,6 @@ def test_parser_raises_unknown_message_style( "scope": "cli", "descriptions": [ "changed option name", - "BREAKING CHANGE: A breaking change description", - "Closes: #555", # This is a bit unusual but its because there is no identifier that will # identify this as a separate commit so it gets included in the previous commit "invalid non-conventional formatted commit", @@ -255,7 +252,6 @@ def test_parser_squashed_commit_bitbucket_squash_style( "some commit subject", "An additional description", "Second paragraph with multiple lines that will be condensed", - "Resolves: #12", "Signed-off-by: author ", ], "linked_issues": ("#12",), @@ -320,7 +316,6 @@ def test_parser_squashed_commit_bitbucket_squash_style( "some commit subject", "An additional description", "Second paragraph with multiple lines that will be condensed", - "Resolves: #12", "Signed-off-by: author ", ], "linked_issues": ("#12",), @@ -344,8 +339,6 @@ def test_parser_squashed_commit_bitbucket_squash_style( "scope": "cli", "descriptions": [ "changed option name", - "BREAKING CHANGE: A breaking change description", - "Closes: #555", ], "breaking_descriptions": [ "A breaking change description", @@ -432,11 +425,9 @@ def test_parser_squashed_commit_git_squash_style( "type": "bug fixes", "scope": "release-config", "descriptions": [ - # TODO: v10 removal of PR number from subject - "some commit subject (#10)", + "some commit subject", "An additional description", "Second paragraph with multiple lines that will be condensed", - "Resolves: #12", "Signed-off-by: author ", ], "linked_issues": ("#12",), @@ -477,11 +468,9 @@ def test_parser_squashed_commit_git_squash_style( "type": "bug fixes", "scope": "release-config", "descriptions": [ - # TODO: v10 removal of PR number from subject - "some commit subject (#10)", + "some commit subject", "An additional description", "Second paragraph with multiple lines that will be condensed", - "Resolves: #12", "Signed-off-by: author ", ], "linked_issues": ("#12",), @@ -508,8 +497,6 @@ def test_parser_squashed_commit_git_squash_style( "scope": "cli", "descriptions": [ "changed option name", - "BREAKING CHANGE: A breaking change description", - "Closes: #555", # This is a bit unusual but its because there is no identifier that will # identify this as a separate commit so it gets included in the previous commit "* invalid non-conventional formatted commit", @@ -689,7 +676,7 @@ def test_parser_return_scope_from_commit_message( ), ( f"fix(tox): fix env \n\n{_long_text}\n\n{_footer}", - ["fix env ", _long_text, _footer], + ["fix env ", _long_text], ), ("fix: superfix", ["superfix"]), ], @@ -717,23 +704,23 @@ def test_parser_return_subject_from_commit_message( # GitHub, Gitea style ( "feat(parser): add emoji parser (#123)", - "add emoji parser (#123)", + "add emoji parser", "#123", ), # GitLab style ( "fix(parser): fix regex in conventional parser (!456)", - "fix regex in conventional parser (!456)", + "fix regex in conventional parser", "!456", ), # BitBucket style ( "feat(parser): add emoji parser (pull request #123)", - "add emoji parser (pull request #123)", + "add emoji parser", "#123", ), # Both a linked merge request and an issue footer (should return the linked merge request) - ("fix: superfix (#123)\n\nCloses: #400", "superfix (#123)", "#123"), + ("fix: superfix (#123)\n\nCloses: #400", "superfix", "#123"), # None ("fix: superfix", "superfix", ""), # None but includes an issue footer it should not be considered a linked merge request @@ -760,7 +747,6 @@ def test_parser_return_linked_merge_request_from_commit_message( @pytest.mark.parametrize( "message, linked_issues", - # TODO: in v10, we will remove the issue reference footers from the descriptions [ *[ # GitHub, Gitea, GitLab style @@ -1040,7 +1026,7 @@ def test_parser_return_linked_issues_from_commit_message( parsed_results = default_conventional_parser.parse(make_commit_obj(message)) assert isinstance(parsed_results, Iterable) - assert len(parsed_results) == 1 + assert 1 == len(parsed_results) result = next(iter(parsed_results)) assert isinstance(result, ParsedCommit) From 261164e457ad5e855638f9e8169a6c924844ebd3 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 17 May 2025 21:35:45 -0600 Subject: [PATCH 117/160] test(parser-scipy): update unit testing to match expected output of modified parser --- tests/fixtures/scipy.py | 6 +- .../commit_parser/test_scipy.py | 570 ++++++++++++------ 2 files changed, 400 insertions(+), 176 deletions(-) diff --git a/tests/fixtures/scipy.py b/tests/fixtures/scipy.py index f9c704090..3d7b07127 100644 --- a/tests/fixtures/scipy.py +++ b/tests/fixtures/scipy.py @@ -99,6 +99,7 @@ def scipy_nonparseable_commits() -> list[str]: def scipy_chore_subjects(scipy_chore_commit_types: list[str]) -> list[str]: subjects = { "BENCH": "disable very slow benchmark in optimize_milp.py", + "DEV": "add unicode check to pre-commit-hook", "DOC": "change approx_fprime doctest (#20568)", "STY": "fixed ruff & mypy issues", "TST": "Skip Cython tests for editable installs", @@ -125,10 +126,8 @@ def scipy_patch_subjects(scipy_patch_commit_types: list[str]) -> list[str]: @pytest.fixture(scope="session") def scipy_minor_subjects(scipy_minor_commit_types: list[str]) -> list[str]: subjects = { - "DEP": "stats: switch kendalltau to kwarg-only, remove initial_lexsort", - "DEV": "add unicode check to pre-commit-hook", "ENH": "stats.ttest_1samp: add array-API support (#20545)", - "REV": "reverted a previous commit", + # "REV": "reverted a previous commit", "FEAT": "added a new feature", } # Test fixture modification failure prevention @@ -140,6 +139,7 @@ def scipy_minor_subjects(scipy_minor_commit_types: list[str]) -> list[str]: def scipy_major_subjects(scipy_major_commit_types: list[str]) -> list[str]: subjects = { "API": "dropped support for python 3.7", + "DEP": "stats: switch kendalltau to kwarg-only, remove initial_lexsort", } # Test fixture modification failure prevention assert len(subjects.keys()) == len(scipy_major_commit_types) diff --git a/tests/unit/semantic_release/commit_parser/test_scipy.py b/tests/unit/semantic_release/commit_parser/test_scipy.py index 2c15fca6f..46f70b211 100644 --- a/tests/unit/semantic_release/commit_parser/test_scipy.py +++ b/tests/unit/semantic_release/commit_parser/test_scipy.py @@ -1,3 +1,4 @@ +# ruff: noqa: SIM300 from __future__ import annotations from re import compile as regexp @@ -9,7 +10,6 @@ from semantic_release.commit_parser.scipy import ( ScipyCommitParser, ScipyParserOptions, - tag_to_section, ) from semantic_release.commit_parser.token import ParsedCommit, ParseError from semantic_release.enums import LevelBump @@ -37,163 +37,400 @@ def test_parser_raises_unknown_message_style( assert isinstance(result, ParseError) -def test_valid_scipy_parsed_chore_commits( - default_scipy_parser: ScipyCommitParser, - make_commit_obj: MakeCommitObjFn, - scipy_chore_commit_parts: list[tuple[str, str, list[str]]], - scipy_chore_commits: list[str], -): - expected_parts = scipy_chore_commit_parts - - for i, full_commit_msg in enumerate(scipy_chore_commits): - (commit_type, subject, commit_bodies) = expected_parts[i] - commit_bodies = [unwordwrap.sub(" ", body).rstrip() for body in commit_bodies] - expected_type = tag_to_section[commit_type] - expected_descriptions = [ - subject, - *[body.rstrip() for body in commit_bodies if body], - ] - expected_brk_desc: list[str] = [] - - commit = make_commit_obj(full_commit_msg) - parsed_results = default_scipy_parser.parse(commit) - assert isinstance(parsed_results, Iterable) - - result = next(iter(parsed_results)) - assert isinstance(result, ParsedCommit) - assert LevelBump.NO_RELEASE is result.bump - assert expected_type == result.type - assert expected_descriptions == result.descriptions - assert expected_brk_desc == result.breaking_descriptions - assert not result.scope +@pytest.mark.parametrize( + "commit_message, expected_commit_details", + [ + pytest.param( + commit_message, + expected_commit_details, + id=test_id, + ) + for test_id, commit_message, expected_commit_details in [ + ( + "Chore Type: Benchmark related", + dedent( + """\ + BENCH:optimize_milp.py: add new benchmark + Benchmarks the performance of the MILP solver + """ + ), + { + "bump": LevelBump.NO_RELEASE, + "type": "none", + "scope": "optimize_milp.py", + "descriptions": [ + "add new benchmark", + "Benchmarks the performance of the MILP solver", + ], + "breaking_descriptions": [], + "linked_issues": (), + "linked_merge_request": "", + }, + ), + ( + "Chore Type: Dev Tool Related", + dedent( + """\ + DEV: add unicode check to pre-commit hook + """ + ), + { + "bump": LevelBump.NO_RELEASE, + "type": "none", + "scope": "", + "descriptions": [ + "add unicode check to pre-commit hook", + ], + "breaking_descriptions": [], + "linked_issues": (), + "linked_merge_request": "", + }, + ), + ( + "Chore Type: documentation related", + dedent( + """\ + DOC: change approx_fprime doctest (#20568) + """ + ), + { + "bump": LevelBump.NO_RELEASE, + "type": "documentation", + "scope": "", + "descriptions": [ + "change approx_fprime doctest", + ], + "breaking_descriptions": [], + "linked_issues": (), + "linked_merge_request": "#20568", + }, + ), + ( + "Chore Type: style related", + dedent( + """\ + STY: fixed ruff & mypy issues + """ + ), + { + "bump": LevelBump.NO_RELEASE, + "type": "none", + "scope": "", + "descriptions": [ + "fixed ruff & mypy issues", + ], + "breaking_descriptions": [], + "linked_issues": (), + "linked_merge_request": "", + }, + ), + ( + "Chore Type: Test related", + dedent( + """\ + TST: Skip Cython tests for editable installs + """ + ), + { + "bump": LevelBump.NO_RELEASE, + "type": "none", + "scope": "", + "descriptions": [ + "Skip Cython tests for editable installs", + ], + "breaking_descriptions": [], + "linked_issues": (), + "linked_merge_request": "", + }, + ), + ( + "Chore Type: Test related", + dedent( + """\ + TEST: Skip Cython tests for editable installs + """ + ), + { + "bump": LevelBump.NO_RELEASE, + "type": "none", + "scope": "", + "descriptions": [ + "Skip Cython tests for editable installs", + ], + "breaking_descriptions": [], + "linked_issues": (), + "linked_merge_request": "", + }, + ), + ( + "Chore Type: Release related", + dedent( + """\ + REL: set version to 1.0.0 + """ + ), + { + "bump": LevelBump.NO_RELEASE, + "type": "none", + "scope": "", + "descriptions": [ + "set version to 1.0.0", + ], + "breaking_descriptions": [], + "linked_issues": (), + "linked_merge_request": "", + }, + ), + ( + "Patch Type: Build related", + dedent( + """\ + BLD: move the optimize build steps earlier into the build sequence + """ + ), + { + "bump": LevelBump.PATCH, + "type": "fix", + "scope": "", + "descriptions": [ + "move the optimize build steps earlier into the build sequence", + ], + "breaking_descriptions": [], + "linked_issues": (), + "linked_merge_request": "", + }, + ), + ( + "Patch Type: Bug fix", + dedent( + """\ + BUG: Fix invalid default bracket selection in _bracket_minimum (#20563) + """ + ), + { + "bump": LevelBump.PATCH, + "type": "fix", + "scope": "", + "descriptions": [ + "Fix invalid default bracket selection in _bracket_minimum", + ], + "breaking_descriptions": [], + "linked_issues": (), + "linked_merge_request": "#20563", + }, + ), + ( + "Patch Type: Maintenance", + dedent( + """\ + MAINT: optimize.linprog: fix bug when integrality is a list of all zeros (#20586) -def test_valid_scipy_parsed_patch_commits( - default_scipy_parser: ScipyCommitParser, - make_commit_obj: MakeCommitObjFn, - scipy_patch_commit_parts: list[tuple[str, str, list[str]]], - scipy_patch_commits: list[str], -): - expected_parts = scipy_patch_commit_parts - - for i, full_commit_msg in enumerate(scipy_patch_commits): - (commit_type, subject, commit_bodies) = expected_parts[i] - commit_bodies = [unwordwrap.sub(" ", body).rstrip() for body in commit_bodies] - expected_type = tag_to_section[commit_type] - expected_descriptions = [ - subject, - *[body.rstrip() for body in commit_bodies if body], - ] - expected_brk_desc: list[str] = [] + This is a bug fix for the linprog function in the optimize module. - commit = make_commit_obj(full_commit_msg) - parsed_results = default_scipy_parser.parse(commit) - assert isinstance(parsed_results, Iterable) + Closes: #555 + """ + ), + { + "bump": LevelBump.PATCH, + "type": "fix", + "scope": "optimize.linprog", + "descriptions": [ + "fix bug when integrality is a list of all zeros", + "This is a bug fix for the linprog function in the optimize module.", + ], + "breaking_descriptions": [], + "linked_issues": ("#555",), + "linked_merge_request": "#20586", + }, + ), + ( + "Feature Type: Enhancement", + dedent( + """\ + ENH: stats.ttest_1samp: add array-API support (#20545) - result = next(iter(parsed_results)) - assert isinstance(result, ParsedCommit) - assert LevelBump.PATCH is result.bump - assert expected_type == result.type - assert expected_descriptions == result.descriptions - assert expected_brk_desc == result.breaking_descriptions - assert not result.scope + Closes: #1444 + """ + ), + { + "bump": LevelBump.MINOR, + "type": "feature", + "scope": "stats.ttest_1samp", + "descriptions": [ + "add array-API support", + ], + "breaking_descriptions": [], + "linked_issues": ("#1444",), + "linked_merge_request": "#20545", + }, + ), + # ( + # NOT CURRENTLY SUPPORTED + # "Feature Type: Revert", + # dedent( + # """\ + # REV: revert "ENH: add new feature (#20545)" + # This reverts commit 63ec09b9e844e616dcaa7bae35a0b66671b59fbb. + # """ + # ), + # { + # "bump": LevelBump.MINOR, + # "type": "other", + # "scope": "", + # "descriptions": [ + # 'revert "ENH: add new feature (#20545)"', + # "This reverts commit 63ec09b9e844e616dcaa7bae35a0b66671b59fbb.", + # ], + # "breaking_descriptions": [], + # "linked_issues": (), + # "linked_merge_request": "", + # }, + # ), + ( + "Feature Type: FEAT", + dedent( + """\ + FEAT: add new feature (#20545) + """ + ), + { + "bump": LevelBump.MINOR, + "type": "feature", + "scope": "", + "descriptions": [ + "add new feature", + ], + "breaking_descriptions": [], + "linked_issues": (), + "linked_merge_request": "#20545", + }, + ), + ( + "Breaking Type: API", + dedent( + """\ + API: dropped support for Python 3.7 + Users of Python 3.7 should use version 1.0.0 or try to upgrade to Python 3.8 + or later to continue using this package. + """ + ), + { + "bump": LevelBump.MAJOR, + "type": "breaking", + "scope": "", + "descriptions": [ + "dropped support for Python 3.7", + ], + "breaking_descriptions": [ + "Users of Python 3.7 should use version 1.0.0 or try to upgrade to Python 3.8 or later to continue using this package.", + ], + "linked_issues": (), + "linked_merge_request": "", + }, + ), + ( + "Breaking Type: Deprecate", + dedent( + """\ + DEP: deprecated the limprog function -def test_valid_scipy_parsed_minor_commits( + The linprog function is deprecated and will be removed in a future release. + Use the new linprog2 function instead. + """ + ), + { + "bump": LevelBump.MAJOR, + "type": "breaking", + "scope": "", + "descriptions": [ + "deprecated the limprog function", + ], + "breaking_descriptions": [ + "The linprog function is deprecated and will be removed in a future release. Use the new linprog2 function instead.", + ], + "linked_issues": (), + "linked_merge_request": "", + }, + ), + ] + ], +) +def test_scipy_parser_parses_commit_message( default_scipy_parser: ScipyCommitParser, make_commit_obj: MakeCommitObjFn, - scipy_minor_commit_parts: list[tuple[str, str, list[str]]], - scipy_minor_commits: list[str], + commit_message: str, + expected_commit_details: dict | None, ): - expected_parts = scipy_minor_commit_parts - - for i, full_commit_msg in enumerate(scipy_minor_commits): - (commit_type, subject, commit_bodies) = expected_parts[i] - commit_bodies = [unwordwrap.sub(" ", body).rstrip() for body in commit_bodies] - expected_type = tag_to_section[commit_type] - expected_descriptions = [ - subject, - *[body for body in commit_bodies if body], - ] - expected_brk_desc: list[str] = [] - - commit = make_commit_obj(full_commit_msg) - parsed_results = default_scipy_parser.parse(commit) - assert isinstance(parsed_results, Iterable) - - result = next(iter(parsed_results)) - assert isinstance(result, ParsedCommit) - assert LevelBump.MINOR is result.bump - assert expected_type == result.type - assert expected_descriptions == result.descriptions - assert expected_brk_desc == result.breaking_descriptions - assert not result.scope + # Setup: Enable squash commit parsing + parser = ScipyCommitParser( + options=ScipyParserOptions( + **{ + **default_scipy_parser.options.__dict__, + "parse_squash_commits": False, + } + ) + ) + # Build the commit object and parse it + the_commit = make_commit_obj(commit_message) + parsed_results = parser.parse(the_commit) -def test_valid_scipy_parsed_major_commits( - default_scipy_parser: ScipyCommitParser, - make_commit_obj: MakeCommitObjFn, - scipy_major_commit_parts: list[tuple[str, str, list[str]]], - scipy_major_commits: list[str], -): - expected_parts = scipy_major_commit_parts - - for i, full_commit_msg in enumerate(scipy_major_commits): - (commit_type, subject, commit_bodies) = expected_parts[i] - commit_bodies = [unwordwrap.sub(" ", body).rstrip() for body in commit_bodies] - expected_type = tag_to_section[commit_type] - expected_descriptions = [ - subject, - *[body for body in commit_bodies if body], - ] - brkg_prefix = "BREAKING CHANGE: " - expected_brk_desc = [ - # TODO: Python 3.8 limitation, change to removeprefix() for 3.9+ - block[block.startswith(brkg_prefix) and len(brkg_prefix) :] - # block.removeprefix("BREAKING CHANGE: ") - for block in commit_bodies - if block.startswith("BREAKING CHANGE") - ] + # Validate the results + assert isinstance(parsed_results, Iterable) + assert 1 == len( + parsed_results + ), f"Expected 1 parsed result, but got {len(parsed_results)}" - commit = make_commit_obj(full_commit_msg) - parsed_results = default_scipy_parser.parse(commit) + result = next(iter(parsed_results)) - assert isinstance(parsed_results, Iterable) - assert len(parsed_results) == 1 + if expected_commit_details is None: + assert isinstance(result, ParseError) + return - result = next(iter(parsed_results)) - assert isinstance(result, ParsedCommit) - assert LevelBump.MAJOR is result.bump - assert expected_type == result.type - assert expected_descriptions == result.descriptions - assert expected_brk_desc == result.breaking_descriptions - assert not result.scope + assert isinstance(result, ParsedCommit) + # Required + assert expected_commit_details["bump"] == result.bump + assert expected_commit_details["type"] == result.type + # Optional + assert expected_commit_details.get("scope", "") == result.scope + # TODO: v11 change to tuples + assert expected_commit_details.get("descriptions", []) == result.descriptions + assert ( + expected_commit_details.get("breaking_descriptions", []) + == result.breaking_descriptions + ) + assert expected_commit_details.get("linked_issues", ()) == result.linked_issues + assert ( + expected_commit_details.get("linked_merge_request", "") + == result.linked_merge_request + ) @pytest.mark.parametrize( "message, subject, merge_request_number", - # TODO: in v10, we will remove the merge request number from the subject line [ # GitHub, Gitea style ( "ENH: add new feature (#123)", - "add new feature (#123)", + "add new feature", "#123", ), # GitLab style ( "BUG: fix regex in parser (!456)", - "fix regex in parser (!456)", + "fix regex in parser", "!456", ), # BitBucket style ( "ENH: add new feature (pull request #123)", - "add new feature (pull request #123)", + "add new feature", "#123", ), # Both a linked merge request and an issue footer (should return the linked merge request) - ("DEP: add dependency (#123)\n\nCloses: #400", "add dependency (#123)", "#123"), + ("DEP: add dependency (#123)\n\nCloses: #400", "add dependency", "#123"), # None ("BUG: superfix", "superfix", ""), # None but includes an issue footer it should not be considered a linked merge request @@ -233,7 +470,7 @@ def test_parser_return_linked_merge_request_from_commit_message( """\ Merged in feat/my-awesome-stuff (pull request #10) - BUG(release-config): some commit subject + BUG: release-config: some commit subject An additional description @@ -254,7 +491,6 @@ def test_parser_return_linked_merge_request_from_commit_message( "some commit subject", "An additional description", "Second paragraph with multiple lines that will be condensed", - "Resolves: #12", "Signed-off-by: author ", ], "linked_issues": ("#12",), @@ -268,7 +504,7 @@ def test_parser_return_linked_merge_request_from_commit_message( """\ Merged in feat/my-awesome-stuff (pull request #10) - BUG(release-config): some commit subject + BUG:release-config: some commit subject An additional description @@ -280,11 +516,11 @@ def test_parser_return_linked_merge_request_from_commit_message( ENH: implemented searching gizmos by keyword - DOC(parser): add new parser pattern + DOC: parser: add new parser pattern - MAINT(cli)!: changed option name + API:cli: changed option name - BREAKING CHANGE: A breaking change description + A breaking change description Closes: #555 @@ -301,7 +537,6 @@ def test_parser_return_linked_merge_request_from_commit_message( "some commit subject", "An additional description", "Second paragraph with multiple lines that will be condensed", - "Resolves: #12", "Signed-off-by: author ", ], "linked_issues": ("#12",), @@ -324,18 +559,16 @@ def test_parser_return_linked_merge_request_from_commit_message( }, { "bump": LevelBump.MAJOR, - "type": "fix", + "type": "breaking", "scope": "cli", "descriptions": [ "changed option name", - "BREAKING CHANGE: A breaking change description", - "Closes: #555", - # This is a bit unusual but its because there is no identifier that will - # identify this as a separate commit so it gets included in the previous commit - "invalid non-conventional formatted commit", ], "breaking_descriptions": [ "A breaking change description", + # This is a bit unusual but its because there is no identifier that will + # identify this as a separate commit so it gets included in the previous commit + "invalid non-conventional formatted commit", ], "linked_issues": ("#555",), "linked_merge_request": "#10", @@ -408,7 +641,7 @@ def test_parser_squashed_commit_bitbucket_squash_style( Author: author Date: Sun Jan 19 12:05:23 2025 +0000 - BUG(release-config): some commit subject + BUG: release-config: some commit subject An additional description @@ -429,7 +662,6 @@ def test_parser_squashed_commit_bitbucket_squash_style( "some commit subject", "An additional description", "Second paragraph with multiple lines that will be condensed", - "Resolves: #12", "Signed-off-by: author ", ], "linked_issues": ("#12",), @@ -446,7 +678,7 @@ def test_parser_squashed_commit_bitbucket_squash_style( Author: author Date: Sun Jan 19 12:05:23 2025 +0000 - BUG(release-config): some commit subject + BUG: release-config: some commit subject An additional description @@ -466,15 +698,15 @@ def test_parser_squashed_commit_bitbucket_squash_style( Author: author Date: Sat Jan 18 10:13:53 2025 +0000 - DOC(parser): add new parser pattern + DOC: parser: add new parser pattern commit 5f0292fb5a88c3a46e4a02bec35b85f5228e8e51 Author: author Date: Sat Jan 18 10:13:53 2025 +0000 - MAINT(cli): changed option name + API:cli: changed option name - BREAKING CHANGE: A breaking change description + A breaking change description Closes: #555 @@ -494,7 +726,6 @@ def test_parser_squashed_commit_bitbucket_squash_style( "some commit subject", "An additional description", "Second paragraph with multiple lines that will be condensed", - "Resolves: #12", "Signed-off-by: author ", ], "linked_issues": ("#12",), @@ -514,12 +745,10 @@ def test_parser_squashed_commit_bitbucket_squash_style( }, { "bump": LevelBump.MAJOR, - "type": "fix", + "type": "breaking", "scope": "cli", "descriptions": [ "changed option name", - "BREAKING CHANGE: A breaking change description", - "Closes: #555", ], "breaking_descriptions": [ "A breaking change description", @@ -589,7 +818,7 @@ def test_parser_squashed_commit_git_squash_style( "Single commit squashed via GitHub PR resolution", dedent( """\ - BUG(release-config): some commit subject (#10) + BUG: release-config: some commit subject (#10) An additional description @@ -606,10 +835,9 @@ def test_parser_squashed_commit_git_squash_style( "type": "fix", "scope": "release-config", "descriptions": [ - "some commit subject (#10)", + "some commit subject", "An additional description", "Second paragraph with multiple lines that will be condensed", - "Resolves: #12", "Signed-off-by: author ", ], "linked_issues": ("#12",), @@ -621,7 +849,7 @@ def test_parser_squashed_commit_git_squash_style( "Multiple commits squashed via GitHub PR resolution", dedent( """\ - BUG(release-config): some commit subject (#10) + BUG: release-config: some commit subject (#10) An additional description @@ -633,13 +861,13 @@ def test_parser_squashed_commit_git_squash_style( * ENH: implemented searching gizmos by keyword - * DOC(parser): add new parser pattern + * DOC: parser: add new parser pattern - * MAINT(cli)!: changed option name + * API:cli: changed option name - BREAKING CHANGE: A breaking change description + A breaking change description - Closes: #555 + Closes: #555 * invalid non-conventional formatted commit """ @@ -650,11 +878,9 @@ def test_parser_squashed_commit_git_squash_style( "type": "fix", "scope": "release-config", "descriptions": [ - # TODO: v10 removal of PR number from subject - "some commit subject (#10)", + "some commit subject", "An additional description", "Second paragraph with multiple lines that will be condensed", - "Resolves: #12", "Signed-off-by: author ", ], "linked_issues": ("#12",), @@ -677,18 +903,16 @@ def test_parser_squashed_commit_git_squash_style( }, { "bump": LevelBump.MAJOR, - "type": "fix", + "type": "breaking", "scope": "cli", "descriptions": [ "changed option name", - "BREAKING CHANGE: A breaking change description", - "Closes: #555", - # This is a bit unusual but its because there is no identifier that will - # identify this as a separate commit so it gets included in the previous commit - "* invalid non-conventional formatted commit", ], "breaking_descriptions": [ "A breaking change description", + # This is a bit unusual but its because there is no identifier that will + # identify this as a separate commit so it gets included in the previous commit + "* invalid non-conventional formatted commit", ], "linked_issues": ("#555",), "linked_merge_request": "#10", @@ -866,7 +1090,7 @@ def test_parser_squashed_commit_github_squash_style( *[ # JIRA style ( - f"ENH(parser): add magic parser\n\n{footer}", + f"ENH: parser: add magic parser\n\n{footer}", linked_issues, ) for footer_prefix in SUPPORTED_ISSUE_CLOSURE_PREFIXES @@ -994,7 +1218,7 @@ def test_parser_squashed_commit_github_squash_style( ], *[ ( - f"ENH(parser): add magic parser\n\n{footer}", + f"ENH: parser: add magic parser\n\n{footer}", linked_issues, ) for footer, linked_issues in [ @@ -1006,13 +1230,13 @@ def test_parser_squashed_commit_github_squash_style( ], ( # Only grabs the issue reference when there is a GitHub PR reference in the subject - "ENH(parser): add magic parser (#123)\n\nCloses: #555", + "ENH: parser: add magic parser (#123)\n\nCloses: #555", ["#555"], ), # Does not grab an issue when there is only a GitHub PR reference in the subject - ("ENH(parser): add magic parser (#123)", []), + ("ENH: parser: add magic parser (#123)", []), # Does not grab an issue when there is only a Bitbucket PR reference in the subject - ("ENH(parser): add magic parser (pull request #123)", []), + ("ENH: parser: add magic parser (pull request #123)", []), ], ) def test_parser_return_linked_issues_from_commit_message( @@ -1044,7 +1268,7 @@ def test_parser_return_linked_issues_from_commit_message( "single notice", dedent( """\ - BUG(parser): fix regex in scipy parser + BUG:parser: fix regex in scipy parser NOTICE: This is a notice """ @@ -1055,7 +1279,7 @@ def test_parser_return_linked_issues_from_commit_message( "multiline notice", dedent( """\ - BUG(parser): fix regex in scipy parser + BUG:parser: fix regex in scipy parser NOTICE: This is a notice that is longer than other notices @@ -1067,7 +1291,7 @@ def test_parser_return_linked_issues_from_commit_message( "multiple notices", dedent( """\ - BUG(parser): fix regex in scipy parser + BUG:parser: fix regex in scipy parser NOTICE: This is a notice @@ -1080,9 +1304,9 @@ def test_parser_return_linked_issues_from_commit_message( "notice with other footer", dedent( """\ - BUG(parser): fix regex in scipy parser + BUG:parser: fix regex in scipy parser - BREAKING CHANGE: This is a breaking change + This is a breaking change NOTICE: This is a notice """ From f54e72b4af39f35cd5acc5e3f219d9fe2da94ed0 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 18 May 2025 18:27:25 -0600 Subject: [PATCH 118/160] test(parser-emoji): update unit testing to match expected output of modified parser --- .../commit_parser/test_emoji.py | 50 +++++-------------- 1 file changed, 12 insertions(+), 38 deletions(-) diff --git a/tests/unit/semantic_release/commit_parser/test_emoji.py b/tests/unit/semantic_release/commit_parser/test_emoji.py index ec2d83a3e..ac7708ebb 100644 --- a/tests/unit/semantic_release/commit_parser/test_emoji.py +++ b/tests/unit/semantic_release/commit_parser/test_emoji.py @@ -23,7 +23,7 @@ ":boom: Breaking changes\n\nMore description\n\nEven more description", LevelBump.MAJOR, ":boom:", - [":boom: Breaking changes", "More description", "Even more description"], + [":boom: Breaking changes"], ["More description", "Even more description"], ), # Minor bump @@ -63,7 +63,7 @@ ":sparkles: Add a new feature\n\n:boom: should not be detected", LevelBump.MINOR, ":sparkles:", - [":sparkles: Add a new feature", ":boom: should not be detected"], + [":sparkles: Add a new feature"], [], ), ], @@ -91,28 +91,27 @@ def test_default_emoji_parser( @pytest.mark.parametrize( "message, subject, merge_request_number", - # TODO: in v10, we will remove the merge request number from the subject line [ # GitHub, Gitea style ( ":sparkles: add new feature (#123)", - ":sparkles: add new feature (#123)", + ":sparkles: add new feature", "#123", ), # GitLab style ( ":bug: fix regex in parser (!456)", - ":bug: fix regex in parser (!456)", + ":bug: fix regex in parser", "!456", ), # BitBucket style ( ":sparkles: add new feature (pull request #123)", - ":sparkles: add new feature (pull request #123)", + ":sparkles: add new feature", "#123", ), # Both a linked merge request and an issue footer (should return the linked merge request) - (":bug: superfix (#123)\n\nCloses: #400", ":bug: superfix (#123)", "#123"), + (":bug: superfix (#123)\n\nCloses: #400", ":bug: superfix", "#123"), # None (":bug: superfix", ":bug: superfix", ""), # None but includes an issue footer it should not be considered a linked merge request @@ -547,9 +546,7 @@ def test_parser_return_release_notices_from_commit_message( { "bump": LevelBump.NO_RELEASE, "type": "Other", - "descriptions": [ - "Merged in feat/my-awesome-stuff (pull request #10)" - ], + "descriptions": ["Merged in feat/my-awesome-stuff"], "linked_merge_request": "#10", }, { @@ -560,7 +557,6 @@ def test_parser_return_release_notices_from_commit_message( ":bug:(release-config): some commit subject", "An additional description", "Second paragraph with multiple lines that will be condensed", - "Resolves: #12", "Signed-off-by: author ", ], "linked_issues": ("#12",), @@ -601,9 +597,7 @@ def test_parser_return_release_notices_from_commit_message( { "bump": LevelBump.NO_RELEASE, "type": "Other", - "descriptions": [ - "Merged in feat/my-awesome-stuff (pull request #10)" - ], + "descriptions": ["Merged in feat/my-awesome-stuff"], "linked_merge_request": "#10", }, { @@ -614,7 +608,6 @@ def test_parser_return_release_notices_from_commit_message( ":bug:(release-config): some commit subject", "An additional description", "Second paragraph with multiple lines that will be condensed", - "Resolves: #12", "Signed-off-by: author ", ], "linked_issues": ("#12",), @@ -643,15 +636,9 @@ def test_parser_return_release_notices_from_commit_message( "scope": "", "descriptions": [ ":boom::bug: changed option name", - "A breaking change description", - "Closes: #555", - # This is a bit unusual but its because there is no identifier that will - # identify this as a separate commit so it gets included in the previous commit - "invalid non-conventional formatted commit", ], "breaking_descriptions": [ "A breaking change description", - "Closes: #555", # This is a bit unusual but its because there is no identifier that will # identify this as a separate commit so it gets included in the previous commit "invalid non-conventional formatted commit", @@ -749,7 +736,6 @@ def test_parser_squashed_commit_bitbucket_squash_style( ":bug:(release-config): some commit subject", "An additional description", "Second paragraph with multiple lines that will be condensed", - "Resolves: #12", "Signed-off-by: author ", ], "linked_issues": ("#12",), @@ -814,7 +800,6 @@ def test_parser_squashed_commit_bitbucket_squash_style( ":bug:(release-config): some commit subject", "An additional description", "Second paragraph with multiple lines that will be condensed", - "Resolves: #12", "Signed-off-by: author ", ], "linked_issues": ("#12",), @@ -839,12 +824,9 @@ def test_parser_squashed_commit_bitbucket_squash_style( "type": ":boom:", "descriptions": [ ":boom::bug: changed option name", - "A breaking change description", - "Closes: #555", ], "breaking_descriptions": [ "A breaking change description", - "Closes: #555", ], "linked_issues": ("#555",), }, @@ -933,11 +915,9 @@ def test_parser_squashed_commit_git_squash_style( "type": ":bug:", "scope": "release-config", "descriptions": [ - # TODO: v10 removal of PR number from subject - ":bug:(release-config): some commit subject (#10)", + ":bug:(release-config): some commit subject", "An additional description", "Second paragraph with multiple lines that will be condensed", - "Resolves: #12", "Signed-off-by: author ", ], "linked_issues": ("#12",), @@ -978,11 +958,9 @@ def test_parser_squashed_commit_git_squash_style( "type": ":bug:", "scope": "release-config", "descriptions": [ - # TODO: v10 removal of PR number from subject - ":bug:(release-config): some commit subject (#10)", + ":bug:(release-config): some commit subject", "An additional description", "Second paragraph with multiple lines that will be condensed", - "Resolves: #12", "Signed-off-by: author ", ], "linked_issues": ("#12",), @@ -1011,15 +989,11 @@ def test_parser_squashed_commit_git_squash_style( "scope": "", "descriptions": [ ":boom::bug: changed option name", - "A breaking change description", - "Closes: #555", - # This is a bit unusual but its because there is no identifier that will - # identify this as a separate commit so it gets included in the previous commit - "* invalid non-conventional formatted commit", ], "breaking_descriptions": [ "A breaking change description", - "Closes: #555", + # This is a bit unusual but its because there is no identifier that will + # identify this as a separate commit so it gets included in the previous commit "* invalid non-conventional formatted commit", ], "linked_issues": ("#555",), From 9d51b2a93907f85273944122e9158d3948b887fc Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 18 May 2025 10:57:56 -0600 Subject: [PATCH 119/160] test(fixtures): update history creator fixture to new parser msg cleanup --- tests/fixtures/git_repo.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 048f77b3a..c1796a364 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -167,6 +167,7 @@ def __call__( self, build_definition: Sequence[RepoActions], filter_4_changelog: bool = False, + ignore_merge_commits: bool = False, ) -> RepoDefinition: ... RepoDefinition: TypeAlias = dict[VersionStr, RepoVersionDef] # type: ignore[misc] # mypy is thoroughly confused @@ -1470,6 +1471,7 @@ def get_commits_from_repo_build_def() -> GetCommitsFromRepoBuildDefFn: def _get_commits( build_definition: Sequence[RepoActions], filter_4_changelog: bool = False, + ignore_merge_commits: bool = False, ) -> RepoDefinition: # Extract the commits from the build definition repo_def: RepoDefinition = {} @@ -1494,7 +1496,14 @@ def _get_commits( if "commit_def" in build_step["details"]: commit_def = build_step["details"]["commit_def"] # type: ignore[typeddict-item] - if filter_4_changelog and not commit_def["include_in_changelog"]: + if any( + ( + ignore_merge_commits + and build_step["action"] == RepoActionStep.GIT_MERGE, + filter_4_changelog + and not commit_def["include_in_changelog"], + ) + ): continue commits.append(commit_def) From 68e39a81b6d1e935948815483ee6e022fa40c4b3 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 18 May 2025 10:59:13 -0600 Subject: [PATCH 120/160] test(release-history): update test to handle new default `ignore_merge_commits` setting --- .../changelog/test_release_history.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/unit/semantic_release/changelog/test_release_history.py b/tests/unit/semantic_release/changelog/test_release_history.py index 17b327cfa..2f45bbfb8 100644 --- a/tests/unit/semantic_release/changelog/test_release_history.py +++ b/tests/unit/semantic_release/changelog/test_release_history.py @@ -74,10 +74,7 @@ def _create_release_history_from_repo_def( if commit["category"] not in commits_per_group: commits_per_group[commit["category"]] = [] - commits_per_group[commit["category"]].append( - # TODO: remove the newline when our release history strips whitespace from commit messages - commit["msg"].strip() + "\n" - ) + commits_per_group[commit["category"]].append(commit["msg"].strip()) if version_str == "Unreleased": unreleased_history = commits_per_group @@ -87,7 +84,9 @@ def _create_release_history_from_repo_def( version = Version.parse(version_str) # add the PSR version commit message - commits_per_group["Unknown"].append(COMMIT_MESSAGE.format(version=version)) + commits_per_group["Unknown"].append( + COMMIT_MESSAGE.format(version=version).strip() + ) # store the organized commits for this version released_history[version] = commits_per_group @@ -132,7 +131,10 @@ def test_release_history( ): repo = repo_result["repo"] expected_release_history = create_release_history_from_repo_def( - get_commits_from_repo_build_def(repo_result["definition"]) + get_commits_from_repo_build_def( + repo_result["definition"], + ignore_merge_commits=default_conventional_parser.options.ignore_merge_commits, + ) ) expected_released_versions = sorted( map(str, expected_release_history.released.keys()) @@ -179,7 +181,7 @@ def test_release_history( "\n---\n", sorted( [ - msg + str(msg).strip() for bucket in [ CONVENTIONAL_COMMITS_MINOR[::-1], *expected_release_history.unreleased.values(), From c9a8ecfb746e06e9c0ea78495d4f6cf3414323e2 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 18 May 2025 11:22:09 -0600 Subject: [PATCH 121/160] test(fixtures): update changelog generator to remove commit bodies --- tests/fixtures/git_repo.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index c1796a364..1c6a56207 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -1685,10 +1685,11 @@ def build_version_entry_markdown( else: commit_cl_desc = f"{commit_cl_desc} {sha_link}\n" - if len(descriptions) > 1: - commit_cl_desc += ( - "\n" + str.join("\n\n", [*descriptions[1:]]) + "\n" - ) + # COMMENTED out for v10 as the defualt changelog now only writes the subject line + # if len(descriptions) > 1: + # commit_cl_desc += ( + # "\n" + str.join("\n\n", [*descriptions[1:]]) + "\n" + # ) # Add commits to section if commit_cl_desc not in section_bullets: @@ -1798,10 +1799,11 @@ def build_version_entry_restructured_text( else: commit_cl_desc = f"{commit_cl_desc} {sha_link}\n" - if len(descriptions) > 1: - commit_cl_desc += ( - "\n" + str.join("\n\n", [*descriptions[1:]]) + "\n" - ) + # COMMENTED out for v10 as the defualt changelog now only writes the subject line + # if len(descriptions) > 1: + # commit_cl_desc += ( + # "\n" + str.join("\n\n", [*descriptions[1:]]) + "\n" + # ) # Add commits to section if commit_cl_desc not in section_bullets: From 6e2631cbfed29366603d169561b12a76719975e8 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 18 May 2025 11:39:55 -0600 Subject: [PATCH 122/160] test(fixtures): update commit object creator as PSR now removes MRs from subject lines --- tests/fixtures/git_repo.py | 34 ++++------------------------------ 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 1c6a56207..9601f44fe 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -534,15 +534,11 @@ def _get_commit_def_of_conventional_commit(msg: str) -> CommitDef: "include_in_changelog": False, } - descriptions = list(parsed_result.descriptions) - if parsed_result.linked_merge_request: - descriptions[0] = str.join("(", descriptions[0].split("(")[:-1]).strip() - return { "msg": msg, "type": parsed_result.type, "category": parsed_result.category, - "desc": str.join("\n\n", descriptions), + "desc": str.join("\n\n", parsed_result.descriptions), "brking_desc": str.join("\n\n", parsed_result.breaking_descriptions), "scope": parsed_result.scope, "mr": parsed_result.linked_merge_request, @@ -571,15 +567,11 @@ def _get_commit_def_of_emoji_commit(msg: str) -> CommitDef: "include_in_changelog": False, } - descriptions = list(parsed_result.descriptions) - if parsed_result.linked_merge_request: - descriptions[0] = str.join("(", descriptions[0].split("(")[:-1]).strip() - return { "msg": msg, "type": parsed_result.type, "category": parsed_result.category, - "desc": str.join("\n\n", descriptions), + "desc": str.join("\n\n", parsed_result.descriptions), "brking_desc": str.join("\n\n", parsed_result.breaking_descriptions), "scope": parsed_result.scope, "mr": parsed_result.linked_merge_request, @@ -608,15 +600,11 @@ def _get_commit_def_of_scipy_commit(msg: str) -> CommitDef: "include_in_changelog": False, } - descriptions = list(parsed_result.descriptions) - if parsed_result.linked_merge_request: - descriptions[0] = str.join("(", descriptions[0].split("(")[:-1]).strip() - return { "msg": msg, "type": parsed_result.type, "category": parsed_result.category, - "desc": str.join("\n\n", descriptions), + "desc": str.join("\n\n", parsed_result.descriptions), "brking_desc": str.join("\n\n", parsed_result.breaking_descriptions), "scope": parsed_result.scope, "mr": parsed_result.linked_merge_request, @@ -1145,21 +1133,7 @@ def _separate_squashed_commit_def( "msg": squashed_message, "type": parsed_result.type, "category": parsed_result.category, - "desc": str.join( - "\n\n", - ( - [ - # Strip out any MR references (since v9 doesn't) to prep for changelog generatro - # TODO: remove in v10, as the parser will remove the MR reference - str.join( - "(", parsed_result.descriptions[0].split("(")[:-1] - ).strip(), - *parsed_result.descriptions[1:], - ] - if parsed_result.linked_merge_request - else [*parsed_result.descriptions] - ), - ), + "desc": str.join("\n\n", parsed_result.descriptions), "brking_desc": str.join("\n\n", parsed_result.breaking_descriptions), "scope": parsed_result.scope, "mr": parsed_result.linked_merge_request or squashed_commit_def["mr"], From fee2ff9c331b740aabd3fac8c2a625849fd5e5a4 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 18 May 2025 11:40:47 -0600 Subject: [PATCH 123/160] test(fixtures): update repo definitions with valid scipy scopes --- .../git_flow/repo_w_1_release_channel.py | 16 +++++----- .../git_flow/repo_w_2_release_channels.py | 28 ++++++++-------- .../git_flow/repo_w_3_release_channels.py | 32 +++++++++---------- .../git_flow/repo_w_4_release_channels.py | 26 +++++++-------- .../github_flow/repo_w_default_release.py | 8 ++--- .../github_flow/repo_w_release_channels.py | 14 ++++---- .../repo_w_dual_version_support.py | 12 +++---- ...po_w_dual_version_support_w_prereleases.py | 16 +++++----- .../trunk_based_dev/repo_w_prereleases.py | 12 +++---- .../repos/trunk_based_dev/repo_w_tags.py | 6 ++-- 10 files changed, 85 insertions(+), 85 deletions(-) diff --git a/tests/fixtures/repos/git_flow/repo_w_1_release_channel.py b/tests/fixtures/repos/git_flow/repo_w_1_release_channel.py index c624a7965..36a2eb3d1 100644 --- a/tests/fixtures/repos/git_flow/repo_w_1_release_channel.py +++ b/tests/fixtures/repos/git_flow/repo_w_1_release_channel.py @@ -111,7 +111,7 @@ def _get_repo_from_defintion( ) # Common static actions or components - changelog_file_definitons: Sequence[RepoActionWriteChangelogsDestFile] = [ + changelog_file_definitions: Sequence[RepoActionWriteChangelogsDestFile] = [ { "path": changelog_md_file, "format": ChangelogOutputFormat.MARKDOWN, @@ -295,7 +295,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -382,7 +382,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -429,7 +429,7 @@ def _get_repo_from_defintion( "\n\n", [ "API: add revolutionary feature", - "BREAKING CHANGE: this is a breaking change", + "This is a breaking change", ], ), "datetime": next(commit_timestamp_gen), @@ -487,7 +487,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -574,7 +574,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -661,7 +661,7 @@ def _get_repo_from_defintion( { "conventional": "feat(cli): add new config cli command", "emoji": ":sparkles: (cli) add new config cli command", - "scipy": "ENH(cli): add new config cli command", + "scipy": "ENH: cli: add new config cli command", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, @@ -717,7 +717,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], diff --git a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py index f4a6005bc..22ccd7083 100644 --- a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py @@ -111,7 +111,7 @@ def _get_repo_from_defintion( ) # Common static actions or components - changelog_file_definitons: Sequence[RepoActionWriteChangelogsDestFile] = [ + changelog_file_definitions: Sequence[RepoActionWriteChangelogsDestFile] = [ { "path": changelog_md_file, "format": ChangelogOutputFormat.MARKDOWN, @@ -301,7 +301,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -351,7 +351,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -388,7 +388,7 @@ def _get_repo_from_defintion( "\n\n", [ "API: add revolutionary feature", - "BREAKING CHANGE: this is a breaking change", + "This is a breaking change", ], ), "datetime": next(commit_timestamp_gen), @@ -409,7 +409,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -486,7 +486,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -517,7 +517,7 @@ def _get_repo_from_defintion( { "conventional": "feat(cli): add new config cli command", "emoji": ":sparkles: (cli) add new config cli command", - "scipy": "ENH(cli): add new config cli command", + "scipy": "ENH: cli: add new config cli command", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, @@ -573,7 +573,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -604,7 +604,7 @@ def _get_repo_from_defintion( { "conventional": "fix(config): fixed configuration generation", "emoji": ":bug: (config) fixed configuration generation", - "scipy": "MAINT(config): fixed configuration generation", + "scipy": "MAINT:config: fixed configuration generation", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, @@ -660,7 +660,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -710,7 +710,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -731,14 +731,14 @@ def _get_repo_from_defintion( { "conventional": "fix(scope): correct some text", "emoji": ":bug: (scope) correct some text", - "scipy": "MAINT(scope): correct some text", + "scipy": "MAINT:scope: correct some text", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, { "conventional": "feat(scope): add some more text", "emoji": ":sparkles:(scope) add some more text", - "scipy": "ENH(scope): add some more text", + "scipy": "ENH: scope: add some more text", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, @@ -757,7 +757,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], diff --git a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py index 10cc98ff8..ba54e64e8 100644 --- a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py @@ -113,7 +113,7 @@ def _get_repo_from_defintion( ) # Common static actions or components - changelog_file_definitons: Sequence[RepoActionWriteChangelogsDestFile] = [ + changelog_file_definitions: Sequence[RepoActionWriteChangelogsDestFile] = [ { "path": changelog_md_file, "format": ChangelogOutputFormat.MARKDOWN, @@ -309,7 +309,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -359,7 +359,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -396,7 +396,7 @@ def _get_repo_from_defintion( "\n\n", [ "API: add revolutionary feature", - "BREAKING CHANGE: this is a breaking change", + "This is a breaking change", ], ), "datetime": next(commit_timestamp_gen), @@ -417,7 +417,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -470,7 +470,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -500,7 +500,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -531,7 +531,7 @@ def _get_repo_from_defintion( { "conventional": "feat(cli): add new config cli command", "emoji": ":sparkles: (cli) add new config cli command", - "scipy": "ENH(cli): add new config cli command", + "scipy": "ENH:cli: add new config cli command", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, @@ -550,7 +550,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -571,7 +571,7 @@ def _get_repo_from_defintion( { "conventional": "feat(config): add new config option", "emoji": ":sparkles: (config) add new config option", - "scipy": "ENH(config): add new config option", + "scipy": "ENH: config: add new config option", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, @@ -590,7 +590,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -650,7 +650,7 @@ def _get_repo_from_defintion( { "conventional": "fix(cli): fix config cli command", "emoji": ":bug: (cli) fix config cli command", - "scipy": "BUG(cli): fix config cli command", + "scipy": "BUG:cli: fix config cli command", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, @@ -699,7 +699,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -729,7 +729,7 @@ def _get_repo_from_defintion( { "conventional": "fix(config): fix config option", "emoji": ":bug: (config) fix config option", - "scipy": "BUG(config): fix config option", + "scipy": "BUG: config: fix config option", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, @@ -778,7 +778,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -808,7 +808,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], diff --git a/tests/fixtures/repos/git_flow/repo_w_4_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_4_release_channels.py index d6abbb5df..f8128015a 100644 --- a/tests/fixtures/repos/git_flow/repo_w_4_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_4_release_channels.py @@ -123,7 +123,7 @@ def _get_repo_from_defintion( ) # Common static actions or components - changelog_file_definitons: Sequence[RepoActionWriteChangelogsDestFile] = [ + changelog_file_definitions: Sequence[RepoActionWriteChangelogsDestFile] = [ { "path": changelog_md_file, "format": ChangelogOutputFormat.MARKDOWN, @@ -381,7 +381,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -413,7 +413,7 @@ def _get_repo_from_defintion( { "conventional": "fix(cli): fix config cli command", "emoji": ":bug: (cli) fix config cli command", - "scipy": "BUG(cli): fix config cli command", + "scipy": "BUG:cli: fix config cli command", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, @@ -462,7 +462,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -492,7 +492,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -523,7 +523,7 @@ def _get_repo_from_defintion( { "conventional": "fix(config): fix config option", "emoji": ":bug: (config) fix config option", - "scipy": "BUG(config): fix config option", + "scipy": "BUG: config: fix config option", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, @@ -572,7 +572,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -602,7 +602,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -634,7 +634,7 @@ def _get_repo_from_defintion( { "conventional": "feat(feat-2): add another primary feature", "emoji": ":sparkles: (feat-2) add another primary feature", - "scipy": "ENH(feat-2): add another primary feature", + "scipy": "ENH: feat-2: add another primary feature", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, @@ -653,7 +653,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -706,7 +706,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -736,7 +736,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -766,7 +766,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], diff --git a/tests/fixtures/repos/github_flow/repo_w_default_release.py b/tests/fixtures/repos/github_flow/repo_w_default_release.py index ce8877dfe..7eddbd852 100644 --- a/tests/fixtures/repos/github_flow/repo_w_default_release.py +++ b/tests/fixtures/repos/github_flow/repo_w_default_release.py @@ -106,7 +106,7 @@ def _get_repo_from_defintion( ) pr_num_gen = (i for i in count(start=2, step=1)) - changelog_file_definitons: Sequence[RepoActionWriteChangelogsDestFile] = [ + changelog_file_definitions: Sequence[RepoActionWriteChangelogsDestFile] = [ { "path": changelog_md_file, "format": ChangelogOutputFormat.MARKDOWN, @@ -182,7 +182,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -326,7 +326,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -377,7 +377,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], diff --git a/tests/fixtures/repos/github_flow/repo_w_release_channels.py b/tests/fixtures/repos/github_flow/repo_w_release_channels.py index 07be6eb5a..c0670a968 100644 --- a/tests/fixtures/repos/github_flow/repo_w_release_channels.py +++ b/tests/fixtures/repos/github_flow/repo_w_release_channels.py @@ -106,7 +106,7 @@ def _get_repo_from_defintion( ) pr_num_gen = (i for i in count(start=2, step=1)) - changelog_file_definitons: Sequence[RepoActionWriteChangelogsDestFile] = [ + changelog_file_definitions: Sequence[RepoActionWriteChangelogsDestFile] = [ { "path": changelog_md_file, "format": ChangelogOutputFormat.MARKDOWN, @@ -188,7 +188,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -237,7 +237,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -277,7 +277,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -331,7 +331,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -381,7 +381,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -435,7 +435,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support.py b/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support.py index c7a33cc16..04f0bc27c 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support.py @@ -99,7 +99,7 @@ def _get_repo_from_defintion( for i in count(step=1) ) - changelog_file_definitons: Sequence[RepoActionWriteChangelogsDestFile] = [ + changelog_file_definitions: Sequence[RepoActionWriteChangelogsDestFile] = [ { "path": changelog_md_file, "format": ChangelogOutputFormat.MARKDOWN, @@ -179,7 +179,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -219,7 +219,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -269,7 +269,7 @@ def _get_repo_from_defintion( "\n\n", [ "API: add revolutionary feature", - "BREAKING CHANGE: this is a breaking change", + "This is a breaking change", ], ), "datetime": next(commit_timestamp_gen), @@ -290,7 +290,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -335,7 +335,7 @@ def _get_repo_from_defintion( "details": { "new_version": new_version, "max_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support_w_prereleases.py b/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support_w_prereleases.py index 2576ec510..d0458cb90 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support_w_prereleases.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support_w_prereleases.py @@ -99,7 +99,7 @@ def _get_repo_from_defintion( for i in count(step=1) ) - changelog_file_definitons: Sequence[RepoActionWriteChangelogsDestFile] = [ + changelog_file_definitions: Sequence[RepoActionWriteChangelogsDestFile] = [ { "path": changelog_md_file, "format": ChangelogOutputFormat.MARKDOWN, @@ -179,7 +179,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -219,7 +219,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -269,7 +269,7 @@ def _get_repo_from_defintion( "\n\n", [ "API: add revolutionary feature", - "BREAKING CHANGE: this is a breaking change", + "This is a breaking change", ], ), "datetime": next(commit_timestamp_gen), @@ -290,7 +290,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -336,7 +336,7 @@ def _get_repo_from_defintion( "details": { "new_version": new_version, "max_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -377,7 +377,7 @@ def _get_repo_from_defintion( "details": { "new_version": new_version, "max_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -418,7 +418,7 @@ def _get_repo_from_defintion( "details": { "new_version": new_version, "max_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py index a2c133d21..8ac0b0674 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py @@ -93,7 +93,7 @@ def _get_repo_from_defintion( for i in count(step=1) ) - changelog_file_definitons: Sequence[RepoActionWriteChangelogsDestFile] = [ + changelog_file_definitions: Sequence[RepoActionWriteChangelogsDestFile] = [ { "path": changelog_md_file, "format": ChangelogOutputFormat.MARKDOWN, @@ -169,7 +169,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -209,7 +209,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -249,7 +249,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -270,7 +270,7 @@ def _get_repo_from_defintion( { "conventional": "feat(cli): add cli command", "emoji": ":sparkles:(cli) add cli command", - "scipy": "ENH(cli): add cli command", + "scipy": "ENH: cli: add cli command", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, @@ -289,7 +289,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py index 9d080ed7a..c8bfd35d1 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py @@ -95,7 +95,7 @@ def _get_repo_from_defintion( for i in count(step=1) ) - changelog_file_definitons: Sequence[RepoActionWriteChangelogsDestFile] = [ + changelog_file_definitions: Sequence[RepoActionWriteChangelogsDestFile] = [ { "path": changelog_md_file, "format": ChangelogOutputFormat.MARKDOWN, @@ -171,7 +171,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], @@ -211,7 +211,7 @@ def _get_repo_from_defintion( "action": RepoActionStep.WRITE_CHANGELOGS, "details": { "new_version": new_version, - "dest_files": changelog_file_definitons, + "dest_files": changelog_file_definitions, }, }, ], From fd3076ef9da9dcd23e91cdf73f29576806922c52 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 18 May 2025 17:29:12 -0600 Subject: [PATCH 124/160] test(fixtures): harden test fixtures from CI environment variables --- tests/fixtures/git_repo.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 9601f44fe..be1ee0fa0 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -8,7 +8,7 @@ from pathlib import Path from textwrap import dedent from time import sleep -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, cast from unittest import mock import pytest @@ -956,10 +956,16 @@ def _get_hvcs_client_from_repo_def( # Prevent the HVCS client from using the environment variables with mock.patch.dict(os.environ, {}, clear=True): - return hvcs_client_class( - example_git_https_url, - hvcs_domain=get_cfg_value_from_def(repo_def, "hvcs_domain"), + hvcs_client = cast( + "HvcsBase", + hvcs_client_class( + example_git_https_url, + hvcs_domain=get_cfg_value_from_def(repo_def, "hvcs_domain"), + ), ) + # Force the HVCS client to attempt to resolve the repo name (as we generally cache it) + assert hvcs_client.repo_name + return cast("Github | Gitlab | Gitea | Bitbucket", hvcs_client) return _get_hvcs_client_from_repo_def From 5310d0c700840538f27874394b9964bf09cd69b1 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 18 May 2025 00:29:19 -0600 Subject: [PATCH 125/160] docs(commit-parsing): define limitation of revert commits with the scipy parser --- docs/commit_parsing.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/commit_parsing.rst b/docs/commit_parsing.rst index 65e523d98..16340abeb 100644 --- a/docs/commit_parsing.rst +++ b/docs/commit_parsing.rst @@ -294,6 +294,11 @@ Guidelines`_ with all different commit types. Because of this small variance, th only extends our :ref:`commit_parser-builtin-angular` parser with pre-defined scipy commit types in the default Scipy Parser Options and all other features are inherited. +**Limitations**: + +- Commits with the ``REV`` type are not currently supported. Track the implementation + of this feature in the issue `#402`_. + If no commit parser options are provided via the configuration, the parser will use PSR's built-in :py:class:`defaults `. From 86f6bd98808f22fa9b3500c03ac4c2f60bf2c2c2 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 18 May 2025 20:02:41 -0600 Subject: [PATCH 126/160] test: refactor to use cli runner with empty default env --- tests/conftest.py | 45 ++++++ tests/e2e/cmd_changelog/test_changelog.py | 128 +++++++----------- .../test_changelog_custom_parser.py | 8 +- .../cmd_changelog/test_changelog_parsing.py | 8 +- .../test_changelog_release_notes.py | 18 ++- tests/e2e/cmd_config/test_generate_config.py | 22 ++- tests/e2e/cmd_publish/test_publish.py | 16 +-- .../git_flow/test_repo_1_channel.py | 8 +- .../git_flow/test_repo_2_channels.py | 8 +- .../git_flow/test_repo_3_channels.py | 8 +- .../git_flow/test_repo_4_channels.py | 8 +- .../github_flow/test_repo_1_channel.py | 8 +- .../github_flow/test_repo_2_channels.py | 8 +- .../trunk_based_dev/test_repo_trunk.py | 8 +- .../test_repo_trunk_dual_version_support.py | 8 +- ...runk_dual_version_support_w_prereleases.py | 8 +- .../test_repo_trunk_w_prereleases.py | 8 +- tests/e2e/cmd_version/test_version.py | 24 ++-- tests/e2e/cmd_version/test_version_build.py | 30 ++-- tests/e2e/cmd_version/test_version_bump.py | 60 ++++---- .../e2e/cmd_version/test_version_changelog.py | 25 ++-- ...est_version_changelog_custom_commit_msg.py | 11 +- .../test_version_github_actions.py | 27 ++-- tests/e2e/cmd_version/test_version_print.py | 60 ++++---- .../cmd_version/test_version_release_notes.py | 12 +- tests/e2e/cmd_version/test_version_stamp.py | 36 +++-- tests/e2e/cmd_version/test_version_strict.py | 12 +- tests/e2e/test_help.py | 21 ++- tests/e2e/test_main.py | 45 +++--- tests/fixtures/git_repo.py | 4 +- tests/util.py | 5 +- 31 files changed, 328 insertions(+), 369 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 2897bbac7..16298e98b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,6 +10,7 @@ from pathlib import Path from tempfile import NamedTemporaryFile from typing import TYPE_CHECKING +from unittest import mock import pytest from click.testing import CliRunner @@ -24,11 +25,36 @@ from tempfile import _TemporaryFileWrapper from typing import Any, Callable, Generator, Protocol, Sequence, TypedDict + from click.testing import Result from filelock import AcquireReturnProxy from git import Actor from tests.fixtures.git_repo import RepoActions + class RunCliFn(Protocol): + """ + Run the CLI with the provided arguments and a clean environment. + + :param argv: The arguments to pass to the CLI. + :type argv: list[str] | None + + :param env: The environment variables to set for the CLI. + :type env: dict[str, str] | None + + :param invoke_kwargs: Additional arguments to pass to the invoke method. + :type invoke_kwargs: dict[str, Any] | None + + :return: The result of the CLI invocation. + :rtype: Result + """ + + def __call__( + self, + argv: list[str] | None = None, + env: dict[str, str] | None = None, + invoke_kwargs: dict[str, Any] | None = None, + ) -> Result: ... + class MakeCommitObjFn(Protocol): def __call__(self, message: str) -> Commit: ... @@ -170,6 +196,25 @@ def cli_runner() -> CliRunner: return CliRunner(mix_stderr=False) +@pytest.fixture(scope="session") +def run_cli(clean_os_environment: dict[str, str]) -> RunCliFn: + def _run_cli( + argv: list[str] | None = None, + env: dict[str, str] | None = None, + invoke_kwargs: dict[str, Any] | None = None, + ) -> Result: + from semantic_release.cli.commands.main import main + + cli_runner = CliRunner(mix_stderr=False) + env_vars = {**clean_os_environment, **(env or {})} + + with mock.patch.dict(os.environ, env_vars, clear=True): + # run the CLI with the provided arguments + return cli_runner.invoke(main, args=(argv or []), **(invoke_kwargs or {})) + + return _run_cli + + @pytest.fixture(scope="session") def default_netrc_username() -> str: return "username" diff --git a/tests/e2e/cmd_changelog/test_changelog.py b/tests/e2e/cmd_changelog/test_changelog.py index d717df497..edc2a8c63 100644 --- a/tests/e2e/cmd_changelog/test_changelog.py +++ b/tests/e2e/cmd_changelog/test_changelog.py @@ -1,7 +1,6 @@ from __future__ import annotations import os -import sys from textwrap import dedent from typing import TYPE_CHECKING from unittest import mock @@ -13,7 +12,6 @@ import semantic_release.hvcs.github from semantic_release.changelog.context import ChangelogMode -from semantic_release.cli.commands.main import main from semantic_release.cli.config import ChangelogOutputFormat from semantic_release.hvcs.github import Github from semantic_release.version.version import Version @@ -77,9 +75,9 @@ from pathlib import Path from typing import TypedDict - from click.testing import CliRunner from requests_mock import Mocker + from tests.conftest import RunCliFn from tests.e2e.conftest import RetrieveRuntimeContextFn from tests.fixtures.example_project import ( ExProjectDir, @@ -123,7 +121,7 @@ class Commit2SectionCommit(TypedDict): def test_changelog_noop_is_noop( repo_result: BuiltRepoResult, arg0: str | None, - cli_runner: CliRunner, + run_cli: RunCliFn, get_versions_from_repo_build_def: GetVersionsFromRepoBuildDefFn, ): repo = repo_result["repo"] @@ -152,7 +150,7 @@ def test_changelog_noop_is_noop( ), requests_mock.Mocker(session=session) as mocker: args = [arg0, f"v{version_str}"] if version_str and arg0 else [] cli_cmd = [MAIN_PROG_NAME, "--noop", CHANGELOG_SUBCMD, *args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -227,7 +225,7 @@ def test_changelog_noop_is_noop( ) def test_changelog_content_regenerated( repo_result: BuiltRepoResult, - cli_runner: CliRunner, + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn, changelog_file: Path, insertion_flag: str, @@ -255,7 +253,7 @@ def test_changelog_content_regenerated( # Act cli_cmd = [MAIN_PROG_NAME, CHANGELOG_SUBCMD] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -289,7 +287,7 @@ def test_changelog_content_regenerated_masked_initial_release( build_repo_from_definition: BuildRepoFromDefinitionFn, get_repo_definition_4_trunk_only_repo_w_tags: GetRepoDefinitionFn, example_project_dir: ExProjectDir, - cli_runner: CliRunner, + run_cli: RunCliFn, changelog_file: Path, insertion_flag: str, ): @@ -319,7 +317,7 @@ def test_changelog_content_regenerated_masked_initial_release( # Act cli_cmd = [MAIN_PROG_NAME, CHANGELOG_SUBCMD] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -353,7 +351,7 @@ def test_changelog_content_regenerated_masked_initial_release( ) def test_changelog_update_mode_unchanged( repo_result: BuiltRepoResult, - cli_runner: CliRunner, + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn, changelog_file: Path, ): @@ -376,7 +374,7 @@ def test_changelog_update_mode_unchanged( # Act cli_cmd = [MAIN_PROG_NAME, CHANGELOG_SUBCMD] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -413,7 +411,7 @@ def test_changelog_update_mode_unchanged( ) def test_changelog_update_mode_no_prev_changelog( repo_result: BuiltRepoResult, - cli_runner: CliRunner, + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn, changelog_file: Path, ): @@ -439,7 +437,7 @@ def test_changelog_update_mode_no_prev_changelog( # Act cli_cmd = [MAIN_PROG_NAME, CHANGELOG_SUBCMD] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -481,7 +479,7 @@ def test_changelog_update_mode_no_prev_changelog( ) def test_changelog_update_mode_no_flag( repo_result: BuiltRepoResult, - cli_runner: CliRunner, + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn, changelog_file: Path, insertion_flag: str, @@ -514,7 +512,7 @@ def test_changelog_update_mode_no_flag( # Act cli_cmd = [MAIN_PROG_NAME, CHANGELOG_SUBCMD] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -555,7 +553,7 @@ def test_changelog_update_mode_no_flag( ) def test_changelog_update_mode_no_header( repo_result: BuiltRepoResult, - cli_runner: CliRunner, + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn, changelog_format: ChangelogOutputFormat, changelog_file: Path, @@ -613,7 +611,7 @@ def test_changelog_update_mode_no_header( # Act cli_cmd = [MAIN_PROG_NAME, CHANGELOG_SUBCMD] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -657,7 +655,7 @@ def test_changelog_update_mode_no_header( ) def test_changelog_update_mode_no_footer( repo_result: BuiltRepoResult, - cli_runner: CliRunner, + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn, changelog_format: ChangelogOutputFormat, changelog_file: Path, @@ -717,7 +715,7 @@ def test_changelog_update_mode_no_footer( # Act cli_cmd = [MAIN_PROG_NAME, CHANGELOG_SUBCMD] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -761,7 +759,7 @@ def test_changelog_update_mode_no_footer( ) def test_changelog_update_mode_no_releases( repo_result: BuiltRepoResult, - cli_runner: CliRunner, + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn, changelog_file: Path, insertion_flag: str, @@ -816,7 +814,7 @@ def test_changelog_update_mode_no_releases( # Act cli_cmd = [MAIN_PROG_NAME, CHANGELOG_SUBCMD] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -862,7 +860,7 @@ def test_changelog_update_mode_unreleased_n_released( repo_result: BuiltRepoResult, commit_type: CommitConvention, changelog_format: ChangelogOutputFormat, - cli_runner: CliRunner, + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn, example_git_ssh_url: str, file_in_repo: str, @@ -933,7 +931,10 @@ def test_changelog_update_mode_unreleased_n_released( repo, commit_n_section[commit_type]["commit"], ) - hvcs = Github(example_git_ssh_url, hvcs_domain=EXAMPLE_HVCS_DOMAIN) + + with mock.patch.dict(os.environ, {}, clear=True): + hvcs = Github(example_git_ssh_url, hvcs_domain=EXAMPLE_HVCS_DOMAIN) + assert hvcs.repo_name # force caching of repo values (ignoring the env) unreleased_change_variants = { ChangelogOutputFormat.MARKDOWN: dedent( @@ -992,7 +993,7 @@ def test_changelog_update_mode_unreleased_n_released( # Act cli_cmd = [MAIN_PROG_NAME, CHANGELOG_SUBCMD] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -1015,11 +1016,11 @@ def test_changelog_update_mode_unreleased_n_released( ) def test_changelog_release_tag_not_in_history( args: list[str], - cli_runner: CliRunner, + run_cli: RunCliFn, ): # Act cli_cmd = [MAIN_PROG_NAME, CHANGELOG_SUBCMD, *args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_exit_code(2, result, cli_cmd) @@ -1035,7 +1036,7 @@ def test_changelog_release_tag_not_in_history( ("--post-to-release-tag", "v0.2.0"), # latest release ], ) -def test_changelog_post_to_release(args: list[str], cli_runner: CliRunner): +def test_changelog_post_to_release(args: list[str], run_cli: RunCliFn): # Set up a requests HTTP session so we can catch the HTTP calls and ensure they're # made @@ -1055,59 +1056,22 @@ def test_changelog_post_to_release(args: list[str], cli_runner: CliRunner): repo_name=EXAMPLE_REPO_NAME, ) - clean_os_environment = dict( - filter( - lambda k_v: k_v[1] is not None, - { - "CI": "true", - "PATH": os.getenv("PATH"), - "HOME": os.getenv("HOME"), - "VIRTUAL_ENV": os.getenv("VIRTUAL_ENV", "./.venv"), - **( - {} - if sys.platform != "win32" - else { - # Windows Required variables - "ALLUSERSAPPDATA": os.getenv("ALLUSERSAPPDATA"), - "ALLUSERSPROFILE": os.getenv("ALLUSERSPROFILE"), - "APPDATA": os.getenv("APPDATA"), - "COMMONPROGRAMFILES": os.getenv("COMMONPROGRAMFILES"), - "COMMONPROGRAMFILES(X86)": os.getenv("COMMONPROGRAMFILES(X86)"), - "DEFAULTUSERPROFILE": os.getenv("DEFAULTUSERPROFILE"), - "HOMEPATH": os.getenv("HOMEPATH"), - "PATHEXT": os.getenv("PATHEXT"), - "PROFILESFOLDER": os.getenv("PROFILESFOLDER"), - "PROGRAMFILES": os.getenv("PROGRAMFILES"), - "PROGRAMFILES(X86)": os.getenv("PROGRAMFILES(X86)"), - "SYSTEM": os.getenv("SYSTEM"), - "SYSTEM16": os.getenv("SYSTEM16"), - "SYSTEM32": os.getenv("SYSTEM32"), - "SYSTEMDRIVE": os.getenv("SYSTEMDRIVE"), - "SYSTEMROOT": os.getenv("SYSTEMROOT"), - "TEMP": os.getenv("TEMP"), - "TMP": os.getenv("TMP"), - "USERPROFILE": os.getenv("USERPROFILE"), - "USERSID": os.getenv("USERSID"), - "USERNAME": os.getenv("USERNAME"), - "WINDIR": os.getenv("WINDIR"), - } - ), - }.items(), - ) - ) - # Patch out env vars that affect changelog URLs but only get set in e.g. # Github actions with mock.patch( # Patching the specific module's reference to the build_requests_session function f"{semantic_release.hvcs.github.__name__}.{semantic_release.hvcs.github.build_requests_session.__name__}", return_value=session, - ) as build_requests_session_mock, mock.patch.dict( - os.environ, clean_os_environment, clear=True - ): + ) as build_requests_session_mock: # Act cli_cmd = [MAIN_PROG_NAME, CHANGELOG_SUBCMD, *args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli( + cli_cmd[1:], + env={ + "CI": "true", + "VIRTUAL_ENV": os.getenv("VIRTUAL_ENV", "./.venv"), + }, + ) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -1127,7 +1091,7 @@ def test_custom_release_notes_template( use_release_notes_template: UseReleaseNotesTemplateFn, retrieve_runtime_context: RetrieveRuntimeContextFn, post_mocker: Mocker, - cli_runner: CliRunner, + run_cli: RunCliFn, ) -> None: """Verify the template `.release_notes.md.j2` from `template_dir` is used.""" expected_call_count = 1 @@ -1157,7 +1121,7 @@ def test_custom_release_notes_template( # Act cli_cmd = [MAIN_PROG_NAME, CHANGELOG_SUBCMD, "--post-to-release-tag", tag] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Assert assert_successful_exit_code(result, cli_cmd) @@ -1174,7 +1138,7 @@ def test_changelog_default_on_empty_template_dir( changelog_template_dir: Path, example_project_template_dir: Path, update_pyproject_toml: UpdatePyprojectTomlFn, - cli_runner: CliRunner, + run_cli: RunCliFn, ): # Setup: Make sure default changelog doesn't already exist example_changelog_md.unlink(missing_ok=True) @@ -1190,7 +1154,7 @@ def test_changelog_default_on_empty_template_dir( # Act cli_cmd = [MAIN_PROG_NAME, CHANGELOG_SUBCMD] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -1205,7 +1169,7 @@ def test_changelog_default_on_incorrect_config_template_file( changelog_template_dir: Path, example_project_template_dir: Path, update_pyproject_toml: UpdatePyprojectTomlFn, - cli_runner: CliRunner, + run_cli: RunCliFn, ): # Setup: Make sure default changelog doesn't already exist example_changelog_md.unlink(missing_ok=True) @@ -1222,7 +1186,7 @@ def test_changelog_default_on_incorrect_config_template_file( # Act cli_cmd = [MAIN_PROG_NAME, CHANGELOG_SUBCMD] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -1236,7 +1200,7 @@ def test_changelog_default_on_incorrect_config_template_file( def test_changelog_prevent_malicious_path_traversal_file( update_pyproject_toml: UpdatePyprojectTomlFn, bad_changelog_file_str: str, - cli_runner: CliRunner, + run_cli: RunCliFn, ): # Setup: A malicious path traversal filepath outside of the repository update_pyproject_toml( @@ -1246,7 +1210,7 @@ def test_changelog_prevent_malicious_path_traversal_file( # Act cli_cmd = [MAIN_PROG_NAME, "--noop", CHANGELOG_SUBCMD] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_exit_code(1, result, cli_cmd) @@ -1261,7 +1225,7 @@ def test_changelog_prevent_malicious_path_traversal_file( def test_changelog_prevent_external_path_traversal_dir( update_pyproject_toml: UpdatePyprojectTomlFn, template_dir_path: str, - cli_runner: CliRunner, + run_cli: RunCliFn, ): # Setup: A malicious path traversal filepath outside of the repository update_pyproject_toml( @@ -1271,7 +1235,7 @@ def test_changelog_prevent_external_path_traversal_dir( # Act cli_cmd = [MAIN_PROG_NAME, "--noop", CHANGELOG_SUBCMD] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_exit_code(1, result, cli_cmd) diff --git a/tests/e2e/cmd_changelog/test_changelog_custom_parser.py b/tests/e2e/cmd_changelog/test_changelog_custom_parser.py index 3c6d88f7a..0173cb49d 100644 --- a/tests/e2e/cmd_changelog/test_changelog_custom_parser.py +++ b/tests/e2e/cmd_changelog/test_changelog_custom_parser.py @@ -7,7 +7,6 @@ from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture from semantic_release.changelog.context import ChangelogMode -from semantic_release.cli.commands.main import main from tests.const import CHANGELOG_SUBCMD, MAIN_PROG_NAME from tests.fixtures.repos import repo_w_no_tags_conventional_commits @@ -19,8 +18,7 @@ if TYPE_CHECKING: from pathlib import Path - from click.testing import CliRunner - + from tests.conftest import RunCliFn from tests.fixtures.example_project import UpdatePyprojectTomlFn, UseCustomParserFn from tests.fixtures.git_repo import BuiltRepoResult, GetCommitDefFn @@ -30,7 +28,7 @@ ) def test_changelog_custom_parser_remove_from_changelog( repo_result: BuiltRepoResult, - cli_runner: CliRunner, + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn, use_custom_parser: UseCustomParserFn, get_commit_def_of_conventional_commit: GetCommitDefFn, @@ -70,7 +68,7 @@ def test_changelog_custom_parser_remove_from_changelog( # Act cli_cmd = [MAIN_PROG_NAME, CHANGELOG_SUBCMD] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Take measurement after action actual_content = changelog_md_file.read_text() diff --git a/tests/e2e/cmd_changelog/test_changelog_parsing.py b/tests/e2e/cmd_changelog/test_changelog_parsing.py index 40b6923cc..4c5c8f2e6 100644 --- a/tests/e2e/cmd_changelog/test_changelog_parsing.py +++ b/tests/e2e/cmd_changelog/test_changelog_parsing.py @@ -10,7 +10,6 @@ from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture from semantic_release.changelog.context import ChangelogMode -from semantic_release.cli.commands.main import main from semantic_release.cli.const import JINJA2_EXTENSION from tests.const import CHANGELOG_SUBCMD, MAIN_PROG_NAME @@ -29,8 +28,7 @@ from tests.util import assert_successful_exit_code if TYPE_CHECKING: - from click.testing import CliRunner - + from tests.conftest import RunCliFn from tests.fixtures.example_project import UpdatePyprojectTomlFn from tests.fixtures.git_repo import BuiltRepoResult @@ -68,7 +66,7 @@ ], ) def test_changelog_parsing_ignore_merge_commits( - cli_runner: CliRunner, + run_cli: RunCliFn, repo_result: BuiltRepoResult, update_pyproject_toml: UpdatePyprojectTomlFn, example_project_template_dir: Path, @@ -131,7 +129,7 @@ def test_changelog_parsing_ignore_merge_commits( # Act cli_cmd = [MAIN_PROG_NAME, CHANGELOG_SUBCMD] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) diff --git a/tests/e2e/cmd_changelog/test_changelog_release_notes.py b/tests/e2e/cmd_changelog/test_changelog_release_notes.py index e585f8b63..ca6d26563 100644 --- a/tests/e2e/cmd_changelog/test_changelog_release_notes.py +++ b/tests/e2e/cmd_changelog/test_changelog_release_notes.py @@ -6,7 +6,6 @@ import pytest from pytest_lazy_fixtures import lf as lazy_fixture -from semantic_release.cli.commands.main import main from semantic_release.version.version import Version from tests.const import CHANGELOG_SUBCMD, EXAMPLE_PROJECT_LICENSE, MAIN_PROG_NAME @@ -20,10 +19,9 @@ from tests.util import assert_successful_exit_code if TYPE_CHECKING: - from click.testing import CliRunner from requests_mock import Mocker - from tests.conftest import GetStableDateNowFn + from tests.conftest import GetStableDateNowFn, RunCliFn from tests.fixtures.example_project import UpdatePyprojectTomlFn from tests.fixtures.git_repo import ( BuiltRepoResult, @@ -49,7 +47,7 @@ def test_changelog_latest_release_notes( get_cfg_value_from_def: GetCfgValueFromDefFn, get_versions_from_repo_build_def: GetVersionsFromRepoBuildDefFn, get_hvcs_client_from_repo_def: GetHvcsClientFromRepoDefFn, - cli_runner: CliRunner, + run_cli: RunCliFn, post_mocker: Mocker, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, generate_default_release_notes_from_def: GenerateDefaultReleaseNotesFromDefFn, @@ -77,7 +75,7 @@ def test_changelog_latest_release_notes( # Act cli_cmd = [MAIN_PROG_NAME, CHANGELOG_SUBCMD, "--post-to-release-tag", release_tag] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -122,7 +120,7 @@ def test_changelog_previous_release_notes( get_cfg_value_from_def: GetCfgValueFromDefFn, get_versions_from_repo_build_def: GetVersionsFromRepoBuildDefFn, get_hvcs_client_from_repo_def: GetHvcsClientFromRepoDefFn, - cli_runner: CliRunner, + run_cli: RunCliFn, post_mocker: Mocker, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, generate_default_release_notes_from_def: GenerateDefaultReleaseNotesFromDefFn, @@ -157,7 +155,7 @@ def test_changelog_previous_release_notes( # Act cli_cmd = [MAIN_PROG_NAME, CHANGELOG_SUBCMD, "--post-to-release-tag", release_tag] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -213,7 +211,7 @@ def test_changelog_release_notes_license_change( get_cfg_value_from_def: GetCfgValueFromDefFn, get_versions_from_repo_build_def: GetVersionsFromRepoBuildDefFn, get_hvcs_client_from_repo_def: GetHvcsClientFromRepoDefFn, - cli_runner: CliRunner, + run_cli: RunCliFn, post_mocker: Mocker, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, generate_default_release_notes_from_def: GenerateDefaultReleaseNotesFromDefFn, @@ -296,7 +294,7 @@ def test_changelog_release_notes_license_change( "--post-to-release-tag", latest_release_tag, ] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -316,7 +314,7 @@ def test_changelog_release_notes_license_change( "--post-to-release-tag", prev_release_tag, ] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) diff --git a/tests/e2e/cmd_config/test_generate_config.py b/tests/e2e/cmd_config/test_generate_config.py index 3d49a3136..4a21f0be7 100644 --- a/tests/e2e/cmd_config/test_generate_config.py +++ b/tests/e2e/cmd_config/test_generate_config.py @@ -6,7 +6,6 @@ import pytest import tomlkit -from semantic_release.cli.commands.main import main from semantic_release.cli.config import RawConfig from tests.const import GENERATE_CONFIG_SUBCMD, MAIN_PROG_NAME, VERSION_SUBCMD @@ -17,8 +16,7 @@ from pathlib import Path from typing import Any - from click.testing import CliRunner - + from tests.conftest import RunCliFn from tests.fixtures.example_project import ExProjectDir @@ -30,7 +28,7 @@ def raw_config_dict() -> dict[str, Any]: @pytest.mark.parametrize("args", [(), ("--format", "toml"), ("--format", "TOML")]) @pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_generate_config_toml( - cli_runner: CliRunner, + run_cli: RunCliFn, args: tuple[str], raw_config_dict: dict[str, Any], example_project_dir: ExProjectDir, @@ -42,7 +40,7 @@ def test_generate_config_toml( # Act: Print the generated configuration to stdout cli_cmd = [MAIN_PROG_NAME, GENERATE_CONFIG_SUBCMD, *args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate: Check that the command ran successfully and that the output matches the expected configuration assert_successful_exit_code(result, cli_cmd) @@ -62,7 +60,7 @@ def test_generate_config_toml( VERSION_SUBCMD, "--print", ] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate: Check that the version command in noop mode ran successfully # which means PSR loaded the configuration successfully @@ -72,7 +70,7 @@ def test_generate_config_toml( @pytest.mark.parametrize("args", [("--format", "json"), ("--format", "JSON")]) @pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_generate_config_json( - cli_runner: CliRunner, + run_cli: RunCliFn, args: tuple[str], raw_config_dict: dict[str, Any], example_project_dir: ExProjectDir, @@ -84,7 +82,7 @@ def test_generate_config_json( # Act: Print the generated configuration to stdout cli_cmd = [MAIN_PROG_NAME, GENERATE_CONFIG_SUBCMD, *args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate: Check that the command ran successfully and that the output matches the expected configuration assert_successful_exit_code(result, cli_cmd) @@ -104,7 +102,7 @@ def test_generate_config_json( VERSION_SUBCMD, "--print", ] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate: Check that the version command in noop mode ran successfully # which means PSR loaded the configuration successfully @@ -113,7 +111,7 @@ def test_generate_config_json( @pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_generate_config_pyproject_toml( - cli_runner: CliRunner, + run_cli: RunCliFn, raw_config_dict: dict[str, Any], example_pyproject_toml: Path, ): @@ -135,7 +133,7 @@ def test_generate_config_pyproject_toml( "toml", "--pyproject", ] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate: Check that the command ran successfully and that the output matches the expected configuration assert_successful_exit_code(result, cli_cmd) @@ -154,7 +152,7 @@ def test_generate_config_pyproject_toml( # Act: Validate that the generated config is a valid configuration for PSR cli_cmd = [MAIN_PROG_NAME, "--noop", "--strict", VERSION_SUBCMD, "--print"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate: Check that the version command in noop mode ran successfully # which means PSR loaded the configuration successfully diff --git a/tests/e2e/cmd_publish/test_publish.py b/tests/e2e/cmd_publish/test_publish.py index ba5307fec..3b4fca2bf 100644 --- a/tests/e2e/cmd_publish/test_publish.py +++ b/tests/e2e/cmd_publish/test_publish.py @@ -6,7 +6,6 @@ import pytest from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture -from semantic_release.cli.commands.main import main from semantic_release.hvcs import Github from tests.const import MAIN_PROG_NAME, PUBLISH_SUBCMD @@ -16,8 +15,7 @@ if TYPE_CHECKING: from typing import Sequence - from click.testing import CliRunner - + from tests.conftest import RunCliFn from tests.fixtures.git_repo import BuiltRepoResult, GetVersionsFromRepoBuildDefFn @@ -27,7 +25,7 @@ ) def test_publish_latest_uses_latest_tag( repo_result: BuiltRepoResult, - cli_runner: CliRunner, + run_cli: RunCliFn, cmd_args: Sequence[str], get_versions_from_repo_build_def: GetVersionsFromRepoBuildDefFn, ): @@ -41,7 +39,7 @@ def test_publish_latest_uses_latest_tag( cli_cmd = [MAIN_PROG_NAME, PUBLISH_SUBCMD, *cmd_args] # Act - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -53,7 +51,7 @@ def test_publish_latest_uses_latest_tag( ) def test_publish_to_tag_uses_tag( repo_result: BuiltRepoResult, - cli_runner: CliRunner, + run_cli: RunCliFn, get_versions_from_repo_build_def: GetVersionsFromRepoBuildDefFn, ): # Testing a non-latest tag to distinguish from test_publish_latest_uses_latest_tag() @@ -64,7 +62,7 @@ def test_publish_to_tag_uses_tag( cli_cmd = [MAIN_PROG_NAME, PUBLISH_SUBCMD, "--tag", previous_tag] # Act - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -74,14 +72,14 @@ def test_publish_to_tag_uses_tag( @pytest.mark.usefixtures(repo_w_trunk_only_conventional_commits.__name__) -def test_publish_fails_on_nonexistant_tag(cli_runner: CliRunner): +def test_publish_fails_on_nonexistant_tag(run_cli: RunCliFn): non_existant_tag = "nonexistant-tag" with mock.patch.object(Github, Github.upload_dists.__name__) as mocked_upload_dists: cli_cmd = [MAIN_PROG_NAME, PUBLISH_SUBCMD, "--tag", non_existant_tag] # Act - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_exit_code(1, result, cli_cmd) diff --git a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_1_channel.py b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_1_channel.py index e12ca31e6..979a1ad8e 100644 --- a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_1_channel.py +++ b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_1_channel.py @@ -7,8 +7,6 @@ from flatdict import FlatDict from freezegun import freeze_time -from semantic_release.cli.commands.main import main - from tests.const import ( MAIN_PROG_NAME, VERSION_SUBCMD, @@ -24,9 +22,9 @@ from pathlib import Path from unittest.mock import MagicMock - from click.testing import CliRunner from requests_mock import Mocker + from tests.conftest import RunCliFn from tests.e2e.cmd_version.bump_version.conftest import InitMirrorRepo4RebuildFn from tests.e2e.conftest import GetSanitizedChangelogContentFn from tests.fixtures.example_project import ExProjectDir @@ -55,7 +53,7 @@ ) def test_gitflow_repo_rebuild_1_channel( repo_fixture_name: str, - cli_runner: CliRunner, + run_cli: RunCliFn, build_git_flow_repo_w_1_release_channels: BuildSpecificRepoFn, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, init_mirror_repo_for_rebuild: InitMirrorRepo4RebuildFn, @@ -145,7 +143,7 @@ def test_gitflow_repo_rebuild_1_channel( else [] ) cli_cmd = [MAIN_PROG_NAME, "--strict", VERSION_SUBCMD, *build_metadata_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command actual_release_commit_text = mirror_git_repo.head.commit.message diff --git a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_2_channels.py b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_2_channels.py index 035f679bd..5ffc6bef4 100644 --- a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_2_channels.py +++ b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_2_channels.py @@ -7,8 +7,6 @@ from flatdict import FlatDict from freezegun import freeze_time -from semantic_release.cli.commands.main import main - from tests.const import ( MAIN_PROG_NAME, VERSION_SUBCMD, @@ -24,9 +22,9 @@ from pathlib import Path from unittest.mock import MagicMock - from click.testing import CliRunner from requests_mock import Mocker + from tests.conftest import RunCliFn from tests.e2e.cmd_version.bump_version.conftest import InitMirrorRepo4RebuildFn from tests.e2e.conftest import GetSanitizedChangelogContentFn from tests.fixtures.example_project import ExProjectDir @@ -55,7 +53,7 @@ ) def test_gitflow_repo_rebuild_2_channels( repo_fixture_name: str, - cli_runner: CliRunner, + run_cli: RunCliFn, build_git_flow_repo_w_2_release_channels: BuildSpecificRepoFn, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, init_mirror_repo_for_rebuild: InitMirrorRepo4RebuildFn, @@ -145,7 +143,7 @@ def test_gitflow_repo_rebuild_2_channels( else [] ) cli_cmd = [MAIN_PROG_NAME, "--strict", VERSION_SUBCMD, *build_metadata_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command actual_release_commit_text = mirror_git_repo.head.commit.message diff --git a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_3_channels.py b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_3_channels.py index 825b7f7c3..c9bd6ccc5 100644 --- a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_3_channels.py +++ b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_3_channels.py @@ -7,8 +7,6 @@ from flatdict import FlatDict from freezegun import freeze_time -from semantic_release.cli.commands.main import main - from tests.const import ( MAIN_PROG_NAME, VERSION_SUBCMD, @@ -25,9 +23,9 @@ from pathlib import Path from unittest.mock import MagicMock - from click.testing import CliRunner from requests_mock import Mocker + from tests.conftest import RunCliFn from tests.e2e.cmd_version.bump_version.conftest import InitMirrorRepo4RebuildFn from tests.e2e.conftest import GetSanitizedChangelogContentFn from tests.fixtures.example_project import ExProjectDir @@ -57,7 +55,7 @@ ) def test_gitflow_repo_rebuild_3_channels( repo_fixture_name: str, - cli_runner: CliRunner, + run_cli: RunCliFn, build_git_flow_repo_w_3_release_channels: BuildSpecificRepoFn, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, init_mirror_repo_for_rebuild: InitMirrorRepo4RebuildFn, @@ -147,7 +145,7 @@ def test_gitflow_repo_rebuild_3_channels( else [] ) cli_cmd = [MAIN_PROG_NAME, "--strict", VERSION_SUBCMD, *build_metadata_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command actual_release_commit_text = mirror_git_repo.head.commit.message diff --git a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_4_channels.py b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_4_channels.py index e1cadb5a6..e031d86d4 100644 --- a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_4_channels.py +++ b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_4_channels.py @@ -7,8 +7,6 @@ from flatdict import FlatDict from freezegun import freeze_time -from semantic_release.cli.commands.main import main - from tests.const import ( MAIN_PROG_NAME, VERSION_SUBCMD, @@ -24,9 +22,9 @@ from pathlib import Path from unittest.mock import MagicMock - from click.testing import CliRunner from requests_mock import Mocker + from tests.conftest import RunCliFn from tests.e2e.cmd_version.bump_version.conftest import InitMirrorRepo4RebuildFn from tests.e2e.conftest import GetSanitizedChangelogContentFn from tests.fixtures.example_project import ExProjectDir @@ -55,7 +53,7 @@ ) def test_gitflow_repo_rebuild_4_channels( repo_fixture_name: str, - cli_runner: CliRunner, + run_cli: RunCliFn, build_git_flow_repo_w_4_release_channels: BuildSpecificRepoFn, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, init_mirror_repo_for_rebuild: InitMirrorRepo4RebuildFn, @@ -145,7 +143,7 @@ def test_gitflow_repo_rebuild_4_channels( else [] ) cli_cmd = [MAIN_PROG_NAME, "--strict", VERSION_SUBCMD, *build_metadata_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command actual_release_commit_text = mirror_git_repo.head.commit.message diff --git a/tests/e2e/cmd_version/bump_version/github_flow/test_repo_1_channel.py b/tests/e2e/cmd_version/bump_version/github_flow/test_repo_1_channel.py index e836716d6..fe166f540 100644 --- a/tests/e2e/cmd_version/bump_version/github_flow/test_repo_1_channel.py +++ b/tests/e2e/cmd_version/bump_version/github_flow/test_repo_1_channel.py @@ -7,8 +7,6 @@ from flatdict import FlatDict from freezegun import freeze_time -from semantic_release.cli.commands.main import main - from tests.const import ( MAIN_PROG_NAME, VERSION_SUBCMD, @@ -24,9 +22,9 @@ from pathlib import Path from unittest.mock import MagicMock - from click.testing import CliRunner from requests_mock import Mocker + from tests.conftest import RunCliFn from tests.e2e.cmd_version.bump_version.conftest import InitMirrorRepo4RebuildFn from tests.e2e.conftest import GetSanitizedChangelogContentFn from tests.fixtures.example_project import ExProjectDir @@ -55,7 +53,7 @@ ) def test_githubflow_repo_rebuild_1_channel( repo_fixture_name: str, - cli_runner: CliRunner, + run_cli: RunCliFn, build_repo_w_github_flow_w_default_release_channel: BuildSpecificRepoFn, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, init_mirror_repo_for_rebuild: InitMirrorRepo4RebuildFn, @@ -145,7 +143,7 @@ def test_githubflow_repo_rebuild_1_channel( else [] ) cli_cmd = [MAIN_PROG_NAME, "--strict", VERSION_SUBCMD, *build_metadata_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command actual_release_commit_text = mirror_git_repo.head.commit.message diff --git a/tests/e2e/cmd_version/bump_version/github_flow/test_repo_2_channels.py b/tests/e2e/cmd_version/bump_version/github_flow/test_repo_2_channels.py index 03054ac6a..3f944fd3b 100644 --- a/tests/e2e/cmd_version/bump_version/github_flow/test_repo_2_channels.py +++ b/tests/e2e/cmd_version/bump_version/github_flow/test_repo_2_channels.py @@ -7,8 +7,6 @@ from flatdict import FlatDict from freezegun import freeze_time -from semantic_release.cli.commands.main import main - from tests.const import ( MAIN_PROG_NAME, VERSION_SUBCMD, @@ -24,9 +22,9 @@ from pathlib import Path from unittest.mock import MagicMock - from click.testing import CliRunner from requests_mock import Mocker + from tests.conftest import RunCliFn from tests.e2e.cmd_version.bump_version.conftest import InitMirrorRepo4RebuildFn from tests.e2e.conftest import GetSanitizedChangelogContentFn from tests.fixtures.example_project import ExProjectDir @@ -55,7 +53,7 @@ ) def test_githubflow_repo_rebuild_2_channels( repo_fixture_name: str, - cli_runner: CliRunner, + run_cli: RunCliFn, build_repo_w_github_flow_w_feature_release_channel: BuildSpecificRepoFn, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, init_mirror_repo_for_rebuild: InitMirrorRepo4RebuildFn, @@ -145,7 +143,7 @@ def test_githubflow_repo_rebuild_2_channels( else [] ) cli_cmd = [MAIN_PROG_NAME, "--strict", VERSION_SUBCMD, *build_metadata_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command actual_release_commit_text = mirror_git_repo.head.commit.message diff --git a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk.py b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk.py index fac01bdff..e091b5d1e 100644 --- a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk.py +++ b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk.py @@ -7,8 +7,6 @@ from flatdict import FlatDict from freezegun import freeze_time -from semantic_release.cli.commands.main import main - from tests.const import ( MAIN_PROG_NAME, VERSION_SUBCMD, @@ -24,9 +22,9 @@ from pathlib import Path from unittest.mock import MagicMock - from click.testing import CliRunner from requests_mock import Mocker + from tests.conftest import RunCliFn from tests.e2e.cmd_version.bump_version.conftest import InitMirrorRepo4RebuildFn from tests.e2e.conftest import GetSanitizedChangelogContentFn from tests.fixtures.example_project import ExProjectDir @@ -57,7 +55,7 @@ ) def test_trunk_repo_rebuild_only_official_releases( repo_fixture_name: str, - cli_runner: CliRunner, + run_cli: RunCliFn, build_trunk_only_repo_w_tags: BuildSpecificRepoFn, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, init_mirror_repo_for_rebuild: InitMirrorRepo4RebuildFn, @@ -147,7 +145,7 @@ def test_trunk_repo_rebuild_only_official_releases( else [] ) cli_cmd = [MAIN_PROG_NAME, "--strict", VERSION_SUBCMD, *build_metadata_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command actual_release_commit_text = mirror_git_repo.head.commit.message diff --git a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support.py b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support.py index 6c15f2bd8..f2f8c0ccf 100644 --- a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support.py +++ b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support.py @@ -7,8 +7,6 @@ from flatdict import FlatDict from freezegun import freeze_time -from semantic_release.cli.commands.main import main - from tests.const import ( DEFAULT_BRANCH_NAME, MAIN_PROG_NAME, @@ -25,9 +23,9 @@ from pathlib import Path from unittest.mock import MagicMock - from click.testing import CliRunner from requests_mock import Mocker + from tests.conftest import RunCliFn from tests.e2e.cmd_version.bump_version.conftest import InitMirrorRepo4RebuildFn from tests.e2e.conftest import GetSanitizedChangelogContentFn from tests.fixtures.example_project import ExProjectDir @@ -56,7 +54,7 @@ ) def test_trunk_repo_rebuild_dual_version_spt_official_releases_only( repo_fixture_name: str, - cli_runner: CliRunner, + run_cli: RunCliFn, build_trunk_only_repo_w_dual_version_support: BuildSpecificRepoFn, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, init_mirror_repo_for_rebuild: InitMirrorRepo4RebuildFn, @@ -151,7 +149,7 @@ def test_trunk_repo_rebuild_dual_version_spt_official_releases_only( else [] ) cli_cmd = [MAIN_PROG_NAME, "--strict", VERSION_SUBCMD, *build_metadata_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command actual_release_commit_text = mirror_git_repo.head.commit.message diff --git a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support_w_prereleases.py b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support_w_prereleases.py index 74d5f361f..bd00935f7 100644 --- a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support_w_prereleases.py +++ b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support_w_prereleases.py @@ -7,8 +7,6 @@ from flatdict import FlatDict from freezegun import freeze_time -from semantic_release.cli.commands.main import main - from tests.const import ( DEFAULT_BRANCH_NAME, MAIN_PROG_NAME, @@ -25,9 +23,9 @@ from pathlib import Path from unittest.mock import MagicMock - from click.testing import CliRunner from requests_mock import Mocker + from tests.conftest import RunCliFn from tests.e2e.cmd_version.bump_version.conftest import InitMirrorRepo4RebuildFn from tests.e2e.conftest import GetSanitizedChangelogContentFn from tests.fixtures.example_project import ExProjectDir @@ -56,7 +54,7 @@ ) def test_trunk_repo_rebuild_dual_version_spt_w_official_n_prereleases( repo_fixture_name: str, - cli_runner: CliRunner, + run_cli: RunCliFn, build_trunk_only_repo_w_dual_version_spt_w_prereleases: BuildSpecificRepoFn, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, init_mirror_repo_for_rebuild: InitMirrorRepo4RebuildFn, @@ -171,7 +169,7 @@ def test_trunk_repo_rebuild_dual_version_spt_w_official_n_prereleases( *build_metadata_args, *prerelease_args, ] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command actual_release_commit_text = mirror_git_repo.head.commit.message diff --git a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_w_prereleases.py b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_w_prereleases.py index 9d1171e59..0d0aff235 100644 --- a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_w_prereleases.py +++ b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_w_prereleases.py @@ -7,8 +7,6 @@ from flatdict import FlatDict from freezegun import freeze_time -from semantic_release.cli.commands.main import main - from tests.const import ( MAIN_PROG_NAME, VERSION_SUBCMD, @@ -24,9 +22,9 @@ from pathlib import Path from unittest.mock import MagicMock - from click.testing import CliRunner from requests_mock import Mocker + from tests.conftest import RunCliFn from tests.e2e.cmd_version.bump_version.conftest import InitMirrorRepo4RebuildFn from tests.e2e.conftest import GetSanitizedChangelogContentFn from tests.fixtures.example_project import ExProjectDir @@ -55,7 +53,7 @@ ) def test_trunk_repo_rebuild_w_prereleases( repo_fixture_name: str, - cli_runner: CliRunner, + run_cli: RunCliFn, build_trunk_only_repo_w_prerelease_tags: BuildSpecificRepoFn, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, init_mirror_repo_for_rebuild: InitMirrorRepo4RebuildFn, @@ -165,7 +163,7 @@ def test_trunk_repo_rebuild_w_prereleases( *build_metadata_args, *prerelease_args, ] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command actual_release_commit_text = mirror_git_repo.head.commit.message diff --git a/tests/e2e/cmd_version/test_version.py b/tests/e2e/cmd_version/test_version.py index 9586073c3..f892c3cf6 100644 --- a/tests/e2e/cmd_version/test_version.py +++ b/tests/e2e/cmd_version/test_version.py @@ -7,8 +7,6 @@ import pytest from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture -from semantic_release.cli.commands.main import main - from tests.const import ( MAIN_PROG_NAME, VERSION_SUBCMD, @@ -22,10 +20,10 @@ if TYPE_CHECKING: from unittest.mock import MagicMock - from click.testing import CliRunner from git import Repo from requests_mock import Mocker + from tests.conftest import RunCliFn from tests.fixtures.example_project import GetWheelFileFn, UpdatePyprojectTomlFn from tests.fixtures.git_repo import BuiltRepoResult, GetVersionsFromRepoBuildDefFn @@ -40,7 +38,7 @@ def test_version_noop_is_noop( repo_result: BuiltRepoResult, next_release_version: str, - cli_runner: CliRunner, + run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, get_wheel_file: GetWheelFileFn, @@ -57,7 +55,7 @@ def test_version_noop_is_noop( # Act cli_cmd = [MAIN_PROG_NAME, "--noop", VERSION_SUBCMD] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command head_after = repo.head.commit @@ -88,7 +86,7 @@ def test_version_noop_is_noop( ) def test_version_no_git_verify( repo_result: BuiltRepoResult, - cli_runner: CliRunner, + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn, mocked_git_push: MagicMock, post_mocker: Mocker, @@ -127,7 +125,7 @@ def test_version_no_git_verify( # Execute cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--patch"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Take measurement after the command head_after = repo.head.commit @@ -148,7 +146,7 @@ def test_version_no_git_verify( ) def test_version_on_nonrelease_branch( repo_result: BuiltRepoResult, - cli_runner: CliRunner, + run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, ): @@ -170,7 +168,7 @@ def test_version_on_nonrelease_branch( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate (expected -> actual) assert_successful_exit_code(result, cli_cmd) @@ -193,7 +191,7 @@ def test_version_on_nonrelease_branch( def test_version_on_last_release( repo_result: BuiltRepoResult, get_versions_from_repo_build_def: GetVersionsFromRepoBuildDefFn, - cli_runner: CliRunner, + run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, ): @@ -219,7 +217,7 @@ def test_version_on_last_release( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command repo_status_after = repo.git.status(short=True) @@ -244,7 +242,7 @@ def test_version_on_last_release( ) def test_version_only_tag_push( repo_result: BuiltRepoResult, - cli_runner: CliRunner, + run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, ) -> None: @@ -265,7 +263,7 @@ def test_version_only_tag_push( "--no-commit", "--tag", ] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # capture values after the command tag_after = repo.tags[-1].name diff --git a/tests/e2e/cmd_version/test_version_build.py b/tests/e2e/cmd_version/test_version_build.py index e2b42045f..1145ce627 100644 --- a/tests/e2e/cmd_version/test_version_build.py +++ b/tests/e2e/cmd_version/test_version_build.py @@ -13,15 +13,12 @@ from flatdict import FlatDict from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture -from semantic_release.cli.commands.main import main - from tests.const import MAIN_PROG_NAME, VERSION_SUBCMD from tests.fixtures.repos import repo_w_trunk_only_conventional_commits from tests.util import assert_successful_exit_code, get_func_qual_name if TYPE_CHECKING: - from click.testing import CliRunner - + from tests.conftest import RunCliFn from tests.fixtures.example_project import GetWheelFileFn, UpdatePyprojectTomlFn from tests.fixtures.git_repo import BuiltRepoResult @@ -67,7 +64,7 @@ def test_version_runs_build_command( repo_result: BuiltRepoResult, cli_args: list[str], next_release_version: str, - cli_runner: CliRunner, + run_cli: RunCliFn, shell: str, get_wheel_file: GetWheelFileFn, example_pyproject_toml: Path, @@ -100,10 +97,10 @@ def test_version_runs_build_command( wraps=subprocess.run, ) as patched_subprocess_run, mock.patch( get_func_qual_name(shellingham.detect_shell), return_value=(shell, shell) - ), mock.patch.dict(os.environ, patched_os_environment, clear=True): + ): # ACT: run & force a new version that will trigger the build command cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, *cli_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:], env=patched_os_environment) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -147,7 +144,7 @@ def test_version_runs_build_command_windows( repo_result: BuiltRepoResult, cli_args: list[str], next_release_version: str, - cli_runner: CliRunner, + run_cli: RunCliFn, shell: str, get_wheel_file: GetWheelFileFn, example_pyproject_toml: Path, @@ -218,10 +215,10 @@ def test_version_runs_build_command_windows( wraps=subprocess.run, ) as patched_subprocess_run, mock.patch( get_func_qual_name(shellingham.detect_shell), return_value=(shell, shell) - ), mock.patch.dict(os.environ, patched_os_environment, clear=True): + ): # ACT: run & force a new version that will trigger the build command cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, *cli_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:], env=patched_os_environment) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -288,12 +285,14 @@ def test_version_runs_build_command_w_user_env( repo_result: BuiltRepoResult, cli_args: list[str], next_release_version: str, - cli_runner: CliRunner, + run_cli: RunCliFn, example_pyproject_toml: Path, update_pyproject_toml: UpdatePyprojectTomlFn, + clean_os_environment: dict[str, str], ): # Setup patched_os_environment = { + **clean_os_environment, "CI": "true", "PATH": os.getenv("PATH", ""), "HOME": "/home/username", @@ -337,7 +336,7 @@ def test_version_runs_build_command_w_user_env( ) as patched_subprocess_run, mock.patch( get_func_qual_name(shellingham.detect_shell), return_value=("bash", "/usr/bin/bash"), - ), mock.patch.dict(os.environ, patched_os_environment, clear=True): + ): cli_cmd = [ MAIN_PROG_NAME, VERSION_SUBCMD, @@ -349,7 +348,7 @@ def test_version_runs_build_command_w_user_env( ] # ACT: run & force a new version that will trigger the build command - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:], env=patched_os_environment) # Evaluate # [1] Make sure it did not error internally @@ -360,6 +359,7 @@ def test_version_runs_build_command_w_user_env( ["bash", "-c", build_command], check=True, env={ + **clean_os_environment, "NEW_VERSION": next_release_version, # injected into environment "CI": patched_os_environment["CI"], "BITBUCKET_CI": "true", # Converted @@ -384,7 +384,7 @@ def test_version_runs_build_command_w_user_env( @pytest.mark.usefixtures(repo_w_trunk_only_conventional_commits.__name__) def test_version_skips_build_command_with_skip_build( - cli_runner: CliRunner, + run_cli: RunCliFn, mocked_git_push: mock.MagicMock, post_mocker: mock.Mock, ): @@ -395,7 +395,7 @@ def test_version_skips_build_command_with_skip_build( return_value=subprocess.CompletedProcess(args=(), returncode=0), ) as patched_subprocess_run: # Act: force a new version - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) diff --git a/tests/e2e/cmd_version/test_version_bump.py b/tests/e2e/cmd_version/test_version_bump.py index 245c05505..1faa10cb2 100644 --- a/tests/e2e/cmd_version/test_version_bump.py +++ b/tests/e2e/cmd_version/test_version_bump.py @@ -11,7 +11,6 @@ # Limitation in pytest-lazy-fixture - see https://stackoverflow.com/a/69884019 from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture -from semantic_release.cli.commands.main import main from semantic_release.commit_parser.conventional import ConventionalCommitParser from semantic_release.commit_parser.emoji import EmojiCommitParser from semantic_release.commit_parser.scipy import ScipyCommitParser @@ -58,10 +57,9 @@ if TYPE_CHECKING: from unittest.mock import MagicMock - from click.testing import CliRunner from requests_mock import Mocker - from tests.conftest import GetStableDateNowFn + from tests.conftest import GetStableDateNowFn, RunCliFn from tests.fixtures.example_project import ExProjectDir, UpdatePyprojectTomlFn from tests.fixtures.git_repo import BuiltRepoResult @@ -315,7 +313,7 @@ def test_version_force_level( next_release_version: str, example_project_dir: ExProjectDir, example_pyproject_toml: Path, - cli_runner: CliRunner, + run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, ): @@ -347,7 +345,7 @@ def test_version_force_level( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, *cli_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command head_after = repo.head.commit @@ -515,7 +513,7 @@ def test_version_next_greater_than_version_one_conventional( prerelease_token: str, next_release_version: str, branch_name: str, - cli_runner: CliRunner, + run_cli: RunCliFn, file_in_repo: str, mocked_git_push: MagicMock, post_mocker: Mocker, @@ -554,7 +552,7 @@ def test_version_next_greater_than_version_one_conventional( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, *prerelease_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command head_after = repo.head.commit @@ -654,7 +652,7 @@ def test_version_next_greater_than_version_one_no_bump_conventional( prerelease_token: str, next_release_version: str, branch_name: str, - cli_runner: CliRunner, + run_cli: RunCliFn, file_in_repo: str, mocked_git_push: MagicMock, post_mocker: Mocker, @@ -693,7 +691,7 @@ def test_version_next_greater_than_version_one_no_bump_conventional( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, *prerelease_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command head_after = repo.head.commit @@ -814,7 +812,7 @@ def test_version_next_greater_than_version_one_emoji( prerelease_token: str, next_release_version: str, branch_name: str, - cli_runner: CliRunner, + run_cli: RunCliFn, file_in_repo: str, mocked_git_push: MagicMock, post_mocker: Mocker, @@ -853,7 +851,7 @@ def test_version_next_greater_than_version_one_emoji( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, *prerelease_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command head_after = repo.head.commit @@ -953,7 +951,7 @@ def test_version_next_greater_than_version_one_no_bump_emoji( prerelease_token: str, next_release_version: str, branch_name: str, - cli_runner: CliRunner, + run_cli: RunCliFn, file_in_repo: str, mocked_git_push: MagicMock, post_mocker: Mocker, @@ -992,7 +990,7 @@ def test_version_next_greater_than_version_one_no_bump_emoji( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, *prerelease_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command head_after = repo.head.commit @@ -1113,7 +1111,7 @@ def test_version_next_greater_than_version_one_scipy( prerelease_token: str, next_release_version: str, branch_name: str, - cli_runner: CliRunner, + run_cli: RunCliFn, file_in_repo: str, mocked_git_push: MagicMock, post_mocker: Mocker, @@ -1152,7 +1150,7 @@ def test_version_next_greater_than_version_one_scipy( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, *prerelease_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command head_after = repo.head.commit @@ -1252,7 +1250,7 @@ def test_version_next_greater_than_version_one_no_bump_scipy( prerelease_token: str, next_release_version: str, branch_name: str, - cli_runner: CliRunner, + run_cli: RunCliFn, file_in_repo: str, mocked_git_push: MagicMock, post_mocker: Mocker, @@ -1291,7 +1289,7 @@ def test_version_next_greater_than_version_one_no_bump_scipy( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, *prerelease_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command head_after = repo.head.commit @@ -1592,7 +1590,7 @@ def test_version_next_w_zero_dot_versions_conventional( branch_name: str, major_on_zero: bool, allow_zero_version: bool, - cli_runner: CliRunner, + run_cli: RunCliFn, file_in_repo: str, update_pyproject_toml: UpdatePyprojectTomlFn, mocked_git_push: MagicMock, @@ -1638,7 +1636,7 @@ def test_version_next_w_zero_dot_versions_conventional( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, *prerelease_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command head_after = repo.head.commit @@ -1746,7 +1744,7 @@ def test_version_next_w_zero_dot_versions_no_bump_conventional( branch_name: str, major_on_zero: bool, allow_zero_version: bool, - cli_runner: CliRunner, + run_cli: RunCliFn, file_in_repo: str, update_pyproject_toml: UpdatePyprojectTomlFn, mocked_git_push: MagicMock, @@ -1792,7 +1790,7 @@ def test_version_next_w_zero_dot_versions_no_bump_conventional( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, *prerelease_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command head_after = repo.head.commit @@ -2072,7 +2070,7 @@ def test_version_next_w_zero_dot_versions_emoji( branch_name: str, major_on_zero: bool, allow_zero_version: bool, - cli_runner: CliRunner, + run_cli: RunCliFn, file_in_repo: str, update_pyproject_toml: UpdatePyprojectTomlFn, mocked_git_push: MagicMock, @@ -2118,7 +2116,7 @@ def test_version_next_w_zero_dot_versions_emoji( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, *prerelease_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command head_after = repo.head.commit @@ -2226,7 +2224,7 @@ def test_version_next_w_zero_dot_versions_no_bump_emoji( branch_name: str, major_on_zero: bool, allow_zero_version: bool, - cli_runner: CliRunner, + run_cli: RunCliFn, file_in_repo: str, update_pyproject_toml: UpdatePyprojectTomlFn, mocked_git_push: MagicMock, @@ -2272,7 +2270,7 @@ def test_version_next_w_zero_dot_versions_no_bump_emoji( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, *prerelease_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command head_after = repo.head.commit @@ -2552,7 +2550,7 @@ def test_version_next_w_zero_dot_versions_scipy( branch_name: str, major_on_zero: bool, allow_zero_version: bool, - cli_runner: CliRunner, + run_cli: RunCliFn, file_in_repo: str, update_pyproject_toml: UpdatePyprojectTomlFn, mocked_git_push: MagicMock, @@ -2598,7 +2596,7 @@ def test_version_next_w_zero_dot_versions_scipy( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, *prerelease_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command head_after = repo.head.commit @@ -2706,7 +2704,7 @@ def test_version_next_w_zero_dot_versions_no_bump_scipy( branch_name: str, major_on_zero: bool, allow_zero_version: bool, - cli_runner: CliRunner, + run_cli: RunCliFn, file_in_repo: str, update_pyproject_toml: UpdatePyprojectTomlFn, mocked_git_push: MagicMock, @@ -2752,7 +2750,7 @@ def test_version_next_w_zero_dot_versions_no_bump_scipy( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, *prerelease_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command head_after = repo.head.commit @@ -3095,7 +3093,7 @@ def test_version_next_w_zero_dot_versions_minimums( branch_name: str, major_on_zero: bool, allow_zero_version: bool, - cli_runner: CliRunner, + run_cli: RunCliFn, file_in_repo: str, update_pyproject_toml: UpdatePyprojectTomlFn, mocked_git_push: MagicMock, @@ -3142,7 +3140,7 @@ def test_version_next_w_zero_dot_versions_minimums( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, *prerelease_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command head_after = repo.head.commit diff --git a/tests/e2e/cmd_version/test_version_changelog.py b/tests/e2e/cmd_version/test_version_changelog.py index 19a3bb3ba..212e6110e 100644 --- a/tests/e2e/cmd_version/test_version_changelog.py +++ b/tests/e2e/cmd_version/test_version_changelog.py @@ -9,7 +9,6 @@ from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture from semantic_release.changelog.context import ChangelogMode -from semantic_release.cli.commands.main import main from semantic_release.cli.config import ChangelogOutputFormat from tests.const import MAIN_PROG_NAME, VERSION_SUBCMD @@ -48,9 +47,7 @@ if TYPE_CHECKING: from pathlib import Path - from click.testing import CliRunner - - from tests.conftest import FormatDateStrFn, GetStableDateNowFn + from tests.conftest import FormatDateStrFn, GetStableDateNowFn, RunCliFn from tests.fixtures.example_project import UpdatePyprojectTomlFn from tests.fixtures.git_repo import ( BuiltRepoResult, @@ -176,7 +173,7 @@ def test_version_updates_changelog_w_new_version( get_versions_from_repo_build_def: GetVersionsFromRepoBuildDefFn, tag_format: str, update_pyproject_toml: UpdatePyprojectTomlFn, - cli_runner: CliRunner, + run_cli: RunCliFn, changelog_file: Path, insertion_flag: str, cache: pytest.Cache, @@ -255,7 +252,7 @@ def test_version_updates_changelog_w_new_version( with freeze_time(now_datetime.astimezone(timezone.utc)): cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--no-push", "--changelog"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Capture the new changelog content (os aware because of expected content) with changelog_file.open(newline=os.linesep) as rfd: @@ -307,7 +304,7 @@ def test_version_updates_changelog_wo_prev_releases( repo_result: BuiltRepoResult, cache_key: str, cache: pytest.Cache, - cli_runner: CliRunner, + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn, changelog_format: ChangelogOutputFormat, changelog_file: Path, @@ -408,7 +405,7 @@ def test_version_updates_changelog_wo_prev_releases( # Act with freeze_time(now_datetime.astimezone(timezone.utc)): cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--no-push", "--changelog"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -532,7 +529,7 @@ def test_version_initializes_changelog_in_update_mode_w_no_prev_changelog( cache_key: str, get_versions_from_repo_build_def: GetVersionsFromRepoBuildDefFn, tag_format: str, - cli_runner: CliRunner, + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn, changelog_file: Path, cache: pytest.Cache, @@ -581,7 +578,7 @@ def test_version_initializes_changelog_in_update_mode_w_no_prev_changelog( # Act with freeze_time(now_datetime.astimezone(timezone.utc)): cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--no-push", "--changelog"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -611,7 +608,7 @@ def test_version_initializes_changelog_in_update_mode_w_no_prev_changelog( @pytest.mark.usefixtures(repo_w_trunk_only_conventional_commits.__name__) def test_version_maintains_changelog_in_update_mode_w_no_flag( changelog_file: Path, - cli_runner: CliRunner, + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn, insertion_flag: str, ): @@ -641,7 +638,7 @@ def test_version_maintains_changelog_in_update_mode_w_no_flag( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--no-push", "--changelog"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -687,7 +684,7 @@ def test_version_updates_changelog_w_new_version_n_filtered_commit( commit_type: CommitConvention, tag_format: str, update_pyproject_toml: UpdatePyprojectTomlFn, - cli_runner: CliRunner, + run_cli: RunCliFn, changelog_file: Path, stable_now_date: GetStableDateNowFn, get_commits_from_repo_build_def: GetCommitsFromRepoBuildDefFn, @@ -740,7 +737,7 @@ def test_version_updates_changelog_w_new_version_n_filtered_commit( # Act with freeze_time(now_datetime.astimezone(timezone.utc)): cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--no-push", "--changelog"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Capture the new changelog content (os aware because of expected content) actual_content = changelog_file.read_text() diff --git a/tests/e2e/cmd_version/test_version_changelog_custom_commit_msg.py b/tests/e2e/cmd_version/test_version_changelog_custom_commit_msg.py index d7ee1d08f..180e7c0f4 100644 --- a/tests/e2e/cmd_version/test_version_changelog_custom_commit_msg.py +++ b/tests/e2e/cmd_version/test_version_changelog_custom_commit_msg.py @@ -10,7 +10,6 @@ from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture from semantic_release.changelog.context import ChangelogMode -from semantic_release.cli.commands.main import main from tests.const import ( MAIN_PROG_NAME, @@ -35,9 +34,7 @@ from pathlib import Path from typing import TypedDict - from click.testing import CliRunner - - from tests.conftest import GetStableDateNowFn + from tests.conftest import GetStableDateNowFn, RunCliFn from tests.e2e.conftest import GetSanitizedChangelogContentFn from tests.fixtures.example_project import UpdatePyprojectTomlFn from tests.fixtures.git_repo import ( @@ -120,7 +117,7 @@ def test_version_changelog_content_custom_commit_message_excluded_automatically( get_cfg_value_from_def: GetCfgValueFromDefFn, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, build_repo_from_definition: BuildRepoFromDefinitionFn, - cli_runner: CliRunner, + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn, changelog_file: Path, changelog_mode: ChangelogMode, @@ -192,7 +189,7 @@ def test_version_changelog_content_custom_commit_message_excluded_automatically( # Act: make the first release again with freeze_time(now_datetime.astimezone(timezone.utc)): - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) assert_successful_exit_code(result, cli_cmd) # Act: apply commits for change of version @@ -206,7 +203,7 @@ def test_version_changelog_content_custom_commit_message_excluded_automatically( # Act: make the second release again with freeze_time(now_datetime.astimezone(timezone.utc) + timedelta(minutes=1)): - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) actual_content = get_sanitized_changelog_content( repo_dir=example_project_dir, diff --git a/tests/e2e/cmd_version/test_version_github_actions.py b/tests/e2e/cmd_version/test_version_github_actions.py index c79e34b15..53917e706 100644 --- a/tests/e2e/cmd_version/test_version_github_actions.py +++ b/tests/e2e/cmd_version/test_version_github_actions.py @@ -4,8 +4,6 @@ import pytest -from semantic_release.cli.commands.main import main - from tests.const import MAIN_PROG_NAME, VERSION_SUBCMD from tests.fixtures.repos import ( repo_w_git_flow_w_alpha_prereleases_n_conventional_commits, @@ -13,26 +11,30 @@ from tests.util import actions_output_to_dict, assert_successful_exit_code if TYPE_CHECKING: - from pathlib import Path - - from click.testing import CliRunner + from tests.conftest import RunCliFn + from tests.fixtures.example_project import ExProjectDir @pytest.mark.usefixtures( repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__ ) def test_version_writes_github_actions_output( - cli_runner: CliRunner, - monkeypatch: pytest.MonkeyPatch, - tmp_path: Path, + run_cli: RunCliFn, + example_project_dir: ExProjectDir, ): - mock_output_file = tmp_path / "action.out" - monkeypatch.setenv("GITHUB_OUTPUT", str(mock_output_file.resolve())) + mock_output_file = example_project_dir / "action.out" + # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--patch", "--no-push"] + result = run_cli( + cli_cmd[1:], env={"GITHUB_OUTPUT": str(mock_output_file.resolve())} + ) + assert_successful_exit_code(result, cli_cmd) - # Act - result = cli_runner.invoke(main, cli_cmd[1:]) + if not mock_output_file.exists(): + pytest.fail( + f"Expected output file {mock_output_file} to be created, but it does not exist." + ) # Extract the output action_outputs = actions_output_to_dict( @@ -40,7 +42,6 @@ def test_version_writes_github_actions_output( ) # Evaluate - assert_successful_exit_code(result, cli_cmd) assert "released" in action_outputs assert action_outputs["released"] == "true" assert "version" in action_outputs diff --git a/tests/e2e/cmd_version/test_version_print.py b/tests/e2e/cmd_version/test_version_print.py index a259036cb..b3afc2fc3 100644 --- a/tests/e2e/cmd_version/test_version_print.py +++ b/tests/e2e/cmd_version/test_version_print.py @@ -5,8 +5,6 @@ import pytest from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture -from semantic_release.cli.commands.main import main - from tests.const import ( MAIN_PROG_NAME, VERSION_SUBCMD, @@ -31,9 +29,9 @@ if TYPE_CHECKING: from unittest.mock import MagicMock - from click.testing import CliRunner from requests_mock import Mocker + from tests.conftest import RunCliFn from tests.fixtures.git_repo import ( BuiltRepoResult, GetCfgValueFromDefFn, @@ -96,7 +94,7 @@ def test_version_print_next_version( force_args: list[str], next_release_version: str, file_in_repo: str, - cli_runner: CliRunner, + run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, ): @@ -128,7 +126,7 @@ def test_version_print_next_version( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print", *force_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command repo_status_after = repo.git.status(short=True) @@ -262,7 +260,7 @@ def test_version_print_tag_prints_next_tag( next_release_version: str, get_cfg_value_from_def: GetCfgValueFromDefFn, file_in_repo: str, - cli_runner: CliRunner, + run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, ): @@ -298,7 +296,7 @@ def test_version_print_tag_prints_next_tag( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print-tag", *force_args] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command repo_status_after = repo.git.status(short=True) @@ -326,7 +324,7 @@ def test_version_print_tag_prints_next_tag( def test_version_print_last_released_prints_version( repo_result: BuiltRepoResult, get_versions_from_repo_build_def: GetVersionsFromRepoBuildDefFn, - cli_runner: CliRunner, + run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, ): @@ -342,7 +340,7 @@ def test_version_print_last_released_prints_version( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print-last-released"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command repo_status_after = repo.git.status(short=True) @@ -376,7 +374,7 @@ def test_version_print_last_released_prints_released_if_commits( repo_result: BuiltRepoResult, get_versions_from_repo_build_def: GetVersionsFromRepoBuildDefFn, commits: list[str], - cli_runner: CliRunner, + run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, file_in_repo: str, @@ -397,7 +395,7 @@ def test_version_print_last_released_prints_released_if_commits( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print-last-released"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command repo_status_after = repo.git.status(short=True) @@ -424,7 +422,7 @@ def test_version_print_last_released_prints_released_if_commits( ) def test_version_print_last_released_prints_nothing_if_no_tags( repo_result: BuiltRepoResult, - cli_runner: CliRunner, + run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, caplog: pytest.LogCaptureFixture, @@ -438,7 +436,7 @@ def test_version_print_last_released_prints_nothing_if_no_tags( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print-last-released"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command repo_status_after = repo.git.status(short=True) @@ -469,7 +467,7 @@ def test_version_print_last_released_prints_nothing_if_no_tags( def test_version_print_last_released_on_detached_head( repo_result: BuiltRepoResult, get_versions_from_repo_build_def: GetVersionsFromRepoBuildDefFn, - cli_runner: CliRunner, + run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, ): @@ -488,7 +486,7 @@ def test_version_print_last_released_on_detached_head( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print-last-released"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command repo_status_after = repo.git.status(short=True) @@ -516,7 +514,7 @@ def test_version_print_last_released_on_detached_head( def test_version_print_last_released_on_nonrelease_branch( repo_result: BuiltRepoResult, get_versions_from_repo_build_def: GetVersionsFromRepoBuildDefFn, - cli_runner: CliRunner, + run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, ): @@ -535,7 +533,7 @@ def test_version_print_last_released_on_nonrelease_branch( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print-last-released"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command repo_status_after = repo.git.status(short=True) @@ -572,7 +570,7 @@ def test_version_print_last_released_tag_prints_correct_tag( repo_result: BuiltRepoResult, get_cfg_value_from_def: GetCfgValueFromDefFn, get_versions_from_repo_build_def: GetVersionsFromRepoBuildDefFn, - cli_runner: CliRunner, + run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, ): @@ -589,7 +587,7 @@ def test_version_print_last_released_tag_prints_correct_tag( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print-last-released-tag"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command repo_status_after = repo.git.status(short=True) @@ -631,7 +629,7 @@ def test_version_print_last_released_tag_prints_released_if_commits( get_cfg_value_from_def: GetCfgValueFromDefFn, get_versions_from_repo_build_def: GetVersionsFromRepoBuildDefFn, commits: list[str], - cli_runner: CliRunner, + run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, file_in_repo: str, @@ -653,7 +651,7 @@ def test_version_print_last_released_tag_prints_released_if_commits( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print-last-released-tag"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command repo_status_after = repo.git.status(short=True) @@ -680,7 +678,7 @@ def test_version_print_last_released_tag_prints_released_if_commits( ) def test_version_print_last_released_tag_prints_nothing_if_no_tags( repo_result: BuiltRepoResult, - cli_runner: CliRunner, + run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, caplog: pytest.LogCaptureFixture, @@ -694,7 +692,7 @@ def test_version_print_last_released_tag_prints_nothing_if_no_tags( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print-last-released-tag"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command repo_status_after = repo.git.status(short=True) @@ -734,7 +732,7 @@ def test_version_print_last_released_tag_on_detached_head( repo_result: BuiltRepoResult, get_cfg_value_from_def: GetCfgValueFromDefFn, get_versions_from_repo_build_def: GetVersionsFromRepoBuildDefFn, - cli_runner: CliRunner, + run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, ): @@ -754,7 +752,7 @@ def test_version_print_last_released_tag_on_detached_head( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print-last-released-tag"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command repo_status_after = repo.git.status(short=True) @@ -791,7 +789,7 @@ def test_version_print_last_released_tag_on_nonrelease_branch( repo_result: BuiltRepoResult, get_cfg_value_from_def: GetCfgValueFromDefFn, get_versions_from_repo_build_def: GetVersionsFromRepoBuildDefFn, - cli_runner: CliRunner, + run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, ): @@ -811,7 +809,7 @@ def test_version_print_last_released_tag_on_nonrelease_branch( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print-last-released-tag"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command repo_status_after = repo.git.status(short=True) @@ -843,7 +841,7 @@ def test_version_print_last_released_tag_on_nonrelease_branch( ) def test_version_print_next_version_fails_on_detached_head( repo_result: BuiltRepoResult, - cli_runner: CliRunner, + run_cli: RunCliFn, simulate_change_commits_n_rtn_changelog_entry: SimulateChangeCommitsNReturnChangelogEntryFn, get_commit_def_fn: GetCommitDefFn, mocked_git_push: MagicMock, @@ -870,7 +868,7 @@ def test_version_print_next_version_fails_on_detached_head( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command repo_status_after = repo.git.status(short=True) @@ -902,7 +900,7 @@ def test_version_print_next_version_fails_on_detached_head( ) def test_version_print_next_tag_fails_on_detached_head( repo_result: BuiltRepoResult, - cli_runner: CliRunner, + run_cli: RunCliFn, simulate_change_commits_n_rtn_changelog_entry: SimulateChangeCommitsNReturnChangelogEntryFn, get_commit_def_fn: GetCommitDefFn, mocked_git_push: MagicMock, @@ -929,7 +927,7 @@ def test_version_print_next_tag_fails_on_detached_head( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print-tag"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command repo_status_after = repo.git.status(short=True) diff --git a/tests/e2e/cmd_version/test_version_release_notes.py b/tests/e2e/cmd_version/test_version_release_notes.py index ccd82dc77..6786e1d3e 100644 --- a/tests/e2e/cmd_version/test_version_release_notes.py +++ b/tests/e2e/cmd_version/test_version_release_notes.py @@ -8,7 +8,6 @@ from freezegun import freeze_time from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture -from semantic_release.cli.commands.main import main from semantic_release.version.version import Version from tests.const import ( @@ -27,10 +26,9 @@ if TYPE_CHECKING: from unittest.mock import MagicMock - from click.testing import CliRunner from requests_mock import Mocker - from tests.conftest import GetStableDateNowFn + from tests.conftest import GetStableDateNowFn, RunCliFn from tests.e2e.conftest import ( RetrieveRuntimeContextFn, ) @@ -54,7 +52,7 @@ def test_custom_release_notes_template( repo_result: BuiltRepoResult, next_release_version: str, - cli_runner: CliRunner, + run_cli: RunCliFn, use_release_notes_template: UseReleaseNotesTemplateFn, retrieve_runtime_context: RetrieveRuntimeContextFn, mocked_git_push: MagicMock, @@ -69,7 +67,7 @@ def test_custom_release_notes_template( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--vcs-release"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Must run this after the action because the release history object should be pulled from the # repository after a tag is created @@ -123,7 +121,7 @@ def test_custom_release_notes_template( ) def test_default_release_notes_license_statement( repo_result: BuiltRepoResult, - cli_runner: CliRunner, + run_cli: RunCliFn, license_name: str, license_setting: str, update_pyproject_toml: UpdatePyprojectTomlFn, @@ -164,7 +162,7 @@ def test_default_release_notes_license_statement( # Act with freeze_time(now_datetime.astimezone(timezone.utc)): cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--no-changelog", "--vcs-release"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) diff --git a/tests/e2e/cmd_version/test_version_stamp.py b/tests/e2e/cmd_version/test_version_stamp.py index 9d45b6019..bad09d23b 100644 --- a/tests/e2e/cmd_version/test_version_stamp.py +++ b/tests/e2e/cmd_version/test_version_stamp.py @@ -11,7 +11,6 @@ from dotty_dict import Dotty from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture -from semantic_release.cli.commands.main import main from semantic_release.version.declarations.enum import VersionStampType from tests.const import EXAMPLE_PROJECT_NAME, MAIN_PROG_NAME, VERSION_SUBCMD @@ -29,8 +28,7 @@ if TYPE_CHECKING: from unittest.mock import MagicMock - from click.testing import CliRunner - + from tests.conftest import RunCliFn from tests.fixtures.example_project import ExProjectDir, UpdatePyprojectTomlFn from tests.fixtures.git_repo import BuiltRepoResult @@ -58,7 +56,7 @@ def test_version_only_stamp_version( repo_result: BuiltRepoResult, expected_new_version: str, - cli_runner: CliRunner, + run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: MagicMock, example_pyproject_toml: Path, @@ -93,7 +91,7 @@ def test_version_only_stamp_version( # Act (stamp the version but also create the changelog) cli_cmd = [*VERSION_STAMP_CMD, "--minor"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command head_after = repo.head.commit @@ -145,7 +143,7 @@ def test_version_only_stamp_version( @pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_stamp_version_variables_python( - cli_runner: CliRunner, + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn, example_project_dir: ExProjectDir, ) -> None: @@ -162,7 +160,7 @@ def test_stamp_version_variables_python( # Act cli_cmd = VERSION_STAMP_CMD - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Check the result assert_successful_exit_code(result, cli_cmd) @@ -178,7 +176,7 @@ def test_stamp_version_variables_python( @pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_stamp_version_toml( - cli_runner: CliRunner, + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn, default_tag_format_str: str, ) -> None: @@ -213,7 +211,7 @@ def test_stamp_version_toml( # Act cli_cmd = VERSION_STAMP_CMD - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Check the result assert_successful_exit_code(result, cli_cmd) @@ -234,7 +232,7 @@ def test_stamp_version_toml( @pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_stamp_version_variables_yaml( - cli_runner: CliRunner, + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn, ) -> None: orig_version = "0.0.0" @@ -258,7 +256,7 @@ def test_stamp_version_variables_yaml( # Act cli_cmd = VERSION_STAMP_CMD - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Check the result assert_successful_exit_code(result, cli_cmd) @@ -277,7 +275,7 @@ def test_stamp_version_variables_yaml( @pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_stamp_version_variables_yaml_cff( - cli_runner: CliRunner, + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn, ) -> None: """ @@ -314,7 +312,7 @@ def test_stamp_version_variables_yaml_cff( # Act cli_cmd = VERSION_STAMP_CMD - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Check the result assert_successful_exit_code(result, cli_cmd) @@ -333,7 +331,7 @@ def test_stamp_version_variables_yaml_cff( @pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_stamp_version_variables_json( - cli_runner: CliRunner, + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn, ) -> None: orig_version = "0.0.0" @@ -356,7 +354,7 @@ def test_stamp_version_variables_json( # Act cli_cmd = VERSION_STAMP_CMD - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Check the result assert_successful_exit_code(result, cli_cmd) @@ -375,7 +373,7 @@ def test_stamp_version_variables_json( @pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_stamp_version_variables_yaml_github_actions( - cli_runner: CliRunner, + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn, default_tag_format_str: str, ) -> None: @@ -425,7 +423,7 @@ def test_stamp_version_variables_yaml_github_actions( # Act cli_cmd = VERSION_STAMP_CMD - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Check the result assert_successful_exit_code(result, cli_cmd) @@ -447,7 +445,7 @@ def test_stamp_version_variables_yaml_github_actions( @pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_stamp_version_variables_yaml_kustomization_container_spec( - cli_runner: CliRunner, + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn, default_tag_format_str: str, ) -> None: @@ -483,7 +481,7 @@ def test_stamp_version_variables_yaml_kustomization_container_spec( # Act cli_cmd = VERSION_STAMP_CMD - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Check the result assert_successful_exit_code(result, cli_cmd) diff --git a/tests/e2e/cmd_version/test_version_strict.py b/tests/e2e/cmd_version/test_version_strict.py index c8dcb56a5..a0ff9bb8d 100644 --- a/tests/e2e/cmd_version/test_version_strict.py +++ b/tests/e2e/cmd_version/test_version_strict.py @@ -5,8 +5,6 @@ import pytest from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture -from semantic_release.cli.commands.main import main - from tests.const import MAIN_PROG_NAME, VERSION_SUBCMD from tests.fixtures.repos import repo_w_trunk_only_conventional_commits from tests.util import assert_exit_code @@ -14,9 +12,9 @@ if TYPE_CHECKING: from unittest.mock import MagicMock - from click.testing import CliRunner from requests_mock import Mocker + from tests.conftest import RunCliFn from tests.fixtures.git_repo import BuiltRepoResult, GetVersionsFromRepoBuildDefFn @@ -27,7 +25,7 @@ def test_version_already_released_when_strict( repo_result: BuiltRepoResult, get_versions_from_repo_build_def: GetVersionsFromRepoBuildDefFn, - cli_runner: CliRunner, + run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, ): @@ -50,7 +48,7 @@ def test_version_already_released_when_strict( # Act cli_cmd = [MAIN_PROG_NAME, "--strict", VERSION_SUBCMD] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # take measurement after running the version command repo_status_after = repo.git.status(short=True) @@ -75,7 +73,7 @@ def test_version_already_released_when_strict( ) def test_version_on_nonrelease_branch_when_strict( repo_result: BuiltRepoResult, - cli_runner: CliRunner, + run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, ): @@ -98,7 +96,7 @@ def test_version_on_nonrelease_branch_when_strict( # Act cli_cmd = [MAIN_PROG_NAME, "--strict", VERSION_SUBCMD] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_exit_code(2, result, cli_cmd) diff --git a/tests/e2e/test_help.py b/tests/e2e/test_help.py index a31454efd..0119586d0 100644 --- a/tests/e2e/test_help.py +++ b/tests/e2e/test_help.py @@ -17,9 +17,8 @@ if TYPE_CHECKING: from click import Command - from click.testing import CliRunner - from git import Repo + from tests.conftest import RunCliFn from tests.fixtures import UpdatePyprojectTomlFn from tests.fixtures.git_repo import BuiltRepoResult @@ -39,7 +38,7 @@ def test_help_no_repo( help_option: str, command: Command, - cli_runner: CliRunner, + run_cli: RunCliFn, change_to_ex_proj_dir: None, ): """ @@ -70,7 +69,7 @@ def test_help_no_repo( ) # Run the command with the help option - result = cli_runner.invoke(main, args, prog_name=MAIN_PROG_NAME) + result = run_cli(args, invoke_kwargs={"prog_name": MAIN_PROG_NAME}) # Evaluate result assert_exit_code(HELP_EXIT_CODE, result, [MAIN_PROG_NAME, *args]) @@ -89,7 +88,7 @@ def test_help_no_repo( def test_help_valid_config( help_option: str, command: Command, - cli_runner: CliRunner, + run_cli: RunCliFn, ): """ Test that the help message is displayed when the current directory is a git repository @@ -118,7 +117,7 @@ def test_help_valid_config( ) # Run the command with the help option - result = cli_runner.invoke(main, args, prog_name=MAIN_PROG_NAME) + result = run_cli(args, invoke_kwargs={"prog_name": MAIN_PROG_NAME}) # Evaluate result assert_exit_code(HELP_EXIT_CODE, result, [MAIN_PROG_NAME, *args]) @@ -133,11 +132,11 @@ def test_help_valid_config( (main, changelog, generate_config, publish, version), ids=lambda cmd: cmd.name, ) +@pytest.mark.usefixtures(repo_w_trunk_only_conventional_commits.__name__) def test_help_invalid_config( help_option: str, command: Command, - cli_runner: CliRunner, - repo_w_trunk_only_conventional_commits: Repo, + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn, ): """ @@ -171,7 +170,7 @@ def test_help_invalid_config( ) # Run the command with the help option - result = cli_runner.invoke(main, args, prog_name=MAIN_PROG_NAME) + result = run_cli(args, invoke_kwargs={"prog_name": MAIN_PROG_NAME}) # Evaluate result assert_exit_code(HELP_EXIT_CODE, result, [MAIN_PROG_NAME, *args]) @@ -192,7 +191,7 @@ def test_help_invalid_config( def test_help_non_release_branch( help_option: str, command: Command, - cli_runner: CliRunner, + run_cli: RunCliFn, repo_result: BuiltRepoResult, ): """ @@ -226,7 +225,7 @@ def test_help_non_release_branch( ) # Run the command with the help option - result = cli_runner.invoke(main, args, prog_name=MAIN_PROG_NAME) + result = run_cli(args, invoke_kwargs={"prog_name": MAIN_PROG_NAME}) # Evaluate result assert_exit_code(HELP_EXIT_CODE, result, [MAIN_PROG_NAME, *args]) diff --git a/tests/e2e/test_main.py b/tests/e2e/test_main.py index fc65c7f21..ff290146e 100644 --- a/tests/e2e/test_main.py +++ b/tests/e2e/test_main.py @@ -11,7 +11,6 @@ from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture from semantic_release import __version__ -from semantic_release.cli.commands.main import main from tests.const import MAIN_PROG_NAME, SUCCESS_EXIT_CODE, VERSION_SUBCMD from tests.fixtures.repos import repo_w_no_tags_conventional_commits @@ -20,8 +19,7 @@ if TYPE_CHECKING: from pathlib import Path - from click.testing import CliRunner - + from tests.conftest import RunCliFn from tests.fixtures.example_project import ExProjectDir, UpdatePyprojectTomlFn from tests.fixtures.git_repo import BuiltRepoResult @@ -50,20 +48,19 @@ def test_entrypoint_scripts(project_script_name: str): assert not proc.stderr -def test_main_prints_version_and_exits(cli_runner: CliRunner): +def test_main_prints_version_and_exits(run_cli: RunCliFn): cli_cmd = [MAIN_PROG_NAME, "--version"] # Act - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) assert result.output == f"semantic-release, version {__version__}\n" -def test_main_no_args_prints_help_text(cli_runner: CliRunner): - result = cli_runner.invoke(main, []) - assert_successful_exit_code(result, [MAIN_PROG_NAME]) +def test_main_no_args_prints_help_text(run_cli: RunCliFn): + assert_successful_exit_code(run_cli(), [MAIN_PROG_NAME]) @pytest.mark.parametrize( @@ -71,14 +68,14 @@ def test_main_no_args_prints_help_text(cli_runner: CliRunner): [lazy_fixture(repo_w_no_tags_conventional_commits.__name__)], ) def test_not_a_release_branch_exit_code( - repo_result: BuiltRepoResult, cli_runner: CliRunner + repo_result: BuiltRepoResult, run_cli: RunCliFn ): # Run anything that doesn't trigger the help text repo_result["repo"].git.checkout("-b", "branch-does-not-exist") # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--no-commit"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -90,14 +87,14 @@ def test_not_a_release_branch_exit_code( ) def test_not_a_release_branch_exit_code_with_strict( repo_result: BuiltRepoResult, - cli_runner: CliRunner, + run_cli: RunCliFn, ): # Run anything that doesn't trigger the help text repo_result["repo"].git.checkout("-b", "branch-does-not-exist") # Act cli_cmd = [MAIN_PROG_NAME, "--strict", VERSION_SUBCMD, "--no-commit"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_exit_code(2, result, cli_cmd) @@ -109,7 +106,7 @@ def test_not_a_release_branch_exit_code_with_strict( ) def test_not_a_release_branch_detached_head_exit_code( repo_result: BuiltRepoResult, - cli_runner: CliRunner, + run_cli: RunCliFn, ): expected_err_msg = ( "Detached HEAD state cannot match any release groups; no release will be made" @@ -120,7 +117,7 @@ def test_not_a_release_branch_detached_head_exit_code( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--no-commit"] - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # detached head states should throw an error as release branches cannot be determined assert_exit_code(1, result, cli_cmd) @@ -153,7 +150,7 @@ def json_file_with_no_configuration_for_psr(tmp_path: Path) -> Path: @pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_default_config_is_used_when_none_in_toml_config_file( - cli_runner: CliRunner, + run_cli: RunCliFn, toml_file_with_no_configuration_for_psr: Path, ): cli_cmd = [ @@ -165,7 +162,7 @@ def test_default_config_is_used_when_none_in_toml_config_file( ] # Act - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -173,7 +170,7 @@ def test_default_config_is_used_when_none_in_toml_config_file( @pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_default_config_is_used_when_none_in_json_config_file( - cli_runner: CliRunner, + run_cli: RunCliFn, json_file_with_no_configuration_for_psr: Path, ): cli_cmd = [ @@ -185,7 +182,7 @@ def test_default_config_is_used_when_none_in_json_config_file( ] # Act - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) @@ -193,7 +190,7 @@ def test_default_config_is_used_when_none_in_json_config_file( @pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_errors_when_config_file_does_not_exist_and_passed_explicitly( - cli_runner: CliRunner, + run_cli: RunCliFn, ): cli_cmd = [ MAIN_PROG_NAME, @@ -204,7 +201,7 @@ def test_errors_when_config_file_does_not_exist_and_passed_explicitly( ] # Act - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_exit_code(2, result, cli_cmd) @@ -213,14 +210,14 @@ def test_errors_when_config_file_does_not_exist_and_passed_explicitly( @pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_errors_when_config_file_invalid_configuration( - cli_runner: CliRunner, update_pyproject_toml: UpdatePyprojectTomlFn + run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn ): # Setup update_pyproject_toml("tool.semantic_release.remote.type", "invalidType") cli_cmd = [MAIN_PROG_NAME, "--config", "pyproject.toml", VERSION_SUBCMD] # Act - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # preprocess results stderr_lines = result.stderr.splitlines() @@ -232,7 +229,7 @@ def test_errors_when_config_file_invalid_configuration( def test_uses_default_config_when_no_config_file_found( - cli_runner: CliRunner, + run_cli: RunCliFn, example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ): @@ -252,7 +249,7 @@ def test_uses_default_config_when_no_config_file_found( cli_cmd = [MAIN_PROG_NAME, "--noop", VERSION_SUBCMD] # Act - result = cli_runner.invoke(main, cli_cmd[1:]) + result = run_cli(cli_cmd[1:]) # Evaluate assert_successful_exit_code(result, cli_cmd) diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index be1ee0fa0..391799e59 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -1032,7 +1032,9 @@ def _build_configured_base_repo( # noqa: C901 raise ValueError(f"Unknown HVCS client name: {hvcs_client_name}") # Create HVCS Client instance - hvcs = hvcs_class(example_git_https_url, hvcs_domain=hvcs_domain) + with mock.patch.dict(os.environ, {}, clear=True): + hvcs = hvcs_class(example_git_https_url, hvcs_domain=hvcs_domain) + assert hvcs.repo_name # Force the HVCS client to cache the repo name # Set tag format in configuration if tag_format_str is not None: diff --git a/tests/util.py b/tests/util.py index 63d7679ac..3d4815064 100644 --- a/tests/util.py +++ b/tests/util.py @@ -66,14 +66,13 @@ def assert_exit_code( "", # Explain what command failed "Unexpected exit code from command:", - # f" '{str.join(' ', cli_cmd)}'", indent(f"'{str.join(' ', cli_cmd)}'", " " * 2), "", # Add indentation to each line for stdout & stderr "stdout:", - indent(result.stdout, " " * 2), + indent(result.stdout, " " * 2) if result.stdout.strip() else "", "stderr:", - indent(result.stderr, " " * 2), + indent(result.stderr, " " * 2) if result.stderr.strip() else "", ], ) ) From b03daec33082d1ea6db0ba5606014b5673ec0e6f Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 18 May 2025 23:31:10 -0600 Subject: [PATCH 127/160] test(fixtures): update repos with merges to ignore merge commits in changelog --- tests/fixtures/git_repo.py | 1 + .../repos/git_flow/repo_w_1_release_channel.py | 15 ++++++++------- .../repos/git_flow/repo_w_2_release_channels.py | 11 ++++++----- .../repos/git_flow/repo_w_3_release_channels.py | 13 +++++++------ .../repos/git_flow/repo_w_4_release_channels.py | 13 +++++++------ .../repos/github_flow/repo_w_default_release.py | 5 +++-- .../repos/github_flow/repo_w_release_channels.py | 5 +++-- tests/fixtures/repos/repo_initial_commit.py | 5 +++-- .../repo_w_dual_version_support.py | 5 +++-- .../repo_w_dual_version_support_w_prereleases.py | 5 +++-- .../repos/trunk_based_dev/repo_w_no_tags.py | 5 +++-- .../repos/trunk_based_dev/repo_w_prereleases.py | 5 +++-- .../fixtures/repos/trunk_based_dev/repo_w_tags.py | 5 +++-- 13 files changed, 53 insertions(+), 40 deletions(-) diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 391799e59..60d0b4bad 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -344,6 +344,7 @@ def __call__( tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, mask_initial_release: bool = False, + ignore_merge_commits: bool = True, # Default as of v10 ) -> Sequence[RepoActions]: ... class BuildRepoFromDefinitionFn(Protocol): diff --git a/tests/fixtures/repos/git_flow/repo_w_1_release_channel.py b/tests/fixtures/repos/git_flow/repo_w_1_release_channel.py index 36a2eb3d1..9a1669888 100644 --- a/tests/fixtures/repos/git_flow/repo_w_1_release_channel.py +++ b/tests/fixtures/repos/git_flow/repo_w_1_release_channel.py @@ -103,6 +103,7 @@ def _get_repo_from_defintion( tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, mask_initial_release: bool = False, + ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: stable_now_datetime = stable_now_date() commit_timestamp_gen = ( @@ -156,7 +157,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEFAULT_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), @@ -272,7 +273,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEV_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), @@ -359,7 +360,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEV_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), @@ -464,7 +465,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEV_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), @@ -551,7 +552,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEV_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), @@ -638,7 +639,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEV_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), @@ -694,7 +695,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEV_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), diff --git a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py index 22ccd7083..9d5863a25 100644 --- a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py @@ -103,6 +103,7 @@ def _get_repo_from_defintion( tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, mask_initial_release: bool = False, + ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: stable_now_datetime = stable_now_date() commit_timestamp_gen = ( @@ -156,7 +157,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEFAULT_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), @@ -278,7 +279,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEV_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), @@ -463,7 +464,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEV_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), @@ -550,7 +551,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEV_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), @@ -637,7 +638,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEV_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), diff --git a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py index ba54e64e8..1e7f2e04d 100644 --- a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py @@ -105,6 +105,7 @@ def _get_repo_from_defintion( tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, mask_initial_release: bool = False, + ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: stable_now_datetime = stable_now_date() commit_timestamp_gen = ( @@ -158,7 +159,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEFAULT_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), @@ -286,7 +287,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEV_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), @@ -454,7 +455,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEV_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), @@ -627,7 +628,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEV_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), @@ -683,7 +684,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEV_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), @@ -762,7 +763,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEV_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), diff --git a/tests/fixtures/repos/git_flow/repo_w_4_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_4_release_channels.py index f8128015a..2c8d24e5f 100644 --- a/tests/fixtures/repos/git_flow/repo_w_4_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_4_release_channels.py @@ -115,6 +115,7 @@ def _get_repo_from_defintion( tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, mask_initial_release: bool = False, + ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: stable_now_datetime = stable_now_date() commit_timestamp_gen = ( @@ -182,7 +183,7 @@ def _get_repo_from_defintion( tgt_branch_name=BETA_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), @@ -209,7 +210,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEFAULT_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), @@ -351,7 +352,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEV_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), @@ -446,7 +447,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEV_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), @@ -556,7 +557,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEV_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), @@ -690,7 +691,7 @@ def _get_repo_from_defintion( tgt_branch_name=DEV_BRANCH_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), diff --git a/tests/fixtures/repos/github_flow/repo_w_default_release.py b/tests/fixtures/repos/github_flow/repo_w_default_release.py index 7eddbd852..b0c2da302 100644 --- a/tests/fixtures/repos/github_flow/repo_w_default_release.py +++ b/tests/fixtures/repos/github_flow/repo_w_default_release.py @@ -91,13 +91,14 @@ def get_repo_definition_4_github_flow_repo_w_default_release_channel( for a single release channel on the default branch. """ - def _get_repo_from_defintion( + def _get_repo_from_definition( commit_type: CommitConvention, hvcs_client_name: str = "github", hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, mask_initial_release: bool = False, + ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: stable_now_datetime = stable_now_date() commit_timestamp_gen = ( @@ -388,7 +389,7 @@ def _get_repo_from_defintion( return repo_construction_steps - return _get_repo_from_defintion + return _get_repo_from_definition @pytest.fixture(scope="session") diff --git a/tests/fixtures/repos/github_flow/repo_w_release_channels.py b/tests/fixtures/repos/github_flow/repo_w_release_channels.py index c0670a968..4549ffbf6 100644 --- a/tests/fixtures/repos/github_flow/repo_w_release_channels.py +++ b/tests/fixtures/repos/github_flow/repo_w_release_channels.py @@ -98,6 +98,7 @@ def _get_repo_from_defintion( tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, mask_initial_release: bool = False, + ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: stable_now_datetime = stable_now_date() commit_timestamp_gen = ( @@ -315,7 +316,7 @@ def _get_repo_from_defintion( branch_name=FIX_BRANCH_1_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), @@ -419,7 +420,7 @@ def _get_repo_from_defintion( branch_name=FEAT_BRANCH_1_NAME, ), "datetime": next(commit_timestamp_gen), - "include_in_changelog": bool(commit_type == "emoji"), + "include_in_changelog": not ignore_merge_commits, }, commit_type, ), diff --git a/tests/fixtures/repos/repo_initial_commit.py b/tests/fixtures/repos/repo_initial_commit.py index c6ffd952b..817386ba7 100644 --- a/tests/fixtures/repos/repo_initial_commit.py +++ b/tests/fixtures/repos/repo_initial_commit.py @@ -71,13 +71,14 @@ def get_repo_definition_4_repo_w_initial_commit( changelog_rst_file: Path, stable_now_date: GetStableDateNowFn, ) -> GetRepoDefinitionFn: - def _get_repo_from_defintion( + def _get_repo_from_definition( commit_type: CommitConvention, hvcs_client_name: str = "github", hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, mask_initial_release: bool = False, + ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: repo_construction_steps: list[RepoActions] = [] repo_construction_steps.extend( @@ -142,7 +143,7 @@ def _get_repo_from_defintion( return repo_construction_steps - return _get_repo_from_defintion + return _get_repo_from_definition @pytest.fixture(scope="session") diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support.py b/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support.py index 04f0bc27c..83665399c 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support.py @@ -85,13 +85,14 @@ def get_repo_definition_4_trunk_only_repo_w_dual_version_support( only official releases with latest and previous version support. """ - def _get_repo_from_defintion( + def _get_repo_from_definition( commit_type: CommitConvention, hvcs_client_name: str = "github", hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, mask_initial_release: bool = False, + ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: stable_now_datetime = stable_now_date() commit_timestamp_gen = ( @@ -358,7 +359,7 @@ def _get_repo_from_defintion( return repo_construction_steps - return _get_repo_from_defintion + return _get_repo_from_definition @pytest.fixture(scope="session") diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support_w_prereleases.py b/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support_w_prereleases.py index d0458cb90..7f57d6e01 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support_w_prereleases.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support_w_prereleases.py @@ -85,13 +85,14 @@ def get_repo_definition_4_trunk_only_repo_w_dual_version_spt_w_prereleases( only official releases with latest and previous version support. """ - def _get_repo_from_defintion( + def _get_repo_from_definition( commit_type: CommitConvention, hvcs_client_name: str = "github", hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, mask_initial_release: bool = False, + ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: stable_now_datetime = stable_now_date() commit_timestamp_gen = ( @@ -441,7 +442,7 @@ def _get_repo_from_defintion( return repo_construction_steps - return _get_repo_from_defintion + return _get_repo_from_definition @pytest.fixture(scope="session") diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py index 65acc4d50..cdb5c2afc 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py @@ -78,13 +78,14 @@ def get_repo_definition_4_trunk_only_repo_w_no_tags( any releases. """ - def _get_repo_from_defintion( + def _get_repo_from_definition( commit_type: CommitConvention, hvcs_client_name: str = "github", hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, mask_initial_release: bool = False, + ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: stable_now_datetime = stable_now_date() commit_timestamp_gen = ( @@ -174,7 +175,7 @@ def _get_repo_from_defintion( return repo_construction_steps - return _get_repo_from_defintion + return _get_repo_from_definition @pytest.fixture(scope="session") diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py index 8ac0b0674..ad4515268 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py @@ -79,13 +79,14 @@ def get_repo_definition_4_trunk_only_repo_w_prerelease_tags( official and prereleases releases. """ - def _get_repo_from_defintion( + def _get_repo_from_definition( commit_type: CommitConvention, hvcs_client_name: str = "github", hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, mask_initial_release: bool = False, + ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: stable_now_datetime = stable_now_date() commit_timestamp_gen = ( @@ -300,7 +301,7 @@ def _get_repo_from_defintion( return repo_construction_steps - return _get_repo_from_defintion + return _get_repo_from_definition @pytest.fixture(scope="session") diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py index c8bfd35d1..08ed83be3 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py @@ -81,13 +81,14 @@ def get_repo_definition_4_trunk_only_repo_w_tags( only official releases. """ - def _get_repo_from_defintion( + def _get_repo_from_definition( commit_type: CommitConvention, hvcs_client_name: str = "github", hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, mask_initial_release: bool = False, + ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: stable_now_datetime = stable_now_date() commit_timestamp_gen = ( @@ -222,7 +223,7 @@ def _get_repo_from_defintion( return repo_construction_steps - return _get_repo_from_defintion + return _get_repo_from_definition @pytest.fixture(scope="session") From 5acac1b503164ad7f491904406b6b9a7297c2789 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 18 May 2025 23:33:18 -0600 Subject: [PATCH 128/160] refactor(cli): configure only PSR for logging & not root logger --- src/semantic_release/cli/commands/main.py | 26 +++++++++++------------ 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/semantic_release/cli/commands/main.py b/src/semantic_release/cli/commands/main.py index 7f0d170e2..3286b119f 100644 --- a/src/semantic_release/cli/commands/main.py +++ b/src/semantic_release/cli/commands/main.py @@ -21,7 +21,7 @@ # pass -FORMAT = "[%(module)s.%(funcName)s] %(message)s" +FORMAT = "%(message)s" class Cli(click.MultiCommand): @@ -107,8 +107,6 @@ def main( For more information, visit https://python-semantic-release.readthedocs.io/ """ - console = Console(stderr=True) - log_levels = [ SemanticReleaseLogLevels.WARNING, SemanticReleaseLogLevels.INFO, @@ -118,18 +116,18 @@ def main( globals.log_level = log_levels[verbosity] - logging.basicConfig( - level=globals.log_level, - format=FORMAT, - datefmt="[%X]", - handlers=[ - RichHandler( - console=console, rich_tracebacks=True, tracebacks_suppress=[click] - ), - ], + # Set up our pretty console formatter + rich_handler = RichHandler( + console=Console(stderr=True), rich_tracebacks=True, tracebacks_suppress=[click] ) - - logger = logging.getLogger(__name__) + rich_handler.setFormatter(logging.Formatter(FORMAT, datefmt="[%X]")) + + # Set up logging with our pretty console formatter + logger = logging.getLogger(semantic_release.__package__) + logger.handlers.clear() + logger.filters.clear() + logger.addHandler(rich_handler) + logger.setLevel(globals.log_level) logger.debug("logging level set to: %s", logging.getLevelName(globals.log_level)) if noop: From 5ba71f5534888c9548f15d2b134e5223d335b96e Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 19 May 2025 00:12:43 -0600 Subject: [PATCH 129/160] refactor: centralize logging object across package --- .../changelog/release_history.py | 24 +++++----- src/semantic_release/changelog/template.py | 13 +++-- src/semantic_release/cli/changelog_writer.py | 9 ++-- .../cli/commands/changelog.py | 9 ++-- src/semantic_release/cli/commands/main.py | 2 +- src/semantic_release/cli/commands/publish.py | 7 +-- src/semantic_release/cli/commands/version.py | 39 ++++++++------- src/semantic_release/cli/config.py | 18 +++---- .../cli/github_actions_output.py | 6 +-- src/semantic_release/cli/masking_filter.py | 18 ++++--- src/semantic_release/cli/util.py | 14 +++--- src/semantic_release/commit_parser/angular.py | 5 +- .../commit_parser/conventional.py | 4 +- src/semantic_release/commit_parser/emoji.py | 4 +- src/semantic_release/commit_parser/scipy.py | 4 +- src/semantic_release/commit_parser/tag.py | 4 +- src/semantic_release/gitproject.py | 4 +- src/semantic_release/globals.py | 10 ++++ src/semantic_release/helpers.py | 18 +++---- src/semantic_release/hvcs/_base.py | 5 -- src/semantic_release/hvcs/bitbucket.py | 8 +--- src/semantic_release/hvcs/gitea.py | 44 ++++++++--------- src/semantic_release/hvcs/github.py | 48 +++++++++---------- src/semantic_release/hvcs/gitlab.py | 33 +++++-------- src/semantic_release/hvcs/remote_hvcs_base.py | 5 -- src/semantic_release/hvcs/util.py | 5 +- src/semantic_release/version/algorithm.py | 4 +- src/semantic_release/version/declaration.py | 11 ++--- .../version/declarations/pattern.py | 11 ++--- .../version/declarations/toml.py | 11 ++--- src/semantic_release/version/translator.py | 6 +-- src/semantic_release/version/version.py | 15 +++--- 32 files changed, 183 insertions(+), 235 deletions(-) diff --git a/src/semantic_release/changelog/release_history.py b/src/semantic_release/changelog/release_history.py index 887fa5492..b947a66ad 100644 --- a/src/semantic_release/changelog/release_history.py +++ b/src/semantic_release/changelog/release_history.py @@ -1,6 +1,5 @@ from __future__ import annotations -import logging from collections import defaultdict from datetime import datetime, timedelta, timezone from typing import TYPE_CHECKING, TypedDict @@ -11,6 +10,7 @@ from semantic_release.commit_parser.token import ParsedCommit from semantic_release.commit_parser.util import force_str from semantic_release.enums import LevelBump +from semantic_release.globals import logger from semantic_release.helpers import validate_types_in_sequence from semantic_release.version.algorithm import tags_and_versions @@ -29,8 +29,6 @@ from semantic_release.version.translator import VersionTranslator from semantic_release.version.version import Version -log = logging.getLogger(__name__) - class ReleaseHistory: @classmethod @@ -72,17 +70,17 @@ def from_git_history( for commit in repo.iter_commits("HEAD", topo_order=True): # Determine if we have found another release - log.debug("checking if commit %s matches any tags", commit.hexsha[:7]) + logger.debug("checking if commit %s matches any tags", commit.hexsha[:7]) t_v = tag_sha_2_version_lookup.get(commit.hexsha, None) if t_v is None: - log.debug("no tags correspond to commit %s", commit.hexsha) + logger.debug("no tags correspond to commit %s", commit.hexsha) else: # Unpack the tuple (overriding the current version) tag, the_version = t_v # we have found the latest commit introduced by this tag # so we create a new Release entry - log.debug("found commit %s for tag %s", commit.hexsha, tag.name) + logger.debug("found commit %s for tag %s", commit.hexsha, tag.name) # tag.object is a Commit if the tag is lightweight, otherwise # it is a TagObject with additional metadata about the tag @@ -110,7 +108,7 @@ def from_git_history( released.setdefault(the_version, release) - log.info( + logger.info( "parsing commit [%s] %s", commit.hexsha[:8], str(commit.message).replace("\n", " ")[:54], @@ -153,7 +151,7 @@ def from_git_history( if isinstance(parsed_result, ParseError) else parsed_result.type ) - log.debug("commit has type '%s'", commit_type) + logger.debug("commit has type '%s'", commit_type) has_exclusion_match = any( pattern.match(commit_message) for pattern in exclude_commit_patterns @@ -166,7 +164,7 @@ def from_git_history( ) if ignore_merge_commits and parsed_result.is_merge_commit(): - log.info("Excluding merge commit[%s]", parsed_result.short_hash) + logger.info("Excluding merge commit[%s]", parsed_result.short_hash) continue # Skip excluded commits except for any commit causing a version bump @@ -174,7 +172,7 @@ def from_git_history( # are included, then the changelog will be empty. Even if ther was other # commits included, the true reason for a version bump would be missing. if has_exclusion_match and commit_level_bump == LevelBump.NO_RELEASE: - log.info( + logger.info( "Excluding %s commit[%s] %s", "piece of squashed" if is_squash_commit else "", parsed_result.short_hash, @@ -186,7 +184,7 @@ def from_git_history( isinstance(parsed_result, ParsedCommit) and not parsed_result.include_in_changelog ): - log.info( + logger.info( str.join( " ", [ @@ -199,7 +197,7 @@ def from_git_history( continue if the_version is None: - log.info( + logger.info( "[Unreleased] adding commit[%s] to unreleased '%s'", parsed_result.short_hash, commit_type, @@ -207,7 +205,7 @@ def from_git_history( unreleased[commit_type].append(parsed_result) continue - log.info( + logger.info( "[%s] adding commit[%s] to release '%s'", the_version, parsed_result.short_hash, diff --git a/src/semantic_release/changelog/template.py b/src/semantic_release/changelog/template.py index 2b80d8f65..d74295404 100644 --- a/src/semantic_release/changelog/template.py +++ b/src/semantic_release/changelog/template.py @@ -1,6 +1,5 @@ from __future__ import annotations -import logging import os import shutil from pathlib import Path, PurePosixPath @@ -9,6 +8,7 @@ from jinja2 import FileSystemLoader from jinja2.sandbox import SandboxedEnvironment +from semantic_release.globals import logger from semantic_release.helpers import dynamic_import if TYPE_CHECKING: # pragma: no cover @@ -17,9 +17,6 @@ from jinja2 import Environment -log = logging.getLogger(__name__) - - # pylint: disable=too-many-arguments,too-many-locals def environment( template_dir: Path | str = ".", @@ -107,7 +104,7 @@ def recursive_render( and not file.startswith(".") ): output_path = (_root_dir / root.relative_to(template_dir)).resolve() - log.info("Rendering templates from %s to %s", root, output_path) + logger.info("Rendering templates from %s to %s", root, output_path) output_path.mkdir(parents=True, exist_ok=True) if file.endswith(".j2"): # We know the file ends with .j2 by the filter in the for-loop @@ -122,18 +119,20 @@ def recursive_render( # contents of a file during the rendering of the template. This mechanism # is used for inserting into a current changelog. When using stream rendering # of the same file, it always came back empty - log.debug("rendering %s to %s", src_file_path, output_file_path) + logger.debug("rendering %s to %s", src_file_path, output_file_path) rendered_file = environment.get_template(src_file_path).render().rstrip() with open(output_file_path, "w", encoding="utf-8") as output_file: output_file.write(f"{rendered_file}\n") rendered_paths.append(output_file_path) + else: src_file = str((root / file).resolve()) target_file = str((output_path / file).resolve()) - log.debug( + logger.debug( "source file %s is not a template, copying to %s", src_file, target_file ) shutil.copyfile(src_file, target_file) rendered_paths.append(target_file) + return rendered_paths diff --git a/src/semantic_release/cli/changelog_writer.py b/src/semantic_release/cli/changelog_writer.py index 5ee86457e..96020b73a 100644 --- a/src/semantic_release/cli/changelog_writer.py +++ b/src/semantic_release/cli/changelog_writer.py @@ -2,7 +2,6 @@ import os from contextlib import suppress -from logging import getLogger from pathlib import Path from typing import TYPE_CHECKING @@ -25,6 +24,7 @@ ) from semantic_release.cli.util import noop_report from semantic_release.errors import InternalError +from semantic_release.globals import logger from semantic_release.helpers import sort_numerically if TYPE_CHECKING: # pragma: no cover @@ -36,9 +36,6 @@ from semantic_release.hvcs._base import HvcsBase -log = getLogger(__name__) - - def get_default_tpl_dir(style: str, sub_dir: str | None = None) -> Path: module_base_path = Path(str(files(semantic_release.__name__))) default_templates_path = module_base_path.joinpath( @@ -210,7 +207,9 @@ def write_changelog_files( noop=noop, ) - log.info("No contents found in %r, using default changelog template", template_dir) + logger.info( + "No contents found in %r, using default changelog template", template_dir + ) return [ write_default_changelog( changelog_file=runtime_ctx.changelog_file, diff --git a/src/semantic_release/cli/commands/changelog.py b/src/semantic_release/cli/commands/changelog.py index 316b44450..523c1d2a5 100644 --- a/src/semantic_release/cli/commands/changelog.py +++ b/src/semantic_release/cli/commands/changelog.py @@ -1,6 +1,5 @@ from __future__ import annotations -import logging from contextlib import suppress from pathlib import Path from typing import TYPE_CHECKING @@ -15,15 +14,13 @@ write_changelog_files, ) from semantic_release.cli.util import noop_report +from semantic_release.globals import logger from semantic_release.hvcs.remote_hvcs_base import RemoteHvcsBase if TYPE_CHECKING: # pragma: no cover from semantic_release.cli.cli_context import CliContextObj -log = logging.getLogger(__name__) - - def get_license_name_for_release(tag_name: str, project_root: Path) -> str: # Retrieve the license name at the time of the specific release tag project_metadata: dict[str, str] = {} @@ -174,7 +171,7 @@ def changelog(cli_ctx: CliContextObj, release_tag: str | None) -> None: hvcs_client=hvcs_client, noop=runtime.global_cli_options.noop, ) - except Exception as e: - log.exception(e) + except Exception as e: # noqa: BLE001 # TODO: catch specific exceptions + logger.exception(e) click.echo("Failed to post release notes to remote", err=True) ctx.exit(1) diff --git a/src/semantic_release/cli/commands/main.py b/src/semantic_release/cli/commands/main.py index 3286b119f..08b893d70 100644 --- a/src/semantic_release/cli/commands/main.py +++ b/src/semantic_release/cli/commands/main.py @@ -123,7 +123,7 @@ def main( rich_handler.setFormatter(logging.Formatter(FORMAT, datefmt="[%X]")) # Set up logging with our pretty console formatter - logger = logging.getLogger(semantic_release.__package__) + logger = globals.logger logger.handlers.clear() logger.filters.clear() logger.addHandler(rich_handler) diff --git a/src/semantic_release/cli/commands/publish.py b/src/semantic_release/cli/commands/publish.py index 0d354c387..4efab72de 100644 --- a/src/semantic_release/cli/commands/publish.py +++ b/src/semantic_release/cli/commands/publish.py @@ -1,12 +1,12 @@ from __future__ import annotations -import logging from typing import TYPE_CHECKING import click from git import Repo from semantic_release.cli.util import noop_report +from semantic_release.globals import logger from semantic_release.hvcs.remote_hvcs_base import RemoteHvcsBase from semantic_release.version.algorithm import tags_and_versions @@ -14,9 +14,6 @@ from semantic_release.cli.cli_context import CliContextObj -log = logging.getLogger(__name__) - - def publish_distributions( tag: str, hvcs_client: RemoteHvcsBase, @@ -36,7 +33,7 @@ def publish_distributions( ) return - log.info("Uploading distributions to release") + logger.info("Uploading distributions to release") for pattern in dist_glob_patterns: hvcs_client.upload_dists(tag=tag, dist_glob=pattern) # type: ignore[attr-defined] diff --git a/src/semantic_release/cli/commands/version.py b/src/semantic_release/cli/commands/version.py index 86d209937..66b42c4cb 100644 --- a/src/semantic_release/cli/commands/version.py +++ b/src/semantic_release/cli/commands/version.py @@ -1,6 +1,5 @@ from __future__ import annotations -import logging import os import subprocess import sys @@ -30,6 +29,7 @@ UnexpectedResponse, ) from semantic_release.gitproject import GitProject +from semantic_release.globals import logger from semantic_release.hvcs.remote_hvcs_base import RemoteHvcsBase from semantic_release.version.algorithm import ( next_version, @@ -48,9 +48,6 @@ from semantic_release.version.version import Version -log = logging.getLogger(__name__) - - def is_forced_prerelease( as_prerelease: bool, forced_level_bump: LevelBump | None, prerelease: bool ) -> bool: @@ -62,7 +59,7 @@ def is_forced_prerelease( Otherwise (``force_level is None``) use the value of ``prerelease`` """ local_vars = list(locals().items()) - log.debug( + logger.debug( "%s: %s", is_forced_prerelease.__name__, str.join(", ", iter(f"{k} = {v}" for k, v in local_vars)), @@ -143,7 +140,7 @@ def apply_version_to_source_files( return [] if not noop: - log.debug("Updating version %s in repository files...", version) + logger.debug("Updating version %s in repository files...", version) paths = list( map( @@ -181,8 +178,8 @@ def shell( try: shell, _ = shellingham.detect_shell() except shellingham.ShellDetectionFailure: - log.warning("failed to detect shell, using default shell: %s", DEFAULT_SHELL) - log.debug("stack trace", exc_info=True) + logger.warning("failed to detect shell, using default shell: %s", DEFAULT_SHELL) + logger.debug("stack trace", exc_info=True) shell = DEFAULT_SHELL if not shell: @@ -261,7 +258,7 @@ def build_distributions( noop_report(f"would have run the build_command {build_command}") return - log.info("Running build command %s", build_command) + logger.info("Running build command %s", build_command) rprint(f"[bold green]:hammer_and_wrench: Running build command: {build_command}") build_env_vars: dict[str, str] = dict( @@ -295,8 +292,8 @@ def build_distributions( shell(build_command, env=build_env_vars, check=True) rprint("[bold green]Build completed successfully!") except subprocess.CalledProcessError as exc: - log.exception(exc) - log.error("Build command failed with exit code %s", exc.returncode) # noqa: TRY400 + logger.exception(exc) + logger.error("Build command failed with exit code %s", exc.returncode) # noqa: TRY400 raise BuildDistributionsError from exc @@ -453,7 +450,7 @@ def version( # noqa: C901 if not ( last_release := last_released(config.repo_dir, tag_format=config.tag_format) ): - log.warning("No release tags found.") + logger.warning("No release tags found.") return click.echo(last_release[0] if print_last_released_tag else last_release[1]) @@ -482,22 +479,24 @@ def version( # noqa: C901 ) if prerelease_token: - log.info("Forcing use of %s as the prerelease token", prerelease_token) + logger.info("Forcing use of %s as the prerelease token", prerelease_token) translator.prerelease_token = prerelease_token # 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") + logger.info("changes will not be pushed because --no-commit disables pushing") push_changes &= commit_changes # Only push if we're creating a tag if push_changes and not create_tag and not commit_changes: - log.info("new tag will not be pushed because --no-tag disables pushing") + logger.info("new tag will not be pushed because --no-tag disables pushing") push_changes &= create_tag # Only make a release if we're pushing the changes if make_vcs_release and not push_changes: - log.info("No vcs release will be created because pushing changes is disabled") + logger.info( + "No vcs release will be created because pushing changes is disabled" + ) make_vcs_release &= push_changes if not forced_level_bump: @@ -511,7 +510,7 @@ def version( # noqa: C901 allow_zero_version=runtime.allow_zero_version, ) else: - log.warning( + logger.warning( "Forcing a '%s' release due to '--%s' command-line flag", force_level, ( @@ -665,7 +664,7 @@ def version( # noqa: C901 noop=opts.noop, ) except GitCommitEmptyIndexError: - log.info("No local changes to add to any commit, skipping") + logger.info("No local changes to add to any commit, skipping") # Tag the version after potentially creating a new HEAD commit. # This way if no source code is modified, i.e. all metadata updates @@ -711,7 +710,7 @@ def version( # noqa: C901 return if not isinstance(hvcs_client, RemoteHvcsBase): - log.info("Remote does not support releases. Skipping release creation...") + logger.info("Remote does not support releases. Skipping release creation...") return license_cfg = runtime.project_metadata.get( @@ -774,7 +773,7 @@ def version( # noqa: C901 exception = err finally: if exception is not None: - log.exception(exception) + logger.exception(exception) click.echo(str(exception), err=True) if help_message: click.echo(help_message, err=True) diff --git a/src/semantic_release/cli/config.py b/src/semantic_release/cli/config.py index be8ad4a5e..0ed1eba78 100644 --- a/src/semantic_release/cli/config.py +++ b/src/semantic_release/cli/config.py @@ -54,13 +54,13 @@ NotAReleaseBranch, ParserLoadError, ) +from semantic_release.globals import logger from semantic_release.helpers import dynamic_import from semantic_release.version.declarations.i_version_replacer import IVersionReplacer from semantic_release.version.declarations.pattern import PatternVersionDeclaration from semantic_release.version.declarations.toml import TomlVersionDeclaration from semantic_release.version.translator import VersionTranslator -log = logging.getLogger(__name__) NonEmptyString = Annotated[str, Field(..., min_length=1)] @@ -179,7 +179,7 @@ def validate_match(cls, patterns: Tuple[str, ...]) -> Tuple[str, ...]: @field_validator("changelog_file", mode="after") @classmethod def changelog_file_deprecation_warning(cls, val: str) -> str: - log.warning( + logger.warning( str.join( " ", [ @@ -329,7 +329,7 @@ def check_insecure_flag(self, url_str: str, field_name: str) -> None: ) if scheme == "https" and self.insecure: - log.warning( + logger.warning( str.join( "\n", [ @@ -402,7 +402,7 @@ def verify_git_repo_dir(cls, dir_path: Path) -> Path: @classmethod def tag_commit_parser_deprecation_warning(cls, val: str) -> str: if val == "tag": - log.warning( + logger.warning( str.join( " ", [ @@ -418,7 +418,7 @@ def tag_commit_parser_deprecation_warning(cls, val: str) -> str: @classmethod def angular_commit_parser_deprecation_warning(cls, val: str) -> str: if val == "angular": - log.warning( + logger.warning( str.join( " ", [ @@ -585,14 +585,14 @@ def select_branch_options( ) -> BranchConfig: for group, options in choices.items(): if regexp(options.match).match(active_branch): - log.info( + logger.info( "Using group %r options, as %r matches %r", group, options.match, active_branch, ) return options - log.debug( + logger.debug( "Rejecting group %r as %r doesn't match %r", group, options.match, @@ -774,10 +774,10 @@ def from_raw_config( # noqa: C901 # Provide warnings if the token is missing if not raw.remote.token: - log.debug("hvcs token is not set") + logger.debug("hvcs token is not set") if not raw.remote.ignore_token_for_push: - log.warning("Token value is missing!") + logger.warning("Token value is missing!") # hvcs_client hvcs_client_cls = _known_hvcs[raw.remote.type] diff --git a/src/semantic_release/cli/github_actions_output.py b/src/semantic_release/cli/github_actions_output.py index 253b2419c..7d7782922 100644 --- a/src/semantic_release/cli/github_actions_output.py +++ b/src/semantic_release/cli/github_actions_output.py @@ -1,12 +1,10 @@ from __future__ import annotations -import logging import os +from semantic_release.globals import logger from semantic_release.version.version import Version -log = logging.getLogger(__name__) - class VersionGitHubActionsOutput: OUTPUT_ENV_VAR = "GITHUB_OUTPUT" @@ -71,7 +69,7 @@ def to_output_text(self) -> str: def write_if_possible(self, filename: str | None = None) -> None: output_file = filename or os.getenv(self.OUTPUT_ENV_VAR) if not output_file: - log.info("not writing GitHub Actions output, as no file specified") + logger.info("not writing GitHub Actions output, as no file specified") return with open(output_file, "a", encoding="utf-8") as f: diff --git a/src/semantic_release/cli/masking_filter.py b/src/semantic_release/cli/masking_filter.py index 2c0fdb947..f2e4f825f 100644 --- a/src/semantic_release/cli/masking_filter.py +++ b/src/semantic_release/cli/masking_filter.py @@ -1,16 +1,20 @@ from __future__ import annotations -import logging import re from collections import defaultdict -from typing import Iterable +from logging import Filter as LoggingFilter +from typing import TYPE_CHECKING -log = logging.getLogger(__name__) +from semantic_release.globals import logger + +if TYPE_CHECKING: # pragma: no cover + from logging import LogRecord + from typing import Iterable # https://relaxdiego.com/2014/07/logging-in-python.html # Updated/adapted for Python3 -class MaskingFilter(logging.Filter): +class MaskingFilter(LoggingFilter): REPLACE_STR = "*" * 4 _UNWANTED = frozenset([s for obj in ("", None) for s in (repr(obj), str(obj))]) @@ -27,11 +31,11 @@ def __init__( def add_mask_for(self, data: str, name: str = "redacted") -> MaskingFilter: if data and data not in self._UNWANTED: - log.debug("Adding redact pattern '%r' to redact_patterns", name) + logger.debug("Adding redact pattern '%r' to redact_patterns", name) self._redact_patterns[name].add(data) return self - def filter(self, record: logging.LogRecord) -> bool: + def filter(self, record: LogRecord) -> bool: # Note if we blindly mask all types, we will actually cast arguments to # log functions from external libraries to strings before they are # formatted into the message - for example, a dependency calling @@ -58,7 +62,7 @@ def filter(self, record: logging.LogRecord) -> bool: def mask(self, msg: str) -> str: if not isinstance(msg, str): - log.debug( # type: ignore[unreachable] + logger.debug( # type: ignore[unreachable] "cannot mask object of type %s", type(msg) ) return msg diff --git a/src/semantic_release/cli/util.py b/src/semantic_release/cli/util.py index 97d264b02..0f62d3d10 100644 --- a/src/semantic_release/cli/util.py +++ b/src/semantic_release/cli/util.py @@ -3,7 +3,6 @@ from __future__ import annotations import json -import logging import sys from pathlib import Path from textwrap import dedent, indent @@ -14,8 +13,7 @@ from tomlkit.exceptions import TOMLKitError from semantic_release.errors import InvalidConfiguration - -log = logging.getLogger(__name__) +from semantic_release.globals import logger def rprint(msg: str) -> None: @@ -76,21 +74,21 @@ def load_raw_config_file(config_file: Path | str) -> dict[Any, Any]: This function will also raise FileNotFoundError if it is raised while trying to read the specified configuration file """ - log.info("Loading configuration from %s", config_file) + logger.info("Loading configuration from %s", config_file) raw_text = (Path() / config_file).resolve().read_text(encoding="utf-8") try: - log.debug("Trying to parse configuration %s in TOML format", config_file) + logger.debug("Trying to parse configuration %s in TOML format", config_file) return parse_toml(raw_text) except InvalidConfiguration as e: - log.debug("Configuration %s is invalid TOML: %s", config_file, str(e)) - log.debug("trying to parse %s as JSON", config_file) + logger.debug("Configuration %s is invalid TOML: %s", config_file, str(e)) + logger.debug("trying to parse %s as JSON", config_file) try: # could be a "parse_json" function but it's a one-liner here return json.loads(raw_text)["semantic_release"] except KeyError: # valid configuration, but no "semantic_release" or "tool.semantic_release" # top level key - log.debug( + logger.debug( "configuration has no 'semantic_release' or 'tool.semantic_release' " "top-level key" ) diff --git a/src/semantic_release/commit_parser/angular.py b/src/semantic_release/commit_parser/angular.py index ca739cc91..eeef82796 100644 --- a/src/semantic_release/commit_parser/angular.py +++ b/src/semantic_release/commit_parser/angular.py @@ -5,7 +5,6 @@ from __future__ import annotations -import logging import re from functools import reduce from itertools import zip_longest @@ -31,15 +30,13 @@ ) from semantic_release.enums import LevelBump from semantic_release.errors import InvalidParserOptions +from semantic_release.globals import logger from semantic_release.helpers import sort_numerically, text_reducer if TYPE_CHECKING: # pragma: no cover from git.objects.commit import Commit -logger = logging.getLogger(__name__) - - def _logged_parse_error(commit: Commit, error: str) -> ParseError: logger.debug(error) return ParseError(commit, error=error) diff --git a/src/semantic_release/commit_parser/conventional.py b/src/semantic_release/commit_parser/conventional.py index b4eac746c..3cd50d9c7 100644 --- a/src/semantic_release/commit_parser/conventional.py +++ b/src/semantic_release/commit_parser/conventional.py @@ -3,7 +3,6 @@ import re from functools import reduce from itertools import zip_longest -from logging import getLogger from re import compile as regexp from textwrap import dedent from typing import TYPE_CHECKING, Tuple @@ -26,6 +25,7 @@ ) from semantic_release.enums import LevelBump from semantic_release.errors import InvalidParserOptions +from semantic_release.globals import logger from semantic_release.helpers import sort_numerically, text_reducer if TYPE_CHECKING: # pragma: no cover @@ -33,7 +33,7 @@ def _logged_parse_error(commit: Commit, error: str) -> ParseError: - getLogger(__name__).debug(error) + logger.debug(error) return ParseError(commit, error=error) diff --git a/src/semantic_release/commit_parser/emoji.py b/src/semantic_release/commit_parser/emoji.py index 2199c6948..801160208 100644 --- a/src/semantic_release/commit_parser/emoji.py +++ b/src/semantic_release/commit_parser/emoji.py @@ -2,7 +2,6 @@ from __future__ import annotations -import logging import re from functools import reduce from itertools import zip_longest @@ -27,10 +26,9 @@ ) from semantic_release.enums import LevelBump from semantic_release.errors import InvalidParserOptions +from semantic_release.globals import logger from semantic_release.helpers import sort_numerically, text_reducer -logger = logging.getLogger(__name__) - @dataclass class EmojiParserOptions(ParserOptions): diff --git a/src/semantic_release/commit_parser/scipy.py b/src/semantic_release/commit_parser/scipy.py index 6083d7359..7e0e6b246 100644 --- a/src/semantic_release/commit_parser/scipy.py +++ b/src/semantic_release/commit_parser/scipy.py @@ -49,7 +49,6 @@ import re from functools import reduce from itertools import zip_longest -from logging import getLogger from re import compile as regexp from textwrap import dedent from typing import TYPE_CHECKING, Tuple @@ -71,6 +70,7 @@ ) from semantic_release.enums import LevelBump from semantic_release.errors import InvalidParserOptions +from semantic_release.globals import logger from semantic_release.helpers import sort_numerically, text_reducer if TYPE_CHECKING: # pragma: no cover @@ -78,7 +78,7 @@ def _logged_parse_error(commit: Commit, error: str) -> ParseError: - getLogger(__name__).debug(error) + logger.debug(error) return ParseError(commit, error=error) diff --git a/src/semantic_release/commit_parser/tag.py b/src/semantic_release/commit_parser/tag.py index b9a042cc7..c6c90a936 100644 --- a/src/semantic_release/commit_parser/tag.py +++ b/src/semantic_release/commit_parser/tag.py @@ -2,7 +2,6 @@ from __future__ import annotations -import logging import re from git.objects.commit import Commit @@ -12,8 +11,7 @@ from semantic_release.commit_parser.token import ParsedCommit, ParseError, ParseResult from semantic_release.commit_parser.util import breaking_re, parse_paragraphs from semantic_release.enums import LevelBump - -logger = logging.getLogger(__name__) +from semantic_release.globals import logger re_parser = re.compile(r"(?P[^\n]+)" + r"(:?\n\n(?P.+))?", re.DOTALL) diff --git a/src/semantic_release/gitproject.py b/src/semantic_release/gitproject.py index ef174d85c..0e4592599 100644 --- a/src/semantic_release/gitproject.py +++ b/src/semantic_release/gitproject.py @@ -4,7 +4,6 @@ from contextlib import nullcontext from datetime import datetime -from logging import getLogger from pathlib import Path from typing import TYPE_CHECKING @@ -19,6 +18,7 @@ GitPushError, GitTagError, ) +from semantic_release.globals import logger if TYPE_CHECKING: # pragma: no cover from contextlib import _GeneratorContextManager @@ -36,7 +36,7 @@ def __init__( credential_masker: MaskingFilter | None = None, ) -> None: self._project_root = Path(directory).resolve() - self._logger = getLogger(__name__) + self._logger = logger self._cred_masker = credential_masker or MaskingFilter() self._commit_author = commit_author diff --git a/src/semantic_release/globals.py b/src/semantic_release/globals.py index a0ac61ddb..deb6af987 100644 --- a/src/semantic_release/globals.py +++ b/src/semantic_release/globals.py @@ -2,7 +2,17 @@ from __future__ import annotations +from logging import getLogger +from typing import TYPE_CHECKING + from semantic_release.enums import SemanticReleaseLogLevels +if TYPE_CHECKING: + from logging import Logger + +# GLOBAL VARIABLES log_level: SemanticReleaseLogLevels = SemanticReleaseLogLevels.WARNING """int: Logging level for semantic-release""" + +logger: Logger = getLogger(__package__) +"""Logger for semantic-release""" diff --git a/src/semantic_release/helpers.py b/src/semantic_release/helpers.py index 5f6723ec4..c50369575 100644 --- a/src/semantic_release/helpers.py +++ b/src/semantic_release/helpers.py @@ -1,7 +1,6 @@ from __future__ import annotations import importlib.util -import logging import os import re import string @@ -12,13 +11,14 @@ from typing import TYPE_CHECKING, Any, Callable, NamedTuple, Sequence, TypeVar from urllib.parse import urlsplit +from semantic_release.globals import logger + if TYPE_CHECKING: # pragma: no cover + from logging import Logger from re import Pattern from typing import Iterable -log = logging.getLogger(__name__) - number_pattern = regexp(r"(?P\S*?)(?P\d[\d,]*)\b") hex_number_pattern = regexp( r"(?P\S*?)(?:0x)?(?P[0-9a-f]+)\b", IGNORECASE @@ -118,7 +118,7 @@ def check_tag_format(tag_format: str) -> None: _FuncType = Callable[..., _R] -def logged_function(logger: logging.Logger) -> Callable[[_FuncType[_R]], _FuncType[_R]]: +def logged_function(logger: Logger) -> Callable[[_FuncType[_R]], _FuncType[_R]]: """ Decorator which adds debug logging of a function's input arguments and return value. @@ -151,7 +151,7 @@ def _wrapper(*args: Any, **kwargs: Any) -> _R: return _logged_function -@logged_function(log) +@logged_function(logger) def dynamic_import(import_path: str) -> Any: """ Dynamically import an object from a conventionally formatted "module:attribute" @@ -175,7 +175,7 @@ def dynamic_import(import_path: str) -> Any: ) if module_path not in sys.modules: - log.debug("Loading '%s' from file '%s'", module_path, module_filepath) + logger.debug("Loading '%s' from file '%s'", module_path, module_filepath) spec = importlib.util.spec_from_file_location( module_path, str(module_filepath) ) @@ -190,9 +190,9 @@ def dynamic_import(import_path: str) -> Any: # Otherwise, import as a module try: - log.debug("Importing module '%s'", module_name) + logger.debug("Importing module '%s'", module_name) module = importlib.import_module(module_name) - log.debug("Loading '%s' from module '%s'", attr, module_name) + logger.debug("Loading '%s' from module '%s'", attr, module_name) return getattr(module, attr) except TypeError as err: raise ImportError( @@ -242,7 +242,7 @@ def parse_git_url(url: str) -> ParsedGitUrl: Raises ValueError if the url can't be parsed. """ - log.debug("Parsing git url %r", url) + logger.debug("Parsing git url %r", url) # Normalizers are a list of tuples of (pattern, replacement) normalizers = [ diff --git a/src/semantic_release/hvcs/_base.py b/src/semantic_release/hvcs/_base.py index 60c6a5f87..fc6668dcd 100644 --- a/src/semantic_release/hvcs/_base.py +++ b/src/semantic_release/hvcs/_base.py @@ -2,7 +2,6 @@ from __future__ import annotations -import logging import warnings from abc import ABCMeta, abstractmethod from functools import lru_cache @@ -14,10 +13,6 @@ from typing import Any, Callable -# Globals -logger = logging.getLogger(__name__) - - class HvcsBase(metaclass=ABCMeta): """ Interface for subclasses interacting with a remote vcs environment diff --git a/src/semantic_release/hvcs/bitbucket.py b/src/semantic_release/hvcs/bitbucket.py index e0f9e8656..08c2aa6be 100644 --- a/src/semantic_release/hvcs/bitbucket.py +++ b/src/semantic_release/hvcs/bitbucket.py @@ -5,7 +5,6 @@ from __future__ import annotations -import logging import os from functools import lru_cache from pathlib import PurePosixPath @@ -14,16 +13,13 @@ from urllib3.util.url import Url, parse_url +from semantic_release.globals import logger from semantic_release.hvcs.remote_hvcs_base import RemoteHvcsBase if TYPE_CHECKING: # pragma: no cover from typing import Any, Callable -# Globals -log = logging.getLogger(__name__) - - class Bitbucket(RemoteHvcsBase): """ Bitbucket HVCS interface for interacting with BitBucket repositories @@ -161,7 +157,7 @@ def _derive_api_url_from_base_domain(self) -> Url: def _get_repository_owner_and_name(self) -> tuple[str, str]: # ref: https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/ if "BITBUCKET_REPO_FULL_NAME" in os.environ: - log.info("Getting repository owner and name from environment variables.") + logger.info("Getting repository owner and name from environment variables.") owner, name = os.environ["BITBUCKET_REPO_FULL_NAME"].rsplit("/", 1) return owner, name diff --git a/src/semantic_release/hvcs/gitea.py b/src/semantic_release/hvcs/gitea.py index c8e241122..994c2459c 100644 --- a/src/semantic_release/hvcs/gitea.py +++ b/src/semantic_release/hvcs/gitea.py @@ -3,7 +3,6 @@ from __future__ import annotations import glob -import logging import os from pathlib import PurePosixPath from re import compile as regexp @@ -18,6 +17,7 @@ IncompleteReleaseError, UnexpectedResponse, ) +from semantic_release.globals import logger from semantic_release.helpers import logged_function from semantic_release.hvcs.remote_hvcs_base import RemoteHvcsBase from semantic_release.hvcs.token_auth import TokenAuth @@ -27,10 +27,6 @@ from typing import Any, Callable -# Globals -log = logging.getLogger(__name__) - - class Gitea(RemoteHvcsBase): """Gitea helper class""" @@ -82,7 +78,7 @@ def __init__( allow_insecure=allow_insecure, ) - @logged_function(log) + @logged_function(logger) def create_release( self, tag: str, @@ -126,7 +122,7 @@ def create_release( ) return -1 - log.info("Creating release for tag %s", tag) + logger.info("Creating release for tag %s", tag) releases_endpoint = self.create_api_url( endpoint=f"/repos/{self.owner}/{self.repo_name}/releases", ) @@ -146,7 +142,7 @@ def create_release( try: release_id: int = response.json()["id"] - log.info("Successfully created release with ID: %s", release_id) + logger.info("Successfully created release with ID: %s", release_id) except JSONDecodeError as err: raise UnexpectedResponse("Unreadable json response") from err except KeyError as err: @@ -154,7 +150,7 @@ def create_release( errors = [] for asset in assets or []: - log.info("Uploading asset %s", asset) + logger.info("Uploading asset %s", asset) try: self.upload_release_asset(release_id, asset) except HTTPError as err: @@ -168,13 +164,13 @@ def create_release( return release_id for error in errors: - log.exception(error) + logger.exception(error) raise IncompleteReleaseError( f"Failed to upload asset{'s' if len(errors) > 1 else ''} to release!" ) - @logged_function(log) + @logged_function(logger) @suppress_not_found def get_release_id_by_tag(self, tag: str) -> int | None: """ @@ -200,7 +196,7 @@ def get_release_id_by_tag(self, tag: str) -> int | None: except KeyError as err: raise UnexpectedResponse("JSON response is missing an id") from err - @logged_function(log) + @logged_function(logger) def edit_release_notes(self, release_id: int, release_notes: str) -> int: """ Edit a release with updated change notes @@ -210,7 +206,7 @@ def edit_release_notes(self, release_id: int, release_notes: str) -> int: :return: The ID of the release that was edited """ - log.info("Updating release %s", release_id) + logger.info("Updating release %s", release_id) release_endpoint = self.create_api_url( endpoint=f"/repos/{self.owner}/{self.repo_name}/releases/{release_id}", ) @@ -225,7 +221,7 @@ def edit_release_notes(self, release_id: int, release_notes: str) -> int: return release_id - @logged_function(log) + @logged_function(logger) def create_or_update_release( self, tag: str, release_notes: str, prerelease: bool = False ) -> int: @@ -236,12 +232,12 @@ def create_or_update_release( :return: The status of the request """ - log.info("Creating release for %s", tag) + logger.info("Creating release for %s", tag) try: return self.create_release(tag, release_notes, prerelease) except HTTPError as err: - log.debug("error creating release: %s", err) - log.debug("looking for an existing release to update") + logger.debug("error creating release: %s", err) + logger.debug("looking for an existing release to update") release_id = self.get_release_id_by_tag(tag) if release_id is None: @@ -250,10 +246,10 @@ def create_or_update_release( ) # If this errors we let it die - log.debug("Found existing release %s, updating", release_id) + logger.debug("Found existing release %s, updating", release_id) return self.edit_release_notes(release_id, release_notes) - @logged_function(log) + @logged_function(logger) def asset_upload_url(self, release_id: str) -> str: """ Get the correct upload url for a release @@ -264,7 +260,7 @@ def asset_upload_url(self, release_id: str) -> str: endpoint=f"/repos/{self.owner}/{self.repo_name}/releases/{release_id}/assets", ) - @logged_function(log) + @logged_function(logger) def upload_release_asset( self, release_id: int, @@ -301,7 +297,7 @@ def upload_release_asset( # Raise an error if the request was not successful response.raise_for_status() - log.info( + logger.info( "Successfully uploaded %s to Gitea, url: %s, status code: %s", file, response.url, @@ -310,7 +306,7 @@ def upload_release_asset( return True - @logged_function(log) + @logged_function(logger) def upload_dists(self, tag: str, dist_glob: str) -> int: """ Upload distributions to a release @@ -322,7 +318,7 @@ def upload_dists(self, tag: str, dist_glob: str) -> int: # Find the release corresponding to this tag release_id = self.get_release_id_by_tag(tag=tag) if not release_id: - log.warning("No release corresponds to tag %s, can't upload dists", tag) + logger.warning("No release corresponds to tag %s, can't upload dists", tag) return 0 # Upload assets @@ -334,7 +330,7 @@ def upload_dists(self, tag: str, dist_glob: str) -> int: self.upload_release_asset(release_id, file_path) n_succeeded += 1 except HTTPError: # noqa: PERF203 - log.exception("error uploading asset %s", file_path) + logger.exception("error uploading asset %s", file_path) return n_succeeded diff --git a/src/semantic_release/hvcs/github.py b/src/semantic_release/hvcs/github.py index 016643267..5ccc9004b 100644 --- a/src/semantic_release/hvcs/github.py +++ b/src/semantic_release/hvcs/github.py @@ -3,7 +3,6 @@ from __future__ import annotations import glob -import logging import mimetypes import os from functools import lru_cache @@ -20,6 +19,7 @@ IncompleteReleaseError, UnexpectedResponse, ) +from semantic_release.globals import logger from semantic_release.helpers import logged_function from semantic_release.hvcs.remote_hvcs_base import RemoteHvcsBase from semantic_release.hvcs.token_auth import TokenAuth @@ -29,10 +29,6 @@ from typing import Any, Callable -# Globals -log = logging.getLogger(__name__) - - # Add a mime type for wheels # Fix incorrect entries in the `mimetypes` registry. # On Windows, the Python standard library's `mimetypes` reads in @@ -200,13 +196,13 @@ def _derive_api_url_from_base_domain(self) -> Url: def _get_repository_owner_and_name(self) -> tuple[str, str]: # Github actions context if "GITHUB_REPOSITORY" in os.environ: - log.debug("getting repository owner and name from environment variables") + logger.debug("getting repository owner and name from environment variables") owner, name = os.environ["GITHUB_REPOSITORY"].rsplit("/", 1) return owner, name return super()._get_repository_owner_and_name() - @logged_function(log) + @logged_function(logger) def create_release( self, tag: str, @@ -253,7 +249,7 @@ def create_release( ) return -1 - log.info("Creating release for tag %s", tag) + logger.info("Creating release for tag %s", tag) releases_endpoint = self.create_api_url( endpoint=f"/repos/{self.owner}/{self.repo_name}/releases", ) @@ -273,7 +269,7 @@ def create_release( try: release_id: int = response.json()["id"] - log.info("Successfully created release with ID: %s", release_id) + logger.info("Successfully created release with ID: %s", release_id) except JSONDecodeError as err: raise UnexpectedResponse("Unreadable json response") from err except KeyError as err: @@ -281,7 +277,7 @@ def create_release( errors = [] for asset in assets or []: - log.info("Uploading asset %s", asset) + logger.info("Uploading asset %s", asset) try: self.upload_release_asset(release_id, asset) except HTTPError as err: @@ -295,13 +291,13 @@ def create_release( return release_id for error in errors: - log.exception(error) + logger.exception(error) raise IncompleteReleaseError( f"Failed to upload asset{'s' if len(errors) > 1 else ''} to release!" ) - @logged_function(log) + @logged_function(logger) @suppress_not_found def get_release_id_by_tag(self, tag: str) -> int | None: """ @@ -326,7 +322,7 @@ def get_release_id_by_tag(self, tag: str) -> int | None: except KeyError as err: raise UnexpectedResponse("JSON response is missing an id") from err - @logged_function(log) + @logged_function(logger) def edit_release_notes(self, release_id: int, release_notes: str) -> int: """ Edit a release with updated change notes @@ -335,7 +331,7 @@ def edit_release_notes(self, release_id: int, release_notes: str) -> int: :param release_notes: The release notes for this version :return: The ID of the release that was edited """ - log.info("Updating release %s", release_id) + logger.info("Updating release %s", release_id) release_endpoint = self.create_api_url( endpoint=f"/repos/{self.owner}/{self.repo_name}/releases/{release_id}", ) @@ -350,7 +346,7 @@ def edit_release_notes(self, release_id: int, release_notes: str) -> int: return release_id - @logged_function(log) + @logged_function(logger) def create_or_update_release( self, tag: str, release_notes: str, prerelease: bool = False ) -> int: @@ -361,12 +357,12 @@ def create_or_update_release( :param prerelease: Whether or not this release should be created as a prerelease :return: The status of the request """ - log.info("Creating release for %s", tag) + logger.info("Creating release for %s", tag) try: return self.create_release(tag, release_notes, prerelease) except HTTPError as err: - log.debug("error creating release: %s", err) - log.debug("looking for an existing release to update") + logger.debug("error creating release: %s", err) + logger.debug("looking for an existing release to update") release_id = self.get_release_id_by_tag(tag) if release_id is None: @@ -374,11 +370,11 @@ def create_or_update_release( f"release id for tag {tag} not found, and could not be created" ) - log.debug("Found existing release %s, updating", release_id) + logger.debug("Found existing release %s, updating", release_id) # If this errors we let it die return self.edit_release_notes(release_id, release_notes) - @logged_function(log) + @logged_function(logger) @suppress_not_found def asset_upload_url(self, release_id: str) -> str | None: """ @@ -405,7 +401,7 @@ def asset_upload_url(self, release_id: str) -> str | None: "JSON response is missing a key 'upload_url'" ) from err - @logged_function(log) + @logged_function(logger) def upload_release_asset( self, release_id: int, file: str, label: str | None = None ) -> bool: @@ -442,7 +438,7 @@ def upload_release_asset( # Raise an error if the upload was unsuccessful response.raise_for_status() - log.debug( + logger.debug( "Successfully uploaded %s to Github, url: %s, status code: %s", file, response.url, @@ -451,7 +447,7 @@ def upload_release_asset( return True - @logged_function(log) + @logged_function(logger) def upload_dists(self, tag: str, dist_glob: str) -> int: """ Upload distributions to a release @@ -462,7 +458,7 @@ def upload_dists(self, tag: str, dist_glob: str) -> int: # Find the release corresponding to this version release_id = self.get_release_id_by_tag(tag=tag) if not release_id: - log.warning("No release corresponds to tag %s, can't upload dists", tag) + logger.warning("No release corresponds to tag %s, can't upload dists", tag) return 0 # Upload assets @@ -474,14 +470,14 @@ def upload_dists(self, tag: str, dist_glob: str) -> int: self.upload_release_asset(release_id, file_path) n_succeeded += 1 except HTTPError: # noqa: PERF203 - log.exception("error uploading asset %s", file_path) + logger.exception("error uploading asset %s", file_path) return n_succeeded def remote_url(self, use_token: bool = True) -> str: """Get the remote url including the token for authentication if requested""" if not (self.token and use_token): - log.info("requested to use token for push but no token set, ignoring...") + logger.info("requested to use token for push but no token set, ignoring...") return self._remote_url actor = os.getenv("GITHUB_ACTOR", None) diff --git a/src/semantic_release/hvcs/gitlab.py b/src/semantic_release/hvcs/gitlab.py index 67b8e7512..198d22e00 100644 --- a/src/semantic_release/hvcs/gitlab.py +++ b/src/semantic_release/hvcs/gitlab.py @@ -2,7 +2,6 @@ from __future__ import annotations -import logging import os from functools import lru_cache from pathlib import PurePosixPath @@ -17,6 +16,7 @@ from semantic_release.cli.util import noop_report from semantic_release.errors import UnexpectedResponse +from semantic_release.globals import logger from semantic_release.helpers import logged_function from semantic_release.hvcs.remote_hvcs_base import RemoteHvcsBase from semantic_release.hvcs.util import suppress_not_found @@ -27,13 +27,6 @@ from gitlab.v4.objects import Project as GitLabProject -log = logging.getLogger(__name__) - - -# Globals -log = logging.getLogger(__name__) - - class Gitlab(RemoteHvcsBase): """Gitlab HVCS interface for interacting with Gitlab repositories""" @@ -91,12 +84,12 @@ def _get_repository_owner_and_name(self) -> tuple[str, str]: available, otherwise from parsing the remote url """ if "CI_PROJECT_NAMESPACE" in os.environ and "CI_PROJECT_NAME" in os.environ: - log.debug("getting repository owner and name from environment variables") + logger.debug("getting repository owner and name from environment variables") return os.environ["CI_PROJECT_NAMESPACE"], os.environ["CI_PROJECT_NAME"] return super()._get_repository_owner_and_name() - @logged_function(log) + @logged_function(logger) def create_release( self, tag: str, @@ -112,7 +105,7 @@ def create_release( :param release_notes: The changelog description for this version only :param prerelease: This parameter has no effect in GitLab :param assets: A list of paths to files to upload as assets (TODO: not implemented) - :param noop: If True, do not perform any actions, only log intents + :param noop: If True, do not perform any actions, only logger intents :return: The tag of the release @@ -123,7 +116,7 @@ def create_release( noop_report(f"would have created a release for tag {tag}") return tag - log.info("Creating release for %s", tag) + logger.info("Creating release for %s", tag) # ref: https://docs.gitlab.com/ee/api/releases/index.html#create-a-release self.project.releases.create( { @@ -133,10 +126,10 @@ def create_release( "description": release_notes, } ) - log.info("Successfully created release for %s", tag) + logger.info("Successfully created release for %s", tag) return tag - @logged_function(log) + @logged_function(logger) @suppress_not_found def get_release_by_tag(self, tag: str) -> gitlab.v4.objects.ProjectRelease | None: """ @@ -151,12 +144,12 @@ def get_release_by_tag(self, tag: str) -> gitlab.v4.objects.ProjectRelease | Non try: return self.project.releases.get(tag) except gitlab.exceptions.GitlabGetError: - log.debug("Release %s not found", tag) + logger.debug("Release %s not found", tag) return None except KeyError as err: raise UnexpectedResponse("JSON response is missing commit.id") from err - @logged_function(log) + @logged_function(logger) def edit_release_notes( # type: ignore[override] self, release: gitlab.v4.objects.ProjectRelease, @@ -174,7 +167,7 @@ def edit_release_notes( # type: ignore[override] :raises: GitlabUpdateError: If the server cannot perform the request """ - log.info( + logger.info( "Updating release %s [%s]", release.name, release.attributes.get("commit", {}).get("id"), @@ -183,7 +176,7 @@ def edit_release_notes( # type: ignore[override] release.save() return str(release.get_id()) - @logged_function(log) + @logged_function(logger) def create_or_update_release( self, tag: str, release_notes: str, prerelease: bool = False ) -> str: @@ -205,7 +198,7 @@ def create_or_update_release( tag=tag, release_notes=release_notes, prerelease=prerelease ) except gitlab.GitlabCreateError: - log.info( + logger.info( "New release %s could not be created for project %s", tag, self.project_namespace, @@ -216,7 +209,7 @@ def create_or_update_release( f"release for tag {tag} could not be found, and could not be created" ) - log.debug( + logger.debug( "Found existing release commit %s, updating", release_obj.commit.get("id") ) # If this errors we let it die diff --git a/src/semantic_release/hvcs/remote_hvcs_base.py b/src/semantic_release/hvcs/remote_hvcs_base.py index d26b01881..14e7a5e2f 100644 --- a/src/semantic_release/hvcs/remote_hvcs_base.py +++ b/src/semantic_release/hvcs/remote_hvcs_base.py @@ -2,7 +2,6 @@ from __future__ import annotations -import logging from abc import ABCMeta, abstractmethod from pathlib import PurePosixPath from typing import TYPE_CHECKING @@ -15,10 +14,6 @@ from typing import Any -# Globals -logger = logging.getLogger(__name__) - - class RemoteHvcsBase(HvcsBase, metaclass=ABCMeta): """ Interface for subclasses interacting with a remote VCS diff --git a/src/semantic_release/hvcs/util.py b/src/semantic_release/hvcs/util.py index f54e08b6b..e125cf638 100644 --- a/src/semantic_release/hvcs/util.py +++ b/src/semantic_release/hvcs/util.py @@ -1,6 +1,5 @@ from __future__ import annotations -import logging from functools import wraps from typing import TYPE_CHECKING, Any, Callable, TypeVar @@ -8,11 +7,11 @@ from requests.adapters import HTTPAdapter from requests.packages.urllib3.util.retry import Retry # type: ignore[import] +from semantic_release.globals import logger + if TYPE_CHECKING: # pragma: no cover from semantic_release.hvcs.token_auth import TokenAuth -logger = logging.getLogger(__name__) - def build_requests_session( raise_for_status: bool = True, diff --git a/src/semantic_release/version/algorithm.py b/src/semantic_release/version/algorithm.py index c738e6581..506848cb1 100644 --- a/src/semantic_release/version/algorithm.py +++ b/src/semantic_release/version/algorithm.py @@ -11,6 +11,7 @@ from semantic_release.const import DEFAULT_VERSION from semantic_release.enums import LevelBump, SemanticReleaseLogLevels from semantic_release.errors import InternalError, InvalidVersion +from semantic_release.globals import logger from semantic_release.helpers import validate_types_in_sequence if TYPE_CHECKING: # pragma: no cover @@ -29,9 +30,6 @@ from semantic_release.version.version import Version -logger = logging.getLogger(__name__) - - def tags_and_versions( tags: Iterable[Tag], translator: VersionTranslator ) -> list[tuple[Tag, Version]]: diff --git a/src/semantic_release/version/declaration.py b/src/semantic_release/version/declaration.py index 3c225d1b5..e0400f3df 100644 --- a/src/semantic_release/version/declaration.py +++ b/src/semantic_release/version/declaration.py @@ -1,13 +1,13 @@ from __future__ import annotations -# TODO: Remove v10 +# TODO: Remove v11 from abc import ABC, abstractmethod -from logging import getLogger from pathlib import Path from typing import TYPE_CHECKING from deprecated.sphinx import deprecated +from semantic_release.globals import logger from semantic_release.version.declarations.enum import VersionStampType from semantic_release.version.declarations.i_version_replacer import IVersionReplacer from semantic_release.version.declarations.pattern import PatternVersionDeclaration @@ -25,7 +25,6 @@ "TomlVersionDeclaration", "VersionDeclarationABC", ] -log = getLogger(__name__) @deprecated( @@ -58,7 +57,7 @@ def content(self) -> str: is cached in the instance variable _content """ if self._content is None: - log.debug( + logger.debug( "No content stored, reading from source file %s", self.path.resolve() ) self._content = self.path.read_text() @@ -66,7 +65,7 @@ def content(self) -> str: @content.deleter def content(self) -> None: - log.debug("resetting instance-stored source file contents") + logger.debug("resetting instance-stored source file contents") self._content = None @abstractmethod @@ -102,6 +101,6 @@ def write(self, content: str) -> None: >>> vd = MyVD("path", r"__version__ = (?P\d+\d+\d+)") >>> vd.write(vd.replace(new_version)) """ - log.debug("writing content to %r", self.path.resolve()) + logger.debug("writing content to %r", self.path.resolve()) self.path.write_text(content) self._content = None diff --git a/src/semantic_release/version/declarations/pattern.py b/src/semantic_release/version/declarations/pattern.py index e96234117..f08c208a4 100644 --- a/src/semantic_release/version/declarations/pattern.py +++ b/src/semantic_release/version/declarations/pattern.py @@ -1,6 +1,5 @@ from __future__ import annotations -from logging import getLogger from pathlib import Path from re import ( MULTILINE, @@ -14,6 +13,7 @@ from semantic_release.cli.util import noop_report from semantic_release.const import SEMVER_REGEX +from semantic_release.globals import logger from semantic_release.version.declarations.enum import VersionStampType from semantic_release.version.declarations.i_version_replacer import IVersionReplacer from semantic_release.version.version import Version @@ -22,9 +22,6 @@ from re import Match -log = getLogger(__name__) - - class VersionSwapper: """Callable to replace a version number in a string with a new version number.""" @@ -78,7 +75,7 @@ def __init__( def content(self) -> str: """A cached property that stores the content of the configured source file.""" if self._content is None: - log.debug("No content stored, reading from source file %s", self._path) + logger.debug("No content stored, reading from source file %s", self._path) if not self._path.exists(): raise FileNotFoundError(f"path {self._path!r} does not exist") @@ -109,7 +106,7 @@ def parse(self) -> set[Version]: # pragma: no cover for m in self._search_pattern.finditer(self.content) } - log.debug( + logger.debug( "Parsing current version: path=%r pattern=%r num_matches=%s", self._path.resolve(), self._search_pattern, @@ -136,7 +133,7 @@ def replace(self, new_version: Version) -> str: self.content, ) - log.debug( + logger.debug( "path=%r pattern=%r num_matches=%r", self._path, self._search_pattern, diff --git a/src/semantic_release/version/declarations/toml.py b/src/semantic_release/version/declarations/toml.py index ed9542870..59e6996ac 100644 --- a/src/semantic_release/version/declarations/toml.py +++ b/src/semantic_release/version/declarations/toml.py @@ -1,6 +1,5 @@ from __future__ import annotations -from logging import getLogger from pathlib import Path from typing import Any, Dict, cast @@ -9,13 +8,11 @@ from dotty_dict import Dotty from semantic_release.cli.util import noop_report +from semantic_release.globals import logger from semantic_release.version.declarations.enum import VersionStampType from semantic_release.version.declarations.i_version_replacer import IVersionReplacer from semantic_release.version.version import Version -# globals -log = getLogger(__name__) - class TomlVersionDeclaration(IVersionReplacer): def __init__( @@ -30,7 +27,7 @@ def __init__( def content(self) -> str: """A cached property that stores the content of the configured source file.""" if self._content is None: - log.debug("No content stored, reading from source file %s", self._path) + logger.debug("No content stored, reading from source file %s", self._path) if not self._path.exists(): raise FileNotFoundError(f"path {self._path!r} does not exist") @@ -52,7 +49,7 @@ def parse(self) -> set[Version]: # pragma: no cover content = self._load() maybe_version: str = content.get(self._search_text) # type: ignore[return-value] if maybe_version is not None: - log.debug( + logger.debug( "Found a key %r that looks like a version (%r)", self._search_text, maybe_version, @@ -69,7 +66,7 @@ def replace(self, new_version: Version) -> str: """ content = self._load() if self._search_text in content: - log.info( + logger.info( "found %r in source file contents, replacing with %s", self._search_text, new_version, diff --git a/src/semantic_release/version/translator.py b/src/semantic_release/version/translator.py index 7a4ce275f..6340701da 100644 --- a/src/semantic_release/version/translator.py +++ b/src/semantic_release/version/translator.py @@ -1,14 +1,12 @@ from __future__ import annotations -import logging import re from semantic_release.const import SEMVER_REGEX +from semantic_release.globals import logger from semantic_release.helpers import check_tag_format from semantic_release.version.version import Version -log = logging.getLogger(__name__) - class VersionTranslator: """ @@ -37,7 +35,7 @@ def _invert_tag_format_to_re(cls, tag_format: str) -> re.Pattern[str]: tag_format.replace(r"{version}", r"(?P.*)"), flags=re.VERBOSE, ) - log.debug("inverted tag_format %r to %r", tag_format, pat.pattern) + logger.debug("inverted tag_format %r to %r", tag_format, pat.pattern) return pat def __init__( diff --git a/src/semantic_release/version/version.py b/src/semantic_release/version/version.py index 41ec5e107..032596e4a 100644 --- a/src/semantic_release/version/version.py +++ b/src/semantic_release/version/version.py @@ -1,6 +1,5 @@ from __future__ import annotations -import logging import re from functools import wraps from itertools import zip_longest @@ -9,11 +8,9 @@ from semantic_release.const import SEMVER_REGEX from semantic_release.enums import LevelBump from semantic_release.errors import InvalidVersion +from semantic_release.globals import logger from semantic_release.helpers import check_tag_format -log = logging.getLogger(__name__) - - # Very heavily inspired by semver.version:_comparator, I don't think there's # a cleaner way to do this # https://github.com/python-semver/python-semver/blob/b5317af9a7e99e6a86df98320e73be72d5adf0de/src/semver/version.py#L32 @@ -116,7 +113,7 @@ def parse( if not isinstance(version_str, str): raise InvalidVersion(f"{version_str!r} cannot be parsed as a Version") - log.debug("attempting to parse string %r as Version", version_str) + logger.debug("attempting to parse string %r as Version", version_str) match = cls._VERSION_REGEX.fullmatch(version_str) if not match: raise InvalidVersion(f"{version_str!r} is not a valid Version") @@ -131,7 +128,7 @@ def parse( r"'1.2.3-my-custom-3rc.4'." ) prerelease_token, prerelease_revision = pm.groups() - log.debug( + logger.debug( "parsed prerelease_token %s, prerelease_revision %s from version " "string %s", prerelease_token, @@ -140,10 +137,10 @@ def parse( ) else: prerelease_revision = None - log.debug("version string %s parsed as a non-prerelease", version_str) + logger.debug("version string %s parsed as a non-prerelease", version_str) build_metadata = match.group("buildmetadata") or "" - log.debug( + logger.debug( "parsed build metadata %r from version string %s", build_metadata, version_str, @@ -218,7 +215,7 @@ def bump(self, level: LevelBump) -> Version: if type(level) != LevelBump: raise TypeError(f"Unexpected level {level!r}: expected {LevelBump!r}") - log.debug("performing a %s level bump", level) + logger.debug("performing a %s level bump", level) if level is LevelBump.MAJOR: return Version( self.major + 1, From d53bfac1ed4bb610387d80b99f68b540f7e4cb99 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 19 May 2025 02:12:40 -0600 Subject: [PATCH 130/160] test(cmd-version): update test cases for updated emoji version values --- tests/const.py | 2 +- tests/e2e/cmd_version/test_version.py | 4 +++- tests/e2e/cmd_version/test_version_print.py | 10 +++++----- tests/e2e/cmd_version/test_version_strict.py | 4 +++- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/const.py b/tests/const.py index 88f5677eb..41df4533d 100644 --- a/tests/const.py +++ b/tests/const.py @@ -92,7 +92,7 @@ class RepoActionStep(str, Enum): *EMOJI_COMMITS_PATCH, ":sparkles::pencil: docs for something special\n", # Emoji in description should not be used to evaluate change type - ":sparkles: last minute rush order\n\n:boom: Good thing we're 10x developers\n", + ":sparkles: last minute rush order\n\nGood thing we're 10x developers :boom:\n", ) EMOJI_COMMITS_MAJOR = ( *EMOJI_COMMITS_MINOR, diff --git a/tests/e2e/cmd_version/test_version.py b/tests/e2e/cmd_version/test_version.py index f892c3cf6..39ce8eb7e 100644 --- a/tests/e2e/cmd_version/test_version.py +++ b/tests/e2e/cmd_version/test_version.py @@ -7,6 +7,8 @@ import pytest from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture +from semantic_release.hvcs.github import Github + from tests.const import ( MAIN_PROG_NAME, VERSION_SUBCMD, @@ -217,7 +219,7 @@ def test_version_on_last_release( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD] - result = run_cli(cli_cmd[1:]) + result = run_cli(cli_cmd[1:], env={Github.DEFAULT_ENV_TOKEN_NAME: "1234"}) # take measurement after running the version command repo_status_after = repo.git.status(short=True) diff --git a/tests/e2e/cmd_version/test_version_print.py b/tests/e2e/cmd_version/test_version_print.py index b3afc2fc3..71c41ccca 100644 --- a/tests/e2e/cmd_version/test_version_print.py +++ b/tests/e2e/cmd_version/test_version_print.py @@ -5,6 +5,8 @@ import pytest from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture +from semantic_release.hvcs.github import Github + from tests.const import ( MAIN_PROG_NAME, VERSION_SUBCMD, @@ -126,7 +128,7 @@ def test_version_print_next_version( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print", *force_args] - result = run_cli(cli_cmd[1:]) + result = run_cli(cli_cmd[1:], env={Github.DEFAULT_ENV_TOKEN_NAME: "1234"}) # take measurement after running the version command repo_status_after = repo.git.status(short=True) @@ -136,7 +138,6 @@ def test_version_print_next_version( # Evaluate assert_successful_exit_code(result, cli_cmd) - assert not result.stderr assert f"{next_release_version}\n" == result.stdout # assert nothing else happened (no code changes, no commit, no tag, no push, no vcs release) @@ -296,7 +297,7 @@ def test_version_print_tag_prints_next_tag( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print-tag", *force_args] - result = run_cli(cli_cmd[1:]) + result = run_cli(cli_cmd[1:], env={Github.DEFAULT_ENV_TOKEN_NAME: "1234"}) # take measurement after running the version command repo_status_after = repo.git.status(short=True) @@ -306,7 +307,6 @@ def test_version_print_tag_prints_next_tag( # Evaluate assert_successful_exit_code(result, cli_cmd) - assert not result.stderr assert f"{next_release_tag}\n" == result.stdout # assert nothing else happened (no code changes, no commit, no tag, no push, no vcs release) @@ -340,7 +340,7 @@ def test_version_print_last_released_prints_version( # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print-last-released"] - result = run_cli(cli_cmd[1:]) + result = run_cli(cli_cmd[1:], env={Github.DEFAULT_ENV_TOKEN_NAME: "1234"}) # take measurement after running the version command repo_status_after = repo.git.status(short=True) diff --git a/tests/e2e/cmd_version/test_version_strict.py b/tests/e2e/cmd_version/test_version_strict.py index a0ff9bb8d..951a9966f 100644 --- a/tests/e2e/cmd_version/test_version_strict.py +++ b/tests/e2e/cmd_version/test_version_strict.py @@ -5,6 +5,8 @@ import pytest from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture +from semantic_release.hvcs.github import Github + from tests.const import MAIN_PROG_NAME, VERSION_SUBCMD from tests.fixtures.repos import repo_w_trunk_only_conventional_commits from tests.util import assert_exit_code @@ -48,7 +50,7 @@ def test_version_already_released_when_strict( # Act cli_cmd = [MAIN_PROG_NAME, "--strict", VERSION_SUBCMD] - result = run_cli(cli_cmd[1:]) + result = run_cli(cli_cmd[1:], env={Github.DEFAULT_ENV_TOKEN_NAME: "1234"}) # take measurement after running the version command repo_status_after = repo.git.status(short=True) From 6a866567c13dfbbdd0d7be5fde8927574c5a3e5f Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 19 May 2025 03:03:16 -0600 Subject: [PATCH 131/160] style: fix spelling in comments --- .../e2e/cmd_version/test_version_changelog_custom_commit_msg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/cmd_version/test_version_changelog_custom_commit_msg.py b/tests/e2e/cmd_version/test_version_changelog_custom_commit_msg.py index 180e7c0f4..acff3e728 100644 --- a/tests/e2e/cmd_version/test_version_changelog_custom_commit_msg.py +++ b/tests/e2e/cmd_version/test_version_changelog_custom_commit_msg.py @@ -99,7 +99,7 @@ class Commit2SectionCommit(TypedDict): ) for commit_msg in [ dedent( - # Conventional compliant prefix with skip-ci idicator + # Conventional compliant prefix with skip-ci indicator """\ chore(release): v{version} [skip ci] From c08b835287d74d3995edde790f31afad59e44a43 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 19 May 2025 03:05:11 -0600 Subject: [PATCH 132/160] refactor(config): update custom `commit_message` regex converter to match trimmed commits --- src/semantic_release/cli/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/semantic_release/cli/config.py b/src/semantic_release/cli/config.py index 0ed1eba78..1badfe37b 100644 --- a/src/semantic_release/cli/config.py +++ b/src/semantic_release/cli/config.py @@ -718,7 +718,7 @@ def from_raw_config( # noqa: C901 # TODO: add any other placeholders here ), # We use re.escape to ensure that the commit message is treated as a literal - regex_escape(raw.commit_message), + regex_escape(raw.commit_message.strip()), ) ) changelog_excluded_commit_patterns = ( From e69e46c9f9ef24b683752b4d75ddae50356b95db Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 19 May 2025 03:27:52 -0600 Subject: [PATCH 133/160] refactor(cmd-version): add `USERNAME` as passed variable to build env on Windows --- src/semantic_release/cli/commands/version.py | 1 + tests/e2e/cmd_version/test_version_build.py | 60 +------------------- 2 files changed, 4 insertions(+), 57 deletions(-) diff --git a/src/semantic_release/cli/commands/version.py b/src/semantic_release/cli/commands/version.py index 66b42c4cb..8a8cf94a1 100644 --- a/src/semantic_release/cli/commands/version.py +++ b/src/semantic_release/cli/commands/version.py @@ -228,6 +228,7 @@ def get_windows_env() -> Mapping[str, str | None]: "SYSTEMROOT", "TEMP", "TMP", + "USERNAME", # must include for python getpass.getuser() on windows "USERPROFILE", "USERSID", "WINDIR", diff --git a/tests/e2e/cmd_version/test_version_build.py b/tests/e2e/cmd_version/test_version_build.py index 1145ce627..390882f9e 100644 --- a/tests/e2e/cmd_version/test_version_build.py +++ b/tests/e2e/cmd_version/test_version_build.py @@ -151,6 +151,7 @@ def test_version_runs_build_command_windows( update_pyproject_toml: UpdatePyprojectTomlFn, mocked_git_push: mock.MagicMock, post_mocker: mock.Mock, + clean_os_environment: dict[str, str], ): if shell == "cmd": build_result_file = get_wheel_file("%NEW_VERSION%") @@ -174,9 +175,8 @@ def test_version_runs_build_command_windows( ) build_command = pyproject_config.get("tool.semantic_release.build_command", "") patched_os_environment = { + **clean_os_environment, "CI": "true", - "PATH": os.getenv("PATH", ""), - "HOME": "/home/username", "VIRTUAL_ENV": "./.venv", # Simulate that all CI's are set "GITHUB_ACTIONS": "true", @@ -184,29 +184,6 @@ def test_version_runs_build_command_windows( "GITEA_ACTIONS": "true", "BITBUCKET_REPO_FULL_NAME": "python-semantic-release/python-semantic-release.git", "PSR_DOCKER_GITHUB_ACTION": "true", - # Windows - "ALLUSERSAPPDATA": "C:\\ProgramData", - "ALLUSERSPROFILE": "C:\\ProgramData", - "APPDATA": "C:\\Users\\Username\\AppData\\Roaming", - "COMMONPROGRAMFILES": "C:\\Program Files\\Common Files", - "COMMONPROGRAMFILES(X86)": "C:\\Program Files (x86)\\Common Files", - "DEFAULTUSERPROFILE": "C:\\Users\\Default", - "HOMEPATH": "\\Users\\Username", - "PATHEXT": ".COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC", - "PROFILESFOLDER": "C:\\Users", - "PROGRAMFILES": "C:\\Program Files", - "PROGRAMFILES(X86)": "C:\\Program Files (x86)", - "SYSTEM": "C:\\Windows\\System32", - "SYSTEM16": "C:\\Windows\\System16", - "SYSTEM32": "C:\\Windows\\System32", - "SYSTEMDRIVE": "C:", - "SYSTEMROOT": "C:\\Windows", - "TEMP": "C:\\Users\\Username\\AppData\\Local\\Temp", - "TMP": "C:\\Users\\Username\\AppData\\Local\\Temp", - "USERPROFILE": "C:\\Users\\Username", - "USERSID": "S-1-5-21-1234567890-123456789-123456789-1234", - "USERNAME": "Username", # must include for python getpass.getuser() on windows - "WINDIR": "C:\\Windows", } # Wrap subprocess.run to capture the arguments to the call @@ -226,42 +203,17 @@ def test_version_runs_build_command_windows( [shell, "/c" if shell == "cmd" else "-Command", build_command], check=True, env={ + **clean_os_environment, "NEW_VERSION": next_release_version, # injected into environment "CI": patched_os_environment["CI"], "BITBUCKET_CI": "true", # Converted "GITHUB_ACTIONS": patched_os_environment["GITHUB_ACTIONS"], "GITEA_ACTIONS": patched_os_environment["GITEA_ACTIONS"], "GITLAB_CI": patched_os_environment["GITLAB_CI"], - "HOME": patched_os_environment["HOME"], - "PATH": patched_os_environment["PATH"], "VIRTUAL_ENV": patched_os_environment["VIRTUAL_ENV"], "PSR_DOCKER_GITHUB_ACTION": patched_os_environment[ "PSR_DOCKER_GITHUB_ACTION" ], - # Windows - "ALLUSERSAPPDATA": patched_os_environment["ALLUSERSAPPDATA"], - "ALLUSERSPROFILE": patched_os_environment["ALLUSERSPROFILE"], - "APPDATA": patched_os_environment["APPDATA"], - "COMMONPROGRAMFILES": patched_os_environment["COMMONPROGRAMFILES"], - "COMMONPROGRAMFILES(X86)": patched_os_environment[ - "COMMONPROGRAMFILES(X86)" - ], - "DEFAULTUSERPROFILE": patched_os_environment["DEFAULTUSERPROFILE"], - "HOMEPATH": patched_os_environment["HOMEPATH"], - "PATHEXT": patched_os_environment["PATHEXT"], - "PROFILESFOLDER": patched_os_environment["PROFILESFOLDER"], - "PROGRAMFILES": patched_os_environment["PROGRAMFILES"], - "PROGRAMFILES(X86)": patched_os_environment["PROGRAMFILES(X86)"], - "SYSTEM": patched_os_environment["SYSTEM"], - "SYSTEM16": patched_os_environment["SYSTEM16"], - "SYSTEM32": patched_os_environment["SYSTEM32"], - "SYSTEMDRIVE": patched_os_environment["SYSTEMDRIVE"], - "SYSTEMROOT": patched_os_environment["SYSTEMROOT"], - "TEMP": patched_os_environment["TEMP"], - "TMP": patched_os_environment["TMP"], - "USERPROFILE": patched_os_environment["USERPROFILE"], - "USERSID": patched_os_environment["USERSID"], - "WINDIR": patched_os_environment["WINDIR"], }, ) @@ -294,11 +246,7 @@ def test_version_runs_build_command_w_user_env( patched_os_environment = { **clean_os_environment, "CI": "true", - "PATH": os.getenv("PATH", ""), - "HOME": "/home/username", "VIRTUAL_ENV": "./.venv", - # Windows - "USERNAME": "Username", # must include for python getpass.getuser() on windows # Simulate that all CI's are set "GITHUB_ACTIONS": "true", "GITLAB_CI": "true", @@ -366,8 +314,6 @@ def test_version_runs_build_command_w_user_env( "GITHUB_ACTIONS": patched_os_environment["GITHUB_ACTIONS"], "GITEA_ACTIONS": patched_os_environment["GITEA_ACTIONS"], "GITLAB_CI": patched_os_environment["GITLAB_CI"], - "HOME": patched_os_environment["HOME"], - "PATH": patched_os_environment["PATH"], "VIRTUAL_ENV": patched_os_environment["VIRTUAL_ENV"], "PSR_DOCKER_GITHUB_ACTION": patched_os_environment[ "PSR_DOCKER_GITHUB_ACTION" From c6b6eabbfe100d2c741620eb3fa12a382531fa94 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 19 May 2025 20:44:26 -0600 Subject: [PATCH 134/160] refactor(config)!: change `allow_zero_version` default to `false` Changes the default behavior of PSR when the `allow_zero_version` setting is not provided. BREAKING CHANGE: This release switches the `allow_zero_version` default to `false`. This change is to encourage less `0.x` releases as the default but rather allow the experienced developer to choose when `0.x` is appropriate. There are way too many projects in the ecosystems that never leave `0.x` and that is problematic for the industry tools that help auto-update based on SemVer. We should strive for publishing usable tools and maintaining good forethought for when compatibility must break. If your configuration already sets the `allow_zero_version` value, this change will have no effect on your project. If you want to use `0.x` versions, from the start then change `allow_zero_version` to `true` in your configuration. --- src/semantic_release/cli/config.py | 2 +- src/semantic_release/version/algorithm.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/semantic_release/cli/config.py b/src/semantic_release/cli/config.py index 1badfe37b..4253e09a3 100644 --- a/src/semantic_release/cli/config.py +++ b/src/semantic_release/cli/config.py @@ -360,7 +360,7 @@ class RawConfig(BaseModel): commit_parser_options: Dict[str, Any] = {} logging_use_named_masks: bool = False major_on_zero: bool = True - allow_zero_version: bool = True + allow_zero_version: bool = False repo_dir: Annotated[Path, Field(validate_default=True)] = Path(".") remote: RemoteConfig = RemoteConfig() no_git_verify: bool = False diff --git a/src/semantic_release/version/algorithm.py b/src/semantic_release/version/algorithm.py index 506848cb1..fa24e3fa1 100644 --- a/src/semantic_release/version/algorithm.py +++ b/src/semantic_release/version/algorithm.py @@ -245,9 +245,9 @@ def next_version( repo: Repo, translator: VersionTranslator, commit_parser: CommitParser[ParseResult, ParserOptions], + allow_zero_version: bool, + major_on_zero: bool, prerelease: bool = False, - major_on_zero: bool = True, - allow_zero_version: bool = True, ) -> Version: """ Evaluate the history within `repo`, and based on the tags and commits in the repo From 203d29d9d6b8e862eabe2f99dbd27eabf04e75e2 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 19 May 2025 20:48:43 -0600 Subject: [PATCH 135/160] docs(configuration): change default value for `allow_zero_version` in the description --- docs/configuration.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index aa904dc9d..979d6e2ab 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -142,7 +142,9 @@ version to be ``1.0.0``, regardless of patch, minor, or major change level. Additionally, when ``allow_zero_version`` is set to ``false``, the :ref:`config-major_on_zero` setting is ignored. -**Default:** ``true`` +*Default changed to ``false`` in $NEW_VERSION* + +**Default:** ``false`` ---- From 7f6899ea8c1eb34868fb0da385c99c857e508213 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 20 May 2025 19:23:09 -0600 Subject: [PATCH 136/160] test(cmd-version): update tests to handle new default `allow_zero_version=False` --- tests/e2e/cmd_version/test_version.py | 4 +- tests/e2e/cmd_version/test_version_bump.py | 5 +- .../e2e/cmd_version/test_version_changelog.py | 2 +- tests/e2e/cmd_version/test_version_print.py | 119 +++++++++++++++++- .../cmd_version/test_version_release_notes.py | 4 +- tests/e2e/cmd_version/test_version_stamp.py | 14 +-- .../repos/trunk_based_dev/repo_w_no_tags.py | 42 +++++++ 7 files changed, 175 insertions(+), 15 deletions(-) diff --git a/tests/e2e/cmd_version/test_version.py b/tests/e2e/cmd_version/test_version.py index 39ce8eb7e..5cb9700cf 100644 --- a/tests/e2e/cmd_version/test_version.py +++ b/tests/e2e/cmd_version/test_version.py @@ -35,7 +35,7 @@ "repo_result, next_release_version", # must use a repo that is ready for a release to prevent no release # logic from being triggered before the noop logic - [(lazy_fixture(repo_w_no_tags_conventional_commits.__name__), "0.1.0")], + [(lazy_fixture(repo_w_no_tags_conventional_commits.__name__), "1.0.0")], ) def test_version_noop_is_noop( repo_result: BuiltRepoResult, @@ -273,7 +273,7 @@ def test_version_only_tag_push( # Assert only tag was created, it was pushed and then release was created assert_successful_exit_code(result, cli_cmd) - assert tag_after == "v0.1.0" + assert tag_after == "v1.0.0" assert head_before == head_after assert mocked_git_push.call_count == 1 # 0 for commit, 1 for tag assert post_mocker.call_count == 1 diff --git a/tests/e2e/cmd_version/test_version_bump.py b/tests/e2e/cmd_version/test_version_bump.py index 1faa10cb2..c08efb9ee 100644 --- a/tests/e2e/cmd_version/test_version_bump.py +++ b/tests/e2e/cmd_version/test_version_bump.py @@ -34,6 +34,7 @@ repo_w_github_flow_w_feature_release_channel_conventional_commits, repo_w_initial_commit, repo_w_no_tags_conventional_commits, + repo_w_no_tags_conventional_commits_w_zero_version, repo_w_no_tags_emoji_commits, repo_w_no_tags_scipy_commits, repo_w_trunk_only_conventional_commits, @@ -69,7 +70,9 @@ [ *( ( - lazy_fixture(repo_w_no_tags_conventional_commits.__name__), + lazy_fixture( + repo_w_no_tags_conventional_commits_w_zero_version.__name__ + ), cli_args, next_release_version, ) diff --git a/tests/e2e/cmd_version/test_version_changelog.py b/tests/e2e/cmd_version/test_version_changelog.py index 212e6110e..ae60e6638 100644 --- a/tests/e2e/cmd_version/test_version_changelog.py +++ b/tests/e2e/cmd_version/test_version_changelog.py @@ -340,7 +340,7 @@ def test_version_updates_changelog_wo_prev_releases( str(changelog_file.name), ) - version = "v0.1.0" + version = "v1.0.0" rst_version_header = f"{version} ({repo_build_date_str})" search_n_replacements = { ChangelogOutputFormat.MARKDOWN: ( diff --git a/tests/e2e/cmd_version/test_version_print.py b/tests/e2e/cmd_version/test_version_print.py index 71c41ccca..4efd1e02f 100644 --- a/tests/e2e/cmd_version/test_version_print.py +++ b/tests/e2e/cmd_version/test_version_print.py @@ -21,6 +21,7 @@ ) from tests.fixtures.repos.trunk_based_dev.repo_w_no_tags import ( repo_w_no_tags_conventional_commits_using_tag_format, + repo_w_no_tags_conventional_commits_w_zero_version, ) from tests.util import ( add_text_to_file, @@ -212,8 +213,7 @@ def test_version_print_next_version( marks=pytest.mark.comprehensive, ) for repo_fixture_name in ( - repo_w_no_tags_conventional_commits.__name__, - repo_w_no_tags_conventional_commits_using_tag_format.__name__, + repo_w_no_tags_conventional_commits_w_zero_version.__name__, ) for cli_args, next_release_version in ( # Dynamic version bump determination (based on commits) @@ -317,6 +317,121 @@ def test_version_print_tag_prints_next_tag( assert post_mocker.call_count == 0 +@pytest.mark.parametrize( + "repo_result, commits, force_args, next_release_version", + [ + pytest.param( + lazy_fixture(repo_fixture_name), + [], + cli_args, + next_release_version, + marks=pytest.mark.comprehensive, + ) + for repo_fixture_name in ( + repo_w_no_tags_conventional_commits.__name__, + repo_w_no_tags_conventional_commits_using_tag_format.__name__, + ) + for cli_args, next_release_version in ( + # Dynamic version bump determination (based on commits) + ([], "1.0.0"), + # Dynamic version bump determination (based on commits) with build metadata + (["--build-metadata", "build.12345"], "1.0.0+build.12345"), + # Forced version bump + (["--prerelease"], "0.0.0-rc.1"), + (["--patch"], "0.0.1"), + (["--minor"], "0.1.0"), + (["--major"], "1.0.0"), + # Forced version bump with --build-metadata + (["--patch", "--build-metadata", "build.12345"], "0.0.1+build.12345"), + # Forced version bump with --as-prerelease + (["--prerelease", "--as-prerelease"], "0.0.0-rc.1"), + (["--patch", "--as-prerelease"], "0.0.1-rc.1"), + (["--minor", "--as-prerelease"], "0.1.0-rc.1"), + (["--major", "--as-prerelease"], "1.0.0-rc.1"), + # Forced version bump with --as-prerelease and modified --prerelease-token + ( + ["--patch", "--as-prerelease", "--prerelease-token", "beta"], + "0.0.1-beta.1", + ), + # Forced version bump with --as-prerelease and modified --prerelease-token + # and --build-metadata + ( + [ + "--patch", + "--as-prerelease", + "--prerelease-token", + "beta", + "--build-metadata", + "build.12345", + ], + "0.0.1-beta.1+build.12345", + ), + ) + ], +) +def test_version_print_tag_prints_next_tag_no_zero_versions( + repo_result: BuiltRepoResult, + commits: list[str], + force_args: list[str], + next_release_version: str, + get_cfg_value_from_def: GetCfgValueFromDefFn, + file_in_repo: str, + run_cli: RunCliFn, + mocked_git_push: MagicMock, + post_mocker: Mocker, +): + """ + Given a generic repository at the latest release version and a subsequent commit, + When running the version command with the --print-tag flag, + Then the expected next release tag should be printed and exit without + making any changes to the repository. + + Note: The point of this test is to only verify that the `--print-tag` flag does not + make any changes to the repository--not to validate if the next version is calculated + correctly per the repository structure (see test_version_release & + test_version_force_level for correctness). + + However, we do validate that --print-tag & a force option and/or --as-prerelease options + work together to print the next release tag correctly but not make a change to the repo. + """ + repo = repo_result["repo"] + repo_def = repo_result["definition"] + tag_format_str: str = get_cfg_value_from_def(repo_def, "tag_format_str") # type: ignore[assignment] + next_release_tag = tag_format_str.format(version=next_release_version) + + if len(commits) > 1: + # Make a commit to ensure we have something to release + # otherwise the "no release will be made" logic will kick in first + add_text_to_file(repo, file_in_repo) + repo.git.commit(m=commits[-1], a=True) + + # Setup: take measurement before running the version command + repo_status_before = repo.git.status(short=True) + head_before = repo.head.commit.hexsha + tags_before = {tag.name for tag in repo.tags} + + # Act + cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print-tag", *force_args] + result = run_cli(cli_cmd[1:], env={Github.DEFAULT_ENV_TOKEN_NAME: "1234"}) + + # take measurement after running the version command + repo_status_after = repo.git.status(short=True) + head_after = repo.head.commit.hexsha + tags_after = {tag.name for tag in repo.tags} + tags_set_difference = set.difference(tags_after, tags_before) + + # Evaluate + assert_successful_exit_code(result, cli_cmd) + assert f"{next_release_tag}\n" == result.stdout + + # assert nothing else happened (no code changes, no commit, no tag, no push, no vcs release) + assert repo_status_before == repo_status_after + assert head_before == head_after + assert not tags_set_difference + assert mocked_git_push.call_count == 0 + assert post_mocker.call_count == 0 + + @pytest.mark.parametrize( "repo_result", [lazy_fixture(repo_w_trunk_only_conventional_commits.__name__)], diff --git a/tests/e2e/cmd_version/test_version_release_notes.py b/tests/e2e/cmd_version/test_version_release_notes.py index 6786e1d3e..2b434f29c 100644 --- a/tests/e2e/cmd_version/test_version_release_notes.py +++ b/tests/e2e/cmd_version/test_version_release_notes.py @@ -46,7 +46,7 @@ @pytest.mark.parametrize( "repo_result, next_release_version", [ - (lazy_fixture(repo_w_no_tags_conventional_commits.__name__), "0.1.0"), + (lazy_fixture(repo_w_no_tags_conventional_commits.__name__), "1.0.0"), ], ) def test_custom_release_notes_template( @@ -131,7 +131,7 @@ def test_default_release_notes_license_statement( get_hvcs_client_from_repo_def: GetHvcsClientFromRepoDefFn, generate_default_release_notes_from_def: GenerateDefaultReleaseNotesFromDefFn, ): - new_version = "0.1.0" + new_version = "1.0.0" # Setup now_datetime = stable_now_date() diff --git a/tests/e2e/cmd_version/test_version_stamp.py b/tests/e2e/cmd_version/test_version_stamp.py index bad09d23b..a12059f37 100644 --- a/tests/e2e/cmd_version/test_version_stamp.py +++ b/tests/e2e/cmd_version/test_version_stamp.py @@ -147,7 +147,7 @@ def test_stamp_version_variables_python( update_pyproject_toml: UpdatePyprojectTomlFn, example_project_dir: ExProjectDir, ) -> None: - new_version = "0.1.0" + new_version = "1.0.0" target_file = example_project_dir.joinpath( "src", EXAMPLE_PROJECT_NAME, "_version.py" ) @@ -181,7 +181,7 @@ def test_stamp_version_toml( default_tag_format_str: str, ) -> None: orig_version = "0.0.0" - new_version = "0.1.0" + new_version = "1.0.0" orig_release = default_tag_format_str.format(version=orig_version) new_release = default_tag_format_str.format(version=new_version) target_file = Path("example.toml") @@ -236,7 +236,7 @@ def test_stamp_version_variables_yaml( update_pyproject_toml: UpdatePyprojectTomlFn, ) -> None: orig_version = "0.0.0" - new_version = "0.1.0" + new_version = "1.0.0" target_file = Path("example.yml") orig_yaml = dedent( f"""\ @@ -286,7 +286,7 @@ def test_stamp_version_variables_yaml_cff( Based on https://github.com/python-semantic-release/python-semantic-release/issues/962 """ orig_version = "0.0.0" - new_version = "0.1.0" + new_version = "1.0.0" target_file = Path("CITATION.cff") orig_yaml = dedent( f"""\ @@ -335,7 +335,7 @@ def test_stamp_version_variables_json( update_pyproject_toml: UpdatePyprojectTomlFn, ) -> None: orig_version = "0.0.0" - new_version = "0.1.0" + new_version = "1.0.0" target_file = Path("plugins.json") orig_json = { "id": "test-plugin", @@ -385,7 +385,7 @@ def test_stamp_version_variables_yaml_github_actions( Based on https://github.com/python-semantic-release/python-semantic-release/issues/1156 """ orig_version = "0.0.0" - new_version = "0.1.0" + new_version = "1.0.0" target_file = Path("combined.yml") action1_yaml_filepath = "my-org/my-actions/.github/workflows/action1.yml" action2_yaml_filepath = "my-org/my-actions/.github/workflows/action2.yml" @@ -457,7 +457,7 @@ def test_stamp_version_variables_yaml_kustomization_container_spec( Based on https://github.com/python-semantic-release/python-semantic-release/issues/846 """ orig_version = "0.0.0" - new_version = "0.1.0" + new_version = "1.0.0" target_file = Path("kustomization.yaml") orig_yaml = dedent( f"""\ diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py index cdb5c2afc..b8134d0d3 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py @@ -259,6 +259,48 @@ def _build_repo(cached_repo_path: Path) -> Sequence[RepoActions]: } +@pytest.fixture +def repo_w_no_tags_conventional_commits_w_zero_version( + build_repo_from_definition: BuildRepoFromDefinitionFn, + get_repo_definition_4_trunk_only_repo_w_no_tags: GetRepoDefinitionFn, + get_cached_repo_data: GetCachedRepoDataFn, + build_repo_or_copy_cache: BuildRepoOrCopyCacheFn, + build_spec_hash_4_repo_w_no_tags: str, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: ExProjectDir, + change_to_ex_proj_dir: None, +) -> BuiltRepoResult: + """Replicates repo with no tags, but with allow_zero_version=True""" + repo_name = repo_w_no_tags_conventional_commits_w_zero_version.__name__ + commit_type: CommitConvention = ( + repo_name.split("_commits", maxsplit=1)[0].split("_")[-1] # type: ignore[assignment] + ) + + def _build_repo(cached_repo_path: Path) -> Sequence[RepoActions]: + repo_construction_steps = get_repo_definition_4_trunk_only_repo_w_no_tags( + commit_type=commit_type, + extra_configs={ + "tool.semantic_release.allow_zero_version": True, + }, + ) + return build_repo_from_definition(cached_repo_path, repo_construction_steps) + + build_repo_or_copy_cache( + repo_name=repo_name, + build_spec_hash=build_spec_hash_4_repo_w_no_tags, + build_repo_func=_build_repo, + dest_dir=example_project_dir, + ) + + if not (cached_repo_data := get_cached_repo_data(proj_dirname=repo_name)): + raise ValueError("Failed to retrieve repo data from cache") + + return { + "definition": cached_repo_data["build_definition"], + "repo": example_project_git_repo(), + } + + @pytest.fixture def repo_w_no_tags_conventional_commits( build_trunk_only_repo_w_no_tags: BuildSpecificRepoFn, From 0e114c3458a24b87bfd2d6b0cd3f5cfdc9497084 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 19 May 2025 21:14:32 -0600 Subject: [PATCH 137/160] refactor(config)!: change `changelog.default_templates.mask_initial_release` default to `true` Changes the default behavior of PSR when `mask_initial_release` setting is not provided. BREAKING CHANGE: This release switches the `changelog.default_templates.mask_initial_release` default to `true`. This change is intended to toggle better recommended outputs of the default changelog. Conceptually, the very first release is hard to describe--one can only provide new features as nothing exists yet for the end user. No changelog should be written as there is no start point to compare the "changes" to. The recommendation instead is to only list a simple message as `Initial Release`. This is now the default for PSR when providing the very first release (no pre-existing tags) in the changelog and release notes. If your configuration already sets the `changelog.default_templates.mask_initial_release` value, then this change will have no effect on your project. If you do NOT want to mask the first release information, then set `changelog.default_templates.mask_initial_release` to `false` in your configuration. --- src/semantic_release/cli/config.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/semantic_release/cli/config.py b/src/semantic_release/cli/config.py index 4253e09a3..ce40ca0b9 100644 --- a/src/semantic_release/cli/config.py +++ b/src/semantic_release/cli/config.py @@ -128,8 +128,7 @@ class ChangelogEnvironmentConfig(BaseModel): class DefaultChangelogTemplatesConfig(BaseModel): changelog_file: str = "CHANGELOG.md" output_format: ChangelogOutputFormat = ChangelogOutputFormat.NONE - # TODO: Breaking Change v10, it will become True - mask_initial_release: bool = False + mask_initial_release: bool = True @model_validator(mode="after") def interpret_output_format(self) -> Self: From 5fb02ab6e3b8278ecbf92ed35083ffb595bc19b8 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 19 May 2025 21:18:53 -0600 Subject: [PATCH 138/160] docs(configuration): change the default for the base changelog's `mask_initial_release` value --- docs/configuration.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index 979d6e2ab..b4b9b2b23 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -390,7 +390,9 @@ is there to document? The message details can be found in the ``first_release.md.j2`` and ``first_release.rst.j2`` templates of the default changelog template directory. -**Default:** ``false`` +*Default changed to ``true`` in $NEW_VERSION.* + +**Default:** ``true`` .. seealso:: - :ref:`changelog-templates-default_changelog` From 06be5fc138b04ff40579ac0ee45f3d603c55c557 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 19 May 2025 21:20:03 -0600 Subject: [PATCH 139/160] test(fixtures): update repo builder's default parameter value for `mask_initial_release` --- .../e2e/cmd_version/test_version_changelog.py | 146 ++++++++++++++++++ .../cmd_version/test_version_release_notes.py | 13 +- tests/fixtures/git_repo.py | 18 +-- .../git_flow/repo_w_1_release_channel.py | 2 +- .../git_flow/repo_w_2_release_channels.py | 2 +- .../git_flow/repo_w_3_release_channels.py | 2 +- .../git_flow/repo_w_4_release_channels.py | 2 +- .../github_flow/repo_w_default_release.py | 2 +- .../github_flow/repo_w_release_channels.py | 2 +- tests/fixtures/repos/repo_initial_commit.py | 2 +- .../repo_w_dual_version_support.py | 2 +- ...po_w_dual_version_support_w_prereleases.py | 2 +- .../repos/trunk_based_dev/repo_w_no_tags.py | 44 +++++- .../trunk_based_dev/repo_w_prereleases.py | 2 +- .../repos/trunk_based_dev/repo_w_tags.py | 2 +- 15 files changed, 219 insertions(+), 24 deletions(-) diff --git a/tests/e2e/cmd_version/test_version_changelog.py b/tests/e2e/cmd_version/test_version_changelog.py index ae60e6638..6dea5139b 100644 --- a/tests/e2e/cmd_version/test_version_changelog.py +++ b/tests/e2e/cmd_version/test_version_changelog.py @@ -33,6 +33,7 @@ repo_w_github_flow_w_feature_release_channel_emoji_commits, repo_w_github_flow_w_feature_release_channel_scipy_commits, repo_w_no_tags_conventional_commits, + repo_w_no_tags_conventional_commits_unmasked_initial_release, repo_w_no_tags_emoji_commits, repo_w_no_tags_scipy_commits, repo_w_trunk_only_conventional_commits, @@ -311,6 +312,151 @@ def test_version_updates_changelog_wo_prev_releases( insertion_flag: str, stable_now_date: GetStableDateNowFn, format_date_str: FormatDateStrFn, +): + """ + Given the repository has no releases and the user has provided a initialized changelog, + When the version command is run with changelog.mode set to "update", + Then the version is created and the changelog file is updated with only an initial release statement + """ + if not (repo_build_data := cache.get(cache_key, None)): + pytest.fail("Repo build date not found in cache") + + repo_build_datetime = datetime.strptime(repo_build_data["build_date"], "%Y-%m-%d") + now_datetime = stable_now_date().replace( + year=repo_build_datetime.year, + month=repo_build_datetime.month, + day=repo_build_datetime.day, + ) + repo_build_date_str = format_date_str(now_datetime) + + # Custom text to maintain (must be different from the default) + custom_text = "---{ls}{ls}Custom footer text{ls}".format(ls=os.linesep) + + # Set the project configurations + update_pyproject_toml( + "tool.semantic_release.changelog.mode", ChangelogMode.UPDATE.value + ) + update_pyproject_toml( + "tool.semantic_release.changelog.default_templates.changelog_file", + str(changelog_file.name), + ) + + version = "v1.0.0" + rst_version_header = f"{version} ({repo_build_date_str})" + txt_after_insertion_flag = { + ChangelogOutputFormat.MARKDOWN: str.join( + os.linesep, + [ + f"## {version} ({repo_build_date_str})", + "", + "- Initial Release", + ], + ), + ChangelogOutputFormat.RESTRUCTURED_TEXT: str.join( + os.linesep, + [ + f".. _changelog-{version}:", + "", + rst_version_header, + f"{'=' * len(rst_version_header)}", + "", + "* Initial Release", + ], + ), + } + + # Capture and modify the current changelog content to become the expected output + # We much use os.linesep here since the insertion flag is os-specific + with changelog_file.open(newline=os.linesep) as rfd: + initial_changelog_parts = rfd.read().split(insertion_flag) + + # content is os-specific because of the insertion flag & how we read the original file + expected_changelog_content = str.join( + insertion_flag, + [ + initial_changelog_parts[0], + str.join( + os.linesep, + [ + os.linesep, + txt_after_insertion_flag[changelog_format], + "", + custom_text, + ], + ), + ], + ) + + # Grab the Unreleased changelog & create the initialized user changelog + # force output to not perform any newline translations + with changelog_file.open(mode="w", newline="") as wfd: + wfd.write( + str.join( + insertion_flag, + [initial_changelog_parts[0], f"{os.linesep * 2}{custom_text}"], + ) + ) + wfd.flush() + + # Act + with freeze_time(now_datetime.astimezone(timezone.utc)): + cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--no-push", "--changelog"] + result = run_cli(cli_cmd[1:]) + + # Evaluate + assert_successful_exit_code(result, cli_cmd) + + # Ensure changelog exists + assert changelog_file.exists() + + # Capture the new changelog content (os aware because of expected content) + with changelog_file.open(newline=os.linesep) as rfd: + actual_content = rfd.read() + + # Check that the changelog footer is maintained and updated with Unreleased info + assert expected_changelog_content == actual_content + + +@pytest.mark.parametrize( + "changelog_format, changelog_file, insertion_flag", + [ + ( + ChangelogOutputFormat.MARKDOWN, + lazy_fixture(example_changelog_md.__name__), + lazy_fixture(default_md_changelog_insertion_flag.__name__), + ), + ( + ChangelogOutputFormat.RESTRUCTURED_TEXT, + lazy_fixture(example_changelog_rst.__name__), + lazy_fixture(default_rst_changelog_insertion_flag.__name__), + ), + ], +) +@pytest.mark.parametrize( + "repo_result, cache_key", + [ + pytest.param( + lazy_fixture(repo_fixture), + f"psr/repos/{repo_fixture}", + marks=pytest.mark.comprehensive, + ) + for repo_fixture in [ + # Must not have a single release/tag + repo_w_no_tags_conventional_commits_unmasked_initial_release.__name__, + ] + ], +) +def test_version_updates_changelog_wo_prev_releases_n_unmasked_initial_release( + repo_result: BuiltRepoResult, + cache_key: str, + cache: pytest.Cache, + run_cli: RunCliFn, + update_pyproject_toml: UpdatePyprojectTomlFn, + changelog_format: ChangelogOutputFormat, + changelog_file: Path, + insertion_flag: str, + stable_now_date: GetStableDateNowFn, + format_date_str: FormatDateStrFn, ): """ Given the repository has no releases and the user has provided a initialized changelog, diff --git a/tests/e2e/cmd_version/test_version_release_notes.py b/tests/e2e/cmd_version/test_version_release_notes.py index 2b434f29c..e21059335 100644 --- a/tests/e2e/cmd_version/test_version_release_notes.py +++ b/tests/e2e/cmd_version/test_version_release_notes.py @@ -98,14 +98,16 @@ def test_custom_release_notes_template( @pytest.mark.parametrize( - "repo_result, license_name, license_setting", + "repo_result, license_name, license_setting, mask_initial_release", [ pytest.param( lazy_fixture(repo_fixture_name), license_name, license_setting, + mask_initial_release, marks=pytest.mark.comprehensive, ) + for mask_initial_release in [True, False] for license_name in ["", "MIT", "GPL-3.0"] for license_setting in [ "project.license-expression", @@ -124,6 +126,7 @@ def test_default_release_notes_license_statement( run_cli: RunCliFn, license_name: str, license_setting: str, + mask_initial_release: bool, update_pyproject_toml: UpdatePyprojectTomlFn, mocked_git_push: MagicMock, post_mocker: Mocker, @@ -151,12 +154,18 @@ def test_default_release_notes_license_statement( # Setup: set the license for the test update_pyproject_toml(license_setting, license_name) + # Setup: set mask_initial_release value in configuration + update_pyproject_toml( + "tool.semantic_release.changelog.default_templates.mask_initial_release", + mask_initial_release, + ) + expected_release_notes = generate_default_release_notes_from_def( version_actions=repo_def, hvcs=get_hvcs_client_from_repo_def(repo_def), previous_version=None, license_name=license_name, - mask_initial_release=False, + mask_initial_release=mask_initial_release, ) # Act diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 60d0b4bad..106bc55e4 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -126,7 +126,7 @@ def __call__( hvcs_domain: str = ..., tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, - mask_initial_release: bool = False, + mask_initial_release: bool = True, # Default as of v10 ) -> tuple[Path, HvcsBase]: ... class CommitNReturnChangelogEntryFn(Protocol): @@ -184,7 +184,7 @@ def __call__( dest_file: Path | None = None, max_version: str | None = None, output_format: ChangelogOutputFormat = ChangelogOutputFormat.MARKDOWN, - mask_initial_release: bool = False, + mask_initial_release: bool = True, # Default as of v10 ) -> str: ... class FormatGitSquashCommitMsgFn(Protocol): @@ -343,7 +343,7 @@ def __call__( hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, - mask_initial_release: bool = False, + mask_initial_release: bool = ..., ignore_merge_commits: bool = True, # Default as of v10 ) -> Sequence[RepoActions]: ... @@ -405,7 +405,7 @@ def __call__( previous_version: Version | None = None, license_name: str = "", dest_file: Path | None = None, - mask_initial_release: bool = False, + mask_initial_release: bool = True, # Default as of v10 ) -> str: ... class GetHvcsClientFromRepoDefFn(Protocol): @@ -1000,7 +1000,7 @@ def _build_configured_base_repo( # noqa: C901 hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, - mask_initial_release: bool = False, + mask_initial_release: bool = True, # Default as of v10 ) -> tuple[Path, HvcsBase]: if not cached_example_git_project.exists(): raise RuntimeError("Unable to find cached git project files!") @@ -1259,7 +1259,7 @@ def _build_repo_from_definition( # noqa: C901, its required and its just test c repo_dir = Path(dest_dir) hvcs: Github | Gitlab | Gitea | Bitbucket tag_format_str: str - mask_initial_release: bool = False + mask_initial_release: bool = True # Default as of v10 current_commits: list[CommitDef] = [] current_repo_def: RepoDefinition = {} @@ -1878,8 +1878,7 @@ def _mimic_semantic_release_default_changelog( dest_file: Path | None = None, max_version: str | None = None, output_format: ChangelogOutputFormat = ChangelogOutputFormat.MARKDOWN, - # TODO: Breaking v10, when default is toggled to true, also change this to True - mask_initial_release: bool = False, + mask_initial_release: bool = True, # Default as of v10 ) -> str: if output_format == ChangelogOutputFormat.MARKDOWN: header = dedent( @@ -2094,8 +2093,7 @@ def _generate_default_release_notes( previous_version: Version | None = None, license_name: str = "", dest_file: Path | None = None, - # TODO: Breaking v10, when default is toggled to true, also change this to True - mask_initial_release: bool = False, + mask_initial_release: bool = True, # Default as of v10 ) -> str: limited_repo_def: RepoDefinition = get_commits_from_repo_build_def( build_definition=version_actions, diff --git a/tests/fixtures/repos/git_flow/repo_w_1_release_channel.py b/tests/fixtures/repos/git_flow/repo_w_1_release_channel.py index 9a1669888..bdaf43d64 100644 --- a/tests/fixtures/repos/git_flow/repo_w_1_release_channel.py +++ b/tests/fixtures/repos/git_flow/repo_w_1_release_channel.py @@ -102,7 +102,7 @@ def _get_repo_from_defintion( hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, - mask_initial_release: bool = False, + mask_initial_release: bool = True, ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: stable_now_datetime = stable_now_date() diff --git a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py index 9d5863a25..537ef4b02 100644 --- a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py @@ -102,7 +102,7 @@ def _get_repo_from_defintion( hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, - mask_initial_release: bool = False, + mask_initial_release: bool = True, ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: stable_now_datetime = stable_now_date() diff --git a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py index 1e7f2e04d..bf49b27b5 100644 --- a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py @@ -104,7 +104,7 @@ def _get_repo_from_defintion( hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, - mask_initial_release: bool = False, + mask_initial_release: bool = True, ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: stable_now_datetime = stable_now_date() diff --git a/tests/fixtures/repos/git_flow/repo_w_4_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_4_release_channels.py index 2c8d24e5f..9062bd2b1 100644 --- a/tests/fixtures/repos/git_flow/repo_w_4_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_4_release_channels.py @@ -114,7 +114,7 @@ def _get_repo_from_defintion( hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, - mask_initial_release: bool = False, + mask_initial_release: bool = True, ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: stable_now_datetime = stable_now_date() diff --git a/tests/fixtures/repos/github_flow/repo_w_default_release.py b/tests/fixtures/repos/github_flow/repo_w_default_release.py index b0c2da302..25d30a271 100644 --- a/tests/fixtures/repos/github_flow/repo_w_default_release.py +++ b/tests/fixtures/repos/github_flow/repo_w_default_release.py @@ -97,7 +97,7 @@ def _get_repo_from_definition( hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, - mask_initial_release: bool = False, + mask_initial_release: bool = True, ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: stable_now_datetime = stable_now_date() diff --git a/tests/fixtures/repos/github_flow/repo_w_release_channels.py b/tests/fixtures/repos/github_flow/repo_w_release_channels.py index 4549ffbf6..eec636dd4 100644 --- a/tests/fixtures/repos/github_flow/repo_w_release_channels.py +++ b/tests/fixtures/repos/github_flow/repo_w_release_channels.py @@ -97,7 +97,7 @@ def _get_repo_from_defintion( hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, - mask_initial_release: bool = False, + mask_initial_release: bool = True, ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: stable_now_datetime = stable_now_date() diff --git a/tests/fixtures/repos/repo_initial_commit.py b/tests/fixtures/repos/repo_initial_commit.py index 817386ba7..7bc1fc3bb 100644 --- a/tests/fixtures/repos/repo_initial_commit.py +++ b/tests/fixtures/repos/repo_initial_commit.py @@ -77,7 +77,7 @@ def _get_repo_from_definition( hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, - mask_initial_release: bool = False, + mask_initial_release: bool = True, ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: repo_construction_steps: list[RepoActions] = [] diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support.py b/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support.py index 83665399c..f909c584b 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support.py @@ -91,7 +91,7 @@ def _get_repo_from_definition( hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, - mask_initial_release: bool = False, + mask_initial_release: bool = True, ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: stable_now_datetime = stable_now_date() diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support_w_prereleases.py b/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support_w_prereleases.py index 7f57d6e01..88c07d6c3 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support_w_prereleases.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support_w_prereleases.py @@ -91,7 +91,7 @@ def _get_repo_from_definition( hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, - mask_initial_release: bool = False, + mask_initial_release: bool = True, ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: stable_now_datetime = stable_now_date() diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py index b8134d0d3..37d9b0450 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py @@ -84,7 +84,7 @@ def _get_repo_from_definition( hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, - mask_initial_release: bool = False, + mask_initial_release: bool = True, ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: stable_now_datetime = stable_now_date() @@ -301,6 +301,48 @@ def _build_repo(cached_repo_path: Path) -> Sequence[RepoActions]: } +@pytest.fixture +def repo_w_no_tags_conventional_commits_unmasked_initial_release( + build_repo_from_definition: BuildRepoFromDefinitionFn, + get_repo_definition_4_trunk_only_repo_w_no_tags: GetRepoDefinitionFn, + get_cached_repo_data: GetCachedRepoDataFn, + build_repo_or_copy_cache: BuildRepoOrCopyCacheFn, + build_spec_hash_4_repo_w_no_tags: str, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: ExProjectDir, + change_to_ex_proj_dir: None, +) -> BuiltRepoResult: + """Replicates repo with no tags, but with allow_zero_version=True""" + repo_name = repo_w_no_tags_conventional_commits_unmasked_initial_release.__name__ + commit_type: CommitConvention = ( + repo_name.split("_commits", maxsplit=1)[0].split("_")[-1] # type: ignore[assignment] + ) + + def _build_repo(cached_repo_path: Path) -> Sequence[RepoActions]: + repo_construction_steps = get_repo_definition_4_trunk_only_repo_w_no_tags( + commit_type=commit_type, + extra_configs={ + "tool.semantic_release.changelog.default_templates.mask_initial_release": False, + }, + ) + return build_repo_from_definition(cached_repo_path, repo_construction_steps) + + build_repo_or_copy_cache( + repo_name=repo_name, + build_spec_hash=build_spec_hash_4_repo_w_no_tags, + build_repo_func=_build_repo, + dest_dir=example_project_dir, + ) + + if not (cached_repo_data := get_cached_repo_data(proj_dirname=repo_name)): + raise ValueError("Failed to retrieve repo data from cache") + + return { + "definition": cached_repo_data["build_definition"], + "repo": example_project_git_repo(), + } + + @pytest.fixture def repo_w_no_tags_conventional_commits( build_trunk_only_repo_w_no_tags: BuildSpecificRepoFn, diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py index ad4515268..a72ef1e20 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py @@ -85,7 +85,7 @@ def _get_repo_from_definition( hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, - mask_initial_release: bool = False, + mask_initial_release: bool = True, ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: stable_now_datetime = stable_now_date() diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py index 08ed83be3..40741bc18 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py @@ -87,7 +87,7 @@ def _get_repo_from_definition( hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, - mask_initial_release: bool = False, + mask_initial_release: bool = True, ignore_merge_commits: bool = True, ) -> Sequence[RepoActions]: stable_now_datetime = stable_now_date() From 7d39e7675f859463b54751d59957b869d5d8395c Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 20 May 2025 19:29:01 -0600 Subject: [PATCH 140/160] refactor(config)!: change `changelog.mode` default to `update` Changes the default behavior of PSR when `changelog.mode` setting is not provided. BREAKING CHANGE: This release switches the `changelog.mode` default to `update`. In this mode, if a changelog exists, PSR will update the changelog **IF AND ONLY IF** the configured insertion flag exists in the changelog. The Changelog output will remain unchanged if no insertion flag exists. The insertion flag may be configured with the `changelog.insertion_flag` setting. When upgrading to `v10`, you must add the insertion flag manually or you can just delete the changelog file and run PSR's changelog generation and it will rebuild the changelog (similar to init mode) but it will add the insertion flag. If your configuration already sets the `changelog.mode` value, then this change will have no effect on your project. If you would rather the changelog be generated from scratch every release, than set the `changelog.mode` value to `init` in your configuration. --- src/semantic_release/cli/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/semantic_release/cli/config.py b/src/semantic_release/cli/config.py index ce40ca0b9..59df69834 100644 --- a/src/semantic_release/cli/config.py +++ b/src/semantic_release/cli/config.py @@ -157,7 +157,7 @@ class ChangelogConfig(BaseModel): ) environment: ChangelogEnvironmentConfig = ChangelogEnvironmentConfig() exclude_commit_patterns: Tuple[str, ...] = () - mode: ChangelogMode = ChangelogMode.INIT + mode: ChangelogMode = ChangelogMode.UPDATE insertion_flag: str = "" template_dir: str = "templates" From 0bed9069df67ae806ad0a15f8434ac4efcc6ba31 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 20 May 2025 19:43:11 -0600 Subject: [PATCH 141/160] docs(configuration): change the default value for `changelog.mode` in the setting description # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # On branch brk/changelog-update-as-default # Changes to be committed: # modified: docs/configuration.rst # --- docs/configuration.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index b4b9b2b23..34411e579 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -664,7 +664,7 @@ The patterns in this list are treated as regular expressions. ``mode`` ******** -*Introduced in v9.10.0* +*Introduced in v9.10.0. Default changed to `update` in $NEW_VERSION.* **Type:** ``Literal["init", "update"]`` @@ -682,7 +682,7 @@ version information at that location. If you are using a custom template directory, the `context.changelog_mode` value will exist in the changelog context but it is up to your implementation to determine if and/or how to use it. -**Default:** ``init`` +**Default:** ``update`` .. seealso:: - :ref:`changelog-templates-default_changelog` From 7d58fde8146fcdefb64631f287be4f5df24b6bee Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 20 May 2025 23:53:18 -0600 Subject: [PATCH 142/160] test(cmd-version): update tests of repo builders for `changelog.mode=update` --- .../e2e/cmd_version/bump_version/conftest.py | 107 +++++++++++++++--- .../git_flow/test_repo_1_channel.py | 31 ++--- .../git_flow/test_repo_2_channels.py | 31 ++--- .../git_flow/test_repo_3_channels.py | 32 ++---- .../git_flow/test_repo_4_channels.py | 31 ++--- .../github_flow/test_repo_1_channel.py | 31 ++--- .../github_flow/test_repo_2_channels.py | 31 ++--- .../trunk_based_dev/test_repo_trunk.py | 31 ++--- .../test_repo_trunk_dual_version_support.py | 29 ++--- ...runk_dual_version_support_w_prereleases.py | 49 ++------ .../test_repo_trunk_w_prereleases.py | 51 ++------- tests/e2e/conftest.py | 14 +-- 12 files changed, 196 insertions(+), 272 deletions(-) diff --git a/tests/e2e/cmd_version/bump_version/conftest.py b/tests/e2e/cmd_version/bump_version/conftest.py index 23924b579..da36ff1d2 100644 --- a/tests/e2e/cmd_version/bump_version/conftest.py +++ b/tests/e2e/cmd_version/bump_version/conftest.py @@ -1,15 +1,23 @@ from __future__ import annotations -import shutil from typing import TYPE_CHECKING import pytest from git import Repo +from semantic_release.hvcs.github import Github + +from tests.const import MAIN_PROG_NAME, VERSION_SUBCMD +from tests.util import assert_successful_exit_code + if TYPE_CHECKING: from pathlib import Path from typing import Protocol + from click.testing import Result + + from tests.conftest import RunCliFn + from tests.fixtures.example_project import UpdatePyprojectTomlFn from tests.fixtures.git_repo import BuildRepoFromDefinitionFn, RepoActionConfigure class InitMirrorRepo4RebuildFn(Protocol): @@ -19,13 +27,19 @@ def __call__( configuration_step: RepoActionConfigure, ) -> Path: ... + class RunPSReleaseFn(Protocol): + def __call__( + self, + next_version_str: str, + git_repo: Repo, + ) -> Result: ... + @pytest.fixture(scope="session") def init_mirror_repo_for_rebuild( - default_changelog_md_template: Path, - default_changelog_rst_template: Path, - changelog_template_dir: Path, build_repo_from_definition: BuildRepoFromDefinitionFn, + changelog_md_file: Path, + changelog_rst_file: Path, ) -> InitMirrorRepo4RebuildFn: def _init_mirror_repo_for_rebuild( mirror_repo_dir: Path, @@ -40,21 +54,80 @@ def _init_mirror_repo_for_rebuild( repo_construction_steps=[configuration_step], ) - # Force custom changelog to be a copy of the default changelog (md and rst) - shutil.copytree( - src=default_changelog_md_template.parent, - dst=mirror_repo_dir / changelog_template_dir, - dirs_exist_ok=True, - ) - shutil.copytree( - src=default_changelog_rst_template.parent, - dst=mirror_repo_dir / changelog_template_dir, - dirs_exist_ok=True, - ) - with Repo(mirror_repo_dir) as mirror_git_repo: - mirror_git_repo.git.add(str(changelog_template_dir)) + # remove the default changelog files to enable Update Mode (new default of v10) + mirror_git_repo.git.rm(str(changelog_md_file), force=True) + mirror_git_repo.git.rm(str(changelog_rst_file), force=True) return mirror_repo_dir return _init_mirror_repo_for_rebuild + + +@pytest.fixture(scope="session") +def run_psr_release( + run_cli: RunCliFn, + changelog_rst_file: Path, + update_pyproject_toml: UpdatePyprojectTomlFn, +) -> RunPSReleaseFn: + base_version_cmd = [MAIN_PROG_NAME, "--strict", VERSION_SUBCMD] + write_changelog_only_cmd = [ + *base_version_cmd, + "--changelog", + "--no-commit", + "--no-tag", + "--skip-build", + ] + + def _run_psr_release( + next_version_str: str, + git_repo: Repo, + ) -> Result: + version_n_buildmeta = next_version_str.split("+", maxsplit=1) + version_n_prerelease = version_n_buildmeta[0].split("-", maxsplit=1) + + build_metadata_args = ( + ["--build-metadata", version_n_buildmeta[-1]] + if len(version_n_buildmeta) > 1 + else [] + ) + + prerelease_args = ( + [ + "--as-prerelease", + "--prerelease-token", + version_n_prerelease[-1].split(".", maxsplit=1)[0], + ] + if len(version_n_prerelease) > 1 + else [] + ) + + # Initial run to write the RST changelog + # 1. configure PSR to write the RST changelog with the RST default insertion flag + update_pyproject_toml( + "tool.semantic_release.changelog.default_templates.changelog_file", + str(changelog_rst_file), + ) + cli_cmd = [*write_changelog_only_cmd, *prerelease_args, *build_metadata_args] + result = run_cli(cli_cmd[1:], env={Github.DEFAULT_ENV_TOKEN_NAME: "1234"}) + assert_successful_exit_code(result, cli_cmd) + + # Reset the index in case PSR added anything to the index + git_repo.git.reset("--mixed", "HEAD") + + # Add the changelog file to the git index but reset the working directory + git_repo.git.add(str(changelog_rst_file)) + git_repo.git.checkout("--", ".") + + # Actual run to release & write the MD changelog + cli_cmd = [ + *base_version_cmd, + *prerelease_args, + *build_metadata_args, + ] + result = run_cli(cli_cmd[1:], env={Github.DEFAULT_ENV_TOKEN_NAME: "1234"}) + assert_successful_exit_code(result, cli_cmd) + + return result + + return _run_psr_release diff --git a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_1_channel.py b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_1_channel.py index 979a1ad8e..e257448f8 100644 --- a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_1_channel.py +++ b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_1_channel.py @@ -7,16 +7,12 @@ from flatdict import FlatDict from freezegun import freeze_time -from tests.const import ( - MAIN_PROG_NAME, - VERSION_SUBCMD, -) from tests.fixtures.repos.git_flow import ( repo_w_git_flow_conventional_commits, repo_w_git_flow_emoji_commits, repo_w_git_flow_scipy_commits, ) -from tests.util import assert_successful_exit_code, temporary_working_directory +from tests.util import temporary_working_directory if TYPE_CHECKING: from pathlib import Path @@ -24,8 +20,10 @@ from requests_mock import Mocker - from tests.conftest import RunCliFn - from tests.e2e.cmd_version.bump_version.conftest import InitMirrorRepo4RebuildFn + from tests.e2e.cmd_version.bump_version.conftest import ( + InitMirrorRepo4RebuildFn, + RunPSReleaseFn, + ) from tests.e2e.conftest import GetSanitizedChangelogContentFn from tests.fixtures.example_project import ExProjectDir from tests.fixtures.git_repo import ( @@ -53,7 +51,7 @@ ) def test_gitflow_repo_rebuild_1_channel( repo_fixture_name: str, - run_cli: RunCliFn, + run_psr_release: RunPSReleaseFn, build_git_flow_repo_w_1_release_channels: BuildSpecificRepoFn, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, init_mirror_repo_for_rebuild: InitMirrorRepo4RebuildFn, @@ -131,19 +129,10 @@ def test_gitflow_repo_rebuild_1_channel( with freeze_time( release_action_step["details"]["datetime"] ), temporary_working_directory(mirror_repo_dir): - build_metadata_args = ( - [ - "--build-metadata", - release_action_step["details"]["version"].split("+", maxsplit=1)[ - -1 - ], - ] - if len(release_action_step["details"]["version"].split("+", maxsplit=1)) - > 1 - else [] + run_psr_release( + next_version_str=release_action_step["details"]["version"], + git_repo=mirror_git_repo, ) - cli_cmd = [MAIN_PROG_NAME, "--strict", VERSION_SUBCMD, *build_metadata_args] - result = run_cli(cli_cmd[1:]) # take measurement after running the version command actual_release_commit_text = mirror_git_repo.head.commit.message @@ -157,7 +146,7 @@ def test_gitflow_repo_rebuild_1_channel( ) # Evaluate (normal release actions should have occurred as expected) - assert_successful_exit_code(result, cli_cmd) + # ------------------------------------------------------------------ # Make sure version file is updated assert expected_pyproject_toml_content == actual_pyproject_toml_content assert expected_version_file_content == actual_version_file_content diff --git a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_2_channels.py b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_2_channels.py index 5ffc6bef4..2f6b30c76 100644 --- a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_2_channels.py +++ b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_2_channels.py @@ -7,16 +7,12 @@ from flatdict import FlatDict from freezegun import freeze_time -from tests.const import ( - MAIN_PROG_NAME, - VERSION_SUBCMD, -) from tests.fixtures.repos.git_flow import ( repo_w_git_flow_w_alpha_prereleases_n_conventional_commits, repo_w_git_flow_w_alpha_prereleases_n_emoji_commits, repo_w_git_flow_w_alpha_prereleases_n_scipy_commits, ) -from tests.util import assert_successful_exit_code, temporary_working_directory +from tests.util import temporary_working_directory if TYPE_CHECKING: from pathlib import Path @@ -24,8 +20,10 @@ from requests_mock import Mocker - from tests.conftest import RunCliFn - from tests.e2e.cmd_version.bump_version.conftest import InitMirrorRepo4RebuildFn + from tests.e2e.cmd_version.bump_version.conftest import ( + InitMirrorRepo4RebuildFn, + RunPSReleaseFn, + ) from tests.e2e.conftest import GetSanitizedChangelogContentFn from tests.fixtures.example_project import ExProjectDir from tests.fixtures.git_repo import ( @@ -53,7 +51,7 @@ ) def test_gitflow_repo_rebuild_2_channels( repo_fixture_name: str, - run_cli: RunCliFn, + run_psr_release: RunPSReleaseFn, build_git_flow_repo_w_2_release_channels: BuildSpecificRepoFn, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, init_mirror_repo_for_rebuild: InitMirrorRepo4RebuildFn, @@ -131,19 +129,10 @@ def test_gitflow_repo_rebuild_2_channels( with freeze_time( release_action_step["details"]["datetime"] ), temporary_working_directory(mirror_repo_dir): - build_metadata_args = ( - [ - "--build-metadata", - release_action_step["details"]["version"].split("+", maxsplit=1)[ - -1 - ], - ] - if len(release_action_step["details"]["version"].split("+", maxsplit=1)) - > 1 - else [] + run_psr_release( + next_version_str=release_action_step["details"]["version"], + git_repo=mirror_git_repo, ) - cli_cmd = [MAIN_PROG_NAME, "--strict", VERSION_SUBCMD, *build_metadata_args] - result = run_cli(cli_cmd[1:]) # take measurement after running the version command actual_release_commit_text = mirror_git_repo.head.commit.message @@ -157,7 +146,7 @@ def test_gitflow_repo_rebuild_2_channels( ) # Evaluate (normal release actions should have occurred as expected) - assert_successful_exit_code(result, cli_cmd) + # ------------------------------------------------------------------ # Make sure version file is updated assert expected_pyproject_toml_content == actual_pyproject_toml_content assert expected_version_file_content == actual_version_file_content diff --git a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_3_channels.py b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_3_channels.py index c9bd6ccc5..a4dc00675 100644 --- a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_3_channels.py +++ b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_3_channels.py @@ -7,17 +7,13 @@ from flatdict import FlatDict from freezegun import freeze_time -from tests.const import ( - MAIN_PROG_NAME, - VERSION_SUBCMD, -) from tests.fixtures.repos.git_flow import ( repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits, repo_w_git_flow_w_rc_n_alpha_prereleases_n_conventional_commits_using_tag_format, repo_w_git_flow_w_rc_n_alpha_prereleases_n_emoji_commits, repo_w_git_flow_w_rc_n_alpha_prereleases_n_scipy_commits, ) -from tests.util import assert_successful_exit_code, temporary_working_directory +from tests.util import temporary_working_directory if TYPE_CHECKING: from pathlib import Path @@ -25,8 +21,10 @@ from requests_mock import Mocker - from tests.conftest import RunCliFn - from tests.e2e.cmd_version.bump_version.conftest import InitMirrorRepo4RebuildFn + from tests.e2e.cmd_version.bump_version.conftest import ( + InitMirrorRepo4RebuildFn, + RunPSReleaseFn, + ) from tests.e2e.conftest import GetSanitizedChangelogContentFn from tests.fixtures.example_project import ExProjectDir from tests.fixtures.git_repo import ( @@ -55,7 +53,7 @@ ) def test_gitflow_repo_rebuild_3_channels( repo_fixture_name: str, - run_cli: RunCliFn, + run_psr_release: RunPSReleaseFn, build_git_flow_repo_w_3_release_channels: BuildSpecificRepoFn, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, init_mirror_repo_for_rebuild: InitMirrorRepo4RebuildFn, @@ -133,20 +131,10 @@ def test_gitflow_repo_rebuild_3_channels( with freeze_time( release_action_step["details"]["datetime"] ), temporary_working_directory(mirror_repo_dir): - build_metadata_args = ( - [ - "--build-metadata", - release_action_step["details"]["version"].split("+", maxsplit=1)[ - -1 - ], - ] - if len(release_action_step["details"]["version"].split("+", maxsplit=1)) - > 1 - else [] + run_psr_release( + next_version_str=release_action_step["details"]["version"], + git_repo=mirror_git_repo, ) - cli_cmd = [MAIN_PROG_NAME, "--strict", VERSION_SUBCMD, *build_metadata_args] - result = run_cli(cli_cmd[1:]) - # take measurement after running the version command actual_release_commit_text = mirror_git_repo.head.commit.message actual_pyproject_toml_content = (mirror_repo_dir / "pyproject.toml").read_text() @@ -159,7 +147,7 @@ def test_gitflow_repo_rebuild_3_channels( ) # Evaluate (normal release actions should have occurred as expected) - assert_successful_exit_code(result, cli_cmd) + # ------------------------------------------------------------------ # Make sure version file is updated assert expected_pyproject_toml_content == actual_pyproject_toml_content assert expected_version_file_content == actual_version_file_content diff --git a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_4_channels.py b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_4_channels.py index e031d86d4..eeeaa7598 100644 --- a/tests/e2e/cmd_version/bump_version/git_flow/test_repo_4_channels.py +++ b/tests/e2e/cmd_version/bump_version/git_flow/test_repo_4_channels.py @@ -7,16 +7,12 @@ from flatdict import FlatDict from freezegun import freeze_time -from tests.const import ( - MAIN_PROG_NAME, - VERSION_SUBCMD, -) from tests.fixtures.repos.git_flow import ( repo_w_git_flow_w_beta_alpha_rev_prereleases_n_conventional_commits, repo_w_git_flow_w_beta_alpha_rev_prereleases_n_emoji_commits, repo_w_git_flow_w_beta_alpha_rev_prereleases_n_scipy_commits, ) -from tests.util import assert_successful_exit_code, temporary_working_directory +from tests.util import temporary_working_directory if TYPE_CHECKING: from pathlib import Path @@ -24,8 +20,10 @@ from requests_mock import Mocker - from tests.conftest import RunCliFn - from tests.e2e.cmd_version.bump_version.conftest import InitMirrorRepo4RebuildFn + from tests.e2e.cmd_version.bump_version.conftest import ( + InitMirrorRepo4RebuildFn, + RunPSReleaseFn, + ) from tests.e2e.conftest import GetSanitizedChangelogContentFn from tests.fixtures.example_project import ExProjectDir from tests.fixtures.git_repo import ( @@ -53,7 +51,7 @@ ) def test_gitflow_repo_rebuild_4_channels( repo_fixture_name: str, - run_cli: RunCliFn, + run_psr_release: RunPSReleaseFn, build_git_flow_repo_w_4_release_channels: BuildSpecificRepoFn, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, init_mirror_repo_for_rebuild: InitMirrorRepo4RebuildFn, @@ -131,19 +129,10 @@ def test_gitflow_repo_rebuild_4_channels( with freeze_time( release_action_step["details"]["datetime"] ), temporary_working_directory(mirror_repo_dir): - build_metadata_args = ( - [ - "--build-metadata", - release_action_step["details"]["version"].split("+", maxsplit=1)[ - -1 - ], - ] - if len(release_action_step["details"]["version"].split("+", maxsplit=1)) - > 1 - else [] + run_psr_release( + next_version_str=release_action_step["details"]["version"], + git_repo=mirror_git_repo, ) - cli_cmd = [MAIN_PROG_NAME, "--strict", VERSION_SUBCMD, *build_metadata_args] - result = run_cli(cli_cmd[1:]) # take measurement after running the version command actual_release_commit_text = mirror_git_repo.head.commit.message @@ -157,7 +146,7 @@ def test_gitflow_repo_rebuild_4_channels( ) # Evaluate (normal release actions should have occurred as expected) - assert_successful_exit_code(result, cli_cmd) + # ------------------------------------------------------------------ # Make sure version file is updated assert expected_pyproject_toml_content == actual_pyproject_toml_content assert expected_version_file_content == actual_version_file_content diff --git a/tests/e2e/cmd_version/bump_version/github_flow/test_repo_1_channel.py b/tests/e2e/cmd_version/bump_version/github_flow/test_repo_1_channel.py index fe166f540..2dec4e393 100644 --- a/tests/e2e/cmd_version/bump_version/github_flow/test_repo_1_channel.py +++ b/tests/e2e/cmd_version/bump_version/github_flow/test_repo_1_channel.py @@ -7,16 +7,12 @@ from flatdict import FlatDict from freezegun import freeze_time -from tests.const import ( - MAIN_PROG_NAME, - VERSION_SUBCMD, -) from tests.fixtures.repos.github_flow import ( repo_w_github_flow_w_default_release_channel_conventional_commits, repo_w_github_flow_w_default_release_channel_emoji_commits, repo_w_github_flow_w_default_release_channel_scipy_commits, ) -from tests.util import assert_successful_exit_code, temporary_working_directory +from tests.util import temporary_working_directory if TYPE_CHECKING: from pathlib import Path @@ -24,8 +20,10 @@ from requests_mock import Mocker - from tests.conftest import RunCliFn - from tests.e2e.cmd_version.bump_version.conftest import InitMirrorRepo4RebuildFn + from tests.e2e.cmd_version.bump_version.conftest import ( + InitMirrorRepo4RebuildFn, + RunPSReleaseFn, + ) from tests.e2e.conftest import GetSanitizedChangelogContentFn from tests.fixtures.example_project import ExProjectDir from tests.fixtures.git_repo import ( @@ -53,7 +51,7 @@ ) def test_githubflow_repo_rebuild_1_channel( repo_fixture_name: str, - run_cli: RunCliFn, + run_psr_release: RunPSReleaseFn, build_repo_w_github_flow_w_default_release_channel: BuildSpecificRepoFn, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, init_mirror_repo_for_rebuild: InitMirrorRepo4RebuildFn, @@ -131,19 +129,10 @@ def test_githubflow_repo_rebuild_1_channel( with freeze_time( release_action_step["details"]["datetime"] ), temporary_working_directory(mirror_repo_dir): - build_metadata_args = ( - [ - "--build-metadata", - release_action_step["details"]["version"].split("+", maxsplit=1)[ - -1 - ], - ] - if len(release_action_step["details"]["version"].split("+", maxsplit=1)) - > 1 - else [] + run_psr_release( + next_version_str=release_action_step["details"]["version"], + git_repo=mirror_git_repo, ) - cli_cmd = [MAIN_PROG_NAME, "--strict", VERSION_SUBCMD, *build_metadata_args] - result = run_cli(cli_cmd[1:]) # take measurement after running the version command actual_release_commit_text = mirror_git_repo.head.commit.message @@ -157,7 +146,7 @@ def test_githubflow_repo_rebuild_1_channel( ) # Evaluate (normal release actions should have occurred as expected) - assert_successful_exit_code(result, cli_cmd) + # ------------------------------------------------------------------ # Make sure version file is updated assert expected_pyproject_toml_content == actual_pyproject_toml_content assert expected_version_file_content == actual_version_file_content diff --git a/tests/e2e/cmd_version/bump_version/github_flow/test_repo_2_channels.py b/tests/e2e/cmd_version/bump_version/github_flow/test_repo_2_channels.py index 3f944fd3b..8d2ebd3c3 100644 --- a/tests/e2e/cmd_version/bump_version/github_flow/test_repo_2_channels.py +++ b/tests/e2e/cmd_version/bump_version/github_flow/test_repo_2_channels.py @@ -7,16 +7,12 @@ from flatdict import FlatDict from freezegun import freeze_time -from tests.const import ( - MAIN_PROG_NAME, - VERSION_SUBCMD, -) from tests.fixtures.repos.github_flow import ( repo_w_github_flow_w_feature_release_channel_conventional_commits, repo_w_github_flow_w_feature_release_channel_emoji_commits, repo_w_github_flow_w_feature_release_channel_scipy_commits, ) -from tests.util import assert_successful_exit_code, temporary_working_directory +from tests.util import temporary_working_directory if TYPE_CHECKING: from pathlib import Path @@ -24,8 +20,10 @@ from requests_mock import Mocker - from tests.conftest import RunCliFn - from tests.e2e.cmd_version.bump_version.conftest import InitMirrorRepo4RebuildFn + from tests.e2e.cmd_version.bump_version.conftest import ( + InitMirrorRepo4RebuildFn, + RunPSReleaseFn, + ) from tests.e2e.conftest import GetSanitizedChangelogContentFn from tests.fixtures.example_project import ExProjectDir from tests.fixtures.git_repo import ( @@ -53,7 +51,7 @@ ) def test_githubflow_repo_rebuild_2_channels( repo_fixture_name: str, - run_cli: RunCliFn, + run_psr_release: RunPSReleaseFn, build_repo_w_github_flow_w_feature_release_channel: BuildSpecificRepoFn, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, init_mirror_repo_for_rebuild: InitMirrorRepo4RebuildFn, @@ -131,19 +129,10 @@ def test_githubflow_repo_rebuild_2_channels( with freeze_time( release_action_step["details"]["datetime"] ), temporary_working_directory(mirror_repo_dir): - build_metadata_args = ( - [ - "--build-metadata", - release_action_step["details"]["version"].split("+", maxsplit=1)[ - -1 - ], - ] - if len(release_action_step["details"]["version"].split("+", maxsplit=1)) - > 1 - else [] + run_psr_release( + next_version_str=release_action_step["details"]["version"], + git_repo=mirror_git_repo, ) - cli_cmd = [MAIN_PROG_NAME, "--strict", VERSION_SUBCMD, *build_metadata_args] - result = run_cli(cli_cmd[1:]) # take measurement after running the version command actual_release_commit_text = mirror_git_repo.head.commit.message @@ -157,7 +146,7 @@ def test_githubflow_repo_rebuild_2_channels( ) # Evaluate (normal release actions should have occurred as expected) - assert_successful_exit_code(result, cli_cmd) + # ------------------------------------------------------------------ # Make sure version file is updated assert expected_pyproject_toml_content == actual_pyproject_toml_content assert expected_version_file_content == actual_version_file_content diff --git a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk.py b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk.py index e091b5d1e..d079b6638 100644 --- a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk.py +++ b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk.py @@ -7,16 +7,12 @@ from flatdict import FlatDict from freezegun import freeze_time -from tests.const import ( - MAIN_PROG_NAME, - VERSION_SUBCMD, -) from tests.fixtures.repos.trunk_based_dev import ( repo_w_trunk_only_conventional_commits, repo_w_trunk_only_emoji_commits, repo_w_trunk_only_scipy_commits, ) -from tests.util import assert_successful_exit_code, temporary_working_directory +from tests.util import temporary_working_directory if TYPE_CHECKING: from pathlib import Path @@ -24,8 +20,10 @@ from requests_mock import Mocker - from tests.conftest import RunCliFn - from tests.e2e.cmd_version.bump_version.conftest import InitMirrorRepo4RebuildFn + from tests.e2e.cmd_version.bump_version.conftest import ( + InitMirrorRepo4RebuildFn, + RunPSReleaseFn, + ) from tests.e2e.conftest import GetSanitizedChangelogContentFn from tests.fixtures.example_project import ExProjectDir from tests.fixtures.git_repo import ( @@ -55,7 +53,7 @@ ) def test_trunk_repo_rebuild_only_official_releases( repo_fixture_name: str, - run_cli: RunCliFn, + run_psr_release: RunPSReleaseFn, build_trunk_only_repo_w_tags: BuildSpecificRepoFn, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, init_mirror_repo_for_rebuild: InitMirrorRepo4RebuildFn, @@ -133,19 +131,10 @@ def test_trunk_repo_rebuild_only_official_releases( with freeze_time( release_action_step["details"]["datetime"] ), temporary_working_directory(mirror_repo_dir): - build_metadata_args = ( - [ - "--build-metadata", - release_action_step["details"]["version"].split("+", maxsplit=1)[ - -1 - ], - ] - if len(release_action_step["details"]["version"].split("+", maxsplit=1)) - > 1 - else [] + run_psr_release( + next_version_str=release_action_step["details"]["version"], + git_repo=mirror_git_repo, ) - cli_cmd = [MAIN_PROG_NAME, "--strict", VERSION_SUBCMD, *build_metadata_args] - result = run_cli(cli_cmd[1:]) # take measurement after running the version command actual_release_commit_text = mirror_git_repo.head.commit.message @@ -159,7 +148,7 @@ def test_trunk_repo_rebuild_only_official_releases( ) # Evaluate (normal release actions should have occurred as expected) - assert_successful_exit_code(result, cli_cmd) + # ------------------------------------------------------------------ # Make sure version file is updated assert expected_pyproject_toml_content == actual_pyproject_toml_content assert expected_version_file_content == actual_version_file_content diff --git a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support.py b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support.py index f2f8c0ccf..f68bf817e 100644 --- a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support.py +++ b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support.py @@ -9,15 +9,13 @@ from tests.const import ( DEFAULT_BRANCH_NAME, - MAIN_PROG_NAME, - VERSION_SUBCMD, ) from tests.fixtures.repos.trunk_based_dev import ( repo_w_trunk_only_dual_version_spt_conventional_commits, repo_w_trunk_only_dual_version_spt_emoji_commits, repo_w_trunk_only_dual_version_spt_scipy_commits, ) -from tests.util import assert_successful_exit_code, temporary_working_directory +from tests.util import temporary_working_directory if TYPE_CHECKING: from pathlib import Path @@ -25,8 +23,10 @@ from requests_mock import Mocker - from tests.conftest import RunCliFn - from tests.e2e.cmd_version.bump_version.conftest import InitMirrorRepo4RebuildFn + from tests.e2e.cmd_version.bump_version.conftest import ( + InitMirrorRepo4RebuildFn, + RunPSReleaseFn, + ) from tests.e2e.conftest import GetSanitizedChangelogContentFn from tests.fixtures.example_project import ExProjectDir from tests.fixtures.git_repo import ( @@ -54,7 +54,7 @@ ) def test_trunk_repo_rebuild_dual_version_spt_official_releases_only( repo_fixture_name: str, - run_cli: RunCliFn, + run_psr_release: RunPSReleaseFn, build_trunk_only_repo_w_dual_version_support: BuildSpecificRepoFn, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, init_mirror_repo_for_rebuild: InitMirrorRepo4RebuildFn, @@ -137,19 +137,10 @@ def test_trunk_repo_rebuild_dual_version_spt_official_releases_only( with freeze_time( release_action_step["details"]["datetime"] ), temporary_working_directory(mirror_repo_dir): - build_metadata_args = ( - [ - "--build-metadata", - release_action_step["details"]["version"].split("+", maxsplit=1)[ - -1 - ], - ] - if len(release_action_step["details"]["version"].split("+", maxsplit=1)) - > 1 - else [] + run_psr_release( + next_version_str=release_action_step["details"]["version"], + git_repo=mirror_git_repo, ) - cli_cmd = [MAIN_PROG_NAME, "--strict", VERSION_SUBCMD, *build_metadata_args] - result = run_cli(cli_cmd[1:]) # take measurement after running the version command actual_release_commit_text = mirror_git_repo.head.commit.message @@ -163,7 +154,7 @@ def test_trunk_repo_rebuild_dual_version_spt_official_releases_only( ) # Evaluate (normal release actions should have occurred as expected) - assert_successful_exit_code(result, cli_cmd) + # ------------------------------------------------------------------ # Make sure version file is updated assert expected_pyproject_toml_content == actual_pyproject_toml_content assert expected_version_file_content == actual_version_file_content diff --git a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support_w_prereleases.py b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support_w_prereleases.py index bd00935f7..1514dac38 100644 --- a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support_w_prereleases.py +++ b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_dual_version_support_w_prereleases.py @@ -9,15 +9,13 @@ from tests.const import ( DEFAULT_BRANCH_NAME, - MAIN_PROG_NAME, - VERSION_SUBCMD, ) from tests.fixtures.repos.trunk_based_dev import ( repo_w_trunk_only_dual_version_spt_w_prereleases_conventional_commits, repo_w_trunk_only_dual_version_spt_w_prereleases_emoji_commits, repo_w_trunk_only_dual_version_spt_w_prereleases_scipy_commits, ) -from tests.util import assert_successful_exit_code, temporary_working_directory +from tests.util import temporary_working_directory if TYPE_CHECKING: from pathlib import Path @@ -25,8 +23,10 @@ from requests_mock import Mocker - from tests.conftest import RunCliFn - from tests.e2e.cmd_version.bump_version.conftest import InitMirrorRepo4RebuildFn + from tests.e2e.cmd_version.bump_version.conftest import ( + InitMirrorRepo4RebuildFn, + RunPSReleaseFn, + ) from tests.e2e.conftest import GetSanitizedChangelogContentFn from tests.fixtures.example_project import ExProjectDir from tests.fixtures.git_repo import ( @@ -54,7 +54,7 @@ ) def test_trunk_repo_rebuild_dual_version_spt_w_official_n_prereleases( repo_fixture_name: str, - run_cli: RunCliFn, + run_psr_release: RunPSReleaseFn, build_trunk_only_repo_w_dual_version_spt_w_prereleases: BuildSpecificRepoFn, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, init_mirror_repo_for_rebuild: InitMirrorRepo4RebuildFn, @@ -137,39 +137,10 @@ def test_trunk_repo_rebuild_dual_version_spt_w_official_n_prereleases( with freeze_time( release_action_step["details"]["datetime"] ), temporary_working_directory(mirror_repo_dir): - build_metadata_args = ( - [ - "--build-metadata", - release_action_step["details"]["version"].split("+", maxsplit=1)[ - -1 - ], - ] - if len(release_action_step["details"]["version"].split("+", maxsplit=1)) - > 1 - else [] - ) - prerelease_args = ( - [ - "--as-prerelease", - "--prerelease-token", - ( - release_action_step["details"]["version"] - .split("-", maxsplit=1)[-1] - .split(".", maxsplit=1)[0] - ), - ] - if len(release_action_step["details"]["version"].split("-", maxsplit=1)) - > 1 - else [] + run_psr_release( + next_version_str=release_action_step["details"]["version"], + git_repo=mirror_git_repo, ) - cli_cmd = [ - MAIN_PROG_NAME, - "--strict", - VERSION_SUBCMD, - *build_metadata_args, - *prerelease_args, - ] - result = run_cli(cli_cmd[1:]) # take measurement after running the version command actual_release_commit_text = mirror_git_repo.head.commit.message @@ -183,7 +154,7 @@ def test_trunk_repo_rebuild_dual_version_spt_w_official_n_prereleases( ) # Evaluate (normal release actions should have occurred as expected) - assert_successful_exit_code(result, cli_cmd) + # ------------------------------------------------------------------ # Make sure version file is updated assert expected_pyproject_toml_content == actual_pyproject_toml_content assert expected_version_file_content == actual_version_file_content diff --git a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_w_prereleases.py b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_w_prereleases.py index 0d0aff235..67af5d56a 100644 --- a/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_w_prereleases.py +++ b/tests/e2e/cmd_version/bump_version/trunk_based_dev/test_repo_trunk_w_prereleases.py @@ -7,16 +7,12 @@ from flatdict import FlatDict from freezegun import freeze_time -from tests.const import ( - MAIN_PROG_NAME, - VERSION_SUBCMD, -) from tests.fixtures.repos.trunk_based_dev import ( repo_w_trunk_only_n_prereleases_conventional_commits, repo_w_trunk_only_n_prereleases_emoji_commits, repo_w_trunk_only_n_prereleases_scipy_commits, ) -from tests.util import assert_successful_exit_code, temporary_working_directory +from tests.util import temporary_working_directory if TYPE_CHECKING: from pathlib import Path @@ -24,8 +20,10 @@ from requests_mock import Mocker - from tests.conftest import RunCliFn - from tests.e2e.cmd_version.bump_version.conftest import InitMirrorRepo4RebuildFn + from tests.e2e.cmd_version.bump_version.conftest import ( + InitMirrorRepo4RebuildFn, + RunPSReleaseFn, + ) from tests.e2e.conftest import GetSanitizedChangelogContentFn from tests.fixtures.example_project import ExProjectDir from tests.fixtures.git_repo import ( @@ -53,7 +51,7 @@ ) def test_trunk_repo_rebuild_w_prereleases( repo_fixture_name: str, - run_cli: RunCliFn, + run_psr_release: RunPSReleaseFn, build_trunk_only_repo_w_prerelease_tags: BuildSpecificRepoFn, split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, init_mirror_repo_for_rebuild: InitMirrorRepo4RebuildFn, @@ -131,39 +129,10 @@ def test_trunk_repo_rebuild_w_prereleases( with freeze_time( release_action_step["details"]["datetime"] ), temporary_working_directory(mirror_repo_dir): - build_metadata_args = ( - [ - "--build-metadata", - release_action_step["details"]["version"].split("+", maxsplit=1)[ - -1 - ], - ] - if len(release_action_step["details"]["version"].split("+", maxsplit=1)) - > 1 - else [] - ) - prerelease_args = ( - [ - "--as-prerelease", - "--prerelease-token", - ( - release_action_step["details"]["version"] - .split("-", maxsplit=1)[-1] - .split(".", maxsplit=1)[0] - ), - ] - if len(release_action_step["details"]["version"].split("-", maxsplit=1)) - > 1 - else [] + run_psr_release( + next_version_str=release_action_step["details"]["version"], + git_repo=mirror_git_repo, ) - cli_cmd = [ - MAIN_PROG_NAME, - "--strict", - VERSION_SUBCMD, - *build_metadata_args, - *prerelease_args, - ] - result = run_cli(cli_cmd[1:]) # take measurement after running the version command actual_release_commit_text = mirror_git_repo.head.commit.message @@ -177,7 +146,7 @@ def test_trunk_repo_rebuild_w_prereleases( ) # Evaluate (normal release actions should have occurred as expected) - assert_successful_exit_code(result, cli_cmd) + # ------------------------------------------------------------------ # Make sure version file is updated assert expected_pyproject_toml_content == actual_pyproject_toml_content assert expected_version_file_content == actual_version_file_content diff --git a/tests/e2e/conftest.py b/tests/e2e/conftest.py index 66aa8ab3d..b64d5aecf 100644 --- a/tests/e2e/conftest.py +++ b/tests/e2e/conftest.py @@ -137,11 +137,10 @@ def get_sanitized_rst_changelog_content( def _get_sanitized_rst_changelog_content( repo_dir: Path, - remove_insertion_flag: bool = True, + remove_insertion_flag: bool = False, ) -> str: - # TODO: v10 change -- default turns to update so this is not needed - # Because we are in init mode, the insertion flag is not present in the changelog - # we must take it out manually because our repo generation fixture includes it automatically + # Note that our repo generation fixture includes the insertion flag automatically + # toggle remove_insertion_flag to True to remove the insertion flag, applies to Init mode repos with (repo_dir / changelog_rst_file).open(newline=os.linesep) as rfd: # use os.linesep here because the insertion flag is os-specific # but convert the content to universal newlines for comparison @@ -169,11 +168,10 @@ def get_sanitized_md_changelog_content( ) -> GetSanitizedChangelogContentFn: def _get_sanitized_md_changelog_content( repo_dir: Path, - remove_insertion_flag: bool = True, + remove_insertion_flag: bool = False, ) -> str: - # TODO: v10 change -- default turns to update so this is not needed - # Because we are in init mode, the insertion flag is not present in the changelog - # we must take it out manually because our repo generation fixture includes it automatically + # Note that our repo generation fixture includes the insertion flag automatically + # toggle remove_insertion_flag to True to remove the insertion flag, applies to Init mode repos with (repo_dir / changelog_md_file).open(newline=os.linesep) as rfd: # use os.linesep here because the insertion flag is os-specific # but convert the content to universal newlines for comparison From 3fbde64a42f480607fc45236e55f72680e2c03e2 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 24 May 2025 22:29:16 -0600 Subject: [PATCH 143/160] chore(config): update semantic-release default branch regular expression --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7c1ae30c9..addaf110e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -436,7 +436,7 @@ mode = "update" template_dir = "config/release-templates" [tool.semantic_release.branches.main] -match = "(main|master)" +match = "^(main|master)$" prerelease = false prerelease_token = "rc" From a5f5e042ae9af909ee9e3ddf57c78adbc92ce378 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 24 May 2025 15:09:54 -0600 Subject: [PATCH 144/160] docs(upgrading): re-locate version upgrade guides into `Upgrading PSR` --- CHANGELOG.rst | 2 +- docs/index.rst | 2 +- .../08-upgrade.rst} | 74 +++++++++---------- docs/upgrading/09-upgrade.rst | 11 +++ docs/upgrading/index.rst | 26 +++++++ 5 files changed, 76 insertions(+), 39 deletions(-) rename docs/{migrating_from_v7.rst => upgrading/08-upgrade.rst} (92%) create mode 100644 docs/upgrading/09-upgrade.rst create mode 100644 docs/upgrading/index.rst diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e802155f2..5714ef3eb 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2310,7 +2310,7 @@ v8.0.0 (2023-07-16) 💥 BREAKING CHANGES -------------------- -* numerous breaking changes, see :ref:`migrating-from-v7` for more information +* numerous breaking changes, see :ref:`upgrade_v8` for more information .. _ec30564: https://github.com/python-semantic-release/python-semantic-release/commit/ec30564b4ec732c001d76d3c09ba033066d2b6fe .. _PR#619: https://github.com/python-semantic-release/python-semantic-release/pull/619 diff --git a/docs/index.rst b/docs/index.rst index f8273c5b7..b5ac6204c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -66,7 +66,7 @@ Documentation Contents troubleshooting contributing contributors - Migrating from Python Semantic Release v7 + upgrading/index Internal API Algorithm Changelog diff --git a/docs/migrating_from_v7.rst b/docs/upgrading/08-upgrade.rst similarity index 92% rename from docs/migrating_from_v7.rst rename to docs/upgrading/08-upgrade.rst index be4cbc14a..a6ce7a652 100644 --- a/docs/migrating_from_v7.rst +++ b/docs/upgrading/08-upgrade.rst @@ -1,9 +1,9 @@ -.. _migrating-from-v7: +.. _upgrade_v8: -Migrating from Python Semantic Release v7 -========================================= +Upgrading to v8 +=============== -Python Semantic Release 8.0.0 introduced a number of breaking changes. +Python Semantic Release v8.0.0 introduced a number of breaking changes. The internals have been changed significantly to better support highly-requested features and to streamline the maintenance of the project. @@ -12,18 +12,18 @@ exhibit different behavior to earlier versions of Python Semantic Release. This page is a guide to help projects to ``pip install python-semantic-release>=8.0.0`` with fewer surprises. -.. _breaking-github-action: +.. _upgrade_v8-github-action: Python Semantic Release GitHub Action ------------------------------------- -.. _breaking-removed-artefact-upload: +.. _upgrade_v8-removed-artefact-upload: GitHub Action no longer publishes artifacts to PyPI or GitHub Releases """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" Python Semantic Release no longer uploads distributions to PyPI - see -:ref:`breaking-commands-repurposed-version-and-publish`. If you are +:ref:`upgrade_v8-commands-repurposed-version-and-publish`. If you are using Python Semantic Release to publish release notes and artifacts to GitHub releases, there is a new GitHub Action `upload-to-gh-release`_ which will perform this action for you. @@ -111,7 +111,7 @@ GitHub Action: .. _upload-to-gh-release: https://github.com/python-semantic-release/upload-to-gh-release .. _pypa/gh-action-pypi-publish: https://github.com/pypa/gh-action-pypi-publish -.. _breaking-github-action-removed-pypi-token: +.. _upgrade_v8-github-action-removed-pypi-token: Removal of ``pypi_token``, ``repository_username`` and ``repository_password`` inputs """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" @@ -121,7 +121,7 @@ Since the library no longer supports publishing to PyPI, the ``pypi_token``, all been removed. See the above section for how to publish to PyPI using the official GitHub Action from the Python Packaging Authority (PyPA). -.. _breaking-options-inputs: +.. _upgrade_v8-options-inputs: Rename ``additional_options`` to ``root_options`` """"""""""""""""""""""""""""""""""""""""""""""""" @@ -132,12 +132,12 @@ reason, and because the usage of the CLI has changed, ``additional_options`` has been renamed to ``root_options`` to reflect the fact that the options are for the main :ref:`cmd-main` command group. -.. _breaking-commands: +.. _upgrade_v8-commands: Commands -------- -.. _breaking-commands-repurposed-version-and-publish: +.. _upgrade_v8-commands-repurposed-version-and-publish: Repurposing of ``version`` and ``publish`` commands """"""""""""""""""""""""""""""""""""""""""""""""""" @@ -189,7 +189,7 @@ With steps 1-6 being handled by the :ref:`cmd-version` command, step 7 being lef to the developer to handle, and lastly step 8 to be handled by the :ref:`cmd-publish` command. -.. _breaking-removed-define-option: +.. _upgrade_v8-removed-define-option: Removal of ``-D/--define`` command-line option """""""""""""""""""""""""""""""""""""""""""""" @@ -206,7 +206,7 @@ specify using just command-line options. .. _#600: https://github.com/python-semantic-release/python-semantic-release/issues/600 -.. _breaking-commands-no-verify-ci: +.. _upgrade_v8-commands-no-verify-ci: Removal of CI verifications """"""""""""""""""""""""""" @@ -230,7 +230,7 @@ shell commands *before* invoking ``semantic-release`` to verify your environment (e.g. via ``export RELEASE_BRANCH=main`` and/or replace the variable with the branch name you want to verify the CI environment for. -.. _breaking-commands-no-verify-ci-travis: +.. _upgrade_v8-commands-no-verify-ci-travis: Travis ~~~~~~ @@ -249,7 +249,7 @@ Travis fi -.. _breaking-commands-no-verify-ci-semaphore: +.. _upgrade_v8-commands-no-verify-ci-semaphore: Semaphore ~~~~~~~~~ @@ -269,7 +269,7 @@ Semaphore fi -.. _breaking-commands-no-verify-ci-frigg: +.. _upgrade_v8-commands-no-verify-ci-frigg: Frigg ~~~~~ @@ -287,7 +287,7 @@ Frigg exit 1 fi -.. _breaking-commands-no-verify-ci-circle-ci: +.. _upgrade_v8-commands-no-verify-ci-circle-ci: Circle CI ~~~~~~~~~ @@ -305,7 +305,7 @@ Circle CI exit 1 fi -.. _breaking-commands-no-verify-ci-gitlab-ci: +.. _upgrade_v8-commands-no-verify-ci-gitlab-ci: GitLab CI ~~~~~~~~~ @@ -320,7 +320,7 @@ GitLab CI exit 1 fi -.. _breaking-commands-no-verify-ci-bitbucket: +.. _upgrade_v8-commands-no-verify-ci-bitbucket: **Condition**: environment variable ``BITBUCKET_BUILD_NUMBER`` is set @@ -335,7 +335,7 @@ GitLab CI exit 1 fi -.. _breaking-commands-no-verify-ci-jenkins: +.. _upgrade_v8-commands-no-verify-ci-jenkins: Jenkins ~~~~~~~ @@ -359,7 +359,7 @@ Jenkins exit 1 fi -.. _breaking-removed-build-status-checking: +.. _upgrade_v8-removed-build-status-checking: Removal of Build Status Checking """""""""""""""""""""""""""""""" @@ -368,7 +368,7 @@ Prior to v8, Python Semantic Release contained a configuration option, ``check_build_status``, which would attempt to prevent a release being made if it was possible to identify that a corresponding build pipeline was failing. For similar reasons to those motivating the removal of -:ref:`CI Checks `, this feature has also been removed. +:ref:`CI Checks `, this feature has also been removed. If you are leveraging this feature in Python Semantic Release v7, the following bash commands will replace the functionality, and you can add these to your pipeline. @@ -386,7 +386,7 @@ installed, you can download it from `the curl website`_ .. _installation guide for jq: https://jqlang.github.io/jq/download/ .. _the curl website: https://curl.se/ -.. _breaking-removed-build-status-checking-github: +.. _upgrade_v8-removed-build-status-checking-github: GitHub ~~~~~~ @@ -407,7 +407,7 @@ GitHub Note that ``$GITHUB_API_DOMAIN`` is typically ``api.github.com`` unless you are using GitHub Enterprise with a custom domain name. -.. _breaking-removed-build-status-checking-gitea: +.. _upgrade_v8-removed-build-status-checking-gitea: Gitea ~~~~~ @@ -425,7 +425,7 @@ Gitea exit 1 fi -.. _breaking-removed-build-status-checking-gitlab: +.. _upgrade_v8-removed-build-status-checking-gitlab: Gitlab ~~~~~~ @@ -451,7 +451,7 @@ Gitlab done -.. _breaking-commands-multibranch-releases: +.. _upgrade_v8-commands-multibranch-releases: Multibranch releases """""""""""""""""""" @@ -462,7 +462,7 @@ has been changed - you must manually check out the branch which you would like t against, and if you would like to create releases against this branch you must also ensure that it belongs to a :ref:`release group `. -.. _breaking-commands-changelog: +.. _upgrade_v8-commands-changelog: ``changelog`` command """"""""""""""""""""" @@ -477,7 +477,7 @@ tag ``v1.1.4``, you should run:: semantic-release changelog --post-to-release-tag v1.1.4 -.. _breaking-changelog-customization: +.. _upgrade_v8-changelog-customization: Changelog customization """"""""""""""""""""""" @@ -492,7 +492,7 @@ fully open up customizing the changelog's appearance. .. _Jinja: https://jinja.palletsprojects.com/en/3.1.x/ -.. _breaking-configuration: +.. _upgrade_v8-configuration: Configuration ------------- @@ -501,7 +501,7 @@ The configuration structure has been completely reworked, so you should read :ref:`configuration` carefully during the process of upgrading to v8+. However, some common pitfalls and potential sources of confusion are summarized here. -.. _breaking-configuration-setup-cfg-unsupported: +.. _upgrade_v8-configuration-setup-cfg-unsupported: ``setup.cfg`` is no longer supported """""""""""""""""""""""""""""""""""" @@ -532,7 +532,7 @@ needs. .. _pip issue: https://github.com/pypa/pip/issues/8437#issuecomment-805313362 -.. _breaking-commit-parser-options: +.. _upgrade_v8-commit-parser-options: Commit parser options """"""""""""""""""""" @@ -547,7 +547,7 @@ and if you need to parse multiple commit styles for a single project it's recomm that you create a parser following :ref:`commit_parser-custom_parser` that is tailored to the specific needs of your project. -.. _breaking-version-variable-rename: +.. _upgrade_v8-version-variable-rename: ``version_variable`` """""""""""""""""""" @@ -555,7 +555,7 @@ is tailored to the specific needs of your project. This option has been renamed to :ref:`version_variables ` as it refers to a list of variables which can be updated. -.. _breaking-version-pattern-removed: +.. _upgrade_v8-version-pattern-removed: ``version_pattern`` """"""""""""""""""" @@ -567,7 +567,7 @@ for a project and store this in an environment variable like so:: export VERSION=$(semantic-release version --print) -.. _breaking-version-toml-type: +.. _upgrade_v8-version-toml-type: ``version_toml`` """""""""""""""" @@ -588,7 +588,7 @@ simply wrap the value in ``[]``: version_toml = ["pyproject.toml:tool.poetry.version"] -.. _breaking-tag-format-validation: +.. _upgrade_v8-tag-format-validation: ``tag_format`` """""""""""""" @@ -597,7 +597,7 @@ This option has the same effect as it did in Python Semantic Release prior to v8 but Python Semantic Release will now verify that it has a ``{version}`` format key and raise an error if this is not the case. -.. _breaking-upload-to-release-rename: +.. _upgrade_v8-upload-to-release-rename: ``upload_to_release`` """"""""""""""""""""" @@ -605,7 +605,7 @@ key and raise an error if this is not the case. This option has been renamed to :ref:`upload_to_vcs_release `. -.. _breaking-custom-commit-parsers: +.. _upgrade_v8-custom-commit-parsers: Custom Commit Parsers --------------------- diff --git a/docs/upgrading/09-upgrade.rst b/docs/upgrading/09-upgrade.rst new file mode 100644 index 000000000..10c0d76e9 --- /dev/null +++ b/docs/upgrading/09-upgrade.rst @@ -0,0 +1,11 @@ +.. _upgrade_v9: + +Upgrading to v9 +=============== + +You are in luck! The upgrade to ``v9`` is a simple one. + +The breaking change for this version is the removal of support for **Python 3.7**, as it has passed +End-Of-Life (EOL). This means that if you are using Python 3.7, you will need to upgrade +to at least Python 3.8 in order to use ``v9``. This will be permanent as all future versions of +``python-semantic-release`` will require Python 3.8 or later. diff --git a/docs/upgrading/index.rst b/docs/upgrading/index.rst new file mode 100644 index 000000000..6b7bcbdd6 --- /dev/null +++ b/docs/upgrading/index.rst @@ -0,0 +1,26 @@ +.. _upgrading: + +============= +Upgrading PSR +============= + +Upgrading PSR is a process that may involve several steps, depending on the version you +are upgrading from and to. This section provides a guide for upgrading from older +versions of PSR to the latest version. + +.. important:: + If you are upgrading across **more than one** major version, you should incrementally + upgrade through each major version and its configuration update guide to ensure a + smooth transition. + + For example, if you are upgrading from v7 to v10, you should first + upgrade to v8 and then to v9, and then lastly to v10 while following the upgrade + guide for each version. At each step you should confirm execution works as expected + before proceeding to the next version. + +.. toctree:: + :caption: Upgrade Guides + :maxdepth: 1 + + Upgrading to v9 <09-upgrade> + Upgrading to v8 <08-upgrade> From 6cd0fbeb44e16d394c210216c7099afa51f5a4a3 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 24 May 2025 15:12:09 -0600 Subject: [PATCH 145/160] docs(algorithm): remove out-of-date algorithm description --- docs/algorithm.rst | 205 --------------------------------------------- docs/index.rst | 1 - 2 files changed, 206 deletions(-) delete mode 100644 docs/algorithm.rst diff --git a/docs/algorithm.rst b/docs/algorithm.rst deleted file mode 100644 index 4ea9fa6dd..000000000 --- a/docs/algorithm.rst +++ /dev/null @@ -1,205 +0,0 @@ -.. _algorithm: - -Python Semantic Release's Version Bumping Algorithm -=================================================== - -Below is a technical description of the algorithm which Python Semantic Release -uses to calculate a new version for a project. - -.. _algorithm-assumptions: - -Assumptions -~~~~~~~~~~~ - -* At runtime, we are in a Git repository with HEAD referring to a commit on - some branch of the repository (i.e. not in detached HEAD state). -* We know in advance whether we want to produce a prerelease or not (based on - the configuration and command-line flags). -* We can parse the tags of the repository into semantic versions, as we are given - the format that those Git tags should follow via configuration, but cannot - cherry-pick only tags that apply to commits on specific branches. We must parse - all tags in order to ensure we have parsed any that might apply to commits in - this branch's history. -* If we can identify a commit as a ``merge-base`` between our HEAD commit and one - or more tags, then that merge-base should be unique. -* We know ahead of time what ``prerelease_token`` to use for prereleases - e.g. - ``rc``. -* We know ahead of time whether ``major`` changes introduced by commits - should cause the new version to remain on ``0.y.z`` if the project is already - on a ``0.`` version - see :ref:`major_on_zero `. - -.. _algorithm-implementation: - -Implementation -~~~~~~~~~~~~~~ - -1. Parse all the Git tags of the repository into semantic versions, and **sort** - in descending (most recent first) order according to `semver precedence`_. - Ignore any tags which do not correspond to valid semantic versions according - to ``tag_format``. - - -2. Find the ``merge-base`` of HEAD and the latest tag according to the sort above. - Call this commit ``M``. - If there are no tags in the repo's history, we set ``M=HEAD``. - -3. Find the latest non-prerelease version whose tag references a commit that is - an ancestor of ``M``. We do this via a breadth-first search through the commit - lineage, starting against ``M``, and for each tag checking if the tag - corresponds to that commit. We break from the search when we find such a tag. - If no such tag is found, see 4a). - Else, suppose that tag corresponds to a commit ``L`` - goto 4b). - -4. - a. If no commit corresponding to the last non-prerelease version is found, - the entire history of the repository is considered. We parse every commit - that is an ancestor of HEAD to determine the type of change introduced - - either ``major``, ``minor``, ``patch``, ``prerelease_revision`` or - ``no_release``. We store this levels in a ``set`` as we only require - the distinct types of change that were introduced. - b. However, if we found a commit ``L`` which is the commit against which the - last non-prerelease was tagged, then we parse only the commits from HEAD - as far back as ``L``, to understand what changes have been introduced - since the previous non-prerelease. We store these levels - either - ``major``, ``minor``, ``patch``, ``prerelease_revision``, or - ``no_release``, in a set, as we only require the distinct types of change - that were introduced. - - c. We look for tags that correspond to each commit during this process, to - identify the latest pre-release that was made within HEAD's ancestry. - -5. If there have been no changes since the last non-prerelease, or all commits - since that release result in a ``no_release`` type according to the commit - parser, then we **terminate the algorithm.** - -6. If we have not exited by this point, we know the following information: - - * The latest version, by `semver precedence`_, within the whole repository. - Call this ``LV``. This might not be within the ancestry of HEAD. - * The latest version, prerelease or non-prerelease, within the whole repository. - Call this ``LVH``. This might not be within the ancestry of HEAD. - This may be the same as ``LV``. - * The latest non-prerelease version within the ancestry of HEAD. Call this - ``LVHF``. This may be the same as ``LVH``. - * The most significant type of change introduced by the commits since the - previous full release. Call this ``level`` - * Whether or not we wish to produce a prerelease from this version increment. - Call this a boolean flag, ``prerelease``. (Assumption) - * Whether or not to increment the major digit if a major change is introduced - against an existing ``0.`` version. Call this ``major_on_zero``, a boolean - flag. (Assumption) - - Using this information, the new version is decided according to the following - criteria: - - a. If ``LV`` has a major digit of ``0``, ``major_on_zero`` is ``False`` and - ``level`` is ``major``, reduce ``level`` to ``minor``. - - b. If ``prerelease=True``, then - - i. Diff ``LV`` with ``LVHF``, to understand if the ``major``, ``minor`` or - ``patch`` digits have changed. For example, diffing ``1.2.1`` and - ``1.2.0`` is a ``patch`` diff, while diffing ``2.1.1`` and ``1.17.2`` is - a ``major`` diff. Call this ``DIFF`` - - ii. If ``DIFF`` is less semantically significant than ``level``, for example - if ``DIFF=patch`` and ``level=minor``, then - - 1. Increment the digit of ``LVF`` corresponding to ``level``, for example - the minor digit if ``level=minor``, setting all less significant - digits to zero. - - 2. Add ``prerelease_token`` as a suffix result of 1., together with a - prerelease revision number of ``1``. Return this new version and - **terminate the algorithm.** - - Thus if ``DIFF=patch``, ``level=minor``, ``prerelease=True``, - ``prerelease_token="rc"``, and ``LVF=1.1.1``, - then the version returned by the algorithm is ``1.2.0-rc.1``. - - iii. If ``DIFF`` is semantically less significant than or equally - significant to ``level``, then this means that the significance - of change introduced by ``level`` is already reflected in a - prerelease version that has been created since the last full release. - For example, if ``LVHF=1.1.1``, ``LV=1.2.0-rc.1`` and ``level=minor``. - - In this case we: - - 1. If the prerelease token of ``LV`` is different from - ``prerelease_token``, take the major, minor and patch digits - of ``LV`` and construct a prerelease version using our given - ``prerelease_token`` and a prerelease revision of ``1``. We - then return this version and **terminate the algorithm.** - - For example, if ``LV=1.2.0-rc.1`` and ``prerelease_token=alpha``, - we return ``1.2.0-alpha.1``. - - 2. If the prerelease token of ``LV`` is the same as ``prerelease_token``, - we increment the revision number of ``LV``, return this version, and - - **terminate the algorithm.** - For example, if ``LV=1.2.0-rc.1`` and ``prerelease_token=rc``, - we return ``1.2.0-rc.2``. - - c. If ``prerelease=False``, then - - i. If ``LV`` is not a prerelease, then we increment the digit of ``LV`` - corresponding to ``level``, for example the minor digit if ``level=minor``, - setting all less significant digits to zero. - We return the result of this and **terminate the algorithm**. - - ii. If ``LV`` is a prerelease, then: - - 1. Diff ``LV`` with ``LVHF``, to understand if the ``major``, ``minor`` or - ``patch`` digits have changed. Call this ``DIFF`` - - 2. If ``DIFF`` is less semantically significant than ``level``, then - - i. Increment the digit of ``LV`` corresponding to ``level``, for example - the minor digit if ``level=minor``, setting all less significant - digits to zero. - - ii. Remove the prerelease token and revision number from the result of i., - ("Finalize" the result of i.) return the result and **terminate the - algorithm.** - - For example, if ``LV=1.2.2-alpha.1`` and ``level=minor``, we return - ``1.3.0``. - - 3. If ``DIFF`` is semantically less significant than or equally - significant to ``level``, then we finalize ``LV``, return the - result and **terminate the algorithm**. - -.. _semver precedence: https://semver.org/#spec-item-11 - -.. _algorithm-complexity: - -Complexity -~~~~~~~~~~ - -**Space:** - -A list of parsed tags takes ``O(number of tags)`` in space. Parsing each commit during -the breadth-first search between ``merge-base`` and the latest tag in the ancestry -of HEAD takes at worst ``O(number of commits)`` in space to track visited commits. -Therefore worst-case space complexity will be linear in the number of commits in the -repo, unless the number of tags significantly exceeds the number of commits -(in which case it will be linear in the number of tags). - -**Time:** - -Assuming using regular expression parsing of each tag is a constant-time operation, -then the following steps contribute to the time complexity of the algorithm: - -* Parsing each tag - ``O(number of tags)`` -* Sorting tags by `semver precedence`_ - - ``O(number of tags * log(number of tags))`` -* Finding the merge-base of HEAD and the latest release tag - - ``O(number of commits)`` (worst case) -* Parsing each commit and checking each tag against each commit - - ``O(number of commits) + O(number of tags * number of commits)`` - (worst case) - -Overall, assuming that the number of tags is less than or equal to the number -of commits in the repository, this would lead to a worst-case time complexity -that's quadratic in the number of commits in the repo. diff --git a/docs/index.rst b/docs/index.rst index b5ac6204c..f0c76e516 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -68,7 +68,6 @@ Documentation Contents contributors upgrading/index Internal API - Algorithm Changelog View on GitHub From 6aad7f17e64fb4717ddd7a9e94d2a730be6a3bd9 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 24 May 2025 15:14:10 -0600 Subject: [PATCH 146/160] docs(github-actions): add reference to manual release workflow example --- docs/automatic-releases/github-actions.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/automatic-releases/github-actions.rst b/docs/automatic-releases/github-actions.rst index 621a13067..94aafaf62 100644 --- a/docs/automatic-releases/github-actions.rst +++ b/docs/automatic-releases/github-actions.rst @@ -986,6 +986,15 @@ The equivalent GitHub Action configuration would be: changelog: false build_metadata: abc123 +.. seealso:: + + - `Publish Action Manual Release Workflow`_: To maintain the Publish Action at the same + version as Python Semantic Release, we use a Manual release workflow which forces the + matching bump type as the root project. Check out this workflow to see how you can + manually provide input that triggers the desired version bump. + +.. _Publish Action Manual Release Workflow: https://github.com/python-semantic-release/publish-action/blob/main/.github/workflows/release.yml + .. _gh_actions-monorepo: Actions with Monorepos From 8bed5bcca4a5759af0e3fb24eadf14aa4e4f53c9 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 24 May 2025 15:40:10 -0600 Subject: [PATCH 147/160] docs(contributing): refactor contributing & contributors layout --- AUTHORS.rst | 7 ------- CONTRIBUTING.rst | 4 +++- docs/commands.rst | 7 ++++--- docs/contributing.rst | 1 - docs/contributing/contributing.rst | 1 + docs/contributing/index.rst | 28 ++++++++++++++++++++++++++++ docs/contributors.rst | 1 - docs/index.rst | 3 +-- 8 files changed, 37 insertions(+), 15 deletions(-) delete mode 100644 AUTHORS.rst delete mode 100644 docs/contributing.rst create mode 100644 docs/contributing/contributing.rst create mode 100644 docs/contributing/index.rst delete mode 100644 docs/contributors.rst diff --git a/AUTHORS.rst b/AUTHORS.rst deleted file mode 100644 index 0a7309b92..000000000 --- a/AUTHORS.rst +++ /dev/null @@ -1,7 +0,0 @@ -Contributors ------------- - -|contributors| - -.. |contributors| image:: https://contributors-img.web.app/image?repo=relekang/python-semantic-release - :target: https://github.com/relekang/python-semantic-release/graphs/contributors diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index e546295cf..bb728da1d 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -1,3 +1,5 @@ +.. _contributing_guide: + Contributing ------------ @@ -7,7 +9,7 @@ Please remember to write tests for the cool things you create or fix. Unsure about something? No worries, `open an issue`_. -.. _open an issue: https://github.com/relekang/python-semantic-release/issues/new +.. _open an issue: https://github.com/python-semantic-release/python-semantic-release/issues/new Commit messages ~~~~~~~~~~~~~~~ diff --git a/docs/commands.rst b/docs/commands.rst index e7e8e5f21..30234e9e7 100644 --- a/docs/commands.rst +++ b/docs/commands.rst @@ -23,10 +23,11 @@ Correct:: semantic-release -vv --noop version --print With the exception of :ref:`cmd-main` and :ref:`cmd-generate-config`, all -commands require that you have set up your project's configuration. To help with -this step, :ref:`cmd-generate-config` can create the default configuration for you, -which will allow you to tweak it to your needs rather than write it from scratch. +commands require that you have set up your project's configuration. +To help with setting up your project configuration, :ref:`cmd-generate-config` +will print out the default configuration to the console, which +you can then modify it to match your project & environment. .. _cmd-main: diff --git a/docs/contributing.rst b/docs/contributing.rst deleted file mode 100644 index e582053ea..000000000 --- a/docs/contributing.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../CONTRIBUTING.rst diff --git a/docs/contributing/contributing.rst b/docs/contributing/contributing.rst new file mode 100644 index 000000000..ac7b6bcf3 --- /dev/null +++ b/docs/contributing/contributing.rst @@ -0,0 +1 @@ +.. include:: ../../CONTRIBUTING.rst diff --git a/docs/contributing/index.rst b/docs/contributing/index.rst new file mode 100644 index 000000000..51d8945c2 --- /dev/null +++ b/docs/contributing/index.rst @@ -0,0 +1,28 @@ +.. _contributing: + +Contributing +============ + +Love Python Semantic Release? Want to help out? There are many ways you can contribute to the project! + +You can help by: + +- Reporting bugs and issues +- Suggesting new features +- Improving the documentation +- Reviewing pull requests +- Contributing code +- Helping with translations +- Spreading the word about Python Semantic Release +- Participating in discussions +- Testing new features and providing feedback + +No matter how you choose to contribute, please check out our +:ref:`Contributing Guidelines ` and know we appreciate your help! + +**Check out all the folks whom already contributed to Python Semantic Release and become one of them today!** + +|contributors| + +.. |contributors| image:: https://contributors-img.web.app/image?repo=python-semantic-release/python-semantic-release + :target: https://github.com/python-semantic-release/python-semantic-release/graphs/contributors diff --git a/docs/contributors.rst b/docs/contributors.rst deleted file mode 100644 index e122f914a..000000000 --- a/docs/contributors.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../AUTHORS.rst diff --git a/docs/index.rst b/docs/index.rst index f0c76e516..515885a39 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -64,11 +64,10 @@ Documentation Contents Multibranch Releases automatic-releases/index troubleshooting - contributing - contributors upgrading/index Internal API Changelog + Contributing View on GitHub Getting Started From 4ea92ec34dcd45d8cbab24e38e55289617b2d728 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 24 May 2025 15:45:17 -0600 Subject: [PATCH 148/160] docs(upgrading-v10): added migration guide for v9 to v10 --- docs/upgrading/10-upgrade.rst | 189 ++++++++++++++++++++++++++++++++++ docs/upgrading/index.rst | 1 + 2 files changed, 190 insertions(+) create mode 100644 docs/upgrading/10-upgrade.rst diff --git a/docs/upgrading/10-upgrade.rst b/docs/upgrading/10-upgrade.rst new file mode 100644 index 000000000..ffd6b0276 --- /dev/null +++ b/docs/upgrading/10-upgrade.rst @@ -0,0 +1,189 @@ +.. _upgrade_v10: + +Upgrading to v10 +================ + +The upgrade to v10 is primarily motivated by a command injection security vulnerability +found in the GitHub Actions configuration interpreter (see details +:ref:`below `). We also bundled a number of other changes, +including new default configuration values and most importantly, a return to 1-line +commit subjects in the default changelog format. + +For more specific change details for v10, please refer to the :ref:`changelog-v10.0.0` +section of the :ref:`changelog`. + + +.. _upgrade_v10-root_options: + +Security Fix: Command Injection Vulnerability (GitHub Actions) +-------------------------------------------------------------- + +In the previous versions of the GitHub Actions configuration, we used a single +``root_options`` parameter to pass any options you wanted to pass to the +``semantic-release`` main command. This parameter was interpreted as a string and +passed directly to the command line, which made it vulnerable to command injection +attacks. An attacker could exploit this by crafting a malicious string as the +:ref:`gh_actions-psr-inputs-root_options` input, and then it would be executed +as part of the command line, potentially allowing them to run arbitrary commands within +the GitHub Actions Docker container. The ability to exploit this vulnerability is limited +to people whom can modify the GitHub Actions workflow file, which is typically only the +repository maintainers unless you are pointing at an organizational workflow file or +another third-party workflow file. + +To mitigate this vulnerability, we have removed the ``root_options`` parameter completely +and replaced it with individual boolean flag inputs which are then used to select the proper +cli parameters for the ``semantic-release`` command. Additionally, users can protect themselves +by limiting the access to secrets in their GitHub Actions workflows and the permissions of +the GitHub Actions CI TOKEN. + +This vulnerability existed in both the +:ref:`python-semantic-release/python-semantic-release ` and +:ref:`python-semantic-release/publish-action ` actions. + +For the main :ref:`python-semantic-release/python-semantic-release ` action, +the following inputs are now available (in place of the old ``root_options`` parameter): + +- :ref:`gh_actions-psr-inputs-config_file` +- :ref:`gh_actions-psr-inputs-noop` +- :ref:`gh_actions-psr-inputs-strict` +- :ref:`gh_actions-psr-inputs-verbosity` + +For the :ref:`python-semantic-release/publish-action ` action, +the following inputs are now available (in place of the old ``root_options`` parameter): + +- :ref:`gh_actions-publish-inputs-config_file` +- :ref:`gh_actions-publish-inputs-noop` +- :ref:`gh_actions-publish-inputs-verbosity` + + +.. _upgrade_v10-changelog_format-1_line_commit_subjects: + +Changelog Format: 1-Line Commit Subjects +---------------------------------------- + +In v10, the default changelog format has been changed to use 1-line commit subjects instead of +including the full commit message. This change was made to improve the readability of the changelog +as many commit messages are long and contain unnecessary details for the changelog. + +.. important:: + If you use a squash commit merge strategy, it is recommended that you use the default + ``parse_squash_commits`` commit parser option to ensure that all the squashed commits are + parsed for version bumping and changelog generation. This is the default behavior in v10 across + all supported commit parsers. If you are upgrading, you likely will need to manually set this + option in your configuration file to ensure that the changelog is generated correctly. + + If you do not enable ``parse_squash_commits``, then version will only be determined by the + commit subject line and the changelog will only include the commit subject line as well. + + +.. _upgrade_v10-changelog_format-mask_initial_release: + +Changelog Format: Mask Initial Release +-------------------------------------- + +In v10, the default behavior for the changelog generation has been changed to mask the initial +release in the changelog. This means that the first release will not contain a break down of the +different types of changes (e.g., features, fixes, etc.), but instead it will just simply state +that this is the initial release. + + +.. _upgrade_v10-changelog_format-commit_parsing: + +Changelog Format: Commit Parsing +-------------------------------- + +We have made some minor changes to the commit parsing logic in *v10* to +separate out components of the commit message more clearly. You will find that the +:py:class:`ParsedCommit ` object's +descriptions list will no longer contain any Breaking Change footers, Release Notice footers, +PR/MR references, or Issue Closure footers. These were all previously extracted and placed +into their own attributes but were still included in the descriptions list. In *v10*, +the descriptions list will only contain the actual commit subject line and any additional +commit body text that is not part of the pre-defined footers. + +If you were relying on the descriptions list to contain these footers, you will need to +update your code and changelog templates to reference the specific attributes you want to use. + + +.. _upgrade_v10-default_config: + +Default Configuration Changes +----------------------------- + +The following table summarizes the changes to the default configuration values in v10: + +.. list-table:: + :widths: 5 55 20 20 + :header-rows: 1 + + * - # + - Configuration Option + - Previous Default Value + - New Default Value + + * - 1 + - :ref:`config-allow_zero_version` + - ``true`` + - ``false`` + + * - 2 + - :ref:`changelog.mode ` + - ``init`` + - ``update`` + + * - 3 + - :ref:`changelog.default_templates.mask_initial_release ` + - ``false`` + - ``true`` + + * - 4 + - :ref:`commit_parser_options.parse_squash_commits ` + - ``false`` + - ``true`` + + * - 5 + - :ref:`commit_parser_options.ignore_merge_commits ` + - ``false`` + - ``true`` + + +.. _upgrade_v10-deprecations: + +Deprecations & Removals +----------------------- + +No additional deprecations were made in *v10*, but the following are staged +for removal in v11: + +.. list-table:: Deprecated Features & Functions + :widths: 5 30 10 10 45 + :header-rows: 1 + + * - # + - Component + - Deprecated + - Planned Removal + - Notes + + * - 1 + - :ref:`GitHub Actions root_options ` + - v10.0.0 + - v10.0.0 + - Replaced with individual boolean flag inputs. See :ref:`above ` for details. + + * - 2 + - :ref:`Angular Commit Parser ` + - v9.19.0 + - v11.0.0 + - Replaced by the :ref:`Conventional Commit Parser `. + + * - 3 + - :ref:`Tag Commit Parser ` + - v9.12.0 + - v11.0.0 + - Replaced by the :ref:`Emoji Commit Parser `. + +.. note:: + For the most up-to-date information on the next version deprecations and removals, please + refer to the issue + `#1066 `_. diff --git a/docs/upgrading/index.rst b/docs/upgrading/index.rst index 6b7bcbdd6..4925d807e 100644 --- a/docs/upgrading/index.rst +++ b/docs/upgrading/index.rst @@ -22,5 +22,6 @@ versions of PSR to the latest version. :caption: Upgrade Guides :maxdepth: 1 + Upgrading to v10 <10-upgrade> Upgrading to v9 <09-upgrade> Upgrading to v8 <08-upgrade> From 4e52f4bba46e96a4762f97d306f15ae52c5cea1b Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 24 May 2025 15:49:30 -0600 Subject: [PATCH 149/160] docs: refactor documentation page navigation --- .gitignore | 2 +- docs/{ => api}/commands.rst | 4 +- docs/{ => concepts}/changelog_templates.rst | 2 +- docs/{ => concepts}/commit_parsing.rst | 4 +- docs/concepts/getting_started.rst | 391 ++++++++++++++++++ docs/concepts/index.rst | 17 + docs/concepts/installation.rst | 14 + docs/{ => concepts}/multibranch_releases.rst | 0 docs/{ => concepts}/strict_mode.rst | 0 docs/conf.py | 6 +- .../automatic-releases/cronjobs.rst | 4 +- .../automatic-releases/github-actions.rst | 0 .../automatic-releases/index.rst | 4 +- .../automatic-releases/travis.rst | 6 +- docs/{ => configuration}/configuration.rst | 4 +- docs/configuration/index.rst | 18 + ...ontributing.rst => contributing_guide.rst} | 0 docs/contributing/index.rst | 7 + docs/index.rst | 263 ++---------- docs/misc/psr_changelog.rst | 1 + docs/{ => misc}/troubleshooting.rst | 0 docs/psr_changelog.rst | 1 - 22 files changed, 513 insertions(+), 235 deletions(-) rename docs/{ => api}/commands.rst (99%) rename docs/{ => concepts}/changelog_templates.rst (99%) rename docs/{ => concepts}/commit_parsing.rst (99%) create mode 100644 docs/concepts/getting_started.rst create mode 100644 docs/concepts/index.rst create mode 100644 docs/concepts/installation.rst rename docs/{ => concepts}/multibranch_releases.rst (100%) rename docs/{ => concepts}/strict_mode.rst (100%) rename docs/{ => configuration}/automatic-releases/cronjobs.rst (96%) rename docs/{ => configuration}/automatic-releases/github-actions.rst (100%) rename docs/{ => configuration}/automatic-releases/index.rst (88%) rename docs/{ => configuration}/automatic-releases/travis.rst (95%) rename docs/{ => configuration}/configuration.rst (99%) create mode 100644 docs/configuration/index.rst rename docs/contributing/{contributing.rst => contributing_guide.rst} (100%) create mode 100644 docs/misc/psr_changelog.rst rename docs/{ => misc}/troubleshooting.rst (100%) delete mode 100644 docs/psr_changelog.rst diff --git a/.gitignore b/.gitignore index 7b02cda5b..db2c6f98d 100644 --- a/.gitignore +++ b/.gitignore @@ -61,7 +61,7 @@ coverage.xml # Sphinx documentation docs/_build/ -docs/api/ +docs/api/modules/ # PyBuilder target/ diff --git a/docs/commands.rst b/docs/api/commands.rst similarity index 99% rename from docs/commands.rst rename to docs/api/commands.rst index 30234e9e7..d99a40152 100644 --- a/docs/commands.rst +++ b/docs/api/commands.rst @@ -1,7 +1,7 @@ .. _commands: -Commands -======== +Command Line Interface (CLI) +============================ All commands accept a ``-h/--help`` option, which displays the help text for the command and exits immediately. diff --git a/docs/changelog_templates.rst b/docs/concepts/changelog_templates.rst similarity index 99% rename from docs/changelog_templates.rst rename to docs/concepts/changelog_templates.rst index e950db3d1..d42a210d7 100644 --- a/docs/changelog_templates.rst +++ b/docs/concepts/changelog_templates.rst @@ -248,7 +248,7 @@ Configuration Examples If identified or supported by the parser, the default changelog templates will include a separate section of breaking changes and additional release information. Refer to the -:ref:`commit parsing ` section to see how to write commit messages that +:ref:`commit parsing ` section to see how to write commit messages that will be properly parsed and displayed in these sections. diff --git a/docs/commit_parsing.rst b/docs/concepts/commit_parsing.rst similarity index 99% rename from docs/commit_parsing.rst rename to docs/concepts/commit_parsing.rst index 16340abeb..163927c39 100644 --- a/docs/commit_parsing.rst +++ b/docs/concepts/commit_parsing.rst @@ -1,4 +1,4 @@ -.. _commit-parsing: +.. _commit_parsing: Commit Parsing ============== @@ -641,7 +641,7 @@ should inherit from the The "options" class is used to validate the options which are configured in the repository, and to provide default values for these options where appropriate. -.. _commit-parsing-commit-parsers: +.. _commit_parsing-commit-parsers: Commit Parsers """""""""""""" diff --git a/docs/concepts/getting_started.rst b/docs/concepts/getting_started.rst new file mode 100644 index 000000000..63007948e --- /dev/null +++ b/docs/concepts/getting_started.rst @@ -0,0 +1,391 @@ +.. _getting-started-guide: + +Getting Started +=============== + +If you haven't done so already, install Python Semantic Release locally following the +:ref:`installation instructions `. + +If you are using a CI/CD service, you may not have to add Python Semantic Release to your +project's dependencies permanently, but for the duration of this guide for the initial +setup, you will need to have it installed locally. + + +Configuring PSR +--------------- + +Python Semantic Release ships with a reasonable default configuration but some aspects **MUST** be +customized to your project. To view the default configuration, run the following command: + +.. code-block:: bash + + semantic-release generate-config + +The output of the above command is the default configuration in TOML format without any modifications. +If this is fine for your project, then you do not need to configure anything else. + +PSR accepts overrides to the default configuration keys individually. If you don't define the +key-value pair in your configuration file, the default value will be used. + +By default, Python Semantic Release will look for configuration overrides in ``pyproject.toml`` under +the TOML table ``[tool.semantic_release]``. You may specify a different file using the +``-c/--config`` option, for example: + +.. code-block:: bash + + # In TOML format with top level table [semantic_release] + semantic-release -c releaserc.toml + + # In JSON format with top level object key {"semantic_release": {}} + semantic-release -c releaserc.json + +The easiest way to get started is to output the default configuration to a file, +delete keys you do not need to override, and then edit the remaining keys to suit your project. + +To set up in ``pyproject.toml``, run the following command: + +.. code-block:: bash + + # In file redirect in bash + semantic-release generate-config --pyproject >> pyproject.toml + + # Open your editor to edit the configuration + vim pyproject.toml + +.. seealso:: + - :ref:`cmd-generate-config` + - :ref:`configuration` + + +Configuring the Version Stamp Feature +''''''''''''''''''''''''''''''''''''' + +One of the best features of Python Semantic Release is the ability to automatically stamp the +new version number into your project files, so you don't have to manually update the version upon +each release. The version that is stamped is automatically determined by Python Semantic Release +from your commit messages which compliments automated versioning seamlessly. + +The most crucial version stamp is the one in your project metadata, which is used by +the Python Package Index (PyPI) and other package managers to identify the version of your package. + +For Python projects, this is typically the ``version`` field in your ``pyproject.toml`` file. First, +set up your project metadata with the base ``version`` value. If you are starting with a brand new project, +set ``project.version = "0.0.0"``. If you are working on an existing project, set it to the last +version number you released. Do not include any ``v`` prefix. + +.. important:: + The version number must be a valid SemVer version, which means it should follow the format + ``MAJOR.MINOR.PATCH`` (e.g., ``1.0.0``). Python Semantic Release does NOT support Canonical + version values defined in the `PEP 440`_ specification at this time. See + `Issue #455 `_ + for more details. Note that you can still define a SemVer version in the ``project.version`` + field, and when your build is generated, the build tool will automatically generate a PEP 440 + compliant version as long as you do **NOT** use a non-pep440 compliant pre-release token. + +.. _PEP 440: https://peps.python.org/pep-0440/ + +Your project metadata might look like this in ``pyproject.toml``:: + + [project] + name = "my-package" + version = "0.0.0" # Set this to the last released version or "0.0.0" for new projects + description = "A sample Python package" + +To configure PSR to automatically update this version number, you need to specify the file and value +to update in your configuration. Since ``pyproject.toml`` uses TOML format, you will add the +replacement specification to the ``tool.semantic_release.version_toml`` list. Update the following +configuration in your ``pyproject.toml`` file to include the version variable location: + +.. code-block:: toml + + [tool.semantic_release] + version_toml = ["pyproject.toml:project.version"] + + # Alternatively, if you are using poetry's 'version' key, then you would use: + version_toml = ["pyproject.toml:tool.poetry.version"] + +If you have other TOML files where you want to stamp the version, you can add them to the +``version_toml`` list as well. In the above example, there is an implicit assumption that +you only want the version as the raw number format. If you want to specify the full tag +value (e.g. v-prefixed version), then include ``:tf`` for "tag format" at the end of the +version variable specification. + +For non-TOML formatted files (such as JSON or YAML files), you can use the +:ref:`config-version_variables` configuration key instead. This feature uses an advanced +Regular Expression to find and replace the version variable in the specified files. + +For Python files, its much more effective to use ``importlib`` instead which will allow you to +dynamically import the version from your package metadata and not require your project to commit +the version number bump to the repository. For example, in your package's base ``__init__.py`` + +.. code-block:: python + + # my_package/__init__.py + from importlib.metadata import version as get_version + + __version__ = get_version(__package__) + # Note: __package__ must match your 'project.name' as defined in pyproject.toml + +.. seealso:: + - Configuration specification of :ref:`config-version_toml` + - Configuration specification of :ref:`config-version_variables` + + +Using PSR to Build your Project +''''''''''''''''''''''''''''''' + +PSR provides a convenient way to build your project artifacts as part of the versioning process +now that you have stamped the version into your project files. To enable this, you will need +to specify the build command in your configuration. This command will be executed after +the next version has been determined, and stamped into your files but before a release tag has +been created. + +To set up the build command, add the following to your ``pyproject.toml`` file: + +.. code-block:: toml + + [tool.semantic_release] + build_command = "python -m build --sdist --wheel ." + +.. seealso:: + - :ref:`config-build_command` - Configuration specification for the build command. + - :ref:`config-build_command_env` - Configuration specification for the build command environment variables. + + +Choosing a Commit Message Parser +'''''''''''''''''''''''''''''''' + +PSR uses commit messages to determine the type of version bump that should be applied +to your project. PSR supports multiple commit message parsing styles, allowing you to choose +the one that best fits your project's needs. Choose **one** of the supported commit parsers +defined in :ref:`commit_parsing`, or provide your own and configure it in your +``pyproject.toml`` file. + +Each commit parser has its own default configuration options so if you want to customize the parser +behavior, you will need to specify the parser options you want to override. + +.. code-block:: toml + + [tool.semantic_release] + commit_parser = "conventional" + + [tool.semantic_release.commit_parser_options] + minor_tags = ["feat"] + patch_tags = ["fix", "perf"] + parse_squash_commits = true + ignore_merge_commits = true + +.. important:: + Python Semantic Release does not currently support Monorepo projects. You will need to provide + a custom commit parser that is built for Monorepos. Follow the Monorepo-support progress in + `Issue #168 `_, + `Issue #614 `_, + and `PR #1143 `_. + + +Choosing your Changelog +''''''''''''''''''''''' + +Prior to creating a release, PSR will generate a changelog from the commit messages of your +project. The changelog is extremely customizable from the format to the content of each section. +PSR ships with a default changelog template that will be used if you do not provide custom +templates. The default should be sufficient for most projects and has its own set of configuration +options. + +For basic customization, you can choose either an traditional Markdown formatted changelog (default) +or if you want to integrate with a Sphinx Documentation project, you can use the +reStructuredText (RST) format. You can also choose the file name and location of where to write the +default changelog. + +To set your changelog location and changelog format, add the following to your ``pyproject.toml`` file: + +.. code-block:: toml + + [tool.semantic_release.changelog.default_templates] + changelog_file = "docs/source/CHANGELOG.rst" + output_format = "rst" # or "md" for Markdown format + +Secondly, the more important aspect of configuring your changelog is to define Commit Exclusion +Patterns or patterns that will be used to filter out commits from the changelog. PSR does **NOT** (yet) +come with a built-in set of exclusion patterns, so you will need to define them yourself. These commit +patterns should be in line with your project's commit parser configuration. + +To set commit exclusion patterns for a conventional commits parsers, add the following to your +``pyproject.toml`` file: + +.. code-block:: toml + + [tool.semantic_release.changelog.exclude_commit_patterns] + # Recommended patterns for conventional commits parser that is scope aware + exclude_commit_patterns = [ + '''chore(?:\([^)]*?\))?: .+''', + '''ci(?:\([^)]*?\))?: .+''', + '''refactor(?:\([^)]*?\))?: .+''', + '''style(?:\([^)]*?\))?: .+''', + '''test(?:\([^)]*?\))?: .+''', + '''build\((?!deps\): .+)''', + '''Initial [Cc]ommit.*''', + ] + +.. seealso:: + - :ref:`Changelog ` - Customize your changelog + - :ref:`changelog.mode ` - Choose the changelog mode ('update' or 'init') + - :ref:`changelog-templates-migrating-existing-changelog` + + +Defining your Release Branches +'''''''''''''''''''''''''''''' + +PSR provides a powerful feature to manage release types across multiple branches which can +allow you to configure your project to have different release branches for different purposes, +such as pre-release branches, beta branches, and your stable releases. + +.. note:: + Most projects that do **NOT** publish pre-releases will be fine with PSR's built-in default. + +To define an alpha pre-release branch when you are working on a fix or new feature, you can +add the following to your ``pyproject.toml`` file: + +.. code-block:: toml + + [tool.semantic_release.branches.alpha] + # Matches branches with the prefixes 'feat/', 'fix/', or 'perf/'. + match = "^(feat|fix|perf)/.+" + prerelease = true + prerelease_token = "alpha" + +Any time you execute ``semantic-release version`` on a branch with the prefix +``feat/``, ``fix/``, or ``perf/``, PSR will determine if a version bump is needed and if so, +the resulting version will be a pre-release version with the ``alpha`` token. For example, + ++-----------+--------------+-----------------+-------------------+ +| Branch | Version Bump | Current Version | Next Version | ++===========+==============+=================+===================+ +| main | Patch | ``1.0.0`` | ``1.0.1`` | ++-----------+--------------+-----------------+-------------------+ +| fix/bug-1 | Patch | ``1.0.0`` | ``1.0.1-alpha.1`` | ++-----------+--------------+-----------------+-------------------+ + +.. seealso:: + - :ref:`multibranch-releases` - Learn about multi-branch releases and how to configure them. + + +Configuring VCS Releases +'''''''''''''''''''''''' + +You can set up Python Semantic Release to create Releases in your remote version +control system, so you can publish assets and release notes for your project. + +In order to do so, you will need to place an authentication token in the +appropriate environment variable so that Python Semantic Release can authenticate +with the remote VCS to push tags, create releases, or upload files. + +GitHub (``GH_TOKEN``) +""""""""""""""""""""" + +For local publishing to GitHub, you should use a personal access token and +store it in your environment variables. Specify the name of the environment +variable in your configuration setting :ref:`remote.token `. +The default is ``GH_TOKEN``. + +To generate a token go to https://github.com/settings/tokens and click on +"Generate new token". + +For Personal Access Token (classic), you will need the ``repo`` scope to write +(ie. push) to the repository. + +For fine-grained Personal Access Tokens, you will need the `contents`__ +permission. + +__ https://docs.github.com/en/rest/authentication/permissions-required-for-fine-grained-personal-access-tokens#repository-permissions-for-contents + +GitLab (``GITLAB_TOKEN``) +""""""""""""""""""""""""" + +A personal access token from GitLab. This is used for authenticating when pushing +tags, publishing releases etc. This token should be stored in the ``GITLAB_TOKEN`` +environment variable. + +Gitea (``GITEA_TOKEN``) +""""""""""""""""""""""" + +A personal access token from Gitea. This token should be stored in the ``GITEA_TOKEN`` +environment variable. + +Bitbucket (``BITBUCKET_TOKEN``) +""""""""""""""""""""""""""""""" + +Bitbucket does not support uploading releases but can still benefit from automated tags +and changelogs. The user has three options to push changes to the repository: + +#. Use SSH keys. + +#. Use an `App Secret`_, store the secret in the ``BITBUCKET_TOKEN`` environment variable + and the username in ``BITBUCKET_USER``. + +#. Use an `Access Token`_ for the repository and store it in the ``BITBUCKET_TOKEN`` + environment variable. + +.. _App Secret: https://support.atlassian.com/bitbucket-cloud/docs/push-back-to-your-repository/#App-secret +.. _Access Token: https://support.atlassian.com/bitbucket-cloud/docs/repository-access-tokens + +.. seealso:: + - :ref:`Changelog ` - customize your project's changelog. + + - :ref:`changelog-templates-custom_release_notes` - customize the published release notes + + - :ref:`version --vcs-release/--no-vcs-release ` - enable/disable VCS release + creation. + + +Testing your Configuration +-------------------------- + +It's time to test your configuration! + +.. code-block:: bash + + # 1. Run the command in no-operation mode to see what would happen + semantic-release -v --noop version + + # 2. If the output looks reasonable, try to run the command without any history changes + # '-vv' will give you verbose debug output, which is useful for troubleshooting + # commit parsing issues. + semantic-release -vv version --no-commit --no-tag + + # 3. Evaluate your repository to see the changes that were made but not committed + # - Check the version number in your pyproject.toml + # - Check the distribution files from the build command + # - Check the changelog file for the new release notes + + # 4. If everything looks good, make sure to commit/save your configuration changes + git add pyproject.toml + git commit -m "chore(config): configure Python Semantic Release" + + # 5. Now, try to run the release command with your history changes but without pushing + semantic-release -v version --no-push --no-vcs-release + + # 6. Check the result on your local repository + git status + git log --graph --decorate --all + + # 7a. If you are happy with the release history and resulting commit & tag, + # reverse your changes before trying the full release command. + git tag -d v0.0.1 # replace with the actual version you released + git reset --hard HEAD~1 + + # 7b. [Optional] Once you have configured a remote VCS token, try + # running the full release command to update the remote repository. + semantic-release version --push --vcs-release + # This is optional as you may not want a personal access token set up or make + # make the release permanent yet. + +.. seealso:: + - :ref:`cmd-version` + - :ref:`troubleshooting-verbosity` + +Configuring CI/CD +----------------- + +PSR is meant to help you release at speed! See our CI/CD Configuration guides under the +:ref:`automatic` section. diff --git a/docs/concepts/index.rst b/docs/concepts/index.rst new file mode 100644 index 000000000..efa3077a8 --- /dev/null +++ b/docs/concepts/index.rst @@ -0,0 +1,17 @@ +.. _concepts: + +Concepts +======== + +This section covers the core concepts of Python Semantic Release and how it +works. Understanding these concepts will help you effectively use Python +Semantic Release in your projects. + +.. toctree:: + :maxdepth: 1 + + getting_started + commit_parsing + changelog_templates + multibranch_releases + strict_mode diff --git a/docs/concepts/installation.rst b/docs/concepts/installation.rst new file mode 100644 index 000000000..3d99b13a5 --- /dev/null +++ b/docs/concepts/installation.rst @@ -0,0 +1,14 @@ +.. _installation: + +Installation +============ + +.. code-block:: bash + + python3 -m pip install python-semantic-release + semantic-release --help + +Python Semantic Release is also available from `conda-forge`_ or as a +:ref:`GitHub Action `. + +.. _conda-forge: https://anaconda.org/conda-forge/python-semantic-release diff --git a/docs/multibranch_releases.rst b/docs/concepts/multibranch_releases.rst similarity index 100% rename from docs/multibranch_releases.rst rename to docs/concepts/multibranch_releases.rst diff --git a/docs/strict_mode.rst b/docs/concepts/strict_mode.rst similarity index 100% rename from docs/strict_mode.rst rename to docs/concepts/strict_mode.rst diff --git a/docs/conf.py b/docs/conf.py index 7db91dd85..0d37bcf7a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,6 @@ import os import sys +from datetime import datetime, timezone sys.path.insert(0, os.path.abspath(".")) sys.path.insert(0, os.path.abspath("..")) @@ -24,7 +25,8 @@ source_suffix = ".rst" master_doc = "index" project = "python-semantic-release" -copyright = f"2024, {author_name}" # noqa: A001 +current_year = datetime.now(timezone.utc).astimezone().year +copyright = f"{current_year}, {author_name}" # noqa: A001 version = semantic_release.__version__ release = semantic_release.__version__ @@ -39,7 +41,7 @@ # -- Automatically run sphinx-apidoc -------------------------------------- docs_path = os.path.dirname(__file__) -apidoc_output_dir = os.path.join(docs_path, "api") +apidoc_output_dir = os.path.join(docs_path, "api", "modules") apidoc_module_dir = os.path.join(docs_path, "..", "src") apidoc_separate_modules = True apidoc_module_first = True diff --git a/docs/automatic-releases/cronjobs.rst b/docs/configuration/automatic-releases/cronjobs.rst similarity index 96% rename from docs/automatic-releases/cronjobs.rst rename to docs/configuration/automatic-releases/cronjobs.rst index c61e44ba8..0ecf6ca58 100644 --- a/docs/automatic-releases/cronjobs.rst +++ b/docs/configuration/automatic-releases/cronjobs.rst @@ -1,7 +1,7 @@ .. _cronjobs: -Publish with cronjobs -~~~~~~~~~~~~~~~~~~~~~ +Cron Job Publishing +=================== This is for you if for some reason you cannot publish from your CI or you would like releases to drop at a certain interval. Before you start, answer this: Are you sure you do not want a CI to diff --git a/docs/automatic-releases/github-actions.rst b/docs/configuration/automatic-releases/github-actions.rst similarity index 100% rename from docs/automatic-releases/github-actions.rst rename to docs/configuration/automatic-releases/github-actions.rst diff --git a/docs/automatic-releases/index.rst b/docs/configuration/automatic-releases/index.rst similarity index 88% rename from docs/automatic-releases/index.rst rename to docs/configuration/automatic-releases/index.rst index c5e6d3453..3c3d8265b 100644 --- a/docs/automatic-releases/index.rst +++ b/docs/configuration/automatic-releases/index.rst @@ -1,13 +1,13 @@ .. _automatic: -Automatic Releases +Automated Releases ------------------ The key point with using this package is to automate your releases and stop worrying about version numbers. Different approaches to automatic releases and publishing with the help of this package can be found below. Using a CI is the recommended approach. -.. _automatic-guides: +.. _automated-release-guides: Guides ^^^^^^ diff --git a/docs/automatic-releases/travis.rst b/docs/configuration/automatic-releases/travis.rst similarity index 95% rename from docs/automatic-releases/travis.rst rename to docs/configuration/automatic-releases/travis.rst index 175f57447..5be380975 100644 --- a/docs/automatic-releases/travis.rst +++ b/docs/configuration/automatic-releases/travis.rst @@ -1,5 +1,7 @@ -Setting up python-semantic-release on Travis CI -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. _travis_ci: + +Travis CI +========= This guide expects you to have activated the repository on Travis CI. If this is not the case, please refer to `Travis documentation`_ on how to do that. diff --git a/docs/configuration.rst b/docs/configuration/configuration.rst similarity index 99% rename from docs/configuration.rst rename to docs/configuration/configuration.rst index 34411e579..4d36852b7 100644 --- a/docs/configuration.rst +++ b/docs/configuration/configuration.rst @@ -1,4 +1,4 @@ -.. _configuration: +.. _config: Configuration ============= @@ -802,7 +802,7 @@ Built-in parsers: You can set any of the built-in parsers by their keyword but you can also specify your own commit parser in ``path/to/module_file.py:Class`` or ``module:Class`` form. -For more information see :ref:`commit-parsing`. +For more information see :ref:`commit_parsing`. **Default:** ``"conventional"`` diff --git a/docs/configuration/index.rst b/docs/configuration/index.rst new file mode 100644 index 000000000..3b5dade61 --- /dev/null +++ b/docs/configuration/index.rst @@ -0,0 +1,18 @@ +.. _configuration: + +Configuration +============= + +Python Semantic Release is highly configurable, allowing you to tailor it to your project's needs. It supports +various runtime environments and can be integrated with different CI/CD services. + +1. Check out the :ref:`Configuration Options ` to customize your release process. + +2. Configure your :ref:`CI/CD services ` to use Python Semantic Release. + +.. toctree:: + :maxdepth: 1 + :hidden: + + Configuration Options + automatic-releases/index diff --git a/docs/contributing/contributing.rst b/docs/contributing/contributing_guide.rst similarity index 100% rename from docs/contributing/contributing.rst rename to docs/contributing/contributing_guide.rst diff --git a/docs/contributing/index.rst b/docs/contributing/index.rst index 51d8945c2..164cc99a0 100644 --- a/docs/contributing/index.rst +++ b/docs/contributing/index.rst @@ -26,3 +26,10 @@ No matter how you choose to contribute, please check out our .. |contributors| image:: https://contributors-img.web.app/image?repo=python-semantic-release/python-semantic-release :target: https://github.com/python-semantic-release/python-semantic-release/graphs/contributors + + +.. toctree:: + :hidden: + :maxdepth: 1 + + Contributing Guide diff --git a/docs/index.rst b/docs/index.rst index 515885a39..c49f1d334 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,53 +1,55 @@ Python Semantic Release *********************** -|Ruff| |Test Status| |PyPI Version| |conda-forge version| |Read the Docs Status| |Pre-Commit Enabled| +|PyPI Version| |conda-forge version| |Last Release| |Monthly Downloads| |PSR License| |Issues| -Automatic Semantic Versioning for Python projects. This is a Python -implementation of `semantic-release`_ for JS by Stephan Bönnemann. If -you find this topic interesting you should check out his `talk from -JSConf Budapest`_. +**Python Semantic Release (PSR)** provides an automated release mechanism +determined by SemVer and Commit Message Conventions for your Git projects. -The general idea is to be able to detect what the next version of the -project should be based on the commits. This tool will use that to -automate the whole release, upload to an artifact repository and post changelogs to -GitHub. You can run the tool on a CI service, or just run it locally. +The purpose of this project is to detect what the next version of the +project should be from parsing the latest commit messages. If the commit messages +describe changes that would require a major, minor or patch version bump, PSR +will automatically bump the version number accordingly. PSR, however, does not +stop there but will help automate the whole release process. It will update the +project code and distribution artifact, upload the artifact and post changelogs +to a remotely hosted Version Control System (VCS). -Installation -============ +The tool is designed to run inside of a CI/CD pipeline service, but it can +also be run locally. -:: +This project was originally inspired by the `semantic-release`_ project for JavaScript +by *Stephan Bönnemann*, but the codebases have significantly deviated since then, as +PSR as driven towards the goal of providing flexible changelogs and simple initial setup. - python3 -m pip install python-semantic-release - semantic-release --help +.. include:: concepts/installation.rst -Python Semantic Release is also available from `conda-forge`_ or as a `GitHub Action`_. -Read more about the setup and configuration in our `getting started guide`_. +Read more about the setup and configuration in our :ref:`Getting Started Guide `. .. _semantic-release: https://github.com/semantic-release/semantic-release -.. _talk from JSConf Budapest: https://www.youtube.com/watch?v=tc2UgG5L7WM -.. _getting started guide: https://python-semantic-release.readthedocs.io/en/latest/#getting-started -.. _GitHub Action: https://python-semantic-release.readthedocs.io/en/latest/automatic-releases/github-actions.html -.. _conda-forge: https://anaconda.org/conda-forge/python-semantic-release - -.. |Test Status| image:: https://img.shields.io/github/actions/workflow/status/python-semantic-release/python-semantic-release/cicd.yml?branch=master&label=Test%20Status&logo=github - :target: https://github.com/python-semantic-release/python-semantic-release/actions/workflows/cicd.yml - :alt: test-status + .. |PyPI Version| image:: https://img.shields.io/pypi/v/python-semantic-release?label=PyPI&logo=pypi :target: https://pypi.org/project/python-semantic-release/ :alt: pypi + .. |conda-forge Version| image:: https://img.shields.io/conda/vn/conda-forge/python-semantic-release?logo=anaconda :target: https://anaconda.org/conda-forge/python-semantic-release :alt: conda-forge -.. |Read the Docs Status| image:: https://img.shields.io/readthedocs/python-semantic-release?label=Read%20the%20Docs&logo=Read%20the%20Docs - :target: https://python-semantic-release.readthedocs.io/en/latest/ - :alt: docs -.. |Pre-Commit Enabled| image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit - :target: https://github.com/pre-commit/pre-commit - :alt: pre-commit -.. |Ruff| image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json - :target: https://github.com/astral-sh/ruff - :alt: Ruff + +.. |Last Release| image:: https://img.shields.io/github/release-date/python-semantic-release/python-semantic-release?display_date=published_at + :target: https://github.com/python-semantic-release/python-semantic-release/releases/latest + :alt: GitHub Release Date + +.. |PSR License| image:: https://img.shields.io/pypi/l/python-semantic-release?color=blue + :target: https://github.com/python-semantic-release/python-semantic-release/blob/master/LICENSE + :alt: PyPI - License + +.. |Issues| image:: https://img.shields.io/github/issues/python-semantic-release/python-semantic-release + :target: https://github.com/python-semantic-release/python-semantic-release/issues + :alt: GitHub Issues + +.. |Monthly Downloads| image:: https://img.shields.io/pypi/dm/python-semantic-release + :target: https://pypistats.org/packages/python-semantic-release + :alt: PyPI - Downloads Documentation Contents @@ -56,194 +58,19 @@ Documentation Contents .. toctree:: :maxdepth: 1 - commands - Strict Mode - configuration - commit_parsing - Changelog Templates - Multibranch Releases - automatic-releases/index - troubleshooting + What's New + Concepts + CLI + configuration/index upgrading/index - Internal API - Changelog + misc/troubleshooting + API Contributing View on GitHub -Getting Started -=============== - -If you haven't done so already, install Python Semantic Release following the -instructions above. - -There is no strict requirement to have it installed locally if you intend on -:ref:`using a CI service `, however running with :ref:`cmd-main-option-noop` can be -useful to test your configuration. - -Generating your configuration ------------------------------ - -Python Semantic Release ships with a command-line interface, ``semantic-release``. You can -inspect the default configuration in your terminal by running - -``semantic-release generate-config`` - -You can also use the :ref:`-f/--format ` option to specify what format you would like this configuration -to be. The default is TOML, but JSON can also be used. - -You can append the configuration to your existing ``pyproject.toml`` file using a standard redirect, -for example: - -``semantic-release generate-config --pyproject >> pyproject.toml`` - -and then editing to your project's requirements. - -.. seealso:: - - :ref:`cmd-generate-config` - - :ref:`configuration` - - -Setting up version numbering ----------------------------- - -Create a variable set to the current version number. This could be anywhere in -your project, for example ``setup.py``:: - - from setuptools import setup - - __version__ = "0.0.0" - - setup( - name="my-package", - version=__version__, - # And so on... - ) - -Python Semantic Release can be configured using a TOML or JSON file; the default configuration file is -``pyproject.toml``, if you wish to use another file you will need to use the ``-c/--config`` option to -specify the file. - -Set :ref:`version_variables ` to a list, the only element of which should be the location of your -version variable inside any Python file, specified in standard ``module:attribute`` syntax: - -``pyproject.toml``:: - - [tool.semantic_release] - version_variables = ["setup.py:__version__"] - -.. seealso:: - - :ref:`configuration` - tailor Python Semantic Release to your project - -Setting up commit parsing -------------------------- - -We rely on commit messages to detect when a version bump is needed. -By default, Python Semantic Release uses the `Conventional Commits Specification`_ -to parse commit messages. You can find out more about this in :ref:`commit-parsing`. - -.. seealso:: - - :ref:`config-branches` - Adding configuration for releases from multiple branches. - - :ref:`commit_parser ` - use a different parser for commit messages. - For example, Python Semantic Release also ships with emoji and scipy-style parsers. - - :ref:`remote.type ` - specify the type of your remote VCS. - -.. _Conventional Commits Specification: https://www.conventionalcommits.org/en/v1.0.0 - -Setting up the changelog ------------------------- - -.. seealso:: - - :ref:`Changelog ` - Customize the changelog generated by Python Semantic Release. - - :ref:`changelog-templates-migrating-existing-changelog` - -.. _index-creating-vcs-releases: - -Creating VCS Releases ---------------------- - -You can set up Python Semantic Release to create Releases in your remote version -control system, so you can publish assets and release notes for your project. - -In order to do so, you will need to place an authentication token in the -appropriate environment variable so that Python Semantic Release can authenticate -with the remote VCS to push tags, create releases, or upload files. - -GitHub (``GH_TOKEN``) -""""""""""""""""""""" - -For local publishing to GitHub, you should use a personal access token and -store it in your environment variables. Specify the name of the environment -variable in your configuration setting :ref:`remote.token `. -The default is ``GH_TOKEN``. - -To generate a token go to https://github.com/settings/tokens and click on -"Generate new token". - -For Personal Access Token (classic), you will need the ``repo`` scope to write -(ie. push) to the repository. - -For fine-grained Personal Access Tokens, you will need the `contents`__ -permission. - -__ https://docs.github.com/en/rest/authentication/permissions-required-for-fine-grained-personal-access-tokens#repository-permissions-for-contents - -GitLab (``GITLAB_TOKEN``) -""""""""""""""""""""""""" - -A personal access token from GitLab. This is used for authenticating when pushing -tags, publishing releases etc. This token should be stored in the ``GITLAB_TOKEN`` -environment variable. - -Gitea (``GITEA_TOKEN``) -""""""""""""""""""""""" - -A personal access token from Gitea. This token should be stored in the ``GITEA_TOKEN`` -environment variable. - -Bitbucket (``BITBUCKET_TOKEN``) -""""""""""""""""""""""""""""""" - -Bitbucket does not support uploading releases but can still benefit from automated tags -and changelogs. The user has three options to push changes to the repository: - -#. Use SSH keys. -#. Use an `App Secret`_, store the secret in the ``BITBUCKET_TOKEN`` environment variable and the username in ``BITBUCKET_USER``. -#. Use an `Access Token`_ for the repository and store it in the ``BITBUCKET_TOKEN`` environment variable. - -.. _App Secret: https://support.atlassian.com/bitbucket-cloud/docs/push-back-to-your-repository/#App-secret -.. _Access Token: https://support.atlassian.com/bitbucket-cloud/docs/repository-access-tokens - -.. seealso:: - - :ref:`Changelog ` - customize your project's changelog. - - :ref:`changelog-templates-custom_release_notes` - customize the published release notes - - :ref:`upload_to_vcs_release ` - - enable/disable uploading artifacts to VCS releases - - :ref:`version --vcs-release/--no-vcs-release ` - enable/disable VCS release - creation. - - `upload-to-gh-release`_, a GitHub Action for running ``semantic-release publish`` - -.. _upload-to-gh-release: https://github.com/python-semantic-release/upload-to-gh-release - -.. _running-from-setuppy: - -Running from setup.py ---------------------- - -Add the following hook to your ``setup.py`` and you will be able to run -``python setup.py `` as you would ``semantic-release ``:: - - try: - from semantic_release import setup_hook - setup_hook(sys.argv) - except ImportError: - pass - -.. note:: - Only the :ref:`version `, :ref:`publish `, and - :ref:`changelog ` commands may be invoked from setup.py in this way. +---- -Running on CI -------------- +.. _inline-getting-started-guide: -Getting a fully automated setup with releases from CI can be helpful for some -projects. See :ref:`automatic`. +.. include:: concepts/getting_started.rst + :start-after: .. _getting-started-guide: diff --git a/docs/misc/psr_changelog.rst b/docs/misc/psr_changelog.rst new file mode 100644 index 000000000..09929fe43 --- /dev/null +++ b/docs/misc/psr_changelog.rst @@ -0,0 +1 @@ +.. include:: ../../CHANGELOG.rst diff --git a/docs/troubleshooting.rst b/docs/misc/troubleshooting.rst similarity index 100% rename from docs/troubleshooting.rst rename to docs/misc/troubleshooting.rst diff --git a/docs/psr_changelog.rst b/docs/psr_changelog.rst deleted file mode 100644 index 565b0521d..000000000 --- a/docs/psr_changelog.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../CHANGELOG.rst From 58d4e7f8f056f465c387689e7514a33c07239ed5 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 1 Dec 2024 17:29:51 -0700 Subject: [PATCH 150/160] test(fixtures): update repo fixtures to include at least one issue closure reference --- tests/fixtures/repos/git_flow/repo_w_1_release_channel.py | 6 +++--- tests/fixtures/repos/git_flow/repo_w_2_release_channels.py | 6 +++--- tests/fixtures/repos/git_flow/repo_w_3_release_channels.py | 6 +++--- tests/fixtures/repos/git_flow/repo_w_4_release_channels.py | 6 +++--- tests/fixtures/repos/github_flow/repo_w_default_release.py | 6 +++--- tests/fixtures/repos/github_flow/repo_w_release_channels.py | 6 +++--- .../repos/trunk_based_dev/repo_w_dual_version_support.py | 6 +++--- .../repo_w_dual_version_support_w_prereleases.py | 6 +++--- tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py | 6 +++--- tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py | 6 +++--- tests/fixtures/repos/trunk_based_dev/repo_w_tags.py | 6 +++--- 11 files changed, 33 insertions(+), 33 deletions(-) diff --git a/tests/fixtures/repos/git_flow/repo_w_1_release_channel.py b/tests/fixtures/repos/git_flow/repo_w_1_release_channel.py index bdaf43d64..91eb5ffa5 100644 --- a/tests/fixtures/repos/git_flow/repo_w_1_release_channel.py +++ b/tests/fixtures/repos/git_flow/repo_w_1_release_channel.py @@ -517,9 +517,9 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "conventional": "fix: correct a bug", - "emoji": ":bug: correct a bug", - "scipy": "BUG: correct a bug", + "conventional": "fix: correct a bug\n\nCloses: #123\n", + "emoji": ":bug: correct a bug\n\nCloses: #123\n", + "scipy": "BUG: correct a bug\n\nCloses: #123\n", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, diff --git a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py index 537ef4b02..3d75af918 100644 --- a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py @@ -603,9 +603,9 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "conventional": "fix(config): fixed configuration generation", - "emoji": ":bug: (config) fixed configuration generation", - "scipy": "MAINT:config: fixed configuration generation", + "conventional": "fix(config): fixed configuration generation\n\nCloses: #123", + "emoji": ":bug: (config) fixed configuration generation\n\nCloses: #123", + "scipy": "MAINT:config: fixed configuration generation\n\nCloses: #123", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, diff --git a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py index bf49b27b5..d52b60f97 100644 --- a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py @@ -728,9 +728,9 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "conventional": "fix(config): fix config option", - "emoji": ":bug: (config) fix config option", - "scipy": "BUG: config: fix config option", + "conventional": "fix(config): fix config option\n\nImplements: #123\n", + "emoji": ":bug: (config) fix config option\n\nImplements: #123\n", + "scipy": "BUG: config: fix config option\n\nImplements: #123\n", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, diff --git a/tests/fixtures/repos/git_flow/repo_w_4_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_4_release_channels.py index 9062bd2b1..5bdb76d7d 100644 --- a/tests/fixtures/repos/git_flow/repo_w_4_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_4_release_channels.py @@ -412,9 +412,9 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "conventional": "fix(cli): fix config cli command", - "emoji": ":bug: (cli) fix config cli command", - "scipy": "BUG:cli: fix config cli command", + "conventional": "fix(cli): fix config cli command\n\nCloses: #123\n", + "emoji": ":bug: (cli) fix config cli command\n\nCloses: #123\n", + "scipy": "BUG:cli: fix config cli command\n\nCloses: #123\n", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, diff --git a/tests/fixtures/repos/github_flow/repo_w_default_release.py b/tests/fixtures/repos/github_flow/repo_w_default_release.py index 25d30a271..61816f0bf 100644 --- a/tests/fixtures/repos/github_flow/repo_w_default_release.py +++ b/tests/fixtures/repos/github_flow/repo_w_default_release.py @@ -194,9 +194,9 @@ def _get_repo_from_definition( fix_branch_1_commits: Sequence[CommitSpec] = [ { - "conventional": "fix(cli): add missing text", - "emoji": ":bug: add missing text", - "scipy": "MAINT: add missing text", + "conventional": "fix(cli): add missing text\n\nResolves: #123\n", + "emoji": ":bug: add missing text\n\nResolves: #123\n", + "scipy": "MAINT: add missing text\n\nResolves: #123\n", "datetime": next(commit_timestamp_gen), }, ] diff --git a/tests/fixtures/repos/github_flow/repo_w_release_channels.py b/tests/fixtures/repos/github_flow/repo_w_release_channels.py index eec636dd4..87c57b609 100644 --- a/tests/fixtures/repos/github_flow/repo_w_release_channels.py +++ b/tests/fixtures/repos/github_flow/repo_w_release_channels.py @@ -217,9 +217,9 @@ def _get_repo_from_defintion( "commits": convert_commit_specs_to_commit_defs( [ { - "conventional": "fix: correct some text", - "emoji": ":bug: correct some text", - "scipy": "MAINT: correct some text", + "conventional": "fix: correct some text\n\nResolves: #123", + "emoji": ":bug: correct some text\n\nResolves: #123", + "scipy": "MAINT: correct some text\n\nResolves: #123", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support.py b/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support.py index f909c584b..008a7b6d6 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support.py @@ -314,9 +314,9 @@ def _get_repo_from_definition( "commits": convert_commit_specs_to_commit_defs( [ { - "conventional": "fix: correct critical bug", - "emoji": ":bug: correct critical bug", - "scipy": "MAINT: correct critical bug", + "conventional": "fix: correct critical bug\n\nResolves: #123\n", + "emoji": ":bug: correct critical bug\n\nResolves: #123\n", + "scipy": "MAINT: correct critical bug\n\nResolves: #123\n", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support_w_prereleases.py b/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support_w_prereleases.py index 88c07d6c3..4fb0d14c7 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support_w_prereleases.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_dual_version_support_w_prereleases.py @@ -315,9 +315,9 @@ def _get_repo_from_definition( "commits": convert_commit_specs_to_commit_defs( [ { - "conventional": "fix: correct critical bug", - "emoji": ":bug: correct critical bug", - "scipy": "MAINT: correct critical bug", + "conventional": "fix: correct critical bug\n\nResolves: #123\n", + "emoji": ":bug: correct critical bug\n\nResolves: #123\n", + "scipy": "MAINT: correct critical bug\n\nResolves: #123\n", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py index 37d9b0450..a1253c8e8 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py @@ -143,9 +143,9 @@ def _get_repo_from_definition( "include_in_changelog": True, }, { - "conventional": "fix: correct more text", - "emoji": ":bug: correct more text", - "scipy": "MAINT: correct more text", + "conventional": "fix: correct more text\n\nCloses: #123", + "emoji": ":bug: correct more text\n\nCloses: #123", + "scipy": "MAINT: correct more text\n\nCloses: #123", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py index a72ef1e20..57e46578f 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py @@ -189,9 +189,9 @@ def _get_repo_from_definition( "commits": convert_commit_specs_to_commit_defs( [ { - "conventional": "fix: correct some text", - "emoji": ":bug: correct some text", - "scipy": "MAINT: correct some text", + "conventional": "fix: correct some text\n\nfixes: #123\n", + "emoji": ":bug: correct some text\n\nfixes: #123\n", + "scipy": "MAINT: correct some text\n\nfixes: #123\n", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py index 40741bc18..b58a23865 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py @@ -191,9 +191,9 @@ def _get_repo_from_definition( "commits": convert_commit_specs_to_commit_defs( [ { - "conventional": "fix: correct some text", - "emoji": ":bug: correct some text", - "scipy": "MAINT: correct some text", + "conventional": "fix: correct some text\n\nResolves: #123\n", + "emoji": ":bug: correct some text\n\nResolves: #123\n", + "scipy": "MAINT: correct some text\n\nResolves: #123\n", "datetime": next(commit_timestamp_gen), "include_in_changelog": True, }, From 8d7b4c089144763aefd326573867afa6f55fcfd7 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Wed, 18 Dec 2024 01:33:19 -0700 Subject: [PATCH 151/160] test(cmd-version): update changelog test case for handling issue closure footers --- tests/e2e/cmd_version/test_version_changelog.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/e2e/cmd_version/test_version_changelog.py b/tests/e2e/cmd_version/test_version_changelog.py index 6dea5139b..a2ce88bf4 100644 --- a/tests/e2e/cmd_version/test_version_changelog.py +++ b/tests/e2e/cmd_version/test_version_changelog.py @@ -891,4 +891,6 @@ def test_version_updates_changelog_w_new_version_n_filtered_commit( # Evaluate assert_successful_exit_code(result, cli_cmd) assert expected_changelog_content == actual_content - assert expected_bump_message in actual_content + + for msg_part in expected_bump_message.split("\n\n"): + assert msg_part.capitalize() in actual_content From bd3e7bfa86d53a03f03ac419399847712c523b02 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 25 May 2025 00:52:13 -0600 Subject: [PATCH 152/160] fix(cli): adjust verbosity parameter to enable silly-level logging --- src/semantic_release/cli/commands/main.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/semantic_release/cli/commands/main.py b/src/semantic_release/cli/commands/main.py index 08b893d70..c10d3f647 100644 --- a/src/semantic_release/cli/commands/main.py +++ b/src/semantic_release/cli/commands/main.py @@ -22,6 +22,12 @@ FORMAT = "%(message)s" +LOG_LEVELS = [ + SemanticReleaseLogLevels.WARNING, + SemanticReleaseLogLevels.INFO, + SemanticReleaseLogLevels.DEBUG, + SemanticReleaseLogLevels.SILLY, +] class Cli(click.MultiCommand): @@ -79,7 +85,7 @@ def get_command(self, _ctx: click.Context, name: str) -> click.Command | None: default=0, count=True, show_default=True, - type=click.IntRange(0, 2, clamp=True), + type=click.IntRange(0, len(LOG_LEVELS) - 1, clamp=True), ) @click.option( "--strict", @@ -107,14 +113,7 @@ def main( For more information, visit https://python-semantic-release.readthedocs.io/ """ - log_levels = [ - SemanticReleaseLogLevels.WARNING, - SemanticReleaseLogLevels.INFO, - SemanticReleaseLogLevels.DEBUG, - SemanticReleaseLogLevels.SILLY, - ] - - globals.log_level = log_levels[verbosity] + globals.log_level = LOG_LEVELS[verbosity] # Set up our pretty console formatter rich_handler = RichHandler( From bca51981f9503c97e7cac82fe1a5f44410285d64 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 25 May 2025 01:13:11 -0600 Subject: [PATCH 153/160] chore(scripts): update doc script for new doc file locations --- scripts/bump_version_in_docs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/bump_version_in_docs.py b/scripts/bump_version_in_docs.py index f8c67331a..7c6104791 100644 --- a/scripts/bump_version_in_docs.py +++ b/scripts/bump_version_in_docs.py @@ -60,7 +60,8 @@ def envsubst(filepath: Path, version: str, release_tag: str) -> None: exit(1) update_github_actions_example( - DOCS_DIR / "automatic-releases" / "github-actions.rst", new_release_tag + DOCS_DIR / "configuration" / "automatic-releases" / "github-actions.rst", + new_release_tag, ) for doc_file in DOCS_DIR.rglob("*.rst"): From e06ee2876880c68ef90b11784bd2060adb9c5908 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Sun, 25 May 2025 07:36:09 +0000 Subject: [PATCH 154/160] 10.0.0 Automatically generated by python-semantic-release --- CHANGELOG.rst | 357 ++++++++++++++++++ .../automatic-releases/github-actions.rst | 26 +- docs/configuration/configuration.rst | 8 +- pyproject.toml | 2 +- 4 files changed, 375 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5714ef3eb..61fa77784 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,363 @@ CHANGELOG ========= +.. _changelog-v10.0.0: + +v10.0.0 (2025-05-25) +==================== + +✨ Features +----------- + +* **cmd-version**: Enable ``version_variables`` version stamp of vars with double-equals + (`PR#1244`_, `080e4bc`_) + +* **parser-conventional**: Set parser to evaluate all squashed commits by default (`6fcdc99`_) + +* **parser-conventional**: Set parser to ignore merge commits by default (`59bf084`_) + +* **parser-emoji**: Set parser to evaluate all squashed commits by default (`514a922`_) + +* **parser-emoji**: Set parser to ignore merge commits by default (`8a51525`_) + +* **parser-scipy**: Set parser to evaluate all squashed commits by default (`634fffe`_) + +* **parser-scipy**: Set parser to ignore merge commits by default (`d4f128e`_) + +🪲 Bug Fixes +------------ + +* **changelog-md**: Change to 1-line descriptions in markdown template, closes `#733`_ (`e7ac155`_) + +* **changelog-rst**: Change to 1-line descriptions in the default ReStructuredText template, closes + `#733`_ (`731466f`_) + +* **cli**: Adjust verbosity parameter to enable silly-level logging (`bd3e7bf`_) + +* **github-action**: Resolve command injection vulnerability in action script (`fb3da27`_) + +* **parser-conventional**: Remove breaking change footer messages from commit descriptions + (`b271cbb`_) + +* **parser-conventional**: Remove issue footer messages from commit descriptions (`b1bb0e5`_) + +* **parser-conventional**: Remove PR/MR references from commit subject line (`eed63fa`_) + +* **parser-conventional**: Remove release notice footer messages from commit descriptions + (`7e8dc13`_) + +* **parser-emoji**: Remove issue footer messages from commit descriptions (`b757603`_) + +* **parser-emoji**: Remove PR/MR references from commit subject line (`16465f1`_) + +* **parser-emoji**: Remove release notice footer messages from commit descriptions (`b6307cb`_) + +* **parser-scipy**: Remove issue footer messages from commit descriptions (`3cfee76`_) + +* **parser-scipy**: Remove PR/MR references from commit subject line (`da4140f`_) + +* **parser-scipy**: Remove release notice footer messages from commit descriptions (`58308e3`_) + +📖 Documentation +---------------- + +* Refactor documentation page navigation (`4e52f4b`_) + +* **algorithm**: Remove out-of-date algorithm description (`6cd0fbe`_) + +* **commit-parsing**: Define limitation of revert commits with the scipy parser (`5310d0c`_) + +* **configuration**: Change default value for ``allow_zero_version`` in the description (`203d29d`_) + +* **configuration**: Change the default for the base changelog's ``mask_initial_release`` value + (`5fb02ab`_) + +* **configuration**: Change the default value for ``changelog.mode`` in the setting description + (`0bed906`_) + +* **configuration**: Update ``version_variables`` section to include double-equals operand support + (`PR#1244`_, `080e4bc`_) + +* **contributing**: Refactor contributing & contributors layout (`8bed5bc`_) + +* **github-actions**: Add reference to manual release workflow example (`6aad7f1`_) + +* **github-actions**: Change recommended workflow to separate release from deploy (`67b2ae0`_) + +* **github-actions**: Update ``python-semantic-release/publish-action`` parameter notes (`c4d45ec`_) + +* **github-actions**: Update PSR action parameter documenation (`a082896`_) + +* **upgrading**: Re-locate version upgrade guides into ``Upgrading PSR`` (`a5f5e04`_) + +* **upgrading-v10**: Added migration guide for v9 to v10 (`4ea92ec`_) + +⚙️ Build System +---------------- + +* **deps**: Prevent update to ``click@8.2.0`` (`PR#1245`_, `4aa6a6e`_) + +♻️ Refactoring +--------------- + +* **config**: Change ``allow_zero_version`` default to ``false`` (`c6b6eab`_) + +* **config**: Change ``changelog.default_templates.mask_initial_release`` default to ``true`` + (`0e114c3`_) + +* **config**: Change ``changelog.mode`` default to ``update`` (`7d39e76`_) + +💥 Breaking Changes +------------------- + +* **changelog-md**: The default Markdown changelog template and release notes template will no + longer print out the entire commit message contents, instead, it will only print the commit + subject line. This comes to meet the high demand of better formatted changelogs and requests for + subject line only. Originally, it was a decision to not hide commit subjects that were included in + the commit body via the ``git merge --squash`` command and PSR did not have another alternative. + At this point, all the built-in parsers have the ability to parse squashed commits and separate + them out into their own entry on the changelog. Therefore, the default template no longer needs to + write out the full commit body. See the commit parser options if you want to enable/disable + parsing squash commits. + +* **changelog-rst**: The default ReStructured changelog template will no longer print out the entire + commit message contents, instead, it will only print the commit subject line. This comes to meet + the high demand of better formatted changelogs and requests for subject line only. Originally, it + was a decision to not hide commit subjects that were included in the commit body via the ``git + merge --squash`` command and PSR did not have another alternative. At this point, all the built-in + parsers have the ability to parse squashed commits and separate them out into their own entry on + the changelog. Therefore, the default template no longer needs to write out the full commit body. + See the commit parser options if you want to enable/disable parsing squash commits. + +* **config**: This release switches the ``allow_zero_version`` default to ``false``. This change is + to encourage less ``0.x`` releases as the default but rather allow the experienced developer to + choose when ``0.x`` is appropriate. There are way too many projects in the ecosystems that never + leave ``0.x`` and that is problematic for the industry tools that help auto-update based on + SemVer. We should strive for publishing usable tools and maintaining good forethought for when + compatibility must break. If your configuration already sets the ``allow_zero_version`` value, + this change will have no effect on your project. If you want to use ``0.x`` versions, from the + start then change ``allow_zero_version`` to ``true`` in your configuration. + +* **config**: This release switches the ``changelog.default_templates.mask_initial_release`` default + to ``true``. This change is intended to toggle better recommended outputs of the default + changelog. Conceptually, the very first release is hard to describe--one can only provide new + features as nothing exists yet for the end user. No changelog should be written as there is no + start point to compare the "changes" to. The recommendation instead is to only list a simple + message as ``Initial Release``. This is now the default for PSR when providing the very first + release (no pre-existing tags) in the changelog and release notes. If your configuration already + sets the ``changelog.default_templates.mask_initial_release`` value, then this change will have no + effect on your project. If you do NOT want to mask the first release information, then set + ``changelog.default_templates.mask_initial_release`` to ``false`` in your configuration. + +* **config**: This release switches the ``changelog.mode`` default to ``update``. In this mode, if a + changelog exists, PSR will update the changelog **IF AND ONLY IF** the configured insertion flag + exists in the changelog. The Changelog output will remain unchanged if no insertion flag exists. + The insertion flag may be configured with the ``changelog.insertion_flag`` setting. When upgrading + to ``v10``, you must add the insertion flag manually or you can just delete the changelog file and + run PSR's changelog generation and it will rebuild the changelog (similar to init mode) but it + will add the insertion flag. If your configuration already sets the ``changelog.mode`` value, then + this change will have no effect on your project. If you would rather the changelog be generated + from scratch every release, than set the ``changelog.mode`` value to ``init`` in your + configuration. + +* **github-action**: The ``root_options`` action input parameter has been removed because it created + a command injection vulernability for arbitrary code to execute within the container context of + the GitHub action if a command injection code was provided as part of the ``root_options`` + parameter string. To eliminate the vulnerability, each relevant option that can be provided to + ``semantic-release`` has been individually added as its own parameter and will be processed + individually to prevent command injection. Please review our `Github Actions Configuration`__ page + on the Python Semantic Release Documentation website to review the newly available configuration + options that replace the ``root_options`` parameter. + +* **parser-conventional**: Any breaking change footer messages that the conventional commit parser + detects will now be removed from the ``commit.descriptions[]`` list but maintained in and only in + the ``commit.breaking_descriptions[]`` list. Previously, the descriptions included all text from + the commit message but that was redundant as the default changelog now handles breaking change + footers in its own section. + +* **parser-conventional**: Any issue resolution footers that the parser detects will now be removed + from the ``commit.descriptions[]`` list. Previously, the descriptions included all text from the + commit message but now that the parser pulls out the issue numbers the numbers will be included in + the ``commit.linked_issues`` tuple for user extraction in any changelog generation. + +* **parser-conventional**: Any release notice footer messages that the commit parser detects will + now be removed from the ``commit.descriptions[]`` list but maintained in and only in the + ``commit.notices[]`` list. Previously, the descriptions included all text from the commit message + but that was redundant as the default changelog now handles release notice footers in its own + section. + +* **parser-conventional**: Generally, a pull request or merge request number reference is included + in the subject line at the end within parentheses on some common VCS's like GitHub. PSR now looks + for this reference and extracts it into the ``commit.linked_merge_request`` and the + ``commit.linked_pull_request`` attributes of a commit object. Since this is now pulled out + individually, it is cleaner to remove this from the first line of the ``commit.descriptions`` list + (ie. the subject line) so that changelog macros do not have to replace the text but instead only + append a PR/MR link to the end of the line. The reference does maintain the PR/MR prefix indicator + (`#` or ``!``). + +* **parser-conventional**: The configuration setting ``commit_parser_options.ignore_merge_commits`` + is now set to ``true`` by default. The feature to ignore squash commits was introduced in + ``v9.18.0`` and was originally set to ``false`` to prevent unexpected results on a non-breaking + update. The ignore merge commits feature prevents additional unnecessary processing on a commit + message that likely will not match a commit message syntax. Most merge commits are syntactically + pre-defined by Git or Remote Version Control System (ex. GitHub, etc.) and do not follow a commit + convention (nor should they). The larger issue with merge commits is that they ultimately are a + full copy of all the changes that were previously created and committed. The merge commit itself + ensures that the previous commit tree is maintained in history, therefore the commit message + always exists. If merge commits are parsed, it generally creates duplicate messages that will end + up in your changelog, which is less than desired in most cases. If you have previously used the + ``changelog.exclude_commit_patterns`` functionality to ignore merge commit messages then you will + want this setting set to ``true`` to improve parsing speed. You can also now remove the merge + commit exclude pattern from the list as well to improve parsing speed. If this functionality is + not desired, you will need to update your configuration to change the new setting to ``false``. + +* **parser-conventional**: The configuration setting ``commit_parser_options.parse_squash_commits`` + is now set to ``true`` by default. The feature to parse squash commits was introduced in + ``v9.17.0`` and was originally set to ``false`` to prevent unexpected results on a non-breaking + update. The parse squash commits feature attempts to find additional commits of the same commit + type within the body of a single commit message. When squash commits are found, Python Semantic + Release will separate out each commit into its own artificial commit object and parse them + individually. This potentially can change the resulting version bump if a larger bump was detected + within the squashed components. It also allows for the changelog and release notes to separately + order and display each commit as originally written. If this is not desired, you will need to + update your configuration to change the new setting to ``false``. + +* **parser-emoji**: Any issue resolution footers that the parser detects will now be removed from + the ``commit.descriptions[]`` list. Previously, the descriptions included all text from the commit + message but now that the parser pulls out the issue numbers the numbers will be included in the + ``commit.linked_issues`` tuple for user extraction in any changelog generation. + +* **parser-emoji**: Any release notice footer messages that the emoji commit parser detects will now + be removed from the ``commit.descriptions[]`` list but maintained in and only in the + ``commit.notices[]`` list. Previously, the descriptions included all text from the commit message + but that was redundant as the default changelog now handles release notice footers in its own + section. + +* **parser-emoji**: Generally, a pull request or merge request number reference is included in the + subject line at the end within parentheses on some common VCS's (e.g. GitHub). PSR now looks for + these references and extract it into the ``commit.linked_merge_request`` field of a commit object. + Since this is now pulled out individually, it is cleaner to remove this from the first line of the + ``commit.descriptions`` list (ie. the subject line) so that changelog macros do not have to + replace the text but instead only append a PR/MR link to the end of the line. The reference will + maintain the PR/MR prefix indicator (e.g. ``#`` or ``!``). + +* **parser-emoji**: The configuration setting ``commit_parser_options.ignore_merge_commits`` is now + set to ``true`` by default. The feature to ignore squash commits was introduced in ``v9.18.0`` and + was originally set to ``false`` to prevent unexpected results on a non-breaking update. The ignore + merge commits feature prevents additional unnecessary processing on a commit message that likely + will not match a commit message syntax. Most merge commits are syntactically pre-defined by Git or + Remote Version Control System (ex. GitHub, etc.) and do not follow a commit convention (nor should + they). The larger issue with merge commits is that they ultimately are a full copy of all the + changes that were previously created and committed. The merge commit itself ensures that the + previous commit tree is maintained in history, therefore the commit message always exists. If + merge commits are parsed, it generally creates duplicate messages that will end up in your + changelog, which is less than desired in most cases. If you have previously used the + ``changelog.exclude_commit_patterns`` functionality to ignore merge commit messages then you will + want this setting set to ``true`` to improve parsing speed. You can also now remove the merge + commit exclude pattern from the list as well to improve parsing speed. If this functionality is + not desired, you will need to update your configuration to change the new setting to ``false``. + +* **parser-emoji**: The configuration setting ``commit_parser_options.parse_squash_commits`` is now + set to ``true`` by default. The feature to parse squash commits was introduced in ``v9.17.0`` and + was originally set to ``false`` to prevent unexpected results on a non-breaking update. The parse + squash commits feature attempts to find additional commits of the same commit type within the body + of a single commit message. When squash commits are found, Python Semantic Release will separate + out each commit into its own artificial commit object and parse them individually. This + potentially can change the resulting version bump if a larger bump was detected within the + squashed components. It also allows for the changelog and release notes to separately order and + display each commit as originally written. If this is not desired, you will need to update your + configuration to change the new setting to ``false``. + +* **parser-scipy**: Any issue resolution footers that the parser detects will now be removed from + the commit.descriptions[] list. Previously, the descriptions included all text from the commit + message but now that the parser pulls out the issue numbers the numbers will be included in the + commit.linked_issues tuple for user extraction in any changelog generation. + +* **parser-scipy**: Any release notice footer messages that the commit parser detects will now be + removed from the ``commit.descriptions[]`` list but maintained in and only in the + ``commit.notices[]`` list. Previously, the descriptions included all text from the commit message + but that was redundant as the default changelog now handles release notice footers in its own + section. + +* **parser-scipy**: Generally, a pull request or merge request number reference is included in the + subject line at the end within parentheses on some common VCS's like GitHub. PSR now looks for + this reference and extracts it into the ``commit.linked_merge_request`` and the + ``commit.linked_pull_request`` attributes of a commit object. Since this is now pulled out + individually, it is cleaner to remove this from the first line of the ``commit.descriptions`` list + (ie. the subject line) so that changelog macros do not have to replace the text but instead only + append a PR/MR link to the end of the line. The reference does maintain the PR/MR prefix indicator + (`#` or ``!``). + +* **parser-scipy**: The configuration setting ``commit_parser_options.ignore_merge_commits`` is now + set to ``true`` by default. The feature to ignore squash commits was introduced in ``v9.18.0`` and + was originally set to ``false`` to prevent unexpected results on a non-breaking update. The ignore + merge commits feature prevents additional unnecessary processing on a commit message that likely + will not match a commit message syntax. Most merge commits are syntactically pre-defined by Git or + Remote Version Control System (ex. GitHub, etc.) and do not follow a commit convention (nor should + they). The larger issue with merge commits is that they ultimately are a full copy of all the + changes that were previously created and committed. The merge commit itself ensures that the + previous commit tree is maintained in history, therefore the commit message always exists. If + merge commits are parsed, it generally creates duplicate messages that will end up in your + changelog, which is less than desired in most cases. If you have previously used the + ``changelog.exclude_commit_patterns`` functionality to ignore merge commit messages then you will + want this setting set to ``true`` to improve parsing speed. You can also now remove the merge + commit exclude pattern from the list as well to improve parsing speed. If this functionality is + not desired, you will need to update your configuration to change the new setting to ``false``. + +* **parser-scipy**: The configuration setting ``commit_parser_options.parse_squash_commits`` is now + set to ``true`` by default. The feature to parse squash commits was introduced in ``v9.17.0`` and + was originally set to ``false`` to prevent unexpected results on a non-breaking update. The parse + squash commits feature attempts to find additional commits of the same commit type within the body + of a single commit message. When squash commits are found, Python Semantic Release will separate + out each commit into its own artificial commit object and parse them individually. This + potentially can change the resulting version bump if a larger bump was detected within the + squashed components. It also allows for the changelog and release notes to separately order and + display each commit as originally written. If this is not desired, you will need to update your + configuration to change the new setting to ``false``. + +.. _#733: https://github.com/python-semantic-release/python-semantic-release/issues/733 +.. _080e4bc: https://github.com/python-semantic-release/python-semantic-release/commit/080e4bcb14048a2dd10445546a7ee3159b3ab85c +.. _0bed906: https://github.com/python-semantic-release/python-semantic-release/commit/0bed9069df67ae806ad0a15f8434ac4efcc6ba31 +.. _0e114c3: https://github.com/python-semantic-release/python-semantic-release/commit/0e114c3458a24b87bfd2d6b0cd3f5cfdc9497084 +.. _16465f1: https://github.com/python-semantic-release/python-semantic-release/commit/16465f133386b09627d311727a6f8d24dd8f174f +.. _203d29d: https://github.com/python-semantic-release/python-semantic-release/commit/203d29d9d6b8e862eabe2f99dbd27eabf04e75e2 +.. _3cfee76: https://github.com/python-semantic-release/python-semantic-release/commit/3cfee76032662bda6fbdd7e2585193213e4f9da2 +.. _4aa6a6e: https://github.com/python-semantic-release/python-semantic-release/commit/4aa6a6edbff75889e09f32f7cba52cb90c9fb626 +.. _4e52f4b: https://github.com/python-semantic-release/python-semantic-release/commit/4e52f4bba46e96a4762f97d306f15ae52c5cea1b +.. _4ea92ec: https://github.com/python-semantic-release/python-semantic-release/commit/4ea92ec34dcd45d8cbab24e38e55289617b2d728 +.. _514a922: https://github.com/python-semantic-release/python-semantic-release/commit/514a922fa87721e2500062dcae841bedd84dc1fe +.. _5310d0c: https://github.com/python-semantic-release/python-semantic-release/commit/5310d0c700840538f27874394b9964bf09cd69b1 +.. _58308e3: https://github.com/python-semantic-release/python-semantic-release/commit/58308e31bb6306aac3a985af01eb779dc923d3f0 +.. _59bf084: https://github.com/python-semantic-release/python-semantic-release/commit/59bf08440a15269afaac81d78dd03ee418f9fd6b +.. _5fb02ab: https://github.com/python-semantic-release/python-semantic-release/commit/5fb02ab6e3b8278ecbf92ed35083ffb595bc19b8 +.. _634fffe: https://github.com/python-semantic-release/python-semantic-release/commit/634fffea29157e9b6305b21802c78ac245454265 +.. _67b2ae0: https://github.com/python-semantic-release/python-semantic-release/commit/67b2ae0050cce540a4126fe280cca6dc4bcf5d3f +.. _6aad7f1: https://github.com/python-semantic-release/python-semantic-release/commit/6aad7f17e64fb4717ddd7a9e94d2a730be6a3bd9 +.. _6cd0fbe: https://github.com/python-semantic-release/python-semantic-release/commit/6cd0fbeb44e16d394c210216c7099afa51f5a4a3 +.. _6fcdc99: https://github.com/python-semantic-release/python-semantic-release/commit/6fcdc99e9462b1186ea9488fc14e4e18f8c7fdb3 +.. _731466f: https://github.com/python-semantic-release/python-semantic-release/commit/731466fec4e06fe71f6c4addd4ae2ec2182ae9c1 +.. _7d39e76: https://github.com/python-semantic-release/python-semantic-release/commit/7d39e7675f859463b54751d59957b869d5d8395c +.. _7e8dc13: https://github.com/python-semantic-release/python-semantic-release/commit/7e8dc13c0b048a95d01f7aecfbe4eeedcddec9a4 +.. _8a51525: https://github.com/python-semantic-release/python-semantic-release/commit/8a5152573b9175f01be06d0c4531ea0ca4de8dd4 +.. _8bed5bc: https://github.com/python-semantic-release/python-semantic-release/commit/8bed5bcca4a5759af0e3fb24eadf14aa4e4f53c9 +.. _a082896: https://github.com/python-semantic-release/python-semantic-release/commit/a08289693085153effdafe3c6ff235a1777bb1fa +.. _a5f5e04: https://github.com/python-semantic-release/python-semantic-release/commit/a5f5e042ae9af909ee9e3ddf57c78adbc92ce378 +.. _b1bb0e5: https://github.com/python-semantic-release/python-semantic-release/commit/b1bb0e55910715754eebef6cb5b21ebed5ee8d68 +.. _b271cbb: https://github.com/python-semantic-release/python-semantic-release/commit/b271cbb2d3e8b86d07d1358b2e7424ccff6ae186 +.. _b6307cb: https://github.com/python-semantic-release/python-semantic-release/commit/b6307cb649043bbcc7ad9f15ac5ac6728914f443 +.. _b757603: https://github.com/python-semantic-release/python-semantic-release/commit/b757603e77ebe26d8a14758d78fd21163a9059b2 +.. _bd3e7bf: https://github.com/python-semantic-release/python-semantic-release/commit/bd3e7bfa86d53a03f03ac419399847712c523b02 +.. _c4d45ec: https://github.com/python-semantic-release/python-semantic-release/commit/c4d45ec46dfa81f645c25ea18ffffe9635922603 +.. _c6b6eab: https://github.com/python-semantic-release/python-semantic-release/commit/c6b6eabbfe100d2c741620eb3fa12a382531fa94 +.. _d4f128e: https://github.com/python-semantic-release/python-semantic-release/commit/d4f128e75e33256c0163fbb475c7c41e18f65147 +.. _da4140f: https://github.com/python-semantic-release/python-semantic-release/commit/da4140f3e3a2ed03c05064f35561b4584f517105 +.. _e7ac155: https://github.com/python-semantic-release/python-semantic-release/commit/e7ac155a91fc2e735d3cbf9b66fb4e5ff40a1466 +.. _eed63fa: https://github.com/python-semantic-release/python-semantic-release/commit/eed63fa9f6e762f55700fc85ef3ebdc0d3144f21 +.. _fb3da27: https://github.com/python-semantic-release/python-semantic-release/commit/fb3da27650ff15bcdb3b7badc919bd8a9a73238d +.. _PR#1244: https://github.com/python-semantic-release/python-semantic-release/pull/1244 +.. _PR#1245: https://github.com/python-semantic-release/python-semantic-release/pull/1245 + + .. _changelog-v9.21.1: v9.21.1 (2025-05-05) diff --git a/docs/configuration/automatic-releases/github-actions.rst b/docs/configuration/automatic-releases/github-actions.rst index 94aafaf62..6e90dfc25 100644 --- a/docs/configuration/automatic-releases/github-actions.rst +++ b/docs/configuration/automatic-releases/github-actions.rst @@ -371,8 +371,8 @@ to the remote repository. This option is equivalent to adding either ``--push`` """""""""""""""" .. important:: - This option has been removed in version $NEW_VERSION and newer because of a - command injection vulnerability. Please update as to $NEW_RELEASE_TAG as soon + This option has been removed in version 10.0.0 and newer because of a + command injection vulnerability. Please update as to v10.0.0 as soon as possible. Additional options for the main ``semantic-release`` command, which will come @@ -382,7 +382,7 @@ before the :ref:`version ` subcommand. .. code:: yaml - - uses: python-semantic-release/python-semantic-release@v9.21.1 + - uses: python-semantic-release/python-semantic-release@v10.0.0 with: root_options: "-vv --noop" @@ -688,8 +688,8 @@ This is useful for testing the action without actually publishing anything. """""""""""""""" .. important:: - This option has been removed in version $NEW_VERSION and newer because of a - command injection vulnerability. Please update as to $NEW_RELEASE_TAG as soon + This option has been removed in version 10.0.0 and newer because of a + command injection vulnerability. Please update as to v10.0.0 as soon as possible. Additional options for the main ``semantic-release`` command, which will come @@ -699,7 +699,7 @@ before the :ref:`publish ` subcommand. .. code:: yaml - - uses: python-semantic-release/publish-action@v9.21.1 + - uses: python-semantic-release/publish-action@v10.0.0 with: root_options: "-vv --noop" @@ -873,14 +873,14 @@ to the GitHub Release Assets as well. - name: Action | Semantic Version Release id: release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.21.1 + uses: python-semantic-release/python-semantic-release@v10.0.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} git_committer_name: "github-actions" git_committer_email: "actions@users.noreply.github.com" - name: Publish | Upload to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.21.1 + uses: python-semantic-release/publish-action@v10.0.0 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -979,7 +979,7 @@ The equivalent GitHub Action configuration would be: - name: Action | Semantic Version Release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.21.1 + uses: python-semantic-release/python-semantic-release@v10.0.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} force: patch @@ -1038,14 +1038,14 @@ Publish Action. - name: Release submodule 1 id: release-submod-1 - uses: python-semantic-release/python-semantic-release@v9.21.1 + uses: python-semantic-release/python-semantic-release@v10.0.0 with: directory: ${{ env.SUBMODULE_1_DIR }} github_token: ${{ secrets.GITHUB_TOKEN }} - name: Release submodule 2 id: release-submod-2 - uses: python-semantic-release/python-semantic-release@v9.21.1 + uses: python-semantic-release/python-semantic-release@v10.0.0 with: directory: ${{ env.SUBMODULE_2_DIR }} github_token: ${{ secrets.GITHUB_TOKEN }} @@ -1057,7 +1057,7 @@ Publish Action. # ------------------------------------------------------------------- # - name: Publish | Upload package 1 to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.21.1 + uses: python-semantic-release/publish-action@v10.0.0 if: steps.release-submod-1.outputs.released == 'true' with: directory: ${{ env.SUBMODULE_1_DIR }} @@ -1065,7 +1065,7 @@ Publish Action. tag: ${{ steps.release-submod-1.outputs.tag }} - name: Publish | Upload package 2 to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.21.1 + uses: python-semantic-release/publish-action@v10.0.0 if: steps.release-submod-2.outputs.released == 'true' with: directory: ${{ env.SUBMODULE_2_DIR }} diff --git a/docs/configuration/configuration.rst b/docs/configuration/configuration.rst index 4d36852b7..89bfc7862 100644 --- a/docs/configuration/configuration.rst +++ b/docs/configuration/configuration.rst @@ -142,7 +142,7 @@ version to be ``1.0.0``, regardless of patch, minor, or major change level. Additionally, when ``allow_zero_version`` is set to ``false``, the :ref:`config-major_on_zero` setting is ignored. -*Default changed to ``false`` in $NEW_VERSION* +*Default changed to ``false`` in 10.0.0* **Default:** ``false`` @@ -390,7 +390,7 @@ is there to document? The message details can be found in the ``first_release.md.j2`` and ``first_release.rst.j2`` templates of the default changelog template directory. -*Default changed to ``true`` in $NEW_VERSION.* +*Default changed to ``true`` in 10.0.0.* **Default:** ``true`` @@ -664,7 +664,7 @@ The patterns in this list are treated as regular expressions. ``mode`` ******** -*Introduced in v9.10.0. Default changed to `update` in $NEW_VERSION.* +*Introduced in v9.10.0. Default changed to `update` in 10.0.0.* **Type:** ``Literal["init", "update"]`` @@ -1351,7 +1351,7 @@ The regular expression generated from the ``version_variables`` definition will: 2. The variable name defined by ``variable`` and the version must be separated by an operand symbol (``=``, ``:``, ``:=``, or ``@``). Whitespace is optional around - the symbol. As of $NEW_VERSION, a double-equals (``==``) operator is also supported + the symbol. As of 10.0.0, a double-equals (``==``) operator is also supported as a valid operand symbol. 3. The value of the variable must match a `SemVer`_ regular expression and can be diff --git a/pyproject.toml b/pyproject.toml index addaf110e..ec0bc1f8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.21.1" +version = "10.0.0" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } From 7338fcfb1fc6686738762ba332c30143b7df2208 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 25 May 2025 01:42:17 -0600 Subject: [PATCH 155/160] Revert "10.0.0" This reverts commit e06ee2876880c68ef90b11784bd2060adb9c5908. Revert commit because release & deploy process failed in CI. Must revert. --- CHANGELOG.rst | 357 ------------------ .../automatic-releases/github-actions.rst | 26 +- docs/configuration/configuration.rst | 8 +- pyproject.toml | 2 +- 4 files changed, 18 insertions(+), 375 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 61fa77784..5714ef3eb 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,363 +4,6 @@ CHANGELOG ========= -.. _changelog-v10.0.0: - -v10.0.0 (2025-05-25) -==================== - -✨ Features ------------ - -* **cmd-version**: Enable ``version_variables`` version stamp of vars with double-equals - (`PR#1244`_, `080e4bc`_) - -* **parser-conventional**: Set parser to evaluate all squashed commits by default (`6fcdc99`_) - -* **parser-conventional**: Set parser to ignore merge commits by default (`59bf084`_) - -* **parser-emoji**: Set parser to evaluate all squashed commits by default (`514a922`_) - -* **parser-emoji**: Set parser to ignore merge commits by default (`8a51525`_) - -* **parser-scipy**: Set parser to evaluate all squashed commits by default (`634fffe`_) - -* **parser-scipy**: Set parser to ignore merge commits by default (`d4f128e`_) - -🪲 Bug Fixes ------------- - -* **changelog-md**: Change to 1-line descriptions in markdown template, closes `#733`_ (`e7ac155`_) - -* **changelog-rst**: Change to 1-line descriptions in the default ReStructuredText template, closes - `#733`_ (`731466f`_) - -* **cli**: Adjust verbosity parameter to enable silly-level logging (`bd3e7bf`_) - -* **github-action**: Resolve command injection vulnerability in action script (`fb3da27`_) - -* **parser-conventional**: Remove breaking change footer messages from commit descriptions - (`b271cbb`_) - -* **parser-conventional**: Remove issue footer messages from commit descriptions (`b1bb0e5`_) - -* **parser-conventional**: Remove PR/MR references from commit subject line (`eed63fa`_) - -* **parser-conventional**: Remove release notice footer messages from commit descriptions - (`7e8dc13`_) - -* **parser-emoji**: Remove issue footer messages from commit descriptions (`b757603`_) - -* **parser-emoji**: Remove PR/MR references from commit subject line (`16465f1`_) - -* **parser-emoji**: Remove release notice footer messages from commit descriptions (`b6307cb`_) - -* **parser-scipy**: Remove issue footer messages from commit descriptions (`3cfee76`_) - -* **parser-scipy**: Remove PR/MR references from commit subject line (`da4140f`_) - -* **parser-scipy**: Remove release notice footer messages from commit descriptions (`58308e3`_) - -📖 Documentation ----------------- - -* Refactor documentation page navigation (`4e52f4b`_) - -* **algorithm**: Remove out-of-date algorithm description (`6cd0fbe`_) - -* **commit-parsing**: Define limitation of revert commits with the scipy parser (`5310d0c`_) - -* **configuration**: Change default value for ``allow_zero_version`` in the description (`203d29d`_) - -* **configuration**: Change the default for the base changelog's ``mask_initial_release`` value - (`5fb02ab`_) - -* **configuration**: Change the default value for ``changelog.mode`` in the setting description - (`0bed906`_) - -* **configuration**: Update ``version_variables`` section to include double-equals operand support - (`PR#1244`_, `080e4bc`_) - -* **contributing**: Refactor contributing & contributors layout (`8bed5bc`_) - -* **github-actions**: Add reference to manual release workflow example (`6aad7f1`_) - -* **github-actions**: Change recommended workflow to separate release from deploy (`67b2ae0`_) - -* **github-actions**: Update ``python-semantic-release/publish-action`` parameter notes (`c4d45ec`_) - -* **github-actions**: Update PSR action parameter documenation (`a082896`_) - -* **upgrading**: Re-locate version upgrade guides into ``Upgrading PSR`` (`a5f5e04`_) - -* **upgrading-v10**: Added migration guide for v9 to v10 (`4ea92ec`_) - -⚙️ Build System ----------------- - -* **deps**: Prevent update to ``click@8.2.0`` (`PR#1245`_, `4aa6a6e`_) - -♻️ Refactoring ---------------- - -* **config**: Change ``allow_zero_version`` default to ``false`` (`c6b6eab`_) - -* **config**: Change ``changelog.default_templates.mask_initial_release`` default to ``true`` - (`0e114c3`_) - -* **config**: Change ``changelog.mode`` default to ``update`` (`7d39e76`_) - -💥 Breaking Changes -------------------- - -* **changelog-md**: The default Markdown changelog template and release notes template will no - longer print out the entire commit message contents, instead, it will only print the commit - subject line. This comes to meet the high demand of better formatted changelogs and requests for - subject line only. Originally, it was a decision to not hide commit subjects that were included in - the commit body via the ``git merge --squash`` command and PSR did not have another alternative. - At this point, all the built-in parsers have the ability to parse squashed commits and separate - them out into their own entry on the changelog. Therefore, the default template no longer needs to - write out the full commit body. See the commit parser options if you want to enable/disable - parsing squash commits. - -* **changelog-rst**: The default ReStructured changelog template will no longer print out the entire - commit message contents, instead, it will only print the commit subject line. This comes to meet - the high demand of better formatted changelogs and requests for subject line only. Originally, it - was a decision to not hide commit subjects that were included in the commit body via the ``git - merge --squash`` command and PSR did not have another alternative. At this point, all the built-in - parsers have the ability to parse squashed commits and separate them out into their own entry on - the changelog. Therefore, the default template no longer needs to write out the full commit body. - See the commit parser options if you want to enable/disable parsing squash commits. - -* **config**: This release switches the ``allow_zero_version`` default to ``false``. This change is - to encourage less ``0.x`` releases as the default but rather allow the experienced developer to - choose when ``0.x`` is appropriate. There are way too many projects in the ecosystems that never - leave ``0.x`` and that is problematic for the industry tools that help auto-update based on - SemVer. We should strive for publishing usable tools and maintaining good forethought for when - compatibility must break. If your configuration already sets the ``allow_zero_version`` value, - this change will have no effect on your project. If you want to use ``0.x`` versions, from the - start then change ``allow_zero_version`` to ``true`` in your configuration. - -* **config**: This release switches the ``changelog.default_templates.mask_initial_release`` default - to ``true``. This change is intended to toggle better recommended outputs of the default - changelog. Conceptually, the very first release is hard to describe--one can only provide new - features as nothing exists yet for the end user. No changelog should be written as there is no - start point to compare the "changes" to. The recommendation instead is to only list a simple - message as ``Initial Release``. This is now the default for PSR when providing the very first - release (no pre-existing tags) in the changelog and release notes. If your configuration already - sets the ``changelog.default_templates.mask_initial_release`` value, then this change will have no - effect on your project. If you do NOT want to mask the first release information, then set - ``changelog.default_templates.mask_initial_release`` to ``false`` in your configuration. - -* **config**: This release switches the ``changelog.mode`` default to ``update``. In this mode, if a - changelog exists, PSR will update the changelog **IF AND ONLY IF** the configured insertion flag - exists in the changelog. The Changelog output will remain unchanged if no insertion flag exists. - The insertion flag may be configured with the ``changelog.insertion_flag`` setting. When upgrading - to ``v10``, you must add the insertion flag manually or you can just delete the changelog file and - run PSR's changelog generation and it will rebuild the changelog (similar to init mode) but it - will add the insertion flag. If your configuration already sets the ``changelog.mode`` value, then - this change will have no effect on your project. If you would rather the changelog be generated - from scratch every release, than set the ``changelog.mode`` value to ``init`` in your - configuration. - -* **github-action**: The ``root_options`` action input parameter has been removed because it created - a command injection vulernability for arbitrary code to execute within the container context of - the GitHub action if a command injection code was provided as part of the ``root_options`` - parameter string. To eliminate the vulnerability, each relevant option that can be provided to - ``semantic-release`` has been individually added as its own parameter and will be processed - individually to prevent command injection. Please review our `Github Actions Configuration`__ page - on the Python Semantic Release Documentation website to review the newly available configuration - options that replace the ``root_options`` parameter. - -* **parser-conventional**: Any breaking change footer messages that the conventional commit parser - detects will now be removed from the ``commit.descriptions[]`` list but maintained in and only in - the ``commit.breaking_descriptions[]`` list. Previously, the descriptions included all text from - the commit message but that was redundant as the default changelog now handles breaking change - footers in its own section. - -* **parser-conventional**: Any issue resolution footers that the parser detects will now be removed - from the ``commit.descriptions[]`` list. Previously, the descriptions included all text from the - commit message but now that the parser pulls out the issue numbers the numbers will be included in - the ``commit.linked_issues`` tuple for user extraction in any changelog generation. - -* **parser-conventional**: Any release notice footer messages that the commit parser detects will - now be removed from the ``commit.descriptions[]`` list but maintained in and only in the - ``commit.notices[]`` list. Previously, the descriptions included all text from the commit message - but that was redundant as the default changelog now handles release notice footers in its own - section. - -* **parser-conventional**: Generally, a pull request or merge request number reference is included - in the subject line at the end within parentheses on some common VCS's like GitHub. PSR now looks - for this reference and extracts it into the ``commit.linked_merge_request`` and the - ``commit.linked_pull_request`` attributes of a commit object. Since this is now pulled out - individually, it is cleaner to remove this from the first line of the ``commit.descriptions`` list - (ie. the subject line) so that changelog macros do not have to replace the text but instead only - append a PR/MR link to the end of the line. The reference does maintain the PR/MR prefix indicator - (`#` or ``!``). - -* **parser-conventional**: The configuration setting ``commit_parser_options.ignore_merge_commits`` - is now set to ``true`` by default. The feature to ignore squash commits was introduced in - ``v9.18.0`` and was originally set to ``false`` to prevent unexpected results on a non-breaking - update. The ignore merge commits feature prevents additional unnecessary processing on a commit - message that likely will not match a commit message syntax. Most merge commits are syntactically - pre-defined by Git or Remote Version Control System (ex. GitHub, etc.) and do not follow a commit - convention (nor should they). The larger issue with merge commits is that they ultimately are a - full copy of all the changes that were previously created and committed. The merge commit itself - ensures that the previous commit tree is maintained in history, therefore the commit message - always exists. If merge commits are parsed, it generally creates duplicate messages that will end - up in your changelog, which is less than desired in most cases. If you have previously used the - ``changelog.exclude_commit_patterns`` functionality to ignore merge commit messages then you will - want this setting set to ``true`` to improve parsing speed. You can also now remove the merge - commit exclude pattern from the list as well to improve parsing speed. If this functionality is - not desired, you will need to update your configuration to change the new setting to ``false``. - -* **parser-conventional**: The configuration setting ``commit_parser_options.parse_squash_commits`` - is now set to ``true`` by default. The feature to parse squash commits was introduced in - ``v9.17.0`` and was originally set to ``false`` to prevent unexpected results on a non-breaking - update. The parse squash commits feature attempts to find additional commits of the same commit - type within the body of a single commit message. When squash commits are found, Python Semantic - Release will separate out each commit into its own artificial commit object and parse them - individually. This potentially can change the resulting version bump if a larger bump was detected - within the squashed components. It also allows for the changelog and release notes to separately - order and display each commit as originally written. If this is not desired, you will need to - update your configuration to change the new setting to ``false``. - -* **parser-emoji**: Any issue resolution footers that the parser detects will now be removed from - the ``commit.descriptions[]`` list. Previously, the descriptions included all text from the commit - message but now that the parser pulls out the issue numbers the numbers will be included in the - ``commit.linked_issues`` tuple for user extraction in any changelog generation. - -* **parser-emoji**: Any release notice footer messages that the emoji commit parser detects will now - be removed from the ``commit.descriptions[]`` list but maintained in and only in the - ``commit.notices[]`` list. Previously, the descriptions included all text from the commit message - but that was redundant as the default changelog now handles release notice footers in its own - section. - -* **parser-emoji**: Generally, a pull request or merge request number reference is included in the - subject line at the end within parentheses on some common VCS's (e.g. GitHub). PSR now looks for - these references and extract it into the ``commit.linked_merge_request`` field of a commit object. - Since this is now pulled out individually, it is cleaner to remove this from the first line of the - ``commit.descriptions`` list (ie. the subject line) so that changelog macros do not have to - replace the text but instead only append a PR/MR link to the end of the line. The reference will - maintain the PR/MR prefix indicator (e.g. ``#`` or ``!``). - -* **parser-emoji**: The configuration setting ``commit_parser_options.ignore_merge_commits`` is now - set to ``true`` by default. The feature to ignore squash commits was introduced in ``v9.18.0`` and - was originally set to ``false`` to prevent unexpected results on a non-breaking update. The ignore - merge commits feature prevents additional unnecessary processing on a commit message that likely - will not match a commit message syntax. Most merge commits are syntactically pre-defined by Git or - Remote Version Control System (ex. GitHub, etc.) and do not follow a commit convention (nor should - they). The larger issue with merge commits is that they ultimately are a full copy of all the - changes that were previously created and committed. The merge commit itself ensures that the - previous commit tree is maintained in history, therefore the commit message always exists. If - merge commits are parsed, it generally creates duplicate messages that will end up in your - changelog, which is less than desired in most cases. If you have previously used the - ``changelog.exclude_commit_patterns`` functionality to ignore merge commit messages then you will - want this setting set to ``true`` to improve parsing speed. You can also now remove the merge - commit exclude pattern from the list as well to improve parsing speed. If this functionality is - not desired, you will need to update your configuration to change the new setting to ``false``. - -* **parser-emoji**: The configuration setting ``commit_parser_options.parse_squash_commits`` is now - set to ``true`` by default. The feature to parse squash commits was introduced in ``v9.17.0`` and - was originally set to ``false`` to prevent unexpected results on a non-breaking update. The parse - squash commits feature attempts to find additional commits of the same commit type within the body - of a single commit message. When squash commits are found, Python Semantic Release will separate - out each commit into its own artificial commit object and parse them individually. This - potentially can change the resulting version bump if a larger bump was detected within the - squashed components. It also allows for the changelog and release notes to separately order and - display each commit as originally written. If this is not desired, you will need to update your - configuration to change the new setting to ``false``. - -* **parser-scipy**: Any issue resolution footers that the parser detects will now be removed from - the commit.descriptions[] list. Previously, the descriptions included all text from the commit - message but now that the parser pulls out the issue numbers the numbers will be included in the - commit.linked_issues tuple for user extraction in any changelog generation. - -* **parser-scipy**: Any release notice footer messages that the commit parser detects will now be - removed from the ``commit.descriptions[]`` list but maintained in and only in the - ``commit.notices[]`` list. Previously, the descriptions included all text from the commit message - but that was redundant as the default changelog now handles release notice footers in its own - section. - -* **parser-scipy**: Generally, a pull request or merge request number reference is included in the - subject line at the end within parentheses on some common VCS's like GitHub. PSR now looks for - this reference and extracts it into the ``commit.linked_merge_request`` and the - ``commit.linked_pull_request`` attributes of a commit object. Since this is now pulled out - individually, it is cleaner to remove this from the first line of the ``commit.descriptions`` list - (ie. the subject line) so that changelog macros do not have to replace the text but instead only - append a PR/MR link to the end of the line. The reference does maintain the PR/MR prefix indicator - (`#` or ``!``). - -* **parser-scipy**: The configuration setting ``commit_parser_options.ignore_merge_commits`` is now - set to ``true`` by default. The feature to ignore squash commits was introduced in ``v9.18.0`` and - was originally set to ``false`` to prevent unexpected results on a non-breaking update. The ignore - merge commits feature prevents additional unnecessary processing on a commit message that likely - will not match a commit message syntax. Most merge commits are syntactically pre-defined by Git or - Remote Version Control System (ex. GitHub, etc.) and do not follow a commit convention (nor should - they). The larger issue with merge commits is that they ultimately are a full copy of all the - changes that were previously created and committed. The merge commit itself ensures that the - previous commit tree is maintained in history, therefore the commit message always exists. If - merge commits are parsed, it generally creates duplicate messages that will end up in your - changelog, which is less than desired in most cases. If you have previously used the - ``changelog.exclude_commit_patterns`` functionality to ignore merge commit messages then you will - want this setting set to ``true`` to improve parsing speed. You can also now remove the merge - commit exclude pattern from the list as well to improve parsing speed. If this functionality is - not desired, you will need to update your configuration to change the new setting to ``false``. - -* **parser-scipy**: The configuration setting ``commit_parser_options.parse_squash_commits`` is now - set to ``true`` by default. The feature to parse squash commits was introduced in ``v9.17.0`` and - was originally set to ``false`` to prevent unexpected results on a non-breaking update. The parse - squash commits feature attempts to find additional commits of the same commit type within the body - of a single commit message. When squash commits are found, Python Semantic Release will separate - out each commit into its own artificial commit object and parse them individually. This - potentially can change the resulting version bump if a larger bump was detected within the - squashed components. It also allows for the changelog and release notes to separately order and - display each commit as originally written. If this is not desired, you will need to update your - configuration to change the new setting to ``false``. - -.. _#733: https://github.com/python-semantic-release/python-semantic-release/issues/733 -.. _080e4bc: https://github.com/python-semantic-release/python-semantic-release/commit/080e4bcb14048a2dd10445546a7ee3159b3ab85c -.. _0bed906: https://github.com/python-semantic-release/python-semantic-release/commit/0bed9069df67ae806ad0a15f8434ac4efcc6ba31 -.. _0e114c3: https://github.com/python-semantic-release/python-semantic-release/commit/0e114c3458a24b87bfd2d6b0cd3f5cfdc9497084 -.. _16465f1: https://github.com/python-semantic-release/python-semantic-release/commit/16465f133386b09627d311727a6f8d24dd8f174f -.. _203d29d: https://github.com/python-semantic-release/python-semantic-release/commit/203d29d9d6b8e862eabe2f99dbd27eabf04e75e2 -.. _3cfee76: https://github.com/python-semantic-release/python-semantic-release/commit/3cfee76032662bda6fbdd7e2585193213e4f9da2 -.. _4aa6a6e: https://github.com/python-semantic-release/python-semantic-release/commit/4aa6a6edbff75889e09f32f7cba52cb90c9fb626 -.. _4e52f4b: https://github.com/python-semantic-release/python-semantic-release/commit/4e52f4bba46e96a4762f97d306f15ae52c5cea1b -.. _4ea92ec: https://github.com/python-semantic-release/python-semantic-release/commit/4ea92ec34dcd45d8cbab24e38e55289617b2d728 -.. _514a922: https://github.com/python-semantic-release/python-semantic-release/commit/514a922fa87721e2500062dcae841bedd84dc1fe -.. _5310d0c: https://github.com/python-semantic-release/python-semantic-release/commit/5310d0c700840538f27874394b9964bf09cd69b1 -.. _58308e3: https://github.com/python-semantic-release/python-semantic-release/commit/58308e31bb6306aac3a985af01eb779dc923d3f0 -.. _59bf084: https://github.com/python-semantic-release/python-semantic-release/commit/59bf08440a15269afaac81d78dd03ee418f9fd6b -.. _5fb02ab: https://github.com/python-semantic-release/python-semantic-release/commit/5fb02ab6e3b8278ecbf92ed35083ffb595bc19b8 -.. _634fffe: https://github.com/python-semantic-release/python-semantic-release/commit/634fffea29157e9b6305b21802c78ac245454265 -.. _67b2ae0: https://github.com/python-semantic-release/python-semantic-release/commit/67b2ae0050cce540a4126fe280cca6dc4bcf5d3f -.. _6aad7f1: https://github.com/python-semantic-release/python-semantic-release/commit/6aad7f17e64fb4717ddd7a9e94d2a730be6a3bd9 -.. _6cd0fbe: https://github.com/python-semantic-release/python-semantic-release/commit/6cd0fbeb44e16d394c210216c7099afa51f5a4a3 -.. _6fcdc99: https://github.com/python-semantic-release/python-semantic-release/commit/6fcdc99e9462b1186ea9488fc14e4e18f8c7fdb3 -.. _731466f: https://github.com/python-semantic-release/python-semantic-release/commit/731466fec4e06fe71f6c4addd4ae2ec2182ae9c1 -.. _7d39e76: https://github.com/python-semantic-release/python-semantic-release/commit/7d39e7675f859463b54751d59957b869d5d8395c -.. _7e8dc13: https://github.com/python-semantic-release/python-semantic-release/commit/7e8dc13c0b048a95d01f7aecfbe4eeedcddec9a4 -.. _8a51525: https://github.com/python-semantic-release/python-semantic-release/commit/8a5152573b9175f01be06d0c4531ea0ca4de8dd4 -.. _8bed5bc: https://github.com/python-semantic-release/python-semantic-release/commit/8bed5bcca4a5759af0e3fb24eadf14aa4e4f53c9 -.. _a082896: https://github.com/python-semantic-release/python-semantic-release/commit/a08289693085153effdafe3c6ff235a1777bb1fa -.. _a5f5e04: https://github.com/python-semantic-release/python-semantic-release/commit/a5f5e042ae9af909ee9e3ddf57c78adbc92ce378 -.. _b1bb0e5: https://github.com/python-semantic-release/python-semantic-release/commit/b1bb0e55910715754eebef6cb5b21ebed5ee8d68 -.. _b271cbb: https://github.com/python-semantic-release/python-semantic-release/commit/b271cbb2d3e8b86d07d1358b2e7424ccff6ae186 -.. _b6307cb: https://github.com/python-semantic-release/python-semantic-release/commit/b6307cb649043bbcc7ad9f15ac5ac6728914f443 -.. _b757603: https://github.com/python-semantic-release/python-semantic-release/commit/b757603e77ebe26d8a14758d78fd21163a9059b2 -.. _bd3e7bf: https://github.com/python-semantic-release/python-semantic-release/commit/bd3e7bfa86d53a03f03ac419399847712c523b02 -.. _c4d45ec: https://github.com/python-semantic-release/python-semantic-release/commit/c4d45ec46dfa81f645c25ea18ffffe9635922603 -.. _c6b6eab: https://github.com/python-semantic-release/python-semantic-release/commit/c6b6eabbfe100d2c741620eb3fa12a382531fa94 -.. _d4f128e: https://github.com/python-semantic-release/python-semantic-release/commit/d4f128e75e33256c0163fbb475c7c41e18f65147 -.. _da4140f: https://github.com/python-semantic-release/python-semantic-release/commit/da4140f3e3a2ed03c05064f35561b4584f517105 -.. _e7ac155: https://github.com/python-semantic-release/python-semantic-release/commit/e7ac155a91fc2e735d3cbf9b66fb4e5ff40a1466 -.. _eed63fa: https://github.com/python-semantic-release/python-semantic-release/commit/eed63fa9f6e762f55700fc85ef3ebdc0d3144f21 -.. _fb3da27: https://github.com/python-semantic-release/python-semantic-release/commit/fb3da27650ff15bcdb3b7badc919bd8a9a73238d -.. _PR#1244: https://github.com/python-semantic-release/python-semantic-release/pull/1244 -.. _PR#1245: https://github.com/python-semantic-release/python-semantic-release/pull/1245 - - .. _changelog-v9.21.1: v9.21.1 (2025-05-05) diff --git a/docs/configuration/automatic-releases/github-actions.rst b/docs/configuration/automatic-releases/github-actions.rst index 6e90dfc25..e080d3415 100644 --- a/docs/configuration/automatic-releases/github-actions.rst +++ b/docs/configuration/automatic-releases/github-actions.rst @@ -371,8 +371,8 @@ to the remote repository. This option is equivalent to adding either ``--push`` """""""""""""""" .. important:: - This option has been removed in version 10.0.0 and newer because of a - command injection vulnerability. Please update as to v10.0.0 as soon + This option has been removed in $NEW_RELEASE_TAG and newer because of a + command injection vulnerability. Please update as to $NEW_RELEASE_TAG as soon as possible. Additional options for the main ``semantic-release`` command, which will come @@ -382,7 +382,7 @@ before the :ref:`version ` subcommand. .. code:: yaml - - uses: python-semantic-release/python-semantic-release@v10.0.0 + - uses: python-semantic-release/python-semantic-release@v9.21.1 with: root_options: "-vv --noop" @@ -688,8 +688,8 @@ This is useful for testing the action without actually publishing anything. """""""""""""""" .. important:: - This option has been removed in version 10.0.0 and newer because of a - command injection vulnerability. Please update as to v10.0.0 as soon + This option has been removed in $NEW_RELEASE_TAG and newer because of a + command injection vulnerability. Please update as to $NEW_RELEASE_TAG as soon as possible. Additional options for the main ``semantic-release`` command, which will come @@ -699,7 +699,7 @@ before the :ref:`publish ` subcommand. .. code:: yaml - - uses: python-semantic-release/publish-action@v10.0.0 + - uses: python-semantic-release/publish-action@v9.21.1 with: root_options: "-vv --noop" @@ -873,14 +873,14 @@ to the GitHub Release Assets as well. - name: Action | Semantic Version Release id: release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v10.0.0 + uses: python-semantic-release/python-semantic-release@v9.21.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} git_committer_name: "github-actions" git_committer_email: "actions@users.noreply.github.com" - name: Publish | Upload to GitHub Release Assets - uses: python-semantic-release/publish-action@v10.0.0 + uses: python-semantic-release/publish-action@v9.21.1 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -979,7 +979,7 @@ The equivalent GitHub Action configuration would be: - name: Action | Semantic Version Release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v10.0.0 + uses: python-semantic-release/python-semantic-release@v9.21.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} force: patch @@ -1038,14 +1038,14 @@ Publish Action. - name: Release submodule 1 id: release-submod-1 - uses: python-semantic-release/python-semantic-release@v10.0.0 + uses: python-semantic-release/python-semantic-release@v9.21.1 with: directory: ${{ env.SUBMODULE_1_DIR }} github_token: ${{ secrets.GITHUB_TOKEN }} - name: Release submodule 2 id: release-submod-2 - uses: python-semantic-release/python-semantic-release@v10.0.0 + uses: python-semantic-release/python-semantic-release@v9.21.1 with: directory: ${{ env.SUBMODULE_2_DIR }} github_token: ${{ secrets.GITHUB_TOKEN }} @@ -1057,7 +1057,7 @@ Publish Action. # ------------------------------------------------------------------- # - name: Publish | Upload package 1 to GitHub Release Assets - uses: python-semantic-release/publish-action@v10.0.0 + uses: python-semantic-release/publish-action@v9.21.1 if: steps.release-submod-1.outputs.released == 'true' with: directory: ${{ env.SUBMODULE_1_DIR }} @@ -1065,7 +1065,7 @@ Publish Action. tag: ${{ steps.release-submod-1.outputs.tag }} - name: Publish | Upload package 2 to GitHub Release Assets - uses: python-semantic-release/publish-action@v10.0.0 + uses: python-semantic-release/publish-action@v9.21.1 if: steps.release-submod-2.outputs.released == 'true' with: directory: ${{ env.SUBMODULE_2_DIR }} diff --git a/docs/configuration/configuration.rst b/docs/configuration/configuration.rst index 89bfc7862..4dc0f2e14 100644 --- a/docs/configuration/configuration.rst +++ b/docs/configuration/configuration.rst @@ -142,7 +142,7 @@ version to be ``1.0.0``, regardless of patch, minor, or major change level. Additionally, when ``allow_zero_version`` is set to ``false``, the :ref:`config-major_on_zero` setting is ignored. -*Default changed to ``false`` in 10.0.0* +*Default changed to ``false`` in $NEW_RELEASE_TAG* **Default:** ``false`` @@ -390,7 +390,7 @@ is there to document? The message details can be found in the ``first_release.md.j2`` and ``first_release.rst.j2`` templates of the default changelog template directory. -*Default changed to ``true`` in 10.0.0.* +*Default changed to ``true`` in $NEW_RELEASE_TAG.* **Default:** ``true`` @@ -664,7 +664,7 @@ The patterns in this list are treated as regular expressions. ``mode`` ******** -*Introduced in v9.10.0. Default changed to `update` in 10.0.0.* +*Introduced in v9.10.0. Default changed to `update` in $NEW_RELEASE_TAG.* **Type:** ``Literal["init", "update"]`` @@ -1351,7 +1351,7 @@ The regular expression generated from the ``version_variables`` definition will: 2. The variable name defined by ``variable`` and the version must be separated by an operand symbol (``=``, ``:``, ``:=``, or ``@``). Whitespace is optional around - the symbol. As of 10.0.0, a double-equals (``==``) operator is also supported + the symbol. As of $NEW_RELEASE_TAG, a double-equals (``==``) operator is also supported as a valid operand symbol. 3. The value of the variable must match a `SemVer`_ regular expression and can be diff --git a/pyproject.toml b/pyproject.toml index ec0bc1f8e..addaf110e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "10.0.0" +version = "9.21.1" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } From 092ace20f4ebed6a656da54b499076f1a5b803c8 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Sun, 25 May 2025 07:53:38 +0000 Subject: [PATCH 156/160] 10.0.0 Automatically generated by python-semantic-release --- CHANGELOG.rst | 357 ++++++++++++++++++ .../automatic-releases/github-actions.rst | 26 +- docs/configuration/configuration.rst | 8 +- pyproject.toml | 2 +- 4 files changed, 375 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5714ef3eb..61fa77784 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,363 @@ CHANGELOG ========= +.. _changelog-v10.0.0: + +v10.0.0 (2025-05-25) +==================== + +✨ Features +----------- + +* **cmd-version**: Enable ``version_variables`` version stamp of vars with double-equals + (`PR#1244`_, `080e4bc`_) + +* **parser-conventional**: Set parser to evaluate all squashed commits by default (`6fcdc99`_) + +* **parser-conventional**: Set parser to ignore merge commits by default (`59bf084`_) + +* **parser-emoji**: Set parser to evaluate all squashed commits by default (`514a922`_) + +* **parser-emoji**: Set parser to ignore merge commits by default (`8a51525`_) + +* **parser-scipy**: Set parser to evaluate all squashed commits by default (`634fffe`_) + +* **parser-scipy**: Set parser to ignore merge commits by default (`d4f128e`_) + +🪲 Bug Fixes +------------ + +* **changelog-md**: Change to 1-line descriptions in markdown template, closes `#733`_ (`e7ac155`_) + +* **changelog-rst**: Change to 1-line descriptions in the default ReStructuredText template, closes + `#733`_ (`731466f`_) + +* **cli**: Adjust verbosity parameter to enable silly-level logging (`bd3e7bf`_) + +* **github-action**: Resolve command injection vulnerability in action script (`fb3da27`_) + +* **parser-conventional**: Remove breaking change footer messages from commit descriptions + (`b271cbb`_) + +* **parser-conventional**: Remove issue footer messages from commit descriptions (`b1bb0e5`_) + +* **parser-conventional**: Remove PR/MR references from commit subject line (`eed63fa`_) + +* **parser-conventional**: Remove release notice footer messages from commit descriptions + (`7e8dc13`_) + +* **parser-emoji**: Remove issue footer messages from commit descriptions (`b757603`_) + +* **parser-emoji**: Remove PR/MR references from commit subject line (`16465f1`_) + +* **parser-emoji**: Remove release notice footer messages from commit descriptions (`b6307cb`_) + +* **parser-scipy**: Remove issue footer messages from commit descriptions (`3cfee76`_) + +* **parser-scipy**: Remove PR/MR references from commit subject line (`da4140f`_) + +* **parser-scipy**: Remove release notice footer messages from commit descriptions (`58308e3`_) + +📖 Documentation +---------------- + +* Refactor documentation page navigation (`4e52f4b`_) + +* **algorithm**: Remove out-of-date algorithm description (`6cd0fbe`_) + +* **commit-parsing**: Define limitation of revert commits with the scipy parser (`5310d0c`_) + +* **configuration**: Change default value for ``allow_zero_version`` in the description (`203d29d`_) + +* **configuration**: Change the default for the base changelog's ``mask_initial_release`` value + (`5fb02ab`_) + +* **configuration**: Change the default value for ``changelog.mode`` in the setting description + (`0bed906`_) + +* **configuration**: Update ``version_variables`` section to include double-equals operand support + (`PR#1244`_, `080e4bc`_) + +* **contributing**: Refactor contributing & contributors layout (`8bed5bc`_) + +* **github-actions**: Add reference to manual release workflow example (`6aad7f1`_) + +* **github-actions**: Change recommended workflow to separate release from deploy (`67b2ae0`_) + +* **github-actions**: Update ``python-semantic-release/publish-action`` parameter notes (`c4d45ec`_) + +* **github-actions**: Update PSR action parameter documenation (`a082896`_) + +* **upgrading**: Re-locate version upgrade guides into ``Upgrading PSR`` (`a5f5e04`_) + +* **upgrading-v10**: Added migration guide for v9 to v10 (`4ea92ec`_) + +⚙️ Build System +---------------- + +* **deps**: Prevent update to ``click@8.2.0`` (`PR#1245`_, `4aa6a6e`_) + +♻️ Refactoring +--------------- + +* **config**: Change ``allow_zero_version`` default to ``false`` (`c6b6eab`_) + +* **config**: Change ``changelog.default_templates.mask_initial_release`` default to ``true`` + (`0e114c3`_) + +* **config**: Change ``changelog.mode`` default to ``update`` (`7d39e76`_) + +💥 Breaking Changes +------------------- + +* **changelog-md**: The default Markdown changelog template and release notes template will no + longer print out the entire commit message contents, instead, it will only print the commit + subject line. This comes to meet the high demand of better formatted changelogs and requests for + subject line only. Originally, it was a decision to not hide commit subjects that were included in + the commit body via the ``git merge --squash`` command and PSR did not have another alternative. + At this point, all the built-in parsers have the ability to parse squashed commits and separate + them out into their own entry on the changelog. Therefore, the default template no longer needs to + write out the full commit body. See the commit parser options if you want to enable/disable + parsing squash commits. + +* **changelog-rst**: The default ReStructured changelog template will no longer print out the entire + commit message contents, instead, it will only print the commit subject line. This comes to meet + the high demand of better formatted changelogs and requests for subject line only. Originally, it + was a decision to not hide commit subjects that were included in the commit body via the ``git + merge --squash`` command and PSR did not have another alternative. At this point, all the built-in + parsers have the ability to parse squashed commits and separate them out into their own entry on + the changelog. Therefore, the default template no longer needs to write out the full commit body. + See the commit parser options if you want to enable/disable parsing squash commits. + +* **config**: This release switches the ``allow_zero_version`` default to ``false``. This change is + to encourage less ``0.x`` releases as the default but rather allow the experienced developer to + choose when ``0.x`` is appropriate. There are way too many projects in the ecosystems that never + leave ``0.x`` and that is problematic for the industry tools that help auto-update based on + SemVer. We should strive for publishing usable tools and maintaining good forethought for when + compatibility must break. If your configuration already sets the ``allow_zero_version`` value, + this change will have no effect on your project. If you want to use ``0.x`` versions, from the + start then change ``allow_zero_version`` to ``true`` in your configuration. + +* **config**: This release switches the ``changelog.default_templates.mask_initial_release`` default + to ``true``. This change is intended to toggle better recommended outputs of the default + changelog. Conceptually, the very first release is hard to describe--one can only provide new + features as nothing exists yet for the end user. No changelog should be written as there is no + start point to compare the "changes" to. The recommendation instead is to only list a simple + message as ``Initial Release``. This is now the default for PSR when providing the very first + release (no pre-existing tags) in the changelog and release notes. If your configuration already + sets the ``changelog.default_templates.mask_initial_release`` value, then this change will have no + effect on your project. If you do NOT want to mask the first release information, then set + ``changelog.default_templates.mask_initial_release`` to ``false`` in your configuration. + +* **config**: This release switches the ``changelog.mode`` default to ``update``. In this mode, if a + changelog exists, PSR will update the changelog **IF AND ONLY IF** the configured insertion flag + exists in the changelog. The Changelog output will remain unchanged if no insertion flag exists. + The insertion flag may be configured with the ``changelog.insertion_flag`` setting. When upgrading + to ``v10``, you must add the insertion flag manually or you can just delete the changelog file and + run PSR's changelog generation and it will rebuild the changelog (similar to init mode) but it + will add the insertion flag. If your configuration already sets the ``changelog.mode`` value, then + this change will have no effect on your project. If you would rather the changelog be generated + from scratch every release, than set the ``changelog.mode`` value to ``init`` in your + configuration. + +* **github-action**: The ``root_options`` action input parameter has been removed because it created + a command injection vulernability for arbitrary code to execute within the container context of + the GitHub action if a command injection code was provided as part of the ``root_options`` + parameter string. To eliminate the vulnerability, each relevant option that can be provided to + ``semantic-release`` has been individually added as its own parameter and will be processed + individually to prevent command injection. Please review our `Github Actions Configuration`__ page + on the Python Semantic Release Documentation website to review the newly available configuration + options that replace the ``root_options`` parameter. + +* **parser-conventional**: Any breaking change footer messages that the conventional commit parser + detects will now be removed from the ``commit.descriptions[]`` list but maintained in and only in + the ``commit.breaking_descriptions[]`` list. Previously, the descriptions included all text from + the commit message but that was redundant as the default changelog now handles breaking change + footers in its own section. + +* **parser-conventional**: Any issue resolution footers that the parser detects will now be removed + from the ``commit.descriptions[]`` list. Previously, the descriptions included all text from the + commit message but now that the parser pulls out the issue numbers the numbers will be included in + the ``commit.linked_issues`` tuple for user extraction in any changelog generation. + +* **parser-conventional**: Any release notice footer messages that the commit parser detects will + now be removed from the ``commit.descriptions[]`` list but maintained in and only in the + ``commit.notices[]`` list. Previously, the descriptions included all text from the commit message + but that was redundant as the default changelog now handles release notice footers in its own + section. + +* **parser-conventional**: Generally, a pull request or merge request number reference is included + in the subject line at the end within parentheses on some common VCS's like GitHub. PSR now looks + for this reference and extracts it into the ``commit.linked_merge_request`` and the + ``commit.linked_pull_request`` attributes of a commit object. Since this is now pulled out + individually, it is cleaner to remove this from the first line of the ``commit.descriptions`` list + (ie. the subject line) so that changelog macros do not have to replace the text but instead only + append a PR/MR link to the end of the line. The reference does maintain the PR/MR prefix indicator + (`#` or ``!``). + +* **parser-conventional**: The configuration setting ``commit_parser_options.ignore_merge_commits`` + is now set to ``true`` by default. The feature to ignore squash commits was introduced in + ``v9.18.0`` and was originally set to ``false`` to prevent unexpected results on a non-breaking + update. The ignore merge commits feature prevents additional unnecessary processing on a commit + message that likely will not match a commit message syntax. Most merge commits are syntactically + pre-defined by Git or Remote Version Control System (ex. GitHub, etc.) and do not follow a commit + convention (nor should they). The larger issue with merge commits is that they ultimately are a + full copy of all the changes that were previously created and committed. The merge commit itself + ensures that the previous commit tree is maintained in history, therefore the commit message + always exists. If merge commits are parsed, it generally creates duplicate messages that will end + up in your changelog, which is less than desired in most cases. If you have previously used the + ``changelog.exclude_commit_patterns`` functionality to ignore merge commit messages then you will + want this setting set to ``true`` to improve parsing speed. You can also now remove the merge + commit exclude pattern from the list as well to improve parsing speed. If this functionality is + not desired, you will need to update your configuration to change the new setting to ``false``. + +* **parser-conventional**: The configuration setting ``commit_parser_options.parse_squash_commits`` + is now set to ``true`` by default. The feature to parse squash commits was introduced in + ``v9.17.0`` and was originally set to ``false`` to prevent unexpected results on a non-breaking + update. The parse squash commits feature attempts to find additional commits of the same commit + type within the body of a single commit message. When squash commits are found, Python Semantic + Release will separate out each commit into its own artificial commit object and parse them + individually. This potentially can change the resulting version bump if a larger bump was detected + within the squashed components. It also allows for the changelog and release notes to separately + order and display each commit as originally written. If this is not desired, you will need to + update your configuration to change the new setting to ``false``. + +* **parser-emoji**: Any issue resolution footers that the parser detects will now be removed from + the ``commit.descriptions[]`` list. Previously, the descriptions included all text from the commit + message but now that the parser pulls out the issue numbers the numbers will be included in the + ``commit.linked_issues`` tuple for user extraction in any changelog generation. + +* **parser-emoji**: Any release notice footer messages that the emoji commit parser detects will now + be removed from the ``commit.descriptions[]`` list but maintained in and only in the + ``commit.notices[]`` list. Previously, the descriptions included all text from the commit message + but that was redundant as the default changelog now handles release notice footers in its own + section. + +* **parser-emoji**: Generally, a pull request or merge request number reference is included in the + subject line at the end within parentheses on some common VCS's (e.g. GitHub). PSR now looks for + these references and extract it into the ``commit.linked_merge_request`` field of a commit object. + Since this is now pulled out individually, it is cleaner to remove this from the first line of the + ``commit.descriptions`` list (ie. the subject line) so that changelog macros do not have to + replace the text but instead only append a PR/MR link to the end of the line. The reference will + maintain the PR/MR prefix indicator (e.g. ``#`` or ``!``). + +* **parser-emoji**: The configuration setting ``commit_parser_options.ignore_merge_commits`` is now + set to ``true`` by default. The feature to ignore squash commits was introduced in ``v9.18.0`` and + was originally set to ``false`` to prevent unexpected results on a non-breaking update. The ignore + merge commits feature prevents additional unnecessary processing on a commit message that likely + will not match a commit message syntax. Most merge commits are syntactically pre-defined by Git or + Remote Version Control System (ex. GitHub, etc.) and do not follow a commit convention (nor should + they). The larger issue with merge commits is that they ultimately are a full copy of all the + changes that were previously created and committed. The merge commit itself ensures that the + previous commit tree is maintained in history, therefore the commit message always exists. If + merge commits are parsed, it generally creates duplicate messages that will end up in your + changelog, which is less than desired in most cases. If you have previously used the + ``changelog.exclude_commit_patterns`` functionality to ignore merge commit messages then you will + want this setting set to ``true`` to improve parsing speed. You can also now remove the merge + commit exclude pattern from the list as well to improve parsing speed. If this functionality is + not desired, you will need to update your configuration to change the new setting to ``false``. + +* **parser-emoji**: The configuration setting ``commit_parser_options.parse_squash_commits`` is now + set to ``true`` by default. The feature to parse squash commits was introduced in ``v9.17.0`` and + was originally set to ``false`` to prevent unexpected results on a non-breaking update. The parse + squash commits feature attempts to find additional commits of the same commit type within the body + of a single commit message. When squash commits are found, Python Semantic Release will separate + out each commit into its own artificial commit object and parse them individually. This + potentially can change the resulting version bump if a larger bump was detected within the + squashed components. It also allows for the changelog and release notes to separately order and + display each commit as originally written. If this is not desired, you will need to update your + configuration to change the new setting to ``false``. + +* **parser-scipy**: Any issue resolution footers that the parser detects will now be removed from + the commit.descriptions[] list. Previously, the descriptions included all text from the commit + message but now that the parser pulls out the issue numbers the numbers will be included in the + commit.linked_issues tuple for user extraction in any changelog generation. + +* **parser-scipy**: Any release notice footer messages that the commit parser detects will now be + removed from the ``commit.descriptions[]`` list but maintained in and only in the + ``commit.notices[]`` list. Previously, the descriptions included all text from the commit message + but that was redundant as the default changelog now handles release notice footers in its own + section. + +* **parser-scipy**: Generally, a pull request or merge request number reference is included in the + subject line at the end within parentheses on some common VCS's like GitHub. PSR now looks for + this reference and extracts it into the ``commit.linked_merge_request`` and the + ``commit.linked_pull_request`` attributes of a commit object. Since this is now pulled out + individually, it is cleaner to remove this from the first line of the ``commit.descriptions`` list + (ie. the subject line) so that changelog macros do not have to replace the text but instead only + append a PR/MR link to the end of the line. The reference does maintain the PR/MR prefix indicator + (`#` or ``!``). + +* **parser-scipy**: The configuration setting ``commit_parser_options.ignore_merge_commits`` is now + set to ``true`` by default. The feature to ignore squash commits was introduced in ``v9.18.0`` and + was originally set to ``false`` to prevent unexpected results on a non-breaking update. The ignore + merge commits feature prevents additional unnecessary processing on a commit message that likely + will not match a commit message syntax. Most merge commits are syntactically pre-defined by Git or + Remote Version Control System (ex. GitHub, etc.) and do not follow a commit convention (nor should + they). The larger issue with merge commits is that they ultimately are a full copy of all the + changes that were previously created and committed. The merge commit itself ensures that the + previous commit tree is maintained in history, therefore the commit message always exists. If + merge commits are parsed, it generally creates duplicate messages that will end up in your + changelog, which is less than desired in most cases. If you have previously used the + ``changelog.exclude_commit_patterns`` functionality to ignore merge commit messages then you will + want this setting set to ``true`` to improve parsing speed. You can also now remove the merge + commit exclude pattern from the list as well to improve parsing speed. If this functionality is + not desired, you will need to update your configuration to change the new setting to ``false``. + +* **parser-scipy**: The configuration setting ``commit_parser_options.parse_squash_commits`` is now + set to ``true`` by default. The feature to parse squash commits was introduced in ``v9.17.0`` and + was originally set to ``false`` to prevent unexpected results on a non-breaking update. The parse + squash commits feature attempts to find additional commits of the same commit type within the body + of a single commit message. When squash commits are found, Python Semantic Release will separate + out each commit into its own artificial commit object and parse them individually. This + potentially can change the resulting version bump if a larger bump was detected within the + squashed components. It also allows for the changelog and release notes to separately order and + display each commit as originally written. If this is not desired, you will need to update your + configuration to change the new setting to ``false``. + +.. _#733: https://github.com/python-semantic-release/python-semantic-release/issues/733 +.. _080e4bc: https://github.com/python-semantic-release/python-semantic-release/commit/080e4bcb14048a2dd10445546a7ee3159b3ab85c +.. _0bed906: https://github.com/python-semantic-release/python-semantic-release/commit/0bed9069df67ae806ad0a15f8434ac4efcc6ba31 +.. _0e114c3: https://github.com/python-semantic-release/python-semantic-release/commit/0e114c3458a24b87bfd2d6b0cd3f5cfdc9497084 +.. _16465f1: https://github.com/python-semantic-release/python-semantic-release/commit/16465f133386b09627d311727a6f8d24dd8f174f +.. _203d29d: https://github.com/python-semantic-release/python-semantic-release/commit/203d29d9d6b8e862eabe2f99dbd27eabf04e75e2 +.. _3cfee76: https://github.com/python-semantic-release/python-semantic-release/commit/3cfee76032662bda6fbdd7e2585193213e4f9da2 +.. _4aa6a6e: https://github.com/python-semantic-release/python-semantic-release/commit/4aa6a6edbff75889e09f32f7cba52cb90c9fb626 +.. _4e52f4b: https://github.com/python-semantic-release/python-semantic-release/commit/4e52f4bba46e96a4762f97d306f15ae52c5cea1b +.. _4ea92ec: https://github.com/python-semantic-release/python-semantic-release/commit/4ea92ec34dcd45d8cbab24e38e55289617b2d728 +.. _514a922: https://github.com/python-semantic-release/python-semantic-release/commit/514a922fa87721e2500062dcae841bedd84dc1fe +.. _5310d0c: https://github.com/python-semantic-release/python-semantic-release/commit/5310d0c700840538f27874394b9964bf09cd69b1 +.. _58308e3: https://github.com/python-semantic-release/python-semantic-release/commit/58308e31bb6306aac3a985af01eb779dc923d3f0 +.. _59bf084: https://github.com/python-semantic-release/python-semantic-release/commit/59bf08440a15269afaac81d78dd03ee418f9fd6b +.. _5fb02ab: https://github.com/python-semantic-release/python-semantic-release/commit/5fb02ab6e3b8278ecbf92ed35083ffb595bc19b8 +.. _634fffe: https://github.com/python-semantic-release/python-semantic-release/commit/634fffea29157e9b6305b21802c78ac245454265 +.. _67b2ae0: https://github.com/python-semantic-release/python-semantic-release/commit/67b2ae0050cce540a4126fe280cca6dc4bcf5d3f +.. _6aad7f1: https://github.com/python-semantic-release/python-semantic-release/commit/6aad7f17e64fb4717ddd7a9e94d2a730be6a3bd9 +.. _6cd0fbe: https://github.com/python-semantic-release/python-semantic-release/commit/6cd0fbeb44e16d394c210216c7099afa51f5a4a3 +.. _6fcdc99: https://github.com/python-semantic-release/python-semantic-release/commit/6fcdc99e9462b1186ea9488fc14e4e18f8c7fdb3 +.. _731466f: https://github.com/python-semantic-release/python-semantic-release/commit/731466fec4e06fe71f6c4addd4ae2ec2182ae9c1 +.. _7d39e76: https://github.com/python-semantic-release/python-semantic-release/commit/7d39e7675f859463b54751d59957b869d5d8395c +.. _7e8dc13: https://github.com/python-semantic-release/python-semantic-release/commit/7e8dc13c0b048a95d01f7aecfbe4eeedcddec9a4 +.. _8a51525: https://github.com/python-semantic-release/python-semantic-release/commit/8a5152573b9175f01be06d0c4531ea0ca4de8dd4 +.. _8bed5bc: https://github.com/python-semantic-release/python-semantic-release/commit/8bed5bcca4a5759af0e3fb24eadf14aa4e4f53c9 +.. _a082896: https://github.com/python-semantic-release/python-semantic-release/commit/a08289693085153effdafe3c6ff235a1777bb1fa +.. _a5f5e04: https://github.com/python-semantic-release/python-semantic-release/commit/a5f5e042ae9af909ee9e3ddf57c78adbc92ce378 +.. _b1bb0e5: https://github.com/python-semantic-release/python-semantic-release/commit/b1bb0e55910715754eebef6cb5b21ebed5ee8d68 +.. _b271cbb: https://github.com/python-semantic-release/python-semantic-release/commit/b271cbb2d3e8b86d07d1358b2e7424ccff6ae186 +.. _b6307cb: https://github.com/python-semantic-release/python-semantic-release/commit/b6307cb649043bbcc7ad9f15ac5ac6728914f443 +.. _b757603: https://github.com/python-semantic-release/python-semantic-release/commit/b757603e77ebe26d8a14758d78fd21163a9059b2 +.. _bd3e7bf: https://github.com/python-semantic-release/python-semantic-release/commit/bd3e7bfa86d53a03f03ac419399847712c523b02 +.. _c4d45ec: https://github.com/python-semantic-release/python-semantic-release/commit/c4d45ec46dfa81f645c25ea18ffffe9635922603 +.. _c6b6eab: https://github.com/python-semantic-release/python-semantic-release/commit/c6b6eabbfe100d2c741620eb3fa12a382531fa94 +.. _d4f128e: https://github.com/python-semantic-release/python-semantic-release/commit/d4f128e75e33256c0163fbb475c7c41e18f65147 +.. _da4140f: https://github.com/python-semantic-release/python-semantic-release/commit/da4140f3e3a2ed03c05064f35561b4584f517105 +.. _e7ac155: https://github.com/python-semantic-release/python-semantic-release/commit/e7ac155a91fc2e735d3cbf9b66fb4e5ff40a1466 +.. _eed63fa: https://github.com/python-semantic-release/python-semantic-release/commit/eed63fa9f6e762f55700fc85ef3ebdc0d3144f21 +.. _fb3da27: https://github.com/python-semantic-release/python-semantic-release/commit/fb3da27650ff15bcdb3b7badc919bd8a9a73238d +.. _PR#1244: https://github.com/python-semantic-release/python-semantic-release/pull/1244 +.. _PR#1245: https://github.com/python-semantic-release/python-semantic-release/pull/1245 + + .. _changelog-v9.21.1: v9.21.1 (2025-05-05) diff --git a/docs/configuration/automatic-releases/github-actions.rst b/docs/configuration/automatic-releases/github-actions.rst index e080d3415..d8a8bd012 100644 --- a/docs/configuration/automatic-releases/github-actions.rst +++ b/docs/configuration/automatic-releases/github-actions.rst @@ -371,8 +371,8 @@ to the remote repository. This option is equivalent to adding either ``--push`` """""""""""""""" .. important:: - This option has been removed in $NEW_RELEASE_TAG and newer because of a - command injection vulnerability. Please update as to $NEW_RELEASE_TAG as soon + This option has been removed in v10.0.0 and newer because of a + command injection vulnerability. Please update as to v10.0.0 as soon as possible. Additional options for the main ``semantic-release`` command, which will come @@ -382,7 +382,7 @@ before the :ref:`version ` subcommand. .. code:: yaml - - uses: python-semantic-release/python-semantic-release@v9.21.1 + - uses: python-semantic-release/python-semantic-release@v10.0.0 with: root_options: "-vv --noop" @@ -688,8 +688,8 @@ This is useful for testing the action without actually publishing anything. """""""""""""""" .. important:: - This option has been removed in $NEW_RELEASE_TAG and newer because of a - command injection vulnerability. Please update as to $NEW_RELEASE_TAG as soon + This option has been removed in v10.0.0 and newer because of a + command injection vulnerability. Please update as to v10.0.0 as soon as possible. Additional options for the main ``semantic-release`` command, which will come @@ -699,7 +699,7 @@ before the :ref:`publish ` subcommand. .. code:: yaml - - uses: python-semantic-release/publish-action@v9.21.1 + - uses: python-semantic-release/publish-action@v10.0.0 with: root_options: "-vv --noop" @@ -873,14 +873,14 @@ to the GitHub Release Assets as well. - name: Action | Semantic Version Release id: release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.21.1 + uses: python-semantic-release/python-semantic-release@v10.0.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} git_committer_name: "github-actions" git_committer_email: "actions@users.noreply.github.com" - name: Publish | Upload to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.21.1 + uses: python-semantic-release/publish-action@v10.0.0 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -979,7 +979,7 @@ The equivalent GitHub Action configuration would be: - name: Action | Semantic Version Release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v9.21.1 + uses: python-semantic-release/python-semantic-release@v10.0.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} force: patch @@ -1038,14 +1038,14 @@ Publish Action. - name: Release submodule 1 id: release-submod-1 - uses: python-semantic-release/python-semantic-release@v9.21.1 + uses: python-semantic-release/python-semantic-release@v10.0.0 with: directory: ${{ env.SUBMODULE_1_DIR }} github_token: ${{ secrets.GITHUB_TOKEN }} - name: Release submodule 2 id: release-submod-2 - uses: python-semantic-release/python-semantic-release@v9.21.1 + uses: python-semantic-release/python-semantic-release@v10.0.0 with: directory: ${{ env.SUBMODULE_2_DIR }} github_token: ${{ secrets.GITHUB_TOKEN }} @@ -1057,7 +1057,7 @@ Publish Action. # ------------------------------------------------------------------- # - name: Publish | Upload package 1 to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.21.1 + uses: python-semantic-release/publish-action@v10.0.0 if: steps.release-submod-1.outputs.released == 'true' with: directory: ${{ env.SUBMODULE_1_DIR }} @@ -1065,7 +1065,7 @@ Publish Action. tag: ${{ steps.release-submod-1.outputs.tag }} - name: Publish | Upload package 2 to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.21.1 + uses: python-semantic-release/publish-action@v10.0.0 if: steps.release-submod-2.outputs.released == 'true' with: directory: ${{ env.SUBMODULE_2_DIR }} diff --git a/docs/configuration/configuration.rst b/docs/configuration/configuration.rst index 4dc0f2e14..08368b337 100644 --- a/docs/configuration/configuration.rst +++ b/docs/configuration/configuration.rst @@ -142,7 +142,7 @@ version to be ``1.0.0``, regardless of patch, minor, or major change level. Additionally, when ``allow_zero_version`` is set to ``false``, the :ref:`config-major_on_zero` setting is ignored. -*Default changed to ``false`` in $NEW_RELEASE_TAG* +*Default changed to ``false`` in v10.0.0* **Default:** ``false`` @@ -390,7 +390,7 @@ is there to document? The message details can be found in the ``first_release.md.j2`` and ``first_release.rst.j2`` templates of the default changelog template directory. -*Default changed to ``true`` in $NEW_RELEASE_TAG.* +*Default changed to ``true`` in v10.0.0.* **Default:** ``true`` @@ -664,7 +664,7 @@ The patterns in this list are treated as regular expressions. ``mode`` ******** -*Introduced in v9.10.0. Default changed to `update` in $NEW_RELEASE_TAG.* +*Introduced in v9.10.0. Default changed to `update` in v10.0.0.* **Type:** ``Literal["init", "update"]`` @@ -1351,7 +1351,7 @@ The regular expression generated from the ``version_variables`` definition will: 2. The variable name defined by ``variable`` and the version must be separated by an operand symbol (``=``, ``:``, ``:=``, or ``@``). Whitespace is optional around - the symbol. As of $NEW_RELEASE_TAG, a double-equals (``==``) operator is also supported + the symbol. As of v10.0.0, a double-equals (``==``) operator is also supported as a valid operand symbol. 3. The value of the variable must match a `SemVer`_ regular expression and can be diff --git a/pyproject.toml b/pyproject.toml index addaf110e..ec0bc1f8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.21.1" +version = "10.0.0" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } From e70325e2ad41fb16d33f0dfc179955a3c5d38604 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 25 May 2025 03:46:51 -0600 Subject: [PATCH 157/160] chore(changelog): update changelog spelling, links, & shorten breaking change descriptions (#1254) * chore(docs): fix references within docs --- CHANGELOG.rst | 193 +++++------------- docs/api/commands.rst | 2 +- .../automatic-releases/travis.rst | 2 +- 3 files changed, 54 insertions(+), 143 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 61fa77784..ec7decbe1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -89,7 +89,7 @@ v10.0.0 (2025-05-25) * **github-actions**: Update ``python-semantic-release/publish-action`` parameter notes (`c4d45ec`_) -* **github-actions**: Update PSR action parameter documenation (`a082896`_) +* **github-actions**: Update PSR action parameter documentation (`a082896`_) * **upgrading**: Re-locate version upgrade guides into ``Upgrading PSR`` (`a5f5e04`_) @@ -164,13 +164,14 @@ v10.0.0 (2025-05-25) configuration. * **github-action**: The ``root_options`` action input parameter has been removed because it created - a command injection vulernability for arbitrary code to execute within the container context of + a command injection vulnerability for arbitrary code to execute within the container context of the GitHub action if a command injection code was provided as part of the ``root_options`` parameter string. To eliminate the vulnerability, each relevant option that can be provided to ``semantic-release`` has been individually added as its own parameter and will be processed individually to prevent command injection. Please review our `Github Actions Configuration`__ page - on the Python Semantic Release Documentation website to review the newly available configuration - options that replace the ``root_options`` parameter. + to review the newly available configuration options that replace the ``root_options`` parameter. + + __ https://github.com/python-semantic-release/python-semantic-release/blob/v10.0.0/docs/configuration/automatic-releases/github-actions.rst * **parser-conventional**: Any breaking change footer messages that the conventional commit parser detects will now be removed from the ``commit.descriptions[]`` list but maintained in and only in @@ -178,146 +179,56 @@ v10.0.0 (2025-05-25) the commit message but that was redundant as the default changelog now handles breaking change footers in its own section. -* **parser-conventional**: Any issue resolution footers that the parser detects will now be removed - from the ``commit.descriptions[]`` list. Previously, the descriptions included all text from the - commit message but now that the parser pulls out the issue numbers the numbers will be included in - the ``commit.linked_issues`` tuple for user extraction in any changelog generation. - -* **parser-conventional**: Any release notice footer messages that the commit parser detects will - now be removed from the ``commit.descriptions[]`` list but maintained in and only in the - ``commit.notices[]`` list. Previously, the descriptions included all text from the commit message - but that was redundant as the default changelog now handles release notice footers in its own - section. - -* **parser-conventional**: Generally, a pull request or merge request number reference is included - in the subject line at the end within parentheses on some common VCS's like GitHub. PSR now looks - for this reference and extracts it into the ``commit.linked_merge_request`` and the - ``commit.linked_pull_request`` attributes of a commit object. Since this is now pulled out - individually, it is cleaner to remove this from the first line of the ``commit.descriptions`` list - (ie. the subject line) so that changelog macros do not have to replace the text but instead only - append a PR/MR link to the end of the line. The reference does maintain the PR/MR prefix indicator - (`#` or ``!``). - -* **parser-conventional**: The configuration setting ``commit_parser_options.ignore_merge_commits`` - is now set to ``true`` by default. The feature to ignore squash commits was introduced in - ``v9.18.0`` and was originally set to ``false`` to prevent unexpected results on a non-breaking - update. The ignore merge commits feature prevents additional unnecessary processing on a commit - message that likely will not match a commit message syntax. Most merge commits are syntactically - pre-defined by Git or Remote Version Control System (ex. GitHub, etc.) and do not follow a commit - convention (nor should they). The larger issue with merge commits is that they ultimately are a - full copy of all the changes that were previously created and committed. The merge commit itself - ensures that the previous commit tree is maintained in history, therefore the commit message - always exists. If merge commits are parsed, it generally creates duplicate messages that will end - up in your changelog, which is less than desired in most cases. If you have previously used the - ``changelog.exclude_commit_patterns`` functionality to ignore merge commit messages then you will - want this setting set to ``true`` to improve parsing speed. You can also now remove the merge - commit exclude pattern from the list as well to improve parsing speed. If this functionality is - not desired, you will need to update your configuration to change the new setting to ``false``. - -* **parser-conventional**: The configuration setting ``commit_parser_options.parse_squash_commits`` - is now set to ``true`` by default. The feature to parse squash commits was introduced in - ``v9.17.0`` and was originally set to ``false`` to prevent unexpected results on a non-breaking - update. The parse squash commits feature attempts to find additional commits of the same commit - type within the body of a single commit message. When squash commits are found, Python Semantic - Release will separate out each commit into its own artificial commit object and parse them - individually. This potentially can change the resulting version bump if a larger bump was detected - within the squashed components. It also allows for the changelog and release notes to separately - order and display each commit as originally written. If this is not desired, you will need to - update your configuration to change the new setting to ``false``. - -* **parser-emoji**: Any issue resolution footers that the parser detects will now be removed from - the ``commit.descriptions[]`` list. Previously, the descriptions included all text from the commit - message but now that the parser pulls out the issue numbers the numbers will be included in the - ``commit.linked_issues`` tuple for user extraction in any changelog generation. - -* **parser-emoji**: Any release notice footer messages that the emoji commit parser detects will now - be removed from the ``commit.descriptions[]`` list but maintained in and only in the - ``commit.notices[]`` list. Previously, the descriptions included all text from the commit message - but that was redundant as the default changelog now handles release notice footers in its own - section. - -* **parser-emoji**: Generally, a pull request or merge request number reference is included in the - subject line at the end within parentheses on some common VCS's (e.g. GitHub). PSR now looks for - these references and extract it into the ``commit.linked_merge_request`` field of a commit object. - Since this is now pulled out individually, it is cleaner to remove this from the first line of the - ``commit.descriptions`` list (ie. the subject line) so that changelog macros do not have to - replace the text but instead only append a PR/MR link to the end of the line. The reference will - maintain the PR/MR prefix indicator (e.g. ``#`` or ``!``). - -* **parser-emoji**: The configuration setting ``commit_parser_options.ignore_merge_commits`` is now - set to ``true`` by default. The feature to ignore squash commits was introduced in ``v9.18.0`` and - was originally set to ``false`` to prevent unexpected results on a non-breaking update. The ignore - merge commits feature prevents additional unnecessary processing on a commit message that likely - will not match a commit message syntax. Most merge commits are syntactically pre-defined by Git or - Remote Version Control System (ex. GitHub, etc.) and do not follow a commit convention (nor should - they). The larger issue with merge commits is that they ultimately are a full copy of all the - changes that were previously created and committed. The merge commit itself ensures that the - previous commit tree is maintained in history, therefore the commit message always exists. If - merge commits are parsed, it generally creates duplicate messages that will end up in your - changelog, which is less than desired in most cases. If you have previously used the - ``changelog.exclude_commit_patterns`` functionality to ignore merge commit messages then you will - want this setting set to ``true`` to improve parsing speed. You can also now remove the merge - commit exclude pattern from the list as well to improve parsing speed. If this functionality is - not desired, you will need to update your configuration to change the new setting to ``false``. - -* **parser-emoji**: The configuration setting ``commit_parser_options.parse_squash_commits`` is now - set to ``true`` by default. The feature to parse squash commits was introduced in ``v9.17.0`` and - was originally set to ``false`` to prevent unexpected results on a non-breaking update. The parse - squash commits feature attempts to find additional commits of the same commit type within the body - of a single commit message. When squash commits are found, Python Semantic Release will separate - out each commit into its own artificial commit object and parse them individually. This - potentially can change the resulting version bump if a larger bump was detected within the - squashed components. It also allows for the changelog and release notes to separately order and - display each commit as originally written. If this is not desired, you will need to update your - configuration to change the new setting to ``false``. +* **parser-conventional, parser-emoji, parser-scipy**: Any issue resolution footers that the parser + detects will now be removed from the ``commit.descriptions[]`` list. Previously, the descriptions + included all text from the commit message but now that the parser pulls out the issue numbers the + numbers will be included in the ``commit.linked_issues`` tuple for user extraction in any + changelog generation. -* **parser-scipy**: Any issue resolution footers that the parser detects will now be removed from - the commit.descriptions[] list. Previously, the descriptions included all text from the commit - message but now that the parser pulls out the issue numbers the numbers will be included in the - commit.linked_issues tuple for user extraction in any changelog generation. - -* **parser-scipy**: Any release notice footer messages that the commit parser detects will now be - removed from the ``commit.descriptions[]`` list but maintained in and only in the - ``commit.notices[]`` list. Previously, the descriptions included all text from the commit message - but that was redundant as the default changelog now handles release notice footers in its own - section. - -* **parser-scipy**: Generally, a pull request or merge request number reference is included in the - subject line at the end within parentheses on some common VCS's like GitHub. PSR now looks for - this reference and extracts it into the ``commit.linked_merge_request`` and the - ``commit.linked_pull_request`` attributes of a commit object. Since this is now pulled out - individually, it is cleaner to remove this from the first line of the ``commit.descriptions`` list - (ie. the subject line) so that changelog macros do not have to replace the text but instead only - append a PR/MR link to the end of the line. The reference does maintain the PR/MR prefix indicator - (`#` or ``!``). - -* **parser-scipy**: The configuration setting ``commit_parser_options.ignore_merge_commits`` is now - set to ``true`` by default. The feature to ignore squash commits was introduced in ``v9.18.0`` and - was originally set to ``false`` to prevent unexpected results on a non-breaking update. The ignore - merge commits feature prevents additional unnecessary processing on a commit message that likely - will not match a commit message syntax. Most merge commits are syntactically pre-defined by Git or - Remote Version Control System (ex. GitHub, etc.) and do not follow a commit convention (nor should - they). The larger issue with merge commits is that they ultimately are a full copy of all the - changes that were previously created and committed. The merge commit itself ensures that the - previous commit tree is maintained in history, therefore the commit message always exists. If - merge commits are parsed, it generally creates duplicate messages that will end up in your - changelog, which is less than desired in most cases. If you have previously used the - ``changelog.exclude_commit_patterns`` functionality to ignore merge commit messages then you will - want this setting set to ``true`` to improve parsing speed. You can also now remove the merge - commit exclude pattern from the list as well to improve parsing speed. If this functionality is - not desired, you will need to update your configuration to change the new setting to ``false``. - -* **parser-scipy**: The configuration setting ``commit_parser_options.parse_squash_commits`` is now - set to ``true`` by default. The feature to parse squash commits was introduced in ``v9.17.0`` and - was originally set to ``false`` to prevent unexpected results on a non-breaking update. The parse - squash commits feature attempts to find additional commits of the same commit type within the body - of a single commit message. When squash commits are found, Python Semantic Release will separate - out each commit into its own artificial commit object and parse them individually. This - potentially can change the resulting version bump if a larger bump was detected within the - squashed components. It also allows for the changelog and release notes to separately order and - display each commit as originally written. If this is not desired, you will need to update your +* **parser-conventional, parser-emoji, parser-scipy**: Any release notice footer messages that the + commit parser detects will now be removed from the ``commit.descriptions[]`` list but maintained + in and only in the ``commit.notices[]`` list. Previously, the descriptions included all text from + the commit message but that was redundant as the default changelog now handles release notice + footers in its own section. + +* **parser-conventional, parser-emoji, parser-scipy**: Generally, a pull request or merge request + number reference is included in the subject line at the end within parentheses on some common + VCS's like GitHub. PSR now looks for this reference and extracts it into the + ``commit.linked_merge_request`` and the ``commit.linked_pull_request`` attributes of a commit + object. Since this is now pulled out individually, it is cleaner to remove this from the first + line of the ``commit.descriptions`` list (ie. the subject line) so that changelog macros do not + have to replace the text but instead only append a PR/MR link to the end of the line. The + reference does maintain the PR/MR prefix indicator (`#` or ``!``). + +* **parser-conventional, parser-emoji, parser-scipy**: The configuration setting + ``commit_parser_options.ignore_merge_commits`` is now set to ``true`` by default. The feature to + ignore squash commits was introduced in ``v9.18.0`` and was originally set to ``false`` to + prevent unexpected results on a non-breaking update. The ignore merge commits feature prevents + additional unnecessary processing on a commit message that likely will not match a commit message + syntax. Most merge commits are syntactically pre-defined by Git or Remote Version Control System + (ex. GitHub, etc.) and do not follow a commit convention (nor should they). The larger issue with + merge commits is that they ultimately are a full copy of all the changes that were previously + created and committed. The merge commit itself ensures that the previous commit tree is + maintained in history, therefore the commit message always exists. If merge commits are parsed, + it generally creates duplicate messages that will end up in your changelog, which is less than + desired in most cases. If you have previously used the ``changelog.exclude_commit_patterns`` + functionality to ignore merge commit messages then you will want this setting set to ``true`` to + improve parsing speed. You can also now remove the merge commit exclude pattern from the list as + well to improve parsing speed. If this functionality is not desired, you will need to update your configuration to change the new setting to ``false``. +* **parser-conventional, parser-emoji, parser-scipy**: The configuration setting + ``commit_parser_options.parse_squash_commits`` is now set to ``true`` by default. The feature to + parse squash commits was introduced in ``v9.17.0`` and was originally set to ``false`` to prevent + unexpected results on a non-breaking update. The parse squash commits feature attempts to find + additional commits of the same commit type within the body of a single commit message. When + squash commits are found, Python Semantic Release will separate out each commit into its own + artificial commit object and parse them individually. This potentially can change the resulting + version bump if a larger bump was detected within the squashed components. It also allows for the + changelog and release notes to separately order and display each commit as originally written. If + this is not desired, you will need to update your configuration to change the new setting to + ``false``. + .. _#733: https://github.com/python-semantic-release/python-semantic-release/issues/733 .. _080e4bc: https://github.com/python-semantic-release/python-semantic-release/commit/080e4bcb14048a2dd10445546a7ee3159b3ab85c .. _0bed906: https://github.com/python-semantic-release/python-semantic-release/commit/0bed9069df67ae806ad0a15f8434ac4efcc6ba31 diff --git a/docs/api/commands.rst b/docs/api/commands.rst index d99a40152..344c7f5f4 100644 --- a/docs/api/commands.rst +++ b/docs/api/commands.rst @@ -496,4 +496,4 @@ corresponding release is found in the remote VCS, then Python Semantic Release w attempt to create one. If using this option, the relevant authentication token *must* be supplied via the -relevant environment variable. For more information, see :ref:`index-creating-vcs-releases`. +relevant environment variable. diff --git a/docs/configuration/automatic-releases/travis.rst b/docs/configuration/automatic-releases/travis.rst index 5be380975..60ee68ce8 100644 --- a/docs/configuration/automatic-releases/travis.rst +++ b/docs/configuration/automatic-releases/travis.rst @@ -18,7 +18,7 @@ You will need to set up an environment variable in Travis. An easy way to do tha is to go to the settings page for your package and add it there. Make sure that the secret toggle is set correctly. -You need to set the :ref:`GH_TOKEN ` environment +You need to set the :ref:`GH_TOKEN ` environment variable with a personal access token for Github. It will need either ``repo`` or ``public_repo`` scope depending on whether the repository is private or public. From bba903ce21306059aebc5c0e8cd4e677a808ff7e Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 25 May 2025 03:34:06 -0600 Subject: [PATCH 158/160] ci(deps): bump `python-semantic-release@v9.21.0` action to `v10.0.0` --- .github/workflows/cicd.yml | 4 ++-- .github/workflows/validate.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 358dff3ab..2dc956b73 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -145,10 +145,10 @@ jobs: - name: Release | Python Semantic Release id: release - uses: python-semantic-release/python-semantic-release@v9.21.1 + uses: python-semantic-release/python-semantic-release@092ace20f4ebed6a656da54b499076f1a5b803c8 # v10.0.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} - root_options: "-v" + verbosity: 1 build: false - name: Release | Add distribution artifacts to GitHub Release Assets diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index a55e468fd..1e93ff0d6 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -112,10 +112,10 @@ jobs: - name: Build | Build next version artifacts id: version - uses: python-semantic-release/python-semantic-release@v9.21.1 + uses: python-semantic-release/python-semantic-release@092ace20f4ebed6a656da54b499076f1a5b803c8 # v10.0.0 with: github_token: "" - root_options: "-v" + verbosity: 1 build: true changelog: true commit: false From 96a9503d4e33bb23290760c79e341f60dd767761 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 25 May 2025 09:17:03 +0000 Subject: [PATCH 159/160] ci(deps): bump `python-semantic-release/publish-action@v9.21.1` to `v10.0.0` --- .github/workflows/cicd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 2dc956b73..8c8b45c45 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -152,7 +152,7 @@ jobs: build: false - name: Release | Add distribution artifacts to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.21.1 + uses: python-semantic-release/publish-action@d62706ce15a7c98325c51a3e5cc789fdbe843e5a # v10.0.0 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} From 2803676cf26c52177fa98d9144934853744a22bb Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 25 May 2025 03:53:27 -0600 Subject: [PATCH 160/160] fix(github-actions): bump the github-actions dependency to `v10.0.0` (#1255) --- src/gh_action/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gh_action/requirements.txt b/src/gh_action/requirements.txt index 1aae03384..2fabec60d 100644 --- a/src/gh_action/requirements.txt +++ b/src/gh_action/requirements.txt @@ -1 +1 @@ -python-semantic-release == 9.21.1 +python-semantic-release == 10.0.0