-
-
Save ukanga/231118 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Example setup configuration file | |
[main] | |
url=git://github.com/mvpdev/rapidsms.git | |
name=sms | |
rev=HEAD | |
[rapidsms] | |
url=git://github.com/rapidsms/rapidsms.git | |
name=rapidsms-git | |
apps=ajax,webapp,logger,httptester,messaging,admin,patterns,locations,reporters | |
install=true | |
[pygsm] | |
url=git://github.com/rapidsms/pygsm.git | |
name=pygsm-git | |
install=true | |
[dimagi-weltel] | |
url=git://github.com/dimagi/rapidsms.git | |
name=dimagi-weltel | |
apps=scheduler | |
branch=weltel | |
[pygsm-ussd] | |
url=git://gist.github.com/177018.git | |
name=pygsm-ussd | |
patch=pygsm, pygsm-ussd.patch | |
[rapidsms-idswitch] | |
url=git://gist.github.com/171942.git | |
name=rapidsms-idswitch | |
patch=rapidsms, rapidsms-idswitch.patch | |
[rapidsms-locationsadmin] | |
url=git://gist.github.com/205135.git | |
name=rapidsms-locationsadmin | |
patch=rapidsms, rapidsms-locationsadmin.patch | |
[rapidsms-webapp-css] | |
url=git://gist.github.com/209063.git | |
name=rapidsms-locationsadmin | |
patch=rapidsms, rapidsms-webapp-css.patch |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
''' | |
Q&D script to setup rsms apps, mvp way. | |
** What does it do ? | |
* clone and/or update your main rapidsms fork | |
* clone and/or update other rapidsms forks | |
* links your third-parties apps into your fork | |
** How it works | |
1. Create a config file (install.ini) | |
2. launch the script | |
done. | |
** Config file | |
* INI file format | |
* each [section] represent a repository | |
* [main] section represent __your__ main fork (target) | |
* [main] is mandatory | |
* [sections] accepts several options. | |
Options: | |
* url (mandatory string): the URL of the git repository to clone | |
* name (recommended string): the name used to refer to that repository. | |
* rev (optional int or HEAD): the revision to pull | |
* install (optional boolean): whether or not to install app (setup.py) | |
* patch (optional string): target repository (section name) and patch file | |
separated by a comma. Example: pygsm, pygsm.patch | |
** Script usage | |
1. Go to the parent of your soon-to-be rapidsms folder | |
2. write or copy your config file as install.ini | |
3. launch: chmod +x setup_rapidsms.py | |
4. launch: ./setup_rapidsms.py | |
* If you have repositories with install=True, | |
sudo will prompt for password (after downloads) | |
* script accepts two optional parameters: | |
-c filename : specify a config file instead of install.ini | |
-t path : specify a target path instead of . | |
''' | |
import sys, os, copy | |
from ConfigParser import ConfigParser, NoOptionError | |
import getopt | |
class Repository(object): | |
''' holds repository information ''' | |
ident = None | |
url = None | |
name = None | |
branch = None | |
rev = 'HEAD' | |
tag = None | |
install = False | |
patch = None | |
patch_target = None | |
fcopy = None | |
fcopy_target = None | |
fcopy_ftarget = None | |
def __init__(self, url=None, name=None): | |
if url and isinstance(url, str): | |
self.url = url | |
if name and isinstance(name, str): | |
self.name = name | |
def get_rev(self): | |
''' return command-line friendly revision string ''' | |
if self.rev == None or self.rev == 'HEAD': | |
return "" | |
else: | |
return self.rev | |
class FeederRepository(Repository): | |
''' Repository which holds apps to link ''' | |
apps = [] | |
def __init__(self, url=None, name=None): | |
super(FeederRepository, self).__init__(url, name) | |
self.apps = [] | |
def add_app(self, name): | |
''' add named app to the list ''' | |
self.apps.append(name) | |
class GitCommander(object): | |
''' grab, updates and links repositories based on configuration ''' | |
main = None | |
others = [] | |
root = None | |
others_dir_name = 'sources' | |
others_dir = None | |
def __init__(self, main=None, others=None): | |
# Add main repository | |
if main and isinstance(main, Repository): | |
self.main = main | |
if others and isinstance(others, (list, tuple)): | |
for other in others: | |
self.others.append(other) | |
self.root = os.getcwd() | |
def build(self): | |
''' launchs all build steps sequencialy ''' | |
# make paths | |
self.make_paths() | |
# clone repositories | |
self.clone_repos() | |
# apply patchs | |
self.apply_patchs() | |
# copy additions | |
self.copy_additions() | |
# create symlinks | |
self.make_symlinks() | |
# install repositories | |
self.install_repos() | |
def make_paths(self): | |
''' creates sources placeholder ''' | |
print "Creating paths" | |
# make folder for others | |
try: | |
os.mkdir(self.others_dir_name) | |
except OSError, e: | |
if e.errno == 17: | |
print " Path %s already exists." % self.others_dir_name | |
pass | |
else: | |
raise | |
# store repo folders | |
self.others_dir = os.path.join(self.root, self.others_dir_name) | |
self.main_dir = os.path.join(self.root, self.main.name) | |
def make_symlinks(self): | |
''' creates symlinks in main repo to others' apps ''' | |
main_apps_dir = os.path.join(self.main_dir, 'apps') | |
for rep in self.others: | |
if rep.apps.__len__() == 0: | |
continue | |
print " Linkings apps from %s" % rep.name | |
rep_dir = os.path.join(self.others_dir, rep.name) | |
apps_dir = os.path.join(rep_dir, 'apps') | |
for app in rep.apps: | |
app_dir = os.path.join(apps_dir, app) | |
dst_dir = os.path.join(main_apps_dir, app) | |
try: | |
os.symlink(app_dir, dst_dir) | |
except OSError, e: | |
# simlink exist | |
if e.errno == 17: | |
print " symlink %s exists." % app | |
else: | |
raise | |
def clone_repos(self): | |
''' clones referenced repositories ''' | |
repos = copy.copy(self.others) | |
repos.append(self.main) | |
for rep in repos: | |
print "Entering repository %s" % rep.name | |
init_dir = os.getcwd() | |
# move to others folder | |
if isinstance(rep, FeederRepository): | |
os.chdir(self.others_dir) | |
# update repo if exists | |
if not os.path.exists(rep.name): | |
print " Cloning git repository" | |
os.system("git clone %(url)s %(dir)s" % {'url': rep.url, \ | |
'dir': rep.name}) | |
else: | |
print " repository %s exists." % rep.name | |
print " Updating repository" | |
GitCommander.update_repo(os.path.join(os.getcwd(), rep.name), \ | |
rep.get_rev(), rep.tag, rep.branch) | |
# move back | |
os.chdir(init_dir) | |
def install_repos(self): | |
for rep in self.others: | |
if rep.install: | |
folder = os.path.join(self.others_dir, rep.name) | |
GitCommander.install(folder) | |
def apply_patchs(self): | |
for rep in self.others: | |
if rep.patch and rep.patch_target: | |
target = self.repo_by_ident(rep.patch_target) | |
if target == None: | |
print " Error with patch localization." | |
continue | |
folder = os.path.join(self.others_dir, target.name) | |
rep_dir = os.path.join(self.others_dir, rep.name) | |
GitCommander.patch(folder, os.path.join(rep_dir, rep.patch)) | |
def copy_additions(self): | |
for rep in self.others: | |
if rep.fcopy and rep.fcopy_target and rep.fcopy_ftarget: | |
target = self.repo_by_ident(rep.fcopy_target) | |
if target == None: | |
print " Error with filecopy location." | |
continue | |
folder = os.path.join(self.others_dir, target.name) | |
rep_dir = os.path.join(self.others_dir, rep.name) | |
GitCommander.copy(rep_dir, rep.fcopy, os.path.join(folder, rep.fcopy_ftarget)) | |
@classmethod | |
def install(cls, folder): | |
init_dir = os.getcwd() | |
os.chdir(folder) | |
print " Installing repository at %s" % folder | |
os.system("sudo python ./setup.py install") | |
os.chdir(init_dir) | |
@classmethod | |
def patch(cls, folder, patch): | |
init_dir = os.getcwd() | |
os.chdir(folder) | |
print " Patching repository at %s" % folder | |
os.system("patch -p1 < %s" % patch) | |
os.chdir(init_dir) | |
@classmethod | |
def copy(cls, folder, source, target): | |
init_dir = os.getcwd() | |
os.chdir(folder) | |
print " Copy %s at %s" % (source, target) | |
os.system("cp -rv %s %s/" % (source, target)) | |
os.chdir(init_dir) | |
@classmethod | |
def update_repo(cls, folder, rev, tag=None, branch=None): | |
''' pulls new changes from a git repository ''' | |
init_dir = os.getcwd() | |
os.chdir(folder) | |
# select branch if applicable | |
if branch: | |
os.system("git checkout --track -b %(branch)s origin/%(branch)s" \ | |
% {'branch': branch}) | |
os.system("git pull --tags --keep %(rev)s" % {'rev': rev}) | |
if tag: | |
os.system("git checkout %(tag)s" % {'tag': tag}) | |
os.chdir(init_dir) | |
def repo_by_ident(self, ident): | |
for rep in self.others: | |
if rep.ident == ident: | |
return rep | |
return None | |
class GitConfig(ConfigParser): | |
''' configures respositories from a config file ''' | |
def __init__(self, path=None): | |
ConfigParser.__init__(self) | |
self.repos = [] | |
self.main_repo = None | |
if path: | |
self.readfp(open(path)) | |
self.config_repos() | |
def config_repos(self): | |
''' configures reposotory objects based on config ''' | |
for repo_name in self.sections(): | |
ismain = repo_name == 'main' | |
repo = self.config_repo(repo_name, ismain) | |
if ismain: | |
self.main_repo = repo | |
else: | |
self.repos.append(repo) | |
def config_repo(self, ident, main=False): | |
''' configure a named repository ''' | |
# url is mandatory | |
url = self.get(ident, 'url') | |
# get name ; if none, default to section name | |
try: | |
name = self.get(ident, 'name') | |
except NoOptionError: | |
name = ident | |
# revision is optional | |
try: | |
rev = self.getint(ident, 'rev') | |
except ValueError: | |
rev = self.get(ident, 'rev') | |
except: | |
rev = 'HEAD' | |
# install is optional | |
try: | |
install = self.getboolean(ident, 'install') | |
except NoOptionError: | |
install = False | |
# apps is optional (feeder only) | |
try: | |
apps = self.get(ident, 'apps') | |
apps = apps.replace(' ','').split(',') | |
except: | |
apps = [] | |
# patch is optional | |
try: | |
patch = self.get(ident, 'patch') | |
patch_target, patch = patch.replace(' ','').split(',') | |
except: | |
patch = None | |
patch_target = None | |
# fcopy is optional | |
try: | |
fcopy = self.get(ident, 'filecopy') | |
fcopy_target, fcopy, fcopy_ftarget = fcopy.replace(' ','').split(',') | |
except: | |
fcopy = None | |
fcopy_target = None | |
fcopy_ftarget = None | |
if main: | |
repo = Repository(url=url, name=name) | |
else: | |
repo = FeederRepository(url=url, name=name) | |
repo.ident = ident | |
repo.rev = rev | |
repo.install= install | |
repo.patch = patch | |
repo.patch_target = patch_target | |
repo.fcopy = fcopy | |
repo.fcopy_target = fcopy_target | |
repo.fcopy_ftarget = fcopy_ftarget | |
try: | |
repo.apps = apps | |
except: | |
pass | |
return repo | |
def usage(me): | |
print u"Usage: %s [-c file, --config=file, -t path, --target=path] \n\n \ | |
\ | |
-c, --config= Use provided configuration file \n \ | |
-t, -target= Use provided path as home for repositories \ | |
" % me | |
def main(): | |
config_file = 'install.ini' | |
target = os.getcwd() | |
try: | |
opts, args = getopt.getopt(sys.argv[1:], "hc:t:", ["help", "config=","target="]) | |
except getopt.GetoptError: | |
usage(sys.argv[0]) | |
sys.exit(2) | |
for o, a in opts: | |
if o in ("-h", "--help"): | |
usage(sys.argv[0]) | |
sys.exit() | |
elif o in ("-c", "--config"): | |
config_file = a | |
elif o in ("-t", "--target"): | |
target = a | |
else: | |
assert False, "Unhandled option" | |
if not os.path.exists(config_file) or not os.path.exists(target): | |
print "Error. File does not exist." | |
sys.exit(1) | |
# read config file | |
config = GitConfig(config_file) | |
# build folder and clones and everything | |
commander = GitCommander(main=config.main_repo, others=config.repos) | |
commander.build() | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment