Metadata-Version: 2.3
Name: edwh
Version: 1.14.2
Summary: Education Warehouse maintenance tools
Keywords: 
Author: Remco Boerma, Robin van der Noord, Romy Schöller, Sven Keimpema
Author-email: Remco Boerma <remco.b@educationwarehouse.nl>, Robin van der Noord <robin.vdn@educationwarehouse.nl>, Romy Schöller <romy.s@educationwarehouse.nl>, Sven Keimpema <sven.k@educationwarehouse.nl>
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Requires-Dist: anyio<5
Requires-Dist: click<9
Requires-Dist: diceware<2
Requires-Dist: ewok>=0.5.3,<1
Requires-Dist: humanize<5
Requires-Dist: more-itertools
Requires-Dist: docstring-parser<1
Requires-Dist: keyring<26
Requires-Dist: packaging
Requires-Dist: plumbum<2
Requires-Dist: python-dateutil<3
Requires-Dist: python-dotenv<2
Requires-Dist: pyyaml<7
Requires-Dist: rapidfuzz<4
Requires-Dist: requests<3
Requires-Dist: ruff==0.16.3
Requires-Dist: ssh-agent-keyring>=0.2,<1
Requires-Dist: tabulate>=0.9,<0.10
Requires-Dist: termcolor<3
Requires-Dist: ty==0.0.71
Requires-Dist: threadful>=0.5,<1
Requires-Dist: tomlkit<1
Requires-Dist: tqdm<5
Requires-Dist: typing-extensions<5
Requires-Dist: uv<1
Requires-Dist: yayarl<3
Requires-Dist: edwh-b2-plugin ; extra == 'b2'
Requires-Dist: edwh-bundler-plugin ; extra == 'bundle'
Requires-Dist: edwh-bundler-plugin ; extra == 'bundler'
Requires-Dist: hatch ; extra == 'dev'
Requires-Dist: pytest ; extra == 'dev'
Requires-Dist: pytest-cov ; extra == 'dev'
Requires-Dist: types-requests ; extra == 'dev'
Requires-Dist: types-python-dateutil ; extra == 'dev'
Requires-Dist: types-redis ; extra == 'dev'
Requires-Dist: types-tabulate ; extra == 'dev'
Requires-Dist: vommit>=0.2.0,<1 ; extra == 'dev'
Requires-Dist: edwh-devdb-plugin ; extra == 'devdb'
Requires-Dist: edwh-files-plugin ; extra == 'files'
Requires-Dist: edwh-git-plugin ; extra == 'git'
Requires-Dist: edwh-locust-plugin ; extra == 'locust'
Requires-Dist: edwh-multipass-plugin ; extra == 'multipass'
Requires-Dist: edwh-passbolt-plugin ; extra == 'passbolt'
Requires-Dist: edwh-pipcompile-plugin ; extra == 'pip'
Requires-Dist: edwh-pipcompile-plugin ; extra == 'pip-compile'
Requires-Dist: edwh-b2-plugin>=0.2.5 ; extra == 'plugins'
Requires-Dist: edwh-bundler-plugin ; extra == 'plugins'
Requires-Dist: edwh-demo-plugin ; extra == 'plugins'
Requires-Dist: edwh-devdb-plugin ; extra == 'plugins'
Requires-Dist: edwh-files-plugin ; extra == 'plugins'
Requires-Dist: edwh-git-plugin ; extra == 'plugins'
Requires-Dist: edwh-locust-plugin ; extra == 'plugins'
Requires-Dist: edwh-multipass-plugin ; extra == 'plugins'
Requires-Dist: edwh-passbolt-plugin ; extra == 'plugins'
Requires-Dist: edwh-pipcompile-plugin ; extra == 'plugins'
Requires-Dist: edwh-restic-plugin ; extra == 'plugins'
Requires-Dist: edwh-server-provisioning-plugin ; extra == 'plugins'
Requires-Dist: edwh-sshfs-plugin ; extra == 'plugins'
Requires-Dist: edwh-sshkey-plugin ; extra == 'plugins'
Requires-Dist: edwh-uptime-plugin ; extra == 'plugins'
Requires-Dist: edwh-whitelabel-plugin ; extra == 'plugins'
Requires-Dist: vommit ; extra == 'plugins'
Requires-Dist: edwh-restic-plugin ; extra == 'restic'
Requires-Dist: edwh-b2-plugin>=0.2.5 ; extra == 'server-plugins'
Requires-Dist: edwh-bundler-plugin ; extra == 'server-plugins'
Requires-Dist: edwh-devdb-plugin ; extra == 'server-plugins'
Requires-Dist: edwh-files-plugin ; extra == 'server-plugins'
Requires-Dist: edwh-pipcompile-plugin ; extra == 'server-plugins'
Requires-Dist: edwh-restic-plugin ; extra == 'server-plugins'
Requires-Dist: edwh-uptime-plugin ; extra == 'server-plugins'
Requires-Dist: edwh-server-provisioning-plugin ; extra == 'server-provisioning'
Requires-Dist: edwh-sshfs-plugin ; extra == 'sshfs'
Requires-Dist: edwh-sshkey-plugin ; extra == 'sshkey'
Requires-Dist: pytest ; extra == 'test'
Requires-Dist: pytest-cov ; extra == 'test'
Requires-Dist: edwh-uptime-plugin ; extra == 'uptime'
Requires-Dist: vommit>=0.2.0,<1 ; extra == 'vommit'
Requires-Dist: edwh-whitelabel-plugin ; extra == 'whitelabel'
Requires-Python: >=3.12
Project-URL: Documentation, https://github.com/educationwarehouse/edwh#readme
Project-URL: Issues, https://github.com/educationwarehouse/edwh/issues
Project-URL: Source, https://github.com/educationwarehouse/edwh
Provides-Extra: b2
Provides-Extra: bundle
Provides-Extra: bundler
Provides-Extra: dev
Provides-Extra: devdb
Provides-Extra: files
Provides-Extra: git
Provides-Extra: locust
Provides-Extra: multipass
Provides-Extra: omgeving
Provides-Extra: passbolt
Provides-Extra: pip
Provides-Extra: pip-compile
Provides-Extra: plugins
Provides-Extra: restic
Provides-Extra: server-plugins
Provides-Extra: server-provisioning
Provides-Extra: sshfs
Provides-Extra: sshkey
Provides-Extra: test
Provides-Extra: uptime
Provides-Extra: vommit
Provides-Extra: whitelabel
Description-Content-Type: text/markdown

# edwh

[![PyPI - Version](https://img.shields.io/pypi/v/edwh.svg)](https://pypi.org/project/edwh)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/edwh.svg)](https://pypi.org/project/edwh)

-----

**Table of Contents**

- [Installation](#installation)
- [Usage](#usage)
- [Sudo authentication](#sudo-authentication)
- [Linting](#linting)
- [Testing](#testing)
- [Plugins](#plugins)
- [License](#license)
- [Changelog](#changelog)

## Installation

```console
pipx install edwh
# or: uvenv install edwh

# or with all plugins:
pipx install[plugins]
# or with specific plugins: 
pipx install[multipass,restic]

# managing plugins later:
edwh plugins
edwh plugin.add multipass
edwh plugin.remove multipass
```

## Usage

```console
# to see all available commands:
ew # or `edwh`
# to see help about a specific namespace:
ew help <namespace> # e.g. `ew help plugin`
# to see help about a specific command:
ew help <command> # e.g. `ew help plugin.list` 
```

## Sudo authentication

`edwh sudo` verifies your sudo password and safely stores it temporarily so commands that require sudo can run without
prompting again.

By default, EDWH uses the operating system keyring (for example, GNOME Secret Service on Linux). This is the preferred
backend when the desktop session and its keyring are available.

When the system keyring is locked, `edwh sudo` offers an SSH-agent fallback. If you accept, EDWH lists the public keys
available through `ssh-add -L`; select the key whose agent should unlock the encrypted EDWH keyring. The selection is
recorded in `~/.config/ssh-agent-keyring/config.json`, the SSH-agent backend's documented configuration file, and later
EDWH commands automatically use that backend.

The SSH-agent backend requires a reachable agent, `SSH_AUTH_SOCK`, and the selected key loaded in that agent. For a
remote development machine, connect with agent forwarding (`ssh -A` or `ForwardAgent yes`). The sudo password remains
encrypted on disk; access depends on the selected SSH agent rather than a second keyring password.

## Linting

`edwh lint` runs Ruff and Ty by default. Disable either tool for a specific project in its `pyproject.toml`:

```toml
[tool.edwh.lint]
ruff = false
ty = false
```

`edwh fmt` sorts imports and reformats Python code with Ruff.

## Releasing plugins

`edwh plugin.release` warns when a project still uses Hatchling or when its
`--hatch` build fallback is selected. It also warns when a project's `uv_build`
requirement no longer matches edwh's current recommendation. Silence any of
these warnings for a project:

```toml
[tool.edwh.release]
warn-hatchling = false
warn-hatch-build = false
warn-uv-build = false
```

## Testing

`edwh test.run` runs pytest with coverage by default. It prefers `./venv/bin/pytest`, falls back to
`pytest` from `PATH`, and requires `pytest-cov` in the selected environment.

```console
edwh test.run
edwh test.run -k test_specific_behavior
edwh test.run --html
edwh test.run --no-coverage
```

If a project provides its own `test.tasks.py`, edwh warns about the override and that local `test.run`
replaces the built-in task.

## Task Load Order

Commands are loaded in the following order:

1. **EDWH Package**:
    - Loaded into the global namespace and its own namespaces (like `ew plugins.`).

2. **Plugins**:
    - Loaded into their own namespaces (like `ew mp.`).

3. **Current Directory**:
    - Loaded into the `local.` namespace. If it doesn't exist, it traverses up the directory tree
    - (e.g., `../tasks.py`, `../../tasks.py`).

4. **Other Local Tasks**:
    - Other local tasks with their own namespace are loaded (e.g., `namespace.tasks.py`) and can be invoked
      with `edwh namespace.command`.

5. **Personal Global Tasks**:
    - Personal global tasks (e.g., `~/.config/edwh/tasks.py`) are also loaded into the **global** namespace, useful for
      shortcuts, custom aliases, etc. (+ `add_alias`).

6. **Personal Namespaced Tasks**:
    - Personal tasks with their own namespace (e.g., `~/.config/edwh/namespace.tasks.py`). Similar to a plugin, but for
      personal use.

## Plugins

### Multipass

- pip name: [`edwh-multipass-plugin`](https://pypi.org/project/edwh-multipass-plugin/)
- github: [`educationwarehouse/edwh-multipass-plugin`](https://github.com/educationwarehouse/edwh-multipass-plugin)
- plugin name: `edwh[multipass]`
- subcommand namespace: `mp`

### Restic

- pip name: [`edwh-restic-plugin`](https://pypi.org/project/edwh-restic-plugin/)
- github: [`educationwarehouse/edwh-restic-plugin`](https://github.com/educationwarehouse/edwh-restic-plugin)
- plugin name: `edwh[restic]`
- subcommand namespace: `restic`

### Pip Compile

- pip name: [`edwh-pipcompile-plugin`](https://pypi.org/project/edwh-pipcompile-plugin/)
- github: [`educationwarehouse/edwh-pipcompile-plugin`](https://github.com/educationwarehouse/edwh-pipcompile-plugin)
- plugin name: `edwh[pip]`
- subcommand namespace: `pip`

### Bundler

- pip name: [`edwh-bundler-plugin`](https://pypi.org/project/edwh-bundler-plugin/)
- github: [`educationwarehouse/edwh-bundler-plugin`](https://github.com/educationwarehouse/edwh-bundler-plugin)
- plugin name: `edwh[bundler]`
- subcommand namespace: `bundle`

### Server Provisioning

- pip name: [`edwh-server-provisioning-plugin`](https://pypi.org/project/edwh-server-provisioning-plugin/)
- github: [`educationwarehouse/server_provisioning`](https://github.com/educationwarehouse/server_provisioning)
- plugin name: `edwh[server-provisioning]`
- subcommand namespace: `remote`

### b2

- pip name: [`edwh-b2-plugin`](https://pypi.org/project/edwh-b2-plugin/)
- github: [`educationwarehouse/edwh-b2-plugin`](https://github.com/educationwarehouse/edwh-b2-plugin)
- plugin name: `edwh[b2]`
- subcommand namespace: `b2`

### Locust

- pip name: [`edwh-locust-plugin`](https://pypi.org/project/edwh-locust-plugin/)
- github: [`educationwarehouse/edwh-locust-plugin`](https://github.com/educationwarehouse/edwh-locust-plugin)
- plugin name: `edwh[locust]`
- subcommand namespace: `locust`

### sshkey

- pip name: [`edwh-sshkey-plugin`](https://pypi.org/project/edwh-sshkey-plugin)
- github: [`educationwarehouse/edwh-sshkey-plugin`](https://github.com/educationwarehouse/edwh-sshkey-plugin)
- plugin name `edwh[sshkey]`
- subcommand namespace `sshkey`

### sshfs

- pip name: [`edwh-sshfs-plugin`](https://pypi.org/project/edwh-sshfs-plugin)
- github: [`educationwarehouse/edwh-sshfs-plugin`](https://github.com/educationwarehouse/edwh-sshfs-plugin)
- plugin name `edwh[sshfs]`
- subcommand namespace `sshfs`

### files

- pip name: [`edwh-files-plugin`](https://pypi.org/project/edwh-files-plugin)
- github: [`educationwarehouse/edwh-files-plugin`](https://github.com/educationwarehouse/edwh-files-plugin)
- plugin name `edwh[files]`
- subcommand namespace `file`

### whitelabel

- pip name: [`edwh-whitelabel-plugin`](https://pypi.org/project/edwh-whitelabel-plugin)
- github: [`educationwarehouse/edwh-whitelabel-plugin`](https://github.com/educationwarehouse/edwh-whitelabel-plugin)
- plugin name `edwh[whitelabel]`
- subcommand namespace `wl`

### devdb

- pip name: [`edwh-devdb-plugin`](https://pypi.org/project/edwh-whitelabel-plugin)
- github: [`educationwarehouse/edwh-devdb-plugin`](https://github.com/educationwarehouse/edwh-whitelabel-plugin)
- plugin name `edwh[devdb]`
- subcommand namespace `devdb`

## Improvements to `@task`

The `edwh.improved_task` decorator enhances the functionality of the standard `@task` decorator from Invoke by
introducing additional features:

- **Flags**: You can now specify custom flags for command line arguments. This allows you to define aliases, rename
  arguments (e.g., using `--json` for an argument named `as_json`), and create custom short flags (e.g., `--exclude` can
  also be represented as `-x`).

- **Hookable**: The improved task supports hooks that allow you to run additional tasks after the main task
  execution. If the `hookable` option is set to `True`, any tasks found across namespaces with the same name will be
  executed in sequence, passing along the context and any provided arguments.

The return value of a hookable task will be available in the context under the key `result`.
Using a dictionary as the return value is recommended, as it allows you to merge the results of multiple cascading tasks.

### Example Usage

```python
from edwh import improved_task as task


@task(flags={"exclude": ["--exclude", "-x"], "as_json": ["--json"]}, hookable=True)
def process_data(ctx, exclude: str, as_json: bool = False):
    # Task implementation here
    return {
        "data": [],
    }


# other plugin (or local tasks.py) can now also specify 'process_data':
@task()
def process_data(ctx, exclude: str):
    # the cascading function can choose whether to include the arguments `exclude` and `as_json` or not.
    # this can be cherry-picked as long as the names match the arguments of the main function.
    print(
        ctx["result"]  # will contain {"data": []}
    )
```

## License

`edwh` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.

## Changelog

[See CHANGELOG.md](CHANGELOG.md)
