##// END OF EJS Templates
requirements: bumped deps
requirements: bumped deps

File last commit:

r1154:1d9af4ac default
r1158:ce7409c6 default
Show More
hooks.py
779 lines | 23.0 KiB | text/x-python | PythonLexer
initial commit
r0 # RhodeCode VCSServer provides access to different vcs backends via network.
source-code: updated copyrights to 2023
r1126 # Copyright (C) 2014-2023 RhodeCode GmbH
initial commit
r0 #
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# 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 General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
exception-handling: better handling of remote exception and logging....
r171 import io
hooks: added new hooks for ssh support
r276 import os
exception-handling: better handling of remote exception and logging....
r171 import sys
import logging
initial commit
r0 import collections
import importlib
svn: added support for hooks management of git and subversion....
r407 import base64
python3: code change for py3 support...
r1048 import msgpack
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 import dataclasses
import pygit2
exception-handling: better handling of remote exception and logging....
r171
hooks: cleanup connection
r1110 import http.client
initial commit
r0
import mercurial.scmutil
import mercurial.node
python3: code change for py3 support...
r1048 from vcsserver.lib.rc_json import json
git: use subprocessio for hooks execution to use non-blocking subprocess.
r369 from vcsserver import exceptions, subprocessio, settings
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 from vcsserver.str_utils import ascii_str, safe_str
core: renamed remote packages to prevent conflicts with builtin libraries like svn core library called same as svn remote
r1145 from vcsserver.remote.git_remote import Repository
initial commit
r0
exception-handling: better handling of remote exception and logging....
r171 log = logging.getLogger(__name__)
initial commit
r0
lint: auto-fixes
r1152 class HooksHttpClient:
python3: code change for py3 support...
r1048 proto = 'msgpack.v1'
initial commit
r0 connection = None
def __init__(self, hooks_uri):
self.hooks_uri = hooks_uri
hooks: cleanup connection
r1110 def __repr__(self):
return f'{self.__class__}(hook_uri={self.hooks_uri}, proto={self.proto})'
initial commit
r0 def __call__(self, method, extras):
hooks: cleanup connection
r1110 connection = http.client.HTTPConnection(self.hooks_uri)
python3: code change for py3 support...
r1048 # binary msgpack body
headers, body = self._serialize(method, extras)
hooks: cleanup connection
r1110 log.debug('Doing a new hooks call using HTTPConnection to %s', self.hooks_uri)
hooks: add extra debug/logging to help track hook errors.
r591 try:
hooks: cleanup connection
r1110 try:
connection.request('POST', '/', body, headers)
except Exception as error:
log.error('Hooks calling Connection failed on %s, org error: %s', connection.__dict__, error)
raise
response = connection.getresponse()
try:
return msgpack.load(response)
except Exception:
response_data = response.read()
log.exception('Failed to decode hook response json data. '
'response_code:%s, raw_data:%s',
response.status, response_data)
raise
finally:
connection.close()
initial commit
r0
python3: code change for py3 support...
r1048 @classmethod
def _serialize(cls, hook_name, extras):
initial commit
r0 data = {
'method': hook_name,
'extras': extras
}
python3: code change for py3 support...
r1048 headers = {
hooks: cleanup connection
r1110 "rc-hooks-protocol": cls.proto,
"Connection": "keep-alive"
python3: code change for py3 support...
r1048 }
return headers, msgpack.packb(data)
initial commit
r0
lint: auto-fixes
r1152 class HooksDummyClient:
initial commit
r0 def __init__(self, hooks_module):
self._hooks_module = importlib.import_module(hooks_module)
def __call__(self, hook_name, extras):
with self._hooks_module.Hooks() as hooks:
return getattr(hooks, hook_name)(extras)
lint: auto-fixes
r1152 class HooksShadowRepoClient:
hooks: added shadow repo dedicated dummy hook....
r780
def __call__(self, hook_name, extras):
return {'output': '', 'status': 0}
lint: auto-fixes
r1152 class RemoteMessageWriter:
initial commit
r0 """Writer base class."""
hooks: fixed function signature.
r222 def write(self, message):
initial commit
r0 raise NotImplementedError()
class HgMessageWriter(RemoteMessageWriter):
"""Writer that knows how to send messages to mercurial clients."""
def __init__(self, ui):
self.ui = ui
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 def write(self, message: str):
initial commit
r0 # TODO: Check why the quiet flag is set by default.
old = self.ui.quiet
self.ui.quiet = False
self.ui.status(message.encode('utf-8'))
self.ui.quiet = old
class GitMessageWriter(RemoteMessageWriter):
"""Writer that knows how to send messages to git clients."""
def __init__(self, stdout=None):
self.stdout = stdout or sys.stdout
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 def write(self, message: str):
self.stdout.write(message)
initial commit
r0
svn: added support for hooks management of git and subversion....
r407 class SvnMessageWriter(RemoteMessageWriter):
"""Writer that knows how to send messages to svn clients."""
def __init__(self, stderr=None):
# SVN needs data sent to stderr for back-to-client messaging
self.stderr = stderr or sys.stderr
def write(self, message):
self.stderr.write(message.encode('utf-8'))
initial commit
r0 def _handle_exception(result):
exception_class = result.get('exception')
exception-handling: better handling of remote exception and logging....
r171 exception_traceback = result.get('exception_traceback')
core: various cleanups and fixes
r1118 log.debug('Handling hook-call exception: %s', exception_class)
exception-handling: better handling of remote exception and logging....
r171
if exception_traceback:
log.error('Got traceback from remote call:%s', exception_traceback)
initial commit
r0 if exception_class == 'HTTPLockedRC':
exceptions: use new wrapper that store the org exception inside the newly generated exceptions....
r490 raise exceptions.RepositoryLockedException()(*result['exception_args'])
hooks: adjust to handle protected branch cases, and force push.
r509 elif exception_class == 'HTTPBranchProtected':
raise exceptions.RepositoryBranchProtectedException()(*result['exception_args'])
initial commit
r0 elif exception_class == 'RepositoryError':
exceptions: use new wrapper that store the org exception inside the newly generated exceptions....
r490 raise exceptions.VcsException()(*result['exception_args'])
initial commit
r0 elif exception_class:
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 raise Exception(
f"""Got remote exception "{exception_class}" with args "{result['exception_args']}" """
)
initial commit
r0
def _get_hooks_client(extras):
hooks: added shadow repo dedicated dummy hook....
r780 hooks_uri = extras.get('hooks_uri')
is_shadow_repo = extras.get('is_shadow_repo')
core: various cleanups and fixes
r1118
hooks: added shadow repo dedicated dummy hook....
r780 if hooks_uri:
core: finished removal of pyro4....
r213 return HooksHttpClient(extras['hooks_uri'])
hooks: added shadow repo dedicated dummy hook....
r780 elif is_shadow_repo:
return HooksShadowRepoClient()
initial commit
r0 else:
return HooksDummyClient(extras['hooks_module'])
def _call_hook(hook_name, extras, writer):
svn: added support for hooks management of git and subversion....
r407 hooks_client = _get_hooks_client(extras)
log.debug('Hooks, using client:%s', hooks_client)
result = hooks_client(hook_name, extras)
hooks: added new hooks for ssh support
r276 log.debug('Hooks got result: %s', result)
hooks: handle errors before trying to fetch the output.
r529 _handle_exception(result)
initial commit
r0 writer.write(result['output'])
return result['status']
def _extras_from_ui(ui):
py3: fixed mercurial clone
r1052 hook_data = ui.config(b'rhodecode', b'RC_SCM_DATA')
hooks: added new hooks for ssh support
r276 if not hook_data:
# maybe it's inside environ ?
hooks: use safer method of exporting the RC_SCM_DATA, prevents JSON decode errors in case of None value
r333 env_hook_data = os.environ.get('RC_SCM_DATA')
if env_hook_data:
hook_data = env_hook_data
hooks: fix case for SSH hooks executed in certain condition with errors....
r334 extras = {}
if hook_data:
extras = json.loads(hook_data)
initial commit
r0 return extras
hooks: adjust to handle protected branch cases, and force push.
r509 def _rev_range_hash(repo, node, check_heads=False):
mercurial: move imports from top-level to prevent from loading mercurial code on hook execution for svn/git
r802 from vcsserver.hgcompat import get_ctx
hooks: expose pushed refs inside hooks....
r223
commits = []
hooks: adjust to handle protected branch cases, and force push.
r509 revs = []
hg: added compat for fetching revision using new hg 4.9 code
r660 start = get_ctx(repo, node).rev()
hooks: adjust to handle protected branch cases, and force push.
r509 end = len(repo)
for rev in range(start, end):
revs.append(rev)
hg: added compat for fetching revision using new hg 4.9 code
r660 ctx = get_ctx(repo, rev)
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 commit_id = ascii_str(mercurial.node.hex(ctx.node()))
branch = safe_str(ctx.branch())
hooks: expose pushed refs inside hooks....
r223 commits.append((commit_id, branch))
hooks: adjust to handle protected branch cases, and force push.
r509 parent_heads = []
if check_heads:
parent_heads = _check_heads(repo, start, end, revs)
return commits, parent_heads
def _check_heads(repo, start, end, commits):
mercurial: move imports from top-level to prevent from loading mercurial code on hook execution for svn/git
r802 from vcsserver.hgcompat import get_ctx
hooks: adjust to handle protected branch cases, and force push.
r509 changelog = repo.changelog
parents = set()
for new_rev in commits:
for p in changelog.parentrevs(new_rev):
if p == mercurial.node.nullrev:
continue
if p < start:
parents.add(p)
for p in parents:
hg: added compat for fetching revision using new hg 4.9 code
r660 branch = get_ctx(repo, p).branch()
hooks: adjust to handle protected branch cases, and force push.
r509 # The heads descending from that parent, on the same branch
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 parent_heads = {p}
reachable = {p}
py3: drop xrange
r982 for x in range(p + 1, end):
hg: added compat for fetching revision using new hg 4.9 code
r660 if get_ctx(repo, x).branch() != branch:
hooks: adjust to handle protected branch cases, and force push.
r509 continue
for pp in changelog.parentrevs(x):
if pp in reachable:
reachable.add(x)
parent_heads.discard(pp)
parent_heads.add(x)
# More than one head? Suggest merging
if len(parent_heads) > 1:
return list(parent_heads)
return []
hooks: expose pushed refs inside hooks....
r223
hooks: expose few extra variables from hook calls
r557 def _get_git_env():
env = {}
for k, v in os.environ.items():
if k.startswith('GIT'):
env[k] = v
# serialized version
return [(k, v) for k, v in env.items()]
def _get_hg_env(old_rev, new_rev, txnid, repo_path):
env = {}
for k, v in os.environ.items():
if k.startswith('HG'):
env[k] = v
env['HG_NODE'] = old_rev
env['HG_NODE_LAST'] = new_rev
env['HG_TXNID'] = txnid
env['HG_PENDING'] = repo_path
return [(k, v) for k, v in env.items()]
hooks: added new hooks for ssh support
r276 def repo_size(ui, repo, **kwargs):
extras = _extras_from_ui(ui)
return _call_hook('repo_size', extras, HgMessageWriter(ui))
def pre_pull(ui, repo, **kwargs):
extras = _extras_from_ui(ui)
return _call_hook('pre_pull', extras, HgMessageWriter(ui))
def pre_pull_ssh(ui, repo, **kwargs):
hooks: fix case for SSH hooks executed in certain condition with errors....
r334 extras = _extras_from_ui(ui)
if extras and extras.get('SSH'):
hooks: added new hooks for ssh support
r276 return pre_pull(ui, repo, **kwargs)
return 0
def post_pull(ui, repo, **kwargs):
extras = _extras_from_ui(ui)
return _call_hook('post_pull', extras, HgMessageWriter(ui))
def post_pull_ssh(ui, repo, **kwargs):
hooks: fix case for SSH hooks executed in certain condition with errors....
r334 extras = _extras_from_ui(ui)
if extras and extras.get('SSH'):
hooks: added new hooks for ssh support
r276 return post_pull(ui, repo, **kwargs)
return 0
hooks: added changes to propagate commit metadata on pre-push....
r170 def pre_push(ui, repo, node=None, **kwargs):
hooks: adjust to handle protected branch cases, and force push.
r509 """
Mercurial pre_push hook
"""
hooks: added changes to propagate commit metadata on pre-push....
r170 extras = _extras_from_ui(ui)
hooks: adjust to handle protected branch cases, and force push.
r509 detect_force_push = extras.get('detect_force_push')
hooks: added changes to propagate commit metadata on pre-push....
r170
rev_data = []
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 hook_type: str = safe_str(kwargs.get('hooktype'))
if node and hook_type == 'pretxnchangegroup':
hooks: added changes to propagate commit metadata on pre-push....
r170 branches = collections.defaultdict(list)
hooks: adjust to handle protected branch cases, and force push.
r509 commits, _heads = _rev_range_hash(repo, node, check_heads=detect_force_push)
for commit_id, branch in commits:
hooks: added changes to propagate commit metadata on pre-push....
r170 branches[branch].append(commit_id)
hooks: adjust to handle protected branch cases, and force push.
r509 for branch, commits in branches.items():
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 old_rev = ascii_str(kwargs.get('node_last')) or commits[0]
hooks: added changes to propagate commit metadata on pre-push....
r170 rev_data.append({
hooks: expose few extra variables from hook calls
r557 'total_commits': len(commits),
hooks: added changes to propagate commit metadata on pre-push....
r170 'old_rev': old_rev,
'new_rev': commits[-1],
'ref': '',
'type': 'branch',
'name': branch,
})
hooks: adjust to handle protected branch cases, and force push.
r509 for push_ref in rev_data:
push_ref['multiple_heads'] = _heads
hooks: expose few extra variables from hook calls
r557 repo_path = os.path.join(
extras.get('repo_store', ''), extras.get('repository', ''))
push_ref['hg_env'] = _get_hg_env(
old_rev=push_ref['old_rev'],
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 new_rev=push_ref['new_rev'], txnid=ascii_str(kwargs.get('txnid')),
hooks: expose few extra variables from hook calls
r557 repo_path=repo_path)
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 extras['hook_type'] = hook_type or 'pre_push'
hooks: added changes to propagate commit metadata on pre-push....
r170 extras['commit_ids'] = rev_data
hooks: expose few extra variables from hook calls
r557
hooks: added changes to propagate commit metadata on pre-push....
r170 return _call_hook('pre_push', extras, HgMessageWriter(ui))
initial commit
r0
hooks: added new hooks for ssh support
r276 def pre_push_ssh(ui, repo, node=None, **kwargs):
git-hooks: store git-env into hooks, so we can use the sandbox storage area, and execute...
r510 extras = _extras_from_ui(ui)
if extras.get('SSH'):
hooks: added new hooks for ssh support
r276 return pre_push(ui, repo, node, **kwargs)
return 0
def pre_push_ssh_auth(ui, repo, node=None, **kwargs):
hooks: adjust to handle protected branch cases, and force push.
r509 """
Mercurial pre_push hook for SSH
"""
hooks: added new hooks for ssh support
r276 extras = _extras_from_ui(ui)
if extras.get('SSH'):
permission = extras['SSH_PERMISSIONS']
if 'repository.write' == permission or 'repository.admin' == permission:
return 0
# non-zero ret code
return 1
return 0
hooks: expose pushed refs inside hooks....
r223 def post_push(ui, repo, node, **kwargs):
hooks: adjust to handle protected branch cases, and force push.
r509 """
Mercurial post_push hook
"""
hooks: expose pushed refs inside hooks....
r223 extras = _extras_from_ui(ui)
commit_ids = []
branches = []
bookmarks = []
tags = []
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 hook_type: str = safe_str(kwargs.get('hooktype'))
initial commit
r0
hooks: adjust to handle protected branch cases, and force push.
r509 commits, _heads = _rev_range_hash(repo, node)
for commit_id, branch in commits:
hooks: expose pushed refs inside hooks....
r223 commit_ids.append(commit_id)
if branch not in branches:
branches.append(branch)
initial commit
r0
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 if hasattr(ui, '_rc_pushkey_bookmarks'):
bookmarks = ui._rc_pushkey_bookmarks
initial commit
r0
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 extras['hook_type'] = hook_type or 'post_push'
initial commit
r0 extras['commit_ids'] = commit_ids
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108
hooks: expose pushed refs inside hooks....
r223 extras['new_refs'] = {
'branches': branches,
'bookmarks': bookmarks,
'tags': tags
}
initial commit
r0
return _call_hook('post_push', extras, HgMessageWriter(ui))
hooks: added new hooks for ssh support
r276 def post_push_ssh(ui, repo, node, **kwargs):
hooks: adjust to handle protected branch cases, and force push.
r509 """
Mercurial post_push hook for SSH
"""
hooks: added new hooks for ssh support
r276 if _extras_from_ui(ui).get('SSH'):
return post_push(ui, repo, node, **kwargs)
return 0
hooks: expose post-key-push hook for Mercurial.
r221 def key_push(ui, repo, **kwargs):
mercurial: move imports from top-level to prevent from loading mercurial code on hook execution for svn/git
r802 from vcsserver.hgcompat import get_ctx
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108
if kwargs['new'] != b'0' and kwargs['namespace'] == b'bookmarks':
hooks: expose post-key-push hook for Mercurial.
r221 # store new bookmarks in our UI object propagated later to post_push
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 ui._rc_pushkey_bookmarks = get_ctx(repo, kwargs['key']).bookmarks()
hooks: expose post-key-push hook for Mercurial.
r221 return
hooks: added new hooks for ssh support
r276
initial commit
r0 # backward compat
log_pull_action = post_pull
# backward compat
log_push_action = post_push
def handle_git_pre_receive(unused_repo_path, unused_revs, unused_env):
"""
Old hook name: keep here for backward compatibility.
This is only required when the installed git hooks are not upgraded.
"""
pass
def handle_git_post_receive(unused_repo_path, unused_revs, unused_env):
"""
Old hook name: keep here for backward compatibility.
This is only required when the installed git hooks are not upgraded.
"""
pass
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 @dataclasses.dataclass
class HookResponse:
status: int
output: str
initial commit
r0
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 def git_pre_pull(extras) -> HookResponse:
initial commit
r0 """
Pre pull hook.
:param extras: dictionary containing the keys defined in simplevcs
:type extras: dict
:return: status code of the hook. 0 for success.
:rtype: int
"""
python3: code change for py3 support...
r1048
initial commit
r0 if 'pull' not in extras['hooks']:
return HookResponse(0, '')
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 stdout = io.StringIO()
initial commit
r0 try:
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 status_code = _call_hook('pre_pull', extras, GitMessageWriter(stdout))
python3: code change for py3 support...
r1048
initial commit
r0 except Exception as error:
python3: code change for py3 support...
r1048 log.exception('Failed to call pre_pull hook')
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 status_code = 128
stdout.write(f'ERROR: {error}\n')
initial commit
r0
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 return HookResponse(status_code, stdout.getvalue())
initial commit
r0
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 def git_post_pull(extras) -> HookResponse:
initial commit
r0 """
Post pull hook.
:param extras: dictionary containing the keys defined in simplevcs
:type extras: dict
:return: status code of the hook. 0 for success.
:rtype: int
"""
if 'pull' not in extras['hooks']:
return HookResponse(0, '')
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 stdout = io.StringIO()
initial commit
r0 try:
status = _call_hook('post_pull', extras, GitMessageWriter(stdout))
except Exception as error:
status = 128
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 stdout.write(f'ERROR: {error}\n')
initial commit
r0
return HookResponse(status, stdout.getvalue())
hooks: added changes to propagate commit metadata on pre-push....
r170 def _parse_git_ref_lines(revision_lines):
rev_data = []
for revision_line in revision_lines or []:
old_rev, new_rev, ref = revision_line.strip().split(' ')
ref_data = ref.split('/', 2)
if ref_data[1] in ('tags', 'heads'):
rev_data.append({
hooks: expose few extra variables from hook calls
r557 # NOTE(marcink):
# we're unable to tell total_commits for git at this point
# but we set the variable for consistency with GIT
'total_commits': -1,
hooks: added changes to propagate commit metadata on pre-push....
r170 'old_rev': old_rev,
'new_rev': new_rev,
'ref': ref,
'type': ref_data[1],
'name': ref_data[2],
})
return rev_data
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 def git_pre_receive(unused_repo_path, revision_lines, env) -> int:
initial commit
r0 """
Pre push hook.
:return: status code of the hook. 0 for success.
"""
extras = json.loads(env['RC_SCM_DATA'])
hooks: added changes to propagate commit metadata on pre-push....
r170 rev_data = _parse_git_ref_lines(revision_lines)
initial commit
r0 if 'push' not in extras['hooks']:
return 0
hooks: adjust to handle protected branch cases, and force push.
r509 empty_commit_id = '0' * 40
detect_force_push = extras.get('detect_force_push')
for push_ref in rev_data:
git-hooks: store git-env into hooks, so we can use the sandbox storage area, and execute...
r510 # store our git-env which holds the temp store
hooks: expose few extra variables from hook calls
r557 push_ref['git_env'] = _get_git_env()
hooks: adjust to handle protected branch cases, and force push.
r509 push_ref['pruned_sha'] = ''
if not detect_force_push:
# don't check for forced-push when we don't need to
continue
type_ = push_ref['type']
new_branch = push_ref['old_rev'] == empty_commit_id
dan
hooks: handle delete branch permission case with branch protection
r643 delete_branch = push_ref['new_rev'] == empty_commit_id
if type_ == 'heads' and not (new_branch or delete_branch):
hooks: adjust to handle protected branch cases, and force push.
r509 old_rev = push_ref['old_rev']
new_rev = push_ref['new_rev']
python3: fixes and code optimization for python3.11
r1114 cmd = [settings.GIT_EXECUTABLE, 'rev-list', old_rev, f'^{new_rev}']
hooks: adjust to handle protected branch cases, and force push.
r509 stdout, stderr = subprocessio.run_command(
cmd, env=os.environ.copy())
dan
hooks: handle delete branch permission case with branch protection
r643 # means we're having some non-reachable objects, this forced push was used
hooks: adjust to handle protected branch cases, and force push.
r509 if stdout:
push_ref['pruned_sha'] = stdout.splitlines()
hooks: store hook type for extensions.
r555 extras['hook_type'] = 'pre_receive'
hooks: added changes to propagate commit metadata on pre-push....
r170 extras['commit_ids'] = rev_data
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108
stdout = sys.stdout
status_code = _call_hook('pre_push', extras, GitMessageWriter(stdout))
return status_code
initial commit
r0
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 def git_post_receive(unused_repo_path, revision_lines, env) -> int:
initial commit
r0 """
Post push hook.
:return: status code of the hook. 0 for success.
"""
extras = json.loads(env['RC_SCM_DATA'])
if 'push' not in extras['hooks']:
return 0
hooks: added changes to propagate commit metadata on pre-push....
r170 rev_data = _parse_git_ref_lines(revision_lines)
initial commit
r0
git_revs = []
# N.B.(skreft): it is ok to just call git, as git before calling a
# subcommand sets the PATH environment variable so that it point to the
# correct version of the git executable.
empty_commit_id = '0' * 40
hooks: expose pushed refs inside hooks....
r223 branches = []
tags = []
initial commit
r0 for push_ref in rev_data:
type_ = push_ref['type']
hooks: expose pushed refs inside hooks....
r223
initial commit
r0 if type_ == 'heads':
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 # starting new branch case
initial commit
r0 if push_ref['old_rev'] == empty_commit_id:
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 push_ref_name = push_ref['name']
if push_ref_name not in branches:
branches.append(push_ref_name)
initial commit
r0
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 need_head_set = ''
with Repository(os.getcwd()) as repo:
try:
repo.head
except pygit2.GitError:
need_head_set = f'refs/heads/{push_ref_name}'
initial commit
r0
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 if need_head_set:
repo.set_head(need_head_set)
print(f"Setting default branch to {push_ref_name}")
cmd = [settings.GIT_EXECUTABLE, 'for-each-ref', '--format=%(refname)', 'refs/heads/*']
subprocess: use subprocessio helper to run various subprocess commands.
r370 stdout, stderr = subprocessio.run_command(
cmd, env=os.environ.copy())
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 heads = safe_str(stdout)
initial commit
r0 heads = heads.replace(push_ref['ref'], '')
git: fixed code after version upgrade
r434 heads = ' '.join(head for head
in heads.splitlines() if head) or '.'
subprocess: use subprocessio helper to run various subprocess commands.
r370 cmd = [settings.GIT_EXECUTABLE, 'log', '--reverse',
'--pretty=format:%H', '--', push_ref['new_rev'],
'--not', heads]
stdout, stderr = subprocessio.run_command(
cmd, env=os.environ.copy())
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 git_revs.extend(list(map(ascii_str, stdout.splitlines())))
# delete branch case
initial commit
r0 elif push_ref['new_rev'] == empty_commit_id:
lint: auto-fixes
r1152 git_revs.append(f'delete_branch=>{push_ref["name"]}')
initial commit
r0 else:
hooks: expose pushed refs inside hooks....
r223 if push_ref['name'] not in branches:
branches.append(push_ref['name'])
git: use subprocessio for hooks execution to use non-blocking subprocess.
r369 cmd = [settings.GIT_EXECUTABLE, 'log',
strings: fix formatting
r1154 f'{push_ref["old_rev"]}..{push_ref["new_rev"]}',
initial commit
r0 '--reverse', '--pretty=format:%H']
subprocess: use subprocessio helper to run various subprocess commands.
r370 stdout, stderr = subprocessio.run_command(
cmd, env=os.environ.copy())
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 # we get bytes from stdout, we need str to be consistent
log_revs = list(map(ascii_str, stdout.splitlines()))
git_revs.extend(log_revs)
# Pure pygit2 impl. but still 2-3x slower :/
# results = []
#
# with Repository(os.getcwd()) as repo:
# repo_new_rev = repo[push_ref['new_rev']]
# repo_old_rev = repo[push_ref['old_rev']]
# walker = repo.walk(repo_new_rev.id, pygit2.GIT_SORT_TOPOLOGICAL)
#
# for commit in walker:
# if commit.id == repo_old_rev.id:
# break
# results.append(commit.id.hex)
# # reverse the order, can't use GIT_SORT_REVERSE
# log_revs = results[::-1]
initial commit
r0 elif type_ == 'tags':
hooks: expose pushed refs inside hooks....
r223 if push_ref['name'] not in tags:
tags.append(push_ref['name'])
lint: auto-fixes
r1152 git_revs.append(f'tag=>{push_ref["name"]}')
initial commit
r0
hooks: store hook type for extensions.
r555 extras['hook_type'] = 'post_receive'
initial commit
r0 extras['commit_ids'] = git_revs
hooks: expose pushed refs inside hooks....
r223 extras['new_refs'] = {
'branches': branches,
'bookmarks': [],
'tags': tags,
}
initial commit
r0
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 stdout = sys.stdout
initial commit
r0 if 'repo_size' in extras['hooks']:
try:
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 _call_hook('repo_size', extras, GitMessageWriter(stdout))
linting
r1095 except Exception:
initial commit
r0 pass
hooks: added few python3 related fixes to handle bytes vs str on Mercurial hooks
r1108 status_code = _call_hook('post_push', extras, GitMessageWriter(stdout))
return status_code
svn: added support for hooks management of git and subversion....
r407
svn: make hooks safe and fully backward compatible....
r436 def _get_extras_from_txn_id(path, txn_id):
extras = {}
try:
svn: expose txn_id in pre-commit hooks so we can analyze it in pre transaction hooks.
r670 cmd = [settings.SVNLOOK_EXECUTABLE, 'pget',
svn: make hooks safe and fully backward compatible....
r436 '-t', txn_id,
'--revprop', path, 'rc-scm-extras']
stdout, stderr = subprocessio.run_command(
cmd, env=os.environ.copy())
extras = json.loads(base64.urlsafe_b64decode(stdout))
except Exception:
log.exception('Failed to extract extras info from txn_id')
return extras
hooks: expose few extra variables from hook calls
r557 def _get_extras_from_commit_id(commit_id, path):
extras = {}
try:
svn: expose txn_id in pre-commit hooks so we can analyze it in pre transaction hooks.
r670 cmd = [settings.SVNLOOK_EXECUTABLE, 'pget',
hooks: expose few extra variables from hook calls
r557 '-r', commit_id,
'--revprop', path, 'rc-scm-extras']
stdout, stderr = subprocessio.run_command(
cmd, env=os.environ.copy())
extras = json.loads(base64.urlsafe_b64decode(stdout))
except Exception:
log.exception('Failed to extract extras info from commit_id')
return extras
svn: added support for hooks management of git and subversion....
r407 def svn_pre_commit(repo_path, commit_data, env):
path, txn_id = commit_data
branches = []
tags = []
svn: make hooks safe and fully backward compatible....
r436 if env.get('RC_SCM_DATA'):
extras = json.loads(env['RC_SCM_DATA'])
else:
# fallback method to read from TXN-ID stored data
extras = _get_extras_from_txn_id(path, txn_id)
if not extras:
return 0
svn: added support for hooks management of git and subversion....
r407
hooks: fixed SVN hook problem
r575 extras['hook_type'] = 'pre_commit'
svn: expose txn_id in pre-commit hooks so we can analyze it in pre transaction hooks.
r670 extras['commit_ids'] = [txn_id]
svn: added support for hooks management of git and subversion....
r407 extras['txn_id'] = txn_id
extras['new_refs'] = {
hooks: expose few extra variables from hook calls
r557 'total_commits': 1,
svn: added support for hooks management of git and subversion....
r407 'branches': branches,
'bookmarks': [],
'tags': tags,
}
svn: make hooks safe and fully backward compatible....
r436
svn: added support for hooks management of git and subversion....
r407 return _call_hook('pre_push', extras, SvnMessageWriter())
def svn_post_commit(repo_path, commit_data, env):
"""
commit_data is path, rev, txn_id
"""
svn: allow legacy (pre SVN 1.7) extraction of post commit data....
r824 if len(commit_data) == 3:
path, commit_id, txn_id = commit_data
elif len(commit_data) == 2:
log.error('Failed to extract txn_id from commit_data using legacy method. '
'Some functionality might be limited')
path, commit_id = commit_data
txn_id = None
svn: added support for hooks management of git and subversion....
r407 branches = []
tags = []
svn: make hooks safe and fully backward compatible....
r436 if env.get('RC_SCM_DATA'):
extras = json.loads(env['RC_SCM_DATA'])
else:
# fallback method to read from TXN-ID stored data
extras = _get_extras_from_commit_id(commit_id, path)
if not extras:
return 0
svn: added support for hooks management of git and subversion....
r407
hooks: fixed SVN hook problem
r575 extras['hook_type'] = 'post_commit'
svn: added support for hooks management of git and subversion....
r407 extras['commit_ids'] = [commit_id]
extras['txn_id'] = txn_id
extras['new_refs'] = {
'branches': branches,
'bookmarks': [],
'tags': tags,
hooks: expose few extra variables from hook calls
r557 'total_commits': 1,
svn: added support for hooks management of git and subversion....
r407 }
if 'repo_size' in extras['hooks']:
try:
_call_hook('repo_size', extras, SvnMessageWriter())
svn: make hooks safe and fully backward compatible....
r436 except Exception:
svn: added support for hooks management of git and subversion....
r407 pass
return _call_hook('post_push', extras, SvnMessageWriter())