Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions projects/kirchDev/coverage-report/de.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Coverage am Pull Request, aus den Reports der eigenen CI

Meldet Gesamt-, Patch- und Delta-Coverage als einen dauerhaften Kommentar samt Check Run, gelesen aus den Coverage-Dateien, die der Lauf ohnehin schreibt.

## why

Kommentare mit Coverage gibt es als fertige Action mehrfach. Die erste Aufgabe war deshalb nicht, noch eine zu bauen, sondern die vorhandenen an einem echten Repository laufen zu lassen, mit Vitest und Pest nebeneinander und einem echten Pull Request. Vier Lücken kamen zurück, und die sind der ganze Umfang: Jede liest nur eine Formatfamilie, keine führt zwei Suiten zu einer Zahl zusammen, keine holt sich den Vergleichswert des Zielbranches selbst, und keine rechnet Patch-Coverage. Was die vorhandenen als geänderte Dateien anbieten, filtert nach Datei und listet darin dann doch jede unabgedeckte Zeile.

Ein Dienst von außen ist dafür nicht nötig. Die Zahlen entstehen im Workflow und verlassen ihn nicht.

## quickstart

```yaml
- uses: kirchDev/coverage-report@v0.3.0
with:
reports: |
coverage/lcov.info
coverage.xml
min-patch: '80'
```

Zwei Suiten, eine Zahl, ein Kommentar am Pull Request.

## features

- **Vier Formate in einem Lauf** — lcov, Cobertura, Clover und Istanbul, erkannt am Inhalt statt am Dateinamen, weil coverage.xml im Laravel-Repository Clover ist und im .NET-Repository Cobertura.
- **Eine Zahl für ein Monorepo** — der Report aus JavaScript und der aus PHP werden zusammengeführt, und eine Zeile, die beide Suiten treffen, zählt einmal.
- **Patch-Coverage, die das Wort verdient** — das Diff wird auf die Trefferzeilen abgebildet, sodass die offenen Zeilen die sind, die diese Änderung angefasst hat, nicht die anderen vierhundert derselben Datei.
- **Der Vergleichswert bleibt im Repository** — die Coverage des Integrationsbranches liegt in einem verwaisten Branch, also braucht das Delta weder einen Dienst noch ein Artefakt mit Ablaufdatum.
- **Kommentar und Check Run** — der Kommentar wird über eine Marke wiedergefunden und bearbeitet statt gestapelt, dazu ein Check Run, auf den ein Branch-Schutz zugreifen kann.
- **Schwellen, die greifen oder schweigen** — mit einer Untergrenze scheitert der Lauf, ohne sie endet der Check neutral statt mit einem grünen Haken, den niemand verdient hat.

## scope

Gemessen wird nichts. Die Coverage-Dateien schreibt weiterhin die eigene Test-Suite, und ohne einen solchen Report hat die Action nichts zu lesen. Codecov ersetzt sie dort, wo es um Kommentar, Delta und Schwellen geht, und sonst nirgends: Es gibt keine Historie über Monate, keinen Blick über mehrere Repositories hinweg und keine Oberfläche außerhalb des Pull Requests.

## install

```yaml
- uses: kirchDev/coverage-report@v0.3.0
```

Dieselben Zahlen lokal, als CLI ohne die GitHub-Hälfte. Das macht eine Abweichung zwischen der Prüfung auf dem eigenen Rechner und dem Kommentar am Pull Request überhaupt erst nachvollziehbar.

```pnpm
pnpm add -D @kirchdev/coverage-report
```

```npm
npm install -D @kirchdev/coverage-report
```
51 changes: 51 additions & 0 deletions projects/kirchDev/coverage-report/en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Coverage on the pull request, from the reports your CI already writes

Reports total, patch and delta coverage as one sticky comment plus a check run, read from the coverage files the run produces anyway.

## why

Coverage comments already exist as ready-made actions, several times over. So the first task was not to build another one but to run the existing ones against a real repository, with Vitest and Pest side by side and a real pull request. Four gaps came back, and they are the whole scope: each reads one format family, none merges two suites into a single number, none fetches the target branch's baseline itself, and none computes patch coverage. What the existing ones offer as changed files filters by file and then still lists every uncovered line inside it.

No outside service is needed for any of it. The numbers are produced inside the workflow and never leave it.

## quickstart

```yaml
- uses: kirchDev/coverage-report@v0.3.0
with:
reports: |
coverage/lcov.info
coverage.xml
min-patch: '80'
```

Two suites, one number, one comment on the pull request.

## features

- **Four formats in one run** — lcov, Cobertura, Clover and Istanbul, detected by content rather than by file name, because coverage.xml is Clover in a Laravel repository and Cobertura in a .NET one.
- **One number for a monorepo** — the JavaScript report and the PHP report merge, and a line both suites hit counts once.
- **Patch coverage that means it** — the diff is mapped onto line hits, so the open lines are the ones this change touched, not the other four hundred in the same file.
- **The baseline stays in the repository** — the integration branch's coverage lives on an orphan branch, so the delta needs neither a service nor an artifact with an expiry date.
- **A comment and a check run** — the comment is found again by a marker and edited rather than piled up, plus a check run for branch protection to gate on.
- **Thresholds that bite or stay quiet** — with a floor the run fails; without one the check concludes neutral rather than showing a green tick nobody earned.

## scope

Nothing here measures. The coverage files still come from your own test suite, and without such a report the action has nothing to read. It replaces Codecov where comment, delta and thresholds are concerned, and nowhere else: there is no history across months, no view spanning several repositories, and no interface outside the pull request.

## install

```yaml
- uses: kirchDev/coverage-report@v0.3.0
```

The same numbers locally, as a CLI without the GitHub half. That is what makes a disagreement between a check on your own machine and the comment on the pull request debuggable at all.

```pnpm
pnpm add -D @kirchdev/coverage-report
```

```npm
npm install -D @kirchdev/coverage-report
```
12 changes: 12 additions & 0 deletions projects/kirchDev/coverage-report/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"$schema": "../../../schema/project.schema.json",
"kind": "oss",
"category": "ci",
"technologies": ["typescript", "node", "pnpm", "github-actions", "github"],
"links": [
{
"type": "npm",
"url": "https://www.npmjs.com/package/@kirchdev/coverage-report"
}
]
}
34 changes: 34 additions & 0 deletions projects/kirchDev/gitignore-sync/de.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# CLI, die eine .gitignore pflegt statt sie zu erzeugen

Hält kuratierte Blöcke in einer Region, die das Werkzeug besitzt, und rendert sie auf Zuruf neu. Alles außerhalb dieser Region bleibt unberührt.

## why

Die verbreiteten Werkzeuge holen und kippen aus: Man zieht sich einmal eine Vorlage, fügt sie ein und driftet von da an ab. Keines davon gleicht später noch einmal ab, und so wächst eine .gitignore zu ein paar hundert Zeilen Ballast, an die sich niemand mehr herantraut.

gitignore-sync hält stattdessen eine verwaltete Region in der Datei und rendert sie neu, so oft man will. Der Rest ist eine freie Zone, in die das Werkzeug nie schreibt. Genau das macht den zweiten Lauf ungefährlich und den hundertsten langweilig.

## quickstart

```bash
gitignore-sync init # erkennen, bestätigen, Region schreiben
gitignore-sync sync # neu rendern, beliebig oft
gitignore-sync check # in der CI: Fehlercode bei Drift
```

Erkannt wird nur beim ersten Befehl. Ein später hinzugekommenes package.json schreibt die Datei nie von selbst um.

## features

- **Gleicht ab, statt neu auszukippen** — der Abgleich ist idempotent und gehört damit in eine Gewohnheit, einen Hook oder einen Cron statt in ein einmaliges Ritual.
- **Eigene Zeilen gehen nie verloren** — eine handgeschriebene Zeile innerhalb eines verwalteten Blocks wandert in die freie Zone, statt gelöscht zu werden. Diese Regel ist es, die den zweiten Lauf ungefährlich macht.
- **Entdoppelung, die git kennt** — exakte Dubletten fallen weg, aber .idea und .idea/ und /.idea sind für git drei verschiedene Muster und werden deshalb gemeldet, nie stillschweigend zusammengezogen.
- **Fängt den Fehler ab, der Ausnahmen aushebelt** — ein verirrter Eintrag neben einer Ausnahmeregel schaltet diese still ab, weil git in ein ignoriertes Verzeichnis nicht hineinschaut. Dafür gibt es eine eigene Warnung.
- **Klappt im Editor zusammen** — die Region ist als Faltbereich ausgezeichnet, sodass ein vierzig Zeilen langer Block auf eine Zeile einklappt.
- **Ein echtes Tor für die CI** — die Prüfung meldet Drift und Dubletten und endet mit einem Fehlercode, sodass ein veralteter Block den Build scheitern lässt statt zu verrotten.

## scope

Es gibt bisher weder ein Release noch ein veröffentlichtes Paket, die Version steht auf 0.0.0. Die Befehle oben laufen heute aus einem Klon, nicht aus einer Installation.

Über die Datei hinaus reicht das Werkzeug ohnehin nicht: Was ignoriert wird, entscheidet weiterhin git allein, und eine Datei, die bereits versioniert ist, holt kein Eintrag zurück.
34 changes: 34 additions & 0 deletions projects/kirchDev/gitignore-sync/en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# A CLI that maintains a .gitignore rather than generating one

Keeps curated blocks inside a region the tool owns and re-renders them on demand. Everything outside that region is left alone.

## why

The common tools fetch and dump: you pull a template once, paste it in, and drift from there. None of them reconciles later, so a .gitignore grows into a few hundred lines of ballast nobody dares touch.

gitignore-sync keeps a managed region inside the file instead and re-renders it as often as you like. The rest is a free zone the tool never writes to. That is what makes the second run safe and the hundredth boring.

## quickstart

```bash
gitignore-sync init # detect, confirm, write the region
gitignore-sync sync # re-render, as often as you like
gitignore-sync check # in CI: non-zero on drift
```

Detection happens on the first command only. A package.json that shows up later never rewrites the file by itself.

## features

- **Reconciles instead of re-dumping** — the sync is idempotent, so it belongs in a habit, a hook or a cron rather than a one-off ritual.
- **Your own lines are never lost** — a hand-written line found inside a managed block moves to the free zone instead of being deleted. That rule is what makes the second run safe.
- **Deduplication that knows git** — exact duplicates go, but .idea and .idea/ and /.idea are three different patterns to git and are therefore reported, never quietly merged.
- **Catches the mistake that disables exceptions** — a stray entry beside an exception rule silently switches it off, because git never looks inside an ignored directory. That gets a warning of its own.
- **Folds in the editor** — the region is marked as a folding range, so a forty-line block collapses to a single line.
- **A real gate for CI** — the check reports drift and duplicates and exits non-zero, so a stale block fails the build instead of rotting.

## scope

There is no release and no published package yet, and the version still reads 0.0.0. The commands above run from a clone today, not from an installation.

Beyond the file the tool does not reach anyway: what gets ignored is still git's decision alone, and a file already under version control is not brought back by any entry.
7 changes: 7 additions & 0 deletions projects/kirchDev/gitignore-sync/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$schema": "../../../schema/project.schema.json",
"kind": "oss",
"category": "cli",
"maturity": "wip",
"technologies": ["typescript", "node", "pnpm", "git"]
}
43 changes: 43 additions & 0 deletions projects/kirchDev/vite-plugin-iconify-bundle/de.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Vite-Plugin, das nur die benutzten Iconify-Icons bündelt

Durchsucht den Quelltext nach Icon-Namen, löst jeden zur Bauzeit gegen die installierten Collections auf und legt genau diese Icons ins Bundle. Zur Laufzeit wird die Iconify-API nicht mehr gefragt.

## why

Die übliche Einbindung holt die Icon-Daten zur Laufzeit von der Iconify-API. Das scheitert offline, rendert unter serverseitigem Rendern nichts und macht den Build unvorhersehbar. Stattdessen ganze Collections mitzuliefern tauscht das gegen Megabyte an Icons, die niemand benutzt.

Das Plugin geht den dritten Weg. Es sucht im Quelltext nach Zeichenketten der Form Präfix und Name, löst jede gegen die installierten Collections auf und erzeugt daraus ein virtuelles Modul, das genau diese Icons registriert. Im Bundle liegt danach, was tatsächlich im Code steht.

## quickstart

```ts
import 'virtual:iconify-bundle';
```

Ein einziger Import ohne Rückgabewert registriert jedes gefundene Icon. Danach funktionieren sie offline, rendern serverseitig und kosten keine Anfrage.

## features

- **Nur die benutzten Icons** — im Bundle liegt, was der Scan gefunden hat, nicht die vollständige Collection.
- **Ein Scan, keine Analyse** — gesucht wird über alle drei Zeichenketten-Begrenzer, ein Name im Kommentar zählt also mit. Das ist Absicht, denn so fällt ein falscher Name laut auf, statt still nichts zu rendern.
- **Unbekannte Namen halten den Build an** — ein Icon, das die installierte Collection nicht kennt, lässt den Build scheitern, statt eine Lücke auszuliefern.
- **Fünf Optionen** — alles Weitere ist Konvention.
- **Überlebt Hot Module Replacement** — wird eine Quelldatei bearbeitet, verfällt das virtuelle Modul, und ein neu geschriebener Icon-Name erscheint ohne Neustart.

## scope

Der Scan ist zugleich die Grenze. Namen, die erst zur Laufzeit zusammengesetzt werden, findet er nicht, eigenes SVG nimmt das Plugin nicht auf, die Icon-Daten verändert es nicht, und andere Bundler als Vite bedient es nicht.

Auf Nuxt ist die dortige Icon-Integration die passendere Wahl: Sie bringt denselben Scan bereits mit und fällt für eine nicht installierte Collection auf die API zurück, wo dieses Plugin den Build scheitern lässt.

## install

```pnpm
pnpm add -D @kirchdev/vite-plugin-iconify-bundle @iconify-json/lucide
```

```npm
npm install -D @kirchdev/vite-plugin-iconify-bundle @iconify-json/lucide
```

Je Collection, die gescannt werden soll, ein eigenes Datenpaket; das Plugin liest deren Daten zur Bauzeit von der Platte. Die Voreinstellung listet zwei Collections und ist mit einiger Sicherheit nicht die richtige, weshalb ein Präfix ohne Eintrag gar nicht erst gescannt wird.
43 changes: 43 additions & 0 deletions projects/kirchDev/vite-plugin-iconify-bundle/en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# A Vite plugin that bundles only the Iconify icons you use

Scans your source for icon names, resolves each one against the installed collections at build time, and puts exactly those icons in the bundle. The Iconify API is never asked at runtime.

## why

The usual integration fetches icon data from the Iconify API at runtime. That fails offline, renders nothing under server-side rendering, and makes the build unpredictable. Shipping whole collections instead trades that for megabytes of icons nobody uses.

This plugin takes the third route. It searches the source for strings of the form prefix and name, resolves each against the installed collections, and emits a virtual module registering exactly those icons. What ends up in the bundle is what is actually written in the code.

## quickstart

```ts
import 'virtual:iconify-bundle';
```

A single side-effect import registers every icon the scan found. From then on they work offline, render on the server, and cost no request.

## features

- **Only the icons in use** — the bundle carries what the scan found, not the complete collection.
- **A scan, not an analysis** — the search covers all three string delimiters, so a name in a comment counts too. That is deliberate: a wrong name then fails loudly instead of quietly rendering nothing.
- **Unknown names stop the build** — an icon the installed collection does not know fails the build rather than shipping a gap.
- **Five options** — everything else is convention.
- **Survives hot module replacement** — editing a source file invalidates the virtual module, so a newly written icon name shows up without a restart.

## scope

The scan is also the boundary. Names assembled at runtime are invisible to it, the plugin takes no custom SVG, it does not transform the icon data, and it serves no bundler other than Vite.

On Nuxt the icon integration there is the better fit: it already carries the same scan and falls back to the API for a collection you have not installed, where this plugin fails the build.

## install

```pnpm
pnpm add -D @kirchdev/vite-plugin-iconify-bundle @iconify-json/lucide
```

```npm
npm install -D @kirchdev/vite-plugin-iconify-bundle @iconify-json/lucide
```

One data package per collection you want scanned; the plugin reads their data off disk at build time. The default lists two collections and is almost certainly not yours, which is why a prefix with no entry never reaches the scan at all.
12 changes: 12 additions & 0 deletions projects/kirchDev/vite-plugin-iconify-bundle/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"$schema": "../../../schema/project.schema.json",
"kind": "oss",
"category": "library",
"technologies": ["typescript", "vite", "node", "vitest", "pnpm"],
"links": [
{
"type": "npm",
"url": "https://www.npmjs.com/package/@kirchdev/vite-plugin-iconify-bundle"
}
]
}
33 changes: 33 additions & 0 deletions projects/kirchDev/workflows/de.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Wiederverwendbare GitHub-Actions-Workflows für eine ganze Estate

Der Rumpf eines Workflows liegt einmal zentral, jedes andere Repository trägt nur noch einen Aufruf samt Auslöser. Ein Fix wird einmal geschrieben, und alle übernehmen ihn beim nächsten Bump.

## why

Die kanonischen Workflows lagen vorher in einer Vorlage und wurden in jedes neue Repository kopiert, wo sie sofort auseinanderliefen. Über die 27 Repositories beider Eigentümer gemessen: Der CI-Workflow existierte in 25 Repositories als 24 Varianten, der zum Landen der Queue in 26 Repositories als 8 Varianten zwischen 189 und 397 Zeilen, die Code-Analyse in 18 als 15.

Den Punkt beweist der Ausreißer: Ein Workflow lag in 20 Repositories byte-identisch, 760 Zeilen für eine einzige Sache. Ein Fix bedeutete zwanzig Commits, und nichts sagte einem, welches Repository man dabei übersehen hatte. Ein Aufruf hat dieses Problem nicht, weil der Rumpf nur an einer Stelle steht.

## quickstart

```yaml
jobs:
promotion-pr:
uses: kirchDev/workflows/.github/workflows/_promotion-pr.yml@9f3c1a2
```

Der Aufruf im Repository trägt den Auslöser und sonst nichts. Acht Zeilen, an denen nichts mehr driften kann.

## features

- **Rumpf und Aufruf getrennt** — der Auslöser bleibt in dem Repository, in dem er feuert, die Logik liegt zentral und wird dort einmal gepflegt.
- **Die Topologie ist die Konfiguration** — der Promotion-Pull-Request findet sein Ziel selbst, indem er prüft, ob ein Stage-Branch existiert. Kein Schalter, keine zweite Variante.
- **Auf einen Commit festgenagelt** — Aufrufer pinnen einen SHA, ein Push in die Rümpfe erreicht also niemanden, bevor ein Bump geprüft und gemerged ist.
- **Benannte Secrets** — ein Aufruf reicht genau das durch, was sein Rumpf braucht, statt pauschal alles zu vererben.
- **Am eigenen Futter geprüft** — dieses Repository trägt denselben Aufruf wie alle anderen und ruft die eigenen Rümpfe über lokale Pfade auf, ein kaputter Rumpf fällt also hier zuerst auf.

## scope

Keine allgemeine Sammlung von Actions. Die Rümpfe kodieren die Branch-Topologie dieser Estate, die Ablage der Secrets je Eigentümer und die hauseigene Coverage-Action; wer sie einbindet, übernimmt diese Konventionen mit.

Im aufrufenden Repository bleibt außerdem, was dorthin gehört: die Deploy-Jobs und der manuelle Auslöser, der eine Queue landet. Ein wiederverwendbarer Workflow lässt sich nicht dispatchen, weshalb genau dieser menschliche Halt nachweislich in dem Repository liegt, das er schützt.
Loading