.-'"'-. / `. ,' \ | ,' `. | | ___ | \ ( . ) / '-.:.-' .:. ::: ::: ::. ':: '
This is mostly an experimental tool, built out of a growing displeasure having to work with existing similar softwares, and for the sole purpose of fitting our specific build needs.
While giving puke an OSS license made sense, while the author is happy to work on it, and while it might suit you, it's quite likely that it won't, and that you would find yourself more satisfied with one of the other solution out there (rake and scons come to mind).
We are using python, and a homemade system named "puke". It's quite similar to rake, jake, jasy, scons, etc, except:
- it doesn't suck ass, unlike ruby
- it installs painless, unlike ruby
- it's extremely straightforward and does just a couple of simple things, avoiding both bloat and indigest documentation
- it's python, so it's cool and sexy, unlike ruby :)
Basic file manipulation, js linting via closure, minification via closure and YUI, scss parser, js documentation via jsdoctoolkit, VirtualEnv creation with package installation, system package check.
- Require.merge() makes now a deep merge
- sh() can take now multiple commandes : sh(['cd somepath', 'do something'])
- prompt('message', 'default') to prompt during the build
- New FileSystem api
- New System api
- New Utils api
- New VirtualEnv api
- patch('dir/to/patch', patchfile) (Supports unix patch format only : man patch)
- Task parameters (puke task arg1 arg2)
- Task infos (puke task -i docstring style)
- few fixes
There is two ways to get there.
Either the recommended sandboxed way (using brew, for MacOSX), read 1.
Or the "system" way (MacOSX and Linux), read 2.
If you don't understand what I'm saying, you are on mac, so just follow 1. If you do, then you already have a working python + easy_install environment, right? Move on to step 3.
# Install XCode: http://developer.apple.com/technologies/xcode.html # Install brew: sudo mkdir /usr/local sudo chmod g+rwx /usr/local sudo chown root:staff local /usr/bin/ruby -e "$(curl -fsSL https://raw.github.com/gist/323731)" # Install python with brew: brew install python # Update your .profile so that the brew python is used echo 'export PATH="/usr/local/share/python:/usr/local/bin:/usr/local/sbin:$PATH"' >> ~/.profile source ~/.profile # Double-check that the sandboxed python is used which easy_install # Should output/usr/local/share/python/easy_install which python # Should output /usr/local/bin/python
Now go to step three.
You have nothing special to do, but you will need to be root in some of the following steps.
Go to step three now.
If on Mac:
easy_install pip
If on Linux, just make sure you got pip:
# If debian based... sudo aptitude install python-pip # or do it whatever way pleases you
# Get some puke on you pip install --upgrade puke
Whenever you want to upgrade to the latest version, just do it again
pip install puke --upgrade
Yeah, that's it.
- some puke functionalities require a working jre. Lion has one by default (does it?). Ubuntu fans might just do a:
sudo aptitude install openjdk-7-jre
- if you happen to have libyaml-dev installed, you need to have python-dev as well. This is not a puke requirement, but a pyyaml gotcha actually.
First time puke runs, it does patch some internal dependency (closure). So, IN THE CASE YOU USED THE SYSTEM WAY, do this after install:
sudo puke --patch
Don't forget to do that (yet again, only if you installed puke as ROOT) every time you install.
That's it.
You pretty much create a puke file, which is a very simple python script that describes build tasks for your project. You may name it either "pukefile", or "pukefile.py".
Then you just call "puke someTaskName".
Help me puke :
$ puke --help Usage: puke [options] Options: -h, --help show this help message and exit -c, --clear Spring time, clean all the vomit -q, --quiet don't print status messages to stdout -v, --verbose print more detailed status messages to stdout -t, --tasks list tasks -l LOGFILE, --log=LOGFILE Write debug messages to given logfile -f FILE, --file=FILE Use the given build script -p, --patch Patch closure
Can't remember your tasks ? Just puke it
$ puke --tasks No tasks to execute. Please choose from: test: coin coin simple: Simple Test lint: lint doc: Documentation gate: Build gate package
#!/usr/bin/env puke # -*- coding: utf8 -*-
Name your task "default" in order to have it executed by simply puke-ing
@task("Simple Test") def simple(): console.log("Do something")
| pukefile.py | helpers.py => helpers.py from puke import * def test(): f = FileList('some/path') print "success" => pukefile.py import helpers helpers.test()
executeTask('simple')
@task("") def default(): executeTask('simple') @task("Simple Test") def simple(): console.log("Do something")
Your pukefile
@task('with args') def test(required, optional = 'default'): """Python docstring style comment""" print "Required : %s" % required print "Optional : %s" % optional
Execute puke
puke test value >>> Required : value >>> Optional : default
Need help with your params ?
puke test --info >>> ------------------------------------- >>> * Help test (task description) >>> ------------------------------------- >>> Help on function test in module puke: >>> >>> test(required, optional = 'default') >>> Python docstring style comment
r = Require('global.yaml') r.merge('build.yaml')
Working with environment variables (Yaml example):
params: # envvar name | default build_dir: "${BUILD_DIR}|/usr/toto/build/" string: "toto"
Yak it and make the puke easier!
r = Require('global.yaml') r.yak('params') @task("Simple Test") def simple(): console.log("Easy to get my conf", Yak.build_dir, Yak.string) #Check if your param exists if 'build_dir' in Yak: print "yes"
Env.get('BUILD_DIR', 'default')
# Info levels console.info("info", arg2, ...) console.confirm("confirm", arg2, ...) console.log("log", arg2, ...) console.debug("debug", arg2, ...) console.warn("warn", arg2, ...) console.error("error", arg2, ...) console.header("header") console.fail("fail")
#prompt(message,default) answer = prompt('How are you doing ?', 'fuck off')
list = FileList("foldername", filter = "*.js", exclude = "*.min.js")` # multicriteria list2 = FileList("src", filter = "*.css,*.scss") #merge lists list.merge(list2)
or
list = ["somefilepath", "someother", "http://example.com/something"]
Note that http:// urls are supported
Note that SCSS are automatically parsed
combine(list, "build/test.js")
deepcopy(list, 'build/copy/')
minify("build/test.js", "build/test.min.js")
patch('dir/to/patch', patchfile)
Packing :
#zip pack(list, "folder/something.zip") #gz pack(list, "folder/something.tar.gz")
Unpacking :
#zip unpack('folder/something.zip', 'folder/test-unpack/') #gz unpack('build/something.tar.gz', 'folder/test-unpack/')
sh("pwd")
#get output pwd = sh("pwd") #multiple commands sh(['cd somepath', 'do something])
stats(list, title = "JS Stats")
- JS stats : ~ Files : 65 ~ Lines : 23477 (361 per file) ~ Size : 1.8KB (28.0bytes per file)
sed = Sed() sed.add('$TOTO$', 'troulute') combine(list, "build/test.js", replace = sed) deepcopy(list, "build/test.js", replace = sed)
Using jslint (see linting for more):
jslint(list, fix = False, relax = False, fail = True)
Using jsdoctoolkit (see documenting javascript for more):
jsdoc(list, "docdestination", [template = "templatepath"])
$ puke -c Spring time, cleaning all the vomit around ... ... You're good to go !
Creates missing hierarchy levels for given directory
FileSystem.makedir('somepath/to/dir')
Get file content
FileSystem.readfile('path/to/file')
Remove file or dir (recursively)
#With protection if you're trying to remove : './', '/', '~/', '~', '.', '..', '../' FileSystem.remove('something')
Copy a file
#creates dirs if dst doesn't exist #Does the copy only if the file doesn't exist or if the file has been modified #To force to copy anyway, use force = True FileSystem.copyfile(src, dst)
Create a file and write your content
FileSystem.writefile('file', 'content')
Check if the path exists
FileSystem.exists('path')
Check if the path is a file
FileSystem.isfile('somefile')
Check if the path is a dir
FileSystem.path('somedir')
Get an OS path (eg : build/lib/folder on Unix)
FileSystem.join('build', 'lib','folder')
Get the absolute path
FileSystem.abspath('./')
Get file name
FileSystem.basename('/path/to/toto.py') >>> 'toto.py'
Check platform
if System.OS == System.MACOS: #do something macos related elif System.OS == System.LINUX: #do something linux related elif System.OS == System.WINDOWS: #Achtung windows ...
Get user login
#Return None if something went wrong System.LOGIN
Check if a system package is here :
System.check_package('nginx') >>> nginx is M.I.A >>> => "brew install nginx" >>> /!\ BUILD FAIL : nginx not installed #Check version too (>= <= > < ==) System.check_package('varnish', '>=3.0.1') >>> varnish : INSTALLED (3.0.0 not >=3.0.1) >>> * Continue anyway ? [Y/N default=Y] >>> N >>> /!\ BUILD FAIL : Failed on version comp varnish #Only check on specific platform (System.LINUX, System.MACOS, System.LINUX, "all") System.check_package('libcaca', platform=System.LINUX)
Get package version
System.get_package_version('uwsgi') >>> "0.9.9"
Create
env = VirtualEnv() env.create('path/to/create/env', python='python3|python|python2.7|...') >>> * Creating env "test" ... >>> virtualenv : OK (1.6.4) >>> Python version : 3.2.2 ... >>> Env "test" created
Load an existing virtualenv
env = VirtualEnv() env.load('test')
install package
env.install('webob') >>> * Install "webob" in env "test" ... >>> Package "webob" is ready
install package in a specific version
env.install('webob', '1.1.1') >>> * Install "webob" in env "test" ... >>> Package "webob" is ready
install / force upgrade if installed
env.install('webob', upgrade=True)
upgrade package / all packages
env.upgrade('webob') env.upgrade('*')
Check package / auto fix
#check if the named package respect the version env.check_package('webob', '>=1.1.1') #check it and fix if the cond fails (make an install or upgrade or downgrade) env.check_package('webob', '>=1.1.1', fix = True)
List packages
env.list() >>> * List packages (env "test") >>> - PIL (1.1.6) >>> - PyYAML (3.10) >>> - WebOb (1.2b2)
Package info
env.package_info('webob')
Remove env
env.remove()
Merge two deep dicts non-destructively
a = {'a': 1, 'b': {1: 1, 2: 2}, 'd': 6} b = {'c': 3, 'b': {2: 7}, 'd': {'z': [1, 2, 3]}} Utils.deepmerge(a, b) >>> {'a': 1, 'b': {1: 1, 2: 7}, 'c': 3, 'd': {'z': [1, 2, 3]}}
MIT license, see LICENSE file
Nope.
Don't puke on yourself!