import shutil import importlib.util from pathlib import Path import PyInstaller.__main__ import typer from rich import print app = typer.Typer() @app.command() def build(build: bool = True, copy: bool = True, test: bool = False, dest: str | None = None): """Windows-focused build script. - `--test` creates a one-file test binary in `dist\test`. - `--copy` (default) copies the artifact to the shared network path used previously. """ project_root = Path(__file__).resolve().parent if build: if test: print("Building Windows test one-file bundle with PyInstaller") # On Windows use ';' as add-data separator (src;dest) # Use absolute path for the icon folder so PyInstaller can find it icon_src = str(project_root / "icon") add_data_args = ["--add-data", f"{icon_src};icon"] # Try to include dicognito package data (release_notes.md) which # PyInstaller may miss. This prevents runtime FileNotFoundError for # dicognito/release_notes.md when the package expects the file at runtime. try: spec = importlib.util.find_spec("dicognito") if spec and spec.submodule_search_locations: dicognito_path = Path(spec.submodule_search_locations[0]) release_notes = dicognito_path / "release_notes.md" if release_notes.exists(): # PyInstaller on Windows expects 'src;dest' add_data_args += ["--add-data", f"{str(release_notes)};dicognito"] else: # include whole package folder if the single file wasn't found add_data_args += ["--add-data", f"{str(dicognito_path)};dicognito"] except Exception as e: print(f"Warning: couldn't locate dicognito package: {e}") PyInstaller.__main__.run([ str(project_root / "nice.py"), "--onefile", "--name", "Uploader_test", "--distpath", str(project_root / "dist" / "test"), "--workpath", str(project_root / "build" / "test"), "--specpath", str(project_root / "build" / "specs"), ] + add_data_args + [ # add some common hidden imports that PyInstaller sometimes misses "--hidden-import", "bs4", "--hidden-import", "requests", "--hidden-import", "nicegui", ]) else: print("Building using spec: nice_uploader.spec") PyInstaller.__main__.run([str(project_root / "nice_uploader.spec")]) if copy: # Windows-only default destination used previously in the repo default_dest = Path(r"\\ict\Go\RCH\Shared\Dragon-Xray\rad-tools\uploader\Uploader") artifact_dir = project_root / "dist" / ("test" if test else "") if not artifact_dir.exists(): artifact_dir = project_root / "dist" # Look for a matching artifact candidates = list(artifact_dir.glob("Uploader*")) + list(artifact_dir.glob("*.exe")) if not candidates: print(f"No build artifact found in {artifact_dir}, skipping copy") return artifact = candidates[0] dest_path = Path(dest) if dest else default_dest dest_path.mkdir(parents=True, exist_ok=True) if artifact.is_file(): target = dest_path / artifact.name print(f"Copying {artifact} -> {target}") shutil.copy2(artifact, target) else: target = dest_path / artifact.name print(f"Copying directory {artifact} -> {target}") if target.exists(): shutil.rmtree(target) shutil.copytree(artifact, target) if __name__ == "__main__": app()