Arquitetura de Plugins¶
📋 Visão Geral¶
O CLI agora suporta uma arquitetura descentralizada com suporte a plugins externos. Cada comando tem sua própria configuração local, facilitando a modularidade e extensibilidade.
🏗️ Estrutura¶
cli/
├── core/ # Core do CLI
│ ├── susa # Entrypoint principal
│ ├── cli.json # Config global (nome, versão, categorias)
│ └── lib/ # Bibliotecas
│
├── commands/ # Comandos built-in
│ ├── install/
│ │ ├── asdf/
│ │ │ ├── command.json # Config do comando
│ │ │ └── main.sh # Script
│ │ └── docker/
│ │ ├── command.json
│ │ └── main.sh
│ └── daily/
│ └── backup/
│ ├── command.json
│ └── main.sh
└── plugins/ # Plugins externos
├── registry.json # Registro de plugins
└── backup-tools/ # Exemplo de plugin
├── plugin.json # ⚠️ Config do plugin (OBRIGATÓRIO)
└── daily/
└── backup-s3/
├── command.json
└── main.sh
📝 Formato do command.json¶
Cada comando deve ter um arquivo command.json no seu diretório:
{
"name": "Backup S3",
"description": "Descrição",
"entrypoint": "main.sh",
"sudo": false,
"os": ["linux", "mac"],
"group": "Backups",
"envs": {
"BACKUP_BUCKET": "my-bucket-name",
"BACKUP_TIMEOUT": "300",
"BACKUP_DIR": "$HOME/.backups"
}
}
Variáveis de Ambiente (envs)¶
Plugins suportam variáveis de ambiente isoladas da mesma forma que comandos built-in.
Definição no command.json:
{
"envs": {
"DEPLOY_API_URL": "https://api.example.com",
"DEPLOY_TIMEOUT": "60",
"DEPLOY_RETRY": "3",
"DEPLOY_CONFIG_DIR": "$HOME/.config/deploy",
"DEPLOY_LOG_FILE": "$PWD/logs/deploy.log",
"DEPLOY_API_TOKEN": "secret-token"
}
}
Uso no main.sh:
#!/bin/bash
# Sempre use fallback
api_url="${DEPLOY_API_URL:-https://default.com}"
timeout="${DEPLOY_TIMEOUT:-30}"
config_dir="${DEPLOY_CONFIG_DIR:-$HOME/.config/deploy}"
curl --max-time "$timeout" "$api_url"
Características:
- ✅ Carregamento automático pelo framework
- ✅ Expansão de variáveis (
$HOME,$USER,$PWD) - ✅ Isolamento total entre comandos
- ✅ Override via variáveis de sistema:
DEPLOY_TIMEOUT=120 susa deploy staging - ✅ Mesma precedência: Sistema > Config > Padrão no script
Documentação completa: Guia de Variáveis de Ambiente
� Formato do plugin.json¶
⚠️ OBRIGATÓRIO: Todo plugin deve ter um arquivo plugin.json na raiz do diretório do plugin.
{
"name": "backup-tools",
"version": "1.2.0",
"description": "Ferramentas de backup e restore",
"directory": "src"
}
Campos¶
Obrigatórios:
name: Nome do plugin (usado para identificação)version: Versão no formato semver (ex: 1.0.0, 2.1.3)
Opcionais:
description: Descrição do que o plugin fazdirectory: Subdiretório onde os comandos estão localizados (útil para organização)
Validação¶
O sistema valida o plugin.json durante a instalação:
- ✅ Arquivo deve existir na raiz do plugin
- ✅ JSON deve ser válido (sem erros de sintaxe)
- ✅ Campo
nameé obrigatório e não pode estar vazio - ✅ Campo
versioné obrigatório e não pode estar vazio - ⚠️ Plugins sem
plugin.jsonválido serão rejeitados
Campo directory¶
O campo directory permite organizar seus comandos em um subdiretório específico do plugin.
Para que serve: Separar os comandos do plugin de outros arquivos (README, testes, docs), mantendo uma estrutura organizada.
Onde usar:
Configure este campo no plugin.json quando seus comandos não estão na raiz do repositório:
Como funciona:
Quando o Susa executa um comando do plugin, ele busca automaticamente no diretório especificado. Por exemplo, com "directory": "src", o comando demo hello será buscado em:
meu-plugin/
├── plugin.json # directory: "src"
├── README.md
└── src/ # Comandos aqui dentro
└── demo/
└── hello/
├── command.json
└── main.sh
O sistema automaticamente resolve o caminho correto usando as informações do susa.lock.
🔌 Como Criar um Plugin¶
1. Crie o plugin.json (OBRIGATÓRIO)¶
Na raiz do seu plugin, crie o arquivo plugin.json:
{
"name": "meu-plugin",
"version": "1.0.0",
"description": "Descrição do meu plugin",
"directory": "src"
}
2. Estrutura Básica¶
Crie um diretório dentro de plugins/:
3. Crie o command.json do Comando¶
{
"name": "Meu Comando",
"description": "Descrição do comando",
"entrypoint": "main.sh",
"sudo": false,
"os": ["linux"],
"envs": {
"MY_API_URL": "https://api.example.com",
"MY_TIMEOUT": "30"
}
}
4. Crie o Script¶
#!/bin/bash
# Variáveis disponíveis automaticamente
api_url="${MY_API_URL:-https://default.com}"
timeout="${MY_TIMEOUT:-30}"
echo "Conectando em $api_url (timeout: ${timeout}s)"
curl --max-time "$timeout" "$api_url"
5. Torne Executável¶
✅ Vantagens¶
- Modularidade: Cada comando é auto-contido
- Plugins Externos: Fácil adicionar comandos sem modificar o core
- Isolamento: Plugins não quebram outros comandos
- Distribuição: Comandos podem ser compartilhados como repositórios Git
- Versionamento: Cada plugin tem sua própria versão via plugin.json
- Validação: Plugin.json obrigatório garante qualidade e compatibilidade
- Metadados: Descrição e informações organizadas em um único arquivo
🚀 Comandos de Gerenciamento¶
Listar Plugins¶
Mostra todos os plugins instalados com:
- Origem (URL Git)
- Versão
- Número de comandos
- Categorias
- Data de instalação
Instalar Plugin¶
# De URL completa
susa self plugin add https://github.com/user/cli-plugin-name
# Atalho GitHub
susa self plugin add user/cli-plugin-name
# Modo desenvolvimento (local)
susa self plugin add /caminho/para/meu-plugin
susa self plugin add .
Durante a instalação:
- Clona o repositório (ou referencia caminho local)
- Valida plugin.json (obrigatório)
- Lê metadados do plugin (nome, versão, descrição)
- Conta comandos e categorias
- Registra no registry.json
- ⚠️ Rejeita plugins sem plugin.json válido
Remover Plugin¶
Remove completamente:
- Diretório do plugin
- Entrada no registry.json
Atualizar Plugin¶
Atualiza o plugin para a versão mais recente:
- Obtém URL de origem do registry
- Faz backup temporário do plugin atual
- Clona versão mais recente do repositório
- Atualiza informações no registry (versão, data)
- Remove backup se sucesso, restaura se falha
Requisitos:
- Plugin deve ter sido instalado via
susa self plugin add - Origem deve ser um repositório Git válido
- Plugins locais não podem ser atualizados
📦 Distribuindo Plugins¶
Plugins podem ser distribuídos como repositórios Git:
# Estrutura do repositório
my-cli-plugin/
├── plugin.json
├── README.md
└── daily/
└── meu-comando/
├── category.json
└── main.sh
plugin.json obrigatório:
Usuários podem instalar diretamente:
# Via GitHub
susa self plugin add user/my-cli-plugin
# Via URL completa
susa self plugin add https://github.com/user/my-cli-plugin.git
⚠️ Importante: Plugins sem plugin.json válido serão rejeitados durante a instalação.
🎨 Categorias com Entrypoint em Plugins¶
Plugins suportam o mesmo sistema de categorias com entrypoint que comandos built-in.
Estrutura¶
meu-plugin/
├── plugin.json
└── demo/
├── category.json # ← Com campo entrypoint
├── main.sh # ← Script da categoria
├── hello/
│ ├── command.json
│ └── main.sh
└── info/
├── command.json
└── main.sh
Configuração¶
demo/category.json:
demo/main.sh:
#!/bin/bash
set -euo pipefail
IFS=$'\n\t'
source "$LIB_DIR/logger.sh"
# Função chamada ao listar comandos da categoria
show_complement_help() {
echo ""
log_output "Opções da categoria:"
log_output " --list Lista comandos"
log_output " --about Sobre o plugin"
}
main() {
case "${1:-}" in
--list)
# Listar comandos da categoria
jq -r '.commands[] | select(.category == "demo")' "$CLI_DIR/susa.lock"
;;
--about)
echo "Informações do plugin..."
;;
*)
log_error "Opção desconhecida: $1"
exit 1
;;
esac
}
# IMPORTANTE: Controle de execução
if [ "${SUSA_SKIP_MAIN:-}" != "1" ]; then
main "$@"
fi
Resolução de Paths¶
O sistema resolve automaticamente o path do script da categoria:
- Verifica no lock se categoria tem
entrypoint - Identifica se é plugin verificando comandos da categoria
- Obtém source do plugin do campo
plugin.sourceno lock - Constrói path correto:
- Plugin instalado:
$CLI_DIR/plugins/<nome>/<categoria>/<entrypoint> - Plugin dev:
<source>/<categoria>/<entrypoint> - Considera
directorydo plugin.json se configurado
Variáveis Disponíveis¶
O script da categoria tem acesso às mesmas variáveis que comandos:
$CLI_DIR- Diretório base do CLI$CORE_DIR- Diretório do core$LIB_DIR- Diretório das bibliotecas$SUSA_SKIP_MAIN- Flag de controle (setada pelo sistema)
Comportamento¶
- Sem argumentos (
susa demo): Lista comandos + mostrashow_complement_help() - Com argumentos (
susa demo --list): Executa script da categoria - Comando específico (
susa demo hello): Executa comando normalmente
🔍 Discovery de Comandos¶
O sistema descobre comandos automaticamente:
- Busca em
commands/categoria/(built-in) - Busca em
plugins/*/categoria/(externos) - Filtra por compatibilidade de SO
- Aplica permissões (sudo)
📋 Registry (plugins/registry.json)¶
O registry mantém controle de todos os plugins:
{
"version": "1.0.0",
"plugins": [
{
"name": "backup-tools",
"source": "https://github.com/user/backup-tools.git",
"version": "1.2.0",
"description": "Ferramentas de backup e restore",
"installedAt": "2026-01-11T22:30:00Z",
"commands": 4,
"categories": "backup, restore",
"dev": false
}
]
}
Campos:
name: Nome do plugin (do plugin.json)source: URL do repositório Git ou caminho local (modo dev)version: Versão instalada (do plugin.json)description: Descrição do plugin (do plugin.json, opcional)installedAt: Data/hora da instalaçãocommands: Quantidade de comandos disponíveis (calculado automaticamente)categories: Lista de categorias de comandos (calculado automaticamente)dev: Flag indicando se é plugin em desenvolvimento local
Funcionalidades:
- Tracking: Origem, versão, data de instalação
- Histórico: Mantém registro de todos os plugins
- Metadados: Comandos e categorias para listagem rápida
- Dev Mode: Campo
dev: truepara plugins em desenvolvimento - Performance: Evita varredura de diretórios ao listar plugins
📄 Lock File (susa.lock)¶
O arquivo susa.lock contém cache de todos os comandos, incluindo campo source para resolução de paths:
{
"commands": [
{
"category": "deploy",
"name": "staging",
"description": "Deploy para staging",
"plugin": {
"name": "backup-tools",
"source": "/home/user/.config/susa/plugins/backup-tools"
}
}
]
}
Campo source no plugin:
- Plugins instalados: Aponta para
$CLI_DIR/plugins/nome-plugin - Plugins dev: Aponta para diretório atual do plugin
- Uso: Sistema usa
sourcepara construir path completo do script
⚡ Performance¶
- Lazy Loading: Configs são lidas apenas quando necessário
- Filesystem-based: Não precisa parsear JSON central
- Cache: Possível implementar cache em
/tmpfuturamente