<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>DevOps on kenji.blog</title><link>http://kenji.blog/de/categories/devops/</link><description>Recent content in DevOps on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>de</language><copyright>kenjinote</copyright><lastBuildDate>Sun, 13 Sep 2026 09:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/de/categories/devops/index.xml" rel="self" type="application/rss+xml"/><item><title>【Git-Befehle】Der Unterschied zwischen rebase und merge und deren richtige Anwendung in der Praxis</title><link>http://kenji.blog/de/p/git-rebase-vs-merge-practical-guide/</link><pubDate>Sun, 13 Sep 2026 09:00:00 +0900</pubDate><guid>http://kenji.blog/de/p/git-rebase-vs-merge-practical-guide/</guid><description>&lt;img src="http://kenji.blog/p/git-rebase-vs-merge-practical-guide/img/eyecatch.jpg" alt="Featured image of post 【Git-Befehle】Der Unterschied zwischen rebase und merge und deren richtige Anwendung in der Praxis" />&lt;h1 id="1-einführung-warum-merge-oder-rebase-ein-ewiges-thema-ist">1. Einführung: Warum „merge oder rebase“ ein ewiges Thema ist
&lt;/h1>&lt;p>Git ist ein unverzichtbares Versionskontrollsystem in der modernen Softwareentwicklung. Wenn mehrere Entwickler gleichzeitig die Codebasis ändern, spielt das leistungsstarke Branching-Modell von Git seine Stärken aus. In der Teamentwicklung ist die Diskussion darüber, ob man &lt;code>merge&lt;/code> oder &lt;code>rebase&lt;/code> verwenden sollte, jedoch eines der Themen, das Entwickler vom Anfänger bis zum Experten immer wieder beschäftigt.&lt;/p>
&lt;p>In diesem Artikel werden wir die Unterschiede im Mechanismus von &lt;code>git merge&lt;/code> und &lt;code>git rebase&lt;/code> tiefgehend untersuchen, indem wir die interne Struktur von Git, wie den DAG (gerichteter azyklischer Graph) und die mathematischen Eigenschaften von Commit-Hashes, entschlüsseln. Darüber hinaus werden wir anhand von konkreten Workflows ausführlich erklären, wie man diese beiden in der Praxis richtig einsetzt. Indem Sie nicht nur die Befehle kennenlernen, sondern auch verstehen, welche Berechnungen Git im Hintergrund durchführt, verlieren Sie die Angst vor Konflikten und können eine saubere, nachvollziehbare Historie aufbauen.&lt;/p>
&lt;hr>
&lt;h1 id="2-interne-struktur-von-git-commit-hashes-und-objektmodell">2. Interne Struktur von Git: Commit-Hashes und Objektmodell
&lt;/h1>&lt;p>Um zu verstehen, wie Git die Historie integriert, müssen wir zunächst wissen, wie Git Daten speichert. Git speichert nicht einfach nur die Unterschiede (Patches) von Dateiänderungen, sondern einen Schnappschuss (Snapshot) des gesamten Dateisystems zu einem bestimmten Zeitpunkt.&lt;/p>
&lt;h2 id="21-kryptographische-eigenschaften-von-commit-hashes">2.1 Kryptographische Eigenschaften von Commit-Hashes
&lt;/h2>&lt;p>Jeder Commit in Git wird durch eine 40-stellige Hexadezimalzahl, die mit der Hash-Funktion SHA-1 (Secure Hash Algorithm 1) basierend auf seinem Inhalt berechnet wird, eindeutig identifiziert. Ein Commit-Objekt besteht aus den folgenden Elementen:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Zeiger auf das Tree-Objekt&lt;/strong>: Ein Schnappschuss der Verzeichnisstruktur und der Dateien (Blobs) zu diesem Zeitpunkt.&lt;/li>
&lt;li>&lt;strong>Zeiger auf den Eltern-Commit&lt;/strong>: Die Hash-Werte eines oder mehrerer Eltern-Commits (der erste Commit hat keinen Elternteil, ein Merge-Commit hat zwei oder mehr).&lt;/li>
&lt;li>&lt;strong>Autor-Informationen (Author)&lt;/strong>: Die Person, die den Code geschrieben hat, und das Datum.&lt;/li>
&lt;li>&lt;strong>Committer-Informationen (Committer)&lt;/strong>: Die Person, die den Commit erstellt/angewendet hat, und das Datum.&lt;/li>
&lt;li>&lt;strong>Commit-Nachricht&lt;/strong>: Text, der die Absicht der Änderung erklärt.&lt;/li>
&lt;/ol>
&lt;p>Mathematisch ausgedrückt wird der Hash-Wert $H(C)$ für ein Commit-Objekt $C$ wie folgt definiert:&lt;/p>
$$
H(C) = \text{SHA-1}( \text{tree} \parallel \text{parent} \parallel \text{author} \parallel \text{committer} \parallel \text{message} )
$$&lt;p>Hierbei steht $\parallel$ für die Verkettung von Daten. Aufgrund der Eigenschaften der Hash-Funktion führt selbst die Änderung eines einzigen Zeichens in der Commit-Nachricht oder ein anderer Eltern-Commit zu einem völlig anderen Hash-Wert. Das bedeutet: &lt;strong>Commits sind unveränderlich (immutable)&lt;/strong>. Wenn später gesagt wird, dass &lt;code>rebase&lt;/code> &amp;ldquo;die Historie umschreibt&amp;rdquo;, so bedeutet das in Wirklichkeit, dass &amp;ldquo;neue Commits erstellt werden, die einen ähnlichen Inhalt, aber unterschiedliche Hash-Werte haben&amp;rdquo;.&lt;/p>
&lt;p>Die Größe des Hash-Raums beträgt $2^{160}$. Die Wahrscheinlichkeit $P$, dass eine Kollision auftritt (dass verschiedene Commits denselben Hash-Wert haben), kann mithilfe der Theorie des Geburtstagsparadoxons (Birthday Paradox) wie folgt angenähert werden (wobei $n$ die Anzahl der Commits ist):&lt;/p>
$$
P(\text{collision}) \approx 1 - \exp\left(-\frac{n^2}{2 \times 2^{160}}\right)
$$&lt;p>Diese Wahrscheinlichkeit ist extrem gering, und in der Praxis ist es nahezu unmöglich, dass Git-Commit-Hashes kollidieren.&lt;/p>
&lt;hr>
&lt;h1 id="3-graphentheorie-und-dag-das-mathematische-modell-der-git-historie">3. Graphentheorie und DAG: Das mathematische Modell der Git-Historie
&lt;/h1>&lt;p>Die Commit-Historie von Git wird als „gerichteter azyklischer Graph“ (Directed Acyclic Graph, DAG) aus der Graphentheorie modelliert.&lt;/p>
&lt;h2 id="31-was-ist-ein-dag-gerichteter-azyklischer-graph">3.1 Was ist ein DAG (gerichteter azyklischer Graph)?
&lt;/h2>&lt;p>In einem Graphen $G = (V, E)$ ist $V$ die Menge der Commits (Knoten) und $E$ die Menge der gerichteten Kanten, die die Eltern-Kind-Beziehungen zwischen den Commits darstellen. In Git ist die Richtung der Kanten „vom Kind-Commit zum Eltern-Commit“ gerichtet, da neue Commits Zeiger auf vergangene Commits speichern.&lt;/p>
&lt;pre class="mermaid">
graph BT
A[&amp;#34;Commit A (Initial)&amp;#34;]
B[&amp;#34;Commit B&amp;#34;]
C[&amp;#34;Commit C (Main)&amp;#34;]
D[&amp;#34;Commit D (Feature)&amp;#34;]
E[&amp;#34;Commit E (Merge)&amp;#34;]
B --&amp;gt; A
C --&amp;gt; B
D --&amp;gt; B
E --&amp;gt; C
E --&amp;gt; D
&lt;/pre>
&lt;p>Das Hauptmerkmal eines DAG ist, dass „keine Zyklen (Schleifen) existieren“. Dadurch geraten Algorithmen, die die Commit-Historie zurückverfolgen, nie in eine Endlosschleife und erreichen sicher das Ende (den initialen Commit).&lt;/p>
&lt;h2 id="32-topologische-sortierung-und-die-reihenfolge-der-historie">3.2 Topologische Sortierung und die Reihenfolge der Historie
&lt;/h2>&lt;p>Wenn Git die Historie mit Befehlen wie &lt;code>git log&lt;/code> anzeigt, wird der DAG durch den Algorithmus der topologischen Sortierung (Topological Sort) als eindimensionale Liste geordnet. Für jede gerichtete Kante $u \to v$ im DAG ($u$ ist das Kind von $v$) wird die Liste so sortiert, dass $u$ vor $v$ steht.&lt;/p>
&lt;hr>
&lt;h1 id="4-der-mechanismus-und-die-arten-von-git-merge">4. Der Mechanismus und die Arten von git merge
&lt;/h1>&lt;p>Der grundlegendste Befehl zur Integration von Branch-Änderungen ist &lt;code>git merge&lt;/code>. Je nach aktuellem Zustand wählt Git jedoch automatisch unterschiedliche Merge-Strategien aus.&lt;/p>
&lt;h2 id="41-fast-forward-merge---ff">4.1 Fast-Forward-Merge (&amp;ndash;ff)
&lt;/h2>&lt;p>Wenn der Ziel-Branch (z. B. &lt;code>main&lt;/code>) ein direkter Vorfahre des Quell-Branch (z. B. &lt;code>feature&lt;/code>) ist, führt Git einen „Fast-Forward“-Merge (Vorspulen) aus. Dabei wird kein neuer Commit erstellt, sondern lediglich der Branch-Zeiger nach vorne verschoben.&lt;/p>
&lt;pre class="mermaid">
gitGraph
commit id: &amp;#34;A&amp;#34;
commit id: &amp;#34;B&amp;#34;
branch feature
checkout feature
commit id: &amp;#34;C&amp;#34;
commit id: &amp;#34;D&amp;#34;
checkout main
merge feature
&lt;/pre>
&lt;p>Ein Fast-Forward-Merge hält die Historie geradlinig, hat aber den Nachteil, dass der Kontext verloren geht, „welche Commit-Gruppe als eine einzige Feature-Entwicklung zusammengehörte“.&lt;/p>
&lt;h2 id="42-non-fast-forward-merge---no-ff">4.2 Non-Fast-Forward-Merge (&amp;ndash;no-ff)
&lt;/h2>&lt;p>Wenn Sie explizit &lt;code>git merge --no-ff&lt;/code> angeben, wird in jedem Fall ein neuer „Merge-Commit“ erstellt, selbst wenn ein Fast-Forward möglich wäre. Ein Merge-Commit ist ein spezieller Commit mit zwei Elternteilen.&lt;/p>
&lt;pre class="mermaid">
gitGraph
commit id: &amp;#34;A&amp;#34;
commit id: &amp;#34;B&amp;#34;
branch feature
checkout feature
commit id: &amp;#34;C&amp;#34;
commit id: &amp;#34;D&amp;#34;
checkout main
commit id: &amp;#34;Hauptarbeit 1&amp;#34;
merge feature type: NORMAL
&lt;/pre>
&lt;p>Der Vorteil dieser Methode besteht darin, dass die Existenz und die Geschichte des Feature-Branches deutlich im DAG erhalten bleiben. Wenn ein Problem auftritt, kann durch Ausführen von &lt;code>git revert -m 1 &amp;lt;Hash des Merge-Commits&amp;gt;&lt;/code> das gesamte Feature auf einmal sicher rückgängig gemacht (reverted) werden.&lt;/p>
&lt;h2 id="43-3-way-merge-algorithmus-3-wege-merge">4.3 3-Way-Merge-Algorithmus (3-Wege-Merge)
&lt;/h2>&lt;p>Wenn der Ziel- und der Quell-Branch jeweils eigene Commits haben, führt Git einen 3-Way-Merge durch. Dabei durchsucht Git den DAG, um den „nächsten gemeinsamen Vorfahren“ (Lowest Common Ancestor, LCA) der beiden Branches zu finden.&lt;/p>
&lt;p>Die zeitliche Komplexität $T_{\text{LCA}}$ des Algorithmus zum Finden des LCA ist linear in Bezug auf die Anzahl der Knoten $|V|$ und Kanten $|E|$:&lt;/p>
$$
T_{\text{LCA}} = \mathcal{O}(|V| + |E|)
$$&lt;p>Git vergleicht den „Zustand des LCA“, den „Zustand des aktuellen Branches“ und den „Zustand des anderen Branches“. Wenn die Änderungen nicht im Konflikt stehen, wird automatisch ein Merge-Commit generiert.&lt;/p>
&lt;hr>
&lt;h1 id="5-der-mechanismus-von-git-rebase-und-die-rekonstruktion-der-historie">5. Der Mechanismus von git rebase und die Rekonstruktion der Historie
&lt;/h1>&lt;p>Während &lt;code>git merge&lt;/code> die Historie „integriert“, wird sie bei &lt;code>git rebase&lt;/code> „rekonstruiert“ (neu angesetzt).&lt;/p>
&lt;h2 id="51-die-vorgänge-hinter-rebase">5.1 Die Vorgänge hinter Rebase
&lt;/h2>&lt;p>Die internen Vorgänge beim Rebasen eines &lt;code>feature&lt;/code>-Branches auf den &lt;code>main&lt;/code>-Branch (&lt;code>git rebase main&lt;/code>) sind wie folgt:&lt;/p>
&lt;ol>
&lt;li>Finde den gemeinsamen Vorfahren (LCA) des &lt;code>feature&lt;/code>-Branches und des &lt;code>main&lt;/code>-Branches.&lt;/li>
&lt;li>Speichere die Diffs (Änderungen) der Commits vom LCA bis zur Spitze des &lt;code>feature&lt;/code>-Branches in einem temporären Bereich.&lt;/li>
&lt;li>Verschiebe den Zeiger des &lt;code>feature&lt;/code>-Branches auf die Spitze des &lt;code>main&lt;/code>-Branches.&lt;/li>
&lt;li>Wende die gespeicherten Diffs der Reihe nach auf die neue Basis (die Spitze von &lt;code>main&lt;/code>) an (Cherry-Pick) und generiere so neue Commits.&lt;/li>
&lt;/ol>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;Commit A&amp;#34;] --&amp;gt; B[&amp;#34;Commit B&amp;#34;]
B --&amp;gt; C[&amp;#34;Commit C (Main)&amp;#34;]
B --&amp;gt; D[&amp;#34;Commit D (Altes Feature)&amp;#34;]
D -.-&amp;gt; E[&amp;#34;Commit D&amp;#39; (Neues Feature)&amp;#34;]
C --&amp;gt; E
style D stroke-dasharray: 5 5, fill: #f9f9f9, color: #999
&lt;/pre>
&lt;p>Hierbei ist wichtig, dass der durch den Rebase erstellte Commit $D'$ einen &lt;strong>anderen Eltern-Commit als der ursprüngliche Commit $D$ hat und somit einen völlig anderen Hash-Wert besitzt&lt;/strong> (siehe Definition der Hash-Funktion $H(C)$ weiter oben).&lt;/p>
&lt;h2 id="52-interaktives-rebase-interactive-rebase">5.2 Interaktives Rebase (Interactive Rebase)
&lt;/h2>&lt;p>Mit &lt;code>git rebase -i&lt;/code> (oder &lt;code>--interactive&lt;/code>) können Sie die Commit-Historie ganz nach Ihren Wünschen bearbeiten. Dies ist das mächtigste Werkzeug, um die lokale Historie zu bereinigen.&lt;/p>
&lt;ul>
&lt;li>&lt;code>pick&lt;/code>: Den Commit so übernehmen, wie er ist.&lt;/li>
&lt;li>&lt;code>reword&lt;/code>: Nur die Commit-Nachricht bearbeiten.&lt;/li>
&lt;li>&lt;code>edit&lt;/code>: Den Vorgang pausieren, um den Inhalt des Commits zu bearbeiten.&lt;/li>
&lt;li>&lt;code>squash&lt;/code>: Diesen Commit mit dem vorherigen Commit verschmelzen und auch die Nachrichten kombinieren.&lt;/li>
&lt;li>&lt;code>fixup&lt;/code>: Wie &lt;code>squash&lt;/code>, jedoch wird die Nachricht dieses Commits verworfen.&lt;/li>
&lt;li>&lt;code>drop&lt;/code>: Den Commit vollständig löschen.&lt;/li>
&lt;/ul>
&lt;p>Aus mathematischer Sicht ergeben sich bei $N$ Commits in einem Branch folgende Variationen (Permutationen) $P$ an möglichen linearen Historien, die durch Ändern der Reihenfolge beim Rebase generiert werden können:&lt;/p>
$$
P = N!
$$&lt;p>Git gibt Entwicklern $N!$ Möglichkeiten und erlaubt es so, die Historie in einem logischen und aufgeräumten Zustand zu halten.&lt;/p>
&lt;hr>
&lt;h1 id="6-die-goldene-regel-des-rebasens-the-golden-rule-of-rebase">6. Die Goldene Regel des Rebasens (The Golden Rule of Rebase)
&lt;/h1>&lt;p>&lt;code>rebase&lt;/code> ist extrem mächtig, aber es gibt eine absolute Regel:&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>„Rebasen Sie niemals eine veröffentlichte, öffentliche Historie!“&lt;/strong>
&lt;em>(Never rebase public history)&lt;/em>&lt;/p>
&lt;/blockquote>
&lt;h2 id="61-warum-darf-man-eine-öffentliche-historie-nicht-rebasen">6.1 Warum darf man eine öffentliche Historie nicht rebasen?
&lt;/h2>&lt;p>Git ist dezentralisiert. Die Commits, die Sie nach &lt;code>origin/main&lt;/code> pushen, werden auch in die lokalen Repositories anderer Entwickler geklont (kopiert). Was passiert, wenn Sie einen bereits gepushten Commit rebasen, die Historie neu schreiben und diese mit &lt;code>git push --force&lt;/code> erzwingen?&lt;/p>
&lt;p>Der DAG in den lokalen Repositories der anderen Entwickler weicht grundlegend vom DAG auf dem Remote-Server ab. Wenn ein anderer Entwickler &lt;code>git pull&lt;/code> ausführt, wird Git versuchen, Commits mit unterschiedlicher Historie gewaltsam zusammenzuführen. Dies führt zu massiven Konflikten und doppelten Commits (Commits mit gleichem Inhalt, aber unterschiedlichen Hashes), was das Repository in einen chaotischen Zustand versetzt.&lt;/p>
&lt;p>Die eiserne Regel lautet: Führen Sie Rebase &lt;strong>nur auf „lokalen Branches, die noch mit niemandem geteilt wurden“&lt;/strong> aus.&lt;/p>
&lt;hr>
&lt;h1 id="7-konfliktauflösung-und-git-rebase---continue">7. Konfliktauflösung und git rebase &amp;ndash;continue
&lt;/h1>&lt;p>Wenn mehrere Personen dieselbe Stelle in derselben Datei ändern, entsteht ein Konflikt (Conflict). Bei &lt;code>merge&lt;/code> und &lt;code>rebase&lt;/code> unterscheidet sich der Prozess der Konfliktauflösung.&lt;/p>
&lt;h2 id="71-konfliktauflösung-beim-merge">7.1 Konfliktauflösung beim Merge
&lt;/h2>&lt;p>Bei &lt;code>git merge&lt;/code> tritt die Konfliktauflösung &lt;strong>nur einmal&lt;/strong> auf. Kurz bevor der endgültige Merge-Commit erstellt wird, werden alle Konflikte auf einmal behoben.&lt;/p>
&lt;h2 id="72-konfliktauflösung-beim-rebase">7.2 Konfliktauflösung beim Rebase
&lt;/h2>&lt;p>Aufgrund der Eigenschaft von &lt;code>git rebase&lt;/code>, Commits nacheinander neu anzuwenden, besteht &lt;strong>die Möglichkeit, dass bei jedem einzelnen Commit ein Konflikt auftritt&lt;/strong>.&lt;/p>
&lt;p>Wenn während eines Rebase ein Konflikt auftritt, pausiert Git den Vorgang. Der Ablauf zur Lösung sieht wie folgt aus:&lt;/p>
&lt;ol>
&lt;li>Öffnen Sie einen Editor oder eine IDE (wie VS Code) und beheben Sie die Konfliktmarkierungen (&lt;code>&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/code>, &lt;code>======&lt;/code> und &lt;code>&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code>) manuell.&lt;/li>
&lt;li>Fügen Sie die bearbeitete Datei dem Index hinzu:
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git add &amp;lt;bearbeitete Datei&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>Setzen Sie den Rebase-Vorgang fort, ohne einen neuen Commit zu erstellen:
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git rebase --continue
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>Wenn Sie den Rebase komplett abbrechen und in den ursprünglichen Zustand zurückkehren möchten, führen Sie folgenden Befehl aus:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git rebase --abort
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>(&lt;em>Wenn keine Konfliktauflösung erforderlich ist und Sie den gesamten Commit überspringen möchten, verwenden Sie &lt;code>git rebase --skip&lt;/code>.&lt;/em>)&lt;/p>
&lt;hr>
&lt;h1 id="8-der-richtige-einsatz-in-der-praxis-workflow-praxis">8. Der richtige Einsatz in der Praxis (Workflow-Praxis)
&lt;/h1>&lt;p>Wie sollten Sie nun in der tatsächlichen Entwicklungsumgebung zwischen &lt;code>merge&lt;/code> und &lt;code>rebase&lt;/code> wählen? Hier stellen wir den standardmäßigsten und sichersten Ansatz vor.&lt;/p>
&lt;h2 id="81-szenario-1-bereinigung-der-lokalen-arbeitshistorie-verwendung-von-rebase">8.1 [Szenario 1] Bereinigung der lokalen Arbeitshistorie (Verwendung von Rebase)
&lt;/h2>&lt;p>Angenommen, Sie haben während der Entwicklung in einem Feature-Branch viele kleine Commits angesammelt (z. B. &amp;ldquo;Tippfehler behoben&amp;rdquo;, &amp;ldquo;Temporär gespeichert&amp;rdquo; usw.). Bevor Sie einen Pull Request (PR) erstellen, nutzen Sie interaktives Rebase, um diese zu sinnvollen Einheiten zusammenzufassen.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Ausführen, während Sie sich im feature-Branch befinden&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git rebase -i HEAD~5
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># (Ein Editor öffnet sich; nutzen Sie squash oder fixup, um die Historie zu bereinigen)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Dadurch können Sie eine saubere Commit-Historie erstellen, bei der die Absichten für den Reviewer leichter nachvollziehbar sind.&lt;/p>
&lt;h2 id="82-szenario-2-aktualisierung-auf-den-neuesten-main-branch-verwendung-von-rebase">8.2 [Szenario 2] Aktualisierung auf den neuesten main-Branch (Verwendung von Rebase)
&lt;/h2>&lt;p>Wenn die Entwicklung länger dauert und fortlaufend Änderungen anderer Personen in den &lt;code>main&lt;/code>-Branch gemergt werden, wird Ihr eigener &lt;code>feature&lt;/code>-Branch veraltet sein. In diesem Fall aktualisieren Sie Ihren &lt;code>feature&lt;/code>-Branch, indem Sie ihn auf den neuesten Stand von &lt;code>main&lt;/code> rebasen.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Die neuesten Informationen von main abrufen&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git fetch origin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Den feature-Branch auf den neuesten main-Branch aufsetzen&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git rebase origin/main
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Dadurch bleibt die Historie linear, und Sie können Konflikte bei späteren Merges vermeiden. Außerdem wird verhindert, dass überflüssige Merge-Commits (&amp;ldquo;Merge branch &amp;lsquo;main&amp;rsquo; into feature&amp;rdquo;) erstellt werden.&lt;/p>
&lt;h2 id="83-szenario-3-integration-abgeschlossener-features-verwendung-von-merge">8.3 [Szenario 3] Integration abgeschlossener Features (Verwendung von Merge)
&lt;/h2>&lt;p>Wenn die Entwicklung im &lt;code>feature&lt;/code>-Branch abgeschlossen ist, kommt die Phase der Integration in den &lt;code>main&lt;/code>-Branch. Hier verwenden Sie &lt;strong>&lt;code>git merge --no-ff&lt;/code>&lt;/strong> (das entspricht der Auswahl von &amp;ldquo;Create a merge commit&amp;rdquo; bei einem Pull Request auf GitHub).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git checkout main
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git merge --no-ff feature -m &lt;span class="s2">&amp;#34;Merge feature: Implementierung der Benutzer-Login-Funktion&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git push origin main
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Dadurch bleibt im DAG des &lt;code>main&lt;/code>-Branches ein Knotenpunkt in der Historie (Merge-Commit) erhalten, der besagt: „Hier wurde ein Feature gemergt“. Wenn Sie später die Historie betrachten, können Sie den Code pro Feature-Einheit viel leichter nachvollziehen.&lt;/p>
&lt;hr>
&lt;h1 id="9-fazit-zusammenfassung">9. Fazit (Zusammenfassung)
&lt;/h1>&lt;p>Bei der Arbeit mit Git haben extreme Ansätze wie „Alles mit Merge erledigen“ oder „Alles mit Rebase linear halten“ jeweils ihre Vor- und Nachteile.&lt;/p>
&lt;p>Die Best Practice in der Praxis ist ein hybrider Ansatz: &lt;strong>„Nutzen Sie rebase, um die private lokale Historie schön aufzuräumen, und verwenden Sie merge &amp;ndash;no-ff für die öffentliche Integrationshistorie, um den Kontext zu erhalten.“&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Lokal (persönlicher Arbeitsbereich)&lt;/strong>: Verwenden Sie &lt;code>rebase&lt;/code>, um unnötige Commits zu eliminieren, der neuesten Mainline zu folgen und eine lineare Historie beizubehalten.&lt;/li>
&lt;li>&lt;strong>Global (geteilter Arbeitsbereich)&lt;/strong>: Verwenden Sie &lt;code>merge --no-ff&lt;/code>, um die Existenz des Feature-Branches als Merge-Commit im DAG aufzuzeichnen und so Reverts und Tracking zu erleichtern.&lt;/li>
&lt;/ul>
&lt;p>Durch das Verständnis des mathematischen und architektonischen Hintergrunds, wie der Struktur des DAGs und der Funktionsweise von Hash-Funktionen, werden Git-Befehle von purem Auswendiglernen zum „Entwurf einer Historie mit Absicht“ erhoben. Beachten Sie die Goldene Regel des Rebasens, wählen Sie je nach Situation den optimalen Befehl und bauen Sie eine saubere Commit-Historie auf, die für das gesamte Team leicht lesbar und wartbar ist.&lt;/p></description></item><item><title>Aufbau einer CI/CD-Pipeline für ein C++-Projekt mit GitHub Actions</title><link>http://kenji.blog/de/p/github-actions-cpp-cicd-pipeline/</link><pubDate>Sat, 12 Sep 2026 18:00:00 +0900</pubDate><guid>http://kenji.blog/de/p/github-actions-cpp-cicd-pipeline/</guid><description>&lt;img src="http://kenji.blog/p/github-actions-cpp-cicd-pipeline/img/eyecatch.jpg" alt="Featured image of post Aufbau einer CI/CD-Pipeline für ein C++-Projekt mit GitHub Actions" />&lt;h1 id="aufbau-einer-cicd-pipeline-für-c-projekte-mit-github-actions-ein-vollständiger-leitfaden">Aufbau einer CI/CD-Pipeline für C++-Projekte mit GitHub Actions: Ein vollständiger Leitfaden
&lt;/h1>&lt;p>Im modernen Softwareentwicklungs-Paradigma sind Continuous Integration (CI) und Continuous Delivery/Deployment (CD) unverzichtbare Elemente für agile Entwicklungsprozesse und die Aufrechterhaltung hochwertiger Software. Unter den zahlreichen Programmiersprachen bringt der Aufbau einer CI/CD-Pipeline für C++ im Vergleich zu anderen Sprachen (wie Python, JavaScript oder Go) einzigartige Schwierigkeiten und Komplexitäten mit sich.&lt;/p>
&lt;p>In diesem Artikel wird äußerst detailliert erklärt, wie Sie mit GitHub Actions eine robuste und praktische CI/CD-Pipeline für C++-Projekte von Grund auf neu aufbauen. Wir behandeln alle praktischen Techniken, einschließlich Matrix-Builds auf verschiedenen Plattformen (Windows, Linux, macOS), der Integration des Build-Systems mit CMake, automatisierter Tests mit CTest, der Automatisierung statischer und dynamischer Analysen, der Messung der Testabdeckung und der automatischen Auslieferung kompilierter Binärdateien über GitHub Releases.&lt;/p>
&lt;h2 id="1-die-bedeutung-von-cicd-in-c-projekten-und-spezifische-herausforderungen">1. Die Bedeutung von CI/CD in C++-Projekten und spezifische Herausforderungen
&lt;/h2>&lt;p>Bei der Entwicklung von Webanwendungen oder mit Skriptsprachen reicht es oft aus, Tests und Builds in einem einzelnen Docker-Container durchzuführen. C++ ist jedoch eine nativ kompilierte Sprache und hängt stark von der Hardwarearchitektur und dem Betriebssystem der Ausführungsumgebung ab.&lt;/p>
&lt;p>Bei der Einführung von CI/CD in einem C++-Projekt treten hauptsächlich folgende Herausforderungen auf:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Plattformvielfalt&lt;/strong>: Verschiedene Betriebssysteme wie Windows, Linux und macOS haben unterschiedliche APIs (Windows API, POSIX usw.). Es ist an der Tagesordnung, dass Code, der in der lokalen Umgebung eines Entwicklers (z. B. macOS) funktioniert, unter Linux oder Windows zu Kompilierungsfehlern führt.&lt;/li>
&lt;li>&lt;strong>Unterschiede bei den Compilern&lt;/strong>: Wichtige Compiler wie Microsoft Visual C++ (MSVC), die GNU Compiler Collection (GCC) und Clang unterscheiden sich in ihrem Implementierungsgrad der C++-Standards (C++17, C++20, C++23), deren Interpretation und der Strenge der Warnungen.&lt;/li>
&lt;li>&lt;strong>Build-Zeiten&lt;/strong>: Bei großen C++-Projekten ist es nicht ungewöhnlich, dass Builds von einigen zehn Minuten bis zu mehreren Stunden dauern. CI-Umgebungen erfordern Caching-Strategien und Parallelisierung, um effizient mit begrenzten Rechenressourcen zu bauen.&lt;/li>
&lt;li>&lt;strong>Verwaltung von Abhängigkeiten&lt;/strong>: In C++ gibt es keinen absoluten Standard-Paketmanager wie npm oder pip. Es ist notwendig, Bibliotheken in der CI-Umgebung jedes Mal korrekt aufzulösen, z. B. durch die Verwendung von vcpkg, Conan oder CMakes &lt;code>FetchContent&lt;/code>.&lt;/li>
&lt;li>&lt;strong>Speicherverwaltung und undefiniertes Verhalten&lt;/strong>: Da Zeigeroperationen und manuelle Speicherverwaltung involviert sind, muss nicht nur die reine Logik getestet, sondern auch die Erkennung von Speicherlecks und undefiniertem Verhalten (Undefined Behavior) automatisiert werden.&lt;/li>
&lt;/ol>
&lt;p>Um diese Herausforderungen zu meistern, GitHub Actions ist die optimale Lösung, da es verschiedene OS-VMs on-demand bereitstellen und komplexe Workflows als Code (Configuration as Code) definieren kann.&lt;/p>
&lt;h2 id="2-architekturübersicht-der-cicd-pipeline">2. Architekturübersicht der CI/CD-Pipeline
&lt;/h2>&lt;p>Lassen Sie uns das Gesamtbild der CI/CD-Pipeline, die wir erstellen werden, visualisieren. Das folgende Mermaid-Sequenzdiagramm zeigt den Workflow vom Push des Codes bis zum Release.&lt;/p>
&lt;pre class="mermaid">
sequenceDiagram
participant Dev as &amp;#34;Entwickler&amp;#34;
participant Repo as &amp;#34;GitHub Repository&amp;#34;
participant Action as &amp;#34;GitHub Actions CI/CD&amp;#34;
participant Rel as &amp;#34;GitHub Releases&amp;#34;
Dev-&amp;gt;&amp;gt;Repo: &amp;#34;Branch pushen / PR öffnen&amp;#34;
Repo-&amp;gt;&amp;gt;Action: &amp;#34;CI-Workflow auslösen&amp;#34;
activate Action
Action-&amp;gt;&amp;gt;Action: &amp;#34;Lint &amp;amp; statische Analyse (Clang-Tidy)&amp;#34;
rect rgb(200, 220, 240)
note right of Action: &amp;#34;Plattformübergreifender Matrix-Build&amp;#34;
Action-&amp;gt;&amp;gt;Action: &amp;#34;Build auf Ubuntu (GCC/Clang)&amp;#34;
Action-&amp;gt;&amp;gt;Action: &amp;#34;Build auf Windows (MSVC)&amp;#34;
Action-&amp;gt;&amp;gt;Action: &amp;#34;Build auf macOS (Apple Clang)&amp;#34;
end
Action-&amp;gt;&amp;gt;Action: &amp;#34;CTest ausführen (mit ASAN/UBSAN)&amp;#34;
Action-&amp;gt;&amp;gt;Action: &amp;#34;Coverage-Bericht generieren&amp;#34;
alt &amp;#34;Wenn ein Tag gepusht wird (z.B. v1.0.0)&amp;#34;
Action-&amp;gt;&amp;gt;Action: &amp;#34;Binärdateien mit CPack paketieren&amp;#34;
Action-&amp;gt;&amp;gt;Rel: &amp;#34;ZIP/Tarball zum Release hochladen&amp;#34;
end
deactivate Action
Repo--&amp;gt;&amp;gt;Dev: &amp;#34;CI-Status melden (Erfolgreich/Fehlgeschlagen)&amp;#34;
&lt;/pre>
&lt;p>In dieser Architektur wird in der Pull-Request-Phase schnelles Feedback (statische Analyse, Build und Test) geliefert, und die Paketierung sowie Verteilung der Artefakte erfolgt zu dem Zeitpunkt, an dem ein Versions-Tag hinzugefügt wird.&lt;/p>
&lt;h2 id="3-projekteinrichtung-mit-modernem-cmake">3. Projekteinrichtung mit modernem CMake
&lt;/h2>&lt;p>Die Grundlage einer hervorragenden CI-Pipeline ist ein robustes Build-System. Wir verwenden CMake, den De-facto-Standard in C++. Hierbei verfolgen wir den zielorientierten Ansatz, der als &amp;ldquo;modernes CMake&amp;rdquo; bezeichnet wird.&lt;/p>
&lt;p>Wir gehen von folgender Verzeichnisstruktur des Projekts aus:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">my_cpp_project/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── CMakeLists.txt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── src/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── main.cpp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── calculator.cpp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── calculator.h
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── tests/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── CMakeLists.txt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── test_calculator.cpp
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Ein Beispiel für die Konfiguration der &lt;code>CMakeLists.txt&lt;/code> im Stammverzeichnis:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cmake_minimum_required&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">VERSION&lt;/span> &lt;span class="s">3.20&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MyCppProject&lt;/span> &lt;span class="s">VERSION&lt;/span> &lt;span class="s">1.0.0&lt;/span> &lt;span class="s">LANGUAGES&lt;/span> &lt;span class="s">CXX&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Einstellung des C++-Standards
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CMAKE_CXX_STANDARD&lt;/span> &lt;span class="s">20&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CMAKE_CXX_STANDARD_REQUIRED&lt;/span> &lt;span class="s">ON&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CMAKE_CXX_EXTENSIONS&lt;/span> &lt;span class="s">OFF&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c"># Deaktiviert compilerspezifische Erweiterungen für bessere Portabilität
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Striktes Warnungs-Handling
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">set_project_warnings&lt;/span> &lt;span class="s">target_name&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MSVC&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">target_compile_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">${&lt;/span>&lt;span class="nv">target_name&lt;/span>&lt;span class="o">}&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">/W4&lt;/span> &lt;span class="s">/WX&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">else&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">target_compile_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">${&lt;/span>&lt;span class="nv">target_name&lt;/span>&lt;span class="o">}&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">-Wall&lt;/span> &lt;span class="s">-Wextra&lt;/span> &lt;span class="s">-Wpedantic&lt;/span> &lt;span class="s">-Werror&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">endif&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">endfunction&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Erstellen des Bibliotheks-Targets
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">add_library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CalculatorLib&lt;/span> &lt;span class="s">src/calculator.cpp&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">target_include_directories&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CalculatorLib&lt;/span> &lt;span class="s">PUBLIC&lt;/span> &lt;span class="o">${&lt;/span>&lt;span class="nv">CMAKE_CURRENT_SOURCE_DIR&lt;/span>&lt;span class="o">}&lt;/span>&lt;span class="s">/src&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set_project_warnings&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CalculatorLib&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Erstellen des ausführbaren Targets
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">add_executable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MyApplication&lt;/span> &lt;span class="s">src/main.cpp&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">target_link_libraries&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MyApplication&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">CalculatorLib&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set_project_warnings&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MyApplication&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Aktivieren der Tests
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">enable_testing&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">add_subdirectory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">tests&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Definieren der Installationsregeln (für CPack)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">include&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">GNUInstallDirs&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">install&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">TARGETS&lt;/span> &lt;span class="s">MyApplication&lt;/span> &lt;span class="s">CalculatorLib&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">RUNTIME&lt;/span> &lt;span class="s">DESTINATION&lt;/span> &lt;span class="o">${&lt;/span>&lt;span class="nv">CMAKE_INSTALL_BINDIR&lt;/span>&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">LIBRARY&lt;/span> &lt;span class="s">DESTINATION&lt;/span> &lt;span class="o">${&lt;/span>&lt;span class="nv">CMAKE_INSTALL_LIBDIR&lt;/span>&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">ARCHIVE&lt;/span> &lt;span class="s">DESTINATION&lt;/span> &lt;span class="o">${&lt;/span>&lt;span class="nv">CMAKE_INSTALL_LIBDIR&lt;/span>&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Paketierungskonfiguration mit CPack
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CPACK_PROJECT_NAME&lt;/span> &lt;span class="o">${&lt;/span>&lt;span class="nv">PROJECT_NAME&lt;/span>&lt;span class="o">}&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CPACK_PROJECT_VERSION&lt;/span> &lt;span class="o">${&lt;/span>&lt;span class="nv">PROJECT_VERSION&lt;/span>&lt;span class="o">}&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CPACK_GENERATOR&lt;/span> &lt;span class="s2">&amp;#34;ZIP;TGZ&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">WIN32&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CPACK_GENERATOR&lt;/span> &lt;span class="s2">&amp;#34;ZIP&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">endif&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">include&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CPack&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Wichtige Punkte:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;code>CMAKE_CXX_EXTENSIONS OFF&lt;/code>: Verhindert die Abhängigkeit von nicht-standardkonformen Funktionen wie GNU-Erweiterungen und gewährleistet plattformübergreifende Kompatibilität.&lt;/li>
&lt;li>&lt;strong>Striktes Warnungs-Handling (&lt;code>-Werror&lt;/code> / &lt;code>/WX&lt;/code>)&lt;/strong>: Indem Compiler-Warnungen in der CI-Umgebung als Fehler behandelt werden, wird eine hohe Codequalität erzwungen.&lt;/li>
&lt;li>&lt;strong>GNUInstallDirs&lt;/strong>: Löst automatisch betriebssystemspezifische Standard-Installationspfade (wie &lt;code>/usr/local/bin&lt;/code> oder &lt;code>C:\Program Files&lt;/code>) auf.&lt;/li>
&lt;/ul>
&lt;h2 id="4-grundlagen-von-github-actions-und-die-matrix-strategie">4. Grundlagen von GitHub Actions und die Matrix-Strategie
&lt;/h2>&lt;p>GitHub Actions wird durch YAML-Dateien im Verzeichnis &lt;code>.github/workflows/&lt;/code> konfiguriert.
Die leistungsstärkste Funktion für C++-Projekte ist die &amp;ldquo;Matrix-Strategie&amp;rdquo; (Matrix Strategy). Dadurch können Kombinationen aus Betriebssystemen und Compilern dynamisch generiert und parallel ausgeführt werden.&lt;/p>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;Workflow auslösen&amp;#34;] --&amp;gt; B[&amp;#34;Matrix-Job-Evaluierung&amp;#34;]
B --&amp;gt; C[&amp;#34;Ubuntu 22.04 (GCC 12)&amp;#34;]
B --&amp;gt; D[&amp;#34;Ubuntu 22.04 (Clang 15)&amp;#34;]
B --&amp;gt; E[&amp;#34;Windows Server 2022 (MSVC)&amp;#34;]
B --&amp;gt; F[&amp;#34;macOS 14 (Apple Clang)&amp;#34;]
&lt;/pre>
&lt;p>Im Folgenden ist die grundlegende Job-Definition in YAML für einen Matrix-Build dargestellt:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">jobs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">build&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Build &amp;amp; Test [${{ matrix.os }} - ${{ matrix.compiler }}]&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ matrix.os }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">strategy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">fail-fast&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># Setzt die Builds für andere Betriebssysteme fort, auch wenn ein Job fehlschlägt&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matrix&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">include&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">os&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">gcc&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">c_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">gcc&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cpp_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">g++&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">os&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">clang&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">c_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">clang&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cpp_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">clang++&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">os&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">windows-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">msvc&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">c_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cl&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cpp_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cl&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">os&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">macos-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">apple-clang&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">c_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">clang&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cpp_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">clang++&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>fail-fast: false&lt;/code> ist sehr wichtig. Wenn man beispielsweise versehentlich eine Linux-spezifische API verwendet, schlägt der Ubuntu-Build fehl, aber man möchte gleichzeitig wissen, ob der Windows-Build erfolgreich gewesen wäre.&lt;/p>
&lt;h2 id="5-build-kosten-und-optimierung-der-parallelverarbeitung-mit-dem-amdahlschen-gesetz">5. Build-Kosten und Optimierung der Parallelverarbeitung mit dem Amdahlschen Gesetz
&lt;/h2>&lt;p>CI/CD in Cloud-Umgebungen ist ein Kampf gegen die Zeit, und Build-Zeiten wirken sich direkt auf Wartezeiten der Entwickler sowie auf laufende Kosten aus.
Lassen Sie uns hier die Optimierung der Build-Zeiten mathematisch anhand des &amp;ldquo;Amdahlschen Gesetzes&amp;rdquo; (Amdahl&amp;rsquo;s Law) aus der Informatik betrachten.&lt;/p>
&lt;p>Das Amdahlsche Gesetz definiert die theoretische maximale Geschwindigkeitssteigerung $S(N)$ bei Verwendung von $N$ Prozessoren, wenn $P$ der Anteil des Programms ist, der parallelisiert werden kann, wie folgt:&lt;/p>
$$ S(N) = \frac{1}{(1 - P) + \frac{P}{N}} $$&lt;p>Im C++-Buildprozess ist die Kompilierung jeder Übersetzungseinheit (Translation Unit: &lt;code>.cpp&lt;/code>-Datei) völlig unabhängig und kann parallelisiert werden. Andererseits sind die CMake-Konfiguration und die finale Link-Phase der Binärdatei im Wesentlichen sequenzielle Prozesse (nicht parallelisierbar).&lt;/p>
&lt;p>Angenommen, 80 % der gesamten Build-Zeit des Projekts entfallen auf die Kompilierungsphase ($P = 0.8$) und 20 % auf die sequenzielle Phase ($1 - P = 0.2$).
Der Standard-Runner (Linux) von GitHub Actions bietet 2 Kerne (Threads). Daher gilt für $N = 2$:&lt;/p>
$$ S(2) = \frac{1}{0.2 + \frac{0.8}{2}} = \frac{1}{0.2 + 0.4} = \frac{1}{0.6} \approx 1.67 $$&lt;p>Allein durch die Nutzung von 2 Kernen erhält man eine Geschwindigkeitssteigerung von etwa 1,67-fach. Um dies zu erreichen, ist es unerlässlich, die Option &lt;code>--parallel&lt;/code> im CMake-Build-Befehl anzugeben.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Projekt bauen&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cmake --build build --config Release --parallel 2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Darüber hinaus berücksichtigen wir die Kostenberechnung. Die Gesamtkosten $C_{total}$ für die Nutzung von GitHub Actions sind die Summe der Produkte aus der Ausführungszeit $T_i$ des Jobs und dem Stückpreis $R_i$ des Runners.&lt;/p>
$$ C_{total} = \sum_{i=1}^{M} \left( T_i \times R_i \right) $$&lt;p>Die Reduzierung der Build-Zeit beschleunigt nicht nur die Feedback-Schleife, sondern führt auch direkt zu einer Senkung der Betriebskosten des Projekts (insbesondere bei privaten Repositories). Wenn noch mehr Geschwindigkeit gefordert ist, ist die Einführung von &lt;code>ccache&lt;/code> zum Cachen der Kompilierungsergebnisse eine effektive Methode.&lt;/p>
&lt;h2 id="6-automatisierte-tests-und-integration-von-sanitizers">6. Automatisierte Tests und Integration von Sanitizers
&lt;/h2>&lt;p>Um Fehler in C++ präventiv zu vermeiden, wird dringend empfohlen, neben Unit-Tests sogenannte &amp;ldquo;Sanitizer&amp;rdquo; einzuführen, die Speicherlecks und undefiniertes Verhalten zur Laufzeit erkennen. Wir verwenden den von Google entwickelten AddressSanitizer (ASAN) und den UndefinedBehaviorSanitizer (UBSAN).&lt;/p>
&lt;p>Fügen Sie eine Option in CMake hinzu, um die Sanitizer zu aktivieren.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">option&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ENABLE_SANITIZERS&lt;/span> &lt;span class="s2">&amp;#34;Enable ASAN and UBSAN&amp;#34;&lt;/span> &lt;span class="s">OFF&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ENABLE_SANITIZERS&lt;/span> &lt;span class="s">AND&lt;/span> &lt;span class="s">CMAKE_CXX_COMPILER_ID&lt;/span> &lt;span class="s">MATCHES&lt;/span> &lt;span class="s2">&amp;#34;GNU|Clang&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_compile_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">-fsanitize=address,undefined&lt;/span> &lt;span class="s">-fno-omit-frame-pointer&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_link_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">-fsanitize=address,undefined&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">endif&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Aktivieren Sie diese Option für den Ubuntu-Job in der CI-Pipeline und führen Sie die Tests aus.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;CMake konfigurieren&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">CC&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ matrix.c_compiler }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">CXX&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ matrix.cpp_compiler }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> cmake -B build
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> -DCMAKE_BUILD_TYPE=Release
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> -DENABLE_SANITIZERS=${{ matrix.os == &amp;#39;ubuntu-latest&amp;#39; &amp;amp;&amp;amp; &amp;#39;ON&amp;#39; || &amp;#39;OFF&amp;#39; }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;CTest ausführen&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">working-directory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">build&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ASAN_OPTIONS&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;detect_leaks=1:symbolize=1&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">UBSAN_OPTIONS&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;print_stacktrace=1&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ctest --build-config Release --output-on-failure --parallel 2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Zur Testausführung wird der Befehl &lt;code>ctest&lt;/code> verwendet. Durch die Angabe von &lt;code>--output-on-failure&lt;/code> werden nur die detaillierten Protokolle der fehlgeschlagenen Tests in der CI-Ausgabe angezeigt, was verhindert, dass das Protokoll zu groß wird.&lt;/p>
&lt;h2 id="7-messung-der-testabdeckung-code-coverage">7. Messung der Testabdeckung (Code Coverage)
&lt;/h2>&lt;p>Die Visualisierung, wie viel Code durch Tests abgedeckt ist, ist für die Qualitätssicherung wichtig. Unter Verwendung der Linux-Umgebung (GCC) messen wir die Abdeckung mit &lt;code>gcov&lt;/code> und &lt;code>lcov&lt;/code>.&lt;/p>
&lt;p>Zuerst legen wir die Kompilierungsflags für die Coverage-Messung in CMake fest.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">option&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ENABLE_COVERAGE&lt;/span> &lt;span class="s2">&amp;#34;Enable coverage reporting&amp;#34;&lt;/span> &lt;span class="s">OFF&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ENABLE_COVERAGE&lt;/span> &lt;span class="s">AND&lt;/span> &lt;span class="s">CMAKE_CXX_COMPILER_ID&lt;/span> &lt;span class="s">STREQUAL&lt;/span> &lt;span class="s2">&amp;#34;GNU&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_compile_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">--coverage&lt;/span> &lt;span class="s">-O0&lt;/span> &lt;span class="s">-g&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_link_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">--coverage&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">endif&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Wir definieren einen separaten Job in GitHub Actions für die Coverage-Messung.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">coverage&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Test Coverage Analysis&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/checkout@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Install lcov&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">sudo apt-get update &amp;amp;&amp;amp; sudo apt-get install -y lcov&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Configure CMake for Coverage&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cmake -B build -DCMAKE_BUILD_TYPE=Debug -DENABLE_COVERAGE=ON&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Build &amp;amp; Test&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">|&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> cmake --build build --parallel 2
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> cd build &amp;amp;&amp;amp; ctest --output-on-failure&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Generate lcov Report&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">working-directory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">build&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">|&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> lcov --capture --directory . --output-file coverage.info
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> lcov --remove coverage.info &amp;#39;/usr/*&amp;#39; &amp;#39;*_deps/*&amp;#39; &amp;#39;*tests/*&amp;#39; --output-file coverage.info
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> &lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Upload Coverage to Codecov&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">codecov/codecov-action@v3&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">files&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">build/coverage.info&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Durch die Verwendung des Befehls &lt;code>lcov --remove&lt;/code> werden Systemheader, Bibliotheken von Drittanbietern und der Testcode selbst von der Coverage-Messung ausgeschlossen. Dadurch erhalten wir eine reine Abdeckung des projektspezifischen Quellcodes.&lt;/p>
&lt;h2 id="8-automatische-auslieferung-von-binärdateien-über-github-releases-cd">8. Automatische Auslieferung von Binärdateien über GitHub Releases (CD)
&lt;/h2>&lt;p>Wir bauen nun den &amp;ldquo;CD&amp;rdquo;-Teil von CI/CD auf. Wenn ein Entwickler einen Versions-Tag in Git (z. B. &lt;code>v1.2.0&lt;/code>) zuweist und pusht, werden die ausführbaren Binärdateien für jedes Betriebssystem automatisch kompiliert, in ZIP- oder Tarball-Archiven paketiert und in GitHub Releases hochgeladen.&lt;/p>
&lt;p>In diesem Schritt verwenden wir &lt;code>CPack&lt;/code>, ein mit CMake mitgeliefertes Paketierungstool.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Package Application (CPack)&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">if&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">startsWith(github.ref, &amp;#39;refs/tags/v&amp;#39;)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">working-directory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">build&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cpack -C Release -V&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Upload Release Assets&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">if&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">startsWith(github.ref, &amp;#39;refs/tags/v&amp;#39;)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">softprops/action-gh-release@v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">files&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">|&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> build/*.tar.gz
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> build/*.zip
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> build/*.sh
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> build/*.exe&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">GITHUB_TOKEN&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ secrets.GITHUB_TOKEN }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Mit dieser Konfiguration genügt die Ausführung von &lt;code>git tag v1.0.0&lt;/code> und &lt;code>git push origin v1.0.0&lt;/code>, damit für Windows-Benutzer eine ZIP-Datei und für Linux/macOS-Benutzer ein Tarball ohne manuelles Eingreifen automatisch auf der Release-Seite veröffentlicht wird. Dies ist eine äußerst leistungsstarke Funktion, um den Benutzern die Software bereitzustellen.&lt;/p>
&lt;h2 id="9-die-vollständige-workflow-yaml-datei">9. Die vollständige Workflow-YAML-Datei
&lt;/h2>&lt;p>Im Folgenden finden Sie den vollständigen Code einer robusten und praktischen &lt;code>.github/workflows/main.yml&lt;/code>, die alle bisher besprochenen Elemente integriert.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt"> 10
&lt;/span>&lt;span class="lnt"> 11
&lt;/span>&lt;span class="lnt"> 12
&lt;/span>&lt;span class="lnt"> 13
&lt;/span>&lt;span class="lnt"> 14
&lt;/span>&lt;span class="lnt"> 15
&lt;/span>&lt;span class="lnt"> 16
&lt;/span>&lt;span class="lnt"> 17
&lt;/span>&lt;span class="lnt"> 18
&lt;/span>&lt;span class="lnt"> 19
&lt;/span>&lt;span class="lnt"> 20
&lt;/span>&lt;span class="lnt"> 21
&lt;/span>&lt;span class="lnt"> 22
&lt;/span>&lt;span class="lnt"> 23
&lt;/span>&lt;span class="lnt"> 24
&lt;/span>&lt;span class="lnt"> 25
&lt;/span>&lt;span class="lnt"> 26
&lt;/span>&lt;span class="lnt"> 27
&lt;/span>&lt;span class="lnt"> 28
&lt;/span>&lt;span class="lnt"> 29
&lt;/span>&lt;span class="lnt"> 30
&lt;/span>&lt;span class="lnt"> 31
&lt;/span>&lt;span class="lnt"> 32
&lt;/span>&lt;span class="lnt"> 33
&lt;/span>&lt;span class="lnt"> 34
&lt;/span>&lt;span class="lnt"> 35
&lt;/span>&lt;span class="lnt"> 36
&lt;/span>&lt;span class="lnt"> 37
&lt;/span>&lt;span class="lnt"> 38
&lt;/span>&lt;span class="lnt"> 39
&lt;/span>&lt;span class="lnt"> 40
&lt;/span>&lt;span class="lnt"> 41
&lt;/span>&lt;span class="lnt"> 42
&lt;/span>&lt;span class="lnt"> 43
&lt;/span>&lt;span class="lnt"> 44
&lt;/span>&lt;span class="lnt"> 45
&lt;/span>&lt;span class="lnt"> 46
&lt;/span>&lt;span class="lnt"> 47
&lt;/span>&lt;span class="lnt"> 48
&lt;/span>&lt;span class="lnt"> 49
&lt;/span>&lt;span class="lnt"> 50
&lt;/span>&lt;span class="lnt"> 51
&lt;/span>&lt;span class="lnt"> 52
&lt;/span>&lt;span class="lnt"> 53
&lt;/span>&lt;span class="lnt"> 54
&lt;/span>&lt;span class="lnt"> 55
&lt;/span>&lt;span class="lnt"> 56
&lt;/span>&lt;span class="lnt"> 57
&lt;/span>&lt;span class="lnt"> 58
&lt;/span>&lt;span class="lnt"> 59
&lt;/span>&lt;span class="lnt"> 60
&lt;/span>&lt;span class="lnt"> 61
&lt;/span>&lt;span class="lnt"> 62
&lt;/span>&lt;span class="lnt"> 63
&lt;/span>&lt;span class="lnt"> 64
&lt;/span>&lt;span class="lnt"> 65
&lt;/span>&lt;span class="lnt"> 66
&lt;/span>&lt;span class="lnt"> 67
&lt;/span>&lt;span class="lnt"> 68
&lt;/span>&lt;span class="lnt"> 69
&lt;/span>&lt;span class="lnt"> 70
&lt;/span>&lt;span class="lnt"> 71
&lt;/span>&lt;span class="lnt"> 72
&lt;/span>&lt;span class="lnt"> 73
&lt;/span>&lt;span class="lnt"> 74
&lt;/span>&lt;span class="lnt"> 75
&lt;/span>&lt;span class="lnt"> 76
&lt;/span>&lt;span class="lnt"> 77
&lt;/span>&lt;span class="lnt"> 78
&lt;/span>&lt;span class="lnt"> 79
&lt;/span>&lt;span class="lnt"> 80
&lt;/span>&lt;span class="lnt"> 81
&lt;/span>&lt;span class="lnt"> 82
&lt;/span>&lt;span class="lnt"> 83
&lt;/span>&lt;span class="lnt"> 84
&lt;/span>&lt;span class="lnt"> 85
&lt;/span>&lt;span class="lnt"> 86
&lt;/span>&lt;span class="lnt"> 87
&lt;/span>&lt;span class="lnt"> 88
&lt;/span>&lt;span class="lnt"> 89
&lt;/span>&lt;span class="lnt"> 90
&lt;/span>&lt;span class="lnt"> 91
&lt;/span>&lt;span class="lnt"> 92
&lt;/span>&lt;span class="lnt"> 93
&lt;/span>&lt;span class="lnt"> 94
&lt;/span>&lt;span class="lnt"> 95
&lt;/span>&lt;span class="lnt"> 96
&lt;/span>&lt;span class="lnt"> 97
&lt;/span>&lt;span class="lnt"> 98
&lt;/span>&lt;span class="lnt"> 99
&lt;/span>&lt;span class="lnt">100
&lt;/span>&lt;span class="lnt">101
&lt;/span>&lt;span class="lnt">102
&lt;/span>&lt;span class="lnt">103
&lt;/span>&lt;span class="lnt">104
&lt;/span>&lt;span class="lnt">105
&lt;/span>&lt;span class="lnt">106
&lt;/span>&lt;span class="lnt">107
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;C++ CI/CD Pipeline&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">push&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">branches&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;main&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;develop&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">tags&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;v*.*.*&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">pull_request&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">branches&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;main&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">BUILD_TYPE&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Release&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">jobs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">build-and-test&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Build [${{ matrix.os }} | ${{ matrix.compiler }}]&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ matrix.os }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">strategy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">fail-fast&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matrix&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">include&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">os&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">gcc&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">c_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">gcc&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cpp_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">g++&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">os&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">clang&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">c_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">clang&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cpp_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">clang++&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">os&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">windows-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">msvc&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">c_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cl&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cpp_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cl&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">os&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">macos-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">apple-clang&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">c_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">clang&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cpp_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">clang++&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Checkout Repository&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/checkout@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Configure CMake&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">CC&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ matrix.c_compiler }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">CXX&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ matrix.cpp_compiler }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> cmake -B build
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> -DENABLE_SANITIZERS=${{ matrix.os == &amp;#39;ubuntu-latest&amp;#39; &amp;amp;&amp;amp; &amp;#39;ON&amp;#39; || &amp;#39;OFF&amp;#39; }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Build Project&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cmake --build build --config ${{ env.BUILD_TYPE }} --parallel 2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Run Unit Tests (CTest)&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">working-directory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">build&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ASAN_OPTIONS&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;detect_leaks=1:symbolize=1&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">UBSAN_OPTIONS&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;print_stacktrace=1&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ctest --build-config ${{ env.BUILD_TYPE }} --output-on-failure --parallel 2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Package with CPack&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">if&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">startsWith(github.ref, &amp;#39;refs/tags/v&amp;#39;)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">working-directory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">build&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cpack -C ${{ env.BUILD_TYPE }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Create GitHub Release and Upload Assets&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">if&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">startsWith(github.ref, &amp;#39;refs/tags/v&amp;#39;)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">softprops/action-gh-release@v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">files&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">|&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> build/*.tar.gz
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> build/*.zip&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">GITHUB_TOKEN&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ secrets.GITHUB_TOKEN }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">coverage&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Code Coverage Analysis&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">if&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">github.event_name == &amp;#39;pull_request&amp;#39; || github.ref == &amp;#39;refs/heads/main&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Checkout Repository&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/checkout@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Install lcov&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">sudo apt-get update &amp;amp;&amp;amp; sudo apt-get install -y lcov&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Configure CMake for Coverage&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cmake -B build -DCMAKE_BUILD_TYPE=Debug -DENABLE_COVERAGE=ON&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Build Project&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cmake --build build --parallel 2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Run Tests&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">working-directory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">build&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ctest --output-on-failure&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Generate lcov Report&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">working-directory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">build&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">|&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> lcov --capture --directory . --output-file coverage.info
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> lcov --remove coverage.info &amp;#39;/usr/*&amp;#39; &amp;#39;*_deps/*&amp;#39; &amp;#39;*tests/*&amp;#39; --output-file coverage.info
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> &lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Upload Coverage to Codecov&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">codecov/codecov-action@v3&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">files&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">build/coverage.info&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">fail_ci_if_error&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="10-auf-dem-weg-zu-fortgeschrittenerem-cicd-statische-analyse-und-formatierung">10. Auf dem Weg zu fortgeschrittenerem CI/CD (Statische Analyse und Formatierung)
&lt;/h2>&lt;p>Obwohl wir hier auf eine detaillierte Erklärung verzichten, wird in der Praxis dringend empfohlen, weitere Qualitätssicherungs-Tools in die Pipeline zu integrieren.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Erzwingung von Clang-Format&lt;/strong>: Um den Aufwand für Code-Reviews zu verringern, integrieren Sie die Überprüfung des Code-Stils mit &lt;code>clang-format&lt;/code> in die CI und lassen Sie die Pipeline fehlschlagen, wenn Formatierungsregeln verletzt werden.&lt;/li>
&lt;li>&lt;strong>Statische Analyse (Clang-Tidy)&lt;/strong>: Um potenzielle Fehler, die durch Compiler-Warnungen allein nicht verhindert werden können, oder ineffizienten Code (z. B. unnötige Kopien) zu erkennen, integrieren Sie &lt;code>clang-tidy&lt;/code> in CMake und führen Sie es in der CI aus.&lt;/li>
&lt;li>&lt;strong>Nutzung von vcpkg / Conan-Caches&lt;/strong>: Wenn Sie viele Drittanbieter-Bibliotheken verwenden, kostet das Erstellen der Abhängigkeiten viel Zeit. Durch die Verwendung von &lt;code>actions/cache&lt;/code> in GitHub Actions zur Speicherung des vcpkg-Installationsverzeichnisses oder des Conan-Caches können Sie die Build-Zeit drastisch reduzieren.&lt;/li>
&lt;/ol>
&lt;h2 id="fazit">Fazit
&lt;/h2>&lt;p>Der Aufbau einer CI/CD-Pipeline in C++-Projekten mag aufgrund der Plattformabhängigkeiten und der Komplexität der Build-Tools auf den ersten Blick als hohe Hürde erscheinen. Durch die richtige Kombination des Ökosystems von GitHub Actions, modernem CMake und CTest/CPack können Sie jedoch einen äußerst leistungsstarken und automatisierten Entwicklungs-Workflow erhalten.&lt;/p>
&lt;p>Die in diesem Artikel erläuterte plattformübergreifende Validierung mithilfe der Matrix-Strategie, die Erkennung von Laufzeitfehlern mithilfe von Sanitizers, die Coverage-Messung und das automatische Deployment in GitHub Releases sind Best Practices, die auch in kommerziellen Open-Source-Projekten weit verbreitet sind.&lt;/p>
&lt;p>Eine automatisierte CI/CD-Pipeline minimiert die Zeit, die Entwickler mit der &amp;ldquo;Fehlersuche&amp;rdquo; und &amp;ldquo;manuellen Build- und Release-Aufgaben&amp;rdquo; verbringen, und wird zur stärksten Waffe, um sich auf die eigentliche kreative Programmierarbeit zu konzentrieren. Bitte führen Sie sie auch in Ihrem C++-Projekt ein, um ein agiles und sicheres Entwicklerleben zu realisieren.&lt;/p></description></item><item><title>Wie man einen Hugo-Blog kostenlos und blitzschnell mit Cloudflare Pages oder GitHub Pages veröffentlicht</title><link>http://kenji.blog/de/p/hugo-hosting-cloudflare-vs-github-pages/</link><pubDate>Sat, 12 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/de/p/hugo-hosting-cloudflare-vs-github-pages/</guid><description>&lt;img src="http://kenji.blog/p/hugo-hosting-cloudflare-vs-github-pages/img/eyecatch.jpg" alt="Featured image of post Wie man einen Hugo-Blog kostenlos und blitzschnell mit Cloudflare Pages oder GitHub Pages veröffentlicht" />&lt;p>Beim Betrieb einer Website oder eines Blogs sind die Ladegeschwindigkeit (Performance), die Betriebskosten und die Sicherheit äußerst wichtige Faktoren. Früher war die Kombination aus dynamischen CMS (Content Management System) wie WordPress und gemieteten Servern der Standard. Heute jedoch zieht eine Architektur namens „Jamstack“ große Aufmerksamkeit auf sich. Insbesondere durch die Kombination von „Hugo“, einem in Go geschriebenen, ultraschnellen Static Site Generator (SSG), mit modernen Hosting-Diensten wie Cloudflare Pages oder GitHub Pages ist es möglich, eine &lt;strong>völlig kostenlose und blitzschnelle&lt;/strong> Blog-Umgebung aufzubauen.&lt;/p>
&lt;p>In diesem Artikel werden wir die konkreten Schritte zur Veröffentlichung einer statischen Website mit Hugo auf Cloudflare Pages oder GitHub Pages, die Unterschiede in den Architekturen der einzelnen Plattformen, den Aufbau von CI/CD (Continuous Integration / Continuous Deployment) mit GitHub Actions, die DNS-Optimierung, Caching-Strategien und die Einführung datenschutzfreundlicher Web-Analysen aus einer technischen Perspektive sehr detailliert erläutern.&lt;/p>
&lt;hr>
&lt;h2 id="1-grundlagen-von-static-site-generatoren-ssg-und-jamstack">1. Grundlagen von Static Site Generatoren (SSG) und Jamstack
&lt;/h2>&lt;h3 id="11-warum-eine-statische-website">1.1 Warum eine statische Website?
&lt;/h3>&lt;p>Herkömmliche dynamische CMS (z. B. WordPress) senden bei jeder Benutzeranfrage eine Abfrage an eine Datenbank (z. B. MySQL) und generieren serverseitig (z. B. mit PHP) dynamisch HTML, das dann zurückgegeben wird. Während dieser Ansatz sehr flexibel ist, weist er eine geringere Widerstandsfähigkeit gegen plötzliche Traffic-Spitzen (sogenannte virale Hits oder DDoS-Angriffe) auf. Dies führt oft zu einer komplexeren Infrastruktur, bei der beispielsweise Cache-Server (wie Redis oder Varnish) vorgeschaltet werden müssen.&lt;/p>
&lt;p>Andererseits generieren Static Site Generatoren (SSG), die die Jamstack-Architektur (JavaScript, APIs und Markup) verwenden, bereits im Voraus (zur Build-Zeit) alle HTML-Dateien, CSS und JavaScript. Bei Benutzeranfragen gibt der Webserver (oder das CDN) lediglich die bereits generierten statischen Dateien zurück. Dies ermöglicht eine überwältigende Geschwindigkeit und robuste Sicherheit.&lt;/p>
&lt;h3 id="12-die-vorteile-von-hugo">1.2 Die Vorteile von Hugo
&lt;/h3>&lt;p>Bei SSGs gibt es verschiedene Optionen wie Next.js, Gatsby, Jekyll oder Astro. Das herausragendste Merkmal von Hugo ist jedoch seine &lt;strong>Build-Geschwindigkeit&lt;/strong>. Dank der gleichzeitigen Verarbeitung durch die Programmiersprache Go ist der Build selbst bei Websites mit Tausenden oder Zehntausenden von Seiten in nur wenigen Sekunden abgeschlossen. Dies reduziert die Wartezeiten in der CI/CD-Pipeline drastisch und führt direkt zu einer besseren Entwicklererfahrung (DX: Developer Experience).&lt;/p>
&lt;hr>
&lt;h2 id="2-vergleich-der-hosting-service-architekturen">2. Vergleich der Hosting-Service-Architekturen
&lt;/h2>&lt;p>Wo die mit Hugo generierten statischen Dateien gehostet werden, ist die nächste Frage. Repräsentative Optionen sind Cloudflare Pages, GitHub Pages und Netlify, die jedoch jeweils über unterschiedliche zugrunde liegende Netzwerkarchitekturen verfügen.&lt;/p>
&lt;h3 id="21-cdn-und-edge-computing">2.1 CDN und Edge-Computing
&lt;/h3>&lt;p>Alle diese Plattformen nutzen ein global verteiltes CDN (Content Delivery Network), um Inhalte bereitzustellen. Der entscheidende Unterschied liegt jedoch nicht nur im reinen Caching statischer Dateien, sondern darin, ob das Routing von Anfragen und das Umschreiben von Headern durch „Edge-Computing“ am Point of Presence (PoP), der dem Benutzer am nächsten ist, ausgeführt werden kann.&lt;/p>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;Benutzer (Tokio)&amp;#34;] --&amp;gt;|Niedrige Latenz| B[&amp;#34;Edge-Knoten (NRT)&amp;#34;]
C[&amp;#34;Benutzer (New York)&amp;#34;] --&amp;gt;|Niedrige Latenz| D[&amp;#34;Edge-Knoten (EWR)&amp;#34;]
E[&amp;#34;Benutzer (London)&amp;#34;] --&amp;gt;|Niedrige Latenz| F[&amp;#34;Edge-Knoten (LHR)&amp;#34;]
B --&amp;gt; G[&amp;#34;Ursprungsspeicher / Objektspeicher&amp;#34;]
D --&amp;gt; G
F --&amp;gt; G
&lt;/pre>
&lt;h3 id="22-github-pages">2.2 GitHub Pages
&lt;/h3>&lt;p>GitHub Pages ist ein Dienst, mit dem HTML-, CSS- und JavaScript-Dateien direkt aus einem GitHub-Repository veröffentlicht werden können. Im Hintergrund werden CDNs wie Fastly verwendet, was eine solide Leistung bietet. Allerdings gibt es Einschränkungen bei der Anpassung von Headern (z. B. &lt;code>Cache-Control&lt;/code> oder Sicherheitsheader), und Weiterleitungen sind auf HTML-Meta-Refresh oder Jekyll-Plugins angewiesen. Die rein infrastrukturellen Funktionen sind daher etwas eingeschränkt.&lt;/p>
&lt;h3 id="23-cloudflare-pages">2.3 Cloudflare Pages
&lt;/h3>&lt;p>Cloudflare Pages ist ein Hosting-Service für statische Websites, der auf Cloudflares weltweit größtem Anycast-Netzwerk (mit Präsenz in über 275 Städten) aufbaut.
Er bietet Standardunterstützung für HTTP/3 (QUIC), Bildoptimierung und die Integration von Edge-Funktionen (Cloudflare Workers), was ein überwältigendes Performance-Tuning ermöglicht. Ein großer Vorteil ist zudem, dass keine Gebühren für die Bandbreite anfallen, sodass die Website auch bei massiven Traffic-Spitzen kostenlos betrieben werden kann.&lt;/p>
&lt;h3 id="24-netlify">2.4 Netlify
&lt;/h3>&lt;p>Netlify ist ein Pionier im Bereich Jamstack und bietet eine All-in-One-DX (Developer Experience), die Formularfunktionen, Authentifizierung (Identity), serverlose Funktionen und vieles mehr integriert. Wenn jedoch das kostenlose Kontingent für die Bandbreite (100 GB pro Monat) überschritten wird, können hohe verbrauchsabhängige Kosten entstehen. Daher ist bei Blogs, die viele Bilder oder Videos verwenden, auf das Kostenmanagement zu achten.&lt;/p>
&lt;hr>
&lt;h2 id="3-theoretische-berechnung-von-performance-und-latenz-mathematisches-modell-mit-latex">3. Theoretische Berechnung von Performance und Latenz (Mathematisches Modell mit LaTeX)
&lt;/h2>&lt;p>Bei der Bewertung der Web-Performance ist die Reduzierung der Latenz (Latency) der wichtigste Indikator. Lassen Sie uns modellieren, wie stark die Latenz durch die Nutzung eines CDN (Edge) im Vergleich zum direkten Zugriff auf den Ursprungsserver reduziert wird.&lt;/p>
&lt;p>Wir bezeichnen die Wahrscheinlichkeit, dass eine Benutzeranfrage den Cache trifft, als „Cache-Trefferrate (Cache Hit Ratio)“ und setzen sie als $C$. Dabei gilt $0 \le C \le 1$.
Die Latenz zum Ursprungsserver bezeichnen wir als $L_{origin}$ und die Latenz zum nächstgelegenen Edge-Knoten als $L_{edge}$.&lt;/p>
&lt;p>Die neue durchschnittliche Latenz $L_{new}$ wird als folgender Erwartungswert berechnet:&lt;/p>
$$ L_{new} = C \times L_{edge} + (1 - C) \times (L_{edge} + L_{origin}) $$&lt;p>Wenn man diese Formel vereinfacht, erhält man Folgendes:&lt;/p>
$$ L_{new} = L_{edge} + (1 - C) \times L_{origin} $$&lt;p>Wenn beispielsweise ein Benutzer in Tokio auf einen Ursprungsserver an der Ostküste der USA (New York) zugreift, beträgt $L_{origin}$ angesichts der physischen Distanz der Glasfaserkabel und der Verarbeitungsverzögerungen an den Routern etwa 200 ms. Nutzt man hingegen ein CDN wie Cloudflare, kann eine Verbindung zu einem Edge-Knoten in Tokio hergestellt werden, wodurch sich $L_{edge}$ auf etwa 10 ms verkürzt.&lt;/p>
&lt;p>Angenommen, die Cache-Trefferrate beträgt $C = 0.95$ (95 %):&lt;/p>
$$ L_{new} = 10 + (1 - 0.95) \times 200 = 10 + 0.05 \times 200 = 10 + 10 = 20 \text{ ms} $$&lt;p>Auf diese Weise ist es durch die Einführung eines CDNs möglich, die durchschnittliche Latenz drastisch (um etwa 90 %) von 210 ms auf 20 ms zu reduzieren.&lt;/p>
&lt;hr>
&lt;h2 id="4-aufbau-einer-cicd-pipeline-mit-github-actions">4. Aufbau einer CI/CD-Pipeline mit GitHub Actions
&lt;/h2>&lt;p>Um den Aktualisierungsprozess eines Hugo-Blogs zu automatisieren, richten wir eine CI/CD-Pipeline mit GitHub Actions ein. Auf diese Weise genügt es, lokal einen Markdown-Artikel zu schreiben und &lt;code>git push&lt;/code> auszuführen; der Build wird automatisch gestartet und auf Cloudflare Pages oder GitHub Pages bereitgestellt.&lt;/p>
&lt;p>Das folgende Sequenzdiagramm zeigt den gesamten Ablauf vom Pushen eines Artikels bis zur Bereitstellung an den Benutzer.&lt;/p>
&lt;pre class="mermaid">
sequenceDiagram
participant U as &amp;#34;Benutzer (Autor)&amp;#34;
participant G as &amp;#34;GitHub-Repository&amp;#34;
participant A as &amp;#34;GitHub Actions&amp;#34;
participant C as &amp;#34;Cloudflare Pages&amp;#34;
participant V as &amp;#34;Besucher&amp;#34;
U-&amp;gt;&amp;gt;G: &amp;#34;git push origin main&amp;#34;
G-&amp;gt;&amp;gt;A: &amp;#34;Push-Ereignis auslösen&amp;#34;
A-&amp;gt;&amp;gt;A: &amp;#34;Checkout &amp;amp; Setup Hugo&amp;#34;
A-&amp;gt;&amp;gt;A: &amp;#34;hugo --minify (Build)&amp;#34;
A-&amp;gt;&amp;gt;C: &amp;#34;Auf Cloudflare Pages bereitstellen (Direkter Upload)&amp;#34;
C--&amp;gt;&amp;gt;A: &amp;#34;Bereitstellungs-URL (Erfolg)&amp;#34;
V-&amp;gt;&amp;gt;C: &amp;#34;Webseite anfordern&amp;#34;
C--&amp;gt;&amp;gt;V: &amp;#34;Gecachtes HTML zurückgeben (Edge)&amp;#34;
&lt;/pre>
&lt;h3 id="41-bereitstellungseinstellungen-für-cloudflare-pages-direkter-upload">4.1 Bereitstellungseinstellungen für Cloudflare Pages (Direkter Upload)
&lt;/h3>&lt;p>Bei Cloudflare Pages gibt es zwei Möglichkeiten: Sie können ein GitHub-Repository verknüpfen, um den Build auf der Infrastruktur von Cloudflare auszuführen, oder Sie laden die mit GitHub Actions erstellten statischen Dateien über „Direct Upload (Direkter Upload)“ hoch. Wenn Sie die Hugo-Versionierung strenger kontrollieren und mit anderen Jobs (wie Tests oder Bildoptimierung) verknüpfen möchten, empfiehlt sich die Methode, den Build in GitHub Actions auszuführen und einen Direct Upload durchzuführen.&lt;/p>
&lt;p>Nachfolgend finden Sie ein praktisches Beispiel für &lt;code>.github/workflows/deploy.yml&lt;/code> zur Bereitstellung auf Cloudflare Pages.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Hugo-Website auf Cloudflare Pages bereitstellen&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">push&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">branches&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;main&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">workflow_dispatch&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">jobs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">build-and-deploy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;ubuntu-latest&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Repository auschecken&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;actions/checkout@v4&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">submodules&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;recursive&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">fetch-depth&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Hugo einrichten&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;peaceiris/actions-hugo@v3&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">hugo-version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;0.125.0&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">extended&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Hugo-Website erstellen&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;hugo --minify --gc&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">HUGO_ENVIRONMENT&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;production&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Auf Cloudflare Pages bereitstellen&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;cloudflare/pages-action@v1&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">apiToken&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ secrets.CLOUDFLARE_API_TOKEN }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">accountId&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ secrets.CLOUDFLARE_ACCOUNT_ID }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">projectName&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;your-project-name&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">directory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;public&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">gitHubToken&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ secrets.GITHUB_TOKEN }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">branch&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;main&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>In dieser Pipeline werden durch die Option &lt;code>--minify&lt;/code> HTML, CSS und JS minimiert, und mit &lt;code>--gc&lt;/code> werden nicht mehr benötigte Dateien gelöscht. Dies sind die Grundlagen der Performance-Optimierung.&lt;/p>
&lt;hr>
&lt;h2 id="5-tiefer-einblick-in-die-dns-einstellungen-benutzerdefinierte-domains-und-cname--alias-einträge">5. Tiefer Einblick in die DNS-Einstellungen: Benutzerdefinierte Domains und CNAME / ALIAS-Einträge
&lt;/h2>&lt;p>Bei der Verwendung einer eigenen Domain (z. B. &lt;code>kenji.blog&lt;/code>) ist die richtige Konfiguration des DNS (Domain Name System) unerlässlich.&lt;/p>
&lt;h3 id="51-einschränkungen-von-cname-einträgen-und-zone-apex">5.1 Einschränkungen von CNAME-Einträgen und Zone Apex
&lt;/h3>&lt;p>Normalerweise wird ein &lt;code>CNAME&lt;/code>-Eintrag verwendet, wenn eine Subdomain (z. B. &lt;code>www.kenji.blog&lt;/code>) auf einen externen Dienst verweisen soll. Gemäß der DNS-Spezifikation (RFC 1034) ist es jedoch nicht möglich, einen &lt;code>CNAME&lt;/code>-Eintrag für die Root-Domain (auch Zone Apex oder Naked Domain genannt, z. B. &lt;code>kenji.blog&lt;/code>) festzulegen. Das liegt daran, dass in der Zone Apex immer ein SOA-Eintrag (Start of Authority), ein NS-Eintrag (Name Server) und ein MX-Eintrag (Mail Exchange) vorhanden sein müssen und die Regel besagt, dass ein CNAME nicht mit anderen Ressourceneinträgen koexistieren darf.&lt;/p>
&lt;h3 id="52-lösung-alias--aname--cname-flattening">5.2 Lösung: ALIAS / ANAME / CNAME Flattening
&lt;/h3>&lt;p>Um dieses Problem zu lösen, bieten moderne DNS-Anbieter eigene Erweiterungsfunktionen an.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>ALIAS / ANAME-Einträge&lt;/strong>: Die Namensauflösung erfolgt dynamisch auf der Seite des DNS-Servers, und der endgültige A-Eintrag (IP-Adresse) wird an den Client zurückgegeben. Amazon Route 53 und andere unterstützen dies.&lt;/li>
&lt;li>&lt;strong>CNAME Flattening&lt;/strong>: Eine von Cloudflare angebotene Funktion. Sie verhält sich so, als wäre ein CNAME in der Zone Apex konfiguriert, aber der autoritative DNS-Server von Cloudflare gibt die automatisch aufgelösten IP-Adressen (A-Einträge und AAAA-Einträge) transparent an den Client zurück.&lt;/li>
&lt;/ul>
&lt;p>Wenn Sie Cloudflare Pages verwenden, ist die Delegierung der Nameserver der Domain an Cloudflare und die Nutzung dieses „CNAME Flattening“ die nahtloseste und leistungsstärkste Konfiguration.&lt;/p>
&lt;hr>
&lt;h2 id="6-caching-strategien-und-steuerung-von-http-headern">6. Caching-Strategien und Steuerung von HTTP-Headern
&lt;/h2>&lt;p>Ein weiterer Schlüssel zur Beschleunigung statischer Websites ist die „Caching-Strategie“. Bei Cloudflare Pages können HTTP-Antwortheader detailliert mithilfe einer generierten Datei (&lt;code>_headers&lt;/code>-Datei) gesteuert werden.&lt;/p>
&lt;h3 id="61-edge-cache-vs-browser-cache">6.1 Edge Cache vs. Browser Cache
&lt;/h3>&lt;p>Caches lassen sich grob in zwei Arten unterteilen: den „Edge Cache“, der auf CDN-Seite vorgehalten wird, und den „Browser Cache“, der im Browser des Benutzers gespeichert wird.&lt;/p>
&lt;p>Es ist ideal, statische Dateien (Bilder, CSS, JS usw., die Hashes in ihren Dateinamen enthalten) für einen langen Zeitraum im Browser zwischenzuspeichern. Im Gegensatz dazu wird für HTML-Dateien in der Regel eine Konfiguration gewählt, bei der der Browser-Cache kurz gehalten (oder deaktiviert) wird und die Verarbeitung über den Edge-Cache erfolgt, damit Aktualisierungen sofort widergespiegelt werden.&lt;/p>
&lt;p>Konfigurationsbeispiel für &lt;code>_headers&lt;/code> in Cloudflare Pages:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl"># HTML-Dateien werden nicht im Browser zwischengespeichert und bei jeder Anfrage validiert
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/*.html
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Cache-Control: public, max-age=0, must-revalidate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Asset-Dateien (CSS/JS/Bilder) werden für 1 Jahr im Browser zwischengespeichert
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/assets/*
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Cache-Control: public, max-age=31536000, immutable
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/img/*
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Cache-Control: public, max-age=31536000, immutable
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="62-berechnungsformel-zur-reduzierung-der-bandbreitenkosten">6.2 Berechnungsformel zur Reduzierung der Bandbreitenkosten
&lt;/h3>&lt;p>Durch das Setzen geeigneter Cache-Header kann die Datenübertragungsmenge vom Server (Edge) deutlich reduziert werden. Die monatlichen Bandbreitenkosten $Cost$ werden durch folgendes Modell dargestellt, basierend auf dem Übertragungsvolumen jeder Ressource $B_i$, der Cache-Trefferrate $C_i$ und dem Einheitspreis für die Bandbreite $R$:&lt;/p>
$$ Cost = \sum_{i=1}^{n} \left( B_i \times (1 - C_i) \times R \right) $$&lt;p>Da der ausgehende Datenverkehr bei Cloudflare kostenlos ist ($R = 0$), belaufen sich die direkten finanziellen Kosten auf $0$. Wenn jedoch andere Infrastrukturen wie GitHub Pages parallel genutzt werden oder wenn beispielsweise AWS S3 als Backend dient, ist die Maximierung dieser Cache-Trefferrate $C_i$ der Schlüssel zur Senkung der Infrastrukturkosten.&lt;/p>
&lt;hr>
&lt;h2 id="7-web-analysen-die-datenschutz-und-performance-in-einklang-bringen">7. Web-Analysen, die Datenschutz und Performance in Einklang bringen
&lt;/h2>&lt;p>Beim Betrieb eines Blogs ist die Web-Analyse unerlässlich, um zu erfahren, wie viele Benutzer die Website besuchen. Lange Zeit war Google Analytics (GA4) der De-facto-Standard, doch mit dem aktuellen Trend zum Datenschutz (DSGVO, CCPA) und der Abschaffung von Third-Party-Cookies ändert sich die Situation.&lt;/p>
&lt;h3 id="71-auswirkungen-auf-die-web-performance">7.1 Auswirkungen auf die Web-Performance
&lt;/h3>&lt;p>Die Implementierung von Google Analytics (insbesondere &lt;code>gtag.js&lt;/code> oder Google Tag Manager) führt zum Laden und Ausführen zahlreicher externer Skripte, was sich negativ auf die Performance (insbesondere auf TTFB und die Blockierungszeit des Main Threads) auswirkt.&lt;/p>
&lt;p>Lassen Sie uns die Ladezeit der Website wie folgt aufschlüsseln und betrachten.&lt;/p>
&lt;pre class="mermaid">
pie title &amp;#34;Aufschlüsselung der typischen Website-Ladezeiten&amp;#34;
&amp;#34;TTFB (Time to First Byte)&amp;#34; : 15
&amp;#34;Content-Download (HTML/CSS)&amp;#34; : 20
&amp;#34;DOM-Parsing &amp;amp; Rendering&amp;#34; : 45
&amp;#34;Analytics &amp;amp; Drittanbieter-JS&amp;#34; : 20
&lt;/pre>
&lt;p>Es ist nicht ungewöhnlich, dass Analyse-Tools von Drittanbietern etwa 20 % bis 30 % der gesamten Ladezeit ausmachen.&lt;/p>
&lt;h3 id="72-einführung-von-cloudflare-web-analytics">7.2 Einführung von Cloudflare Web Analytics
&lt;/h3>&lt;p>Aus diesem Grund rücken privatsphäre-fokussierte (Privacy First) Web-Analysen ohne Cookies (Cookieless) wie Cloudflare Web Analytics oder Plausible Analytics ins Rampenlicht.&lt;/p>
&lt;p>Cloudflare Web Analytics funktioniert durch einfaches Einbetten eines sehr ressourcenschonenden JavaScript-Snippets. Da es keine Cookies setzt, entfällt die Notwendigkeit, ein lästiges Cookie-Zustimmungsbanner (Cookie Consent Banner) zu installieren.&lt;/p>
&lt;p>Auch die Implementierung in Hugo ist sehr einfach. Sie müssen lediglich das bereitgestellte Snippet zu &lt;code>layouts/partials/head.html&lt;/code> oder &lt;code>layouts/partials/analytics.html&lt;/code> hinzufügen.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">{{ if eq hugo.Environment &amp;#34;production&amp;#34; }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- Cloudflare Web Analytics --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">defer&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#39;https://static.cloudflareinsights.com/beacon.min.js&amp;#39;&lt;/span> &lt;span class="na">data-cf-beacon&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#39;{&amp;#34;token&amp;#34;: &amp;#34;YOUR_CLOUDFLARE_BEACON_TOKEN&amp;#34;}&amp;#39;&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- End Cloudflare Web Analytics --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ end }}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Durch das Hinzufügen des Attributs &lt;code>defer&lt;/code> kann das Skript asynchron geladen werden, ohne das Parsen des HTMLs zu blockieren, und nach dem Aufbau des DOMs ausgeführt werden. Dadurch werden die Auswirkungen auf die anfängliche Ladegeschwindigkeit (LCP: Largest Contentful Paint und FCP: First Contentful Paint) auf ein Minimum reduziert.&lt;/p>
&lt;hr>
&lt;h2 id="8-zusammenfassung-und-best-practices">8. Zusammenfassung und Best Practices
&lt;/h2>&lt;p>Beim Betrieb von statischen Websites mit Hugo bietet der Einsatz moderner Hosting-Plattformen wie Cloudflare Pages oder GitHub Pages überwältigende Vorteile in allen Bereichen: Kosteneffizienz, Ladegeschwindigkeit und Sicherheit.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Blitzschnelle Builds&lt;/strong>: Nutzen Sie die Geschwindigkeit von Hugo, um die Ausführungszeit der CI/CD-Pipeline (GitHub Actions) zu minimieren.&lt;/li>
&lt;li>&lt;strong>Bereitstellung am Edge&lt;/strong>: Nutzen Sie das Edge-Netzwerk von Cloudflare, um Inhalte mit Latenzen im Millisekundenbereich an Benutzer auf der ganzen Welt auszuliefern.&lt;/li>
&lt;li>&lt;strong>Geeignete DNS-Konfiguration&lt;/strong>: Nutzen Sie CNAME Flattening, um die Zone Apex (benutzerdefinierte Domain) sicher und schnell zu betreiben.&lt;/li>
&lt;li>&lt;strong>Optimierung der Caching-Strategie&lt;/strong>: Verwenden Sie &lt;code>_headers&lt;/code>, um Browser-Cache und Edge-Cache je nach Ressourcentyp angemessen zu trennen.&lt;/li>
&lt;li>&lt;strong>Ressourcenschonende Analysen&lt;/strong>: Führen Sie Tools wie Cloudflare Web Analytics ein, die die Privatsphäre respektieren und gleichzeitig die Performance nicht beeinträchtigen.&lt;/li>
&lt;/ol>
&lt;p>Durch die Kombination dieser Elemente kann kostenlos ein skalierbares und robustes Blog-System aufgebaut werden, das massivem Traffic im Bereich von mehreren Millionen Seitenaufrufen pro Monat standhält. Wenn Sie die Erstellung eines Technik-Blogs, einer Unternehmenswebsite oder einer Portfolio-Website in Betracht ziehen, sollten Sie diese Architektur aus Jamstack + Hugo + Cloudflare Pages unbedingt ausprobieren.&lt;/p></description></item></channel></rss>