diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 26ae338a..37fc7ffb 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -27,7 +27,7 @@ Before you ask for help, please make sure you do the following: * no plugins or only those related to the issue **NOTE:** The most common sources of problems are anomalies in (1) themes, -(2) settings files, and (3) ``make``/``fab`` automation wrappers. If you can't +(2) settings files, and (3) ``make``/``invoke`` automation wrappers. If you can't reproduce your problem when using the following steps to generate your site, then the problem is almost certainly with your chosen theme and/or settings file (and not Pelican itself):: diff --git a/docs/changelog.rst b/docs/changelog.rst index aa594a2c..883c86d0 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,6 +5,7 @@ Next release ============ * New signal: ``feed_generated`` +* Replace Fabric by Invoke and ``fabfile.py`` template by ``tasks.py``. 3.7.1 (2017-01-10) ================== diff --git a/docs/install.rst b/docs/install.rst index 8075632f..09097666 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -101,9 +101,9 @@ can optionally add yourself if you plan to create non-chronological content):: yourproject/ ├── content - │   └── (pages) + │ └── (pages) ├── output - ├── fabfile.py + ├── tasks.py ├── Makefile ├── pelicanconf.py # Main settings file └── publishconf.py # Settings to use when ready to publish diff --git a/docs/publish.rst b/docs/publish.rst index 7fb4b587..9b49ae3f 100644 --- a/docs/publish.rst +++ b/docs/publish.rst @@ -101,7 +101,7 @@ While the ``pelican`` command is the canonical way to generate your site, automation tools can be used to streamline the generation and publication flow. One of the questions asked during the ``pelican-quickstart`` process pertains to whether you want to automate site generation and publication. -If you answered "yes" to that question, a ``fabfile.py`` and +If you answered "yes" to that question, a ``tasks.py`` and ``Makefile`` will be generated in the root of your project. These files, pre-populated with certain information gleaned from other answers provided during the ``pelican-quickstart`` process, are meant as a starting point and @@ -113,55 +113,43 @@ files can deleted at any time and will not affect usage of the canonical Following are automation tools that "wrap" the ``pelican`` command and can simplify the process of generating, previewing, and uploading your site. -Fabric +Invoke ------ -The advantage of Fabric_ is that it is written in Python and thus can be used +The advantage of Invoke_ is that it is written in Python and thus can be used in a wide range of environments. The downside is that it must be installed -separately. Use the following command to install Fabric, prefixing with +separately. Use the following command to install Invoke, prefixing with ``sudo`` if your environment requires it:: - pip install Fabric + pip install invoke -.. note:: Installing PyCrypto on Windows - - Fabric depends upon PyCrypto_, which is tricky to install - if your system doesn't have a C compiler. - For Windows users, before installing Fabric, use - ``easy_install http://www.voidspace.org.uk/downloads/pycrypto26/pycrypto-2.6.win32-py2.7.exe`` - per this `StackOverflow suggestion `_ - You're more likely to have success - with the Win32 versions of Python 2.7 and PyCrypto, - than with the Win64—\ - even if your operating system is a 64-bit version of Windows. - -Take a moment to open the ``fabfile.py`` file that was generated in your +Take a moment to open the ``tasks.py`` file that was generated in your project root. You will see a number of commands, any one of which can be renamed, removed, and/or customized to your liking. Using the out-of-the-box configuration, you can generate your site via:: - fab build + invoke build If you'd prefer to have Pelican automatically regenerate your site every time a change is detected (which is handy when testing locally), use the following command instead:: - fab regenerate + invoke regenerate To serve the generated site so it can be previewed in your browser at http://localhost:8000/:: - fab serve + invoke serve If during the ``pelican-quickstart`` process you answered "yes" when asked whether you want to upload your site via SSH, you can use the following command to publish your site via rsync over SSH:: - fab publish + invoke publish These are just a few of the commands available by default, so feel free to -explore ``fabfile.py`` and see what other commands are available. More -importantly, don't hesitate to customize ``fabfile.py`` to suit your specific +explore ``tasks.py`` and see what other commands are available. More +importantly, don't hesitate to customize ``tasks.py`` to suit your specific needs and preferences. Make @@ -216,5 +204,4 @@ That's it! Your site should now be live. executables, such as ``python3``, you can set the ``PY`` and ``PELICAN`` environment variables, respectively, to override the default executable names.) -.. _Fabric: http://fabfile.org/ -.. _PyCrypto: http://pycrypto.org +.. _Invoke: http://www.pyinvoke.org diff --git a/docs/tips.rst b/docs/tips.rst index 4e89d148..f9da8217 100644 --- a/docs/tips.rst +++ b/docs/tips.rst @@ -67,7 +67,7 @@ already exist). The ``git push origin gh-pages`` command updates the remote ``gh-pages`` branch, effectively publishing the Pelican site. .. note:: - The ``github`` target of the Makefile (and the ``gh_pages`` task of the Fabfile) + The ``github`` target of the Makefile (and the ``gh_pages`` task of ``tasks.py``) created by the ``pelican-quickstart`` command publishes the Pelican site as Project Pages, as described above. diff --git a/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py index 7033cf28..1c6cbce9 100755 --- a/pelican/tools/pelican_quickstart.py +++ b/pelican/tools/pelican_quickstart.py @@ -266,7 +266,7 @@ needed by Pelican. CONF['timezone'] = ask_timezone('What is your time zone?', CONF['timezone'], _TZ_URL) - automation = ask('Do you want to generate a Fabfile/Makefile ' + automation = ask('Do you want to generate a tasks.py/Makefile ' 'to automate generation and publishing?', bool, True) if automation: @@ -364,9 +364,9 @@ needed by Pelican. if automation: try: - with codecs.open(os.path.join(CONF['basedir'], 'fabfile.py'), + with codecs.open(os.path.join(CONF['basedir'], 'tasks.py'), 'w', 'utf-8') as fd: - _template = _jinja_env.get_template('fabfile.py.jinja2') + _template = _jinja_env.get_template('tasks.py.jinja2') fd.write(_template.render(**CONF)) fd.close() except OSError as e: diff --git a/pelican/tools/templates/fabfile.py.jinja2 b/pelican/tools/templates/fabfile.py.jinja2 deleted file mode 100644 index 467f9ec2..00000000 --- a/pelican/tools/templates/fabfile.py.jinja2 +++ /dev/null @@ -1,95 +0,0 @@ -from fabric.api import * -import fabric.contrib.project as project -import os -import shutil -import sys - -# Local path configuration (can be absolute or relative to fabfile) -env.deploy_path = 'output' -DEPLOY_PATH = env.deploy_path - -{% if ssh %} -# Remote server configuration -production = '{{ssh_user}}@{{ssh_host}}:{{ssh_port}}' -dest_path = '{{ssh_target_dir}}' - -{% endif %} -{% if cloudfiles %} -# Rackspace Cloud Files configuration settings -env.cloudfiles_username = '{{cloudfiles_username}}' -env.cloudfiles_api_key = '{{cloudfiles_api_key}}' -env.cloudfiles_container = '{{cloudfiles_container}}' - -{% endif %} -{% if github %} -# Github Pages configuration -env.github_pages_branch = '{{github_pages_branch}}' -{% endif %} - -# Port for `serve` -PORT = 8000 - -def clean(): - """Remove generated files""" - if os.path.isdir(DEPLOY_PATH): - shutil.rmtree(DEPLOY_PATH) - os.makedirs(DEPLOY_PATH) - -def build(): - """Build local version of site""" - local('pelican -s pelicanconf.py') - -def rebuild(): - """`build` with the delete switch""" - local('pelican -d -s pelicanconf.py') - -def regenerate(): - """Automatically regenerate site upon file modification""" - local('pelican -r -s pelicanconf.py') - -def serve(): - """Serve site at http://localhost:8000/""" - local('pelican -l -s pelicanconf.py') - -def devserver(): - """Serve site at http://localhost:8000/ and regenerate automatically""" - local('pelican -r -l -s pelicanconf.py') - -def reserve(): - """`build`, then `serve`""" - build() - serve() - -def preview(): - """Build production version of site""" - local('pelican -s publishconf.py') - -{% if cloudfiles %} -def cf_upload(): - """Publish to Rackspace Cloud Files""" - rebuild() - with lcd(DEPLOY_PATH): - local('swift -v -A https://auth.api.rackspacecloud.com/v1.0 ' - '-U {cloudfiles_username} ' - '-K {cloudfiles_api_key} ' - 'upload -c {cloudfiles_container} .'.format(**env)) -{% endif %} - -@hosts(production) -def publish(): - """Publish to production via rsync""" - local('pelican -s publishconf.py') - project.rsync_project( - remote_dir=dest_path, - exclude=".DS_Store", - local_dir=DEPLOY_PATH.rstrip('/') + '/', - delete=True, - extra_opts='-c', - ) - -{% if github %} -def gh_pages(): - """Publish to GitHub Pages""" - rebuild() - local("ghp-import -b {github_pages_branch} {deploy_path} -p".format(**env)) -{% endif %} diff --git a/pelican/tools/templates/tasks.py.jinja2 b/pelican/tools/templates/tasks.py.jinja2 new file mode 100644 index 00000000..d9645e19 --- /dev/null +++ b/pelican/tools/templates/tasks.py.jinja2 @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- + +import os +import shutil +import sys +try: + import socketserver +except ImportError: + import SocketServer as socketserver + +from invoke import task +from invoke.util import cd +from pelican.server import ComplexHTTPRequestHandler + +CONFIG = { + # Local path configuration (can be absolute or relative to tasks.py) + 'deploy_path': 'output', +{% if ssh %} + # Remote server configuration + 'production': '{{ssh_user}}@{{ssh_host}}:{{ssh_port}}', + 'dest_path': '{{ssh_target_dir}}', +{% endif %} +{% if cloudfiles %} + # Rackspace Cloud Files configuration settings + 'cloudfiles_username': '{{cloudfiles_username}}', + 'cloudfiles_api_key': '{{cloudfiles_api_key}}', + 'cloudfiles_container': '{{cloudfiles_container}}', +{% endif %} +{% if github %} + # Github Pages configuration + 'github_pages_branch': '{{github_pages_branch}}', +{% endif %} + # Port for `serve` + 'port': 8000, +} + +@task +def clean(c): + """Remove generated files""" + if os.path.isdir(CONFIG['deploy_path']): + shutil.rmtree(CONFIG['deploy_path']) + os.makedirs(CONFIG['deploy_path']) + +@task +def build(c): + """Build local version of site""" + c.run('pelican -s pelicanconf.py') + +@task +def rebuild(c): + """`build` with the delete switch""" + c.run('pelican -d -s pelicanconf.py') + +@task +def regenerate(c): + """Automatically regenerate site upon file modification""" + c.run('pelican -r -s pelicanconf.py') + +@task +def serve(c): + """Serve site at http://localhost:8000/""" + os.chdir(CONFIG['deploy_path']) + + class AddressReuseTCPServer(socketserver.TCPServer): + allow_reuse_address = True + + server = AddressReuseTCPServer( + ('', CONFIG['port']), + ComplexHTTPRequestHandler) + + sys.stderr.write('Serving on port {port} ...\n'.format(**CONFIG)) + server.serve_forever() + +@task +def reserve(c): + """`build`, then `serve`""" + build(c) + serve(c) + +@task +def preview(c): + """Build production version of site""" + c.run('pelican -s publishconf.py') + +{% if cloudfiles %} +@task +def cf_upload(c): + """Publish to Rackspace Cloud Files""" + rebuild(c) + with cd(CONFIG['deploy_path']): + c.run('swift -v -A https://auth.api.rackspacecloud.com/v1.0 ' + '-U {cloudfiles_username} ' + '-K {cloudfiles_api_key} ' + 'upload -c {cloudfiles_container} .'.format(**CONFIG)) +{% endif %} + +@task +def publish(c): + """Publish to production via rsync""" + c.run('pelican -s publishconf.py') + c.run( + 'rsync --delete --exclude ".DS_Store" -pthrvz -c ' + '{} {production}:{dest_path}'.format( + CONFIG['deploy_path'].rstrip('/') + '/', + **CONFIG)) + +{% if github %} +@task +def gh_pages(c): + """Publish to GitHub Pages""" + rebuild(c) + c.run("ghp-import -b {github_pages_branch} {deploy_path} -p".format( + **CONFIG)) +{% endif %}