From c3f271481aa7740e54342b663dce0e1a1306f5f0 Mon Sep 17 00:00:00 2001 From: Anish Athalye Date: Thu, 24 May 2018 12:00:13 -0400 Subject: [PATCH 01/10] Fix handling of base directory Prior to this patch, Dotbot was relying on running with the base directory being the current working directory. In practice, it was relying on the install shim to set up this context. It makes more sense sense to actually execute `chdir()` within Dotbot itself, rather than relying on the install shim to do so. --- dotbot/cli.py | 9 +++++---- test/test-lib.bash | 10 ++++------ test/tests/plugin-dir.bash | 2 +- test/tests/plugin.bash | 2 +- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/dotbot/cli.py b/dotbot/cli.py index d77ab42..8febb26 100644 --- a/dotbot/cli.py +++ b/dotbot/cli.py @@ -14,10 +14,10 @@ def add_options(parser): help='suppress most output') parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', help='enable verbose output') - parser.add_argument('-d', '--base-directory', nargs=1, + parser.add_argument('-d', '--base-directory', dest='base_directory', help='execute commands from within BASEDIR', metavar='BASEDIR', required=True) - parser.add_argument('-c', '--config-file', nargs=1, dest='config_file', + parser.add_argument('-c', '--config-file', dest='config_file', help='run commands given in CONFIGFILE', metavar='CONFIGFILE', required=True) parser.add_argument('-p', '--plugin', action='append', dest='plugins', default=[], @@ -55,10 +55,11 @@ def main(): for plugin_path in plugin_paths: abspath = os.path.abspath(plugin_path) module.load(abspath) - tasks = read_config(options.config_file[0]) + tasks = read_config(options.config_file) if not isinstance(tasks, list): raise ReadingError('Configuration file must be a list of tasks') - dispatcher = Dispatcher(options.base_directory[0]) + os.chdir(options.base_directory) + dispatcher = Dispatcher(options.base_directory) success = dispatcher.dispatch(tasks) if success: log.info('\n==> All tasks executed successfully') diff --git a/test/test-lib.bash b/test/test-lib.bash index 008c2f8..3a24510 100644 --- a/test/test-lib.bash +++ b/test/test-lib.bash @@ -51,17 +51,15 @@ initialize() { run_dotbot() { ( - cd "${DOTFILES}" - cat > "${INSTALL_CONF}" - ${DOTBOT_EXEC} -d . -c "${INSTALL_CONF}" "${@}" + cat > "${DOTFILES}/${INSTALL_CONF}" + ${DOTBOT_EXEC} -d "${DOTFILES}" -c "${DOTFILES}/${INSTALL_CONF}" "${@}" ) } run_dotbot_json() { ( - cd "${DOTFILES}" - cat > "${INSTALL_CONF_JSON}" - ${DOTBOT_EXEC} -d . -c "${INSTALL_CONF_JSON}" "${@}" + cat > "${DOTFILES}/${INSTALL_CONF_JSON}" + ${DOTBOT_EXEC} -d "${DOTFILES}" -c "${DOTFILES}/${INSTALL_CONF_JSON}" "${@}" ) } diff --git a/test/tests/plugin-dir.bash b/test/tests/plugin-dir.bash index 299f144..f3a5e94 100644 --- a/test/tests/plugin-dir.bash +++ b/test/tests/plugin-dir.bash @@ -19,7 +19,7 @@ EOF ' test_expect_success 'run' ' -run_dotbot --plugin-dir plugins < Date: Thu, 24 May 2018 10:30:24 -0400 Subject: [PATCH 02/10] Point PyYAML dependency to official repository Previously, PyYAML was hosted on BitBucket, so we had a mirror of the repo on GitHub. Now, official hosting has moved to GitHub, so we can point to the official repository instead. Thanks to Marco A. Feliu for pointing this out. This patch also updates the install shim to update submodule URLs. To preserve the functionality of earlier Dotbot versions, we will need to maintain 'https://github.com/anishathalye/pyyaml'. Because old versions of the install shim used with new Dotbot versions will not update submodule URLs, we will need to keep the old repository in sync with the upstream repository as we upgrade PyYAML versions. This patch also upgrades the dependency to PyYAML 3.12. --- .gitmodules | 2 +- lib/pyyaml | 2 +- test/Vagrantfile | 3 +-- test/test-lib.bash | 6 +----- test/test_travis | 2 +- test/tests/shim.bash | 29 +++++++++++++++++++++++++++++ tools/git-submodule/install | 1 + 7 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 test/tests/shim.bash diff --git a/.gitmodules b/.gitmodules index 111c39c..ffb9af9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "lib/pyyaml"] path = lib/pyyaml - url = https://github.com/anishathalye/pyyaml + url = https://github.com/yaml/pyyaml ignore = dirty diff --git a/lib/pyyaml b/lib/pyyaml index f30c956..7e026bf 160000 --- a/lib/pyyaml +++ b/lib/pyyaml @@ -1 +1 @@ -Subproject commit f30c956c11aa6b5e7827fe5840cc9ed40b938d17 +Subproject commit 7e026bfee9cc0bddeb1bbca0c4a0bcd826c2bfdf diff --git a/test/Vagrantfile b/test/Vagrantfile index 8ce1739..05d6747 100644 --- a/test/Vagrantfile +++ b/test/Vagrantfile @@ -2,8 +2,7 @@ Vagrant.configure(2) do |config| config.vm.box = 'debian/stretch64' # sync by copying for isolation - config.vm.synced_folder "..", "/dotbot", type: "rsync", - rsync__exclude: ".git/" + config.vm.synced_folder "..", "/dotbot", type: "rsync" # disable default synced folder config.vm.synced_folder ".", "/vagrant", disabled: true diff --git a/test/test-lib.bash b/test/test-lib.bash index 008c2f8..b334b81 100644 --- a/test/test-lib.bash +++ b/test/test-lib.bash @@ -1,10 +1,6 @@ DEBUG=${DEBUG:-false} USE_VAGRANT=${USE_VAGRANT:-true} -if ${USE_VAGRANT}; then - DOTBOT_EXEC=${DOTBOT_EXEC:-"python /dotbot/bin/dotbot"} -else - DOTBOT_EXEC=${DOTBOT_EXEC:-"/dotbot/bin/dotbot"} -fi +DOTBOT_EXEC=${DOTBOT_EXEC:-"python /dotbot/bin/dotbot"} DOTFILES="/home/$(whoami)/dotfiles" INSTALL_CONF='install.conf.yaml' INSTALL_CONF_JSON='install.conf.json' diff --git a/test/test_travis b/test/test_travis index 20ec1ae..79439e1 100755 --- a/test/test_travis +++ b/test/test_travis @@ -6,7 +6,7 @@ set -e # set -x # set -v -BASEDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +export BASEDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" # Prevent execution outside of Travis CI builds if [[ "${TRAVIS}" != true || "${CI}" != true ]]; then diff --git a/test/tests/shim.bash b/test/tests/shim.bash new file mode 100644 index 0000000..2ed7d54 --- /dev/null +++ b/test/tests/shim.bash @@ -0,0 +1,29 @@ +test_description='install shim works' +. '../test-lib.bash' + +test_expect_success 'setup' ' +cd ${DOTFILES} +git init +if ${USE_VAGRANT}; then + git submodule add /dotbot dotbot +else + git submodule add ${BASEDIR} dotbot +fi +cp ./dotbot/tools/git-submodule/install . +echo "pear" > ${DOTFILES}/foo +' + +test_expect_success 'run' ' +cat > ${DOTFILES}/install.conf.yaml < Date: Wed, 30 May 2018 09:37:36 -0400 Subject: [PATCH 03/10] Update dates --- LICENSE.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index 432fcc0..1af799f 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,7 +1,7 @@ The MIT License (MIT) ===================== -**Copyright (c) 2014-2017 Anish Athalye (me@anishathalye.com)** +**Copyright (c) 2014-2018 Anish Athalye (me@anishathalye.com)** Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/README.md b/README.md index 06d073b..f73d473 100644 --- a/README.md +++ b/README.md @@ -369,7 +369,7 @@ Do you have a feature request, bug report, or patch? Great! See License ------- -Copyright (c) 2014-2017 Anish Athalye. Released under the MIT License. See +Copyright (c) 2014-2018 Anish Athalye. Released under the MIT License. See [LICENSE.md][license] for details. [init-dotfiles]: https://github.com/Vaelatern/init-dotfiles From 30caaf27803a9529e1809504a39c514c80b859c2 Mon Sep 17 00:00:00 2001 From: Anish Athalye Date: Wed, 30 May 2018 09:41:55 -0400 Subject: [PATCH 04/10] Add PyPI package This patch also makes the '-d' argument optional, with the base directory defaulting to the directory of the configuration file. --- .gitignore | 3 ++ README.md | 21 ++++++++++++ dotbot/__init__.py | 2 ++ dotbot/cli.py | 37 ++++++++++++++------- setup.cfg | 2 ++ setup.py | 82 ++++++++++++++++++++++++++++++++++++++++++++++ test/test-lib.bash | 4 +-- 7 files changed, 138 insertions(+), 13 deletions(-) create mode 100644 setup.cfg create mode 100644 setup.py diff --git a/.gitignore b/.gitignore index 0d20b64..bf45d22 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ +*.egg-info *.pyc +build/ +dist/ diff --git a/README.md b/README.md index f73d473..1e5a5fe 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,17 @@ submodule; be sure to commit your changes before running `./install`, otherwise the old version of Dotbot will be checked out by the install script. If using a subrepo, run `git fetch && git checkout origin/master` in the Dotbot directory. +If you prefer, you can install Dotbot from [PyPI] and call it as a command-line +program: + +```bash +pip install dotbot +touch install.conf.yaml +``` + +In this case, rather than running `./install`, you can invoke Dotbot with +`dotbot -c `. + ### Full Example Here's an example of a complete configuration. @@ -366,12 +377,22 @@ Contributing Do you have a feature request, bug report, or patch? Great! See [CONTRIBUTING.md][contributing] for information on what you can do about that. +Packaging +--------- + +1. Update version information. + +2. Build the package using ``python setup.py sdist bdist_wheel``. + +3. Sign and upload the package using ``twine upload -s dist/*``. + License ------- Copyright (c) 2014-2018 Anish Athalye. Released under the MIT License. See [LICENSE.md][license] for details. +[PyPI]: https://pypi.org/project/dotbot/ [init-dotfiles]: https://github.com/Vaelatern/init-dotfiles [dotfiles-template]: https://github.com/anishathalye/dotfiles_template [inspiration]: https://github.com/anishathalye/dotbot/wiki/Users diff --git a/dotbot/__init__.py b/dotbot/__init__.py index 1d03464..20ec150 100644 --- a/dotbot/__init__.py +++ b/dotbot/__init__.py @@ -1,2 +1,4 @@ from .cli import main from .plugin import Plugin + +__version__ = '1.12.1' diff --git a/dotbot/cli.py b/dotbot/cli.py index 8febb26..0674cbe 100644 --- a/dotbot/cli.py +++ b/dotbot/cli.py @@ -7,25 +7,29 @@ from .messenger import Messenger from .messenger import Level from .util import module +import dotbot +import yaml + def add_options(parser): - parser.add_argument('-Q', '--super-quiet', dest='super_quiet', action='store_true', + parser.add_argument('-Q', '--super-quiet', action='store_true', help='suppress almost all output') - parser.add_argument('-q', '--quiet', dest='quiet', action='store_true', + parser.add_argument('-q', '--quiet', action='store_true', help='suppress most output') - parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', + parser.add_argument('-v', '--verbose', action='store_true', help='enable verbose output') parser.add_argument('-d', '--base-directory', - dest='base_directory', help='execute commands from within BASEDIR', - metavar='BASEDIR', required=True) - parser.add_argument('-c', '--config-file', dest='config_file', - help='run commands given in CONFIGFILE', metavar='CONFIGFILE', - required=True) + help='execute commands from within BASEDIR', + metavar='BASEDIR') + parser.add_argument('-c', '--config-file', + help='run commands given in CONFIGFILE', metavar='CONFIGFILE') parser.add_argument('-p', '--plugin', action='append', dest='plugins', default=[], help='load PLUGIN as a plugin', metavar='PLUGIN') - parser.add_argument('--disable-built-in-plugins', dest='disable_built_in_plugins', + parser.add_argument('--disable-built-in-plugins', action='store_true', help='disable built-in plugins') parser.add_argument('--plugin-dir', action='append', dest='plugin_dirs', default=[], metavar='PLUGIN_DIR', help='load all plugins in PLUGIN_DIR') + parser.add_argument('--version', action='store_true', + help='show program\'s version number and exit') def read_config(config_file): reader = ConfigReader(config_file) @@ -37,6 +41,9 @@ def main(): parser = ArgumentParser() add_options(parser) options = parser.parse_args() + if options.version: + print('Dotbot version %s (yaml: %s)' % (dotbot.__version__, yaml.__version__)) + exit(0) if options.super_quiet: log.set_level(Level.WARNING) if options.quiet: @@ -55,11 +62,19 @@ def main(): for plugin_path in plugin_paths: abspath = os.path.abspath(plugin_path) module.load(abspath) + if not options.config_file: + log.error('No configuration file specified') + exit(1) tasks = read_config(options.config_file) if not isinstance(tasks, list): raise ReadingError('Configuration file must be a list of tasks') - os.chdir(options.base_directory) - dispatcher = Dispatcher(options.base_directory) + if options.base_directory: + base_directory = options.base_directory + else: + # default to directory of config file + base_directory = os.path.dirname(os.path.realpath(options.config_file)) + os.chdir(base_directory) + dispatcher = Dispatcher(base_directory) success = dispatcher.dispatch(tasks) if success: log.info('\n==> All tasks executed successfully') diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..3c6e79c --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal=1 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..93ba954 --- /dev/null +++ b/setup.py @@ -0,0 +1,82 @@ +from setuptools import setup +from codecs import open # For a consistent encoding +from os import path +import re + + +here = path.dirname(__file__) + + +with open(path.join(here, 'README.md'), encoding='utf-8') as f: + long_description = f.read() + + +def read(*names, **kwargs): + with open( + path.join(here, *names), + encoding=kwargs.get("encoding", "utf8") + ) as fp: + return fp.read() + + +def find_version(*file_paths): + version_file = read(*file_paths) + version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", + version_file, re.M) + if version_match: + return version_match.group(1) + raise RuntimeError("Unable to find version string.") + + +setup( + name='dotbot', + + version=find_version('dotbot', '__init__.py'), + + description='A tool that bootstraps your dotfiles', + long_description=long_description, + long_description_content_type='text/markdown', + + url='https://github.com/anishathalye/dotbot', + + author='Anish Athalye', + author_email='me@anishathalye.com', + + license='MIT', + + classifiers=[ + 'Development Status :: 5 - Production/Stable', + + 'Intended Audience :: Developers', + + 'License :: OSI Approved :: MIT License', + + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.2', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + + 'Topic :: Utilities', + ], + + keywords='dotfiles', + + packages=['dotbot'], + + install_requires=[ + 'PyYAML>=3.12,<4', + ], + + # To provide executable scripts, use entry points in preference to the + # "scripts" keyword. Entry points provide cross-platform support and allow + # pip to create the appropriate form of executable for the target platform. + entry_points={ + 'console_scripts': [ + 'dotbot=dotbot:main', + ], + }, +) diff --git a/test/test-lib.bash b/test/test-lib.bash index 7b5e9ca..e4d9a4e 100644 --- a/test/test-lib.bash +++ b/test/test-lib.bash @@ -48,14 +48,14 @@ initialize() { run_dotbot() { ( cat > "${DOTFILES}/${INSTALL_CONF}" - ${DOTBOT_EXEC} -d "${DOTFILES}" -c "${DOTFILES}/${INSTALL_CONF}" "${@}" + ${DOTBOT_EXEC} -c "${DOTFILES}/${INSTALL_CONF}" "${@}" ) } run_dotbot_json() { ( cat > "${DOTFILES}/${INSTALL_CONF_JSON}" - ${DOTBOT_EXEC} -d "${DOTFILES}" -c "${DOTFILES}/${INSTALL_CONF_JSON}" "${@}" + ${DOTBOT_EXEC} -c "${DOTFILES}/${INSTALL_CONF_JSON}" "${@}" ) } From 1dc80c77a74b356a0d90a1c584b0f9a8301024a9 Mon Sep 17 00:00:00 2001 From: Anish Athalye Date: Fri, 1 Jun 2018 07:57:23 -0400 Subject: [PATCH 05/10] Add setup_requires Both setuptools and wheel need to be at some minimum version to be able to upload Markdown READMEs. --- setup.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/setup.py b/setup.py index 93ba954..350bc5b 100644 --- a/setup.py +++ b/setup.py @@ -67,6 +67,11 @@ setup( packages=['dotbot'], + setup_requires=[ + 'setuptools>=38.6.0', + 'wheel>=0.31.0', + ], + install_requires=[ 'PyYAML>=3.12,<4', ], From 49b29da8e7affbaecbf58047d84342d79470736f Mon Sep 17 00:00:00 2001 From: Anish Athalye Date: Fri, 1 Jun 2018 08:00:50 -0400 Subject: [PATCH 06/10] Release 1.12.2 --- dotbot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotbot/__init__.py b/dotbot/__init__.py index 20ec150..2301447 100644 --- a/dotbot/__init__.py +++ b/dotbot/__init__.py @@ -1,4 +1,4 @@ from .cli import main from .plugin import Plugin -__version__ = '1.12.1' +__version__ = '1.12.2' From 7b23188602ea84abb1b5b555cb9d0950083c56d9 Mon Sep 17 00:00:00 2001 From: Anish Athalye Date: Fri, 1 Jun 2018 23:08:12 -0400 Subject: [PATCH 07/10] Fix missing packages in PyPI distribution --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 350bc5b..975deb3 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -from setuptools import setup +from setuptools import setup, find_packages from codecs import open # For a consistent encoding from os import path import re @@ -65,7 +65,7 @@ setup( keywords='dotfiles', - packages=['dotbot'], + packages=find_packages(), setup_requires=[ 'setuptools>=38.6.0', From a22838db76afe6abe5e7fe616b8e97571c58aa32 Mon Sep 17 00:00:00 2001 From: Anish Athalye Date: Sun, 3 Jun 2018 15:46:52 -0400 Subject: [PATCH 08/10] Release 1.12.3 --- dotbot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotbot/__init__.py b/dotbot/__init__.py index 2301447..dbb3f38 100644 --- a/dotbot/__init__.py +++ b/dotbot/__init__.py @@ -1,4 +1,4 @@ from .cli import main from .plugin import Plugin -__version__ = '1.12.2' +__version__ = '1.12.3' From e72e3b47a0f9b23d966961776a1575c9354a491f Mon Sep 17 00:00:00 2001 From: Anish Athalye Date: Tue, 5 Jun 2018 11:42:11 -0400 Subject: [PATCH 09/10] Make install script less noisy Before this patch, the `git submodule sync` line would always print to stdout. --- tools/git-submodule/install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/git-submodule/install b/tools/git-submodule/install index f11b48b..5a7e72c 100755 --- a/tools/git-submodule/install +++ b/tools/git-submodule/install @@ -9,7 +9,7 @@ DOTBOT_BIN="bin/dotbot" BASEDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "${BASEDIR}" -git -C "${DOTBOT_DIR}" submodule sync --recursive +git -C "${DOTBOT_DIR}" submodule sync --quiet --recursive git submodule update --init --recursive "${DOTBOT_DIR}" "${BASEDIR}/${DOTBOT_DIR}/${DOTBOT_BIN}" -d "${BASEDIR}" -c "${CONFIG}" "${@}" From f7a8bf10ba1d4073f6501ea1518d0e94ded4ac0e Mon Sep 17 00:00:00 2001 From: Anish Athalye Date: Thu, 7 Jun 2018 13:04:19 -0400 Subject: [PATCH 10/10] Fix script It is only legal to `return` from a function or sourced script. The right builtin to use here is `exit`. --- bin/dotbot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/dotbot b/bin/dotbot index 78a0725..123fc93 100755 --- a/bin/dotbot +++ b/bin/dotbot @@ -11,7 +11,7 @@ which python3 >/dev/null 2>&1 && exec python3 "$0" "$@" which python >/dev/null 2>&1 && exec python "$0" "$@" which python2 >/dev/null 2>&1 && exec python2 "$0" "$@" >&2 echo "error: cannot find python" -return 1 +exit 1 ''' # python code