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:
- IDE favorito
- TypeScript instalado + node.js + npm (agora eu instalei as versões 3.5.1 \ 12.4 \ 6.9.0)
- 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
- 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
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
- Vá para tfs_server_url / _gallery / manage
- Clique em Upload extension
- Especifique o caminho ou arraste e solte para lançar o arquivo * .vsix recebido anteriormente
- 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.
- Após esta extensão, você pode começar a usá-la.
Usando a etapa de compilação
- Abra o pipeline necessário
- Vá para adicionar build step'a
- Estamos à procura de extensão

- Indicamos todas as configurações necessárias

- 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:
linkAguardamos 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").