Skip to content

Register base-protection health check for conda doctor --fix#88

Open
jezdez wants to merge 63 commits intomainfrom
fix-base-task
Open

Register base-protection health check for conda doctor --fix#88
jezdez wants to merge 63 commits intomainfrom
fix-base-task

Conversation

@jezdez
Copy link
Member

@jezdez jezdez commented Dec 17, 2025

Registers a base-protection health check that integrates with conda doctor.

Fixes #74.

Requires: conda >= 26.1.0 (health check fixer API from conda/conda#15530)

What it does

  • conda doctor - shows if base environment is protected or not
  • conda doctor --fix - offers to protect your base environment

When fixing, the health check:

  1. Clones your base environment to a new default environment
  2. Resets base to essentials only
  3. Freezes base to prevent accidental modifications
  4. Sets the default activation environment to the new default

Usage

# Check base protection status
conda doctor

# Run fix to protect base environment
conda doctor --fix

# Run only this specific check
conda doctor base-protection
conda doctor base-protection --fix

Changes

  • Registers base-protection health check via conda_health_checks hook
  • Health check reports protection status in conda doctor output
  • Fix capability handles the full protection workflow
  • Updated README with new CLI syntax
  • Bumped minimum conda version to 26.1.0

…elp text

Fix #74.

- Introduced `conda migrate base` for base environment migration.
- Added `--list` option to display available migration tasks.
- Updated documentation and README to reflect new command structure.
- Enhanced tests for new functionality and help commands.
- Remove standalone 'conda migrate' subcommand
- Expose base environment protection via 'conda fix base' using conda's
  new fix task plugin framework
- Rename main_migrate.py to main_fix_base.py and clean up unused code
- Update README to document both 'conda self' and 'conda fix base'
- Update tests to use 'conda fix' instead of 'conda migrate'
- Improve test coverage and type hints

The 'conda self' subcommand for managing base environment packages
remains unchanged. The base protection task is now accessed via
'conda fix base' which uses conda's plugin-based fix task system.
- Update plugin.py: CondaFixTask -> CondaHealthFix
- Update plugin.py: conda_fix_tasks -> conda_health_fixes
- Update main_fix_base.py docstrings
- Update README.md terminology
- Update tests for new UI text and error handling
- Rename news file

This aligns with the conda plugin API rename for better
symmetry with 'health checks' from conda doctor.
The CondaHealthFix type only exists in conda versions with the fix
subcommand. Wrap the import in try/except so conda-self works with
older conda versions (just without the fix base feature).
The base environment protection functionality remains available via
conda self commands. The conda_health_fixes hook has been removed from
conda in favor of the simpler conda doctor --fix approach.
Register a health check with conda doctor that:
- Checks if the base environment is protected (frozen)
- Offers to fix by protecting the base environment

The health check only runs when checking the base environment and
provides verbose output explaining why protection is recommended.

Implementation split: hookimpl in plugin.py, functions in health_check.py
Import health check status marks from conda instead of defining locally.
- Move health_check.py to health_checks/base_protection.py
- Add health_checks/__init__.py with plugins list
- Move @hookimpl decorator into base_protection module
- Update plugin.py to import health_checks for registration
Keep the implementation in health_checks/base_protection.py but
define the hookimpl in plugin.py with a lazy import to avoid
import-time side effects.
- Keep main_fix_base.py as internal implementation for health check fix
- Update README to document using 'conda doctor --fix' for base protection
- Remove protect subcommand registration that was never completed
- Delete main_fix_base.py
- Move all protection workflow logic into health_checks/base_protection.py
- The fix is now self-contained in the health check module
- Fix SUCCESS_MESSAGE to show activation command instead of claiming auto-activation
- Simplify variable names and reduce redundancy
- Shorten warning messages
- Use pathlib.Path for cleaner path handling
- Set id='base-protection' for cleaner CLI usage
- Update README with new --list and --fix id syntax
@danyeaw danyeaw moved this from In Progress 🏗️ to In review 🔍 in conda Roadmap and Sprint Planning Feb 4, 2026
- Change macos-latest to use conda-forge instead of defaults
- Remove macOS platforms from pixi config when testing defaults
  (conda 26.1.0 not available on defaults channel for macOS)
Copy link
Contributor

@soapy1 soapy1 left a comment

Choose a reason for hiding this comment

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

Looking great! Just a few UX type questions.

No need to unlink packages individually from an environment that is
about to be deleted entirely.
Previously the check returned silently, making it unclear whether it
ran at all. Now prints a clear skip message.
These attributes cannot be passed via conda doctor; simplify to
direct constants.
Conda's QuietSpinner still prints "Preparing/Verifying/Executing
transaction: ...working... done" lines. Redirect stdout during
clone_env and reset calls to fully silence output in quiet mode.
Replace print_explicit with Environment.from_prefix() and the YAML
environment exporter plugin, which captures both conda and
pip-installed packages in the snapshot.
@jezdez jezdez requested review from jaimergp and soapy1 February 26, 2026 11:10
conda 26.1.1 has not been released yet; the >=26.1.1 constraint
was unsolvable on conda-forge.
Copy link
Contributor

@soapy1 soapy1 left a comment

Choose a reason for hiding this comment

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

Looks great! I think if this diff is applied then it should make the package builds work. I think it's ok to be depending on the main channel here.

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 6a3bcc1..3c92488 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -53,7 +53,7 @@ jobs:
           environments: ${{ env.PIXI_ENV_NAME }}
 
       - name: Build recipe
-        run: pixi run --environment ${{ env.PIXI_ENV_NAME }} build --channel conda-forge
+        run: pixi run --environment ${{ env.PIXI_ENV_NAME }} build --channel conda-forge --channel main
 
       - name: Upload to conda-canary
         # Only publish canary builds after successful pushes to main in the canonical repo

@jezdez jezdez requested review from marcoesters and soapy1 March 5, 2026 13:57
verbose=False,
quiet=True,
)
reset(uninstallable_packages=uninstallable_packages)
Copy link
Contributor

Choose a reason for hiding this comment

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

This will keep pip-installed packages in the new protected base environment still, doesn't it? This essentially re-implements conda self migrate, but as a conda doctor fix. Should there be some kind of warning if we encounter non-conda packages?

frozen_path.write_text(json.dumps({"message": message}) if message else "")
except OSError as e:
raise CondaOSError(f"Could not protect environment: {e}") from e

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would be nice to add an @EXPLICIT file to enable resetting to the post-migration state. Similar to what was done here: marcoesters@c03fd8f#diff-ab450c1dd96dee7dffda61f8532a2e4b44c1f3b3adbcb5f0fd9c3aae8c76dca5R152-R157

@@ -0,0 +1,148 @@
# Copyright (C) 2012 Anaconda, Inc
Copy link
Contributor

Choose a reason for hiding this comment

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

Are these (seemingly old) copyright notices needed?

Copy link
Contributor

Choose a reason for hiding this comment

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

This is consistent with the copyright in conda. Though it doesn't look like any other file in conda-self includes these.

Comment on lines +176 to +177
# Additional fields only available in conda >= 26.1.0
if hasattr(hc, "fixer"):
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't that always the case since we require conda >= 26.1.0?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extensible migration/fix tasks via conda doctor health checks

3 participants