Reported bug with utf-8 strings
[be.git] / libbe / upgrade.py
1 # Copyright (C) 2009 W. Trevor King <wking@drexel.edu>
2 #
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License along
14 # with this program; if not, write to the Free Software Foundation, Inc.,
15 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16
17 """
18 Handle conversion between the various on-disk images.
19 """
20
21 import os, os.path
22 import sys
23 import doctest
24
25 import encoding
26 import mapfile
27 import vcs
28
29 # a list of all past versions
30 BUGDIR_DISK_VERSIONS = ["Bugs Everywhere Tree 1 0",
31                         "Bugs Everywhere Directory v1.1",
32                         "Bugs Everywhere Directory v1.2"]
33
34 # the current version
35 BUGDIR_DISK_VERSION = BUGDIR_DISK_VERSIONS[-1]
36
37 class Upgrader (object):
38     "Class for converting "
39     initial_version = None
40     final_version = None
41     def __init__(self, root):
42         self.root = root
43         # use the "None" VCS to ensure proper encoding/decoding and
44         # simplify path construction.
45         self.vcs = vcs.vcs_by_name("None")
46         self.vcs.root(self.root)
47         self.vcs.encoding = encoding.get_encoding()
48
49     def get_path(self, *args):
50         """
51         Return a path relative to .root.
52         """
53         dir = os.path.join(self.root, ".be")
54         if len(args) == 0:
55             return dir
56         assert args[0] in ["version", "settings", "bugs"], str(args)
57         return os.path.join(dir, *args)
58
59     def check_initial_version(self):
60         path = self.get_path("version")
61         version = self.vcs.get_file_contents(path).rstrip("\n")
62         assert version == self.initial_version, version
63
64     def set_version(self):
65         path = self.get_path("version")
66         self.vcs.set_file_contents(path, self.final_version+"\n")
67
68     def upgrade(self):
69         print >> sys.stderr, "upgrading bugdir from '%s' to '%s'" \
70             % (self.initial_version, self.final_version)
71         self.check_initial_version()
72         self.set_version()
73         self._upgrade()
74
75     def _upgrade(self):
76         raise NotImplementedError
77
78
79 class Upgrade_1_0_to_1_1 (Upgrader):
80     initial_version = "Bugs Everywhere Tree 1 0"
81     final_version = "Bugs Everywhere Directory v1.1"
82     def _upgrade_mapfile(self, path):
83         contents = self.vcs.get_file_contents(path)
84         old_format = False
85         for line in contents.splitlines():
86             if len(line.split("=")) == 2:
87                 old_format = True
88                 break
89         if old_format == True:
90             # translate to YAML.
91             newlines = []
92             for line in contents.splitlines():
93                 line = line.rstrip('\n')
94                 if len(line) == 0:
95                     continue
96                 fields = line.split("=")
97                 if len(fields) == 2:
98                     key,value = fields
99                     newlines.append('%s: "%s"' % (key, value.replace('"','\\"')))
100                 else:
101                     newlines.append(line)
102             contents = '\n'.join(newlines)
103             # load the YAML and save
104             map = mapfile.parse(contents)
105             mapfile.map_save(self.vcs, path, map)
106
107     def _upgrade(self):
108         """
109         Comment value field "From" -> "Author".
110         Homegrown mapfile -> YAML.
111         """
112         path = self.get_path("settings")
113         self._upgrade_mapfile(path)
114         for bug_uuid in os.listdir(self.get_path("bugs")):
115             path = self.get_path("bugs", bug_uuid, "values")
116             self._upgrade_mapfile(path)
117             c_path = ["bugs", bug_uuid, "comments"]
118             if not os.path.exists(self.get_path(*c_path)):
119                 continue # no comments for this bug
120             for comment_uuid in os.listdir(self.get_path(*c_path)):
121                 path_list = c_path + [comment_uuid, "values"]
122                 path = self.get_path(*path_list)
123                 self._upgrade_mapfile(path)
124                 settings = mapfile.map_load(self.vcs, path)
125                 if "From" in settings:
126                     settings["Author"] = settings.pop("From")
127                     mapfile.map_save(self.vcs, path, settings)
128
129
130 class Upgrade_1_1_to_1_2 (Upgrader):
131     initial_version = "Bugs Everywhere Directory v1.1"
132     final_version = "Bugs Everywhere Directory v1.2"
133     def _upgrade(self):
134         """
135         BugDir settings field "rcs_name" -> "vcs_name".
136         """
137         path = self.get_path("settings")
138         settings = mapfile.map_load(self.vcs, path)
139         if "rcs_name" in settings:
140             settings["vcs_name"] = settings.pop("rcs_name")
141             mapfile.map_save(self.vcs, path, settings)
142
143
144 upgraders = [Upgrade_1_0_to_1_1,
145              Upgrade_1_1_to_1_2]
146 upgrade_classes = {}
147 for upgrader in upgraders:
148     upgrade_classes[(upgrader.initial_version,upgrader.final_version)]=upgrader
149
150 def upgrade(path, current_version,
151             target_version=BUGDIR_DISK_VERSION):
152     """
153     Call the appropriate upgrade function to convert current_version
154     to target_version.  If a direct conversion function does not exist,
155     use consecutive conversion functions.
156     """
157     if current_version not in BUGDIR_DISK_VERSIONS:
158         raise NotImplementedError, \
159             "Cannot handle version '%s' yet." % version
160     if target_version not in BUGDIR_DISK_VERSIONS:
161         raise NotImplementedError, \
162             "Cannot handle version '%s' yet." % version
163
164     if (current_version, target_version) in upgrade_classes:
165         # direct conversion
166         upgrade_class = upgrade_classes[(current_version, target_version)]
167         u = upgrade_class(path)
168         u.upgrade()
169     else:
170         # consecutive single-step conversion
171         i = BUGDIR_DISK_VERSIONS.index(current_version)
172         while True:
173             version_a = BUGDIR_DISK_VERSIONS[i]
174             version_b = BUGDIR_DISK_VERSIONS[i+1]
175             try:
176                 upgrade_class = upgrade_classes[(version_a, version_b)]
177             except KeyError:
178                 raise NotImplementedError, \
179                     "Cannot convert version '%s' to '%s' yet." \
180                     % (version_a, version_b)
181             u = upgrade_class(path)
182             u.upgrade()
183             if version_b == target_version:
184                 break
185             i += 1
186
187 suite = doctest.DocTestSuite()