Patchula.pkl
This is the complete repository schema, bundled with every build. Print it with patchula config schema. The configuration guide explains how the pieces fit together.
Top-level fields
| Field | Type | Default |
|---|---|---|
managers | Listing<Manager> | every manager |
defaults | Policy | see below |
rules | Listing<Rule> | none |
files | Listing<FileSet> | each manager's own patterns |
extractors | Listing<Extractor> | none |
ignore | Listing<String> | none |
exclude | Listing<String> | **/node_modules/**, **/vendor/**, **/.git/** |
prs | PullRequests | see below |
lockfiles | Lockfiles | see below |
useVersionsHost | Boolean | true |
submodules | Boolean | false |
ignoreAuthors | Listing<String> | none |
Policy
The defaults every rule starts from.
| Field | Type | Default |
|---|---|---|
active | Boolean | true |
versions | "exact"|"preserve"|"widen" | "exact" |
channel | "stable"|"lts" | "stable" |
actionDigests | Boolean | true |
imageDigests | Boolean | true |
merge | Boolean | false |
group | String? | null |
labels | Listing<String> | "dependencies" |
commitPrefix | String | "chore(deps):" |
releaseNotes | Boolean | true |
bodyNotes | Listing<String> | none |
permitted | String? | null |
waitHours | Int | 24 |
windows | Listing<Window> | none (any time) |
A rule's apply has the same fields as nullable overrides, plus digest, and leaves out the two digest switches. null leaves the earlier value alone.
Pull requests
| Field | Type | Default |
|---|---|---|
concurrent | Int? | null (unlimited) |
perHour | Int? | null (unlimited) |
dashboard | Boolean | false |
branch | String | "patchula/" |
rebase | "conflicts"|"behind" | "conflicts" |
batchMerge | Boolean | false |
Lockfiles
| Field | Type | Default |
|---|---|---|
refresh | Boolean | false |
waitHours | Int | 0 |
group | String | "lockfile refresh" |
tidyGo | Boolean | false |
npmEngine | "aube"|"npm" | "aube" |
php | "static"|"compiled" | "static" |
The schema
pkl
/// Patchula's explicit defaults. Vendor with `patchula config schema` and amend
/// this module, or amend an organisation module that itself amends this one.
module Patchula
typealias Manager = "npm"|"bun"|"github-actions"|"mise"|"cargo"|"dockerfile"|"compose"|"go"|"terraform"|"kubernetes"|"kustomize"|"flux"|"helm-values"|"requirements"|"pyproject"|"pre-commit"|"helm"|"devcontainer"|"maven"|"gradle"|"nuget"|"bundler"|"composer"|"regex"
typealias Source = "npm"|"github-releases"|"github-tags"|"github-runners"|"docker"|"crates"|"go"|"terraform"|"pypi"|"helm"|"oci"|"mise"|"forge-tags"|"maven"|"nuget"|"rubygems"|"packagist"
typealias Update = "major"|"minor"|"patch"|"digest"|"pin"
typealias VersionPolicy = "exact"|"preserve"|"widen"
typealias ReleaseChannel = "stable"|"lts"
typealias Scheme = "semver"|"docker"|"pep440"|"poetry"|"maven"|"nuget"|"rubygems"|"composer"|"go"|"hashicorp"|"loose"
/// Dependency roles emitted by first-release managers. npm/bun use the first
/// four roles; Cargo also uses build. Ordinary Go/Python/regex packages use
/// production. Actions distinguish steps from reusable workflows; containers,
/// mise tools, Go language directives, Terraform providers/modules and Helm
/// charts have explicit roles. These are exact values, not match patterns.
typealias DependencyKind = "production"|"development"|"peer"|"optional"|"build"|"action"|"reusable-workflow"|"runner"|"container-image"|"tool"|"go-directive"|"toolchain"|"provider"|"module"|"chart"
/// Match lists are OR; populated selector fields are AND. Empty means any.
/// Patterns are path-aware globs, or `regex:...`; prefix `!` excludes a match.
/// Version filters are `semver:...`, `regex:...`, or their `!` negations.
class Selector {
packages: Listing<String> = new {}
dependencies: Listing<String> = new {}
managers: Listing<Manager> = new {}
files: Listing<String> = new {}
updates: Listing<Update> = new {}
sources: Listing<Source> = new {}
kinds: Listing<DependencyKind> = new {}
current: String? = null
}
class Window {
/// Five-field cron expressions, interpreted in the named IANA timezone.
cron: Listing<String> = new {}
zone: String = "UTC"
}
/// Policy applied before rules. No implicit grouping or automatic merging.
class Policy {
active: Boolean = true
versions: VersionPolicy = "exact"
/// Node LTS is opt-in; other sources retain stable selection by default.
channel: ReleaseChannel = "stable"
actionDigests: Boolean = true
imageDigests: Boolean = true
merge: Boolean = false
/// Nonempty groups share a PR; an empty string forces individual PRs.
group: String? = null
labels: Listing<String> = new { "dependencies" }
commitPrefix: String = "chore(deps):"
releaseNotes: Boolean = true
bodyNotes: Listing<String> = new {}
permitted: String? = null
/// Minimum release age when the source reports a date. Current pins are retained.
waitHours: Int = 24
/// An empty window list means updates may be proposed at any time.
windows: Listing<Window> = new {}
}
/// Null leaves an earlier policy unchanged. Rules apply in declaration order;
/// scalars and lists replace earlier values. `digest` overrides source defaults.
class Overrides {
active: Boolean? = null
versions: VersionPolicy? = null
channel: ReleaseChannel? = null
digest: Boolean? = null
merge: Boolean? = null
group: String? = null
labels: Listing<String>? = null
commitPrefix: String? = null
releaseNotes: Boolean? = null
bodyNotes: Listing<String>? = null
permitted: String? = null
waitHours: Int? = null
windows: Listing<Window>? = null
}
class Rule {
note: String = ""
select: Selector = new {}
apply: Overrides = new {}
}
class FileSet {
manager: Manager
/// Replaces that manager's built-in file selection; empty is invalid.
paths: Listing<String>
}
/// Regex captures use Rust regex syntax: (?<current>...), (?<dependency>...).
/// Templates substitute {capture} only; no expressions or executable helpers.
class Extractor {
note: String = ""
paths: Listing<String>
patterns: Listing<String>
source: String = "{source}"
dependency: String = "{dependency}"
package: String = "{dependency}"
/// A Scheme name or a template such as "{scheme}".
scheme: String = "semver"
/// Optional tag regex with a required named `version` capture.
extract: String? = null
}
class PullRequests {
/// Null means unlimited; positive integers set a limit. Zero is invalid.
concurrent: Int? = null
perHour: Int? = null
dashboard: Boolean = false
branch: String = "patchula/"
rebase: "conflicts"|"behind" = "conflicts"
/// The batch PR collects updates whose "Add to batch" box was ticked. It
/// only auto-merges when this is true, behind the usual check gates.
batchMerge: Boolean = false
}
class Lockfiles {
refresh: Boolean = false
waitHours: Int = 0
group: String = "lockfile refresh"
tidyGo: Boolean = false
/// Engine that regenerates npm lockfiles: aube by default, or native npm.
/// pnpm and Yarn lockfiles always use aube and Yarn respectively.
npmEngine: "aube"|"npm" = "aube"
/// PHP for composer.lock: a prebuilt static binary, or PHP compiled from
/// source by mise, which needs a C toolchain and development libraries.
php: "static"|"compiled" = "static"
}
/// All first-release managers are selected unless a repository replaces this list.
managers: Listing<Manager> = new {
"npm"; "bun"; "github-actions"; "mise"; "cargo"; "dockerfile"; "compose"
"go"; "terraform"; "kubernetes"; "kustomize"; "flux"; "helm-values"
"requirements"; "pyproject"; "pre-commit"; "helm"; "devcontainer"; "maven"; "gradle"; "nuget"; "bundler"; "composer"; "regex"
}
defaults: Policy = new {}
rules: Listing<Rule> = new {}
files: Listing<FileSet> = new {}
extractors: Listing<Extractor> = new {}
/// Dependency identifiers to leave untouched, using the same pattern syntax.
ignore: Listing<String> = new {}
/// Generated and vendored directories are excluded; repos can replace the list.
exclude: Listing<String> = new { "**/node_modules/**"; "**/vendor/**"; "**/.git/**" }
prs: PullRequests = new {}
lockfiles: Lockfiles = new {}
/// Prefer mise-versions for public catalogs; false uses the upstream sources.
useVersionsHost: Boolean = true
submodules: Boolean = false
/// Commits by these authors do not trigger a new extraction.
ignoreAuthors: Listing<String> = new {}