FixBug.bjs
[gitlive] / gitlive2.js
1 #!/usr/bin/seed
2 ///<script type="text/javascript">
3 /**
4 * Git Live
5
6 * inotify hooks for ~/gitlive
7 * that commit and push any changes made.
8 * Bit like a revision controled backed up file system!?
9 *
10 *
11 * The aims of this
12 * A) have a gitlive branch - where all our commits go.. - so they can be replicated on the server 
13 * B) HEAD branch - where things get merged to..
14 *    -- eventually on closing issues..
15 *    -- currently when we switch from one feature to another..
16 *
17 * CURRENT HEAD?   
18 * git log -n 1
19 * --pretty=format:%H BRANCHNAME
20
21 *
22 * Notes on feature branch implementation
23 * We need to add a gitlive branch on the remote server..
24 *   git push origin origin:refs/heads/gitlive 
25 *   git checkout --track -b gitlive origin/gitlive << means pull will use both branches..
26 *
27 *
28 * On our feature tree..  
29 *   git push origin origin:refs/heads/feature_2
30
31 * we clone directory into gitlive_feature/XXXXX
32 *     git branch issue_XXX
33 *     git checkout issue_XXX
34 *    
35 * run this on the feature branch it merges and commits..
36 *  git pull origin master << or gitlive..
37 *  
38 *
39 * Standard change file (same bug as before..)
40 *   cd gitlive
41 *     commit etc.. (with bug no..)
42 *   cd featuredir
43 *     git pull origin gitlive
44 *     git push
45 *   cd gitlive
46 *     git push
47 *     
48 *  Change to new bug number..
49 *  cd featuredir
50 *    git checkout -b master origin/master
51 *    git checkout master <<< make sure
52 *    git pull --squash origin gitlive
53 *    git commit -m 'done with old bug number'
54 *    git push
55 *  cd gitlive
56 *    git push
57 *  cd featuredir
58 *     git push origin origin:refs/heads/feature_XXX
59 *     git checkout feature_XXX
60 *   cd gitlive
61 *     commit etc. (with new bug number) 
62 *    cd featuredir
63 *     git pull origin gitlive
64 *     git push
65 *   cd gitlive
66 *     git push
67
68 */
69
70 GI      = imports.gi.GIRepository
71 GLib        = imports.gi.GLib;
72
73 // we add this in, as it appears to get lost sometimes if we set it using the ENV. variable in builder.sh
74 GI.IRepository.prepend_search_path(GLib.get_home_dir() + '/.Builder/girepository-1.1');
75
76 Gio         = imports.gi.Gio;
77 Gtk         = imports.gi.Gtk;
78 Notify      = imports.gi.Notify;
79
80 Spawn       = imports.Spawn;
81 Git         = imports.Git;
82 StatusIcon  = imports.StatusIcon.StatusIcon;
83 Monitor     = imports.Monitor.Monitor;
84
85
86 //File = imports[__script_path__+'/../introspection-doc-generator/File.js'].File
87 Gtk.init (null, null);
88
89 var gitlive = GLib.get_home_dir() + "/gitlive";
90
91 if (!GLib.file_test(gitlive, GLib.FileTest.IS_DIR)) {
92     var msg = new Gtk.MessageDialog({message_type:
93         Gtk.MessageType.INFO, buttons : Gtk.ButtonsType.OK, text: "GIT Live - ~/gitlive does not exist."});
94     msg.run();
95     msg.destroy();
96     
97     Seed.quit();
98 }
99
100  
101 var monitor = new Monitor({
102     /**
103      *
104      * queue objects
105      *  action: 'add' | rm | update
106      *  repo : 'gitlive'
107      *  file : XXXXX
108      *
109      * 
110      *
111      */
112     action_queue : [],
113     queueRunning : false,
114      
115     start: function()
116     {
117         var _this = this;
118         this.lastAdd = new Date();
119          
120         // start monitoring first..
121         Monitor.prototype.start.call(this);
122         
123         // then start our queue runner..
124         GLib.timeout_add(GLib.PRIORITY_LOW, 500, function() {
125             //TIMEOUT", _this.queue.length , _this.queueRunning].join(', '));
126             if (!_this.queue.length || _this.queueRunning) {
127                 return 1;
128             }
129             var last = Math.floor(((new Date()) - this.lastAdd) / 100);
130             if (last < 4) { // wait 1/2 a seconnd before running.
131                 return 1;
132             }
133             _this.runQueue();
134             return 1;
135         },null,null);
136         
137         
138         var notification = new Notify.Notification({
139             summary: "Git Live",
140             body : gitlive + "\nMonitoring " + this.monitors.length + " Directories"
141         });
142
143         notification.set_timeout(2000);
144         notification.show();   
145     },
146     /**
147      * run the queue.
148      * - pulls the items off the queue 
149      *    (as commands run concurrently and new items may get added while it's running)
150      * - runs the queue items
151      * - pushes upstream.
152      * 
153      */
154     runQueue: function()
155     {
156         this.queueRunning = true;
157         var cmds = [];
158         //this.queue.forEach(function (q) {
159         //    cmds.push(q);
160         //});
161         
162         this.action_queue.forEach(function (q) {
163             cmds.push(q);
164         });
165         //this.queue = []; // empty queue!
166         this.action_queue = [];
167         var success = [];
168         var failure = [];
169         var repos = [];
170         var done = [];
171         
172         function readResult(sp) {
173             switch (sp.result * 1) {
174                 case 0: // success:
175                     success.push(sp.args.join(' '));
176                     if (sp.output.length) success.push(sp.output + '');
177                   // if (sp.stderr.length) success.push(sp.stderr + '');
178                     break;
179                 default: 
180                     failure.push(sp.args.join(' '));
181                     if (sp.output.length) failure.push(sp.output);
182                     if (sp.stderr.length) failure.push(sp.stderr);
183                     break;
184             }
185         }
186             
187         cmds.forEach(function(cmd) {
188             // prevent duplicate calls..
189             if (done.indexOf(JSON.stringify(cmd)) > -1) {
190                 return;
191             }
192             done.push(JSON.stringify(cmd));
193             // --- we keep a list of repositories that will be pushed to at the end..
194             
195             if (repos.indexOf(cmd.repos) < 0) {
196                 repos.push(cmd.repos);
197                 //    Git.run(cmd.repos , 'pull'); // pull before we push!
198             }
199             
200             var gp  = gitlive + '/' + cmd.repo;
201             
202             switch( cmd.action ) {
203                 case 'add':
204                     readResult(Git.run(gp, 'add',  cmd.file ));
205                     readResult(Git.run(gp, 'commit',  src.file, { message: cmd.file}  ));
206                     break;
207                     
208                 case 'rm':
209                     readResult(Git.run(gp, 'rm',  cmd.file ));
210                     readResult(Git.run(gp, 'commit',  { all: true, message: cmd.file}  ));
211                     break;
212                      
213                 case 'update':
214                     readResult(Git.run(gp, 'commit', cmd.file  , {   message: cmd.file}  ));
215                     break;
216                     
217                 case 'mv':
218                     readResult(Git.run(gp, 'mv', cmd.file , cmd.target));
219                     readResult(Git.run(gp, 'commit', cmd.file  , cmd.target,
220                             {   message: 'MOVED ' + src.file +' to ' + dest.target }  ));
221                     break; 
222             }
223             
224              
225             
226         });
227          
228         // push upstream.
229         repos.forEach(function(r) {
230             var sp = Git.run(gitlive + '/' +r , 'push', { all: true } );
231             if (sp.length) {
232                 success.push(sp);
233             }
234             
235         });
236         
237         if (success.length) {
238             print(success.join("\n"));
239             var notification = new Notify.Notification({
240                 summary: "Git Live Commited",
241                 body : success.join("\n")
242                 
243             });
244
245             notification.set_timeout(2000);
246             notification.show();   
247         }
248         if (failure.length) {
249         
250             var notification = new Notify.Notification({
251                 summary: "Git Live ERROR!!",
252                 body : failure.join("\n")
253                 
254             });
255
256             notification.set_timeout(5000); // show errros for longer
257             notification.show();   
258         }
259         this.queueRunning = false;
260     },
261     
262     shouldIgnore: function(f)
263     {
264         if (f.name[0] == '.') {
265             // except!
266             if (f.name == '.htaccess') {
267                 return false;
268             }
269             
270             return true;
271         }
272         if (f.name.match(/~$/)) {
273             return true;
274         }
275         // ignore anything in top level!!!!
276         if (!f.vpath.length) {
277             return true;
278         }
279         
280         return false;
281         
282     },
283     
284     /**
285      * set gitpath and vpath
286      * 
287      * 
288      */
289     
290     parsePath: function(f)
291     {
292            
293         var vpath_ar = f.path.substring(gitlive.length +1).split('/');
294         f.repo = vpath_ar.shift();
295         f.gitpath = gitlive + '/' + f.repo;
296         f.vpath =  vpath_ar.join('/');
297         
298         
299     },
300     
301     just_created : {},
302       
303     onChanged : function(src) 
304     { 
305         return; // always ignore this..?
306         //this.parsePath(src);
307     },
308     
309     /**
310      *  results in  git add  + git commit..
311      *  
312      *
313      *
314      */
315     
316     onChangesDoneHint : function(src) 
317     { 
318         this.parsePath(src);
319         if (this.shouldIgnore(src)) {
320             return;
321         }
322         
323         var add_it = false;
324         if (typeof(this.just_created[src.path]) !='undefined') {
325             delete this.just_created[src.path];
326             this.lastAdd = new Date();
327             //this.queue.push( 
328             //    [ src.gitpath,  'add', src.vpath ],
329             //    [ src.gitpath,  'commit',  src.vpath, { message: src.vpath} ] 
330             //    
331             //);
332             this.action_queue.push({
333                 action: 'add',
334                 repo : src.repo,
335                 file : src.vpath
336             });
337             
338             
339          
340             return;
341         }
342         this.lastAdd = new Date();
343         //this.queue.push( 
344         //    [ src.gitpath,  'add', src.vpath ],
345         //    [ src.gitpath,  'commit', src.vpath, {  message: src.vpath} ]
346         //
347         //);
348         
349         this.action_queue.push({
350             action: 'add',
351             repo : src.repo,
352             file : src.vpath
353         });
354         
355
356     },
357     onDeleted : function(src) 
358     { 
359         this.parsePath(src);
360         if (this.shouldIgnore(src)) {
361             return;
362         }
363         // should check if monitor needs removing..
364         // it should also check if it was a directory.. - so we dont have to commit all..
365         
366         this.lastAdd = new Date();
367         //this.queue.push( 
368         //    [ src.gitpath, 'rm' , src.vpath ],
369         //    [ src.gitpath, 'commit', { all: true, message: src.vpath} ]
370         //    
371         //);
372         this.action_queue.push({
373             action: 'rm',
374             repo : src.repo,
375             file : src.vpath
376         });
377         
378     },
379     onCreated : function(src) 
380     { 
381         this.parsePath(src);
382         if (this.shouldIgnore(src)) {
383             return;
384         }
385         
386         if (!GLib.file_test(src.path, GLib.FileTest.IS_DIR)) {
387             this.just_created[src.path] = true;
388             return; // we do not handle file create flags... - use done hint.
389         }
390         // director has bee created
391         this.monitor(src.path);
392         
393         /*
394           since git does not really handle directory adds...
395          
396         this.lastAdd = new Date();
397         this.action_queue.push({
398             action: 'add',
399             repo : src.repo,
400             file : src.vpath
401         });
402         
403         this.queue.push( 
404             [ src.gitpath, 'add' , src.vpath,  { all: true } ],
405             [ src.gitpath, 'commit' , { all: true, message: src.vpath} ]
406             
407         );
408         */
409         
410         
411     },
412     onAttributeChanged : function(src) { 
413         this.parsePath(src);
414         if (this.shouldIgnore(src)) {
415             return;
416         }
417         this.lastAdd = new Date();
418         
419         
420         //this.queue.push( 
421        //     [ src.gitpath, 'commit' ,  src.vpath, { message: src.vpath} ]
422        // );
423         this.action_queue.push({
424             action: 'update',
425             repo : src.repo,
426             file : src.vpath
427         });
428  
429     
430     },
431     
432     onMoved : function(src,dest)
433     { 
434         this.parsePath(src);
435         this.parsePath(dest);
436         
437         if (src.gitpath != dest.gitpath) {
438             this.onDeleted(src);
439             this.onCreated(dest);
440             this.onChangedDoneHint(dest);
441             return;
442         }
443         // needs to handle move to/from unsupported types..
444         
445         if (this.shouldIgnore(src)) {
446             return;
447         }
448         if (this.shouldIgnore(dest)) {
449             return;
450         }
451         this.lastAdd = new Date();
452        // this.queue.push( 
453        //     [ src.gitpath, 'mv',  '-k', src.vpath, dest.vpath ],
454        //     [ src.gitpath, 'commit' ,  src.vpath, dest.vpath ,
455        //         { message:   'MOVED ' + src.vpath +' to ' + dest.vpath} ]
456        // );
457         
458         this.action_queue.push({
459             action: 'mv',
460             repo : src.repo,
461             file : src.vpath,
462             target : dest.vpath
463             
464         });
465         
466     }
467           
468     
469 });
470  
471  
472   
473
474 function errorDialog(data) {
475     var msg = new Gtk.MessageDialog({
476             message_type: Gtk.MessageType.ERROR, 
477             buttons : Gtk.ButtonsType.OK, 
478             text: data
479     });
480     msg.run();
481     msg.destroy();
482 }
483
484  
485
486
487
488 //
489 // need a better icon...
490
491
492 StatusIcon.init();   
493
494
495 Notify.init("gitlive");
496
497 monitor.add(GLib.get_home_dir() + "/gitlive");
498 monitor.start();
499 Gtk.main();
500 //icon.signal["activate"].connect(on_left_click);
501