diff --git a/bob/extension/scripts/__init__.py b/bob/extension/scripts/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..60b3f6d43e89d1f4dfbe246105d1e47f1a23021c
--- /dev/null
+++ b/bob/extension/scripts/__init__.py
@@ -0,0 +1,4 @@
+from .new_version import main as new_version
+
+# gets sphinx autodoc done right - don't remove it
+__all__ = [_ for _ in dir() if not _.startswith('_')]
diff --git a/bob/extension/scripts/new_version.py b/bob/extension/scripts/new_version.py
new file mode 100755
index 0000000000000000000000000000000000000000..75546695dade4a3c99770e4586dcc9c50e63269c
--- /dev/null
+++ b/bob/extension/scripts/new_version.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+
+from __future__ import print_function
+import sys, os
+import subprocess
+
+import argparse
+from distutils.version import StrictVersion as Version
+
+def main(command_line_options = None):
+  """
+  This script will push a new ``'stable'`` version of the current package on GitHub and PyPI, and update the new version of the package to the given ``'latest'`` version.
+  It assumes that you are in the main directory of the package and have successfully ran bootstrap, and that you have submitted all changes that should go into the new version.
+  Preferably, the build on Travis passed.
+  For database packages, it also assumes that the ``.sql3`` files have been generated.
+  Also, it assumes that the ``'stable'`` version has not yet been uploaded to PyPI, and that no GitHub tag with this version exists.
+
+  The ``'stable'`` version (i.e., what will be downloadable from PyPI) can be current version of the package, but not lower than that.
+  The ``'latest'`` version (i.e., what will be the new master branch on GitHub) must be higher than the current and than the stable version.
+  By default, five steps are executed, in this order:
+
+  - ``tag``: If given, the --stable-version will be set and added to GitHub; and the version is tagged in GitHub and pushed.
+  - ``build``: The package will be (re-)built with bin/buildout using the provided build options.
+  - ``pypi``: The --stable-version (or the current version) will be registered and uploaded to PyPI
+  - ``docs``: The documentation will be generated and uploaded to PythonHosted
+  - ``latest``: The --latest-version will be set and committed to GitHub
+
+  If any of these commands fail, the remaining steps will be skipped, unless you specify the ``--keep-going`` option.
+
+  If you only want a subset of the steps to be executed, you can limit them using the ``--steps`` option.
+  A valid use case, e.g., is only to re-upload the documentation.
+  """
+
+  parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
+
+  parser.add_argument("--latest-version", '-l', required = True, help = "The latest version for the package")
+  parser.add_argument("--stable-version", '-s', help = "The stable version for the package; if not given, the current version is used")
+  parser.add_argument("--build-options", '-b', nargs='+', default = [], help = "Add options to build your package")
+  parser.add_argument("--steps", nargs = "+", choices = ['tag', 'build', 'pypi', 'docs', 'latest'], default = ['tag', 'build', 'pypi', 'docs', 'latest'], help = "Select the steps that you want to execute")
+  parser.add_argument("--dry-run", '-q', action = 'store_true', help = "Only print the actions, but do not execute them")
+  parser.add_argument("--keep-going", '-f', action = 'store_true', help = "Run all steps, even if some of them fail. HANDLE THIS FLAG WITH CARE!")
+  parser.add_argument("--verbose", '-v', action = 'store_true', help = "Print more information")
+
+  args = parser.parse_args(command_line_options)
+
+
+  # assert the the version file is there
+  version_file = 'version.txt'
+  if not os.path.exists(version_file):
+    raise ValueError("Could not find the file '%s' containing the version number. Are you inside the root directory of your package?")
+
+  def run_commands(version, *calls):
+    """Updates the version.txt to the given version and runs the given commands."""
+    if version is not None and (args.verbose or args.dry_run):
+      print (" - cat '%s' > %s" % (version, version_file))
+    if not args.dry_run and version is not None:
+      # update version to stable version, if not done yet
+      with open(version_file, 'w') as f:
+        f.write(version)
+
+    # get all calls
+    for call in calls:
+      if args.verbose or args.dry_run:
+        print (' - ' + ' '.join(call))
+      if not args.dry_run:
+        # execute call
+        if subprocess.call(call):
+          # call failed (has non-zero exit status)
+          print ("Command '%s' failed; stopping" % ' '.join(call))
+          if not args.keep_going:
+            raise ValueError("Command '%s' failed; stopping" % ' '.join(call))
+
+  # get current version
+  current_version = open(version_file).read().rstrip()
+
+  # check the versions
+  if args.stable_version is not None and Version(args.latest_version) <= Version(args.stable_version):
+    raise ValueError("The latest version '%s' must be greater than the stable version '%s'" % (args.latest_version, args.stable_version))
+  if Version(current_version) >= Version(args.latest_version):
+    raise ValueError("The latest version '%s' must be greater than the current version '%s'" % (args.latest_version, current_version))
+  if args.stable_version is not None and Version(current_version) > Version(args.stable_version):
+    raise ValueError("The stable version '%s' cannot be smaller than the current version '%s'" % (args.stable_version, current_version))
+
+
+  if 'tag' in args.steps:
+    if args.stable_version is not None and Version(args.stable_version) > Version(current_version):
+      # update stable version on github
+      run_commands(args.stable_version, ['git', 'add', 'version.txt'], ['git', 'commit', '-m', 'Increased version to %s [skip ci]' % args.stable_version])
+    else:
+      # assure that we have the current version
+      args.stable_version = current_version
+    # add a github tag
+    print ("\nTagging version '%s'" % args.stable_version)
+    run_commands(None, ['git', 'tag', 'v%s' % args.stable_version], ['git', 'push', '--tags'])
+
+
+  if 'build' in args.steps:
+    print ("\nBuilding the package")
+    run_commands(None, ['./bin/buildout'] + args.build_options)
+
+
+  if 'pypi' in args.steps:
+    print ("\nUploading version '%s' to PyPI" % args.stable_version)
+    # update version on github and add a tag
+    run_commands(None, ['./bin/python', 'setup.py', 'register'], ['./bin/python', 'setup.py', 'sdist', '--formats', 'zip', 'upload'])
+
+
+  if 'docs' in args.steps:
+    # Documentation can be uploaded, independent of the versions
+    print ("\nUploading documentation to PythonHosted.org")
+    run_commands(None, ["./bin/python", "setup.py", "build_sphinx", "--source-dir", "doc", "--build-dir", "build/doc", "--all-files"], ["./bin/python", "setup.py", "upload_docs", "--upload-dir", "build/doc/html"])
+
+
+  if 'latest' in args.steps:
+    # update GitHub version to latest version
+    print ("\nSetting latest version '%s'" % args.latest_version)
+    run_commands(args.latest_version, ['git', 'add', 'version.txt'], ['git', 'commit', '-m', 'Increased version to %s' % args.latest_version], ['git', 'push'])
+
+
+if __name__ == '__main__':
+  main()
+
diff --git a/bob/extension/test_scripts.py b/bob/extension/test_scripts.py
new file mode 100644
index 0000000000000000000000000000000000000000..a710a41d6a0fccf0ff194cc9bfd39e5b9dd68e6b
--- /dev/null
+++ b/bob/extension/test_scripts.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+# Andre Anjos <andre.anjos@idiap.ch>
+# Thu 20 Mar 2014 12:43:48 CET
+
+"""Tests for scripts
+"""
+
+import os
+import sys
+import nose.tools
+
+def test_new_version():
+  # Tests the bin/bob_new_version.py script
+
+  from bob.extension.scripts import new_version
+
+  # test the script using the dry-run option (to avoid to actually tag and push code)
+
+  # assert that it does not raise an exception, when both latest and stable version are specified
+  new_version(['--dry-run', '--stable-version', '20.7.0', '--latest-version', '20.8.0'])
+
+  # assert that it does not raise an exception, when only the latest version is specified
+  new_version(['--dry-run', '--latest-version', '20.8.0'])
+
+  # assert that it does raise an exception, when the latest version is too low
+  nose.tools.assert_raises(ValueError, new_version, ['--dry-run', '--latest-version', '0.8.0'])
+
+  # assert that it does raise an exception, when the stable version is too low
+  nose.tools.assert_raises(ValueError, new_version, ['--dry-run', '--stable-version', '0.8.0', '--latest-version', '0.9.0'])
+
+  # assert that it does raise an exception, when the stable version is higher than latest version
+  nose.tools.assert_raises(ValueError, new_version, ['--dry-run', '--stable-version', '20.8.0', '--latest-version', '20.8.0a1'])
+
diff --git a/doc/guide.rst b/doc/guide.rst
index fce96db1da2c934f8707a8298c3adfc52e97a561..7df091be61b954675ded4e1ae9e86db6c8acc811 100644
--- a/doc/guide.rst
+++ b/doc/guide.rst
@@ -324,7 +324,7 @@ This file will be used to generate the front page of your package on PyPI_ and w
   The ``README.rst`` of **this package** (``bob.extension``) is a good example, including all the badges that show the current status of the package and the link to relevant information.
 
 To ease up your life, we also provide a script to run all steps to publish your package.
-Please read the following paragraphs to understand the steps in the ``./scripts/new_version.py`` script that will be explained at the end of this section.
+Please read the following paragraphs to understand the steps in the ``./bin/bob_new_version.py`` script that will be explained at the end of this section.
 
 Version Numbering Scheme
 ========================
@@ -472,13 +472,13 @@ and, finally, to keep track of new changes:
 
 5. Switch to a new version number.
 
-All these steps are combined in the ``./scripts/new_version.py`` script.
+All these steps are combined in the ``./bin/bob_new_version.py`` script.
 This script needs to be run from within the root directory of your package.
 Please run:
 
 .. code-block:: sh
 
-  $ [PATH/TO/]bob.extension/script/new_version.py --help
+  $ ./bin/bob_new_version.py --help
 
 to see a list of options.
 
diff --git a/doc/py_api.rst b/doc/py_api.rst
index e4e924cb50df1d1305af207d9c0d268ada2418bc..258431af5ace26b03c527340470858e3f6dc7468 100644
--- a/doc/py_api.rst
+++ b/doc/py_api.rst
@@ -6,7 +6,14 @@
  Python API to `bob.extension`
 ================================
 
-This section includes information for using the Python API of
-``bob.extension``.
+This section includes information for using the Python API of ``bob.extension``.
+
+Functions
+---------
 
 .. automodule:: bob.extension
+
+Scripts
+-------
+
+.. automodule:: bob.extension.scripts
diff --git a/scripts/new_version.py b/scripts/new_version.py
deleted file mode 100755
index 9c40544a86f810216ab1015ee3f374777213daea..0000000000000000000000000000000000000000
--- a/scripts/new_version.py
+++ /dev/null
@@ -1,120 +0,0 @@
-#!/usr/bin/env python
-
-from __future__ import print_function
-import sys, os
-import subprocess
-
-import argparse
-from distutils.version import StrictVersion as Version
-
-doc = """
-This script will push a new 'stable' version of the current package on GitHub and PyPI, and update the new version of the package to the given 'latest' version.
-It assumes that you are in the main directory of the package and have successfully ran bootstrap, and that you have submitted all changes that should go into the new version.
-For database packages, it also assumes that the .sql3 files have been generated.
-Also, it assumes that the 'stable' version has not yet been uploaded to PyPI, and that no GitHub tag with this version exists.
-
-The 'stable' version (i.e., what will be downloadable from PyPI) can be current version of the package, but not lower than that.
-The 'latest' version (i.e., what will be the new master branch on GitHub) must be higher than the current and than the stable version.
-By default, five steps are executed, in this order:
-
-- tag: If given, the --stable-version will be set and added to GitHub; and the version is tagged in GitHub and pushed.
-- build: The package will be (re-)built with bin/buildout using the provided build options.
-- pypi: The --stable-version (or the current version) will be registered and uploaded to PyPI
-- docs: The documentation will be generated and uploaded to PythonHosted
-- latest: The --latest-version will be set and committed to GitHub
-
-If any of these commands fail, the remaining steps will be skipped, unless you specify the --keep-going option.
-
-If you only want a subset of the steps to be executed, you can limit them using the --steps option.
-A valid use case, e.g., is only to re-upload the documentation.
-"""
-
-parser = argparse.ArgumentParser(description=doc, formatter_class=argparse.RawDescriptionHelpFormatter)
-
-parser.add_argument("--latest-version", '-l', required = True, help = "The latest version for the package")
-parser.add_argument("--stable-version", '-s', help = "The stable version for the package; if not given, the current version is used")
-parser.add_argument("--build-options", '-b', nargs='+', default = [], help = "Add options to build your package")
-parser.add_argument("--steps", nargs = "+", choices = ['tag', 'build', 'pypi', 'docs', 'latest'], default = ['tag', 'build', 'pypi', 'docs', 'latest'], help = "Select the steps that you want to execute")
-parser.add_argument("--dry-run", '-q', action = 'store_true', help = "Only print the actions, but do not execute them")
-parser.add_argument("--keep-going", '-f', action = 'store_true', help = "Run all steps, even if some of them fail. HANDLE THIS FLAG WITH CARE!")
-parser.add_argument("--verbose", '-v', action = 'store_true', help = "Print more information")
-
-args = parser.parse_args()
-
-
-# assert the the version file is there
-version_file = 'version.txt'
-if not os.path.exists(version_file):
-  print("Could not find the file '%s' containing the version number. Are you inside the root directory of your package?")
-  sys.exit(-1)
-
-def run_commands(version, *calls):
-  """Updates the version.txt to the given version and runs the given commands."""
-  if version is not None and (args.verbose or args.dry_run):
-    print (" - cat '%s' > %s" % (version, version_file))
-  if not args.dry_run and version is not None:
-    # update version to stable version, if not done yet
-    with open(version_file, 'w') as f:
-      f.write(version)
-
-  # get all calls
-  for call in calls:
-    if args.verbose or args.dry_run:
-      print (' - ' + ' '.join(call))
-    if not args.dry_run:
-      # execute call
-      if subprocess.call(call):
-        # call failed (has non-zero exit status)
-        print ("Command '%s' failed; stopping" % ' '.join(call))
-        if not args.keep_going:
-          sys.exit(-1)
-
-# get current version
-current_version = open(version_file).read().rstrip()
-
-# check the versions
-if args.stable_version is not None and Version(args.latest_version) <= Version(args.stable_version):
-  print ("The latest version '%s' must be greater than the stable version '%s'" % (args.latest_version, args.stable_version))
-  sys.exit(-1)
-if Version(current_version) >= Version(args.latest_version):
-  print ("The latest version '%s' must be greater than the current version '%s'" % (args.latest_version, current_version))
-  sys.exit(-1)
-if args.stable_version is not None and Version(current_version) > Version(args.stable_version):
-  print ("The stable version '%s' cannot be smaller than the current version '%s'" % (args.stable_version, current_version))
-  sys.exit(-1)
-
-
-if 'tag' in args.steps:
-  if args.stable_version is not None and Version(args.stable_version) > Version(current_version):
-    # update stable version on github
-    run_commands(args.stable_version, ['git', 'add', 'version.txt'], ['git', 'commit', '-m', 'Increased version to %s [skip ci]' % args.stable_version])
-  else:
-    # assure that we have the current version
-    args.stable_version = current_version
-  # add a github tag
-  print ("\nTagging version '%s'" % args.stable_version)
-  run_commands(None, ['git', 'tag', 'v%s' % args.stable_version], ['git', 'push', '--tags'])
-
-
-if 'build' in args.steps:
-  print ("\nBuilding the package")
-  run_commands(None, ['./bin/buildout'] + args.build_options)
-
-
-if 'pypi' in args.steps:
-  print ("\nUploading version '%s' to PyPI" % args.stable_version)
-  # update version on github and add a tag
-  run_commands(None, ['./bin/python', 'setup.py', 'register'], ['./bin/python', 'setup.py', 'sdist', '--formats', 'zip', 'upload'])
-
-
-if 'docs' in args.steps:
-  # Documentation can be uploaded, independent of the versions
-  print ("\nUploading documentation to PythonHosted.org")
-  run_commands(None, ["./bin/python", "setup.py", "build_sphinx", "--source-dir", "doc", "--build-dir", "build/doc", "--all-files"], ["./bin/python", "setup.py", "upload_docs", "--upload-dir", "build/doc/html"])
-
-
-if 'latest' in args.steps:
-  # update GitHub version to latest version
-  print ("\nSetting latest version '%s'" % args.latest_version)
-  run_commands(args.latest_version, ['git', 'add', 'version.txt'], ['git', 'commit', '-m', 'Increased version to %s' % args.latest_version], ['git', 'push'])
-
diff --git a/setup.py b/setup.py
index 580ef281ea73d62c6a3a90e5070ce1e1bb8f20a1..6a2f8d5960b1e1a1e63e73a7ee1ca73e112de818 100644
--- a/setup.py
+++ b/setup.py
@@ -34,6 +34,12 @@ setup(
       'setuptools',
     ],
 
+    entry_points = {
+      'console_scripts': [
+        'bob_new_version.py = bob.extension.scripts:new_version',
+      ],
+    },
+
     classifiers = [
       'Framework :: Bob',
       'Development Status :: 4 - Beta',