├── .gitignore ├── README.md ├── getMostFrequentUser ├── getMostFrequentUser.sh └── README.txt ├── tccmanager ├── tcc_services.plist └── tccmanager.py ├── icnsToWp ├── README.md └── icnsToWp.py ├── make_adobe_encorecs6_content_pkg └── make_adobe_encorecs6_content_pkg.sh ├── GenerateSibeliusContentPkg ├── README.md └── GenerateSibeliusContentPkg.py └── repo_sync_and_mail └── repo_sync_and_mail.py /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Collection of scripts for Mac sysadmin work. 2 | -------------------------------------------------------------------------------- /getMostFrequentUser/getMostFrequentUser.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ac -p | sort -nrk 2 | awk 'NR == 2 { print $1; exit }' 4 | -------------------------------------------------------------------------------- /getMostFrequentUser/README.txt: -------------------------------------------------------------------------------- 1 | This one-liner retrieves the username of the user with longest time logged in, using the 2 | UNIX ac command. -------------------------------------------------------------------------------- /tccmanager/tcc_services.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | kTCCServiceAddressBook 6 | 7 | com.adiumX.adiumX 8 | 9 | client_type 10 | 0 11 | allowed 12 | 1 13 | prompt_count 14 | 0 15 | 16 | com.macromates.TextMate.preview 17 | 18 | client_type 19 | 0 20 | allowed 21 | 0 22 | prompt_count 23 | 0 24 | 25 | com.apple.FinalCutPro 26 | 27 | client_type 28 | 0 29 | allowed 30 | 1 31 | prompt_count 32 | 0 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /icnsToWp/README.md: -------------------------------------------------------------------------------- 1 | # icnsToWp 2 | 3 | ## overview 4 | 5 | A very simple utility that takes one or more icns files as input and optimizes, compress and uploads select sizes to a Wordpress instance using its XMLRPC API. This API is enabled by default in Wordpress 3.5. 6 | 7 | There is no proper error handling in this script. 8 | 9 | Edit the config options at the top of the script to set options for the Wordpress URL and optimization tools if you plan to use these options. 10 | 11 | ## example usage 12 | 13 | icnsToWp.py --size 128 --wordpress-upload --include-retina /Applications/TextEdit.app/Contents/Resources/Edit.icns 14 | 15 | For detailed usage and options: 16 | 17 | icnsToWp.py -h 18 | 19 | ## optimizing 20 | 21 | For the `--optimize` option, this script was only tested with optimizer tool `optipng` and compress tool `convert` (ImageMagick/GraphicsMagick) in mind, but others should work fine if the compress tool takes the following argument structure: 22 | 23 | `tool [options here] infile outfile` 24 | 25 | `optipng` and `convert` are installed easily with [Homebrew](http://mxcl.github.com/homebrew). 26 | -------------------------------------------------------------------------------- /make_adobe_encorecs6_content_pkg/make_adobe_encorecs6_content_pkg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Packaging script for Adobe Encore CS6 "Functional Content". 4 | # 5 | # Thanks, Adobe, for making three different versions of the "functional content" 6 | # available all in different locations. The ESD download, which is actually in a "standard" 7 | # AAMEE-packageable format, contains only a fraction of the content, and we must resort 8 | # to looking for .zip or .7z archives on forums and blog posts. 9 | # 10 | # Define your own ID_PREFIX, NAME and VERSION. Package identifier will be $ID_PREFIX.$NAME. 11 | # 12 | # Requires fpm: 13 | # 14 | # gem install fpm 15 | 16 | NAME=AdobeEncoreCS6_Content 17 | ID_PREFIX=org.great.my 18 | VERSION=6.0.2013.03.01 19 | URL=https://blogs.adobe.com/premiereprotrainingfiles/EncoreContent_en-US.zip 20 | ARK=EncoreContent_en-US.zip 21 | MD5=f42ef6070cd29c8fcb0478fe7c71bf22 22 | 23 | # download 24 | if [ ! -e "$ARK" ]; then 25 | curl -k -L -o "$ARK" "$URL" 26 | fi 27 | VERIFY=$(md5 -q $ARK) 28 | if [ "$VERIFY" != "$MD5" ]; then 29 | echo "md5 don't match. try wiping it and letting it re-download." 30 | exit 31 | else 32 | echo "md5 matches." 33 | fi 34 | 35 | fpm --verbose \ 36 | -s tar \ 37 | -t osxpkg \ 38 | --osxpkg-identifier-prefix $ID_PREFIX \ 39 | -n $NAME \ 40 | -v $VERSION \ 41 | -C en-US \ 42 | --prefix /Applications/Adobe\ Encore\ CS6 \ 43 | "$ARK" 44 | -------------------------------------------------------------------------------- /GenerateSibeliusContentPkg/README.md: -------------------------------------------------------------------------------- 1 | GenerateSibeliusContentPkg 2 | ========================== 3 | 4 | Overview 5 | -------- 6 | 7 | Sibelius 7 has a lot of content to optionally install. This script's purpose is to automate the process of repacking this content (or a subset thereof) into an OS X installer package using The Luggage. It takes advantage of Python's multiprocessing module to greatly speed up the bzip2 decompression of the audio sample files to stage them for packaging. 8 | 9 | Sibelius ships with 3 content discs, but there is also additional content on the main application disc, as well as content update DMGs available for download. These are all valid input to the script, so it's possible to combine them in multiple packages if desired. 10 | 11 | 12 | Requirements 13 | ------------ 14 | 15 | * The Luggage 16 | * Python 2.6 or greater 17 | * A _lot_ of disk space (100GB or greater for all content discs, due to space required to stage the package, archive, and wrap in a disk image) 18 | 19 | 20 | Caveats 21 | ------- 22 | 23 | There's very little error handling. It also assumes the shell the script is run from will be able to run the 'make' command to process Luggage Makefiles. 24 | 25 | 26 | Usage 27 | ----- 28 | 29 | 1. Create DMGs of the content discs you want to repackage. 30 | 2. Run the script using the -f argument to specify each of these DMGs whose content you want to add to the package. You can use the -f argument multiple times to combine multiple packages. 31 | 3. The script will ask a couple questions, the package title, version string, etc. 32 | 4. For each disk image, the content will be decompressed to a staging area, a Luggage Makefile will be generated and 'make dmg' will be run against it. There is an optional argument '-p' that will just run 'make pkg' instead. 33 | 5. Due to the amount of space required to stage, package and compress the data, you may want to manually run a 'make clean' from this script's working folder to clean up the Luggage temporary files. -------------------------------------------------------------------------------- /repo_sync_and_mail/repo_sync_and_mail.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # Simple wrapper for repo_sync. Saves a datestamped log to LOGDIR, and e-mails the log if 4 | # the log contains the string '.pkg'. (ie. if a new product was replicated). The log will 5 | # also contain the contents of English.dist for any new products. 6 | # 7 | # Additional requirements: 8 | # - 'psutil' module, available from PyPi - version currently tested with this script is 9 | # 2.1.1, provided by the Debian Jessie 'python-psutil' apt package 10 | 11 | import os 12 | import sys 13 | import subprocess 14 | import plistlib 15 | from time import strftime, localtime 16 | import re 17 | import psutil # psutil is used to more easily detect whether repo_sync is already running 18 | 19 | import smtplib 20 | from email.MIMEMultipart import MIMEMultipart 21 | from email.MIMEBase import MIMEBase 22 | from email.MIMEText import MIMEText 23 | from email.Utils import formatdate 24 | from email import Encoders 25 | 26 | LOGDIR = '/var/log/reposado' 27 | REPO_SYNC = '/home/reposado/git/reposado/code/repo_sync' 28 | REPO_PREFS = '/home/reposado/git/reposado/code/preferences.plist' 29 | prefs = plistlib.readPlist(REPO_PREFS) 30 | 31 | 32 | 33 | mail_from = "reposado@my.org" 34 | mail_to = ["admin@my.org"] 35 | smtpserver = "smtp.my.org" 36 | 37 | def reposync_is_running(): 38 | proclist = psutil.get_process_list() 39 | for p in proclist: 40 | for arg in p.cmdline(): 41 | if os.path.basename(arg) == 'repo_sync': 42 | return True 43 | return False 44 | 45 | # E-mail code largely based on http://code.google.com/p/munki/wiki/PreflightAndPostflightScripts 46 | def send_mail(send_from, send_to, subject, text, files=[], server="localhost"): 47 | assert type(send_to) == list 48 | assert type(files) == list 49 | 50 | msg = MIMEMultipart() 51 | msg['From'] = send_from 52 | msg['To'] = ", ".join(send_to) 53 | msg['Date'] = formatdate(localtime=True) 54 | msg['Subject'] = subject 55 | 56 | msg.attach(MIMEText(text)) 57 | 58 | for f in files: 59 | part = MIMEBase('application', "octet-stream") 60 | part.set_payload(open(f, "rb").read()) 61 | Encoders.encode_base64(part) 62 | part.add_header('Content-Disposition', 63 | 'attachment; filename="%s"' % os.path.basename(f)) 64 | msg.attach(part) 65 | 66 | smtp = smtplib.SMTP(server) 67 | smtp.sendmail(send_from, send_to, msg.as_string()) 68 | smtp.close() 69 | 70 | if reposync_is_running(): 71 | print "repo_sync is already running. Exiting.." 72 | sys.exit(1) 73 | 74 | if not os.path.exists(LOGDIR): 75 | os.mkdir(LOGDIR) 76 | 77 | logfile = os.path.join(LOGDIR, strftime('%Y-%m-%d_%H%M.log', localtime())) 78 | 79 | # do the repo_sync 80 | cmd = [REPO_SYNC, '--log=%s' % logfile] 81 | subprocess.call(cmd) 82 | 83 | try: 84 | lfb = open(logfile) 85 | logfile_contents = lfb.read() 86 | lfb.close() 87 | except: 88 | print "Can't read the logfile" 89 | sys.exit(1) 90 | 91 | if logfile_contents.find('pkg') != -1: 92 | meta = plistlib.readPlist(os.path.join(prefs['UpdatesMetadataDir'], 'ProductInfo.plist')) 93 | distpaths = re.findall("content/.*English.dist", logfile_contents) 94 | localdists = [os.path.join(prefs['UpdatesRootDir'], p) for p in distpaths] 95 | subject = "Reposado log" 96 | body = logfile_contents 97 | body += "\n\n" 98 | for dist in localdists: 99 | distcontent = open(dist, 'r') 100 | body += distcontent.read() 101 | distcontent.close() 102 | body += "\n\n\n" 103 | send_mail(mail_from, mail_to, subject, body, files=[], server=smtpserver) 104 | -------------------------------------------------------------------------------- /icnsToWp/icnsToWp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # icnsToWp - Tim Sutton, December 2012 4 | 5 | import os 6 | import sys 7 | import xmlrpclib 8 | import optparse 9 | from subprocess import call 10 | from tempfile import mkdtemp 11 | import shutil 12 | from getpass import getpass 13 | 14 | WORDPRESS_URL = 'http://macops.ca/xmlrpc.php' 15 | OPTI_TOOL = '/usr/local/bin/optipng' # optipng's defaults are good enough, no options 16 | COMPRESS_TOOL = '/usr/local/bin/convert' # using imagemagick 17 | # imagemagick options.. IM docs are poor, use at your own risk 18 | COMPRESS_ARGS = ['-quality', '95', '-depth', '7', '-dither', 'Riemersma'] 19 | ICONUTIL = '/usr/bin/iconutil' 20 | 21 | def upload(path, proxy, user, password): 22 | with open(path, 'rb') as fd: 23 | imgdata = fd.read() 24 | # wp.uploadFile endpoint: blogid, username, password, data struct {name, type, bits, overwrite} 25 | # from API docs: 26 | # blogid: Not applicable for WordPress, can be any value and will be ignored. 27 | results = proxy.wp.uploadFile(1, user, password, 28 | {'name': os.path.basename(path), 29 | 'type': 'image/%s' % (os.path.splitext(path)[1]), 30 | 'bits': xmlrpclib.Binary(imgdata)}) 31 | return results 32 | 33 | 34 | def main(): 35 | usage = """%prog [options] /path/to/file.icns 36 | Convert a .icns file to one or more png files, optimize and optionally upload to Wordpress""" 37 | o = optparse.OptionParser(usage=usage) 38 | o.add_option('-s', '--size', action='append', 39 | help="Icon size to use - can be specified multiple \ 40 | times, should be multiple of 2.") 41 | o.add_option('-n', '--name', 42 | help="Name to prepend to file - defaults to name of input .icns file.") 43 | o.add_option('-w', '--wordpress-upload', action='store_true', 44 | help="Upload files to Wordpress. Set WORDPRESS_URL in this script to \ 45 | http://my-wordpress-site-root/xmlrpc.php and ensure XMLRPC access is enabled.") 46 | o.add_option('-o', '--optimize', action='store_true', 47 | help="Optimize file using tools: %s and %s. Modify OPTI_TOOL and \ 48 | COMPRESS_TOOL at top of this script edit paths and options." % (OPTI_TOOL, COMPRESS_TOOL)) 49 | o.add_option('-r', '--include-retina', action='store_true', 50 | help="Include retina (ie. 'file@2x.png') versions") 51 | opts, args = o.parse_args() 52 | 53 | if len(args) < 1: 54 | sys.stderr << "Required argument: one or more .icns files to convert!" 55 | sys.exit 56 | 57 | if opts.wordpress_upload: 58 | user = raw_input("Wordpress user: ") 59 | password = getpass("Wordpress password: ") 60 | proxy = xmlrpclib.ServerProxy(WORDPRESS_URL) 61 | 62 | versions = [''] 63 | if opts.include_retina: 64 | versions.append('@2x') 65 | 66 | for icnsfile in args: 67 | icons_out = mkdtemp() + ".iconset" 68 | call([ICONUTIL, '-c', 'iconset', icnsfile, '-o', icons_out]) 69 | for size in opts.size: 70 | for version in versions: 71 | if opts.name: 72 | name = opts.name 73 | else: 74 | name = os.path.splitext(os.path.basename(icnsfile))[0] 75 | 76 | iconpath = os.path.join(icons_out, 'icon_%sx%s%s.png' % (size, size, version)) 77 | renamed_iconpath = os.path.join(os.path.dirname(iconpath), os.path.basename(iconpath).replace('icon', name)) 78 | os.rename(iconpath, renamed_iconpath) 79 | outfile = os.path.join(os.getcwd(), "%s_%s%s.png" % (name, size, version)) 80 | if opts.optimize: 81 | # optimize (optipng is in-place) 82 | call([OPTI_TOOL, renamed_iconpath]) 83 | compress_cmd = [COMPRESS_TOOL] + COMPRESS_ARGS 84 | # outfile = os.path.join(os.getcwd(), "%s_%s%s.png" % (name, size, version)) 85 | compress_cmd += [renamed_iconpath, outfile] 86 | # compress 87 | call(compress_cmd) 88 | else: 89 | # outfile = 90 | shutil.copyfile(renamed_iconpath, outfile) 91 | if opts.wordpress_upload: 92 | results = upload(outfile, proxy, user, password) 93 | print "Upload results:" 94 | print results 95 | # clean up 96 | shutil.rmtree(icons_out) 97 | 98 | if __name__ == '__main__': 99 | main() 100 | -------------------------------------------------------------------------------- /tccmanager/tccmanager.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Make arbitrary changes to the TCC sqlite3 database, which stores the 4 | # applications that are allowed to access a user's contacts. 5 | # 6 | # Accepted input is in the form of a plist - see the 'tcc_services.plist' example. 7 | # It will add entries that don't already exist and update existing ones. It does not remove 8 | # entries that are present in the DB but not in the input plist. 9 | # 10 | # Ideally we could pass app bundle names or .app paths directly at the command line so that changes can 11 | # be made more atomically or run as one-off commands in a LaunchAgent script, for example. 12 | 13 | import sqlite3 14 | import os 15 | import sys 16 | import plistlib 17 | import optparse 18 | 19 | TCC_DIR = os.path.expanduser('~/Library/Application Support/com.apple.TCC') 20 | DBPATH = os.path.join(TCC_DIR, 'TCC.db') 21 | 22 | def createDB(path): 23 | conn = sqlite3.connect(path) 24 | c = conn.cursor() 25 | 26 | c.execute('''CREATE TABLE admin 27 | (key TEXT PRIMARY KEY NOT NULL, value INTEGER NOT NULL)''') 28 | c.execute("INSERT INTO admin VALUES ('version', 4)") 29 | c.execute('''CREATE TABLE access 30 | (service TEXT NOT NULL, 31 | client TEXT NOT NULL, 32 | client_type INTEGER NOT NULL, 33 | allowed INTEGER NOT NULL, 34 | prompt_count INTEGER NOT NULL, 35 | CONSTRAINT key PRIMARY KEY (service, client, client_type))''') 36 | c.execute('''CREATE TABLE access_times 37 | (service TEXT NOT NULL, 38 | client TEXT NOT NULL, 39 | client_type INTEGER NOT NULL, 40 | last_used_time INTEGER NOT NULL, 41 | CONSTRAINT key PRIMARY KEY (service, client, client_type))''') 42 | c.execute('''CREATE TABLE access_overrides 43 | (service TEXT PRIMARY KEY NOT NULL)''') 44 | conn.commit() 45 | conn.close() 46 | 47 | def main(): 48 | usage = "usage: %prog --plist db_entries.plist | (--allow | --disallow) app.bundle.id" 49 | o = optparse.OptionParser(usage=usage) 50 | o.add_option('-p', '--plist', action="store", 51 | help='Path to a plist to be synced to the TCC db.') 52 | o.add_option('-a', '--allow', metavar="bundle-id", action="append", 53 | help='Name of an application bundle ID to allow access to contacts. \ 54 | Can be specified multiple times.') 55 | o.add_option('-d', '--disallow', metavar="bundle-id", action="append", 56 | help='Name of an application bundle ID to disallow access to contacts. \ 57 | Can be specified multiple times.') 58 | opts, args = o.parse_args() 59 | 60 | if (opts.plist and opts.allow) or (opts.plist and opts.disallow): 61 | print "--plist option is mutually exclusive to using --allow or --disallow." 62 | sys.exit(1) 63 | 64 | db_exists = False 65 | if not os.path.exists(TCC_DIR): 66 | os.mkdir(TCC_DIR, int('700', 8)) 67 | else: 68 | db_exists = True 69 | 70 | conn = sqlite3.connect(DBPATH) 71 | c = conn.cursor() 72 | 73 | # Setup the database if it doesn't already exist 74 | if not db_exists: 75 | createDB(DBPATH) 76 | 77 | if opts.plist: 78 | try: 79 | apps = plistlib.readPlist(opts.plist) 80 | except: 81 | print "Error reading plist!" 82 | print sys.exc_info() 83 | print sys.exit(2) 84 | 85 | # add or modify any kTCCServiceAddressBook items we might have defined in the plist 86 | if 'kTCCServiceAddressBook' in apps.keys(): 87 | for app, attrs in apps['kTCCServiceAddressBook'].items(): 88 | data = ('kTCCServiceAddressBook', 89 | app, 90 | attrs['client_type'], 91 | attrs['allowed'], 92 | attrs['prompt_count']) 93 | c.execute('''INSERT or REPLACE INTO access values 94 | (?, ?, ?, ?, ?)''', data) 95 | conn.commit() 96 | 97 | else: 98 | if opts.allow: 99 | for bundle_id in opts.allow: 100 | c.execute('''INSERT or REPLACE INTO access values 101 | ('kTCCServiceAddressBook', ?, 0, 1, 0)''', (bundle_id,)) 102 | conn.commit() 103 | 104 | if opts.disallow: 105 | for bundle_id in opts.disallow: 106 | c.execute('''INSERT or REPLACE INTO access values 107 | ('kTCCServiceAddressBook', ?, 0, 0, 0)''', (bundle_id,)) 108 | conn.commit() 109 | 110 | conn.close() 111 | 112 | if __name__ == '__main__': 113 | main() 114 | -------------------------------------------------------------------------------- /GenerateSibeliusContentPkg/GenerateSibeliusContentPkg.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | import subprocess 5 | import sys 6 | import time 7 | import tarfile 8 | import optparse 9 | import tempfile 10 | 11 | REVERSE_DOMAIN = 'org.awesome.my' 12 | 13 | 14 | def generateLuggageMakefile(pkgTitle, pkgVersion, sourcePath, destPath): 15 | """Generate a Luggage Makefile for the project with a single new 16 | 'pack-sibelius-library' rule""" 17 | makefilePath = os.path.join(destPath, 'Makefile') 18 | fb = open(makefilePath, 'w') 19 | fb.write(""" 20 | include /usr/local/share/luggage/luggage.make 21 | 22 | TITLE=%s 23 | REVERSE_DOMAIN=%s 24 | PACKAGE_VERSION=%s 25 | PAYLOAD=pack-sibelius-library 26 | SOUNDS_DEST_PATH="${WORK_D}/Library/Application Support/Avid/Sibelius Sounds" 27 | 28 | pack-sibelius-library: 29 | sudo mkdir -p ${SOUNDS_DEST_PATH} 30 | sudo mv "%s" ${SOUNDS_DEST_PATH}/ 31 | sudo chown -R root:admin ${SOUNDS_DEST_PATH} 32 | sudo chmod -R 755 ${SOUNDS_DEST_PATH} 33 | """ % (pkgTitle, REVERSE_DOMAIN, pkgVersion, sourcePath)) 34 | fb.close() 35 | 36 | 37 | def getPackageInfo(): 38 | """Prompt the user for the name and version of the new package""" 39 | pkgTitle = raw_input("Enter a name for this package: ") 40 | rightNow = time.localtime() 41 | versionDate = "%s.%02d.%02d" % (rightNow.tm_year, rightNow.tm_mon, rightNow.tm_mday) 42 | pkgVersion = raw_input("Enter a package version: [%s] " % versionDate) 43 | if not pkgVersion: 44 | pkgVersion = versionDate 45 | return (pkgTitle, pkgVersion) 46 | 47 | 48 | def mountDMGs(files): 49 | """Given a list of DMGs, mount them one by one, returning a list of tempfile mountpoints""" 50 | mountPoints = [] 51 | if options.file: 52 | for item in options.file: 53 | if os.path.exists(item): 54 | tmpMount = tempfile.mkdtemp() 55 | # Mount 56 | print "Mounting %s..." % os.path.basename(item) 57 | retcode = subprocess.call(['hdiutil', 'attach', '-nobrowse', '-mountpoint', tmpMount, item]) 58 | if retcode: 59 | print "There was an issue mounting the specified image" 60 | sys.exit(1) 61 | else: 62 | print "Mounted at %s." % tmpMount 63 | # Check if it's a valid content disc 64 | if not os.path.exists(os.path.join(tmpMount, 'InstallerData')): 65 | print "Not a valid content disc!" 66 | sys.exit(1) 67 | else: 68 | mountPoints.append(tmpMount) 69 | 70 | else: 71 | print "ERROR: No file at path %s!" % item 72 | return mountPoints 73 | 74 | 75 | def unmountDMGs(mountPoints): 76 | """Given a list of DMGs, unmount them one by one""" 77 | for mountPoint in mountPoints: 78 | print "Unmounting %s..." % mountPoint 79 | subprocess.call(['hdiutil', 'detach', mountPoint]) 80 | 81 | 82 | # 83 | # MAIN 84 | # 85 | 86 | usage = """usage: %prog -f [/path/to/dmg] [-f /path/to/another/dmg...] 87 | %prog --help for more information.""" 88 | 89 | p = optparse.OptionParser(usage=usage) 90 | p.add_option('--file', '-f', action="append", 91 | help='''Path to a content dmg. Can be specified multiple times and they will be 92 | combined into a single package.''') 93 | p.add_option('--make-pkg', '-p', action="store_true", 94 | help="""Build a pkg with Luggage instead of the default DMG.""") 95 | 96 | if os.geteuid() != 0: 97 | print "ERROR: This script should be run as root, in order for Luggage to run successfully. Exiting..." 98 | sys.exit(1) 99 | 100 | options, arguments = p.parse_args() 101 | 102 | if options.file: 103 | mountPoints = mountDMGs(options.file) 104 | else: 105 | print "You need to specify at least one content DMG!" 106 | sys.exit(1) 107 | 108 | if mountPoints == []: 109 | print "No valid DMGs to mount. Exiting..." 110 | sys.exit(1) 111 | 112 | else: 113 | # Get package info from user interactively 114 | (pkgTitle, pkgVersion) = getPackageInfo() 115 | #(pkgTitle, pkgVersion) = ('SibeliusContentTest', '2012.01.15') 116 | 117 | # Set up build dirs 118 | # buildDirName = 'build' + '.' + pkgTitle 119 | buildDirPath = os.path.join(os.getcwd(), "build.%s" % pkgTitle) 120 | libraryExtractPath = os.path.join(buildDirPath, 'Sibelius 7 Sounds') 121 | if not os.path.exists(buildDirPath): 122 | os.mkdir(buildDirPath) 123 | os.mkdir(libraryExtractPath) 124 | 125 | def unbz2File(path): 126 | tar = tarfile.open(name=path, mode='r:bz2') 127 | tar.extractall(path=libraryExtractPath) 128 | tar.close() 129 | print "%s done" % os.path.basename(path) 130 | 131 | from multiprocessing import Pool 132 | pool = Pool() 133 | 134 | archivesToProcess = [] 135 | for mountPoint in mountPoints: 136 | installerDataPath = os.path.join(mountPoint, 'InstallerData') 137 | for archive in os.listdir(installerDataPath): 138 | if archive.endswith('.tbz'): 139 | archivesToProcess.append(os.path.join(installerDataPath, archive)) 140 | print "Added archive %s" % (os.path.join(installerDataPath, archive)) 141 | # print "Extracting %s..." % archive 142 | # subprocess.call(['tar', '-xjvf', os.path.join(installerDataPath, archive), '-C', libraryExtractPath]) 143 | # unbz2File(os.path.join(installerDataPath, archive), installerDataPath) 144 | 145 | imap_it = pool.imap(unbz2File, archivesToProcess) 146 | 147 | print "Beginning extraction..." 148 | for item in imap_it: 149 | pass 150 | 151 | # print "Extracting %s" % os.path.basename(item) 152 | 153 | unmountDMGs(mountPoints) 154 | 155 | # Make the Luggage Makefile 156 | generateLuggageMakefile(pkgTitle, pkgVersion, libraryExtractPath, buildDirPath) 157 | 158 | # Set up Luggage run 159 | luggageCmd = ['make'] 160 | if options.make_pkg: 161 | luggageTarget = 'pkg' 162 | else: 163 | luggageTarget = 'dmg' 164 | luggageCmd.append(luggageTarget) 165 | 166 | # Do Luggage 167 | print "Building a %s with Luggage..." % luggageTarget 168 | subprocess.call(luggageCmd, cwd=buildDirPath) 169 | --------------------------------------------------------------------------------