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 GI      = imports.gi.GIRepository;
13 Gio      = imports.gi.Gio;
14 GLib      = imports.gi.GLib;
15 Gtk      = imports.gi.Gtk;
16 Notify = imports.gi.Notify;
17
18 Spawn = imports.Spawn;
19 Git = imports.Git;
20 StatusIcon = imports.StatusIcon.StatusIcon;
21 Monitor = imports.Monitor.Monitor;
22
23
24 //File = imports[__script_path__+'/../introspection-doc-generator/File.js'].File
25 Gtk.init (null, null);
26  
27 var gitlive = GLib.get_home_dir() + "/gitlive";
28
29 if (!GLib.file_test(gitlive, GLib.FileTest.IS_DIR)) {
30     var msg = new Gtk.MessageDialog({message_type:
31         Gtk.MessageType.INFO, buttons : Gtk.ButtonsType.OK, text: "GIT Live - ~/gitlive does not exist."});
32     msg.run();
33     msg.destroy();
34     
35     Seed.quit();
36 }
37
38  
39 var monitor = new Monitor({
40     
41     queue : [],
42     queueRunning : false,
43     nqv : false, // temp var while I switch to queued version.
44     
45     start: function() {
46         var _this = this;
47         this.lastAdd = new Date();
48          
49         GLib.timeout_add(GLib.PRIORITY_LOW, 500, function() {
50             //TIMEOUT", _this.queue.length , _this.queueRunning].join(', '));
51             if (!_this.queue.length || _this.queueRunning) {
52                 return 1;
53             }
54             var last = Math.floor(((new Date()) - this.lastAdd) / 100);
55             if (last < 4) { // wait 1/2 a seconnd before running.
56                 return 1;
57             }
58             _this.runQueue();
59             return 1;
60         },null,null);
61         
62         Monitor.prototype.start.call(this);
63         var notification = new Notify.Notification({
64             summary: "Git Live",
65             body : gitlive + "\nMonitoring " + this.monitors.length + " Directories"
66         });
67
68         notification.set_timeout(500);
69         notification.show();   
70         
71     },
72     /**
73      * run the queue.
74      * - pulls the items off the queue 
75      *    (as commands run concurrently and new items may get added while it's running)
76      * - runs the queue items
77      * - pushes upstream.
78      * 
79      */
80     runQueue: function()
81     {
82         this.queueRunning = true;
83         var cmds = [];
84         this.queue.forEach(function (q) {
85             cmds.push(q);
86         });
87         this.queue = []; // empty queue!
88         
89         var success = [];
90         var failure = [];
91         var repos = [];
92         cmds.forEach(function(cmd) {
93             if (repos.indexOf(cmd[0]) < 0) {
94                 repos.push(cmd[0]);
95             }
96             var sp = Git.run.apply(Git,cmd);
97              
98             switch (sp.result) {
99                 case 0: // success:
100                     success.push(sp.args.join(' '));
101                     success.push(sp.output);
102                     break;
103                 default: 
104                     failure.push(sp.args.join(' '));
105                     failure.push(sp.output);
106                     failure.push(sp.stderr);
107                     break;
108             }
109             
110         });
111         // push upstream.
112         repos.forEach(function(r) {
113             var sp = Git.run(r , 'push', { all: true } );
114             if (sp.length) {
115                 success.push(sp);
116             }
117             
118         });
119         
120         if (success.length) {
121         
122             var notification = new Notify.Notification({
123                 summary: "Git Live Commited",
124                 body : success.join("\n")
125                 
126             });
127
128             notification.set_timeout(500);
129             notification.show();   
130         }
131         if (failure.length) {
132         
133             var notification = new Notify.Notification({
134                 summary: "Git Live ERROR!!",
135                 body : failure.join("\n")
136                 
137             });
138
139             notification.set_timeout(500);
140             notification.show();   
141         }
142         this.queueRunning = false;
143     },
144     
145     shouldIgnore: function(f)
146     {
147         if (f.name[0] == '.') {
148             // except!
149             if (f.name == '.htaccess') {
150                 return false;
151             }
152             
153             return true;
154         }
155         if (f.name.match(/~$/)) {
156             return true;
157         }
158         // ignore anything in top level!!!!
159         if (!f.vpath.length) {
160             return true;
161         }
162         
163         return false;
164         
165     },
166     
167     parsePath: function(f) {
168            
169         var vpath_ar = f.path.substring(gitlive.length +1).split('/');
170         
171         f.gitpath = gitlive + '/' + vpath_ar.shift();
172         f.vpath =  vpath_ar.join('/');
173         
174         
175     },
176     
177     just_created : {},
178       
179     onChanged : function(src) 
180     { 
181         return; // always ignore this..?
182         //this.parsePath(src);
183     },
184     onChangesDoneHint : function(src) 
185     { 
186         this.parsePath(src);
187         if (this.shouldIgnore(src)) {
188             return;
189         }
190         
191         var add_it = false;
192         if (typeof(this.just_created[src.path]) !='undefined') {
193             delete this.just_created[src.path];
194             this.lastAdd = new Date();
195             this.queue.push( 
196                 [ src.gitpath,  'add', src.vpath ],
197                 [ src.gitpath,  'commit',  src.vpath, { message: src.vpath} ] 
198                 
199             );
200             if (this.nqv) {
201                 
202                 Git.run(src.gitpath, 'add', src.vpath);
203                 var sp = Git.run(src.gitpath, 'commit', { all: true, message: src.vpath});
204                 Git.run(src.gitpath , 'push', { all: true } );
205                 notify(src.name,"CHANGED", sp);
206             }
207             return;
208         }
209         this.lastAdd = new Date();
210         this.queue.push( 
211             [ src.gitpath,  'add', src.vpath ],
212             [ src.gitpath,  'commit', src.vpath, {  message: src.vpath} ]
213
214             
215         );
216         if (this.nqv) {
217             var sp = Git.run(src.gitpath, 'commit', { all: true, message: src.vpath});
218             Git.run(src.gitpath , 'push', '--all' );
219             notify(src.name,"CHANGED", sp);
220         }
221
222     },
223     onDeleted : function(src) 
224     { 
225         this.parsePath(src);
226         if (this.shouldIgnore(src)) {
227             return;
228         }
229         // should check if monitor needs removing..
230         // it should also check if it was a directory.. - so we dont have to commit all..
231         
232         this.lastAdd = new Date();
233         this.queue.push( 
234             [ src.gitpath, 'rm' , src.vpath ],
235             [ src.gitpath, 'commit', { all: true, message: src.vpath} ]
236             
237         );
238         if (!this.nqv) {
239             return;
240         }
241         
242         var sp = Git.run(src.gitpath,'rm' , src.vpath);
243         Git.run(src.gitpath , 'push', { all: true } );
244         if (sp.status !=0) {
245             notify(src.name,"DELETED", sp);
246             return;
247         }
248         sp = Git.run(src.gitpath,'commit' ,{ all: true, message: src.vpath});
249         Git.run(src.gitpath , 'push',{ all: true });
250         notify(src.name,"DELETED", sp);
251         return;
252         
253     },
254     onCreated : function(src) 
255     { 
256         this.parsePath(src);
257         if (this.shouldIgnore(src)) {
258             return;
259         }
260         
261         if (!GLib.file_test(src.path, GLib.FileTest.IS_DIR)) {
262             this.just_created[src.path] = true;
263             return; // we do not handle file create flags... - use done hint.
264         }
265         // director has bee created
266         this.monitor(src.path);
267         this.lastAdd = new Date();
268         this.queue.push( 
269             [ src.gitpath, 'add' , src.vpath,  { all: true } ],
270             [ src.gitpath, 'commit' , { all: true, message: src.vpath} ]
271             
272         );
273         if (!this.nqv) {
274             return;
275         }
276         var sp = Git.run(src.gitpath, 'add', src.vpath);
277         Git.run(src.gitpath , 'push', { all: true } );
278
279         if (sp.status !=0) {
280             notify(src.path,"CREATED", sp);
281             return;
282         }
283         //uh.call(fm,f,of, event_type);
284         sp = Git.run(src.gitpath,'commit' , { all: true, message: src.vpath});
285         Git.run(src.gitpath , 'push', { all: true } );
286         notify(src.path,"CREATED", sp);
287         return;
288         
289     },
290     onAttributeChanged : function(src) { 
291         this.parsePath(src);
292         if (this.shouldIgnore(src)) {
293             return;
294         }
295         this.lastAdd = new Date();
296         this.queue.push( 
297             [ src.gitpath, 'commit' ,  src.vpath, { message: src.vpath} ]
298         );
299         if (!this.nqv) {
300             return;
301         }
302         var sp = Git.run(src.gitpath, 'commit',{ all: true, message: src.vpath});
303         Git.run(src.gitpath , 'push', { all: true } );
304         notify(src.path,"ATTRIBUTE_CHANGED", sp);
305         return;
306     
307     },
308     
309     onMoved : function(src,dest)
310     { 
311         this.parsePath(src);
312         this.parsePath(dest);
313         
314         if (src.gitpath != dest.gitpath) {
315             this.onDeleted(src);
316             this.onCreated(dest);
317             this.onChangedDoneHint(dest);
318             return;
319         }
320         // needs to handle move to/from unsupported types..
321         
322         if (this.shouldIgnore(src)) {
323             return;
324         }
325         if (this.shouldIgnore(dest)) {
326             return;
327         }
328         this.lastAdd = new Date();
329         this.queue.push( 
330             [ src.gitpath, 'mv',  '-k', src.vpath, dest.vpath ],
331             [ src.gitpath, 'commit' ,  src.vpath, dest.vpath ,
332                 { message:   'MOVED ' + src.vpath +' to ' + dest.vpath} ]
333         );
334         
335         if (!this.nqv) {
336             return;
337         }
338         
339         var sp = Git.run(src.gitpath,  'mv',  '-k', src.vpath, dest.vpath);
340         if (sp.status !=0) {
341             notify(dest.path,"MOVED", sp);
342             return;
343         }
344         sp = Git.run(src.gitpath,'commit' , { all: true, message:   'MOVED ' + src.vpath +' to ' + dest.vpath} );
345         Git.run(src.gitpath , 'push', { all: true } );
346         notify(src.path,"MOVED", sp);
347         
348     }
349           
350     
351 });
352  
353  
354  
355
356 function notify(fn, act , sp)
357 {
358     var sum = act + " " + fn;
359     
360     var notification = new Notify.Notification({
361         summary: sum,
362                 body : sp
363         });
364
365     notification.set_timeout(500);
366     notification.show();
367 }
368
369
370
371   
372
373 function errorDialog(data) {
374     var msg = new Gtk.MessageDialog({
375             message_type: Gtk.MessageType.ERROR, 
376             buttons : Gtk.ButtonsType.OK, 
377             text: data
378     });
379     msg.run();
380     msg.destroy();
381 }
382
383  
384
385
386
387 //
388 // need a better icon...
389
390 StatusIcon.init(); 
391 Notify.init("gitlive");
392 monitor.add(GLib.get_home_dir() + "/gitlive");
393 monitor.start();
394 Gtk.main();
395 //icon.signal["activate"].connect(on_left_click);
396