Kai Ole Hartwig
Go · Apache-2.0 · GitLab und GitHub

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@latest oder die signierten Releases
  • Konfiguration:renovate.json bleibt, wie sie ist · .pinup.yaml geht 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

1

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.

2

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.

3

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.

4

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

Einstieg

Das Binary, ein Token, der erste Plan, der erste Lauf, ein CI-Job.

Lesen →
Einstieg

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.

Lesen →
Container-Images

Konfiguration

Renovates Sprache, die drei Schichten, die Merge-Semantik, jeder unterstützte Schlüssel und die Umgebungsvariablen.

Lesen →
Konfiguration

Manager, Datenquellen, Versionierungen

Welche Dateien gelesen werden, welche Registries gefragt werden, wie Versionen geordnet werden.

Lesen →
Manager, Datenquellen, Versionierungen

Der Plan

Das JSON, das jeder Lauf schreibt, bevor er etwas anderes schreibt: Felder, Halte-Gründe, und wie man es mit jq liest.

Lesen →
Der Plan

Effective-Label

Das zweite Label auf jedem Update: der Helm-Chart-Analyzer, die Sicherheitsregel, matchEffective und trustEffective.

Lesen →
Effective-Label

Tasks und Plugins

Lock-Refreshes und Post-Upgrade-Kommandos: was läuft, in welchem Scope, mit welcher Umgebung.

Lesen →
Tasks und Plugins

Plattformen

GitLab und GitHub hinter einer Schnittstelle: Zugangsdaten, was jede kann und nicht kann, wie eine dritte dazukommt.

Lesen →
Plattformen

Sicherheit

Wohin der Token geht, was ein Repository den Bot tun lassen kann und was nicht, wie Commits signiert werden.

Lesen →
Sicherheit

Quellcode und Releases

Go-Modul

github.com/ohartwig/pinup · CGO aus, ein Binary

go install github.com/ohartwig/pinup/cmd/pinup@latest

pkg.go.dev →
Go-Modul

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.

docs/ auf GitHub →
Dokumentation

GitHub

Linux und macOS, amd64 und arm64

Quellcode, Releases mit signierten Binaries, Issues. Apache-2.0-lizenziert.

Auf GitHub ansehen →
GitHub

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.

Anfragen →