@@ -2,6 +2,7 @@ | |||||
"core.min.js": [ | "core.min.js": [ | ||||
"wn/provide.js", | "wn/provide.js", | ||||
"wn/xmlhttp.js", | "wn/xmlhttp.js", | ||||
"wn/versions.js", | |||||
"wn/assets.js", | "wn/assets.js", | ||||
"wn/require.js", | "wn/require.js", | ||||
"wn/dom.js", | "wn/dom.js", | ||||
@@ -1,3 +1,5 @@ | |||||
// find files changed since last version | |||||
wn.versions.check(); | |||||
// load all critical libraries | // load all critical libraries | ||||
wn.require("lib/js/lib/jquery.min.js"); | wn.require("lib/js/lib/jquery.min.js"); | ||||
@@ -5,9 +5,14 @@ parent=parent[n];}} | |||||
wn.provide('wn.settings');wn.provide('wn.ui');wn.xmlhttp={request:function(){if(window.XMLHttpRequest) | wn.provide('wn.settings');wn.provide('wn.ui');wn.xmlhttp={request:function(){if(window.XMLHttpRequest) | ||||
return new XMLHttpRequest();else if(window.ActiveXObject) | return new XMLHttpRequest();else if(window.ActiveXObject) | ||||
return new ActiveXObject("MsXml2.XmlHttp");},complete:function(req,callback,url){if(req.status==200||req.status==304){callback(req.responseText);}else{alert(url+' request error: '+req.statusText+' ('+req.status+')');}},get:function(url,callback,args,async){if(async===null)async=true;var req=wn.xmlhttp.request();req.onreadystatechange=function(){if(req.readyState==4){wn.xmlhttp.complete(req,callback,url)}} | return new ActiveXObject("MsXml2.XmlHttp");},complete:function(req,callback,url){if(req.status==200||req.status==304){callback(req.responseText);}else{alert(url+' request error: '+req.statusText+' ('+req.status+')');}},get:function(url,callback,args,async){if(async===null)async=true;var req=wn.xmlhttp.request();req.onreadystatechange=function(){if(req.readyState==4){wn.xmlhttp.complete(req,callback,url)}} | ||||
req.open('GET',url,async);req.send(args?url+'?'+args:null);if(!async){wn.xmlhttp.complete(req,callback,url)}}} | |||||
wn.assets={executed_:{},exists:function(src){if('localStorage'in window&&localStorage.getItem(src)&&localStorage.getItem('[ts] '+src)==asset_timestamps_[src]) | |||||
return true},add:function(src,txt){if('localStorage'in window){localStorage.setItem(src,txt);localStorage.setItem('[ts] '+src,asset_timestamps_[src]);}},get:function(src){return localStorage.getItem(src);},extn:function(src){return src.split('.').slice(-1)[0];},html_src:function(src){if(src.indexOf('/')!=-1){var t=src.split('/').slice(0,-1);t.push('src');t=t.join('/')+'/'+a.split('/').slice(-1)[0];}else{var t='src/'+src;} | |||||
var u=args?(url+'?'+args):url;req.open('GET',u,async);req.send(null);if(!async){wn.xmlhttp.complete(req,callback,url)}}} | |||||
wn.versions={is_latest:function(){if(window._version_number==(localStorage?localStorage['_version_number']:null)){return true;} | |||||
return false;},get_diff:function(){if(!localStorage)return;wn.xmlhttp.get('index.cgi',function(txt){r=JSON.parse(txt);if(r.exc){alert(r.exc);} | |||||
wn.versions.set(r.message);},'cmd=get_diff&version_number='+localStorage['_version_number'],false);},set:function(diff){for(var i=0;i<diff.length;i++){localStorage.removeItem(diff[i]);} | |||||
localStorage['_version_number']=_version_number;},check:function(){if(localStorage&&!localStorage['_version_number']){localStorage['_version_number']=_version_number;return;} | |||||
if(!wn.versions.is_latest())wn.versions.get_diff();}} | |||||
wn.assets={executed_:{},exists:function(src){if('localStorage'in window&&localStorage.getItem(src)) | |||||
return true},add:function(src,txt){if('localStorage'in window){localStorage.setItem(src,txt);}},get:function(src){return localStorage.getItem(src);},extn:function(src){return src.split('.').slice(-1)[0];},html_src:function(src){if(src.indexOf('/')!=-1){var t=src.split('/').slice(0,-1);t.push('src');t=t.join('/')+'/'+a.split('/').slice(-1)[0];}else{var t='src/'+src;} | |||||
return t;},load:function(src){var t=wn.assets.extn(src)=='html'?wn.assets.html_src(src):src;wn.xmlhttp.get(t,function(txt){wn.assets.add(src,txt);},'q='&Math.floor(Math.random()*1000),false)},execute:function(src){if(!wn.assets.exists(src)){wn.assets.load(src);} | return t;},load:function(src){var t=wn.assets.extn(src)=='html'?wn.assets.html_src(src):src;wn.xmlhttp.get(t,function(txt){wn.assets.add(src,txt);},'q='&Math.floor(Math.random()*1000),false)},execute:function(src){if(!wn.assets.exists(src)){wn.assets.load(src);} | ||||
var type=wn.assets.extn(src);if(wn.assets.handler[type]){wn.assets.handler[type](wn.assets.get(src),src);wn.assets.executed_[src]=1;}},handler:{js:function(txt,src){wn.dom.eval(txt);},css:function(txt,src){var se=document.createElement('style');se.type="text/css";if(se.styleSheet){se.styleSheet.cssText=txt;}else{se.appendChild(document.createTextNode(txt));} | var type=wn.assets.extn(src);if(wn.assets.handler[type]){wn.assets.handler[type](wn.assets.get(src),src);wn.assets.executed_[src]=1;}},handler:{js:function(txt,src){wn.dom.eval(txt);},css:function(txt,src){var se=document.createElement('style');se.type="text/css";if(se.styleSheet){se.styleSheet.cssText=txt;}else{se.appendChild(document.createTextNode(txt));} | ||||
document.getElementsByTagName('head')[0].appendChild(se);},html:function(txt,src){var page=wn.dom.add($('.outer .inner').get(0),'div','content',null,txt);page.setAttribute("_src",src);}}} | document.getElementsByTagName('head')[0].appendChild(se);},html:function(txt,src){var page=wn.dom.add($('.outer .inner').get(0),'div','content',null,txt);page.setAttribute("_src",src);}}} | ||||
@@ -52,4 +57,4 @@ return reviver.call(holder,key,value);} | |||||
text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+ | text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+ | ||||
('0000'+a.charCodeAt(0).toString(16)).slice(-4);});} | ('0000'+a.charCodeAt(0).toString(16)).slice(-4);});} | ||||
if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;} | if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;} | ||||
throw new SyntaxError('JSON.parse');};}}());wn.require("lib/js/lib/jquery.min.js");wn.require("lib/js/lib/history/history.min.js");$(document).bind('ready',function(){var base=window.location.href.split('#')[0];$.each($('a[softlink!="false"]'),function(i,v){if(v.href.substr(0,base.length)==base){var path=(v.href.substr(base.length));if(path.substr(0,1)!='#'){v.href=base+'#'+path;}}});if(!wn.settings.no_history&&window.location.hash){wn.page.set(window.location.hash.substr(1));}}); | |||||
throw new SyntaxError('JSON.parse');};}}());wn.versions.check();wn.require("lib/js/lib/jquery.min.js");wn.require("lib/js/lib/history/history.min.js");$(document).bind('ready',function(){var base=window.location.href.split('#')[0];$.each($('a[softlink!="false"]'),function(i,v){if(v.href.substr(0,base.length)==base){var path=(v.href.substr(base.length));if(path.substr(0,1)!='#'){v.href=base+'#'+path;}}});if(!wn.settings.no_history&&window.location.hash){wn.page.set(window.location.hash.substr(1));}}); |
@@ -1,19 +1,17 @@ | |||||
// library to mange assets (js, css, models, html) etc in the app. | // library to mange assets (js, css, models, html) etc in the app. | ||||
// will try and get from localStorge if latest are available | // will try and get from localStorge if latest are available | ||||
// or will load them via xmlhttp | // or will load them via xmlhttp | ||||
// depends on asset_timestamps_ loaded via boot | |||||
// depends on wn.versions to manage versioning | |||||
wn.assets = { | wn.assets = { | ||||
// keep track of executed assets | // keep track of executed assets | ||||
executed_ : {}, | executed_ : {}, | ||||
// check if the asset exists in | // check if the asset exists in | ||||
// localstorage and if the timestamp | |||||
// matches with the loaded timestamp | |||||
// localstorage | |||||
exists: function(src) { | exists: function(src) { | ||||
if('localStorage' in window | if('localStorage' in window | ||||
&& localStorage.getItem(src) | |||||
&& localStorage.getItem('[ts] '+src) == asset_timestamps_[src]) | |||||
&& localStorage.getItem(src)) | |||||
return true | return true | ||||
}, | }, | ||||
@@ -22,7 +20,6 @@ wn.assets = { | |||||
add: function(src, txt) { | add: function(src, txt) { | ||||
if('localStorage' in window) { | if('localStorage' in window) { | ||||
localStorage.setItem(src, txt); | localStorage.setItem(src, txt); | ||||
localStorage.setItem('[ts] ' + src, asset_timestamps_[src]); | |||||
} | } | ||||
}, | }, | ||||
@@ -0,0 +1,42 @@ | |||||
// manage app versioning | |||||
// get the last_version_number from the server (loaded) | |||||
// and update based on it | |||||
wn.versions = { | |||||
is_latest: function() { | |||||
if(window._version_number == (localStorage ? localStorage['_version_number'] : null)) { | |||||
return true; | |||||
} | |||||
return false; | |||||
}, | |||||
// get the change list of all files | |||||
// from current version and local version | |||||
get_diff: function() { | |||||
if(!localStorage) return; | |||||
wn.xmlhttp.get('index.cgi', function(txt) { | |||||
// add it to localstorage | |||||
r = JSON.parse(txt); | |||||
if(r.exc) { alert(r.exc); } | |||||
wn.versions.set(r.message); | |||||
}, 'cmd=get_diff&version_number=' + localStorage['_version_number'], false); | |||||
}, | |||||
// set will clear all changes since the last update | |||||
set: function(diff) { | |||||
for(var i=0; i<diff.length; i++) { | |||||
localStorage.removeItem(diff[i]); | |||||
} | |||||
localStorage['_version_number'] = _version_number; | |||||
}, | |||||
check: function() { | |||||
if(localStorage && !localStorage['_version_number']) { | |||||
// first load | |||||
localStorage['_version_number'] = _version_number; | |||||
return; | |||||
} | |||||
if(!wn.versions.is_latest()) wn.versions.get_diff(); | |||||
} | |||||
} |
@@ -25,8 +25,9 @@ wn.xmlhttp = { | |||||
wn.xmlhttp.complete(req, callback, url) | wn.xmlhttp.complete(req, callback, url) | ||||
} | } | ||||
} | } | ||||
req.open('GET', url, async); | |||||
req.send(args ? url + '?' + args : null); | |||||
var u = args ? (url + '?' + args) : url; | |||||
req.open('GET', u, async); | |||||
req.send(null); | |||||
// for sync | // for sync | ||||
if(!async) { | if(!async) { | ||||
@@ -2,20 +2,17 @@ verbose = True | |||||
force_rebuild = False | force_rebuild = False | ||||
no_minify = False | no_minify = False | ||||
def run(): | |||||
def run(path): | |||||
""" | """ | ||||
Run the builder | Run the builder | ||||
""" | """ | ||||
global verbose | global verbose | ||||
import sys, os | import sys, os | ||||
sys.path.append('py') | |||||
sys.path.append('lib/py') | |||||
from build.project import Project | from build.project import Project | ||||
verbose = True | verbose = True | ||||
Project().build() | |||||
Project().build(path) | |||||
if __name__=='__main__': | if __name__=='__main__': | ||||
run() | run() |
@@ -40,7 +40,9 @@ class Bundle: | |||||
f = open(outfile, 'w') | f = open(outfile, 'w') | ||||
f.write(temp.getvalue()) | f.write(temp.getvalue()) | ||||
f.close() | f.close() | ||||
self.timestamps.update(outfile) | |||||
self.vc.repo.add(outfile) | |||||
if verbose: print 'Wrote %s' % outfile | if verbose: print 'Wrote %s' % outfile | ||||
return temp | return temp | ||||
@@ -50,13 +52,15 @@ class Bundle: | |||||
Returns true if the files are changed since last build | Returns true if the files are changed since last build | ||||
""" | """ | ||||
import os | import os | ||||
from build import force_rebuild | |||||
from build import force_rebuild, verbose | |||||
if force_rebuild: | if force_rebuild: | ||||
return True | return True | ||||
for f in files: | for f in files: | ||||
if f in self.timestamps.dirty: | |||||
if f in self.dirty: | |||||
if verbose: | |||||
print '*** %s changed' % f | |||||
return True | return True | ||||
return False | return False | ||||
@@ -82,7 +86,7 @@ class Bundle: | |||||
jsm.minify(temp, out) | jsm.minify(temp, out) | ||||
out.close() | out.close() | ||||
self.timestamps.update(outfile) | |||||
self.vc.repo.add(outfile) | |||||
new_size = os.path.getsize(outfile) | new_size = os.path.getsize(outfile) | ||||
@@ -92,7 +96,7 @@ class Bundle: | |||||
print 'Compressed: %.2f kB' % (new_size / 1024.0) | print 'Compressed: %.2f kB' % (new_size / 1024.0) | ||||
print 'Reduction: %.1f%%' % (float(org_size - new_size) / org_size * 100) | print 'Reduction: %.1f%%' % (float(org_size - new_size) / org_size * 100) | ||||
def make(self, path): | |||||
def make(self, bpath): | |||||
""" | """ | ||||
Build (stitch + compress) the file defined in build.json | Build (stitch + compress) the file defined in build.json | ||||
""" | """ | ||||
@@ -101,10 +105,12 @@ class Bundle: | |||||
# open the build.json file and read | # open the build.json file and read | ||||
# the dict | # the dict | ||||
bfile = open(os.path.join(path, 'build.json'), 'r') | |||||
bfile = open(bpath, 'r') | |||||
bdata = json.loads(bfile.read()) | bdata = json.loads(bfile.read()) | ||||
bfile.close() | bfile.close() | ||||
path = os.path.dirname(bpath) | |||||
for outfile in bdata: | for outfile in bdata: | ||||
prefix, fname = False, outfile | prefix, fname = False, outfile | ||||
@@ -115,8 +121,6 @@ class Bundle: | |||||
# build the file list relative to the main folder | # build the file list relative to the main folder | ||||
fl = [os.path.relpath(os.path.join(path, f), os.curdir) for f in bdata[outfile]] | fl = [os.path.relpath(os.path.join(path, f), os.curdir) for f in bdata[outfile]] | ||||
self.timestamps.bundled += fl | |||||
if self.changed(fl): | if self.changed(fl): | ||||
# js files are minified by default unless explicitly | # js files are minified by default unless explicitly | ||||
# mentioned in the prefix. | # mentioned in the prefix. | ||||
@@ -127,16 +131,15 @@ class Bundle: | |||||
else: | else: | ||||
self.concat(fl, os.path.relpath(os.path.join(path, fname), os.curdir)) | self.concat(fl, os.path.relpath(os.path.join(path, fname), os.curdir)) | ||||
def bundle(self, timestamps): | |||||
def bundle(self, vc): | |||||
""" | """ | ||||
Build js files from "build.json" | |||||
Build js files from "build.json" found in version control | |||||
""" | """ | ||||
import os | import os | ||||
self.timestamps = timestamps | |||||
self.dirty = vc.repo.uncommitted() | |||||
self.vc = vc | |||||
# walk the parent folder and build all files as defined in the build.json files | # walk the parent folder and build all files as defined in the build.json files | ||||
for wt in os.walk('.', followlinks=True): | |||||
if 'build.json' in wt[2]: | |||||
# found build file | |||||
self.make(os.path.abspath(wt[0])) | |||||
for b in vc.repo.sql("select fname from bundles"): | |||||
self.make(os.path.abspath(os.path.join(vc.root_path, b[0]))) | |||||
@@ -14,11 +14,9 @@ class Project: | |||||
""" | """ | ||||
load libraries | load libraries | ||||
""" | """ | ||||
from build.timestamps import Timestamps | |||||
from build.bundle import Bundle | from build.bundle import Bundle | ||||
from nav import Nav | from nav import Nav | ||||
self.timestamps = Timestamps() | |||||
self.bundle = Bundle() | self.bundle = Bundle() | ||||
self.nav = Nav() | self.nav = Nav() | ||||
@@ -29,11 +27,13 @@ class Project: | |||||
import json | import json | ||||
corejs = open('lib/js/core.min.js', 'r') | corejs = open('lib/js/core.min.js', 'r') | ||||
v = int(self.vc.repo.get_value('last_version_number') or 0) + 1 | |||||
boot = 'var asset_timestamps_=' + self.timestamps.get('json', ('js', 'html', 'css')) \ | |||||
+ '\n' + corejs.read() | |||||
boot = ('window._version_number="%s"' % str(v)) + \ | |||||
'\n' + corejs.read() | |||||
corejs.close() | corejs.close() | ||||
return boot | return boot | ||||
def render_templates(self): | def render_templates(self): | ||||
@@ -69,15 +69,22 @@ class Project: | |||||
print "Rendered %s | %.2fkb" % (fpath, os.path.getsize(fpath) / 1024.0) | print "Rendered %s | %.2fkb" % (fpath, os.path.getsize(fpath) / 1024.0) | ||||
def build(self): | |||||
def build(self, root_path): | |||||
""" | """ | ||||
Build all js files, timestamps.js, index.html and template.html | |||||
Build all js files, index.html and template.html | |||||
""" | """ | ||||
from build.version import VersionControl | |||||
# make bundles | |||||
self.bundle.bundle(self.timestamps) | |||||
self.vc = VersionControl(root_path) | |||||
self.vc.add_all() | |||||
# index, template if framework is dirty | # index, template if framework is dirty | ||||
if self.timestamps.dirty: | |||||
if self.vc.repo.uncommitted(): | |||||
self.bundle.bundle(self.vc) | |||||
self.render_templates() | self.render_templates() | ||||
self.timestamps.write() | |||||
# again add all bundles | |||||
self.vc.add_all() | |||||
self.vc.repo.commit() | |||||
self.vc.close() |
@@ -7,6 +7,7 @@ | |||||
uncommitted (fname, ftype, content, timestamp) | uncommitted (fname, ftype, content, timestamp) | ||||
files (fname, ftype, content, timestamp, version) | files (fname, ftype, content, timestamp, version) | ||||
log (fname, ftype, version) | log (fname, ftype, version) | ||||
bundle_files (fname primary key) | |||||
Discussion: | Discussion: | ||||
@@ -27,23 +28,29 @@ TODO | |||||
import unittest | import unittest | ||||
import os | import os | ||||
root_path = os.path.abspath(os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', '..')) | |||||
test_file = {'fname':'test.js', 'ftype':'js', 'content':'test_code', 'timestamp':'1100'} | test_file = {'fname':'test.js', 'ftype':'js', 'content':'test_code', 'timestamp':'1100'} | ||||
root_path = os.path.abspath(os.curdir) | |||||
def edit_file(): | def edit_file(): | ||||
# edit a file | # edit a file | ||||
p = os.path.join(root_path, 'js/core.min.js') | |||||
content = open(p, 'r').read() | |||||
p = os.path.join(root_path, 'lib/js/core.js') | |||||
# read | |||||
f1 = open(p, 'r') | |||||
content = f1.read() | |||||
f1.close() | |||||
# write | |||||
f = open(p, 'w') | f = open(p, 'w') | ||||
f.write(content) | f.write(content) | ||||
f.close() | f.close() | ||||
return p | |||||
return os.path.relpath(p, root_path) | |||||
verbose = False | verbose = False | ||||
class TestVC(unittest.TestCase): | class TestVC(unittest.TestCase): | ||||
def setUp(self): | def setUp(self): | ||||
self.vc = VersionControl(root_path) | |||||
self.vc = VersionControl() | |||||
self.vc.repo.setup() | self.vc.repo.setup() | ||||
def test_add(self): | def test_add(self): | ||||
@@ -86,13 +93,12 @@ class TestVC(unittest.TestCase): | |||||
self.vc.repo.commit() | self.vc.repo.commit() | ||||
p = edit_file() | p = edit_file() | ||||
# add | # add | ||||
self.vc.add_all() | self.vc.add_all() | ||||
# check if added | # check if added | ||||
ret = self.vc.repo.sql("select * from uncommitted", as_dict=1) | ret = self.vc.repo.sql("select * from uncommitted", as_dict=1) | ||||
self.assertTrue(ret[0]['fname']==p) | |||||
self.assertTrue(p in [r['fname'] for r in ret]) | |||||
def test_merge(self): | def test_merge(self): | ||||
self.vc.add_all() | self.vc.add_all() | ||||
@@ -114,7 +120,7 @@ class TestVC(unittest.TestCase): | |||||
self.vc.merge(self.vc.repo, self.vc.master) | self.vc.merge(self.vc.repo, self.vc.master) | ||||
log = self.vc.master.diff(int(self.vc.master.get_value('last_version_number'))-1) | log = self.vc.master.diff(int(self.vc.master.get_value('last_version_number'))-1) | ||||
self.assertTrue(log, [p]) | |||||
self.assertTrue(p in log) | |||||
def tearDown(self): | def tearDown(self): | ||||
self.vc.close() | self.vc.close() | ||||
@@ -124,25 +130,26 @@ class TestVC(unittest.TestCase): | |||||
class VersionControl: | class VersionControl: | ||||
def __init__(self, root): | |||||
def __init__(self, root=None): | |||||
#self.master = Repository(self, 'versions-master.db') | #self.master = Repository(self, 'versions-master.db') | ||||
self.root(root) | |||||
self.set_root(root) | |||||
self.repo = Repository(self, 'versions-local.db') | self.repo = Repository(self, 'versions-local.db') | ||||
self.ignore_folders = ['.git', '.', '..'] | self.ignore_folders = ['.git', '.', '..'] | ||||
self.ignore_files = ['pyc', 'DS_Store', 'txt', 'db-journal', 'db'] | |||||
self.ignore_files = ['py', 'pyc', 'DS_Store', 'txt', 'db-journal', 'db'] | |||||
def setup_master(self): | def setup_master(self): | ||||
self.master = Repository(self, 'versions-master.db') | self.master = Repository(self, 'versions-master.db') | ||||
def root(self, path=None): | |||||
def set_root(self, path=None): | |||||
""" | """ | ||||
set / reset root and connect | set / reset root and connect | ||||
(the root path is the path of the folder) | |||||
""" | """ | ||||
if path: | |||||
self.root_path = path | |||||
else: | |||||
return self.root_path | |||||
if not path: | |||||
raise Exception, 'path must be given' | |||||
self.root_path = path | |||||
def timestamp(self, path): | def timestamp(self, path): | ||||
""" | """ | ||||
@@ -164,12 +171,16 @@ class VersionControl: | |||||
wt[1].remove(folder) | wt[1].remove(folder) | ||||
for fname in wt[2]: | for fname in wt[2]: | ||||
fpath = os.path.join(wt[0], fname) | |||||
if fname.endswith('build.json'): | |||||
self.repo.add_bundle(fpath) | |||||
continue | |||||
if fname.split('.')[-1] in self.ignore_files: | if fname.split('.')[-1] in self.ignore_files: | ||||
# nothing to do | # nothing to do | ||||
continue | continue | ||||
fpath = os.path.join(wt[0], fname) | |||||
# file does not exist | # file does not exist | ||||
if not self.repo.exists(fpath): | if not self.repo.exists(fpath): | ||||
if verbose: | if verbose: | ||||
@@ -208,6 +219,7 @@ class VersionControl: | |||||
target.commit(d[0]) | target.commit(d[0]) | ||||
def close(self): | def close(self): | ||||
self.repo.conn.commit() | |||||
self.repo.conn.close() | self.repo.conn.close() | ||||
class Repository: | class Repository: | ||||
@@ -223,12 +235,14 @@ class Repository: | |||||
""" | """ | ||||
setup the schema | setup the schema | ||||
""" | """ | ||||
print "setting up %s..." % self.db_path | |||||
self.cur.executescript(""" | self.cur.executescript(""" | ||||
create table properties(pkey primary key, value); | create table properties(pkey primary key, value); | ||||
create table uncommitted(fname primary key, ftype, content, timestamp); | create table uncommitted(fname primary key, ftype, content, timestamp); | ||||
create table files (fname primary key, ftype, content, timestamp, version); | create table files (fname primary key, ftype, content, timestamp, version); | ||||
create table log (fname, ftype, version); | create table log (fname, ftype, version); | ||||
create table versions (number integer primary key, version); | create table versions (number integer primary key, version); | ||||
create table bundles(fname primary key); | |||||
""") | """) | ||||
def sql(self, query, values=(), as_dict=None): | def sql(self, query, values=(), as_dict=None): | ||||
@@ -267,13 +281,17 @@ class Repository: | |||||
""" | """ | ||||
add to uncommitted | add to uncommitted | ||||
""" | """ | ||||
import os | |||||
# commit relative path | |||||
fname = os.path.relpath(fname, self.vc.root_path) | |||||
if not ftype: | if not ftype: | ||||
ftype = fname.split('.')[-1] | ftype = fname.split('.')[-1] | ||||
if not timestamp: | if not timestamp: | ||||
timestamp = self.vc.timestamp(fname) | timestamp = self.vc.timestamp(fname) | ||||
self.sql("insert into uncommitted(fname, ftype, timestamp, content) values (?, ?, ?, ?)" \ | |||||
self.sql("insert or replace into uncommitted(fname, ftype, timestamp, content) values (?, ?, ?, ?)" \ | |||||
, (fname, ftype, timestamp, content)) | , (fname, ftype, timestamp, content)) | ||||
def new_version(self): | def new_version(self): | ||||
@@ -296,15 +314,30 @@ class Repository: | |||||
def commit(self, version=None): | def commit(self, version=None): | ||||
""" | """ | ||||
rebuild bundles if necessary | |||||
copy uncommitted files to repository, update the log and add the change | copy uncommitted files to repository, update the log and add the change | ||||
""" | """ | ||||
# make bundles | |||||
from bundle import Bundle | |||||
Bundle().bundle(self.vc) | |||||
# get a new version number | # get a new version number | ||||
if not version: | |||||
version = self.new_version() | |||||
if not version: version = self.new_version() | |||||
self.update_number(version) | self.update_number(version) | ||||
# find added files to commit | # find added files to commit | ||||
self.add_from_uncommitted(version) | |||||
# clear uncommitted | |||||
self.sql("delete from uncommitted") | |||||
def add_from_uncommitted(self, version): | |||||
""" | |||||
move files from uncommitted table to files table | |||||
""" | |||||
added = self.sql("select * from uncommitted", as_dict=1) | added = self.sql("select * from uncommitted", as_dict=1) | ||||
for f in added: | for f in added: | ||||
@@ -316,21 +349,20 @@ class Repository: | |||||
""", (f['fname'], f['ftype'], f['timestamp'], f['content'], version)) | """, (f['fname'], f['ftype'], f['timestamp'], f['content'], version)) | ||||
# update log | # update log | ||||
self.add_log(f['fname'], f['ftype'], version) | |||||
# clear uncommitted | |||||
self.sql("delete from uncommitted") | |||||
self.add_log(f['fname'], f['ftype'], version) | |||||
def exists(self, fname): | def exists(self, fname): | ||||
""" | """ | ||||
true if exists | true if exists | ||||
""" | """ | ||||
fname = os.path.relpath(fname, self.vc.root_path) | |||||
return self.sql("select fname from files where fname=?", (fname,)) | return self.sql("select fname from files where fname=?", (fname,)) | ||||
def timestamp(self, fname): | def timestamp(self, fname): | ||||
""" | """ | ||||
get timestamp | get timestamp | ||||
""" | """ | ||||
fname = os.path.relpath(fname, self.vc.root_path) | |||||
return int(self.sql("select timestamp from files where fname=?", (fname,))[0][0] or 0) | return int(self.sql("select timestamp from files where fname=?", (fname,))[0][0] or 0) | ||||
def diff(self, number): | def diff(self, number): | ||||
@@ -345,6 +377,12 @@ class Repository: | |||||
return list(set([f[0] for f in ret])) | return list(set([f[0] for f in ret])) | ||||
def uncommitted(self): | |||||
""" | |||||
return list of uncommitted files | |||||
""" | |||||
return [f[0] for f in self.sql("select fname from uncommitted")] | |||||
def get_file(self, fname): | def get_file(self, fname): | ||||
""" | """ | ||||
return file info as dict | return file info as dict | ||||
@@ -356,7 +394,15 @@ class Repository: | |||||
add file to log | add file to log | ||||
""" | """ | ||||
self.sql("insert into log(fname, ftype, version) values (?,?,?)", (fname, ftype, version)) | self.sql("insert into log(fname, ftype, version) values (?,?,?)", (fname, ftype, version)) | ||||
def add_bundle(self, fname): | |||||
""" | |||||
add to bundles | |||||
""" | |||||
self.sql("insert or replace into bundles(fname) values (?)", (fname,)) | |||||
if __name__=='__main__': | if __name__=='__main__': | ||||
import os, sys | |||||
sys.path.append('py') | |||||
sys.path.append('lib/py') | |||||
unittest.main() | unittest.main() |
@@ -40,6 +40,15 @@ def runserverobj(arg=None): | |||||
def logout(): | def logout(): | ||||
webnotes.login_manager.logout() | webnotes.login_manager.logout() | ||||
# versions | |||||
# -------- | |||||
def get_diff(): | |||||
import os | |||||
v = webnotes.form_dict.get('version_number') | |||||
from build.version import VersionControl | |||||
webnotes.response['message'] = VersionControl(os.path.abspath(os.path.curdir)).repo.diff(v) | |||||
# DocType Mapper | # DocType Mapper | ||||
# ------------------------------------------------------------------------------------ | # ------------------------------------------------------------------------------------ | ||||
@@ -1,4 +1,57 @@ | |||||
#!/usr/bin/env python | #!/usr/bin/env python | ||||
print 'hello' | |||||
import os, sys | |||||
from py.build import version | |||||
version.verbose = True | |||||
def run(): | |||||
sys.path.append('lib') | |||||
sys.path.append('lib/py') | |||||
vc = version.VersionControl(os.path.abspath(os.curdir)) | |||||
if len(sys.argv)<2: | |||||
print "wnframework version control utility" | |||||
print "Usage: wnf build|add|commit|diff|merge|setup" | |||||
cmd = sys.argv[1] | |||||
if cmd=='build': | |||||
from py import build | |||||
build.run(os.path.abspath(os.curdir)) | |||||
if cmd=='add': | |||||
if not len(sys.argv)>1: | |||||
print 'usage: wnf add path/to/file' | |||||
return | |||||
vc.repo.add(sys.argv[2]) | |||||
if cmd=='commit': | |||||
if len(sys.argv>2) and sys.argv[2]=='-a': | |||||
vc.add_all() | |||||
vc.repo.commit() | |||||
if cmd=='diff': | |||||
vc.repo.uncommitted() | |||||
if cmd=='merge': | |||||
vc.setup_master() | |||||
if sys.argv[2]=='local': | |||||
vc.merge(vc.repo, vc.master) | |||||
elif sys.argv[2]=='master': | |||||
vc.merge(vc.master, vc.repo) | |||||
else: | |||||
print "usage: wnf merge local|master" | |||||
print "help: parameter (local or master) is the source" | |||||
if cmd=='setup': | |||||
vc.repo.setup() | |||||
vc.close() | |||||
if __name__=='__main__': | |||||
run() |