Mercurial > moin > 2.0
changeset 2312:9f516825b465
change default venv name; rename m.py to make.py; delete makemoinmenu.py; rerun "python quickinstall.py" after applying this cs
author | RogerHaase |
---|---|
date | Thu, 19 Dec 2013 14:53:20 -0700 |
parents | c4abbe125226 |
children | 68abaa6bcfc3 |
files | .hgignore m.py make.py makemoinmenu.py quickinstall.py |
diffstat | 5 files changed, 548 insertions(+), 528 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Tue Dec 03 11:46:31 2013 -0700 +++ b/.hgignore Thu Dec 19 14:53:20 2013 -0700 @@ -13,6 +13,8 @@ ^wiki/data/content/ ^wiki/data/userprofiles/ ^wiki/index/ +^wiki/backup.*\.moin$ +^wiki/deleted-backup\.moin$ ^instance/ ^wikiconfig_.+\.py ^MoinMoin/translations/.*/LC_MESSAGES/messages.mo$ @@ -34,3 +36,11 @@ ^upload.py ^build/ \..*sw[op]$ +^activate.bat$ +^deactivate.bat$ +^moin.bat$ +^m.bat$ +^activate$ +^moin$ +^m$ +^m-.*\.txt$
--- a/m.py Tue Dec 03 11:46:31 2013 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,477 +0,0 @@ -#!/usr/bin/python -# Copyright: 2013 MoinMoin:RogerHaase -# License: GNU GPL v2 (or any later version), see LICENSE.txt for details. - -""" -m.py - -Provides a menu of functions frequently used by moin2 developers and desktop wiki users. - - duplicates some common moin commands, do "moin --help" for all alternatives - - adds default file names for selected moin commands (backup, restore, ...) - - creates log files for functions with large output, extracts success/failure messages - - displays error messages when user tries to run commands out of sequence -""" - -import os -import sys -import subprocess -import glob -import shutil -import fnmatch -from collections import Counter - -import MoinMoin # validate python version - - -# text files created by commands with high volume output -QUICKINSTALL = 'm-quickinstall.txt' -PYTEST = 'm-pytest.txt' -PEP8 = 'm-pep8.txt' -CODING_STD = 'm-coding-std.txt' -DOCS = 'm-docs.txt' -NEWWIKI = 'm-new-wiki.txt' -DELWIKI = 'm-delete-wiki.txt' -BACKUPWIKI = 'm-backup-wiki.txt' -EXTRAS = 'm-extras.txt' -DIST = 'm-create-dist.txt' -# default files used for backup and restore -BACKUP_FILENAME = 'wiki/backup.moin' -JUST_IN_CASE_BACKUP = 'wiki/deleted-backup.moin' - - -if os.name == 'nt': - M = 'm' # customize help to local OS - WINDOWS_OS = True -else: - M = './m' - WINDOWS_OS = False - - -# commands that create log files; "tests" creates 2 log files - pytest + pep8 -CMD_LOGS = { - 'quickinstall': QUICKINSTALL, - 'pytest': PYTEST, - 'pep8': PEP8, - # 'coding-std': CODING_STD, # not logged due to small output - 'docs': DOCS, - 'new-wiki': NEWWIKI, - 'del-wiki': DELWIKI, - 'backup': BACKUPWIKI, - 'extras': EXTRAS, - 'dist': DIST, -} - - -help = r""" - -usage: "%s <target>" where <target> is: - -quickinstall create or update virtual environment with required packages -docs create local Sphinx html documentation -extras install OpenID, Pillow, pymongo, sqlalchemy, ldap, upload.py -interwiki refresh contrib\interwiki\intermap.txt (hg version control) -log <target> view detailed log generated by <target>, omit to see list - -new-wiki create empty wiki -sample create wiki and load sample data -restore * create wiki and restore wiki\backup.moin *option, specify file -import <dir> import a moin 1.9 wiki/data instance from <dir> - -run run built-in wiki server with local OS and logging options -backup * roll 3 prior backups and create new backup *option, specify file - -css run Stylus to update CSS files -tests run tests, output goes to pytest.txt and pytestpep8.txt -coding-std correct scripts that taint the repository with trailing spaces.. -api update Sphinx API docs (files are under hg version control) -dist delete wiki data, then create distribution archive in /dist - -del-all same as running the 4 del-* commands below -del-orig delete all files matching *.orig -del-pyc delete all files matching *.pyc -del-rej delete all files matching *.rej -del-wiki create a backup, then delete all wiki data -""" % M - - -def search_for_phrase(filename): - """Search a text file for key phrases and print the lines of interest or print a count by phrase.""" - files = { - # filename: (list of phrases) - QUICKINSTALL: ('could not find', 'error', 'fail', 'timeout', 'traceback', 'success', 'cache location', 'must be deactivated', ), - NEWWIKI: ('error', 'fail', 'timeout', 'traceback', 'success', ), - BACKUPWIKI: ('error', 'fail', 'timeout', 'traceback', 'success', ), - # 'error ' to avoid catching .../Modules/errors.o.... - EXTRAS: ('error ', 'error:', 'error.', 'error,', 'fail', 'timeout', 'traceback', 'success', 'already satisfied', 'active version', 'installed', 'finished', ), - PYTEST: ('seconds =', ), - PEP8: ('seconds =', ), - CODING_STD: ('remove trailing blanks', 'dos line endings', 'unix line endings', 'remove empty lines', ), - DIST: ('creating', 'copying', 'adding', 'hard linking', ), - DOCS: ('build finished', 'build succeeded', 'traceback', 'failed', 'error', 'usage', 'importerror', 'Exception occurred', ) - } - # for these file names, display a count of occurrances rather than each found line - print_counts = (CODING_STD, DIST, ) - - with open(filename, "r") as f: - lines = f.readlines() - name = os.path.split(filename)[1] - phrases = files[name] - counts = Counter() - for idx, line in enumerate(lines): - line = line.lower() - for phrase in phrases: - if phrase in line: - if filename in print_counts: - counts[phrase] += 1 - else: - print idx + 1, line.rstrip() - break - for key in counts: - print 'The phrase "%s" was found %s times.' % (key, counts[key]) - - -def wiki_exists(): - """Return truthy if a wiki exists.""" - return glob.glob('wiki/index/_all_revs_*.toc') - - -def make_wiki(command): - """Process command to create a new wiki.""" - if wiki_exists(): - print 'Error: a wiki exists, delete it and try again.' - else: - print 'Output messages redirected to %s' % NEWWIKI - with open(NEWWIKI, 'w') as messages: - result = subprocess.call(command, shell=True, stderr=messages, stdout=messages) - if result == 0: - print '\nSuccess: a new wiki has been created.' - else: - print 'Important messages from %s are shown below:' % NEWWIKI - search_for_phrase(NEWWIKI) - print '\nError: attempt to create wiki failed. Do "%s log new-wiki" to see complete log.' % M - - -def delete_files(pattern): - """Recursively delete all files matching pattern.""" - matches = [] - for root, dirnames, filenames in os.walk(os.path.abspath(os.path.dirname(__file__))): - for filename in fnmatch.filter(filenames, pattern): - matches.append(os.path.join(root, filename)) - for match in matches: - os.remove(match) - print 'Deleted %s files matching "%s".' % (len(matches), pattern) - - -class Menu(object): - """Each cmd_ method processes an option on the menu.""" - def __init__(self): - pass - - def cmd_quickinstall(self, *args): - """create or update a virtual environment with the required packages""" - command = '%s quickinstall.py %s' % (sys.executable, ' '.join(args)) - print 'Running quickinstall.py... output messages redirected to %s' % QUICKINSTALL - with open(QUICKINSTALL, 'w') as messages: - result = subprocess.call(command, shell=True, stderr=messages, stdout=messages) - if result != 0: - open(QUICKINSTALL, 'a').write('Error: quickinstall passed non-zero return code: %s' % result) - print 'Searching %s, important messages are shown below... Do "%s log quickinstall" to see complete log.\n' % (QUICKINSTALL, M) - search_for_phrase(QUICKINSTALL) - - def cmd_docs(self, *args): - """create local Sphinx html documentation""" - if WINDOWS_OS: - command = 'activate.bat & cd docs & make.bat html' # windows separates commands with "&" - else: - # in terminal "source activate" works, but shell requires "source ./activate" - command = 'source ./activate; cd docs; make html' # unix separates commands with ";" - print 'Creating HTML docs... output messages written to %s.' % DOCS - with open(DOCS, 'w') as messages: - result = subprocess.call(command, shell=True, stderr=messages, stdout=messages) - print 'Searching %s, important messages are shown below...\n' % DOCS - search_for_phrase(DOCS) - if result == 0: - print 'HTML docs successfully created in docs/_build/html.' - else: - print 'Error: creation of HTML docs failed with return code "%s". Do "%s log docs" to see complete log.' % (result, M) - - def cmd_extras(self, *args): - """install optional packages: OpenID, Pillow, pymongo, sqlalchemy, ldap; and upload.py""" - upload = '%s MoinMoin/script/win/wget.py https://codereview.appspot.com/static/upload.py upload.py' % sys.executable - if WINDOWS_OS: - print 'Installing OpenId, Pillow, pymongo, sqlalchemy, upload.py... output messages written to %s.' % EXTRAS - # easy_install is used for windows because it installs binary packages, pip does not - command = 'activate.bat & easy_install python-openid & easy_install pillow & easy_install pymongo & easy_install sqlalchemy' + ' & ' + upload - # TODO: easy_install python-ldap fails on windows - # try google: installing python-ldap in a virtualenv on windows - # or, download from http://www.lfd.uci.edu/~gohlke/pythonlibs/#python-ldap - # activate.bat - # easy_install <path to downloaded .exe file> - # deactivate.bat - else: - print 'Installing OpenId, Pillow, pymongo, sqlalchemy, ldap, upload.py... output messages written to %s.' % EXTRAS - command = 'source ./activate; pip install python-openid; pip install pillow; pip install pymongo; pip install sqlalchemy; pip install python-ldap' + '; ' + upload - with open(EXTRAS, 'w') as messages: - subprocess.call(command, shell=True, stderr=messages, stdout=messages) - print 'Important messages from %s are shown below. Do "%s log extras" to see complete log.' % (EXTRAS, M) - search_for_phrase(EXTRAS) - - def cmd_interwiki(self, *args): - """refresh contrib\interwiki\intermap.txt""" - print 'Refreshing contrib\interwiki\intermap.txt...' - command = '%s MoinMoin/script/win/wget.py http://master19.moinmo.in/InterWikiMap?action=raw contrib/interwiki/intermap.txt' % sys.executable - subprocess.call(command, shell=True) - - def cmd_log(self, *args): - """View a log file with the default text editor""" - - def log_help(logs): - """Print list of available logs to view.""" - print "usage: %s log <target> where <target> is:\n\n" % M - choices = '{0: <16}- {1}' - for log in sorted(logs): - if os.path.isfile(CMD_LOGS[log]): - print choices.format(log, CMD_LOGS[log]) - else: - print choices.format(log, '* file does not exist') - - logs = set(CMD_LOGS.keys()) - if args and args[0] in logs and os.path.isfile(CMD_LOGS[args[0]]): - if WINDOWS_OS: - command = 'start %s' % CMD_LOGS[args[0]] - else: - command = '${VISUAL:-${FCEDIT:-${EDITOR:-less}}} %s' % CMD_LOGS[args[0]] - subprocess.call(command, shell=True) - else: - log_help(logs) - - def cmd_new_wiki(self, *args): - """create empty wiki""" - if WINDOWS_OS: - command = 'moin.bat index-create -s -i' - else: - command = './moin index-create -s -i' - print 'Creating a new empty wiki...' - make_wiki(command) # share code with loading sample data or restoring backups - - def cmd_sample(self, *args): - """create wiki and load sample data""" - if WINDOWS_OS: - command = 'moin.bat index-create -s -i & moin.bat load --file contrib/serialized/items.moin & moin.bat index-build' - else: - command = './moin index-create -s -i; ./moin load --file contrib/serialized/items.moin; ./moin index-build' - print 'Creating a new wiki populated with sample data...' - make_wiki(command) - - def cmd_restore(self, *args): - """create wiki and load data from wiki/backup.moin or user specified path""" - if WINDOWS_OS: - command = 'moin.bat index-create -s -i & moin.bat load --file %s & moin.bat index-build' - else: - command = './moin index-create -s -i; ./moin load --file %s; ./moin index-build' - filename = BACKUP_FILENAME - if args: - filename = args[0] - if os.path.isfile(filename): - command = command % filename - print 'Creating a new wiki and loading it with data from %s...' % filename - make_wiki(command) - else: - print 'Error: cannot create wiki because %s does not exist.' % filename - - def cmd_import(self, *args): - """import a moin 1.9 wiki directory named dir""" - if WINDOWS_OS: - command = 'moin.bat import19 -s -i --data_dir %s' - else: - command = './moin import19 -s -i --data_dir %s' - if args: - dirname = args[0] - if os.path.isdir(dirname): - command = command % dirname - print 'Creating a new wiki populated with data from %s...' % dirname - make_wiki(command) - else: - print 'Error: cannot create wiki because %s does not exist.' % dirname - else: - print 'Error: a path to the Moin 1.9 wiki/data data directory is required.' - - def cmd_run(self, *args): - """run built-in wiki server with local options""" - if wiki_exists(): - if os.path.isfile('logging.conf'): - if WINDOWS_OS: - logfile = 'set MOINLOGGINGCONF=logging.conf & ' - else: - logfile = 'MOINLOGGINGCONF=logging.conf; export MOINLOGGINGCONF; ' - else: - logfile = '' - if WINDOWS_OS: - command = '%smoin.bat moin %s --threaded' % (logfile, ' '.join(args)) - else: - command = '%s./moin moin %s' % (logfile, ' '.join(args)) - try: - subprocess.call(command, shell=True) - except KeyboardInterrupt: - pass # on windows pass eliminates traceback but "Terminate batch job..." message is displayed twice - else: - print 'Error: a wiki must be created before running the built-in server.' - - def cmd_backup(self, *args): - """roll 3 prior backups and create new wiki/backup.moin or backup to user specified file""" - if wiki_exists(): - filename = BACKUP_FILENAME - if args: - filename = args[0] - print 'Creating a wiki backup to %s...' % filename - else: - print 'Creating a wiki backup to %s after rolling 3 prior backups...' - b3 = BACKUP_FILENAME.replace('.', '3.') - b2 = BACKUP_FILENAME.replace('.', '2.') - b1 = BACKUP_FILENAME.replace('.', '1.') - if os.path.exists(b3): - os.remove(b3) - for src, dst in ((b2, b3), (b1, b2), (BACKUP_FILENAME, b1)): - if os.path.exists(src): - os.rename(src, dst) - - if WINDOWS_OS: - command = 'moin.bat save --all-backends --file %s' % filename - else: - command = './moin save --all-backends --file %s' % filename - with open(BACKUPWIKI, 'w') as messages: - result = subprocess.call(command, shell=True, stderr=messages, stdout=messages) - if result == 0: - print 'Success: wiki was backed up to %s' % filename - else: - print 'Important messages from %s are shown below. Do "%s log backup" to see complete log.' % (BACKUPWIKI, M) - search_for_phrase(BACKUPWIKI) - print '\nError: attempt to backup wiki failed.' - else: - print 'Error: cannot backup wiki because it has not been created.' - - def cmd_css(self, *args): - """run Stylus to update CSS files""" - print 'Running Stylus to update CSS files...' - if WINDOWS_OS: - command = r'cd MoinMoin\themes\modernized\static\css\stylus & stylus --include-css --compress < main.styl > ../common.css' - else: - command = 'cd MoinMoin/themes/modernized/static/css/stylus; stylus --include-css --compress < main.styl > ../common.css' - result = subprocess.call(command, shell=True) - - if WINDOWS_OS: - command = r'cd MoinMoin\themes\foobar\static\css\stylus & stylus --include-css --compress < main.styl > ../common.css' - else: - command = 'cd MoinMoin/themes/foobar/static/css/stylus; stylus --include-css --compress < main.styl > ../common.css' - result2 = subprocess.call(command, shell=True) - - if result == 0 and result2 == 0: - print 'Success: CSS files updated.' - else: - print 'Error: stylus failed to update css files, see error messages above.' - - def cmd_tests(self, *args): - """run tests, output goes to pytest.txt and pytestpep8.txt""" - print 'Running tests... output written to %s and %s.' % (PYTEST, PEP8) - if WINDOWS_OS: - command = 'activate.bat & py.test.exe > %s 2>&1 & py.test.exe --pep8 -k pep8 --clearcache > %s 2>&1' % (PYTEST, PEP8) - else: - command = 'source ./activate; py.test > %s 2>&1; py.test --pep8 -k pep8 --clearcache > %s 2>&1' % (PYTEST, PEP8) - result = subprocess.call(command, shell=True) - print 'Summary message from %s is shown below. Do "%s log pytest" to see complete log.' % (PYTEST, M) - search_for_phrase(PYTEST) - print 'Summary message from %s is shown below. Do "%s log pep8" to see complete log.' % (PEP8, M) - search_for_phrase(PEP8) - - def cmd_coding_std(self, *args): - """correct scripts that taint the HG repository and clutter subsequent code reviews""" - print 'Checking for trailing blanks, DOS line endings, Unix line endings, empty lines at eof...' - command = '%s contrib/pep8/coding_std.py' % sys.executable - subprocess.call(command, shell=True) - - def cmd_api(self, *args): - """update Sphinx API docs, these docs are under hg version control""" - print 'Refreshing api docs...' - if WINDOWS_OS: - command = 'activate.bat & sphinx-apidoc -f -o docs/devel/api MoinMoin & %s MoinMoin/script/win/dos2unix.py docs/devel/api' % sys.executable - else: - command = 'source ./activate; sphinx-apidoc -f -o docs/devel/api MoinMoin' - result = subprocess.call(command, shell=True) - - def cmd_dist(self, *args): - """create distribution archive in dist/""" - print 'Deleting wiki data, then creating distribution archive in /dist, output written to %s.' % DIST - self.cmd_del_wiki(*args) - command = '%s setup.py sdist' % sys.executable - with open(DIST, 'w') as messages: - result = subprocess.call(command, shell=True, stderr=messages, stdout=messages) - print 'Summary message from %s is shown below:' % DIST - search_for_phrase(DIST) - if result == 0: - print 'Success: a distribution archive was created in /dist.' - else: - print 'Error: create dist failed with return code = %s. Do "%s log dist" to see complete log.' % (result, M) - - def cmd_del_all(self, *args): - """same as running the 4 del-* commands below""" - self.cmd_del_orig(*args) - self.cmd_del_pyc(*args) - self.cmd_del_rej(*args) - self.cmd_del_wiki(*args) - - def cmd_del_orig(self, *args): - """delete all files matching *.orig""" - delete_files('*.orig') - - def cmd_del_pyc(self, *args): - """delete all files matching *.pyc""" - delete_files('*.pyc') - - def cmd_del_rej(self, *args): - """delete all files matching *.rej""" - delete_files('*.rej') - - def cmd_del_wiki(self, *args): - """create a just-in-case backup, then delete all wiki data""" - if WINDOWS_OS: - command = 'moin.bat save --all-backends --file %s' % JUST_IN_CASE_BACKUP - else: - command = './moin save --all-backends --file %s' % JUST_IN_CASE_BACKUP - if wiki_exists(): - print 'Creating a backup named %s; then deleting all wiki data and indexes...' % JUST_IN_CASE_BACKUP - with open(DELWIKI, 'w') as messages: - result = subprocess.call(command, shell=True, stderr=messages, stdout=messages) - if result != 0: - print 'Error: backup failed with return code = %s. Complete log is in %s.' % (result, DELWIKI) - # destroy wiki even though files are damaged and backup may have failed - if os.path.isdir('wiki/data') or os.path.isdir('wiki/index'): - shutil.rmtree('wiki/data') - shutil.rmtree('wiki/index') - print 'Wiki data successfully deleted.' - else: - print 'Wiki data not deleted because it does not exist.' - - -if __name__ == '__main__': - # create a set of valid menu choices - menu = Menu() - choices = set() - names = dir(menu) - for name in names: - if name.startswith('cmd_'): - choices.add(name) - - if len(sys.argv) == 1 or sys.argv[1] == '-h' or sys.argv[1] == '--help': - print help - else: - if sys.argv[1] != 'quickinstall' and not (os.path.isfile('activate') or os.path.isfile('activate.bat')): - print 'Error: files created by quickinstall are missing, run "%s quickinstall" and try again.' % M - else: - choice = 'cmd_%s' % sys.argv[1] - choice = choice.replace('-', '_') - if choice in choices: - choice = getattr(menu, choice) - choice(*sys.argv[2:]) - else: - print help - print 'Error: unknown menu selection "%s"' % sys.argv[1]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make.py Thu Dec 19 14:53:20 2013 -0700 @@ -0,0 +1,489 @@ +#!/usr/bin/python +# Copyright: 2013 MoinMoin:RogerHaase +# License: GNU GPL v2 (or any later version), see LICENSE.txt for details. + +""" +make.py provides a menu of commands frequently used by moin2 developers and desktop wiki users. + + - duplicates some common moin commands, do "moin --help" for all alternatives + - adds default file names for selected moin commands (backup, restore, ...) + - creates log files for functions with large output, extracts success/failure messages + - displays error messages when user tries to run commands out of sequence + +usage (to display a menu of commands): + - unix: ./m + - windows: m + +For make.py to work, it needs to know the name of a python executable and the location of a +virtual env. These needs are met by running "python quickinstall.py" after cloning the moin2 +repository. quickinstall.py creates these files or symlinks in the repo root: + - unix: m, activate, moin + - windows: m.bat, activate.bat, deactivate.bat, moin.bat + +Executing m.bat or ./m will run make.py. The name of the python executable is within the m.bat or ./m +script. The location of the virtual env is within the activate and moin symlinks or activate.bat and +moin.bat scripts. Depending upon the command to be executed, some mix of the python executable, moin, +or activate will be used to construct a command string to pass to a subprocess call. +""" + +import os +import sys +import subprocess +import glob +import shutil +import fnmatch +from collections import Counter + +import MoinMoin # validate python version + + +# text files created by commands with high volume output +QUICKINSTALL = 'm-quickinstall.txt' +PYTEST = 'm-pytest.txt' +PEP8 = 'm-pep8.txt' +CODING_STD = 'm-coding-std.txt' +DOCS = 'm-docs.txt' +NEWWIKI = 'm-new-wiki.txt' +DELWIKI = 'm-delete-wiki.txt' +BACKUPWIKI = 'm-backup-wiki.txt' +EXTRAS = 'm-extras.txt' +DIST = 'm-create-dist.txt' +# default files used for backup and restore +BACKUP_FILENAME = 'wiki/backup.moin' +JUST_IN_CASE_BACKUP = 'wiki/deleted-backup.moin' + + +if os.name == 'nt': + M = 'm' # customize help to local OS + WINDOWS_OS = True +else: + M = './m' + WINDOWS_OS = False + + +# commands that create log files; "tests" creates 2 log files - pytest + pep8 +CMD_LOGS = { + 'quickinstall': QUICKINSTALL, + 'pytest': PYTEST, + 'pep8': PEP8, + # 'coding-std': CODING_STD, # not logged due to small output + 'docs': DOCS, + 'new-wiki': NEWWIKI, + 'del-wiki': DELWIKI, + 'backup': BACKUPWIKI, + 'extras': EXTRAS, + 'dist': DIST, +} + + +help = r""" + +usage: "%s <target>" where <target> is: + +quickinstall update virtual environment with required packages +docs create local Sphinx html documentation +extras install OpenID, Pillow, pymongo, sqlalchemy, ldap, upload.py +interwiki refresh contrib\interwiki\intermap.txt (hg version control) +log <target> view detailed log generated by <target>, omit to see list + +new-wiki create empty wiki +sample create wiki and load sample data +restore * create wiki and restore wiki\backup.moin *option, specify file +import <dir> import a moin 1.9 wiki/data instance from <dir> + +run run built-in wiki server with local OS and logging options +backup * roll 3 prior backups and create new backup *option, specify file + +css run Stylus to update CSS files +tests run tests, output goes to pytest.txt and pytestpep8.txt +coding-std correct scripts that taint the repository with trailing spaces.. +api update Sphinx API docs (files are under hg version control) +dist delete wiki data, then create distribution archive in /dist + +del-all same as running the 4 del-* commands below +del-orig delete all files matching *.orig +del-pyc delete all files matching *.pyc +del-rej delete all files matching *.rej +del-wiki create a backup, then delete all wiki data +""" % M + + +def search_for_phrase(filename): + """Search a text file for key phrases and print the lines of interest or print a count by phrase.""" + files = { + # filename: (list of phrases) + QUICKINSTALL: ('could not find', 'error', 'fail', 'timeout', 'traceback', 'success', 'cache location', 'must be deactivated', ), + NEWWIKI: ('error', 'fail', 'timeout', 'traceback', 'success', ), + BACKUPWIKI: ('error', 'fail', 'timeout', 'traceback', 'success', ), + # use of 'error ' below is to avoid matching .../Modules/errors.o.... + EXTRAS: ('error ', 'error:', 'error.', 'error,', 'fail', 'timeout', 'traceback', 'success', 'already satisfied', 'active version', 'installed', 'finished', ), + PYTEST: ('seconds =', ), + PEP8: ('seconds =', ), + CODING_STD: ('remove trailing blanks', 'dos line endings', 'unix line endings', 'remove empty lines', ), + DIST: ('creating', 'copying', 'adding', 'hard linking', ), + DOCS: ('build finished', 'build succeeded', 'traceback', 'failed', 'error', 'usage', 'importerror', 'Exception occurred', ) + } + # for these file names, display a count of occurrances rather than each found line + print_counts = (CODING_STD, DIST, ) + + with open(filename, "r") as f: + lines = f.readlines() + name = os.path.split(filename)[1] + phrases = files[name] + counts = Counter() + for idx, line in enumerate(lines): + line = line.lower() + for phrase in phrases: + if phrase in line: + if filename in print_counts: + counts[phrase] += 1 + else: + print idx + 1, line.rstrip() + break + for key in counts: + print 'The phrase "%s" was found %s times.' % (key, counts[key]) + + +def wiki_exists(): + """Return truthy if a wiki exists.""" + return glob.glob('wiki/index/_all_revs_*.toc') + + +def make_wiki(command): + """Process command to create a new wiki.""" + if wiki_exists(): + print 'Error: a wiki exists, delete it and try again.' + else: + print 'Output messages redirected to %s' % NEWWIKI + with open(NEWWIKI, 'w') as messages: + result = subprocess.call(command, shell=True, stderr=messages, stdout=messages) + if result == 0: + print '\nSuccess: a new wiki has been created.' + else: + print 'Important messages from %s are shown below:' % NEWWIKI + search_for_phrase(NEWWIKI) + print '\nError: attempt to create wiki failed. Do "%s log new-wiki" to see complete log.' % M + + +def delete_files(pattern): + """Recursively delete all files matching pattern.""" + matches = [] + for root, dirnames, filenames in os.walk(os.path.abspath(os.path.dirname(__file__))): + for filename in fnmatch.filter(filenames, pattern): + matches.append(os.path.join(root, filename)) + for match in matches: + os.remove(match) + print 'Deleted %s files matching "%s".' % (len(matches), pattern) + + +class Commands(object): + """Each cmd_ method processes a choice on the menu.""" + def __init__(self): + pass + + def cmd_quickinstall(self, *args): + """create or update a virtual environment with the required packages""" + command = '%s quickinstall.py %s' % (sys.executable, ' '.join(args)) + print 'Running quickinstall.py... output messages redirected to %s' % QUICKINSTALL + with open(QUICKINSTALL, 'w') as messages: + result = subprocess.call(command, shell=True, stderr=messages, stdout=messages) + if result != 0: + open(QUICKINSTALL, 'a').write('Error: quickinstall passed non-zero return code: %s' % result) + print 'Searching %s, important messages are shown below... Do "%s log quickinstall" to see complete log.\n' % (QUICKINSTALL, M) + search_for_phrase(QUICKINSTALL) + + def cmd_docs(self, *args): + """create local Sphinx html documentation""" + if WINDOWS_OS: + command = 'activate.bat & cd docs & make.bat html' # windows separates commands with "&" + else: + # in terminal "source activate" works, but shell requires "source ./activate" + command = 'source ./activate; cd docs; make html' # unix separates commands with ";" + print 'Creating HTML docs... output messages written to %s.' % DOCS + with open(DOCS, 'w') as messages: + result = subprocess.call(command, shell=True, stderr=messages, stdout=messages) + print 'Searching %s, important messages are shown below...\n' % DOCS + search_for_phrase(DOCS) + if result == 0: + print 'HTML docs successfully created in docs/_build/html.' + else: + print 'Error: creation of HTML docs failed with return code "%s". Do "%s log docs" to see complete log.' % (result, M) + + def cmd_extras(self, *args): + """install optional packages: OpenID, Pillow, pymongo, sqlalchemy, ldap; and upload.py""" + upload = '%s MoinMoin/script/win/wget.py https://codereview.appspot.com/static/upload.py upload.py' % sys.executable + if WINDOWS_OS: + print 'Installing OpenId, Pillow, pymongo, sqlalchemy, upload.py... output messages written to %s.' % EXTRAS + # easy_install is used for windows because it installs binary packages, pip does not + command = 'activate.bat & easy_install python-openid & easy_install pillow & easy_install pymongo & easy_install sqlalchemy' + ' & ' + upload + # TODO: "easy_install python-ldap" fails on windows + # try google: installing python-ldap in a virtualenv on windows + # or, download from http://www.lfd.uci.edu/~gohlke/pythonlibs/#python-ldap + # activate.bat + # easy_install <path to downloaded .exe file> + else: + print 'Installing OpenId, Pillow, pymongo, sqlalchemy, ldap, upload.py... output messages written to %s.' % EXTRAS + command = 'source ./activate; pip install python-openid; pip install pillow; pip install pymongo; pip install sqlalchemy; pip install python-ldap' + '; ' + upload + with open(EXTRAS, 'w') as messages: + subprocess.call(command, shell=True, stderr=messages, stdout=messages) + print 'Important messages from %s are shown below. Do "%s log extras" to see complete log.' % (EXTRAS, M) + search_for_phrase(EXTRAS) + + def cmd_interwiki(self, *args): + """refresh contrib\interwiki\intermap.txt""" + print 'Refreshing contrib\interwiki\intermap.txt...' + command = '%s MoinMoin/script/win/wget.py http://master19.moinmo.in/InterWikiMap?action=raw contrib/interwiki/intermap.txt' % sys.executable + subprocess.call(command, shell=True) + + def cmd_log(self, *args): + """View a log file with the default text editor""" + + def log_help(logs): + """Print list of available logs to view.""" + print "usage: %s log <target> where <target> is:\n\n" % M + choices = '{0: <16}- {1}' + for log in sorted(logs): + if os.path.isfile(CMD_LOGS[log]): + print choices.format(log, CMD_LOGS[log]) + else: + print choices.format(log, '* file does not exist') + + logs = set(CMD_LOGS.keys()) + if args and args[0] in logs and os.path.isfile(CMD_LOGS[args[0]]): + if WINDOWS_OS: + command = 'start %s' % CMD_LOGS[args[0]] + else: + command = '${VISUAL:-${FCEDIT:-${EDITOR:-less}}} %s' % CMD_LOGS[args[0]] + subprocess.call(command, shell=True) + else: + log_help(logs) + + def cmd_new_wiki(self, *args): + """create empty wiki""" + if WINDOWS_OS: + command = 'moin.bat index-create -s -i' + else: + command = './moin index-create -s -i' + print 'Creating a new empty wiki...' + make_wiki(command) # share code with loading sample data and restoring backups + + def cmd_sample(self, *args): + """create wiki and load sample data""" + if WINDOWS_OS: + command = 'moin.bat index-create -s -i & moin.bat load --file contrib/serialized/items.moin & moin.bat index-build' + else: + command = './moin index-create -s -i; ./moin load --file contrib/serialized/items.moin; ./moin index-build' + print 'Creating a new wiki populated with sample data...' + make_wiki(command) + + def cmd_restore(self, *args): + """create wiki and load data from wiki/backup.moin or user specified path""" + if WINDOWS_OS: + command = 'moin.bat index-create -s -i & moin.bat load --file %s & moin.bat index-build' + else: + command = './moin index-create -s -i; ./moin load --file %s; ./moin index-build' + filename = BACKUP_FILENAME + if args: + filename = args[0] + if os.path.isfile(filename): + command = command % filename + print 'Creating a new wiki and loading it with data from %s...' % filename + make_wiki(command) + else: + print 'Error: cannot create wiki because %s does not exist.' % filename + + def cmd_import(self, *args): + """import a moin 1.9 wiki directory named dir""" + if WINDOWS_OS: + command = 'moin.bat import19 -s -i --data_dir %s' + else: + command = './moin import19 -s -i --data_dir %s' + if args: + dirname = args[0] + if os.path.isdir(dirname): + command = command % dirname + print 'Creating a new wiki populated with data from %s...' % dirname + make_wiki(command) + else: + print 'Error: cannot create wiki because %s does not exist.' % dirname + else: + print 'Error: a path to the Moin 1.9 wiki/data data directory is required.' + + def cmd_run(self, *args): + """run built-in wiki server with local options""" + if wiki_exists(): + if os.path.isfile('logging.conf'): + if WINDOWS_OS: + logfile = 'set MOINLOGGINGCONF=logging.conf & ' + else: + logfile = 'MOINLOGGINGCONF=logging.conf; export MOINLOGGINGCONF; ' + else: + logfile = '' + if WINDOWS_OS: + command = '%smoin.bat moin %s --threaded' % (logfile, ' '.join(args)) + else: + command = '%s./moin moin %s' % (logfile, ' '.join(args)) + try: + subprocess.call(command, shell=True) + except KeyboardInterrupt: + pass # on windows pass eliminates traceback but "Terminate batch job..." message is displayed twice + else: + print 'Error: a wiki must be created before running the built-in server.' + + def cmd_backup(self, *args): + """roll 3 prior backups and create new wiki/backup.moin or backup to user specified file""" + if wiki_exists(): + filename = BACKUP_FILENAME + if args: + filename = args[0] + print 'Creating a wiki backup to %s...' % filename + else: + print 'Creating a wiki backup to %s after rolling 3 prior backups...' % filename + b3 = BACKUP_FILENAME.replace('.', '3.') + b2 = BACKUP_FILENAME.replace('.', '2.') + b1 = BACKUP_FILENAME.replace('.', '1.') + if os.path.exists(b3): + os.remove(b3) + for src, dst in ((b2, b3), (b1, b2), (BACKUP_FILENAME, b1)): + if os.path.exists(src): + os.rename(src, dst) + if WINDOWS_OS: + command = 'moin.bat save --all-backends --file %s' % filename + else: + command = './moin save --all-backends --file %s' % filename + with open(BACKUPWIKI, 'w') as messages: + result = subprocess.call(command, shell=True, stderr=messages, stdout=messages) + if result == 0: + print 'Success: wiki was backed up to %s' % filename + else: + print 'Important messages from %s are shown below. Do "%s log backup" to see complete log.' % (BACKUPWIKI, M) + search_for_phrase(BACKUPWIKI) + print '\nError: attempt to backup wiki failed.' + else: + print 'Error: cannot backup wiki because it has not been created.' + + def cmd_css(self, *args): + """run Stylus to update CSS files""" + print 'Running Stylus to update CSS files...' + if WINDOWS_OS: + command = r'cd MoinMoin\themes\modernized\static\css\stylus & stylus --include-css --compress < main.styl > ../common.css' + else: + command = 'cd MoinMoin/themes/modernized/static/css/stylus; stylus --include-css --compress < main.styl > ../common.css' + result = subprocess.call(command, shell=True) + + if WINDOWS_OS: + command = r'cd MoinMoin\themes\foobar\static\css\stylus & stylus --include-css --compress < main.styl > ../common.css' + else: + command = 'cd MoinMoin/themes/foobar/static/css/stylus; stylus --include-css --compress < main.styl > ../common.css' + result2 = subprocess.call(command, shell=True) + + if result == 0 and result2 == 0: + print 'Success: CSS files updated.' + else: + print 'Error: stylus failed to update css files, see error messages above.' + + def cmd_tests(self, *args): + """run tests, output goes to pytest.txt and pytestpep8.txt""" + print 'Running tests... output written to %s and %s.' % (PYTEST, PEP8) + if WINDOWS_OS: + command = 'activate.bat & py.test.exe > %s 2>&1 & py.test.exe --pep8 -k pep8 --clearcache > %s 2>&1' % (PYTEST, PEP8) + else: + command = 'source ./activate; py.test > %s 2>&1; py.test --pep8 -k pep8 --clearcache > %s 2>&1' % (PYTEST, PEP8) + result = subprocess.call(command, shell=True) + print 'Summary message from %s is shown below. Do "%s log pytest" to see complete log.' % (PYTEST, M) + search_for_phrase(PYTEST) + print 'Summary message from %s is shown below. Do "%s log pep8" to see complete log.' % (PEP8, M) + search_for_phrase(PEP8) + + def cmd_coding_std(self, *args): + """correct scripts that taint the HG repository and clutter subsequent code reviews""" + print 'Checking for trailing blanks, DOS line endings, Unix line endings, empty lines at eof...' + command = '%s contrib/pep8/coding_std.py' % sys.executable + subprocess.call(command, shell=True) + + def cmd_api(self, *args): + """update Sphinx API docs, these docs are under hg version control""" + print 'Refreshing api docs...' + if WINDOWS_OS: + command = 'activate.bat & sphinx-apidoc -f -o docs/devel/api MoinMoin & %s MoinMoin/script/win/dos2unix.py docs/devel/api' % sys.executable + else: + command = 'source ./activate; sphinx-apidoc -f -o docs/devel/api MoinMoin' + result = subprocess.call(command, shell=True) + + def cmd_dist(self, *args): + """create distribution archive in dist/""" + print 'Deleting wiki data, then creating distribution archive in /dist, output written to %s.' % DIST + self.cmd_del_wiki(*args) + command = '%s setup.py sdist' % sys.executable + with open(DIST, 'w') as messages: + result = subprocess.call(command, shell=True, stderr=messages, stdout=messages) + print 'Summary message from %s is shown below:' % DIST + search_for_phrase(DIST) + if result == 0: + print 'Success: a distribution archive was created in /dist.' + else: + print 'Error: create dist failed with return code = %s. Do "%s log dist" to see complete log.' % (result, M) + + def cmd_del_all(self, *args): + """same as running the 4 del-* commands below""" + self.cmd_del_orig(*args) + self.cmd_del_pyc(*args) + self.cmd_del_rej(*args) + self.cmd_del_wiki(*args) + + def cmd_del_orig(self, *args): + """delete all files matching *.orig""" + delete_files('*.orig') + + def cmd_del_pyc(self, *args): + """delete all files matching *.pyc""" + delete_files('*.pyc') + + def cmd_del_rej(self, *args): + """delete all files matching *.rej""" + delete_files('*.rej') + + def cmd_del_wiki(self, *args): + """create a just-in-case backup, then delete all wiki data""" + if WINDOWS_OS: + command = 'moin.bat save --all-backends --file %s' % JUST_IN_CASE_BACKUP + else: + command = './moin save --all-backends --file %s' % JUST_IN_CASE_BACKUP + if wiki_exists(): + print 'Creating a backup named %s; then deleting all wiki data and indexes...' % JUST_IN_CASE_BACKUP + with open(DELWIKI, 'w') as messages: + result = subprocess.call(command, shell=True, stderr=messages, stdout=messages) + if result != 0: + print 'Error: backup failed with return code = %s. Complete log is in %s.' % (result, DELWIKI) + # destroy wiki even if backup fails + if os.path.isdir('wiki/data') or os.path.isdir('wiki/index'): + shutil.rmtree('wiki/data') + shutil.rmtree('wiki/index') + print 'Wiki data successfully deleted.' + else: + print 'Wiki data not deleted because it does not exist.' + + +if __name__ == '__main__': + # create a set of valid menu choices + commands = Commands() + choices = set() + names = dir(commands) + for name in names: + if name.startswith('cmd_'): + choices.add(name) + + if len(sys.argv) == 1 or sys.argv[1] == '-h' or sys.argv[1] == '--help': + print help + else: + if sys.argv[1] != 'quickinstall' and not (os.path.isfile('activate') or os.path.isfile('activate.bat')): + print 'Error: files created by quickinstall are missing, run "%s quickinstall" and try again.' % M + else: + choice = 'cmd_%s' % sys.argv[1] + choice = choice.replace('-', '_') + if choice in choices: + choice = getattr(commands, choice) + choice(*sys.argv[2:]) + else: + print help + print 'Error: unknown menu selection "%s"' % sys.argv[1]
--- a/makemoinmenu.py Tue Dec 03 11:46:31 2013 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -#!/usr/bin/python -# Copyright: 2013 MoinMoin:RogerHaase -# License: GNU GPL v2 (or any later version), see LICENSE.txt for details. - -""" -MakeMoinMenu.py - create a tiny script that provides a menu making it easy to run common moin2 admin tasks. -Then run quickinstall process to create a virtual env and install required packages. -""" - -import os -import sys - -import MoinMoin # validate python version -from m import help, Menu - -# run the script from the hosts main Python 2.7 installation, the virtual env may not exist -if os.name == 'nt': - with open('m.bat', 'w') as f: - f.write('@{} m.py %*\n'.format(sys.executable)) -else: - with open('m', 'w') as f: - f.write('{} m.py $*\n'.format(sys.executable)) - os.fchmod(f.fileno(), 0775) -# run quickinstall to create or refresh the virtual env using the menu process -menu = Menu() -choice = getattr(menu, 'cmd_quickinstall') -choice(*sys.argv[1:]) # <override-path-to-venv> --download_cache <override-path-to-cache> -# give user a hint as to what to do next -if os.name == 'nt': - print '\n> > > Type "m" for menu < < <' -else: - print '\n> > > Type "./m" for menu < < <'
--- a/quickinstall.py Tue Dec 03 11:46:31 2013 -0700 +++ b/quickinstall.py Thu Dec 19 14:53:20 2013 -0700 @@ -22,21 +22,40 @@ or the virtual environment must be deactivated before rerunning quickinstall.py """) +from make import Commands, WINDOWS_OS, M + + +WIN_INFO = 'm.bat, activate.bat, deactivate.bat, and moin.bat are created by quickinstall.py' +NIX_INFO = 'the m bash script and the activate and moin symlinks are created by quickinstall.py' + + +def create_m(): + """Create an 'm.bat or 'm' bash script that will run make.py using this Python""" + if WINDOWS_OS: + with open('m.bat', 'w') as f: + f.write(':: {}\n\n@{} make.py %*\n'.format(WIN_INFO, sys.executable)) + else: + with open('m', 'w') as f: + f.write('# {}\n\n{} make.py $*\n'.format(NIX_INFO, sys.executable)) + os.fchmod(f.fileno(), 0775) + class QuickInstall(object): def __init__(self, source, venv=None, download_cache=None): self.dir_source = source if venv is None: base, source_name = os.path.split(source) - venv = os.path.join(base, '{}-venv'.format(source_name)) + executable = os.path.basename(sys.executable).split('.exe')[0] + venv = os.path.join(base, '{}-venv-{}'.format(source_name, executable)) if download_cache is None: # make cache sibling of ~/pip/pip.log or ~/.pip/pip.log - if os.name == 'nt': + if WINDOWS_OS: download_cache = '~/pip/pip-download-cache' else: # XXX: move cache to XDG cache dir download_cache = '~/.pip/pip-download-cache' + venv = os.path.abspath(venv) venv_home, venv_lib, venv_inc, venv_bin = virtualenv.path_locations(venv) self.dir_venv = venv_home self.dir_venv_bin = venv_bin @@ -76,19 +95,20 @@ '--directory', os.path.join(os.path.dirname(__file__), 'MoinMoin', 'translations'), )) - def do_helpers(self): - """Create small helper scripts or symlinks in repo root.""" + def create_wrapper(self, filename, target): + """Create files in the repo root that wrap files in <path-to-virtual-env>\Scripts.""" + target = os.path.join(self.dir_venv_bin, target) + with open(filename, 'w') as f: + f.write(':: {}\n\n@call {} %*\n'.format(WIN_INFO, target)) - def create_wrapper(filename, contents): - """Create files in the repo root that wrap files in the v-env/bin or v-env\Scripts.""" - with open(filename, 'w') as f: - f.write(contents) - - if os.name == 'nt': + def do_helpers(self): + """Create small helper scripts or symlinks in repo root, avoid keying the long path to virtual env.""" + create_m() # recreate m.bat or ./m to insure it is consistent with activate and moin + if WINDOWS_OS: # windows commands are: activate | deactivate | moin - create_wrapper('activate.bat', '@call {}\n'.format(os.path.join(self.dir_venv_bin, 'activate.bat'))) - create_wrapper('deactivate.bat', '@call {}\n'.format(os.path.join(self.dir_venv_bin, 'deactivate.bat'))) - create_wrapper('moin.bat', '@call {} %*\n'.format(os.path.join(self.dir_venv_bin, 'moin.exe'))) + self.create_wrapper('activate.bat', 'activate.bat') + self.create_wrapper('deactivate.bat', 'deactivate.bat') + self.create_wrapper('moin.bat', 'moin.exe') else: # linux commands are: source activate | deactivate | ./moin if os.path.exists('activate'): @@ -100,11 +120,21 @@ if __name__ == '__main__': - logging.basicConfig(level=logging.INFO) + if os.path.isfile('m') or os.path.isfile('m.bat'): + # create the virtual env + logging.basicConfig(level=logging.INFO) - parser = argparse.ArgumentParser() - parser.add_argument('venv', metavar='VENV', nargs='?', help='location of v(irtual)env') - parser.add_argument('--download_cache', dest='download_cache', help='location of pip download cache') - args = parser.parse_args() + parser = argparse.ArgumentParser() + parser.add_argument('venv', metavar='VENV', nargs='?', help='location of v(irtual)env') + parser.add_argument('--download_cache', dest='download_cache', help='location of pip download cache') + args = parser.parse_args() - QuickInstall(os.path.dirname(os.path.realpath(sys.argv[0])), venv=args.venv, download_cache=args.download_cache)() + QuickInstall(os.path.dirname(os.path.realpath(sys.argv[0])), venv=args.venv, download_cache=args.download_cache)() + else: + # run this same script (quickinstall.py) again to create the virtual env + create_m() # create file so above IF will be true next time around + # Use the make.py subprocess so user will see a few success/failure messages instead of ~500 info messages. + commands = Commands() + choice = getattr(commands, 'cmd_quickinstall') + choice(*sys.argv[1:]) # <override-path-to-venv> --download_cache <override-path-to-cache> + print '\n> > > Type "%s" for menu < < <' % M