diff --git a/index.json b/index.json index 1e11ef4..812910e 100755 --- a/index.json +++ b/index.json @@ -1,6 +1,6 @@ { "version": "1.0.0", - "last_updated": "2026-07-15", + "last_updated": "2026-07-31", "records": [ { "id": "KB-INFRA-001", diff --git a/records/plugin-dev/KB-PLUGIN-018-plugin-release-zip-wrapper-convention.md b/records/plugin-dev/KB-PLUGIN-018-plugin-release-zip-wrapper-convention.md index 3399d2a..6c3e49f 100644 --- a/records/plugin-dev/KB-PLUGIN-018-plugin-release-zip-wrapper-convention.md +++ b/records/plugin-dev/KB-PLUGIN-018-plugin-release-zip-wrapper-convention.md @@ -95,6 +95,57 @@ zip -r mcprotocol-1.0.3.zip mcprotocol/ \ **Não** fazer `cd plugin && zip -r ...zip .` — esse padrão produz ZIP sem wrapper e era o bug histórico do script. +## Gotcha — ZIP gerado com `python zipfile` precisa de permissões explícitas + +Quando o `zip` CLI não está disponível e o pacote é montado com `zipfile` +(cenário comum neste ambiente, ver [[redmine-plugin-project]]), as entradas +criadas com `zipfile.ZipInfo(nome)` nascem com `external_attr = 0` — sem bits +de permissão e sem a flag MS-DOS de diretório. O `unzip` então cria as pastas +como **`drw-------`** (sem bit de execução), e o resultado no servidor é: + +- o usuário do webserver **não consegue atravessar** o diretório do plugin; +- `Plugin::isLoadable()` não enxerga o `setup.php`; +- o plugin **some da lista** sem nenhum erro, ou aparece + `Unable to load plugin "" information. at Plugin.php line 882` + quando já existe registro dele no banco. + +Diagnosticado em 2026-07-31 no build VIP do butterfly instalado no HML de um +cliente (`drw------- root root` na pasta do plugin). + +### Forma correta + +```python +DIR_ATTR = (0o40755 << 16) | 0x10 # drwxr-xr-x + flag de diretório +FILE_ATTR = (0o100644 << 16) # -rw-r--r-- + +zi = zipfile.ZipInfo(arcname, date_time=(2026, 7, 31, 12, 0, 0)) +zi.external_attr = DIR_ATTR # entradas de diretório +z.writestr(zi, '') + +zi = zipfile.ZipInfo.from_file(src, arcname) +zi.external_attr = FILE_ATTR # arquivos +zi.compress_type = zipfile.ZIP_DEFLATED +``` + +Sintoma acessório que denuncia o problema: data **`Jan 1 1980`** no diretório +extraído (timestamp default de `ZipInfo` sem `date_time`). + +### Validação obrigatória do ZIP + +```bash +# extrair em pasta temporária e conferir os modos +unzip -q -.zip -d /tmp/ziptest && ls -ld /tmp/ziptest/ +# esperado: drwxr-xr-x (NUNCA drw-------) +``` + +Correção no servidor quando o ZIP defeituoso já foi extraído: + +```bash +chown -R www-data:www-data /plugins/ +find /plugins/ -type d -exec chmod 755 {} \; +find /plugins/ -type f -exec chmod 644 {} \; +``` + ## .gitignore padrão do plugin Para que o ZIP não inclua arquivos sensíveis ou de teste, garantir `.gitignore` na raiz do plugin: