Verificado no butterfly/GLPI 11.0.8: CheckPluginsStates desativa o plugin quando a version do setup.php muda; sintoma engana como falha de licença. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
15 KiB
| id | title | domain | tags | status | severity | created_at | updated_at | applies_to | |||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| KB-PLUGIN-013 | Runbook — Publicação de plugin no Mindplace (procedimento padrão) | plugin-dev |
|
active | high | 2026-05-09 | 2026-05-21 |
|
Runbook — Publicação de plugin no Mindplace
Procedimento padrão completo para publicar um novo plugin proprietário no Mindplace (marketplace privado da Mindtek). Cobre desde a integração do kill switch de licença no código até a entrega do serial ao cliente.
⚡ TL;DR — Publicação automatizada: As Fases 2, 3 e 4 (Forgejo + ZIP + Release + plugins.json) estão 100% automatizadas pelo script
bin/mindplace-release.sh. Leia a seção Publicação Automatizada antes de executar os passos manuais.
Pré-requisitos
- Plugin está em estado funcional e testado localmente
- Acesso de admin ao Forgejo (
servicedesk.mindtek.com.br/git) - Acesso ao GLPI de produção da Mindtek (
servicedesk.mindtek.com.br) com perfil que crie licenças de software - Plugin já tem
setup.phpválido complugin_version_<name>()retornando metadata versionnosetup.phpsegue SemVer (ex:1.0.0)- Para publicação automatizada:
FORGEJO_TOKENdisponível (PAT com escopowrite:repository)
⚡ Publicação Automatizada (Fases 2–5)
As fases de publicação no Forgejo e catálogo são cobertas pelo script bin/mindplace-release.sh localizado na raiz do projeto GLPI11.
Pré-requisito único: Personal Access Token (PAT)
Gere em: https://servicedesk.mindtek.com.br/git/user/settings/applications
- Nome sugerido:
antigravity-publisher - Escopo necessário:
write:repository
Exporte como variável de ambiente (ou edite a variável FORGEJO_TOKEN diretamente no script):
export FORGEJO_TOKEN="seu_token_aqui"
Uso
# Na raiz do projeto GLPI11:
./bin/mindplace-release.sh /home/glpi/glpi_dev/plugins/<nome-do-plugin>
O que o script faz automaticamente
| Fase | Ação |
|---|---|
| 1 | Cria repositório no Forgejo (se não existir) |
| 2 | git init + commit + push para o Forgejo |
| 3 | Gera ZIP limpo do plugin (exclui .git, vendor, node_modules, *.DS_Store) |
| 4 | Cria tag v{versão} + Release no Forgejo + faz upload do ZIP como asset |
| 5 | Atualiza plugins.json no repo mindplace com a entrada do novo plugin |
Comportamento idempotente
O script é seguro para rodar múltiplas vezes:
- ✅ Repositório já existe → pula criação
- ✅ Release já existe → pula criação
- ✅ Plugin já no
plugins.json→ pula atualização - ✅ Sempre faz push do código mais recente
Fluxo de nova versão
# 1. Bump da versão no setup.php
# 'version' => '1.0.0' → '1.1.0'
# 2. Rodar o script — ele detecta a nova versão e cria nova Release
./bin/mindplace-release.sh /home/glpi/glpi_dev/plugins/meu-plugin
Notas importantes
- O script lê a versão diretamente da constante
PLUGIN_XXXVERSIONnosetup.php - Arquivos de teste (
ajax/test*.php,front/test*.php) devem estar no.gitignoredo plugin para não serem publicados - O
.gitignorepadrão para plugins está documentado abaixo - O ZIP do asset pode acumular múltiplas versões na mesma Release se o script for rodado mais de uma vez sem bump de versão (não causa erro, apenas duplicata)
.gitignore padrão para plugins
# Dependências
vendor/
node_modules/
# macOS
.DS_Store
**/.DS_Store
# IDE
.idea/
.vscode/
# Logs
*.log
# Build artifacts
*.zip
# Arquivos de teste (não publicar)
ajax/test*.php
ajax/mcp_test.php
front/test*.php
Fase 1 — Kill switch de licença no código
Cada plugin do Mindplace precisa validar a licença antes de fazer qualquer coisa. O padrão do "Kill Switch" funciona em duas camadas:
plugin_<name>_check_config()impede ativação se licença inválidaplugin_init_<name>()aborta silenciosamente se licença inválida (proteção em profundidade)
Template do setup.php
<?php
use Glpi\Plugin\Hooks;
function plugin_version_<name>(): array
{
return [
'name' => 'Plugin Name',
'version' => '1.0.0',
'author' => 'Mindtek Tecnologia',
'license' => 'GPLv2+',
'requirements' => ['glpi' => ['min' => '11.0', 'max' => '12.0']],
];
}
function plugin_<name>_check_prerequisites(): bool
{
if (version_compare(GLPI_VERSION, '11.0', '<')) {
echo 'This plugin requires GLPI >= 11.0';
return false;
}
return true;
}
/**
* Carrega a classe License do Mindplace caso o autoload ainda não tenha rodado.
* Necessário porque o plugin pode inicializar antes do mindplace.
*/
function plugin_<name>_load_license(): void
{
if (class_exists('\GlpiPlugin\Mindplace\License')) {
return;
}
foreach ([
GLPI_ROOT . '/plugins/mindplace/src/License.php',
GLPI_ROOT . '/marketplace/mindplace/src/License.php',
] as $path) {
if (file_exists($path)) {
include_once $path;
return;
}
}
}
function plugin_<name>_check_config($verbose = false): bool
{
plugin_<name>_load_license();
if (!class_exists('\GlpiPlugin\Mindplace\License') || !\GlpiPlugin\Mindplace\License::isValid()) {
if ($verbose && !isCommandLine()) {
echo "<div class='alert alert-danger'>Licença Mindtek inativa ou Mind Place ausente. O plugin foi desativado por segurança.</div>";
}
return false;
}
return true;
}
function plugin_init_<name>(): void
{
global $PLUGIN_HOOKS;
$PLUGIN_HOOKS['csrf_compliant']['<name>'] = true;
// ──────────────────────────────────────────────────────────────────
// KILL SWITCH — abort silently if license invalid
// ──────────────────────────────────────────────────────────────────
plugin_<name>_load_license();
if (!class_exists('\GlpiPlugin\Mindplace\License') || !\GlpiPlugin\Mindplace\License::isValid()) {
return;
}
// ─── A partir daqui, registrar todos os hooks/classes do plugin ───
// $PLUGIN_HOOKS[Hooks::ADD_CSS]['<name>'] = [...];
// Plugin::registerClass(...);
}
Comportamento do Kill Switch
| Cenário | Resultado |
|---|---|
| Mindplace não instalado | check_config retorna false → plugin não pode ativar |
| Mindplace instalado mas licença inválida/expirada | mesmo do anterior |
| Plugin já estava ativo e licença expirou | init aborta sem registrar hooks → GLPI ignora o plugin como se não existisse |
| Tudo OK | Plugin funciona normalmente |
Checklist da Fase 1
plugin_<name>_load_license()adicionadaplugin_<name>_check_config()valida licençaplugin_init_<name>()aborta se inválida- Testado: desativar mindplace → tentar ativar plugin → deve falhar com aviso
- Testado: licença válida → plugin ativa e funciona
Fase 2 — Publicação no Forgejo
Forgejo é onde o código-fonte e os releases ficam. URL base: https://servicedesk.mindtek.com.br/git/.
2.1 — Criar repositório
- Acesse o Forgejo logado como admin (
rodolpho.lopes) - + → New Repository
- Owner:
rodolpho.lopes - Repository name: nome do plugin (ex:
tilesections) - Visibility: Public
- Initialize repository: ✅ (com README mínimo — releases exigem ao menos um commit)
- Owner:
- Create Repository
2.2 — Push do código-fonte
cd /caminho/local/do/plugin
# Se ainda não é um repo git
git init
git branch -M main
# Adicionar Forgejo como remote
git remote add forgejo https://servicedesk.mindtek.com.br/git/rodolpho.lopes/<plugin>.git
# Commit + push
git add .
git commit -m "Initial commit"
git push forgejo main
Vai pedir credenciais do Forgejo na primeira vez.
2.3 — Criar release com ZIP
- No Forgejo, no repo do plugin: Releases → New Release
- Tag name:
v1.0.0(deve bater comversionemsetup.php) - Title:
v1.0.0 - Description: changelog resumido
- Em Attachments, fazer upload do ZIP gerado:
cd /caminho/local/do/plugin zip -r <plugin>-1.0.0.zip . \ --exclude "*.git*" \ --exclude "node_modules/*" \ --exclude "vendor/*" \ --exclude "*.DS_Store" - Publish Release
Checklist da Fase 2
- Repositório criado público no Forgejo
- Código-fonte push para
main - Logo
logo.pngna raiz do plugin (será exibido no Mindplace) - Tag
v<versão>criada - ZIP do plugin anexado como asset da release
- Versão do ZIP bate com
setup.php
Fase 3 — Cadastro no catálogo Mindplace
O Mindplace lê plugins.json do repositório rodolpho.lopes/mindplace no Forgejo. Cada plugin é uma entrada nesse array.
3.1 — Editar plugins.json
Acesse: https://servicedesk.mindtek.com.br/git/rodolpho.lopes/mindplace
Edite o arquivo plugins.json (ícone do lápis na visualização do arquivo) e adicione a nova entrada:
{
"key": "<plugin>",
"repo": "rodolpho.lopes/<plugin>",
"name": "Nome Bonito do Plugin",
"description": "Descrição curta de uma frase.",
"authors": [{"name": "Rodolpho O Lopes"}],
"license": "GPL-2.0-or-later",
"logo_url": null,
"homepage_url": "https://servicedesk.mindtek.com.br/git/rodolpho.lopes/<plugin>",
"issues_url": "https://servicedesk.mindtek.com.br/git/rodolpho.lopes/<plugin>/issues",
"note": 5
}
Campos importantes:
key: igual ao nome do diretório do plugin (sem espaços, lowercase)repo: caminho<owner>/<repo>no Forgejologo_url: null: faz o Mindplace buscarlogo.pngda raiz do repo automaticamente
Commit direto pelo Forgejo (Commit Changes na interface).
3.2 — Validação
No GLPI do cliente (com mindplace instalado e licença ativa), abra a aba Mind Place. O novo plugin deve aparecer no grid em até 1 hora (cache TTL). Para forçar refresh imediato, clica no botão de refresh do Mindplace.
Checklist da Fase 3
- Entrada adicionada em
plugins.json - Commit feito
- Plugin aparece no grid do Mindplace
- Logo carrega corretamente (se
logo.pngexiste na raiz do repo) - Botão Download/Install funciona
Fase 4 — Cadastro de licença no GLPI de produção
Cada cliente recebe um serial único. O serial fica em glpi_softwarelicenses do GLPI da Mindtek e é validado pelo mindtek_validador.php.
4.1 — Criar entrada de licença
No GLPI de produção (servicedesk.mindtek.com.br):
- Ativos → Licenças de Software → +
- Preencher:
- Nome:
Licença <Plugin> — <Cliente>(ex:Licença Tilesections — Empresa Acme) - Software: associar ao software do plugin (criar se não existir)
- Número de série: gerar hash hex aleatório de 32 caracteres
openssl rand -hex 16 | tr 'a-z' 'A-Z' # exemplo: 7C9F1E3A4B5D6F8E2C0A1B3D4E5F6071 - Tipo: livre (sugestão:
Mindplace Plugin) - Validade: opcional, conforme contrato
- Nome:
- Salvar
4.2 — Entregar o serial ao cliente
Mensagem padrão para o cliente:
Olá,
Sua licença do plugin <Plugin> está ativa. Use o serial abaixo para ativar no Mindplace do seu GLPI:
7C9F1E3A4B5D6F8E2C0A1B3D4E5F6071Como ativar:
- No GLPI, acesse Configurar → Plugins → Mind Place (ícone de chave)
- Cole o serial no campo API Token e salve
- Aguarde o status mudar para "Ativo" (alguns segundos)
- Pronto, o plugin pode ser ativado normalmente
Checklist da Fase 4
- Software cadastrado em
Ativos → Software(se primeira vez) - Licença criada com serial único de 32 chars hex
- Serial enviado ao cliente
- Cliente confirmou ativação no Mindplace dele
Fase 5 — Manutenção contínua
Lançar nova versão do plugin
- Atualizar
versionnosetup.php(ex:1.0.0→1.1.0) - Commit + push para
mainno Forgejo - Criar nova release no Forgejo com tag
v1.1.0+ novo ZIP - Não atualiza
plugins.json— o Mindplace pega automaticamente a release mais recente - Clientes verão o botão Atualizar no Mindplace dentro de 1 hora (cache TTL)
Gotcha (verificado no butterfly, GLPI 11.0.8, 2026-07-03): ao mudar a
versiondo setup.php, oCheckPluginsStatesdo boot desativa o plugin na hora ("version changed... has to be launched") — status vira "Para atualizar", o setup.php deixa de ser carregado e os hooks somem (funções do plugin ficam indefinidas). No ambiente de dev, rodarbin/console glpi:plugin:install <key> -u glpi && bin/console glpi:plugin:activate <key>logo após o bump. Sintoma enganoso: parece que o kill switch/licença derrubou o plugin, mas é só o fluxo de update pendente.
Revogar licença de um cliente
- No GLPI de produção: Ativos → Licenças de Software → encontrar o serial
- Marcar
is_deleted = 1(mover para a lixeira) OU deletar permanentemente - No próximo cron (1x por dia) ou na próxima vez que o cliente salvar a config do mindplace,
License::cronCheck()vai retornarexpired - Plugin do cliente desativa automaticamente
Verificar status de licença de um cliente
-- No GLPI de produção
SELECT id, name, serial, is_deleted, completename
FROM glpi_softwarelicenses
WHERE serial = 'SERIAL_DO_CLIENTE';
Ou consultar o validador diretamente:
curl "https://servicedesk.mindtek.com.br/mindtek_validador.php?token=SERIAL_DO_CLIENTE"
# {"status":"active"} ou {"status":"expired"}
Referências cruzadas
- KB-PLUGIN-011 — procedimento de ZIP via GitHub (legado, antes da migração para Forgejo)
- KB-PLUGIN-012 — padrões para estender o sistema de tiles do Helpdesk
Script de automação
| Arquivo | Localização | Função |
|---|---|---|
mindplace-release.sh |
bin/mindplace-release.sh na raiz do projeto GLPI11 |
Script Bash que automatiza as Fases 2–5 completas via API do Forgejo |
Arquivos críticos
| Arquivo | Localização | Função |
|---|---|---|
mindtek_validador.php |
/var/www/html/glpi/public/ no servidor de produção |
Endpoint que valida serials contra glpi_softwarelicenses |
License.php |
plugins/mindplace/src/ no GLPI do cliente |
Cron + isValid() que outros plugins consultam |
plugins.json |
repo rodolpho.lopes/mindplace no Forgejo |
Catálogo do Mindplace |
secrets.php |
NÃO USAR no setup atual com Forgejo (era para PAT do GitHub) | — |
Notas importantes
- Forgejo não tem rate limit — não precisa de token de autenticação. Repositórios são públicos
License::isValid()é zero-latency — lê cache no banco, não bate em API. Pode ser chamado em cada page load sem custo- Cron de validação roda 1x/dia — se o cliente quer validar agora, salvar a config do mindplace força o check imediato
- Sem licença, sem prejuízo — o kill switch é silencioso; o GLPI continua funcional sem o plugin, sem mensagens de erro