Stack: Accept AbstractResolvers as valid resolvers in stack.yaml

Created on 11 Aug 2016  Â·  8Comments  Â·  Source: commercialhaskell/stack

This is an improvement request.

It would be great if you could specify an abstract resolver as a resolver in a stack.yaml.
To clarify the term, on the command line, you can specify a certain resolver to be used with the --resolver flag. This flag does not only support specific resolver specifications (like lts-6.10 or nightly-2016-08-06, represented internally by Resolver), but also abstract specifications like lts or lts-6 (represented internally by AbstractResolver).
This abstract resolver is later turned into a concrete one with makeConcreteResolver (in src/Stack/Config.hs). However, you can't currently set such an abstract resolver in a ´stack.yaml` file.

The user guide provides two reasons why this currently isn't possible:

  1. [It will] slow down your build (since stack then needs to download information on the latest available LTS each time it builds)
  2. [It produces] unreliable results (since a build run today may proceed differently tomorrow because of changes outside of your control)

First of all, I think both points are very valid, however there surely are tons of other ways to shoot yourself in the foot using stack (or cabal, etc.). A warning when trying to specify an abstract resolver the first time might hinder the unknowing newcomer from getting himself in greater trouble.

To address point 1 in particular, I think a simple timestamp saved somewhere in .stack-work could largely resolve the issue.

Regarding point 2, I'd say that stack.yaml is first and foremost a development file. While cabal considers the cabal files of dependencies, stack.yaml files are really only read when building the very project they are located in. This limits the point of somewhat unexpected behaviour to one file that is most likely located in your working directory. Furthermore, stack could provide a warning that it is switching the resolver. Similar to cabal's freeze command that freezes dependencies in a sandbox, stack could provide a freeze command that changes an abstract resolver into a concrete one and an unfreeze command to do the opposite.

On the other side, I'd say there are considerable merits in making this change: Concrete nightly resolvers are more or less out of date 24 hours after their release (with exceptions, of course), stack-8.0.yaml in this very project points to nightly-2016-07-19. There is nothing special about that nightly version, and even if it had been set to a specific version because later nightlies caused problems, that would be an undesirable workaround: After all, stack (or all other projects, for that matter) should work and be tested with newer versions of its dependencies. Similarly, users of lts-6.0 should really upgrade to the latest lts-6, after all, the motivation for LTS Haskell was smooth intra-major updates.

I'm very much looking forward to your comments!

discuss enhancement

Most helpful comment

Just want to point out that Stack will take a simplicity hit if we do this -- right now when explaining the benefits of stack one of the first things to mention is that stack.yml is enough to make a project reproducible. If we go with this option that will no longer be correct without adding a caveat.

Additionally (as I just learned this today) stack already has the stack config set resolver nightly and stack config set resolver ltsconvenience commands. Those come pretty close to the convenience of an abstract stack resolver setting, without changing the fundamentals of what stack.yml means.

EDIT: It was @sjakobi who told me those commands. I should start reading usernames.

All 8 comments

After all, stack (or all other projects, for that matter) should work and be tested with newer versions of its dependencies.

I guess Stackage does this for the included packages. For libraries that are not on stackage, being able to specify resolver: nightly seems like a useful feature.

I'm +1 on this as long as there's a visible warning regarding reproducibility. Maybe the warning should be shown more often than once though… perhaps daily?

Possibly stack sdist should make sure that stack.yamls included in tarballs use concrete resolvers.

Yeah, I think it's sensible to lift this restrictions in a controlled way. One idea is to have something like:

lenient: true
resolver: nightly

We can tighten up stack.yaml file reproducibility, and add lenient: true as an explicit "yup, I understand this might not build in the future".

Maybe reproducible: false would be a better name?

Regarding point 2, I'd say that stack.yaml is first and foremost a development file.

Not sure I understand the prioritization. One of stack's biggest priorities is reliablity / reproducibility of the build for a project among for multiple developers / machines. Some metadata could indeed be written down to notify the user that they are implicitly switching snapshots.

So, in summary, I can understand the desire for this feature, particularly for, say, CI. However, for the CI case you can just specify --resolver nightly. If a feature like this is added, I'd prefer that stack.yaml files default to having restrictions that encourage reproducibility.

Maybe reproducible: false would be a better name?

I think that's more memorable than lenient.

Regarding point 2, I'd say that stack.yaml is first and foremost a development file.

Not sure I understand the prioritization. One of stack's biggest priorities is reliablity / reproducibility of the build for a project among for multiple developers / machines. Some metadata could indeed be written down to notify the user that they are implicitly switching snapshots.

What I meant to say is that it would be undesirable if, say, some definitions in the cabal file of some dependency changed their meaning (resulting in unexpected behaviour) based on the time of day. However, in this case, the only unexpected behaviour that could arise stems from a single line in a prominent file right in the root of the project, namely stack.yaml. This reduces the possible negative effects. With "development file", I meant that stack.yaml is only ever important if you're building the package right in front of you, the stack.yamls of any dependencies are irrelevant to the process. You don't break anything other than your own package if you mess up your stack.yaml.

Regarding the name, reproducible: false sound quite good.

What do you think of my suggestion above of something like stack config freeze-resolver/stack config unfreeze-resolver? Those seem like an excellent place to print warnings. Other than that, I'd suggest printing warnings (or rather, very visible notes) when implicitly changing resolvers.
On a second thought, if someone really set reproducible: false in their stack.yaml, I'm not quite sure if that really called for a warning on every normal invocation - reproducible: false should really be a I-know-what-I'm-doing flag. (If it really were to print a big fat warning on every invocation of stack, there had to be a way of silencing it, but it seems a bit overengineered to add another flag for that.)

I already have some basic patches lying around, so I will go ahead and try to implement some of the protection mechanisms suggested here.

I'm imagining that there'd always at least be a short warning. Why? Because ideally, stack.yaml files should "just work", and let the downstream user know that this might cause things to break.

So, with reproducible: false, you'd get something like:

Warning: "reproducible: false" specified in the project configuration.
You may not get the same results as when this configuration was authored. 

With reproducible: true (default) and an abstract resolver you'd get something like

Error: Abstract resolver "nightly" specified in the project configuration.
Either specify the full, concrete version of the resolver or add "reproducble: false" to your configuration.

What do you think of my suggestion above of something like stack config freeze-resolver/stack config unfreeze-resolver? Those seem like an excellent place to print warnings.

Seems too complicated to me. I don't like having behavior affecting state in .stack-work.

I think reproducible is slightly misleading/misnomer, but okay. I would prefer say abstract-resolver: true, which I feel is more precise and intelligible.

True, that certainly makes sense based on the info here. I am imagining a meaning a bit more involved than just abstract-resolver: true. There are other features that could be added to the configuration format that make it less likely to be reproducible, for example, resolving extra-deps from hackage.

We could also consider another setting for reproducible (or another name, strict ?) which is stricter than the current defaults. Some flags, like extra-lib-dirs seem unlikely to be reproducible unless combined +with some form of environment sandboxing.

Just want to point out that Stack will take a simplicity hit if we do this -- right now when explaining the benefits of stack one of the first things to mention is that stack.yml is enough to make a project reproducible. If we go with this option that will no longer be correct without adding a caveat.

Additionally (as I just learned this today) stack already has the stack config set resolver nightly and stack config set resolver ltsconvenience commands. Those come pretty close to the convenience of an abstract stack resolver setting, without changing the fundamentals of what stack.yml means.

EDIT: It was @sjakobi who told me those commands. I should start reading usernames.

Was this page helpful?
0 / 5 - 0 ratings