npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@devix-tecnologia/deploy-kit

v0.1.0

Published

Kit de deploy Devix: tags, registry, git, env, docker, receita one-click, CapRover e release, dirigidos por um DevixConfig

Readme

@devix-tecnologia/deploy-kit

@devix-tecnologia/deploy-kit

Kit de deploy da Devix: o que todo projeto faz para publicar uma imagem Docker e apontar uma app do CapRover para ela — tags, registry, guard-rails de git, .env por ambiente, build, receita One-Click, deploy e release.

Extraído do packages/deploy/scripts/lib do projeto de referência (decisão 3 do ADR 001) e parametrizado: tudo que antes era constante hardcoded — prefixo de imagem, mapa de nomes fora do padrão, lista de imagens de terceiros, registro de serviços deployáveis, prefixos CAPROVER_<PREFIX>_* — virou DevixConfig. Nada específico de projeto mora aqui.

O kit não é um CLI. Ele é a biblioteca que o @devix-tecnologia/devix-cli chama; é também o que um script solto pode importar por subpath.


Instalação e uso

import { planejarReleaseStaging, executarPassos } from '@devix-tecnologia/deploy-kit';
import { config } from './devix.config.js';

await executarPassos(
  planejarReleaseStaging(config, { gerarReceita: true, pinSha: true }),
  console.log
);

Cada módulo também é importável isoladamente, para um comando pequeno não arrastar o kit inteiro:

import { comporTags } from '@devix-tecnologia/deploy-kit/tags';
import { consultarDigest } from '@devix-tecnologia/deploy-kit/docker';

Módulos

| Subpath | O que faz | Principais exports | | --- | --- | --- | | /config | O DevixConfig e as consultas sobre ele. Valida com zod antes de qualquer build. | definirConfig, validarConfig, servicoDaConfig, servicosNoCicloDeTags, lerVersaoDoProjeto, diretorioDeArtefatos | | /registry | Resolve o registry por imagem e compõe a referência (sem esquema). | resolverRegistry, comporReferencia, nomeImagemDoServico, variavelDeOverride | | /tags | Esquema de tags, arquivos build-tags-*.json e decisão de push. | comporTags, tagsDoAmbiente, conteudoBuildTags, lerBuildTags, carregarBuildTags, decidirServicosParaPush | | /git | Guard-rails: árvore limpa e HEAD pushado antes de publicar. | shaParaPublicacao, estadoDoPush, exigirHeadPushado | | /env | Carrega .env.<ambiente> e valida contra a config (decisão 13). | carregarEnvDoAmbiente, validarEnv, esquemaEnv, credenciaisDoRegistry | | /docker | Build, tag, pull, push, login e digest — tudo por um executor injetável. | buildImagem, tagImagem, pushTags, puxarImagem, loginNoRegistry, consultarDigest, imagemExisteNoRegistry, detectarContextoDocker | | /one-click | Reescreve os defaultValue de imagem do template CapRover. Puro. | gerarReceita, resolverImagensDoTemplate, escolherTag, servicoDaVariavel | | /caprover | Deploy por imagem e promoção staging → produção (decisão 11). | deployPorImagem, resolverImagemDeDeploy, planejarPromocao, promoverParaProducao | | /release | Orquestra tudo como PassoDeRelease[], com colaboradores injetados. | planejarReleaseStaging, planejarReleaseProducao, executarPassos, registrarLog |


DevixConfig

Vive em devix.config.ts na raiz do projeto. Exemplo completo de um projeto fictício (acme) — é a mesma config que os testes do kit usam:

import { definirConfig } from '@devix-tecnologia/deploy-kit/config';

export default definirConfig({
  // Slug do projeto. Prefixo padrão das imagens: `acme-<serviço>`.
  projeto: 'acme',

  // Opcional. Nome da variável com a URL do registry (COM esquema).
  // Padrão: DOCKER_REGISTRY_URL.
  registry: { urlEnv: 'DOCKER_REGISTRY_URL' },

  ambientes: {
    staging: {
      envFile: 'packages/deploy/.env.staging',
      caprover: { urlEnv: 'CAPROVER_URL' },
    },
    production: {
      envFile: 'packages/deploy/.env.production',
      caprover: { urlEnv: 'CAPROVER_URL' },
    },
  },

  servicos: {
    // Serviço comum: imagem `acme-directus`, tag com versão.
    directus: {
      dockerfile: 'docker/directus/Dockerfile',
      contexto: '.',
      args: { NODE_VERSION: '24' },
      caprover: { envPrefix: 'DIRECTUS' },
    },

    // `usaVersao: false` → só `<env>-latest` e `<sha>`. Para imagens de
    // infraestrutura, cuja versão não acompanha a do artefato.
    nginx: {
      dockerfile: 'docker/nginx/Dockerfile',
      contexto: 'docker/nginx',
      usaVersao: false,
      caprover: { envPrefix: 'NGINX' },
    },

    // `imagem` quebra o padrão `<projeto>-<serviço>` quando o nome no registry
    // já existe com outro nome.
    'mqtt-bridge': {
      imagem: 'acme-activemq-mqtt-bridge',
      dockerfile: 'docker/bridge/Dockerfile',
      contexto: 'docker/bridge',
      caprover: { envPrefix: 'MQTT_BRIDGE' },
    },

    // `foraDoCicloDeTags`: imagem própria com esquema de tag PRÓPRIO (por
    // cliente, por região, versão upstream fixada). A receita mantém o
    // defaultValue do template e o `--pin-sha` não exige build-tags dela.
    portal: {
      dockerfile: 'docker/portal/Dockerfile',
      contexto: 'docker/portal',
      foraDoCicloDeTags: true,
      caprover: { envPrefix: 'PORTAL' },
    },

    // Terceiro: nunca construído nem publicado. A referência vem do template
    // da receita; um override `DOCKER_REGISTRY_URL_LOKI` o espelha.
    loki: { terceiro: true },
  },

  receita: {
    template: 'packages/deploy/caprover/one-click-app.yml',
    // Variável do template → serviço da config, quando os nomes divergem:
    // `$$cap_bridge_image` publica como `mqtt-bridge`.
    aliases: { bridge: 'mqtt-bridge' },
    // Apps CapRover fora da receita. Consumido pela auditoria do
    // `caprover-stack`, não pelo kit.
    appsStandalone: ['acme-backup'],
  },

  // Origem da versão do artefato (decisão 12).
  // Padrão: packages/deploy/package.json.
  versao: { fonte: 'packages/deploy/package.json' },

  // Onde caem os arquivos que o release GERA (decisão 14).
  // Padrão: a pasta de `versao.fonte` — aqui, `packages/deploy`.
  artefatos: { diretorio: 'packages/deploy' },
});

Onde caem os artefatos do release

Três arquivos são gerados por um release, e todos os três vão para o diretório de artefatos (decisão 14 do ADR 001):

| Arquivo | Para quê | | --- | --- | | build-tags-<ambiente>-<serviço>.json | prova do que foi publicado; é o que autoriza a promoção para produção | | one-click-app-<ambiente>.gerado.yml | receita com os defaultValue de imagem resolvidos | | .releases/<data>-<ambiente>-<sha>.log | o que exatamente aconteceu naquele release |

O padrão é a pasta de versao.fonte (packages/deploy), não a raiz: a config já aponta para lá duas vezes — ambientes.*.envFile e versao.fonte — e deixá-los na raiz espalha um arquivo por serviço por ambiente ao lado do package.json do projeto. artefatos.diretorio sobrepõe.

A distinção que importa: a raiz é a âncora do que a config declara (receita.template, dockerfile, contexto); o diretório de artefatos é o destino do que o release produz. Os primeiros continuam relativos à raiz.

Na leitura dos build-tags, o diretório de artefatos vem primeiro e a raiz entra como segunda opção — um projeto com arquivos gravados antes desta mudança continua promovendo sem rebuild.

Variáveis de ambiente que o kit lê

| Variável | Quem usa | Obrigatória | | --- | --- | --- | | DOCKER_REGISTRY_URL (ou o registry.urlEnv) | registry, docker | sim, no build | | DOCKER_REGISTRY_URL_<IMAGEM> | registry | não (override por imagem) | | DOCKER_REGISTRY | registry | não (legado, sem esquema, avisa) | | DOCKER_REGISTRY_USERNAME / _PASSWORD | docker (login) | não (registry anônimo é válido) | | DOCKER_USERNAME / DOCKER_PASSWORD | docker (login) | não (fallback) | | DOCKER_HOST, DOCKER_BUILDKIT | docker | não (respeitadas se presentes) | | CAPROVER_URL (ou o caprover.urlEnv do ambiente) | caprover | sim, no deploy | | CAPROVER_<PREFIX>_APP_NAME / _APP_TOKEN | caprover | sim, por serviço deployado | | CAPROVER_<PREFIX>_URL | caprover | não (sobrepõe a URL do ambiente) | | CAPROVER_<PREFIX>_IMAGE | caprover | não (escape hatch: imagem literal) |


Precedência de resolução do registry

DOCKER_REGISTRY_URL_<IMAGEM>   → override por imagem (espelho, registry do cliente)
DOCKER_REGISTRY_URL            → padrão do projeto, COM esquema
DOCKER_REGISTRY                → legado, SEM esquema; resolve, mas avisa
(nenhuma)                      → RegistryNaoConfiguradoError

O esquema fica na configuração, não na tag: é ele que permite ao docker login acertar o endpoint e distinguir http de https. Uma tag Docker não aceita esquema, então resolverRegistry devolve os dois lados — url (com) e host (sem) —, e comporReferencia usa o host.

Dois casos especiais:

  • Docker Hub (docker.io, registry-1.docker.io, index.docker.io) resolve host: '': a referência sai sem prefixo, como o Docker espera.
  • Terceiro sem override não resolve registry nenhum: a referência é a que está fixada no template da receita. Com override, o terceiro é espelhado preservando o caminho original (grafana/loki → <registry>/grafana/loki).

Esquema de tags

Todas as tags saem sem esquema. Todo build publica três (ou duas):

| Tag | Para quê | | --- | --- | | <imagem>:<ambiente>-latest | flutuante do ambiente; é reescrita a cada push | | <imagem>:<tag de versão> | rollback por versão (omitida com usaVersao: false) | | <imagem>:<sha> | identidade imutável do artefato; sempre publicada |

A tag de versão difere por ambiente (decisão 12):

| Ambiente | Tag de versão | Por quê | | --- | --- | --- | | production | production-<versão> | a versão bumpa na promoção (lerna version, conventional commits), então ela identifica um artefato só | | staging | staging-<versão>-<sha> | há muitos builds por versão; sem o sha, o segundo build sobrescreveria o primeiro e staging-1.2.0 não identificaria nada |

A versão sai do version do package.json apontado por versao.fonte (padrão packages/deploy/package.json).

Produção é promoção, não rebuild (decisão 11): promoverParaProducao puxa <imagem>:<sha> publicada por staging, retagueia para as tags de produção e publica. Ela recusa um sha que não consta nos build-tags-staging-*.json, salvo force — e o log do release registra que passou sem prova.


Como o --platform é decidido

O alvo é sempre o CapRover, que roda Linux x86_64. Buildar num Mac Apple Silicon sem forçar a plataforma produz uma imagem arm64 que sobe e morre com "exec format error" — falha no deploy, longe da causa.

plataforma: null            → nenhum --platform (desligado de propósito)
plataforma: 'linux/arm64'   → o valor informado
process.arch !== 'x64'      → --platform linux/amd64
process.arch === 'x64'      → nenhum --platform (build nativo, mais rápido)

DOCKER_HOST e o contexto Docker ativo são herdados, nunca sobrescritos: buildar numa máquina Linux a partir do Mac é só exportar DOCKER_HOST (e aí process.arch do host que decide continua sendo o do Mac — forçar linux/amd64 num daemon amd64 é inócuo).

A única variável que o kit decide sozinho é DOCKER_BUILDKIT, herdando o wrapper de Colima de origem:

DOCKER_BUILDKIT definido  → respeitado
contexto/DOCKER_HOST com "colima", ou `colima status` ok no macOS  → '0'
caso contrário            → '1'

BuildKit desligado em Colima porque o buildkitd da VM quebra em builds multi-estágio grandes; o builder clássico não.


Testes

299 testes, vitest --run. Nenhum toca docker, rede, git ou filesystem real: o executor de processo (ExecutorDeProcesso), o git (ExecutarGit), o leitor e o escritor de arquivo e o cliente CapRover (ClienteCapRover) são todos injetados, e nos testes são sempre falsos.

A config de exemplo dos testes é src/teste/projeto-ficticio.ts — o mesmo acme deste README. Ela fica fora do pacote publicado.


O que ficou de fora, e por quê

  • Auditorias da receita ($cap_ com um $, srv-captain-- órfão, depends_on fora de ordem, heredoc em dockerfileLines, paridade staging/produção, pgcat sem réplica, superset sub-filter…). Vão para o pacote caprover-stack, junto com o template que elas auditam (decisão 16). O kit resolve imagens; ele não opina sobre o conteúdo da receita.
  • O cliente CapRover. @devix-tecnologia/caprover-client NÃO é dependência de compilação: caprover.types.ts declara só o método que o kit usa (deployarImagem). Assim o kit não fica preso ao contrato 1.14/1.15 do painel e os testes não precisam de servidor fake.
  • Início automático do daemon Docker (colima start, open -a Docker, o laço de espera do docker-build-wrapper.mjs). É preparo de máquina, com espera interativa; o kit expõe daemonRespondendo() e quem decide o que fazer com um "não" é o CLI. A parte que importava — detecção de Colima e escolha do BuildKit — foi absorvida em detectarContextoDocker.
  • docker compose. A versão anterior buildava via docker-compose -f … build <serviço>, o que amarrava a lista de imagens ao compose e exigia um mapa de "nome do serviço no compose". O kit chama docker build direto, com dockerfile e contexto vindos da config (decisão 9: um Dockerfile por imagem).
  • Impressão colorida / chalk / zx. O kit devolve dados e passos nomeados; quem imprime é o CLI. É o que permite testar a orquestração sem capturar stdout.
  • aplicar-receita-local, e2e-caprover, diagnósticos de conectividade. São ferramentas de teste de integração de um projeto, não parte do deploy.
  • Multi-tenant / multi-região (--service portal:cliente-x). É domínio de um projeto (decisão 4). O equivalente genérico aqui é foraDoCicloDeTags mais um serviço por slug na config do projeto.
  • lerna version e a tag git v<versão>. O bump de versão é do monorepo do projeto (decisão 12); o kit só LÊ a versão resultante.