[28303] in Source-Commits

home help back first fref pref prev next nref lref last post

build-system commit: Add dareprepro and friends

daemon@ATHENA.MIT.EDU (Jonathan D Reed)
Sat Jul 5 14:54:52 2014

Date: Sat, 5 Jul 2014 14:54:44 -0400
From: Jonathan D Reed <jdreed@MIT.EDU>
Message-Id: <201407051854.s65Isi4R018447@drugstore.mit.edu>
To: source-commits@MIT.EDU

https://github.com/mit-athena/build-system/commit/e67af634e06dc06ff4e7cf0c6890ca217ad76d6b
commit e67af634e06dc06ff4e7cf0c6890ca217ad76d6b
Author: Jonathan Reed <jdreed@mit.edu>
Date:   Tue Jun 17 16:29:03 2014 -0400

    Add dareprepro and friends
    
    - Add dareprepro, a wrapper around reprepro.call() that runs
      various common reprepro operations.
    - Add dacopy, damove, and daremove as symlinks to dareprepro

 dacopy     |    1 +
 damove     |    1 +
 daremove   |    1 +
 dareprepro |  199 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 202 insertions(+), 0 deletions(-)

diff --git a/dacopy b/dacopy
new file mode 120000
index 0000000..81b8994
--- /dev/null
+++ b/dacopy
@@ -0,0 +1 @@
+dareprepro
\ No newline at end of file
diff --git a/damove b/damove
new file mode 120000
index 0000000..81b8994
--- /dev/null
+++ b/damove
@@ -0,0 +1 @@
+dareprepro
\ No newline at end of file
diff --git a/daremove b/daremove
new file mode 120000
index 0000000..81b8994
--- /dev/null
+++ b/daremove
@@ -0,0 +1 @@
+dareprepro
\ No newline at end of file
diff --git a/dareprepro b/dareprepro
new file mode 100755
index 0000000..53235f5
--- /dev/null
+++ b/dareprepro
@@ -0,0 +1,199 @@
+#!/usr/bin/python
+
+"""
+If invoked as "dareprepro", it is a simple wrapper around reprepro.
+If invoked as "damove", "dacopy", or "daremove", it performs those
+operations, enforcing our repository workflow.
+"""
+
+import dabuildsys
+from dabuildsys import config, reprepro
+
+import argparse
+import os
+import re
+import subprocess
+import sys
+
+# Maybe this should move into config?
+suffix_operations = { 'production': (),
+                      'proposed': ('production'),
+                      'development': ('proposed'),
+                      'bleeding': (),
+                      'staging': ()
+                      }
+
+# Remove deprecated syntax in Jun 2015, along with
+# fix_deprecated() below
+deprecated_suffix=re.compile('^-([A-Za-z]{2,})$')
+
+def notify(msg):
+    """Notify something that the operation succeeded.
+
+    In this case, notify using zwrite"""
+    cmdline = ['zwrite', '-d', '-c', 'debathena', '-i', 'apt', '-m']
+    try:
+        subprocess.check_call(cmdline + [msg])
+    except subprocess.CalledProcessError as e:
+        print >>sys.stderr, "Unable to notify: ", e.message
+
+def distro(pocket, release):
+    """Given a pocket (e.g. "proposed") and a release,
+    concatenate them in a useful manner"""
+    assert pocket in suffix_operations
+    # Yes, I know we just deprecated '', but I want the command line to
+    # be clear about what is going on, and not rely on how we have our
+    # apt repo laid out at the moment.
+    if pocket == 'production':
+        return release
+    else:
+        return "{0}-{1}".format(release, pocket)
+
+def validate_suffixes(*suffixes):
+    """Validate suffixes/pockets, failing if it encounters an
+    unknown one."""
+    for s in suffixes:
+        if s not in suffix_operations:
+            sys.exit("Unknown suffix: {0}".format(s))
+    return suffixes
+
+# Remove in Jun 2015
+def fix_deprecated(arg):
+    """Support for the old build system syntax."""
+    if arg == '':
+        print >>sys.stderr, "W: Deprecated syntax: use 'production', not ''"
+        return 'production'
+    is_suffix = deprecated_suffix.match(arg)
+    if is_suffix is not None and is_suffix.group(1) in suffix_operations:
+        print >>sys.stderr, \
+            "W: Deprecated syntax: omit leading '-' from pockets"
+        return is_suffix.group(1)
+    return arg
+
+def yesreally():
+    """Confirm that the user really does want to shoot themselves
+    in the foot."""
+    try:
+        if raw_input("This is a terrible idea.  "
+                     "Continue anyway? [y/N] ").lower() == 'y':
+            return True
+        sys.exit("You have chosen wisely.")
+    except (EOFError, KeyboardInterrupt):
+        sys.exit("\nYou could have just typed 'N'...")
+    return False
+
+
+def dacopy(moving=False):
+    """Copy (or move, if moving=True) source packages from
+    one pocket to another, enforcing workflow."""
+    verb, participle = ("move" if moving else "copy",
+                        "Moving" if moving else "Copying")
+    parser = argparse.ArgumentParser(
+        description="{0} packages from one pocket to another".format(
+            verb.capitalize()))
+    parser.add_argument("--bad-idea", action="store_true",
+                        dest="override", help=argparse.SUPPRESS)
+    parser.add_argument("dst", metavar="TO_POCKET",
+                        help="Destination pocket (e.g. 'production'")
+    parser.add_argument("src", metavar="FROM_POCKET",
+                        help="Souce pocket (e.g. 'proposed'")
+    parser.add_argument("packages", nargs='+', metavar='SRCPKG',
+                        help="Source package name(s)")
+    args = parser.parse_args([fix_deprecated(x) for x in sys.argv[1:]])
+    dst, src = validate_suffixes(args.dst, args.src)
+    if dst not in suffix_operations[src]:
+        print >>sys.stderr, \
+            "WARNING: {participle} a package from '{src}' to '{dst}'\n" \
+            "         is not an approved workflow.".format(
+            src=src, dst=dst, participle=participle)
+        if src in suffix_operations[dst]:
+            print >>sys.stderr, \
+                "         (Ensure the source and destination are not reversed.)"
+        if not (args.override and yesreally()):
+            sys.exit(1)
+    for package in args.packages:
+        success, fail = [], []
+        for r in config.releases:
+            if run_reprepro('copysrc', distro(dst, r),
+                            distro(src, r), package):
+                if (not moving) or \
+                        (moving and
+                         run_reprepro('removesrc', distro(src, r), package)):
+                    success.append(r)
+                    continue
+            fail.append(r)
+        if len(fail) > 0:
+            print >>sys.stderr, "FAILED to {verb} {pkg} for: {suites}".format(
+                verb=verb, pkg=package, suites=fail)
+            if package != args.packages[-1]:
+                if raw_input('Keep going? [y/N] ').lower() != 'y':
+                    sys.exit(1)
+        elif moving:
+            notify("Moved {0} from {1} to {2} for\n{3}".format(package,
+                                                               src, dst,
+                                                               success))
+
+def damove():
+    dacopy(moving=True)
+
+def daremove():
+    """Remove a source package from a pocket"""
+    parser = argparse.ArgumentParser(
+        description="Remove packages from a pocket")
+    parser.add_argument("--bad-idea", action="store_true",
+                        dest="override", help=argparse.SUPPRESS)
+    parser.add_argument("src", metavar="FROM", help="Old suffix")
+    parser.add_argument("packages", nargs='+', metavar='SRCPKG',
+                        help="Source package name(s)")
+    args = parser.parse_args([fix_deprecated(x) for x in sys.argv[1:]])
+    src = validate_suffixes(args.src)[0]
+    if src == 'production':
+        print >>sys.stderr, "You should not delete packages from production."
+        if not (args.override and yesreally()):
+            sys.exit("Aborting...")
+    for package in args.packages:
+        success, fail = [], []
+        for r in config.releases:
+            if run_reprepro('removesrc', distro(src, r), package):
+                success.append(r)
+            else:
+                fail.append(r)
+        if len(fail) > 0:
+            print >>sys.stderr, "FAILED to move {0} for: {1}".format(package,
+                                                                     fail)
+            if package != args.packages[-1]:
+                if raw_input('Keep going? [y/N] ').lower() != 'y':
+                    sys.exit(1)
+
+def run_reprepro(*args):
+    """Call reprepro, displaying output, and returning True
+    if it succeeded, else False"""
+    try:
+        print reprepro.call(*args)
+        return True
+    except subprocess.CalledProcessError as e:
+        print >>sys.stderr, e.output
+        return False
+
+
+def main():
+    """A state in New England"""
+    dispatch = { 'dacopy': dacopy,
+                 'damove': damove,
+                 'daremove': daremove }
+    invoked_as = os.path.basename(sys.argv[0])
+    if invoked_as in dispatch:
+        dispatch[invoked_as]()
+    else:
+        if not run_reprepro(*sys.argv[1:]):
+            sys.exit(1)
+    sys.exit(0)
+
+if __name__ == '__main__':
+    if not dabuildsys.claim_lock():
+        print >>sys.stderr, "The lock is in place; unable to proceed"
+        sys.exit(1)
+    try:
+        main()
+    finally:
+        dabuildsys.release_lock()

home help back first fref pref prev next nref lref last post