Skip to content

Commit

Permalink
refactor: Remove the need for parsing the template dir of the python-…
Browse files Browse the repository at this point in the history
…semantic-release config by requiring the user to pass in the full path to the files to use in the templates.
  • Loading branch information
nfelt14 committed Aug 26, 2024
1 parent 38c689c commit 630c3e3
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 146 deletions.
32 changes: 14 additions & 18 deletions .github/workflows/_reusable-package-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,15 @@ on:
required: false
default: '["ubuntu", "windows", "macos"]'
type: string
previous-changelog-filename:
description: The name of the file to copy the contents of the changelog into
for use in the `python-semantic-release` templates. This file will be created
inside of the directory defined by the `[tool.semantic_release.changelog.template_dir]`
key in the `pyproject.toml` file.
previous-changelog-filepath:
description: The full path of the file to copy the contents of the changelog
into for use in the `python-semantic-release` templates.
required: false
type: string
default: .previous_changelog_for_template.md
previous-release-notes-filename:
description: The name of the file to copy the contents of the `## Unreleased`
section of the changelog into for use in the GitHub Release Notes. This
file will be created inside of the directory defined by the `[tool.semantic_release.changelog.template_dir]`
key in the `pyproject.toml` file.
previous-release-notes-filepath:
description: The full path of the file to copy the contents of the `## Unreleased`
section of the changelog into for use in the GitHub Release Notes.
required: false
type: string
default: .previous_release_notes_for_template.md
Expand Down Expand Up @@ -97,14 +93,14 @@ jobs:
uses: ./actions/find_unreleased_changelog_items
with:
release-level: ${{ inputs.release-level }}
previous-changelog-filename: ${{ inputs.previous-changelog-filename }}
previous-release-notes-filename: ${{ inputs.previous-release-notes-filename }}
previous-changelog-filepath: ${{ inputs.previous-changelog-filepath }}
previous-release-notes-filepath: ${{ inputs.previous-release-notes-filepath }}
- if: ${{ !endsWith(github.repository, '/python-package-ci-cd') }} # Run the public action when this is run outside the python-package-ci-cd repository
uses: tektronix/python-package-ci-cd/actions/[email protected]
with:
release-level: ${{ inputs.release-level }}
previous-changelog-filename: ${{ inputs.previous-changelog-filename }}
previous-release-notes-filename: ${{ inputs.previous-release-notes-filename }}
previous-changelog-filepath: ${{ inputs.previous-changelog-filepath }}
previous-release-notes-filepath: ${{ inputs.previous-release-notes-filepath }}
# Update the package version using the python-semantic-release package (https://github.com/python-semantic-release/python-semantic-release)
# This job requires a Personal Access Token (Classic) with
# the public_repo permission. It also needs a private/public
Expand All @@ -127,13 +123,13 @@ jobs:
- if: ${{ endsWith(github.repository, '/python-package-ci-cd') }} # Run the local action when this is run in the python-package-ci-cd repository
uses: ./actions/find_unreleased_changelog_items
with:
previous-changelog-filename: ${{ inputs.previous-changelog-filename }}
previous-release-notes-filename: ${{ inputs.previous-release-notes-filename }}
previous-changelog-filepath: ${{ inputs.previous-changelog-filepath }}
previous-release-notes-filepath: ${{ inputs.previous-release-notes-filepath }}
- if: ${{ !endsWith(github.repository, '/python-package-ci-cd') }} # Run the public action when this is run outside the python-package-ci-cd repository
uses: tektronix/python-package-ci-cd/actions/[email protected]
with:
previous-changelog-filename: ${{ inputs.previous-changelog-filename }}
previous-release-notes-filename: ${{ inputs.previous-release-notes-filename }}
previous-changelog-filepath: ${{ inputs.previous-changelog-filepath }}
previous-release-notes-filepath: ${{ inputs.previous-release-notes-filepath }}
- name: Python Semantic Release
uses: python-semantic-release/[email protected]
id: release
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test-actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ jobs:
- uses: ./actions/find_unreleased_changelog_items
with:
release-level: patch
previous-changelog-filename: .testing_previous_changelog_for_template.md
previous-release-notes-filename: .testing_previous_release_notes_for_template.md
previous-changelog-filepath: python_semantic_release_templates/.testing_previous_changelog_for_template.md
previous-release-notes-filepath: python_semantic_release_templates/.testing_previous_release_notes_for_template.md
- name: Get Job Summary
uses: austenstone/[email protected]
id: job-summary
Expand Down
16 changes: 6 additions & 10 deletions actions/find_unreleased_changelog_items/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,14 @@ inputs:
minor for backward compatible larger changes,
major for non-backward compatible changes.
required: false
previous-changelog-filename:
description: The name of the file to copy the contents of the changelog into for
use in the `python-semantic-release` templates. This file will be created inside
of the directory defined by the `[tool.semantic_release.changelog.template_dir]`
key in the `pyproject.toml` file.
previous-changelog-filepath:
description: The full path of the file to copy the contents of the changelog into
for use in the `python-semantic-release` templates.
required: false
default: .previous_changelog_for_template.md
previous-release-notes-filename:
description: The name of the file to copy the contents of the `## Unreleased`
section of the changelog into for use in the GitHub Release Notes. This file
will be created inside of the directory defined by the `[tool.semantic_release.changelog.template_dir]`
key in the `pyproject.toml` file.
previous-release-notes-filepath:
description: The full path of the file to copy the contents of the `## Unreleased`
section of the changelog into for use in the GitHub Release Notes.
required: false
default: .previous_release_notes_for_template.md
runs:
Expand Down
31 changes: 4 additions & 27 deletions actions/find_unreleased_changelog_items/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,9 @@
import re
import shutil

import tomli

PYPROJECT_FILE = pathlib.Path("./pyproject.toml")
CHANGELOG_FILE = pathlib.Path("./CHANGELOG.md")


def find_template_folder() -> pathlib.Path:
"""Find the template folder from the pyproject.toml file.
Returns:
The path to the template folder.
"""
with PYPROJECT_FILE.open("rb") as file_handle:
pyproject_data = tomli.load(file_handle)
try:
template_folder = pathlib.Path(
pyproject_data["tool"]["semantic_release"]["changelog"]["template_dir"]
)
except KeyError:
template_folder = pathlib.Path("./templates")
return template_folder


def main() -> None:
"""Check for entries in the Unreleased section of the CHANGELOG.md file.
Expand All @@ -50,15 +30,12 @@ def main() -> None:
"""
# Load in the GitHub Action inputs
# See https://docs.github.com/en/actions/sharing-automations/creating-actions/metadata-syntax-for-github-actions#example-specifying-inputs
filename_for_previous_changelog = os.environ["INPUT_PREVIOUS-CHANGELOG-FILENAME"]
filename_for_previous_release_notes = os.environ["INPUT_PREVIOUS-RELEASE-NOTES-FILENAME"]
filepath_for_previous_changelog = os.environ["INPUT_PREVIOUS-CHANGELOG-FILEPATH"]
filepath_for_previous_release_notes = os.environ["INPUT_PREVIOUS-RELEASE-NOTES-FILEPATH"]
release_level = os.getenv("INPUT_RELEASE-LEVEL")
# Set the filepaths for the template files
template_folder = find_template_folder()
template_changelog_filepath = template_folder / pathlib.Path(filename_for_previous_changelog)
template_release_notes_filepath = template_folder / pathlib.Path(
filename_for_previous_release_notes
)
template_changelog_filepath = pathlib.Path(filepath_for_previous_changelog)
template_release_notes_filepath = pathlib.Path(filepath_for_previous_release_notes)

release_notes_content = ""
found_entries = False
Expand Down
14 changes: 7 additions & 7 deletions actions/find_unreleased_changelog_items/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ will be used to fill in the GitHub Release Notes.
## Inputs

| Input variable | Necessity | Description | Default |
| --------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- |
| `release-level` | optional | The level of the impending release. Must be one of `major`, `minor`, or `patch`. Setting this input will trigger the action to output the summary of the incoming release level and the unreleased changes to the Workflow Summary. | |
| `previous-changelog-filename` | optional | The name of the file to copy the contents of the changelog into for use in the `python-semantic-release` templates. This file will be created inside of the directory defined by the `[tool.semantic_release.changelog.template_dir]` key in the `pyproject.toml` file. | `'.previous_changelog_for_template.md'` |
| `previous-release-notes-filename` | optional | The name of the file to copy the contents of the `## Unreleased` section of the changelog into for use in the GitHub Release Notes. This file will be created inside of the directory defined by the `[tool.semantic_release.changelog.template_dir]` key in the `pyproject.toml` file. | `'.previous_release_notes_for_template.md'` |
| Input variable | Necessity | Description | Default |
| --------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- |
| `release-level` | optional | The level of the impending release. Must be one of `major`, `minor`, or `patch`. Setting this input will trigger the action to output the summary of the incoming release level and the unreleased changes to the Workflow Summary. | |
| `previous-changelog-filepath` | optional | The full path of the file to copy the contents of the changelog into for use in the `python-semantic-release` templates. | `'.previous_changelog_for_template.md'` |
| `previous-release-notes-filepath` | optional | The full path of the file to copy the contents of the `## Unreleased` section of the changelog into for use in the GitHub Release Notes. | `'.previous_release_notes_for_template.md'` |

## Example

Expand All @@ -51,6 +51,6 @@ jobs:
- uses: tektronix/python-package-ci-cd/actions/find_unreleased_changelog_items@main # it is recommended to use the latest release tag instead of `main`
with:
release-level: ${{ inputs.release-level }} # optional
previous-changelog-filename: .previous_changelog_for_template.md # optional
previous-release-notes-filename: .previous_release_notes_for_template.md # optional
previous-changelog-filepath: .previous_changelog_for_template.md # optional
previous-release-notes-filepath: .previous_release_notes_for_template.md # optional
```
99 changes: 31 additions & 68 deletions tests/test_find_unreleased_changelog_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,36 @@

from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING

import pytest

from actions.find_unreleased_changelog_items.main import find_template_folder, main
from actions.find_unreleased_changelog_items.main import main

PREVIOUS_CHANGELOG_FILENAME = "previous_changelog.md"
PREVIOUS_RELEASE_NOTES_FILENAME = "previous_release_notes.md"
if TYPE_CHECKING:
from pathlib import Path

PREVIOUS_CHANGELOG_FILEPATH = "previous_changelog.md"
PREVIOUS_RELEASE_NOTES_FILEPATH = "previous_release_notes.md"
MOCK_TEMPLATES_FOLDER = "mock_templates"


@pytest.fixture()
def mock_pyproject_file(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> tuple[Path, Path]:
"""Mock the pyproject.toml file.
def mock_previous_files(tmp_path: Path) -> tuple[Path, Path]:
"""Create filepaths in the temporary directory for the template files.
Args:
monkeypatch: The monkeypatch fixture.
tmp_path: The temporary path fixture.
Returns:
The path to the pyproject.toml file and the path to the template folder.
The path to the previous changelog file and previous release notes file.
"""
pyproject_content = f"""
[tool.semantic_release.changelog]
template_dir = "{MOCK_TEMPLATES_FOLDER}"
"""
mock_path = tmp_path / "pyproject.toml"
mock_path.write_text(pyproject_content)
monkeypatch.setattr("actions.find_unreleased_changelog_items.main.PYPROJECT_FILE", mock_path)
template_folder = tmp_path / MOCK_TEMPLATES_FOLDER
template_folder.mkdir()
return mock_path, template_folder
return (
template_folder / PREVIOUS_CHANGELOG_FILEPATH,
template_folder / PREVIOUS_RELEASE_NOTES_FILEPATH,
)


@pytest.fixture()
Expand Down Expand Up @@ -73,63 +71,39 @@ def summary_file(tmp_path: Path) -> Path:


@pytest.fixture()
def mock_env_vars(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, summary_file: Path) -> None:
def mock_env_vars(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
summary_file: Path,
mock_previous_files: tuple[Path, Path],
) -> None:
"""Mock the environment variables to simulate GitHub Actions inputs.
Args:
tmp_path: The temporary path fixture.
monkeypatch: The monkeypatch fixture.
summary_file: The path to the job summary file.
mock_previous_files: Paths to the previous changelog file and previous release notes file.
"""
# Change the working directory
monkeypatch.chdir(tmp_path)
monkeypatch.setenv("INPUT_PREVIOUS-CHANGELOG-FILENAME", PREVIOUS_CHANGELOG_FILENAME)
monkeypatch.setenv("INPUT_PREVIOUS-RELEASE-NOTES-FILENAME", PREVIOUS_RELEASE_NOTES_FILENAME)
monkeypatch.setenv("INPUT_PREVIOUS-CHANGELOG-FILEPATH", mock_previous_files[0].as_posix())
monkeypatch.setenv("INPUT_PREVIOUS-RELEASE-NOTES-FILEPATH", mock_previous_files[1].as_posix())
monkeypatch.setenv("INPUT_RELEASE-LEVEL", "minor")
monkeypatch.setenv("GITHUB_STEP_SUMMARY", str(summary_file))


@pytest.mark.parametrize(
("pyproject_content", "expected_template_folder"),
[
(
'[tool.semantic_release.changelog]\ntemplate_dir = "mock_templates"\n',
Path("mock_templates"),
),
(
"[tool.semantic_release.changelog]\n",
Path("templates"),
),
],
)
def test_find_template_folder(
mock_pyproject_file: tuple[Path, Path], pyproject_content: str, expected_template_folder: Path
) -> None:
"""Test the find_template_folder function.
Args:
mock_pyproject_file: Mock the pyproject.toml file.
pyproject_content: The content to write to the pyproject.toml file.
expected_template_folder: The expected template folder path.
"""
mock_pyproject_file[0].write_text(pyproject_content)
template_folder = find_template_folder()
assert template_folder == expected_template_folder


def test_main_no_unreleased_entries(
mock_env_vars: None, # noqa: ARG001
mock_changelog_file: Path,
summary_file: Path, # noqa: ARG001
mock_pyproject_file: Path, # noqa: ARG001
) -> None:
"""Test the main function when no unreleased entries are found.
Args:
mock_env_vars: Mock the environment variables.
mock_changelog_file: Mock the changelog file.
summary_file: Mock the environment variables.
mock_pyproject_file: Mock the pyproject.toml file.
"""
# Modify the changelog content to have no unreleased entries
changelog_content = """# Changelog
Expand All @@ -146,27 +120,22 @@ def test_main_no_unreleased_entries(

def test_main_with_unreleased_entries(
mock_env_vars: None, # noqa: ARG001
mock_pyproject_file: tuple[Path, Path],
mock_changelog_file: Path,
summary_file: Path,
mock_previous_files: tuple[Path, Path],
) -> None:
"""Test the main function when unreleased entries are found.
Args:
mock_env_vars: Mock the environment variables.
mock_pyproject_file: Mock the pyproject.toml file.
mock_changelog_file: Mock the changelog file.
summary_file: Mock the environment variables.
mock_previous_files: Paths to the previous changelog file and previous release notes file.
"""
_, template_folder = mock_pyproject_file
template_changelog_file = template_folder / PREVIOUS_CHANGELOG_FILENAME
template_release_notes_file = template_folder / PREVIOUS_RELEASE_NOTES_FILENAME
main()

assert template_changelog_file.read_text() == mock_changelog_file.read_text()
assert (
template_release_notes_file.read_text().strip() == "## Unreleased\n### Added\n- New feature"
)
assert mock_previous_files[0].read_text() == mock_changelog_file.read_text()
assert mock_previous_files[1].read_text().strip() == "## Unreleased\n### Added\n- New feature"

with summary_file.open("r") as summary_file_handle:
summary_contents = summary_file_handle.read()
Expand All @@ -176,30 +145,24 @@ def test_main_with_unreleased_entries(

def test_main_with_no_release_level(
mock_env_vars: None, # noqa: ARG001
mock_pyproject_file: tuple[Path, Path],
mock_changelog_file: Path,
summary_file: Path,
mock_previous_files: tuple[Path, Path],
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Test the main function when unreleased entries are found but no release_level is provided.
Args:
mock_env_vars: Mock the environment variables.
mock_pyproject_file: Mock the pyproject.toml file.
mock_changelog_file: Mock the changelog file.
summary_file: Mock the environment variables.
mock_previous_files: Paths to the previous changelog file and previous release notes file.
monkeypatch: The monkeypatch fixture.
"""
_, template_folder = mock_pyproject_file
template_changelog_file = template_folder / PREVIOUS_CHANGELOG_FILENAME
template_release_notes_file = template_folder / PREVIOUS_RELEASE_NOTES_FILENAME

# Unset the INPUT_RELEASE-LEVEL environment variable
monkeypatch.delenv("INPUT_RELEASE-LEVEL", raising=False)
main()

assert template_changelog_file.read_text() == mock_changelog_file.read_text()
assert (
template_release_notes_file.read_text().strip() == "## Unreleased\n### Added\n- New feature"
)
assert mock_previous_files[0].read_text() == mock_changelog_file.read_text()
assert mock_previous_files[1].read_text().strip() == "## Unreleased\n### Added\n- New feature"
assert not summary_file.exists()
Loading

0 comments on commit 630c3e3

Please sign in to comment.