]> git.ipfire.org Git - people/stevee/pakfire.git/blob - python/pakfire/cli.py
Fix pakfire-client CLI.
[people/stevee/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 datetime
24 import os
25 import shutil
26 import sys
27 import tempfile
28
29 import base
30 import client
31 import config
32 import logger
33 import packages
34 import repository
35 import server
36 import transaction
37 import util
38
39 from system import system
40 from constants import *
41 from i18n import _
42
43 # Initialize a very simple logging that is removed when a Pakfire instance
44 # is started.
45 logger.setup_logging()
46
47 class Cli(object):
48 pakfire = base.Pakfire
49
50 def __init__(self):
51 self.parser = argparse.ArgumentParser(
52 description = _("Pakfire command line interface."),
53 )
54
55 self.parse_common_arguments()
56
57 self.parser.add_argument("--root", metavar="PATH",
58 default="/",
59 help=_("The path where pakfire should operate in."))
60
61 # Add sub-commands.
62 self.sub_commands = self.parser.add_subparsers()
63
64 self.parse_command_install()
65 self.parse_command_reinstall()
66 self.parse_command_remove()
67 self.parse_command_info()
68 self.parse_command_search()
69 self.parse_command_check_update()
70 self.parse_command_update()
71 self.parse_command_downgrade()
72 self.parse_command_provides()
73 self.parse_command_grouplist()
74 self.parse_command_groupinstall()
75 self.parse_command_repolist()
76 self.parse_command_clean()
77 self.parse_command_check()
78 self.parse_command_resolvdep()
79 self.parse_command_extract()
80
81 # Finally parse all arguments from the command line and save them.
82 self.args = self.parser.parse_args()
83
84 self.action2func = {
85 "install" : self.handle_install,
86 "reinstall" : self.handle_reinstall,
87 "remove" : self.handle_remove,
88 "check_update" : self.handle_check_update,
89 "update" : self.handle_update,
90 "downgrade" : self.handle_downgrade,
91 "info" : self.handle_info,
92 "search" : self.handle_search,
93 "provides" : self.handle_provides,
94 "grouplist" : self.handle_grouplist,
95 "groupinstall" : self.handle_groupinstall,
96 "repolist" : self.handle_repolist,
97 "clean_all" : self.handle_clean_all,
98 "check" : self.handle_check,
99 "resolvdep" : self.handle_resolvdep,
100 "extract" : self.handle_extract,
101 }
102
103 @property
104 def pakfire_args(self):
105 ret = {}
106
107 if hasattr(self.args, "root"):
108 ret["path"] = self.args.root
109
110 if hasattr(self.args, "offline") and self.args.offline:
111 ret["downloader"] = {
112 "offline" : self.args.offline,
113 }
114
115 if hasattr(self.args, "config"):
116 ret["configs"] = self.args.config
117 else:
118 ret["configs"] = None
119
120 return ret
121
122 def create_pakfire(self, cls=None, **kwargs):
123 if cls is None:
124 cls = self.pakfire
125
126 args = self.pakfire_args
127 args.update(kwargs)
128
129 p = cls(**args)
130
131 # Disable repositories.
132 for repo in self.args.disable_repo:
133 p.repos.disable_repo(repo)
134
135 # Enable repositories.
136 for repo in self.args.enable_repo:
137 p.repos.enable_repo(repo)
138
139 return p
140
141 def parse_common_arguments(self, offline_switch=True):
142 self.parser.add_argument("--version", action="version",
143 version="%(prog)s " + PAKFIRE_VERSION)
144
145 self.parser.add_argument("-v", "--verbose", action="store_true",
146 help=_("Enable verbose output."))
147
148 self.parser.add_argument("-c", "--config", nargs="?",
149 help=_("Path to a configuration file to load."))
150
151 self.parser.add_argument("--disable-repo", nargs="*", metavar="REPO",
152 help=_("Disable a repository temporarily."), default=[])
153
154 self.parser.add_argument("--enable-repo", nargs="*", metavar="REPO",
155 help=_("Enable a repository temporarily."), default=[])
156
157 if offline_switch:
158 self.parser.add_argument("--offline", action="store_true",
159 help=_("Run pakfire in offline mode."))
160
161 def parse_command_install(self):
162 # Implement the "install" command.
163 sub_install = self.sub_commands.add_parser("install",
164 help=_("Install one or more packages to the system."))
165 sub_install.add_argument("package", nargs="+",
166 help=_("Give name of at least one package to install."))
167 sub_install.add_argument("--without-recommends", action="store_true",
168 help=_("Don't install recommended packages."))
169 sub_install.add_argument("action", action="store_const", const="install")
170
171 def parse_command_reinstall(self):
172 # Implement the "reinstall" command.
173 sub_install = self.sub_commands.add_parser("reinstall",
174 help=_("Reinstall one or more packages."))
175 sub_install.add_argument("package", nargs="+",
176 help=_("Give name of at least one package to reinstall."))
177 sub_install.add_argument("action", action="store_const", const="reinstall")
178
179 def parse_command_remove(self):
180 # Implement the "remove" command.
181 sub_remove = self.sub_commands.add_parser("remove",
182 help=_("Remove one or more packages from the system."))
183 sub_remove.add_argument("package", nargs="+",
184 help=_("Give name of at least one package to remove."))
185 sub_remove.add_argument("action", action="store_const", const="remove")
186
187 @staticmethod
188 def _parse_command_update(parser):
189 parser.add_argument("package", nargs="*",
190 help=_("Give a name of a package to update or leave emtpy for all."))
191 parser.add_argument("--exclude", "-x", nargs="+",
192 help=_("Exclude package from update."))
193 parser.add_argument("--allow-vendorchange", action="store_true",
194 help=_("Allow changing the vendor of packages."))
195 parser.add_argument("--disallow-archchange", action="store_true",
196 help=_("Disallow changing the architecture of packages."))
197
198 def parse_command_update(self):
199 # Implement the "update" command.
200 sub_update = self.sub_commands.add_parser("update",
201 help=_("Update the whole system or one specific package."))
202 sub_update.add_argument("action", action="store_const", const="update")
203 self._parse_command_update(sub_update)
204
205 def parse_command_check_update(self):
206 # Implement the "check-update" command.
207 sub_check_update = self.sub_commands.add_parser("check-update",
208 help=_("Check, if there are any updates available."))
209 sub_check_update.add_argument("action", action="store_const", const="check_update")
210 self._parse_command_update(sub_check_update)
211
212 def parse_command_downgrade(self):
213 # Implement the "downgrade" command.
214 sub_downgrade = self.sub_commands.add_parser("downgrade",
215 help=_("Downgrade one or more packages."))
216 sub_downgrade.add_argument("package", nargs="*",
217 help=_("Give a name of a package to downgrade."))
218 sub_downgrade.add_argument("--allow-vendorchange", action="store_true",
219 help=_("Allow changing the vendor of packages."))
220 sub_downgrade.add_argument("--disallow-archchange", action="store_true",
221 help=_("Disallow changing the architecture of packages."))
222 sub_downgrade.add_argument("action", action="store_const", const="downgrade")
223
224 def parse_command_info(self):
225 # Implement the "info" command.
226 sub_info = self.sub_commands.add_parser("info",
227 help=_("Print some information about the given package(s)."))
228 sub_info.add_argument("package", nargs="+",
229 help=_("Give at least the name of one package."))
230 sub_info.add_argument("action", action="store_const", const="info")
231
232 def parse_command_search(self):
233 # Implement the "search" command.
234 sub_search = self.sub_commands.add_parser("search",
235 help=_("Search for a given pattern."))
236 sub_search.add_argument("pattern",
237 help=_("A pattern to search for."))
238 sub_search.add_argument("action", action="store_const", const="search")
239
240 def parse_command_provides(self):
241 # Implement the "provides" command
242 sub_provides = self.sub_commands.add_parser("provides",
243 help=_("Get a list of packages that provide a given file or feature."))
244 sub_provides.add_argument("pattern", nargs="+",
245 help=_("File or feature to search for."))
246 sub_provides.add_argument("action", action="store_const", const="provides")
247
248 def parse_command_grouplist(self):
249 # Implement the "grouplist" command
250 sub_grouplist = self.sub_commands.add_parser("grouplist",
251 help=_("Get list of packages that belong to the given group."))
252 sub_grouplist.add_argument("group", nargs=1,
253 help=_("Group name to search for."))
254 sub_grouplist.add_argument("action", action="store_const", const="grouplist")
255
256 def parse_command_groupinstall(self):
257 # Implement the "grouplist" command
258 sub_groupinstall = self.sub_commands.add_parser("groupinstall",
259 help=_("Install all packages that belong to the given group."))
260 sub_groupinstall.add_argument("group", nargs=1,
261 help=_("Group name."))
262 sub_groupinstall.add_argument("action", action="store_const", const="groupinstall")
263
264 def parse_command_repolist(self):
265 # Implement the "repolist" command
266 sub_repolist = self.sub_commands.add_parser("repolist",
267 help=_("List all currently enabled repositories."))
268 sub_repolist.add_argument("action", action="store_const", const="repolist")
269
270 def parse_command_clean(self):
271 sub_clean = self.sub_commands.add_parser("clean", help=_("Cleanup commands."))
272
273 sub_clean_commands = sub_clean.add_subparsers()
274
275 self.parse_command_clean_all(sub_clean_commands)
276
277 def parse_command_clean_all(self, sub_commands):
278 sub_create = sub_commands.add_parser("all",
279 help=_("Cleanup all temporary files."))
280 sub_create.add_argument("action", action="store_const", const="clean_all")
281
282 def parse_command_check(self):
283 # Implement the "check" command
284 sub_check = self.sub_commands.add_parser("check",
285 help=_("Check the system for any errors."))
286 sub_check.add_argument("action", action="store_const", const="check")
287
288 def parse_command_resolvdep(self):
289 # Implement the "resolvdep" command.
290 sub_resolvdep = self.sub_commands.add_parser("resolvdep",
291 help=_("Check the dependencies for a particular package."))
292 sub_resolvdep.add_argument("package", nargs=1,
293 help=_("Give name of at least one package to check."))
294 sub_resolvdep.add_argument("action", action="store_const", const="resolvdep")
295
296 def parse_command_extract(self):
297 # Implement the "extract" command.
298 sub_extract = self.sub_commands.add_parser("extract",
299 help=_("Extract a package to a directory."))
300 sub_extract.add_argument("package", nargs="+",
301 help=_("Give name of the file to extract."))
302 sub_extract.add_argument("--target", nargs="?",
303 help=_("Target directory where to extract to."))
304 sub_extract.add_argument("action", action="store_const", const="extract")
305
306 def run(self):
307 action = self.args.action
308
309 try:
310 func = self.action2func[action]
311 except KeyError:
312 raise Exception, "Unhandled action: %s" % action
313
314 return func()
315
316 def handle_info(self, long=False):
317 p = self.create_pakfire()
318
319 for pkg in p.info(self.args.package):
320 print pkg.dump(long=long)
321
322 def handle_search(self):
323 p = self.create_pakfire()
324
325 for pkg in p.search(self.args.pattern):
326 print pkg.dump(short=True)
327
328 def handle_update(self, **args):
329 p = self.create_pakfire()
330 p.update(
331 self.args.package,
332 excludes=self.args.exclude,
333 allow_vendorchange=self.args.allow_vendorchange,
334 allow_archchange=not self.args.disallow_archchange,
335 **args
336 )
337
338 def handle_check_update(self):
339 self.handle_update(check=True)
340
341 def handle_downgrade(self, **args):
342 p = self.create_pakfire()
343 p.downgrade(
344 self.args.package,
345 allow_vendorchange=self.args.allow_vendorchange,
346 allow_archchange=not self.args.disallow_archchange,
347 **args
348 )
349
350 def handle_install(self):
351 p = self.create_pakfire()
352 p.install(self.args.package, ignore_recommended=self.args.without_recommends)
353
354 def handle_reinstall(self):
355 p = self.create_pakfire()
356 p.reinstall(self.args.package)
357
358 def handle_remove(self):
359 p = self.create_pakfire()
360 p.remove(self.args.package)
361
362 def handle_provides(self, long=False):
363 p = self.create_pakfire()
364
365 for pkg in p.provides(self.args.pattern):
366 print pkg.dump(long=long)
367
368 def handle_grouplist(self):
369 p = self.create_pakfire()
370
371 for pkg in p.grouplist(self.args.group[0]):
372 print " * %s" % pkg
373
374 def handle_groupinstall(self):
375 p = self.create_pakfire()
376 p.groupinstall(self.args.group[0])
377
378 def handle_repolist(self):
379 p = self.create_pakfire()
380
381 # Get a list of all repositories.
382 repos = p.repo_list()
383
384 FORMAT = " %-20s %8s %12s %12s "
385 title = FORMAT % (_("Repository"), _("Enabled"), _("Priority"), _("Packages"))
386 print title
387 print "=" * len(title) # spacing line
388
389 for repo in repos:
390 print FORMAT % (repo.name, repo.enabled, repo.priority, len(repo))
391
392 def handle_clean_all(self):
393 print _("Cleaning up everything...")
394
395 p = self.create_pakfire()
396 p.clean_all()
397
398 def handle_check(self):
399 p = self.create_pakfire()
400 p.check()
401
402 def handle_resolvdep(self):
403 p = self.create_pakfire()
404
405 (pkg,) = self.args.package
406
407 solver = p.resolvdep(pkg)
408 assert solver.status
409
410 t = transaction.Transaction.from_solver(p, solver)
411 t.dump()
412
413 def handle_extract(self):
414 p = self.create_pakfire()
415
416 # Open all packages.
417 pkgs = []
418 for pkg in self.args.package:
419 pkg = packages.open(self, None, pkg)
420 pkgs.append(pkg)
421
422 target_prefix = self.args.target
423
424 # Search for binary packages.
425 binary_packages = any([p.type == "binary" for p in pkgs])
426 source_packages = any([p.type == "source" for p in pkgs])
427
428 if binary_packages and source_packages:
429 raise Error, _("Cannot extract mixed package types")
430
431 if binary_packages and not target_prefix:
432 raise Error, _("You must provide an install directory with --target=...")
433
434 elif source_packages and not target_prefix:
435 target_prefix = "/usr/src/packages/"
436
437 if target_prefix == "/":
438 raise Error, _("Cannot extract to /.")
439
440 for pkg in pkgs:
441 if pkg.type == "binary":
442 target_dir = target_prefix
443 elif pkg.type == "source":
444 target_dir = os.path.join(target_prefix, pkg.friendly_name)
445
446 pkg.extract(message=_("Extracting"), prefix=target_dir)
447
448
449 class CliBuilder(Cli):
450 pakfire = base.PakfireBuilder
451
452 def __init__(self):
453 # Check if we are already running in a pakfire container. In that
454 # case, we cannot start another pakfire-builder.
455 if os.environ.get("container", None) == "pakfire-builder":
456 raise PakfireContainerError, _("You cannot run pakfire-builder in a pakfire chroot.")
457
458 self.parser = argparse.ArgumentParser(
459 description = _("Pakfire builder command line interface."),
460 )
461
462 self.parse_common_arguments()
463
464 # Add sub-commands.
465 self.sub_commands = self.parser.add_subparsers()
466
467 self.parse_command_build()
468 self.parse_command_dist()
469 self.parse_command_info()
470 self.parse_command_search()
471 self.parse_command_shell()
472 self.parse_command_update()
473 self.parse_command_provides()
474 self.parse_command_grouplist()
475 self.parse_command_repolist()
476 self.parse_command_clean()
477 self.parse_command_resolvdep()
478 self.parse_command_extract()
479
480 # Finally parse all arguments from the command line and save them.
481 self.args = self.parser.parse_args()
482
483 self.action2func = {
484 "build" : self.handle_build,
485 "dist" : self.handle_dist,
486 "update" : self.handle_update,
487 "info" : self.handle_info,
488 "search" : self.handle_search,
489 "shell" : self.handle_shell,
490 "provides" : self.handle_provides,
491 "grouplist" : self.handle_grouplist,
492 "repolist" : self.handle_repolist,
493 "clean_all" : self.handle_clean_all,
494 "resolvdep" : self.handle_resolvdep,
495 "extract" : self.handle_extract,
496 }
497
498 @property
499 def pakfire_args(self):
500 ret = {
501 "arch" : self.args.arch,
502 }
503
504 if hasattr(self.args, "offline") and self.args.offline:
505 ret["downloader"] = {
506 "offline" : self.args.offline,
507 }
508
509 if hasattr(self.args, "distro"):
510 ret["distro_name"] = self.args.distro
511
512 return ret
513
514 def parse_common_arguments(self, *args, **kwargs):
515 Cli.parse_common_arguments(self, *args, **kwargs)
516
517 self.parser.add_argument("--distro", nargs="?",
518 help=_("Choose the distribution configuration to use for build"))
519
520 self.parser.add_argument("--arch", "-a", nargs="?",
521 help=_("Run pakfire for the given architecture."))
522
523 def parse_command_update(self):
524 # Implement the "update" command.
525 sub_update = self.sub_commands.add_parser("update",
526 help=_("Update the package indexes."))
527 sub_update.add_argument("action", action="store_const", const="update")
528
529 def parse_command_build(self):
530 # Implement the "build" command.
531 sub_build = self.sub_commands.add_parser("build",
532 help=_("Build one or more packages."))
533 sub_build.add_argument("package", nargs=1,
534 help=_("Give name of at least one package to build."))
535 sub_build.add_argument("action", action="store_const", const="build")
536
537 sub_build.add_argument("--resultdir", nargs="?",
538 help=_("Path were the output files should be copied to."))
539 sub_build.add_argument("-m", "--mode", nargs="?", default="development",
540 help=_("Mode to run in. Is either 'release' or 'development' (default)."))
541 sub_build.add_argument("--after-shell", action="store_true",
542 help=_("Run a shell after a successful build."))
543 sub_build.add_argument("--no-install-test", action="store_true",
544 help=_("Do not perform the install test."))
545
546 def parse_command_shell(self):
547 # Implement the "shell" command.
548 sub_shell = self.sub_commands.add_parser("shell",
549 help=_("Go into a shell."))
550 sub_shell.add_argument("package", nargs="?",
551 help=_("Give name of a package."))
552 sub_shell.add_argument("action", action="store_const", const="shell")
553
554 sub_shell.add_argument("-m", "--mode", nargs="?", default="development",
555 help=_("Mode to run in. Is either 'release' or 'development' (default)."))
556
557 def parse_command_dist(self):
558 # Implement the "dist" command.
559 sub_dist = self.sub_commands.add_parser("dist",
560 help=_("Generate a source package."))
561 sub_dist.add_argument("package", nargs="+",
562 help=_("Give name(s) of a package(s)."))
563 sub_dist.add_argument("action", action="store_const", const="dist")
564
565 sub_dist.add_argument("--resultdir", nargs="?",
566 help=_("Path were the output files should be copied to."))
567
568 def handle_info(self):
569 Cli.handle_info(self, long=True)
570
571 def handle_build(self):
572 # Get the package descriptor from the command line options
573 pkg = self.args.package[0]
574
575 # Check, if we got a regular file
576 if os.path.exists(pkg):
577 pkg = os.path.abspath(pkg)
578
579 else:
580 raise FileNotFoundError, pkg
581
582 # Check whether to enable the install test.
583 install_test = not self.args.no_install_test
584
585 if self.args.mode == "release":
586 release_build = True
587 else:
588 release_build = False
589
590 p = self.create_pakfire()
591 p.build(pkg,
592 install_test=install_test,
593 resultdirs=[self.args.resultdir,],
594 shell=True,
595 after_shell=self.args.after_shell,
596 release_build=release_build,
597 )
598
599 def handle_shell(self):
600 pkg = None
601
602 # Get the package descriptor from the command line options
603 if self.args.package:
604 pkg = self.args.package
605
606 # Check, if we got a regular file
607 if os.path.exists(pkg):
608 pkg = os.path.abspath(pkg)
609
610 else:
611 raise FileNotFoundError, pkg
612
613 if self.args.mode == "release":
614 release_build = True
615 else:
616 release_build = False
617
618 p = self.create_pakfire()
619 p.shell(pkg, release_build=release_build)
620
621 def handle_dist(self):
622 # Get the packages from the command line options
623 pkgs = []
624
625 for pkg in self.args.package:
626 # Check, if we got a regular file
627 if os.path.exists(pkg):
628 pkg = os.path.abspath(pkg)
629 pkgs.append(pkg)
630
631 else:
632 raise FileNotFoundError, pkg
633
634 # Put packages to where the user said or our
635 # current working directory.
636 resultdir = self.args.resultdir or os.getcwd()
637
638 p = self.create_pakfire()
639 for pkg in pkgs:
640 p.dist(pkg, resultdir=resultdir)
641
642 def handle_provides(self):
643 Cli.handle_provides(self, long=True)
644
645
646 class CliServer(Cli):
647 pakfire = base.PakfireServer
648
649 def __init__(self):
650 self.parser = argparse.ArgumentParser(
651 description = _("Pakfire server command line interface."),
652 )
653
654 self.parse_common_arguments()
655
656 # Add sub-commands.
657 self.sub_commands = self.parser.add_subparsers()
658
659 self.parse_command_build()
660 self.parse_command_keepalive()
661 self.parse_command_repoupdate()
662 self.parse_command_repo()
663 self.parse_command_info()
664
665 # Finally parse all arguments from the command line and save them.
666 self.args = self.parser.parse_args()
667
668 self.server = server.Server(**self.pakfire_args)
669
670 self.action2func = {
671 "build" : self.handle_build,
672 "info" : self.handle_info,
673 "keepalive" : self.handle_keepalive,
674 "repoupdate" : self.handle_repoupdate,
675 "repo_create": self.handle_repo_create,
676 }
677
678 @property
679 def pakfire_args(self):
680 ret = {}
681
682 if hasattr(self.args, "offline") and self.args.offline:
683 ret["downloader"] = {
684 "offline" : self.args.offline,
685 }
686
687 return ret
688
689 def parse_command_build(self):
690 # Implement the "build" command.
691 sub_build = self.sub_commands.add_parser("build",
692 help=_("Send a scrach build job to the server."))
693 sub_build.add_argument("package", nargs=1,
694 help=_("Give name of at least one package to build."))
695 sub_build.add_argument("--arch", "-a",
696 help=_("Limit build to only these architecture(s)."))
697 sub_build.add_argument("action", action="store_const", const="build")
698
699 def parse_command_keepalive(self):
700 # Implement the "keepalive" command.
701 sub_keepalive = self.sub_commands.add_parser("keepalive",
702 help=_("Send a keepalive to the server."))
703 sub_keepalive.add_argument("action", action="store_const",
704 const="keepalive")
705
706 def parse_command_repoupdate(self):
707 # Implement the "repoupdate" command.
708 sub_repoupdate = self.sub_commands.add_parser("repoupdate",
709 help=_("Update all repositories."))
710 sub_repoupdate.add_argument("action", action="store_const",
711 const="repoupdate")
712
713 def parse_command_repo(self):
714 sub_repo = self.sub_commands.add_parser("repo",
715 help=_("Repository management commands."))
716
717 sub_repo_commands = sub_repo.add_subparsers()
718
719 self.parse_command_repo_create(sub_repo_commands)
720
721 def parse_command_repo_create(self, sub_commands):
722 sub_create = sub_commands.add_parser("create",
723 help=_("Create a new repository index."))
724 sub_create.add_argument("path", nargs=1,
725 help=_("Path to the packages."))
726 sub_create.add_argument("inputs", nargs="+",
727 help=_("Path to input packages."))
728 sub_create.add_argument("--key", "-k", nargs="?",
729 help=_("Key to sign the repository with."))
730 sub_create.add_argument("action", action="store_const", const="repo_create")
731
732 def parse_command_info(self):
733 sub_info = self.sub_commands.add_parser("info",
734 help=_("Dump some information about this machine."))
735 sub_info.add_argument("action", action="store_const", const="info")
736
737 def handle_keepalive(self):
738 self.server.update_info()
739
740 def handle_build(self):
741 # Arch.
742 if self.args.arch:
743 arches = self.args.arch.split()
744
745 (package,) = self.args.package
746
747 self.server.create_scratch_build({})
748 return
749
750 # Temporary folter for source package.
751 tmpdir = "/tmp/pakfire-%s" % util.random_string()
752
753 try:
754 os.makedirs(tmpdir)
755
756 pakfire.dist(package, resultdir=[tmpdir,])
757
758 for file in os.listdir(tmpdir):
759 file = os.path.join(tmpdir, file)
760
761 print file
762
763 finally:
764 if os.path.exists(tmpdir):
765 util.rm(tmpdir)
766
767 def handle_repoupdate(self):
768 self.server.update_repositories()
769
770 def handle_repo_create(self):
771 path = self.args.path[0]
772
773 p = self.pakfire(**self.pakfire_args)
774 p.repo_create(path, self.args.inputs, key_id=self.args.key)
775
776 def handle_info(self):
777 info = self.server.info()
778
779 print "\n".join(info)
780
781
782 class CliBuilderIntern(Cli):
783 def __init__(self):
784 self.parser = argparse.ArgumentParser(
785 description = _("Pakfire builder command line interface."),
786 )
787
788 self.parse_common_arguments()
789
790 # Add sub-commands.
791 self.sub_commands = self.parser.add_subparsers()
792
793 self.parse_command_build()
794
795 # Finally parse all arguments from the command line and save them.
796 self.args = self.parser.parse_args()
797
798 self.action2func = {
799 "build" : self.handle_build,
800 }
801
802 def parse_command_build(self):
803 # Implement the "build" command.
804 sub_build = self.sub_commands.add_parser("build",
805 help=_("Build one or more packages."))
806 sub_build.add_argument("package", nargs=1,
807 help=_("Give name of at least one package to build."))
808 sub_build.add_argument("action", action="store_const", const="build")
809
810 sub_build.add_argument("-a", "--arch",
811 help=_("Build the package for the given architecture."))
812 sub_build.add_argument("--resultdir", nargs="?",
813 help=_("Path were the output files should be copied to."))
814 sub_build.add_argument("-m", "--mode", nargs="?", default="development",
815 help=_("Mode to run in. Is either 'release' or 'development' (default)."))
816 sub_build.add_argument("--nodeps", action="store_true",
817 help=_("Do not verify build dependencies."))
818 sub_build.add_argument("--prepare", action="store_true",
819 help=_("Only run the prepare stage."))
820
821 def handle_build(self):
822 # Get the package descriptor from the command line options
823 pkg = self.args.package[0]
824
825 # Check, if we got a regular file
826 if os.path.exists(pkg):
827 pkg = os.path.abspath(pkg)
828 else:
829 raise FileNotFoundError, pkg
830
831 # Create pakfire instance.
832 c = config.ConfigBuilder()
833 p = base.Pakfire(arch = self.args.arch, config = c)
834
835 # Disable all repositories.
836 if self.args.nodeps:
837 p.repos.disable_repo("*")
838
839 # Limit stages that are to be run.
840 if self.args.prepare:
841 stages = ["prepare"]
842 else:
843 stages = None
844
845 p.build(pkg, resultdir=self.args.resultdir, stages=stages)
846
847
848 class CliClient(Cli):
849 pakfire = base.PakfireClient
850
851 def __init__(self):
852 self.parser = argparse.ArgumentParser(
853 description = _("Pakfire client command line interface."),
854 )
855
856 self.parse_common_arguments(offline_switch=True)
857
858 # Add sub-commands.
859 self.sub_commands = self.parser.add_subparsers()
860
861 self.parse_command_build()
862 self.parse_command_connection_check()
863 self.parse_command_info()
864 self.parse_command_jobs()
865 self.parse_command_builds()
866 self.parse_command_test()
867
868 # Finally parse all arguments from the command line and save them.
869 self.args = self.parser.parse_args()
870
871 self.action2func = {
872 "build" : self.handle_build,
873 "conn-check" : self.handle_connection_check,
874 "info" : self.handle_info,
875 "jobs_show" : self.handle_jobs_show,
876 "jobs_active" : self.handle_jobs_active,
877 "jobs_latest" : self.handle_jobs_latest,
878 "builds_show" : self.handle_builds_show,
879 "test" : self.handle_test,
880 }
881
882 # Read configuration for the pakfire client.
883 self.config = config.ConfigClient()
884
885 # Create connection to pakfire hub.
886 self.client = client.PakfireUserClient(
887 self.config.get("client", "server"),
888 self.config.get("client", "username"),
889 self.config.get("client", "password"),
890 )
891
892 @property
893 def pakfire_args(self):
894 return {
895 "config" : self.config,
896 }
897
898 def parse_command_build(self):
899 # Parse "build" command.
900 sub_build = self.sub_commands.add_parser("build",
901 help=_("Build a package remotely."))
902 sub_build.add_argument("package", nargs=1,
903 help=_("Give name of a package to build."))
904 sub_build.add_argument("action", action="store_const", const="build")
905
906 sub_build.add_argument("-a", "--arch",
907 help=_("Build the package for the given architecture."))
908
909 def parse_command_info(self):
910 # Implement the "info" command.
911 sub_info = self.sub_commands.add_parser("info",
912 help=_("Print some information about this host."))
913 sub_info.add_argument("action", action="store_const", const="info")
914
915 def parse_command_connection_check(self):
916 # Implement the "conn-check" command.
917 sub_conn_check = self.sub_commands.add_parser("conn-check",
918 help=_("Check the connection to the hub."))
919 sub_conn_check.add_argument("action", action="store_const", const="conn-check")
920
921 def parse_command_jobs(self):
922 sub_jobs = self.sub_commands.add_parser("jobs",
923 help=_("Show information about build jobs."))
924
925 sub_jobs_commands = sub_jobs.add_subparsers()
926
927 self.parse_command_jobs_active(sub_jobs_commands)
928 self.parse_command_jobs_latest(sub_jobs_commands)
929 self.parse_command_jobs_show(sub_jobs_commands)
930
931 def parse_command_jobs_active(self, sub_commands):
932 sub_active = sub_commands.add_parser("active",
933 help=_("Show a list of all active jobs."))
934 sub_active.add_argument("action", action="store_const", const="jobs_active")
935
936 def parse_command_jobs_latest(self, sub_commands):
937 sub_latest = sub_commands.add_parser("latest",
938 help=_("Show a list of all recently finished of failed build jobs."))
939 sub_latest.add_argument("action", action="store_const", const="jobs_latest")
940
941 def parse_command_jobs_show(self, sub_commands):
942 sub_show = sub_commands.add_parser("show",
943 help=_("Show details about given build job."))
944 sub_show.add_argument("job_id", nargs=1, help=_("The ID of the build job."))
945 sub_show.add_argument("action", action="store_const", const="jobs_show")
946
947 def parse_command_builds(self):
948 sub_builds = self.sub_commands.add_parser("builds",
949 help=_("Show information about builds."))
950
951 sub_builds_commands = sub_builds.add_subparsers()
952
953 self.parse_command_builds_show(sub_builds_commands)
954
955 def parse_command_builds_show(self, sub_commands):
956 sub_show = sub_commands.add_parser("show",
957 help=_("Show details about the given build."))
958 sub_show.add_argument("build_id", nargs=1, help=_("The ID of the build."))
959 sub_show.add_argument("action", action="store_const", const="builds_show")
960
961 def parse_command_test(self):
962 sub_test = self.sub_commands.add_parser("test",
963 help=_("Test the connection to the hub."))
964 sub_test.add_argument("error_code", nargs=1, help=_("Error code to test."))
965 sub_test.add_argument("action", action="store_const", const="test")
966
967 def handle_build(self):
968 (package,) = self.args.package
969
970 # XXX just for now, we do only upload source pfm files.
971 assert os.path.exists(package)
972
973 # Create a temporary directory.
974 temp_dir = tempfile.mkdtemp()
975
976 try:
977 if package.endswith(".%s" % MAKEFILE_EXTENSION):
978 pakfire_args = {}
979
980 # Create a source package from the makefile.
981 p = self.pakfire(**self.pakfire_args)
982 package = p.dist(package, temp_dir)
983
984 elif package.endswith(".%s" % PACKAGE_EXTENSION):
985 pass
986
987 else:
988 raise Exception, "Unknown filetype: %s" % package
989
990 # Format arches.
991 if self.args.arch:
992 arches = self.args.arch.replace(",", " ")
993 else:
994 arches = None
995
996 # Create a new build on the server.
997 build = self.client.build_create(package, arches=arches)
998
999 # XXX Print the resulting build.
1000 print build
1001
1002 finally:
1003 # Cleanup the temporary directory and all files.
1004 if os.path.exists(temp_dir):
1005 shutil.rmtree(temp_dir, ignore_errors=True)
1006
1007 def handle_info(self):
1008 ret = []
1009
1010 ret.append("")
1011 ret.append(" PAKFIRE %s" % PAKFIRE_VERSION)
1012 ret.append("")
1013 ret.append(" %-20s: %s" % (_("Hostname"), system.hostname))
1014 ret.append(" %-20s: %s" % (_("Pakfire hub"), self.config.get("client", "server")))
1015 if self.config.get("client", "username") and self.config.get("client", "password"):
1016 ret.append(" %-20s: %s" % \
1017 (_("Username"), self.config.get("client", "username")))
1018 ret.append("")
1019
1020 # Hardware information
1021 ret.append(" %s:" % _("Hardware information"))
1022 ret.append(" %-16s: %s" % (_("CPU model"), system.cpu_model))
1023 ret.append(" %-16s: %s" % (_("Memory"), util.format_size(system.memory)))
1024 ret.append(" %-16s: %s" % (_("Parallelism"), system.parallelism))
1025 ret.append("")
1026 ret.append(" %-16s: %s" % (_("Native arch"), system.native_arch))
1027 if not system.arch == system.native_arch:
1028 ret.append(" %-16s: %s" % (_("Default arch"), system.arch))
1029
1030 header = _("Supported arches")
1031 for arch in system.supported_arches:
1032 ret.append(" %-16s: %s" % (header, arch))
1033 header = ""
1034 ret.append("")
1035
1036 for line in ret:
1037 print line
1038
1039 def handle_connection_check(self):
1040 ret = []
1041
1042 address = self.client.get_my_address()
1043 ret.append(" %-20s: %s" % (_("Your IP address"), address))
1044 ret.append("")
1045
1046 authenticated = self.client.check_auth()
1047 if authenticated:
1048 ret.append(" %s" % _("You are authenticated to the build service:"))
1049
1050 user = self.client.get_user_profile()
1051 assert user, "Could not fetch user infomation"
1052
1053 keys = [
1054 ("name", _("User name")),
1055 ("realname", _("Real name")),
1056 ("email", _("Email address")),
1057 ("registered", _("Registered")),
1058 ]
1059
1060 for key, desc in keys:
1061 ret.append(" %-18s: %s" % (desc, user.get(key)))
1062
1063 else:
1064 ret.append(_("You could not be authenticated to the build service."))
1065
1066 for line in ret:
1067 print line
1068
1069 def _print_jobs(self, jobs, heading=None):
1070 if heading:
1071 print "%s:" % heading
1072 print
1073
1074 for job in jobs:
1075 line = " [%(type)8s] %(name)-30s: %(state)s"
1076
1077 print line % job
1078
1079 print # Empty line at the end.
1080
1081 def handle_jobs_active(self):
1082 jobs = self.client.get_active_jobs()
1083
1084 if not jobs:
1085 print _("No ongoing jobs found.")
1086 return
1087
1088 self._print_jobs(jobs, _("Active build jobs"))
1089
1090 def handle_jobs_latest(self):
1091 jobs = self.client.get_latest_jobs()
1092
1093 if not jobs:
1094 print _("No jobs found.")
1095 return
1096
1097 self._print_jobs(jobs, _("Recently processed build jobs"))
1098
1099 def handle_builds_show(self):
1100 (build_id,) = self.args.build_id
1101
1102 build = self.client.get_build(build_id)
1103 if not build:
1104 print _("A build with ID %s could not be found.") % build_id
1105 return
1106
1107 print _("Build: %(name)s") % build
1108
1109 fmt = "%-14s: %s"
1110 lines = [
1111 fmt % (_("State"), build["state"]),
1112 fmt % (_("Priority"), build["priority"]),
1113 ]
1114
1115 lines.append("%s:" % _("Jobs"))
1116 for job in build["jobs"]:
1117 lines.append(" * [%(uuid)s] %(name)-30s: %(state)s" % job)
1118
1119 for line in lines:
1120 print " ", line
1121 print
1122
1123 def handle_jobs_show(self):
1124 (job_id,) = self.args.job_id
1125
1126 job = self.client.get_job(job_id)
1127 if not job:
1128 print _("A job with ID %s could not be found.") % job_id
1129 return
1130
1131 builder = None
1132 if job["builder_id"]:
1133 builder = self.client.get_builder(job["builder_id"])
1134
1135 print _("Job: %(name)s") % job
1136
1137 fmt = "%-14s: %s"
1138 lines = [
1139 fmt % (_("State"), job["state"]),
1140 fmt % (_("Arch"), job["arch"]),
1141 ]
1142
1143 if builder:
1144 lines += [
1145 fmt % (_("Build host"), builder["name"]),
1146 "",
1147 ]
1148
1149 lines += [
1150 fmt % (_("Time created"), job["time_created"]),
1151 fmt % (_("Time started"), job["time_started"]),
1152 fmt % (_("Time finished"), job["time_finished"]),
1153 fmt % (_("Duration"), job["duration"]),
1154 ]
1155
1156 if job["packages"]:
1157 lines += ["", "%s:" % _("Packages")]
1158
1159 for pkg in job["packages"]:
1160 pkg_lines = [
1161 "* %(friendly_name)s" % pkg,
1162 " %(uuid)s" % pkg,
1163 "",
1164 ]
1165
1166 lines += [" %s" % line for line in pkg_lines]
1167
1168 for line in lines:
1169 print " ", line
1170 print # New line.
1171
1172 def handle_test(self):
1173 error_code = self.args.error_code[0]
1174
1175 try:
1176 error_code = int(error_code)
1177 except ValueError:
1178 error_code = 0
1179
1180 if error_code < 100 or error_code > 999:
1181 raise Error, _("Invalid error code given.")
1182
1183 res = self.client.test_code(error_code)
1184 print _("Reponse from the server: %s") % res
1185
1186
1187 class CliDaemon(Cli):
1188 def __init__(self):
1189 self.parser = argparse.ArgumentParser(
1190 description = _("Pakfire daemon command line interface."),
1191 )
1192
1193 self.parse_common_arguments(offline_switch=True)
1194
1195 # Finally parse all arguments from the command line and save them.
1196 self.args = self.parser.parse_args()
1197
1198 def run(self):
1199 """
1200 Runs the pakfire daemon with provided settings.
1201 """
1202 # Read the configuration file for the daemon.
1203 conf = config.ConfigDaemon()
1204
1205 # Create daemon instance.
1206 d = pakfire.client.PakfireDaemon()
1207 try:
1208 d.run()
1209
1210 # We cannot just kill the daemon, it needs a smooth shutdown.
1211 except (SystemExit, KeyboardInterrupt):
1212 d.shutdown()
1213
1214
1215 class CliKey(Cli):
1216 pakfire = base.PakfireKey
1217
1218 def __init__(self):
1219 self.parser = argparse.ArgumentParser(
1220 description = _("Pakfire key command line interface."),
1221 )
1222
1223 self.parse_common_arguments(offline_switch=True)
1224
1225 # Add sub-commands.
1226 self.sub_commands = self.parser.add_subparsers()
1227
1228 self.parse_command_generate()
1229 self.parse_command_import()
1230 self.parse_command_export()
1231 self.parse_command_delete()
1232 self.parse_command_list()
1233 self.parse_command_sign()
1234 self.parse_command_verify()
1235
1236 # Finally parse all arguments from the command line and save them.
1237 self.args = self.parser.parse_args()
1238
1239 self.action2func = {
1240 "generate" : self.handle_generate,
1241 "import" : self.handle_import,
1242 "export" : self.handle_export,
1243 "delete" : self.handle_delete,
1244 "list" : self.handle_list,
1245 "sign" : self.handle_sign,
1246 "verify" : self.handle_verify,
1247 }
1248
1249 @property
1250 def pakfire_args(self):
1251 return {}
1252
1253 def parse_command_generate(self):
1254 # Parse "generate" command.
1255 sub_gen = self.sub_commands.add_parser("generate",
1256 help=_("Import a key from file."))
1257 sub_gen.add_argument("--realname", nargs=1,
1258 help=_("The real name of the owner of this key."))
1259 sub_gen.add_argument("--email", nargs=1,
1260 help=_("The email address of the owner of this key."))
1261 sub_gen.add_argument("action", action="store_const", const="generate")
1262
1263 def parse_command_import(self):
1264 # Parse "import" command.
1265 sub_import = self.sub_commands.add_parser("import",
1266 help=_("Import a key from file."))
1267 sub_import.add_argument("filename", nargs=1,
1268 help=_("Filename of that key to import."))
1269 sub_import.add_argument("action", action="store_const", const="import")
1270
1271 def parse_command_export(self):
1272 # Parse "export" command.
1273 sub_export = self.sub_commands.add_parser("export",
1274 help=_("Export a key to a file."))
1275 sub_export.add_argument("keyid", nargs=1,
1276 help=_("The ID of the key to export."))
1277 sub_export.add_argument("filename", nargs=1,
1278 help=_("Write the key to this file."))
1279 sub_export.add_argument("action", action="store_const", const="export")
1280
1281 def parse_command_delete(self):
1282 # Parse "delete" command.
1283 sub_del = self.sub_commands.add_parser("delete",
1284 help=_("Delete a key from the local keyring."))
1285 sub_del.add_argument("keyid", nargs=1,
1286 help=_("The ID of the key to delete."))
1287 sub_del.add_argument("action", action="store_const", const="delete")
1288
1289 def parse_command_list(self):
1290 # Parse "list" command.
1291 sub_list = self.sub_commands.add_parser("list",
1292 help=_("List all imported keys."))
1293 sub_list.add_argument("action", action="store_const", const="list")
1294
1295 def parse_command_sign(self):
1296 # Implement the "sign" command.
1297 sub_sign = self.sub_commands.add_parser("sign",
1298 help=_("Sign one or more packages."))
1299 sub_sign.add_argument("--key", "-k", nargs=1,
1300 help=_("Key that is used sign the package(s)."))
1301 sub_sign.add_argument("package", nargs="+",
1302 help=_("Package(s) to sign."))
1303 sub_sign.add_argument("action", action="store_const", const="sign")
1304
1305 def parse_command_verify(self):
1306 # Implement the "verify" command.
1307 sub_verify = self.sub_commands.add_parser("verify",
1308 help=_("Verify one or more packages."))
1309 #sub_verify.add_argument("--key", "-k", nargs=1,
1310 # help=_("Key that is used verify the package(s)."))
1311 sub_verify.add_argument("package", nargs="+",
1312 help=_("Package(s) to verify."))
1313 sub_verify.add_argument("action", action="store_const", const="verify")
1314
1315 def handle_generate(self):
1316 realname = self.args.realname[0]
1317 email = self.args.email[0]
1318
1319 print _("Generating the key may take a moment...")
1320 print
1321
1322 # Generate the key.
1323 p = self.pakfire(**self.pakfire_args)
1324 p.keyring.gen_key(realname, email)
1325
1326 def handle_import(self):
1327 filename = self.args.filename[0]
1328
1329 # Simply import the file.
1330 p = self.pakfire(**self.pakfire_args)
1331 p.keyring.import_key(filename)
1332
1333 def handle_export(self):
1334 keyid = self.args.keyid[0]
1335 filename = self.args.filename[0]
1336
1337 p = self.pakfire(**self.pakfire_args)
1338 p.keyring.export_key(keyid, filename)
1339
1340 def handle_delete(self):
1341 keyid = self.args.keyid[0]
1342
1343 p = self.pakfire(**self.pakfire_args)
1344 p.keyring.delete_key(keyid)
1345
1346 def handle_list(self):
1347 p = self.pakfire(**self.pakfire_args)
1348 for line in p.keyring.list_keys():
1349 print line
1350
1351 def handle_sign(self):
1352 # Get the files from the command line options
1353 files = []
1354
1355 for file in self.args.package:
1356 # Check, if we got a regular file
1357 if os.path.exists(file):
1358 file = os.path.abspath(file)
1359 files.append(file)
1360
1361 else:
1362 raise FileNotFoundError, file
1363
1364 key = self.args.key[0]
1365
1366 # Create pakfire instance.
1367 p = self.pakfire(**self.pakfire_args)
1368
1369 for file in files:
1370 # Open the package.
1371 pkg = packages.open(p, None, file)
1372
1373 print _("Signing %s...") % pkg.friendly_name
1374 pkg.sign(key)
1375
1376 def handle_verify(self):
1377 # Get the files from the command line options
1378 files = []
1379
1380 for file in self.args.package:
1381 # Check, if we got a regular file
1382 if os.path.exists(file) and not os.path.isdir(file):
1383 file = os.path.abspath(file)
1384 files.append(file)
1385
1386 # Create pakfire instance.
1387 p = self.pakfire(**self.pakfire_args)
1388
1389 for file in files:
1390 # Open the package.
1391 pkg = packages.open(p, None, file)
1392
1393 print _("Verifying %s...") % pkg.friendly_name
1394 sigs = pkg.verify()
1395
1396 for sig in sigs:
1397 key = self.pakfire.keyring.get_key(sig.fpr)
1398 if key:
1399 subkey = key.subkeys[0]
1400
1401 print " %s %s" % (subkey.fpr[-16:], key.uids[0].uid)
1402 if sig.validity:
1403 print " %s" % _("This signature is valid.")
1404
1405 else:
1406 print " %s <%s>" % (sig.fpr, _("Unknown key"))
1407 print " %s" % _("Could not check if this signature is valid.")
1408
1409 created = datetime.datetime.fromtimestamp(sig.timestamp)
1410 print " %s" % _("Created: %s") % created
1411
1412 if sig.exp_timestamp:
1413 expires = datetime.datetime.fromtimestamp(sig.exp_timestamp)
1414 print " %s" % _("Expires: %s") % expires
1415
1416 print # Empty line