Die traditionelle Veröffentlichung von Inhalten folgt oft einem ineffizienten Dreiklang: Text schreiben, CMS öffnen, formatieren, einfügen und schließlich veröffentlichen. Für einzelne Artikel mag das akzeptabel sein – doch für Teams, die monatlich Dutzende Beiträge publizieren, wird daraus ein stiller Zeitfresser.
Eine automatisierte Lösung mit GitHub Actions kann diesen Prozess komplett revolutionieren. Statt manueller Schritte überträgt ein einfaches Commit eines .md-Files direkt in ein CMS – sei es WordPress, Ghost oder Webflow. Innerhalb von maximal 90 Sekunden erscheint der Artikel online, formatiert, getaggt und bereit für die Leserschaft. Hier erfahren Sie, wie Sie diese Automatisierung aufbauen und welche Fallstricke Sie dabei vermeiden müssen.
Der Workflow im Überblick
Am Ende dieser Anleitung haben Sie eine voll funktionsfähige Pipeline, die folgende Komponenten umfasst:
- Ein Git-Repository als einzige Quelle für Ihre Inhalte
- Eine GitHub-Actions-Workflow-Datei, die bei jedem Push auf den
main-Branch ausgelöst wird - Ein Node.js-Skript, das Frontmatter ausliest, Markdown verarbeitet und die Inhalte per API an Ihr CMS übermittelt
- Unterstützung für WordPress (REST-API), Ghost (Admin-API) und Webflow (CMS-API) – wahlweise einzeln oder kombiniert
- Separate Branches für Entwürfe (Staging) und finale Veröffentlichungen (Main)
Die finale Ordnerstruktur sieht wie folgt aus:
content-pipeline/
├── .github/
│ └── workflows/
│ └── publish.yml
├── posts/
│ ├── mein-erster-beitrag.md
│ └── ein-zweiter-beitrag.md
├── scripts/
│ ├── publish.js
│ ├── publishers/
│ │ ├── wordpress.js
│ │ ├── ghost.js
│ │ └── webflow.js
│ └── utils/
│ ├── parse-frontmatter.js
│ └── transform-markdown.js
├── package.json
└── .env.exampleWarum die Umsetzung komplexer ist als gedacht
Die naheliegende Idee – einfach den Markdown-Text an die CMS-API zu senden – scheitert in der Praxis an drei zentralen Herausforderungen:
- Formatkonflikte: CMS-Systeme akzeptieren keinen reinen Markdown-Text. WordPress erwartet HTML, Ghost verwendet ein eigenes Lexical-Editor-Format für komplexe Inhalte, und Webflow verlangt strukturierte JSON-Daten mit spezifischen Rich-Text-Feld-Schemata.
- Frontmatter als Metadaten-Kontrakt: Die Zuordnung von Metadaten wie Tags oder Status variiert stark zwischen den Systemen. In Ghost sind Tags als Array von Tag-Objekten definiert, in WordPress müssen Tags zunächst in IDs aufgelöst werden, und Webflow kennt keine klassischen Tags – stattdessen kommen benutzerdefinierte Feld-Slugs zum Einsatz.
- Idempotenz: Ein versehentlich doppelt ausgelöster Workflow darf nicht zu doppelten Artikeln führen. Eine zuverlässige Lösung erfordert eine Abfrage vor jedem Publish, um zu entscheiden, ob ein neuer Artikel erstellt oder ein bestehender aktualisiert wird.
Diese Hürden sind keine Randfälle, sondern treten bereits beim ersten echten Publish auf. Das folgende Skript berücksichtigt alle drei Problemstellungen.
Voraussetzungen für die Umsetzung
Bevor Sie mit der Implementierung beginnen, stellen Sie sicher, dass folgende Grundlagen erfüllt sind:
- Node.js in Version 18 oder höher
- Ein GitHub-Repository (auch kostenlose Konten sind ausreichend)
- Zugang zu mindestens einem der unterstützten CMS:
- WordPress mit aktivierten Application Passwords
- Ghost mit Admin-API-Zugriff
- Webflow mit einer CMS-Collection
- Grundlegende Kenntnisse der GitHub-Actions-Syntax
Schritt 1: Der Frontmatter-Kontrakt definieren
Jeder Beitrag benötigt einen konsistenten Frontmatter-Block am Anfang der Markdown-Datei. Dieses Metadaten-Fragment dient als zentrale Steuerungsinstanz für den Publish-Prozess.
---
title: "Mein Artikel-Titel"
slug: "mein-artikel-titel"
description: "Ein Satz Zusammenfassung für Meta-Beschreibungen und CMS-Auszüge."
tags: ["javascript", "devops", "tutorial"]
status: "published" # oder "draft"
targets: ["wordpress", "ghost"] # Welche CMS-Systeme ansteuern?
date: "2025-01-15"
cover_image: "
---Der targets-Parameter ist dabei besonders wichtig. Er ermöglicht es, dass ein einzelnes Repository unterschiedliche CMS-Systeme ansteuert. Ein Marketing-Beitrag könnte beispielsweise nur WordPress ansprechen, während ein technischer Deep-Dive auch Ghost erreicht. Das Skript wertet diese Liste aus und leitet die Inhalte entsprechend weiter.
Schritt 2: Frontmatter parsen und Markdown transformieren
Ein zentraler Baustein ist das Parsen des Frontmatter und die Umwandlung des Markdown-Inhalts in das benötigte Format. Hier ein Beispiel-Parser:
// scripts/utils/parse-frontmatter.js
import fs from 'fs';
import matter from 'gray-matter';
import { marked } from 'marked';
export function parsePost(filePath) {
const raw = fs.readFileSync(filePath, 'utf-8');
const { data: frontmatter, content } = matter(raw);
// Überprüfen, ob alle Pflichtfelder vorhanden sind
const required = ['title', 'slug', 'targets'];
const missing = required.filter(field => !frontmatter[field]);
if (missing.length > 0) {
throw new Error(`Fehlende Pflichtfelder im Frontmatter: ${missing.join(', ')} in ${filePath}`);
}
return {
frontmatter,
markdown: content,
html: marked(content), // WordPress benötigt HTML
filePath,
};
}Zwei wichtige Details:
- Die Bibliothek
gray-matterist der Standard für das Parsen von Frontmatter in Markdown – stabil, weit verbreitet und zuverlässig. - Die Markdown-Inhalte werden bereits beim Parsen in HTML umgewandelt. So stehen allen Publisher-Skripten beide Formate zur Verfügung, und jedes System kann die passende Darstellung wählen.
Installieren Sie die benötigten Abhängigkeiten mit:
npm install gray-matter markedSchritt 3: Integration mit WordPress
WordPress bietet zwar die ausgereifteste API der drei unterstützten Systeme, verlangt aber besondere Aufmerksamkeit bei der Tag-Verwaltung. Tags können nicht einfach als Namen übergeben werden – stattdessen müssen sie zunächst in IDs aufgelöst werden. Falls ein Tag noch nicht existiert, wird er automatisch angelegt.
// scripts/publishers/wordpress.js
const WP_BASE = process.env.WP_BASE_URL; // z.B.
const WP_USER = process.env.WP_USERNAME;
const WP_PASS = process.env.WP_APP_PASSWORD; // Application Password
const authHeader = 'Basic ' + Buffer.from(`${WP_USER}:${WP_PASS}`).toString('base64');
async function resolveTagIds(tagNames) {
const ids = [];
for (const name of tagNames) {
// Prüfen, ob der Tag bereits existiert
const searchRes = await fetch(
`${WP_BASE}/wp-json/wp/v2/tags?search=${encodeURIComponent(name)}`,
{ headers: { Authorization: authHeader } }
);
const existing = await searchRes.json();
if (existing.length > 0) {
ids.push(existing[0].id);
} else {
// Tag neu anlegen
const createRes = await fetch(`${WP_BASE}/wp-json/wp/v2/tags`, {
method: 'POST',
headers: {
Authorization: authHeader,
'Content-Type': 'application/json',
},
body: JSON.stringify({ name }),
});
const newTag = await createRes.json();
ids.push(newTag.id);
}
}
return ids;
}
export async function publishToWordPress({ frontmatter, html }) {
const tagIds = await resolveTagIds(frontmatter.tags ?? []);
// Prüfen, ob der Artikel bereits existiert (Idempotenz)
const slugCheckRes = await fetch(
`${WP_BASE}/wp-json/wp/v2/posts?slug=${frontmatter.slug}`,
{ headers: { Authorization: authHeader } }
);
const existing = await slugCheckRes.json();Die Idempotenz-Prüfung stellt sicher, dass ein versehentlich doppelt ausgelöster Workflow nicht zu doppelten Artikeln führt. Das System erkennt anhand des Slugs, ob ein neuer Artikel erstellt oder ein bestehender aktualisiert werden muss.
Mit dieser Pipeline können Sie Ihre Markdown-Inhalte effizient und ohne manuelle Formatierungsarbeit in WordPress, Ghost oder Webflow veröffentlichen. Die Automatisierung spart nicht nur Zeit, sondern reduziert auch menschliche Fehler und sorgt für eine konsistente Darstellung Ihrer Inhalte.
Für Teams, die regelmäßig Inhalte produzieren, ist diese Lösung ein Game-Changer – doch der größte Nutzen liegt in der Freiheit, sich auf das Wesentliche zu konzentrieren: großartige Inhalte zu schreiben, statt sie manuell zu veröffentlichen.
KI-Zusammenfassung
GitHub Actions ile birlikte Markdown dosyalarınızı CMS'ye otomatik olarak yayınlayabileceğiniz bir sistem oluşturabilirsiniz. Bu sistem, WordPress, Ghost ve Webflow gibi platformları desteklemektedir.