Coverage for dak/show_new.py: 77%

109 statements  

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

1#! /usr/bin/env python3 

2 

3"""Output html for packages in NEW""" 

4# Copyright (C) 2007, 2009 Joerg Jaspert <joerg@debian.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# <elmo> I'm James Troup, long term source of all evil in Debian. you may 

23# know me from such debian-devel-announce gems as "Serious 

24# Problems With ...." 

25 

26################################################################################ 

27 

28import os 

29import sys 

30import time 

31from multiprocessing import Manager 

32 

33import apt_pkg 

34 

35import dak.examine_package 

36from daklib import policy 

37from daklib.config import Config 

38from daklib.dakmultiprocessing import ( 

39 PROC_STATUS_EXCEPTION, 

40 PROC_STATUS_SUCCESS, 

41 DakProcessPool, 

42) 

43from daklib.dbconn import DBChange, DBConn, PolicyQueue, PolicyQueueUpload 

44 

45# Globals 

46cnf: Config 

47Options: apt_pkg.Configuration 

48manager = Manager() 

49sources = manager.list() 

50htmlfiles_to_process = manager.list() 

51timeout_str = "Error or timeout while processing" 

52 

53 

54################################################################################ 

55################################################################################ 

56################################################################################ 

57 

58 

59def html_header(name, missing): 

60 if name.endswith(".changes"): 60 ↛ 61line 60 didn't jump to line 61 because the condition on line 60 was never true

61 name = " ".join(name.split("_")[:2]) 

62 result = """<!DOCTYPE html> 

63<html lang="en"> 

64 <head> 

65 <meta charset="utf-8"> 

66 <title>%(name)s - Debian NEW package overview</title> 

67 <link rel="stylesheet" href="/style.css"> 

68 <link rel="shortcut icon" href="https://www.debian.org/favicon.ico"> 

69 <script> 

70 function toggle(id, initial, display) { 

71 var o = document.getElementById(id); 

72 toggleObj(o, initial, display); 

73 } 

74 function show(id, display) { 

75 var o = document.getElementById(id); 

76 o.style.display = 'table-row-group'; 

77 } 

78 function toggleObj(o, initial, display) { 

79 if(! o.style.display) 

80 o.style.display = initial; 

81 if(o.style.display == display) { 

82 o.style.display = "none"; 

83 } else { 

84 o.style.display = display; 

85 } 

86 } 

87 </script> 

88 </head> 

89 <body id="NEW-details-page"> 

90 <div id="logo"> 

91 <a href="https://www.debian.org/"> 

92 <img src="https://www.debian.org/logos/openlogo-nd-50.png" 

93 alt=""></a> 

94 <a href="https://www.debian.org/"> 

95 <img src="https://www.debian.org/Pics/debian.png" 

96 alt="Debian Project"></a> 

97 </div> 

98 <div id="titleblock"> 

99 <img src="https://www.debian.org/Pics/red-upperleft.png" 

100 id="red-upperleft" alt=""> 

101 <img src="https://www.debian.org/Pics/red-lowerleft.png" 

102 id="red-lowerleft" alt=""> 

103 <img src="https://www.debian.org/Pics/red-upperright.png" 

104 id="red-upperright" alt=""> 

105 <img src="https://www.debian.org/Pics/red-lowerright.png" 

106 id="red-lowerright" alt=""> 

107 <span class="title"> 

108 Debian NEW package overview for %(name)s 

109 </span> 

110 </div> 

111 

112 """ % { 

113 "name": name 

114 } 

115 

116 # we assume only one source (.dsc) per changes here 

117 result += """ 

118 <div id="menu"> 

119 <p class="title">Navigation</p> 

120 <p><a href="#changes" onclick="show('changes-body')">.changes</a></p> 

121 <p><a href="#dsc" onclick="show('dsc-body')">.dsc</a></p> 

122 <p><a href="#source-lintian" onclick="show('source-lintian-body')">source lintian</a></p> 

123 

124""" 

125 for binarytype, packagename in [m for m in missing if m[0] in ("deb", "udeb")]: 

126 result += """ 

127 <p class="subtitle">%(pkg)s</p> 

128 <p><a href="#binary-%(pkg)s-control" onclick="show('binary-%(pkg)s-control-body')">control file</a></p> 

129 <p><a href="#binary-%(pkg)s-lintian" onclick="show('binary-%(pkg)s-lintian-body')">binary lintian</a></p> 

130 <p><a href="#binary-%(pkg)s-contents" onclick="show('binary-%(pkg)s-contents-body')">.deb contents</a></p> 

131 <p><a href="#binary-%(pkg)s-copyright" onclick="show('binary-%(pkg)s-copyright-body')">copyright</a></p> 

132 <p><a href="#binary-%(pkg)s-file-listing" onclick="show('binary-%(pkg)s-file-listing-body')">file listing</a></p> 

133 

134""" % { 

135 "pkg": packagename 

136 } 

137 result += " </div>" 

138 return result 

139 

140 

141def html_footer(): 

142 result = """ <p class="validate">Timestamp: %s (UTC)</p> 

143""" % ( 

144 time.strftime("%d.%m.%Y / %H:%M:%S", time.gmtime()) 

145 ) 

146 result += "</body></html>" 

147 return result 

148 

149 

150################################################################################ 

151 

152 

153def do_pkg(upload_id): 

154 cnf = Config() 

155 

156 session = DBConn().session() 

157 upload = session.query(PolicyQueueUpload).filter_by(id=upload_id).one() 

158 

159 queue = upload.policy_queue 

160 changes = upload.changes 

161 

162 origchanges = os.path.join(queue.path, changes.changesname) 

163 print(origchanges) 

164 

165 htmlname = "{0}_{1}.html".format(changes.source, changes.version) 

166 htmlfile = os.path.join(cnf["Show-New::HTMLPath"], htmlname) 

167 

168 # Have we already processed this? 

169 if os.path.exists(htmlfile) and os.stat(htmlfile).st_mtime > time.mktime( 169 ↛ 172line 169 didn't jump to line 172 because the condition on line 169 was never true

170 changes.created.timetuple() 

171 ): 

172 with open(htmlfile, "r") as fd: 

173 if fd.read() != timeout_str: 

174 sources.append(htmlname) 

175 return (PROC_STATUS_SUCCESS, "%s already up-to-date" % htmlfile) 

176 

177 # Go, process it... Now! 

178 htmlfiles_to_process.append(htmlfile) 

179 sources.append(htmlname) 

180 

181 group = cnf.get("Dinstall::UnprivGroup") or None 

182 

183 with ( 

184 open(htmlfile, "w") as outfile, 

185 policy.UploadCopy(upload, group=group) as upload_copy, 

186 ): 

187 handler = policy.PolicyQueueUploadHandler(upload, session) 

188 missing = [(o["type"], o["package"]) for o in handler.missing_overrides()] 

189 distribution = changes.distribution 

190 

191 outfile.write(html_header(changes.source, missing)) 

192 outfile.write(dak.examine_package.display_changes(distribution, origchanges)) 

193 

194 if upload.source is not None and ("dsc", upload.source.source) in missing: 194 ↛ 197line 194 didn't jump to line 197 because the condition on line 194 was always true

195 fn = os.path.join(upload_copy.directory, upload.source.poolfile.basename) 

196 outfile.write(dak.examine_package.check_dsc(distribution, fn, session)) 

197 for binary in upload.binaries: 

198 if (binary.binarytype, binary.package) not in missing: 198 ↛ 199line 198 didn't jump to line 199 because the condition on line 198 was never true

199 continue 

200 fn = os.path.join(upload_copy.directory, binary.poolfile.basename) 

201 outfile.write(dak.examine_package.check_deb(distribution, fn, session)) 

202 

203 outfile.write(html_footer()) 

204 

205 session.close() 

206 htmlfiles_to_process.remove(htmlfile) 

207 return (PROC_STATUS_SUCCESS, "{0} already updated".format(htmlfile)) 

208 

209 

210################################################################################ 

211 

212 

213def usage(exit_code=0): 

214 print( 

215 """Usage: dak show-new [OPTION]... [CHANGES]... 

216 -h, --help show this help and exit. 

217 -p, --html-path [path] override output directory. 

218 """ 

219 ) 

220 sys.exit(exit_code) 

221 

222 

223################################################################################ 

224 

225 

226def init(session): 

227 global cnf, Options 

228 

229 cnf = Config() 

230 

231 Arguments = [ 

232 ("h", "help", "Show-New::Options::Help"), 

233 ("p", "html-path", "Show-New::HTMLPath", "HasArg"), 

234 ("q", "queue", "Show-New::Options::Queue", "HasArg"), 

235 ] 

236 

237 for i in ["help"]: 

238 key = "Show-New::Options::%s" % i 

239 if key not in cnf: 239 ↛ 237line 239 didn't jump to line 237 because the condition on line 239 was always true

240 cnf[key] = "" 

241 

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

243 Options = cnf.subtree("Show-New::Options") 

244 

245 if Options["help"]: 

246 usage() 

247 

248 queue_names = Options.find("Queue", "new").split(",") 

249 uploads = ( 

250 session.query(PolicyQueueUpload) 

251 .join(PolicyQueueUpload.policy_queue) 

252 .filter(PolicyQueue.queue_name.in_(queue_names)) 

253 .join(PolicyQueueUpload.changes) 

254 .order_by(DBChange.source) 

255 ) 

256 

257 if len(changesnames) > 0: 257 ↛ 258line 257 didn't jump to line 258 because the condition on line 257 was never true

258 uploads = uploads.filter(DBChange.changesname.in_(changesnames)) 

259 

260 return uploads 

261 

262 

263def result_callback(r): 

264 code, msg = r 

265 if code == PROC_STATUS_EXCEPTION: 

266 print("Job raised exception: %s" % (msg)) 

267 elif code != PROC_STATUS_SUCCESS: 

268 print("Job failed: %s" % (msg)) 

269 

270 

271################################################################################ 

272################################################################################ 

273 

274 

275def main(): 

276 dak.examine_package.use_html = True 

277 pool = DakProcessPool(processes=5) 

278 

279 session = DBConn().session() 

280 upload_ids = [u.id for u in init(session)] 

281 session.close() 

282 

283 for upload_id in upload_ids: 

284 pool.apply_async(do_pkg, [upload_id], callback=result_callback) 

285 pool.close() 

286 

287 pool.join() 

288 

289 for htmlfile in htmlfiles_to_process: 289 ↛ 290line 289 didn't jump to line 290 because the loop on line 289 never started

290 with open(htmlfile, "w") as fd: 

291 fd.write(timeout_str) 

292 

293 if pool.overall_status() != PROC_STATUS_SUCCESS: 293 ↛ 294line 293 didn't jump to line 294 because the condition on line 293 was never true

294 raise Exception("Processing failed (code %s)" % (pool.overall_status())) 

295 

296 files = set(os.listdir(cnf["Show-New::HTMLPath"])) 

297 to_delete = [x for x in files.difference(set(sources)) if x.endswith(".html")] 

298 for f in to_delete: 

299 os.remove(os.path.join(cnf["Show-New::HTMLPath"], f)) 

300 

301 

302################################################################################ 

303 

304 

305if __name__ == "__main__": 

306 main()