Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

edwh

PyPI - Version PyPI - Python Version


Table of Contents

Installation

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

# 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:

[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 releases with python-semantic-release, and when the --hatch build fallback is selected. Silence either for a project:

[tool.edwh.release]
warn-psr = false
warn-hatch-build = false

Warnings about the build configuration itself (Hatchling, the uv_build pin, a hatch build release command) come from vommit, which runs the build. Its readme documents them under "Build checks", including how to silence one. When using python-semantic-release they are not reported at all; that prints its deprecation notice instead.

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.

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

To generate both the terminal summary and an HTML report on every run, add this to pyproject.toml:

[tool.edwh.test]
html = true

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

Worktrees

edwh worktree <branch> builds a second, isolated environment for a branch: a git worktree plus the gitignored config, its own ports and hostnames, and a seeded database. edwh worktree.rm <branch> removes it again, containers and volumes included.

edwh worktree.setup                  # configure this project once (writes [worktree] to .toml)
edwh worktree feature/login          # create + start
edwh worktree feature/login --no-up  # create, do not start
edwh worktree.list                   # branches, slugs, paths, ports, running containers
cd $(edwh worktree.path feature/login)
edwh worktree.rm feature/login       # containers, volumes, directory and branch

Worktrees live under ~/.cache/edwh/worktrees/<repo>-<slug>; override with $EDWH_WORKTREE_ROOT or root in the config. The slugified directory name keeps $PROJECT, the compose project and the volume/container prefixes identical.

How it works

worktree copies the .env, deletes the keys that must be unique, and reruns edwh setup --non-interactive so your local.setup regenerates them via next_value / next_available_port. Port allocation also scans git worktree list, so environments find each other even though they are not adjacent directories.

Configuration

[worktree]
copy = [".env", ".toml", "shared_keys/"]   # gitignored paths to carry over
reset = ["*_PORT", "SCHEMA_VERSION"]       # fnmatch globs; deleted so local.setup recomputes them
seed = "clone"                             # fresh | clone | devdb

[worktree.env]
# rewritten instead of regenerated; {value} {repo} {branch} {slug} available. All fields have defaults.
PROJECT = "{repo}-{slug}"
HOSTINGDOMAIN = "{slug}.{value}"

worktree.setup proposes both lists: copy comes from .gitignore, and reset is detected from keys on the host side of a ports: mapping or with differing values across existing checkouts on this machine (identical everywhere means shared config, copied verbatim). COMPOSE_PROJECT_NAME is always reset, since a copied value would fuse the environments. Ticked keys collapse back to a glob when the glob covers exactly your selection.

reset or template?

Resetting only produces a new value when local.setup's default is environment-aware (next_value, ports, os.getcwd()). With a constant default like "localhost" it silently rewrites the same value, and both environments share it. Those keys need a [worktree.env] template instead; after setup, worktree warns about any reset that came back identical.

Seeding:

  • fresh - leave it empty and let migrate fill it.
  • clone - copy the source's named docker volumes via a throwaway container each. Project-agnostic and current data, but services mounted on a copied volume pause in the source, so worktree asks first unless --yes. Note it copies everything: N worktrees means N copies of the full volume.
  • devdb - run devdb.recover after up using the trimmed edwh-devdb-plugin snapshot (put migrate/data/snapshot/ in copy). Postgres only, much smaller than a clone.

After seeding, a worktree task in your project's tasks.py runs last, inside the new worktree (e.g. c.run("./bin/load-fixtures")).

Hostnames

Traefik routes on Host(), so overlapping hostnames make it pick a router at random, breaking both environments. Rewriting HOSTINGDOMAIN covers all rules, but needs wildcard DNS one label deeper and therefore a wildcard certificate (or CERTRESOLVER=default locally).

worktree compares the traefik Host() labels of the new environment against every other checkout and refuses to up on an overlap. --force starts it anyway.

Task Load Order

Commands are loaded in the following order:

  1. EDWH Package:

    • Loaded into the global namespace and its own namespaces (like edwh plugins.).
  2. Plugins:

    • Loaded into their own namespaces (like edwh 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

Restic

Pip Compile

Bundler

Server Provisioning

b2

Locust

sshkey

sshfs

files

whitelabel

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

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 license.

Changelog

See CHANGELOG.md

Metadata

Release files for edwh 1.17.0b1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for edwh 1.17.0b1
File Size Uploaded
edwh-1.17.0b1.tar.gz 88.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for edwh 1.17.0b1
File Interpreter ABI Platform
edwh-1.17.0b1-py3-none-any.whl Python 3 none any Details

Total release size: 182.0 kB

Release files / edwh-1.17.0b1.tar.gz

Download URL edwh-1.17.0b1.tar.gz
Size 88.4 kB
Tags Source
SHA-256 checksum
How to use checksums
8b390ccc3cd1504996b227a7450a8535ef8ded2dd51b5a894ebbdb6f8d2629d7
BLAKE2b-256 checksum
How to use checksums
f3b00fe7799abb13c46e8ef5a8d54a9ffeb6c8ce9c807290ae0a3ff6c283ccaf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"PikaOS","version":"4","id":"nest","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / edwh-1.17.0b1-py3-none-any.whl

Download URL edwh-1.17.0b1-py3-none-any.whl
Size 93.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
82b2a8f5d155e5b148f57dca04f1f6dde8360557afec484fbb6cd4a214a044f3
BLAKE2b-256 checksum
How to use checksums
1239ffccf20983fa52e4b3df62b6460702029d56fca720807e1716eb93bf6570
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"PikaOS","version":"4","id":"nest","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

1.18.0

2 release files

1.17.4

2 release files

This release

1.17.0b1 This release

2 release files

1.16.0

2 release files

1.15.0

2 release files

1.14.3

2 release files

1.14.2

2 release files

1.14.1

2 release files

1.14.0

2 release files

1.12.1

2 release files

1.12.0

2 release files

1.11.3

2 release files

1.11.2

2 release files

1.11.1

2 release files

1.11.0

2 release files

1.10.4

2 release files

1.10.3

2 release files

1.10.2

2 release files

1.10.1

2 release files

1.9.2

2 release files

1.9.1

2 release files

1.9.0

2 release files

1.8.6

2 release files

1.8.5

2 release files

1.8.4

2 release files

1.8.3

2 release files

1.8.2

2 release files

1.8.1

2 release files

1.8.0

2 release files

1.7.3

2 release files

1.7.2

2 release files

1.7.1

2 release files

1.7.0

2 release files

1.6.3

2 release files

1.6.2

2 release files

1.6.1

2 release files

1.6.0

2 release files

1.5.0

2 release files

1.4.3

2 release files

1.4.2

2 release files

1.4.1

2 release files

1.4.0

2 release files

1.3.0

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.6

2 release files

1.0.5

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.60.0

2 release files

0.59.1

2 release files

0.59.0

2 release files

0.58.1

2 release files

0.58.0

2 release files

0.57.2

2 release files

0.56.9

2 release files

0.56.8

2 release files

0.56.7

2 release files

0.56.6

2 release files

0.53.7

2 release files

0.53.6

2 release files

0.53.5

2 release files

0.53.2

2 release files

0.52.2

2 release files

0.52.1

2 release files

0.52.0

2 release files

0.51.3

2 release files

0.51.2

2 release files

0.51.1

2 release files

0.51.0

2 release files

0.50.0

2 release files

0.49.1

2 release files

0.48.2

2 release files

0.48.1

2 release files

0.47.0

2 release files

0.46.7

2 release files

0.46.6

2 release files

0.46.5

2 release files

0.46.4

2 release files

0.46.3

2 release files

0.46.2

2 release files

0.45.1

2 release files

0.45.0

2 release files

0.44.3

2 release files

0.44.2

2 release files

0.44.1

2 release files

0.44.0

2 release files

0.43.9

2 release files

0.43.8

2 release files

0.43.7

2 release files

0.43.6

2 release files

0.43.5

2 release files

0.43.4

2 release files

0.43.3

2 release files

0.43.2

2 release files

0.43.1

2 release files

0.43.0

2 release files

0.42.3

2 release files

0.42.2

2 release files

0.42.1

2 release files

0.42.0

2 release files

0.41.4

2 release files

0.41.2

2 release files

0.41.1

2 release files

0.41.0

2 release files

0.40.6

2 release files

0.40.5

2 release files

0.40.4

2 release files

0.40.3

2 release files

0.40.2

2 release files

0.40.1

2 release files

0.40.0

2 release files

0.39.2

2 release files

0.39.1

2 release files

0.39.0

2 release files

0.38.3

2 release files

0.37.1

2 release files

0.37.0

2 release files

0.36.5

2 release files

0.36.4

2 release files

0.36.3

2 release files

0.36.2

2 release files

0.36.1

2 release files

0.36.0

2 release files

0.34.1

2 release files

0.34.0

2 release files

0.30.1

2 release files

0.30.0

2 release files

0.29.6

2 release files

0.29.5

2 release files

0.29.4

2 release files

0.27.2

2 release files

0.27.1

2 release files

0.27.0

2 release files

0.26.0

2 release files

0.25.2

2 release files

0.25.1

2 release files

0.25.0

2 release files

0.24.0

2 release files

0.20.1

2 release files

0.19.5

2 release files

0.19.4

2 release files

0.19.3

2 release files

0.19.2

2 release files

0.19.1

2 release files

0.19.0

2 release files

0.18.4

2 release files

0.18.3

2 release files

0.18.2

2 release files

0.18.1

2 release files

0.18.0

2 release files

0.17.2

2 release files

0.17.1

2 release files

0.17.0

2 release files

0.16.1

2 release files

0.16.0

2 release files

0.15.2

2 release files

0.15.1

2 release files

0.15.0

2 release files

0.9.1

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.6

2 release files

0.6.5

2 release files

0.6.4

2 release files

0.6.3

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.4.5

2 release files

0.4.4

2 release files

0.4.3

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

0.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page