Ir para o conteúdo

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 faz
  • directory: 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.json vá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:

{
  "name": "meu-plugin",
  "version": "1.0.0",
  "directory": "src"
}

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/:

mkdir -p plugins/meu-plugin/src/categoria/comando

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

chmod +x plugins/meu-plugin/src/categoria/comando/main.sh

✅ Vantagens

  1. Modularidade: Cada comando é auto-contido
  2. Plugins Externos: Fácil adicionar comandos sem modificar o core
  3. Isolamento: Plugins não quebram outros comandos
  4. Distribuição: Comandos podem ser compartilhados como repositórios Git
  5. Versionamento: Cada plugin tem sua própria versão via plugin.json
  6. Validação: Plugin.json obrigatório garante qualidade e compatibilidade
  7. Metadados: Descrição e informações organizadas em um único arquivo

🚀 Comandos de Gerenciamento

Listar Plugins

susa self plugin list

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

susa self plugin remove plugin-name

Remove completamente:

  • Diretório do plugin
  • Entrada no registry.json

Atualizar Plugin

susa self plugin update plugin-name

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:

{
  "name": "my-cli-plugin",
  "version": "1.0.0",
  "description": "Meu plugin CLI"
}

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:

{
  "name": "Demo",
  "description": "Comandos de demonstração",
  "entrypoint": "main.sh"
}

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:

  1. Verifica no lock se categoria tem entrypoint
  2. Identifica se é plugin verificando comandos da categoria
  3. Obtém source do plugin do campo plugin.source no lock
  4. Constrói path correto:
  5. Plugin instalado: $CLI_DIR/plugins/<nome>/<categoria>/<entrypoint>
  6. Plugin dev: <source>/<categoria>/<entrypoint>
  7. Considera directory do 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 + mostra show_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:

  1. Busca em commands/categoria/ (built-in)
  2. Busca em plugins/*/categoria/ (externos)
  3. Filtra por compatibilidade de SO
  4. 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ção
  • commands: 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: true para 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 source para 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 /tmp futuramente