]> git.ipfire.org Git - pakfire.git/blob - python/pakfire/cli.py
Add --after-shell switch to build command.
[pakfire.git] / python / pakfire / cli.py
1 #!/usr/bin/python
2 ###############################################################################
3 # #
4 # Pakfire - The IPFire package management system #
5 # Copyright (C) 2011 Pakfire development team #
6 # #
7 # This program is free software: you can redistribute it and/or modify #
8 # it under the terms of the GNU General Public License as published by #
9 # the Free Software Foundation, either version 3 of the License, or #
10 # (at your option) any later version. #
11 # #
12 # This program is distributed in the hope that it will be useful, #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15 # GNU General Public License for more details. #
16 # #
17 # You should have received a copy of the GNU General Public License #
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
19 # #
20 ###############################################################################
21
22 import argparse
23 import os
24 import sys
25
26 import logger
27 import packages
28 import repository
29 import server
30 import util
31
32 import pakfire.api as pakfire
33 from constants import *
34 from i18n import _
35
36 # Initialize a very simple logging that is removed when a Pakfire instance
37 # is started.
38 logger.setup_logging()
39
40 class Cli(object):
41 def __init__(self):
42 self.parser = argparse.ArgumentParser(
43 description = _("Pakfire command line interface."),
44 )
45
46 self.parse_common_arguments()
47
48 self.parser.add_argument("--root", metavar="PATH",
49 default="/",
50 help=_("The path where pakfire should operate in."))
51
52 # Add sub-commands.
53 self.sub_commands = self.parser.add_subparsers()
54
55 self.parse_command_install()
56 self.parse_command_localinstall()
57 self.parse_command_remove()
58 self.parse_command_info()
59 self.parse_command_search()
60 self.parse_command_check_update()
61 self.parse_command_update()
62 self.parse_command_provides()
63 self.parse_command_grouplist()
64 self.parse_command_groupinstall()
65 self.parse_command_repolist()
66 self.parse_command_clean()
67 self.parse_command_check()
68 self.parse_command_resolvdep()
69
70 # Finally parse all arguments from the command line and save them.
71 self.args = self.parser.parse_args()
72
73 self.action2func = {
74 "install" : self.handle_install,
75 "localinstall" : self.handle_localinstall,
76 "remove" : self.handle_remove,
77 "check_update" : self.handle_check_update,
78 "update" : self.handle_update,
79 "info" : self.handle_info,
80 "search" : self.handle_search,
81 "provides" : self.handle_provides,
82 "grouplist" : self.handle_grouplist,
83 "groupinstall" : self.handle_groupinstall,
84 "repolist" : self.handle_repolist,
85 "clean_all" : self.handle_clean_all,
86 "check" : self.handle_check,
87 "resolvdep" : self.handle_resolvdep,
88 }
89
90 @property
91 def pakfire_args(self):
92 ret = { "mode" : "normal" }
93
94 if hasattr(self.args, "root"):
95 ret["path"] = self.args.root
96
97 if hasattr(self.args, "disable_repo"):
98 ret["disable_repos"] = self.args.disable_repo
99
100 if hasattr(self.args, "enable_repo"):
101 ret["enable_repos"] = self.args.enable_repo
102
103 if hasattr(self.args, "offline"):
104 ret["offline"] = self.args.offline
105
106 return ret
107
108 def parse_common_arguments(self):
109 self.parser.add_argument("--version", action="version",
110 version="%(prog)s " + PAKFIRE_VERSION)
111
112 self.parser.add_argument("-v", "--verbose", action="store_true",
113 help=_("Enable verbose output."))
114
115 self.parser.add_argument("-c", "--config", nargs="?",
116 help=_("Path to a configuration file to load."))
117
118 self.parser.add_argument("--disable-repo", nargs="*", metavar="REPO",
119 help=_("Disable a repository temporarily."))
120
121 self.parser.add_argument("--enabled-repo", nargs="*", metavar="REPO",
122 help=_("Enable a repository temporarily."))
123
124 self.parser.add_argument("--offline", action="store_true",
125 help=_("Run pakfire in offline mode."))
126
127 def parse_command_install(self):
128 # Implement the "install" command.
129 sub_install = self.sub_commands.add_parser("install",
130 help=_("Install one or more packages to the system."))
131 sub_install.add_argument("package", nargs="+",
132 help=_("Give name of at least one package to install."))
133 sub_install.add_argument("action", action="store_const", const="install")
134
135 def parse_command_localinstall(self):
136 # Implement the "localinstall" command.
137 sub_install = self.sub_commands.add_parser("localinstall",
138 help=_("Install one or more packages from the filesystem."))
139 sub_install.add_argument("package", nargs="+",
140 help=_("Give filename of at least one package."))
141 sub_install.add_argument("action", action="store_const", const="localinstall")
142
143 def parse_command_remove(self):
144 # Implement the "remove" command.
145 sub_remove = self.sub_commands.add_parser("remove",
146 help=_("Remove one or more packages from the system."))
147 sub_remove.add_argument("package", nargs="+",
148 help=_("Give name of at least one package to remove."))
149 sub_remove.add_argument("action", action="store_const", const="remove")
150
151 def parse_command_update(self):
152 # Implement the "update" command.
153 sub_update = self.sub_commands.add_parser("update",
154 help=_("Update the whole system or one specific package."))
155 sub_update.add_argument("package", nargs="*",
156 help=_("Give a name of a package to update or leave emtpy for all."))
157 sub_update.add_argument("action", action="store_const", const="update")
158
159 def parse_command_check_update(self):
160 # Implement the "check-update" command.
161 sub_check_update = self.sub_commands.add_parser("check-update",
162 help=_("Check, if there are any updates available."))
163 sub_check_update.add_argument("package", nargs="*",
164 help=_("Give a name of a package to update or leave emtpy for all."))
165 sub_check_update.add_argument("action", action="store_const", const="check_update")
166
167 def parse_command_info(self):
168 # Implement the "info" command.
169 sub_info = self.sub_commands.add_parser("info",
170 help=_("Print some information about the given package(s)."))
171 sub_info.add_argument("package", nargs="+",
172 help=_("Give at least the name of one package."))
173 sub_info.add_argument("action", action="store_const", const="info")
174
175 def parse_command_search(self):
176 # Implement the "search" command.
177 sub_search = self.sub_commands.add_parser("search",
178 help=_("Search for a given pattern."))
179 sub_search.add_argument("pattern",
180 help=_("A pattern to search for."))
181 sub_search.add_argument("action", action="store_const", const="search")
182
183 def parse_command_provides(self):
184 # Implement the "provides" command
185 sub_provides = self.sub_commands.add_parser("provides",
186 help=_("Get a list of packages that provide a given file or feature."))
187 sub_provides.add_argument("pattern", nargs="+",
188 help=_("File or feature to search for."))
189 sub_provides.add_argument("action", action="store_const", const="provides")
190
191 def parse_command_grouplist(self):
192 # Implement the "grouplist" command
193 sub_grouplist = self.sub_commands.add_parser("grouplist",
194 help=_("Get list of packages that belong to the given group."))
195 sub_grouplist.add_argument("group", nargs=1,
196 help=_("Group name to search for."))
197 sub_grouplist.add_argument("action", action="store_const", const="grouplist")
198
199 def parse_command_groupinstall(self):
200 # Implement the "grouplist" command
201 sub_groupinstall = self.sub_commands.add_parser("groupinstall",
202 help=_("Install all packages that belong to the given group."))
203 sub_groupinstall.add_argument("group", nargs=1,
204 help=_("Group name."))
205 sub_groupinstall.add_argument("action", action="store_const", const="groupinstall")
206
207 def parse_command_repolist(self):
208 # Implement the "repolist" command
209 sub_repolist = self.sub_commands.add_parser("repolist",
210 help=_("List all currently enabled repositories."))
211 sub_repolist.add_argument("action", action="store_const", const="repolist")
212
213 def parse_command_clean(self):
214 sub_clean = self.sub_commands.add_parser("clean", help=_("Cleanup commands."))
215
216 sub_clean_commands = sub_clean.add_subparsers()
217
218 self.parse_command_clean_all(sub_clean_commands)
219
220 def parse_command_clean_all(self, sub_commands):
221 sub_create = sub_commands.add_parser("all",
222 help=_("Cleanup all temporary files."))
223 sub_create.add_argument("action", action="store_const", const="clean_all")
224
225 def parse_command_check(self):
226 # Implement the "check" command
227 sub_check = self.sub_commands.add_parser("check",
228 help=_("Check the system for any errors."))
229 sub_check.add_argument("action", action="store_const", const="check")
230
231 def parse_command_resolvdep(self):
232 # Implement the "resolvdep" command.
233 sub_resolvdep = self.sub_commands.add_parser("resolvdep",
234 help=_("Check the dependencies for a particular package."))
235 sub_resolvdep.add_argument("package", nargs="+",
236 help=_("Give name of at least one package to check."))
237 sub_resolvdep.add_argument("action", action="store_const", const="resolvdep")
238
239 def run(self):
240 action = self.args.action
241
242 if not self.action2func.has_key(action):
243 raise
244
245 try:
246 func = self.action2func[action]
247 except KeyError:
248 raise # XXX catch and return better error message
249
250 return func()
251
252 def handle_info(self, long=False):
253 pkgs = pakfire.info(self.args.package, **self.pakfire_args)
254
255 for pkg in pkgs:
256 print pkg.dump(long=long)
257
258 def handle_search(self):
259 pkgs = pakfire.search(self.args.pattern, **self.pakfire_args)
260
261 for pkg in pkgs:
262 print pkg.dump(short=True)
263
264 def handle_update(self):
265 pakfire.update(self.args.package, **self.pakfire_args)
266
267 def handle_check_update(self):
268 pakfire.update(self.args.package, check=True, **self.pakfire_args)
269
270 def handle_install(self):
271 pakfire.install(self.args.package, **self.pakfire_args)
272
273 def handle_localinstall(self):
274 pakfire.localinstall(self.args.package, **self.pakfire_args)
275
276 def handle_remove(self):
277 pakfire.remove(self.args.package, **self.pakfire_args)
278
279 def handle_provides(self):
280 pkgs = pakfire.provides(self.args.pattern, **self.pakfire_args)
281
282 for pkg in pkgs:
283 print pkg.dump()
284
285 def handle_grouplist(self):
286 pkgs = pakfire.grouplist(self.args.group[0], **self.pakfire_args)
287
288 for pkg in pkgs:
289 print " * %s" % pkg
290
291 def handle_groupinstall(self):
292 pakfire.groupinstall(self.args.group[0], **self.pakfire_args)
293
294 def handle_repolist(self):
295 repos = pakfire.repo_list(**self.pakfire_args)
296
297 FORMAT = " %-20s %8s %12s %12s "
298
299 title = FORMAT % (_("Repository"), _("Enabled"), _("Priority"), _("Packages"))
300 print title
301 print "=" * len(title) # spacing line
302
303 for repo in repos:
304 # Skip the installed repository.
305 if repo.name == "installed":
306 continue
307
308 print FORMAT % (repo.name, repo.enabled, repo.priority, len(repo))
309
310 def handle_clean_all(self):
311 print _("Cleaning up everything...")
312
313 pakfire.clean_all(**self.pakfire_args)
314
315 def handle_check(self):
316 pakfire.check(**self.pakfire_args)
317
318 def handle_resolvdep(self):
319 pakfire.resolvdep(self.args.package, **self.pakfire_args)
320
321
322 class CliBuilder(Cli):
323 def __init__(self):
324 # Check if we are already running in a pakfire container. In that
325 # case, we cannot start another pakfire-builder.
326 if os.environ.get("container", None) == "pakfire-builder":
327 raise PakfireContainerError, _("You cannot run pakfire-builder in a pakfire chroot.")
328
329 self.parser = argparse.ArgumentParser(
330 description = _("Pakfire builder command line interface."),
331 )
332
333 self.parse_common_arguments()
334
335 # Add sub-commands.
336 self.sub_commands = self.parser.add_subparsers()
337
338 self.parse_command_build()
339 self.parse_command_dist()
340 self.parse_command_info()
341 self.parse_command_search()
342 self.parse_command_shell()
343 self.parse_command_update()
344 self.parse_command_provides()
345 self.parse_command_grouplist()
346 self.parse_command_repolist()
347 self.parse_command_clean()
348 self.parse_command_resolvdep()
349
350 # Finally parse all arguments from the command line and save them.
351 self.args = self.parser.parse_args()
352
353 self.action2func = {
354 "build" : self.handle_build,
355 "dist" : self.handle_dist,
356 "update" : self.handle_update,
357 "info" : self.handle_info,
358 "search" : self.handle_search,
359 "shell" : self.handle_shell,
360 "provides" : self.handle_provides,
361 "grouplist" : self.handle_grouplist,
362 "repolist" : self.handle_repolist,
363 "clean_all" : self.handle_clean_all,
364 "resolvdep" : self.handle_resolvdep,
365 }
366
367 @property
368 def pakfire_args(self):
369 ret = { "mode" : "builder" }
370
371 if hasattr(self.args, "disable_repo"):
372 ret["disable_repos"] = self.args.disable_repo
373
374 if hasattr(self.args, "enable_repo"):
375 ret["enable_repos"] = self.args.enable_repo
376
377 if hasattr(self.args, "offline"):
378 ret["offline"] = self.args.offline
379
380 return ret
381
382 def parse_command_update(self):
383 # Implement the "update" command.
384 sub_update = self.sub_commands.add_parser("update",
385 help=_("Update the package indexes."))
386 sub_update.add_argument("action", action="store_const", const="update")
387
388 def parse_command_build(self):
389 # Implement the "build" command.
390 sub_build = self.sub_commands.add_parser("build",
391 help=_("Build one or more packages."))
392 sub_build.add_argument("package", nargs=1,
393 help=_("Give name of at least one package to build."))
394 sub_build.add_argument("action", action="store_const", const="build")
395
396 sub_build.add_argument("-a", "--arch",
397 help=_("Build the package for the given architecture."))
398 sub_build.add_argument("--resultdir", nargs="?",
399 help=_("Path were the output files should be copied to."))
400 sub_build.add_argument("-m", "--mode", nargs="?", default="development",
401 help=_("Mode to run in. Is either 'release' or 'development' (default)."))
402 sub_build.add_argument("--after-shell", action="store_true",
403 help=_("Run a shell after a successful build."))
404
405 def parse_command_shell(self):
406 # Implement the "shell" command.
407 sub_shell = self.sub_commands.add_parser("shell",
408 help=_("Go into a shell."))
409 sub_shell.add_argument("package", nargs="?",
410 help=_("Give name of a package."))
411 sub_shell.add_argument("action", action="store_const", const="shell")
412
413 sub_shell.add_argument("-a", "--arch",
414 help=_("Emulated architecture in the shell."))
415 sub_shell.add_argument("-m", "--mode", nargs="?", default="development",
416 help=_("Mode to run in. Is either 'release' or 'development' (default)."))
417
418 def parse_command_dist(self):
419 # Implement the "dist" command.
420 sub_dist = self.sub_commands.add_parser("dist",
421 help=_("Generate a source package."))
422 sub_dist.add_argument("package", nargs="+",
423 help=_("Give name(s) of a package(s)."))
424 sub_dist.add_argument("action", action="store_const", const="dist")
425
426 sub_dist.add_argument("--resultdir", nargs="?",
427 help=_("Path were the output files should be copied to."))
428
429 def handle_info(self):
430 Cli.handle_info(self, long=True)
431
432 def handle_build(self):
433 # Get the package descriptor from the command line options
434 pkg = self.args.package[0]
435
436 # Check, if we got a regular file
437 if os.path.exists(pkg):
438 pkg = os.path.abspath(pkg)
439
440 else:
441 raise FileNotFoundError, pkg
442
443 # Create distribution configuration from command line.
444 distro_config = {
445 "arch" : self.args.arch,
446 }
447
448 pakfire.build(pkg, builder_mode=self.args.mode,
449 distro_config=distro_config, resultdirs=[self.args.resultdir,],
450 shell=True, after_shell=self.args.after_shell, **self.pakfire_args)
451
452 def handle_shell(self):
453 pkg = None
454
455 # Get the package descriptor from the command line options
456 if self.args.package:
457 pkg = self.args.package
458
459 # Check, if we got a regular file
460 if os.path.exists(pkg):
461 pkg = os.path.abspath(pkg)
462
463 else:
464 raise FileNotFoundError, pkg
465
466 # Create distribution configuration from command line.
467 distro_config = {
468 "arch" : self.args.arch,
469 }
470
471 pakfire.shell(pkg, builder_mode=self.args.mode,
472 distro_config=distro_config, **self.pakfire_args)
473
474 def handle_dist(self):
475 # Get the packages from the command line options
476 pkgs = []
477
478 for pkg in self.args.package:
479 # Check, if we got a regular file
480 if os.path.exists(pkg):
481 pkg = os.path.abspath(pkg)
482 pkgs.append(pkg)
483
484 else:
485 raise FileNotFoundError, pkg
486
487 pakfire.dist(pkgs, resultdirs=[self.args.resultdir,],
488 **self.pakfire_args)
489
490 def handle_provides(self):
491 pkgs = pakfire.provides(self.args.pattern, **self.pakfire_args)
492
493 for pkg in pkgs:
494 print pkg.dump(long=True)
495
496
497 class CliServer(Cli):
498 def __init__(self):
499 self.parser = argparse.ArgumentParser(
500 description = _("Pakfire server command line interface."),
501 )
502
503 self.parse_common_arguments()
504
505 # Add sub-commands.
506 self.sub_commands = self.parser.add_subparsers()
507
508 self.parse_command_build()
509 self.parse_command_keepalive()
510 self.parse_command_repoupdate()
511 self.parse_command_repo()
512
513 # Finally parse all arguments from the command line and save them.
514 self.args = self.parser.parse_args()
515
516 self.server = server.Server(**self.pakfire_args)
517
518 self.action2func = {
519 "build" : self.handle_build,
520 "keepalive" : self.handle_keepalive,
521 "repoupdate" : self.handle_repoupdate,
522 "repo_create": self.handle_repo_create,
523 }
524
525 @property
526 def pakfire_args(self):
527 ret = { "mode" : "server" }
528
529 if hasattr(self.args, "offline"):
530 ret["offline"] = self.args.offline
531
532 return ret
533
534 def parse_command_build(self):
535 # Implement the "build" command.
536 sub_keepalive = self.sub_commands.add_parser("build",
537 help=_("Request a build job from the server."))
538 sub_keepalive.add_argument("action", action="store_const", const="build")
539
540 def parse_command_keepalive(self):
541 # Implement the "keepalive" command.
542 sub_keepalive = self.sub_commands.add_parser("keepalive",
543 help=_("Send a keepalive to the server."))
544 sub_keepalive.add_argument("action", action="store_const",
545 const="keepalive")
546
547 def parse_command_repoupdate(self):
548 # Implement the "repoupdate" command.
549 sub_repoupdate = self.sub_commands.add_parser("repoupdate",
550 help=_("Update all repositories."))
551 sub_repoupdate.add_argument("action", action="store_const",
552 const="repoupdate")
553
554 def parse_command_repo(self):
555 sub_repo = self.sub_commands.add_parser("repo",
556 help=_("Repository management commands."))
557
558 sub_repo_commands = sub_repo.add_subparsers()
559
560 self.parse_command_repo_create(sub_repo_commands)
561
562 def parse_command_repo_create(self, sub_commands):
563 sub_create = sub_commands.add_parser("create",
564 help=_("Create a new repository index."))
565 sub_create.add_argument("path", nargs=1, help=_("Path to the packages."))
566 sub_create.add_argument("inputs", nargs="+", help=_("Path to input packages."))
567 sub_create.add_argument("action", action="store_const", const="repo_create")
568
569 def handle_keepalive(self):
570 self.server.update_info()
571
572 def handle_build(self):
573 self.server.build_job()
574
575 def handle_repoupdate(self):
576 self.server.update_repositories()
577
578 def handle_repo_create(self):
579 path = self.args.path[0]
580
581 pakfire.repo_create(path, self.args.inputs, **self.pakfire_args)
582
583
584 class CliBuilderIntern(Cli):
585 def __init__(self):
586 self.parser = argparse.ArgumentParser(
587 description = _("Pakfire builder command line interface."),
588 )
589
590 self.parse_common_arguments()
591
592 # Add sub-commands.
593 self.sub_commands = self.parser.add_subparsers()
594
595 self.parse_command_build()
596
597 # Finally parse all arguments from the command line and save them.
598 self.args = self.parser.parse_args()
599
600 self.action2func = {
601 "build" : self.handle_build,
602 }
603
604 def parse_command_build(self):
605 # Implement the "build" command.
606 sub_build = self.sub_commands.add_parser("build",
607 help=_("Build one or more packages."))
608 sub_build.add_argument("package", nargs=1,
609 help=_("Give name of at least one package to build."))
610 sub_build.add_argument("action", action="store_const", const="build")
611
612 sub_build.add_argument("-a", "--arch",
613 help=_("Build the package for the given architecture."))
614 sub_build.add_argument("--resultdir", nargs="?",
615 help=_("Path were the output files should be copied to."))
616 sub_build.add_argument("-m", "--mode", nargs="?", default="development",
617 help=_("Mode to run in. Is either 'release' or 'development' (default)."))
618 sub_build.add_argument("--nodeps", action="store_true",
619 help=_("Do not verify build dependencies."))
620
621 def handle_build(self):
622 # Get the package descriptor from the command line options
623 pkg = self.args.package[0]
624
625 # Check, if we got a regular file
626 if os.path.exists(pkg):
627 pkg = os.path.abspath(pkg)
628 else:
629 raise FileNotFoundError, pkg
630
631 # Create distribution configuration from command line.
632 distro_config = {
633 "arch" : self.args.arch,
634 }
635
636 pakfire._build(pkg, builder_mode=self.args.mode,
637 distro_config=distro_config, resultdir=self.args.resultdir,
638 nodeps=self.args.nodeps, **self.pakfire_args)