Skip to content

Navigation Menu

Sign in
Appearance settings

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

Provide feedback

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

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Feat/add release url filters #1161

New issue

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

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

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions 10 config/release-templates/.release_notes.md.j2
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
)
}}
74 changes: 59 additions & 15 deletions 74 docs/changelog_templates.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 <https://jinja.palletsprojects.com/en/3.1.x/templates/#filters>`_
Expand Down
15 changes: 15 additions & 0 deletions 15 src/semantic_release/hvcs/bitbucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -216,13 +217,27 @@ 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,
self.create_repo_url,
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:
Expand Down
21 changes: 21 additions & 0 deletions 21 src/semantic_release/hvcs/gitea.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -380,13 +381,33 @@ 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,
self.create_repo_url,
self.commit_hash_url,
self.issue_url,
self.pull_request_url,
self.create_release_url,
self.format_w_official_vcs_name,
)


Expand Down
21 changes: 21 additions & 0 deletions 21 src/semantic_release/hvcs/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down Expand Up @@ -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,
Expand All @@ -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,
)


Expand Down
20 changes: 20 additions & 0 deletions 20 src/semantic_release/hvcs/gitlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__(
Expand Down Expand Up @@ -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,
Expand All @@ -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,
)


Expand Down
91 changes: 91 additions & 0 deletions 91 tests/unit/semantic_release/changelog/test_changelog_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Loading
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.