docker-app-launcher 0.1.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Asterios Raptis
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,141 @@
1
+ Metadata-Version: 2.4
2
+ Name: docker-app-launcher
3
+ Version: 0.1.0
4
+ Summary: Configurable desktop launcher for Docker-based applications
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Keywords: docker,launcher,desktop,tkinter,gui
8
+ Author: Asterios Raptis
9
+ Author-email: aster.raptis@gmail.com
10
+ Requires-Python: >=3.10,<4.0
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: X11 Applications
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Software Development :: Build Tools
22
+ Classifier: Topic :: System :: Installation/Setup
23
+ Classifier: Typing :: Typed
24
+ Provides-Extra: tray
25
+ Requires-Dist: Pillow (>=10.0) ; extra == "tray"
26
+ Requires-Dist: pystray (>=0.19) ; extra == "tray"
27
+ Project-URL: Changelog, https://github.com/astrapi69/docker-app-launcher/blob/main/CHANGELOG.md
28
+ Project-URL: Documentation, https://github.com/astrapi69/docker-app-launcher#readme
29
+ Project-URL: Homepage, https://github.com/astrapi69/docker-app-launcher
30
+ Project-URL: Issue Tracker, https://github.com/astrapi69/docker-app-launcher/issues
31
+ Project-URL: Repository, https://github.com/astrapi69/docker-app-launcher
32
+ Description-Content-Type: text/markdown
33
+
34
+ # docker-app-launcher
35
+
36
+ Configurable desktop launcher for Docker-based applications.
37
+ **One persistent window.** It opens, shows progress, and never closes itself —
38
+ no dialog chains.
39
+
40
+ [![CI](https://github.com/astrapi69/docker-app-launcher/actions/workflows/ci.yml/badge.svg)](https://github.com/astrapi69/docker-app-launcher/actions/workflows/ci.yml)
41
+ [![PyPI](https://img.shields.io/pypi/v/docker-app-launcher.svg)](https://pypi.org/project/docker-app-launcher/)
42
+ [![Python](https://img.shields.io/pypi/pyversions/docker-app-launcher.svg)](https://pypi.org/project/docker-app-launcher/)
43
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
44
+
45
+ > 🇩🇪 [Deutsche Version](README-de.md)
46
+
47
+ ## Quick Start
48
+
49
+ ```bash
50
+ pip install docker-app-launcher
51
+ ```
52
+
53
+ ### Python API (3 lines)
54
+
55
+ ```python
56
+ from docker_app_launcher import LauncherConfig, launch
57
+
58
+ launch(LauncherConfig(
59
+ app_name="My App",
60
+ container_name="my-app",
61
+ default_port=8080,
62
+ ))
63
+ ```
64
+
65
+ ### CLI
66
+
67
+ ```bash
68
+ docker-app-launcher --config launcher.json # open the window
69
+ docker-app-launcher --status # print state and exit
70
+ docker-app-launcher --install --port 9000 # build + start headless
71
+ docker-app-launcher --check # is Docker running?
72
+ ```
73
+
74
+ ### launcher.json
75
+
76
+ Everything is configurable. Only `app_name` is required — the rest is derived
77
+ (slug, container/image names, compose project, config dir) or defaulted.
78
+
79
+ ```json
80
+ {
81
+ "app_name": "My App",
82
+ "container_name": "my-app",
83
+ "default_port": 8080,
84
+ "compose_file": "docker-compose.prod.yml",
85
+ "install_dir": "/opt/my-app",
86
+ "health_check_path": "/api/health",
87
+ "health_check_key": "status",
88
+ "health_check_value": "ok",
89
+ "repo_url": "https://github.com/owner/repo",
90
+ "locale": "en"
91
+ }
92
+ ```
93
+
94
+ ## Features
95
+
96
+ - **One persistent window** — never closes itself; only the X closes it.
97
+ - **Docker check on startup** — distinguishes *not installed* / *running* / *stopped* / *no Docker*.
98
+ - **Live build progress** — the Docker build is streamed line-by-line into the window.
99
+ - **Configurable port** — editable in the GUI and via `--port`, validated and persisted (to `launcher.json` and `.env`).
100
+ - **Verified actions** — install runs a health check; uninstall re-lists the containers to confirm they are gone.
101
+ - **Install manifest + startup cleanup** — finds and offers to remove stale leftovers of older installs.
102
+ - **Verbose uninstall / cleanup** — every step reported with a ✓ / ✗ result.
103
+ - **System tray** (optional) — `pip install docker-app-launcher[tray]`; the window minimizes to the tray while running.
104
+ - **DE / EN i18n** — with per-app custom-string overrides via `custom_strings`.
105
+ - **Actions architecture** — all business logic is GUI-free and unit-tested without a display.
106
+ - **CLI ↔ GUI parity** — both call the exact same actions layer.
107
+
108
+ ## Architecture
109
+
110
+ | Module | Responsibility |
111
+ |---------------|-------------------------------------------------------------|
112
+ | `config.py` | `LauncherConfig` dataclass — the single source of truth. |
113
+ | `actions.py` | All business logic. No `tkinter`. Fully testable. |
114
+ | `gui.py` | `LauncherApp(tk.Tk)` — a thin window over the actions. |
115
+ | `tray.py` | Optional system tray (pystray + Pillow). |
116
+ | `i18n.py` | DE/EN strings with custom-string overrides. |
117
+ | `__main__.py` | CLI entry point + GUI router. |
118
+
119
+ ## Configuration reference
120
+
121
+ See [LauncherConfig](src/docker_app_launcher/config.py) for the full field list
122
+ (app identity, network/health, Docker timeouts, paths, GUI, links, cleanup,
123
+ tray, i18n, and lifecycle callbacks).
124
+
125
+ ## Development
126
+
127
+ ```bash
128
+ poetry install --with dev --all-extras
129
+ make ci # lint + format-check + typecheck + tests
130
+ make test # tests with coverage
131
+ make fix # auto-fix lint + format
132
+ ```
133
+
134
+ ## Used by
135
+
136
+ - [Adaptive Learner](https://github.com/astrapi69/adaptive-learner)
137
+
138
+ ## License
139
+
140
+ [MIT](LICENSE) © Asterios Raptis
141
+
@@ -0,0 +1,107 @@
1
+ # docker-app-launcher
2
+
3
+ Configurable desktop launcher for Docker-based applications.
4
+ **One persistent window.** It opens, shows progress, and never closes itself —
5
+ no dialog chains.
6
+
7
+ [![CI](https://github.com/astrapi69/docker-app-launcher/actions/workflows/ci.yml/badge.svg)](https://github.com/astrapi69/docker-app-launcher/actions/workflows/ci.yml)
8
+ [![PyPI](https://img.shields.io/pypi/v/docker-app-launcher.svg)](https://pypi.org/project/docker-app-launcher/)
9
+ [![Python](https://img.shields.io/pypi/pyversions/docker-app-launcher.svg)](https://pypi.org/project/docker-app-launcher/)
10
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
11
+
12
+ > 🇩🇪 [Deutsche Version](README-de.md)
13
+
14
+ ## Quick Start
15
+
16
+ ```bash
17
+ pip install docker-app-launcher
18
+ ```
19
+
20
+ ### Python API (3 lines)
21
+
22
+ ```python
23
+ from docker_app_launcher import LauncherConfig, launch
24
+
25
+ launch(LauncherConfig(
26
+ app_name="My App",
27
+ container_name="my-app",
28
+ default_port=8080,
29
+ ))
30
+ ```
31
+
32
+ ### CLI
33
+
34
+ ```bash
35
+ docker-app-launcher --config launcher.json # open the window
36
+ docker-app-launcher --status # print state and exit
37
+ docker-app-launcher --install --port 9000 # build + start headless
38
+ docker-app-launcher --check # is Docker running?
39
+ ```
40
+
41
+ ### launcher.json
42
+
43
+ Everything is configurable. Only `app_name` is required — the rest is derived
44
+ (slug, container/image names, compose project, config dir) or defaulted.
45
+
46
+ ```json
47
+ {
48
+ "app_name": "My App",
49
+ "container_name": "my-app",
50
+ "default_port": 8080,
51
+ "compose_file": "docker-compose.prod.yml",
52
+ "install_dir": "/opt/my-app",
53
+ "health_check_path": "/api/health",
54
+ "health_check_key": "status",
55
+ "health_check_value": "ok",
56
+ "repo_url": "https://github.com/owner/repo",
57
+ "locale": "en"
58
+ }
59
+ ```
60
+
61
+ ## Features
62
+
63
+ - **One persistent window** — never closes itself; only the X closes it.
64
+ - **Docker check on startup** — distinguishes *not installed* / *running* / *stopped* / *no Docker*.
65
+ - **Live build progress** — the Docker build is streamed line-by-line into the window.
66
+ - **Configurable port** — editable in the GUI and via `--port`, validated and persisted (to `launcher.json` and `.env`).
67
+ - **Verified actions** — install runs a health check; uninstall re-lists the containers to confirm they are gone.
68
+ - **Install manifest + startup cleanup** — finds and offers to remove stale leftovers of older installs.
69
+ - **Verbose uninstall / cleanup** — every step reported with a ✓ / ✗ result.
70
+ - **System tray** (optional) — `pip install docker-app-launcher[tray]`; the window minimizes to the tray while running.
71
+ - **DE / EN i18n** — with per-app custom-string overrides via `custom_strings`.
72
+ - **Actions architecture** — all business logic is GUI-free and unit-tested without a display.
73
+ - **CLI ↔ GUI parity** — both call the exact same actions layer.
74
+
75
+ ## Architecture
76
+
77
+ | Module | Responsibility |
78
+ |---------------|-------------------------------------------------------------|
79
+ | `config.py` | `LauncherConfig` dataclass — the single source of truth. |
80
+ | `actions.py` | All business logic. No `tkinter`. Fully testable. |
81
+ | `gui.py` | `LauncherApp(tk.Tk)` — a thin window over the actions. |
82
+ | `tray.py` | Optional system tray (pystray + Pillow). |
83
+ | `i18n.py` | DE/EN strings with custom-string overrides. |
84
+ | `__main__.py` | CLI entry point + GUI router. |
85
+
86
+ ## Configuration reference
87
+
88
+ See [LauncherConfig](src/docker_app_launcher/config.py) for the full field list
89
+ (app identity, network/health, Docker timeouts, paths, GUI, links, cleanup,
90
+ tray, i18n, and lifecycle callbacks).
91
+
92
+ ## Development
93
+
94
+ ```bash
95
+ poetry install --with dev --all-extras
96
+ make ci # lint + format-check + typecheck + tests
97
+ make test # tests with coverage
98
+ make fix # auto-fix lint + format
99
+ ```
100
+
101
+ ## Used by
102
+
103
+ - [Adaptive Learner](https://github.com/astrapi69/adaptive-learner)
104
+
105
+ ## License
106
+
107
+ [MIT](LICENSE) © Asterios Raptis
@@ -0,0 +1,121 @@
1
+ [project]
2
+ name = "docker-app-launcher"
3
+ version = "0.1.0"
4
+ description = "Configurable desktop launcher for Docker-based applications"
5
+ authors = [{ name = "Asterios Raptis", email = "aster.raptis@gmail.com" }]
6
+ readme = "README.md"
7
+ license = "MIT"
8
+ requires-python = ">=3.10,<4.0"
9
+ keywords = ["docker", "launcher", "desktop", "tkinter", "gui"]
10
+ classifiers = [
11
+ "Development Status :: 3 - Alpha",
12
+ "Environment :: X11 Applications",
13
+ "Intended Audience :: Developers",
14
+ "License :: OSI Approved :: MIT License",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.10",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Programming Language :: Python :: 3.14",
21
+ "Topic :: Software Development :: Build Tools",
22
+ "Topic :: System :: Installation/Setup",
23
+ "Typing :: Typed",
24
+ ]
25
+ dependencies = []
26
+
27
+ [project.optional-dependencies]
28
+ tray = ["pystray>=0.19", "Pillow>=10.0"]
29
+
30
+ [project.urls]
31
+ Homepage = "https://github.com/astrapi69/docker-app-launcher"
32
+ Repository = "https://github.com/astrapi69/docker-app-launcher"
33
+ Documentation = "https://github.com/astrapi69/docker-app-launcher#readme"
34
+ Changelog = "https://github.com/astrapi69/docker-app-launcher/blob/main/CHANGELOG.md"
35
+ "Issue Tracker" = "https://github.com/astrapi69/docker-app-launcher/issues"
36
+
37
+ [project.scripts]
38
+ docker-app-launcher = "docker_app_launcher.__main__:main"
39
+
40
+ [tool.poetry]
41
+ packages = [{ include = "docker_app_launcher", from = "src" }]
42
+
43
+ [tool.poetry.group.dev.dependencies]
44
+ ruff = "^0.15.0"
45
+ mypy = "^2.1.0"
46
+ pre-commit = "^4.6.0"
47
+ pytest = "^9.1.0"
48
+ pytest-cov = "^7.1.0"
49
+ pytest-mock = "^3.15.0"
50
+ codespell = "^2.4.0"
51
+ twine = "^6.2.0"
52
+
53
+ [build-system]
54
+ requires = ["poetry-core>=2.0"]
55
+ build-backend = "poetry.core.masonry.api"
56
+
57
+ # ---------------------------------------------------------------------------
58
+ # Tool Configuration
59
+ # ---------------------------------------------------------------------------
60
+
61
+ [tool.ruff]
62
+ target-version = "py310"
63
+ line-length = 120
64
+ src = ["src", "tests"]
65
+
66
+ [tool.ruff.lint]
67
+ select = [
68
+ "E", # pycodestyle errors
69
+ "W", # pycodestyle warnings
70
+ "F", # pyflakes
71
+ "I", # isort
72
+ "B", # flake8-bugbear
73
+ "C4", # flake8-comprehensions
74
+ "UP", # pyupgrade
75
+ "SIM", # flake8-simplify
76
+ "BLE", # flake8-blind-except
77
+ "RUF", # ruff-specific rules
78
+ ]
79
+ ignore = []
80
+
81
+ [tool.ruff.lint.isort]
82
+ known-first-party = ["docker_app_launcher"]
83
+
84
+ [tool.ruff.format]
85
+ docstring-code-format = true
86
+
87
+ [tool.mypy]
88
+ python_version = "3.10"
89
+ strict = true
90
+ warn_unused_configs = true
91
+ warn_return_any = true
92
+ ignore_missing_imports = true
93
+ files = ["src", "tests"]
94
+
95
+ # Tests stay readable: pytest fixtures are injected untyped, so don't require
96
+ # annotations on every test function/parameter. Bodies are still type-checked.
97
+ [[tool.mypy.overrides]]
98
+ module = "tests.*"
99
+ disallow_untyped_defs = false
100
+ disallow_incomplete_defs = false
101
+
102
+ [tool.pytest.ini_options]
103
+ testpaths = ["tests"]
104
+ addopts = "--cov=docker_app_launcher --cov-report=term-missing --strict-markers"
105
+ markers = [
106
+ "slow: marks tests as slow (deselect with '-m \"not slow\"')",
107
+ ]
108
+
109
+ [tool.coverage.run]
110
+ source = ["docker_app_launcher"]
111
+ branch = true
112
+ omit = ["tests/*"]
113
+
114
+ [tool.coverage.report]
115
+ show_missing = true
116
+ skip_covered = false
117
+
118
+ [tool.codespell]
119
+ # i18n.py is a bilingual (EN/DE) string catalog; German words are not typos.
120
+ skip = "poetry.lock,.git,src/docker_app_launcher/i18n.py"
121
+ ignore-words-list = "oeffnen,uebersprungen,ueberspringen"
@@ -0,0 +1,45 @@
1
+ """Configurable desktop launcher for Docker-based applications.
2
+
3
+ Public API::
4
+
5
+ from docker_app_launcher import LauncherConfig, launch
6
+
7
+ launch(
8
+ LauncherConfig(
9
+ app_name="My App",
10
+ container_name="my-app",
11
+ default_port=8080,
12
+ )
13
+ )
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from importlib.metadata import PackageNotFoundError, version
19
+
20
+ from docker_app_launcher.config import LauncherConfig
21
+
22
+ try:
23
+ __version__ = version("docker-app-launcher")
24
+ except PackageNotFoundError: # pragma: no cover - package not installed
25
+ __version__ = "0.1.0"
26
+
27
+ __all__ = ["LauncherConfig", "__version__", "launch"]
28
+
29
+
30
+ def launch(config: LauncherConfig | None = None, **kwargs: object) -> int:
31
+ """Launch the GUI with the given config (or one built from ``kwargs``).
32
+
33
+ Usage::
34
+
35
+ launch(LauncherConfig(app_name="My App", default_port=8080))
36
+ # or
37
+ launch(app_name="My App", default_port=8080)
38
+ """
39
+ if config is None:
40
+ config = LauncherConfig(**kwargs) # type: ignore[arg-type]
41
+ config.resolve()
42
+
43
+ from docker_app_launcher.gui import run
44
+
45
+ return run(config)
@@ -0,0 +1,122 @@
1
+ """CLI entry point + GUI router.
2
+
3
+ With no action flag the persistent window opens. With an action flag
4
+ (``--install`` / ``--status`` / ...) the request routes straight through the
5
+ :mod:`actions` layer and exits - same code path the GUI uses, so the CLI and
6
+ GUI stay in lockstep (CLI<->GUI parity).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import logging
13
+ import sys
14
+ from collections.abc import Sequence
15
+
16
+ from docker_app_launcher import __version__, actions
17
+ from docker_app_launcher.config import LauncherConfig
18
+
19
+ logger = logging.getLogger("docker_app_launcher")
20
+
21
+
22
+ def build_parser() -> argparse.ArgumentParser:
23
+ """Build the command-line argument parser."""
24
+ parser = argparse.ArgumentParser(
25
+ prog="docker-app-launcher",
26
+ description="Configurable desktop launcher for Docker-based applications.",
27
+ )
28
+ parser.add_argument(
29
+ "--config", default="launcher.json", help="Path to the launcher config JSON (default: launcher.json)."
30
+ )
31
+ parser.add_argument("--port", type=int, default=None, help="Host port for the app (1024-65535).")
32
+ parser.add_argument("--debug", action="store_true", help="Verbose logging to stdout.")
33
+ parser.add_argument("--version", action="store_true", help="Print the launcher version and exit.")
34
+ # Headless action flags (CLI<->GUI parity).
35
+ parser.add_argument("--check", action="store_true", help="Check Docker status and exit.")
36
+ parser.add_argument("--status", action="store_true", help="Print the app state and exit.")
37
+ parser.add_argument("--install", action="store_true", help="Build + start the app and exit.")
38
+ parser.add_argument("--start", action="store_true", help="Start the stopped app and exit.")
39
+ parser.add_argument("--stop", action="store_true", help="Stop the running app and exit.")
40
+ parser.add_argument("--uninstall", action="store_true", help="Remove the app containers/images and exit.")
41
+ parser.add_argument("--cleanup", action="store_true", help="Remove stale leftovers and exit.")
42
+ parser.add_argument("--open", action="store_true", help="Open the app in the browser and exit.")
43
+ return parser
44
+
45
+
46
+ def _setup_logging(*, debug: bool) -> None:
47
+ logging.basicConfig(
48
+ level=logging.DEBUG if debug else logging.INFO,
49
+ format="%(asctime)s %(levelname)s %(name)s %(message)s",
50
+ stream=sys.stdout,
51
+ )
52
+
53
+
54
+ def run_cli_action(args: argparse.Namespace, config: LauncherConfig) -> int | None:
55
+ """Route a headless CLI action through the actions layer.
56
+
57
+ Returns an exit code when an action flag was handled, or ``None`` when no
58
+ action flag was present (the caller then launches the GUI).
59
+ """
60
+ if args.check:
61
+ ok, msg = actions.check_docker()
62
+ print(msg)
63
+ return 0 if ok else 1
64
+ if args.status:
65
+ print(f"Status: {actions.get_state(config)}")
66
+ return 0
67
+ if args.install:
68
+ ok, msg = actions.install(config, on_step=print, on_output=print)
69
+ print(msg)
70
+ return 0 if ok else 1
71
+ if args.start:
72
+ ok, msg = actions.start(config, on_step=print, on_output=print)
73
+ print(msg)
74
+ return 0 if ok else 1
75
+ if args.stop:
76
+ ok, msg = actions.stop(config)
77
+ print(msg)
78
+ return 0 if ok else 1
79
+ if args.uninstall:
80
+ ok, msg = actions.uninstall(config, on_step=print)
81
+ print(msg)
82
+ return 0 if ok else 1
83
+ if args.cleanup:
84
+ stale = actions.find_stale_artifacts(config)
85
+ ok, msg = actions.cleanup_stale(config, stale, on_step=print)
86
+ print(msg)
87
+ return 0 if ok else 1
88
+ if args.open:
89
+ actions.open_browser(config)
90
+ return 0
91
+ return None
92
+
93
+
94
+ def main(argv: Sequence[str] | None = None) -> int:
95
+ """CLI entry point. Returns a process exit code."""
96
+ args = build_parser().parse_args(argv)
97
+
98
+ if args.version:
99
+ print(f"docker-app-launcher {__version__}")
100
+ return 0
101
+
102
+ _setup_logging(debug=args.debug)
103
+ config = LauncherConfig.from_json(args.config)
104
+
105
+ if args.port is not None:
106
+ ok, msg = actions.set_port(config, args.port)
107
+ if not ok:
108
+ print(msg, file=sys.stderr)
109
+ return 2
110
+
111
+ action_rc = run_cli_action(args, config)
112
+ if action_rc is not None:
113
+ return action_rc
114
+
115
+ # No action flag -> open the persistent window.
116
+ from docker_app_launcher.gui import run
117
+
118
+ return run(config, debug=args.debug)
119
+
120
+
121
+ if __name__ == "__main__":
122
+ raise SystemExit(main())