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                 GLib.debug("GitMonitor.runQueue\n");
228
229                 this.queueRunning = true;
230
231                 var cmds = new Gee.ArrayList<GitMonitorQueue>();
232
233                 for(var i = 0; i < this.queue.size; i++) {
234                         cmds.add(this.queue.get(i));
235                 }
236
237                 this.queue = new Gee.ArrayList<GitMonitorQueue>();// empty queue!
238  
239                 string[] success = {};
240                 string[] failure = {};
241            //var repos = new Array<GitRepo>(); //??
242                 //var done = new Array<GitMonitorQueue>();
243                 
244                 // first build a array of repo's to work with
245                 var repo_list = new Array<GitRepo>();
246                 
247                 // pull and group.
248                 
249                 //print(JSON.stringify(cmds));
250                 // make sure nothing get's added to the queue where we are doing this..
251
252                 this.paused = true;
253
254                 var leave_queued = new Gee.ArrayList<GitMonitorQueue>();
255                 GLib.debug("GitMonitor.runQueue - creating repos");
256                 
257                 for(var i = 0; i < cmds.size; i++) {
258                    
259                         var cmd = cmds.get(i);
260                 
261                         var gitpath = cmd.gitpath; 
262                         
263                         var repo = GitRepo.get( gitpath );
264                         if ( !repo.is_wip_branch()) {
265                                 leave_queued.add(cmd);
266                                 continue;
267                         }
268                         
269                         GLib.debug("GitMonitor.runQueue - finding %s", cmd.gitpath);
270                 
271                         var ix  = GitRepo.indexOf(repo_list,   gitpath);
272                         if (ix < 0) {
273                                 repo_list.append_val( GitRepo.get( gitpath ));
274                                 ix = GitRepo.indexOf(repo_list,  cmd.gitpath);
275                         }
276                         GLib.debug("GitMonitor.runQueue - adding to repolist %d", ix);
277
278                         //if (typeof(repo_list[gitpath]) == 'undefined') {
279                         //    repo_list[gitpath] = new imports.Scm.Git.Repo.Repo( { repopath : gitpath });
280                         //    repo_list[gitpath].cmds = [];
281                          //   repo_list[gitpath].pull();
282                         //}
283                         repo_list.index(ix).cmds.add(cmd);
284
285                 }
286                 this.queue = leave_queued;
287                 
288                 this.paused = false;
289                 // build add, remove and commit message list..
290
291                 GLib.debug("GitMonitor.runQueue - creating actions");
292                 
293                 for(var i = 0;i < repo_list.length;i++) {
294          
295                         var repo = repo_list.index(i);
296
297                         var add_files = new Gee.ArrayList<GitMonitorQueue>();
298                         var add_files_f = new Gee.ArrayList<GitMonitorQueue>();
299                         var remove_files = new Gee.ArrayList<GitMonitorQueue>();
300                         var messages = new Gee.ArrayList<GitMonitorQueue>();
301                         //print(JSON.stringify(repo.cmds,null,4));
302                         
303                         for(var ii = 0;ii < repo.cmds.size;ii++) {
304                                 var cmd = repo.cmds.get(ii);
305         
306                                 if (repo.is_ignore(cmd.vname)) {
307                                         continue;
308                                 }
309         
310                                 
311                                 switch(cmd.action) {
312                                         case "add" :
313                                                 
314                                                 if (GitMonitorQueue.indexOfAdd(add_files, cmd.vname) > -1) {
315                                                    break;
316                                                 }
317                                                 
318                                                 add_files.add(cmd);
319                                                 break;
320                                         
321                                         case "rm":
322                                                 if (GitMonitorQueue.indexOfAdd(remove_files, cmd.vname) > -1 ) {
323                                                    break;
324                                                 }
325                                                 
326                                                 // if file exists, do not try and delete it.
327                                                 if (FileUtils.test(cmd.fullpath(), FileTest.EXISTS)) {
328                                                         break;
329                                                 }
330                                                 
331                                                 remove_files.add(cmd);
332                                                 break;
333                                         
334                                         case "commit" :
335                                                 if (GitMonitorQueue.indexOfMessage(messages, cmd.message) > -1 ) {
336                                                    break;
337                                                 }
338                                                 messages.add(cmd);
339                                                 
340                                                 break;
341                                         default:
342                                                 stdout.printf("Opps unmatched action %s\n", cmd.action);
343                                                 break;
344                                 } 
345                         }
346                         
347                         repo.cmds.clear(); // reset the repo's command list..
348                         
349                         GLib.debug( "ADD : %s", GitMonitorQueue.queueArrayToString(add_files));
350                         GLib.debug( "REMOVE FILES: %s", GitMonitorQueue.queueArrayToString(remove_files));
351                         
352                         //repo.debug = 1;
353                         // these can fail... at present... as we wildcard stuff.
354                    
355                         // make sure added files do not get removed.. ?? 
356                         /*
357                         var remove_files_f = new Array<GitMonitorQueue>();
358                         for(var ii = 0;ii < remove_files.length;ii++) {
359                                 if (GitMonitorQueue.indexOfAdd(add_files,  remove_files.index(ii).vname) > -1 ) {
360                                          continue;
361                                 }
362                                 remove_files_f.append_val(remove_files.index(ii));
363                         };
364                         stdout.printf("REMOVE : %u files\n"  , remove_files_f.length);
365                         */
366                         
367                         // if file was added, then removed, 
368                         var remove_files_f = new Gee.ArrayList<GitMonitorQueue>();
369                         for(var ii = 0;ii < remove_files.size;ii++) {
370                                 
371                                 
372                                 
373                                 if (GitMonitorQueue.indexOfAdd(add_files,  remove_files.get(ii).vname) > -1 ) {
374                                         // in add and remove - do not remvove
375                                         continue;
376                                 }
377                                 remove_files_f.add(remove_files.get(ii));
378                         };
379                         for(var ii = 0;ii < add_files.size;ii++) {
380                                 if (GitMonitorQueue.indexOfAdd(remove_files,  add_files.get(ii).vname) > -1 ) {
381                                         // the add file is in the remove list, and it does not exist - do not add it..
382                                         print("check exists ? %s\n",add_files.get(ii).fullpath());
383                                         
384                                         if (!FileUtils.test(add_files.get(ii).fullpath(), FileTest.EXISTS)) {
385                                                         continue;
386                                         }
387                                          
388                                 } 
389                                 
390                                 add_files_f.add(add_files.get(ii));
391                         };
392                         
393                         GLib.debug( "ADD : %s", GitMonitorQueue.queueArrayToString(add_files_f));
394                         GLib.debug( "REMOVE FILES: %s", GitMonitorQueue.queueArrayToString(remove_files_f));
395                     
396                     if (add_files_f.size < 1 && remove_files_f.size < 1) {
397                                 continue;
398                     }
399                     
400                         // make sure monitoring is paused so it does not recursively pick up
401                         // deletions
402                         this.paused = true; 
403                           
404                           
405                         try {
406                                 
407                                 repo.pull();
408                         } catch(Error e) {
409                                 this.pauseError(e.message);
410                                 //failure +=  e.message;
411                                 //print("Pull failed:\n");
412                                 return;
413                         }
414                                                                         
415                         // -- DO STUFF..            
416                         try {
417                                 repo.add(add_files_f);
418                         } catch(Error e) {
419                                 this.pauseError(e.message);
420                                 return;
421                                 failure +=  e.message;
422                                 GLib.debug("Add failed:");
423                         }  
424                         try {
425                                  repo.remove(remove_files_f);
426                         } catch(Error e) {
427                                 this.pauseError(e.message);
428                                 return;
429                                 failure +=  e.message;
430                                 GLib.debug("Remove failed:");
431                         }  
432  
433                         try { 
434                                 success += repo.commit(
435                                         GitMonitorQueue.messageToString(messages),
436                                         add_files_f
437                                 );
438                                 success += repo.push();
439
440
441                         } catch(Error e) {
442                         
443                                 // if the error is 'nothing to commit, working tree clean'
444                                 // then it's not an error, - just continue;
445                                 if (/nothing to commit, working tree clean/.match(e.message)) {
446                                         GLib.debug("%s",e.message);
447                                         success += e.message;
448                                         this.paused = false; 
449                                         continue;
450                                 }
451                                 
452                         
453                         
454                                 this.paused = false;                    
455                                 this.pauseError(e.message);
456                                 
457                                 return;
458                                 //failure += e.message;
459                                 //print("Push failed:\n");
460                                 
461                         }   
462                         this.paused = false;                                            
463                 }
464                 
465                 
466                 // finally merge all the commit messages.
467                  
468                 try {
469                         // catch notification failures.. so we can carry on..
470                         if (success.length > 0) {
471
472                                 
473                                 var notification = new Notify.Notification(
474                                         "Git Live Commited",
475                                         string.joinv("\n",success),
476                                          "dialog-information"
477                                         
478                                 );
479         
480                                 notification.set_timeout(5);
481                                 notification.show();   
482                         }
483                         
484                         //if (failure.length > 0) {
485
486                                 // should never get this far...
487                         //      this.pauseError();   
488                         //}
489                 } catch(Error e) {
490                         GLib.debug(e.message);
491                         
492                 }
493                 this.queueRunning = false;
494         }
495         
496
497
498         
499
500         //string[] just_created;
501  
502  
503
504
505
506    
507
508
509         public override  void onChanged(MonitorNamePathDir src) 
510         { 
511                 //print("GitMonitor.onChanged\n");        
512                 return; // always ignore this..?
513                 //this.parsePath(src);
514         }
515         
516
517  
518         /**
519          *  results in  git add  + git commit..
520          *
521          */
522         public override void onChangesDoneHint(MonitorNamePathDir src)  
523         { 
524
525                 if (this.paused) {
526                         return;
527                 }
528                 GLib.debug("GitMonitor.onChangedHint");                         
529
530                 this.lastAdd = new DateTime.now(new TimeZone.local()); 
531                 var cmd = new GitMonitorQueue(src);
532                 if (cmd.shouldIgnore()) {
533                         GLib.debug("GitMonitor.onChangedHint - ignored");
534                         return;
535                 }
536                 
537                 //var add_it = false;
538                 /*
539                 if (this.is_just_created(cmd.path)) {
540                         
541                 if (typeof(this.just_created[src.path]) !='undefined') {
542                         delete this.just_created[src.path];
543                         
544                         this.queue.push( 
545                                 [ src.gitpath,  'add', src.vpath ],
546                                 [ src.gitpath,  'commit',    { message: src.vpath} ] 
547                                 
548                         );
549                  
550                         return;
551                 }
552                 */
553                 cmd.action = "add";
554                 this.queue.add(cmd);
555
556                 cmd = new GitMonitorQueue(src);
557                 cmd.action = "commit";
558                 cmd.message = cmd.vname;
559                 this.queue.add(cmd);
560                 
561                 
562                  if (!cmd.shouldIgnore() && !cmd.repo.is_wip_branch()) {
563                 NewBranch.singleton().show(cmd.repo);
564          }
565  
566                  
567         }
568         public override  void onDeleted(MonitorNamePathDir src) 
569    { 
570
571                 if (this.paused) {
572                         return;
573                 }
574                 GLib.debug("GitMonitor.onDeleted");                     
575                 this.lastAdd = new DateTime.now(new TimeZone.local()); 
576                 var cmd = new GitMonitorQueue(src);
577                 if (cmd.shouldIgnore()) {
578                         return;
579                 }
580                 // should check if monitor needs removing..
581                 // it should also check if it was a directory.. - so we dont have to commit all..
582                 cmd.action = "rm";
583                 this.queue.add(cmd);
584
585                 cmd = new GitMonitorQueue(src);
586                 cmd.action = "commit";
587                 cmd.message = cmd.vname;
588                 cmd.commit_all = true;
589
590                 this.queue.add(cmd);
591                 
592                  if (!cmd.shouldIgnore() && !cmd.repo.is_wip_branch()) {
593                         NewBranch.singleton().show(cmd.repo);
594                 }
595                 
596  
597         }
598         public override  void onCreated(MonitorNamePathDir src) {
599
600                 if (this.paused) {
601                         return;
602                 }
603                 GLib.debug("GitMonitor.onCreated");                     
604                 this.lastAdd = new DateTime.now(new TimeZone.local()); 
605                 var cmd = new GitMonitorQueue(src);
606                 if (cmd.shouldIgnore()) {
607                         return;
608                 }
609
610                 if (!FileUtils.test(src.path, GLib.FileTest.IS_DIR)) {
611                    // this.just_created[src.path] = true;
612                         return; // we do not handle file create flags... - use done hint.
613                 }
614                 // directory has bee created
615                 this.monitor(src.path);
616                 //this.top.append_val(src.path);
617                 //this.monitor(src.path );
618
619
620 // -- no point in adding a dir.. as git does not handle them...
621 //        this.queue.push( 
622   //          [ src.gitpath, 'add' , src.vpath,  { all: true } ],
623  //           [ src.gitpath, 'commit' , { all: true, message: src.vpath} ]
624   //          
625    //     );
626
627         }
628
629         public  override void onAttributeChanged(MonitorNamePathDir src) 
630         { 
631
632                 if (this.paused) {
633                         return;
634                 }
635                 GLib.debug("GitMonitor.onAttributeChanged %s", src.name);                       
636                 if (src.dir == GitMonitor.gitlive) {
637                    return; // attribute on top level..
638                 }
639                 
640                 this.lastAdd = new DateTime.now(new TimeZone.local()); 
641                 var cmd = new GitMonitorQueue(src);
642                 if (cmd.shouldIgnore()) {
643                         return;
644                 }
645                 cmd.action = "add";
646                 this.queue.add(cmd);
647
648                 cmd = new GitMonitorQueue(src);
649                 cmd.action = "commit";
650                 cmd.message = "Attribute changed " + cmd.vname;
651                 this.queue.add(cmd);
652                 
653                 if (!cmd.shouldIgnore() && !cmd.repo.is_wip_branch()) {
654                         NewBranch.singleton().show(cmd.repo);
655         }
656         }
657  
658    public  override void onMoved(MonitorNamePathDir src,MonitorNamePathDir dest)  
659         { 
660
661                 if (this.paused) {
662                         return;
663                 }
664                 GLib.debug("GitMonitor.onMoved");                       
665                 this.lastAdd = new DateTime.now(new TimeZone.local()); 
666                 var cmd_s = new GitMonitorQueue(src);
667
668                 var cmd_d = new GitMonitorQueue(dest);
669    
670                 
671                 if (cmd_d.gitpath != cmd_s.gitpath) {
672                         this.onDeleted(src);
673                         this.onCreated(dest);
674                         this.onChangesDoneHint(dest);
675                         return;
676                 }
677                 // needs to handle move to/from unsupported types..
678                 
679                 if (cmd_s.shouldIgnore()) {
680                         this.onCreated(dest);
681                         this.onChangesDoneHint(dest);
682                         return;
683
684                 }
685                 if (cmd_d.shouldIgnore()) {
686                         
687                         this.onDeleted(src);
688  
689
690                         return;
691                 }
692                 
693                 
694                 GLib.debug("RM: %s", cmd_s.vname);
695                 cmd_s.action = "rm";
696                 this.queue.add(cmd_s);
697
698                 
699                 
700                 GLib.debug("ADD: %s", cmd_d.vname);
701                 cmd_d.action = "add";
702                 this.queue.add(cmd_d);
703
704
705                 var cmd = new GitMonitorQueue(dest);
706                 cmd.action = "commit";
707                 cmd.message = "MOVED " + cmd_s.vname + " to " + cmd_d.vname;
708                 if (GitMonitorQueue.queueHas(this.queue, cmd_s, "add")) {
709                         cmd.message = cmd_d.vname;
710                 }
711                 
712                 
713                 this.queue.add(cmd);
714
715                 if (!cmd.shouldIgnore() && !cmd.repo.is_wip_branch()) {
716                         NewBranch.singleton().show(cmd.repo);
717         }
718         // this is where it get's complicated...
719         // as we might be moving across repo's...
720         
721         
722         
723                  
724         }
725            
726 }