Comment être notifié d'une nouvelle release d'un projet hébérgé sur Github

avatar-Nicolas Nicolas
Publié le

Si vous exploitez de nombreux projets publiés sur GitHub, il devient difficile avec le temps de suivre leurs mises à jour. Voici une petite solution maison pour être notifié par mail lorsque l'un de ces projets publie une release.

Le principe est simple, un script est lancé à intervalles réguliers (via un job Cron par exemple) et va se charger de :

  • consulter le fil Atom des versions publiées par le projet, ces urls sont du type : https://github.com/user/repository/releases.atom et contiennent les dernières releases avec pour chacune la date de publication, l'auteur et le contenu de la mise à jour (si renseigné par l'auteur),
  • vérifier la date de chaque release par rapport à la dernière consultation du fil,
  • envoyer un mail contenant la liste des nouveautés si des entrées sont plus récentes.

Plusieurs choix techniques pour l'implémenter, mais je suis parti sur Python, je me demande encore pourquoi (le typage est horrible, la doc craint, …) car il assure une portabilité accrue (et surtout parce que j'ai pu pompé allégrement du code pour parser le fil Atom…)

Prérequis évident : installer Python et les quelques librairies nécessaires : apt-get install python python-feedparser python-dateutil.

Voilà le script dans son intégralité (commentaire en anglais basique ; je les ai écrits, vous devriez pouvoir les comprendre ^^ ) :

#!/usr/bin/python

import feedparser
import logging
import sys
from datetime import datetime
from dateutil.parser import parse
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from subprocess import Popen, PIPE

# declare projects
github_release_rss_urls = [
'https://github.com/nioc/tumblr-photo-browse/releases.atom',
'https://github.com/nioc/web-music-player/releases.atom'
]

# declare mail parameters
mail_from = 'github-notifier'
mail_to = 'vous@domain.tld'

# declare log file
log_file = '/var/log/github-notifier.log'

# declare timestamp file
timestamp_filename = '/usr/local/bin/github-release-notifier/github_notifier_timestamp'

# Log level of the script. Values are :
#   logging.DEBUG
#   logging.INFO
#   logging.WARNING
#   logging.ERROR
#   logging.CRITICAL
log_level = logging.INFO

#======================= DO NOT CHANGE BELOW =======================#

try:
    logging.basicConfig(format='%(asctime)s %(levelname)-8s %(message)s', datefmt='%b %d %Y %H:%M:%S', level=log_level, filename=log_file)
    logging.info('Start of github-notifier')
except IOError as e:
    sys.exit(1)

# get last timestamp
timestamp = parse(datetime.utcnow().isoformat())
try:
    logging.debug('  Read last timestamp')
    previous_timestamp_file = open(timestamp_filename, 'r')
    previous_timestamp = parse(previous_timestamp_file.read())
    previous_timestamp_file.close()
except IOError as e:
    logging.warning('  No file found for last timestamp on local system')
    previous_timestamp = parse(datetime.utcnow().isoformat())
except ValueError as e:
    logging.error('  Invalid last timestamp')
    previous_timestamp = parse(datetime.utcnow().isoformat())
logging.debug('  Last timestamp='+previous_timestamp.isoformat())

# prepare message contents
message_content_html = ''
message_content_text = ''
some_project_has_new_release = False

# request each feeds
for url in github_release_rss_urls:
    logging.debug('  Processing '+url)
    # clear message content of the project
    message_content_project_html = ''
    message_content_project_text = ''
    project_has_new_release = False
    feed = feedparser.parse( url )
    # handle new feeds
    for entry in feed.entries:
        logging.debug('    Processing entry')
        entry_datetime = datetime.strptime(entry.updated, '%Y-%m-%dT%H:%M:%SZ')
        if previous_timestamp < entry_datetime:
            # this is a new entry
            logging.debug('    Entry is new')
            project_has_new_release = True
            some_project_has_new_release = True
            message_content_project_html = message_content_project_html + '<br />' + entry.author + ' released ' + entry.title + ' (' + entry.updated + ')'
            message_content_project_text = message_content_project_text + '\r\n  - ' + entry.author + ' released ' + entry.title + ' (' + entry.updated + ')'
            if entry.content:
                # there is a content description
                message_content_project_html = message_content_project_html + '<br />' + entry.content[0].value
    # test if there was new item
    if project_has_new_release:
        logging.info('    New release available for '+url)
        message_content_html = message_content_html + '<br/><h3><a href="' + feed.feed.link + '">"' + feed.feed.title + '</a></h3>' + message_content_project_html
        message_content_text = message_content_text + '\r\n\r\n' + feed.feed.title + message_content_project_text

# there is some new versions, send mail
if some_project_has_new_release:
    logging.debug('  Prepare message')
    # prepare message
    message_content_html = 'There is a new releases for followed project on Github:' + message_content_html
    message_content_text = 'There is a new releases for followed project on Github:' + message_content_text
    # prepare mail
    msg = MIMEMultipart('alternative')
    msg['From'] = mail_from
    msg['To'] = mail_to
    msg['Subject'] = 'New releases on followed Github projects'
    part1 = MIMEText(message_content_text, 'plain')
    part2 = MIMEText(message_content_html, 'html')
    msg.attach(part1)
    msg.attach(part2)
    # send mail
    try:
        p = Popen(['/usr/sbin/sendmail', '-t', '-oi'], stdin=PIPE)
        p.communicate(msg.as_string())
    except OSError as e:
        logging.error('    Error sending mail')

# set current timestamp
logging.debug('  Store timestamp')
timestamp_file = open(timestamp_filename,'w')
timestamp_file.write(timestamp.isoformat())
timestamp_file.close()
logging.info('End of github-notifier')

C'est tout pour aujourd'hui. Si vous avez d'autres idées pour être notifié de la release d'un projet hébergé sur Github ou que vous voulez améliorer le script, laissez un commentaire :)