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