Merge lp:~lifeless/lptools/lp-project into lp:~dobey/lptools/trunk
- lp-project
- Merge into trunk
Status: | Merged |
---|---|
Merge reported by: | dobey |
Merged at revision: | not available |
Proposed branch: | lp:~lifeless/lptools/lp-project |
Merge into: | lp:~dobey/lptools/trunk |
Diff against target: |
380 lines (+251/-69) 5 files modified
MANUAL.txt (+12/-0) bin/lp-milestones (+8/-67) bin/lp-project (+119/-0) lptools/command.py (+101/-0) lptools/config.py (+11/-2) |
To merge this branch: | bzr merge lp:~lifeless/lptools/lp-project |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jelmer Vernooij (community) | code | Needs Fixing | |
dobey | Pending | ||
Review via email: mp+21124@code.launchpad.net |
Commit message
Description of the change
If its not obvious, call me names.
Robert Collins (lifeless) wrote : | # |
- 13. By Robert Collins
-
Refactor CLI support to permit multiple front ends.
- 14. By Robert Collins
-
Cleanups.
- 15. By Robert Collins
-
Finish making the new command module work generically.
- 16. By Robert Collins
-
Bah, missed imports when migrating.
- 17. By Robert Collins
-
Add lp-project CLI script, which can create a project in launchpad.
Robert Collins (lifeless) wrote : | # |
Final tweaks done - lp-project now exists, and 'lp-project create foo' will create a foo project, management team, mailing list and trunk branch in a fairly sensible layout.
Jelmer Vernooij (jelmer) wrote : | # |
This seems reasonable overall. It will have to be updated to not use edge, which has been deprecated.
Robert Collins (lifeless) wrote : | # |
I won't be able to do this for some time; care to tweak-as-landing ?
Jelmer Vernooij (jelmer) wrote : | # |
On 26/08/11 01:28, Robert Collins wrote:
> I won't be able to do this for some time; care to tweak-as-landing ?
Sure, happy to.
Cheers,
Jelmer
Jelmer Vernooij (jelmer) wrote : | # |
This is now merged into lp:lptools; it won't show up here though, as this branch was originally proposed against Dobey's lptools trunk rather than the team one.
Preview Diff
1 | === added file 'MANUAL.txt' |
2 | --- MANUAL.txt 1970-01-01 00:00:00 +0000 |
3 | +++ MANUAL.txt 2010-03-11 11:42:24 +0000 |
4 | @@ -0,0 +1,12 @@ |
5 | +======= |
6 | +LPTools |
7 | +======= |
8 | + |
9 | +Environment Variables |
10 | +--------------------- |
11 | + |
12 | +When running lptools, the variable LAUNCHPAD_API can be set to cause a specific |
13 | +Launchpad service to be connected to. Current values are: |
14 | + |
15 | +* edge |
16 | +* staging |
17 | |
18 | === modified file 'bin/lp-milestones' |
19 | --- bin/lp-milestones 2010-02-20 06:48:17 +0000 |
20 | +++ bin/lp-milestones 2010-03-11 11:42:24 +0000 |
21 | @@ -16,42 +16,19 @@ |
22 | # You should have received a copy of the GNU General Public License along |
23 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
24 | |
25 | -from __future__ import with_statement |
26 | +"""lp-milestones -- An lptools command to work with milestones in launchpad. |
27 | +https://launchpad.net/lptools/ |
28 | + |
29 | +lp-milestones help commands -- list commands |
30 | +""" |
31 | + |
32 | import time |
33 | -import optparse |
34 | -import os |
35 | -import sys |
36 | |
37 | # Might want to make errors import lazy. |
38 | -from bzrlib import commands, errors, ui, version_info as bzr_version_info |
39 | +from bzrlib import errors |
40 | from launchpadlib.errors import HTTPError |
41 | |
42 | -from lptools import config |
43 | - |
44 | -def list_commands(command_names): |
45 | - mod = sys.modules[__name__] |
46 | - command_names.update(commands._scan_module_for_commands(mod)) |
47 | - return command_names |
48 | - |
49 | - |
50 | -def get_command(cmd_or_None, cmd_name): |
51 | - if cmd_name is None: |
52 | - return cmd_help() |
53 | - try: |
54 | - return globals()['cmd_' + cmd_name]() |
55 | - except KeyError: |
56 | - return cmd_or_None |
57 | - |
58 | - |
59 | -class LaunchpadCommand(commands.Command): |
60 | - """Base class for commands working with launchpad.""" |
61 | - |
62 | - def run_argv_aliases(self, argv, alias_argv=None): |
63 | - # This might not be unique-enough for a cachedir; can do |
64 | - # lp-milestones/cmdname if needed. |
65 | - self.launchpad = config.get_launchpad('lp-milestones') |
66 | - return commands.Command.run_argv_aliases(self, argv, alias_argv) |
67 | - |
68 | +from lptools.command import * |
69 | |
70 | class cmd_create(LaunchpadCommand): |
71 | """Create a milestone. |
72 | @@ -108,25 +85,6 @@ |
73 | raise |
74 | |
75 | |
76 | -class cmd_help(commands.Command): |
77 | - """Show help on a command or other topic.""" |
78 | - |
79 | - # Can't use the stock bzrlib help, because the help indices aren't quite |
80 | - # generic enough. |
81 | - takes_args = ['topic?'] |
82 | - def run(self, topic=None): |
83 | - if topic is None: |
84 | - self.outf.write( |
85 | -"""lp-milestones -- An lptools command to work with milestones in launchpad. |
86 | -https://launchpad.net/lptools/ |
87 | - |
88 | -lp-milestones help commands -- list commands |
89 | -""") |
90 | - else: |
91 | - import bzrlib.help |
92 | - bzrlib.help.help(topic) |
93 | - |
94 | - |
95 | class cmd_release(LaunchpadCommand): |
96 | """Create a release from a milestone. |
97 | |
98 | @@ -168,22 +126,5 @@ |
99 | m.lp_save() |
100 | |
101 | |
102 | -def do_run_bzr(argv): |
103 | - if bzr_version_info > (2, 2, 0): |
104 | - # in bzr 2.2 we can disable bzr plugins so bzr commands don't show |
105 | - # up. |
106 | - return commands.run_bzr(argv, lambda:None, lambda:None) |
107 | - else: |
108 | - return commands.run_bzr(argv) |
109 | - |
110 | - |
111 | -def main(): |
112 | - commands.Command.hooks.install_named_hook('list_commands', list_commands, |
113 | - "list") |
114 | - commands.Command.hooks.install_named_hook('get_command', get_command, |
115 | - "get") |
116 | - ui.ui_factory = ui.make_ui_for_terminal(sys.stdin, sys.stdout, sys.stderr) |
117 | - sys.exit(commands.exception_to_return_code(do_run_bzr, sys.argv[1:])) |
118 | - |
119 | if __name__ == "__main__": |
120 | main() |
121 | |
122 | === added file 'bin/lp-project' |
123 | --- bin/lp-project 1970-01-01 00:00:00 +0000 |
124 | +++ bin/lp-project 2010-03-11 11:42:24 +0000 |
125 | @@ -0,0 +1,119 @@ |
126 | +#!/usr/bin/python |
127 | +# |
128 | +# Author: Robert Collins <robert.collins@canonical.com> |
129 | +# |
130 | +# Copyright 2010 Canonical Ltd. |
131 | +# |
132 | +# This program is free software: you can redistribute it and/or modify it |
133 | +# under the terms of the GNU General Public License version 3, as published |
134 | +# by the Free Software Foundation. |
135 | +# |
136 | +# This program is distributed in the hope that it will be useful, but |
137 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
138 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
139 | +# PURPOSE. See the GNU General Public License for more details. |
140 | +# |
141 | +# You should have received a copy of the GNU General Public License along |
142 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
143 | + |
144 | +"""lp-project -- An lptools command to work with projects in Launchpad. |
145 | +https://launchpad.net/lptools/ |
146 | + |
147 | +lp-project help commands -- list commands |
148 | +""" |
149 | + |
150 | +import time |
151 | +import os |
152 | +import sys |
153 | + |
154 | +# Might want to make errors import lazy. |
155 | +from bzrlib import bzrdir, directory_service, errors, transport |
156 | +from launchpadlib.errors import HTTPError |
157 | + |
158 | +from lptools.command import * |
159 | + |
160 | +class cmd_create(LaunchpadCommand): |
161 | + """Create a project. |
162 | + |
163 | + This creates two teams - a management/committer team owner by you and |
164 | + a -dev mailing list owned by the mgmt team. It then creates the project, |
165 | + makes it owned by the committer team, creates a new empty bzr branch for |
166 | + trunk and then fires up a web browser pointing at the project for you to |
167 | + fine tune. |
168 | + |
169 | + After running this command you need to: |
170 | + - set what LP features you are using |
171 | + (https://bugs.edge.launchpad.net/launchpad/+bug/537269) |
172 | + - describe your project (title, summary, description) |
173 | + - upload your code to lp:PROJECT |
174 | + - turn on the mailing list by hand |
175 | + (https://bugs.edge.launchpad.net/launchpad/+bug/537258) |
176 | + |
177 | + lp-project create projectname |
178 | + """ |
179 | + |
180 | + takes_args = ['project'] |
181 | + |
182 | + def run(self, project): |
183 | + self.outf.write('creating project %s\n' % project) |
184 | + #load the lp plugin - we needs it, but don't want its commands shown. |
185 | + from bzrlib.plugins import launchpad |
186 | + project_d = {'project':project} |
187 | + mgmt = self.launchpad.people.newTeam( |
188 | + display_name='%s committers' % project, name=project, |
189 | + subscription_policy='Moderated Team', team_description="This is the" |
190 | + " %(project)s project maintainers and committers team. Membership " |
191 | + "in this team grants commit access to the %(project)s trunk and " |
192 | + "release branches, and owner access to the project. To become a " |
193 | + "member of this team, please contact the project via the " |
194 | + "%(project)s-dev mailing list." % {'project':project}) |
195 | + dev = self.launchpad.people.newTeam( |
196 | + display_name='%s mailing list' % project, name='%s-dev' % project, |
197 | + subscription_policy='Open Team', team_description="This is the" |
198 | + " %(project)s (https://launchpad.net/%(project)s) development " |
199 | + "discussion mailing list. To subscribe to the list join this team " |
200 | + "(it does not receive bug mail or other such dross)." % project_d) |
201 | + dev.team_owner = mgmt |
202 | + dev.lp_save() |
203 | + proj = self.launchpad.projects.new_project( |
204 | + display_name=project, |
205 | + name=project, |
206 | + registrant=mgmt, |
207 | + title="Put what you want to show in browser window titles for %s " |
208 | + "here" % project, |
209 | + description="""Put a long description of the %(project)s project here. |
210 | + |
211 | +A mailing list for discussion, usage and development is at https://launchpad.net/~%(project)s-dev - all are welcome to join. Some folk hang out on #%(project)s on irc.freenode.net. |
212 | +""" % project_d, |
213 | + home_page_url="https://launchpad.net/%s" % project, |
214 | + summary="Put a pithy summary of %s here." % project, |
215 | + ) |
216 | + proj.bug_reporting_guidelines = """Enough data to reproduce the behaviour if possible. |
217 | + |
218 | +If that is not possible, just describe as well as you can what is happening and we will talk through the issue with you. |
219 | +""" |
220 | + proj.owner = mgmt |
221 | + proj.lp_save() |
222 | + branch_path = '~%(project)s/%(project)s/trunk' % project_d |
223 | + lp_host = os.environ.get('LAUNCHPAD_API', '') |
224 | + if lp_host: |
225 | + lp_host = '//%s/' % lp_host |
226 | + branch = bzrdir.BzrDir.create_branch_convenience( |
227 | + 'lp:%s%s' % (lp_host, branch_path), force_new_tree=False) |
228 | + series = proj.getSeries(name='trunk') |
229 | + series.branch_link = self.launchpad.load(branch_path) |
230 | + series.lp_save() |
231 | + self.outf.write(""" |
232 | +Project created at %s (sorry about the url. (https://bugs.edge.launchpad.net/launchpadlib/+bug/316694)). |
233 | +You now need to: |
234 | + - set what LP features you are using |
235 | + (https://bugs.edge.launchpad.net/launchpad/+bug/537269) |
236 | + - describe your project (title, summary, description) |
237 | + - upload your code to lp:PROJECT |
238 | + - turn on the mailing list by hand |
239 | + (https://bugs.edge.launchpad.net/launchpad/+bug/537258) |
240 | +""" % proj.self_link) |
241 | + |
242 | + |
243 | +if __name__ == "__main__": |
244 | + main() |
245 | |
246 | === added file 'lptools/command.py' |
247 | --- lptools/command.py 1970-01-01 00:00:00 +0000 |
248 | +++ lptools/command.py 2010-03-11 11:42:24 +0000 |
249 | @@ -0,0 +1,101 @@ |
250 | +# Author: Robert Collins <robert.collins@canonical.com> |
251 | +# |
252 | +# Copyright 2010 Canonical Ltd. |
253 | +# |
254 | +# This program is free software: you can redistribute it and/or modify it |
255 | +# under the terms of the GNU General Public License version 3, as published |
256 | +# by the Free Software Foundation. |
257 | +# |
258 | +# This program is distributed in the hope that it will be useful, but |
259 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
260 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
261 | +# PURPOSE. See the GNU General Public License for more details. |
262 | +# |
263 | +# You should have received a copy of the GNU General Public License along |
264 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
265 | + |
266 | +"""Command abstraction for the CLI parts of lptools. |
267 | + |
268 | +To use: |
269 | + |
270 | +* in your front end script define a docstring - this is used for global help. |
271 | + |
272 | +* from this module import * |
273 | + |
274 | +* derive commands from LaunchpadCommand and call them cmd_NAME. |
275 | + |
276 | +* At the bottom of your script, do:: |
277 | + |
278 | + if __name__ == '__main__': |
279 | + main() |
280 | +""" |
281 | + |
282 | +__all__ = [ |
283 | + 'LaunchpadCommand', |
284 | + 'main', |
285 | + ] |
286 | + |
287 | +import os |
288 | +import sys |
289 | + |
290 | +from bzrlib import commands, ui, version_info as bzr_version_info |
291 | + |
292 | +from lptools import config |
293 | + |
294 | +def list_commands(command_names): |
295 | + mod = sys.modules['__main__'] |
296 | + command_names.update(commands._scan_module_for_commands(mod)) |
297 | + return command_names |
298 | + |
299 | + |
300 | +def get_command(cmd_or_None, cmd_name): |
301 | + if cmd_name is None: |
302 | + return cmd_help() |
303 | + elif cmd_name == 'help': |
304 | + return cmd_help() |
305 | + klass = getattr(sys.modules['__main__'], 'cmd_' + cmd_name, None) |
306 | + if klass is not None: |
307 | + return klass() |
308 | + return cmd_or_None |
309 | + |
310 | + |
311 | +class LaunchpadCommand(commands.Command): |
312 | + """Base class for commands working with launchpad.""" |
313 | + |
314 | + def run_argv_aliases(self, argv, alias_argv=None): |
315 | + # This might not be unique-enough for a cachedir; can do |
316 | + # $frontend/cmdname if needed. |
317 | + self.launchpad = config.get_launchpad(os.path.basename(sys.argv[0])) |
318 | + return commands.Command.run_argv_aliases(self, argv, alias_argv) |
319 | + |
320 | + |
321 | +class cmd_help(commands.Command): |
322 | + """Show help on a command or other topic.""" |
323 | + |
324 | + # Can't use the stock bzrlib help, because the help indices aren't quite |
325 | + # generic enough. |
326 | + takes_args = ['topic?'] |
327 | + def run(self, topic=None): |
328 | + if topic is None: |
329 | + self.outf.write(sys.modules['__main__'].__doc__) |
330 | + else: |
331 | + import bzrlib.help |
332 | + bzrlib.help.help(topic) |
333 | + |
334 | + |
335 | +def do_run_bzr(argv): |
336 | + if bzr_version_info > (2, 2, 0): |
337 | + # in bzr 2.2 we can disable bzr plugins so bzr commands don't show |
338 | + # up. |
339 | + return commands.run_bzr(argv, lambda:None, lambda:None) |
340 | + else: |
341 | + return commands.run_bzr(argv) |
342 | + |
343 | + |
344 | +def main(): |
345 | + commands.Command.hooks.install_named_hook('list_commands', list_commands, |
346 | + "list") |
347 | + commands.Command.hooks.install_named_hook('get_command', get_command, |
348 | + "get") |
349 | + ui.ui_factory = ui.make_ui_for_terminal(sys.stdin, sys.stdout, sys.stderr) |
350 | + sys.exit(commands.exception_to_return_code(do_run_bzr, sys.argv[1:])) |
351 | |
352 | === modified file 'lptools/config.py' |
353 | --- lptools/config.py 2010-02-20 04:46:06 +0000 |
354 | +++ lptools/config.py 2010-03-11 11:42:24 +0000 |
355 | @@ -55,10 +55,10 @@ |
356 | creds = Credentials() |
357 | with file(credspath) as f: |
358 | creds.load(f) |
359 | - return launchpad.Launchpad(creds, EDGE_SERVICE_ROOT, cachedir) |
360 | + return launchpad.Launchpad(creds, _service_root(), cachedir) |
361 | else: |
362 | result = launchpad.Launchpad.get_token_and_login('lptools', |
363 | - EDGE_SERVICE_ROOT, cachedir) |
364 | + _service_root(), cachedir) |
365 | with file(credspath, "w") as f: |
366 | result.credentials.save(f) |
367 | return result |
368 | @@ -81,3 +81,12 @@ |
369 | cachedir = lptools_cachedir() |
370 | ensure_dir(cachedir) |
371 | return os.path.join(lptools_cachedir(), 'credentials') |
372 | + |
373 | + |
374 | +def _service_root(): |
375 | + """Get the service root to connect to. |
376 | + |
377 | + By default this is EDGE_SERVICE_ROOT. If LAUNCHPAD_API is set in the |
378 | + environment, that value is used instead. |
379 | + """ |
380 | + return os.environ.get('LAUNCHPAD_API', EDGE_SERVICE_ROOT) |
Ok, so now it needs an explanation. I've refactored the minimal command support stuff to permit it to be reused via convention in other front-end scripts.