Skip to content

Instantly share code, notes, and snippets.

@yaniv-aknin
Created April 3, 2012 09:50
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yaniv-aknin/2290744 to your computer and use it in GitHub Desktop.
Save yaniv-aknin/2290744 to your computer and use it in GitHub Desktop.
muroku: small utility to aid syncing staging/testing/live heroku environments
#!/usr/bin/env python
# Written by @aknin
# https://gist.github.com/gists/2290744
# This code has been placed in the public domain, no strings attached either way.
from __future__ import print_function
import platform
import os
import sys
import argparse
import json
from clint.textui import colored
import heroku
global options
class MurokuAbort(Exception):
pass
def parse_arguments(argv):
parser = argparse.ArgumentParser()
parser.add_argument('-u', '--username', help="Heroku username to use")
parser.add_argument('-p', '--password', help="Heroku password to use")
parser.add_argument('-k', '--key', help="Heroku API key to use")
parser.add_argument('--key-file', help="JSON file containing API keys to use")
parser.add_argument('--key-name', help="Name of key to use when loading key file", default="")
parser.add_argument('--ignore-prefixes', help="Variable prefixes to skip when comparing apps", nargs="+",
default=('PATH', 'PYTHONUNBUFFERED', 'LD_LIBRARY_PATH', 'LIBRARY_PATH',
'HEROKU_SHARED_POSTGRESQL_'))
parser.add_argument('app1')
parser.add_argument('app2')
options = parser.parse_args(argv[1:])
if options.key_file:
if not os.path.exists(options.key_file):
parser.error("key file %s does not exist" % (options.key_file,))
else:
options.key_file = os.path.expanduser("~/.heroku/keys.json")
if not options.key_name:
with os.popen("git config --get heroku.account 2> /dev/null") as handle:
options.key_name = handle.read().strip()
return options
def get_heroku_handle():
if options.username and options.password:
return heroku.from_pass(options.username, options.password)
if options.key:
return heroku.from_key(options.key)
if os.path.exists(options.key_file):
with open(options.key_file) as handle:
keys = json.load(handle)
return heroku.from_key(keys[options.key_name])
raise MurokuAbort("no credentials provided (see --help for help)")
def get_app_configs(handle):
try:
return handle.apps[options.app1].config, handle.apps[options.app2].config
except KeyError, error:
raise MurokuAbort("no such app %s" % (error,))
def yield_keys(set1, set2, operation_name):
for key in getattr(set1, operation_name)(set2):
for prefix in options.ignore_prefixes:
if key.startswith(prefix):
break
else:
yield key
def report_missing_keys(this, that, colors):
for key in sorted(yield_keys(set(this.data), set(that.data), 'difference')):
print(colored.yellow(key), "is missing in", getattr(colored, colors[that.app.name])(that.app.name))
def report_changed_keys(this, that, colors):
this_color = getattr(colored, colors[this.app.name])
that_color = getattr(colored, colors[that.app.name])
for key in sorted(yield_keys(set(this.data), set(that.data), 'intersection')):
if this.data[key] == that.data[key]:
continue
print(colored.yellow(key), 'is', this_color(this.data[key] or "''"), 'in', this_color(this.app.name))
print(" " * len(key), 'is', that_color(that.data[key] or "''"), 'in', that_color(that.app.name))
def main():
handle = get_heroku_handle()
config1, config2 = get_app_configs(handle)
colors = {config1.app.name: "blue", config2.app.name: "green"}
report_missing_keys(config1, config2, colors)
report_missing_keys(config2, config1, colors)
report_changed_keys(config1, config2, colors)
if __name__ == '__main__':
try:
if platform.system() == 'Windows':
raise MurokuAbort("contact the author for paid development of Windows support") # :)
options = parse_arguments(sys.argv)
sys.exit(main() or 0)
except MurokuAbort, error:
print(colored.red("%s: %s" % (os.path.basename(sys.argv[0]), error)))
sys.exit(1)
certifi==0.0.8
chardet==1.0.1
clint==0.3.1
heroku==0.1.2
python-dateutil==1.5
requests==0.11.1
wsgiref==0.1.2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment