لماذا كان `serve` يعرض موقعًا قبيحًا بينما كان `publishSite` مثاليًا
Publié le 22 April 2026
هل تعلم تلك اللحظة التي يبدو فيها موقعك المحلي لا شيء، لكن بمجرد نشره على الإنترنت يصبح كل شيء مثاليًا؟ هذا ما حدث لي مع مكوّن Gradle JBake الإضافي. المهمة`serve`كان يعرض أزرارًا مربعة، نصًا داكنًا على خلفية سوداء، وCSS مفقودًا بشكل غريب. ومع ذلك`publishSite`كان يولد نفس الموقع، هو، بلا عيوب. لم يكن المذنب هو CSS ولا المتصفح ولا الكاش. كان سطرًا من كود Kotlin — وعادة سيئة مع الوسائط في سطر الأوامر.
- تشنج
-
[]
المشهد: أربع لقطات شاشة، عرضان
كان ذلك بعد تعطل النظام. كنت قد أخذت أربع لقطات شاشة لمقارنة العرض المحلي (./gradlew serve`على`localhost:8820) مع العرض على الإنترنت (`publishSite`على GitHub Pages). لقطتين لكل صفحة: الأعلى والاسفل.
العرض المحلي
أعلى الصفحة— الأزرار Project و Template هيصغير، مستطيل, بالألوان القياسية (الأزرق والأخضر). البطاقة Start Writing لهاخلفية سوداء موحدة, بدون إطار
أسفل الصفحة— العنوان الفرعي "مقالات وموارد حديثة" هوشبه غير قابل للقراءة, شديد الظلام على خلفية سوداء. التواريخ تحت صور المدونة ("17 October 2013"، إلخ) هيغائبات.
العرض عبر الإنترنت
أعلى الصفحة— نفس الأزرار هيكبير, ضأني, أخضر نيون زاهٍ, نص بحروف كبيرة. البطاقة لديهاإطار أبيض مستدير مع ظلّ.
تذييل الصفحة— العنوان الفرعي هوقابلة للقراءة جيدًا. التواريخ هيمرئيونبطاقات العناصر لديها بعضزوايا مستديرةوالعناوين هيعريض.
الخطوة الأولى: السمة. أستخدم نظام سمة ديناميكي (فاتح، غامق، عالي التباين) مستند إلى`localStorage`. ربما كان المتجر المحلي يُفعّل سمة تباين عالي ? لا. كنت قد كتبت بالفعلhttps://cheroliv.com/blog/2025/0098_preloading_css_variable_post.html[مقال كامل عن التحميل المسبق للمواضيع], أنا أعرف كيف يعمل ذلك. ثم، حتى في وضع high-contrast، كان من المفترض أن تكون بنية CSS (الأزرار البيضوية، الظلال) موجودة. لم تكن كذلك.
الانعكاس الثاني: ذاكرة التخزين المؤقت للمتصفح. لا، لقد أجريت الاختبار باستخدام ملفات تعريف نظيفة. استمر الاختلاف
الانعكاس الثالث:`serve`لم يكن يقدم نفس الملفات التي`publishSite`.
التشخيص: serve لم يكن يشير إلى المجلد الصحيح
ملحق Gradle الخاص بي`bakery`له مهامتان رئيسيتان:
-
bake: ينشئ الموقع الثابت في`build/bake/` -
serve: يشغل خادم ويب محلي -
publishSite: يُدفع`build/bake/`نحو GitHub Pages
لم يكن بإمكان التباين أن يأتي إلا من`serve`. Si publishSite`كان ينشر المحتوى الصحيح، وهذا هو`build/bake/`كان جيدًا. إذا`serve`كان يعرض شيئًا آخر، وهذا يعني أنه لم يكن مفيدًا`build/bake/.
لننظر إلى الكود. في`SiteManager.kt`, المهمة`serve`يُعرّف كما يلي :
tasks.register("serve", JavaExec::class.java) { task ->
task.apply {
mainClass.set("org.jbake.launcher.Main")
classpath = jbakeRuntime
environment("GEM_PATH", jbakeRuntime.asPath)
jvmArgs(/* ... */)
args = listOf(
"-b", file(site.bake.srcPath).absolutePath,
"-s", layout.buildDirectory.get()
.asFile.resolve(site.bake.destDirPath)
.absolutePath
)
}
}
org.jbake.launcher.Main`هو نقطة الدخول لواجهة سطر أوامر JBake 2.7.0. الفكرة : تمرير-b`للمجلد المصدر و`-s`للمجلد الوجهة، ثم تشغيل وضع الخادم. إلا أن…
السبب الجذري: JBake CLI لا يعمل بهذه الطريقة
JBake 2.7.0 ينتظرالوسائط الموضعيةلمصدر والوجه، ثم أعلام اختيارية. التوقيع هو:
jbake <source> <destination> [options]
أو في شفريتي، كنت أكتب :
args = listOf("-b", "/path/to/site", "-s", "/path/to/build/bake")
كان JBake يفسّر ذلك على أنه :
-
-b→ تحليل العلم "bake" (منطقي)، المستهلك -
/path/to/site→ يصبح الوسيط الموضعي 1 (المصدر) -
`-s`تم تحليل العلم "serve" (منطقي)، تم تشغيل الخادم.فوراً
-
/path/to/build/bake→ يصبح حجةً يتيماً، مهملاً أو مفسراً بشكل خاطئ
النتيجة: كان JBake يأخذ`/path/to/site`كمصدر،لم يأخذ في الاعتبار الوجهةمُزَوَّدة (build/bake) , كان يستخدم قائمته الافتراضية (غالبًا`./output`أو نسخة مؤقتة). الملف`css/styles.css`كان يتم سحق أو تجاهل الـ build، وبالتالي كان يتم تقديم CSS افتراضي قديم (بدون المتغيرات المخصصة، بدون التقوس، بدون الظلال).
بالمقارنة،bake et publishSite`يستخدمونالإضافة الرسمية ل Gradle JBake(`jbake-gradle-plugin) الذي يكتب بخط نظيف في`build/bake/`عبر API Gradle. لا يمرّون عبر واجهة سطر الأوامر.
الحل: الوسائط الموضعية، وليس أعلام
التصحيح بسيط — بمجرد أن تعرف ذلك. كل ما عليك هو تمرير المصدر والوجهة كوسائط موضعية، ثم`-s`أخيرا :
args = listOf(
file(site.bake.srcPath).absolutePath,
layout.buildDirectory.get()
.asFile.resolve(site.bake.destDirPath)
.absolutePath,
"-s"
)
هذا كل شيء. تم تعديل ثلاثة أسطر، تم حلّ الخطأ.
بعد النشر المحلي للإضافة (publishToMavenLocal), un `./gradlew serve`تشغيل JBake مع الصياغة الصحيحة :
jbake /home/user/project/site /home/user/project/build/bake -s
و هذه المرة، يخدم خادم Jetty المدمج في JBake المحتوى جيدًا من`build/bake/`— متطابق مع ما تم نشره على الإنترنت.
التحقق السريع باستخدام curl
لتأكد من أن الخادم يقدم المحتوى الصحيح، بسيط`curl`يؤكد أن`index.html`يحتوي`data-bs-theme="light"`وأن`build/bake/css/styles.css`يزن 31 402 بايت — متطابق مع الملف المولّد بواسطة`bake`.
$ ./gradlew serve
# Dans un autre terminal :
$ curl -s http://localhost:8820/ | grep data-bs-theme
<html ... data-bs-theme="light" ...>
$ curl -I http://localhost:8820/css/styles.css
HTTP/1.1 200 OK
Content-Length: 31402
Content-Type: text/css
العرض المحلي الآنمتطابق مع البكسلللتقديم المنفذ
لماذا كان هذا الخطأ شريرًا
هل ترى لماذا كان من الصعب الإمساك به؟
-
لا يوجد خطأ صريحJBake لم يتعطل. كان "يعمل" ببساطة مع مجلد خاطئ.
-
كان البناء يعمل:`./gradlew bake`كان يُنشئ الملفات بشكل صحيح في`build/bake/`.
-
كان النشر يعمل : `publishSite`كان يعزز المحتوى الجيد على الإنترنت
-
فقط
serveكان مكسورًا: مهمة التطوير، وهي التي نستخدمها بشكل مستمر للتكرار -
عادة
-key value: كمطور، نحن مشروطون بفعل سنوات من واجهة سطر أوامر يونكس (-o output`,-i input). JBake CLI استثناء — المصدر والوجهة موضعيتان.
الخاتمة
إذا كنت تُحافظ على إضافة Gradle التي تغلف JBake (أو أي أداة سطر أوامر),اقرأ وثيقة الحججحتى إذا كنت تعتقد أنك تعرفهم. فرضية ضمنية (-s = "set destination") يمكن أن يكلفك ساعات من تصحيح الأخطاء البصري.
هنا، كان الإصلاح حرفياً هو تغيير :
args = listOf("-b", src, "-s", dest) // ❌ BUG
en :
args = listOf(src, dest, "-s") // ✅ FIX
بعد نقل ثلاثة رموز، يصبح موقعي المحلي جميلًا مرة أخرى مثل موقع الإنتاج.
درس: عندما يختلف العرض بين المحلي والإنتاج دون سبب واضح، اشتبه أولاً في pipeline — وليس CSS، ولا المتصفح، وبالتأكيد ليس الإطار. غالبًا ما تكون الخطوة السابقة للعرض التي تغش.