| #!/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 |