Criar extensão do DevOps do Azure

Temos um projeto com um processo de CI / CD personalizado. Quando o desenvolvedor conclui a tarefa e injeta suas alterações no develop \ qa, a compilação é iniciada automaticamente, o que coloca a nova versão do aplicativo no ambiente de teste. Em um mundo ideal, o testador aprende automaticamente sobre as tarefas que foram concluídas e em que ambiente elas são implantadas. Nesse caso, o fluxo de trabalho se torna contínuo, ininterrupto e requer menos comunicação, distraindo o trabalho concentrado. Na prática, nem tudo é tão róseo.

E uma manhã, o líder da equipe me perguntou: "Você pode criar algo para o TFS, para que as tags anexadas à compilação suspendam a especificada depois de passar por essa compilação?"

Decidi implementar minha tarefa de build \ release para a tarefa. Além disso, as fontes de todas as tarefas de construção estão no github e todas as informações estão disponíveis.

Nossa tarefa marca a cor da tarefa, que é concluída, mas não testada. Graças a isso, o desenvolvedor notará imediatamente se ele esqueceu de colocar a tag desejada e o controle de qualidade verá imediatamente o que precisa ser verificado. Isso visualiza o status das tarefas e acelera o trabalho no projeto.

Neste artigo, falarei sobre a implementação da lógica necessária, empacotando em extensão. Portanto, se você estiver interessado em como criar um plug-in, bem-vindo ao gato.

Para os mais impacientes: github e uma extensão pronta no mercado .



O Azure DevOps tem a capacidade de criar filtros que permitem colorir as máscaras no quadro em cores diferentes.




Estamos interessados ​​em tarefas que:

  • concluído
  • derramado no ambiente de teste
  • controle de qualidade ainda não verificado

Para os itens, as etiquetas bec são mais adequadas, mas defini-las manualmente é nojento e todos esquecem de fazê-lo. Então, escreveremos uma extensão que, após a implantação, as afixará automaticamente.

Portanto, precisamos da etapa customizada build / release para reduzir o fator humano (o desenvolvedor esqueceu de colocar uma tag) e ajudar no controle de qualidade (você pode ver imediatamente o que precisa ser verificado).

Pré-requisitos


Para desenvolver a extensão, precisamos:

  1. IDE favorito
  2. TypeScript instalado + node.js + npm (agora eu instalei as versões 3.5.1 \ 12.4 \ 6.9.0)
  3. tfx-cli - biblioteca para extensão de empacotamento'a (npm i -g tfx-cli).

Observe a presença do sinalizador -g

A Microsoft possui uma boa documentação na qual eles estão no topo e informam como criar algum tipo de extensão. Além disso, da mesma maneira, há um dock para criar uma tarefa de build \ release.

Na minha opinião, existem desvantagens em ambos os artigos do ponto de vista de detalhamento ou explicação de certos pontos, por isso vou confiar neles, concentrando-me naqueles pontos que na realidade me pareciam não muito óbvios.

De um modo geral, você pode escrever a etapa build \ release em um número bastante grande de idiomas. Vou dar um exemplo no TypeScript.

Por que TypeScript?


A primeira versão do build step'a foi escrita no PowerShell'e, apenas nossa equipe e algumas pessoas sabiam disso. Quase imediatamente, fomos confrontados com o fato de que, se você tentar adicionar uma tarefa à compilação que é executada no agente de compilação do docker, não haverá PowerShell e a tarefa simplesmente não funcionará. Além disso, de tempos em tempos, vários tipos de erros aconteciam nas pessoas, atribuídos ao PowerShell Kooky. Daí a conclusão - a solução deve ser multiplataforma.

Estrutura do projeto


|--- README.md |--- images |---extension-icon.png |--- TaskFolder (     build\release step'a) |--- vss-extension.json ( ) 

Em seguida, precisamos instalar a biblioteca para a implementação da etapa de compilação

  1. cd TaskFolder
  2. npm init
  3. npm install azure-pipelines-task-lib --save && npm install @ types / node --save-dev && npm install @ types / q --save-dev
  4. tsc --init

Extensão de desenvolvimento


Primeiro de tudo, dentro do TaskFolder, precisamos criar o arquivo task.json - este é o arquivo de manifesto para a própria etapa de compilação. Ele contém informações de serviço (versão, criador, descrição), um ambiente para o lançamento e configuração de todas as entradas, que veremos no futuro no formulário.

Proponho estudar sua estrutura com mais detalhes na documentação .
No nosso caso, haverá 2 input'a no formulário - a tag que adicionaremos aos itens de trabalho e a escolha do tipo de pipeline (compilação ou liberação).

 "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" } ] 

Por nome, no código abaixo, nos referiremos ao valor de cada uma das entradas.
Crie um index.ts no TaskFolder e escreva o primeiro pedaço de código

 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(); 

Vale notar que o TFS possui uma documentação muito rica sobre a API REST existente, mas, por enquanto, tudo o que precisamos fazer é anexar itens de trabalho à compilação.

Instale uma biblioteca para facilitar a execução de consultas

 npm install request --save && npm install request-promise-native --save 

Adicione-o ao index.ts

 import * as request from "request-promise-native"; 

Implementamos a função que, a partir da compilação atual, obterá os itens de trabalho anexados

Um pouco sobre autorização


Para acessar a API REST, precisamos obter accessToken

 const accessToken = tl.getEndpointAuthorization('SystemVssConnection', true).parameters.AccessToken; 

Em seguida, defina a autorização do cabeçalho como "Portador $ {accessToken}"

Voltamos a receber itens de trabalho vinculados à construção.

O servidor de URL DevOps do Azure e o nome do TeamProject podem ser obtidos de variáveis ​​de ambiente da seguinte maneira

 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; } 

Como resposta a uma solicitação GET por URL

 ${collectionUrl}/${teamProject}/_apis/build/builds/${buildId}/workitems 

nós temos esse tipo de 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" } ] } 

Para cada URL, através da mesma API REST, você pode obter dados sobre o item de trabalho.

No momento, nosso método de execução é o seguinte.

O método para obter itens de trabalho a partir da liberação é quase idêntico ao já descrito.

 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); } 

A próxima etapa é obter o conjunto atual de tags para cada um dos itens de trabalho recebidos e adicionar o que especificamos.

Vamos adicionar o método run:

 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); } } 

Vamos considerar adicionar uma tag aos itens de trabalho.

Primeiro, precisamos obter a tag que indicamos no formulário.

 const tagFromInput = tl.getInput('tagToAdd'); 

Porque Duas etapas atrás, recebemos URLs para a API de cada item de trabalho e, com a ajuda deles, podemos facilmente solicitar uma lista de tags atuais:

 const uri = workItem.url + "?fields=System.Tags&api-version=2.0"; const getOptions = createGetRequestOptions(uri) const result = await request.get(getOptions); 

Em resposta, obtemos este 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" } 

Pegamos todas as tags antigas e adicionamos uma nova a elas:

 const currentTags = result.fields['System.Tags']; let newTags = ''; if (currentTags !== undefined) { newTags = currentTags + ";" + tagFromInput; } else { newTags = tagFromInput; } 

Enviamos uma solicitação de patch para o item de trabalho 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 } 

Extensão de montagem e embalagem


Para a beleza de tudo o que acontece, proponho adicionar tsconfig.json ao compilerOptions
 "outDir": "dist" 
. Agora, se executarmos o comando tsc dentro do TaskFolder, obteremos a pasta dist, dentro da qual será index.js, que irá para o pacote final.

Porque Nosso index.js está localizado na pasta dist e, em seguida, também o copiaremos para o pacote final. Precisamos corrigir um pouco o task.json:

 "execution": { "Node": { "target": "dist/index.js" } } 

Em vss-extension.json na seção de arquivos, você deve declarar explicitamente o que será copiado para o pacote final.

 "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" } ] 

Última etapa - precisamos compactar nossa extensão.

Para fazer isso, execute o comando:

  tfx extension create --manifest-globs ./vss-extension.json 

Após a execução, obtemos um arquivo * .vsix, que será instalado posteriormente no TFS.

O arquivo PS * .vsix é essencialmente um arquivo comum, você pode abri-lo facilmente através do 7-zip, por exemplo, e ver que tudo o que você precisa está realmente dentro.

Adicione um pouco de beleza


Se você deseja ter uma imagem ao selecionar sua etapa de construção enquanto a adiciona ao pipeline, esse arquivo deve ser colocado ao lado de task.json e denominado icon.png. Você não precisa fazer alterações no próprio task.json.

Você pode adicionar uma seção ao vss-extension.json:

 "icons": { "default": "images/logo.png" } 

Esta imagem será exibida na galeria de extensões locais.

Instalar extensão


  1. Vá para tfs_server_url / _gallery / manage
  2. Clique em Upload extension
  3. Especifique o caminho ou arraste e solte para lançar o arquivo * .vsix recebido anteriormente
  4. Depois que a verificação for aprovada, no menu de contexto da extensão, selecione Visualizar extensão, na página exibida, selecione a coleção na qual deseja instalá-la.
  5. Após esta extensão, você pode começar a usá-la.

Usando a etapa de compilação


  1. Abra o pipeline necessário
  2. Vá para adicionar build step'a
  3. Estamos à procura de extensão


  4. Indicamos todas as configurações necessárias

  5. Aproveite a vida :)

Conclusão


Neste artigo, mostrei como criar um plug-in para o Azure DevOps, que coloca automaticamente a marca correta nas tarefas da liberação. Os colegas o incorporaram ao pipeline, que é executado nos agentes de compilação do Windows e do Linux.

Graças a este plug-in, ficou mais fácil trabalhar com tarefas e criar trabalho contínuo no projeto. Os desenvolvedores não se distraem com coisas estranhas e o controle de qualidade aprende rapidamente sobre novas tarefas de teste.

Mais uma vez, recordo o link para download: link

Aguardamos ansiosamente comentários e sugestões para revisão :)

PS

Há também uma ideia para adicionar a capacidade de remover as tags especificadas no plug-in. Se o testador encontrar um bug e você precisar implantar a tarefa novamente, poderá se livrar das tags "Verificadas").

Source: https://habr.com/ru/post/pt461389/


All Articles