Coverage for dak/override.py: 76%
156 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"""Microscopic modification and query tool for overrides in projectb"""
3# Copyright (C) 2004, 2006 Daniel Silverstone <dsilvers@digital-scurf.org>
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20################################################################################
21## So line up your soldiers and she'll shoot them all down
22## Coz Alisha Rules The World
23## You think you found a dream, then it shatters and it seems,
24## That Alisha Rules The World
25################################################################################
27import os
28import sys
29from typing import NoReturn, cast
31import apt_pkg
32from sqlalchemy import sql
33from sqlalchemy.engine import CursorResult
35from daklib import daklog, utils
36from daklib.config import Config
37from daklib.dbconn import (
38 DBConn,
39 get_override_type,
40 get_priority,
41 get_section,
42 get_suite,
43)
45################################################################################
47# Shamelessly stolen from 'dak rm'. Should probably end up in utils.py
50def game_over() -> None:
51 answer = utils.input_or_exit("Continue (y/N)? ").lower()
52 if answer != "y": 52 ↛ 53line 52 didn't jump to line 53 because the condition on line 52 was never true
53 print("Aborted.")
54 sys.exit(1)
57def usage(exit_code=0) -> NoReturn:
58 print(
59 """Usage: dak override [OPTIONS] package [section] [priority]
60Make microchanges or microqueries of the binary overrides
62 -h, --help show this help and exit
63 -c, --check check override compliance (deprecated)
64 -d, --done=BUG# send priority/section change as closure to bug#
65 -n, --no-action don't do anything
66 -s, --suite specify the suite to use
67"""
68 )
69 sys.exit(exit_code)
72def main() -> None:
73 cnf = Config()
75 Arguments = [
76 ("h", "help", "Override::Options::Help"),
77 ("c", "check", "Override::Options::Check"),
78 ("d", "done", "Override::Options::Done", "HasArg"),
79 ("n", "no-action", "Override::Options::No-Action"),
80 ("s", "suite", "Override::Options::Suite", "HasArg"),
81 ]
82 for i in ["help", "check", "no-action"]:
83 key = "Override::Options::%s" % i
84 if key not in cnf: 84 ↛ 82line 84 didn't jump to line 82 because the condition on line 84 was always true
85 cnf[key] = ""
86 if "Override::Options::Suite" not in cnf: 86 ↛ 89line 86 didn't jump to line 89 because the condition on line 86 was always true
87 cnf["Override::Options::Suite"] = "unstable"
89 arguments = apt_pkg.parse_commandline(cnf.Cnf, Arguments, sys.argv) # type: ignore[attr-defined]
90 Options = cnf.subtree("Override::Options")
92 if Options["Help"]:
93 usage()
95 session = DBConn().session()
97 if not arguments: 97 ↛ 98line 97 didn't jump to line 98 because the condition on line 97 was never true
98 utils.fubar("package name is a required argument.")
100 package = arguments.pop(0)
101 suite_name = Options["Suite"]
102 if arguments and len(arguments) > 2: 102 ↛ 103line 102 didn't jump to line 103 because the condition on line 102 was never true
103 utils.fubar("Too many arguments")
105 suite = get_suite(suite_name, session)
106 if suite is None: 106 ↛ 107line 106 didn't jump to line 107 because the condition on line 106 was never true
107 utils.fubar("Unknown suite '{0}'".format(suite_name))
109 if arguments and len(arguments) == 1:
110 # Determine if the argument is a priority or a section...
111 arg = arguments.pop()
112 q = session.execute(
113 sql.text(
114 """
115 SELECT ( SELECT COUNT(*) FROM section WHERE section = :arg ) AS secs,
116 ( SELECT COUNT(*) FROM priority WHERE priority = :arg ) AS prios
117 """
118 ),
119 {"arg": arg},
120 )
121 r = q.fetchall()
122 if r[0][0] == 1:
123 arguments = (arg, ".")
124 elif r[0][1] == 1: 124 ↛ 127line 124 didn't jump to line 127 because the condition on line 124 was always true
125 arguments = (".", arg)
126 else:
127 utils.fubar("%s is not a valid section or priority" % (arg))
129 # Retrieve current section/priority...
130 oldsection, oldsourcesection, oldpriority = None, None, None
131 for packagetype in ["source", "binary"]:
132 eqdsc = "!="
133 if packagetype == "source":
134 eqdsc = "="
135 q = session.execute(
136 sql.text(
137 """
138 SELECT priority.priority AS prio, section.section AS sect, override_type.type AS type
139 FROM override, priority, section, suite, override_type
140 WHERE override.priority = priority.id
141 AND override.type = override_type.id
142 AND override_type.type %s 'dsc'
143 AND override.section = section.id
144 AND override.package = :package
145 AND override.suite = suite.id
146 AND suite.suite_name = :suite_name
147 """
148 % (eqdsc)
149 ),
150 {"package": package, "suite_name": suite_name},
151 )
153 rowcount = cast(CursorResult, q).rowcount
154 if rowcount == 0: 154 ↛ 155line 154 didn't jump to line 155 because the condition on line 154 was never true
155 continue
156 if rowcount > 1: 156 ↛ 157line 156 didn't jump to line 157 because the condition on line 156 was never true
157 utils.fubar("%s is ambiguous. Matches %d packages" % (package, rowcount))
159 r2 = q.fetchone()
160 assert r2 is not None
161 if packagetype == "binary":
162 oldsection = r2[1]
163 oldpriority = r2[0]
164 else:
165 oldsourcesection = r2[1]
166 oldpriority = "optional"
168 if not oldpriority and not oldsourcesection: 168 ↛ 169line 168 didn't jump to line 169 because the condition on line 168 was never true
169 utils.fubar("Unable to find package %s" % (package))
171 if oldsection and oldsourcesection and oldsection != oldsourcesection: 171 ↛ 173line 171 didn't jump to line 173 because the condition on line 171 was never true
172 # When setting overrides, both source & binary will become the same section
173 utils.warn(
174 "Source is in section '%s' instead of '%s'" % (oldsourcesection, oldsection)
175 )
177 if not oldsection: 177 ↛ 178line 177 didn't jump to line 178 because the condition on line 177 was never true
178 oldsection = oldsourcesection
180 if not arguments:
181 print(
182 "%s is in section '%s' at priority '%s'"
183 % (package, oldsection, oldpriority)
184 )
185 sys.exit(0)
187 # At this point, we have a new section and priority... check they're valid...
188 newsection, newpriority = arguments
190 if newsection == ".":
191 newsection = oldsection
192 if newpriority == ".":
193 newpriority = oldpriority
195 s = get_section(newsection, session)
196 if s is None: 196 ↛ 197line 196 didn't jump to line 197 because the condition on line 196 was never true
197 utils.fubar("Supplied section %s is invalid" % (newsection))
198 newsecid = s.section_id
200 p = get_priority(newpriority, session)
201 if p is None: 201 ↛ 202line 201 didn't jump to line 202 because the condition on line 201 was never true
202 utils.fubar("Supplied priority %s is invalid" % (newpriority))
203 newprioid = p.priority_id
205 if newpriority == oldpriority and newsection == oldsection: 205 ↛ 206line 205 didn't jump to line 206 because the condition on line 205 was never true
206 print("I: Doing nothing")
207 sys.exit(0)
209 if Options["Check"]: 209 ↛ 210line 209 didn't jump to line 210 because the condition on line 209 was never true
210 print("WARNING: Check option is deprecated by Debian Policy 4.0.1")
212 # If we're in no-action mode
213 if Options["No-Action"]: 213 ↛ 214line 213 didn't jump to line 214 because the condition on line 213 was never true
214 if newpriority != oldpriority:
215 print("I: Would change priority from %s to %s" % (oldpriority, newpriority))
216 if newsection != oldsection:
217 print("I: Would change section from %s to %s" % (oldsection, newsection))
218 if "Done" in Options:
219 print("I: Would also close bug(s): %s" % (Options["Done"]))
221 sys.exit(0)
223 if newpriority != oldpriority:
224 print("I: Will change priority from %s to %s" % (oldpriority, newpriority))
226 if newsection != oldsection:
227 print("I: Will change section from %s to %s" % (oldsection, newsection))
229 if "Done" not in Options:
230 pass
231 # utils.warn("No bugs to close have been specified. Noone will know you have done this.")
232 else:
233 print("I: Will close bug(s): %s" % (Options["Done"]))
235 game_over()
237 Logger = daklog.Logger("override")
239 dsc_otype = get_override_type("dsc")
240 assert dsc_otype is not None
241 dsc_otype_id = dsc_otype.overridetype_id
243 # We're already in a transaction
244 # We're in "do it" mode, we have something to do... do it
245 if newpriority != oldpriority:
246 session.execute(
247 sql.text(
248 """
249 UPDATE override
250 SET priority = :newprioid
251 WHERE package = :package
252 AND override.type != :otypedsc
253 AND suite = (SELECT id FROM suite WHERE suite_name = :suite_name)"""
254 ),
255 {
256 "newprioid": newprioid,
257 "package": package,
258 "otypedsc": dsc_otype_id,
259 "suite_name": suite_name,
260 },
261 )
263 Logger.log(["changed priority", package, oldpriority, newpriority])
265 if newsection != oldsection:
266 q = session.execute(
267 sql.text(
268 """
269 UPDATE override
270 SET section = :newsecid
271 WHERE package = :package
272 AND suite = (SELECT id FROM suite WHERE suite_name = :suite_name)"""
273 ),
274 {"newsecid": newsecid, "package": package, "suite_name": suite_name},
275 )
277 Logger.log(["changed section", package, oldsection, newsection])
279 session.commit()
281 if "Done" in Options:
282 if "Dinstall::BugServer" not in cnf: 282 ↛ 283line 282 didn't jump to line 283 because the condition on line 282 was never true
283 utils.warn(
284 "Asked to send Done message but Dinstall::BugServer is not configured"
285 )
286 Logger.close()
287 return
289 Subst = {}
290 Subst["__OVERRIDE_ADDRESS__"] = cnf["Dinstall::MyEmailAddress"]
291 Subst["__BUG_SERVER__"] = cnf["Dinstall::BugServer"]
292 bcc = []
293 if cnf.find("Dinstall::Bcc") != "": 293 ↛ 294line 293 didn't jump to line 294 because the condition on line 293 was never true
294 bcc.append(cnf["Dinstall::Bcc"])
295 if bcc: 295 ↛ 296line 295 didn't jump to line 296 because the condition on line 295 was never true
296 Subst["__BCC__"] = "Bcc: " + ", ".join(bcc)
297 else:
298 Subst["__BCC__"] = "X-Filler: 42"
299 if "Dinstall::PackagesServer" in cnf: 299 ↛ 300line 299 didn't jump to line 300
300 Subst["__CC__"] = (
301 "Cc: "
302 + package
303 + "@"
304 + cnf["Dinstall::PackagesServer"]
305 + "\nX-DAK: dak override"
306 )
307 else:
308 Subst["__CC__"] = "X-DAK: dak override"
309 Subst["__ADMIN_ADDRESS__"] = cnf["Dinstall::MyAdminAddress"]
310 Subst["__DISTRO__"] = cnf["Dinstall::MyDistribution"]
311 Subst["__WHOAMI__"] = utils.whoami()
312 Subst["__SOURCE__"] = package
314 summary = "Concerning package %s...\n" % (package)
315 summary += "Operating on the %s suite\n" % (suite_name)
316 if newpriority != oldpriority: 316 ↛ 318line 316 didn't jump to line 318 because the condition on line 316 was always true
317 summary += "Changed priority from %s to %s\n" % (oldpriority, newpriority)
318 if newsection != oldsection: 318 ↛ 320line 318 didn't jump to line 320 because the condition on line 318 was always true
319 summary += "Changed section from %s to %s\n" % (oldsection, newsection)
320 Subst["__SUMMARY__"] = summary
322 template = os.path.join(cnf["Dir::Templates"], "override.bug-close")
323 for bug in utils.split_args(Options["Done"]):
324 Subst["__BUG_NUMBER__"] = bug
325 mail_message = utils.TemplateSubst(Subst, template)
326 utils.send_mail(mail_message)
327 Logger.log(["closed bug", bug])
329 Logger.close()