Coverage for dak/admin.py: 45%
848 statements
« prev ^ index » next coverage.py v7.6.0, created at 2026-08-03 16:46 +0000
« prev ^ index » next coverage.py v7.6.0, created at 2026-08-03 16:46 +0000
1"""Configure dak parameters in the database"""
3# Copyright (C) 2009 Mark Hymers <mhy@debian.org>
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19################################################################################
21import collections
22import json
23import sys
24from collections.abc import Callable, Sequence
25from dataclasses import dataclass
26from typing import Any, NoReturn
28import apt_pkg
29from sqlalchemy import select
30from sqlalchemy.exc import IntegrityError, SQLAlchemyError
32import daklib.archive
33import daklib.gpg
34from daklib import utils
35from daklib.dbconn import (
36 ACL,
37 Architecture,
38 Archive,
39 BuildQueue,
40 Component,
41 DBBinary,
42 DBConfig,
43 DBConn,
44 DBSource,
45 Keyring,
46 Override,
47 OverrideType,
48 PolicyQueue,
49 SignatureHistory,
50 SrcFormat,
51 Suite,
52 VersionCheck,
53 get_active_keyring_files,
54 get_architecture,
55 get_archive,
56 get_component,
57 get_component_by_name,
58 get_policy_queue,
59 get_suite,
60 get_suite_by_name,
61 get_version_checks,
62 session_wrapper,
63)
65################################################################################
67dispatch = {}
68dryrun = False
70################################################################################
73def warn(msg: str) -> None:
74 print(msg, file=sys.stderr)
77def die(msg: str, exit_code: int = 1) -> NoReturn:
78 print(msg, file=sys.stderr)
79 sys.exit(exit_code)
82def die_arglen(args: Sequence, args_needed: int, msg: str) -> None:
83 if len(args) < args_needed: 83 ↛ 84line 83 didn't jump to line 84 because the condition on line 83 was never true
84 die(msg)
87def get_suite_or_die(
88 suite_name: str,
89 session=None,
90 error_message="E: Invalid/unknown suite %(suite_name)s",
91) -> Suite:
92 suite = get_suite(suite_name.lower(), session=session)
93 if suite is None: 93 ↛ 94line 93 didn't jump to line 94 because the condition on line 93 was never true
94 die(error_message % {"suite_name": suite_name})
95 return suite
98def usage(exit_code: int = 0) -> NoReturn:
99 """Perform administrative work on the dak database."""
101 print(
102 """Usage: dak admin COMMAND
103Perform administrative work on the dak database.
105 -h, --help show this help and exit.
106 -n, --dry-run don't do anything, just show what would have been done
107 (only applies to add or rm operations).
109 Commands can use a long or abbreviated form:
111 config / c:
112 c db show db config
113 c db-shell show db config in a usable form for psql
114 c NAME show option NAME as set in configuration table
116 keyring / k:
117 k list-all list all keyrings
118 k list-binary list all keyrings with a NULL source acl
119 k list-source list all keyrings with a non NULL source acl
120 k add-buildd NAME ARCH... add buildd keyring with upload permission
121 for the given architectures
123 architecture / a:
124 a list show a list of architectures
125 a rm ARCH remove an architecture (will only work if
126 no longer linked to any suites)
127 a add ARCH DESCRIPTION [SUITELIST]
128 add architecture ARCH with DESCRIPTION.
129 If SUITELIST is given, add to each of the
130 suites at the same time
132 component:
133 component list show a list of components
134 component rm COMPONENT remove a component (will only work if
135 empty)
136 component add NAME DESCRIPTION ORDERING
137 add component NAME with DESCRIPTION.
138 Ordered at ORDERING.
140 suite / s:
141 s list [--print-archive]
142 show a list of suites
143 s show SUITE show config details for a suite
144 s add SUITE VERSION [ label=LABEL ] [ description=DESCRIPTION ]
145 [ origin=ORIGIN ] [ codename=CODENAME ]
146 [ signingkey=SIGNINGKEY ] [ archive=ARCHIVE ]
147 add suite SUITE, version VERSION.
148 label, description, origin, codename
149 and signingkey are optional.
150 s rm SUITE remove a suite (will only work if empty)
152 s add-all-arches SUITE VERSION... as "s add" but adds suite-architecture
153 relationships for all architectures
154 s add-build-queue SUITE BUILD-QUEUE BUILD-QUEUE-CODENAME BUILD-QUEUE-ARCHIVE
155 add a build queue for an existing suite
157 suite-architecture / s-a:
158 s-a list show the architectures for all suites
159 s-a list-suite ARCH show the suites an ARCH is in
160 s-a list-arch SUITE show the architectures in a SUITE
161 s-a add SUITE ARCH add ARCH to suite
162 s-a rm SUITE ARCH remove ARCH from suite (will only work if
163 no packages remain for the arch in the suite)
165 suite-component / s-c:
166 s-c list show the architectures for all suites
167 s-c list-suite COMPONENT
168 show the suites a COMPONENT is in
169 s-c list-component SUITE
170 show the components in a SUITE
171 s-c add SUITE COMPONENT
172 add COMPONENT to suite
173 s-c rm SUITE COMPONENT remove component from suite (will only work if
174 no packages remain for the component in the suite)
176 suite-config / suite-cfg / s-cfg:
177 s-cfg list show the names of the configurations
178 s-cfg list SUITE show the configuration values for SUITE
179 s-cfg list-json SUITE show the configuration values for SUITE in JSON format
180 s-cfg get SUITE NAME ...
181 show the value for NAME in SUITE (format: NAME=VALUE)
182 s-cfg get-value SUITE NAME ...
183 show the value for NAME in SUITE (format: VALUE)
184 s-cfg get-json SUITE NAME ...
185 show the value for NAME in SUITE (format: JSON object)
186 s-cfg set SUITE NAME=VALUE ...
187 set NAME to VALUE in SUITE
188 s-cfg set-json SUITE
189 s-cfg set-json SUITE FILENAME
190 parse FILENAME (if absent or "-", then stdin) as JSON
191 and update all configurations listed to match the
192 value in the JSON.
193 Uses the same format as list-json or get-json outputs.
195 archive:
196 archive list list all archives
197 archive add NAME ROOT DESCRIPTION [primary-mirror=MIRROR] [tainted=1]
198 add archive NAME with path ROOT,
199 primary mirror MIRROR.
200 archive rm NAME remove archive NAME (will only work if there are
201 no files and no suites in the archive)
202 archive rename OLD NEW rename archive OLD to NEW
204 version-check / v-c:
205 v-c list show version checks for all suites
206 v-c list-suite SUITE show version checks for suite SUITE
207 v-c add SUITE CHECK REFERENCE add a version check for suite SUITE
208 v-c rm SUITE CHECK REFERENCE remove a version check
209 where
210 CHECK is one of Enhances, MustBeNewerThan, MustBeOlderThan
211 REFERENCE is another suite name
213 change-component:
214 change-component SUITE COMPONENT source SOURCE...
215 change-component SUITE COMPONENT binary BINARY...
216 Move source or binary packages to a different component by copying
217 associated files and changing the overrides.
219 forget-signature FILE: forget that we saw FILE
220"""
221 )
222 sys.exit(exit_code)
225################################################################################
228def __architecture_list(d: DBConn, args: list[str]) -> NoReturn:
229 q = select(Architecture.arch_string).order_by(Architecture.arch_string)
230 for arch_string in d.session().scalars(q):
231 # HACK: We should get rid of source from the arch table
232 if arch_string == "source":
233 continue
234 print(arch_string)
235 sys.exit(0)
238def __architecture_add(d: DBConn, args: list[str]) -> None:
239 die_arglen(args, 4, "E: adding an architecture requires a name and a description")
240 print("Adding architecture %s" % args[2])
241 suites = [str(x) for x in args[4:]]
242 if len(suites) > 0: 242 ↛ 243line 242 didn't jump to line 243 because the condition on line 242 was never true
243 print("Adding to suites %s" % ", ".join(suites))
244 if not dryrun: 244 ↛ 265line 244 didn't jump to line 265 because the condition on line 244 was always true
245 try:
246 s = d.session()
247 a = Architecture()
248 a.arch_string = str(args[2]).lower()
249 a.description = str(args[3])
250 s.add(a)
251 for sn in suites: 251 ↛ 252line 251 didn't jump to line 252 because the loop on line 251 never started
252 su = get_suite(sn, s)
253 if su is not None:
254 a.suites.append(su)
255 else:
256 warn("W: Cannot find suite %s" % su)
257 s.commit()
258 except IntegrityError:
259 die(
260 "E: Integrity error adding architecture %s (it probably already exists)"
261 % args[2]
262 )
263 except SQLAlchemyError as e:
264 die("E: Error adding architecture %s (%s)" % (args[2], e))
265 print("Architecture %s added" % (args[2]))
268def __architecture_rm(d: DBConn, args: list[str]) -> None:
269 die_arglen(args, 3, "E: removing an architecture requires at least a name")
270 print("Removing architecture %s" % args[2])
271 if not dryrun:
272 try:
273 s = d.session()
274 a = get_architecture(args[2].lower(), s)
275 if a is None:
276 die("E: Cannot find architecture %s" % args[2])
277 s.delete(a)
278 s.commit()
279 except IntegrityError:
280 die(
281 "E: Integrity error removing architecture %s (suite-arch entries probably still exist)"
282 % args[2]
283 )
284 except SQLAlchemyError as e:
285 die("E: Error removing architecture %s (%s)" % (args[2], e))
286 print("Architecture %s removed" % args[2])
289def architecture(args: list[str]) -> None:
290 d = DBConn()
292 die_arglen(args, 2, "E: architecture needs at least a command")
294 mode = args[1].lower()
295 if mode == "list": 295 ↛ 296line 295 didn't jump to line 296 because the condition on line 295 was never true
296 __architecture_list(d, args)
297 elif mode == "add": 297 ↛ 299line 297 didn't jump to line 299 because the condition on line 297 was always true
298 __architecture_add(d, args)
299 elif mode == "rm":
300 __architecture_rm(d, args)
301 else:
302 die("E: architecture command unknown")
305dispatch["architecture"] = architecture
306dispatch["a"] = architecture
308################################################################################
311def component_list() -> None:
312 session = DBConn().session()
313 for component_name, ordering in session.execute(
314 select(Component.component_name, Component.ordering).order_by(
315 Component.component_name
316 )
317 ):
318 print("{0} ordering={1}".format(component_name, ordering))
321def component_add(args: list[str]) -> None:
322 (name, description, ordering) = args[0:3]
324 attributes = {
325 "component_name": name,
326 "description": description,
327 "ordering": ordering,
328 }
330 for option in args[3:]:
331 (key, value) = option.split("=")
332 attributes[key] = value
334 session = DBConn().session()
336 component = Component()
337 for key, value in attributes.items():
338 setattr(component, key, value)
340 session.add(component)
341 session.flush()
343 if dryrun:
344 session.rollback()
345 else:
346 session.commit()
349def component_rm(name: str) -> None:
350 session = DBConn().session()
351 component = get_component(name, session)
352 session.delete(component)
353 session.flush()
355 if dryrun:
356 session.rollback()
357 else:
358 session.commit()
361def component_rename(oldname: str, newname: str) -> None:
362 session = DBConn().session()
363 component = get_component(oldname, session)
364 assert component is not None
365 component.component_name = newname
366 session.flush()
368 if dryrun:
369 session.rollback()
370 else:
371 session.commit()
374def component(args: list[str]) -> None:
375 mode = args[1]
376 if mode == "list":
377 component_list()
378 elif mode == "rename":
379 component_rename(args[2], args[3])
380 elif mode == "add":
381 component_add(args[2:])
382 elif mode == "rm":
383 component_rm(args[2])
384 else:
385 die("E: component command unknown")
388dispatch["component"] = component
390################################################################################
393def __suite_list(d: DBConn, args: list[str]) -> None:
394 s = d.session()
395 for j in s.scalars(
396 select(Suite)
397 .join(Suite.archive)
398 .order_by(Archive.archive_name, Suite.suite_name)
399 ):
400 if len(args) > 2 and args[2] == "--print-archive": 400 ↛ 401line 400 didn't jump to line 401 because the condition on line 400 was never true
401 print("{0} {1}".format(j.archive.archive_name, j.suite_name))
402 else:
403 print("{0}".format(j.suite_name))
406def __suite_show(d: DBConn, args: list[str]) -> None:
407 if len(args) < 2:
408 die("E: showing an suite entry requires a suite")
410 su = get_suite_or_die(args[2])
412 print(su.details())
415def __suite_add(d: DBConn, args: list[str], addallarches=False) -> None:
416 die_arglen(args, 4, "E: adding a suite requires at least a name and a version")
417 suite_name = args[2].lower()
418 version = args[3] or None
419 kvpairs = __suite_config_set_confing_args_as_dict(args[4:])
421 print("Adding suite %s" % suite_name)
422 if not dryrun: 422 ↛ 462line 422 didn't jump to line 462 because the condition on line 422 was always true
423 try:
424 s = d.session()
425 suite = Suite()
426 suite.suite_name = suite_name
427 suite.overridecodename = None
428 suite.version = version or None
429 # Most configurations will be handled by
430 # __suite_config_internal_set. However, a few are managed
431 # manually here because __suite_config_internal_set cannot
432 # handle them. Either because they are create-only or
433 # because suite-add handled them different (historically)
434 suite.codename = kvpairs.pop("codename", None)
435 signingkey = kvpairs.pop("signingkey", None)
436 if signingkey is not None: 436 ↛ 437line 436 didn't jump to line 437 because the condition on line 436 was never true
437 suite.signingkeys = [signingkey.upper()]
438 archive_name = kvpairs.pop("archive", None)
439 if archive_name is not None:
440 archive = get_archive(archive_name, s)
441 assert archive is not None
442 suite.archive = archive
443 else:
444 suite.archive = s.execute(
445 select(Archive).where(
446 ~Archive.archive_name.in_(["build-queues", "new", "policy"])
447 )
448 ).scalar_one()
449 __suite_config_internal_set(
450 suite, suite_name, kvpairs, print_config_set=False
451 )
452 s.add(suite)
453 suite.srcformats = list(s.scalars(select(SrcFormat)))
454 s.flush()
455 except IntegrityError:
456 die(
457 "E: Integrity error adding suite %s (it probably already exists)"
458 % suite_name
459 )
460 except SQLAlchemyError as e:
461 die("E: Error adding suite %s (%s)" % (suite_name, e))
462 print("Suite %s added" % (suite_name))
464 if addallarches:
465 arches = []
466 q = select(Architecture).order_by(Architecture.arch_string)
467 for arch in s.scalars(q):
468 suite.architectures.append(arch)
469 arches.append(arch.arch_string)
471 print("Architectures %s added to %s" % (",".join(arches), suite_name))
473 s.commit()
476def __suite_rm(d: DBConn, args: list[str]) -> None:
477 die_arglen(args, 3, "E: removing a suite requires at least a name")
478 name = args[2]
479 print("Removing suite {0}".format(name))
480 if not dryrun:
481 try:
482 s = d.session()
483 su = get_suite_or_die(name, s)
484 s.delete(su)
485 s.commit()
486 except IntegrityError:
487 die(
488 "E: Integrity error removing suite {0} (suite-arch entries probably still exist)".format(
489 name
490 )
491 )
492 except SQLAlchemyError as e:
493 die("E: Error removing suite {0} ({1})".format(name, e))
494 print("Suite {0} removed".format(name))
497def __suite_add_build_queue(d: DBConn, args: list[str]) -> None:
498 session = d.session()
500 die_arglen(args, 6, "E: Adding a build queue needs four parameters.")
502 suite_name = args[2]
503 build_queue_name = args[3]
504 build_queue_codename = args[4]
505 build_queue_archive_name = args[5]
506 suite = session.execute(
507 select(Suite).filter_by(suite_name=suite_name)
508 ).scalar_one_or_none()
509 if suite is None: 509 ↛ 510line 509 didn't jump to line 510 because the condition on line 509 was never true
510 die("E: Unknown suite '{0}'".format(suite_name))
511 build_queue_archive = session.execute(
512 select(Archive).filter_by(archive_name=build_queue_archive_name)
513 ).scalar_one_or_none()
514 if build_queue_archive is None: 514 ↛ 515line 514 didn't jump to line 515 because the condition on line 514 was never true
515 die("E: Unknown archive '{0}'".format(build_queue_archive_name))
517 # Create suite
518 s = Suite()
519 s.suite_name = build_queue_name
520 s.origin = suite.origin
521 s.label = suite.label
522 s.description = "buildd {0} incoming".format(suite_name)
523 s.codename = build_queue_codename
524 s.notautomatic = suite.notautomatic
525 s.overridesuite = suite.overridesuite or suite.suite_name
526 s.butautomaticupgrades = suite.butautomaticupgrades
527 s.signingkeys = suite.signingkeys
528 s.include_long_description = False
530 # Do not accept direct uploads to the build queue
531 s.accept_source_uploads = False
532 s.accept_binary_uploads = False
534 session.add(s)
536 s.archive = build_queue_archive
537 s.architectures.extend(suite.architectures)
538 s.components.extend(suite.components)
539 s.srcformats.extend(suite.srcformats)
541 session.flush()
543 bq = BuildQueue()
544 bq.queue_name = build_queue_codename
545 bq.suite = s
547 session.add(bq)
548 session.flush()
550 suite.copy_queues.append(bq)
552 session.commit()
555def suite(args: list[str]) -> None:
556 d = DBConn()
558 die_arglen(args, 2, "E: suite needs at least a command")
560 mode = args[1].lower()
562 if mode == "list":
563 __suite_list(d, args)
564 elif mode == "show": 564 ↛ 565line 564 didn't jump to line 565 because the condition on line 564 was never true
565 __suite_show(d, args)
566 elif mode == "rm": 566 ↛ 567line 566 didn't jump to line 567 because the condition on line 566 was never true
567 __suite_rm(d, args)
568 elif mode == "add":
569 __suite_add(d, args, False)
570 elif mode == "add-all-arches":
571 __suite_add(d, args, True)
572 elif mode == "add-build-queue": 572 ↛ 575line 572 didn't jump to line 575 because the condition on line 572 was always true
573 __suite_add_build_queue(d, args)
574 else:
575 die("E: suite command unknown")
578dispatch["suite"] = suite
579dispatch["s"] = suite
581################################################################################
584def __suite_architecture_list(d: DBConn, args: list[str]) -> None:
585 s = d.session()
586 for j in s.scalars(select(Suite).order_by(Suite.suite_name)):
587 architectures = j.get_architectures(skipsrc=True, skipall=True)
588 print(j.suite_name + ": " + ", ".join([a.arch_string for a in architectures]))
591def __suite_architecture_listarch(d: DBConn, args: list[str]) -> None:
592 die_arglen(args, 3, "E: suite-architecture list-arch requires a suite")
593 suite = get_suite_or_die(args[2], d.session())
594 a = suite.get_architectures(skipsrc=True, skipall=True)
595 for j in a:
596 print(j.arch_string)
599def __suite_architecture_listsuite(d: DBConn, args: list[str]) -> None:
600 die_arglen(args, 3, "E: suite-architecture list-suite requires an arch")
601 architecture = get_architecture(args[2].lower(), d.session())
602 if architecture is None:
603 die("E: architecture %s is invalid" % args[2].lower())
604 for j in architecture.suites:
605 print(j.suite_name)
608def __suite_architecture_add(d: DBConn, args: list[str]) -> None:
609 if len(args) < 3: 609 ↛ 610line 609 didn't jump to line 610 because the condition on line 609 was never true
610 die("E: adding a suite-architecture entry requires a suite and arch")
612 s = d.session()
614 suite = get_suite_or_die(args[2], s)
616 for arch_name in args[3:]:
617 arch = get_architecture(arch_name.lower(), s)
618 if arch is None: 618 ↛ 619line 618 didn't jump to line 619 because the condition on line 618 was never true
619 die("E: Can't find architecture %s" % args[3].lower())
621 try:
622 suite.architectures.append(arch)
623 s.flush()
624 except IntegrityError:
625 die(
626 "E: Can't add suite-architecture entry (%s, %s) - probably already exists"
627 % (args[2].lower(), arch_name)
628 )
629 except SQLAlchemyError as e:
630 die(
631 "E: Can't add suite-architecture entry (%s, %s) - %s"
632 % (args[2].lower(), arch_name, e)
633 )
635 print(
636 "Added suite-architecture entry for %s, %s" % (args[2].lower(), arch_name)
637 )
639 if not dryrun: 639 ↛ 642line 639 didn't jump to line 642 because the condition on line 639 was always true
640 s.commit()
642 s.close()
645def __suite_architecture_rm(d: DBConn, args: list[str]) -> None:
646 if len(args) < 3:
647 die("E: removing an suite-architecture entry requires a suite and arch")
649 s = d.session()
650 if not dryrun:
651 try:
652 suite_name = args[2].lower()
653 suite = get_suite_or_die(suite_name, s)
654 arch_string = args[3].lower()
655 architecture = get_architecture(arch_string, s)
656 if architecture not in suite.architectures:
657 die(
658 "E: architecture %s not found in suite %s"
659 % (arch_string, suite_name)
660 )
661 suite.architectures.remove(architecture)
662 s.commit()
663 except IntegrityError:
664 die(
665 "E: Can't remove suite-architecture entry (%s, %s) - it's probably referenced"
666 % (args[2].lower(), args[3].lower())
667 )
668 except SQLAlchemyError as e:
669 die(
670 "E: Can't remove suite-architecture entry (%s, %s) - %s"
671 % (args[2].lower(), args[3].lower(), e)
672 )
674 print(
675 "Removed suite-architecture entry for %s, %s"
676 % (args[2].lower(), args[3].lower())
677 )
680def suite_architecture(args: list[str]) -> None:
681 d = DBConn()
683 die_arglen(args, 2, "E: suite-architecture needs at least a command")
685 mode = args[1].lower()
687 if mode == "list": 687 ↛ 688line 687 didn't jump to line 688 because the condition on line 687 was never true
688 __suite_architecture_list(d, args)
689 elif mode == "list-arch":
690 __suite_architecture_listarch(d, args)
691 elif mode == "list-suite": 691 ↛ 692line 691 didn't jump to line 692 because the condition on line 691 was never true
692 __suite_architecture_listsuite(d, args)
693 elif mode == "add": 693 ↛ 695line 693 didn't jump to line 695 because the condition on line 693 was always true
694 __suite_architecture_add(d, args)
695 elif mode == "rm":
696 __suite_architecture_rm(d, args)
697 else:
698 die("E: suite-architecture command unknown")
701dispatch["suite-architecture"] = suite_architecture
702dispatch["s-a"] = suite_architecture
704################################################################################
707def __suite_component_list(d: DBConn, args: list[str]) -> None:
708 s = d.session()
709 for j in s.scalars(select(Suite).order_by(Suite.suite_name)):
710 components = j.components
711 print(j.suite_name + ": " + ", ".join([c.component_name for c in components]))
714def __suite_component_listcomponent(d: DBConn, args: list[str]) -> None:
715 die_arglen(args, 3, "E: suite-component list-component requires a suite")
716 suite = get_suite_or_die(args[2], d.session())
717 for c in suite.components:
718 print(c.component_name)
721def __suite_component_listsuite(d: DBConn, args: list[str]) -> None:
722 die_arglen(args, 3, "E: suite-component list-suite requires an component")
723 component = get_component(args[2].lower(), d.session())
724 if component is None:
725 die("E: component %s is invalid" % args[2].lower())
726 for s in component.suites:
727 print(s.suite_name)
730def __suite_component_add(d: DBConn, args: list[str]) -> None:
731 if len(args) < 3: 731 ↛ 732line 731 didn't jump to line 732 because the condition on line 731 was never true
732 die("E: adding a suite-component entry requires a suite and component")
734 s = d.session()
736 suite = get_suite_or_die(args[2], s)
738 for component_name in args[3:]:
739 component = get_component(component_name.lower(), s)
740 if component is None: 740 ↛ 741line 740 didn't jump to line 741 because the condition on line 740 was never true
741 die("E: Can't find component %s" % args[3].lower())
743 try:
744 suite.components.append(component)
745 s.flush()
746 except IntegrityError:
747 die(
748 "E: Can't add suite-component entry (%s, %s) - probably already exists"
749 % (args[2].lower(), component_name)
750 )
751 except SQLAlchemyError as e:
752 die(
753 "E: Can't add suite-component entry (%s, %s) - %s"
754 % (args[2].lower(), component_name, e)
755 )
757 print(
758 "Added suite-component entry for %s, %s" % (args[2].lower(), component_name)
759 )
761 if not dryrun: 761 ↛ 763line 761 didn't jump to line 763 because the condition on line 761 was always true
762 s.commit()
763 s.close()
766def __suite_component_rm(d: DBConn, args: list[str]) -> None:
767 if len(args) < 3:
768 die("E: removing an suite-component entry requires a suite and component")
770 s = d.session()
771 if not dryrun:
772 try:
773 suite_name = args[2].lower()
774 suite = get_suite_or_die(suite_name, s)
775 component_string = args[3].lower()
776 component = get_component(component_string, s)
777 if component not in suite.components:
778 die(
779 "E: component %s not found in suite %s"
780 % (component_string, suite_name)
781 )
782 suite.components.remove(component)
783 s.commit()
784 except IntegrityError:
785 die(
786 "E: Can't remove suite-component entry (%s, %s) - it's probably referenced"
787 % (args[2].lower(), args[3].lower())
788 )
789 except SQLAlchemyError as e:
790 die(
791 "E: Can't remove suite-component entry (%s, %s) - %s"
792 % (args[2].lower(), args[3].lower(), e)
793 )
795 print(
796 "Removed suite-component entry for %s, %s" % (args[2].lower(), args[3].lower())
797 )
800def suite_component(args: list[str]) -> None:
801 d = DBConn()
803 die_arglen(args, 2, "E: suite-component needs at least a command")
805 mode = args[1].lower()
807 if mode == "list": 807 ↛ 808line 807 didn't jump to line 808 because the condition on line 807 was never true
808 __suite_component_list(d, args)
809 elif mode == "list-component": 809 ↛ 810line 809 didn't jump to line 810 because the condition on line 809 was never true
810 __suite_component_listcomponent(d, args)
811 elif mode == "list-suite": 811 ↛ 812line 811 didn't jump to line 812 because the condition on line 811 was never true
812 __suite_component_listsuite(d, args)
813 elif mode == "add": 813 ↛ 818line 813 didn't jump to line 818 because the condition on line 813 was always true
814 __suite_component_add(d, args)
815 # elif mode == 'rm':
816 # __suite_architecture_rm(d, args)
817 else:
818 die("E: suite-component command unknown")
821dispatch["suite-component"] = suite_component
822dispatch["s-c"] = suite_component
825################################################################################
827# Sentinel for detecting read-only configurations
828SUITE_CONFIG_READ_ONLY = object()
829SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON = object()
832@dataclass
833class SuiteConfigSerializer:
834 db_name: str
835 serialize: Callable[[Any], str | None]
836 deserialize: Callable[[str | None], Any] | None
839def _serialize_suite(x: int | None) -> str | None:
840 if x is None: 840 ↛ 842line 840 didn't jump to line 842 because the condition on line 840 was always true
841 return None
842 return Suite.get(x).suite_name
845def _deserialize_suite(x: str | None) -> int | None:
846 if x is None: 846 ↛ 847line 846 didn't jump to line 847 because the condition on line 846 was never true
847 return None
848 return get_suite_or_die(x).suite_id
851@session_wrapper
852def _serialize_policy_queue(x, session=None):
853 if x is None: 853 ↛ 855line 853 didn't jump to line 855 because the condition on line 853 was always true
854 return None
855 return session.execute(
856 select(PolicyQueue.queue_name).filter_by(policy_queue_id=x)
857 ).scalar_one_or_none()
860def _deserialize_policy_queue(x):
861 if x is None:
862 return None
863 policy_queue = get_policy_queue(x)
864 if policy_queue is None:
865 raise ValueError("There is no policy queue with name %s" % x)
866 return policy_queue.policy_queue_id
869@session_wrapper
870def _serialize_archive(x, session=None):
871 if x is None: 871 ↛ 872line 871 didn't jump to line 872 because the condition on line 871 was never true
872 return None
873 return session.execute(
874 select(Archive.archive_name).filter_by(archive_id=x)
875 ).scalar_one_or_none()
878CUSTOM_SUITE_CONFIG_SERIALIZERS = {
879 "archive": SuiteConfigSerializer(
880 db_name="archive_id", serialize=_serialize_archive, deserialize=None
881 ),
882 "debugsuite": SuiteConfigSerializer(
883 db_name="debugsuite_id",
884 serialize=_serialize_suite,
885 deserialize=_deserialize_suite,
886 ),
887 "new_queue": SuiteConfigSerializer(
888 db_name="new_queue_id",
889 serialize=_serialize_policy_queue,
890 deserialize=_deserialize_policy_queue,
891 ),
892 "policy_queue": SuiteConfigSerializer(
893 db_name="policy_queue_id",
894 serialize=_serialize_policy_queue,
895 deserialize=_deserialize_policy_queue,
896 ),
897}
900ALLOWED_SUITE_CONFIGS: dict[str, Callable[[str], object] | object] = {
901 "accept_binary_uploads": utils.parse_boolean_from_user,
902 "accept_source_uploads": utils.parse_boolean_from_user,
903 "allowcsset": utils.parse_boolean_from_user,
904 "announce": SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON,
905 "archive": SUITE_CONFIG_READ_ONLY,
906 "butautomaticupgrades": utils.parse_boolean_from_user,
907 "byhash": utils.parse_boolean_from_user,
908 "changelog": str,
909 "changelog_url": str,
910 "checksums": SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON,
911 "close_bugs": utils.parse_boolean_from_user,
912 "codename": SUITE_CONFIG_READ_ONLY,
913 "debugsuite": str,
914 "description": str,
915 "include_long_description": utils.parse_boolean_from_user,
916 "indices_compression": SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON,
917 "label": str,
918 "mail_whitelist": str,
919 "merged_pdiffs": utils.parse_boolean_from_user,
920 "new_queue": str,
921 "notautomatic": utils.parse_boolean_from_user,
922 "origin": str,
923 "overridecodename": str,
924 "overrideorigin": str,
925 "overrideprocess": utils.parse_boolean_from_user,
926 "overridesuite": str,
927 "policy_queue": str,
928 "priority": int,
929 "separate_contents_architecture_all": utils.parse_boolean_from_user,
930 # We do not support separate Packages-all, so do not let people set it.
931 "separate_packages_architecture_all": SUITE_CONFIG_READ_ONLY,
932 "signingkeys": SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON,
933 "suite_name": SUITE_CONFIG_READ_ONLY,
934 "untouchable": utils.parse_boolean_from_user,
935 "validtime": int,
936 "stayofexecution": str,
937}
940def _get_suite_value(suite: Suite, conf_name: str):
941 serial_config = CUSTOM_SUITE_CONFIG_SERIALIZERS.get(conf_name)
942 if serial_config is None:
943 return getattr(suite, conf_name)
944 db_value = getattr(suite, serial_config.db_name)
945 return serial_config.serialize(db_value)
948def _set_suite_value(suite: Suite, conf_name: str, new_value):
949 serial_config = CUSTOM_SUITE_CONFIG_SERIALIZERS.get(conf_name)
950 db_name = conf_name
951 if serial_config is not None:
952 assert serial_config.deserialize is not None, (
953 "Changing %s is not supported!" % conf_name
954 )
955 new_value = serial_config.deserialize(new_value)
956 db_name = serial_config.db_name
957 setattr(suite, db_name, new_value)
960def __suite_config_get(
961 d: DBConn, args: list[str], direct_value=False, json_format=False
962) -> None:
963 die_arglen(args, 4, "E: suite-config get needs the name of a configuration")
964 session = d.session()
965 suite_name = args[2]
966 suite = get_suite_or_die(suite_name, session)
967 values = {}
968 for arg in args[3:]:
969 if arg not in ALLOWED_SUITE_CONFIGS: 969 ↛ 970line 969 didn't jump to line 970 because the condition on line 969 was never true
970 die("Unknown (or unsupported) suite configuration variable")
971 value = _get_suite_value(suite, arg)
972 if json_format: 972 ↛ 973line 972 didn't jump to line 973 because the condition on line 972 was never true
973 values[arg] = value
974 elif direct_value: 974 ↛ 975line 974 didn't jump to line 975 because the condition on line 974 was never true
975 print(value)
976 else:
977 print("%s=%s" % (arg, value))
978 if json_format: 978 ↛ 979line 978 didn't jump to line 979 because the condition on line 978 was never true
979 print(json.dumps(values, indent=2, sort_keys=True))
982def __suite_config_set(d: DBConn, args: list[str]) -> None:
983 die_arglen(args, 4, "E: suite-config set needs the name of a configuration")
984 session = d.session()
985 suite_name = args[2]
986 suite = get_suite_or_die(suite_name, session)
987 args_as_kvpairs = __suite_config_set_confing_args_as_dict(args[3:])
988 __suite_config_internal_set(
989 suite, suite_name, args_as_kvpairs, print_config_set=True
990 )
991 if dryrun:
992 session.rollback()
993 print()
994 print("This was a dryrun; changes have been rolled back")
995 else:
996 session.commit()
999def __suite_config_set_confing_args_as_dict(args: list[str]) -> dict[str, str]:
1000 # Use OrderedDict to preserve order (makes "dak admin suite-config set ..."
1001 # less confusing when things are processed in the input order)
1002 kvpairs = collections.OrderedDict()
1003 for arg in args:
1004 if "=" not in arg: 1004 ↛ 1005line 1004 didn't jump to line 1005 because the condition on line 1004 was never true
1005 die("Missing value for configuration %s: Use key=value format" % arg)
1006 conf_name, new_value_str = arg.split("=", 1)
1007 kvpairs[conf_name] = new_value_str
1008 return kvpairs
1011def __suite_config_internal_set(
1012 suite: Suite, suite_name: str, kvpairs: dict[str, str], print_config_set=True
1013) -> None:
1014 for kvpair in kvpairs.items():
1015 conf_name, new_value_str = kvpair
1016 cli_parser = ALLOWED_SUITE_CONFIGS.get(conf_name)
1017 if cli_parser is None: 1017 ↛ 1018line 1017 didn't jump to line 1018 because the condition on line 1017 was never true
1018 die("Unknown (or unsupported) suite configuration variable")
1019 if cli_parser in (SUITE_CONFIG_READ_ONLY, SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON): 1019 ↛ 1020line 1019 didn't jump to line 1020 because the condition on line 1019 was never true
1020 if cli_parser == SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON:
1021 msg = (
1022 "Cannot parse value for %s"
1023 """ - set via echo '{"%s": <...>}' | dak suite-config set-json %s instead"""
1024 )
1025 warn(msg % (conf_name, conf_name, suite_name))
1026 die("Cannot change %s from the command line" % conf_name)
1027 assert callable(cli_parser)
1028 try:
1029 new_value = cli_parser(new_value_str)
1030 except (RuntimeError, ValueError, TypeError):
1031 warn(
1032 "Could not parse new value for %s (given: %s)"
1033 % (conf_name, new_value_str)
1034 )
1035 raise
1036 try:
1037 _set_suite_value(suite, conf_name, new_value)
1038 except (RuntimeError, ValueError, TypeError):
1039 warn("Could not set new value for %s (given: %s)" % (conf_name, new_value))
1040 raise
1041 if print_config_set:
1042 print("%s=%s" % (conf_name, _get_suite_value(suite, conf_name)))
1045def __suite_config_set_json(d: DBConn, args: list[str]) -> None:
1046 session = d.session()
1047 suite_name = args[2]
1048 suite = get_suite_or_die(suite_name, session)
1049 filename = "-"
1050 if len(args) > 3:
1051 if len(args) > 4:
1052 warn("W: Ignoring extra argument after the json file name")
1053 filename = args[3]
1054 if filename != "-":
1055 with open(filename) as fd:
1056 update_config = json.load(fd)
1057 else:
1058 update_config = json.load(sys.stdin)
1059 if update_config is None or not isinstance(update_config, dict):
1060 die(
1061 "E: suite-config set-json expects a dictionary (json object), got %s"
1062 % type(update_config)
1063 )
1065 for conf_name in sorted(update_config):
1066 new_value = update_config[conf_name]
1067 cli_parser = ALLOWED_SUITE_CONFIGS.get(conf_name)
1068 if cli_parser is None:
1069 die("Unknown (or unsupported) suite configuration variable: %s" % conf_name)
1070 if cli_parser is SUITE_CONFIG_READ_ONLY:
1071 die("Cannot change %s via JSON" % conf_name)
1072 try:
1073 _set_suite_value(suite, conf_name, new_value)
1074 except (RuntimeError, ValueError, TypeError):
1075 warn("Could not set new value for %s (given: %s)" % (conf_name, new_value))
1076 raise
1077 print("%s=%s" % (conf_name, _get_suite_value(suite, conf_name)))
1078 if dryrun:
1079 session.rollback()
1080 print()
1081 print("This was a dryrun; changes have been rolled back")
1082 else:
1083 session.commit()
1086def __suite_config_list(d: DBConn, args: list[str], json_format=False) -> None:
1087 suite = None
1088 session = d.session()
1089 if len(args) > 3: 1089 ↛ 1090line 1089 didn't jump to line 1090 because the condition on line 1089 was never true
1090 warn("W: Ignoring extra argument after the suite name")
1091 if len(args) == 3: 1091 ↛ 1095line 1091 didn't jump to line 1095 because the condition on line 1091 was always true
1092 suite_name = args[2]
1093 suite = get_suite_or_die(suite_name, session)
1094 else:
1095 if json_format:
1096 die("E: suite-config list-json requires a suite name!")
1097 print("Valid suite-config options manageable by this command:")
1098 print()
1099 values = {}
1101 for arg in sorted(ALLOWED_SUITE_CONFIGS):
1102 mode = "writable"
1103 if suite is not None: 1103 ↛ 1110line 1103 didn't jump to line 1110 because the condition on line 1103 was always true
1104 value = _get_suite_value(suite, arg)
1105 if json_format: 1105 ↛ 1106line 1105 didn't jump to line 1106 because the condition on line 1105 was never true
1106 values[arg] = value
1107 else:
1108 print("%s=%s" % (arg, value))
1109 else:
1110 converter = ALLOWED_SUITE_CONFIGS[arg]
1111 if converter is SUITE_CONFIG_READ_ONLY:
1112 mode = "read-only"
1113 elif converter is SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON:
1114 mode = "writeable (via set-json only)"
1115 print(" * %s (%s)" % (arg, mode))
1116 if json_format: 1116 ↛ 1117line 1116 didn't jump to line 1117 because the condition on line 1116 was never true
1117 print(json.dumps(values, indent=2, sort_keys=True))
1120def suite_config(args: list[str]) -> None:
1121 d = DBConn()
1123 die_arglen(args, 2, "E: suite-config needs a command")
1124 mode = args[1].lower()
1126 if mode in {"get", "get-value", "get-json"}:
1127 direct_value = False
1128 json_format = mode == "get-json"
1129 if mode == "get-value": 1129 ↛ 1130line 1129 didn't jump to line 1130 because the condition on line 1129 was never true
1130 direct_value = True
1131 if len(args) > 4:
1132 die("E: get-value must receive exactly one key to lookup")
1133 __suite_config_get(d, args, direct_value=direct_value, json_format=json_format)
1134 elif mode == "set":
1135 __suite_config_set(d, args)
1136 elif mode == "set-json": 1136 ↛ 1137line 1136 didn't jump to line 1137 because the condition on line 1136 was never true
1137 __suite_config_set_json(d, args)
1138 elif mode in {"list", "list-json"}: 1138 ↛ 1142line 1138 didn't jump to line 1142 because the condition on line 1138 was always true
1139 json_format = mode == "list-json"
1140 __suite_config_list(d, args, json_format=json_format)
1141 else:
1142 suite = get_suite(mode, d.session())
1143 if suite is not None:
1144 warn("Did you get the order of the suite and the subcommand wrong?")
1145 warn("Syntax: dak admin %s {get,set,...} <suite>" % args[0])
1146 die("E: suite-config command unknown")
1149dispatch["suite-config"] = suite_config
1150dispatch["suite-cfg"] = suite_config
1151dispatch["s-cfg"] = suite_config
1154################################################################################
1157def archive_list() -> None:
1158 session = DBConn().session()
1159 for archive in session.scalars(select(Archive).order_by(Archive.archive_name)):
1160 print(
1161 "{0} path={1} description={2} tainted={3}".format(
1162 archive.archive_name, archive.path, archive.description, archive.tainted
1163 )
1164 )
1167def archive_add(args: list[str]) -> None:
1168 (name, path, description) = args[0:3]
1170 attributes = {
1171 "archive_name": name,
1172 "path": path,
1173 "description": description,
1174 }
1176 for option in args[3:]: 1176 ↛ 1177line 1176 didn't jump to line 1177 because the loop on line 1176 never started
1177 (key, value) = option.split("=")
1178 attributes[key] = value
1180 session = DBConn().session()
1182 archive = Archive()
1183 for key, value in attributes.items():
1184 setattr(archive, key, value)
1186 session.add(archive)
1187 session.flush()
1189 if dryrun: 1189 ↛ 1190line 1189 didn't jump to line 1190 because the condition on line 1189 was never true
1190 session.rollback()
1191 else:
1192 session.commit()
1195def archive_rm(name: str) -> None:
1196 session = DBConn().session()
1197 archive = get_archive(name, session)
1198 session.delete(archive)
1199 session.flush()
1201 if dryrun:
1202 session.rollback()
1203 else:
1204 session.commit()
1207def archive_rename(oldname: str, newname: str) -> None:
1208 session = DBConn().session()
1209 archive = get_archive(oldname, session)
1210 assert archive is not None
1211 archive.archive_name = newname
1212 session.flush()
1214 if dryrun:
1215 session.rollback()
1216 else:
1217 session.commit()
1220def archive(args: list[str]) -> None:
1221 mode = args[1]
1222 if mode == "list":
1223 archive_list()
1224 elif mode == "rename": 1224 ↛ 1225line 1224 didn't jump to line 1225 because the condition on line 1224 was never true
1225 archive_rename(args[2], args[3])
1226 elif mode == "add": 1226 ↛ 1228line 1226 didn't jump to line 1228 because the condition on line 1226 was always true
1227 archive_add(args[2:])
1228 elif mode == "rm":
1229 archive_rm(args[2])
1230 else:
1231 die("E: archive command unknown")
1234dispatch["archive"] = archive
1236################################################################################
1239def __version_check_list(d: DBConn) -> None:
1240 session = d.session()
1241 for suite_name in session.scalars(
1242 select(Suite.suite_name).order_by(Suite.suite_name)
1243 ):
1244 __version_check_list_suite(d, suite_name)
1247def __version_check_list_suite(d: DBConn, suite_name: str) -> None:
1248 vcs = get_version_checks(suite_name)
1249 for vc in vcs:
1250 print("%s %s %s" % (suite_name, vc.check, vc.reference.suite_name))
1253def __version_check_add(
1254 d: DBConn, suite_name: str, check: str, reference_name: str
1255) -> None:
1256 suite = get_suite_or_die(
1257 suite_name, error_message="E: Could not find suite %(suite_name)s"
1258 )
1259 reference = get_suite_or_die(
1260 reference_name, error_message="E: Could not find reference suite %(suite_name)s"
1261 )
1263 session = d.session()
1264 vc = VersionCheck()
1265 vc.suite = suite
1266 vc.check = check
1267 vc.reference = reference
1268 session.add(vc)
1269 session.commit()
1272def __version_check_rm(
1273 d: DBConn, suite_name: str, check: str, reference_name: str
1274) -> None:
1275 suite = get_suite_or_die(
1276 suite_name, error_message="E: Could not find suite %(suite_name)s"
1277 )
1278 reference = get_suite_or_die(
1279 reference_name, error_message="E: Could not find reference suite %(suite_name)s"
1280 )
1282 session = d.session()
1283 vc = session.execute(
1284 select(VersionCheck).filter_by(suite=suite, check=check, reference=reference)
1285 ).scalar_one_or_none()
1286 if vc is None:
1287 print("W: version-check not found.")
1288 else:
1289 session.delete(vc)
1290 session.commit()
1293def version_check(args: list[str]) -> None:
1294 d = DBConn()
1296 die_arglen(args, 2, "E: version-check needs at least a command")
1297 mode = args[1].lower()
1299 if mode == "list": 1299 ↛ 1300line 1299 didn't jump to line 1300 because the condition on line 1299 was never true
1300 __version_check_list(d)
1301 elif mode == "list-suite": 1301 ↛ 1302line 1301 didn't jump to line 1302 because the condition on line 1301 was never true
1302 if len(args) != 3:
1303 die("E: version-check list-suite needs a single parameter")
1304 __version_check_list_suite(d, args[2])
1305 elif mode == "add": 1305 ↛ 1309line 1305 didn't jump to line 1309 because the condition on line 1305 was always true
1306 if len(args) != 5: 1306 ↛ 1307line 1306 didn't jump to line 1307 because the condition on line 1306 was never true
1307 die("E: version-check add needs three parameters")
1308 __version_check_add(d, args[2], args[3], args[4])
1309 elif mode == "rm":
1310 if len(args) != 5:
1311 die("E: version-check rm needs three parameters")
1312 __version_check_rm(d, args[2], args[3], args[4])
1313 else:
1314 die("E: version-check command unknown")
1317dispatch["version-check"] = version_check
1318dispatch["v-c"] = version_check
1320################################################################################
1323def show_config(args: list[str]) -> None:
1324 cnf = utils.get_conf()
1326 die_arglen(args, 2, "E: config needs at least a command")
1328 mode = args[1].lower()
1330 if mode == "db":
1331 connstr = ""
1332 if "DB::Service" in cnf:
1333 # Service mode
1334 connstr = "postgresql://service=%s" % cnf["DB::Service"]
1335 elif "DB::Host" in cnf:
1336 # TCP/IP
1337 connstr = "postgresql://%s" % cnf["DB::Host"]
1338 if "DB::Port" in cnf and cnf["DB::Port"] != "-1":
1339 connstr += ":%s" % cnf["DB::Port"]
1340 connstr += "/%s" % cnf["DB::Name"]
1341 else:
1342 # Unix Socket
1343 connstr = "postgresql:///%s" % cnf["DB::Name"]
1344 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
1345 connstr += "?port=%s" % cnf["DB::Port"]
1346 print(connstr)
1347 elif mode == "db-shell":
1348 e = []
1349 if "DB::Service" in cnf:
1350 e.append("PGSERVICE")
1351 print("PGSERVICE=%s" % cnf["DB::Service"])
1352 if "DB::Name" in cnf:
1353 e.append("PGDATABASE")
1354 print("PGDATABASE=%s" % cnf["DB::Name"])
1355 if "DB::Host" in cnf:
1356 print("PGHOST=%s" % cnf["DB::Host"])
1357 e.append("PGHOST")
1358 if "DB::Port" in cnf and cnf["DB::Port"] != "-1":
1359 print("PGPORT=%s" % cnf["DB::Port"])
1360 e.append("PGPORT")
1361 print("export " + " ".join(e))
1362 elif mode == "get":
1363 print(cnf.get(args[2]))
1364 else:
1365 session = DBConn().session()
1366 o = session.execute(select(DBConfig).filter_by(name=mode)).scalar_one_or_none()
1367 if o is None:
1368 print("W: option '%s' not set" % mode)
1369 else:
1370 print(o.value)
1373dispatch["config"] = show_config
1374dispatch["c"] = show_config
1376################################################################################
1379def show_keyring(args: list[str]) -> None:
1380 die_arglen(args, 2, "E: keyring needs at least a command")
1382 mode = args[1].lower()
1384 d = DBConn()
1386 q = select(Keyring.keyring_name).where(Keyring.active.is_(True))
1388 if mode == "list-all":
1389 pass
1390 elif mode == "list-binary":
1391 q = q.join(Keyring.acl).where(~ACL.allow_source)
1392 elif mode == "list-source":
1393 q = q.join(Keyring.acl).where(ACL.allow_source)
1394 else:
1395 die("E: keyring command unknown")
1397 for keyring_name in d.session().scalars(q):
1398 print(keyring_name)
1401def keyring_add_buildd(command: list[str]) -> None:
1402 name = command[2]
1403 arch_names = command[3:]
1405 session = DBConn().session()
1406 arches = session.scalars(
1407 select(Architecture).where(Architecture.arch_string.in_(arch_names))
1408 )
1410 acl = ACL()
1411 acl.name = "buildd-{0}".format("+".join(arch_names))
1412 acl.architectures.update(arches)
1413 acl.allow_new = True
1414 acl.allow_binary = True
1415 acl.allow_binary_only = True
1416 acl.allow_hijack = True
1417 session.add(acl)
1419 k = Keyring()
1420 k.keyring_name = name
1421 k.acl = acl
1422 k.priority = 10
1423 session.add(k)
1425 session.commit()
1428def keyring(args: list[str]) -> None:
1429 if args[1].startswith("list-"):
1430 show_keyring(args)
1431 elif args[1] == "add-buildd":
1432 keyring_add_buildd(args)
1433 else:
1434 die("E: keyring command unknown")
1437dispatch["keyring"] = keyring
1438dispatch["k"] = keyring
1440################################################################################
1443def change_component_source(
1444 transaction: daklib.archive.ArchiveTransaction,
1445 suite: Suite,
1446 component: Component,
1447 source_names: list[str],
1448) -> None:
1449 session = transaction.session
1451 overrides = session.scalars(
1452 select(Override)
1453 .where(Override.package.in_(source_names))
1454 .filter_by(suite=suite)
1455 .join(OverrideType)
1456 .filter_by(overridetype="dsc")
1457 )
1458 for override in overrides:
1459 print("Changing override for {0}".format(override.package))
1460 override.component = component
1461 session.flush()
1463 sources = session.scalars(
1464 select(DBSource)
1465 .where(DBSource.source.in_(source_names))
1466 .where(DBSource.suites.contains(suite))
1467 )
1468 for source in sources:
1469 print("Copying {0}={1}".format(source.source, source.version))
1470 transaction.copy_source(source, suite, component)
1473def change_component_binary(
1474 transaction: daklib.archive.ArchiveTransaction,
1475 suite: Suite,
1476 component: Component,
1477 binary_names: list[str],
1478) -> None:
1479 session = transaction.session
1481 overrides = session.scalars(
1482 select(Override)
1483 .where(Override.package.in_(binary_names))
1484 .filter_by(suite=suite)
1485 .join(OverrideType)
1486 .where(OverrideType.overridetype.in_(["deb", "udeb"]))
1487 )
1488 for override in overrides:
1489 print("Changing override for {0}".format(override.package))
1490 override.component = component
1491 session.flush()
1493 binaries = session.scalars(
1494 select(DBBinary)
1495 .where(DBBinary.package.in_(binary_names))
1496 .where(DBBinary.suites.contains(suite))
1497 )
1498 for binary in binaries:
1499 print(
1500 "Copying {0}={1} [{2}]".format(
1501 binary.package, binary.version, binary.architecture.arch_string
1502 )
1503 )
1504 transaction.copy_binary(binary, suite, component)
1507def change_component(args: list[str]) -> None:
1508 with daklib.archive.ArchiveTransaction() as transaction:
1509 session = transaction.session
1511 suite = get_suite_by_name(args[1], session)
1512 component = get_component_by_name(args[2], session)
1514 if args[3] == "source":
1515 change_component_source(transaction, suite, component, args[4:])
1516 elif args[3] == "binary":
1517 change_component_binary(transaction, suite, component, args[4:])
1518 else:
1519 raise Exception("Can only move source or binary, not {0}".format(args[3]))
1521 transaction.commit()
1524dispatch["change-component"] = change_component
1526################################################################################
1529def forget_signature(args: list[str]) -> None:
1530 filename = args[1]
1531 with open(filename, "rb") as fh:
1532 data = fh.read()
1534 session = DBConn().session()
1535 keyrings = get_active_keyring_files(session)
1536 signed_file = daklib.gpg.SignedFile(data, keyrings)
1537 history = SignatureHistory.from_signed_file(signed_file).query(session)
1538 if history is not None: 1538 ↛ 1542line 1538 didn't jump to line 1542 because the condition on line 1538 was always true
1539 session.delete(history)
1540 session.commit()
1541 else:
1542 print("Signature was not known to dak.")
1543 session.rollback()
1546dispatch["forget-signature"] = forget_signature
1548################################################################################
1551def main() -> None:
1552 """Perform administrative work on the dak database"""
1553 global dryrun
1554 Cnf = utils.get_conf()
1555 arguments = [
1556 ("h", "help", "Admin::Options::Help"),
1557 ("n", "dry-run", "Admin::Options::Dry-Run"),
1558 ]
1559 for i in ["help", "dry-run"]:
1560 key = "Admin::Options::%s" % i
1561 if key not in Cnf: 1561 ↛ 1559line 1561 didn't jump to line 1559 because the condition on line 1561 was always true
1562 Cnf[key] = "" # type: ignore[index]
1564 args = apt_pkg.parse_commandline(Cnf, arguments, sys.argv) # type: ignore[attr-defined]
1566 options = Cnf.subtree("Admin::Options") # type: ignore[attr-defined]
1567 if options["Help"] or len(args) < 1:
1568 usage()
1569 if options["Dry-Run"]:
1570 dryrun = True
1572 subcommand = args[0]
1574 if subcommand in dispatch: 1574 ↛ 1577line 1574 didn't jump to line 1577 because the condition on line 1574 was always true
1575 dispatch[subcommand](args)
1576 else:
1577 die("E: Unknown command")