- reorganize source to build a python module
[opensuse:osc.git] / osc / core.py
1 #!/usr/bin/python
2
3 # Copyright (C) 2006 Peter Poeml.  All rights reserved.
4 # This program is free software; it may be used, copied, modified
5 # and distributed under the terms of the GNU General Public Licence,
6 # either version 2, or (at your option) any later version.
7
8 __version__ = '0.2'
9
10 import os
11 import sys
12 import urllib2
13 import netrc
14 from urlparse import urlunsplit
15 import cElementTree as ET
16 from cStringIO import StringIO
17
18 # the needed entry in .netrc looks like this:
19 # machine api.opensuse.org login your_login password your_pass
20 info = netrc.netrc()
21 username, account, password = info.authenticators("api.opensuse.org")
22
23 from xml.dom.ext.reader import Sax2
24 from xml.dom.ext import PrettyPrint
25
26 netloc = 'api.opensuse.org'
27 scheme = 'http'
28
29 BUFSIZE = 1024*1024
30 store = '.osc'
31 exclude_stuff = [store, '.svn', 'CVS']
32
33
34 def makeurl(l):
35     """given a list of path compoments, construct a complete URL"""
36     return urlunsplit((scheme, netloc, '/'.join(l), '', ''))               
37
38
39 def copy_file(src, dst):
40     s = open(src)
41     d = open(dst, 'w')
42     while 1:
43         buf = s.read(BUFSIZE)
44         if not buf: break
45         d.write(buf)
46     s.close()
47     d.close()
48
49
50 def init_basicauth():
51
52     passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
53     # this creates a password manager
54     passmgr.add_password(None, netloc, username, password)
55     # because we have put None at the start it will always
56     # use this username/password combination for  urls
57     # for which `netloc` is a super-url
58
59     authhandler = urllib2.HTTPBasicAuthHandler(passmgr)
60     # create the AuthHandler
61
62     opener = urllib2.build_opener(authhandler)
63
64     urllib2.install_opener(opener)
65     # All calls to urllib2.urlopen will now use our handler
66     # Make sure not to include the protocol in with the URL, or
67     # HTTPPasswordMgrWithDefaultRealm will be very confused.
68     # You must (of course) use it when fetching the page though.
69
70
71 def init_package_dir(project, package, dir):
72     if not os.path.isdir(store):
73         os.mkdir(store)
74     os.chdir(store)
75     f = open('_project', 'w')
76     f.write(project + '\n')
77     f.close
78     f = open('_package', 'w')
79     f.write(package + '\n')
80     f.close
81
82     f = open('_files', 'w')
83     f.write(''.join(show_files_meta(project, package)))
84     f.close()
85
86     f = open('_osclib_version', 'w')
87     f.write(__version__ + '\n')
88     f.close()
89
90     return
91
92
93 def check_store_version():
94     try:
95         v = open(os.path.join(store, '_osclib_version')).read().strip()
96     except:
97         v = ''
98
99     if v != __version__:
100         print 
101         print 'the osc metadata of your working copy'
102         print '   %s' % os.getcwd()
103         print 'has the wrong version (%s), should be %s' % (v, __version__)
104         print 'please do a fresh checkout'
105         print 
106         sys.exit(1)
107     
108
109 def meta_get_packagelist(prj):
110
111     u = makeurl(['source', prj, '_meta'])
112     f = urllib2.urlopen(u)
113
114     tree = ET.parse(f)
115     root = tree.getroot()
116
117     r = []
118     for node in root.findall('package'):
119         r.append(node.get('name'))
120     return r
121
122
123 def meta_get_filelist(prj, package):
124
125     u = makeurl(['source', prj, package])
126     f = urllib2.urlopen(u)
127     tree = ET.parse(f)
128
129     r = []
130     for node in tree.getroot():
131         r.append(node.get('name'))
132     return r
133
134
135 def localmeta_addfile(filename):
136
137     if filename in localmeta_get_filelist():
138         return
139
140     reader = Sax2.Reader()
141     f = open(os.path.join(store, '_files')).read()
142     doc = reader.fromString(f)
143
144     new = doc.createElement('entry')
145     #new.setAttribute('filetype', 'source')
146     new.setAttribute('name', filename)
147     doc.documentElement.appendChild(new)
148
149     o = open(os.path.join(store, '_files'), 'w')
150     PrettyPrint(doc, stream=o)
151     o.close()
152     
153 def localmeta_removefile(filename):
154
155     reader = Sax2.Reader()
156     f = open(os.path.join(store, '_files')).read()
157     doc = reader.fromString(f)
158
159     for i in doc.getElementsByTagName('entry'):
160         if i.getAttribute('name') == filename:
161             i.parentNode.removeChild(i)
162
163     o = open(os.path.join(store, '_files'), 'w')
164     PrettyPrint(doc, stream=o)
165     o.close()
166     
167
168 def localmeta_get_filelist():
169
170     tree = ET.parse(os.path.join(store, '_files'))
171     root = tree.getroot()
172
173     r = []
174     for node in root.findall('entry'):
175         r.append(node.get('name'))
176     return r
177
178
179 def get_slash_source():
180     u = makeurl(['source'])
181     tree = ET.parse(urllib2.urlopen(u))
182
183     r = []
184     for node in tree.getroot():
185         r.append(node.get('name'))
186     r.sort()
187     return r
188
189 def show_project_meta(prj):
190     f = urllib2.urlopen(makeurl(['source', prj, '_meta']))
191     return f.readlines()
192
193
194 def show_package_meta(prj, pac):
195     f = urllib2.urlopen(makeurl(['source', prj, pac, '_meta']))
196     return f.readlines()
197
198 def show_files_meta(prj, pac):
199     f = urllib2.urlopen(makeurl(['source', prj, pac]))
200     return f.readlines()
201
202 def get_user_id(user):
203     u = makeurl(['person', user])
204     f = urllib2.urlopen(u)
205     return f.readlines()
206
207
208 def get_source_file(prj, package, filename):
209     u = makeurl(['source', prj, package, filename])
210     #print 'checking out', u
211     f = urllib2.urlopen(u)
212
213     o = open(filename, 'w')
214     while 1:
215         buf = f.read(BUFSIZE)
216         if not buf: break
217         o.write(buf)
218     o.close()
219
220
221
222 def dgst(file):
223
224     if not os.path.exists(file):
225         return None
226
227     import sha
228     s = sha.new()
229     f = open(file, 'r')
230     while 1:
231         buf = f.read(BUFSIZE)
232         if not buf: break
233         s.update(buf)
234     return s.digest()
235
236
237 def get_file_status(prj, package, filename, filelist=None):
238     """
239     status can be:
240
241      file  storefile  file present  STATUS
242     exists  exists      in _files
243
244       x       x            -        'D'
245       x       x            x        'M', if digest differs, else ' '
246       x       -            -        '?'
247       x       -            x        'A'
248       -       x            x        '!'
249       -       x            -        'D' (when file in working copy is already deleted)
250       -       -            x        NOT DEFINED
251       -       -            -        NEVER REACHED
252
253     """
254     known_by_meta = False
255     exists = False
256     exists_in_store = False
257
258     if not filelist:
259         filelist = localmeta_get_filelist()
260
261     if filename in filelist:
262         known_by_meta = True
263
264     if os.path.exists(filename):
265         exists = True
266
267     if os.path.exists(os.path.join(store, filename)):
268         exists_in_store = True
269
270     if exists and exists_in_store and not known_by_meta:
271         state = 'D'
272     elif exists and exists_in_store and known_by_meta:
273         if dgst(filename) != dgst(os.path.join(store, filename)):
274             state = 'M'
275         else:
276             state = ' '
277     elif exists and not exists_in_store and not known_by_meta:
278         state = '?'
279     elif exists and not exists_in_store and known_by_meta:
280         state = 'A'
281     elif not exists and exists_in_store and known_by_meta:
282         state = '!'
283     elif not exists and not exists_in_store and known_by_meta:
284         print '%s: not exists and not exists_in_store and known_by_meta' % filename
285         print 'this state is undefined!'
286         sys.exit(1)
287     elif not exists and exists_in_store and not known_by_meta:
288         state = 'D'
289     elif not exists and not exists_in_store and not known_by_meta:
290         print '%s: not exists and not exists_in_store and not nown_by_meta' % filename
291         print 'this code path should never be reached!'
292         sys.exit(1)
293         
294         
295     return '%s    %s' % (state, filename)
296
297
298 def get_source_file_diff(prj, package, filename):
299     url = makeurl(['source', prj, package, filename])
300     f = urllib2.urlopen(url)
301
302     localfile = open(filename, 'r')
303
304     import difflib
305     #print url
306     d = difflib.unified_diff(f.readlines(), localfile.readlines(), fromfile = url, tofile = filename)
307
308     localfile.close()
309
310     return ''.join(d)
311
312
313 #def put_source_file_and_meta(prj, package, filename):
314 #    if filename == '_meta':
315 #        put_source_file(prj, package, filename)
316 #        return
317 #
318 #    get_source_file(prj, package, '_meta')
319 #    localmeta_addfile(os.path.basename(filename))
320 #    put_source_file(prj, package, filename)
321 #    put_source_file(prj, package, '_meta')
322
323
324 def put_source_file(prj, package, filename):
325     import othermethods
326     
327     sys.stdout.write('.')
328     u = makeurl(['source', prj, package, os.path.basename(filename)])
329     othermethods.putfile(u, filename, username, password)
330     #f = urllib2.urlopen(u)
331
332     #o = open(filename, 'w')
333     #o.write(f.read())
334     #o.close()
335
336 def del_source_file(prj, package, filename):
337     import othermethods
338     
339     u = makeurl(['source', prj, package, filename])
340     othermethods.delfile(u, filename, username, password)
341
342     wcfilename = os.path.join(store, filename)
343     if os.path.exists(filename): os.unlink(filename)
344     if os.path.exists(wcfilename): os.unlink(wcfilename)
345
346
347 def make_dir(project, package):
348     #print "creating directory '%s'" % project
349     print 'A    %s' % project
350     if not os.path.exists(project):
351         os.mkdir(project)
352         os.mkdir(os.path.join(project, store))
353
354     #print "creating directory '%s/%s'" % (project, package)
355     print 'A    %s/%s' % (project, package)
356     if not os.path.exists(os.path.join(project, package)):
357         os.mkdir(os.path.join(project, package))
358         os.mkdir(os.path.join(project, package, store))
359
360     return(os.path.join(project, package))
361
362
363 def checkout_package(project, package):
364     olddir = os.getcwd()
365
366     os.chdir(make_dir(project, package))
367     for filename in meta_get_filelist(project, package):
368         get_source_file(project, package, filename)
369         copy_file(filename, os.path.join(store, filename))
370         print 'A   ', os.path.join(project, package, filename)
371
372     init_package_dir(project, package, store)
373
374     os.chdir(olddir)
375
376
377 def get_platforms():
378     f = urllib2.urlopen(makeurl(['platform']))
379     tree = ET.parse(f)
380     r = []
381     for node in tree.getroot():
382         r.append(node.get('name'))
383     r.sort()
384     return r
385
386
387 def get_platforms_of_project(prj):
388     f = show_project_meta(prj)
389     tree = ET.parse(StringIO(''.join(f)))
390
391     r = []
392     for node in tree.findall('repository'):
393         r.append(node.get('name'))
394     return r
395
396
397 def show_results_meta(prj, package, platform):
398     u = makeurl(['result', prj, platform, package, 'result'])
399     f = urllib2.urlopen(u)
400     return f.readlines()
401
402
403 def get_results(prj, package, platform):
404     #print '----------------------------------------'
405
406     r = []
407     #result_line_templ = '%(prj)-15s %(pac)-15s %(rep)-15s %(arch)-10s %(status)s'
408     result_line_templ = '%(rep)-15s %(arch)-10s %(status)s %(hint)s'
409
410     f = show_results_meta(prj, package, platform)
411     tree = ET.parse(StringIO(''.join(f)))
412
413     root = tree.getroot()
414
415     rmap = {}
416     rmap['hint'] = ''
417     rmap['prj'] = root.get('project')
418     rmap['pac'] = root.get('package')
419     rmap['rep'] = root.get('repository')
420
421     for node in root.findall('archresult'):
422         rmap['arch'] = node.get('arch')
423
424         statusnode =  node.find('status')
425         rmap['status'] = statusnode.get('code')
426
427         if rmap['status'] == 'expansion error':
428             rmap['status'] += ': ' + statusnode.find('summary').text
429
430         if rmap['status'] == 'failed':
431             rmap['status'] += ':'
432             rmap['hint'] = '\'osc log %(rep)s %(arch)s\' -> ' % rmap + \
433                             '(%s://%s' % (scheme, netloc) + \
434                             '/result/%(prj)s/%(rep)s/%(pac)s/%(arch)s/log)' % rmap
435
436         r.append(result_line_templ % rmap)
437     return r
438
439
440 def get_log(prj, package, platform, arch):
441     u = makeurl(['result', prj, platform, package, arch, 'log'])
442     f = urllib2.urlopen(u)
443     return f.readlines()
444
445
446 def store_read_project(dir):
447     p = open(os.path.join(dir, store, '_project')).readlines()[0].strip()
448     return p
449
450
451 def store_read_package(dir):
452     p = open(os.path.join(dir, store, '_package')).readlines()[0].strip()
453     return p
454
455