diff options
author | Evgeny Fadeev <evgeny.fadeev@gmail.com> | 2010-06-15 01:09:39 -0400 |
---|---|---|
committer | Evgeny Fadeev <evgeny.fadeev@gmail.com> | 2010-06-15 01:09:39 -0400 |
commit | 9d2c4337d8ae0ec8c9f1903dcc3b613c80449b5b (patch) | |
tree | 53a1f25228d86a03825f50158d50ccc0f651df40 | |
parent | 85a3c85a887e4caf21a8d79b71fe41cc94aacc64 (diff) | |
download | askbot-9d2c4337d8ae0ec8c9f1903dcc3b613c80449b5b.tar.gz askbot-9d2c4337d8ae0ec8c9f1903dcc3b613c80449b5b.tar.bz2 askbot-9d2c4337d8ae0ec8c9f1903dcc3b613c80449b5b.zip |
basic deployment script works
-rw-r--r-- | askbot/deployment/__init__.py | 101 | ||||
-rw-r--r-- | askbot/deployment/dialogs.py | 20 | ||||
-rw-r--r-- | askbot/deployment/messages.py | 91 | ||||
-rw-r--r-- | askbot/deployment/path_utils.py | 146 | ||||
-rw-r--r-- | askbot/doc/INSTALL | 98 | ||||
-rw-r--r-- | askbot/setup_templates/settings.py | 12 | ||||
-rw-r--r-- | askbot/upfiles/README | 3 | ||||
-rw-r--r-- | askbot/urls.py | 3 | ||||
-rw-r--r-- | setup.py | 15 |
9 files changed, 435 insertions, 54 deletions
diff --git a/askbot/deployment/__init__.py b/askbot/deployment/__init__.py new file mode 100644 index 00000000..9f4ebaf9 --- /dev/null +++ b/askbot/deployment/__init__.py @@ -0,0 +1,101 @@ +""" +module for deploying askbot +""" +import os.path +from askbot.deployment import messages +from askbot.deployment import dialogs +from askbot.deployment import path_utils + +def startforum(): + """basic deployment procedure + asks user several questions, then either creates + new deployment (in the case of new installation) + or gives hints on how to add askbot to an existing + Django project + """ + #ask + print messages.DEPLOY_PREAMBLE + + directory = None #directory where to put stuff + create_new = False #create new django project or not + where_to_deploy_msg = messages.WHERE_TO_DEPLOY + while directory is None: + + directory = raw_input(where_to_deploy_msg + ' ') + + where_to_deploy_msg = messages.WHERE_TO_DEPLOY_QUIT + + directory = os.path.normpath(directory) + directory = os.path.abspath(directory) + + if os.path.isfile(directory): + print messages.CANT_INSTALL_INTO_FILE % {'path':directory} + directory = None + continue + + if path_utils.can_create_path(directory): + if os.path.exists(directory): + if path_utils.path_is_clean_for_django(directory): + if path_utils.has_existing_django_project(directory): + message = messages.SHOULD_ADD_APP_HERE % \ + { + 'path': directory + } + should_add_app = dialogs.multiple_choice_input( + message, + options = ['yes','no'] + ) + if should_add_app == 'yes': + assert(create_new == False) + if path_utils.dir_name_acceptable(directory): + break + else: + print messages.format_msg_bad_dir_name(directory) + directory = None + continue + else: + directory = None + continue + else: + assert(directory != None) + if path_utils.dir_name_acceptable(directory): + create_new = True + break + else: + print messages.format_msg_bad_dir_name(directory) + directory = None + continue + else: + print messages.format_msg_dir_unclean_django(directory) + directory = None + continue + else: + message = messages.format_msg_create(directory) + should_create_new = dialogs.multiple_choice_input( + message, + options = ['yes','no'] + ) + if should_create_new == 'yes': + if path_utils.dir_name_acceptable(directory): + create_new = True + break + else: + print messages.format_msg_bad_dir_name(directory) + directory = None + continue + else: + directory = None + continue + else: + print messages.format_msg_dir_not_writable(directory) + directory = None + continue + + help_file = os.path.join(directory, 'askbot', 'doc', 'INSTALL') + if create_new: + path_utils.create_path(directory) + path_utils.deploy_into(directory, new_project = True) + print messages.HOW_TO_DEPLOY_NEW % {'help_file': help_file} + else: + path_utils.deploy_into(directory, new_project = False) + print messages.HOW_TO_ADD_ASKBOT_TO_DJANGO % {'help_file': help_file} diff --git a/askbot/deployment/dialogs.py b/askbot/deployment/dialogs.py new file mode 100644 index 00000000..40f0d2ee --- /dev/null +++ b/askbot/deployment/dialogs.py @@ -0,0 +1,20 @@ +"""functions that directly handle user input +""" +from askbot.deployment import messages +import time + +def multiple_choice_input(prompt_phrase, options = None): + """prints a prompt, accepts keyboard input + and makes sure that user response is one of given + in the options argument, which is required + and must be a list + """ + assert(isinstance(options, list)) + while 1: + response = raw_input('\n%s (type %s): ' % (prompt_phrase, '/'.join(options))) + if response in options: + return response + else: + opt_string = ','.join(options) + print messages.INVALID_INPUT % {'opt_string': opt_string} + time.sleep(1) diff --git a/askbot/deployment/messages.py b/askbot/deployment/messages.py new file mode 100644 index 00000000..1569de6c --- /dev/null +++ b/askbot/deployment/messages.py @@ -0,0 +1,91 @@ +"""Messages used in the procedure of deploying Askbot +""" +import os.path +from askbot.deployment import path_utils + +DEPLOY_PREAMBLE = """ +Deploying Askbot - Django Q&A forum application +Problems installing? -> please email admin@askbot.org + +To CANCEL - hit Ctr-C at any time""" + +WHERE_TO_DEPLOY = 'Where to deploy (in which directory)?' + +WHERE_TO_DEPLOY_QUIT = 'Where deploy forum (directory)? (Ctrl-C to quit)' + +CANT_INSTALL_INTO_FILE = '%(path)s is a file\ncannot install there' + +SHOULD_ADD_APP_HERE = 'Directory %(path)s?\nalready has a Django ' \ + + 'project - do you want to add askbot app to that project?' + +HOW_TO_DEPLOY_NEW = 'Done. Please find further instructions in the file below:'\ + + '\n%(help_file)s' + +HOW_TO_ADD_ASKBOT_TO_DJANGO = HOW_TO_DEPLOY_NEW + +DIR_IS_NOT_WRITABLE = 'Directory %(dir)s is not writable' + +PARENT_DIR_IS_NOT_WRITABLE = """To create directory %(target_dir)s +we need to add %(non_existing_tail)s to %(existing_prefix)s +but %(existing_prefix)s is not writable""" + +CONFIRM_DIR_CREATION = """Adding new directories:\n%(existing_prefix)s <-/%(non_existing_tail)s +Accept?""" + +INVALID_INPUT = 'Please type one of: %(opt_string)s ' \ + + '(or hit Ctrl-C to quit)' + +DIR_NAME_TAKEN_BY_PYTHON = """Directory '%(dir)s' is aready used by other Python module. +Please choose some other name for your django project""" + +DIR_NAME_TAKEN_BY_ASKBOT = """Please do not name your entire Django project 'askbot', +because this name is already used by the askbot app itself""" + +def format_msg_dir_not_writable(directory): + """returns a meaningful message explaining why directory + is not writable by the user + """ + if os.path.exists(directory): + if path_utils.directory_is_writable(directory): + return '' + else: + return DIR_IS_NOT_WRITABLE % {'dir': directory} + else: + prefix, tail = path_utils.split_at_break_point(directory) + data = { + 'existing_prefix': prefix, + 'non_existing_tail': tail, + 'target_dir': directory + } + return PARENT_DIR_IS_NOT_WRITABLE % data + +def format_msg_create(directory): + """returns a message explaining wha directories + are about to be created and asks user if they want to proceed + """ + if os.path.exists(directory): + raise Exception('directory %s aready exists' % directory) + else: + prefix, tail = path_utils.split_at_break_point(directory) + data = { + 'existing_prefix': prefix, + 'non_existing_tail': tail, + } + return CONFIRM_DIR_CREATION % data + +def format_msg_dir_unclean_django(directory): + """retuns a message telling which of the parent + directories contains a django project + so that users don't create nested projects + """ + parent_django_dir = path_utils.find_parent_dir_with_django(directory) + +def format_msg_bad_dir_name(directory): + """directory name must be bad - i.e. taken by other python module + on PYTHONPATH + """ + dir_name = os.path.basename(directory) + if dir_name == 'askbot': + return DIR_NAME_TAKEN_BY_ASKBOT + else: + return DIR_NAME_TAKEN_BY_PYTHON % {'dir': dir_name} diff --git a/askbot/deployment/path_utils.py b/askbot/deployment/path_utils.py new file mode 100644 index 00000000..71e66182 --- /dev/null +++ b/askbot/deployment/path_utils.py @@ -0,0 +1,146 @@ +"""utilities in addition to os.path +that +* help to test existing paths on usability for the installation +* create necessary directories +* install deployment files +""" +import os +import os.path +import tempfile +import re +import glob +import shutil +import imp + +def split_at_break_point(directory): + """splits directory path into two pieces + first that exists and secon - that does not + by determining a point at which path breaks + + exception will be raised if directory in fact exists + """ + assert(os.path.exists(directory) == False) + + head = directory + tail_bits = list() + while os.path.exists(head) == False: + head, tail = os.path.split(head) + tail_bits.insert(0, tail) + return head, os.path.join(*tail_bits) + + +def directory_is_writable(directory): + """returns True if directory exists + and is writable, False otherwise + """ + tempfile.tempdir = directory + try: + #run writability test + temp_path = tempfile.mktemp() + assert(os.path.dirname(temp_path) == directory) + temp_file = open(temp_path, 'w') + temp_file.close() + os.unlink(temp_path) + return True + except IOError: + return False + + +def can_create_path(directory): + """returns True if user can write file into + directory even if it does not exist yet + and False otherwise + """ + if os.path.exists(directory): + if not os.path.isdir(directory): + return False + else: + directory, junk = split_at_break_point(directory) + return directory_is_writable(directory) + + +IMPORT_RE1 = re.compile(r'from django.*import') +IMPORT_RE2 = re.compile(r'import django') +def has_existing_django_project(directory): + """returns True is any of the .py files + in a given directory imports anything from django + """ + file_list = glob.glob(directory + '*.py') + for file_name in file_list: + py_file = open(file_name) + for line in py_file: + if IMPORT_RE1.match(line) or IMPORT_RE2.match(line): + py_file.close() + return True + py_file.close() + return False + + +def find_parent_dir_with_django(directory): + """returns path to Django project anywhere + above the directory + if nothing is found returns None + """ + parent_dir = os.path.dirname(directory) + while parent_dir != directory: + if has_existing_django_project(parent_dir): + return parent_dir + else: + directory = parent_dir + parent_dir = os.path.dirname(directory) + return None + + +def path_is_clean_for_django(directory): + """returns False if any of the parent directories + contains a Django project, otherwise True + does not check the current directory + """ + django_dir = find_parent_dir_with_django(directory) + return (django_dir is None) + + +def create_path(directory): + if os.path.isdir(directory): + return + elif os.path.exists(directory): + raise ValueError('expect directory or a non-existing path') + else: + os.makedirs(directory) + +SOURCE_DIR = os.path.dirname(os.path.dirname(__file__)) +def deploy_into(directory, new_project = None): + """will copy necessary files into the directory + """ + assert(new_project is not None) + if new_project: + copy_files = ('__init__.py', 'settings.py', 'manage.py', 'urls.py') + print 'copying files: ', + for file_name in copy_files: + src = os.path.join(SOURCE_DIR, 'setup_templates', file_name) + print '%s ' % file_name, + shutil.copy(src, directory) + #copy log directory + src = os.path.join(SOURCE_DIR, 'setup_templates', 'log') + dst = os.path.join(directory, 'log') + shutil.copytree(src, dst) + + print '' + app_dir = os.path.join(directory, 'askbot') + + print 'copying directories: ', + copy_dirs = ('doc','cron','upfiles') + for dir_name in copy_dirs: + src = os.path.join(SOURCE_DIR, dir_name) + dst = os.path.join(app_dir, dir_name) + print dir_name + ' ', + shutil.copytree(src, dst) + print '' + +def dir_name_acceptable(directory): + dir_name = os.path.basename(directory) + try: + imp.find_module(dir_name) + return False + except ImportError: + return True diff --git a/askbot/doc/INSTALL b/askbot/doc/INSTALL index 55b8614d..4589e1ba 100644 --- a/askbot/doc/INSTALL +++ b/askbot/doc/INSTALL @@ -1,7 +1,7 @@ CONTENTS ------------------ -A. PREREQUISITES -B. INSTALLATION +A. INSTALLATION +B. DEPLOYMENT 1. Settings file 2. Database 3. Running Askbot in the development server @@ -14,67 +14,69 @@ C. CONFIGURATION PARAMETERS (settings_local.py) D. CUSTOMIZATION -A. PREREQUISITES ------------------------------------------------ -Note: by default all installation activity is made in the superuser 'root' account. -This can be achieved either by logging in as root (su root), -or - if you have program sudo installed - prefix all commands with sudo. -So sodo will be listed below as optional. +A. INSTALLATION + +NOTE: if you need to install in non-standard locations +please take a look here: http://askbot.org/wiki/index.php/Installing_software_components_at_non-standard_locations + +type + +> python setup.py install + +or + +> easy_install askbot + +then, if you need to start a new deployment, type + +> startforum + +Please read a few paragraphs of the following section on +how to initialize the application + +ATTENTION WINDOWS USERS: you will need to manually install +binary for mysql-python from here +http://www.codegood.com/archives/4 + +B. DEPLOYMENT + +if you are adding askbot to your existing project that already +has other apps, you'll need to "merge" some of the files in +directory askbot/install_templates into your corresponding project files +more instuctions about this are to follow soon - if you have questions +ask on the forum + +for new deployment +> startforum + +and answer questions in the end you should be able to get almost +working installation, you'll need to edit file settings.py +and at least enter database name, user name and password + +then type + +> python manage.py syncdb +> python manage.py migrate forum -0. We recommend you to use python-setuptools to install pre-requirement libraries. -If you haven't installed it, please try to install it first. -e.g, [sudo] apt-get install python-setuptools +now run: -1. Python2.5/2.6, Django v1.1.1 +> python manage.py runserver `hostname -i`:8000 #or use some other port -1A If you are using MySQL, mysql client for python must be installed -[sudo] easy_install mysql-python +command `hostname -i` returns IP address on Unix, you can type the IP by hand too -2. Python-openid v2.2 -http://openidenabled.com/python-openid/ -[sudo] easy_install python-openid -4. html5lib -http://code.google.com/p/html5lib/ -Used for HTML sanitizer -[sudo] easy_install html5lib -5. Markdown2 -http://code.google.com/p/python-markdown2/ -[sudo] easy_install markdown2 -6. Django Debug Toolbar -http://github.com/robhudson/django-debug-toolbar/tree/master -7. djangosphinx (optional - for full text questions+answer+tag) -http://github.com/dcramer/django-sphinx/tree/master/djangosphinx -8. sphinx search engine (optional, works together with djangosphinx) -http://sphinxsearch.com/downloads.html +NOTES BELOW ARE OUTDATED, but may be useful +if you have questions - please ask at http://askbot.org -9. recaptcha_django (installed through svn) -http://code.google.com/p/recaptcha-django/ -10. python recaptcha module -http://code.google.com/p/recaptcha/ -easy_install recaptcha-client -Notice that you will need to register with recaptcha.net and receive -recaptcha public and private keys that need to be saved in your -settings_local.py file -11. South -http://south.aeracode.org/docs/installation.html -Used for database schema and data migrations -[sudo] easy_install South -EXTRA DEPENDENCIES FOR PYTHON 2.4 -* hashlib (made standard in python 2.5) -NOTES: django_authopenid is included into Askbot code -and is significantly modified. http://code.google.com/p/django-authopenid/ -no need to install this library -B. INSTALLATION NOTE: If you want to upgrade software, not install from scratch - take a look into forum/documentation/UPGRADE diff --git a/askbot/setup_templates/settings.py b/askbot/setup_templates/settings.py index ec3a511f..aa6194aa 100644 --- a/askbot/setup_templates/settings.py +++ b/askbot/setup_templates/settings.py @@ -13,9 +13,9 @@ ADMINS = ( MANAGERS = ADMINS DATABASE_ENGINE = 'mysql' # only mysql is supported, more will be in the near future -DATABASE_NAME = 'junk' # Or path to database file if using sqlite3. -DATABASE_USER = 'junk' # Not used with sqlite3. -DATABASE_PASSWORD = 'secret' # Not used with sqlite3. +DATABASE_NAME = '' # Or path to database file if using sqlite3. +DATABASE_USER = '' # Not used with sqlite3. +DATABASE_PASSWORD = '' # Not used with sqlite3. DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3. DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3. @@ -51,12 +51,14 @@ LANGUAGE_CODE = 'en' # Absolute path to the directory that holds media. # Example: "/home/media/media.lawrence.com/" -MEDIA_ROOT = '' +MEDIA_ROOT = os.path.join(os.path.dirname(__file__), 'askbot', 'upfiles') # URL that handles the media served from MEDIA_ROOT. Make sure to use a # trailing slash if there is a path component (optional in other cases). # Examples: "http://media.lawrence.com", "http://example.com/media/" -MEDIA_URL = '' +MEDIA_URL = ''#set this to serve uploaded files correctly + +PROJECT_ROOT = os.path.dirname(__file__) # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a # trailing slash. diff --git a/askbot/upfiles/README b/askbot/upfiles/README new file mode 100644 index 00000000..097ee8a5 --- /dev/null +++ b/askbot/upfiles/README @@ -0,0 +1,3 @@ +this directory is empty +it is copied at deployment time to some other +location where it will hold user uploaded files diff --git a/askbot/urls.py b/askbot/urls.py index 8a4f9b29..c538df3b 100644 --- a/askbot/urls.py +++ b/askbot/urls.py @@ -9,6 +9,7 @@ from askbot import views as app from askbot.feed import RssLastestQuestionsFeed from askbot.sitemap import QuestionsSitemap from django.utils.translation import ugettext as _ +from django.conf import settings admin.autodiscover() feeds = { @@ -36,7 +37,7 @@ urlpatterns = patterns('', url( r'^%s(?P<path>.*)$' % _('upfiles/'), 'django.views.static.serve', - {'document_root': os.path.join(APP_PATH,'upfiles').replace('\\','/')}, + {'document_root': os.path.join(settings.PROJECT_ROOT, 'askbot', 'upfiles').replace('\\','/')}, name='uploaded_file', ), url(r'^%s$' % _('about/'), app.meta.about, name='about'), @@ -25,6 +25,11 @@ setup( author_email = 'evgeny.fadeev@gmail.com', license = 'GPLv3', keywords = 'forum, community, wiki, Q&A', + entry_points = { + 'console_scripts' : [ + 'startforum = askbot.deployment:startforum', + ] + }, url = 'http://askbot.org', include_package_data = True, install_requires = install_requires, @@ -37,3 +42,13 @@ setup( if sys.platform in WIN_PLATFORMS: print 'ATTENTION!! please install windows binary mysql-python package' print 'at http://www.codegood.com/archives/4' + +print '**************************************************************' +print '* *' +print '* Thanks for installing Askbot. *' +print '* To start deploying type: >startforum *' +print '* Please take a look at the manual askbot/doc/INSTALL *' +print '* And please do not hesitate to ask your questions at *' +print '* at http://askbot.org *' +print '* *' +print '**************************************************************' |