~ubuntu-branches/ubuntu/natty/quickly/natty

« back to all changes in this revision

Viewing changes to bin/quickly

  • Committer: Bazaar Package Importer
  • Author(s): Didier Roche
  • Date: 2009-07-24 18:16:30 UTC
  • Revision ID: james.westby@ubuntu.com-20090724181630-s2wo7hvahk8u0hqb
Tags: 0.1
Initial release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
# -*- coding: utf-8 -*-
 
3
# Copyright 2009 Canonical Ltd.
 
4
# Author 2009 Didier Roche
 
5
#
 
6
# This file is part of Quickly
 
7
#
 
8
#This program is free software: you can redistribute it and/or modify it 
 
9
#under the terms of the GNU General Public License version 3, as published 
 
10
#by the Free Software Foundation.
 
11
 
 
12
#This program is distributed in the hope that it will be useful, but 
 
13
#WITHOUT ANY WARRANTY; without even the implied warranties of 
 
14
#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 
 
15
#PURPOSE.  See the GNU General Public License for more details.
 
16
 
 
17
#You should have received a copy of the GNU General Public License along 
 
18
#with this program.  If not, see <http://www.gnu.org/licenses/>.
 
19
 
 
20
 
 
21
import os, re
 
22
import sys
 
23
import subprocess
 
24
import gettext
 
25
from gettext import gettext as _
 
26
 
 
27
# add quickly root directory (especially for trunk execution)
 
28
pathname = os.path.dirname(sys.argv[0])
 
29
quickly_root_directory = pathname + '/..'
 
30
quickly_root_directory =  os.path.abspath(quickly_root_directory)
 
31
if os.path.exists(quickly_root_directory + '/quickly') and quickly_root_directory not in sys.path:
 
32
    sys.path.insert(0, quickly_root_directory)
 
33
    os.putenv('PYTHONPATH', quickly_root_directory) # for subprocesses
 
34
 
 
35
import quickly
 
36
from quickly import commands, configurationhandler, tools, quicklyconfig
 
37
 
 
38
command_followed_by_template = ('create', 'quickly')
 
39
 
 
40
def usage():
 
41
    print _("""Usage:
 
42
    quickly [OPTIONS] command ...
 
43
 
 
44
Options:
 
45
    -t, --template <template>  Template to use if it differs from default
 
46
                               project one
 
47
                               one used to create the project)
 
48
    --staging                  Target launchpad staging server
 
49
    --verbose                  Verbose mode
 
50
    -h, --help                 Show help information
 
51
 
 
52
Commands:
 
53
    create <template> <project-name> (template is mandatory for this command)
 
54
    quickly <template_origin> <template_dest> to create a create derived template
 
55
    getstarted to get some starting hints
 
56
 
 
57
Examples:
 
58
    quickly create ubuntu-project foobar
 
59
    quickly push 'awesome new comment system'
 
60
    quickly -t cool-template push 'awesome new comment system'""")
 
61
 
 
62
def show_version():
 
63
    """ print version information """
 
64
    
 
65
    
 
66
    print _("""Quickly %s
 
67
  Python interpreter: %s %s
 
68
  Python standard library: /usr/lib/python2.6
 
69
  
 
70
  Quickly used library: %s
 
71
  Quickly data path: %s
 
72
  Quickly detected template directories:
 
73
          %s
 
74
 
 
75
Copyright 2009 Canonical Ltd.
 
76
  #Author 2009 Rick Spencer
 
77
  #Author 2009 Didier Roche
 
78
https://launchpad.net/quickly
 
79
 
 
80
quickly comes with ABSOLUTELY NO WARRANTY. quickly is free software, and
 
81
you may use, modify and redistribute it under the terms of the GNU
 
82
General Public License version 3 or later.""") % (
 
83
    quicklyconfig.__version__, sys.executable, ".".join([str(x) for x in sys.version_info[0:3]]),
 
84
    os.path.dirname(quickly.__file__), tools.get_quickly_data_path(),
 
85
    "\n          ".join(tools.get_template_directories()))
 
86
    sys.exit(0)
 
87
 
 
88
def get_commands():
 
89
    """seek for available commands for shell completion
 
90
 
 
91
    : return tuples with list of available commands and origin (default or template)
 
92
    """
 
93
 
 
94
    available_completion = []
 
95
 
 
96
    # option completion
 
97
    if sys.argv[-1].startswith("-"):
 
98
        options = ("-h", "--help", "-t", "--template", "--staging", "--verbose", "--version")
 
99
        available_completion = [option for option in options if option.startswith(sys.argv[-1])]
 
100
 
 
101
    else:
 
102
        (opt_command, need_template, opt_has_template, opt_template, 
 
103
          out_off_project_tree_command) = process_command_line(sys.argv[3:])
 
104
 
 
105
        # seek for template if exists
 
106
        if not opt_template:
 
107
            try:
 
108
                project_dir = tools.get_root_project_path()
 
109
                configurationhandler.loadConfig()
 
110
                opt_template = configurationhandler.project_config['template']
 
111
            except tools.project_path_not_found:
 
112
                pass
 
113
 
 
114
        # get available templates
 
115
        if sys.argv[-2] in ("-t", "--template"):
 
116
            for template_dir in tools.get_template_directories():
 
117
                    for template in os.listdir(template_dir):
 
118
                        available_completion.append(os.path.basename(template))
 
119
 
 
120
        elif len(opt_command) > 1:
 
121
            # if no template, check the create command for templates proposal or -t completion
 
122
            # (-1 is space or template begining)
 
123
            if (opt_command[len(opt_command) - 2] in command_followed_by_template and not opt_template):
 
124
                for template_dir in tools.get_template_directories():
 
125
                    for template in os.listdir(template_dir):
 
126
                        available_completion.append(os.path.basename(template))
 
127
        else:
 
128
            # get the template path if exists
 
129
            if opt_template:
 
130
                template_path = tools.get_template_directory(opt_template)
 
131
                files = []
 
132
                for fullname in os.listdir(template_path):
 
133
                    files.append(os.path.basename(fullname))
 
134
 
 
135
                for found_file in files:
 
136
                    command = re.search('(.*)\.py$', found_file)
 
137
                    if command:
 
138
                        available_completion.append(command.group(1))
 
139
 
 
140
            # builtins commands
 
141
            not_real_commands = ['quicklyconfig', 'configurationhandler', 'gettext', 'os', 'shutil', 'tools']
 
142
            commands_in_module = [ command for command in dir(commands) if not command.startswith('_') and not command.startswith('pre_') \
 
143
                                                          and not command.startswith('post_') and not command in not_real_commands]
 
144
            commands_in_module.append('create')
 
145
            available_completion.extend(commands_in_module)
 
146
 
 
147
    print " ".join(available_completion)
 
148
    sys.exit(0)
 
149
 
 
150
 
 
151
def check_this_command(command_name, template_path, opt_template):
 
152
    """check if the command exist in a template
 
153
 
 
154
            For instance, for a command like foo, the inside template foo.py script
 
155
            file is preferred to built-in foo() function.
 
156
            There can be pre_foo() and post_foo() built-in functions.
 
157
 
 
158
            :command_name
 
159
            :template_path
 
160
 
 
161
            return list of commands ready to be launched
 
162
    """
 
163
 
 
164
    commands_available = {}
 
165
 
 
166
    # check for template command
 
167
    command_path = template_path + "/" + command_name + ".py"
 
168
    if os.path.exists(command_path):
 
169
        commands_available[opt_template] = command_path
 
170
    else:
 
171
        #check for built-in command
 
172
        if hasattr(commands, command_name):
 
173
            commands_available[opt_template] = getattr(commands, command_name)
 
174
        else:
 
175
            print _("ERROR: command '%s' in '%s' not found.") % (command_name, opt_template)
 
176
            print _("Aborting")
 
177
            exit(1)
 
178
 
 
179
    #check for pre-post built-in commands
 
180
    for hook in ("pre", "post"):
 
181
        if hasattr(commands, hook + '_' + command_name):
 
182
            commands_available[hook] = getattr(commands, hook + '_' + command_name)
 
183
 
 
184
    return commands_available
 
185
 
 
186
 
 
187
def process_command_line(argv):
 
188
    """Entry point for command line processing
 
189
    use sys.argv by default if no args to parse
 
190
 
 
191
    :return: options
 
192
    """
 
193
 
 
194
    opt_command = []
 
195
    opt_template = ''
 
196
    out_off_project_tree_command = False
 
197
    opt_has_template = False
 
198
    need_template = True
 
199
    i = 0
 
200
 
 
201
    if len(argv) == 0:
 
202
        usage()
 
203
        return 0
 
204
 
 
205
    while i < len(argv):
 
206
        arg = argv[i]
 
207
 
 
208
        if arg == '--template' or arg == '-t':
 
209
            opt_has_template = True
 
210
            opt_template = argv[i + 1]
 
211
            i += 1
 
212
        elif arg == '--staging':
 
213
            oldenv = ""
 
214
            if os.environ.has_key('QUICKLY'):
 
215
                oldenv = os.environ['QUICKLY']
 
216
            os.environ['QUICKLY'] = "staging " + oldenv
 
217
        elif arg == '--verbose':
 
218
            oldenv = ""
 
219
            if os.environ.has_key('QUICKLY'):
 
220
                oldenv = os.environ['QUICKLY']
 
221
            os.environ['QUICKLY'] = "verbose " + oldenv
 
222
        elif arg == '--version':
 
223
            show_version()
 
224
        elif arg == '--help' or arg == '-h':
 
225
            usage()
 
226
        else:
 
227
            if arg in command_followed_by_template: # need template (default) and out of tree command
 
228
                out_off_project_tree_command = True
 
229
            elif arg == 'getstarted':
 
230
                out_off_project_tree_command = True
 
231
                need_template = False
 
232
            opt_command.append(arg)
 
233
        i += 1
 
234
 
 
235
    return (opt_command, need_template, opt_has_template, opt_template, out_off_project_tree_command)
 
236
 
 
237
def main():
 
238
    """Main ubuntu command line processor
 
239
 
 
240
    :return: exit code of quickly command.
 
241
    """
 
242
 
 
243
    (opt_command, need_template, opt_has_template, opt_template, out_off_project_tree_command) = process_command_line(sys.argv[1:])
 
244
 
 
245
    # ensure the command exists
 
246
    if not opt_command:
 
247
        print _("ERROR: No command found")
 
248
        print _("Aborting")
 
249
        usage()
 
250
        return 1
 
251
 
 
252
    # create command can't be launched in an existing project (don't create mess for the user)
 
253
    if 'create' in opt_command:
 
254
        try:
 
255
            project_path = tools.get_root_project_path()
 
256
        except tools.project_path_not_found:
 
257
            pass
 
258
        else: # no error, so we are in a project path, not good!
 
259
            print _("ERROR: %s is already a project. You can't create a project within another project. " \
 
260
                "Please choose another path." % project_path)
 
261
            sys.exit(1)
 
262
 
 
263
    # get the template if needed
 
264
    if need_template:
 
265
        # if processing a "out_off_project_tree_command", template argument
 
266
        # and project name must be there (with -t, --template or just following the command): create, quickly
 
267
        if out_off_project_tree_command:
 
268
            if not opt_has_template:
 
269
                if len(opt_command) < 3 or opt_command[1].startswith('-'):
 
270
                    print _("ERROR: %s command must be followed by a template and destination name" % opt_command[0])
 
271
                    print _("Aborting")
 
272
                    usage()
 
273
                    return 1
 
274
                else:
 
275
                    opt_template = opt_command[1]
 
276
                    opt_command.remove(opt_command[1])
 
277
                    opt_has_template = True
 
278
 
 
279
            #in every cases, the project name is now in first position.
 
280
            project_name = opt_command[1]
 
281
            project_dir = os.path.abspath(os.getcwd() + '/' + project_name)
 
282
            
 
283
 
 
284
        # the config file already exist, get project path and load configuration.
 
285
        else:
 
286
            try:
 
287
                project_dir = tools.get_root_project_path()
 
288
            except tools.project_path_not_found:
 
289
                print _("ERROR: Can't find project in %s.\nEnsure you launch this command from a quickly project directory.") % os.getcwd()
 
290
                sys.exit(1)
 
291
            configurationhandler.loadConfig()
 
292
 
 
293
        #if no template provided, guess it from the current tree
 
294
        if not opt_has_template and need_template:
 
295
            try:
 
296
                opt_template = configurationhandler.project_config['template']
 
297
            except KeyError:
 
298
                print _("ERROR: No template provided and none found in the current tree. Ensure you " \
 
299
                            "don't want to create a new project or that your are in your directory project.")
 
300
                print _("Aborting")
 
301
                return 1
 
302
 
 
303
        # get the template path
 
304
        template_path = tools.get_template_directory(opt_template)
 
305
 
 
306
    # no template needed, empty them
 
307
    else:
 
308
        template_path = ''
 
309
        opt_template = ''
 
310
        project_dir = ''
 
311
 
 
312
 
 
313
    # execute the command
 
314
    commands_to_execute = check_this_command(opt_command[0], template_path, opt_template)
 
315
    return_code = 0
 
316
    # pre-hook
 
317
    if 'pre' in commands_to_execute:
 
318
        return_code = commands_to_execute['pre'](opt_template, project_dir, opt_command[1:])
 
319
    if return_code != 0:
 
320
        print _("ERROR: pre_%s command failed") % opt_command[0]
 
321
        print _("Aborting")
 
322
        return return_code
 
323
 
 
324
    # main execution
 
325
    if callable(commands_to_execute[opt_template]):
 
326
        return_code = commands_to_execute[opt_template](opt_template, project_dir, opt_command[1:])
 
327
    else:
 
328
        return_code = subprocess.call(["python", commands_to_execute[opt_template]] + opt_command[1:], cwd=project_dir)
 
329
 
 
330
    if return_code != 0:
 
331
        print _("ERROR: %s command failed") % opt_command[0]
 
332
        print _("Aborting")
 
333
        return return_code
 
334
 
 
335
    # post-hook
 
336
    if 'post' in commands_to_execute:
 
337
         commands_to_execute['post'](opt_template, project_dir, opt_command[1:])
 
338
    if return_code != 0:
 
339
        print _("ERROR: post_%s command failed") % opt_command[0]
 
340
        print _("Aborting")
 
341
        return return_code
 
342
 
 
343
    return 0
 
344
 
 
345
if __name__ == '__main__':
 
346
 
 
347
    gettext.textdomain('quickly')
 
348
 
 
349
    # process the command line to send the right instructions
 
350
    if 'shell-completion' in sys.argv:
 
351
        get_commands()
 
352
    else:
 
353
        exit(main())