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