renovatebot / renovate

Home of the Renovate CLI: Cross-platform Dependency Automation by Mend.io
https://mend.io/renovate
GNU Affero General Public License v3.0
17.44k stars 2.29k forks source link

Everything is a package rule #4726

Open rarkins opened 5 years ago

rarkins commented 5 years ago

There are existing issues this is partly a duplicate of, but I wanted to start a clean feature request discussion.

I think that internally, Renovate should adopt the concept that "everything is a package rule". All other nested approaches of configuration (e.g. "major": { "enabled": true } or "python": { "masterIssue": true } should be considered as kind of "shortcuts" or syntactic sugar that are immediately massaged into packageRules.

Of course, packageRules are evaluated in their array order, so the order of such shortcuts massaging is important. I think that the order we massage them into the packageRules array should be logical, but if ever in doubt then users should simply use the longer-form packageRules in the exact order they want, instead of shortcuts.

What type of "shortcuts" exist today?

  1. Languages e.g. python or docker
  2. Managers, e.g. pip_requirements
  3. DepTypes, e.g. devDependencies and engines
  4. Update types, e.g. major

Missing today: Datasources

The next question is how to order them. Let's say there are in fact 7 different sources for the final packageRules, in the order I think they need to be applied:

  1. The config's explicitly defined packageRules
  2. All non-nested (top-level) config, e.g. "semanticCommits": false
  3. languages
  4. managers
  5. depTypes (maybe)
  6. datasources
  7. updateTypes

I haven't considered how to handle config and rules within presets either, but I think they need the same "flattening" logic applied and then be put first so that everything in config can potentially override anything in presets.

e.g. take this config:

{
  "semanticCommits": false,
  "docker": {
    "pinDigests": false
  },
  "npm": {
    "major": {
      "enabled": false
    }
  },
  "rubygems": {
    "semanticCommits": true
  },
  "packageRules": [{
    "packageNames": ["lodash"],
    "enabled": false
  }]
}

You'd get:

{
  "packageRules": [{
    "packageNames": ["lodash"],
    "enabled": false
  },
  {
    "packagePatterns": ["*"],
    "semanticCommits": false
  },
  {
    "languages": ["docker"],
    "pinDigests": false
  },
  {
    "managers": ["npm"],
    "updateTypes": ["major"],
    "enabled": false
  },
  {
    "datasources": ["docker"],
    "pinDigests": false
  },
  {
    "datasources": ["rubygems"],
    "semanticCommits": true
  }]
}

Recall that the "final" packageRules like the above is intended to be understandable although not necessarily simple/optimized.

Challenges:


edit: fix json typo

rarkins commented 5 years ago

Another impact of this: we do not need to define every language/manager/datasource within lib/config/definitions. Maybe we only define them if they need custom config.

I even have some doubts if we need to put each manager's custom config - most of which is highly recommended to never be overridden - within the definitions, or just define it within source code in lib/managers.

HonkingGoose commented 3 years ago

From what I gather, this would be a big potentially breaking change? At least it seems to me that this is a totally different concept than what Renovate uses now?

Do we still want to make such a big sweeping change?

I'm asking because this issue is from Oct 2019, and the code base has probably changed a lot in that time. 😄

I noticed we have some related issues linking back to this one, so might want to look at that first to decide whether or not this is still relevant?

rarkins commented 3 years ago

From what I gather, this would be a big potentially breaking change?

Big change to our config logic: yes

Potentially breaking: it would change the way we resolve configs, fixing many things which produce confusing behaviour today but also changing some behaviour that people may have been relying on.

At least it seems to me that this is a totally different concept than what Renovate uses now?

Not at all. The end result will be that most cases function exactly as they were

Do we still want to make such a big sweeping change?

Yes, and we must

I'm asking because this issue is from Oct 2019, and the code base has probably changed a lot in that time. 😄

I noticed we have some related issues linking back to this one, so might want to look at that first to decide whether or not this is still relevant?

It's a lot of work, and we haven't decided how to handle the backwards-incompatible aspect of it for users of the app, who can't control when they take a major release.

rarkins commented 2 years ago

A way forward: I think we might be able to implement this as a feature flag - potentially even hidden at first - and opt ourselves into it as test subjects first.

rarkins commented 2 years ago

Here's how to convert a config into all packageRules.

First of all, there's going to be some certain config which can't go into packageRules, such as hostRules, which I'm going to ignore for now.

function configToRules(input):

output = { packageRules: [] }
// Add extends rules first
if input.extends:
  output.packageRules = configToRules(resolve(input.packageRules))
  delete input.extends
// now add top-level config
let rootRule = { matchAll: true }
output.packageRules.push(rootRule)
for (key, value) of input:
  if key === 'packageRules':
    // concatenate them to output.packageRules
    // now reset the rootRule to come after those
    rootRule = { matchAll: true }
    output.packageRules.push(rootRule)
  else if value is an object:
    nestedRule = {}
    // add appropriate matching based on the key name (e.g. minor, or npm, etc)
    nestedRule = { ...nestedRule, ...value }
    output.packageRules.push(nestedRule)
  else:
    rootRule[key] = value
rarkins commented 2 years ago

Summarizing: