Skip to content

O postgresql-dev sai das três imagens depois de compilar o driver (#391) - #409

Merged
matbrgz merged 2 commits into
masterfrom
fix/391-postgresql-dev-so-na-compilacao
Sep 25, 2026
Merged

matbrgz merged 2 commits into
masterfrom
fix/391-postgresql-dev-so-na-compilacao

Conversation

@matbrgz

@matbrgz matbrgz commented Sep 25, 2026

Copy link
Copy Markdown
Member

Closes #391.

A primeira pergunta da issue: o juiz precisa do driver?

Precisa. O serviço autojudge do docker-compose.yml roda autojudge:start, que busca as runs pendentes direto no banco (depends_on: db). Quem hospeda em PostgreSQL precisa do pdo_pgsql no juiz. A saída (3) da issue — "conferir se o juiz precisa de pdo_pgsql de todo" — quebraria essa instalação. O que sai é o cabeçalho, não o driver.

A mudança

Nas três imagens:

  • a lista de infraestrutura instala libpq (runtime) em vez de postgresql-dev;
  • o passo das extensões instala o postgresql-dev como --virtual .pgsql-build e o remove no mesmo RUN. Remover num RUN seguinte não economizaria nada: a camada que o instalou continuaria na imagem, com o LLVM dentro.

É a saída (1) da issue.

Medido

Sobre a base real, php:8.3-cli-alpine (Alpine 3.24.2):

Perfil Antes Depois O que sai
corta o Clang (ex.: maratona) +446 MiB +0 MiB clang22, llvm22, llvm22-libs, clang22-libs
completo +446 MiB +306 MiB llvm22 (as ferramentas); o clang22 fica

apk del só remove o que nada mais exige. No perfil que oferece Clang o clang22 está em /etc/apk/world e sobrevive — que é o correto.

Conferido no contêiner, e não suposto

  • o driver funciona: pdo_pgsql compila, aparece em php -m, e libpq.so.5 => /usr/lib/libpq.so.5. Um new PDO("pgsql:...") contra uma porta fechada devolve Connection refused — erro de conexão, não "could not find driver";
  • o Clang continua julgando sem o llvm22: os comandos exatos do catálogo compilam, linkam estático e rodam:
    c_clang17  clang   -static -O2 -std=c17   ... -lm   ->  3 4  => 7
    cpp_clang  clang++ -static -O2 -std=c++20 ...       ->  5 6  => 11
    
  • o que vem depois da remoção ainda constrói: pecl install redis e pecl install pcov rodam depois do apk del nas imagens, e poderiam depender dos openssl-dev/lz4-dev/zstd-dev que o grupo leva embora. Replicada a sequência inteira: os dois constroem e carregam.

A guarda

tests/Unit/Judge/PostgresqlDevSoNaCompilacaoTest.php, nas três imagens, lendo o Dockerfile na granularidade de instrução (continuações juntadas, comentários fora — o que o Docker faz antes de executar):

  1. postgresql-dev só como --virtual, removido no mesmo RUN;
  2. o libpq continua num apk add persistente — senão o driver compila e não carrega;
  3. o pdo_pgsql continua sendo compilado — o conserto não pode virar "tirar o driver".

Mutação, uma por guarda, contra o Dockerfile.judge:

tiro o `apk del`                       -> Failures: 1
devolvo o `postgresql-dev` à lista     -> Failures: 1
tiro o `libpq`                         -> Failures: 1
tiro o `pdo_pgsql` da compilação       -> Failures: 1
restaurado                             -> OK (9 tests)

tests/Unit e tests/Feature: 1886 testes, verdes.

O que a medição não cobre

  • As medições são sobre a base com os pacotes relevantes, não sobre a imagem inteira. O número real de cada imagem é o que o job do juiz vai construir — a economia do completo pode diferir se algum outro pacote da lista também exigir o llvm22.
  • A afirmação "no perfil maratona o Clang some" se apoia no apk info -r clang22 que a issue mediu na imagem real (só o postgresql18-dev o exige) e na reprodução acima. O CI constrói o perfil completo, então essa parte não é conferida pelo job.

docs/specs/306-perfis-de-toolchain.md ganhou a nota de resolução no parágrafo que tinha registrado o achado e o mandado para cá.

🤖 Generated with Claude Code

matbrgz and others added 2 commits September 25, 2026 09:24
…391)

As três imagens instalavam `postgresql-dev` na lista grande de `apk add`,
só para compilar o `pdo_pgsql`. O PostgreSQL 18 traz suporte a JIT, e o
`-dev` dele arrasta o Clang e o LLVM 22 inteiros -- que ficavam na imagem.

## A pergunta que a issue fazia primeiro: o juiz precisa do driver?

Precisa. O serviço `autojudge` do docker-compose roda `autojudge:start`,
que busca as runs pendentes DIRETO no banco. Quem hospeda em PostgreSQL
precisa do `pdo_pgsql` no juiz. A opção "tirar o driver" quebraria essa
instalação; o que sai é o CABEÇALHO.

## O que muda

- a lista de infraestrutura instala `libpq` (runtime) em vez de
  `postgresql-dev`;
- o passo das extensões instala o `postgresql-dev` como `--virtual
  .pgsql-build` e o remove no MESMO `RUN`. Remover num `RUN` seguinte não
  economizaria nada: a camada que o instalou continuaria na imagem.

## Medido sobre a base `php:8.3-cli-alpine` (Alpine 3.24.2)

    perfil que corta o Clang, como era:  +446 MiB, clang22 e llvm22 na imagem
    perfil que corta o Clang, proposto:  +0 MiB, só o libpq
    perfil completo, como era:           +446 MiB
    perfil completo, proposto:           +306 MiB (o clang22 fica; sai o llvm22)

`apk del` só remove o que nada mais exige, e no perfil que oferece Clang o
`clang22` está em /etc/apk/world e sobrevive -- que é o correto.

Conferido no contêiner, e não suposto:

- `pdo_pgsql` compila, `php -m` o lista, e `libpq.so.5` resolve para
  `/usr/lib/libpq.so.5`;
- um `new PDO("pgsql:...")` contra uma porta fechada devolve `Connection
  refused` -- erro de CONEXÃO, e não "could not find driver";
- os comandos EXATOS de `c_clang17` e `cpp_clang` do catálogo (`-static`,
  `-std=c17` / `-std=c++20`, `-lm`) compilam, linkam estático e rodam sem o
  `llvm22`;
- os `pecl install redis` e `pcov` que vêm DEPOIS da remoção ainda
  constroem e carregam.

## A guarda

`PostgresqlDevSoNaCompilacaoTest`, nas três imagens, lendo o Dockerfile na
granularidade de instrução (continuações juntadas, comentários fora):

1. `postgresql-dev` só como `--virtual`, removido no mesmo RUN;
2. o `libpq` de runtime continua num `apk add` persistente;
3. o `pdo_pgsql` continua sendo compilado.

Mutação, uma por guarda, contra o `Dockerfile.judge`:

    tiro o `apk del`                        -> 1 falha
    devolvo o `postgresql-dev` à lista      -> 1 falha
    tiro o `libpq`                          -> 1 falha
    tiro o `pdo_pgsql` da compilação        -> 1 falha
    restaurado                              -> OK (9 testes)

`tests/Unit` e `tests/Feature` passam (1886 testes).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
O commit anterior deste PR derrubou o check "Os pinos das três imagens
resolvem no Alpine de hoje". Não foi pino morto: foi o parser.

`DockerfileToolchain::apkPackages()` -- o mesmo que as guardas e o
`pinos-alpine.yml` usam -- tratava como lista de pacotes TODO token depois
de `apk add` até o fim da instrução. O `RUN` novo deste PR,

    apk add --virtual .pgsql-build postgresql-dev \
      && docker-php-ext-install ... pdo_pgsql ... \
      && apk del .pgsql-build

virou uma "lista" com `&&`, `apk`, `del`, `docker-php-ext-install` e
`pdo_pgsql`, e o `apk add --simulate` do workflow recusou.

O defeito era latente: nenhuma linha de `apk` do repositório tinha `&&` nem
`--virtual`, então nunca tinha disparado. As guardas passaram verdes -- lendo
lixo em silêncio.

## Três regras

1. o comando termina no primeiro separador de shell (`&&`, `||`, `;`, `|`);
2. opção com argumento (`--virtual NOME`, `--repository URL`, ...) consome
   o token seguinte: o nome do grupo não é pacote;
3. um grupo `--virtual` removido com `apk del` no MESMO `RUN` não chega à
   imagem final, então não é pacote dela. Sem o `apk del`, os pacotes ficam
   -- senão a regra esconderia o erro de esquecer a remoção, que é o erro
   que a #391 corrige.

O parser agora lê a instrução inteira (continuações juntadas, comentários
fora), que é o que o Docker executa.

## Conferido

- **sem regressão**: nos três Dockerfiles do `master`, o parser novo dá
  EXATAMENTE a mesma lista que o antigo (60, 59 e 62 pacotes, byte a byte);
- nos Dockerfiles deste PR, a única diferença para o `master` é
  `-postgresql-dev +libpq`, nos três;
- o `apk add --simulate` do workflow, reproduzido localmente em bash com a
  mesma extração: as três imagens resolvem;
- quatro testes novos, um por regra e um para o contrário da regra 3.
  Mutação, uma de cada vez: tirar a parada no separador, não consumir o
  argumento, não descartar o grupo removido, descartar pelo `--virtual`
  sozinho -- cada uma reprova;
- `tests/Unit` e `tests/Feature` passam.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@matbrgz
matbrgz merged commit 8c7aeca into master Sep 25, 2026
11 checks passed
@matbrgz
matbrgz deleted the fix/391-postgresql-dev-so-na-compilacao branch September 25, 2026 14:39
matbrgz added a commit that referenced this pull request Sep 25, 2026
…ara de quebrar a cada revisão (#408) (#411)

* O pino do Alpine passa a prometer o que o rótulo promete, e o build para de quebrar a cada revisão (#408)

A #303 fixou todo toolchain por `pacote=versão-rN`. O Alpine não guarda
revisões antigas: quando publica a próxima, a fixada some e o build sem
cache para em `unable to select packages`. Foram três em menos de um dia --
`erlang27` na 27.3.4.17-r0, `perl` na 5.42.2-r0 (só recompilação do Alpine,
mesma versão) e `ocaml` na 4.14.3-r0.

Decisão do mantenedor na #408: opção (b) agora, publicar por digest (c)
depois.

## O operador

`~` do `apk`, que casa POR COMPONENTE. Medido no Alpine de hoje:

    perl=5.42.2-r0    morto        perl~5        resolve
    ocaml=4.14.3-r0   morto        ocaml~4.14    resolve
    rust~1.9          NAO casa com 1.96.1
    gcc~1             NAO casa com 15
    ocaml~4.15, perl~6, python3~3.15   recusados

Afrouxar não deixa entrar versão que o rótulo não anuncia.

## A regra, calculada e não escrita à mão

- o pacote que É o executável que a sonda de versão de uma linguagem mede
  (`ToolchainVersions`) fica na granularidade que o rótulo promete:
  `python3~3.14`, `nodejs~24`, `gcc~15`, `perl~5` -- 28 pacotes;
- o resto (bibliotecas, `bash`, `bubblewrap`, `npm`) fica em `~MAJOR.MINOR`
  -- 12 pacotes.

"É o executável" quer dizer o PRIMEIRO requisito da procedência. A sonda do
Kotlin mede o `kotlinc`, que roda na JVM do `openjdk21-jdk`; o "2.4" do
rótulo é do Kotlin, não da JVM. A primeira versão do cálculo, sem essa
distinção, dava à mesma JVM três promessas (21, 2.4, 1.12), e ao `gmp` o
"10" do SWI-Prolog. Com ela: 40 pinos, zero conflitos, e todos casam com a
versão instalada hoje.

## Onze leitores de `nome=pino`, e um que teria falhado em silêncio

`docker/judge/perfil/perfil` extraía o nome com `${arg%%=*}`. Com `perl~5` o
"nome" virava `perl~5`, o corte `apk:perl` não casava, e o perfil
`maratona` -- que corta perl, ocaml e ghc -- instalaria os três, GHC
inteiro, sem erro. Demonstrado antes do conserto:

    perfil filtra git perl~5 ocaml~4.14 ghc~9.10  ->  todos passam
    depois do conserto                            ->  git (os três cortados)

Conferido no `sh` do Alpine (busybox), que é o que roda no `RUN`.

O `ImagemPorPerfilTest` roda esse script de verdade -- mas montava os
tokens com `nome=pino` à mão, e por isso NÃO pegaria o defeito. Passou a
usar os tokens reais da linha; revertendo o script, ele reprova.

Os outros: o parser ganhou `apkSpecs()` e `specFor()`, que devolvem o token
exato com o operador, e cinco lugares que montavam `alvo.'='.pinFor()` --
e transformariam `perl~5` em `perl=5`, que o `apk` lê como "exatamente a
versão 5" -- passaram a usá-los: `ToolchainManifest::resolvedPackagesFor`,
`judge:toolchain-profile`, o workflow `pinos-alpine.yml` e dois testes.

## A guarda

`PinoNaGranularidadeDaPromessaTest`, nas três imagens: nenhum pino exato; o
pacote de cada linguagem na granularidade exata do rótulo; o resto em
`MAJOR.MINOR`; e nenhum pacote com duas promessas. Mutação, uma por vez:

    volto `perl=5.42.2-r1`          -> 2 falhas
    `gcc~15.2` (mais fino)          -> 1 falha
    `python3~3` (mais frouxo)       -> 1 falha
    `zlib~1` (fora de major.minor)  -> 1 falha
    script `perfil` revertido       -> ImagemPorPerfilTest reprova

## O que "reprodutível" passa a querer dizer

Antes: (commit, perfil) -> toolchains idênticos. Agora: (commit, perfil) ->
a versão do upstream que o rótulo promete, e o patch exato de cada
veredito fica gravado no julgamento (#402). O inventário diz isso onde a
tabela de pinos começa.

## Conferido

- `apk add --simulate` das três imagens, com a mesma extração do workflow:
  60, 59 e 62 pacotes, resolvem;
- `tests/Unit` e `tests/Feature`: 1900 testes.

Empilhado sobre o #409 (o parser que sabe onde termina um `apk add`).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Estilo: o Pint alinha o docblock do teste novo (#408)

O job Backend roda o Pint nos arquivos novos, e o
PinoNaGranularidadeDaPromessaTest tinha um espaço duplo no @return. Só
espaço em branco. Conferido o Pint em todos os PHP que o PR toca.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Estilo: o Pint formata os métodos novos do parser (#408)

O job Backend só confere o Pint nos arquivos NOVOS, e por isso não pegou o
`DockerfileToolchain.php`, que é modificado. Conferido que o problema era
deste PR: no `master` o arquivo passa, aqui não passava. A diferença é só
espaço em branco (`git diff -w` vazio). Pint verde em todos os PHP do PR.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

O postgresql-dev puxa ~355 MiB de Clang/LLVM para toda imagem de juiz

1 participant