{"id":"l-git-4-4","courseId":"git-github","lang":"ar","kind":"lesson","summary":"أضف workflow في GitHub Actions يفحص كل pull request، ثم احمِ main بـ ruleset بحيث لا تصل التغييرات إليه إلا عبر pull request نجحت فحوصه.","html":"<p>كل ما سبق اعتمد على تذكّرك أنت. أن تتذكّر تشغيل فحص الـ HTML، وأن تفتح pull request بدل الرفع إلى <code dir=\"ltr\">main</code>، وألّا ترفع بالقوة أبدًا. والناس ينسون، خصوصًا في الحادية عشرة ليلًا قبل التقدّم إلى وظيفة. ينقل هذا الدرس هذه العادات من رأسك إلى GitHub: آلة تشغّل الفحوص على كل pull request، وقواعد تجعل تغيير <code dir=\"ltr\">main</code> بأي طريقة أخرى مستحيلًا.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">أعطِ الموقع الشخصي شيئًا يُفحص</h2>\n<p>التكامل المستمر (continuous integration) يحتاج إلى أمر يفشل حين يكون هناك خطأ. ولموقع HTML، فإن أداة التحقّق (validator) بداية جيدة. في موقعك الشخصي، على فرع جديد:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> switch</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -c</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> add-ci</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">npm</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> init</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -y</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">npm</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> install</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --save-dev</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> html-validate</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">npm</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> pkg</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> set</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> scripts.test=\"html-validate index.html projects.html\"</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">echo</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> '{ \"extends\": [\"html-validate:recommended\"] }'</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> ></span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> .htmlvalidate.json</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">npm</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> test</span></span></code></pre></div>\n<p><code dir=\"ltr\">node_modules/</code> موجود أصلًا في <code dir=\"ltr\">.gitignore</code> منذ درسين؛ سجّل في commit كلًا من <code dir=\"ltr\">package.json</code> و <code dir=\"ltr\">package-lock.json</code> و <code dir=\"ltr\">.htmlvalidate.json</code>. وإن أبلغ <code dir=\"ltr\">npm test</code> عن مشكلات، فهذا هو المقصود: أصلحها، أو دوّنها لأول تشغيل للـ CI. وفي موقع شخصي جديد كثيرًا ما يجد هذا:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">Text</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code>index.html\n  1:1  error  DOCTYPE should be uppercase  doctype-style</code></pre></div>\n<h2 id=\"sec-2\" tabindex=\"-1\">أول workflow لك</h2>\n<p>تشغّل <strong>GitHub Actions</strong> الـ workflows المعرَّفة في ملفات YAML تحت <code dir=\"ltr\">.github/workflows/</code>. أنشئ واحدًا:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"yaml\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">.github/workflows/ci.yml</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">name</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">CI</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">on</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">  pull_request</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    branches</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: [</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">main</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">  push</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    branches</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: [</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">main</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">jobs</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">  check</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    runs-on</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">ubuntu-latest</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    steps</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      - </span><span style=\"color:#116329;--shiki-dark:#7EE787\">uses</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">actions/checkout@v7</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      - </span><span style=\"color:#116329;--shiki-dark:#7EE787\">uses</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">actions/setup-node@v7</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">        with</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">          node-version</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">24</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">          cache</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">npm</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      - </span><span style=\"color:#116329;--shiki-dark:#7EE787\">run</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">npm ci</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      - </span><span style=\"color:#116329;--shiki-dark:#7EE787\">run</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">npm test</span></span></code></pre></div>\n<p>اقرأه من الأعلى إلى الأسفل:</p>\n<ul>\n<li><code dir=\"ltr\">on</code> يسرد <strong>الأحداث</strong> (events) التي تبدأ الـ workflow: أي pull request إلى <code dir=\"ltr\">main</code> (عند فتحه، أو تحديثه بـ commits جديدة)، وعمليات الرفع إلى <code dir=\"ltr\">main</code> بعد الدمج.</li>\n<li><code dir=\"ltr\">jobs</code> يحتوي job واحدًا، <code dir=\"ltr\">check</code>. هذا المعرّف يصبح اسم الـ status check الذي يعرضه GitHub على الـ pull request.</li>\n<li><code dir=\"ltr\">runs-on</code> يختار <strong>runner</strong>، وهو آلة افتراضية جديدة يوفّرها GitHub. وتبدأ فارغة في كل مرة.</li>\n<li><code dir=\"ltr\">steps</code> تُنفَّذ بالترتيب. <code dir=\"ltr\">uses</code> يشغّل <strong>action</strong> منشورًا: <code dir=\"ltr\">actions/checkout</code> ينسخ الـ repository إلى الـ runner، و <code dir=\"ltr\">actions/setup-node</code> يثبّت Node.js 24 ويخزّن تنزيلات npm مؤقتًا. و <code dir=\"ltr\">run</code> ينفّذ أمر shell: <code dir=\"ltr\">npm ci</code> يثبّت بالضبط ما يسرده <code dir=\"ltr\">package-lock.json</code>، و <code dir=\"ltr\">npm test</code> يشغّل أداة التحقّق.</li>\n</ul>\n<p>أي خطوة تنتهي بخطأ تُفشل الـ job. سجّل الـ workflow في commit، وارفع الفرع، وافتح pull request.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">مشاهدته وهو يعمل</h2>\n<p>خلال ثوانٍ يعرض الـ pull request فحصًا، “CI / check (pull_request)”، بنقطة صفراء أثناء التشغيل. ثم علامة صح خضراء، أو علامة خطأ حمراء. انقر <strong>Details</strong> لترى السجلّ الكامل لكل خطوة؛ والخطوة الفاشلة تكون موسّعة، وتعرض ناتج أداة التحقّق نفسه الذي ستحصل عليه محليًا.</p>\n<p>لإصلاح فحص أحمر، افعل ما تفعله مع ملاحظات المراجعة: أنشئ commit على الفرع نفسه وارفعه. يعمل الـ workflow مجددًا على الـ commit الجديد. اعتد على قراءة هذه السجلّات. الـ CI لا يحكم عليك؛ إنه زميل يعيد تشغيل الفحوص في كل مرة دون أن يملّ.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 700 220\" role=\"img\" aria-labelledby=\"l-git-4-4-t1\"><defs><marker id=\"arrow-l-git-4-4-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-git-4-4-t1\">مسار من اليسار إلى اليمين: فرع feature، ثم رفع، ثم pull request، ثم بوّابتان متجاورتان، نجاح فحص الـ CI وموافقة المراجعة، ثم الدمج في main. وسهم محجوب يبيّن أن الـ ruleset يرفض الرفع المباشر إلى main.</title>\n  <rect class=\"d-box\" x=\"10\" y=\"80\" width=\"110\" height=\"50\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"65\" y=\"110\" text-anchor=\"middle\">فرع</text>\n  <rect class=\"d-box\" x=\"150\" y=\"80\" width=\"120\" height=\"50\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"210\" y=\"110\" text-anchor=\"middle\">pull request</text>\n  <rect class=\"d-box-success\" x=\"300\" y=\"40\" width=\"140\" height=\"44\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"370\" y=\"67\" text-anchor=\"middle\">CI: check</text>\n  <rect class=\"d-box-success\" x=\"300\" y=\"126\" width=\"140\" height=\"44\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"370\" y=\"153\" text-anchor=\"middle\">مراجعة</text>\n  <rect class=\"d-box-primary\" x=\"480\" y=\"80\" width=\"90\" height=\"50\" rx=\"10\"/>\n  <text class=\"d-label-strong\" x=\"525\" y=\"110\" text-anchor=\"middle\">دمج</text>\n  <rect class=\"d-box-accent\" x=\"600\" y=\"80\" width=\"90\" height=\"50\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"645\" y=\"110\" text-anchor=\"middle\">main</text>\n  <path class=\"d-arrow\" d=\"M120 105 L146 105\" marker-end=\"url(#arrow-l-git-4-4-1)\"/>\n  <path class=\"d-arrow\" d=\"M270 98 L296 70\" marker-end=\"url(#arrow-l-git-4-4-1)\"/>\n  <path class=\"d-arrow\" d=\"M270 112 L296 140\" marker-end=\"url(#arrow-l-git-4-4-1)\"/>\n  <path class=\"d-arrow\" d=\"M440 62 L476 96\" marker-end=\"url(#arrow-l-git-4-4-1)\"/>\n  <path class=\"d-arrow\" d=\"M440 148 L476 114\" marker-end=\"url(#arrow-l-git-4-4-1)\"/>\n  <path class=\"d-arrow\" d=\"M570 105 L596 105\" marker-end=\"url(#arrow-l-git-4-4-1)\"/>\n  <path class=\"d-line d-dashed\" d=\"M65 130 C65 205 600 205 640 136\"/>\n  <rect class=\"d-box-warn\" x=\"250\" y=\"186\" width=\"200\" height=\"30\" rx=\"8\"/>\n  <text class=\"d-label\" x=\"350\" y=\"206\" text-anchor=\"middle\">رفع مباشر: مرفوض</text>\n</svg><figcaption>الطريق المحمي إلى main</figcaption></figure>\n<h2 id=\"sec-4\" tabindex=\"-1\">احمِ main بـ ruleset</h2>\n<p>الفحص الناجح لا يفيد إلا إذا انتظره الدمج. وهذا ما يفرضه الـ <strong>ruleset</strong> (مجموعة القواعد). على GitHub، افتح <strong>Settings</strong> في الـ repository، ثم <strong>Rules</strong>، ثم <strong>Rulesets</strong>، واختر <strong>New ruleset</strong>، ثم <strong>New branch ruleset</strong>:</p>\n<ol>\n<li>سمِّه “Protect main” واضبط <strong>Enforcement status</strong> على Active.</li>\n<li>تحت <strong>Target branches</strong>، أضف الفرع الافتراضي.</li>\n<li>أبقِ <strong>Restrict deletions</strong> و <strong>Block force pushes</strong> محدَّدين.</li>\n<li>حدّد <strong>Require a pull request before merging</strong>. في repository تعمل عليه وحدك اضبط عدد الموافقات المطلوبة على 0 (لا تستطيع الموافقة على الـ pull request الخاص بك)؛ وفي الفريق، 1 أو أكثر.</li>\n<li>حدّد <strong>Require status checks to pass</strong>، وأضف الفحص <code dir=\"ltr\">check</code>، واحفظ.</li>\n</ol>\n<p>الآن حاول تخطّي العملية:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> switch</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> main</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> commit</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --allow-empty</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -m</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"Sneak a commit onto main\"</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> push</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> origin</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> main</span></span></code></pre></div>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">Text</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code>remote: error: GH013: Repository rule violations found for refs/heads/main.</code></pre></div>\n<p>يُرفض الرفع، ويبقى الـ commit المحلي دون أن يُمسّ. كل تغيير على <code dir=\"ltr\">main</code> يمرّ الآن عبر pull request، ويبقى زر الدمج معطّلًا حتى يصبح الفحص أخضر. شغّل <code dir=\"ltr\">git reset --hard origin/main</code> لتتخلّص من الـ commit الفارغ.</p>\n<p>الـ rulesets مجانية في الـ repositories العامة؛ أما في الخاصة فتحتاج إلى خطة مدفوعة مثل GitHub Pro أو Team. وقد ترى أيضًا <strong>branch protection rules</strong> الأقدم في الإعدادات. إنها تؤدّي مهمّة مشابهة؛ لكن الـ rulesets أحدث، ويمكن تركيبها في طبقات، ويستطيع أي شخص لديه صلاحية القراءة أن يرى القواعد المطبّقة.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">خطأ شائع</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>اشتراط فحص لا يبلّغ أبدًا</span></p>\n<p>الفحوص المطلوبة تُطابق بالاسم. إن اشترطت <code dir=\"ltr\">check</code> قبل أن يعمل الـ workflow ولو مرة، فلن يستطيع GitHub عرضه في القائمة؛ وإن أعدت تسمية الـ job لاحقًا، تنتظر الـ pull requests إلى الأبد عند “Expected — Waiting for status to be reported”. شغّل الـ workflow مرة واحدة قبل أن تشترطه، وغيّر اسم الـ job والـ ruleset معًا.</p>\n</aside>\n<h2 id=\"sec-5\" tabindex=\"-1\">ما الذي تستطيع فعله الآن</h2>\n<p>انظر من أين بدأ الموقع الشخصي: مجلد فيه <code dir=\"ltr\">index.html</code>. صار الآن repository بسجلّ مقروء، وفروع تُدمج أو يُعاد تأسيسها بنظافة، و remote على GitHub تصل إليه بمصادقة سليمة، و issues تشرح سبب وجود كل عمل، وإصدارات موسومة بـ tags، وفرع <code dir=\"ltr\">main</code> لا يقبل إلا pull requests مراجَعة ومفحوصة آليًا. وتستطيع التراجع عن معظم الأخطاء، وتعرف أيّ طريقة تراجع تناسب كلًا منها.</p>\n<p>وأهمّ من الأوامر، صار لديك النموذج: الـ commits لقطات مرتبطة بآبائها، والـ branches والـ tags أسماء دالّة، والـ HEAD هو حيث تقف، والـ remotes نسخ أخرى من الرسم البياني نفسه. وحين يفاجئك Git مستقبلًا، شغّل <code dir=\"ltr\">git log --oneline --graph --all</code>، وارسم ما تراه، وغالبًا سيكون الأمر التالي واضحًا.</p>\n","toc":[{"id":"sec-1","text":"أعطِ الموقع الشخصي شيئًا يُفحص","level":2},{"id":"sec-2","text":"أول workflow لك","level":2},{"id":"sec-3","text":"مشاهدته وهو يعمل","level":2},{"id":"sec-4","text":"احمِ main بـ ruleset","level":2},{"id":"sec-5","text":"ما الذي تستطيع فعله الآن","level":2}],"takeaways":["الـ workflow ملف YAML في <code dir=\"ltr\">.github/workflows/</code>؛ يحدّد <code dir=\"ltr\">on</code> الأحداث، وتعمل الـ <code dir=\"ltr\">jobs</code> على runners، وكل خطوة في <code dir=\"ltr\">steps</code> إما تستخدم action عبر <code dir=\"ltr\">uses</code> أو تنفّذ أمرًا عبر <code dir=\"ltr\">run</code>.","كل job يبلّغ عن status check على الـ pull request، يظهر كعلامة صح خضراء أو علامة خطأ حمراء مع السجلّات الكاملة.","الـ branch ruleset على <code dir=\"ltr\">main</code> يستطيع منع الرفع بالقوة والحذف، واشتراط pull request، واشتراط نجاح status checks مسمّاة.","الـ status check المطلوب يُطابَق باسم الـ job، ولا يقترح GitHub إلا الفحوص التي أبلغت عن نتيجتها من قبل، لذا شغّل الـ workflow قبل أن تشترطه.","الحلقة المكتملة هي فرع، ثم رفع، ثم pull request، ثم فحص آلي، ثم مراجعة، ثم دمج، ولا أحد يستطيع تخطّي خطوة."],"further":[{"title":"Building and testing Node.js","url":"https://docs.github.com/en/actions/tutorials/build-and-test-code/nodejs"},{"title":"Workflow syntax for GitHub Actions","url":"https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax"},{"title":"About rulesets","url":"https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/about-rulesets"},{"title":"Available rules for rulesets","url":"https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets"}],"quiz":[{"q":"<p>يبدأ الـ workflow الخاص بك بـ <code dir=\"ltr\">on: pull_request</code>. متى يعمل؟</p>","options":[{"text":"فقط حين ينقر أحدهم Run workflow في تبويب Actions.","why":"هذا هو الحدث <code dir=\"ltr\">workflow_dispatch</code>. أما <code dir=\"ltr\">pull_request</code> فيُطلق تلقائيًا بنشاط الـ pull requests."},{"text":"حين يُفتح pull request، ومجددًا في كل مرة تُرفع فيها commits جديدة إلى فرعه.","why":"صحيح. افتراضيًا يعمل <code dir=\"ltr\">pull_request</code> عند الفتح (opened) والمزامنة (synchronize، أي commits جديدة) وإعادة الفتح (reopened)، فيُفحص كل تحديث."},{"text":"فقط بعد دمج الـ pull request.","why":"الغرض من فحص الـ pull request هو المعرفة قبل الدمج. والتشغيل بعد الدمج يكون حدث <code dir=\"ltr\">push</code> إلى <code dir=\"ltr\">main</code>."},{"text":"في كل مرة يرفع فيها أي شخص إلى أي فرع.","why":"هذا هو الحدث <code dir=\"ltr\">push</code> دون مرشّح للفروع. أما <code dir=\"ltr\">pull_request</code> فلا يُطلق إلا بنشاط الـ pull requests."}],"answer":1},{"q":"<p>يفشل الـ CI job عند <code dir=\"ltr\">npm ci</code> بخطأ مفاده أن <code dir=\"ltr\">package.json</code> غير موجود. الخطوة الأولى هي <code dir=\"ltr\">run: npm ci</code>. ما المشكلة؟</p>","options":[{"text":"الـ runner لا يحتوي npm مثبّتًا.","why":"الـ runners العاملة بـ Ubuntu في GitHub تتضمّن Node.js و npm. والخطأ يتعلّق بملف مفقود، لا بأداة مفقودة."},{"text":"<code dir=\"ltr\">npm ci</code> لا يعمل إلا على runners بنظام Windows.","why":"<code dir=\"ltr\">npm ci</code> يعمل على كل أنظمة التشغيل التي توفّرها الـ runners."},{"text":"ملف الـ workflow في المجلد الخطأ.","why":"لو كان الملف في المجلد الخطأ لما عمل الـ workflow أصلًا. لكنه عمل، وفشل عند إحدى الخطوات."},{"text":"لم ينسخ الـ job الـ repository قط؛ يجب أن تأتي <code dir=\"ltr\">actions/checkout</code> قبل أي خطوة تحتاج إلى ملفاتك.","why":"صحيح. يبدأ الـ runner آلةً فارغة. و <code dir=\"ltr\">actions/checkout</code> يضع ملفات الـ repository في مكانها."}],"answer":3},{"q":"<p>أضفت ruleset على <code dir=\"ltr\">main</code> يشترط pull request. ماذا يحدث حين تشغّل <code dir=\"ltr\">git push origin main</code> ولديك commit محلي؟</p>","options":[{"text":"يُرفض الرفع بخطأ مخالفة قاعدة في الـ repository؛ ويجب أن يصل الـ commit عبر pull request.","why":"صحيح. يرفض GitHub تحديث <code dir=\"ltr\">main</code>، ويبقى الـ commit المحلي دون أن يُمسّ، فتستطيع رفعه إلى فرع وفتح pull request."},{"text":"ينجح الرفع ويفتح GitHub pull request تلقائيًا.","why":"لا يحوّل GitHub عمليات الرفع إلى pull requests. القواعد إما تسمح بالتحديث أو ترفضه."},{"text":"ينجح الرفع لأن القواعد لا تنطبق إلا على الآخرين.","why":"القواعد تنطبق على الجميع، بمن فيهم المالك، ما لم تضف نفسك إلى قائمة الاستثناء (bypass list)."},{"text":"يُحذف الـ commit المحلي ليبقى متزامنًا مع <code dir=\"ltr\">main</code>.","why":"القواعد البعيدة لا تستطيع لمس الـ repository المحلي. لا شيء محلي يتغيّر حين يُرفض الرفع."}],"answer":0},{"q":"<p>أعدت تسمية الـ job في الـ workflow من <code dir=\"ltr\">check</code> إلى <code dir=\"ltr\">html</code>. والآن يعرض كل pull request الفحص المطلوب <code dir=\"ltr\">check</code> بالحالة “Expected — Waiting for status to be reported”، ولا شيء يمكن دمجه. لماذا؟</p>","options":[{"text":"الـ rulesets لا تستطيع اشتراط فحوص إلا من jobs اسمها <code dir=\"ltr\">check</code> أو <code dir=\"ltr\">test</code>.","why":"أي اسم job يصلح. المشكلة أن الـ ruleset والـ workflow صارا مختلفين في الاسم."},{"text":"في الـ workflow خطأ في صياغة YAML.","why":"الـ job المسمّى <code dir=\"ltr\">html</code> يعمل جيدًا؛ لكنه يبلّغ باسم لا ينتظره الـ ruleset."},{"text":"ما زال الـ ruleset يشترط فحصًا اسمه <code dir=\"ltr\">check</code>، ولم يعد أي job يبلّغ به؛ حدّث الفحص المطلوب إلى <code dir=\"ltr\">html</code>.","why":"صحيح. الفحوص المطلوبة تُطابق بالاسم. إن أعدت تسمية أحدهما، فحدّث الآخر في الوقت نفسه."}],"answer":2}],"exercise":{"kind":"spot-bug","language":"yaml","code":"name: CI\n\non:\n  pull_requests:\n    branches: [main]\n\njobs:\n  check:\n    runs-on: ubuntu-latest\n    steps:\n      - run: npm ci\n      - uses: actions/checkout@v7\n      - uses: actions/setup-node\n        with:\n          node-version: 24\n          cache: npm\n      - run: npm test","bugLines":[4,11,13],"title":"أصلح الـ CI workflow","prompt":"<p>كتب زميل ملف <code dir=\"ltr\">.github/workflows/ci.yml</code> ليشغّل فحص الـ HTML في الموقع الشخصي على كل pull request إلى <code dir=\"ltr\">main</code>. لكنه لا يظهر أبدًا على الـ pull requests، وحتى بعد إصلاح ذلك سيظل يفشل. حدّد الأسطر الثلاثة الخاطئة.</p>\n","hints":["أسماء الأحداث في <code dir=\"ltr\">on</code> يجب أن تكون دقيقة؛ قارنها باسم الحدث الذي رأيته في الدرس.","يبدأ الـ runner فارغًا. ما الذي يجب أن يحدث قبل أن يجد <code dir=\"ltr\">npm</code> الملف <code dir=\"ltr\">package.json</code>؟","كل <code dir=\"ltr\">uses:</code> يجب أن يحدّد أيّ إصدار من الـ action يُشغَّل."],"explanation":"<p>السطر 4: الحدث هو <code dir=\"ltr\">pull_request</code> (بالمفرد)؛ و <code dir=\"ltr\">pull_requests</code> ليس حدثًا يعرفه GitHub، فيصبح الـ workflow غير صالح ولا يعمل أبدًا على الـ pull requests. السطر 11: يعمل <code dir=\"ltr\">npm ci</code> قبل <code dir=\"ltr\">actions/checkout</code>، على runner فارغ لا يحتوي <code dir=\"ltr\">package.json</code>؛ انقله إلى ما بعد الـ checkout والـ setup-node. السطر 13: يجب أن يتضمّن <code dir=\"ltr\">uses</code> مرجعًا، مثل <code dir=\"ltr\">actions/setup-node@v7</code>؛ ودون <code dir=\"ltr\">@…</code> يفشل تحميل الـ workflow. وبعد الإصلاح تصبح الخطوات: checkout، ثم setup-node مع Node 24، ثم <code dir=\"ltr\">npm ci</code>، ثم <code dir=\"ltr\">npm test</code>.</p>\n","checkLabels":{}},"words":740,"readMinutes":4}