1717
1818logger = logging .getLogger (__name__ )
1919
20- # IGV.js: prefer local cached copy (inlined), fall back to CDN.
20+ # IGV.js is bundled as a package resource at
21+ # ``chorus/analysis/static/igv.min.js`` so every install has an offline-
22+ # usable copy without any network round-trip. The legacy CDN + HF
23+ # fallback paths remain as secondary options in case a downstream
24+ # consumer stripped the static file from the wheel.
2125#
22- # The committed HTML reports need IGV.js to render the embedded genome
23- # browser. To stay robust when the viewer is offline / behind a proxy / on
24- # a network that MITMs TLS (observed during the 2026-04-16 audit:
25- # `net::ERR_CERT_AUTHORITY_INVALID` on 2/19 reports), we lazy-download
26- # the bundle once into ``~/.chorus/lib/igv.min.js`` the first time
27- # ``build_igv_html`` runs on a machine, then inline the JS into every
28- # subsequent report. If the download fails we silently fall back to the
29- # CDN <script> tag so report generation still succeeds.
26+ # Inlining the JS into every report makes the committed HTMLs
27+ # self-contained (viewable offline, through SSL-MITM proxies, on air-gapped
28+ # hosts). The CDN <script> fallback is the last resort and only triggers
29+ # when both the bundled copy and both network paths fail.
3030_IGV_CDN = "https://cdn.jsdelivr.net/npm/igv@3.1.1/dist/igv.min.js"
3131_IGV_LOCAL = Path .home () / ".chorus" / "lib" / "igv.min.js"
32- # HuggingFace mirror — secondary fallback for environments where the
33- # institutional proxy MITMs TLS and breaks stdlib ``urllib`` but leaves
34- # ``huggingface_hub`` (httpx + certifi) working. Requires the file to
35- # exist in the dataset; gracefully no-ops if it doesn't .
32+ _IGV_BUNDLED = Path ( __file__ ). parent / "static" / "igv.min.js"
33+ # HuggingFace mirror — tertiary fallback for unusual installs where the
34+ # bundled resource is missing (e.g. stripped by a packer) and stdlib
35+ # urllib is blocked by a MITM proxy .
3636_IGV_HF_REPO = "lucapinello/chorus-backgrounds"
3737_IGV_HF_FILENAME = "igv.min.js"
3838
3939
4040def _ensure_igv_local () -> Path | None :
41- """Ensure ``_IGV_LOCAL`` exists; download it on first use.
42-
43- Tries (1) the CDN via stdlib ``urllib`` (``download_with_resume``),
44- then (2) the HuggingFace mirror via ``huggingface_hub`` if the CDN
45- path fails (typical on SSL-MITM institutional networks where
46- stdlib ``urllib`` rejects the proxy's self-signed cert but
47- ``httpx + certifi`` accepts it).
48-
49- Returns the local path when the file is available, ``None`` if
50- both downloads failed (callers then fall back to a CDN <script>
51- tag in the rendered HTML).
41+ """Return a path to ``igv.min.js`` that callers can read + inline.
42+
43+ Resolution order:
44+ 1. ``chorus/analysis/static/igv.min.js`` — bundled with the
45+ package. Always present in a standard install; no network
46+ touched.
47+ 2. ``~/.chorus/lib/igv.min.js`` — legacy cache from earlier chorus
48+ versions. Kept for continuity.
49+ 3. CDN via stdlib ``urllib`` (``download_with_resume``).
50+ 4. HuggingFace dataset mirror via ``huggingface_hub``.
51+
52+ Returns the local path when the file is available, ``None`` if all
53+ four sources failed (callers then fall back to a CDN ``<script>``
54+ tag in the rendered HTML — reports remain viewable online).
5255 """
56+ # 1. Bundled package resource (fast path — no I/O beyond the stat).
57+ if _IGV_BUNDLED .exists () and _IGV_BUNDLED .stat ().st_size > 0 :
58+ return _IGV_BUNDLED
59+
60+ # 2. Legacy user cache from pre-v13 installs.
5361 if _IGV_LOCAL .exists () and _IGV_LOCAL .stat ().st_size > 0 :
5462 return _IGV_LOCAL
63+
64+ # Bundled file missing (stripped by a packer?) and no legacy cache.
65+ # Fall back to the download paths to stay functional.
5566 _IGV_LOCAL .parent .mkdir (parents = True , exist_ok = True )
5667
57- # Attempt 1: CDN via stdlib urllib.
68+ # 3. CDN via stdlib urllib.
5869 try :
5970 from chorus .utils .http import download_with_resume
6071 download_with_resume (_IGV_CDN , _IGV_LOCAL , label = "igv.min.js" )
@@ -64,8 +75,8 @@ def _ensure_igv_local() -> Path | None:
6475 except Exception as exc :
6576 logger .debug ("CDN fetch of igv.min.js failed (%s); trying HF mirror." , exc )
6677
67- # Attempt 2: HuggingFace mirror (works through SSL-MITM proxies
68- # where stdlib urllib fails — huggingface_hub uses httpx+ certifi).
78+ # 4. HuggingFace mirror (works through SSL-MITM proxies where stdlib
79+ # urllib fails — huggingface_hub uses httpx + certifi).
6980 try :
7081 from huggingface_hub import hf_hub_download
7182 downloaded = hf_hub_download (
@@ -74,8 +85,6 @@ def _ensure_igv_local() -> Path | None:
7485 repo_type = "dataset" ,
7586 local_dir = str (_IGV_LOCAL .parent ),
7687 )
77- # hf_hub_download returns the actual local path; move to
78- # canonical _IGV_LOCAL if different.
7988 dp = Path (downloaded )
8089 if dp != _IGV_LOCAL and dp .exists ():
8190 dp .replace (_IGV_LOCAL )
@@ -84,8 +93,8 @@ def _ensure_igv_local() -> Path | None:
8493 return _IGV_LOCAL
8594 except Exception as exc :
8695 logger .warning (
87- "Could not pre-cache igv.min.js from CDN or HF mirror (%s); "
88- "reports will reference %s at view time." ,
96+ "igv.min.js unavailable: bundled resource missing, CDN and HF "
97+ "mirror both failed (%s); reports will reference %s at view time." ,
8998 exc , _IGV_CDN ,
9099 )
91100 return None
0 commit comments