Skip to content

Commit c3ba000

Browse files
committed
fix: marketplace name normalization
1 parent 938d55d commit c3ba000

1 file changed

Lines changed: 43 additions & 15 deletions

File tree

app/living_ui/manager.py

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3122,15 +3122,19 @@ async def install_from_marketplace(
31223122
root_prefix = None
31233123
app_prefix = None
31243124

3125+
# Match on path SEGMENTS, never substrings. GitHub names the
3126+
# zip root "{repo}-{ref with / as -}", so a ref named after the
3127+
# app it carries ("feature/invoice-tracker") produces a root
3128+
# folder ENDING in the app id. A substring search then resolves
3129+
# the prefix to the repo root and extracts the whole
3130+
# marketplace, leaving no manifest.json where one is expected.
31253131
for name in zf.namelist():
3132+
parts = name.split("/")
31263133
if root_prefix is None:
3127-
root_prefix = name.split("/")[0] + "/"
3128-
# Look for the app folder: root/{app_id}/
3129-
if f"/{app_id}/" in name:
3130-
if app_prefix is None:
3131-
# Find the prefix up to and including the app folder
3132-
idx = name.index(f"{app_id}/")
3133-
app_prefix = name[: idx + len(app_id) + 1]
3134+
root_prefix = parts[0] + "/"
3135+
# The app folder is exactly root/{app_id}/
3136+
if len(parts) > 2 and parts[1] == app_id:
3137+
app_prefix = f"{root_prefix}{app_id}/"
31343138
break
31353139

31363140
if not app_prefix:
@@ -3179,14 +3183,40 @@ async def install_from_marketplace(
31793183
# projects (root manifest.json, livingUIVersion 2, PocketBase
31803184
# backend). Legacy V1 apps (config/manifest.json, FastAPI
31813185
# backend) are rejected until re-published in the current format.
3186+
# Say WHICH check failed. A missing manifest is usually an
3187+
# extraction/layout fault on our side, not a stale publish, and
3188+
# reporting both as "legacy V1" sends people to fix the wrong repo.
31823189
mf = project_path / "manifest.json"
31833190
is_v2 = False
3184-
if mf.exists():
3191+
reason = ""
3192+
if not mf.exists():
3193+
if (project_path / "config" / "manifest.json").exists():
3194+
# config/manifest.json + FastAPI backend == the real V1.
3195+
reason = (
3196+
f"'{app_id}' is in the legacy V1 format and needs to "
3197+
"be re-published in the current format in the "
3198+
"marketplace"
3199+
)
3200+
else:
3201+
reason = (
3202+
f"no manifest.json at the root of '{app_id}' after "
3203+
f"extraction (looked in {project_path.name})"
3204+
)
3205+
else:
31853206
try:
3186-
is_v2 = json.loads(mf.read_text()).get("livingUIVersion") == 2
3187-
except Exception:
3188-
is_v2 = False
3207+
version = json.loads(mf.read_text()).get("livingUIVersion")
3208+
is_v2 = version == 2
3209+
if not is_v2:
3210+
reason = (
3211+
f"'{app_id}' declares livingUIVersion "
3212+
f"{version!r}; this platform runs 2 (legacy V1 "
3213+
"apps must be re-published in the current format "
3214+
"in the marketplace)"
3215+
)
3216+
except Exception as e:
3217+
reason = f"manifest.json for '{app_id}' is unreadable: {e}"
31893218
if not is_v2:
3219+
logger.error(f"[LIVING_UI:MARKETPLACE] Compatibility gate: {reason}")
31903220
shutil.rmtree(project_path, ignore_errors=True)
31913221
if preserved_hold is not None:
31923222
# Adoption: give the scaffold its requirements/factory
@@ -3201,10 +3231,8 @@ async def install_from_marketplace(
32013231
return {
32023232
"status": "error",
32033233
"error": (
3204-
f"Marketplace app '{app_id}' is in the legacy V1 "
3205-
"format and cannot run on this platform. It "
3206-
"needs to be re-published in the current format in the "
3207-
"marketplace."
3234+
f"Marketplace app '{app_id}' cannot run on this "
3235+
f"platform: {reason}."
32083236
),
32093237
}
32103238

0 commit comments

Comments
 (0)