blob: a8906b664cfdf6587c7ac214fd188c6a47e625e4 [file] [log] [blame]
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL2.0
# Copyright Thomas Gleixner <tglx@linutronix.de>
from tipbot.git import git_repo
from tipbot.util import FatalException, FetchException
from tipbot.mail import mailer
import subprocess
import tempfile
import signal
import time
import os
class tipbot(object):
def __init__(self, args, logger):
self.args = args
self.log = logger
self._should_stop = False
self._should_reload = False
self.siginstall()
self.linus = git_repo(args.linusdir, logger)
self.tip = git_repo(args.tipdir, logger)
self.mailer = mailer(args, logger)
def term_handler(self, signum, frame):
self.log.log_debug('term_handler received SIG %d\n' % signum)
self._should_stop = True
def reload_handler(self, signum, frame):
self.log.log_debug('reload_handler received SIG %d\n' % signum)
self._should_reload = True
def siginstall(self):
signal.signal(signal.SIGINT, self.term_handler)
signal.signal(signal.SIGTERM, self.term_handler)
signal.signal(signal.SIGHUP, self.reload_handler)
def should_stop(self):
return self._should_stop
def known_commit(self, sha):
fname = sha[:2] + '-known_commits'
fname = os.path.join(self.args.known_commits, fname)
if not os.path.isfile(fname):
return False
for l in open(fname).readlines():
if l.find(sha) == 0:
return True
return False
def write_known_commit(self, sha):
fname = sha[:2] + '-known_commits'
fname = os.path.join(self.args.known_commits, fname)
with open(fname, 'a') as fd:
fd.write('%s\n' %sha)
def write_known_commits(self, shas):
for sha in shas:
if not self.known_commit(sha):
self.write_known_commit(sha)
def update_known_commits(self, shas, repomsg, repo=None):
msg = 'Update known commits from %s\n\n' %repomsg
if repo:
for sha in shas:
msg += '%s\n' %repo.shortlog(sha)
tf = tempfile.NamedTemporaryFile(delete=False)
tf.write(msg.encode('UTF-8'))
tf.close()
args = [ 'git', 'commit', '-a', '-q', '-F', '%s' %tf.name ]
res = subprocess.run(args)
try:
res.check_returncode()
os.unlink(tf.name)
except Exception as ex:
self.log.log_exception(ex, 'Commit known_commits failed\n')
os.unlink(tf.name)
raise FatalException
args = [ 'git', 'push', '-q' ]
res = subprocess.run(args)
try:
res.check_returncode()
except Exception as ex:
self.log.log_exception(ex, 'Commit push failed\n')
raise FetchException
def update_linus(self):
if self.args.forcelinus:
old_head = '0ecfebd2b52404ae0c54a878c872bb93363ada36'
self.args.forcelinus = False
else:
old_head = self.linus.get_branch_head()
self.linus.fetch()
new_head = self.linus.get_branch_head()
self.linus_head = new_head
if old_head == new_head:
self.log.log_debug("Linus head unchanged %s\n" %old_head)
return
self.log.log_debug("Linus head updated %s\n" %new_head)
shas = []
for sha in self.linus.log_revs_from(old_head):
if not self.known_commit(sha):
shas.append(sha)
if len(shas):
self.write_known_commits(shas)
self.update_known_commits(shas, 'Linus tree')
def update_tip(self):
self.tip.fetch()
self.tip.set_base_ref('refs/heads/linus-base', self.linus_head)
path = '.tip/auto-branches/auto-latest'
shas = []
for ref in self.tip.get_autobranch_refs('tip', path):
for entry in self.tip.log_revs_ref_from('linus-base', ref):
if not self.known_commit(entry) and not entry in shas:
if len(shas) >= self.args.limit:
break
branch = ref.replace('/refs/heads/', '')
branch = ref.replace('refs/heads/', '')
self.mailer.send_mail(self.tip, branch, entry)
shas.append(entry)
self.write_known_commits([entry])
if len(shas):
self.update_known_commits(shas, 'tip', self.tip)
self.log.log_debug("Tip updated %d notifications\n" %len(shas))
else:
self.log.log_debug("Tip unchanged\n")
if len(shas) >= self.args.limit:
raise Exception('Mail limit %d reached' %self.args.limit)
def run(self):
res = 0
while not self.should_stop():
try:
self.update_linus()
self.update_tip()
except FetchException as ex:
# Don't try to be smart for now except for temporary
# network and name resolution failures
if (str(ex).find('Network is unreachable') < 0 and
str(ex).find('early EOF') < 0 and
str(ex).find('Temporary failure in name resolution') < 0):
res = 1
break
except FatalException as ex:
res = 1
break
except Exception as ex:
self.log.log_exception(ex)
res = 1
break
i = 0
while i < (self.args.pause * 60) and not self.should_stop():
i += 1
time.sleep(1)
return res