zplCore.comzplCore.com

27Testing+, zplcore check & GitHub Action

Etiketten sind Code – testen Sie sie wie Code. ZplCore.Testing prüft, was ein Etikett druckt, nicht nur wie es aussieht: jeder Barcode wird mit zwei Decodern zurückgelesen, jedes Element vermessen. Schlägt etwas fehl, nennt die Meldung Element, Position (Dots, mm, ZPL-Zeile) und ein Bild mit markiertem Element. Läuft mit xUnit, NUnit, MSTest und jedem anderen Runner.

dotnet add reference src/ZplCore.Testing/ZplCore.Testing.csproj
// using ZplCore.Compliance; using ZplCore.Testing;

ZplAssert.Label(zpl)
    .HasLabels(1)
    .NoOverlaps()
    .NoClipping()
    .Complies(CompliancePacks.Gs1LogisticLabel)
    .Barcode("SSCC").Decodes("(00)376104250021234569").HasGrade(atLeast: 'B').HasQuietZones()
    .And.Text("RETAIL DC").IsWithinLabel().DoesNotOverlap("SSCC")
    .And.FitsPrinter(ZplCore.Twin.PrinterProfiles.Get("ZD421d-203"))
    .MatchesSnapshot("pallet");          // Pixel + Semantik, Freigabe mit ZPLCORE_ACCEPT_SNAPSHOTS=1

Fehlermeldung mit Bild

Palettenetikett, der SSCC-Barcode ist rot markiert

Was geprüft werden kann

  • HasLabels, Renders, NoOverlaps (echte Druckfarbe, Rahmen zählen nicht), NoClipping
  • Complies(CompliancePacks…), AllBarcodesDecode(), AllBarcodesHaveGrade('C')
  • Barcode("SSCC"): Decodes, HasGrade, HasQuietZones, HasXDimension, IsGs1
  • Text("…"): IsWithinLabel, DoesNotOverlap, IsAbove/IsBelow, HasCharHeight
  • FitsPrinter(PrinterProfile) mit dem Printer Twin, FieldEquals("FN1", …)
  • MatchesSnapshot: Pixel und Semantik – ein fehlschlagender Snapshot sagt, was sich geändert hat („Text verschoben um +12 Dots“)
Label(zpl).Barcode("SSCC").Decodes("(00)376104250021234576") failed: the decoders read "(00)376104250021234569", expected "(00)376104250021234576".
  element     ^BC Code128 "(00)376104250021234569" at x=94 y=800 (11.8 / 100.1 mm), 624 × 260 dots (78.1 × 32.5 mm) · line 17, col 1
  source      ^FO94,800^BCN,260,Y,N,N,D^FD(00)376104250021234569^FS
  decoders    zxing-cpp: "(00)376104250021234569" · ZXing.Net: "(00)376104250021234569"

CI ohne Testprojekt: zplcore check

Dieselben Prüfungen über viele Dateien: Rendern, Layout, Compliance-Pakete, Scan-Proof, Druckerprofil, Snapshots und Vergleich mit dem Basis-Branch – Ausgabe als Text, JUnit-XML, SARIF, JSON, Markdown (für PR-Kommentare) oder GitHub-Annotationen. Exit-Codes 0/1/2.

zplcore check "labels/**/*.zpl" --pack gs1-logistic --verify --min-grade C --layout --printer ZD421d-203
zplcore check labels/ --snapshots tests/__snapshots__ --format junit -o labels.junit.xml
zplcore check labels/ --baseline base/ --report-dir report/ --format markdown -o report.md   # vorher/nachher/diff-PNGs
zplcore snapshot accept tests/__snapshots__

GitHub Action

Die Composite Action tools/github-action prüft in jedem Pull Request die geänderten Etikettendateien gegen den Basis-Branch und schreibt Annotationen, eine Job-Zusammenfassung, einen aktualisierten PR-Kommentar mit Vorher-/Nachher-/Diff-Bildern und SARIF für Code Scanning. Sie liegt im Lieferumfang; die Veröffentlichung im Marketplace als zplcore/label-check ist vorgesehen. Bis dahin: Ordner ins eigene Repository kopieren und die CLI aus Ihrem Paket-Feed installieren lassen.

name: Label check
on:
  pull_request:
    paths: ["**/*.zpl", "**/*.json"]
permissions:
  contents: read
  pull-requests: write     # PR-Kommentar
  security-events: write   # SARIF → Code Scanning
jobs:
  labels:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: ./.github/actions/label-check          # Kopie von tools/github-action
        with:
          packs: gs1-logistic
          min-grade: C
          nuget-source: https://nuget.example.com/v3/index.json   # Feed mit dem Paket ZplCore.Cli

Die Note ist eine digitale Vorprüfung nach dem Vorbild von ISO/IEC 15415/15416, keine ISO-Verifikation; Compliance-Pakete sind Pre-Checks, keine Zertifizierungen. Fork-PRs ohne Schreibrechte bekommen statt Kommentar und SARIF nur Annotationen und die Job-Zusammenfassung.

Gates in der CI und im Container-Build

Eine fehlerhafte Vorlage, die in Produktion ankommt, wird teuer: Barcodes, die an der Rampe nicht scannen, stehende Linien, Sendungen, die der Versanddienstleister ablehnt. Dieselben Prüfungen laufen deshalb überall, wo sich eine Vorlage ändert – im Commit, im Pull Request, in der Pipeline und im Container-Build – und entscheiden deterministisch: gleiche Vorlage, gleiches Ergebnis, Exit-Code 0 oder 1.

CI-Gate (jede Pipeline)

# Exit-Code 1 bei Fehlern, Berichte für die Pipeline
zplcore check templates/ --pack gs1-logistic \
  --verify --min-grade B --printer ZD421d-203 \
  --junit out/labels.junit.xml --sarif out/labels.sarif

Auch in Azure DevOps, GitLab CI oder Jenkins: CLI, Exit-Codes und JUnit-XML.

Gate im Container-Build

# Build-Stufe: Vorlagen prüfen, bevor ein Image entsteht
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS label-gate
WORKDIR /src
COPY nuget.config ./
COPY .config/ .config/
COPY templates/ templates/
RUN dotnet tool restore \
 && dotnet zplcore check templates/ --pack gs1-logistic \
    --verify --min-grade B --printer ZD421d-203
FROM mcr.microsoft.com/dotnet/aspnet:10.0
# nur geprüfte Vorlagen landen im Image
COPY --from=label-gate /src/templates /app/templates

So sieht ein gestopptes Gate aus (echte Ausgabe, gekürzt):

PASS  templates/dhl-100x150.zpl
FAIL  templates/express-100x150.zpl
  FAIL comply gs1-logistic
    :38:1  error ZPL3104  X-dimension 0.375 mm < 0.495 mm
    :38:1  error ZPL3105  bar height 13.0 mm < 31.75 mm
  FAIL scan-proof
    :32:1  error ZT1301   QR Code grade D, required B
  ok   printer ZD421d (ZD421d-203)
FAILED (fail-on error) · exit 1

Schnitte und RFID im Gate. Eine kleine Änderung an ^MM oder ^PQ schneidet plötzlich jedes Label oder keines; ein festes ^RF schreibt dieselbe EPC auf jede Kopie. Beides fällt meist erst an der Linie auf – im Gate ist es vorher geklärt (Regel ZPL3909: dieselbe EPC auf mehreren Labels; ZPL3904: EPC passt nicht zum Barcode).

# CI: RFID-Paket im Gate, EPC je Label anzeigen
zplcore check templates/ --pack gs1-logistic,rfid
zplcore rfid simulate templates/palette.zpl
# Staging: Drucker mit Cutter und RFID simulieren
zplcore serve --profile ZT411-203 --option cutter,rfid --rfid --cut-marks
// Schnitte: wo der Cutter schneidet
var plan = ZplCutPlan.Analyze(zpl);
Assert.Equal([2, 5], plan.CutAfter);            // nach Label 3 und 6
// RFID: EPC passt zum Barcode, keine doppelte EPC
ZplAssert.Label(zpl)
    .Complies(CompliancePacks.Rfid)
    .FitsPrinter(PrinterProfiles.Get("ZT411-203").WithOptions(PrinterOption.Cutter | PrinterOption.Rfid).AsPrinterFit());

Von KI erklärt, von Menschen freigegeben. Ein Coding-Agent (Claude Code, GitHub Copilot, Cursor) prüft Änderungen über zplcore-mcp (comply, verify, printer_check, render, diff), erklärt die Befunde und schlägt die Korrektur als Commit vor. Die Freigabe bleibt bei Ihnen, z. B. mit Pflicht-Reviewern für Label-Änderungen über CODEOWNERS und Branch-Schutz.