Coverage for dak/manage_build_queues.py: 85%
63 statements
« prev ^ index » next coverage.py v7.6.0, created at 2026-01-04 16:18 +0000
« prev ^ index » next coverage.py v7.6.0, created at 2026-01-04 16:18 +0000
1#! /usr/bin/env python3
3"""Manage build queues
5@contact: Debian FTPMaster <ftpmaster@debian.org>
6@copyright: 2000, 2001, 2002, 2006 James Troup <james@nocrew.org>
7@copyright: 2009 Mark Hymers <mhy@debian.org>
8@copyright: 2012, Ansgar Burchardt <ansgar@debian.org>
10"""
12# This program is free software; you can redistribute it and/or modify
13# it under the terms of the GNU General Public License as published by
14# the Free Software Foundation; either version 2 of the License, or
15# (at your option) any later version.
17# This program is distributed in the hope that it will be useful,
18# but WITHOUT ANY WARRANTY; without even the implied warranty of
19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20# GNU General Public License for more details.
22# You should have received a copy of the GNU General Public License
23# along with this program; if not, write to the Free Software
24# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26################################################################################
28import sys
29from datetime import datetime, timedelta
30from typing import NoReturn
32import apt_pkg
33import sqlalchemy.sql as sql
35from daklib import daklog
36from daklib.archive import ArchiveTransaction
37from daklib.config import Config
38from daklib.dbconn import BuildQueue, DBBinary, DBSource
40################################################################################
42Options: apt_pkg.Configuration
43Logger: daklog.Logger
45################################################################################
48def usage(exit_code=0) -> NoReturn:
49 print(
50 """Usage: dak manage-build-queues [OPTIONS] buildqueue1 buildqueue2
51Manage the contents of one or more build queues
53 -a, --all run on all known build queues
54 -n, --no-action don't do anything
55 -h, --help show this help and exit"""
56 )
58 sys.exit(exit_code)
61################################################################################
64def clean(
65 build_queue: BuildQueue,
66 transaction: ArchiveTransaction,
67 now: datetime | None = None,
68) -> None:
69 session = transaction.session
70 if now is None: 70 ↛ 71line 70 didn't jump to line 71 because the condition on line 70 was never true
71 now = datetime.now()
73 delete_before = now - timedelta(seconds=build_queue.stay_of_execution)
74 suite = build_queue.suite
75 suite_was_changed = False
77 # Remove binaries subject to the following conditions:
78 # 1. Keep binaries that are in policy queues.
79 # 2. Remove binaries that are not in suites.
80 # 3. Remove binaries that have been in the build queue for some time.
81 query = sql.text(
82 """
83 SELECT b.*
84 FROM binaries b
85 JOIN bin_associations ba ON b.id = ba.bin
86 WHERE ba.suite = :suite_id
87 AND NOT EXISTS
88 (SELECT 1 FROM policy_queue_upload_binaries_map pqubm
89 JOIN policy_queue_upload pqu ON pqu.id = pqubm.policy_queue_upload_id
90 JOIN policy_queue pq ON pq.id = pqu.policy_queue_id
91 JOIN suite s ON s.policy_queue_id = pq.id
92 JOIN suite_build_queue_copy sbqc ON sbqc.suite = s.id
93 WHERE pqubm.binary_id = ba.bin AND pq.send_to_build_queues
94 AND sbqc.build_queue_id = :build_queue_id)
95 AND (ba.created < :delete_before
96 OR NOT EXISTS
97 (SELECT 1 FROM bin_associations ba2
98 JOIN suite_build_queue_copy sbqc ON sbqc.suite = ba2.suite
99 WHERE ba2.bin = ba.bin AND sbqc.build_queue_id = :build_queue_id))"""
100 )
101 binaries = (
102 session.query(DBBinary)
103 .from_statement(query) # type: ignore[arg-type]
104 .params(
105 {
106 "build_queue_id": build_queue.queue_id,
107 "suite_id": suite.suite_id,
108 "delete_before": delete_before,
109 }
110 )
111 )
112 for binary in binaries:
113 Logger.log(
114 [
115 "removed binary from build queue",
116 build_queue.queue_name,
117 binary.package,
118 binary.version,
119 ]
120 )
121 transaction.remove_binary(binary, suite)
122 suite_was_changed = True
124 # Remove sources
125 # Conditions are similar as for binaries, but we also keep sources
126 # if there is a binary in the build queue that uses it.
127 query = sql.text(
128 """
129 SELECT s.*
130 FROM source s
131 JOIN src_associations sa ON s.id = sa.source
132 WHERE sa.suite = :suite_id
133 AND NOT EXISTS
134 (SELECT 1 FROM policy_queue_upload pqu
135 JOIN policy_queue pq ON pq.id = pqu.policy_queue_id
136 JOIN suite s ON s.policy_queue_id = pq.id
137 JOIN suite_build_queue_copy sbqc ON sbqc.suite = s.id
138 WHERE pqu.source_id = sa.source AND pq.send_to_build_queues
139 AND sbqc.build_queue_id = :build_queue_id)
140 AND (sa.created < :delete_before
141 OR NOT EXISTS
142 (SELECT 1 FROM src_associations sa2
143 JOIN suite_build_queue_copy sbqc ON sbqc.suite = sa2.suite
144 WHERE sbqc.build_queue_id = :build_queue_id
145 AND sa2.source = sa.source))
146 AND NOT EXISTS
147 (SELECT 1 FROM bin_associations ba
148 JOIN binaries b ON ba.bin = b.id
149 WHERE ba.suite = :suite_id
150 AND b.source = s.id)"""
151 )
152 sources = (
153 session.query(DBSource)
154 .from_statement(query) # type: ignore[arg-type]
155 .params(
156 {
157 "build_queue_id": build_queue.queue_id,
158 "suite_id": suite.suite_id,
159 "delete_before": delete_before,
160 }
161 )
162 )
163 for source in sources:
164 Logger.log(
165 [
166 "removed source from build queue",
167 build_queue.queue_name,
168 source.source,
169 source.version,
170 ]
171 )
172 transaction.remove_source(source, suite)
173 suite_was_changed = True
175 if suite_was_changed: 175 ↛ exitline 175 didn't return from function 'clean' because the condition on line 175 was always true
176 suite.update_last_changed()
179def main() -> None:
180 global Options, Logger
182 cnf = Config()
184 for i in ["Help", "No-Action", "All"]:
185 key = "Manage-Build-Queues::Options::%s" % i
186 if key not in cnf: 186 ↛ 184line 186 didn't jump to line 184 because the condition on line 186 was always true
187 cnf[key] = ""
189 Arguments = [
190 ("h", "help", "Manage-Build-Queues::Options::Help"),
191 ("n", "no-action", "Manage-Build-Queues::Options::No-Action"),
192 ("a", "all", "Manage-Build-Queues::Options::All"),
193 ]
195 queue_names = apt_pkg.parse_commandline(cnf.Cnf, Arguments, sys.argv) # type: ignore[attr-defined]
196 Options = cnf.subtree("Manage-Build-Queues::Options")
198 if Options["Help"]:
199 usage()
201 Logger = daklog.Logger("manage-build-queues", Options["No-Action"])
203 starttime = datetime.now()
205 with ArchiveTransaction() as transaction:
206 session = transaction.session
207 if Options["All"]: 207 ↛ 208line 207 didn't jump to line 208 because the condition on line 207 was never true
208 if len(queue_names) != 0:
209 print("E: Cannot use both -a and a queue name")
210 sys.exit(1)
211 queues = session.query(BuildQueue)
212 else:
213 queues = session.query(BuildQueue).filter(
214 BuildQueue.queue_name.in_(queue_names)
215 )
217 for q in queues:
218 Logger.log(
219 ["cleaning queue %s using datetime %s" % (q.queue_name, starttime)]
220 )
221 clean(q, transaction, now=starttime)
222 if not Options["No-Action"]: 222 ↛ 225line 222 didn't jump to line 225 because the condition on line 222 was always true
223 transaction.commit()
224 else:
225 transaction.rollback()
227 Logger.close()
230#######################################################################################
233if __name__ == "__main__":
234 main()