Do not create a project dir after checkout
[osc-contrib.git] / osc-contrib.py
blob64c12c392d965c6f7aafb741625e976de475a9e9
2 # A contrib plugin for osc
4 # This tool makes maintenance of openSUSE:Factory:Contrib more easier than an
5 # universal osc commands.
7 # Copyright: (c) 2009 Michal Vyskocil <mvyskocil@suse.cz>
9 # Version: 0.1
12 @cmdln.option('-m', '--message', metavar='MESSAGE',
13 help='the request message (optional)')
14 @cmdln.option('-i', '--id', metavar='REQUEST_ID',
15 help='The concrete request id. Usefull whe multiple requests exists.')
16 @cmdln.option('-l', '--last-request', action='store_true', default=False,
17 help='Use a last request (default False)')
18 @cmdln.option('-s', '--state', metavar='STATE',
19 help='Show a requests with a specified state.')
20 @cmdln.option('-f', '--full-view', action='store_true', default=False,
21 help='Make a full view for show command (default False)')
22 @cmdln.alias("cb")
23 def do_contrib(self, subcmd, opts, *args):
24 """${cmd_name}: Handling a requests for Contrib
26 osc subcommand for maintenance of openSUSE Contrib repository. This command
27 tries to make the maintenance process easier than common osc commands. These
28 commands are derived from existing ones, but have a different arguments.
30 osc contrib show [PACKAGE]
31 Show all new requests towards Contrib. The optional argument package will
32 filter only a requests to it.
34 Options:
35 -s, --state filter the state (type 'any' for all)
36 -f, --full-view a full view of requests
38 osc contrib new [DEST_PACKAGE]
39 osc contrib new PROJECT PACKAGE [DEST_PACKAGE]
40 A request for adding a new package to Contrib. When requesting from package dir
41 all necessary informations are readed from osc metadata. Only a DEST_PACKAGE
42 should be givven, if you want to have another name of your package in Contrib.
44 If you are in common dir, then you have to specify the PROJECT and PACKAGE
45 manually and DEST_PACKAGE is also optional.
47 osc contrib checkout PACKAGE
48 Checkout the requested package to the current dir.
50 Options:
51 -i, --id id of request (if multiple exists)
52 -l, --last use a last request (if multiple exists)
55 osc contrib [accept|decline|revoke] PACKAGE
56 Change the state of package to <state>
58 Options:
59 -i, --id id of request (if multiple exists)
60 -l, --last use a last request (if multiple exists)
61 -m, --message the submit message (optional for accept)
62 """
64 import types
65 cmds = [cmd[12:] for cmd in dir(self) if cmd[0:12] == '_do_contrib_' and type(getattr(self, cmd)) == types.MethodType]
66 if not args or args[0] not in cmds:
67 raise oscerr.WrongArgs("Unknown contrib action. Choose one of %s." \
68 % ', '.join(cmds))
70 command = args[0]
72 self.project = 'openSUSE:Factory:Contrib'
73 #self.project = 'home:mvyskocil'
74 self.apiurl = conf.config['apiurl']
76 # call
77 getattr(self, "_do_contrib_%s" % (command))(opts, args[1:])
79 def _sr_from_package(self, package, reqid=None, use_last=False, req_state=''):
80 requests = get_submit_request_list(self.apiurl, self.project, package, req_state=req_state)
81 if len(requests) == 0:
82 raise oscerr.WrongArgs("No request for package %s found" % (package))
83 elif len(requests) > 1:
84 if use_last:
85 requests = requests[-1:]
86 elif reqid == None:
87 raise oscerr.WrongArgs(
88 "There are multiple requests (%s) towards package %s. Specify one by -i/--id, or use -l/--last-request argument!" %
89 (", ".join([str(r.reqid) for r in requests]), package))
90 else:
91 ret = [req for req in requests if req.reqid == int(reqid)]
92 if len(ret) == 0:
93 raise oscerr.WrongArgs("The package %s and request id %s doesn't match! \
94 Use one of these (%s)" % (package, reqid, ", ".join([str(r.reqid) for r in requests])))
95 requests = ret
97 return requests[0]
99 def _do_contrib_show(self, opts, args):
101 package = ''
102 if len(args) > 0:
103 package = args[0]
105 state = opts.state or 'new'
106 if state == 'any':
107 state = ''
109 srs = get_submit_request_list(self.apiurl, self.project, package, req_state=state)
110 if opts.full_view:
111 for sr in srs:
112 print(sr)
113 else:
114 for sr in srs:
115 print(sr.list_view())
117 def _do_contrib_new(self, opts, args):
118 if is_package_dir(os.getcwdu()):
119 src_project = store_read_project(os.getcwdu())
120 src_package = store_read_package(os.getcwdu())
121 dest_package = src_package
122 if len(args) > 0:
123 dest_package = args[0]
124 else:
125 if len(args) < 2:
126 raise oscerr.WrongArgs("The source project and package names are mandatory!!")
127 src_project, src_package = args[0], args[1]
128 if not src_package in meta_get_packagelist(self.apiurl, src_project):
129 raise oscerr.WrongArgs("Package '%s' don't exists in project '%s'" % (src_package, src_project))
130 dest_package = src_package
131 if len(args) == 3:
132 dest_package = args[2]
134 message = opts.message or "please add a '%s' to Contrib" % (dest_package)
135 id = create_submit_request(self.apiurl, src_project, src_package, self.project, dest_package, message)
136 print("Request id %s created" % (id))
138 def _do_contrib_accept(self, opts, args):
140 return self._contrib_sr_change(opts, args, "accepted",
141 "Reviewed and checked OK.")
143 def _do_contrib_decline(self, opts, args):
145 if not opts.message:
146 raise oscerr.WrongArgs('A message is mandatory for decline')
148 return self._contrib_sr_change(opts, args, "declined",
149 opts.message)
151 def _do_contrib_revoke(self, opts, args):
153 if not opts.message:
154 raise oscerr.WrongArgs('A message is mandatory for decline')
156 return self._contrib_sr_change(opts, args, "revoked",
157 opts.message)
160 def _do_contrib_co(self, opts, args):
161 return self._do_contrib_checkout(opts, args)
163 def _do_contrib_checkout(self, opts, args):
164 package = args[0]
165 if not package:
166 raise oscerr.WrongArgs("The package names are mandatory!!")
168 request = self._sr_from_package(package, opts.id, opts.last_request)
170 checkout_package(self.apiurl,
171 request.src_project, package,
172 expand_link=True)
174 # the original API is *very* ugly!!
175 # return the meta in an xml form first
176 def _get_meta_xml(self, package):
177 path = quote_plus(self.project),
178 kind = 'prj'
179 if package:
180 path = path + (quote_plus(package),)
181 kind = 'pkg'
182 data = meta_exists(metatype=kind,
183 path_args=path,
184 template_args=None,
185 create_new=False)
186 if data:
187 return ET.fromstring(''.join(data))
188 raise oscerr.PackageError('Meta data for package %s missing' % (package))
190 # return all persons from meta
191 def _get_persons_from_meta(self, meta):
192 return meta.getiterator('person')
194 def _get_roles_from_meta(self, meta, role):
195 assert(role in ['maintainer', 'bugowner'])
196 return [p for p in self._get_persons_from_meta(meta) if p.get('role') == role]
198 def _has_user_role(self, meta, role, user):
199 assert(role in ['maintainer', 'bugowner'])
200 if not get_user_meta(self.apiurl, user):
201 raise oscerr.WrongArgs("The user %s doesn't exists" % (user))
203 return user in [p.get('userid') for p in self._get_roles_from_meta(meta, role)]
205 # from osc.core, FIXME, this is broken
206 # look at the svn, or send a patch to obs
207 def _addBugowner(self, apiurl, prj, pac, user):
208 """ add a new bugowner to a package or project """
209 path = quote_plus(prj),
210 kind = 'prj'
211 if pac:
212 path = path + (quote_plus(pac),)
213 kind = 'pkg'
214 data = meta_exists(metatype=kind,
215 path_args=path,
216 template_args=None,
217 create_new=False)
219 if data and get_user_meta(apiurl, user) != None:
220 tree = ET.fromstring(''.join(data))
221 found = False
222 for person in tree.getiterator('person'):
223 if person.get('userid') == user and person.get('role') == 'bugowner':
224 found = True
225 print "user already exists"
226 break
227 if not found:
228 # the xml has a fixed structure
229 tree.insert(2, ET.Element('person', role='bugowner', userid=user))
230 print 'user \'%s\' added to \'%s\'' % (user, pac or prj)
231 edit_meta(metatype=kind,
232 path_args=path,
233 data=ET.tostring(tree))
234 else:
235 print "osc: an error occured"
237 def _contrib_sr_change(self, opts, args, action, message):
239 if len(args) == 0:
240 raise oscerr.WrongArgs('The package name is mandatory for %s' % (action))
242 package = args[0]
243 request = self._sr_from_package(package, opts.id, opts.last_request)
245 id = str(request.reqid)
247 # check the current state
248 sr = get_submit_request(self.apiurl, id)
250 if sr.state.name != 'new':
251 print "The state of %s request was changed to '%s' by '%s'" % (package, sr.state.name, sr.state.who)
252 res = raw_input("Do you want to change it to '%s'? [y/N] " % (action))
253 if res != 'y' and res != 'Y':
254 return
256 # check before change of commit request
257 is_new_package = not package in meta_get_packagelist(self.apiurl, self.project)
258 if action == 'accepted' and is_new_package:
259 essage = "You are now a maintainer of %s in openSUSE:Factory:Contrib" % (package)
261 # change to the state action
262 response = change_submit_request_state(self.apiurl, id, action, message)
264 # change the state for a new packages
265 if action == 'accepted' and is_new_package:
266 # fix the maintainer and a bugowner
267 meta = self._get_meta_xml(package)
268 who = sr.statehistory[0].who
269 if not self._has_user_role(meta, 'maintainer', who):
270 delMaintainer(self.apiurl, self.project, package, sr.state.who)
271 addMaintainer(self.apiurl, self.project, package, who)
272 if not self._has_user_role(meta, 'bugowner', who):
273 self._addBugowner(self.apiurl, self.project, package, who)
275 print(response)