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