Skip to content

Commit 442b848

Browse files
committed
Merge remote-tracking branch 'upstream/master' into add-plugin-config-loading
2 parents 6dd426d + da928a4 commit 442b848

File tree

101 files changed

+2716
-2401
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+2716
-2401
lines changed

.editorconfig

-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,3 @@ indent_size = 4
1515

1616
[*.yml]
1717
indent_size = 2
18-
19-
[*.md]
20-
trim_trailing_whitespace = false

.github/workflows/build.yml

+34-7
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,44 @@ on:
55
schedule:
66
- cron: '0 8 * * 6'
77
jobs:
8-
build:
9-
runs-on: ubuntu-latest
8+
test:
9+
env:
10+
PIP_DISABLE_PIP_VERSION_CHECK: 1
1011
strategy:
12+
fail-fast: false
1113
matrix:
12-
python: [2.7, pypy2, 3.5, 3.6, 3.7, 3.8, 3.9, pypy3]
13-
name: Python ${{ matrix.python }}
14+
os: ["ubuntu-20.04", "macos-latest"]
15+
python: ["2.7", "pypy-2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10", "pypy-3.9"]
16+
include:
17+
- os: "windows-latest"
18+
python: "3.8"
19+
- os: "windows-latest"
20+
python: "3.9"
21+
- os: "windows-latest"
22+
python: "3.10"
23+
runs-on: ${{ matrix.os }}
24+
name: "Test: Python ${{ matrix.python }} on ${{ matrix.os }}"
1425
steps:
15-
- uses: actions/checkout@v2
26+
- uses: actions/checkout@v3
1627
with:
1728
submodules: recursive
18-
- uses: actions/setup-python@v2
29+
- uses: actions/setup-python@v4
1930
with:
2031
python-version: ${{ matrix.python }}
21-
- run: ./test/test
32+
- name: "Install dependencies"
33+
run: |
34+
python -m pip install --upgrade pip setuptools
35+
python -m pip install tox tox-gh-actions
36+
- name: "Run tests"
37+
run: |
38+
python -m tox
39+
python -m tox -e coverage_report
40+
- uses: codecov/codecov-action@v3
41+
42+
fmt:
43+
name: Format
44+
runs-on: ubuntu-22.04
45+
steps:
46+
- uses: actions/checkout@v3
47+
- uses: psf/black@stable
48+
- uses: isort/isort-action@v1

.gitignore

+7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
*.egg-info
22
*.pyc
3+
.coverage*
4+
.eggs/
5+
.idea/
6+
.tox/
7+
.venv/
38
build/
9+
coverage.xml
410
dist/
11+
htmlcov/

README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ touch install.conf.yaml
6767
```
6868

6969
If you are using PowerShell instead of a POSIX shell, you can use the provided
70-
`install.ps1` script instead of `install`.
70+
`install.ps1` script instead of `install`. On Windows, Dotbot only supports
71+
Python 3.8+, and it requires that your account is [allowed to create symbolic
72+
links][windows-symlinks].
7173

7274
To get started, you just need to fill in the `install.conf.yaml` and Dotbot
7375
will take care of the rest. To help you get started we have [an
@@ -481,7 +483,7 @@ Copyright (c) 2014-2021 Anish Athalye. Released under the MIT License. See
481483
[init-dotfiles]: https://github.com/Vaelatern/init-dotfiles
482484
[dotfiles-template]: https://github.com/anishathalye/dotfiles_template
483485
[inspiration]: https://github.com/anishathalye/dotbot/wiki/Users
484-
[managing-dotfiles-post]: http://www.anishathalye.com/2014/08/03/managing-your-dotfiles/
486+
[windows-symlinks]: https://learn.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/create-symbolic-links
485487
[json2yaml]: https://www.json2yaml.com/
486488
[plugins]: https://github.com/anishathalye/dotbot/wiki/Plugins
487489
[wiki]: https://github.com/anishathalye/dotbot/wiki

dotbot/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
from .cli import main
22
from .plugin import Plugin
33

4-
__version__ = '1.19.0'
4+
__version__ = "1.19.1"

dotbot/cli.py

+99-56
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,87 @@
1-
import os, glob
1+
import glob
2+
import os
3+
import subprocess
24
import sys
3-
45
from argparse import ArgumentParser, RawTextHelpFormatter
6+
7+
import dotbot
8+
59
from .config import ConfigReader, ReadingError
610
from .dispatcher import Dispatcher, DispatchError
7-
from .messenger import Messenger
8-
from .messenger import Level
11+
from .messenger import Level, Messenger
12+
from .plugins import Clean, Create, Link, Plugins, Shell
913
from .util import module
1014

11-
import dotbot
12-
import os
13-
import subprocess
1415

1516
def add_options(parser):
16-
parser.add_argument('-Q', '--super-quiet', action='store_true',
17-
help='suppress almost all output')
18-
parser.add_argument('-q', '--quiet', action='store_true',
19-
help='suppress most output')
20-
parser.add_argument('-v', '--verbose', action='count', default=0,
21-
help='enable verbose output\n'
22-
'-v: typical verbose\n'
23-
'-vv: also, set shell commands stderr/stdout to true')
24-
parser.add_argument('-d', '--base-directory',
25-
help='execute commands from within BASEDIR',
26-
metavar='BASEDIR')
27-
parser.add_argument('-c', '--config-file',
28-
help='run commands given in CONFIGFILE', metavar='CONFIGFILE')
29-
parser.add_argument('-p', '--plugin', action='append', dest='plugins', default=[],
30-
help='load PLUGIN as a plugin', metavar='PLUGIN')
31-
parser.add_argument('--disable-built-in-plugins',
32-
action='store_true', help='disable built-in plugins')
33-
parser.add_argument('--plugin-dir', action='append', dest='plugin_dirs', default=[],
34-
metavar='PLUGIN_DIR', help='load all plugins in PLUGIN_DIR')
35-
parser.add_argument('--only', nargs='+',
36-
help='only run specified directives', metavar='DIRECTIVE')
37-
parser.add_argument('--except', nargs='+', dest='skip',
38-
help='skip specified directives', metavar='DIRECTIVE')
39-
parser.add_argument('--force-color', dest='force_color', action='store_true',
40-
help='force color output')
41-
parser.add_argument('--no-color', dest='no_color', action='store_true',
42-
help='disable color output')
43-
parser.add_argument('--version', action='store_true',
44-
help='show program\'s version number and exit')
45-
parser.add_argument('-x', '--exit-on-failure', dest='exit_on_failure', action='store_true',
46-
help='exit after first failed directive')
17+
parser.add_argument(
18+
"-Q", "--super-quiet", action="store_true", help="suppress almost all output"
19+
)
20+
parser.add_argument("-q", "--quiet", action="store_true", help="suppress most output")
21+
parser.add_argument(
22+
"-v",
23+
"--verbose",
24+
action="count",
25+
default=0,
26+
help="enable verbose output\n"
27+
"-v: typical verbose\n"
28+
"-vv: also, set shell commands stderr/stdout to true",
29+
)
30+
parser.add_argument(
31+
"-d", "--base-directory", help="execute commands from within BASEDIR", metavar="BASEDIR"
32+
)
33+
parser.add_argument(
34+
"-c", "--config-file", help="run commands given in CONFIGFILE", metavar="CONFIGFILE"
35+
)
36+
parser.add_argument(
37+
"-p",
38+
"--plugin",
39+
action="append",
40+
dest="plugins",
41+
default=[],
42+
help="load PLUGIN as a plugin",
43+
metavar="PLUGIN",
44+
)
45+
parser.add_argument(
46+
"--disable-built-in-plugins", action="store_true", help="disable built-in plugins"
47+
)
48+
parser.add_argument(
49+
"--plugin-dir",
50+
action="append",
51+
dest="plugin_dirs",
52+
default=[],
53+
metavar="PLUGIN_DIR",
54+
help="load all plugins in PLUGIN_DIR",
55+
)
56+
parser.add_argument(
57+
"--only", nargs="+", help="only run specified directives", metavar="DIRECTIVE"
58+
)
59+
parser.add_argument(
60+
"--except", nargs="+", dest="skip", help="skip specified directives", metavar="DIRECTIVE"
61+
)
62+
parser.add_argument(
63+
"--force-color", dest="force_color", action="store_true", help="force color output"
64+
)
65+
parser.add_argument(
66+
"--no-color", dest="no_color", action="store_true", help="disable color output"
67+
)
68+
parser.add_argument(
69+
"--version", action="store_true", help="show program's version number and exit"
70+
)
71+
parser.add_argument(
72+
"-x",
73+
"--exit-on-failure",
74+
dest="exit_on_failure",
75+
action="store_true",
76+
help="exit after first failed directive",
77+
)
78+
4779

4880
def read_config(config_file):
4981
reader = ConfigReader(config_file)
5082
return reader.get_config()
5183

84+
5285
def main():
5386
log = Messenger()
5487
try:
@@ -58,12 +91,15 @@ def main():
5891
if options.version:
5992
try:
6093
with open(os.devnull) as devnull:
61-
git_hash = subprocess.check_output(['git', 'rev-parse', 'HEAD'],
62-
cwd=os.path.dirname(os.path.abspath(__file__)), stderr=devnull)
63-
hash_msg = ' (git %s)' % git_hash[:10]
94+
git_hash = subprocess.check_output(
95+
["git", "rev-parse", "HEAD"],
96+
cwd=os.path.dirname(os.path.abspath(__file__)),
97+
stderr=devnull,
98+
)
99+
hash_msg = " (git %s)" % git_hash[:10]
64100
except (OSError, subprocess.CalledProcessError):
65-
hash_msg = ''
66-
print('Dotbot version %s%s' % (dotbot.__version__, hash_msg))
101+
hash_msg = ""
102+
print("Dotbot version %s%s" % (dotbot.__version__, hash_msg))
67103
exit(0)
68104
if options.super_quiet:
69105
log.set_level(Level.WARNING)
@@ -82,43 +118,50 @@ def main():
82118
else:
83119
log.use_color(sys.stdout.isatty())
84120

121+
plugins = []
85122
plugin_directories = list(options.plugin_dirs)
86123
if not options.disable_built_in_plugins:
87-
from .plugins import Clean, Create, Link, Shell, Plugins
124+
plugins.extend([Clean, Create, Link, Plugins, Shell])
88125
plugin_paths = []
89126
for directory in plugin_directories:
90-
for plugin_path in glob.glob(os.path.join(directory, '*.py')):
91-
plugin_paths.append(plugin_path)
127+
for plugin_path in glob.glob(os.path.join(directory, "*.py")):
128+
plugin_paths.append(plugin_path)
92129
for plugin_path in options.plugins:
93130
plugin_paths.append(plugin_path)
94131
for plugin_path in plugin_paths:
95132
abspath = os.path.abspath(plugin_path)
96-
module.load(abspath)
133+
plugins.extend(module.load(abspath))
97134
if not options.config_file:
98-
log.error('No configuration file specified')
135+
log.error("No configuration file specified")
99136
exit(1)
100137
tasks = read_config(options.config_file)
101138
if tasks is None:
102-
log.warning('Configuration file is empty, no work to do')
139+
log.warning("Configuration file is empty, no work to do")
103140
tasks = []
104141
if not isinstance(tasks, list):
105-
raise ReadingError('Configuration file must be a list of tasks')
142+
raise ReadingError("Configuration file must be a list of tasks")
106143
if options.base_directory:
107144
base_directory = os.path.abspath(options.base_directory)
108145
else:
109146
# default to directory of config file
110147
base_directory = os.path.dirname(os.path.abspath(options.config_file))
111148
os.chdir(base_directory)
112-
dispatcher = Dispatcher(base_directory, only=options.only, skip=options.skip,
113-
exit_on_failure=options.exit_on_failure, options=options)
149+
dispatcher = Dispatcher(
150+
base_directory,
151+
only=options.only,
152+
skip=options.skip,
153+
exit_on_failure=options.exit_on_failure,
154+
options=options,
155+
plugins=plugins,
156+
)
114157
success = dispatcher.dispatch(tasks)
115158
if success:
116-
log.info('\n==> All tasks executed successfully')
159+
log.info("\n==> All tasks executed successfully")
117160
else:
118-
raise DispatchError('\n==> Some tasks were not executed successfully')
161+
raise DispatchError("\n==> Some tasks were not executed successfully")
119162
except (ReadingError, DispatchError) as e:
120-
log.error('%s' % e)
163+
log.error("%s" % e)
121164
exit(1)
122165
except KeyboardInterrupt:
123-
log.error('\n==> Operation aborted')
166+
log.error("\n==> Operation aborted")
124167
exit(1)

dotbot/config.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
import yaml
21
import json
32
import os.path
3+
4+
import yaml
5+
46
from .util import string
57

8+
69
class ConfigReader(object):
710
def __init__(self, config_file_path):
811
self._config = self._read(config_file_path)
@@ -11,17 +14,18 @@ def _read(self, config_file_path):
1114
try:
1215
_, ext = os.path.splitext(config_file_path)
1316
with open(config_file_path) as fin:
14-
if ext == '.json':
17+
if ext == ".json":
1518
data = json.load(fin)
1619
else:
1720
data = yaml.safe_load(fin)
1821
return data
1922
except Exception as e:
2023
msg = string.indent_lines(str(e))
21-
raise ReadingError('Could not read config file:\n%s' % msg)
24+
raise ReadingError("Could not read config file:\n%s" % msg)
2225

2326
def get_config(self):
2427
return self._config
2528

29+
2630
class ReadingError(Exception):
2731
pass

dotbot/context.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
import os
33
from argparse import Namespace
44

5+
56
class Context(object):
6-
'''
7+
"""
78
Contextual data and information for plugins.
8-
'''
9+
"""
910

1011
def __init__(self, base_directory, options=Namespace()):
1112
self._base_directory = base_directory

0 commit comments

Comments
 (0)