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