]> git.ipfire.org Git - people/stevee/pakfire.git/blob - python/pakfire/cli.py
Add option to extract packages.
[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("--allow-archchange", action="store_true",
196 help=_("Allow 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("--allow-archchange", action="store_true",
221 help=_("Allow 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=self.args.allow_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=self.args.allow_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 def __init__(self):
850 self.parser = argparse.ArgumentParser(
851 description = _("Pakfire client command line interface."),
852 )
853
854 self.parse_common_arguments(repo_manage_switches=True, offline_switch=True)
855
856 # Add sub-commands.
857 self.sub_commands = self.parser.add_subparsers()
858
859 self.parse_command_build()
860 self.parse_command_connection_check()
861 self.parse_command_info()
862 self.parse_command_jobs()
863 self.parse_command_builds()
864 self.parse_command_test()
865
866 # Finally parse all arguments from the command line and save them.
867 self.args = self.parser.parse_args()
868
869 self.action2func = {
870 "build" : self.handle_build,
871 "conn-check" : self.handle_connection_check,
872 "info" : self.handle_info,
873 "jobs_show" : self.handle_jobs_show,
874 "jobs_active" : self.handle_jobs_active,
875 "jobs_latest" : self.handle_jobs_latest,
876 "builds_show" : self.handle_builds_show,
877 "test" : self.handle_test,
878 }
879
880 # Read configuration for the pakfire client.
881 self.conf = conf = config.ConfigClient()
882
883 # Create connection to pakfire hub.
884 self.client = client.PakfireUserClient(
885 conf.get("client", "server"),
886 conf.get("client", "username"),
887 conf.get("client", "password"),
888 )
889
890 def parse_command_build(self):
891 # Parse "build" command.
892 sub_build = self.sub_commands.add_parser("build",
893 help=_("Build a package remotely."))
894 sub_build.add_argument("package", nargs=1,
895 help=_("Give name of a package to build."))
896 sub_build.add_argument("action", action="store_const", const="build")
897
898 sub_build.add_argument("-a", "--arch",
899 help=_("Build the package for the given architecture."))
900
901 def parse_command_info(self):
902 # Implement the "info" command.
903 sub_info = self.sub_commands.add_parser("info",
904 help=_("Print some information about this host."))
905 sub_info.add_argument("action", action="store_const", const="info")
906
907 def parse_command_connection_check(self):
908 # Implement the "conn-check" command.
909 sub_conn_check = self.sub_commands.add_parser("conn-check",
910 help=_("Check the connection to the hub."))
911 sub_conn_check.add_argument("action", action="store_const", const="conn-check")
912
913 def parse_command_jobs(self):
914 sub_jobs = self.sub_commands.add_parser("jobs",
915 help=_("Show information about build jobs."))
916
917 sub_jobs_commands = sub_jobs.add_subparsers()
918
919 self.parse_command_jobs_active(sub_jobs_commands)
920 self.parse_command_jobs_latest(sub_jobs_commands)
921 self.parse_command_jobs_show(sub_jobs_commands)
922
923 def parse_command_jobs_active(self, sub_commands):
924 sub_active = sub_commands.add_parser("active",
925 help=_("Show a list of all active jobs."))
926 sub_active.add_argument("action", action="store_const", const="jobs_active")
927
928 def parse_command_jobs_latest(self, sub_commands):
929 sub_latest = sub_commands.add_parser("latest",
930 help=_("Show a list of all recently finished of failed build jobs."))
931 sub_latest.add_argument("action", action="store_const", const="jobs_latest")
932
933 def parse_command_jobs_show(self, sub_commands):
934 sub_show = sub_commands.add_parser("show",
935 help=_("Show details about given build job."))
936 sub_show.add_argument("job_id", nargs=1, help=_("The ID of the build job."))
937 sub_show.add_argument("action", action="store_const", const="jobs_show")
938
939 def parse_command_builds(self):
940 sub_builds = self.sub_commands.add_parser("builds",
941 help=_("Show information about builds."))
942
943 sub_builds_commands = sub_builds.add_subparsers()
944
945 self.parse_command_builds_show(sub_builds_commands)
946
947 def parse_command_builds_show(self, sub_commands):
948 sub_show = sub_commands.add_parser("show",
949 help=_("Show details about the given build."))
950 sub_show.add_argument("build_id", nargs=1, help=_("The ID of the build."))
951 sub_show.add_argument("action", action="store_const", const="builds_show")
952
953 def parse_command_test(self):
954 sub_test = self.sub_commands.add_parser("test",
955 help=_("Test the connection to the hub."))
956 sub_test.add_argument("error_code", nargs=1, help=_("Error code to test."))
957 sub_test.add_argument("action", action="store_const", const="test")
958
959 def handle_build(self):
960 (package,) = self.args.package
961
962 # XXX just for now, we do only upload source pfm files.
963 assert os.path.exists(package)
964
965 # Create a temporary directory.
966 temp_dir = tempfile.mkdtemp()
967
968 try:
969 if package.endswith(".%s" % MAKEFILE_EXTENSION):
970 pakfire_args = {}
971
972 # Create a source package from the makefile.
973 p = self.pakfire(**self.pakfire_args)
974 package = p.dist(package, temp_dir)
975
976 elif package.endswith(".%s" % PACKAGE_EXTENSION):
977 pass
978
979 else:
980 raise Exception, "Unknown filetype: %s" % package
981
982 # Format arches.
983 if self.args.arch:
984 arches = self.args.arch.replace(",", " ")
985 else:
986 arches = None
987
988 # Create a new build on the server.
989 build = self.client.build_create(package, arches=arches)
990
991 # XXX Print the resulting build.
992 print build
993
994 finally:
995 # Cleanup the temporary directory and all files.
996 if os.path.exists(temp_dir):
997 shutil.rmtree(temp_dir, ignore_errors=True)
998
999 def handle_info(self):
1000 ret = []
1001
1002 ret.append("")
1003 ret.append(" PAKFIRE %s" % PAKFIRE_VERSION)
1004 ret.append("")
1005 ret.append(" %-20s: %s" % (_("Hostname"), system.hostname))
1006 ret.append(" %-20s: %s" % (_("Pakfire hub"), self.conf.get("client", "server")))
1007 if self.conf.get("client", "username") and self.conf.get("client", "password"):
1008 ret.append(" %-20s: %s" % \
1009 (_("Username"), self.conf.get("client", "username")))
1010 ret.append("")
1011
1012 # Hardware information
1013 ret.append(" %s:" % _("Hardware information"))
1014 ret.append(" %-16s: %s" % (_("CPU model"), system.cpu_model))
1015 ret.append(" %-16s: %s" % (_("Memory"), util.format_size(system.memory)))
1016 ret.append(" %-16s: %s" % (_("Parallelism"), system.parallelism))
1017 ret.append("")
1018 ret.append(" %-16s: %s" % (_("Native arch"), system.native_arch))
1019 if not system.arch == system.native_arch:
1020 ret.append(" %-16s: %s" % (_("Default arch"), system.arch))
1021
1022 header = _("Supported arches")
1023 for arch in system.supported_arches:
1024 ret.append(" %-16s: %s" % (header, arch))
1025 header = ""
1026 ret.append("")
1027
1028 for line in ret:
1029 print line
1030
1031 def handle_connection_check(self):
1032 ret = []
1033
1034 address = self.client.get_my_address()
1035 ret.append(" %-20s: %s" % (_("Your IP address"), address))
1036 ret.append("")
1037
1038 authenticated = self.client.check_auth()
1039 if authenticated:
1040 ret.append(" %s" % _("You are authenticated to the build service:"))
1041
1042 user = self.client.get_user_profile()
1043 assert user, "Could not fetch user infomation"
1044
1045 keys = [
1046 ("name", _("User name")),
1047 ("realname", _("Real name")),
1048 ("email", _("Email address")),
1049 ("registered", _("Registered")),
1050 ]
1051
1052 for key, desc in keys:
1053 ret.append(" %-18s: %s" % (desc, user.get(key)))
1054
1055 else:
1056 ret.append(_("You could not be authenticated to the build service."))
1057
1058 for line in ret:
1059 print line
1060
1061 def _print_jobs(self, jobs, heading=None):
1062 if heading:
1063 print "%s:" % heading
1064 print
1065
1066 for job in jobs:
1067 line = " [%(type)8s] %(name)-30s: %(state)s"
1068
1069 print line % job
1070
1071 print # Empty line at the end.
1072
1073 def handle_jobs_active(self):
1074 jobs = self.client.get_active_jobs()
1075
1076 if not jobs:
1077 print _("No ongoing jobs found.")
1078 return
1079
1080 self._print_jobs(jobs, _("Active build jobs"))
1081
1082 def handle_jobs_latest(self):
1083 jobs = self.client.get_latest_jobs()
1084
1085 if not jobs:
1086 print _("No jobs found.")
1087 return
1088
1089 self._print_jobs(jobs, _("Recently processed build jobs"))
1090
1091 def handle_builds_show(self):
1092 (build_id,) = self.args.build_id
1093
1094 build = self.client.get_build(build_id)
1095 if not build:
1096 print _("A build with ID %s could not be found.") % build_id
1097 return
1098
1099 print _("Build: %(name)s") % build
1100
1101 fmt = "%-14s: %s"
1102 lines = [
1103 fmt % (_("State"), build["state"]),
1104 fmt % (_("Priority"), build["priority"]),
1105 ]
1106
1107 lines.append("%s:" % _("Jobs"))
1108 for job in build["jobs"]:
1109 lines.append(" * [%(uuid)s] %(name)-30s: %(state)s" % job)
1110
1111 for line in lines:
1112 print " ", line
1113 print
1114
1115 def handle_jobs_show(self):
1116 (job_id,) = self.args.job_id
1117
1118 job = self.client.get_job(job_id)
1119 if not job:
1120 print _("A job with ID %s could not be found.") % job_id
1121 return
1122
1123 builder = None
1124 if job["builder_id"]:
1125 builder = self.client.get_builder(job["builder_id"])
1126
1127 print _("Job: %(name)s") % job
1128
1129 fmt = "%-14s: %s"
1130 lines = [
1131 fmt % (_("State"), job["state"]),
1132 fmt % (_("Arch"), job["arch"]),
1133 ]
1134
1135 if builder:
1136 lines += [
1137 fmt % (_("Build host"), builder["name"]),
1138 "",
1139 ]
1140
1141 lines += [
1142 fmt % (_("Time created"), job["time_created"]),
1143 fmt % (_("Time started"), job["time_started"]),
1144 fmt % (_("Time finished"), job["time_finished"]),
1145 fmt % (_("Duration"), job["duration"]),
1146 ]
1147
1148 if job["packages"]:
1149 lines += ["", "%s:" % _("Packages")]
1150
1151 for pkg in job["packages"]:
1152 pkg_lines = [
1153 "* %(friendly_name)s" % pkg,
1154 " %(uuid)s" % pkg,
1155 "",
1156 ]
1157
1158 lines += [" %s" % line for line in pkg_lines]
1159
1160 for line in lines:
1161 print " ", line
1162 print # New line.
1163
1164 def handle_test(self):
1165 error_code = self.args.error_code[0]
1166
1167 try:
1168 error_code = int(error_code)
1169 except ValueError:
1170 error_code = 0
1171
1172 if error_code < 100 or error_code > 999:
1173 raise Error, _("Invalid error code given.")
1174
1175 res = self.client.test_code(error_code)
1176 print _("Reponse from the server: %s") % res
1177
1178
1179 class CliDaemon(Cli):
1180 def __init__(self):
1181 self.parser = argparse.ArgumentParser(
1182 description = _("Pakfire daemon command line interface."),
1183 )
1184
1185 self.parse_common_arguments(repo_manage_switches=True, offline_switch=True)
1186
1187 # Finally parse all arguments from the command line and save them.
1188 self.args = self.parser.parse_args()
1189
1190 def run(self):
1191 """
1192 Runs the pakfire daemon with provided settings.
1193 """
1194 # Read the configuration file for the daemon.
1195 conf = config.ConfigDaemon()
1196
1197 # Create daemon instance.
1198 d = pakfire.client.PakfireDaemon()
1199 try:
1200 d.run()
1201
1202 # We cannot just kill the daemon, it needs a smooth shutdown.
1203 except (SystemExit, KeyboardInterrupt):
1204 d.shutdown()
1205
1206
1207 class CliKey(Cli):
1208 def __init__(self):
1209 self.parser = argparse.ArgumentParser(
1210 description = _("Pakfire key command line interface."),
1211 )
1212
1213 self.parse_common_arguments(repo_manage_switches=False,
1214 offline_switch=True)
1215
1216 # Add sub-commands.
1217 self.sub_commands = self.parser.add_subparsers()
1218
1219 self.parse_command_generate()
1220 self.parse_command_import()
1221 self.parse_command_export()
1222 self.parse_command_delete()
1223 self.parse_command_list()
1224 self.parse_command_sign()
1225 self.parse_command_verify()
1226
1227 # Finally parse all arguments from the command line and save them.
1228 self.args = self.parser.parse_args()
1229
1230 self.action2func = {
1231 "generate" : self.handle_generate,
1232 "import" : self.handle_import,
1233 "export" : self.handle_export,
1234 "delete" : self.handle_delete,
1235 "list" : self.handle_list,
1236 "sign" : self.handle_sign,
1237 "verify" : self.handle_verify,
1238 }
1239
1240 @property
1241 def pakfire_args(self):
1242 return {}
1243
1244 def parse_command_generate(self):
1245 # Parse "generate" command.
1246 sub_gen = self.sub_commands.add_parser("generate",
1247 help=_("Import a key from file."))
1248 sub_gen.add_argument("--realname", nargs=1,
1249 help=_("The real name of the owner of this key."))
1250 sub_gen.add_argument("--email", nargs=1,
1251 help=_("The email address of the owner of this key."))
1252 sub_gen.add_argument("action", action="store_const", const="generate")
1253
1254 def parse_command_import(self):
1255 # Parse "import" command.
1256 sub_import = self.sub_commands.add_parser("import",
1257 help=_("Import a key from file."))
1258 sub_import.add_argument("filename", nargs=1,
1259 help=_("Filename of that key to import."))
1260 sub_import.add_argument("action", action="store_const", const="import")
1261
1262 def parse_command_export(self):
1263 # Parse "export" command.
1264 sub_export = self.sub_commands.add_parser("export",
1265 help=_("Export a key to a file."))
1266 sub_export.add_argument("keyid", nargs=1,
1267 help=_("The ID of the key to export."))
1268 sub_export.add_argument("filename", nargs=1,
1269 help=_("Write the key to this file."))
1270 sub_export.add_argument("--secret", action="store_true",
1271 help=_("Export the secret key, too."))
1272 sub_export.add_argument("action", action="store_const", const="export")
1273
1274 def parse_command_delete(self):
1275 # Parse "delete" command.
1276 sub_del = self.sub_commands.add_parser("delete",
1277 help=_("Delete a key from the local keyring."))
1278 sub_del.add_argument("keyid", nargs=1,
1279 help=_("The ID of the key to delete."))
1280 sub_del.add_argument("action", action="store_const", const="delete")
1281
1282 def parse_command_list(self):
1283 # Parse "list" command.
1284 sub_list = self.sub_commands.add_parser("list",
1285 help=_("List all imported keys."))
1286 sub_list.add_argument("action", action="store_const", const="list")
1287
1288 def parse_command_sign(self):
1289 # Implement the "sign" command.
1290 sub_sign = self.sub_commands.add_parser("sign",
1291 help=_("Sign one or more packages."))
1292 sub_sign.add_argument("--key", "-k", nargs=1,
1293 help=_("Key that is used sign the package(s)."))
1294 sub_sign.add_argument("package", nargs="+",
1295 help=_("Package(s) to sign."))
1296 sub_sign.add_argument("action", action="store_const", const="sign")
1297
1298 def parse_command_verify(self):
1299 # Implement the "verify" command.
1300 sub_verify = self.sub_commands.add_parser("verify",
1301 help=_("Verify one or more packages."))
1302 #sub_verify.add_argument("--key", "-k", nargs=1,
1303 # help=_("Key that is used verify the package(s)."))
1304 sub_verify.add_argument("package", nargs="+",
1305 help=_("Package(s) to verify."))
1306 sub_verify.add_argument("action", action="store_const", const="verify")
1307
1308 def handle_generate(self):
1309 realname = self.args.realname[0]
1310 email = self.args.email[0]
1311
1312 print _("Generating the key may take a moment...")
1313 print
1314
1315 # Generate the key.
1316 p = self.pakfire(**self.pakfire_args)
1317 p.key_generate(realname, email)
1318
1319 def handle_import(self):
1320 filename = self.args.filename[0]
1321
1322 # Simply import the file.
1323 p = self.pakfire(**self.pakfire_args)
1324 p.key_import(filename)
1325
1326 def handle_export(self):
1327 keyid = self.args.keyid[0]
1328 filename = self.args.filename[0]
1329 secret = self.args.secret
1330
1331 p = self.pakfire(**self.pakfire_args)
1332 p.key_export(keyid, filename, secret=secret)
1333
1334 def handle_delete(self):
1335 keyid = self.args.keyid[0]
1336
1337 p = self.pakfire(**self.pakfire_args)
1338 p.key_delete(keyid)
1339
1340 def handle_list(self):
1341 p = self.pakfire(**self.pakfire_args)
1342 for line in p.key_list():
1343 print line
1344
1345 def handle_sign(self):
1346 # Get the files from the command line options
1347 files = []
1348
1349 for file in self.args.package:
1350 # Check, if we got a regular file
1351 if os.path.exists(file):
1352 file = os.path.abspath(file)
1353 files.append(file)
1354
1355 else:
1356 raise FileNotFoundError, file
1357
1358 key = self.args.key[0]
1359
1360 # Create pakfire instance.
1361 p = self.pakfire(**self.pakfire_args)
1362
1363 for file in files:
1364 # Open the package.
1365 pkg = packages.open(p, None, file)
1366
1367 print _("Signing %s...") % pkg.friendly_name
1368 pkg.sign(key)
1369
1370 def handle_verify(self):
1371 # Get the files from the command line options
1372 files = []
1373
1374 for file in self.args.package:
1375 # Check, if we got a regular file
1376 if os.path.exists(file) and not os.path.isdir(file):
1377 file = os.path.abspath(file)
1378 files.append(file)
1379
1380 # Create pakfire instance.
1381 p = self.pakfire(**self.pakfire_args)
1382
1383 for file in files:
1384 # Open the package.
1385 pkg = packages.open(p, None, file)
1386
1387 print _("Verifying %s...") % pkg.friendly_name
1388 sigs = pkg.verify()
1389
1390 for sig in sigs:
1391 key = self.pakfire.keyring.get_key(sig.fpr)
1392 if key:
1393 subkey = key.subkeys[0]
1394
1395 print " %s %s" % (subkey.fpr[-16:], key.uids[0].uid)
1396 if sig.validity:
1397 print " %s" % _("This signature is valid.")
1398
1399 else:
1400 print " %s <%s>" % (sig.fpr, _("Unknown key"))
1401 print " %s" % _("Could not check if this signature is valid.")
1402
1403 created = datetime.datetime.fromtimestamp(sig.timestamp)
1404 print " %s" % _("Created: %s") % created
1405
1406 if sig.exp_timestamp:
1407 expires = datetime.datetime.fromtimestamp(sig.exp_timestamp)
1408 print " %s" % _("Expires: %s") % expires
1409
1410 print # Empty line