pinup — Dependency-Updates als ein statisches Binary.
pinup liest deine vorhandene Renovate-Konfiguration, plant jede Änderung, bevor es eine schreibt, und öffnet Merge Requests, die sagen, was sie bringen. Ein Go-Binary, kein Node, keine Sidecars: Datenquellen und Manager sind eingebaut, die Plattform ist eine Schnittstelle mit GitLab (produktiv über 200 Repositories) und GitHub.
- Binary:
go install github.com/ohartwig/pinup/cmd/pinup@latestoder die signierten Releases - Konfiguration:
renovate.jsonbleibt, wie sie ist ·.pinup.yamlgeht auch - Lizenz: Apache-2.0 · zwei Abhängigkeiten (YAML-Parser, bbolt) · Go 1.27
Renovate läuft. Warum kommt das Update nicht?
Bisher
- Ein Update fehlt, und niemand weiß, ob eine Regel, ein Zeitplan oder ein Fehler es hält
- Zwanzig bis dreißig Minuten pro Stunde auf einem Node-Runner mit Sidecars für jede fremde Datenquelle
- Ein Chart-Anbieter zählt bei jedem Release die Major-Version hoch, und jedes davon wartet auf Freigabe
- Ein
postUpgradeTasks-Kommando außerhalb der Allowlist wird übersprungen, der Branch trotzdem gepusht
Mit pinup
- Jeder Lauf schreibt einen Plan, in dem jedes gehaltene Update seinen Grund, seine Thaw-Zeit und die Regel trägt
- Dieselben 200 Repositories in zehn statt vierzig Minuten Wall-Zeit, ein Binary, keine Laufzeit
- Ein Analyzer liest, was sich im Chart wirklich geändert hat; das strengere Label entscheidet, es sei denn, eine Regel vertraut dem Analyzer
- Ein abgelehntes Kommando hält den Branch und nennt sich im Plan
Passt das zu meinem Setup?
Nutze es wenn …
- du Renovate betreibst und wissen willst, warum ein Update nicht kommt
- dein Runner viel Zeit in einer Node-Laufzeit mit Sidecars verbringt
- Charts oder Pakete bei jedem Release die Major-Version hochzählen und du automatisch mergen willst, wo sich nichts geändert hat
- dir wichtig ist, dass ein Plugin nie den Plattform-Token sieht und ein Task nur in seinem Scope schreibt
Nutze es nicht wenn …
- du Bitbucket, Azure DevOps oder Gitea brauchst: heute gibt es GitLab und GitHub
- dein Stack Manager braucht, die pinup nicht kennt: Maven, Gradle, NuGet, Cargo, pip-Requirements
- du Mend Merge Confidence willst: pinup klassifiziert selbst und ruft keinen Drittdienst
Was pinup mitbringt
Ein Plan vor jedem Schreiben
pinup whatif hört dort auf, pinup run macht weiter
Jeder Lauf erzeugt einen maschinenlesbaren Plan, bevor ein Byte geschrieben wird. Jedes gehaltene Update trägt seinen Grund, seine Thaw-Zeit und die Regel, die es hält. Apply verbraucht Byte-Range-Edits, nie Abhängigkeiten: nichts hinter dem Plan kann eine Änderung erfinden.
Effective-Label
Für Chart-Anbieter, die bei jedem Release die Major-Version hochzählen
Neben dem deklarierten Label (major, minor, patch) fragt eine Regel mit analyze: true einen Analyzer, was sich wirklich geändert hat: für ein Helm-Chart die Anwendungsversion, entfernte Values-Keys, die Subcharts. Das strengere Label gewinnt, es sei denn, die Regel sagt trustEffective.
Renovate-kompatibel
pinup migrate sagt Schlüssel für Schlüssel, was gelesen wird
Dieselbe Konfigurationssprache: extends, Presets, packageRules, Custom Manager mit Handlebars und JSONata, Zeitpläne, minimumReleaseAge, Dashboard. Dieselben Branch-Namen, also werden offene Merge Requests übernommen statt dupliziert.
Shadow-Modus
Umstellung ohne Vertrauensvorschuss
pinup shadow vergleicht pinups Pläne mit den Merge Requests, die Renovate offen hat, Lauf für Lauf, und nennt sich erst richtig, wenn beide übereinstimmen. Ein Kontroll-Repository muss abweichen, damit der Vergleich beweist, dass er scheitern kann.
Plugins ohne Zugangsdaten
Der Kern entscheidet, Tasks wenden an
Ein Lock-Refresh oder ein postUpgradeTasks-Kommando läuft in einem eigenen HOME, ohne Plattform-Token und ohne Signierschlüssel, gegen eine Allowlist auf dem kompilierten Kommando. Was es außerhalb seines Scopes schreibt, wird verworfen.
Clean Room, Apache-2.0
Zwei Abhängigkeiten: YAML-Parser, bbolt
Renovate ist AGPL-3.0. Nichts wurde kopiert: Verhalten wurde im gepinnten Container beobachtet und aus aufgezeichneten Ein-/Ausgabepaaren neu geschrieben, die zugleich die Testsuite sind.
Audit der Konfiguration
Vier Kategorien, 38 Checks, jeder mit einem Fall, der ihn auslöst
pinup advise liest die Konfiguration wie ein Lauf und nennt, was sich ändern sollte: Regeln, die nie feuern, Werte, die ein Preset schon setzt, ein Automerge über Majors. --fix schreibt die Fixes byte-genau in die Datei, hinter einem Gate auf der Auflösung.
Warum
Ich habe Renovate über ein Estate von 200 Repositories betrieben: sieben Scan-Partitionen im Stundentakt, eine Release-Fast-Lane, eine default.json von 88 KB mit 25 Custom Managern und der Incident-Geschichte eines Jahres. Es funktionierte, und es kostete: 40 Minuten Wall-Zeit und 128 Runner-Minuten pro Stundenlauf, ein Node-Sidecar für die apk-Datenquelle, die Renovate nicht hat, gespiegelte Toolchains, damit install-tool nie GitHub anfasst – und eine Klasse stiller Fehler: ein Composer-Lock, das ein Jahr lang nie aktualisiert wurde; ein Kommando, das die Allowlist ablehnte und das ohne roten Job einfach übersprungen wurde; dreizehn Security-Merge-Requests, grün gemerged um einen Task herum, der nie lief.
Renovate sagt, was es getan hat. Es sagt nicht, was es nicht getan hat und warum. Das ist der Punkt, an dem pinup ansetzt. Renovate ist außerdem AGPL-3.0: die Konfigurationssprache durfte ich behalten, den Code nicht lesen. pinup ist ein Clean-Room-Nachbau aus aufgezeichneten Ein-/Ausgabepaaren des gepinnten Containers – deshalb kann es permissiv lizenziert sein: Apache-2.0, wie alle meine Go-Werkzeuge.
Gemessen nach der Umstellung, Stundenlauf über das ganze Estate: 75 statt 128 Runner-Minuten, 10,6 statt 40 Minuten Wall-Zeit, pro Partition etwa die Hälfte. Ein First-Party-Release erreicht seine Konsumenten in Minuten, ein OSV-Advisory wird binnen fünfzehn Minuten bearbeitet.
Quickstart
Ein Repository planen, das du schon hast
whatif löst die Konfiguration auf, liest die Manifeste, schlägt die Versionen nach und schreibt einen Plan. Es ändert nichts und braucht keinen Token. Ohne eigene Datei reicht eine .pinup.yaml mit extends: [config:recommended] für den Anfang.
cd ein-repository
pinup whatif --repo . --config .pinup.yaml --report plan.json
# was sich bewegen würde, und was gehalten wird und warum
jq -r '.updates[] | "\(.dep.depName) \(.dep.currentValue) -> \(.newValue) \(.blocks[0].reason // "ready")"' plan.json
lodash 4.17.20 -> 4.17.21 ready wird beim nächsten run ein Branch; redis 20.13.4 -> 28.1.0 dependencyDashboardApproval sagt, was das Major hält. --now 2026-01-01T00:00:00Z plant für einen anderen Zeitpunkt.
Gegen ein Projekt laufen
run tut, was whatif tut, und pusht dann die Branches, öffnet oder aktualisiert die Merge Requests und führt das Dashboard-Issue.
export PINUP_GITLAB_URL=https://gitlab.example.org
export PINUP_GITLAB_TOKEN=glpat-… # Scopes: api, write_repository
export PINUP_GIT_NAME="Dependency Bot" PINUP_GIT_EMAIL=bot@example.org
export PINUP_SIGNING_FORMAT=none # oder openpgp / ssh + PINUP_SIGNING_KEY
pinup run --project gruppe/projekt --config .pinup.yaml --report plan.json
Auf GitHub liest dasselbe Kommando PINUP_GITHUB_TOKEN und --project owner/repository. --dry-run hört nach dem Plan auf: der sichere erste Lauf gegen ein echtes Projekt. Was entsteht: ein Branch pro Update oder Gruppe, benannt wie bei Renovate, ein Merge Request mit Edits und Release-Notes, automerge, wo eine Regel es sagt, und ein Dashboard-Issue mit einer Checkbox pro gehaltenem Update.
Eine Konfiguration für viele Repositories
Die Regeln des Estates liegen in einem Runner-Projekt, jedes Repository bindet sie ein; ein Repository ganz ohne Datei bekommt die Defaults des Runners.
// Runner-Projekt: default.json
{ "extends": ["config:recommended"], "packageRules": [ … ] }
# jedes Repository: .pinup.yaml
extends: ["local>gruppe/pinup-runner"]
pinup run --autodiscover '["gruppe/**", "!gruppe/archiv/**"]' \
--config 'local>gruppe/pinup-runner' --report 'reports/%s.json'
--autodiscover läuft über jedes nicht archivierte Projekt, das der Token sieht und das die Globs matchen, acht gleichzeitig. Eine vollständige Runner-Konfiguration liegt unter docs/examples im Repository.
Einplanen
Unter docs/examples liegen ein geplanter GitLab-Job (Toolchain-Image mit pinup, composer, npm und go, der Token als maskierte Variable, der Lookup-Cache zwischen den Läufen, die Pläne als Artefakte) und dasselbe als GitHub-Actions-Workflow. Stündlich ist in Ordnung: ein Lauf über zweihundert Repositories dauert zehn Minuten Wall-Zeit, und ein Lauf, der nichts Neues findet, schreibt nichts.
Die Dokumentation
Container-Images
Die beiden Abbilder auf ghcr.io, ihre Tags, was darin steckt, woher das Binary kommt und wie sich eine Signatur prüfen lässt.
Konfiguration
Renovates Sprache, die drei Schichten, die Merge-Semantik, jeder unterstützte Schlüssel und die Umgebungsvariablen.
Manager, Datenquellen, Versionierungen
Welche Dateien gelesen werden, welche Registries gefragt werden, wie Versionen geordnet werden.
Der Plan
Das JSON, das jeder Lauf schreibt, bevor er etwas anderes schreibt: Felder, Halte-Gründe, und wie man es mit jq liest.
Effective-Label
Das zweite Label auf jedem Update: der Helm-Chart-Analyzer, die Sicherheitsregel, matchEffective und trustEffective.
Tasks und Plugins
Lock-Refreshes und Post-Upgrade-Kommandos: was läuft, in welchem Scope, mit welcher Umgebung.
Plattformen
GitLab und GitHub hinter einer Schnittstelle: Zugangsdaten, was jede kann und nicht kann, wie eine dritte dazukommt.
Sicherheit
Wohin der Token geht, was ein Repository den Bot tun lassen kann und was nicht, wie Commits signiert werden.
Quellcode und Releases
Go-Modul
github.com/ohartwig/pinup · CGO aus, ein Binary
go install github.com/ohartwig/pinup/cmd/pinup@latest
Dokumentation
Englisch im Repository, Deutsch und Englisch hier
Einstieg, jedes Kommando, die Konfigurationsschlüssel, das Plan-Format, Plattformen, Sicherheit: im Repository unter docs/, und hier auf den Unterseiten.
GitHub
Linux und macOS, amd64 und arm64
Quellcode, Releases mit signierten Binaries, Issues. Apache-2.0-lizenziert.
Renovate im Betrieb, aber die Runner-Rechnung oder die stillen Aussetzer stören?
Ich helfe bei Shadow-Betrieb, Umstellung und Runner-Setup: erst vergleichen, dann schneiden, mit einem Rückfallpunkt an jedem Schritt.