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