Spaces:
Running
Running
Upload 7 files
Browse files
app.py
CHANGED
|
@@ -392,6 +392,22 @@ def detect_type(src: Path) -> str:
|
|
| 392 |
has_fw = False
|
| 393 |
if has_web_entry or has_fw or has_pkg:
|
| 394 |
return "hybrid-capacitor"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 395 |
if (src / "config.xml").exists() and (src / "package.json").exists():
|
| 396 |
return "hybrid-cordova"
|
| 397 |
if list(src.rglob("app/build.gradle")) or list(src.rglob("app/build.gradle.kts")):
|
|
@@ -1641,6 +1657,59 @@ async def run_build(job_id: str, build_id: str, task: str, signing: dict, app_co
|
|
| 1641 |
log(f"WARNING: capacitor dev server.url={server_url} — remove it for production builds, else the app loads your machine instead of the bundled web")
|
| 1642 |
except Exception as e:
|
| 1643 |
log(f"capacitor config note: {e} — using webDir={web_dir_name}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1644 |
# --- env preflight (versions only) + JDK gate (Cap7+ cannot compile on JDK17) ---
|
| 1645 |
try:
|
| 1646 |
res = await _run_cmd(["node", "-v"], timeout=15)
|
|
@@ -1782,13 +1851,22 @@ async def run_build(job_id: str, build_id: str, task: str, signing: dict, app_co
|
|
| 1782 |
# used as-is (extra files under android/ preserved).
|
| 1783 |
try:
|
| 1784 |
andra = work_base / "android"
|
| 1785 |
-
|
|
|
|
|
|
|
|
|
|
| 1786 |
baked_bytes = None
|
| 1787 |
-
|
| 1788 |
-
|
| 1789 |
-
|
| 1790 |
-
|
| 1791 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1792 |
shell_complete = (
|
| 1793 |
(andra / "app" / "build.gradle").is_file()
|
| 1794 |
or (andra / "app" / "build.gradle.kts").is_file()
|
|
@@ -1809,7 +1887,10 @@ async def run_build(job_id: str, build_id: str, task: str, signing: dict, app_co
|
|
| 1809 |
log("cap: local binary missing — using npx @capacitor/cli")
|
| 1810 |
if not andra.exists() or not shell_complete:
|
| 1811 |
if baked_bytes is not None:
|
| 1812 |
-
|
|
|
|
|
|
|
|
|
|
| 1813 |
else:
|
| 1814 |
log("android/ missing or incomplete — running cap add android")
|
| 1815 |
# Move the partial shell aside (never rmtree): on `cap add`
|
|
@@ -1826,7 +1907,16 @@ async def run_build(job_id: str, build_id: str, task: str, signing: dict, app_co
|
|
| 1826 |
res = await _run_cmd(cap_add, cwd=work_base, timeout=180)
|
| 1827 |
if res.returncode != 0:
|
| 1828 |
try:
|
| 1829 |
-
if partial_bak.exists()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1830 |
partial_bak.rename(andra)
|
| 1831 |
log("Restored partial android/ after cap add failure")
|
| 1832 |
except Exception:
|
|
|
|
| 392 |
has_fw = False
|
| 393 |
if has_web_entry or has_fw or has_pkg:
|
| 394 |
return "hybrid-capacitor"
|
| 395 |
+
# Flat baked manifest: AndroidManifest.xml at project root next to web
|
| 396 |
+
# files (same level as index.html / capacitor config). Same baked-perms
|
| 397 |
+
# intent as the partial-shell path above, minus the scaffolding. Gated on
|
| 398 |
+
# a web entry so stray files alone never hijack routing; native uploads
|
| 399 |
+
# keep their manifests under android/... and never match here.
|
| 400 |
+
if (src / "AndroidManifest.xml").is_file():
|
| 401 |
+
try:
|
| 402 |
+
has_flat_web = (
|
| 403 |
+
(src / "index.html").exists()
|
| 404 |
+
or (src / "www" / "index.html").exists()
|
| 405 |
+
or any(p.suffix == ".html" for p in src.rglob("*.html"))
|
| 406 |
+
)
|
| 407 |
+
except Exception:
|
| 408 |
+
has_flat_web = False
|
| 409 |
+
if has_flat_web:
|
| 410 |
+
return "hybrid-capacitor"
|
| 411 |
if (src / "config.xml").exists() and (src / "package.json").exists():
|
| 412 |
return "hybrid-cordova"
|
| 413 |
if list(src.rglob("app/build.gradle")) or list(src.rglob("app/build.gradle.kts")):
|
|
|
|
| 1657 |
log(f"WARNING: capacitor dev server.url={server_url} — remove it for production builds, else the app loads your machine instead of the bundled web")
|
| 1658 |
except Exception as e:
|
| 1659 |
log(f"capacitor config note: {e} — using webDir={web_dir_name}")
|
| 1660 |
+
# --- webDir "." normalization (Capacitor CLI rejects it by design:
|
| 1661 |
+
# checkWebDir invalidFolders = ['', '.', '..', '../', './'] — copying
|
| 1662 |
+
# the project root would swallow android/ itself. Materialize www/
|
| 1663 |
+
# in the disposable work copy (the upload is never touched) and point
|
| 1664 |
+
# the work-copy config at it. Meaning-preserving: identical files,
|
| 1665 |
+
# CLI-accepted directory. Baked-manifest handling below is unchanged.
|
| 1666 |
+
try:
|
| 1667 |
+
if web_dir_name.strip() in (".", "./", ""):
|
| 1668 |
+
www = work_base / "www"
|
| 1669 |
+
www.mkdir(parents=True, exist_ok=True)
|
| 1670 |
+
skip = {"android", "android.partial_bak", "node_modules",
|
| 1671 |
+
"package.json", "package-lock.json",
|
| 1672 |
+
"capacitor.config.json", "capacitor.config.ts",
|
| 1673 |
+
"capacitor.config.js", "AndroidManifest.xml"}
|
| 1674 |
+
copied = 0
|
| 1675 |
+
for child in work_base.iterdir():
|
| 1676 |
+
if child.name in skip or child.name.startswith("."):
|
| 1677 |
+
continue
|
| 1678 |
+
dest = www / child.name
|
| 1679 |
+
if dest.exists():
|
| 1680 |
+
continue
|
| 1681 |
+
try:
|
| 1682 |
+
if child.is_dir() and not child.is_symlink():
|
| 1683 |
+
shutil.copytree(child, dest, symlinks=False, ignore_dangling_symlinks=True)
|
| 1684 |
+
elif child.is_file():
|
| 1685 |
+
shutil.copy2(child, dest)
|
| 1686 |
+
else:
|
| 1687 |
+
continue
|
| 1688 |
+
copied += 1
|
| 1689 |
+
except Exception as ce:
|
| 1690 |
+
log(f"webDir normalize skip {child.name}: {ce}")
|
| 1691 |
+
if cfg_json.exists():
|
| 1692 |
+
try:
|
| 1693 |
+
data = json.loads(cfg_json.read_text(encoding="utf-8", errors="ignore"))
|
| 1694 |
+
data["webDir"] = "www"
|
| 1695 |
+
cfg_json.write_text(json.dumps(data, indent=2), encoding="utf-8")
|
| 1696 |
+
except Exception as je:
|
| 1697 |
+
log(f"webDir config rewrite note: {je}")
|
| 1698 |
+
else:
|
| 1699 |
+
for cn in ("capacitor.config.ts", "capacitor.config.js"):
|
| 1700 |
+
cp = work_base / cn
|
| 1701 |
+
if cp.is_file():
|
| 1702 |
+
try:
|
| 1703 |
+
ctxt = cp.read_text(encoding="utf-8", errors="ignore")
|
| 1704 |
+
ntxt = re.sub(r"(webDir\s*:\s*)['\"][^'\"]*['\"]", r'\1"www"', ctxt, count=1)
|
| 1705 |
+
if ntxt != ctxt:
|
| 1706 |
+
cp.write_text(ntxt, encoding="utf-8")
|
| 1707 |
+
except Exception as te:
|
| 1708 |
+
log(f"webDir config rewrite note: {te}")
|
| 1709 |
+
web_dir_name = "www"
|
| 1710 |
+
log(f"webDir '.' is rejected by Capacitor CLI — normalized root web assets into www/ ({copied} entries) in work copy, upload untouched")
|
| 1711 |
+
except Exception as e:
|
| 1712 |
+
log(f"webDir normalize note: {e}")
|
| 1713 |
# --- env preflight (versions only) + JDK gate (Cap7+ cannot compile on JDK17) ---
|
| 1714 |
try:
|
| 1715 |
res = await _run_cmd(["node", "-v"], timeout=15)
|
|
|
|
| 1851 |
# used as-is (extra files under android/ preserved).
|
| 1852 |
try:
|
| 1853 |
andra = work_base / "android"
|
| 1854 |
+
# Baked manifest sources, in order: partial-shell manifest, then
|
| 1855 |
+
# a flat AndroidManifest.xml at project root next to the web
|
| 1856 |
+
# files (same baked-perms intent, minus scaffolding). First hit
|
| 1857 |
+
# wins; the restore below is source-agnostic.
|
| 1858 |
baked_bytes = None
|
| 1859 |
+
baked_src = ""
|
| 1860 |
+
for cand in (andra / "app" / "src" / "main" / "AndroidManifest.xml",
|
| 1861 |
+
work_base / "AndroidManifest.xml"):
|
| 1862 |
+
if cand.is_file():
|
| 1863 |
+
try:
|
| 1864 |
+
baked_bytes = cand.read_bytes()
|
| 1865 |
+
baked_src = str(cand.relative_to(work_base))
|
| 1866 |
+
break
|
| 1867 |
+
except Exception:
|
| 1868 |
+
baked_bytes = None
|
| 1869 |
+
baked_src = ""
|
| 1870 |
shell_complete = (
|
| 1871 |
(andra / "app" / "build.gradle").is_file()
|
| 1872 |
or (andra / "app" / "build.gradle.kts").is_file()
|
|
|
|
| 1887 |
log("cap: local binary missing — using npx @capacitor/cli")
|
| 1888 |
if not andra.exists() or not shell_complete:
|
| 1889 |
if baked_bytes is not None:
|
| 1890 |
+
if baked_src == "AndroidManifest.xml":
|
| 1891 |
+
log("Root-level AndroidManifest.xml found alongside web files — cap add builds the shell first, your manifest replaces the generated one afterwards")
|
| 1892 |
+
else:
|
| 1893 |
+
log("Partial baked android/ detected (manifest without Gradle shell) — cap add builds the shell first, baked manifest replaces the generated one afterwards")
|
| 1894 |
else:
|
| 1895 |
log("android/ missing or incomplete — running cap add android")
|
| 1896 |
# Move the partial shell aside (never rmtree): on `cap add`
|
|
|
|
| 1907 |
res = await _run_cmd(cap_add, cwd=work_base, timeout=180)
|
| 1908 |
if res.returncode != 0:
|
| 1909 |
try:
|
| 1910 |
+
if partial_bak.exists():
|
| 1911 |
+
# Failed `cap add` can leave a half-created android/
|
| 1912 |
+
# behind (skeleton is made before the copy step that
|
| 1913 |
+
# failed). That output is disposable; the moved-aside
|
| 1914 |
+
# original is the only thing worth keeping. The rmtree
|
| 1915 |
+
# is safe exactly because partial_bak exists — it proves
|
| 1916 |
+
# the original was moved aside, so current android/
|
| 1917 |
+
# can only be cap's failed output, never user files.
|
| 1918 |
+
if andra.exists():
|
| 1919 |
+
shutil.rmtree(andra, ignore_errors=True)
|
| 1920 |
partial_bak.rename(andra)
|
| 1921 |
log("Restored partial android/ after cap add failure")
|
| 1922 |
except Exception:
|