gitlive.js
[gitlive] / gitlive.js
1 #!/usr/bin/seed
2 ///<script type="text/javascript">
3 /**
4 * Git Live
5
6 * inotify hooks for ~/gitlive
7 * that commit and push any changes made.
8 * Bit like a revision controled backed up file system!?
9
10
11 */
12
13 GI      = imports.gi.GIRepository
14 GLib        = imports.gi.GLib;
15
16 // we add this in, as it appears to get lost sometimes if we set it using the ENV. variable in builder.sh
17 GI.IRepository.prepend_search_path(GLib.get_home_dir() + '/.Builder/girepository-1.1');
18
19 Gio         = imports.gi.Gio;
20 Gtk         = imports.gi.Gtk;
21 Notify      = imports.gi.Notify;
22
23 Spawn       = imports.Spawn;
24 Git         = imports.Git;
25 StatusIcon  = imports.StatusIcon.StatusIcon;
26 Monitor     = imports.Monitor.Monitor;
27
28
29 //File = imports[__script_path__+'/../introspection-doc-generator/File.js'].File
30 Gtk.init (null, null);
31
32 var gitlive = GLib.get_home_dir() + "/gitlive";
33
34 if (!GLib.file_test(gitlive, GLib.FileTest.IS_DIR)) {
35     var msg = new Gtk.MessageDialog({message_type:
36         Gtk.MessageType.INFO, buttons : Gtk.ButtonsType.OK, text: "GIT Live - ~/gitlive does not exist."});
37     msg.run();
38     msg.destroy();
39     
40     Seed.quit();
41 }
42
43  
44 var monitor = new Monitor({
45     /**
46      *
47      * queue objects
48      *  action: 'add' | rm | update
49      *  repo : 'gitlive'
50      *  file : XXXXX
51      *
52      *
53      *
54      *  
55      * 
56      *
57      */
58     action_queue : [],
59     queue : [],
60     queueRunning : false,
61      
62     start: function()
63     {
64         var _this = this;
65         this.lastAdd = new Date();
66          
67         GLib.timeout_add(GLib.PRIORITY_LOW, 500, function() {
68             //TIMEOUT", _this.queue.length , _this.queueRunning].join(', '));
69             if (!_this.queue.length || _this.queueRunning) {
70                 return 1;
71             }
72             var last = Math.floor(((new Date()) - this.lastAdd) / 100);
73             if (last < 4) { // wait 1/2 a seconnd before running.
74                 return 1;
75             }
76             _this.runQueue();
77             return 1;
78         },null,null);
79         
80         Monitor.prototype.start.call(this);
81         var notification = new Notify.Notification({
82             summary: "Git Live",
83             body : gitlive + "\nMonitoring " + this.monitors.length + " Directories"
84         });
85
86         notification.set_timeout(2000);
87         notification.show();   
88     },
89     /**
90      * run the queue.
91      * - pulls the items off the queue 
92      *    (as commands run concurrently and new items may get added while it's running)
93      * - runs the queue items
94      * - pushes upstream.
95      * 
96      */
97     runQueue: function()
98     {
99         this.queueRunning = true;
100         var cmds = [];
101         //this.queue.forEach(function (q) {
102         //    cmds.push(q);
103         //});
104         
105         this.action_queue.forEach(function (q) {
106             cmds.push(q);
107         });
108         //this.queue = []; // empty queue!
109         this.action_queue = [];
110         var success = [];
111         var failure = [];
112         var repos = [];
113         var done = [];
114         cmds.forEach(function(cmd) {
115             // prevent duplicate calls..
116             if (done.indexOf(JSON.stringify(cmd)) > -1) {
117                 return;
118             }
119             done.push(cmd.join(','));
120             
121             if (repos.indexOf(cmd[0]) < 0) {
122                 repos.push(cmd[0]);
123                 Git.run(cmd[0] , 'pull'); // pull before we push!
124             }
125             var sp = Git.run.apply(Git,cmd);
126              
127             switch (sp.result * 1) {
128                 case 0: // success:
129                     success.push(sp.args.join(' '));
130                     if (sp.output.length) success.push(sp.output + '');
131                   // if (sp.stderr.length) success.push(sp.stderr + '');
132                     break;
133                 default: 
134                     failure.push(sp.args.join(' '));
135                     if (sp.output.length) failure.push(sp.output);
136                     if (sp.stderr.length) failure.push(sp.stderr);
137                     break;
138             }
139             
140         });
141          
142         // push upstream.
143         repos.forEach(function(r) {
144             var sp = Git.run(r , 'push', { all: true } );
145             if (sp.length) {
146                 success.push(sp);
147             }
148             
149         });
150         
151         if (success.length) {
152             print(success.join("\n"));
153             var notification = new Notify.Notification({
154                 summary: "Git Live Commited",
155                 body : success.join("\n")
156                 
157             });
158
159             notification.set_timeout(2000);
160             notification.show();   
161         }
162         if (failure.length) {
163         
164             var notification = new Notify.Notification({
165                 summary: "Git Live ERROR!!",
166                 body : failure.join("\n")
167                 
168             });
169
170             notification.set_timeout(5000); // show errros for longer
171             notification.show();   
172         }
173         this.queueRunning = false;
174     },
175     
176     shouldIgnore: function(f)
177     {
178         if (f.name[0] == '.') {
179             // except!
180             if (f.name == '.htaccess') {
181                 return false;
182             }
183             
184             return true;
185         }
186         if (f.name.match(/~$/)) {
187             return true;
188         }
189         // ignore anything in top level!!!!
190         if (!f.vpath.length) {
191             return true;
192         }
193         
194         return false;
195         
196     },
197     
198     /**
199      * set gitpath and vpath
200      * 
201      * 
202      */
203     
204     parsePath: function(f)
205     {
206            
207         var vpath_ar = f.path.substring(gitlive.length +1).split('/');
208         f.repo = vpath_ar.shift();
209         f.gitpath = gitlive + '/' + f.repo;
210         f.vpath =  vpath_ar.join('/');
211         
212         
213     },
214     
215     just_created : {},
216       
217     onChanged : function(src) 
218     { 
219         return; // always ignore this..?
220         //this.parsePath(src);
221     },
222     
223     /**
224      *  results in  git add  + git commit..
225      *  
226      *
227      *
228      */
229     
230     onChangesDoneHint : function(src) 
231     { 
232         this.parsePath(src);
233         if (this.shouldIgnore(src)) {
234             return;
235         }
236         
237         var add_it = false;
238         if (typeof(this.just_created[src.path]) !='undefined') {
239             delete this.just_created[src.path];
240             this.lastAdd = new Date();
241             //this.queue.push( 
242             //    [ src.gitpath,  'add', src.vpath ],
243             //    [ src.gitpath,  'commit',  src.vpath, { message: src.vpath} ] 
244             //    
245             //);
246             this.action_queue.push({
247                 action: 'add',
248                 repo : src.repo,
249                 file : src.vpath
250             });
251             
252             
253          
254             return;
255         }
256         this.lastAdd = new Date();
257         //this.queue.push( 
258         //    [ src.gitpath,  'add', src.vpath ],
259         //    [ src.gitpath,  'commit', src.vpath, {  message: src.vpath} ]
260         //
261         //);
262         
263         this.action_queue.push({
264             action: 'add',
265             repo : src.repo,
266             file : src.vpath
267         });
268         
269
270     },
271     onDeleted : function(src) 
272     { 
273         this.parsePath(src);
274         if (this.shouldIgnore(src)) {
275             return;
276         }
277         // should check if monitor needs removing..
278         // it should also check if it was a directory.. - so we dont have to commit all..
279         
280         this.lastAdd = new Date();
281         //this.queue.push( 
282         //    [ src.gitpath, 'rm' , src.vpath ],
283         //    [ src.gitpath, 'commit', { all: true, message: src.vpath} ]
284         //    
285         //);
286         this.action_queue.push({
287             action: 'rm',
288             repo : src.repo,
289             file : src.vpath
290         });
291         
292     },
293     onCreated : function(src) 
294     { 
295         this.parsePath(src);
296         if (this.shouldIgnore(src)) {
297             return;
298         }
299         
300         if (!GLib.file_test(src.path, GLib.FileTest.IS_DIR)) {
301             this.just_created[src.path] = true;
302             return; // we do not handle file create flags... - use done hint.
303         }
304         // director has bee created
305         this.monitor(src.path);
306         
307         /*
308           since git does not really handle directory adds...
309          
310         this.lastAdd = new Date();
311         this.action_queue.push({
312             action: 'add',
313             repo : src.repo,
314             file : src.vpath
315         });
316         
317         this.queue.push( 
318             [ src.gitpath, 'add' , src.vpath,  { all: true } ],
319             [ src.gitpath, 'commit' , { all: true, message: src.vpath} ]
320             
321         );
322         */
323         
324         
325     },
326     onAttributeChanged : function(src) { 
327         this.parsePath(src);
328         if (this.shouldIgnore(src)) {
329             return;
330         }
331         this.lastAdd = new Date();
332         
333         
334         //this.queue.push( 
335        //     [ src.gitpath, 'commit' ,  src.vpath, { message: src.vpath} ]
336        // );
337         this.action_queue.push({
338             action: 'update',
339             repo : src.repo,
340             file : src.vpath
341         });
342  
343     
344     },
345     
346     onMoved : function(src,dest)
347     { 
348         this.parsePath(src);
349         this.parsePath(dest);
350         
351         if (src.gitpath != dest.gitpath) {
352             this.onDeleted(src);
353             this.onCreated(dest);
354             this.onChangedDoneHint(dest);
355             return;
356         }
357         // needs to handle move to/from unsupported types..
358         
359         if (this.shouldIgnore(src)) {
360             return;
361         }
362         if (this.shouldIgnore(dest)) {
363             return;
364         }
365         this.lastAdd = new Date();
366        // this.queue.push( 
367        //     [ src.gitpath, 'mv',  '-k', src.vpath, dest.vpath ],
368        //     [ src.gitpath, 'commit' ,  src.vpath, dest.vpath ,
369        //         { message:   'MOVED ' + src.vpath +' to ' + dest.vpath} ]
370        // );
371         
372         this.action_queue.push({
373             action: 'mv',
374             repo : src.repo,
375             file : src.vpath,
376             target : dest.vpath
377             
378         });
379         
380     }
381           
382     
383 });
384  
385  
386   
387
388 function errorDialog(data) {
389     var msg = new Gtk.MessageDialog({
390             message_type: Gtk.MessageType.ERROR, 
391             buttons : Gtk.ButtonsType.OK, 
392             text: data
393     });
394     msg.run();
395     msg.destroy();
396 }
397
398  
399
400
401
402 //
403 // need a better icon...
404
405
406 StatusIcon.init();   
407
408
409 Notify.init("gitlive");
410
411 monitor.add(GLib.get_home_dir() + "/gitlive");
412 monitor.start();
413 Gtk.main();
414 //icon.signal["activate"].connect(on_left_click);
415