Especificação da Linguagem de Programação Nozzle
Versão da linguagem 0.1.6 (20 de julho de 2025)
Especificação da Linguagem de Programação Nozzle
Versão da linguagem 0.1.6 (20 de julho de 2025)
Visão geral
O Nozzle é um MVP de linguagem/CLI de automação escrito em Python (compatível com Python 3.4+). Ele:
Transpila um subconjunto de “Nozzle” para Python e executa o resultado.
Fornece um runtime de automação com primitivas para:
Execução local de shell (sh).
Execução remota via SSH com suporte a sudo (ssh).
Transferência de arquivos para hosts remotos via SCP (scp_write, scp_file).
Manipulação de arquivos locais (ler/escrever/verificar/alterar linha).
Template simples com {{ var }} (tpl).
Paralelismo com threads (parallel_for).
Utilitários de JSON (json_parse, json_dump).
Possui modo dry-run para simulações.
Executa scripts .nzl via nozzle [--dry-run] arquivo(.nzl).
Linguagem (transpilação Nozzle → Python)
O transpiler faz substituições linha a linha:
fn nome(...): → def nome(...):
let é removido (permite sintaxe “estilo JS” para atribuições).
true/false/null → True/False/None fora de strings.
Na prática, você escreve algo “parecido com Python”, com açúcar sintático de fn/let e booleanos “JS-like”.
O código transpila e roda em um namespace controlado, com builtins limitados (ex.: print, range, len, Exception, etc.) e todas as funções do runtime já injetadas (sh, ssh, scp_*, …).
Primitivas do runtime
1) Execução de comandos
Local:
sh(cmd) → retorna { 'rc': <int>, 'stdout': <str> }.
Remota via SSH:
ssh(host, cmd, user="root", port=22, key=None, sudo=False)
Usa o cliente ssh do sistema, com StrictHostKeyChecking=no.
sudo=True adiciona sudo -n (sem prompt de senha).
Retorna { 'rc': <int>, 'stdout': <str> }.
2) Arquivos locais
exists(path) → True/False.
read(path) / write(path, text) → leitura/escrita (cria diretório pai se necessário).
replace_line(path, regex, newline) → varre o arquivo e substitui a linha inteira que casar com regex por newline. Retorna quantidade de linhas alteradas.
3) SCP / Deploy de arquivos
scp_write(host, path, text, user="root", port=22, key=None, sudo=False, mode=None, mkdir=True)
Grava conteúdo (string) no host remoto. Faz upload para /tmp/... e depois mv para o destino (opcional mkdir -p e chmod).
scp_file(host, local_path, dest_path, ...)
Envia arquivo local para o destino remoto (mesma estratégia de /tmp + mv).
4) Templates
tpl(text, ctx) → substitui {{ chave }} por valores de ctx.
5) JSON helpers
json_parse(text) → json.loads, retornando {"_error": "..."}
em caso de erro.
json_dump(obj) → json.dumps(...), idem para erros.
6) Paralelismo (threads)
parallel_for(items, func, forks=4)
Executa func(item) em paralelo usando até forks threads.
Preserva a ordem dos resultados conforme a ordem em items.
Retorna uma lista com os retornos de func.
7) Execução de scripts
CLI:
nozzle [--dry-run] [--version] arquivo(.nzl)
Se não houver extensão e existir arquivo.nzl, ele assume .nzl.
--dry-run ativa o modo simulado (não executa de fato comandos/transferências; apenas loga).
O que dá para fazer (exemplos práticos)
A) Rodar comandos locais e remotos
# local
sh("dnf -y update")
# remoto com sudo
ssh("10.0.0.11", "systemctl restart nginx", user="ec2-user", sudo=true)
B) Subir arquivo de configuração gerado por template
let ctx = {"env": "prod", "port": 8080}
let conf = tpl("ENV={{ env }}\nPORT={{ port }}\n", ctx)
# escreve local e também envia para remoto com permissão
write("/tmp/app.env", conf)
scp_write("10.0.0.11", "/etc/app/app.env", conf, sudo=true, mode=600)
C) Substituir linhas em um arquivo (idempotência simples)
# Garante "PermitRootLogin no" no sshd_config
replace_line("/etc/ssh/sshd_config", r"^#?PermitRootLogin.*", "PermitRootLogin no")
D) Instalar pacote remoto (upload + comando)
scp_file("10.0.0.11", "files/agent.rpm", "/tmp/agent.rpm", user="ec2-user", sudo=true)
ssh("10.0.0.11", "rpm -Uvh /tmp/agent.rpm", user="ec2-user", sudo=true)
E) Executar em paralelo sobre uma lista de hosts
let hosts = ["10.0.0.11","10.0.0.12","10.0.0.13"]
fn do_host(h):
return ssh(h, "uname -a")
let results = parallel_for(hosts, do_host, forks=3)
print(results)
F) Tratar erros com try/except (exposto no ambiente)
fn restart_app(h):
try:
return ssh(h, "systemctl restart app", sudo=true)
except Exception as e:
print("erro em", h, e)
return {"rc": 1, "stdout": ""}
let out = parallel_for(["h1","h2"], restart_app, forks=2)
print(out)
G) Usar dry-run para validar sem executar
nozzle --dry-run deploy # também aceita deploy.nzl
Retornos e comportamento
sh/ssh → sempre {rc, stdout}.
replace_line → número de linhas alteradas.
scp_write/scp_file → True se sucesso (lança exceção em erro).
parallel_for → lista de resultados (na mesma ordem de items).
Limitações e observações (pelo código atual)
Segurança SSH: usa StrictHostKeyChecking=no (não valida known_hosts).
Sudo sem senha: sudo -n (falha se exigir senha).
Sem timeouts/retries: ssh/scp dependem das ferramentas do sistema; não há timeouts configuráveis, retries ou backoff.
Sem inventário nativo: você mesmo gerencia listas de hosts (mas parallel_for ajuda).
Sem streaming de logs: saída é coletada ao final do processo do comando.
Sem módulos/plugins: não há sistema de plugins embutido; extensões seriam por código.
Builtins restritos: o ambiente de execução expõe um subconjunto de funções Python.
Idempotência básica: há replace_line, mas não há recursos declarativos/estado desejado completos.
Em resumo
Com o que está implementado, o Nozzle já consegue:
Orquestrar comandos locais e remotos (SSH) com sudo.
Distribuir arquivos e conteúdos gerados (templates) para servidores.
Editar arquivos de configuração de modo simples e repetível.
Rodar em paralelo sobre múltiplos alvos (aumentando velocidade).
Estruturar scripts com sintaxe amigável (fn, let, true/false/null) que são transpilados para Python.
Simular execuções em dry-run para checagem de impacto.
Isso já viabiliza playbooks leves para deploy, configuração e manutenção de servidores.
Se quiser, posso sugerir um roadmap curto (timeouts, inventário, logs estruturados, plugins, variáveis de ambiente/secret management, etc.) a partir desse núcleo. Quer que eu proponha?
Abaixo estão exemplos em Nozzle (.nzl) mostrando laços de repetição (for/while) e contadores — todos compatíveis com o transpiler (pois Nozzle é “Python-like” com fn/let e true/false/null):
1) Contador simples (for + range)
# Contador de 1 a 5
for i in range(1, 6):
print("Iteração:", i)
2) Loop com passo / números pares
# 0, 2, 4, 6, 8
for i in range(0, 10, 2):
print("Par:", i)
3) Contador acumulando soma
let total = 0
for i in range(1, 6): # 1..5
total = total + i
print("Soma de 1 a 5 =", total) # 15
4) Enumerando itens com índice (for + enumerate)
let hosts = ["10.0.0.11", "10.0.0.12", "10.0.0.13"]
for idx, h in enumerate(hosts):
print("Host #", idx, "->", h)
5) Contar sucessos e falhas ao executar comando remoto
let hosts = ["10.0.0.11", "10.0.0.12", "10.0.0.13"]
let ok = 0
let fail = 0
for h in hosts:
let r = ssh(h, "uname -a", user="ec2-user")
if r["rc"] == 0:
ok = ok + 1
else:
fail = fail + 1
print("Sucesso:", ok, "Falha:", fail, "Total:", ok + fail)
6) While loop com contador e condição
let count = 3
while count > 0:
print("Contagem regressiva:", count)
count = count - 1
print("Fim do loop while")
7) Padrão de retry (while + contador de tentativas)
fn run_with_retries(h, cmd, max_tries=3):
let attempt = 1
while attempt <= max_tries:
let r = ssh(h, cmd, sudo=true)
if r["rc"] == 0:
print("OK em tentativa", attempt, "->", h)
return r
print("Falha tentativa", attempt, "->", h, "rc=", r["rc"])
attempt = attempt + 1
return r # retorna o último resultado (falhou todas)
# Uso
let hosts = ["10.0.0.11", "10.0.0.12"]
for h in hosts:
run_with_retries(h, "systemctl restart nginx")
8) Contando alterações de configuração (replace_line)
# Garante uma linha declarativa e conta mudanças
let changed = replace_line("/etc/ssh/sshd_config", r"^#?PermitRootLogin.*", "PermitRootLogin no")
print("Linhas alteradas:", changed)
9) Loop aninhado (grupos x hosts) + contador total
let grupos = {
"web": ["10.0.0.11", "10.0.0.12"],
"db": ["10.0.0.21"]
}
let total_rc0 = 0
for g in grupos:
print("Grupo:", g)
for h in grupos[g]:
let r = ssh(h, "hostname")
if r["rc"] == 0:
total_rc0 = total_rc0 + 1
print("Hosts com rc=0:", total_rc0)
10) Paralelismo com contador (usando parallel_for)
let hosts = ["10.0.0.11","10.0.0.12","10.0.0.13"]
fn check_host(h):
return ssh(h, "uptime")
let results = parallel_for(hosts, check_host, forks=3)
# Conta quantos retornaram rc=0
let ok = 0
for r in results:
if r and r["rc"] == 0:
ok = ok + 1
print("OK:", ok, "de", len(results))
11) Exemplo completo: contador + template + envio
let hosts = ["10.0.0.11","10.0.0.12"]
let applied = 0
for h in hosts:
let cfg = tpl("ENV={{ env }}\nPORT={{ port }}\n", {"env": "prod", "port": 8080})
scp_write(h, "/etc/app/app.env", cfg, sudo=true, mode=600)
let r = ssh(h, "systemctl restart app", sudo=true)
if r["rc"] == 0:
applied = applied + 1
print("Aplicado com sucesso em", applied, "de", len(hosts), "hosts")
Dica de uso em simulação (sem executar de fato):
nozzle --dry-run meu-script # ou meu-script.nzl
Se quiser, eu junto esses exemplos em um arquivo .nzl organizado por seções (loops, contadores, retry, paralelo) para você colar direto no seu repositório. Quer que eu gere esse arquivo?
Exemplos de uso de if / else no Nozzle, todos compatíveis com o transpiler (lembrando que a sintaxe é Python-like com suporte a true/false/null):
1) If simples
let rc = sh("ls /tmp")["rc"]
if rc == 0:
print("Comando executado com sucesso")
2) If / Else
let r = ssh("10.0.0.11", "hostname")
if r["rc"] == 0:
print("Host respondeu:", r["stdout"])
else:
print("Falha ao conectar ao host")
3) If / Else If / Else
let uso = 85
if uso >= 90:
print("ALERTA: Uso de CPU crítico!")
elif uso >= 70:
print("Atenção: Uso de CPU alto")
else:
print("CPU dentro do normal")
4) If dentro de loop para filtrar hosts
let hosts = ["10.0.0.11", "10.0.0.12", "10.0.0.13"]
for h in hosts:
let r = ssh(h, "systemctl is-active nginx", user="ec2-user")
if r["stdout"].strip() == "active":
print(h, "-> Nginx está ativo")
else:
print(h, "-> Nginx está parado ou falhou")
5) If com string vazia / não vazia
let saida = sh("cat /etc/os-release")["stdout"]
if saida:
print("Arquivo encontrado e lido com sucesso")
else:
print("Arquivo não encontrado ou vazio")
6) If verificando existência de arquivo
if exists("/etc/hosts"):
print("O arquivo /etc/hosts existe")
else:
print("Arquivo não encontrado")
7) If + Template + SCP condicional
let cfg = tpl("ENV={{ env }}\nPORT={{ port }}\n", {"env": "dev", "port": 8080})
if cfg:
scp_write("10.0.0.11", "/etc/app/app.env", cfg, sudo=true, mode=600)
print("Config enviada com sucesso")
else:
print("Não foi possível gerar o template")
8) If com replace_line para controle de alteração
let changes = replace_line("/etc/ssh/sshd_config", r"^#?PermitRootLogin.*", "PermitRootLogin no")
if changes > 0:
print("Arquivo alterado, reiniciando sshd")
ssh("10.0.0.11", "systemctl restart sshd", sudo=true)
else:
print("Nenhuma alteração necessária")
Esses exemplos permitem construir lógica condicional para deploy, configuração e auditoria de maneira simples no Nozzle.
Exemplos aritméticos em Nozzle, usando operadores compatíveis com Python (pois Nozzle é transpilado para Python):
1) Operações básicas
let a = 10
let b = 3
print("Soma:", a + b) # 13
print("Subtração:", a - b) # 7
print("Multiplicação:", a * b) # 30
print("Divisão real:", a / b) # 3.3333
print("Divisão inteira:", a // b) # 3
print("Resto (módulo):", a % b) # 1
print("Potência:", a ** b) # 1000
2) Incremento e decremento de contador
let count = 0
for i in range(1, 6):
count = count + i
print("Somatório parcial:", count)
print("Total:", count)
3) Média aritmética
let numeros = [10, 20, 30, 40, 50]
let soma = 0
for n in numeros:
soma = soma + n
let media = soma / len(numeros)
print("Média =", media)
4) Cálculo de porcentagem
let total = 200
let parte = 45
let percentual = (parte / total) * 100
print("Percentual:", percentual, "%")
5) Cálculo com condicionais (básico de alerta)
let cpu = 72
let limite = 80
let diferenca = limite - cpu
if cpu >= limite:
print("ALERTA: CPU", cpu, "% ultrapassou limite de", limite)
else:
print("CPU abaixo do limite, faltam", diferenca, "% para alerta")
Esses exemplos cobrem soma, subtração, multiplicação, divisão, módulo, potência e uso em laços e condicionais, que são comuns para contadores, métricas, somatórios e cálculos em automação.
1) Guia da linguagem (Nozzle → Python)
Sintaxe completa suportada pelo transpiler: fn, let, comentários, strings, listas/dicts, true/false/null, try/except.
Padrões de código: indentação, nomes de funções/variáveis, retornos.
Limitações atuais da linguagem (o que não compila ainda).
Ação rápida: criar uma “Referência de Sintaxe” (1 página) com exemplos mínimos lado a lado (Nozzle vs Python).
2) Referência do Runtime (API)
Documentar cada função exposta com assinatura, parâmetros, retorno e exemplos:
sh(cmd) e ssh(host, cmd, user, port, key, sudo)
Retornos {rc, stdout}, convenções de rc, exemplos com sudo.
scp_write, scp_file
Estratégia /tmp + mv, mode, mkdir, requisitos do remoto (SSH/sudo).
exists, read, write, replace_line
Idempotência básica: quando replace_line retorna 0/1+, exemplos práticos.
tpl(text, ctx)
Escopo de variáveis, placeholders ausentes, exemplos de multi-linha.
parallel_for(items, func, forks)
Ordenação dos resultados, padrão de uso, quando não usar.
json_parse, json_dump
Tratamento de erro ({"_error": ...}), charset.
Ação rápida: uma tabela “Resumo de Funções” + exemplos copy/paste.
3) Execução, Dry-run e Convenções de Saída
CLI: nozzle [--dry-run] [--version] arquivo(.nzl).
Dry-run: o que é simulado (SSH/SCP/FS) e o que ainda acontece (logs).
Códigos de saída da CLI (sys.exit): 0 sucesso, 1 uso inválido, 2 erro de execução, 3 arquivo ausente.
Logs: formato atual (stdout), ideia de padronizar (ex.: prefixos [local], [host]).
Ação rápida: “Como testar com segurança: dry-run + checagens”.
4) Segurança e Operação
SSH: StrictHostKeyChecking=no (implicações), recomendação de bastion, chaves, usuários, sudo -n.
Privilégios: quando exigir sudo=true, políticas para NOPASSWD.
Segredos: hoje não há vault; sugerir boas práticas (variáveis de ambiente, arquivos fora do repo, integração futura).
Auditoria: guardar stdout/stderr por host (futuro: JSONL de execuções).
Ação rápida: uma página “Boas práticas de segurança”.
5) Confiabilidade e Desempenho
Timeouts/retries: não existem ainda (sugerir padrões com while/try + backoff).
Paralelismo: forks e sizing (ex.: min(len(hosts), 8)).
Idempotência: padrões com exists/replace_line/grep para evitar mudanças desnecessárias.
Ação rápida: seção “Padrões de retry e backoff” com snippet pronto.
6) Organização de Projetos Nozzle
Estrutura sugerida:
project/
playbooks/
files/
templates/
inventory.json (ou .py/.nzl)
vars/ (dev, qa, prod)
scripts/
Inventário: hoje manual; exemplos de como carregar listas/dicts e iterar.
Naming: deploy.nzl, hardening.nzl, checks.nzl.
Reutilização: fn como “módulos” (até haver plugins).
Ação rápida: repo-exemplo com 2–3 playbooks.
7) Exemplos “de verdade” (end-to-end)
Provisionamento leve: subir conf + restart serviço.
Hardening: 3–5 regras (replace_line) com contador de mudanças.
Rolling restart em grupo (web/db) com verificação health.
Relatório: coletar uname/uptime/df -h em N hosts e gerar JSON.
Ação rápida: pasta examples/ com .nzl prontos.
8) Compatibilidade e Requisitos
Python: 3.4+ (do código), binários necessários: ssh, scp.
Sistemas: Linux/macOS/WSL (testado), observações para Windows.
Dependências externas: nenhuma lib Python adicional para o core.
Ação rápida: tabela de compatibilidade + “como instalar”.
9) Roadmap público (curto prazo)
Timeouts e retries nativos em ssh/scp/sh.
Inventário nativo (arquivo YAML/JSON).
Níveis de log e formato estruturado (JSONL).
Plugins/Módulos (padrão de extensão).
Gestão de segredos (env/arquivo/integrações).
Validação de host key opcional (segurança).
Ação rápida: arquivo ROADMAP.md.
10) Comunidade e Contribuição
Guia de contribuição: como abrir PR, padrão de testes, estilo.
Changelog: CHANGELOG.md.
Licença (ex.: MIT/Apache-2.0).
Code of Conduct.
Ação rápida: CONTRIBUTING.md, CODE_OF_CONDUCT.md, LICENSE.
11) FAQ / Solução de Problemas
“SSH falha com sudo -n”
“Permissão negada ao mover arquivo” (usar sudo + chmod).
“Script não encontrado” (sufixo .nzl).
“Parallel travado” (hosts off-line; necessidade de timeouts).
Ação rápida: docs/faq.md com respostas curtas.
1. Posicionamento e Caso de Uso
Uma seção “Quando usar Nozzle”
Para quem precisa de automação simples e rápida sem instalar Ansible/Chef/Puppet.
Cenários ideais: hardening leve, deploys pontuais, tarefas em ambientes pequenos/médios.
Uma seção “Quando NÃO usar Nozzle”
Se o ambiente exige gerenciamento de estado complexo, alta disponibilidade ou milhares de hosts em paralelo, ferramentas maduras de DevOps são recomendadas.
2. Fluxo de Trabalho Recomendado
Explicar em 4 passos claros:
Escrever script .nzl
Testar com --dry-run
Executar em ambiente de teste
Aplicar em produção
Isso ajuda iniciantes a adotarem sem medo.
3. Exemplo End-to-End
Um playbook real completo com:
Upload de template
Edição de config (replace_line)
Restart de serviço
Paralelismo em hosts múltiplos
Uso de dry-run antes do deploy
Isso mostra o poder do Nozzle em 1 script único.
4. Seção de Benchmark ou Comparação
Comparar tamanho do código e setup com Ansible ou Fabric para mostrar que o Nozzle é leve.
Mostrar que é zero dependência, só Python + SSH/SCP.
5. Roadmap Visual
Mostrar linha do tempo ou bullets do que vem a seguir (timeouts, inventário YAML, plugins, logging estruturado).
Passa sensação de projeto vivo.