Skip to content

Instantly share code, notes, and snippets.

@sorz
Last active May 12, 2018 13:57
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sorz/98a61b670ae0e0ee83716bfbf60bd4b0 to your computer and use it in GitHub Desktop.
Save sorz/98a61b670ae0e0ee83716bfbf60bd4b0 to your computer and use it in GitHub Desktop.
Sending new release notification (via email) for GitHub repos.
#!/usr/bin/env python3
"""GitHub New Release Checker
./check_release.py path/to/releases.ini
It fetches release tag from GitHub, then comparing it with the
old one in ini file. If tag changed, send a email to configured
address.
"""
import re
import sys
import logging
from smtplib import SMTP
from email.message import EmailMessage
from configparser import ConfigParser
import requests
URL_LATEST_RELEASE = 'https://github.com/{repo}/releases/latest'
RE_REPO_PATH = f'^[^/]+/[^/]+$'
RE_RELEASE_TAG = r'.*/releases/tag/(.*)$'
session = requests.session()
def get_release_tag(repo: str) -> str:
resp = session.head(URL_LATEST_RELEASE.replace('{repo}', repo))
location = resp.headers.get('location', '')
match = re.match(RE_RELEASE_TAG, location)
if match:
return match.group(1)
def template_replace(template: str, kvs: dict) -> str:
for key, value in kvs.items():
template = template.replace(f'{{{key}}}', f'{value}')
return template
def main():
logging.basicConfig(
level=logging.INFO,
format='%(levelname)-s: %(message)s'
)
if len(sys.argv) != 2:
print(f'Usage: {sys.argv[0]} path/to/releases.ini', file=sys.stderr)
sys.exit(1)
cfgfile = sys.argv[1]
cfg = ConfigParser()
cfg.read(cfgfile)
changes = []
for repo in cfg.sections():
if not re.match(RE_REPO_PATH, repo):
logging.warning('[%s] is not a GitHub repo, ignored.')
continue
logging.debug('Checking for %s...', repo)
tag = get_release_tag(repo)
if tag is None:
logging.warning('Fail to get new release tag for %s', repo)
continue
old_tag = cfg[repo].get('tag')
if tag == old_tag:
logging.info('%s: %s no change', repo, tag)
continue
logging.info('%s: %s => %s', repo, old_tag, tag)
cfg[repo]['tag'] = tag
changes.append((repo, old_tag, tag))
if not changes:
logging.debug('No change, exit.')
return
logging.debug('Writing out new tags...')
with open(cfgfile, 'w') as f:
cfg.write(f)
if 'smtp' not in cfg['DEFAULT'] or 'to' not in cfg['DEFAULT']:
logging.debug('No email configured, exit.')
return
with SMTP(cfg['DEFAULT']['smtp']) as smtp:
logging.debug('Sending email notifications...')
for repo, old_tag, tag in changes:
kws = dict(repo=repo, old_tag=old_tag, tag=tag)
subject = template_replace(cfg['DEFAULT']['subject'], kws)
content = template_replace(cfg['DEFAULT']['content'], kws)
msg = EmailMessage()
msg.set_content(content)
msg['Subject'] = subject
msg['From'] = cfg['DEFAULT']['from']
msg['To'] = cfg['DEFAULT']['to']
logging.info('Sending "%s"...', subject)
smtp.send_message(msg)
if __name__ == '__main__':
main()
[Unit]
Description=Checking new release on GitHub repo
[Service]
Type=simple
User=EXAMPLE
Group=EXAMPLES
WorkingDirectory=/opt/check-release
ExecStart=/opt/check-release/check_release.py releases.ini
PrivateDevices=True
ProtectHome=True
NoNewPrivileges=True
ProtectSystem=strict
ReadWritePaths=/opt/check-release/releases.ini
[Unit]
Description=Checking new release every 3 hours
[Timer]
OnCalendar=00/3:00
RandomizedDelaySec=1h
AccuracySec=1h
[Install]
WantedBy=multi-user.target
[DEFAULT]
smtp = 127.0.0.1
from = Release Checker <releases@example.com>
to = you@example.com
subject = {repo} {tag} released
content = {repo}: {old_tag} => {tag}
https://github.com/{repo}/releases/tag/{tag}
; add repos below
[tootsuite/mastodon]
[cternes/openkeepass]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment