Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions mailman/patchset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import git
import pathlib
import sys
import tempfile

REMOTE_URL = "http://host.containers.internal:3366/cgi-bin/git-receive-pack/grading.git" # NOQA: E501

MAIL_DIR_ABSPATH = "/var/lib/email/mail"


def try_or_false(do, exc):
try:
do()
return True
except exc as e:
print(e, file=sys.stderr)
return False


def tag_and_push(repo_path, tag_name):
try:
repo = git.Repo(repo_path)
repo.create_tag(tag_name)
repo.create_remote("origin", REMOTE_URL)
repo.git.push("origin", tags=True)
return True
except git.GitCommandError as e:
print(e, file=sys.stderr)
return False


def do_check(repo_path, cover_letter, patches):
repo = git.Repo.init(repo_path)
maildir = pathlib.Path(MAIL_DIR_ABSPATH)
author_args = ["-c", "user.name=Denis", "-c",
"user.email=daemon@mailman.d"]
git_am_args = ["git", *author_args, "am", "--keep"]
whitespace_errors = []

def am_cover_letter(keep_empty=True):
args = git_am_args
if keep_empty:
args.append("--empty=keep")
repo.git.execute([*args, str(maildir/cover_letter.msg_id)])

if try_or_false(lambda: am_cover_letter(keep_empty=False),
git.GitCommandError):
return "missing cover letter"

repo.git.execute(["git", *author_args, "am", "--abort"])
if not try_or_false(lambda: am_cover_letter(keep_empty=True),
git.GitCommandError):
return ("missing cover letter and "
"first patch failed to apply")

for i, patch in enumerate(patches):
patch_abspath = str(maildir / patch.msg_id)

# Try and apply and fail if there are whitespace errors
def do_git_am(extra_args=[]):
repo.git.execute([*git_am_args, *extra_args, patch_abspath]),

# If this fails, the patch may apply with whitespace errors
if try_or_false(lambda: do_git_am(['--whitespace=error-all']),
git.GitCommandError):
continue

repo.git.execute(["git", *author_args, "am", "--abort"])

# Try again, if we succeed, count this patch as a whitespace error
if try_or_false(lambda: do_git_am(), git.GitCommandError):
whitespace_errors.append(str(i+1))
continue

# If we still fail, the patch does not apply
return f'patch {i+1} failed to apply'

if whitespace_errors:
return ('whitespace error patch(es) '
f'{",".join(whitespace_errors)}')
else:
return 'pathchset applies'


def check(cover_letter, patches, submission_id):
with tempfile.TemporaryDirectory() as repo_path:
status = do_check(repo_path, cover_letter, patches)
tag_and_push(repo_path, submission_id)
return status
79 changes: 3 additions & 76 deletions mailman/submit.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,15 @@
import collections
from pathlib import Path
import sys
import git
import tempfile

import patchset
import db

ASSIGNMENT_LIST = ["introductions",
"exercise0", "exercise1", "exercise2",
"programming0", "programming1", "programming2",
"final0", "final1"]

MAIL_DIR_ABSPATH = "/var/lib/email/mail"

REMOTE_URL = "http://host.containers.internal:3366/cgi-bin/git-receive-pack/grading.git" # NOQA: E501


def try_or_false(do, exc):
try:
do()
return True
except exc as e:
print(e, file=sys.stderr)
return False


Email = collections.namedtuple('Email', ['rcpt', 'msg_id'])

Expand Down Expand Up @@ -78,67 +64,8 @@ def main(argv):
sub.save()
return 0

with tempfile.TemporaryDirectory() as repo_path:
repo = git.Repo.init(repo_path)
maildir = Path(MAIL_DIR_ABSPATH)
author_args = ["-c", "user.name=Denis", "-c",
"user.email=daemon@mailman.d"]
git_am_args = ["git", *author_args, "am", "--keep"]
whitespace_errors = []

def am_cover_letter(keep_empty=True):
args = git_am_args
if keep_empty:
args.append("--empty=keep")
repo.git.execute([*args, str(maildir/cover_letter.msg_id)])

if try_or_false(lambda: am_cover_letter(keep_empty=False),
git.GitCommandError):
sub.status = "missing cover letter"
sub.save()
return 0
repo.git.execute(["git", *author_args, "am", "--abort"])
if not try_or_false(lambda: am_cover_letter(keep_empty=True),
git.GitCommandError):
sub.status = ("missing cover letter and "
"first patch failed to apply")
sub.save()
return 0

for i, patch in enumerate(patches):
patch_abspath = str(maildir / patch.msg_id)

# Try and apply and fail if there are whitespace errors
def do_git_am(extra_args=[]):
repo.git.execute([*git_am_args, *extra_args, patch_abspath]),

# If this fails, the patch may apply with whitespace errors
if try_or_false(lambda: do_git_am(['--whitespace=error-all']),
git.GitCommandError):
continue

repo.git.execute(["git", *author_args, "am", "--abort"])

# Try again, if we succeed, count this patch as a whitespace error
if try_or_false(lambda: do_git_am(), git.GitCommandError):
whitespace_errors.append(str(i+1))
continue

# If we still fail, the patch does not apply
sub.status = f'patch {i+1} failed to apply'
sub.save()
return 0

if whitespace_errors:
sub.status = ('whitespace error patch(es) '
f'{",".join(whitespace_errors)}')
else:
sub.status = 'patchset applies'
sub.save()

repo.create_tag(logfile)
repo.create_remote("origin", REMOTE_URL)
repo.git.push("origin", tags=True)
sub.status = patchset.check(cover_letter, patches, submission_id=logfile)
sub.save()


if __name__ == "__main__":
Expand Down