Coverage for dak/override.py: 76%

156 statements  

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

2 

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

4 

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. 

9 

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. 

14 

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 

18 

19 

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

26 

27import os 

28import sys 

29from typing import NoReturn, cast 

30 

31import apt_pkg 

32from sqlalchemy import sql 

33from sqlalchemy.engine import CursorResult 

34 

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) 

44 

45################################################################################ 

46 

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

48 

49 

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) 

55 

56 

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

58 print( 

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

60Make microchanges or microqueries of the binary overrides 

61 

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) 

70 

71 

72def main() -> None: 

73 cnf = Config() 

74 

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" 

88 

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

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

91 

92 if Options["Help"]: 

93 usage() 

94 

95 session = DBConn().session() 

96 

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

99 

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

104 

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

108 

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

128 

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 ) 

152 

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

158 

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" 

167 

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

170 

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 ) 

176 

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 

179 

180 if not arguments: 

181 print( 

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

183 % (package, oldsection, oldpriority) 

184 ) 

185 sys.exit(0) 

186 

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

188 newsection, newpriority = arguments 

189 

190 if newsection == ".": 

191 newsection = oldsection 

192 if newpriority == ".": 

193 newpriority = oldpriority 

194 

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 

199 

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 

204 

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) 

208 

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

211 

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

220 

221 sys.exit(0) 

222 

223 if newpriority != oldpriority: 

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

225 

226 if newsection != oldsection: 

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

228 

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

234 

235 game_over() 

236 

237 Logger = daklog.Logger("override") 

238 

239 dsc_otype = get_override_type("dsc") 

240 assert dsc_otype is not None 

241 dsc_otype_id = dsc_otype.overridetype_id 

242 

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 ) 

262 

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

264 

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 ) 

276 

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

278 

279 session.commit() 

280 

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 

288 

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 

313 

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 

321 

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

328 

329 Logger.close()