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