Skip to content

Commit

Permalink
refactor(ci)!: Change output at GitHub Actions (#203)
Browse files Browse the repository at this point in the history
To not use `::set-output ...` echoing into stdout in favor of writing to
file from `GITHUB_OUTPUT` env var.

Fixes: #185
  • Loading branch information
playpauseandstop authored Dec 20, 2022
1 parent 8a4c1e8 commit 02bc9ff
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 27 deletions.
9 changes: 5 additions & 4 deletions src/badabump/cli/output.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import os
from difflib import ndiff
from typing import Union


EMPTY = "-"
VALUE_ESCAPE_MAPPING = (("%", "%25"), ("\n", "%0A"), ("\r", "%0D"))


def diff(current_content: str, next_content: str) -> str:
Expand Down Expand Up @@ -33,6 +33,7 @@ def echo_value(


def github_actions_output(name: str, value: str) -> None:
for symbol, code in VALUE_ESCAPE_MAPPING:
value = value.replace(symbol, code)
print(f"::set-output name={name}::{value}")
with open(os.environ["GITHUB_OUTPUT"], "a+") as github_output_handler:
github_output_handler.write(f"{name}<<EOF\n")
github_output_handler.write(value)
github_output_handler.write("\nEOF\n")
20 changes: 17 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@
TagTuple = Tuple[str, str]


@pytest.fixture()
@pytest.fixture(scope="function", autouse=True)
def setup_github_output_env_var(monkeypatch, github_output_path):
monkeypatch.setenv("GITHUB_OUTPUT", str(github_output_path))
yield


@pytest.fixture(scope="function")
def create_git_commit():
def factory(path: Path, commit: str) -> None:
subprocess.check_call(["git", "add", "."], cwd=path)
Expand All @@ -20,7 +26,7 @@ def factory(path: Path, commit: str) -> None:
return factory


@pytest.fixture()
@pytest.fixture(scope="function")
def create_git_repository(tmpdir, create_git_commit, create_git_tag):
def factory(*commits: CommitTuple, tag: TagTuple = None) -> Git:
path = Path(tmpdir)
Expand All @@ -39,11 +45,19 @@ def factory(*commits: CommitTuple, tag: TagTuple = None) -> Git:
return factory


@pytest.fixture()
@pytest.fixture(scope="function")
def create_git_tag():
def factory(path: Path, tag: str, message: str) -> None:
subprocess.check_call(
["git", "tag", "-a", tag, "-m", message], cwd=path
)

return factory


@pytest.fixture(scope="function")
def github_output_path(tmp_path) -> Path:
path = Path(tmp_path) / "github-output.txt"
if not path.exists():
path.write_text("")
return path
29 changes: 17 additions & 12 deletions tests/test_ci_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,23 @@ def test_invalid_subcommand():


@pytest.mark.parametrize("ref", (("v20.1.0", "refs/tags/v20.1.0")))
def test_prepare_release(capsys, prepare_repository_for_release, ref):
def test_prepare_release(
capsys, github_output_path, prepare_repository_for_release, ref
):
path = prepare_repository_for_release()
assert main(["-C", str(path), "prepare_release", ref]) == 0

captured = capsys.readouterr()
assert captured.out == ""
assert captured.err == ""

assert "::set-output name=tag_name::v20.1.0"
assert "::set-output name=is_pre_release::false" in captured.out
assert "::set-output name=release_name::20.1.0 Release" in captured.out
github_output = github_output_path.read_text()
assert "tag_name<<EOF\nv20.1.0\nEOF\n" in github_output
assert "is_pre_release<<EOF\nfalse\nEOF\n" in github_output
assert "release_name<<EOF\n20.1.0 Release\nEOF\n" in github_output
assert (
"::set-output name=release_body::Features:%0A---------"
"%0A%0A- Initial release"
) in captured.out
"release_body<<EOF\nFeatures:\n---------\n\n- Initial release\nEOF\n"
) in github_output


def test_prepare_release_env_var(monkeypatch, prepare_repository_for_release):
Expand All @@ -68,7 +71,7 @@ def test_prepare_release_env_var(monkeypatch, prepare_repository_for_release):
assert main(["-C", str(path), "prepare_release"]) == 0


def test_prepare_tag(capsys, create_git_repository):
def test_prepare_tag(capsys, create_git_repository, github_output_path):
git = create_git_repository(
(
"README.md",
Expand All @@ -90,13 +93,15 @@ def test_prepare_tag(capsys, create_git_repository):
assert main(["-C", str(git.path), "prepare_tag"]) == 0

captured = capsys.readouterr()
assert captured.out == ""
assert captured.err == ""

assert "::set-output name=tag_name::v20.1.0" in captured.out
github_output = github_output_path.read_text()
assert "tag_name<<EOF\nv20.1.0\nEOF\n" in github_output
assert (
"::set-output name=tag_message::20.1.0 Release%0A%0AFeatures:"
"%0A---------%0A%0A- Initial release%0A"
) in captured.out
"tag_message<<EOF\n20.1.0 Release\n\nFeatures:\n"
"---------\n\n- Initial release\n\n\nEOF\n"
) in github_output


def test_prepare_tag_empty_body(capsys, create_git_repository):
Expand Down
9 changes: 6 additions & 3 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ def test_ci_output(
capsys,
create_git_commit,
create_git_repository,
github_output_path,
file_name,
content,
version,
Expand All @@ -102,11 +103,13 @@ def test_ci_output(
assert main(["-C", str(path), "--ci"]) == 0

captured = capsys.readouterr()
assert captured.out != ""
assert captured.err == ""

assert f"::set-output name=current_tag::v{version}" in captured.out
assert f"::set-output name=current_version::{version}" in captured.out
assert f"::set-output name=next_version::{next_version}" in captured.out
github_output = github_output_path.read_text()
assert f"current_tag<<EOF\nv{version}\nEOF\n" in github_output
assert f"current_version<<EOF\n{version}\nEOF\n" in github_output
assert f"next_version<<EOF\n{next_version}\n" in github_output

changelog = (path / "CHANGELOG.md").read_text()
assert f"# {next_version}" in changelog
Expand Down
16 changes: 11 additions & 5 deletions tests/test_cli_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@
("Hello, world!", "Hello, world!"),
("$var", "$var"),
("`pwd`", "`pwd`"),
("Multi\nLine\nString", "Multi%0ALine%0AString"),
("Multi\r\nLine\r\nString", "Multi%0D%0ALine%0D%0AString"),
("Multi\nLine\nString", "Multi\nLine\nString"),
("Multi\r\nLine\r\nString", "Multi\nLine\nString"),
),
)
def test_github_actions_output(capsys, value, expected):
def test_github_actions_output(capsys, github_output_path, value, expected):
github_actions_output("name", value)
out, _ = capsys.readouterr()
assert out == f"::set-output name=name::{expected}\n"

# Check that nothing written into stdout / stderr
captured = capsys.readouterr()
assert captured.out == ""
assert captured.err == ""

# But GITHUB_OUTPUT file contains proper context
assert github_output_path.read_text() == f"name<<EOF\n{expected}\nEOF\n"

0 comments on commit 02bc9ff

Please sign in to comment.