#! /usr/bin/env python """ This is a git hook meant to warn the alternative arches folks about package that turned off building on alternative arches. """ from __future__ import print_function import os import smtplib import subprocess import sys from email.mime.text import MIMEText abspath = os.path.abspath(os.environ['GIT_DIR']) PATTERNS = ('+ExclusiveArch:', '+ExcludeArch:', '+%ifarch', '+%ifnarch', '-ExclusiveArch:', '-ExcludeArch:', '-%ifarch', '-%ifnarch') FROM_EMAIL = 'githook-noreply@fedoraproject.org' TO_MAIL = 'arch-excludes@lists.fedoraproject.org' CGIT_URL = 'https://src.fedoraproject.org/cgit/%s/commit/?id=%s' EMAIL_SEND = True SMTP_SERVER = 'localhost' SMTP_PORT = 25 DEBUG = False TEXT = u""" The package %(pkg)s has added or updated architecture specific content in its spec file (ExclusiveArch/ExcludeArch or %%ifarch/%%ifnarch) in commit(s): %(url)s. Change: %(change)s Thanks. Full change: ============ %(full_change)s """ def read_output(cmd, abspath, input=None, keepends=False, **kw): """ Read the output from the given command to run """ if input: stdin = subprocess.PIPE else: stdin = None if DEBUG: print('command called:', cmd) procs = subprocess.Popen( cmd, stdin=stdin, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=abspath, universal_newlines=True, **kw) (out, err) = procs.communicate(input) retcode = procs.wait() if retcode: print('ERROR: %s =-- %s' % (cmd, retcode)) print(out) print(err) if not keepends: out = out.rstrip('\n\r') return out def read_git_output(args, abspath, input=None, keepends=False, **kw): """Read the output of a Git command.""" return read_output( ['git'] + args, abspath, input=input, keepends=keepends, **kw) def read_git_lines(args, abspath, keepends=False, **kw): """Return the lines output by Git command. Return as single lines, with newlines stripped off.""" return read_git_output( args, abspath, keepends=keepends, **kw ).splitlines(keepends) def get_revs_between(oldrev, newrev, abspath, refname): """ Yield revisions between HEAD and BASE. """ cmd = ['rev-list', '%s...%s' % (oldrev, newrev)] if set(newrev) == set('0'): cmd = ['rev-list', '%s' % oldrev] elif set(oldrev) == set('0') or set(oldrev) == set('^0'): head = get_default_branch(abspath) cmd = ['rev-list', '%s' % newrev, '^%s' % head] if head in refname: cmd = ['rev-list', '%s' % newrev] return read_git_lines(cmd, abspath) def get_default_branch(abspath): """ Return the default branch of a repo. """ cmd = ['rev-parse', '--abbrev-ref', 'HEAD'] out = read_git_lines(cmd, abspath) if out: return out[0] else: return 'master' def send_email(text, subject, to_mail): # pragma: no cover ''' Send an email with the specified information. :arg text: the content of the email to send :arg subject: the subject of the email :arg to_mail: a string representing a list of recipient separated by a coma ''' if not to_mail: return from_email = FROM_EMAIL if not EMAIL_SEND: print('******EMAIL******') print('From: %s' % from_email) print('To: %s' % to_mail) print('Subject: %s' % subject) print('Contents:') print(text.encode('utf-8')) print('*****/EMAIL******') return smtp = smtplib.SMTP(SMTP_SERVER, SMTP_PORT) for mailto in to_mail.split(','): msg = MIMEText(text.encode('utf-8'), 'plain', 'utf-8') msg['Subject'] = subject msg['From'] = from_email # Send the message via our own SMTP server, but don't include the # envelope header. msg['To'] = mailto try: smtp.sendmail( from_email, [mailto], msg.as_string()) except smtplib.SMTPException as err: pagure.LOG.exception(err) smtp.quit() return msg def run_as_post_receive_hook(): ''' Check what was changed in the commit(s) and warn the alternative-arch folks if either ExclusiveArch or ExcludesArch are changed. ''' for line in sys.stdin: if DEBUG: print('Received:', line.strip()) (oldrev, newrev, refname) = line.strip().split(' ', 2) # Skip all the branches that are not master and do not start with an 'f' # ie: personal branches and epel if not refname.startswith(('refs/heads/master', 'refs/heads/f')): continue new_commits_list = get_revs_between(oldrev, newrev, abspath, refname) if DEBUG: print('List of commits:', new_commits_list) full_change = u'' exclude_arch = {} for commit in new_commits_list: if DEBUG: print('Diff of commit:', commit) full_change += '\n' for line in read_git_lines(['show', commit], abspath): full_change += '%s\n' % line if DEBUG: print(line) if line.strip().startswith(PATTERNS): exclude_arch[commit] = line.strip() if DEBUG: print('Commit %s selected' % commit) if exclude_arch: print('* Notifying alternative-arch people') package = '/'.join(abspath.rsplit(os.path.sep, 2)[-2:]) if DEBUG: print('Package:', package) links = [] diffs = exclude_arch.values() for commit in exclude_arch: links.append( CGIT_URL % (package, commit) ) send_email( text=TEXT % dict( pkg=package, url='\n'.join(links), change="\n".join(diffs), full_change=full_change ), subject='Architecture specific change in %s' % package, to_mail=TO_MAIL ) if __name__ == '__main__': run_as_post_receive_hook()