epicyon/git.py

222 lines
7.3 KiB
Python
Raw Normal View History

2020-05-02 10:07:50 +00:00
__filename__ = "git.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
2021-01-26 10:07:42 +00:00
__version__ = "1.2.0"
2020-05-02 10:07:50 +00:00
__maintainer__ = "Bob Mottram"
2021-09-10 16:14:50 +00:00
__email__ = "bob@libreserver.org"
2020-05-02 10:07:50 +00:00
__status__ = "Production"
2021-06-26 11:16:41 +00:00
__module_group__ = "Profile Metadata"
2020-05-02 10:07:50 +00:00
import os
2020-05-02 18:09:54 +00:00
import html
2021-12-26 12:02:29 +00:00
from utils import acct_dir
2021-12-26 17:12:07 +00:00
from utils import has_object_stringType
2020-05-02 10:07:50 +00:00
def _gitFormatContent(content: str) -> str:
2020-05-02 17:06:13 +00:00
""" replace html formatting, so that it's more
like the original patch file
"""
2020-05-03 11:39:44 +00:00
patchStr = content.replace('<br>', '\n').replace('<br />', '\n')
patchStr = patchStr.replace('<p>', '').replace('</p>', '\n')
patchStr = html.unescape(patchStr)
if 'From ' in patchStr:
patchStr = 'From ' + patchStr.split('From ', 1)[1]
return patchStr
2020-05-02 17:06:13 +00:00
2021-12-25 16:17:53 +00:00
def _getGitProjectName(base_dir: str, nickname: str, domain: str,
subject: str) -> str:
2020-05-02 11:08:38 +00:00
"""Returns the project name for a git patch
The project name should be contained within the subject line
and should match against a list of projects which the account
holder wants to receive
"""
gitProjectsFilename = \
2021-12-26 12:02:29 +00:00
acct_dir(base_dir, nickname, domain) + '/gitprojects.txt'
2020-05-02 11:08:38 +00:00
if not os.path.isfile(gitProjectsFilename):
return None
2020-05-02 16:05:28 +00:00
subjectLineWords = subject.lower().split(' ')
for word in subjectLineWords:
2020-05-02 16:29:43 +00:00
if word in open(gitProjectsFilename).read():
2020-05-02 11:08:38 +00:00
return word
2020-05-02 16:24:17 +00:00
return None
2020-05-02 11:08:38 +00:00
2021-12-25 16:17:53 +00:00
def isGitPatch(base_dir: str, nickname: str, domain: str,
2020-05-03 10:56:29 +00:00
messageType: str,
subject: str, content: str,
2021-06-20 11:28:35 +00:00
checkProjectName: bool = True) -> bool:
2020-05-02 10:07:50 +00:00
"""Is the given post content a git patch?
"""
2020-05-03 10:56:29 +00:00
if messageType != 'Note' and \
2021-11-18 18:43:58 +00:00
messageType != 'Page' and \
2020-05-03 12:52:13 +00:00
messageType != 'Patch':
2020-05-03 10:56:29 +00:00
return False
2020-05-02 10:07:50 +00:00
# must have a subject line
2020-05-02 10:19:24 +00:00
if not subject:
2020-05-02 10:07:50 +00:00
return False
if '[PATCH]' not in content:
return False
if '---' not in content:
return False
if 'diff ' not in content:
return False
if 'From ' not in content:
return False
2020-05-02 10:07:50 +00:00
if 'From:' not in content:
return False
if 'Date:' not in content:
return False
if 'Subject:' not in content:
return False
2020-05-02 14:47:30 +00:00
if '<br>' not in content:
if '<br />' not in content:
return False
if checkProjectName:
projectName = \
2021-12-25 16:17:53 +00:00
_getGitProjectName(base_dir, nickname, domain, subject)
if not projectName:
return False
2020-05-02 11:08:38 +00:00
return True
def _getGitHash(patchStr: str) -> str:
2020-05-03 11:38:09 +00:00
"""Returns the commit hash from a given patch
"""
2020-05-03 11:39:44 +00:00
patchLines = patchStr.split('\n')
2020-05-03 11:38:09 +00:00
for line in patchLines:
if line.startswith('From '):
words = line.split(' ')
if len(words) > 1:
if len(words[1]) > 20:
return words[1]
break
return None
def _getPatchDescription(patchStr: str) -> str:
2020-05-03 13:50:01 +00:00
"""Returns the description from a given patch
"""
patchLines = patchStr.split('\n')
description = ''
started = False
for line in patchLines:
if started:
if line.strip() == '---':
break
description += line + '\n'
if line.startswith('Subject:'):
started = True
return description
2021-12-25 16:17:53 +00:00
def convertPostToPatch(base_dir: str, nickname: str, domain: str,
2021-12-25 22:09:19 +00:00
post_json_object: {}) -> bool:
"""Detects whether the given post contains a patch
2020-05-03 12:52:13 +00:00
and if so then converts it to a Patch ActivityPub type
"""
2021-12-26 17:12:07 +00:00
if not has_object_stringType(post_json_object, False):
return False
2021-12-25 22:09:19 +00:00
if post_json_object['object']['type'] == 'Patch':
return True
2021-12-25 22:09:19 +00:00
if not post_json_object['object'].get('summary'):
return False
2021-12-25 22:09:19 +00:00
if not post_json_object['object'].get('content'):
return False
2021-12-25 22:09:19 +00:00
if not post_json_object['object'].get('attributedTo'):
return False
2021-12-25 22:09:19 +00:00
if not isinstance(post_json_object['object']['attributedTo'], str):
2020-08-06 16:21:46 +00:00
return False
2021-12-25 16:17:53 +00:00
if not isGitPatch(base_dir, nickname, domain,
2021-12-25 22:09:19 +00:00
post_json_object['object']['type'],
post_json_object['object']['summary'],
post_json_object['object']['content'],
False):
return False
2021-12-25 22:09:19 +00:00
patchStr = _gitFormatContent(post_json_object['object']['content'])
commitHash = _getGitHash(patchStr)
if not commitHash:
return False
2021-12-25 22:09:19 +00:00
post_json_object['object']['type'] = 'Patch'
# add a commitedBy parameter
2021-12-25 22:09:19 +00:00
if not post_json_object['object'].get('committedBy'):
post_json_object['object']['committedBy'] = \
post_json_object['object']['attributedTo']
post_json_object['object']['hash'] = commitHash
post_json_object['object']['description'] = {
"mediaType": "text/plain",
"content": _getPatchDescription(patchStr)
}
2020-05-03 12:52:13 +00:00
# remove content map
2021-12-25 22:09:19 +00:00
if post_json_object['object'].get('contentMap'):
del post_json_object['object']['contentMap']
2020-05-03 12:52:13 +00:00
print('Converted post to Patch ActivityPub type')
return True
def _gitAddFromHandle(patchStr: str, handle: str) -> str:
2020-05-03 09:48:12 +00:00
"""Adds the activitypub handle of the sender to the patch
"""
fromStr = 'AP-signed-off-by: '
2020-05-03 11:39:44 +00:00
if fromStr in patchStr:
return patchStr
2020-05-03 09:48:12 +00:00
2020-05-03 11:42:18 +00:00
patchLines = patchStr.split('\n')
2020-05-03 11:39:44 +00:00
patchStr = ''
2020-05-03 09:48:12 +00:00
for line in patchLines:
2020-05-03 11:39:44 +00:00
patchStr += line + '\n'
2020-05-03 09:48:12 +00:00
if line.startswith('From:'):
2020-05-03 11:39:44 +00:00
if fromStr not in patchStr:
patchStr += fromStr + handle + '\n'
return patchStr
2020-05-03 09:48:12 +00:00
2021-12-25 16:17:53 +00:00
def receiveGitPatch(base_dir: str, nickname: str, domain: str,
2020-05-03 13:12:52 +00:00
messageType: str, subject: str, content: str,
2020-05-03 09:48:12 +00:00
fromNickname: str, fromDomain: str) -> bool:
2020-05-02 11:08:38 +00:00
"""Receive a git patch
"""
2021-12-25 16:17:53 +00:00
if not isGitPatch(base_dir, nickname, domain,
2020-05-03 13:12:52 +00:00
messageType, subject, content):
2020-05-02 11:08:38 +00:00
return False
2020-05-02 16:05:28 +00:00
patchStr = _gitFormatContent(content)
2020-05-02 16:05:28 +00:00
2020-05-03 11:39:44 +00:00
patchLines = patchStr.split('\n')
2020-05-02 10:07:50 +00:00
patchFilename = None
2020-05-02 16:53:20 +00:00
projectDir = None
2021-12-26 12:02:29 +00:00
patchesDir = acct_dir(base_dir, nickname, domain) + '/patches'
2020-05-02 10:07:50 +00:00
# get the subject line and turn it into a filename
for line in patchLines:
if line.startswith('Subject:'):
patchSubject = \
2020-05-02 16:44:46 +00:00
line.replace('Subject:', '').replace('/', '|')
patchSubject = patchSubject.replace('[PATCH]', '').strip()
patchSubject = patchSubject.replace(' ', '_')
2020-05-02 11:08:38 +00:00
projectName = \
2021-12-25 16:17:53 +00:00
_getGitProjectName(base_dir, nickname, domain, subject)
2020-05-02 16:53:20 +00:00
if not os.path.isdir(patchesDir):
os.mkdir(patchesDir)
projectDir = patchesDir + '/' + projectName
if not os.path.isdir(projectDir):
os.mkdir(projectDir)
2020-05-02 10:07:50 +00:00
patchFilename = \
2020-05-02 16:53:20 +00:00
projectDir + '/' + patchSubject + '.patch'
2020-05-02 10:07:50 +00:00
break
if not patchFilename:
return False
2020-05-03 11:39:44 +00:00
patchStr = \
_gitAddFromHandle(patchStr, '@' + fromNickname + '@' + fromDomain)
2021-11-25 21:18:53 +00:00
try:
with open(patchFilename, 'w+') as patchFile:
patchFile.write(patchStr)
2021-11-25 21:18:53 +00:00
patchNotifyFilename = \
2021-12-26 12:02:29 +00:00
acct_dir(base_dir, nickname, domain) + '/.newPatchContent'
2021-11-25 21:18:53 +00:00
with open(patchNotifyFilename, 'w+') as patchFile:
patchFile.write(patchStr)
return True
2021-12-25 15:28:52 +00:00
except OSError as ex:
print('EX: receiveGitPatch ' + patchFilename + ' ' + str(ex))
2020-05-02 16:44:46 +00:00
return False