Metadata-Version: 2.1
Name: wexample-wex-addon-app
Version: 30.0.0
Summary: Adds Docker application management to wex: service lifecycle commands, environment config, and managed workdir setup
Author-Email: weeger <contact@wexample.com>
License: MIT
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Project-URL: homepage, https://github.com/wexample/python-wex-addon-app
Requires-Python: >=3.10
Requires-Dist: attrs>=23.1.0
Requires-Dist: cattrs>=23.1.0
Requires-Dist: jinja2>=3.0
Requires-Dist: tomlkit
Requires-Dist: wexample-migration>=10.1.0
Requires-Dist: wexample-runner>=9.3.0
Requires-Dist: wexample-wex-core>=30.0.0
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Description-Content-Type: text/markdown

# wex_addon_app

Version: 30.0.0

wex_addon_app extends `wex` with Docker application management: service lifecycle commands, environment configuration, and the `ManagedWorkdir` base class that every managed project builds on. It installs a per-project `.wex/bin/app-manager` executable that runs each project's commands in an isolated subprocess, so suite-level operations can iterate over all packages without shared state leaking between them. It is aimed at developers who maintain multi-package Python suites and need a consistent, scriptable interface for building, running, and publishing Docker-backed applications.

## Table of Contents

- [Installation](#installation)
- [Quickstart](#quickstart)
- [Tests](#tests)
- [Architecture](#architecture)
- [Integration in the Suite](#integration-in-the-suite)
- [Dependencies](#dependencies)
- [Versioning & Compatibility Policy](#versioning--compatibility-policy)
- [License](#license)
- [About us](#about-us)
- [Known Limitations & Roadmap](#known-limitations--roadmap)
- [Status & Compatibility](#status--compatibility)
- [Useful Links](#useful-links)
- [Migration Notes](#migration-notes)

## Installation

```bash
pip install wexample-wex-addon-app
```

Requires Python >=3.10.

## Quickstart

Install the package:

```bash
pip install wexample-wex-addon-app
```

From Python, the first real thing the package can do is detect whether a directory is a wex-managed application — one whose `.wex/config.yml` defines `global.version`:

```python
from wexample_wex_addon_app.workdir.managed_workdir import ManagedWorkdir

print(ManagedWorkdir.is_app_workdir_path("/path/to/my-project"))
# True when .wex/config.yml defines global.version, False otherwise
```

Once a project is set up (`.wex/bin/app-manager` exists), every command the addon registers is available through that script. The simplest end-to-end call returns a JSON summary of the project:

```bash
.wex/bin/app-manager app::info/show
```

```json
{"name": "my-app", "version": "1.0.0", "env": "local"}
```

`app-manager` is a thin shell wrapper that delegates to the `wex` binary with the current project's addon stack loaded. The addon provides it with the `app::` command namespace — `app::info/show`, `app::setup/install`, `app::app/restart`, and the rest — so adding the package to a wex kernel is all that is needed to make those commands available.

## Tests

This project uses `pytest` for testing and `pytest-cov` for code coverage analysis.

### Installation

First, install the required testing dependencies:
```bash
.venv/bin/python -m pip install pytest pytest-cov
```

### Basic Usage

Run all tests with coverage:
```bash
.venv/bin/python -m pytest --cov --cov-report=html
```

### Common Commands
```bash
# Run tests with coverage for a specific module
.venv/bin/python -m pytest --cov=your_module

# Show which lines are not covered
.venv/bin/python -m pytest --cov=your_module --cov-report=term-missing

# Generate an HTML coverage report
.venv/bin/python -m pytest --cov=your_module --cov-report=html

# Combine terminal and HTML reports
.venv/bin/python -m pytest --cov=your_module --cov-report=term-missing --cov-report=html

# Run specific test file with coverage
.venv/bin/python -m pytest tests/test_file.py --cov=your_module --cov-report=term-missing
```

### Viewing HTML Reports

After generating an HTML report, open `htmlcov/index.html` in your browser to view detailed line-by-line coverage information.

### Coverage Threshold

To enforce a minimum coverage percentage:
```bash
.venv/bin/python -m pytest --cov=your_module --cov-fail-under=80
```

This will cause the test suite to fail if coverage drops below 80%.

## Architecture

The addon is built around four cooperating layers: a kernel-level addon manager, a workdir hierarchy, a middleware pipeline, and a pair of command resolvers. A thin Bash script acts as the per-project entrypoint and delegates every call to the `wex` binary.

### Entrypoint

Each managed project contains a copy of src/wexample_wex_addon_app/resources/app-manager.sh written to `.wex/bin/app-manager` by the filestate rectifier. The script finds the `wex` binary (via `$CORE_BIN` or `which wex`) and forwards all arguments verbatim with `exec "$CORE_BIN" "$@"`. The `wex` kernel then routes the command through the addon stack.

### AppAddonManager

src/wexample_wex_addon_app/app_addon_manager.py is registered with the kernel as the addon for this package. It owns:

- **Workdir instantiation.** `create_app_workdir(path)` reads `.wex/config.yml` to confirm a valid app directory, then loads `.wex/python/app_manager/app_workdir.py` if the project ships a custom `AppWorkdir` class, otherwise falls back to `ManagedWorkdir`. Services contribute their own `children` configuration before `workdir.configure()` is called.
- **Service registry.** `get_app_services(workdir)` always injects the `default` service first, then adds any service declared under `service:` in `config.yml`. Service manifests support `extends:` inheritance; `get_service_inheritance_chain()` resolves the full chain and `get_service_manifest()` deep-merges it.
- **Command resolvers.** `get_command_resolver_classes()` returns `AppCommandResolver` and `ServiceCommandResolver`.
- **Middleware classes.** `get_middlewares_classes()` registers all five middleware types described below.

### Workdir hierarchy

The workdir classes live in `src/wexample_wex_addon_app/workdir/` and form a single inheritance chain.

**src/wexample_wex_addon_app/workdir/managed_workdir.py** is the base for every managed project. It assembles the mixin stack (`WithMigrationWorkdirMixin`, `WithRunnersRootMixin`, `WithAppConfigWorkdirMixin`, `WithSuiteTreeWorkdirMixin`, `WithReadmeWorkdirMixin`, `WithAppVersionWorkdirMixin`, `WithRuntimeConfigMixin`, `WithAppRegistryMixin`, `WithLocalDataMixin`, `Workdir`). Key responsibilities: declaring the `.wex/` directory tree via `prepare_value()`, invoking the app-manager subprocess through `manager_run()` / `manager_run_command()`, and managing Docker container naming.

**src/wexample_wex_addon_app/workdir/repo_workdir.py** extends `ManagedWorkdir` with the version lifecycle: `bump()` creates a `version-x.y.z` branch and writes the new version; `release()` orchestrates the full publish pipeline (test → library sync → bump → rectify → push → propagate → build → publish); `classify_version_bump()` inspects git changes to pick patch vs minor vs major.

**src/wexample_wex_addon_app/workdir/code_base_workdir.py** extends `RepoWorkdir` with git operations and dependency management: `add_publication_tag()`, `commit_changes()`, `push_changes()`, `update_dependencies()`, `depends_from()`. This is the workdir type used for individual publishable packages.

**src/wexample_wex_addon_app/workdir/framework_packages_suite_workdir.py** extends `RepoWorkdir` for a directory that aggregates multiple packages. `get_packages()` discovers child packages via `find_all_by_type()`; `get_ordered_packages()` returns them in topological dependency order. `packages_execute_manager(command, arguments)` iterates over every package path and calls `ManagedWorkdir.manager_run_from_path()` in each — so each package runs in its own subprocess with its own `app-manager`. `packages_execute_function()` resolves a Python function reference to a CLI command string first, then delegates to `packages_execute_manager()`.

### Workdir mixins

src/wexample_wex_addon_app/workdir/mixin/with_suite_tree_workdir_mixin.py walks the filesystem upward to locate a parent suite directory (one that declares `package_suite:` in its config), caches the result, and provides `search_closest_in_suites_tree()` / `collect_stack_in_suites_tree()` for config and env-parameter lookups that fall back up the tree.

src/wexample_wex_addon_app/workdir/mixin/with_app_config_workdir_mixin.py declares the abstract `get_app_config_file()` that language-specific subclasses implement to return their manifest file (e.g. `pyproject.toml`).

### Middleware pipeline

All middleware lives in `src/wexample_wex_addon_app/middleware/`. They all extend `AbstractMiddleware`; a command selects one or more via `@middleware(middleware=...)` decorators.

**src/wexample_wex_addon_app/middleware/app_middleware.py** — the base. `build_execution_contexts()` pops `app_path` from the kwargs (defaulting to the kernel's call directory), calls `AppAddonManager.create_app_workdir()`, and injects the result as `app_workdir`. Also enforces `config_requirements` and `env_requirements` declared in the command's `extra` dict.

**src/wexample_wex_addon_app/middleware/package_suite_middleware.py** — extends `AppMiddleware`. Validates that the resolved workdir is a `FrameworkPackageSuiteWorkdir`; raises `InvalidWorkdirTypeException` (with a hint about the nearest suite path) when it is not and `_fail_if_not_suite_workdir` is `True`.

**src/wexample_wex_addon_app/middleware/each_suite_package_middleware.py** — extends `PackageSuiteMiddleware`. Adds `--all-packages`. When the flag is absent the command runs normally on the single workdir. When it is present, the middleware replaces the command function with a closure that calls `suite_workdir.packages_execute_manager(command, arguments)`, iterating every package in its own subprocess.

**src/wexample_wex_addon_app/middleware/suite_or_each_package_middleware.py** — extends `PackageSuiteMiddleware`. Adds `--all-packages`, `--packages-only`, and `--suite-only`. Builds up to two execution contexts: one for the suite itself and one that iterates packages, depending on the flags.

**src/wexample_wex_addon_app/middleware/code_base_middleware.py** — restricts execution to workdirs that implement the `CodeBaseWorkdir` interface.

### Command resolvers

**src/wexample_wex_addon_app/resolver/app_command_resolver.py** handles the `.group/command` address pattern. It walks up from the current working directory to find the nearest `.wex/commands/` directory and maps the address to a file there. Commands of this type are local to the project, not to the addon package.

**src/wexample_wex_addon_app/resolver/service_command_resolver.py** handles `@service::group/command`. It scans `services/<name>/commands/` directories inside every registered addon, building the registry from those files. When building an execution context it creates an `AppService` instance and injects it as `service` into the function kwargs. `is_attachment_active()` skips service-attached commands when the current project does not declare that service.

### Services

**src/wexample_wex_addon_app/service/app_service.py** represents one active service. It holds the service name, the resolved `service_dir` path from the addon, the parsed manifest, and the owning workdir. `get_runtime_contribution()` resolves compose file paths (including env-specific overrides) and `runtime.bind` declarations from `service.yml`. A project can ship a custom `app_service.py` in its service directory; `AppAddonManager.get_app_service()` loads it dynamically.

Two services are bundled with this addon:

- src/wexample_wex_addon_app/services/default/service.yml — always injected first; provides the base Docker Compose configuration (network, restart policy, tty).
- src/wexample_wex_addon_app/services/proxy/service.yml — optional reverse proxy, tagged `proxy` and `network`.

### Filestate integration

`ManagedWorkdir.prepare_value()` declares the full `.wex/` directory tree as a filestate configuration. The `SetupManagerOption` (registered via src/wexample_wex_addon_app/filestate/options_provider/setup_manager_options_provider.py) handles `setup_manager.auto_migrate: true` in config, which triggers `SetupManagerMigrationOperation` during every rectify pass.

### Publication pipeline

src/wexample_wex_addon_app/publication/strategy/abstract_publication_strategy.py defines `prepare_commit()` (called before the bump commit) and `run_post_publish_pipeline()` (called after the package reaches the registry). Two concrete strategies are shipped: `BranchMergePublicationStrategy` merges the version branch back to main; `MainPushPublicationStrategy` pushes directly from main. The active strategy is selected by `AbstractPublicationStrategy.from_workdir(self)` based on the project's configuration.

### Call path for a single command

```
.wex/bin/app-manager app::info/show
  → wex binary (kernel)
  → AppCommandResolver.supports() matches app::info/show
  → loads commands/info/show.py → app__info__show function
  → @middleware(AppMiddleware) fires:
      AppAddonManager.create_app_workdir(cwd)
        reads .wex/config.yml
        loads optional .wex/python/app_manager/app_workdir.py
        assembles services, calls workdir.configure()
  → app__info__show(context, app_workdir=<ManagedWorkdir>)
```

### Call path across all packages

```
.wex/bin/app-manager app::state/rectify --all-packages
  → EachSuitePackageMiddleware.build_execution_contexts()
      resolves suite_workdir (FrameworkPackageSuiteWorkdir)
      --all-packages present → replaces function with:
          suite_workdir.packages_execute_manager(
              "app::state/rectify", original_arguments
          )
  → packages_execute_manager() iterates get_packages_paths():
      for each package_path:
          ManagedWorkdir.manager_run_from_path(
              cmd=[".wex/bin/app-manager", "--subprocess",
                   "app::state/rectify", ...],
              path=package_path
          )
```

Each package subprocess runs its own isolated kernel with its own `app-manager`, so custom `AppWorkdir` classes and per-package dependencies do not leak across packages.

## Integration in the Suite

This package is part of the Wexample Suite — a collection of high-quality, modular tools designed to work seamlessly together across multiple languages and environments.

### Related Packages

The suite includes packages for configuration management, file handling, prompts, and more. Each package can be used independently or as part of the integrated suite.

Visit the [Wexample Suite documentation](https://docs.wexample.com) for the complete package ecosystem.

## Dependencies

- attrs: >=23.1.0
- cattrs: >=23.1.0
- jinja2: >=3.0
- tomlkit: 
- wexample-migration: >=10.1.0
- wexample-runner: >=9.3.0
- wexample-wex-core: >=30.0.0

## Versioning & Compatibility Policy

Wexample packages follow **Semantic Versioning** (SemVer):

- **MAJOR**: Breaking changes
- **MINOR**: New features, backward compatible
- **PATCH**: Bug fixes, backward compatible

We maintain backward compatibility within major versions and provide clear migration guides for breaking changes.

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

Free to use in both personal and commercial projects.

## About us

[Wexample](https://wexample.com) stands as a cornerstone of the digital ecosystem — a collective of seasoned engineers, researchers, and creators driven by a relentless pursuit of technological excellence. More than a media platform, it has grown into a vibrant community where innovation meets craftsmanship, and where every line of code reflects a commitment to clarity, durability, and shared intelligence.

This packages suite embodies this spirit. Trusted by professionals and enthusiasts alike, it delivers a consistent, high-quality foundation for modern development — open, elegant, and battle-tested. Its reputation is built on years of collaboration, refinement, and rigorous attention to detail, making it a natural choice for those who demand both robustness and beauty in their tools.

Wexample cultivates a culture of mastery. Each package, each contribution carries the mark of a community that values precision, ethics, and innovation — a community proud to shape the future of digital craftsmanship.

## Known Limitations & Roadmap

Current limitations and planned features are tracked in the GitHub issues.

See the [project roadmap](https://github.com/wexample/python-wex_addon_app/issues) for upcoming features and improvements.

## Status & Compatibility

**Maturity**: Production-ready

**Python Support**: >=3.10

**OS Support**: Linux, macOS, Windows

**Status**: Actively maintained

## Useful Links

- **Homepage**: https://github.com/wexample/python-wex-addon-app
- **Documentation**: [docs.wexample.com](https://docs.wexample.com)
- **Issue Tracker**: https://github.com/wexample/python-wex-addon-app/issues
- **Discussions**: https://github.com/wexample/python-wex-addon-app/discussions
- **PyPI**: [pypi.org/project/wexample-wex-addon-app](https://pypi.org/project/wexample-wex-addon-app/)

## Migration Notes

When upgrading between major versions, refer to the migration guides in the documentation.

Breaking changes are clearly documented with upgrade paths and examples.
