تخطي إلى المحتوى

التطبيقات والمكتبات المشتركة في مستودعات منفصلة

هذه الصفحة مترجمة من الإنجليزية. اقرأ الصفحة الإنجليزية

يعمل Zeyn في مستودع واحد، لكن فرقاً كثيرة تحتفظ بكل تطبيق في مستودع خاص به وتضع الشيفرة المشتركة (الكيانات والخدمات وميزات كاملة) في مكتبات تستخدمها عدة تطبيقات. يوضح هذا الدليل كيفية إعداد ذلك، والقاعدة الوحيدة التي تُبقيه يعمل.

ما الذي يوضع أين

المستودعيُنشأ بـيحتوي
تطبيقzeyn new billingmain.rs و app.rs وميزات التطبيق والإعدادات وكتالوجات التطبيق
مكتبة مشتركةzeyn new acme-domain --libالكيانات والخدمات والميزات وكتالوجاتها وCATALOGS

ميزة المكتبة هي نفسها ميزة التطبيق: النماذج والـ actions والشاشات والصلاحيات والترجمات تعيش معاً في مجلد واحد. والتطبيق هو من يقرر أي ميزات المكتبة يركّبها.

let i18n = I18n::new("en")
    .catalogs(acme_domain::CATALOGS)?
    .catalog("en", include_str!("../locales/en.json"))?
    .catalog("tr", include_str!("../locales/tr.json"))?;

App::new(t!("app.name"))
    .i18n(i18n)
    .service(acme_domain::customers::CustomerStore::default())
    .feature(acme_domain::customers::feature())

تُحمَّل كتالوجات المكتبة أولاً، فيستطيع التطبيق تجاوز نص في المكتبة بتعريف المفتاح نفسه. ابدأ مفاتيح المكتبة باسم الميزة (customers.list) كي لا تتصادم مكتبتان.

examples/shared-domain مكتبة من هذا النوع. يركّب كل من examples/billing و examples/crm ميزة customers فيها؛ ويعيد crm تسمية "Customers" إلى "Accounts" في كتالوجه.

ما الذي يجب أن تملكه المكتبة

تنص قاعدة اليتيم (orphan rule) في Rust على أنه لا يمكن تنفيذ سمة (trait) إلا في الحزمة التي تعرّف السمة أو النوع. لا يستطيع التطبيق تنفيذ zeyn::Model أو zeyn::Entity لنوع قادم من مكتبة، لذلك:

  • ضع #[entity] و #[model] و #[choice] على النوع في المكتبة، بجانب قواعده.
  • أبقِ قواعد التحقق (#[rule(...)]) على النوع المشترك. عندها يتحقق كل تطبيق بالطريقة نفسها.
  • التطبيق الذي يحتاج سلوكاً إضافياً يغلّف النوع (struct Invoice { customer: Customer, ... }) أو يضيف سمته الخاصة. ولا يعدّل نوع المكتبة.

كيف تحصل التطبيقات على المكتبة

الخيارالمزاياما يجب الانتباه إليه
اعتماد Git مع وسم (tag)لا شيء لتشغيله، يعمل مع أي خادم Gitينزّل Cargo المستودع كاملاً؛ وكل تطبيق يذكر العنوان
سجل Cargo خاص (Kellnr وسجل حزم GitLab وغيرهما)اعتمادات عادية بالشكل version = "0.3" وحل للإصدارات وفق semverخدمة تُشغَّل وتُنسخ احتياطياً وتُؤمَّن
مستودع workspace واحد لكل شيءبناء واحد وCargo.lock واحد وإعادة هيكلة في commit واحدكل فريق يرى كل تطبيق؛ والتحكم بالوصول لكل مستودع لا لكل مجلد

ابدأ بوسوم Git. وانتقل إلى سجل عندما يكون لديك أكثر من حفنة من المكتبات أو عندما يصبح حل الإصدارات يدوياً مملاً. شيفرة التطبيق لا تتغير؛ تتغير أسطر Cargo.toml فقط.

[dependencies]
zeyn = { git = "https://git.example.com/platform/zeyn.git", tag = "v0.0.2" }
acme-domain = { git = "https://git.example.com/shared/acme-domain.git", tag = "v0.3.0" }

يكتب zeyn new السطر zeyn = "<version>" افتراضياً. إلى أن يُنشر Zeyn، غيّر ذلك السطر إلى صيغة Git أعلاه في كل تطبيق ومكتبة جديدين.

القاعدة: إصدار Zeyn واحد في كل مكان

يجب أن تعتمد كل مكتبة وكل تطبيق على Zeyn نفسه تماماً. ومع اعتمادات Git يعني ذلك العنوان نفسه والوسم نفسه.

يعامل Cargo مصدرين مختلفين لـ Zeyn على أنهما حزمتان مختلفتان. المكتبة المبنية على الوسم v0.0.2 تعيد zeyn::Feature لا يستطيع تطبيق مبني على v0.0.3 قبوله، ويبلّغ المصرّف عن عدم تطابق نوعين يحملان الاسم نفسه. السجل يخفف ذلك إلى "الإصدار نفسه المتوافق وفق semver"، لكن القاعدة تبقى: Zeyn واحد لكل بناء.

لترقية Zeyn:

  1. حدّث الوسم في كل مكتبة، وشغّل اختباراتها، وأصدر وسماً جديداً للمكتبة.
  2. حدّث Zeyn ووسوم المكتبات في كل تطبيق معاً، في commit واحد.

يعرض cargo tree -d -i zeyn التكرارات. وإذا طبع أكثر من Zeyn واحد، فهناك اعتماد ما زال يشير إلى وسم قديم.

خطوة بخطوة

  1. أنشئ المكتبة وادفعها إلى مستودعها الخاص.
   zeyn new acme-domain --lib
   cd acme-domain
   cargo test
   git init && git add . && git commit -m "First version"
   git tag v0.1.0 && git push origin main --tags
  1. أضفها إلى Cargo.toml الخاص بالتطبيق مع الوسم، وركّب كتالوجاتها وخدماتها وميزاتها كما هو موضح أعلاه.
  2. اسمح لـ CI الخاص بالتطبيق بقراءة المكتبة. يعيد الملف المولَّد .gitlab-ci.yml كتابة العناوين https://<gitlab host>/ و ssh://git@<gitlab host>/ لتستخدم رمز المهمة (job token). في GitLab، افتح مشروع المكتبة، ثم إعدادات CI/CD، ثم Job token permissions، وأضف مشروع التطبيق إلى القائمة المسموح بها.
  3. شغّل app.check() في اختبار للتطبيق (وهذا ما يفعله tests/app.rs المولَّد). يفشل عندما تكون في ميزة مركّبة مشاكل بنيوية، مثلاً مفتاح ترجمة تفتقده إحدى اللغات.

العمل على مكتبة وتطبيق في وقت واحد

وجّه التطبيق إلى نسختك المحلية دون تعديل Cargo.toml. ضع هذا في .cargo/config.toml الخاص بالتطبيق وأبقِ ذلك الملف خارج Git:

[patch."https://git.example.com/shared/acme-domain.git"]
acme-domain = { path = "../acme-domain" }

أزله، ثم ضع وسماً للمكتبة وارفع الوسم في التطبيق عندما ينتهي التغيير.

إصدار مكتبة

  1. ارفع version في Cargo.toml.
  2. شغّل cargo semver-checks إن كان مثبّتاً. يبلّغ عن تغييرات الواجهة العامة (public API) التي تكسر المستدعين. التغيير الكاسر يحتاج إصداراً ثانوياً (minor) جديداً ما دامت المكتبة دون 1.0.
  3. نفّذ commit، ثم ضع الوسم v<version> وادفعه. تفشل مهمة CI المسماة tag-matches-version عندما يختلف الوسم عن Cargo.toml.