Coverage for dak/override.py: 76%

156 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2026-01-04 16:18 +0000

1#! /usr/bin/env python3 

2 

3"""Microscopic modification and query tool for overrides in projectb""" 

4# Copyright (C) 2004, 2006 Daniel Silverstone <dsilvers@digital-scurf.org> 

5 

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. 

10 

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. 

15 

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 

19 

20 

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################################################################################ 

27 

28import os 

29import sys 

30from typing import NoReturn, cast 

31 

32import apt_pkg 

33from sqlalchemy import sql 

34from sqlalchemy.engine import CursorResult 

35 

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) 

45 

46################################################################################ 

47 

48# Shamelessly stolen from 'dak rm'. Should probably end up in utils.py 

49 

50 

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) 

56 

57 

58def usage(exit_code=0) -> NoReturn: 

59 print( 

60 """Usage: dak override [OPTIONS] package [section] [priority] 

61Make microchanges or microqueries of the binary overrides 

62 

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) 

71 

72 

73def main() -> None: 

74 cnf = Config() 

75 

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" 

89 

90 arguments = apt_pkg.parse_commandline(cnf.Cnf, Arguments, sys.argv) # type: ignore[attr-defined] 

91 Options = cnf.subtree("Override::Options") 

92 

93 if Options["Help"]: 

94 usage() 

95 

96 session = DBConn().session() 

97 

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.") 

100 

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") 

105 

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)) 

109 

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)) 

129 

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 ) 

153 

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)) 

159 

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" 

168 

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)) 

171 

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 ) 

177 

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 

180 

181 if not arguments: 

182 print( 

183 "%s is in section '%s' at priority '%s'" 

184 % (package, oldsection, oldpriority) 

185 ) 

186 sys.exit(0) 

187 

188 # At this point, we have a new section and priority... check they're valid... 

189 newsection, newpriority = arguments 

190 

191 if newsection == ".": 

192 newsection = oldsection 

193 if newpriority == ".": 

194 newpriority = oldpriority 

195 

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 

200 

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 

205 

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) 

209 

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") 

212 

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"])) 

221 

222 sys.exit(0) 

223 

224 if newpriority != oldpriority: 

225 print("I: Will change priority from %s to %s" % (oldpriority, newpriority)) 

226 

227 if newsection != oldsection: 

228 print("I: Will change section from %s to %s" % (oldsection, newsection)) 

229 

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"])) 

235 

236 game_over() 

237 

238 Logger = daklog.Logger("override") 

239 

240 dsc_otype = get_override_type("dsc") 

241 assert dsc_otype is not None 

242 dsc_otype_id = dsc_otype.overridetype_id 

243 

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 ) 

263 

264 Logger.log(["changed priority", package, oldpriority, newpriority]) 

265 

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 ) 

277 

278 Logger.log(["changed section", package, oldsection, newsection]) 

279 

280 session.commit() 

281 

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 

289 

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 

314 

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 

322 

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]) 

329 

330 Logger.close() 

331 

332 

333################################################################################# 

334 

335 

336if __name__ == "__main__": 

337 main()