code-sandboxes 1.9.1__tar.gz → 1.9.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/CHANGELOG.md +25 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/PKG-INFO +1 -1
- code_sandboxes-1.9.3/build.log +5 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/__version__.py +1 -1
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/adapters/datalayer.py +45 -15
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/spec.py +24 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_datalayer_builder.py +47 -13
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_spec.py +29 -1
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/.github/workflows/build.yml +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/.github/workflows/environments-live.yml +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/.github/workflows/py-code-style.yml +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/.github/workflows/py-tests.yml +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/.github/workflows/py-typing.yml +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/.github/workflows/reusable-python.yml +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/.gitignore +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/.licenserc.yaml +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/.pre-commit-config.yaml +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/.vscode/settings.json +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/Dockerfile +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/LICENSE +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/Makefile +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/README.md +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/__init__.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/base.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/bridge_mount.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/builds.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/cli.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/client.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/cloudflare_sandbox.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/commands.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/console.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/contents.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/coreweave_sandbox.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/datalayer_sandbox.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/daytona_sandbox.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/docker_sandbox.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/e2b_sandbox.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/__init__.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/__main__.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/accounts.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/adapters/__init__.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/adapters/daytona.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/adapters/e2b.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/adapters/managed.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/adapters/modal.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/attest.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/bases.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/breaker.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/build_secrets.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/builders.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/canonical.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/conformance.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/constraints/sandbox-contract-v1.txt +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/constraints/wheelhouse/README.md +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/constraints/wheelhouse/jupyter_server-2.21.0+datalayer.1-py3-none-any.whl +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/contract.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/doctor/__init__.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/doctor/build.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/doctor/datalayer_sandbox.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/doctor/report.schema.json +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/errors.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/files.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/image_import.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/lifecycle.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/policy.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/redact.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/resolve.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/schema.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/eval_sandbox.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/exceptions.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/filesystem.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/google_colab.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/google_colab_sandbox.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/interfaces.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/jupyter_ingress.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/jupyter_server_sandbox.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/kaggle.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/kaggle_execute.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/kaggle_live.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/kaggle_sandbox.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/lifecycle.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/manage.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/modal_sandbox.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/models.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/monty_sandbox.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/provider_ingress.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/providers.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docker/Dockerfile +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/.gitignore +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/LICENSE +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/Makefile +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/README.md +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/babel.config.js +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/api-reference/_category_.yml +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/api-reference/index.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/cli/_category_.yml +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/cli/index.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/contribute/_category_.yml +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/contribute/index.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/environments/_category_.yml +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/environments/contract.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/environments/index.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/environments/specification.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/environments/spike-2026-09.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/examples/_category_.yml +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/examples/index.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/guide/_category_.yml +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/guide/index.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/index.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/install/_category_.yml +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/install/index.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/provider-ingress.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/providers/_category_.yml +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/providers/cloudflare.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/providers/coreweave.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/providers/datalayer.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/providers/daytona.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/providers/docker.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/providers/e2b.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/providers/eval.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/providers/google-colab.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/providers/index.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/providers/jupyter-server.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/providers/kaggle.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/providers/modal.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docs/providers/monty.mdx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/docusaurus.config.js +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/package.json +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/sidebars.js +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/src/components/HomepageRow1.js +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/src/components/HomepageRow1.module.css +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/src/components/HomepageRow2.js +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/src/components/HomepageRow2.module.css +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/src/components/HomepageRow3.js +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/src/components/HomepageRow3.module.css +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/src/css/custom.css +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/src/pages/index.module.css +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/src/pages/markdown-page.md +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/src/theme/CustomDocItem.tsx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/src/theme/IconsTitle.tsx +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/bricks.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/building-construction.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/cloud-sun.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/cloud.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/collaboration.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/copy-left.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/datalayer/logo.png +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/datalayer/logo.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/datalayer/personas/business.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/datalayer/personas/data-scientist.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/datalayer/personas/devops.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/datalayer/project/project-line.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/favicon.ico +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/jupyter.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/open-source.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/react-js.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/react-js2.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/ringed-planet.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/robot.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/rocket.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/shield.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/sparkles.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/streamlit.gif +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/super-hero.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/target.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/docs/static/img/thumbs-up.svg +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/README.md +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/exec/Makefile +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/exec/cloudflare_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/exec/coreweave_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/exec/datalayer_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/exec/daytona_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/exec/docker_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/exec/e2b_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/exec/eval_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/exec/google_colab_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/exec/jupyter_server_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/exec/kaggle_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/exec/modal_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/exec/monty_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/repl/Makefile +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/repl/cloudflare_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/repl/coreweave_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/repl/datalayer_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/repl/daytona_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/repl/docker_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/repl/e2b_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/repl/eval_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/repl/google_colab_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/repl/jupyter_server_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/repl/kaggle_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/repl/modal_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/examples/repl/monty_sandbox_example.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/pyproject.toml +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/schemas/environment-v1alpha1.json +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/__init__.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/conftest.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_a_datalayer_sandbox_can_be_interrupted.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_bridge_mount.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_builds.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_cli_exec.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_cli_repl.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_client.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_client_contents.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_client_files.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_client_outcome_error_output.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_cloudflare.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_commands_envelope.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_console.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_contents_contract.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_contents_live_matrix.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_coreweave.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_datalayer_sandbox.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_daytona.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_e2b.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_accounts.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_attest.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_bases.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_breaker.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_build_secrets.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_builders.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_canonical.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_conformance.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_constraints.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_contract.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_cross_variant_report.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_daytona_builder.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_doctor.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_e2b_builder.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_errors.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_image_import.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_lifecycle.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_live_matrix.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_managed_builders.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_modal_builder.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_redact.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_environment_resolve.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_eval.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_every_variant_has_a_real_interrupt.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_factory.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_google_colab.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_integration.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_jupyter_ingress.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_jupyter_server.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_jupyter_server_streaming.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_kaggle.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_kaggle_execute.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_kaggle_live.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_kaggle_session.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_kernel_client_compatibility.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_lifecycle_vocabulary.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_manage.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_manage_datalayer.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_modal_google_colab_sandbox.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_modal_session.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_models.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_output_line_fidelity.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_provider_catalog_names.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_provider_ingress.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_providers.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_sandboxes.py +0 -0
- {code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/tests/test_the_kill_relaunch_drill.py +0 -0
|
@@ -8,6 +8,31 @@
|
|
|
8
8
|
|
|
9
9
|
## Unreleased
|
|
10
10
|
|
|
11
|
+
## 1.9.3
|
|
12
|
+
|
|
13
|
+
- **A build secret is mounted only on the `postInstall` commands that name it**
|
|
14
|
+
(`environments.spec.command_names_secret`, `environments.adapters.datalayer`;
|
|
15
|
+
PLAN_ENV.md E3-05). Every declared secret used to be mounted on every
|
|
16
|
+
`postInstall` `RUN`. A command now gets a secret's mount only when the
|
|
17
|
+
secret's name is in it as a whole word (`$NAME`, `${NAME}`, `--key-env NAME`,
|
|
18
|
+
`/run/secrets/NAME`). A declared secret that no command names is a
|
|
19
|
+
`DL_ENV_SPEC_INVALID` finding on `spec.buildSecrets[i]`, rather than a build
|
|
20
|
+
that runs with it empty.
|
|
21
|
+
|
|
22
|
+
## 1.9.2
|
|
23
|
+
|
|
24
|
+
- **A real ECR repository name is lowercase; a real owner uid is not**
|
|
25
|
+
(`environments.adapters.datalayer.owner_repository`, new
|
|
26
|
+
`owner_cache_repository`; PLAN_ENV.md E1-07). Found live, 2026-09-14, the
|
|
27
|
+
first real Datalayer build ever run for a real account's own uid rather
|
|
28
|
+
than a lowercase test fixture: `DescribeRepositories` refused outright,
|
|
29
|
+
"Invalid parameter at 'repositoryName'" — this project's own uids are
|
|
30
|
+
ULIDs, conventionally uppercase, and ECR repository names match
|
|
31
|
+
`[a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*` per path segment. Both the owner's own
|
|
32
|
+
repository and the owner's cache repository are lowered at the one place
|
|
33
|
+
each is built, so every caller (create, inspect, exists, delete, the
|
|
34
|
+
cache import/export) stays consistent with itself.
|
|
35
|
+
|
|
11
36
|
## 1.9.1
|
|
12
37
|
|
|
13
38
|
- **mTLS to the build pool's `buildkitd`** (`environments.adapters.datalayer.Builder`,
|
{code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/adapters/datalayer.py
RENAMED
|
@@ -69,9 +69,14 @@ from ..errors import (
|
|
|
69
69
|
)
|
|
70
70
|
from ..files import files_step
|
|
71
71
|
from ..resolve import WHEELHOUSE_IMAGE_PATH, WHEELHOUSE_PATH, apt_pins_in, locked_versions
|
|
72
|
-
from ..spec import BuildSecret, Environment
|
|
72
|
+
from ..spec import BuildSecret, Environment, command_names_secret
|
|
73
73
|
|
|
74
|
-
__all__ = [
|
|
74
|
+
__all__ = [
|
|
75
|
+
"ECR_ENVIRONMENT_PREFIX",
|
|
76
|
+
"Builder",
|
|
77
|
+
"owner_cache_repository",
|
|
78
|
+
"owner_repository",
|
|
79
|
+
]
|
|
75
80
|
|
|
76
81
|
#: Where an owner's environments live in ECR (D-10).
|
|
77
82
|
ECR_ENVIRONMENT_PREFIX = "environments/u/"
|
|
@@ -89,10 +94,32 @@ DEFAULT_MAX_ARTIFACT_BYTES = 20 * 1024**3
|
|
|
89
94
|
|
|
90
95
|
|
|
91
96
|
def owner_repository(owner_uid: str, environment_name: str) -> str:
|
|
92
|
-
"""The owner's repository for one environment: ``environments/u/<owner>/<name>``.
|
|
97
|
+
"""The owner's repository for one environment: ``environments/u/<owner>/<name>``.
|
|
98
|
+
|
|
99
|
+
Lowercased: an ECR repository name matches
|
|
100
|
+
``[a-z0-9]+((\\.|_|__|-+)[a-z0-9]+)*`` per path segment, and this
|
|
101
|
+
project's own uids are ULIDs, conventionally uppercase — found live,
|
|
102
|
+
2026-09-14, the first real build ever run for a real account's own
|
|
103
|
+
uid rather than a lowercase test fixture: ``DescribeRepositories``
|
|
104
|
+
refused it outright, "Invalid parameter at 'repositoryName'". Lowering
|
|
105
|
+
both segments here, the one place this reference is built, keeps every
|
|
106
|
+
caller (create, inspect, exists, delete) consistent with itself without
|
|
107
|
+
each needing to remember to.
|
|
108
|
+
"""
|
|
93
109
|
if not owner_uid or not environment_name:
|
|
94
110
|
raise ValueError("an owner and an environment name make the repository")
|
|
95
|
-
return f"{ECR_ENVIRONMENT_PREFIX}{owner_uid}/{environment_name}"
|
|
111
|
+
return f"{ECR_ENVIRONMENT_PREFIX}{owner_uid}/{environment_name}".lower()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def owner_cache_repository(owner_uid: str) -> str:
|
|
115
|
+
"""The owner's own BuildKit cache repository: ``environments/cache/u/<owner>``.
|
|
116
|
+
|
|
117
|
+
Lowercased for the same reason ``owner_repository`` is (a ULID, not a
|
|
118
|
+
lowercase test fixture, is what a real build actually names).
|
|
119
|
+
"""
|
|
120
|
+
if not owner_uid:
|
|
121
|
+
raise ValueError("an owner makes the cache repository")
|
|
122
|
+
return f"environments/cache/u/{owner_uid}".lower()
|
|
96
123
|
|
|
97
124
|
|
|
98
125
|
def _secret_mount(secret: BuildSecret) -> str:
|
|
@@ -320,15 +347,14 @@ class Builder:
|
|
|
320
347
|
"/opt/datalayer/lock.txt",
|
|
321
348
|
]
|
|
322
349
|
)
|
|
323
|
-
# A build secret is mounted on the postInstall
|
|
324
|
-
# (§4.1, D-11): never an `ARG` or `ENV`, which bakes a
|
|
325
|
-
# image's history,
|
|
326
|
-
#
|
|
327
|
-
# BuildKit mount: the value is available to
|
|
328
|
-
# never written to a layer.
|
|
350
|
+
# A build secret is mounted on the postInstall commands that name it
|
|
351
|
+
# and nowhere else (§4.1, D-11): never an `ARG` or `ENV`, which bakes a
|
|
352
|
+
# value into the image's history, never the package-install or files
|
|
353
|
+
# steps, and never a postInstall command that does not read it.
|
|
354
|
+
# `--mount=type=secret` is a BuildKit mount: the value is available to
|
|
355
|
+
# the one `RUN`'s shell and never written to a layer.
|
|
329
356
|
wanted = set(request.build_secret_ids)
|
|
330
357
|
secrets = [secret for secret in spec.build_secrets if secret.id in wanted]
|
|
331
|
-
secret_mounts = " ".join(_secret_mount(secret) for secret in secrets)
|
|
332
358
|
baked = files_step(request.environment, variant=self.variant)
|
|
333
359
|
if baked:
|
|
334
360
|
lines.append("USER 1000:100")
|
|
@@ -338,11 +364,15 @@ class Builder:
|
|
|
338
364
|
if spec.commands.post_install:
|
|
339
365
|
lines.append("USER 1000:100")
|
|
340
366
|
lines.append("WORKDIR /home/datalayer/content")
|
|
341
|
-
prefix = "RUN --network=none " + (f"{secret_mounts} " if secret_mounts else "")
|
|
342
367
|
for command in spec.commands.post_install:
|
|
343
368
|
# No network: a command that fetches something makes an
|
|
344
369
|
# artifact whose contents depend on the day it was built.
|
|
345
|
-
|
|
370
|
+
mounts = "".join(
|
|
371
|
+
f"{_secret_mount(secret)} "
|
|
372
|
+
for secret in secrets
|
|
373
|
+
if command_names_secret(command, secret)
|
|
374
|
+
)
|
|
375
|
+
lines.append(f"RUN --network=none {mounts}{command}")
|
|
346
376
|
lines.extend(
|
|
347
377
|
[
|
|
348
378
|
"USER 1000:100",
|
|
@@ -374,7 +404,7 @@ class Builder:
|
|
|
374
404
|
repository = owner_repository(request.owner_uid, request.environment.metadata.name)
|
|
375
405
|
registry = self._registry_host()
|
|
376
406
|
self._ensure_repository(repository)
|
|
377
|
-
self._ensure_cache_repository(
|
|
407
|
+
self._ensure_cache_repository(owner_cache_repository(request.owner_uid))
|
|
378
408
|
tag = f"v{request.version}-{request.build_uid}"
|
|
379
409
|
reference = f"{registry}/{repository}:{tag}"
|
|
380
410
|
with tempfile.TemporaryDirectory(prefix="dl-build-") as directory:
|
|
@@ -637,7 +667,7 @@ class Builder:
|
|
|
637
667
|
def _cache_options(self, request: BuildRequest) -> list[str]:
|
|
638
668
|
"""The owner's cache, imported and exported: never another owner's (D-12)."""
|
|
639
669
|
registry = self._registry_host()
|
|
640
|
-
cache = f"{registry}/
|
|
670
|
+
cache = f"{registry}/{owner_cache_repository(request.owner_uid)}:{self.variant}"
|
|
641
671
|
return [
|
|
642
672
|
"--import-cache",
|
|
643
673
|
f"type=registry,ref={cache}",
|
|
@@ -83,6 +83,7 @@ __all__ = [
|
|
|
83
83
|
"VariantSet",
|
|
84
84
|
"VersionStatus",
|
|
85
85
|
"assert_publishable",
|
|
86
|
+
"command_names_secret",
|
|
86
87
|
"parse_environment",
|
|
87
88
|
"parse_requirements_txt",
|
|
88
89
|
"publication_findings",
|
|
@@ -200,6 +201,18 @@ class BuildSecret(_Model):
|
|
|
200
201
|
name: str = Field(pattern=r"^[A-Za-z0-9_.-]+$")
|
|
201
202
|
|
|
202
203
|
|
|
204
|
+
def command_names_secret(command: str, secret: BuildSecret) -> bool:
|
|
205
|
+
"""Whether one ``postInstall`` command names ``secret``, so its step gets the mount (E3-05).
|
|
206
|
+
|
|
207
|
+
A command names a secret when the secret's name is in it as a whole word:
|
|
208
|
+
``$PIP_TOKEN``, ``${PIP_TOKEN}``, ``--key-env PIP_TOKEN`` or
|
|
209
|
+
``/run/secrets/netrc``. A secret is mounted on exactly the steps that name
|
|
210
|
+
it (§4.1), so a command that reads none holds none.
|
|
211
|
+
"""
|
|
212
|
+
word = rf"(?<![A-Za-z0-9_.-]){re.escape(secret.name)}(?![A-Za-z0-9_.-])"
|
|
213
|
+
return re.search(word, command) is not None
|
|
214
|
+
|
|
215
|
+
|
|
203
216
|
class Accelerator(_Model):
|
|
204
217
|
type: str
|
|
205
218
|
count: int = Field(default=1, ge=1)
|
|
@@ -640,6 +653,17 @@ def spec_findings(
|
|
|
640
653
|
values = [getattr(secret, attribute) for secret in spec.build_secrets]
|
|
641
654
|
if len(values) != len(set(values)):
|
|
642
655
|
findings.append(SpecFinding("spec.buildSecrets", f"a secret {label} is listed twice"))
|
|
656
|
+
for index, secret in enumerate(spec.build_secrets):
|
|
657
|
+
# A secret no command names would be mounted nowhere: the build would
|
|
658
|
+
# run with it empty and fail far from the cause.
|
|
659
|
+
if not any(command_names_secret(command, secret) for command in spec.commands.post_install):
|
|
660
|
+
findings.append(
|
|
661
|
+
SpecFinding(
|
|
662
|
+
f"spec.buildSecrets[{index}]",
|
|
663
|
+
f"`{secret.name}` is named by no `postInstall` command, and a secret "
|
|
664
|
+
"is mounted only on the commands that name it",
|
|
665
|
+
)
|
|
666
|
+
)
|
|
643
667
|
|
|
644
668
|
resources = spec.resources
|
|
645
669
|
if resources.size_class not in SIZE_CLASSES:
|
|
@@ -19,7 +19,11 @@ from pathlib import Path
|
|
|
19
19
|
|
|
20
20
|
import pytest
|
|
21
21
|
|
|
22
|
-
from code_sandboxes.environments.adapters.datalayer import
|
|
22
|
+
from code_sandboxes.environments.adapters.datalayer import (
|
|
23
|
+
Builder,
|
|
24
|
+
owner_cache_repository,
|
|
25
|
+
owner_repository,
|
|
26
|
+
)
|
|
23
27
|
from code_sandboxes.environments.builders import ArtifactReference, BuildRequest
|
|
24
28
|
from code_sandboxes.environments.errors import EnvironmentsError
|
|
25
29
|
from code_sandboxes.environments.spec import parse_environment
|
|
@@ -91,6 +95,18 @@ class FakeEcr:
|
|
|
91
95
|
return error
|
|
92
96
|
|
|
93
97
|
|
|
98
|
+
#: One build secret, named by one of two `postInstall` commands (E3-05).
|
|
99
|
+
A_SECRET = {
|
|
100
|
+
"buildSecrets": [{"id": "dlsec_01J9BUILDSECRET0000000000", "name": "PIP_TOKEN"}],
|
|
101
|
+
"commands": {
|
|
102
|
+
"postInstall": [
|
|
103
|
+
"python -c 'import geopandas'",
|
|
104
|
+
"python unpack_tiles.py --key-env PIP_TOKEN",
|
|
105
|
+
]
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
94
110
|
def a_request(**changes) -> BuildRequest:
|
|
95
111
|
"""The section 4.1 example as a build of version 3, attempt `bld-1`."""
|
|
96
112
|
spec = {
|
|
@@ -232,21 +248,20 @@ class TestTheDockerfileItGenerates:
|
|
|
232
248
|
# Never the loose list: that is the whole point of resolving once.
|
|
233
249
|
assert "geopandas==1.1.1" not in dockerfile
|
|
234
250
|
|
|
235
|
-
def
|
|
236
|
-
"""Mounted on the `postInstall` `RUN` alone — never an
|
|
237
|
-
which bakes a value into the image's history,
|
|
238
|
-
package-install or files steps,
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
build_secret_ids=("dlsec_01J9BUILDSECRET0000000000",),
|
|
242
|
-
)
|
|
251
|
+
def test_a_build_secret_is_mounted_for_the_command_that_names_it_alone(self) -> None:
|
|
252
|
+
"""Mounted on the `postInstall` `RUN` that names it alone — never an
|
|
253
|
+
`ARG`/`ENV`, which bakes a value into the image's history, never the
|
|
254
|
+
package-install or files steps, and never a `postInstall` command
|
|
255
|
+
that does not read it (§4.1, D-11)."""
|
|
256
|
+
request = a_request(spec=A_SECRET, build_secret_ids=("dlsec_01J9BUILDSECRET0000000000",))
|
|
243
257
|
dockerfile = a_builder().dockerfile(request)
|
|
244
258
|
lines = dockerfile.splitlines()
|
|
245
259
|
mount_lines = [line for line in lines if "--mount=type=secret" in line]
|
|
246
260
|
assert mount_lines == [
|
|
247
261
|
"RUN --network=none --mount=type=secret,id=dlsec_01J9BUILDSECRET0000000000,"
|
|
248
|
-
"env=PIP_TOKEN python -
|
|
262
|
+
"env=PIP_TOKEN python unpack_tiles.py --key-env PIP_TOKEN"
|
|
249
263
|
]
|
|
264
|
+
assert "RUN --network=none python -c 'import geopandas'" in lines
|
|
250
265
|
# Nowhere else in the Dockerfile: no `ARG`/`ENV` line, and no other
|
|
251
266
|
# `RUN` mentions the id or the value's own name.
|
|
252
267
|
assert not any(line.startswith(("ARG", "ENV")) and "PIP_TOKEN" in line for line in lines)
|
|
@@ -261,7 +276,8 @@ class TestTheDockerfileItGenerates:
|
|
|
261
276
|
"name": "netrc",
|
|
262
277
|
"mountAs": "file",
|
|
263
278
|
}
|
|
264
|
-
]
|
|
279
|
+
],
|
|
280
|
+
"commands": {"postInstall": ["python unpack_tiles.py --netrc /run/secrets/netrc"]},
|
|
265
281
|
},
|
|
266
282
|
build_secret_ids=("dlsec_01J9BUILDSECRET0000000000",),
|
|
267
283
|
)
|
|
@@ -476,7 +492,7 @@ class TestBuildingAndPushing:
|
|
|
476
492
|
return result
|
|
477
493
|
|
|
478
494
|
request = a_request(
|
|
479
|
-
spec=
|
|
495
|
+
spec=A_SECRET,
|
|
480
496
|
build_secret_ids=("dlsec_01J9BUILDSECRET0000000000",),
|
|
481
497
|
)
|
|
482
498
|
buildctl = RecordingBuildctl()
|
|
@@ -497,7 +513,7 @@ class TestBuildingAndPushing:
|
|
|
497
513
|
raise EnvironmentsError(BUILD_SECRET_UNAVAILABLE, "IAM is unreachable")
|
|
498
514
|
|
|
499
515
|
request = a_request(
|
|
500
|
-
spec=
|
|
516
|
+
spec=A_SECRET,
|
|
501
517
|
build_secret_ids=("dlsec_01J9BUILDSECRET0000000000",),
|
|
502
518
|
)
|
|
503
519
|
buildctl = Buildctl()
|
|
@@ -565,6 +581,24 @@ class TestBuildingAndPushing:
|
|
|
565
581
|
a_builder(run=Buildctl(), ecr=ecr).build(a_request())
|
|
566
582
|
assert ecr.created == []
|
|
567
583
|
|
|
584
|
+
def test_a_real_uppercase_ulid_owner_still_names_a_valid_ecr_repository(self) -> None:
|
|
585
|
+
"""Found live, 2026-09-14: the first real build for a real account's
|
|
586
|
+
own uid, not a lowercase test fixture. ECR repository names match
|
|
587
|
+
``[a-z0-9]+((\\.|_|__|-+)[a-z0-9]+)*`` per segment; this project's own
|
|
588
|
+
uids are ULIDs, conventionally uppercase — `DescribeRepositories`
|
|
589
|
+
refused the request outright before this fix."""
|
|
590
|
+
ulid_owner = "01JV1VE1T5VG22Z05F6EFBMW8E"
|
|
591
|
+
ecr = FakeEcr()
|
|
592
|
+
a_builder(run=Buildctl(), ecr=ecr).build(a_request(owner_uid=ulid_owner))
|
|
593
|
+
names = [item["repositoryName"] for item in ecr.created]
|
|
594
|
+
assert names, "the build should have created at least the owner's own repository"
|
|
595
|
+
for name in names:
|
|
596
|
+
assert name == name.lower(), f"{name!r} is not a valid ECR repository name"
|
|
597
|
+
assert owner_repository(ulid_owner, "geospatial-analysis") == (
|
|
598
|
+
f"environments/u/{ulid_owner.lower()}/geospatial-analysis"
|
|
599
|
+
)
|
|
600
|
+
assert owner_cache_repository(ulid_owner) == f"environments/cache/u/{ulid_owner.lower()}"
|
|
601
|
+
|
|
568
602
|
def test_the_credential_reaches_buildctl_and_not_the_workers_own_config(self) -> None:
|
|
569
603
|
seen: dict[str, object] = {}
|
|
570
604
|
|
|
@@ -23,8 +23,10 @@ from code_sandboxes.environments.errors import EnvironmentsError
|
|
|
23
23
|
from code_sandboxes.environments.schema import main as schema_main
|
|
24
24
|
from code_sandboxes.environments.schema import schema_text
|
|
25
25
|
from code_sandboxes.environments.spec import (
|
|
26
|
+
BuildSecret,
|
|
26
27
|
Environment,
|
|
27
28
|
assert_publishable,
|
|
29
|
+
command_names_secret,
|
|
28
30
|
parse_environment,
|
|
29
31
|
publication_findings,
|
|
30
32
|
spec_digest,
|
|
@@ -72,10 +74,11 @@ spec:
|
|
|
72
74
|
commands:
|
|
73
75
|
postInstall:
|
|
74
76
|
- "python -c 'import geopandas'"
|
|
77
|
+
- "python unpack_tiles.py --key-env TILES_LICENSE_KEY"
|
|
75
78
|
buildSecrets:
|
|
76
79
|
- id: dlsec_01J8ZK
|
|
77
80
|
mountAs: env
|
|
78
|
-
name:
|
|
81
|
+
name: TILES_LICENSE_KEY
|
|
79
82
|
resources:
|
|
80
83
|
accelerator: none
|
|
81
84
|
hints:
|
|
@@ -195,6 +198,13 @@ UNSUPPORTED = "DL_ENV_CAPABILITY_UNSUPPORTED"
|
|
|
195
198
|
("spec.language.version", "3.9", "spec.language.version", INVALID),
|
|
196
199
|
("spec.build.source", "dockerfile", "spec.build.source", UNSUPPORTED),
|
|
197
200
|
("spec.packages.python.manager", "conda", "spec.packages.python.manager", UNSUPPORTED),
|
|
201
|
+
# E3-05: a secret no postInstall command names would be mounted nowhere.
|
|
202
|
+
(
|
|
203
|
+
"spec.commands.postInstall",
|
|
204
|
+
["python -c 'import geopandas'"],
|
|
205
|
+
"spec.buildSecrets[0]",
|
|
206
|
+
INVALID,
|
|
207
|
+
),
|
|
198
208
|
(
|
|
199
209
|
"spec.packages.python.dependencies.0",
|
|
200
210
|
"geopandas>=>1",
|
|
@@ -526,6 +536,24 @@ def test_a_spec_is_digested_the_same_from_the_environment_or_the_spec() -> None:
|
|
|
526
536
|
assert spec_digest(environment) == spec_digest(environment.spec)
|
|
527
537
|
|
|
528
538
|
|
|
539
|
+
@pytest.mark.parametrize(
|
|
540
|
+
("command", "named"),
|
|
541
|
+
[
|
|
542
|
+
("python unpack_tiles.py --key-env TOKEN", True),
|
|
543
|
+
('curl -H "Authorization: $TOKEN" x', True),
|
|
544
|
+
("echo ${TOKEN}", True),
|
|
545
|
+
("cat /run/secrets/TOKEN", True),
|
|
546
|
+
("echo $TOKEN_FILE", False),
|
|
547
|
+
("echo $MY_TOKEN", False),
|
|
548
|
+
("python -c 'import geopandas'", False),
|
|
549
|
+
],
|
|
550
|
+
)
|
|
551
|
+
def test_a_command_names_a_secret_by_its_whole_name(command: str, named: bool) -> None:
|
|
552
|
+
"""E3-05: the Datalayer builder mounts a secret on the commands that name it."""
|
|
553
|
+
secret = BuildSecret(id="dlsec_01J8ZK", name="TOKEN")
|
|
554
|
+
assert command_names_secret(command, secret) is named
|
|
555
|
+
|
|
556
|
+
|
|
529
557
|
class TestPublicationFindings:
|
|
530
558
|
"""D-12: a version with any build secret can never be published to the Library."""
|
|
531
559
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/adapters/__init__.py
RENAMED
|
File without changes
|
{code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/adapters/daytona.py
RENAMED
|
File without changes
|
|
File without changes
|
{code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/adapters/managed.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/doctor/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{code_sandboxes-1.9.1 → code_sandboxes-1.9.3}/code_sandboxes/environments/doctor/report.schema.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|