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