# Pipeline CI com Validação

**Laboratório 1 – Primeiro Pipeline CI com Validação**

* Cenário: Um time começa a usar Azure DevOps para um app Node.js.
    
* Tarefas:
    
    * Criar pipeline YAML com `npm install`, `lint`, `test`, `build`.
        
    * Criar stage de Instalação e Build
        
    * Exibir badges de status no repositório.
        
    * Validação de PRs com lint + testes.
        
    * Usar cache
        
    * Publicar artefato no Pipeline Artifact
        
    * Publicar o artefato no Azure Artifact
        
* Desafio real: Automatizar o mínimo necessário para evitar que código quebrado entre na main.
    

## **Pré requisitos**

* Azure Devops Account
    

### 1️⃣ **Step 1: Criar e clonar o Repositório**

1. Criar Novo Projeto:
    
2. Ao entrar no painel do Azure DevOps da sua organização, clique no botão "Novo Projeto", geralmente no canto superior direito ou no centro da tela.
    
3. Configurar Detalhes do Projeto:
    
4. Nome do Projeto: Insira "Projeto Pessoal" como nome do projeto.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747662231953/afa0ee43-5e02-49c3-a59c-ec6372af4875.png align="center")

1. Acesse Repositórios -&gt; Importar Repositório -&gt; Clonar URL: [https://github.com/joaochiroli/Devops-Challenge](https://github.com/joaochiroli/Devops-Challenge)
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747662305756/6821ea40-73af-4378-9494-905e86e0d853.png align="center")

1. Como a intenção é começar um projeto novo do zero e testar algumas habilidades iremos excluir o conteúdo que está no arquivo **azure-pipelines.yml**
    

### 2️⃣ **Step 2:** Criar pipeline YAML (`azure-pipelines.yml`) na branch feature

1. Criar a Branch feature
    
    1. Clique na branch main → clique em seguida em new branch → crie a branch feature
        
2. Criar Pipeline:
    
    1. Vá até a aba Pipelines → Criar Pipeline → Integre sua Pipeline com o Azure Repos
        
    2. Crie o arquivo `azure-pipelines.yml`:
        

```yaml
trigger: none

pr:
  branches:
    include:
    -  main


stages:

# Instala, Valida e Publica o Código

  - stage: InstallAndValidate
    displayName: Install And Validate
    pool: 
      vmImage: 'ubuntu-latest'
    jobs:
    - job: InstallAndValidate
      displayName: 'InstallAndValidate'
      steps:
      - checkout: self
        path: 'src'
        displayName: 'Checkout code'
      - task: UseNode@1
        inputs:
          version: '15.x'
        displayName: 'Install Node.js'
      - task: Cache@2
        inputs:
          key: 'npm | "$(Agent.OS)" | $(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0/package-lock.json'
          restoreKeys: |
            npm | "$(Agent.OS)"
          path: $(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0
        displayName: 'Cache node_modules'
        
      - script: npm ci 
        workingDirectory: $(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0
        displayName: 'Install dependencies with ci'
        
      - script: npm run lint
        workingDirectory: $(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0
        displayName: 'Linter (ESLint)'

      - script: |
          npm install --save-dev prettier
          npm run prettier
        workingDirectory: $(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0
        displayName: 'Formatter (Prettier)'
        continueOnError: true
    
      - script: CI=true npm run test
        workingDirectory: $(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0
        displayName: 'Test (Jest) CI=true'

      - task: PublishPipelineArtifact@1
        inputs:
          targetPath: '$(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0'
          artifact: 'validated-source'
          publishLocation: 'pipeline'
        displayName: 'Validated Source'
```

* Tarefas:
    
    * Criar pipeline YAML com npm install, lint, test, build ✅
        
    * Criar stage de Instalação e Build ✅
        
    * Exibir badges de status no repositório ❗
        
    * Validação de PRs com lint + testes ❗
        
    * Usar cache ✅
        
    * Publicar artefato no Pipeline Artifact ✅
        
    * Publicar o artefato no Azure Artifact❗
        

## 3️⃣ Configurar **Validação de Pull Request**

A configuração `pr:` no YAML já cuida disso, mas você pode reforçar no portal:

1. Vá para o repositório → **Branches**
    
2. Clique no botão `...` ao lado da branch `main` → **Branch policies**
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747667213524/68d2ec02-8324-4e95-8343-634096ebc5fc.png align="center")
    
3. Ative:
    
    * ✔️ Require a minimum number of reviewers
        
    * ✔️ Check for linked work items
        
    * ✔️ Build validation → Selecione o pipeline YAML criado (deixe como obrigatório
        
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747667856622/1b97f46c-1af8-4cce-aa23-44e6ed984d37.png align="center")
    
4. Tarefas:
    
    * Criar pipeline YAML com `npm install`, `lint`, `test`, `build` ✅
        
    * Criar stage de Instalação e Build ✅
        
    * Exibir badges de status no repositório ❗
        
    * Validação de PRs com lint + testes ✅
        
    * Usar cache ✅
        
    * Publicar artefato no Pipeline Artifact ✅
        
    * Publicar o artefato no Azure Artifact❗
        

## 4️⃣ Exibir badges de status no repositório

1. Vá em **Pipelines** → clique no pipeline → ⚙️ `View YAML` → Copie o **Status Badge Markdown**
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747668211615/6da73e3a-939b-4620-93e5-40e7eeff553d.png align="center")
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747668229295/b4772f80-7ec1-4da3-b0f3-ba32f611c287.png align="center")
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747668293686/9ff05453-c850-4198-b14c-778ffe3d9c89.png align="center")
    
2. Adicione ao seu [`README.md`](http://README.md):
    

```yaml
mdCopiarEditar![Build Status](https://dev.azure.com/<organization>/<project>/_apis/build/status/<pipeline-name>?branchName=main)
```

3. Depois de adicionar no README.md basta fazer o commit:
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747668307091/cf625fd6-b6ad-4eb2-adf8-56df7622f176.png align="center")
    
4. Tarefas:
    
    * Criar pipeline YAML com `npm install`, `lint`, `test`, `build` ✅
        
    * Criar stage de Instalação e Build ✅
        
    * Exibir badges de status no repositório ✅
        
    * Validação de PRs com lint + testes ✅
        
    * Usar cache ✅
        
    * Publicar artefato no Pipeline Artifact ✅
        
    * Publicar o artefato no Azure Artifact❗
        

## 5️⃣ Publicar o artefato no Azure Artifact

1. Criar um **Azure Artifacts feed** no seu projeto DevOps com o nome Projeto Pessoal
    
2. Adicionar a seguinte task no seu arquivo `azure-pipelines.yml`:
    

```yaml
- task: UniversalPackages@0
        inputs:
          command: 'publish'
          publishDirectory: '$(Build.ArtifactStagingDirectory)'
          feedsToUsePublish: 'internal'
          vstsFeedPublish: 'ee03fcac-046a-4290-b5f4-79508dd47b04/82dd1496-0532-4c69-b3e0-feb482096c20'
          vstsFeedPackagePublish: 'validated-source'
          versionOption: 'patch'
```

3. Detalhe o vstsFeedPublish é o ID do meu feed no Azure Artifact ou seja, no seu caso será uma ID diferente.
    
4. Agora salva e executa sua pipeline.
    
5. No meu caso, tive o seguinte problema: **User 'b83dcc85-38ca-4958-b336-c89e9ffbca0d' lacks permission to complete this action. You need to have 'AddPackage'.","@i":"c2baee9b","@l":"Error","SourceContext":"ArtifactTool.Program","UtcTimestamp":"2025-05-19 17:05:49.785Z"})**
    
    1. Neste caso vá até seu feed → Clique na engrenagem no canto superior direito → Permissão → Add User/Group → Contributor → &lt;Nome\_do\_Projeto\_Service\_Build&gt;
        
6. Execute sua pipeline e você irá encontrar um novo artefato gerado no seu feed, neste caso é um artefato de patch mas você pode alterar esta opção.
    
7. Esta publicação é incremental, ou seja, cada nova execução irá gerar um novo artefato.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747676886908/566fc680-4346-42a5-b78e-ec52d906244f.png align="center")
    
8. Tarefas:
    
    * Criar pipeline YAML com `npm install`, `lint`, `test`, `build` ✅
        
    * Criar stage de Instalação e Build ✅
        
    * Exibir badges de status no repositório ✅
        
    * Validação de PRs com lint + testes ✅
        
    * Usar cache ✅
        
    * Publicar artefato no Pipeline Artifact ✅
        
    * Publicar o artefato no Azure Artifact ✅
        

## 6️⃣ Merge com a branch main

1. Agora só basta ir até Azure Repos → Pull Request → Aprovar a PR e fazer o Merge com a branch main
    
    1. Arquivo `azure-pipelines.yml` completo:
        

```yaml
trigger: none

pr:
  branches:
    include:
    -  main


stages:

# Instala, Valida e Publica o Código

  - stage: InstallAndValidate
    displayName: Install And Validate
    pool: 
      vmImage: 'ubuntu-latest'
    jobs:
    - job: InstallAndValidate
      displayName: 'InstallAndValidate'
      steps:
      - checkout: self
        path: 'src'
        displayName: 'Checkout code'
      - task: UseNode@1
        inputs:
          version: '15.x'
        displayName: 'Install Node.js'
      - task: Cache@2
        inputs:
          key: 'npm | "$(Agent.OS)" | $(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0/package-lock.json'
          restoreKeys: |
            npm | "$(Agent.OS)"
          path: $(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0
        displayName: 'Cache node_modules'
        
      - script: npm ci 
        workingDirectory: $(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0
        displayName: 'Install dependencies with ci'
        
      - script: npm run lint
        workingDirectory: $(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0
        displayName: 'Linter (ESLint)'

      - script: |
          npm install --save-dev prettier
          npm run prettier -- --write
        workingDirectory: $(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0
        displayName: 'Formatter (Prettier)'
        continueOnError: true
    
      - script: CI=true npm run test
        workingDirectory: $(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0
        displayName: 'Test (Jest) CI=true'
  
      - task: PublishPipelineArtifact@1
        inputs:
          targetPath: '$(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0'
          artifact: 'build-artifact'
          publishLocation: 'pipeline'
        displayName: 'Validated Source'
    
      - task: UniversalPackages@0
        inputs:
          command: 'publish'
          publishDirectory: '$(Build.ArtifactStagingDirectory)'
          feedsToUsePublish: 'internal'
          vstsFeedPublish: 'ee03fcac-046a-4290-b5f4-79508dd47b04/82dd1496-0532-4c69-b3e0-feb482096c20'
          vstsFeedPackagePublish: 'validated-source'
          versionOption: patch
```

## 7️⃣ Bônus - Publicar o artefato no Azure Artifact no formato .npmrc

1. Vá até o arquivo **.npmrc** na raiz do projeto e coloque as seguintes informações de acordo com o seu feed:
    

```yaml
registry=https://pkgs.dev.azure.com/joaochiroli123/ee03fcac-046a-4290-b5f4-79508dd47b04/_packaging/ProjetoPessoal/npm/registry/

engine-strict=true
```

2. Altere também `azure-pipelines.yml`:
    

```yaml
- task: Npm@1
        inputs:
          command: 'publish'
          workingDir: '$(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0'
          publishRegistry: 'useFeed'
          publishFeed: 'ee03fcac-046a-4290-b5f4-79508dd47b04/82dd1496-0532-4c69-b3e0-feb482096c20'
```

3. Execute a Pipeline
    
4. Provavelmente vai dar um erro dizendo que o arquivo package.json está com uma flag private habilitada o que você pode fazer é adicionar o script abaixo que irá remover o private do seu arquivo de configuração:
    

```yaml
- script: |
    echo "🔧 Removendo 'private' de package.json após o npm ci"
    sed -i '/"private": true,/d' package.json
    cat package.json
  workingDirectory: '$(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0'
  displayName: '🔧 Remoção do campo private após instalação'
```

5. Execute a pipeline
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747684302870/11cb1986-360f-40aa-854e-928d9119f375.png align="center")

6. **Observação:** Quando você realiza a publicação de pacotes do tipo **npm** no Azure Artifacts, o processo é diferente da publicação via **Universal Packages**.
    
    No caso do **npm**, o Azure utiliza a versão definida no arquivo `package.json`. Isso significa que, se a versão `0.1.3` já tiver sido publicada no feed, será necessário atualizar o `package.json` para `0.1.4` (ou superior) para que a nova publicação seja aceita. Caso contrário, a pipeline falhará com erro de versão duplicada.
    
    Para evitar esse tipo de falha, você pode alterar o valor da versão manualmente ou automatizar a atualização da versão dentro da própria pipeline, utilizando comandos como `npm version patch`. Essa abordagem garante um controle incremental e contínuo das versões publicadas, evitando conflitos.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1747755987988/1aa57c79-1f47-4f57-8bf8-a1a0271a3531.png align="center")
    
    Exemplo de script incremental:
    
    ```yaml
     - script: |
              echo "Versão atual:"
              node -p "require('./package.json').version"
    
              npm version patch --no-git-tag-version
    
              echo "Nova versão:"
              node -p "require('./package.json').version"
            workingDirectory: $(Pipeline.Workspace)/validated-source
            displayName: 'Incrementar versão patch no package.json'
    ```
