Skip to content

feat: custom env - #33

Open
echarles wants to merge 9 commits into
mainfrom
feat/env-custom
Open

echarles wants to merge 9 commits into
mainfrom
feat/env-custom

Conversation

@echarles

Copy link
Copy Markdown
Member

No description provided.

Copilot AI lite review requested due to automatic review settings September 15, 2026 06:36

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved critical and moderate issues remain in Conda locking, dependency handling, runtime setup, and resolver integration.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds custom Conda environment.yml support with micromamba resolution, explicit locks, and provider-specific installation.

Changes:

  • Extends schema and validation for Conda environments.
  • Adds Conda solving and lock generation.
  • Integrates Conda builds across managed providers and updates conformance checks.
File summaries
File Summary
tests/test_environment_spec.py Tests publication rules.
tests/test_environment_resolve_conda.py Tests Conda resolution.
tests/test_environment_modal_builder.py Tests Modal Conda builds.
tests/test_environment_managed_builders.py Tests managed capabilities.
tests/test_environment_e2b_builder.py Tests E2B Conda builds.
tests/test_environment_daytona_builder.py Tests Daytona Conda builds.
tests/test_environment_datalayer_builder.py Tests Datalayer Conda builds.
tests/test_environment_conformance.py Tests GPU gating.
schemas/environment-v1alpha1.json Adds Conda source support.
code_sandboxes/environments/spec.py Adds Conda validation and publication checks.
code_sandboxes/environments/resolve.py Routes Conda resolution.
code_sandboxes/environments/resolve_conda.py Implements Conda parsing, solving, and locks.
code_sandboxes/environments/conformance.py Gates accelerator conformance checks.
code_sandboxes/environments/adapters/modal.py Installs Conda locks on Modal.
code_sandboxes/environments/adapters/managed.py Adds dependency-format capabilities.
code_sandboxes/environments/adapters/e2b.py Installs Conda locks on E2B.
code_sandboxes/environments/adapters/daytona.py Installs Conda locks on Daytona.
code_sandboxes/environments/adapters/datalayer.py Builds Conda locks with Docker.
Review details

Suppressed comments (5)

code_sandboxes/environments/adapters/managed.py:223

  • build.source == "dependencyFile" is allowed to reach capability validation before full spec validation, but spec.build.dependency_file can still be None. This dereference raises AttributeError instead of returning a capability report for a draft such as {"source": "dependencyFile"}; guard the missing file before reading source_format.
        source_format = spec.build.dependency_file.source_format
        if source_format in self.dependency_formats:
            return []

code_sandboxes/environments/resolve.py:1264

  • This branch passes only the YAML content and Python version, so the conda path drops the existing package contract: spec.packages.python.indexes and user constraints are not applied to the YAML pip: layer, and spec.packages.system.apt is never resolved into the lock. Private pip requirements therefore fall back to the default index, while requested apt packages are silently omitted from every builder.
            return resolve_conda_environment(
                environment_yml=dependency_file.content,
                python_version=environment.spec.language.version,
                resolved_bases=resolved_bases,
                credential=credential,

code_sandboxes/environments/resolve_conda.py:705

  • This resolver has no apt input or output. When resolve_environment selects the conda path, spec.packages.system.apt is never pinned or recorded, so apt_pins_in() sees nothing and every adapter silently omits requested system packages. Resolve apt packages in the same base or explicitly reject apt packages for conda sources.
def resolve_conda_environment(
    *,
    environment_yml: str,
    python_version: str,
    resolved_bases: Mapping[str, str],
    platform: str = CONDA_PLATFORM,

code_sandboxes/environments/resolve_conda.py:731

  • merge_conda_pip only receives the parsed pip: entries; spec.packages.python.constraints never reaches this path. A conda source can therefore bypass user constraints and protected-package conflicts that the normal resolver would merge or reject. Pass the Python constraints through the conda merge/resolve path.
    environment = parse_conda_environment(environment_yml)
    merged = merge_conda_pip(environment)
    for note in merged.notes:
        say(note)
    rendered = rendered_environment(environment, merged, python_version=python_version)

code_sandboxes/environments/resolve_conda.py:737

  • CondaResolveRequest has no index field, and the runners only set PIP_FIND_LINKS. Thus pip: dependencies in environment.yml ignore spec.packages.python.indexes (including private indexes) and resolve through pip's ambient/default index instead of the spec. Carry the indexes into the request and configure the pip step with them.
    request = CondaResolveRequest(
        environment_yml=rendered,
        python_version=python_version,
        platform=platform,
        base_reference=solving_in,
  • Files reviewed: 18/18 changed files
  • Comments generated: 8
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

spec or the one pip mapping — a nested list, a bare number — is refused with
its position, because a solve is not the place to discover a malformed file.
"""
import yaml
Comment on lines +239 to +241
text = channel.strip()
if text:
channels.append(text)
Comment on lines +522 to +524
"RUN --mount=type=cache,target=/opt/conda/pkgs "
"micromamba create --yes --prefix /solve/prefix "
f"--platform {shlex.quote(request.platform)} --file environment.yml",
Comment on lines +678 to +679
for constraint in merged.constraints:
header.append(f"{PROTECTED_PIN_PREFIX}{constraint}")
Comment on lines +1260 to +1266
return resolve_conda_environment(
environment_yml=dependency_file.content,
python_version=environment.spec.language.version,
resolved_bases=resolved_bases,
credential=credential,
log=say,
resolved_at=resolved_at,
Comment on lines +455 to +465
export = subprocess.run( # noqa: S603 - the argv is built here
[self._micromamba, "env", "export", "--explicit", "--prefix", str(prefix)],
capture_output=True,
text=True,
timeout=self._timeout,
check=False,
)
if export.returncode != 0:
for line in (export.stderr or "").splitlines():
say(line)
raise parse_conda_failure(export.stderr or export.stdout or "")
Comment on lines +895 to 899
private = [
url
for url in environment.spec.packages.python.indexes
if not index_is_public(url)
]
Comment on lines 156 to +157
"additionalProperties": false,
"description": "A `requirements.txt`, or a `pyproject.toml` with its `uv.lock` (E3-01).\n\n``requirements`` resolves the way ``packages`` does \u2014 the protected\nconstraints merged in, the same solve. ``pyproject`` does not resolve at\nall: its own ``uv.lock`` is verified against the current\n``pyproject.toml`` and exported, never re-solved, because a lock the\nauthor already made is the whole point of bringing one.",
"description": "A `requirements.txt`, a `pyproject.toml` with its `uv.lock`, or a conda\n`environment.yml` (E3-01, E3-02).\n\n``requirements`` resolves the way ``packages`` does \u2014 the protected\nconstraints merged in, the same solve. ``pyproject`` does not resolve at\nall: its own ``uv.lock`` is verified against the current\n``pyproject.toml`` and exported, never re-solved, because a lock the\nauthor already made is the whole point of bringing one. ``conda`` resolves\nthe ``environment.yml`` in its own ``micromamba`` solve into an explicit\nlock, with the protected constraints merged over its ``pip:`` layer.",
echarles and others added 8 commits September 15, 2026 10:15
The pooled kernel manager moved to the public jupyter-kernels package
(jupyter_kernels.pool.mapping.PooledMappingKernelManager, published as
1.2.23). PyPI serves it, so the protected pin resolves from the index and
its wheel is dropped from the wheelhouse. Update the contract pin, the
wheelhouse README, and the constraint/resolve tests.
…he contract's workdir

Both found by the r1 geospatial drill on 2026-09-16 — the first one
whose smoke test ran the Appendix B core tier for real against a built
artifact. Seven of nine checks passed; these are the two that did not.

- check 7 read "state survived the restart ('True')".
  CodeSandboxClient.restart() was stop() + start(), which destroys and
  recreates a sandbox this process owns but only drops the websocket of
  one attached to somebody else's Jupyter server — the kernel process
  keeps running and the reconnect lands in the same interpreter.
  JupyterServerSandbox.restart_kernel() now asks the server's own
  POST /api/kernels/{id}/restart, as _do_interrupt already does.

- check 2 read "cwd is '/home/datalayer', not '/home/datalayer/content'".
  The image declares WORKDIR there and the contract's User row requires
  it, but a kernel's cwd is the Jupyter server's to choose and
  jupyter-python roots it at $HOME. The contract layer now sets
  MappingKernelManager.root_dir, which moves the kernel without moving
  the file browser. Channel repinned to sha256:122d3e31f5e2....

Also re-pinned the channel digest and apt snapshot in one place: they
were duplicated across two test files and three base releases had left
both red rather than catching anything.

1049 environment/client/jupyter-server tests pass; pre-commit clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… shared engine

contents_build is a first-class field on the environment build spec, baked
through the same build_commands engine as the spec's uploaded files
(build_entries/files_step) on every provider adapter — verified fetch, checksum
that fails the build, environment-contents.json manifest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants