مواد پر جائیں

پائپ لائنز اور آٹومیشن

پائپ لائنز Control Center کا وہ نصف ہیں جو طے شدہ ہے۔ ایجنٹ فیصلہ کرتا ہے کہ کیا کہنا ہے؛ پائپ لائن فیصلہ کرتی ہے کہ آگے کیا ہوگا — ہر بار بالکل اسی طرح۔ پائپ لائن قدموں کا ایک DAG ہے جو ایجنٹس ڈسپیچ کر سکتا ہے، اسکرپٹس چلا سکتا ہے، کسی شرط پر شاخ بنا سکتا ہے، کسی مجموعے پر فین آؤٹ کر سکتا ہے اور قدموں کے درمیان ڈیٹا منتقل کر سکتا ہے — محفوظ، دوبارہ شروع ہونے کے قابل اور لاگت کے حساب سے۔

یہ تین وجوہ سے موجود ہیں: کسی دہرائے جانے والے کئی قدمی عمل کو زنجیر بنانے کے لیے، کئی ایجنٹس کو ترتیب وار یا متوازی طور پر منظم کرنے کے لیے، اور کسی ایسی چیز پر ردِ عمل دینے کے لیے جو کوئی انسان موجود نہ ہونے کے باوجود رونما ہوئی ہو۔

ایک ٹیمپلیٹ (PipelineDefinition) اعلانیہ DAG ہے: قدم، اعلان شدہ اِن پٹس اور کنفیگریشن۔ یہ ورک اسپیس کے اپنے ڈیٹا بیس میں رہتا ہے۔ ایک رن (PipelineRun) اُس ٹیمپلیٹ کا ایک اجرا ہے، جس کے ساتھ بدلنے والا اسٹیٹ بیگ، لاگت اور ٹوکن کے مجموعے اور فی قدم اسٹیٹس ہوتے ہیں۔

ایک ٹیمپلیٹ کے کئی رنز ہوتے ہیں۔ رنز ری اسٹارٹ سے بچ جاتے ہیں: انجن جاری رنز دوبارہ لوڈ کرتا ہے اور اُنہیں وہیں سے اٹھاتا ہے جہاں وہ رکے تھے۔

دو حفاظتی انتظامات جان بوجھ کر کسی قدم کو دوبارہ شروع کرنے کے بجائے پورا رن ناکام کر دیتے ہیں، کیونکہ دوبارہ شروع کرنا رکنے سے بدتر ہوگا:

  • جو قدم suspendedStepTimeout (ڈیفالٹ طور پر 24 گھنٹے) سے زیادہ عرصے تک suspended رہا ہو، وہ ایسے ٹکٹس کا انتظار کر رہا ہے جو کبھی حتمی حالت تک نہیں پہنچیں گے، اس لیے رن کو آزاد کرنے کے لیے اُسے ناکام کر دیا جاتا ہے۔ استثنا extras['awaitApproval'] == true ہے — وہی orchestration.awaitApproval جزوی منظوری گیٹ ہے۔ human.gate مستثنیٰ نہیں ہے۔
  • وہ قدم جس کے لیے extras: {'idempotent': false} اعلان کیا گیا ہو اور سرور رکنے کے وقت جاری تھا، ممکن ہے اپنا سائیڈ ایفیکٹ پہلے ہی لاگو کر چکا ہو (squash merge، worktree کی صفائی)۔ اُسے دوبارہ چلانے سے اثر دو بار لاگو ہو سکتا ہے، اس لیے انجن اُسے ناکام کر دیتا ہے اور دوبارہ کوشش آپ پر چھوڑ دیتا ہے۔

ایک قدم کے دو الگ الگ حصے ہوتے ہیں اور اُنہیں الگ رکھنا ہی وہ چیز ہے جو انجن کو چھوٹا رکھتی ہے۔

قسم (StepKind) انجن کو بتاتی ہے کہ ایک قدم اپنے بالائی قدموں کے مقابلے میں کب چلنے کے قابل ہوتا ہے — شیڈیولر کو صرف یہی جاننا ہوتا ہے۔ یہ چھ ہیں: trigger، listen، join، router، forEach اور terminal۔ باڈی (bodyKey) وہ کام ہے جو قدم شیڈیول ہونے کے بعد انجام دیتا ہے۔ باڈیز سرور کے آغاز پر کلید کے ذریعے رجسٹر ہونے والے کلوزر ہوتے ہیں، اس لیے مختلف اقسام کے کئی قدم ایک ہی باڈی شیئر کر سکتے ہیں: مثلاً کئی listen قدم جو سب conversation.promptAgent چلا رہے ہوں۔

چونکہ شیڈولنگ کے معنی چھ اقسام میں رہتے ہیں، رویوں کی فہرست شیڈیولر کو چھوئے بغیر بڑھ سکتی ہے۔ اقسام، باڈیز اور اُن کی کنفیگریشن کی مکمل فہرست کے لیے پائپ لائن قدم دیکھیں۔

رجسٹری کے ڈیزائن کے دو نتائج کوئی ٹیمپلیٹ ہاتھ سے لکھنے سے پہلے جاننے کے قابل ہیں:

  • جس قدم کا bodyKey رجسٹر نہ ہو، وہ ٹیمپلیٹ لوڈ کے وقت وارننگ پیدا کرتا ہے اور پھر کبھی چلتا نہیں۔ رن ٹائم پر کچھ بھی بلند آواز میں ناکام نہیں ہوتا۔ trigger اور terminal قدم اُس وارننگ سے مستثنیٰ ہیں: ٹریگر پھر بھی no-op pipeline.trigger باڈی چلاتا ہے تاکہ نیچے کے سننے والے چلیں؛ terminal قدم کی کوئی باڈی نہیں ہوتی اور وہ شیڈیولر کا سینٹینل ہے۔
  • کسی نوڈ کے extras میپ کی کلیدیں عین مطابق میچ ہوتی ہیں اور ورنہ خاموشی سے نظر انداز کر دی جاتی ہیں۔ صرف extras['spaceId'] کسی ایجنٹ قدم کا کام کسی موجودہ گفتگو میں ڈالتا ہے۔ extras['mode'] (chat / plan / review) اُس نوڈ پر پڑھا جاتا ہے جو کمرہ کھولتا ہے (messaging.createSpace، ڈیفالٹ review) — کوئی بعد کا ایجنٹ قدم اُس گفتگو کی اجازتیں دوبارہ نہیں دے سکتا جس میں وہ شامل ہو چکا۔ اِن دونوں میں سے کسی کلید کی کوئی اور ہجے نظر انداز کر دی جاتی ہے۔

قدم ایک دوسرے کو کال نہیں کرتے۔ وہ رن پر موجود مشترکہ اسٹیٹ بیگ سے پڑھتے اور اُس میں لکھتے ہیں: ایک قدم اپنے اِن پٹس کلید کے ذریعے پڑھتا ہے، چلتا ہے اور اپنا آؤٹ پٹ اپنی ہی outputKey کے تحت واپس لکھتا ہے۔ جب متوازی شاخیں ایک ہی کلید کو نشانہ بناتی ہیں تو StateReducer بیک وقت ہونے والی تحریروں کو ضم کرتا ہے اور TemplateRenderer اُسی اسٹیٹ سے پرامپٹس اور اسکرپٹس میں {{...}} پلیس ہولڈرز بدلتا ہے۔

اسی لیے پائپ لائن کو بعد ازاں جانچا جا سکتا ہے — اسٹیٹ بیگ ہی رن کی پوری کہانی ہے۔

PipelineTrigger قطاریں بتاتی ہیں کہ کوئی ٹیمپلیٹ خودکار طور پر کب شروع ہوتا ہے۔ یہ چار اقسام ہیں:

ٹریگر رن کب شروع کرتا ہے
ڈومین ایونٹ کوئی مماثل ڈومین ایونٹ آتا ہے — مثلاً PullRequestPublished یا TicketAssigned
شیڈول کوئی cron/انٹرول اظہار کی میعاد آ جاتی ہے
دستی آپ اسے ہاتھ سے چلاتے ہیں؛ فعال دستی ٹریگر کی موجودگی ہی ٹیمپلیٹ کو رن پکر میں ڈالتی ہے
ویب ہک کوئی بیرونی نظام سرور کے HTTP پورٹ پر /webhooks/<token> پر POST کرتا ہے

ایونٹ ٹریگرز ایک بند مجموعہ ہیں: ڈسپیچر کو 16 ایونٹ ٹائپس معلوم ہیں اور وہی پکر میں پیش کیے جاتے ہیں (EventPayloadMapper.knownEventTypes) اور کوئی اور چیز پائپ لائن شروع نہیں کر سکتی۔ اُن 16 میں سے چودہ کے لیے پے لوڈ میپنگ موجود ہے۔ دو — TicketCreated اور TicketStatusChanged — کے لیے کوئی نہیں، اس لیے اِن میں سے کسی ایک پر ٹریگر فعال پڑا رہتا ہے اور کبھی نہیں چلتا۔ مکمل فہرست کے لیے ڈومین ایونٹس دیکھیں۔

ویب ہک ٹوکن HMAC سیکرٹ کا بھی کام کرتا ہے: درخواست کے X-Hub-Signature-256: sha256=<hex> ہیڈر کی تصدیق HMAC-SHA256(body, token) کے خلاف کی جاتی ہے، غیر تصدیق شدہ ترسیلیں مسترد کر دی جاتی ہیں اور دوبارہ نہیں چلائی جا سکتیں، اور نقول dedupe_key کے ذریعے گرا دی جاتی ہیں۔ چونکہ ٹریگر قطاریں (ورک اسپیس، ایونٹ ٹائپ، ٹیمپلیٹ) پر یکتا ہوتی ہیں، ایک ٹیمپلیٹ میں زیادہ سے زیادہ ایک ویب ہک ٹریگر اور فی ڈومین ایونٹ ٹائپ ایک ٹریگر ہو سکتا ہے۔

بلٹ اِن ٹیمپلیٹس اپنے دستی اور ایونٹ ٹریگرز فعال رکھ کر سیڈ کرتے ہیں؛ شیڈول شدہ ٹریگرز غیر فعال سیڈ ہوتے ہیں، اس لیے کوئی بار بار ہونے والی صفائی ہمیشہ صریحاً فعال کرنی پڑتی ہے۔

ہر ورک اسپیس میں تیرہ پائپ لائن ٹیمپلیٹس سیڈ کیے جاتے ہیں اور ان میں سے آٹھ غیر فعال آتے ہیں: external_pr_welcome، cross_review، ticket_to_pr، pr_triage، pre_merge_gate، release_notes، dep_audit اور pr_digest۔

یہی وہ حصہ ہے جو لوگوں کو حیران کرتا ہے۔ اُن کے ٹریگرز فعال سیڈ ہوتے ہیں، مگر PipelineEngine.start غیر فعال ٹیمپلیٹ سے انکار کرتا ہے اور null واپس دیتا ہے — چنانچہ ٹریگر خالی جگہ میں چلتا ہے اور کوئی رن ظاہر نہیں ہوتا۔ سوئچ ٹیمپلیٹ ہے، ٹریگر نہیں۔ اُن آٹھ میں سے کسی سے کوئی کام کی توقع رکھنے سے پہلے اسے ترتیبات → ورک اسپیس → پائپ لائن ٹیمپلیٹس میں فعال کریں۔

ورک اسپیس کو دوبارہ سیڈ کرنا آپ کے ہر انتخاب کو محفوظ رکھتا ہے: آپ کی isEnabled ترتیب اور آپ کی موجودہ ٹریگر قطاریں برقرار رہتی ہیں۔ صرف ایک جان بوجھ کر رکھا گیا استثنا ہے — pr_merged_cleanup کا روزانہ شیڈول زبردستی فعال کر دیا جاتا ہے چاہے وہ پہلے آف ہو، کیونکہ وہ گاربیج کلیکشن ہے۔ اُس کے بغیر worktree قطاریں، کوڈ گراف پارٹیشنز اور copy-on-write نقول جمع ہوتی رہتی ہیں اور یہ کبھی صارف کا انتخاب ہونے کے لیے نہیں تھا۔

جاننے کے قابل ایک اور عدم توازن: بلٹ اِن pr_review جان بوجھ کر اب GitHub پر پوسٹ نہیں کرتا۔ ریویو شائع کرنا ایک الگ قدم ہے جو صارف کی منظوری سے مشروط ہے اور وہ پائپ لائن جو براہِ راست پوسٹ کرے دو بار شائع کر دے گی۔ cross_review، ticket_to_pr اور pr_triage چلنے پر پھر بھی سیدھا GitHub پر تبصرہ پوسٹ کرتے ہیں۔

PipelineEngine رن کا ریکارڈ بناتا ہے، اُس اسٹارٹ نوڈ کو شیڈیول کرتا ہے جو رن کے ایونٹ ٹائپ سے مماثل ہو، اور پھر DAG پر چلتا ہے: جیسے جیسے قدم مکمل ہوتے ہیں وہ نیچے کے ٹریگرز کا جائزہ لیتا ہے، روٹر شاخوں کو حل کرتا ہے، جوائنز کا انتظار کرتا ہے، ریٹرائی پالیسیاں اور ناکامی پر جاری رکھنا لاگو کرتا ہے، اور ہر قدم پر اسٹیٹ، لاگت اور خرابیاں محفوظ کرتا ہے۔ وہ کنارے جو کسی دوسرے اسٹارٹ سے نکلتے ہیں، اُس رن کے لیے نظر انداز کر دیے جاتے ہیں، اس لیے شیڈول کا چلنا صرف دستی شرط پر انتظار نہیں کرتا۔

جو چیز یہ نہیں جانتا، وہ ٹکٹنگ، ڈسپیچ، GitHub یا میسجنگ سے متعلق کچھ بھی ہے۔ اِس کی توسیع کی واحد سیم PipelineBodyRegistry ہے۔ وہاں رجسٹر ہونے والی باڈیز ہی AgentDispatchPort، MessagingPort اور اُن کے ہم منصب رکھتی ہیں — اسی لیے پائپ لائنز میں کوئی صلاحیت شامل کرنے کا مطلب کبھی شیڈیولر بدلنا نہیں ہوتا۔

ناکام ہونے والا قدم اپنا خرابی کا پیغام ریکارڈ کرتا ہے اور failed پر چلا جاتا ہے۔ اگر قدم continueOnFail کا اعلان کرتا ہو تو نیچے کے قدم آگے بڑھتے ہیں اور خرابی رن کے اسٹیٹ میں رکھ دی جاتی ہے؛ ورنہ رن ناکام ہو جاتا ہے۔ StepRetryPolicy والا قدم اِن دونوں میں سے کسی کے لاگو ہونے سے پہلے، لکیری یا ایکسپونینشل بیک آف کے ساتھ، پہلے دوبارہ آزمایا جاتا ہے۔