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