YAML para Desenvolvedores Web

Um guia simples para ajudar você a ler e escrever arquivos .yml

Publicado em 19 de mar. de 2020, e leva aproximadamente 6 minutos para ler.

Mais e mais, nós, desenvolvedores web precisamos aprender sobre diferentes áreas para nos tornarmos melhores profissionais e menos dependentes de outras pessoas para tarefas simples.

Se você já começou sua carreira como pessoa desenvolvedora front-end, você já deve ter visto vários arquivos .yml, como por exemplo, .travis.yml (para CI com o Travis), .gitlab-ci.yml (CI com o git lab), etc.. Mas vamos ser sinceros... que diabos é isso?

Porque e para que as pessoas as pessoas usariam esse tipo de arquivo? Quais são os benefícios? Como isso funciona?

O objetivo desse artigo é fazer uma introdução a você à estrutura do YAML e te dar mais confiança pra ler, entender, escrever e alterar arquivos como esse quando precisar.

Afinal, não sei você, mas quando eu preciso fazer uma modificação em algo que não sei e não conheço, gera um sentimento bem ruim de frustração.

A única forma de conseguir superar isso é encarando de frente o problema e resolvendo, certo? Então bora!

Mas antes de tudo, o que é YAML?

De acordo com o site oficial, Yaml é (tradução livre):

"YAML (um acrónimo recursivo para "YAML Ain't Markup Language" [algo como, não é uma linguagem de marcação]) é um padrão amigável para serialização de dados para todas as linguagens de programação."

Comumente usado como arquivos de configuração, o que explica muito, não é?

Alguém um dia, muito cansado de escrever um monte de arquivo de configuração sem padrão nenhum pensou:

E se a gente tivesse de alguma maneira de escrever essas configurações como "receitas de bolo"? Tipo, texto simples e claro, direto ao ponto.

E dessa necessidade, em Maio de 2001 nasce o Yaml.

YAML vs JSON

Acredite ou não, Yaml é um superset do nosso velho amigo JSON.

"Superset é uma linguagem de programação que contém todas as funcionalidades de uma outra, porém expandida ou com mais funcionalidades." - Fonte

Trazendo pro nosso mundo de Front-end, eu diria que :

O Yaml está para o JSON assim como o TypeScript está para o JavaScript.

Mas para entender melhor como isso é sequer possível, vamos ver um exemplo:

tsconfig.json
{
  "compilerOptions": {
    "module": "system",
    "noImplicitAny": true,
    "removeComments": true,
    "preserveConstEnums": true,
    "outFile": "../../built/local/tsc.js",
    "sourceMap": false,
    "types": ["node", "lodash", "express"]
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "**/*.spec.ts"]
}

O arquivo acima é um tsconfig.json, arquivo de configuração que todo projeto TypeScript precisa ter.

Nós que temos o olho treinado no JSON, podemos achar muito simples e claro o que está sendo declarado, porém, ainda existem algumas limitações, como por exemplo:

  • Não criar variáveis;
  • Não podemos importar ou usar variáveis de fora (por exemplo variáveis de ambiente);
  • Não temos maneiras de sobrescrever valores, caso alguma condição seja atendida.

Tanto essas limitações são reais que em vários projetos, quando fazemos uso de ESLint, BabelJS e queremos utilizar uma configuração mais robustas, trocamos nosso arquivo de JSON para JavaScript (.babelrc -> babel.config.js), que então nos permite carregar configurações condicionais e baseadas em que ambiente estamos, etc., resolvendo as limitações mencionadas anteriormente.

Mas se você estiver usando uma outra linguagem que não seja JavaScript, arquivos .js para configurar coisas nem são uma opção, e é aqui que o Yaml começa a brilhar.

Se pudéssemos reescrever o tsconfig.json usando a sintaxe YAML, seria algo como:

tsconfig.yml
compilerOptions:
  module: system
  noImplicitAny: true
  removeComments: true
  preserveConstEnums: true
  outFile: '../../built/local/tsc.js'
  sourceMap: false
  types:
    - node
    - lodash
    - express
include:
  - src/**/*
exclude:
  - node_modules
  - '**/*.spec.ts'
Repare que isso é apenas um exemplo. Você não pode escrever tsconfig em Yaml😜

Conceitos, Tipos e Sintaxe

Agora, vamos entender mais a fundo sobre os conceitos dessa linguagem.

Indentação

Em Yaml, indentação é importante. Ele usa espaços em branco para agrupar informações. Por espaço em branco, entenda apenas como literalmente espaço (space), uma vez que o Tab não é permitido.

Não se assuste. Se você, assim como eu, usa o tab para indentar código, um simples arquivo .editorconfig, uma configuração ou um plugin no editor resolvem isso facilmente. Assim, quando você apertar tab, o editor troca o caractere de Tab por barras de espaços (2 ou 4).

Raíz

Uma vez que a indentação importa, se não tiver nenhum espaço antes da primeira declaração do arquivo, o YAML vai entender que estamos escrevendo código na raíz do arquivo (no nível 0):

pessoa:
  idade: 20

Em formato JSON, seria equivalente a:

{
  "pessoa": {
    "idade": 20
  }
}

Chave/Valor

Assim como em JSON ou JavaScript, YAML também usa o conceito de key/value (chave/valor). Podemos declarar de diversas maneiras:

chave: valor
chave_um: valor um
chave um: valor # Por mais estranho que pareça, é valido
'minha chave': algumvalor

Comentários

Diferentemente do JSON que não podemos escrever comentários, aqui, basta utilizarmos o caractere # e em seguida sua mensagem:

# Algum comentário relevante
pessoa: # Outro comentário
  idade: 20

Listas

Existem duas maneiras de escrever listas:

Igual JSON: lista de strings

Lembra que Yaml é o JSON com superpoderes? A gente pode usar a mesma sintaxe:

pessoas: ['Maria', 'Carla', 'Pedro']

Usando Hífen

A maneira mais comumente vista (e talvez até recomendada) é quebrando uma linha e adicionando hífen antes de cada valor:

pessoas:
  - Maria
  - Carla
  - Pedro

Strings

Diferentemente do JSON, onde podemos usar apenas aspas duplas para declarar strings, em Yaml temos vários jeitos diferentes de fazer:

empresa: Google # Uma palavra, sem aspas
nome_completo: Marcus da Silva # frases, sem aspas
nome: 'Cleber' # Usando aspas simples 
sobrenome: "Fernando Dias" # Usando aspas duplas
Dica: dê preferência para quotes quando sua string possuir qualquer caractere especial como _, @, etc.

Números

Como em qualquer outra linguagem de programação, temos dois tipos de tipos numéricos: inteiros (integer) e decimais (float):

ano: 2019 # Inteiro
nodeVersion: 10.8 # Decimal

Nós âncoras (variável)

Âncora é um mecanismo muito útil para criação e grupos de dados (objetos) que podem ser injetados or estendidos por outro objeto.

Vamos imaginar que você precisa criar uma configuração pra sua CI. Ela terá dois ambientes, o de produção (production) e o de homologação (staging) e como você pode imaginar, ambos possuem quase as mesmas configurações.

No mundo JSON, teríamos que duplicar as configurações pela simples limitação da linguagem:

{
  "production": {
    "node_version": "13.0.0",
    "os": "ubuntu",
    "package_manager": "yarn",
    "run": ["yarn install", "NODE_ENV=${ENVIRONMENT} yarn build"],
    "env": {
      "ENVIRONMENT": "production"
    }
  },
  "staging": {
    "node_version": "13.0.0",
    "os": "ubuntu",
    "package_manager": "yarn",
    "run": ["yarn install", "NODE_ENV=${ENVIRONMENT} yarn build"],
    "env": {
      "ENVIRONMENT": "staging"
    }
  }
}

Copiar e colar é muito chato, especialmente quando você tem alterar alguma informação que é idêntica em ambos casos e é aqui que âncoras se destacam.

Então, aplicando o conceito, fazemos:

1. Criamos nossa âncora

# Eu coloquei o nome de "base-config" mas pode ser qualquer coisa que quiser
# o nome "&base" vai ser o nome da nossa variável e usaremos ele nos outros lugares
base-config: &base
  node_version: 13.0.0
  os: ubuntu
  package_manager: yarn
  run:
    - yarn install
    - NODE_ENV=${ENVIRONMENT} yarn build

2. Injetamos a âncora criada no nível que desejamos injetá-las. No caso, é dentro de production e staging:

base-config: &base
  node_version: 13.0.0
  os: ubuntu
  package_manager: yarn
  run:
    - yarn install
    - NODE_ENV=${ENVIRONMENT} yarn build

production:
  # Injetando todas os atributos e valores de "base-config"
  <<: *base
  env:
    - ENVIRONMENT: production

staging:
  # Injetando todas os atributos e valores de "base-config"
  <<: *base
  env:
    - ENVIRONMENT: staging

Simples, não? Também muito fácil de manter. Caso alguma alteração seja feita em nossa base-config, será automaticamente refletida em ambos ambientes.

Se você copiar e colar o código acima em um conversor online de "Yaml para JSON", teremos quase o mesmo código mencionado no exemplo do JSON, apenas adicionando o objeto "base-config":

{
  "base-config": {
    "node_version": "13.0.0",
    "os": "ubuntu",
    "package_manager": "yarn",
    "run": ["yarn install", "NODE_ENV=${ENVIRONMENT} yarn build"]
  },
  "production": {
    "node_version": "13.0.0",
    "os": "ubuntu",
    "package_manager": "yarn",
    "run": ["yarn install", "NODE_ENV=${ENVIRONMENT} yarn build"],
    "env": [
      {
        "ENVIRONMENT": "production"
      }
    ]
  },
  "staging": {
    "node_version": "13.0.0",
    "os": "ubuntu",
    "package_manager": "yarn",
    "run": ["yarn install", "NODE_ENV=${ENVIRONMENT} yarn build"],
    "env": [
      {
        "ENVIRONMENT": "staging"
      }
    ]
  }
}

Sintaxe JSON (sim, JSON)

Como expliquei anteriormente, um superset de uma linguagem é ela mesma mais algumas funcionalidades extras. Isso significa que podemos escrever arquivos Yaml com a sintaxe do JSON:

{
  "details": {
    "company": {
      "name": "Google",
      "year": 2019,
      "active": true
    },
    "employees": [
      "Anne",
      "John",
      "Max"
    ]
  }
}

Se usarmos novamente um conversor Yaml para JSON, veremos que tanto a entrada quanto a saída serão os mesmos:

{
  "details": {
    "company": {
      "name": "Google",
      "year": 2019,
      "active": true
    },
    "employees": ["Anne", "John", "Max"]
  }
}
Não acredita? Copia e cola o código Yaml anterior nesta ferramenta e veja com seus próprios olhos.

Ambientes Shell/Bash

Como disse no começo do artigo, é muito comum vermos arquivos .yml sendo utilizados para muitas coisas, mas especialmente para ambientes de CI/CD (Travis, Github Actions, GitLab CI, CircleCI, etc).

Nesses casos, precisamos descrever como a máquina ou o container deve trabalhar, o que deve ser instalado, rodado, etc.

Geralmente esses .yml rodarão em um ambiente Linux e quem está executando te dará acesso ao exterior, ou seja, a variáveis de ambiente, acesso a rodar shell scripts, como se fosse um terminal UNIX mesmo.

Quando usamos GitLab CI por exemplo, podemos especificar no topo do arquivo um objeto chamado variables. Dentro dele, tudo que for declarado será injetado como variável de ambiente e poderá ser usado dentro os processos que queremos executar:

variables:
  NODE_IMAGE: node:10

stages:
  - build

test:
  image: $NODE_IMAGE
  stage: build

Perceba que a sintaxe de usar variável com $ não é do Yaml, mas sim, shell/bash.

Dependendo da plataforma, ela também oferece outras variáveis que não são ou estão declaradas dentro do arquivo, mas que podem ser acessadas tranquilamente como referência do commit, o nome do branch, nome do autor, variáveis secretas (SECRETS), entre várias outras:

variables:
  NODE_IMAGE: node:10

stages:
  - build

test:
  image: $NODE_IMAGE
  stage: build
  artifacts:
    name: $CI_COMMIT_REF_NAME

No exemplo acima, usamos $CI_COMMIT_REF_NAME que se refere ao nome do branch para qual a integração esteja rodando para dar nome ao artefato que será gerado ao final do processo. Ou seja, se você criou um branch chamado nova-funcionalidade, esse nome será usado como nome do artefato.

Conclusão

Espero que você tenha conseguido absorver os conceitos de Yaml e já se sinta mais confiante quando encontrar um monstrinho desses por aí.

Lembre-se sempre que, o que você vai ter acesso ou não, vai depender de onde aquele Yaml está sendo utilizado. Diferentes plataformas (GitLab CI, CircleCI, Github Actions) possuem diferentes estratégias de configuração, variáveis e acessos.

Lembre-se sempre de checar a documentação da plataforma que está trabalhando para entender sua individualidade e o que pode ser feito ou não! :)

Referências