Created install.sh and build.sh

Also refactored methods to work as a zipapp
This commit is contained in:
2025-08-08 18:27:28 +00:00
parent d334ec4ada
commit f108bab2f9
5 changed files with 88 additions and 6 deletions

2
build.sh vendored
View File

@@ -1,3 +1,5 @@
#!/usr/bin/env sh #!/usr/bin/env sh
# uv export > requirements.txt # uv export > requirements.txt
# mkdir if not exists release
mkdir -p release
python -m zipapp src --compress --output=release/pyshelf --python="/usr/bin/env python" python -m zipapp src --compress --output=release/pyshelf --python="/usr/bin/env python"

8
instal.sh vendored Executable file
View File

@@ -0,0 +1,8 @@
cd /tmp/
git clone https://github.com/th3r00t/pyShelf.git
cd pyshelf
git checkout 0.8.0--dev-zipapp
./build.sh
sudo cp ./src/frontend/static /var/lib/pyshelf/assets -r
sudo cp ./src/frontend/templates /var/lib/pyshelf/assets -r
sudo cp ./release/pyshelf /usr/local/bin/pyshelf

View File

@@ -16,10 +16,13 @@ from fastapi.templating import Jinja2Templates
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from backend.lib.storage import Storage from backend.lib.storage import Storage
from .objects import JSInterface from .objects import JSInterface
from .runtime_paths import ensure_assets
from backend.lib.config import Config from backend.lib.config import Config
app = FastAPI() app = FastAPI()
templates = Jinja2Templates(directory="src/frontend/templates") STATIC_DIR, TEMPLATES_DIR = ensure_assets()
templates = Jinja2Templates(directory=str(TEMPLATES_DIR))
# templates = Jinja2Templates(directory="src/frontend/templates")
origins = [ origins = [
"http://localhost", "http://localhost",
"http://localhost:8081", "http://localhost:8081",
@@ -34,7 +37,6 @@ app.add_middleware(
allow_headers=["*"], allow_headers=["*"],
) )
def base64decode(string) -> str: def base64decode(string) -> str:
"""Decode a base64 string.""" """Decode a base64 string."""
try: try:
@@ -134,9 +136,10 @@ class FastAPIServer():
def __init__(self, config): def __init__(self, config):
"""Initialize FastAPIServer object parameters.""" """Initialize FastAPIServer object parameters."""
self.config = config self.config = config
app.mount("/static", app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
StaticFiles(directory="src/frontend/static"), # app.mount("/static",
name="static") # StaticFiles(directory="src/frontend/static"),
# name="static")
self.fe_config = uvicorn.Config(app, host="0.0.0.0", port=8080, self.fe_config = uvicorn.Config(app, host="0.0.0.0", port=8080,
log_level="info", reload=True) log_level="info", reload=True)
self.fe_server = uvicorn.Server(self.fe_config) self.fe_server = uvicorn.Server(self.fe_config)

View File

@@ -1,7 +1,7 @@
"""pyShelf's Frontend Objects.""" """pyShelf's Frontend Objects."""
from subprocess import run from subprocess import run
from pathlib import Path from pathlib import Path
from src.backend.lib.config import Config from backend.lib.config import Config
class JSInterface(): class JSInterface():

69
src/frontend/lib/runtime_paths.py vendored Normal file
View File

@@ -0,0 +1,69 @@
# src/frontend/lib/runtime_paths.py
from __future__ import annotations
import os, sys, shutil
from pathlib import Path
from importlib import resources
ASSET_TOPS = ("static", "templates")
def _inside_zipapp() -> bool:
# zipapps can be a single file (…/pyshelf.pyz) or an executable file without .pyz
# When invoked, sys.argv[0] is the archive path; treat non-directory as zipapp
return not Path(sys.argv[0]).is_dir()
def assets_root() -> Path:
"""
Directory that *contains* static/ and templates/.
Priority:
1) PYSHELF_ASSETS
2) ./pyshelf (sibling dir next to the archive) when running as zipapp
3) frontend/ (package dir) when running from source/unpacked tree
"""
env = os.environ.get("PYSHELF_ASSETS")
if env:
return Path(env)
if _inside_zipapp():
# e.g. /opt/pyshelf/pyshelf -> use /opt/pyshelf/pyshelf{,/static,/templates}
base = Path(sys.argv[0]).resolve()
# strip suffix like ".pyz" if present to get a nice folder name
return base.with_suffix("")
# Dev/regular run: __file__ = …/frontend/lib/runtime_paths.py => parents[1] == …/frontend
return Path(__file__).resolve().parents[1]
def _copy_traversable_tree(src_trav, dst_dir: Path) -> None:
"""Recursively copy a Traversable (importlib.resources) tree to dst_dir."""
for child in src_trav.iterdir():
target = dst_dir / child.name
if child.is_dir():
target.mkdir(parents=True, exist_ok=True)
_copy_traversable_tree(child, target)
else:
target.parent.mkdir(parents=True, exist_ok=True)
with child.open("rb") as r, open(target, "wb") as w:
shutil.copyfileobj(r, w)
def ensure_assets() -> tuple[Path, Path]:
"""
Ensure static/ and templates/ exist on disk and return their paths.
If running from zipapp and they don't exist yet, extract packaged copies.
"""
root = assets_root()
static_dir = root / "static"
tmpl_dir = root / "templates"
# If both already exist, use them (works in repo tree and next to .pyz)
if static_dir.exists() and tmpl_dir.exists():
return static_dir, tmpl_dir
# Extract from package data into root/{static,templates}
pkg = "frontend" # package that contains 'static' and 'templates'
for top in ASSET_TOPS:
src = resources.files(pkg) / top # Traversable
dst = root / top
dst.mkdir(parents=True, exist_ok=True)
_copy_traversable_tree(src, dst)
return static_dir, tmpl_dir