Skip to content

Commit

Permalink
patch: Add Python API for GitHub Actions (#210)
Browse files Browse the repository at this point in the history
  • Loading branch information
carlcsaposs-canonical authored Jul 23, 2024
1 parent abab4c2 commit 60625c4
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,14 @@
"""

import argparse
import enum
import json
import logging
import os
import pathlib
import sys

import yaml

from .. import github_actions
from . import craft

logging.basicConfig(level=logging.INFO, stream=sys.stdout)
Expand Down Expand Up @@ -94,13 +93,11 @@ def collect(craft_: craft.Craft):
else:
id_ = architecture.value
bases.append({"id": id_, "runner": RUNNERS[architecture]})
logging.info(f"Collected {bases=}")
github_actions.output["bases"] = json.dumps(bases)
default_prefix = f'packed-{craft_.value}-{args.directory.replace("/", "-")}'
if craft_ is craft.Craft.CHARM:
default_prefix = f'packed-{craft_.value}-cache-{args.cache}-{args.directory.replace("/", "-")}'
logging.info(f"{default_prefix=}")
with open(os.environ["GITHUB_OUTPUT"], "a") as file:
file.write(f"bases={json.dumps(bases)}\ndefault_prefix={default_prefix}")
github_actions.output["default_prefix"] = default_prefix


def snap():
Expand Down
14 changes: 5 additions & 9 deletions python/cli/data_platform_workflows_cli/craft_tools/release.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
import dataclasses
import json
import logging
import os
import pathlib
import re
import subprocess
import sys

import yaml

from .. import github_actions
from . import craft

logging.basicConfig(level=logging.INFO, stream=sys.stdout)
Expand Down Expand Up @@ -66,20 +66,18 @@ class Revision:

# Output GitHub release info
release_tag = f"rev{max(revision.value for revision in revisions)}"
github_actions.output["release_tag"] = release_tag
if len(revisions) == 1:
release_title = "Revision "
else:
release_title = "Revisions "
release_title += ", ".join(str(revision.value) for revision in revisions)
github_actions.output["release_title"] = release_title
release_notes = f"Released to {args.channel}"
for revision in revisions:
release_notes += f"\n- {revision.architecture}: revision {revision.value}"
with open("release_notes.txt", "w") as file:
file.write(release_notes)
output = f"release_tag={release_tag}\nrelease_title={release_title}"
logging.info(output)
with open(os.environ["GITHUB_OUTPUT"], "a") as file:
file.write(output)


def rock():
Expand Down Expand Up @@ -201,17 +199,15 @@ def charm():

# Output GitHub release info
release_tag = f"rev{max(charm_revisions)}"
github_actions.output["release_tag"] = release_tag
if len(charm_revisions) == 1:
release_title = "Revision "
else:
release_title = "Revisions "
release_title += ", ".join(str(revision) for revision in charm_revisions)
github_actions.output["release_title"] = release_title
release_notes = f"Released to {args.channel}\nOCI images:\n" + "\n".join(
f"- {dataclasses.asdict(oci)}" for oci in oci_resources
)
with open("release_notes.txt", "w") as file:
file.write(release_notes)
output = f"release_tag={release_tag}\nrelease_title={release_title}"
logging.info(output)
with open(os.environ["GITHUB_OUTPUT"], "a") as file:
file.write(output)
10 changes: 3 additions & 7 deletions python/cli/data_platform_workflows_cli/get_pack_command.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import argparse
import json
import logging
import os
import pathlib
import subprocess
import sys

from . import github_actions


def main():
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
parser = argparse.ArgumentParser()
parser.add_argument("--cache", required=True)
parser.add_argument("--charm-directory", required=True)
Expand Down Expand Up @@ -45,6 +43,4 @@ def main():
)
else:
command = f"charmcraft pack -v --bases-index='{args.bases_index}'"
logging.info(f"{command=}")
with open(os.environ["GITHUB_OUTPUT"], "a") as file:
file.write(f"command={command}")
github_actions.output["command"] = command
79 changes: 79 additions & 0 deletions python/cli/data_platform_workflows_cli/github_actions/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""Python API for GitHub Actions
Supports:
- Workflow commands: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions
- Default environment variables: https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables
Does not include support for GitHub REST API
"""

import collections.abc
import os
import pathlib
import sys

_OutputBaseType = collections.abc.MutableMapping[str, str | None]
_output_file = pathlib.Path(os.environ["GITHUB_OUTPUT"])


class _Output(_OutputBaseType):
def __setitem__(self, key, value: str | None):
if value is None:
value = ""
if "\n" in value:
raise NotImplementedError(
"Output of multi-line strings not supported by Python API. Write directly to GITHUB_OUTPUT file\n"
"https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-output-parameter"
)
with _output_file.open("a", encoding="utf-8") as file:
file.write(f"{key}={value}\n")
print(f"GitHub Actions step output: {key}={value}")

def __delitem__(self, key):
self.__setitem__(key, None)

def __getitem__(self, key):
raise NotImplementedError(
"Cannot read GitHub Actions output. Output is write-only"
)

def __iter__(self):
raise NotImplementedError(
"Cannot read GitHub Actions output. Output is write-only"
)

def __len__(self):
raise NotImplementedError(
"Cannot read GitHub Actions output. Output is write-only"
)


class _ThisModule(sys.modules[__name__].__class__):
"""Contains properties for this module
https://stackoverflow.com/a/34829743
"""

_output = _Output()

@property
def output(self) -> _OutputBaseType:
return self._output

@output.setter
def output(self, value: _OutputBaseType):
# Clear file contents
with _output_file.open(mode="w", encoding="utf-8"):
pass

self._output = _Output()
self._output.update(value)


output: _OutputBaseType
"""Step outputs
https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-output-parameter
"""

sys.modules[__name__].__class__ = _ThisModule
15 changes: 7 additions & 8 deletions python/cli/data_platform_workflows_cli/parse_snap_version.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import argparse
import os

from . import github_actions


def main():
Expand All @@ -9,15 +10,13 @@ def main():
parser.add_argument("--revision-input-name", required=True)
parser.add_argument("--channel-input-name", required=True)
args = parser.parse_args()
output = "install_flag="
if args.revision:
assert (
not args.channel
), f"`{args.channel_input_name}` input cannot be used if `{args.revision_input_name}` input is passed"
output += f"'--revision={args.revision}'"
install_flag = f"'--revision={args.revision}'"
elif args.channel:
output += f"'--channel={args.channel}'"

print(output)
with open(os.environ["GITHUB_OUTPUT"], "a") as file:
file.write(output)
install_flag = f"'--channel={args.channel}'"
else:
install_flag = None
github_actions.output["install_flag"] = install_flag
7 changes: 3 additions & 4 deletions python/cli/data_platform_workflows_cli/update_bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import requests
import yaml

from . import github_actions


def get_ubuntu_version(series: str) -> str:
"""Gets Ubuntu version (e.g. "22.04") from series (e.g. "jammy")."""
Expand Down Expand Up @@ -71,7 +73,4 @@ def main():
with open(file_path, "w") as file:
yaml.dump(file_data, file)

output = f"updates_available={json.dumps(old_file_data != file_data)}"
print(output)
with open(os.environ["GITHUB_OUTPUT"], "a") as file:
file.write(output)
github_actions.output["updates_available"] = json.dumps(old_file_data != file_data)

0 comments on commit 60625c4

Please sign in to comment.