Alojar as camadas fornecedoras num monorepo Turborepo
A camada passa para packages/duxt e o site para apps/www, cada pacote publicado versiona e marca por si como <name>@vX.Y.Z, e a decisão de repositórios próprios registada em
Contexto
#74 e #75 decidiram cada uma que os fornecedores de pesquisa oficiais são
publicados como camadas complementares independentes em repositórios
próprios — @kirchdev/duxt-typesense, @kirchdev/duxt-meilisearch. Nenhum
desses repositórios existe, e aqui um novo repositório open source não está a um
comando de distância: é aprovisionado via OpenTofu e depois carrega toda a camada
meta sozinho — stubs de workflows, release-please, Dependabot, CodeQL, uma
licença, um README e uma configuração de agentes. É o preço permanente de dois
adaptadores finos sobre exatamente um hook de compilação.
O contrato que ambos consomem, duxt:search:records, vive neste repositório.
Repartido por três, cada alteração tornar-se-ia uma dança de versões — subir a
camada, publicar, alargar o intervalo de cada fornecedor, publicar — sem uma única
barreira que execute o hook contra um consumidor. O site de desenvolvimento não
podia experimentar um fornecedor sem depender de uma versão publicada.
O repositório estava ainda talhado para um só pacote: a sua raiz era a
camada, e trinta módulos de compilação ficavam soltos ao lado da configuração
meta, separados dela apenas por uma lista files.
Decisão
Um repositório, um workspace pnpm conduzido por Turborepo.
packages/duxté@kirchdev/duxt. Os seus módulos de compilação saem da raiz plana para pastas temáticas sobbuild/—sources/,sections/,bruno/,openapi/,search/,content/,og-image/,git/,config/,cli/— e a listafilesencolhe para diretórios. O mapaexportsmantém todos os nomes de subcaminho.apps/wwwé o site que desenvolve a camada, com as verificações que leem a sua compilação.- A raiz não é um pacote. Mantém a configuração do workspace e a meta, e
docs/, queapps/wwwpublica. - Os pacotes fornecedores chegam ao lado da camada como
packages/duxt-typesenseepackages/duxt-meilisearch, com #74 e #75.
Cada pacote publicado é a sua própria unidade de versão. O release-please corre em modo manifesto com uma entrada por pacote, cada uma com a sua versão e o seu changelog, e uma versão publica apenas os pacotes que incrementou. Uma versão partilhada foi rejeitada, porque republica pacotes inalterados com um número novo.
Cada pacote marca <name>@vX.Y.Z, a camada incluída —
include-component-in-tag com tag-separator: "@", de modo que duxt@v0.5.0
fica ao lado de duxt-typesense@v0.1.0. A versão em si continua a ser um número
simples em package.json e no npm. As tags simples v0.1.0…v0.4.0 ficam como
estão, e last-release-sha aponta uma vez para o commit da versão v0.4.0, para
que o release-please encontre a versão anterior da camada apesar do novo padrão.
O changelog da camada fica na raiz do repositório, como
changelog-path: "/CHANGELOG.md". O site publica o changelog de cada versão que
serve, lido no checkout dessa versão, e todas as tags existentes têm o ficheiro
aí. Mudá-lo para o pacote teria deixado cada edição anterior sem as suas páginas
de versões. O changelog de um fornecedor vive no seu próprio diretório; não tem
histórico a preservar.
O versionamento por tags do duxt aprende as tags por componente, e tem de
chegar antes da primeira versão após a mudança. latest, a descoberta de versões
e a ordem do seletor leem <name>@vX.Y.Z como vX.Y.Z; a etiqueta e o segmento
de URL mostram apenas a versão; e uma fonte pode nomear um tagComponent para se
limitar às tags de um pacote, com as tags simples a contar como o seu histórico
anterior. Sem isto, latest em apps/www teria ficado em v0.4.0 depois de
duxt@v0.5.0 sem que nenhuma compilação falhasse.
Quem decide uma publicação é o registo. Um pacote é publicado quando a sua
versão tem uma tag <component>@v<version> e o npm não tem essa versão. O
_publish-npm.yml central publica a raiz do repositório e o
_release-please.yml central só reencaminha as saídas do pacote raiz, por isso
os jobs de publicação são do próprio repositório até os corpos centrais aceitarem
um diretório de trabalho.
Os fornecedores dependem como peer de @kirchdev/duxt com um intervalo largo,
>=0.4.0 <1, com o limite inferior no que o fornecedor precisar primeiro —
nunca ^0.x, que obrigaria cada fornecedor a publicar a cada minor da camada. A
barreira do monorepo é o que prova a compatibilidade, e o limite inferior só sobe
quando o contrato do hook muda de forma incompatível.
A cache do Turborepo é apenas local — sem cache remota, sem conta, sem token.
Uma tarefa só é guardada em cache onde as suas entradas declaradas determinam o
resultado: os testes unitários sim; o typecheck, a compilação e cada verificação
sobre uma compilação resolvem latest contra um remoto e não.
Consequências
A decisão de repositórios próprios registada em #74 e #75 fica substituída; essas issues constroem aqui os seus pacotes fornecedores.
Cada caminho relativo à camada mudou, e nenhum se resolve como se lê, pelo que a mudança vale o que valer a sua verificação: a barreira completa, uma compilação para Workers com a verificação da classificação de rotas e o tarball empacotado instalado num consumidor de teste.
Uma alteração ao contrato do hook e aos seus consumidores chega agora num único pull request atrás de uma única barreira, e o site de desenvolvimento pode usar um fornecedor como dependência do workspace.
Um monorepo que marca com release-please pode apontar o duxt às suas próprias tags, o que torna o suporte a tags com prefixo uma funcionalidade para consumidores e não apenas uma comodidade deste repositório.
Os caminhos dos registos anteriores descrevem a estrutura do seu tempo. Não são reescritos, porque estes registos só se acrescentam.