source: tailor/vcpx/tailor.py @ 45

Revision 45, 8.0 KB checked in by lele@…, 9 years ago (diff)

Documentation on TailorizedProject?

Line 
1#! /usr/bin/python
2# -*- mode: python; coding: utf-8 -*-
3# :Progetto: vcpx -- Frontend capabilities
4# :Creato:   dom 04 lug 2004 00:40:54 CEST
5# :Autore:   Lele Gaifax <lele@nautilus.homeip.net>
6#
7
8"""
9Implement the basic capabilities of the frontend.
10
11This implementation stores the relevant project information, needed to
12keep the whole thing going on, such as the last synced revision, in a
13unversioned file named `tailor.info` at the root.
14"""
15
16__docformat__ = 'reStructuredText'
17
18from dualwd import DualWorkingDir
19
20STATUS_FILENAME = 'tailor.info'
21LOG_FILENAME = 'tailor.log'
22   
23class TailorizedProject(object):
24    """
25    A TailorizedProject has two main capabilities: it may be bootstrapped
26    from an upstream repository or brought in sync with current upstream
27    revision.
28    """
29   
30    def __init__(self, root):
31        import logging
32        from os import makedirs
33        from os.path import join, exists
34
35        self.root = root
36        if not exists(root):
37            makedirs(root)
38       
39        self.logger = logging.getLogger('tailor')
40        hdlr = logging.FileHandler(join(root, LOG_FILENAME))
41        formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s')
42        hdlr.setFormatter(formatter)
43        self.logger.addHandler(hdlr) 
44        self.logger.setLevel(logging.INFO)
45
46        self.source_kind = self.target_kind = None
47               
48    def __saveStatus(self):
49        """
50        Save relevant project information in a persistent way.
51        """
52       
53        from os.path import join, exists
54
55        statusfilename = join(self.root, STATUS_FILENAME)
56        f = open(statusfilename, 'w')
57        print >>f, self.source_kind
58        print >>f, self.target_kind       
59        print >>f, self.module       
60        print >>f, self.upstream_repos
61        print >>f, self.upstream_revision
62        f.close()
63       
64    def __loadStatus(self):
65        """
66        Load relevant project information.
67        """
68       
69        from os.path import join, exists
70
71        statusfilename = join(self.root, STATUS_FILENAME)
72        f = open(statusfilename)
73        (srck, dstk,
74         module, upstream_repos, upstream_revision) = f.readlines()
75        self.source_kind = srck[:-1]
76        self.target_kind = dstk[:-1]
77        self.module = module[:-1]
78        self.upstream_repos = upstream_repos[:-1]
79        self.upstream_revision = upstream_revision[:-1]
80        f.close()
81
82    def bootstrap(self, source_kind, target_kind,
83                  repository, module, revision):
84        """
85        Bootstrap a new tailorized module.
86
87        Extract a copy of the `repository` at given `revision` in the `root`
88        directory and initialize a target repository with its content.
89
90        The actual information on the project are stored in a text file.
91        """
92
93        from os.path import join
94       
95        self.logger.info("Bootstrapping '%s'" % (self.root,))
96
97        dwd = DualWorkingDir(source_kind, target_kind)
98        self.logger.info("getting %s revision '%s' of '%s' from '%s'" % (
99            source_kind, revision, module, repository))
100        actual = dwd.checkoutUpstreamRevision(self.root, repository,
101                                              module, revision)
102        self.logger.info("initializing %s shadow" % target_kind)
103        dwd.initializeNewWorkingDir(self.root, repository, actual)
104
105        self.source_kind = source_kind
106        self.target_kind = target_kind
107        self.upstream_repos = repository
108        self.module = module       
109        self.upstream_revision = actual
110
111        self.__saveStatus()
112
113        self.logger.info("Bootstrap completed")
114
115    def update(self):
116        """
117        Update an existing tailorized project.
118
119        Fetch the upstream changesets and apply them to the working copy.
120        Use the information stored in the `tailor.info` file to ask just
121        the new changeset since last bootstrap/synchronization.
122        """
123       
124        from os.path import join
125       
126        self.__loadStatus()
127
128        proj = join(self.root, self.module)
129        self.logger.info("Updating '%s' from revision '%s'" % (
130            proj, self.upstream_revision))
131       
132        dwd = DualWorkingDir(self.source_kind, self.target_kind)
133        actual,conflics = dwd.applyUpstreamChangesets(proj,
134                                                      self.upstream_revision)
135        if actual:
136            self.upstream_revision = actual.revision
137            self.__saveStatus()
138            self.logger.info("Update completed, now at revision '%s'" % (
139                self.upstream_revision,))
140        else:
141            self.logger.info("Update completed with no upstream changes")
142
143
144from optparse import OptionParser, OptionError, OptionGroup, make_option
145
146OPTIONS = [
147    make_option("-D", "--debug", dest="debug",
148                action="store_true", default=False),
149    make_option("-s", "--source-kind", dest="source_kind", metavar="VC-KIND",
150                help="Select the backend for the upstream source "
151                     "version control VC-KIND. Default is 'cvs'.",
152                default="cvs"),
153    make_option("-t", "--target-kind", dest="target_kind", metavar="VC-KIND",
154                help="Select VC-KIND as backend for the shadow repository, "
155                     "with 'darcs' as default.",
156                default="darcs"),   
157]   
158
159ACTIONS = [
160    make_option("-b", "--bootstrap", action="store_true", default=False,
161                help="Bootstrap mode, that is the initial copy of the "
162                     "upstream tree, given as an URI possibly followed "
163                     "by a revision."),
164    make_option("-i", "--info", action="store_true", default=False,
165                help="Just show ancestry information."),
166]
167
168class ExistingProjectError(Exception):
169    """
170    Raised when, in bootstrap mode, the directory for the project is already
171    there.
172    """
173
174class UnknownProjectError(Exception):
175    """
176    Raised when, in normal mode, the directory for the project does not
177    exist.
178    """
179
180class ProjectNotTailored(Exception):
181    """
182    Raised when trying to do something on a project that has not been
183    tailored.
184    """
185   
186def main():
187    """
188    Script entry point.
189
190    Parse the command line options and arguments, and for each
191    specified working copy directory (the current working directory by
192    default) execute the tailorization steps.
193    """
194   
195    from os import getcwd, chdir
196    from os.path import abspath, exists, join, split
197    from shwrap import SystemCommand
198   
199    parser = OptionParser(usage='%prog [options] [proj [URI[@revision]]]...',
200                          option_list=OPTIONS)
201    actions = OptionGroup(parser, "Other actions")
202    actions.add_options(ACTIONS)
203    parser.add_option_group(actions)
204   
205    options, args = parser.parse_args()
206   
207    SystemCommand.VERBOSE = options.debug
208   
209    base = getcwd()
210   
211    if len(args) == 0:
212        args.append(base)
213       
214    while args:
215        chdir(base)
216       
217        proj = args.pop(0)
218        root = abspath(proj)       
219        basedir, module = split(root)
220       
221        if options.bootstrap:
222            if exists(join(basedir, STATUS_FILENAME)):
223                raise ExistingProjectError(
224                    "Project %r cannot be bootstrapped twice" % proj)
225           
226            if not args:
227                raise OptionError('expected the source URI for %r' % proj,
228                                  '--bootstrap')
229            uri = args.pop(0)
230        else:               
231            uri = None
232           
233        if not options.bootstrap and not exists(proj):
234            raise UnknownProjectError("Project %r does not exist" % proj)
235           
236        tailored = TailorizedProject(basedir)
237       
238        if options.bootstrap:
239            if uri and '@' in uri:
240                uri, rev = uri.split('@')
241            else:
242                rev = 'HEAD'
243            tailored.bootstrap(options.source_kind, options.target_kind,
244                               uri, module, rev)
245        elif options.info:
246            pass
247        else:
248            tailored.update()
249            print
Note: See TracBrowser for help on using the repository browser.