<?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/ar/categories/devops/</link><description>Recent content in DevOps on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ar</language><copyright>kenjinote</copyright><lastBuildDate>Sun, 13 Sep 2026 09:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/ar/categories/devops/index.xml" rel="self" type="application/rss+xml"/><item><title>【أوامر Git】الفرق بين rebase و merge، والاستخدام الصحيح في العمل الفعلي</title><link>http://kenji.blog/ar/p/git-rebase-vs-merge-practical-guide/</link><pubDate>Sun, 13 Sep 2026 09:00:00 +0900</pubDate><guid>http://kenji.blog/ar/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】الفرق بين rebase و merge، والاستخدام الصحيح في العمل الفعلي" />&lt;h1 id="1-مقدمة-لماذا-يمثل-الاختيار-بين-merge-و-rebase-تحديا-مستمرا">1. مقدمة: لماذا يمثل الاختيار بين &amp;ldquo;merge و rebase&amp;rdquo; تحدياً مستمراً؟
&lt;/h1>&lt;p>Git هو نظام تحكم في الإصدارات لا غنى عنه في تطوير البرمجيات الحديثة. عندما يقوم العديد من المطورين بتعديل قاعدة الكود في وقت واحد، فإن نموذج الفروع القوي في Git يظهر فاعليته. ومع ذلك، في تطوير الفرق، يمثل النقاش حول &amp;ldquo;أيهما يجب استخدامه: &lt;code>merge&lt;/code> أو &lt;code>rebase&lt;/code>&amp;rdquo; موضوعاً يؤرق دائماً المطورين، من المبتدئين إلى الخبراء.&lt;/p>
&lt;p>في هذا المقال، سنتعمق في الفروق بين آليات &lt;code>git merge&lt;/code> و &lt;code>git rebase&lt;/code>، من خلال استكشاف البنية الداخلية لـ Git، مثل الرسم البياني الموجه غير الدوري (DAG) والخصائص الرياضية لتجزئة الالتزام (Commit Hash). وبعد ذلك، سنشرح بالتفصيل كيفية التمييز بينهما في العمل الفعلي، مع تقديم أمثلة على سير العمل (Workflows). بدلاً من مجرد سرد الأوامر، فإن فهم العمليات الحسابية التي يقوم بها Git في الخلفية سيزيل الخوف من التعارضات (Conflicts)، مما يتيح لك بناء سجل تاريخي نظيف وقابل للتتبع.&lt;/p>
&lt;hr>
&lt;h1 id="2-البنية-الداخلية-لـ-git-تجزئة-الالتزام-ونموذج-الكائنات">2. البنية الداخلية لـ Git: تجزئة الالتزام ونموذج الكائنات
&lt;/h1>&lt;p>لفهم كيف يقوم Git بدمج السجل التاريخي، يجب أن نعرف أولاً كيف يخزن Git البيانات. لا يقوم Git فقط بحفظ اختلافات (Patches) تغييرات الملفات، بل يحتفظ بلقطة (Snapshot) لنظام الملفات بأكمله في نقطة زمنية معينة.&lt;/p>
&lt;h2 id="21-الخصائص-التشفيرية-لتجزئة-الالتزام-commit-hash">2.1 الخصائص التشفيرية لتجزئة الالتزام (Commit Hash)
&lt;/h2>&lt;p>يتم تعريف كل التزام (Commit) في Git بشكل فريد بواسطة سلسلة من 40 حرفاً سداسياً عشرياً (Hexadecimal)، يتم حسابها بناءً على محتواه باستخدام دالة التجزئة SHA-1 (Secure Hash Algorithm 1). يتكون كائن الالتزام من العناصر التالية:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>مؤشر لكائن Tree&lt;/strong>: لقطة من هيكل الدليل والملفات (Blob) في تلك النقطة.&lt;/li>
&lt;li>&lt;strong>مؤشر للالتزام الأب (Parent Commit)&lt;/strong>: قيمة تجزئة واحدة أو أكثر للالتزامات الآباء (الالتزام الأول لا يحتوي على أب، والتزام الدمج يحتوي على أبوين أو أكثر).&lt;/li>
&lt;li>&lt;strong>معلومات المؤلف (Author)&lt;/strong>: الشخص الذي كتب الكود والوقت.&lt;/li>
&lt;li>&lt;strong>معلومات الملتزم (Committer)&lt;/strong>: الشخص الذي أنشأ أو طبق الالتزام والوقت.&lt;/li>
&lt;li>&lt;strong>رسالة الالتزام (Commit Message)&lt;/strong>: نص يوضح القصد من التغيير.&lt;/li>
&lt;/ol>
&lt;p>رياضياً، تُعرّف قيمة التجزئة $H(C)$ لكائن الالتزام $C$ على النحو التالي:&lt;/p>
$$
H(C) = \text{SHA-1}( \text{tree} \parallel \text{parent} \parallel \text{author} \parallel \text{committer} \parallel \text{message} )
$$&lt;p>هنا، تمثل $\parallel$ دمج (Concatenation) البيانات. نظراً لخصائص دالة التجزئة، فإن تغيير حرف واحد فقط في رسالة الالتزام، أو حتى وجود التزام أب مختلف، سيؤدي إلى إنشاء قيمة تجزئة مختلفة تماماً. وهذا يعني أن &lt;strong>الالتزام غير قابل للتغيير (Immutable)&lt;/strong>. عندما يقال إن &lt;code>rebase&lt;/code> &amp;ldquo;يعيد كتابة السجل التاريخي&amp;rdquo;، فهذا يعني فعلياً أنه &amp;ldquo;ينشئ التزامات جديدة بمحتوى مشابه ولكن بقيم تجزئة مختلفة&amp;rdquo;.&lt;/p>
&lt;p>حجم مساحة التجزئة هو $2^{160}$، ويمكن تقريب احتمال $P$ لحدوث تصادم (Collision) (أي أن يكون لالتزامين مختلفين نفس قيمة التجزئة) باستخدام نظرية مفارقة عيد الميلاد (Birthday Paradox) على النحو التالي (حيث $n$ هو عدد الالتزامات):&lt;/p>
$$
P(\text{collision}) \approx 1 - \exp\left(-\frac{n^2}{2 \times 2^{160}}\right)
$$&lt;p>هذا الاحتمال منخفض للغاية، وعملياً من شبه المستحيل أن تتصادم تجزئة التزامات Git.&lt;/p>
&lt;hr>
&lt;h1 id="3-نظرية-الرسومات-البيانية-و-dag-النموذج-الرياضي-لسجل-git">3. نظرية الرسومات البيانية و DAG: النموذج الرياضي لسجل Git
&lt;/h1>&lt;p>يُنمذج السجل التاريخي لالتزامات Git على أنه &amp;ldquo;رسم بياني موجه غير دوري&amp;rdquo; (Directed Acyclic Graph, DAG) في نظرية الرسومات البيانية.&lt;/p>
&lt;h2 id="31-ما-هو-dag-الرسم-البياني-الموجه-غير-الدوري">3.1 ما هو DAG (الرسم البياني الموجه غير الدوري)؟
&lt;/h2>&lt;p>في الرسم البياني $G = (V, E)$، تمثل $V$ مجموعة الالتزامات (العُقد - Vertices)، وتمثل $E$ مجموعة الحواف الموجهة (Directed Edges) التي تشير إلى علاقات الآباء والأبناء بين الالتزامات. في Git، يتجه اتجاه الحافة من &amp;ldquo;الالتزام الابن إلى الالتزام الأب&amp;rdquo;. هذا لأن الالتزام الجديد يحتفظ بمؤشر إلى الالتزام السابق.&lt;/p>
&lt;pre class="mermaid">
graph BT
A[&amp;#34;الالتزام A (الأولي)&amp;#34;]
B[&amp;#34;الالتزام B&amp;#34;]
C[&amp;#34;الالتزام C (الرئيسي)&amp;#34;]
D[&amp;#34;الالتزام D (ميزة)&amp;#34;]
E[&amp;#34;الالتزام E (دمج)&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>أهم ميزة لـ DAG هي &amp;ldquo;عدم وجود دورات (Cycles)&amp;rdquo;. بفضل هذا، فإن الخوارزمية التي تتتبع السجل التاريخي للوراء لا تقع في حلقة مفرغة، وتصل بالتأكيد إلى النهاية (الالتزام الأولي).&lt;/p>
&lt;h2 id="32-الفرز-الطوبولوجي-وترتيب-السجل">3.2 الفرز الطوبولوجي وترتيب السجل
&lt;/h2>&lt;p>عند عرض السجل التاريخي باستخدام أوامر مثل &lt;code>git log&lt;/code>، يتم ترتيب DAG كقائمة أحادية البعد بواسطة خوارزمية الفرز الطوبولوجي (Topological Sort). لأي حافة موجهة $u \to v$ في DAG (حيث $u$ هو ابن $v$)، يتم ترتيبها بحيث يظهر $u$ قبل $v$ في القائمة.&lt;/p>
&lt;hr>
&lt;h1 id="4-آليات-git-merge-وأنواعها">4. آليات git merge وأنواعها
&lt;/h1>&lt;p>الأمر الأساسي لدمج التغييرات من الفروع هو &lt;code>git merge&lt;/code>. ومع ذلك، بناءً على الحالة الحالية، يختار Git تلقائياً استراتيجية دمج مختلفة.&lt;/p>
&lt;h2 id="41-دمج-التقديم-السريع-fast-forward---ff">4.1 دمج التقديم السريع (Fast-Forward) (&amp;ndash;ff)
&lt;/h2>&lt;p>إذا كان الفرع الوجهة (مثال: &lt;code>main&lt;/code>) هو السلف المباشر للفرع المصدر (مثال: &lt;code>feature&lt;/code>)، يقوم Git بتنفيذ دمج &amp;ldquo;التقديم السريع (Fast-Forward)&amp;rdquo;. هذه العملية لا تُنشئ التزاماً جديداً، بل مجرد تحريك مؤشر الفرع للأمام.&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>يحافظ دمج Fast-Forward على السجل التاريخي في خط مستقيم، ولكن من عيوبه فقدان السياق حول &amp;ldquo;أي مجموعة من الالتزامات تمثل تطوير ميزة (feature) واحدة&amp;rdquo;.&lt;/p>
&lt;h2 id="42-الدمج-بدون-التقديم-السريع-non-fast-forward---no-ff">4.2 الدمج بدون التقديم السريع (Non-Fast-Forward) (&amp;ndash;no-ff)
&lt;/h2>&lt;p>عند تحديد &lt;code>git merge --no-ff&lt;/code> صراحةً، حتى وإن كان الموقف يسمح بـ Fast-Forward، يتم دائماً إنشاء &amp;ldquo;التزام دمج (Merge Commit)&amp;rdquo; جديد. التزام الدمج هو التزام خاص يمتلك أبوين.&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;العمل الرئيسي 1&amp;#34;
merge feature type: NORMAL
&lt;/pre>
&lt;p>ميزة هذه الطريقة هي أن وجود وتاريخ فرع الميزة يبقى بوضوح على DAG. عند حدوث مشكلة، يمكن التراجع بأمان عن الميزة بأكملها دفعة واحدة عن طريق تنفيذ &lt;code>git revert -m 1 &amp;lt;تجزئة التزام الدمج&amp;gt;&lt;/code>.&lt;/p>
&lt;h2 id="43-خوارزمية-الدمج-ثلاثي-الاتجاهات-3-way-merge">4.3 خوارزمية الدمج ثلاثي الاتجاهات (3-Way Merge)
&lt;/h2>&lt;p>إذا كان لدى كل من الفرع الوجهة والفرع المصدر التزامات خاصة بهما، ينفذ Git دمجاً ثلاثي الاتجاهات. في هذه الحالة، يبحث Git في DAG للعثور على &amp;ldquo;أدنى سلف مشترك (Lowest Common Ancestor, LCA)&amp;rdquo; للفرعين.&lt;/p>
&lt;p>التعقيد الزمني $T_{\text{LCA}}$ لخوارزمية العثور على LCA يمكن تنفيذه في وقت خطي بالنسبة لعدد العُقد $|V|$ وعدد الحواف $|E|$:&lt;/p>
$$
T_{\text{LCA}} = \mathcal{O}(|V| + |E|)
$$&lt;p>يقارن Git بين ثلاثة أشياء: &amp;ldquo;حالة LCA&amp;rdquo;، &amp;ldquo;حالة الفرع الحالي&amp;rdquo;، و&amp;quot;حالة الفرع الآخر&amp;quot;، وإذا لم تتعارض التغييرات، يقوم تلقائياً بإنشاء التزام دمج.&lt;/p>
&lt;hr>
&lt;h1 id="5-آلية-git-rebase-وإعادة-بناء-السجل-التاريخي">5. آلية git rebase وإعادة بناء السجل التاريخي
&lt;/h1>&lt;p>بينما يقوم &lt;code>git merge&lt;/code> بـ &amp;ldquo;دمج&amp;rdquo; السجل، يقوم &lt;code>git rebase&lt;/code> بـ &amp;ldquo;إعادة بناء (إعادة ربط)&amp;rdquo; السجل.&lt;/p>
&lt;h2 id="51-ما-يحدث-خلف-كواليس-rebase">5.1 ما يحدث خلف كواليس Rebase
&lt;/h2>&lt;p>عندما تقوم بإعادة ربط (Rebase) فرع &lt;code>feature&lt;/code> بفرع &lt;code>main&lt;/code> (باستخدام &lt;code>git rebase main&lt;/code>)، تكون العمليات الداخلية كما يلي:&lt;/p>
&lt;ol>
&lt;li>إيجاد السلف المشترك (LCA) لفرعي &lt;code>feature&lt;/code> و &lt;code>main&lt;/code>.&lt;/li>
&lt;li>حفظ الفروقات (Diffs) من LCA إلى طرف فرع &lt;code>feature&lt;/code> في منطقة مؤقتة.&lt;/li>
&lt;li>نقل مؤشر فرع &lt;code>feature&lt;/code> إلى طرف فرع &lt;code>main&lt;/code>.&lt;/li>
&lt;li>تطبيق الفروقات المحفوظة تباعاً واحداً تلو الآخر (Cherry-Pick) فوق الأساس الجديد (طرف &lt;code>main&lt;/code>)، وإنشاء التزامات جديدة.&lt;/li>
&lt;/ol>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;الالتزام A&amp;#34;] --&amp;gt; B[&amp;#34;الالتزام B&amp;#34;]
B --&amp;gt; C[&amp;#34;الالتزام C (الرئيسي)&amp;#34;]
B --&amp;gt; D[&amp;#34;الالتزام D (الميزة القديمة)&amp;#34;]
D -.-&amp;gt; E[&amp;#34;الالتزام &amp;#39;D (الميزة الجديدة)&amp;#34;]
C --&amp;gt; E
style D stroke-dasharray: 5 5, fill: #f9f9f9, color: #999
&lt;/pre>
&lt;p>النقطة المهمة هنا هي أن الالتزام $'D$ الناتج عن إعادة الربط &lt;strong>له تجزئة مختلفة تماماً&lt;/strong> عن الالتزام الأصلي $D$، لأنه يمتلك التزاماً أباً مختلفاً (راجع تعريف دالة التجزئة $H(C)$ المذكور سابقاً).&lt;/p>
&lt;h2 id="52-إعادة-الربط-التفاعلية-interactive-rebase">5.2 إعادة الربط التفاعلية (Interactive Rebase)
&lt;/h2>&lt;p>يتيح لك استخدام &lt;code>git rebase -i&lt;/code> (أو &lt;code>--interactive&lt;/code>) التلاعب بالسجل التاريخي للالتزامات كما تشاء. إنها الأداة الأقوى لترتيب السجل المحلي.&lt;/p>
&lt;ul>
&lt;li>&lt;code>pick&lt;/code>: احتفاظ بالالتزام كما هو.&lt;/li>
&lt;li>&lt;code>reword&lt;/code>: تعديل رسالة الالتزام فقط.&lt;/li>
&lt;li>&lt;code>edit&lt;/code>: الإيقاف المؤقت لتعديل محتوى الالتزام.&lt;/li>
&lt;li>&lt;code>squash&lt;/code>: دمج هذا الالتزام مع الالتزام السابق ودمج الرسائل معاً.&lt;/li>
&lt;li>&lt;code>fixup&lt;/code>: مثل &lt;code>squash&lt;/code> ولكن مع تجاهل رسالة هذا الالتزام.&lt;/li>
&lt;li>&lt;code>drop&lt;/code>: حذف الالتزام تماماً.&lt;/li>
&lt;/ul>
&lt;p>من الناحية الرياضية، إذا كان هناك $N$ من الالتزامات في فرع ما، فإن المتغيرات (التباديل) الممكنة $P$ للسجل الخطي التي يمكن إنشاؤها عن طريق تغيير ترتيب إعادة الربط هي كما يلي:&lt;/p>
$$
P = N!
$$&lt;p>يمنح Git المطورين حرية اختيار من بين $N!$ طريقة، مما يتيح إبقاء السجل التاريخي منطقياً وجميلاً.&lt;/p>
&lt;hr>
&lt;h1 id="6-القاعدة-الذهبية-لإعادة-الربط-the-golden-rule-of-rebase">6. القاعدة الذهبية لإعادة الربط (The Golden Rule of Rebase)
&lt;/h1>&lt;p>بالرغم من قوة &lt;code>rebase&lt;/code> الهائلة، إلا أن هناك قاعدة واحدة مطلقة.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>&amp;ldquo;يجب ألا تقوم أبداً بإعادة ربط (Rebase) السجل التاريخي العام (Public History).&amp;rdquo;&lt;/strong>
&lt;em>(Never rebase public history)&lt;/em>&lt;/p>
&lt;/blockquote>
&lt;h2 id="61-لماذا-لا-يجب-إعادة-ربط-السجل-العام">6.1 لماذا لا يجب إعادة ربط السجل العام؟
&lt;/h2>&lt;p>Git هو نظام لا مركزي. التزاماتك التي تم دفعها إلى &lt;code>origin/main&lt;/code> تم استنساخها (Cloned) بالفعل في المستودعات المحلية للمطورين الآخرين. ماذا يحدث إذا قمت بإعادة ربط التزامات تم دفعها بالفعل، وأعدت كتابة السجل التاريخي، ثم قمت بالكتابة فوقه بالقوة باستخدام &lt;code>git push --force&lt;/code>؟&lt;/p>
&lt;p>سوف يتفرع DAG الموجود في أجهزة المطورين الآخرين بشكل جذري عن DAG الموجود في المستودع البعيد (Remote). عندما يقوم المطورون الآخرون بتشغيل &lt;code>git pull&lt;/code>، سيحاول Git دمج مجموعات من الالتزامات ذات تواريخ مختلفة بالقوة، مما يؤدي إلى حدوث عدد هائل من التعارضات والتزامات مكررة (التزامات لها نفس التغييرات ولكن بقيم تجزئة مختلفة)، وستسود الفوضى في المستودع.&lt;/p>
&lt;p>القاعدة الذهبية هي إجراء إعادة الربط (Rebase) &lt;strong>&amp;ldquo;فقط على الفروع المحلية التي لم تشاركها بعد مع أي شخص&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h1 id="7-حل-التعارضات-conflicts-و-git-rebase---continue">7. حل التعارضات (Conflicts) و git rebase &amp;ndash;continue
&lt;/h1>&lt;p>عندما يقوم عدة أشخاص بتعديل نفس الجزء من نفس الملف، يحدث تعارض (Conflict). تختلف عملية حل التعارضات بين &lt;code>merge&lt;/code> و &lt;code>rebase&lt;/code>.&lt;/p>
&lt;h2 id="71-حل-التعارضات-في-merge">7.1 حل التعارضات في Merge
&lt;/h2>&lt;p>في حالة &lt;code>git merge&lt;/code>، يحدث حل التعارضات &lt;strong>مرة واحدة فقط&lt;/strong>. قبل إنشاء التزام الدمج النهائي مباشرة، تقوم بتصحيح جميع أجزاء التعارض دفعة واحدة.&lt;/p>
&lt;h2 id="72-حل-التعارضات-في-rebase">7.2 حل التعارضات في Rebase
&lt;/h2>&lt;p>في حالة &lt;code>git rebase&lt;/code>، نظراً لطبيعته في إعادة تطبيق الالتزامات واحداً تلو الآخر، &lt;strong>من الممكن حدوث تعارض في كل التزام&lt;/strong>.&lt;/p>
&lt;p>عندما يحدث تعارض أثناء إعادة الربط، يقوم Git بإيقاف العملية مؤقتاً. تسلسل الحل هو كما يلي:&lt;/p>
&lt;ol>
&lt;li>افتح محرر نصوص أو بيئة تطوير متكاملة (IDE مثل VS Code)، وقم بتصحيح علامات التعارض (&lt;code>&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/code> و &lt;code>======&lt;/code> و &lt;code>&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code>) يدوياً.&lt;/li>
&lt;li>أضف الملفات المصححة إلى الفهرس (Index):
&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;الملف المعدل&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>لا تقم بإنشاء التزام، بل استأنف عملية إعادة الربط:
&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>إذا كنت ترغب في إلغاء عملية إعادة الربط نفسها والعودة إلى الحالة الأصلية، فقم بتنفيذ الأمر التالي:&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;code>git rebase --skip&lt;/code>)&lt;/p>
&lt;hr>
&lt;h1 id="8-الاستخدام-الصحيح-في-العمل-الفعلي-تطبيق-سير-العمل">8. الاستخدام الصحيح في العمل الفعلي (تطبيق سير العمل)
&lt;/h1>&lt;p>إذن، كيف يجب أن نميز بين استخدام &lt;code>merge&lt;/code> و &lt;code>rebase&lt;/code> في بيئة التطوير الفعلية؟ إليك النهج الأكثر قياسية وأماناً.&lt;/p>
&lt;h2 id="81-السيناريو-1-ترتيب-سجل-العمل-المحلي-استخدام-rebase">8.1 [السيناريو 1] ترتيب سجل العمل المحلي (استخدام Rebase)
&lt;/h2>&lt;p>لنفترض أنك تعمل على فرع ميزة (feature)، وتراكمت لديك الكثير من الالتزامات الصغيرة (مثل &amp;ldquo;تصحيح خطأ مطبعي&amp;rdquo; أو &amp;ldquo;حفظ مؤقت&amp;rdquo;). قبل فتح طلب سحب (Pull Request - PR)، استخدم إعادة الربط التفاعلية لترتيب هذه الالتزامات في وحدات ذات معنى.&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"># نفذ هذا أثناء تواجدك في فرع feature&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"># (سيتم فتح المحرر، استخدم squash و fixup لتنظيف السجل)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>بذلك، يمكنك إنشاء سجل التزامات جميل ومفهوم يسهل على المراجعين (Reviewers) فهم نواياك.&lt;/p>
&lt;h2 id="82-السيناريو-2-مواكبة-أحدث-فرع-main-استخدام-rebase">8.2 [السيناريو 2] مواكبة أحدث فرع main (استخدام Rebase)
&lt;/h2>&lt;p>إذا طالت فترة التطوير، واستمر دمج تغييرات الآخرين في فرع &lt;code>main&lt;/code>، فإن فرع &lt;code>feature&lt;/code> الخاص بك سيصبح قديماً. في هذه الحالة، قم بإعادة ربط فرع &lt;code>feature&lt;/code> بأحدث &lt;code>main&lt;/code> لمواكبته.&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"># الحصول على أحدث المعلومات لـ main&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"># نقل فرع feature فوق أحدث main&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>هذا يجعل السجل التاريخي مستقيماً، مما يمنع التعارضات عند الدمج لاحقاً. كما يمنع إنشاء التزامات دمج غير ضرورية (&amp;ldquo;Merge branch &amp;lsquo;main&amp;rsquo; into feature&amp;rdquo;).&lt;/p>
&lt;h2 id="83-السيناريو-3-دمج-ميزة-مكتملة-استخدام-merge">8.3 [السيناريو 3] دمج ميزة مكتملة (استخدام Merge)
&lt;/h2>&lt;p>اكتمل التطوير في فرع &lt;code>feature&lt;/code>، وحان وقت دمجها في فرع &lt;code>main&lt;/code>. هنا نستخدم &lt;strong>&lt;code>git merge --no-ff&lt;/code>&lt;/strong> (وهذا يعادل اختيار &amp;ldquo;Create a merge commit&amp;rdquo; في Pull Request على منصات مثل 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;دمج ميزة: تنفيذ ميزة تسجيل دخول المستخدم&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>هذا يترك نقطة التقاء تاريخية (التزام دمج) على DAG لفرع &lt;code>main&lt;/code> تفيد بأنه &amp;ldquo;هنا تم دمج ميزة واحدة&amp;rdquo;. عند مراجعة السجل لاحقاً، سيكون من الأسهل تتبع الكود حسب الميزة (Feature).&lt;/p>
&lt;hr>
&lt;h1 id="9-الخلاصة-ملخص">9. الخلاصة (ملخص)
&lt;/h1>&lt;p>في استخدام Git، إن الأساليب المتطرفة مثل &amp;ldquo;إنجاز كل شيء باستخدام Merge&amp;rdquo; أو &amp;ldquo;جعل كل شيء مستقيماً باستخدام Rebase&amp;rdquo; لها مزاياها وعيوبها.&lt;/p>
&lt;p>أفضل ممارسة في العمل الفعلي هي النهج الهجين (Hybrid): &lt;strong>&amp;ldquo;ترتيب السجل الخاص المحلي بشكل جميل باستخدام rebase، والاحتفاظ بسياق السجل العام المتكامل باستخدام merge &amp;ndash;no-ff&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>محلياً (مساحة العمل الشخصية)&lt;/strong>: استخدم &lt;code>rebase&lt;/code> لإزالة الالتزامات غير الضرورية، ومواكبة أحدث خط رئيسي، والحفاظ على سجل خطي.&lt;/li>
&lt;li>&lt;strong>عالمياً (مساحة العمل المشتركة)&lt;/strong>: استخدم &lt;code>merge --no-ff&lt;/code> لتسجيل وجود فرع الميزة كالتزام دمج على DAG، مما يسهل عمليات التراجع (Revert) والتتبع.&lt;/li>
&lt;/ul>
&lt;p>من خلال فهم الخلفية المعمارية والرياضية مثل هيكل DAG وآليات دوال التجزئة، ترتقي أوامر Git من مجرد حفظ عن ظهر قلب إلى &amp;ldquo;تصميم مقصود للسجل التاريخي&amp;rdquo;. بالالتزام بالقاعدة الذهبية لإعادة الربط واختيار الأوامر المثلى وفقاً للموقف، يمكنك بناء سجل التزامات نظيف سهل القراءة والصيانة للفريق بأكمله.&lt;/p></description></item><item><title>إعداد مسار CI/CD لمشاريع C++ باستخدام GitHub Actions</title><link>http://kenji.blog/ar/p/github-actions-cpp-cicd-pipeline/</link><pubDate>Sat, 12 Sep 2026 18:00:00 +0900</pubDate><guid>http://kenji.blog/ar/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 إعداد مسار CI/CD لمشاريع C++ باستخدام GitHub Actions" />&lt;h1 id="إعداد-مسار-cicd-لمشاريع-c-باستخدام-github-actions-الدليل-الشامل">إعداد مسار CI/CD لمشاريع C++ باستخدام GitHub Actions: الدليل الشامل
&lt;/h1>&lt;p>في نموذج تطوير البرمجيات الحديث، يعتبر التكامل المستمر (Continuous Integration: CI) والتسليم/النشر المستمر (Continuous Delivery/Deployment: CD) عنصرين أساسيين للحفاظ على عملية تطوير رشيقة وبرمجيات عالية الجودة. مع وجود العديد من لغات البرمجة، فإن إعداد مسار CI/CD في C++ يأتي مع صعوبات وتعقيدات فريدة مقارنة باللغات الأخرى (مثل Python، JavaScript، Go، إلخ).&lt;/p>
&lt;p>في هذه المقالة، سنشرح بتفصيل شديد كيفية بناء مسار CI/CD قوي وعملي لمشاريع C++ من الصفر باستخدام GitHub Actions. سنغطي جميع التقنيات العملية، بدءًا من البناء المصفوفي (Matrix Build) عبر المنصات المتعددة (Windows، Linux، macOS)، دمج نظام البناء باستخدام CMake، الاختبار الآلي باستخدام CTest، أتمتة التحليل الثابت والديناميكي، قياس تغطية الكود، وحتى التسليم التلقائي للثنائيات المجمّعة عبر إصدارات جيتهاب (GitHub Releases).&lt;/p>
&lt;h2 id="1-أهمية-cicd-في-مشاريع-c-والتحديات-الخاصة-بها">1. أهمية CI/CD في مشاريع C++ والتحديات الخاصة بها
&lt;/h2>&lt;p>في تطوير تطبيقات الويب أو باستخدام لغات السكربت، غالباً ما يكون الاختبار والبناء على حاوية Docker واحدة كافياً. ومع ذلك، فإن C++ هي لغة تُترجم محليًا (natively compiled) وتعتمد بشكل كبير على معمارية الأجهزة ونظام التشغيل في بيئة التنفيذ.&lt;/p>
&lt;p>عند تقديم CI/CD إلى مشاريع C++، فإن التحديات الرئيسية التي نواجهها هي كالتالي:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>تنوع المنصات&lt;/strong>: تختلف واجهات برمجة التطبيقات (مثل Windows API، POSIX، إلخ) باختلاف أنظمة التشغيل مثل Windows وLinux وmacOS. من الشائع جدًا أن يعمل الكود في بيئة التطوير المحلية للمطور (مثل macOS) ولكنه يواجه أخطاء ترجمة (Compile Errors) على Linux أو Windows.&lt;/li>
&lt;li>&lt;strong>اختلافات المترجمات (Compilers)&lt;/strong>: المترجمات الرئيسية مثل Microsoft Visual C++ (MSVC) وGNU Compiler Collection (GCC) وClang تختلف في درجة تنفيذها وتفسيرها لمعايير C++ (C++17، C++20، C++23) وصرامة تحذيراتها.&lt;/li>
&lt;li>&lt;strong>وقت البناء&lt;/strong>: في مشاريع C++ الكبيرة، ليس من النادر أن يستغرق البناء من عشرات الدقائق إلى عدة ساعات. تتطلب بيئة CI استراتيجيات تخزين مؤقت (Caching) وتوازي (Parallelization) للبناء بكفاءة باستخدام موارد حوسبة محدودة.&lt;/li>
&lt;li>&lt;strong>إدارة التبعيات&lt;/strong>: لا تمتلك C++ مدير حزم قياسي مطلق مثل npm أو pip. من الضروري حل المكتبات بشكل صحيح في كل مرة على بيئة CI باستخدام أدوات مثل vcpkg، Conan، أو &lt;code>FetchContent&lt;/code> في CMake.&lt;/li>
&lt;li>&lt;strong>إدارة الذاكرة والسلوك غير المحدد&lt;/strong>: نظرًا لوجود عمليات المؤشرات وإدارة الذاكرة اليدوية، فمن الضروري أتمتة الكشف عن تسرب الذاكرة والسلوك غير المحدد (Undefined Behavior) بالإضافة إلى اختبار المنطق العادي.&lt;/li>
&lt;/ol>
&lt;p>لحل هذه التحديات، تُعد GitHub Actions الحل الأمثل حيث يمكنها توفير أجهزة افتراضية لأنظمة تشغيل مختلفة عند الطلب، وتحديد مسارات العمل المعقدة من خلال الكود (Configuration as Code).&lt;/p>
&lt;h2 id="2-نظرة-عامة-على-بنية-مسار-cicd">2. نظرة عامة على بنية مسار CI/CD
&lt;/h2>&lt;p>دعونا نتصور الصورة الكاملة لمسار CI/CD الذي سنقوم ببنائه. يوضح مخطط تسلسل Mermaid أدناه سير العمل من دفع الكود (Push) إلى الإصدار (Release).&lt;/p>
&lt;pre class="mermaid">
sequenceDiagram
participant Dev as &amp;#34;المطور&amp;#34;
participant Repo as &amp;#34;مستودع GitHub&amp;#34;
participant Action as &amp;#34;GitHub Actions CI/CD&amp;#34;
participant Rel as &amp;#34;إصدارات GitHub&amp;#34;
Dev-&amp;gt;&amp;gt;Repo: &amp;#34;دفع الفرع / فتح PR&amp;#34;
Repo-&amp;gt;&amp;gt;Action: &amp;#34;بدء تشغيل مسار CI&amp;#34;
activate Action
Action-&amp;gt;&amp;gt;Action: &amp;#34;فحص الكود والتحليل الثابت (Clang-Tidy)&amp;#34;
rect rgb(200, 220, 240)
note right of Action: &amp;#34;بناء مصفوفي متعدد المنصات&amp;#34;
Action-&amp;gt;&amp;gt;Action: &amp;#34;البناء على Ubuntu (GCC/Clang)&amp;#34;
Action-&amp;gt;&amp;gt;Action: &amp;#34;البناء على Windows (MSVC)&amp;#34;
Action-&amp;gt;&amp;gt;Action: &amp;#34;البناء على macOS (Apple Clang)&amp;#34;
end
Action-&amp;gt;&amp;gt;Action: &amp;#34;تشغيل CTest (مع ASAN/UBSAN)&amp;#34;
Action-&amp;gt;&amp;gt;Action: &amp;#34;إنشاء تقرير التغطية&amp;#34;
alt &amp;#34;إذا تم دفع علامة (مثال، v1.0.0)&amp;#34;
Action-&amp;gt;&amp;gt;Action: &amp;#34;تغليف الثنائيات مع CPack&amp;#34;
Action-&amp;gt;&amp;gt;Rel: &amp;#34;رفع ZIP/Tarball إلى الإصدار&amp;#34;
end
deactivate Action
Repo--&amp;gt;&amp;gt;Dev: &amp;#34;الإبلاغ عن حالة CI (نجاح/فشل)&amp;#34;
&lt;/pre>
&lt;p>في هذه البنية، نوفر ملاحظات سريعة (التحليل الثابت والبناء والاختبار) في مرحلة طلب السحب (Pull Request)، ونقوم بتغليف وتوزيع المخرجات عند إضافة علامة الإصدار (Version Tag).&lt;/p>
&lt;h2 id="3-إعداد-المشروع-باستخدام-modern-cmake">3. إعداد المشروع باستخدام Modern CMake
&lt;/h2>&lt;p>أساس أي مسار CI ممتاز هو نظام بناء قوي. سنستخدم CMake، والذي يعد المعيار الفعلي (De facto standard) في C++. هنا، نعتمد نهجًا موجهًا نحو الأهداف (Target-oriented) يُعرف باسم &amp;ldquo;Modern CMake&amp;rdquo;.&lt;/p>
&lt;p>نفترض أن هيكل دليل المشروع هو كالتالي:&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>مثال على إعدادات &lt;code>CMakeLists.txt&lt;/code> في الجذر:&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"># إعداد معيار C++
&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"># تعطيل الامتدادات الخاصة بالمترجم لزيادة قابلية النقل
&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"># صرامة تحذيرات المترجم
&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"># إنشاء هدف المكتبة
&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"># إنشاء هدف الملف التنفيذي
&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"># تفعيل الاختبارات
&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"># تعريف قواعد التثبيت (لـ 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"># إعدادات التغليف باستخدام 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>النقاط المهمة:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;code>CMAKE_CXX_EXTENSIONS OFF&lt;/code>: يمنع الاعتماد على ميزات غير قياسية مثل امتدادات GNU، مما يضمن التوافق عبر المنصات.&lt;/li>
&lt;li>&lt;strong>صرامة التحذيرات (&lt;code>-Werror&lt;/code> / &lt;code>/WX&lt;/code>)&lt;/strong>: من خلال التعامل مع تحذيرات المترجم كأخطاء في بيئة CI، فإننا نفرض جودة كود عالية بشكل صارم.&lt;/li>
&lt;li>&lt;strong>GNUInstallDirs&lt;/strong>: يحل مسارات التثبيت القياسية لكل نظام تشغيل (مثل &lt;code>/usr/local/bin&lt;/code> أو &lt;code>C:\Program Files&lt;/code>) تلقائيًا.&lt;/li>
&lt;/ul>
&lt;h2 id="4-أساسيات-github-actions-واستراتيجية-المصفوفة">4. أساسيات GitHub Actions واستراتيجية المصفوفة
&lt;/h2>&lt;p>تتكون GitHub Actions من ملفات YAML موجودة في دليل &lt;code>.github/workflows/&lt;/code>.
أقوى ميزة لمشاريع C++ هي &amp;ldquo;استراتيجية المصفوفة&amp;rdquo; (Matrix Strategy). تتيح لك هذه الميزة إنشاء مجموعات ديناميكية من أنظمة التشغيل والمترجمات وتشغيلها بالتوازي.&lt;/p>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;بدء مسار العمل&amp;#34;] --&amp;gt; B[&amp;#34;تقييم مهمة المصفوفة&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>فيما يلي تعريف وظيفة YAML الأساسي لبناء مصفوفة.&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"># الاستمرار في بناء أنظمة التشغيل الأخرى حتى في حالة فشل إحدى الوظائف&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> مهم جداً. على سبيل المثال، إذا تم استخدام واجهة برمجة تطبيقات (API) خاصة بنظام Linux بالخطأ، فسيفشل البناء على Ubuntu، ولكننا نريد أيضًا معرفة ما إذا كان البناء على Windows سينجح في نفس الوقت.&lt;/p>
&lt;h2 id="5-تكاليف-البناء-وتحسين-المعالجة-المتوازية-باستخدام-قانون-أمدال">5. تكاليف البناء وتحسين المعالجة المتوازية باستخدام قانون أمدال
&lt;/h2>&lt;p>يعد CI/CD في البيئات السحابية معركة مع الوقت، حيث يرتبط وقت البناء مباشرة بوقت انتظار المطورين وتكاليف التشغيل.
هنا، دعونا نتناول التحسين في أوقات البناء رياضيًا باستخدام &amp;ldquo;قانون أمدال&amp;rdquo; (Amdahl&amp;rsquo;s Law) في علوم الكمبيوتر.&lt;/p>
&lt;p>قانون أمدال يُعرّف الحد الأقصى النظري لنسبة تحسين السرعة $S(N)$ عند استخدام $N$ معالجات، حيث $P$ هي نسبة الجزء القابل للتوازي من البرنامج، على النحو التالي:&lt;/p>
$$ S(N) = \frac{1}{(1 - P) + \frac{P}{N}} $$&lt;p>في عملية بناء C++، تكون ترجمة كل وحدة ترجمة (Translation Unit: ملف &lt;code>.cpp&lt;/code>) في الكود المصدري مستقلة تمامًا، مما يجعل التوازي ممكنًا. من ناحية أخرى، فإن تكوين CMake ومرحلة ربط الثنائيات النهائية (Linking Phase) تكون بشكل أساسي تسلسلية (لا يمكن موازنتها).&lt;/p>
&lt;p>بافتراض أن 80٪ من وقت البناء الإجمالي للمشروع هو مرحلة الترجمة ($P = 0.8$) و 20٪ هو المرحلة التسلسلية ($1 - P = 0.2$).
يوفر المشغل الافتراضي (Runner) لـ GitHub Actions (Linux) نواتين (2 مسارات/threads). لذا عندما تكون $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>باستخدام نواتين فقط، نحصل على تحسين في السرعة بنحو 1.67 مرة. لتحقيق ذلك، من الضروري تحديد خيار &lt;code>--parallel&lt;/code> في أمر بناء CMake.&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;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 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>علاوة على ذلك، نأخذ في الاعتبار حساب التكلفة. تكلفة الاستخدام الإجمالية $C_{total}$ لـ GitHub Actions هي مجموع حاصل ضرب وقت تنفيذ الوظيفة $T_i$ في سعر الوحدة للمشغل $R_i$.&lt;/p>
$$ C_{total} = \sum_{i=1}^{M} \left( T_i \times R_i \right) $$&lt;p>لا يؤدي تقليل وقت البناء إلى تسريع حلقة الملاحظات فحسب، بل يؤدي أيضًا بشكل مباشر إلى تقليل تكاليف تشغيل المشروع (خاصة بالنسبة للمستودعات الخاصة). إذا كانت هناك حاجة إلى سرعات أعلى، فإن تطبيق &lt;code>ccache&lt;/code> للتخزين المؤقت لنتائج الترجمة يُعد طريقة فعالة.&lt;/p>
&lt;h2 id="6-الاختبار-الآلي-ودمج-المطهرات-sanitizers">6. الاختبار الآلي ودمج المطهرات (Sanitizers)
&lt;/h2>&lt;p>لمنع الأخطاء بشكل استباقي في C++، يوصى بشدة بتقديم &amp;ldquo;المطهرات&amp;rdquo; (Sanitizers) التي تكتشف تسرب الذاكرة والسلوكيات غير المحددة أثناء وقت التشغيل (Runtime)، بالإضافة إلى اختبار الوحدة (Unit Testing). سنستخدم AddressSanitizer (ASAN) و UndefinedBehaviorSanitizer (UBSAN) التي طورتها جوجل.&lt;/p>
&lt;p>نضيف خيارًا في CMake لتفعيل المطهرات.&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>نقوم بتفعيل هذا الخيار في وظيفة Ubuntu الخاصة بمسار CI ونقوم بتشغيل الاختبارات.&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;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=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;Run 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 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>نستخدم أمر &lt;code>ctest&lt;/code> لتشغيل الاختبارات. من خلال تحديد &lt;code>--output-on-failure&lt;/code>، سيتم عرض السجلات التفصيلية للاختبارات الفاشلة فقط في مخرجات CI، مما يمنع تضخم السجلات.&lt;/p>
&lt;h2 id="7-قياس-التغطية-code-coverage">7. قياس التغطية (Code Coverage)
&lt;/h2>&lt;p>يعد تصور مقدار الكود الذي تغطيه الاختبارات أمرًا مهمًا لضمان الجودة. باستخدام بيئة Linux (GCC)، نقيس التغطية بواسطة &lt;code>gcov&lt;/code> و &lt;code>lcov&lt;/code>.&lt;/p>
&lt;p>أولاً، نقوم بتعيين أعلام الترجمة (Compile Flags) في CMake لقياس التغطية.&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>نحدد وظيفة مستقلة لقياس التغطية في GitHub Actions.&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>باستخدام أمر &lt;code>lcov --remove&lt;/code>، يتم استبعاد رؤوس النظام (System Headers) والمكتبات الخارجية (Third-party Libraries) ورمز الاختبار نفسه من قياس التغطية. يتيح لنا هذا الحصول على التغطية الخالصة للكود المصدري الخاص بالمشروع.&lt;/p>
&lt;h2 id="8-التسليم-التلقائي-للثنائيات-عبر-github-releases-cd">8. التسليم التلقائي للثنائيات عبر GitHub Releases (CD)
&lt;/h2>&lt;p>الآن سنبني جزء &amp;ldquo;CD&amp;rdquo; من CI/CD. عندما يقوم المطور بإضافة علامة إصدار في Git (مثل: &lt;code>v1.2.0&lt;/code>) ويدفعها (Push)، سيتم تلقائيًا تجميع الثنائيات القابلة للتنفيذ لكل نظام تشغيل، وتغليفها في ملفات ZIP أو Tarball، ثم رفعها إلى GitHub Releases.&lt;/p>
&lt;p>في هذه الخطوة، نستخدم أداة التغليف &lt;code>CPack&lt;/code> المرفقة مع CMake.&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>باستخدام هذا الإعداد، بمجرد تنفيذ الأوامر &lt;code>git tag v1.0.0&lt;/code> و &lt;code>git push origin v1.0.0&lt;/code>، سيتم نشر ملف ZIP لمستخدمي Windows وملف Tarball لمستخدمي Linux/macOS تلقائيًا على صفحة الإصدار (Release Page) دون أي تدخل يدوي. إنها ميزة قوية للغاية في إيصال البرمجيات للمستخدمين.&lt;/p>
&lt;h2 id="9-ملف-workflow-yaml-الكامل">9. ملف Workflow YAML الكامل
&lt;/h2>&lt;p>يظهر أدناه الكود الكامل والقوي والعملي لملف &lt;code>.github/workflows/main.yml&lt;/code> الذي يجمع كل العناصر التي شرحناها حتى الآن.&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-نحو-cicd-أكثر-تقدما-التحليل-الثابت-والتنسيق">10. نحو CI/CD أكثر تقدمًا (التحليل الثابت والتنسيق)
&lt;/h2>&lt;p>على الرغم من حذف الشرح التفصيلي هنا، يوصى بدمج أدوات إضافية لضمان الجودة في مسار العمل أثناء التشغيل الفعلي.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>فرض Clang-Format&lt;/strong>: لتقليل عبء مراجعات الكود (Code Review)، قم بدمج فحص نمط الكود (Code Style) باستخدام &lt;code>clang-format&lt;/code> في CI، واجعل مسار العمل يفشل إذا كان ينتهك قواعد التنسيق.&lt;/li>
&lt;li>&lt;strong>التحليل الثابت (Clang-Tidy)&lt;/strong>: لاكتشاف الأخطاء الكامنة التي لا يمكن منعها بتحذيرات المترجم وحدها، أو الكود غير الفعال (مثل النسخ غير الضروري)، قم بدمج &lt;code>clang-tidy&lt;/code> في CMake وتشغيله على CI.&lt;/li>
&lt;li>&lt;strong>استخدام التخزين المؤقت لـ vcpkg / Conan&lt;/strong>: في حال استخدام العديد من مكتبات الطرف الثالث، يستغرق بناء التبعيات وقتًا طويلاً. يمكنك تقليل وقت البناء بشكل كبير عن طريق استخدام &lt;code>actions/cache&lt;/code> من GitHub Actions للاحتفاظ بالدليل المثبت لـ vcpkg أو التخزين المؤقت لـ Conan.&lt;/li>
&lt;/ol>
&lt;h2 id="الخاتمة">الخاتمة
&lt;/h2>&lt;p>قد يبدو إعداد مسار CI/CD في مشاريع C++ للوهلة الأولى كعقبة كبيرة بسبب الاعتماد على المنصة وتعقيد أدوات البناء. ولكن، من خلال الدمج الصحيح لبيئة GitHub Actions، و Modern CMake، ونظام CTest/CPack، يمكنك الحصول على تدفق تطوير مؤتمت وقوي للغاية.&lt;/p>
&lt;p>إن التحقق متعدد المنصات (Cross-platform Verification) باستخدام استراتيجية المصفوفة، واكتشاف الأخطاء أثناء وقت التشغيل عبر المطهرات (Sanitizers)، وقياس التغطية، والنشر التلقائي في GitHub Releases الذي تم شرحه في هذه المقالة هي أفضل الممارسات المعتمدة على نطاق واسع حتى في مشاريع مفتوحة المصدر على المستوى التجاري.&lt;/p>
&lt;p>يقلل مسار CI/CD المؤتمت من الوقت الذي يقضيه المطورون في &amp;ldquo;البحث عن الأخطاء&amp;rdquo; و&amp;quot;عمليات البناء والإصدار اليدوية&amp;quot; إلى الحد الأدنى، ويصبح أقوى سلاح للتركيز على نشاط البرمجة الإبداعي الأصلي. تأكد من إضافته إلى مشروع C++ الخاص بك لتستمتع بحياة تطوير رشيقة ومريحة.&lt;/p></description></item><item><title>كيفية استضافة مدونة Hugo مجانًا وبسرعة فائقة على Cloudflare Pages و GitHub Pages</title><link>http://kenji.blog/ar/p/hugo-hosting-cloudflare-vs-github-pages/</link><pubDate>Sat, 12 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/ar/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 كيفية استضافة مدونة Hugo مجانًا وبسرعة فائقة على Cloudflare Pages و GitHub Pages" />&lt;p>عند إدارة موقع ويب أو مدونة، تعتبر سرعة العرض (الأداء)، تكاليف التشغيل، والأمان عوامل في غاية الأهمية. في الماضي، كان المزيج بين أنظمة إدارة المحتوى الديناميكية (CMS) مثل WordPress وخوادم الاستضافة المشتركة هو السائد، أما اليوم فإن المعمارية المعروفة باسم &amp;ldquo;Jamstack&amp;rdquo; تحظى باهتمام كبير. من بين هذه التقنيات، بدمج &amp;ldquo;Hugo&amp;rdquo; وهو مولد مواقع ثابتة (SSG) فائق السرعة مبني بلغة Go، مع خدمات استضافة حديثة مثل Cloudflare Pages و GitHub Pages، يمكنك بناء بيئة تدوين &lt;strong>مجانية بالكامل وفائقة السرعة&lt;/strong>.&lt;/p>
&lt;p>في هذا المقال، سنشرح بتعمق من منظور تقني الخطوات العملية لنشر موقع ثابت مبني بـ Hugo على Cloudflare Pages و GitHub Pages، والفرق في المعمارية بين كل منصة، وبناء CI/CD (التكامل المستمر/التسليم المستمر) باستخدام GitHub Actions، وتحسين DNS، واستراتيجيات التخزين المؤقت (Cache)، ووصولاً إلى دمج أدوات تحليل الزيارات التي تراعي الخصوصية.&lt;/p>
&lt;hr>
&lt;h2 id="1-أساسيات-مولدات-المواقع-الثابتة-ssg-ومعمارية-jamstack">1. أساسيات مولدات المواقع الثابتة (SSG) ومعمارية Jamstack
&lt;/h2>&lt;h3 id="11-لماذا-المواقع-الثابتة">1.1 لماذا المواقع الثابتة؟
&lt;/h3>&lt;p>أنظمة إدارة المحتوى الديناميكية التقليدية (مثل: WordPress) تقوم بإرسال استعلام إلى قاعدة البيانات (مثل MySQL) عند كل طلب من المستخدم، وتقوم بإنشاء HTML ديناميكيًا على جانب الخادم (مثل PHP) ثم إعادته. على الرغم من أن هذا الأسلوب يتميز بمرونة عالية، إلا أن مقاومته للزيادات المفاجئة في حركة المرور (ما يسمى بالانتشار الواسع أو هجمات DDoS) ضعيفة، وغالبًا ما تصبح بنية النظام معقدة بسبب الحاجة إلى وضع خوادم تخزين مؤقت (مثل Redis أو Varnish) في المقدمة.&lt;/p>
&lt;p>من ناحية أخرى، في مولدات المواقع الثابتة (SSG) التي تعتمد معمارية Jamstack (وهي اختصار لـ JavaScript و APIs و Markup)، يتم إنشاء جميع ملفات HTML و CSS و JavaScript مسبقًا (أثناء عملية البناء). عند استلام طلب المستخدم، يقوم خادم الويب (أو CDN) ببساطة بإرجاع الملفات الثابتة التي تم إنشاؤها مسبقًا كما هي، مما يوفر سرعة فائقة وأمانًا قويًا.&lt;/p>
&lt;h3 id="12-أفضلية-hugo">1.2 أفضلية Hugo
&lt;/h3>&lt;p>هناك العديد من الخيارات المتاحة لـ SSG مثل Next.js و Gatsby و Jekyll و Astro، ولكن الميزة الأكبر لـ Hugo هي &lt;strong>سرعة البناء&lt;/strong>. بفضل المعالجة المتزامنة في لغة Go، تكتمل عملية البناء في بضع ثوانٍ فقط حتى لموقع يحتوي على آلاف أو عشرات الآلاف من الصفحات. هذا يقلل بشكل كبير من أوقات الانتظار في مسار CI/CD، مما يؤدي بشكل مباشر إلى تحسين تجربة المطور (DX: Developer Experience).&lt;/p>
&lt;hr>
&lt;h2 id="2-مقارنة-معمارية-خدمات-الاستضافة">2. مقارنة معمارية خدمات الاستضافة
&lt;/h2>&lt;p>التحدي التالي هو تحديد المكان الذي سيتم فيه استضافة الملفات الثابتة التي تم إنشاؤها بواسطة Hugo. تشمل الخيارات البارزة Cloudflare Pages و GitHub Pages و Netlify، ولكن بنية الشبكة التي تعمل خلف الكواليس تختلف في كل منها.&lt;/p>
&lt;h3 id="21-شبكات-توصيل-المحتوى-cdn-والحوسبة-الطرفية-edge-computing">2.1 شبكات توصيل المحتوى (CDN) والحوسبة الطرفية (Edge Computing)
&lt;/h3>&lt;p>تستخدم جميع هذه المنصات شبكة توصيل محتوى (CDN) موزعة عالميًا لتوصيل المحتوى. ومع ذلك، لا يقتصر الأمر على مجرد التخزين المؤقت للملفات الثابتة، بل إن العامل الفارق هو القدرة على استخدام &amp;ldquo;الحوسبة الطرفية&amp;rdquo; لتوجيه الطلبات وإعادة كتابة الترويسات (Headers) في أقرب نقطة تواجد (PoP) للمستخدم.&lt;/p>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;المستخدم (طوكيو)&amp;#34;] --&amp;gt;|Low Latency| B[&amp;#34;عقدة طرفية (NRT)&amp;#34;]
C[&amp;#34;المستخدم (نيويورك)&amp;#34;] --&amp;gt;|Low Latency| D[&amp;#34;عقدة طرفية (EWR)&amp;#34;]
E[&amp;#34;المستخدم (لندن)&amp;#34;] --&amp;gt;|Low Latency| F[&amp;#34;عقدة طرفية (LHR)&amp;#34;]
B --&amp;gt; G[&amp;#34;تخزين الأصل / مخزن الكائنات&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 هي خدمة تتيح لك نشر ملفات HTML و CSS و JavaScript مباشرة من مستودع GitHub. يتم استخدام شبكات CDN مثل Fastly في الخلفية، مما يوفر أداءً ممتازًا. ومع ذلك، هناك قيود على تخصيص الترويسات (مثل إعدادات &lt;code>Cache-Control&lt;/code> وترويسات الأمان)، وتعتمد إعدادات إعادة التوجيه على meta refresh في HTML أو إضافات Jekyll، مما يجعل وظائفها كبنية تحتية بحتة متواضعة إلى حد ما.&lt;/p>
&lt;h3 id="23-cloudflare-pages">2.3 Cloudflare Pages
&lt;/h3>&lt;p>Cloudflare Pages هي خدمة استضافة مواقع ثابتة مبنية على أكبر شبكة Anycast في العالم تفخر بها Cloudflare (منتشرة في أكثر من 275 مدينة).
توفر دعمًا قياسيًا لـ HTTP/3 (QUIC)، وتحسين الصور، وتكامل الوظائف الطرفية (Cloudflare Workers)، مما يتيح ضبط أداء استثنائي. بالإضافة إلى ذلك، لا توجد رسوم على عرض النطاق الترددي (Bandwidth)، وهي ميزة كبيرة حيث يمكنك تشغيلها مجانًا مهما زادت حركة المرور بشكل مفاجئ.&lt;/p>
&lt;h3 id="24-netlify">2.4 Netlify
&lt;/h3>&lt;p>تعتبر Netlify رائدة في معمارية Jamstack، حيث توفر تجربة مطور (DX) متكاملة تجمع بين النماذج، المصادقة (Identity)، والوظائف بدون خادم (Serverless Functions). ولكن إذا تجاوزت حد عرض النطاق الترددي المجاني (100 جيجابايت شهريًا)، ستتحمل رسوم استهلاك باهظة، لذلك يجب الانتباه إلى إدارة التكاليف في المدونات التي تستخدم الصور ومقاطع الفيديو بكثرة.&lt;/p>
&lt;hr>
&lt;h2 id="3-الحساب-النظري-للأداء-وزمن-الانتقال-النموذج-الرياضي-باستخدام-latex">3. الحساب النظري للأداء وزمن الانتقال (النموذج الرياضي باستخدام LaTeX)
&lt;/h2>&lt;p>عند تقييم أداء الويب، يعد تقليل زمن الانتقال (Latency) أهم مؤشر. دعونا نضع نموذجًا لمدى تقليل زمن الانتقال عند استخدام شبكة توصيل المحتوى CDN (الطرفية) مقارنة بالوصول المباشر إلى خادم الأصل (Origin Server).&lt;/p>
&lt;p>نفترض أن احتمالية نجاح طلب المستخدم في العثور على ما يطلبه في التخزين المؤقت هي &amp;ldquo;نسبة إصابة التخزين المؤقت (Cache Hit Ratio)&amp;rdquo; ونرمز لها بـ $C$. حيث $0 \le C \le 1$.
ونرمز لزمن الانتقال إلى خادم الأصل بـ $L_{origin}$، وزمن الانتقال إلى أقرب عقدة طرفية بـ $L_{edge}$.&lt;/p>
&lt;p>يتم حساب متوسط زمن الانتقال الجديد $L_{new}$ كقيمة متوقعة على النحو التالي:&lt;/p>
$$ L_{new} = C \times L_{edge} + (1 - C) \times (L_{edge} + L_{origin}) $$&lt;p>عند تبسيط هذه المعادلة تصبح كالتالي:&lt;/p>
$$ L_{new} = L_{edge} + (1 - C) \times L_{origin} $$&lt;p>على سبيل المثال، عندما يصل مستخدم في طوكيو إلى خادم أصل يقع على الساحل الشرقي للولايات المتحدة (نيويورك)، فإن $L_{origin}$ سيكون حوالي 200 مللي ثانية، مع الأخذ في الاعتبار المسافة المادية للألياف الضوئية وتأخير المعالجة في الموجهات (Routers). من ناحية أخرى، عند استخدام شبكة CDN مثل Cloudflare، يمكن الاتصال بالعقدة الطرفية في طوكيو، مما يقلل $L_{edge}$ إلى حوالي 10 مللي ثانية.&lt;/p>
&lt;p>بافتراض أن نسبة إصابة التخزين المؤقت $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>بهذه الطريقة، من خلال اعتماد CDN، يصبح من الممكن تقليل متوسط زمن الانتقال بشكل كبير (حوالي 90٪) من 210 مللي ثانية إلى 20 مللي ثانية.&lt;/p>
&lt;hr>
&lt;h2 id="4-بناء-مسار-cicd-باستخدام-github-actions">4. بناء مسار CI/CD باستخدام GitHub Actions
&lt;/h2>&lt;p>من أجل أتمتة عملية تحديث مدونة Hugo، سنقوم ببناء مسار CI/CD باستخدام GitHub Actions. بفضل هذا، بمجرد كتابة مقالات بصيغة Markdown محليًا وتنفيذ أمر &lt;code>git push&lt;/code>، ستبدأ عملية البناء تلقائيًا وسيتم نشر الموقع على Cloudflare Pages أو GitHub Pages.&lt;/p>
&lt;p>يوضح المخطط التسلسلي (Sequence Diagram) التالي التدفق الكامل منذ عملية الدفع (Push) للمقال وحتى تسليمه للمستخدم.&lt;/p>
&lt;pre class="mermaid">
sequenceDiagram
participant U as &amp;#34;المستخدم (المؤلف)&amp;#34;
participant G as &amp;#34;مستودع GitHub&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;الزائر&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 Event)&amp;#34;
A-&amp;gt;&amp;gt;A: &amp;#34;سحب الكود وإعداد Hugo&amp;#34;
A-&amp;gt;&amp;gt;A: &amp;#34;hugo --minify (بناء)&amp;#34;
A-&amp;gt;&amp;gt;C: &amp;#34;النشر على Cloudflare Pages (رفع مباشر)&amp;#34;
C--&amp;gt;&amp;gt;A: &amp;#34;رابط النشر (نجاح)&amp;#34;
V-&amp;gt;&amp;gt;C: &amp;#34;طلب صفحة الويب&amp;#34;
C--&amp;gt;&amp;gt;V: &amp;#34;إرجاع HTML المخزن مؤقتا (عبر العقدة الطرفية)&amp;#34;
&lt;/pre>
&lt;h3 id="41-إعدادات-النشر-على-cloudflare-pages-الرفع-المباشر---direct-upload">4.1 إعدادات النشر على Cloudflare Pages (الرفع المباشر - Direct Upload)
&lt;/h3>&lt;p>في Cloudflare Pages، توجد طريقتان: ربط مستودع GitHub وبناء الموقع على بنية Cloudflare التحتية، أو طريقة &amp;ldquo;الرفع المباشر (Direct Upload)&amp;rdquo; للملفات الثابتة التي تم بناؤها بواسطة GitHub Actions. إذا كنت ترغب في إدارة إصدارات Hugo بشكل أكثر صرامة وربطه بمهام أخرى (مثل الاختبار وتحسين الصور)، يُوصى بالبناء على GitHub Actions واستخدام الرفع المباشر.&lt;/p>
&lt;p>فيما يلي مثال عملي لملف &lt;code>.github/workflows/deploy.yml&lt;/code> للنشر على 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;Deploy Hugo site to Cloudflare Pages&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;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="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;Setup Hugo&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;Build Hugo Site&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;Deploy to Cloudflare Pages&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>في مسار العمل هذا، يقوم خيار &lt;code>--minify&lt;/code> بتصغير ملفات HTML/CSS/JS، ويقوم &lt;code>--gc&lt;/code> بحذف الملفات غير الضرورية. وتعتبر هذه أساسيات تحسين الأداء.&lt;/p>
&lt;hr>
&lt;h2 id="5-التعمق-في-إعدادات-dns-النطاق-المخصص-وسجلات-cname--alias">5. التعمق في إعدادات DNS: النطاق المخصص وسجلات CNAME / ALIAS
&lt;/h2>&lt;p>عند استخدام نطاق خاص (مثل: &lt;code>kenji.blog&lt;/code>)، يكون الإعداد الصحيح لـ DNS (نظام أسماء النطاقات) أمرًا حيويًا.&lt;/p>
&lt;h3 id="51-قيود-سجل-cname-ونطاق-الجذر-zone-apex">5.1 قيود سجل CNAME ونطاق الجذر (Zone Apex)
&lt;/h3>&lt;p>عادةً، عند توجيه نطاق فرعي (مثل: &lt;code>www.kenji.blog&lt;/code>) إلى خدمة خارجية، يتم استخدام سجل &lt;code>CNAME&lt;/code>. ومع ذلك، وبسبب مواصفات DNS (حسب RFC 1034)، لا يمكن إعداد سجل &lt;code>CNAME&lt;/code> للنطاق الجذر (المعروف أيضًا باسم Zone Apex أو النطاق المجرد، مثل: &lt;code>kenji.blog&lt;/code>). والسبب في ذلك هو أن النطاق الجذر يجب أن يحتوي دائمًا على سجلات SOA (Start of Authority) وسجلات NS (Name Server) وسجلات MX (Mail Exchange)، وتوجد قاعدة تنص على أن سجل CNAME لا يمكن أن يتواجد مع سجلات الموارد الأخرى.&lt;/p>
&lt;h3 id="52-الحلول-alias--aname--cname-flattening">5.2 الحلول: ALIAS / ANAME / CNAME Flattening
&lt;/h3>&lt;p>لحل هذه المشكلة، توفر شركات تقديم خدمات DNS الحديثة إضافات خاصة بها.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>سجلات ALIAS / ANAME&lt;/strong>: تقوم بتحليل الاسم ديناميكيًا على جانب خادم DNS وإرجاع سجل A النهائي (عنوان IP) إلى العميل. تدعم خدمات مثل Amazon Route 53 هذه الميزة.&lt;/li>
&lt;li>&lt;strong>CNAME Flattening&lt;/strong>: هي ميزة تقدمها Cloudflare. فهي تتصرف كما لو تم إعداد سجل CNAME على النطاق الجذر (Zone Apex)، بينما يقوم خادم DNS المعتمد من Cloudflare بإرجاع عناوين IP التي تم حلها تلقائيًا (سجلات A وسجلات AAAA) إلى العميل بشفافية.&lt;/li>
&lt;/ul>
&lt;p>عند استخدام Cloudflare Pages، فإن تفويض خوادم الأسماء (Name Servers) الخاصة بالنطاق إلى Cloudflare واستغلال تقنية &amp;ldquo;CNAME Flattening&amp;rdquo; يوفر البنية الأكثر سلاسة وعالية الأداء.&lt;/p>
&lt;hr>
&lt;h2 id="6-استراتيجيات-التخزين-المؤقت-والتحكم-في-ترويسات-http">6. استراتيجيات التخزين المؤقت والتحكم في ترويسات HTTP
&lt;/h2>&lt;p>في عملية تسريع المواقع الثابتة، هناك عنصر أساسي آخر وهو &amp;ldquo;استراتيجية التخزين المؤقت&amp;rdquo;. في Cloudflare Pages، يمكنك استخدام ملف يتم إنشاؤه (ملف &lt;code>_headers&lt;/code>) للتحكم الدقيق في ترويسات الاستجابة HTTP (HTTP Response Headers).&lt;/p>
&lt;h3 id="61-التخزين-المؤقت-الطرفي-edge-cache-مقابل-التخزين-المؤقت-للمتصفح-browser-cache">6.1 التخزين المؤقت الطرفي (Edge Cache) مقابل التخزين المؤقت للمتصفح (Browser Cache)
&lt;/h3>&lt;p>ينقسم التخزين المؤقت بشكل عام إلى نوعين: &amp;ldquo;التخزين المؤقت الطرفي (Edge Cache)&amp;rdquo; الذي يتم الاحتفاظ به على جانب شبكة CDN، و &amp;ldquo;التخزين المؤقت للمتصفح (Browser Cache)&amp;rdquo; الذي يتم تخزينه في متصفح المستخدم.&lt;/p>
&lt;p>من المثالي جعل الملفات الثابتة (مثل الصور، CSS، JS، والتي تحتوي أسماؤها على دوال تجزئة &amp;ldquo;Hashes&amp;rdquo;) تُخزن مؤقتًا في المتصفح لفترات طويلة. من ناحية أخرى، بالنسبة لملفات HTML، ولعكس التحديثات فورًا، من الشائع تقصير مدة التخزين المؤقت في المتصفح (أو تعطيله بالكامل) والاعتماد على التخزين المؤقت الطرفي للتعامل معها.&lt;/p>
&lt;p>مثال على إعداد &lt;code>_headers&lt;/code> في 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 في المتصفح والتحقق منها في كل مرة
&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"># تخزين ملفات الأصول (CSS/JS/الصور) في المتصفح لمدة عام واحد
&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-معادلة-حساب-خفض-تكاليف-عرض-النطاق-الترددي">6.2 معادلة حساب خفض تكاليف عرض النطاق الترددي
&lt;/h3>&lt;p>من خلال إعداد ترويسات التخزين المؤقت المناسبة، يمكنك تقليل كمية نقل البيانات من الخادم (الطرفية) بشكل كبير. يمكن التعبير عن التكلفة الشهرية لعرض النطاق الترددي $Cost$ بالنموذج التالي بناءً على كمية النقل $B_i$ لكل مورد، نسبة إصابة التخزين المؤقت $C_i$، وسعر الوحدة لعرض النطاق الترددي $R$:&lt;/p>
$$ Cost = \sum_{i=1}^{n} \left( B_i \times (1 - C_i) \times R \right) $$&lt;p>نظرًا لأن Cloudflare يوفر حركة نقل البيانات الصادرة مجانًا ($R = 0$)، فإن التكلفة المالية المباشرة ستكون $0$. ومع ذلك، عند استخدام بنية تحتية أخرى بالاقتران مثل GitHub Pages، أو استخدام خوادم خلفية مثل AWS S3، فإن تعظيم نسبة إصابة التخزين المؤقت $C_i$ يعد عاملاً رئيسيًا في تقليل تكاليف البنية التحتية.&lt;/p>
&lt;hr>
&lt;h2 id="7-تحليلات-الزيارات-التي-تجمع-بين-الخصوصية-والأداء">7. تحليلات الزيارات التي تجمع بين الخصوصية والأداء
&lt;/h2>&lt;p>أثناء إدارة مدونة، تُعتبر تحليلات الزيارات (Web Analytics) لمعرفة عدد المستخدمين الزائرين أمرًا لا غنى عنه. لفترة طويلة كانت أداة Google Analytics (GA4) هي المعيار الفعلي السائد، ولكن مع توجهات حماية الخصوصية الحديثة (مثل GDPR و CCPA)، والتخلص التدريجي من ملفات تعريف الارتباط للجهات الخارجية (Third-party Cookies)، بدأت الأوضاع في التغير.&lt;/p>
&lt;h3 id="71-التأثير-على-أداء-الويب">7.1 التأثير على أداء الويب
&lt;/h3>&lt;p>عند دمج Google Analytics (وتحديدًا &lt;code>gtag.js&lt;/code> أو Google Tag Manager)، يتم تحميل وتنفيذ العديد من السكربتات الخارجية، مما يؤثر سلبًا على الأداء (خاصة وقت الاستجابة الأولي TTFB ووقت حظر مؤشر الترابط الرئيسي Main-thread Blocking Time).&lt;/p>
&lt;p>دعونا نقسم ونفكر في أوقات تحميل الموقع كما يلي:&lt;/p>
&lt;pre class="mermaid">
pie title &amp;#34;توزيع أوقات تحميل الموقع النموذجي&amp;#34;
&amp;#34;TTFB (الوقت حتى البايت الأول)&amp;#34; : 15
&amp;#34;تنزيل المحتوى (HTML/CSS)&amp;#34; : 20
&amp;#34;تحليل DOM والعرض&amp;#34; : 45
&amp;#34;التحليلات وجافا سكريبت للجهات الخارجية&amp;#34; : 20
&lt;/pre>
&lt;p>ليس من النادر أن تستهلك أدوات التحليل التي تعتمد على جافا سكريبت من جهات خارجية حوالي 20٪ إلى 30٪ من إجمالي وقت التحميل.&lt;/p>
&lt;h3 id="72-دمج-cloudflare-web-analytics">7.2 دمج Cloudflare Web Analytics
&lt;/h3>&lt;p>لذلك، تتجه الأنظار نحو أدوات تحليل الزيارات التي تعطي الأولوية للخصوصية ولا تستخدم ملفات تعريف الارتباط (Cookieless)، مثل Cloudflare Web Analytics و Plausible Analytics.&lt;/p>
&lt;p>تعمل أداة Cloudflare Web Analytics بمجرد تضمين مقطع JavaScript خفيف الوزن للغاية، ولأنها لا تصدر ملفات تعريف الارتباط، فلن تكون هناك حاجة لإعداد لافتات الموافقة على ملفات تعريف الارتباط المزعجة (Cookie Consent Banner).&lt;/p>
&lt;p>التنفيذ في Hugo سهل جدًا أيضًا. كل ما عليك فعله هو إضافة المقطع المقدم إلى ملفات مثل &lt;code>layouts/partials/head.html&lt;/code> أو &lt;code>layouts/partials/analytics.html&lt;/code>.&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>من خلال إضافة سمة &lt;code>defer&lt;/code>، سيتم تحميل السكربت بشكل غير متزامن دون حظر عملية تحليل HTML، ليتم تنفيذه بعد اكتمال بناء DOM. هذا يقلل من التأثير على سرعة العرض الأولي (مثل LCP: عرض أكبر محتوى مرئي، و FCP: سرعة عرض أول محتوى) إلى الحد الأدنى.&lt;/p>
&lt;hr>
&lt;h2 id="8-الخلاصة-وأفضل-الممارسات">8. الخلاصة وأفضل الممارسات
&lt;/h2>&lt;p>عند تشغيل موقع ثابت باستخدام Hugo، فإن اعتماد منصات الاستضافة الحديثة مثل Cloudflare Pages أو GitHub Pages يوفر فوائد هائلة من حيث الأداء مقابل التكلفة، سرعة العرض، والأمان.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>بناء فائق السرعة&lt;/strong>: الاستفادة من سرعة Hugo لتقليل وقت تنفيذ مسار CI/CD (GitHub Actions) إلى أدنى حد ممكن.&lt;/li>
&lt;li>&lt;strong>التسليم عبر الطرفيات (Edge)&lt;/strong>: استخدام شبكة طرفيات Cloudflare لتوصيل المحتوى للمستخدمين حول العالم بزمن انتقال يقاس بالمللي ثانية.&lt;/li>
&lt;li>&lt;strong>التكوين المناسب لـ DNS&lt;/strong>: الاستفادة من تقنية CNAME Flattening لتشغيل النطاق الجذر (النطاق الخاص) بأمان وسرعة.&lt;/li>
&lt;li>&lt;strong>تحسين استراتيجية التخزين المؤقت&lt;/strong>: استخدام &lt;code>_headers&lt;/code> لفصل التخزين المؤقت للمتصفح والتخزين المؤقت الطرفي بشكل مناسب لكل نوع من أنواع الموارد.&lt;/li>
&lt;li>&lt;strong>تحليلات خفيفة الوزن&lt;/strong>: دمج أدوات مثل Cloudflare Web Analytics التي تحافظ على الأداء وتراعي الخصوصية.&lt;/li>
&lt;/ol>
&lt;p>من خلال الجمع بين هذه العناصر، يمكنك مجانًا بناء نظام تدوين قابل للتوسع وقوي، قادر على تحمل حركة مرور ضخمة تصل إلى الملايين من مشاهدات الصفحات (PV) شهريًا. إذا كنت تفكر في إطلاق مدونة تقنية، موقع شركة، أو موقع محفظة أعمال (Portfolio)، فننصحك بشدة بتجربة بنية Jamstack + Hugo + Cloudflare Pages.&lt;/p></description></item></channel></rss>