دليل تشغيل دورة الإصدار
سير عمل الإصدار الكامل من البوابات المحلية مرورًا بإصدار GitHub الموقَّع وصولًا إلى نشر npm.
يقود دليل التشغيل هذا إصدار vX.Y.Z من apothem من شجرة عمل
نظيفة إلى أسطح توزيع مُتحقَّق منها. كُتب الإجراء للمشرف الذي يستخدم gh
وgit وصدفة طبقة الإصدار؛ وينتهي بصفحة إصدار مُتحقَّق منها،
وCHANGELOG مُدوَّن، واختبار دخان لمسار التثبيت يؤكّد أن كل سطح يواجه
المستخدم يستجيب لأمر التثبيت المرجعي.
نموذج الإصدار تراكمي. يضيف كل إصدار كوميتًا واحدًا على main ووسم
vX.Y.Z واحدًا موقَّعًا ومُعلَّقًا؛ لا يُدفَع أي شيء بالقوة، ولا يُزال
أي وسم سابق أو إصدار GitHub سابق أو نسخة npm سابقة. يحتفظ كل إصدار بوسمه وإصدار GitHub الخاص به ونسخة npm الخاصة به، وتبقى جميعها منشورة: نسخ npm غير قابلة للتغيير (لا يمكن أبدًا
استبدال name@version منشور)، وعمليات التثبيت المقيّدة بنسخة محدّدة
تعتمد على الوسوم.
يبدأ دفعُ الوسم ثلاثةَ سير عمل. يبني .github/workflows/release.yml إصدار
GitHub ويوقّعه ويُصدِر شهاداته وينشره مع مجموعة مُنتَجاته الكاملة:
sdist، وwheel، وCycloneDX SBOM، وحِزم
Sigstore cosign، والمنشأ SLSA-3، وأرشيفات بيئة التشغيل
الخاصة بالمنصّات. وينشر publish-npm.yml الحزمة
@ahmed-g-gad/apothem على npmjs.com عبر النشر الموثوق في
npm، وpublish-vscode.yml يحزّم امتداد VS Code وينشره على
Visual Studio Marketplace. يصل قسم CHANGELOG المُرقَّم بالنسخة في كوميت الإصدار؛
ويُشغِّل المُشغِّل اختبار دخان مسار التثبيت بعد انتهاء سيرَي العمل كليهما.
1. المتطلّبات المسبقة
يتأكّد المشرف مما يلي قبل وضع الوسم:
-
شجرة العمل. نظيفة (لا يُظهر
git statusشيئًا معلّقًا). كل عمل الميزات الخاص بالإصدار موجود علىmain. -
CI أخضر. بالنسبة لأحدث
main، يُرجِعgh run list --branch=main --limit=1 --json conclusion --jq '.[0].conclusion'القيمةsuccess. -
الأدوات.
gh(GitHub CLI) بالنسخة 2.40 أو أحدث، مُصادَق عليه لحسابahmed-g-gad؛ وgit2.40 أو أحدث مع تحميل مفتاح توقيع GPG الفرعي؛ وربط الناشر الموثوق في npm للحزمة@ahmed-g-gad/apothemفعّال (مُتحقَّق منه وفق دليل تشغيل إعداد حساب الناشر). -
مراسي النسخة متوائمة. سلسلة النسخة المُعلَنة في
pyproject.tomlتطابق الوسم المُخطَّط له دون البادئةv(يضبطها القسم 2.1، مع كل مرساة أخرى). تحقّق:grep '^version' pyproject.tomlالمخرجات المتوقّعة (لإصدار
vX.Y.Z؛ العناصر النائبة تُمثّل وسم الإصدار الجاري):version = "X.Y.Z" -
مدخلات CHANGELOG مُجهَّزة. تقع نقاط الإصدار تحت
## [Unreleased]ضمن عناوين Keep-a-Changelog؛ ويحوّلها القسم 2.1 إلى قسم النسخة المؤرَّخ في الكوميت الذي يحمل الوسم.
2. الوسم والتشغيل
2.1 تأليف كوميت الإصدار
تعيش النسخة في أكثر من اثني عشر ملفًا، لذا لا تعدّلها يدويًا. أمر واحد يُحرّكها جميعًا:
git checkout main
git pull --ff-only origin main
python scripts/release/bump_version.py X.Y.Zيُحدّث bump_version.py كل مراسي النسخة معًا:
-
كل بيان تحزيم، وامتداد، وإضافة، وسوق إضافات، إضافةً إلى ملف الاستشهاد (النسخة وتاريخ الإصدار). القائمة الكاملة:
pyproject.toml package.json site/package.json site/package-lock.json (the project entries only) vscode-extension/package.json gemini-extension.json qwen-extension.json .claude-plugin/marketplace.json (top level and metadata) plugins/apothem/plugin.json plugins/apothem/.codex-plugin/plugin.json CITATION.cff -
CHANGELOG.md: تصبح مدخلات[Unreleased]قسمًا مؤرَّخًا## [X.Y.Z]، ويقارن[Unreleased]بدءًا منvX.Y.Z، ويُضاف رابط[X.Y.Z]. -
SECURITY.md: يحصل الإصدار الثانوي الجديد على صفّ مدعومX.Y.x. أمّا بقاء الإصدارات الثانوية الأقدم مُدرَجة فقرارك؛ ويطبع السكربت تذكيرًا.
ثم يُعيد توليد الأشجار المشتقّة: حزمة إضافة Claude Code، واللقطات
الذهبية لفارق السلوك، وصفحات مرجع الوثائق وسجلّ التغييرات. مرِّر
--dry-run لسرد التغييرات أولًا، أو --date YYYY-MM-DD لضبط
تاريخ الإصدار (الافتراضي: تاريخ اليوم بتوقيت UTC)، أو
--no-regen لتخطّي إعادة التوليد. يرفض السكربت النسخة المشوّهة أو خفض
النسخة، وإعادة تشغيله بالنسخة الحالية لا تفعل سوى إعادة مواءمة المراسي الشاردة.
راجِع الفرق، ثم أنشئ الكوميت:
git add -A
git commit -S -m "chore(release): cut vX.Y.Z"
git push origin mainتُوقّع الراية -S الكوميت بمفتاح GPG. يُطلِق الدفع مصفوفة
CI القياسية (lint، test، build)؛ انتظر
النتيجة الخضراء قبل وضع الوسم.
يحتاج فحص الجاهزية في القسم 2.4 أيضًا إلى أن تكون نتيجة سيرَي عمل CodeQL وConformity خضراء على كوميت الإصدار. يعمل كلٌّ منهما فقط عندما تتغيّر مساراته الخاصة، وعادةً لا يغيّر كوميت الإصدار أيًّا منها، لذا شغِّلهما بعد أن يصبح كوميت الإصدار رأس main، ثم تحقّق من أن كل تشغيل يُسمّي ذلك الكوميت:
gh workflow run codeql.yml --ref main
gh workflow run conformity.yml --ref main
gh run list --workflow codeql.yml --commit "$(git rev-parse HEAD)" --limit 1
gh run list --workflow conformity.yml --commit "$(git rev-parse HEAD)" --limit 1انتظر حتى ينجح الاثنان قبل وضع الوسم، ولا تدفع شيئًا آخر إلى main حتى يُنشر الإصدار: فالدفع اللاحق يُبعد main عن كوميت الإصدار، وCI يُلغي أي تشغيل ما زال جاريًا على الفرع نفسه.
2.2 توقيع الوسم ودفعه
git tag -a -s vX.Y.Z -m "vX.Y.Z"
git push origin vX.Y.Zيُنتِج الجمع بين -a -s وسمًا مُعلَّقًا موقَّعًا بـ GPG.
يُطلِق الدفع .github/workflows/release.yml، الذي يبني ويوقّع ويُصدِر
الشهادات وينشر.
2.3 تأكيد اكتمال سير عمل الإصدار
gh run watch $(gh run list --workflow=release.yml --limit=1 --json databaseId --jq '.[0].databaseId')يُشغِّل سير العمل البناء (sdist + wheel، ثم CycloneDX SBOM
لتلك التوزيعات يُكتَب بجانبها)، وتوقيع cosign بلا مفاتيح، وتوليد المنشأ SLSA-3 والتحقّق منه، وبناء أرشيفات المنصّات
(darwin / linux / windows)، وتوقيع الأرشيفات مع بيان
SHA256SUMS مُجمَّع، والنشر النهائي لإصدار GitHub. يفحص
المشرف سجلّ مهمة publish GitHub Release بحثًا عن تأكيدات رفع الأصول.
تعمل مهمة publish GitHub Release في بيئة release، وهي تتطلّب موافقة المشرف، لذا ينتظر التشغيل هناك بعد نجاح المهام السابقة. وافِق عليه من صفحة التشغيل عبر الزر Review deployments (مراجعة عمليات النشر)، أو من الصدفة:
run_id=$(gh run list --workflow=release.yml --limit=1 --json databaseId --jq '.[0].databaseId')
env_id=$(gh api repos/ahmed-g-gad/apothem/environments/release --jq .id)
gh api -X POST "repos/ahmed-g-gad/apothem/actions/runs/${run_id}/pending_deployments" \
-F "environment_ids[]=${env_id}" -f state=approved -f comment="vX.Y.Z"يُطلِق دفعُ الوسم أيضًا سيرَي عمل npm وامتداد VS Code. وتنتظر مهام النشر فيهما الموافقة نفسها حتى القسم 2.4.
2.4 تأكيد نشر npm
يُشغِّل دفعُ الوسم أيضًا publish-npm.yml بجانب release.yml. ولا ينتظر سيرُ عمل npm سيرَ عمل الإصدار؛ فموافقة بيئة release هي البوابة. وبعد أن ينتهي سير عمل الإصدار من القسم 2.3، تأكّد من الجاهزية. ويقرأ الفحص الإصدارات، وتشغيلات سير العمل، وملف SBOM المنشور، لذا جهّز مُدخلَين أولًا:
GH_TOKEN. يرفض السكربت العمل بدونه، حتى عندما يكونghمُسجَّل الدخول أصلًا. استخدم رمز وصول شخصيًا دقيق النطاق (fine-grained) مقصورًا علىahmed-g-gad/apothem، مع صلاحية Actions: Read (تشغيلات سير العمل) و**Contents: Read** (الإصدارات). ولا يكتب الفحص شيئًا، لذا لا تلزم صلاحية كتابة. ويحمل الرمز الصادر عنgh auth tokenكل نطاقات تسجيل الدخول، لذا يُفضَّل الرمز دقيق النطاق.- ملف SBOM المنشور. يقرأ الفحص
dist/release-assets/sbom.cdx.json، وهو ملف CycloneDX الذي يرفقه الإصدار. نزّل النسخة التي نشرها الإصدار بدلًا من بناء محلي.
read -rs GH_TOKEN && export GH_TOKEN
mkdir -p dist/release-assets
gh release download vX.Y.Z --repo ahmed-g-gad/apothem \
--pattern sbom.cdx.json --dir dist/release-assets --clobber
bash scripts/release/check-release-ready.sh vX.Y.Zيفشل الفحص عندما يكون الإصدار مسودّةً أو إصدارًا تجريبيًا (prerelease)، أو عندما لا يكون سير عمل مطلوب لإيداع الوسم أخضر، أو عندما يغيب ملف SBOM، أو عندما يصف إصدارًا آخر، أو عندما يُغفل حزمةً مُضمَّنة (vendored). وتُسمّي رسالة الفشل السببَ.
عند نجاح الفحص، وافِق على تشغيل Publish npm المنتظر بالطريقة نفسها المذكورة في القسم 2.3 (يأتي معرّف التشغيل من gh run list --workflow=publish-npm.yml). ينشر سير العمل @ahmed-g-gad/[email protected] على npmjs.com عبر النشر الموثوق في npm (OIDC)؛ ولا يُخزَّن أي رمز مميّز للسجلّ في المستودع. ووافِق كذلك على تشغيل Publish VS Code Extension المنتظر. وهو يحزّم apothem.vsix وينشره على Visual Studio Marketplace بالمعرّف ahmed-g-gad.apothem-vscode. لا تعمل خطوة النشر إلا حين يساوي متغيّر المستودع VSCODE_MARKETPLACE_READY القيمة true ويكون اعتماد واحد مهيّأً: المتغيّر VSCE_AUTH=oidc (النشر الموثوق في Marketplace، بلا سرّ مخزَّن)، أو المتغيّرات VSCE_AUTH=entra وAZURE_CLIENT_ID وAZURE_TENANT_ID (رمز Microsoft Entra ID قصير العمر، بلا سرّ مخزَّن)، أو سرّ VSCE_PAT. وفي غياب أيٍّ منها يحزّم التشغيل الامتداد ويتخطّى النشر. الاعتماد مفتاح تبديل لا إعادة محاولة: فالنشر الفاشل عبر أحد المسارات لا يرتدّ إلى مسار آخر من تلقاء نفسه. إعداد كل مسار موجود في النشر على Marketplace عبر Trusted Publishing (المُفضَّل) والنشر على Marketplace عبر Microsoft Entra ID أدناه.
لإعادة النشر بعد تشغيل فاشل، شغِّل سير العمل يدويًا للوسم نفسه
(gh workflow run publish-npm.yml --field tag=vX.Y.Z). يتخطّى سير العمل
النشر عندما تكون النسخة موجودة بالفعل في السجلّ، لأن نسخ npm غير قابلة
للتغيير.
النشر على Marketplace عبر Trusted Publishing
يتيح النشر الموثوق (Trusted Publishing) لـ Marketplace أن يقبل رمز GitHub OIDC الخاص بهذا المستودع مباشرةً: إذ يستبدل vsce publish --oidc رمزَ مهمة النشر باعتماد Marketplace قصير العمر. لا يوجد سرّ مخزَّن ولا مورد Azure، لذا فهذا هو المسار المُفضَّل. ويبقى مسار Entra أدناه وسرّ VSCE_PAT بديلَين يختارهما المشغِّل.
-
استخدم إصدارًا من vsce يدعمه. على هذا المسار تفحص مهمة النشر إصدار vsce المقفَل في
tools/vsceقبل أن تنشر، وتتوقّف بخطأ يُسمّي الإصدار الأدنى حين يكون القفل أقدم منه. يقترح Dependabot تحديثات vsce لملف القفل ذاك؛ ادمج التحديث الذي يبلغ الإصدار الأدنى قبل أن تبدّل المسار. -
أنشئ السياسة. سجّل الدخول بصفتك مالكًا (Owner) للناشر، وافتح
https://marketplace.visualstudio.com/manage/publishers/ahmed-g-gad، وأضف سياسة تحت Trusted Publishing:- اسم السياسة: أي اسم، مثل
apothem-release - مالك المستودع:
Ahmed-G-Gad - المستودع:
apothem - سير العمل:
publish-vscode.yml - البيئة:
release
أسماء الحقول هي التي يسردها دليل النشر في VS Code، في قسمه عن النشر باستخدام GitHub Actions وTrusted Publishing (
https://code.visualstudio.com/api/working-with-extensions/publishing-extension). غير مؤكَّد: ما إذا كان كل ناشر يرى تبويب Trusted Publishing حتى الآن. إن لم يظهر لك، فاستخدم مسار Entra. - اسم السياسة: أي اسم، مثل
-
بدّل المسار. أبقِ
VSCODE_MARKETPLACE_READYمضبوطًا علىtrueواضبط المُحدِّد. اضبطه حين لا يكون أي وسم في الانتظار، لأن دفع الوسم التالي ينشر عبر هذا المسار.gh variable set VSCE_AUTH --body oidc -
انشر. يَنشر دفعُ الوسم التالي، أو تشغيلٌ يدوي لوسم موجود، عبر Trusted Publishing. لا يملك هذا المسار تشغيلًا تجريبيًا: النشر الأول هو الفحص، والتشغيل اليدوي مع
dry_runلا يفعل سوى التحذير من أن شيئًا لم يُتحقَّق منه. -
ارجع إلى البديل إن فشل. احذف المُحدِّد وشغِّل سير العمل يدويًا مرّة أخرى للوسم نفسه؛ ومع ضبط سرّ
VSCE_PATينشر مسار الرمز. النشر الفاشل لم ينشر شيئًا، لذا يمكن تشغيل الوسم نفسه يدويًا مرّة أخرى.gh variable delete VSCE_AUTH
النشر على Marketplace عبر Microsoft Entra ID
يستبدل مسار Entra رمزَ الوصول الشخصي المخزَّن برمز قصير العمر. يطلب publish-vscode.yml من GitHub رمز OIDC، ويستبدله الإجراء Azure/login بتسجيل دخول إلى Entra عبر اعتماد اتحادي (federated credential)، وينشر vsce publish --azure-credential بالرمز الناتج. لا يوجد أي سرّ عميل (client secret). ويبقى سرّ VSCE_PAT بديلًا احتياطيًا يختاره المشغِّل؛ ولا ينتقل سير العمل إليه من تلقاء نفسه أبدًا.
يدعم إصدار vsce المثبَّت في tools/vsce هذا المسار. ويحتاج المسار إلى مستأجر (tenant) في Entra، وتسجيل تطبيق، وعضوية في الناشر، وهو ما لا يحتاجه Trusted Publishing أعلاه؛ استخدمه حين لا يكون Trusted Publishing متاحًا للناشر.
-
تحقّق من الشروط المسبقة. اعثر على الحساب الذي يملك الناشر
ahmed-g-gad(تبويب Members فيhttps://marketplace.visualstudio.com/manage/publishers/ahmed-g-gad) وعلى مستأجر Entra الذي يمكنك فيه تسجيل التطبيقات. غير مؤكَّد: ما إذا كان يجب أن يطابق مستأجرُ مالك الناشر مستأجرَ التطبيق، وما إذا كان يجب ربط مؤسسة Azure DevOps المسمّاةAhmed-G-Gadبـ Entra (dev.azure.com/Ahmed-G-Gad، Organization settings، Microsoft Entra). لا يذكر أي مصدر أيًّا من الأمرين. إن فشلت الخطوة 5 أو 6، فتحقّق من هذين أولًا. -
سجّل التطبيق. في
https://entra.microsoft.com، افتح App registrations، ثم New registration. سمِّهapothem-vscode-publish، واختر مستأجرًا واحدًا (single tenant)، واترك عنوان إعادة التوجيه (redirect URI) فارغًا. ومن Overview، انسخ Application (client) ID وDirectory (tenant) ID. لا تُنشئ أي سرّ عميل ولا تمنح أي صلاحية API. تحتاج الخطوة إلى حقوق مالك التطبيق أو إلى دور في Entra مثل Application Administrator. -
أضف الاعتماد الاتحادي. على ذلك التطبيق، افتح Certificates & secrets، ثم Federated credentials، ثم Add credential، واختر السيناريو "GitHub Actions deploying Azure resources". أدخل المؤسسة
Ahmed-G-Gad، والمستودعapothem، ونوع الكيانEnvironment، واسم البيئةrelease، واسم الاعتمادapothem-release. يجب أن تكون الحقول المولَّدة كما يلي:issuer: https://token.actions.githubusercontent.com subject: repo:Ahmed-G-Gad/apothem:environment:release audience: api://AzureADTokenExchangeالمطابقة دقيقة وحسّاسة لحالة الأحرف. إن فشل تسجيل دخول لاحق مع AADSTS700213 أو AADSTS7002138، فقارن الاعتماد بسطر
subject claimالذي يكتبهAzure/loginفي سجلّ المهمة. ويعني AADSTS70021 مباشرةً بعد الإنشاء أن الاعتماد لم ينتشر بعد؛ انتظر بضع دقائق وأعد المحاولة. -
اضبط متغيّرات المستودع. ما دام
VSCODE_MARKETPLACE_READYيساويtrue، يصبح مسار Entra فعّالًا بمجرّد ضبط المتغيّرات الثلاثة كلها، لذا اضبطها حين لا يكون أي وسم إصدار في انتظار الموافقة. ومن دونVSCODE_MARKETPLACE_READY=trueلا يجري أي تسجيل دخول ويتخطّى التشغيل النشر.gh variable set AZURE_CLIENT_ID --body "<application-client-id>" gh variable set AZURE_TENANT_ID --body "<directory-tenant-id>" gh variable set VSCE_AUTH --body entraاترك
VSCODE_MARKETPLACE_READYوVSCE_PATعلى حالهما. حين يكونVSCE_AUTHمساويًا لـentraلكنAZURE_CLIENT_IDأوAZURE_TENANT_IDفارغ، يسجّل سير العمل تحذيرًا، سواء أكانVSCE_PATمضبوطًا أم لا، ولا يستخدم مسار Entra. ثم يستخدم سرّVSCE_PATإن كان مضبوطًا؛ ومن دون السرّ لا ينشر شيئًا وينتهي التشغيل مع ذلك أخضر، لذا ابحث عن التحذير. -
شغِّل التشغيل التجريبي. شغِّل سير العمل يدويًا لوسم إصدار موجود مع ضبط
dry_run. على مسار Entra يسجّل الدخول، ويطبع ملف تعريف Azure DevOps، ويتحقّق من الوصول إلى الناشر دون نشر. لا يملك مسار الرمز تشغيلًا تجريبيًا: إن أظهر السجلّMarketplace credential mode: pat، ينتهي التشغيل أخضر مع تحذير دون أن يتحقّق من شيء. وافِق على تشغيل بيئةreleaseالمنتظر.gh workflow run publish-vscode.yml --ref main -f tag=vX.Y.Z -f dry_run=trueالمتوقّع:
Marketplace credential mode: entra، وسطرsubject claimمساوٍ لقيمة subject في الخطوة 3، والقيمتانidوdisplayNameلملف التعريف كما تطبعهما أول خطوة من التشغيل التجريبي. يُتوقَّع أن تفشل خطوة التحقّق بالرمز 403 حتى الخطوة 6. غير مؤكَّد: أن تسجيل دخول بلا اشتراك Azure يُنتِج رمزًا لـ Marketplace عبر Azure CLI. وهذا التشغيل هو أول اختبار لذلك. إن فشل هنا، فسجّل بدلًا من ذلك هوية مُدارة مُعيَّنة من المستخدم (user-assigned managed identity) ضمن اشتراك، وهو المسار الذي توثّقه Microsoft. -
أضف التطبيق إلى الناشر. في تبويب Members من الخطوة 1، سجّل الدخول بصفتك مالكًا للناشر وأضف عضوًا باستخدام قيمة
idلملف التعريف من الخطوة 5، بالدور Contributor. غير مؤكَّد: تسمية الحقل في مربّع الحوار، وما إذا كان يقبل المعرّف قبل أن يصبح التطبيق عضوًا في مؤسسة Azure DevOps. تضيف Azure DevOps هويات التطبيقات لكل مؤسسة على حدة كقاعدة عامة. إن رفض مربّع الحوار المعرّف، فأضف كيان الخدمة (service principal) إلى المؤسسةAhmed-G-Gad(Organization settings، Users، باستخدام Object ID الخاص بـ Enterprise application) وأعد المحاولة. وغير مؤكَّد أيضًا: ما إذا كان ملف التعريف موجودًا قبل أول استدعاء تُجريه الهوية إلى Azure DevOps. -
كرّر التشغيل التجريبي. تنجح خطوة التحقّق الآن بالرسالة "verification succeeded for the publisher"؛ ولا تزال رسائلها تذكر Personal Access Token، لأن رمز Entra يسلك المسار نفسه. يُثبت الفحص عضوية الناشر (Creator أو Owner أو Contributor أو Reader)، لا صلاحيات النشر. غير مؤكَّد: أن Contributor يكفي لنشر تحديث. يقول وصف Microsoft للدور إنه يكفي، ولا يُظهر أي مصدر نشرًا به، لذا فالنشر الحقيقي الأول هو البرهان.
-
انشر إصدارًا. ضع الوسم كما في القسم 2.2 ووافِق على التشغيل. تعمل الخطوة
Publish to Visual Studio Marketplace (Entra ID)، وتُدرِج ترويسة بيئتهاAZURE_TENANT_IDوحده، وتُتخطّى خطوة الرمز الاحتياطية، ويطبع vsce النصDONE Published ahmed-g-gad.apothem-vscode. ويُدرِج معرض Marketplace النسخة الجديدة بعد نحو أربع دقائق. -
قرّر بشأن الرمز. احتفظ بـ
VSCE_PATحتى أول نشر ناجح عبر Entra، ثم احذف السرّ إن لم تكن تريد بديلًا احتياطيًا. وبمجرّد حذفه، يترك متغيّرُ Entra المحذوف أو الفارغ التشغيلَ أخضر بلا نشر: مع تحذير حين يظلVSCE_AUTHمساويًا لـentra، ومع إشعار التخطّي وحده حين لا يكون كذلك. اقرأ تعليقات التشغيل التوضيحية (annotations) بعد كل وسم. وللاحتفاظ ببديل احتياطي بعد إيقاف رموز الوصول الشخصية العامّة (global)، أنشئ رمزًا مقصورًا على المؤسسةAhmed-G-Gadبالنطاق المخصّص Marketplace Manage وأقصر مدّة صلاحية تقبلها، وخزّنه باسمVSCE_PAT. غير مؤكَّد: تسميات البوابة في هذه الخطوة مأخوذة من الذاكرة، لا من مصدر أوّلي؛ وتصف Microsoft الرموز المقصورة على المؤسسة بأنها حلّ التفافي مؤقّت ويمكنها أن تتوقّف عن قبولها؛ وتتعارض صفحات Microsoft بشأن تاريخ إيقاف الرموز العامّة. -
ارجع إلى الرمز. احذف المُحدِّد وشغِّل سير العمل يدويًا مرّة أخرى، ثم وافِق على التشغيل. النشر الفاشل عبر Entra لم ينشر شيئًا، لذا يمكن تشغيل الوسم نفسه يدويًا مرّة أخرى؛ تحقّق من صفحة Marketplace أولًا، لأن النسخة التي وصلت لا يمكن نشرها مرّتين.
gh variable delete VSCE_AUTH gh workflow run publish-vscode.yml --ref main -f tag=vX.Y.Z
حين يفشل النشر عبر Entra، اقرأ سجلّ الخطوة:
- الرسالة التي تبدأ بـ "Can not acquire a Microsoft Entra ID access token" تعني أن تسجيل الدخول أو سلسلة الاعتماد قد فشلت. تحقّق من خطوة
Sign in to Microsoft Entra IDومن الاعتماد الاتحادي أولًا. إن كان تسجيل الدخول أخضر، فأزِلAZURE_TENANT_IDمنenvفي خطوة vsce ضمن سير العمل؛ فسير عمل إصدار الامتداد الذي تعتمده GitHub نفسها لا يُصدِّره. - تعني "InvalidAccessException" أو "not allowed" أن هوية الرمز ليست عضوًا في الناشر. قارن قيمة
idلملف التعريف من التشغيل التجريبي بتبويب Members. - تعني الاستجابة 403 من خطوة التحقّق في التشغيل التجريبي أن التطبيق لا يملك دورًا بعد.
تنطبق ثلاثة قيود على هذا المسار:
- تُمنح
id-token: writeلمهمة النشر كلها، لذا يمكن لكل خطوة فيها أن تطلب رمز OIDC، في وضع الرمز أيضًا. ويرتبط subject الرمز ببيئةrelease، التي تتطلّب موافقة مُراجِع. - لا تملك بيئة
releaseأي سياسة فروع أو وسوم للنشر، لذا يمكن لأي مرجع (ref) يوافق عليه مشرف أن يحصل على الرمز ذي subject البيئة. ويحتاج تقييدها بوسومv*.*.*إلى التحقّق أولًا مما يُشغِّلهrelease.ymlوpublish-npm.yml. - تغيير اسم المستودع أو نقله يغيّر صيغة subject في OIDC ويكسر الاعتماد الاتحادي بصمت. انسخ قيمة subject من سجلّ
Azure/loginإلى الاعتماد مرّة أخرى بعد أي تغيير للاسم.
2.5 تحديث القيم المثبَّتة لبصمات القوالب
بعد أن يوجد الوسم، اضبط _LATEST_RELEASE و_LATEST_RELEASE_DIGESTS في
tests/unit/test_template_digests.py على الإصدار الجديد وعلى بصمة SHA-256
لكل قالب نصي عادي كما شحنه ذلك الإصدار
(git show vX.Y.Z:<template path> | sha256sum). افعل ذلك في التغيير التالي على
main، وأضف البصمة السابقة إلى earlier_digests في مُدخل البيان حين
يكون القالب قد تغيّر. تستخرج مهمة release-preflight في CI كل وسم،
لذا تفشل مع ذكر أسماء الوسوم إلى أن تُحدَّث القيم المثبَّتة.
3. أسطح التوزيع
لكل سطح أمر تحقّق يجيب عن السؤال «هل وصل الإصدار إلى هنا؟»:
| # | السطح | المُنتَج | التحقّق |
|---|---|---|---|
| 1 | إصدار GitHub (توزيعات Python) | apothem-X.Y.Z.tar.gz (sdist) + apothem-X.Y.Z-py3-none-any.whl | يتضمّن خرج gh release view vX.Y.Z --json assets --jq '.assets[].name' اسمَي الملفّين كليهما |
| 2 | إصدار GitHub (SBOM) | sbom.cdx.json + sbom.cdx.json.cosign.bundle | تتضمّن قائمة الأصول كليهما؛ وقيمة metadata.component.purl في الـ SBOM هي pkg:pypi/[email protected]، وتتضمّن مكوّناته كل حزمة في src/apothem/_vendor/vendor.txt |
| 3 | إصدار GitHub (التوقيعات) | ملف <asset>.cosign.bundle واحد لكلٍّ من الـ sdist والـ wheel والـ SBOM وامتداد VS Code وكل أرشيف منصّة وSHA256SUMS (تُسمّي الإصدارات السابقة حِزم الأرشيفات وSHA256SUMS بالاسم <asset>.sig) | يخرج cosign verify-blob بالقيمة 0 مع الهوية الدقيقة الواردة في القسم 5.5 |
| 4 | إصدار GitHub (المنشأ) | provenance.intoto.jsonl | يخرج bash scripts/release/verify-provenance.sh <download-dir> بالقيمة 0 (القسم 5.5) |
| 5 | إصدار GitHub (أرشيفات المنصّات) | apothem-vX.Y.Z-darwin.tar.gz، apothem-vX.Y.Z-linux.tar.gz، apothem-vX.Y.Z-windows.zip | تتضمّن قائمة الأصول كل أرشيف إضافةً إلى SHA256SUMS. أرشيفا darwin وlinux هما البايتات نفسها تحت اسمين (بيئة التشغيل محايدة تجاه المنصّة؛ ولا يختار --platform إلا بين tar.gz وzip)، لذا تُظهر سطورهما في SHA256SUMS بصمةً واحدة مرّتين |
| 6 | إصدار GitHub (امتداد VS Code) | apothem.vsix + apothem.vsix.cosign.bundle | تتضمّن قائمة الأصول كليهما؛ ويُثبّته code --install-extension apothem.vsix. الإصدارات التي سبقت تحزيم سير عمل الإصدار للامتداد لا تحمل أيًّا منهما |
| 7 | npmjs.com | @ahmed-g-gad/[email protected] | يُرجِع npm view @ahmed-g-gad/apothem version القيمة X.Y.Z؛ ويطبع npx @ahmed-g-gad/[email protected] --version النص Apothem, version X.Y.Z |
| 8 | Pages (مُثبِّتات السكربت) | install.sh / install.ps1 على موقع الوثائق | يُرجِع curl -fsSL https://apothem.ahmedgad.com/install.sh | head -n 1 ترويسة السكربت |
| 9 | Visual Studio Marketplace | ahmed-g-gad.apothem-vscode النسخة X.Y.Z | تُدرِج صفحة الامتداد في Marketplace النسخة X.Y.Z؛ ويُثبّته code --install-extension ahmed-g-gad.apothem-vscode |
إصدار GitHub هو سطح المُنتَجات المرجعي؛ وحزمة npm ومُثبِّتات السكربت هي مسارات التثبيت المبنيّة فوقه. ويعمل نشر npm بجانب سير عمل الإصدار على دفع الوسم نفسه.
4. انضباط CHANGELOG
يحمل CHANGELOG.md قسمًا واحدًا لكل إصدار وفق اصطلاح
Keep-a-Changelog. تعديل CHANGELOG في كوميت الإصدار:
- يتأكّد من أن قسم النسخة يستخدم العنوان
## [X.Y.Z] - YYYY-MM-DD(بشَرطة قصيرة؛ يكتبهbump_version.py، ويطابقextract_release_notes.pyالبادئة## [X.Y.Z]). - يتأكّد من استخدام العناوين القياسية — Added، Changed، Deprecated، Removed، Fixed، Security — حيثما ينطبق. تُسقَط العناوين الفارغة من الملف المعروض.
- يلتقط القدرات، لا التنفيذ. لغة المجال فقط؛ بلا مراجع داخلية للخطط؛ بلا بصمات كوميتات؛ بلا مُعرِّفات خطط أو مراحل.
قسم CHANGELOG الذي اجتاز مراجعة المشرف يُقرأ بوصفه مرساة ملاحظات الإصدار لسرد الإعلان.
5. اختبار دخان مسار التثبيت
بعد اكتمال المرحلتين كلتيهما، يُشغِّل المشرف اختبار الدخان. اختبار الدخان هو التحقّق المرجعي من أن أوامر التثبيت التي تواجه المستخدم تستجيب.
5.1 إضافة Claude Code
/plugin marketplace add ahmed-g-gad/apothem
/plugin install apothem@apothemالمتوقّع: تُثبَّت الإضافة وتصبح أوامر apothem متاحة داخل
Claude Code.
5.2 Node.js (npx)
npx @ahmed-g-gad/[email protected] --versionالمخرجات المتوقّعة: Apothem, version X.Y.Z.
5.3 مُثبِّت السكربت بخطوة واحدة (POSIX)
يكتب المُثبِّت المُشغِّل apothem في ~/.local/bin، أو في الدليل المسمّى في APOTHEM_BIN_DIR حين يكون هذا المتغيّر مضبوطًا. ويحتاج الفحص أدناه إلى أن يكون ذلك الدليل على PATH. وحين لا يكون كذلك، يطبع المُثبِّت سطر export PATH الذي يجب إضافته. وما لم يكن ذلك الدليل على PATH، فلن تجد الصدفة apothem، ويفشل الفحص. وإذا تعذّر على المُثبِّت وضع المُشغِّل، طبع تحذيرًا دون سطر export PATH. وفي مضيف لا يحمل مفتاح توقيع المشرف بعد، يستورده السطر الأول، ويتوقّف المُثبِّت من دونه.
curl -fsSL https://github.com/ahmed-g-gad.gpg | gpg --import
curl -fsSL https://apothem.ahmedgad.com/install.sh | sh
apothem --versionالمخرجات المتوقّعة: Apothem, version X.Y.Z.
5.4 مُثبِّت السكربت بخطوة واحدة (Windows)
يستورد السطر الأول مفتاح توقيع المشرف عبر bash الخاص بـGit نفسه، إذ إن حلقة مفاتيح gpg التابعة له هي التي يقرأها git verify-tag.
& "$env:ProgramFiles\Git\bin\bash.exe" -c 'curl -fsSL https://github.com/ahmed-g-gad.gpg | gpg --import'
irm https://apothem.ahmedgad.com/install.ps1 | iex
apothem --versionالمخرجات المتوقّعة: Apothem, version X.Y.Z.
5.5 التحقّق من المُنتَجات (أدلّة سلسلة التوريد)
نزِّل كل الأصول إلى دليل فارغ، ثم تحقّق. هوية الشهادة هي سير عمل الإصدار عند هذا
الوسم بالضبط. فحوص الهوية حسّاسة لحالة الأحرف، وتحمل الشهادات اسم المالك كما
يُبلغ عنه GitHub، أي Ahmed-G-Gad، لذا يفشل اسم المالك
المكتوب بأحرف صغيرة.
mkdir release-vX.Y.Z && cd release-vX.Y.Z
gh release download vX.Y.Z --repo ahmed-g-gad/apothem
cosign verify-blob \
--bundle apothem-X.Y.Z-py3-none-any.whl.cosign.bundle \
--certificate-identity https://github.com/Ahmed-G-Gad/apothem/.github/workflows/release.yml@refs/tags/vX.Y.Z \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
apothem-X.Y.Z-py3-none-any.whl
slsa-verifier verify-artifact \
--provenance-path provenance.intoto.jsonl \
--source-uri github.com/Ahmed-G-Gad/apothem \
--source-tag vX.Y.Z \
apothem-X.Y.Z-py3-none-any.whl apothem-X.Y.Z.tar.gz
bash ../scripts/release/verify-provenance.sh .المتوقّع: Verified OK، وPASSED: SLSA verification passed،
وverify-provenance: chain OK (3 of 3 layers verified). يفحص السكربت
SHA256SUMS، وكل حزمة توقيع، والمنشأ في تشغيل واحد؛ ويستنتج
الوسم من اسم الـ wheel (ويتجاوزه --tag vX.Y.Z).
يُنتِج اختبار الدخان صفًّا من سطر واحد PASS / FAIL لكل سطح؛ سجِّل الصفوف في سجلّ إصدار المشرف.
6. الاسترداد من الفشل
لسير عمل الإصدار أنماط فشل موثّقة لمحرّك الإصدار ولناشر npm:
| الفشل | التشخيص | الاسترداد |
|---|---|---|
| رُفِض دفع الوسم (فشل التوقيع) | يُرجِع git push origin vX.Y.Z الخطأ error: failed to push some refs مع رسالة متعلّقة بـ GPG | تحقّق من أن مفتاح GPG الفرعي مُحمَّل (gpg --list-secret-keys --keyid-format=long)؛ أعِد وضع الوسم محليًا باستخدام -s بعد إصلاح حلقة المفاتيح؛ أعِد الدفع |
يتوقّف بناء الإصدار عند Check the plugin version changed with the plugin | يُسمّي سجلّ مهمة البناء الإضافةَ والوسمَ السابق؛ لم يُبنَ أي أصل ولم يُنشَر | تغيّرت حزمة الإضافة منذ الإصدار السابق لكن نسختها لم تتغيّر. احذف الوسم (git push --delete origin vX.Y.Z، git tag -d vX.Y.Z)، وشغِّل python scripts/release/bump_version.py X.Y.Z، وأنشئ الكوميت، ثم وقِّع الوسم وادفعه مجددًا (القسم 2.2). يعمل الفحص نفسه على كل طلب سحب بوصفه تعليقًا تحذيريًا، لذا يظهر هذا عادةً قبل وضع الوسم |
تفشل مهمة في release.yml في مرحلة واحدة | يُسمّي gh run view <run-id> --log المهمة الفاشلة (build، التي تكتب الـ SBOM أيضًا / sign / provenance / archive / publish) | أعِد تشغيل المهمة الفاشلة وحدها عبر gh run rerun <run-id> --failed؛ وإذا تكرّر الفشل، أصلِح مُدخَل المهمة عند مصدره قبل إعادة وضع الوسم |
| يفشل التحقّق من المنشأ SLSA | تخرج مهمة verify SLSA provenance artifact بقيمة غير صفرية | افحص مُنتَج المنشأ بحثًا عن عدم تطابق بصمة الموضوع؛ أعِد البناء من الوسم نفسه — يرتبط المنشأ ببصمات المُنتَجات الدقيقة، لذا تتطلّب أي إعادة توليد لمُنتَج إعادة تشغيل كاملة لسير العمل |
| رُفِض النشر على npmjs.com | تخرج مهمة npm بقيمة غير صفرية أثناء npm publish؛ والخطأ 404 من السجلّ أثناء PUT هو عدم تطابق في التفويض / الناشر الموثوق، لا دليل على أن شيفرة الحزمة مفقودة | تحقّق من أن npm Trusted Publishing للحزمة @ahmed-g-gad/apothem يُسمّي المالك ahmed-g-gad، والمستودع apothem، وسير العمل publish-npm.yml، والإجراء المسموح به npm publish؛ وتأكّد من أن المهمة تعمل بنسخة Node وبالحدّ الأدنى لدعم OIDC في npm CLI، المُثبَّتين في سير العمل |
| الوسم ظاهر لكن الأصول مفقودة | يُرجِع gh release view vX.Y.Z إصدارًا هيكليًا فارغًا | أعِد تشغيل مهمة النشر في سير عمل الإصدار؛ فهي تُعدِّل الإصدار الموجود وتُعيد رفع كل الأصول. لا تحذف الوسم وتُعِد وضعه إلا عندما لا يكون أي شيء من الوسم قد وصل إلى المستخدمين (لا أصول، ولا نسخة npm)؛ وإلا فأصلِح إلى الأمام بالإصدار التصحيحي التالي (دليل تشغيل استرداد الإصدار، القسم 2) |
إذا أخفقت دورة استرداد في إيصال سطحٍ ما خلال تمريرة تشخيص واحدة، يُسجَّل السطح بوصفه فشلًا معروفًا في ملاحظات الإصدار مع رابط إلى المسألة المفتوحة؛ ويُوجَّه المستخدمون اللاحقون إلى الأسطح العاملة بينما يُصلَح السطح المعطوب في إصدار تصحيحي.
7. الإحالات المرجعية
- إعداد الناشر. يُؤسِّس دليل تشغيل إعداد حساب الناشر ربط الناشر الموثوق في npm والمتطلّبات المسبقة على جانب GitHub التي يفترضها دليل التشغيل هذا.
- مسار الاسترداد. يتبع دليل تشغيل استرداد الإصدار النموذج التراكمي نفسه: فهو يستردّ وسمًا أو مرحلة ناشر فاشلة مرحلةً بمرحلة، ويتراجع بإصدار الإصلاح بوصفه نسخة تصحيحية جديدة، لا بحذف وسم أو إصدار أو نسخة npm منشورة أو إعادة توجيهها أبدًا.
- تفعيل Pages. يتحقّق سير العمل
publish-static-site.ymlمن موقع المشروع وينشره على النطاق المخصّص؛ ودليل تشغيل تفعيل Pages (site/content/docs/runbooks/pages-enablement.mdx) هو الإجراء السابق الذي أسّس النطاق المخصّص في المقام الأول. - سياسة ترقيم النسخ.
site/content/docs/governance/release-engineering-policy.mdxيحمل سياسة SemVer + التوقيع + الإهمال التي يلتزم بها دليل التشغيل هذا.