katalon-cli 0.3.2__tar.gz → 0.3.4__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.
Files changed (21) hide show
  1. {katalon_cli-0.3.2 → katalon_cli-0.3.4}/PKG-INFO +1 -1
  2. {katalon_cli-0.3.2 → katalon_cli-0.3.4}/pyproject.toml +1 -1
  3. {katalon_cli-0.3.2 → katalon_cli-0.3.4}/pyproject.toml.orig +1 -1
  4. {katalon_cli-0.3.2 → katalon_cli-0.3.4}/src/katalon_cli/core/checks.py +29 -0
  5. {katalon_cli-0.3.2 → katalon_cli-0.3.4}/src/katalon_cli/core/docker.py +6 -0
  6. {katalon_cli-0.3.2 → katalon_cli-0.3.4}/src/katalon_cli/main.py +11 -0
  7. {katalon_cli-0.3.2 → katalon_cli-0.3.4}/src/katalon_cli/templates/nginx.conf.j2 +8 -0
  8. {katalon_cli-0.3.2 → katalon_cli-0.3.4}/README.md +0 -0
  9. {katalon_cli-0.3.2 → katalon_cli-0.3.4}/src/katalon_cli/__init__.py +0 -0
  10. {katalon_cli-0.3.2 → katalon_cli-0.3.4}/src/katalon_cli/core/__init__.py +0 -0
  11. {katalon_cli-0.3.2 → katalon_cli-0.3.4}/src/katalon_cli/core/backup.py +0 -0
  12. {katalon_cli-0.3.2 → katalon_cli-0.3.4}/src/katalon_cli/core/compose_gen.py +0 -0
  13. {katalon_cli-0.3.2 → katalon_cli-0.3.4}/src/katalon_cli/core/paths.py +0 -0
  14. {katalon_cli-0.3.2 → katalon_cli-0.3.4}/src/katalon_cli/core/permissions.py +0 -0
  15. {katalon_cli-0.3.2 → katalon_cli-0.3.4}/src/katalon_cli/core/reindex.py +0 -0
  16. {katalon_cli-0.3.2 → katalon_cli-0.3.4}/src/katalon_cli/core/release.py +0 -0
  17. {katalon_cli-0.3.2 → katalon_cli-0.3.4}/src/katalon_cli/core/selfupdate.py +0 -0
  18. {katalon_cli-0.3.2 → katalon_cli-0.3.4}/src/katalon_cli/core/state.py +0 -0
  19. {katalon_cli-0.3.2 → katalon_cli-0.3.4}/src/katalon_cli/py.typed +0 -0
  20. {katalon_cli-0.3.2 → katalon_cli-0.3.4}/src/katalon_cli/templates/Caddyfile.j2 +0 -0
  21. {katalon_cli-0.3.2 → katalon_cli-0.3.4}/src/katalon_cli/templates/compose.yaml.j2 +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: katalon-cli
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Summary: Installer & Updater CLI für Katalon Production-Instanzen
5
5
  Author: Karl Krägelin
6
6
  Author-email: Karl Krägelin <karlkraegelin@gmail.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "katalon-cli"
3
- version = "0.3.2"
3
+ version = "0.3.4"
4
4
  description = "Installer & Updater CLI für Katalon Production-Instanzen"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "katalon-cli"
3
- version = "0.3.2"
3
+ version = "0.3.4"
4
4
  description = "Installer & Updater CLI für Katalon Production-Instanzen"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -9,6 +9,9 @@ from dataclasses import dataclass
9
9
  from pathlib import Path
10
10
 
11
11
  MIN_FREE_DISK_GB = 5
12
+ # Elasticsearch verweigert Shard-Allokation ab 90 % (high watermark).
13
+ MAX_USED_PERCENT = 90
14
+ REAL_FS = {"ext2", "ext3", "ext4", "xfs", "btrfs", "zfs", "f2fs"}
12
15
 
13
16
 
14
17
  @dataclass
@@ -49,6 +52,32 @@ def check_disk_space(path: Path, min_gb: int = MIN_FREE_DISK_GB) -> CheckResult:
49
52
  "Diskspace", ok, f"{free_gb:.1f} GB frei (min. {min_gb} GB) in {path}"
50
53
  )
51
54
 
55
+ def _mount_points(instance_dir: Path) -> list[Path]:
56
+ """Alle echten Dateisysteme des Hosts; ohne /proc/mounts Instanzverzeichnis und `/`."""
57
+ try:
58
+ lines = Path("/proc/mounts").read_text().splitlines()
59
+ except OSError:
60
+ return [instance_dir, Path("/")]
61
+ return [Path(f[1].replace("\\040", " ")) for f in map(str.split, lines) if f[2] in REAL_FS]
62
+
63
+
64
+ def check_server_disk(instance_dir: Path, max_used: int = MAX_USED_PERCENT) -> CheckResult:
65
+ full = []
66
+ for mount in _mount_points(instance_dir):
67
+ try:
68
+ usage = shutil.disk_usage(mount)
69
+ except OSError:
70
+ continue
71
+ used = usage.used * 100 / (usage.used + usage.free)
72
+ if used >= max_used:
73
+ full.append(f"{mount} {used:.0f} %")
74
+ if full:
75
+ return CheckResult(
76
+ "Server-Disk", False, f"≥ {max_used} % belegt: {', '.join(full)} — Elasticsearch blockiert ab 90 %"
77
+ )
78
+ return CheckResult("Server-Disk", True, f"alle Dateisysteme < {max_used} % belegt")
79
+
80
+
52
81
  def check_openssl() -> CheckResult:
53
82
  if not shutil.which("openssl"):
54
83
  return CheckResult("OpenSSL", False, "nicht gefunden — für TLS-Modus 'standalone' erforderlich")
@@ -45,6 +45,12 @@ def compose(
45
45
  )
46
46
 
47
47
 
48
+ def reload_nginx(instance_dir: Path) -> None:
49
+ """`up -d` erstellt nginx nicht neu, wenn nur die gemountete nginx.conf sich ändert."""
50
+ compose(instance_dir, "exec", "-T", "nginx", "nginx", "-t")
51
+ compose(instance_dir, "exec", "-T", "nginx", "nginx", "-s", "reload")
52
+
53
+
48
54
  def is_healthy(instance_dir: Path, service: str = "api") -> bool:
49
55
  result = compose(
50
56
  instance_dir, "ps", "--format", "json", service, check=False, capture=True
@@ -689,6 +689,7 @@ def update(
689
689
  ):
690
690
  """Update auf neue Version — Backup zuerst, immer."""
691
691
  dir = instance_dir_or_raise(dir)
692
+ _require_disk(dir)
692
693
  state = InstallationState.load(dir)
693
694
  try:
694
695
  meta = release.get_release(target) if target else release.get_latest_release()
@@ -754,6 +755,7 @@ def update(
754
755
  ("Medienrechte prüfen", lambda: _ensure_media_permissions(dir)),
755
756
  # DB-Migration läuft automatisch im api-Container-Entrypoint beim Start.
756
757
  ("Container neu starten", lambda: docker.compose(dir, "up", "-d")),
758
+ ("nginx-Konfiguration neu laden", lambda: docker.reload_nginx(dir)),
757
759
  ]
758
760
 
759
761
  with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), console=console) as progress:
@@ -843,10 +845,19 @@ def rollback(
843
845
 
844
846
  _ensure_media_permissions(dir)
845
847
  docker.compose(dir, "up", "-d")
848
+ docker.reload_nginx(dir)
846
849
  console.print("[green]✔[/] Rollback abgeschlossen.")
847
850
 
848
851
 
852
+ def _require_disk(dir: Path) -> None:
853
+ result = checks.check_server_disk(dir)
854
+ if not result.ok:
855
+ console.print(f"[red]✖[/] {result.name}: {result.detail}")
856
+ raise typer.Exit(1)
857
+
858
+
849
859
  def _run_reindex(dir: Path, wait: bool = True) -> None:
860
+ _require_disk(dir)
850
861
  if not docker.is_healthy(dir, "api") or not docker.is_running(dir, "worker"):
851
862
  console.print("[red]✖[/] API oder Worker laufen nicht — erst `katalon start`.")
852
863
  raise typer.Exit(1)
@@ -19,11 +19,15 @@ server {
19
19
  {% endif %}
20
20
  client_max_body_size 500m;
21
21
  absolute_redirect off;
22
+ server_tokens off;
22
23
  add_header Content-Security-Policy "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'self'; form-action 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data: blob: https:; media-src 'self' blob: https:; connect-src 'self' https:; frame-src 'self' https://www.openstreetmap.org; worker-src 'self' blob:" always;
23
24
  add_header Referrer-Policy "strict-origin-when-cross-origin" always;
24
25
  add_header X-Content-Type-Options "nosniff" always;
25
26
  add_header X-Frame-Options "SAMEORIGIN" always;
26
27
  add_header Permissions-Policy "camera=(), geolocation=(), microphone=()" always;
28
+ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
29
+ add_header Cross-Origin-Opener-Policy "same-origin" always;
30
+ add_header Cross-Origin-Resource-Policy "same-origin" always;
27
31
 
28
32
  location /api/ {
29
33
  set $upstream_api api:8000;
@@ -97,6 +101,10 @@ server {
97
101
  add_header X-Content-Type-Options "nosniff" always;
98
102
  add_header X-Frame-Options "SAMEORIGIN" always;
99
103
  add_header Permissions-Policy "camera=(), geolocation=(), microphone=()" always;
104
+ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
105
+ add_header Cross-Origin-Opener-Policy "same-origin" always;
106
+ # IIIF images are meant to be embedded by other sites; access is decided by auth_request.
107
+ add_header Cross-Origin-Resource-Policy "cross-origin" always;
100
108
  }
101
109
 
102
110
  location = /internal/media-authorize {
File without changes