summaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
authorOlly Cope <olly@ollycope.com>2018-06-11 14:34:30 +0000
committerOlly Cope <olly@ollycope.com>2018-06-11 14:34:30 +0000
commit460725565c274223edcd6b6eb9020513361aa9a8 (patch)
treeaf3ae0862682c23c32abdcf6bb9885becfe7f782 /doc
parent343928b0df6d354c2d69b71f8105268998d27fd7 (diff)
downloadyoyo-460725565c274223edcd6b6eb9020513361aa9a8.tar.gz
Move documentation into sphinx
Diffstat (limited to 'doc')
-rw-r--r--doc/Makefile20
-rw-r--r--doc/conf.py180
-rw-r--r--doc/index.rst309
3 files changed, 509 insertions, 0 deletions
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 0000000..cdf7c80
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = ../.tox/py36/bin/sphinx-build
+SPHINXPROJ = yoyo-migrations
+SOURCEDIR = .
+BUILDDIR = _build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/doc/conf.py b/doc/conf.py
new file mode 100644
index 0000000..f3f8dc0
--- /dev/null
+++ b/doc/conf.py
@@ -0,0 +1,180 @@
+# -*- coding: utf-8 -*-
+#
+# Configuration file for the Sphinx documentation builder.
+#
+# This file does only contain a selection of the most common options. For a
+# full list see the documentation:
+# http://www.sphinx-doc.org/en/master/config
+
+import re
+import yoyo
+import fresco_sphinx_theme
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+
+# -- Project information -----------------------------------------------------
+
+project = 'yoyo-migrations'
+copyright = '2018, Oliver Cope'
+author = 'Oliver Cope'
+
+# The full version, including alpha/beta/rc tags
+release = yoyo.__version__
+
+# The short X.Y version
+version = re.sub(r'([\d\.]+)', r'\1', release)
+
+
+# -- General configuration ---------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.doctest',
+ 'sphinx.ext.intersphinx',
+ 'sphinx.ext.viewcode',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+template_bridge = 'fresco_sphinx_theme.TemplateBridge'
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path .
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'fresco_sphinx_theme'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#
+html_theme_options = {
+ 'repo_url': 'https://bitbucket.org/ollyc/yoyo/',
+ 'issues_url': 'https://bitbucket.org/ollyc/yoyo/issues/',
+}
+
+# Add any paths that contain custom themes here, relative to this directory.
+html_theme_path = fresco_sphinx_theme.get_theme_paths()
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Custom sidebar templates, must be a dictionary that maps document names
+# to template names.
+#
+# The default sidebars (for documents that don't match any pattern) are
+# defined by theme itself. Builtin themes are using these templates by
+# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
+# 'searchbox.html']``.
+#
+# html_sidebars = {}
+html_show_sphinx = True
+html_show_sourcelink = False
+
+# -- Options for HTMLHelp output ---------------------------------------------
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'yoyo-migrationsdoc'
+
+
+# -- Options for LaTeX output ------------------------------------------------
+
+latex_elements = {
+ # The paper size ('letterpaper' or 'a4paper').
+ #
+ # 'papersize': 'letterpaper',
+
+ # The font size ('10pt', '11pt' or '12pt').
+ #
+ # 'pointsize': '10pt',
+
+ # Additional stuff for the LaTeX preamble.
+ #
+ # 'preamble': '',
+
+ # Latex figure (float) alignment
+ #
+ # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ (master_doc, 'yoyo-migrations.tex', 'yoyo-migrations Documentation',
+ 'Oliver Cope', 'manual'),
+]
+
+
+# -- Options for manual page output ------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ (master_doc, 'yoyo-migrations', 'yoyo-migrations Documentation',
+ [author], 1)
+]
+
+
+# -- Options for Texinfo output ----------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ (master_doc, 'yoyo-migrations', 'yoyo-migrations Documentation',
+ author, 'yoyo-migrations', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+
+# -- Extension configuration -------------------------------------------------
+
+# -- Options for intersphinx extension ---------------------------------------
+
+# Example configuration for intersphinx: refer to the Python standard library.
+intersphinx_mapping = {'https://docs.python.org/': None}
diff --git a/doc/index.rst b/doc/index.rst
new file mode 100644
index 0000000..ea4ccf8
--- /dev/null
+++ b/doc/index.rst
@@ -0,0 +1,309 @@
+Yoyo database migrations
+########################
+
+Yoyo is a Python database schema migration tool. You write migrations as Python
+scripts containing raw SQL statements or Python functions.
+They can be as simple as this:
+
+.. code:: python
+
+ # file: migrations/0001.create-foo.py
+ from yoyo import step
+ steps = [
+ step("CREATE TABLE foo (id INT, bar VARCHAR(20), PRIMARY KEY (id))",
+ "DROP TABLE foo"),
+ ]
+
+Command line usage
+==================
+
+Start a new migration::
+
+ yoyo new ./migrations -m "Add column to foo"
+
+Apply migrations from directory ``migrations`` to a PostgreSQL database::
+
+ yoyo apply --database postgresql://scott:tiger@localhost/db ./migrations
+
+Rollback migrations previously applied to a MySQL database::
+
+ yoyo rollback --database mysql://scott:tiger@localhost/database ./migrations
+
+Reapply (ie rollback then apply again) migrations to a SQLite database at
+location ``/home/sheila/important.db``::
+
+ yoyo reapply --database sqlite:////home/sheila/important.db ./migrations
+
+By default, yoyo-migrations starts in an interactive mode, prompting you for
+each migration file before applying it, making it easy to preview which
+migrations to apply and rollback.
+
+Connections
+-----------
+
+Database connections are specified using a URL. Examples::
+
+ # SQLite: use 4 slashes for an absolute database path on unix like platforms
+ database = sqlite:////home/user/mydb.sqlite
+
+ # SQLite: use 3 slashes for a relative path
+ database = sqlite:///mydb.sqlite
+
+ # SQLite: absolute path on Windows.
+ database = sqlite:///c:\home\user\mydb.sqlite
+
+ # MySQL: Network database connection
+ database = mysql://scott:tiger@localhost/mydatabase
+
+ # MySQL: unix socket connection
+ database = mysql://scott:tiger@/mydatabase?unix_socket=/tmp/mysql.sock
+
+ # MySQL with the MySQLdb driver (instead of pymysql)
+ database = mysql+mysqldb://scott:tiger@localhost/mydatabase
+
+ # PostgreSQL: database connection
+ database = postgresql://scott:tiger@localhost/mydatabase
+
+ # PostgreSQL: unix socket connection
+ database = postgresql://scott:tiger@/mydatabase
+
+ # PostgreSQL: changing the schema (via set search_path)
+ database = postgresql://scott:tiger@/mydatabase?schema=some_schema
+
+Password security
+-----------------
+
+You can specify your database username and password either as part of the
+database connection string on the command line (exposing your database
+password in the process list)
+or in a configuration file where other users may be able to read it.
+
+The ``-p`` or ``--prompt-password`` flag causes yoyo to prompt
+for a password, helping prevent your credentials from being leaked.
+
+Migration files
+===============
+
+The migrations directory contains a series of migration scripts. Each
+migration script is a python file (``.py``) containing a series of steps. Each
+step should comprise a migration query and (optionally) a rollback query:
+
+.. code:: python
+
+ #
+ # file: migrations/0001.create-foo.py
+ #
+ from yoyo import step
+ step(
+ "CREATE TABLE foo (id INT, bar VARCHAR(20), PRIMARY KEY (id))",
+ "DROP TABLE foo",
+ )
+
+Migrations may also declare dependencies on earlier migrations via the
+``__depends__`` attribute::
+
+ #
+ # file: migrations/0002.modify-foo.py
+ #
+ __depends__ = {'0001.create-foo'}
+
+ step(
+ "ALTER TABLE foo ADD baz INT",
+ "ALTER TABLE foo DROP baz",
+ )
+
+
+The filename of each file (without the .py extension) is used as migration's
+identifier. In the absence of a ``__depends__`` attribute, migrations
+are applied in filename order, so it's useful to name your files using a date
+(eg '20090115-xyz.py') or some other incrementing number.
+
+yoyo creates a table in your target database, ``_yoyo_migration``, to
+track which migrations have been applied.
+
+Steps may also take an optional argument ``ignore_errors``, which must be one
+of ``apply``, ``rollback``, or ``all``. If in the previous example the table
+foo might have already been created by another means, we could add
+``ignore_errors='apply'`` to the step to allow the migrations to continue
+regardless::
+
+ #
+ # file: migrations/0001.create-foo.py
+ #
+ from yoyo import step
+ step(
+ "CREATE TABLE foo (id INT, bar VARCHAR(20), PRIMARY KEY (id))",
+ "DROP TABLE foo",
+ ignore_errors='apply',
+ )
+
+Steps can also be python functions taking a database connection as
+their only argument::
+
+ #
+ # file: migrations/0002.update-keys.py
+ #
+ from yoyo import step
+ def do_step(conn):
+ cursor = conn.cursor()
+ cursor.execute(
+ "INSERT INTO sysinfo "
+ " (osname, hostname, release, version, arch)"
+ " VALUES (%s, %s, %s, %s, %s %s)",
+ os.uname()
+ )
+
+ step(do_step)
+
+Post-apply hook
+---------------
+
+It can be useful to have a script that is run after every successful migration.
+For example you could use this to update database permissions or re-create
+views.
+To do this, create a special migration file called ``post-apply.py``.
+This file should have the same format as any other migration file.
+
+
+Configuration file
+==================
+
+Yoyo looks for a configuration file named ``yoyo.ini`` in the current working
+directory or any ancestor directory.
+
+If no configuration file is found ``yoyo`` will prompt you to
+create one, popuplated with the current command line args.
+
+Using a configuration file saves repeated typing,
+avoids your database username and password showing in process listings
+and lessens the risk of accidentally running migrations
+against the wrong database (ie by re-running an earlier ``yoyo`` entry in
+your command history when you have moved to a different directory).
+
+If you do not want a config file to be loaded
+add the ``--no-config`` parameter to the command line options.
+
+The configuration file may contain the following options::
+
+ [DEFAULT]
+
+ # List of migration source directories. "%(here)s" is expanded to the
+ # full path of the directory containing this ini file.
+ sources = %(here)s/migrations %(here)s/lib/module/migrations
+
+ # Target database
+ database = postgresql://scott:tiger@localhost/mydb
+
+ # Verbosity level. Goes from 0 (least verbose) to 3 (most verbose)
+ verbosity = 3
+
+ # Disable interactive features
+ batch_mode = on
+
+ # Editor to use when starting new migrations
+ # "{}" is expanded to the filename of the new migration
+ editor = /usr/local/bin/vim -f {}
+
+ # An arbitrary command to run after a migration has been created
+ # "{}" is expanded to the filename of the new migration
+ post_create_command = hg add {}
+
+ # A prefix to use for generated migration filenames
+ prefix = myproject_
+
+
+Config file inheritance may be used to customize configuration per site::
+
+ #
+ # file: yoyo-defaults.ini
+ #
+ [DEFAULT]
+ sources = %(here)s/migrations
+
+ #
+ # file: yoyo.ini
+ #
+ [DEFAULT]
+
+ ; Inherit settings from yoyo-defaults.ini
+ %inherit = %(here)s/yoyo-defaults.ini
+
+ ; Use '?' to avoid raising an error if the file does not exist
+ %inherit = ?%(here)s/yoyo-defaults.ini
+
+ database = sqlite:///%(here)s/mydb.sqlite
+
+Transactions
+============
+
+Each migration runs in a separate transaction. Savepoints are used
+to isolate steps within each migration.
+
+If an error occurs during a step and the step has ``ignore_errors`` set,
+then that individual step will be rolled back and
+execution will pick up from the next step.
+If ``ignore_errors`` is not set then the entire migration will be rolled back
+and execution stopped.
+
+Note that some databases (eg MySQL) do not support rollback on DDL statements
+(eg ``CREATE ...`` and ``ALTER ...`` statements). For these databases
+you may need to manually intervene to reset the database state
+should errors occur in your migration.
+
+Using ``group`` allows you to nest steps, giving you control of where
+rollbacks happen. For example::
+
+ group([
+ step("ALTER TABLE employees ADD tax_code TEXT"),
+ step("CREATE INDEX tax_code_idx ON employees (tax_code)")
+ ], ignore_errors='all')
+ step("UPDATE employees SET tax_code='C' WHERE pay_grade < 4")
+ step("UPDATE employees SET tax_code='B' WHERE pay_grade >= 6")
+ step("UPDATE employees SET tax_code='A' WHERE pay_grade >= 8")
+
+Disabling transactions
+----------------------
+
+In PostgreSQL it is an error to run certain statements inside a transaction
+block. These include:
+
+.. code::sql
+
+ CREATE TABLE <foo>
+ ALTER TYPE <enum> ADD ...
+
+Migrations containing such statements should set
+``__transactional__ = False``, eg:
+
+.. code::python
+
+ __transactional__ = False
+
+ step("CREATE DATABASE mydb", "DROP DATABASE mydb")
+
+Note that this feature is implemented for the PostgreSQL backend only.
+
+
+Using yoyo from python code
+===========================
+
+The following example shows how to apply migrations from inside python code::
+
+ from yoyo import read_migrations
+ from yoyo import get_backend
+
+ backend = get_backend('postgres://myuser@localhost/mydatabase')
+ migrations = read_migrations('path/to/migrations')
+ with backend.lock():
+ backend.apply_migrations(backend.to_apply(migrations))
+
+.. :vim:sw=4:et
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Contents:
+
+Changelog
+=========
+
+.. include:: ../CHANGELOG.rst