1#! /usr/bin/env python3 

2 

3""" 

4Generate Maintainers file used by e.g. the Debian Bug Tracking System 

5@contact: Debian FTP Master <ftpmaster@debian.org> 

6@copyright: 2000, 2001, 2002, 2003, 2004, 2006 James Troup <james@nocrew.org> 

7@copyright: 2011 Torsten Werner <twerner@debian.org> 

8@license: GNU General Public License version 2 or later 

9 

10""" 

11 

12# This program is free software; you can redistribute it and/or modify 

13# it under the terms of the GNU General Public License as published by 

14# the Free Software Foundation; either version 2 of the License, or 

15# (at your option) any later version. 

16 

17# This program is distributed in the hope that it will be useful, 

18# but WITHOUT ANY WARRANTY; without even the implied warranty of 

19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

20# GNU General Public License for more details. 

21 

22# You should have received a copy of the GNU General Public License 

23# along with this program; if not, write to the Free Software 

24# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 

25 

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

27 

28# ``As opposed to "Linux sucks. Respect my academic authoritah, damn 

29# you!" or whatever all this hot air amounts to.'' 

30# -- ajt@ in _that_ thread on debian-devel@ 

31 

32################################################################################ 

33 

34import sys 

35 

36import apt_pkg 

37from sqlalchemy.sql import text 

38 

39from daklib import daklog 

40from daklib.config import Config 

41from daklib.dbconn import Archive, DBConn 

42from daklib.regexes import re_comments 

43 

44################################################################################ 

45 

46 

47def usage(exit_code=0): 

48 print( 

49 """Usage: dak make-maintainers [OPTION] -a ARCHIVE EXTRA_FILE[...] 

50Generate an index of packages <=> Maintainers / Uploaders. 

51 

52 -a, --archive=ARCHIVE archive to take packages from 

53 -s, --source output source packages only 

54 -p, --print print package list to stdout instead of writing it to files 

55 -h, --help show this help and exit 

56""" 

57 ) 

58 sys.exit(exit_code) 

59 

60 

61################################################################################ 

62 

63 

64def format(package, person): 

65 """Return a string nicely formatted for writing to the output file.""" 

66 return "%-20s %s\n" % (package, person) 

67 

68 

69################################################################################ 

70 

71 

72def main(): 

73 cnf = Config() 

74 

75 Arguments = [ 

76 ("h", "help", "Make-Maintainers::Options::Help"), 

77 ("a", "archive", "Make-Maintainers::Options::Archive", "HasArg"), 

78 ("s", "source", "Make-Maintainers::Options::Source"), 

79 ("p", "print", "Make-Maintainers::Options::Print"), 

80 ] 

81 for i in ["Help", "Source", "Print"]: 

82 key = "Make-Maintainers::Options::%s" % i 

83 if key not in cnf: 83 ↛ 81line 83 didn't jump to line 81, because the condition on line 83 was never false

84 cnf[key] = "" 

85 

86 extra_files = apt_pkg.parse_commandline(cnf.Cnf, Arguments, sys.argv) 

87 Options = cnf.subtree("Make-Maintainers::Options") 

88 

89 if Options["Help"] or not Options.get("Archive"): 

90 usage() 

91 

92 Logger = daklog.Logger("make-maintainers") 

93 session = DBConn().session() 

94 

95 archive = session.query(Archive).filter_by(archive_name=Options["Archive"]).one() 

96 

97 # dictionary packages to maintainer names 

98 maintainers = dict() 

99 # dictionary packages to list of uploader names 

100 uploaders = dict() 

101 

102 query = session.execute( 

103 text( 

104 """ 

105SELECT 

106 bs.package, 

107 bs.name AS maintainer, 

108 array_agg(mu.name) OVER ( 

109 PARTITION BY bs.source, bs.id 

110 ORDER BY mu.name 

111 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING 

112 ) AS uploaders 

113 FROM ( 

114 SELECT DISTINCT ON (package) 

115 * 

116 FROM ( 

117 SELECT 

118 s.id AS source, 

119 0 AS id, 

120 s.source AS package, 

121 s.version, 

122 m.name 

123 FROM 

124 source AS s INNER JOIN 

125 maintainer AS m ON s.maintainer = m.id INNER JOIN 

126 src_associations AS sa ON s.id = sa.source INNER JOIN 

127 suite on sa.suite = suite.id 

128 WHERE 

129 suite.archive_id = :archive_id 

130 UNION SELECT 

131 b.source, 

132 b.id, 

133 b.package, 

134 b.version, 

135 m.name 

136 FROM 

137 binaries AS b INNER JOIN 

138 maintainer AS m ON b.maintainer = m.id INNER JOIN 

139 bin_associations AS ba ON b.id = ba.bin INNER JOIN 

140 suite on ba.suite = suite.id 

141 WHERE 

142 NOT :source_only AND 

143 suite.archive_id = :archive_id 

144 ) AS bs 

145 ORDER BY package, version desc 

146 ) AS bs LEFT OUTER JOIN 

147 -- find all uploaders for a given source 

148 src_uploaders AS su ON bs.source = su.source LEFT OUTER JOIN 

149 maintainer AS mu ON su.maintainer = mu.id 

150""" 

151 ).params( 

152 archive_id=archive.archive_id, 

153 source_only="True" if Options["Source"] else "False", 

154 ) 

155 ) 

156 

157 Logger.log(["database"]) 

158 for entry in query: 

159 maintainers[entry["package"]] = entry["maintainer"] 

160 if all(x is None for x in entry["uploaders"]): 160 ↛ exit,   160 ↛ 1612 missed branches: 1) line 160 didn't finish the generator expression on line 160, 2) line 160 didn't jump to line 161, because the condition on line 160 was never true

161 uploaders[entry["package"]] = [""] 

162 else: 

163 uploaders[entry["package"]] = entry["uploaders"] 

164 

165 Logger.log(["files"]) 

166 # Process any additional Maintainer files (e.g. from pseudo 

167 # packages) 

168 for filename in extra_files: 168 ↛ 169line 168 didn't jump to line 169, because the loop on line 168 never started

169 with open(filename) as extrafile: 

170 for line in extrafile.readlines(): 

171 line = re_comments.sub("", line).strip() 

172 if line == "": 

173 continue 

174 (package, maintainer) = line.split(None, 1) 

175 maintainers[package] = maintainer 

176 uploaders[package] = [maintainer] 

177 

178 if Options["Print"]: 178 ↛ 182line 178 didn't jump to line 182, because the condition on line 178 was never false

179 for package in sorted(maintainers): 

180 print(format(package, maintainers[package]), end="") 

181 else: 

182 with open("Maintainers", "w") as maintainer_file, open( 

183 "Uploaders", "w" 

184 ) as uploader_file: 

185 for package in sorted(uploaders): 

186 maintainer_file.write(format(package, maintainers[package])) 

187 for uploader in uploaders[package]: 

188 uploader_file.write(format(package, uploader)) 

189 

190 Logger.close() 

191 

192 

193############################################################################### 

194 

195 

196if __name__ == "__main__": 

197 main()