baldwin 0.0.8__tar.gz → 0.0.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of baldwin might be problematic. Click here for more details.
- {baldwin-0.0.8 → baldwin-0.0.9}/PKG-INFO +7 -5
- {baldwin-0.0.8 → baldwin-0.0.9}/README.md +4 -1
- baldwin-0.0.9/baldwin/__init__.py +4 -0
- {baldwin-0.0.8 → baldwin-0.0.9}/baldwin/__main__.py +2 -0
- {baldwin-0.0.8 → baldwin-0.0.9}/baldwin/lib.py +33 -10
- {baldwin-0.0.8 → baldwin-0.0.9}/baldwin/main.py +5 -1
- baldwin-0.0.9/baldwin/utils.py +46 -0
- {baldwin-0.0.8 → baldwin-0.0.9}/pyproject.toml +27 -28
- baldwin-0.0.9/tests/conftest.py +24 -0
- baldwin-0.0.9/tests/pyproject.toml +10 -0
- baldwin-0.0.9/tests/test_commit.py +101 -0
- baldwin-0.0.9/tests/test_format.py +85 -0
- baldwin-0.0.9/tests/test_git_wrapper.py +25 -0
- baldwin-0.0.9/tests/test_info.py +19 -0
- baldwin-0.0.9/tests/test_init.py +81 -0
- baldwin-0.0.9/tests/test_install_units.py +27 -0
- baldwin-0.0.8/baldwin/__init__.py +0 -2
- {baldwin-0.0.8 → baldwin-0.0.9}/LICENSE.txt +0 -0
- {baldwin-0.0.8 → baldwin-0.0.9}/baldwin/py.typed +0 -0
- {baldwin-0.0.8 → baldwin-0.0.9}/baldwin/resources/default_gitattributes.txt +0 -0
- {baldwin-0.0.8 → baldwin-0.0.9}/baldwin/resources/default_gitignore.txt +0 -0
- {baldwin-0.0.8 → baldwin-0.0.9}/baldwin/resources/prettier.config.json +0 -0
- {baldwin-0.0.8 → baldwin-0.0.9}/man/baldwin.1 +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: baldwin
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.9
|
|
4
4
|
Summary: Simple tracking of your home directory with easy-to-read diffs.
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: command line,file management,git,version control
|
|
@@ -16,14 +16,13 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
18
18
|
Classifier: Typing :: Typed
|
|
19
|
-
Provides-Extra: erdantic
|
|
20
19
|
Requires-Dist: binaryornot (>=0.4.4,<0.5.0)
|
|
21
20
|
Requires-Dist: click (>=8.1.8,<9.0.0)
|
|
22
|
-
Requires-Dist:
|
|
21
|
+
Requires-Dist: colorlog (>=6.9.0,<7.0.0)
|
|
23
22
|
Requires-Dist: gitpython (>=3.1.44,<4.0.0)
|
|
24
23
|
Requires-Dist: platformdirs (>=4.3.6,<5.0.0)
|
|
25
24
|
Requires-Dist: tomlkit (>=0.13.2,<0.14.0)
|
|
26
|
-
Requires-Dist: typing-extensions (>=4.13.
|
|
25
|
+
Requires-Dist: typing-extensions (>=4.13.2,<5.0.0)
|
|
27
26
|
Project-URL: Documentation, https://baldwin.readthedocs.org
|
|
28
27
|
Project-URL: Homepage, https://tatsh.github.io/baldwin/
|
|
29
28
|
Project-URL: Issues, https://github.com/Tatsh/baldwin/issues
|
|
@@ -32,6 +31,7 @@ Description-Content-Type: text/markdown
|
|
|
32
31
|
|
|
33
32
|
# Simple home directory versioning
|
|
34
33
|
|
|
34
|
+
[](https://github.com/pre-commit/pre-commit)
|
|
35
35
|
[](https://github.com/Tatsh/baldwin/actions/workflows/qa.yml)
|
|
36
36
|
[](https://github.com/Tatsh/baldwin/actions/workflows/tests.yml)
|
|
37
37
|
[](https://coveralls.io/github/Tatsh/baldwin?branch=master)
|
|
@@ -39,7 +39,9 @@ Description-Content-Type: text/markdown
|
|
|
39
39
|

|
|
40
40
|

|
|
41
41
|

|
|
42
|
-
](https://img.shields.io/github/commits-since/Tatsh/baldwin/v0.0.9/master)
|
|
43
|
+
|
|
44
|
+
[](https://bsky.app/profile/tatsh.bsky.social)
|
|
43
45
|
|
|
44
46
|
This is a conversion of my simple scripts to version my home directory with very specific excludes
|
|
45
47
|
and formatting every file upon commit so that readable diffs can be generated.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# Simple home directory versioning
|
|
2
2
|
|
|
3
|
+
[](https://github.com/pre-commit/pre-commit)
|
|
3
4
|
[](https://github.com/Tatsh/baldwin/actions/workflows/qa.yml)
|
|
4
5
|
[](https://github.com/Tatsh/baldwin/actions/workflows/tests.yml)
|
|
5
6
|
[](https://coveralls.io/github/Tatsh/baldwin?branch=master)
|
|
@@ -7,7 +8,9 @@
|
|
|
7
8
|

|
|
8
9
|

|
|
9
10
|

|
|
10
|
-
](https://img.shields.io/github/commits-since/Tatsh/baldwin/v0.0.9/master)
|
|
12
|
+
|
|
13
|
+
[](https://bsky.app/profile/tatsh.bsky.social)
|
|
11
14
|
|
|
12
15
|
This is a conversion of my simple scripts to version my home directory with very specific excludes
|
|
13
16
|
and formatting every file upon commit so that readable diffs can be generated.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Baldwin library."""
|
|
2
|
-
from
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
3
4
|
from dataclasses import dataclass
|
|
4
5
|
from datetime import datetime, timezone
|
|
5
6
|
from importlib import resources
|
|
@@ -7,7 +8,7 @@ from itertools import chain
|
|
|
7
8
|
from pathlib import Path
|
|
8
9
|
from shlex import quote
|
|
9
10
|
from shutil import which
|
|
10
|
-
from typing import Any, Literal
|
|
11
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
11
12
|
import logging
|
|
12
13
|
import os
|
|
13
14
|
import subprocess as sp
|
|
@@ -17,6 +18,9 @@ from git import Actor, Repo
|
|
|
17
18
|
import platformdirs
|
|
18
19
|
import tomlkit
|
|
19
20
|
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from collections.abc import Iterable
|
|
23
|
+
|
|
20
24
|
log = logging.getLogger(__name__)
|
|
21
25
|
|
|
22
26
|
|
|
@@ -61,16 +65,24 @@ def init() -> None:
|
|
|
61
65
|
|
|
62
66
|
def auto_commit() -> None:
|
|
63
67
|
"""Automated commit of changed and untracked files."""
|
|
68
|
+
def can_open(file: Path) -> bool:
|
|
69
|
+
"""Check if a file can be opened."""
|
|
70
|
+
try:
|
|
71
|
+
with file.open('rb'):
|
|
72
|
+
pass
|
|
73
|
+
except OSError:
|
|
74
|
+
return False
|
|
75
|
+
return True
|
|
76
|
+
|
|
64
77
|
repo = get_repo()
|
|
65
|
-
diff_items = [Path.home() / e.a_path for e in repo.index.diff(None)
|
|
66
|
-
if e.a_path is not None] # pragma: no cover
|
|
78
|
+
diff_items = [Path.home() / e.a_path for e in repo.index.diff(None) if e.a_path is not None]
|
|
67
79
|
items_to_add = [
|
|
68
80
|
*[p for p in diff_items if p.exists()], *[
|
|
69
|
-
x for x in (Path.home() / y
|
|
70
|
-
|
|
81
|
+
x for x in (Path.home() / y for y in repo.untracked_files)
|
|
82
|
+
if can_open(x) and x.is_file() and not is_binary(str(x))
|
|
71
83
|
]
|
|
72
84
|
]
|
|
73
|
-
items_to_remove = [p for p in diff_items if not p.exists()]
|
|
85
|
+
items_to_remove = [p for p in diff_items if not p.exists()]
|
|
74
86
|
if items_to_add:
|
|
75
87
|
format_(items_to_add)
|
|
76
88
|
repo.index.add(items_to_add)
|
|
@@ -96,7 +108,13 @@ def repo_info() -> RepoInfo:
|
|
|
96
108
|
|
|
97
109
|
|
|
98
110
|
def install_units() -> None:
|
|
99
|
-
"""
|
|
111
|
+
"""
|
|
112
|
+
Install systemd units for automatic committing.
|
|
113
|
+
|
|
114
|
+
Raises
|
|
115
|
+
------
|
|
116
|
+
FileNotFoundError
|
|
117
|
+
"""
|
|
100
118
|
bw = which('bw')
|
|
101
119
|
if not bw:
|
|
102
120
|
raise FileNotFoundError
|
|
@@ -105,8 +123,9 @@ def install_units() -> None:
|
|
|
105
123
|
Description=Home directory VCS commit
|
|
106
124
|
|
|
107
125
|
[Service]
|
|
108
|
-
|
|
126
|
+
Environment=NO_COLOR=1
|
|
109
127
|
ExecStart={bw} auto-commit
|
|
128
|
+
Type=oneshot
|
|
110
129
|
""")
|
|
111
130
|
log.debug('Wrote to `%s`.', service_file)
|
|
112
131
|
timer_file = Path('~/.config/systemd/user/home-vcs.timer').expanduser()
|
|
@@ -176,7 +195,11 @@ def format_(filenames: Iterable[Path | str] | None = None,
|
|
|
176
195
|
for d in repo.index.diff(None) if d.a_path is not None),
|
|
177
196
|
*(x for x in (Path.home() / y for y in repo.untracked_files)
|
|
178
197
|
if x.is_file() and not is_binary(str(x))))
|
|
179
|
-
if not (filenames := list(filenames))
|
|
198
|
+
if not (filenames := list(filenames)):
|
|
199
|
+
log.debug('No files to format.')
|
|
200
|
+
return
|
|
201
|
+
if not (prettier := which('prettier')):
|
|
202
|
+
log.debug('Prettier not found in PATH.')
|
|
180
203
|
return
|
|
181
204
|
with resources.path('baldwin.resources', 'prettier.config.json') as default_config_file:
|
|
182
205
|
config_file = get_config().get('baldwin', {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
"""Commands."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
1
4
|
import logging
|
|
2
5
|
|
|
3
6
|
import click
|
|
@@ -11,6 +14,7 @@ from .lib import (
|
|
|
11
14
|
repo_info,
|
|
12
15
|
set_git_env_vars,
|
|
13
16
|
)
|
|
17
|
+
from .utils import setup_logging
|
|
14
18
|
|
|
15
19
|
log = logging.getLogger(__name__)
|
|
16
20
|
|
|
@@ -22,7 +26,7 @@ __all__ = ('baldwin', 'git')
|
|
|
22
26
|
def baldwin(*, debug: bool = False) -> None:
|
|
23
27
|
"""Manage a home directory with Git."""
|
|
24
28
|
set_git_env_vars()
|
|
25
|
-
|
|
29
|
+
setup_logging(debug=debug)
|
|
26
30
|
|
|
27
31
|
|
|
28
32
|
@click.command(context_settings={
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Utilities."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
import logging.config
|
|
6
|
+
|
|
7
|
+
log = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def setup_logging(*,
|
|
11
|
+
debug: bool = False,
|
|
12
|
+
force_color: bool = False,
|
|
13
|
+
no_color: bool = False) -> None: # pragma: no cover
|
|
14
|
+
"""Set up logging configuration."""
|
|
15
|
+
logging.config.dictConfig({
|
|
16
|
+
'disable_existing_loggers': True,
|
|
17
|
+
'root': {
|
|
18
|
+
'level': 'DEBUG' if debug else 'INFO',
|
|
19
|
+
'handlers': ['console'],
|
|
20
|
+
},
|
|
21
|
+
'formatters': {
|
|
22
|
+
'default': {
|
|
23
|
+
'()': 'colorlog.ColoredFormatter',
|
|
24
|
+
'force_color': force_color,
|
|
25
|
+
'format': (
|
|
26
|
+
'%(light_cyan)s%(asctime)s%(reset)s | %(log_color)s%(levelname)-8s%(reset)s | '
|
|
27
|
+
'%(light_green)s%(name)s%(reset)s:%(light_red)s%(funcName)s%(reset)s:'
|
|
28
|
+
'%(blue)s%(lineno)d%(reset)s - %(message)s'),
|
|
29
|
+
'no_color': no_color,
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
'handlers': {
|
|
33
|
+
'console': {
|
|
34
|
+
'class': 'colorlog.StreamHandler',
|
|
35
|
+
'formatter': 'default',
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
'loggers': {
|
|
39
|
+
'baldwin': {
|
|
40
|
+
'level': 'DEBUG' if debug else 'INFO',
|
|
41
|
+
'handlers': ['console'],
|
|
42
|
+
'propagate': False,
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
'version': 1
|
|
46
|
+
})
|
|
@@ -20,15 +20,12 @@ keywords = ["command line", "file management", "git", "version control"]
|
|
|
20
20
|
license = "MIT"
|
|
21
21
|
name = "baldwin"
|
|
22
22
|
readme = "README.md"
|
|
23
|
-
version = "0.0.
|
|
23
|
+
version = "0.0.9"
|
|
24
24
|
|
|
25
25
|
[[project.authors]]
|
|
26
26
|
email = "audvare@gmail.com"
|
|
27
27
|
name = "Andrew Udvare"
|
|
28
28
|
|
|
29
|
-
[project.optional-dependencies]
|
|
30
|
-
erdantic = ["erdantic<2.0"]
|
|
31
|
-
|
|
32
29
|
[project.scripts]
|
|
33
30
|
bw = "baldwin.main:baldwin"
|
|
34
31
|
hgit = "baldwin.main:git"
|
|
@@ -39,22 +36,21 @@ documentation = "https://baldwin.readthedocs.org"
|
|
|
39
36
|
homepage = "https://tatsh.github.io/baldwin/"
|
|
40
37
|
repository = "https://github.com/Tatsh/baldwin"
|
|
41
38
|
|
|
42
|
-
[tool]
|
|
43
|
-
|
|
44
39
|
[tool.commitizen]
|
|
45
40
|
tag_format = "v$version"
|
|
46
41
|
version_files = [
|
|
47
42
|
".wiswa.jsonnet",
|
|
48
43
|
"CITATION.cff",
|
|
49
44
|
"README.md",
|
|
50
|
-
"package.json",
|
|
51
45
|
"baldwin/__init__.py",
|
|
46
|
+
"docs/index.rst",
|
|
47
|
+
"man/baldwin.1",
|
|
48
|
+
"package.json",
|
|
52
49
|
]
|
|
53
50
|
version_provider = "pep621"
|
|
54
51
|
|
|
55
|
-
[tool.coverage]
|
|
56
|
-
|
|
57
52
|
[tool.coverage.report]
|
|
53
|
+
exclude_also = ["if TYPE_CHECKING:"]
|
|
58
54
|
omit = ["conftest.py", "tests.py", "tests/test_*.py", "__main__.py"]
|
|
59
55
|
show_missing = true
|
|
60
56
|
|
|
@@ -84,28 +80,27 @@ strict_optional = true
|
|
|
84
80
|
warn_unreachable = true
|
|
85
81
|
|
|
86
82
|
[tool.poetry]
|
|
87
|
-
include = ["man"]
|
|
83
|
+
include = ["man", { format = "sdist", path = "tests" }]
|
|
88
84
|
|
|
89
85
|
[tool.poetry.dependencies]
|
|
90
86
|
binaryornot = "^0.4.4"
|
|
91
87
|
click = "^8.1.8"
|
|
88
|
+
colorlog = "^6.9.0"
|
|
92
89
|
gitpython = "^3.1.44"
|
|
93
90
|
platformdirs = "^4.3.6"
|
|
94
91
|
python = ">=3.10,<3.14"
|
|
95
92
|
tomlkit = "^0.13.2"
|
|
96
|
-
typing-extensions = "^4.13.
|
|
97
|
-
|
|
98
|
-
[tool.poetry.group]
|
|
93
|
+
typing-extensions = "^4.13.2"
|
|
99
94
|
|
|
100
95
|
[tool.poetry.group.dev]
|
|
101
96
|
optional = true
|
|
102
97
|
|
|
103
98
|
[tool.poetry.group.dev.dependencies]
|
|
104
99
|
binaryornot-stubs = "^0"
|
|
105
|
-
commitizen = "^4.
|
|
100
|
+
commitizen = "^4.7.0"
|
|
106
101
|
djlint = "^1.36.4"
|
|
107
102
|
mypy = ">=1.12,<1.16"
|
|
108
|
-
ruff = "^0.11.
|
|
103
|
+
ruff = "^0.11.9"
|
|
109
104
|
yapf = "^0.43.0"
|
|
110
105
|
|
|
111
106
|
[tool.poetry.group.docs]
|
|
@@ -118,16 +113,23 @@ docutils = "^0.21.2"
|
|
|
118
113
|
esbonio = "^0.16.5"
|
|
119
114
|
numpydoc = "^1.8.0"
|
|
120
115
|
restructuredtext-lint = "^1.4.0"
|
|
121
|
-
sphinx = "<8.2.0"
|
|
122
116
|
sphinx-click = "^6.0.0"
|
|
123
117
|
sphinx-datatables = "^0.2.1"
|
|
124
118
|
sphinx-hoverxref = "^1.4.2"
|
|
125
|
-
sphinx-immaterial = "^0.13.
|
|
119
|
+
sphinx-immaterial = "^0.13.5"
|
|
126
120
|
|
|
127
121
|
[tool.poetry.group.docs.dependencies.enum-tools]
|
|
128
122
|
extras = ["sphinx"]
|
|
129
123
|
version = "^0.12.0"
|
|
130
124
|
|
|
125
|
+
[[tool.poetry.group.docs.dependencies.sphinx]]
|
|
126
|
+
python = ">=3.11"
|
|
127
|
+
version = "^8.2.0"
|
|
128
|
+
|
|
129
|
+
[[tool.poetry.group.docs.dependencies.sphinx]]
|
|
130
|
+
python = "<3.11"
|
|
131
|
+
version = "^7.2.5"
|
|
132
|
+
|
|
131
133
|
[tool.poetry.group.tests]
|
|
132
134
|
optional = true
|
|
133
135
|
|
|
@@ -160,8 +162,6 @@ reportUnnecessaryTypeIgnoreComment = "none"
|
|
|
160
162
|
typeCheckingMode = "off"
|
|
161
163
|
useLibraryCodeForTypes = false
|
|
162
164
|
|
|
163
|
-
[tool.pytest]
|
|
164
|
-
|
|
165
165
|
[tool.pytest.ini_options]
|
|
166
166
|
mock_use_standalone_module = true
|
|
167
167
|
norecursedirs = ["node_modules"]
|
|
@@ -191,6 +191,7 @@ extend-select = [
|
|
|
191
191
|
"CPY",
|
|
192
192
|
"D",
|
|
193
193
|
"DJ",
|
|
194
|
+
"DOC",
|
|
194
195
|
"DTZ",
|
|
195
196
|
"E",
|
|
196
197
|
"EM",
|
|
@@ -198,6 +199,7 @@ extend-select = [
|
|
|
198
199
|
"EXE",
|
|
199
200
|
"F",
|
|
200
201
|
"FA",
|
|
202
|
+
"FAST",
|
|
201
203
|
"FBT",
|
|
202
204
|
"FIX",
|
|
203
205
|
"FLY",
|
|
@@ -229,11 +231,12 @@ extend-select = [
|
|
|
229
231
|
"SLOT",
|
|
230
232
|
"T10",
|
|
231
233
|
"T20",
|
|
232
|
-
"
|
|
234
|
+
"TC",
|
|
233
235
|
"TD",
|
|
234
236
|
"TID",
|
|
235
237
|
"TRY",
|
|
236
238
|
"UP",
|
|
239
|
+
"W",
|
|
237
240
|
"YTT",
|
|
238
241
|
]
|
|
239
242
|
ignore = [
|
|
@@ -245,17 +248,11 @@ ignore = [
|
|
|
245
248
|
"C901",
|
|
246
249
|
"COM812",
|
|
247
250
|
"CPY001",
|
|
248
|
-
"
|
|
249
|
-
"D101",
|
|
250
|
-
"D102",
|
|
251
|
-
"D103",
|
|
252
|
-
"D104",
|
|
253
|
-
"D105",
|
|
254
|
-
"D106",
|
|
255
|
-
"D107",
|
|
251
|
+
"D201",
|
|
256
252
|
"D203",
|
|
257
253
|
"D204",
|
|
258
254
|
"D212",
|
|
255
|
+
"DOC201",
|
|
259
256
|
"EM101",
|
|
260
257
|
"N818",
|
|
261
258
|
"PLR0912",
|
|
@@ -284,6 +281,8 @@ multiline-quotes = "double"
|
|
|
284
281
|
case-sensitive = true
|
|
285
282
|
combine-as-imports = true
|
|
286
283
|
from-first = true
|
|
284
|
+
required-imports = ["from __future__ import annotations"]
|
|
285
|
+
section-order = ["future", "standard-library", "third-party", "local-folder"]
|
|
287
286
|
|
|
288
287
|
[tool.ruff.lint.pep8-naming]
|
|
289
288
|
extend-ignore-names = ["test_*"]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Configuration for Pytest."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import NoReturn
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
from click.testing import CliRunner
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
if os.getenv('_PYTEST_RAISE', '0') != '0': # pragma no cover
|
|
11
|
+
|
|
12
|
+
@pytest.hookimpl(tryfirst=True)
|
|
13
|
+
def pytest_exception_interact(call: pytest.CallInfo[None]) -> NoReturn:
|
|
14
|
+
assert call.excinfo is not None
|
|
15
|
+
raise call.excinfo.value
|
|
16
|
+
|
|
17
|
+
@pytest.hookimpl(tryfirst=True)
|
|
18
|
+
def pytest_internalerror(excinfo: pytest.ExceptionInfo[BaseException]) -> NoReturn:
|
|
19
|
+
raise excinfo.value
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.fixture
|
|
23
|
+
def runner() -> CliRunner:
|
|
24
|
+
return CliRunner()
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from baldwin.main import baldwin
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from click.testing import CliRunner
|
|
9
|
+
from pytest_mock import MockerFixture
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_commit(runner: CliRunner, mocker: MockerFixture) -> None:
|
|
13
|
+
path = mocker.patch('baldwin.lib.Path')
|
|
14
|
+
mocker.patch('baldwin.lib.platformdirs.user_data_path')
|
|
15
|
+
mocker.patch('baldwin.lib.resources')
|
|
16
|
+
which = mocker.patch('baldwin.lib.which')
|
|
17
|
+
which.return_value = None # Disable format
|
|
18
|
+
repo = mocker.patch('baldwin.lib.Repo')
|
|
19
|
+
repo.return_value.untracked_files = ['untracked1']
|
|
20
|
+
changed_file = mocker.MagicMock()
|
|
21
|
+
changed_file.a_path = 'changed1'
|
|
22
|
+
deleted_file = mocker.MagicMock()
|
|
23
|
+
deleted_file.a_path = 'deleted1'
|
|
24
|
+
repo.return_value.index.diff.return_value = [changed_file, deleted_file]
|
|
25
|
+
path.home.return_value.__truediv__.return_value.exists.side_effect = [True, False, True, False]
|
|
26
|
+
runner.invoke(baldwin, ('auto-commit',))
|
|
27
|
+
assert repo.return_value.index.add.called
|
|
28
|
+
assert repo.return_value.index.remove.called
|
|
29
|
+
assert repo.return_value.index.commit.called
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_commit_no_files(runner: CliRunner, mocker: MockerFixture) -> None:
|
|
33
|
+
mocker.patch('baldwin.lib.Path')
|
|
34
|
+
mocker.patch('baldwin.lib.platformdirs.user_data_path')
|
|
35
|
+
mocker.patch('baldwin.lib.resources')
|
|
36
|
+
which = mocker.patch('baldwin.lib.which')
|
|
37
|
+
which.return_value = None # Disable format
|
|
38
|
+
repo = mocker.patch('baldwin.lib.Repo')
|
|
39
|
+
repo.return_value.untracked_files = []
|
|
40
|
+
repo.return_value.index.diff.return_value = []
|
|
41
|
+
runner.invoke(baldwin, ('auto-commit',))
|
|
42
|
+
assert not repo.return_value.index.add.called
|
|
43
|
+
assert not repo.return_value.index.remove.called
|
|
44
|
+
assert not repo.return_value.index.commit.called
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_commit_no_add(runner: CliRunner, mocker: MockerFixture) -> None:
|
|
48
|
+
path = mocker.patch('baldwin.lib.Path')
|
|
49
|
+
mocker.patch('baldwin.lib.platformdirs.user_data_path')
|
|
50
|
+
mocker.patch('baldwin.lib.resources')
|
|
51
|
+
which = mocker.patch('baldwin.lib.which')
|
|
52
|
+
which.return_value = None # Disable format
|
|
53
|
+
repo = mocker.patch('baldwin.lib.Repo')
|
|
54
|
+
repo.return_value.untracked_files = []
|
|
55
|
+
deleted_file = mocker.MagicMock()
|
|
56
|
+
deleted_file.a_path = 'deleted1'
|
|
57
|
+
repo.return_value.index.diff.return_value = [deleted_file]
|
|
58
|
+
path.home.return_value.__truediv__.return_value.exists.return_value = False
|
|
59
|
+
runner.invoke(baldwin, ('auto-commit',))
|
|
60
|
+
assert not repo.return_value.index.add.called
|
|
61
|
+
assert repo.return_value.index.remove.called
|
|
62
|
+
assert repo.return_value.index.commit.called
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_commit_no_delete(runner: CliRunner, mocker: MockerFixture) -> None:
|
|
66
|
+
mocker.patch('baldwin.lib.Path')
|
|
67
|
+
mocker.patch('baldwin.lib.platformdirs.user_data_path')
|
|
68
|
+
mocker.patch('baldwin.lib.resources')
|
|
69
|
+
which = mocker.patch('baldwin.lib.which')
|
|
70
|
+
which.return_value = None # Disable format
|
|
71
|
+
repo = mocker.patch('baldwin.lib.Repo')
|
|
72
|
+
repo.return_value.untracked_files = ['untracked1']
|
|
73
|
+
changed_file = mocker.MagicMock()
|
|
74
|
+
changed_file.a_path = 'changed1'
|
|
75
|
+
repo.return_value.index.diff.return_value = [changed_file]
|
|
76
|
+
runner.invoke(baldwin, ('auto-commit',))
|
|
77
|
+
assert repo.return_value.index.add.called
|
|
78
|
+
assert not repo.return_value.index.remove.called
|
|
79
|
+
assert repo.return_value.index.commit.called
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_commit_ignore_unreadable_files(runner: CliRunner, mocker: MockerFixture) -> None:
|
|
83
|
+
path = mocker.patch('baldwin.lib.Path')
|
|
84
|
+
mocker.patch('baldwin.lib.platformdirs.user_data_path')
|
|
85
|
+
mocker.patch('baldwin.lib.resources')
|
|
86
|
+
which = mocker.patch('baldwin.lib.which')
|
|
87
|
+
which.return_value = None # Disable format
|
|
88
|
+
repo = mocker.patch('baldwin.lib.Repo')
|
|
89
|
+
repo.return_value.untracked_files = ['untracked1']
|
|
90
|
+
changed_file = mocker.MagicMock()
|
|
91
|
+
changed_file.a_path = 'changed1'
|
|
92
|
+
deleted_file = mocker.MagicMock()
|
|
93
|
+
deleted_file.a_path = 'deleted1'
|
|
94
|
+
repo.return_value.index.diff.return_value = [changed_file, deleted_file]
|
|
95
|
+
path.home.return_value.__truediv__.return_value.exists.side_effect = [True, False, True, False]
|
|
96
|
+
path.home.return_value.__truediv__.return_value.is_file.side_effect = [True, False, True]
|
|
97
|
+
path.home.return_value.__truediv__.return_value.open.side_effect = PermissionError
|
|
98
|
+
runner.invoke(baldwin, ('auto-commit',))
|
|
99
|
+
assert len(repo.return_value.index.add.call_args[0][0]) == 1
|
|
100
|
+
assert repo.return_value.index.remove.called
|
|
101
|
+
assert repo.return_value.index.commit.called
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from baldwin.main import baldwin
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from click.testing import CliRunner
|
|
9
|
+
from pytest_mock import MockerFixture
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_format_no_prettier(runner: CliRunner, mocker: MockerFixture) -> None:
|
|
13
|
+
path = mocker.patch('baldwin.lib.Path')
|
|
14
|
+
mocker.patch('baldwin.lib.platformdirs.user_data_path')
|
|
15
|
+
mocker.patch('baldwin.lib.resources')
|
|
16
|
+
run = mocker.patch('baldwin.lib.sp.run')
|
|
17
|
+
which = mocker.patch('baldwin.lib.which')
|
|
18
|
+
which.return_value = None
|
|
19
|
+
repo = mocker.patch('baldwin.lib.Repo')
|
|
20
|
+
repo.untracked_files = ['untracked1']
|
|
21
|
+
changed_file = mocker.MagicMock()
|
|
22
|
+
changed_file.a_path = 'changed1'
|
|
23
|
+
deleted_file = mocker.MagicMock()
|
|
24
|
+
deleted_file.a_path = 'deleted1'
|
|
25
|
+
repo.return_value.index.diff.return_value = [changed_file, deleted_file]
|
|
26
|
+
path.home.return_value.__truediv__.return_value.exists.side_effect = [True, False, True, False]
|
|
27
|
+
runner.invoke(baldwin, ('format',))
|
|
28
|
+
assert not run.called
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_format_no_files(runner: CliRunner, mocker: MockerFixture) -> None:
|
|
32
|
+
mocker.patch('baldwin.lib.Path')
|
|
33
|
+
mocker.patch('baldwin.lib.platformdirs.user_data_path')
|
|
34
|
+
mocker.patch('baldwin.lib.resources')
|
|
35
|
+
run = mocker.patch('baldwin.lib.sp.run')
|
|
36
|
+
which = mocker.patch('baldwin.lib.which')
|
|
37
|
+
repo = mocker.patch('baldwin.lib.Repo')
|
|
38
|
+
repo.untracked_files = []
|
|
39
|
+
repo.return_value.index.diff.return_value = []
|
|
40
|
+
runner.invoke(baldwin, ('format',))
|
|
41
|
+
assert not run.called
|
|
42
|
+
assert not which.called
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_format(runner: CliRunner, mocker: MockerFixture) -> None:
|
|
46
|
+
path = mocker.patch('baldwin.lib.Path')
|
|
47
|
+
mocker.patch('baldwin.lib.platformdirs.user_data_path')
|
|
48
|
+
mocker.patch('baldwin.lib.platformdirs.user_config_path'
|
|
49
|
+
).return_value.__truediv__.return_value.exists.return_value = False
|
|
50
|
+
mocker.patch('baldwin.lib.resources')
|
|
51
|
+
run = mocker.patch('baldwin.lib.sp.run')
|
|
52
|
+
which = mocker.patch('baldwin.lib.which')
|
|
53
|
+
which.return_value = '/bin/prettier'
|
|
54
|
+
repo = mocker.patch('baldwin.lib.Repo')
|
|
55
|
+
repo.untracked_files = ['untracked1']
|
|
56
|
+
changed_file = mocker.MagicMock()
|
|
57
|
+
changed_file.a_path = 'changed1'
|
|
58
|
+
deleted_file = mocker.MagicMock()
|
|
59
|
+
deleted_file.a_path = 'deleted1'
|
|
60
|
+
repo.return_value.index.diff.return_value = [changed_file, deleted_file]
|
|
61
|
+
path.home.return_value.__truediv__.return_value.exists.side_effect = [True, False, True, False]
|
|
62
|
+
runner.invoke(baldwin, ('format',))
|
|
63
|
+
assert run.called
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_format_config_file_exists(runner: CliRunner, mocker: MockerFixture) -> None:
|
|
67
|
+
path = mocker.patch('baldwin.lib.Path')
|
|
68
|
+
mocker.patch('baldwin.lib.platformdirs.user_data_path')
|
|
69
|
+
mocker.patch('baldwin.lib.platformdirs.user_config_path'
|
|
70
|
+
).return_value.__truediv__.return_value.exists.return_value = True
|
|
71
|
+
mocker.patch('baldwin.lib.resources')
|
|
72
|
+
mocker.patch('baldwin.lib.tomlkit.loads').return_value = {}
|
|
73
|
+
run = mocker.patch('baldwin.lib.sp.run')
|
|
74
|
+
which = mocker.patch('baldwin.lib.which')
|
|
75
|
+
which.return_value = '/bin/prettier'
|
|
76
|
+
repo = mocker.patch('baldwin.lib.Repo')
|
|
77
|
+
repo.untracked_files = ['untracked1']
|
|
78
|
+
changed_file = mocker.MagicMock()
|
|
79
|
+
changed_file.a_path = 'changed1'
|
|
80
|
+
deleted_file = mocker.MagicMock()
|
|
81
|
+
deleted_file.a_path = 'deleted1'
|
|
82
|
+
repo.return_value.index.diff.return_value = [changed_file, deleted_file]
|
|
83
|
+
path.home.return_value.__truediv__.return_value.exists.side_effect = [True, False, True, False]
|
|
84
|
+
runner.invoke(baldwin, ('format',))
|
|
85
|
+
assert run.called
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from baldwin.lib import get_git_path
|
|
7
|
+
from baldwin.main import baldwin, git
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from click.testing import CliRunner
|
|
11
|
+
from pytest_mock import MockerFixture
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_hgit_wrapper(runner: CliRunner, mocker: MockerFixture) -> None:
|
|
15
|
+
run = mocker.patch('baldwin.lib.sp.run')
|
|
16
|
+
runner.invoke(git, ('status',))
|
|
17
|
+
run.assert_called_once_with(
|
|
18
|
+
('git', f'--git-dir={get_git_path()}', f'--work-tree={Path.home()}', 'status'), check=False)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_bw_git_wrapper(runner: CliRunner, mocker: MockerFixture) -> None:
|
|
22
|
+
run = mocker.patch('baldwin.lib.sp.run')
|
|
23
|
+
runner.invoke(baldwin, ('git', 'status'))
|
|
24
|
+
run.assert_called_once_with(
|
|
25
|
+
('git', f'--git-dir={get_git_path()}', f'--work-tree={Path.home()}', 'status'), check=False)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
from baldwin.main import baldwin
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from click.testing import CliRunner
|
|
10
|
+
from pytest_mock import MockerFixture
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_init_returns_if_exists(runner: CliRunner, mocker: MockerFixture) -> None:
|
|
14
|
+
mocker.patch('baldwin.lib.platformdirs.user_data_path').return_value.exists.return_value = True
|
|
15
|
+
mocker.patch('baldwin.lib.Path')
|
|
16
|
+
run = runner.invoke(baldwin, ('info',))
|
|
17
|
+
lines = run.stdout.splitlines()
|
|
18
|
+
assert re.match(r'^git-dir path: <MagicMock.*>$', lines[0])
|
|
19
|
+
assert re.match(r'^work-tree path: <MagicMock.*>$', lines[1])
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from baldwin.main import baldwin
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from click.testing import CliRunner
|
|
9
|
+
from pytest_mock import MockerFixture
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_init_returns_if_exists(runner: CliRunner, mocker: MockerFixture) -> None:
|
|
13
|
+
mocker.patch('baldwin.lib.platformdirs.user_data_path').return_value.exists.return_value = True
|
|
14
|
+
repo = mocker.patch('baldwin.lib.Repo')
|
|
15
|
+
runner.invoke(baldwin, ('init',))
|
|
16
|
+
assert not repo.called
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_init(runner: CliRunner, mocker: MockerFixture) -> None:
|
|
20
|
+
path = mocker.patch('baldwin.lib.Path')
|
|
21
|
+
mocker.patch('baldwin.lib.platformdirs.user_data_path').return_value.exists.return_value = False
|
|
22
|
+
repo = mocker.patch('baldwin.lib.Repo')
|
|
23
|
+
mocker.patch('baldwin.lib.resources')
|
|
24
|
+
which = mocker.patch('baldwin.lib.which')
|
|
25
|
+
which.side_effect = ['jq', 'prettier']
|
|
26
|
+
runner.invoke(baldwin, ('init',))
|
|
27
|
+
assert repo.init.called
|
|
28
|
+
assert repo.init.return_value.index.add.called
|
|
29
|
+
path.home.return_value.__truediv__.assert_any_call('.gitattributes')
|
|
30
|
+
path.home.return_value.__truediv__.assert_any_call('.gitignore')
|
|
31
|
+
assert path.home.return_value.__truediv__.return_value.write_text.call_count == 2
|
|
32
|
+
which.assert_any_call('jq')
|
|
33
|
+
which.assert_any_call('prettier')
|
|
34
|
+
repo.init.return_value.git.execute.assert_any_call(('git', 'config', 'commit.gpgsign', 'false'))
|
|
35
|
+
repo.init.return_value.git.execute.assert_any_call(
|
|
36
|
+
('git', 'config', 'diff.json.textconv', '"jq" -MS .'))
|
|
37
|
+
repo.init.return_value.git.execute.assert_any_call(
|
|
38
|
+
('git', 'config', 'diff.xml.textconv',
|
|
39
|
+
'"prettier" --no-editorconfig --parser xml --xml-whitespace-sensitivity ignore'))
|
|
40
|
+
repo.init.return_value.git.execute.assert_any_call(
|
|
41
|
+
('git', 'config', 'diff.yaml.textconv', '"prettier" --no-editorconfig --parser yaml'))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_init_no_tools(runner: CliRunner, mocker: MockerFixture) -> None:
|
|
45
|
+
path = mocker.patch('baldwin.lib.Path')
|
|
46
|
+
mocker.patch('baldwin.lib.platformdirs.user_data_path').return_value.exists.return_value = False
|
|
47
|
+
repo = mocker.patch('baldwin.lib.Repo')
|
|
48
|
+
mocker.patch('baldwin.lib.resources')
|
|
49
|
+
which = mocker.patch('baldwin.lib.which')
|
|
50
|
+
which.return_value = False
|
|
51
|
+
runner.invoke(baldwin, ('init',))
|
|
52
|
+
assert repo.init.called
|
|
53
|
+
path.home.return_value.__truediv__.assert_any_call('.gitattributes')
|
|
54
|
+
path.home.return_value.__truediv__.assert_any_call('.gitignore')
|
|
55
|
+
assert path.home.return_value.__truediv__.return_value.write_text.call_count == 2
|
|
56
|
+
which.assert_any_call('jq')
|
|
57
|
+
which.assert_any_call('prettier')
|
|
58
|
+
repo.init.return_value.git.execute.assert_any_call(('git', 'config', 'commit.gpgsign', 'false'))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_init_no_xml_plugin(runner: CliRunner, mocker: MockerFixture) -> None:
|
|
62
|
+
path = mocker.patch('baldwin.lib.Path')
|
|
63
|
+
mocker.patch('baldwin.lib.platformdirs.user_data_path').return_value.exists.return_value = False
|
|
64
|
+
repo = mocker.patch('baldwin.lib.Repo')
|
|
65
|
+
mocker.patch('baldwin.lib.resources')
|
|
66
|
+
which = mocker.patch('baldwin.lib.which')
|
|
67
|
+
which.side_effect = ['jq', 'prettier']
|
|
68
|
+
path.return_value.resolve.return_value.parent.__truediv__.return_value.__truediv__.return_value.resolve.return_value.__truediv__.return_value.exists.return_value = False # noqa: E501
|
|
69
|
+
runner.invoke(baldwin, ('init',))
|
|
70
|
+
assert repo.init.called
|
|
71
|
+
assert repo.init.return_value.index.add.called
|
|
72
|
+
path.home.return_value.__truediv__.assert_any_call('.gitattributes')
|
|
73
|
+
path.home.return_value.__truediv__.assert_any_call('.gitignore')
|
|
74
|
+
assert path.home.return_value.__truediv__.return_value.write_text.call_count == 2
|
|
75
|
+
which.assert_any_call('jq')
|
|
76
|
+
which.assert_any_call('prettier')
|
|
77
|
+
repo.init.return_value.git.execute.assert_any_call(('git', 'config', 'commit.gpgsign', 'false'))
|
|
78
|
+
repo.init.return_value.git.execute.assert_any_call(
|
|
79
|
+
('git', 'config', 'diff.json.textconv', '"jq" -MS .'))
|
|
80
|
+
repo.init.return_value.git.execute.assert_any_call(
|
|
81
|
+
('git', 'config', 'diff.yaml.textconv', '"prettier" --no-editorconfig --parser yaml'))
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from baldwin.main import baldwin
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from click.testing import CliRunner
|
|
9
|
+
from pytest_mock import MockerFixture
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_install_units_no_bw(runner: CliRunner, mocker: MockerFixture) -> None:
|
|
13
|
+
mocker.patch('baldwin.lib.which').return_value = None
|
|
14
|
+
run = runner.invoke(baldwin, ('install-units',))
|
|
15
|
+
assert run.exit_code != 0
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_install_units(runner: CliRunner, mocker: MockerFixture) -> None:
|
|
19
|
+
path = mocker.patch('baldwin.lib.Path')
|
|
20
|
+
mocker.patch('baldwin.lib.sp.run')
|
|
21
|
+
runner.invoke(baldwin, ('install-units',))
|
|
22
|
+
service_content = path.return_value.expanduser.return_value.write_text.mock_calls[0].args[0]
|
|
23
|
+
assert 'Type=oneshot' in service_content
|
|
24
|
+
assert 'ExecStart=' in service_content
|
|
25
|
+
timer_content = path.return_value.expanduser.return_value.write_text.mock_calls[1].args[0]
|
|
26
|
+
assert 'OnCalendar=' in timer_content
|
|
27
|
+
assert 'WantedBy=timers.target' in timer_content
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|