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(JSON.stringify(cmd));
120             // --- what does this do?????
121             
122             //if (repos.indexOf(cmd[0]) < 0) {
123             //    repos.push(cmd[0]);
124             //    Git.run(cmd[0] , 'pull'); // pull before we push!
125             //}
126             
127             switch( cmd.action ) {
128                 case 'add':
129                     Git.run(gitlive + '/' + cmd.repo, 'add',  cmd.file ])
130                     
131                 case 'rm':
132                     
133                     
134                 case 'update':
135                 
136                 case 'mv':
137                 
138                 
139             }
140             
141             
142             var sp = Git.run.apply(Git,cmd);
143              
144             switch (sp.result * 1) {
145                 case 0: // success:
146                     success.push(sp.args.join(' '));
147                     if (sp.output.length) success.push(sp.output + '');
148                   // if (sp.stderr.length) success.push(sp.stderr + '');
149                     break;
150                 default: 
151                     failure.push(sp.args.join(' '));
152                     if (sp.output.length) failure.push(sp.output);
153                     if (sp.stderr.length) failure.push(sp.stderr);
154                     break;
155             }
156             
157         });
158          
159         // push upstream.
160         repos.forEach(function(r) {
161             var sp = Git.run(r , 'push', { all: true } );
162             if (sp.length) {
163                 success.push(sp);
164             }
165             
166         });
167         
168         if (success.length) {
169             print(success.join("\n"));
170             var notification = new Notify.Notification({
171                 summary: "Git Live Commited",
172                 body : success.join("\n")
173                 
174             });
175
176             notification.set_timeout(2000);
177             notification.show();   
178         }
179         if (failure.length) {
180         
181             var notification = new Notify.Notification({
182                 summary: "Git Live ERROR!!",
183                 body : failure.join("\n")
184                 
185             });
186
187             notification.set_timeout(5000); // show errros for longer
188             notification.show();   
189         }
190         this.queueRunning = false;
191     },
192     
193     shouldIgnore: function(f)
194     {
195         if (f.name[0] == '.') {
196             // except!
197             if (f.name == '.htaccess') {
198                 return false;
199             }
200             
201             return true;
202         }
203         if (f.name.match(/~$/)) {
204             return true;
205         }
206         // ignore anything in top level!!!!
207         if (!f.vpath.length) {
208             return true;
209         }
210         
211         return false;
212         
213     },
214     
215     /**
216      * set gitpath and vpath
217      * 
218      * 
219      */
220     
221     parsePath: function(f)
222     {
223            
224         var vpath_ar = f.path.substring(gitlive.length +1).split('/');
225         f.repo = vpath_ar.shift();
226         f.gitpath = gitlive + '/' + f.repo;
227         f.vpath =  vpath_ar.join('/');
228         
229         
230     },
231     
232     just_created : {},
233       
234     onChanged : function(src) 
235     { 
236         return; // always ignore this..?
237         //this.parsePath(src);
238     },
239     
240     /**
241      *  results in  git add  + git commit..
242      *  
243      *
244      *
245      */
246     
247     onChangesDoneHint : function(src) 
248     { 
249         this.parsePath(src);
250         if (this.shouldIgnore(src)) {
251             return;
252         }
253         
254         var add_it = false;
255         if (typeof(this.just_created[src.path]) !='undefined') {
256             delete this.just_created[src.path];
257             this.lastAdd = new Date();
258             //this.queue.push( 
259             //    [ src.gitpath,  'add', src.vpath ],
260             //    [ src.gitpath,  'commit',  src.vpath, { message: src.vpath} ] 
261             //    
262             //);
263             this.action_queue.push({
264                 action: 'add',
265                 repo : src.repo,
266                 file : src.vpath
267             });
268             
269             
270          
271             return;
272         }
273         this.lastAdd = new Date();
274         //this.queue.push( 
275         //    [ src.gitpath,  'add', src.vpath ],
276         //    [ src.gitpath,  'commit', src.vpath, {  message: src.vpath} ]
277         //
278         //);
279         
280         this.action_queue.push({
281             action: 'add',
282             repo : src.repo,
283             file : src.vpath
284         });
285         
286
287     },
288     onDeleted : function(src) 
289     { 
290         this.parsePath(src);
291         if (this.shouldIgnore(src)) {
292             return;
293         }
294         // should check if monitor needs removing..
295         // it should also check if it was a directory.. - so we dont have to commit all..
296         
297         this.lastAdd = new Date();
298         //this.queue.push( 
299         //    [ src.gitpath, 'rm' , src.vpath ],
300         //    [ src.gitpath, 'commit', { all: true, message: src.vpath} ]
301         //    
302         //);
303         this.action_queue.push({
304             action: 'rm',
305             repo : src.repo,
306             file : src.vpath
307         });
308         
309     },
310     onCreated : function(src) 
311     { 
312         this.parsePath(src);
313         if (this.shouldIgnore(src)) {
314             return;
315         }
316         
317         if (!GLib.file_test(src.path, GLib.FileTest.IS_DIR)) {
318             this.just_created[src.path] = true;
319             return; // we do not handle file create flags... - use done hint.
320         }
321         // director has bee created
322         this.monitor(src.path);
323         
324         /*
325           since git does not really handle directory adds...
326          
327         this.lastAdd = new Date();
328         this.action_queue.push({
329             action: 'add',
330             repo : src.repo,
331             file : src.vpath
332         });
333         
334         this.queue.push( 
335             [ src.gitpath, 'add' , src.vpath,  { all: true } ],
336             [ src.gitpath, 'commit' , { all: true, message: src.vpath} ]
337             
338         );
339         */
340         
341         
342     },
343     onAttributeChanged : function(src) { 
344         this.parsePath(src);
345         if (this.shouldIgnore(src)) {
346             return;
347         }
348         this.lastAdd = new Date();
349         
350         
351         //this.queue.push( 
352        //     [ src.gitpath, 'commit' ,  src.vpath, { message: src.vpath} ]
353        // );
354         this.action_queue.push({
355             action: 'update',
356             repo : src.repo,
357             file : src.vpath
358         });
359  
360     
361     },
362     
363     onMoved : function(src,dest)
364     { 
365         this.parsePath(src);
366         this.parsePath(dest);
367         
368         if (src.gitpath != dest.gitpath) {
369             this.onDeleted(src);
370             this.onCreated(dest);
371             this.onChangedDoneHint(dest);
372             return;
373         }
374         // needs to handle move to/from unsupported types..
375         
376         if (this.shouldIgnore(src)) {
377             return;
378         }
379         if (this.shouldIgnore(dest)) {
380             return;
381         }
382         this.lastAdd = new Date();
383        // this.queue.push( 
384        //     [ src.gitpath, 'mv',  '-k', src.vpath, dest.vpath ],
385        //     [ src.gitpath, 'commit' ,  src.vpath, dest.vpath ,
386        //         { message:   'MOVED ' + src.vpath +' to ' + dest.vpath} ]
387        // );
388         
389         this.action_queue.push({
390             action: 'mv',
391             repo : src.repo,
392             file : src.vpath,
393             target : dest.vpath
394             
395         });
396         
397     }
398           
399     
400 });
401  
402  
403   
404
405 function errorDialog(data) {
406     var msg = new Gtk.MessageDialog({
407             message_type: Gtk.MessageType.ERROR, 
408             buttons : Gtk.ButtonsType.OK, 
409             text: data
410     });
411     msg.run();
412     msg.destroy();
413 }
414
415  
416
417
418
419 //
420 // need a better icon...
421
422
423 StatusIcon.init();   
424
425
426 Notify.init("gitlive");
427
428 monitor.add(GLib.get_home_dir() + "/gitlive");
429 monitor.start();
430 Gtk.main();
431 //icon.signal["activate"].connect(on_left_click);
432