blob: 1271311c09f8b12141454231d6b967b7ad89b242 [file] [log] [blame]
Tony Hansenbb384022017-09-19 18:49:04 +00001#!/usr/bin/env python3
2# -*- indent-tabs-mode: nil -*- vi: set expandtab:
3
4import sys, os, argparse, time, re, posix, atexit, binascii
5from pathlib import Path
6
7yamlOk = True
8try:
9 import yaml
10except:
11 yamlOk = False
12jsonOk = True
13try:
14 import simplejson as json
15except:
16 try:
17 import json
18 except:
19 jsonOk = False
20
21def date():
22 """ return a datestamp """
23 return time.strftime("%Y-%m-%d %H:%M:%S")
24
25def infoMsg(msg):
26 """ generate an informational message to stdout """
27 print("%s:INFO:%s" % (date(), msg))
28
29def traceMsg(msg):
30 """ if verbose flag is on, generate an informational message to stdout """
31 global args
32 if args.verbose:
33 infoMsg(msg)
34
35def warnMsg(msg):
36 """ generate a warning message to stdout """
37 print("%s:WARNING:%s" % (date(), msg))
38
39def die(msg):
40 """ generate a FATAL message to stdout and exit """
41 print("%s:FATAL:%s" % (date(), msg))
42 sys.exit(2)
43
44def displayCwd():
45 """ display the working directory """
46 infoMsg("working directory '" + os.getcwd() + "'")
47
48def cdCheck(dir):
49 """ cd to a new directory and die if we cannot """
50 try:
51 traceMsg("cd %s" % dir)
52 os.chdir(dir)
53 except:
54 die("Cannot chdir(" + dir + ")")
55
56def removeDirPath(path, prmsg = True, gone_ok = False):
57 """
58 remove a directory path
59 prmsg - print a message before proceeding
60 gone_ok - do not warn if a path does not exist
61 """
62 if prmsg:
63 infoMsg("Removing path '%s'" % path)
64 nmlist = None
65 try:
66 nmlist = os.listdir(path)
67 except FileNotFoundError:
68 if not gone_ok:
69 warnMsg("path no longer exists: %s" % path)
70 return
71 except:
72 e = sys.exc_info()[0]
73 warnMsg("removing path (%s) gave this error: %s" % (path, e))
74 return
75
76 for nm in nmlist:
77 if nm != "." and nm != "..":
78 pathnm = path + "/" + nm
79 if os.path.isdir(pathnm):
80 removeDirPath(pathnm, prmsg = False)
81 else:
82 # infoMsg(">>>>removing file %s" % pathnm)
83 try:
84 os.remove(pathnm)
85 except:
86 e = sys.exc_info()[0]
87 warnMsg("Could not remove file (%s) because of %s" % (pathnm, e))
88
89 # infoMsg(">>>>removing directory %s" % pathnm)
90 try:
91 os.rmdir(path)
92 except FileNotFoundError:
93 if not gone_ok:
94 warnMsg("Could not remove directory (%s) because of FileNotFound" % path)
95 except:
96 e = sys.exc_info()[0]
97 warnMsg("Could not remove directory (%s) because of %s" % (path, e))
98
99def verboseOsSystem(cmd):
100 """ execute a shell command, printing a trace message first """
101 traceMsg("About to execute '%s'" % cmd)
102 os.system(cmd)
103
104def lndir(fr, to):
105 """ create a copy of a tree structure, using hard links where possible """
106 global args
107 removeDirPath(to + "/" + fr, prmsg = args.verbose, gone_ok = True)
108 verboseOsSystem("find '%s' -print0 | cpio -pdml0 '%s'" % ( fr, to ))
109
110y = None
111
112def getParam(name, dflt = None):
113 """
114 Retrieve the contents of a parameter file, rooted where specified.
115 Return None when it does not exist.
116 """
117 global y, args
118 if y is None:
119 fname = args.directory + "/" + args.repackageyaml
120 if args.repackageyaml.endswith(".yaml"):
121 if not yamlOk:
122 die("YAML not available on this machine")
123 else:
124 with open(fname, "r") as fd:
125 try:
126 contents = fd.read()
127 contents = re.sub("^\t+", " ", contents, flags=re.M)
128 y = yaml.safe_load(contents)
129 except:
130 die("Invalid yaml in '%s'" % fname)
131 elif args.repackageyaml.endswith(".json"):
132 if not jsonOk:
133 die("JSON not available on this machine")
134 else:
135 with open(fname, "r") as fd:
136 try:
137 contents = fd.read()
138 y = json.loads(contents)
139 except:
140 type, value, traceback = sys.exc_info()
141 die("Invalid json in '%s': %s" % (fname, value))
142 else:
143 die("%s must end either in .yaml or .json" % repackageyaml)
144
145 e = "y" + name
146 inp = None
147 try:
148 inp = eval(e,{"__builtins__":None},{"y":y})
149 except KeyError:
150 if dflt is not None:
151 return dflt
152 if inp is None:
153 die("The %s must be be set in %s" % (name, args.repackageyaml))
154 return inp
155
156def cleanupTmpRoot():
157 """ clean out the tmp directory """
158 global TMPROOT
159 removeDirPath(TMPROOT, prmsg = args.verbose, gone_ok = True)
160
161def genDebianChangelog(fname):
162 """ generate a Debian change log, hard-coded to this for now """
163 with open(fname, "w") as fd:
164 fd.write("OpenECOMP 1701 Demo\n")
165
166def uploadDocker(name,tag):
167 """ tag & push Docker image to nexus docker registry """
168 ns = getParam( '["docker"]["namespace"]' )
169 registry = getParam( '["docker"]["registry"]' )
170 image = name + ":" + tag
171 repo = os.environ.get("DOCKERREGISTRY") + "/" + ns + "/" + image
172 repo = os.environ.get("DOCKERREGISTRY") + "/" + ns + "/" + image
173 verboseOsSystem("docker tag " + image + " " + repo)
174 verboseOsSystem("docker push " + repo)
175 i = 2
176 while os.environ.get("DOCKERREGISTRY" + str(i)):
177 repo = os.environ.get("DOCKERREGISTRY" + str(i)) + "/" + ns + "/" + image
178 verboseOsSystem("docker tag " + image + " " + repo)
179 verboseOsSystem("docker push " + repo)
180 i += 1
181
182# The Debian control archive contents can include the following files:
183#
184# control: A list of dependencies, and other useful information to indentify the package, such as
185# a brief description of the package.
186# md5sums: contains MD5 checksums of all files in the package in order to detect corrupt or incomplete files.
187# preinst, postinst, prerm and postrm are optional scripts that are executed before or after installing,
188# updating or removing the package.
189# copyright: any needed copyright notice
190# changelog:
191# conffiles: Lists the files of the package that should be treated as configuration files.
192# Configuration files are not overwritten during an update unless specified.
193# debian-binary: contains the deb-package version, currently 2.0
194# templates: A file with error descriptions and dialogs during installation
195# config: is an optional script that supports the debconf configuration mechanism.
196# shlibs: list of shared library dependencies.
197
198def genDebianControl(fname):
199 """ generate a Debian control file """
200 with open(fname, "w") as fd:
201 global APPL, VER, BNBR, MAINTAINER
202 fd.write("Package: %s\n" % APPL)
203 fd.write("Version: %s-%s\n" % (VER, BNBR))
204 fd.write("Section: utils\n")
205 fd.write("Priority: optional\n")
206 fd.write("Architecture: all\n")
207 fd.write("Maintainer: %s\n" % MAINTAINER)
208 deps = getParam('["debian"]["externalDependencies"]')
209 depends = ""
210 sep = " "
211 if deps:
212 for dep in deps:
213 for d, v in dep.items():
214 depends += sep + d + " (" + v + ")"
215 sep = ", "
216 fd.write("Depends:%s\n" % depends)
217 fd.write("Conflicts:\n")
218 fd.write("Replaces:\n")
219 desc = getParam( '["description"]' )
220 desc = re.sub("^[ \t]*$", ".", desc, flags=re.M)
221 desc = re.sub("^[ \t]*", " ", desc, flags=re.M)
222 fd.write("Description:%s\n" % desc)
223
224def genDebianMd5sums(fname):
225 """ generate an MD5 listing of all of the staged files """
226 global ROOTDIR
227 verboseOsSystem("cd '%s/stage' && find * -type f -exec md5sum -b {} + > %s" % (ROOTDIR, fname))
228
229def genCopyright(fname, prefix = ""):
230 """ generate a copyright statement, with the given prefix on each line """
231 with open(fname, "w") as fd:
232 fd.write(prefix + "Copyright (C) 2016 AT&T Intellectual Property. All rights reserved.\n")
233 fd.write(prefix + "\n")
234 fd.write(prefix + "This code is licensed under the Apache License, Version 2.0;\n")
235 fd.write(prefix + "you may not use this code for any purpose except in compliance\n")
236 fd.write(prefix + "with the Apache License. You may obtain a copy of the License\n")
237 fd.write(prefix + "at http://www.att.com/openecomp.html.\n")
238
239def isExe(fname):
240 """ check if a path exists and is executable """
241 return os.path.exists(fname) and os.access(fname, os.X_OK)
242
243def isFileExe(fname):
244 """ check if a path exists as a file and is executable """
245 return os.path.isfile(fname) and os.access(fname, os.X_OK)
246
247def genFileList(path, testFn):
248 """ generate a list of files, rooted at path, that all pass the given test """
249 ret = []
250 try:
251 nmlist = os.listdir(path)
252 except FileNotFoundError:
253 return ret
254 except:
255 e = sys.exc_info()[0]
256 warnMsg("error while listing path (%s): %s" % (path, e))
257 return ret
258
259 for nm in nmlist:
260 if nm != "." and nm != "..":
261 pathnm = path + "/" + nm
262 if os.path.isdir(pathnm):
263 more = genFileList(pathnm, testFn)
264 ret.extend(more)
265 elif testFn(pathnm):
266 ret.append(pathnm)
267 return ret
268
269
270def createDockerTempFiles(L):
271 """ create the temp file structure needed to create a docker image """
272 global args, ROOTDIR
273 removeDirPath(L, prmsg = args.verbose, gone_ok = True)
274 os.makedirs(L, exist_ok = True)
275
276 cdCheck(ROOTDIR + "/stage")
277 copyList = []
278 for i in os.listdir():
279 if not i.startswith("."):
280 lndir(i, L)
281 copyList.append(i)
282
283 posix.link(ROOTDIR + "/Dockerfile", L + "/Dockerfile")
284
285def genApplVerBnbrSuffix(suffix, whichBuildNumber):
286 """ Generate a number of constants used in building a package """
287 global APPL, VER, BNBR, TIMESTAMP
288 applVer = APPL + "_" + VER
289 buildNumber = BNBR if whichBuildNumber == '{buildnumber}' else TIMESTAMP if whichBuildNumber == '{datetime}' else whichBuildNumber
290 if buildNumber.startswith("{") and buildNumber.endswith("}"):
291 die("Unrecognized buildnumber macro name found: %s" % buildNumber)
292 applVerBnbr = applVer + "-" + buildNumber
293 applVerBnbrSuffix = applVerBnbr + "." + suffix
294 applVerSuffix = applVer + "." + suffix
295 outdirApplVerBnbrSuffix = args.outputdirectory + "/" + applVerBnbrSuffix
296 return applVer, applVerBnbr, applVerBnbrSuffix, applVerSuffix, outdirApplVerBnbrSuffix
297
298def genApplVerBnbrSuffix2(suffix, buildString):
299 """ Generate a number of constants used in building a package """
300 global APPL, VER, BNBR, TIMESTAMP
301 buildString = buildString.replace('{buildnumber}',BNBR).replace('{datetime}',TIMESTAMP).replace('{appname}',APPL).replace('{suffix}',suffix).replace('{version}',VER)
302 if buildString.find("{") != -1 and buildString.find("}") != -1:
303 die("Unrecognized buildstring macro name found: %s" % buildNumber)
304 return buildString
305
306def uploadAll(envName, groupId, outdirApplVerBnbrSuffix, suffix, applVer, applVerSuffix):
307 """
308 Execute the various upload commands for a given package.
309 Take into account args.multipleuploadversions
310 """
311 if args.allExtendedUploadVersions:
312 print("================ in extended -X upload ================")
313 for buildString in args.allExtendedUploadVersions:
314 print(">>>> buildString=%s" % buildString)
315 applVerBnbrSuffix = genApplVerBnbrSuffix2(suffix, buildString)
316 print(">>>> applVerBnbrSuffix=%s" % applVerBnbrSuffix)
317 verboseOsSystem(os.environ.get(envName).format(outdirApplVerBnbrSuffix, applVerBnbrSuffix, groupId, applVerSuffix, applVer))
318 i = 2
319 while os.environ.get(envName + str(i)):
320 verboseOsSystem(os.environ.get(envName + str(i)).format(outdirApplVerBnbrSuffix, applVerBnbrSuffix, groupId, applVerSuffix, applVer))
321 i += 1
322 else:
323 print("================ in regular -M upload ================")
324 for buildNumber in args.allUploadVersions:
325 ignored1, ignored2, applVerBnbrSuffix, ignored3, ignored4 = genApplVerBnbrSuffix(suffix, buildNumber)
326 verboseOsSystem(os.environ.get(envName).format(outdirApplVerBnbrSuffix, applVerBnbrSuffix, groupId, applVerSuffix, applVer))
327 i = 2
328 while os.environ.get(envName + str(i)):
329 verboseOsSystem(os.environ.get(envName + str(i)).format(outdirApplVerBnbrSuffix, applVerBnbrSuffix, groupId, applVerSuffix, applVer))
330 i += 1
331
332def buildDebian():
333 """ Build a local debian formatted package """
334 infoMsg( 'Building a Debian package ...' )
335 global args, TMPROOT, ROOTDIR
336 if args.skipexecution:
337 return
338
339 suffix = "deb"
340 applVer, applVerBnbr, applVerBnbrSuffix, applVerSuffix, outdirApplVerBnbrSuffix = genApplVerBnbrSuffix(suffix, '{buildnumber}')
341
342 if args.usecache and os.path.exists(outdirApplVerBnbrSuffix):
343 infoMsg( "Already built %s" % applVerBnbrSuffix)
344
345 else:
346 L = TMPROOT + "/debian"
347 LD = TMPROOT + "/debian/DEBIAN"
348 removeDirPath(L, prmsg = args.verbose, gone_ok = True)
349 os.makedirs(LD, exist_ok = True)
350
351 cdCheck(ROOTDIR + "/stage")
352 for i in os.listdir():
353 if not i.startswith("."):
354 lndir(i, L)
355
356 genCopyright(LD + "/copyright")
357 genDebianControl(LD + "/control")
358 genDebianChangelog(LD + "/changelog")
359 genDebianMd5sums(LD + "/md5sums")
360
361 cdCheck(ROOTDIR)
362 execUser = getParam('["executionUser"]')
363 fileUser = getParam('["fileUser"]')
364 fileGroup = getParam('["fileGroup"]')
365 isRoot = execUser == "root"
366 for cname in [ "preinst", "postinst", "prerm", "postrm" ]:
367 comCname = "common/" + cname
368 ldName = LD + "/" + cname
369 if isExe(comCname) or cname == "postinst":
370 traceMsg("linking %s to %s" % (comCname, ldName))
371 if isRoot and isExe(comCname):
372 posix.link(comCname, ldName)
373 else:
374 with open(ldName, "w") as out:
375 if cname == "postinst" and fileUser != "root":
376 for nm in os.listdir("stage"):
377 t = getParam( '["directoryTreeTops"]["/' + nm + '"]', "n/a" )
378 if t == "n/a":
379 t = "/" + nm
380 print("chown -R '%s:%s' '%s'" % (fileUser, fileGroup, t), file=out)
381 print("find '%s' -type d -exec chmod 755 {} +" % t, file=out)
382 print("find '%s' ! -type d -exec chmod 644 {} +" % t, file=out)
383 # list each executable file separately
384 for fname in genFileList("stage", isFileExe):
385 fname = fname[6:] # remove 'stage/' from beginning
386 print("chmod 755 '/%s'" % fname, file=out)
387
388 if isExe(comCname):
389 with open(comCname, "r") as inp:
390 print("gawk '{\n" +
391 " f = $0\n" +
392 " for (i = 1; i <= length(f); i+=2) {\n" +
393 " printf(\"%c\", strtonum(\"0X\" substr(f,i,2)))\n" +
394 " }\n" +
395 "}' > /tmp/rep.$$ <<EOF", file=out)
396 for line in inp:
397 for c in line:
398 # print(">>%02x<<" % ord(c))
399 print("%02x" % ord(c), file=out, end="")
400 print("", file=out)
401 print("EOF\n" +
402 "chmod a+x /tmp/rep.$$\n" +
403 "su " + execUser + " -c /tmp/rep.$$\n" +
404 "rm -f /tmp/rep.$$\n", file=out)
405 verboseOsSystem("chmod a+x " + ldName)
406
407 elif os.path.exists(comCname):
408 die(comCname + " must be executable")
409
410 cdCheck(TMPROOT)
411
412 if args.skipbuild:
413 traceMsg('Skipping final build')
414 return
415
416 verboseOsSystem(". '%s'; fakeroot -- dpkg-deb --verbose --build '%s'" % (args.environfile, L))
417 os.makedirs(args.outputdirectory, exist_ok = True)
418 os.rename("debian.deb", outdirApplVerBnbrSuffix)
419
420 if not os.path.exists(outdirApplVerBnbrSuffix):
421 infoMsg( "Unsuccesful in building %s" % applVerBnbrSuffix)
422 return
423
424 infoMsg( "Successfully built %s" % applVerBnbrSuffix)
425
426 if args.upload:
427 envName = "REPACKAGEDEBIANUPLOAD"
428 groupId = getParam('["debian"]["groupId"]', getParam('["groupId"]'))
429 uploadAll(envName, groupId, outdirApplVerBnbrSuffix, suffix, applVer, applVerSuffix)
430
431def buildTar(useGzip):
432 """ Build a local tarball formatted package """
433 infoMsg( 'Building a tar package ...' )
434 global args, TMPROOT, ROOTDIR
435 if args.skipexecution:
436 return
437
438 suffix = "tgz" if useGzip else "tar"
439 applVer, applVerBnbr, applVerBnbrSuffix, applVerSuffix, outdirApplVerBnbrSuffix = genApplVerBnbrSuffix(suffix, '{buildnumber}')
440
441 if args.usecache and os.path.isfile(outdirApplVerBnbrSuffix):
442 infoMsg( "Already built %s" % applVerBnbrSuffix)
443
444 else:
445 L = TMPROOT + "/" + suffix
446 LD = L + "/" + applVerBnbr
447 removeDirPath(L, prmsg = args.verbose, gone_ok = True)
448 os.makedirs(LD, exist_ok = True)
449
450 cdCheck(ROOTDIR + "/stage")
451 for i in os.listdir():
452 if not i.startswith("."):
453 lndir(i, LD)
454
455 cdCheck(L)
456
457 if args.skipbuild:
458 traceMsg('Skipping final build')
459 return
460
461 taropts = "-zc" if useGzip else "-c"
462 if args.verbose: taropts += "v"
463 taropts += "f"
464 verboseOsSystem(". '%s'; fakeroot -- tar %s tar.%s %s" % (args.environfile, taropts, suffix, applVerBnbr))
465 os.makedirs(args.outputdirectory, exist_ok = True)
466 if args.verbose:
467 print("renaming tar.%s to %s" % (suffix, outdirApplVerBnbrSuffix))
468 os.rename("tar.%s" % suffix, outdirApplVerBnbrSuffix)
469
470 if not os.path.exists(outdirApplVerBnbrSuffix):
471 infoMsg( "Unsuccesful in building %s" % applVerBnbrSuffix)
472 return
473
474 infoMsg( "Successfully built %s" % applVerBnbrSuffix)
475
476
477 if args.upload:
478 envName = "REPACKAGETGZUPLOAD" if useGzip else "REPACKAGETARUPLOAD"
479 groupId = getParam('["%s"]["groupId"]' % suffix, getParam('["groupId"]'))
480 uploadAll(envName, groupId, outdirApplVerBnbrSuffix, suffix, applVer, applVerSuffix)
481
482def buildDocker():
483 """ Build a DOCKER image """
484 image = getParam( '["docker"]["image"]', "n/a" )
485 if image == "n/a":
486 global APPL
487 image = APPL
488 tag = getParam( '["docker"]["tag"]' )
489
490 infoMsg( 'Building a (local) docker image ...' )
491 global args, TMPROOT
492 if args.skipexecution:
493 return
494
495 L = TMPROOT + "/docker"
496 createDockerTempFiles(L)
497
498 if args.skipbuild:
499 traceMsg('Skipping final build')
500 return
501
502 cdCheck(L)
503 verboseOsSystem(". '%s'; docker build -t '%s:%s' ." % (args.environfile, image, tag))
504
505 if args.upload:
506 uploadDocker(image,tag)
507
508
509def strToBool(string):
510 return True if (type(string) is str and string == "true") else False if (type(string) is str and string == "false") else string
511
512def main():
513 """ the main executable function """
514
515 #
516 # deal with the program arguments -
517 # we build two different types of argument lists based on
518 # context. jenkins requires positional arguments while linux cmd line
519 # permits parameterized ones. the jenkins positional argument list is
520 # smaller
521 #
522 parser = argparse.ArgumentParser(
523 description="Build the specified packages. 'package-type' is one or more of " +
524 "docker, debian, tar, tgz" +
525 " (comma-separated), or 'all' to build all of them."
526 )
527
528 REPACKAGEYAML = "repackage.yaml"
529 REPACKAGEJSON = "repackage.json"
530 if os.environ.get("JENKINS"):
531 parser.add_argument("packagetype",help= "debian" +
532 "|docker|tar|tgz" +
533 "|all")
534 parser.add_argument("upload",help="upload package to appropriate repository",nargs='?',default="false")
535 parser.add_argument("directory", type=str, help="where to find the stage directory and %s. Defaults to '.'" % REPACKAGEYAML, default=".",nargs='?')
536 parser.add_argument("environfile", type=str, help="Optional environment file. Overrides $REPACKAGEENVFILE, defaults to /dev/null", default="/dev/null", nargs='?')
537 parser.add_argument("outputdirectory", type=str, help="Output directory. Defaults to 'output' under --directory path.", default=None, nargs='?')
538 parser.add_argument("verbose",help="turn on verbosity",nargs='?',default="true")
539 parser.add_argument("skipexecution",help="indcate packages and exit ",nargs='?',default="false")
540 parser.add_argument("skipbuild",help="skip actually bulding the packages",nargs='?',default="false")
541 parser.add_argument("usecache",help="if debian/tar/tgz artifact already exists use it",nargs='?',default="false")
542 parser.add_argument("keeptempfiles",help="keep temp files at exit",nargs='?',default="false")
543 else:
544 parser.add_argument("-n", "--skipexecution", help="indicate the packages and exit", action="store_true")
545 parser.add_argument("-c", "--usecache", help="if a debian/tar/tgz artifact already exists use it", action="store_true")
546 parser.add_argument("-N", "--skipbuild", help="skip actually building the packages", action="store_true")
547 parser.add_argument("-K", "--keeptempfiles", help="keep temp files at exit", action="store_true")
548 parser.add_argument("-v", "--verbose", help="turn on verbosity", action="store_true")
549 parser.add_argument("-b", "--packagetype", type=str, help="""The package-type may be specified multiple times or may use a ','-separated
550 or space-separated list. 'all' is an alias for all of them. Potential values are debian, docker""" +
551 ", tar or tgz", required=True)
552 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.
553 For Docker, uses $DOCKERREGISTRY as the remote repository to push the image.
554
555 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,
556 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}
557 as the image name with no build number and no .deb suffix.
558 For additional uploads, this will also look for $REPACKAGEDEBIANUPLOAD2, $REPACKAGEDEBIANUPLOAD3, etc., and repeat the upload.
559
560 For tar, uses $REPACKAGETARUPLOAD as the command. Everything said about $REPACKAGEDEBIANUPLOAD applies to $REPACKAGETARUPLOAD.
561 For tgz, uses $REPACKAGETGZUPLOAD as the command. Everything said about $REPACKAGEDEBIANUPLOAD applies to $REPACKAGETGZUPLOAD.
562
563 In addition, if --multipleuploadversions is used, the above will be executed using the list of upload version numbers specified there.
564
565 This is typically used to create multiple versions (using --multipleuploadversions) on multiple remote repositories (using $REPACKAGE*UPLOAD).
566 """)
567 # For additional uploads, repackage will also look for $DOCKERREGISTRY2, $DOCKERREGISTRY3, etc.
568 parser.add_argument("-d", "--directory", type=str, help="where to find the stage directory and %s. Defaults to '.'" % REPACKAGEYAML, default=".")
569 parser.add_argument("-e", "--environfile", type=str, help="Optional environment file. Overrides $REPACKAGEENVFILE, defaults to /dev/null", default="/dev/null")
570 parser.add_argument("-o", "--outputdirectory", type=str, help="Output directory. Defaults to 'output' under --directory path.", default=None)
571 parser.add_argument("-y", "--repackageyaml", type=str, help="Name of parameter file. Defaults to '" + REPACKAGEYAML + "' or '" + REPACKAGEJSON + "' under --directory path.", default=REPACKAGEYAML)
572 parser.add_argument("-B", "--buildnumber", type=str, help="Build number. Defaults to $BUILD_NUMBER, which defaults to a date-based string.", default="")
573 parser.add_argument("-D", "--define", type=str, action='append', help="define an argument at runtime in key=value format")
574 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}")
575 parser.add_argument("-X", "--extendedmultipleuploadversions", type=str, help="""Use multiple names for upload.
576 Comma-separated list of arbitrary strings
577 that can contain any combination of {appname}, {suffix}, {datetime}, {buildnumber} and {version}.
578 There is no default, but if used, this overrides -M.""", default="")
579
580 global args
581 args = parser.parse_args()
582
583 # for some reason, the Jenkins branch leaves these as strings instead of the proper boolean values
584 args.upload = strToBool(args.upload)
585 args.verbose = strToBool(args.verbose)
586 args.skipexecution = strToBool(args.skipexecution)
587 args.skipbuild = strToBool(args.skipbuild)
588 args.usecache = strToBool(args.usecache)
589 args.keeptempfiles = strToBool(args.keeptempfiles)
590
591 # arguments defined at runtime as key=value pairs
592 global rtdef
593 rtdef = {}
594
595 if args.define:
596 for k in args.define:
597 tag, val = k.split("=")
598 rtdef[tag] = val
599
600 for k, v in rtdef.items():
601 traceMsg("runtime defined %s <- %s" % (k,v))
602
603 # check the -e/$REPACKAGEENVFILE value
604 if args.environfile == "":
605 if os.environ.get("REPACKAGEENVFILE") is not None:
606 args.environfile = os.environ["REPACKAGEENVFILE"]
607 if not os.path.isfile(args.environfile) and args.environfile != "/dev/null":
608 die("-e / $REPACKAGEENVFILE must be a file that can be sourced by the shell")
609 if not args.environfile.startswith("/"):
610 args.environfile = os.getcwd() + "/" + args.environfile
611
612 allPackages = [ "debian", "tar", "tgz",
613 "docker" ]
614 args.builds = { }
615 for pkg in allPackages:
616 args.builds[pkg] = False
617 if args.packagetype == "all":
618 args.packagetype = ",".join(allPackages)
619 for build in re.split("[, \t]", args.packagetype):
620 args.builds[build] = True
621
622 args.allUploadVersions = args.multipleuploadversions.split(",")
623 args.allExtendedUploadVersions = args.extendedmultipleuploadversions.split(",") if args.extendedmultipleuploadversions != "" else None
624
625 if args.upload and args.builds["debian"]:
626 if os.environ.get("REPACKAGEDEBIANUPLOAD") is None:
627 die("-u requires $REPACKAGEDEBIANUPLOAD to be set when building debian")
628 elif not re.search("[{]0[}]", os.environ.get("REPACKAGEDEBIANUPLOAD")):
629 die("$REPACKAGEDEBIANUPLOAD is missing {0}")
630 elif not re.search("[{][13][}]", os.environ.get("REPACKAGEDEBIANUPLOAD")):
631 die("$REPACKAGEDEBIANUPLOAD is missing either {1}, {3} or {4}")
632
633 if args.upload and args.builds["tar"]:
634 if os.environ.get("REPACKAGETARUPLOAD") is None:
635 die("-u requires $REPACKAGETARUPLOAD to be set when building tar")
636 elif not re.search("[{]0[}]", os.environ.get("REPACKAGETARUPLOAD")):
637 die("$REPACKAGETARUPLOAD is missing {0}")
638 elif not re.search("[{][134][}]", os.environ.get("REPACKAGETARUPLOAD")):
639 die("$REPACKAGETARUPLOAD is missing either {1}, {3} or {4}")
640
641 if args.upload and args.builds["tgz"]:
642 if os.environ.get("REPACKAGETGZUPLOAD") is None:
643 die("-u requires $REPACKAGETGZUPLOAD to be set when building tgz")
644 elif not re.search("[{]0[}]", os.environ.get("REPACKAGETGZUPLOAD")):
645 die("$REPACKAGETGZUPLOAD is missing {0}")
646 elif not re.search("[{][134][}]", os.environ.get("REPACKAGETGZUPLOAD")):
647 die("$REPACKAGETGZUPLOAD is missing either {1}, {3} or {4}")
648
649 if args.upload and args.builds["docker"] and os.environ.get("DOCKERREGISTRY") is None:
650 die("-u requires $DOCKERREGISTRY to be set when building docker")
651
652 if not os.path.isdir(args.directory):
653 die("The root directory %s does not exist" % args.directory)
654 if not args.directory.startswith("/"):
655 args.directory = os.getcwd() + "/" + args.directory
656 if args.repackageyaml != REPACKAGEYAML:
657 if not os.path.exists(args.directory + "/" + args.repackageyaml):
658 die("The file %s/%s does not exist" % (args.directory, args.repackageyaml))
659 else:
660 if os.path.exists(args.directory + "/" + REPACKAGEYAML):
661 args.repackageyaml = REPACKAGEYAML
662 elif os.path.exists(args.directory + "/" + REPACKAGEJSON):
663 args.repackageyaml = REPACKAGEJSON
664 else:
665 die("Either %s/%s or %s/%s must exist" % (args.directory, args.repackageyaml, args.directory, args.repackagejson))
666
667 if args.outputdirectory is None:
668 args.outputdirectory = args.directory + "/output"
669 else:
670 if not args.outputdirectory.startswith("/"):
671 args.outputdirectory = os.getcwd() + "/" + args.outputdirectory
672 if not os.path.isdir(args.outputdirectory):
673 die("The specified --outputdirectory %s does not exist" % args.outputdirectory)
674
675 # establish some global variables used everywhere
676 global ROOTDIR, TMPROOT
677 ROOTDIR = args.directory
678 TMPROOT = args.directory + "/tmp"
679
680 # and cd to our ROOTDIR
681 cdCheck(ROOTDIR)
682
683 # unless -K is specified, remove any temp files at the end
684 if not args.keeptempfiles:
685 atexit.register(cleanupTmpRoot)
686
687 # grab and share some variables that are used by lots of build functions
688 global APPL, MAINTAINER, VER, BNBR, TIMESTAMP
689 APPL = getParam( '["applicationName"]' )
690 MAINTAINER = getParam( '["maintainer"]' )
691 VER = getParam( '["version"]' )
692 TIMESTAMP = time.strftime("%Y%m%d%H%M%S")
693 BNBR = args.buildnumber if args.buildnumber != "" else os.environ.get("BUILD_NUMBER") if os.environ.get("BUILD_NUMBER") is not None else TIMESTAMP
694
695 # build whatever was requested
696 if args.builds["docker"]:
697 buildDocker()
698 if args.builds["debian"]:
699 buildDebian()
700 if args.builds["tar"]:
701 buildTar(False)
702 if args.builds["tgz"]:
703 buildTar(True)
704
705if __name__ == "__main__":
706 main()