source: tailor/vcpx/tailor.py @ 495

Revision 495, 14.2 KB checked in by lele@…, 8 years ago (diff)

ConfigParser? option values must be strings

Line 
1# -*- mode: python; coding: utf-8 -*-
2# :Progetto: vcpx -- Frontend capabilities
3# :Creato:   dom 04 lug 2004 00:40:54 CEST
4# :Autore:   Lele Gaifax <lele@nautilus.homeip.net>
5# :Licenza:  GNU General Public License
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
18__version__ = '0.9.2'
19
20from optparse import OptionParser, OptionGroup, make_option
21from config import Config
22from source import InvocationError
23from session import interactive
24
25class Tailorizer(object):
26    """
27    A Tailorizer has two main capabilities: its able to bootstrap a
28    new Project, or brought it in sync with its current upstream
29    revision.
30    """
31
32    def __init__(self, project):
33        self.project = project
34
35    def bootstrap(self):
36        """
37        Bootstrap a new tailorized module.
38
39        First of all prepare the target system working directory such
40        that it can host the upstream source tree. This is backend
41        specific.
42
43        Then extract a copy of the upstream repository and import its
44        content into the target repository.
45        """
46
47        self.project.log_info("Bootstrapping '%s'" % self.project.root)
48
49        try:
50            self.project.prepareWorkingDirectory()
51        except:
52            self.project.log_error('Cannot prepare working directory!', True)
53            raise
54
55        try:
56            self.project.checkoutUpstreamRevision()
57        except:
58            self.project.log_error("Checkout of '%s' failed!" %
59                                   self.project.name, True)
60            raise
61
62        self.project.log_info("Bootstrap completed")
63
64    def update(self):
65        """
66        Update an existing tailorized project.
67        """
68
69        self.project.log_info("Updating '%s'" % self.project.name)
70
71        try:
72            self.project.applyPendingChangesets()
73        except:
74            self.project.log_error("Cannot update '%s'!" % self.project.name,
75                                   True)
76            raise
77
78        self.project.log_info("Update completed")
79
80    def __call__(self, options):
81        from shwrap import ExternalCommand
82        from target import SyncronizableTargetWorkingDir
83        from changes import Changeset
84
85        def pconfig(option):
86            return self.project.config(self.project.name, option)
87
88        ExternalCommand.VERBOSE = pconfig('debug')
89        encoding = pconfig('encoding')
90        if encoding:
91            ExternalCommand.FORCE_ENCODING = encoding
92
93            # Make printouts be encoded as well. A better solution would be
94            # using the replace mechanism of the encoder, and keep printing
95            # in the user LC_CTYPE/LANG setting.
96
97            import codecs, sys
98            sys.stdout = codecs.getwriter(encoding)(sys.stdout)
99
100        pname_format = pconfig('patch-name-format')
101        if pname_format is not None:
102            SyncronizableTargetWorkingDir.PATCH_NAME_FORMAT = pname_format
103        SyncronizableTargetWorkingDir.REMOVE_FIRST_LOG_LINE = pconfig('remove-first-log-line')
104        Changeset.REFILL_MESSAGE = not pconfig('dont-refill-changelogs')
105
106        if options.bootstrap:
107            self.bootstrap()
108        else:
109            self.update()
110
111GENERAL_OPTIONS = [
112    make_option("-i", "--interactive", default=False, action="store_true",
113                help="Start an interactive session."),
114    make_option("-D", "--debug", dest="debug",
115                action="store_true", default=False,
116                help="Print each executed command. This also keeps "
117                     "temporary files with the upstream logs, that are "
118                     "otherwise removed after use."),
119    make_option("-v", "--verbose", dest="verbose",
120                action="store_true", default=False,
121                help="Be verbose, echoing the changelog of each applied "
122                     "changeset to stdout."),
123    make_option("--configfile", metavar="CONFNAME",
124                help="Centralized storage of projects info.  With this "
125                     "option and no other arguments tailor will update "
126                     "every project found in the config file."),
127    make_option("--migrate-config", dest="migrate",
128                action="store_true", default=False,
129                help="Migrate old configuration to new centralized storage."),
130    make_option("--encoding", metavar="CHARSET", default=None,
131                help="Force the output encoding to given CHARSET, rather "
132                     "then using the user default settings specified in the "
133                     "environment."),
134]
135
136UPDATE_OPTIONS = [
137    make_option("--update", action="store_true", default=True,
138                help="Update the given repositories, fetching upstream "
139                     "changesets, applying and re-registering each one. "
140                     "This is the default behaviour."),
141    make_option("-S", "--single-commit", action="store_true", default=False,
142                help="Do a single, final commit on the target VC, effectively "
143                     "grouping together all upstream changeset into a single "
144                     "one, from the target VC point of view."),
145    make_option("-C", "--concatenate-logs", action="store_true", default=False,
146                help="With --single-commit, concatenate each changeset "
147                     "message log to the final changelog, instead of just "
148                     "the name of the patch."),
149    make_option("-F", "--patch-name-format", metavar="FORMAT",
150                help="Specify the prototype that will be used "
151                     "to compute the patch name.  The prototype may contain "
152                     "%(keyword)s such as 'module', 'author', 'date', "
153                     "'revision', 'firstlogline', 'remaininglog' for normal "
154                     "updates, otherwise 'module', 'authors', 'nchangesets', "
155                     "'mindate' and 'maxdate' when using --single-commit. It "
156                     "defaults to '%(module)s: changeset %(revision)s'; "
157                     "setting it to the empty string means that tailor will "
158                     "simply use the original changelog."),
159    make_option("-1", "--remove-first-log-line", action="store_true",
160                default=False,
161                help="Remove the first line of the upstream changelog. This "
162                     "is intended to go in pair with --patch-name-format, "
163                     "when using it's 'firstlogline' variable to build the "
164                     "name of the patch."),
165    make_option("-N", "--dont-refill-changelogs", action="store_true",
166                default=False,
167                help="Do not refill every changelog, but keep them as is. "
168                     "This is usefull when using --patch-name-format, or "
169                     "when upstream developers are already formatting their "
170                     "notes with a consistent layout."),
171]
172
173BOOTSTRAP_OPTIONS = [
174    make_option("-b", "--bootstrap", action="store_true", default=False,
175                help="Bootstrap mode, that is the initial copy of the "
176                     "upstream tree, given as an URI (see -R) and maybe "
177                     "a revision (-r).  This overrides --update."),
178    make_option("-s", "--source-kind", dest="source_kind", metavar="VC-KIND",
179                help="Select the backend for the upstream source "
180                     "version control VC-KIND. Default is 'cvs'.",
181                default="cvs"),
182    make_option("-t", "--target-kind", dest="target_kind", metavar="VC-KIND",
183                help="Select VC-KIND as backend for the shadow repository, "
184                     "with 'darcs' as default.",
185                default="darcs"),
186    make_option("-R", "--repository", "--source-repository",
187                dest="source_repository", metavar="REPOS",
188                help="Specify the upstream repository, from where bootstrap "
189                     "will checkout the module.  REPOS syntax depends on "
190                     "the source version control kind."),
191    make_option("-m", "--module", "--source-module", dest="source_module",
192                metavar="MODULE",
193                help="Specify the module to checkout at bootstrap time. "
194                     "This has different meanings under the various upstream "
195                     "systems: with CVS it indicates the module, while under "
196                     "SVN it's the prefix of the tree you want and must begin "
197                     "with a slash. Since it's used in the description of the "
198                     "target repository, you may want to give it a value with "
199                     "darcs too even if it is otherwise ignored."),
200    make_option("-r", "--revision", "--start-revision", dest="start_revision",
201                metavar="REV",
202                help="Specify the revision bootstrap should checkout.  REV "
203                     "must be a valid 'name' for a revision in the upstream "
204                     "version control kind. For CVS it may be either a branch "
205                     "name, a timestamp or both separated by a space, and "
206                     "timestamp may be 'INITIAL' to denote the beginning of "
207                     "time for the given branch. Under Darcs, INITIAL is a "
208                     "shortcut for the name of the first patch in the upstream "
209                     "repository, otherwise it is interpreted as the name of "
210                     "a tag. Under Subversion, 'INITIAL' is the first patch "
211                     "that touches given repos/module, otherwise it must be "
212                     "an integer revision number. "
213                     "'HEAD', the default, means the latest version in all "
214                     "backends.",
215                default="HEAD"),
216    make_option("-T", "--target-repository",
217                dest="target_repository", metavar="REPOS", default=None,
218                help="Specify the target repository, the one that will "
219                     "receive the patches coming from the source one."),
220    make_option("-M", "--target-module", dest="target_module",
221                metavar="MODULE",
222                help="Specify the module on the target repository that will "
223                     "actually contain the upstream source tree."),
224    make_option("--subdir", metavar="DIR",
225                help="Force the subdirectory where the checkout will happen, "
226                     "by default it's the tail part of the module name."),
227]
228
229VC_SPECIFIC_OPTIONS = [
230    make_option("--use-svn-propset", action="store_true", default=False,
231                help="Use 'svn propset' to set the real date and author of "
232                     "each commit, instead of appending these information to "
233                     "the changelog. This requires some tweaks on the SVN "
234                     "repository to enable revision propchanges."),
235]
236
237class ExistingProjectError(Exception):
238    "Project seems already tailored"
239
240class ProjectNotTailored(Exception):
241    "Not a tailored project"
242
243def main():
244    """
245    Script entry point.
246
247    Parse the command line options and arguments, and for each
248    specified working copy directory (the current working directory by
249    default) execute the tailorization steps.
250    """
251
252    from os import getcwd
253
254    parser = OptionParser(usage='%prog [options] [project ...]',
255                          version=__version__,
256                          option_list=GENERAL_OPTIONS)
257
258    bsoptions = OptionGroup(parser, "Bootstrap options")
259    bsoptions.add_options(BOOTSTRAP_OPTIONS)
260
261    upoptions = OptionGroup(parser, "Update options")
262    upoptions.add_options(UPDATE_OPTIONS)
263
264    vcoptions = OptionGroup(parser, "VC specific options")
265    vcoptions.add_options(VC_SPECIFIC_OPTIONS)
266
267    parser.add_option_group(bsoptions)
268    parser.add_option_group(upoptions)
269    parser.add_option_group(vcoptions)
270
271    options, args = parser.parse_args()
272
273    if options.interactive:
274        interactive(options, args)
275    else:
276        defaults = {}
277        for k,v in options.__dict__.items():
278            if k not in ['interactive', 'bootstrap', 'configfile']:
279                defaults[k.replace('_', '-')] = str(v)
280
281        if options.configfile:
282            config = Config(open(options.configfile), defaults)
283
284            if not args:
285                args = config.projects()
286
287            for projname in args:
288                project = config[projname]
289                tailorizer = Tailorizer(project)
290                tailorizer(options)
291        else:
292            config = Config(None, defaults)
293
294            config.add_section('project')
295            source = options.source_kind + ':source'
296            config.set('project', 'source', source)
297            target = options.target_kind + ':target'
298            config.set('project', 'target', target)
299            config.set('project', 'root', getcwd())
300            config.set('project', 'subdir', options.subdir or '.')
301            config.set('project', 'state-file', 'tailor.state')
302            config.set('project', 'start-revision', options.start_revision)
303
304            config.add_section(source)
305            if options.bootstrap and not options.source_repository:
306                raise InvocationError('Need a source repository to bootstrap')
307
308            config.set(source, 'repository', options.source_repository)
309            if options.source_module:
310                config.set(source, 'module', options.source_module)
311
312            config.add_section(target)
313            if options.target_repository:
314                config.set(target, 'repository', options.target_repository)
315            if options.target_module:
316                config.set(target, 'module', options.target_module)
317
318            if options.verbose:
319                import sys
320
321                sys.stderr.write("You should put the following configuration "
322                                 "in some file, adjust it as needed\n"
323                                 "and use --configfile option with that "
324                                 "file as argument:\n")
325                config.write(sys.stdout)
326
327            if not options.debug:
328                project = config['project']
329                tailorizer = Tailorizer(project)
330                tailorizer(options)
331            else:
332                sys.stderr.write("Operation not performed, given --debug\n")
Note: See TracBrowser for help on using the repository browser.