fix repo link not visible when checkout is missing
[packages.grml.org.git] / update.rb
1 #!/usr/bin/env ruby
2 require 'git'
3 require 'erb'
4 require 'yaml'
5 require 'net/http'
6 require 'uri'
7 require 'zlib'
8 require 'yaml'
9
10 DEBUG = false
11
12 def build_used_package_list(source_uri)
13   source_uri.map do |source_uri|
14     fetch_file(source_uri).split("\n").reject do |l|
15       l.strip.start_with?('#') or l.strip == ""
16     end
17   end.flatten
18 end
19
20 def parse_debian_sources(data)
21   data.split("\n").push('').inject({}) do |pkgs, l|
22     l.rstrip!
23     pkg = pkgs[:_tmp] || {}
24     if pkg[:_tmp]
25       if l[0..0] == ' '
26         pkg[:_tmp][:data] << l.strip
27       else
28         pkg[pkg[:_tmp][:hdr]] = pkg[:_tmp][:data].compact
29         pkg.delete :_tmp
30       end
31     end
32     unless pkg[:_tmp]
33       if l == '':
34         pkgs[pkg['Package'][0]] = pkg
35         pkg = nil
36       else
37         hdr, data = l.strip.split(':', 2)
38         data.strip! unless data.nil?
39         pkg[:_tmp] = {:hdr => hdr, :data => [data]}
40       end
41     end
42     if pkg.nil?
43       pkgs.delete :_tmp
44     else
45       pkgs[:_tmp] = pkg
46     end
47     pkgs
48   end
49 end
50
51 def build_package_list(repos, used, sources)
52   data = {}
53   (repos.keys + sources.keys).each do |pkg|
54     p = {
55       :problem => nil,
56       :used => {},
57       :name => pkg,
58       :git_version => nil,
59       :has_tags => false,
60       :repo_version => nil,
61     }
62
63     begin
64       g = Git.bare(working_dir = repos[pkg])
65       tree = g.ls_tree('HEAD')["tree"]
66       if pkg == 'grml-kernel'
67         tree = g.ls_tree(g.ls_tree("HEAD")["tree"]["linux-3"][:sha])["tree"]
68       end
69       current_head = g.gcommit('HEAD')
70       raise "no head" if not current_head.parent
71       raise "no debian dir in git" if not tree.keys.include?("debian")
72       p.merge!({
73         :git_browser => "http://git.grml.org/?p=%s.git;a=summary" % pkg,
74         :git_anon => "git://git.grml.org/%s.git" % pkg,
75       })
76     rescue Exception
77     end
78
79     used.each do |dist,l|
80       p[:used][dist] = l.include?(pkg)
81     end
82     if sources[pkg]
83       p.merge!({
84         :repo_url => "http://deb.grml.org/pool/main/%s/%s/" % [sources[pkg]['Package'][0][0..0], sources[pkg]['Package'][0]],
85         :repo_version => sources[pkg]['Version'][0],
86         :source_name => sources[pkg]['Package'][0],
87       })
88     end
89
90     if p[:git_anon]
91       head_is_tagged = false
92       for tag in g.tags.reverse
93         p[:has_tags] = true
94         t = g.gcommit(tag.name)
95         next if not t.parent
96         #$stderr.puts "#{pkg}: Checking tag #{tag.name}: tag parent: #{t.parent.sha} HEAD: #{current_head.parent.sha}"
97         if t.parent.sha === current_head.parent.sha
98           head_is_tagged = true
99           p[:git_version] = tag.name
100           break
101         end
102       end
103       if !head_is_tagged
104         p[:problem] = 'Untagged changes'
105       end
106     else
107       p[:problem] = 'Unknown (Missing checkout)'
108     end
109
110     if !p[:problem]
111       if p[:git_version] and p[:repo_version]
112         repo_version = p[:repo_version].gsub('~','_')
113         if (p[:git_version] != repo_version) and (p[:git_version] != 'v'+repo_version)
114           p[:problem] = 'Git/Repo not in sync'
115         end
116       end
117     end
118
119     data[pkg] = p
120   end
121   data
122 end
123
124 def fetch_file(uri)
125   response = Net::HTTP.get_response(URI.parse(uri))
126   body = response.body
127   if uri.match(/\.gz$/)
128     body = Zlib::GzipReader.new(StringIO.new(body.to_s)).read
129   end
130   body
131 end
132
133 def update_git_repos(git_repos)
134   git_repos.each do |name, path|
135     out = %x{cd #{path} && git remote update --prune 2>&1}
136     puts "#{name}: " + out if DEBUG
137   end
138 end
139
140 template = ERB.new <<-EOF
141 <!doctype html>
142 <head>
143   <meta charset="utf-8">
144   <title>Grml.org Package Index</title>
145   <link rel="stylesheet" href="style.css">
146 </head>
147 <body>
148   <header>
149   <h1>All Grml packages</h1>
150   </header>
151   <div id="main">
152   <table>
153   <tr>
154   <th>Package</th><th>Git</th><th>Download</th><th>Git Version</th><th>grml-testing</th><th>In FULL?</th>
155   </tr>
156   <% packages.keys.sort.each do |pn|
157   p = packages[pn]
158   %>
159   <tr>
160   <td><%= p[:name] %></td>
161   <td class="git"><% if p[:git_browser] %><a href="<%= p[:git_browser] %>">Git</a><% end %></td>
162   <td class="download"><% if p[:repo_url] %><a href="<%= p[:repo_url] %>">Download</a><% end %></td>
163   <% if !p[:problem] %>
164   <td class="ok">Version <%= p[:git_version] %></td>
165   <% else %>
166   <td class="error <% if p[:used][:full] %>important<% end %>"><%= p[:problem] %></td>
167   <% end %>
168   <td><%= p[:repo_version] || "" %></td>
169   <td class="installed"><%= p[:used][:full] ? "Yes" : "No" %></td>
170   </tr>
171   <% end %>
172   </table>
173   </div>
174   <div>
175   </div>
176   <footer>
177   <p>Last update: <%= Time.now.to_s %></p>
178   </footer>
179 </body>
180 </html>
181 EOF
182
183 used_packages = {
184   :full => build_used_package_list([
185                                     'http://git.grml.org/?p=grml-live.git;a=blob_plain;f=etc/grml/fai/config/package_config/GRMLBASE',
186                                     'http://git.grml.org/?p=grml-live.git;a=blob_plain;f=etc/grml/fai/config/package_config/GRML_FULL',
187                                    ]),
188 }
189 sources = {}
190 parse_debian_sources(fetch_file('http://deb.grml.org/dists/grml-testing/main/source/Sources.gz')).each do |k,v|
191   if v['Vcs-Git'] and v['Vcs-Git'][0]
192     m = v['Vcs-Git'][0].match 'git.grml.org\/(.*).git$'
193     k = m[1] if m
194   end
195   sources[k] = v
196 end
197
198 git_repos = Hash[*(Dir.glob('git/*.git').map do |p| [File.basename(p, '.git'), p] end.flatten)]
199
200 update_git_repos git_repos
201
202 packages = build_package_list(git_repos, used_packages, sources)
203
204 File.open('index.html.new','w') do |f|
205   f.write template.result(binding)
206 end
207 File.open('packages.yaml.new','w') do |f|
208   f.write packages.to_yaml
209 end
210
211 FileUtils.mv 'index.html.new', 'index.html'
212 FileUtils.mv 'packages.yaml.new', 'packages.yaml'
213