1#! /usr/bin/env python3 

2# vim:set et ts=4 sw=4: 

3 

4"""Handles packages from policy queues 

5 

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

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

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

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

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

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

12""" 

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

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

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

16# (at your option) any later version. 

17 

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

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

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

21# GNU General Public License for more details. 

22 

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

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

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

26 

27################################################################################ 

28 

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

30# <stew> Probably incorrectly. 

31 

32################################################################################ 

33 

34import datetime 

35import functools 

36import os 

37import re 

38import sys 

39import traceback 

40from collections.abc import Callable 

41from typing import NoReturn 

42 

43import apt_pkg 

44import sqlalchemy.sql as sql 

45from sqlalchemy.orm.exc import NoResultFound 

46 

47import daklib.announce 

48import daklib.upload 

49import daklib.utils 

50from daklib import daklog, utils 

51from daklib.archive import ArchiveTransaction, source_component_from_package_list 

52from daklib.config import Config 

53from daklib.dbconn import ( 

54 ArchiveFile, 

55 Component, 

56 DBBinary, 

57 DBChange, 

58 DBConn, 

59 DBSource, 

60 Override, 

61 OverrideType, 

62 PolicyQueue, 

63 PolicyQueueUpload, 

64 PoolFile, 

65 Suite, 

66 get_mapped_component, 

67) 

68from daklib.externalsignature import check_upload_for_external_signature_request 

69from daklib.packagelist import PackageList 

70from daklib.urgencylog import UrgencyLog 

71 

72# Globals 

73Options = None 

74Logger = None 

75 

76################################################################################ 

77 

78ProcessingCallable = Callable[ 

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

80] 

81 

82 

83def do_comments( 

84 dir: str, 

85 srcqueue: PolicyQueue, 

86 opref: str, 

87 npref: str, 

88 line: str, 

89 fn: ProcessingCallable, 

90 transaction: ArchiveTransaction, 

91) -> None: 

92 session = transaction.session 

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

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

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

96 lines = fd.readlines() 

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

98 continue 

99 

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

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

102 changes_prefix = comm[len(opref) :] 

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

104 changes_prefix = changes_prefix + "_" 

105 else: 

106 changes_prefix = changes_prefix + ".changes" 

107 

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

109 # SQLA startwith) later. 

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

111 

112 uploads = ( 

113 session.query(PolicyQueueUpload) 

114 .filter_by(policy_queue=srcqueue) 

115 .join(PolicyQueueUpload.changes) 

116 .filter(DBChange.changesname.startswith(changes_prefix)) 

117 .order_by(PolicyQueueUpload.source_id) 

118 ) 

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

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

121 

122 if opref != npref: 

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

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

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

126 

127 actions.sort() 

128 

129 for u, reason in actions: 

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

131 fn(u, srcqueue, reason, transaction) 

132 

133 

134################################################################################ 

135 

136 

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

138 @functools.wraps(function) 

139 def wrapper( 

140 upload: PolicyQueueUpload, 

141 srcqueue: PolicyQueue, 

142 comments: str, 

143 transaction: ArchiveTransaction, 

144 ) -> None: 

145 try: 

146 function(upload, srcqueue, comments, transaction) 

147 except Exception: 

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

149 traceback.format_exc(), comments 

150 ) 

151 try: 

152 transaction.rollback() 

153 real_comment_reject(upload, srcqueue, comments, transaction) 

154 except Exception: 

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

156 traceback.format_exc(), comments 

157 ) 

158 transaction.rollback() 

159 real_comment_reject( 

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

161 ) 

162 if not Options["No-Action"]: 162 ↛ 165line 162 didn't jump to line 165, because the condition on line 162 was never false

163 transaction.commit() 

164 else: 

165 transaction.rollback() 

166 

167 return wrapper 

168 

169 

170################################################################################ 

171 

172 

173@try_or_reject 

174def comment_accept( 

175 upload: PolicyQueueUpload, 

176 srcqueue: PolicyQueue, 

177 comments: str, 

178 transaction: ArchiveTransaction, 

179) -> None: 

180 for byhand in upload.byhand: 180 ↛ 181line 180 didn't jump to line 181, because the loop on line 180 never started

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

182 if os.path.exists(path): 

183 raise Exception( 

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

185 byhand.filename 

186 ) 

187 ) 

188 

189 cnf = Config() 

190 

191 fs = transaction.fs 

192 session = transaction.session 

193 changesname = upload.changes.changesname 

194 allow_tainted = srcqueue.suite.archive.tainted 

195 

196 # We need overrides to get the target component 

197 overridesuite = upload.target_suite 

198 if overridesuite.overridesuite is not None: 

199 overridesuite = ( 

200 session.query(Suite).filter_by(suite_name=overridesuite.overridesuite).one() 

201 ) 

202 

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

204 section = db_binary.proxy["Section"] 

205 component_name = "main" 

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

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

208 return get_mapped_component(component_name, session=session) 

209 

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

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

212 

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

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

215 

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

217 package_list = PackageList(db_source.proxy) 

218 component = source_component_from_package_list( 

219 package_list, upload.target_suite 

220 ) 

221 if component is not None: 221 ↛ 225line 221 didn't jump to line 225

222 return get_mapped_component(component.component_name, session=session) 

223 

224 # Fallback for packages without Package-List field 

225 query = ( 

226 session.query(Override) 

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

228 .join(OverrideType) 

229 .filter(OverrideType.overridetype == "dsc") 

230 .join(Component) 

231 ) 

232 return query.one().component 

233 

234 policy_queue = upload.target_suite.policy_queue 

235 if policy_queue == srcqueue: 

236 policy_queue = None 

237 

238 all_target_suites = [ 

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

240 ] 

241 if policy_queue is None or policy_queue.send_to_build_queues: 241 ↛ 244line 241 didn't jump to line 244, because the condition on line 241 was never false

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

243 

244 throw_away_binaries = False 

245 if upload.source is not None: 

246 source_component = source_component_func(upload.source) 

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

248 "Dinstall::ThrowAwayNewBinarySuites" 

249 ) and source_component.component_name in cnf.value_list( 

250 "Dinstall::ThrowAwayNewBinaryComponents" 

251 ): 

252 throw_away_binaries = True 

253 

254 for suite in all_target_suites: 

255 debug_suite = suite.debug_suite 

256 

257 if upload.source is not None: 

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

259 # upload suite. 

260 transaction.copy_source( 

261 upload.source, 

262 suite, 

263 source_component, 

264 allow_tainted=allow_tainted, 

265 ) 

266 

267 if not throw_away_binaries: 

268 if debug_suite is not None and has_debug_binaries(upload): 

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

270 # source in the debug suite as well. 

271 transaction.copy_source( 

272 upload.source, 

273 debug_suite, 

274 source_component_func(upload.source), 

275 allow_tainted=allow_tainted, 

276 ) 

277 

278 if not throw_away_binaries: 

279 for db_binary in upload.binaries: 

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

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

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

283 # suite. 

284 copy_to_suite = suite 

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

286 copy_to_suite = debug_suite 

287 

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

289 # if this is a binary-only upload. 

290 if copy_to_suite != upload.target_suite: 

291 transaction.copy_source( 

292 db_binary.source, 

293 copy_to_suite, 

294 source_component_func(db_binary.source), 

295 allow_tainted=allow_tainted, 

296 ) 

297 

298 transaction.copy_binary( 

299 db_binary, 

300 copy_to_suite, 

301 binary_component_func(db_binary), 

302 allow_tainted=allow_tainted, 

303 extra_archives=[upload.target_suite.archive], 

304 ) 

305 

306 check_upload_for_external_signature_request( 

307 session, suite, copy_to_suite, db_binary 

308 ) 

309 

310 suite.update_last_changed() 

311 

312 # Copy .changes if needed 

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

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

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

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

317 

318 # List of files in the queue directory 

319 queue_files = [changesname] 

320 chg = daklib.upload.Changes( 

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

322 ) 

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

324 

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

326 if policy_queue is not None: 

327 # register upload in policy queue 

328 new_upload = PolicyQueueUpload() 

329 new_upload.policy_queue = policy_queue 

330 new_upload.target_suite = upload.target_suite 

331 new_upload.changes = upload.changes 

332 new_upload.source = upload.source 

333 new_upload.binaries = upload.binaries 

334 session.add(new_upload) 

335 session.flush() 

336 

337 # copy .changes & similar to policy queue 

338 for fn in queue_files: 

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

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

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

342 

343 # Copy upload to Process-Policy::CopyDir 

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

345 # should eventually be replaced by something else. 

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

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

348 mode = upload.target_suite.archive.mode 

349 if upload.source is not None: 

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

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

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

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

354 

355 for db_binary in upload.binaries: 

356 f = db_binary.poolfile 

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

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

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

360 

361 for fn in queue_files: 

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

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

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

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

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

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

368 

369 if policy_queue is None: 

370 utils.process_buildinfos( 

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

372 ) 

373 

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

375 urgency = upload.changes.urgency 

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

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

378 if " " in urgency: 378 ↛ 379line 378 didn't jump to line 379, because the condition on line 378 was never true

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

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

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

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

383 

384 if policy_queue is None: 

385 print(" ACCEPT") 

386 else: 

387 print(" ACCEPT-TO-QUEUE") 

388 if not Options["No-Action"]: 388 ↛ 391line 388 didn't jump to line 391, because the condition on line 388 was never false

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

390 

391 if policy_queue is None: 

392 pu = get_processed_upload(upload) 

393 daklib.announce.announce_accept(pu) 

394 

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

396 # Move .changes to done 

397 now = datetime.datetime.now() 

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

399 if policy_queue is None: 

400 for fn in queue_files: 

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

402 if os.path.exists(src): 402 ↛ 400line 402 didn't jump to line 400, because the condition on line 402 was never false

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

404 dst = utils.find_next_free(dst) 

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

406 

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

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

409 

410 utils.move_to_morgue( 

411 morguesubdir, 

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

413 fs, 

414 Logger, 

415 ) 

416 

417 remove_upload(upload, transaction) 

418 

419 

420################################################################################ 

421 

422 

423@try_or_reject 

424def comment_reject(*args) -> None: 

425 real_comment_reject(*args, manual=True) 

426 

427 

428def real_comment_reject( 

429 upload: PolicyQueueUpload, 

430 srcqueue: PolicyQueue, 

431 comments: str, 

432 transaction: ArchiveTransaction, 

433 notify=True, 

434 manual=False, 

435) -> None: 

436 cnf = Config() 

437 

438 fs = transaction.fs 

439 session = transaction.session 

440 changesname = upload.changes.changesname 

441 queuedir = upload.policy_queue.path 

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

443 

444 ### Copy files to reject/ 

445 

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

447 if upload.source is not None: 447 ↛ 450line 447 didn't jump to line 450, because the condition on line 447 was never false

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

449 # Not beautiful... 

450 files = [ 

451 af.path 

452 for af in session.query(ArchiveFile) 

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

454 .join(ArchiveFile.file) 

455 .filter(PoolFile.file_id.in_([f.file_id for f in poolfiles])) 

456 ] 

457 for byhand in upload.byhand: 457 ↛ 458line 457 didn't jump to line 458, because the loop on line 457 never started

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

459 if os.path.exists(path): 

460 files.append(path) 

461 chg = daklib.upload.Changes( 

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

463 ) 

464 for f in chg.buildinfo_files: 

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

466 if os.path.exists(path): 466 ↛ 464line 466 didn't jump to line 464, because the condition on line 466 was never false

467 files.append(path) 

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

469 

470 for fn in files: 

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

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

473 

474 ### Write reason 

475 

476 dst = utils.find_next_free( 

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

478 ) 

479 fh = fs.create(dst) 

480 fh.write(comments) 

481 fh.close() 

482 

483 ### Send mail notification 

484 

485 if notify: 485 ↛ 499line 485 didn't jump to line 499, because the condition on line 485 was never false

486 rejected_by = None 

487 reason = comments 

488 

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

490 # This is not very elegant... 

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

492 if match: 492 ↛ 496line 492 didn't jump to line 496, because the condition on line 492 was never false

493 rejected_by = match.group(1) 

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

495 

496 pu = get_processed_upload(upload) 

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

498 

499 print(" REJECT") 

500 if not Options["No-Action"]: 500 ↛ 505line 500 didn't jump to line 505, because the condition on line 500 was never false

501 Logger.log( 

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

503 ) 

504 

505 changes = upload.changes 

506 remove_upload(upload, transaction) 

507 session.delete(changes) 

508 

509 

510################################################################################ 

511 

512 

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

514 fs = transaction.fs 

515 session = transaction.session 

516 

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

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

519 queuedir = upload.policy_queue.path 

520 for byhand in upload.byhand: 520 ↛ 521line 520 didn't jump to line 521, because the loop on line 520 never started

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

522 if os.path.exists(path): 

523 fs.unlink(path) 

524 session.delete(byhand) 

525 

526 chg = daklib.upload.Changes( 

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

528 ) 

529 queue_files = [upload.changes.changesname] 

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

531 for fn in queue_files: 

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

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

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

535 if os.path.exists(path): 535 ↛ 531line 535 didn't jump to line 531, because the condition on line 535 was never false

536 fs.unlink(path) 

537 

538 session.delete(upload) 

539 session.flush() 

540 

541 

542################################################################################ 

543 

544 

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

546 pu = daklib.announce.ProcessedUpload() 

547 

548 pu.maintainer = upload.changes.maintainer 

549 pu.changed_by = upload.changes.changedby 

550 pu.fingerprint = upload.changes.fingerprint 

551 

552 pu.suites = [upload.target_suite] 

553 pu.from_policy_suites = [upload.target_suite] 

554 

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

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

557 pu.changes = fd.read() 

558 pu.changes_filename = upload.changes.changesname 

559 pu.sourceful = upload.source is not None 

560 pu.source = upload.changes.source 

561 pu.version = upload.changes.version 

562 pu.architecture = upload.changes.architecture 

563 pu.bugs = upload.changes.closes 

564 

565 pu.program = "process-policy" 

566 

567 return pu 

568 

569 

570################################################################################ 

571 

572 

573def remove_unreferenced_binaries( 

574 policy_queue: PolicyQueue, transaction: ArchiveTransaction 

575) -> None: 

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

577 session = transaction.session 

578 suite = policy_queue.suite 

579 

580 query = sql.text( 

581 """ 

582 SELECT b.* 

583 FROM binaries b 

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

585 WHERE ba.suite = :suite_id 

586 AND NOT EXISTS (SELECT 1 FROM policy_queue_upload_binaries_map pqubm 

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

588 WHERE pqu.policy_queue_id = :policy_queue_id 

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

590 ) 

591 binaries = ( 

592 session.query(DBBinary) 

593 .from_statement(query) 

594 .params( 

595 { 

596 "suite_id": policy_queue.suite_id, 

597 "policy_queue_id": policy_queue.policy_queue_id, 

598 } 

599 ) 

600 ) 

601 

602 for binary in binaries: 

603 Logger.log( 

604 [ 

605 "removed binary from policy queue", 

606 policy_queue.queue_name, 

607 binary.package, 

608 binary.version, 

609 ] 

610 ) 

611 transaction.remove_binary(binary, suite) 

612 

613 

614def remove_unreferenced_sources( 

615 policy_queue: PolicyQueue, transaction: ArchiveTransaction 

616) -> None: 

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

618 session = transaction.session 

619 suite = policy_queue.suite 

620 

621 query = sql.text( 

622 """ 

623 SELECT s.* 

624 FROM source s 

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

626 WHERE sa.suite = :suite_id 

627 AND NOT EXISTS (SELECT 1 FROM policy_queue_upload pqu 

628 WHERE pqu.policy_queue_id = :policy_queue_id 

629 AND pqu.source_id = s.id) 

630 AND NOT EXISTS (SELECT 1 FROM binaries b 

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

632 WHERE b.source = s.id 

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

634 ) 

635 sources = ( 

636 session.query(DBSource) 

637 .from_statement(query) 

638 .params( 

639 { 

640 "suite_id": policy_queue.suite_id, 

641 "policy_queue_id": policy_queue.policy_queue_id, 

642 } 

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

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 never false

683 cnf[key] = "" 

684 

685 queue_name = apt_pkg.parse_commandline(cnf.Cnf, Arguments, sys.argv) 

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 never false

699 urgencylog = UrgencyLog() 

700 

701 with ArchiveTransaction() as transaction: 

702 session = transaction.session 

703 try: 

704 pq = session.query(PolicyQueue).filter_by(queue_name=queue_name).one() 

705 except NoResultFound: 

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

707 sys.exit(1) 

708 

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

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

711 os.chdir(pq.path) 

712 

713 do_comments( 

714 commentsdir, 

715 pq, 

716 "REJECT.", 

717 "REJECTED.", 

718 "NOTOK", 

719 comment_reject, 

720 transaction, 

721 ) 

722 do_comments( 

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

724 ) 

725 do_comments( 

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

727 ) 

728 

729 remove_unreferenced_binaries(pq, transaction) 

730 remove_unreferenced_sources(pq, transaction) 

731 

732 if not Options["No-Action"]: 732 ↛ exitline 732 didn't return from function 'main', because the condition on line 732 was never false

733 urgencylog.close() 

734 

735 

736################################################################################ 

737 

738 

739if __name__ == "__main__": 

740 main()