Renovate: Support ranges for package.json > engines

Created on 5 Mar 2018  路  16Comments  路  Source: renovatebot/renovate

We should support upgrading node ranges in package.json > engines the same way we support ranges in regular npm.

Follows from #320

priority-4-low feature

Most helpful comment

I'm going to close this as implemented now, as we support ranges in package.json > engines for node, npm and yarn.

However I have opened a new issue (https://github.com/renovateapp/renovate/issues/2084) for "Bump deprecated/unsupported ranges" related to the functionality requested by @hbetts above.

All 16 comments

This will require some refactoring of our npm semver logic so that it can be used for engines too (i.e. with a set of releases not originating from npmjs)

Any process on that? I would use it when implemented. As I understand it, with combination of lts_latest supportPolicy it will be great. Exactly the case that @hbetts described at #320.

Because pinning of engines won't be good feel.

Yes it should be any day now. What鈥檚 an example from/to range you鈥檇 like to see?

For a few Node libraries I help maintain, conventional-github-releaser and conventional-gitlab-releaser, we had the following engines setup in their package.json files:

"engines": {
  "node": ">=4.2.0"
}

I recently upgrade the range manually:

"engines": {
  "node": ">=6.9.0"
}

My expectation is that the lower bound for the range is the oldest initial LTS release for a major version of Node that is still supported. Version 6.9.0 of Node was the first release of Node 6 when it entered _Active LTS_ support.

Ranges are important for libraries to ensure maximum compatibility for downstream consumers (such as application and services). I would not expecct the node property in engines to be pinned.

You'll also notice that I use >=, which will match non-LTS versions of Node. That's a compromise we made, as we have consumers that are working, for one reason or another, on newer, non-LTS, releases of Node, and we are trying to be minimally disruptive to their workflow (though we do state that we only support LTS releases).

I think that the current configuration options - even if fully implemented for node - still don't capture all the options people would want.

Even when using exact versions in engines, I can see three different approaches:

  • >= 6.0.0: doesn't make too much sense to me (6.0.0 wasn't LTS) but I'm sure some wants it
  • >= 6.9.0: e.g. as @hbetts above says: the earliest LTS in node@6
  • >= 6.14.0: the minimum "secure" version

I'm thinking how we can capture these concepts in a generic yet understandable way.

One way would be to consider non-LTS versions (both odd major versions as well as pre-LTS versions) of node to be "unstable", and unsupported versions (e.g. 4.x now) to be "deprecated". However, you could also use the concept of "deprecated" to mean any Node.js version considered insecure. So maybe we need a new concept about "upgrade insecure" where the only use case right now is for Node.js.

In @hbetts there's some pragmatic compromise between what the official support policy is vs the engines value. If your official policy is "Active LTS" then engines should in theory be ^6.9.0 || ^8.9.0. If your policy is pragmatically to include non-deprecated newer versions then it would be ^6.9.0 || ^8.9.0 || ^10.0.0. >= 6.9.0 doesn't really map to any policy we currently have however doesn't mean it can't be supported.

@hbetts do you need to support 7.x and 9.x in engines?

I guess that the node.js "support policy" you configure for Renovate doesn't necessarily have to be your actual/official support policy in the readme - it's more about the values you want in engines, travis, etc.

>= 6.0.0: doesn't make too much sense to me (6.0.0 wasn't LTS) but I'm sure some wants it

I suspect someone will want to allow users to consume, and execute, their library on earlier, non-LTS, releases of Node. I tend to advocated against relying on a non-LTS version of an LTS release line. I've become an advocate against it because it seems many people assume Node 6.* is LTS, without realizing that LTS only starts at 6.9.

>= 6.14.0: the minimum "secure" version

It's not my place to force downstream consumers to use a secure version of Node. It's not necessary for the purpose of using the library. Furthermore, practices that restrict user freedom for the purpose of _security_ will ultimately lead to the result of those users working around those restrictions (users will always seek a path that is beneficial, lower cost, to them. It's difficult to internalize the long-term costs of insecure practices.).

If you believe your downstream consumers should be using secure versions of Node, build the tools that make using a secure version of Node easy (such as nvm, or auto-updating pinned Node versions in applications and services using Renovate, etc.), and educate them on how to use those tools.

Okay, so that ended up being more of a _soap box_ than a constructive technical discussion on the merits of not setting the minimum version of Node to 6.14.

In @hbetts there's some pragmatic compromise between what the official support policy is vs the engines value.

There definitely is. Package manager behavior with respect to the node engines property does well to capture the technical requirements of a library, but fails to capture a library maintainer's policy with regard to support. It's why I've had to take a pragmatic approach to the problem by using a relaxed Node version range in the package.json file, while writing up my support policy in the project's README.md. I don't feel it's as intuitive to downstream consumers as it should be, but it's the best I've been able to achieve up to this point.

If your official policy is "Active LTS" then engines should in theory be ^6.9.0 || ^8.9.0

Our official policy is _Active_ and _Maintenance_ LTS versions. Therefore, your pattern is correct from a policy standpoint.

If your policy is pragmatically to include non-deprecated newer versions then it would be ^6.9.0 || ^8.9.0 || ^10.0.0.

That wouldn't actually align with any objective since, by policy, we don't support Node 10 (It's not yet LTS).

>= 6.9.0 doesn't really map to any policy we currently have however doesn't mean it can't be supported.

>= 6.9.0 is meant to say that the library can probably work, _technically_, on version Node 7, but I won't go out of my way to _make_ it work on Node 7.

@hbetts do you need to support 7.x and 9.x in engines?

Personally, no, but there are users of projects like conventional-github-releaser that are using the library on versions of Node, such as 7, 9, and 10, and conventional-github-releaser, _technically_ does work on those versions.

Thinking through how we configure/support this type of engines requirement. Sounds like the next change for it would be from >=6.9.0 to >=8.9.0 once node@6 is no longer maintained.

Normally in Renovate with rangeStrategy set to auto, replace or widen then we'd never update >=6.9.0 because there's never a new version that necessitates it. This would require us to understand that one day every node version before 8.9.0 is now unsupported. As mentioned above, I think the deprecated field/concept could be used with this special meaning in the node context. If the current version is deprecated it should trigger a bump in the same way we trigger a rollback today if the current version disappears.

I think the field might be called bumpDeprecated and default to true. Logically it would mean "if the current version is deprecated then apply rangeStrategy=bump".

For the insecure option, I can think of two ways:

  1. A bumpInsecure option that behaves similarly if the current version is marked as insecure
  2. An option deprecateInsecure that treats insecure versions as deprecated and then can be combined with bumpDeprecated.

@olstenlarck can you describe your requirements so I can see if they align with these ideas?

I cant write much now, because im on phone. But i'm totally agree with @hbetts and my ideas are pretty similar to case that he describes.

i'm creating libraries and want to support current/latest lts and any higher versions, and when next lts come to bump engine to it. In that case it is very easy to say to your consumers (of my libs) that you only support last lts and above, so they will be noted earlier.

As noted in my comment here, it would be nice if the implementation could support multiple application types (such as libraries vs applications/services).

The alternative is that we would need to export two, or more, presets, that represent the whether the project is an application/service or library.

Though, how would you handle mono-repositories that contain both libraries, and applications/services?

Though, how would you handle mono-repositories that contain both libraries, and applications/services?

Huh... is there such monorepos? Damn. Can you share some links, it's interesting to me and definitely not sounds like a good architecture decision..

There are a few mono-repositories that I'm aware of that contain both a service and a Node library (I don't have any links to share because these projects are private). Service teams structure their project this way so that they can ship validation functions in a separate npm package that can be consumed by one or more third-party web applications (that need to communicate with the service).

We want the service pinned to a specific Node version to ensure build/deployment reproducibility when deploying to a Heroku-like environment, while we want the library to use a semver range (for maximum compatibility with downstream consumers).

Ooh... right, got it. :)

I'm going to close this as implemented now, as we support ranges in package.json > engines for node, npm and yarn.

However I have opened a new issue (https://github.com/renovateapp/renovate/issues/2084) for "Bump deprecated/unsupported ranges" related to the functionality requested by @hbetts above.

Was this page helpful?
0 / 5 - 0 ratings