Repository navigation
Add hub scaffold documentation and integration tests - #39
Merged
Merged
Conversation
…hub(skip_install=True)
A published Seahaven world is consumed with
`AutoEnv.from_hub("<owner>/<space>", skip_install=True)` (or `base_url=` for a
self-hosted server), which returns OpenEnv's `GenericEnvClient` and never
installs the world's package or runs discovery. Plain `from_hub` without
`skip_install` is documented as unsupported.
Scaffold (`seahaven new --hub`):
- openenv.yaml gains `app: <package>.openenv_app:app`, which OpenEnv's Modal
and Daytona providers read from the image to start the server; its comment
now says what reads the file, and why there is no `validation:` block.
- Dockerfile puts `/app/.venv/bin` on PATH, so the providers' `python -m
uvicorn <app>` uses the world's interpreter, and adds a HEALTHCHECK on
/health. How it installs Seahaven is unchanged.
- README.md starts with Hugging Face Space settings (`sdk: docker`,
`app_port: 8000`, `base_path: /console`) and gains a section on connecting,
both from new `templates/hub_readme/` fragments. `base_path` is /console
because every image serves the console, while /web exists only when push
enables its web interface; push keeps an existing `base_path`. A world
without `--hub` gets the README it had.
Docs: serving_and_openenv.md "Publishing to a hub" covers the Space settings,
connecting with from_hub, and the image; reference/cli.md lists the three
deviations from an `openenv init` environment (no `validation:` block, no
server/app.py, no openenv-* name or discoverable client).
Tests: tests/test_hub_scaffold.py (serve extra) renders a hub scaffold and
checks it against upstream's own code: run_validation reports only the
missing `validation:` block, validate_env_structure passes, the `app:` value
(read with parse_openenv_app_field) imports and serves /health and the
world's metadata on a real port, the Dockerfile CMD, HEALTHCHECK and PATH
agree with it, the Space settings' port and base_path are served, and
`AutoEnv.from_hub(..., base_url=url, skip_install=True)` drives reset, list
tools, two tool calls, state and close. tests/serving.py gains serving_app().
Not verifiable until Seahaven is on PyPI: the image booting, and from_hub
against a real Space. uv.lock unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QWJSvm3Hep8BHqyKWBkKvp
Publishing to a hub now has one way in: `seahaven new x`, then `seahaven hub` in the world's directory. - `seahaven hub` finds the world's root as the other commands do (nearest pyproject.toml), reads `[project] name` and imports nothing. It writes the five files `openenv push` requires, puts the Space front matter at the top of README.md, and adds the connecting section after `## Using it` (or at the end). A missing README.md is created with both sections. - All or nothing, never overwrite: every target is checked first; an existing hub file, existing front matter or an existing connecting section refuses with every obstacle named and nothing written. A world without src/<package>/openenv_app.py is refused. A failed write rolls back the files it created. - `seahaven new` loses `--hub` and the README placeholders; its next steps mention `seahaven hub`. Template rendering moves to cli/scaffold.py, shared by both commands. `project_root` and `project_name` are public in seahaven.cli, with a remedy for commands that have no `--world`. - Output is byte-identical to the old `new --hub` scaffold. - Docs: reference/cli.md gains a `seahaven hub` section (the three OpenEnv deviations move with it); serving_and_openenv.md and index.md updated. - Tests: tests/test_cli_hub.py covers placement, every refusal with a nothing-written snapshot, rollback, and the CLI entry point; test_hub_scaffold.py keeps every check, now driving `new` then `hub`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QWJSvm3Hep8BHqyKWBkKvp
…pytest `seahaven hub` writes an `__init__.py` at the world's root because `openenv push` requires one. When the directory has the package's name (any one-word world), pytest collected the root as a package and imported that file as the world's package, so every test of a fresh `seahaven new demo` + `seahaven hub` scaffold failed with "demo has no 'world'". - The pytest plugin collects a directory holding an `__init__.py` and `src/<its own name>/` as a plain directory (optionalhook, so pytest 7 runs are unaffected). - `find_world` recognises the root `__init__.py` standing in for the package, which a `tests/__init__.py` or a root `conftest.py` still causes, and fails with a one-line message naming both causes and the fixes. - testing.md states the exception and the one layout rule. - Tests: the scaffold's own pytest after `seahaven new demo` and `seahaven hub`, pytester cases for the shadowing, the collector chosen per layout and both causes of the new message; the scaffold subprocess run is a shared helper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QWJSvm3Hep8BHqyKWBkKvp
Brings in the 0.5.0 release prep, the docs sweep, seahaven.http, the scaffold's serve-with-its-extra fix and dev tooling under tools/, on top of `seahaven hub` replacing `seahaven new --hub`. Conflicts: - src/seahaven/cli/__init__.py: kept main's CliError fix/line/root fields and placed-import errors, and this branch's public project_root/ project_name and root-__init__.py SH501. That SH501 now also carries a fix=, as main's no-world SH501 does. - src/seahaven/cli/new.py: kept main's `import sys` for its stdlib-name refusal; dropped the template-walking imports, which moved to scaffold.py. - src/seahaven/docs/reference/cli.md: took main's "prints what to do next" with no unpublished warning, kept the `seahaven hub` section, and dropped its "while Seahaven is unpublished, the Dockerfile does not build" paragraph, which 0.5.0 on PyPI makes false. - src/seahaven/docs/serving_and_openenv.md: kept this branch's Publishing to a hub section and dropped its "Dockerfile does not build a working image today" paragraph, as main dropped the same paragraph. - tests/conftest.py: kept both assert_scaffold_pytest_passes and main's broken_tidy helper. - tests/serving.py: took main's serving_app(ASGIApp, **config), a superset of this branch's FastAPI-only one. - tests/test_cli_new.py: kept main's python_in/build_as_the_readme_says and new tests, with the scaffold pytest runs through assert_scaffold_pytest_passes. Main's render(..., hub=True) container test moved to test_cli_hub.py, driven through add_hub_files. - tests/test_pytest_plugin.py: kept both sides' new tests. Outside the markers: - tests/test_cli_new.py: the no-hub-option test checks the parsed `new` namespace instead of passing the removed flag, so the flag's spelling appears nowhere outside specs/. - tests/test_find_world.py: a unit test for the root-__init__.py SH501 and its fix. uv.lock is main's, unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QWJSvm3Hep8BHqyKWBkKvp
…-root The image `seahaven hub` generates could not build, and could not run as the user a Hugging Face Space runs it as: - openenv-base:latest ships uv 0.5.27, which can download no final CPython 3.14, so `uv sync` stopped with "No interpreter found for Python >=3.14". Copy in uv 0.12.18 at an exact version before the sync. - A Space runs the container as uid 1000. uv installed CPython under /root (mode 700), so the venv's interpreter was unreadable, and `uv run` in the CMD could neither write a cache nor sync the root-owned venv. Install CPython under /opt/uv/python, and start the server with `python -m uvicorn`, the way OpenEnv's Modal and Daytona providers do. - The HEALTHCHECK ran a bare `python`, which fell through to the base image's 3.11 and passed on a broken image. It now runs the venv's interpreter by path. Tests pin each fix: an exact uv of at least 0.9.0 is copied before the sync, the install dir is absolute, outside /root and set before the sync, the CMD does not start with uv, and the HEALTHCHECK uses <WORKDIR>/.venv/bin/python. The docs say a Space runs the image as user 1000, which cannot write /app. Verified by host emulation, not a docker build: uv 0.5.27 fails the sync; a root build into /app with uv 0.12.24 and the template's env serves as uid 1000 (HOME=/ and unset): /health, /console and /web/ return 200, the HEALTHCHECK passes, and an AutoEnv drive creates and reads an item. The emulation used uv 0.12.24; the template pins 0.12.18, the uv a real Hugging Face build used. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QWJSvm3Hep8BHqyKWBkKvp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds comprehensive documentation and integration tests for the
seahaven new --hubscaffold, ensuring that published worlds work correctly with OpenEnv's tooling and clients.Key Changes
New integration test suite (
tests/test_hub_scaffold.py): Validates that a hub scaffold:openenv validate(with expected validation block warning)openenv pushopenenv.yamlAutoEnv.from_hub(..., skip_install=True)Enhanced scaffold generation (
src/seahaven/cli/new.py):HUB_README_TEMPLATESandHUB_README_PARTSto manage Space card and connection instructions_readme_parts()function to conditionally include hub documentationNew README templates:
space_card.md.tmpl: Hugging Face Space front matter with docker SDK configurationconnecting.md.tmpl: Instructions for connecting to published worlds usingAutoEnv.from_hubUpdated scaffold files:
openenv.yaml: Addedappfield pointing to the ASGI app, clarified why there's no validation blockDockerfile: Added PATH configuration for venv, HEALTHCHECK endpoint, and explanatory commentsREADME.md: Now includes Space card front matter and connection instructions when--hubis usedDocumentation updates (
src/seahaven/docs/):Test utilities (
tests/serving.py):serving_app()helper to serve pre-built FastAPI apps, enabling testing of scaffold-generated appsNotable Implementation Details
validation:block (documented as expected behavior)appfield inopenenv.yamlis critical for OpenEnv's container providers to start the serverhttps://claude.ai/code/session_01QWJSvm3Hep8BHqyKWBkKvp