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