##// END OF EJS Templates
docs: added newline
docs: added newline

File last commit:

r5608:6d33e504 default
r5656:629f48be default
Show More
base.py
300 lines | 10.7 KiB | text/x-python | PythonLexer
core: updated copyright to 2024
r5608 # Copyright (C) 2012-2024 RhodeCode GmbH
dan
integrations: add integration support...
r411 #
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 import colander
webhook: abstract webhook handler to base for easier re-usage.
r2585 import string
import collections
import logging
integrations: restructure code and added better coverage.
r5123
integrations: implement retry to HTTP[S] calls for integrations.
r3110 import requests
integrations: fixed code for python3
r5064 import urllib.request
import urllib.parse
import urllib.error
integrations: implement retry to HTTP[S] calls for integrations.
r3110 from requests.adapters import HTTPAdapter
integrations: restructure code and added better coverage.
r5123 from requests.packages.urllib3.util import Retry
dan
integrations: added safe renderers with detailed traceback information.
r2646
from mako import exceptions
integrations: restructure code and added better coverage.
r5123
from rhodecode.lib.str_utils import safe_str
dan
integrations: add integration support...
r411
dan
integrations: added safe renderers with detailed traceback information.
r2646
webhook: abstract webhook handler to base for easier re-usage.
r2585 log = logging.getLogger(__name__)
dan
integrations: add integration support...
r411
webhook: quote URL variables to prevent url errors with special chars like # in pr title.
r3477 class UrlTmpl(string.Template):
def safe_substitute(self, **kws):
# url encode the kw for usage in url
python3: fix urllib usage
r4914 kws = {k: urllib.parse.quote(safe_str(v)) for k, v in kws.items()}
modernize: updates for python3
r5095 return super().safe_substitute(**kws)
webhook: quote URL variables to prevent url errors with special chars like # in pr title.
r3477
dan
integrations: add integration support...
r411 class IntegrationTypeBase(object):
""" Base class for IntegrationType plugins """
integrations: expose EE integrations as dummy objects to show...
r2138 is_dummy = False
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 description = ''
integrations: use classmethod for icon extraction for easier code readability.
r2576
@classmethod
def icon(cls):
return '''
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://setwww.inkscape.org/namespaces/inkscape"
viewBox="0 -256 1792 1792"
id="svg3025"
version="1.1"
inkscape:version="0.48.3.1 r9886"
width="100%"
height="100%"
sodipodi:docname="cog_font_awesome.svg">
<metadata
id="metadata3035">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs3033" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="640"
inkscape:window-height="480"
id="namedview3031"
showgrid="false"
inkscape:zoom="0.13169643"
inkscape:cx="896"
inkscape:cy="896"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="svg3025" />
<g
transform="matrix(1,0,0,-1,121.49153,1285.4237)"
id="g3027">
<path
d="m 1024,640 q 0,106 -75,181 -75,75 -181,75 -106,0 -181,-75 -75,-75 -75,-181 0,-106 75,-181 75,-75 181,-75 106,0 181,75 75,75 75,181 z m 512,109 V 527 q 0,-12 -8,-23 -8,-11 -20,-13 l -185,-28 q -19,-54 -39,-91 35,-50 107,-138 10,-12 10,-25 0,-13 -9,-23 -27,-37 -99,-108 -72,-71 -94,-71 -12,0 -26,9 l -138,108 q -44,-23 -91,-38 -16,-136 -29,-186 -7,-28 -36,-28 H 657 q -14,0 -24.5,8.5 Q 622,-111 621,-98 L 593,86 q -49,16 -90,37 L 362,16 Q 352,7 337,7 323,7 312,18 186,132 147,186 q -7,10 -7,23 0,12 8,23 15,21 51,66.5 36,45.5 54,70.5 -27,50 -41,99 L 29,495 Q 16,497 8,507.5 0,518 0,531 v 222 q 0,12 8,23 8,11 19,13 l 186,28 q 14,46 39,92 -40,57 -107,138 -10,12 -10,24 0,10 9,23 26,36 98.5,107.5 72.5,71.5 94.5,71.5 13,0 26,-10 l 138,-107 q 44,23 91,38 16,136 29,186 7,28 36,28 h 222 q 14,0 24.5,-8.5 Q 914,1391 915,1378 l 28,-184 q 49,-16 90,-37 l 142,107 q 9,9 24,9 13,0 25,-10 129,-119 165,-170 7,-8 7,-22 0,-12 -8,-23 -15,-21 -51,-66.5 -36,-45.5 -54,-70.5 26,-50 41,-98 l 183,-28 q 13,-2 21,-12.5 8,-10.5 8,-23.5 z"
id="path3029"
inkscape:connector-curvature="0"
style="fill:currentColor" />
</g>
</svg>
'''
dan
integrations: refactor/cleanup + features, fixes #4181...
r731
dan
integrations: add integration support...
r411 def __init__(self, settings):
"""
:param settings: dict of settings to be used for the integration
"""
self.settings = settings
dan
forms: add deform for integration settings forms
r518 def settings_schema(self):
dan
integrations: add integration support...
r411 """
A colander schema of settings for the integration type
"""
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 return colander.Schema()
integrations: expose EE integrations as dummy objects to show...
r2138
events: added support for pull-request-comment and commit-comment events....
r4314 def event_enabled(self, event):
"""
Checks if submitted event is enabled based on the plugin settings
:param event:
:return: bool
"""
api: added proper full permission flush on API calls when creating repos and repo groups....
r4697 allowed_events = self.settings.get('events') or []
events: added support for pull-request-comment and commit-comment events....
r4314 if event.name not in allowed_events:
log.debug('event ignored: %r event %s not in allowed set of events %s',
event, event.name, allowed_events)
return False
return True
integrations: expose EE integrations as dummy objects to show...
r2138
class EEIntegration(IntegrationTypeBase):
description = 'Integration available in RhodeCode EE edition.'
is_dummy = True
def __init__(self, name, key, settings=None):
self.display_name = name
self.key = key
integrations: extract get_auth to common shared code.
r2577 super(EEIntegration, self).__init__(settings)
integrations: expose some common methods for web based calls of integrations.
r2580 # Helpers #
dan
integrations: added branch_head into URL variables. This allow triggering more explicit...
r2864 # updating this required to update the `common_vars` as well.
webhook: use textarea and format url vars better for webhook integration....
r2584 WEBHOOK_URL_VARS = [
events: added support for pull-request-comment and commit-comment events....
r4314 # GENERAL
('General', [
('event_name', 'Unique name of the event type, e.g pullrequest-update'),
('repo_name', 'Full name of the repository'),
('repo_type', 'VCS type of repository'),
('repo_id', 'Unique id of repository'),
('repo_url', 'Repository url'),
]
),
integrations: expose some common methods for web based calls of integrations.
r2580 # extra repo fields
events: added support for pull-request-comment and commit-comment events....
r4314 ('Repository', [
('extra:<extra_key_name>', 'Extra repo variables, read from its settings.'),
]
),
integrations: expose some common methods for web based calls of integrations.
r2580 # special attrs below that we handle, using multi-call
events: added support for pull-request-comment and commit-comment events....
r4314 ('Commit push - Multicalls', [
('branch', 'Name of each branch submitted, if any.'),
('branch_head', 'Head ID of pushed branch (full sha of last commit), if any.'),
('commit_id', 'ID (full sha) of each commit submitted, if any.'),
]
),
integrations: expose some common methods for web based calls of integrations.
r2580 # pr events vars
events: added support for pull-request-comment and commit-comment events....
r4314 ('Pull request', [
('pull_request_id', 'Unique ID of the pull request.'),
('pull_request_title', 'Title of the pull request.'),
('pull_request_url', 'Pull request url.'),
('pull_request_shadow_url', 'Pull request shadow repo clone url.'),
('pull_request_commits_uid', 'Calculated UID of all commits inside the PR. '
'Changes after PR update'),
]
),
# commit comment event vars
('Commit comment', [
('commit_comment_id', 'Unique ID of the comment made on a commit.'),
('commit_comment_text', 'Text of commit comment.'),
('commit_comment_type', 'Type of comment, e.g note/todo.'),
integrations: expose some common methods for web based calls of integrations.
r2580
events: added support for pull-request-comment and commit-comment events....
r4314 ('commit_comment_f_path', 'Optionally path of file for inline comments.'),
('commit_comment_line_no', 'Line number of the file: eg o10, or n200'),
('commit_comment_commit_id', 'Commit id that comment was left at.'),
('commit_comment_commit_branch', 'Commit branch that comment was left at'),
('commit_comment_commit_message', 'Commit message that comment was left at'),
]
),
integrations: expose some common methods for web based calls of integrations.
r2580 # user who triggers the call
events: added support for pull-request-comment and commit-comment events....
r4314 ('Caller', [
('username', 'User who triggered the call.'),
('user_id', 'User id who triggered the call.'),
]
),
integrations: expose some common methods for web based calls of integrations.
r2580 ]
webhook: use textarea and format url vars better for webhook integration....
r2584 # common vars for url template used for CI plugins. Shared with webhook
CI_URL_VARS = WEBHOOK_URL_VARS
integrations: extract get_auth to common shared code.
r2577
dan
integrations: use a common logic for parsing the commits branches inside the integrations that require it....
r2644 class CommitParsingDataHandler(object):
def aggregate_branch_data(self, branches, commits):
branch_data = collections.OrderedDict()
for obj in branches:
branch_data[obj['name']] = obj
branches_commits = collections.OrderedDict()
for commit in commits:
if commit.get('git_ref_change'):
# special case for GIT that allows creating tags,
# deleting branches without associated commit
continue
commit_branch = commit['branch']
if commit_branch not in branches_commits:
_branch = branch_data[commit_branch] \
if commit_branch else commit_branch
branch_commits = {'branch': _branch,
dan
integrations: added branch_head into URL variables. This allow triggering more explicit...
r2864 'branch_head': '',
dan
integrations: use a common logic for parsing the commits branches inside the integrations that require it....
r2644 'commits': []}
branches_commits[commit_branch] = branch_commits
branch_commits = branches_commits[commit_branch]
branch_commits['commits'].append(commit)
dan
integrations: added branch_head into URL variables. This allow triggering more explicit...
r2864 branch_commits['branch_head'] = commit['raw_id']
dan
integrations: use a common logic for parsing the commits branches inside the integrations that require it....
r2644 return branches_commits
integrations: extract get_auth to common shared code.
r2577 def get_auth(settings):
from requests.auth import HTTPBasicAuth
username = settings.get('username')
password = settings.get('password')
if username and password:
return HTTPBasicAuth(username, password)
return None
integrations: expose some common methods for web based calls of integrations.
r2580
webhook: abstract webhook handler to base for easier re-usage.
r2585 def get_web_token(settings):
return settings['secret_token']
integrations: expose some common methods for web based calls of integrations.
r2580 def get_url_vars(url_vars):
events: added support for pull-request-comment and commit-comment events....
r4314 items = []
for section, section_items in url_vars:
modernize: updates for python3
r5095 items.append(f'\n*{section}*')
events: added support for pull-request-comment and commit-comment events....
r4314 for key, explanation in section_items:
items.append(' {} - {}'.format('${' + key + '}', explanation))
return '\n'.join(items)
dan
integrations: added safe renderers with detailed traceback information.
r2646
def render_with_traceback(template, *args, **kwargs):
try:
return template.render(*args, **kwargs)
except Exception:
log.error(exceptions.text_error_template().render())
raise
integrations: implement retry to HTTP[S] calls for integrations.
r3110
STATUS_400 = (400, 401, 403)
STATUS_500 = (500, 502, 504)
def requests_retry_call(
retries=3, backoff_factor=0.3, status_forcelist=STATUS_400+STATUS_500,
session=None):
"""
session = requests_retry_session()
response = session.get('http://example.com')
:param retries:
:param backoff_factor:
:param status_forcelist:
:param session:
"""
session = session or requests.Session()
retry = Retry(
total=retries,
read=retries,
connect=retries,
backoff_factor=backoff_factor,
status_forcelist=status_forcelist,
)
adapter = HTTPAdapter(max_retries=retry)
session.mount('http://', adapter)
session.mount('https://', adapter)
return session