knowledge-base/records/plugin-dev/KB-PLUGIN-013-mindplace-plugin-publication-runbook.md
Gemini f3bf69c510 KB-PLUGIN-013: gotcha do bump de versão (plugin desativado até rodar plugin:install)
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>
2026-07-03 20:17:28 -03:00

15 KiB
Raw Blame History

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
mindplace
runbook
publish
license
forgejo
release
kill-switch
workflow
automation
script
active high 2026-05-09 2026-05-21
GLPI 11.0.x
Mindplace 1.0+
Forgejo (servicedesk.mindtek.com.br/git)

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.php válido com plugin_version_<name>() retornando metadata
  • version no setup.php segue SemVer (ex: 1.0.0)
  • Para publicação automatizada: FORGEJO_TOKEN disponível (PAT com escopo write:repository)

Publicação Automatizada (Fases 25)

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_XXXVERSION no setup.php
  • Arquivos de teste (ajax/test*.php, front/test*.php) devem estar no .gitignore do plugin para não serem publicados
  • O .gitignore padrã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:

  1. plugin_<name>_check_config() impede ativação se licença inválida
  2. plugin_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() adicionada
  • plugin_<name>_check_config() valida licença
  • plugin_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

  1. Acesse o Forgejo logado como admin (rodolpho.lopes)
  2. +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)
  3. 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

  1. No Forgejo, no repo do plugin: ReleasesNew Release
  2. Tag name: v1.0.0 (deve bater com version em setup.php)
  3. Title: v1.0.0
  4. Description: changelog resumido
  5. 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"
    
  6. Publish Release

Checklist da Fase 2

  • Repositório criado público no Forgejo
  • Código-fonte push para main
  • Logo logo.png na 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 Forgejo
  • logo_url: null: faz o Mindplace buscar logo.png da 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.png existe 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):

  1. Ativos → Licenças de Software → +
  2. 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
  3. 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:

7C9F1E3A4B5D6F8E2C0A1B3D4E5F6071

Como ativar:

  1. No GLPI, acesse Configurar → Plugins → Mind Place (ícone de chave)
  2. Cole o serial no campo API Token e salve
  3. Aguarde o status mudar para "Ativo" (alguns segundos)
  4. 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

  1. Atualizar version no setup.php (ex: 1.0.01.1.0)
  2. Commit + push para main no Forgejo
  3. Criar nova release no Forgejo com tag v1.1.0 + novo ZIP
  4. Não atualiza plugins.json — o Mindplace pega automaticamente a release mais recente
  5. 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 version do setup.php, o CheckPluginsStates do 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, rodar bin/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

  1. No GLPI de produção: Ativos → Licenças de Software → encontrar o serial
  2. Marcar is_deleted = 1 (mover para a lixeira) OU deletar permanentemente
  3. No próximo cron (1x por dia) ou na próxima vez que o cliente salvar a config do mindplace, License::cronCheck() vai retornar expired
  4. 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 25 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