dotman-cli 0.2.9__tar.gz → 0.3.0__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.
- {dotman_cli-0.2.9/src/dotman_cli.egg-info → dotman_cli-0.3.0}/PKG-INFO +152 -1
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/README.md +151 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/pyproject.toml +1 -1
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/cli/__init__.py +2 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/cli/commands/__init__.py +2 -0
- dotman_cli-0.3.0/src/dotman/cli/commands/doctor.py +135 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/core/config.py +15 -1
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/core/exceptions.py +12 -0
- dotman_cli-0.3.0/src/dotman/services/__init__.py +19 -0
- dotman_cli-0.3.0/src/dotman/services/doctor.py +109 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0/src/dotman_cli.egg-info}/PKG-INFO +152 -1
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman_cli.egg-info/SOURCES.txt +2 -0
- dotman_cli-0.2.9/src/dotman/services/__init__.py +0 -8
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/LICENSE +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/setup.cfg +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/__init__.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/cli/commands/absorb.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/cli/commands/clean.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/cli/commands/clone.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/cli/commands/deploy.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/cli/commands/history.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/cli/commands/init.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/cli/commands/pull.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/cli/commands/push.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/cli/commands/repo.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/cli/commands/status.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/cli/commands/watch.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/cli_utils.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/core/__init__.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/core/link_manager.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/core/template_engine.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/main.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/managers/__init__.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/managers/history.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/managers/remote.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/managers/repository.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/managers/watcher.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman/services/hook_executor.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman_cli.egg-info/dependency_links.txt +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman_cli.egg-info/entry_points.txt +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman_cli.egg-info/requires.txt +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.3.0}/src/dotman_cli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dotman-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: One more dot file manager but an interesting and easy to use one.
|
|
5
5
|
Author: Nimit Bhardwaj
|
|
6
6
|
License: MIT
|
|
@@ -165,6 +165,7 @@ packages:
|
|
|
165
165
|
| `dotman rollback [id]` | Rollback a deployment by ID |
|
|
166
166
|
| `dotman repo add <name>` | Register current directory as a repository |
|
|
167
167
|
| `dotman repo list` | List all registered repositories |
|
|
168
|
+
| `dotman doctor [packages]` | Check required executables are present |
|
|
168
169
|
|
|
169
170
|
### Options
|
|
170
171
|
|
|
@@ -221,6 +222,156 @@ packages:
|
|
|
221
222
|
|
|
222
223
|
The `absorb_ignore` field accepts a list of regex patterns that are matched against the full file path. If a file matches any pattern, it will be skipped during absorption.
|
|
223
224
|
|
|
225
|
+
## Doctor Configuration
|
|
226
|
+
|
|
227
|
+
Dotman can check if required executables are present on your system before deploying packages. This helps identify missing dependencies early, preventing partial deployments and confusing errors.
|
|
228
|
+
|
|
229
|
+
### Basic Configuration
|
|
230
|
+
|
|
231
|
+
Define executable requirements in your package configuration:
|
|
232
|
+
|
|
233
|
+
```yaml
|
|
234
|
+
packages:
|
|
235
|
+
nvim:
|
|
236
|
+
depends: []
|
|
237
|
+
files:
|
|
238
|
+
- source: "nvim"
|
|
239
|
+
target: "~/.config/nvim"
|
|
240
|
+
doctor:
|
|
241
|
+
executables:
|
|
242
|
+
- name: nvim
|
|
243
|
+
severity: error
|
|
244
|
+
- name: git
|
|
245
|
+
severity: error
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Severity Levels
|
|
249
|
+
|
|
250
|
+
The `severity` field determines how missing executables are treated:
|
|
251
|
+
|
|
252
|
+
- **`error`**: The executable is required for the package to work. If missing, `dotman deploy` may fail or produce unexpected results. The doctor command returns exit code 1.
|
|
253
|
+
- **`warning`**: The executable is optional or only needed for certain features. If missing, the package may still work with reduced functionality. The doctor command returns exit code 0 but shows a warning.
|
|
254
|
+
|
|
255
|
+
### Example Configuration
|
|
256
|
+
|
|
257
|
+
```yaml
|
|
258
|
+
packages:
|
|
259
|
+
nvim-base:
|
|
260
|
+
description: "Neovim configuration with LazyVim for base"
|
|
261
|
+
files:
|
|
262
|
+
- source: "nvim/base/"
|
|
263
|
+
target: "~/.config/nvim"
|
|
264
|
+
doctor:
|
|
265
|
+
executables:
|
|
266
|
+
- name: nvim
|
|
267
|
+
severity: error
|
|
268
|
+
- name: git
|
|
269
|
+
severity: error
|
|
270
|
+
|
|
271
|
+
opencode:
|
|
272
|
+
description: "OpenCode AI assistant configuration"
|
|
273
|
+
files:
|
|
274
|
+
- source: "opencode"
|
|
275
|
+
target: "~/.config/opencode"
|
|
276
|
+
doctor:
|
|
277
|
+
executables:
|
|
278
|
+
- name: node
|
|
279
|
+
severity: warning
|
|
280
|
+
- name: bun
|
|
281
|
+
severity: warning
|
|
282
|
+
|
|
283
|
+
zsh:
|
|
284
|
+
description: "Zsh Config for the system"
|
|
285
|
+
files:
|
|
286
|
+
- source: "zsh/.zshrc"
|
|
287
|
+
target: "~/.zshrc"
|
|
288
|
+
doctor:
|
|
289
|
+
executables:
|
|
290
|
+
- name: zsh
|
|
291
|
+
severity: error
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Using the Doctor Command
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
# Check all enabled packages
|
|
298
|
+
dotman doctor
|
|
299
|
+
|
|
300
|
+
# Check specific packages
|
|
301
|
+
dotman doctor nvim zsh
|
|
302
|
+
|
|
303
|
+
# Check with custom config directory
|
|
304
|
+
dotman doctor --config-dir ~/.dotfiles
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Example Output
|
|
308
|
+
|
|
309
|
+
```
|
|
310
|
+
$ dotman doctor
|
|
311
|
+
|
|
312
|
+
╭───────────────────────────────────────────────────────────────────────────╮
|
|
313
|
+
│ Doctor Check Results │
|
|
314
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
315
|
+
│ Package: nvim-base │
|
|
316
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
317
|
+
│ Executable Status Severity Path │
|
|
318
|
+
│ nvim ✓ Found error /usr/bin/nvim │
|
|
319
|
+
│ git ✓ Found error /usr/bin/git │
|
|
320
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
321
|
+
│ Package: opencode │
|
|
322
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
323
|
+
│ Executable Status Severity Path │
|
|
324
|
+
│ node ✓ Found warning /usr/bin/node │
|
|
325
|
+
│ bun ✗ Missing warning Not in PATH │
|
|
326
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
327
|
+
│ Package: zsh │
|
|
328
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
329
|
+
│ Executable Status Severity Path │
|
|
330
|
+
│ zsh ✓ Found error /usr/bin/zsh │
|
|
331
|
+
╰───────────────────────────────────────────────────────────────────────────╯
|
|
332
|
+
|
|
333
|
+
Summary: 0 errors, 1 warning, 4 passed
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Exit Codes
|
|
337
|
+
|
|
338
|
+
- **Exit code 0**: No error-severity executables are missing (warnings are acceptable)
|
|
339
|
+
- **Exit code 1**: One or more error-severity executables are missing
|
|
340
|
+
|
|
341
|
+
### How It Works
|
|
342
|
+
|
|
343
|
+
1. The doctor command loads your configuration and resolves the package dependency tree
|
|
344
|
+
2. For each package with `doctor.executables` defined, it checks if each executable exists in your system's PATH
|
|
345
|
+
3. Executable lookup uses `shutil.which()` for cross-platform compatibility (Linux, macOS, Windows)
|
|
346
|
+
4. Results are displayed in a formatted table grouped by package
|
|
347
|
+
5. A summary shows totals for errors, warnings, and passed checks
|
|
348
|
+
|
|
349
|
+
### Packages Without Doctor Config
|
|
350
|
+
|
|
351
|
+
Packages without a `doctor` configuration are still shown in the output with "No executable requirements":
|
|
352
|
+
|
|
353
|
+
```
|
|
354
|
+
$ dotman doctor
|
|
355
|
+
|
|
356
|
+
╭───────────────────────────────────────────────────────────────────────────╮
|
|
357
|
+
│ Doctor Check Results │
|
|
358
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
359
|
+
│ Package: simple-config │
|
|
360
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
361
|
+
│ Executable Status Severity Path │
|
|
362
|
+
│ No executable requirements │
|
|
363
|
+
╰───────────────────────────────────────────────────────────────────────────╯
|
|
364
|
+
|
|
365
|
+
Summary: 0 errors, 0 warnings, 0 passed
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### Best Practices
|
|
369
|
+
|
|
370
|
+
- Set `severity: error` for executables that are strictly required (e.g., `nvim`, `git`)
|
|
371
|
+
- Set `severity: warning` for executables that are optional or only needed for specific features
|
|
372
|
+
- Run `dotman doctor` before `dotman deploy` to catch missing dependencies early
|
|
373
|
+
- Use `--dry-run` with deploy to preview changes after fixing any doctor issues
|
|
374
|
+
|
|
224
375
|
### Overlapping Targets
|
|
225
376
|
|
|
226
377
|
If multiple packages target the same directory, the first package (alphabetically) processes the files, and subsequent packages are skipped with a warning. This ensures predictable behavior:
|
|
@@ -144,6 +144,7 @@ packages:
|
|
|
144
144
|
| `dotman rollback [id]` | Rollback a deployment by ID |
|
|
145
145
|
| `dotman repo add <name>` | Register current directory as a repository |
|
|
146
146
|
| `dotman repo list` | List all registered repositories |
|
|
147
|
+
| `dotman doctor [packages]` | Check required executables are present |
|
|
147
148
|
|
|
148
149
|
### Options
|
|
149
150
|
|
|
@@ -200,6 +201,156 @@ packages:
|
|
|
200
201
|
|
|
201
202
|
The `absorb_ignore` field accepts a list of regex patterns that are matched against the full file path. If a file matches any pattern, it will be skipped during absorption.
|
|
202
203
|
|
|
204
|
+
## Doctor Configuration
|
|
205
|
+
|
|
206
|
+
Dotman can check if required executables are present on your system before deploying packages. This helps identify missing dependencies early, preventing partial deployments and confusing errors.
|
|
207
|
+
|
|
208
|
+
### Basic Configuration
|
|
209
|
+
|
|
210
|
+
Define executable requirements in your package configuration:
|
|
211
|
+
|
|
212
|
+
```yaml
|
|
213
|
+
packages:
|
|
214
|
+
nvim:
|
|
215
|
+
depends: []
|
|
216
|
+
files:
|
|
217
|
+
- source: "nvim"
|
|
218
|
+
target: "~/.config/nvim"
|
|
219
|
+
doctor:
|
|
220
|
+
executables:
|
|
221
|
+
- name: nvim
|
|
222
|
+
severity: error
|
|
223
|
+
- name: git
|
|
224
|
+
severity: error
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Severity Levels
|
|
228
|
+
|
|
229
|
+
The `severity` field determines how missing executables are treated:
|
|
230
|
+
|
|
231
|
+
- **`error`**: The executable is required for the package to work. If missing, `dotman deploy` may fail or produce unexpected results. The doctor command returns exit code 1.
|
|
232
|
+
- **`warning`**: The executable is optional or only needed for certain features. If missing, the package may still work with reduced functionality. The doctor command returns exit code 0 but shows a warning.
|
|
233
|
+
|
|
234
|
+
### Example Configuration
|
|
235
|
+
|
|
236
|
+
```yaml
|
|
237
|
+
packages:
|
|
238
|
+
nvim-base:
|
|
239
|
+
description: "Neovim configuration with LazyVim for base"
|
|
240
|
+
files:
|
|
241
|
+
- source: "nvim/base/"
|
|
242
|
+
target: "~/.config/nvim"
|
|
243
|
+
doctor:
|
|
244
|
+
executables:
|
|
245
|
+
- name: nvim
|
|
246
|
+
severity: error
|
|
247
|
+
- name: git
|
|
248
|
+
severity: error
|
|
249
|
+
|
|
250
|
+
opencode:
|
|
251
|
+
description: "OpenCode AI assistant configuration"
|
|
252
|
+
files:
|
|
253
|
+
- source: "opencode"
|
|
254
|
+
target: "~/.config/opencode"
|
|
255
|
+
doctor:
|
|
256
|
+
executables:
|
|
257
|
+
- name: node
|
|
258
|
+
severity: warning
|
|
259
|
+
- name: bun
|
|
260
|
+
severity: warning
|
|
261
|
+
|
|
262
|
+
zsh:
|
|
263
|
+
description: "Zsh Config for the system"
|
|
264
|
+
files:
|
|
265
|
+
- source: "zsh/.zshrc"
|
|
266
|
+
target: "~/.zshrc"
|
|
267
|
+
doctor:
|
|
268
|
+
executables:
|
|
269
|
+
- name: zsh
|
|
270
|
+
severity: error
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Using the Doctor Command
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
# Check all enabled packages
|
|
277
|
+
dotman doctor
|
|
278
|
+
|
|
279
|
+
# Check specific packages
|
|
280
|
+
dotman doctor nvim zsh
|
|
281
|
+
|
|
282
|
+
# Check with custom config directory
|
|
283
|
+
dotman doctor --config-dir ~/.dotfiles
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Example Output
|
|
287
|
+
|
|
288
|
+
```
|
|
289
|
+
$ dotman doctor
|
|
290
|
+
|
|
291
|
+
╭───────────────────────────────────────────────────────────────────────────╮
|
|
292
|
+
│ Doctor Check Results │
|
|
293
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
294
|
+
│ Package: nvim-base │
|
|
295
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
296
|
+
│ Executable Status Severity Path │
|
|
297
|
+
│ nvim ✓ Found error /usr/bin/nvim │
|
|
298
|
+
│ git ✓ Found error /usr/bin/git │
|
|
299
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
300
|
+
│ Package: opencode │
|
|
301
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
302
|
+
│ Executable Status Severity Path │
|
|
303
|
+
│ node ✓ Found warning /usr/bin/node │
|
|
304
|
+
│ bun ✗ Missing warning Not in PATH │
|
|
305
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
306
|
+
│ Package: zsh │
|
|
307
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
308
|
+
│ Executable Status Severity Path │
|
|
309
|
+
│ zsh ✓ Found error /usr/bin/zsh │
|
|
310
|
+
╰───────────────────────────────────────────────────────────────────────────╯
|
|
311
|
+
|
|
312
|
+
Summary: 0 errors, 1 warning, 4 passed
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Exit Codes
|
|
316
|
+
|
|
317
|
+
- **Exit code 0**: No error-severity executables are missing (warnings are acceptable)
|
|
318
|
+
- **Exit code 1**: One or more error-severity executables are missing
|
|
319
|
+
|
|
320
|
+
### How It Works
|
|
321
|
+
|
|
322
|
+
1. The doctor command loads your configuration and resolves the package dependency tree
|
|
323
|
+
2. For each package with `doctor.executables` defined, it checks if each executable exists in your system's PATH
|
|
324
|
+
3. Executable lookup uses `shutil.which()` for cross-platform compatibility (Linux, macOS, Windows)
|
|
325
|
+
4. Results are displayed in a formatted table grouped by package
|
|
326
|
+
5. A summary shows totals for errors, warnings, and passed checks
|
|
327
|
+
|
|
328
|
+
### Packages Without Doctor Config
|
|
329
|
+
|
|
330
|
+
Packages without a `doctor` configuration are still shown in the output with "No executable requirements":
|
|
331
|
+
|
|
332
|
+
```
|
|
333
|
+
$ dotman doctor
|
|
334
|
+
|
|
335
|
+
╭───────────────────────────────────────────────────────────────────────────╮
|
|
336
|
+
│ Doctor Check Results │
|
|
337
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
338
|
+
│ Package: simple-config │
|
|
339
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
340
|
+
│ Executable Status Severity Path │
|
|
341
|
+
│ No executable requirements │
|
|
342
|
+
╰───────────────────────────────────────────────────────────────────────────╯
|
|
343
|
+
|
|
344
|
+
Summary: 0 errors, 0 warnings, 0 passed
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Best Practices
|
|
348
|
+
|
|
349
|
+
- Set `severity: error` for executables that are strictly required (e.g., `nvim`, `git`)
|
|
350
|
+
- Set `severity: warning` for executables that are optional or only needed for specific features
|
|
351
|
+
- Run `dotman doctor` before `dotman deploy` to catch missing dependencies early
|
|
352
|
+
- Use `--dry-run` with deploy to preview changes after fixing any doctor issues
|
|
353
|
+
|
|
203
354
|
### Overlapping Targets
|
|
204
355
|
|
|
205
356
|
If multiple packages target the same directory, the first package (alphabetically) processes the files, and subsequent packages are skipped with a warning. This ensures predictable behavior:
|
|
@@ -15,7 +15,7 @@ classifiers = [
|
|
|
15
15
|
"Environment :: Console",
|
|
16
16
|
"License :: OSI Approved :: MIT License",
|
|
17
17
|
]
|
|
18
|
-
version = "0.
|
|
18
|
+
version = "0.3.0"
|
|
19
19
|
description = "One more dot file manager but an interesting and easy to use one."
|
|
20
20
|
readme = "README.md"
|
|
21
21
|
requires-python = ">=3.12"
|
|
@@ -5,6 +5,7 @@ from dotman.cli.commands import (
|
|
|
5
5
|
add_repository,
|
|
6
6
|
clone,
|
|
7
7
|
deploy,
|
|
8
|
+
doctor,
|
|
8
9
|
history,
|
|
9
10
|
init,
|
|
10
11
|
list_packages,
|
|
@@ -32,6 +33,7 @@ __all__ = [
|
|
|
32
33
|
"add_repository",
|
|
33
34
|
"app",
|
|
34
35
|
"console",
|
|
36
|
+
"doctor",
|
|
35
37
|
"get_config",
|
|
36
38
|
"get_repository_option",
|
|
37
39
|
"repo_app",
|
|
@@ -5,6 +5,7 @@ from dotman.cli.commands.absorb import absorb_changes
|
|
|
5
5
|
from dotman.cli.commands.clean import clean
|
|
6
6
|
from dotman.cli.commands.clone import clone
|
|
7
7
|
from dotman.cli.commands.deploy import deploy, undeploy
|
|
8
|
+
from dotman.cli.commands.doctor import doctor
|
|
8
9
|
from dotman.cli.commands.init import init
|
|
9
10
|
from dotman.cli.commands.pull import pull
|
|
10
11
|
from dotman.cli.commands.push import push
|
|
@@ -27,6 +28,7 @@ __all__ = [
|
|
|
27
28
|
"clean",
|
|
28
29
|
"clone",
|
|
29
30
|
"deploy",
|
|
31
|
+
"doctor",
|
|
30
32
|
"history",
|
|
31
33
|
"init",
|
|
32
34
|
"list_packages",
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""Doctor command for checking required executables."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Annotated
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
from rich.table import Table
|
|
8
|
+
|
|
9
|
+
from dotman.cli_utils import app, console, get_config
|
|
10
|
+
from dotman.core.exceptions import MissingDependencyError
|
|
11
|
+
from dotman.services import DoctorCheckResult, DoctorCommandResult, ExecutableChecker
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@app.command(name="doctor")
|
|
15
|
+
def doctor(
|
|
16
|
+
packages: Annotated[
|
|
17
|
+
list[str] | None,
|
|
18
|
+
typer.Argument(help="Packages to check (default: all enabled)"),
|
|
19
|
+
] = None,
|
|
20
|
+
config_dir: Annotated[
|
|
21
|
+
Path | None,
|
|
22
|
+
typer.Option("--config-dir", "-c", help="The path of config directory"),
|
|
23
|
+
] = None,
|
|
24
|
+
backup_dir: Annotated[
|
|
25
|
+
str | None,
|
|
26
|
+
typer.Option("--backup-dir", help="Override backup directory"),
|
|
27
|
+
] = None,
|
|
28
|
+
repo_name: Annotated[
|
|
29
|
+
str | None,
|
|
30
|
+
typer.Option("--repo", "-r", help="Repository name"),
|
|
31
|
+
] = None,
|
|
32
|
+
) -> None:
|
|
33
|
+
"""Check if required executables are present in PATH."""
|
|
34
|
+
config = get_config(config_dir, backup_dir, repo_name=repo_name)
|
|
35
|
+
|
|
36
|
+
if not config.is_initialized():
|
|
37
|
+
console.print("[red]Dotman is not initialized. Run 'dotman init' first.[/red]")
|
|
38
|
+
raise typer.Exit(1)
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
packages_to_check = config.get_all_packages_with_dependencies(packages)
|
|
42
|
+
except MissingDependencyError as e:
|
|
43
|
+
console.print(f"[red]Dependency error:[/red] {e}")
|
|
44
|
+
raise typer.Exit(1)
|
|
45
|
+
|
|
46
|
+
if not packages_to_check:
|
|
47
|
+
console.print("[yellow]No packages to check.[/yellow]")
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
checker = ExecutableChecker()
|
|
51
|
+
all_results: list[DoctorCommandResult] = []
|
|
52
|
+
|
|
53
|
+
for pkg_name in packages_to_check:
|
|
54
|
+
pkg = config.get_package(pkg_name)
|
|
55
|
+
if not pkg:
|
|
56
|
+
continue
|
|
57
|
+
|
|
58
|
+
pkg_result = DoctorCommandResult(package_name=pkg_name)
|
|
59
|
+
if pkg.doctor is None or not pkg.doctor.executables:
|
|
60
|
+
all_results.append(pkg_result)
|
|
61
|
+
continue
|
|
62
|
+
|
|
63
|
+
for executable in pkg.doctor.executables:
|
|
64
|
+
found, path = checker.find_executable(executable.name)
|
|
65
|
+
check_result = DoctorCheckResult(
|
|
66
|
+
package_name=pkg_name,
|
|
67
|
+
executable_name=executable.name,
|
|
68
|
+
found=found,
|
|
69
|
+
path=path,
|
|
70
|
+
severity=executable.severity,
|
|
71
|
+
)
|
|
72
|
+
pkg_result.checks.append(check_result)
|
|
73
|
+
|
|
74
|
+
pkg_result.summary = pkg_result.compute_summary()
|
|
75
|
+
all_results.append(pkg_result)
|
|
76
|
+
|
|
77
|
+
table = Table(title="Doctor Check Results")
|
|
78
|
+
table.add_column("Package", style="cyan")
|
|
79
|
+
table.add_column("Executable", style="white")
|
|
80
|
+
table.add_column("Status", style="white")
|
|
81
|
+
table.add_column("Severity", style="white")
|
|
82
|
+
table.add_column("Path", style="dim")
|
|
83
|
+
|
|
84
|
+
for pkg_result in all_results:
|
|
85
|
+
table.add_section()
|
|
86
|
+
table.add_row(
|
|
87
|
+
f"[bold]{pkg_result.package_name}[/bold]",
|
|
88
|
+
"",
|
|
89
|
+
"",
|
|
90
|
+
"",
|
|
91
|
+
"",
|
|
92
|
+
)
|
|
93
|
+
if not pkg_result.checks:
|
|
94
|
+
table.add_row(
|
|
95
|
+
"",
|
|
96
|
+
"[dim]No executable requirements[/dim]",
|
|
97
|
+
"",
|
|
98
|
+
"",
|
|
99
|
+
"",
|
|
100
|
+
)
|
|
101
|
+
else:
|
|
102
|
+
for check in pkg_result.checks:
|
|
103
|
+
if check.found:
|
|
104
|
+
status = "[green]✓ Found[/green]"
|
|
105
|
+
else:
|
|
106
|
+
status = "[red]✗ Missing[/red]"
|
|
107
|
+
|
|
108
|
+
if check.severity == "error":
|
|
109
|
+
severity = "[red]error[/red]"
|
|
110
|
+
else:
|
|
111
|
+
severity = "[yellow]warning[/yellow]"
|
|
112
|
+
|
|
113
|
+
path_str = check.path if check.path else "Not in PATH"
|
|
114
|
+
table.add_row(
|
|
115
|
+
"",
|
|
116
|
+
check.executable_name,
|
|
117
|
+
status,
|
|
118
|
+
severity,
|
|
119
|
+
path_str,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
console.print(table)
|
|
123
|
+
|
|
124
|
+
total_errors = sum(r.summary.get("errors", 0) for r in all_results)
|
|
125
|
+
total_warnings = sum(r.summary.get("warnings", 0) for r in all_results)
|
|
126
|
+
total_passed = sum(r.summary.get("passed", 0) for r in all_results)
|
|
127
|
+
|
|
128
|
+
console.print(
|
|
129
|
+
f"\nSummary: [red]{total_errors} errors[/red],"
|
|
130
|
+
f" [yellow]{total_warnings} warnings[/yellow],"
|
|
131
|
+
f" [green]{total_passed} passed[/green]"
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
if total_errors > 0:
|
|
135
|
+
raise typer.Exit(1)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Configuration management for Dotman."""
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import Any
|
|
4
|
+
from typing import Any, Literal
|
|
5
5
|
|
|
6
6
|
import yaml
|
|
7
7
|
from pydantic import BaseModel, Field
|
|
@@ -33,6 +33,19 @@ class HookConfig(BaseModel):
|
|
|
33
33
|
post_deploy: list[str] = Field(default_factory=list)
|
|
34
34
|
|
|
35
35
|
|
|
36
|
+
class DoctorExecutable(BaseModel):
|
|
37
|
+
"""A required or optional executable for a package."""
|
|
38
|
+
|
|
39
|
+
name: str
|
|
40
|
+
severity: Literal["error", "warning"]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class DoctorConfig(BaseModel):
|
|
44
|
+
"""Configuration for doctor executable checks."""
|
|
45
|
+
|
|
46
|
+
executables: list[DoctorExecutable] = Field(default_factory=list)
|
|
47
|
+
|
|
48
|
+
|
|
36
49
|
class PackageConfig(BaseModel):
|
|
37
50
|
"""Configuration for a single package."""
|
|
38
51
|
|
|
@@ -40,6 +53,7 @@ class PackageConfig(BaseModel):
|
|
|
40
53
|
files: list[FileMapping] = Field(default_factory=list)
|
|
41
54
|
variables: dict[str, Any] = Field(default_factory=dict)
|
|
42
55
|
hooks: HookConfig = Field(default_factory=HookConfig)
|
|
56
|
+
doctor: DoctorConfig | None = None
|
|
43
57
|
|
|
44
58
|
|
|
45
59
|
class GlobalSettings(BaseModel):
|
|
@@ -209,3 +209,15 @@ class NothingToCommitError(RepositoryError):
|
|
|
209
209
|
"""No changes to commit in the repository."""
|
|
210
210
|
|
|
211
211
|
pass
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class DoctorError(DotmanError):
|
|
215
|
+
"""Doctor-related errors."""
|
|
216
|
+
|
|
217
|
+
pass
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class MissingExecutableError(DoctorError):
|
|
221
|
+
"""Required executable is not found in PATH."""
|
|
222
|
+
|
|
223
|
+
pass
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Execution services for Dotman.
|
|
2
|
+
|
|
3
|
+
Provides services for executing shell command hooks during package deployment
|
|
4
|
+
and checking for required executables.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from dotman.services.doctor import (
|
|
8
|
+
DoctorCheckResult,
|
|
9
|
+
DoctorCommandResult,
|
|
10
|
+
ExecutableChecker,
|
|
11
|
+
)
|
|
12
|
+
from dotman.services.hook_executor import HookExecutor
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"DoctorCheckResult",
|
|
16
|
+
"DoctorCommandResult",
|
|
17
|
+
"ExecutableChecker",
|
|
18
|
+
"HookExecutor",
|
|
19
|
+
]
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Executable checking services for the doctor command."""
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DoctorCheckResult(BaseModel):
|
|
10
|
+
"""Result of a single executable check for a package."""
|
|
11
|
+
|
|
12
|
+
package_name: str
|
|
13
|
+
executable_name: str
|
|
14
|
+
found: bool
|
|
15
|
+
path: str | None
|
|
16
|
+
severity: Literal["error", "warning"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class DoctorCommandResult(BaseModel):
|
|
20
|
+
"""Result of the doctor command for a single package."""
|
|
21
|
+
|
|
22
|
+
package_name: str
|
|
23
|
+
checks: list[DoctorCheckResult] = Field(default_factory=list)
|
|
24
|
+
summary: dict[str, int] = Field(default_factory=dict)
|
|
25
|
+
|
|
26
|
+
def compute_summary(self) -> dict[str, int]:
|
|
27
|
+
"""Compute summary statistics from checks.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
A dictionary with totals for 'errors', 'warnings', and 'passed'.
|
|
31
|
+
"""
|
|
32
|
+
errors = 0
|
|
33
|
+
warnings = 0
|
|
34
|
+
passed = 0
|
|
35
|
+
|
|
36
|
+
for check in self.checks:
|
|
37
|
+
if check.found:
|
|
38
|
+
passed += 1
|
|
39
|
+
elif check.severity == "error":
|
|
40
|
+
errors += 1
|
|
41
|
+
else:
|
|
42
|
+
warnings += 1
|
|
43
|
+
|
|
44
|
+
return {"errors": errors, "warnings": warnings, "passed": passed}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ExecutableChecker:
|
|
48
|
+
"""Checks if executables are present in the system PATH."""
|
|
49
|
+
|
|
50
|
+
def __init__(self) -> None:
|
|
51
|
+
"""Initialize the executable checker with an empty cache."""
|
|
52
|
+
self._cache: dict[str, tuple[bool, str | None]] = {}
|
|
53
|
+
|
|
54
|
+
def find_executable(self, name: str) -> tuple[bool, str | None]:
|
|
55
|
+
"""Find an executable in the system PATH.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
name: The name of the executable to find.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
A tuple of (found, path) where:
|
|
62
|
+
- found: True if the executable exists in PATH
|
|
63
|
+
- path: The full path to the executable if found, None otherwise
|
|
64
|
+
"""
|
|
65
|
+
if not name:
|
|
66
|
+
return (False, None)
|
|
67
|
+
|
|
68
|
+
name = name.strip()
|
|
69
|
+
|
|
70
|
+
if not name:
|
|
71
|
+
return (False, None)
|
|
72
|
+
|
|
73
|
+
if name in self._cache:
|
|
74
|
+
return self._cache[name]
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
path: str | None = shutil.which(name)
|
|
78
|
+
if path is not None:
|
|
79
|
+
result: tuple[bool, str | None] = (True, path)
|
|
80
|
+
else:
|
|
81
|
+
result = (False, None)
|
|
82
|
+
except (OSError, TypeError, ValueError):
|
|
83
|
+
result = (False, None)
|
|
84
|
+
|
|
85
|
+
self._cache[name] = result
|
|
86
|
+
return result
|
|
87
|
+
|
|
88
|
+
def find_executables(self, names: list[str]) -> dict[str, tuple[bool, str | None]]:
|
|
89
|
+
"""Find multiple executables in the system PATH.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
names: List of executable names to find.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
A dictionary mapping executable names to (found, path) tuples.
|
|
96
|
+
"""
|
|
97
|
+
return {name: self.find_executable(name) for name in names}
|
|
98
|
+
|
|
99
|
+
def clear_cache(self) -> None:
|
|
100
|
+
"""Clear the executable cache."""
|
|
101
|
+
self._cache.clear()
|
|
102
|
+
|
|
103
|
+
def get_cache_size(self) -> int:
|
|
104
|
+
"""Get the number of cached lookups.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
The number of executables in the cache.
|
|
108
|
+
"""
|
|
109
|
+
return len(self._cache)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dotman-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: One more dot file manager but an interesting and easy to use one.
|
|
5
5
|
Author: Nimit Bhardwaj
|
|
6
6
|
License: MIT
|
|
@@ -165,6 +165,7 @@ packages:
|
|
|
165
165
|
| `dotman rollback [id]` | Rollback a deployment by ID |
|
|
166
166
|
| `dotman repo add <name>` | Register current directory as a repository |
|
|
167
167
|
| `dotman repo list` | List all registered repositories |
|
|
168
|
+
| `dotman doctor [packages]` | Check required executables are present |
|
|
168
169
|
|
|
169
170
|
### Options
|
|
170
171
|
|
|
@@ -221,6 +222,156 @@ packages:
|
|
|
221
222
|
|
|
222
223
|
The `absorb_ignore` field accepts a list of regex patterns that are matched against the full file path. If a file matches any pattern, it will be skipped during absorption.
|
|
223
224
|
|
|
225
|
+
## Doctor Configuration
|
|
226
|
+
|
|
227
|
+
Dotman can check if required executables are present on your system before deploying packages. This helps identify missing dependencies early, preventing partial deployments and confusing errors.
|
|
228
|
+
|
|
229
|
+
### Basic Configuration
|
|
230
|
+
|
|
231
|
+
Define executable requirements in your package configuration:
|
|
232
|
+
|
|
233
|
+
```yaml
|
|
234
|
+
packages:
|
|
235
|
+
nvim:
|
|
236
|
+
depends: []
|
|
237
|
+
files:
|
|
238
|
+
- source: "nvim"
|
|
239
|
+
target: "~/.config/nvim"
|
|
240
|
+
doctor:
|
|
241
|
+
executables:
|
|
242
|
+
- name: nvim
|
|
243
|
+
severity: error
|
|
244
|
+
- name: git
|
|
245
|
+
severity: error
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Severity Levels
|
|
249
|
+
|
|
250
|
+
The `severity` field determines how missing executables are treated:
|
|
251
|
+
|
|
252
|
+
- **`error`**: The executable is required for the package to work. If missing, `dotman deploy` may fail or produce unexpected results. The doctor command returns exit code 1.
|
|
253
|
+
- **`warning`**: The executable is optional or only needed for certain features. If missing, the package may still work with reduced functionality. The doctor command returns exit code 0 but shows a warning.
|
|
254
|
+
|
|
255
|
+
### Example Configuration
|
|
256
|
+
|
|
257
|
+
```yaml
|
|
258
|
+
packages:
|
|
259
|
+
nvim-base:
|
|
260
|
+
description: "Neovim configuration with LazyVim for base"
|
|
261
|
+
files:
|
|
262
|
+
- source: "nvim/base/"
|
|
263
|
+
target: "~/.config/nvim"
|
|
264
|
+
doctor:
|
|
265
|
+
executables:
|
|
266
|
+
- name: nvim
|
|
267
|
+
severity: error
|
|
268
|
+
- name: git
|
|
269
|
+
severity: error
|
|
270
|
+
|
|
271
|
+
opencode:
|
|
272
|
+
description: "OpenCode AI assistant configuration"
|
|
273
|
+
files:
|
|
274
|
+
- source: "opencode"
|
|
275
|
+
target: "~/.config/opencode"
|
|
276
|
+
doctor:
|
|
277
|
+
executables:
|
|
278
|
+
- name: node
|
|
279
|
+
severity: warning
|
|
280
|
+
- name: bun
|
|
281
|
+
severity: warning
|
|
282
|
+
|
|
283
|
+
zsh:
|
|
284
|
+
description: "Zsh Config for the system"
|
|
285
|
+
files:
|
|
286
|
+
- source: "zsh/.zshrc"
|
|
287
|
+
target: "~/.zshrc"
|
|
288
|
+
doctor:
|
|
289
|
+
executables:
|
|
290
|
+
- name: zsh
|
|
291
|
+
severity: error
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Using the Doctor Command
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
# Check all enabled packages
|
|
298
|
+
dotman doctor
|
|
299
|
+
|
|
300
|
+
# Check specific packages
|
|
301
|
+
dotman doctor nvim zsh
|
|
302
|
+
|
|
303
|
+
# Check with custom config directory
|
|
304
|
+
dotman doctor --config-dir ~/.dotfiles
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Example Output
|
|
308
|
+
|
|
309
|
+
```
|
|
310
|
+
$ dotman doctor
|
|
311
|
+
|
|
312
|
+
╭───────────────────────────────────────────────────────────────────────────╮
|
|
313
|
+
│ Doctor Check Results │
|
|
314
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
315
|
+
│ Package: nvim-base │
|
|
316
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
317
|
+
│ Executable Status Severity Path │
|
|
318
|
+
│ nvim ✓ Found error /usr/bin/nvim │
|
|
319
|
+
│ git ✓ Found error /usr/bin/git │
|
|
320
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
321
|
+
│ Package: opencode │
|
|
322
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
323
|
+
│ Executable Status Severity Path │
|
|
324
|
+
│ node ✓ Found warning /usr/bin/node │
|
|
325
|
+
│ bun ✗ Missing warning Not in PATH │
|
|
326
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
327
|
+
│ Package: zsh │
|
|
328
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
329
|
+
│ Executable Status Severity Path │
|
|
330
|
+
│ zsh ✓ Found error /usr/bin/zsh │
|
|
331
|
+
╰───────────────────────────────────────────────────────────────────────────╯
|
|
332
|
+
|
|
333
|
+
Summary: 0 errors, 1 warning, 4 passed
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Exit Codes
|
|
337
|
+
|
|
338
|
+
- **Exit code 0**: No error-severity executables are missing (warnings are acceptable)
|
|
339
|
+
- **Exit code 1**: One or more error-severity executables are missing
|
|
340
|
+
|
|
341
|
+
### How It Works
|
|
342
|
+
|
|
343
|
+
1. The doctor command loads your configuration and resolves the package dependency tree
|
|
344
|
+
2. For each package with `doctor.executables` defined, it checks if each executable exists in your system's PATH
|
|
345
|
+
3. Executable lookup uses `shutil.which()` for cross-platform compatibility (Linux, macOS, Windows)
|
|
346
|
+
4. Results are displayed in a formatted table grouped by package
|
|
347
|
+
5. A summary shows totals for errors, warnings, and passed checks
|
|
348
|
+
|
|
349
|
+
### Packages Without Doctor Config
|
|
350
|
+
|
|
351
|
+
Packages without a `doctor` configuration are still shown in the output with "No executable requirements":
|
|
352
|
+
|
|
353
|
+
```
|
|
354
|
+
$ dotman doctor
|
|
355
|
+
|
|
356
|
+
╭───────────────────────────────────────────────────────────────────────────╮
|
|
357
|
+
│ Doctor Check Results │
|
|
358
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
359
|
+
│ Package: simple-config │
|
|
360
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
361
|
+
│ Executable Status Severity Path │
|
|
362
|
+
│ No executable requirements │
|
|
363
|
+
╰───────────────────────────────────────────────────────────────────────────╯
|
|
364
|
+
|
|
365
|
+
Summary: 0 errors, 0 warnings, 0 passed
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### Best Practices
|
|
369
|
+
|
|
370
|
+
- Set `severity: error` for executables that are strictly required (e.g., `nvim`, `git`)
|
|
371
|
+
- Set `severity: warning` for executables that are optional or only needed for specific features
|
|
372
|
+
- Run `dotman doctor` before `dotman deploy` to catch missing dependencies early
|
|
373
|
+
- Use `--dry-run` with deploy to preview changes after fixing any doctor issues
|
|
374
|
+
|
|
224
375
|
### Overlapping Targets
|
|
225
376
|
|
|
226
377
|
If multiple packages target the same directory, the first package (alphabetically) processes the files, and subsequent packages are skipped with a warning. This ensures predictable behavior:
|
|
@@ -10,6 +10,7 @@ src/dotman/cli/commands/absorb.py
|
|
|
10
10
|
src/dotman/cli/commands/clean.py
|
|
11
11
|
src/dotman/cli/commands/clone.py
|
|
12
12
|
src/dotman/cli/commands/deploy.py
|
|
13
|
+
src/dotman/cli/commands/doctor.py
|
|
13
14
|
src/dotman/cli/commands/history.py
|
|
14
15
|
src/dotman/cli/commands/init.py
|
|
15
16
|
src/dotman/cli/commands/pull.py
|
|
@@ -28,6 +29,7 @@ src/dotman/managers/remote.py
|
|
|
28
29
|
src/dotman/managers/repository.py
|
|
29
30
|
src/dotman/managers/watcher.py
|
|
30
31
|
src/dotman/services/__init__.py
|
|
32
|
+
src/dotman/services/doctor.py
|
|
31
33
|
src/dotman/services/hook_executor.py
|
|
32
34
|
src/dotman_cli.egg-info/PKG-INFO
|
|
33
35
|
src/dotman_cli.egg-info/SOURCES.txt
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|