Coverage for dak/admin.py: 45%

848 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2026-08-03 16:46 +0000

1"""Configure dak parameters in the database""" 

2 

3# Copyright (C) 2009 Mark Hymers <mhy@debian.org> 

4 

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. 

9 

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. 

14 

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 

18 

19################################################################################ 

20 

21import collections 

22import json 

23import sys 

24from collections.abc import Callable, Sequence 

25from dataclasses import dataclass 

26from typing import Any, NoReturn 

27 

28import apt_pkg 

29from sqlalchemy import select 

30from sqlalchemy.exc import IntegrityError, SQLAlchemyError 

31 

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) 

64 

65################################################################################ 

66 

67dispatch = {} 

68dryrun = False 

69 

70################################################################################ 

71 

72 

73def warn(msg: str) -> None: 

74 print(msg, file=sys.stderr) 

75 

76 

77def die(msg: str, exit_code: int = 1) -> NoReturn: 

78 print(msg, file=sys.stderr) 

79 sys.exit(exit_code) 

80 

81 

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) 

85 

86 

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 

96 

97 

98def usage(exit_code: int = 0) -> NoReturn: 

99 """Perform administrative work on the dak database.""" 

100 

101 print( 

102 """Usage: dak admin COMMAND 

103Perform administrative work on the dak database. 

104 

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). 

108 

109 Commands can use a long or abbreviated form: 

110 

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 

115 

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 

122 

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 

131 

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. 

139 

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) 

151 

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 

156 

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) 

164 

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) 

175 

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. 

194 

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 

203 

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 

212 

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. 

218 

219 forget-signature FILE: forget that we saw FILE 

220""" 

221 ) 

222 sys.exit(exit_code) 

223 

224 

225################################################################################ 

226 

227 

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) 

236 

237 

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])) 

266 

267 

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]) 

287 

288 

289def architecture(args: list[str]) -> None: 

290 d = DBConn() 

291 

292 die_arglen(args, 2, "E: architecture needs at least a command") 

293 

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") 

303 

304 

305dispatch["architecture"] = architecture 

306dispatch["a"] = architecture 

307 

308################################################################################ 

309 

310 

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)) 

319 

320 

321def component_add(args: list[str]) -> None: 

322 (name, description, ordering) = args[0:3] 

323 

324 attributes = { 

325 "component_name": name, 

326 "description": description, 

327 "ordering": ordering, 

328 } 

329 

330 for option in args[3:]: 

331 (key, value) = option.split("=") 

332 attributes[key] = value 

333 

334 session = DBConn().session() 

335 

336 component = Component() 

337 for key, value in attributes.items(): 

338 setattr(component, key, value) 

339 

340 session.add(component) 

341 session.flush() 

342 

343 if dryrun: 

344 session.rollback() 

345 else: 

346 session.commit() 

347 

348 

349def component_rm(name: str) -> None: 

350 session = DBConn().session() 

351 component = get_component(name, session) 

352 session.delete(component) 

353 session.flush() 

354 

355 if dryrun: 

356 session.rollback() 

357 else: 

358 session.commit() 

359 

360 

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() 

367 

368 if dryrun: 

369 session.rollback() 

370 else: 

371 session.commit() 

372 

373 

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") 

386 

387 

388dispatch["component"] = component 

389 

390################################################################################ 

391 

392 

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)) 

404 

405 

406def __suite_show(d: DBConn, args: list[str]) -> None: 

407 if len(args) < 2: 

408 die("E: showing an suite entry requires a suite") 

409 

410 su = get_suite_or_die(args[2]) 

411 

412 print(su.details()) 

413 

414 

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:]) 

420 

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)) 

463 

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) 

470 

471 print("Architectures %s added to %s" % (",".join(arches), suite_name)) 

472 

473 s.commit() 

474 

475 

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)) 

495 

496 

497def __suite_add_build_queue(d: DBConn, args: list[str]) -> None: 

498 session = d.session() 

499 

500 die_arglen(args, 6, "E: Adding a build queue needs four parameters.") 

501 

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)) 

516 

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 

529 

530 # Do not accept direct uploads to the build queue 

531 s.accept_source_uploads = False 

532 s.accept_binary_uploads = False 

533 

534 session.add(s) 

535 

536 s.archive = build_queue_archive 

537 s.architectures.extend(suite.architectures) 

538 s.components.extend(suite.components) 

539 s.srcformats.extend(suite.srcformats) 

540 

541 session.flush() 

542 

543 bq = BuildQueue() 

544 bq.queue_name = build_queue_codename 

545 bq.suite = s 

546 

547 session.add(bq) 

548 session.flush() 

549 

550 suite.copy_queues.append(bq) 

551 

552 session.commit() 

553 

554 

555def suite(args: list[str]) -> None: 

556 d = DBConn() 

557 

558 die_arglen(args, 2, "E: suite needs at least a command") 

559 

560 mode = args[1].lower() 

561 

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") 

576 

577 

578dispatch["suite"] = suite 

579dispatch["s"] = suite 

580 

581################################################################################ 

582 

583 

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])) 

589 

590 

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) 

597 

598 

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) 

606 

607 

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") 

611 

612 s = d.session() 

613 

614 suite = get_suite_or_die(args[2], s) 

615 

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()) 

620 

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 ) 

634 

635 print( 

636 "Added suite-architecture entry for %s, %s" % (args[2].lower(), arch_name) 

637 ) 

638 

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() 

641 

642 s.close() 

643 

644 

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") 

648 

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 ) 

673 

674 print( 

675 "Removed suite-architecture entry for %s, %s" 

676 % (args[2].lower(), args[3].lower()) 

677 ) 

678 

679 

680def suite_architecture(args: list[str]) -> None: 

681 d = DBConn() 

682 

683 die_arglen(args, 2, "E: suite-architecture needs at least a command") 

684 

685 mode = args[1].lower() 

686 

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") 

699 

700 

701dispatch["suite-architecture"] = suite_architecture 

702dispatch["s-a"] = suite_architecture 

703 

704################################################################################ 

705 

706 

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])) 

712 

713 

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) 

719 

720 

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) 

728 

729 

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") 

733 

734 s = d.session() 

735 

736 suite = get_suite_or_die(args[2], s) 

737 

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()) 

742 

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 ) 

756 

757 print( 

758 "Added suite-component entry for %s, %s" % (args[2].lower(), component_name) 

759 ) 

760 

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() 

764 

765 

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") 

769 

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 ) 

794 

795 print( 

796 "Removed suite-component entry for %s, %s" % (args[2].lower(), args[3].lower()) 

797 ) 

798 

799 

800def suite_component(args: list[str]) -> None: 

801 d = DBConn() 

802 

803 die_arglen(args, 2, "E: suite-component needs at least a command") 

804 

805 mode = args[1].lower() 

806 

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") 

819 

820 

821dispatch["suite-component"] = suite_component 

822dispatch["s-c"] = suite_component 

823 

824 

825################################################################################ 

826 

827# Sentinel for detecting read-only configurations 

828SUITE_CONFIG_READ_ONLY = object() 

829SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON = object() 

830 

831 

832@dataclass 

833class SuiteConfigSerializer: 

834 db_name: str 

835 serialize: Callable[[Any], str | None] 

836 deserialize: Callable[[str | None], Any] | None 

837 

838 

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 

843 

844 

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 

849 

850 

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() 

858 

859 

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 

867 

868 

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() 

876 

877 

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} 

898 

899 

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} 

938 

939 

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) 

946 

947 

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) 

958 

959 

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)) 

980 

981 

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() 

997 

998 

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 

1009 

1010 

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))) 

1043 

1044 

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 ) 

1064 

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() 

1084 

1085 

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 = {} 

1100 

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)) 

1118 

1119 

1120def suite_config(args: list[str]) -> None: 

1121 d = DBConn() 

1122 

1123 die_arglen(args, 2, "E: suite-config needs a command") 

1124 mode = args[1].lower() 

1125 

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") 

1147 

1148 

1149dispatch["suite-config"] = suite_config 

1150dispatch["suite-cfg"] = suite_config 

1151dispatch["s-cfg"] = suite_config 

1152 

1153 

1154################################################################################ 

1155 

1156 

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 ) 

1165 

1166 

1167def archive_add(args: list[str]) -> None: 

1168 (name, path, description) = args[0:3] 

1169 

1170 attributes = { 

1171 "archive_name": name, 

1172 "path": path, 

1173 "description": description, 

1174 } 

1175 

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 

1179 

1180 session = DBConn().session() 

1181 

1182 archive = Archive() 

1183 for key, value in attributes.items(): 

1184 setattr(archive, key, value) 

1185 

1186 session.add(archive) 

1187 session.flush() 

1188 

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() 

1193 

1194 

1195def archive_rm(name: str) -> None: 

1196 session = DBConn().session() 

1197 archive = get_archive(name, session) 

1198 session.delete(archive) 

1199 session.flush() 

1200 

1201 if dryrun: 

1202 session.rollback() 

1203 else: 

1204 session.commit() 

1205 

1206 

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() 

1213 

1214 if dryrun: 

1215 session.rollback() 

1216 else: 

1217 session.commit() 

1218 

1219 

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") 

1232 

1233 

1234dispatch["archive"] = archive 

1235 

1236################################################################################ 

1237 

1238 

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) 

1245 

1246 

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)) 

1251 

1252 

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 ) 

1262 

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() 

1270 

1271 

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 ) 

1281 

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() 

1291 

1292 

1293def version_check(args: list[str]) -> None: 

1294 d = DBConn() 

1295 

1296 die_arglen(args, 2, "E: version-check needs at least a command") 

1297 mode = args[1].lower() 

1298 

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") 

1315 

1316 

1317dispatch["version-check"] = version_check 

1318dispatch["v-c"] = version_check 

1319 

1320################################################################################ 

1321 

1322 

1323def show_config(args: list[str]) -> None: 

1324 cnf = utils.get_conf() 

1325 

1326 die_arglen(args, 2, "E: config needs at least a command") 

1327 

1328 mode = args[1].lower() 

1329 

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) 

1371 

1372 

1373dispatch["config"] = show_config 

1374dispatch["c"] = show_config 

1375 

1376################################################################################ 

1377 

1378 

1379def show_keyring(args: list[str]) -> None: 

1380 die_arglen(args, 2, "E: keyring needs at least a command") 

1381 

1382 mode = args[1].lower() 

1383 

1384 d = DBConn() 

1385 

1386 q = select(Keyring.keyring_name).where(Keyring.active.is_(True)) 

1387 

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") 

1396 

1397 for keyring_name in d.session().scalars(q): 

1398 print(keyring_name) 

1399 

1400 

1401def keyring_add_buildd(command: list[str]) -> None: 

1402 name = command[2] 

1403 arch_names = command[3:] 

1404 

1405 session = DBConn().session() 

1406 arches = session.scalars( 

1407 select(Architecture).where(Architecture.arch_string.in_(arch_names)) 

1408 ) 

1409 

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) 

1418 

1419 k = Keyring() 

1420 k.keyring_name = name 

1421 k.acl = acl 

1422 k.priority = 10 

1423 session.add(k) 

1424 

1425 session.commit() 

1426 

1427 

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") 

1435 

1436 

1437dispatch["keyring"] = keyring 

1438dispatch["k"] = keyring 

1439 

1440################################################################################ 

1441 

1442 

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 

1450 

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() 

1462 

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) 

1471 

1472 

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 

1480 

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() 

1492 

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) 

1505 

1506 

1507def change_component(args: list[str]) -> None: 

1508 with daklib.archive.ArchiveTransaction() as transaction: 

1509 session = transaction.session 

1510 

1511 suite = get_suite_by_name(args[1], session) 

1512 component = get_component_by_name(args[2], session) 

1513 

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])) 

1520 

1521 transaction.commit() 

1522 

1523 

1524dispatch["change-component"] = change_component 

1525 

1526################################################################################ 

1527 

1528 

1529def forget_signature(args: list[str]) -> None: 

1530 filename = args[1] 

1531 with open(filename, "rb") as fh: 

1532 data = fh.read() 

1533 

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() 

1544 

1545 

1546dispatch["forget-signature"] = forget_signature 

1547 

1548################################################################################ 

1549 

1550 

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] 

1563 

1564 args = apt_pkg.parse_commandline(Cnf, arguments, sys.argv) # type: ignore[attr-defined] 

1565 

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 

1571 

1572 subcommand = args[0] 

1573 

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")