| """core/releases.py β what each deployed environment is RUNNING (wave 20, owner item 12 / R6). |
| |
| β **THIS MODULE EXISTS BECAUSE OF `ops/verify_portability.py` B2**, and the reason is worth |
| stating rather than inferring: the API process must stay HOST-AGNOSTIC. The releases panel was |
| first written with `HfApi`/`hf_hub_download` inside `routes_platform_admin.py`, and the gate |
| turned red on the first run β correctly. A hub client in a request handler couples the runtime to |
| HuggingFace, in the one process whose design promise is that moving hosts is a config change. |
| |
| So the route DELEGATES here, exactly as `routes_assets` delegates to `core/assets.py` (the same |
| rule, the same shape, and that module's own note says so). The SDK import is LAZY and every path |
| is gated on a token being configured, so a deployment with no hub credential simply reports |
| nothing rather than failing to import. |
| |
| β **WHAT THIS READS IS EACH SPACE'S OWN CLAIM ABOUT ITSELF.** `VERSION` is written INTO the Space |
| by `deploy_web.py` at deploy time, so "what is LIVE running" is a lookup, not an inference from |
| timestamps β `last_modified` moves when a SECRET is pushed, which would report a deploy that never |
| happened. An unreachable environment yields a NOTE, never a blank: about a production environment, |
| "no version" reads as "nothing is deployed", which is the worst available way to be wrong. |
| """ |
| import json |
| import os |
|
|
|
|
| def _token(): |
| """The credential that can read the Spaces. `AIOS_HF_TOKEN` is the personal (fsanyoto) token |
| that owns the staging Space; `HF_TOKEN` is the org-scoped one. Either can read.""" |
| return os.environ.get('AIOS_HF_TOKEN') or os.environ.get('HF_TOKEN') or None |
|
|
|
|
| |
| |
| |
| ENVIRONMENTS = (('staging', 'fsanyoto/loopable'), ('live', 'royal-imports/cfo-os')) |
|
|
|
|
| def environments(): |
| """[{env, space, version, note, stage}] β one row per deployed environment. |
| |
| Never raises: every failure becomes a `note` on its own row. A panel that 500s because one |
| Space was briefly unreachable tells the operator less than a row saying so. |
| """ |
| tok = _token() |
| out = [] |
| for name, repo in ENVIRONMENTS: |
| row = {'env': name, 'space': repo, 'version': None, 'note': None, 'stage': None} |
| if not tok: |
| row['note'] = ('no hub credential is configured on this deployment, so other ' |
| 'environments cannot be read from here') |
| out.append(row) |
| continue |
| try: |
| from huggingface_hub import hf_hub_download |
| p = hf_hub_download(repo, 'VERSION', repo_type='space', token=tok) |
| with open(p, encoding='utf-8') as f: |
| row['version'] = f.read().strip().splitlines()[0] |
| except Exception as e: |
| row['note'] = f'could not read this Space\'s VERSION ({type(e).__name__})' |
| try: |
| from huggingface_hub import HfApi |
| row['stage'] = str(HfApi(token=tok).space_info(repo).runtime.stage) |
| except Exception: |
| pass |
| out.append(row) |
| return out |
|
|
|
|
| def history(): |
| """Every `vN` ever cut: [{version, sha, date, subject}], newest first. |
| |
| Read from `RELEASES.json`, which `deploy_web.py` ships INTO the Space beside `VERSION` β the |
| history has to travel with the build because the container has no git checkout to derive it |
| from. Tries the local filesystem FIRST (this deployment's own copy, no network at all) and |
| only then asks the hub, so the common case costs nothing. |
| """ |
| from pathlib import Path |
|
|
| for p in (Path('RELEASES.json'), Path(__file__).resolve().parents[2] / 'RELEASES.json'): |
| try: |
| if p.is_file(): |
| return (json.loads(p.read_text(encoding='utf-8')) or {}).get('releases') or [] |
| except Exception: |
| pass |
| tok = _token() |
| if not tok: |
| return [] |
| for _name, repo in ENVIRONMENTS: |
| try: |
| from huggingface_hub import hf_hub_download |
| q = hf_hub_download(repo, 'RELEASES.json', repo_type='space', token=tok) |
| with open(q, encoding='utf-8') as f: |
| return (json.load(f) or {}).get('releases') or [] |
| except Exception: |
| continue |
| return [] |
|
|