Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove defaults channel on input channel: nodefaults #364

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

liamhuber
Copy link

Changes conda setup to run conda config --remove channels defaults when nodefaults is found in the channels input instead of adding the - nodefaults channel. I.e.

- uses: conda-incubator/setup-miniconda
      with:
        channels: conda-forge,nodefaults
        channel-priority: strict

Will correctly give

channels:
- conda-forge
dependencies:
...

Instead of

channels:
- conda-forge
- nodefaults
dependencies:
...

Here is a log of the modified version in action.

Closes #207

@liamhuber
Copy link
Author

Example 6 during Adding tools to 'base' env...:

  Removing channel defaults
  /home/runner/miniconda3/condabin/conda config --remove channels defaults
  Warning: 
  CondaKeyError: 'channels': 'defaults' is not in the 'channels' key of the config file
  
  
  
  CondaKeyError: 'channels': 'defaults' is not in the 'channels' key of the config file

The patch is working as-intendend and in Applying initial configuration... we correctly get:

  Removing channel defaults
  /home/runner/miniconda3/condabin/conda config --remove channels defaults

But then there's something in the Ex6 config that differs from my simple test case that causes it to double-dip for defaults.

@liamhuber
Copy link
Author

Ok, got it: the setup runs applyCondaConfiguration a second time. So it's just my own new if-clause come to bite me.

@liamhuber
Copy link
Author

Ok, the log shows the nodefaults option correctly purging the defaults channel. After that, defaults gets added back in because it's included explicitly in etc/example-environment.yml. This is a bit odd when nodefaults gets passed to as an action arg, but it is internally consistent with the existing behaviour where the provided env file is the highest authority, so it seems reasonable to me. I'm content with this and feel this is ready for review.

@liamhuber
Copy link
Author

@conda-incubator/setup-miniconda tests are passing, just waiting on a review.

Comment on lines 47287 to 47288
core.info("Removing channel defaults");
if (channel === "nodefaults") {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
core.info("Removing channel defaults");
if (channel === "nodefaults") {
if (channel === "nodefaults") {
core.info("Removing channel defaults");

src/conda.ts Outdated Show resolved Hide resolved
Copy link
Member

@jaimergp jaimergp left a comment

Choose a reason for hiding this comment

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

Just a tiny comment with the logging statements. LGTM otherwise.

@jezdez
Copy link
Member

jezdez commented Sep 10, 2024

I don't agree we should further the "nodefaults" workaround in setup-miniconda, it's a bad API since it's not an actual channel and tends to increase the risk of a messy channel management

@jezdez
Copy link
Member

jezdez commented Sep 10, 2024

I think it might be enough to simply offer "override_channels" as a config option, which requires specifying a channel but doesn't hard-code the "nodefaults" workaround.

Copy link
Member

@jezdez jezdez left a comment

Choose a reason for hiding this comment

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

A more explicit and future-proof solution is relying on the --override-channels CLI option since nodefaults is a pretty bad workaround within conda. That way, running conda config --remove channels defaults isn't needed here.

Co-authored-by: jaimergp <[email protected]>
@liamhuber
Copy link
Author

Just a tiny comment with the logging statements. LGTM otherwise.

@jaimergp, nice catch! Committed 🚀

I don't agree we should further the "nodefaults" workaround in setup-miniconda, it's a bad API since it's not an actual channel and tends to increase the risk of a messy channel management...

@jezdez, the current situation is one "bad" API and a second API that silently deviates from the first API in an unproductive way (i.e. a channel named "nodefaults" is literally added to the channels list). Regardless of whether the conda API is good or bad, I would strongly recommend conda-incubator/setup-miniconda favour consistency over goodness where the two APIs use the same terminology.

I definitely like your suggested --override-channels solution, but I see it as a "yes, and" approach, and I'm afraid you'll need to identify a different volunteer for its implementation.

@liamhuber
Copy link
Author

Nice, even better, if conda/conda#14227 is merged I think we can even simply close this PR?

@jaimergp
Copy link
Member

Yep, timing will work this time. The only other workaround right now is to explicitly pass a condarc file correctly populated to the condarc-file input.

@liamhuber
Copy link
Author

Yep, timing will work this time. The only other workaround right now is to explicitly pass a condarc file correctly populated to the condarc-file input.

Haha, yep, did exactly that downstream a couple weeks ago! pyiron/actions#129

@oursland
Copy link

Builds are breaking in CI as the package defaults/noarch::conda-libmamba-solver-23.12.0 is being selected, which is incompatible.

@liamhuber
Copy link
Author

@oursland I'm afraid I don't have enough context to understand your statement. The only thing breaking here was the linter test -- which failed because I never rebuilt the committed ts suggestions into the js.

I did that now, but I also see that conda/conda#14227 got merged last week. @jaimergp my understanding is that now we just need to wait for the next release of conda, and this PR becomes completely unnecessary, yeah? If that's the case I think we can just close this PR.

@oursland
Copy link

GitHub Actions should not be considered like a normal software release as this is a piece of core CI infrastructure and is not something that can always be deferred until the next release of an upstream package, which will then be packaged into a release, and eventually distribution.

The current behavior of setup-miniconda still breaks GitHub actions. During update, it pulls in an incompatible version of libmamba-solver from the defaults channel. This has been breaking CI for FreeCAD among others.

@liamhuber
Copy link
Author

GitHub Actions should not be considered like a normal software release as this is a piece of core CI infrastructure and is not something that can always be deferred until the next release of an upstream package, which will then be packaged into a release, and eventually distribution.

I don't think I feel as strongly about it, but I also don't disagree. My opinion doesn't matter though, I'm just a rando who patched what was bothering me. If you want to get this in ASAP you'll need to convince the two reviewers.

If you want something immediately that avoids erroneously including the defaults, you can check out https://github.com/pyiron/actions/blob/main/cached-miniforge/action.yml, a wrapper for setup-miniconda that patches over the defaults issue and solves an edge case in caching (#361)

The current behavior of setup-miniconda still breaks GitHub actions. During update, it pulls in an incompatible version of libmamba-solver from the defaults channel. This has been breaking CI for FreeCAD among others.

I'm afraid I'm a little lost again. Do you mean the main branch of setup-miniconda, or do you mean that you tried out this branch and something I've done here broke the libmamba-solver versioning? If it's the main branch, raising a separate issue and referencing this PR might be the way to go; if it's something I've done here I'll need some assistance tracking down the problem -- at least a MWE for the breakage -- as I don't usually do any work in javascript.

@oursland
Copy link

I'm afraid I'm a little lost again. Do you mean the main branch of setup-miniconda, or do you mean that you tried out this branch and something I've done here broke the libmamba-solver versioning?

The main branch is broken. This PR fixes the issue. Waiting what could be months for to fix CI is not a reasonable strategy.

Your fix through caching is unfortunately not feasible for our application. For FreeCAD and others, we already use 100% of our GitHub provided caching storage budget for various compiler caches.

@liamhuber
Copy link
Author

@oursland, the action I linked also removes the defaults channel; if this PR fixes your problem, it's likely that action will provide immediate relief as well -- the caching issue is an additional, unrelated issue it fixes. It is not a pure-wrapper though -- it will also try to pip install your local package by default -- but you should have enough flexibility in the inputs to get it to work as a pure wrapper. If you need something to solve the defaults problem immediately, I recommend trying that.

@jakirkham
Copy link
Member

AIUI Conda will go through a depreciation cycle before removal. It will probably be a bit before installers come prebaked with the newer Conda

Not to mention some users may pin an installer version as part of this workflow

Given this, think this change still is desirable. What do we need to do to get it in?

@jaimergp
Copy link
Member

Thanks @jakirkham, yes, when I posted this comment I had gone for the straight removal disregarding the deprecation cycle, which was brought in the code review.

This change is still needed as a result, but I think @jezdez doesn't want the nodefaults hack in more places, so we'll have to come up with a different solution.

My ideas:

  • Warn about channels implicitly adding defaults
  • Add a new setting to remove defaults if not in channels explicitly. Something like ignore_defaults_in_channels.

Since this is a new approach, I'll open a new PR. Thanks all for the fruitful discussion!

@oursland
Copy link

Add a new setting to remove defaults if not in channels explicitly. Something like ignore_defaults_in_channels.

This is the expected behavior. If I provide a list of channels, then I expect that those channels and only those channels are being used. The inclusion of defaults implicitly in the channels list was a surprise when it caused breakage in our builds. I consider this inclusion to be a bug.

I personally do not believe an additional flag to remove defaults should be necessary as I consider it's implicit inclusion to be a bug that should be resolved.

@jaimergp
Copy link
Member

Believe it or not, it was intentionally designed that way. Not an oversight. You are "adding" channels to the list, which is prepopulated with defaults. I agree it is not great UX, so that's why conda is deprecating that using the standard deprecation window of three months.

If anything, the bug is here in setup-miniconda because we are using conda config --add to populate the channels, and it wasn't clear in the documentation that it happened that way. The option would have been better called add_channels. Since we can't guarantee that changing the default will break users workflows, the conservative way forward is adding a flag that people can opt-in to, and eventually turn it on by default. If you'd like to advocate for a more aggressive change, let's discuss that in the new PR.

@jakirkham
Copy link
Member

Yep that is reasonable Jaime. Am not saying it needs to be this fix, but we do need something

How about we do the same thing we do in conda-forge? Namely conda config --remove channels defaults, which gives us

# .condarc
channels: []

Then we can add any channels after

@liamhuber
Copy link
Author

Regardless of whether the nodefaults syntax is good or not, and even in the presence of the new better PR, I would actually still suggest also merging this PR. As long as conda itself supports nodefaults in the channels removing defaults, I feel it would be wise to synchronize behaviour in the channels arg here. Literally adding nodefaults to the channels list is unexpected and undesirable.

Copy link
Member

@jakirkham jakirkham left a comment

Choose a reason for hiding this comment

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

Thanks Liam! 🙏

Perhaps a concrete suggestion makes it clearer what I'm proposing. Please let me know what you think 🙂

@@ -47284,8 +47284,19 @@ function applyCondaConfiguration(inputs, options) {
// LIFO: reverse order to preserve higher priority as listed in the option
// .slice ensures working against a copy
for (const channel of channels.slice().reverse()) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
for (const channel of channels.slice().reverse()) {
if (channels.length) {
yield condaCommand(["config", "--remove", "channels", "defaults"], options);
}
for (const channel of channels.slice().reverse()) {

Copy link
Author

Choose a reason for hiding this comment

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

Doesn't this change the behaviour so that the absence of defaults is the new default behaviour?

I actually disagree with @oursland that including defaults by default is a bug that needs to be removed -- at least here. I'm 100% in favour of getting rid of it in the default behaviour of conda itself, but for me it's important that setup-miniconda be consistent with conda where they share terminology (like "channels"). To this end I actually don't recommend changing the default behaviour, and still never want to see the channel nodefaults explicitly get added to the channels list.

Ie fix problems upstream, and keep behaviour downstream consistent with upstream (maybe adding extra convenience on top of that consistency as in... can't find the link on mobile, but you know, the other PR relevant here)

Copy link
Member

Choose a reason for hiding this comment

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

In the case when a user specifies channels (like below), it would only use those channels

setup-miniconda/README.md

Lines 349 to 353 in 8f65dda

- uses: conda-incubator/setup-miniconda@v3
with:
activate-environment: foo
python-version: 3.6
channels: conda-forge,spyder-ide

If no channels are specified, it would fallback to the default behavior of the selected installer/Conda (typically using defaults)

Copy link
Author

Choose a reason for hiding this comment

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

But this still differs from default conda behviour, yeah? Where defaults is included by default and setting the channel nodefaults removes it rather than adding a channel nodefaults?

Copy link
Member

Choose a reason for hiding this comment

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

Not sure I follow

We don't specify how the channel public API of this GHA is implemented. It could be specified through conda config commands (like now) or it could be specified by writing out a condarc or it could be options tacked onto each conda create/conda install command or some other way not currently being considered

Think relying on how these public APIs are implemented under-the-hood is relying on an implementation detail, which would be subject to change. There is no guarantee the implementation details would remain the same

Copy link
Member

Choose a reason for hiding this comment

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

Though did miss we do say we will add defaults to the end of the list 🤦‍♂️

setup-miniconda/README.md

Lines 335 to 337 in 8f65dda

- conda-forge
- spyder-ide
- defaults

That said, I think this is worth breaking. It would mean using a major version bump

Comment on lines +47287 to +47299
if (channel === "nodefaults") {
core.info("Removing channel defaults");
try {
yield condaCommand(["config", "--remove", "channels", "defaults"], options);
}
catch (err) {
core.info("Removing defaults raised an error -- it was probably not present.");
}
}
else {
core.info(`Adding channel '${channel}'`);
yield condaCommand(["config", "--add", "channels", channel], options);
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (channel === "nodefaults") {
core.info("Removing channel defaults");
try {
yield condaCommand(["config", "--remove", "channels", "defaults"], options);
}
catch (err) {
core.info("Removing defaults raised an error -- it was probably not present.");
}
}
else {
core.info(`Adding channel '${channel}'`);
yield condaCommand(["config", "--add", "channels", channel], options);
}
core.info(`Adding channel '${channel}'`);
yield condaCommand(["config", "--add", "channels", channel], options);

@@ -126,8 +126,22 @@ export async function applyCondaConfiguration(
// LIFO: reverse order to preserve higher priority as listed in the option
// .slice ensures working against a copy
for (const channel of channels.slice().reverse()) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
for (const channel of channels.slice().reverse()) {
if (channels.length) {
yield condaCommand(["config", "--remove", "channels", "defaults"], options);
}
for (const channel of channels.slice().reverse()) {

Comment on lines +129 to +144
if (channel === "nodefaults") {
core.info("Removing channel defaults");
try {
await condaCommand(
["config", "--remove", "channels", "defaults"],
options,
);
} catch (err) {
core.info(
"Removing defaults raised an error -- it was probably not present.",
);
}
} else {
core.info(`Adding channel '${channel}'`);
await condaCommand(["config", "--add", "channels", channel], options);
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (channel === "nodefaults") {
core.info("Removing channel defaults");
try {
await condaCommand(
["config", "--remove", "channels", "defaults"],
options,
);
} catch (err) {
core.info(
"Removing defaults raised an error -- it was probably not present.",
);
}
} else {
core.info(`Adding channel '${channel}'`);
await condaCommand(["config", "--add", "channels", channel], options);
}
core.info(`Adding channel '${channel}'`);
yield condaCommand(["config", "--add", "channels", channel], options);

@liamhuber
Copy link
Author

liamhuber commented Sep 30, 2024

It's maybe worth quickly revisiting why this PR exists: I ran into the nasty situation that defaults was getting added to my channels even if I didn't ask for it, #186. When I looked at conda, I saw this could be fixed by adding the channel nodefaults, but in setup-miniconda this simply literally added the channel nodefaults!

Now the real solution is to fix this upstream in conda, which is exactly what @jaimergp did conda/conda#14227, so to the extend that this PR raised the visibility of #186 high enough to motivate that change, it's a full success. Everything after that real, upstream fix to the fundamental problem is just transient window dressing.

But what window dressing should there be? IMO, unless it is very explicitly documented, setup-miniconda should behave like conda where we would naively expect it to. That means I don't recommend getting rid of defaults by default; extending new syntax to get rid of defaults like in #367 is great; and where setup-miniconda input looks like conda input and behaves almost like conda input, it ought to behave exactly like conda input -- anything else is just super confusing.

I patched the problem downstream in my own workflows by initializing an empty .condarc, so I've got basically zero skin in this game beyond trying to be a helpful neighbour. My feelings won't be hurt if you close this and just run with #367 until conda/conda#14227 eventually becomes the conda standard -- but for the sake of other users, it's my earnest recommendation to drive for consistency between setup-miniconda and conda above other considerations.

EDIT: missed a mini

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.

defaults channel in .condarc file when only conda-forge is specified
5 participants