GitMonitor.vala
[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 GitMonitorQueue.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                     // in add and remove - do not remvove
440                     continue;
441                 }
442                 add_files_f.append_val(add_files.index(ii));
443             };
444             
445             print( "ADD : %s\n", GitMonitorQueue.queueArrayToString(add_files_f));
446             print( "REMOVE FILES: %s\n", GitMonitorQueue.queueArrayToString(remove_files_f));
447            
448             
449             // make sure monitoring is paused so it does not recursively pick up
450             // deletions
451             try {
452                 repo.pull();
453             } catch(Error e) {
454                 failure +=  e.message;
455             }
456             
457             
458             // -- DO STUFF..            
459             try {
460                 repo.add(add_files_f);
461             } catch(Error e) {
462                 failure +=  e.message;
463             }  
464             try {
465                  repo.remove(remove_files_f);
466             } catch(Error e) {
467                 failure +=  e.message;
468             }  
469
470             this.paused = false;
471             
472             
473             try { 
474                 success += repo.commit(
475                     GitMonitorQueue.messageToString(messages),
476                     add_files_f
477                 );
478                 success += repo.push();
479
480             } catch(Error e) {
481                 failure += e.message;
482                 
483             }   
484         }
485         
486         // finally merge all the commit messages.
487          
488         try {
489             // catch notification failures.. so we can carry on..
490             if (success.length > 0) {
491
492                 
493                 var notification = new Notify.Notification(
494                     "Git Live Commited",
495                     string.joinv("\n",success),
496                      "dialog-information"
497                     
498                 );
499     
500                 notification.set_timeout(5);
501                 notification.show();   
502             }
503             
504             if (failure.length > 0) {
505
506                 var notification = new Notify.Notification(
507                       "Git Live ERROR!!",
508                     string.joinv("\n",failure),
509                     "dialog-information"
510                     
511                 );
512     
513                 notification.set_timeout(5); // show errros for longer
514                 notification.show();   
515             }
516         } catch(Error e) {
517             print(e.message);
518             
519         }
520         this.queueRunning = false;
521     }
522     
523
524
525     
526
527     //string[] just_created;
528  
529  
530
531
532
533    
534
535
536     public override  void onChanged(MonitorNamePathDir src) 
537     { 
538         print("GitMonitor.onChanged\n");        
539         return; // always ignore this..?
540         //this.parsePath(src);
541     }
542     
543
544  
545     /**
546      *  results in  git add  + git commit..
547      *
548      */
549     public override void onChangesDoneHint(MonitorNamePathDir src)  
550     { 
551         print("GitMonitor.onChangedHint\n");        
552         if (this.paused) {
553             return;
554         }
555             
556
557         this.lastAdd = new DateTime.now(new TimeZone.local()); 
558         var cmd = new GitMonitorQueue(src);
559         if (cmd.shouldIgnore()) {
560             return;
561         }
562         
563        
564         //var add_it = false;
565         /*
566         if (this.is_just_created(cmd.path)) {
567             
568         if (typeof(this.just_created[src.path]) !='undefined') {
569             delete this.just_created[src.path];
570             
571             this.queue.push( 
572                 [ src.gitpath,  'add', src.vpath ],
573                 [ src.gitpath,  'commit',    { message: src.vpath} ] 
574                 
575             );
576          
577             return;
578         }
579         */
580         cmd.action = "add";
581         this.queue.append_val(cmd);
582
583         cmd = new GitMonitorQueue(src);
584         cmd.action = "commit";
585         cmd.message = cmd.vname;
586         this.queue.append_val(cmd);
587  
588          
589     }
590     public override  void onDeleted(MonitorNamePathDir src) 
591    { 
592         print("GitMonitor.onDeleted\n");        
593         if (this.paused) {
594             return;
595         }
596         this.lastAdd = new DateTime.now(new TimeZone.local()); 
597         var cmd = new GitMonitorQueue(src);
598         if (cmd.shouldIgnore()) {
599             return;
600         }
601         // should check if monitor needs removing..
602         // it should also check if it was a directory.. - so we dont have to commit all..
603         cmd.action = "rm";
604         this.queue.append_val(cmd);
605
606         cmd = new GitMonitorQueue(src);
607         cmd.action = "commit";
608         cmd.message = cmd.vname;
609         cmd.commit_all = true;
610
611         this.queue.append_val(cmd);
612  
613     }
614     public override  void onCreated(MonitorNamePathDir src) {
615         print("GitMonitor.onCreated\n");        
616         if (this.paused) {
617             return;
618         }
619         this.lastAdd = new DateTime.now(new TimeZone.local()); 
620         var cmd = new GitMonitorQueue(src);
621         if (cmd.shouldIgnore()) {
622             return;
623         }
624
625         if (!FileUtils.test(src.path, GLib.FileTest.IS_DIR)) {
626            // this.just_created[src.path] = true;
627             return; // we do not handle file create flags... - use done hint.
628         }
629         // directory has bee created
630         this.monitor(src.path);
631         //this.top.append_val(src.path);
632         //this.monitor(src.path );
633
634
635 // -- no point in adding a dir.. as git does not handle them...
636 //        this.queue.push( 
637   //          [ src.gitpath, 'add' , src.vpath,  { all: true } ],
638  //           [ src.gitpath, 'commit' , { all: true, message: src.vpath} ]
639   //          
640    //     );
641
642     }
643
644     public  override void onAttributeChanged(MonitorNamePathDir src) { 
645         print("GitMonitor.onAttributeChanged\n");        
646         if (this.paused) {
647             return;
648         }
649         if (src.dir == GitMonitor.gitlive) {
650            return; // attribute on top level..
651         }
652         
653         this.lastAdd = new DateTime.now(new TimeZone.local()); 
654         var cmd = new GitMonitorQueue(src);
655         if (cmd.shouldIgnore()) {
656             return;
657         }
658         cmd.action = "add";
659         this.queue.append_val(cmd);
660
661         cmd = new GitMonitorQueue(src);
662         cmd.action = "commit";
663         cmd.message = "Attribute changed " + cmd.vname;
664         this.queue.append_val(cmd);
665     }
666  
667    public  override void onMoved(MonitorNamePathDir src,MonitorNamePathDir dest)  
668     { 
669         print("GitMonitor.onMoved\n");        
670         if (this.paused) {
671             return;
672         }
673         this.lastAdd = new DateTime.now(new TimeZone.local()); 
674         var cmd_s = new GitMonitorQueue(src);
675
676         var cmd_d = new GitMonitorQueue(dest);
677    
678         
679         if (cmd_d.gitpath != cmd_s.gitpath) {
680             this.onDeleted(src);
681             this.onCreated(dest);
682             this.onChangesDoneHint(dest);
683             return;
684         }
685         // needs to handle move to/from unsupported types..
686         
687         if (cmd_s.shouldIgnore()) {
688             this.onCreated(dest);
689             this.onChangesDoneHint(dest);
690             return;
691
692         }
693         if (cmd_d.shouldIgnore()) {
694             
695             this.onDeleted(src);
696  
697
698             return;
699         }
700         
701         
702         print("RM: %s\n", cmd_s.vname);
703         cmd_s.action = "rm";
704         this.queue.append_val(cmd_s);
705
706         
707         
708         print("ADD: %s\n", cmd_d.vname);
709         cmd_d.action = "add";
710         this.queue.append_val(cmd_d);
711
712
713         var cmd = new GitMonitorQueue(dest);
714         cmd.action = "commit";
715         cmd.message = "MOVED " + cmd_s.vname + " to " + cmd_d.vname;
716         if (GitMonitorQueue.queueHas(this.queue, cmd_s, "add")) {
717             cmd.message = cmd_d.vname;
718         }
719         
720         this.queue.append_val(cmd);
721
722
723          
724     }
725        
726 }