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