Coverage for dak/process_policy.py: 81%

331 statements  

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

1"""Handles packages from policy queues 

2 

3@contact: Debian FTP Master <ftpmaster@debian.org> 

4@copyright: 2001, 2002, 2003, 2004, 2005, 2006 James Troup <james@nocrew.org> 

5@copyright: 2009 Joerg Jaspert <joerg@debian.org> 

6@copyright: 2009 Frank Lichtenheld <djpig@debian.org> 

7@copyright: 2009 Mark Hymers <mhy@debian.org> 

8@license: GNU General Public License version 2 or later 

9""" 

10 

11# This program is free software; you can redistribute it and/or modify 

12# it under the terms of the GNU General Public License as published by 

13# the Free Software Foundation; either version 2 of the License, or 

14# (at your option) any later version. 

15 

16# This program is distributed in the hope that it will be useful, 

17# but WITHOUT ANY WARRANTY; without even the implied warranty of 

18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

19# GNU General Public License for more details. 

20 

21# You should have received a copy of the GNU General Public License 

22# along with this program; if not, write to the Free Software 

23# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 

24 

25################################################################################ 

26 

27# <mhy> So how do we handle that at the moment? 

28# <stew> Probably incorrectly. 

29 

30################################################################################ 

31 

32import datetime 

33import functools 

34import os 

35import re 

36import sys 

37import traceback 

38from collections.abc import Callable 

39from typing import NoReturn 

40 

41import apt_pkg 

42from sqlalchemy import select, sql 

43 

44import daklib.announce 

45import daklib.upload 

46import daklib.utils 

47from daklib import daklog, utils 

48from daklib.archive import ArchiveTransaction, source_component_from_package_list 

49from daklib.config import Config 

50from daklib.dbconn import ( 

51 ArchiveFile, 

52 Component, 

53 DBBinary, 

54 DBChange, 

55 DBConn, 

56 DBSource, 

57 Override, 

58 OverrideType, 

59 PolicyQueue, 

60 PolicyQueueUpload, 

61 PoolFile, 

62 get_mapped_component, 

63 get_suite_by_name, 

64) 

65from daklib.externalsignature import check_upload_for_external_signature_request 

66from daklib.packagelist import PackageList 

67from daklib.urgencylog import UrgencyLog 

68 

69# Globals 

70Options: apt_pkg.Configuration 

71Logger: daklog.Logger 

72 

73################################################################################ 

74 

75ProcessingCallable = Callable[ 

76 [PolicyQueueUpload, PolicyQueue, str, ArchiveTransaction], None 

77] 

78 

79 

80def do_comments( 

81 dir: str, 

82 srcqueue: PolicyQueue, 

83 opref: str, 

84 npref: str, 

85 line: str, 

86 fn: ProcessingCallable, 

87 transaction: ArchiveTransaction, 

88) -> None: 

89 session = transaction.session 

90 actions: list[tuple[PolicyQueueUpload, str]] = [] 

91 for comm in [x for x in os.listdir(dir) if x.startswith(opref)]: 

92 with open(os.path.join(dir, comm)) as fd: 

93 lines = fd.readlines() 

94 if len(lines) == 0 or lines[0] != line + "\n": 94 ↛ 95line 94 didn't jump to line 95 because the condition on line 94 was never true

95 continue 

96 

97 # If the ACCEPT includes a _<arch> we only accept that .changes. 

98 # Otherwise we accept all .changes that start with the given prefix 

99 changes_prefix = comm[len(opref) :] 

100 if changes_prefix.count("_") < 2: 

101 changes_prefix = changes_prefix + "_" 

102 else: 

103 changes_prefix = changes_prefix + ".changes" 

104 

105 # We need to escape "_" as we use it with the LIKE operator (via the 

106 # SQLA startwith) later. 

107 changes_prefix = changes_prefix.replace("_", r"\_") 

108 

109 uploads = session.scalars( 

110 select(PolicyQueueUpload) 

111 .filter_by(policy_queue=srcqueue) 

112 .join(PolicyQueueUpload.changes) 

113 .where(DBChange.changesname.startswith(changes_prefix)) 

114 .order_by(PolicyQueueUpload.source_id) 

115 ) 

116 reason = "".join(lines[1:]) 

117 actions.extend((u, reason) for u in uploads) 

118 

119 if opref != npref: 

120 newcomm = npref + comm[len(opref) :] 

121 newcomm = utils.find_next_free(os.path.join(dir, newcomm)) 

122 transaction.fs.move(os.path.join(dir, comm), newcomm) 

123 

124 actions.sort() 

125 

126 for u, reason in actions: 

127 print("Processing changes file: {0}".format(u.changes.changesname)) 

128 fn(u, srcqueue, reason, transaction) 

129 

130 

131################################################################################ 

132 

133 

134def try_or_reject(function: ProcessingCallable) -> ProcessingCallable: 

135 @functools.wraps(function) 

136 def wrapper( 

137 upload: PolicyQueueUpload, 

138 srcqueue: PolicyQueue, 

139 comments: str, 

140 transaction: ArchiveTransaction, 

141 ) -> None: 

142 try: 

143 function(upload, srcqueue, comments, transaction) 

144 except Exception: 

145 comments = "An exception was raised while processing the package:\n{0}\nOriginal comments:\n{1}".format( 

146 traceback.format_exc(), comments 

147 ) 

148 try: 

149 transaction.rollback() 

150 real_comment_reject(upload, srcqueue, comments, transaction) 

151 except Exception: 

152 comments = "In addition an exception was raised while trying to reject the upload:\n{0}\nOriginal rejection:\n{1}".format( 

153 traceback.format_exc(), comments 

154 ) 

155 transaction.rollback() 

156 real_comment_reject( 

157 upload, srcqueue, comments, transaction, notify=False 

158 ) 

159 if not Options["No-Action"]: 159 ↛ 162line 159 didn't jump to line 162 because the condition on line 159 was always true

160 transaction.commit() 

161 else: 

162 transaction.rollback() 

163 

164 return wrapper 

165 

166 

167################################################################################ 

168 

169 

170@try_or_reject 

171def comment_accept( 

172 upload: PolicyQueueUpload, 

173 srcqueue: PolicyQueue, 

174 comments: str, 

175 transaction: ArchiveTransaction, 

176) -> None: 

177 for byhand in upload.byhand: 177 ↛ 178line 177 didn't jump to line 178 because the loop on line 177 never started

178 path = os.path.join(srcqueue.path, byhand.filename) 

179 if os.path.exists(path): 

180 raise Exception( 

181 "E: cannot ACCEPT upload with unprocessed byhand file {0}".format( 

182 byhand.filename 

183 ) 

184 ) 

185 

186 cnf = Config() 

187 

188 fs = transaction.fs 

189 session = transaction.session 

190 changesname = upload.changes.changesname 

191 allow_tainted = srcqueue.suite.archive.tainted 

192 

193 # We need overrides to get the target component 

194 overridesuite = upload.target_suite 

195 if overridesuite.overridesuite is not None: 

196 overridesuite = get_suite_by_name(overridesuite.overridesuite, session) 

197 

198 def binary_component_func(db_binary: DBBinary) -> Component: 

199 section = db_binary.proxy["Section"] 

200 component_name = "main" 

201 if section.find("/") != -1: 

202 component_name = section.split("/", 1)[0] 

203 component = get_mapped_component(component_name, session=session) 

204 assert component is not None 

205 return component 

206 

207 def is_debug_binary(db_binary: DBBinary) -> bool: 

208 return daklib.utils.is_in_debug_section(db_binary.proxy) 

209 

210 def has_debug_binaries(upload: PolicyQueueUpload) -> bool: 

211 return any(is_debug_binary(x) for x in upload.binaries) 

212 

213 def source_component_func(db_source: DBSource) -> Component: 

214 package_list = PackageList(db_source.proxy) 

215 component = source_component_from_package_list( 

216 package_list, upload.target_suite 

217 ) 

218 if component is not None: 218 ↛ 224line 218 didn't jump to line 224

219 component = get_mapped_component(component.component_name, session=session) 

220 assert component is not None 

221 return component 

222 

223 # Fallback for packages without Package-List field 

224 query = ( 

225 select(Override) 

226 .filter_by(suite=overridesuite, package=db_source.source) 

227 .join(OverrideType) 

228 .where(OverrideType.overridetype == "dsc") 

229 .join(Component) 

230 ) 

231 return session.execute(query).scalar_one().component 

232 

233 policy_queue = upload.target_suite.policy_queue 

234 if policy_queue == srcqueue: 

235 policy_queue = None 

236 

237 all_target_suites = [ 

238 upload.target_suite if policy_queue is None else policy_queue.suite 

239 ] 

240 if policy_queue is None or policy_queue.send_to_build_queues: 240 ↛ 243line 240 didn't jump to line 243 because the condition on line 240 was always true

241 all_target_suites.extend([q.suite for q in upload.target_suite.copy_queues]) 

242 

243 throw_away_binaries = False 

244 if upload.source is not None: 

245 source_component = source_component_func(upload.source) 

246 if upload.target_suite.suite_name in cnf.value_list( 

247 "Dinstall::ThrowAwayNewBinarySuites" 

248 ) and source_component.component_name in cnf.value_list( 

249 "Dinstall::ThrowAwayNewBinaryComponents" 

250 ): 

251 throw_away_binaries = True 

252 

253 for suite in all_target_suites: 

254 debug_suite = suite.debug_suite 

255 

256 if upload.source is not None: 

257 # If we have Source in this upload, let's include it into 

258 # upload suite. 

259 transaction.copy_source( 

260 upload.source, 

261 suite, 

262 source_component, 

263 allow_tainted=allow_tainted, 

264 ) 

265 

266 if ( 

267 not throw_away_binaries 

268 and debug_suite is not None 

269 and has_debug_binaries(upload) 

270 ): 

271 # If we're handing a debug package, we also need to include the 

272 # source in the debug suite as well. 

273 transaction.copy_source( 

274 upload.source, 

275 debug_suite, 

276 source_component_func(upload.source), 

277 allow_tainted=allow_tainted, 

278 ) 

279 

280 if not throw_away_binaries: 

281 for db_binary in upload.binaries: 

282 # Now, let's work out where to copy this guy to -- if it's 

283 # a debug binary, and the suite has a debug suite, let's go 

284 # ahead and target the debug suite rather then the stock 

285 # suite. 

286 copy_to_suite = suite 

287 if debug_suite is not None and is_debug_binary(db_binary): 

288 copy_to_suite = debug_suite 

289 

290 # build queues and debug suites may miss the source package 

291 # if this is a binary-only upload. 

292 if copy_to_suite != upload.target_suite: 

293 transaction.copy_source( 

294 db_binary.source, 

295 copy_to_suite, 

296 source_component_func(db_binary.source), 

297 allow_tainted=allow_tainted, 

298 ) 

299 

300 transaction.copy_binary( 

301 db_binary, 

302 copy_to_suite, 

303 binary_component_func(db_binary), 

304 allow_tainted=allow_tainted, 

305 extra_archives=[upload.target_suite.archive], 

306 ) 

307 

308 check_upload_for_external_signature_request( 

309 session, suite, copy_to_suite, db_binary 

310 ) 

311 

312 suite.update_last_changed() 

313 

314 # Copy .changes if needed 

315 if policy_queue is None and upload.target_suite.copychanges: 315 ↛ 316line 315 didn't jump to line 316 because the condition on line 315 was never true

316 src = os.path.join(upload.policy_queue.path, upload.changes.changesname) 

317 dst = os.path.join(upload.target_suite.path, upload.changes.changesname) 

318 fs.copy(src, dst, mode=upload.target_suite.archive.mode) 

319 

320 # List of files in the queue directory 

321 queue_files = [changesname] 

322 chg = daklib.upload.Changes( 

323 upload.policy_queue.path, changesname, keyrings=[], require_signature=False 

324 ) 

325 queue_files.extend(f.filename for f in chg.buildinfo_files) 

326 

327 # TODO: similar code exists in archive.py's `ArchiveUpload._install_policy` 

328 if policy_queue is not None: 

329 # register upload in policy queue 

330 new_upload = PolicyQueueUpload() 

331 new_upload.policy_queue = policy_queue 

332 new_upload.target_suite = upload.target_suite 

333 new_upload.changes = upload.changes 

334 new_upload.source = upload.source 

335 new_upload.binaries = upload.binaries 

336 session.add(new_upload) 

337 session.flush() 

338 

339 # copy .changes & similar to policy queue 

340 for fn in queue_files: 

341 src = os.path.join(upload.policy_queue.path, fn) 

342 dst = os.path.join(policy_queue.path, fn) 

343 transaction.fs.copy(src, dst, mode=policy_queue.change_perms) 

344 

345 # Copy upload to Process-Policy::CopyDir 

346 # Used on security.d.o to sync accepted packages to ftp-master, but this 

347 # should eventually be replaced by something else. 

348 copydir = cnf.get("Process-Policy::CopyDir") or None 

349 if policy_queue is None and copydir is not None: 349 ↛ 350line 349 didn't jump to line 350 because the condition on line 349 was never true

350 mode = upload.target_suite.archive.mode 

351 if upload.source is not None: 

352 for f in [df.poolfile for df in upload.source.srcfiles]: 

353 dst = os.path.join(copydir, f.basename) 

354 if not os.path.exists(dst): 

355 fs.copy(f.fullpath, dst, mode=mode) 

356 

357 for db_binary in upload.binaries: 

358 f = db_binary.poolfile 

359 dst = os.path.join(copydir, f.basename) 

360 if not os.path.exists(dst): 

361 fs.copy(f.fullpath, dst, mode=mode) 

362 

363 for fn in queue_files: 

364 src = os.path.join(upload.policy_queue.path, fn) 

365 dst = os.path.join(copydir, fn) 

366 # We check for `src` to exist as old uploads in policy queues 

367 # might still miss the `.buildinfo` files. 

368 if os.path.exists(src) and not os.path.exists(dst): 

369 fs.copy(src, dst, mode=mode) 

370 

371 if policy_queue is None: 

372 utils.process_buildinfos( 

373 upload.policy_queue.path, chg.buildinfo_files, fs, Logger 

374 ) 

375 

376 if policy_queue is None and upload.source is not None and not Options["No-Action"]: 

377 urgency = upload.changes.urgency 

378 # As per policy 5.6.17, the urgency can be followed by a space and a 

379 # comment. Extract only the urgency from the string. 

380 if " " in urgency: 380 ↛ 381line 380 didn't jump to line 381 because the condition on line 380 was never true

381 urgency, comment = urgency.split(" ", 1) 

382 if urgency not in cnf.value_list("Urgency::Valid"): 382 ↛ 383line 382 didn't jump to line 383 because the condition on line 382 was never true

383 urgency = cnf["Urgency::Default"] 

384 UrgencyLog().log(upload.source.source, upload.source.version, urgency) 

385 

386 if policy_queue is None: 

387 print(" ACCEPT") 

388 else: 

389 print(" ACCEPT-TO-QUEUE") 

390 if not Options["No-Action"]: 390 ↛ 393line 390 didn't jump to line 393 because the condition on line 390 was always true

391 Logger.log(["Policy Queue ACCEPT", srcqueue.queue_name, changesname]) 

392 

393 if policy_queue is None: 

394 pu = get_processed_upload(upload) 

395 daklib.announce.announce_accept(pu) 

396 

397 # TODO: code duplication. Similar code is in process-upload. 

398 # Move .changes to done 

399 now = datetime.datetime.now() 

400 donedir = os.path.join(cnf["Dir::Done"], now.strftime("%Y/%m/%d")) 

401 if policy_queue is None: 

402 for fn in queue_files: 

403 src = os.path.join(upload.policy_queue.path, fn) 

404 if os.path.exists(src): 404 ↛ 402line 404 didn't jump to line 402 because the condition on line 404 was always true

405 dst = os.path.join(donedir, fn) 

406 dst = utils.find_next_free(dst) 

407 fs.copy(src, dst, mode=0o644) 

408 

409 if throw_away_binaries and upload.target_suite.archive.use_morgue: 

410 morguesubdir = cnf.get("New::MorgueSubDir", "new") 

411 

412 utils.move_to_morgue( 

413 morguesubdir, 

414 [db_binary.poolfile.fullpath for db_binary in upload.binaries], 

415 fs, 

416 Logger, 

417 ) 

418 

419 remove_upload(upload, transaction) 

420 

421 

422################################################################################ 

423 

424 

425@try_or_reject 

426def comment_reject(*args) -> None: 

427 real_comment_reject(*args, manual=True) 

428 

429 

430def real_comment_reject( 

431 upload: PolicyQueueUpload, 

432 srcqueue: PolicyQueue, 

433 comments: str, 

434 transaction: ArchiveTransaction, 

435 notify=True, 

436 *, 

437 manual=False, 

438) -> None: 

439 cnf = Config() 

440 

441 fs = transaction.fs 

442 session = transaction.session 

443 changesname = upload.changes.changesname 

444 queuedir = upload.policy_queue.path 

445 rejectdir = cnf["Dir::Reject"] 

446 

447 ### Copy files to reject/ 

448 

449 poolfiles = [b.poolfile for b in upload.binaries] 

450 if upload.source is not None: 450 ↛ 453line 450 didn't jump to line 453 because the condition on line 450 was always true

451 poolfiles.extend([df.poolfile for df in upload.source.srcfiles]) 

452 # Not beautiful... 

453 files = [ 

454 af.path 

455 for af in session.scalars( 

456 select(ArchiveFile) 

457 .filter_by(archive=upload.policy_queue.suite.archive) 

458 .join(ArchiveFile.file) 

459 .where(PoolFile.file_id.in_([f.file_id for f in poolfiles])) 

460 ) 

461 ] 

462 for byhand in upload.byhand: 462 ↛ 463line 462 didn't jump to line 463 because the loop on line 462 never started

463 path = os.path.join(queuedir, byhand.filename) 

464 if os.path.exists(path): 

465 files.append(path) 

466 chg = daklib.upload.Changes( 

467 queuedir, changesname, keyrings=[], require_signature=False 

468 ) 

469 for f in chg.buildinfo_files: 

470 path = os.path.join(queuedir, f.filename) 

471 if os.path.exists(path): 471 ↛ 469line 471 didn't jump to line 469 because the condition on line 471 was always true

472 files.append(path) 

473 files.append(os.path.join(queuedir, changesname)) 

474 

475 for fn in files: 

476 dst = utils.find_next_free(os.path.join(rejectdir, os.path.basename(fn))) 

477 fs.copy(fn, dst, link=True) 

478 

479 ### Write reason 

480 

481 dst = utils.find_next_free( 

482 os.path.join(rejectdir, "{0}.reason".format(changesname)) 

483 ) 

484 fh = fs.create(dst) 

485 fh.write(comments) 

486 fh.close() 

487 

488 ### Send mail notification 

489 

490 if notify: 490 ↛ 504line 490 didn't jump to line 504 because the condition on line 490 was always true

491 rejected_by = None 

492 reason = comments 

493 

494 # Try to use From: from comment file if there is one. 

495 # This is not very elegant... 

496 match = re.match(r"\AFrom: ([^\n]+)\n\n", comments) 

497 if match: 497 ↛ 501line 497 didn't jump to line 501 because the condition on line 497 was always true

498 rejected_by = match.group(1) 

499 reason = "\n".join(comments.splitlines()[2:]) 

500 

501 pu = get_processed_upload(upload) 

502 daklib.announce.announce_reject(pu, reason, rejected_by) 

503 

504 print(" REJECT") 

505 if not Options["No-Action"]: 505 ↛ 510line 505 didn't jump to line 510 because the condition on line 505 was always true

506 Logger.log( 

507 ["Policy Queue REJECT", srcqueue.queue_name, upload.changes.changesname] 

508 ) 

509 

510 changes = upload.changes 

511 remove_upload(upload, transaction) 

512 session.delete(changes) 

513 

514 

515################################################################################ 

516 

517 

518def remove_upload(upload: PolicyQueueUpload, transaction: ArchiveTransaction) -> None: 

519 fs = transaction.fs 

520 session = transaction.session 

521 

522 # Remove byhand and changes files. Binary and source packages will be 

523 # removed from {bin,src}_associations and eventually removed by clean-suites automatically. 

524 queuedir = upload.policy_queue.path 

525 for byhand in upload.byhand: 525 ↛ 526line 525 didn't jump to line 526 because the loop on line 525 never started

526 path = os.path.join(queuedir, byhand.filename) 

527 if os.path.exists(path): 

528 fs.unlink(path) 

529 session.delete(byhand) 

530 

531 chg = daklib.upload.Changes( 

532 queuedir, upload.changes.changesname, keyrings=[], require_signature=False 

533 ) 

534 queue_files = [upload.changes.changesname] 

535 queue_files.extend(f.filename for f in chg.buildinfo_files) 

536 for fn in queue_files: 

537 # We check for `path` to exist as old uploads in policy queues 

538 # might still miss the `.buildinfo` files. 

539 path = os.path.join(queuedir, fn) 

540 if os.path.exists(path): 540 ↛ 536line 540 didn't jump to line 536 because the condition on line 540 was always true

541 fs.unlink(path) 

542 

543 session.delete(upload) 

544 session.flush() 

545 

546 

547################################################################################ 

548 

549 

550def get_processed_upload(upload: PolicyQueueUpload) -> daklib.announce.ProcessedUpload: 

551 pu = daklib.announce.ProcessedUpload() 

552 

553 pu.maintainer = upload.changes.maintainer 

554 pu.changed_by = upload.changes.changedby 

555 pu.fingerprint = upload.changes.fingerprint 

556 pu.authorized_by_fingerprint = upload.changes.authorized_by_fingerprint 

557 

558 pu.suites = [upload.target_suite] 

559 pu.from_policy_suites = [upload.target_suite] 

560 

561 changes_path = os.path.join(upload.policy_queue.path, upload.changes.changesname) 

562 with open(changes_path, "r") as fd: 

563 pu.changes = fd.read() 

564 pu.changes_filename = upload.changes.changesname 

565 pu.sourceful = upload.source is not None 

566 pu.source = upload.changes.source 

567 pu.version = upload.changes.version 

568 pu.architecture = upload.changes.architecture 

569 pu.bugs = upload.changes.closes 

570 

571 pu.program = "process-policy" 

572 

573 return pu 

574 

575 

576################################################################################ 

577 

578 

579def remove_unreferenced_binaries( 

580 policy_queue: PolicyQueue, transaction: ArchiveTransaction 

581) -> None: 

582 """Remove binaries that are no longer referenced by an upload""" 

583 session = transaction.session 

584 suite = policy_queue.suite 

585 

586 query = sql.text( 

587 """ 

588 SELECT b.* 

589 FROM binaries b 

590 JOIN bin_associations ba ON b.id = ba.bin 

591 WHERE ba.suite = :suite_id 

592 AND NOT EXISTS (SELECT 1 FROM policy_queue_upload_binaries_map pqubm 

593 JOIN policy_queue_upload pqu ON pqubm.policy_queue_upload_id = pqu.id 

594 WHERE pqu.policy_queue_id = :policy_queue_id 

595 AND pqubm.binary_id = b.id)""" 

596 ) 

597 binaries = session.scalars( 

598 select(DBBinary).from_statement(query), 

599 { 

600 "suite_id": policy_queue.suite_id, 

601 "policy_queue_id": policy_queue.policy_queue_id, 

602 }, 

603 ) 

604 

605 for binary in binaries: 

606 Logger.log( 

607 [ 

608 "removed binary from policy queue", 

609 policy_queue.queue_name, 

610 binary.package, 

611 binary.version, 

612 ] 

613 ) 

614 transaction.remove_binary(binary, suite) 

615 

616 

617def remove_unreferenced_sources( 

618 policy_queue: PolicyQueue, transaction: ArchiveTransaction 

619) -> None: 

620 """Remove sources that are no longer referenced by an upload or a binary""" 

621 session = transaction.session 

622 suite = policy_queue.suite 

623 

624 query = sql.text( 

625 """ 

626 SELECT s.* 

627 FROM source s 

628 JOIN src_associations sa ON s.id = sa.source 

629 WHERE sa.suite = :suite_id 

630 AND NOT EXISTS (SELECT 1 FROM policy_queue_upload pqu 

631 WHERE pqu.policy_queue_id = :policy_queue_id 

632 AND pqu.source_id = s.id) 

633 AND NOT EXISTS (SELECT 1 FROM binaries b 

634 JOIN bin_associations ba ON b.id = ba.bin 

635 WHERE b.source = s.id 

636 AND ba.suite = :suite_id)""" 

637 ) 

638 sources = session.scalars( 

639 select(DBSource).from_statement(query), 

640 { 

641 "suite_id": policy_queue.suite_id, 

642 "policy_queue_id": policy_queue.policy_queue_id, 

643 }, 

644 ) 

645 

646 for source in sources: 

647 Logger.log( 

648 [ 

649 "removed source from policy queue", 

650 policy_queue.queue_name, 

651 source.source, 

652 source.version, 

653 ] 

654 ) 

655 transaction.remove_source(source, suite) 

656 

657 

658################################################################################ 

659 

660 

661def usage(status=0) -> NoReturn: 

662 print("""Usage: dak process-policy QUEUE""") 

663 sys.exit(status) 

664 

665 

666################################################################################ 

667 

668 

669def main() -> None: 

670 global Options, Logger 

671 

672 cnf = Config() 

673 session = DBConn().session() 

674 

675 Arguments = [ 

676 ("h", "help", "Process-Policy::Options::Help"), 

677 ("n", "no-action", "Process-Policy::Options::No-Action"), 

678 ] 

679 

680 for i in ["help", "no-action"]: 

681 key = "Process-Policy::Options::%s" % i 

682 if key not in cnf: 682 ↛ 680line 682 didn't jump to line 680 because the condition on line 682 was always true

683 cnf[key] = "" 

684 

685 queue_name = apt_pkg.parse_commandline(cnf.Cnf, Arguments, sys.argv) # type: ignore[attr-defined] 

686 

687 Options = cnf.subtree("Process-Policy::Options") 

688 if Options["Help"]: 

689 usage() 

690 

691 if len(queue_name) != 1: 691 ↛ 692line 691 didn't jump to line 692 because the condition on line 691 was never true

692 print("E: Specify exactly one policy queue") 

693 sys.exit(1) 

694 

695 queue_name = queue_name[0] 

696 

697 Logger = daklog.Logger("process-policy") 

698 if not Options["No-Action"]: 698 ↛ 701line 698 didn't jump to line 701 because the condition on line 698 was always true

699 urgencylog = UrgencyLog() 

700 

701 with ArchiveTransaction() as transaction: 

702 session = transaction.session 

703 pq = session.execute( 

704 select(PolicyQueue).filter_by(queue_name=queue_name) 

705 ).scalar_one_or_none() 

706 if pq is None: 706 ↛ 707line 706 didn't jump to line 707 because the condition on line 706 was never true

707 print("E: Cannot find policy queue %s" % queue_name) 

708 sys.exit(1) 

709 

710 commentsdir = os.path.join(pq.path, "COMMENTS") 

711 # The comments stuff relies on being in the right directory 

712 os.chdir(pq.path) 

713 

714 do_comments( 

715 commentsdir, 

716 pq, 

717 "REJECT.", 

718 "REJECTED.", 

719 "NOTOK", 

720 comment_reject, 

721 transaction, 

722 ) 

723 do_comments( 

724 commentsdir, pq, "ACCEPT.", "ACCEPTED.", "OK", comment_accept, transaction 

725 ) 

726 do_comments( 

727 commentsdir, pq, "ACCEPTED.", "ACCEPTED.", "OK", comment_accept, transaction 

728 ) 

729 

730 remove_unreferenced_binaries(pq, transaction) 

731 remove_unreferenced_sources(pq, transaction) 

732 

733 if not Options["No-Action"]: 733 ↛ exitline 733 didn't return from function 'main' because the condition on line 733 was always true

734 urgencylog.close()