تخطَّ إلى المحتوى

تعبئة مشروع من مستودع GitHub

وجِّه وكيلًا إلى مستودع على GitHub تسترجع لوحة جاهزة للعمل: كل مشكلة (issue) قصةً، في الحالة التي يفرضها تاريخها، ومعها قوائم التحقق والوسوم والمعالم. ثم يلتقط الوكيل نفسه قصةً، ويطالب بها، ويسوقها عبر آلة الحالات، ويربط طلب السحب الذي فتحه.

هذه الصفحة تعرض تلك الحلقة من طرفها إلى طرفها. لخطوة التعبئة مساران: GitHub-to-EAT، أداة الاستيراد مفتوحة المصدر من East Agile، تنجزها بأمر واحد (الخطوة 3)؛ وواجهة الاستيراد البرمجية تؤدي العمل نفسه نداءً بنداء (الخطوتان 4 و5)، وهي ما يقوده الوكيل حين يريد مقبض المهمة. وكل ما يلي ذلك يجري على الواجهة البرمجية، لأن المقصد أن ينجز الوكيل البقية بلا إشراف.

هذا ليس «استيراد ذكاء اصطناعي» منفصلًا. فخطوة التعبئة هي أداة استيراد GitHub نفسها التي تشغّلها يدويًا من إعدادات المشروع ← استيراد / تصدير، الموصوفة في تعليمات التشغيل ← الاستيراد من أدوات تتبع أخرى. والوكيل ينادي النقطة الطرفية نفسها التي كنت ستناديها. أما ما تضيفه هذه الصفحة فهو كل ما يحيط بها: من يمسك المفتاح، وكيف تتحقق من الاستيراد قبل أن يكتب، وماذا يفعل الوكيل باللوحة بعد أن تصير موجودة.

مصدر GitHub في تبويب Import / Export: المالك والمستودع مُدخلان، والرمز فارغ، وطلبات السحب والمراحل محددة

  • مشروع — وجلسة أو مفتاح ea_user_… لإنشائه.
  • مفتاح وكيل — مفتاح ea_agent_… محصور في ذلك المشروع. والدور الذي يحتاجه يتوقف على قدر ما تريد أن يجريه الوكيل من الحلقة؛ انظر الخطوة 2. وانظر أيضًا دليل واجهة برمجة التطبيقات ← نوعان من المفاتيح.
  • رمز وصول شخصي من GitHub — بصلاحية قراءة مشكلات المستودع. وكل استيراد يستوثق، لأن الجلب يجري على واجهة GraphQL من GitHub، وGraphQL يرفض طلبًا بلا رمز. ولا يجوز إغفاله إلا حين يجلب Tracker نيابةً عنك: مستودع عام، على نشر لديه رمز احتياطي مشترك (الخدمة المستضافة eastagiletracker.com لديها واحد؛ أما التثبيت الذاتي فلا يملك أيًّا منه حتى يضبط مشغّله GITHUB_IMPORT_PAT)، وليس مع --engine direct في GitHub-to-EAT. انظر الرموز وحدود المعدل.
  • Node.js 22+ — لمسار GitHub-to-EAT في الخطوة 3 فقط. أما مسار الواجهة البرمجية فلا يحتاج غير curl.

يجب أن يوجد المشروع قبل مفتاح الوكيل، ويجب أن ينشئه شخص: فمفاتيح الوكلاء تُربَط بمشروع واحد لحظة سكّها ولا تستطيع إنشاء مشاريع. أنشئه من الواجهة، أو بمفتاح ea_user_… الخاص بك:

Terminal window
curl -X POST https://eastagiletracker.com/api/v1/projects \
-H "X-TrackerToken: $TRACKER_USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "hello-world", "iteration_length_weeks": 1}'

يحمل الرد المعرّف project_id الذي يحتاجه كل نداء أدناه.

يُنشئ مالك المشروع مفاتيح الوكلاء في إعدادات المشروع ← الوكلاء. والدور الذي تختاره يقرّر كم من هذه الصفحة يستطيع الوكيل إنجازه وحده، وثمة جوابان معقولان:

  • owner — مفتاح واحد يدير الحلقة كلها، والاستيراد ضمنها. والاستيراد للمالك وحده، لأن الاستيراد يعيد كتابة شكل المشروع جملةً واحدة. وسكّ وكيل بدور owner يقتضي أن تكون أنت مالك المشروع: فدور الوكيل لا يتجاوز دور من أنشأه أبدًا.
  • member — أدنى امتياز. يطالب الوكيل بالقصص، ويحرّكها، ويعلّق، ويربط طلبات السحب، لكنه لا يستورد. أما الاستيراد فتجريه أنت بنفسك (الخطوة 5) بمفتاحك، ثم تسلّم اللوحة إلى الوكيل.

وفي الحالتين لا تتركه على القيمة الافتراضية. فالمفتاح الجديد للوكيل هو viewer ما لم تقل غير ذلك، والـ viewer يقرأ اللوحة لكنه لا يطالب بقصة ولا يحرّكها — وذلك معظم هذه الحلقة.

ومفاتيح الوكلاء مهمة هنا لسبب يتجاوز الوصول. فمفتاح الوكيل يتصرف بوصفه مشاركًا مسمّى في مشروع واحد، فكل قصة ينشئها، وكل تغيير حالة يجريه، وكل تعليق يكتبه يُنسَب إلى ذلك الوكيل في السجل — متميزًا عن عملك أنت لا ممتزجًا به.

Terminal window
export TRACKER_TOKEN="ea_agent_xxxxx"

اجعل الوكيل يقرأ /meta قبل كل شيء:

Terminal window
curl https://eastagiletracker.com/api/v1/meta \
-H "X-TrackerToken: $TRACKER_TOKEN"

فذلك يجيب عن سؤالين كان الوكيل سيخمّنهما وإلا: بأي مشروع ارتبط المفتاح (auth.project_id)، وأي تنقّلات الحالة مشروعة لكل نوع قصة (transitions). فالـ feature تسير unstarted → started → finished → delivered → accepted؛ أما الـ chore فليست إلا unstarted → started → accepted. وقراءة الخريطة خير من تثبيتها في الشيفرة.

GitHub-to-EAT هي أداة الاستيراد مفتوحة المصدر الخاصة بـ East Agile: أداة سطر أوامر برخصة MIT تنجز خطوة التعبئة كاملة — الخطوتين 4 و5 أدناه — بأمر واحد. الجأ إليها حين يجلس شخص أمام طرفية. والجأ إلى الواجهة البرمجية تحتها حين يقود وكيل بلا إشراف ويريد مقبض المهمة ليستعلم عنه.

تحتاج إلى Node.js 22+ وليست لها اعتماديات تشغيل خاصة بها. ولم تُنشَر بعد على npm، فثبّتها من المستودع:

Terminal window
git clone git@github.com:EastAgile/GitHub-to-EAT.git
cd GitHub-to-EAT
npm install --global .

ثم وجّهها إلى المفتاح الذي سككته في الخطوة 2 والمشروع الذي أنشأته في الخطوة 1:

Terminal window
export EAT_AGENT_KEY="ea_agent_xxxxx"
github-to-eat --project $PROJECT_ID --repo octocat/hello-world

تطبع أولًا مفتاح شرح للمطابقة — كيف سيحطّ كل نوع مختار بالضبط — وتطلب تأكيدًا قبل أن تكتب شيئًا. وخارج الطرفية، في أنبوب أو في CI أو داخل وكيل، لا مكان لعرض ذلك السؤال، فالتشغيل الذي سيكتب عليه أن يمرّر --yes؛ وبدونه تخرج الأداة بالرمز 2 ولا تكتب شيئًا بدل أن تخمّن جوابك. وإعادة التشغيل آمنة: فما استُورِد سابقًا يُتخطّى ولا يُكرَّر أبدًا.

العَلَمما يفعله
--dry-runفحص مسبق، ثم طباعة الخطة التي كان سينفّذها — كم قصة سيستورد، وكم سيتخطّى لوجودها — ولا يكتب شيئًا. ولا يحتاج --yes.
--includeأي الأنواع تُستورَد، مفصولةً بفواصل: issues,prs,milestones,releases,deps. والافتراضي issues، وعلى كل اختيار أن يحتويه. وهي الخيارات نفسها التي في جدول الخطوة 6.
--tokenرمز وصولك الشخصي على GitHub (ويُحتسَب كذلك GITHUB_TOKEN في البيئة أو في ملف .env). ويحتاج إلى repo، أو إلى الصلاحية الدقيقة Issues: Read، على ذلك المستودع. وهو مطلوب لمستودع خاص، ولخادم بلا رمز مشترك احتياطي، ودائمًا لـ --engine direct. وإن أغفلته على المحرك الافتراضي للخدمة المستضافة أنفق Tracker ميزانيته المشتركة — انظر الرموز وحدود المعدل.
--engineserver، وهو الافتراضي، يرسل نداءً واحدًا إلى /import/json ويترك الجلب والمطابقة والكتابة لـ Tracker. أما direct فيشغّل الأنبوب نفسه على جهازك ويكتب عبر الواجهة البرمجية العامة — أي أنه يقرأ GitHub بنفسه فيحتاج رمزًا دائمًا، وإلا خرج بالرمز 2.
--states، --milestones، --story-type، --no-comments، --no-tasksتضيّق المطابقة أو تتجاوزها لتشغيلة واحدة؛ ولا يُحفَظ شيء. وكل واحد منها يستلزم --engine direct.

اضبط EAT_API_BASE وEAT_APP_BASE لتوجيهها إلى Tracker مستضاف ذاتيًا أو محلي؛ وكلاهما يشير افتراضيًا إلى الخدمة المستضافة. ويحمل README مرجع الأعلام الكامل ورموز الخروج ومعالجة الأعطال.

وكل ما أدناه هو الاستيراد نفسه مقودًا نداءً بنداء، وهو ما تريده حين يشغّله وكيل.

4. جرّب على الفارغ أولًا

Section titled “4. جرّب على الفارغ أولًا”

الاستيراد للمالك وحده — استخدم مفتاح وكيل بدور owner، أو مفتاحك أنت إن تركت الوكيل عند member. شغّله أولًا بـ dry_run، قبل أن تدعه يكتب شيئًا:

Terminal window
curl -X POST https://eastagiletracker.com/api/v1/projects/$PROJECT_ID/import/json \
-H "X-TrackerToken: $TRACKER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"source": "github",
"owner": "octocat",
"repo": "hello-world",
"include_pull_requests": true,
"include_milestones": true,
"dry_run": true
}'

التشغيل على الفارغ يجلب من GitHub، ويحسم، ويزيل التكرار تمامًا كالتشغيل الحقيقي، ويبلّغ الأعداد نفسها — imported وskipped وerrors وunmatched — ثم يتراجع عن المعاملة كلها. فلا يبقى شيء، ولا يبلغ سجلَّ تدقيقك أي حدث استيراد مكتمل. وهو أرخص سبيل لتكتشف أنك كنت تقصد ضم المعالم، أو أن المستودع أكبر مما ظننت، ما دام ذلك لا يكلّفك بعدُ شيئًا.

كل استدعاء لـ /import/json غير متزامن، بما في ذلك التشغيل على الفارغ: تُعيد النقطة الطرفية 202 ومعها مقبض مهمة، لا نتيجة، وتصل الأعداد على المهمة حين تستطلعها (الخطوة 5). وتبلغ مهمة التشغيل على الفارغ الحالة done كالمهمة الحقيقية؛ والفرق أنه لم يُكتب شيء.

احذف dry_run وأعد الإرسال. وكما في السابق، تُعيد النقطة الطرفية 202 ومعها مقبض مهمة:

{ "import_id": "…", "status": "pending" }

استعلم عن المهمة حتى تبلغ حالة نهائية:

Terminal window
curl https://eastagiletracker.com/api/v1/projects/$PROJECT_ID/imports/$IMPORT_ID \
-H "X-TrackerToken: $TRACKER_TOKEN"

تسير الحالة pending → fetching → writing → done | failed. والنهائيتان هما الأخيرتان فقط: done تحمل أعداد النتيجة، وfailed تحمل رسالة خطأ ورمزًا آليًا ثابتًا يمكنك التفرّع عليه. وبينما يقسّم الجلب الصفحات، يخبرك progress_current وprogress_total بالصفحة التي بلغها — وهو جدير بالعرض إن كان أحد يراقب.

الرموز. مرّر "token": "github_pat_…". وإن أغفلته وضع الخادم مكانه رمز المنصّة المشترك، وهو لا يقرأ إلا المستودعات العامة وتُحتسَب حصته على كل مستدعٍ في النشر — والرموز وحدود المعدل تشرح ما يكلّفك ذلك. وأيًّا كان الرمز المستخدم، فهو يقود نداءات GitHub الصاعدة لا غير: لا يُسجَّل قط، ولا يدخل سجل التدقيق، ولا يُخزَّن، ولا يُعاد صداه في رد أو في خطأ.

إعادة التشغيل آمنة. فالصف الذي استُورِد سابقًا يُطابَق بمعرّف مصدره ويُتخطّى لا يُكرَّر. والاستيراد الثاني يكمّل اللوحة بما ظهر بعد الأول.

6. ما الذي يحطّ على اللوحة

Section titled “6. ما الذي يحطّ على اللوحة”

تُستورَد المشكلات افتراضيًا. وكل ما عداها اختياري، بعَلَم واحد لكل نوع:

من GitHubيصيرالعَلَم
مشكلة (issue)قصة. مفتوحة ← unstarted في Backlog. مغلقة ← accepted، أو rejected حين يذكر GitHub أنها أُغلقت بوصفها not_planned أو duplicate (فتحمل القصة عندئذٍ وسمًا مطابقًا).افتراضي
قائمة تحقق في متن المشكلةمهام — كل سطر - [ ] / - [x] يصير مهمة واحدة بترتيب المتن، ويصل [x] مكتملًا. وتبقى قائمة التحقق في الوصف أيضًا.افتراضي
الوسوموسوم، تُنقَل كما هي.افتراضي
طلب سحبقصة موسومة بـ pull-request. مفتوح ← started، مدموج ← accepted، مغلق بلا دمج ← rejected.include_pull_requests
معلَمملحمة باسم المعلَم، منزوعة التكرار بالعنوان — فمشكلتان تتشاركان معلَمًا تحطّان في ملحمة واحدة. وإن أُطفئ العَلَم رافقها بدلًا من ذلك وسمُ milestone:<العنوان>.include_milestones
إصدارقصة إصدار. منشور ← accepted، مسودة ← unstarted.include_releases
تبعية بين مشكلتينعائق على القصة. للمشكلات وحدها، ولطلبات السحب أبدًا.include_dependencies

يُستنتَج نوع القصة حين لا تذكره المشكلة. فوسم يحوي bug أو fix أو defect — أو عنوان يبدأ بـ fix أو bug — يجعلها علة؛ وchore أو maintenance أو devops أو infra تجعلها chore؛ وما عدا ذلك feature. ومعرفة هذا قبل الاستيراد مفيدة، لأن في East Agile Tracker لا تحمل النقاط إلا الـ features ولا تغذّي السرعة إلا الـ features. انظر مقدمة ← القصص.

لوحة المشروع بعد استيراد المستودع التجريبي مباشرة: المشكلات قصص بتسمياتها، والمراحل ملاحم، وأشخاص GitHub مالكون

صار للوحة تاريخ، وصار للوكيل مفتاح. والحلقة من هنا أربعة نداءات.

اعثر على قصة، أو اكتب واحدة. رشّح اللوحة بحثًا عما يُلتقَط:

Terminal window
curl "https://eastagiletracker.com/api/v1/projects/$PROJECT_ID/stories?state=unstarted" \
-H "X-TrackerToken: $TRACKER_TOKEN"

ويضيّق import_source=github النتيجة إلى ما جلبه الاستيراد. وإن وجد الوكيل عملًا لم يلتقطه المستودع قط، أنشأ القصة بنفسه — انظر دليل واجهة برمجة التطبيقات ← إنشاء قصة.

طالِب بها. يضيف الوكيل نفسه مالكًا بإرسال جسم فارغ:

Terminal window
curl -X POST https://eastagiletracker.com/api/v1/projects/$PROJECT_ID/stories/$STORY_ID/owners \
-H "X-TrackerToken: $TRACKER_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'

والجسم الفارغ يعني المستدعي، فلا يحتاج الوكيل إلى معرفة معرّفه. وتعرض اللوحة الآن الوكيل مالكًا، وبذلك يعلم من يراقب أن العمل قد أُخِذ.

ابدأها.

Terminal window
curl -X POST https://eastagiletracker.com/api/v1/projects/$PROJECT_ID/stories/$STORY_ID/transitions \
-H "X-TrackerToken: $TRACKER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"to": "started"}'

ثم يمضي الوكيل ويؤدي العمل — يقرأ المستودع، ويكتب الشيفرة، ويفتح طلب السحب. وذلك الجزء يجري في أداة البرمجة عندك، لا هنا.

أرفِق طلب السحب.

Terminal window
curl -X POST https://eastagiletracker.com/api/v1/projects/$PROJECT_ID/stories/$STORY_ID/links \
-H "X-TrackerToken: $TRACKER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"url": "https://github.com/octocat/hello-world/pull/42"}'

ويُتعرَّف على رابط طلب سحب GitHub بوصفه كذلك — ولا يلزمك التصريح به. فصارت القصة والشيفرة التي تغلقها على بعد نقرة واحدة في الاتجاهين.

أنهِها. انتقل إلى finished وقف عندها. فأمام الـ feature بعدُ delivered وaccepted، وهما بوابتا المراجعة: يقرّر سواك من الوكيل أن العمل صحيح. أما الـ chore فلا بوابة لها — وstarted → accepted هو كل ما بقي من طريقها.

مشكلة مغلقة مستوردة يربط قسم CODE فيها طلب السحب الذي أصلحها، وبجانبها تعليقات GitHub

كل استيراد يستوثق. فجلب المشكلات والتعليقات وطلبات السحب يجري على واجهة GraphQL من GitHub، وGraphQL يرفض طلبًا بلا رمز — ولا طبقة مجهولة، لا على مستودع عام ولا على خاص. والسؤال ليس أبدًا أيصل رمز إلى GitHub، بل رمز مَن فحسب.

مرّر token في نداء الاستيراد، أو --token إلى GitHub-to-EAT. ويكفي رمز وصول شخصي دقيق الصلاحيات له حق قراءة مشكلات المستودع. وهو يقود نداءات GitHub الصاعدة لا غير: لا يُسجَّل قط، ولا يدخل سجل التدقيق، ولا يُخزَّن، ولا يُعاد صداه في رد أو في خطأ.

وأحضِر رمزك لكل ما تجاوز العرض التوضيحي. فتنفق عندئذٍ ميزانية لا يمسّها سواك، ولا يستطيع أي فحص مسبق أن يرفضك بسبب استيراد غيرك.

و--engine direct لا يترك لك خيارًا. فذلك المحرك يقرأ GitHub من جهازك لا عبر Tracker، فرمز الخادم بعيد المنال؛ والتشغيل بلا رمز يخرج بالرمز 2 مع خطأ استعمال قبل أن يجلب أو يكتب شيئًا. ويُحتسَب GITHUB_TOKEN في بيئتك أو في ملف .env عندك كما يُحتسَب --token.

وما يفرض الرمز هو مسار المشكلات، لا المحرّك بأكمله. فـ direct يقرأ المشكلات والتعليقات وطلبات السحب عبر GraphQL، الذي لا وضع مجهول فيه؛ ولا يمسّ REST إلا لقائمة الإصدارات وفحص /rate_limit المجاني. ولا يزال الأداة تشحن جالبًا قديمًا مجهولًا عبر REST كان يشغّل استيراد مستودع عام داخل ميزانية الـ 60 في الساعة، لكن لم يعد أي مسار في سطر الأوامر يصل إليه وهو مرشّح للحذف، فاعتبر --token مطلوبًا لـ direct.

لا ترسل أي token فيضع الخادم مكانه رمز المنصّة الذي ضبطه مشغّله (GITHUB_IMPORT_PAT). وترافقه ثلاثة حدود:

  • إنه ضبط اختياري. الخدمة المستضافة eastagiletracker.com توفّر واحدًا، فيعمل عليها استيراد مستودع عام بلا رمز. أما التثبيت الذاتي — الملف التنفيذي المُنزَّل — فلا يملك أيًّا منه حتى يضبط مشغّله GITHUB_IMPORT_PAT في البيئة، وحتى ذلك الحين يرفض كل استيراد بلا رمز بالرمز 400 import_github_no_token.
  • لا يقرأ إلا المستودعات العامة. فالخدمة المستضافة تسكّه للقراءة فقط على المستودعات العامة، فالمستودع الخاص يتطلب رمزك أنت دائمًا.
  • يتشارك كل مستدعٍ في النشر ميزانية واحدة. فقبل تشغيل استيراد بلا رمز، يقرأ الخادم ما تبقّى من نقاط GraphQL للرمز المشترك ويرفض بالرمز 400 import_github_shared_quota_low دون 500. والميزانية التي تنفد في منتصف الاستيراد تُفشِل المهمة بالرمز import_github_rate_limited_platform. وكلتا الرسالتين تسمّيان العلاج نفسه: هات رمزك أنت.

يقيس GitHub واجهتيه على حدة، والسقف غير المستوثق أدنى بمرتبتين من حيث الحجم.

واجهة GitHubتُستعمل لـمع رمزبلا رمز
GraphQLالمشكلات والتعليقات وطلبات السحب والمشكلات الفرعية والتبعيات5٬000 نقطة في الساعة، تُحسَب على العُقَد التي يعيدها الاستعلاممرفوض — لا طبقة مجهولة في GraphQL
RESTالإصدارات (include_releases)، والفحص المسبق /rate_limit5٬000 طلب في الساعة60 طلبًا في الساعة، تُحصى لكل عنوان IP وتُتقاسَم مع كل من خلفه

ولا يهبط استيراد قط إلى تلك الطبقة ذات الستين في الساعة: فما دام لا رمز يُرسَل، يُرفَض الطلب مقدّمًا بدل أن يُعاد مجهولًا. والعدد مهم لما تفعله حول الاستيراد — فبرنامج نصي يقرأ GitHub مباشرة، أو صدفة في الشبكة نفسها مع عملاء آخرين، يستنفد 60 طلبًا في ثوانٍ.

واقرأ ما تبقّى من ميزانيتك متى شئت؛ فـ GET /rate_limit معفى من الحدّين، فلا يكلّفك الفحص شيئًا:

Terminal window
curl -H "Authorization: Bearer $GITHUB_TOKEN" https://api.github.com/rate_limit

ونقاط GraphQL ليست طلبات. فـ GitHub يقيس الاستعلام بالعُقَد التي يعيدها، فصفحة واحدة فيها 100 مشكلة مع تعليقاتها ومكلَّفيها تكلّف نقاطًا كثيرة، والمستودع الكبير ينفق ميزانية الساعة في نداءات أقل بكثير مما توحي به أرقام عصر REST. و--dry-run (الخطوة 3) وdry_run (الخطوة 4) يكلّف كلٌّ منهما من النقاط ما يكلّفه الجلب الحقيقي — وهذا ما يجعل أعدادهما جديرة بالثقة — فاحسب حساب جولتين حين تفحص استيرادًا كبيرًا مسبقًا.

  • دليل واجهة برمجة التطبيقات — نحو البحث، وتيار الأحداث، والتنقّلات الجماعية، والكتابات متكافئة الأثر، وسائر السطح.
  • تعليمات التشغيل — العمليات نفسها من الواجهة، وأدوات الاستيراد العشر الأخرى.
  • مقدمة — لِمَ جاءت آلة الحالات وأنواع القصص الأربعة على هذا الشكل.
  • GitHub-to-EAT — مستودع أداة الاستيراد نفسها: كل عَلَم، وكلا المحركين، وكيف تسهم فيها.