Coverage for dak/admin.py: 45%
861 statements
« prev ^ index » next coverage.py v7.6.0, created at 2026-01-04 16:18 +0000
« prev ^ index » next coverage.py v7.6.0, created at 2026-01-04 16:18 +0000
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
30from sqlalchemy.exc import IntegrityError, SQLAlchemyError
31from sqlalchemy.orm.exc import NoResultFound
33import daklib.archive
34import daklib.gpg
35from daklib import utils
36from daklib.dbconn import (
37 ACL,
38 Architecture,
39 Archive,
40 BuildQueue,
41 Component,
42 DBBinary,
43 DBConfig,
44 DBConn,
45 DBSource,
46 Keyring,
47 Override,
48 OverrideType,
49 PolicyQueue,
50 SignatureHistory,
51 SrcFormat,
52 Suite,
53 VersionCheck,
54 get_architecture,
55 get_archive,
56 get_component,
57 get_policy_queue,
58 get_suite,
59 get_version_checks,
60 session_wrapper,
61)
63################################################################################
65dispatch = {}
66dryrun = False
68################################################################################
71def warn(msg: str) -> None:
72 print(msg, file=sys.stderr)
75def die(msg: str, exit_code: int = 1) -> NoReturn:
76 print(msg, file=sys.stderr)
77 sys.exit(exit_code)
80def die_arglen(args: Sequence, args_needed: int, msg: str) -> None:
81 if len(args) < args_needed: 81 ↛ 82line 81 didn't jump to line 82 because the condition on line 81 was never true
82 die(msg)
85def get_suite_or_die(
86 suite_name: str,
87 session=None,
88 error_message="E: Invalid/unknown suite %(suite_name)s",
89) -> Suite:
90 suite = get_suite(suite_name.lower(), session=session)
91 if suite is None: 91 ↛ 92line 91 didn't jump to line 92 because the condition on line 91 was never true
92 die(error_message % {"suite_name": suite_name})
93 return suite
96def usage(exit_code: int = 0) -> NoReturn:
97 """Perform administrative work on the dak database."""
99 print(
100 """Usage: dak admin COMMAND
101Perform administrative work on the dak database.
103 -h, --help show this help and exit.
104 -n, --dry-run don't do anything, just show what would have been done
105 (only applies to add or rm operations).
107 Commands can use a long or abbreviated form:
109 config / c:
110 c db show db config
111 c db-shell show db config in a usable form for psql
112 c NAME show option NAME as set in configuration table
114 keyring / k:
115 k list-all list all keyrings
116 k list-binary list all keyrings with a NULL source acl
117 k list-source list all keyrings with a non NULL source acl
118 k add-buildd NAME ARCH... add buildd keyring with upload permission
119 for the given architectures
121 architecture / a:
122 a list show a list of architectures
123 a rm ARCH remove an architecture (will only work if
124 no longer linked to any suites)
125 a add ARCH DESCRIPTION [SUITELIST]
126 add architecture ARCH with DESCRIPTION.
127 If SUITELIST is given, add to each of the
128 suites at the same time
130 component:
131 component list show a list of components
132 component rm COMPONENT remove a component (will only work if
133 empty)
134 component add NAME DESCRIPTION ORDERING
135 add component NAME with DESCRIPTION.
136 Ordered at ORDERING.
138 suite / s:
139 s list [--print-archive]
140 show a list of suites
141 s show SUITE show config details for a suite
142 s add SUITE VERSION [ label=LABEL ] [ description=DESCRIPTION ]
143 [ origin=ORIGIN ] [ codename=CODENAME ]
144 [ signingkey=SIGNINGKEY ] [ archive=ARCHIVE ]
145 add suite SUITE, version VERSION.
146 label, description, origin, codename
147 and signingkey are optional.
148 s rm SUITE remove a suite (will only work if empty)
150 s add-all-arches SUITE VERSION... as "s add" but adds suite-architecture
151 relationships for all architectures
152 s add-build-queue SUITE BUILD-QUEUE BUILD-QUEUE-CODENAME BUILD-QUEUE-ARCHIVE
153 add a build queue for an existing suite
155 suite-architecture / s-a:
156 s-a list show the architectures for all suites
157 s-a list-suite ARCH show the suites an ARCH is in
158 s-a list-arch SUITE show the architectures in a SUITE
159 s-a add SUITE ARCH add ARCH to suite
160 s-a rm SUITE ARCH remove ARCH from suite (will only work if
161 no packages remain for the arch in the suite)
163 suite-component / s-c:
164 s-c list show the architectures for all suites
165 s-c list-suite COMPONENT
166 show the suites a COMPONENT is in
167 s-c list-component SUITE
168 show the components in a SUITE
169 s-c add SUITE COMPONENT
170 add COMPONENT to suite
171 s-c rm SUITE COMPONENT remove component from suite (will only work if
172 no packages remain for the component in the suite)
174 suite-config / suite-cfg / s-cfg:
175 s-cfg list show the names of the configurations
176 s-cfg list SUITE show the configuration values for SUITE
177 s-cfg list-json SUITE show the configuration values for SUITE in JSON format
178 s-cfg get SUITE NAME ...
179 show the value for NAME in SUITE (format: NAME=VALUE)
180 s-cfg get-value SUITE NAME ...
181 show the value for NAME in SUITE (format: VALUE)
182 s-cfg get-json SUITE NAME ...
183 show the value for NAME in SUITE (format: JSON object)
184 s-cfg set SUITE NAME=VALUE ...
185 set NAME to VALUE in SUITE
186 s-cfg set-json SUITE
187 s-cfg set-json SUITE FILENAME
188 parse FILENAME (if absent or "-", then stdin) as JSON
189 and update all configurations listed to match the
190 value in the JSON.
191 Uses the same format as list-json or get-json outputs.
193 archive:
194 archive list list all archives
195 archive add NAME ROOT DESCRIPTION [primary-mirror=MIRROR] [tainted=1]
196 add archive NAME with path ROOT,
197 primary mirror MIRROR.
198 archive rm NAME remove archive NAME (will only work if there are
199 no files and no suites in the archive)
200 archive rename OLD NEW rename archive OLD to NEW
202 version-check / v-c:
203 v-c list show version checks for all suites
204 v-c list-suite SUITE show version checks for suite SUITE
205 v-c add SUITE CHECK REFERENCE add a version check for suite SUITE
206 v-c rm SUITE CHECK REFERENCE remove a version check
207 where
208 CHECK is one of Enhances, MustBeNewerThan, MustBeOlderThan
209 REFERENCE is another suite name
211 change-component:
212 change-component SUITE COMPONENT source SOURCE...
213 change-component SUITE COMPONENT binary BINARY...
214 Move source or binary packages to a different component by copying
215 associated files and changing the overrides.
217 forget-signature FILE: forget that we saw FILE
218"""
219 )
220 sys.exit(exit_code)
223################################################################################
226def __architecture_list(d: DBConn, args: list[str]) -> NoReturn:
227 q = d.session().query(Architecture).order_by(Architecture.arch_string)
228 for j in q.all():
229 # HACK: We should get rid of source from the arch table
230 if j.arch_string == "source":
231 continue
232 print(j.arch_string)
233 sys.exit(0)
236def __architecture_add(d: DBConn, args: list[str]) -> None:
237 die_arglen(args, 4, "E: adding an architecture requires a name and a description")
238 print("Adding architecture %s" % args[2])
239 suites = [str(x) for x in args[4:]]
240 if len(suites) > 0: 240 ↛ 241line 240 didn't jump to line 241 because the condition on line 240 was never true
241 print("Adding to suites %s" % ", ".join(suites))
242 if not dryrun: 242 ↛ 263line 242 didn't jump to line 263 because the condition on line 242 was always true
243 try:
244 s = d.session()
245 a = Architecture()
246 a.arch_string = str(args[2]).lower()
247 a.description = str(args[3])
248 s.add(a)
249 for sn in suites: 249 ↛ 250line 249 didn't jump to line 250 because the loop on line 249 never started
250 su = get_suite(sn, s)
251 if su is not None:
252 a.suites.append(su)
253 else:
254 warn("W: Cannot find suite %s" % su)
255 s.commit()
256 except IntegrityError:
257 die(
258 "E: Integrity error adding architecture %s (it probably already exists)"
259 % args[2]
260 )
261 except SQLAlchemyError as e:
262 die("E: Error adding architecture %s (%s)" % (args[2], e))
263 print("Architecture %s added" % (args[2]))
266def __architecture_rm(d: DBConn, args: list[str]) -> None:
267 die_arglen(args, 3, "E: removing an architecture requires at least a name")
268 print("Removing architecture %s" % args[2])
269 if not dryrun:
270 try:
271 s = d.session()
272 a = get_architecture(args[2].lower(), s)
273 if a is None:
274 die("E: Cannot find architecture %s" % args[2])
275 s.delete(a)
276 s.commit()
277 except IntegrityError:
278 die(
279 "E: Integrity error removing architecture %s (suite-arch entries probably still exist)"
280 % args[2]
281 )
282 except SQLAlchemyError as e:
283 die("E: Error removing architecture %s (%s)" % (args[2], e))
284 print("Architecture %s removed" % args[2])
287def architecture(args: list[str]) -> None:
288 d = DBConn()
290 die_arglen(args, 2, "E: architecture needs at least a command")
292 mode = args[1].lower()
293 if mode == "list": 293 ↛ 294line 293 didn't jump to line 294 because the condition on line 293 was never true
294 __architecture_list(d, args)
295 elif mode == "add": 295 ↛ 297line 295 didn't jump to line 297 because the condition on line 295 was always true
296 __architecture_add(d, args)
297 elif mode == "rm":
298 __architecture_rm(d, args)
299 else:
300 die("E: architecture command unknown")
303dispatch["architecture"] = architecture
304dispatch["a"] = architecture
306################################################################################
309def component_list() -> None:
310 session = DBConn().session()
311 for component in session.query(Component).order_by(Component.component_name):
312 print("{0} ordering={1}".format(component.component_name, component.ordering))
315def component_add(args: list[str]) -> None:
316 (name, description, ordering) = args[0:3]
318 attributes = dict(
319 component_name=name,
320 description=description,
321 ordering=ordering,
322 )
324 for option in args[3:]:
325 (key, value) = option.split("=")
326 attributes[key] = value
328 session = DBConn().session()
330 component = Component()
331 for key, value in attributes.items():
332 setattr(component, key, value)
334 session.add(component)
335 session.flush()
337 if dryrun:
338 session.rollback()
339 else:
340 session.commit()
343def component_rm(name: str) -> None:
344 session = DBConn().session()
345 component = get_component(name, session)
346 session.delete(component)
347 session.flush()
349 if dryrun:
350 session.rollback()
351 else:
352 session.commit()
355def component_rename(oldname: str, newname: str) -> None:
356 session = DBConn().session()
357 component = get_component(oldname, session)
358 assert component is not None
359 component.component_name = newname
360 session.flush()
362 if dryrun:
363 session.rollback()
364 else:
365 session.commit()
368def component(args: list[str]) -> None:
369 mode = args[1]
370 if mode == "list":
371 component_list()
372 elif mode == "rename":
373 component_rename(args[2], args[3])
374 elif mode == "add":
375 component_add(args[2:])
376 elif mode == "rm":
377 component_rm(args[2])
378 else:
379 die("E: component command unknown")
382dispatch["component"] = component
384################################################################################
387def __suite_list(d: DBConn, args: list[str]) -> None:
388 s = d.session()
389 for j in (
390 s.query(Suite)
391 .join(Suite.archive)
392 .order_by(Archive.archive_name, Suite.suite_name)
393 .all()
394 ):
395 if len(args) > 2 and args[2] == "--print-archive": 395 ↛ 396line 395 didn't jump to line 396 because the condition on line 395 was never true
396 print("{0} {1}".format(j.archive.archive_name, j.suite_name))
397 else:
398 print("{0}".format(j.suite_name))
401def __suite_show(d: DBConn, args: list[str]) -> None:
402 if len(args) < 2:
403 die("E: showing an suite entry requires a suite")
405 su = get_suite_or_die(args[2])
407 print(su.details())
410def __suite_add(d: DBConn, args: list[str], addallarches=False) -> None:
411 die_arglen(args, 4, "E: adding a suite requires at least a name and a version")
412 suite_name = args[2].lower()
413 version = args[3] or None
414 kvpairs = __suite_config_set_confing_args_as_dict(args[4:])
416 print("Adding suite %s" % suite_name)
417 if not dryrun: 417 ↛ 459line 417 didn't jump to line 459 because the condition on line 417 was always true
418 try:
419 s = d.session()
420 suite = Suite()
421 suite.suite_name = suite_name
422 suite.overridecodename = None
423 suite.version = version or None
424 # Most configurations will be handled by
425 # __suite_config_internal_set. However, a few are managed
426 # manually here because __suite_config_internal_set cannot
427 # handle them. Either because they are create-only or
428 # because suite-add handled them different (historically)
429 suite.codename = kvpairs.pop("codename", None)
430 signingkey = kvpairs.pop("signingkey", None)
431 if signingkey is not None: 431 ↛ 432line 431 didn't jump to line 432 because the condition on line 431 was never true
432 suite.signingkeys = [signingkey.upper()]
433 archive_name = kvpairs.pop("archive", None)
434 if archive_name is not None:
435 archive = get_archive(archive_name, s)
436 assert archive is not None
437 suite.archive = archive
438 else:
439 suite.archive = (
440 s.query(Archive)
441 .filter(
442 ~Archive.archive_name.in_(["build-queues", "new", "policy"])
443 )
444 .one()
445 )
446 __suite_config_internal_set(
447 suite, suite_name, kvpairs, print_config_set=False
448 )
449 s.add(suite)
450 suite.srcformats = s.query(SrcFormat).all()
451 s.flush()
452 except IntegrityError:
453 die(
454 "E: Integrity error adding suite %s (it probably already exists)"
455 % suite_name
456 )
457 except SQLAlchemyError as e:
458 die("E: Error adding suite %s (%s)" % (suite_name, e))
459 print("Suite %s added" % (suite_name))
461 if addallarches:
462 arches = []
463 q = s.query(Architecture).order_by(Architecture.arch_string)
464 for arch in q.all():
465 suite.architectures.append(arch)
466 arches.append(arch.arch_string)
468 print("Architectures %s added to %s" % (",".join(arches), suite_name))
470 s.commit()
473def __suite_rm(d: DBConn, args: list[str]) -> None:
474 die_arglen(args, 3, "E: removing a suite requires at least a name")
475 name = args[2]
476 print("Removing suite {0}".format(name))
477 if not dryrun:
478 try:
479 s = d.session()
480 su = get_suite_or_die(name, s)
481 s.delete(su)
482 s.commit()
483 except IntegrityError:
484 die(
485 "E: Integrity error removing suite {0} (suite-arch entries probably still exist)".format(
486 name
487 )
488 )
489 except SQLAlchemyError as e:
490 die("E: Error removing suite {0} ({1})".format(name, e))
491 print("Suite {0} removed".format(name))
494def __suite_add_build_queue(d: DBConn, args: list[str]) -> None:
495 session = d.session()
497 die_arglen(args, 6, "E: Adding a build queue needs four parameters.")
499 suite_name = args[2]
500 build_queue_name = args[3]
501 build_queue_codename = args[4]
502 build_queue_archive_name = args[5]
503 try:
504 suite = session.query(Suite).filter_by(suite_name=suite_name).one()
505 except NoResultFound:
506 die("E: Unknown suite '{0}'".format(suite_name))
507 try:
508 build_queue_archive = (
509 session.query(Archive)
510 .filter_by(archive_name=build_queue_archive_name)
511 .one()
512 )
513 except NoResultFound:
514 die("E: Unknown archive '{0}'".format(build_queue_archive_name))
516 # Create suite
517 s = Suite()
518 s.suite_name = build_queue_name
519 s.origin = suite.origin
520 s.label = suite.label
521 s.description = "buildd {0} incoming".format(suite_name)
522 s.codename = build_queue_codename
523 s.notautomatic = suite.notautomatic
524 s.overridesuite = suite.overridesuite or suite.suite_name
525 s.butautomaticupgrades = suite.butautomaticupgrades
526 s.signingkeys = suite.signingkeys
527 s.include_long_description = False
529 # Do not accept direct uploads to the build queue
530 s.accept_source_uploads = False
531 s.accept_binary_uploads = False
533 session.add(s)
535 s.archive = build_queue_archive
536 s.architectures.extend(suite.architectures)
537 s.components.extend(suite.components)
538 s.srcformats.extend(suite.srcformats)
540 session.flush()
542 bq = BuildQueue()
543 bq.queue_name = build_queue_codename
544 bq.suite = s
546 session.add(bq)
547 session.flush()
549 suite.copy_queues.append(bq)
551 session.commit()
554def suite(args: list[str]) -> None:
555 d = DBConn()
557 die_arglen(args, 2, "E: suite needs at least a command")
559 mode = args[1].lower()
561 if mode == "list":
562 __suite_list(d, args)
563 elif mode == "show": 563 ↛ 564line 563 didn't jump to line 564 because the condition on line 563 was never true
564 __suite_show(d, args)
565 elif mode == "rm": 565 ↛ 566line 565 didn't jump to line 566 because the condition on line 565 was never true
566 __suite_rm(d, args)
567 elif mode == "add":
568 __suite_add(d, args, False)
569 elif mode == "add-all-arches":
570 __suite_add(d, args, True)
571 elif mode == "add-build-queue": 571 ↛ 574line 571 didn't jump to line 574 because the condition on line 571 was always true
572 __suite_add_build_queue(d, args)
573 else:
574 die("E: suite command unknown")
577dispatch["suite"] = suite
578dispatch["s"] = suite
580################################################################################
583def __suite_architecture_list(d: DBConn, args: list[str]) -> None:
584 s = d.session()
585 for j in s.query(Suite).order_by(Suite.suite_name):
586 architectures = j.get_architectures(skipsrc=True, skipall=True)
587 print(j.suite_name + ": " + ", ".join([a.arch_string for a in architectures]))
590def __suite_architecture_listarch(d: DBConn, args: list[str]) -> None:
591 die_arglen(args, 3, "E: suite-architecture list-arch requires a suite")
592 suite = get_suite_or_die(args[2], d.session())
593 a = suite.get_architectures(skipsrc=True, skipall=True)
594 for j in a:
595 print(j.arch_string)
598def __suite_architecture_listsuite(d: DBConn, args: list[str]) -> None:
599 die_arglen(args, 3, "E: suite-architecture list-suite requires an arch")
600 architecture = get_architecture(args[2].lower(), d.session())
601 if architecture is None:
602 die("E: architecture %s is invalid" % args[2].lower())
603 for j in architecture.suites:
604 print(j.suite_name)
607def __suite_architecture_add(d: DBConn, args: list[str]) -> None:
608 if len(args) < 3: 608 ↛ 609line 608 didn't jump to line 609 because the condition on line 608 was never true
609 die("E: adding a suite-architecture entry requires a suite and arch")
611 s = d.session()
613 suite = get_suite_or_die(args[2], s)
615 for arch_name in args[3:]:
616 arch = get_architecture(arch_name.lower(), s)
617 if arch is None: 617 ↛ 618line 617 didn't jump to line 618 because the condition on line 617 was never true
618 die("E: Can't find architecture %s" % args[3].lower())
620 try:
621 suite.architectures.append(arch)
622 s.flush()
623 except IntegrityError:
624 die(
625 "E: Can't add suite-architecture entry (%s, %s) - probably already exists"
626 % (args[2].lower(), arch_name)
627 )
628 except SQLAlchemyError as e:
629 die(
630 "E: Can't add suite-architecture entry (%s, %s) - %s"
631 % (args[2].lower(), arch_name, e)
632 )
634 print(
635 "Added suite-architecture entry for %s, %s" % (args[2].lower(), arch_name)
636 )
638 if not dryrun: 638 ↛ 641line 638 didn't jump to line 641 because the condition on line 638 was always true
639 s.commit()
641 s.close()
644def __suite_architecture_rm(d: DBConn, args: list[str]) -> None:
645 if len(args) < 3:
646 die("E: removing an suite-architecture entry requires a suite and arch")
648 s = d.session()
649 if not dryrun:
650 try:
651 suite_name = args[2].lower()
652 suite = get_suite_or_die(suite_name, s)
653 arch_string = args[3].lower()
654 architecture = get_architecture(arch_string, s)
655 if architecture not in suite.architectures:
656 die(
657 "E: architecture %s not found in suite %s"
658 % (arch_string, suite_name)
659 )
660 suite.architectures.remove(architecture)
661 s.commit()
662 except IntegrityError:
663 die(
664 "E: Can't remove suite-architecture entry (%s, %s) - it's probably referenced"
665 % (args[2].lower(), args[3].lower())
666 )
667 except SQLAlchemyError as e:
668 die(
669 "E: Can't remove suite-architecture entry (%s, %s) - %s"
670 % (args[2].lower(), args[3].lower(), e)
671 )
673 print(
674 "Removed suite-architecture entry for %s, %s"
675 % (args[2].lower(), args[3].lower())
676 )
679def suite_architecture(args: list[str]) -> None:
680 d = DBConn()
682 die_arglen(args, 2, "E: suite-architecture needs at least a command")
684 mode = args[1].lower()
686 if mode == "list": 686 ↛ 687line 686 didn't jump to line 687 because the condition on line 686 was never true
687 __suite_architecture_list(d, args)
688 elif mode == "list-arch":
689 __suite_architecture_listarch(d, args)
690 elif mode == "list-suite": 690 ↛ 691line 690 didn't jump to line 691 because the condition on line 690 was never true
691 __suite_architecture_listsuite(d, args)
692 elif mode == "add": 692 ↛ 694line 692 didn't jump to line 694 because the condition on line 692 was always true
693 __suite_architecture_add(d, args)
694 elif mode == "rm":
695 __suite_architecture_rm(d, args)
696 else:
697 die("E: suite-architecture command unknown")
700dispatch["suite-architecture"] = suite_architecture
701dispatch["s-a"] = suite_architecture
703################################################################################
706def __suite_component_list(d: DBConn, args: list[str]) -> None:
707 s = d.session()
708 for j in s.query(Suite).order_by(Suite.suite_name):
709 components = j.components
710 print(j.suite_name + ": " + ", ".join([c.component_name for c in components]))
713def __suite_component_listcomponent(d: DBConn, args: list[str]) -> None:
714 die_arglen(args, 3, "E: suite-component list-component requires a suite")
715 suite = get_suite_or_die(args[2], d.session())
716 for c in suite.components:
717 print(c.component_name)
720def __suite_component_listsuite(d: DBConn, args: list[str]) -> None:
721 die_arglen(args, 3, "E: suite-component list-suite requires an component")
722 component = get_component(args[2].lower(), d.session())
723 if component is None:
724 die("E: component %s is invalid" % args[2].lower())
725 for s in component.suites:
726 print(s.suite_name)
729def __suite_component_add(d: DBConn, args: list[str]) -> None:
730 if len(args) < 3: 730 ↛ 731line 730 didn't jump to line 731 because the condition on line 730 was never true
731 die("E: adding a suite-component entry requires a suite and component")
733 s = d.session()
735 suite = get_suite_or_die(args[2], s)
737 for component_name in args[3:]:
738 component = get_component(component_name.lower(), s)
739 if component is None: 739 ↛ 740line 739 didn't jump to line 740 because the condition on line 739 was never true
740 die("E: Can't find component %s" % args[3].lower())
742 try:
743 suite.components.append(component)
744 s.flush()
745 except IntegrityError:
746 die(
747 "E: Can't add suite-component entry (%s, %s) - probably already exists"
748 % (args[2].lower(), component_name)
749 )
750 except SQLAlchemyError as e:
751 die(
752 "E: Can't add suite-component entry (%s, %s) - %s"
753 % (args[2].lower(), component_name, e)
754 )
756 print(
757 "Added suite-component entry for %s, %s" % (args[2].lower(), component_name)
758 )
760 if not dryrun: 760 ↛ 762line 760 didn't jump to line 762 because the condition on line 760 was always true
761 s.commit()
762 s.close()
765def __suite_component_rm(d: DBConn, args: list[str]) -> None:
766 if len(args) < 3:
767 die("E: removing an suite-component entry requires a suite and component")
769 s = d.session()
770 if not dryrun:
771 try:
772 suite_name = args[2].lower()
773 suite = get_suite_or_die(suite_name, s)
774 component_string = args[3].lower()
775 component = get_component(component_string, s)
776 if component not in suite.components:
777 die(
778 "E: component %s not found in suite %s"
779 % (component_string, suite_name)
780 )
781 suite.components.remove(component)
782 s.commit()
783 except IntegrityError:
784 die(
785 "E: Can't remove suite-component entry (%s, %s) - it's probably referenced"
786 % (args[2].lower(), args[3].lower())
787 )
788 except SQLAlchemyError as e:
789 die(
790 "E: Can't remove suite-component entry (%s, %s) - %s"
791 % (args[2].lower(), args[3].lower(), e)
792 )
794 print(
795 "Removed suite-component entry for %s, %s" % (args[2].lower(), args[3].lower())
796 )
799def suite_component(args: list[str]) -> None:
800 d = DBConn()
802 die_arglen(args, 2, "E: suite-component needs at least a command")
804 mode = args[1].lower()
806 if mode == "list": 806 ↛ 807line 806 didn't jump to line 807 because the condition on line 806 was never true
807 __suite_component_list(d, args)
808 elif mode == "list-component": 808 ↛ 809line 808 didn't jump to line 809 because the condition on line 808 was never true
809 __suite_component_listcomponent(d, args)
810 elif mode == "list-suite": 810 ↛ 811line 810 didn't jump to line 811 because the condition on line 810 was never true
811 __suite_component_listsuite(d, args)
812 elif mode == "add": 812 ↛ 817line 812 didn't jump to line 817 because the condition on line 812 was always true
813 __suite_component_add(d, args)
814 # elif mode == 'rm':
815 # __suite_architecture_rm(d, args)
816 else:
817 die("E: suite-component command unknown")
820dispatch["suite-component"] = suite_component
821dispatch["s-c"] = suite_component
824################################################################################
826# Sentinel for detecting read-only configurations
827SUITE_CONFIG_READ_ONLY = object()
828SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON = object()
831@dataclass
832class SuiteConfigSerializer:
833 db_name: str
834 serialize: Callable[[Any], str | None]
835 deserialize: Optional[Callable[[str | None], Any]]
838def _serialize_suite(x: int | None) -> str | None:
839 if x is None: 839 ↛ 841line 839 didn't jump to line 841 because the condition on line 839 was always true
840 return None
841 return Suite.get(x).suite_name
844def _deserialize_suite(x: str | None) -> int | None:
845 if x is None: 845 ↛ 846line 845 didn't jump to line 846 because the condition on line 845 was never true
846 return None
847 return get_suite_or_die(x).suite_id
850@session_wrapper
851def _serialize_policy_queue(x, session=None):
852 if x is None: 852 ↛ 854line 852 didn't jump to line 854 because the condition on line 852 was always true
853 return None
854 try:
855 policy_obj = session.query(PolicyQueue).filter_by(policy_queue_id=x).one()
856 except NoResultFound:
857 return None
858 return policy_obj.queue_name
861def _deserialize_policy_queue(x):
862 if x is None:
863 return None
864 policy_queue = get_policy_queue(x)
865 if policy_queue is None:
866 raise ValueError("There is no policy queue with name %s" % x)
867 return policy_queue.policy_queue_id
870@session_wrapper
871def _serialize_archive(x, session=None):
872 if x is None: 872 ↛ 873line 872 didn't jump to line 873 because the condition on line 872 was never true
873 return None
874 try:
875 archive_obj = session.query(Archive).filter_by(archive_id=x).one()
876 except NoResultFound:
877 return None
878 return archive_obj.archive_name
881CUSTOM_SUITE_CONFIG_SERIALIZERS = {
882 "archive": SuiteConfigSerializer(
883 db_name="archive_id", serialize=_serialize_archive, deserialize=None
884 ),
885 "debugsuite": SuiteConfigSerializer(
886 db_name="debugsuite_id",
887 serialize=_serialize_suite,
888 deserialize=_deserialize_suite,
889 ),
890 "new_queue": SuiteConfigSerializer(
891 db_name="new_queue_id",
892 serialize=_serialize_policy_queue,
893 deserialize=_deserialize_policy_queue,
894 ),
895 "policy_queue": SuiteConfigSerializer(
896 db_name="policy_queue_id",
897 serialize=_serialize_policy_queue,
898 deserialize=_deserialize_policy_queue,
899 ),
900}
903ALLOWED_SUITE_CONFIGS: dict[str, Callable[[str], object] | object] = {
904 "accept_binary_uploads": utils.parse_boolean_from_user,
905 "accept_source_uploads": utils.parse_boolean_from_user,
906 "allowcsset": utils.parse_boolean_from_user,
907 "announce": SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON,
908 "archive": SUITE_CONFIG_READ_ONLY,
909 "butautomaticupgrades": utils.parse_boolean_from_user,
910 "byhash": utils.parse_boolean_from_user,
911 "changelog": str,
912 "changelog_url": str,
913 "checksums": SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON,
914 "close_bugs": utils.parse_boolean_from_user,
915 "codename": SUITE_CONFIG_READ_ONLY,
916 "debugsuite": str,
917 "description": str,
918 "include_long_description": utils.parse_boolean_from_user,
919 "indices_compression": SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON,
920 "label": str,
921 "mail_whitelist": str,
922 "merged_pdiffs": utils.parse_boolean_from_user,
923 "new_queue": str,
924 "notautomatic": utils.parse_boolean_from_user,
925 "origin": str,
926 "overridecodename": str,
927 "overrideorigin": str,
928 "overrideprocess": utils.parse_boolean_from_user,
929 "overridesuite": str,
930 "policy_queue": str,
931 "priority": int,
932 "separate_contents_architecture_all": utils.parse_boolean_from_user,
933 # We do not support separate Packages-all, so do not let people set it.
934 "separate_packages_architecture_all": SUITE_CONFIG_READ_ONLY,
935 "signingkeys": SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON,
936 "suite_name": SUITE_CONFIG_READ_ONLY,
937 "untouchable": utils.parse_boolean_from_user,
938 "validtime": int,
939 "stayofexecution": str,
940}
943def _get_suite_value(suite: Suite, conf_name: str):
944 serial_config = CUSTOM_SUITE_CONFIG_SERIALIZERS.get(conf_name)
945 if serial_config is None:
946 return getattr(suite, conf_name)
947 db_value = getattr(suite, serial_config.db_name)
948 return serial_config.serialize(db_value)
951def _set_suite_value(suite: Suite, conf_name: str, new_value):
952 serial_config = CUSTOM_SUITE_CONFIG_SERIALIZERS.get(conf_name)
953 db_name = conf_name
954 if serial_config is not None:
955 assert serial_config.deserialize is not None, (
956 "Changing %s is not supported!" % conf_name
957 )
958 new_value = serial_config.deserialize(new_value)
959 db_name = serial_config.db_name
960 setattr(suite, db_name, new_value)
963def __suite_config_get(
964 d: DBConn, args: list[str], direct_value=False, json_format=False
965) -> None:
966 die_arglen(args, 4, "E: suite-config get needs the name of a configuration")
967 session = d.session()
968 suite_name = args[2]
969 suite = get_suite_or_die(suite_name, session)
970 values = {}
971 for arg in args[3:]:
972 if arg not in ALLOWED_SUITE_CONFIGS: 972 ↛ 973line 972 didn't jump to line 973 because the condition on line 972 was never true
973 die("Unknown (or unsupported) suite configuration variable")
974 value = _get_suite_value(suite, arg)
975 if json_format: 975 ↛ 976line 975 didn't jump to line 976 because the condition on line 975 was never true
976 values[arg] = value
977 elif direct_value: 977 ↛ 978line 977 didn't jump to line 978 because the condition on line 977 was never true
978 print(value)
979 else:
980 print("%s=%s" % (arg, value))
981 if json_format: 981 ↛ 982line 981 didn't jump to line 982 because the condition on line 981 was never true
982 print(json.dumps(values, indent=2, sort_keys=True))
985def __suite_config_set(d: DBConn, args: list[str]) -> None:
986 die_arglen(args, 4, "E: suite-config set needs the name of a configuration")
987 session = d.session()
988 suite_name = args[2]
989 suite = get_suite_or_die(suite_name, session)
990 args_as_kvpairs = __suite_config_set_confing_args_as_dict(args[3:])
991 __suite_config_internal_set(
992 suite, suite_name, args_as_kvpairs, print_config_set=True
993 )
994 if dryrun:
995 session.rollback()
996 print()
997 print("This was a dryrun; changes have been rolled back")
998 else:
999 session.commit()
1002def __suite_config_set_confing_args_as_dict(args: list[str]) -> dict[str, str]:
1003 # Use OrderedDict to preserve order (makes "dak admin suite-config set ..."
1004 # less confusing when things are processed in the input order)
1005 kvpairs = collections.OrderedDict()
1006 for arg in args:
1007 if "=" not in arg: 1007 ↛ 1008line 1007 didn't jump to line 1008 because the condition on line 1007 was never true
1008 die("Missing value for configuration %s: Use key=value format" % arg)
1009 conf_name, new_value_str = arg.split("=", 1)
1010 kvpairs[conf_name] = new_value_str
1011 return kvpairs
1014def __suite_config_internal_set(
1015 suite: Suite, suite_name: str, kvpairs: dict[str, str], print_config_set=True
1016) -> None:
1017 for kvpair in kvpairs.items():
1018 conf_name, new_value_str = kvpair
1019 cli_parser = ALLOWED_SUITE_CONFIGS.get(conf_name)
1020 if cli_parser is None: 1020 ↛ 1021line 1020 didn't jump to line 1021 because the condition on line 1020 was never true
1021 die("Unknown (or unsupported) suite configuration variable")
1022 if cli_parser in (SUITE_CONFIG_READ_ONLY, SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON): 1022 ↛ 1023line 1022 didn't jump to line 1023 because the condition on line 1022 was never true
1023 if cli_parser == SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON:
1024 msg = (
1025 "Cannot parse value for %s"
1026 """ - set via echo '{"%s": <...>}' | dak suite-config set-json %s instead"""
1027 )
1028 warn(msg % (conf_name, conf_name, suite_name))
1029 die("Cannot change %s from the command line" % conf_name)
1030 assert callable(cli_parser)
1031 try:
1032 new_value = cli_parser(new_value_str)
1033 except (RuntimeError, ValueError, TypeError) as e:
1034 warn(
1035 "Could not parse new value for %s (given: %s)"
1036 % (conf_name, new_value_str)
1037 )
1038 raise e
1039 try:
1040 _set_suite_value(suite, conf_name, new_value)
1041 except (RuntimeError, ValueError, TypeError) as e:
1042 warn("Could not set new value for %s (given: %s)" % (conf_name, new_value))
1043 raise e
1044 if print_config_set:
1045 print("%s=%s" % (conf_name, _get_suite_value(suite, conf_name)))
1048def __suite_config_set_json(d: DBConn, args: list[str]) -> None:
1049 session = d.session()
1050 suite_name = args[2]
1051 suite = get_suite_or_die(suite_name, session)
1052 filename = "-"
1053 if len(args) > 3:
1054 if len(args) > 4:
1055 warn("W: Ignoring extra argument after the json file name")
1056 filename = args[3]
1057 if filename != "-":
1058 with open(filename) as fd:
1059 update_config = json.load(fd)
1060 else:
1061 update_config = json.load(sys.stdin)
1062 if update_config is None or not isinstance(update_config, dict):
1063 die(
1064 "E: suite-config set-json expects a dictionary (json object), got %s"
1065 % type(update_config)
1066 )
1068 for conf_name in sorted(update_config):
1069 new_value = update_config[conf_name]
1070 cli_parser = ALLOWED_SUITE_CONFIGS.get(conf_name)
1071 if cli_parser is None:
1072 die("Unknown (or unsupported) suite configuration variable: %s" % conf_name)
1073 if cli_parser is SUITE_CONFIG_READ_ONLY:
1074 die("Cannot change %s via JSON" % conf_name)
1075 try:
1076 _set_suite_value(suite, conf_name, new_value)
1077 except (RuntimeError, ValueError, TypeError) as e:
1078 warn("Could not set new value for %s (given: %s)" % (conf_name, new_value))
1079 raise e
1080 print("%s=%s" % (conf_name, _get_suite_value(suite, conf_name)))
1081 if dryrun:
1082 session.rollback()
1083 print()
1084 print("This was a dryrun; changes have been rolled back")
1085 else:
1086 session.commit()
1089def __suite_config_list(d: DBConn, args: list[str], json_format=False) -> None:
1090 suite = None
1091 session = d.session()
1092 if len(args) > 3: 1092 ↛ 1093line 1092 didn't jump to line 1093 because the condition on line 1092 was never true
1093 warn("W: Ignoring extra argument after the suite name")
1094 if len(args) == 3: 1094 ↛ 1098line 1094 didn't jump to line 1098 because the condition on line 1094 was always true
1095 suite_name = args[2]
1096 suite = get_suite_or_die(suite_name, session)
1097 else:
1098 if json_format:
1099 die("E: suite-config list-json requires a suite name!")
1100 print("Valid suite-config options manageable by this command:")
1101 print()
1102 values = {}
1104 for arg in sorted(ALLOWED_SUITE_CONFIGS):
1105 mode = "writable"
1106 if suite is not None: 1106 ↛ 1113line 1106 didn't jump to line 1113 because the condition on line 1106 was always true
1107 value = _get_suite_value(suite, arg)
1108 if json_format: 1108 ↛ 1109line 1108 didn't jump to line 1109 because the condition on line 1108 was never true
1109 values[arg] = value
1110 else:
1111 print("%s=%s" % (arg, value))
1112 else:
1113 converter = ALLOWED_SUITE_CONFIGS[arg]
1114 if converter is SUITE_CONFIG_READ_ONLY:
1115 mode = "read-only"
1116 elif converter is SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON:
1117 mode = "writeable (via set-json only)"
1118 print(" * %s (%s)" % (arg, mode))
1119 if json_format: 1119 ↛ 1120line 1119 didn't jump to line 1120 because the condition on line 1119 was never true
1120 print(json.dumps(values, indent=2, sort_keys=True))
1123def suite_config(args: list[str]) -> None:
1124 d = DBConn()
1126 die_arglen(args, 2, "E: suite-config needs a command")
1127 mode = args[1].lower()
1129 if mode in {"get", "get-value", "get-json"}:
1130 direct_value = False
1131 json_format = mode == "get-json"
1132 if mode == "get-value": 1132 ↛ 1133line 1132 didn't jump to line 1133 because the condition on line 1132 was never true
1133 direct_value = True
1134 if len(args) > 4:
1135 die("E: get-value must receive exactly one key to lookup")
1136 __suite_config_get(d, args, direct_value=direct_value, json_format=json_format)
1137 elif mode == "set":
1138 __suite_config_set(d, args)
1139 elif mode == "set-json": 1139 ↛ 1140line 1139 didn't jump to line 1140 because the condition on line 1139 was never true
1140 __suite_config_set_json(d, args)
1141 elif mode in {"list", "list-json"}: 1141 ↛ 1145line 1141 didn't jump to line 1145 because the condition on line 1141 was always true
1142 json_format = mode == "list-json"
1143 __suite_config_list(d, args, json_format=json_format)
1144 else:
1145 suite = get_suite(mode, d.session())
1146 if suite is not None:
1147 warn("Did you get the order of the suite and the subcommand wrong?")
1148 warn("Syntax: dak admin %s {get,set,...} <suite>" % args[0])
1149 die("E: suite-config command unknown")
1152dispatch["suite-config"] = suite_config
1153dispatch["suite-cfg"] = suite_config
1154dispatch["s-cfg"] = suite_config
1157################################################################################
1160def archive_list() -> None:
1161 session = DBConn().session()
1162 for archive in session.query(Archive).order_by(Archive.archive_name):
1163 print(
1164 "{0} path={1} description={2} tainted={3}".format(
1165 archive.archive_name, archive.path, archive.description, archive.tainted
1166 )
1167 )
1170def archive_add(args: list[str]) -> None:
1171 (name, path, description) = args[0:3]
1173 attributes = dict(
1174 archive_name=name,
1175 path=path,
1176 description=description,
1177 )
1179 for option in args[3:]: 1179 ↛ 1180line 1179 didn't jump to line 1180 because the loop on line 1179 never started
1180 (key, value) = option.split("=")
1181 attributes[key] = value
1183 session = DBConn().session()
1185 archive = Archive()
1186 for key, value in attributes.items():
1187 setattr(archive, key, value)
1189 session.add(archive)
1190 session.flush()
1192 if dryrun: 1192 ↛ 1193line 1192 didn't jump to line 1193 because the condition on line 1192 was never true
1193 session.rollback()
1194 else:
1195 session.commit()
1198def archive_rm(name: str) -> None:
1199 session = DBConn().session()
1200 archive = get_archive(name, session)
1201 session.delete(archive)
1202 session.flush()
1204 if dryrun:
1205 session.rollback()
1206 else:
1207 session.commit()
1210def archive_rename(oldname: str, newname: str) -> None:
1211 session = DBConn().session()
1212 archive = get_archive(oldname, session)
1213 assert archive is not None
1214 archive.archive_name = newname
1215 session.flush()
1217 if dryrun:
1218 session.rollback()
1219 else:
1220 session.commit()
1223def archive(args: list[str]) -> None:
1224 mode = args[1]
1225 if mode == "list":
1226 archive_list()
1227 elif mode == "rename": 1227 ↛ 1228line 1227 didn't jump to line 1228 because the condition on line 1227 was never true
1228 archive_rename(args[2], args[3])
1229 elif mode == "add": 1229 ↛ 1231line 1229 didn't jump to line 1231 because the condition on line 1229 was always true
1230 archive_add(args[2:])
1231 elif mode == "rm":
1232 archive_rm(args[2])
1233 else:
1234 die("E: archive command unknown")
1237dispatch["archive"] = archive
1239################################################################################
1242def __version_check_list(d: DBConn) -> None:
1243 session = d.session()
1244 for s in session.query(Suite).order_by(Suite.suite_name):
1245 __version_check_list_suite(d, s.suite_name)
1248def __version_check_list_suite(d: DBConn, suite_name: str) -> None:
1249 vcs = get_version_checks(suite_name)
1250 for vc in vcs:
1251 print("%s %s %s" % (suite_name, vc.check, vc.reference.suite_name))
1254def __version_check_add(
1255 d: DBConn, suite_name: str, check: str, reference_name: str
1256) -> None:
1257 suite = get_suite_or_die(
1258 suite_name, error_message="E: Could not find suite %(suite_name)s"
1259 )
1260 reference = get_suite_or_die(
1261 reference_name, error_message="E: Could not find reference suite %(suite_name)s"
1262 )
1264 session = d.session()
1265 vc = VersionCheck()
1266 vc.suite = suite
1267 vc.check = check
1268 vc.reference = reference
1269 session.add(vc)
1270 session.commit()
1273def __version_check_rm(
1274 d: DBConn, suite_name: str, check: str, reference_name: str
1275) -> None:
1276 suite = get_suite_or_die(
1277 suite_name, error_message="E: Could not find suite %(suite_name)s"
1278 )
1279 reference = get_suite_or_die(
1280 reference_name, error_message="E: Could not find reference suite %(suite_name)s"
1281 )
1283 session = d.session()
1284 try:
1285 vc = (
1286 session.query(VersionCheck)
1287 .filter_by(suite=suite, check=check, reference=reference)
1288 .one()
1289 )
1290 session.delete(vc)
1291 session.commit()
1292 except NoResultFound:
1293 print("W: version-check not found.")
1296def version_check(args: list[str]) -> None:
1297 d = DBConn()
1299 die_arglen(args, 2, "E: version-check needs at least a command")
1300 mode = args[1].lower()
1302 if mode == "list": 1302 ↛ 1303line 1302 didn't jump to line 1303 because the condition on line 1302 was never true
1303 __version_check_list(d)
1304 elif mode == "list-suite": 1304 ↛ 1305line 1304 didn't jump to line 1305 because the condition on line 1304 was never true
1305 if len(args) != 3:
1306 die("E: version-check list-suite needs a single parameter")
1307 __version_check_list_suite(d, args[2])
1308 elif mode == "add": 1308 ↛ 1312line 1308 didn't jump to line 1312 because the condition on line 1308 was always true
1309 if len(args) != 5: 1309 ↛ 1310line 1309 didn't jump to line 1310 because the condition on line 1309 was never true
1310 die("E: version-check add needs three parameters")
1311 __version_check_add(d, args[2], args[3], args[4])
1312 elif mode == "rm":
1313 if len(args) != 5:
1314 die("E: version-check rm needs three parameters")
1315 __version_check_rm(d, args[2], args[3], args[4])
1316 else:
1317 die("E: version-check command unknown")
1320dispatch["version-check"] = version_check
1321dispatch["v-c"] = version_check
1323################################################################################
1326def show_config(args: list[str]) -> None:
1327 cnf = utils.get_conf()
1329 die_arglen(args, 2, "E: config needs at least a command")
1331 mode = args[1].lower()
1333 if mode == "db":
1334 connstr = ""
1335 if "DB::Service" in cnf:
1336 # Service mode
1337 connstr = "postgresql://service=%s" % cnf["DB::Service"]
1338 elif "DB::Host" in cnf:
1339 # TCP/IP
1340 connstr = "postgresql://%s" % cnf["DB::Host"]
1341 if "DB::Port" in cnf and cnf["DB::Port"] != "-1":
1342 connstr += ":%s" % cnf["DB::Port"]
1343 connstr += "/%s" % cnf["DB::Name"]
1344 else:
1345 # Unix Socket
1346 connstr = "postgresql:///%s" % cnf["DB::Name"]
1347 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
1348 connstr += "?port=%s" % cnf["DB::Port"]
1349 print(connstr)
1350 elif mode == "db-shell":
1351 e = []
1352 if "DB::Service" in cnf:
1353 e.append("PGSERVICE")
1354 print("PGSERVICE=%s" % cnf["DB::Service"])
1355 if "DB::Name" in cnf:
1356 e.append("PGDATABASE")
1357 print("PGDATABASE=%s" % cnf["DB::Name"])
1358 if "DB::Host" in cnf:
1359 print("PGHOST=%s" % cnf["DB::Host"])
1360 e.append("PGHOST")
1361 if "DB::Port" in cnf and cnf["DB::Port"] != "-1":
1362 print("PGPORT=%s" % cnf["DB::Port"])
1363 e.append("PGPORT")
1364 print("export " + " ".join(e))
1365 elif mode == "get":
1366 print(cnf.get(args[2]))
1367 else:
1368 session = DBConn().session()
1369 try:
1370 o = session.query(DBConfig).filter_by(name=mode).one()
1371 print(o.value)
1372 except NoResultFound:
1373 print("W: option '%s' not set" % mode)
1376dispatch["config"] = show_config
1377dispatch["c"] = show_config
1379################################################################################
1382def show_keyring(args: list[str]) -> None:
1383 die_arglen(args, 2, "E: keyring needs at least a command")
1385 mode = args[1].lower()
1387 d = DBConn()
1389 q = d.session().query(Keyring).filter(Keyring.active == True) # noqa:E712
1391 if mode == "list-all":
1392 pass
1393 elif mode == "list-binary":
1394 q = q.join(Keyring.acl).filter(ACL.allow_source == False) # noqa:E712
1395 elif mode == "list-source":
1396 q = q.join(Keyring.acl).filter(ACL.allow_source == True) # noqa:E712
1397 else:
1398 die("E: keyring command unknown")
1400 for k in q.all():
1401 print(k.keyring_name)
1404def keyring_add_buildd(command: list[str]) -> None:
1405 name = command[2]
1406 arch_names = command[3:]
1408 session = DBConn().session()
1409 arches = session.query(Architecture).filter(
1410 Architecture.arch_string.in_(arch_names)
1411 )
1413 acl = ACL()
1414 acl.name = "buildd-{0}".format("+".join(arch_names))
1415 acl.architectures.update(arches)
1416 acl.allow_new = True
1417 acl.allow_binary = True
1418 acl.allow_binary_only = True
1419 acl.allow_hijack = True
1420 session.add(acl)
1422 k = Keyring()
1423 k.keyring_name = name
1424 k.acl = acl
1425 k.priority = 10
1426 session.add(k)
1428 session.commit()
1431def keyring(args: list[str]) -> None:
1432 if args[1].startswith("list-"):
1433 show_keyring(args)
1434 elif args[1] == "add-buildd":
1435 keyring_add_buildd(args)
1436 else:
1437 die("E: keyring command unknown")
1440dispatch["keyring"] = keyring
1441dispatch["k"] = keyring
1443################################################################################
1446def change_component_source(
1447 transaction: daklib.archive.ArchiveTransaction,
1448 suite: Suite,
1449 component: Component,
1450 source_names: list[str],
1451) -> None:
1452 session = transaction.session
1454 overrides = (
1455 session.query(Override)
1456 .filter(Override.package.in_(source_names))
1457 .filter_by(suite=suite)
1458 .join(OverrideType)
1459 .filter_by(overridetype="dsc")
1460 )
1461 for override in overrides:
1462 print("Changing override for {0}".format(override.package))
1463 override.component = component
1464 session.flush()
1466 sources = (
1467 session.query(DBSource)
1468 .filter(DBSource.source.in_(source_names))
1469 .filter(DBSource.suites.contains(suite))
1470 )
1471 for source in sources:
1472 print("Copying {0}={1}".format(source.source, source.version))
1473 transaction.copy_source(source, suite, component)
1476def change_component_binary(
1477 transaction: daklib.archive.ArchiveTransaction,
1478 suite: Suite,
1479 component: Component,
1480 binary_names: list[str],
1481) -> None:
1482 session = transaction.session
1484 overrides = (
1485 session.query(Override)
1486 .filter(Override.package.in_(binary_names))
1487 .filter_by(suite=suite)
1488 .join(OverrideType)
1489 .filter(OverrideType.overridetype.in_(["deb", "udeb"]))
1490 )
1491 for override in overrides:
1492 print("Changing override for {0}".format(override.package))
1493 override.component = component
1494 session.flush()
1496 binaries = (
1497 session.query(DBBinary)
1498 .filter(DBBinary.package.in_(binary_names))
1499 .filter(DBBinary.suites.contains(suite))
1500 )
1501 for binary in binaries:
1502 print(
1503 "Copying {0}={1} [{2}]".format(
1504 binary.package, binary.version, binary.architecture.arch_string
1505 )
1506 )
1507 transaction.copy_binary(binary, suite, component)
1508 pass
1511def change_component(args: list[str]) -> None:
1512 with daklib.archive.ArchiveTransaction() as transaction:
1513 session = transaction.session
1515 suite = session.query(Suite).filter_by(suite_name=args[1]).one()
1516 component = session.query(Component).filter_by(component_name=args[2]).one()
1518 if args[3] == "source":
1519 change_component_source(transaction, suite, component, args[4:])
1520 elif args[3] == "binary":
1521 change_component_binary(transaction, suite, component, args[4:])
1522 else:
1523 raise Exception("Can only move source or binary, not {0}".format(args[3]))
1525 transaction.commit()
1528dispatch["change-component"] = change_component
1530################################################################################
1533def forget_signature(args: list[str]) -> None:
1534 filename = args[1]
1535 with open(filename, "rb") as fh:
1536 data = fh.read()
1538 session = DBConn().session()
1539 keyrings = [
1540 k.keyring_name
1541 for k in session.query(Keyring)
1542 .filter_by(active=True)
1543 .order_by(Keyring.priority)
1544 ]
1545 signed_file = daklib.gpg.SignedFile(data, keyrings)
1546 history = SignatureHistory.from_signed_file(signed_file).query(session)
1547 if history is not None: 1547 ↛ 1551line 1547 didn't jump to line 1551 because the condition on line 1547 was always true
1548 session.delete(history)
1549 session.commit()
1550 else:
1551 print("Signature was not known to dak.")
1552 session.rollback()
1555dispatch["forget-signature"] = forget_signature
1557################################################################################
1560def main() -> None:
1561 """Perform administrative work on the dak database"""
1562 global dryrun
1563 Cnf = utils.get_conf()
1564 arguments = [
1565 ("h", "help", "Admin::Options::Help"),
1566 ("n", "dry-run", "Admin::Options::Dry-Run"),
1567 ]
1568 for i in ["help", "dry-run"]:
1569 key = "Admin::Options::%s" % i
1570 if key not in Cnf: 1570 ↛ 1568line 1570 didn't jump to line 1568 because the condition on line 1570 was always true
1571 Cnf[key] = "" # type: ignore[index]
1573 args = apt_pkg.parse_commandline(Cnf, arguments, sys.argv) # type: ignore[attr-defined]
1575 options = Cnf.subtree("Admin::Options") # type: ignore[attr-defined]
1576 if options["Help"] or len(args) < 1:
1577 usage()
1578 if options["Dry-Run"]:
1579 dryrun = True
1581 subcommand = args[0]
1583 if subcommand in dispatch: 1583 ↛ 1586line 1583 didn't jump to line 1586 because the condition on line 1583 was always true
1584 dispatch[subcommand](args)
1585 else:
1586 die("E: Unknown command")
1589################################################################################
1592if __name__ == "__main__":
1593 main()