commit 3abe3e251c15fb89ba2cbeca688932a42b03bb3d
parent 75b028a4a87d3165e733918c1a4d93e8bf463efd
Author: arjoonn <arjoonn@noreply.localhost>
Date: Tue, 31 Jan 2023 02:19:55 +0000
Add support for github remote (!30)
Branch auto created by JayporeCI
```jayporeci
╔ 🟢 : JayporeCI [sha f2f319af5a]
┏━ Docker
┃
┃ 🟢 : Jci [bba52dc0] 1:49
┃ 🟢 : JciEnv [0378dfd5] 1:41
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┏━ Jobs
┃
┃ 🟢 : black [b11e858b] 0: 0
┃ 🟢 : pylint [09e63926] 0: 7
┃ 🟢 : pytest [9b152c06] 0: 2
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┏━ Publish
┃
┃ 🟢 : DockerHubJci [236fbde6] 31:23
┃ 🟢 : DockerHubJcienv [d387fd54] 26:48
┃ 🟢 : PublishDocs [f0df7c7e] 0: 6
┃ 🟢 : PublishPypi [6ba561b6] 0: 7
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
```
Co-authored-by: arjoonn sharma <arjoonn@midpathsoftware.com>
Reviewed-on: https://gitea.midpathsoftware.com/midpath/jaypore_ci/pulls/30
Diffstat:
4 files changed, 194 insertions(+), 5 deletions(-)
diff --git a/jaypore_ci/jci.py b/jaypore_ci/jci.py
@@ -93,9 +93,12 @@ class Job: # pylint: disable=too-many-instance-attributes
Status.TIMEOUT: "warning",
Status.SKIPPED: "warning",
}[self.pipeline.get_status()]
- self.pipeline.remote.publish(
- self.pipeline.reporter.render(self.pipeline), status
- )
+ report = self.pipeline.reporter.render(self.pipeline)
+ try:
+ self.pipeline.remote.publish(report, status)
+ except Exception as e: # pylint: disable=broad-except
+ self.logging().exeception(e)
+ return report
def trigger(self):
"""
@@ -372,7 +375,8 @@ class Pipeline: # pylint: disable=too-many-instance-attributes
break
self.logging().error("Pipeline passed")
if job is not None:
- job.update_report()
+ report = job.update_report()
+ self.logging().info("Report:", report=report)
@contextmanager
def stage(self, name, **kwargs):
diff --git a/jaypore_ci/remotes/__init__.py b/jaypore_ci/remotes/__init__.py
@@ -1,2 +1,3 @@
from .mock import Mock
from .gitea import Gitea
+from .github import Github
diff --git a/jaypore_ci/remotes/github.py b/jaypore_ci/remotes/github.py
@@ -0,0 +1,184 @@
+"""
+A github remote git host.
+
+This is used to report pipeline status to the remote.
+"""
+import os
+import subprocess
+from pathlib import Path
+from urllib.parse import urlparse
+
+import requests
+
+from jaypore_ci.interfaces import Remote
+from jaypore_ci.logging import logger
+
+
+class Github(Remote): # pylint: disable=too-many-instance-attributes
+ """
+ The remote implementation for github.
+ """
+
+ def __headers__(self):
+ return {
+ "Authorization": f"Bearer {self.token}",
+ "Accept": "application/vnd.github+json",
+ "X-Github-Api-Version": "2022-11-28",
+ }
+
+ @classmethod
+ def from_env(cls):
+ """
+ Creates a remote instance from the environment.
+ It will:
+
+ - Find the remote location using `git remote`.
+ - Find the current branch
+ - Create a new pull request for that branch
+ - Allow posting updates using the gitea token provided
+ """
+ remote = (
+ subprocess.check_output(
+ "git remote -v | grep push | awk '{print $2}'", shell=True
+ )
+ .decode()
+ .strip()
+ )
+ assert "https://github.com" in remote, "Only https remotes supported"
+ assert ".git" in remote
+ remote = urlparse(remote)
+ branch = (
+ subprocess.check_output(
+ r"git branch | grep \* | awk '{print $2}'", shell=True
+ )
+ .decode()
+ .strip()
+ )
+ os.environ["JAYPORE_COMMIT_BRANCH"] = branch
+ sha = subprocess.check_output("git rev-parse HEAD", shell=True).decode().strip()
+ os.environ["JAYPORE_COMMIT_SHA"] = sha
+ owner = Path(remote.path).parts[1]
+ repo = Path(remote.path).parts[2].replace(".git", "")
+ token = os.environ["JAYPORE_GITHUB_TOKEN"]
+ return cls(
+ root="https://api.github.com",
+ owner=owner,
+ repo=repo,
+ branch=branch,
+ token=token,
+ sha=sha,
+ )
+
+ def __init__(
+ self, *, root, owner, repo, token, **kwargs
+ ): # pylint: disable=too-many-arguments
+ super().__init__(**kwargs)
+ # --- customer
+ self.root = root
+ self.api = root
+ self.owner = owner
+ self.repo = repo
+ self.token = token
+ self.timeout = 10
+ self.base_branch = "main"
+
+ def logging(self):
+ """
+ Return's a logging instance with information about gitea bound to it.
+ """
+ return logger.bind(
+ root=self.root, owner=self.owner, repo=self.repo, branch=self.branch
+ )
+
+ def get_pr_id(self):
+ """
+ Returns the pull request ID for the current branch.
+ """
+ r = requests.post(
+ f"{self.api}/repos/{self.owner}/{self.repo}/pulls",
+ headers=self.__headers__(),
+ timeout=self.timeout,
+ json={
+ "base": self.base_branch,
+ "body": "Branch auto created by JayporeCI",
+ "head": self.branch,
+ "title": self.branch,
+ },
+ )
+ self.logging().debug("Create PR", status_code=r.status_code)
+ if r.status_code == 201:
+ return r.json()["number"]
+ r = requests.get(
+ f"{self.api}/repos/{self.owner}/{self.repo}/pulls",
+ headers=self.__headers__(),
+ timeout=self.timeout,
+ json={"base": self.base_branch, "head": self.branch, "draft": True},
+ )
+ self.logging().debug("Get PR", status_code=r.status_code)
+ if r.status_code == 200:
+ if len(r.json()) == 1:
+ return r.json()[0]["number"]
+ self.logging().debug(
+ "Failed github api",
+ api=self.api,
+ owner=self.owner,
+ repo=self.repo,
+ token=self.token,
+ branch=self.branch,
+ status=r.status_code,
+ response=r.text,
+ )
+ raise Exception(r)
+
+ def publish(self, report: str, status: str):
+ """
+ Will publish the report to the remote.
+
+ :param report: Report to write to remote.
+ :param status: One of ["pending", "success", "error", "failure"]
+ This is the dot/tick next to each commit in gitea.
+ """
+ assert status in ("pending", "success", "error", "failure")
+ issue_id = self.get_pr_id()
+ # Get existing PR body
+ r = requests.get(
+ f"{self.api}/repos/{self.owner}/{self.repo}/pulls/{issue_id}",
+ timeout=self.timeout,
+ headers=self.__headers__(),
+ )
+ self.logging().debug("Get existing body", status_code=r.status_code)
+ assert r.status_code == 200
+ body = r.json()["body"]
+ body = (line for line in body.split("\n"))
+ prefix = []
+ for line in body:
+ if "```jayporeci" in line:
+ prefix = prefix[:-1]
+ break
+ prefix.append(line)
+ while prefix and prefix[-1].strip() == "":
+ prefix = prefix[:-1]
+ prefix.append("")
+ # Post new body with report
+ report = "\n".join(prefix) + "\n" + report
+ r = requests.patch(
+ f"{self.api}/repos/{self.owner}/{self.repo}/pulls/{issue_id}",
+ json={"body": report},
+ timeout=self.timeout,
+ headers=self.__headers__(),
+ )
+ self.logging().debug("Published new report", status_code=r.status_code)
+ # Set commit status
+ r = requests.post(
+ f"{self.api}/repos/{self.owner}/{self.repo}/statuses/{self.sha}",
+ json={
+ "context": "JayporeCi",
+ "description": f"Pipeline status is: {status}",
+ "state": status,
+ },
+ timeout=self.timeout,
+ headers=self.__headers__(),
+ )
+ self.logging().debug(
+ "Published new status", status=status, status_code=r.status_code
+ )
diff --git a/pyproject.toml b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "jaypore_ci"
-version = "0.1.14"
+version = "0.2"
description = ""
authors = ["arjoonn sharma <arjoonn.94@gmail.com>"]