1#! /usr/bin/env python3
3"""Configure dak parameters in the database"""
4# Copyright (C) 2009 Mark Hymers <mhy@debian.org>
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20################################################################################
22import collections
23import json
24import sys
25from collections.abc import Callable, Sequence
26from dataclasses import dataclass
27from typing import Any, NoReturn, Optional
29import apt_pkg
31import daklib.archive
32import daklib.gpg
34from daklib import utils
35from daklib.dbconn import *
36from sqlalchemy.orm.exc import NoResultFound
38################################################################################
40dispatch = {}
41dryrun = False
43################################################################################
46def warn(msg: str) -> None:
47 print(msg, file=sys.stderr)
50def die(msg: str, exit_code: int = 1) -> NoReturn:
51 print(msg, file=sys.stderr)
52 sys.exit(exit_code)
55def die_arglen(args: Sequence, args_needed: int, msg: str) -> None:
56 if len(args) < args_needed: 56 ↛ 57line 56 didn't jump to line 57, because the condition on line 56 was never true
57 die(msg)
60def get_suite_or_die(
61 suite_name: str,
62 session=None,
63 error_message="E: Invalid/unknown suite %(suite_name)s",
64) -> Suite:
65 suite = get_suite(suite_name.lower(), session=session)
66 if suite is None: 66 ↛ 67line 66 didn't jump to line 67, because the condition on line 66 was never true
67 die(error_message % {'suite_name': suite_name})
68 return suite
71def usage(exit_code: int = 0) -> NoReturn:
72 """Perform administrative work on the dak database."""
74 print("""Usage: dak admin COMMAND
75Perform administrative work on the dak database.
77 -h, --help show this help and exit.
78 -n, --dry-run don't do anything, just show what would have been done
79 (only applies to add or rm operations).
81 Commands can use a long or abbreviated form:
83 config / c:
84 c db show db config
85 c db-shell show db config in a usable form for psql
86 c NAME show option NAME as set in configuration table
88 keyring / k:
89 k list-all list all keyrings
90 k list-binary list all keyrings with a NULL source acl
91 k list-source list all keyrings with a non NULL source acl
92 k add-buildd NAME ARCH... add buildd keyring with upload permission
93 for the given architectures
95 architecture / a:
96 a list show a list of architectures
97 a rm ARCH remove an architecture (will only work if
98 no longer linked to any suites)
99 a add ARCH DESCRIPTION [SUITELIST]
100 add architecture ARCH with DESCRIPTION.
101 If SUITELIST is given, add to each of the
102 suites at the same time
104 component:
105 component list show a list of components
106 component rm COMPONENT remove a component (will only work if
107 empty)
108 component add NAME DESCRIPTION ORDERING
109 add component NAME with DESCRIPTION.
110 Ordered at ORDERING.
112 suite / s:
113 s list [--print-archive]
114 show a list of suites
115 s show SUITE show config details for a suite
116 s add SUITE VERSION [ label=LABEL ] [ description=DESCRIPTION ]
117 [ origin=ORIGIN ] [ codename=CODENAME ]
118 [ signingkey=SIGNINGKEY ] [ archive=ARCHIVE ]
119 add suite SUITE, version VERSION.
120 label, description, origin, codename
121 and signingkey are optional.
122 s rm SUITE remove a suite (will only work if empty)
124 s add-all-arches SUITE VERSION... as "s add" but adds suite-architecture
125 relationships for all architectures
126 s add-build-queue SUITE BUILD-QUEUE BUILD-QUEUE-CODENAME BUILD-QUEUE-ARCHIVE
127 add a build queue for an existing suite
129 suite-architecture / s-a:
130 s-a list show the architectures for all suites
131 s-a list-suite ARCH show the suites an ARCH is in
132 s-a list-arch SUITE show the architectures in a SUITE
133 s-a add SUITE ARCH add ARCH to suite
134 s-a rm SUITE ARCH remove ARCH from suite (will only work if
135 no packages remain for the arch in the suite)
137 suite-component / s-c:
138 s-c list show the architectures for all suites
139 s-c list-suite COMPONENT
140 show the suites a COMPONENT is in
141 s-c list-component SUITE
142 show the components in a SUITE
143 s-c add SUITE COMPONENT
144 add COMPONENT to suite
145 s-c rm SUITE COMPONENT remove component from suite (will only work if
146 no packages remain for the component in the suite)
148 suite-config / suite-cfg / s-cfg:
149 s-cfg list show the names of the configurations
150 s-cfg list SUITE show the configuration values for SUITE
151 s-cfg list-json SUITE show the configuration values for SUITE in JSON format
152 s-cfg get SUITE NAME ...
153 show the value for NAME in SUITE (format: NAME=VALUE)
154 s-cfg get-value SUITE NAME ...
155 show the value for NAME in SUITE (format: VALUE)
156 s-cfg get-json SUITE NAME ...
157 show the value for NAME in SUITE (format: JSON object)
158 s-cfg set SUITE NAME=VALUE ...
159 set NAME to VALUE in SUITE
160 s-cfg set-json SUITE
161 s-cfg set-json SUITE FILENAME
162 parse FILENAME (if absent or "-", then stdin) as JSON
163 and update all configurations listed to match the
164 value in the JSON.
165 Uses the same format as list-json or get-json outputs.
167 archive:
168 archive list list all archives
169 archive add NAME ROOT DESCRIPTION [primary-mirror=MIRROR] [tainted=1]
170 add archive NAME with path ROOT,
171 primary mirror MIRROR.
172 archive rm NAME remove archive NAME (will only work if there are
173 no files and no suites in the archive)
174 archive rename OLD NEW rename archive OLD to NEW
176 version-check / v-c:
177 v-c list show version checks for all suites
178 v-c list-suite SUITE show version checks for suite SUITE
179 v-c add SUITE CHECK REFERENCE add a version check for suite SUITE
180 v-c rm SUITE CHECK REFERENCE remove a version check
181 where
182 CHECK is one of Enhances, MustBeNewerThan, MustBeOlderThan
183 REFERENCE is another suite name
185 change-component:
186 change-component SUITE COMPONENT source SOURCE...
187 change-component SUITE COMPONENT binary BINARY...
188 Move source or binary packages to a different component by copying
189 associated files and changing the overrides.
191 forget-signature FILE: forget that we saw FILE
192""")
193 sys.exit(exit_code)
195################################################################################
198def __architecture_list(d, args) -> NoReturn:
199 q = d.session().query(Architecture).order_by(Architecture.arch_string)
200 for j in q.all():
201 # HACK: We should get rid of source from the arch table
202 if j.arch_string == 'source':
203 continue
204 print(j.arch_string)
205 sys.exit(0)
208def __architecture_add(d, args):
209 die_arglen(args, 4, "E: adding an architecture requires a name and a description")
210 print("Adding architecture %s" % args[2])
211 suites = [str(x) for x in args[4:]]
212 if len(suites) > 0: 212 ↛ 213line 212 didn't jump to line 213, because the condition on line 212 was never true
213 print("Adding to suites %s" % ", ".join(suites))
214 if not dryrun: 214 ↛ 232line 214 didn't jump to line 232, because the condition on line 214 was never false
215 try:
216 s = d.session()
217 a = Architecture()
218 a.arch_string = str(args[2]).lower()
219 a.description = str(args[3])
220 s.add(a)
221 for sn in suites: 221 ↛ 222line 221 didn't jump to line 222, because the loop on line 221 never started
222 su = get_suite(sn, s)
223 if su is not None:
224 a.suites.append(su)
225 else:
226 warn("W: Cannot find suite %s" % su)
227 s.commit()
228 except IntegrityError as e:
229 die("E: Integrity error adding architecture %s (it probably already exists)" % args[2])
230 except SQLAlchemyError as e:
231 die("E: Error adding architecture %s (%s)" % (args[2], e))
232 print("Architecture %s added" % (args[2]))
235def __architecture_rm(d, args):
236 die_arglen(args, 3, "E: removing an architecture requires at least a name")
237 print("Removing architecture %s" % args[2])
238 if not dryrun:
239 try:
240 s = d.session()
241 a = get_architecture(args[2].lower(), s)
242 if a is None:
243 die("E: Cannot find architecture %s" % args[2])
244 s.delete(a)
245 s.commit()
246 except IntegrityError as e:
247 die("E: Integrity error removing architecture %s (suite-arch entries probably still exist)" % args[2])
248 except SQLAlchemyError as e:
249 die("E: Error removing architecture %s (%s)" % (args[2], e))
250 print("Architecture %s removed" % args[2])
253def architecture(args):
254 Cnf = utils.get_conf()
255 d = DBConn()
257 die_arglen(args, 2, "E: architecture needs at least a command")
259 mode = args[1].lower()
260 if mode == 'list': 260 ↛ 261line 260 didn't jump to line 261, because the condition on line 260 was never true
261 __architecture_list(d, args)
262 elif mode == 'add': 262 ↛ 264line 262 didn't jump to line 264, because the condition on line 262 was never false
263 __architecture_add(d, args)
264 elif mode == 'rm':
265 __architecture_rm(d, args)
266 else:
267 die("E: architecture command unknown")
270dispatch['architecture'] = architecture
271dispatch['a'] = architecture
273################################################################################
276def component_list():
277 session = DBConn().session()
278 for component in session.query(Component).order_by(Component.component_name):
279 print("{0} ordering={1}".format(component.component_name, component.ordering))
282def component_add(args):
283 (name, description, ordering) = args[0:3]
285 attributes = dict(
286 component_name=name,
287 description=description,
288 ordering=ordering,
289 )
291 for option in args[3:]:
292 (key, value) = option.split('=')
293 attributes[key] = value
295 session = DBConn().session()
297 component = Component()
298 for key, value in attributes.items():
299 setattr(component, key, value)
301 session.add(component)
302 session.flush()
304 if dryrun:
305 session.rollback()
306 else:
307 session.commit()
310def component_rm(name: str) -> None:
311 session = DBConn().session()
312 component = get_component(name, session)
313 session.delete(component)
314 session.flush()
316 if dryrun:
317 session.rollback()
318 else:
319 session.commit()
322def component_rename(oldname: str, newname: str) -> None:
323 session = DBConn().session()
324 component = get_component(oldname, session)
325 component.component_name = newname
326 session.flush()
328 if dryrun:
329 session.rollback()
330 else:
331 session.commit()
334def component(command):
335 mode = command[1]
336 if mode == 'list':
337 component_list()
338 elif mode == 'rename':
339 component_rename(command[2], command[3])
340 elif mode == 'add':
341 component_add(command[2:])
342 elif mode == 'rm':
343 component_rm(command[2])
344 else:
345 die("E: component command unknown")
348dispatch['component'] = component
350################################################################################
353def __suite_list(d, args):
354 s = d.session()
355 for j in s.query(Suite).join(Suite.archive).order_by(Archive.archive_name, Suite.suite_name).all():
356 if len(args) > 2 and args[2] == "--print-archive": 356 ↛ 357line 356 didn't jump to line 357, because the condition on line 356 was never true
357 print("{0} {1}".format(j.archive.archive_name, j.suite_name))
358 else:
359 print("{0}".format(j.suite_name))
362def __suite_show(d, args):
363 if len(args) < 2:
364 die("E: showing an suite entry requires a suite")
366 s = d.session()
367 su = get_suite_or_die(args[2])
369 print(su.details())
372def __suite_add(d, args, addallarches=False) -> None:
373 die_arglen(args, 4, "E: adding a suite requires at least a name and a version")
374 suite_name = args[2].lower()
375 version = args[3]
376 kvpairs = __suite_config_set_confing_args_as_dict(args[4:])
378 if len(version) == 0: 378 ↛ 381line 378 didn't jump to line 381, because the condition on line 378 was never false
379 version = None
381 print("Adding suite %s" % suite_name)
382 if not dryrun: 382 ↛ 412line 382 didn't jump to line 412, because the condition on line 382 was never false
383 try:
384 s = d.session()
385 suite = Suite()
386 suite.suite_name = suite_name
387 suite.overridecodename = None
388 suite.version = version or None
389 # Most configurations will be handled by
390 # __suite_config_internal_set. However, a few are managed
391 # manually here because __suite_config_internal_set cannot
392 # handle them. Either because they are create-only or
393 # because suite-add handled them different (historically)
394 suite.codename = kvpairs.pop('codename', None)
395 signingkey = kvpairs.pop('signingkey', None)
396 if signingkey is not None: 396 ↛ 397line 396 didn't jump to line 397, because the condition on line 396 was never true
397 suite.signingkeys = [signingkey.upper()]
398 archive_name = kvpairs.pop('archive', None)
399 if archive_name is not None:
400 suite.archive = get_archive(archive_name, s)
401 else:
402 suite.archive = s.query(Archive).filter(~Archive.archive_name.in_(['build-queues', 'new', 'policy'])).one()
403 suite.srcformats = s.query(SrcFormat).all()
404 __suite_config_internal_set(suite, suite_name, kvpairs,
405 print_config_set=False)
406 s.add(suite)
407 s.flush()
408 except IntegrityError as e:
409 die("E: Integrity error adding suite %s (it probably already exists)" % suite_name)
410 except SQLAlchemyError as e:
411 die("E: Error adding suite %s (%s)" % (suite_name, e))
412 print("Suite %s added" % (suite_name))
414 if addallarches:
415 arches = []
416 q = s.query(Architecture).order_by(Architecture.arch_string)
417 for arch in q.all():
418 suite.architectures.append(arch)
419 arches.append(arch.arch_string)
421 print("Architectures %s added to %s" % (','.join(arches), suite_name))
423 s.commit()
426def __suite_rm(d, args):
427 die_arglen(args, 3, "E: removing a suite requires at least a name")
428 name = args[2]
429 print("Removing suite {0}".format(name))
430 if not dryrun:
431 try:
432 s = d.session()
433 su = get_suite_or_die(name, s)
434 s.delete(su)
435 s.commit()
436 except IntegrityError as e:
437 die("E: Integrity error removing suite {0} (suite-arch entries probably still exist)".format(name))
438 except SQLAlchemyError as e:
439 die("E: Error removing suite {0} ({1})".format(name, e))
440 print("Suite {0} removed".format(name))
443def __suite_add_build_queue(d, args):
444 session = d.session()
446 die_arglen(args, 6, "E: Adding a build queue needs four parameters.")
448 suite_name = args[2]
449 build_queue_name = args[3]
450 build_queue_codename = args[4]
451 build_queue_archive_name = args[5]
452 try:
453 suite = session.query(Suite).filter_by(suite_name=suite_name).one()
454 except NoResultFound:
455 die("E: Unknown suite '{0}'".format(suite_name))
456 try:
457 build_queue_archive = session.query(Archive).filter_by(archive_name=build_queue_archive_name).one()
458 except NoResultFound:
459 die("E: Unknown archive '{0}'".format(build_queue_archive_name))
461 # Create suite
462 s = Suite()
463 s.suite_name = build_queue_name
464 s.origin = suite.origin
465 s.label = suite.label
466 s.description = "buildd {0} incoming".format(suite_name)
467 s.codename = build_queue_codename
468 s.notautomatic = suite.notautomatic
469 s.overridesuite = suite.overridesuite or suite.suite_name
470 s.butautomaticupgrades = suite.butautomaticupgrades
471 s.signingkeys = suite.signingkeys
472 s.include_long_description = False
474 # Do not accept direct uploads to the build queue
475 s.accept_source_uploads = False
476 s.accept_binary_uploads = False
478 s.archive = build_queue_archive
479 s.architectures.extend(suite.architectures)
480 s.components.extend(suite.components)
481 s.srcformats.extend(suite.srcformats)
483 session.add(s)
484 session.flush()
486 bq = BuildQueue()
487 bq.queue_name = build_queue_codename
488 bq.suite = s
490 session.add(bq)
491 session.flush()
493 suite.copy_queues.append(bq)
495 session.commit()
498def suite(args):
499 Cnf = utils.get_conf()
500 d = DBConn()
502 die_arglen(args, 2, "E: suite needs at least a command")
504 mode = args[1].lower()
506 if mode == 'list':
507 __suite_list(d, args)
508 elif mode == 'show': 508 ↛ 509line 508 didn't jump to line 509, because the condition on line 508 was never true
509 __suite_show(d, args)
510 elif mode == 'rm': 510 ↛ 511line 510 didn't jump to line 511, because the condition on line 510 was never true
511 __suite_rm(d, args)
512 elif mode == 'add':
513 __suite_add(d, args, False)
514 elif mode == 'add-all-arches':
515 __suite_add(d, args, True)
516 elif mode == 'add-build-queue': 516 ↛ 519line 516 didn't jump to line 519, because the condition on line 516 was never false
517 __suite_add_build_queue(d, args)
518 else:
519 die("E: suite command unknown")
522dispatch['suite'] = suite
523dispatch['s'] = suite
525################################################################################
528def __suite_architecture_list(d, args):
529 s = d.session()
530 for j in s.query(Suite).order_by(Suite.suite_name):
531 architectures = j.get_architectures(skipsrc=True, skipall=True)
532 print(j.suite_name + ': '
533 + ', '.join([a.arch_string for a in architectures]))
536def __suite_architecture_listarch(d, args):
537 die_arglen(args, 3, "E: suite-architecture list-arch requires a suite")
538 suite = get_suite_or_die(args[2], d.session())
539 a = suite.get_architectures(skipsrc=True, skipall=True)
540 for j in a:
541 print(j.arch_string)
544def __suite_architecture_listsuite(d, args):
545 die_arglen(args, 3, "E: suite-architecture list-suite requires an arch")
546 architecture = get_architecture(args[2].lower(), d.session())
547 if architecture is None:
548 die("E: architecture %s is invalid" % args[2].lower())
549 for j in architecture.suites:
550 print(j.suite_name)
553def __suite_architecture_add(d, args):
554 if len(args) < 3: 554 ↛ 555line 554 didn't jump to line 555, because the condition on line 554 was never true
555 die("E: adding a suite-architecture entry requires a suite and arch")
557 s = d.session()
559 suite = get_suite_or_die(args[2], s)
561 for arch_name in args[3:]:
562 arch = get_architecture(arch_name.lower(), s)
563 if arch is None: 563 ↛ 564line 563 didn't jump to line 564, because the condition on line 563 was never true
564 die("E: Can't find architecture %s" % args[3].lower())
566 try:
567 suite.architectures.append(arch)
568 s.flush()
569 except IntegrityError as e:
570 die("E: Can't add suite-architecture entry (%s, %s) - probably already exists" % (args[2].lower(), arch_name))
571 except SQLAlchemyError as e:
572 die("E: Can't add suite-architecture entry (%s, %s) - %s" % (args[2].lower(), arch_name, e))
574 print("Added suite-architecture entry for %s, %s" % (args[2].lower(), arch_name))
576 if not dryrun: 576 ↛ 579line 576 didn't jump to line 579, because the condition on line 576 was never false
577 s.commit()
579 s.close()
582def __suite_architecture_rm(d, args):
583 if len(args) < 3:
584 die("E: removing an suite-architecture entry requires a suite and arch")
586 s = d.session()
587 if not dryrun:
588 try:
589 suite_name = args[2].lower()
590 suite = get_suite_or_die(suite_name, s)
591 arch_string = args[3].lower()
592 architecture = get_architecture(arch_string, s)
593 if architecture not in suite.architectures:
594 die("E: architecture %s not found in suite %s" % (arch_string, suite_name))
595 suite.architectures.remove(architecture)
596 s.commit()
597 except IntegrityError as e:
598 die("E: Can't remove suite-architecture entry (%s, %s) - it's probably referenced" % (args[2].lower(), args[3].lower()))
599 except SQLAlchemyError as e:
600 die("E: Can't remove suite-architecture entry (%s, %s) - %s" % (args[2].lower(), args[3].lower(), e))
602 print("Removed suite-architecture entry for %s, %s" % (args[2].lower(), args[3].lower()))
605def suite_architecture(args):
606 Cnf = utils.get_conf()
607 d = DBConn()
609 die_arglen(args, 2, "E: suite-architecture needs at least a command")
611 mode = args[1].lower()
613 if mode == 'list': 613 ↛ 614line 613 didn't jump to line 614, because the condition on line 613 was never true
614 __suite_architecture_list(d, args)
615 elif mode == 'list-arch':
616 __suite_architecture_listarch(d, args)
617 elif mode == 'list-suite': 617 ↛ 618line 617 didn't jump to line 618, because the condition on line 617 was never true
618 __suite_architecture_listsuite(d, args)
619 elif mode == 'add': 619 ↛ 621line 619 didn't jump to line 621, because the condition on line 619 was never false
620 __suite_architecture_add(d, args)
621 elif mode == 'rm':
622 __suite_architecture_rm(d, args)
623 else:
624 die("E: suite-architecture command unknown")
627dispatch['suite-architecture'] = suite_architecture
628dispatch['s-a'] = suite_architecture
630################################################################################
633def __suite_component_list(d, args):
634 s = d.session()
635 for j in s.query(Suite).order_by(Suite.suite_name):
636 components = j.components
637 print(j.suite_name + ': '
638 + ', '.join([c.component_name for c in components]))
641def __suite_component_listcomponent(d, args):
642 die_arglen(args, 3, "E: suite-component list-component requires a suite")
643 suite = get_suite_or_die(args[2], d.session())
644 for c in suite.components:
645 print(c.component_name)
648def __suite_component_listsuite(d, args):
649 die_arglen(args, 3, "E: suite-component list-suite requires an component")
650 component = get_component(args[2].lower(), d.session())
651 if component is None:
652 die("E: component %s is invalid" % args[2].lower())
653 for s in component.suites:
654 print(s.suite_name)
657def __suite_component_add(d, args):
658 if len(args) < 3: 658 ↛ 659line 658 didn't jump to line 659, because the condition on line 658 was never true
659 die("E: adding a suite-component entry requires a suite and component")
661 s = d.session()
663 suite = get_suite_or_die(args[2], s)
665 for component_name in args[3:]:
666 component = get_component(component_name.lower(), s)
667 if component is None: 667 ↛ 668line 667 didn't jump to line 668, because the condition on line 667 was never true
668 die("E: Can't find component %s" % args[3].lower())
670 try:
671 suite.components.append(component)
672 s.flush()
673 except IntegrityError as e:
674 die("E: Can't add suite-component entry (%s, %s) - probably already exists" % (args[2].lower(), component_name))
675 except SQLAlchemyError as e:
676 die("E: Can't add suite-component entry (%s, %s) - %s" % (args[2].lower(), component_name, e))
678 print("Added suite-component entry for %s, %s" % (args[2].lower(), component_name))
680 if not dryrun: 680 ↛ 682line 680 didn't jump to line 682, because the condition on line 680 was never false
681 s.commit()
682 s.close()
685def __suite_component_rm(d, args):
686 if len(args) < 3:
687 die("E: removing an suite-component entry requires a suite and component")
689 s = d.session()
690 if not dryrun:
691 try:
692 suite_name = args[2].lower()
693 suite = get_suite_or_die(suite_name, s)
694 component_string = args[3].lower()
695 component = get_component(component_string, s)
696 if component not in suite.components:
697 die("E: component %s not found in suite %s" % (component_string, suite_name))
698 suite.components.remove(component)
699 s.commit()
700 except IntegrityError as e:
701 die("E: Can't remove suite-component entry (%s, %s) - it's probably referenced" % (args[2].lower(), args[3].lower()))
702 except SQLAlchemyError as e:
703 die("E: Can't remove suite-component entry (%s, %s) - %s" % (args[2].lower(), args[3].lower(), e))
705 print("Removed suite-component entry for %s, %s" % (args[2].lower(), args[3].lower()))
708def suite_component(args):
709 Cnf = utils.get_conf()
710 d = DBConn()
712 die_arglen(args, 2, "E: suite-component needs at least a command")
714 mode = args[1].lower()
716 if mode == 'list': 716 ↛ 717line 716 didn't jump to line 717, because the condition on line 716 was never true
717 __suite_component_list(d, args)
718 elif mode == 'list-component': 718 ↛ 719line 718 didn't jump to line 719, because the condition on line 718 was never true
719 __suite_component_listcomponent(d, args)
720 elif mode == 'list-suite': 720 ↛ 721line 720 didn't jump to line 721, because the condition on line 720 was never true
721 __suite_component_listsuite(d, args)
722 elif mode == 'add': 722 ↛ 727line 722 didn't jump to line 727, because the condition on line 722 was never false
723 __suite_component_add(d, args)
724 # elif mode == 'rm':
725 # __suite_architecture_rm(d, args)
726 else:
727 die("E: suite-component command unknown")
730dispatch['suite-component'] = suite_component
731dispatch['s-c'] = suite_component
734################################################################################
736# Sentinel for detecting read-only configurations
737SUITE_CONFIG_READ_ONLY = object()
738SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON = object()
741@dataclass
742class SuiteConfigSerializer:
743 db_name: str
744 serialize: Callable[[Any], str]
745 deserialize: Optional[Callable[[str], Any]]
748def _serialize_suite(x):
749 if x is None: 749 ↛ 751line 749 didn't jump to line 751, because the condition on line 749 was never false
750 return None
751 return Suite.get(x).suite_name
754def _deserialize_suite(x):
755 if x is None: 755 ↛ 756line 755 didn't jump to line 756, because the condition on line 755 was never true
756 return None
757 return get_suite_or_die(x).suite_id
760@session_wrapper
761def _serialize_policy_queue(x, session=None):
762 if x is None: 762 ↛ 764line 762 didn't jump to line 764, because the condition on line 762 was never false
763 return None
764 try:
765 policy_obj = session.query(PolicyQueue).filter_by(policy_queue_id=x).one()
766 except NoResultFound:
767 return None
768 return policy_obj.queue_name
771def _deserialize_policy_queue(x):
772 if x is None:
773 return None
774 policy_queue = get_policy_queue(x)
775 if policy_queue is None:
776 raise ValueError("There is no policy queue with name %s" % x)
777 return policy_queue.policy_queue_id
780@session_wrapper
781def _serialize_archive(x, session=None):
782 if x is None: 782 ↛ 783line 782 didn't jump to line 783, because the condition on line 782 was never true
783 return None
784 try:
785 archive_obj = session.query(Archive).filter_by(archive_id=x).one()
786 except NoResultFound:
787 return None
788 return archive_obj.archive_name
791CUSTOM_SUITE_CONFIG_SERIALIZERS = {
792 'archive': SuiteConfigSerializer(db_name='archive_id', serialize=_serialize_archive,
793 deserialize=None),
794 'debugsuite': SuiteConfigSerializer(db_name='debugsuite_id', serialize=_serialize_suite,
795 deserialize=_deserialize_suite),
796 'new_queue': SuiteConfigSerializer(db_name='new_queue_id', serialize=_serialize_policy_queue,
797 deserialize=_deserialize_policy_queue),
798 'policy_queue': SuiteConfigSerializer(db_name='policy_queue_id', serialize=_serialize_policy_queue,
799 deserialize=_deserialize_policy_queue),
800}
803ALLOWED_SUITE_CONFIGS = {
804 'accept_binary_uploads': utils.parse_boolean_from_user,
805 'accept_source_uploads': utils.parse_boolean_from_user,
806 'allowcsset': utils.parse_boolean_from_user,
807 'announce': SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON,
808 'archive': SUITE_CONFIG_READ_ONLY,
809 'butautomaticupgrades': utils.parse_boolean_from_user,
810 'byhash': utils.parse_boolean_from_user,
811 'changelog': str,
812 'changelog_url': str,
813 'checksums': SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON,
814 'close_bugs': utils.parse_boolean_from_user,
815 'codename': SUITE_CONFIG_READ_ONLY,
816 'debugsuite': str,
817 'description': str,
818 'include_long_description': utils.parse_boolean_from_user,
819 'indices_compression': SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON,
820 'label': str,
821 'mail_whitelist': str,
822 'merged_pdiffs': utils.parse_boolean_from_user,
823 'new_queue': str,
824 'notautomatic': utils.parse_boolean_from_user,
825 'origin': str,
826 'overridecodename': str,
827 'overrideorigin': str,
828 'overrideprocess': utils.parse_boolean_from_user,
829 'overridesuite': str,
830 'policy_queue': str,
831 'priority': int,
832 'separate_contents_architecture_all': utils.parse_boolean_from_user,
833 # We do not support separate Packages-all, so do not let people set it.
834 'separate_packages_architecture_all': SUITE_CONFIG_READ_ONLY,
835 'signingkeys': SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON,
836 'suite_name': SUITE_CONFIG_READ_ONLY,
837 'untouchable': utils.parse_boolean_from_user,
838 'validtime': int,
839}
842def _get_suite_value(suite, conf_name):
843 serial_config = CUSTOM_SUITE_CONFIG_SERIALIZERS.get(conf_name)
844 if serial_config is None:
845 return getattr(suite, conf_name)
846 db_value = getattr(suite, serial_config.db_name)
847 return serial_config.serialize(db_value)
850def _set_suite_value(suite, conf_name, new_value):
851 serial_config = CUSTOM_SUITE_CONFIG_SERIALIZERS.get(conf_name)
852 db_name = conf_name
853 if serial_config is not None:
854 assert serial_config.deserialize is not None, "Changing %s is not supported!" % conf_name
855 new_value = serial_config.deserialize(new_value)
856 db_name = serial_config.db_name
857 setattr(suite, db_name, new_value)
860def __suite_config_get(d, args, direct_value=False, json_format=False):
861 die_arglen(args, 4, "E: suite-config get needs the name of a configuration")
862 session = d.session()
863 suite_name = args[2]
864 suite = get_suite_or_die(suite_name, session)
865 values = {}
866 for arg in args[3:]:
867 if arg not in ALLOWED_SUITE_CONFIGS: 867 ↛ 868line 867 didn't jump to line 868, because the condition on line 867 was never true
868 die("Unknown (or unsupported) suite configuration variable")
869 value = _get_suite_value(suite, arg)
870 if json_format: 870 ↛ 871line 870 didn't jump to line 871, because the condition on line 870 was never true
871 values[arg] = value
872 elif direct_value: 872 ↛ 873line 872 didn't jump to line 873, because the condition on line 872 was never true
873 print(value)
874 else:
875 print("%s=%s" % (arg, value))
876 if json_format: 876 ↛ 877line 876 didn't jump to line 877, because the condition on line 876 was never true
877 print(json.dumps(values, indent=2, sort_keys=True))
880def __suite_config_set(d, args):
881 die_arglen(args, 4, "E: suite-config set needs the name of a configuration")
882 session = d.session()
883 suite_name = args[2]
884 suite = get_suite_or_die(suite_name, session)
885 args_as_kvpairs = __suite_config_set_confing_args_as_dict(args[3:])
886 __suite_config_internal_set(suite, suite_name, args_as_kvpairs,
887 print_config_set=True
888 )
889 if dryrun:
890 session.rollback()
891 print()
892 print("This was a dryrun; changes have been rolled back")
893 else:
894 session.commit()
897def __suite_config_set_confing_args_as_dict(args):
898 # Use OrderedDict to preserve order (makes "dak admin suite-config set ..."
899 # less confusing when things are processed in the input order)
900 kvpairs = collections.OrderedDict()
901 for arg in args:
902 if '=' not in arg: 902 ↛ 903line 902 didn't jump to line 903, because the condition on line 902 was never true
903 die("Missing value for configuration %s: Use key=value format" % arg)
904 conf_name, new_value_str = arg.split('=', 1)
905 kvpairs[conf_name] = new_value_str
906 return kvpairs
909def __suite_config_internal_set(suite, suite_name, kvpairs, print_config_set=True):
910 for kvpair in kvpairs.items():
911 conf_name, new_value_str = kvpair
912 cli_parser = ALLOWED_SUITE_CONFIGS.get(conf_name)
913 if cli_parser is None: 913 ↛ 914line 913 didn't jump to line 914, because the condition on line 913 was never true
914 die("Unknown (or unsupported) suite configuration variable")
915 if cli_parser in (SUITE_CONFIG_READ_ONLY, SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON): 915 ↛ 916line 915 didn't jump to line 916, because the condition on line 915 was never true
916 if cli_parser == SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON:
917 msg = "Cannot parse value for %s" \
918 ''' - set via echo '{"%s": <...>}' | dak suite-config set-json %s instead'''
919 warn(msg % (conf_name, conf_name, suite_name))
920 die("Cannot change %s from the command line" % conf_name)
921 try:
922 new_value = cli_parser(new_value_str)
923 except (RuntimeError, ValueError, TypeError) as e:
924 warn("Could not parse new value for %s (given: %s)" % (conf_name, new_value_str))
925 raise e
926 try:
927 _set_suite_value(suite, conf_name, new_value)
928 except (RuntimeError, ValueError, TypeError) as e:
929 warn("Could not set new value for %s (given: %s)" % (conf_name, new_value))
930 raise e
931 if print_config_set:
932 print("%s=%s" % (conf_name, _get_suite_value(suite, conf_name)))
935def __suite_config_set_json(d, args):
936 session = d.session()
937 suite_name = args[2]
938 suite = get_suite_or_die(suite_name, session)
939 filename = '-'
940 if len(args) > 3:
941 if len(args) > 4:
942 warn("W: Ignoring extra argument after the json file name")
943 filename = args[3]
944 if filename != '-':
945 with open(filename) as fd:
946 update_config = json.load(fd)
947 else:
948 update_config = json.load(sys.stdin)
949 if update_config is None or not isinstance(update_config, dict):
950 die("E: suite-config set-json expects a dictionary (json object), got %s" % type(update_config))
952 for conf_name in sorted(update_config):
953 new_value = update_config[conf_name]
954 cli_parser = ALLOWED_SUITE_CONFIGS.get(conf_name)
955 if cli_parser is None:
956 die("Unknown (or unsupported) suite configuration variable: %s" % conf_name)
957 if cli_parser is SUITE_CONFIG_READ_ONLY:
958 die("Cannot change %s via JSON" % conf_name)
959 try:
960 _set_suite_value(suite, conf_name, new_value)
961 except (RuntimeError, ValueError, TypeError) as e:
962 warn("Could not set new value for %s (given: %s)" % (conf_name, new_value))
963 raise e
964 print("%s=%s" % (conf_name, _get_suite_value(suite, conf_name)))
965 if dryrun:
966 session.rollback()
967 print()
968 print("This was a dryrun; changes have been rolled back")
969 else:
970 session.commit()
973def __suite_config_list(d, args, json_format=False):
974 suite = None
975 session = d.session()
976 if len(args) > 3: 976 ↛ 977line 976 didn't jump to line 977, because the condition on line 976 was never true
977 warn("W: Ignoring extra argument after the suite name")
978 if len(args) == 3: 978 ↛ 982line 978 didn't jump to line 982, because the condition on line 978 was never false
979 suite_name = args[2]
980 suite = get_suite_or_die(suite_name, session)
981 else:
982 if json_format:
983 die("E: suite-config list-json requires a suite name!")
984 print("Valid suite-config options manageable by this command:")
985 print()
986 values = {}
988 for arg in sorted(ALLOWED_SUITE_CONFIGS):
989 mode = 'writable'
990 if suite is not None: 990 ↛ 997line 990 didn't jump to line 997, because the condition on line 990 was never false
991 value = _get_suite_value(suite, arg)
992 if json_format: 992 ↛ 993line 992 didn't jump to line 993, because the condition on line 992 was never true
993 values[arg] = value
994 else:
995 print("%s=%s" % (arg, value))
996 else:
997 converter = ALLOWED_SUITE_CONFIGS[arg]
998 if converter is SUITE_CONFIG_READ_ONLY:
999 mode = 'read-only'
1000 elif converter is SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON:
1001 mode = 'writeable (via set-json only)'
1002 print(" * %s (%s)" % (arg, mode))
1003 if json_format: 1003 ↛ 1004line 1003 didn't jump to line 1004, because the condition on line 1003 was never true
1004 print(json.dumps(values, indent=2, sort_keys=True))
1007def suite_config(args):
1008 Cnf = utils.get_conf()
1009 d = DBConn()
1011 die_arglen(args, 2, "E: suite-config needs a command")
1012 mode = args[1].lower()
1014 if mode in {'get', 'get-value', 'get-json'}:
1015 direct_value = False
1016 json_format = mode == 'get-json'
1017 if mode == 'get-value': 1017 ↛ 1018line 1017 didn't jump to line 1018, because the condition on line 1017 was never true
1018 direct_value = True
1019 if len(args) > 4:
1020 die("E: get-value must receive exactly one key to lookup")
1021 __suite_config_get(d, args, direct_value=direct_value, json_format=json_format)
1022 elif mode == 'set':
1023 __suite_config_set(d, args)
1024 elif mode == 'set-json': 1024 ↛ 1025line 1024 didn't jump to line 1025, because the condition on line 1024 was never true
1025 __suite_config_set_json(d, args)
1026 elif mode in {'list', 'list-json'}: 1026 ↛ 1030line 1026 didn't jump to line 1030, because the condition on line 1026 was never false
1027 json_format = mode == 'list-json'
1028 __suite_config_list(d, args, json_format=json_format)
1029 else:
1030 suite = get_suite(mode, d.session())
1031 if suite is not None:
1032 warn("Did you get the order of the suite and the subcommand wrong?")
1033 warn("Syntax: dak admin %s {get,set,...} <suite>" % args[0])
1034 die("E: suite-config command unknown")
1037dispatch['suite-config'] = suite_config
1038dispatch['suite-cfg'] = suite_config
1039dispatch['s-cfg'] = suite_config
1042################################################################################
1045def archive_list():
1046 session = DBConn().session()
1047 for archive in session.query(Archive).order_by(Archive.archive_name):
1048 print("{0} path={1} description={2} tainted={3}".format(archive.archive_name, archive.path, archive.description, archive.tainted))
1051def archive_add(args):
1052 (name, path, description) = args[0:3]
1054 attributes = dict(
1055 archive_name=name,
1056 path=path,
1057 description=description,
1058 )
1060 for option in args[3:]: 1060 ↛ 1061line 1060 didn't jump to line 1061, because the loop on line 1060 never started
1061 (key, value) = option.split('=')
1062 attributes[key] = value
1064 session = DBConn().session()
1066 archive = Archive()
1067 for key, value in attributes.items():
1068 setattr(archive, key, value)
1070 session.add(archive)
1071 session.flush()
1073 if dryrun: 1073 ↛ 1074line 1073 didn't jump to line 1074, because the condition on line 1073 was never true
1074 session.rollback()
1075 else:
1076 session.commit()
1079def archive_rm(name):
1080 session = DBConn().session()
1081 archive = get_archive(name, session)
1082 session.delete(archive)
1083 session.flush()
1085 if dryrun:
1086 session.rollback()
1087 else:
1088 session.commit()
1091def archive_rename(oldname, newname):
1092 session = DBConn().session()
1093 archive = get_archive(oldname, session)
1094 archive.archive_name = newname
1095 session.flush()
1097 if dryrun:
1098 session.rollback()
1099 else:
1100 session.commit()
1103def archive(command):
1104 mode = command[1]
1105 if mode == 'list':
1106 archive_list()
1107 elif mode == 'rename': 1107 ↛ 1108line 1107 didn't jump to line 1108, because the condition on line 1107 was never true
1108 archive_rename(command[2], command[3])
1109 elif mode == 'add': 1109 ↛ 1111line 1109 didn't jump to line 1111, because the condition on line 1109 was never false
1110 archive_add(command[2:])
1111 elif mode == 'rm':
1112 archive_rm(command[2])
1113 else:
1114 die("E: archive command unknown")
1117dispatch['archive'] = archive
1119################################################################################
1122def __version_check_list(d):
1123 session = d.session()
1124 for s in session.query(Suite).order_by(Suite.suite_name):
1125 __version_check_list_suite(d, s.suite_name)
1128def __version_check_list_suite(d, suite_name):
1129 vcs = get_version_checks(suite_name)
1130 for vc in vcs:
1131 print("%s %s %s" % (suite_name, vc.check, vc.reference.suite_name))
1134def __version_check_add(d, suite_name, check, reference_name):
1135 suite = get_suite_or_die(suite_name,
1136 error_message="E: Could not find suite %(suite_name)s")
1137 reference = get_suite_or_die(reference_name,
1138 error_message="E: Could not find reference suite %(suite_name)s")
1140 session = d.session()
1141 vc = VersionCheck()
1142 vc.suite = suite
1143 vc.check = check
1144 vc.reference = reference
1145 session.add(vc)
1146 session.commit()
1149def __version_check_rm(d, suite_name, check, reference_name):
1150 suite = get_suite_or_die(suite_name,
1151 error_message="E: Could not find suite %(suite_name)s")
1152 reference = get_suite_or_die(reference_name,
1153 error_message="E: Could not find reference suite %(suite_name)s")
1155 session = d.session()
1156 try:
1157 vc = session.query(VersionCheck).filter_by(suite=suite, check=check, reference=reference).one()
1158 session.delete(vc)
1159 session.commit()
1160 except NoResultFound:
1161 print("W: version-check not found.")
1164def version_check(args):
1165 Cnf = utils.get_conf()
1166 d = DBConn()
1168 die_arglen(args, 2, "E: version-check needs at least a command")
1169 mode = args[1].lower()
1171 if mode == 'list': 1171 ↛ 1172line 1171 didn't jump to line 1172, because the condition on line 1171 was never true
1172 __version_check_list(d)
1173 elif mode == 'list-suite': 1173 ↛ 1174line 1173 didn't jump to line 1174, because the condition on line 1173 was never true
1174 if len(args) != 3:
1175 die("E: version-check list-suite needs a single parameter")
1176 __version_check_list_suite(d, args[2])
1177 elif mode == 'add': 1177 ↛ 1181line 1177 didn't jump to line 1181, because the condition on line 1177 was never false
1178 if len(args) != 5: 1178 ↛ 1179line 1178 didn't jump to line 1179, because the condition on line 1178 was never true
1179 die("E: version-check add needs three parameters")
1180 __version_check_add(d, args[2], args[3], args[4])
1181 elif mode == 'rm':
1182 if len(args) != 5:
1183 die("E: version-check rm needs three parameters")
1184 __version_check_rm(d, args[2], args[3], args[4])
1185 else:
1186 die("E: version-check command unknown")
1189dispatch['version-check'] = version_check
1190dispatch['v-c'] = version_check
1192################################################################################
1195def show_config(args):
1196 cnf = utils.get_conf()
1198 die_arglen(args, 2, "E: config needs at least a command")
1200 mode = args[1].lower()
1202 if mode == 'db':
1203 connstr = ""
1204 if "DB::Service" in cnf:
1205 # Service mode
1206 connstr = "postgresql://service=%s" % cnf["DB::Service"]
1207 elif "DB::Host" in cnf:
1208 # TCP/IP
1209 connstr = "postgresql://%s" % cnf["DB::Host"]
1210 if "DB::Port" in cnf and cnf["DB::Port"] != "-1":
1211 connstr += ":%s" % cnf["DB::Port"]
1212 connstr += "/%s" % cnf["DB::Name"]
1213 else:
1214 # Unix Socket
1215 connstr = "postgresql:///%s" % cnf["DB::Name"]
1216 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
1217 connstr += "?port=%s" % cnf["DB::Port"]
1218 print(connstr)
1219 elif mode == 'db-shell':
1220 e = []
1221 if "DB::Service" in cnf:
1222 e.append('PGSERVICE')
1223 print("PGSERVICE=%s" % cnf["DB::Service"])
1224 if "DB::Name" in cnf:
1225 e.append('PGDATABASE')
1226 print("PGDATABASE=%s" % cnf["DB::Name"])
1227 if "DB::Host" in cnf:
1228 print("PGHOST=%s" % cnf["DB::Host"])
1229 e.append('PGHOST')
1230 if "DB::Port" in cnf and cnf["DB::Port"] != "-1":
1231 print("PGPORT=%s" % cnf["DB::Port"])
1232 e.append('PGPORT')
1233 print("export " + " ".join(e))
1234 elif mode == 'get':
1235 print(cnf.get(args[2]))
1236 else:
1237 session = DBConn().session()
1238 try:
1239 o = session.query(DBConfig).filter_by(name=mode).one()
1240 print(o.value)
1241 except NoResultFound:
1242 print("W: option '%s' not set" % mode)
1245dispatch['config'] = show_config
1246dispatch['c'] = show_config
1248################################################################################
1251def show_keyring(args):
1252 cnf = utils.get_conf()
1254 die_arglen(args, 2, "E: keyring needs at least a command")
1256 mode = args[1].lower()
1258 d = DBConn()
1260 q = d.session().query(Keyring).filter(Keyring.active == True) # noqa:E712
1262 if mode == 'list-all':
1263 pass
1264 elif mode == 'list-binary':
1265 q = q.join(Keyring.acl).filter(ACL.allow_source == False) # noqa:E712
1266 elif mode == 'list-source':
1267 q = q.join(Keyring.acl).filter(ACL.allow_source == True) # noqa:E712
1268 else:
1269 die("E: keyring command unknown")
1271 for k in q.all():
1272 print(k.keyring_name)
1275def keyring_add_buildd(command):
1276 name = command[2]
1277 arch_names = command[3:]
1279 session = DBConn().session()
1280 arches = session.query(Architecture).filter(Architecture.arch_string.in_(arch_names))
1282 acl = ACL()
1283 acl.name = 'buildd-{0}'.format('+'.join(arch_names))
1284 acl.architectures.update(arches)
1285 acl.allow_new = True
1286 acl.allow_binary = True
1287 acl.allow_binary_only = True
1288 acl.allow_hijack = True
1289 session.add(acl)
1291 k = Keyring()
1292 k.keyring_name = name
1293 k.acl = acl
1294 k.priority = 10
1295 session.add(k)
1297 session.commit()
1300def keyring(command):
1301 if command[1].startswith('list-'):
1302 show_keyring(command)
1303 elif command[1] == 'add-buildd':
1304 keyring_add_buildd(command)
1305 else:
1306 die("E: keyring command unknown")
1309dispatch['keyring'] = keyring
1310dispatch['k'] = keyring
1312################################################################################
1315def change_component_source(transaction, suite, component, source_names):
1316 session = transaction.session
1318 overrides = session.query(Override).filter(Override.package.in_(source_names)).filter_by(suite=suite).join(OverrideType).filter_by(overridetype='dsc')
1319 for override in overrides:
1320 print("Changing override for {0}".format(override.package))
1321 override.component = component
1322 session.flush()
1324 sources = session.query(DBSource).filter(DBSource.source.in_(source_names)).filter(DBSource.suites.contains(suite))
1325 for source in sources:
1326 print("Copying {0}={1}".format(source.source, source.version))
1327 transaction.copy_source(source, suite, component)
1330def change_component_binary(transaction, suite, component, binary_names):
1331 session = transaction.session
1333 overrides = session.query(Override).filter(Override.package.in_(binary_names)).filter_by(suite=suite).join(OverrideType).filter(OverrideType.overridetype.in_(['deb', 'udeb']))
1334 for override in overrides:
1335 print("Changing override for {0}".format(override.package))
1336 override.component = component
1337 session.flush()
1339 binaries = session.query(DBBinary).filter(DBBinary.package.in_(binary_names)).filter(DBBinary.suites.contains(suite))
1340 for binary in binaries:
1341 print("Copying {0}={1} [{2}]".format(binary.package, binary.version, binary.architecture.arch_string))
1342 transaction.copy_binary(binary, suite, component)
1343 pass
1346def change_component(args):
1347 with daklib.archive.ArchiveTransaction() as transaction:
1348 session = transaction.session
1350 suite = session.query(Suite).filter_by(suite_name=args[1]).one()
1351 component = session.query(Component).filter_by(component_name=args[2]).one()
1353 if args[3] == 'source':
1354 change_component_source(transaction, suite, component, args[4:])
1355 elif args[3] == 'binary':
1356 change_component_binary(transaction, suite, component, args[4:])
1357 else:
1358 raise Exception("Can only move source or binary, not {0}".format(args[3]))
1360 transaction.commit()
1363dispatch['change-component'] = change_component
1365################################################################################
1368def forget_signature(args):
1369 filename = args[1]
1370 with open(filename, 'rb') as fh:
1371 data = fh.read()
1373 session = DBConn().session()
1374 keyrings = [k.keyring_name for k in session.query(Keyring).filter_by(active=True).order_by(Keyring.priority)]
1375 signed_file = daklib.gpg.SignedFile(data, keyrings)
1376 history = SignatureHistory.from_signed_file(signed_file).query(session)
1377 if history is not None: 1377 ↛ 1381line 1377 didn't jump to line 1381, because the condition on line 1377 was never false
1378 session.delete(history)
1379 session.commit()
1380 else:
1381 print("Signature was not known to dak.")
1382 session.rollback()
1385dispatch['forget-signature'] = forget_signature
1387################################################################################
1390def main():
1391 """Perform administrative work on the dak database"""
1392 global dryrun
1393 Cnf = utils.get_conf()
1394 arguments = [('h', "help", "Admin::Options::Help"),
1395 ('n', "dry-run", "Admin::Options::Dry-Run")]
1396 for i in ["help", "dry-run"]:
1397 key = "Admin::Options::%s" % i
1398 if key not in Cnf: 1398 ↛ 1396line 1398 didn't jump to line 1396, because the condition on line 1398 was never false
1399 Cnf[key] = ""
1401 arguments = apt_pkg.parse_commandline(Cnf, arguments, sys.argv)
1403 options = Cnf.subtree("Admin::Options")
1404 if options["Help"] or len(arguments) < 1:
1405 usage()
1406 if options["Dry-Run"]:
1407 dryrun = True
1409 subcommand = arguments[0]
1411 if subcommand in dispatch: 1411 ↛ 1414line 1411 didn't jump to line 1414, because the condition on line 1411 was never false
1412 dispatch[subcommand](arguments)
1413 else:
1414 die("E: Unknown command")
1416################################################################################
1419if __name__ == '__main__':
1420 main()