From dc9b79e583e944358b897dfec9a142a0cf4564ec Mon Sep 17 00:00:00 2001 From: Alex Kerney Date: Tue, 17 Sep 2024 11:23:58 -0400 Subject: [PATCH] feat: Export conda environment.yml (#2003) Co-authored-by: Joshua L. Adelman Co-authored-by: Ruben Arts --- .github/workflows/rust.yml | 29 ++ .github/workflows/test_exports.yml | 74 ++++ docs/reference/cli.md | 23 ++ docs/switching_from/conda.md | 6 + src/cli/project/export/conda_environment.rs | 362 ++++++++++++++++++ src/cli/project/export/mod.rs | 4 + ...nt__tests__test_export_conda_env_yaml.snap | 14 + ...export_conda_env_yaml_pyproject_panic.snap | 17 + ...nda_env_yaml_with_pip_custom_registry.snap | 14 + ...export_conda_env_yaml_with_pip_extras.snap | 23 ++ ...rt_conda_env_yaml_with_pip_find_links.snap | 15 + ...t_conda_env_yaml_with_source_editable.snap | 18 + tests/test_export.sh | 65 ++++ 13 files changed, 664 insertions(+) create mode 100644 .github/workflows/test_exports.yml create mode 100644 src/cli/project/export/conda_environment.rs create mode 100644 src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml.snap create mode 100644 src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml_pyproject_panic.snap create mode 100644 src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml_with_pip_custom_registry.snap create mode 100644 src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml_with_pip_extras.snap create mode 100644 src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml_with_pip_find_links.snap create mode 100644 src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml_with_source_editable.snap create mode 100644 tests/test_export.sh diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index a02b042a0..81c5d3b06 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -418,6 +418,35 @@ jobs: - build uses: ./.github/workflows/test_downstream.yml + export-filter: + name: Only run example tests if relevant code or manifests have changed + runs-on: ubuntu-20.04 + needs: + - build + outputs: + exports: ${{ steps.filter.outputs.exports }} + + steps: + - name: checkout repo + uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + exports: + - src/cli/project/export/conda_environment.rs + - src/cli/project/export/test-data/testenv/* + - examples/pypi-source-deps/* + - examples/pypi-custom-registry/* + - examples/pypi-find-links/* + - examples/docker/* + + export_tests: + name: "Export tests" + needs: export-filter + if: ${{ needs.export-filter.outputs.exports == 'true' }} + uses: ./.github/workflows/test_exports.yml + test_common_wheels: name: "Test installation of common wheels" needs: diff --git a/.github/workflows/test_exports.yml b/.github/workflows/test_exports.yml new file mode 100644 index 000000000..27f4d21ae --- /dev/null +++ b/.github/workflows/test_exports.yml @@ -0,0 +1,74 @@ +name: "Test exports" + +on: + workflow_call: + + +jobs: + test-export: + name: ${{ matrix.arch.name }} - Export Tests + runs-on: ${{ matrix.arch.os }} + strategy: + fail-fast: false + matrix: + arch: + # Linux + - { + target: x86_64-unknown-linux-musl, + os: ubuntu-20.04, + name: "Linux", + } + # MacOS + - { target: x86_64-apple-darwin, os: macos-13, name: "MacOS-x86" } + - { target: aarch64-apple-darwin, os: macos-14, name: "MacOS-arm" } # macOS-14 is the ARM chipset + # # Windows + # - { + # target: x86_64-pc-windows-msvc, + # os: windows-latest, + # extension: .exe, + # name: "Windows", + # } + steps: + - name: checkout repo + uses: actions/checkout@v4 + - name: setup micromamba + uses: mamba-org/setup-micromamba@v1.8.1 + - name: Download binary from build + uses: actions/download-artifact@v4 + with: + name: pixi-${{ matrix.arch.target }}${{ matrix.arch.extension }} + path: pixi_bin + - name: Debug + run: | + pwd + - name: Setup unix binary, add to github path + if: matrix.arch.name != 'Windows' + run: | + mv pixi_bin/pixi-${{ matrix.arch.target }} pixi_bin/pixi + chmod a+x pixi_bin/pixi + echo "$(pwd)/pixi_bin" >> $GITHUB_PATH + - name: Create Directory and Move Executable + if: matrix.arch.name == 'Windows' && matrix.arch.target == 'x86_64-pc-windows-msvc' + run: | + New-Item -ItemType Directory -Force -Path "D:\.pixi" + Move-Item -Path "pixi_bin/pixi-${{ matrix.arch.target }}${{ matrix.arch.extension }}" -Destination "D:\.pixi\pixi.exe" + shell: pwsh + - name: Add to PATH + if: matrix.arch.name == 'Windows' && matrix.arch.target == 'x86_64-pc-windows-msvc' + run: echo "D:\.pixi" | Out-File -Append -Encoding utf8 -FilePath $env:GITHUB_PATH + shell: pwsh + - name: Verify and Use Executable + if: matrix.arch.name == 'Windows' && matrix.arch.target == 'x86_64-pc-windows-msvc' + run: | + echo "Current PATH: $env:PATH" + pixi --version + shell: pwsh + - name: Help + run: pixi --help + - name: Info + run: pixi info + - name: Install pixi + run: pixi install -v + - name: Test export + shell: bash + run: bash tests/test_export.sh diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 64fae06b0..a1a4b0f86 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -1178,6 +1178,29 @@ List the environments in the manifest file. pixi project environment list ``` +### `project export conda_environment` + +Exports a conda [`environment.yml` file](https://conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html#creating-an-environment-from-an-environment-yml-file). The file can be used to create a conda environment using conda/mamba: + +```shell +pixi project export conda-environment environment.yml +mamba create --name --file environment.yml +``` + +##### Arguments + +1. ``: Optional path to render environment.yml to. Otherwise it will be printed to standard out. + +##### Options + +- `--environment (-e)`: Environment to render. +- `--platform (-p)`: The platform to render. + +```sh +pixi project export conda-environment --environment lint +pixi project export conda-environment --platform linux-64 environment.linux-64.yml +``` + ### `project export conda_explicit_spec` Render a platform-specific conda [explicit specification file](https://conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html#building-identical-conda-environments) diff --git a/docs/switching_from/conda.md b/docs/switching_from/conda.md index 7c7b1d3df..dea4311bf 100644 --- a/docs/switching_from/conda.md +++ b/docs/switching_from/conda.md @@ -70,6 +70,12 @@ pixi init --import environment.yml ``` This will create a new project with the dependencies from the `environment.yml` file. +??? tip "Exporting your environment" + If you are working with Conda users or systems, you can [export your environment to a `environment.yml`](../reference/cli.md#project-export-conda_environment) file to share them. + ```shell + pixi project export conda + ``` + Additionally you can export a [conda explicit specification](../reference/cli.md#project-export-conda_explicit_spec). ## Troubleshooting Encountering issues? Here are solutions to some common problems when being used to the `conda` workflow: diff --git a/src/cli/project/export/conda_environment.rs b/src/cli/project/export/conda_environment.rs new file mode 100644 index 000000000..4223f49e9 --- /dev/null +++ b/src/cli/project/export/conda_environment.rs @@ -0,0 +1,362 @@ +use std::path::PathBuf; + +use clap::Parser; +use itertools::Itertools; +use miette::{Context, IntoDiagnostic}; +use pep508_rs::ExtraName; +use pixi_manifest::{ + pypi::{PyPiPackageName, VersionOrStar}, + FeaturesExt, HasFeaturesIter, PyPiRequirement, +}; +use rattler_conda_types::{ + EnvironmentYaml, MatchSpec, MatchSpecOrSubSection, ParseStrictness, Platform, +}; +use rattler_lock::FindLinksUrlOrPath; + +use crate::project::Environment; +use crate::Project; + +#[derive(Debug, Parser)] +pub struct Args { + /// Explicit path to export the environment to + pub output_path: Option, + + /// The platform to render the environment file for. + /// Defaults to the current platform. + #[arg(short, long)] + pub platform: Option, + + /// The environment to render the environment file for. + /// Defaults to the default environment. + #[arg(short, long)] + pub environment: Option, +} + +fn format_pip_extras(extras: &[ExtraName]) -> String { + if extras.is_empty() { + return String::new(); + } + format!( + "[{}]", + extras.iter().map(|extra| format!("{extra}")).join("") + ) +} + +fn format_pip_dependency(name: &PyPiPackageName, requirement: &PyPiRequirement) -> String { + match requirement { + PyPiRequirement::Git { + url: git_url, + extras, + } => { + let mut git_string = format!( + "{name}{extras} @ git+{url}", + name = name.as_normalized(), + extras = format_pip_extras(extras), + url = git_url.git, + ); + + if let Some(ref branch) = git_url.branch { + git_string.push_str(&format!("@{branch}")); + } else if let Some(ref tag) = git_url.tag { + git_string.push_str(&format!("@{tag}")); + } else if let Some(ref rev) = git_url.rev { + git_string.push_str(&format!("@{rev}")); + } + + if let Some(ref subdirectory) = git_url.subdirectory { + git_string.push_str(&format!("#subdirectory=={subdirectory}")); + } + + git_string + } + PyPiRequirement::Path { + path, + editable, + extras, + } => { + if let Some(_editable) = editable { + format!( + "-e {path}{extras}", + path = path.to_string_lossy(), + extras = format_pip_extras(extras), + ) + } else { + format!( + "{path}{extras}", + path = path.to_string_lossy(), + extras = format_pip_extras(extras), + ) + } + } + PyPiRequirement::Url { + url, + subdirectory, + extras, + } => { + let mut url_string = format!( + "{name}{extras} @ {url}", + name = name.as_normalized(), + extras = format_pip_extras(extras), + url = url, + ); + + if let Some(ref subdirectory) = subdirectory { + url_string.push_str(&format!("#subdirectory=={subdirectory}")); + } + + url_string + } + PyPiRequirement::Version { version, extras } => { + format!( + "{name}{extras}{version}", + name = name.as_normalized(), + extras = format_pip_extras(extras), + version = version + ) + } + PyPiRequirement::RawVersion(version) => match version { + VersionOrStar::Version(_) => format!( + "{name}{version}", + name = name.as_normalized(), + version = version + ), + VersionOrStar::Star => format!("{name}", name = name.as_normalized()), + }, + } +} + +fn build_env_yaml( + platform: &Platform, + environment: &Environment, +) -> miette::Result { + let mut env_yaml = rattler_conda_types::EnvironmentYaml { + name: Some(environment.name().as_str().to_string()), + channels: environment.channels().into_iter().cloned().collect_vec(), + ..Default::default() + }; + + let mut pip_dependencies: Vec = Vec::new(); + + for feature in environment.features() { + if let Some(dependencies) = feature.dependencies(None, Some(*platform)) { + for (key, value) in dependencies.iter() { + let spec = MatchSpec { + name: Some(key.clone()), + version: value.clone().into_version(), + build: None, + build_number: None, + subdir: None, + md5: None, + sha256: None, + url: None, + file_name: None, + channel: None, + namespace: None, + }; + env_yaml + .dependencies + .push(MatchSpecOrSubSection::MatchSpec(spec)); + } + } + + if feature.has_pypi_dependencies() { + if let Some(pypi_dependencies) = feature.pypi_dependencies(Some(*platform)) { + for (name, requirement) in pypi_dependencies.iter() { + pip_dependencies.push(format_pip_dependency(name, requirement)); + } + } + } + } + + if !pip_dependencies.is_empty() { + let pypi_options = environment.pypi_options(); + if let Some(ref find_links) = pypi_options.find_links { + for find_link in find_links { + match find_link { + FindLinksUrlOrPath::Url(url) => { + pip_dependencies.insert(0, format!("--find-links {url}")); + } + FindLinksUrlOrPath::Path(path) => { + pip_dependencies + .insert(0, format!("--find-links {}", path.to_string_lossy())); + } + } + } + } + if let Some(ref extra_index_urls) = pypi_options.extra_index_urls { + for extra_index_url in extra_index_urls { + pip_dependencies.insert(0, format!("--extra-index-url {extra_index_url}")); + } + } + if let Some(ref index_url) = pypi_options.index_url { + pip_dependencies.insert(0, format!("--index-url {index_url}")); + } + + env_yaml.dependencies.push(MatchSpecOrSubSection::MatchSpec( + MatchSpec::from_str("pip", ParseStrictness::Lenient).unwrap(), + )); + + env_yaml + .dependencies + .push(MatchSpecOrSubSection::SubSection( + "pip".to_string(), + pip_dependencies.into_iter().collect_vec(), + )); + } + + Ok(env_yaml) +} + +pub async fn execute(project: Project, args: Args) -> miette::Result<()> { + let environment = project.environment_from_name_or_env_var(args.environment)?; + let platform = args.platform.unwrap_or_else(|| environment.best_platform()); + + let env_yaml = build_env_yaml(&platform, &environment).unwrap(); + + if let Some(output_path) = args.output_path { + env_yaml + .to_path(output_path.as_path()) + .into_diagnostic() + .with_context(|| "failed to write environment YAML")?; + } else { + println!("{}", env_yaml.to_yaml_string()); + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + use std::path::Path; + + #[test] + fn test_export_conda_env_yaml() { + let path = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("src/cli/project/export/test-data/testenv/pixi.toml"); + let project = Project::from_path(&path).unwrap(); + let args = Args { + output_path: None, + platform: Some(Platform::Osx64), + environment: Some("default".to_string()), + }; + let environment = project + .environment_from_name_or_env_var(args.environment) + .unwrap(); + let platform = args.platform.unwrap_or_else(|| environment.best_platform()); + + let env_yaml = build_env_yaml(&platform, &environment); + insta::assert_snapshot!( + "test_export_conda_env_yaml", + env_yaml.unwrap().to_yaml_string() + ); + } + + #[test] + fn test_export_conda_env_yaml_with_pip_extras() { + let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("examples/pypi/pixi.toml"); + let project = Project::from_path(&path).unwrap(); + let args = Args { + output_path: None, + platform: None, + environment: Some("default".to_string()), + }; + let environment = project + .environment_from_name_or_env_var(args.environment) + .unwrap(); + let platform = args.platform.unwrap_or_else(|| environment.best_platform()); + + let env_yaml = build_env_yaml(&platform, &environment); + insta::assert_snapshot!( + "test_export_conda_env_yaml_with_pip_extras", + env_yaml.unwrap().to_yaml_string() + ); + } + + #[test] + fn test_export_conda_env_yaml_with_pip_source_editable() { + let path = + Path::new(env!("CARGO_MANIFEST_DIR")).join("examples/pypi-source-deps/pixi.toml"); + let project = Project::from_path(&path).unwrap(); + let args = Args { + output_path: None, + platform: None, + environment: Some("default".to_string()), + }; + let environment = project + .environment_from_name_or_env_var(args.environment) + .unwrap(); + let platform = args.platform.unwrap_or_else(|| environment.best_platform()); + + let env_yaml = build_env_yaml(&platform, &environment); + insta::assert_snapshot!( + "test_export_conda_env_yaml_with_source_editable", + env_yaml.unwrap().to_yaml_string() + ); + } + + #[test] + fn test_export_conda_env_yaml_with_pip_custom_registry() { + let path = + Path::new(env!("CARGO_MANIFEST_DIR")).join("examples/pypi-custom-registry/pixi.toml"); + let project = Project::from_path(&path).unwrap(); + let args = Args { + output_path: None, + platform: None, + environment: Some("alternative".to_string()), + }; + let environment = project + .environment_from_name_or_env_var(args.environment) + .unwrap(); + let platform = args.platform.unwrap_or_else(|| environment.best_platform()); + + let env_yaml = build_env_yaml(&platform, &environment); + insta::assert_snapshot!( + "test_export_conda_env_yaml_with_pip_custom_registry", + env_yaml.unwrap().to_yaml_string() + ); + } + + #[test] + fn test_export_conda_env_yaml_with_pip_find_links() { + let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("examples/pypi-find-links/pixi.toml"); + let project = Project::from_path(&path).unwrap(); + let args = Args { + output_path: None, + platform: None, + environment: Some("default".to_string()), + }; + let environment = project + .environment_from_name_or_env_var(args.environment) + .unwrap(); + let platform = args.platform.unwrap_or_else(|| environment.best_platform()); + + let env_yaml = build_env_yaml(&platform, &environment); + insta::assert_snapshot!( + "test_export_conda_env_yaml_with_pip_find_links", + env_yaml.unwrap().to_yaml_string() + ); + } + + #[test] + fn test_export_conda_env_yaml_pyproject_panic() { + let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("examples/docker/pyproject.toml"); + let project = Project::from_path(&path).unwrap(); + let args = Args { + output_path: None, + platform: Some(Platform::OsxArm64), + environment: Some("default".to_string()), + }; + let environment = project + .environment_from_name_or_env_var(args.environment) + .unwrap(); + let platform = args.platform.unwrap_or_else(|| environment.best_platform()); + + let env_yaml = build_env_yaml(&platform, &environment); + insta::assert_snapshot!( + "test_export_conda_env_yaml_pyproject_panic", + env_yaml.unwrap().to_yaml_string() + ); + } +} diff --git a/src/cli/project/export/mod.rs b/src/cli/project/export/mod.rs index 8c135f2a1..6fb4f8e99 100644 --- a/src/cli/project/export/mod.rs +++ b/src/cli/project/export/mod.rs @@ -1,4 +1,5 @@ use std::path::PathBuf; +pub mod conda_environment; pub mod conda_explicit_spec; use crate::Project; @@ -20,12 +21,15 @@ pub enum Command { /// Export project environment to a conda explicit specification file #[clap(visible_alias = "ces")] CondaExplicitSpec(conda_explicit_spec::Args), + /// Export project environment to a conda environment.yaml file + CondaEnvironment(conda_environment::Args), } pub async fn execute(args: Args) -> miette::Result<()> { let project = Project::load_or_else_discover(args.manifest_path.as_deref())?; match args.command { Command::CondaExplicitSpec(args) => conda_explicit_spec::execute(project, args).await?, + Command::CondaEnvironment(args) => conda_environment::execute(project, args).await?, }; Ok(()) } diff --git a/src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml.snap b/src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml.snap new file mode 100644 index 000000000..3cc9acd2b --- /dev/null +++ b/src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml.snap @@ -0,0 +1,14 @@ +--- +source: src/cli/project/export/conda_environment.rs +assertion_line: 130 +expression: env_yaml.unwrap().to_yaml_string() +--- +name: default +channels: +- conda-forge +dependencies: +- python >=3.12.5,<4 +- pyyaml >=6.0.2,<7 +- pip +- pip: + - rich>=13.8.0, <14 diff --git a/src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml_pyproject_panic.snap b/src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml_pyproject_panic.snap new file mode 100644 index 000000000..03babcb22 --- /dev/null +++ b/src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml_pyproject_panic.snap @@ -0,0 +1,17 @@ +--- +source: src/cli/project/export/conda_environment.rs +assertion_line: 367 +expression: env_yaml.unwrap().to_yaml_string() +--- +name: default +channels: +- conda-forge +dependencies: +- pytest * +- hatch ==1.12.0 +- flask >=3.0.2,<3.1 +- gunicorn >=21.2.0,<21.3 +- python >=3.12 +- pip +- pip: + - -e . diff --git a/src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml_with_pip_custom_registry.snap b/src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml_with_pip_custom_registry.snap new file mode 100644 index 000000000..ef9886219 --- /dev/null +++ b/src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml_with_pip_custom_registry.snap @@ -0,0 +1,14 @@ +--- +source: src/cli/project/export/conda_environment.rs +assertion_line: 315 +expression: env_yaml.unwrap().to_yaml_string() +--- +name: alternative +channels: +- conda-forge +dependencies: +- python ==3.12 +- pip +- pip: + - --index-url https://pypi.tuna.tsinghua.edu.cn/simple/ + - flask==3.0.3 diff --git a/src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml_with_pip_extras.snap b/src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml_with_pip_extras.snap new file mode 100644 index 000000000..fb4a6e131 --- /dev/null +++ b/src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml_with_pip_extras.snap @@ -0,0 +1,23 @@ +--- +source: src/cli/project/export/conda_environment.rs +assertion_line: 187 +expression: env_yaml.unwrap().to_yaml_string() +--- +name: default +channels: +- conda-forge +dependencies: +- libclang ~=16.0.6 +- numpy 1.26.* +- python ~=3.11.0 +- scipy ~=1.11.4 +- pip +- pip: + - black[jupyter]~=24.0 + - flask + - pyboy==1.6.6 + - tensorflow==2.14.0 + - env-test-package==0.0.3 + - plot-antenna==1.8 + - pycosat + - pyliblzfse diff --git a/src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml_with_pip_find_links.snap b/src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml_with_pip_find_links.snap new file mode 100644 index 000000000..47d09c6b1 --- /dev/null +++ b/src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml_with_pip_find_links.snap @@ -0,0 +1,15 @@ +--- +source: src/cli/project/export/conda_environment.rs +assertion_line: 339 +expression: env_yaml.unwrap().to_yaml_string() +--- +name: default +channels: +- conda-forge +dependencies: +- python ==3.12 +- pip +- pip: + - --index-url https://pypi.org/simple + - --find-links ./links + - requests==2.31.0 diff --git a/src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml_with_source_editable.snap b/src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml_with_source_editable.snap new file mode 100644 index 000000000..de5f4bd89 --- /dev/null +++ b/src/cli/project/export/snapshots/pixi__cli__project__export__conda_environment__tests__test_export_conda_env_yaml_with_source_editable.snap @@ -0,0 +1,18 @@ +--- +source: src/cli/project/export/conda_environment.rs +assertion_line: 250 +expression: env_yaml.unwrap().to_yaml_string() +--- +name: default +channels: +- conda-forge +dependencies: +- python * +- pip +- pip: + - rich~=13.7 + - flask @ git+https://github.com/pallets/flask + - requests @ git+https://github.com/psf/requests.git@0106aced5faa299e6ede89d1230bd6784f2c3660 + - -e ./minimal-project + - click @ https://github.com/pallets/click/releases/download/8.1.7/click-8.1.7-py3-none-any.whl + - pytest @ git+https://github.com/pytest-dev/pytest.git diff --git a/tests/test_export.sh b/tests/test_export.sh new file mode 100644 index 000000000..618f5ca9e --- /dev/null +++ b/tests/test_export.sh @@ -0,0 +1,65 @@ +# Run from the root of the project using `bash tests/test_export.sh` +set -e +set -x +echo "Running test_export.sh" + +echo "Exporting the export test environment" +cd src/cli/project/export/test-data/testenv +pixi project export conda-environment | tee test-env.yml +echo "Creating the export test environment with micromamba" +micromamba create -y -f test-env.yml -n export-test +micromamba env list +micromamba env remove -y -n export-test +rm test-env.yml +cd ../../../../../.. + +# Setuptools error with env_test_package +# echo "Exporting an environment.yml with pip extras" +# cd examples/pypi +# pixi project export conda-environment | tee test-env-pip-extras.yml +# echo "Creating the pip extra test environment with micromamba" +# micromamba create -y -f test-env-pip-extras.yml -n export-test-pip-extras +# micromamba env list +# micromamba env remove -y -n export-test-pip-extras +# rm test-env-pip-extras.yml +# cd ../.. + +echo "Export an environment.yml with editable source dependencies" +cd examples/pypi-source-deps +pixi project export conda-environment | tee test-env-source-deps.yml +echo "Creating the editable source dependencies test environment with micromamba" +micromamba create -y -f test-env-source-deps.yml -n export-test-source-deps +micromamba env list +micromamba env remove -y -n export-test-source-deps +rm test-env-source-deps.yml +cd ../.. + +echo "Export an environment.yml with custom pip registry" +cd examples/pypi-custom-registry +pixi project export conda-environment | tee test-env-custom-registry.yml +echo "Creating the custom pip registry test environment with micromamba" +micromamba create -y -f test-env-custom-registry.yml -n export-test-custom-registry +micromamba env list +micromamba env remove -y -n export-test-custom-registry +rm test-env-custom-registry.yml +cd ../.. + +echo "Export an environment.yml with pip find links" +cd examples/pypi-find-links +pixi project export conda-environment | tee test-env-find-links.yml +echo "Creating the pip find links test environment with micromamba" +micromamba create -y -f test-env-find-links.yml -n export-test-find-links +micromamba env list +micromamba env remove -y -n export-test-find-links +rm test-env-find-links.yml +cd ../.. + +echo "Export an environment.yml from a pyproject.toml that has caused panics" +cd examples/docker +pixi project export conda-environment | tee test-env-pyproject-panic.yml +echo "Creating the pyproject.toml panic test environment with micromamba" +micromamba create -y -f test-env-pyproject-panic.yml -n export-test-pyproject-panic +micromamba env list +micromamba env remove -y -n export-test-pyproject-panic +rm test-env-pyproject-panic.yml +cd ../..