Ir para o conteúdo

Sistema de Categorias e Subcategorias Aninhadas

📋 Visão Geral

O CLI suporta uma estrutura hierárquica de categorias e subcategorias baseada em diretórios, permitindo organizar comandos em múltiplos níveis de profundidade.

📖 Pré-requisito: Este guia assume que você já conhece os conceitos básicos de estrutura de comandos e criação de scripts. Se não, veja primeiro Como Adicionar Novos Comandos.

🏗️ Estrutura de Diretórios

Diferença entre Comandos e Subcategorias

🔑 Regra Fundamental: A existência do script executável determina o comportamento!

O sistema verifica:

  1. Se o diretório tem arquivo de configuração (command.json ou category.json)
  2. Se o command.json tem o campo entrypoint: definido
  3. Se o arquivo do script existe

Resultado:

  • Tem entrypoint: E arquivo existe → É um comando executável
  • Sistema executa o script
  • Aparece na seção "Commands"

  • Não tem entrypoint: OU arquivo não existe → É uma subcategoria navegável

  • Sistema permite navegar (listar sub-itens)
  • Aparece na seção "Subcategories"

Arquivos de Configuração Diferenciados

Tanto comandos quanto categorias usam arquivos de configuração, mas com campos diferentes:

Tipo Campos de configuração
Comando (command.json) name, description, entrypoint (obrigatório), sudo, os
Categoria/Subcategoria (category.json) name, description, entrypoint (opcional)

ℹ️ Para detalhes completos sobre campos de configuração, veja Configuração de Comandos.

Vantagens dessa abordagem:

  • ✅ Mais intuitivo: "tem script = é executável"
  • ✅ Mais consistente: categorias usam category.json e comandos usam command.json
  • ✅ Mais lógico: comandos PRECISAM de script, subcategorias não
  • ✅ Categorias podem ter entrypoint para aceitar parâmetros

Estrutura Exemplo

commands/
  setup/                            # Categoria principal
    category.json                     # name, description (sem entrypoint)
    asdf/                           # Comando direto
      command.json                   # category, id, name, description, script, sudo, os
      main.sh                       # Script executável
    python/                         # Subcategoria
      command.json                   # name, description (sem script)
      pip/                          # Comando
        command.json                 # category, id, name, description, script
        main.sh
      poetry/                       # Comando
        command.json
        main.sh
      tools/                        # Sub-subcategoria (nível 3)
        command.json                 # name, description (sem script)
        venv/                       # Comando nível 3
          command.json               # category, id, name, description, entrypoint
          main.sh
    nodejs/                         # Subcategoria
      command.json                   # name, description (sem script)
      npm/                          # Comando
        command.json                 # category, id, name, description, script
        main.sh

Comandos de Navegação

# Listar categorias principais
susa

# Listar subcategorias e comandos de uma categoria
susa setup

# Navegar para uma subcategoria
susa setup python

# Navegar para sub-subcategoria (nível 3)
susa setup python tools

# Executar comando direto
susa setup asdf

# Executar comando em subcategoria
susa setup python pip

# Executar comando em sub-subcategoria
susa setup python tools venv

📝 Arquivos de Configuração

Arquivos de Configuração

Categorias e subcategorias usam category.json, enquanto comandos usam command.json. A diferença está nos campos definidos.

1. Categoria/Subcategoria (Navegável)

Usado para itens que contêm outros itens.

{
  "name": "Python",
  "description": "Ferramentas Python"
}

Localização: commands/{categoria}/category.json ou commands/{categoria}/{subcategoria}/command.json

2. Comando (Executável)

Configuração completa de um comando executável.

{
  "name": "Pip",
  "description": "Instala gerenciador de pacotes Python (pip)",
  "entrypoint": "main.sh",
  "sudo": false,
  "os": ["linux", "mac"]
}

Localização: commands/{categoria}/.../{comando}/command.json

Importante: O arquivo definido em entrypoint: DEVE existir e ter permissão de execução.

✨ Campos de Configuração

📖 Referência completa: Veja Configuração de Comandos para detalhes sobre todos os campos disponíveis.

Resumo Rápido

Para Comandos (Executáveis):

  • Devem ter o campo entrypoint: apontando para um arquivo executável
  • Exemplo: entrypoint: "main.sh"

Para Subcategorias (Navegáveis):

  • NÃO devem ter o campo entrypoint
  • Apenas name e description

Lista de Categoria com Subcategorias

Instalar software (Ubuntu)

Usage: susa setup <command> [options]

Subcategories:
  nodejs          Ferramentas Node.js
  python          Ferramentas Python

Commands:
  asdf            Instala ASDF Version Manager

Lista de Subcategoria

Ferramentas Python

Usage: susa setup/python <command> [options]

Subcategories:
  tools           Ferramentas Python Avançadas

Commands:
  pip             Instala gerenciador de pacotes Python (pip)
  poetry          Instala Poetry (gerenciador de dependências Python)

🚀 Como Adicionar Comandos em Hierarquias

📖 Para criar comandos simples, veja Como Adicionar Novos Comandos. Esta seção foca em estruturas hierárquicas com subcategorias.

1. Comando em Categoria Existente

Veja guia básico para detalhes.

Resumo:

mkdir -p commands/setup/comando-novo
# Criar arquivos de configuração e main.sh conforme guia básico

2. Comando em Nova Subcategoria

A diferença principal: criar um category.json sem campo entrypoint para a subcategoria.

# Criar estrutura
mkdir -p commands/install/nova-categoria/comando-xyz

# Criar configuração da subcategoria (SEM campo 'script')
cat > commands/install/nova-categoria/category.json << EOF
name: "Nova Categoria"
description: "Descrição da nova categoria"
# Sem campo 'script' = subcategoria navegável
EOF

# Criar configuração do comando (COM campo 'script')
cat > commands/install/nova-categoria/comando-xyz/command.json << EOF
name: "Comando XYZ"
description: "Descrição do comando XYZ"
entrypoint: "main.sh"       # ← Indica que é executável
sudo: false
EOF

# Criar script
cat > commands/install/nova-categoria/comando-xyz/main.sh << 'EOF'
#!/bin/bash
echo "Executando XYZ!"
EOF

# Tornar executável
chmod +x commands/setup/nova-categoria/comando-xyz/main.sh

Uso: ./susa setup nova-categoria comando-xyz

3. Comando em Sub-Subcategoria (3 níveis)

# Criar estrutura completa
mkdir -p commands/install/categoria/subcategoria/comando

# Criar arquivos de configuração para cada nível navegável
cat > commands/install/categoria/category.json << EOF
name: "Categoria"
description: "Nível 1"
EOF

cat > commands/install/categoria/subcategoria/category.json << EOF
name: "Subcategoria"
description: "Nível 2"
EOF

# Criar comando executável (COM campo 'script')
cat > commands/install/categoria/subcategoria/comando/command.json << EOF
name: "Comando"
description: "Comando no nível 3"
entrypoint: "main.sh"       # ← Indica que é executável
sudo: false
EOF

cat > commands/install/categoria/subcategoria/comando/main.sh << 'EOF'
#!/bin/bash
echo "Comando profundo!"
EOF

chmod +x commands/setup/categoria/subcategoria/comando/main.sh

Uso: ./susa setup categoria subcategoria comando

🔍 Descoberta Automática

O sistema descobre automaticamente:

  • ✅ Todas as categorias em commands/
  • ✅ Todas as subcategorias (diretórios com category.json)
  • ✅ Todos os comandos (diretórios com command.json e campo entrypoint)
  • ✅ Múltiplos níveis de aninhamento
  • ✅ Comandos em plugins externos

Não é necessário registrar manualmente categorias ou comandos em arquivos centrais.

📦 Plugins

Plugins também suportam a mesma estrutura hierárquica com subcategorias aninhadas:

plugins/
  dev-tools/                    # Plugin
    deploy/                     # Categoria
      command.json               # name, description (sem script)
      staging/                  # Comando
        command.json             # name, description, script
        main.sh
      production/               # Comando
        command.json
        main.sh
      aws/                      # Subcategoria
        command.json             # name, description (sem script)
        ec2/                    # Comando
          command.json           # name, description, entrypoint
          main.sh
        lambda/                 # Comando
          category.json
          main.sh
    test/                       # Categoria
      command.json
      unit/                     # Comando
        command.json
        main.sh
      integration/              # Comando
        command.json
        main.sh
# Listar categorias do plugin
./susa deploy                    # Mostra: staging, production, aws (subcategoria)

# Navegar para subcategoria
./susa deploy aws                # Mostra: ec2, lambda

# Executar comando em subcategoria
./susa deploy aws ec2            # Executa deploy EC2

# Outro exemplo
./susa test                      # Mostra: unit, integration
./susa test unit                 # Executa testes unitários

Importante sobre Plugins

✅ Plugins funcionam exatamente como commands/:

  • Mesma lógica de detecção (script = comando, sem script = subcategoria)
  • Mesma estrutura de arquivos de configuração (command.json/category.json)
  • Mesma navegação multinível
  • Mesma descoberta automática

Diferenças:

  • Diretório: plugins/{nome-plugin}/ ao invés de commands/
  • Comandos de plugins exibem o indicador [plugin] na listagem

Exemplo de listagem com plugins:

Commands:
  asdf           Instala ASDF Version Manager
  staging        Deploy para staging [plugin]
  production     Deploy produção (requer sudo) [plugin] [sudo]

🎨 Categorias com Parâmetros (Feature Avançada)

Visão Geral

Categorias podem ter um entrypoint opcional que permite aceitar parâmetros diretamente, sem precisar criar comandos individuais. Isso é útil para operações em massa ou ações que afetam todos os comandos da categoria.

Como Funciona

Quando uma categoria tem um entrypoint:

  1. Sem parâmetros (susa setup): Lista comandos normalmente + mostra help complementar
  2. Com parâmetros (susa setup --upgrade): Executa o script da categoria
  3. Comandos específicos (susa setup docker): Funciona normalmente

Configuração da Categoria

category.json com entrypoint:

{
  "name": "Setup",
  "description": "Instalação e atualização de softwares e ferramentas",
  "entrypoint": "main.sh"
}

Script da Categoria (main.sh)

O script deve implementar a função show_complement_help() para exibir ajuda adicional:

#!/bin/bash
set -euo pipefail
IFS=$'\n\t'

# Source libraries
source "$LIB_DIR/logger.sh"
source "$LIB_DIR/color.sh"

# Show complement help (exibida ao final da listagem de comandos)
show_complement_help() {
    echo ""
    log_output "${LIGHT_GREEN}Opções da categoria:${NC}"
    log_output "  -u, --upgrade    Atualiza todos os softwares instalados"
    log_output "  --list           Lista todos os softwares instalados"
}

upgrade_all() {
    # criar logica
}

list_installed() {
    # criar logica
}

# Main function
main() {
    while [[ $# -gt 0 ]]; do
        case "$1" in
            -u|--upgrade)
                upgrade_all
                exit 0
                ;;
            --list)
                list_installed
                exit 0
                ;;
            *)
                log_error "Opção desconhecida: $1"
                echo ""
                log_output "Use ${LIGHT_CYAN}susa setup --help${NC} para ver as opções"
                exit 1
                ;;
        esac
    done
}

# Execute main (controlado por SUSA_SKIP_MAIN)
if [ "${SUSA_SKIP_MAIN:-}" != "1" ]; then
    main "$@"
fi

Importante sobre SUSA_SKIP_MAIN

A variável SUSA_SKIP_MAIN é usada pelo sistema para evitar execução do main quando apenas quer chamar show_complement_help:

# Sempre adicione esta condição no final do script
if [ "${SUSA_SKIP_MAIN:-}" != "1" ]; then
    main "$@"
fi

Isso permite que o sistema:

  1. Execute o script normalmente quando o usuário passa parâmetros
  2. Apenas source o script e chame show_complement_help() ao listar comandos

Exemplo de Uso

# Lista comandos + mostra help complementar ao final
$ susa setup
Instalação e atualização de softwares e ferramentas

Uso: susa <comando> setup

Comandos:
  docker          Instala Docker
  poetry          Instala Poetry
  uv              Instala UV

Opções da categoria:
  -u, --upgrade    Atualiza todos os softwares instalados
  --list           Lista todos os softwares instalados

# Executa ação em massa
$ susa setup --upgrade
[INFO] Atualizando softwares instalados...
[INFO] Atualizando docker...
[SUCCESS] Docker atualizado!
...

# Lista softwares instalados
$ susa setup --list
[INFO] Softwares instalados (categoria setup):
  docker          v24.0.7
  poetry          v1.7.1
  uv              v0.1.9

# Comando específico funciona normalmente
$ susa setup docker
[INFO] Instalando Docker...

Quando Usar Entrypoints em Categorias

✅ Bons casos de uso:

  • Operações em massa (atualizar todos, listar todos)
  • Ações que afetam múltiplos comandos da categoria
  • Parâmetros comuns que se aplicam a toda categoria
  • Help complementar com informações da categoria

❌ Evite usar para:

  • Comandos individuais (use comandos normais)
  • Lógica complexa que deveria ser um comando próprio
  • Categorias que são apenas contêineres de navegação

Estrutura de Exemplo Completa

commands/
  setup/
    category.json          # ← Com entrypoint
    main.sh                # ← Script da categoria
    docker/
      command.json
      main.sh
    poetry/
      command.json
      main.sh

category.json:

{
  "name": "Setup",
  "description": "Instalação e atualização de softwares",
  "entrypoint": "main.sh"
}

⚙️ Filtros de Sistema Operacional e Sudo

📖 Referência completa: Veja Configuração de Comandos para detalhes sobre os campos os e sudo.

Resumo:

  • Use o campo os: ["linux", "mac"] para restringir sistemas
  • Use sudo: true para comandos que requerem privilégios elevados
  • Comandos com sudo: true exibem o indicador [sudo] na listagem

Exemplo de exibição:

Commands:
  docker          Instala Docker CE [sudo]
  asdf            Instala ASDF Version Manager
  podman          Instala Podman

📊 Agrupamento de Comandos

Comandos podem ser agrupados para melhor organização:

// commands/install/tool1/command.json
{
  "group": "Development Tools"
}

// commands/install/tool2/command.json
{
  "group": "Development Tools"
}

Exibição:

Commands:
  standalone-cmd  Comando sem grupo

 Development Tools
  tool1           Primeira ferramenta
  tool2           Segunda ferramenta

🎯 Boas Práticas

Nomenclatura

  • Diretórios: Use kebab-case: install-python, backup-tools
  • Nomes (config): Use formato legível: "Install Python", "Backup Tools"
  • Scripts: Sempre main.sh (ou o nome definido em entrypoint:)

Organização

  1. Categorias principais → Grandes áreas funcionais (install, daily, backup)
  2. Subcategorias → Agrupamento lógico (python, nodejs, docker)
  3. Comandos → Ações específicas (pip, poetry, npm)

Hierarquia Recomendada

✅ Boa hierarquia:
commands/install/python/pip
commands/install/python/poetry
commands/install/nodejs/npm

❌ Hierarquia excessiva:
commands/tools/dev/lang/python/pkg/pip
(muito profunda, evite mais de 3 níveis)

🐛 Troubleshooting

Comando não aparece na listagem

1:** Falta campo entrypoint: no command.json

Solução: Adicionar o campo script

{
  "entrypoint": "main.sh"
}

Causa 2: Arquivo do script não existe ou não tem o nome correto

Solução: Verificar se o arquivo existe e corresponde ao nome em entrypoint:

ls -la commands/categoria/comando/main.sh

Causa 3: Incompatível com o sistema operacional atual

Solução: Verificar campo os: no command.json

Subcategoria aparece como comando (não consigo navegar)

Causa: command.json tem campo entrypoint: definido e o arquivo existe

Explicação: O sistema identifica como comando executável pela presença do script.

Soluç 1: Script não está executável

Solução:

chmod +x commands/path/to/command/main.sh

Causa 2: Nome do script no command.json não corresponde ao arquivo

Solução: Verificar se entrypoint: aponta para o arquivo correto

Causa 3: Script não existe

Solução: Criar o arquivo do script

Descrição não aparece

Causa: Falta campo description: no command.json

Solução: Adicionar descrição

{
  "name": "Nome",
  "description": "Descrição aqui"
}

Descrição da subcategoria não aparece

Causa: Falta arquivo de configuração (command.json ou category.json) ou está sem campos obrigatórios

Solução: Criar category.json com name e description (SEM campo entrypoint)

{
  "name": "Nome da Subcategoria",
  "description": "Descrição aqui"
}

📚 Exemplos Completos

📖 Para exemplos de comandos simples, veja Exemplo Completo no guia básico.

Exemplo: Hierarquia com Subcategorias (Foco deste guia)

# Estrutura
mkdir -p commands/backup/{local,cloud}/{full,incremental}

# Subcategoria: backup/local (SEM campo 'script')
cat > commands/backup/local/command.json << EOF
name: "Local"
description: "Backups locais"
EOF

# Comando: backup/local/full (COM campo 'script')
cat > commands/backup/local/full/command.json << EOF
name: "Full Backup"
description: "Backup completo local"
entrypoint: "main.sh"
sudo: false
EOF

cat > commands/backup/local/full/main.sh << 'EOF'
#!/bin/bash
echo "Executando backup completo local..."
tar -czf /tmp/backup-$(date +%Y%m%d).tar.gz /home/$USER/Documents
echo "✅ Backup concluído!"
EOF

chmod +x commands/backup/local/full/main.sh

Uso:

  • ./susa backup → Lista local e cloud como subcategorias
  • ./susa backup local → Lista full e incremental como comandos
  • ./susa backup local full → Executa o backup

Exemplo 3: Plugin com Subcategorias Aninhadas

# Estrutura completa para plugin dev-tools
mkdir -p plugins/dev-tools/deploy/{staging,production,aws/{ec2,lambda}}
mkdir -p plugins/dev-tools/test/{unit,integration}

# Categoria: deploy (SEM script)
cat > plugins/dev-tools/deploy/category.json << EOF
name: "Deploy"
description: "Ferramentas de deployment"
EOF

# Comando: deploy/staging
cat > plugins/dev-tools/deploy/staging/command.json << EOF
name: "Staging"
description: "Deploy para ambiente de staging"
entrypoint: "main.sh"
EOF

cat > plugins/dev-tools/deploy/staging/main.sh << 'EOF'
#!/bin/bash
echo "🚀 Deploy para Staging..."
echo "✅ Deploy concluído!"
EOF

# Subcategoria: deploy/aws (SEM script)
cat > plugins/dev-tools/deploy/aws/command.json << EOF
name: "AWS"
description: "Deploy para serviços AWS"
EOF

# Comando em subcategoria: deploy/aws/ec2
cat > plugins/dev-tools/deploy/aws/ec2/command.json << EOF
name: "EC2"
description: "Deploy para instâncias EC2"
entrypoint: "main.sh"
EOF

cat > plugins/dev-tools/deploy/aws/ec2/main.sh << 'EOF'
#!/bin/bash
echo "☁️ Deploy para AWS EC2..."
echo "✅ Deploy EC2 concluído!"
EOF

# Tornar scripts executáveis
chmod +x plugins/dev-tools/deploy/staging/main.sh
chmod +x plugins/dev-tools/deploy/aws/ec2/main.sh

Uso:

  • ./susa deploy → Lista staging, production, aws (subcategoria)
  • ./susa deploy staging → Executa deploy staging
  • ./susa deploy aws → Lista ec2, lambda
  • ./susa deploy aws ec2 → Executa deploy EC2

🔗 Guias Relacionados