Wir haben ein Projekt mit einem maßgeschneiderten CI / CD-Prozess. Wenn der Entwickler die Aufgabe beendet und seine Änderungen in "Develop \ qa" einfügt, wird der Build automatisch gestartet, wodurch die neue Version der Anwendung in die Testumgebung gestellt wird. In einer idealen Welt erfährt der Tester automatisch, welche Aufgaben erledigt wurden und in welcher Umgebung sie bereitgestellt werden. In diesem Fall wird der Workflow kontinuierlich, ununterbrochen und erfordert weniger Kommunikation, was von konzentrierter Arbeit ablenkt. In der Praxis ist nicht alles so rosig.
Und dann fragte mich eines Morgens der Teamleiter: "Können Sie so etwas für TFS machen, damit das an den Build angehängte Tag nach dem Aufhängen dieses Builds das angegebene Tag hängt?"
Ich habe beschlossen, meine Build \ Release-Aufgabe für die Aufgabe zu implementieren. Darüber hinaus befinden sich die Quellen aller Build-Aufgaben auf
Github , und alle Informationen sind verfügbar.
Unsere Aufgabe markiert die Farbe der Aufgabe, die abgeschlossen, aber nicht getestet wurde. Dank dessen wird der Entwickler sofort bemerken, wenn er vergessen hat, das gewünschte Tag zu setzen, und die Qualitätssicherung sieht sofort, was überprüft werden muss. Dies visualisiert den Status von Aufgaben und beschleunigt die Arbeit am Projekt.
In diesem Artikel werde ich über die Implementierung der notwendigen Logik sprechen, Verpackung in Erweiterung. Wenn Sie also daran interessiert sind, wie Sie ein solches Plugin erstellen können, sind Sie bei cat willkommen.
Für die Ungeduldigsten:
Github und eine fertige Erweiterung auf dem
Markt .

Azure DevOps bietet die Möglichkeit, Filter zu erstellen, mit denen Sie die Masken auf der Platine in verschiedenen Farben einfärben können.

Wir interessieren uns für Aufgaben, die:
- abgeschlossen
- in die Testumgebung gegossen
- noch nicht verifizierte Qualitätssicherung
Für Elemente sind die Tags b und c am besten geeignet, aber das manuelle Festlegen ist widerlich, und jeder vergisst, dies zu tun. Wir werden also eine Erweiterung schreiben, die sie nach der Bereitstellung automatisch anbringt.
Daher benötigen wir den benutzerdefinierten Build \ Release-Schritt, um den Faktor Mensch zu reduzieren (der Entwickler hat vergessen, ein Tag einzufügen) und um die Qualitätssicherung zu unterstützen (Sie können sofort sehen, was überprüft werden muss).
Voraussetzungen
Um die Erweiterung zu entwickeln, benötigen wir:
- Lieblings-IDE
- installiertes TypeScript + node.js + npm (jetzt habe ich die Versionen 3.5.1 \ 12.4 \ 6.9.0 installiert)
- tfx-cli - Bibliothek zur Verpackungserweiterung'a (npm i -g tfx-cli).
Beachten Sie das Vorhandensein des Flags -g
Microsoft hat eine gute
Dokumentation, in der sie ganz oben stehen und erklären, wie man eine Art Erweiterung erstellt. Auf die gleiche Weise gibt es auch ein Dock zum Erstellen einer Build \ Release-Aufgabe.
Meiner Meinung nach gibt es in beiden Artikeln Nachteile im Hinblick auf die Detaillierung oder Erläuterung bestimmter Punkte, daher werde ich mich auf sie verlassen und mich auf die Punkte konzentrieren, die mir in Wirklichkeit nicht ganz offensichtlich erschienen.
Im Allgemeinen können Sie den Schritt build \ release in einer relativ großen Anzahl von Sprachen schreiben. Ich werde ein Beispiel für TypeScript geben.
Warum TypeScript?
Die allererste Version von Build Step'a wurde in PowerShell'e geschrieben, nur unser Team und einige Leute wussten davon. Fast sofort wurden wir mit der Tatsache konfrontiert, dass es keine PowerShell gibt, wenn Sie versuchen, dem Build, der auf dem Docker-Build-Agenten ausgeführt wird, eine Aufgabe hinzuzufügen, und die Aufgabe einfach nicht funktioniert. Darüber hinaus traten von Zeit zu Zeit verschiedene Arten von Fehlern bei Personen auf, die PowerShell Kooky zugeschrieben wurden. Daher die Schlussfolgerung - die Lösung sollte plattformübergreifend sein.
Projektstruktur
|--- README.md |--- images |---extension-icon.png |--- TaskFolder ( build\release step'a) |--- vss-extension.json ( )
Als nächstes müssen wir die Bibliothek für die Implementierung von Build Step'a installieren
- cd TaskFolder
- npm init
- npm install azure-pipelines-task-lib --save && npm install @ types / node --save-dev && npm install @ types / q --save-dev
- tsc --init
Entwicklungserweiterung
Zunächst müssen wir im TaskFolder die Datei task.json erstellen - dies ist die Manifestdatei für den Erstellungsschritt selbst. Es enthält Serviceinformationen (Version, Ersteller, Beschreibung), eine Umgebung zum Starten und Konfigurieren aller Eingaben, die wir in Zukunft auf dem Formular sehen werden.
Ich schlage vor, seine Struktur in der
Dokumentation genauer zu untersuchen.
In unserem Fall enthält das Formular zwei Eingaben: das Tag, das wir den Arbeitselementen hinzufügen, und die Auswahl des Pipeline-Typs (Build oder Release).
"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" } ]
Mit Namen beziehen wir uns im folgenden Code auf den Wert jeder der Eingaben.
Erstellen Sie eine index.ts in TaskFolder und schreiben Sie den ersten Code
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();
Es ist erwähnenswert, dass TFS über eine sehr umfangreiche Dokumentation zur vorhandenen REST-API verfügt. Im Moment müssen wir jedoch nur Arbeitselemente an den Build anhängen.
Installieren Sie eine Bibliothek für die einfache Ausführung von Abfragen
npm install request --save && npm install request-promise-native --save
Fügen Sie es zu index.ts hinzu
import * as request from "request-promise-native";
Wir implementieren die Funktion, die aus dem aktuellen Build die angehängten Workitems erhält
Ein bisschen über Autorisierung
Um auf die REST-API zugreifen zu können, benötigen wir accessToken
const accessToken = tl.getEndpointAuthorization('SystemVssConnection', true).parameters.AccessToken;
Setzen Sie als Nächstes die Header-Berechtigung auf "Bearer $ {accessToken}".
Wir kehren zum Empfang von Workitems zurück, die an den Build gebunden sind.
Der Name des Url Azure DevOps-Servers und des TeamProject kann wie folgt aus den Umgebungsvariablen abgerufen werden
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; }
Als Antwort auf eine GET-Anfrage per URL
${collectionUrl}/${teamProject}/_apis/build/builds/${buildId}/workitems
Wir bekommen diese Art von 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" } ] }
Für jede URL können Sie über dieselbe REST-API Daten zum Arbeitselement abrufen.
Im Moment ist unsere Ausführungsmethode wie folgt.
Die Methode zum Abrufen von Workitems aus der Version ist nahezu identisch mit der bereits beschriebenen.
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); }
Der nächste Schritt besteht darin, den aktuellen Satz von Tags für jedes der empfangenen Arbeitselemente abzurufen und das von uns angegebene hinzuzufügen.
Fügen wir die Ausführungsmethode hinzu:
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); } }
Betrachten wir das Hinzufügen eines Tags zu Arbeitselementen.
Zuerst müssen wir das Tag erhalten, das wir auf dem Formular angegeben haben.
const tagFromInput = tl.getInput('tagToAdd');
Weil 2 Schritte zurück haben wir URLs zur API jedes Arbeitselements erhalten, und mit ihrer Hilfe können wir einfach eine Liste der aktuellen Tags anfordern:
const uri = workItem.url + "?fields=System.Tags&api-version=2.0"; const getOptions = createGetRequestOptions(uri) const result = await request.get(getOptions);
Als Antwort erhalten wir diesen 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" }
Wir nehmen alle alten Tags und fügen ihnen ein neues hinzu:
const currentTags = result.fields['System.Tags']; let newTags = ''; if (currentTags !== undefined) { newTags = currentTags + ";" + tagFromInput; } else { newTags = tagFromInput; }
Wir senden eine Patch-Anfrage an die Workitem-API:
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 }
Montage- und Verpackungserweiterung'a
Für die Schönheit von allem, was passiert, schlage ich vor, in tsconfig.json zu compilerOptions hinzuzufügen
"outDir": "dist"
. Wenn wir nun den Befehl
tsc
im TaskFolder ausführen, erhalten wir den Ordner dist, in dem sich index.js befindet, der zum endgültigen Paket wechselt.
Weil Unsere index.js befindet sich im dist-Ordner und dann werden wir sie auch in das endgültige Paket kopieren. Wir müssen task.json ein wenig reparieren:
"execution": { "Node": { "target": "dist/index.js" } }
In vss-extension.json im Abschnitt "Dateien" müssen Sie explizit angeben, was in das endgültige Paket kopiert werden soll.
"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" } ]
Letzter Schritt - wir müssen unsere Erweiterung packen.
Führen Sie dazu den folgenden Befehl aus:
tfx extension create --manifest-globs ./vss-extension.json
Nach der Ausführung erhalten wir eine * .vsix-Datei, die in TFS weiter installiert wird.
Die PS * .vsix-Datei ist im Wesentlichen ein gewöhnliches Archiv. Sie können sie beispielsweise einfach über 7-zip öffnen und feststellen, dass sich alles, was Sie benötigen, wirklich darin befindet.
Fügen Sie etwas Schönheit hinzu
Wenn Sie ein Image haben möchten, wenn Sie Ihren Erstellungsschritt auswählen, während Sie ihn der Pipeline hinzufügen, sollte diese Datei neben task.json und mit dem Namen icon.png abgelegt werden. Sie müssen keine Änderungen an task.json selbst vornehmen.
Sie können vss-extension.json einen Abschnitt hinzufügen:
"icons": { "default": "images/logo.png" }
Dieses Bild wird in der Galerie der lokalen Erweiterungen angezeigt.
Erweiterung installieren
- Gehen Sie zu tfs_server_url / _gallery / manage
- Klicken Sie auf Erweiterung hochladen
- Geben Sie den Pfad oder Drag'n'Drop an, um die zuvor empfangene * .vsix-Datei zu werfen
- Wählen Sie nach Abschluss der Überprüfung im Kontextmenü der Erweiterung die Option Ansichtserweiterung aus. Wählen Sie auf der sich öffnenden Seite die Sammlung aus, in der Sie sie installieren möchten
- Nach dieser Erweiterung können Sie sie verwenden.
Mit build step'a
- Öffnen Sie die Pipeline, die Sie benötigen
- Gehen Sie, um build step'a hinzuzufügen
- Wir suchen eine Erweiterung

- Wir geben alle notwendigen Einstellungen an

- Genieße das Leben :)
Fazit
In diesem Artikel habe ich gezeigt, wie ein Plug-In für Azure DevOps erstellt wird, mit dem die Aufgaben für die Version automatisch mit dem richtigen Tag versehen werden. Kollegen haben es in die Pipeline integriert, die sowohl unter Windows- als auch unter Linux-Build-Agenten ausgeführt wird.
Dank dieses Plugins ist es für uns einfacher geworden, mit Aufgaben zu arbeiten und kontinuierlich am Projekt zu arbeiten. Entwickler lassen sich nicht mehr von fremden Dingen ablenken, und die Qualitätssicherung lernt schnell neue Testaufgaben kennen.
Ich erinnere mich noch einmal an den Download-
Link :
LinkWir freuen uns auf Feedback und Vorschläge zur Überarbeitung :)
PS
Es gibt auch eine Idee, die Möglichkeit hinzuzufügen, die angegebenen Tags in das Plugin zu entfernen. Wenn der Tester einen Fehler gefunden hat und Sie die Aufgabe erneut bereitstellen müssen, können Sie die "Verified" -Tags entfernen.