مقدمة

الجمهور المستهدف: مطورو Gradle الذين واجهوا بالفعل سلوكيات بناء «سحرية» أو غير متوقعة.

في رحلتنا لإنشاء الإضافة`site-baker`, نحن اتبعنا نهج TDD صارمًا. كل وظيفة تم اختبارها، والتحقق منها، وتقدمنا بثقة. ثم، في يوم ما، وصل غير المتوقع. začal البناء يتصرف بشكل غير منتظم. بدت التعديلات في منطق البرنامج الإضافي أو في ملفات التكوين وكأنها تُتجاهل، واختباراتنا الوظيفية، التي كانت موثوقة سابقًا، فشلت دون سبب واضح.

قد يكون هذا النوع من المشكلة محبطًا بشكل لا يصدق. إنه يشكك في موثوقية الأداة وصحة كودنا. بعد جلسة تصحيح أخطاء مكثفة، تم تحديد الجاني:ذاكرة التخزين المؤقت لتكوين Gradle.

العرض: البنيات الشبحية

كانت المشكلة تتجلى بعدة طرق :

  1. كنت أقوم بتعديل سلسلة أحرف في مهمة`println`, لكن السلسلة القديمة كانت تستمر في الظهور أثناء التنفيذ.

  2. كنت أغير قيمة في ملفي`managed-jbake-context.yml`, لكن المكون الإضافي كان يتصرف كما لو أن الملف لم يتم تعديله.

  3. كانت الاختبارات الوظيفية التي تنشئ مشاريع اختبار على الفور تفشل لأن البرنامج المساعد لم يبدو أنه يكتشف ملفات التكوين التي تم إنشاؤها حديثًا.

كان كل شيء يسير كما لو أن Gradle كان ينفّذ "نسخة شبح" من بنائنا، متناسيًا تغييراتنا الأحدث.

الاستقصاء: ما هو ذاكرة التخزين المؤقت للتكوين؟

ذاكرة التخزين المؤقت للتكوين هي ميزة نسبيًا حديثة وقوية جدًا في Gradle، مفعلة افتراضيًا في الإصدارات الجديدة. هدفها هو جعل عمليات البناء أسرع.

مبدأ بسيط :
  1. عند أول تنفيذ، ينفذ Gradle مرحلةالتكوين(القراءة`build.gradle.kts`, إنشاء المهام، حل الاعتماديات) ويبني رسمًا بيانيًا للمهام.

  2. في نهاية هذه المرحلة، Gradleسلسلة هذا الرسم البياني للمهامويخزنها في الذاكرة المؤقتة

  3. عند التنفيذات التالية، إذا لم يتغير شيء (سكريبتات البناء,gradle.properties, إلخ), Gradleيتخطى تمامًا مرحلة التكوينويعيد استخدام رسم بياني للمهام المخزن مؤقتًا

توفير الوقت مذهل في المشاريع الكبيرة. ومع ذلك، فإن هذا الأداء يأتي بتكلفة: فهو يفرض قواعد صارمة على كيفية كتابة المكونات الإضافية.

config cache lifecycle
Figure 1. دورة حياة ذاكرة التخزين المؤقت للتكوين

السبب: إضافة غير متوافقة

إضافتنا`site-baker`كان يخالف، دون علمه، عدة قواعد لتخزين الإعدادات. حتى يكون رسم بياني للمهام قابلاً للتسلسل، لا يجب أن تحتوي المهام على مراجع إلى كائنات معقدة مثل الكائن`Project`أو قراءة الملفات بطريقة تعسفية أثناء مرحلة التنفيذ.

خطأنا الرئيسي كان قراءة محتوى ملف YAML مباشرةً داخل منطق تنفيذ المهمة، باستخدام إشارة إلى المسار المخزن في امتدادنا. هذه الطريقة غير متوافقة مع التخزين المؤقت لأن Gradle لا يمكنه معرفة ما إذا كان محتوى الملف قد تغير إذا لم تُصَمَّم هذه القراءة على أنهاإدخال المهمة(إدخال المهمة)

الحل المؤقت: إلغاء تنشيط الذاكرة المؤقتة

لإلغاء حظرنا واستعادة سلوك البناء المتوقّع، كان الحل الأسرع هو تعطيل ذاكرة التخزين المؤقت للتكوين. يكفي إضافة السطر التالي في الملف`gradle.properties`المشروع الذي يستخدم المكون الإضافي (أو في حالتنا، مشروع الاختبار)site-baker).

# site-baker/gradle.properties
org.gradle.configuration-cache=false

على الفور، استعادت عمليات البناء سلوكها الطبيعي. كانت كل عملية تنفيذ تعيد تشغيل مرحلة التكوين، وكانت تغييراتنا تؤخذ بالحسبان.

ومع ذلك، هذا حل بديل، وليس حلاً دائمًا. إنه يضحي بالأداء ولا يحل المشكلة الأساسية في مكوّننا.

الحل الحقيقي : جعل الإضافة متوافقة

لكي يكون البرنامج المساعد مواطنًا جيدًا في نظام Gradle الحديث، يجب أن يكون متوافقًا مع ذاكرة التخزين المؤقت للتكوين. وهذا يعني إعادة التفكير في كيفية تدفق البيانات إلى مهامنا.

المفتاح هو استخدام الـواجهات برمجة التطبيقات للمزودمن Gradle. بدلاً من تمرير القيم المباشرة (مثل`String` ou un File) إلى مهامنا، يجب علينا المرور عبر بعض`Property<T>`أو بعض`Provider<T>`.

هنا خطة إعادة هيكلة :

  1. إعلان إدخالات المهام :المهمة التي تحلل ملف YAML يجب أن تعلن هذا الملف كمدخل. يُستخدم لذلك التعليق`@InputFile`.

[source,kotlin] ---- @get:InputFile مجرد val configFile: RegularFileProperty ----


Articles connexes