diff --git a/ablog/__init__.py b/ablog/__init__.py index c284a71..144d889 100755 --- a/ablog/__init__.py +++ b/ablog/__init__.py @@ -8,7 +8,7 @@ from .post import (PostDirective, PostListDirective, UpdateDirective, UpdateNode, process_posts, process_postlist, purge_posts, generate_archive_pages, generate_atom_feeds) -__version__ = '0.5.1' +__version__ = '0.6' def anchor(post): diff --git a/ablog/commands.py b/ablog/commands.py index 7c5c1e2..09a543e 100644 --- a/ablog/commands.py +++ b/ablog/commands.py @@ -1,10 +1,14 @@ import os import sys +import glob import ablog +import shutil import argparse + + def find_confdir(subparser): from os.path import isfile, join, abspath @@ -107,7 +111,7 @@ def ablog_build(subparser, **kwargs): subparser = ablog_commands.add_parser('build', help='build your blog project', - description="Build options can be set in conf.py. " + description="Path options can be set in conf.py. " "Default values of paths are relative to conf.py.") subparser.add_argument('-b', dest='builder', type=str, @@ -116,14 +120,14 @@ subparser.add_argument('-b', dest='builder', type=str, subparser.add_argument('-d', dest='doctrees', type=str, default='.doctrees', help="path for the cached environment and doctree files, " - "default `ablog_doctrees` or .doctrees") + "default .doctrees when `ablog_doctrees` is not set in conf.py") subparser.add_argument('-s', dest='sourcedir', type=str, help="root path for source files, " "default is path to the folder that contains conf.py") subparser.add_argument('-w', dest='website', type=str, - help="path for website, default `ablog_website` or _website") + help="path for website, default is _website when `ablog_website` is not set in conf.py") subparser.add_argument('-T', dest='traceback', action='store_true', default=False, @@ -133,6 +137,50 @@ subparser.set_defaults(func=lambda ns: ablog_build(**ns.__dict__)) subparser.set_defaults(subparser=subparser) +def ablog_clean(subparser, **kwargs): + + confdir = find_confdir(subparser) + conf = read_conf(confdir) + + website = (kwargs['website'] or + os.path.join(confdir, getattr(conf, 'ablog_builddir', '_website'))) + + doctrees = (kwargs['doctrees'] or + os.path.join(confdir, getattr(conf, 'ablog_doctrees', '.doctrees'))) + + + if glob.glob(os.path.join(website, '*')): + shutil.rmtree(website) + print('Removed {}.'.format(os.path.relpath(website))) + else: + print('Nothing to clean.') + + if kwargs['deep'] and glob.glob(os.path.join(doctrees, '*')): + shutil.rmtree(doctrees) + print('Removed {}.'.format(os.path.relpath(doctrees))) + + +subparser = ablog_commands.add_parser('clean', + help='clean your blog build files', + description="Path options can be set in conf.py. " + "Default values of paths are relative to conf.py.") + +subparser.add_argument('-d', dest='doctrees', type=str, + default='.doctrees', + help="path for the cached environment and doctree files, " + "default .doctrees when `ablog_doctrees` is not set in conf.py") + +subparser.add_argument('-w', dest='website', type=str, + help="path for website, default is _website when `ablog_website` is not set in conf.py") + +subparser.add_argument('-D', dest='deep', + action='store_true', default=False, + help="deep clean, remove cached environment and doctree files") + + +subparser.set_defaults(func=lambda ns: ablog_clean(**ns.__dict__)) +subparser.set_defaults(subparser=subparser) + def ablog_serve(subparser, **kwargs): @@ -178,16 +226,18 @@ subparser.add_argument('-p', dest='port', type=int, subparser.add_argument('-w', dest='website', type=str, help="path for website, " - "default `ablog_website` or _website") + "default is _website when `ablog_website` is not set in conf.py") subparser.set_defaults(func=lambda ns: ablog_serve(**ns.__dict__)) subparser.set_defaults(subparser=subparser) + def ablog_post(subparser, **kwargs): POST_TEMPLATE =u''' %(title)s ==================== + .. post:: %(date)s :tags: :category: @@ -237,6 +287,78 @@ subparser.add_argument(dest='filename', type=str, subparser.set_defaults(func=lambda ns: ablog_post(**ns.__dict__)) subparser.set_defaults(subparser=subparser) +def ablog_deploy(subparser, **kwargs): + + confdir = find_confdir(subparser) + conf = read_conf(confdir) + + github_pages = (kwargs['github_pages'] or + getattr(conf, 'github_pages') or None) + + #from subprocess import call + + website = (kwargs['website'] or + os.path.join(confdir, getattr(conf, 'ablog_builddir', '_website'))) + + tomove = glob.glob(os.path.join(website, '*')) + if not tomove: + print('Nothing to deploy, build first.') + return + + try: + from invoke import run + except ImportError: + raise ImportError("invoke is required by deploy command, " + "run `pip install invoke`") + + + if github_pages: + + gitdir = os.path.join(confdir, "{0}.github.io".format(github_pages)) + if os.path.isdir(gitdir): + os.chdir(gitdir) + run("git pull", echo=True) + else: + run("git clone https://github.com/{0}/{0}.github.io.git" + .format(github_pages), echo=True) + + git_add = [] + for tm in tomove: + for root, dirnames, filenames in os.walk(website): + for filename in filenames: + fn = os.path.join(root, filename) + fnnew = fn.replace(website, gitdir) + os.renames(fn, fnnew) + git_add.append(fnnew) + print('Moved {} files to {}.github.io' + .format(len(git_add), github_pages)) + os.chdir(gitdir) + + run("git add -f " + + " ".join([os.path.relpath(p) for p in git_add]), echo=True) + run('git commit -m "Updates."', echo=True) + run('git push', echo=True) + + else: + print('No place to deploy.') + + +subparser = ablog_commands.add_parser('deploy', + help='deploy your website build files', + description="Path options can be set in conf.py. " + "Default values of paths are relative to conf.py.") + +subparser.add_argument('-g', dest='github_pages', type=str, + help="GitHub user name for deploying to GitHub pages") + +subparser.add_argument('-w', dest='website', type=str, + help="path for website, default is _website when `ablog_website` is not set in conf.py") + + +subparser.set_defaults(func=lambda ns: ablog_deploy(**ns.__dict__)) +subparser.set_defaults(subparser=subparser) + + def ablog_main(): diff --git a/docs/index.rst b/docs/index.rst index 0844c09..dedce9a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -28,14 +28,14 @@ You can install ABlog using pip_:: pip install -U ablog -This will also install `Sphinx `_, Alabaster_, and -Werkzeug_, respectively required for building your website, making it look -good, and generating feeds. +This will also install `Sphinx `_, Alabaster_, +Werkzeug_, and Invoke_ respectively required for building your website, +making it look good, generating feeds, and running deploy commands. .. _pip: https://pip.pypa.io .. _Werkzeug: http://werkzeug.pocoo.org/ .. _Alabaster: https://github.com/bitprophet/alabaster - +.. _Invoke: http://www.pyinvoke.org/ Getting Started --------------- diff --git a/docs/manual/ablog-commands.rst b/docs/manual/ablog-commands.rst index f1da872..34f4325 100644 --- a/docs/manual/ablog-commands.rst +++ b/docs/manual/ablog-commands.rst @@ -16,7 +16,7 @@ and viewing blog pages, as well as starting a new blog. :: $ ablog - usage: ablog [-h] [-v] {start,build,serve,post} ... + usage: ablog [-h] [-v] {start,build,clean,serve,post,deploy} ... ABlog for blogging with Sphinx @@ -25,15 +25,23 @@ and viewing blog pages, as well as starting a new blog. -v, --version print ABlog version and exit subcommands: - {start,build,serve,post} + {start,build,clean,serve,post,deploy} start start a new blog project build build your blog project + clean clean your blog build files serve serve and view your project post create a blank post + deploy deploy your website build files See 'ablog -h' for more information on a specific command. + + +.. contents:: Here are all the things you can do: + :local: + :backlinks: top + Start a Project --------------- @@ -43,7 +51,7 @@ Start a Project :: - $ ablog serve -h + $ ablog start -h usage: ablog start [-h] Start a new blog project with in less than 10 seconds. After answering a few @@ -65,17 +73,18 @@ Running ``ablog build`` in your project folder builds your website HTML pages. usage: ablog build [-h] [-b BUILDER] [-d DOCTREES] [-s SOURCEDIR] [-w WEBSITE] [-T] - Build options can be set in conf.py. Default values of paths are relative to + Path options can be set in conf.py. Default values of paths are relative to conf.py. optional arguments: -h, --help show this help message and exit -b BUILDER builder to use, default `ablog_builder` or dirhtml -d DOCTREES path for the cached environment and doctree files, default - `ablog_doctrees` or .doctrees + .doctrees when `ablog_doctrees` is not set in conf.py -s SOURCEDIR root path for source files, default is path to the folder that contains conf.py - -w WEBSITE path for website, default `ablog_website` or _website + -w WEBSITE path for website, default is _website when `ablog_website` is + not set in conf.py -T show full traceback on exception Serve and View @@ -96,7 +105,29 @@ server and open up browser tab to view your website. -h, --help show this help message and exit -n do not open website in a new browser tab -p PORT port number for HTTP server; default is 8000 - -w WEBSITE path for website, default `ablog_website` or _website + -w WEBSITE path for website, default is _website when `ablog_website` is + not set in conf.py + +.. _deploy: + +Deploy Website +-------------- + +Running ``ablog deploy`` will push your website to GitHub. + +:: + + $ ablog deploy -h + usage: ablog deploy [-h] [-g GITHUB_PAGES] [-w WEBSITE] + + Path options can be set in conf.py. Default values of paths are relative to + conf.py. + + optional arguments: + -h, --help show this help message and exit + -g GITHUB_PAGES GitHub user name for deploying to GitHub pages + -w WEBSITE path for website, default is _website when `ablog_website` + is not set in conf.py Create a Post ------------- @@ -113,4 +144,31 @@ Finally, ``ablog post`` will make a new post template file. optional arguments: -h, --help show this help message and exit - -t TITLE post title; default is `New Post \ No newline at end of file + -t TITLE post title; default is `New Post + +Clean Files +----------- + +In case you needed, running ``ablog clean`` will remove build files and +do a deep clean with ``-D`` option. + +:: + + $ ablog clean -h + usage: ablog clean [-h] [-d DOCTREES] [-w WEBSITE] [-D] + + Path options can be set in conf.py. Default values of paths are relative to + conf.py. + + optional arguments: + -h, --help show this help message and exit + -d DOCTREES path for the cached environment and doctree files, default + .doctrees when `ablog_doctrees` is not set in conf.py + -w WEBSITE path for website, default is _website when `ablog_website` is + not set in conf.py + -D deep clean, remove cached environment and doctree files + + +.. update:: Apr 7, 2015 + + Added ``ablog clean`` and ``ablog deploy`` commands. \ No newline at end of file diff --git a/docs/manual/ablog-configuration-options.rst b/docs/manual/ablog-configuration-options.rst index 747bf55..f5ab1b0 100644 --- a/docs/manual/ablog-configuration-options.rst +++ b/docs/manual/ablog-configuration-options.rst @@ -91,7 +91,7 @@ Authors, languages, & locations .. update:: Sep 15, 2014 Added :confval:`blog_languages` and :confval:`blog_default_language` - confivuration variables. + configuration variables. Post related ------------ @@ -245,3 +245,31 @@ you see on the left are listed below in the same order: link to a archive pages generated for each tag, category, and year. In addition, there are ``authors.html``, ``languages.html``, and ``locations.html`` sidebars that link to author and location archive pages. + +Command Options +--------------- + +.. update:: Apr 7, 2015 + + Added :ref:`commands` options. + +.. confval:: ablog_website + + Directory name for build output files. Default is ``_website``. + +.. confval:: ablog_doctrees + + Directory name for build cache files. Default is ``.doctrees``. + +.. confval:: ablog_builder + + HTML builder, default is ``dirhtml``. Build HTML pages, but with + a single directory per document. Makes for prettier URLs (no .html) + if served from a webserver. Alternative is ``html`` to build + one HTML file per document. + +.. confval:: github_pages + + GitHub user name used by ``ablog deploy`` command. See :ref:`deploy` + and :ref:`deploy-to-github-pages` for more information. + diff --git a/docs/manual/deploy-to-github-pages.rst b/docs/manual/deploy-to-github-pages.rst new file mode 100644 index 0000000..e5d109b --- /dev/null +++ b/docs/manual/deploy-to-github-pages.rst @@ -0,0 +1,37 @@ + +Deploy to GitHub Pages +====================== + + +.. post:: Apr 07, 2015 + :tags: + :category: + +If you are looking for a place to publish your blog, `GitHub Pages`_ might +be the place for you. + +.. _GitHub Pages: https://pages.github.com/ + +Assuming that you have a GitHub account, here are what you need to do +to get published: + +1. Head over to GitHub_ and create a new repository named ``username.github.io``, where + username is your username (or organization name) on GitHub. + +2. (optional) If you followed the link, you might as well give a star to ABlog ;) + +3. Set :confval:`github_pages` configuration variable in :file:`conf.py` file. + +4. Run ``ablog build`` in your project folder. + +5. Run ``ablog deploy``. This command will + + i. clone your GitHub pages repository to project folder, + + ii. copy all files from build folder (:file:`_website`) to :file:`username.github.io`, + + iii. add copied files and commit, + + iv. and finally push the changes to publish. + +Let us know how this works for you! \ No newline at end of file diff --git a/docs/release/ablog-v0.6-released.rst b/docs/release/ablog-v0.6-released.rst new file mode 100644 index 0000000..66210cb --- /dev/null +++ b/docs/release/ablog-v0.6-released.rst @@ -0,0 +1,12 @@ +ABlog v0.6 released +=================== + +.. post:: Apr 8, 2015 + :author: Ahmet + :category: Release + :location: SF + + +ABlog v0.6 is released with new :ref:`ablog-commands`. You can +``ablog deploy`` use to :ref:`deploy-to-github-pages`, and also ``ablog clean`` +to do spring cleaning every once in a while. diff --git a/setup.py b/setup.py index 1d5ded7..2922634 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ setup( 'Programming Language :: Python :: 3', ], provides=['ablog ({0:s})'.format(__version__)], - install_requires=['Werkzeug', 'Sphinx', 'alabaster'], + install_requires=['Werkzeug', 'Sphinx', 'alabaster', 'invoke'], message_extractors={ 'ablog': [ ('**.html', 'jinja2', None),