baldwin 0.0.3__tar.gz → 0.0.5__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.

@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 baldwin authors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: baldwin
3
- Version: 0.0.3
3
+ Version: 0.0.5
4
4
  Summary: Simple tracking of your home directory with easy-to-read diffs.
5
5
  Home-page: https://github.com/Tatsh/baldwin
6
6
  License: MIT
@@ -27,6 +27,15 @@ Description-Content-Type: text/markdown
27
27
 
28
28
  # Simple home directory versioning
29
29
 
30
+ [![QA](https://github.com/Tatsh/baldwin/actions/workflows/qa.yml/badge.svg)](https://github.com/Tatsh/baldwin/actions/workflows/qa.yml)
31
+ [![Tests](https://github.com/Tatsh/baldwin/actions/workflows/tests.yml/badge.svg)](https://github.com/Tatsh/baldwin/actions/workflows/tests.yml)
32
+ [![Coverage Status](https://coveralls.io/repos/github/Tatsh/baldwin/badge.svg?branch=master)](https://coveralls.io/github/Tatsh/baldwin?branch=master)
33
+ [![Documentation Status](https://readthedocs.org/projects/baldwin/badge/?version=latest)](https://baldwin.readthedocs.io/en/latest/?badge=latest)
34
+ ![PyPI - Version](https://img.shields.io/pypi/v/baldwin)
35
+ ![GitHub tag (with filter)](https://img.shields.io/github/v/tag/Tatsh/baldwin)
36
+ ![GitHub](https://img.shields.io/github/license/Tatsh/baldwin)
37
+ ![GitHub commits since latest release (by SemVer including pre-releases)](https://img.shields.io/github/commits-since/Tatsh/baldwin/v0.0.5/master)
38
+
30
39
  This is a conversion of my simple scripts to version my home directory with very specific excludes
31
40
  and formatting every file upon commit so that readable diffs can be generated.
32
41
 
@@ -65,8 +74,8 @@ In addition to the `bw` command, `hgit` is a shortcut for `bw git`.
65
74
  bw init
66
75
  ```
67
76
 
68
- Find out where the bare Git directory is by running `bw info`. This can be done even if `init` has
69
- not been run.
77
+ Find out where the Git directory is by running `bw info`. This can be done even if `init` has not
78
+ been run.
70
79
 
71
80
  ### Automation
72
81
 
@@ -1,5 +1,14 @@
1
1
  # Simple home directory versioning
2
2
 
3
+ [![QA](https://github.com/Tatsh/baldwin/actions/workflows/qa.yml/badge.svg)](https://github.com/Tatsh/baldwin/actions/workflows/qa.yml)
4
+ [![Tests](https://github.com/Tatsh/baldwin/actions/workflows/tests.yml/badge.svg)](https://github.com/Tatsh/baldwin/actions/workflows/tests.yml)
5
+ [![Coverage Status](https://coveralls.io/repos/github/Tatsh/baldwin/badge.svg?branch=master)](https://coveralls.io/github/Tatsh/baldwin?branch=master)
6
+ [![Documentation Status](https://readthedocs.org/projects/baldwin/badge/?version=latest)](https://baldwin.readthedocs.io/en/latest/?badge=latest)
7
+ ![PyPI - Version](https://img.shields.io/pypi/v/baldwin)
8
+ ![GitHub tag (with filter)](https://img.shields.io/github/v/tag/Tatsh/baldwin)
9
+ ![GitHub](https://img.shields.io/github/license/Tatsh/baldwin)
10
+ ![GitHub commits since latest release (by SemVer including pre-releases)](https://img.shields.io/github/commits-since/Tatsh/baldwin/v0.0.5/master)
11
+
3
12
  This is a conversion of my simple scripts to version my home directory with very specific excludes
4
13
  and formatting every file upon commit so that readable diffs can be generated.
5
14
 
@@ -38,8 +47,8 @@ In addition to the `bw` command, `hgit` is a shortcut for `bw git`.
38
47
  bw init
39
48
  ```
40
49
 
41
- Find out where the bare Git directory is by running `bw info`. This can be done even if `init` has
42
- not been run.
50
+ Find out where the Git directory is by running `bw info`. This can be done even if `init` has not
51
+ been run.
43
52
 
44
53
  ### Automation
45
54
 
@@ -1,2 +1,2 @@
1
1
  """Manage a home directory with Git."""
2
- __version__ = '0.0.3'
2
+ __version__ = '0.0.5'
@@ -0,0 +1,193 @@
1
+ """Baldwin library."""
2
+ from collections.abc import Iterable
3
+ from dataclasses import dataclass
4
+ from datetime import UTC, datetime
5
+ from importlib import resources
6
+ from itertools import chain
7
+ from pathlib import Path
8
+ from shlex import quote
9
+ from shutil import which
10
+ from typing import Literal
11
+ import logging
12
+ import os
13
+ import subprocess as sp
14
+
15
+ from binaryornot.check import is_binary
16
+ from git import Actor, Repo
17
+ import platformdirs
18
+
19
+ log = logging.getLogger(__name__)
20
+
21
+
22
+ def git(args: Iterable[str]) -> None:
23
+ """Front-end to git with git-dir and work-tree passed."""
24
+ # Pass these arguments because of the hgit shortcut
25
+ cmd = ('git', f'--git-dir={get_git_path()}', f'--work-tree={Path.home()}', *args)
26
+ log.debug('Running: %s', ' '.join(quote(x) for x in cmd))
27
+ sp.run(cmd, check=False) # do not use env= because env vars controlling colour will be lost
28
+
29
+
30
+ def init() -> None:
31
+ """
32
+ Start tracking a home directory.
33
+
34
+ Does nothing if the Git directory already exists.
35
+ """
36
+ if (git_path := get_git_path()).exists():
37
+ return
38
+ repo = Repo.init(git_path, expand_vars=False)
39
+ repo.git.execute(('git', 'config', 'commit.gpgsign', 'false'))
40
+ gitattributes = Path.home() / '.gitattributes'
41
+ gitattributes.write_text(resources.read_text('baldwin.resources', 'default_gitattributes.txt'))
42
+ gitignore = Path.home() / '.gitignore'
43
+ gitignore.write_text(resources.read_text('baldwin.resources', 'default_gitignore.txt'))
44
+ repo.index.add([gitattributes, gitignore])
45
+ if jq := which('jq'):
46
+ repo.git.execute(('git', 'config', 'diff.json.textconv', f'"{jq}" -MS .'))
47
+ repo.git.execute(('git', 'config', 'diff.json.cachetextconv', 'true'))
48
+ if (prettier := which('prettier')):
49
+ node_modules_path = (Path(prettier).resolve(strict=True).parent / '..' /
50
+ '..').resolve(strict=True)
51
+ if (node_modules_path / '@prettier/plugin-xml/src/plugin.js').exists():
52
+ repo.git.execute((
53
+ 'git', 'config', 'diff.xml.textconv',
54
+ f'"{prettier}" --no-editorconfig --parser xml --xml-whitespace-sensitivity ignore'))
55
+ repo.git.execute(('git', 'config', 'diff.xml.cachetextconv', 'true'))
56
+ repo.git.execute(('git', 'config', 'diff.yaml.textconv',
57
+ f'"{prettier}" --no-editorconfig --parser yaml'))
58
+ repo.git.execute(('git', 'config', 'diff.yaml.cachetextconv', 'true'))
59
+
60
+
61
+ def auto_commit() -> None:
62
+ """Automated commit of changed and untracked files."""
63
+ repo = get_repo()
64
+ diff_items = [Path.home() / e.a_path for e in repo.index.diff(None)] # pragma: no cover
65
+ items_to_add = [
66
+ *[p for p in diff_items if p.exists()], *[
67
+ x for x in (Path.home() / y
68
+ for y in repo.untracked_files) if x.is_file() and not is_binary(str(x))
69
+ ]
70
+ ]
71
+ items_to_remove = [p for p in diff_items if not p.exists()] # pragma: no cover
72
+ if items_to_add:
73
+ format_(items_to_add)
74
+ repo.index.add(items_to_add)
75
+ if items_to_remove:
76
+ repo.index.remove(items_to_remove)
77
+ if items_to_add or items_to_remove:
78
+ repo.index.commit(f'Automatic commit @ {datetime.now(tz=UTC).isoformat()}',
79
+ committer=Actor('Auto-commiter', 'hgit@tat.sh'))
80
+
81
+
82
+ @dataclass
83
+ class RepoInfo:
84
+ """General repository information."""
85
+ git_dir_path: Path
86
+ """Git directory."""
87
+ work_tree_path: Path
88
+ """Work tree."""
89
+
90
+
91
+ def repo_info() -> RepoInfo:
92
+ """Get general repository information."""
93
+ return RepoInfo(git_dir_path=get_git_path(), work_tree_path=Path.home())
94
+
95
+
96
+ def install_units() -> None:
97
+ """Install systemd units for automatic committing."""
98
+ bw = which('bw')
99
+ if not bw:
100
+ raise FileNotFoundError
101
+ service_file = Path('~/.config/systemd/user/home-vcs.service').expanduser()
102
+ service_file.write_text(f"""[Unit]
103
+ Description=Home directory VCS commit
104
+
105
+ [Service]
106
+ Type=oneshot
107
+ ExecStart={bw} auto-commit
108
+ """)
109
+ log.debug('Wrote to `%s`.', service_file)
110
+ timer_file = Path('~/.config/systemd/user/home-vcs.timer').expanduser()
111
+ timer_file.write_text("""[Unit]
112
+ Description=Hexahourly trigger for Home directory VCS
113
+
114
+ [Timer]
115
+ OnCalendar=0/6:0:00
116
+
117
+ [Install]
118
+ WantedBy=timers.target
119
+ """)
120
+ log.debug('Wrote to `%s`.', timer_file)
121
+ cmd: tuple[str, ...] = ('systemctl', '--user', 'enable', '--now', 'home-vcs.timer')
122
+ log.debug('Running: %s', ' '.join(quote(x) for x in cmd))
123
+ sp.run(cmd, check=True)
124
+ cmd = ('systemctl', '--user', 'daemon-reload')
125
+ log.debug('Running: %s', ' '.join(quote(x) for x in cmd))
126
+ sp.run(('systemctl', '--user', 'daemon-reload'), check=True)
127
+
128
+
129
+ def get_git_path() -> Path:
130
+ """
131
+ Get the Git directory (``GIT_DIR``).
132
+
133
+ This path is platform-specific. On Windows, the Roaming AppData directory will be used.
134
+ """
135
+ return platformdirs.user_data_path('home-git', roaming=True)
136
+
137
+
138
+ def get_repo() -> Repo:
139
+ """
140
+ Get a :py:class:`git.Repo` object.
141
+
142
+ Also disables GPG signing for the repository.
143
+ """
144
+ repo = Repo(get_git_path(), expand_vars=False)
145
+ repo.git.execute(('git', 'config', 'commit.gpgsign', 'false'))
146
+ return repo
147
+
148
+
149
+ def format_(filenames: Iterable[Path | str] | None = None,
150
+ log_level: Literal['silent', 'error', 'warn', 'log', 'debug'] = 'error') -> None:
151
+ """
152
+ Format untracked and modified files in the repository.
153
+
154
+ Does nothing if Prettier is not in ``PATH``.
155
+
156
+ The following plugins will be detected and enabled if found:
157
+
158
+ * @prettier/plugin-xml
159
+ * prettier-plugin-ini
160
+ * prettier-plugin-sort-json
161
+ * prettier-plugin-toml
162
+ """
163
+ if filenames is None:
164
+ repo = get_repo()
165
+ filenames = (*(Path.home() / d.a_path for d in repo.index.diff(None)),
166
+ *(x for x in (Path.home() / y for y in repo.untracked_files)
167
+ if x.is_file() and not is_binary(str(x))))
168
+ if not (filenames := list(filenames)):
169
+ return
170
+ with resources.path('baldwin.resources', 'prettier.config.json') as config_file:
171
+ if not (prettier := which('prettier')):
172
+ return
173
+ # Detect plugins
174
+ node_modules_path = (Path(prettier).resolve(strict=True).parent / '..' /
175
+ '..').resolve(strict=True)
176
+ cmd_prefix = (prettier, '--config', str(config_file), '--write',
177
+ '--no-error-on-unmatched-pattern', '--ignore-unknown', '--log-level',
178
+ log_level, *chain(*(('--plugin', str(fp))
179
+ for module in ('@prettier/plugin-xml/src/plugin.js',
180
+ 'prettier-plugin-ini/src/plugin.js',
181
+ 'prettier-plugin-sort-json/dist/index.js',
182
+ 'prettier-plugin-toml/lib/index.cjs')
183
+ if (fp := (node_modules_path / module)).exists())))
184
+ for filename in filenames:
185
+ cmd = (*cmd_prefix, str(filename))
186
+ log.debug('Running: %s', ' '.join(quote(x) for x in cmd))
187
+ sp.run(cmd, check=False)
188
+
189
+
190
+ def set_git_env_vars() -> None:
191
+ """Set environment variables for Git."""
192
+ os.environ['GIT_DIR'] = str(get_git_path())
193
+ os.environ['GIT_WORK_TREE'] = str(Path.home())
@@ -0,0 +1,75 @@
1
+ import logging
2
+
3
+ import click
4
+
5
+ from .lib import (
6
+ auto_commit,
7
+ format_,
8
+ git,
9
+ init,
10
+ install_units,
11
+ repo_info,
12
+ set_git_env_vars,
13
+ )
14
+
15
+ log = logging.getLogger(__name__)
16
+
17
+ __all__ = ('baldwin_main', 'git_main')
18
+
19
+
20
+ @click.group(context_settings={'help_option_names': ('-h', '--help')})
21
+ @click.option('-d', '--debug', help='Enable debug logging.', is_flag=True)
22
+ def baldwin_main(*, debug: bool = False) -> None:
23
+ """Manage a home directory with Git."""
24
+ set_git_env_vars()
25
+ logging.basicConfig(level=logging.DEBUG if debug else logging.ERROR)
26
+
27
+
28
+ @click.command(context_settings={
29
+ 'help_option_names': ('-h', '--help'),
30
+ 'ignore_unknown_options': True
31
+ })
32
+ @click.argument('args', nargs=-1, type=click.UNPROCESSED)
33
+ def git_main(args: tuple[str, ...]) -> None:
34
+ """Wrap git with git-dir and work-tree passed."""
35
+ git(args)
36
+
37
+
38
+ @click.command(context_settings={'help_option_names': ('-h', '--help')})
39
+ def init_main() -> None:
40
+ """Start tracking a home directory."""
41
+ init()
42
+
43
+
44
+ @click.command(context_settings={'help_option_names': ('-h', '--help')})
45
+ def auto_commit_main() -> None:
46
+ """Automated commit of changed and untracked files."""
47
+ auto_commit()
48
+
49
+
50
+ @click.command(context_settings={'help_option_names': ('-h', '--help')})
51
+ def format_main() -> None:
52
+ """Format changed and untracked files."""
53
+ format_()
54
+
55
+
56
+ @click.command(context_settings={'help_option_names': ('-h', '--help')})
57
+ def info_main() -> None:
58
+ """Get basic information about the repository."""
59
+ data = repo_info()
60
+ click.echo(f'git-dir path: {data.git_dir_path}')
61
+ click.echo(f'work-tree path: {data.work_tree_path}')
62
+
63
+
64
+ @click.command(context_settings={'help_option_names': ('-h', '--help')})
65
+ def install_units_main() -> None:
66
+ """Install systemd units for automatic committing."""
67
+ install_units()
68
+
69
+
70
+ baldwin_main.add_command(auto_commit_main, 'auto-commit')
71
+ baldwin_main.add_command(format_main, 'format')
72
+ baldwin_main.add_command(git_main, 'git')
73
+ baldwin_main.add_command(info_main, 'info')
74
+ baldwin_main.add_command(init_main, 'init')
75
+ baldwin_main.add_command(install_units_main, 'install-units')
@@ -18,12 +18,12 @@ version_provider = "poetry"
18
18
  [tool.coverage]
19
19
 
20
20
  [tool.coverage.report]
21
- omit = ["conftest.py", "tests/test_*.py"]
21
+ omit = ["tests/conftest.py", "tests/test_*.py"]
22
22
  show_missing = true
23
23
 
24
24
  [tool.coverage.run]
25
25
  branch = true
26
- omit = ["conftest.py", "tests/test_*.py"]
26
+ omit = ["tests/conftest.py", "tests/test_*.py"]
27
27
 
28
28
  [tool.doc8]
29
29
  max-line-length = 100
@@ -57,7 +57,7 @@ license = "MIT"
57
57
  name = "baldwin"
58
58
  readme = "README.md"
59
59
  repository = "https://github.com/Tatsh/baldwin"
60
- version = "0.0.3"
60
+ version = "0.0.5"
61
61
 
62
62
  [tool.poetry.dependencies]
63
63
  binaryornot = "^0.4.4"
@@ -72,10 +72,10 @@ python = ">=3.12,<4"
72
72
  optional = true
73
73
 
74
74
  [tool.poetry.group.dev.dependencies]
75
- binaryornot-stubs = "^0.0.3"
76
- commitizen = ">=3.31,<5.0"
75
+ binaryornot-stubs = "^0"
76
+ commitizen = "^4.0.0"
77
77
  mypy = "^1.13.0"
78
- ruff = ">=0.7.4,<0.9.0"
78
+ ruff = "^0.8.0"
79
79
  yapf = "^0.43.0"
80
80
 
81
81
  [tool.poetry.group.docs]
@@ -1,144 +0,0 @@
1
- from datetime import UTC, datetime
2
- from importlib import resources
3
- from pathlib import Path
4
- from shlex import quote
5
- from shutil import which
6
- import logging
7
- import os
8
- import subprocess as sp
9
-
10
- from binaryornot.check import is_binary
11
- from git import Actor, Repo
12
- import click
13
-
14
- from .utils import format_, get_git_path, get_repo
15
-
16
- log = logging.getLogger(__name__)
17
-
18
- __all__ = ('baldwin_main', 'git')
19
-
20
-
21
- @click.group(context_settings={'help_option_names': ('-h', '--help')})
22
- @click.option('-d', '--debug', help='Enable debug logging.', is_flag=True)
23
- def baldwin_main(*, debug: bool = False) -> None:
24
- """Manage a home directory with Git."""
25
- os.environ['GIT_DIR'] = str(get_git_path())
26
- os.environ['GIT_WORK_TREE'] = str(Path.home())
27
- logging.basicConfig(level=logging.DEBUG if debug else logging.ERROR)
28
-
29
-
30
- @click.command(context_settings={
31
- 'help_option_names': ('-h', '--help'),
32
- 'ignore_unknown_options': True
33
- })
34
- @click.argument('args', nargs=-1, type=click.UNPROCESSED)
35
- def git(args: tuple[str, ...]) -> None:
36
- # Pass these arguments because of the hgit shortcut
37
- cmd = ('git', f'--git-dir={get_git_path()}', f'--work-tree={Path.home()}', *args)
38
- log.debug('Running: %s', ' '.join(quote(x) for x in cmd))
39
- sp.run(cmd, check=False) # do not use env= because env vars controlling colour will be lost
40
-
41
-
42
- @click.command(context_settings={'help_option_names': ('-h', '--help')})
43
- def init() -> None:
44
- """Start tracking a home directory."""
45
- git_path = get_git_path()
46
- if not git_path.exists():
47
- repo = Repo.init(git_path, expand_vars=False)
48
- repo.git.execute(('git', 'config', 'commit.gpgsign', 'false'))
49
- gitattributes = Path.home() / '.gitattributes'
50
- gitattributes.write_text(
51
- resources.read_text('baldwin.resources', 'default_gitattributes.txt'))
52
- gitignore = Path.home() / '.gitignore'
53
- gitignore.write_text(resources.read_text('baldwin.resources', 'default_gitignore.txt'))
54
- repo.index.add([gitattributes, gitignore])
55
- if which('jq'):
56
- repo.git.execute(('git', 'config', 'diff.json.textconv', 'jq -MS .'))
57
- repo.git.execute(('git', 'config', 'diff.json.cachetextconv', 'true'))
58
- if (prettier := which('prettier')):
59
- node_modules_path = (Path(prettier).resolve(strict=True).parent / '..' /
60
- '..').resolve(strict=True)
61
- if (node_modules_path / '@prettier/plugin-xml/src/plugin.js').exists():
62
- repo.git.execute(
63
- ('git', 'config', 'diff.xml.textconv',
64
- 'prettier --no-editorconfig --parser xml --xml-whitespace-sensitivity ignore'))
65
- repo.git.execute(('git', 'config', 'diff.xml.cachetextconv', 'true'))
66
- repo.git.execute(
67
- ('git', 'config', 'diff.yaml.textconv', 'prettier --no-editorconfig --parser yaml'))
68
- repo.git.execute(('git', 'config', 'diff.yaml.cachetextconv', 'true'))
69
-
70
-
71
- @click.command(context_settings={'help_option_names': ('-h', '--help')})
72
- def auto_commit() -> None:
73
- """Automatic commit of changed and untracked files."""
74
- repo = get_repo()
75
- diff_items = repo.index.diff(None)
76
- items_to_add = [
77
- *[p for e in diff_items if (p := Path.home() / e.a_path).exists()], *[
78
- x for x in (Path.home() / y
79
- for y in repo.untracked_files) if x.is_file() and not is_binary(str(x))
80
- ]
81
- ]
82
- format_(items_to_add)
83
- repo.index.add(items_to_add)
84
- repo.index.remove([p for e in diff_items if not (p := Path.home() / e.a_path).exists()])
85
- repo.index.commit(f'Automatic commit @ {datetime.now(tz=UTC).isoformat()}',
86
- committer=Actor('Auto-commiter', 'hgit@tat.sh'))
87
-
88
-
89
- @click.command(context_settings={'help_option_names': ('-h', '--help')})
90
- def format_main() -> None:
91
- """Format changed and untracked files."""
92
- repo = get_repo()
93
- format_(
94
- (*(Path.home() / d.a_path for d in repo.index.diff(None)),
95
- *(x for x in (Path.home() / y
96
- for y in repo.untracked_files) if x.is_file() and not is_binary(str(x)))))
97
-
98
-
99
- @click.command(context_settings={'help_option_names': ('-h', '--help')})
100
- def info() -> None:
101
- """Get basic information about the repository."""
102
- click.echo(f'git-dir path: {get_git_path()}')
103
- click.echo(f'work-tree path: {Path.home()}')
104
-
105
-
106
- @click.command(context_settings={'help_option_names': ('-h', '--help')})
107
- def install_units() -> None:
108
- """Install systemd units for automatic committing."""
109
- bw = which('bw')
110
- assert bw is not None
111
- service_file = Path('~/.config/systemd/user/home-vcs.service').expanduser()
112
- service_file.write_text(f"""[Unit]
113
- Description=Home directory VCS commit
114
-
115
- [Service]
116
- Type=oneshot
117
- ExecStart={bw} auto-commit
118
- """)
119
- log.debug('Wrote to `%s`.', service_file)
120
- timer_file = Path('~/.config/systemd/user/home-vcs.timer').expanduser()
121
- timer_file.write_text("""[Unit]
122
- Description=Hexahourly trigger for Home directory VCS
123
-
124
- [Timer]
125
- OnCalendar=0/6:0:00
126
-
127
- [Install]
128
- WantedBy=timers.target
129
- """)
130
- log.debug('Wrote to `%s`.', timer_file)
131
- cmd: tuple[str, ...] = ('systemctl', '--user', 'enable', '--now', 'home-vcs.timer')
132
- log.debug('Running: %s', ' '.join(quote(x) for x in cmd))
133
- sp.run(cmd, check=True)
134
- cmd = ('systemctl', '--user', 'daemon-reload')
135
- log.debug('Running: %s', ' '.join(quote(x) for x in cmd))
136
- sp.run(('systemctl', '--user', 'daemon-reload'), check=True)
137
-
138
-
139
- baldwin_main.add_command(auto_commit, 'auto-commit')
140
- baldwin_main.add_command(format_main, 'format')
141
- baldwin_main.add_command(git)
142
- baldwin_main.add_command(info)
143
- baldwin_main.add_command(init)
144
- baldwin_main.add_command(install_units)
@@ -1,67 +0,0 @@
1
- from collections.abc import Iterable
2
- from importlib import resources
3
- from itertools import chain
4
- from pathlib import Path
5
- from shlex import quote
6
- from shutil import which
7
- import logging
8
- import subprocess as sp
9
-
10
- from git import Repo
11
- import platformdirs
12
-
13
- __all__ = ('format_', 'get_git_path', 'get_repo')
14
-
15
- log = logging.getLogger(__name__)
16
-
17
-
18
- def get_git_path() -> Path:
19
- """
20
- Get the bare Git directory (``GIT_DIR``).
21
-
22
- This path is platform-specific. On Windows, the Roaming AppData directory will be used.
23
- """
24
- return platformdirs.user_data_path('home-git', roaming=True)
25
-
26
-
27
- def get_repo() -> Repo:
28
- """
29
- Get a :py:class:`git.Repo` object.
30
-
31
- Also disables GPG signing for the repository.
32
- """
33
- repo = Repo(get_git_path(), expand_vars=False)
34
- repo.git.execute(('git', 'config', 'commit.gpgsign', 'false'))
35
- return repo
36
-
37
-
38
- def format_(filenames: Iterable[Path | str], log_level: str = 'error') -> None:
39
- """
40
- Format untracked and modified files in the repository.
41
-
42
- Does nothing if Prettier is not in ``PATH``.
43
-
44
- The following plugins will be detected and enabled if found:
45
-
46
- * @prettier/plugin-xml
47
- * prettier-plugin-ini
48
- * prettier-plugin-sort-json
49
- * prettier-plugin-toml
50
- """
51
- if not (filenames := list(filenames)):
52
- return
53
- with resources.path('baldwin.resources', 'prettier.config.json') as config_file:
54
- if not (prettier := which('prettier')):
55
- return
56
- # Detect plugins
57
- node_modules_path = (Path(prettier).resolve(strict=True).parent / '..' /
58
- '..').resolve(strict=True)
59
- cmd = ('prettier', '--config', str(config_file), '--write',
60
- '--no-error-on-unmatched-pattern', '--ignore-unknown', '--log-level', log_level,
61
- *chain(*(('--plugin', str(fp)) for module in (
62
- '@prettier/plugin-xml/src/plugin.js', 'prettier-plugin-ini/src/plugin.js',
63
- 'prettier-plugin-sort-json/dist/index.js', 'prettier-plugin-toml/lib/index.cjs')
64
- if (fp := (node_modules_path / module)).exists())), *(str(x)
65
- for x in filenames))
66
- log.debug('Running: %s', ' '.join(quote(x) for x in cmd))
67
- sp.run(cmd, check=True)