Better metadata handling.
[opensuse:osc-contrib.git] / osc-contrib.py
1 @cmdln.option('-m', '--message', metavar='MESSAGE',
2               help='the request message (optional)')
3 @cmdln.option('-k', '--keep', metavar='MAINTAINER',
4               help='keep the original maintainer (usefull for new packages)')
5 @cmdln.alias("cb")
6 def do_contrib(self, subcmd, opts, *args):
7     """${cmd_name}: Handling a requests for Contrib
8
9     A specialized version of submitreq command for
10     openSUSE:Factory:Contrib repository
11
12     The documentation will follows ;-)
13     
14     list - list all new requests to openSUSE:Factory:Contrib
15     new - a new (package) request for Contrib
16     accept - accept a package request
17     decline - decline a package request
18
19     ${cmd_usage}
20     ${cmd_option_list}
21     """
22
23     cmds = ['list', 'new', 'accept', 'decline']
24     if not args or args[0] not in cmds:
25         raise oscerr.WrongArgs("Unknown contrib action. Choose one of %s." \
26                                 % ', '.join(cmds))
27     
28     command = args[0]
29
30     self.project = 'openSUSE:Factory:Contrib'
31     #self.project = 'home:mvyskocil'
32     self.apiurl  = conf.config['apiurl']
33
34     # call
35     getattr(self, "_do_contrib_%s" % (command))(opts, args[1:])
36
37 def _sr_from_package(self, package, req_state=''):
38     return get_submit_request_list(self.apiurl, self.project, package, req_state=req_state)
39     
40 def _do_contrib_list(self, opts, args):
41
42     package = ''
43     if len(args) > 0:
44         package = args[0]
45     
46     for sr in get_submit_request_list(self.apiurl, self.project, package, req_state='new'):
47         print(sr)
48
49 def _do_contrib_new(self, opts, args):
50     src_project, src_package, dest_package = args[0], args[1], args[2]
51     if not dest_project or not package:
52         raise oscerr.WrongArgs("The project and package names are mandatory!!")
53     if not dest_package or dest_package == '':
54         dest_package = src_package
55
56     message = opts.message or "please add a '%s' to Contrib" % (package)
57     id = osc.core.create_submit_request(self.apiurl, src_project, src_package, self.project, dest_package, message)
58     print("Request id %d created" % (id))
59
60 def _do_contrib_accept(self, opts, args):
61     
62     return self._do_contrib_sr_change(opts, args, "accept", 
63            "Reviewed and checked OK, welcome to Contrib.")
64
65 def _do_contrib_decline(self, opts, args):
66
67     if not opts.message:
68         raise oscerr.WrongArgs('A message is mandatory for decline')
69     
70     return self._do_contrib_sr_change(opts, args, "decline",
71            opts.message)
72
73 # the original API is *very* ugly!!
74 # return the meta in an xml form first
75 def _get_meta_xml(self, package):
76     path = quote_plus(self.project),
77     kind = 'prj'
78     if pac:
79         path = path + (quote_plus(package),)
80         kind = 'pkg'
81     data = meta_exists(metatype=kind,
82                        path_args=path,
83                        template_args=None,
84                        create_new=False)
85     if data:
86         return ET.fromstring(''.join(data))
87     raise oscerr.PackageError('Meta data for package %s missing' % (package))
88
89 # return all persons from meta
90 def _get_persons_from_meta(self, meta):
91     return meta.getiterator('person')
92
93 def _get_roles_from_meta(self, meta, role):
94     assert(role in ['maintainer', 'bugowner'])
95     return [p for p in self._get_persons_from_meta(meta) if p.get('role') == role]
96
97 def _has_user_role(self, meta, role, user):
98     assert(role in ['maintainer', 'bugowner'])
99     if get_user_meta(self.apiurl, user):
100         raise osc.WrongArgs("The user %s doesn't exists" % (user))
101
102     return user in [p.get('userid') for p in self._get_roles_from_meta(meta, role)]
103
104 # from osc.core, FIXME, this is broken
105 # look at the svn, or send a patch to obs
106 def _addBugowner(self, apiurl, prj, pac, user):
107     """ add a new bugowner to a package or project """
108     path = quote_plus(prj),
109     kind = 'prj'
110     if pac:
111         path = path + (quote_plus(pac),)
112         kind = 'pkg'
113     data = meta_exists(metatype=kind,
114                        path_args=path,
115                        template_args=None,
116                        create_new=False)
117                        
118     if data and get_user_meta(apiurl, user) != None:
119         tree = ET.fromstring(''.join(data))
120         found = False
121         for person in tree.getiterator('person'):
122             if person.get('userid') == user:
123                 found = True
124                 print "user already exists"
125                 break
126         if not found:
127             # the xml has a fixed structure
128             tree.insert(2, ET.Element('person', role='bugowner', userid=user))
129             print 'user \'%s\' added to \'%s\'' % (user, pac or prj)
130             edit_meta(metatype=kind,
131                       path_args=path,
132                       data=ET.tostring(tree))
133     else:
134         print "osc: an error occured"
135
136 def _do_contrib_sr_change(self, opts, args, action, message):
137
138     if len(args) == 0:
139         raise oscerr.WrongArgs('The package name is mandatory for %s' % (action))
140
141     package = args[0]
142     requests = self._sr_from_package(package)
143     if len(requests) == 0:
144         raise oscerr.PackageMissing("No request for package %s found" % (package))
145     elif len(requests) > 1:
146         raise oscerr.WrongArgs("There are multiple requests towards package %s. Use osc submitreq command instead!" % (package))
147     
148     id = str(requests[0].reqid)
149
150     # check the current state
151     sr = get_submit_request(self.apiurl, id)
152
153     if sr.state.name != 'new':
154         print "The state of %s request was changed to '%s' by '%s'" % (package, sr.state.name, sr.state.who)
155         res = raw_input("Do you want to change it to '%s'? [y/N] " % (action))
156         if res != 'y' and res != 'Y':
157             return
158
159     # change to the state action
160     response = change_submit_request_state(self.apiurl, id, action, message)
161     
162     if opts.keep:
163         # fix the maintainer and a bugowner
164         meta = self._get_meta_xml(package)
165         if not self._has_user_role(meta, 'maintainer', sr.who):
166             addMaintainer(self.apiurl, self.project, package, sr.who)
167         if not self._has_user_role(meta, 'bugowner', sr.who):
168             self._addBugowner(self.apiurl, self.project, package, sr.who)
169
170     print(response)