trying on production (my real dir :-))
[pauldeden-misc.git] / s3git.py
bloba197a3d467594e7e67cdfcf8e4758d69ec8f978a
1 #!/usr/bin/python
2 #
3 # Author: Paul D. Eden <paul@benchline.org>
4 # Distributed under the terms of the GNU Public License version 2
5 # Created: 2008-08-04
7 import os
8 import sys
9 import subprocess
11 def run(cmd):
12 subprocess.check_call(cmd, shell=True)
14 def runbool(cmd):
15 if subprocess.call(cmd, shell=True) == 0:
16 return True
17 else:
18 return False
20 def output(cmd):
21 process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
22 rval = process.communicate()[0]
23 if process.returncode != 0:
24 raise Exception, "%s failed" % cmd
25 return rval
27 class NotRepoException(Exception): pass
29 class S3Git(object):
30 """
31 Simple script to automate syncing a local git repository
32 with Amazon S3
34 The remote local git directory must have the same
35 name as the remote S3 directory
36 """
37 def __init__(self):
38 home = os.environ["HOME"]
39 self.git_repo_dir = self._get_repo()
40 self.shadow_repo_dir = self._setup_shadow_repo()
42 def _get_repo(self, directory=""):
43 """
44 Recursively tries to find the git repo we are in
45 if any
46 """
47 if not directory:
48 directory = os.path.realpath(os.getcwd())
49 if directory == "/":
50 raise NotRepoException, "This directory is not a git repository"
51 else:
52 if self._is_git_repo(directory):
53 return directory
54 else:
55 return self._get_repo(os.path.dirname(directory))
57 def _setup_shadow_repo(self):
58 """
59 Makes the repo a clone of shadow_dir
60 if necessary
61 """
62 short_name = os.path.basename(self.git_repo_dir)
63 parent_dir = os.path.dirname(self.git_repo_dir)
64 shadow_dir = os.path.join(parent_dir, ".%s.s3" % short_name)
65 if not os.path.exists(shadow_dir):
66 self._setup_clone(shadow_dir)
67 return shadow_dir
69 def _setup_clone(self, shadow_dir):
70 # make sure this code is right. It can be destructive
71 os.chdir("/tmp")
72 self._backup_repo()
73 run("cp -a %s %s" % (self.git_repo_dir, shadow_dir))
74 run("rm -fr %s" % self.git_repo_dir)
75 run("git clone file://%s %s" % (shadow_dir, self.git_repo_dir))
76 os.chdir(self.git_repo_dir)
78 def _backup_repo(self):
79 backup_dir = self.git_repo_dir + ".bak"
80 if os.path.exists(backup_dir):
81 run("rm -fr %s" % backup_dir)
82 run("cp -a %s %s" % (self.git_repo_dir, backup_dir))
84 def _is_git_repo(self, directory):
85 return os.path.exists("%s/.git" % directory)
87 def _commit_repo(self):
88 runbool("git status")
89 ans = raw_input("Commit the changes to git? ")
90 if ans.lower() == "y":
91 print "Here's a shell with which to commit."
92 run("/bin/dash")
94 def _s3_to_shadow(self):
95 run("s3cmd --force --delete-removed sync s3://git_repos/%s %s" % (os.path.basename(self.git_repo_dir), self.shadow_repo_dir))
97 def _shadow_to_s3(self):
98 run("s3cmd --delete-removed sync %s s3://git_repos/%s" % (self.shadow_repo_dir,
99 os.path.basename(self.git_repo_dir)))
101 def pull(self):
102 self._s3_to_shadow()
103 run("git pull")
105 def push(self):
106 self._commit_repo()
107 self._s3_to_shadow()
108 run("git push")
109 self._shadow_to_s3()
111 def main(self, args):
112 cmd = args[0]
113 if cmd == "push":
114 self.push()
115 elif cmd == "pull":
116 self.pull()
117 else:
118 raise Exception, "%s is an invalid command" % cmd
120 if __name__ == "__main__":
121 from optparse import OptionParser
122 parser = OptionParser()
123 parser.add_option("-v", "--verbose", action="store_true",
124 help="run gregariously")
125 parser.add_option("-q", "--quiet", action="store_true",
126 help="run quietly")
127 (options, args) = parser.parse_args()
128 if len(args) < 1 or (args[0] not in ("push", "pull")):
129 parser.error("You must specify one of 'push' or 'pull'")
130 c = S3Git()
131 c.main(args)