Merge pull request #885 from lnussel/announcer

Refactor announcer
This commit is contained in:
Ludwig Nussel 2017-05-09 09:26:26 +02:00 committed by GitHub
commit 86e5ea9fad

View File

@ -1,6 +1,6 @@
#!/usr/bin/python #!/usr/bin/python
# Copyright (c) 2014 SUSE Linux Products GmbH # Copyright (c) 2014 SUSE Linux Products GmbH
# Copyright (c) 2015, 2016 SUSE Linux GmbH # Copyright (c) 2015, 2016, 2017 SUSE Linux GmbH
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
@ -28,46 +28,87 @@ from email.mime.text import MIMEText
import os import os
import sys import sys
import email.utils import email.utils
from optparse import OptionParser import argparse
import logging
import yaml
from xdg.BaseDirectory import save_data_path from xdg.BaseDirectory import save_data_path
from collections import namedtuple
parser = OptionParser() logger = logging.getLogger()
parser.add_option("--dry", action="store_true", help="dry run")
parser.add_option("--debug", action="store_true", help="debug output")
parser.add_option("--verbose", action="store_true", help="verbose")
parser.add_option("--from", dest='sender', metavar="EMAIL", help="sender email address")
parser.add_option("--to", metavar="EMAIL", help="recepient email address")
parser.add_option("--relay", metavar="RELAY", help="SMTP relay server address")
parser.add_option("--version", metavar="VERSION", help="announce specific version")
(options, args) = parser.parse_args() # map of default config entries
config_defaults = {
if not options.sender or not options.to or not options.relay: 'sender': None,
print >>sys.stderr, "need to specify --from and --to and --relay" 'to': None,
sys.exit(1) 'relay': None,
'url' : "http://download.opensuse.org/tumbleweed/iso/",
url = "http://download.opensuse.org/tumbleweed/iso/" 'iso' : "openSUSE-Tumbleweed-DVD-x86_64-Current.iso",
iso = "openSUSE-Tumbleweed-DVD-x86_64-Current.iso" 'name' : 'factory-announcer',
changes = "Changes.%s.txt" 'subject' :'New Tumbleweed snapshot {version} released!',
DATADIR = save_data_path('opensuse.org', 'factory-announcer') 'changesfile' : "Changes.{version}.txt",
intro = """ 'bodytemplate' : """
Please note that this mail was generated by a script. Please note that this mail was generated by a script.
The described changes are computed based on the x86_64 DVD. The described changes are computed based on the x86_64 DVD.
The full online repo contains too many changes to be listed here. The full online repo contains too many changes to be listed here.
Please check the known defects of this snapshot before upgrading: Please check the known defects of this snapshot before upgrading:
https://openqa.opensuse.org/tests/overview?distri=opensuse&groupid=1&version=Tumbleweed&build={} https://openqa.opensuse.org/tests/overview?distri=opensuse&groupid=1&version=Tumbleweed&build={version}
When you reply to report some issues, make sure to change the subject. When you reply to report some issues, make sure to change the subject.
It is not helpful to keep the release announcement subject in a thread It is not helpful to keep the release announcement subject in a thread
while discussing a specific problem. while discussing a specific problem.
""" {text}
""",
current_fn = os.path.join(DATADIR, "announcer-current-version") }
def _load_config(handle = None):
d = config_defaults
y = yaml.safe_load(handle) if handle is not None else {}
return namedtuple('Config', sorted(d.keys()))(*[ y.get(p, d[p]) for p in sorted(d.keys()) ])
parser = argparse.ArgumentParser(description="Announce new snapshots")
parser.add_argument("--dry", action="store_true", help="dry run")
parser.add_argument("--debug", action="store_true", help="debug output")
parser.add_argument("--verbose", action="store_true", help="verbose")
parser.add_argument("--from", dest='sender', metavar="EMAIL", help="sender email address")
parser.add_argument("--to", metavar="EMAIL", help="recepient email address")
parser.add_argument("--relay", metavar="RELAY", help="SMTP relay server address")
parser.add_argument("--version", metavar="VERSION", help="announce specific version")
parser.add_argument("--config", metavar="FILE", type=argparse.FileType(), help="YAML config file to override defaults")
parser.add_argument("--dump-config", action="store_true", help="dump built in YAML config")
options = parser.parse_args()
# Set logging configuration
logging.basicConfig(level=logging.DEBUG if options.debug
else logging.INFO,
format='%(asctime)s - %(module)s:%(lineno)d - %(levelname)s - %(message)s')
if options.dump_config:
print yaml.dump(config_defaults, default_flow_style=False)
sys.exit(0)
config = _load_config(options.config)
if not options.sender:
options.sender = config.sender
if not options.to:
options.to = config.to
if not options.relay:
options.relay = config.relay
if not options.sender or not options.to or not options.relay:
logger.error("need to specify --from and --to and --relay")
sys.exit(1)
datadir = save_data_path('opensuse.org', config.name)
current_fn = os.path.join(datadir, "announcer-current-version")
if not options.version: if not options.version:
u = urlparse(urljoin(url, iso)) u = urlparse(urljoin(config.url, config.iso))
conn = httplib.HTTPConnection(u.hostname, 80) conn = httplib.HTTPConnection(u.hostname, 80)
conn.request('HEAD', u.path) conn.request('HEAD', u.path)
res = conn.getresponse() res = conn.getresponse()
@ -78,24 +119,22 @@ if not options.version:
if loc is None: if loc is None:
raise Exception("empty location!") raise Exception("empty location!")
m = re.search('Snapshot(\d+)-Media', loc) m = re.search('(?:Snapshot|Build)(\d+)-Media', loc)
if m is None: if m is None:
raise Exception("invalid location") raise Exception("failed to parse %s"%loc)
version = m.group(1) version = m.group(1)
if options.verbose: logger.debug("found version %s", version)
print "found version", version
else: else:
version = options.version version = options.version
if os.path.lexists(current_fn): if os.path.lexists(current_fn):
prev = os.readlink(current_fn) prev = os.readlink(current_fn)
if prev == version: if prev == version:
if options.verbose: logger.debug("version unchanged, exit")
print "version unchanged, exit"
sys.exit(0) sys.exit(0)
u = urlparse(urljoin(url, changes % version)) u = urlparse(urljoin(config.url, config.changesfile.format(version=version)))
conn = httplib.HTTPConnection(u.hostname, 80) conn = httplib.HTTPConnection(u.hostname, 80)
conn.request('HEAD', u.path) conn.request('HEAD', u.path)
res = conn.getresponse() res = conn.getresponse()
@ -114,12 +153,11 @@ if res.status != 200:
txt = res.read() txt = res.read()
if "====" not in txt: if "====" not in txt:
if options.verbose: logger.error("no changes or file corrupt? not sending anything")
print >>sys.stderr, "no changes or file corrupt? not sending anything"
sys.exit(1) sys.exit(1)
msg = MIMEText(intro.format(version) + txt) msg = MIMEText(config.bodytemplate.format(version=version, text=txt))
msg['Subject'] = 'New Tumbleweed snapshot %s released!' % version msg['Subject'] = config.subject.format(version=version)
msg['From'] = options.sender msg['From'] = options.sender
msg['To'] = options.to msg['To'] = options.to
msg['Mail-Followup-To'] = options.to msg['Mail-Followup-To'] = options.to
@ -130,11 +168,12 @@ if options.dry:
print "sending ..." print "sending ..."
print msg.as_string() print msg.as_string()
else: else:
logger.info("announcing version {}".format(version))
s = smtplib.SMTP(options.relay) s = smtplib.SMTP(options.relay)
s.sendmail(options.sender, [msg['To']], msg.as_string()) s.sendmail(options.sender, [msg['To']], msg.as_string())
s.quit() s.quit()
tmpfn = os.path.join(DATADIR, ".announcer-current-version") tmpfn = os.path.join(datadir, ".announcer-current-version")
os.symlink(version, tmpfn) os.symlink(version, tmpfn)
os.rename(tmpfn, current_fn) os.rename(tmpfn, current_fn)