GitMonitor.vala
[gitlive] / GitMonitor.vala
1
2
3 public class GitMonitorQueue : MonitorNamePathDir {
4         // name = basename
5         // path = full path..
6         // dir = dir path
7     
8         public string gitpath;
9         public string vpath;  // relative path (within git)
10         public string vname;  // relative filename (within git)
11         public string message ; // for commit
12         public bool commit_all;
13         
14         public string add; // 
15
16
17
18
19         public GitMonitorQueue(MonitorNamePathDir f) {
20
21             base(f.name, f.path, f.dir);
22
23
24             this.message = "";
25             this.commit_all = false;
26  
27            
28             var vpath_ar = this.path.substring(GitMonitor.gitlive.length +1).split("/", 0);
29             
30             this.gitpath = GitMonitor.gitlive + "/" + vpath_ar[0];
31             
32             string[]  vpath = {};
33             for (var i = 1; i< vpath_ar.length; i++) {
34                 vpath += vpath_ar[i];
35             }
36
37             this.vpath =  string.joinv("/", vpath);
38             //f.repo = new imports.Scm.Git.Repo({ repopath: f.gitpath })
39         
40         
41         }
42
43         public bool shouldIgnore(GitMonitor gm)
44         {
45             
46             
47             
48             // vim.. what a seriously brain dead program..
49             if (this.name == "4913") {
50                 return true;
51             }
52             
53             if (this.name[0] == '.') {
54                 // except!
55                 if (this.name == ".htaccess") {
56                     return false;
57                 }
58                 
59                 return true;
60             }
61             //if (f.name.match(/~$/)) {
62             //    return true;
63             //}
64             //if (f.name.match(/^nbproject/)) {
65             //    return true;
66             //}
67             // ignore anything in top level!!!!
68             if (this.vpath.length < 1) {
69                 return true;
70             }
71             
72             return false;
73         }
74         
75         /** -- statics --*/
76         
77         public static int indexOfAdd( Array<GitMonitorQueue> add_files, string add) {
78             for(var i =0; i < add_files.length; i++) {
79                 if (add_files.index(i).add == add) {
80                     return i;
81                 }
82             }
83             return -1;
84         }
85         public static  int indexOfMessage(Array<GitMonitorQueue> messages, string message)  {
86             for(var i =0; i < messages.length; i++) {
87                 if (messages.index(i).message == message) {
88                     return i;
89                 }
90             }
91             return -1;
92         }
93         public static string messageToString(Array<GitMonitorQueue> messages ) {
94             string[] ret = {};
95             for(var i =0; i < messages.length; i++) {
96                 ret+= messages.index(i).message;
97             }
98             return string.joinv("\n",ret);
99         }
100
101 }
102
103
104
105 public class GitMonitor : Monitor
106 {
107
108  
109     /**
110      * @property {String} the "gitlive" directory, normally ~/gitlive
111      *  dset by OWNER... - we should do this as a CTOR.
112      *  
113      */
114     public static string gitlive = "";
115     
116     
117     public Array<GitMonitorQueue> queue ;
118     public bool queueRunning = false;
119     
120     public DateTime lastAdd;
121      
122      
123     public void pause() {
124         this.paused = true;
125         // what does this do to the old one...
126         this.queue = new Array<GitMonitorQueue> ();
127         StatusIconA.statusicon.set_from_stock( Gtk.Stock.MEDIA_PAUSE );
128
129     }
130     
131     public void resume () {
132         this.paused = false;
133         this.queue = new Array<GitMonitorQueue> ();
134         StatusIconA.statusicon.set_from_stock( Gtk.Stock.MEDIA_PLAY );
135         
136         
137     }
138     /**
139      * Start the monitoring
140      * and run the queue every 500 milliseconds..
141      *
142      */
143     public void start() {
144         StatusIconA.statusicon.set_from_stock( Gtk.Stock.MEDIA_REFRESH );
145         
146          
147         this.lastAdd = new DateTime.now(); 
148         
149         Timeout.add_full(GLib.PRIORITY_LOW, 500, () => {
150
151             // call this.monitor on each of 'top'
152             for(int i = 0; i < this.top.length ; i++) {
153                 this.monitor(this.top.index(i) );
154             }
155             StatusIconA.statusicon.set_from_stock( Gtk.Stock.MEDIA_PLAY );
156              
157            
158             
159             try { 
160                 var notification = new Notify.Notification({
161                     "Git Live",
162                     GitMonitor.gitlive + "\nMonitoring " + _this.monitors.length + " Directories",
163                      "dialog-information"
164                 });
165         
166                 notification.set_timeout(5);
167                 notification.show();
168             } catch(Error e) {
169                 print(e.toString());
170             }
171
172         });
173         
174         Timeout.add_full(GLib.PRIORITY_LOW, 1000, () => {
175             //TIMEOUT", _this.queue.length , _this.queueRunning].join(', '));
176             if (!_this.queue.length || _this.queueRunning) {
177                 return true;
178             }
179
180             var last = this.lastAdd.difference(new DateTime.now());
181
182             
183             //print("LAST RUN?" + last);
184             
185             if (last < 5 * Timespan.SECOND) { // wait 1/2 a seconnd before running.
186                 return 1;
187             }
188             //_this.lastAdd = new Date();
189             //return 1;
190         
191             this.runQueue();
192             return true;
193         },null,null);
194         
195       
196     }
197
198
199     public void stop() {
200         StatusIconA.statusicon.set_from_stock( Gtk.Stock.MEDIA_PAUSE );;
201         base.stop();
202     }
203     
204     
205     public void monitor (string path,  int depth = 0)
206     {
207         
208         //var depth = typeof(depth) == 'number'  ? depth *1 : 0;
209         
210          
211         // if we are not at top level.. and there is a .git directory  (it's a submodule .. ignore) 
212         if (depth > 1 && GLib.file_test(path + "/.git" , GLib.FileTest.IS_DIR)) {
213             return;
214         }
215         
216         if (depth == 1) {
217             // FIXME - check if repo is flagged as not autocommit..
218             //var repo = imports.Scm.Repo.Repo.get(path);
219             //if (!repo || !repo.autocommit()) {
220             //    return;
221             //} 
222         }
223         
224         
225         // check if the repo is to be monitored.
226         //print("PATH : " + path);
227         
228         
229         base.monitor(path, depth);
230     }
231
232     
233
234     /**
235      * run the queue.
236      * - pulls the items off the queue 
237      *    (as commands run concurrently and new items may get added while it's running)
238      * - runs the queue items
239      * - pushes upstream.
240      * 
241      */
242     public void runQueue()
243     {
244         
245         if (this.paused) {
246             return;
247         }
248         this.queueRunning = true;
249
250         var cmds = new Array<GitMonitorQueue>();
251         for(var i = 0; i < this.queue.length; i++) {
252             cmds.append_val(this.queue.item(i));
253         }
254
255         this.queue = new Array<GitMonitorQueue>();// empty queue!
256
257         
258         string[] success = {};
259         string[] failure = {};
260         var repos = new Array<GitRepo>(); //??
261         var done = new Array<GitMonitorQueue>();
262         
263         // first build a array of repo's to work with
264         var repo_list = new Array<GitRepo>();
265         
266         // pull and group.
267         
268         //print(JSON.stringify(cmds));
269         this.paused = true;
270         
271         for(var i = 0; i < cmds.length; i++) {
272            
273         
274             var gitpath = cmd.gitpath; 
275             var ix  = GitRepo.indexOf(this.repos,  cmd.gitpath);
276             if (ix < 0) {
277                 repo_list.append_val(new GitRepo( gitpath ));
278                 ix = GitRepo.indexOf(this.repos,  cmd.gitpath);
279             }
280             
281
282             //if (typeof(repo_list[gitpath]) == 'undefined') {
283             //    repo_list[gitpath] = new imports.Scm.Git.Repo.Repo( { repopath : gitpath });
284             //    repo_list[gitpath].cmds = [];
285              //   repo_list[gitpath].pull();
286             //}
287             repo_list.item(ix).cmds.append_val(cmd);
288
289         }
290         this.paused = false;
291         // build add, remove and commit message list..
292         
293         for(var i = 0;i < repo_list.length;i++) {
294     
295
296             var repo = repo_list.item(i);
297
298             var add_files = new Array<GitMonitorQueue>();
299             var remove_files = new Array<GitMonitorQueue>();
300             var messages = new Array<GitMonitorQueue>();
301             //print(JSON.stringify(repo.cmds,null,4));
302             
303             for(var ii = 0;ii < repo.cmds.length;ii++) {
304                 var cmd = repo.cmds.item(ii);
305     
306                 
307                 switch(cmd.action) {
308                     case "add" :
309                         
310                         if (GitMonitorQueue.indexOfAdd(add_files, cmd.add) > -1) {
311                            break;
312                         }
313         
314                         
315                         add_files.append_val(cmd);
316                         break;
317                     
318                     case "rm":
319                         if (GitMonitorQueue.indexOfAdd(add_files, cmd.rm) > -1 ) {
320                            break;
321                         }
322                         
323                         // if file exists, do not try and delete it.
324                         if (GLib.file_test(cmd.rm, GLib.FileTest.EXISTS)) {
325                             break;
326                         }
327                         
328                         remove_files.append_val(cmd);
329                         break;
330                     
331                     case "commit" :
332                         if (GitMonitorQueue.indexOfMessage(messages, cmd.message) > -1 ) {
333                            break;
334                         }
335                          
336                         messages.append_val(cmd);
337                         
338                         break;
339                     default:
340                         print("Opps unmatched action");
341                         break;
342                 } 
343             }
344             
345             //repo.debug = 1;
346             // these can fail... at present... as we wildcard stuff.
347             stdout.printf("ADD : %d files"  , add_files.length);
348             
349             // make sure added files do not get removed..
350
351             var remove_files_f = new Array<GitMonitorQueue>();
352             for(var ii = 0;ii < remove_files.length;ii++) {
353                 if (GitMonitorQueue.indexOfAdd(add_files,  remove_files.item(ii).rm) > -1 ) {
354                      continue;
355                 }
356                 remove_files_f.append_val(remove_files.item(ii));
357             };
358             stdout.printf("REMOVE : %d files"  , remove_files.length);
359              
360             // make sure monitoring is paused so it does not recursively pick up
361             // deletions
362             
363             // -- DO STUFF..
364             
365             repo.add(add_files);
366             repo.remove(remove_files_f);
367             this.paused = false;
368             
369             
370             try { 
371                 success += repo.commit(
372                     GitMonitorQueue.messageToString(messages),
373                     add_files  
374                 );
375                 success += repo.push();
376
377             } catch(Error e) {
378                 failure += e.message;
379                 
380             }   
381         }
382         
383         // finally merge all the commit messages.
384          
385         try {
386             // catch notification failures.. so we can carry on..
387             if (success.length) {
388
389                 
390                 var notification = new Notify.Notification(
391                     "Git Live Commited",
392                     string.joinv("\n",success),
393                      "dialog-information"
394                     
395                 );
396     
397                 notification.set_timeout(5);
398                 notification.show();   
399             }
400             
401             if (failure.length) {
402
403                 var notification = new Notify.Notification({
404                     summary: "Git Live ERROR!!",
405                     string.joinv("\n",failure),
406                     "dialog-information"
407                     
408                 });
409     
410                 notification.set_timeout(5); // show errros for longer
411                 notification.show();   
412             }
413         } catch(Error e) {
414             print(e.message);
415             
416         }
417         this.queueRunning = false;
418     }
419     
420
421
422     
423
424     //string[] just_created;
425  
426  
427
428
429
430    
431
432
433     public void onChanged(MonitorNamePathDir src) 
434     { 
435         return; // always ignore this..?
436         //this.parsePath(src);
437     }
438     
439
440  
441     /**
442      *  results in  git add  + git commit..
443      *
444      */
445     public void onChangesDoneHint(MonitorNamePathDir src)  
446     { 
447         
448         if (this.paused) {
449             return true;
450         }
451             
452
453         this.lastAdd = new DateTime.now(); 
454         var cmd = new GitMonitorQueue(src);
455         if (cmd.shouldIgnore()) {
456             return;
457         }
458         
459        
460         var add_it = false;
461         /*
462         if (this.is_just_created(cmd.path)) {
463             
464         if (typeof(this.just_created[src.path]) !='undefined') {
465             delete this.just_created[src.path];
466             
467             this.queue.push( 
468                 [ src.gitpath,  'add', src.vpath ],
469                 [ src.gitpath,  'commit',    { message: src.vpath} ] 
470                 
471             );
472          
473             return;
474         }
475         */
476         cmd.add = src.vpath;
477         this.queue.append_val(cmd);
478
479         var cmd = new GitMonitorQueue(src);
480         cmd.action = "commit";
481         cmd.message = src.vpath;
482         this.queue.append_val(cmd);
483  
484          
485     }
486     public void onDeleted(MonitorNamePathDir src) 
487    { 
488         if (this.paused) {
489             return true;
490         }
491         this.lastAdd = new DateTime.now(); 
492         var cmd = new GitMonitorQueue(src);
493         if (cmd.shouldIgnore()) {
494             return;
495         }
496         // should check if monitor needs removing..
497         // it should also check if it was a directory.. - so we dont have to commit all..
498         cmd.action = "rm";
499         cmd.rm = src.vpath;
500         this.queue.append_val(cmd);
501
502         var cmd = new GitMonitorQueue(src);
503         cmd.action = "commit";
504         cmd.message = src.vpath;
505         cmd.commit_all = true;
506
507         this.queue.append_val(cmd);
508  
509     }
510     public void onCreated(MonitorNamePathDir src) {
511
512         if (this.paused) {
513             return true;
514         }
515         this.lastAdd = new DateTime.now(); 
516         var cmd = new GitMonitorQueue(src);
517         if (cmd.shouldIgnore()) {
518             return;
519         }
520
521         if (!GLib.file_test(src.path, GLib.FileTest.IS_DIR)) {
522            // this.just_created[src.path] = true;
523             return; // we do not handle file create flags... - use done hint.
524         }
525         // directory has bee created
526         this.monitor(src.path);
527         this.top.append_val(src.path);
528         this.monitor(src.path );
529
530
531 // -- no point in adding a dir.. as git does not handle them...
532 //        this.queue.push( 
533   //          [ src.gitpath, 'add' , src.vpath,  { all: true } ],
534  //           [ src.gitpath, 'commit' , { all: true, message: src.vpath} ]
535   //          
536    //     );
537
538     }
539
540     public void onAttributeChanged(MonitorNamePathDir src) { 
541
542         if (this.paused) {
543             return true;
544         }
545         this.lastAdd = new DateTime.now(); 
546         var cmd = new GitMonitorQueue(src);
547         if (cmd.shouldIgnore()) {
548             return;
549         }
550         cmd.action = "add";
551         cmd.add = src.vpath;
552         this.queue.append_val(cmd);
553
554         var cmd = new GitMonitorQueue(src);
555         cmd.action = "commit";
556         cmd.message = "Attribute changed " + cmd.vpath;
557         this.queue.append_val(cmd);
558     }
559
560
561    public void onMoved(MonitorNamePathDir src,MonitorNamePathDir dest)  
562     { 
563         this.lastAdd = new DateTime.now(); 
564         var cmd_s = new GitMonitorQueue(src);
565
566         var cmd_d = new GitMonitorQueue(src);
567    
568         
569         if (cmd_d.gitpath != cmd_s.gitpath) {
570             this.onDeleted(src);
571             this.onCreated(dest);
572             this.onChangedDoneHint(dest);
573             return;
574         }
575         // needs to handle move to/from unsupported types..
576         
577         if (this.shouldIgnore(src)) {
578             this.onCreated(dest);
579             this.onChangedDoneHint(dest);
580             return;
581
582         }
583         if (this.shouldIgnore(dest)) {
584             
585             this.onDeleted(src);
586  
587
588             return;
589         }
590         
591         cmd_s.action = "rm";
592         cmd_s.rm = src.vpath;
593         this.queue.append_val(cmd_s);
594
595
596
597
598         cmd_d.action = "add";
599         cmd_d.add = src.vpath;
600         this.queue.append_val(cmd_d);
601
602
603         var cmd = new GitMonitorQueue(dest);
604         cmd.action = "commit";
605         cmd.message = "MOVED " + cmd_s.vpath + " to " + cmd_d.vpath;
606         this.queue.append_val(cmd);
607
608
609          
610     }
611        
612 }