-
Notifications
You must be signed in to change notification settings - Fork 21
/
fabfile.py
407 lines (340 loc) · 11.6 KB
/
fabfile.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
"""
Fabfile for deploying and setting up code that looks like the production
environment. it also makes it easy to start up the servers
If you want to run on the localhost you may need to first do::
rm -rf ~/.ssh/known_hosts
"""
from __future__ import with_statement
import os
import re
from fabric.api import local, settings, abort, run , cd, env, lcd, sudo, prompt
from fabric.contrib.console import confirm
from fabric.contrib import files
env.roledefs = {'local':['localhost']}
env.use_ssh_config=True
TAG_REGEX = re.compile('^[0-9]+\.[0-9]+\.[0-9]+')
STABLE_MSG = '**stable**'
LINK_CODE_DIR = os.path.split(os.path.abspath(__file__))[0]
def dir_code_base():
"""
If you are using any localhost then it will use the current directory.
Otherwise you will use the code_dir
"""
if 'localhost' in env.host_string:
return os.getcwd()
return code_dir
def dir_scripts():
"""
The directory where you house all the scripts
"""
return '%s/scripts' % (dir_code_base())
config_dir = '~/.link'
def test_install():
import os
#set the link dir to something silly
os.environ['LNK_DIR']='saodusah'
#create a virtual environment
local('echo $LNK_DIR')
local('virtualenv env')
#remove everything from the build directory
local('rm -rf build')
#run this and see that it works
local('source env/bin/activate && python setup.py install')
def configure():
"""
Create the base configuration so that you can change it. Might want to
include the configuration in a different repo
"""
if not files.exists(config_dir):
run('mkdir %s' % config_dir)
lnk_config = '%s/link.config' % config_dir
if not files.exists(lnk_config):
run('touch %s' % lnk_config)
def script(script_name, command = 'python', **args):
"""
Will run the script that is in the scripts folder. you can pass in a
dictionory of args and it will pass it through to the script as command line
args in this format
fab -R local script:example.py,arg1=value1,arg2=value2
that will result in running this command
<command> <scripts_directory>/<scriptname> --arg1=value1 --arg2=value2
"""
with cd(dir_scripts()):
parameters = ''
if args:
parameters = ' '.join(['--%s=%s' % (key, value) for key,value in
args.iteritems()])
run("%s %s %s" % (command , script_name, parameters))
def commit(msg=None):
"""
Commit your changes to git
:msg: @todo
:returns: @todo
"""
print '---Commiting---'
print
msg = msg or prompt('Commit message: ')
commit = False
commit = prompt('Confirm commit? [y/n]') == 'y'
if commit:
with settings(warn_only=True):
_commit = not local('git commit -a -m "%s"' % msg).failed
if not _commit:
#nothing was committed
commit = False
print "Nothing to commit"
else:
abort('commit aborted')
print
print '---Done---'
return commit
def tag_names(number = 10, stable=False):
number = int(number)
print "fetching tags first"
local('git fetch --tags ')
print "Showing latest tags for reference"
tags = local('git tag -n1 ', capture = True)
tags = [x for x in tags.split('\n') if TAG_REGEX.findall(x) and
(not stable or STABLE_MSG in x)]
tags.sort(reverse=True)
#take the first <number> things in the list
tags = tags[0:min(len(tags), number)]
print '\n'.join(tags)
print
return tags
def check_tag_format(tag):
"""
Checks the tag format and returns the component parts
"""
parsed = tag.split('.')
try:
#allow for at most 2 minor decimals...i mean comeon
major = int(parsed[0])
minor = int(parsed[1])
build = int(parsed[2][0:2])
return (major, minor, build)
except Exception as e:
print e
abort("""Must be of the form <major_version>.<minor>.<maintence>, like
0.0.1. Only integers allowed""")
def write_version(version):
"""
Write out the version python file to the link directory before installing
version needs to be a list or tuple of the form (<major>, <minor>, <build>)
or a string in the format <major>.<minor>.<build> all ints
"""
file_name ='link/__init__.py'
init = open(file_name)
init_read = init.readlines()
init.close()
version_line = [idx for idx, x in enumerate(init_read) if '__version__ = ' in x]
if len(version_line)>1:
raise Exception('version is in there more than once')
if isinstance(version, str):
try:
version_split = map(int, version.split('.'))
except:
raise Exception("Version string must be in the format <major>.<minor>.<build>")
if not isinstance(version_split, (list, tuple)) or len(version_split)!=3:
raise Exception('invalid version %s' % version)
init_read[version_line[0]] = "__version__ = '%s'\n" % version
init = open(file_name, 'w')
try:
init.write(''.join(init_read))
finally:
init.close()
def prompt_for_tag(default_offset=1, stable_only = False):
"""
Prompt for the tag you want to use, offset for the default by input
"""
tags = tag_names(10, stable_only)
print "Showing latest tags for reference"
default = '0.0.1'
if tags:
default = tags[0]
(major, minor, build) = check_tag_format(default)
build = build+default_offset
new_default = '%s.%s.%s' % (major, minor, build)
tag = prompt('Tag name [in format x.xx] (default: %s) ? ' % new_default)
tag = tag or new_default
return tag
def push_to_pypi():
"""
Will push the code to pypi
"""
if prompt('would you like to tag a new version first [y/n]') == 'y':
tag()
local('python setup.py sdist upload')
def prompt_commit():
"""
prompts if you would like to commit
"""
local('git status')
print
print
_commit = prompt('Do you want to commit? [y/n]') == 'y'
if _commit:
msg = prompt('Commit message: ')
return commit(msg)
def tag(mark_stable=False):
"""
Tag a release, will prompt you for the tag version. You can mark it as
stable here as well
"""
tag = prompt_for_tag()
print "writing this tag version to version.py before commiting"
write_version(tag)
print
_commit = prompt_commit()
print
if not _commit and not tag:
print
print "Nothing commited, using default tag %s" % default
print
tag = default
else:
msg = ''
if mark_stable:
msg = STABLE_MSG + ' '
msg += prompt("enter msg for tag: ")
local('git tag %(ref)s -m "%(msg)s"' % { 'ref': tag, 'msg':msg})
local('git push --tags')
return tag
def merge(branch=None, merge_to = 'master'):
"""
Merge your changes and delete the old branch
"""
if not branch:
print "no branch specified, using current"
branch = current_branch()
if prompt('confirm merge with of branch %s to %s [y/N]' % (branch, merge_to)) == 'y':
prompt_commit()
local('git checkout %s ' % merge_to)
local('git merge %s ' % branch)
if prompt('delete the old branch locally and remotely? [y/N]') == 'y':
local('git branch -d %s' % branch)
local('git push origin :%s' % branch)
else:
print "leaving branch where it is"
if prompt('push results [y/N]' ) == 'y':
local('git push')
def tag_deploy(mark_stable=False):
"""
Asks you to tag this release and Figures out what branch you are on.
It then calls the deploy function
"""
local('git fetch --tags')
branch = local('git branch | grep "^*" | cut -d" " -f2', capture=True)
_tag = tag(mark_stable=mark_stable)
deploy(_tag, branch)
def retag(tag, msg):
"""
Retag a tag with a new message
"""
local('git tag %s %s -f -m "%s"' % (tag, tag, msg))
local('git push --tags')
def mark_stable(tag, msg = None):
"""
Mark a previous tag as stable
"""
retag(tag, '%s %s' % (STABLE_MSG, msg) )
def current_branch():
current_branch = local('git branch | grep "^*"', capture=True).lstrip('* ')
print "Current branch is %s" % current_branch
return current_branch
def deploy(tag=None, branch=None, stable_only=False):
"""
This is only for deployment on a dev box where everything can be owned by
this user. This is NOT for production deployment. Put's the code in
code_dir
"""
if not tag:
tag = prompt_for_tag(0, stable_only = stable_only)
configure()
setup_environment()
#check out all the code in the right place
with cd(code_dir):
# i **THINK** you have to have the branch checked out before you can
# checkout the tag
if branch:
#then you haven't even checkout this branch
branches = run('git branch')
if branch not in branches:
run('git checkout -b %s' % branch)
_current_branch = current_branch()
if "* %s" % branch != _current_branch:
run('git checkout %s' % branch)
#pull the latest
run('git pull origin %s' % branch)
else:
run("git pull origin master")
#check out a specific tag
if tag:
run("git fetch --tags")
run("git checkout %s" % tag)
#hacky
if env.user == 'root':
#make sure everything is still owned by the deployer
run('chown -R %s %s' % (deploy_user, code_dir))
###
# How to setup a fresh box. You probably have to run this as root for it to
# work
###
def install_easy_install():
"""
Installs setup tool, this should also go into an RPM
"""
run('wget http://pypi.python.org/packages/2.7/s/setuptools/setuptools-0.6c11-py2.7.egg#md5=fe1f997bc722265116870bc7919059ea')
run('sh setuptools-0.6c11-py2.7.egg')
def install_python():
"""
Installs python, I should be able to create an RPM eventually
"""
run('wget http://python.org/ftp/python/2.7.2/Python-2.7.2.tgz')
run('tar -xvf Python-2.7.2.tgz')
with cd('Python-2.7.2'):
run('./configure')
run('make')
run('make install')
###
# This isn't reall necessary but i'll keep it for now
###
def install_python_dependancies():
"""
Easy install all the packages we need
"""
run('easy_install requests')
run('easy_install numpy')
run('easy_install pandas')
run('easy_install happybase')
run('easy_install flask')
run('easy_install ipython')
run('easy_install gunicorn')
run('easy_install link')
run('easy_install pymongo')
run('easy_install mysql-python')
run('easy_install docutils')
def install_box_libraries():
"""
Installs the libs you need like readlines and libsqlite. This will only
run on a ubuntu machine with apt-get
"""
with settings(warn_only=True):
has_apt = run('which apt-get')
if has_apt:
run('apt-get install make')
run('apt-get install libsqlite3-dev')
run('apt-get install libreadline6 libreadline6-dev')
run('apt-get install libmysqlclient-dev')
else:
print "this is not an ubuntu system...skipping"
def setup_box():
"""
Will install python and all libs needed to set up this box to run the
examjam code. Eventually this needs to be more RPM based
"""
#place_pub_key()
install_box_libraries()
install_python()
install_easy_install()
install_python_dependancies()