dotx 2.1.0__tar.gz → 2.2.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.
Files changed (35) hide show
  1. {dotx-2.1.0/src/dotx.egg-info → dotx-2.2.0}/PKG-INFO +106 -51
  2. {dotx-2.1.0 → dotx-2.2.0}/README.md +105 -50
  3. {dotx-2.1.0 → dotx-2.2.0}/pyproject.toml +1 -1
  4. dotx-2.2.0/src/dotx/cli.py +119 -0
  5. dotx-2.2.0/src/dotx/commands/__init__.py +1 -0
  6. dotx-2.2.0/src/dotx/commands/database.py +430 -0
  7. dotx-2.2.0/src/dotx/commands/install_cmd.py +117 -0
  8. dotx-2.2.0/src/dotx/commands/uninstall_cmd.py +90 -0
  9. {dotx-2.1.0 → dotx-2.2.0/src/dotx.egg-info}/PKG-INFO +106 -51
  10. {dotx-2.1.0 → dotx-2.2.0}/src/dotx.egg-info/SOURCES.txt +4 -0
  11. dotx-2.2.0/tests/test_cli.py +248 -0
  12. {dotx-2.1.0 → dotx-2.2.0}/tests/test_cli_database.py +126 -0
  13. dotx-2.1.0/src/dotx/cli.py +0 -622
  14. dotx-2.1.0/tests/test_cli.py +0 -30
  15. {dotx-2.1.0 → dotx-2.2.0}/LICENSE +0 -0
  16. {dotx-2.1.0 → dotx-2.2.0}/MANIFEST.in +0 -0
  17. {dotx-2.1.0 → dotx-2.2.0}/setup.cfg +0 -0
  18. {dotx-2.1.0 → dotx-2.2.0}/src/dotx/__init__.py +0 -0
  19. {dotx-2.1.0 → dotx-2.2.0}/src/dotx/database.py +0 -0
  20. {dotx-2.1.0 → dotx-2.2.0}/src/dotx/ignore.py +0 -0
  21. {dotx-2.1.0 → dotx-2.2.0}/src/dotx/install.py +0 -0
  22. {dotx-2.1.0 → dotx-2.2.0}/src/dotx/installed-schema.sql +0 -0
  23. {dotx-2.1.0 → dotx-2.2.0}/src/dotx/options.py +0 -0
  24. {dotx-2.1.0 → dotx-2.2.0}/src/dotx/plan.py +0 -0
  25. {dotx-2.1.0 → dotx-2.2.0}/src/dotx/uninstall.py +0 -0
  26. {dotx-2.1.0 → dotx-2.2.0}/src/dotx.egg-info/dependency_links.txt +0 -0
  27. {dotx-2.1.0 → dotx-2.2.0}/src/dotx.egg-info/entry_points.txt +0 -0
  28. {dotx-2.1.0 → dotx-2.2.0}/src/dotx.egg-info/requires.txt +0 -0
  29. {dotx-2.1.0 → dotx-2.2.0}/src/dotx.egg-info/top_level.txt +0 -0
  30. {dotx-2.1.0 → dotx-2.2.0}/tests/test_ignore.py +0 -0
  31. {dotx-2.1.0 → dotx-2.2.0}/tests/test_ignore_rules.py +0 -0
  32. {dotx-2.1.0 → dotx-2.2.0}/tests/test_install.py +0 -0
  33. {dotx-2.1.0 → dotx-2.2.0}/tests/test_options.py +0 -0
  34. {dotx-2.1.0 → dotx-2.2.0}/tests/test_plan.py +0 -0
  35. {dotx-2.1.0 → dotx-2.2.0}/tests/test_uninstall.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dotx
3
- Version: 2.1.0
3
+ Version: 2.2.0
4
4
  Summary: A command-line tool to install a link-farm to your dotfiles
5
5
  Author-email: Wolf <Wolf@zv.cx>
6
6
  License-Expression: MIT
@@ -241,15 +241,78 @@ Installations:
241
241
 
242
242
  #### Rebuild database from filesystem
243
243
 
244
- If you have existing dotfile installations and an empty or missing database, use `sync` to rebuild it:
244
+ If you have existing dotfile installations and an empty or missing database, use `sync` to rebuild it.
245
245
 
246
+ ##### The Problem: Unwanted Symlinks
247
+
248
+ By default, `sync` scans your home directory and `~/.config` for **all** symlinks. This can discover symlinks you don't want to track as dotfiles packages, such as:
249
+ - System symlinks in `~/Library/` (macOS)
250
+ - Application symlinks in `/Applications/`
251
+ - IDE or tool-generated symlinks
252
+ - Homebrew-managed symlinks
253
+
254
+ For example, without filtering you might see:
246
255
  ```bash
247
- # Preview what would be added to database
248
256
  +$ dotx sync --dry-run
257
+ ✓ Found 44 symlink(s)
258
+
259
+ Discovered 25 potential package(s):
260
+ /Users/wolf/dotfiles/bash # ✓ Want this
261
+ 5 symlink(s)
262
+ /Users/wolf/dotfiles/vim # ✓ Want this
263
+ 8 symlink(s)
264
+ /Users/wolf/Library # ✗ Don't want this!
265
+ 1 symlink(s)
266
+ /Applications/Raycast.app/... # ✗ Don't want this!
267
+ 1 symlink(s)
268
+ ```
269
+
270
+ ##### The Solution: `--package-root`
249
271
 
250
- # Interactively rebuild database from filesystem
251
- +$ dotx sync
252
- Found 15 symlink(s) in /Users/wolf
272
+ Use `--package-root` to filter packages to **only** those under specific directories. This is **strongly recommended** to avoid tracking unwanted symlinks:
273
+
274
+ ```bash
275
+ # Filter to only your dotfiles directory
276
+ +$ dotx sync --dry-run --package-root ~/dotfiles
277
+ ✓ Found 44 symlink(s)
278
+ Filtered out 6 symlink(s) not under --package-root
279
+
280
+ Discovered 3 potential package(s):
281
+ /Users/wolf/dotfiles/bash
282
+ 5 symlink(s)
283
+ /Users/wolf/dotfiles/vim
284
+ 8 symlink(s)
285
+ /Users/wolf/dotfiles/git
286
+ 2 symlink(s)
287
+ ```
288
+
289
+ You can specify multiple roots if your packages are in different locations:
290
+
291
+ ```bash
292
+ +$ dotx sync --package-root ~/dotfiles --package-root ~/work/configs
293
+ ```
294
+
295
+ ##### Safety Warning
296
+
297
+ If you run `sync` without `--package-root` and have an empty database, you'll see a warning:
298
+
299
+ ```bash
300
+ +$ dotx sync --dry-run
301
+ ✓ Found 44 symlink(s)
302
+ ⚠ Warning: No --package-root specified and database is empty.
303
+ Consider using --package-root to filter packages (e.g., --package-root ~/dotfiles)
304
+ ```
305
+
306
+ ##### Complete Example
307
+
308
+ ```bash
309
+ # Preview what will be synced (recommended first step)
310
+ +$ dotx sync --dry-run --package-root ~/dotfiles
311
+
312
+ # After reviewing, sync for real
313
+ +$ dotx sync --package-root ~/dotfiles
314
+ ✓ Found 15 symlink(s)
315
+ Filtered out 0 symlink(s) not under --package-root
253
316
 
254
317
  Discovered 3 potential package(s):
255
318
  /Users/wolf/dotfiles/bash
@@ -265,66 +328,58 @@ Continue? [y/N]: y
265
328
  ✓ Recorded 15 installation(s) in database.
266
329
  ```
267
330
 
268
- The `sync` command scans your home directory for symlinks and attempts to determine which package they belong to, then rebuilds the database accordingly.
331
+ **Note:** The `sync` command is **additive** - it updates existing entries and adds new ones, but doesn't delete entries for packages not found. This means running sync with `--package-root` won't remove other packages from your database.
269
332
 
270
- ### How it works
333
+ ##### Cleaning Orphaned Entries with `--clean`
271
334
 
272
- *Documentation to be expanded*
335
+ Over time, your database may accumulate **orphaned entries** - records for symlinks that no longer exist on the filesystem. Use `--clean` to remove these automatically, similar to `git fetch --prune`:
273
336
 
274
- ### What's New in v1.1.0
337
+ ```bash
338
+ # Preview what would be cleaned
339
+ +$ dotx sync --dry-run --clean --package-root ~/dotfiles
340
+ ✓ Found 15 symlink(s)
275
341
 
276
- #### Testing and Quality
277
- - **Comprehensive test coverage**: 77.64% overall (up from 53%)
278
- - **21 new database command tests**: Full integration testing for `list`, `verify`, `show`, `sync`
279
- - **Automated coverage tracking**: pytest-cov integration with HTML reports
280
- - **Bug fix**: Database now correctly stores symlink paths instead of resolved paths
342
+ Would clean orphaned entries:
343
+ bash: 2 orphaned entry(ies)
344
+ vim: 1 orphaned entry(ies)
345
+ Would remove 3 orphaned entry(ies).
281
346
 
282
- #### Documentation
283
- - Added Development section with testing, pre-commit hooks, and integration testing docs
284
- - Added clean-up instructions for development artifacts
285
- - Coverage reports available via `open htmlcov/index.html`
347
+ Dry run - no database changes made.
286
348
 
287
- ### What's New in v1.0.0
349
+ # Clean for real
350
+ +$ dotx sync --clean --package-root ~/dotfiles
351
+ ✓ Found 15 symlink(s)
352
+ Filtered out 0 symlink(s) not under --package-root
288
353
 
289
- #### Installation Database
290
- - **SQLite database** tracks which packages installed which files
291
- - **New commands**: `list`, `verify`, `show`, `sync` for package management
292
- - Database location respects `XDG_DATA_HOME` environment variable
293
- - Export reinstall commands with `dotx list --as-commands` for easy migration
354
+ Discovered 3 potential package(s):
355
+ /Users/wolf/dotfiles/bash
356
+ 5 symlink(s)
357
+ ...
294
358
 
295
- #### Improved Ignore System
296
- - **`.dotxignore` files** replace CLI `-i/--ignore` option (breaking change)
297
- - Full gitignore-style pattern syntax support
298
- - Nested `.dotxignore` files in subdirectories
299
- - Global ignore file at `~/.config/dotx/ignore`
300
- - Negation patterns (`!important.conf`)
359
+ This will rebuild the database with the discovered installations.
360
+ Continue? [y/N]: y
301
361
 
302
- #### Quality Improvements
303
- - Pre-commit hooks with ruff, pyrefly, pytest
304
- - Modern Python type annotations throughout
305
- - Better logging with loguru
306
- - Comprehensive code documentation and comments
362
+ Recorded 15 installation(s) in database.
307
363
 
308
- ### Migration from older versions
364
+ Cleaning orphaned entries...
365
+ ✓ Removed 3 orphaned entry(ies).
366
+ ```
309
367
 
310
- **Breaking change in v1.0.0:** The `-i/--ignore` command-line option has been removed in favor of `.dotxignore` files.
368
+ **When to use `--clean`:**
369
+ - After manually removing symlinks
370
+ - After uninstalling packages without using `dotx uninstall`
371
+ - During regular maintenance to keep database accurate
372
+ - When migrating or reorganizing dotfiles
311
373
 
312
- If you were using:
313
- ```bash
314
- dotx --ignore=README.* --ignore=.mypy_cache install bash
315
- ```
374
+ **Important:** `--clean` removes database entries for files that don't exist. Always preview with `--dry-run` first to ensure you're not removing entries you want to keep.
316
375
 
317
- Now create a `.dotxignore` file in your package:
318
- ```bash
319
- +$ cat > bash/.dotxignore <<EOF
320
- README.*
321
- .mypy_cache
322
- EOF
376
+ ### How it works
323
377
 
324
- +$ dotx install bash
325
- ```
378
+ *Documentation to be expanded*
379
+
380
+ ### Changelog
326
381
 
327
- Or use the global ignore file at `~/.config/dotx/ignore` for patterns that apply to all packages.
382
+ See [CHANGELOG.md](CHANGELOG.md) for version history and release notes.
328
383
 
329
384
  ## Development
330
385
 
@@ -213,15 +213,78 @@ Installations:
213
213
 
214
214
  #### Rebuild database from filesystem
215
215
 
216
- If you have existing dotfile installations and an empty or missing database, use `sync` to rebuild it:
216
+ If you have existing dotfile installations and an empty or missing database, use `sync` to rebuild it.
217
217
 
218
+ ##### The Problem: Unwanted Symlinks
219
+
220
+ By default, `sync` scans your home directory and `~/.config` for **all** symlinks. This can discover symlinks you don't want to track as dotfiles packages, such as:
221
+ - System symlinks in `~/Library/` (macOS)
222
+ - Application symlinks in `/Applications/`
223
+ - IDE or tool-generated symlinks
224
+ - Homebrew-managed symlinks
225
+
226
+ For example, without filtering you might see:
218
227
  ```bash
219
- # Preview what would be added to database
220
228
  +$ dotx sync --dry-run
229
+ ✓ Found 44 symlink(s)
230
+
231
+ Discovered 25 potential package(s):
232
+ /Users/wolf/dotfiles/bash # ✓ Want this
233
+ 5 symlink(s)
234
+ /Users/wolf/dotfiles/vim # ✓ Want this
235
+ 8 symlink(s)
236
+ /Users/wolf/Library # ✗ Don't want this!
237
+ 1 symlink(s)
238
+ /Applications/Raycast.app/... # ✗ Don't want this!
239
+ 1 symlink(s)
240
+ ```
241
+
242
+ ##### The Solution: `--package-root`
221
243
 
222
- # Interactively rebuild database from filesystem
223
- +$ dotx sync
224
- Found 15 symlink(s) in /Users/wolf
244
+ Use `--package-root` to filter packages to **only** those under specific directories. This is **strongly recommended** to avoid tracking unwanted symlinks:
245
+
246
+ ```bash
247
+ # Filter to only your dotfiles directory
248
+ +$ dotx sync --dry-run --package-root ~/dotfiles
249
+ ✓ Found 44 symlink(s)
250
+ Filtered out 6 symlink(s) not under --package-root
251
+
252
+ Discovered 3 potential package(s):
253
+ /Users/wolf/dotfiles/bash
254
+ 5 symlink(s)
255
+ /Users/wolf/dotfiles/vim
256
+ 8 symlink(s)
257
+ /Users/wolf/dotfiles/git
258
+ 2 symlink(s)
259
+ ```
260
+
261
+ You can specify multiple roots if your packages are in different locations:
262
+
263
+ ```bash
264
+ +$ dotx sync --package-root ~/dotfiles --package-root ~/work/configs
265
+ ```
266
+
267
+ ##### Safety Warning
268
+
269
+ If you run `sync` without `--package-root` and have an empty database, you'll see a warning:
270
+
271
+ ```bash
272
+ +$ dotx sync --dry-run
273
+ ✓ Found 44 symlink(s)
274
+ ⚠ Warning: No --package-root specified and database is empty.
275
+ Consider using --package-root to filter packages (e.g., --package-root ~/dotfiles)
276
+ ```
277
+
278
+ ##### Complete Example
279
+
280
+ ```bash
281
+ # Preview what will be synced (recommended first step)
282
+ +$ dotx sync --dry-run --package-root ~/dotfiles
283
+
284
+ # After reviewing, sync for real
285
+ +$ dotx sync --package-root ~/dotfiles
286
+ ✓ Found 15 symlink(s)
287
+ Filtered out 0 symlink(s) not under --package-root
225
288
 
226
289
  Discovered 3 potential package(s):
227
290
  /Users/wolf/dotfiles/bash
@@ -237,66 +300,58 @@ Continue? [y/N]: y
237
300
  ✓ Recorded 15 installation(s) in database.
238
301
  ```
239
302
 
240
- The `sync` command scans your home directory for symlinks and attempts to determine which package they belong to, then rebuilds the database accordingly.
303
+ **Note:** The `sync` command is **additive** - it updates existing entries and adds new ones, but doesn't delete entries for packages not found. This means running sync with `--package-root` won't remove other packages from your database.
241
304
 
242
- ### How it works
305
+ ##### Cleaning Orphaned Entries with `--clean`
243
306
 
244
- *Documentation to be expanded*
307
+ Over time, your database may accumulate **orphaned entries** - records for symlinks that no longer exist on the filesystem. Use `--clean` to remove these automatically, similar to `git fetch --prune`:
245
308
 
246
- ### What's New in v1.1.0
309
+ ```bash
310
+ # Preview what would be cleaned
311
+ +$ dotx sync --dry-run --clean --package-root ~/dotfiles
312
+ ✓ Found 15 symlink(s)
247
313
 
248
- #### Testing and Quality
249
- - **Comprehensive test coverage**: 77.64% overall (up from 53%)
250
- - **21 new database command tests**: Full integration testing for `list`, `verify`, `show`, `sync`
251
- - **Automated coverage tracking**: pytest-cov integration with HTML reports
252
- - **Bug fix**: Database now correctly stores symlink paths instead of resolved paths
314
+ Would clean orphaned entries:
315
+ bash: 2 orphaned entry(ies)
316
+ vim: 1 orphaned entry(ies)
317
+ Would remove 3 orphaned entry(ies).
253
318
 
254
- #### Documentation
255
- - Added Development section with testing, pre-commit hooks, and integration testing docs
256
- - Added clean-up instructions for development artifacts
257
- - Coverage reports available via `open htmlcov/index.html`
319
+ Dry run - no database changes made.
258
320
 
259
- ### What's New in v1.0.0
321
+ # Clean for real
322
+ +$ dotx sync --clean --package-root ~/dotfiles
323
+ ✓ Found 15 symlink(s)
324
+ Filtered out 0 symlink(s) not under --package-root
260
325
 
261
- #### Installation Database
262
- - **SQLite database** tracks which packages installed which files
263
- - **New commands**: `list`, `verify`, `show`, `sync` for package management
264
- - Database location respects `XDG_DATA_HOME` environment variable
265
- - Export reinstall commands with `dotx list --as-commands` for easy migration
326
+ Discovered 3 potential package(s):
327
+ /Users/wolf/dotfiles/bash
328
+ 5 symlink(s)
329
+ ...
266
330
 
267
- #### Improved Ignore System
268
- - **`.dotxignore` files** replace CLI `-i/--ignore` option (breaking change)
269
- - Full gitignore-style pattern syntax support
270
- - Nested `.dotxignore` files in subdirectories
271
- - Global ignore file at `~/.config/dotx/ignore`
272
- - Negation patterns (`!important.conf`)
331
+ This will rebuild the database with the discovered installations.
332
+ Continue? [y/N]: y
273
333
 
274
- #### Quality Improvements
275
- - Pre-commit hooks with ruff, pyrefly, pytest
276
- - Modern Python type annotations throughout
277
- - Better logging with loguru
278
- - Comprehensive code documentation and comments
334
+ Recorded 15 installation(s) in database.
279
335
 
280
- ### Migration from older versions
336
+ Cleaning orphaned entries...
337
+ ✓ Removed 3 orphaned entry(ies).
338
+ ```
281
339
 
282
- **Breaking change in v1.0.0:** The `-i/--ignore` command-line option has been removed in favor of `.dotxignore` files.
340
+ **When to use `--clean`:**
341
+ - After manually removing symlinks
342
+ - After uninstalling packages without using `dotx uninstall`
343
+ - During regular maintenance to keep database accurate
344
+ - When migrating or reorganizing dotfiles
283
345
 
284
- If you were using:
285
- ```bash
286
- dotx --ignore=README.* --ignore=.mypy_cache install bash
287
- ```
346
+ **Important:** `--clean` removes database entries for files that don't exist. Always preview with `--dry-run` first to ensure you're not removing entries you want to keep.
288
347
 
289
- Now create a `.dotxignore` file in your package:
290
- ```bash
291
- +$ cat > bash/.dotxignore <<EOF
292
- README.*
293
- .mypy_cache
294
- EOF
348
+ ### How it works
295
349
 
296
- +$ dotx install bash
297
- ```
350
+ *Documentation to be expanded*
351
+
352
+ ### Changelog
298
353
 
299
- Or use the global ignore file at `~/.config/dotx/ignore` for patterns that apply to all packages.
354
+ See [CHANGELOG.md](CHANGELOG.md) for version history and release notes.
300
355
 
301
356
  ## Development
302
357
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dotx"
3
- version = "2.1.0"
3
+ version = "2.2.0"
4
4
  description = "A command-line tool to install a link-farm to your dotfiles"
5
5
  authors = [
6
6
  { name = "Wolf", email = "Wolf@zv.cx" }
@@ -0,0 +1,119 @@
1
+ """
2
+ Command-line interface for dotx.
3
+
4
+ This module defines the CLI app structure and global options. Individual commands
5
+ are defined in the commands subpackage.
6
+ """
7
+
8
+ import sys
9
+ from pathlib import Path
10
+ from typing import Annotated, Optional
11
+
12
+ import click
13
+ import typer
14
+ from loguru import logger
15
+
16
+ from dotx import __version__, __homepage__
17
+ from dotx.options import set_option
18
+
19
+ # Create the Typer app
20
+ app = typer.Typer(
21
+ help="Manage a link farm: (un)install groups of links from source packages.",
22
+ no_args_is_help=True,
23
+ epilog=f"dotx version {__version__} | {__homepage__}",
24
+ )
25
+
26
+
27
+ def version_callback(value: bool):
28
+ """Handle --version flag."""
29
+ if value:
30
+ typer.echo(f"dotx version {__version__}")
31
+ raise typer.Exit()
32
+
33
+
34
+ def configure_logging(debug: bool, verbose: bool, log: Optional[Path]):
35
+ """Configure logging based on CLI options."""
36
+ logger.remove() # Remove default handler
37
+
38
+ # Determine log level
39
+ if debug:
40
+ level = "DEBUG"
41
+ elif verbose:
42
+ level = "INFO"
43
+ else:
44
+ level = "WARNING"
45
+
46
+ # Add handler
47
+ if log:
48
+ logger.add(log, level=level)
49
+ else:
50
+ logger.add(sys.stderr, level=level)
51
+
52
+
53
+ @app.callback()
54
+ def main(
55
+ ctx: click.Context,
56
+ debug: Annotated[
57
+ bool,
58
+ typer.Option("--debug/--no-debug", help="Enable debug logging"),
59
+ ] = False,
60
+ verbose: Annotated[
61
+ bool,
62
+ typer.Option("--verbose/--quiet", help="Enable verbose output / suppress output"),
63
+ ] = False,
64
+ log: Annotated[
65
+ Optional[Path],
66
+ typer.Option(help="Where to write the log (defaults to stderr)"),
67
+ ] = None,
68
+ target: Annotated[
69
+ Optional[Path],
70
+ typer.Option(
71
+ help="Where to install (defaults to $HOME)",
72
+ exists=True,
73
+ file_okay=False,
74
+ dir_okay=True,
75
+ writable=True,
76
+ ),
77
+ ] = None,
78
+ dry_run: Annotated[
79
+ bool,
80
+ typer.Option("--dry-run/--no-dry-run", help="Just echo; don't actually (un)install"),
81
+ ] = False,
82
+ version: Annotated[
83
+ Optional[bool],
84
+ typer.Option("--version", "-V", callback=version_callback, is_eager=True, help="Show version and exit."),
85
+ ] = None,
86
+ ):
87
+ """
88
+ Manage a link farm: (un)install groups of links from source packages.
89
+
90
+ Global options like --debug, --verbose, and --target apply to all commands.
91
+ """
92
+ configure_logging(debug, verbose, log)
93
+
94
+ # Store options in context for commands to access
95
+ ctx.ensure_object(dict)
96
+ if target:
97
+ ctx.obj["TARGET"] = target
98
+ set_option("TARGET", target, ctx)
99
+
100
+ # Set global options
101
+ set_option("DEBUG", debug, ctx)
102
+ set_option("VERBOSE", verbose, ctx)
103
+ set_option("DRYRUN", dry_run, ctx)
104
+
105
+ if log:
106
+ set_option("LOG", log, ctx)
107
+
108
+
109
+ # Register commands from submodules
110
+ from dotx.commands import install_cmd, uninstall_cmd, database
111
+
112
+ install_cmd.register_command(app)
113
+ uninstall_cmd.register_command(app)
114
+ database.register_commands(app)
115
+
116
+
117
+ def cli():
118
+ """Entry point for the CLI."""
119
+ app()
@@ -0,0 +1 @@
1
+ """CLI commands for dotx."""