CI/CD عمليّ لتطبيقات WinForms / WPF ── أتمتة من البناء إلى التوقيع والتوزيع عبر GitHub Actions
· آخر تحديث: · غو كومورا · CI/CD, GitHub Actions, WinForms, WPF, C#, .NET, توقيع الشيفرة, MSIX, Deployment, تطوير Windows, جدول القرار
«الإصدار لا يمكن بناؤه إلّا على حاسوب ذلك الشخص» ── جملة نسمعها كثيراً حقّاً في استشارات تطبيقات الأعمال المبنيّة بـWinForms أو WPF. بناء Release على Visual Studio يدويّاً، وضغطه في zip، ووضعه في مجلّد مشترَك. الأمر يعمل، لكن لا أحد يستطيع الإجابة عمّا إذا كان بالإمكان إصدار نسخة إصلاح خطأ في اليوم الذي يتغيّب فيه ذلك المطوِّر.
معلومات CI/CD لتطبيقات الويب متوفّرة بكثرة، لكن بمجرّد الانتقال إلى تطبيقات سطح المكتب تصبح شحيحة فجأة. وجود اختلاف جذريّ يتمثّل في «وجهة النشر ليست خادماً بل حاسوب العميل» يجعل من غير الممكن تقليد مقالات الويب كما هي. لكن حتّى أتمتة البناء والاختبار، يمكن بناؤها في تطبيقات سطح المكتب بجهد يماثل تقريباً الويب. العائق يكمن فيما بعد ذلك، أي التوقيع والتوزيع، وهناك يختلف الحلّ الواقعيّ حسب أسلوب التوزيع.
في هذه المدوّنة، رتّبنا كيفيّة اختيار أسلوب التوزيع في «جدول القرار حول أساليب توزيع تطبيقات Windows»، وفكرة التوقيع في «SmartScreen وتوقيع الشيفرة». هذا المقال يفترض هذين المقالين، ويرتِّب إلى أيّ مدى نُؤتمِت البناء والاختبار وترقيم الإصدار والتوقيع وإنشاء ملفّات التوزيع لتطبيقات WinForms / WPF باستخدام GitHub Actions من منظور عمليّ.
1. الخلاصة أوّلاً
- أكبر خطر هو حالة «لا يمكن البناء إلّا على حاسوب المطوِّر». الهدف الأوّل من CI/CD ليس الأتمتة الكاملة للتوزيع، بل إمكانيّة إعادة إنتاج البناء دون الاعتماد على حاسوب أيّ شخص.
- البنية الدنيا للبناء + الاختبار وحدها لها قيمة كافية. يمكن بناؤها بملفّ YAML واحد:
windows-latest+actions/checkout+actions/setup-dotnet+dotnet build / test+actions/upload-artifact.12 - WinForms / WPF يفترضان عامل تشغيل Windows. لأنّهما يستهدفان TFM خاصّاً بـWindows مثل
net8.0-windows3، فإن أردتَ تنفيذ الاختبارات عبر CI أيضاً، تحتاج بيئة Windows. - ترقيم الإصدار المدفوع بالوسم (Tag) هو الحلّ الواقعيّ. تُشغَّل عمليّة بناء الإصدار عند دفع وسم
v1.2.3، ويُحقَن قيمة الوسم في خاصّيّةVersionالخاصّة بـMSBuild.4 - التوقيع هو أكبر عائق للأتمتة. منذ يونيو 2023 أصبح حفظ المفتاح الخاصّ لشهادات OV الرسميّة في HSM إلزاميّاً، فلم يعد النمط التقليديّ «وضع PFX في الأسرار والتوقيع عبر signtool» صالحاً كما هو. إن أردتَ الإكمال داخل CI، فالحلّ الواقعيّ هو المرور عبر خدمة توقيع سحابيّة.5
- سهولة الدمج مع CI تختلف اختلافاً كبيراً حسب شكل التوزيع. xcopy (zip) هو الأبسط، ويتطلّب MSIX توقيعاً إلزاميّاً6، وMSI يتطلّب ربطاً بأدوات سطر أوامر مثل WiX، أمّا ClickOnce فيتطلّب
msbuild /target:publishوله طبائع خاصّة7، بهذا الترتيب من حيث الصعوبة. - لا تجعل الاختبار التلقائيّ للواجهة (UI) شرطاً إلزاميّاً في CI. الاختبار الوحدويّ (unit test) إلزاميّ في CI، أمّا اختبار الواجهة فالحلّ الواقعيّ هو الاقتصار على اختبار تدخينيّ (smoke) وتشغيله في مهمّة منفصلة.
2. ما الفرق بين CI/CD لتطبيقات سطح المكتب والويب
قبل كلّ شيء، نرتّب سبب تعذّر نقل نمط CI/CD لتطبيقات الويب (push ← بناء ← اختبار ← نشر إلى خادم) كما هو.
| الجانب | تطبيق الويب | تطبيق سطح مكتب WinForms / WPF |
|---|---|---|
| وجهة النشر | خادم تُديره الشركة نفسها | حاسوب العميل أو الموقع الميدانيّ (خارج الإدارة) |
| وحدة التوزيع | تبديل جماعيّ على الخادم | متعدّد: MSI / MSIX / ClickOnce / zip وغيرها. توقيت النشر يعتمد على الطرف الآخر |
| التراجع (Rollback) | يمكن الرجوع من جانب الخادم | يصعب الرجوع من الأجهزة الموزَّعة بسهولة. حفظ المُثبِّت القديم إلزاميّ |
| التوقيع | غالباً غير مطلوب (TLS من جانب البنية التحتيّة) | توقيع الشيفرة للملفّ التنفيذيّ/الحزمة إلزاميّ عمليّاً |
| بيئة البناء | يسهل الاكتمال على عامل تشغيل Linux | عامل تشغيل Windows مُفترَض |
| الاختبار | يسهل الاكتمال دون رأس (headless) | الاختبار الوحدويّ نفسه، لكن اختبار الواجهة يحتاج جلسة سطح مكتب |
| معنى «النشر (Deploy)» | حتّى الانعكاس في الإنتاج | نطاق CI هو «اكتمال ملفّ التوزيع». التثبيت خطوة منفصلة |
المهمّ هو السطر الأخير. في تطبيقات سطح المكتب، مخرج خطّ أنابيب CI/CD ليس «الانعكاس في الإنتاج»، بل «وضع ملفّ توزيع موقَّع في مكان يمكن أخذه في أيّ وقت». وما بعد ذلك (النشر لدى العملاء، التحديث التلقائيّ) هو موضوع تصميم أسلوب التوزيع، وهو المجال الذي تناولناه في «جدول القرار حول أساليب التوزيع». وبالمقابل، إذا تمّت التسوية عند هذا المخرج، يمكن بناء CI/CD لتطبيقات سطح المكتب بنفس الأدوات المستخدَمة في الويب.
3. البنية الدنيا ── البناء + الاختبار عبر GitHub Actions
هذا فقط ما ينبغي إدخاله أوّلاً. بما أنّ عوامل التشغيل المُستضافة من GitHub تُخصَّص لها آلة افتراضيّة جديدة لكلّ مهمّة1، يعمل البناء والاختبار عند كلّ push على Windows نظيف، فتنكشف على الفور حالة الاعتماد على «SDK موجود فقط على حاسوب ذلك الشخص».
name: build-and-test
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: windows-latest # WinForms / WPF تتطلّب عامل تشغيل Windows
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Restore
run: dotnet restore
- name: Build
run: dotnet build --configuration Release --no-restore
- name: Test
run: dotnet test --configuration Release --no-build
- name: Publish
run: dotnet publish src/MyApp/MyApp.csproj -c Release -o publish
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: MyApp
path: publish
نضيف 3 نقاط توضيحيّة.
أوّلاً، runs-on: windows-latest هو الأساس. مشاريع WinForms / WPF لها TargetFramework من نوع TFM خاصّ بـWindows مثل net8.0-windows، وهي مشاريع SDK سطح مكتب لـ.NET مُفعَّل فيها UseWindowsForms أو UseWPF.3 بدقّة، يمكن للتجميع (compile) وحده أن يُبنى حتّى على عامل تشغيل Linux بتفعيل EnableWindowsTargeting، لكن الخطوات التي تتضمّن تنفيذاً فعليّاً مثل dotnet test تحتاج بيئة Windows، لذا في هذه البنية التي تجري فيها البناء والاختبار في مهمّة واحدة، نستخدم عامل تشغيل Windows ببساطة. أمّا مع .NET Framework 4.x (csproj بالصيغة القديمة)، فيُستخدَم MSBuild وNuGet CLI بدلاً من dotnet build، لكن كلاهما مثبَّت مسبقاً على عامل تشغيل Windows، والفكرة نفسها.
ثانياً، احفظ النواتج دائماً عبر actions/upload-artifact. كون «نواتج ذلك البناء بأكملها قابلة للأخذ من GitHub» هو جوهر الخروج من الاعتماد على حاسوب شخص معيّن. حتّى نسخة التحقّق العاجلة تصبح مجرّد تنزيل zip من شاشة Actions.
ثالثاً، في هذه المرحلة لا نقوم بالتوقيع ولا التوزيع بعد. بهذه البنية الدنيا وحدها، نحصل على ضمانين: «main قابل دائماً للبناء والاختبار» و«يمكن لأيّ شخص أخذ نفس النواتج»، وبحسب خبرة الكاتب، هذا يحلّ معظم مشاكل الفِرَق الصغيرة.
4. الترقيم التلقائيّ لرقم الإصدار ── إصدار مدفوع بالوسم (Tag)
المرحلة التالية هي حلّ مشكلة «هذا الملفّ المضغوط، ما رقم إصداره؟». في تشغيل البناء اليدويّ، حادث نسيان تحديث Version في csproj شائع، حيث يبقى نفس الرقم 1.0.0 لعدّة أجيال. الحلّ الواقعيّ في العمل الفعليّ هو الإصدار المدفوع بالوسم، حيث يؤدّي وضع وسم مثل v1.2.3 على الالتزام (commit) الذي تريد إصداره إلى تشغيل سير عمل يأخذ الإصدار من اسم الوسم ويحقنه في البناء.
name: release
on:
push:
tags: [ 'v*' ]
jobs:
release:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
# لا يُشغَّل سير عمل البناء + الاختبار من الفصل 3 عند دفع الوسم (tag)،
# لذا نُشغِّل الاختبار هنا أيضاً قبل إنشاء نواتج الإصدار
- name: Test
run: dotnet test --configuration Release
- name: Publish with version from tag
shell: pwsh
run: |
$version = $env:GITHUB_REF_NAME.TrimStart('v') # v1.2.3 -> 1.2.3
dotnet publish src/MyApp/MyApp.csproj `
-c Release -o publish `
-p:Version=$version
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: MyApp-${{ github.ref_name }}
path: publish
عند تمرير -p:Version=1.2.3 كخاصّيّة MSBuild، يتولّد افتراضيّاً في مشاريع .NET SDK كلّ من AssemblyVersion وFileVersion (من جزء Version قبل اللاحقة) وInformationalVersion (من Version نفسه).4 بهذا، يبقى في csproj فقط قيمة مؤقّتة للتطوير، بينما رقم الإصدار الرسميّ عند الإصدار تملكه الوسوم وحدها، ما يحقِّق إدارة موحَّدة.
نقطة تنبيه واحدة: لنواتج actions/upload-artifact مدّة احتفاظ (الافتراضيّ 90 يوماً)، وتُحذَف بعد انتهائها. في تطبيقات سطح المكتب، يلزم الاحتفاظ طويل الأمد بالمُثبِّتات القديمة للتراجع، لذا يجب نشر نواتج بناء الوسوم إلى مكان تخزين دائم، مثل إرفاقها بإصدار (release) على GitHub، مع اعتبار نواتج Actions مجرّد وسيلة نقل مؤقّتة.
الميزة هي بقاء التشغيل ضمن Git. تُحدَّد «أيّ التزام هو 1.2.3 في بيئة العميل» عبر الوسم بشكل قاطع، ويتطابق رقم إصدار الملفّ الظاهر في خصائص الملفّ التنفيذيّ (EXE) مع وسم Git آليّاً. علاوة على ذلك، منذ .NET 8 SDK فما بعده، يُضاف تجزئة (hash) التزام Git (SourceRevisionId) إلى InformationalVersion افتراضيّاً4، فإن عرضتَ هذه القيمة في شاشة الإصدار، يمكن تحديد الالتزام مباشرة من الناتج.
5. إدماج توقيع الشيفرة في CI ── العائق الأكبر هنا
الفِرَق التي أتمتت البناء وترقيم الإصدار بسلاسة، تتوقّف تقريباً بالتأكيد عند التوقيع. أسباب كون توقيع الشيفرة إلزاميّاً عمليّاً لتطبيقات Windows الموزَّعة خارج المتجر (SmartScreen، منتجات أمن الشركات، كشف التلاعب) مرتَّبة في «SmartScreen وتوقيع الشيفرة»، لذا نركِّز هنا على أين وكيف نُنفِّذه داخل CI.
5.1 الصيغة الأساسيّة لـsigntool
تنفيذ التوقيع نفسه أمر واحد. signtool مشمول في Windows SDK، ومتاح أيضاً على عامل تشغيل Windows في GitHub. في SDK الحاليّ، تحديد /fd (ملخّص الملفّ) و/td (ملخّص الطابع الزمنيّ) إلزاميّ، وSHA256 موصى به.8
signtool sign /f MyCert.pfx /p $env:PFX_PASSWORD `
/fd SHA256 /tr http://timestamp.digicert.com /td SHA256 `
publish\MyApp.exe
الطابع الزمنيّ (/tr) اختياريّ لكن أضِفه دائماً. مع وجود الطابع الزمنيّ، يمكن إثبات «أنّه كان صالحاً وقت التوقيع» حتّى بعد انتهاء صلاحيّة الشهادة، فيستمرّ توقيع الملفّ الموزَّع في العمل.86
5.2 نوع الشهادة والواقع في دمج CI
المشكلة ليست في الأمر بل في أين يوضَع المفتاح الخاصّ. تختلف طريقة الدمج مع CI جذريّاً حسب شكل الحصول على الشهادة.
| شكل الشهادة | موقع المفتاح الخاصّ | الدمج مع CI | ملاحظات |
|---|---|---|---|
| خدمة توقيع سحابيّة (Azure Artifact Signing = المعروفة سابقاً بـTrusted Signing، وغيرها) | جانب السحابة | دمج سهل (الخيار الأساسيّ). مصمَّمة أصلاً للتكامل مع GitHub Actions وغيرها | مقيَّدة بدول ومناطق مُتاحة (للشركات: الولايات المتّحدة، كندا، الاتّحاد الأوروبيّ، المملكة المتّحدة وغيرها)5 |
| شهادة OV (مُصدَرة حديثاً بعد يونيو 2023) | HSM / رمز USB إلزاميّ | غير ممكن كما هي لعدم إمكانيّة إدخال الرمز في عامل التشغيل. ممكن عبر خيار HSM سحابيّ من جهة الاعتماد | حسب متطلّب CA/Browser Forum5 |
| شهادة EV | HSM / رمز USB | نفس ما سبق | أُلغي أثر الثقة الفوريّ لـSmartScreen في 2024. من الناحية التشغيليّة يُعامَل مثل OV5 |
| ملفّ PFX تقليديّ (مُصدَر سابقاً · CA داخليّ · توقيع ذاتيّ) | ملفّ | حفظه بصيغة Base64 في الأسرار واستعادته (أدناه) | لم يعد بالإمكان الحصول على هذا الشكل من حيث المبدأ للحصول الجديد لأغراض التوزيع العامّ |
بعبارة أخرى، النمط الشائع في نتائج البحث «وضع PFX في أسرار GitHub والتوقيع عبر signtool» لا يزال صالحاً في حال CA الداخليّ أو PFX القائم، لكنّ الافتراض ينهار في حالة الحصول على شهادة رسميّة جديدة. عند البناء من الصفر، الواقعيّ هو النظر في خدمة توقيع سحابيّة تدعم التكامل مع CI منذ البداية.5 إن كان التشغيل بواسطة رمز USB مستمرّاً، فسيصبح التكوين توفيقيّاً: إبقاء خطوة التوقيع فقط يدويّاً أو على عامل تشغيل ذاتيّ الاستضافة مزوَّد بالرمز.
5.3 ملاحظات إدارة الأسرار
هذه هي القواعد الثابتة لتحميل نمط PFX (CA داخليّ · شهادة قائمة) على CI.
- حوِّل PFX إلى سلسلة Base64 وخزِّنه في أسرار GitHub، وأعِد بناءه كملفّ داخل المهمّة. هذه الخطوة موثَّقة رسميّاً من GitHub Docs كطريقة للتعامل مع البيانات الثنائيّة عبر الأسرار.9
- اجعل كلمة المرور سرّاً منفصلاً. تُخفى قيم الأسرار تلقائيّاً في السجلّات (log)9، لكن هذا لا يشمل القيم المشتقّة المُعالَجة. لا تُمرِّر متغيّرات البيئة إلى ما وراء خطوة التوقيع.
- لا تُمرَّر الأسرار (باستثناء
GITHUB_TOKEN) إلى طلبات السحب (pull requests) الآتية من fork.9 لكن بما أنّ المهمّة نفسها تعمل بسرٍّ فارغ، ستفشل خطوة الاستعادة أعلاه بسبب فكّ ترميز Base64 لسلسلة فارغة. اجعل خطوة التوقيع منفصلة ضمن سير عمل إصدار يبدأ بالوسم كما في الفصل 4 (لا يُشغَّل عند طلبات السحب من fork)، أو أضِف شرطاً مثلif: github.event_name != 'pull_request'لتخطّيه صراحةً.
- name: Restore signing certificate
shell: pwsh
run: |
$bytes = [Convert]::FromBase64String($env:PFX_BASE64)
[IO.File]::WriteAllBytes("$env:RUNNER_TEMP\sign.pfx", $bytes)
env:
PFX_BASE64: ${{ secrets.SIGNING_PFX_BASE64 }}
نقطة حسّاسة أخرى هي تضييق سير العمل القادر على الوصول إلى مفتاح التوقيع. بما أنّ من يملك صلاحيّة الكتابة على المستودع يستطيع إعادة كتابة سير العمل، يجب وضع أسرار التوقيع في Environment مزوَّد بموافقة (approver)، بحيث لا يمكن لغير سير عمل الإصدار الوصول إليها. الملفّ الثنائيّ الموقَّع هو الإثبات نفسه على أنّ «الشركة صنعته»، لذا صمِّم التعامل مع المفتاح بنفس مستوى حدود الثقة الخاصّة ببنية توزيع التحديث التلقائيّ (راجع هذا المنظور في «أمن التحديث التلقائيّ»).
6. جدول قرار لدمج CI/CD حسب شكل التوزيع
بعد الوصول إلى التوقيع، الأخير هو شكل ملفّ التوزيع. نترك اختيار الأسلوب نفسه لـ«جدول القرار حول أساليب التوزيع»، ونقارن هنا من منظور CI/CD فقط.
| شكل التوزيع | سهولة الإنشاء عبر CI | وسيلة الإنشاء عبر CI | متطلّب التوقيع | التحديث التلقائيّ |
|---|---|---|---|---|
| xcopy (توزيع zip) | الأبسط | dotnet publish + الضغط فقط |
توقيع EXE/DLL (موصى به) | لا يوجد (نشر يدويّ) |
| xcopy + محدِّث ذاتيّ الصنع | سهل (البناء نفسه). تصميم بثّ التحديث ثقيل بذاته | dotnet publish + توليد بيان (manifest) |
توقيع EXE + تصميم التحقّق من ملفّات التحديث إلزاميّ | ذاتيّ الصنع (يحتاج تصميم حدود ثقة) |
| MSI | متوسّط | تشغيل أداة مثل WiX عبر سطر الأوامر | توقيع ملفّ MSI (موصى به~إلزاميّ فعليّاً) | لا يوجد (يحتاج آليّة توزيع منفصلة) |
| MSIX | متوسّط | MSBuild / MakeAppx + signtool | توقيع الحزمة إلزاميّ (لا يمكن التثبيت دون توقيع)6 | ممكن عبر App Installer وغيره |
| ClickOnce | طبائع خاصّة قويّة | msbuild /target:publish + ملفّ نشر (dotnet CLI غير مدعوم)7 |
توقيع البيان + توقيع EXE | مُدمَج (الغرض الرئيسيّ من الأسلوب) |
توضيحات إضافيّة.
- xcopy (zip): سير عمل الفصلين 3 و4 يكاد يكون الشكل النهائيّ كما هو. مهما كان شكل التوزيع النهائيّ، تمرير هذا الشكل مرّة واحدة هو الطريق الأقصر.
- MSI: ضع تعريف المُثبِّت (WiX وغيره) في المستودع وابنِه عبر CLI. جوهر العمل ليس الإنشاء نفسه بل تصميم «ما الذي يتضمّنه MSI» (تسجيل الخدمة، per-machine/per-user).
- MSIX: لا يسمح Windows بتثبيت حزمة MSIX غير موقَّعة، لذا لا يكتمل تحويلها إلى CI إلّا مع أتمتة التوقيع معاً.6 وبالمقابل، إذا كانت بنية التوقيع جاهزة، فهو شكل سهل الدمج مع CI. مع توزيع عبر Microsoft Store، يعيد المتجر نفسه التوقيع، ما يعني حلّاً بديلاً يُغني عن شهادة خاصّة بك.5
- ClickOnce: لا يمكن النشر عبر dotnet CLI، بل يُستخدَم
msbuild /target:publish /p:PublishProfile=...بتحديد ملفّ نشر (.pubxml). في بيئة IDE، يُضاف رقم المراجعة (ApplicationRevision) تلقائيّاً عند كلّ نشر، لكن لا يُضاف عبر سطر الأوامر7، لذا يصبح تصميم تمرير الإصدار صراحةً عبر الإصدار المدفوع بالوسم في الفصل 4 إلزاميّاً. تنبيه إضافيّ: يُحدَّد التحديث في ClickOnce ليس بواسطة-p:Version(معلومات التجميع) بل بواسطة إصدار جانب النشر (ApplicationVersion/ApplicationRevision)، لذا إن لم تُمرَّر القيمة المُنشأة من الوسم بصيغة رباعيّة الأجزاء بشكل منفصل مثل/p:ApplicationVersion=1.2.3.0، لن يُتعرَّف على الإصدار الجديد كتحديث. الآليّة ومدى الملاءمة مشروحة في «ما هو ClickOnce».
من منظور CI/CD فقط، «البدء بـzip، ثمّ إضافة MSIX أو MSI كمهمّة عند استقرار متطلّبات التوزيع» هو التقدّم الأقلّ زيادة تراكميّة. المرحلة السابقة (البناء، الاختبار، الإصدار) مشتركة بين جميع الأشكال، لذا حتّى لو استُبدلت خطوة شكل التوزيع لاحقاً، لا تُهدَر الأصول المبنيّة.
7. إلى أيّ مدى نُؤتمِت الاختبار
أخيراً، خطّ الفصل حول إلى أيّ مدى نفرض الاختبار كبوّابة إلزاميّة في CI.
| طبقة الاختبار | التعامل في CI | السبب |
|---|---|---|
| الاختبار الوحدويّ (المنطق) | بوّابة إلزاميّة. يُنفَّذ عند كلّ طلب سحب (pull request) | سريع ومستقرّ ويعمل كما هو على عامل تشغيل Windows |
| اختبار التكامل بلا واجهة (قاعدة بيانات، I/O ملفّات) | إلزاميّ من حيث المبدأ. إن كان بطيئاً، يُفصَل إلى تشغيل ليليّ | تهيئة الاعتماديّة الخارجيّة تحتاج جهداً، لكن قيمة الأتمتة عالية |
| اختبار الواجهة التلقائيّ (تدخينيّ / smoke) | عدد قليل في مهمّة منفصلة. من البدء إلى انتقال الشاشات الرئيسيّة فقط | يحتاج جلسة سطح مكتب إلزاميّاً وعوامل عدم استقرار كثيرة |
| اختبار الواجهة التلقائيّ (شامل) | لا يُجعَل بوّابة في CI | غالباً ما تفوق تكلفة الصيانة الفائدة |
القيمة الأعلى للأتمتة في تطبيقات سطح المكتب ليست في الواجهة بل فيما تحتها. عندما يكون منطق الأعمال مدفوناً في الشيفرة الخلفيّة للشاشة (code-behind)، يتعذّر كتابة الاختبار الوحدويّ، لذا فصل المنطق عن الشاشة نفسه هو الاستثمار المسبَق لـCI/CD. اقتصر الاختبار التلقائيّ للواجهة على اختبار تدخينيّ من نوع «شغِّل، وسجِّل الدخول، وافتح الشاشات الرئيسيّة»، وشغِّله عبر مُشغِّل منفصل مثل تشغيل ليليّ ─ هذا هو الحلّ الواقعيّ. اختبارات الواجهة على عامل التشغيل تحمل فخاخاً كثيرة تتعلّق بجلسة الشاشة والدقّة والتوقيت، وهذا المجال متناوَل حتّى فخاخ CI والتنفيذ غير المُراقَب في «الاختبار التلقائيّ للواجهة لتطبيقات سطح مكتب Windows».
8. الخلاصة
- إذا عُرِّف مخرج CI/CD لتطبيقات سطح المكتب بأنّه «اكتمال ملفّ توزيع موقَّع»، يمكن بناؤه بنفس أدوات الويب.
- البنية الدنيا هي
windows-latest+actions/checkout+actions/setup-dotnet+dotnet build / test+actions/upload-artifact. هذا وحده يزيل خطر «لا يمكن البناء إلّا على حاسوب المطوِّر».12 - بما أنّ WinForms / WPF يستهدفان TFM خاصّاً بـWindows مثل
net8.0-windows، فعامل تشغيل Windows مُفترَض.3 - تُوحَّد إدارة الإصدار عبر إصدار مدفوع بالوسم: وسم
v1.2.3← حقن-p:Version.4 - التوقيع هو أكبر عائق للأتمتة عبر CI. بعد أن أصبح حفظ HSM إلزاميّاً حتّى لشهادات OV، فإنّ الخدمة السحابيّة للتوقيع هي الخيار الأساسيّ إن أردتَ الإكمال داخل CI، أمّا نمط PFX + الأسرار فمناسب لحالة CA الداخليّ أو الشهادة القائمة.59
- سهولة دمج شكل التوزيع مع CI بالترتيب: xcopy (zip) ← MSI / MSIX ← ClickOnce. يتطلّب MSIX توقيعاً إلزاميّاً6، ويتطلّب ClickOnce الانتباه إلى
msbuild /target:publishوعدم الإضافة التلقائيّة لرقم المراجعة.7 - اجعل الاختبار الوحدويّ بوّابة إلزاميّة في CI، واقتصر اختبار الواجهة على التدخينيّ في مهمّة منفصلة. فصل المنطق عن الشاشة هو الاستثمار المسبَق.
مقالات ذات صلة
- كيف نختار أسلوب توزيع تطبيقات Windows - جدول قرار لـ MSI / MSIX / ClickOnce / xcopy / محدِّث ذاتيّ الصنع
- لماذا تظهر رسالة «حمى Windows جهاز الكمبيوتر» - SmartScreen وتوقيع الشيفرة
- ما هو ClickOnce - الآليّة والتحديث والحالات المناسبة وغير المناسبة من منظور عمليّ
- المحدِّث التلقائيّ حدود ثقة - لماذا لا يكفي HTTPS وحده
- الاختبار التلقائيّ للواجهة لتطبيقات سطح مكتب Windows
مجالات الاستشارة ذات الصلة
تتعامل شركة Komura Soft LLC، إلى جانب تطوير تطبيقات WinForms / WPF، مع الانتقال من تشغيل البناء اليدويّ إلى CI/CD، وتصميم خطّ أنابيب البناء والتوقيع والتوزيع عبر GitHub Actions، وجعل تطبيقات سطح المكتب القائمة قابلة للاختبار (فصل المنطق).
- تطوير تطبيقات Windows
- تعديل وصيانة برمجيّات Windows القائمة
- الاستشارة التقنيّة ومراجعة التصميم
- التواصل معنا
المراجع
-
GitHub Docs، GitHub-hosted runners reference. حول تسميات عوامل التشغيل مثل
windows-latest، وتخصيص آلة افتراضيّة جديدة لكلّ مهمّة، ومجّانيّة عوامل التشغيل القياسيّة في المستودعات العامّة. ↩ ↩2 ↩3 -
Microsoft Learn، GitHub Actions and .NET. حول CI/CD لـ.NET عبر GitHub Actions، ودور actions/checkout وactions/setup-dotnet، واستخدام dotnet restore / build / test / publish داخل سير العمل. ↩ ↩2
-
Microsoft Learn، MSBuild reference for .NET Desktop SDK projects. حول تحديد مشاريع WinForms / WPF لـTFM خاصّ بـWindows مثل
net8.0-windows، وتفعيل SDK سطح مكتب .NET عبرUseWindowsForms/UseWPF. ↩ ↩2 ↩3 -
Microsoft Learn، Set assembly attributes in a project file. حول توليد
AssemblyVersion/FileVersion(باستثناء اللاحقة) وInformationalVersionافتراضيّاً من خاصّيّةVersion، وإضافةSourceRevisionId(تجزئة الالتزام) إلىInformationalVersionمنذ .NET 8 SDK فما بعده. ↩ ↩2 ↩3 ↩4 -
Microsoft Learn، Code signing options for Windows app developers. حول إلزاميّة حفظ HSM/رمز عتاديّ للمفتاح الخاصّ لشهادات OV منذ يونيو 2023 وفق متطلّب CA/Browser Forum، وإلغاء تجاوز SmartScreen الفوريّ لشهادات EV في 2024، وأنّ Azure Artifact Signing (المعروفة سابقاً بـTrusted Signing) لا تحتاج رمزاً وتتكامل مع GitHub Actions وغيرها مع قيود على مناطق الاستخدام، وإعادة توقيع Microsoft لتوزيع MSIX عبر Store. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7
-
Microsoft Learn، Sign an MSIX package. حول إلزاميّة توقيع شيفرة صالح لحزمة MSIX من جانب Windows، وبقاء التحقّق من التوقيع صالحاً بعد انتهاء صلاحيّة الشهادة بفضل الطابع الزمنيّ. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn، Build .NET ClickOnce applications from the command line. حول حاجة نشر ClickOnce في .NET إلى
msbuild /target:publishبتحديد ملفّ نشر، وعدم إضافةApplicationRevisionتلقائيّاً في بناء سطر الأوامر. ↩ ↩2 ↩3 ↩4 -
Microsoft Learn، SignTool. حول شمول SignTool ضمن Windows SDK، وإلزاميّة تحديد
/fdو/tdفي البناء الحاليّ مع توصية SHA256، وتحديد طابع زمنيّ RFC 3161 عبر/tr. ↩ ↩2 -
GitHub Docs، Using secrets in GitHub Actions. حول الإخفاء التلقائيّ لقيم الأسرار من السجلّات، وخطوات حفظ البيانات الثنائيّة مثل الشهادات بصيغة Base64 في الأسرار واستعادتها داخل المهمّة، وعدم تمرير الأسرار إلى سير العمل المُشغَّل من fork. ↩ ↩2 ↩3 ↩4
مقالات ذات صلة
أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.
أيقونات علبة النظام والإشعارات المنبثقة في تطبيقات Windows — مزالق NotifyIcon وكيفية اختيار AppNotification المناسب
دليل عملي لإبقاء تطبيق Windows الخاص بالأعمال مقيمًا في علبة النظام (منطقة الإشعارات) وإخطار المستخدم عبر الإشعارات المنبثقة (Toast). يتن...
الاختبار الآليّ لواجهة المستخدم في تطبيقات سطح مكتب Windows ── آليّة UI Automation وبناء اختبارات لا تنكسر بسهولة باستخدام FlaUI
نرتّب الاختبار الآليّ لواجهة المستخدم في تطبيقات WinForms/WPF انطلاقاً من آليّة عمل Windows UI Automation. نشرح التنفيذ الأدنى عبر FlaUI،...
إدارة إصدارات مخطّط قاعدة بيانات تطبيقات الأعمال ── ممارسات الترحيل (migration) لمنع «اختلاف قاعدة البيانات من عميل لآخر»
دليل عمليّ لإدارة إصدارات مخطّط قاعدة بيانات تطبيقات الأعمال الموزَّعة على عملاء متعدّدين. نشرح PRAGMA user_version وتنفيذ الترحيل الأمام...
عندما يُعامَل تطبيق Windows الذي طوّرته شركتك بوصفه فيروساً ── التعامل مع الكشف الخاطئ في Microsoft Defender والتعايش مع أثره على الأداء
نرتّب الإجراء الرسميّ عند كشف Microsoft Defender تطبيق Windows الذي طوّرته شركتك خطأً. نشرح آليّة برامج مكافحة الفيروسات الحديثة، والإبلا...
إلى متى ستستمرّ تطبيقات VB6 في العمل ── وضع دعم بيئة التشغيل والمسار العمليّ للترحيل إلى .NET
إلى متى تستمرّ تطبيقات VB6 في العمل؟ يُنظّم هذا المقال التناقض بين سياسة دعم بيئة تشغيل VB6 (المدعومة حتّى على Windows 11) وحقيقة انتهاء ...
أين يتصل هذا الموضوع
ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.
تطوير تطبيقات ويندوز
ندعم تطوير برامج ويندوز للأعمال، وتكامل الأجهزة، وأدوات التواصل.
صيانة وتحديث برامج ويندوز الحالية
ندعم إضافة الميزات، والصيانة، والتحديث المتدرّج لبرامج ويندوز الحالية.
الأسئلة الشائعة
أسئلة شائعة حول موضوع هذه المقالة.
- على أيّ عامل تشغيل (runner) في GitHub Actions ينبغي تشغيل بناء تطبيقات WinForms / WPF؟
- استخدم عوامل تشغيل Windows مثل windows-latest. تستهدف مشاريع WinForms / WPF إطار عمل مستهدَف (TFM) خاصّاً بـWindows مثل net8.0-windows، ويحتاج التحقّق من عمل نواتج البناء وتنفيذ الاختبارات عبر dotnet test إلى بيئة Windows. بما أنّ عوامل التشغيل المُستضافة من GitHub تُخصَّص لها آلة افتراضيّة جديدة لكلّ مهمّة (job)، يُحصَل على بناء قابل لإعادة الإنتاج لا يعتمد على حاسوب المطوِّر. في المستودعات العامّة، عوامل التشغيل القياسيّة مجّانيّة، وفي المستودعات الخاصّة تخضع للفوترة بالدقيقة.
- هل يمكن أتمتة توقيع الشيفرة بالكامل عبر CI؟
- يعتمد على طريقة حفظ الشهادة. الطريقة السابقة المتمثِّلة في وضع ملفّ PFX في الأسرار (secrets) والتوقيع عبر signtool لم تعد صالحة من حيث المبدأ للشهادات المُصدَرة حديثاً، بعد أن أصبح متطلّب CA/Browser Forum منذ يونيو 2023 يفرض حفظ المفتاح الخاصّ لشهادات OV الرسميّة في HSM (عتاد). لا يمكن إدخال الشهادات من نوع رمز USB في عامل تشغيل سحابيّ، لذا إن أردتَ الأتمتة الكاملة عبر CI، فالحلّ الواقعيّ هو خدمة توقيع سحابيّة مثل Azure Artifact Signing (المعروفة سابقاً باسم Trusted Signing)، أو المرور عبر HSM سحابيّ توفِّره جهة الاعتماد (CA). أمّا إذا استمرّ استخدام الرمز، فسيصبح التكوين إبقاء خطوة التوقيع فقط يدويّاً أو على عامل تشغيل ذاتيّ الاستضافة.
- من أين ينبغي البدء بالأتمتة؟
- ابدأ بأتمتة البناء + الاختبار فقط. مجرّد جعل dotnet build / dotnet test يعملان على عامل تشغيل windows-latest عند كلّ push يزيل أكبر خطر، وهو حالة «لا يمكن البناء إلّا على حاسوب المطوِّر» أو «لا يُلاحَظ انكسار البناء بسبب الدمج (merge) إلّا قبيل التوزيع مباشرة». يمكن إضافة أتمتة التوقيع وإنشاء المُثبِّت (installer) والتوزيع تدريجيّاً بعد ذلك، ومحاولة بناء كلّ شيء دفعة واحدة من البداية غالباً ما تتوقّف عند مسائل التوقيع.
- هل تختلف سهولة بناء CI/CD حسب شكل التوزيع (MSI / MSIX / ClickOnce / xcopy)؟
- تختلف اختلافاً كبيراً. توزيع xcopy (zip) هو الأبسط لأنّه مجرّد ضغط لناتج dotnet publish. يمكن إدماج MSIX في CI عبر MSBuild وsigntool، لكن توقيع الحزمة إلزاميّ. يمكن أتمتة MSI عبر استدعاء أدوات مثل WiX من CI. أمّا ClickOnce فلا يمكن نشره عبر dotnet CLI، بل يحتاج مزيجاً من msbuild /target:publish وملفّ (profile) النشر، مع الانتباه إلى أنّ رقم المراجعة (revision) لا يُضاف تلقائيّاً عند استخدام سطر الأوامر. ينبغي إدراج مدى سهولة الدمج مع CI ضمن معايير القرار عند تحديد أسلوب التوزيع.
الملف الشخصي للمؤلف
صفحة الملف الشخصي لمؤلف المقالة.
غو كومورا
مؤسّس شركة كومورا سوفت ذ.م.م.
يركّز على تطوير برامج ويندوز، والاستشارات التقنية، والتحقيق في الأخطاء، ويتميّز في المشاريع التي تبقى فيها الأصول القديمة ناشطة، وفي تشخيص الأعطال التي يصعب تحديد سببها.
روابط عامة