| #!/usr/bin/env python3 |
| # -*- indent-tabs-mode: nil -*- vi: set expandtab: |
| |
| import sys, os, argparse, time, re, posix, atexit, binascii |
| from pathlib import Path |
| |
| yamlOk = True |
| try: |
| import yaml |
| except: |
| yamlOk = False |
| jsonOk = True |
| try: |
| import simplejson as json |
| except: |
| try: |
| import json |
| except: |
| jsonOk = False |
| |
| def date(): |
| """ return a datestamp """ |
| return time.strftime("%Y-%m-%d %H:%M:%S") |
| |
| def infoMsg(msg): |
| """ generate an informational message to stdout """ |
| print("%s:INFO:%s" % (date(), msg)) |
| |
| def traceMsg(msg): |
| """ if verbose flag is on, generate an informational message to stdout """ |
| global args |
| if args.verbose: |
| infoMsg(msg) |
| |
| def warnMsg(msg): |
| """ generate a warning message to stdout """ |
| print("%s:WARNING:%s" % (date(), msg)) |
| |
| def die(msg): |
| """ generate a FATAL message to stdout and exit """ |
| print("%s:FATAL:%s" % (date(), msg)) |
| sys.exit(2) |
| |
| def displayCwd(): |
| """ display the working directory """ |
| infoMsg("working directory '" + os.getcwd() + "'") |
| |
| def cdCheck(dir): |
| """ cd to a new directory and die if we cannot """ |
| try: |
| traceMsg("cd %s" % dir) |
| os.chdir(dir) |
| except: |
| die("Cannot chdir(" + dir + ")") |
| |
| def removeDirPath(path, prmsg = True, gone_ok = False): |
| """ |
| remove a directory path |
| prmsg - print a message before proceeding |
| gone_ok - do not warn if a path does not exist |
| """ |
| if prmsg: |
| infoMsg("Removing path '%s'" % path) |
| nmlist = None |
| try: |
| nmlist = os.listdir(path) |
| except FileNotFoundError: |
| if not gone_ok: |
| warnMsg("path no longer exists: %s" % path) |
| return |
| except: |
| e = sys.exc_info()[0] |
| warnMsg("removing path (%s) gave this error: %s" % (path, e)) |
| return |
| |
| for nm in nmlist: |
| if nm != "." and nm != "..": |
| pathnm = path + "/" + nm |
| if os.path.isdir(pathnm): |
| removeDirPath(pathnm, prmsg = False) |
| else: |
| # infoMsg(">>>>removing file %s" % pathnm) |
| try: |
| os.remove(pathnm) |
| except: |
| e = sys.exc_info()[0] |
| warnMsg("Could not remove file (%s) because of %s" % (pathnm, e)) |
| |
| # infoMsg(">>>>removing directory %s" % pathnm) |
| try: |
| os.rmdir(path) |
| except FileNotFoundError: |
| if not gone_ok: |
| warnMsg("Could not remove directory (%s) because of FileNotFound" % path) |
| except: |
| e = sys.exc_info()[0] |
| warnMsg("Could not remove directory (%s) because of %s" % (path, e)) |
| |
| def verboseOsSystem(cmd): |
| """ execute a shell command, printing a trace message first """ |
| traceMsg("About to execute '%s'" % cmd) |
| os.system(cmd) |
| |
| def lndir(fr, to): |
| """ create a copy of a tree structure, using hard links where possible """ |
| global args |
| removeDirPath(to + "/" + fr, prmsg = args.verbose, gone_ok = True) |
| verboseOsSystem("find '%s' -print0 | cpio -pdml0 '%s'" % ( fr, to )) |
| |
| y = None |
| |
| def getParam(name, dflt = None): |
| """ |
| Retrieve the contents of a parameter file, rooted where specified. |
| Return None when it does not exist. |
| """ |
| global y, args |
| if y is None: |
| fname = args.directory + "/" + args.repackageyaml |
| if args.repackageyaml.endswith(".yaml"): |
| if not yamlOk: |
| die("YAML not available on this machine") |
| else: |
| with open(fname, "r") as fd: |
| try: |
| contents = fd.read() |
| contents = re.sub("^\t+", " ", contents, flags=re.M) |
| y = yaml.safe_load(contents) |
| except: |
| die("Invalid yaml in '%s'" % fname) |
| elif args.repackageyaml.endswith(".json"): |
| if not jsonOk: |
| die("JSON not available on this machine") |
| else: |
| with open(fname, "r") as fd: |
| try: |
| contents = fd.read() |
| y = json.loads(contents) |
| except: |
| type, value, traceback = sys.exc_info() |
| die("Invalid json in '%s': %s" % (fname, value)) |
| else: |
| die("%s must end either in .yaml or .json" % repackageyaml) |
| |
| e = "y" + name |
| inp = None |
| try: |
| inp = eval(e,{"__builtins__":None},{"y":y}) |
| except KeyError: |
| if dflt is not None: |
| return dflt |
| if inp is None: |
| die("The %s must be be set in %s" % (name, args.repackageyaml)) |
| return inp |
| |
| def cleanupTmpRoot(): |
| """ clean out the tmp directory """ |
| global TMPROOT |
| removeDirPath(TMPROOT, prmsg = args.verbose, gone_ok = True) |
| |
| def genDebianChangelog(fname): |
| """ generate a Debian change log, hard-coded to this for now """ |
| with open(fname, "w") as fd: |
| fd.write("OpenECOMP 1701 Demo\n") |
| |
| def uploadDocker(name,tag): |
| """ tag & push Docker image to nexus docker registry """ |
| ns = getParam( '["docker"]["namespace"]' ) |
| registry = getParam( '["docker"]["registry"]' ) |
| image = name + ":" + tag |
| repo = os.environ.get("DOCKERREGISTRY") + "/" + ns + "/" + image |
| repo = os.environ.get("DOCKERREGISTRY") + "/" + ns + "/" + image |
| verboseOsSystem("docker tag " + image + " " + repo) |
| verboseOsSystem("docker push " + repo) |
| i = 2 |
| while os.environ.get("DOCKERREGISTRY" + str(i)): |
| repo = os.environ.get("DOCKERREGISTRY" + str(i)) + "/" + ns + "/" + image |
| verboseOsSystem("docker tag " + image + " " + repo) |
| verboseOsSystem("docker push " + repo) |
| i += 1 |
| |
| # The Debian control archive contents can include the following files: |
| # |
| # control: A list of dependencies, and other useful information to indentify the package, such as |
| # a brief description of the package. |
| # md5sums: contains MD5 checksums of all files in the package in order to detect corrupt or incomplete files. |
| # preinst, postinst, prerm and postrm are optional scripts that are executed before or after installing, |
| # updating or removing the package. |
| # copyright: any needed copyright notice |
| # changelog: |
| # conffiles: Lists the files of the package that should be treated as configuration files. |
| # Configuration files are not overwritten during an update unless specified. |
| # debian-binary: contains the deb-package version, currently 2.0 |
| # templates: A file with error descriptions and dialogs during installation |
| # config: is an optional script that supports the debconf configuration mechanism. |
| # shlibs: list of shared library dependencies. |
| |
| def genDebianControl(fname): |
| """ generate a Debian control file """ |
| with open(fname, "w") as fd: |
| global APPL, VER, BNBR, MAINTAINER |
| fd.write("Package: %s\n" % APPL) |
| fd.write("Version: %s-%s\n" % (VER, BNBR)) |
| fd.write("Section: utils\n") |
| fd.write("Priority: optional\n") |
| fd.write("Architecture: all\n") |
| fd.write("Maintainer: %s\n" % MAINTAINER) |
| deps = getParam('["debian"]["externalDependencies"]') |
| depends = "" |
| sep = " " |
| if deps: |
| for dep in deps: |
| for d, v in dep.items(): |
| depends += sep + d + " (" + v + ")" |
| sep = ", " |
| fd.write("Depends:%s\n" % depends) |
| fd.write("Conflicts:\n") |
| fd.write("Replaces:\n") |
| desc = getParam( '["description"]' ) |
| desc = re.sub("^[ \t]*$", ".", desc, flags=re.M) |
| desc = re.sub("^[ \t]*", " ", desc, flags=re.M) |
| fd.write("Description:%s\n" % desc) |
| |
| def genDebianMd5sums(fname): |
| """ generate an MD5 listing of all of the staged files """ |
| global ROOTDIR |
| verboseOsSystem("cd '%s/stage' && find * -type f -exec md5sum -b {} + > %s" % (ROOTDIR, fname)) |
| |
| def genCopyright(fname, prefix = ""): |
| """ generate a copyright statement, with the given prefix on each line """ |
| with open(fname, "w") as fd: |
| fd.write(prefix + "Copyright (C) 2016 AT&T Intellectual Property. All rights reserved.\n") |
| fd.write(prefix + "\n") |
| fd.write(prefix + "This code is licensed under the Apache License, Version 2.0;\n") |
| fd.write(prefix + "you may not use this code for any purpose except in compliance\n") |
| fd.write(prefix + "with the Apache License. You may obtain a copy of the License\n") |
| fd.write(prefix + "at http://www.att.com/openecomp.html.\n") |
| |
| def isExe(fname): |
| """ check if a path exists and is executable """ |
| return os.path.exists(fname) and os.access(fname, os.X_OK) |
| |
| def isFileExe(fname): |
| """ check if a path exists as a file and is executable """ |
| return os.path.isfile(fname) and os.access(fname, os.X_OK) |
| |
| def genFileList(path, testFn): |
| """ generate a list of files, rooted at path, that all pass the given test """ |
| ret = [] |
| try: |
| nmlist = os.listdir(path) |
| except FileNotFoundError: |
| return ret |
| except: |
| e = sys.exc_info()[0] |
| warnMsg("error while listing path (%s): %s" % (path, e)) |
| return ret |
| |
| for nm in nmlist: |
| if nm != "." and nm != "..": |
| pathnm = path + "/" + nm |
| if os.path.isdir(pathnm): |
| more = genFileList(pathnm, testFn) |
| ret.extend(more) |
| elif testFn(pathnm): |
| ret.append(pathnm) |
| return ret |
| |
| |
| def createDockerTempFiles(L): |
| """ create the temp file structure needed to create a docker image """ |
| global args, ROOTDIR |
| removeDirPath(L, prmsg = args.verbose, gone_ok = True) |
| os.makedirs(L, exist_ok = True) |
| |
| cdCheck(ROOTDIR + "/stage") |
| copyList = [] |
| for i in os.listdir(): |
| if not i.startswith("."): |
| lndir(i, L) |
| copyList.append(i) |
| |
| posix.link(ROOTDIR + "/Dockerfile", L + "/Dockerfile") |
| |
| def genApplVerBnbrSuffix(suffix, whichBuildNumber): |
| """ Generate a number of constants used in building a package """ |
| global APPL, VER, BNBR, TIMESTAMP |
| applVer = APPL + "_" + VER |
| buildNumber = BNBR if whichBuildNumber == '{buildnumber}' else TIMESTAMP if whichBuildNumber == '{datetime}' else whichBuildNumber |
| if buildNumber.startswith("{") and buildNumber.endswith("}"): |
| die("Unrecognized buildnumber macro name found: %s" % buildNumber) |
| applVerBnbr = applVer + "-" + buildNumber |
| applVerBnbrSuffix = applVerBnbr + "." + suffix |
| applVerSuffix = applVer + "." + suffix |
| outdirApplVerBnbrSuffix = args.outputdirectory + "/" + applVerBnbrSuffix |
| return applVer, applVerBnbr, applVerBnbrSuffix, applVerSuffix, outdirApplVerBnbrSuffix |
| |
| def genApplVerBnbrSuffix2(suffix, buildString): |
| """ Generate a number of constants used in building a package """ |
| global APPL, VER, BNBR, TIMESTAMP |
| buildString = buildString.replace('{buildnumber}',BNBR).replace('{datetime}',TIMESTAMP).replace('{appname}',APPL).replace('{suffix}',suffix).replace('{version}',VER) |
| if buildString.find("{") != -1 and buildString.find("}") != -1: |
| die("Unrecognized buildstring macro name found: %s" % buildNumber) |
| return buildString |
| |
| def uploadAll(envName, groupId, outdirApplVerBnbrSuffix, suffix, applVer, applVerSuffix): |
| """ |
| Execute the various upload commands for a given package. |
| Take into account args.multipleuploadversions |
| """ |
| if args.allExtendedUploadVersions: |
| print("================ in extended -X upload ================") |
| for buildString in args.allExtendedUploadVersions: |
| print(">>>> buildString=%s" % buildString) |
| applVerBnbrSuffix = genApplVerBnbrSuffix2(suffix, buildString) |
| print(">>>> applVerBnbrSuffix=%s" % applVerBnbrSuffix) |
| verboseOsSystem(os.environ.get(envName).format(outdirApplVerBnbrSuffix, applVerBnbrSuffix, groupId, applVerSuffix, applVer)) |
| i = 2 |
| while os.environ.get(envName + str(i)): |
| verboseOsSystem(os.environ.get(envName + str(i)).format(outdirApplVerBnbrSuffix, applVerBnbrSuffix, groupId, applVerSuffix, applVer)) |
| i += 1 |
| else: |
| print("================ in regular -M upload ================") |
| for buildNumber in args.allUploadVersions: |
| ignored1, ignored2, applVerBnbrSuffix, ignored3, ignored4 = genApplVerBnbrSuffix(suffix, buildNumber) |
| verboseOsSystem(os.environ.get(envName).format(outdirApplVerBnbrSuffix, applVerBnbrSuffix, groupId, applVerSuffix, applVer)) |
| i = 2 |
| while os.environ.get(envName + str(i)): |
| verboseOsSystem(os.environ.get(envName + str(i)).format(outdirApplVerBnbrSuffix, applVerBnbrSuffix, groupId, applVerSuffix, applVer)) |
| i += 1 |
| |
| def buildDebian(): |
| """ Build a local debian formatted package """ |
| infoMsg( 'Building a Debian package ...' ) |
| global args, TMPROOT, ROOTDIR |
| if args.skipexecution: |
| return |
| |
| suffix = "deb" |
| applVer, applVerBnbr, applVerBnbrSuffix, applVerSuffix, outdirApplVerBnbrSuffix = genApplVerBnbrSuffix(suffix, '{buildnumber}') |
| |
| if args.usecache and os.path.exists(outdirApplVerBnbrSuffix): |
| infoMsg( "Already built %s" % applVerBnbrSuffix) |
| |
| else: |
| L = TMPROOT + "/debian" |
| LD = TMPROOT + "/debian/DEBIAN" |
| removeDirPath(L, prmsg = args.verbose, gone_ok = True) |
| os.makedirs(LD, exist_ok = True) |
| |
| cdCheck(ROOTDIR + "/stage") |
| for i in os.listdir(): |
| if not i.startswith("."): |
| lndir(i, L) |
| |
| genCopyright(LD + "/copyright") |
| genDebianControl(LD + "/control") |
| genDebianChangelog(LD + "/changelog") |
| genDebianMd5sums(LD + "/md5sums") |
| |
| cdCheck(ROOTDIR) |
| execUser = getParam('["executionUser"]') |
| fileUser = getParam('["fileUser"]') |
| fileGroup = getParam('["fileGroup"]') |
| isRoot = execUser == "root" |
| for cname in [ "preinst", "postinst", "prerm", "postrm" ]: |
| comCname = "common/" + cname |
| ldName = LD + "/" + cname |
| if isExe(comCname) or cname == "postinst": |
| traceMsg("linking %s to %s" % (comCname, ldName)) |
| if isRoot and isExe(comCname): |
| posix.link(comCname, ldName) |
| else: |
| with open(ldName, "w") as out: |
| if cname == "postinst" and fileUser != "root": |
| for nm in os.listdir("stage"): |
| t = getParam( '["directoryTreeTops"]["/' + nm + '"]', "n/a" ) |
| if t == "n/a": |
| t = "/" + nm |
| print("chown -R '%s:%s' '%s'" % (fileUser, fileGroup, t), file=out) |
| print("find '%s' -type d -exec chmod 755 {} +" % t, file=out) |
| print("find '%s' ! -type d -exec chmod 644 {} +" % t, file=out) |
| # list each executable file separately |
| for fname in genFileList("stage", isFileExe): |
| fname = fname[6:] # remove 'stage/' from beginning |
| print("chmod 755 '/%s'" % fname, file=out) |
| |
| if isExe(comCname): |
| with open(comCname, "r") as inp: |
| print("gawk '{\n" + |
| " f = $0\n" + |
| " for (i = 1; i <= length(f); i+=2) {\n" + |
| " printf(\"%c\", strtonum(\"0X\" substr(f,i,2)))\n" + |
| " }\n" + |
| "}' > /tmp/rep.$$ <<EOF", file=out) |
| for line in inp: |
| for c in line: |
| # print(">>%02x<<" % ord(c)) |
| print("%02x" % ord(c), file=out, end="") |
| print("", file=out) |
| print("EOF\n" + |
| "chmod a+x /tmp/rep.$$\n" + |
| "su " + execUser + " -c /tmp/rep.$$\n" + |
| "rm -f /tmp/rep.$$\n", file=out) |
| verboseOsSystem("chmod a+x " + ldName) |
| |
| elif os.path.exists(comCname): |
| die(comCname + " must be executable") |
| |
| cdCheck(TMPROOT) |
| |
| if args.skipbuild: |
| traceMsg('Skipping final build') |
| return |
| |
| verboseOsSystem(". '%s'; fakeroot -- dpkg-deb --verbose --build '%s'" % (args.environfile, L)) |
| os.makedirs(args.outputdirectory, exist_ok = True) |
| os.rename("debian.deb", outdirApplVerBnbrSuffix) |
| |
| if not os.path.exists(outdirApplVerBnbrSuffix): |
| infoMsg( "Unsuccesful in building %s" % applVerBnbrSuffix) |
| return |
| |
| infoMsg( "Successfully built %s" % applVerBnbrSuffix) |
| |
| if args.upload: |
| envName = "REPACKAGEDEBIANUPLOAD" |
| groupId = getParam('["debian"]["groupId"]', getParam('["groupId"]')) |
| uploadAll(envName, groupId, outdirApplVerBnbrSuffix, suffix, applVer, applVerSuffix) |
| |
| def buildTar(useGzip): |
| """ Build a local tarball formatted package """ |
| infoMsg( 'Building a tar package ...' ) |
| global args, TMPROOT, ROOTDIR |
| if args.skipexecution: |
| return |
| |
| suffix = "tgz" if useGzip else "tar" |
| applVer, applVerBnbr, applVerBnbrSuffix, applVerSuffix, outdirApplVerBnbrSuffix = genApplVerBnbrSuffix(suffix, '{buildnumber}') |
| |
| if args.usecache and os.path.isfile(outdirApplVerBnbrSuffix): |
| infoMsg( "Already built %s" % applVerBnbrSuffix) |
| |
| else: |
| L = TMPROOT + "/" + suffix |
| LD = L + "/" + applVerBnbr |
| removeDirPath(L, prmsg = args.verbose, gone_ok = True) |
| os.makedirs(LD, exist_ok = True) |
| |
| cdCheck(ROOTDIR + "/stage") |
| for i in os.listdir(): |
| if not i.startswith("."): |
| lndir(i, LD) |
| |
| cdCheck(L) |
| |
| if args.skipbuild: |
| traceMsg('Skipping final build') |
| return |
| |
| taropts = "-zc" if useGzip else "-c" |
| if args.verbose: taropts += "v" |
| taropts += "f" |
| verboseOsSystem(". '%s'; fakeroot -- tar %s tar.%s %s" % (args.environfile, taropts, suffix, applVerBnbr)) |
| os.makedirs(args.outputdirectory, exist_ok = True) |
| if args.verbose: |
| print("renaming tar.%s to %s" % (suffix, outdirApplVerBnbrSuffix)) |
| os.rename("tar.%s" % suffix, outdirApplVerBnbrSuffix) |
| |
| if not os.path.exists(outdirApplVerBnbrSuffix): |
| infoMsg( "Unsuccesful in building %s" % applVerBnbrSuffix) |
| return |
| |
| infoMsg( "Successfully built %s" % applVerBnbrSuffix) |
| |
| |
| if args.upload: |
| envName = "REPACKAGETGZUPLOAD" if useGzip else "REPACKAGETARUPLOAD" |
| groupId = getParam('["%s"]["groupId"]' % suffix, getParam('["groupId"]')) |
| uploadAll(envName, groupId, outdirApplVerBnbrSuffix, suffix, applVer, applVerSuffix) |
| |
| def buildDocker(): |
| """ Build a DOCKER image """ |
| image = getParam( '["docker"]["image"]', "n/a" ) |
| if image == "n/a": |
| global APPL |
| image = APPL |
| tag = getParam( '["docker"]["tag"]' ) |
| |
| infoMsg( 'Building a (local) docker image ...' ) |
| global args, TMPROOT |
| if args.skipexecution: |
| return |
| |
| L = TMPROOT + "/docker" |
| createDockerTempFiles(L) |
| |
| if args.skipbuild: |
| traceMsg('Skipping final build') |
| return |
| |
| cdCheck(L) |
| verboseOsSystem(". '%s'; docker build -t '%s:%s' ." % (args.environfile, image, tag)) |
| |
| if args.upload: |
| uploadDocker(image,tag) |
| |
| |
| def strToBool(string): |
| return True if (type(string) is str and string == "true") else False if (type(string) is str and string == "false") else string |
| |
| def main(): |
| """ the main executable function """ |
| |
| # |
| # deal with the program arguments - |
| # we build two different types of argument lists based on |
| # context. jenkins requires positional arguments while linux cmd line |
| # permits parameterized ones. the jenkins positional argument list is |
| # smaller |
| # |
| parser = argparse.ArgumentParser( |
| description="Build the specified packages. 'package-type' is one or more of " + |
| "docker, debian, tar, tgz" + |
| " (comma-separated), or 'all' to build all of them." |
| ) |
| |
| REPACKAGEYAML = "repackage.yaml" |
| REPACKAGEJSON = "repackage.json" |
| if os.environ.get("JENKINS"): |
| parser.add_argument("packagetype",help= "debian" + |
| "|docker|tar|tgz" + |
| "|all") |
| parser.add_argument("upload",help="upload package to appropriate repository",nargs='?',default="false") |
| parser.add_argument("directory", type=str, help="where to find the stage directory and %s. Defaults to '.'" % REPACKAGEYAML, default=".",nargs='?') |
| parser.add_argument("environfile", type=str, help="Optional environment file. Overrides $REPACKAGEENVFILE, defaults to /dev/null", default="/dev/null", nargs='?') |
| parser.add_argument("outputdirectory", type=str, help="Output directory. Defaults to 'output' under --directory path.", default=None, nargs='?') |
| parser.add_argument("verbose",help="turn on verbosity",nargs='?',default="true") |
| parser.add_argument("skipexecution",help="indcate packages and exit ",nargs='?',default="false") |
| parser.add_argument("skipbuild",help="skip actually bulding the packages",nargs='?',default="false") |
| parser.add_argument("usecache",help="if debian/tar/tgz artifact already exists use it",nargs='?',default="false") |
| parser.add_argument("keeptempfiles",help="keep temp files at exit",nargs='?',default="false") |
| else: |
| parser.add_argument("-n", "--skipexecution", help="indicate the packages and exit", action="store_true") |
| parser.add_argument("-c", "--usecache", help="if a debian/tar/tgz artifact already exists use it", action="store_true") |
| parser.add_argument("-N", "--skipbuild", help="skip actually building the packages", action="store_true") |
| parser.add_argument("-K", "--keeptempfiles", help="keep temp files at exit", action="store_true") |
| parser.add_argument("-v", "--verbose", help="turn on verbosity", action="store_true") |
| parser.add_argument("-b", "--packagetype", type=str, help="""The package-type may be specified multiple times or may use a ','-separated |
| or space-separated list. 'all' is an alias for all of them. Potential values are debian, docker""" + |
| ", tar or tgz", required=True) |
| parser.add_argument("-u", "--upload", action="store_true", help="""Depending on package type -- docker, debian, tar or tgz -- uploads the artifact to a remote repository. |
| For Docker, uses $DOCKERREGISTRY as the remote repository to push the image. |
| |
| For Debian, uses $REPACKAGEDEBIANUPLOAD as the command, with {0} as the local path to the debian image, {1} as the image name with build number, |
| and optionally {2} as groupId (may be used as part of the directory path), {3} as the image name without the build number, and {4} |
| as the image name with no build number and no .deb suffix. |
| For additional uploads, this will also look for $REPACKAGEDEBIANUPLOAD2, $REPACKAGEDEBIANUPLOAD3, etc., and repeat the upload. |
| |
| For tar, uses $REPACKAGETARUPLOAD as the command. Everything said about $REPACKAGEDEBIANUPLOAD applies to $REPACKAGETARUPLOAD. |
| For tgz, uses $REPACKAGETGZUPLOAD as the command. Everything said about $REPACKAGEDEBIANUPLOAD applies to $REPACKAGETGZUPLOAD. |
| |
| In addition, if --multipleuploadversions is used, the above will be executed using the list of upload version numbers specified there. |
| |
| This is typically used to create multiple versions (using --multipleuploadversions) on multiple remote repositories (using $REPACKAGE*UPLOAD). |
| """) |
| # For additional uploads, repackage will also look for $DOCKERREGISTRY2, $DOCKERREGISTRY3, etc. |
| parser.add_argument("-d", "--directory", type=str, help="where to find the stage directory and %s. Defaults to '.'" % REPACKAGEYAML, default=".") |
| parser.add_argument("-e", "--environfile", type=str, help="Optional environment file. Overrides $REPACKAGEENVFILE, defaults to /dev/null", default="/dev/null") |
| parser.add_argument("-o", "--outputdirectory", type=str, help="Output directory. Defaults to 'output' under --directory path.", default=None) |
| parser.add_argument("-y", "--repackageyaml", type=str, help="Name of parameter file. Defaults to '" + REPACKAGEYAML + "' or '" + REPACKAGEJSON + "' under --directory path.", default=REPACKAGEYAML) |
| parser.add_argument("-B", "--buildnumber", type=str, help="Build number. Defaults to $BUILD_NUMBER, which defaults to a date-based string.", default="") |
| parser.add_argument("-D", "--define", type=str, action='append', help="define an argument at runtime in key=value format") |
| parser.add_argument("-M", "--multipleuploadversions", type=str, help="Use multiple versions for upload. Comma-separated list of {datetime}, {buildnumber} or arbitrary strings. Defaults to {buildnumber}, which is the value from --buildnumber.", default="{buildnumber}") |
| parser.add_argument("-X", "--extendedmultipleuploadversions", type=str, help="""Use multiple names for upload. |
| Comma-separated list of arbitrary strings |
| that can contain any combination of {appname}, {suffix}, {datetime}, {buildnumber} and {version}. |
| There is no default, but if used, this overrides -M.""", default="") |
| |
| global args |
| args = parser.parse_args() |
| |
| # for some reason, the Jenkins branch leaves these as strings instead of the proper boolean values |
| args.upload = strToBool(args.upload) |
| args.verbose = strToBool(args.verbose) |
| args.skipexecution = strToBool(args.skipexecution) |
| args.skipbuild = strToBool(args.skipbuild) |
| args.usecache = strToBool(args.usecache) |
| args.keeptempfiles = strToBool(args.keeptempfiles) |
| |
| # arguments defined at runtime as key=value pairs |
| global rtdef |
| rtdef = {} |
| |
| if args.define: |
| for k in args.define: |
| tag, val = k.split("=") |
| rtdef[tag] = val |
| |
| for k, v in rtdef.items(): |
| traceMsg("runtime defined %s <- %s" % (k,v)) |
| |
| # check the -e/$REPACKAGEENVFILE value |
| if args.environfile == "": |
| if os.environ.get("REPACKAGEENVFILE") is not None: |
| args.environfile = os.environ["REPACKAGEENVFILE"] |
| if not os.path.isfile(args.environfile) and args.environfile != "/dev/null": |
| die("-e / $REPACKAGEENVFILE must be a file that can be sourced by the shell") |
| if not args.environfile.startswith("/"): |
| args.environfile = os.getcwd() + "/" + args.environfile |
| |
| allPackages = [ "debian", "tar", "tgz", |
| "docker" ] |
| args.builds = { } |
| for pkg in allPackages: |
| args.builds[pkg] = False |
| if args.packagetype == "all": |
| args.packagetype = ",".join(allPackages) |
| for build in re.split("[, \t]", args.packagetype): |
| args.builds[build] = True |
| |
| args.allUploadVersions = args.multipleuploadversions.split(",") |
| args.allExtendedUploadVersions = args.extendedmultipleuploadversions.split(",") if args.extendedmultipleuploadversions != "" else None |
| |
| if args.upload and args.builds["debian"]: |
| if os.environ.get("REPACKAGEDEBIANUPLOAD") is None: |
| die("-u requires $REPACKAGEDEBIANUPLOAD to be set when building debian") |
| elif not re.search("[{]0[}]", os.environ.get("REPACKAGEDEBIANUPLOAD")): |
| die("$REPACKAGEDEBIANUPLOAD is missing {0}") |
| elif not re.search("[{][13][}]", os.environ.get("REPACKAGEDEBIANUPLOAD")): |
| die("$REPACKAGEDEBIANUPLOAD is missing either {1}, {3} or {4}") |
| |
| if args.upload and args.builds["tar"]: |
| if os.environ.get("REPACKAGETARUPLOAD") is None: |
| die("-u requires $REPACKAGETARUPLOAD to be set when building tar") |
| elif not re.search("[{]0[}]", os.environ.get("REPACKAGETARUPLOAD")): |
| die("$REPACKAGETARUPLOAD is missing {0}") |
| elif not re.search("[{][134][}]", os.environ.get("REPACKAGETARUPLOAD")): |
| die("$REPACKAGETARUPLOAD is missing either {1}, {3} or {4}") |
| |
| if args.upload and args.builds["tgz"]: |
| if os.environ.get("REPACKAGETGZUPLOAD") is None: |
| die("-u requires $REPACKAGETGZUPLOAD to be set when building tgz") |
| elif not re.search("[{]0[}]", os.environ.get("REPACKAGETGZUPLOAD")): |
| die("$REPACKAGETGZUPLOAD is missing {0}") |
| elif not re.search("[{][134][}]", os.environ.get("REPACKAGETGZUPLOAD")): |
| die("$REPACKAGETGZUPLOAD is missing either {1}, {3} or {4}") |
| |
| if args.upload and args.builds["docker"] and os.environ.get("DOCKERREGISTRY") is None: |
| die("-u requires $DOCKERREGISTRY to be set when building docker") |
| |
| if not os.path.isdir(args.directory): |
| die("The root directory %s does not exist" % args.directory) |
| if not args.directory.startswith("/"): |
| args.directory = os.getcwd() + "/" + args.directory |
| if args.repackageyaml != REPACKAGEYAML: |
| if not os.path.exists(args.directory + "/" + args.repackageyaml): |
| die("The file %s/%s does not exist" % (args.directory, args.repackageyaml)) |
| else: |
| if os.path.exists(args.directory + "/" + REPACKAGEYAML): |
| args.repackageyaml = REPACKAGEYAML |
| elif os.path.exists(args.directory + "/" + REPACKAGEJSON): |
| args.repackageyaml = REPACKAGEJSON |
| else: |
| die("Either %s/%s or %s/%s must exist" % (args.directory, args.repackageyaml, args.directory, args.repackagejson)) |
| |
| if args.outputdirectory is None: |
| args.outputdirectory = args.directory + "/output" |
| else: |
| if not args.outputdirectory.startswith("/"): |
| args.outputdirectory = os.getcwd() + "/" + args.outputdirectory |
| if not os.path.isdir(args.outputdirectory): |
| die("The specified --outputdirectory %s does not exist" % args.outputdirectory) |
| |
| # establish some global variables used everywhere |
| global ROOTDIR, TMPROOT |
| ROOTDIR = args.directory |
| TMPROOT = args.directory + "/tmp" |
| |
| # and cd to our ROOTDIR |
| cdCheck(ROOTDIR) |
| |
| # unless -K is specified, remove any temp files at the end |
| if not args.keeptempfiles: |
| atexit.register(cleanupTmpRoot) |
| |
| # grab and share some variables that are used by lots of build functions |
| global APPL, MAINTAINER, VER, BNBR, TIMESTAMP |
| APPL = getParam( '["applicationName"]' ) |
| MAINTAINER = getParam( '["maintainer"]' ) |
| VER = getParam( '["version"]' ) |
| TIMESTAMP = time.strftime("%Y%m%d%H%M%S") |
| BNBR = args.buildnumber if args.buildnumber != "" else os.environ.get("BUILD_NUMBER") if os.environ.get("BUILD_NUMBER") is not None else TIMESTAMP |
| |
| # build whatever was requested |
| if args.builds["docker"]: |
| buildDocker() |
| if args.builds["debian"]: |
| buildDebian() |
| if args.builds["tar"]: |
| buildTar(False) |
| if args.builds["tgz"]: |
| buildTar(True) |
| |
| if __name__ == "__main__": |
| main() |