source: tailor/vcpx/tailor.py @ 120

Revision 120, 10.6 KB checked in by lele@…, 9 years ago (diff)

Use the last component of the module's name

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, split
34
35        self.root = root
36        if not exists(root):
37            makedirs(root)
38       
39        self.logger = logging.getLogger('tailor.%s' % split(root)[1])
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 split
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                                              logger=self.logger)
103
104        self.logger.info("initializing %s shadow" % target_kind)
105       
106        # the above machinery checked out a copy under of the wc
107        # in the directory named as the last component of the module's name
108
109        dwd.initializeNewWorkingDir(self.root, repository,
110                                    split(module)[1], actual)
111
112        self.source_kind = source_kind
113        self.target_kind = target_kind
114        self.upstream_repos = repository
115        self.module = module       
116        self.upstream_revision = actual
117
118        self.__saveStatus()
119
120        self.logger.info("Bootstrap completed")
121
122    def applied(self, root, changeset):
123        """
124        Save current status.
125        """
126
127        self.upstream_revision = changeset.revision
128        self.__saveStatus()
129        print "# Applied changeset %s" % changeset.revision
130
131    def update(self, single_commit, concatenate_logs):
132        """
133        Update an existing tailorized project.
134
135        Fetch the upstream changesets and apply them to the working copy.
136        Use the information stored in the `tailor.info` file to ask just
137        the new changeset since last bootstrap/synchronization.
138        """
139       
140        from os.path import join, split
141       
142        self.__loadStatus()
143
144        proj = join(self.root, split(self.module)[1])
145        self.logger.info("Updating '%s' from revision '%s'" % (
146            self.module, self.upstream_revision))
147       
148        print "\nUpdating '%s' from revision '%s'" % (self.module,
149                                                      self.upstream_revision)
150       
151        dwd = DualWorkingDir(self.source_kind, self.target_kind)
152        actual,conflicts = dwd.applyUpstreamChangesets(
153            proj, self.upstream_revision, applied=self.applied,
154            logger=self.logger, delayed_commit=single_commit)
155        if actual:
156            if single_commit:
157                dwd.commitDelayedChangesets(proj, concatenate_logs)
158
159            self.logger.info("Update completed, now at revision '%s'" % (
160                self.upstream_revision,))
161        else:
162            self.logger.info("Update completed with no upstream changes")
163
164
165from optparse import OptionParser, OptionError, OptionGroup, make_option
166
167GENERAL_OPTIONS = [
168    make_option("-D", "--debug", dest="debug",
169                action="store_true", default=False,
170                help="Print each executed command."),
171   
172]   
173
174UPDATE_OPTIONS = [
175    make_option("--update", action="store_true", default=True,
176                help="Update the given repositories, fetching upstream "
177                     "changesets, applying and re-registering each one. "
178                     "This is the default behaviour."),
179    make_option("-S", "--single-commit", action="store_true", default=False,
180                help="Do a single, final commit on the target VC, effectively "
181                     "grouping together all upstream changeset into a single "
182                     "one, from the target VC point of view."),
183    make_option("-C", "--concatenate-logs", action="store_true", default=False,
184                help="With --single-commit, concatenate each changeset "
185                     "message log to the final changelog, instead of just "
186                     "the name of the patch."),
187]
188
189BOOTSTRAP_OPTIONS = [
190    make_option("-b", "--bootstrap", action="store_true", default=False,
191                help="Bootstrap mode, that is the initial copy of the "
192                     "upstream tree, given as an URI (see -R) and maybe "
193                     "a revision (-r).  This overrides --update."),
194    make_option("-s", "--source-kind", dest="source_kind", metavar="VC-KIND",
195                help="Select the backend for the upstream source "
196                     "version control VC-KIND. Default is 'cvs'.",
197                default="cvs"),
198    make_option("-t", "--target-kind", dest="target_kind", metavar="VC-KIND",
199                help="Select VC-KIND as backend for the shadow repository, "
200                     "with 'darcs' as default.",
201                default="darcs"),
202    make_option("-R", "--repository", dest="repository", metavar="REPOS",
203                help="Specify the upstream repository, from where bootstrap "
204                     "will checkout the module.  REPOS syntax depends on "
205                     "the source version control kind."),
206    make_option("-m", "--module", dest="module", metavar="MODULE",
207                help="Specify the module to checkout at bootstrap time."),
208    make_option("-r", "--revision", dest="revision", metavar="REV",
209                help="Specify the revision bootstrap should checkout.  REV "
210                     "must be a valid 'name' for a revision in the upstream "
211                     "version control kind.  For CVS it may be a tag/branch. "
212                     "'HEAD', the default, means the latest version in all "
213                     "backends.",
214                default="HEAD"),
215]
216
217class ExistingProjectError(Exception):
218    """
219    Raised when, in bootstrap mode, the directory for the project is already
220    there.
221    """
222
223class UnknownProjectError(Exception):
224    """
225    Raised when, in normal mode, the directory for the project does not
226    exist.
227    """
228
229class ProjectNotTailored(Exception):
230    """
231    Raised when trying to do something on a project that has not been
232    tailored.
233    """
234   
235def main():
236    """
237    Script entry point.
238
239    Parse the command line options and arguments, and for each
240    specified working copy directory (the current working directory by
241    default) execute the tailorization steps.
242    """
243   
244    from os import getcwd, chdir
245    from os.path import abspath, exists, join, split
246    from shwrap import SystemCommand
247   
248    parser = OptionParser(usage='%prog [options] [project ...]',
249                          option_list=GENERAL_OPTIONS)
250   
251    bsoptions = OptionGroup(parser, "Bootstrap options")
252    bsoptions.add_options(BOOTSTRAP_OPTIONS)
253
254    upoptions = OptionGroup(parser, "Update options")
255    upoptions.add_options(UPDATE_OPTIONS)
256   
257    parser.add_option_group(bsoptions)
258    parser.add_option_group(upoptions)
259   
260    options, args = parser.parse_args()
261   
262    SystemCommand.VERBOSE = options.debug
263   
264    base = getcwd()
265   
266    if len(args) == 0:
267        args.append(base)
268       
269    while args:
270        chdir(base)
271       
272        proj = args.pop(0)
273        root = abspath(proj)
274
275        if options.bootstrap:
276            if exists(join(root, STATUS_FILENAME)):
277                raise ExistingProjectError(
278                    "Project %r cannot be bootstrapped twice" % proj)
279           
280            if not options.repository:
281                raise OptionError('Need a repository to bootstrap %r' % proj)
282        else:
283            if not exists(proj):
284                raise UnknownProjectError("Project %r does not exist" % proj)
285           
286            if not exists(join(root, STATUS_FILENAME)):
287                raise UnknownProjectError(
288                    "%r is not a tailorized project" % proj)
289           
290        tailored = TailorizedProject(root)
291
292        if options.bootstrap:
293            tailored.bootstrap(options.source_kind, options.target_kind,
294                               options.repository,
295                               options.module,
296                               options.revision)
297        elif options.update:
298            tailored.update(options.single_commit, options.concatenate_logs)
299
Note: See TracBrowser for help on using the repository browser.