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