520c71604e471f8577bd6935069695f012077234
[build-farm.git] / buildfarm / history.py
1 #!/usr/bin/python
2 # script to show recent checkins in cvs / svn / git
3 #
4 # Copyright (C) Andrew Tridgell <tridge@samba.org>     2001
5 # Copyright (C) Martin Pool <mbp@samba.org>            2003
6 # Copyright (C) Jelmer Vernooij <jelmer@samba.org>     2007-2010
7 #
8 #   This program is free software; you can redistribute it and/or modify
9 #   it under the terms of the GNU General Public License as published by
10 #   the Free Software Foundation; either version 2 of the License, or
11 #   (at your option) any later version.
12 #
13 #   This program is distributed in the hope that it will be useful,
14 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #   GNU General Public License for more details.
17 #
18 #   You should have received a copy of the GNU General Public License
19 #   along with this program; if not, write to the Free Software
20 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22
23 from buildfarm import util
24
25 import commands
26 import os
27 import time
28
29 BASEDIR = "/home/build/master"
30 HISTORYDIR = "/home/build/master/cache"
31 TIMEZONE = "PST"
32 TIMEOFFSET = 0
33 UNPACKED_DIR = "/home/ftp/pub/unpacked"
34
35 CVSWEB_BASE = "http://pserver.samba.org/cgi-bin/cvsweb"
36 VIEWCVS_BASE = "http://websvn.samba.org/cgi-bin/viewcvs.cgi"
37 UNPACKED_BASE = "http://svn.samba.org/ftp/unpacked"
38 GITWEB_BASE = "http://gitweb.samba.org"
39
40
41 class History(object):
42
43     def __init__(self, db):
44         self.db = db
45
46     def _log(self, tree):
47         return util.LoadStructure(os.path.join(HISTORYDIR, "history.%s" % tree))
48
49     def diff(self, author, date, tree, revision):
50         """get recent cvs/svn entries"""
51         # validate the tree
52         t = self.db.trees[tree]
53
54         if t["scm"] == "cvs":
55             self._cvs_diff(t, author, date, tree)
56         elif t["scm"] == "svn":
57             self._svn_diff(t, revision, tree)
58         elif t["scm"] == "git":
59             self._git_diff(t, revision, tree)
60         else:
61             raise Exception("Unknown VCS %s" % t["scm"])
62
63     def _svn_diff(self, t, revision, tree):
64         """show recent svn entries"""
65
66         os.chdir(os.path.join(UNPACKED_DIR, tree))
67
68         # determine the most recent version known to this database
69         for l in commands.getoutput("svn info").splitlines():
70             if l.startswith("Revision"):
71                 current_revision = l.strip().split(":")
72                 break
73         else:
74             raise Exception("Unable to find current revision")
75
76         if (not revision.isdigit() or int(revision) < 0 or
77             int(revision) > int(current_revision)):
78             raise Exception("unknown revision[%s]" % revision)
79
80         log = self._log(tree)
81
82         # backwards? why? well, usually our users are looking for the newest
83         # stuff, so it's most likely to be found sooner
84         for i in range(len(log), 0, -1):
85             if log[i]["REVISION"] == revision:
86                 entry = log[i]
87                 break
88         else:
89             raise Exception("Unable to locate commit information revision[%s]." % revision)
90
91         # get information about the current diff
92         title = "SVN Diff in %s:%s for revision r%s" % (
93             tree, t["branch"], revision)
94
95         old_revision = revision - 1
96         cmd = "svn diff -r %s:%s" % (old_revision, revision)
97
98         return (title, entry, tree, [(cmd, commands.getoutput("%s 2> /dev/null" % cmd))])
99
100     def _cvs_diff(self, t, author, date, tree):
101         """show recent cvs entries"""
102         os.chdir(os.path.join(UNPACKED_DIR, tree))
103
104         log = self._log(tree)
105
106         # for paranoia, check that the date string is a valid date
107         if not date[0].isdigit():
108             raise Exception("unknown date")
109
110         for i in range(log):
111             if author == log[i]["AUTHOR"] and date == log[i]["DATE"]:
112                 entry = log[i]
113                 break
114         else:
115             raise Exception("Unable to locate commit information author[%s] data[%s]." % (
116                 author, date))
117
118         t1 = time.ctime(date-60+(TIMEOFFSET*60*60)).strip()
119         t2 = time.ctime(date+60+(TIMEOFFSET*60*60)).strip()
120
121         title = "CVS Diff in %s:%s for %s" % (tree, t["branch"], t1)
122
123         if entry["TAG"] != "" and entry["REVISIONS"] != "":
124             raise Exception("sorry, cvs diff on branches not currently possible due to a limitation in cvs")
125
126         os.environ['CVS_PASSFILE'] = os.path.join(BASEDIR, ".cvspass")
127
128         if entry["REVISIONS"]:
129             diffs = []
130             for f in entry["REVISIONS"].keys():
131                 if entry["REVISIONS"][f]["REV1"] == "NONE":
132                     cmd = "cvs rdiff -u -r 0 -r %s %s" % (entry["REVISIONS"][f]["REV2"], f)
133                 elif entry["REVISIONS"][f]["REV2"] == "NONE":
134                     cmd = "cvs rdiff -u -r %s -r 0 %s" % (
135                         entry["REVISIONS"][f]["REV1"], f)
136                 else:
137                     cmd = "cvs diff -b -u -r %s -r %s %s" % (
138                         entry["REVISIONS"][f]["REV1"], entry["REVISIONS"][f]["REV2"], f)
139
140                 diffs.append((cmd, commands.getoutput("%s 2> /dev/null" % cmd)))
141         else:
142             cmd = "cvs diff -b -u -D \"%s %s\" -D \"%s %s\" %s" % (
143                 t1, TIMEZONE, t2, TIMEZONE, entry["FILES"])
144
145             diffs = [(cmd, commands.getoutput("%s 2> /dev/null" % cmd))]
146         return (title, entry, tree, diffs)
147
148     def _git_diff(self, t, revision, tree):
149         """show recent git entries"""
150         os.chdir(os.path.join(UNPACKED_DIR, tree))
151
152         log = self._log(tree)
153
154         # backwards? why? well, usually our users are looking for the newest
155         # stuff, so it's most likely to be found sooner
156         for i in range(len(log), 0, -1):
157             if log[i]["REVISION"] == revision:
158                 entry = log[i]
159                 break
160         else:
161             raise Exception("Unable to locate commit information revision[%s]." % revision)
162
163         # get information about the current diff
164         title = "GIT Diff in %s:%s for revision %s" % (
165             tree, t["branch"], revision)
166
167         cmd = "git diff %s^ %s ./" % (revision, revision)
168         return (title, entry, tree, [(cmd, commands.getoutput("%s 2> /dev/null" % cmd))])
169
170     def authors(self, tree):
171         log = self._log(tree)
172         authors = set()
173         for entry in log:
174             authors.add(entry["AUTHOR"])
175         return authors
176
177     def history(self, tree, author=None):
178         """get commit history for the given tree"""
179         log = self._log(tree)
180
181         # what? backwards? why is that? oh... I know... we want the newest first
182         for i in range(len(log), 0, -1):
183             entry = log[i]
184             if (author is None or
185                 (author == "") or
186                 (author == "ALL") or
187                 (author == entry["AUTHOR"])):
188                 yield entry, tree