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