Kai Ole Hartwig
pinup · documentation

The plan

Every run produces a machine-readable plan before it writes anything: whatif stops there, run continues. The plan is the backbone, not a debug flag. The compare job, the dashboard, the merge request text and the estate report read it.

  • A plan explains why nothing happens. Every held update carries its reason, when the hold lifts and which rule imposed it.
  • Nothing downstream of the plan invents a change. Branches carry byte-range edits and tasks; apply consumes those, never a dependency.
  • Format: JSON, --report <path>, %s for the project path, schemaVersion 1

The fields

Top level

FieldMeaning
pinupVersion, generatedAtwhich pinup planned, and when
repo.paththe project
limitsprHourlyLimit, prConcurrentLimit as resolved; the runner enforces them
dashboardwhether the dashboard issue is kept, and its title
branchingprefix and, during a rename, prefixOld; the runner counts and prunes under both
deps, updates, branches, warnings, statssee below
unmanagedwith --coverage only: pinned versions nothing updates, as {file, line, value}

deps

One entry per dependency per file: manager, file, depName, packageName, currentValue, currentDigest, lockedVersion, datasource, versioning, registryUrls, the values from the pre-lookup rule pass (rangeStrategy, minimumReleaseAge, pinDigests), the locus (byte offsets of value and digest) and, for custom managers, the index and the captures. A dependency that was not looked up says why in skipReason; one disabled by a rule names the rule in disabled.

allowedVersions is the limit a rule set, allowedVersionsBy the rule. When the limit excludes every newer release, skipReason does not say “up to date” but something like held by allowedVersions "/^8\.2\./" (packageRules[12]): 3 newer releases excluded, the newest 8.5.2. When the dependency gets an allowed update and the rule still holds back something newer, the same sentence is in heldBack. The dashboard shows both on the dependency.

updates

One entry per movement: dep, newValue (the bytes written), newVersion, newDigest, updateType (major, minor, patch, digest, pin, pinDigest, rollback, lockFileMaintenance, majorAvailable), stream on a majorAvailable of a series pin (the sibling package of the higher series), declared and effective, analyzer and evidence, releaseTime with timeSource, securityFix. And:

branches

One entry per branch: name (Renovate-compatible), plannedName during a rename, title, commitBody, groupName, updateKeys, edits (file, byte range, old and new bytes, manager), tasks (lock refreshes and post-upgrade commands with their scope), automerge, labels, schedule, existing (the open merge request) and suppressedBy with heldWith where the branch is held as a whole.

warnings

stage (config, extract, lookup, analyze, apply, publish, cache), file, msg. An unreachable datasource, a preset that resolves to nothing, a task whose output left its scope, a cache that is too young: warnings, never a failed run for the rest of the repository.

Reading a plan

# every held update with its reason
jq -r '.updates[] | select(.blocks) | "\(.dep.depName) \(.dep.currentValue) -> \(.newValue): \(.blocks[0].reason) (\(.blocks[0].origin.source))"' plan.json

# what allowedVersions holds back, with or without an allowed update
jq -r '.deps[] | (.heldBack // .skipReason // "") as $r | select($r | startswith("held by")) | "\(.depName) \(.currentValue): \($r)"' plan.json

# the branches that would be written now
jq -r '.branches[] | select(.suppressedBy == null) | .name' plan.json

Next

Back to pinup

The overview: the problem, the fit check, the quickstart, every chapter.

pinup →
Back to pinup

Effective label

The second label on every update: the Helm chart analyzer, the safety rule, matchEffective and trustEffective.

Read →
Effective label