--- id: KB-PLUGIN-013 title: Runbook — Publicação de plugin no Mindplace (procedimento padrão) domain: plugin-dev tags: - mindplace - runbook - publish - license - forgejo - release - kill-switch - workflow - automation - script status: active severity: high created_at: 2026-05-09 updated_at: 2026-05-21 applies_to: - 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_()` 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 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): ```bash export FORGEJO_TOKEN="seu_token_aqui" ``` ### Uso ```bash # Na raiz do projeto GLPI11: ./bin/mindplace-release.sh ./docker/glpi/plugins/ ``` ### 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 ```bash # 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 ./docker/glpi/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 ```gitignore # 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__check_config()` impede ativação se licença inválida 2. `plugin_init_()` aborta silenciosamente se licença inválida (proteção em profundidade) ### Template do `setup.php` ```php (): array { return [ 'name' => 'Plugin Name', 'version' => '1.0.0', 'author' => 'Mindtek Tecnologia', 'license' => 'GPLv2+', 'requirements' => ['glpi' => ['min' => '11.0', 'max' => '12.0']], ]; } function plugin__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__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__check_config($verbose = false): bool { plugin__load_license(); if (!class_exists('\GlpiPlugin\Mindplace\License') || !\GlpiPlugin\Mindplace\License::isValid()) { if ($verbose && !isCommandLine()) { echo "
Licença Mindtek inativa ou Mind Place ausente. O plugin foi desativado por segurança.
"; } return false; } return true; } function plugin_init_(): void { global $PLUGIN_HOOKS; $PLUGIN_HOOKS['csrf_compliant'][''] = true; // ────────────────────────────────────────────────────────────────── // KILL SWITCH — abort silently if license invalid // ────────────────────────────────────────────────────────────────── plugin__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][''] = [...]; // 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__load_license()` adicionada - [ ] `plugin__check_config()` valida licença - [ ] `plugin_init_()` 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 ```bash 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/.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: **Releases** → **New 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: ```bash cd /caminho/local/do/plugin zip -r -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` 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: ```json { "key": "", "repo": "rodolpho.lopes/", "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/", "issues_url": "https://servicedesk.mindtek.com.br/git/rodolpho.lopes//issues", "note": 5 } ``` **Campos importantes:** - `key`: igual ao nome do diretório do plugin (sem espaços, lowercase) - `repo`: caminho `/` 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 ` (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 ```bash 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 **\** 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.0` → `1.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) ### 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 ```sql -- 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: ```bash curl "https://servicedesk.mindtek.com.br/mindtek_validador.php?token=SERIAL_DO_CLIENTE" # {"status":"active"} ou {"status":"expired"} ``` --- ## Referências cruzadas - [KB-PLUGIN-011](KB-PLUGIN-011-mindplace-release-zip-procedure.md) — procedimento de ZIP via GitHub (legado, antes da migração para Forgejo) - [KB-PLUGIN-012](KB-PLUGIN-012-helpdesk-tiles-extension-patterns.md) — 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