Skip to content

Commit 3d8e6bc

Browse files
committed
feat: verify live Store pages
1 parent 59853e6 commit 3d8e6bc

7 files changed

Lines changed: 175 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## Unreleased
44

5+
- Added an opt-in `store-materials --live-pages` gate that checks the configured
6+
privacy and support URLs over HTTPS. GitHub Pages is now enabled for the
7+
repository and both public Store routes are deployed.
58
- Added a conservative runtime MCP reaper for duplicate process generations left
69
under the Codex Store desktop app-server. It groups launches by Codex's direct
710
`node_repl.exe` runtime anchors, always protects the newest launch cohort, requires

README.de.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ Das Projekt enthält die Grundlage für den Windows Store:
154154
- `docs/privacy.md`
155155
- `docs/support.md`
156156

157-
Geplante öffentliche Store-Ziele:
157+
Öffentliche Store-Seiten:
158158

159159
- Datenschutz: `https://dev-bricks.github.io/CareCenter-for-Codex/privacy`
160160
- Support: `https://dev-bricks.github.io/CareCenter-for-Codex/support`
@@ -163,10 +163,13 @@ Validieren mit:
163163

164164
```powershell
165165
python -m codex_logdatenbank_wartung.cli store-materials
166+
python -m codex_logdatenbank_wartung.cli store-materials --live-pages
166167
python -m codex_logdatenbank_wartung.cli store-materials --exe-path C:\_Local_DEV\codex-maintenance\bin
167168
```
168169

169-
Ohne `--exe-path` versucht der Check, die gebaute EXE automatisch aus `build_exe.bat` (`DIST_DIR`) zu finden. Mit `--exe-path` kann entweder die konkrete `.exe` oder nur der Build-Ordner übergeben werden.
170+
Mit `--live-pages` wird das externe Release-Gate geprüft: Der Befehl ruft beide konfigurierten Store-URLs über HTTPS ab und meldet nicht erreichbare Seiten als Warnung. Ohne `--exe-path` versucht der Check, die gebaute EXE automatisch aus `build_exe.bat` (`DIST_DIR`) zu finden. Mit `--exe-path` kann entweder die konkrete `.exe` oder nur der Build-Ordner übergeben werden.
171+
172+
Der Check baut die statischen GitHub-Pages-Dateien außerdem temporär und prüft `privacy/index.html`, `support/index.html`, `index.html` sowie den Build-Marker. Der aktive Workflow `.github/workflows/pages.yml` veröffentlicht die Routen `/privacy/` und `/support/` über GitHub Pages.
170173

171174
## Entwicklung
172175

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ The project includes Windows Store groundwork:
166166
- `docs/privacy.md`
167167
- `docs/support.md`
168168

169-
Planned public Store targets:
169+
Public Store pages:
170170

171171
- Privacy: `https://dev-bricks.github.io/CareCenter-for-Codex/privacy`
172172
- Support: `https://dev-bricks.github.io/CareCenter-for-Codex/support`
@@ -175,18 +175,19 @@ Validate them with:
175175

176176
```powershell
177177
python -m codex_logdatenbank_wartung.cli store-materials
178+
python -m codex_logdatenbank_wartung.cli store-materials --live-pages
178179
python -m codex_logdatenbank_wartung.cli store-materials --exe-path C:\_Local_DEV\codex-maintenance\bin
179180
```
180181

181-
Without `--exe-path`, the check tries to discover the built EXE automatically from `build_exe.bat` (`DIST_DIR`). With `--exe-path`, you can pass either the exact `.exe` file or just the build directory.
182+
Use `--live-pages` for the external release gate: it requests both configured Store URLs over HTTPS and reports unreachable pages as warnings. Without `--exe-path`, the check tries to discover the built EXE automatically from `build_exe.bat` (`DIST_DIR`). With `--exe-path`, you can pass either the exact `.exe` file or just the build directory.
182183

183184
The Store privacy/support URLs are prepared for GitHub Pages. `store-materials` also runs a temporary static Pages build and verifies `privacy/index.html`, `support/index.html`, `index.html`, and the build marker. You can still build the artifact explicitly with:
184185

185186
```powershell
186187
python scripts\build_store_pages.py --output _site
187188
```
188189

189-
The workflow `.github/workflows/pages.yml` publishes the generated `/privacy/` and `/support/` routes after GitHub Pages is configured to use GitHub Actions for this repository.
190+
The active workflow `.github/workflows/pages.yml` publishes the generated `/privacy/` and `/support/` routes through GitHub Pages.
190191

191192
## Development
192193

src/codex_logdatenbank_wartung/cli.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,11 @@ def cmd_store_materials(args: argparse.Namespace) -> int:
144144
from .store_release import validate_store_materials
145145

146146
exe_path = Path(args.exe_path) if args.exe_path else None
147-
report = validate_store_materials(project_root=Path(args.project_root), exe_path=exe_path)
147+
report = validate_store_materials(
148+
project_root=Path(args.project_root),
149+
exe_path=exe_path,
150+
check_live_pages=args.live_pages,
151+
)
148152
print(report.to_text())
149153
return {"ok": 0, "warning": 2, "failed": 1}[report.status]
150154

@@ -443,6 +447,11 @@ def build_parser() -> argparse.ArgumentParser:
443447
default=None,
444448
help="Optionaler Pfad zur gebauten EXE fuer einen konkreten Existenzcheck.",
445449
)
450+
store_materials_parser.add_argument(
451+
"--live-pages",
452+
action="store_true",
453+
help="Oeffentliche Privacy-/Support-URLs per HTTPS pruefen.",
454+
)
446455
store_materials_parser.set_defaults(func=cmd_store_materials)
447456

448457
store_screenshot_parser = subparsers.add_parser(

src/codex_logdatenbank_wartung/store_release.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
from importlib import util as importlib_util
1111
from pathlib import Path
1212
from types import ModuleType
13+
from urllib import error as urlerror
14+
from urllib import request as urlrequest
1315
from urllib.parse import urlparse
1416

1517
PROJECT_ROOT = Path(__file__).resolve().parents[2]
@@ -151,6 +153,40 @@ def _check_urls(payload: dict[str, object]) -> StoreCheck:
151153
return StoreCheck("Store-URLs", "ok", " / ".join(ok_fields))
152154

153155

156+
def _check_live_store_pages(
157+
payload: dict[str, object],
158+
timeout_seconds: float = 10.0,
159+
) -> StoreCheck:
160+
problems: list[str] = []
161+
ok_fields: list[str] = []
162+
163+
for field in URL_FIELDS:
164+
raw = str(payload.get(field, "")).strip()
165+
if not raw:
166+
problems.append(f"{field} fehlt")
167+
continue
168+
169+
request = urlrequest.Request(
170+
raw,
171+
headers={"User-Agent": "CareCenter-for-Codex store-readiness"},
172+
)
173+
try:
174+
with urlrequest.urlopen(request, timeout=timeout_seconds) as response:
175+
status = int(response.status)
176+
except (urlerror.HTTPError, urlerror.URLError, TimeoutError, OSError) as exc:
177+
problems.append(f"{field}: {exc}")
178+
continue
179+
180+
if status != 200:
181+
problems.append(f"{field}: HTTP {status}")
182+
continue
183+
ok_fields.append(field)
184+
185+
if problems:
186+
return StoreCheck("Store-Webseiten-Live", "warning", "; ".join(problems))
187+
return StoreCheck("Store-Webseiten-Live", "ok", " / ".join(ok_fields))
188+
189+
154190
def _check_docs(project_root: Path) -> StoreCheck:
155191
missing = [name for name in STORE_DOCS if not (project_root / name).exists()]
156192
if missing:
@@ -377,6 +413,7 @@ def _check_executable(project_root: Path, payload: dict[str, object], exe_path:
377413
def validate_store_materials(
378414
project_root: Path = PROJECT_ROOT,
379415
exe_path: Path | None = None,
416+
check_live_pages: bool = False,
380417
) -> StoreMaterialsReport:
381418
checks: list[StoreCheck] = []
382419
payload, config_check = _load_store_package(project_root / STORE_PACKAGE_PATH.name)
@@ -391,6 +428,8 @@ def validate_store_materials(
391428
checks.append(_check_docs(project_root))
392429
checks.append(_check_published_store_docs(project_root, payload))
393430
checks.append(_check_store_pages_build(project_root))
431+
if check_live_pages:
432+
checks.append(_check_live_store_pages(payload))
394433
checks.append(_check_screenshot(project_root))
395434
checks.append(_check_executable(project_root, payload, exe_path))
396435
return StoreMaterialsReport(checks)

tests/test_cli.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from pathlib import Path
77
from unittest.mock import patch
88

9-
from codex_logdatenbank_wartung.cli import main
9+
from codex_logdatenbank_wartung.cli import build_parser, main
1010

1111

1212
def test_init_config_writes_on_fresh_system(tmp_path: Path, capsys) -> None:
@@ -81,6 +81,12 @@ def test_store_materials_command_returns_warning_for_missing_files(tmp_path: Pat
8181
assert "Store-Materialien" in out
8282

8383

84+
def test_store_materials_parser_accepts_live_pages_flag() -> None:
85+
args = build_parser().parse_args(["store-materials", "--live-pages"])
86+
87+
assert args.live_pages is True
88+
89+
8490
def test_store_screenshot_command_writes_png(tmp_path: Path, capsys, monkeypatch) -> None:
8591
cfg = tmp_path / "config.json"
8692
cfg.write_text("{}", encoding="utf-8")

tests/test_store_release.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import json
44
from pathlib import Path
55

6+
from codex_logdatenbank_wartung import store_release
67
from codex_logdatenbank_wartung.store_release import validate_store_materials
78

89

@@ -97,6 +98,112 @@ def test_validate_store_materials_reports_ok_for_complete_materials(tmp_path: Pa
9798
assert pages_build_check.status == "ok"
9899

99100

101+
def test_validate_store_materials_checks_live_pages_on_request(
102+
tmp_path: Path,
103+
monkeypatch,
104+
) -> None:
105+
class FakeResponse:
106+
status = 200
107+
108+
def __enter__(self):
109+
return self
110+
111+
def __exit__(self, *_args) -> None:
112+
return None
113+
114+
requested_urls: list[str] = []
115+
116+
def fake_urlopen(request, *, timeout: float):
117+
requested_urls.append(request.full_url)
118+
assert timeout == 10.0
119+
return FakeResponse()
120+
121+
monkeypatch.setattr(store_release.urlrequest, "urlopen", fake_urlopen)
122+
exe_path = tmp_path / "CareCenterForCodex.exe"
123+
exe_path.write_bytes(b"exe")
124+
_write_store_files(
125+
tmp_path,
126+
{
127+
"app_name": "CareCenter for Codex",
128+
"publisher": "CN=01234567-89AB-CDEF-0123-456789ABCDEF",
129+
"publisher_display": "Lukas Geiger",
130+
"identity_name": "LukasGeiger.CareCenterForCodex",
131+
"version": "0.8.0.0",
132+
"description": "Offline Wartung und Reparatur fuer die Codex-Desktop-App.",
133+
"executable": "CareCenterForCodex.exe",
134+
"capabilities": "runFullTrust",
135+
"category": "Developer Tools",
136+
"age_rating": "3+",
137+
"privacy_url": "https://dev-bricks.github.io/CareCenter-for-Codex/privacy",
138+
"support_url": "https://dev-bricks.github.io/CareCenter-for-Codex/support",
139+
},
140+
)
141+
142+
report = validate_store_materials(
143+
project_root=tmp_path,
144+
exe_path=exe_path,
145+
check_live_pages=True,
146+
)
147+
148+
live_check = next(check for check in report.checks if check.name == "Store-Webseiten-Live")
149+
assert live_check.status == "ok"
150+
assert requested_urls == [
151+
"https://dev-bricks.github.io/CareCenter-for-Codex/privacy",
152+
"https://dev-bricks.github.io/CareCenter-for-Codex/support",
153+
]
154+
155+
156+
def test_validate_store_materials_warns_when_live_page_is_missing(
157+
tmp_path: Path,
158+
monkeypatch,
159+
) -> None:
160+
class FakeResponse:
161+
def __init__(self, status: int) -> None:
162+
self.status = status
163+
164+
def __enter__(self):
165+
return self
166+
167+
def __exit__(self, *_args) -> None:
168+
return None
169+
170+
def fake_urlopen(request, *, timeout: float):
171+
del timeout
172+
status = 404 if request.full_url.endswith("/support") else 200
173+
return FakeResponse(status)
174+
175+
monkeypatch.setattr(store_release.urlrequest, "urlopen", fake_urlopen)
176+
exe_path = tmp_path / "CareCenterForCodex.exe"
177+
exe_path.write_bytes(b"exe")
178+
_write_store_files(
179+
tmp_path,
180+
{
181+
"app_name": "CareCenter for Codex",
182+
"publisher": "CN=01234567-89AB-CDEF-0123-456789ABCDEF",
183+
"publisher_display": "Lukas Geiger",
184+
"identity_name": "LukasGeiger.CareCenterForCodex",
185+
"version": "0.8.0.0",
186+
"description": "Offline Wartung und Reparatur fuer die Codex-Desktop-App.",
187+
"executable": "CareCenterForCodex.exe",
188+
"capabilities": "runFullTrust",
189+
"category": "Developer Tools",
190+
"age_rating": "3+",
191+
"privacy_url": "https://dev-bricks.github.io/CareCenter-for-Codex/privacy",
192+
"support_url": "https://dev-bricks.github.io/CareCenter-for-Codex/support",
193+
},
194+
)
195+
196+
report = validate_store_materials(
197+
project_root=tmp_path,
198+
exe_path=exe_path,
199+
check_live_pages=True,
200+
)
201+
202+
live_check = next(check for check in report.checks if check.name == "Store-Webseiten-Live")
203+
assert live_check.status == "warning"
204+
assert "support_url: HTTP 404" in live_check.message
205+
206+
100207
def test_validate_store_materials_auto_detects_built_exe_from_build_script(tmp_path: Path) -> None:
101208
dist_dir = tmp_path / "_local" / "bin"
102209
dist_dir.mkdir(parents=True)

0 commit comments

Comments
 (0)