Files
Gallus_Pub/.woodpecker.yml
T
KenzoandClaude Opus 5 1a7a4f8a02
ci/woodpecker/push/woodpecker Pipeline was successful
perf(ci): Builds beschleunigen und kaputtes sharp reparieren
Nur bauen was sich geaendert hat:
- deploy_backend laeuft nur bei Aenderungen unter backend/, deploy_frontend
  nur bei src/, public/ und den Frontend-Konfigdateien. Die haeufigsten
  Commits sind Inhaltsaenderungen aus dem CMS und fassen nur src/ und
  public/ an - die brauchen kein Backend-Deployment mehr.
- Der Abhaengigkeits-Audit laeuft nur noch, wenn sich package.json oder
  package-lock.json aendern. Sonst kam bei jedem Inhalts-Commit derselbe
  Bericht nach Discord.
- "[ALL]" in der Commit-Message erzwingt weiterhin den vollen Durchlauf.

Build-Kontext:
- Es gab kein .dockerignore im Root. Bei jedem Frontend-Deploy wanderte
  das komplette Repo zum Fly-Remote-Builder, inklusive 50 MB Git-Historie
  und des gesamten Backends. 98 MB -> 47 MB.
- Zwei tote Zeilen im Frontend-Dockerfile entfernt: ein Root-styles/ gibt
  es nicht und /styles/ wird als URL nirgends verwendet.

Backend-Image:
- npm ci lief in beiden Stages, better-sqlite3 wurde also doppelt
  uebersetzt. Jetzt einmal im Builder, danach npm prune --omit=dev und die
  fertigen node_modules wandern weiter.
- Die Build-Werkzeuge sind raus aus der Laufzeit-Stage. Das apk del vorher
  hat sie nur unsichtbar gemacht, die Layer blieben im Image.
- Ohne Cache lokal: 1:31 -> 0:29. Image: 845 MB -> 312 MB.

sharp war die ganze Zeit kaputt:
- backend/package-lock.json enthielt als einziges Plattform-Binary
  @img/sharp-win32-x64, das Lockfile stammt von einer Windows-Maschine.
  npm ci installiert strikt nach Lockfile, auf Alpine kam damit gar kein
  sharp-Binary an. Der Upload fiel jedes Mal auf den Fallback zurueck:
  keine Verkleinerung auf 1600px, keine WebP-Wandlung, das Originalbild
  landete unveraendert im Repo. Genau deshalb sind die Bilder so gross -
  in der Historie liegen entsprechend .jpeg statt .webp.
- Lockfile mit allen Plattformvarianten neu aufgeloest. Keine einzige
  bestehende Paketversion aendert sich dabei; dazugekommen sind die
  Binaries, weggefallen sind 12 Postgres-Pakete, die drizzle-kit optional
  mitzieht und die hier niemand benutzt.
- vips aus dem Image entfernt, sharp bringt seit 0.33 sein eigenes libvips
  mit.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-08-11 13:13:52 +02:00

229 lines
8.2 KiB
YAML

steps:
audit_dependencies:
image: node:20
commands:
- npm install --package-lock-only
- npm audit --audit-level=moderate --json > audit-result.json 2>&1 || echo "Audit completed"
- npm audit --audit-level=moderate > audit-output.txt 2>&1 || echo "Audit completed"
# Nur wenn sich Abhaengigkeiten geaendert haben - bei reinen
# Inhaltsaenderungen kaeme sonst jedes Mal derselbe Bericht
when:
- branch: main
event: push
path:
include:
- 'package.json'
- 'package-lock.json'
- '.woodpecker.yml'
ignore_message: '[ALL]'
discord_notify_audit:
image: alpine:latest
environment:
DISCORD_WEBHOOK:
from_secret: discord_webhook
commands:
- apk add --no-cache curl jq
- |
if [ -f audit-result.json ]; then
TOTAL=$(jq -r '.metadata.vulnerabilities.total // 0' audit-result.json 2>/dev/null || echo "0")
CRITICAL=$(jq -r '.metadata.vulnerabilities.critical // 0' audit-result.json 2>/dev/null || echo "0")
HIGH=$(jq -r '.metadata.vulnerabilities.high // 0' audit-result.json 2>/dev/null || echo "0")
MODERATE=$(jq -r '.metadata.vulnerabilities.moderate // 0' audit-result.json 2>/dev/null || echo "0")
LOW=$(jq -r '.metadata.vulnerabilities.low // 0' audit-result.json 2>/dev/null || echo "0")
if [ "$CRITICAL" -gt 0 ] || [ "$HIGH" -gt 0 ] || [ "$MODERATE" -gt 0 ]; then
COLOR=16744448
STATUS="⚠️ Vulnerabilities Found"
else
COLOR=3066993
STATUS="✅ No Vulnerabilities"
fi
if [ -f audit-output.txt ]; then
VULNS=$(head -50 audit-output.txt | tail -40 || echo "No details")
else
VULNS="No audit output available"
fi
printf '%s' "$VULNS" > /tmp/vulns.txt
PAYLOAD=$(jq -n \
--arg title "🔒 Security Audit - Build #${CI_BUILD_NUMBER}" \
--arg status "$STATUS" \
--arg total "$TOTAL" \
--arg critical "$CRITICAL" \
--arg high "$HIGH" \
--arg moderate "$MODERATE" \
--arg low "$LOW" \
--arg commit "${CI_COMMIT_SHA:0:7}" \
--rawfile details /tmp/vulns.txt \
--arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%S.000Z)" \
--argjson color "$COLOR" \
'{
embeds: [{
title: $title,
description: $status,
color: $color,
fields: [
{ name: "Total", value: $total, inline: true },
{ name: "Critical", value: $critical, inline: true },
{ name: "High", value: $high, inline: true },
{ name: "Moderate", value: $moderate, inline: true },
{ name: "Low", value: $low, inline: true },
{ name: "Commit", value: ("`" + $commit + "`"), inline: true },
{ name: "Details", value: ("```\n" + ($details[:800]) + (if ($details | length) > 800 then "\n... (truncated)" else "" end) + "\n```"), inline: false }
],
timestamp: $timestamp
}]
}')
curl -H "Content-Type: application/json" -X POST \
-d "$PAYLOAD" "$DISCORD_WEBHOOK"
else
echo "No audit results found - listing workspace files:"
ls -la
fi
# Gleicher Filter wie der Audit-Schritt, sonst meldet Discord bei jedem
# Inhalts-Commit "keine Ergebnisse gefunden"
when:
- branch: main
event: push
path:
include:
- 'package.json'
- 'package-lock.json'
- '.woodpecker.yml'
ignore_message: '[ALL]'
# Backend zuerst - die Admin-Seite braucht die API.
# Laeuft nur, wenn sich am Backend etwas geaendert hat. Die haeufigsten
# Commits sind Inhaltsaenderungen aus dem CMS und fassen nur src/ und
# public/ an - die brauchen kein Backend-Deployment.
deploy_backend:
image: node:20
environment:
FLY_API_TOKEN:
from_secret: FLY_API_TOKEN
commands:
- curl -L https://fly.io/install.sh | sh
- export PATH="$HOME/.fly/bin:$PATH"
# aus backend/ heraus, damit Build-Kontext und Dockerfile stimmen
- cd backend && flyctl deploy --app gallus-cms-backend --remote-only
when:
- branch: main
event: push
path:
include:
- 'backend/**'
- '.woodpecker.yml'
# "[ALL]" in der Commit-Message erzwingt den vollen Durchlauf
ignore_message: '[ALL]'
deploy_frontend:
image: node:20
environment:
FLY_API_TOKEN:
from_secret: FLY_API_TOKEN
commands:
- curl -L https://fly.io/install.sh | sh
- export PATH="$HOME/.fly/bin:$PATH"
- flyctl deploy --config fly.toml --app gallus-pub --remote-only
when:
- branch: main
event: push
path:
include:
- 'src/**'
- 'public/**'
- 'package.json'
- 'package-lock.json'
- 'astro.config.mjs'
- 'tsconfig.json'
- 'Dockerfile'
- '.dockerignore'
- 'fly.toml'
- '.woodpecker.yml'
ignore_message: '[ALL]'
notify_success:
image: alpine:latest
environment:
DISCORD_WEBHOOK:
from_secret: discord_webhook
commands:
- apk add --no-cache curl jq
- |
# Schreibe Commit-Message in Datei (sicher gegen Shell-Sonderzeichen)
printf '%s\n' "$CI_COMMIT_MESSAGE" > /tmp/commit_msg.txt
PAYLOAD=$(cat /tmp/commit_msg.txt | jq -Rs \
--arg title "✅ Build #${CI_BUILD_NUMBER} - Success" \
--arg repo "${CI_REPO}" \
--arg branch "${CI_COMMIT_BRANCH}" \
--arg commit "${CI_COMMIT_SHA:0:7}" \
--arg author "${CI_COMMIT_AUTHOR}" \
--arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%S.000Z)" \
'. as $message | {
embeds: [{
title: $title,
description: "Build und Deployment erfolgreich abgeschlossen!",
color: 3066993,
fields: [
{ name: "Repository", value: $repo, inline: true },
{ name: "Branch", value: $branch, inline: true },
{ name: "Commit", value: ("`" + $commit + "`"), inline: true },
{ name: "Author", value: $author, inline: true },
{ name: "Commit Message", value: $message, inline: false }
],
timestamp: $timestamp
}]
}')
curl -H "Content-Type: application/json" -X POST \
-d "$PAYLOAD" "$DISCORD_WEBHOOK"
when:
- branch: main
event: push
status: success
notify_failure:
image: alpine:latest
environment:
DISCORD_WEBHOOK:
from_secret: discord_webhook
commands:
- apk add --no-cache curl jq
- |
# Schreibe Commit-Message in Datei (sicher gegen Shell-Sonderzeichen)
printf '%s\n' "$CI_COMMIT_MESSAGE" > /tmp/commit_msg.txt
PAYLOAD=$(cat /tmp/commit_msg.txt | jq -Rs \
--arg title "❌ Build #${CI_BUILD_NUMBER} - Failure" \
--arg repo "${CI_REPO}" \
--arg branch "${CI_COMMIT_BRANCH}" \
--arg commit "${CI_COMMIT_SHA:0:7}" \
--arg author "${CI_COMMIT_AUTHOR}" \
--arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%S.000Z)" \
'. as $message | {
embeds: [{
title: $title,
description: "Build oder Deployment ist fehlgeschlagen!",
color: 15158332,
fields: [
{ name: "Repository", value: $repo, inline: true },
{ name: "Branch", value: $branch, inline: true },
{ name: "Commit", value: ("`" + $commit + "`"), inline: true },
{ name: "Author", value: $author, inline: true },
{ name: "Commit Message", value: $message, inline: false }
],
timestamp: $timestamp
}]
}')
curl -H "Content-Type: application/json" -X POST \
-d "$PAYLOAD" "$DISCORD_WEBHOOK"
when:
- branch: main
event: push
status: failure