Source code for semantic_release.cli.github_actions_output
from __future__ import annotations
import os
from enum import Enum
from re import compile as regexp
from typing import TYPE_CHECKING
from semantic_release.globals import logger
from semantic_release.version.version import Version
if TYPE_CHECKING:
from typing import Any
from semantic_release.hvcs.github import Github
[docs]
class PersistenceMode(Enum):
TEMPORARY = "temporary"
PERMANENT = "permanent"
[docs]
class VersionGitHubActionsOutput:
OUTPUT_ENV_VAR = "GITHUB_OUTPUT"
def __init__(
self,
gh_client: Github | None = None,
mode: PersistenceMode = PersistenceMode.PERMANENT,
released: bool | None = None,
version: Version | None = None,
commit_sha: str | None = None,
release_notes: str | None = None,
prev_version: Version | None = None,
) -> None:
self._gh_client = gh_client
self._mode = mode
self._released = released
self._version = version
self._commit_sha = commit_sha
self._release_notes = release_notes
self._prev_version = prev_version
@property
def released(self) -> bool | None:
return self._released
@released.setter
def released(self, value: bool) -> None:
if not isinstance(value, bool):
raise TypeError("output 'released' is boolean")
self._released = value
@property
def version(self) -> Version | None:
return self._version if self._version is not None else None
@version.setter
def version(self, value: Version) -> None:
if not isinstance(value, Version):
raise TypeError("output 'released' should be a Version")
self._version = value
@property
def tag(self) -> str | None:
return self.version.as_tag() if self.version is not None else None
@property
def is_prerelease(self) -> bool | None:
return self.version.is_prerelease if self.version is not None else None
@property
def commit_sha(self) -> str | None:
return self._commit_sha if self._commit_sha else None
@commit_sha.setter
def commit_sha(self, value: str) -> None:
if not isinstance(value, str):
raise TypeError("output 'commit_sha' should be a string")
if not regexp(r"^[0-9a-f]{40}$").match(value):
raise ValueError(
"output 'commit_sha' should be a valid 40-hex-character SHA"
)
self._commit_sha = value
@property
def release_notes(self) -> str | None:
return self._release_notes if self._release_notes else None
@release_notes.setter
def release_notes(self, value: str) -> None:
if not isinstance(value, str):
raise TypeError("output 'release_notes' should be a string")
self._release_notes = value
@property
def prev_version(self) -> Version | None:
if not self.released:
return self.version
return self._prev_version if self._prev_version else None
@prev_version.setter
def prev_version(self, value: Version) -> None:
if not isinstance(value, Version):
raise TypeError("output 'prev_version' should be a Version")
self._prev_version = value
@property
def gh_client(self) -> Github:
if not self._gh_client:
raise ValueError("GitHub client not set, cannot create links")
return self._gh_client
[docs]
def to_output_text(self) -> str:
missing: set[str] = set()
if self.version is None:
missing.add("version")
if self.released is None:
missing.add("released")
if self.released:
if self.release_notes is None:
missing.add("release_notes")
if self._mode is PersistenceMode.PERMANENT and self.commit_sha is None:
missing.add("commit_sha")
if missing:
raise ValueError(
f"some required outputs were not set: {', '.join(missing)}"
)
output_values: dict[str, Any] = {
"released": str(self.released).lower(),
"version": str(self.version),
"tag": self.tag,
"is_prerelease": str(self.is_prerelease).lower(),
"link": self.gh_client.create_release_url(self.tag) if self.tag else "",
"previous_version": str(self.prev_version) if self.prev_version else "",
"commit_sha": self.commit_sha if self.commit_sha else "",
}
multiline_output_values: dict[str, str] = {
"release_notes": self.release_notes if self.release_notes else "",
}
output_lines = [
*[f"{key}={value!s}{os.linesep}" for key, value in output_values.items()],
*[
f"{key}<<EOF{os.linesep}{value}EOF{os.linesep}"
if value
else f"{key}={os.linesep}"
for key, value in multiline_output_values.items()
],
]
return str.join("", output_lines)
[docs]
def write_if_possible(self, filename: str | None = None) -> None:
output_file = filename or os.getenv(self.OUTPUT_ENV_VAR)
if not output_file:
logger.info("not writing GitHub Actions output, as no file specified")
return
with open(output_file, "ab") as f:
f.write(self.to_output_text().encode("utf-8"))