42 Commits
Author SHA1 Message Date
DragonSlayer_14andClaude Sonnet 5 c57d2da457 CI: Fuegt automatisiertes Nightly-Release und Testing-Registry-Publish hinzu, behebt Zeitzone bei geplanten Workflows
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
Nightly Build & Publish / Erkenne relevante Code-Änderungen (push) Waiting to run
Nightly Build & Publish / Build & Publish Nightly-Crate zur Gitea Registry (push) Blocked by required conditions
Code Quality (Auto-Format & Clippy-Fix) / Formatierung & Clippy automatisch beheben (push) In progress
Security Scans / Trivy & OSV-Scanner (push) In progress
TruffleHog Secret Scan / TruffleHog (push) In progress
- Neuer Workflow "nightly-pr.yaml": Erstellt und merged taeglich automatisch
  einen Pull Request von dev nach nightly (09:00 Uhr Europe/Berlin); legt den
  nightly-Branch bei Bedarf initial von dev an.
- Neuer Workflow "nightly.yaml": Testet, paketiert und veroeffentlicht bei
  Aenderungen auf nightly eine Pre-Release-Version (-nightly.<Zeitstempel>)
  in die Gitea Cargo Registry; erstellt bewusst kein Gitea-Release.
- testing.yaml veroeffentlicht nun zusaetzlich eine Pre-Release-Version
  (-testing.<Zeitstempel>) in die Gitea Cargo Registry, zusaetzlich zum
  bisherigen Preview-Release mit Crate-Anhang.
- Beide Versions-Suffixe sind SemVer-Pre-Releases, wodurch Renovates
  Standardverhalten (ignoreUnstable) sie automatisch ignoriert.
- CRON_TZ=Europe/Berlin in security-scan.yaml und trufflehog-scan.yaml
  ergaenzt: Gitea Actions interpretiert schedule-Cron sonst in der lokalen
  Zeitzone des Gitea-Servers statt in der beabsichtigten Zeitzone.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 00:27:42 +02:00
DragonSlayer_14 a14264b541 Feat: Hebt Version auf 1.1.0
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
TruffleHog Secret Scan / TruffleHog (push) Successful in 16s
Code Quality (Auto-Format & Clippy-Fix) / Formatierung & Clippy automatisch beheben (push) Successful in 30s
Security Scans / Trivy & OSV-Scanner (push) Successful in 43s
Auto Patch-Version-Bump (Dev → Testing PR) / Erkenne relevante Änderungen im PR (pull_request) Successful in 13s
TruffleHog Secret Scan / TruffleHog (pull_request) Successful in 22s
Auto Patch-Version-Bump (Dev → Testing PR) / Patch-Version erhöhen & auf Dev pushen (pull_request) Successful in 10s
Security Scans / Trivy & OSV-Scanner (pull_request) Successful in 42s
Unit-Tests / Unit-Tests (pull_request) Successful in 40s
2026-09-20 23:50:31 +02:00
DragonSlayer_14 b70db3d434 Merge remote-tracking branch 'origin/dev' into dev
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
TruffleHog Secret Scan / TruffleHog (push) Successful in 16s
Code Quality (Auto-Format & Clippy-Fix) / Formatierung & Clippy automatisch beheben (push) Successful in 28s
Security Scans / Trivy & OSV-Scanner (push) Successful in 32s
2026-09-20 23:49:29 +02:00
DragonSlayer_14andClaude Sonnet 5 5d15c15cb0 Fügt farbige Terminalausgabe je nach Log-Level hinzu
Die komplette Ausgabezeile wird nun abhängig vom Schweregrad eingefärbt
(Error rot, Warn gelb, Info grün, Debug cyan), aber nur wenn stdout/stderr
ein echtes Terminal ist und weder NO_COLOR noch TERM=dumb gesetzt sind.
Die Logdatei bleibt unverändert klartextbasiert ohne ANSI-Codes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 23:49:20 +02:00
DragonSlayer_14andClaude Sonnet 5 91189e0cfc CI: Renovate-Schedule von wöchentlich auf täglich verkürzt
'before 6am on monday' erlaubte nur einmal pro Woche automatische
PR-Erstellung - jeder außerhalb dieses Fensters gefundene Update musste
im Dependency-Dashboard-Issue erst manuell abgehakt werden ("Awaiting
Schedule"), auch nach der letzten Automerge-Änderung. 'before 6am' (ohne
Wochentag) öffnet das Fenster jetzt täglich, PRs für sichere Updates
laufen dadurch spürbar zeitnaher und größtenteils ohne manuelles
Abhaken durch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 23:49:20 +02:00
DragonSlayer_14andClaude Sonnet 5 ec1066a56d CI: Renovate merged sichere Updates automatisch, unit-tests laufen jetzt auch für PRs gegen dev
Patch-/Minor-/Digest-Updates (Cargo, Gitea Actions, Docker-Images)
werden künftig automatisch gemergt, sobald alle CI-Checks erfolgreich
sind - Major-Updates bleiben weiterhin manuell zu prüfen, da sie eher
brechende Änderungen enthalten.

Voraussetzung dafür: unit-tests.yaml (der eigentliche 'cargo test'-Lauf)
löste bisher nur bei Pull Requests gegen 'testing' aus, nicht gegen
'dev' - Renovate-PRs zielen aber auf 'dev' und wurden damit nie durch
einen echten Build/Test abgesichert, nur durch die Security-/Secret-
Scans. Jetzt läuft er auch dort.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 23:49:20 +02:00
DragonSlayer_14andClaude Sonnet 5 56e8c5c8a3 CI: Ersetzt rohes actions/cache für Cargo-Build-Artefakte durch Swatinem/rust-cache
Ein rohes 'actions/cache' auf target/ lieferte zwar einen technischen
Cache-Hit, Cargo kompilierte die Abhängigkeiten aber trotzdem neu: der
tar-basierte Restore-Vorgang setzt bei allen wiederhergestellten
Dateien dieselbe Mtime, wodurch Cargos Fingerprinting nicht mehr
zuverlässig erkennen kann, was älter/neuer als was ist. Swatinem/rust-cache
behebt das gezielt (u. a. gezielte Mtime-Korrektur nach dem Restore).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 23:49:20 +02:00
Renovate-Bot b96e4a62a6 chore(deps): update gitea actions 2026-09-20 23:49:20 +02:00
Renovate-Bot e216dd07c8 chore(deps): update gitea actions 2026-09-20 23:49:20 +02:00
DragonSlayer_14andClaude Sonnet 5 22515c4fc5 CI: Renovate-Schedule von wöchentlich auf täglich verkürzt
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
TruffleHog Secret Scan / TruffleHog (push) Successful in 27s
Code Quality (Auto-Format & Clippy-Fix) / Formatierung & Clippy automatisch beheben (push) Successful in 56s
Security Scans / Trivy & OSV-Scanner (push) Successful in 1m11s
'before 6am on monday' erlaubte nur einmal pro Woche automatische
PR-Erstellung - jeder außerhalb dieses Fensters gefundene Update musste
im Dependency-Dashboard-Issue erst manuell abgehakt werden ("Awaiting
Schedule"), auch nach der letzten Automerge-Änderung. 'before 6am' (ohne
Wochentag) öffnet das Fenster jetzt täglich, PRs für sichere Updates
laufen dadurch spürbar zeitnaher und größtenteils ohne manuelles
Abhaken durch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 23:36:52 +02:00
DragonSlayer_14andClaude Sonnet 5 490b8731a7 CI: Renovate merged sichere Updates automatisch, unit-tests laufen jetzt auch für PRs gegen dev
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
TruffleHog Secret Scan / TruffleHog (push) Successful in 29s
Code Quality (Auto-Format & Clippy-Fix) / Formatierung & Clippy automatisch beheben (push) Successful in 56s
Security Scans / Trivy & OSV-Scanner (push) Successful in 1m8s
Patch-/Minor-/Digest-Updates (Cargo, Gitea Actions, Docker-Images)
werden künftig automatisch gemergt, sobald alle CI-Checks erfolgreich
sind - Major-Updates bleiben weiterhin manuell zu prüfen, da sie eher
brechende Änderungen enthalten.

Voraussetzung dafür: unit-tests.yaml (der eigentliche 'cargo test'-Lauf)
löste bisher nur bei Pull Requests gegen 'testing' aus, nicht gegen
'dev' - Renovate-PRs zielen aber auf 'dev' und wurden damit nie durch
einen echten Build/Test abgesichert, nur durch die Security-/Secret-
Scans. Jetzt läuft er auch dort.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 23:27:39 +02:00
DragonSlayer_14andClaude Sonnet 5 d799935f26 CI: Ersetzt rohes actions/cache für Cargo-Build-Artefakte durch Swatinem/rust-cache
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
Code Quality (Auto-Format & Clippy-Fix) / Formatierung & Clippy automatisch beheben (push) Successful in 1m14s
Security Scans / Trivy & OSV-Scanner (push) Successful in 1m9s
TruffleHog Secret Scan / TruffleHog (push) Successful in 26s
Ein rohes 'actions/cache' auf target/ lieferte zwar einen technischen
Cache-Hit, Cargo kompilierte die Abhängigkeiten aber trotzdem neu: der
tar-basierte Restore-Vorgang setzt bei allen wiederhergestellten
Dateien dieselbe Mtime, wodurch Cargos Fingerprinting nicht mehr
zuverlässig erkennen kann, was älter/neuer als was ist. Swatinem/rust-cache
behebt das gezielt (u. a. gezielte Mtime-Korrektur nach dem Restore).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 23:18:41 +02:00
DragonSlayer_14 844b5fcd43 Merge pull request 'chore(deps): update gitea actions' (#19) from renovate/gitea-actions into dev
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
Code Quality (Auto-Format & Clippy-Fix) / Formatierung & Clippy automatisch beheben (push) Successful in 54s
TruffleHog Secret Scan / TruffleHog (push) Successful in 25s
Security Scans / Trivy & OSV-Scanner (push) Successful in 1m6s
Reviewed-on: #19
Reviewed-by: dragonslayer14@murena.io <4+dragonslayer_14@noreply.localhost>
2026-09-20 09:01:51 +00:00
Renovate-Bot d77d76a421 chore(deps): update gitea actions
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
Unit-Tests / Unit-Tests (pull_request) Skipped
TruffleHog Secret Scan / TruffleHog (push) Successful in 16s
TruffleHog Secret Scan / TruffleHog (pull_request) Successful in 16s
Security Scans / Trivy & OSV-Scanner (pull_request) Successful in 32s
2026-09-20 03:06:59 +00:00
DragonSlayer_14 5f1cdb3a9c Merge pull request 'chore(deps): update gitea actions' (#18) from renovate/gitea-actions into dev
TruffleHog Secret Scan / TruffleHog (push) Successful in 21s
Security Scans / Trivy & OSV-Scanner (push) Successful in 30s
Code Quality (Auto-Format & Clippy-Fix) / Formatierung & Clippy automatisch beheben (push) Successful in 44s
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
Reviewed-on: #18
Reviewed-by: dragonslayer14@murena.io <4+dragonslayer_14@noreply.localhost>
2026-09-14 10:53:32 +00:00
Renovate-Bot be92ae4fc4 chore(deps): update gitea actions
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
Unit-Tests / Unit-Tests (pull_request) Skipped
TruffleHog Secret Scan / TruffleHog (push) Successful in 16s
TruffleHog Secret Scan / TruffleHog (pull_request) Successful in 13s
Security Scans / Trivy & OSV-Scanner (pull_request) Successful in 22s
2026-09-14 09:07:09 +00:00
DragonSlayer_14 1e2d4cf187 Merge pull request 'Merge testing in main: Release 1.0.5' (#17) from testing into main
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
Main Release & Publish / Erkenne relevante Code-Änderungen (push) Successful in 7s
Security Scans / Trivy & OSV-Scanner (push) Successful in 37s
Main Release & Publish / Build, Publish Crate to Gitea Registry & Create Release (push) Successful in 1m16s
TruffleHog Secret Scan / TruffleHog (push) Successful in 21s
Reviewed-on: #17
2026-09-13 12:44:49 +00:00
DragonSlayer_14 bd8e8c1550 Merge pull request 'Merge dev in testing: Aktualisiert dependencys und Workflows' (#16) from dev into testing
Unit-Tests / Unit-Tests (pull_request) Skipped
Testing Build, Check & Preview Release / Erkenne relevante Code-Änderungen (push) Successful in 9s
Auto-PR (Testing → Main) / Erstelle automatisch PR von testing nach main (push) Successful in 14s
Security Scans / Trivy & OSV-Scanner (push) Successful in 42s
TruffleHog Secret Scan / TruffleHog (pull_request) Successful in 31s
Security Scans / Trivy & OSV-Scanner (pull_request) Successful in 55s
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Successful in 1m11s
TruffleHog Secret Scan / TruffleHog (push) Successful in 25s
Reviewed-on: #16
2026-09-13 12:40:31 +00:00
Gitea-Bot cbff7f3351 Chore: Erhöht Patch-Version auf 1.0.5 für Promotion nach testing
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
TruffleHog Secret Scan / TruffleHog (push) Successful in 28s
TruffleHog Secret Scan / TruffleHog (pull_request) Successful in 29s
Security Scans / Trivy & OSV-Scanner (push) Successful in 54s
Security Scans / Trivy & OSV-Scanner (pull_request) Successful in 56s
Code Quality (Auto-Format & Clippy-Fix) / Formatierung & Clippy automatisch beheben (push) Successful in 57s
Unit-Tests / Unit-Tests (pull_request) Successful in 1m1s
2026-09-13 12:36:38 +00:00
DragonSlayer_14 a927feb55f Merge branch 'dev' of origin into dev
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
TruffleHog Secret Scan / TruffleHog (push) Successful in 26s
Security Scans / Trivy & OSV-Scanner (push) Successful in 52s
Code Quality (Auto-Format & Clippy-Fix) / Formatierung & Clippy automatisch beheben (push) Successful in 57s
Auto Patch-Version-Bump (Dev → Testing PR) / Erkenne relevante Änderungen im PR (pull_request) Successful in 9s
TruffleHog Secret Scan / TruffleHog (pull_request) Successful in 20s
Auto Patch-Version-Bump (Dev → Testing PR) / Patch-Version erhöhen & auf Dev pushen (pull_request) Successful in 11s
Security Scans / Trivy & OSV-Scanner (pull_request) Successful in 30s
Unit-Tests / Unit-Tests (pull_request) Successful in 44s
2026-09-13 14:28:04 +02:00
DragonSlayer_14andClaude Sonnet 5 d902812919 Feat: Fügt automatischen PR-Workflow für Testing→Main hinzu
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AjPGeW28DpjnMkuvLSRTKv
2026-09-13 14:26:01 +02:00
DragonSlayer_14 b832548f95 Merge pull request 'chore(deps): update ghcr.io/renovatebot/renovate docker tag to v44.83.0' (#15) from renovate/gitea-actions into dev
TruffleHog Secret Scan / TruffleHog (push) Successful in 25s
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
Security Scans / Trivy & OSV-Scanner (push) Successful in 34s
Code Quality (Auto-Format & Clippy-Fix) / Formatierung & Clippy automatisch beheben (push) Successful in 37s
Reviewed-on: #15
Reviewed-by: dragonslayer14@murena.io <4+dragonslayer_14@noreply.localhost>
2026-09-13 12:16:00 +00:00
DragonSlayer_14 5709046b50 Merge pull request 'chore(deps): update rust crate program-ctdra to v1.0.1' (#14) from renovate/cargo-dependencies into dev
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
Security Scans / Trivy & OSV-Scanner (push) Successful in 24s
TruffleHog Secret Scan / TruffleHog (push) Successful in 15s
Code Quality (Auto-Format & Clippy-Fix) / Formatierung & Clippy automatisch beheben (push) Successful in 26s
Reviewed-on: #14
2026-09-13 12:13:33 +00:00
DragonSlayer_14andClaude Sonnet 5 e0cd7b8f3f Feat: Fügt automatischen Patch-Version-Bump für Dev→Testing-PRs hinzu
TruffleHog Secret Scan / TruffleHog (push) Successful in 28s
Code Quality (Auto-Format & Clippy-Fix) / Formatierung & Clippy automatisch beheben (push) Successful in 57s
Security Scans / Trivy & OSV-Scanner (push) Successful in 55s
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DrWxHCG7URf4FEy8Hja23f
2026-09-13 14:04:51 +02:00
Renovate-Bot b452f8406d chore(deps): update ghcr.io/renovatebot/renovate docker tag to v44.83.0
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
TruffleHog Secret Scan / TruffleHog (push) Successful in 10s
TruffleHog Secret Scan / TruffleHog (pull_request) Successful in 28s
Security Scans / Trivy & OSV-Scanner (pull_request) Successful in 53s
Unit-Tests / Unit-Tests (pull_request) Skipped
2026-09-13 12:02:20 +00:00
Renovate-Bot 0b82945d81 chore(deps): update rust crate program-ctdra to v1.0.1
TruffleHog Secret Scan / TruffleHog (push) Successful in 15s
Security Scans / Trivy & OSV-Scanner (pull_request) Successful in 20s
TruffleHog Secret Scan / TruffleHog (pull_request) Successful in 10s
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
Unit-Tests / Unit-Tests (pull_request) Skipped
2026-09-13 12:02:16 +00:00
DragonSlayer_14andClaude Sonnet 5 2ca8715cb7 Feat: Überspringt Release/Publish bei bereits veröffentlichter Version
Security Scans / Trivy & OSV-Scanner (push) Successful in 52s
TruffleHog Secret Scan / TruffleHog (push) Successful in 28s
Code Quality (Auto-Format & Clippy-Fix) / Formatierung & Clippy automatisch beheben (push) Successful in 56s
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
Prüft vor Build & Tests per Gitea Package-API, ob die aktuelle Crate-Version
schon in der Registry existiert, und bricht den Release/Publish-Job dann
gar nicht erst an, statt an einem Registry-Fehler zu scheitern.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K2DxAVavuu4vMb2MC2G15g
2026-09-13 12:34:09 +02:00
DragonSlayer_14 7bf0e52271 Merge pull request 'Merge testing in main: Passt Workflows an' (#13) from testing into main
TruffleHog Secret Scan / TruffleHog (push) Successful in 28s
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
Security Scans / Trivy & OSV-Scanner (push) Successful in 52s
Main Release & Publish / Build, Publish Crate to Gitea Registry & Create Release (push) Failing after 1m13s
Main Release & Publish / Erkenne relevante Code-Änderungen (push) Successful in 13s
Reviewed-on: #13
2026-09-13 10:19:17 +00:00
DragonSlayer_14 8b1ede0af3 Merge pull request 'Merge dev in testing: Passt Workflows an' (#12) from dev into testing
Unit-Tests / Unit-Tests (pull_request) Skipped
Testing Build, Check & Preview Release / Erkenne relevante Code-Änderungen (push) Successful in 13s
TruffleHog Secret Scan / TruffleHog (push) Successful in 29s
TruffleHog Secret Scan / TruffleHog (pull_request) Successful in 26s
Security Scans / Trivy & OSV-Scanner (push) Successful in 55s
Security Scans / Trivy & OSV-Scanner (pull_request) Successful in 50s
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Successful in 1m8s
Reviewed-on: #12
2026-09-13 10:14:55 +00:00
DragonSlayer_14andClaude Sonnet 5 e9280d2ee5 Feat: Baut Pakete nur bei tatsächlichen Code-Änderungen
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
TruffleHog Secret Scan / TruffleHog (push) Successful in 26s
Code Quality (Auto-Format & Clippy-Fix) / Formatierung & Clippy automatisch beheben (push) Successful in 57s
Security Scans / Trivy & OSV-Scanner (push) Successful in 1m12s
TruffleHog Secret Scan / TruffleHog (pull_request) Successful in 29s
Security Scans / Trivy & OSV-Scanner (pull_request) Successful in 45s
Unit-Tests / Unit-Tests (pull_request) Successful in 57s
Fügt einen vorgeschalteten detect-changes-Job ein, der mittels
dorny/paths-filter@v4 prüft, ob sich Programmcode (src/, Cargo.toml,
Cargo.lock, scripts/, .cargo/ bzw. die jeweilige Workflow-Datei selbst)
geändert hat. Der Build-/Publish-/Release-Job wird nur noch ausgeführt,
wenn das der Fall ist; bei reinen Config- oder Workflow-Änderungen
schließt der Workflow erfolgreich ohne Build/Release ab.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TcFzG54jW33nvp1TawCZJV
2026-09-13 11:39:21 +02:00
DragonSlayer_14andClaude Sonnet 5 54d4f1da01 Fix: Verschiebt allowCustomCrateRegistries in die globale Renovate-Config
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
Code Quality (Auto-Format & Clippy-Fix) / Formatierung & Clippy automatisch beheben (push) Successful in 54s
TruffleHog Secret Scan / TruffleHog (push) Successful in 22s
Security Scans / Trivy & OSV-Scanner (push) Successful in 47s
Die Option ist repo-only nicht erlaubt und wurde in der Renovate-Workflow-
Umgebung (RENOVATE_ALLOW_CUSTOM_CRATE_REGISTRIES) gesetzt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0156cWd2mGWNQU1kBbBPWpD7
2026-09-13 01:18:18 +02:00
DragonSlayer_14 4f6a05dd3b Merge pull request 'Merge testing in main: Release 1.0.4' (#10) from testing into main
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Skipped
Security Scans / Trivy & OSV-Scanner (push) Successful in 55s
Main Release & Publish / Build, Publish Crate to Gitea Registry & Create Release (push) Successful in 1m17s
TruffleHog Secret Scan / TruffleHog (push) Successful in 24s
Reviewed-on: #10
2026-09-12 22:58:38 +00:00
DragonSlayer_14 2bd85e61bf Merge pull request 'Merge dev in testing: Feat: Fügt Workflows für Security-Scans, Code-Qualität, Unit-Tests und Dependency-Management hinzu sowie Python-Skript zur Erstellung von Security-Berichten. Hebt Version auf 1.0.4 an.' (#9) from dev into testing
Unit-Tests / Unit-Tests (pull_request) Skipped
Security Scans / Trivy & OSV-Scanner (push) Successful in 28s
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Successful in 40s
TruffleHog Secret Scan / TruffleHog (pull_request) Successful in 18s
Security Scans / Trivy & OSV-Scanner (pull_request) Successful in 33s
TruffleHog Secret Scan / TruffleHog (push) Successful in 20s
Reviewed-on: #9
2026-09-12 22:56:33 +00:00
Gitea-Bot 95d2ea6d9c Style: Automatische Formatierung & Clippy-Fixes
TruffleHog Secret Scan / TruffleHog (push) Successful in 16s
Security Scans / Trivy & OSV-Scanner (push) Successful in 26s
Code Quality (Auto-Format & Clippy-Fix) / Formatierung & Clippy automatisch beheben (push) Successful in 27s
TruffleHog Secret Scan / TruffleHog (pull_request) Successful in 16s
Security Scans / Trivy & OSV-Scanner (pull_request) Successful in 26s
Unit-Tests / Unit-Tests (pull_request) Successful in 30s
2026-09-12 22:36:36 +00:00
Gitea-Bot 3abd8d9c24 Style: Automatische Formatierung & Clippy-Fixes
TruffleHog Secret Scan / TruffleHog (push) Successful in 16s
Security Scans / Trivy & OSV-Scanner (push) Successful in 26s
Code Quality (Auto-Format & Clippy-Fix) / Formatierung & Clippy automatisch beheben (push) Successful in 28s
2026-09-12 22:36:04 +00:00
DragonSlayer_14 01f6948c2b Feat: Fügt Workflows für Security-Scans, Code-Qualität, Unit-Tests und Dependency-Management hinzu sowie Python-Skript zur Erstellung von Security-Berichten. Hebt Version auf 1.0.4 an.
TruffleHog Secret Scan / TruffleHog (push) Successful in 18s
Code Quality (Auto-Format & Clippy-Fix) / Formatierung & Clippy automatisch beheben (push) Successful in 35s
Security Scans / Trivy & OSV-Scanner (push) Successful in 37s
2026-09-13 00:35:25 +02:00
DragonSlayer_14 c34f094569 Merge pull request 'Mereg testing in main: Release 1.0.3' (#8) from testing into main
Main Release & Publish / Build, Publish Crate to Gitea Registry & Create Release (push) Successful in 22s
Reviewed-on: #8
2026-08-26 18:12:04 +00:00
DragonSlayer_14 84e6796455 Merge pull request 'Merge dev in testing: Ersetzt die Ermittlung des Programmnamen durch die Crate program-ctdra' (#7) from dev into testing
Testing Build, Check & Preview Release / Build, Check & Create Preview Release (push) Successful in 23s
Reviewed-on: #7
2026-08-26 18:11:10 +00:00
DragonSlayer_14 1c7c4845fb Feat: Passt Version auf 1.0.3 an. 2026-08-26 20:10:34 +02:00
DragonSlayer_14 258ee26496 Merge remote-tracking branch 'origin/main' into dev 2026-08-26 20:09:55 +02:00
DragonSlayer_14 ea4fca3f53 Feat: Fügt Integrationstests für Logging-Funktionen und verwendet für die Ermittlung des Programmnamen die Crate program-ctdra 2026-08-26 20:09:15 +02:00
DragonSlayer_14 2d181a4361 Fix: Passt LICENSE an 2026-08-26 18:54:02 +02:00
25 changed files with 1620 additions and 152 deletions
+9
View File
@@ -0,0 +1,9 @@
[registry]
default = "gitea"
[registries.gitea]
index = "sparse+https://gitea.creative-dragonslayer.de/api/packages/Rust-Crates/cargo/" # Sparse index
# index = "https://gitea.creative-dragonslayer.de/Rust-Crates/_cargo-index.git" # Git
[net]
git-fetch-with-cli = true
+52
View File
@@ -0,0 +1,52 @@
name: Code Quality (Auto-Format & Clippy-Fix)
on:
push:
branches:
- dev
workflow_dispatch:
jobs:
fix:
name: Formatierung & Clippy automatisch beheben
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v7
with:
ref: ${{ gitea.ref_name || github.ref_name }}
token: ${{ secrets.PACKAGE_TOKEN || secrets.RELEASE_TOKEN || secrets.PUBLISH_TOKEN || secrets.API_TOKEN || secrets.PAT_TOKEN || secrets.CUSTOM_TOKEN || secrets.GITEA_TOKEN || secrets.GITHUB_TOKEN || github.token }}
- name: Install Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@v2
with:
toolchain: stable
components: clippy, rustfmt
cache: false
# Ein rohes 'actions/cache' auf target/ liefert zwar einen technischen Cache-Hit (Dateien
# werden wiederhergestellt), Cargo kompiliert die Abhängigkeiten aber oft trotzdem neu:
# Der tar-basierte Restore-Vorgang setzt bei allen wiederhergestellten Dateien dieselbe
# Mtime, wodurch Cargos Fingerprinting nicht mehr zuverlässig erkennen kann, was
# älter/neuer als was ist, und sicherheitshalber alles neu baut. Swatinem/rust-cache ist
# genau dafür gebaut (u. a. gezielte Mtime-Korrektur nach dem Restore).
- name: Cache Cargo-Abhängigkeiten & Build-Artefakte
uses: Swatinem/rust-cache@v2
- name: Formatierung automatisch beheben
run: cargo fmt
- name: Clippy-Fixes automatisch anwenden
run: cargo clippy --fix --allow-dirty --allow-staged --all-targets
- name: Änderungen committen & pushen
run: |
if [ -n "$(git status --porcelain)" ]; then
git config user.name "Gitea-Bot"
git config user.email "no-reply@creativedragonslayer.de"
git add -A
git commit -m "Style: Automatische Formatierung & Clippy-Fixes"
git push origin HEAD:${{ gitea.ref_name || github.ref_name }}
else
echo "Keine Formatierungs- oder Clippy-Änderungen."
fi
+47 -2
View File
@@ -6,19 +6,63 @@ on:
- main
jobs:
detect-changes:
name: Erkenne relevante Code-Änderungen
runs-on: ubuntu-latest
outputs:
code_changed: ${{ steps.filter.outputs.code }}
version_exists: ${{ steps.check-version.outputs.exists }}
steps:
- name: Checkout Repository
uses: actions/checkout@v7
- name: Prüfe auf Änderungen am Programmcode
uses: dorny/paths-filter@v4
id: filter
with:
filters: |
code:
- 'src/**'
- 'Cargo.toml'
- 'Cargo.lock'
- 'scripts/**'
- '.cargo/**'
- '.gitea/workflows/main.yaml'
- name: Prüfe ob Version bereits in der Registry existiert
id: check-version
if: steps.filter.outputs.code == 'true'
env:
GITEA_URL: ${{ gitea.server_url || github.server_url }}
REPO_OWNER: ${{ gitea.repository_owner || github.repository_owner }}
TOKEN: ${{ secrets.PACKAGE_TOKEN || secrets.RELEASE_TOKEN || secrets.PUBLISH_TOKEN || secrets.API_TOKEN || secrets.PAT_TOKEN || secrets.CUSTOM_TOKEN || secrets.GITEA_TOKEN || secrets.GITHUB_TOKEN || github.token }}
run: |
python3 scripts/check-version-published.py
release-and-publish:
name: Build, Publish Crate to Gitea Registry & Create Release
needs: detect-changes
if: needs.detect-changes.outputs.code_changed == 'true' && needs.detect-changes.outputs.version_exists != 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v7
- name: Install Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
uses: actions-rust-lang/setup-rust-toolchain@v2
with:
toolchain: stable
cache: false
# Ein rohes 'actions/cache' auf target/ liefert zwar einen technischen Cache-Hit (Dateien
# werden wiederhergestellt), Cargo kompiliert die Abhängigkeiten aber oft trotzdem neu:
# Der tar-basierte Restore-Vorgang setzt bei allen wiederhergestellten Dateien dieselbe
# Mtime, wodurch Cargos Fingerprinting nicht mehr zuverlässig erkennen kann, was
# älter/neuer als was ist, und sicherheitshalber alles neu baut. Swatinem/rust-cache ist
# genau dafür gebaut (u. a. gezielte Mtime-Korrektur nach dem Restore).
- name: Cache Cargo-Abhängigkeiten & Build-Artefakte
uses: Swatinem/rust-cache@v2
- name: Run Tests
run: |
cargo test
@@ -58,12 +102,13 @@ jobs:
env:
GITEA_URL: ${{ gitea.server_url || github.server_url }}
REPO: ${{ gitea.repository || github.repository }}
REPO_NAME: ${{ gitea.repository_name || github.event.repository.name }}
TOKEN: ${{ secrets.PACKAGE_TOKEN || secrets.RELEASE_TOKEN || secrets.PUBLISH_TOKEN || secrets.API_TOKEN || secrets.PAT_TOKEN || secrets.CUSTOM_TOKEN || secrets.GITEA_TOKEN || secrets.GITHUB_TOKEN || github.token }}
run: |
VERSION="$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n1)"
TAG_NAME="v${VERSION}"
RELEASE_TITLE="Release ${TAG_NAME}"
RELEASE_NOTES="Automatisches Release für logger-ctdra Crate ${VERSION}."
RELEASE_NOTES="Automatisches Release für ${REPO_NAME} Crate ${VERSION}."
echo "Erstelle oder hole Release für Tag ${TAG_NAME} in ${REPO}..."
+91
View File
@@ -0,0 +1,91 @@
name: Nightly Auto-Merge (Dev → Nightly)
on:
schedule:
# Gitea Actions interpretiert 'schedule'-Cron standardmaessig in der lokalen
# Zeitzone des Gitea-Servers (anders als GitHub Actions, das immer UTC nutzt).
# Das CRON_TZ-Praefix ist eine Gitea-Erweiterung und legt die Zeitzone explizit
# und DST-sicher fest, unabhaengig von der Server-Konfiguration.
- cron: "CRON_TZ=Europe/Berlin 0 9 * * *"
workflow_dispatch:
jobs:
merge-dev-into-nightly:
name: Erstelle & merge automatisch PR von dev nach nightly
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v7
with:
fetch-depth: 0
token: ${{ secrets.PACKAGE_TOKEN || secrets.RELEASE_TOKEN || secrets.PUBLISH_TOKEN || secrets.API_TOKEN || secrets.PAT_TOKEN || secrets.CUSTOM_TOKEN || secrets.GITEA_TOKEN || secrets.GITHUB_TOKEN || github.token }}
- name: Stelle sicher, dass der nightly-Branch existiert
run: |
git fetch origin dev
if git ls-remote --exit-code --heads origin nightly > /dev/null 2>&1; then
echo "nightly-Branch existiert bereits."
else
echo "nightly-Branch existiert noch nicht, erstelle ihn initial von dev..."
git push origin origin/dev:refs/heads/nightly
fi
- name: Prüfe auf Unterschiede zwischen dev und nightly
id: diff
run: |
git fetch origin nightly
if git diff --quiet origin/nightly origin/dev; then
echo "Keine Unterschiede zwischen dev und nightly, überspringe."
echo "has_changes=false" >> "$GITHUB_OUTPUT"
else
echo "has_changes=true" >> "$GITHUB_OUTPUT"
fi
- name: Prüfe auf bereits offenen PR nach nightly
id: check_pr
if: steps.diff.outputs.has_changes == 'true'
env:
GITEA_URL: ${{ gitea.server_url || github.server_url }}
REPO: ${{ gitea.repository || github.repository }}
TOKEN: ${{ secrets.PACKAGE_TOKEN || secrets.RELEASE_TOKEN || secrets.PUBLISH_TOKEN || secrets.API_TOKEN || secrets.PAT_TOKEN || secrets.CUSTOM_TOKEN || secrets.GITEA_TOKEN || secrets.GITHUB_TOKEN || github.token }}
run: |
OPEN_PRS=$(curl -s -H "Authorization: token ${TOKEN}" "${GITEA_URL}/api/v1/repos/${REPO}/pulls?state=open&limit=50")
PR_NUMBER=$(echo "$OPEN_PRS" | jq -r '[.[] | select(.base.ref == "nightly" and .head.ref == "dev")][0].number // empty')
echo "Bereits offener dev→nightly PR: ${PR_NUMBER:-keiner}"
echo "pr_number=${PR_NUMBER}" >> "$GITHUB_OUTPUT"
- name: Erstelle PR (dev -> nightly)
id: create_pr
if: steps.diff.outputs.has_changes == 'true' && steps.check_pr.outputs.pr_number == ''
env:
GITEA_URL: ${{ gitea.server_url || github.server_url }}
REPO: ${{ gitea.repository || github.repository }}
TOKEN: ${{ secrets.PACKAGE_TOKEN || secrets.RELEASE_TOKEN || secrets.PUBLISH_TOKEN || secrets.API_TOKEN || secrets.PAT_TOKEN || secrets.CUSTOM_TOKEN || secrets.GITEA_TOKEN || secrets.GITHUB_TOKEN || github.token }}
run: |
DATE="$(date -u +%Y-%m-%d)"
PAYLOAD=$(jq -n --arg title "Nightly-Build: Merge dev in nightly (${DATE})" --arg head "dev" --arg base "nightly" \
'{title: $title, head: $head, base: $base}')
RESP=$(curl -f -s -S -X POST \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
-d "$PAYLOAD" \
"${GITEA_URL}/api/v1/repos/${REPO}/pulls")
PR_NUMBER=$(echo "$RESP" | jq -r '.number')
echo "PR #${PR_NUMBER} erstellt."
echo "pr_number=${PR_NUMBER}" >> "$GITHUB_OUTPUT"
- name: Merge PR automatisch
if: steps.diff.outputs.has_changes == 'true'
env:
GITEA_URL: ${{ gitea.server_url || github.server_url }}
REPO: ${{ gitea.repository || github.repository }}
TOKEN: ${{ secrets.PACKAGE_TOKEN || secrets.RELEASE_TOKEN || secrets.PUBLISH_TOKEN || secrets.API_TOKEN || secrets.PAT_TOKEN || secrets.CUSTOM_TOKEN || secrets.GITEA_TOKEN || secrets.GITHUB_TOKEN || github.token }}
PR_NUMBER: ${{ steps.create_pr.outputs.pr_number || steps.check_pr.outputs.pr_number }}
run: |
echo "Merge PR #${PR_NUMBER} automatisch (dev -> nightly)..."
curl -f -s -S -X POST \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"Do": "merge"}' \
"${GITEA_URL}/api/v1/repos/${REPO}/pulls/${PR_NUMBER}/merge"
echo "PR #${PR_NUMBER} erfolgreich gemerged."
+97
View File
@@ -0,0 +1,97 @@
name: Nightly Build & Publish
on:
push:
branches:
- nightly
jobs:
detect-changes:
name: Erkenne relevante Code-Änderungen
runs-on: ubuntu-latest
outputs:
code_changed: ${{ steps.filter.outputs.code }}
steps:
- name: Checkout Repository
uses: actions/checkout@v7
- name: Prüfe auf Änderungen am Programmcode
uses: dorny/paths-filter@v4
id: filter
with:
filters: |
code:
- 'src/**'
- 'Cargo.toml'
- 'Cargo.lock'
- 'scripts/**'
- '.cargo/**'
- '.gitea/workflows/nightly.yaml'
build-and-publish:
name: Build & Publish Nightly-Crate zur Gitea Registry
needs: detect-changes
if: needs.detect-changes.outputs.code_changed == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v7
- name: Install Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@v2
with:
toolchain: stable
cache: false
- name: Cache Cargo-Abhängigkeiten & Build-Artefakte
uses: Swatinem/rust-cache@v2
- name: Run Tests
run: |
cargo test
- name: Compiler Check
run: |
cargo check --all-targets
# Nightly-Builds duerfen nie als regulaeres Update erscheinen: Ein SemVer-Pre-Release-
# Suffix (-nightly.<Zeitstempel>) sorgt dafuer, dass Renovates Standardverhalten
# (ignoreUnstable: true) diese Version automatisch ignoriert, solange die Basis-Version
# in Cargo.toml selbst kein Pre-Release ist. Die Aenderung ist rein fuer den Build-
# Workspace bestimmt und wird nie zurueck in den Branch committet.
- name: Ermittle & setze Nightly-Version (temporär, wird nicht committet)
id: nightly_version
run: |
BASE_VERSION="$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n1)"
TIMESTAMP="$(date -u +%Y%m%d%H%M)"
NIGHTLY_VERSION="${BASE_VERSION}-nightly.${TIMESTAMP}"
echo "Basis-Version: ${BASE_VERSION} -> Nightly-Version: ${NIGHTLY_VERSION}"
sed -i "0,/^version = \"${BASE_VERSION}\"/s//version = \"${NIGHTLY_VERSION}\"/" Cargo.toml
echo "version=${NIGHTLY_VERSION}" >> "$GITHUB_OUTPUT"
- name: Package Crate
run: |
cargo package --allow-dirty
- name: Publish Nightly-Crate zur Gitea Package Registry
env:
GITEA_URL: ${{ gitea.server_url || github.server_url }}
REPO_OWNER: ${{ gitea.repository_owner || github.repository_owner }}
TOKEN: ${{ secrets.PACKAGE_TOKEN || secrets.RELEASE_TOKEN || secrets.PUBLISH_TOKEN || secrets.API_TOKEN || secrets.PAT_TOKEN || secrets.CUSTOM_TOKEN || secrets.GITEA_TOKEN || secrets.GITHUB_TOKEN || github.token }}
run: |
mkdir -p ~/.cargo
cat <<EOF > ~/.cargo/config.toml
[registries.gitea]
index = "sparse+${GITEA_URL}/api/packages/${REPO_OWNER}/cargo/"
EOF
cat <<EOF > ~/.cargo/credentials.toml
[registries.gitea]
token = "Bearer ${TOKEN}"
EOF
export CARGO_REGISTRIES_GITEA_INDEX="sparse+${GITEA_URL}/api/packages/${REPO_OWNER}/cargo/"
export CARGO_REGISTRIES_GITEA_TOKEN="Bearer ${TOKEN}"
echo "Veröffentliche Nightly-Crate ${{ steps.nightly_version.outputs.version }} in Gitea Cargo Package Registry..."
cargo publish --registry gitea --allow-dirty
+27
View File
@@ -0,0 +1,27 @@
name: Renovate
on:
schedule:
- cron: "0 * * * *"
workflow_dispatch:
jobs:
renovate:
name: Dependency-Updates prüfen & Pull Requests erstellen
runs-on: ubuntu-latest
container: ghcr.io/renovatebot/renovate:44.103.6
steps:
- name: Renovate ausführen
run: renovate
env:
RENOVATE_PLATFORM: gitea
RENOVATE_ENDPOINT: ${{ gitea.server_url || github.server_url }}/api/v1/
RENOVATE_TOKEN: ${{ secrets.RENOVATE_TOKEN }}
RENOVATE_REPOSITORIES: ${{ gitea.repository || github.repository }}
RENOVATE_AUTODISCOVER: "false"
RENOVATE_ALLOW_CUSTOM_CRATE_REGISTRIES: "true"
RENOVATE_GIT_AUTHOR: "Renovate Bot <renovate-bot@creativedragonslayer.de>"
RENOVATE_HOST_RULES: >-
[{"hostType":"cargo","matchHost":"${{ gitea.server_url || github.server_url }}","token":"${{ secrets.RENOVATE_TOKEN }}"}]
GITHUB_COM_TOKEN: ${{ secrets.GH_RENOVATE_TOKEN }}
LOG_LEVEL: info
+95
View File
@@ -0,0 +1,95 @@
name: Security Scans
on:
push:
branches:
- main
- testing
- dev
pull_request:
schedule:
- cron: "CRON_TZ=Europe/Berlin 0 5 * * 1"
workflow_dispatch:
jobs:
security-scan:
name: Trivy & OSV-Scanner
runs-on: ubuntu-latest
env:
TRIVY_VERSION: "0.74.0"
OSV_SCANNER_VERSION: "2.6.0"
steps:
- name: Checkout Repository
uses: actions/checkout@v7
- name: Lokales bin-Verzeichnis zum PATH hinzufügen
run: |
mkdir -p "$HOME/.local/bin"
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Cache Trivy-Binary
id: cache-trivy
uses: actions/cache@v6
with:
path: ~/.local/bin/trivy
key: trivy-bin-${{ runner.os }}-${{ env.TRIVY_VERSION }}
- name: Install Trivy
if: steps.cache-trivy.outputs.cache-hit != 'true'
run: |
curl -fsSL -o trivy.tar.gz \
"https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz"
tar -xzf trivy.tar.gz trivy
chmod +x trivy
mv trivy "$HOME/.local/bin/trivy"
rm -f trivy.tar.gz
- name: Ermittle Cache-Datum für Trivy-DB
run: echo "CACHE_DATE=$(date -u +%Y-%m-%d)" >> "$GITHUB_ENV"
- name: Cache Trivy-Schwachstellen-Datenbank
uses: actions/cache@v6
with:
path: ~/.cache/trivy
key: trivy-db-${{ runner.os }}-${{ env.CACHE_DATE }}
restore-keys: |
trivy-db-${{ runner.os }}-
- name: Run Trivy Scanner
run: |
trivy fs \
--scanners vuln,secret,misconfig \
--severity CRITICAL,HIGH \
--format json \
--output trivy-results.json \
--exit-code 0 \
.
- name: Cache OSV-Scanner-Binary
id: cache-osv-scanner
uses: actions/cache@v6
with:
path: ~/.local/bin/osv-scanner
key: osv-scanner-bin-${{ runner.os }}-${{ env.OSV_SCANNER_VERSION }}
- name: Install OSV-Scanner
if: steps.cache-osv-scanner.outputs.cache-hit != 'true'
run: |
curl -fsSL -o "$HOME/.local/bin/osv-scanner" \
"https://github.com/google/osv-scanner/releases/download/v${OSV_SCANNER_VERSION}/osv-scanner_linux_amd64"
chmod +x "$HOME/.local/bin/osv-scanner"
- name: Run OSV-Scanner
run: |
set +e
osv-scanner scan source --recursive --format json --output-file osv-results.json .
echo "OSV_EXIT=$?" >> "$GITHUB_ENV"
- name: Ergebnisse & Gitea-Issue erstellen/aktualisieren
env:
GITEA_URL: ${{ gitea.server_url || github.server_url }}
REPO: ${{ gitea.repository || github.repository }}
TOKEN: ${{ secrets.SECURITY_TOKEN }}
RUN_URL: ${{ gitea.server_url || github.server_url }}/${{ gitea.repository || github.repository }}/actions/runs/${{ gitea.run_id || github.run_id }}
run: |
python3 scripts/report-security-issue.py trivy-results.json osv-results.json
+111
View File
@@ -0,0 +1,111 @@
name: Auto-PR (Testing → Main)
on:
push:
branches:
- testing
jobs:
create-pr:
name: Erstelle automatisch PR von testing nach main
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Prüfe auf bereits offenen PR nach main
id: check_pr
env:
GITEA_URL: ${{ gitea.server_url || github.server_url }}
REPO: ${{ gitea.repository || github.repository }}
TOKEN: ${{ secrets.PACKAGE_TOKEN || secrets.RELEASE_TOKEN || secrets.PUBLISH_TOKEN || secrets.API_TOKEN || secrets.PAT_TOKEN || secrets.CUSTOM_TOKEN || secrets.GITEA_TOKEN || secrets.GITHUB_TOKEN || github.token }}
run: |
OPEN_PRS=$(curl -s -H "Authorization: token ${TOKEN}" "${GITEA_URL}/api/v1/repos/${REPO}/pulls?state=open&limit=50")
EXISTS=$(echo "$OPEN_PRS" | jq -r '[.[] | select(.base.ref == "main" and .head.ref == "testing")] | length')
echo "Bereits offene testing→main PRs: ${EXISTS}"
echo "exists=${EXISTS}" >> "$GITHUB_OUTPUT"
- name: Ermittle Versionen auf main & testing
id: versions
if: steps.check_pr.outputs.exists == '0'
run: |
git fetch origin main
MAIN_VERSION="$(git show origin/main:Cargo.toml | sed -n 's/^version = "\(.*\)"/\1/p' | head -n1)"
TESTING_VERSION="$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n1)"
echo "Version auf main: ${MAIN_VERSION} / Version auf testing: ${TESTING_VERSION}"
echo "main_version=${MAIN_VERSION}" >> "$GITHUB_OUTPUT"
echo "testing_version=${TESTING_VERSION}" >> "$GITHUB_OUTPUT"
- name: Ermittle geänderte Kategorien (main...testing)
id: categories
if: steps.check_pr.outputs.exists == '0' && steps.versions.outputs.main_version == steps.versions.outputs.testing_version
run: |
CHANGED_FILES="$(git diff --name-only origin/main...HEAD)"
echo "Geänderte Dateien main...testing:"
echo "$CHANGED_FILES"
WORKFLOWS="false"
CONFIG="false"
DOCS="false"
if echo "$CHANGED_FILES" | grep -q '^\.gitea/workflows/'; then
WORKFLOWS="true"
fi
if echo "$CHANGED_FILES" | grep -qE '^(renovate\.json|qodana\.yaml|Cargo\.toml|Cargo\.lock|\.cargo/)'; then
CONFIG="true"
fi
if echo "$CHANGED_FILES" | grep -qE '(^|/)[^/]+\.md$|^LICENSE$'; then
DOCS="true"
fi
echo "workflows=${WORKFLOWS}" >> "$GITHUB_OUTPUT"
echo "config=${CONFIG}" >> "$GITHUB_OUTPUT"
echo "docs=${DOCS}" >> "$GITHUB_OUTPUT"
- name: Bestimme PR-Titel
id: title
if: steps.check_pr.outputs.exists == '0'
run: |
if [ "${{ steps.versions.outputs.main_version }}" != "${{ steps.versions.outputs.testing_version }}" ]; then
TITLE="Merge testing in main: Release ${{ steps.versions.outputs.testing_version }}"
else
PARTS=()
[ "${{ steps.categories.outputs.workflows }}" = "true" ] && PARTS+=("Workflows")
[ "${{ steps.categories.outputs.config }}" = "true" ] && PARTS+=("Konfigurationen")
[ "${{ steps.categories.outputs.docs }}" = "true" ] && PARTS+=("Dokumentation")
if [ ${#PARTS[@]} -eq 0 ]; then
TITLE="Merge testing in main"
else
JOINED=""
for PART in "${PARTS[@]}"; do
if [ -z "$JOINED" ]; then
JOINED="$PART"
else
JOINED="${JOINED} & ${PART}"
fi
done
TITLE="Merge testing in main: ${JOINED} aktualisiert"
fi
fi
echo "Ermittelter PR-Titel: ${TITLE}"
echo "title=${TITLE}" >> "$GITHUB_OUTPUT"
- name: Erstelle PR (testing -> main)
if: steps.check_pr.outputs.exists == '0'
env:
GITEA_URL: ${{ gitea.server_url || github.server_url }}
REPO: ${{ gitea.repository || github.repository }}
TOKEN: ${{ secrets.PACKAGE_TOKEN || secrets.RELEASE_TOKEN || secrets.PUBLISH_TOKEN || secrets.API_TOKEN || secrets.PAT_TOKEN || secrets.CUSTOM_TOKEN || secrets.GITEA_TOKEN || secrets.GITHUB_TOKEN || github.token }}
TITLE: ${{ steps.title.outputs.title }}
run: |
PAYLOAD=$(jq -n --arg title "$TITLE" --arg head "testing" --arg base "main" \
'{title: $title, head: $head, base: $base}')
curl -f -s -S -X POST \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
-d "$PAYLOAD" \
"${GITEA_URL}/api/v1/repos/${REPO}/pulls"
echo "PR erstellt: ${TITLE}"
+81 -3
View File
@@ -6,19 +6,52 @@ on:
- testing
jobs:
detect-changes:
name: Erkenne relevante Code-Änderungen
runs-on: ubuntu-latest
outputs:
code_changed: ${{ steps.filter.outputs.code }}
steps:
- name: Checkout Repository
uses: actions/checkout@v7
- name: Prüfe auf Änderungen am Programmcode
uses: dorny/paths-filter@v4
id: filter
with:
filters: |
code:
- 'src/**'
- 'Cargo.toml'
- 'Cargo.lock'
- 'scripts/**'
- '.cargo/**'
- '.gitea/workflows/testing.yaml'
build-and-preview:
name: Build, Check & Create Preview Release
needs: detect-changes
if: needs.detect-changes.outputs.code_changed == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v7
- name: Install Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
uses: actions-rust-lang/setup-rust-toolchain@v2
with:
toolchain: stable
cache: false
# Ein rohes 'actions/cache' auf target/ liefert zwar einen technischen Cache-Hit (Dateien
# werden wiederhergestellt), Cargo kompiliert die Abhängigkeiten aber oft trotzdem neu:
# Der tar-basierte Restore-Vorgang setzt bei allen wiederhergestellten Dateien dieselbe
# Mtime, wodurch Cargos Fingerprinting nicht mehr zuverlässig erkennen kann, was
# älter/neuer als was ist, und sicherheitshalber alles neu baut. Swatinem/rust-cache ist
# genau dafür gebaut (u. a. gezielte Mtime-Korrektur nach dem Restore).
- name: Cache Cargo-Abhängigkeiten & Build-Artefakte
uses: Swatinem/rust-cache@v2
- name: Run Tests
run: |
cargo test
@@ -27,9 +60,54 @@ jobs:
run: |
cargo check --all-targets
- name: Ermittle Basis-Version (für Release-Tag)
id: base_version
run: |
BASE_VERSION="$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n1)"
echo "version=${BASE_VERSION}" >> "$GITHUB_OUTPUT"
# Testing-Builds duerfen nie als regulaeres Update erscheinen: Ein SemVer-Pre-Release-
# Suffix (-testing.<Zeitstempel>) sorgt dafuer, dass Renovates Standardverhalten
# (ignoreUnstable: true) diese Version automatisch ignoriert, solange die Basis-Version
# in Cargo.toml selbst kein Pre-Release ist. Die Aenderung ist rein fuer den Build-
# Workspace bestimmt und wird nie zurueck in den Branch committet; der Release-Tag
# weiter unten nutzt bewusst die zuvor ermittelte Basis-Version, damit er stabil bleibt.
- name: Ermittle & setze Testing-Version (temporär, wird nicht committet)
id: testing_version
run: |
BASE_VERSION="${{ steps.base_version.outputs.version }}"
TIMESTAMP="$(date -u +%Y%m%d%H%M)"
TESTING_VERSION="${BASE_VERSION}-testing.${TIMESTAMP}"
echo "Basis-Version: ${BASE_VERSION} -> Testing-Version: ${TESTING_VERSION}"
sed -i "0,/^version = \"${BASE_VERSION}\"/s//version = \"${TESTING_VERSION}\"/" Cargo.toml
echo "version=${TESTING_VERSION}" >> "$GITHUB_OUTPUT"
- name: Package Crate
run: |
cargo package
cargo package --allow-dirty
- name: Publish Testing-Crate zur Gitea Package Registry
env:
GITEA_URL: ${{ gitea.server_url || github.server_url }}
REPO_OWNER: ${{ gitea.repository_owner || github.repository_owner }}
TOKEN: ${{ secrets.PACKAGE_TOKEN || secrets.RELEASE_TOKEN || secrets.PUBLISH_TOKEN || secrets.API_TOKEN || secrets.PAT_TOKEN || secrets.CUSTOM_TOKEN || secrets.GITEA_TOKEN || secrets.GITHUB_TOKEN || github.token }}
run: |
mkdir -p ~/.cargo
cat <<EOF > ~/.cargo/config.toml
[registries.gitea]
index = "sparse+${GITEA_URL}/api/packages/${REPO_OWNER}/cargo/"
EOF
cat <<EOF > ~/.cargo/credentials.toml
[registries.gitea]
token = "Bearer ${TOKEN}"
EOF
export CARGO_REGISTRIES_GITEA_INDEX="sparse+${GITEA_URL}/api/packages/${REPO_OWNER}/cargo/"
export CARGO_REGISTRIES_GITEA_TOKEN="Bearer ${TOKEN}"
echo "Veröffentliche Testing-Crate ${{ steps.testing_version.outputs.version }} in Gitea Cargo Package Registry..."
cargo publish --registry gitea --allow-dirty
- name: Create Gitea Pre-Release and Upload Assets
env:
@@ -38,7 +116,7 @@ jobs:
REPO_NAME: ${{ gitea.repository_name || github.event.repository.name }}
TOKEN: ${{ secrets.PACKAGE_TOKEN || secrets.RELEASE_TOKEN || secrets.PUBLISH_TOKEN || secrets.API_TOKEN || secrets.PAT_TOKEN || secrets.CUSTOM_TOKEN || secrets.GITEA_TOKEN || secrets.GITHUB_TOKEN || github.token }}
run: |
VERSION="$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n1)"
VERSION="${{ steps.base_version.outputs.version }}"
TAG_NAME="v${VERSION}-preview"
RELEASE_TITLE="Preview Release ${TAG_NAME}"
RELEASE_NOTES="Automatisches Preview-Release für ${REPO_NAME} Crate ${VERSION} (Branch: Testing)."
+59
View File
@@ -0,0 +1,59 @@
name: TruffleHog Secret Scan
on:
push:
pull_request:
schedule:
- cron: "CRON_TZ=Europe/Berlin 0 6 * * 1"
workflow_dispatch:
jobs:
trufflehog-scan:
name: TruffleHog
runs-on: ubuntu-latest
env:
TRUFFLEHOG_VERSION: "3.97.5"
steps:
- name: Checkout Repository
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Lokales bin-Verzeichnis zum PATH hinzufügen
run: |
mkdir -p "$HOME/.local/bin"
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Cache TruffleHog-Binary
id: cache-trufflehog
uses: actions/cache@v6
with:
path: ~/.local/bin/trufflehog
key: trufflehog-bin-${{ runner.os }}-${{ env.TRUFFLEHOG_VERSION }}
- name: Install TruffleHog
if: steps.cache-trufflehog.outputs.cache-hit != 'true'
run: |
curl -fsSL -o trufflehog.tar.gz \
"https://github.com/trufflesecurity/trufflehog/releases/download/v${TRUFFLEHOG_VERSION}/trufflehog_${TRUFFLEHOG_VERSION}_linux_amd64.tar.gz"
tar -xzf trufflehog.tar.gz trufflehog
chmod +x trufflehog
mv trufflehog "$HOME/.local/bin/trufflehog"
rm trufflehog.tar.gz
- name: Run TruffleHog Scanner
run: |
set +e
trufflehog git file://. --results=verified,unknown --fail --json > trufflehog-results.json
echo "TRUFFLEHOG_EXIT=$?" >> "$GITHUB_ENV"
- name: Ergebnisse & Gitea-Issue erstellen/aktualisieren
env:
GITEA_URL: ${{ gitea.server_url || github.server_url }}
REPO: ${{ gitea.repository || github.repository }}
TOKEN: ${{ secrets.SECURITY_TOKEN }}
RUN_URL: ${{ gitea.server_url || github.server_url }}/${{ gitea.repository || github.repository }}/actions/runs/${{ gitea.run_id || github.run_id }}
ISSUE_TITLE: "Security-Scan: TruffleHog Secrets"
ISSUE_LABEL: "security-scan-trufflehog"
run: |
python3 scripts/report-security-issue.py "" "" trufflehog-results.json
+37
View File
@@ -0,0 +1,37 @@
name: Unit-Tests
on:
pull_request:
types:
- opened
- synchronize
- reopened
branches:
- testing
- dev
jobs:
test:
name: Unit-Tests
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v7
- name: Install Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@v2
with:
toolchain: stable
cache: false
# Ein rohes 'actions/cache' auf target/ liefert zwar einen technischen Cache-Hit (Dateien
# werden wiederhergestellt), Cargo kompiliert die Abhängigkeiten aber oft trotzdem neu:
# Der tar-basierte Restore-Vorgang setzt bei allen wiederhergestellten Dateien dieselbe
# Mtime, wodurch Cargos Fingerprinting nicht mehr zuverlässig erkennen kann, was
# älter/neuer als was ist, und sicherheitshalber alles neu baut. Swatinem/rust-cache ist
# genau dafür gebaut (u. a. gezielte Mtime-Korrektur nach dem Restore).
- name: Cache Cargo-Abhängigkeiten & Build-Artefakte
uses: Swatinem/rust-cache@v2
- name: Run Tests
run: cargo test
+84
View File
@@ -0,0 +1,84 @@
name: Auto Patch-Version-Bump (Dev → Testing PR)
on:
pull_request:
types: [opened]
branches:
- testing
jobs:
detect-changes:
name: Erkenne relevante Änderungen im PR
runs-on: ubuntu-latest
if: ${{ (gitea.head_ref || github.head_ref) == 'dev' }}
outputs:
code_changed: ${{ steps.filter.outputs.code }}
steps:
- name: Checkout Dev-Branch (PR-Head)
uses: actions/checkout@v7
with:
ref: ${{ gitea.head_ref || github.head_ref }}
fetch-depth: 0
- name: Prüfe auf Änderungen an Cargo.toml, Cargo.lock oder src/
uses: dorny/paths-filter@v4
id: filter
with:
base: ${{ gitea.base_ref || github.base_ref }}
filters: |
code:
- 'Cargo.toml'
- 'Cargo.lock'
- 'src/**'
bump-version:
name: Patch-Version erhöhen & auf Dev pushen
needs: detect-changes
if: needs.detect-changes.outputs.code_changed == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout Dev-Branch (PR-Head)
uses: actions/checkout@v7
with:
ref: ${{ gitea.head_ref || github.head_ref }}
fetch-depth: 0
token: ${{ secrets.PACKAGE_TOKEN || secrets.RELEASE_TOKEN || secrets.PUBLISH_TOKEN || secrets.API_TOKEN || secrets.PAT_TOKEN || secrets.CUSTOM_TOKEN || secrets.GITEA_TOKEN || secrets.GITHUB_TOKEN || github.token }}
- name: Ermittle Cargo-Version auf testing & dev
id: versions
run: |
git fetch origin testing --depth=1
TESTING_VERSION="$(git show origin/testing:Cargo.toml | sed -n 's/^version = "\(.*\)"/\1/p' | head -n1)"
DEV_VERSION="$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n1)"
echo "Version auf testing: ${TESTING_VERSION} / Version auf dev: ${DEV_VERSION}"
echo "testing_version=${TESTING_VERSION}" >> "$GITHUB_OUTPUT"
echo "dev_version=${DEV_VERSION}" >> "$GITHUB_OUTPUT"
- name: Patch-Version um 1 erhöhen (Cargo.toml & Cargo.lock)
if: steps.versions.outputs.testing_version == steps.versions.outputs.dev_version
run: |
VERSION="${{ steps.versions.outputs.dev_version }}"
MAJOR="$(echo "$VERSION" | cut -d. -f1)"
MINOR="$(echo "$VERSION" | cut -d. -f2)"
PATCH="$(echo "$VERSION" | cut -d. -f3)"
NEW_VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))"
echo "Erhöhe Version: ${VERSION} -> ${NEW_VERSION}"
sed -i "0,/^version = \"${VERSION}\"/s//version = \"${NEW_VERSION}\"/" Cargo.toml
PACKAGE_NAME="$(sed -n 's/^name = "\(.*\)"/\1/p' Cargo.toml | head -n1)"
awk -v new="$NEW_VERSION" -v pkg="$PACKAGE_NAME" '
found_name && /^version = "/ {
print "version = \"" new "\""
found_name = 0
next
}
$0 == "name = \"" pkg "\"" { found_name = 1 }
{ print }
' Cargo.lock > Cargo.lock.tmp && mv Cargo.lock.tmp Cargo.lock
git config user.name "Gitea-Bot"
git config user.email "no-reply@creativedragonslayer.de"
git add Cargo.toml Cargo.lock
git commit -m "Chore: Erhöht Patch-Version auf ${NEW_VERSION} für Promotion nach testing"
git push origin HEAD:${{ gitea.head_ref || github.head_ref }}
+2
View File
@@ -108,3 +108,5 @@ fabric.properties
# Built Visual Studio Code Extensions
*.vsix
.junie/plans
+2
View File
@@ -3,6 +3,8 @@
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/examples" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
+5 -3
View File
@@ -12,7 +12,7 @@ Diese Datei dient als Leitfaden und Kontextdokumentation für autonome Agenten u
- **Keine Unicode-Emojis im Logging**: Das Logging verwendet einheitliche ASCII-Präfixe (`[!]` Error, `[?]` Warn, `[i]` Info, `[d]` Debug).
- **Threadsicherheit**: Sämtliche Zustände (Log-Level, Dateizugriffe, Pfade) sind threadsicher gekapselt.
- **Minimaler Overhead**: Geringe Abhängigkeiten (ausschließlich `time` für formatierte Zeitstempel).
- **Minimaler Overhead**: Geringe Abhängigkeiten (`time` für formatierte Zeitstempel, `program-ctdra` zur Programmnamen-Ermittlung).
- **Lizenz**: GNU General Public License v3.0 or later (`GPL-3.0-or-later`).
- **Veröffentlichung**: Über Gitea Actions CI/CD in die Gitea Cargo Package Registry.
@@ -31,8 +31,10 @@ Logger/
├── LICENSE # GPL-3.0 Lizenztext
├── README.md # Projektdokumentation & Nutzungsbeispiele
├── AGENTS.md # Entwickler- und Agenten-Richtlinien
── src/
└── lib.rs # Hauptimplementierung (LogLevel, log, set_log_level, format_message, Tests)
── src/
└── lib.rs # Hauptimplementierung (LogLevel, log, set_log_level, format_message)
└── tests/
└── integration_tests.rs # Integrationstests für alle Modulfunktionen und Traits
```
---
Generated
+8 -1
View File
@@ -16,8 +16,9 @@ checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
[[package]]
name = "logger-ctdra"
version = "1.0.2"
version = "1.1.0"
dependencies = [
"program-ctdra",
"time",
]
@@ -51,6 +52,12 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "program-ctdra"
version = "1.0.1"
source = "sparse+https://gitea.creative-dragonslayer.de/api/packages/Rust-Crates/cargo/"
checksum = "528d5771916f74bdffb77ad60ea00c0ae4bd85f9a84e920cd65730a082133d67"
[[package]]
name = "quote"
version = "1.0.47"
+2 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "logger-ctdra"
version = "1.0.2"
version = "1.1.0"
edition = "2024"
authors = ['DragonSlayer_14']
readme = "README.md"
@@ -9,6 +9,7 @@ repository = "https://gitea.creative-dragonslayer.de/Rust-Crates/logger"
description = "Einfaches, threadsicheres Logging-Framework für Rust mit Schweregrad-Filterung, formatierten Ausgaben und automatischer Dateiprotokollierung"
[dependencies]
program-ctdra = { version = "1.0.0", registry = "gitea" }
time = { version = "0.3.55", features = ["formatting", "macros", "local-offset"] }
[profile.release]
+3 -3
View File
@@ -208,8 +208,8 @@ If you develop a new program, and you want it to be of the greatest possible use
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found.
DockerUpdater
Copyright (C) 2026 Linuxapps
logger
Copyright (C) 2026 Rust-Crates
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
@@ -221,7 +221,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:
DockerUpdater Copyright (C) 2026 Linuxapps
logger Copyright (C) 2026 Rust-Crates
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
+5 -7
View File
@@ -10,11 +10,12 @@
- `Warn` (2) — Warnungen (Ausgabe auf `stdout` mit `[?]`-Präfix)
- `Info` (3) — Betriebsinformationen (Ausgabe auf `stdout` mit `[i]`-Präfix)
- `Debug` (4) — Diagnoseausgaben (Ausgabe auf `stdout` mit `[d]`-Präfix)
- **Farbige Terminalausgabe**: Jede Zeile wird je nach Schweregrad eingefärbt (`Error` rot, `Warn` gelb, `Info` grün, `Debug` cyan). Farben werden nur ausgegeben, wenn `stdout`/`stderr` ein echtes Terminal ist (keine Umleitung in Datei/Pipe), `NO_COLOR` nicht gesetzt ist und `TERM` nicht `dumb` ist. Die Logdatei enthält stets reinen Text ohne ANSI-Codes.
- **Threadsicherheit**: Dateischreibzugriffe und Konfiguration sind thread-sicher serialisiert.
- **Einheitliches Format**: `[Präfix][DD.MM.YYYY HH:MM:SS.mmm][LEVEL][Tag]: Nachricht`
- **Flexible Konfiguration**:
- Log-Level per Code (`set_log_level`), Umgebungsvariable (`LOG_LEVEL` bzw. `RUST_LOG`) oder Fallback (`Info`).
- Einstellbarer Programmname (`set_program_name`) oder automatisches Ermitteln via `current_exe()`.
- Automatisches Ermitteln des Programmnamens via `program-ctdra` (mit zufälligem Fallback-Wert).
- Anpassbares Log-Verzeichnis (`set_log_dir`) oder automatische Ablage im Temp-Verzeichnis (`std::env::temp_dir()`).
---
@@ -81,17 +82,14 @@ fn run() {
}
```
### Konfiguration des Programmnamens und Logpfads
### Konfiguration des Logpfads
```rust
use logger_ctdra::{set_log_dir, set_program_name};
use logger_ctdra::set_log_dir;
use std::path::PathBuf;
fn init() {
// Setzt den App-Namen für das Logverzeichnis (/tmp/<app-name>-<uuid>/)
set_program_name("my-service");
// Oder ein individuelles Logverzeichnis vorgeben:
// Individuelles Logverzeichnis vorgeben:
set_log_dir(PathBuf::from("/var/log/my-service"));
}
```
+50
View File
@@ -0,0 +1,50 @@
#-------------------------------------------------------------------------------#
# Qodana analysis is configured by qodana.yaml file #
# https://www.jetbrains.com/help/qodana/qodana-yaml.html #
#-------------------------------------------------------------------------------#
#################################################################################
# WARNING: Do not store sensitive information in this file, #
# as its contents will be included in the Qodana report. #
#################################################################################
version: "1.0"
#Specify inspection profile for code analysis
profile:
name: qodana.starter
#Enable inspections
#include:
# - name: <SomeEnabledInspectionId>
#Disable inspections
#exclude:
# - name: <SomeDisabledInspectionId>
# paths:
# - <path/where/not/run/inspection>
#Execute shell command before Qodana execution (Applied in CI/CD pipeline)
#bootstrap: sh ./prepare-qodana.sh
#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline)
#plugins:
# - id: <plugin.id> #(plugin id can be found at https://plugins.jetbrains.com)
# Quality gate. Will fail the CI/CD pipeline if any condition is not met
# severityThresholds - configures maximum thresholds for different problem severities
# testCoverageThresholds - configures minimum code coverage on a whole project and newly added code
# dependencyLicenses - fails the run on prohibited or unknown dependency licenses
# Code Coverage is available in Ultimate and Ultimate Plus plans
#failureConditions:
# severityThresholds:
# any: 15
# critical: 5
# testCoverageThresholds:
# fresh: 70
# total: 50
# dependencyLicenses:
# failOnProhibited: true
# failOnUnknown: false
#Specify Qodana linter for analysis (Applied in CI/CD pipeline)
linter: jetbrains/qodana-<linter>:2026.2
+131
View File
@@ -0,0 +1,131 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
],
"timezone": "Europe/Berlin",
"schedule": [
"before 6am"
],
"baseBranchPatterns": [
"dev"
],
"packageRules": [
{
"matchFileNames": [
".gitea/workflows/**"
],
"groupName": "Gitea Actions",
"separateMajorMinor": false,
"separateMinorPatch": false
},
{
"matchManagers": [
"cargo"
],
"groupName": "Cargo Dependencies",
"separateMajorMinor": false,
"separateMinorPatch": false
},
{
"matchManagers": [
"dockerfile",
"docker-compose"
],
"groupName": "Docker-Images",
"separateMajorMinor": false,
"separateMinorPatch": false
},
{
"description": "Patch-/Minor-/Digest-Updates automatisch mergen, sobald alle CI-Checks (inkl. Unit-Tests) erfolgreich sind - Major-Updates sind unten explizit ausgenommen (siehe nächste Regel).",
"matchUpdateTypes": [
"patch",
"minor",
"digest",
"lockFileMaintenance"
],
"automerge": true,
"automergeType": "pr",
"platformAutomerge": true
},
{
"description": "Major-Updates immer manuell prüfen, da potenziell brechende Änderungen.",
"matchUpdateTypes": [
"major"
],
"automerge": false
}
],
"customManagers": [
{
"customType": "regex",
"managerFilePatterns": [
"/^\\.gitea/workflows/.+\\.ya?ml$/"
],
"matchStrings": [
"TRIVY_VERSION:\\s*\"(?<currentValue>[^\"]+)\""
],
"depNameTemplate": "aquasecurity/trivy",
"datasourceTemplate": "github-releases",
"extractVersionTemplate": "^v(?<version>.*)$"
},
{
"customType": "regex",
"managerFilePatterns": [
"/^\\.gitea/workflows/.+\\.ya?ml$/"
],
"matchStrings": [
"OSV_SCANNER_VERSION:\\s*\"(?<currentValue>[^\"]+)\""
],
"depNameTemplate": "google/osv-scanner",
"datasourceTemplate": "github-releases",
"extractVersionTemplate": "^v(?<version>.*)$"
},
{
"customType": "regex",
"managerFilePatterns": [
"/^\\.gitea/workflows/.+\\.ya?ml$/"
],
"matchStrings": [
"TRUFFLEHOG_VERSION:\\s*\"(?<currentValue>[^\"]+)\""
],
"depNameTemplate": "trufflesecurity/trufflehog",
"datasourceTemplate": "github-releases",
"extractVersionTemplate": "^v(?<version>.*)$"
},
{
"customType": "regex",
"managerFilePatterns": [
"/^\\.gitea/workflows/.+\\.ya?ml$/"
],
"matchStrings": [
"CARGO_BINSTALL_VERSION:\\s*\"(?<currentValue>[^\"]+)\""
],
"depNameTemplate": "cargo-bins/cargo-binstall",
"datasourceTemplate": "github-releases",
"extractVersionTemplate": "^v(?<version>.*)$"
},
{
"customType": "regex",
"managerFilePatterns": [
"/^\\.gitea/workflows/.+\\.ya?ml$/"
],
"matchStrings": [
"CARGO_DEB_VERSION:\\s*\"(?<currentValue>[^\"]+)\""
],
"depNameTemplate": "cargo-deb",
"datasourceTemplate": "crate"
},
{
"customType": "regex",
"managerFilePatterns": [
"/^\\.gitea/workflows/.+\\.ya?ml$/"
],
"matchStrings": [
"CARGO_GENERATE_RPM_VERSION:\\s*\"(?<currentValue>[^\"]+)\""
],
"depNameTemplate": "cargo-generate-rpm",
"datasourceTemplate": "crate"
}
]
}
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env python3
"""Prüft, ob die aktuelle Crate-Version bereits in der Gitea Cargo-Registry existiert.
Liest Name und Version aus Cargo.toml und fragt die Gitea Package-API ab.
Das Ergebnis wird als Step-Output "exists" (true/false) in GITHUB_OUTPUT
geschrieben, damit der Release/Publish-Job komplett übersprungen werden kann,
statt erst nach Build & Tests an einem "Version existiert bereits"-Fehler der
Registry zu scheitern.
"""
import os
import re
import urllib.error
import urllib.request
def read_cargo_field(field, path="Cargo.toml"):
pattern = re.compile(rf'^{field}\s*=\s*"(.*)"')
with open(path) as f:
for line in f:
match = pattern.match(line.strip())
if match:
return match.group(1)
raise SystemExit(f"Feld '{field}' nicht in {path} gefunden.")
def version_exists(gitea_url, owner, name, version, token):
url = f"{gitea_url}/api/v1/packages/{owner}/cargo/{name}/{version}"
req = urllib.request.Request(url, method="GET")
req.add_header("Authorization", f"token {token}")
try:
with urllib.request.urlopen(req, timeout=10) as resp:
return resp.status == 200
except urllib.error.HTTPError as e:
if e.code == 404:
return False
raise
def main():
gitea_url = os.environ["GITEA_URL"].strip().rstrip("/")
owner = os.environ["REPO_OWNER"].strip()
token = os.environ["TOKEN"].strip()
github_output = os.environ["GITHUB_OUTPUT"]
name = read_cargo_field("name")
version = read_cargo_field("version")
print(f"Prüfe {name}@{version} in der Gitea Cargo Registry...")
exists = version_exists(gitea_url, owner, name, version, token)
if exists:
print(f"Version {version} existiert bereits in der Registry. Release/Publish wird übersprungen.")
else:
print(f"Version {version} ist neu.")
with open(github_output, "a") as f:
f.write(f"exists={'true' if exists else 'false'}\n")
if __name__ == "__main__":
main()
+312
View File
@@ -0,0 +1,312 @@
#!/usr/bin/env python3
"""Erstellt oder kommentiert ein Gitea-Issue mit den Ergebnissen der Security-Scans.
Sucht ein offenes Issue mit dem Label ISSUE_LABEL (Standard: "security-scan").
Existiert eines, wird der aktuelle Scan-Stand als neuer Kommentar angehängt
(die Historie bleibt erhalten). Existiert keines (z.B. weil das letzte
geschlossen wurde), wird ein neues Issue erstellt. Gibt es keine Funde mehr,
wird ein offenes Issue nur kommentiert, nicht geschlossen.
Titel und Label lassen sich per Umgebungsvariable ISSUE_TITLE / ISSUE_LABEL
überschreiben, damit z.B. TruffleHog-Funde in ein eigenes Issue laufen statt
in das gemeinsame Trivy/OSV-Issue.
"""
import json
import os
import sys
import urllib.error
import urllib.request
LABEL_NAME = os.environ.get("ISSUE_LABEL", "security-scan")
LABEL_COLOR = "#b60205"
ISSUE_TITLE = os.environ.get("ISSUE_TITLE", "Security-Scan: Offene Schwachstellen")
SEVERITY_ORDER = {
"VERIFIED": -1,
"CRITICAL": 0,
"HIGH": 1,
"MEDIUM": 2,
"LOW": 3,
"UNKNOWN": 4,
"UNVERIFIED": 6,
}
def api(method, path, token, gitea_url, data=None):
url = f"{gitea_url}/api/v1{path}"
body = json.dumps(data).encode() if data is not None else None
req = urllib.request.Request(url, data=body, method=method)
req.add_header("Authorization", f"token {token}")
req.add_header("Content-Type", "application/json")
try:
with urllib.request.urlopen(req, timeout=10) as resp:
raw = resp.read()
return json.loads(raw) if raw else None
except urllib.error.HTTPError as e:
print(f"Gitea API Fehler ({method} {path}): {e.code} {e.read().decode()}", file=sys.stderr)
raise
def make_finding(source, id, severity, package, installed="-", fixed="-", target="-"):
return {
"source": source,
"id": id,
"severity": severity,
"package": package,
"installed": installed,
"fixed": fixed,
"target": target,
}
def cvss_score_to_severity(score):
try:
score = float(score)
except (TypeError, ValueError):
return "UNKNOWN"
if score >= 9.0:
return "CRITICAL"
if score >= 7.0:
return "HIGH"
if score >= 4.0:
return "MEDIUM"
if score > 0.0:
return "LOW"
return "UNKNOWN"
def load_trivy(path):
findings = []
if not path or not os.path.isfile(path):
return findings
with open(path) as f:
data = json.load(f)
for result in data.get("Results", []) or []:
target = result.get("Target", "?")
for vuln in result.get("Vulnerabilities", []) or []:
findings.append(make_finding(
"Trivy",
vuln.get("VulnerabilityID", "?"),
vuln.get("Severity", "UNKNOWN"),
vuln.get("PkgName", "?"),
installed=vuln.get("InstalledVersion", "?"),
fixed=vuln.get("FixedVersion") or "-",
target=target,
))
for misc in result.get("Misconfigurations", []) or []:
findings.append(make_finding(
"Trivy (Misconfig)",
misc.get("ID", "?"),
misc.get("Severity", "UNKNOWN"),
misc.get("Title", "?"),
target=target,
))
for secret in result.get("Secrets", []) or []:
findings.append(make_finding(
"Trivy (Secret)",
secret.get("RuleID", "?"),
secret.get("Severity", "UNKNOWN"),
secret.get("Title", "?"),
target=target,
))
return findings
def load_osv(path):
findings = []
if not path or not os.path.isfile(path):
return findings
with open(path) as f:
data = json.load(f)
for result in data.get("results", []) or []:
source = (result.get("source") or {}).get("path", "?")
for pkg in result.get("packages", []) or []:
info = pkg.get("package", {})
pkg_name = f"{info.get('name', '?')} ({info.get('ecosystem', '?')})"
severity_by_id = {}
for group in pkg.get("groups", []) or []:
label = cvss_score_to_severity(group.get("max_severity"))
for vuln_id in group.get("ids", []) or []:
severity_by_id[vuln_id] = label
for vuln in pkg.get("vulnerabilities", []) or []:
vuln_id = vuln.get("id", "?")
findings.append(make_finding(
"OSV-Scanner",
vuln_id,
severity_by_id.get(vuln_id, "UNKNOWN"),
pkg_name,
installed=info.get("version", "?"),
target=source,
))
return findings
def load_trufflehog(path):
findings = []
if not path or not os.path.isfile(path):
return findings
with open(path) as f:
for line in f:
line = line.strip()
if not line:
continue
try:
entry = json.loads(line)
except json.JSONDecodeError:
continue
git_meta = ((entry.get("SourceMetadata") or {}).get("Data") or {}).get("Git") or {}
findings.append(make_finding(
"TruffleHog",
entry.get("DetectorName", "?"),
"VERIFIED" if entry.get("Verified") else "UNVERIFIED",
git_meta.get("file", "?"),
target=git_meta.get("commit", "-"),
))
return findings
def sort_findings(findings):
return sorted(findings, key=lambda f: (SEVERITY_ORDER.get(f["severity"], 9), f["id"]))
def print_summary(findings):
if not findings:
print("Keine Funde.")
return
widths = {
key: max(len(key), *(len(str(f[key])) for f in findings))
for key in ("source", "id", "severity", "package", "installed", "fixed", "target")
}
header = ("source", "id", "severity", "package", "installed", "fixed", "target")
row_fmt = " ".join(f"{{:{widths[k]}}}" for k in header)
print(row_fmt.format(*header))
print(row_fmt.format(*("-" * widths[k] for k in header)))
for f in findings:
print(row_fmt.format(*(str(f[k]) for k in header)))
TRUFFLEHOG_GUIDANCE = """### Vorgehen bei gefundenen Secrets
1. **Sofort rotieren/widerrufen**: Das betroffene Secret (Token, Passwort, Schlüssel) beim jeweiligen Dienst ungültig machen und durch ein neues ersetzen. Ein einmal committetes Secret gilt als kompromittiert, auch wenn es später aus der Historie entfernt wird.
2. **Ursache beheben**: Neues Secret nur noch über Umgebungsvariablen/Secrets-Store einbinden, nicht erneut hart codieren.
3. **Historie bereinigen (optional, manuell, erst nach Schritt 1)**: Mit `git filter-repo` oder BFG Repo-Cleaner den Commit-Inhalt entfernen, danach `git push --force` in Absprache mit allen Mitwirkenden bestehende Clones/Forks werden dadurch ungültig.
4. **Issue schließen**, sobald rotiert wurde. TruffleHog findet das alte Secret ggf. weiterhin in der Historie nach der Rotation ist das unkritisch.
> Der Wert des Secrets selbst wird hier bewusst nicht ausgegeben, auch nicht gekürzt nur Detector, Datei und Commit. Fund lässt sich über "Ziel" (Commit-Hash) und "Paket" (Dateipfad) lokalisieren."""
def escape_md_cell(value):
return str(value).replace("|", "\\|").replace("\r", " ").replace("\n", " ")
def build_report(findings, run_url):
lines = [
"Automatisch erstellt vom Security-Scan-Workflow.",
f"Lauf: {run_url}" if run_url else "",
"",
"| Quelle | ID | Schweregrad | Paket | Installiert | Fix | Ziel |",
"|---|---|---|---|---|---|---|",
]
for f in findings:
cells = (f["source"], f["id"], f["severity"], f["package"], f["installed"], f["fixed"], f["target"])
lines.append("| " + " | ".join(escape_md_cell(c) for c in cells) + " |")
if any(f["source"] == "TruffleHog" for f in findings):
lines.append("")
lines.append(TRUFFLEHOG_GUIDANCE)
return "\n".join(lines)
def ensure_label(token, gitea_url, repo):
page = 1
while True:
labels = api("GET", f"/repos/{repo}/labels?limit=50&page={page}", token, gitea_url) or []
for label in labels:
if label.get("name") == LABEL_NAME:
return label["id"]
if len(labels) < 50:
break
page += 1
created = api("POST", f"/repos/{repo}/labels", token, gitea_url, {
"name": LABEL_NAME,
"color": LABEL_COLOR,
"description": "Automatisch verwaltet vom Security-Scan-Workflow",
})
return created["id"]
def find_open_issue(token, gitea_url, repo):
issues = api(
"GET",
f"/repos/{repo}/issues?state=open&type=issues&labels={LABEL_NAME}",
token,
gitea_url,
) or []
for issue in issues:
if issue.get("title") == ISSUE_TITLE:
return issue
return None
def main():
trivy_path = sys.argv[1] if len(sys.argv) > 1 else None
osv_path = sys.argv[2] if len(sys.argv) > 2 else None
trufflehog_path = sys.argv[3] if len(sys.argv) > 3 else None
gitea_url = os.environ.get("GITEA_URL", "").strip().rstrip("/")
repo = os.environ.get("REPO", "").strip()
token = os.environ.get("TOKEN", "").strip()
run_url = os.environ.get("RUN_URL", "")
osv_exit = int(os.environ.get("OSV_EXIT", "0"))
trufflehog_exit = int(os.environ.get("TRUFFLEHOG_EXIT", "0"))
findings = sort_findings(load_trivy(trivy_path) + load_osv(osv_path) + load_trufflehog(trufflehog_path))
print_summary(findings)
if not token or not gitea_url or not repo:
missing = [name for name, val in [("TOKEN", token), ("GITEA_URL", gitea_url), ("REPO", repo)] if not val]
print(f"{', '.join(missing)} nicht gesetzt oder leer überspringe Gitea-Issue-Synchronisation.")
else:
open_issue = find_open_issue(token, gitea_url, repo)
if findings:
report = build_report(findings, run_url)
if open_issue:
print(f"Kommentiere bestehendes Issue #{open_issue['number']} mit {len(findings)} Fund(en).")
api("POST", f"/repos/{repo}/issues/{open_issue['number']}/comments", token, gitea_url, {"body": report})
else:
label_id = ensure_label(token, gitea_url, repo)
print(f"Erstelle neues Issue mit {len(findings)} Fund(en).")
api("POST", f"/repos/{repo}/issues", token, gitea_url, {
"title": ISSUE_TITLE,
"body": report,
"labels": [label_id],
})
elif open_issue:
print(f"Keine aktuellen Funde mehr. Kommentiere Issue #{open_issue['number']}.")
api("POST", f"/repos/{repo}/issues/{open_issue['number']}/comments", token, gitea_url, {
"body": f"Aktueller Scan hat keine offenen Schwachstellen mehr gefunden.\n\n{run_url}".strip(),
})
else:
print("Keine Funde und kein offenes Issue vorhanden.")
osv_ok_exits = {0, 1}
trufflehog_ok_exits = {0, 183}
if osv_exit not in osv_ok_exits:
print(f"WARNUNG: osv-scanner beendete sich mit unerwartetem Exit-Code {osv_exit} - Scan evtl. unvollständig.", file=sys.stderr)
if trufflehog_exit not in trufflehog_ok_exits:
print(f"WARNUNG: trufflehog beendete sich mit unerwartetem Exit-Code {trufflehog_exit} - Scan evtl. unvollständig.", file=sys.stderr)
has_trivy_findings = any(f["source"].startswith("Trivy") for f in findings)
if (
has_trivy_findings
or osv_exit == 1
or trufflehog_exit == 183
or osv_exit not in osv_ok_exits
or trufflehog_exit not in trufflehog_ok_exits
):
sys.exit(1)
if __name__ == "__main__":
main()
+55 -128
View File
@@ -23,13 +23,13 @@
use std::env;
use std::fmt::{Display, Formatter};
use std::fs::{create_dir_all, File, OpenOptions};
use std::io::Write;
use std::fs::{File, OpenOptions, create_dir_all};
use std::io::{IsTerminal, Write};
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::atomic::{AtomicU8, Ordering};
use std::sync::{Mutex, OnceLock, RwLock};
use time::{macros::format_description, OffsetDateTime};
use time::{OffsetDateTime, macros::format_description};
/// Schweregrade für Logeinträge in aufsteigender Detailtiefe.
///
@@ -99,9 +99,6 @@ static LOG_LEVEL: AtomicU8 = AtomicU8::new(0);
/// Lazy-initialisiertes Handle zur Logdatei; kann `None` sein, falls das Öffnen fehlschlug.
static LOG_FILE: OnceLock<Mutex<Option<File>>> = OnceLock::new();
/// Optionaler benutzerdefinierter Programmname für die Verzeichnisstruktur im Temp-Ordner.
static PROGRAM_NAME: OnceLock<RwLock<Option<String>>> = OnceLock::new();
/// Optionaler benutzerdefinierter Pfad für Logdateien.
static CUSTOM_LOG_DIR: OnceLock<RwLock<Option<PathBuf>>> = OnceLock::new();
@@ -142,39 +139,15 @@ pub fn get_log_level() -> LogLevel {
};
}
if let Ok(val) = env::var("LOG_LEVEL").or_else(|_| env::var("RUST_LOG")) {
if let Ok(lvl) = LogLevel::try_from(val.as_str()) {
if let Ok(val) = env::var("LOG_LEVEL").or_else(|_| env::var("RUST_LOG"))
&& let Ok(lvl) = LogLevel::try_from(val.as_str())
{
return lvl;
}
}
LogLevel::Info
}
/// Setzt den Programmnamen für die Logdateibenennung explizit.
pub fn set_program_name(name: impl Into<String>) {
let lock = PROGRAM_NAME.get_or_init(|| RwLock::new(None));
if let Ok(mut guard) = lock.write() {
*guard = Some(name.into());
}
}
/// Ermittelt den Programmnamen (aus `set_program_name` oder `std::env::current_exe`).
pub fn get_program_name() -> String {
if let Some(lock) = PROGRAM_NAME.get() {
if let Ok(guard) = lock.read() {
if let Some(name) = guard.as_ref() {
return name.clone();
}
}
}
env::current_exe()
.ok()
.and_then(|p| p.file_stem().map(|s| s.to_string_lossy().to_string()))
.filter(|s| !s.is_empty())
.unwrap_or_else(|| "app".to_string())
}
/// Setzt ein benutzerdefiniertes Verzeichnis für Logdateien.
pub fn set_log_dir(path: impl Into<PathBuf>) {
let lock = CUSTOM_LOG_DIR.get_or_init(|| RwLock::new(None));
@@ -183,6 +156,33 @@ pub fn set_log_dir(path: impl Into<PathBuf>) {
}
}
/// ANSI-Escape-Sequenz zum Zurücksetzen der Textfarbe.
const ANSI_RESET: &str = "\x1b[0m";
/// Liefert die ANSI-Farbsequenz für den jeweiligen Schweregrad.
///
/// Farben: `Error` -> Rot, `Warn` -> Gelb, `Info` -> Grün, `Debug` -> Cyan.
fn ansi_color(log_level: &LogLevel) -> &'static str {
match log_level {
LogLevel::Error => "\x1b[31m",
LogLevel::Warn => "\x1b[33m",
LogLevel::Info => "\x1b[32m",
LogLevel::Debug => "\x1b[36m",
}
}
/// Prüft, ob Farbausgabe für den gegebenen Stream sinnvoll ist.
///
/// Voraussetzungen:
/// - Der Stream muss ein echtes Terminal sein (`is_terminal`), keine Umleitung in eine Datei oder Pipe.
/// - Die Umgebungsvariable `NO_COLOR` darf nicht gesetzt sein (siehe <https://no-color.org/>).
/// - `TERM` darf nicht auf `dumb` stehen.
fn color_supported(stream_is_terminal: bool) -> bool {
stream_is_terminal
&& env::var_os("NO_COLOR").is_none()
&& env::var("TERM").map(|term| term != "dumb").unwrap_or(true)
}
/// Formatiert eine Lognachricht mit Zeitstempel, Level, Tag und Präfix.
///
/// Format:
@@ -210,7 +210,9 @@ pub fn format_message(tag: &str, message: &str, log_level: &LogLevel) -> String
let message = format!(
"[{}][{}]{}: {}",
now_local.format(fmt).unwrap_or_else(|_| "00.00.0000 00:00:00.000".to_string()),
now_local
.format(fmt)
.unwrap_or_else(|_| "00.00.0000 00:00:00.000".to_string()),
log_level,
prefix,
message
@@ -249,20 +251,26 @@ pub fn log(tag: &str, message: &str, log_level: LogLevel) {
let formatted = format_message(tag, message, &log_level);
if log_level == LogLevel::Error {
if color_supported(std::io::stderr().is_terminal()) {
eprintln!("{}{}{}", ansi_color(&log_level), formatted, ANSI_RESET);
} else {
eprintln!("{}", formatted);
}
} else if color_supported(std::io::stdout().is_terminal()) {
println!("{}{}{}", ansi_color(&log_level), formatted, ANSI_RESET);
} else {
println!("{}", formatted);
}
let file_lock = get_or_init_log_file();
if let Ok(mut guard) = file_lock.lock() {
if let Some(f) = guard.as_mut() {
if let Ok(mut guard) = file_lock.lock()
&& let Some(f) = guard.as_mut()
{
let _ = writeln!(f, "{}", formatted);
let _ = f.flush();
}
}
}
}
/// Schreibt einen Log-Eintrag mit Schweregrad `Error`.
pub fn error(tag: &str, message: &str) {
@@ -292,6 +300,15 @@ fn get_or_init_log_file() -> &'static Mutex<Option<File>> {
})
}
/// Generiert einen zufälligen Fallback-Programmnamen, falls der Name der ausführbaren Datei nicht ermittelt werden kann.
fn random_fallback_program_name() -> String {
use std::hash::{BuildHasher, Hasher, RandomState};
let mut hasher = RandomState::new().build_hasher();
hasher.write_i128(OffsetDateTime::now_utc().unix_timestamp_nanos());
let val = hasher.finish();
format!("app-{:016x}", val)
}
/// Öffnet (und erstellt bei Bedarf) die tagesbasierte Logdatei.
fn open_log_file() -> std::io::Result<File> {
let custom_dir = if let Some(lock) = CUSTOM_LOG_DIR.get() {
@@ -305,7 +322,7 @@ fn open_log_file() -> std::io::Result<File> {
};
let mut dir = custom_dir.unwrap_or_else(|| {
let program = get_program_name();
let program = program_ctdra::program_name_or(random_fallback_program_name());
let mut d = env::temp_dir();
d.push(format!("{}-{}", program, DEFAULT_LOG_UUID));
d
@@ -324,93 +341,3 @@ fn open_log_file() -> std::io::Result<File> {
OpenOptions::new().create(true).append(true).open(dir)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_message_prefixes() {
let msg_info = format_message("test", "Info message", &LogLevel::Info);
assert!(msg_info.starts_with("[i]"));
assert!(msg_info.contains("[INFO][test]: Info message"));
let msg_err = format_message("test", "Error message", &LogLevel::Error);
assert!(msg_err.starts_with("[!]"));
assert!(msg_err.contains("[ERROR][test]: Error message"));
let msg_warn = format_message("test", "Warn message", &LogLevel::Warn);
assert!(msg_warn.starts_with("[?]"));
assert!(msg_warn.contains("[WARN][test]: Warn message"));
let msg_debug = format_message("test", "Debug message", &LogLevel::Debug);
assert!(msg_debug.starts_with("[d]"));
assert!(msg_debug.contains("[DEBUG][test]: Debug message"));
}
#[test]
fn test_format_message_empty_tag() {
let msg = format_message("", "No tag message", &LogLevel::Info);
assert!(msg.starts_with("[i]"));
assert!(msg.contains("[INFO]: No tag message"));
}
#[test]
fn test_log_level_display() {
assert_eq!(LogLevel::Error.to_string(), "ERROR");
assert_eq!(LogLevel::Warn.to_string(), "WARN");
assert_eq!(LogLevel::Info.to_string(), "INFO");
assert_eq!(LogLevel::Debug.to_string(), "DEBUG");
}
#[test]
fn test_log_level_parsing() {
assert_eq!(LogLevel::try_from("error").unwrap(), LogLevel::Error);
assert_eq!(LogLevel::try_from("ERROR").unwrap(), LogLevel::Error);
assert_eq!(LogLevel::try_from("warn").unwrap(), LogLevel::Warn);
assert_eq!(LogLevel::try_from("warning").unwrap(), LogLevel::Warn);
assert_eq!(LogLevel::try_from("info").unwrap(), LogLevel::Info);
assert_eq!(LogLevel::try_from("debug").unwrap(), LogLevel::Debug);
assert_eq!(LogLevel::try_from("invalid").unwrap_err(), LogLevel::Info);
assert_eq!(LogLevel::from_str("debug").unwrap(), LogLevel::Debug);
}
#[test]
fn test_log_level_ordering() {
assert!(LogLevel::Error < LogLevel::Warn);
assert!(LogLevel::Warn < LogLevel::Info);
assert!(LogLevel::Info < LogLevel::Debug);
}
#[test]
fn test_set_and_get_log_level() {
set_log_level(LogLevel::Debug);
assert_eq!(get_log_level(), LogLevel::Debug);
set_log_level(LogLevel::Error);
assert_eq!(get_log_level(), LogLevel::Error);
set_log_level(LogLevel::Info);
assert_eq!(get_log_level(), LogLevel::Info);
}
#[test]
fn test_convenience_functions() {
set_log_level(LogLevel::Debug);
error("test", "An error occurred");
warn("test", "A warning occurred");
info("test", "An info message");
debug("test", "A debug message");
}
#[test]
fn test_program_name_and_custom_dir() {
set_program_name("my-custom-app");
assert_eq!(get_program_name(), "my-custom-app");
let custom_dir = env::temp_dir().join("test-logger-custom-dir");
set_log_dir(custom_dir.clone());
let file = open_log_file();
assert!(file.is_ok());
}
}
+189
View File
@@ -0,0 +1,189 @@
use logger_ctdra::{
LogLevel, debug, error, format_message, get_log_level, info, init, log, set_log_dir,
set_log_level, warn,
};
use std::env;
use std::str::FromStr;
#[test]
fn test_format_message_prefixes() {
let msg_info = format_message("test", "Info message", &LogLevel::Info);
assert!(msg_info.starts_with("[i]"));
assert!(msg_info.contains("[INFO][test]: Info message"));
let msg_err = format_message("test", "Error message", &LogLevel::Error);
assert!(msg_err.starts_with("[!]"));
assert!(msg_err.contains("[ERROR][test]: Error message"));
let msg_warn = format_message("test", "Warn message", &LogLevel::Warn);
assert!(msg_warn.starts_with("[?]"));
assert!(msg_warn.contains("[WARN][test]: Warn message"));
let msg_debug = format_message("test", "Debug message", &LogLevel::Debug);
assert!(msg_debug.starts_with("[d]"));
assert!(msg_debug.contains("[DEBUG][test]: Debug message"));
}
#[test]
fn test_format_message_empty_tag() {
let msg = format_message("", "No tag message", &LogLevel::Info);
assert!(msg.starts_with("[i]"));
assert!(msg.contains("[INFO]: No tag message"));
assert!(!msg.contains("[INFO][]"));
}
#[test]
fn test_format_message_timestamp_format() {
let msg = format_message("tag", "payload", &LogLevel::Info);
// Format: [i][DD.MM.YYYY HH:MM:SS.mmm][INFO][tag]: payload
assert!(msg.starts_with("[i]["));
let closing_bracket = msg[3..].find(']').expect("Timestamp closing bracket");
let timestamp = &msg[4..4 + closing_bracket - 1];
// Timestamp format: DD.MM.YYYY HH:MM:SS.mmm (length: 23)
assert_eq!(timestamp.len(), 23);
assert_eq!(&timestamp[2..3], ".");
assert_eq!(&timestamp[5..6], ".");
assert_eq!(&timestamp[10..11], " ");
assert_eq!(&timestamp[13..14], ":");
assert_eq!(&timestamp[16..17], ":");
assert_eq!(&timestamp[19..20], ".");
}
#[test]
fn test_log_level_display() {
assert_eq!(LogLevel::Error.to_string(), "ERROR");
assert_eq!(LogLevel::Warn.to_string(), "WARN");
assert_eq!(LogLevel::Info.to_string(), "INFO");
assert_eq!(LogLevel::Debug.to_string(), "DEBUG");
}
#[test]
fn test_log_level_parsing_str() {
assert_eq!(LogLevel::try_from("error").unwrap(), LogLevel::Error);
assert_eq!(LogLevel::try_from("ERROR").unwrap(), LogLevel::Error);
assert_eq!(LogLevel::try_from(" error ").unwrap(), LogLevel::Error);
assert_eq!(LogLevel::try_from("warn").unwrap(), LogLevel::Warn);
assert_eq!(LogLevel::try_from("warning").unwrap(), LogLevel::Warn);
assert_eq!(LogLevel::try_from("WARN").unwrap(), LogLevel::Warn);
assert_eq!(LogLevel::try_from("WARNING").unwrap(), LogLevel::Warn);
assert_eq!(LogLevel::try_from("info").unwrap(), LogLevel::Info);
assert_eq!(LogLevel::try_from("INFO").unwrap(), LogLevel::Info);
assert_eq!(LogLevel::try_from("debug").unwrap(), LogLevel::Debug);
assert_eq!(LogLevel::try_from("DEBUG").unwrap(), LogLevel::Debug);
assert_eq!(LogLevel::try_from("invalid").unwrap_err(), LogLevel::Info);
assert_eq!(LogLevel::try_from("").unwrap_err(), LogLevel::Info);
}
#[test]
fn test_log_level_parsing_string_and_from_str() {
assert_eq!(
LogLevel::try_from(String::from("error")).unwrap(),
LogLevel::Error
);
assert_eq!(
LogLevel::try_from(String::from("WARN")).unwrap(),
LogLevel::Warn
);
assert_eq!(
LogLevel::try_from(String::from("invalid")).unwrap_err(),
LogLevel::Info
);
assert_eq!(LogLevel::from_str("debug").unwrap(), LogLevel::Debug);
assert_eq!(LogLevel::from_str("INFO").unwrap(), LogLevel::Info);
assert_eq!(LogLevel::from_str("unknown").unwrap_err(), LogLevel::Info);
}
#[test]
fn test_log_level_discriminants_and_traits() {
assert_eq!(LogLevel::Error as u8, 1);
assert_eq!(LogLevel::Warn as u8, 2);
assert_eq!(LogLevel::Info as u8, 3);
assert_eq!(LogLevel::Debug as u8, 4);
let lvl = LogLevel::Info;
let lvl_cloned = lvl;
assert_eq!(lvl, lvl_cloned);
assert_eq!(format!("{:?}", LogLevel::Debug), "Debug");
}
#[test]
fn test_log_level_ordering() {
assert!(LogLevel::Error < LogLevel::Warn);
assert!(LogLevel::Warn < LogLevel::Info);
assert!(LogLevel::Info < LogLevel::Debug);
assert!(LogLevel::Error <= LogLevel::Error);
assert!(LogLevel::Debug >= LogLevel::Info);
}
#[test]
fn test_set_and_get_log_level() {
set_log_level(LogLevel::Debug);
assert_eq!(get_log_level(), LogLevel::Debug);
set_log_level(LogLevel::Error);
assert_eq!(get_log_level(), LogLevel::Error);
set_log_level(LogLevel::Warn);
assert_eq!(get_log_level(), LogLevel::Warn);
set_log_level(LogLevel::Info);
assert_eq!(get_log_level(), LogLevel::Info);
}
#[test]
fn test_init_alias() {
init(LogLevel::Debug);
assert_eq!(get_log_level(), LogLevel::Debug);
init(LogLevel::Warn);
assert_eq!(get_log_level(), LogLevel::Warn);
}
#[test]
fn test_convenience_functions() {
set_log_level(LogLevel::Debug);
error("test", "An error occurred");
warn("test", "A warning occurred");
info("test", "An info message");
debug("test", "A debug message");
}
#[test]
fn test_log_filtering_behavior() {
set_log_level(LogLevel::Error);
// Debug and Info should be filtered out, Error should pass
log(
"filter_test",
"This debug log should be ignored",
LogLevel::Debug,
);
log(
"filter_test",
"This info log should be ignored",
LogLevel::Info,
);
log(
"filter_test",
"This warn log should be ignored",
LogLevel::Warn,
);
log("filter_test", "This error log should pass", LogLevel::Error);
set_log_level(LogLevel::Debug);
log(
"filter_test",
"All logs should pass in Debug level",
LogLevel::Debug,
);
}
#[test]
fn test_set_log_dir_string_and_pathbuf() {
let dir_str = env::temp_dir().join("test-integration-str-dir");
set_log_dir(dir_str.to_string_lossy().to_string());
let dir_pathbuf = env::temp_dir().join("test-integration-pathbuf-dir");
set_log_dir(dir_pathbuf);
set_log_dir("/tmp/test-str-slice-dir");
}