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