dotman-cli 0.2.9__tar.gz → 0.4.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.4.0}/PKG-INFO +156 -2
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/README.md +155 -1
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/pyproject.toml +3 -1
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/cli/__init__.py +4 -2
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/cli/commands/__init__.py +4 -0
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/cli/commands/absorb.py +16 -4
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/cli/commands/deploy.py +37 -55
- dotman_cli-0.4.0/src/dotman/cli/commands/diff.py +93 -0
- dotman_cli-0.4.0/src/dotman/cli/commands/doctor.py +135 -0
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/cli/commands/history.py +32 -42
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/cli/commands/init.py +8 -4
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/cli/commands/push.py +1 -5
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/cli/commands/repo.py +4 -3
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/cli/commands/status.py +11 -39
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/cli/commands/watch.py +2 -2
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/cli_utils.py +15 -23
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/core/config.py +68 -5
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/core/exceptions.py +12 -0
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/core/link_manager.py +39 -23
- dotman_cli-0.4.0/src/dotman/main.py +20 -0
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/managers/history.py +2 -5
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/managers/remote.py +16 -1
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/managers/watcher.py +4 -0
- dotman_cli-0.4.0/src/dotman/services/__init__.py +19 -0
- dotman_cli-0.4.0/src/dotman/services/doctor.py +109 -0
- {dotman_cli-0.2.9 → dotman_cli-0.4.0/src/dotman_cli.egg-info}/PKG-INFO +156 -2
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman_cli.egg-info/SOURCES.txt +3 -0
- dotman_cli-0.2.9/src/dotman/main.py +0 -13
- dotman_cli-0.2.9/src/dotman/services/__init__.py +0 -8
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/LICENSE +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/setup.cfg +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/__init__.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/cli/commands/clean.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/cli/commands/clone.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/cli/commands/pull.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/core/__init__.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/core/template_engine.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/managers/__init__.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/managers/repository.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman/services/hook_executor.py +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman_cli.egg-info/dependency_links.txt +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman_cli.egg-info/entry_points.txt +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.4.0}/src/dotman_cli.egg-info/requires.txt +0 -0
- {dotman_cli-0.2.9 → dotman_cli-0.4.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.4.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
|
|
@@ -155,6 +155,7 @@ packages:
|
|
|
155
155
|
| `dotman deploy [packages]` | Deploy dotfiles (create symlinks) |
|
|
156
156
|
| `dotman undeploy [packages]` | Remove deployed symlinks |
|
|
157
157
|
| `dotman status [packages]` | Show status of deployed dotfiles |
|
|
158
|
+
| `dotman diff [packages]` | Diff deployed files against what deploy would write (exit 1 on drift) |
|
|
158
159
|
| `dotman list` | List all available packages |
|
|
159
160
|
| `dotman absorb [packages]` | Absorb unmanaged files from target directories |
|
|
160
161
|
| `dotman watch` | Watch for file changes and deploy automatically |
|
|
@@ -165,10 +166,13 @@ packages:
|
|
|
165
166
|
| `dotman rollback [id]` | Rollback a deployment by ID |
|
|
166
167
|
| `dotman repo add <name>` | Register current directory as a repository |
|
|
167
168
|
| `dotman repo list` | List all registered repositories |
|
|
169
|
+
| `dotman doctor [packages]` | Check required executables are present |
|
|
168
170
|
|
|
169
171
|
### Options
|
|
170
172
|
|
|
171
173
|
- `--config-dir, -c` - Override the config directory (default: `.dotman/` in current directory or `DOTMAN_CONFIG_DIR` env var)
|
|
174
|
+
- `--repo, -r` - Use a registered repository. With no flag and no dotman repo in the current directory, the default registered repository is used, so `dotman status` works from anywhere
|
|
175
|
+
- `--backup-dir` - Override `settings.backup_dir` (relative paths are relative to the repo)
|
|
172
176
|
- `--dry-run, -n` - Preview changes without applying them
|
|
173
177
|
- `--force, -f` - Overwrite existing files (with backup)
|
|
174
178
|
- Specific packages can be passed to commands for targeted operations
|
|
@@ -219,7 +223,157 @@ packages:
|
|
|
219
223
|
- ".git/**" # Ignore git directories
|
|
220
224
|
```
|
|
221
225
|
|
|
222
|
-
|
|
226
|
+
Each `absorb_ignore` entry is tried as a glob against the path relative to the target (and the file name), then as a regex against the full path. If a file matches any pattern, it is skipped during absorption.
|
|
227
|
+
|
|
228
|
+
## Doctor Configuration
|
|
229
|
+
|
|
230
|
+
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.
|
|
231
|
+
|
|
232
|
+
### Basic Configuration
|
|
233
|
+
|
|
234
|
+
Define executable requirements in your package configuration:
|
|
235
|
+
|
|
236
|
+
```yaml
|
|
237
|
+
packages:
|
|
238
|
+
nvim:
|
|
239
|
+
depends: []
|
|
240
|
+
files:
|
|
241
|
+
- source: "nvim"
|
|
242
|
+
target: "~/.config/nvim"
|
|
243
|
+
doctor:
|
|
244
|
+
executables:
|
|
245
|
+
- name: nvim
|
|
246
|
+
severity: error
|
|
247
|
+
- name: git
|
|
248
|
+
severity: error
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Severity Levels
|
|
252
|
+
|
|
253
|
+
The `severity` field determines how missing executables are treated:
|
|
254
|
+
|
|
255
|
+
- **`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.
|
|
256
|
+
- **`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.
|
|
257
|
+
|
|
258
|
+
### Example Configuration
|
|
259
|
+
|
|
260
|
+
```yaml
|
|
261
|
+
packages:
|
|
262
|
+
nvim-base:
|
|
263
|
+
description: "Neovim configuration with LazyVim for base"
|
|
264
|
+
files:
|
|
265
|
+
- source: "nvim/base/"
|
|
266
|
+
target: "~/.config/nvim"
|
|
267
|
+
doctor:
|
|
268
|
+
executables:
|
|
269
|
+
- name: nvim
|
|
270
|
+
severity: error
|
|
271
|
+
- name: git
|
|
272
|
+
severity: error
|
|
273
|
+
|
|
274
|
+
opencode:
|
|
275
|
+
description: "OpenCode AI assistant configuration"
|
|
276
|
+
files:
|
|
277
|
+
- source: "opencode"
|
|
278
|
+
target: "~/.config/opencode"
|
|
279
|
+
doctor:
|
|
280
|
+
executables:
|
|
281
|
+
- name: node
|
|
282
|
+
severity: warning
|
|
283
|
+
- name: bun
|
|
284
|
+
severity: warning
|
|
285
|
+
|
|
286
|
+
zsh:
|
|
287
|
+
description: "Zsh Config for the system"
|
|
288
|
+
files:
|
|
289
|
+
- source: "zsh/.zshrc"
|
|
290
|
+
target: "~/.zshrc"
|
|
291
|
+
doctor:
|
|
292
|
+
executables:
|
|
293
|
+
- name: zsh
|
|
294
|
+
severity: error
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Using the Doctor Command
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
# Check all enabled packages
|
|
301
|
+
dotman doctor
|
|
302
|
+
|
|
303
|
+
# Check specific packages
|
|
304
|
+
dotman doctor nvim zsh
|
|
305
|
+
|
|
306
|
+
# Check with custom config directory
|
|
307
|
+
dotman doctor --config-dir ~/.dotfiles
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Example Output
|
|
311
|
+
|
|
312
|
+
```
|
|
313
|
+
$ dotman doctor
|
|
314
|
+
|
|
315
|
+
╭───────────────────────────────────────────────────────────────────────────╮
|
|
316
|
+
│ Doctor Check Results │
|
|
317
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
318
|
+
│ Package: nvim-base │
|
|
319
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
320
|
+
│ Executable Status Severity Path │
|
|
321
|
+
│ nvim ✓ Found error /usr/bin/nvim │
|
|
322
|
+
│ git ✓ Found error /usr/bin/git │
|
|
323
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
324
|
+
│ Package: opencode │
|
|
325
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
326
|
+
│ Executable Status Severity Path │
|
|
327
|
+
│ node ✓ Found warning /usr/bin/node │
|
|
328
|
+
│ bun ✗ Missing warning Not in PATH │
|
|
329
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
330
|
+
│ Package: zsh │
|
|
331
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
332
|
+
│ Executable Status Severity Path │
|
|
333
|
+
│ zsh ✓ Found error /usr/bin/zsh │
|
|
334
|
+
╰───────────────────────────────────────────────────────────────────────────╯
|
|
335
|
+
|
|
336
|
+
Summary: 0 errors, 1 warning, 4 passed
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Exit Codes
|
|
340
|
+
|
|
341
|
+
- **Exit code 0**: No error-severity executables are missing (warnings are acceptable)
|
|
342
|
+
- **Exit code 1**: One or more error-severity executables are missing
|
|
343
|
+
|
|
344
|
+
### How It Works
|
|
345
|
+
|
|
346
|
+
1. The doctor command loads your configuration and resolves the package dependency tree
|
|
347
|
+
2. For each package with `doctor.executables` defined, it checks if each executable exists in your system's PATH
|
|
348
|
+
3. Executable lookup uses `shutil.which()` for cross-platform compatibility (Linux, macOS, Windows)
|
|
349
|
+
4. Results are displayed in a formatted table grouped by package
|
|
350
|
+
5. A summary shows totals for errors, warnings, and passed checks
|
|
351
|
+
|
|
352
|
+
### Packages Without Doctor Config
|
|
353
|
+
|
|
354
|
+
Packages without a `doctor` configuration are still shown in the output with "No executable requirements":
|
|
355
|
+
|
|
356
|
+
```
|
|
357
|
+
$ dotman doctor
|
|
358
|
+
|
|
359
|
+
╭───────────────────────────────────────────────────────────────────────────╮
|
|
360
|
+
│ Doctor Check Results │
|
|
361
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
362
|
+
│ Package: simple-config │
|
|
363
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
364
|
+
│ Executable Status Severity Path │
|
|
365
|
+
│ No executable requirements │
|
|
366
|
+
╰───────────────────────────────────────────────────────────────────────────╯
|
|
367
|
+
|
|
368
|
+
Summary: 0 errors, 0 warnings, 0 passed
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Best Practices
|
|
372
|
+
|
|
373
|
+
- Set `severity: error` for executables that are strictly required (e.g., `nvim`, `git`)
|
|
374
|
+
- Set `severity: warning` for executables that are optional or only needed for specific features
|
|
375
|
+
- Run `dotman doctor` before `dotman deploy` to catch missing dependencies early
|
|
376
|
+
- Use `--dry-run` with deploy to preview changes after fixing any doctor issues
|
|
223
377
|
|
|
224
378
|
### Overlapping Targets
|
|
225
379
|
|
|
@@ -134,6 +134,7 @@ packages:
|
|
|
134
134
|
| `dotman deploy [packages]` | Deploy dotfiles (create symlinks) |
|
|
135
135
|
| `dotman undeploy [packages]` | Remove deployed symlinks |
|
|
136
136
|
| `dotman status [packages]` | Show status of deployed dotfiles |
|
|
137
|
+
| `dotman diff [packages]` | Diff deployed files against what deploy would write (exit 1 on drift) |
|
|
137
138
|
| `dotman list` | List all available packages |
|
|
138
139
|
| `dotman absorb [packages]` | Absorb unmanaged files from target directories |
|
|
139
140
|
| `dotman watch` | Watch for file changes and deploy automatically |
|
|
@@ -144,10 +145,13 @@ packages:
|
|
|
144
145
|
| `dotman rollback [id]` | Rollback a deployment by ID |
|
|
145
146
|
| `dotman repo add <name>` | Register current directory as a repository |
|
|
146
147
|
| `dotman repo list` | List all registered repositories |
|
|
148
|
+
| `dotman doctor [packages]` | Check required executables are present |
|
|
147
149
|
|
|
148
150
|
### Options
|
|
149
151
|
|
|
150
152
|
- `--config-dir, -c` - Override the config directory (default: `.dotman/` in current directory or `DOTMAN_CONFIG_DIR` env var)
|
|
153
|
+
- `--repo, -r` - Use a registered repository. With no flag and no dotman repo in the current directory, the default registered repository is used, so `dotman status` works from anywhere
|
|
154
|
+
- `--backup-dir` - Override `settings.backup_dir` (relative paths are relative to the repo)
|
|
151
155
|
- `--dry-run, -n` - Preview changes without applying them
|
|
152
156
|
- `--force, -f` - Overwrite existing files (with backup)
|
|
153
157
|
- Specific packages can be passed to commands for targeted operations
|
|
@@ -198,7 +202,157 @@ packages:
|
|
|
198
202
|
- ".git/**" # Ignore git directories
|
|
199
203
|
```
|
|
200
204
|
|
|
201
|
-
|
|
205
|
+
Each `absorb_ignore` entry is tried as a glob against the path relative to the target (and the file name), then as a regex against the full path. If a file matches any pattern, it is skipped during absorption.
|
|
206
|
+
|
|
207
|
+
## Doctor Configuration
|
|
208
|
+
|
|
209
|
+
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.
|
|
210
|
+
|
|
211
|
+
### Basic Configuration
|
|
212
|
+
|
|
213
|
+
Define executable requirements in your package configuration:
|
|
214
|
+
|
|
215
|
+
```yaml
|
|
216
|
+
packages:
|
|
217
|
+
nvim:
|
|
218
|
+
depends: []
|
|
219
|
+
files:
|
|
220
|
+
- source: "nvim"
|
|
221
|
+
target: "~/.config/nvim"
|
|
222
|
+
doctor:
|
|
223
|
+
executables:
|
|
224
|
+
- name: nvim
|
|
225
|
+
severity: error
|
|
226
|
+
- name: git
|
|
227
|
+
severity: error
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Severity Levels
|
|
231
|
+
|
|
232
|
+
The `severity` field determines how missing executables are treated:
|
|
233
|
+
|
|
234
|
+
- **`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.
|
|
235
|
+
- **`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.
|
|
236
|
+
|
|
237
|
+
### Example Configuration
|
|
238
|
+
|
|
239
|
+
```yaml
|
|
240
|
+
packages:
|
|
241
|
+
nvim-base:
|
|
242
|
+
description: "Neovim configuration with LazyVim for base"
|
|
243
|
+
files:
|
|
244
|
+
- source: "nvim/base/"
|
|
245
|
+
target: "~/.config/nvim"
|
|
246
|
+
doctor:
|
|
247
|
+
executables:
|
|
248
|
+
- name: nvim
|
|
249
|
+
severity: error
|
|
250
|
+
- name: git
|
|
251
|
+
severity: error
|
|
252
|
+
|
|
253
|
+
opencode:
|
|
254
|
+
description: "OpenCode AI assistant configuration"
|
|
255
|
+
files:
|
|
256
|
+
- source: "opencode"
|
|
257
|
+
target: "~/.config/opencode"
|
|
258
|
+
doctor:
|
|
259
|
+
executables:
|
|
260
|
+
- name: node
|
|
261
|
+
severity: warning
|
|
262
|
+
- name: bun
|
|
263
|
+
severity: warning
|
|
264
|
+
|
|
265
|
+
zsh:
|
|
266
|
+
description: "Zsh Config for the system"
|
|
267
|
+
files:
|
|
268
|
+
- source: "zsh/.zshrc"
|
|
269
|
+
target: "~/.zshrc"
|
|
270
|
+
doctor:
|
|
271
|
+
executables:
|
|
272
|
+
- name: zsh
|
|
273
|
+
severity: error
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Using the Doctor Command
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
# Check all enabled packages
|
|
280
|
+
dotman doctor
|
|
281
|
+
|
|
282
|
+
# Check specific packages
|
|
283
|
+
dotman doctor nvim zsh
|
|
284
|
+
|
|
285
|
+
# Check with custom config directory
|
|
286
|
+
dotman doctor --config-dir ~/.dotfiles
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Example Output
|
|
290
|
+
|
|
291
|
+
```
|
|
292
|
+
$ dotman doctor
|
|
293
|
+
|
|
294
|
+
╭───────────────────────────────────────────────────────────────────────────╮
|
|
295
|
+
│ Doctor Check Results │
|
|
296
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
297
|
+
│ Package: nvim-base │
|
|
298
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
299
|
+
│ Executable Status Severity Path │
|
|
300
|
+
│ nvim ✓ Found error /usr/bin/nvim │
|
|
301
|
+
│ git ✓ Found error /usr/bin/git │
|
|
302
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
303
|
+
│ Package: opencode │
|
|
304
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
305
|
+
│ Executable Status Severity Path │
|
|
306
|
+
│ node ✓ Found warning /usr/bin/node │
|
|
307
|
+
│ bun ✗ Missing warning Not in PATH │
|
|
308
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
309
|
+
│ Package: zsh │
|
|
310
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
311
|
+
│ Executable Status Severity Path │
|
|
312
|
+
│ zsh ✓ Found error /usr/bin/zsh │
|
|
313
|
+
╰───────────────────────────────────────────────────────────────────────────╯
|
|
314
|
+
|
|
315
|
+
Summary: 0 errors, 1 warning, 4 passed
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Exit Codes
|
|
319
|
+
|
|
320
|
+
- **Exit code 0**: No error-severity executables are missing (warnings are acceptable)
|
|
321
|
+
- **Exit code 1**: One or more error-severity executables are missing
|
|
322
|
+
|
|
323
|
+
### How It Works
|
|
324
|
+
|
|
325
|
+
1. The doctor command loads your configuration and resolves the package dependency tree
|
|
326
|
+
2. For each package with `doctor.executables` defined, it checks if each executable exists in your system's PATH
|
|
327
|
+
3. Executable lookup uses `shutil.which()` for cross-platform compatibility (Linux, macOS, Windows)
|
|
328
|
+
4. Results are displayed in a formatted table grouped by package
|
|
329
|
+
5. A summary shows totals for errors, warnings, and passed checks
|
|
330
|
+
|
|
331
|
+
### Packages Without Doctor Config
|
|
332
|
+
|
|
333
|
+
Packages without a `doctor` configuration are still shown in the output with "No executable requirements":
|
|
334
|
+
|
|
335
|
+
```
|
|
336
|
+
$ dotman doctor
|
|
337
|
+
|
|
338
|
+
╭───────────────────────────────────────────────────────────────────────────╮
|
|
339
|
+
│ Doctor Check Results │
|
|
340
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
341
|
+
│ Package: simple-config │
|
|
342
|
+
├───────────────────────────────────────────────────────────────────────────┤
|
|
343
|
+
│ Executable Status Severity Path │
|
|
344
|
+
│ No executable requirements │
|
|
345
|
+
╰───────────────────────────────────────────────────────────────────────────╯
|
|
346
|
+
|
|
347
|
+
Summary: 0 errors, 0 warnings, 0 passed
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### Best Practices
|
|
351
|
+
|
|
352
|
+
- Set `severity: error` for executables that are strictly required (e.g., `nvim`, `git`)
|
|
353
|
+
- Set `severity: warning` for executables that are optional or only needed for specific features
|
|
354
|
+
- Run `dotman doctor` before `dotman deploy` to catch missing dependencies early
|
|
355
|
+
- Use `--dry-run` with deploy to preview changes after fixing any doctor issues
|
|
202
356
|
|
|
203
357
|
### Overlapping Targets
|
|
204
358
|
|
|
@@ -15,7 +15,7 @@ classifiers = [
|
|
|
15
15
|
"Environment :: Console",
|
|
16
16
|
"License :: OSI Approved :: MIT License",
|
|
17
17
|
]
|
|
18
|
-
version = "0.
|
|
18
|
+
version = "0.4.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"
|
|
@@ -74,6 +74,8 @@ section-order = ["future", "standard-library", "third-party", "first-party", "lo
|
|
|
74
74
|
combine-as-imports = true
|
|
75
75
|
|
|
76
76
|
[tool.mypy]
|
|
77
|
+
# select.kqueue only exists in the darwin stubs; check as darwin everywhere
|
|
78
|
+
platform = "darwin"
|
|
77
79
|
warn_return_any = true
|
|
78
80
|
warn_unused_configs = true
|
|
79
81
|
warn_redundant_casts = true
|
|
@@ -5,6 +5,8 @@ from dotman.cli.commands import (
|
|
|
5
5
|
add_repository,
|
|
6
6
|
clone,
|
|
7
7
|
deploy,
|
|
8
|
+
diff,
|
|
9
|
+
doctor,
|
|
8
10
|
history,
|
|
9
11
|
init,
|
|
10
12
|
list_packages,
|
|
@@ -23,7 +25,6 @@ from dotman.cli_utils import (
|
|
|
23
25
|
app,
|
|
24
26
|
console,
|
|
25
27
|
get_config,
|
|
26
|
-
get_repository_option,
|
|
27
28
|
repo_app,
|
|
28
29
|
)
|
|
29
30
|
|
|
@@ -32,14 +33,15 @@ __all__ = [
|
|
|
32
33
|
"add_repository",
|
|
33
34
|
"app",
|
|
34
35
|
"console",
|
|
36
|
+
"doctor",
|
|
35
37
|
"get_config",
|
|
36
|
-
"get_repository_option",
|
|
37
38
|
"repo_app",
|
|
38
39
|
"init",
|
|
39
40
|
"clone",
|
|
40
41
|
"push",
|
|
41
42
|
"pull",
|
|
42
43
|
"deploy",
|
|
44
|
+
"diff",
|
|
43
45
|
"undeploy",
|
|
44
46
|
"status",
|
|
45
47
|
"list_packages",
|
|
@@ -5,6 +5,8 @@ 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.diff import diff
|
|
9
|
+
from dotman.cli.commands.doctor import doctor
|
|
8
10
|
from dotman.cli.commands.init import init
|
|
9
11
|
from dotman.cli.commands.pull import pull
|
|
10
12
|
from dotman.cli.commands.push import push
|
|
@@ -27,6 +29,8 @@ __all__ = [
|
|
|
27
29
|
"clean",
|
|
28
30
|
"clone",
|
|
29
31
|
"deploy",
|
|
32
|
+
"diff",
|
|
33
|
+
"doctor",
|
|
30
34
|
"history",
|
|
31
35
|
"init",
|
|
32
36
|
"list_packages",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Absorb command for dotman CLI."""
|
|
2
2
|
|
|
3
|
+
import fnmatch
|
|
3
4
|
import re
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from typing import Annotated
|
|
@@ -10,10 +11,21 @@ from dotman.cli_utils import app, console, get_config
|
|
|
10
11
|
from dotman.core.link_manager import LinkManager
|
|
11
12
|
|
|
12
13
|
|
|
14
|
+
def _is_ignored(pattern: str, path: Path, relative: Path) -> bool:
|
|
15
|
+
"""Match a pattern as a glob on the relative path, or a regex on the full path."""
|
|
16
|
+
if fnmatch.fnmatch(str(relative), pattern) or fnmatch.fnmatch(path.name, pattern):
|
|
17
|
+
return True
|
|
18
|
+
try:
|
|
19
|
+
return re.search(pattern, str(path)) is not None
|
|
20
|
+
except re.error:
|
|
21
|
+
return False
|
|
22
|
+
|
|
23
|
+
|
|
13
24
|
def _should_skip_file(
|
|
14
25
|
target_file: Path,
|
|
15
26
|
absorb_ignore: list[str] | None,
|
|
16
27
|
dest_file: Path,
|
|
28
|
+
relative: Path | None = None,
|
|
17
29
|
) -> tuple[bool, str]:
|
|
18
30
|
"""Check if a file should be skipped during absorption.
|
|
19
31
|
|
|
@@ -24,8 +36,9 @@ def _should_skip_file(
|
|
|
24
36
|
return True, "symlink"
|
|
25
37
|
if target_file.name.endswith(".j2"):
|
|
26
38
|
return True, "template"
|
|
39
|
+
relative = relative or Path(target_file.name)
|
|
27
40
|
if absorb_ignore and any(
|
|
28
|
-
|
|
41
|
+
_is_ignored(pattern, target_file, relative) for pattern in absorb_ignore
|
|
29
42
|
):
|
|
30
43
|
return True, "ignored"
|
|
31
44
|
if dest_file.exists():
|
|
@@ -94,9 +107,8 @@ def absorb_changes(
|
|
|
94
107
|
console.print("[red]Dotman is not initialized. Run 'dotman init' first.[/red]")
|
|
95
108
|
raise typer.Exit(1)
|
|
96
109
|
|
|
97
|
-
packages_to_absorb = packages or config.get_enabled_packages()
|
|
98
110
|
# Sort for deterministic behavior (first package wins conflicts)
|
|
99
|
-
packages_to_absorb.
|
|
111
|
+
packages_to_absorb = sorted(packages or config.get_enabled_packages())
|
|
100
112
|
|
|
101
113
|
package_objs = []
|
|
102
114
|
for package_name in packages_to_absorb:
|
|
@@ -138,7 +150,7 @@ def absorb_changes(
|
|
|
138
150
|
dest_file = source / relative_path
|
|
139
151
|
|
|
140
152
|
should_skip, _ = _should_skip_file(
|
|
141
|
-
target_file, absorb_ignore, dest_file
|
|
153
|
+
target_file, absorb_ignore, dest_file, relative_path
|
|
142
154
|
)
|
|
143
155
|
if should_skip:
|
|
144
156
|
continue
|
|
@@ -10,8 +10,6 @@ from dotman.cli_utils import app, console, get_config
|
|
|
10
10
|
from dotman.core.exceptions import (
|
|
11
11
|
DotmanError,
|
|
12
12
|
HookExecutionError,
|
|
13
|
-
LinkExistsError,
|
|
14
|
-
LinkTargetMissingError,
|
|
15
13
|
MissingDependencyError,
|
|
16
14
|
)
|
|
17
15
|
from dotman.core.link_manager import LinkManager, LinkStatus
|
|
@@ -44,17 +42,13 @@ def deploy(
|
|
|
44
42
|
str | None,
|
|
45
43
|
typer.Option("--backup-dir", help="Override backup directory"),
|
|
46
44
|
] = None,
|
|
47
|
-
template_suffix: Annotated[
|
|
48
|
-
str | None,
|
|
49
|
-
typer.Option("--template-suffix", help="Override template suffix"),
|
|
50
|
-
] = None,
|
|
51
45
|
repo_name: Annotated[
|
|
52
46
|
str | None,
|
|
53
47
|
typer.Option("--repo", "-r", help="Repository name"),
|
|
54
48
|
] = None,
|
|
55
49
|
) -> None:
|
|
56
50
|
"""Deploy dotfiles by creating symlinks."""
|
|
57
|
-
config = get_config(config_dir, backup_dir,
|
|
51
|
+
config = get_config(config_dir, backup_dir, repo_name)
|
|
58
52
|
|
|
59
53
|
if repo_name:
|
|
60
54
|
console.print(f"[cyan]Using repository: {repo_name}[/cyan]")
|
|
@@ -86,6 +80,7 @@ def deploy(
|
|
|
86
80
|
|
|
87
81
|
deployment_id = str(uuid.uuid4())[:8]
|
|
88
82
|
deployed_files: list[DeployedFile] = []
|
|
83
|
+
failed = False
|
|
89
84
|
|
|
90
85
|
if dry_run:
|
|
91
86
|
console.print("[cyan]Dry run mode - no changes will be made[/cyan]")
|
|
@@ -126,6 +121,7 @@ def deploy(
|
|
|
126
121
|
f" [yellow]Skipping package '{pkg_name}'...[/yellow]"
|
|
127
122
|
)
|
|
128
123
|
skip_package = True
|
|
124
|
+
failed = True
|
|
129
125
|
break
|
|
130
126
|
|
|
131
127
|
if skip_package:
|
|
@@ -136,57 +132,39 @@ def deploy(
|
|
|
136
132
|
target = Path(file_mapping.target).expanduser()
|
|
137
133
|
|
|
138
134
|
try:
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
console.print(f" [cyan]Would render:[/cyan] {target}")
|
|
135
|
+
results = link_manager.create_link(
|
|
136
|
+
source, target, force, dry_run, template_engine, variables
|
|
137
|
+
)
|
|
138
|
+
except DotmanError as e:
|
|
139
|
+
console.print(f" [red]Error:[/red] {e}")
|
|
140
|
+
failed = True
|
|
141
|
+
continue
|
|
147
142
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
143
|
+
for result in results:
|
|
144
|
+
if result.status != LinkStatus.LINKED:
|
|
145
|
+
continue
|
|
146
|
+
is_template = link_manager.is_template_file(result.source)
|
|
147
|
+
if dry_run:
|
|
148
|
+
console.print(f" [cyan]{result.message}[/cyan]")
|
|
149
|
+
elif is_template:
|
|
150
|
+
console.print(f" [green]Rendered:[/green] {result.target}")
|
|
155
151
|
else:
|
|
156
|
-
|
|
157
|
-
|
|
152
|
+
console.print(
|
|
153
|
+
f" [green]Linked:[/green] {result.target} -> {result.source}"
|
|
154
|
+
)
|
|
155
|
+
if result.backed_up:
|
|
156
|
+
console.print(
|
|
157
|
+
f" [yellow]Backed up to:[/yellow] {result.backed_up}"
|
|
158
158
|
)
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
if result.backed_up:
|
|
169
|
-
console.print(
|
|
170
|
-
f" [yellow]Backed"
|
|
171
|
-
f" up to:[/yellow] {result.backed_up}"
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
deployed_files.append(
|
|
175
|
-
DeployedFile(
|
|
176
|
-
source=str(result.source),
|
|
177
|
-
target=str(result.target),
|
|
178
|
-
is_template=False,
|
|
179
|
-
backup_path=str(result.backed_up)
|
|
180
|
-
if result.backed_up
|
|
181
|
-
else None,
|
|
182
|
-
)
|
|
183
|
-
)
|
|
184
|
-
except LinkExistsError as e:
|
|
185
|
-
console.print(f" [red]Error:[/red] {e}")
|
|
186
|
-
except LinkTargetMissingError as e:
|
|
187
|
-
console.print(f" [red]Error:[/red] {e}")
|
|
188
|
-
except DotmanError as e:
|
|
189
|
-
console.print(f" [red]Error:[/red] {e}")
|
|
159
|
+
|
|
160
|
+
deployed_files.append(
|
|
161
|
+
DeployedFile(
|
|
162
|
+
source=str(result.source),
|
|
163
|
+
target=str(result.target),
|
|
164
|
+
is_template=is_template,
|
|
165
|
+
backup_path=str(result.backed_up) if result.backed_up else None,
|
|
166
|
+
)
|
|
167
|
+
)
|
|
190
168
|
|
|
191
169
|
if pkg.hooks.post_deploy:
|
|
192
170
|
target_dir = None
|
|
@@ -232,6 +210,10 @@ def deploy(
|
|
|
232
210
|
else:
|
|
233
211
|
console.print("\n[green]Deploy complete![/green]")
|
|
234
212
|
|
|
213
|
+
if failed:
|
|
214
|
+
console.print("[red]Some files failed to deploy (see errors above).[/red]")
|
|
215
|
+
raise typer.Exit(1)
|
|
216
|
+
|
|
235
217
|
|
|
236
218
|
@app.command()
|
|
237
219
|
def undeploy(
|