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()
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
163               
164                 var notification = new Notify.Notification(
165                     "Git Live",
166                     "%s\nMonitoring %u Directories".printf(GitMonitor.gitlive, this.monitors.length), 
167                      "dialog-information"
168                 );
169         
170                 notification.set_timeout(5);
171                 notification.show();
172             } catch(Error e) {
173                 print(e.message);
174             }
175
176         });
177         
178         Timeout.add_full(Priority.LOW, 1000, () => {
179             //TIMEOUT", _this.queue.length , _this.queueRunning].join(', '));
180             if (this.queue.length < 1  || this.queueRunning) {
181                 return true;
182             }
183
184             var last = this.lastAdd.difference(new DateTime.now(new TimeZone.local()));
185
186             
187             //print("LAST RUN?" + last);
188             
189             if (last < 5 * TimeSpan.SECOND) { // wait 5 seconds before running. ????
190                 return true;
191             }
192             //_this.lastAdd = new Date();
193             //return 1;
194         
195             this.runQueue();
196             return true;
197         });
198         
199       
200     }
201
202
203     public new void stop() {
204         StatusIconA.statusicon.set_from_stock( Gtk.Stock.MEDIA_PAUSE );;
205         base.stop();
206     }
207     
208     
209     public new void monitor (string path,  int depth = 0)
210     {
211         
212         //var depth = typeof(depth) == 'number'  ? depth *1 : 0;
213         
214          
215         // if we are not at top level.. and there is a .git directory  (it's a submodule .. ignore) 
216         if (depth > 1 && FileUtils.test(path + "/.git" , FileTest.IS_DIR)) {
217             return;
218         }
219         
220         if (depth == 1) {
221             // FIXME - check if repo is flagged as not autocommit..
222             //var repo = imports.Scm.Repo.Repo.get(path);
223             //if (!repo || !repo.autocommit()) {
224             //    return;
225             //} 
226         }
227         
228         
229         // check if the repo is to be monitored.
230         //print("PATH : " + path);
231         
232         
233         base.monitor(path, depth);
234     }
235
236     
237
238     /**
239      * run the queue.
240      * - pulls the items off the queue 
241      *    (as commands run concurrently and new items may get added while it's running)
242      * - runs the queue items
243      * - pushes upstream.
244      * 
245      */
246     public void runQueue()
247     {
248         
249         if (this.paused) {
250             return;
251         }
252         this.queueRunning = true;
253
254         var cmds = new Array<GitMonitorQueue>();
255         for(var i = 0; i < this.queue.length; i++) {
256             cmds.append_val(this.queue.index(i));
257         }
258
259         this.queue = new Array<GitMonitorQueue>();// empty queue!
260
261         
262         string[] success = {};
263         string[] failure = {};
264        //var repos = new Array<GitRepo>(); //??
265         var done = new Array<GitMonitorQueue>();
266         
267         // first build a array of repo's to work with
268         var repo_list = new Array<GitRepo>();
269         
270         // pull and group.
271         
272         //print(JSON.stringify(cmds));
273         this.paused = true;
274         
275         for(var i = 0; i < cmds.length; i++) {
276            
277             var cmd = cmds.index(i);
278         
279             var gitpath = cmd.gitpath; 
280             var ix  = GitRepo.indexOf(repo_list,  cmd.gitpath);
281             if (ix < 0) {
282                 repo_list.append_val(new GitRepo( gitpath ));
283                 ix = GitRepo.indexOf(repo_list,  cmd.gitpath);
284             }
285             
286
287             //if (typeof(repo_list[gitpath]) == 'undefined') {
288             //    repo_list[gitpath] = new imports.Scm.Git.Repo.Repo( { repopath : gitpath });
289             //    repo_list[gitpath].cmds = [];
290              //   repo_list[gitpath].pull();
291             //}
292             repo_list.index(ix).cmds.append_val(cmd);
293
294         }
295         this.paused = false;
296         // build add, remove and commit message list..
297         
298         for(var i = 0;i < repo_list.length;i++) {
299     
300
301             var repo = repo_list.index(i);
302
303             var add_files = new Array<GitMonitorQueue>();
304             var remove_files = new Array<GitMonitorQueue>();
305             var messages = new Array<GitMonitorQueue>();
306             //print(JSON.stringify(repo.cmds,null,4));
307             
308             for(var ii = 0;ii < repo.cmds.length;ii++) {
309                 var cmd = repo.cmds.index(ii);
310     
311                 
312                 switch(cmd.action) {
313                     case "add" :
314                         
315                         if (GitMonitorQueue.indexOfAdd(add_files, cmd.vname) > -1) {
316                            break;
317                         }
318         
319                         
320                         add_files.append_val(cmd);
321                         break;
322                     
323                     case "rm":
324                         if (GitMonitorQueue.indexOfAdd(add_files, cmd.vname) > -1 ) {
325                            break;
326                         }
327                         
328                         // if file exists, do not try and delete it.
329                         if (FileUtils.test(cmd.vname, FileTest.EXISTS)) {
330                             break;
331                         }
332                         
333                         remove_files.append_val(cmd);
334                         break;
335                     
336                     case "commit" :
337                         if (GitMonitorQueue.indexOfMessage(messages, cmd.message) > -1 ) {
338                            break;
339                         }
340                          
341                         messages.append_val(cmd);
342                         
343                         break;
344                     default:
345                         print("Opps unmatched action");
346                         break;
347                 } 
348             }
349             
350             //repo.debug = 1;
351             // these can fail... at present... as we wildcard stuff.
352             stdout.printf("ADD : %u files"  , add_files.length);
353             
354             // make sure added files do not get removed..
355
356             var remove_files_f = new Array<GitMonitorQueue>();
357             for(var ii = 0;ii < remove_files.length;ii++) {
358                 if (GitMonitorQueue.indexOfAdd(add_files,  remove_files.index(ii).vname) > -1 ) {
359                      continue;
360                 }
361                 remove_files_f.append_val(remove_files.index(ii));
362             };
363             stdout.printf("REMOVE : %u files"  , remove_files.length);
364              
365             // make sure monitoring is paused so it does not recursively pick up
366             // deletions
367             
368             // -- DO STUFF..
369             try {
370                 repo.add(add_files);
371             } catch(Error e) {
372                 failure +=  e.message;
373             }  
374             try {
375                  repo.remove(remove_files_f);
376             } catch(Error e) {
377                 failure +=  e.message;
378             }  
379
380             this.paused = false;
381             
382             
383             try { 
384                 success += repo.commit(
385                     GitMonitorQueue.messageToString(messages),
386                     add_files  
387                 );
388                 success += repo.push();
389
390             } catch(Error e) {
391                 failure += e.message;
392                 
393             }   
394         }
395         
396         // finally merge all the commit messages.
397          
398         try {
399             // catch notification failures.. so we can carry on..
400             if (success.length > 0) {
401
402                 
403                 var notification = new Notify.Notification(
404                     "Git Live Commited",
405                     string.joinv("\n",success),
406                      "dialog-information"
407                     
408                 );
409     
410                 notification.set_timeout(5);
411                 notification.show();   
412             }
413             
414             if (failure.length > 0) {
415
416                 var notification = new Notify.Notification(
417                       "Git Live ERROR!!",
418                     string.joinv("\n",failure),
419                     "dialog-information"
420                     
421                 );
422     
423                 notification.set_timeout(5); // show errros for longer
424                 notification.show();   
425             }
426         } catch(Error e) {
427             print(e.message);
428             
429         }
430         this.queueRunning = false;
431     }
432     
433
434
435     
436
437     //string[] just_created;
438  
439  
440
441
442
443    
444
445
446     public new void onChanged(MonitorNamePathDir src) 
447     { 
448         return; // always ignore this..?
449         //this.parsePath(src);
450     }
451     
452
453  
454     /**
455      *  results in  git add  + git commit..
456      *
457      */
458     public new void onChangesDoneHint(MonitorNamePathDir src)  
459     { 
460         
461         if (this.paused) {
462             return;
463         }
464             
465
466         this.lastAdd = new DateTime.now(new TimeZone.local()); 
467         var cmd = new GitMonitorQueue(src);
468         if (cmd.shouldIgnore()) {
469             return;
470         }
471         
472        
473         var add_it = false;
474         /*
475         if (this.is_just_created(cmd.path)) {
476             
477         if (typeof(this.just_created[src.path]) !='undefined') {
478             delete this.just_created[src.path];
479             
480             this.queue.push( 
481                 [ src.gitpath,  'add', src.vpath ],
482                 [ src.gitpath,  'commit',    { message: src.vpath} ] 
483                 
484             );
485          
486             return;
487         }
488         */
489
490         this.queue.append_val(cmd);
491
492         cmd = new GitMonitorQueue(src);
493         cmd.action = "commit";
494         cmd.message = cmd.vname;
495         this.queue.append_val(cmd);
496  
497          
498     }
499     public new void onDeleted(MonitorNamePathDir src) 
500    { 
501         if (this.paused) {
502             return;
503         }
504         this.lastAdd = new DateTime.now(new TimeZone.local()); 
505         var cmd = new GitMonitorQueue(src);
506         if (cmd.shouldIgnore()) {
507             return;
508         }
509         // should check if monitor needs removing..
510         // it should also check if it was a directory.. - so we dont have to commit all..
511         cmd.action = "rm";
512         cmd.rm = cmd.vname;
513         this.queue.append_val(cmd);
514
515         var cmd = new GitMonitorQueue(src);
516         cmd.action = "commit";
517         cmd.message = cmd.vname;
518         cmd.commit_all = true;
519
520         this.queue.append_val(cmd);
521  
522     }
523     public new void onCreated(MonitorNamePathDir src) {
524
525         if (this.paused) {
526             return;
527         }
528         this.lastAdd = new DateTime.now(new TimeZone.local()); 
529         var cmd = new GitMonitorQueue(src);
530         if (cmd.shouldIgnore()) {
531             return;
532         }
533
534         if (!FileUtils.test(src.path, GLib.FileTest.IS_DIR)) {
535            // this.just_created[src.path] = true;
536             return; // we do not handle file create flags... - use done hint.
537         }
538         // directory has bee created
539         this.monitor(src.path);
540         this.top.append_val(src.path);
541         this.monitor(src.path );
542
543
544 // -- no point in adding a dir.. as git does not handle them...
545 //        this.queue.push( 
546   //          [ src.gitpath, 'add' , src.vpath,  { all: true } ],
547  //           [ src.gitpath, 'commit' , { all: true, message: src.vpath} ]
548   //          
549    //     );
550
551     }
552
553     public new void onAttributeChanged(MonitorNamePathDir src) { 
554
555         if (this.paused) {
556             return;
557         }
558         this.lastAdd = new DateTime.now(new TimeZone.local()); 
559         var cmd = new GitMonitorQueue(src);
560         if (cmd.shouldIgnore()) {
561             return;
562         }
563         cmd.action = "add";
564         this.queue.append_val(cmd);
565
566         var cmd = new GitMonitorQueue(src);
567         cmd.action = "commit";
568         cmd.message = "Attribute changed " + cmd.vpath;
569         this.queue.append_val(cmd);
570     }
571
572
573    public new void onMoved(MonitorNamePathDir src,MonitorNamePathDir dest)  
574     { 
575         if (this.paused) {
576             return;
577         }
578         this.lastAdd = new DateTime.now(new TimeZone.local()); 
579         var cmd_s = new GitMonitorQueue(src);
580
581         var cmd_d = new GitMonitorQueue(src);
582    
583         
584         if (cmd_d.gitpath != cmd_s.gitpath) {
585             this.onDeleted(src);
586             this.onCreated(dest);
587             this.onChangedDoneHint(dest);
588             return;
589         }
590         // needs to handle move to/from unsupported types..
591         
592         if (this.shouldIgnore(src)) {
593             this.onCreated(dest);
594             this.onChangedDoneHint(dest);
595             return;
596
597         }
598         if (this.shouldIgnore(dest)) {
599             
600             this.onDeleted(src);
601  
602
603             return;
604         }
605         
606         cmd_s.action = "rm";
607         this.queue.append_val(cmd_s);
608
609
610
611
612         cmd_d.action = "add";
613         this.queue.append_val(cmd_d);
614
615
616         var cmd = new GitMonitorQueue(dest);
617         cmd.action = "commit";
618         cmd.message = "MOVED " + cmd_s.vpath + " to " + cmd_d.vpath;
619         this.queue.append_val(cmd);
620
621
622          
623     }
624        
625 }