Skip to content

Migrating a config ​

There is no converter. A renovate.json is usually a handful of presets and a few rules, and most of those presets turn out to be the Count's defaults already. Translating by hand, or asking an agent to, takes minutes and leaves you with a config you understand.

Start from the defaults ​

config:recommended, :semanticCommits, :dependencyDashboard, :pinAllExceptPeerDependencies, helpers:pinGitHubActionDigests and docker:pinDigests mostly describe what the Count does without being asked: exact pins, digest pins for actions and images, semantic commit prefixes and one PR per update. Turn the dashboard on and you are close.

pkl
// .github/patchula.pkl
amends "../Patchula.pkl"

prs { dashboard = true }

Read through the defaults and keep what you actually rely on.

Option by option ​

RenovateCount Patchula
extendsamends "org.pkl" or import
enabledManagersmanagers (Renovate names differ: gomod is go, docker-compose is compose, pip_requirements is requirements, pep621 and poetry are pyproject, helmv3 is helm)
ignoreDepsignore, with globs and regexes
ignorePathsexclude
managerFilePatternsfiles
packageRulesrules
matchPackageNamesselect { packages { ... } }
matchDepNamesselect { dependencies { ... } }
matchManagers, matchDatasourcesselect { managers }, select { sources }
matchFileNamesselect { files }
matchUpdateTypesselect { updates } (pinDigest is pin)
matchDepTypesselect { kinds }
matchCurrentVersionselect { current = "semver:..." }
allowedVersionsapply { permitted = "semver:..." }
enabled: falseapply { active = false }
groupNameapply { group = "..." }
automergeapply { merge = true }
rangeStrategyapply { versions = "exact" | "preserve" | "widen" }
pinDigests: falseapply { digest = false }
minimumReleaseAgeapply { waitHours = 72 }
schedule, timezonewindows { new Window { cron { ... } zone = "..." } }
labels, addLabelslabels
semanticCommitType, commitMessagePrefixcommitPrefix
prBodyNotesbodyNotes
fetchChangeLogs: "off"releaseNotes = false
prConcurrentLimit, prHourlyLimitprs { concurrent; perHour }
branchPrefixprs { branch }
rebaseWhenprs { rebase = "conflicts" | "behind" }
dependencyDashboardprs { dashboard = true }
lockFileMaintenancelockfiles { refresh = true }
postUpdateOptions: ["gomodTidy"]lockfiles { tidyGo = true }
customManagers (regex)extractors
regexManagers capture depNamecapture dependency
currentValuecurrent
extractVersionextract
datasourceTemplate, depNameTemplate, packageNameTemplate, versioningTemplatesource, dependency, package, scheme

Rules apply in order, and a later rule's value replaces an earlier one's, lists included. Empty group means separate PRs. Zero is never "unlimited"; use null.

A worked example ​

json
{
  "extends": ["config:recommended"],
  "schedule": ["before 6am on monday"],
  "packageRules": [
    {
      "matchUpdateTypes": ["minor", "patch"],
      "matchCurrentVersion": "!/^0/",
      "automerge": true
    },
    {
      "matchPackageNames": ["/^@vitest//", "vitest"],
      "groupName": "vitest"
    }
  ]
}

becomes

pkl
amends "../Patchula.pkl"

defaults {
  windows {
    new Window { cron { "* 0-5 * * 1" } }
  }
}
rules {
  new Rule {
    select {
      updates { "minor"; "patch" }
      current = "!semver:<1"
    }
    apply { merge = true }
  }
  new Rule {
    select { packages { "vitest"; "glob:@vitest/*" } }
    apply { group = "vitest" }
  }
}

Then check it:

sh
patchula config schema > Patchula.pkl
patchula config check .
patchula run --dry-run owner/repo

The examples directory has a dozen real configs translated this way.

Things that do not carry over ​

Presets, onboarding, config migration, hostRules, private registries, postUpgradeTasks, vulnerability alerts, replacement PRs and platforms other than GitHub have no equivalent. The compatibility matrix lists everything. A # renovate: comment used by a regex manager can stay in your files: it is only text, and your Pkl extractor can match it.