#!/usr/bin/env python # Sample CI usage: scripts\common\fetch-binaries.py -p windows -b release -t abc123 -n 33 # Sample desktop usage: scripts\common\fetch-binaries.py -i ..\plex-dependency-builder\output\Packages\pms-depends-windows-i386-debug-dev.bz2 import hashlib import optparse import ConfigParser import os, re import platform import subprocess import sys import shutil import urllib2 import base64 import glob # Edit these to set a new default dependencies build default_tag = "auto" default_release_build_number = "109" default_release_dir = "plexmediaplayer-dependencies" default_branch = "master" def sha1_for_file(path): hash=hashlib.sha1() fp=file(path, "rb") while True: data=fp.read(4096) if not data: break hash.update(data) return hash.hexdigest() def exec_cmd(args, env={}, supress_output=False): """ main exec_cmd function """ # forward SSH_AUTH_SOCK, so that ssh-agent works if os.name != "nt" and "SSH_AUTH_SOCK" in os.environ: env = os.environ extra_env={"SSH_AUTH_SOCK":os.environ["SSH_AUTH_SOCK"]} env.update(extra_env) else: env = os.environ cmd = subprocess.Popen(args, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, env = env) output = '' while True: out = cmd.stdout.read(1) if out == '' and cmd.poll() != None: break if out != '': if not supress_output: sys.stdout.write(out) output += out if cmd.wait() != 0: raise Exception("Command failed: \"%s\"" % " ".join(args), output) return output platform_map={"linux-synology-i386":"synology-i686", "linux-readynas-arm":"ubuntu-arm", "linux-debian-4-i386":"debian-i686", "linux-control4-arm":"control4-arm", "linux-apm-ppc":"apm-ppc", "linux-armada-arm7":"armada-arm7", "linux-synology-arm":"synology-arm"} def platform_str(): if "BUILD_TAG" in os.environ: for (k, v) in platform_map.iteritems(): if k in os.environ["BUILD_TAG"]: return "linux-"+v return "linux-%s-%s"%(platform.linux_distribution()[0].strip().lower(), platform.machine()) def merge_directories(src, dest, move = False): for src_dir, dirs, files in os.walk(src): dst_dir = src_dir.replace(src, dest) if not os.path.exists(dst_dir): os.mkdir(dst_dir) for file_ in files: src_file = os.path.join(src_dir, file_) dst_file = os.path.join(dst_dir, file_) if os.path.exists(dst_file): os.remove(dst_file) if move: shutil.move(src_file, dst_dir) else: shutil.copy(src_file, dst_dir) if move and os.path.exists(src): shutil.rmtree(src) def unpack_and_install(download, inputfile, installed_filepath): # Make paths absolute before changing directories inputfile = os.path.abspath(inputfile) if not os.path.exists(inputfile): print "Input file %s does not exist" % inputfile sys.exit(1) shafile = "%s.sha.txt" % inputfile installed_filepath = os.path.abspath(installed_filepath) # Go to directory. old_cwd = os.getcwd() os.chdir(options.output) # Check the SHA if options.nochecksha: print "-- Skipping SHA verification" elif os.path.exists(shafile): f = open(shafile, "r") sha = f.readline().strip() computed = sha1_for_file(inputfile) if not computed == sha: print "-- SHA didn't match: %s != %s" % (sha, computed) sys.exit(1) else: print "-- SHA %s matches" % sha f.close() else: print "-- ERROR - No SHA file is available to verify the file's integrity" sys.exit(1) # Untar the package file inputfile_tarfriendly = inputfile if os.name == "nt": pattern = re.compile(r'([a-z]):\\', re.IGNORECASE) inputfile_tarfriendly = pattern.sub('/\\1/', inputfile).replace('\\','/') # The final destination directory is the filename without a version number # The version number is the last element in the filename (by convention) packagename = os.path.splitext(os.path.basename(inputfile))[0] packagename_elements = packagename.split("-") del packagename_elements[-1] packagename_noversion = "-".join(packagename_elements) if os.path.exists(packagename_noversion): shutil.rmtree(packagename_noversion, ignore_errors=True) os.makedirs(packagename_noversion) print "-- Unpacking %s... to %s" % (os.path.basename(inputfile), packagename_noversion) if os.name == "nt": # Touch files from the package, which often arrive from the future when freshly built on our Windows build machine exec_cmd(["tar", "xjf", inputfile_tarfriendly, "-C", packagename_noversion, "--touch", "--strip-components", "1", "--no-same-owner"]) else: exec_cmd(["tar", "xjf", inputfile_tarfriendly, "-C", packagename_noversion, "--strip-components", "1", "--no-same-owner"]) if download and installed_filepath: # Create the installed stamp file to note our success open(installed_filepath, "wb") # Restore directory. os.chdir(old_cwd) return packagename if __name__=='__main__': parser=optparse.OptionParser() parser.add_option("-p", "--platform", action="store", type="string", dest="platform", help="Platform identifier (e.g. windows-i386)", default=None) parser.add_option("-b", "--buildconfig", action="store", type="string", dest="buildconfig", help="Build configuration (release or debug). Default is release", default="release") parser.add_option("-t", "--tag", action="store", type="string", dest="tag", help="Build tag. Default is %s" % default_tag, default=default_tag) parser.add_option("-n", "--buildnumber", action="store", type="string", dest="buildnumber", help="Build number. Default is %s for release builds" % (default_release_build_number), default=None) parser.add_option("-d", "--dir", action="store", type="string", dest="dir", help="CI build dir. Default is %s for release builds" % (default_release_dir), default=None) parser.add_option("-i", "--inputfile", action="store", type="string", dest="inputfile", help="Dependencies package filename, can be supplied instead of platform, buildconfig, tag and buildnumber", default=None) parser.add_option("-o", "--output", action="store", type="string", dest="output", help="Output directory. Default is Dependencies", default="Dependencies") parser.add_option("-x", "--nochecksha", action="store_true", dest="nochecksha", help="Don't check the SHA. Default is false", default=False) parser.add_option("-r", "--branch", action="store", type="string", dest="branch", help="Git branch", default=None) (options, args)=parser.parse_args(sys.argv) if not os.path.exists(options.output): os.makedirs(options.output) # Fail early if platform is not known download = not options.inputfile if download and not options.platform: print "ERROR - A platform must be specified" sys.exit(1) installed_filepath = None if download: if not options.buildnumber: if options.buildconfig == "release": options.buildnumber = default_release_build_number else: options.buildnumber = default_debug_build_number if not options.dir: if options.buildconfig == "release": options.dir = default_release_dir else: options.dir = default_debug_dir installed_filepath = os.path.join(options.output, "konvergo-depends-%s-%s-%s.installed" % (options.platform, options.buildconfig, options.buildnumber)) if os.path.exists(installed_filepath) and options.buildnumber != "latest": print "The required deps bundle was already downloaded and installed." print "You can delete %s to force a reinstall." % installed_filepath sys.exit(0) # Delete previous installed stamps cont = ["-c"] for path in glob.iglob(os.path.join(options.output, "konvergo-depends-%s-%s-*.installed") % (options.platform, options.buildconfig)): match = re.search("-(\d+)\.installed", path, re.DOTALL) if match: if not match.group(1) == options.buildnumber: cont = [] os.remove(path) if options.tag == "auto": req = urllib2.Request("https://nightlies.plex.tv/directdl/plex-dependencies/%s/%s/hash.txt" % (options.dir, options.buildnumber)) try: match = urllib2.urlopen(req).read().rstrip() except urllib2.URLError, err: print err print "ERROR - Download failed" sys.exit(1) options.tag = match base_filename = "konvergo-depends-%s-%s-%s" % (options.platform, options.buildconfig, options.tag) filename = "%s.tbz2" % base_filename installed_filepath = os.path.join(options.output, "konvergo-depends-%s-%s-%s.installed" % (options.platform, options.buildconfig, options.buildnumber)) if os.path.exists(installed_filepath): print "%s was already downloaded and installed." % filename print "You can delete %s to force a reinstall." % installed_filepath sys.exit(0) url = "https://nightlies.plex.tv/directdl/plex-dependencies/%s/%s/%s" % (options.dir, options.buildnumber, filename) inputfile = os.path.join(options.output, filename) print "-- Downloading %s ..." % url exec_cmd(["wget", "--no-check-certificate"] + cont + ["-O", inputfile, url]) shaurl = "%s.sha.txt" % url shafile = "%s.sha.txt" % inputfile print "-- Downloading %s ..." % shaurl exec_cmd(["wget", "--no-check-certificate"] + ["-O", shafile, shaurl]) else: inputfile = options.inputfile # Unpack and install packagename = unpack_and_install(download, inputfile, installed_filepath) # On OS X, we need to postprocess the dependencies. if platform.system() == 'Darwin': root = os.path.realpath(os.path.join(os.getcwd())) script = os.path.join(root, "scripts", "fix-install-names.py") for p in ("lib", "bin", "update_installer"): path = os.path.join(root, "Dependencies", "konvergo-depends-" + options.platform + "-" + options.buildconfig, p) exec_cmd([script, path]) # Done! print "-- Done with %s" % packagename