Metadata-Version: 2.5
Name: drivecfg
Version: 0.1.0
Summary: Load a shared, private description of local drives and media folders from one TOML file.
Project-URL: Homepage, https://github.com/killett/drivecfg
Project-URL: Repository, https://github.com/killett/drivecfg
Project-URL: Issues, https://github.com/killett/drivecfg/issues
Author-email: Emmy Killett <57272627+killett@users.noreply.github.com>
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: backup,config,drives,toml,xdg
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: System Administrators
Classifier: Operating System :: POSIX
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: System :: Archiving :: Backup
Classifier: Typing :: Typed
Requires-Python: >=3.11
Provides-Extra: dev
Requires-Dist: mypy; extra == 'dev'
Requires-Dist: pytest; extra == 'dev'
Requires-Dist: pytest-cov; extra == 'dev'
Requires-Dist: ruff; extra == 'dev'
Description-Content-Type: text/markdown

# drivecfg

One TOML file describing your local drives, backup destinations, and media
folders — loaded by every tool that needs them, so the list exists exactly once
and never ends up in a public repository.

## Why

Personal backup and media scripts accumulate copies of the same list of drives.
The copies drift, and publishing any one of those scripts publishes the layout of
your machine. `drivecfg` moves that description into a single private file under
`~/.config`, and gives every consumer one validated way to read it.

## Installation

```bash
pip install drivecfg
```

No runtime dependencies. Requires Python 3.11 or newer.

## Configuration

`drivecfg` reads the first of these that is set:

1. an explicit path passed by the calling program (typically `--config PATH`)
2. `$DRIVECFG_CONFIG`
3. `$XDG_CONFIG_HOME/drivecfg/drives.toml`, defaulting to `~/.config/drivecfg/drives.toml`

An explicit path or `$DRIVECFG_CONFIG` that does not exist is an error — never a
silent fallback to a different file.

## The config file

A complete example. Every drive id, directory and folder name below is invented.

```toml
# Required. The only version this release reads; an unknown value is refused
# rather than half-read.
schema_version = 1

# Required, absolute (after ~ expansion). The directory every drive is
# mounted under.
base_dir = "/media/you"

# Each entry is one drive. Only `id` is required.
#   dir         directory name under base_dir       (default: id)
#   backup_dir  directory name of this drive's backup (default: "<dir>_backup")
#   enclosure   free-text grouping label, for your own notes
#   backup      take part in a whole-machine backup run (default: true)
drives = [
  { id = "A", enclosure = "Left bay" },                  # /media/you/A, backup /media/you/A_backup
  { id = "B", enclosure = "Left bay" },
  { id = "C", dir = "scratch", backup_dir = "scratch_bk" },
  { id = "D", backup = false },                          # resolvable, but never backed up
]

# The order a whole-machine backup runs in. Optional; defaults to every
# drive with backup = true, in file order. Tokens are drive ids and
# endpoint names; "*drives" splices in every backed-up drive, in file
# order, and may appear at most once. No token may appear twice.
backup_order = ["mypython", "*drives", "~"]

# Named source/destination pairs. Each side is either drive-relative
# ({ drive, path }, so the pair survives a rename of the drive's mount
# directory) or standalone ({ path }, which must be absolute or
# ~-prefixed). A drive-relative path must stay inside its drive: no
# leading "/", no "..".
[endpoints.mypython]
source = { drive = "A", path = "Documents/code/python" }
dest   = { drive = "C", path = "python_backup" }

[endpoints."~"]
source = { path = "~" }
dest   = { drive = "C", path = "home_backup/you" }

# Media folders, grouped into categories. Each category is an ordered
# list; order is preserved on resolution. `folder` is drive-relative,
# with the same no-"/" / no-".." rule. drive = "*" expands to that folder
# on every drive, in file order.
[media.categories]
Films = [
  { drive = "A", folder = "Films" },
  { drive = "B", folder = "Films" },
]
Downloads = [
  { drive = "A", folder = "A_Downloads" },
  { drive = "B", folder = "Downloads" },
  { drive = "*", folder = "finished_downloads" },
]
```

### Validation

Loading fails closed, and reports every problem at once rather than one per
run. Unknown keys are errors, so a typo like `enclosuer` is caught at load
time instead of being silently ignored. Beyond the per-field rules above,
`drivecfg` refuses configurations that would make an
`rsync --delete-before` run destroy live data: two drives sharing a `dir`
or a `backup_dir`, a drive whose `dir` is another drive's `backup_dir` (or
its own), and an endpoint whose destination resolves onto a drive's mount
directory or onto its own source.

## Usage

```python
from drivecfg import load_config

cfg = load_config()
cfg.drive("A").path            # /media/you/A
cfg.drive("A").backup_path     # /media/you/A_backup
cfg.backup_order               # ordered tokens for a full backup run
cfg.category_paths("Films")    # every configured Films folder, in order
```

## CLI

```bash
drivecfg path        # which config file would be used
drivecfg validate    # report every problem in the file, exit 1 if any
drivecfg show        # the fully resolved layout, wildcards expanded
```

## Licence

Apache-2.0.
