Skip to content
archversion 10.4 KiB
Newer Older
Seblu's avatar
Seblu committed
#!/usr/bin/python3
Seblu's avatar
Seblu committed
# coding: utf-8

# archversion - Archlinux Version Controller
# Copyright © 2012 Sébastien Luttringer
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

'''Archlinux Version Controller'''

Seblu's avatar
Seblu committed
from archversion import VERSION, CONFIG_SENDMAIL
Seblu's avatar
Seblu committed
from archversion.config import BaseConfigFile
Seblu's avatar
Seblu committed
from archversion.error import BaseError, MissingConfigFile, NoSuchFile
Seblu's avatar
Seblu committed
from archversion.error import ERR_FATAL, ERR_ABORT, ERR_UNKNOWN
Seblu's avatar
Seblu committed
from archversion.pacman import parse_pkgbuild, pkgbuild_set_version, pkgbuild_update_checksums
from archversion.version import VersionController
Seblu's avatar
Seblu committed
from email.mime.text import MIMEText
Seblu's avatar
Seblu committed
from email.utils import formatdate
Seblu's avatar
Seblu committed
from io import StringIO
from pprint import pprint
Seblu's avatar
Seblu committed
from smtplib import SMTP
Seblu's avatar
Seblu committed
import argparse
import logging
Seblu's avatar
Seblu committed
import os
Seblu's avatar
Seblu committed
import sys
Seblu's avatar
Seblu committed

def parse_argv():
    '''Parse command line arguments'''
    p_main = argparse.ArgumentParser()
    p_main.add_argument("--version", action="version",
Seblu's avatar
Seblu committed
                        version="%(prog)s version " + VERSION)
Seblu's avatar
Seblu committed
    p_main.add_argument("--debug", action="store_true",
                        help="debug mode")
    sp_main = p_main.add_subparsers()
Seblu's avatar
Seblu committed
    # config parser
Seblu's avatar
Seblu committed
    p_conf = sp_main.add_parser("config",
Seblu's avatar
Seblu committed
                                help="list configured packages")
    p_conf.add_argument("-s", "--sort", action="store_true",
                        help="sort listing")
    p_conf.add_argument("packages", nargs='*', help="only check these packages")
Seblu's avatar
Seblu committed
    p_conf.set_defaults(func=command_config)
Seblu's avatar
Seblu committed
    # sync parser
    p_sync = sp_main.add_parser("sync",
                                help="retrieve upstream and dowstream versions")
    p_sync.add_argument("-s", "--sort", action="store_true",
                        help="sort syncing")
    p_sync.add_argument("packages", nargs='*', help="only sync these packages")
    p_sync.set_defaults(func=command_sync)
Seblu's avatar
Seblu committed
    # modes parser
    p_modes = sp_main.add_parser("modes",
Seblu's avatar
Seblu committed
                                 help="list check against modes")
    p_modes.set_defaults(func=command_modes)
Seblu's avatar
Seblu committed
    # report parser
Seblu's avatar
Seblu committed
    p_report = sp_main.add_parser("report",
Seblu's avatar
Seblu committed
                                 help="report packages versions")
    p_report.add_argument("-f", "--fresh", action="store_true",
                         help="Only report fresh versions")
    p_report.add_argument("-n", "--new", action="store_true",
                         help="Only report new versions")
Seblu's avatar
Seblu committed
    p_report.add_argument("-s", "--sort", action="store_true",
Seblu's avatar
Seblu committed
                         help="sort packages by name")
Seblu's avatar
Seblu committed
    p_report.add_argument("-S", "--sync", action="store_true",
                         help="sync packages versions before report")
Seblu's avatar
Seblu committed
    p_report.add_argument("packages", nargs='*',
                         help="only report these packages")
    p_report.set_defaults(func=command_report)
Seblu's avatar
Seblu committed
    # check parser
    p_check = sp_main.add_parser("check",
                                 help="check packages versions")
    p_check.add_argument("-f", "--fresh", action="store_true",
                         help="Only report fresh versions")
    p_check.add_argument("-n", "--new", action="store_true",
                         help="Only report new versions")
    p_check.add_argument("-s", "--sort", action="store_true",
                         help="sort packages by name")
    p_check.add_argument("packages", nargs='*',
                         help="only check these packages")
    p_check.set_defaults(func=command_check)
Seblu's avatar
Seblu committed
    # sendmail parser
    p_sendmail = sp_main.add_parser("sendmail",
                                 help="sendmail packages versions by mail")
    p_sendmail.add_argument("-f", "--fresh", action="store_true",
                         help="Only sendmail fresh versions")
    p_sendmail.add_argument("-n", "--new", action="store_true",
                         help="Only sendmail new versions")
    p_sendmail.add_argument("-s", "--sort", action="store_true",
                         help="sort packages by name")
    p_sendmail.add_argument("-S", "--sync", action="store_true",
                         help="sync packages versions before sendmail")
    p_sendmail.add_argument("--to", help="mail destination address")
    p_sendmail.add_argument("--smtp", help="smtp server")
    p_sendmail.add_argument("packages", nargs='*',
                         help="only sendmail these packages")
    p_sendmail.set_defaults(func=command_sendmail)
Seblu's avatar
Seblu committed
    # update parser
Seblu's avatar
Seblu committed
    p_update = sp_main.add_parser("update",
                                  help="update a PKGBUILD with the latest version")
Seblu's avatar
Seblu committed
    p_update.add_argument("-p", "--path", default="PKGBUILD",
                          help="name of the file to update. Default PKGBUILD")
    p_update.add_argument("-v", "--vars", action="store_true",
                          help="print variables extracted from the PKGBUILD")
Seblu's avatar
Seblu committed
    p_update.add_argument("-c", "--checksum", action="store_true",
                          help="run updpkgsums after update")
Seblu's avatar
Seblu committed
    p_update.add_argument("-S", "--no-sync", action="store_true",
Seblu's avatar
Seblu committed
                           help="don't sync packages versions prior update")
Seblu's avatar
Seblu committed
    p_update.set_defaults(func=command_update)
    # do parse
    namespace = p_main.parse_args()
    # Ensure subparser was choosen
    if "func" not in namespace:
        p_main.error("missing argument")
    return namespace
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
def command_config(args, vctrl):
    '''list configured packages'''
    if len(args.packages) > 0:
Seblu's avatar
Seblu committed
        vctrl.select(args.packages)
Seblu's avatar
Seblu committed
    if args.sort:
Seblu's avatar
Seblu committed
        vctrl.sort()
Seblu's avatar
Seblu committed
    vctrl.print_names()

def command_modes(args, vctrl):
    '''list checking against modes'''
    vctrl.print_modes()

Seblu's avatar
Seblu committed
def command_sync(args, vctrl):
    '''Handle sync command call'''
    # reduce the package list
    if len(args.packages) > 0:
Seblu's avatar
Seblu committed
        vctrl.select(args.packages)
Seblu's avatar
Seblu committed
    # sort packages if asked
    if args.sort:
Seblu's avatar
Seblu committed
        vctrl.sort()
Seblu's avatar
Seblu committed
    # start syncing
Seblu's avatar
Seblu committed
    vctrl.sync()
Seblu's avatar
Seblu committed
def command_check(args, vctrl):
    '''Handle check command call'''
    # reduce the package list
    args.sync=True
    command_report(args, vctrl)

Seblu's avatar
Seblu committed
def command_report(args, vctrl):
    '''Handle report command call'''
    # reduce the package list
Seblu's avatar
Seblu committed
    if len(args.packages) > 0:
Seblu's avatar
Seblu committed
        vctrl.select(args.packages)
Seblu's avatar
Seblu committed
    # sort packages if asked
    if args.sort:
Seblu's avatar
Seblu committed
        vctrl.sort()
    # sync if asked
    if args.sync:
Seblu's avatar
Seblu committed
        vctrl.sync()
    # start report
    vctrl.print_versions(args.new, args.fresh)
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
def command_sendmail(args, vctrl):
    '''Handle sendmail command call'''
Seblu's avatar
Seblu committed
    # load sendmail config
    config = BaseConfigFile(CONFIG_SENDMAIL)
Seblu's avatar
Seblu committed
    # check args
    try:
Seblu's avatar
Seblu committed
        to = config["mail"]["to"]
Seblu's avatar
Seblu committed
        from_ = config["mail"].get("from", "Archversion <noreply@archlinux.org>")
        subject = config["mail"].get("subject", "Archversion Report")
Seblu's avatar
Seblu committed
        smtp = config["smtp"]["host"]
    except KeyError as exp:
        logging.error("Invalid sendmail config: %s" % exp)
Seblu's avatar
Seblu committed
        exit(1)
    # capture a report
    stdout = StringIO()
    stdout_bak = sys.stdout
    sys.stdout = stdout
    command_report(args, vctrl)
    sys.stdout = stdout_bak
    # no data, no mail!
    if len(stdout.getvalue()) == 0:
        return
    # format the mail
    msg = MIMEText(stdout.getvalue())
Seblu's avatar
Seblu committed
    msg["Subject"] = subject
Seblu's avatar
Seblu committed
    msg["From"] = from_
Seblu's avatar
Seblu committed
    msg["To"] = to
    msg["Date"] = formatdate(localtime=True)
Seblu's avatar
Seblu committed
    # send the mail
    try:
        s = SMTP(smtp)
        s.send_message(msg)
        s.quit()
    except Exception as exp:
        logging.error("Sendmail fail: %s" % exp)
        exit(1)

Seblu's avatar
Seblu committed
def command_update(args, vctrl):
    '''Handle update command call'''
    if not os.path.exists(args.path):
        raise NoSuchFile(args.path)
    if os.getresuid()[1] == 0:
        logging.warn("Warning: You should not run this as root")
    pkgdict = parse_pkgbuild(args.path)
    if args.vars:
        pprint(pkgdict)
    pkgbase = pkgdict.get("pkgbase", None)
    pkgname0 = pkgdict.get("pkgname0", None)
Seblu's avatar
Seblu committed
    pkgver = pkgdict.get("pkgver", None)
    # some sanity checks
    if pkgname0 is None and pkgbase is None:
        raise BaseError("Unable to detect pkgname/pkgbase in %s" % args.path)
    if pkgver is None:
Seblu's avatar
Seblu committed
        raise BaseError("Unable to detect pkgver in %s" % args.path)
    if pkgname0 in vctrl.packages:
        pkgname = pkgname0
    elif pkgbase in vctrl.packages:
        pkgname = pkgbase
    else:
        raise BaseError("No registered package %s" % (pkgbase or pkgname0))
    # redure packge list to the extracted one
Seblu's avatar
Seblu committed
    vctrl.select((pkgname,))
Seblu's avatar
Seblu committed
    # sync if not refused
    if not args.no_sync:
Seblu's avatar
Seblu committed
        vctrl.sync()
Seblu's avatar
Seblu committed
    # get upstream version
Seblu's avatar
Seblu committed
    upver = vctrl.versions[pkgname][0]
Seblu's avatar
Seblu committed
    if upver is None:
        raise BaseError("Unable to detect upstream version of %s" % pkgname)
    # print what we detect
    print("Package name: %s" % pkgname)
    print("PKGBUILD version: %s" % pkgver)
Seblu's avatar
Seblu committed
    print("Upstream version: %s" % upver)
Seblu's avatar
Seblu committed
    # compare version
    if pkgver == upver:
        print("Version are the same. Do nothing!")
Seblu's avatar
Seblu committed
        return
    # update version with upstream
    pkgbuild_set_version(args.path, upver)
    # update checksum
    if args.checksum:
        pkgbuild_update_checksums(args.path)

Seblu's avatar
Seblu committed
def main():
    '''Program entry point'''
    try:
        # parse command line
        args = parse_argv()
        # set global debug mode
        if args.debug:
            logging.getLogger().setLevel(logging.DEBUG)
Seblu's avatar
Seblu committed
        # load controller
        vctrl = VersionController()
Seblu's avatar
Seblu committed
        # call command function
        return args.func(args, vctrl)
    except KeyboardInterrupt:
        exit(ERR_ABORT)
    except BaseError as exp:
        logging.error(exp)
Seblu's avatar
Seblu committed
        exit(ERR_FATAL)
    except Exception as exp:
        logging.error("Unknown error. Please report it with --debug.")
        logging.error(exp)
        if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
            raise
Seblu's avatar
Seblu committed
        exit(ERR_UNKNOWN)

if __name__ == '__main__':
    main()

# vim:set ts=4 sw=4 et ai: