Plugins
Esta página descreve como o MB CLI descobre, armazena e executa plugins — diretório de plugins, cache, sync e resolução de paths. Para criar um plugin e usar mb plugins no dia a dia, veja o Guia: Criar um plugin e Comandos de plugins.
Diretório de plugins e plugins locais
O MB usa um diretório de plugins derivado de os.UserConfigDir():
- Linux:
~/.config/mb/plugins - macOS:
~/Library/Application Support/mb/plugins
O cache SQLite do MB fica em ConfigDir/cache.db (o mesmo diretório de configuração; ex.: ~/.config/mb/cache.db no Linux), não dentro da pasta de plugins.
Plugins locais: com mb plugins add <path|.> o path fica em plugin_sources.local_path; nada é copiado para PluginsDir. No sync, esse path é escaneado como uma fonte extra.
Subdiretório de plugins
Ao escanear um pacote instalado, o MB procura por manifest.yaml num subdiretório configurável. O valor padrão é src. Para sobrescrever:
MB_PLUGIN_SUBDIR=lib mb plugins sync
Se o subdiretório não contiver nenhum manifest.yaml, o scanner faz fallback para a raiz do pacote. Definir MB_PLUGIN_SUBDIR= (vazio) desativa a detecção e escaneia sempre da raiz.
Nome do pacote (identificador)
Ao instalar sem --package, o identificador do pacote é:
| Origem | Identificador | Exemplo |
|---|---|---|
| Git | Último segmento da URL | org/infra-tools → infra-tools |
| Local | Nome do diretório | /path/meu-plugin → meu-plugin |
Local (monorepo: sem manifest.yaml na raiz, manifests em src/ ou MB_PLUGIN_SUBDIR) | Nome do diretório do repositório (ou --package) | mb plugins add . em …/meu-repo → um pacote meu-repo com local_path em …/meu-repo/src, como após instalar o mesmo repo por Git |
| Coleção | Nome de cada subdiretório | repo/foo/, repo/bar/ → foo, bar |
Esse identificador aparece como PACOTE em mb plugins list e é usado em
mb plugins remove <pacote> [<pacote>...] / mb plugins remove --all e
mb plugins update <pacote> [<pacote>...] / mb plugins update --all.
Descoberta: scanner e manifest.yaml
O scanner percorre a árvore à procura de manifest.yaml. Para cada ficheiro:
Validação (alinhada ao código):
- Com entrypoint: o ficheiro tem de existir e estar dentro do diretório do plugin; o entrypoint não pode apontar para fora.
- Com entrypoint ou flags: o campo
commandno manifest é obrigatório. - Readme e ficheiros em env_files: paths relativos ao plugin e validados sob o diretório do plugin.
- flags (entrypoints por flag): cada entrypoint declarado tem de estar sob o diretório do plugin.
- flags.envs: quando definido, deve ser lista de pares
KEY=VALUEválidos.
Manifestos inválidos geram avisos no sync; o manifest é ignorado.
Tipo no cache (sh / bin): não há campo type obrigatório no manifest. O scanner define o tipo pelo entrypoint: termina em .sh → sh; caso contrário → bin.
command_path e tipos de manifest
Sob a raiz da fonte (cada subpasta imediata de PluginsDir, ou o local_path registado), o CLI monta o command_path:
- Em cada nível de pasta usa
manifest.commandse existir; senão o nome da pasta. - Na folha, o último segmento é o nome da pasta do plugin (ou
commandse for folha na raiz com entrypoint/flags).
Tipos:
- Folha com entrypoint — um executável por comando.
- Folha flags-only — só
flags; o CLI escolhe o entrypoint pela flag. - Categoria — sem entrypoint e sem
flags; aparece como subcomando intermédio (descrição, opcionalmente README com-r). O campoaliasesdo manifest é gravado no cache (categories.aliases_json) e o Cobra regista os mesmos aliases que para uma folha (ex.:mb pai <alias>em vez do nome do segmento).
Modos de scan:
- Scan(
PluginsDir) — Para cada subdiretório dePluginsDir, essa pasta é a raiz da árvore. Ocommand_pathnão inclui o nome da pasta clone. - ScanDir(
rootPath,installName) — O segundo argumento não entra no path do CLI; serve para identificar a fonte emplugin_sources. Usado para cadalocal_path.
São guardados plugin_dir, exec_path e readme_path absolutos quando aplicável.
Grupos de help (groups.yaml, group_id e Cobra)
Ficheiro groups.yaml
Pode existir em qualquer pasta do pacote. Formato: lista de pares id / title:
- id: meu_grupo
title: TÍTULO NO HELP
Regras de id (validação ao carregar o ficheiro):
- Deve coincidir com
^[a-zA-Z][a-zA-Z0-9_]*$— começa com letra, depois letras, dígitos ou_. - Não pode ser
commandsnemplugin_commands(reservados para o Cobra). titlenão pode ser vazio.- O mesmo
idrepetido dentro do mesmo ficheiro é erro (o ficheiro é rejeitado com aviso no sync).
Merge no sync e registo global
No sync, cada groups.yaml válido é um lote. Dentro de cada árvore, os paths dos ficheiros são processados em ordem lexicográfica. A ordem global dos lotes é:
- Cada subpasta de
PluginsDir, na ordem devolvida pelo sistema de ficheiros (ReadDir). - Depois, cada fonte com
local_pathnão vazio, na ordem deListPluginSources().
Para o mesmo id em lotes diferentes: prevalece a última definição. Se o título mudar relativamente ao valor já fixado, o CLI regista log debug (visível com mb -v, via gum quando disponível).
O resultado é gravado na tabela plugin_help_groups (group_id → title). Qualquer folha ou categoria aninhada pode referenciar qualquer id presente nesse registo após o merge (não está limitado ao groups.yaml do pai imediato).
Campo group_id no manifest.yaml
Campo opcional YAML: group_id.
- Só é usado para entradas aninhadas, isto é, quando o path tem
/(command_pathde plugin oupathde categoria, ex.infra/k8s). - Top-level (sem
/):group_idé ignorado e gera log debug se estiver preenchido. - O manifest não valida o formato de
group_idcomo ogroups.yaml; convém usar o mesmo padrão deid. No sync, se ogroup_idnão existir emplugin_help_groupsapós o merge, o campo é removido no cache e é emitido log debug. O sync não falha.
Como o help aparece no CLI
Ao registar comandos a partir do cache:
- Comandos cuja categoria está diretamente sob a raiz
mbficam no grupo COMANDOS DE PLUGINS (id internoplugin_commands). - Subcomandos aninhados (ex.
mb infra ci):- Com
group_idválido no cache → secção com o título definido emplugin_help_groups. - Sem
group_idou inválido no cache → secção COMANDOS (idcommands).
- Com
- Nos comandos pai aninhados, o MB também regista no Cobra os grupos custom do registo, para as folhas herdarem as mesmas secções.
Cache SQLite e sync
Tabelas relevantes
- plugins — Entre outros:
command_path,command_name,plugin_dir, descrição,exec_path, tipo (sh/binou vazio),config_hash,readme_path,flags_json,env_files_json,hidden,group_id(só faz sentido para paths aninhados; valores inválidos são limpos no sync). - categories —
path, descrição,readme_path,hidden,group_id(categorias aninhadas; mesma regra de validação que nos plugins). - plugin_help_groups —
group_id→title(resultado do merge dosgroups.yaml). - plugin_sources — Por
install_dir: remoto (git_url, ref, versão) oulocal_pathpara plugins adicionados localmente.
Fluxo do sync (mb plugins sync e após add/remove/update de plugins)
Ordem efetiva no código:
- Scan de todas as subpastas de
PluginsDir→ plugins, categorias, avisos e lotes degroups.yaml. - Garantir helpers de shell em
ConfigDir/lib/shell(ficheiros*.shembebidos no binário e.checksum; em cada sync reescreve-se e removem-se*.shórfãos). Falha → erro. - ScanDir de cada
local_pathregistado → acrescenta plugins, categorias, avisos e lotes. - Merge global dos grupos de help; em seguida, emissão de avisos de scan (warn) se houver logger.
- Verificação de colisão de
command_pathentre pacotes distintos → erro e sync abortado. - Normalização de
group_id: plugins e categorias aninhados comgroup_idinexistente no merge são limpos (+ debug). - Apagar todos os plugins e todas as linhas de
plugin_help_groups; inserir o merge emplugin_help_groups; upsert de cada plugin. - Apagar todas as categorias; upsert de cada categoria.
Nota: plugin_sources não é alterado pelo sync; só por mb plugins add/remove/update.
A árvore de comandos no CLI reflete PluginsDir mais os paths locais registados.
config_hash (digest da folha)
Para cada manifest de folha (com entrypoint ou só flags), o config_hash é um SHA-256 hexadecimal de um bloco canónico: inclui o hash do manifest.yaml bruto e, por ficheiro referenciado e ordenado por path relativo, path<TAB>SHA256(conteúdo). Ficheiros considerados: entrypoint, cada entrypoint em flags, cada file em env_files, e readme se definido. Não há walk recursivo do diretório.
No diff do sync (com logger), só são emitidas linhas para comandos adicionados, com digest alterado em relação ao cache, ou removidos da árvore; comandos inalterados não geram linha. Com EmitSuccess (ex.: mb plugins sync), se não houver nenhuma alteração nesses termos, é mostrada uma única mensagem de resumo em vez do antigo contador genérico de plugins. A lógica de contagem é a mesma usada pelo CLI em mb plugins add para decidir se mostra a mensagem genérica de “pacote já alinhado” (nenhum comando novo, atualizado ou removido).
Execução: binário ou script
- Com entrypoint: o cache guarda
ExecPathabsoluto. Se o ficheiro termina em.sh, o executor usa bash com esse script; senão executa o ficheiro diretamente. - Flags-only: o diretório de trabalho é o
plugin_dir; o entrypoint de cada flag é resolvido dentro dele.
Indicação (local) no Short da folha quando a fonte tem local_path em plugin_sources (match pelo diretório do plugin).
Códigos de saída convencionais para scripts shell
Plugins que participam em batches como mb tools --update-all (ou equivalentes) podem usar códigos acordados com o script agregador:
- 86 (
MB_EXIT_UPDATE_SKIPPED_SUDO) — semsudonão interativo / root para operações de pacote; o batch trata como “saltado”, não como erro fatal. - 87 (
MB_EXIT_UPDATE_SKIPPED_NOT_INSTALLED) — nada a atualizar porque a ferramenta não está instalada; ignorado no batch.
O executor Go repassa o código de saída do subprocesso; mensagens amigáveis para 86 em invocação direta ficam a cargo dos scripts (helper warn_and_skip_without_sudo em sudo.sh no diretório de helpers, sincronizado com mb plugins sync). Ver Códigos de saída no guia.
Execução: flags e argumentos
O processo do plugin não recebe as flags tratadas pelo CLI; recebe apenas argumentos posicionais (e o ambiente mesclado, incl. MB_VERBOSE / MB_QUIET quando aplicável).
Flags que o CLI consome
| Origem | Flags | Efeito |
|---|---|---|
| Root | -v / --verbose, -q / --quiet, --env-file, -e / --env | Consumidas pelo CLI; -v/-q viram variáveis de ambiente do plugin. |
| Plugin com README | -r / --readme | Abre a documentação no terminal. |
Plugin com flags no manifest | Flags declaradas | Escolhem o entrypoint; não são repassadas como args ao script; flags[].envs só é injetado para flags efetivamente informadas. |
O que o script recebe
- Posicionais após o consumo das flags acima. Ex.:
mb tools hello foo bar→$1,$2. - Ambiente: sistema + defaults +
env_filesdo plugin +flags[].envs(apenas das flags usadas) +--env+MB_VERBOSE/MB_QUIET+ variáveis de contextoMB_CTX_*(invocação, path no manifest, irmãos Cobra, etc.).- Precedência prática em conflito de chave:
--env>flags[].envs>env_files. - Ver Variáveis de ambiente, Flags globais e Contexto de invocação de plugins.
- Precedência prática em conflito de chave:
--help / -h
Mostra o help Cobra do comando; o entrypoint não é executado.
Flags desconhecidas
- Um único entrypoint: globais e opcionalmente
--readmesão consumidas pelo CLI; o restante depende do parser — para compatibilidade, prefira posicionais ou declare flags no manifest. - Plugin com
flagsno manifest: flags não declaradas provocam erro unknown flag e o plugin não corre.
Segurança
Os plugins correm com as permissões do utilizador; o CLI confina paths ao diretório do plugin no scan e no executor e suporta timeout opcional. Ver Segurança.