├── .gitignore ├── README.md ├── autopkg ├── autopkg_tweeter.py └── setup_tweet_changes.py ├── deployFusionTemplate.py ├── pickServer.py ├── plist_example.py ├── removeDeprecatedProducts.sh ├── scriptRunner.py └── scriptRunner.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | *.swp 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Copyright (C) 2012 Nate Walck 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /autopkg/autopkg_tweeter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import twitter 5 | import plistlib 6 | import subprocess 7 | import logging 8 | 9 | app_versions = os.path.expanduser('~/Library/AutoPkg/app_versions.plist') 10 | autopkg_report = os.path.expanduser('~/Library/AutoPkg/autopkg_report.plist') 11 | recipe_list = os.path.expanduser('~/Library/AutoPkg/recipe_list.txt') 12 | log_directory = os.path.expanduser('~/Library/AutoPkg/autopkg_tweeter.log') 13 | 14 | twitter_account_name = 'autopkgsays' 15 | 16 | logging.basicConfig(format='%(asctime)s %(message)s', filename=log_directory, level=logging.DEBUG) 17 | 18 | def load_app_keys(): 19 | """Load app keys from a file on disk""" 20 | twitter_app_keys_path = os.path.expanduser('~/.twitter_app_keys') 21 | with open (twitter_app_keys_path) as f: 22 | credentials = [x.strip().split(':') for x in f.readlines()] 23 | 24 | return credentials[0] 25 | 26 | 27 | def load_app_versions(): 28 | if os.path.isfile(app_versions): 29 | versions = plistlib.readPlist(app_versions) 30 | else: 31 | versions = None 32 | 33 | return versions 34 | 35 | 36 | def load_autopkg_results(): 37 | if os.path.isfile(autopkg_report): 38 | report_data = plistlib.readPlist(autopkg_report) 39 | else: 40 | report_data = None 41 | 42 | return report_data 43 | 44 | 45 | def get_previous_app_version(app_name): 46 | app_history = load_app_versions() 47 | if app_history and app_name in app_history: 48 | return app_history[app_name] 49 | else: 50 | return False 51 | 52 | 53 | def store_app_version(app_name, version): 54 | app_history = load_app_versions() 55 | if app_history: 56 | app_history.update({app_name: version}) 57 | plistlib.writePlist(app_history, app_versions) 58 | else: 59 | app_history = {app_name: version} 60 | plistlib.writePlist(app_history, app_versions) 61 | 62 | 63 | def run_autopkg(): 64 | cmd = ['/usr/local/bin/autopkg', 'run', '--recipe-list', recipe_list, '--report-plist=' + autopkg_report] 65 | string_cmd = " ".join(cmd) 66 | proc = subprocess.Popen(string_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) 67 | (output, error_output) = proc.communicate() 68 | return output.strip() 69 | 70 | 71 | def tweet(app_name, version): 72 | MY_TWITTER_CREDS = os.path.expanduser('~/.twitter_oauth') 73 | CONSUMER_KEY, CONSUMER_SECRET = load_app_keys() 74 | 75 | if not os.path.exists(MY_TWITTER_CREDS): 76 | twitter.oauth_dance(twitter_account_name, CONSUMER_KEY, CONSUMER_SECRET, 77 | MY_TWITTER_CREDS) 78 | oauth_token, oauth_secret = twitter.read_token_file(MY_TWITTER_CREDS) 79 | twitter_instance = twitter.Twitter(auth=twitter.OAuth( 80 | oauth_token, oauth_secret, CONSUMER_KEY, CONSUMER_SECRET)) 81 | # Now work with Twitter 82 | twitter_instance.statuses.update(status="%s %s has been released" % (app_name, version)) 83 | 84 | 85 | def tweet_if_new(app_name, version): 86 | previous_version = get_previous_app_version(app_name) 87 | 88 | if previous_version: 89 | if version > previous_version: 90 | logging.info("%s is newer than %s, saving version and sending tweet" % (version, previous_version)) 91 | store_app_version(app_name, version) 92 | try: 93 | tweet(app_name, version) 94 | logging.info("Tweeted %s has been updated to %s" % (app_name, version)) 95 | except: 96 | logging.info("Duplicate Tweet or Failed for another reason") 97 | else: 98 | logging.info("%s is not newer than %s" % (version, previous_version)) 99 | else: 100 | logging.info("%s is newer than %s, saving version and sending tweet" % (version, previous_version)) 101 | store_app_version(app_name, version) 102 | try: 103 | tweet(app_name, version) 104 | logging.info("Tweeted %s has been updated to %s" % (app_name, version)) 105 | except: 106 | logging.info("Duplicate Tweet or Failed for another reason") 107 | 108 | def main(): 109 | logging.info("Running AutoPkg Tweeter...") 110 | autopkg_run_results = run_autopkg() 111 | autopkg_results = load_autopkg_results() 112 | autopkg_run = {} 113 | for item in autopkg_results['new_imports']: 114 | autopkg_run.update({item['name']:item['version']}) 115 | 116 | for app in autopkg_run: 117 | tweet_if_new(app, autopkg_run[app]) 118 | 119 | 120 | if __name__ == "__main__": 121 | main() 122 | -------------------------------------------------------------------------------- /autopkg/setup_tweet_changes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import twitter 5 | 6 | account_name = 'autopkgsays' 7 | 8 | def load_app_keys(): 9 | """Load app keys from a file on disk""" 10 | twitter_app_keys_path = os.path.expanduser('~/.twitter_app_keys') 11 | with open (twitter_app_keys_path) as f: 12 | credentials = [x.strip().split(':') for x in f.readlines()] 13 | 14 | return credentials[0] 15 | 16 | 17 | def setup_twitter_oauth(CONSUMER_KEY, CONSUMER_SECRET): 18 | MY_TWITTER_CREDS = os.path.expanduser('~/.twitter_oauth') 19 | 20 | if not os.path.exists(MY_TWITTER_CREDS): 21 | twitter.oauth_dance(account_name, CONSUMER_KEY, CONSUMER_SECRET, 22 | MY_TWITTER_CREDS) 23 | oauth_token, oauth_secret = twitter.read_token_file(MY_TWITTER_CREDS) 24 | 25 | def main(): 26 | CONSUMER_KEY, CONSUMER_SECRET = load_app_keys() 27 | setup_twitter_oauth(CONSUMER_KEY, CONSUMER_SECRET) 28 | 29 | 30 | if __name__ == "__main__": 31 | main() -------------------------------------------------------------------------------- /deployFusionTemplate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import optparse 4 | import os 5 | import shutil 6 | import string 7 | import re 8 | 9 | p = optparse.OptionParser() 10 | p.set_usage("""Usage: %prog [options]""") 11 | p.add_option('--template', '-t', dest='template', 12 | help="""Path to template you wish to deploy.""") 13 | p.add_option('--output', '-o', dest='output', 14 | help="""Path and name you would like to deploy the template to.""") 15 | 16 | options, arguments = p.parse_args() 17 | 18 | if not options.template: 19 | p.error('You must supply a VM to use as a template') 20 | 21 | if not options.output: 22 | p.error('You must supply an output location and name') 23 | 24 | 25 | templateName = os.path.splitext(os.path.basename(options.template))[0] 26 | 27 | if options.output.endswith('.vmwarevm'): 28 | outputName = os.path.basename(options.output) 29 | else: 30 | outputName = os.path.basename(options.output + '.vmwarevm') 31 | 32 | outputPath = os.path.dirname(options.output) 33 | 34 | print outputName 35 | print outputPath 36 | 37 | print "Copying %s to %s" % (options.template, os.path.join(outputPath, outputName)) 38 | 39 | shutil.copytree(options.template, os.path.join(outputPath, outputName)) 40 | 41 | for file in os.listdir(os.path.join(outputPath, outputName)): 42 | file = os.path.join(outputPath, outputName, file) 43 | if templateName in file: 44 | fileHandler = open(file, 'r') 45 | fileContents = fileHandler.read() 46 | fileHandler.close() 47 | modifiedFileContents = re.sub(templateName, os.path.splitext(outputName)[0], fileContents) 48 | fileHandler = open(file, 'w') 49 | fileHandler.write(modifiedFileContents) 50 | fileHandler.close() 51 | 52 | os.rename(file, string.replace(file, templateName, os.path.splitext(outputName)[0])) 53 | 54 | print "Template Deployed to %s" % os.path.join(outputPath, outputName) 55 | -------------------------------------------------------------------------------- /pickServer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Takes a list of servers and pings them to figure out 3 | # which one is the "best" one to connect to. 4 | 5 | 6 | import subprocess 7 | import re 8 | 9 | serverList = ["www.google.com", "www.yahoo.com", "www.bing.com"] 10 | pingStats = [] 11 | 12 | 13 | def ping(server): 14 | ping = subprocess.Popen( 15 | ["ping", "-c", "4", server], 16 | stdout=subprocess.PIPE, 17 | stderr=subprocess.PIPE 18 | ) 19 | 20 | out, error = ping.communicate() 21 | return out 22 | 23 | 24 | def get_avg_ping(pingResults): 25 | match = re.search(r"(\d+.\d+)/(\d+.\d+)/(\d+.\d+)/(\d+.\d+)", pingResults) 26 | return match.group(2) 27 | 28 | 29 | def get_packet_loss(pingResults): 30 | match = re.search(r"(\d+.\d+\%)", pingResults) 31 | return match.group().strip("%") 32 | 33 | for server in serverList: 34 | tempDict = {} 35 | pingResults = ping(server) 36 | get_packet_loss(pingResults) 37 | tempDict = {"server": server, "avgPing": get_avg_ping(pingResults), "packetLoss": get_packet_loss(pingResults)} 38 | pingStats.append(tempDict) 39 | 40 | print pingStats[0] 41 | -------------------------------------------------------------------------------- /plist_example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # The Following bits were borrowed from Greg Neagle's FoundationPlist: 4 | # https://code.google.com/p/munki/source/browse/code/client/munkilib/FoundationPlist.py 5 | 6 | from Foundation import NSData, \ 7 | NSPropertyListSerialization, \ 8 | NSPropertyListMutableContainers, \ 9 | NSPropertyListXMLFormat_v1_0 10 | 11 | class FoundationPlistException(Exception): 12 | pass 13 | 14 | class NSPropertyListSerializationException(FoundationPlistException): 15 | pass 16 | 17 | class NSPropertyListWriteException(FoundationPlistException): 18 | pass 19 | 20 | def readPlist(filepath): 21 | """ 22 | Read a .plist file from filepath. Return the unpacked root object 23 | (which is usually a dictionary). 24 | """ 25 | plistData = NSData.dataWithContentsOfFile_(filepath) 26 | dataObject, plistFormat, error = \ 27 | NSPropertyListSerialization.propertyListFromData_mutabilityOption_format_errorDescription_( 28 | plistData, NSPropertyListMutableContainers, None, None) 29 | if error: 30 | error = error.encode('ascii', 'ignore') 31 | errmsg = "%s in file %s" % (error, filepath) 32 | raise NSPropertyListSerializationException(errmsg) 33 | else: 34 | return dataObject 35 | 36 | def writePlist(dataObject, filepath): 37 | ''' 38 | Write 'rootObject' as a plist to filepath. 39 | ''' 40 | plistData, error = \ 41 | NSPropertyListSerialization.dataFromPropertyList_format_errorDescription_( 42 | dataObject, NSPropertyListXMLFormat_v1_0, None) 43 | if error: 44 | error = error.encode('ascii', 'ignore') 45 | raise NSPropertyListSerializationException(error) 46 | else: 47 | if plistData.writeToFile_atomically_(filepath, True): 48 | return 49 | else: 50 | raise NSPropertyListWriteException( 51 | "Failed to write plist data to %s" % filepath) 52 | 53 | # Set the plist_path object to the path of the plist you wish to edit 54 | plist_path="/Users/nate/Library/Preferences/com.apple.Safari.plist" 55 | 56 | # Get the contents of the plist specified in plist_path and assign them to plist_contents 57 | plist_contents = readPlist(plist_path) 58 | 59 | # Print out the contents of the 'DomainsToNeverSetUp' key found in plist_contents 60 | print plist_contents["DomainsToNeverSetUp"] -------------------------------------------------------------------------------- /removeDeprecatedProducts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # To use this script, simple type ./removeDeprecatedProducts BRANCH_NAME to remove 3 | # all deprecated products from the specified branch. 4 | 5 | # Place this script in the same folder as repoutil, otherwise it will not work properly. 6 | 7 | # Sanity Check. Makes sure a branch is specified. 8 | if [[ -z $1 ]]; then 9 | echo "Please specify a branch" 10 | exit 1 11 | fi 12 | 13 | # Iterates through the output that shows deprecated items in a given branch. 14 | i=0 15 | for item in $(./repoutil --list-branch=${1} | awk '/Deprecated/{ print $1 }'); do 16 | deprecatedProducts[$i]=$item 17 | let i++ 18 | done 19 | 20 | # Removes deprecated products 21 | ./repoutil --remove-products=${deprecatedProducts[*]} $1 22 | -------------------------------------------------------------------------------- /scriptRunner.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # scriptRunner will run all scripts within a folder that you specify. 3 | # Useful for LaunchAgents and running multiple scripts at user login. 4 | # Thanks to Greg Neagle for an example of how to do this 5 | 6 | import optparse 7 | import os 8 | import subprocess 9 | import plistlib 10 | import datetime 11 | import sys 12 | import stat 13 | 14 | 15 | def main(): 16 | """Main""" 17 | 18 | p = optparse.OptionParser() 19 | p.set_usage("""Usage: %prog [options]""") 20 | p.add_option('--once', '-o', dest='runOnce', 21 | help="""Directory of scripts to run only once.""") 22 | p.add_option('--every', '-e', dest='runEvery', 23 | help="""Directory of scripts to run every time.""") 24 | 25 | options, arguments = p.parse_args() 26 | 27 | # Check to see if passed options are a directory or not 28 | for path in (options.runOnce, options.runEvery): 29 | if path is not None: 30 | if not os.path.isdir(path): 31 | sys.exit(path + " is not a directory") 32 | 33 | runOncePlist = os.path.expanduser("~/Library/Preferences/") +\ 34 | "com.company.scriptrunner.plist" 35 | 36 | try: 37 | runOncePlistValues = plistlib.readPlist(runOncePlist) 38 | except IOError: 39 | runOncePlistValues = {} 40 | 41 | if options.runEvery: 42 | for script in os.listdir(options.runEvery): 43 | st = os.stat(os.path.join(options.runEvery, script)) 44 | mode = st.st_mode 45 | if not mode & stat.S_IWOTH: 46 | try: 47 | subprocess.call(os.path.join(options.runEvery, script), 48 | stdin=None) 49 | except OSError: 50 | print "Something went wrong!" 51 | else: 52 | print script + " is not executable or has bad permissions" 53 | 54 | if options.runOnce: 55 | for script in os.listdir(options.runOnce): 56 | if script in runOncePlistValues: 57 | print os.path.join(options.runOnce, script) + " already run!" 58 | else: 59 | st = os.stat(os.path.join(options.runOnce, script)) 60 | mode = st.st_mode 61 | if not mode & stat.S_IWOTH: 62 | try: 63 | subprocess.call(os.path.join(options.runOnce, script), 64 | stdin=None) 65 | runOncePlistValues[script] = datetime.datetime.now() 66 | except OSError: 67 | print "Uh oh" 68 | else: 69 | print script + " is not executable or has bad permissions" 70 | plistlib.writePlist(runOncePlistValues, runOncePlist) 71 | 72 | 73 | if __name__ == '__main__': 74 | main() 75 | -------------------------------------------------------------------------------- /scriptRunner.rb: -------------------------------------------------------------------------------- 1 | # == Synopsis 2 | # 3 | # scriptRunner: Runs scripts either once or every time 4 | # 5 | # == Usage 6 | # 7 | # scriptRunner.rb -o /path/to/run_once -e /path/to/run_every 8 | # 9 | # -h, --help: 10 | # show help 11 | # 12 | # --once /path/to/run_once, -o /path/to/run_once 13 | # Run scripts in 'run_once' one time 14 | # 15 | # 16 | # --every /path/to/run_every, -e /path/to/run_every 17 | # Run scripts in 'run_every' one time 18 | # 19 | 20 | require 'getoptlong' 21 | require 'rdoc/usage' 22 | require 'osx/cocoa' 23 | require 'time' 24 | 25 | opts = GetoptLong.new( 26 | [ '--help', '-h', GetoptLong::NO_ARGUMENT ], 27 | [ '--once', '-o', GetoptLong::OPTIONAL_ARGUMENT ], 28 | [ '--every', '-e', GetoptLong::OPTIONAL_ARGUMENT ] 29 | ) 30 | 31 | run_once = nil 32 | run_every = nil 33 | 34 | opts.each do |opt, arg| 35 | case opt 36 | when '--help' 37 | RDoc::usage 38 | when '--once' 39 | if arg.nil? || arg == '' then 40 | raise ArgumentError, arg + '--once argument is nil or empty' 41 | else 42 | if File.directory? arg then 43 | run_once = arg 44 | else 45 | raise ArgumentError, arg + ' is not a directory' 46 | end 47 | end 48 | when '--every' 49 | if arg.nil? || arg == '' then 50 | raise ArgumentError, arg + '--every argument is nil or empty' 51 | else 52 | if File.directory? arg then 53 | run_every = arg 54 | else 55 | raise ArgumentError, arg + ' is not a directory' 56 | end 57 | end 58 | end 59 | end 60 | 61 | 62 | 63 | if run_every then 64 | Dir.foreach(run_every) do |script| 65 | next if script == '.' or script == '..' 66 | script_path = File.join(run_every, script) 67 | if [2, 3, 6 ,7].include?(Integer(sprintf("%o", File.stat(script_path).mode)[-1,1])) 68 | puts "#{script_path} has dubious permissions" 69 | elsif !File.executable?(script_path) 70 | puts "#{script_path} is not executable" 71 | else 72 | system(script_path) 73 | end 74 | end 75 | end 76 | 77 | if run_once then 78 | run_once_plist = File.expand_path("~/Library/Preferences/" + "com.company.scriptrunner.plist") 79 | plist_contents = OSX::NSMutableDictionary.dictionaryWithContentsOfFile(run_once_plist) 80 | if plist_contents == nil then 81 | plist_contents = OSX::NSMutableDictionary.alloc.init 82 | end 83 | 84 | Dir.foreach(run_once) do |script| 85 | next if script == '.' or script == '..' 86 | if plist_contents.has_key?(script) 87 | puts "#{script} has already been run" 88 | else 89 | script_path = File.join(run_once, script) 90 | if [2, 3, 6 ,7].include?(Integer(sprintf("%o", File.stat(script_path).mode)[-1,1])) 91 | puts "#{script_path} has dubious permissions" 92 | elsif !File.executable?(script_path) 93 | puts "#{script_path} is not executable" 94 | else 95 | system(script_path) 96 | plist_contents[script] = Time.now.utc.iso8601 97 | end 98 | end 99 | end 100 | plist_contents.writeToFile_atomically(run_once_plist,true) 101 | end 102 | --------------------------------------------------------------------------------