Repository: pydanny/cookiecutter-djangopackage

Issue:
Use a version manager tool instead of get_version()
Our current approach to getting the version number involves a custom `get_version()` that reads `__init__py` and parses it with a regexp: https://github.com/pydanny/cookiecutter-djangopackage/blob/master/%7B%7Bcookiecutter.repo_name%7D%7D/setup.py#L20-L29

It's better than importing the package to get the version (which causes chicken-egg problems), but really not ideal.

There are tools that can handle this for us:
- https://github.com/lgiordani/punch
- https://github.com/peritus/bumpversion

TODOs:
- Choose one of the above tools (or maybe one that I don't know about)
- Set it up with the generated package
- Remove old `get_version()` code
- Document how to increment versions with the chosen tool in `docs/versioning.rst` or wherever you think makes the most sense



Discussion:
[This commit](https://github.com/pydanny/cookiecutter-djangopackage/commit/ee32c20072e71d57f1cb330ca7071eba7be71a62) added bumpversion to this package, but it's currently crashing* because it cannot find the version string in the `setup.py` file: it was removed a few days ago in 89157eef71a16b9d60959141de09ce59cdf90b3c

Either we remove `[bumpversion:file:setup.py]` from `setup.cfg` or we revert 89157eef71a16b9d60959141de09ce59cdf90b3c and document it properly.

(*) I just tried to run bumpversion on a recently generated package and I had: `AssertionError: Did not find '0.1.0' or '0.1.0' in file setup.py`.


I just ran into this and thought I did something wrong with the setup, I feel like it should be reverted until fixed because grabbing the latest from github includes this bug :(

also the bumpversion docs and stuff are a little confusing, I found just doing

`bumpversion patch` for 0.1.0 -> 0.1.1
`bumpversion minor` for 0.1.0 -> 0.2.0
`bumpversion major` for 0.1.0 -> 1.0.0

works because bumpversion automatically looks for a `.bumpversion.cfg` file and if not found falls back to `setup.cfg`, finds the settings and uses those.

the whole `get_version()` doesn't really need to be used with bumpversion because it automatically modifies the `setup.py`, `__init__.py`, and `setup.cfg` versioning along with providing the commit message for the version bump.

bumpversion also takes care of creating the git tag based on the new version so the documentation about creating the git tag isn't needed, but we could also include how you pass a message to the git tag with bumpversion if that is desired.

I am willing to update all these things in a PR if you want!

Resolving patch:
diff --git a/{{cookiecutter.repo_name}}/tasks.py b/{{cookiecutter.repo_name}}/tasks.py
new file mode 100644
--- /dev/null
+++ b/{{cookiecutter.repo_name}}/tasks.py
@@ -0,0 +1,109 @@
+import os
+import webbrowser
+
+from invoke import task
+
+
+def open_browser(path):
+    try:
+        from urllib import pathname2url
+    except:
+        from urllib.request import pathname2url
+    webbrowser.open("file://" + pathname2url(os.path.abspath(path)))
+
+
+@task
+def clean_build(c):
+    """
+    Remove build artifacts
+    """
+    c.run("rm -fr build/")
+    c.run("rm -fr dist/")
+    c.run("rm -fr *.egg-info")
+
+
+@task
+def clean_pyc(c):
+    """
+    Remove python file artifacts
+    """
+    c.run("find . -name '*.pyc' -exec rm -f {} +")
+    c.run("find . -name '*.pyo' -exec rm -f {} +")
+    c.run("find . -name '*~' -exec rm -f {} +")
+
+
+@task
+def coverage(c):
+    """
+    check code coverage quickly with the default Python
+    """
+    c.run("coverage run --source {{ cookiecutter.repo_name }} runtests.py tests")
+    c.run("coverage report -m")
+    c.run("coverage html")
+    c.run("open htmlcov/index.html")
+
+
+@task
+def docs(c):
+    """
+    Build the documentation and open it in the browser
+    """
+    c.run("rm -f docs/{{ cookiecutter.repo_name }}.rst")
+    c.run("rm -f docs/modules.rst")
+    c.run("sphinx-apidoc -o docs/ {{ cookiecutter.app_name }}")
+
+    c.run("sphinx-build -E -b html docs docs/_build")
+    open_browser(path='docs/_build/html/index.html')
+
+
+@task
+def test_all(c):
+    """
+    Run tests on every python version with tox
+    """
+    c.run("tox")
+
+
+@task
+def clean(c):
+    """
+    Remove python file and build artifacts
+    """
+    clean_build(c)
+    clean_pyc(c)
+
+
+@task
+def unittest(c):
+    """
+    Run unittests
+    """
+    c.run("python manage.py test")
+
+
+@task
+def lint(c):
+    """
+    Check style with flake8
+    """
+    c.run("flake8 {{ cookiecutter.repo_name }} tests")
+
+
+@task(help={'bumpsize': 'Bump either for a "feature" or "breaking" change'})
+def release(c, bumpsize=''):
+    """
+    Package and upload a release
+    """
+    clean(c)
+    if bumpsize:
+        bumpsize = '--' + bumpsize
+
+    c.run("bumpversion {bump} --no-input".format(bump=bumpsize))
+
+    import {{ cookiecutter.repo_name|replace('-', '_') }}
+    c.run("python setup.py sdist bdist_wheel")
+    c.run("twine upload dist/*")
+
+    c.run('git tag -a {version} -m "New version: {version}"'.format(version={{ cookiecutter.repo_name|replace('-', '_') }}.__version__))
+    c.run("git push --tags")
+    c.run("git push origin master")


Test patch: