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