Coverage for dak/manage_build_queues.py: 91%
63 statements
« prev ^ index » next coverage.py v7.6.0, created at 2026-02-10 22:10 +0000
« prev ^ index » next coverage.py v7.6.0, created at 2026-02-10 22:10 +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 WHERE ba2.bin = ba.bin
99 AND (EXISTS (SELECT 1 FROM suite_build_queue_copy sbqc
100 WHERE sbqc.build_queue_id = :build_queue_id
101 AND sbqc.suite = ba2.suite)
102 OR EXISTS (SELECT 1 FROM suite_build_queue_copy sbqc
103 JOIN suite s ON s.id = sbqc.suite
104 WHERE sbqc.build_queue_id = :build_queue_id
105 AND s.debugsuite_id = ba2.suite))))"""
106 )
107 binaries = (
108 session.query(DBBinary)
109 .from_statement(query) # type: ignore[arg-type]
110 .params(
111 {
112 "build_queue_id": build_queue.queue_id,
113 "suite_id": suite.suite_id,
114 "delete_before": delete_before,
115 }
116 )
117 )
118 for binary in binaries:
119 Logger.log(
120 [
121 "removed binary from build queue",
122 build_queue.queue_name,
123 binary.package,
124 binary.version,
125 ]
126 )
127 transaction.remove_binary(binary, suite)
128 suite_was_changed = True
130 # Remove sources
131 # Conditions are similar as for binaries, but we also keep sources
132 # if there is a binary in the build queue that uses it.
133 query = sql.text(
134 """
135 SELECT s.*
136 FROM source s
137 JOIN src_associations sa ON s.id = sa.source
138 WHERE sa.suite = :suite_id
139 AND NOT EXISTS
140 (SELECT 1 FROM policy_queue_upload pqu
141 JOIN policy_queue pq ON pq.id = pqu.policy_queue_id
142 JOIN suite s ON s.policy_queue_id = pq.id
143 JOIN suite_build_queue_copy sbqc ON sbqc.suite = s.id
144 WHERE pqu.source_id = sa.source AND pq.send_to_build_queues
145 AND sbqc.build_queue_id = :build_queue_id)
146 AND (sa.created < :delete_before
147 OR NOT EXISTS
148 (SELECT 1 FROM src_associations sa2
149 JOIN suite_build_queue_copy sbqc ON sbqc.suite = sa2.suite
150 WHERE sbqc.build_queue_id = :build_queue_id
151 AND sa2.source = sa.source))
152 AND NOT EXISTS
153 (SELECT 1 FROM bin_associations ba
154 JOIN binaries b ON ba.bin = b.id
155 WHERE ba.suite = :suite_id
156 AND b.source = s.id)"""
157 )
158 sources = (
159 session.query(DBSource)
160 .from_statement(query) # type: ignore[arg-type]
161 .params(
162 {
163 "build_queue_id": build_queue.queue_id,
164 "suite_id": suite.suite_id,
165 "delete_before": delete_before,
166 }
167 )
168 )
169 for source in sources:
170 Logger.log(
171 [
172 "removed source from build queue",
173 build_queue.queue_name,
174 source.source,
175 source.version,
176 ]
177 )
178 transaction.remove_source(source, suite)
179 suite_was_changed = True
181 if suite_was_changed:
182 suite.update_last_changed()
185def main() -> None:
186 global Options, Logger
188 cnf = Config()
190 for i in ["Help", "No-Action", "All"]:
191 key = "Manage-Build-Queues::Options::%s" % i
192 if key not in cnf: 192 ↛ 190line 192 didn't jump to line 190 because the condition on line 192 was always true
193 cnf[key] = ""
195 Arguments = [
196 ("h", "help", "Manage-Build-Queues::Options::Help"),
197 ("n", "no-action", "Manage-Build-Queues::Options::No-Action"),
198 ("a", "all", "Manage-Build-Queues::Options::All"),
199 ]
201 queue_names = apt_pkg.parse_commandline(cnf.Cnf, Arguments, sys.argv) # type: ignore[attr-defined]
202 Options = cnf.subtree("Manage-Build-Queues::Options")
204 if Options["Help"]:
205 usage()
207 Logger = daklog.Logger("manage-build-queues", Options["No-Action"])
209 starttime = datetime.now()
211 with ArchiveTransaction() as transaction:
212 session = transaction.session
213 if Options["All"]:
214 if len(queue_names) != 0: 214 ↛ 215line 214 didn't jump to line 215 because the condition on line 214 was never true
215 print("E: Cannot use both -a and a queue name")
216 sys.exit(1)
217 queues = session.query(BuildQueue)
218 else:
219 queues = session.query(BuildQueue).filter(
220 BuildQueue.queue_name.in_(queue_names)
221 )
223 for q in queues:
224 Logger.log(
225 ["cleaning queue %s using datetime %s" % (q.queue_name, starttime)]
226 )
227 clean(q, transaction, now=starttime)
228 if not Options["No-Action"]: 228 ↛ 231line 228 didn't jump to line 231 because the condition on line 228 was always true
229 transaction.commit()
230 else:
231 transaction.rollback()
233 Logger.close()
236#######################################################################################
239if __name__ == "__main__":
240 main()