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