Last active
August 14, 2024 10:40
-
-
Save tslmy/84f34a25babe045eb302ec72f2bf39eb to your computer and use it in GitHub Desktop.
How to log messages from Python to GitHub as Checks within a Jenkinsfile
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import logging | |
from datetime import datetime | |
from github import GitHubIntegration | |
class GitHubCheckHandler(logging.handler): | |
""" | |
A logging handler that sends messages to GitHub as a Check. | |
""" | |
def __init__( | |
self, | |
private_key_path: str = "my-github-app.2023-09-03.private-key.pem", | |
github_app_id: int = 123, | |
installation_id: int = 456, | |
owner_repo: str = "username/repo_name", | |
head_sha: str = "377e195414498fe4c97cb5a386fbaf33a145acbfb51ab7169a0fa6136314a079", | |
details_url: str = "https://jenkins.example.com/job/MyJob/42/console", | |
base_url: str = "https://api.github.com", | |
): | |
""" | |
- `base_url`: If using GitHub Enterprise, it's usually `https://github.your.company.com/api/v3`. | |
- `details_url`: Defines the link on the GitHub PR Checks tab that refers back to the Jenkins Build. | |
""" | |
super().__init__() | |
with open(private_key_path) as f: | |
private_key = f.read() | |
self.github_integration = GitHubIntegration( | |
integration_id=github_app_id, | |
base_url=base_url, | |
private_key=private_key, | |
) | |
self.head_sha = head_sha | |
self.installation_id = installation_id | |
self.owner_repo = owner_repo | |
self.check_run = None | |
self.details_url = details_url | |
def emit(self, record: logging.LogRecord): | |
""" | |
Implements the interface of the `handler` class. | |
This is called every time you log a message via a logger. | |
""" | |
output = { | |
"title": record.getMessage(), | |
"summary": record.getMessage(), | |
} | |
# If a Check Run has already been created, we don't want to create another, because that would require users on the Checks tab to refresh the page to see the new messages, instead of just having them show up in real time, async. | |
if self.check_run: | |
self.check_run.edit(output=output) | |
# Return early. | |
return | |
# If a Check Run hasn't been created, we create one. | |
gh = self.github_integration.get_github_for_installation(self.installation_id) | |
repo = gh.get_repo(self.owner_repo) | |
self.check_run = repo.create_check_run( | |
# Use the logger's name as the name of the PR Check. | |
name=record.name, | |
head_sha=self.head_sha, | |
details_url=self.details_url, | |
status="in_progress", | |
output=output, | |
) | |
def conclude(self, conclusion: str, output=None): | |
""" | |
Conclusion can be one of action_required, cancelled, failure, netural, success, skipped, stale, timed_out. | |
""" | |
if not self.check_run: | |
# Nothing to conclude. | |
return | |
if not output: | |
output = self.check_run.output | |
self.check_run.edit( | |
completed_at=datetime.now(), | |
conclusion=conclusion, | |
output=output, | |
status="completed", | |
) | |
def attach_logger_to_github(logger): | |
if ( | |
os.getenv("GITHUB_APP_PRIVATE_KEY_PATH") | |
and os.getenv("GITHUB_APP_ID") | |
and os.getenv("GITHUB_APP_INSTALLATION_ID") | |
and os.getenv("GITHUB_PR_HEAD_SHA") | |
and os.getenv("GITHUB_REPO_SSH_URL") | |
and os.getenv("BUILD_URL") | |
): | |
handler = GitHubCheckHandler( | |
private_key_path=str(os.getenv("GITHUB_APP_PRIVATE_KEY_PATH")), | |
installation_id=int(str(os.getenv("GITHUB_APP_INSTALLATION_ID"))), | |
github_app_id=int(str(os.getenv("GITHUB_APP_ID"))), | |
owner_repo=str(os.getenv("GITHUB_REPO_SSH_URL")) | |
.removeprefix("[email protected]:") | |
.removesuffix(".git"), | |
commit_sha=str(os.getenv("GITHUB_PR_HEAD_SHA")), | |
details_url=str(os.getenv("BUILD_URL")), | |
) | |
logger.addHandler(handler) | |
return handler | |
if __name__ == "__main__": | |
logger = logging.getLogger("test-handler") | |
handler = attach_logger_to_github(logger) | |
logger.fatal("test-message") | |
handler.conclude("success") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment