]>
Commit | Line | Data |
---|---|---|
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 | |
22 | import argparse | |
68c0e769 | 23 | import datetime |
17563cd0 | 24 | import logging |
936f6b37 | 25 | import os |
b913e798 | 26 | import shutil |
17563cd0 | 27 | import signal |
47a4cb89 | 28 | import sys |
b913e798 | 29 | import tempfile |
10b3cc9c | 30 | import time |
47a4cb89 | 31 | |
964aa579 | 32 | from . import base |
2274aab3 | 33 | from . import builder |
964aa579 MT |
34 | from . import client |
35 | from . import config | |
36 | from . import daemon | |
80ed5fae | 37 | from . import ui |
964aa579 MT |
38 | from . import util |
39 | ||
40 | from .system import system | |
41 | from .constants import * | |
42 | from .i18n import _ | |
47a4cb89 | 43 | |
47a4cb89 | 44 | class Cli(object): |
e75d1e3c MT |
45 | default_path = "/" |
46 | ||
47a4cb89 | 47 | def __init__(self): |
80ed5fae | 48 | self.ui = ui.cli.CliUI() |
685cb819 | 49 | |
67b139fb MT |
50 | def parse_cli(self): |
51 | parser = argparse.ArgumentParser( | |
52 | description = _("Pakfire command line interface"), | |
53 | ) | |
54 | subparsers = parser.add_subparsers() | |
55 | ||
56 | # Add common arguments | |
57 | self._add_common_arguments(parser) | |
58 | ||
5f4c668a MT |
59 | parser.add_argument("--arch", "-a", nargs="?", |
60 | help=_("Run pakfire for the given architecture")) | |
67b139fb MT |
61 | parser.add_argument("--root", metavar="PATH", default="/", |
62 | help=_("The path where pakfire should operate in")) | |
63 | ||
64 | # check | |
65 | check = subparsers.add_parser("check", help=_("Check the system for any errors")) | |
66 | check.set_defaults(func=self.handle_check) | |
67 | ||
68 | # check-update | |
69 | check_update = subparsers.add_parser("check-update", | |
70 | help=_("Check, if there are any updates available")) | |
71 | check_update.set_defaults(func=self.handle_check_update) | |
72 | check_update.add_argument("--exclude", "-x", nargs="+", | |
73 | help=_("Exclude package from update")) | |
47857184 MT |
74 | check_update.add_argument("--allow-archchange", action="store_true", |
75 | help=_("Allow changing the architecture of packages")) | |
76 | check_update.add_argument("--allow-downgrade", action="store_true", | |
77 | help=_("Allow downgrading of packages")) | |
67b139fb MT |
78 | check_update.add_argument("--allow-vendorchange", action="store_true", |
79 | help=_("Allow changing the vendor of packages")) | |
67b139fb MT |
80 | |
81 | # clean | |
82 | clean = subparsers.add_parser("clean", help=_("Cleanup all temporary files")) | |
83 | clean.set_defaults(func=self.handle_clean) | |
84 | ||
67b139fb MT |
85 | # downgrade |
86 | downgrade = subparsers.add_parser("downgrade", help=_("Downgrade one or more packages")) | |
87 | downgrade.add_argument("package", nargs="*", | |
88 | help=_("Give a name of a package to downgrade")) | |
89 | downgrade.add_argument("--allow-vendorchange", action="store_true", | |
90 | help=_("Allow changing the vendor of packages")) | |
91 | downgrade.add_argument("--disallow-archchange", action="store_true", | |
92 | help=_("Disallow changing the architecture of packages")) | |
93 | downgrade.set_defaults(func=self.handle_downgrade) | |
94 | ||
8d86a58f MT |
95 | # execute |
96 | execute = subparsers.add_parser("execute", | |
97 | help=_("Executes a command in the pakfire environment (useful for development)")) | |
1577db9b | 98 | execute.add_argument("--bind", action="append", default=[], dest="binds", |
8d86a58f MT |
99 | help=_("Bind-mounts the given directory")) |
100 | execute.add_argument("command", nargs=argparse.REMAINDER) | |
101 | execute.set_defaults(func=self.handle_execute) | |
102 | ||
67b139fb MT |
103 | # extract |
104 | extract = subparsers.add_parser("extract", | |
105 | help=_("Extract a package to a directory")) | |
106 | extract.add_argument("package", nargs="+", | |
107 | help=_("Give name of the file to extract")) | |
108 | extract.add_argument("--target", nargs="?", | |
109 | help=_("Target directory where to extract to")) | |
110 | extract.set_defaults(func=self.handle_extract) | |
111 | ||
112 | # info | |
113 | info = subparsers.add_parser("info", | |
114 | help=_("Print some information about the given package(s)")) | |
31ed921f MT |
115 | info.add_argument("--long", action="store_true", |
116 | help=_("Show more information")) | |
67b139fb MT |
117 | info.add_argument("package", nargs="+", |
118 | help=_("Give at least the name of one package")) | |
119 | info.set_defaults(func=self.handle_info) | |
120 | ||
121 | # install | |
122 | install = subparsers.add_parser("install", | |
123 | help=_("Install one or more packages to the system")) | |
124 | install.add_argument("package", nargs="+", | |
125 | help=_("Give name of at least one package to install")) | |
126 | install.add_argument("--without-recommends", action="store_true", | |
127 | help=_("Don't install recommended packages")) | |
128 | install.set_defaults(func=self.handle_install) | |
129 | ||
130 | # provides | |
131 | provides = subparsers.add_parser("provides", | |
132 | help=_("Get a list of packages that provide a given file or feature")) | |
133 | provides.add_argument("pattern", nargs="+", help=_("File or feature to search for")) | |
134 | provides.set_defaults(func=self.handle_provides) | |
135 | ||
136 | # reinstall | |
137 | reinstall = subparsers.add_parser("reinstall", | |
138 | help=_("Reinstall one or more packages")) | |
139 | reinstall.add_argument("package", nargs="+", | |
140 | help=_("Give name of at least one package to reinstall")) | |
141 | reinstall.set_defaults(func=self.handle_reinstall) | |
142 | ||
143 | # remove | |
144 | remove = subparsers.add_parser("remove", | |
145 | help=_("Remove one or more packages from the system")) | |
146 | remove.add_argument("package", nargs="+", | |
147 | help=_("Give name of at least one package to remove")) | |
148 | remove.set_defaults(func=self.handle_remove) | |
149 | ||
150 | # repolist | |
151 | repolist = subparsers.add_parser("repolist", | |
152 | help=_("List all currently enabled repositories")) | |
153 | repolist.set_defaults(func=self.handle_repolist) | |
154 | ||
67b139fb MT |
155 | # search |
156 | search = subparsers.add_parser("search", help=_("Search for a given pattern")) | |
157 | search.add_argument("pattern", help=_("A pattern to search for")) | |
158 | search.set_defaults(func=self.handle_search) | |
159 | ||
7b6f9f7b MT |
160 | # sync |
161 | sync = subparsers.add_parser("sync", | |
162 | help=_("Sync all installed with the latest one in the distribution")) | |
163 | sync.set_defaults(func=self.handle_sync) | |
164 | ||
67b139fb MT |
165 | # update |
166 | update = subparsers.add_parser("update", | |
167 | help=_("Update the whole system or one specific package")) | |
168 | update.add_argument("package", nargs="*", | |
169 | help=_("Give a name of a package to update or leave emtpy for all")) | |
170 | update.add_argument("--exclude", "-x", nargs="+", | |
47857184 MT |
171 | help=_("Exclude package from update")) |
172 | update.add_argument("--allow-archchange", action="store_true", | |
173 | help=_("Allow changing the architecture of packages")) | |
174 | update.add_argument("--allow-downgrade", action="store_true", | |
175 | help=_("Allow downgrading of packages")) | |
67b139fb | 176 | update.add_argument("--allow-vendorchange", action="store_true", |
47857184 | 177 | help=_("Allow changing the vendor of packages")) |
67b139fb | 178 | update.set_defaults(func=self.handle_update) |
685cb819 | 179 | |
67b139fb | 180 | return parser.parse_args() |
685cb819 | 181 | |
95a7e4d9 MT |
182 | def _add_common_arguments(self, parser, offline_switch=True): |
183 | parser.add_argument("--version", action="version", | |
50381f5c MT |
184 | version="%(prog)s " + PAKFIRE_VERSION) |
185 | ||
95a7e4d9 | 186 | parser.add_argument("-v", "--verbose", action="store_true", |
47a4cb89 MT |
187 | help=_("Enable verbose output.")) |
188 | ||
95a7e4d9 | 189 | parser.add_argument("-c", "--config", nargs="?", |
47a4cb89 MT |
190 | help=_("Path to a configuration file to load.")) |
191 | ||
95a7e4d9 | 192 | parser.add_argument("--disable-repo", nargs="*", metavar="REPO", |
685cb819 | 193 | help=_("Disable a repository temporarily."), default=[]) |
f781b1ab | 194 | |
95a7e4d9 | 195 | parser.add_argument("--enable-repo", nargs="*", metavar="REPO", |
685cb819 | 196 | help=_("Enable a repository temporarily."), default=[]) |
f9a012a8 | 197 | |
c62d93f1 | 198 | if offline_switch: |
95a7e4d9 | 199 | parser.add_argument("--offline", action="store_true", |
c62d93f1 | 200 | help=_("Run pakfire in offline mode.")) |
6a509182 | 201 | |
67b139fb | 202 | def pakfire(self, ns): |
e75d1e3c | 203 | p = base.Pakfire( |
5f4c668a | 204 | arch=ns.arch, |
8a36f1f2 | 205 | conf=ns.config, |
e75d1e3c MT |
206 | path=ns.root if "root" in ns else self.default_path, |
207 | offline=ns.offline, | |
208 | ) | |
05fb1da0 | 209 | |
67b139fb | 210 | # Disable repositories. |
8ef6c388 MT |
211 | for repo_name in ns.disable_repo: |
212 | repo = p.get_repo(repo_name) | |
213 | repo.enabled = False | |
67b139fb MT |
214 | |
215 | # Enable repositories. | |
8ef6c388 MT |
216 | for repo_name in ns.enable_repo: |
217 | repo = p.get_repo(repo_name) | |
218 | repo.enabled = True | |
67b139fb MT |
219 | |
220 | return p | |
995ed2dd | 221 | |
8d86a58f MT |
222 | def handle_execute(self, ns): |
223 | pakfire = self.pakfire(ns) | |
224 | ||
225 | # Bind-mount everything | |
226 | for bind in ns.binds: | |
227 | pakfire.bind(bind) | |
228 | ||
59b9d1c2 MT |
229 | # Log everything to the console |
230 | def logging_callback(level, line): | |
231 | if level >= logging.ERROR: | |
232 | sys.stderr.write("%s\n" % line) | |
233 | else: | |
234 | sys.stdout.write("%s\n" % line) | |
235 | ||
236 | return pakfire.execute(ns.command, logging_callback=logging_callback) | |
8d86a58f | 237 | |
47a4cb89 | 238 | def run(self): |
95a7e4d9 | 239 | args = self.parse_cli() |
74b61c5c | 240 | assert args.func, "Argument function not defined" |
47a4cb89 | 241 | |
17563cd0 MT |
242 | try: |
243 | return args.func(args) | |
244 | ||
245 | except KeyboardInterrupt: | |
246 | self.ui.message(_("Received keyboard interupt (Ctrl-C). Exiting."), | |
247 | level=logging.CRITICAL) | |
248 | ||
249 | return 128 + signal.SIGINT | |
250 | ||
27160734 MT |
251 | except DependencyError as e: |
252 | self.ui.message(_("One or more dependencies could not been resolved")) | |
253 | self.ui.message("") # empty line | |
254 | ||
255 | # This exception provides a list of all problems | |
256 | problems, = e.args | |
257 | ||
258 | # List all problems | |
259 | for problem in problems: | |
260 | self.ui.message(" * %s" % problem) | |
261 | ||
262 | self.ui.message(" %s" % _("Possible solutions are:")) | |
263 | for solution in problem.solutions: | |
264 | self.ui.message(" * %s" % solution) | |
265 | ||
266 | # Add another empty line | |
267 | self.ui.message("") | |
268 | ||
269 | return 4 | |
270 | ||
17563cd0 MT |
271 | # Catch all errors and show a user-friendly error message. |
272 | except Error as e: | |
273 | self.ui.message(_("An error has occured when running Pakfire"), level=logging.CRITICAL) | |
274 | ||
275 | self.ui.message(_("%s: %s") % (e.__class__.__name__, e.message), | |
276 | level=logging.ERROR) | |
277 | ||
278 | return e.exit_code | |
47a4cb89 | 279 | |
47857184 MT |
280 | def _dump_transaction(self, transaction): |
281 | """ | |
282 | Dumps the transaction | |
283 | """ | |
b546c786 | 284 | t = transaction.dump() |
47857184 | 285 | |
b546c786 MT |
286 | for line in t.splitlines(): |
287 | self.ui.message(line) | |
288 | ||
47857184 | 289 | def _execute_transaction(self, transaction): |
dea70c9f | 290 | transaction.run() |
b546c786 | 291 | |
145f61fe | 292 | def handle_info(self, ns): |
279509bc MT |
293 | with self.pakfire(ns) as p: |
294 | for pkg in p.info(ns.package): | |
31ed921f | 295 | s = pkg.dump(long=ns.long) |
312fd26f | 296 | self.ui.message(s) |
47a4cb89 | 297 | |
279509bc MT |
298 | def handle_search(self, ns): |
299 | with self.pakfire(ns) as p: | |
300 | for pkg in p.search(ns.pattern): | |
2b2d5b1b MT |
301 | # Skip any -debuginfo packages |
302 | if pkg.name.endswith("-debuginfo"): | |
303 | continue | |
304 | ||
465dc959 | 305 | self.ui.message("%-24s: %s" % (pkg.name, pkg.summary)) |
47a4cb89 | 306 | |
47857184 MT |
307 | def handle_update(self, ns, check=False): |
308 | with self.pakfire(ns) as p: | |
309 | transaction = p.update( | |
310 | ns.package, excludes=ns.exclude, | |
311 | allow_archchange=ns.allow_archchange, | |
312 | allow_vendorchange=ns.allow_vendorchange, | |
313 | ) | |
d314d72b | 314 | |
47857184 MT |
315 | # If we are only checking for updates, |
316 | # we dump the transaction and exit here. | |
317 | if check: | |
318 | self._dump_transaction(transaction) | |
319 | return | |
d314d72b | 320 | |
47857184 MT |
321 | # Otherwise we execute the transaction |
322 | self._execute_transaction(transaction) | |
d314d72b | 323 | |
7b6f9f7b | 324 | def handle_sync(self, ns): |
47857184 MT |
325 | with self.pakfire(ns) as p: |
326 | transaction = p.update( | |
327 | allow_archchange=True, | |
328 | allow_vendorchange=True, | |
329 | ) | |
330 | ||
331 | self._execute_transaction(transaction) | |
279509bc MT |
332 | |
333 | def handle_check_update(self, ns): | |
334 | self.handle_update(ns, check=True) | |
335 | ||
336 | def handle_downgrade(self, ns, **args): | |
337 | with self.pakfire(ns) as p: | |
338 | p.downgrade( | |
339 | self.args.package, | |
340 | allow_vendorchange=self.args.allow_vendorchange, | |
341 | allow_archchange=not self.args.disallow_archchange, | |
342 | **args | |
343 | ) | |
344 | ||
345 | def handle_install(self, ns): | |
346 | with self.pakfire(ns) as p: | |
b546c786 MT |
347 | transaction = p.install(ns.package, without_recommends=ns.without_recommends) |
348 | ||
349 | # Execute the transaction | |
350 | self._execute_transaction(transaction) | |
279509bc MT |
351 | |
352 | def handle_reinstall(self, ns): | |
353 | with self.pakfire(ns) as p: | |
b546c786 MT |
354 | transaction = p.reinstall(ns.package) |
355 | ||
356 | # Execute the transaction | |
357 | self._execute_transaction(transaction) | |
279509bc MT |
358 | |
359 | def handle_remove(self, ns): | |
360 | with self.pakfire(ns) as p: | |
2f1289eb | 361 | transaction = p.erase(ns.package) |
b546c786 MT |
362 | |
363 | self._execute_transaction(transaction) | |
279509bc MT |
364 | |
365 | def handle_provides(self, ns, long=False): | |
366 | with self.pakfire(ns) as p: | |
367 | for pkg in p.provides(ns.pattern): | |
368 | s = pkg.dump(long=long) | |
369 | print(s) | |
370 | ||
279509bc MT |
371 | def handle_repolist(self, ns): |
372 | with self.pakfire(ns) as p: | |
279509bc MT |
373 | FORMAT = " %-20s %8s %12s %12s " |
374 | title = FORMAT % (_("Repository"), _("Enabled"), _("Priority"), _("Packages")) | |
375 | print(title) | |
376 | print("=" * len(title)) # spacing line | |
377 | ||
6e7e70a2 | 378 | for repo in p.repos: |
279509bc MT |
379 | print(FORMAT % (repo.name, repo.enabled, repo.priority, len(repo))) |
380 | ||
381 | def handle_clean(self, ns): | |
cb6d631c | 382 | self.ui.message(_("Cleaning up everything...")) |
31267a64 | 383 | |
cb6d631c MT |
384 | p = self.pakfire(ns) |
385 | p.clean() | |
c901e1f8 | 386 | |
279509bc MT |
387 | def handle_check(self, ns): |
388 | with self.pakfire(ns) as p: | |
f466d0db MT |
389 | # This will throw an exception when there are errors |
390 | transaction = p.check() | |
391 | ||
392 | self.ui.message(_("Everything okay")) | |
36b328f2 | 393 | |
279509bc MT |
394 | def handle_extract(self, ns): |
395 | with self.pakfire(ns) as p: | |
e7da5970 | 396 | p.extract(ns.package, target=ns.target) |
995ed2dd | 397 | |
47a4cb89 MT |
398 | |
399 | class CliBuilder(Cli): | |
e75d1e3c MT |
400 | default_path = None |
401 | ||
145f61fe MT |
402 | def parse_cli(self): |
403 | parser = argparse.ArgumentParser( | |
404 | description = _("Pakfire builder command line interface"), | |
47a4cb89 | 405 | ) |
145f61fe | 406 | subparsers = parser.add_subparsers() |
47a4cb89 | 407 | |
145f61fe MT |
408 | # Add common arguments |
409 | self._add_common_arguments(parser) | |
47a4cb89 | 410 | |
145f61fe MT |
411 | # Add additional arguments |
412 | parser.add_argument("--arch", "-a", nargs="?", | |
413 | help=_("Run pakfire for the given architecture")) | |
e947f34f | 414 | parser.add_argument("--distro", nargs="?", default="ipfire3", # XXX for now |
145f61fe | 415 | help=_("Choose the distribution configuration to use for build")) |
1c2ff7a3 MT |
416 | parser.add_argument("--disable-snapshot", action="store_true", |
417 | help=_("Disable using snapshots")) | |
47a4cb89 | 418 | |
145f61fe MT |
419 | # build |
420 | build = subparsers.add_parser("build", help=_("Build one or more packages")) | |
421 | build.add_argument("package", nargs=1, | |
422 | help=_("Give name of at least one package to build")) | |
423 | build.set_defaults(func=self.handle_build) | |
424 | ||
425 | build.add_argument("--resultdir", nargs="?", | |
426 | help=_("Path were the output files should be copied to")) | |
427 | build.add_argument("-m", "--mode", nargs="?", default="development", | |
428 | help=_("Mode to run in. Is either 'release' or 'development' (default)")) | |
429 | build.add_argument("--after-shell", action="store_true", | |
430 | help=_("Run a shell after a successful build")) | |
431 | build.add_argument("--skip-install-test", action="store_true", | |
432 | help=_("Do not perform the install test")) | |
433 | build.add_argument("--private-network", action="store_true", | |
434 | help=_("Disable network in container")) | |
435 | ||
436 | # clean | |
437 | clean = subparsers.add_parser("clean", help=_("Cleanup all temporary files")) | |
0c547b16 | 438 | clean.set_defaults(func=self.handle_clean) |
145f61fe MT |
439 | |
440 | # dist | |
441 | dist = subparsers.add_parser("dist", help=_("Generate a source package")) | |
442 | dist.add_argument("package", nargs="+", help=_("Give name(s) of a package(s)")) | |
443 | dist.set_defaults(func=self.handle_dist) | |
444 | ||
445 | dist.add_argument("--resultdir", nargs="?", | |
446 | help=_("Path were the output files should be copied to")) | |
447 | ||
448 | # extract | |
449 | extract = subparsers.add_parser("extract", help=_("Extract a package to a directory")) | |
450 | extract.add_argument("package", nargs="+", | |
451 | help=_("Give name of the file to extract")) | |
452 | extract.add_argument("--target", nargs="?", | |
453 | help=_("Target directory where to extract to")) | |
454 | extract.set_defaults(func=self.handle_extract) | |
455 | ||
456 | # info | |
457 | info = subparsers.add_parser("info", | |
458 | help=_("Print some information about the given package(s)")) | |
31ed921f MT |
459 | info.add_argument("--filelist", action="store_true", |
460 | help=_("Show filelist")) | |
145f61fe MT |
461 | info.add_argument("package", nargs="+", |
462 | help=_("Give at least the name of one package.")) | |
31ed921f | 463 | info.set_defaults(func=self.handle_info) |
145f61fe | 464 | |
145f61fe MT |
465 | # provides |
466 | provides = subparsers.add_parser("provides", | |
467 | help=_("Get a list of packages that provide a given file or feature")) | |
468 | provides.add_argument("pattern", nargs="+", | |
469 | help=_("File or feature to search for")) | |
470 | provides.set_defaults(func=self.handle_provides) | |
471 | ||
472 | # repolist | |
473 | repolist = subparsers.add_parser("repolist", | |
474 | help=_("List all currently enabled repositories")) | |
475 | repolist.set_defaults(func=self.handle_repolist) | |
476 | ||
145f61fe MT |
477 | # search |
478 | search = subparsers.add_parser("search", help=_("Search for a given pattern")) | |
479 | search.add_argument("pattern", help=_("A pattern to search for")) | |
480 | search.set_defaults(func=self.handle_search) | |
481 | ||
482 | # shell | |
483 | shell = subparsers.add_parser("shell", help=_("Go into a build shell")) | |
b3cdc86c | 484 | shell.add_argument("package", nargs="*", help=_("Give name of a package")) |
f0ae5ac2 MT |
485 | shell.add_argument("--install", nargs="*", |
486 | help=_("Packages that should be installed in the shell")) | |
145f61fe MT |
487 | shell.set_defaults(func=self.handle_shell) |
488 | ||
489 | shell.add_argument("-m", "--mode", nargs="?", default="development", | |
490 | help=_("Mode to run in. Is either 'release' or 'development' (default).")) | |
491 | shell.add_argument("--private-network", action="store_true", | |
492 | help=_("Disable network in container")) | |
47a4cb89 | 493 | |
145f61fe MT |
494 | # update |
495 | update = subparsers.add_parser("update", help=_("Update the package indexes")) | |
496 | update.set_defaults(func=self.handle_update) | |
497 | ||
498 | return parser.parse_args() | |
47a4cb89 | 499 | |
2274aab3 | 500 | def builder(self, ns): |
e947f34f MT |
501 | # Find distro configuration file |
502 | conf = os.path.join(CONFIG_DISTRO_DIR, "%s.conf" % ns.distro) | |
503 | ||
1c2ff7a3 MT |
504 | return builder.Builder( |
505 | conf=conf, | |
506 | arch=ns.arch, | |
507 | enable_snapshot=not ns.disable_snapshot | |
508 | ) | |
47a4cb89 | 509 | |
2274aab3 MT |
510 | def handle_build(self, ns): |
511 | package, = ns.package | |
4fffe3c4 | 512 | |
2274aab3 MT |
513 | # Initialise a builder instance and build this package |
514 | with self.builder(ns) as b: | |
515 | b.build(package) | |
392371f7 | 516 | |
5cea773c MT |
517 | def handle_info(self, ns): |
518 | with self.builder(ns) as b: | |
519 | with b.pakfire as p: | |
520 | for pkg in p.info(ns.package): | |
31ed921f | 521 | s = pkg.dump(long=True, filelist=ns.filelist) |
5cea773c MT |
522 | self.ui.message(s) |
523 | ||
a7c71410 MT |
524 | def handle_repolist(self, ns): |
525 | with self.builder(ns) as b: | |
526 | with b.pakfire as p: | |
527 | FORMAT = " %-20s %8s %12s %12s " | |
528 | title = FORMAT % (_("Repository"), _("Enabled"), _("Priority"), _("Packages")) | |
529 | print(title) | |
530 | print("=" * len(title)) # spacing line | |
531 | ||
532 | for repo in p.repos: | |
533 | print(FORMAT % (repo.name, repo.enabled, repo.priority, len(repo))) | |
534 | ||
2274aab3 MT |
535 | def handle_shell(self, ns): |
536 | with self.builder(ns) as b: | |
b3cdc86c | 537 | b.shell(packages=ns.package, install=ns.install) |
47a4cb89 | 538 | |
0fcb9c3c | 539 | def handle_dist(self, ns): |
e412b8dc MT |
540 | # Get the packages from the command line options |
541 | pkgs = [] | |
47a4cb89 | 542 | |
0fcb9c3c | 543 | for pkg in ns.package: |
e412b8dc MT |
544 | # Check, if we got a regular file |
545 | if os.path.exists(pkg): | |
546 | pkg = os.path.abspath(pkg) | |
7c8f2953 | 547 | pkgs.append(pkg) |
47a4cb89 | 548 | |
e412b8dc | 549 | else: |
964aa579 | 550 | raise FileNotFoundError(pkg) |
7c8f2953 | 551 | |
7d40ac70 MT |
552 | # Put packages to where the user said or our |
553 | # current working directory. | |
0fcb9c3c | 554 | resultdir = ns.resultdir or os.getcwd() |
7d40ac70 | 555 | |
0fcb9c3c | 556 | p = self.pakfire(ns) |
7d40ac70 | 557 | for pkg in pkgs: |
af41db7e | 558 | p.dist(pkg, resultdir) |
47a4cb89 | 559 | |
47a4cb89 | 560 | |
c62d93f1 MT |
561 | class CliClient(Cli): |
562 | def __init__(self): | |
95a7e4d9 MT |
563 | # Create connection to pakfire hub |
564 | self.client = client.Client() | |
565 | ||
566 | def parse_cli(self): | |
567 | parser = argparse.ArgumentParser( | |
755dd8db | 568 | description = _("Pakfire client command line interface"), |
c62d93f1 | 569 | ) |
95a7e4d9 | 570 | subparsers = parser.add_subparsers(help=_("sub-command help")) |
c62d93f1 | 571 | |
95a7e4d9 MT |
572 | # Add common arguments |
573 | self._add_common_arguments(parser, offline_switch=False) | |
c62d93f1 | 574 | |
95a7e4d9 MT |
575 | # build |
576 | build = subparsers.add_parser("build", help=_("Build a package remote")) | |
577 | build.add_argument("packages", nargs="+", help=_("Package(s) to build")) | |
578 | build.set_defaults(func=self.handle_build) | |
c62d93f1 | 579 | |
95a7e4d9 | 580 | build.add_argument("-a", "--arch", |
755dd8db | 581 | help=_("Build the package(s) for the given architecture only")) |
c62d93f1 | 582 | |
95a7e4d9 MT |
583 | # check-connection |
584 | check_connection = subparsers.add_parser("check-connection", | |
6e3e13fb | 585 | help=_("Check the connection to the hub")) |
95a7e4d9 MT |
586 | check_connection.set_defaults(func=self.handle_check_connection) |
587 | ||
ec5917f3 MT |
588 | # upload |
589 | upload = subparsers.add_parser("upload", help=_("Upload a file to the build service")) | |
590 | upload.add_argument("file", nargs=1, help=_("Filename")) | |
591 | upload.set_defaults(func=self.handle_upload) | |
592 | ||
10b3cc9c MT |
593 | # watch-build |
594 | watch_build = subparsers.add_parser("watch-build", help=_("Watch the status of a build")) | |
595 | watch_build.add_argument("id", nargs=1, help=_("Build ID")) | |
596 | watch_build.set_defaults(func=self.handle_watch_build) | |
597 | ||
badb4cf2 MT |
598 | # watch-job |
599 | watch_job = subparsers.add_parser("watch-job", help=_("Watch the status of a job")) | |
600 | watch_job.add_argument("id", nargs=1, help=_("Job ID")) | |
601 | watch_job.set_defaults(func=self.handle_watch_job) | |
602 | ||
95a7e4d9 | 603 | return parser.parse_args() |
c62d93f1 | 604 | |
755dd8db | 605 | def handle_build(self, ns): |
b913e798 MT |
606 | # Create a temporary directory. |
607 | temp_dir = tempfile.mkdtemp() | |
608 | ||
755dd8db MT |
609 | # Format arches. |
610 | if ns.arch: | |
611 | arches = self.args.arch.split(",") | |
612 | else: | |
613 | arches = None | |
614 | ||
b913e798 | 615 | try: |
755dd8db MT |
616 | # Package all packages first and save the actual filenames |
617 | packages = [] | |
b913e798 | 618 | |
755dd8db MT |
619 | for package in ns.packages: |
620 | if package.endswith(".%s" % MAKEFILE_EXTENSION): | |
621 | # Create a source package from the makefile. | |
622 | p = self.pakfire() | |
623 | package = p.dist(package, temp_dir) | |
624 | packages.append(package) | |
b913e798 | 625 | |
755dd8db MT |
626 | elif package.endswith(".%s" % PACKAGE_EXTENSION): |
627 | packages.append(package) | |
c62d93f1 | 628 | |
755dd8db MT |
629 | else: |
630 | raise Exception("Unknown filetype: %s" % package) | |
b913e798 | 631 | |
755dd8db MT |
632 | assert packages |
633 | ||
634 | # Upload the packages to the build service | |
635 | for package in packages: | |
636 | build = self.client.create_build(package, type="scratch", arches=arches) | |
c62d93f1 | 637 | |
755dd8db MT |
638 | # Show information about the uploaded build |
639 | summary = build.dump() | |
640 | for line in summary.splitlines(): | |
641 | print(" %s" % line) | |
b913e798 MT |
642 | |
643 | finally: | |
644 | # Cleanup the temporary directory and all files. | |
645 | if os.path.exists(temp_dir): | |
646 | shutil.rmtree(temp_dir, ignore_errors=True) | |
c62d93f1 | 647 | |
6e3e13fb MT |
648 | def handle_check_connection(self, ns): |
649 | success = self.client.check_connection() | |
c62d93f1 | 650 | |
6e3e13fb MT |
651 | if success: |
652 | print("%s: %s" % (_("Connection OK"), success)) | |
c62d93f1 | 653 | |
ec5917f3 MT |
654 | def handle_upload(self, ns): |
655 | for path in ns.file: | |
656 | self.client.upload_file(path) | |
657 | ||
10b3cc9c MT |
658 | def handle_watch_build(self, ns): |
659 | build = self.client.get_build(ns.id[0]) | |
660 | ||
badb4cf2 MT |
661 | return self._watch_something(build) |
662 | ||
663 | def handle_watch_job(self, ns): | |
664 | job = self.client.get_job(ns.id[0]) | |
665 | ||
666 | return self._watch_something(job) | |
667 | ||
668 | def _watch_something(self, o): | |
10b3cc9c | 669 | while True: |
badb4cf2 | 670 | s = o.dump() |
10b3cc9c MT |
671 | print(s) |
672 | ||
badb4cf2 | 673 | # Break the loop if the build/job is not active any more |
10b3cc9c | 674 | # (since we don't expect any changes) |
badb4cf2 | 675 | if not o.is_active(): |
10b3cc9c MT |
676 | break |
677 | ||
678 | time.sleep(60) | |
679 | ||
8f1c63a8 MT |
680 | # Update data before the next loop is shown |
681 | o.refresh() | |
682 | ||
c62d93f1 MT |
683 | |
684 | class CliDaemon(Cli): | |
4ceab0c3 MT |
685 | def parse_cli(self): |
686 | parser = argparse.ArgumentParser( | |
687 | description = _("Pakfire daemon command line interface"), | |
c62d93f1 | 688 | ) |
4ceab0c3 | 689 | self._add_common_arguments(parser, offline_switch=False) |
c62d93f1 | 690 | |
4ceab0c3 MT |
691 | # There is only one default action |
692 | parser.set_defaults(func=self.handle_run) | |
c62d93f1 | 693 | |
4ceab0c3 MT |
694 | return parser.parse_args() |
695 | ||
696 | def handle_run(self, ns): | |
c62d93f1 | 697 | """ |
4ceab0c3 | 698 | Runs the pakfire daemon |
c62d93f1 | 699 | """ |
4ceab0c3 MT |
700 | d = daemon.PakfireDaemon() |
701 | ||
c62d93f1 MT |
702 | try: |
703 | d.run() | |
704 | ||
4ceab0c3 | 705 | # We cannot just kill the daemon, it needs a smooth shutdown |
c62d93f1 MT |
706 | except (SystemExit, KeyboardInterrupt): |
707 | d.shutdown() | |
68c0e769 MT |
708 | |
709 | ||
710 | class CliKey(Cli): | |
0a89bb8a MT |
711 | def parse_cli(self): |
712 | parser = argparse.ArgumentParser( | |
713 | description = _("Pakfire key command line interface"), | |
68c0e769 | 714 | ) |
0a89bb8a | 715 | subparsers = parser.add_subparsers() |
68c0e769 | 716 | |
0a89bb8a MT |
717 | # Add common arguments |
718 | self._add_common_arguments(parser) | |
68c0e769 | 719 | |
b9b6a13e MT |
720 | # delete |
721 | delete = subparsers.add_parser("delete", help=_("Delete a key from the local keyring")) | |
722 | delete.add_argument("fingerprint", nargs="+", help=_("The fingerprint of the key to delete")) | |
723 | delete.set_defaults(func=self.handle_delete) | |
724 | ||
7b526dd2 MT |
725 | # export |
726 | export = subparsers.add_parser("export", help=_("Export a key to a file")) | |
727 | export.add_argument("fingerprint", nargs=1, | |
728 | help=_("The fingerprint of the key to export")) | |
729 | export.add_argument("--filename", nargs="*", help=_("Write the key to this file")) | |
730 | export.add_argument("--secret", action="store_true", | |
731 | help=_("Export the secret key")) | |
732 | export.set_defaults(func=self.handle_export) | |
733 | ||
d36b1022 | 734 | # import |
7b526dd2 MT |
735 | _import = subparsers.add_parser("import", help=_("Import a key from file")) |
736 | _import.add_argument("filename", nargs="+", help=_("Filename of that key to import")) | |
737 | _import.set_defaults(func=self.handle_import) | |
738 | ||
0a89bb8a MT |
739 | # generate |
740 | generate = subparsers.add_parser("generate", help=_("Import a key from file")) | |
741 | generate.add_argument("--realname", nargs=1, | |
742 | help=_("The real name of the owner of this key")) | |
743 | generate.add_argument("--email", nargs=1, | |
744 | help=_("The email address of the owner of this key")) | |
745 | generate.set_defaults(func=self.handle_generate) | |
746 | ||
747 | # list | |
748 | list = subparsers.add_parser("list", help=_("List all imported keys")) | |
749 | list.set_defaults(func=self.handle_list) | |
750 | ||
0a89bb8a | 751 | return parser.parse_args() |
68c0e769 | 752 | |
68c0e769 MT |
753 | def parse_command_sign(self): |
754 | # Implement the "sign" command. | |
755 | sub_sign = self.sub_commands.add_parser("sign", | |
756 | help=_("Sign one or more packages.")) | |
757 | sub_sign.add_argument("--key", "-k", nargs=1, | |
758 | help=_("Key that is used sign the package(s).")) | |
759 | sub_sign.add_argument("package", nargs="+", | |
760 | help=_("Package(s) to sign.")) | |
761 | sub_sign.add_argument("action", action="store_const", const="sign") | |
762 | ||
763 | def parse_command_verify(self): | |
764 | # Implement the "verify" command. | |
765 | sub_verify = self.sub_commands.add_parser("verify", | |
766 | help=_("Verify one or more packages.")) | |
767 | #sub_verify.add_argument("--key", "-k", nargs=1, | |
768 | # help=_("Key that is used verify the package(s).")) | |
769 | sub_verify.add_argument("package", nargs="+", | |
770 | help=_("Package(s) to verify.")) | |
771 | sub_verify.add_argument("action", action="store_const", const="verify") | |
772 | ||
0a89bb8a MT |
773 | def handle_generate(self, ns): |
774 | p = self.pakfire(ns) | |
68c0e769 | 775 | |
964aa579 MT |
776 | print(_("Generating the key may take a moment...")) |
777 | print() | |
68c0e769 | 778 | |
ad62bb07 | 779 | key = p.generate_key("%s <%s>" % (ns.realname.pop(), ns.email.pop())) |
0a89bb8a MT |
780 | |
781 | def handle_list(self, ns): | |
782 | p = self.pakfire(ns) | |
783 | ||
ad62bb07 | 784 | for key in p.keys: |
0a89bb8a MT |
785 | print(key) |
786 | ||
787 | def handle_export(self, ns): | |
788 | p = self.pakfire(ns) | |
789 | ||
790 | for fingerprint in ns.fingerprint: | |
ad62bb07 | 791 | key = p.get_key(fingerprint) |
0a89bb8a MT |
792 | if not key: |
793 | print(_("Could not find key with fingerprint %s") % fingerprint) | |
794 | continue | |
795 | ||
796 | data = key.export(ns.secret) | |
797 | ||
798 | for file in ns.filename: | |
799 | with open(file, "w") as f: | |
800 | f.write(data) | |
801 | else: | |
802 | print(data) | |
68c0e769 | 803 | |
7b526dd2 MT |
804 | def handle_import(self, ns): |
805 | p = self.pakfire(ns) | |
68c0e769 | 806 | |
7b526dd2 MT |
807 | for filename in ns.filename: |
808 | with open(filename) as f: | |
809 | data = f.read() | |
810 | ||
91f40bc5 MT |
811 | keys = p.import_key(data) |
812 | for key in keys: | |
813 | print(key) | |
68c0e769 | 814 | |
b9b6a13e MT |
815 | def handle_delete(self, ns): |
816 | p = self.pakfire(ns) | |
eaf999ef | 817 | |
b9b6a13e MT |
818 | for fingerprint in ns.fingerprint: |
819 | key = p.get_key(fingerprint) | |
820 | if key: | |
821 | key.delete() | |
eaf999ef | 822 | |
68c0e769 MT |
823 | def handle_sign(self): |
824 | # Get the files from the command line options | |
825 | files = [] | |
826 | ||
827 | for file in self.args.package: | |
828 | # Check, if we got a regular file | |
829 | if os.path.exists(file): | |
830 | file = os.path.abspath(file) | |
831 | files.append(file) | |
832 | ||
833 | else: | |
964aa579 | 834 | raise FileNotFoundError(file) |
68c0e769 MT |
835 | |
836 | key = self.args.key[0] | |
837 | ||
36b328f2 | 838 | # Create pakfire instance. |
3a1ddabb | 839 | p = self.create_pakfire() |
36b328f2 | 840 | |
68c0e769 MT |
841 | for file in files: |
842 | # Open the package. | |
36b328f2 | 843 | pkg = packages.open(p, None, file) |
68c0e769 | 844 | |
964aa579 | 845 | print(_("Signing %s...") % pkg.friendly_name) |
68c0e769 MT |
846 | pkg.sign(key) |
847 | ||
848 | def handle_verify(self): | |
b4f722d1 MT |
849 | # TODO needs to use new key code from libpakfire |
850 | ||
68c0e769 MT |
851 | # Get the files from the command line options |
852 | files = [] | |
853 | ||
854 | for file in self.args.package: | |
855 | # Check, if we got a regular file | |
856 | if os.path.exists(file) and not os.path.isdir(file): | |
857 | file = os.path.abspath(file) | |
858 | files.append(file) | |
859 | ||
36b328f2 | 860 | # Create pakfire instance. |
3a1ddabb | 861 | p = self.create_pakfire() |
36b328f2 | 862 | |
68c0e769 MT |
863 | for file in files: |
864 | # Open the package. | |
36b328f2 | 865 | pkg = packages.open(p, None, file) |
68c0e769 | 866 | |
964aa579 | 867 | print(_("Verifying %s...") % pkg.friendly_name) |
68c0e769 MT |
868 | sigs = pkg.verify() |
869 | ||
870 | for sig in sigs: | |
3a1ddabb | 871 | key = p.keyring.get_key(sig.fpr) |
68c0e769 MT |
872 | if key: |
873 | subkey = key.subkeys[0] | |
874 | ||
964aa579 | 875 | print(" %s %s" % (subkey.fpr[-16:], key.uids[0].uid)) |
68c0e769 | 876 | if sig.validity: |
964aa579 | 877 | print(" %s" % _("This signature is valid.")) |
68c0e769 MT |
878 | |
879 | else: | |
964aa579 MT |
880 | print(" %s <%s>" % (sig.fpr, _("Unknown key"))) |
881 | print(" %s" % _("Could not check if this signature is valid.")) | |
68c0e769 MT |
882 | |
883 | created = datetime.datetime.fromtimestamp(sig.timestamp) | |
964aa579 | 884 | print(" %s" % _("Created: %s") % created) |
68c0e769 MT |
885 | |
886 | if sig.exp_timestamp: | |
887 | expires = datetime.datetime.fromtimestamp(sig.exp_timestamp) | |
964aa579 | 888 | print(" %s" % _("Expires: %s") % expires) |
68c0e769 | 889 | |
964aa579 | 890 | print() # Empty line |