| |
@@ -18,10 +18,14 @@
|
| |
|
| |
import pagure
|
| |
import pagure.exceptions
|
| |
- import pagure.lib.lib_ci as lib_ci
|
| |
+ import pagure.lib.query
|
| |
+ from pagure.config import config as pagure_config
|
| |
import pagure.lib.plugins
|
| |
import pagure.lib.query
|
| |
from pagure.api import API, APIERROR, api_method
|
| |
+ from pagure.api.ci import BUILD_STATS
|
| |
+
|
| |
+ import time
|
| |
|
| |
_log = logging.getLogger(__name__)
|
| |
|
| |
@@ -43,6 +47,8 @@
|
| |
methods=["POST"],
|
| |
)
|
| |
@api_method
|
| |
+ # <ci_type>_ci_notification
|
| |
+ # convention required to ensure unique names in API namespace
|
| |
def jenkins_ci_notification(
|
| |
repo, pagure_ci_token, username=None, namespace=None
|
| |
):
|
| |
@@ -96,7 +102,7 @@
|
| |
raise pagure.exceptions.APIError(400, error_code=APIERROR.EINVALIDREQ)
|
| |
|
| |
try:
|
| |
- lib_ci.process_jenkins_build(flask.g.session, project, build_id)
|
| |
+ _process_build(flask.g.session, project, build_id)
|
| |
except pagure.exceptions.NoCorrespondingPR as err:
|
| |
raise pagure.exceptions.APIError(
|
| |
400, error_code=APIERROR.ENOCODE, error=str(err)
|
| |
@@ -109,3 +115,149 @@
|
| |
|
| |
_log.info("Successfully proccessed jenkins notification")
|
| |
return ("", 204)
|
| |
+
|
| |
+
|
| |
+ def _process_build(session, project, build_id, iteration=0):
|
| |
+ """Gets the build info from jenkins and flags that particular
|
| |
+ pull-request.
|
| |
+ """
|
| |
+ import jenkins
|
| |
+
|
| |
+ # This import is needed as pagure.lib relies on Project.ci_hook to be
|
| |
+ # defined and accessible and this happens in pagure.hooks.pagure_ci
|
| |
+ from pagure.hooks import pagure_ci # noqa: E402,F401
|
| |
+
|
| |
+ # Jenkins Base URL
|
| |
+ _log.info("Querying jenkins at: %s", project.ci_hook.ci_url)
|
| |
+ jenk = jenkins.Jenkins(
|
| |
+ project.ci_hook.ci_url,
|
| |
+ username=project.ci_hook.ci_username or None,
|
| |
+ password=project.ci_hook.ci_password or None,
|
| |
+ )
|
| |
+ jenkins_name = project.ci_hook.ci_job
|
| |
+ _log.info(
|
| |
+ "Querying jenkins for project: %s, build: %s", jenkins_name, build_id
|
| |
+ )
|
| |
+ try:
|
| |
+ build_info = jenk.get_build_info(jenkins_name, build_id)
|
| |
+ except jenkins.NotFoundException:
|
| |
+ _log.debug("Could not find build %s at: %s", build_id, jenkins_name)
|
| |
+ raise pagure.exceptions.PagureException(
|
| |
+ "Could not find build %s at: %s" % (build_id, jenkins_name)
|
| |
+ )
|
| |
+
|
| |
+ if build_info.get("building") is True:
|
| |
+ if iteration < 5:
|
| |
+ _log.info("Build is still going, let's wait a sec and try again")
|
| |
+ time.sleep(1)
|
| |
+ return _process_build(
|
| |
+ session, project, build_id, iteration=iteration + 1
|
| |
+ )
|
| |
+ _log.info(
|
| |
+ "We've been waiting for 5 seconds and the build is still "
|
| |
+ "not finished, so let's keep going."
|
| |
+ )
|
| |
+
|
| |
+ result = build_info.get("result")
|
| |
+ if not result and build_info.get("building") is True:
|
| |
+ result = "BUILDING"
|
| |
+
|
| |
+ _log.info("Result from jenkins: %s", result)
|
| |
+ url = build_info["url"]
|
| |
+ _log.info("URL from jenkins: %s", url)
|
| |
+
|
| |
+ pr_id = None
|
| |
+ for action in build_info["actions"]:
|
| |
+ for cause in action.get("causes", []):
|
| |
+ try:
|
| |
+ pr_id = int(cause["note"])
|
| |
+ except (KeyError, ValueError):
|
| |
+ continue
|
| |
+
|
| |
+ if not pr_id:
|
| |
+ raise pagure.exceptions.NoCorrespondingPR("No corresponding PR found")
|
| |
+
|
| |
+ if not result or result not in BUILD_STATS:
|
| |
+ raise pagure.exceptions.PagureException(
|
| |
+ "Unknown build status: %s" % result
|
| |
+ )
|
| |
+
|
| |
+ request = pagure.lib.query.search_pull_requests(
|
| |
+ session, project_id=project.id, requestid=pr_id
|
| |
+ )
|
| |
+
|
| |
+ if not request:
|
| |
+ raise pagure.exceptions.PagureException("Request not found")
|
| |
+
|
| |
+ comment, state, percent = BUILD_STATS[result]
|
| |
+ comment = comment % build_id
|
| |
+ # Adding build ID to the CI type
|
| |
+ username = "%s" % project.ci_hook.ci_type
|
| |
+ if request.commit_stop:
|
| |
+ comment += " (commit: %s)" % (request.commit_stop[:8])
|
| |
+
|
| |
+ uid = None
|
| |
+ for flag in request.flags:
|
| |
+ if (
|
| |
+ flag.status == pagure_config["FLAG_PENDING"]
|
| |
+ and flag.username == username
|
| |
+ ):
|
| |
+ uid = flag.uid
|
| |
+ break
|
| |
+
|
| |
+ _log.info("Flag's UID: %s", uid)
|
| |
+ pagure.lib.query.add_pull_request_flag(
|
| |
+ session,
|
| |
+ request=request,
|
| |
+ username=username,
|
| |
+ percent=percent,
|
| |
+ comment=comment,
|
| |
+ url=url,
|
| |
+ status=state,
|
| |
+ uid=uid,
|
| |
+ user=project.user.username,
|
| |
+ token=None,
|
| |
+ )
|
| |
+ session.commit()
|
| |
+
|
| |
+
|
| |
+ def trigger_build(
|
| |
+ project_path,
|
| |
+ url,
|
| |
+ job,
|
| |
+ token,
|
| |
+ branch,
|
| |
+ branch_to,
|
| |
+ cause,
|
| |
+ ci_username=None,
|
| |
+ ci_password=None,
|
| |
+ ):
|
| |
+ """Trigger a build on a jenkins instance."""
|
| |
+ try:
|
| |
+ import jenkins
|
| |
+ except ImportError:
|
| |
+ _log.error("Pagure-CI: Failed to load the jenkins module, bailing")
|
| |
+ return
|
| |
+
|
| |
+ _log.info("Jenkins CI")
|
| |
+
|
| |
+ repo = "%s/%s" % (pagure_config["GIT_URL_GIT"].rstrip("/"), project_path)
|
| |
+
|
| |
+ data = {
|
| |
+ "cause": cause,
|
| |
+ "REPO": repo,
|
| |
+ "BRANCH": branch,
|
| |
+ "BRANCH_TO": branch_to,
|
| |
+ }
|
| |
+
|
| |
+ server = jenkins.Jenkins(
|
| |
+ url, username=ci_username or None, password=ci_password or None
|
| |
+ )
|
| |
+ _log.info(
|
| |
+ "Pagure-CI: Triggering at: %s for: %s - data: %s", url, job, data
|
| |
+ )
|
| |
+ try:
|
| |
+ server.build_job(name=job, parameters=data, token=token)
|
| |
+ _log.info("Pagure-CI: Build triggered")
|
| |
+ except Exception as err:
|
| |
+ _log.info("Pagure-CI:An error occured: %s", err)
|
| |
First step to support additional CI types other than Jenkins.
This should make it generic enough that adding a new file to
pagure/api/ci/
and adjustingPAGURE_CI_SERVICES
enables it.The tests still use Jenkins, it's the currently available CI type and testing the general
Pagure CI
functionality with it is fine.New CI type should then define additional tests, specific to themselves, in
tests/test_<ci_type>.py
.Problem
Currently pagure supports only Jenkins out-of-the-box.
A plugin system is in place but there is Jenkins specific code in global libraries.
Solution
Refactor the code to move all Jenkins specific parts into
pagure/api/ci/jenkins.py
Affected files:
- pagure/lib/tasks_services.py
- pagure/api/init.py
- pagure/hooks/pagure_ci.py
- pagure/lib/lib_ci.py
Goal
Pagure core handles all CI types generic.
Specific code is only located in a single file
pagure/api/ci/<ci_type>.py
.They are loaded by adding the name to the config Parameter
PAGURE_CI_SERVICES
.