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