taurido 1.0.0__py3-none-any.whl

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.
taurido/__init__.py ADDED
@@ -0,0 +1,2 @@
1
+ __all__ = ["__version__"]
2
+ __version__ = "1.0.0"
taurido/cli.py ADDED
@@ -0,0 +1,176 @@
1
+ """Canonical command-line interface for every Dock2Tauri launcher."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import os
8
+ from pathlib import Path
9
+ from typing import Optional, Sequence, Tuple
10
+
11
+ from .core import Runner
12
+ from .plan import BuildPlan
13
+
14
+
15
+ def _load_env_file(path: Path) -> None:
16
+ """Load simple KEY=VALUE settings without evaluating shell code."""
17
+ if not path.is_file():
18
+ return
19
+ for raw_line in path.read_text(encoding="utf-8").splitlines():
20
+ line = raw_line.strip()
21
+ if not line or line.startswith("#") or "=" not in line:
22
+ continue
23
+ if line.startswith("export "):
24
+ line = line[7:].lstrip()
25
+ key, value = line.split("=", 1)
26
+ key = key.strip()
27
+ value = value.strip().strip("\"'")
28
+ if key.isidentifier():
29
+ os.environ.setdefault(key, value)
30
+
31
+
32
+ def _copy_targets(value: Optional[str]) -> Tuple[str, ...]:
33
+ if not value:
34
+ return ()
35
+ return tuple(item.strip() for item in value.split(",") if item.strip())
36
+
37
+
38
+ def build_parser() -> argparse.ArgumentParser:
39
+ config_root = Path(os.environ.get("TAURIDO_PROJECT_ROOT", Path.cwd()))
40
+ _load_env_file(config_root / ".env")
41
+
42
+ parser = argparse.ArgumentParser(
43
+ prog="taurido",
44
+ description="Package a Dockerized web application as a Tauri desktop app",
45
+ )
46
+ parser.add_argument(
47
+ "image_or_dockerfile",
48
+ nargs="?",
49
+ default=os.environ.get("DOCKER_IMAGE", "nginx:alpine"),
50
+ help="Docker image or Dockerfile path (default: nginx:alpine)",
51
+ )
52
+ parser.add_argument("host_port", nargs="?", default="8088", help="Host port (default: 8088)")
53
+ parser.add_argument("container_port", nargs="?", default="80", help="Container port (default: 80)")
54
+ parser.add_argument("--build", "-b", action="store_true", help="Build Tauri release bundles")
55
+ parser.add_argument("--target", help="Target triple for cargo tauri build")
56
+ parser.add_argument("--health-url", dest="health_url", help="Override readiness URL")
57
+ parser.add_argument("--timeout", type=int, default=30, help="Readiness timeout seconds (default: 30)")
58
+ parser.add_argument("--cross", action="store_true", help="Attempt installed cross targets")
59
+ parser.add_argument(
60
+ "--project-root",
61
+ help="Directory containing src-tauri; automatically detected when omitted",
62
+ )
63
+ parser.add_argument(
64
+ "--export-dir",
65
+ "--output-dir",
66
+ dest="export_dir",
67
+ default=os.environ.get("OUTPUT_DIR"),
68
+ help="Bundle export directory (default: project-root/dist)",
69
+ )
70
+ parser.add_argument(
71
+ "--app-name",
72
+ default=os.environ.get("CUSTOM_APP_NAME"),
73
+ help="Product name stored in the generated Tauri configuration",
74
+ )
75
+ parser.add_argument(
76
+ "--filename",
77
+ dest="filename_prefix",
78
+ default=os.environ.get("CUSTOM_FILENAME"),
79
+ help="Prefix copied artifact filenames",
80
+ )
81
+ parser.add_argument(
82
+ "--copy-to",
83
+ default=os.environ.get("ADDITIONAL_OUTPUT_DIRS"),
84
+ help="Comma-separated additional artifact directories",
85
+ )
86
+ parser.add_argument("--launch", action="store_true", help="Launch the built application")
87
+ parser.add_argument("--list-bundles", action="store_true", help="List generated bundle contents")
88
+ parser.add_argument("--debug", action="store_true", help="Enable diagnostic environment output")
89
+ parser.add_argument(
90
+ "--print-plan",
91
+ action="store_true",
92
+ help="Print the normalized JSON execution plan without running Docker or Tauri",
93
+ )
94
+ return parser
95
+
96
+
97
+ def parse_args(argv: Optional[Sequence[str]] = None) -> argparse.Namespace:
98
+ args = build_parser().parse_args(argv)
99
+ if os.path.isfile(args.image_or_dockerfile) and not args.build:
100
+ args.build = True
101
+ os.environ.setdefault("EXPORT_BUNDLES", "1")
102
+ if not args.print_plan:
103
+ print("\x1b[34mℹ️ Dockerfile input detected; defaulting to build and export bundles.\x1b[0m")
104
+ return args
105
+
106
+
107
+ def create_plan(args: argparse.Namespace) -> BuildPlan:
108
+ return BuildPlan(
109
+ image_or_dockerfile=args.image_or_dockerfile,
110
+ host_port=str(args.host_port),
111
+ container_port=str(args.container_port),
112
+ build=args.build,
113
+ target=args.target,
114
+ health_url=args.health_url,
115
+ timeout=int(args.timeout),
116
+ cross=args.cross,
117
+ project_root=args.project_root,
118
+ export_dir=args.export_dir,
119
+ app_name=args.app_name,
120
+ filename_prefix=args.filename_prefix,
121
+ copy_to=_copy_targets(args.copy_to),
122
+ launch=args.launch,
123
+ list_bundles=args.list_bundles,
124
+ debug=args.debug,
125
+ )
126
+
127
+
128
+ def execute_plan(plan: BuildPlan) -> None:
129
+ if plan.debug:
130
+ os.environ["DOCK2TAURI_DEBUG"] = "1"
131
+ runner = Runner(
132
+ base_dir=Path(plan.project_root) if plan.project_root else None,
133
+ export_dir=Path(plan.export_dir) if plan.export_dir else None,
134
+ launch_after_build=plan.launch,
135
+ list_bundles=plan.list_bundles,
136
+ app_name=plan.app_name,
137
+ filename_prefix=plan.filename_prefix,
138
+ additional_output_dirs=[Path(item) for item in plan.copy_to],
139
+ )
140
+ try:
141
+ if plan.build:
142
+ runner.build_flow(
143
+ image_or_dockerfile=plan.image_or_dockerfile,
144
+ host_port=plan.host_port,
145
+ container_port=plan.container_port,
146
+ target=plan.target or "",
147
+ cross=plan.cross,
148
+ health_url=plan.health_url,
149
+ timeout=plan.timeout,
150
+ )
151
+ else:
152
+ runner.dev_flow(
153
+ image_or_dockerfile=plan.image_or_dockerfile,
154
+ host_port=plan.host_port,
155
+ container_port=plan.container_port,
156
+ health_url=plan.health_url,
157
+ timeout=plan.timeout,
158
+ )
159
+ except KeyboardInterrupt:
160
+ pass
161
+ finally:
162
+ runner.cleanup()
163
+
164
+
165
+ def main(argv: Optional[Sequence[str]] = None) -> int:
166
+ args = parse_args(argv)
167
+ plan = create_plan(args)
168
+ if args.print_plan:
169
+ print(json.dumps(plan.to_dict(), sort_keys=True))
170
+ return 0
171
+ execute_plan(plan)
172
+ return 0
173
+
174
+
175
+ if __name__ == "__main__":
176
+ raise SystemExit(main())