GitMonitor.js
[gitlive] / GitMonitor.js
1
2 var Gio      = imports.gi.Gio;
3 var Gtk      = imports.gi.Gtk;
4 var Notify   = imports.gi.Notify;
5 var GLib     = imports.gi.GLib;
6
7 var Spawn = imports.Spawn;
8 var StatusIcon = imports.StatusIcon.StatusIcon;
9 var Monitor = imports.Monitor.Monitor;
10
11
12
13 var GitMonitor = new Monitor({
14     
15     /**
16      * @property {String} the "gitlive" directory, normally ~/gitlive
17      *  dset by OWNER... - we should do this as a CTOR.
18      *  
19      */
20     gitlive : false,
21     
22     
23     queue : [],
24     queueRunning : false,
25      
26      
27      
28     pause : function() {
29         this.paused = true;
30         this.queue = [];
31         imports.StatusIcon.StatusIcon.el.set_from_stock( Gtk.STOCK_MEDIA_PAUSE );
32     },
33     
34     resume : function() {
35         this.paused = false;
36         this.queue = [];
37         imports.StatusIcon.StatusIcon.el.set_from_stock( Gtk.STOCK_MEDIA_PLAY );
38         
39         
40     },
41     
42     /**
43      * Start the monitoring
44      * and run the queue every 500 milliseconds..
45      *
46      */
47     start: function() {
48         imports.StatusIcon.StatusIcon.el.set_from_stock( Gtk.STOCK_REFRESH );
49         var _this = this;
50         this.lastAdd = new Date();
51         
52         GLib.timeout_add(GLib.PRIORITY_LOW, 500, function() {
53             _this.top.forEach(_this.monitor, _this);
54             imports.StatusIcon.StatusIcon.el.set_from_stock( Gtk.STOCK_MEDIA_PLAY );
55
56         });
57         
58         GLib.timeout_add(GLib.PRIORITY_LOW, 1000, function() {
59             //TIMEOUT", _this.queue.length , _this.queueRunning].join(', '));
60             if (!_this.queue.length || _this.queueRunning) {
61                 return 1;
62             }
63             var last = Math.floor(((new Date()) - _this.lastAdd) / 100);
64             
65             //print("LAST RUN?" + last);
66             
67             if (last < 5) { // wait 1/2 a seconnd before running.
68                 return 1;
69             }
70             //_this.lastAdd = new Date();
71             //return 1;
72         
73             _this.runQueue();
74             return 1;
75         },null,null);
76         
77         try { 
78             var notification = new Notify.Notification({
79                 summary: "Git Live",
80                 body : this.gitlive + "\nMonitoring " + this.monitors.length + " Directories",
81                 timeout : 5
82             });
83     
84             notification.set_timeout(5);
85             notification.show();
86         } catch(e) {
87             print(e.toString());
88         }
89     },
90     
91     
92     stop: function() {
93         this.parent.parent.el.set_from_stock( Gtk.STOCK_MEDIA_PAUSE );
94         Monitor.prototype.stop.call(this);
95     },
96     
97     
98     monitor : function(path, fn, depth)
99     {
100         depth = typeof(depth) == 'number'  ? depth *1 : 0;
101         
102          
103         // if we are not at top level.. and there is a .git directory  (it's a submodule .. ignore) 
104         if (depth > 1 && GLib.file_test(path + '/.git' , GLib.FileTest.IS_DIR)) {
105             return;
106         }
107         
108         if (depth == 1) {
109             var repo = imports.Scm.Repo.Repo.get(path);
110             if (!repo || !repo.autocommit()) {
111                 return;
112             } 
113         }
114         
115         
116         // check if the repo is to be monitored.
117         //print("PATH : " + path);
118         
119         
120         Monitor.prototype.monitor.call(this, path,fn, depth);
121     },
122     
123     /**
124      * run the queue.
125      * - pulls the items off the queue 
126      *    (as commands run concurrently and new items may get added while it's running)
127      * - runs the queue items
128      * - pushes upstream.
129      * 
130      */
131     runQueue: function()
132     {
133         
134         if (this.paused) {
135             return;
136         }
137         this.queueRunning = true;
138         var cmds = [];
139         this.queue.forEach(function (q) {
140             cmds.push(q);
141         });
142         this.queue = []; // empty queue!
143         
144         var success = [];
145         var failure = [];
146         var repos = [];
147         var done = [];
148         
149         // first build a array of repo's to work with
150         var repo_list = {};
151         
152         // pull and group.
153         
154         //print(JSON.stringify(cmds));
155         this.paused = true;
156         cmds.forEach(function(cmd) {
157             var gitpath = cmd.shift(); 
158             if (typeof(repo_list[gitpath]) == 'undefined') {
159                 repo_list[gitpath] = new imports.Scm.Git.Repo.Repo( { repopath : gitpath });
160                 repo_list[gitpath].cmds = [];
161                 repo_list[gitpath].pull();
162             }
163             repo_list[gitpath].cmds.push(cmd);
164         });
165         this.paused = false;
166         // build add, remove and commit message list..
167         
168          
169          
170         for (var gitpath in repo_list) {
171             var repo = repo_list[gitpath];
172             var add_files = [];
173             var remove_files = [];
174             var messages = [];
175             //print(JSON.stringify(repo.cmds,null,4));
176             
177             repo.cmds.forEach(function(cmd) {
178                 
179                 var name = cmd.shift();
180                 var arg = cmd.shift();
181                 
182                 switch(name) {
183                     case 'add' :
184                         
185                         if (add_files.indexOf(arg) > -1) {
186                             break;
187                         }
188                         
189                         // if file does not exist.. s,ip
190                         
191                         //if (!GLib.file_test(arg, GLib.FileTest.EXISTS)) {
192                              
193                         //    break;
194                          // }
195         
196                         
197                         add_files.push(arg);
198                         break;
199                     
200                     case 'rm':
201                         
202                         if (add_files.indexOf(arg) > -1) {
203                             break;
204                         }
205                         
206                         // if file exists, do not try and delete it.
207                         if (GLib.file_test(arg, GLib.FileTest.EXISTS)) {
208                             break;
209                         }
210                         
211                         remove_files.push(arg);
212                         break;
213                     
214                     case 'commit' :
215                         
216                         if (messages.indexOf(arg.message) < 0) { 
217                             messages.push(arg.message);
218                         }
219                         break;    
220                 } 
221             });
222             
223             //repo.debug = 1;
224             // these can fail... at present... as we wildcard stuff.
225             print("ADD : "  + JSON.stringify(add_files));
226             
227             // make sure added files do not get removed..
228             remove_files  = remove_files.filter(function(v) {
229                 return add_files.indexOf(v) < 0;
230             });
231             print("REMOVE : "  + JSON.stringify(remove_files));
232             
233             
234             // make sure monitoring is paused so it does not recursively pick up
235             // deletions
236             
237             // -- DO STUFF..
238             
239             repo.add(add_files);
240             
241             repo.remove(remove_files);
242             this.paused = false;
243             
244             
245             try { 
246                 success.push(repo.commit({
247                     reason : messages.join("\n"),
248                     files : add_files  
249                 }));
250                 success.push(repo.push());
251
252             } catch(e) {
253                 failure.push(e.message);
254                 
255             }   
256         }
257         
258         // finally merge all the commit messages.
259          
260         try {
261             // catch notification failures.. so we can carry on..
262             if (success.length) {
263                 print(success.join("\n"));
264                 
265                 var notification = new Notify.Notification({
266                     summary: "Git Live Commited",
267                     body : success.join("\n"),
268                     timeout : 5
269                     
270                 });
271     
272                 notification.set_timeout(5);
273                 notification.show();   
274             }
275             
276             if (failure.length) {
277             
278                 var notification = new Notify.Notification({
279                     summary: "Git Live ERROR!!",
280                     body : failure.join("\n"),
281                     timeout : 5
282                     
283                 });
284     
285                 notification.set_timeout(5); // show errros for longer
286                 notification.show();   
287             }
288         } catch(e) {
289             print(e.toString());
290             
291         }
292         this.queueRunning = false;
293     },
294     
295     shouldIgnore: function(f)
296     {
297         
298         if (this.paused) {
299             return true;
300         }
301         
302         
303         // vim.. what a seriously brain dead program..
304         if (f.name == '4913') {
305             return true;
306         }
307         
308         if (f.name[0] == '.') {
309             // except!
310             if (f.name == '.htaccess') {
311                 return false;
312             }
313             
314             return true;
315         }
316         if (f.name.match(/~$/)) {
317             return true;
318         }
319         if (f.name.match(/^nbproject/)) {
320             return true;
321         }
322         // ignore anything in top level!!!!
323         if (!f.vpath.length) {
324             return true;
325         }
326         
327         return false;
328     },
329     
330     /**
331      * parsePath:
332      * Fill in gitlive, vpath and repo  
333      * 
334      */
335     parsePath: function(f)
336     {
337            
338         var vpath_ar = f.path.substring(this.gitlive.length +1).split('/');
339         
340         f.gitpath = this.gitlive + '/' + vpath_ar.shift();
341         f.vpath =  vpath_ar.join('/');
342         //f.repo = new imports.Scm.Git.Repo({ repopath: f.gitpath })
343         
344         
345     },
346     
347     just_created : {},
348       
349     onChanged : function(src) 
350     { 
351         return; // always ignore this..?
352         //this.parsePath(src);
353     },
354     
355     
356     
357     
358     /**
359      *  results in  git add  + git commit..
360      *
361      */
362     onChangesDoneHint : function(src) 
363     { 
364         this.lastAdd = new Date();
365         this.parsePath(src);
366         if (this.shouldIgnore(src)) {
367             return;
368         }
369         
370        
371         var add_it = false;
372         if (typeof(this.just_created[src.path]) !='undefined') {
373             delete this.just_created[src.path];
374             
375             this.queue.push( 
376                 [ src.gitpath,  'add', src.vpath ],
377                 [ src.gitpath,  'commit',    { message: src.vpath} ] 
378                 
379             );
380          
381             return;
382         }
383         
384         this.queue.push( 
385             [ src.gitpath,  'add', src.vpath ],
386             [ src.gitpath,  'commit',  {  message: src.vpath} ]
387
388             
389         );
390     },
391     onDeleted : function(src) 
392     { 
393         this.lastAdd = new Date();
394         this.parsePath(src);
395         if (this.shouldIgnore(src)) {
396             return;
397         }
398         // should check if monitor needs removing..
399         // it should also check if it was a directory.. - so we dont have to commit all..
400         
401         
402         this.queue.push( 
403             [ src.gitpath, 'rm' , src.vpath ],
404             [ src.gitpath, 'commit', { all: true, message: src.vpath} ]
405             
406         );
407     
408         
409     },
410     onCreated : function(src) 
411     { 
412         this.lastAdd = new Date();
413         this.parsePath(src);
414         if (this.shouldIgnore(src)) {
415             return;
416         }
417         
418         if (!GLib.file_test(src.path, GLib.FileTest.IS_DIR)) {
419             this.just_created[src.path] = true;
420             return; // we do not handle file create flags... - use done hint.
421         }
422         // director has bee created
423         this.monitor(src.path);
424         
425         this.queue.push( 
426             [ src.gitpath, 'add' , src.vpath,  { all: true } ],
427             [ src.gitpath, 'commit' , { all: true, message: src.vpath} ]
428             
429         );
430         
431         
432     },
433     onAttributeChanged : function(src)
434     { 
435         this.lastAdd = new Date();
436         this.parsePath(src);
437         if (this.shouldIgnore(src)) {
438             return;
439         }
440         
441         this.queue.push(
442                         
443             //[ src.gitpath, 'commit' ,  src.vpath, { message: src.vpath} ]
444             [ src.gitpath, 'add' ,  src.vpath ],
445              [ src.gitpath, 'commit' ,    {  message: "Attribute Changed :" + src.vpath} ]
446         );
447  
448     
449     },
450     
451     onMoved : function(src,dest)
452     { 
453         this.lastAdd = new Date();
454         this.parsePath(src);
455         this.parsePath(dest);
456         
457         if (src.gitpath != dest.gitpath) {
458             this.onDeleted(src);
459             this.onCreated(dest);
460             this.onChangedDoneHint(dest);
461             return;
462         }
463         // needs to handle move to/from unsupported types..
464         
465         if (this.shouldIgnore(src)) {
466             return;
467         }
468         if (this.shouldIgnore(dest)) {
469             return;
470         }
471         
472         this.queue.push( 
473            // [ src.gitpath, 'mv',  '-k', src.vpath, dest.vpath ],
474              [ src.gitpath, 'add',    dest.vpath ],
475              [ src.gitpath, 'rm',    src.vpath ],
476              
477             [ src.gitpath, 'commit' , 
478                 { message:   'MOVED ' + src.vpath +' to ' + dest.vpath}
479             ]
480         );
481          
482     }
483           
484     
485 });
486  
487