Skip to content

Commit

Permalink
Add: Extend pontos-release release to add GitHub Actions output
Browse files Browse the repository at this point in the history
Allow to get release outputs in a GitHub Action using pontos-release
release.
  • Loading branch information
bjoernricks committed May 9, 2023
1 parent ef85a3e commit 84b98e2
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 11 deletions.
49 changes: 46 additions & 3 deletions pontos/github/actions/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import contextlib
import os
from contextlib import contextmanager
from io import TextIOWrapper
from pathlib import Path
from typing import Optional
from typing import Generator, Optional

from pontos.github.actions.errors import GitHubActionsError

Expand Down Expand Up @@ -68,7 +69,7 @@ class Console:
"""

@classmethod
@contextlib.contextmanager
@contextmanager
def group(cls, title: str):
"""
ContextManager to display a foldable group
Expand Down Expand Up @@ -199,7 +200,49 @@ def debug(message: str):
print(f"::debug::{message}")


class ActionOutput:
def __init__(self, file: TextIOWrapper) -> None:
self._file = file

def write(self, name: str, value: str):
"""
Set action output
An action output can be consumed by another job
Args:
name: Name of the output variable
value: Value of the output variable
"""
self._file.write(f"{name}={value}\n")


class ActionIO:
@staticmethod
def has_output() -> bool:
"""
Check if GITHUB_OUTPUT is set
"""
return "GITHUB_OUTPUT" in os.environ

@staticmethod
@contextmanager
def out() -> Generator[ActionOutput, None, None]:
"""
Create action output
An action output can be consumed by another job
"""
output_filename = os.environ.get("GITHUB_OUTPUT")
if not output_filename:
raise GitHubActionsError(
"GITHUB_OUTPUT environment variable not set. Can't write "
"action output."
)

with Path(output_filename).open("a", encoding="utf8") as f:
yield ActionOutput(f)

@staticmethod
def output(name: str, value: str):
"""
Expand Down
26 changes: 26 additions & 0 deletions pontos/release/release.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import asyncio
from argparse import Namespace
from dataclasses import dataclass
from enum import IntEnum, auto
from pathlib import Path
from typing import Optional
Expand All @@ -27,6 +28,7 @@
from pontos.changelog.conventional_commits import ChangelogBuilder
from pontos.errors import PontosError
from pontos.git import Git
from pontos.github.actions.core import ActionIO
from pontos.github.api import GitHubAsyncRESTApi
from pontos.terminal import Terminal
from pontos.version import Version, VersionCalculator, VersionError
Expand All @@ -37,6 +39,21 @@
from .helper import ReleaseType, find_signing_key, get_git_repository_name


@dataclass
class ReleaseInformation:
last_release_version: Version
release_version: Version
git_release_tag: str
next_version: Version

def write_github_output(self):
with ActionIO.out() as output:
output.write("last-release-version", self.last_release_version)
output.write("release-version", self.release_version)
output.write("git-release-tag", self.git_release_tag)
output.write("next-version", self.next_version)


class ReleaseReturnValue(IntEnum):
"""
Possible return values of ReleaseCommand
Expand Down Expand Up @@ -345,6 +362,15 @@ async def run(
self.terminal.info("Pushing changes")
self.git.push(follow_tags=True, remote=git_remote_name)

self.release_information = ReleaseInformation(
last_release_version=last_release_version,
release_version=release_version,
git_release_tag=git_version,
next_version=next_version,
)
if ActionIO.has_output():
self.release_information.write_github_output()

return ReleaseReturnValue.SUCCESS


Expand Down
43 changes: 37 additions & 6 deletions tests/github/actions/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,45 @@ def test_output(self):

self.assertEqual(output, "foo=bar\nlorem=ipsum\n")

@patch.dict("os.environ", {}, clear=True)
def test_output_no_env(self):
with patch.dict("os.environ", {}, clear=True), self.assertRaises(
GitHubActionsError
):
with self.assertRaises(GitHubActionsError):
ActionIO.output("foo", "bar")

@patch.dict("os.environ", {"GITHUB_OUTPUT": ""}, clear=True)
def test_output_empty_env(self):
with patch.dict(
"os.environ", {"GITHUB_OUTPUT": ""}, clear=True
), self.assertRaises(GitHubActionsError):
with self.assertRaises(GitHubActionsError):
ActionIO.output("foo", "bar")

@patch.dict("os.environ", {}, clear=True)
def test_no_github_output(self):
self.assertFalse(ActionIO.has_output())

@patch.dict(
"os.environ", {"GITHUB_OUTPUT": "/foo/github.output"}, clear=True
)
def test_has_github_output(self):
self.assertTrue(ActionIO.has_output())

def test_out(self):
with temp_directory() as temp_dir:
outfile = temp_dir / "github.output"
with patch.dict(
"os.environ",
{"GITHUB_OUTPUT": str(outfile.absolute())},
clear=True,
):
with ActionIO.out() as output:
output.write("foo", "bar")

self.assertEqual(outfile.read_text(encoding="utf8"), "foo=bar\n")

@patch.dict("os.environ", {}, clear=True)
def test_out_failure(self):
with self.assertRaisesRegex(
GitHubActionsError,
"GITHUB_OUTPUT environment variable not set. Can't write "
"action output.",
):
with ActionIO.out():
pass
71 changes: 69 additions & 2 deletions tests/release/test_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@

from pontos.git.git import ConfigScope, Git
from pontos.git.status import StatusEntry
from pontos.github.actions.errors import GitHubActionsError
from pontos.release.main import parse_args
from pontos.release.release import ReleaseReturnValue, release
from pontos.release.release import (
ReleaseInformation,
ReleaseReturnValue,
release,
)
from pontos.terminal.terminal import Terminal
from pontos.testing import temp_git_repository
from pontos.testing import temp_directory, temp_git_repository
from pontos.version import VersionError, VersionUpdate
from pontos.version.commands import GoVersionCommand
from pontos.version.schemes._pep440 import PEP440Version, PEP440VersioningScheme
Expand Down Expand Up @@ -76,6 +81,68 @@ def setup_go_project(
yield tmp_git


class ReleaseInformationTestCase(unittest.TestCase):
def test_release_info(self):
release_info = ReleaseInformation(
last_release_version=PEP440Version.from_string("1.2.3"),
release_version=PEP440Version.from_string("2.0.0"),
git_release_tag="v2.0.0",
next_version=PEP440Version.from_string("2.0.1.dev1"),
)

self.assertEqual(
release_info.last_release_version,
PEP440Version.from_string("1.2.3"),
)
self.assertEqual(
release_info.release_version, PEP440Version.from_string("2.0.0")
)
self.assertEqual(release_info.git_release_tag, "v2.0.0")
self.assertEqual(
release_info.next_version, PEP440Version.from_string("2.0.1.dev1")
)

@patch.dict("os.environ", {}, clear=True)
def test_no_github_output(self):
release_info = ReleaseInformation(
last_release_version=PEP440Version.from_string("1.2.3"),
release_version=PEP440Version.from_string("2.0.0"),
git_release_tag="v2.0.0",
next_version=PEP440Version.from_string("2.0.1.dev1"),
)

with self.assertRaisesRegex(
GitHubActionsError,
"GITHUB_OUTPUT environment variable not set. Can't write "
"action output.",
):
release_info.write_github_output()

def test_github_output(self):
expected = """last-release-version=1.2.3
release-version=2.0.0
git-release-tag=v2.0.0
next-version=2.0.1.dev1
"""
with temp_directory() as temp_dir:
out_file = temp_dir / "out.txt"
with patch.dict(
"os.environ", {"GITHUB_OUTPUT": str(out_file.absolute())}
):
release_info = ReleaseInformation(
last_release_version=PEP440Version.from_string("1.2.3"),
release_version=PEP440Version.from_string("2.0.0"),
git_release_tag="v2.0.0",
next_version=PEP440Version.from_string("2.0.1.dev1"),
)

release_info.write_github_output()

self.assertTrue(out_file.exists())
actual = out_file.read_text(encoding="utf8")
self.assertEqual(actual, expected)


@patch.dict(
"os.environ",
{
Expand Down

0 comments on commit 84b98e2

Please sign in to comment.