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