لدينا مشروع مع عملية CI / CD مخصصة. عندما ينتهي المطور من المهمة ويضخ تغييراته في التطوير ، يبدأ التشغيل تلقائيًا ، مما يضع الإصدار الجديد من التطبيق في بيئة الاختبار. في عالم مثالي ، يتعرف المختبر تلقائيًا على المهام التي تم إكمالها ، وعلى أي بيئة يتم نشرها. في هذه الحالة ، يصبح سير العمل مستمرًا دون انقطاع ويتطلب اتصالًا أقل ، مما يصرف الانتباه عن العمل المركّز. في الممارسة العملية ، كل شيء ليس وردية للغاية.
وفي صباح أحد الأيام ، سألني قائد الفريق: "هل يمكنك عمل شيء مثل TFS بحيث تعلق العلامات المرتبطة بالبناء العلامة المحددة بعد المرور عبر هذه البنية؟"
قررت تنفيذ مهمة build / release الخاصة بي للمهمة. علاوة على ذلك ، فإن مصادر جميع مهام البناء على
جيثب ، وجميع المعلومات المتاحة.
تميز مهمتنا لون المهمة ، الذي تم إكماله ، ولكن لم يتم اختباره. وبفضل هذا ، سوف يلاحظ المطور على الفور ما إذا كان قد نسي وضع العلامة المطلوبة ، ورأت QA على الفور ما يجب التحقق منه. هذا يصور حالة المهام ويسرع العمل في المشروع.
في هذه المقالة سأتحدث عن تنفيذ المنطق اللازم ، والتغليف في التمديد. لذلك إذا كنت مهتمًا بكيفية إنشاء مثل هذا البرنامج المساعد ، مرحبًا بك في cat.
لمعظم الصبر:
جيثب وتمديد جاهز في
السوق .

لدى Azure DevOps القدرة على إنشاء عوامل تصفية تسمح لك بتلوين الأقنعة على اللوحة بألوان مختلفة.

نحن مهتمون بالمهام التالية:
- الانتهاء
- سكب في بيئة الاختبار
- لم يتم التحقق بعد
بالنسبة للعناصر ، تعد العلامات b و c أكثر ملاءمة ، لكن إعدادها يدويًا أمر مثير للاشمئزاز ، وينسى الجميع القيام بذلك. لذلك ، سنكتب امتدادًا ، والذي بعد النشر سيتم تثبيته تلقائيًا.
وبالتالي ، نحتاج إلى خطوة build / release المخصصة لتقليل العامل البشري (نسي المطور وضع العلامة) ولمساعدة QA (يمكنك على الفور معرفة ما يلزم التحقق منه).
المتطلبات الأساسية
لتطوير الامتداد ، نحتاج إلى:
- IDE المفضل
- تثبيت TypeScript + node.js + npm (الآن قمت بتثبيت الإصدارات 3.5.1 \ 12.4 \ 6.9.0)
- tfx-cli - مكتبة لتمديد التغليف (npm i -g tfx-cli).
لاحظ وجود العلم -g
لدى Microsoft بعض
الوثائق الجيدة التي تكون في المقدمة وتحدث عن كيفية إنشاء بعض الامتدادات. بالإضافة إلى ذلك ، بنفس الطريقة ، يوجد رصيف لإنشاء مهمة build \ release.
في رأيي ، هناك عيوب في كلا المقالين من وجهة نظر تفصيلية أو شرح بعض النقاط ، لذلك سأعتمد عليها ، مع التركيز على تلك النقاط التي بدا لي في الواقع غير واضحة تمامًا.
بشكل عام ، يمكنك كتابة خطوة build \ release بعدد كبير من اللغات. سأقدم مثالا على TypeScript.
لماذا TypeScript؟
تمت كتابة الإصدار الأول من build step'a في PowerShell'e ، وفريقنا واثنين فقط من الأشخاص يعرفون ذلك. على الفور تقريبًا ، واجهنا حقيقة أنه إذا حاولت إضافة مهمة إلى البنية التي تعمل على عامل بناء عامل ميناء ، فلن يكون هناك PowerShell ولن تعمل المهمة ببساطة. بالإضافة إلى ذلك ، من وقت لآخر ، خلعت أنواع مختلفة من الأخطاء من الناس ، والتي نسبت إلى PowerShell kooky. ومن هنا الاستنتاج - يجب أن يكون الحل عبر منصة.
هيكل المشروع
|--- README.md |--- images |---extension-icon.png |--- TaskFolder ( build\release step'a) |--- vss-extension.json ( )
بعد ذلك ، نحتاج إلى تثبيت المكتبة لتنفيذ build'a
- TaskFolder مؤتمر نزع السلاح
- npm init
- npm install azure-pipelines-task-lib - حفظ & npm install @ types / node --save-dev && npm install @ types / q --save-dev
- tsc -
تمديد التنمية
بادئ ذي بدء ، داخل TaskFolder ، نحتاج إلى إنشاء ملف task.json - هذا هو ملف البيان لخطوة الإنشاء نفسها. أنه يحتوي على معلومات الخدمة (الإصدار ، المنشئ ، الوصف) ، بيئة لإطلاق وتكوين جميع المدخلات ، والتي سنرىها في المستقبل في النموذج.
أقترح دراسة هيكلها بمزيد من التفصيل في
الوثائق .
في حالتنا ، سيكون هناك 2 إدخال في النموذج - العلامة التي سنضيفها إلى عناصر العمل ، واختيار نوع خط الأنابيب (إنشاء أو إصدار).
"inputs": [ { "name": "pipelineType", "type": "pickList", "label": "Specify type of pipeline", "helpMarkDown": "Specify whether task is used for build or release", "required": true, "defaultValue": "Build", "options":{ "Build": "Build", "Release": "Release" } }, { "name": "tagToAdd", "type": "string", "label": "Tag to add to work items", "defaultValue": "", "required": true, "helpMarkDown": "Specify a tag that will be added to work items" } ]
بالاسم ، في الكود أدناه سنشير إلى قيمة كل من المدخلات.
إنشاء index.ts في TaskFolder وكتابة أول قطعة من التعليمات البرمجية
import * as tl from 'azure-pipelines-task-lib/task'; async function run() { try { const pipelineType = tl.getInput('pipelineType'); } catch (err) { tl.setResult(tl.TaskResult.Failed, err.message); } } run();
تجدر الإشارة إلى أن TFS لديه وثائق غنية جدًا على واجهة برمجة تطبيقات REST الحالية ، ولكن في الوقت الحالي ، كل ما نحتاج إلى القيام به هو الحصول على عناصر العمل مرفقة بالبناء.
تثبيت مكتبة لتنفيذ الاستعلام سهل
npm install request --save && npm install request-promise-native --save
إضافته إلى index.ts
import * as request from "request-promise-native";
ننفذ الوظيفة ، والتي من البنية الحالية ستحصل على عناصر العمل المرفقة
قليلا عن الترخيص
للوصول إلى REST API ، نحتاج إلى الوصول إلى accessToken
const accessToken = tl.getEndpointAuthorization('SystemVssConnection', true).parameters.AccessToken;
بعد ذلك ، قم بتعيين تفويض الرأس على "Bearer $ {accessToken}"
نعود إلى تلقي عناصر العمل المرتبطة بالبناء.
يمكن الحصول على خادم Url Azure DevOps واسم TeamProject من متغيرات البيئة كما يلي
const collectionUrl = process.env["SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"]; const teamProject = process.env["SYSTEM_TEAMPROJECT"];
async function getWorkItemsFromBuild() { const buildId = process.env["BUILD_BUILDID"]; const uri = `${collectionUrl}/${teamProject}/_apis/build/builds/${buildId}/workitems`; const options = createGetRequestOptions(uri); const result = await request.get(options); return result.value; }
function createGetRequestOptions(uri: string): any { let options = { uri: uri, headers: { "authorization": `Bearer ${accessToken}`, "content-type": "application/json" }, json: true }; return options; }
كرد على طلب GET بواسطة URL
${collectionUrl}/${teamProject}/_apis/build/builds/${buildId}/workitems
نحصل على هذا النوع من JSON
{ "count": 3, "value": [ { "id": "55402", "url": "https://.../_apis/wit/workItems/55402" }, { "id": "59777", "url": "https://.../_apis/wit/workItems/59777" }, { "id": "60199", "url": "https://.../_apis/wit/workItems/60199" } ] }
لكل عنوان url ، من خلال نفس واجهة برمجة تطبيقات REST ، يمكنك الحصول على بيانات حول عنصر العمل.
في الوقت الحالي ، طريقة التشغيل لدينا هي كما يلي.
طريقة الحصول على عناصر العمل من الإصدار مماثلة تقريبا لتلك الموصوفة بالفعل.
async function run() { try { const pipelineType = tl.getInput('pipelineType'); const workItemsData = pipelineType === "Build" ? await getWorkItemsFromBuild() : await getWorkItemsFromRelease(); catch (err) { tl.setResult(tl.TaskResult.Failed, err.message); }
الخطوة التالية هي الحصول على مجموعة العلامات الحالية لكل عنصر من عناصر العمل المستلمة وإضافة العنصر الذي حددناه.
دعنا نضيف طريقة التشغيل:
async function run() { try { const pipelineType = tl.getInput('pipelineType'); const workItemsData = pipelineType === "Build" ? await getWorkItemsFromBuild() : await getWorkItemsFromRelease(); workItemsData.forEach(async (workItem: any) => { await addTagToWorkItem(workItem); }); } catch (err) { tl.setResult(tl.TaskResult.Failed, err.message); } }
دعنا نفكر في إضافة علامة إلى عناصر العمل.
أولاً ، نحتاج إلى الحصول على العلامة التي أشرنا إليها في النموذج.
const tagFromInput = tl.getInput('tagToAdd');
لأن خطوتين إلى الوراء تلقينا عناوين url إلى واجهة برمجة التطبيقات لكل عنصر من عناصر العمل ، ثم بمساعدتهم يمكننا بسهولة طلب قائمة بالعلامات الحالية:
const uri = workItem.url + "?fields=System.Tags&api-version=2.0"; const getOptions = createGetRequestOptions(uri) const result = await request.get(getOptions);
استجابة لذلك ، حصلنا على هذا JSON:
{ "id": 55402, "rev": 85, "fields": { "System.Tags": "added-to-prod-package; test-tag" }, "_links": { "self": { "href": "https://.../_apis/wit/workItems/55402" }, "workItemUpdates": { "href": "https://.../_apis/wit/workItems/55402/updates" }, "workItemRevisions": { "href": "https://.../_apis/wit/workItems/55402/revisions" }, "workItemHistory": { "href": "https://.../_apis/wit/workItems/55402/history" }, "html": { "href": "https://..../web/wi.aspx?pcguid=e3c978d9-6ea1-406f-987d-5b03e24973a1&id=55402" }, "workItemType": { "href": "https://.../602fd27d-4e0d-4aec-82a0-dcf55c8eef73/_apis/wit/workItemTypes" }, "fields": { "href": "https://.../_apis/wit/fields" } }, "url": "https://.../_apis/wit/workItems/55402" }
نأخذ جميع العلامات القديمة ونضيف واحدة جديدة لهم:
const currentTags = result.fields['System.Tags']; let newTags = ''; if (currentTags !== undefined) { newTags = currentTags + ";" + tagFromInput; } else { newTags = tagFromInput; }
نرسل طلب تصحيح للعمل api item:
const patchOptions = getPatchRequestOptions(uri, newTags); await request.patch(patchOptions) function getPatchRequestOptions(uri: string, newTags: string): any { const options = { uri: uri, headers: { "authorization": `Bearer ${accessToken}`, "content-type": "application/json-patch+json" }, body: [{ "op": "add", "path": "/fields/System.Tags", "value": newTags }], json: true }; return options }
الجمعية والتعبئة والتغليف extension'a
من أجل جمال كل ما يحدث ، أقترح إضافة tsconfig.json إلى compilerOptions
"outDir": "dist"
. الآن ، إذا قمنا بتنفيذ الأمر tsc داخل TaskFolder ، فسنحصل على مجلد dist ، والذي سيكون index.js بداخله ، والذي سينتقل إلى الحزمة النهائية.
لأن يوجد index.js الخاص بنا في مجلد dist ومن ثم سنقوم بنسخه إلى الحزمة النهائية أيضًا ، نحتاج إلى إصلاح task.json قليلاً:
"execution": { "Node": { "target": "dist/index.js" } }
في vss-extension.json في قسم الملفات ، يجب أن تعلن بوضوح ما سيتم نسخه إلى الحزمة النهائية.
"files": [ { "path": "TaskFolder/dist", "packagePath": "TaskFolder/dist" }, { "path": "TaskFolder/node_modules", "packagePath": "TaskFolder/node_modules" }, { "path": "TaskFolder/icon.png", "packagePath": "TaskFolder/icon.png" }, { "path": "TaskFolder/task.json", "packagePath": "TaskFolder/task.json" } ]
الخطوة الأخيرة - نحن بحاجة إلى حزمة ملحق لدينا.
للقيام بذلك ، قم بتشغيل الأمر:
tfx extension create --manifest-globs ./vss-extension.json
بعد التنفيذ ، نحصل على ملف * .vsix ، والذي سيتم تثبيته مرة أخرى في TFS.
يعد ملف PS * .vsix أرشيفًا أساسيًا ، يمكنك فتحه بسهولة من خلال 7-zip ، على سبيل المثال ، ومعرفة أن كل ما تحتاجه موجود بالفعل.
أضف بعض الجمال
إذا كنت ترغب في الحصول على صورة عند تحديد خطوة الإنشاء الخاصة بك أثناء إضافتها إلى خط الأنابيب ، فيجب وضع هذا الملف بجوار task.json وتسميته icon.png. لا تحتاج إلى إجراء أي تغييرات على task.json نفسها.
يمكنك إضافة قسم إلى vss-extension.json:
"icons": { "default": "images/logo.png" }
سيتم عرض هذه الصورة في معرض الإضافات المحلية.
تثبيت التمديد
- انتقل إلى tfs_server_url / _gallery / management
- انقر فوق تحميل الإضافة
- حدد المسار أو اسحب لإلقاء ملف * .vsix الذي تم استلامه مسبقًا
- بعد مرور عملية التحقق ، في قائمة السياق بالملحق ، حدد امتداد العرض ، على الصفحة التي تفتح ، حدد المجموعة التي تريد تثبيتها بها
- بعد هذا التمديد ، يمكنك البدء في استخدامه.
باستخدام بناء step'a
- افتح خط الأنابيب الذي تحتاجه
- انتقل لإضافة بناء step'a
- نحن نبحث عن التمديد

- نشير إلى جميع الإعدادات اللازمة

- استمتع بالحياة :)
استنتاج
لقد أوضحت في هذه المقالة كيفية إنشاء مكون إضافي لـ Azure DevOps ، والذي يضع العلامة الصحيحة تلقائيًا في مهام الإصدار. قام الزملاء ببنائه في خط الأنابيب ، والذي يعمل على كل من الويندوز ووكلاء بناء لينكس.
بفضل هذا البرنامج المساعد ، أصبح من الأسهل بالنسبة لنا العمل مع المهام وبناء عمل مستمر في المشروع. لم يعد المطورون يصرف انتباههم عن أشياء غريبة ، وسرعان ما تتعرف ضمان الجودة على مهام اختبار جديدة.
مرة أخرى ، أذكر
رابط التنزيل:
linkونحن نتطلع إلى ردود الفعل والاقتراحات للمراجعة :)
PS
هناك أيضًا فكرة لإضافة القدرة على إزالة العلامات المحددة في البرنامج المساعد. إذا عثر المختبر على خطأ وكان عليك نشر المهمة مرة أخرى ، فيمكنك التخلص من علامات "تم التحقق".