diff --git a/.github/workflows/trampoline.yaml b/.github/workflows/trampoline.yaml index 217daac56..d087e11c7 100644 --- a/.github/workflows/trampoline.yaml +++ b/.github/workflows/trampoline.yaml @@ -4,10 +4,14 @@ on: push: paths: - 'crates/pixi_trampoline/**' + - '.github/workflows/trampoline.yaml' + - 'src/global/trampoline.rs' workflow_dispatch: pull_request: paths: - 'crates/pixi_trampoline/**' + - '.github/workflows/trampoline.yaml' + - 'src/global/trampoline.rs' permissions: contents: write # Allow write permissions for contents (like pushing to the repo) @@ -25,6 +29,7 @@ jobs: include: - { name: "Linux-x86_64", target: x86_64-unknown-linux-musl, os: ubuntu-latest } - { name: "Linux-aarch64", target: aarch64-unknown-linux-musl, os: ubuntu-latest } + - { name: "Linux-powerpc64", target: powerpc64-unknown-linux-gnu, os: ubuntu-latest } - { name: "macOS-x86", target: x86_64-apple-darwin, os: macos-13 } - { name: "macOS-arm", target: aarch64-apple-darwin, os: macos-14 } - { name: "Windows", target: x86_64-pc-windows-msvc, os: windows-latest } diff --git a/CHANGELOG.md b/CHANGELOG.md index 966a7194b..33a9a6bb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,50 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +### [0.35.0] - 2024-11-05 +#### ✨ Highlights + +`pixi global` now exposed binaries are not scripts anymore but actual executables. +Resulting in significant speedup and better compatibility with other tools. + +#### Added + +- Add language packages with minor pinning by default by @ruben-arts in [#2310](https://github.com/prefix-dev/pixi/pull/2310) +- Add grouping for exposing and removing by @nichmor in [#2387](https://github.com/prefix-dev/pixi/pull/2387) +- Add trampoline for pixi global by @Hofer-Julian and @nichmor in [#2381](https://github.com/prefix-dev/pixi/pull/2381) +- Adding SCM option for init command by @alvgaona in [#2342](https://github.com/prefix-dev/pixi/pull/2342) +- Create `.pixi/.gitignore` containing `*` by @maresb in [#2361](https://github.com/prefix-dev/pixi/pull/2361) + +#### Changed + +- Use the same package cache folder by @nichmor in [#2335](https://github.com/prefix-dev/pixi/pull/2335)zx +- Disable progress in non tty by @ruben-arts in [#2308](https://github.com/prefix-dev/pixi/pull/2308) +- Improve global install reporting by @Hofer-Julian in [#2395](https://github.com/prefix-dev/pixi/pull/2395) +- Suggest fix in platform error message by @maurosilber in [#2404](https://github.com/prefix-dev/pixi/pull/2404) +- Upgrading uv to `0.4.30` by @tdejager in [#2372](https://github.com/prefix-dev/pixi/pull/2372) + +#### Documentation + +- Add pybind11 example by @alvgaona in [#2324](https://github.com/prefix-dev/pixi/pull/2324) +- Replace build with uv in pybind11 example by @alvgaona in [#2341](https://github.com/prefix-dev/pixi/pull/2341) +- Fix incorrect statement about env location by @opcode81 in [#2370](https://github.com/prefix-dev/pixi/pull/2370) + +#### Fixed + +- Global update reporting by @Hofer-Julian in [#2352](https://github.com/prefix-dev/pixi/pull/2352) +- Correctly display unrequested environments on `task list` by @jjjermiah in [#2402](https://github.com/prefix-dev/pixi/pull/2402) + +#### Refactor + +- Use built in string methods by @KGrewal1 in [#2348](https://github.com/prefix-dev/pixi/pull/2348) +- Reorganize integration tests by @Hofer-Julian in [#2408](https://github.com/prefix-dev/pixi/pull/2408) +- Reimplement barrier cell on OnceLock by @KGrewal1 in [#2347](https://github.com/prefix-dev/pixi/pull/2347) + +#### New Contributors +* @maurosilber made their first contribution in [#2404](https://github.com/prefix-dev/pixi/pull/2404) +* @opcode81 made their first contribution in [#2370](https://github.com/prefix-dev/pixi/pull/2370) +* @alvgaona made their first contribution in [#2342](https://github.com/prefix-dev/pixi/pull/2342) + ### [0.34.0] - 2024-10-21 #### ✨ Highlights diff --git a/CITATION.cff b/CITATION.cff index d3c951fc3..742594948 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -30,8 +30,8 @@ authors: - given-names: Julian family-names: Hofer email: julian.hofer@protonmail.com -repository-code: 'https://github.com/prefix-dev/pixi/releases/tag/v0.34.0' -url: 'https://pixi.sh/v0.34.0' +repository-code: 'https://github.com/prefix-dev/pixi/releases/tag/v0.35.0' +url: 'https://pixi.sh/v0.35.0' abstract: >- A cross-platform, language agnostic, package/project management tool for development in virtual environments. diff --git a/Cargo.lock b/Cargo.lock index c48de3cc7..65cbfe6e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3201,7 +3201,7 @@ dependencies = [ [[package]] name = "pixi" -version = "0.34.0" +version = "0.35.0" dependencies = [ "ahash", "assert_matches", diff --git a/Cargo.toml b/Cargo.toml index 64ec48c1b..64a3ee85e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -151,7 +151,7 @@ license.workspace = true name = "pixi" readme.workspace = true repository.workspace = true -version = "0.34.0" +version = "0.35.0" [features] default = ["rustls-tls"] diff --git a/crates/pixi_consts/src/consts.rs b/crates/pixi_consts/src/consts.rs index a422215fb..9bd989dfa 100644 --- a/crates/pixi_consts/src/consts.rs +++ b/crates/pixi_consts/src/consts.rs @@ -14,7 +14,7 @@ pub const CONFIG_FILE: &str = "config.toml"; pub const PIXI_DIR: &str = ".pixi"; pub const PIXI_VERSION: &str = match option_env!("PIXI_VERSION") { Some(v) => v, - None => "0.34.0", + None => "0.35.0", }; pub const PREFIX_FILE_NAME: &str = "pixi_env_prefix"; pub const ENVIRONMENTS_DIR: &str = "envs"; diff --git a/docs/advanced/explain_info_command.md b/docs/advanced/explain_info_command.md index 49aa29876..dabfed2ae 100644 --- a/docs/advanced/explain_info_command.md +++ b/docs/advanced/explain_info_command.md @@ -57,7 +57,7 @@ In that case, if pixi cannot find the `__cuda` virtual package on your machine t ### Cache dir The directory where pixi stores its cache. -Checkout the [cache documentation](../features/environment.md#caching) for more information. +Checkout the [cache documentation](../features/environment.md#caching-packages) for more information. ### Auth storage diff --git a/docs/advanced/github_actions.md b/docs/advanced/github_actions.md index 7be3f5616..543521db6 100644 --- a/docs/advanced/github_actions.md +++ b/docs/advanced/github_actions.md @@ -15,7 +15,7 @@ We created [prefix-dev/setup-pixi](https://github.com/prefix-dev/setup-pixi) to ```yaml - uses: prefix-dev/setup-pixi@v0.8.0 with: - pixi-version: v0.34.0 + pixi-version: v0.35.0 cache: true auth-host: prefix.dev auth-token: ${{ secrets.PREFIX_DEV_TOKEN }} diff --git a/docs/advanced/production_deployment.md b/docs/advanced/production_deployment.md index c9dc900d1..67c463842 100644 --- a/docs/advanced/production_deployment.md +++ b/docs/advanced/production_deployment.md @@ -33,7 +33,7 @@ It also makes use of `pixi shell-hook` to not rely on pixi being installed in th For more examples, take a look at [pavelzw/pixi-docker-example](https://github.com/pavelzw/pixi-docker-example). ```Dockerfile -FROM ghcr.io/prefix-dev/pixi:0.34.0 AS build +FROM ghcr.io/prefix-dev/pixi:0.35.0 AS build # copy source code, pixi.toml and pixi.lock to the container WORKDIR /app diff --git a/docs/features/environment.md b/docs/features/environment.md index e99aa2d69..2e0124e67 100644 --- a/docs/features/environment.md +++ b/docs/features/environment.md @@ -40,6 +40,31 @@ These directories are conda environments, and you can use them as such, but you Pixi will always make sure the environment is in sync with the `pixi.lock` file. If this is not the case then all the commands that use the environment will automatically update the environment, e.g. `pixi run`, `pixi shell`. +### Environment Installation Metadata +On environment installation, pixi will write a small file to the environment that contains some metadata about installation. +This file is called `pixi` and is located in the `conda-meta` folder of the environment. +This file contains the following information: +- `manifest_path`: The path to the manifest file that describes the project used to create this environment +- `environment_name`: The name of the environment +- `pixi_version`: The version of pixi that was used to create this environment +- `environment_lock_file_hash`: The hash of the `pixi.lock` file that was used to create this environment + +```json +{ + "manifest_path": "/home/user/dev/pixi/pixi.toml", + "environment_name": "default", + "pixi_version": "0.34.0", + "environment_lock_file_hash": "4f36ee620f10329d" +} +``` + +The `environment_lock_file_hash` is used to check if the environment is in sync with the `pixi.lock` file. +If the hash of the `pixi.lock` file is different from the hash in the `pixi` file, pixi will update the environment. + +This is used to speedup activation, in order to trigger a full revalidation pass `--revalidate` to the `pixi run` or `pixi shell` command. +A broken environment would typically not be found with a hash comparison, but a revalidation would reinstall the environment. +By default, all lock file modifying commands will always use the revalidation and on `pixi install` it always revalidates. + ### Cleaning up If you want to clean up the environments, you can simply delete the `.pixi/envs` directory, and pixi will recreate the environments when needed. @@ -186,7 +211,7 @@ For the `[pypi-dependencies]`, `uv` implements `sdist` building to retrieve the For this building step, `pixi` requires to first install `python` in the (conda)`[dependencies]` section of the `pixi.toml` file. This will always be slower than the pure conda solves. So for the best pixi experience you should stay within the `[dependencies]` section of the `pixi.toml` file. -## Caching +## Caching packages Pixi caches all previously downloaded packages in a cache folder. This cache folder is shared between all pixi projects and globally installed tools. diff --git a/docs/ide_integration/devcontainer.md b/docs/ide_integration/devcontainer.md index 5fa0b0b86..c368ad21b 100644 --- a/docs/ide_integration/devcontainer.md +++ b/docs/ide_integration/devcontainer.md @@ -11,7 +11,7 @@ Then, create the following two files in the `.devcontainer` directory: ```dockerfile title=".devcontainer/Dockerfile" FROM mcr.microsoft.com/devcontainers/base:jammy -ARG PIXI_VERSION=v0.34.0 +ARG PIXI_VERSION=v0.35.0 RUN curl -L -o /usr/local/bin/pixi -fsSL --compressed "https://github.com/prefix-dev/pixi/releases/download/${PIXI_VERSION}/pixi-$(uname -m)-unknown-linux-musl" \ && chmod +x /usr/local/bin/pixi \ diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 26d970299..3bd9242a9 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -189,6 +189,33 @@ pixi update --dry-run pixi update --no-install boto3 ``` +## `upgrade` + +The `upgrade` command checks if there are newer versions of the dependencies and upgrades them in the [manifest file](project_configuration.md). +`update` updates dependencies in the lock file while still fulfilling the version requirements set in the manifest. +`upgrade` loosens the requirements for the given packages, updates the lock file and the adapts the manifest accordingly. + +##### Arguments + +1. `[PACKAGES]...` The packages to upgrade, space separated. If no packages are provided, all packages will be upgraded. + +##### Options +- `--manifest-path `: the path to [manifest file](project_configuration.md), by default it searches for one in the parent directories. +- `--feature (-e)`: The feature to upgrade, if none are provided all features are upgraded. +- `--no-install`: Don't install the (solve) environment needed for solving pypi-dependencies. +- `--json`: Output the changes in json format. +- `--dry-run (-n)`: Only show the changes that would be made, without actually updating the manifest, lock file, or environment. + +```shell +pixi upgrade +pixi upgrade numpy +pixi upgrade numpy pandas +pixi upgrade --manifest-path ~/myproject/pixi.toml numpy +pixi upgrade --feature lint python +pixi upgrade --json +pixi upgrade --dry-run +``` + ## `run` The `run` commands first checks if the environment is ready to use. @@ -208,6 +235,8 @@ You cannot run `pixi run source setup.bash` as `source` is not available in the - `--locked`: only install if the `pixi.lock` is up-to-date with the [manifest file](project_configuration.md)[^1]. It can also be controlled by the `PIXI_LOCKED` environment variable (example: `PIXI_LOCKED=true`). Conflicts with `--frozen`. - `--environment (-e)`: The environment to run the task in, if none are provided the default environment will be used or a selector will be given to select the right environment. - `--clean-env`: Run the task in a clean environment, this will remove all environment variables of the shell environment except for the ones pixi sets. THIS DOESN't WORK ON `Windows`. +- `--revalidate`: Revalidate the full environment, instead of checking the lock file hash. [more info](../features/environment.md#environment-installation-metadata) + ```shell pixi run python pixi run cowpy "Hey pixi user" @@ -636,6 +665,7 @@ To exit the pixi shell, simply run `exit`. - `--frozen`: install the environment as defined in the lock file, doesn't update `pixi.lock` if it isn't up-to-date with [manifest file](project_configuration.md). It can also be controlled by the `PIXI_FROZEN` environment variable (example: `PIXI_FROZEN=true`). - `--locked`: only install if the `pixi.lock` is up-to-date with the [manifest file](project_configuration.md)[^1]. It can also be controlled by the `PIXI_LOCKED` environment variable (example: `PIXI_LOCKED=true`). Conflicts with `--frozen`. - `--environment (-e)`: The environment to activate the shell in, if none are provided the default environment will be used or a selector will be given to select the right environment. +- `--revalidate`: Revalidate the full environment, instead of checking lock file hash. [more info](../features/environment.md#environment-installation-metadata) ```shell pixi shell @@ -664,6 +694,7 @@ This command prints the activation script of an environment. - `--environment (-e)`: The environment to activate, if none are provided the default environment will be used or a selector will be given to select the right environment. - `--json`: Print all environment variables that are exported by running the activation script as JSON. When specifying this option, `--shell` is ignored. +- `--revalidate`: Revalidate the full environment, instead of checking lock file hash. [more info](../features/environment.md#environment-installation-metadata) ```shell pixi shell-hook diff --git a/install/install.ps1 b/install/install.ps1 index 24cf5ad43..426f1bd06 100644 --- a/install/install.ps1 +++ b/install/install.ps1 @@ -18,7 +18,7 @@ .LINK https://github.com/prefix-dev/pixi .NOTES - Version: v0.34.0 + Version: v0.35.0 #> param ( [string] $PixiVersion = 'latest', diff --git a/install/install.sh b/install/install.sh index e7788fff2..16e1404cd 100644 --- a/install/install.sh +++ b/install/install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash set -euo pipefail -# Version: v0.34.0 +# Version: v0.35.0 __wrap__() { diff --git a/pixi.toml b/pixi.toml index 72f5b96d5..59d034bbd 100644 --- a/pixi.toml +++ b/pixi.toml @@ -56,7 +56,7 @@ test-integration-fast = { cmd = "pytest -m 'not slow' --pixi-build=debug --numpr # you can also pass a specific test function, like this: # /path/to/test.py::test_function test-specific-test = { cmd = "pytest", depends-on = ["build"] } -update-integration-test-channels = { cmd = "python update-channels.py", cwd = "tests/integration/test_data" } +update-test-channels = { cmd = "python update-channels.py", cwd = "tests/data/channels" } [feature.dev.dependencies] # Needed for the citation diff --git a/schema/schema.json b/schema/schema.json index fb2a0cf31..722a06a09 100644 --- a/schema/schema.json +++ b/schema/schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://pixi.sh/v0.34.0/schema/manifest/schema.json", + "$id": "https://pixi.sh/v0.35.0/schema/manifest/schema.json", "title": "`pixi.toml` manifest file", "description": "The configuration for a [`pixi`](https://pixi.sh) project.", "type": "object", @@ -13,7 +13,7 @@ "title": "Schema", "description": "The schema identifier for the project's configuration", "type": "string", - "default": "https://pixi.sh/v0.34.0/schema/manifest/schema.json", + "default": "https://pixi.sh/v0.35.0/schema/manifest/schema.json", "format": "uri-reference" }, "activation": { diff --git a/src/cli/add.rs b/src/cli/add.rs index 066a03b7d..0c79a6f8b 100644 --- a/src/cli/add.rs +++ b/src/cli/add.rs @@ -1,38 +1,14 @@ -use std::{ - collections::{HashMap, HashSet}, - str::FromStr, -}; - use clap::Parser; use indexmap::IndexMap; -use itertools::Itertools; -use pep440_rs::VersionSpecifiers; -use pep508_rs::{Requirement, VersionOrUrl::VersionSpecifier}; -use pixi_config::PinningStrategy; -use pixi_manifest::{ - pypi::PyPiPackageName, DependencyOverwriteBehavior, FeatureName, FeaturesExt, HasFeaturesIter, - SpecType, -}; -use rattler_conda_types::{MatchSpec, Platform, Version}; -use rattler_lock::{LockFile, Package}; +use pixi_manifest::FeatureName; use super::has_specs::HasSpecs; -use crate::environment::LockFileUsage; use crate::{ cli::cli_config::{DependencyConfig, PrefixUpdateConfig, ProjectConfig}, environment::verify_prefix_location_unchanged, - load_lock_file, - lock_file::{filter_lock_file, LockFileDerivedData, UpdateContext}, - project::{grouped_environment::GroupedEnvironment, DependencyType, Project}, + project::{DependencyType, Project}, }; -/// List of packages that are not following the semver versioning scheme -/// but will use the minor version by default when adding a dependency. -// Don't forget to add to the docstring if you add a package here! -const NON_SEMVER_PACKAGES: [&str; 11] = [ - "python", "rust", "julia", "gcc", "gxx", "gfortran", "nodejs", "deno", "r", "r-base", "perl", -]; - /// Adds dependencies to the project /// /// The dependencies should be defined as MatchSpec for conda package, or a PyPI @@ -101,9 +77,9 @@ pub struct Args { pub async fn execute(args: Args) -> miette::Result<()> { let (dependency_config, prefix_update_config, project_config) = ( - args.dependency_config, - args.prefix_update_config, - args.project_config, + &args.dependency_config, + &args.prefix_update_config, + &args.project_config, ); let mut project = Project::load_or_else_discover(project_config.manifest_path.as_deref())? @@ -115,349 +91,44 @@ pub async fn execute(args: Args) -> miette::Result<()> { // Add the platform if it is not already present project .manifest - .add_platforms(dependency_config.platform.iter(), &FeatureName::Default)?; + .add_platforms(dependency_config.platforms.iter(), &FeatureName::Default)?; - // Add the individual specs to the project. - let mut conda_specs_to_add_constraints_for = IndexMap::new(); - let mut pypi_specs_to_add_constraints_for = IndexMap::new(); - let mut conda_packages = HashSet::new(); - let mut pypi_packages = HashSet::new(); - match dependency_config.dependency_type() { + let (match_specs, pypi_deps) = match dependency_config.dependency_type() { DependencyType::CondaDependency(spec_type) => { - let specs = dependency_config.specs()?; - let channel_config = project.channel_config(); - for (name, spec) in specs { - let added = project.manifest.add_dependency( - &spec, - spec_type, - &dependency_config.platform, - &dependency_config.feature_name(), - DependencyOverwriteBehavior::Overwrite, - &channel_config, - )?; - if added { - if spec.version.is_none() { - conda_specs_to_add_constraints_for.insert(name.clone(), (spec_type, spec)); - } - conda_packages.insert(name); - } - } + let match_specs = dependency_config + .specs()? + .into_iter() + .map(|(name, spec)| (name, (spec, spec_type))) + .collect(); + let pypi_deps = IndexMap::default(); + (match_specs, pypi_deps) } DependencyType::PypiDependency => { - let specs = dependency_config.pypi_deps(&project)?; - for (name, spec) in specs { - let added = project.manifest.add_pep508_dependency( - &spec, - &dependency_config.platform, - &dependency_config.feature_name(), - Some(args.editable), - DependencyOverwriteBehavior::Overwrite, - )?; - if added { - if spec.version_or_url.is_none() { - pypi_specs_to_add_constraints_for.insert(name.clone(), spec); - } - pypi_packages.insert(name.as_normalized().clone()); - } - } + let match_specs = IndexMap::default(); + let pypi_deps = dependency_config.pypi_deps(&project)?; + (match_specs, pypi_deps) } - } - - // If the lock-file should not be updated we only need to save the project. - if prefix_update_config.lock_file_usage() != LockFileUsage::Update { - project.save()?; - - // Notify the user we succeeded. - dependency_config.display_success("Added", HashMap::default()); - - Project::warn_on_discovered_from_env(project_config.manifest_path.as_deref()); - return Ok(()); - } - - // Load the current lock-file - let lock_file = load_lock_file(&project).await?; - - // Determine the environments that are affected by the change. - let feature_name = dependency_config.feature_name(); - let affected_environments = project - .environments() - .iter() - // Filter out any environment that does not contain the feature we modified - .filter(|e| e.features().any(|f| f.name == feature_name)) - // Expand the selection to also included any environment that shares the same solve - // group - .flat_map(|e| { - GroupedEnvironment::from(e.clone()) - .environments() - .collect_vec() - }) - .unique() - .collect_vec(); - let default_environment_is_affected = - affected_environments.contains(&project.default_environment()); - - tracing::debug!( - "environments affected by the add command: {}", - affected_environments.iter().map(|e| e.name()).format(", ") - ); - - // Determine the combination of platforms and environments that are affected by - // the command - let affect_environment_and_platforms = affected_environments - .into_iter() - // Create an iterator over all environment and platform combinations - .flat_map(|e| e.platforms().into_iter().map(move |p| (e.clone(), p))) - // Filter out any platform that is not affected by the changes. - .filter(|(_, platform)| { - dependency_config.platform.is_empty() || dependency_config.platform.contains(platform) - }) - .map(|(e, p)| (e.name().to_string(), p)) - .collect_vec(); - - // Create an updated lock-file where the dependencies to be added are removed - // from the lock-file. - let unlocked_lock_file = unlock_packages( - &project, - &lock_file, - conda_packages, - pypi_packages, - affect_environment_and_platforms - .iter() - .map(|(e, p)| (e.as_str(), *p)) - .collect(), - ); - - // Solve the updated project. - let LockFileDerivedData { - project: _, // We don't need the project here - lock_file, - package_cache, - uv_context, - updated_conda_prefixes, - updated_pypi_prefixes, - io_concurrency_limit, - } = UpdateContext::builder(&project) - .with_lock_file(unlocked_lock_file) - .with_no_install(prefix_update_config.no_install()) - .finish()? - .update() - .await?; - - // Update the constraints of specs that didn't have a version constraint based - // on the contents of the lock-file. - let implicit_constraints = if !conda_specs_to_add_constraints_for.is_empty() { - update_conda_specs_from_lock_file( - &mut project, - &lock_file, - conda_specs_to_add_constraints_for, - affect_environment_and_platforms, - &feature_name, - &dependency_config.platform, - )? - } else if !pypi_specs_to_add_constraints_for.is_empty() { - update_pypi_specs_from_lock_file( - &mut project, - &lock_file, - pypi_specs_to_add_constraints_for, - affect_environment_and_platforms, - &feature_name, - &dependency_config.platform, - args.editable, - )? - } else { - HashMap::new() - }; - - // Write the lock-file and the project to disk - project.save()?; - - // Reconstruct the lock-file derived data. - let mut updated_lock_file = LockFileDerivedData { - project: &project, - lock_file, - package_cache, - updated_conda_prefixes, - updated_pypi_prefixes, - uv_context, - io_concurrency_limit, }; - if !prefix_update_config.no_lockfile_update { - updated_lock_file.write_to_disk()?; - } + // TODO: add dry_run logic to add + let dry_run = false; + + let update_deps = project + .update_dependencies( + match_specs, + pypi_deps, + prefix_update_config, + &args.dependency_config.feature, + &args.dependency_config.platforms, + args.editable, + dry_run, + ) + .await?; - // Install/update the default environment if: - // - we are not skipping the installation, - // - there is only the default environment, - // - and the default environment is affected by the changes, - if !prefix_update_config.no_install() - && project.environments().len() == 1 - && default_environment_is_affected - { - updated_lock_file - .prefix(&project.default_environment()) - .await?; + if let Some(update_deps) = update_deps { + // Notify the user we succeeded + dependency_config.display_success("Added", update_deps.implicit_constraints); } - // Notify the user we succeeded. - dependency_config.display_success("Added", implicit_constraints); - Project::warn_on_discovered_from_env(project_config.manifest_path.as_deref()); Ok(()) } - -/// Update the pypi specs of newly added packages based on the contents of the -/// updated lock-file. -fn update_pypi_specs_from_lock_file( - project: &mut Project, - updated_lock_file: &LockFile, - pypi_specs_to_add_constraints_for: IndexMap, - affect_environment_and_platforms: Vec<(String, Platform)>, - feature_name: &FeatureName, - platforms: &[Platform], - editable: bool, -) -> miette::Result> { - let mut implicit_constraints = HashMap::new(); - - let pypi_records = affect_environment_and_platforms - .into_iter() - // Get all the conda and pypi records for the combination of environments and - // platforms - .filter_map(|(env, platform)| { - let locked_env = updated_lock_file.environment(&env)?; - locked_env.pypi_packages_for_platform(platform) - }) - .flatten() - .collect_vec(); - - let pinning_strategy = project.config().pinning_strategy.unwrap_or_default(); - - // Determine the versions of the packages in the lock-file - for (name, req) in pypi_specs_to_add_constraints_for { - let version_constraint = pinning_strategy.determine_version_constraint( - pypi_records - .iter() - .filter_map(|(data, _)| { - if &data.name == name.as_normalized() { - Version::from_str(&data.version.to_string()).ok() - } else { - None - } - }) - .collect_vec() - .iter(), - ); - - let version_spec = - version_constraint.and_then(|spec| VersionSpecifiers::from_str(&spec.to_string()).ok()); - if let Some(version_spec) = version_spec { - implicit_constraints.insert(name.as_source().to_string(), version_spec.to_string()); - let req = Requirement { - version_or_url: Some(VersionSpecifier(version_spec)), - ..req - }; - project.manifest.add_pep508_dependency( - &req, - platforms, - feature_name, - Some(editable), - DependencyOverwriteBehavior::Overwrite, - )?; - } - } - - Ok(implicit_constraints) -} - -/// Update the conda specs of newly added packages based on the contents of the -/// updated lock-file. -fn update_conda_specs_from_lock_file( - project: &mut Project, - updated_lock_file: &LockFile, - conda_specs_to_add_constraints_for: IndexMap< - rattler_conda_types::PackageName, - (SpecType, MatchSpec), - >, - affect_environment_and_platforms: Vec<(String, Platform)>, - feature_name: &FeatureName, - platforms: &[Platform], -) -> miette::Result> { - let mut implicit_constraints = HashMap::new(); - - // Determine the conda records that were affected by the add. - let conda_records = affect_environment_and_platforms - .into_iter() - // Get all the conda and pypi records for the combination of environments and - // platforms - .filter_map(|(env, platform)| { - let locked_env = updated_lock_file.environment(&env)?; - locked_env - .conda_repodata_records_for_platform(platform) - .ok()? - }) - .flatten() - .collect_vec(); - - let mut pinning_strategy = project.config().pinning_strategy; - let channel_config = project.channel_config(); - for (name, (spec_type, spec)) in conda_specs_to_add_constraints_for { - // Edge case: some packages are a special case where we want to pin the minor version by default. - // This is done to avoid early user confusion when the minor version changes and environments magically start breaking. - // This move a `>=3.13, <4` to a `>=3.13, <3.14` constraint. - if NON_SEMVER_PACKAGES.contains(&name.as_normalized()) && pinning_strategy.is_none() { - tracing::info!( - "Pinning {} to minor version by default", - name.as_normalized() - ); - pinning_strategy = Some(PinningStrategy::Minor); - } - - let version_constraint = pinning_strategy - .unwrap_or_default() - .determine_version_constraint(conda_records.iter().filter_map(|record| { - if record.package_record.name == name { - Some(record.package_record.version.version()) - } else { - None - } - })); - - if let Some(version_constraint) = version_constraint { - implicit_constraints - .insert(name.as_source().to_string(), version_constraint.to_string()); - let spec = MatchSpec { - version: Some(version_constraint), - ..spec - }; - project.manifest.add_dependency( - &spec, - spec_type, - platforms, - feature_name, - DependencyOverwriteBehavior::Overwrite, - &channel_config, - )?; - } - } - - Ok(implicit_constraints) -} - -/// Constructs a new lock-file where some of the constraints have been removed. -fn unlock_packages( - project: &Project, - lock_file: &LockFile, - conda_packages: HashSet, - pypi_packages: HashSet, - affected_environments: HashSet<(&str, Platform)>, -) -> LockFile { - filter_lock_file(project, lock_file, |env, platform, package| { - if affected_environments.contains(&(env.name().as_str(), platform)) { - match package { - Package::Conda(package) => !conda_packages.contains(&package.package_record().name), - Package::Pypi(package) => !pypi_packages.contains(&package.data().package.name), - } - } else { - true - } - }) -} diff --git a/src/cli/cli_config.rs b/src/cli/cli_config.rs index e8084e245..9fcd00eb4 100644 --- a/src/cli/cli_config.rs +++ b/src/cli/cli_config.rs @@ -1,5 +1,6 @@ use crate::cli::has_specs::HasSpecs; use crate::environment::LockFileUsage; +use crate::lock_file::UpdateMode; use crate::DependencyType; use crate::Project; use clap::Parser; @@ -98,6 +99,10 @@ pub struct PrefixUpdateConfig { #[clap(flatten)] pub config: ConfigCli, + + /// Run the complete environment validation. This will reinstall a broken environment. + #[arg(long)] + pub revalidate: bool, } impl PrefixUpdateConfig { pub fn lock_file_usage(&self) -> LockFileUsage { @@ -114,6 +119,15 @@ impl PrefixUpdateConfig { pub(crate) fn no_install(&self) -> bool { self.no_install || self.no_lockfile_update } + + /// Which `[UpdateMode]` to use + pub(crate) fn update_mode(&self) -> UpdateMode { + if self.revalidate { + UpdateMode::Revalidate + } else { + UpdateMode::QuickValidate + } + } } #[derive(Parser, Debug, Default)] pub struct DependencyConfig { @@ -137,12 +151,12 @@ pub struct DependencyConfig { pub pypi: bool, /// The platform(s) for which the dependency should be modified - #[arg(long, short)] - pub platform: Vec, + #[arg(long = "platform", short)] + pub platforms: Vec, /// The feature for which the dependency should be modified - #[arg(long, short)] - pub feature: Option, + #[clap(long, short, default_value_t)] + pub feature: FeatureName, } impl DependencyConfig { @@ -157,11 +171,7 @@ impl DependencyConfig { DependencyType::CondaDependency(SpecType::Run) } } - pub(crate) fn feature_name(&self) -> FeatureName { - self.feature - .clone() - .map_or(FeatureName::Default, FeatureName::Named) - } + pub(crate) fn display_success( &self, operation: &str, @@ -193,14 +203,14 @@ impl DependencyConfig { } // Print something if we've modified for platforms - if !self.platform.is_empty() { + if !self.platforms.is_empty() { eprintln!( "{operation} these only for platform(s): {}", - console::style(self.platform.iter().join(", ")).bold() + console::style(self.platforms.iter().join(", ")).bold() ) } // Print something if we've modified for features - if let Some(feature) = &self.feature { + if let FeatureName::Named(feature) = &self.feature { { eprintln!( "{operation} these only for feature: {}", diff --git a/src/cli/install.rs b/src/cli/install.rs index 2a0af222e..62c1aa020 100644 --- a/src/cli/install.rs +++ b/src/cli/install.rs @@ -1,5 +1,6 @@ use crate::cli::cli_config::ProjectConfig; use crate::environment::update_prefix; +use crate::lock_file::UpdateMode; use crate::Project; use clap::Parser; use fancy_display::FancyDisplay; @@ -52,7 +53,13 @@ pub async fn execute(args: Args) -> miette::Result<()> { let environment = project.environment_from_name_or_env_var(Some(env))?; // Update the prefix by installing all packages - update_prefix(&environment, args.lock_file_usage.into(), false).await?; + update_prefix( + &environment, + args.lock_file_usage.into(), + false, + UpdateMode::Revalidate, + ) + .await?; installed_envs.push(environment.name().clone()); } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 7c749dc2a..d526639eb 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -35,6 +35,7 @@ pub mod shell_hook; pub mod task; pub mod tree; pub mod update; +pub mod upgrade; pub mod upload; #[derive(Parser, Debug)] @@ -106,6 +107,7 @@ pub enum Command { #[clap(visible_alias = "i")] Install(install::Args), Update(update::Args), + Upgrade(upgrade::Args), #[clap(visible_alias = "r")] Run(run::Args), @@ -290,6 +292,7 @@ pub async fn execute_command(command: Command) -> miette::Result<()> { Command::List(cmd) => list::execute(cmd).await, Command::Tree(cmd) => tree::execute(cmd).await, Command::Update(cmd) => update::execute(cmd).await, + Command::Upgrade(cmd) => upgrade::execute(cmd).await, Command::Exec(args) => exec::execute(args).await, } } diff --git a/src/cli/project/channel/add.rs b/src/cli/project/channel/add.rs index 98735edc3..e4137df81 100644 --- a/src/cli/project/channel/add.rs +++ b/src/cli/project/channel/add.rs @@ -1,5 +1,6 @@ use crate::{ environment::{update_prefix, LockFileUsage}, + lock_file::UpdateMode, Project, }; @@ -16,6 +17,7 @@ pub async fn execute(mut project: Project, args: AddRemoveArgs) -> miette::Resul &project.default_environment(), LockFileUsage::Update, args.no_install, + UpdateMode::Revalidate, ) .await?; project.save()?; diff --git a/src/cli/project/channel/remove.rs b/src/cli/project/channel/remove.rs index b93b773f2..b78306981 100644 --- a/src/cli/project/channel/remove.rs +++ b/src/cli/project/channel/remove.rs @@ -1,3 +1,4 @@ +use crate::lock_file::UpdateMode; use crate::{ environment::{update_prefix, LockFileUsage}, Project, @@ -16,6 +17,7 @@ pub async fn execute(mut project: Project, args: AddRemoveArgs) -> miette::Resul &project.default_environment(), LockFileUsage::Update, args.no_install, + UpdateMode::Revalidate, ) .await?; project.save()?; diff --git a/src/cli/project/platform/add.rs b/src/cli/project/platform/add.rs index eda05acf0..79382e2eb 100644 --- a/src/cli/project/platform/add.rs +++ b/src/cli/project/platform/add.rs @@ -2,6 +2,7 @@ use std::str::FromStr; use crate::{ environment::{update_prefix, LockFileUsage}, + lock_file::UpdateMode, Project, }; use clap::Parser; @@ -48,6 +49,7 @@ pub async fn execute(mut project: Project, args: Args) -> miette::Result<()> { &project.default_environment(), LockFileUsage::Update, args.no_install, + UpdateMode::Revalidate, ) .await?; project.save()?; diff --git a/src/cli/project/platform/remove.rs b/src/cli/project/platform/remove.rs index ed63344ba..0cc22137f 100644 --- a/src/cli/project/platform/remove.rs +++ b/src/cli/project/platform/remove.rs @@ -1,5 +1,6 @@ use std::str::FromStr; +use crate::lock_file::UpdateMode; use crate::{ environment::{update_prefix, LockFileUsage}, Project, @@ -47,6 +48,7 @@ pub async fn execute(mut project: Project, args: Args) -> miette::Result<()> { &project.default_environment(), LockFileUsage::Update, args.no_install, + UpdateMode::Revalidate, ) .await?; project.save()?; diff --git a/src/cli/remove.rs b/src/cli/remove.rs index d9c1a0cff..7dde9f224 100644 --- a/src/cli/remove.rs +++ b/src/cli/remove.rs @@ -6,6 +6,7 @@ use crate::DependencyType; use crate::Project; use crate::cli::cli_config::{DependencyConfig, PrefixUpdateConfig, ProjectConfig}; +use crate::lock_file::UpdateMode; use super::has_specs::HasSpecs; @@ -46,8 +47,8 @@ pub async fn execute(args: Args) -> miette::Result<()> { .manifest .remove_pypi_dependency( name, - &dependency_config.platform, - &dependency_config.feature_name(), + &dependency_config.platforms, + &dependency_config.feature, ) .wrap_err(format!( "failed to remove PyPI dependency: '{}'", @@ -62,8 +63,8 @@ pub async fn execute(args: Args) -> miette::Result<()> { .remove_dependency( name, spec_type, - &dependency_config.platform, - &dependency_config.feature_name(), + &dependency_config.platforms, + &dependency_config.feature, ) .wrap_err(format!( "failed to remove dependency: '{}'", @@ -82,6 +83,7 @@ pub async fn execute(args: Args) -> miette::Result<()> { &project.default_environment(), prefix_update_config.lock_file_usage(), prefix_update_config.no_install, + UpdateMode::Revalidate, ) .await?; } diff --git a/src/cli/run.rs b/src/cli/run.rs index f2841eb52..725eea267 100644 --- a/src/cli/run.rs +++ b/src/cli/run.rs @@ -1,12 +1,11 @@ -use std::collections::hash_map::Entry; -use std::collections::HashSet; -use std::convert::identity; -use std::{collections::HashMap, string::String}; - use clap::Parser; use dialoguer::theme::ColorfulTheme; use itertools::Itertools; use miette::{Diagnostic, IntoDiagnostic}; +use std::collections::hash_map::Entry; +use std::collections::HashSet; +use std::convert::identity; +use std::{collections::HashMap, string::String}; use crate::cli::cli_config::{PrefixUpdateConfig, ProjectConfig}; use crate::environment::verify_prefix_location_unchanged; @@ -169,7 +168,12 @@ pub async fn execute(args: Args) -> miette::Result<()> { Entry::Occupied(env) => env.into_mut(), Entry::Vacant(entry) => { // Ensure there is a valid prefix - lock_file.prefix(&executable_task.run_environment).await?; + lock_file + .prefix( + &executable_task.run_environment, + args.prefix_update_config.update_mode(), + ) + .await?; let command_env = get_task_env( &executable_task.run_environment, diff --git a/src/cli/shell.rs b/src/cli/shell.rs index cac1d2d61..1b8d7cbb0 100644 --- a/src/cli/shell.rs +++ b/src/cli/shell.rs @@ -9,6 +9,7 @@ use rattler_shell::{ }; use crate::cli::cli_config::{PrefixUpdateConfig, ProjectConfig}; +use crate::lock_file::UpdateMode; use crate::{ activation::CurrentEnvVarBehavior, environment::update_prefix, project::virtual_packages::verify_current_platform_has_required_virtual_packages, prompt, @@ -241,6 +242,7 @@ pub async fn execute(args: Args) -> miette::Result<()> { &environment, args.prefix_update_config.lock_file_usage(), false, + UpdateMode::QuickValidate, ) .await?; diff --git a/src/cli/shell_hook.rs b/src/cli/shell_hook.rs index 5da49c455..6236451ff 100644 --- a/src/cli/shell_hook.rs +++ b/src/cli/shell_hook.rs @@ -10,12 +10,12 @@ use rattler_shell::{ use serde::Serialize; use serde_json; -use crate::cli::cli_config::{PrefixUpdateConfig, ProjectConfig}; -use crate::project::HasProjectRef; +use crate::activation::CurrentEnvVarBehavior; +use crate::environment::update_prefix; use crate::{ - activation::{get_activator, CurrentEnvVarBehavior}, - environment::update_prefix, - project::Environment, + activation::get_activator, + cli::cli_config::{PrefixUpdateConfig, ProjectConfig}, + project::{Environment, HasProjectRef}, Project, }; @@ -113,6 +113,7 @@ pub async fn execute(args: Args) -> miette::Result<()> { &environment, args.prefix_update_config.lock_file_usage(), false, + args.prefix_update_config.update_mode(), ) .await?; diff --git a/src/cli/update.rs b/src/cli/update.rs index 15876f1fe..e10ecaf3a 100644 --- a/src/cli/update.rs +++ b/src/cli/update.rs @@ -1,32 +1,24 @@ -use std::{ - borrow::Cow, - cmp::Ordering, - collections::HashSet, - io::{stdout, Write}, -}; +use std::{cmp::Ordering, collections::HashSet}; use fancy_display::FancyDisplay; -use crate::cli::cli_config::ProjectConfig; +use crate::{ + cli::cli_config::ProjectConfig, + diff::{LockFileDiff, LockFileJsonDiff}, +}; use crate::{ load_lock_file, lock_file::{filter_lock_file, UpdateContext}, Project, }; -use ahash::HashMap; use clap::Parser; -use indexmap::IndexMap; -use itertools::{Either, Itertools}; +use itertools::Itertools; use miette::{Context, IntoDiagnostic, MietteDiagnostic}; use pixi_config::ConfigCli; use pixi_consts::consts; use pixi_manifest::EnvironmentName; -use pixi_manifest::FeaturesExt; use rattler_conda_types::Platform; use rattler_lock::{LockFile, Package}; -use serde::Serialize; -use serde_json::Value; -use tabwriter::TabWriter; /// Update dependencies as recorded in the local lock file #[derive(Parser, Debug, Default)] @@ -186,7 +178,7 @@ pub async fn execute(args: Args) -> miette::Result<()> { let json = serde_json::to_string_pretty(&json_diff).expect("failed to convert to json"); println!("{}", json); } else if diff.is_empty() { - println!( + eprintln!( "{}Lock-file was already up-to-date", console::style(console::Emoji("✔ ", "")).green() ); @@ -279,495 +271,3 @@ fn unlock_packages(project: &Project, lock_file: &LockFile, specs: &UpdateSpecs) !specs.should_relax(env.name(), &platform, package) }) } - -// Represents the differences between two sets of packages. -#[derive(Default, Clone)] -pub struct PackagesDiff { - pub added: Vec, - pub removed: Vec, - pub changed: Vec<(rattler_lock::Package, rattler_lock::Package)>, -} - -impl PackagesDiff { - /// Returns true if the diff is empty. - pub(crate) fn is_empty(&self) -> bool { - self.added.is_empty() && self.removed.is_empty() && self.changed.is_empty() - } -} - -/// Contains the changes between two lock-files. -pub struct LockFileDiff { - pub environment: IndexMap>, -} - -impl LockFileDiff { - /// Determine the difference between two lock-files. - pub(crate) fn from_lock_files(previous: &LockFile, current: &LockFile) -> Self { - let mut result = Self { - environment: IndexMap::new(), - }; - - for (environment_name, environment) in current.environments() { - let previous = previous.environment(environment_name); - - let mut environment_diff = IndexMap::new(); - - for (platform, packages) in environment.packages_by_platform() { - // Determine the packages that were previously there. - let (mut previous_conda_packages, mut previous_pypi_packages): ( - HashMap<_, _>, - HashMap<_, _>, - ) = previous - .as_ref() - .and_then(|e| e.packages(platform)) - .into_iter() - .flatten() - .partition_map(|p| match p { - rattler_lock::Package::Conda(p) => { - Either::Left((p.package_record().name.clone(), p)) - } - rattler_lock::Package::Pypi(p) => { - Either::Right((p.data().package.name.clone(), p)) - } - }); - - let mut diff = PackagesDiff::default(); - - // Find new and changed packages - for package in packages { - match package { - Package::Conda(p) => { - let name = &p.package_record().name; - match previous_conda_packages.remove(name) { - Some(previous) if previous.url() != p.url() => { - diff.changed - .push((Package::Conda(previous), Package::Conda(p))); - } - None => { - diff.added.push(Package::Conda(p)); - } - _ => {} - } - } - Package::Pypi(p) => { - let name = &p.data().package.name; - match previous_pypi_packages.remove(name) { - Some(previous) if previous.url() != p.url() => { - diff.changed - .push((Package::Pypi(previous), Package::Pypi(p))); - } - None => { - diff.added.push(Package::Pypi(p)); - } - _ => {} - } - } - } - } - - // Determine packages that were removed - for (_, p) in previous_conda_packages { - diff.removed.push(Package::Conda(p)); - } - for (_, p) in previous_pypi_packages { - diff.removed.push(Package::Pypi(p)); - } - - environment_diff.insert(platform, diff); - } - - // Find platforms that were completely removed - for (platform, packages) in previous - .as_ref() - .map(|e| e.packages_by_platform()) - .into_iter() - .flatten() - .filter(|(platform, _)| !environment_diff.contains_key(platform)) - .collect_vec() - { - let mut diff = PackagesDiff::default(); - for package in packages { - match package { - Package::Conda(p) => { - diff.removed.push(Package::Conda(p)); - } - Package::Pypi(p) => { - diff.removed.push(Package::Pypi(p)); - } - } - } - environment_diff.insert(platform, diff); - } - - // Remove empty diffs - environment_diff.retain(|_, diff| !diff.is_empty()); - - result - .environment - .insert(environment_name.to_string(), environment_diff); - } - - // Find environments that were completely removed - for (environment_name, environment) in previous - .environments() - .filter(|(name, _)| !result.environment.contains_key(*name)) - .collect_vec() - { - let mut environment_diff = IndexMap::new(); - for (platform, packages) in environment.packages_by_platform() { - let mut diff = PackagesDiff::default(); - for package in packages { - match package { - Package::Conda(p) => { - diff.removed.push(Package::Conda(p)); - } - Package::Pypi(p) => { - diff.removed.push(Package::Pypi(p)); - } - } - } - environment_diff.insert(platform, diff); - } - result - .environment - .insert(environment_name.to_string(), environment_diff); - } - - // Remove empty environments - result.environment.retain(|_, diff| !diff.is_empty()); - - result - } - - /// Returns true if the diff is empty. - pub(crate) fn is_empty(&self) -> bool { - self.environment.is_empty() - } - - // Format the lock-file diff. - pub(crate) fn print(&self) -> std::io::Result<()> { - let mut writer = TabWriter::new(stdout()); - for (idx, (environment_name, environment)) in self - .environment - .iter() - .sorted_by(|(a, _), (b, _)| a.cmp(b)) - .enumerate() - { - // Find the changes that happened in all platforms. - let changes_by_platform = environment - .into_iter() - .map(|(platform, packages)| { - let changes = Self::format_changes(packages) - .into_iter() - .collect::>(); - (platform, changes) - }) - .collect::>(); - - // Find the changes that happened in all platforms. - let common_changes = changes_by_platform - .iter() - .fold(None, |acc, (_, changes)| match acc { - None => Some(changes.clone()), - Some(acc) => Some(acc.intersection(changes).cloned().collect()), - }) - .unwrap_or_default(); - - // Add a new line between environments - if idx > 0 { - writeln!(writer, "\t\t\t",)?; - } - - writeln!( - writer, - "{}: {}\t\t\t", - console::style("Environment").underlined(), - consts::ENVIRONMENT_STYLE.apply_to(environment_name) - )?; - - // Print the common changes. - for (_, line) in common_changes.iter().sorted_by_key(|(name, _)| name) { - writeln!(writer, " {}", line)?; - } - - // Print the per-platform changes. - for (platform, changes) in changes_by_platform { - let mut changes = changes - .iter() - .filter(|change| !common_changes.contains(change)) - .sorted_by_key(|(name, _)| name) - .peekable(); - if changes.peek().is_some() { - writeln!( - writer, - "{}: {}:{}\t\t\t", - console::style("Platform").underlined(), - consts::ENVIRONMENT_STYLE.apply_to(environment_name), - consts::PLATFORM_STYLE.apply_to(platform), - )?; - for (_, line) in changes { - writeln!(writer, " {}", line)?; - } - } - } - } - - writer.flush()?; - - Ok(()) - } - - fn format_changes(packages: &PackagesDiff) -> Vec<(Cow<'_, str>, String)> { - enum Change<'i> { - Added(&'i Package), - Removed(&'i Package), - Changed(&'i Package, &'i Package), - } - - fn format_package_identifier(package: &Package) -> String { - match package { - Package::Conda(p) => format!( - "{} {}", - &p.package_record().version.as_str(), - &p.package_record().build - ), - Package::Pypi(p) => p.data().package.version.to_string(), - } - } - - itertools::chain!( - packages.added.iter().map(Change::Added), - packages.removed.iter().map(Change::Removed), - packages.changed.iter().map(|a| Change::Changed(&a.0, &a.1)) - ) - .sorted_by_key(|c| match c { - Change::Added(p) => p.name(), - Change::Removed(p) => p.name(), - Change::Changed(p, _) => p.name(), - }) - .map(|p| match p { - Change::Added(p) => ( - p.name(), - format!( - "{} {} {}\t{}\t\t", - console::style("+").green(), - match p { - Package::Conda(_) => consts::CondaEmoji.to_string(), - Package::Pypi(_) => consts::PypiEmoji.to_string(), - }, - p.name(), - format_package_identifier(p) - ), - ), - Change::Removed(p) => ( - p.name(), - format!( - "{} {} {}\t{}\t\t", - console::style("-").red(), - match p { - Package::Conda(_) => consts::CondaEmoji.to_string(), - Package::Pypi(_) => consts::PypiEmoji.to_string(), - }, - p.name(), - format_package_identifier(p) - ), - ), - Change::Changed(previous, current) => { - fn choose_style<'a>(a: &'a str, b: &'a str) -> console::StyledObject<&'a str> { - if a == b { - console::style(a).dim() - } else { - console::style(a) - } - } - - let name = previous.name(); - let line = match (previous, current) { - (Package::Conda(previous), Package::Conda(current)) => { - let previous = previous.package_record(); - let current = current.package_record(); - - format!( - "{} {} {}\t{} {}\t->\t{} {}", - console::style("~").yellow(), - consts::CondaEmoji, - name, - choose_style(&previous.version.as_str(), ¤t.version.as_str()), - choose_style(previous.build.as_str(), current.build.as_str()), - choose_style(¤t.version.as_str(), &previous.version.as_str()), - choose_style(current.build.as_str(), previous.build.as_str()), - ) - } - (Package::Pypi(previous), Package::Pypi(current)) => { - let previous = previous.data().package; - let current = current.data().package; - - format!( - "{} {} {}\t{}\t->\t{}", - console::style("~").yellow(), - consts::PypiEmoji, - name, - choose_style( - &previous.version.to_string(), - ¤t.version.to_string() - ), - choose_style( - ¤t.version.to_string(), - &previous.version.to_string() - ), - ) - } - _ => unreachable!(), - }; - - (name, line) - } - }) - .collect() - } -} - -#[derive(Serialize, Clone)] -pub struct JsonPackageDiff { - name: String, - before: Option, - after: Option, - #[serde(rename = "type")] - ty: JsonPackageType, - #[serde(skip_serializing_if = "std::ops::Not::not")] - explicit: bool, -} - -#[derive(Serialize, Copy, Clone)] -#[serde(rename_all = "kebab-case")] -pub enum JsonPackageType { - Conda, - Pypi, -} - -#[derive(Serialize, Clone)] -pub struct LockFileJsonDiff { - pub version: usize, - pub environment: IndexMap>>, -} - -impl LockFileJsonDiff { - fn new(project: &Project, value: LockFileDiff) -> Self { - let mut environment = IndexMap::new(); - - for (environment_name, environment_diff) in value.environment { - let mut environment_diff_json = IndexMap::new(); - - for (platform, packages_diff) in environment_diff { - let conda_dependencies = project - .environment(environment_name.as_str()) - .map(|env| env.dependencies(None, Some(platform))) - .unwrap_or_default(); - - let pypi_dependencies = project - .environment(environment_name.as_str()) - .map(|env| env.pypi_dependencies(Some(platform))) - .unwrap_or_default(); - - let add_diffs = packages_diff.added.into_iter().map(|new| match new { - Package::Conda(pkg) => JsonPackageDiff { - name: pkg.package_record().name.as_normalized().to_string(), - before: None, - after: Some(serde_json::to_value(&pkg).unwrap()), - ty: JsonPackageType::Conda, - explicit: conda_dependencies.contains_key(&pkg.package_record().name), - }, - Package::Pypi(pkg) => JsonPackageDiff { - name: pkg.data().package.name.as_dist_info_name().into_owned(), - before: None, - after: Some(serde_json::to_value(&pkg).unwrap()), - ty: JsonPackageType::Pypi, - explicit: pypi_dependencies.contains_key(&pkg.data().package.name), - }, - }); - - let removed_diffs = packages_diff.removed.into_iter().map(|old| match old { - Package::Conda(pkg) => JsonPackageDiff { - name: pkg.package_record().name.as_normalized().to_string(), - before: Some(serde_json::to_value(&pkg).unwrap()), - after: None, - ty: JsonPackageType::Conda, - explicit: conda_dependencies.contains_key(&pkg.package_record().name), - }, - - Package::Pypi(pkg) => JsonPackageDiff { - name: pkg.data().package.name.as_dist_info_name().into_owned(), - before: Some(serde_json::to_value(&pkg).unwrap()), - after: None, - ty: JsonPackageType::Pypi, - explicit: pypi_dependencies.contains_key(&pkg.data().package.name), - }, - }); - - let changed_diffs = packages_diff.changed.into_iter().map(|(old, new)| match (old, new) { - (Package::Conda(old), Package::Conda(new)) => - { - let before = serde_json::to_value(&old).unwrap(); - let after = serde_json::to_value(&new).unwrap(); - let (before, after) = compute_json_diff(before, after); - JsonPackageDiff { - name: old.package_record().name.as_normalized().to_string(), - before: Some(before), - after: Some(after), - ty: JsonPackageType::Conda, - explicit: conda_dependencies.contains_key(&old.package_record().name), - } - } - (Package::Pypi(old), Package::Pypi(new)) => { - let before = serde_json::to_value(&old).unwrap(); - let after = serde_json::to_value(&new).unwrap(); - let (before, after) = compute_json_diff(before, after); - JsonPackageDiff { - name: old.data().package.name.as_dist_info_name().into_owned(), - before: Some(before), - after: Some(after), - ty: JsonPackageType::Pypi, - explicit: pypi_dependencies.contains_key(&old.data().package.name), - } - } - _ => unreachable!("packages cannot change type, they are represented as removals and inserts instead"), - }); - - let packages_diff_json = add_diffs - .chain(removed_diffs) - .chain(changed_diffs) - .sorted_by_key(|diff| diff.name.clone()) - .collect_vec(); - - environment_diff_json.insert(platform, packages_diff_json); - } - - environment.insert(environment_name, environment_diff_json); - } - - Self { - version: 1, - environment, - } - } -} - -fn compute_json_diff( - mut a: serde_json::Value, - mut b: serde_json::Value, -) -> (serde_json::Value, serde_json::Value) { - if let (Some(a), Some(b)) = (a.as_object_mut(), b.as_object_mut()) { - a.retain(|key, value| { - if let Some(other_value) = b.get(key) { - if other_value == value { - b.remove(key); - return false; - } - } else { - b.insert(key.to_string(), Value::Null); - } - true - }); - } - (a, b) -} diff --git a/src/cli/upgrade.rs b/src/cli/upgrade.rs new file mode 100644 index 000000000..428e3a5b0 --- /dev/null +++ b/src/cli/upgrade.rs @@ -0,0 +1,255 @@ +use std::cmp::Ordering; +use std::sync::Arc; + +use crate::cli::cli_config::ProjectConfig; +use crate::Project; +use clap::Parser; +use fancy_display::FancyDisplay; +use itertools::Itertools; +use miette::MietteDiagnostic; +use miette::{Context, IntoDiagnostic}; + +use super::cli_config::PrefixUpdateConfig; +use crate::diff::LockFileJsonDiff; +use pep508_rs::MarkerTree; +use pep508_rs::Requirement; +use pixi_manifest::FeatureName; +use pixi_manifest::PyPiRequirement; +use pixi_manifest::SpecType; +use pixi_spec::PixiSpec; +use rattler_conda_types::MatchSpec; + +/// Update dependencies as recorded in the local lock file +#[derive(Parser, Debug, Default)] +pub struct Args { + #[clap(flatten)] + pub project_config: ProjectConfig, + + #[clap(flatten)] + pub prefix_update_config: PrefixUpdateConfig, + + #[clap(flatten)] + pub specs: UpgradeSpecsArgs, + + /// Output the changes in JSON format. + #[clap(long)] + pub json: bool, + + /// Only show the changes that would be made, without actually updating the manifest, lock file, or environment. + #[clap(short = 'n', long)] + pub dry_run: bool, +} + +#[derive(Parser, Debug, Default)] +pub struct UpgradeSpecsArgs { + /// The packages to upgrade + pub packages: Option>, + + /// The feature to update + #[clap(long = "feature", short = 'f', default_value_t)] + pub feature: FeatureName, + + /// The packages which should be excluded + #[clap(long, conflicts_with = "packages")] + pub exclude: Option>, +} + +pub async fn execute(args: Args) -> miette::Result<()> { + let mut project = Project::load_or_else_discover(args.project_config.manifest_path.as_deref())? + .with_cli_config(args.prefix_update_config.config.clone()); + + // Ensure that the given feature exists + let Some(feature) = project.manifest.feature(&args.specs.feature) else { + miette::bail!( + "could not find a feature named {}", + args.specs.feature.fancy_display() + ) + }; + + // TODO: Also support build and host + let spec_type = SpecType::Run; + let match_spec_iter = feature + .dependencies(Some(spec_type), None) + .into_iter() + .flat_map(|deps| deps.into_owned()); + + let pypi_deps_iter = feature + .pypi_dependencies(None) + .into_iter() + .flat_map(|deps| deps.into_owned()); + + // If the user specified a package name, check to see if it is even there. + if let Some(package_names) = &args.specs.packages { + let available_packages = match_spec_iter + .clone() + .map(|(name, _)| name.as_normalized().to_string()) + .chain( + pypi_deps_iter + .clone() + .map(|(name, _)| name.as_normalized().to_string()), + ) + .collect_vec(); + + for package in package_names { + ensure_package_exists(package, &available_packages)? + } + } + + let match_specs = match_spec_iter + // Don't upgrade excluded packages + .filter(|(name, _)| match &args.specs.exclude { + None => true, + Some(exclude) if exclude.contains(&name.as_normalized().to_string()) => false, + _ => true, + }) + // If specific packages have been requested, only upgrade those + .filter(|(name, _)| match &args.specs.packages { + None => true, + Some(packages) if packages.contains(&name.as_normalized().to_string()) => true, + _ => false, + }) + // Only upgrade version specs + .filter_map(|(name, req)| match req { + PixiSpec::DetailedVersion(version_spec) => { + let channel = version_spec + .channel + .and_then(|c| c.into_channel(&project.channel_config()).ok()) + .map(Arc::new); + Some(( + name.clone(), + ( + MatchSpec { + name: Some(name), + channel, + ..Default::default() + }, + spec_type, + ), + )) + } + PixiSpec::Version(_) => Some((name.clone(), (MatchSpec::from(name), spec_type))), + _ => None, + }) + .collect(); + + let pypi_deps = pypi_deps_iter + // Don't upgrade excluded packages + .filter(|(name, _)| match &args.specs.exclude { + None => true, + Some(exclude) if exclude.contains(&name.as_normalized().to_string()) => false, + _ => true, + }) + // If specific packages have been requested, only upgrade those + .filter(|(name, _)| match &args.specs.packages { + None => true, + Some(packages) if packages.contains(&name.as_normalized().to_string()) => true, + _ => false, + }) + // Only upgrade version specs + .filter_map(|(name, req)| match req { + PyPiRequirement::Version { extras, .. } => Some(( + name.clone(), + Requirement { + name: name.as_normalized().clone(), + extras, + marker: MarkerTree::default(), + origin: None, + version_or_url: None, + }, + )), + PyPiRequirement::RawVersion(_) => Some(( + name.clone(), + Requirement { + name: name.as_normalized().clone(), + extras: Vec::default(), + marker: MarkerTree::default(), + origin: None, + version_or_url: None, + }, + )), + _ => None, + }) + .collect(); + + let update_deps = project + .update_dependencies( + match_specs, + pypi_deps, + &args.prefix_update_config, + &args.specs.feature, + &[], + false, + args.dry_run, + ) + .await?; + + // Is there something to report? + if let Some(update_deps) = update_deps { + let diff = update_deps.lock_file_diff; + // Format as json? + if args.json { + let json_diff = LockFileJsonDiff::new(&project, diff); + let json = serde_json::to_string_pretty(&json_diff).expect("failed to convert to json"); + println!("{}", json); + } else { + diff.print() + .into_diagnostic() + .context("failed to print lock-file diff")?; + } + } else { + eprintln!( + "{}All packages are already up-to-date", + console::style(console::Emoji("✔ ", "")).green() + ); + } + + Project::warn_on_discovered_from_env(args.project_config.manifest_path.as_deref()); + Ok(()) +} + +/// Ensures the existence of the specified package +/// +/// # Returns +/// +/// Returns `miette::Result` with a descriptive error message +/// if the package does not exist. +fn ensure_package_exists(package_name: &str, available_packages: &[String]) -> miette::Result<()> { + let similar_names = available_packages + .iter() + .unique() + .filter_map(|name| { + let distance = strsim::jaro(package_name, name); + if distance > 0.6 { + Some((name, distance)) + } else { + None + } + }) + .sorted_by(|(_, a), (_, b)| b.partial_cmp(a).unwrap_or(Ordering::Equal)) + .take(5) + .map(|(name, _)| name) + .collect_vec(); + + if similar_names.first().map(|s| s.as_str()) == Some(package_name) { + return Ok(()); + } + + let message = format!("could not find a package named '{package_name}'"); + + Err(MietteDiagnostic { + message, + code: None, + severity: None, + help: if !similar_names.is_empty() { + Some(format!( + "did you mean '{}'?", + similar_names.iter().format("', '") + )) + } else { + None + }, + url: None, + labels: None, + } + .into()) +} diff --git a/src/diff.rs b/src/diff.rs new file mode 100644 index 000000000..b14560d4f --- /dev/null +++ b/src/diff.rs @@ -0,0 +1,509 @@ +use std::{ + borrow::Cow, + collections::HashSet, + io::{stderr, Write}, +}; + +use crate::Project; +use ahash::HashMap; +use indexmap::IndexMap; +use itertools::{Either, Itertools}; +use pixi_consts::consts; +use pixi_manifest::FeaturesExt; +use rattler_conda_types::Platform; +use rattler_lock::{LockFile, Package}; +use serde::Serialize; +use serde_json::Value; +use tabwriter::TabWriter; + +// Represents the differences between two sets of packages. +#[derive(Default, Clone)] +pub struct PackagesDiff { + pub added: Vec, + pub removed: Vec, + pub changed: Vec<(rattler_lock::Package, rattler_lock::Package)>, +} + +impl PackagesDiff { + /// Returns true if the diff is empty. + pub(crate) fn is_empty(&self) -> bool { + self.added.is_empty() && self.removed.is_empty() && self.changed.is_empty() + } +} + +/// Contains the changes between two lock-files. +pub struct LockFileDiff { + pub environment: IndexMap>, +} + +impl LockFileDiff { + /// Determine the difference between two lock-files. + pub(crate) fn from_lock_files(previous: &LockFile, current: &LockFile) -> Self { + let mut result = Self { + environment: IndexMap::new(), + }; + + for (environment_name, environment) in current.environments() { + let previous = previous.environment(environment_name); + + let mut environment_diff = IndexMap::new(); + + for (platform, packages) in environment.packages_by_platform() { + // Determine the packages that were previously there. + let (mut previous_conda_packages, mut previous_pypi_packages): ( + HashMap<_, _>, + HashMap<_, _>, + ) = previous + .as_ref() + .and_then(|e| e.packages(platform)) + .into_iter() + .flatten() + .partition_map(|p| match p { + rattler_lock::Package::Conda(p) => { + Either::Left((p.package_record().name.clone(), p)) + } + rattler_lock::Package::Pypi(p) => { + Either::Right((p.data().package.name.clone(), p)) + } + }); + + let mut diff = PackagesDiff::default(); + + // Find new and changed packages + for package in packages { + match package { + Package::Conda(p) => { + let name = &p.package_record().name; + match previous_conda_packages.remove(name) { + Some(previous) if previous.url() != p.url() => { + diff.changed + .push((Package::Conda(previous), Package::Conda(p))); + } + None => { + diff.added.push(Package::Conda(p)); + } + _ => {} + } + } + Package::Pypi(p) => { + let name = &p.data().package.name; + match previous_pypi_packages.remove(name) { + Some(previous) if previous.url() != p.url() => { + diff.changed + .push((Package::Pypi(previous), Package::Pypi(p))); + } + None => { + diff.added.push(Package::Pypi(p)); + } + _ => {} + } + } + } + } + + // Determine packages that were removed + for (_, p) in previous_conda_packages { + diff.removed.push(Package::Conda(p)); + } + for (_, p) in previous_pypi_packages { + diff.removed.push(Package::Pypi(p)); + } + + environment_diff.insert(platform, diff); + } + + // Find platforms that were completely removed + for (platform, packages) in previous + .as_ref() + .map(|e| e.packages_by_platform()) + .into_iter() + .flatten() + .filter(|(platform, _)| !environment_diff.contains_key(platform)) + .collect_vec() + { + let mut diff = PackagesDiff::default(); + for package in packages { + match package { + Package::Conda(p) => { + diff.removed.push(Package::Conda(p)); + } + Package::Pypi(p) => { + diff.removed.push(Package::Pypi(p)); + } + } + } + environment_diff.insert(platform, diff); + } + + // Remove empty diffs + environment_diff.retain(|_, diff| !diff.is_empty()); + + result + .environment + .insert(environment_name.to_string(), environment_diff); + } + + // Find environments that were completely removed + for (environment_name, environment) in previous + .environments() + .filter(|(name, _)| !result.environment.contains_key(*name)) + .collect_vec() + { + let mut environment_diff = IndexMap::new(); + for (platform, packages) in environment.packages_by_platform() { + let mut diff = PackagesDiff::default(); + for package in packages { + match package { + Package::Conda(p) => { + diff.removed.push(Package::Conda(p)); + } + Package::Pypi(p) => { + diff.removed.push(Package::Pypi(p)); + } + } + } + environment_diff.insert(platform, diff); + } + result + .environment + .insert(environment_name.to_string(), environment_diff); + } + + // Remove empty environments + result.environment.retain(|_, diff| !diff.is_empty()); + + result + } + + /// Returns true if the diff is empty. + pub(crate) fn is_empty(&self) -> bool { + self.environment.is_empty() + } + + // Format the lock-file diff. + pub(crate) fn print(&self) -> std::io::Result<()> { + let mut writer = TabWriter::new(stderr()); + for (idx, (environment_name, environment)) in self + .environment + .iter() + .sorted_by(|(a, _), (b, _)| a.cmp(b)) + .enumerate() + { + // Find the changes that happened in all platforms. + let changes_by_platform = environment + .into_iter() + .map(|(platform, packages)| { + let changes = Self::format_changes(packages) + .into_iter() + .collect::>(); + (platform, changes) + }) + .collect::>(); + + // Find the changes that happened in all platforms. + let common_changes = changes_by_platform + .iter() + .fold(None, |acc, (_, changes)| match acc { + None => Some(changes.clone()), + Some(acc) => Some(acc.intersection(changes).cloned().collect()), + }) + .unwrap_or_default(); + + // Add a new line between environments + if idx > 0 { + writeln!(writer, "\t\t\t",)?; + } + + writeln!( + writer, + "{}: {}\t\t\t", + console::style("Environment").underlined(), + consts::ENVIRONMENT_STYLE.apply_to(environment_name) + )?; + + // Print the common changes. + for (_, line) in common_changes.iter().sorted_by_key(|(name, _)| name) { + writeln!(writer, " {}", line)?; + } + + // Print the per-platform changes. + for (platform, changes) in changes_by_platform { + let mut changes = changes + .iter() + .filter(|change| !common_changes.contains(change)) + .sorted_by_key(|(name, _)| name) + .peekable(); + if changes.peek().is_some() { + writeln!( + writer, + "{}: {}:{}\t\t\t", + console::style("Platform").underlined(), + consts::ENVIRONMENT_STYLE.apply_to(environment_name), + consts::PLATFORM_STYLE.apply_to(platform), + )?; + for (_, line) in changes { + writeln!(writer, " {}", line)?; + } + } + } + } + + writer.flush()?; + + Ok(()) + } + + fn format_changes(packages: &PackagesDiff) -> Vec<(Cow<'_, str>, String)> { + enum Change<'i> { + Added(&'i Package), + Removed(&'i Package), + Changed(&'i Package, &'i Package), + } + + fn format_package_identifier(package: &Package) -> String { + match package { + Package::Conda(p) => format!( + "{} {}", + &p.package_record().version.as_str(), + &p.package_record().build + ), + Package::Pypi(p) => p.data().package.version.to_string(), + } + } + + itertools::chain!( + packages.added.iter().map(Change::Added), + packages.removed.iter().map(Change::Removed), + packages.changed.iter().map(|a| Change::Changed(&a.0, &a.1)) + ) + .sorted_by_key(|c| match c { + Change::Added(p) => p.name(), + Change::Removed(p) => p.name(), + Change::Changed(p, _) => p.name(), + }) + .map(|p| match p { + Change::Added(p) => ( + p.name(), + format!( + "{} {} {}\t{}\t\t", + console::style("+").green(), + match p { + Package::Conda(_) => consts::CondaEmoji.to_string(), + Package::Pypi(_) => consts::PypiEmoji.to_string(), + }, + p.name(), + format_package_identifier(p) + ), + ), + Change::Removed(p) => ( + p.name(), + format!( + "{} {} {}\t{}\t\t", + console::style("-").red(), + match p { + Package::Conda(_) => consts::CondaEmoji.to_string(), + Package::Pypi(_) => consts::PypiEmoji.to_string(), + }, + p.name(), + format_package_identifier(p) + ), + ), + Change::Changed(previous, current) => { + fn choose_style<'a>(a: &'a str, b: &'a str) -> console::StyledObject<&'a str> { + if a == b { + console::style(a).dim() + } else { + console::style(a) + } + } + + let name = previous.name(); + let line = match (previous, current) { + (Package::Conda(previous), Package::Conda(current)) => { + let previous = previous.package_record(); + let current = current.package_record(); + + format!( + "{} {} {}\t{} {}\t->\t{} {}", + console::style("~").yellow(), + consts::CondaEmoji, + name, + choose_style(&previous.version.as_str(), ¤t.version.as_str()), + choose_style(previous.build.as_str(), current.build.as_str()), + choose_style(¤t.version.as_str(), &previous.version.as_str()), + choose_style(current.build.as_str(), previous.build.as_str()), + ) + } + (Package::Pypi(previous), Package::Pypi(current)) => { + let previous = previous.data().package; + let current = current.data().package; + + format!( + "{} {} {}\t{}\t->\t{}", + console::style("~").yellow(), + consts::PypiEmoji, + name, + choose_style( + &previous.version.to_string(), + ¤t.version.to_string() + ), + choose_style( + ¤t.version.to_string(), + &previous.version.to_string() + ), + ) + } + _ => unreachable!(), + }; + + (name, line) + } + }) + .collect() + } +} + +#[derive(Serialize, Clone)] +pub struct JsonPackageDiff { + name: String, + before: Option, + after: Option, + #[serde(rename = "type")] + ty: JsonPackageType, + #[serde(skip_serializing_if = "std::ops::Not::not")] + explicit: bool, +} + +#[derive(Serialize, Copy, Clone)] +#[serde(rename_all = "kebab-case")] +pub enum JsonPackageType { + Conda, + Pypi, +} + +#[derive(Serialize, Clone)] +pub struct LockFileJsonDiff { + pub version: usize, + pub environment: IndexMap>>, +} + +impl LockFileJsonDiff { + pub fn new(project: &Project, value: LockFileDiff) -> Self { + let mut environment = IndexMap::new(); + + for (environment_name, environment_diff) in value.environment { + let mut environment_diff_json = IndexMap::new(); + + for (platform, packages_diff) in environment_diff { + let conda_dependencies = project + .environment(environment_name.as_str()) + .map(|env| env.dependencies(None, Some(platform))) + .unwrap_or_default(); + + let pypi_dependencies = project + .environment(environment_name.as_str()) + .map(|env| env.pypi_dependencies(Some(platform))) + .unwrap_or_default(); + + let add_diffs = packages_diff.added.into_iter().map(|new| match new { + Package::Conda(pkg) => JsonPackageDiff { + name: pkg.package_record().name.as_normalized().to_string(), + before: None, + after: Some(serde_json::to_value(&pkg).unwrap()), + ty: JsonPackageType::Conda, + explicit: conda_dependencies.contains_key(&pkg.package_record().name), + }, + Package::Pypi(pkg) => JsonPackageDiff { + name: pkg.data().package.name.as_dist_info_name().into_owned(), + before: None, + after: Some(serde_json::to_value(&pkg).unwrap()), + ty: JsonPackageType::Pypi, + explicit: pypi_dependencies.contains_key(&pkg.data().package.name), + }, + }); + + let removed_diffs = packages_diff.removed.into_iter().map(|old| match old { + Package::Conda(pkg) => JsonPackageDiff { + name: pkg.package_record().name.as_normalized().to_string(), + before: Some(serde_json::to_value(&pkg).unwrap()), + after: None, + ty: JsonPackageType::Conda, + explicit: conda_dependencies.contains_key(&pkg.package_record().name), + }, + + Package::Pypi(pkg) => JsonPackageDiff { + name: pkg.data().package.name.as_dist_info_name().into_owned(), + before: Some(serde_json::to_value(&pkg).unwrap()), + after: None, + ty: JsonPackageType::Pypi, + explicit: pypi_dependencies.contains_key(&pkg.data().package.name), + }, + }); + + let changed_diffs = packages_diff.changed.into_iter().map(|(old, new)| match (old, new) { + (Package::Conda(old), Package::Conda(new)) => + { + let before = serde_json::to_value(&old).unwrap(); + let after = serde_json::to_value(&new).unwrap(); + let (before, after) = compute_json_diff(before, after); + JsonPackageDiff { + name: old.package_record().name.as_normalized().to_string(), + before: Some(before), + after: Some(after), + ty: JsonPackageType::Conda, + explicit: conda_dependencies.contains_key(&old.package_record().name), + } + } + (Package::Pypi(old), Package::Pypi(new)) => { + let before = serde_json::to_value(&old).unwrap(); + let after = serde_json::to_value(&new).unwrap(); + let (before, after) = compute_json_diff(before, after); + JsonPackageDiff { + name: old.data().package.name.as_dist_info_name().into_owned(), + before: Some(before), + after: Some(after), + ty: JsonPackageType::Pypi, + explicit: pypi_dependencies.contains_key(&old.data().package.name), + } + } + _ => unreachable!("packages cannot change type, they are represented as removals and inserts instead"), + }); + + let packages_diff_json = add_diffs + .chain(removed_diffs) + .chain(changed_diffs) + .sorted_by_key(|diff| diff.name.clone()) + .collect_vec(); + + environment_diff_json.insert(platform, packages_diff_json); + } + + environment.insert(environment_name, environment_diff_json); + } + + Self { + version: 1, + environment, + } + } +} + +fn compute_json_diff( + mut a: serde_json::Value, + mut b: serde_json::Value, +) -> (serde_json::Value, serde_json::Value) { + if let (Some(a), Some(b)) = (a.as_object_mut(), b.as_object_mut()) { + a.retain(|key, value| { + if let Some(other_value) = b.get(key) { + if other_value == value { + b.remove(key); + return false; + } + } else { + b.insert(key.to_string(), Value::Null); + } + true + }); + } + (a, b) +} diff --git a/src/environment.rs b/src/environment.rs index a5c941d20..77e2ecd9a 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -1,13 +1,14 @@ -use std::{ - collections::HashMap, - convert::identity, - io::ErrorKind, - path::{Path, PathBuf}, - sync::Arc, +use crate::{ + install_pypi, + lock_file::{UpdateLockFileOptions, UpdateMode, UvResolutionContext}, + prefix::Prefix, + project::{grouped_environment::GroupedEnvironment, Environment, HasProjectRef}, + rlimit::try_increase_rlimit_to_sensible, + Project, }; - use dialoguer::theme::ColorfulTheme; use fancy_display::FancyDisplay; +use fs_err as fs; use miette::{IntoDiagnostic, WrapErr}; use pixi_consts::consts; use pixi_manifest::{EnvironmentName, FeaturesExt, SystemRequirements}; @@ -17,20 +18,23 @@ use rattler::{ package_cache::PackageCache, }; use rattler_conda_types::{Platform, PrefixRecord, RepoDataRecord}; +use rattler_lock::Package::{Conda, Pypi}; use rattler_lock::{PypiIndexes, PypiPackageData, PypiPackageEnvironmentData}; use reqwest_middleware::ClientWithMiddleware; use serde::{Deserialize, Serialize}; +use std::hash::{Hash, Hasher}; +use std::{ + collections::HashMap, + convert::identity, + io, + io::ErrorKind, + path::{Path, PathBuf}, + sync::Arc, +}; use tokio::sync::Semaphore; use uv_distribution_types::{InstalledDist, Name}; -use crate::{ - install_pypi, - lock_file::{UpdateLockFileOptions, UvResolutionContext}, - prefix::Prefix, - project::{grouped_environment::GroupedEnvironment, Environment, HasProjectRef}, - rlimit::try_increase_rlimit_to_sensible, - Project, -}; +use xxhash_rust::xxh3::Xxh3; /// Verify the location of the prefix folder is not changed so the applied /// prefix path is still valid. Errors when there is a file system error or the @@ -102,13 +106,22 @@ async fn prefix_location_changed( } } +// Write the contents to the file at the given path. +fn write_file, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { + // Verify existence of parent + if let Some(parent) = path.as_ref().parent() { + fs::create_dir_all(parent)?; + } + + fs::write(path, contents) +} + /// Create the prefix location file. /// Give it the environment path to place it. fn create_prefix_location_file(environment_dir: &Path) -> miette::Result<()> { let prefix_file_path = environment_dir .join("conda-meta") .join(consts::PREFIX_FILE_NAME); - tracing::info!("Creating prefix file at: {}", prefix_file_path.display()); let parent_dir = prefix_file_path.parent().ok_or_else(|| { miette::miette!( @@ -120,18 +133,17 @@ fn create_prefix_location_file(environment_dir: &Path) -> miette::Result<()> { if parent_dir.exists() { let contents = parent_dir.to_string_lossy(); - let path = Path::new(&prefix_file_path); // Read existing contents to determine if an update is necessary - if path.exists() { - let existing_contents = std::fs::read_to_string(path).into_diagnostic()?; + if prefix_file_path.exists() { + let existing_contents = fs::read_to_string(&prefix_file_path).into_diagnostic()?; if existing_contents == contents { tracing::info!("No update needed for the prefix file."); return Ok(()); } } - // Write new contents to the prefix file - std::fs::write(path, &*contents).into_diagnostic()?; + write_file(&prefix_file_path, contents.as_bytes()).into_diagnostic()?; + tracing::info!("Prefix file updated with: '{}'.", contents); } Ok(()) @@ -142,43 +154,80 @@ fn create_prefix_location_file(environment_dir: &Path) -> miette::Result<()> { fn create_history_file(environment_dir: &Path) -> miette::Result<()> { let history_file = environment_dir.join("conda-meta").join("history"); - tracing::info!( - "Checking if history file exists: {}", - history_file.display() - ); + tracing::info!("Verify history file exists: {}", history_file.display()); - let binding = history_file.clone(); - let parent = binding - .parent() - .ok_or_else(|| miette::miette!("cannot find parent of '{}'", binding.display()))?; + write_file( + history_file, + "// not relevant for pixi but for `conda run -p`", + ) + .into_diagnostic() +} - if parent.exists() && !history_file.exists() { - tracing::info!("Creating history file: {}", history_file.display()); - std::fs::write( - history_file, - "// not relevant for pixi but for `conda run -p`", - ) - .into_diagnostic()?; +#[derive(Debug, Hash, Serialize, Deserialize, PartialEq, Eq)] +pub struct LockedEnvironmentHash(String); +impl LockedEnvironmentHash { + pub(crate) fn from_environment( + environment: rattler_lock::Environment, + platform: Platform, + ) -> Self { + let mut hasher = Xxh3::new(); + + if let Some(packages) = environment.packages(platform) { + for package in packages { + // Always has the url or path + package + .url_or_path() + .into_owned() + .to_string() + .hash(&mut hasher); + + match package { + // A select set of fields are used to hash the package + Conda(pack) => { + if let Some(sha) = pack.package_record().sha256 { + sha.hash(&mut hasher); + } else if let Some(md5) = pack.package_record().md5 { + md5.hash(&mut hasher); + } + } + Pypi(pack) => { + pack.is_editable().hash(&mut hasher); + pack.extras().hash(&mut hasher); + } + } + } + } + + LockedEnvironmentHash(format!("{:x}", hasher.finish())) } - Ok(()) } +/// Information about the environment that was used to create the environment. #[derive(Serialize, Deserialize)] pub(crate) struct EnvironmentFile { + /// The path to the manifest file that was used to create the environment. pub(crate) manifest_path: PathBuf, + /// The name of the environment. pub(crate) environment_name: String, + /// The version of the pixi that was used to create the environment. pub(crate) pixi_version: String, + /// The hash of the lock file that was used to create the environment. + pub(crate) environment_lock_file_hash: LockedEnvironmentHash, +} + +/// The path to the environment file in the `conda-meta` directory of the environment. +fn environment_file_path(environment_dir: &Path) -> PathBuf { + environment_dir + .join(consts::CONDA_META_DIR) + .join(consts::ENVIRONMENT_FILE_NAME) } /// Write information about the environment to a file in the environment -/// directory. This can be useful for other tools that only know the environment -/// directory to find the original project. +/// directory. Used by the prefix updating to validate if it needs to be updated. pub(crate) fn write_environment_file( environment_dir: &Path, env_file: EnvironmentFile, ) -> miette::Result { - let path = environment_dir - .join("conda-meta") - .join(consts::ENVIRONMENT_FILE_NAME); + let path = environment_file_path(environment_dir); let parent = path .parent() @@ -195,6 +244,45 @@ pub(crate) fn write_environment_file( Ok(path) } +/// Reading the environment file of the environment. +/// Removing it if it's not valid. +pub(crate) fn read_environment_file( + environment_dir: &Path, +) -> miette::Result> { + let path = environment_file_path(environment_dir); + + let contents = match std::fs::read_to_string(&path) { + Ok(contents) => contents, + Err(e) if e.kind() == ErrorKind::NotFound => { + tracing::debug!("Environment file not yet found at: {:?}", path); + return Ok(None); + } + Err(e) => { + tracing::debug!( + "Failed to read environment file at: {:?}, error: {}, will try to remove it.", + path, + e + ); + let _ = std::fs::remove_file(&path); + return Err(e).into_diagnostic(); + } + }; + let env_file: EnvironmentFile = match serde_json::from_str(&contents) { + Ok(env_file) => env_file, + Err(e) => { + tracing::debug!( + "Failed to read environment file at: {:?}, error: {}, will try to remove it.", + path, + e + ); + let _ = std::fs::remove_file(&path); + return Ok(None); + } + }; + + Ok(Some(env_file)) +} + /// Runs the following checks to make sure the project is in a sane state: /// 1. It verifies that the prefix location is unchanged. /// 2. It verifies that the system requirements are met. @@ -292,6 +380,7 @@ pub async fn update_prefix( environment: &Environment<'_>, lock_file_usage: LockFileUsage, mut no_install: bool, + update_mode: UpdateMode, ) -> miette::Result<()> { let current_platform = environment.best_platform(); let project = environment.project(); @@ -316,7 +405,7 @@ pub async fn update_prefix( // Get the locked environment from the lock-file. if !no_install { - lock_file.prefix(environment).await?; + lock_file.prefix(environment, update_mode).await?; } Ok(()) } diff --git a/src/global/trampoline.rs b/src/global/trampoline.rs index b16cfd57e..d9ea13ab6 100644 --- a/src/global/trampoline.rs +++ b/src/global/trampoline.rs @@ -64,6 +64,12 @@ const TRAMPOLINE_BIN: &[u8] = include_bytes!( "../../crates/pixi_trampoline/trampolines/pixi-trampoline-x86_64-pc-windows-msvc.exe.zst" ); +#[cfg(target_arch = "powerpc64")] +#[cfg(target_os = "linux")] +const TRAMPOLINE_BIN: &[u8] = include_bytes!( + "../../crates/pixi_trampoline/trampolines/pixi-trampoline-powerpc64le-unknown-linux-gnu.zst" +); + #[cfg(target_arch = "x86_64")] #[cfg(target_os = "linux")] const TRAMPOLINE_BIN: &[u8] = include_bytes!( diff --git a/src/lib.rs b/src/lib.rs index 59592ea1c..ce8438777 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,12 @@ pub mod activation; pub mod cli; pub(crate) mod conda_pypi_clobber; +mod diff; pub mod environment; mod global; mod install_pypi; mod install_wheel; -mod lock_file; +pub mod lock_file; mod prefix; mod project; mod prompt; diff --git a/src/lock_file/mod.rs b/src/lock_file/mod.rs index 01d379ba3..c70aca2e0 100644 --- a/src/lock_file/mod.rs +++ b/src/lock_file/mod.rs @@ -7,17 +7,21 @@ mod update; mod utils; use miette::{IntoDiagnostic, WrapErr}; -pub use outdated::OutdatedEnvironments; -pub use package_identifier::PypiPackageIdentifier; +pub(crate) use outdated::OutdatedEnvironments; +pub(crate) use package_identifier::PypiPackageIdentifier; use rattler_conda_types::RepoDataRecord; use rattler_lock::{LockFile, PypiPackageData, PypiPackageEnvironmentData}; -pub use records_by_name::{PypiRecordsByName, RepoDataRecordsByName}; -pub use resolve::{ +pub(crate) use records_by_name::{PypiRecordsByName, RepoDataRecordsByName}; +pub(crate) use resolve::{ conda::resolve_conda, pypi::resolve_pypi, uv_resolution_context::UvResolutionContext, }; -pub use satisfiability::{verify_environment_satisfiability, verify_platform_satisfiability}; -pub use update::{LockFileDerivedData, UpdateContext, UpdateLockFileOptions}; -pub use utils::filter_lock_file; +pub use satisfiability::{ + verify_environment_satisfiability, verify_platform_satisfiability, EnvironmentUnsat, + PlatformUnsat, +}; +pub(crate) use update::{LockFileDerivedData, UpdateContext}; +pub use update::{UpdateLockFileOptions, UpdateMode}; +pub(crate) use utils::filter_lock_file; use crate::Project; diff --git a/src/lock_file/records_by_name.rs b/src/lock_file/records_by_name.rs index 82178efea..201669ad7 100644 --- a/src/lock_file/records_by_name.rs +++ b/src/lock_file/records_by_name.rs @@ -8,8 +8,8 @@ use std::collections::hash_map::Entry; use std::collections::HashMap; use std::hash::Hash; -pub type RepoDataRecordsByName = DependencyRecordsByName; -pub type PypiRecordsByName = DependencyRecordsByName; +pub(crate) type RepoDataRecordsByName = DependencyRecordsByName; +pub(crate) type PypiRecordsByName = DependencyRecordsByName; /// A trait required from the dependencies stored in DependencyRecordsByName pub(crate) trait HasNameVersion { @@ -49,8 +49,8 @@ impl HasNameVersion for RepoDataRecord { /// A struct that holds both a ``Vec` of `DependencyRecord` and a mapping from name to index. #[derive(Clone, Debug)] -pub struct DependencyRecordsByName { - pub records: Vec, +pub(crate) struct DependencyRecordsByName { + pub(crate) records: Vec, by_name: HashMap, } diff --git a/src/lock_file/update.rs b/src/lock_file/update.rs index ae318372f..5cdb996dd 100644 --- a/src/lock_file/update.rs +++ b/src/lock_file/update.rs @@ -1,17 +1,3 @@ -use std::{ - borrow::Cow, - collections::{HashMap, HashSet, VecDeque}, - fmt::Write, - future::{ready, Future}, - iter, - path::PathBuf, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, - time::{Duration, Instant}, -}; - use barrier_cell::BarrierCell; use fancy_display::FancyDisplay; use futures::{future::Either, stream::FuturesUnordered, FutureExt, StreamExt, TryFutureExt}; @@ -34,12 +20,27 @@ use rattler_conda_types::{Arch, Channel, MatchSpec, Platform, RepoDataRecord}; use rattler_lock::{LockFile, PypiIndexes, PypiPackageData, PypiPackageEnvironmentData}; use rattler_repodata_gateway::{Gateway, RepoData}; use rattler_solve::ChannelPriority; +use std::cmp::PartialEq; +use std::{ + borrow::Cow, + collections::{HashMap, HashSet, VecDeque}, + fmt::Write, + future::{ready, Future}, + iter, + path::PathBuf, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + time::{Duration, Instant}, +}; use thiserror::Error; use tokio::sync::Semaphore; use tracing::Instrument; use url::Url; use uv_normalize::ExtraName; +use crate::environment::{read_environment_file, LockedEnvironmentHash}; use crate::repodata::Repodata; use crate::{ activation::CurrentEnvVarBehavior, @@ -121,6 +122,20 @@ pub struct LockFileDerivedData<'p> { pub io_concurrency_limit: IoConcurrencyLimit, } +/// The mode to use when updating a prefix. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum UpdateMode { + /// Validate if the prefix is up-to-date. + /// Using a fast and simple validation method. + /// Used for skipping the update if the prefix is already up-to-date, in activating commands. + /// Like `pixi shell` or `pixi run`. + QuickValidate, + /// Force a prefix install without running the short validation. + /// Used for updating the prefix when the lock-file likely out of date. + /// Like `pixi install` or `pixi update`. + Revalidate, +} + impl<'p> LockFileDerivedData<'p> { /// Write the lock-file to disk. pub(crate) fn write_to_disk(&self) -> miette::Result<()> { @@ -131,22 +146,71 @@ impl<'p> LockFileDerivedData<'p> { .context("failed to write lock-file to disk") } + fn locked_environment_hash( + &self, + environment: &Environment<'p>, + ) -> miette::Result { + let locked_environment = self + .lock_file + .environment(environment.name().as_str()) + .ok_or_else(|| UpdateError::LockFileMissingEnv(environment.name().clone()))?; + Ok(LockedEnvironmentHash::from_environment( + locked_environment, + environment.best_platform(), + )) + } + /// Returns the up-to-date prefix for the given environment. - pub async fn prefix(&mut self, environment: &Environment<'p>) -> miette::Result { - // Save an environment file to the environment directory + pub async fn prefix( + &mut self, + environment: &Environment<'p>, + update_mode: UpdateMode, + ) -> miette::Result { + // Check if the prefix is already up-to-date by validating the hash with the environment file + let hash = self.locked_environment_hash(environment)?; + if update_mode == UpdateMode::QuickValidate { + if let Ok(Some(environment_file)) = read_environment_file(&environment.dir()) { + if environment_file.environment_lock_file_hash == hash { + tracing::info!( + "Environment '{}' is up-to-date with lock file hash", + environment.name().fancy_display() + ); + return Ok(Prefix::new(environment.dir())); + } + } else { + tracing::debug!( + "Environment file not found or parsable for '{}'", + environment.name().fancy_display() + ); + } + } + + // Get the up-to-date prefix + let prefix = self.update_prefix(environment).await?; + + // Save an environment file to the environment directory after the update. + // Avoiding writing the cache away before the update is done. write_environment_file( &environment.dir(), EnvironmentFile { manifest_path: environment.project().manifest_path(), environment_name: environment.name().to_string(), pixi_version: consts::PIXI_VERSION.to_string(), + environment_lock_file_hash: hash, }, )?; + Ok(prefix) + } + + /// Returns the up-to-date prefix for the given environment. + async fn update_prefix(&mut self, environment: &Environment<'p>) -> miette::Result { + // If we previously updated this environment, early out. if let Some(prefix) = self.updated_pypi_prefixes.get(environment.name()) { return Ok(prefix.clone()); } + tracing::info!("Updating prefix"); // Get the prefix with the conda packages installed. let platform = environment.best_platform(); let (prefix, python_status) = self.conda_prefix(environment).await?; @@ -173,6 +237,7 @@ impl<'p> LockFileDerivedData<'p> { Some(context) => context.clone(), }; + // TODO: This can be really slow (~200ms for pixi on @ruben-arts machine). let env_variables = self .project .get_activated_environment_variables(environment, CurrentEnvVarBehavior::Exclude) diff --git a/src/project/mod.rs b/src/project/mod.rs index ccc8bac59..14254daee 100644 --- a/src/project/mod.rs +++ b/src/project/mod.rs @@ -14,31 +14,44 @@ use std::{ fmt::{Debug, Formatter}, hash::Hash, path::{Path, PathBuf}, + str::FromStr, sync::{Arc, OnceLock}, }; use async_once_cell::OnceCell as AsyncCell; pub use environment::Environment; +use grouped_environment::GroupedEnvironment; pub use has_project_ref::HasProjectRef; -use indexmap::Equivalent; +use indexmap::{Equivalent, IndexMap}; use itertools::Itertools; use miette::IntoDiagnostic; use once_cell::sync::OnceCell; -use pixi_config::Config; +use pep440_rs::VersionSpecifiers; +use pep508_rs::{Requirement, VersionOrUrl::VersionSpecifier}; +use pixi_config::{Config, PinningStrategy}; use pixi_consts::consts; use pixi_manifest::{ - EnvironmentName, Environments, HasManifestRef, Manifest, ParsedManifest, SpecType, + pypi::PyPiPackageName, DependencyOverwriteBehavior, EnvironmentName, Environments, FeatureName, + FeaturesExt, HasFeaturesIter, HasManifestRef, Manifest, ParsedManifest, SpecType, }; use pixi_utils::reqwest::build_reqwest_clients; use pypi_mapping::{ChannelName, CustomMapping, MappingLocation, MappingSource}; -use rattler_conda_types::{Channel, ChannelConfig, Version}; +use rattler_conda_types::{Channel, ChannelConfig, MatchSpec, PackageName, Platform, Version}; +use rattler_lock::{LockFile, Package}; use rattler_repodata_gateway::Gateway; use reqwest_middleware::ClientWithMiddleware; pub use solve_group::SolveGroup; use url::{ParseError, Url}; use xxhash_rust::xxh3::xxh3_64; -use crate::activation::{initialize_env_variables, CurrentEnvVarBehavior}; +use crate::{ + activation::{initialize_env_variables, CurrentEnvVarBehavior}, + cli::cli_config::PrefixUpdateConfig, + diff::LockFileDiff, + environment::LockFileUsage, + load_lock_file, + lock_file::{filter_lock_file, LockFileDerivedData, UpdateContext, UpdateMode}, +}; static CUSTOM_TARGET_DIR_WARN: OnceCell<()> = OnceCell::new(); @@ -93,6 +106,13 @@ impl EnvironmentVars { } } +/// List of packages that are not following the semver versioning scheme +/// but will use the minor version by default when adding a dependency. +// Don't forget to add to the docstring if you add a package here! +const NON_SEMVER_PACKAGES: [&str; 11] = [ + "python", "rust", "julia", "gcc", "gxx", "gfortran", "nodejs", "deno", "r", "r-base", "perl", +]; + /// The pixi project, this main struct to interact with the project. This struct /// holds the `Manifest` and has functions to modify or request information from /// it. This allows in the future to have multiple environments or manifests @@ -604,6 +624,342 @@ impl Project { pub fn manifest(&self) -> &Manifest { &self.manifest } + + /// Update the manifest with the given package specs, and upgrade the packages if possible + /// + /// 1. Modify the manifest with the given package specs, if no version is given, use `no-pin` strategy + /// 2. Update the lock file + /// 3. Given packages without version restrictions will get a semver restriction + #[allow(clippy::too_many_arguments)] + pub async fn update_dependencies( + &mut self, + match_specs: IndexMap, + pypi_deps: IndexMap, + prefix_update_config: &PrefixUpdateConfig, + feature_name: &FeatureName, + platforms: &[Platform], + editable: bool, + dry_run: bool, + ) -> Result, miette::Error> { + let mut conda_specs_to_add_constraints_for = IndexMap::new(); + let mut pypi_specs_to_add_constraints_for = IndexMap::new(); + let mut conda_packages = HashSet::new(); + let mut pypi_packages = HashSet::new(); + let channel_config = self.channel_config(); + for (name, (spec, spec_type)) in match_specs { + let added = self.manifest.add_dependency( + &spec, + spec_type, + platforms, + feature_name, + DependencyOverwriteBehavior::Overwrite, + &channel_config, + )?; + if added { + if spec.version.is_none() { + conda_specs_to_add_constraints_for.insert(name.clone(), (spec_type, spec)); + } + conda_packages.insert(name); + } + } + + for (name, spec) in pypi_deps { + let added = self.manifest.add_pep508_dependency( + &spec, + platforms, + feature_name, + Some(editable), + DependencyOverwriteBehavior::Overwrite, + )?; + if added { + if spec.version_or_url.is_none() { + pypi_specs_to_add_constraints_for.insert(name.clone(), spec); + } + pypi_packages.insert(name.as_normalized().clone()); + } + } + + // Only save to disk if not a dry run + if !dry_run { + self.save()?; + } + + if prefix_update_config.lock_file_usage() != LockFileUsage::Update { + return Ok(None); + } + + let original_lock_file = load_lock_file(self).await?; + let affected_environments = self + .environments() + .iter() + // Filter out any environment that does not contain the feature we modified + .filter(|e| e.features().any(|f| f.name == *feature_name)) + // Expand the selection to also included any environment that shares the same solve + // group + .flat_map(|e| { + GroupedEnvironment::from(e.clone()) + .environments() + .collect_vec() + }) + .unique() + .collect_vec(); + let default_environment_is_affected = + affected_environments.contains(&self.default_environment()); + tracing::debug!( + "environments affected by the add command: {}", + affected_environments.iter().map(|e| e.name()).format(", ") + ); + let affect_environment_and_platforms = affected_environments + .into_iter() + // Create an iterator over all environment and platform combinations + .flat_map(|e| e.platforms().into_iter().map(move |p| (e.clone(), p))) + // Filter out any platform that is not affected by the changes. + .filter(|(_, platform)| platforms.is_empty() || platforms.contains(platform)) + .map(|(e, p)| (e.name().to_string(), p)) + .collect_vec(); + let unlocked_lock_file = self.unlock_packages( + &original_lock_file, + conda_packages, + pypi_packages, + affect_environment_and_platforms + .iter() + .map(|(e, p)| (e.as_str(), *p)) + .collect(), + ); + let LockFileDerivedData { + project: _, // We don't need the project here + lock_file, + package_cache, + uv_context, + updated_conda_prefixes, + updated_pypi_prefixes, + io_concurrency_limit, + } = UpdateContext::builder(self) + .with_lock_file(unlocked_lock_file) + .with_no_install(prefix_update_config.no_install() || dry_run) + .finish()? + .update() + .await?; + + let mut implicit_constraints = HashMap::new(); + if !conda_specs_to_add_constraints_for.is_empty() { + let conda_constraints = self.update_conda_specs_from_lock_file( + &lock_file, + conda_specs_to_add_constraints_for, + affect_environment_and_platforms.clone(), + feature_name, + platforms, + )?; + implicit_constraints.extend(conda_constraints); + } + + if !pypi_specs_to_add_constraints_for.is_empty() { + let pypi_constraints = self.update_pypi_specs_from_lock_file( + &lock_file, + pypi_specs_to_add_constraints_for, + affect_environment_and_platforms, + feature_name, + platforms, + editable, + )?; + implicit_constraints.extend(pypi_constraints); + } + + // Only write to disk if not a dry run + if !dry_run { + self.save()?; + } + + let mut updated_lock_file = LockFileDerivedData { + project: self, + lock_file, + package_cache, + updated_conda_prefixes, + updated_pypi_prefixes, + uv_context, + io_concurrency_limit, + }; + if !prefix_update_config.no_lockfile_update && !dry_run { + updated_lock_file.write_to_disk()?; + } + if !prefix_update_config.no_install() + && !dry_run + && self.environments().len() == 1 + && default_environment_is_affected + { + updated_lock_file + .prefix(&self.default_environment(), UpdateMode::Revalidate) + .await?; + } + + let lock_file_diff = + LockFileDiff::from_lock_files(&original_lock_file, &updated_lock_file.lock_file); + + Ok(Some(UpdateDeps { + implicit_constraints, + lock_file_diff, + })) + } + + /// Constructs a new lock-file where some of the constraints have been removed. + fn unlock_packages( + &self, + lock_file: &LockFile, + conda_packages: HashSet, + pypi_packages: HashSet, + affected_environments: HashSet<(&str, Platform)>, + ) -> LockFile { + filter_lock_file(self, lock_file, |env, platform, package| { + if affected_environments.contains(&(env.name().as_str(), platform)) { + match package { + Package::Conda(package) => { + !conda_packages.contains(&package.package_record().name) + } + Package::Pypi(package) => !pypi_packages.contains(&package.data().package.name), + } + } else { + true + } + }) + } + + /// Update the conda specs of newly added packages based on the contents of the + /// updated lock-file. + fn update_conda_specs_from_lock_file( + &mut self, + updated_lock_file: &LockFile, + conda_specs_to_add_constraints_for: IndexMap, + affect_environment_and_platforms: Vec<(String, Platform)>, + feature_name: &FeatureName, + platforms: &[Platform], + ) -> miette::Result> { + let mut implicit_constraints = HashMap::new(); + + // Determine the conda records that were affected by the add. + let conda_records = affect_environment_and_platforms + .into_iter() + // Get all the conda and pypi records for the combination of environments and + // platforms + .filter_map(|(env, platform)| { + let locked_env = updated_lock_file.environment(&env)?; + locked_env + .conda_repodata_records_for_platform(platform) + .ok()? + }) + .flatten() + .collect_vec(); + + let mut pinning_strategy = self.config().pinning_strategy; + let channel_config = self.channel_config(); + for (name, (spec_type, spec)) in conda_specs_to_add_constraints_for { + // Edge case: some packages are a special case where we want to pin the minor version by default. + // This is done to avoid early user confusion when the minor version changes and environments magically start breaking. + // This move a `>=3.13, <4` to a `>=3.13, <3.14` constraint. + if NON_SEMVER_PACKAGES.contains(&name.as_normalized()) && pinning_strategy.is_none() { + tracing::info!( + "Pinning {} to minor version by default", + name.as_normalized() + ); + pinning_strategy = Some(PinningStrategy::Minor); + } + let version_constraint = pinning_strategy + .unwrap_or_default() + .determine_version_constraint(conda_records.iter().filter_map(|record| { + if record.package_record.name == name { + Some(record.package_record.version.version()) + } else { + None + } + })); + + if let Some(version_constraint) = version_constraint { + implicit_constraints + .insert(name.as_source().to_string(), version_constraint.to_string()); + let spec = MatchSpec { + version: Some(version_constraint), + ..spec + }; + self.manifest.add_dependency( + &spec, + spec_type, + platforms, + feature_name, + DependencyOverwriteBehavior::Overwrite, + &channel_config, + )?; + } + } + + Ok(implicit_constraints) + } + + /// Update the pypi specs of newly added packages based on the contents of the + /// updated lock-file. + fn update_pypi_specs_from_lock_file( + &mut self, + updated_lock_file: &LockFile, + pypi_specs_to_add_constraints_for: IndexMap, + affect_environment_and_platforms: Vec<(String, Platform)>, + feature_name: &FeatureName, + platforms: &[Platform], + editable: bool, + ) -> miette::Result> { + let mut implicit_constraints = HashMap::new(); + + let pypi_records = affect_environment_and_platforms + .into_iter() + // Get all the conda and pypi records for the combination of environments and + // platforms + .filter_map(|(env, platform)| { + let locked_env = updated_lock_file.environment(&env)?; + locked_env.pypi_packages_for_platform(platform) + }) + .flatten() + .collect_vec(); + + let pinning_strategy = self.config().pinning_strategy.unwrap_or_default(); + + // Determine the versions of the packages in the lock-file + for (name, req) in pypi_specs_to_add_constraints_for { + let version_constraint = pinning_strategy.determine_version_constraint( + pypi_records + .iter() + .filter_map(|(data, _)| { + if &data.name == name.as_normalized() { + Version::from_str(&data.version.to_string()).ok() + } else { + None + } + }) + .collect_vec() + .iter(), + ); + + let version_spec = version_constraint + .and_then(|spec| VersionSpecifiers::from_str(&spec.to_string()).ok()); + if let Some(version_spec) = version_spec { + implicit_constraints.insert(name.as_source().to_string(), version_spec.to_string()); + let req = Requirement { + version_or_url: Some(VersionSpecifier(version_spec)), + ..req + }; + self.manifest.add_pep508_dependency( + &req, + platforms, + feature_name, + Some(editable), + DependencyOverwriteBehavior::Overwrite, + )?; + } + } + + Ok(implicit_constraints) + } +} + +pub struct UpdateDeps { + pub implicit_constraints: HashMap, + pub lock_file_diff: LockFileDiff, } impl<'source> HasManifestRef<'source> for &'source Project { diff --git a/tbump.toml b/tbump.toml index b079ca725..6510ccee9 100644 --- a/tbump.toml +++ b/tbump.toml @@ -1,7 +1,7 @@ github_url = "https://github.com/prefix-dev/pixi" [version] -current = "0.34.0" +current = "0.35.0" # Example of a semver regexp. # Make sure this matches current_version before @@ -19,7 +19,7 @@ regex = ''' [git] # The current version will get updated when tbump is run -message_template = "Bump version: 0.34.0 → {new_version}" +message_template = "Bump version: 0.35.0 → {new_version}" tag_template = "v{new_version}" # For each file to patch, add a [[file]] config @@ -65,12 +65,16 @@ src = "install/install.ps1" [[file]] search = "PIXI_VERSION = \"{current_version}\"" -src = "tests/integration/common.py" +src = "tests/integration_python/common.py" [[file]] search = "None => \"{current_version}\"," src = "crates/pixi_consts/src/consts.rs" +[[file]] +search = "\"pixi_version\": \"{current_version}\"," +src = "docs/features/environment.md" + [[field]] # the name of the field name = "candidate" diff --git a/tests/data/channels/channels/dummy_channel_1/linux-64/dummy-a-0.1.0-hb0f4dca_0.conda b/tests/data/channels/channels/dummy_channel_1/linux-64/dummy-a-0.1.0-hb0f4dca_0.conda index 609543914..14e747199 100644 Binary files a/tests/data/channels/channels/dummy_channel_1/linux-64/dummy-a-0.1.0-hb0f4dca_0.conda and b/tests/data/channels/channels/dummy_channel_1/linux-64/dummy-a-0.1.0-hb0f4dca_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_1/linux-64/dummy-b-0.1.0-hb0f4dca_0.conda b/tests/data/channels/channels/dummy_channel_1/linux-64/dummy-b-0.1.0-hb0f4dca_0.conda index 88daa3e44..a984f9135 100644 Binary files a/tests/data/channels/channels/dummy_channel_1/linux-64/dummy-b-0.1.0-hb0f4dca_0.conda and b/tests/data/channels/channels/dummy_channel_1/linux-64/dummy-b-0.1.0-hb0f4dca_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_1/linux-64/dummy-c-0.1.0-hb0f4dca_0.conda b/tests/data/channels/channels/dummy_channel_1/linux-64/dummy-c-0.1.0-hb0f4dca_0.conda index 08143f96d..e7a664506 100644 Binary files a/tests/data/channels/channels/dummy_channel_1/linux-64/dummy-c-0.1.0-hb0f4dca_0.conda and b/tests/data/channels/channels/dummy_channel_1/linux-64/dummy-c-0.1.0-hb0f4dca_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_1/linux-64/dummy-d-0.1.0-hb0f4dca_0.conda b/tests/data/channels/channels/dummy_channel_1/linux-64/dummy-d-0.1.0-hb0f4dca_0.conda index 5e3ffbc34..442a1c0cc 100644 Binary files a/tests/data/channels/channels/dummy_channel_1/linux-64/dummy-d-0.1.0-hb0f4dca_0.conda and b/tests/data/channels/channels/dummy_channel_1/linux-64/dummy-d-0.1.0-hb0f4dca_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_1/linux-64/dummy_e-0.1.0-hb0f4dca_0.conda b/tests/data/channels/channels/dummy_channel_1/linux-64/dummy_e-0.1.0-hb0f4dca_0.conda index 0a6fd381d..9be8f35ec 100644 Binary files a/tests/data/channels/channels/dummy_channel_1/linux-64/dummy_e-0.1.0-hb0f4dca_0.conda and b/tests/data/channels/channels/dummy_channel_1/linux-64/dummy_e-0.1.0-hb0f4dca_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_1/linux-64/repodata.json b/tests/data/channels/channels/dummy_channel_1/linux-64/repodata.json index d1387d15b..cf54aabe4 100644 --- a/tests/data/channels/channels/dummy_channel_1/linux-64/repodata.json +++ b/tests/data/channels/channels/dummy_channel_1/linux-64/repodata.json @@ -11,13 +11,13 @@ "depends": [ "dummy-c" ], - "md5": "65cc8c3f42bcfdda42074f23a270347a", + "md5": "9e3022e5b3092c45015196ed3a679bca", "name": "dummy-a", "platform": "linux", - "sha256": "f25f1f03f35573f4b8e2dcd4bc183c7ad90f420196ae83db018f02a53b5b28c0", - "size": 1240, + "sha256": "602570943a8b4856bd4a6ad04c3ddea01d0a165cf5c0299fce45f7641ac2ac8e", + "size": 1242, "subdir": "linux-64", - "timestamp": 1730467011994, + "timestamp": 1730898132208, "version": "0.1.0" }, "dummy-b-0.1.0-hb0f4dca_0.conda": { @@ -25,13 +25,13 @@ "build": "hb0f4dca_0", "build_number": 0, "depends": [], - "md5": "f9aa31cad64411828fdd6ad808eb82f3", + "md5": "ba79f8081f0a1e87356c82a169c29b5b", "name": "dummy-b", "platform": "linux", - "sha256": "951ceae07a739273f7c29ca02eb9c4d824a8ce64241f08da917f1dbcda12a9a1", + "sha256": "daf38b547f42b8b581da0c50a54bee4d4a8bcbfc8c8790c8315e844e0f961f23", "size": 1156, "subdir": "linux-64", - "timestamp": 1730467011994, + "timestamp": 1730898132208, "version": "0.1.0" }, "dummy-c-0.1.0-hb0f4dca_0.conda": { @@ -39,13 +39,13 @@ "build": "hb0f4dca_0", "build_number": 0, "depends": [], - "md5": "c8473921ef500b89067d19225944d809", + "md5": "0ea2b9fed053c6618c82a8f54ecb9391", "name": "dummy-c", "platform": "linux", - "sha256": "4f12bf588a68ca03f8fa95214c4f44bdff1126355e308416d7327011f9a6ccfb", - "size": 1156, + "sha256": "b9ccb4610c0e77694197a4c9a83c0e034c586d733367fef7167032826069b3b7", + "size": 1157, "subdir": "linux-64", - "timestamp": 1730467011994, + "timestamp": 1730898132208, "version": "0.1.0" }, "dummy-d-0.1.0-hb0f4dca_0.conda": { @@ -55,13 +55,13 @@ "depends": [ "dummy-x" ], - "md5": "5cfa4ad4404616acfe3ee044573de899", + "md5": "4e12f116517dad87732e54df1ec216a1", "name": "dummy-d", "platform": "linux", - "sha256": "aa06324e297af42a15601b4d566673dc1d174f5af097a073f0b1c0d3df45040c", - "size": 1169, + "sha256": "1b2193383211a8b9eac6418a6d1ca84d92c88fe6c25d9227e2b3b573e0e87c6f", + "size": 1168, "subdir": "linux-64", - "timestamp": 1730467011994, + "timestamp": 1730898132208, "version": "0.1.0" }, "dummy_e-0.1.0-hb0f4dca_0.conda": { @@ -69,13 +69,13 @@ "build": "hb0f4dca_0", "build_number": 0, "depends": [], - "md5": "61e7728d43437b0e983c982651674119", + "md5": "44a05cf560c85eff8cd31d597eac9e04", "name": "dummy_e", "platform": "linux", - "sha256": "df6b751e2e00ebf15e706411ee0b54211c419238fb5fb71f7c590d478d54dd6b", + "sha256": "2dedb0328e6ccb55ebfc6e52ca09823713febf54c09aaf7117fb66ad1160ca0c", "size": 1157, "subdir": "linux-64", - "timestamp": 1730467011994, + "timestamp": 1730898132208, "version": "0.1.0" } }, diff --git a/tests/data/channels/channels/dummy_channel_1/osx-64/dummy-a-0.1.0-h0dc7051_0.conda b/tests/data/channels/channels/dummy_channel_1/osx-64/dummy-a-0.1.0-h0dc7051_0.conda index dfa747096..aff48a099 100644 Binary files a/tests/data/channels/channels/dummy_channel_1/osx-64/dummy-a-0.1.0-h0dc7051_0.conda and b/tests/data/channels/channels/dummy_channel_1/osx-64/dummy-a-0.1.0-h0dc7051_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_1/osx-64/dummy-b-0.1.0-h0dc7051_0.conda b/tests/data/channels/channels/dummy_channel_1/osx-64/dummy-b-0.1.0-h0dc7051_0.conda index 5c4f57c93..40a182f9c 100644 Binary files a/tests/data/channels/channels/dummy_channel_1/osx-64/dummy-b-0.1.0-h0dc7051_0.conda and b/tests/data/channels/channels/dummy_channel_1/osx-64/dummy-b-0.1.0-h0dc7051_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_1/osx-64/dummy-c-0.1.0-h0dc7051_0.conda b/tests/data/channels/channels/dummy_channel_1/osx-64/dummy-c-0.1.0-h0dc7051_0.conda index 00e40f1d0..3b44a6a4a 100644 Binary files a/tests/data/channels/channels/dummy_channel_1/osx-64/dummy-c-0.1.0-h0dc7051_0.conda and b/tests/data/channels/channels/dummy_channel_1/osx-64/dummy-c-0.1.0-h0dc7051_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_1/osx-64/dummy-d-0.1.0-h0dc7051_0.conda b/tests/data/channels/channels/dummy_channel_1/osx-64/dummy-d-0.1.0-h0dc7051_0.conda index ec89df6a9..e3f3057d0 100644 Binary files a/tests/data/channels/channels/dummy_channel_1/osx-64/dummy-d-0.1.0-h0dc7051_0.conda and b/tests/data/channels/channels/dummy_channel_1/osx-64/dummy-d-0.1.0-h0dc7051_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_1/osx-64/dummy_e-0.1.0-h0dc7051_0.conda b/tests/data/channels/channels/dummy_channel_1/osx-64/dummy_e-0.1.0-h0dc7051_0.conda index 51de57c55..11b5e1c2c 100644 Binary files a/tests/data/channels/channels/dummy_channel_1/osx-64/dummy_e-0.1.0-h0dc7051_0.conda and b/tests/data/channels/channels/dummy_channel_1/osx-64/dummy_e-0.1.0-h0dc7051_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_1/osx-64/repodata.json b/tests/data/channels/channels/dummy_channel_1/osx-64/repodata.json index c9d5f7cd1..df4d6553a 100644 --- a/tests/data/channels/channels/dummy_channel_1/osx-64/repodata.json +++ b/tests/data/channels/channels/dummy_channel_1/osx-64/repodata.json @@ -11,13 +11,13 @@ "depends": [ "dummy-c" ], - "md5": "5b9257c15e5a3a013b6cd5dc45d221b2", + "md5": "f0b93ffa2b933543088e14481cff1c6c", "name": "dummy-a", "platform": "osx", - "sha256": "d8feb8f43f1a07d13b1a91c4c6ac41b3c390aed2ab6c7cda63ccc6022fbe4a08", - "size": 1238, + "sha256": "ed8a1ae490eff14a210b1949a2e99cdc1a0bf8309f641f4ee5008520817fea06", + "size": 1241, "subdir": "osx-64", - "timestamp": 1730467012139, + "timestamp": 1730898132397, "version": "0.1.0" }, "dummy-b-0.1.0-h0dc7051_0.conda": { @@ -25,13 +25,13 @@ "build": "h0dc7051_0", "build_number": 0, "depends": [], - "md5": "18c3b223321a0a88f0c1cc5a06020d1e", + "md5": "9796ad981f289e954eb810d5a4ecb660", "name": "dummy-b", "platform": "osx", - "sha256": "0b74bf5b7d9b7565412c8b5fafb0c3703972d651d29032204d14f686ece9b861", - "size": 1149, + "sha256": "b072ad6c52e6edf7297c06ef68b278ee31f5e36d1caf058180b75588e6c7bc30", + "size": 1150, "subdir": "osx-64", - "timestamp": 1730467012139, + "timestamp": 1730898132397, "version": "0.1.0" }, "dummy-c-0.1.0-h0dc7051_0.conda": { @@ -39,13 +39,13 @@ "build": "h0dc7051_0", "build_number": 0, "depends": [], - "md5": "252be9451d2b661f87d2f3def37733bb", + "md5": "f20b0c0f48a7ba5a9d3bfbba98c3c67f", "name": "dummy-c", "platform": "osx", - "sha256": "44b179ff519c040b0aaf2943203b4b3b3bf3ab24aedf48755e3f7988421b5546", - "size": 1150, + "sha256": "59b2e33644e78ab7b517369a8e80b8d167cf3894020fd3b7195318fcf6083548", + "size": 1151, "subdir": "osx-64", - "timestamp": 1730467012139, + "timestamp": 1730898132397, "version": "0.1.0" }, "dummy-d-0.1.0-h0dc7051_0.conda": { @@ -55,13 +55,13 @@ "depends": [ "dummy-x" ], - "md5": "51443dded9c961eb6df5f577780d5f06", + "md5": "102c1e0b4d9f43776717b5664ad86ea5", "name": "dummy-d", "platform": "osx", - "sha256": "01b281aa493bdf67da80cab884d9917d6f9ca99fec514ac4eb9dd7a8afd250a6", - "size": 1164, + "sha256": "40dc0982a8325bca00bd607837eebb4b5e89818c24c04922a12c8c1996e4977d", + "size": 1167, "subdir": "osx-64", - "timestamp": 1730467012139, + "timestamp": 1730898132396, "version": "0.1.0" }, "dummy_e-0.1.0-h0dc7051_0.conda": { @@ -69,13 +69,13 @@ "build": "h0dc7051_0", "build_number": 0, "depends": [], - "md5": "a9a42213f406e7977ae2af74b4ef66e6", + "md5": "5cb25825488e2f51cb61eaea00784647", "name": "dummy_e", "platform": "osx", - "sha256": "bf38339b1ff8516f621791a17a3f4b5c39f37d8b8e3b9baf9d876566b887d16b", - "size": 1149, + "sha256": "aa67d4080afe626e2149a87fd12a192147327f76493302906a6f0d3c1dad4a2d", + "size": 1150, "subdir": "osx-64", - "timestamp": 1730467012139, + "timestamp": 1730898132397, "version": "0.1.0" } }, diff --git a/tests/data/channels/channels/dummy_channel_1/osx-arm64/dummy-a-0.1.0-h60d57d3_0.conda b/tests/data/channels/channels/dummy_channel_1/osx-arm64/dummy-a-0.1.0-h60d57d3_0.conda index 4e4bda345..871ed462a 100644 Binary files a/tests/data/channels/channels/dummy_channel_1/osx-arm64/dummy-a-0.1.0-h60d57d3_0.conda and b/tests/data/channels/channels/dummy_channel_1/osx-arm64/dummy-a-0.1.0-h60d57d3_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_1/osx-arm64/dummy-b-0.1.0-h60d57d3_0.conda b/tests/data/channels/channels/dummy_channel_1/osx-arm64/dummy-b-0.1.0-h60d57d3_0.conda index 5543b8f3c..d4ca876ed 100644 Binary files a/tests/data/channels/channels/dummy_channel_1/osx-arm64/dummy-b-0.1.0-h60d57d3_0.conda and b/tests/data/channels/channels/dummy_channel_1/osx-arm64/dummy-b-0.1.0-h60d57d3_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_1/osx-arm64/dummy-c-0.1.0-h60d57d3_0.conda b/tests/data/channels/channels/dummy_channel_1/osx-arm64/dummy-c-0.1.0-h60d57d3_0.conda index 635e8e4cd..0cf0627ab 100644 Binary files a/tests/data/channels/channels/dummy_channel_1/osx-arm64/dummy-c-0.1.0-h60d57d3_0.conda and b/tests/data/channels/channels/dummy_channel_1/osx-arm64/dummy-c-0.1.0-h60d57d3_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_1/osx-arm64/dummy-d-0.1.0-h60d57d3_0.conda b/tests/data/channels/channels/dummy_channel_1/osx-arm64/dummy-d-0.1.0-h60d57d3_0.conda index f431623f9..5dcf0f6e6 100644 Binary files a/tests/data/channels/channels/dummy_channel_1/osx-arm64/dummy-d-0.1.0-h60d57d3_0.conda and b/tests/data/channels/channels/dummy_channel_1/osx-arm64/dummy-d-0.1.0-h60d57d3_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_1/osx-arm64/dummy_e-0.1.0-h60d57d3_0.conda b/tests/data/channels/channels/dummy_channel_1/osx-arm64/dummy_e-0.1.0-h60d57d3_0.conda index 525eca2e2..b02eede3d 100644 Binary files a/tests/data/channels/channels/dummy_channel_1/osx-arm64/dummy_e-0.1.0-h60d57d3_0.conda and b/tests/data/channels/channels/dummy_channel_1/osx-arm64/dummy_e-0.1.0-h60d57d3_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_1/osx-arm64/repodata.json b/tests/data/channels/channels/dummy_channel_1/osx-arm64/repodata.json index 45ff4eb3d..0b8032a23 100644 --- a/tests/data/channels/channels/dummy_channel_1/osx-arm64/repodata.json +++ b/tests/data/channels/channels/dummy_channel_1/osx-arm64/repodata.json @@ -11,13 +11,13 @@ "depends": [ "dummy-c" ], - "md5": "4a163d255318d7a7b203be510ffca6f5", + "md5": "838eb7e1214fae014d0a987859f859af", "name": "dummy-a", "platform": "osx", - "sha256": "84b5cfc7ec6555f9d5d95dd37e5509f87f37598b6956369e3cadd39e3b15ae4f", - "size": 1237, + "sha256": "d76bd1ace7187f11ae12bb6bffe3006e43a36e7d29ceda51ce0116477a259bb2", + "size": 1240, "subdir": "osx-arm64", - "timestamp": 1730467012062, + "timestamp": 1730898132307, "version": "0.1.0" }, "dummy-b-0.1.0-h60d57d3_0.conda": { @@ -25,13 +25,13 @@ "build": "h60d57d3_0", "build_number": 0, "depends": [], - "md5": "a5adc2e8fd286ffc3ae368a7064288c9", + "md5": "ed8f85c5c7b99235b77f729c77da5e20", "name": "dummy-b", "platform": "osx", - "sha256": "a8bde66a25b032e3ffee2695447f3635ed92bf66045139e70a630fbaaa3ca231", - "size": 1154, + "sha256": "fc19ef430dce6fe01f1172e7a99f2d6d24279149ab1df370120da93bbb958f69", + "size": 1155, "subdir": "osx-arm64", - "timestamp": 1730467012062, + "timestamp": 1730898132307, "version": "0.1.0" }, "dummy-c-0.1.0-h60d57d3_0.conda": { @@ -39,13 +39,13 @@ "build": "h60d57d3_0", "build_number": 0, "depends": [], - "md5": "a8b352e2079a43f27fda8aa6a55871fd", + "md5": "51422bf40c92483b988ae0329c5cabe7", "name": "dummy-c", "platform": "osx", - "sha256": "0d6acd24838857732aa68d9d56b1bec2405e9bdd050dffcb3af2ea030df8b9e6", - "size": 1155, + "sha256": "63847478ea06ea5c025e29a803dc43f48e05b701258cc060e8a570b4ab634018", + "size": 1154, "subdir": "osx-arm64", - "timestamp": 1730467012062, + "timestamp": 1730898132307, "version": "0.1.0" }, "dummy-d-0.1.0-h60d57d3_0.conda": { @@ -55,13 +55,13 @@ "depends": [ "dummy-x" ], - "md5": "06c91bcc80491a1cc3d86538b2d8cb46", + "md5": "9b5038e47b58cb8b848f4c653e032bb5", "name": "dummy-d", "platform": "osx", - "sha256": "1a1178c9ccb7edaadbe4b2758c57e9a57dd6b013eae857c4da75d33c0f1db004", - "size": 1167, + "sha256": "2b16ec795114f502afa2395b123c63ed0ecbd56b021d1f0b396d072c87f627a2", + "size": 1168, "subdir": "osx-arm64", - "timestamp": 1730467012062, + "timestamp": 1730898132307, "version": "0.1.0" }, "dummy_e-0.1.0-h60d57d3_0.conda": { @@ -69,13 +69,13 @@ "build": "h60d57d3_0", "build_number": 0, "depends": [], - "md5": "66e03241d31cae2ed0dba772fa8426ef", + "md5": "307c471891a2cef3b53523682b65711c", "name": "dummy_e", "platform": "osx", - "sha256": "cc0532ef71bf3223796747972eaffaddbd44cfb60f9fe848069517cf9f0a6766", - "size": 1154, + "sha256": "44886db4bf8db2b3223c4aa7bf8eb7b07748d86b13da4ebd644fc7cd1a444f61", + "size": 1155, "subdir": "osx-arm64", - "timestamp": 1730467012062, + "timestamp": 1730898132307, "version": "0.1.0" } }, diff --git a/tests/data/channels/channels/dummy_channel_1/win-64/dummy-a-0.1.0-h9490d1a_0.conda b/tests/data/channels/channels/dummy_channel_1/win-64/dummy-a-0.1.0-h9490d1a_0.conda index caf1c6241..dbf4b4cb0 100644 Binary files a/tests/data/channels/channels/dummy_channel_1/win-64/dummy-a-0.1.0-h9490d1a_0.conda and b/tests/data/channels/channels/dummy_channel_1/win-64/dummy-a-0.1.0-h9490d1a_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_1/win-64/dummy-b-0.1.0-h9490d1a_0.conda b/tests/data/channels/channels/dummy_channel_1/win-64/dummy-b-0.1.0-h9490d1a_0.conda index d95881b42..82eedb084 100644 Binary files a/tests/data/channels/channels/dummy_channel_1/win-64/dummy-b-0.1.0-h9490d1a_0.conda and b/tests/data/channels/channels/dummy_channel_1/win-64/dummy-b-0.1.0-h9490d1a_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_1/win-64/dummy-c-0.1.0-h9490d1a_0.conda b/tests/data/channels/channels/dummy_channel_1/win-64/dummy-c-0.1.0-h9490d1a_0.conda index 8e075a1ad..890e6cbb3 100644 Binary files a/tests/data/channels/channels/dummy_channel_1/win-64/dummy-c-0.1.0-h9490d1a_0.conda and b/tests/data/channels/channels/dummy_channel_1/win-64/dummy-c-0.1.0-h9490d1a_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_1/win-64/dummy-d-0.1.0-h9490d1a_0.conda b/tests/data/channels/channels/dummy_channel_1/win-64/dummy-d-0.1.0-h9490d1a_0.conda index 822406830..2a2f72eca 100644 Binary files a/tests/data/channels/channels/dummy_channel_1/win-64/dummy-d-0.1.0-h9490d1a_0.conda and b/tests/data/channels/channels/dummy_channel_1/win-64/dummy-d-0.1.0-h9490d1a_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_1/win-64/dummy_e-0.1.0-h9490d1a_0.conda b/tests/data/channels/channels/dummy_channel_1/win-64/dummy_e-0.1.0-h9490d1a_0.conda index 2062d494f..cbdb346a5 100644 Binary files a/tests/data/channels/channels/dummy_channel_1/win-64/dummy_e-0.1.0-h9490d1a_0.conda and b/tests/data/channels/channels/dummy_channel_1/win-64/dummy_e-0.1.0-h9490d1a_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_1/win-64/repodata.json b/tests/data/channels/channels/dummy_channel_1/win-64/repodata.json index bb943df35..389b9f52f 100644 --- a/tests/data/channels/channels/dummy_channel_1/win-64/repodata.json +++ b/tests/data/channels/channels/dummy_channel_1/win-64/repodata.json @@ -11,13 +11,13 @@ "depends": [ "dummy-c" ], - "md5": "5f70afc5282d5fdd7b07d2ea540b8e27", + "md5": "3f444debe78692289e67811c2316fc6a", "name": "dummy-a", "platform": "win", - "sha256": "4b6f09666c1cf3b7849b9cec6e566e27267892b24d975988c4d9826564310925", - "size": 1247, + "sha256": "8222d7e5f9b215559e8ac911b064c4d87dceaab4d9054308eaa33276488e6e27", + "size": 1246, "subdir": "win-64", - "timestamp": 1730467011936, + "timestamp": 1730898132124, "version": "0.1.0" }, "dummy-b-0.1.0-h9490d1a_0.conda": { @@ -25,13 +25,13 @@ "build": "h9490d1a_0", "build_number": 0, "depends": [], - "md5": "81c6a8446a97fdb178d0f129a66ff684", + "md5": "3b5dd75611f655ad1aabe21a846ea3ac", "name": "dummy-b", "platform": "win", - "sha256": "3db748ecaf693eb94ef8e675e53613b8589ace6266678e633b9551bb919127b0", - "size": 1158, + "sha256": "08c1afaee9577ab6aeabf4c9918719b77ad890d63082e55f7edddd411c06b009", + "size": 1157, "subdir": "win-64", - "timestamp": 1730467011936, + "timestamp": 1730898132124, "version": "0.1.0" }, "dummy-c-0.1.0-h9490d1a_0.conda": { @@ -39,13 +39,13 @@ "build": "h9490d1a_0", "build_number": 0, "depends": [], - "md5": "4ac99efb2c8d8a5dda34f61de93830e0", + "md5": "088b5e2e04b51f3650653d524fa4c8a5", "name": "dummy-c", "platform": "win", - "sha256": "45572c560c144283148dd77ba365fcdbbafa8f4b872408fef982743f2253b50f", - "size": 1158, + "sha256": "00e1b007fab059653a7bb953de1b8745ed1cb1811953d742290504be6e888774", + "size": 1157, "subdir": "win-64", - "timestamp": 1730467011936, + "timestamp": 1730898132124, "version": "0.1.0" }, "dummy-d-0.1.0-h9490d1a_0.conda": { @@ -55,13 +55,13 @@ "depends": [ "dummy-x" ], - "md5": "717f22ab565874d38898638c5c90bf29", + "md5": "1663e50d6bb75b95808c877bd79628ef", "name": "dummy-d", "platform": "win", - "sha256": "342a1fc0ba864b46613185c85b02632ab661a4d46fc1bd16706a48fa48c7919d", - "size": 1175, + "sha256": "00d4b0579b61eff982db49f3227f68dd6b25fc1bdaf7e4736b3c876e4c06550c", + "size": 1173, "subdir": "win-64", - "timestamp": 1730467011936, + "timestamp": 1730898132124, "version": "0.1.0" }, "dummy_e-0.1.0-h9490d1a_0.conda": { @@ -69,13 +69,13 @@ "build": "h9490d1a_0", "build_number": 0, "depends": [], - "md5": "b1a59c475554a56c2110d626137bec50", + "md5": "4814b700ab73c8ca0cda30de842d4770", "name": "dummy_e", "platform": "win", - "sha256": "268867267c9154311ce74978f4eb56752834973d8c7a15bc2847df25e5d93638", - "size": 1156, + "sha256": "2f3a1cc01daecfc007d80858805e1a79b89adcc21c91e9a4b8ae125a1e658891", + "size": 1158, "subdir": "win-64", - "timestamp": 1730467011936, + "timestamp": 1730898132124, "version": "0.1.0" } }, diff --git a/tests/data/channels/channels/dummy_channel_2/linux-64/dummy-b-0.1.0-hb0f4dca_0.conda b/tests/data/channels/channels/dummy_channel_2/linux-64/dummy-b-0.1.0-hb0f4dca_0.conda index c3a365ce1..b8fcd674f 100644 Binary files a/tests/data/channels/channels/dummy_channel_2/linux-64/dummy-b-0.1.0-hb0f4dca_0.conda and b/tests/data/channels/channels/dummy_channel_2/linux-64/dummy-b-0.1.0-hb0f4dca_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_2/linux-64/dummy-x-0.1.0-hb0f4dca_0.conda b/tests/data/channels/channels/dummy_channel_2/linux-64/dummy-x-0.1.0-hb0f4dca_0.conda index dde4ce588..22a36e7c6 100644 Binary files a/tests/data/channels/channels/dummy_channel_2/linux-64/dummy-x-0.1.0-hb0f4dca_0.conda and b/tests/data/channels/channels/dummy_channel_2/linux-64/dummy-x-0.1.0-hb0f4dca_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_2/linux-64/repodata.json b/tests/data/channels/channels/dummy_channel_2/linux-64/repodata.json index d34ba0b62..f34b7c9d8 100644 --- a/tests/data/channels/channels/dummy_channel_2/linux-64/repodata.json +++ b/tests/data/channels/channels/dummy_channel_2/linux-64/repodata.json @@ -9,13 +9,13 @@ "build": "hb0f4dca_0", "build_number": 0, "depends": [], - "md5": "07f568c93f5af76b77af54662394cce5", + "md5": "5999cc251f2427c4020179f874a44ec6", "name": "dummy-b", "platform": "linux", - "sha256": "d4580f904b74c3bc71f491e0dc53e27881c1b363d1ba3d5dd295002ee6759eff", - "size": 1156, + "sha256": "5973cc8ff460a97fcbbd31cecd02d41f4dfbf191e170176351a65f8549219b9e", + "size": 1157, "subdir": "linux-64", - "timestamp": 1730467012242, + "timestamp": 1730898132532, "version": "0.1.0" }, "dummy-x-0.1.0-hb0f4dca_0.conda": { @@ -23,13 +23,13 @@ "build": "hb0f4dca_0", "build_number": 0, "depends": [], - "md5": "68bb05af77a766b17bf748ad7c769944", + "md5": "83e6f2e970e6a485bbdde0ba355ad9cc", "name": "dummy-x", "platform": "linux", - "sha256": "ded45f9bf1a1c9d2a2b81b0d1226909544f3937205a4f26ca33c6cff230afb20", - "size": 1154, + "sha256": "6a2ba209a1736f9bbc91e4770fd1dd9a6f448dc1be3504f2b934c33c284bc0fe", + "size": 1156, "subdir": "linux-64", - "timestamp": 1730467012242, + "timestamp": 1730898132532, "version": "0.1.0" } }, diff --git a/tests/data/channels/channels/dummy_channel_2/osx-64/dummy-b-0.1.0-h0dc7051_0.conda b/tests/data/channels/channels/dummy_channel_2/osx-64/dummy-b-0.1.0-h0dc7051_0.conda index a1a514aaa..441e2b579 100644 Binary files a/tests/data/channels/channels/dummy_channel_2/osx-64/dummy-b-0.1.0-h0dc7051_0.conda and b/tests/data/channels/channels/dummy_channel_2/osx-64/dummy-b-0.1.0-h0dc7051_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_2/osx-64/dummy-x-0.1.0-h0dc7051_0.conda b/tests/data/channels/channels/dummy_channel_2/osx-64/dummy-x-0.1.0-h0dc7051_0.conda index 1c886acca..bfcff63eb 100644 Binary files a/tests/data/channels/channels/dummy_channel_2/osx-64/dummy-x-0.1.0-h0dc7051_0.conda and b/tests/data/channels/channels/dummy_channel_2/osx-64/dummy-x-0.1.0-h0dc7051_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_2/osx-64/repodata.json b/tests/data/channels/channels/dummy_channel_2/osx-64/repodata.json index 4aa2cea97..3a21f0664 100644 --- a/tests/data/channels/channels/dummy_channel_2/osx-64/repodata.json +++ b/tests/data/channels/channels/dummy_channel_2/osx-64/repodata.json @@ -9,13 +9,13 @@ "build": "h0dc7051_0", "build_number": 0, "depends": [], - "md5": "bb859c514fb53020cf7fe5c6b032b889", + "md5": "017d34f4de6894ab134b67aeaf314b11", "name": "dummy-b", "platform": "osx", - "sha256": "31799a5e7ca77192ba3d31925c44345c94c8c6ce18e96e72e3b7a231d40632c4", - "size": 1148, + "sha256": "c7a6c8596c39218a0b11fd40ef8ae1b5ab63995b94ec3644dcca6fb8ffefe763", + "size": 1150, "subdir": "osx-64", - "timestamp": 1730467012353, + "timestamp": 1730898132631, "version": "0.1.0" }, "dummy-x-0.1.0-h0dc7051_0.conda": { @@ -23,13 +23,13 @@ "build": "h0dc7051_0", "build_number": 0, "depends": [], - "md5": "635aee5171a783386bfb7096001b3bdb", + "md5": "54adbbf15ee081c9d6728556263b5f0b", "name": "dummy-x", "platform": "osx", - "sha256": "fc6de173d4d64bed648f5ab0009f19072c6638c3e88b93d0adfa99c052601d51", - "size": 1146, + "sha256": "75454ecb66f5921652e704ae4681862f37e379e8c8eb09fb4ad8cd78069464a2", + "size": 1149, "subdir": "osx-64", - "timestamp": 1730467012353, + "timestamp": 1730898132631, "version": "0.1.0" } }, diff --git a/tests/data/channels/channels/dummy_channel_2/osx-arm64/dummy-b-0.1.0-h60d57d3_0.conda b/tests/data/channels/channels/dummy_channel_2/osx-arm64/dummy-b-0.1.0-h60d57d3_0.conda index 647c25c65..d06291521 100644 Binary files a/tests/data/channels/channels/dummy_channel_2/osx-arm64/dummy-b-0.1.0-h60d57d3_0.conda and b/tests/data/channels/channels/dummy_channel_2/osx-arm64/dummy-b-0.1.0-h60d57d3_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_2/osx-arm64/dummy-x-0.1.0-h60d57d3_0.conda b/tests/data/channels/channels/dummy_channel_2/osx-arm64/dummy-x-0.1.0-h60d57d3_0.conda index 2881cab62..0376a2323 100644 Binary files a/tests/data/channels/channels/dummy_channel_2/osx-arm64/dummy-x-0.1.0-h60d57d3_0.conda and b/tests/data/channels/channels/dummy_channel_2/osx-arm64/dummy-x-0.1.0-h60d57d3_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_2/osx-arm64/repodata.json b/tests/data/channels/channels/dummy_channel_2/osx-arm64/repodata.json index 4d30cdd0c..c07fc9cf5 100644 --- a/tests/data/channels/channels/dummy_channel_2/osx-arm64/repodata.json +++ b/tests/data/channels/channels/dummy_channel_2/osx-arm64/repodata.json @@ -9,13 +9,13 @@ "build": "h60d57d3_0", "build_number": 0, "depends": [], - "md5": "a5290fca90787581555c004dff1bf802", + "md5": "0eae7022f8c4ee6b2f2e045e084706f7", "name": "dummy-b", "platform": "osx", - "sha256": "39e17ef0b4932d6ac1959f9fd9df794c8e82685f87adb023611a806903febad5", - "size": 1153, + "sha256": "4dbd1d9589cfb97dc778f766fbdeb6a516965ed1b24692fd10b25b3596378af4", + "size": 1155, "subdir": "osx-arm64", - "timestamp": 1730467012282, + "timestamp": 1730898132583, "version": "0.1.0" }, "dummy-x-0.1.0-h60d57d3_0.conda": { @@ -23,13 +23,13 @@ "build": "h60d57d3_0", "build_number": 0, "depends": [], - "md5": "f2b20eade2f7766e342860eadac264f6", + "md5": "c629b99c51eb9577dc9716412f32eaf4", "name": "dummy-x", "platform": "osx", - "sha256": "7f0672d625892c22580a1bd2c8e55b39ac91f2393173f78edd9f593187110c99", - "size": 1152, + "sha256": "e23fc4f1e611d0924d31d53bb48e1e16bba07936a821ce8e67fb3bb7cc7a8f0a", + "size": 1154, "subdir": "osx-arm64", - "timestamp": 1730467012282, + "timestamp": 1730898132583, "version": "0.1.0" } }, diff --git a/tests/data/channels/channels/dummy_channel_2/win-64/dummy-b-0.1.0-h9490d1a_0.conda b/tests/data/channels/channels/dummy_channel_2/win-64/dummy-b-0.1.0-h9490d1a_0.conda index a62e800cd..72d2c7a2a 100644 Binary files a/tests/data/channels/channels/dummy_channel_2/win-64/dummy-b-0.1.0-h9490d1a_0.conda and b/tests/data/channels/channels/dummy_channel_2/win-64/dummy-b-0.1.0-h9490d1a_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_2/win-64/dummy-x-0.1.0-h9490d1a_0.conda b/tests/data/channels/channels/dummy_channel_2/win-64/dummy-x-0.1.0-h9490d1a_0.conda index 4fcadb756..f8e35a523 100644 Binary files a/tests/data/channels/channels/dummy_channel_2/win-64/dummy-x-0.1.0-h9490d1a_0.conda and b/tests/data/channels/channels/dummy_channel_2/win-64/dummy-x-0.1.0-h9490d1a_0.conda differ diff --git a/tests/data/channels/channels/dummy_channel_2/win-64/repodata.json b/tests/data/channels/channels/dummy_channel_2/win-64/repodata.json index fed5f0fd0..18e973373 100644 --- a/tests/data/channels/channels/dummy_channel_2/win-64/repodata.json +++ b/tests/data/channels/channels/dummy_channel_2/win-64/repodata.json @@ -9,13 +9,13 @@ "build": "h9490d1a_0", "build_number": 0, "depends": [], - "md5": "96c3e4946ceb63e79d6ffb317d1e8f12", + "md5": "d7bc95b275a95933afdcfc8b43485ea6", "name": "dummy-b", "platform": "win", - "sha256": "9eb385c0188344674c70daa0a689c7e8380663aa571c1a6da4548ff7445657d3", - "size": 1158, + "sha256": "8665d5f04c8149ab3c8b0860a183c2193de796fdfbf536be6b168fa9e36f3604", + "size": 1157, "subdir": "win-64", - "timestamp": 1730467012205, + "timestamp": 1730898132483, "version": "0.1.0" }, "dummy-x-0.1.0-h9490d1a_0.conda": { @@ -23,13 +23,13 @@ "build": "h9490d1a_0", "build_number": 0, "depends": [], - "md5": "141705c0c7ccb5fc939626b28506ce3a", + "md5": "5b55c726ced58cc0b00c24b5e0dceb22", "name": "dummy-x", "platform": "win", - "sha256": "288fecab26b405e29d065701be7a31cf25a87e85795f2769d75639df415eafde", - "size": 1158, + "sha256": "a59d0e871826438b2d8abbdec0ec4429b40442b58e966c3dd21656ded2d87f4d", + "size": 1159, "subdir": "win-64", - "timestamp": 1730467012205, + "timestamp": 1730898132483, "version": "0.1.0" } }, diff --git a/tests/data/channels/channels/global_update_channel_1/linux-64/package-0.1.0-hb0f4dca_0.conda b/tests/data/channels/channels/global_update_channel_1/linux-64/package-0.1.0-hb0f4dca_0.conda deleted file mode 100644 index a7cdcef1f..000000000 Binary files a/tests/data/channels/channels/global_update_channel_1/linux-64/package-0.1.0-hb0f4dca_0.conda and /dev/null differ diff --git a/tests/data/channels/channels/global_update_channel_1/linux-64/package-0.2.0-hb0f4dca_0.conda b/tests/data/channels/channels/global_update_channel_1/linux-64/package-0.2.0-hb0f4dca_0.conda deleted file mode 100644 index 31d60de5d..000000000 Binary files a/tests/data/channels/channels/global_update_channel_1/linux-64/package-0.2.0-hb0f4dca_0.conda and /dev/null differ diff --git a/tests/data/channels/channels/global_update_channel_1/linux-64/package2-0.1.0-hb0f4dca_0.conda b/tests/data/channels/channels/global_update_channel_1/linux-64/package2-0.1.0-hb0f4dca_0.conda deleted file mode 100644 index e3d1d7234..000000000 Binary files a/tests/data/channels/channels/global_update_channel_1/linux-64/package2-0.1.0-hb0f4dca_0.conda and /dev/null differ diff --git a/tests/data/channels/channels/global_update_channel_1/linux-64/package2-0.2.0-hb0f4dca_0.conda b/tests/data/channels/channels/global_update_channel_1/linux-64/package2-0.2.0-hb0f4dca_0.conda deleted file mode 100644 index 46ba4ac38..000000000 Binary files a/tests/data/channels/channels/global_update_channel_1/linux-64/package2-0.2.0-hb0f4dca_0.conda and /dev/null differ diff --git a/tests/data/channels/channels/global_update_channel_1/osx-64/package-0.1.0-h0dc7051_0.conda b/tests/data/channels/channels/global_update_channel_1/osx-64/package-0.1.0-h0dc7051_0.conda deleted file mode 100644 index 429756647..000000000 Binary files a/tests/data/channels/channels/global_update_channel_1/osx-64/package-0.1.0-h0dc7051_0.conda and /dev/null differ diff --git a/tests/data/channels/channels/global_update_channel_1/osx-64/package-0.2.0-h0dc7051_0.conda b/tests/data/channels/channels/global_update_channel_1/osx-64/package-0.2.0-h0dc7051_0.conda deleted file mode 100644 index 3c7360bf9..000000000 Binary files a/tests/data/channels/channels/global_update_channel_1/osx-64/package-0.2.0-h0dc7051_0.conda and /dev/null differ diff --git a/tests/data/channels/channels/global_update_channel_1/osx-64/package2-0.1.0-h0dc7051_0.conda b/tests/data/channels/channels/global_update_channel_1/osx-64/package2-0.1.0-h0dc7051_0.conda deleted file mode 100644 index 0881dce9c..000000000 Binary files a/tests/data/channels/channels/global_update_channel_1/osx-64/package2-0.1.0-h0dc7051_0.conda and /dev/null differ diff --git a/tests/data/channels/channels/global_update_channel_1/osx-64/package2-0.2.0-h0dc7051_0.conda b/tests/data/channels/channels/global_update_channel_1/osx-64/package2-0.2.0-h0dc7051_0.conda deleted file mode 100644 index 17f252c1c..000000000 Binary files a/tests/data/channels/channels/global_update_channel_1/osx-64/package2-0.2.0-h0dc7051_0.conda and /dev/null differ diff --git a/tests/data/channels/channels/global_update_channel_1/osx-arm64/package-0.1.0-h60d57d3_0.conda b/tests/data/channels/channels/global_update_channel_1/osx-arm64/package-0.1.0-h60d57d3_0.conda deleted file mode 100644 index f9f97c1ba..000000000 Binary files a/tests/data/channels/channels/global_update_channel_1/osx-arm64/package-0.1.0-h60d57d3_0.conda and /dev/null differ diff --git a/tests/data/channels/channels/global_update_channel_1/osx-arm64/package-0.2.0-h60d57d3_0.conda b/tests/data/channels/channels/global_update_channel_1/osx-arm64/package-0.2.0-h60d57d3_0.conda deleted file mode 100644 index 735d45d9f..000000000 Binary files a/tests/data/channels/channels/global_update_channel_1/osx-arm64/package-0.2.0-h60d57d3_0.conda and /dev/null differ diff --git a/tests/data/channels/channels/global_update_channel_1/osx-arm64/package2-0.1.0-h60d57d3_0.conda b/tests/data/channels/channels/global_update_channel_1/osx-arm64/package2-0.1.0-h60d57d3_0.conda deleted file mode 100644 index a60e78cb5..000000000 Binary files a/tests/data/channels/channels/global_update_channel_1/osx-arm64/package2-0.1.0-h60d57d3_0.conda and /dev/null differ diff --git a/tests/data/channels/channels/global_update_channel_1/osx-arm64/package2-0.2.0-h60d57d3_0.conda b/tests/data/channels/channels/global_update_channel_1/osx-arm64/package2-0.2.0-h60d57d3_0.conda deleted file mode 100644 index 3c3775499..000000000 Binary files a/tests/data/channels/channels/global_update_channel_1/osx-arm64/package2-0.2.0-h60d57d3_0.conda and /dev/null differ diff --git a/tests/data/channels/channels/global_update_channel_1/win-64/package-0.1.0-h9490d1a_0.conda b/tests/data/channels/channels/global_update_channel_1/win-64/package-0.1.0-h9490d1a_0.conda deleted file mode 100644 index 4d5950f7c..000000000 Binary files a/tests/data/channels/channels/global_update_channel_1/win-64/package-0.1.0-h9490d1a_0.conda and /dev/null differ diff --git a/tests/data/channels/channels/global_update_channel_1/win-64/package-0.2.0-h9490d1a_0.conda b/tests/data/channels/channels/global_update_channel_1/win-64/package-0.2.0-h9490d1a_0.conda deleted file mode 100644 index f0598e4e6..000000000 Binary files a/tests/data/channels/channels/global_update_channel_1/win-64/package-0.2.0-h9490d1a_0.conda and /dev/null differ diff --git a/tests/data/channels/channels/global_update_channel_1/win-64/package2-0.1.0-h9490d1a_0.conda b/tests/data/channels/channels/global_update_channel_1/win-64/package2-0.1.0-h9490d1a_0.conda deleted file mode 100644 index daf4f4ed7..000000000 Binary files a/tests/data/channels/channels/global_update_channel_1/win-64/package2-0.1.0-h9490d1a_0.conda and /dev/null differ diff --git a/tests/data/channels/channels/global_update_channel_1/win-64/package2-0.2.0-h9490d1a_0.conda b/tests/data/channels/channels/global_update_channel_1/win-64/package2-0.2.0-h9490d1a_0.conda deleted file mode 100644 index ad16850f4..000000000 Binary files a/tests/data/channels/channels/global_update_channel_1/win-64/package2-0.2.0-h9490d1a_0.conda and /dev/null differ diff --git a/tests/data/channels/channels/multiple_versions_channel_1/linux-64/package-0.1.0-hb0f4dca_0.conda b/tests/data/channels/channels/multiple_versions_channel_1/linux-64/package-0.1.0-hb0f4dca_0.conda new file mode 100644 index 000000000..943a782c4 Binary files /dev/null and b/tests/data/channels/channels/multiple_versions_channel_1/linux-64/package-0.1.0-hb0f4dca_0.conda differ diff --git a/tests/data/channels/channels/multiple_versions_channel_1/linux-64/package-0.2.0-hb0f4dca_0.conda b/tests/data/channels/channels/multiple_versions_channel_1/linux-64/package-0.2.0-hb0f4dca_0.conda new file mode 100644 index 000000000..7ba5a21a6 Binary files /dev/null and b/tests/data/channels/channels/multiple_versions_channel_1/linux-64/package-0.2.0-hb0f4dca_0.conda differ diff --git a/tests/data/channels/channels/multiple_versions_channel_1/linux-64/package2-0.1.0-hb0f4dca_0.conda b/tests/data/channels/channels/multiple_versions_channel_1/linux-64/package2-0.1.0-hb0f4dca_0.conda new file mode 100644 index 000000000..327296c5a Binary files /dev/null and b/tests/data/channels/channels/multiple_versions_channel_1/linux-64/package2-0.1.0-hb0f4dca_0.conda differ diff --git a/tests/data/channels/channels/multiple_versions_channel_1/linux-64/package2-0.2.0-hb0f4dca_0.conda b/tests/data/channels/channels/multiple_versions_channel_1/linux-64/package2-0.2.0-hb0f4dca_0.conda new file mode 100644 index 000000000..bfe47f296 Binary files /dev/null and b/tests/data/channels/channels/multiple_versions_channel_1/linux-64/package2-0.2.0-hb0f4dca_0.conda differ diff --git a/tests/data/channels/channels/global_update_channel_1/linux-64/repodata.json b/tests/data/channels/channels/multiple_versions_channel_1/linux-64/repodata.json similarity index 61% rename from tests/data/channels/channels/global_update_channel_1/linux-64/repodata.json rename to tests/data/channels/channels/multiple_versions_channel_1/linux-64/repodata.json index b446f1253..6caed42de 100644 --- a/tests/data/channels/channels/global_update_channel_1/linux-64/repodata.json +++ b/tests/data/channels/channels/multiple_versions_channel_1/linux-64/repodata.json @@ -9,13 +9,13 @@ "build": "hb0f4dca_0", "build_number": 0, "depends": [], - "md5": "f85d007f4ad492736c85a0c7687b0796", + "md5": "a2f28b306e1ad1196ca6dae59e96a18d", "name": "package", "platform": "linux", - "sha256": "b91164a9b24069647c7df728cb08345ed5d3ce5853e23ffc19c86737c90bc081", - "size": 1239, + "sha256": "cc382444ece609943e5d4ec18abf5aeabfc364ac59c1eed30c89524db92c8805", + "size": 1237, "subdir": "linux-64", - "timestamp": 1730467012489, + "timestamp": 1730898132739, "version": "0.1.0" }, "package-0.2.0-hb0f4dca_0.conda": { @@ -23,13 +23,13 @@ "build": "hb0f4dca_0", "build_number": 0, "depends": [], - "md5": "69ac89c4ff399cc2c1b1ba4590bbf77f", + "md5": "868a498e8f36354c28d91005752797d5", "name": "package", "platform": "linux", - "sha256": "2fa9f8a906d4e6043c8630d6d3f494a706c6e6af4f4d9346b56f08af817e535e", - "size": 1238, + "sha256": "74fc123c3902ba53e570ed9172a591d7025cec9995193500bd4aee672a565da3", + "size": 1239, "subdir": "linux-64", - "timestamp": 1730467012656, + "timestamp": 1730898132949, "version": "0.2.0" }, "package2-0.1.0-hb0f4dca_0.conda": { @@ -37,13 +37,13 @@ "build": "hb0f4dca_0", "build_number": 0, "depends": [], - "md5": "feaca039f9a9432dc5d52442ae61cc4f", + "md5": "e764e06f7d440c3358f10a4e15f8bda5", "name": "package2", "platform": "linux", - "sha256": "196b693f5200116411fdfffd390beea8dc0a0b25c0cbb6a897a27fe76470efbe", - "size": 1160, + "sha256": "f0826cb7a6f56741d320dbfd783d28c7ea63b860ac4abcaa73373c0e408fe9d3", + "size": 1161, "subdir": "linux-64", - "timestamp": 1730467012489, + "timestamp": 1730898132739, "version": "0.1.0" }, "package2-0.2.0-hb0f4dca_0.conda": { @@ -51,13 +51,13 @@ "build": "hb0f4dca_0", "build_number": 0, "depends": [], - "md5": "0137db9a93c1a8dee96dff9b80557f87", + "md5": "2341a61eac6973353c46a795a292b8f9", "name": "package2", "platform": "linux", - "sha256": "b81c14fdd919cf9cef84ad2c43b46d8b7c1dc3faebf55711d17621ffb0a9bfe8", - "size": 1159, + "sha256": "3269562fee84b6c1b5af97bfb31d0db6c128a7253f16e60895bd4117408ec58c", + "size": 1158, "subdir": "linux-64", - "timestamp": 1730467012656, + "timestamp": 1730898132949, "version": "0.2.0" } }, diff --git a/tests/data/channels/channels/global_update_channel_1/noarch/repodata.json b/tests/data/channels/channels/multiple_versions_channel_1/noarch/repodata.json similarity index 100% rename from tests/data/channels/channels/global_update_channel_1/noarch/repodata.json rename to tests/data/channels/channels/multiple_versions_channel_1/noarch/repodata.json diff --git a/tests/data/channels/channels/multiple_versions_channel_1/osx-64/package-0.1.0-h0dc7051_0.conda b/tests/data/channels/channels/multiple_versions_channel_1/osx-64/package-0.1.0-h0dc7051_0.conda new file mode 100644 index 000000000..e90cbc1db Binary files /dev/null and b/tests/data/channels/channels/multiple_versions_channel_1/osx-64/package-0.1.0-h0dc7051_0.conda differ diff --git a/tests/data/channels/channels/multiple_versions_channel_1/osx-64/package-0.2.0-h0dc7051_0.conda b/tests/data/channels/channels/multiple_versions_channel_1/osx-64/package-0.2.0-h0dc7051_0.conda new file mode 100644 index 000000000..89853bb29 Binary files /dev/null and b/tests/data/channels/channels/multiple_versions_channel_1/osx-64/package-0.2.0-h0dc7051_0.conda differ diff --git a/tests/data/channels/channels/multiple_versions_channel_1/osx-64/package2-0.1.0-h0dc7051_0.conda b/tests/data/channels/channels/multiple_versions_channel_1/osx-64/package2-0.1.0-h0dc7051_0.conda new file mode 100644 index 000000000..10630b324 Binary files /dev/null and b/tests/data/channels/channels/multiple_versions_channel_1/osx-64/package2-0.1.0-h0dc7051_0.conda differ diff --git a/tests/data/channels/channels/multiple_versions_channel_1/osx-64/package2-0.2.0-h0dc7051_0.conda b/tests/data/channels/channels/multiple_versions_channel_1/osx-64/package2-0.2.0-h0dc7051_0.conda new file mode 100644 index 000000000..2957953e7 Binary files /dev/null and b/tests/data/channels/channels/multiple_versions_channel_1/osx-64/package2-0.2.0-h0dc7051_0.conda differ diff --git a/tests/data/channels/channels/global_update_channel_1/osx-64/repodata.json b/tests/data/channels/channels/multiple_versions_channel_1/osx-64/repodata.json similarity index 61% rename from tests/data/channels/channels/global_update_channel_1/osx-64/repodata.json rename to tests/data/channels/channels/multiple_versions_channel_1/osx-64/repodata.json index 65774b73a..608b2a966 100644 --- a/tests/data/channels/channels/global_update_channel_1/osx-64/repodata.json +++ b/tests/data/channels/channels/multiple_versions_channel_1/osx-64/repodata.json @@ -9,13 +9,13 @@ "build": "h0dc7051_0", "build_number": 0, "depends": [], - "md5": "122323e58cae43fc93c6c3002aa56224", + "md5": "4e840b763722d23947a58cb583598112", "name": "package", "platform": "osx", - "sha256": "2bfcf62f99c82693169a224d26634fa2a3d64b0c5bd019079bfe169fe5f944d3", - "size": 1230, + "sha256": "6879c4592a030f28f6d971b2f22668179ce4f795205b44a29b0338ab612f7a51", + "size": 1231, "subdir": "osx-64", - "timestamp": 1730467012569, + "timestamp": 1730898132845, "version": "0.1.0" }, "package-0.2.0-h0dc7051_0.conda": { @@ -23,13 +23,13 @@ "build": "h0dc7051_0", "build_number": 0, "depends": [], - "md5": "dbc3beb6bc71cf8f81b8cd084163acb5", + "md5": "8cc0b6cec2e59931ddadca42240ff7f0", "name": "package", "platform": "osx", - "sha256": "6199637480349a2ded5e651958c8e1e42f32fe9ef54897fdff04cc8d4fb9e365", + "sha256": "6dab29d04cf60699d6e3b63247dbbb76212f707f79d4ec7660d42553684a02fe", "size": 1230, "subdir": "osx-64", - "timestamp": 1730467012742, + "timestamp": 1730898133054, "version": "0.2.0" }, "package2-0.1.0-h0dc7051_0.conda": { @@ -37,13 +37,13 @@ "build": "h0dc7051_0", "build_number": 0, "depends": [], - "md5": "e2ac49eeccb6af31229607d65c787d44", + "md5": "55569971ef4023d3d8bc4406e07dfd50", "name": "package2", "platform": "osx", - "sha256": "b656b682b92eb4dd9c82cc17c3b71aa784f14b1f6abcc673c22ad9fc7e6a0c38", - "size": 1158, + "sha256": "25f70b1283ccdc00cd052d0b392d2fc11753574073eb00140f5064b1c587eccd", + "size": 1159, "subdir": "osx-64", - "timestamp": 1730467012569, + "timestamp": 1730898132846, "version": "0.1.0" }, "package2-0.2.0-h0dc7051_0.conda": { @@ -51,13 +51,13 @@ "build": "h0dc7051_0", "build_number": 0, "depends": [], - "md5": "654b07f9de93f6c93c52de80639c0d7d", + "md5": "fbbb2080c0cf031504886eaf4e57c6a4", "name": "package2", "platform": "osx", - "sha256": "1b0bf84284043444acfafa4fa0d79d104d37b7d38b0f68cd01596b3fa4a5af37", + "sha256": "7b0844fb3358077e0929c5c3da1d11c68514b8a29bba7ee766073f600be03a76", "size": 1158, "subdir": "osx-64", - "timestamp": 1730467012741, + "timestamp": 1730898133054, "version": "0.2.0" } }, diff --git a/tests/data/channels/channels/multiple_versions_channel_1/osx-arm64/package-0.1.0-h60d57d3_0.conda b/tests/data/channels/channels/multiple_versions_channel_1/osx-arm64/package-0.1.0-h60d57d3_0.conda new file mode 100644 index 000000000..c5222ee8c Binary files /dev/null and b/tests/data/channels/channels/multiple_versions_channel_1/osx-arm64/package-0.1.0-h60d57d3_0.conda differ diff --git a/tests/data/channels/channels/multiple_versions_channel_1/osx-arm64/package-0.2.0-h60d57d3_0.conda b/tests/data/channels/channels/multiple_versions_channel_1/osx-arm64/package-0.2.0-h60d57d3_0.conda new file mode 100644 index 000000000..2acdd9e19 Binary files /dev/null and b/tests/data/channels/channels/multiple_versions_channel_1/osx-arm64/package-0.2.0-h60d57d3_0.conda differ diff --git a/tests/data/channels/channels/multiple_versions_channel_1/osx-arm64/package2-0.1.0-h60d57d3_0.conda b/tests/data/channels/channels/multiple_versions_channel_1/osx-arm64/package2-0.1.0-h60d57d3_0.conda new file mode 100644 index 000000000..426cc44af Binary files /dev/null and b/tests/data/channels/channels/multiple_versions_channel_1/osx-arm64/package2-0.1.0-h60d57d3_0.conda differ diff --git a/tests/data/channels/channels/multiple_versions_channel_1/osx-arm64/package2-0.2.0-h60d57d3_0.conda b/tests/data/channels/channels/multiple_versions_channel_1/osx-arm64/package2-0.2.0-h60d57d3_0.conda new file mode 100644 index 000000000..c97944095 Binary files /dev/null and b/tests/data/channels/channels/multiple_versions_channel_1/osx-arm64/package2-0.2.0-h60d57d3_0.conda differ diff --git a/tests/data/channels/channels/global_update_channel_1/osx-arm64/repodata.json b/tests/data/channels/channels/multiple_versions_channel_1/osx-arm64/repodata.json similarity index 59% rename from tests/data/channels/channels/global_update_channel_1/osx-arm64/repodata.json rename to tests/data/channels/channels/multiple_versions_channel_1/osx-arm64/repodata.json index 8c956eac4..1896d3393 100644 --- a/tests/data/channels/channels/global_update_channel_1/osx-arm64/repodata.json +++ b/tests/data/channels/channels/multiple_versions_channel_1/osx-arm64/repodata.json @@ -9,13 +9,13 @@ "build": "h60d57d3_0", "build_number": 0, "depends": [], - "md5": "c22be839e38f01c52d9a5e08d316edd3", + "md5": "066f9bdf289cc4e12ed885bcffdb026b", "name": "package", "platform": "osx", - "sha256": "f9d685157a4ac99e7de9278be8393b6423d28dd07a24dda2343f8725fa06821f", - "size": 1239, + "sha256": "d81a3861e03f8f90dbb4ece27bafc267564a5dd6667f81af2a78fd0618ec7147", + "size": 1238, "subdir": "osx-arm64", - "timestamp": 1730467012532, + "timestamp": 1730898132792, "version": "0.1.0" }, "package-0.2.0-h60d57d3_0.conda": { @@ -23,13 +23,13 @@ "build": "h60d57d3_0", "build_number": 0, "depends": [], - "md5": "15ec5933542508973dcfbfbfa656c97f", + "md5": "a38507e7536708b09b42ec84103eb635", "name": "package", "platform": "osx", - "sha256": "196f29963081bec5c5ff5d75c2ca2dd632867bc565e507aab7bf8f72ec3f4c46", - "size": 1236, + "sha256": "a0562541a0cb6f91fba34734ff2fa4bf31dbe2d5f42b6bf7372160c7063f1bd2", + "size": 1230, "subdir": "osx-arm64", - "timestamp": 1730467012700, + "timestamp": 1730898133, "version": "0.2.0" }, "package2-0.1.0-h60d57d3_0.conda": { @@ -37,13 +37,13 @@ "build": "h60d57d3_0", "build_number": 0, "depends": [], - "md5": "922e987377fec11224c5ca54d82f1e34", + "md5": "d91c7863513e7900022e2847fa709c44", "name": "package2", "platform": "osx", - "sha256": "1cef086f3fe77ddc1bc1b75696bddbaf26989c3d3bdc50d3ddd5cafa267366c1", - "size": 1158, + "sha256": "54b560e721fae0674ce64ede3cc12e93a33ccee0164b08af9b88de2ad1bf537a", + "size": 1159, "subdir": "osx-arm64", - "timestamp": 1730467012532, + "timestamp": 1730898132792, "version": "0.1.0" }, "package2-0.2.0-h60d57d3_0.conda": { @@ -51,13 +51,13 @@ "build": "h60d57d3_0", "build_number": 0, "depends": [], - "md5": "2544da549da65381bade8621f665caf6", + "md5": "da479bbc76cf304ff31f5d4a3610364f", "name": "package2", "platform": "osx", - "sha256": "824043c70dd177f7a36f684b754f91ce72a2baa2e7c0d5d5d035a42d7ba0aeac", - "size": 1157, + "sha256": "6788f6d1d79bbdc280eee31d9565ca6e355cfe83198aa8c9b8468892e22f9d0a", + "size": 1151, "subdir": "osx-arm64", - "timestamp": 1730467012700, + "timestamp": 1730898133, "version": "0.2.0" } }, diff --git a/tests/data/channels/channels/multiple_versions_channel_1/win-64/package-0.1.0-h9490d1a_0.conda b/tests/data/channels/channels/multiple_versions_channel_1/win-64/package-0.1.0-h9490d1a_0.conda new file mode 100644 index 000000000..e732d3440 Binary files /dev/null and b/tests/data/channels/channels/multiple_versions_channel_1/win-64/package-0.1.0-h9490d1a_0.conda differ diff --git a/tests/data/channels/channels/multiple_versions_channel_1/win-64/package-0.2.0-h9490d1a_0.conda b/tests/data/channels/channels/multiple_versions_channel_1/win-64/package-0.2.0-h9490d1a_0.conda new file mode 100644 index 000000000..8c6ad3e75 Binary files /dev/null and b/tests/data/channels/channels/multiple_versions_channel_1/win-64/package-0.2.0-h9490d1a_0.conda differ diff --git a/tests/data/channels/channels/multiple_versions_channel_1/win-64/package2-0.1.0-h9490d1a_0.conda b/tests/data/channels/channels/multiple_versions_channel_1/win-64/package2-0.1.0-h9490d1a_0.conda new file mode 100644 index 000000000..d7d3597c8 Binary files /dev/null and b/tests/data/channels/channels/multiple_versions_channel_1/win-64/package2-0.1.0-h9490d1a_0.conda differ diff --git a/tests/data/channels/channels/multiple_versions_channel_1/win-64/package2-0.2.0-h9490d1a_0.conda b/tests/data/channels/channels/multiple_versions_channel_1/win-64/package2-0.2.0-h9490d1a_0.conda new file mode 100644 index 000000000..14998289e Binary files /dev/null and b/tests/data/channels/channels/multiple_versions_channel_1/win-64/package2-0.2.0-h9490d1a_0.conda differ diff --git a/tests/data/channels/channels/global_update_channel_1/win-64/repodata.json b/tests/data/channels/channels/multiple_versions_channel_1/win-64/repodata.json similarity index 59% rename from tests/data/channels/channels/global_update_channel_1/win-64/repodata.json rename to tests/data/channels/channels/multiple_versions_channel_1/win-64/repodata.json index ac2747eea..dfa9f377a 100644 --- a/tests/data/channels/channels/global_update_channel_1/win-64/repodata.json +++ b/tests/data/channels/channels/multiple_versions_channel_1/win-64/repodata.json @@ -9,13 +9,13 @@ "build": "h9490d1a_0", "build_number": 0, "depends": [], - "md5": "92d2eff2de4b49177428e039e5cfee31", + "md5": "745d63cedc7ab134762eed55fa130224", "name": "package", "platform": "win", - "sha256": "97251a7d2779237354a51853e66926884a38a8abd9fc4674b04e66b50e83e33e", - "size": 1238, + "sha256": "3ca104fc659f7e7d43f028665ff009b1afbd1df45c6a55787176eda973746a87", + "size": 1240, "subdir": "win-64", - "timestamp": 1730467012451, + "timestamp": 1730898132680, "version": "0.1.0" }, "package-0.2.0-h9490d1a_0.conda": { @@ -23,13 +23,13 @@ "build": "h9490d1a_0", "build_number": 0, "depends": [], - "md5": "813bf1b0e65d16a2dc683350f356ffe1", + "md5": "986f652f269e8ac205f395581a5cc59f", "name": "package", "platform": "win", - "sha256": "39b3f3397a34741f0d695b39c1147d74f007cb60c405d68678831f0d227529e9", - "size": 1242, + "sha256": "c5d72e133b9f37f2030a92149375f1ae1a6ac5bcfc3e373364c4bbf5dbeb8c98", + "size": 1241, "subdir": "win-64", - "timestamp": 1730467012619, + "timestamp": 1730898132900, "version": "0.2.0" }, "package2-0.1.0-h9490d1a_0.conda": { @@ -37,13 +37,13 @@ "build": "h9490d1a_0", "build_number": 0, "depends": [], - "md5": "e4291bf68b8a894723c1b747bdfdf0fd", + "md5": "ed3dbf7b4f1b80b63f9ff6e34c5501a8", "name": "package2", "platform": "win", - "sha256": "92843981ba11e0941a4855a892ed408c248242891ce4e72a9e6faf99f07431b8", - "size": 1172, + "sha256": "1038a96bafb2fb49357848d5c9aca6a67cdd89d6c4d04ed6ce4480eff6b81a81", + "size": 1169, "subdir": "win-64", - "timestamp": 1730467012451, + "timestamp": 1730898132681, "version": "0.1.0" }, "package2-0.2.0-h9490d1a_0.conda": { @@ -51,13 +51,13 @@ "build": "h9490d1a_0", "build_number": 0, "depends": [], - "md5": "49619c2359c11993877c23d4c1d15e1d", + "md5": "07a7e70d365efdcc754bf329feb3d95a", "name": "package2", "platform": "win", - "sha256": "92e2ee3f15e89e484e233ad0ff4dc188e3068c0246a1e93eddc0bff48e693da8", - "size": 1172, + "sha256": "ea6324a02aaf3f5ef7ad9f3e164aeb81f8c7dd46b5b25eb026101c53d81aca47", + "size": 1168, "subdir": "win-64", - "timestamp": 1730467012619, + "timestamp": 1730898132900, "version": "0.2.0" } }, diff --git a/tests/data/channels/channels/non_self_expose_channel_1/linux-64/jupyter-0.1.0-hb0f4dca_0.conda b/tests/data/channels/channels/non_self_expose_channel_1/linux-64/jupyter-0.1.0-hb0f4dca_0.conda index ac7dea2bf..1d41343dd 100644 Binary files a/tests/data/channels/channels/non_self_expose_channel_1/linux-64/jupyter-0.1.0-hb0f4dca_0.conda and b/tests/data/channels/channels/non_self_expose_channel_1/linux-64/jupyter-0.1.0-hb0f4dca_0.conda differ diff --git a/tests/data/channels/channels/non_self_expose_channel_1/linux-64/jupyter-core-0.1.0-hb0f4dca_0.conda b/tests/data/channels/channels/non_self_expose_channel_1/linux-64/jupyter-core-0.1.0-hb0f4dca_0.conda index da79a4294..0b30c5f7c 100644 Binary files a/tests/data/channels/channels/non_self_expose_channel_1/linux-64/jupyter-core-0.1.0-hb0f4dca_0.conda and b/tests/data/channels/channels/non_self_expose_channel_1/linux-64/jupyter-core-0.1.0-hb0f4dca_0.conda differ diff --git a/tests/data/channels/channels/non_self_expose_channel_1/linux-64/repodata.json b/tests/data/channels/channels/non_self_expose_channel_1/linux-64/repodata.json index eee1376ee..83354200f 100644 --- a/tests/data/channels/channels/non_self_expose_channel_1/linux-64/repodata.json +++ b/tests/data/channels/channels/non_self_expose_channel_1/linux-64/repodata.json @@ -11,13 +11,13 @@ "depends": [ "jupyter-core" ], - "md5": "dd76030488e9c435415ace679dfc6c6c", + "md5": "e21e5d39645428ebcc38113558eae5d8", "name": "jupyter", "platform": "linux", - "sha256": "12aa3b2a43b3f5c2491d9245317980775b102ad8b105f0893c8cce7510348142", - "size": 992, + "sha256": "f326acf6bcd44f203a9e2375c8a646f42c649a08de22f4a9c7758ee78fa16704", + "size": 993, "subdir": "linux-64", - "timestamp": 1730467012820, + "timestamp": 1730898133150, "version": "0.1.0" }, "jupyter-core-0.1.0-hb0f4dca_0.conda": { @@ -25,13 +25,13 @@ "build": "hb0f4dca_0", "build_number": 0, "depends": [], - "md5": "027fe944cdd5d45122f6ab927bee070e", + "md5": "ffac5d2147953383cf34752e2996d066", "name": "jupyter-core", "platform": "linux", - "sha256": "775fb74e448a547aca498afd0d0c1b91e4de513dee567c10de617494fba02933", - "size": 1177, + "sha256": "7a6f7fa35475b5370a4081c27d15f3e5f5cfe59a33fa010fa573badb503c16ff", + "size": 1179, "subdir": "linux-64", - "timestamp": 1730467012820, + "timestamp": 1730898133150, "version": "0.1.0" } }, diff --git a/tests/data/channels/channels/non_self_expose_channel_1/osx-64/jupyter-0.1.0-h0dc7051_0.conda b/tests/data/channels/channels/non_self_expose_channel_1/osx-64/jupyter-0.1.0-h0dc7051_0.conda index 173368e7c..088829755 100644 Binary files a/tests/data/channels/channels/non_self_expose_channel_1/osx-64/jupyter-0.1.0-h0dc7051_0.conda and b/tests/data/channels/channels/non_self_expose_channel_1/osx-64/jupyter-0.1.0-h0dc7051_0.conda differ diff --git a/tests/data/channels/channels/non_self_expose_channel_1/osx-64/jupyter-core-0.1.0-h0dc7051_0.conda b/tests/data/channels/channels/non_self_expose_channel_1/osx-64/jupyter-core-0.1.0-h0dc7051_0.conda index d0f5396e5..154b9be67 100644 Binary files a/tests/data/channels/channels/non_self_expose_channel_1/osx-64/jupyter-core-0.1.0-h0dc7051_0.conda and b/tests/data/channels/channels/non_self_expose_channel_1/osx-64/jupyter-core-0.1.0-h0dc7051_0.conda differ diff --git a/tests/data/channels/channels/non_self_expose_channel_1/osx-64/repodata.json b/tests/data/channels/channels/non_self_expose_channel_1/osx-64/repodata.json index 4fdfc2cb3..33952f8ca 100644 --- a/tests/data/channels/channels/non_self_expose_channel_1/osx-64/repodata.json +++ b/tests/data/channels/channels/non_self_expose_channel_1/osx-64/repodata.json @@ -11,13 +11,13 @@ "depends": [ "jupyter-core" ], - "md5": "addc1ae4c193b2b17f15c74c2266a3e6", + "md5": "521a7441ff2515d8f662cd64a2bd0779", "name": "jupyter", "platform": "osx", - "sha256": "2708ac7130ba75081b40bd6876b1d72cc2cb596d088e61082b837980c6780031", - "size": 990, + "sha256": "a4b91bd16c655cf91addacd1e6f8a81c2142f79b061045da252bbb9af5e3fb0d", + "size": 991, "subdir": "osx-64", - "timestamp": 1730467012901, + "timestamp": 1730898133244, "version": "0.1.0" }, "jupyter-core-0.1.0-h0dc7051_0.conda": { @@ -25,13 +25,13 @@ "build": "h0dc7051_0", "build_number": 0, "depends": [], - "md5": "fa1da2ec0d147fd43071791a5263a9b1", + "md5": "18f2ebe336c8f9c89ef2e3ac6e8af241", "name": "jupyter-core", "platform": "osx", - "sha256": "fe3514c1930c49acf8b952c8ce9117731826bbc2fbc1852f75e37240e8aa70b2", - "size": 1174, + "sha256": "fdc800c1690a78f679d8445f7da1d20856b58bc0cea15204818002d8b462357c", + "size": 1177, "subdir": "osx-64", - "timestamp": 1730467012901, + "timestamp": 1730898133244, "version": "0.1.0" } }, diff --git a/tests/data/channels/channels/non_self_expose_channel_1/osx-arm64/jupyter-0.1.0-h60d57d3_0.conda b/tests/data/channels/channels/non_self_expose_channel_1/osx-arm64/jupyter-0.1.0-h60d57d3_0.conda index 86f7b85b4..3d2681d7a 100644 Binary files a/tests/data/channels/channels/non_self_expose_channel_1/osx-arm64/jupyter-0.1.0-h60d57d3_0.conda and b/tests/data/channels/channels/non_self_expose_channel_1/osx-arm64/jupyter-0.1.0-h60d57d3_0.conda differ diff --git a/tests/data/channels/channels/non_self_expose_channel_1/osx-arm64/jupyter-core-0.1.0-h60d57d3_0.conda b/tests/data/channels/channels/non_self_expose_channel_1/osx-arm64/jupyter-core-0.1.0-h60d57d3_0.conda index f8c166f2b..dedb92d73 100644 Binary files a/tests/data/channels/channels/non_self_expose_channel_1/osx-arm64/jupyter-core-0.1.0-h60d57d3_0.conda and b/tests/data/channels/channels/non_self_expose_channel_1/osx-arm64/jupyter-core-0.1.0-h60d57d3_0.conda differ diff --git a/tests/data/channels/channels/non_self_expose_channel_1/osx-arm64/repodata.json b/tests/data/channels/channels/non_self_expose_channel_1/osx-arm64/repodata.json index 9e45342f2..536d79b69 100644 --- a/tests/data/channels/channels/non_self_expose_channel_1/osx-arm64/repodata.json +++ b/tests/data/channels/channels/non_self_expose_channel_1/osx-arm64/repodata.json @@ -11,13 +11,13 @@ "depends": [ "jupyter-core" ], - "md5": "2f23c27a6137ff9fceefac1baa30aa91", + "md5": "ab87fefd9d91e53bdf24e3fa81d8dc2f", "name": "jupyter", "platform": "osx", - "sha256": "92da9f340b9e8e3ddb4cd5a2b952110c93137cc210b57daede7906fe99f355c6", - "size": 990, + "sha256": "bdf53fb2268e9e9558dc4694550c34297c8bb0a9f9253aec5291a6303d6ef88a", + "size": 991, "subdir": "osx-arm64", - "timestamp": 1730467012866, + "timestamp": 1730898133196, "version": "0.1.0" }, "jupyter-core-0.1.0-h60d57d3_0.conda": { @@ -25,13 +25,13 @@ "build": "h60d57d3_0", "build_number": 0, "depends": [], - "md5": "4d82d043e9bbcda7af6a771d71a40cc3", + "md5": "9513cce3dd18ea97cda6210e5fd038c1", "name": "jupyter-core", "platform": "osx", - "sha256": "2f5ea82314fedec0d573d3f5303bc431a787512ec441e37284dce3788b24ed7c", - "size": 1176, + "sha256": "27b8c5b6e216129cd6a6d9cf7a43b627c038b2529660b6d81fc7459ab1e12965", + "size": 1178, "subdir": "osx-arm64", - "timestamp": 1730467012866, + "timestamp": 1730898133196, "version": "0.1.0" } }, diff --git a/tests/data/channels/channels/non_self_expose_channel_1/win-64/jupyter-0.1.0-h9490d1a_0.conda b/tests/data/channels/channels/non_self_expose_channel_1/win-64/jupyter-0.1.0-h9490d1a_0.conda index 415513702..5cab8c43a 100644 Binary files a/tests/data/channels/channels/non_self_expose_channel_1/win-64/jupyter-0.1.0-h9490d1a_0.conda and b/tests/data/channels/channels/non_self_expose_channel_1/win-64/jupyter-0.1.0-h9490d1a_0.conda differ diff --git a/tests/data/channels/channels/non_self_expose_channel_1/win-64/jupyter-core-0.1.0-h9490d1a_0.conda b/tests/data/channels/channels/non_self_expose_channel_1/win-64/jupyter-core-0.1.0-h9490d1a_0.conda index 88009c97d..615edf4d5 100644 Binary files a/tests/data/channels/channels/non_self_expose_channel_1/win-64/jupyter-core-0.1.0-h9490d1a_0.conda and b/tests/data/channels/channels/non_self_expose_channel_1/win-64/jupyter-core-0.1.0-h9490d1a_0.conda differ diff --git a/tests/data/channels/channels/non_self_expose_channel_1/win-64/repodata.json b/tests/data/channels/channels/non_self_expose_channel_1/win-64/repodata.json index ed559acb0..41e8e1d9c 100644 --- a/tests/data/channels/channels/non_self_expose_channel_1/win-64/repodata.json +++ b/tests/data/channels/channels/non_self_expose_channel_1/win-64/repodata.json @@ -11,13 +11,13 @@ "depends": [ "jupyter-core" ], - "md5": "73673e341d4868188e1d95884240f2cf", + "md5": "b084adf91a0b0cc9248389c203e6b0c6", "name": "jupyter", "platform": "win", - "sha256": "ab7b8f31bbb31d40946f6baaa6a9fe6c3740e4cfba23eed0b6f97eed13fbb995", - "size": 993, + "sha256": "484c47d5b5715943af1589f08764eaed7fbd9a9eb156c76d8c195a8f4744ba03", + "size": 992, "subdir": "win-64", - "timestamp": 1730467012785, + "timestamp": 1730898133106, "version": "0.1.0" }, "jupyter-core-0.1.0-h9490d1a_0.conda": { @@ -25,13 +25,13 @@ "build": "h9490d1a_0", "build_number": 0, "depends": [], - "md5": "49811ba04ebf3f233d380c8e7f0f808a", + "md5": "2ccc77fec0a0a6bd881e1114f2ac4ae8", "name": "jupyter-core", "platform": "win", - "sha256": "38c7ce0b56c3a421aa8eab4dc2324687ebb8ee61842d6e9d3c4160dce4894ddd", - "size": 1186, + "sha256": "823222c9ea2f41898aec542eda9f68867f6f21b33d775f215be68568494690e5", + "size": 1184, "subdir": "win-64", - "timestamp": 1730467012785, + "timestamp": 1730898133106, "version": "0.1.0" } }, diff --git a/tests/data/channels/channels/non_self_expose_channel_2/linux-64/jupyter-0.1.0-hb0f4dca_0.conda b/tests/data/channels/channels/non_self_expose_channel_2/linux-64/jupyter-0.1.0-hb0f4dca_0.conda index b55bae700..77190f350 100644 Binary files a/tests/data/channels/channels/non_self_expose_channel_2/linux-64/jupyter-0.1.0-hb0f4dca_0.conda and b/tests/data/channels/channels/non_self_expose_channel_2/linux-64/jupyter-0.1.0-hb0f4dca_0.conda differ diff --git a/tests/data/channels/channels/non_self_expose_channel_2/linux-64/jupyter-core-0.2.0-hb0f4dca_0.conda b/tests/data/channels/channels/non_self_expose_channel_2/linux-64/jupyter-core-0.2.0-hb0f4dca_0.conda index 4b1891081..8223f7ae4 100644 Binary files a/tests/data/channels/channels/non_self_expose_channel_2/linux-64/jupyter-core-0.2.0-hb0f4dca_0.conda and b/tests/data/channels/channels/non_self_expose_channel_2/linux-64/jupyter-core-0.2.0-hb0f4dca_0.conda differ diff --git a/tests/data/channels/channels/non_self_expose_channel_2/linux-64/repodata.json b/tests/data/channels/channels/non_self_expose_channel_2/linux-64/repodata.json index cafff35b1..e16e5adb4 100644 --- a/tests/data/channels/channels/non_self_expose_channel_2/linux-64/repodata.json +++ b/tests/data/channels/channels/non_self_expose_channel_2/linux-64/repodata.json @@ -11,13 +11,13 @@ "depends": [ "jupyter-core" ], - "md5": "763763d735b8406bb2fb6c5316495943", + "md5": "f573365a3c55a98cc1fec9cd46994b6f", "name": "jupyter", "platform": "linux", - "sha256": "06dbeb2ca95e16e45ea0f82c70bc852ea28955d438c48e2faf4aa4518015663e", - "size": 992, + "sha256": "1be25cb22a8315b150e6009dabcf5bd98ea084d4a92c3c89f0253451b0303e60", + "size": 993, "subdir": "linux-64", - "timestamp": 1730467012975, + "timestamp": 1730898133339, "version": "0.1.0" }, "jupyter-core-0.2.0-hb0f4dca_0.conda": { @@ -25,13 +25,13 @@ "build": "hb0f4dca_0", "build_number": 0, "depends": [], - "md5": "09138c2ac9baa0438b66dad8dad2ce09", + "md5": "9f9bd66dab65c8163aa352861bd9cbfd", "name": "jupyter-core", "platform": "linux", - "sha256": "55518094982a90a5c90ad0ebef4e9ffb4f5bd1010c3c04b5c5b6cf62be1212d4", - "size": 1177, + "sha256": "1a5b2ddf62347cf6c942c8d393d7d46cf960bc78ade4ee28e9d029771371d21a", + "size": 1179, "subdir": "linux-64", - "timestamp": 1730467012975, + "timestamp": 1730898133340, "version": "0.2.0" } }, diff --git a/tests/data/channels/channels/non_self_expose_channel_2/osx-64/jupyter-0.1.0-h0dc7051_0.conda b/tests/data/channels/channels/non_self_expose_channel_2/osx-64/jupyter-0.1.0-h0dc7051_0.conda index 36c5cd516..792c382a0 100644 Binary files a/tests/data/channels/channels/non_self_expose_channel_2/osx-64/jupyter-0.1.0-h0dc7051_0.conda and b/tests/data/channels/channels/non_self_expose_channel_2/osx-64/jupyter-0.1.0-h0dc7051_0.conda differ diff --git a/tests/data/channels/channels/non_self_expose_channel_2/osx-64/jupyter-core-0.2.0-h0dc7051_0.conda b/tests/data/channels/channels/non_self_expose_channel_2/osx-64/jupyter-core-0.2.0-h0dc7051_0.conda index 13f2b7cc6..344157039 100644 Binary files a/tests/data/channels/channels/non_self_expose_channel_2/osx-64/jupyter-core-0.2.0-h0dc7051_0.conda and b/tests/data/channels/channels/non_self_expose_channel_2/osx-64/jupyter-core-0.2.0-h0dc7051_0.conda differ diff --git a/tests/data/channels/channels/non_self_expose_channel_2/osx-64/repodata.json b/tests/data/channels/channels/non_self_expose_channel_2/osx-64/repodata.json index c9a015c99..2c83bf9c4 100644 --- a/tests/data/channels/channels/non_self_expose_channel_2/osx-64/repodata.json +++ b/tests/data/channels/channels/non_self_expose_channel_2/osx-64/repodata.json @@ -11,13 +11,13 @@ "depends": [ "jupyter-core" ], - "md5": "ce62299fb763726200ab5344bc5df8d3", + "md5": "5864c37ec76421f67fbbaa180f2a3255", "name": "jupyter", "platform": "osx", - "sha256": "4ba6fa35a5bbaeba69375b14a19e7a8e55bb85e575ff560bb135b9877a89b774", + "sha256": "7580d8c3a5eec707ca9ce642425e083205385103414bab3fe3d8b6a18f6cf109", "size": 991, "subdir": "osx-64", - "timestamp": 1730467013049, + "timestamp": 1730898133432, "version": "0.1.0" }, "jupyter-core-0.2.0-h0dc7051_0.conda": { @@ -25,13 +25,13 @@ "build": "h0dc7051_0", "build_number": 0, "depends": [], - "md5": "752223f6aaa70f42dd5471fdfca604e8", + "md5": "8f2e95f21707480920e7b5dbac0f4d08", "name": "jupyter-core", "platform": "osx", - "sha256": "0a983cfcd871c8016fdd738f70ab0b20326c27069c8112d0512efa45cd272f96", - "size": 1178, + "sha256": "b88f90c20bf198878bc49ba806715590b7fbe0470b9db09c567489c36a0833ea", + "size": 1177, "subdir": "osx-64", - "timestamp": 1730467013049, + "timestamp": 1730898133432, "version": "0.2.0" } }, diff --git a/tests/data/channels/channels/non_self_expose_channel_2/osx-arm64/jupyter-0.1.0-h60d57d3_0.conda b/tests/data/channels/channels/non_self_expose_channel_2/osx-arm64/jupyter-0.1.0-h60d57d3_0.conda index cb277bf0b..5b29d20e4 100644 Binary files a/tests/data/channels/channels/non_self_expose_channel_2/osx-arm64/jupyter-0.1.0-h60d57d3_0.conda and b/tests/data/channels/channels/non_self_expose_channel_2/osx-arm64/jupyter-0.1.0-h60d57d3_0.conda differ diff --git a/tests/data/channels/channels/non_self_expose_channel_2/osx-arm64/jupyter-core-0.2.0-h60d57d3_0.conda b/tests/data/channels/channels/non_self_expose_channel_2/osx-arm64/jupyter-core-0.2.0-h60d57d3_0.conda index 51b4efcd5..4f7336fd4 100644 Binary files a/tests/data/channels/channels/non_self_expose_channel_2/osx-arm64/jupyter-core-0.2.0-h60d57d3_0.conda and b/tests/data/channels/channels/non_self_expose_channel_2/osx-arm64/jupyter-core-0.2.0-h60d57d3_0.conda differ diff --git a/tests/data/channels/channels/non_self_expose_channel_2/osx-arm64/repodata.json b/tests/data/channels/channels/non_self_expose_channel_2/osx-arm64/repodata.json index 284511d98..5ead9e4af 100644 --- a/tests/data/channels/channels/non_self_expose_channel_2/osx-arm64/repodata.json +++ b/tests/data/channels/channels/non_self_expose_channel_2/osx-arm64/repodata.json @@ -11,13 +11,13 @@ "depends": [ "jupyter-core" ], - "md5": "24a08416c58107e715f76a4ebe5db321", + "md5": "dcdba2684e9b362a8b146d42e9d5c1ac", "name": "jupyter", "platform": "osx", - "sha256": "3b6ad2a90d252f2356f87a3e6a95269dc458308b5bb9ae34e7e5383d05e10d37", - "size": 988, + "sha256": "5c5c4d8abf9e7a3976572d2cf4c601ea7d56e415d8fd7e43cea9a5fc5f0ce4c7", + "size": 991, "subdir": "osx-arm64", - "timestamp": 1730467013015, + "timestamp": 1730898133387, "version": "0.1.0" }, "jupyter-core-0.2.0-h60d57d3_0.conda": { @@ -25,13 +25,13 @@ "build": "h60d57d3_0", "build_number": 0, "depends": [], - "md5": "5643d6fbbdb2e9e46bacfdc41677d596", + "md5": "7a0d4ee42bff11fe98e53df9f85a65fa", "name": "jupyter-core", "platform": "osx", - "sha256": "48ac33cef1201005a6abd4d7240ddb3ff97c9c99013a675dc9d78c601a477ccd", + "sha256": "21323847eb4ad0711523cf3ff7329988d81ea130741822bc3c6602cc6300358d", "size": 1178, "subdir": "osx-arm64", - "timestamp": 1730467013015, + "timestamp": 1730898133387, "version": "0.2.0" } }, diff --git a/tests/data/channels/channels/non_self_expose_channel_2/win-64/jupyter-0.1.0-h9490d1a_0.conda b/tests/data/channels/channels/non_self_expose_channel_2/win-64/jupyter-0.1.0-h9490d1a_0.conda index 35b07d85e..c5d44b5c8 100644 Binary files a/tests/data/channels/channels/non_self_expose_channel_2/win-64/jupyter-0.1.0-h9490d1a_0.conda and b/tests/data/channels/channels/non_self_expose_channel_2/win-64/jupyter-0.1.0-h9490d1a_0.conda differ diff --git a/tests/data/channels/channels/non_self_expose_channel_2/win-64/jupyter-core-0.2.0-h9490d1a_0.conda b/tests/data/channels/channels/non_self_expose_channel_2/win-64/jupyter-core-0.2.0-h9490d1a_0.conda index bfa5866f1..db986d3af 100644 Binary files a/tests/data/channels/channels/non_self_expose_channel_2/win-64/jupyter-core-0.2.0-h9490d1a_0.conda and b/tests/data/channels/channels/non_self_expose_channel_2/win-64/jupyter-core-0.2.0-h9490d1a_0.conda differ diff --git a/tests/data/channels/channels/non_self_expose_channel_2/win-64/repodata.json b/tests/data/channels/channels/non_self_expose_channel_2/win-64/repodata.json index 161512ec0..3321c4d7c 100644 --- a/tests/data/channels/channels/non_self_expose_channel_2/win-64/repodata.json +++ b/tests/data/channels/channels/non_self_expose_channel_2/win-64/repodata.json @@ -11,13 +11,13 @@ "depends": [ "jupyter-core" ], - "md5": "2ce68852bb3e8f1058ff1ac48c65bd8e", + "md5": "79449188cf7890c58129c1382006cb4d", "name": "jupyter", "platform": "win", - "sha256": "5f4cfc459395577b73b5e1b8ad98bbca11a1c9efd910c7ce3ad4e7cf7ee426ca", - "size": 991, + "sha256": "dea256127dbe4145790c7d3cb7f1df3671613476ac684f470effae164b4a2feb", + "size": 993, "subdir": "win-64", - "timestamp": 1730467012940, + "timestamp": 1730898133293, "version": "0.1.0" }, "jupyter-core-0.2.0-h9490d1a_0.conda": { @@ -25,13 +25,13 @@ "build": "h9490d1a_0", "build_number": 0, "depends": [], - "md5": "e31679d8adcf337e435fecc6aef06431", + "md5": "927402097618f1aef1971f6415235c04", "name": "jupyter-core", "platform": "win", - "sha256": "f0c6687053d1582d3e48e2b8b0640292d98a0c903e8d75254284b69c255fb0eb", + "sha256": "21ca5e89ad4f5e1b05002c086b971b0ae36f2313b411f9bd963e12c40c8d7c08", "size": 1185, "subdir": "win-64", - "timestamp": 1730467012940, + "timestamp": 1730898133293, "version": "0.2.0" } }, diff --git a/tests/data/channels/channels/trampoline_1/linux-64/dummy-trampoline-0.1.0-hb0f4dca_0.conda b/tests/data/channels/channels/trampoline_1/linux-64/dummy-trampoline-0.1.0-hb0f4dca_0.conda new file mode 100644 index 000000000..5e331b14c Binary files /dev/null and b/tests/data/channels/channels/trampoline_1/linux-64/dummy-trampoline-0.1.0-hb0f4dca_0.conda differ diff --git a/tests/data/channels/channels/trampoline_1/linux-64/repodata.json b/tests/data/channels/channels/trampoline_1/linux-64/repodata.json new file mode 100644 index 000000000..a11b5d683 --- /dev/null +++ b/tests/data/channels/channels/trampoline_1/linux-64/repodata.json @@ -0,0 +1,23 @@ +{ + "info": { + "subdir": "linux-64" + }, + "packages": {}, + "packages.conda": { + "dummy-trampoline-0.1.0-hb0f4dca_0.conda": { + "arch": "x86_64", + "build": "hb0f4dca_0", + "build_number": 0, + "depends": [], + "md5": "347d5984630e6be5217bfbf485be24b9", + "name": "dummy-trampoline", + "platform": "linux", + "sha256": "6ab98c0e5b7aba534d4b4e881cc8dab471a695bf47c53fd654cc3aed0fa776e7", + "size": 1624, + "subdir": "linux-64", + "timestamp": 1730898133538, + "version": "0.1.0" + } + }, + "repodata_version": 2 +} diff --git a/tests/data/channels/channels/trampoline_1/noarch/repodata.json b/tests/data/channels/channels/trampoline_1/noarch/repodata.json new file mode 100644 index 000000000..7402d6d29 --- /dev/null +++ b/tests/data/channels/channels/trampoline_1/noarch/repodata.json @@ -0,0 +1,8 @@ +{ + "info": { + "subdir": "noarch" + }, + "packages": {}, + "packages.conda": {}, + "repodata_version": 2 +} diff --git a/tests/data/channels/channels/trampoline_1/osx-64/dummy-trampoline-0.1.0-h0dc7051_0.conda b/tests/data/channels/channels/trampoline_1/osx-64/dummy-trampoline-0.1.0-h0dc7051_0.conda new file mode 100644 index 000000000..13934e10a Binary files /dev/null and b/tests/data/channels/channels/trampoline_1/osx-64/dummy-trampoline-0.1.0-h0dc7051_0.conda differ diff --git a/tests/data/channels/channels/trampoline_1/osx-64/repodata.json b/tests/data/channels/channels/trampoline_1/osx-64/repodata.json new file mode 100644 index 000000000..682534266 --- /dev/null +++ b/tests/data/channels/channels/trampoline_1/osx-64/repodata.json @@ -0,0 +1,23 @@ +{ + "info": { + "subdir": "osx-64" + }, + "packages": {}, + "packages.conda": { + "dummy-trampoline-0.1.0-h0dc7051_0.conda": { + "arch": "x86_64", + "build": "h0dc7051_0", + "build_number": 0, + "depends": [], + "md5": "8ac53c67bbd2cc5b2fecb8a5647a02c6", + "name": "dummy-trampoline", + "platform": "osx", + "sha256": "419aada6da7a0aa1b332c2d3b8a5b34a517100040073e4399967ed4975f06141", + "size": 1622, + "subdir": "osx-64", + "timestamp": 1730898133623, + "version": "0.1.0" + } + }, + "repodata_version": 2 +} diff --git a/tests/data/channels/channels/trampoline_1/osx-arm64/dummy-trampoline-0.1.0-h60d57d3_0.conda b/tests/data/channels/channels/trampoline_1/osx-arm64/dummy-trampoline-0.1.0-h60d57d3_0.conda new file mode 100644 index 000000000..bda8a5492 Binary files /dev/null and b/tests/data/channels/channels/trampoline_1/osx-arm64/dummy-trampoline-0.1.0-h60d57d3_0.conda differ diff --git a/tests/data/channels/channels/trampoline_1/osx-arm64/repodata.json b/tests/data/channels/channels/trampoline_1/osx-arm64/repodata.json new file mode 100644 index 000000000..31bd62f21 --- /dev/null +++ b/tests/data/channels/channels/trampoline_1/osx-arm64/repodata.json @@ -0,0 +1,23 @@ +{ + "info": { + "subdir": "osx-arm64" + }, + "packages": {}, + "packages.conda": { + "dummy-trampoline-0.1.0-h60d57d3_0.conda": { + "arch": "arm64", + "build": "h60d57d3_0", + "build_number": 0, + "depends": [], + "md5": "276bdf61769e64a9b26cc5ffe9b91828", + "name": "dummy-trampoline", + "platform": "osx", + "sha256": "912102acdd17263520f72afa7f080081615ffbf8e36b1be64ca923f039c9564f", + "size": 1622, + "subdir": "osx-arm64", + "timestamp": 1730898133580, + "version": "0.1.0" + } + }, + "repodata_version": 2 +} diff --git a/tests/data/channels/channels/trampoline_1/win-64/dummy-trampoline-0.1.0-h9490d1a_0.conda b/tests/data/channels/channels/trampoline_1/win-64/dummy-trampoline-0.1.0-h9490d1a_0.conda new file mode 100644 index 000000000..1eb7dab0f Binary files /dev/null and b/tests/data/channels/channels/trampoline_1/win-64/dummy-trampoline-0.1.0-h9490d1a_0.conda differ diff --git a/tests/data/channels/channels/trampoline_1/win-64/repodata.json b/tests/data/channels/channels/trampoline_1/win-64/repodata.json new file mode 100644 index 000000000..9c4c8691f --- /dev/null +++ b/tests/data/channels/channels/trampoline_1/win-64/repodata.json @@ -0,0 +1,23 @@ +{ + "info": { + "subdir": "win-64" + }, + "packages": {}, + "packages.conda": { + "dummy-trampoline-0.1.0-h9490d1a_0.conda": { + "arch": "x86_64", + "build": "h9490d1a_0", + "build_number": 0, + "depends": [], + "md5": "231a9110ff4cd7021a5e6f3708045ab6", + "name": "dummy-trampoline", + "platform": "win", + "sha256": "78d7357684cff33081f8197a92ddeaa95c109cebaef605b536d21740035dcaff", + "size": 1602, + "subdir": "win-64", + "timestamp": 1730898133481, + "version": "0.1.0" + } + }, + "repodata_version": 2 +} diff --git a/tests/data/channels/channels/trampoline_2/linux-64/dummy-trampoline-0.2.0-hb0f4dca_0.conda b/tests/data/channels/channels/trampoline_2/linux-64/dummy-trampoline-0.2.0-hb0f4dca_0.conda new file mode 100644 index 000000000..369e9d527 Binary files /dev/null and b/tests/data/channels/channels/trampoline_2/linux-64/dummy-trampoline-0.2.0-hb0f4dca_0.conda differ diff --git a/tests/data/channels/channels/trampoline_2/linux-64/repodata.json b/tests/data/channels/channels/trampoline_2/linux-64/repodata.json new file mode 100644 index 000000000..e5e314e92 --- /dev/null +++ b/tests/data/channels/channels/trampoline_2/linux-64/repodata.json @@ -0,0 +1,23 @@ +{ + "info": { + "subdir": "linux-64" + }, + "packages": {}, + "packages.conda": { + "dummy-trampoline-0.2.0-hb0f4dca_0.conda": { + "arch": "x86_64", + "build": "hb0f4dca_0", + "build_number": 0, + "depends": [], + "md5": "5cf3ff552794c2244ded9cb84111a5de", + "name": "dummy-trampoline", + "platform": "linux", + "sha256": "e0002e6221ca171cc2b87f981c997e58c1ea210da680b7f63182970d96a80c97", + "size": 1630, + "subdir": "linux-64", + "timestamp": 1730898133718, + "version": "0.2.0" + } + }, + "repodata_version": 2 +} diff --git a/tests/data/channels/channels/trampoline_2/noarch/repodata.json b/tests/data/channels/channels/trampoline_2/noarch/repodata.json new file mode 100644 index 000000000..7402d6d29 --- /dev/null +++ b/tests/data/channels/channels/trampoline_2/noarch/repodata.json @@ -0,0 +1,8 @@ +{ + "info": { + "subdir": "noarch" + }, + "packages": {}, + "packages.conda": {}, + "repodata_version": 2 +} diff --git a/tests/data/channels/channels/trampoline_2/osx-64/dummy-trampoline-0.2.0-h0dc7051_0.conda b/tests/data/channels/channels/trampoline_2/osx-64/dummy-trampoline-0.2.0-h0dc7051_0.conda new file mode 100644 index 000000000..880c96d6c Binary files /dev/null and b/tests/data/channels/channels/trampoline_2/osx-64/dummy-trampoline-0.2.0-h0dc7051_0.conda differ diff --git a/tests/data/channels/channels/trampoline_2/osx-64/repodata.json b/tests/data/channels/channels/trampoline_2/osx-64/repodata.json new file mode 100644 index 000000000..cc9c6845e --- /dev/null +++ b/tests/data/channels/channels/trampoline_2/osx-64/repodata.json @@ -0,0 +1,23 @@ +{ + "info": { + "subdir": "osx-64" + }, + "packages": {}, + "packages.conda": { + "dummy-trampoline-0.2.0-h0dc7051_0.conda": { + "arch": "x86_64", + "build": "h0dc7051_0", + "build_number": 0, + "depends": [], + "md5": "ed82cfca5dc790ce8600cd7f93c7e685", + "name": "dummy-trampoline", + "platform": "osx", + "sha256": "d352817b1fd10ef2816a94bcae75569f4d788341bfb09bea1c8ae80eac3f5719", + "size": 1629, + "subdir": "osx-64", + "timestamp": 1730898133805, + "version": "0.2.0" + } + }, + "repodata_version": 2 +} diff --git a/tests/data/channels/channels/trampoline_2/osx-arm64/dummy-trampoline-0.2.0-h60d57d3_0.conda b/tests/data/channels/channels/trampoline_2/osx-arm64/dummy-trampoline-0.2.0-h60d57d3_0.conda new file mode 100644 index 000000000..c6f7c6e07 Binary files /dev/null and b/tests/data/channels/channels/trampoline_2/osx-arm64/dummy-trampoline-0.2.0-h60d57d3_0.conda differ diff --git a/tests/data/channels/channels/trampoline_2/osx-arm64/repodata.json b/tests/data/channels/channels/trampoline_2/osx-arm64/repodata.json new file mode 100644 index 000000000..18f124c37 --- /dev/null +++ b/tests/data/channels/channels/trampoline_2/osx-arm64/repodata.json @@ -0,0 +1,23 @@ +{ + "info": { + "subdir": "osx-arm64" + }, + "packages": {}, + "packages.conda": { + "dummy-trampoline-0.2.0-h60d57d3_0.conda": { + "arch": "arm64", + "build": "h60d57d3_0", + "build_number": 0, + "depends": [], + "md5": "33dff0375ebac00e35f37e8140a7805f", + "name": "dummy-trampoline", + "platform": "osx", + "sha256": "897ae0414a05ac9bbccf6b640ec60049f209a6f54158ab4b8700e70909be9f14", + "size": 1629, + "subdir": "osx-arm64", + "timestamp": 1730898133762, + "version": "0.2.0" + } + }, + "repodata_version": 2 +} diff --git a/tests/data/channels/channels/trampoline_2/win-64/dummy-trampoline-0.2.0-h9490d1a_0.conda b/tests/data/channels/channels/trampoline_2/win-64/dummy-trampoline-0.2.0-h9490d1a_0.conda new file mode 100644 index 000000000..4e8abc84b Binary files /dev/null and b/tests/data/channels/channels/trampoline_2/win-64/dummy-trampoline-0.2.0-h9490d1a_0.conda differ diff --git a/tests/data/channels/channels/trampoline_2/win-64/repodata.json b/tests/data/channels/channels/trampoline_2/win-64/repodata.json new file mode 100644 index 000000000..1158f6d52 --- /dev/null +++ b/tests/data/channels/channels/trampoline_2/win-64/repodata.json @@ -0,0 +1,23 @@ +{ + "info": { + "subdir": "win-64" + }, + "packages": {}, + "packages.conda": { + "dummy-trampoline-0.2.0-h9490d1a_0.conda": { + "arch": "x86_64", + "build": "h9490d1a_0", + "build_number": 0, + "depends": [], + "md5": "ce4e3a397793d4ddcc34398f4593b10d", + "name": "dummy-trampoline", + "platform": "win", + "sha256": "058f5d3c112b76ee73a4dbefb7bbcb977c088be84545ac7f4a2cd386a237051f", + "size": 1610, + "subdir": "win-64", + "timestamp": 1730898133667, + "version": "0.2.0" + } + }, + "repodata_version": 2 +} diff --git a/tests/data/channels/mappings.toml b/tests/data/channels/mappings.toml index 734f0a9d4..6912706be 100644 --- a/tests/data/channels/mappings.toml +++ b/tests/data/channels/mappings.toml @@ -1,7 +1,7 @@ "dummy_channel_1.yaml" = "dummy_channel_1" "dummy_channel_2.yaml" = "dummy_channel_2" -"global_update_channel_1_010.yaml" = "global_update_channel_1" -"global_update_channel_1_020.yaml" = "global_update_channel_1" +"multiple_versions_channel_1_010.yaml" = "multiple_versions_channel_1" +"multiple_versions_channel_1_020.yaml" = "multiple_versions_channel_1" "non_self_expose_channel_1.yaml" = "non_self_expose_channel_1" "non_self_expose_channel_2.yaml" = "non_self_expose_channel_2" "trampoline/trampoline_1.yaml" = "trampoline_1" diff --git a/tests/data/channels/recipes/global_update_channel_1_010.yaml b/tests/data/channels/recipes/multiple_versions_channel_1_010.yaml similarity index 95% rename from tests/data/channels/recipes/global_update_channel_1_010.yaml rename to tests/data/channels/recipes/multiple_versions_channel_1_010.yaml index df78a0a0e..d1abfb773 100644 --- a/tests/data/channels/recipes/global_update_channel_1_010.yaml +++ b/tests/data/channels/recipes/multiple_versions_channel_1_010.yaml @@ -1,5 +1,5 @@ recipe: - name: global-update-channel + name: multiple-versions-channel version: 1.0.0 context: @@ -10,6 +10,7 @@ outputs: version: ${{ version }} build: + number: 0 script: - mkdir -p $PREFIX/bin # Expose two binaries, with and without version number diff --git a/tests/data/channels/recipes/global_update_channel_1_020.yaml b/tests/data/channels/recipes/multiple_versions_channel_1_020.yaml similarity index 96% rename from tests/data/channels/recipes/global_update_channel_1_020.yaml rename to tests/data/channels/recipes/multiple_versions_channel_1_020.yaml index 090dddb19..6238927d1 100644 --- a/tests/data/channels/recipes/global_update_channel_1_020.yaml +++ b/tests/data/channels/recipes/multiple_versions_channel_1_020.yaml @@ -1,5 +1,5 @@ recipe: - name: global-update-channel + name: multiple-versions-channel version: 1.0.0 context: diff --git a/tests/data/channels/recipes/trampoline/check_env_1.bat b/tests/data/channels/recipes/trampoline/check_env_1.bat new file mode 100644 index 000000000..79a01e567 --- /dev/null +++ b/tests/data/channels/recipes/trampoline/check_env_1.bat @@ -0,0 +1,26 @@ +@echo off +setlocal + +:: Name of the environment variable to check +set "ENV_VAR_NAME=TRAMPOLINE_TEST_ENV" + +:: Expected value +set "EXPECTED_VALUE=teapot" + +:: Get the value of the environment variable +set "ACTUAL_VALUE=%TRAMPOLINE_TEST_ENV%" + +if "%ACTUAL_VALUE%"=="" ( + echo Error: Environment variable '%ENV_VAR_NAME%' is not set. + exit /b 1 +) + +:: Assert that the value matches the expected value +if "%ACTUAL_VALUE%"=="%EXPECTED_VALUE%" ( + echo Success: '%ENV_VAR_NAME%' is set to the expected value. +) else ( + echo Error: '%ENV_VAR_NAME%' is set to '%ACTUAL_VALUE%', but expected '%EXPECTED_VALUE%'. + exit /b 1 +) + +exit /b 0 diff --git a/tests/data/channels/recipes/trampoline/check_env_1.sh b/tests/data/channels/recipes/trampoline/check_env_1.sh new file mode 100644 index 000000000..bfb5cbd8d --- /dev/null +++ b/tests/data/channels/recipes/trampoline/check_env_1.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Name of the environment variable to check +ENV_VAR_NAME="TRAMPOLINE_TEST_ENV" + +# Expected value +EXPECTED_VALUE="teapot" + +# Get the value of the environment variable +ACTUAL_VALUE=$(printenv "$ENV_VAR_NAME") + +# Check if the environment variable is set +if [ -z "$ACTUAL_VALUE" ]; then + echo "Error: Environment variable '$ENV_VAR_NAME' is not set." + exit 1 +fi + +# Assert that the value matches the expected value +if [ "$ACTUAL_VALUE" == "$EXPECTED_VALUE" ]; then + echo "Success: '$ENV_VAR_NAME' is set to the expected value." +else + echo "Error: '$ENV_VAR_NAME' is set to '$ACTUAL_VALUE', but expected '$EXPECTED_VALUE'." + exit 1 +fi diff --git a/tests/data/channels/recipes/trampoline/check_env_2.bat b/tests/data/channels/recipes/trampoline/check_env_2.bat new file mode 100644 index 000000000..2ff0c1c65 --- /dev/null +++ b/tests/data/channels/recipes/trampoline/check_env_2.bat @@ -0,0 +1,26 @@ +@echo off +setlocal + +:: Name of the environment variable to check +set "ENV_VAR_NAME=TRAMPOLINE_V2_TEST_ENV" + +:: Expected value +set "EXPECTED_VALUE=teapot_v2" + +:: Get the value of the environment variable +set ACTUAL_VALUE=%TRAMPOLINE_V2_TEST_ENV% + +if "%ACTUAL_VALUE%"=="" ( + echo Error: Environment variable '%TRAMPOLINE_V2_TEST_ENV%' is not set. + exit /b 1 +) + +:: Assert that the value matches the expected value +if "%ACTUAL_VALUE%"=="%EXPECTED_VALUE%" ( + echo Success: '%TRAMPOLINE_V2_TEST_ENV%' is set to the expected value. +) else ( + echo Error: '%ENV_VAR_NAME%' is set to '%ACTUAL_VALUE%', but expected '%EXPECTED_VALUE%'. + exit /b 1 +) + +exit /b 0 diff --git a/tests/data/channels/recipes/trampoline/check_env_2.sh b/tests/data/channels/recipes/trampoline/check_env_2.sh new file mode 100644 index 000000000..47dca313f --- /dev/null +++ b/tests/data/channels/recipes/trampoline/check_env_2.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Name of the environment variable to check +ENV_VAR_NAME="TRAMPOLINE_V2_TEST_ENV" + +# Expected value +EXPECTED_VALUE="teapot_v2" + +# Get the value of the environment variable +ACTUAL_VALUE=$(printenv "$ENV_VAR_NAME") + +# Check if the environment variable is set +if [ -z "$ACTUAL_VALUE" ]; then + echo "Error: Environment variable '$ENV_VAR_NAME' is not set." + exit 1 +fi + +# Assert that the value matches the expected value +if [ "$ACTUAL_VALUE" == "$EXPECTED_VALUE" ]; then + echo "Success: '$ENV_VAR_NAME' is set to the expected value." +else + echo "Error: '$ENV_VAR_NAME' is set to '$ACTUAL_VALUE', but expected '$EXPECTED_VALUE'." + exit 1 +fi diff --git a/tests/data/channels/recipes/trampoline/trampoline_1.yaml b/tests/data/channels/recipes/trampoline/trampoline_1.yaml new file mode 100644 index 000000000..97516ad8b --- /dev/null +++ b/tests/data/channels/recipes/trampoline/trampoline_1.yaml @@ -0,0 +1,24 @@ +recipe: + name: trampoline + version: 1.0.0 + +outputs: + - package: + name: dummy-trampoline + version: 0.1.0 + source: + path: . + + build: + script: + - mkdir -p $PREFIX/bin + - if: win + then: + - mkdir -p $PREFIX/etc/conda/activate.d + - echo "set TRAMPOLINE_TEST_ENV=teapot" > $PREFIX/etc/conda/activate.d/activate-trampoline.bat + - mv check_env_1.bat $PREFIX/bin/dummy-trampoline.bat + else: + - mkdir -p $PREFIX/etc/conda/activate.d + - echo "export TRAMPOLINE_TEST_ENV='teapot'" > $PREFIX/etc/conda/activate.d/activate-trampoline.sh + - mv check_env_1.sh $PREFIX/bin/dummy-trampoline + - chmod +x $PREFIX/bin/dummy-trampoline diff --git a/tests/data/channels/recipes/trampoline/trampoline_2.yaml b/tests/data/channels/recipes/trampoline/trampoline_2.yaml new file mode 100644 index 000000000..7d1beef5a --- /dev/null +++ b/tests/data/channels/recipes/trampoline/trampoline_2.yaml @@ -0,0 +1,24 @@ +recipe: + name: trampoline + version: 1.0.0 + +outputs: + - package: + name: dummy-trampoline + version: 0.2.0 + source: + path: . + + build: + script: + - mkdir -p $PREFIX/bin + - if: win + then: + - mkdir -p $PREFIX/etc/conda/activate.d + - echo "set TRAMPOLINE_V2_TEST_ENV=teapot_v2" > $PREFIX/etc/conda/activate.d/activate-trampoline.bat + - mv check_env_2.bat $PREFIX/bin/dummy-trampoline.bat + else: + - mkdir -p $PREFIX/etc/conda/activate.d + - echo "export TRAMPOLINE_V2_TEST_ENV='teapot_v2'" > $PREFIX/etc/conda/activate.d/activate-trampoline.sh + - mv check_env_2.sh $PREFIX/bin/dummy-trampoline + - chmod +x $PREFIX/bin/dummy-trampoline diff --git a/tests/integration_python/common.py b/tests/integration_python/common.py index 19fdc83c0..ed127a04e 100644 --- a/tests/integration_python/common.py +++ b/tests/integration_python/common.py @@ -4,7 +4,7 @@ import subprocess import os -PIXI_VERSION = "0.34.0" +PIXI_VERSION = "0.35.0" class ExitCode(IntEnum): @@ -101,3 +101,11 @@ def is_binary(path: Path) -> bool: textchars = bytearray({7, 8, 9, 10, 12, 13, 27} | set(range(0x20, 0x100)) - {0x7F}) with open(path, "rb") as f: return bool(f.read(2048).translate(None, textchars)) + + +def pixi_dir(project_root: Path) -> Path: + return project_root.joinpath(".pixi") + + +def default_env_path(project_root: Path) -> Path: + return pixi_dir(project_root).joinpath("envs", "default") diff --git a/tests/integration_python/conftest.py b/tests/integration_python/conftest.py index aac4807fc..d897b4eba 100644 --- a/tests/integration_python/conftest.py +++ b/tests/integration_python/conftest.py @@ -34,8 +34,8 @@ def dummy_channel_2(channels: Path) -> str: @pytest.fixture -def global_update_channel_1(channels: Path) -> str: - return channels.joinpath("global_update_channel_1").as_uri() +def multiple_versions_channel_1(channels: Path) -> str: + return channels.joinpath("multiple_versions_channel_1").as_uri() @pytest.fixture diff --git a/tests/integration_python/global/__init__.py b/tests/integration_python/global/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/integration_python/global/conftest.py b/tests/integration_python/global/conftest.py new file mode 100644 index 000000000..315d473f5 --- /dev/null +++ b/tests/integration_python/global/conftest.py @@ -0,0 +1,12 @@ +import pytest +from pathlib import Path + + +@pytest.fixture +def trampoline_channel_1(channels: Path) -> str: + return channels.joinpath("trampoline_1").as_uri() + + +@pytest.fixture +def trampoline_channel_2(channels: Path) -> str: + return channels.joinpath("trampoline_2").as_uri() diff --git a/tests/integration_python/test_global.py b/tests/integration_python/global/test_global.py similarity index 97% rename from tests/integration_python/test_global.py rename to tests/integration_python/global/test_global.py index f3ea24d68..b6ccd8605 100644 --- a/tests/integration_python/test_global.py +++ b/tests/integration_python/global/test_global.py @@ -3,7 +3,7 @@ import pytest import tomli_w -from .common import verify_cli_command, ExitCode, exec_extension, bat_extension +from ..common import verify_cli_command, ExitCode, exec_extension, bat_extension import platform import os import stat @@ -1094,14 +1094,14 @@ def test_install_multi_env_install(pixi: Path, tmp_path: Path, dummy_channel_1: @pytest.mark.skipif(platform.system() == "Windows", reason="Not reliable on Windows") -def test_pixi_install_cleanup(pixi: Path, tmp_path: Path, global_update_channel_1: str) -> None: +def test_pixi_install_cleanup(pixi: Path, tmp_path: Path, multiple_versions_channel_1: str) -> None: env = {"PIXI_HOME": str(tmp_path)} package0_1_0 = tmp_path / "bin" / exec_extension("package0.1.0") package0_2_0 = tmp_path / "bin" / exec_extension("package0.2.0") verify_cli_command( - [pixi, "global", "install", "--channel", global_update_channel_1, "package==0.1.0"], + [pixi, "global", "install", "--channel", multiple_versions_channel_1, "package==0.1.0"], env=env, ) assert package0_1_0.is_file() @@ -1110,7 +1110,7 @@ def test_pixi_install_cleanup(pixi: Path, tmp_path: Path, global_update_channel_ # Install the same package but with a different version # The old version should be removed and the new version should be installed without error. verify_cli_command( - [pixi, "global", "install", "--channel", global_update_channel_1, "package==0.2.0"], + [pixi, "global", "install", "--channel", multiple_versions_channel_1, "package==0.2.0"], env=env, ) @@ -1339,7 +1339,7 @@ def test_uninstall_only_reverts_failing(pixi: Path, tmp_path: Path, dummy_channe def test_global_update_single_package( - pixi: Path, tmp_path: Path, global_update_channel_1: str + pixi: Path, tmp_path: Path, multiple_versions_channel_1: str ) -> None: env = {"PIXI_HOME": str(tmp_path)} # Test update with no environments @@ -1350,7 +1350,7 @@ def test_global_update_single_package( # Test update of a single package verify_cli_command( - [pixi, "global", "install", "--channel", global_update_channel_1, "package 0.1.0"], + [pixi, "global", "install", "--channel", multiple_versions_channel_1, "package 0.1.0"], env=env, ) # Replace the version with a "*" @@ -1397,7 +1397,7 @@ def test_global_update_single_package_with_transient_dependency( def test_global_update_all_packages( - pixi: Path, tmp_path: Path, global_update_channel_1: str + pixi: Path, tmp_path: Path, multiple_versions_channel_1: str ) -> None: env = {"PIXI_HOME": str(tmp_path)} @@ -1407,7 +1407,7 @@ def test_global_update_all_packages( "global", "install", "--channel", - global_update_channel_1, + multiple_versions_channel_1, "package2==0.1.0", "package==0.1.0", ], @@ -1450,7 +1450,7 @@ def test_global_update_all_packages( def test_global_update_multiple_packages_in_one_env( - pixi: Path, tmp_path: Path, global_update_channel_1: str + pixi: Path, tmp_path: Path, multiple_versions_channel_1: str ) -> None: env = {"PIXI_HOME": str(tmp_path)} @@ -1460,7 +1460,7 @@ def test_global_update_multiple_packages_in_one_env( "global", "install", "--channel", - global_update_channel_1, + multiple_versions_channel_1, "--environment", "my-packages", "package2==0.1.0", @@ -1506,14 +1506,14 @@ def test_global_update_multiple_packages_in_one_env( assert "0.2.0" in bin_file_package2.read_text() -def test_pixi_update_cleanup(pixi: Path, tmp_path: Path, global_update_channel_1: str) -> None: +def test_pixi_update_cleanup(pixi: Path, tmp_path: Path, multiple_versions_channel_1: str) -> None: env = {"PIXI_HOME": str(tmp_path)} package0_1_0 = tmp_path / "bin" / exec_extension("package0.1.0") package0_2_0 = tmp_path / "bin" / exec_extension("package0.2.0") verify_cli_command( - [pixi, "global", "install", "--channel", global_update_channel_1, "package==0.1.0"], + [pixi, "global", "install", "--channel", multiple_versions_channel_1, "package==0.1.0"], env=env, ) assert package0_1_0.is_file() @@ -1543,7 +1543,7 @@ def test_pixi_update_cleanup(pixi: Path, tmp_path: Path, global_update_channel_1 def test_pixi_update_subset_expose( - pixi: Path, tmp_path: Path, global_update_channel_1: str + pixi: Path, tmp_path: Path, multiple_versions_channel_1: str ) -> None: env = {"PIXI_HOME": str(tmp_path)} @@ -1551,7 +1551,7 @@ def test_pixi_update_subset_expose( package0_2_0 = tmp_path / "bin" / exec_extension("package0.2.0") verify_cli_command( - [pixi, "global", "install", "--channel", global_update_channel_1, "package==0.1.0"], + [pixi, "global", "install", "--channel", multiple_versions_channel_1, "package==0.1.0"], env=env, ) assert package0_1_0.is_file() diff --git a/tests/integration_python/global/test_trampoline.py b/tests/integration_python/global/test_trampoline.py new file mode 100644 index 000000000..b46af7b4f --- /dev/null +++ b/tests/integration_python/global/test_trampoline.py @@ -0,0 +1,137 @@ +import json +import pathlib +from pathlib import Path +import platform + +from ..common import verify_cli_command, exec_extension, is_binary + + +def test_trampoline_respect_activation_variables( + pixi: Path, tmp_path: Path, trampoline_channel_1: str +) -> None: + env = {"PIXI_HOME": str(tmp_path)} + + dummy_b = tmp_path / "bin" / exec_extension("dummy-trampoline") + + verify_cli_command( + [ + pixi, + "global", + "install", + "--channel", + trampoline_channel_1, + "dummy-trampoline", + ], + env=env, + ) + + assert is_binary(dummy_b) + + dummy_b_json = tmp_path / "bin" / "trampoline_configuration" / "dummy-trampoline.json" + + trampoline_metadata = json.loads(dummy_b_json.read_text()) + + # get envs of the trampoline + trampoline_env = trampoline_metadata["env"] + assert trampoline_env["TRAMPOLINE_TEST_ENV"] == "teapot" + assert "CONDA_PREFIX" in trampoline_env + assert "PATH" in trampoline_env + + # verify that exe and root folder is correctly set to the original one + original_dummy_b = tmp_path / "envs" / "dummy-trampoline" / "bin" / "dummy-trampoline" + if platform.system() == "Windows": + original_dummy_b = original_dummy_b.with_suffix(".bat") + assert pathlib.Path(trampoline_metadata["exe"]) == pathlib.Path(original_dummy_b) + assert trampoline_metadata["path"] == str(original_dummy_b.parent) + + # now execute the binary + verify_cli_command([dummy_b], stdout_contains="Success:") + + +def test_trampoline_new_activation_scripts( + pixi: Path, tmp_path: Path, trampoline_channel_1: str, trampoline_channel_2: str +) -> None: + env = {"PIXI_HOME": str(tmp_path)} + + dummy_b = tmp_path / "bin" / exec_extension("dummy-trampoline") + + verify_cli_command( + [ + pixi, + "global", + "install", + "--channel", + trampoline_channel_1, + "dummy-trampoline==0.1.0", + ], + env=env, + ) + + assert is_binary(dummy_b) + + dummy_b_json = tmp_path / "bin" / "trampoline_configuration" / "dummy-trampoline.json" + + trampoline_metadata = json.loads(dummy_b_json.read_text()) + + # get envs of the trampoline + assert trampoline_metadata["env"]["TRAMPOLINE_TEST_ENV"] == "teapot" + + # now install newever version that have different activation scripts + + # Replace the version with a "*" + manifest = tmp_path.joinpath("manifests", "pixi-global.toml") + manifest.write_text(manifest.read_text().replace("trampoline_1", "trampoline_2")) + + verify_cli_command( + [ + pixi, + "global", + "install", + "--force-reinstall", + "dummy-trampoline==0.2.0", + ], + env=env, + ) + + # verify that newever activation is recorded + dummy_b_json = tmp_path / "bin" / "trampoline_configuration" / "dummy-trampoline.json" + + trampoline_metadata = json.loads(dummy_b_json.read_text()) + + # get envs of the trampoline + assert trampoline_metadata["env"]["TRAMPOLINE_V2_TEST_ENV"] == "teapot_v2" + # verify that older env is not present + assert "TRAMPOLINE_TEST_ENV" not in trampoline_metadata["env"] + + # now execute the binary + verify_cli_command([dummy_b], stdout_contains="Success:") + + +def test_trampoline_migrate_previous_script( + pixi: Path, tmp_path: Path, trampoline_channel_1: str +) -> None: + # this test will validate if new trampoline will migrate the previous way of running packages (using scripts) + env = {"PIXI_HOME": str(tmp_path)} + + # create a dummy script that will act as already installed package + dummy_trampoline = tmp_path / "bin" / exec_extension("dummy-trampoline") + + # now run install again, this time it should migrate the script to the new trampoline + verify_cli_command( + [ + pixi, + "global", + "install", + "--channel", + trampoline_channel_1, + "dummy-trampoline", + ], + env=env, + ) + + assert dummy_trampoline.is_file() + assert is_binary(dummy_trampoline) + + dummy_trampoline_json = tmp_path / "bin" / "trampoline_configuration" / "dummy-trampoline.json" + + assert dummy_trampoline_json.is_file() diff --git a/tests/integration_python/test_main_cli.py b/tests/integration_python/test_main_cli.py index 36385231d..2fa34d978 100644 --- a/tests/integration_python/test_main_cli.py +++ b/tests/integration_python/test_main_cli.py @@ -1,5 +1,8 @@ +import os from pathlib import Path from .common import verify_cli_command, ExitCode, PIXI_VERSION +import tomllib +import json import pytest @@ -290,3 +293,215 @@ def test_pixi_init_pyproject(pixi: Path, tmp_path: Path) -> None: verify_cli_command([pixi, "init", tmp_path, "--format", "pyproject"], ExitCode.SUCCESS) # Verify that install works verify_cli_command([pixi, "install", "--manifest-path", manifest_path], ExitCode.SUCCESS) + + +def test_upgrade_package_does_not_exist( + pixi: Path, tmp_path: Path, multiple_versions_channel_1: str +) -> None: + manifest_path = tmp_path / "pixi.toml" + + # Create a new project + verify_cli_command([pixi, "init", "--channel", multiple_versions_channel_1, tmp_path]) + + # Add package + verify_cli_command([pixi, "add", "--manifest-path", manifest_path, "package"]) + + # Similar package names that don't exist should get suggestions + verify_cli_command( + [pixi, "upgrade", "--manifest-path", manifest_path, "package_similar_name"], + ExitCode.FAILURE, + stderr_contains=[ + "could not find a package named 'package_similar_name'", + "did you mean 'package'", + ], + ) + + verify_cli_command( + [pixi, "upgrade", "--manifest-path", manifest_path, "different_name"], + ExitCode.FAILURE, + stderr_contains="could not find a package named 'different_name'", + stderr_excludes="did you mean 'package'", + ) + + +def test_upgrade_conda_package( + pixi: Path, tmp_path: Path, multiple_versions_channel_1: str +) -> None: + manifest_path = tmp_path / "pixi.toml" + + # Create a new project + verify_cli_command([pixi, "init", "--channel", multiple_versions_channel_1, tmp_path]) + + # Add package pinned to version 0.1.0 + verify_cli_command( + [ + pixi, + "add", + "--manifest-path", + manifest_path, + f"package==0.1.0[channel={multiple_versions_channel_1},build_number=0]", + ] + ) + parsed_manifest = tomllib.loads(manifest_path.read_text()) + package = parsed_manifest["dependencies"]["package"] + assert package["version"] == "==0.1.0" + assert package["channel"] == multiple_versions_channel_1 + assert package["build-number"] == "==0" + + # Upgrade package, it should now be at 0.2.0, with semver ranges + # The channel should still be specified + verify_cli_command( + [pixi, "upgrade", "--manifest-path", manifest_path, "package"], + stderr_contains=["package", "0.1.0", "0.2.0"], + ) + parsed_manifest = tomllib.loads(manifest_path.read_text()) + package = parsed_manifest["dependencies"]["package"] + assert package["version"] == ">=0.2.0,<0.3" + assert package["channel"] == multiple_versions_channel_1 + assert "build-number" not in package + + +def test_upgrade_exclude(pixi: Path, tmp_path: Path, multiple_versions_channel_1: str) -> None: + manifest_path = tmp_path / "pixi.toml" + + # Create a new project + verify_cli_command([pixi, "init", "--channel", multiple_versions_channel_1, tmp_path]) + + # Add package pinned to version 0.1.0 + verify_cli_command( + [pixi, "add", "--manifest-path", manifest_path, "package==0.1.0", "package2==0.1.0"] + ) + parsed_manifest = tomllib.loads(manifest_path.read_text()) + assert parsed_manifest["dependencies"]["package"] == "==0.1.0" + assert parsed_manifest["dependencies"]["package2"] == "==0.1.0" + + # Upgrade package, it should now be at 0.2.0, with semver ranges + # package2, should still be at 0.1.0, since we excluded it + verify_cli_command( + [pixi, "upgrade", "--manifest-path", manifest_path, "--exclude", "package2"], + stderr_contains=["package", "0.1.0", "0.2.0"], + stderr_excludes="package2", + ) + parsed_manifest = tomllib.loads(manifest_path.read_text()) + assert parsed_manifest["dependencies"]["package"] == ">=0.2.0,<0.3" + assert parsed_manifest["dependencies"]["package2"] == "==0.1.0" + + +def test_upgrade_json_output(pixi: Path, tmp_path: Path, multiple_versions_channel_1: str) -> None: + manifest_path = tmp_path / "pixi.toml" + + # Create a new project + verify_cli_command([pixi, "init", "--channel", multiple_versions_channel_1, tmp_path]) + + # Add package pinned to version 0.1.0 + verify_cli_command( + [pixi, "add", "--manifest-path", manifest_path, "package==0.1.0", "package2==0.1.0"] + ) + parsed_manifest = tomllib.loads(manifest_path.read_text()) + assert parsed_manifest["dependencies"]["package"] == "==0.1.0" + assert parsed_manifest["dependencies"]["package2"] == "==0.1.0" + + # Check if json output is correct and readable + result = verify_cli_command( + [pixi, "upgrade", "--manifest-path", manifest_path, "--json"], + stdout_contains=["package", "package2", "0.1.0", "0.2.0", 'version": ', "before", "after"], + ) + + data = json.loads(result.stdout) + assert data["environment"]["default"] + + +def test_upgrade_dryrun(pixi: Path, tmp_path: Path, multiple_versions_channel_1: str) -> None: + manifest_path = tmp_path / "pixi.toml" + lock_file_path = tmp_path / "pixi.lock" + # Create a new project + verify_cli_command([pixi, "init", "--channel", multiple_versions_channel_1, tmp_path]) + + # Add package pinned to version 0.1.0 + verify_cli_command( + [pixi, "add", "--manifest-path", manifest_path, "package==0.1.0", "package2==0.1.0"] + ) + + manifest_content = manifest_path.read_text() + lock_file_content = lock_file_path.read_text() + # Rename .pixi folder, no remove to avoid remove logic. + os.renames(tmp_path / ".pixi", tmp_path / ".pixi_backup") + + parsed_manifest = tomllib.loads(manifest_path.read_text()) + assert parsed_manifest["dependencies"]["package"] == "==0.1.0" + assert parsed_manifest["dependencies"]["package2"] == "==0.1.0" + + verify_cli_command( + [pixi, "upgrade", "--manifest-path", manifest_path, "--dry-run"], + stderr_contains=["package", "0.1.0", "0.2.0"], + ) + + # Verify the manifest, lock file and .pixi folder are not modified + assert manifest_path.read_text() == manifest_content + assert lock_file_path.read_text() == lock_file_content + assert not os.path.exists(tmp_path / ".pixi") + + +@pytest.mark.slow +def test_upgrade_pypi_package(pixi: Path, tmp_path: Path) -> None: + manifest_path = tmp_path / "pixi.toml" + + # Create a new project + verify_cli_command([pixi, "init", tmp_path]) + + # Add python + verify_cli_command([pixi, "add", "--manifest-path", manifest_path, "python=3.13"]) + + # Add httpx pinned to version 0.26.0 + verify_cli_command( + [ + pixi, + "add", + "--manifest-path", + manifest_path, + "--pypi", + "httpx[cli]==0.26.0", + ] + ) + parsed_manifest = tomllib.loads(manifest_path.read_text()) + assert parsed_manifest["pypi-dependencies"]["httpx"]["version"] == "==0.26.0" + assert parsed_manifest["pypi-dependencies"]["httpx"]["extras"] == ["cli"] + + # Upgrade httpx, it should now be upgraded + # Extras should be preserved + verify_cli_command( + [pixi, "upgrade", "--manifest-path", manifest_path, "httpx"], + stderr_contains=["httpx", "0.26.0"], + ) + parsed_manifest = tomllib.loads(manifest_path.read_text()) + assert parsed_manifest["pypi-dependencies"]["httpx"]["version"] != "==0.26.0" + assert parsed_manifest["pypi-dependencies"]["httpx"]["extras"] == ["cli"] + + +@pytest.mark.slow +def test_upgrade_pypi_and_conda_package(pixi: Path, tmp_path: Path) -> None: + manifest_path = tmp_path / "pyproject.toml" + + # Create a new project + verify_cli_command([pixi, "init", "--format", "pyproject", tmp_path]) + + # Add pinned numpy as conda and pypi dependency + verify_cli_command([pixi, "add", "--manifest-path", manifest_path, "numpy==1.*"]) + verify_cli_command([pixi, "add", "--manifest-path", manifest_path, "--pypi", "numpy==1.*"]) + + parsed_manifest = tomllib.loads(manifest_path.read_text()) + numpy_pypi = parsed_manifest["project"]["dependencies"][0] + assert numpy_pypi == "numpy==1.*" + numpy_conda = parsed_manifest["tool"]["pixi"]["dependencies"]["numpy"] + assert numpy_conda == "1.*" + + # Upgrade numpy, both conda and pypi should be upgraded + verify_cli_command( + [pixi, "upgrade", "--manifest-path", manifest_path, "numpy"], + stderr_contains=["numpy", "1."], + ) + parsed_manifest = tomllib.loads(manifest_path.read_text()) + numpy_pypi = parsed_manifest["project"]["dependencies"][0] + assert "1.*" not in numpy_pypi + numpy_conda = parsed_manifest["tool"]["pixi"]["dependencies"]["numpy"] + assert numpy_conda != "1.*" diff --git a/tests/integration_python/test_run_cli.py b/tests/integration_python/test_run_cli.py index b26abe7e4..7fbc3d848 100644 --- a/tests/integration_python/test_run_cli.py +++ b/tests/integration_python/test_run_cli.py @@ -1,5 +1,5 @@ from pathlib import Path -from .common import verify_cli_command, ExitCode +from .common import verify_cli_command, ExitCode, default_env_path ALL_PLATFORMS = '["linux-64", "osx-64", "win-64", "linux-ppc64le", "linux-aarch64"]' @@ -57,3 +57,99 @@ def test_run_in_shell(pixi: Path, tmp_path: Path) -> None: stdout_contains=["a", "a1"], env=env, ) + + +def test_using_prefix_validation(pixi: Path, tmp_path: Path, dummy_channel_1: str) -> None: + manifest = tmp_path.joinpath("pixi.toml") + toml = f""" + [project] + name = "test" + channels = ["{dummy_channel_1}"] + platforms = ["linux-64", "osx-64", "osx-arm64", "win-64"] + + [dependencies] + dummy-a = "*" + """ + manifest.write_text(toml) + + # Run the install + verify_cli_command( + [pixi, "install", "--manifest-path", manifest], + ) + + # Validate creation of the pixi file with the hash + pixi_file = default_env_path(tmp_path).joinpath("conda-meta").joinpath("pixi") + assert pixi_file.exists() + assert "environment_lock_file_hash" in pixi_file.read_text() + + # Break environment on purpose + dummy_a_meta_files = default_env_path(tmp_path).joinpath("conda-meta").glob("dummy-a*.json") + + for file in dummy_a_meta_files: + path = Path(file) + if path.exists(): + path.unlink() # Removes the file + + # Run simple script, which shouldn't reinstall + verify_cli_command( + [pixi, "run", "--manifest-path", manifest, "echo", "hello"], + stdout_contains="hello", + ) + + # Validate that the dummy-a files still don't exist + for file in dummy_a_meta_files: + assert not Path(file).exists() + + # Run an actual re-install + verify_cli_command( + [pixi, "install", "--manifest-path", manifest], + ) + + # Validate the files are back + for file in dummy_a_meta_files: + # All dummy-a files should be back as `install` will ignore the hash + assert Path(file).exists() + + +def test_prefix_revalidation(pixi: Path, tmp_path: Path, dummy_channel_1: str) -> None: + manifest = tmp_path.joinpath("pixi.toml") + toml = f""" + [project] + name = "test" + channels = ["{dummy_channel_1}"] + platforms = ["linux-64", "osx-64", "osx-arm64", "win-64"] + + [dependencies] + dummy-a = "*" + """ + manifest.write_text(toml) + + # Run the installation + verify_cli_command( + [pixi, "install", "--manifest-path", manifest], + ExitCode.SUCCESS, + ) + + # Validate creation of the pixi file with the hash + pixi_file = default_env_path(tmp_path).joinpath("conda-meta").joinpath("pixi") + assert pixi_file.exists() + assert "environment_lock_file_hash" in pixi_file.read_text() + + # Break environment on purpose + dummy_a_meta_files = default_env_path(tmp_path).joinpath("conda-meta").glob("dummy-a*.json") + + for file in dummy_a_meta_files: + path = Path(file) + if path.exists(): + path.unlink() # Removes the file + + # Run with revalidation to force reinstallation + verify_cli_command( + [pixi, "run", "--manifest-path", manifest, "--revalidate", "echo", "hello"], + ExitCode.SUCCESS, + stdout_contains="hello", + ) + + # Validate that the dummy-a files are reinstalled + for file in dummy_a_meta_files: + assert Path(file).exists() diff --git a/tests/integration_rust/common/builders.rs b/tests/integration_rust/common/builders.rs index 2e551c10a..4bdef2660 100644 --- a/tests/integration_rust/common/builders.rs +++ b/tests/integration_rust/common/builders.rs @@ -37,7 +37,7 @@ use pixi::{ task::TaskName, DependencyType, }; -use pixi_manifest::{EnvironmentName, SpecType}; +use pixi_manifest::{EnvironmentName, FeatureName, SpecType}; use rattler_conda_types::{NamedChannelOrUrl, Platform}; use url::Url; @@ -138,8 +138,8 @@ pub trait HasDependencyConfig: Sized { host: false, build: false, pypi: false, - platform: Default::default(), - feature: None, + platforms: Default::default(), + feature: Default::default(), } } @@ -175,7 +175,7 @@ pub trait HasDependencyConfig: Sized { } fn set_platforms(mut self, platforms: &[Platform]) -> Self { - self.dependency_config().platform.extend(platforms.iter()); + self.dependency_config().platforms.extend(platforms.iter()); self } } @@ -193,7 +193,7 @@ impl AddBuilder { } pub fn with_feature(mut self, feature: impl ToString) -> Self { - self.args.dependency_config.feature = Some(feature.to_string()); + self.args.dependency_config.feature = FeatureName::Named(feature.to_string()); self } } diff --git a/tests/integration_rust/common/mod.rs b/tests/integration_rust/common/mod.rs index bcf635412..54f60842d 100644 --- a/tests/integration_rust/common/mod.rs +++ b/tests/integration_rust/common/mod.rs @@ -12,6 +12,7 @@ use std::{ use indicatif::ProgressDrawTarget; use miette::{Context, Diagnostic, IntoDiagnostic}; +use pixi::lock_file::UpdateMode; use pixi::{ cli::{ add, @@ -304,6 +305,7 @@ impl PixiControl { no_install: true, lock_file_usage: LockFileUsageArgs::default(), config: Default::default(), + revalidate: false, }, editable: false, }, @@ -323,6 +325,7 @@ impl PixiControl { no_install: true, lock_file_usage: LockFileUsageArgs::default(), config: Default::default(), + revalidate: false, }, }, } @@ -418,7 +421,9 @@ impl PixiControl { // Construct the task environment if not already created. let task_env = match task_env.as_ref() { None => { - lock_file.prefix(&task.run_environment).await?; + lock_file + .prefix(&task.run_environment, UpdateMode::Revalidate) + .await?; let env = get_task_env(&task.run_environment, args.clean_env).await?; task_env.insert(env) } diff --git a/tests/integration_rust/install_tests.rs b/tests/integration_rust/install_tests.rs index d99bb3bb8..9cf48484e 100644 --- a/tests/integration_rust/install_tests.rs +++ b/tests/integration_rust/install_tests.rs @@ -6,6 +6,7 @@ use crate::common::{LockFileExt, PixiControl}; use pixi::cli::cli_config::{PrefixUpdateConfig, ProjectConfig}; use pixi::cli::{run, run::Args, LockFileUsageArgs}; use pixi::environment::LockFileUsage; +use pixi::lock_file::UpdateMode; use pixi::Project; use pixi_config::{Config, DetachedEnvironments}; use pixi_consts::consts; @@ -445,7 +446,6 @@ async fn test_channels_changed() { } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] -#[cfg_attr(not(feature = "slow_integration_tests"), ignore)] async fn install_conda_meta_history() { let pixi = PixiControl::new().unwrap(); pixi.init().await.unwrap(); @@ -569,9 +569,14 @@ async fn test_old_lock_install() { "tests/data/satisfiability/old_lock_file/pyproject.toml", )) .unwrap(); - pixi::environment::update_prefix(&project.default_environment(), LockFileUsage::Update, false) - .await - .unwrap(); + pixi::environment::update_prefix( + &project.default_environment(), + LockFileUsage::Update, + false, + UpdateMode::Revalidate, + ) + .await + .unwrap(); assert_eq!( lock_str, std::fs::read_to_string("tests/data/satisfiability/old_lock_file/pixi.lock").unwrap()