Packaging: the wheel carries the PWA, so a release install serves the UI (v2.7.10)

- pyproject declared build-backend = "setuptools.backends._legacy:_Backend",
  which does not exist: `python -m build`, `pip install .` and even
  `pip install -e .` died with "Backend ... is not available".  It is
  setuptools.build_meta now.
- the wheel and sdist carried only the Python packages, and _default_web_dir()
  resolved <site-packages>/../frontend, so an installed server started fine but
  answered every static request with 404.  setup.py's build_py now copies the
  checkout's frontend/ into simtrace_analyser/web/ inside the build (the PWA's
  tests excluded), and _default_web_dir() prefers that packaged copy, falling
  back to <repo>/frontend for source and editable runs.  The source directory
  is resolved from setup.py (so an sdist build works) and a missing frontend/
  fails the build loudly instead of shipping a package without its UI.
- MANIFEST.in grafts frontend/ (with the READMEs, the udev rule and the four
  launch scripts) into the sdist, so a wheel built from the sdist has the PWA.
- README (EN/RU): an install-from-a-release section (pipx/pip with the wheel,
  the direct release URL for pip) and the --web-dir default documented as the
  bundled PWA, or <repo>/frontend from source.
- tests/test_packaging.py pins the packaged-first resolution (including the
  empty-directory fallback), the source checkout default, the real build
  backend, the manifest graft and setup.py's bundling.
- verified: a fresh venv install from the wheel and from the sdist serve
  GET / (200, byte-identical index.html, PWA v2.7.10) out of
  site-packages/simtrace_analyser/web; an editable install still resolves
  <repo>/frontend.  547 Python + 80 Node tests.
This commit is contained in:
catarrh committed 2026-10-20 23:06:33 +03:00
1 parent 4a03f83459
commit 25aa87ba1e
11 files changed
+196 -16

No files matched your search

+83
View File
@@ -0,0 +1,83 @@
"""Tests for the distributable packaging (bundled PWA, backend, manifest)."""
import os
import tempfile
import unittest
from simtrace_analyser.__main__ import _default_web_dir, _pick_web_dir
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
def _touch(path):
with open(path, 'w') as f:
f.write('')
class TestPickWebDir(unittest.TestCase):
def test_prefers_packaged_pwa(self):
with tempfile.TemporaryDirectory() as tmp:
pkg = os.path.join(tmp, 'simtrace_analyser')
repo = os.path.join(tmp, 'repo')
os.makedirs(os.path.join(pkg, 'web'))
os.makedirs(os.path.join(repo, 'frontend'))
_touch(os.path.join(pkg, 'web', 'index.html'))
_touch(os.path.join(repo, 'frontend', 'index.html'))
self.assertEqual(_pick_web_dir(pkg, repo),
os.path.join(pkg, 'web'))
def test_falls_back_when_packaged_dir_is_missing(self):
with tempfile.TemporaryDirectory() as tmp:
pkg = os.path.join(tmp, 'simtrace_analyser')
repo = os.path.join(tmp, 'repo')
os.makedirs(pkg)
os.makedirs(os.path.join(repo, 'frontend'))
_touch(os.path.join(repo, 'frontend', 'index.html'))
self.assertEqual(_pick_web_dir(pkg, repo),
os.path.join(repo, 'frontend'))
def test_falls_back_when_packaged_dir_has_no_index(self):
# A stale/empty simtrace_analyser/web directory must not shadow the
# source checkout (e.g. an editable install of a half-built tree).
with tempfile.TemporaryDirectory() as tmp:
pkg = os.path.join(tmp, 'simtrace_analyser')
repo = os.path.join(tmp, 'repo')
os.makedirs(os.path.join(pkg, 'web'))
os.makedirs(os.path.join(repo, 'frontend'))
_touch(os.path.join(repo, 'frontend', 'index.html'))
self.assertEqual(_pick_web_dir(pkg, repo),
os.path.join(repo, 'frontend'))
class TestDefaultWebDir(unittest.TestCase):
def test_source_checkout(self):
web = _default_web_dir()
self.assertTrue(os.path.isfile(os.path.join(web, 'index.html')),
'no index.html in %s' % web)
class TestDistributionMetadata(unittest.TestCase):
def _read(self, name):
with open(os.path.join(REPO, name)) as f:
return f.read()
def test_build_backend_exists(self):
pyproject = self._read('pyproject.toml')
self.assertIn('build-backend = "setuptools.build_meta"', pyproject)
self.assertNotIn('backends._legacy', pyproject)
def test_manifest_ships_the_pwa(self):
# The sdist carries frontend/, so a wheel built from it has the PWA.
self.assertIn('graft frontend', self._read('MANIFEST.in'))
def test_setup_py_bundles_the_pwa(self):
setup_py = self._read('setup.py')
self.assertIn('frontend', setup_py)
self.assertIn('copytree', setup_py)
self.assertIn("'web'", setup_py)
self.assertIn("'build_py'", setup_py)
self.assertIn('ignore_patterns', setup_py)
if __name__ == '__main__':
unittest.main()