Publicação de gems bp-*
no RubyGems
Uma distribuição identificável, versionada e pública do Decidim para o governo brasileiro — instalável com uma linha no Gemfile, sem confundir com o upstream.
01Problem Statement
Hoje, o repositório decidim é um fork do Decidim com customizações para o governo brasileiro — entre elas a gem decidim-govbr (Padrão Digital de Governo), modificações em decidim-forms e decidim-surveys, e uma coleção de módulos de terceiros vendored em vendor/modules/ com patches de compatibilidade local.
Aplicações downstream (instâncias do Decidim para órgãos do governo) precisam consumir essas customizações. O modelo atual — clonar o repositório inteiro ou referenciar via path: / git: no Gemfile — escala mal, não permite versionamento semântico adequado no ecossistema Ruby, e gera confusão com as gems oficiais do upstream Decidim publicadas no RubyGems. Não há um pacote público, versionado e identificável como "nossa distribuição" que times downstream possam instalar com uma única linha no Gemfile.
02Solution
Publicar as gems customizadas no RubyGems sob o namespace bp-* (Brasil Participativo), com versionamento próprio sincronizado ao major.minor do upstream Decidim e sufixo bp1, bp2, … para as customizações locais. Uma meta-gem bp-decidim agrega todas as outras, permitindo que aplicações downstream instalem tudo com gem "bp-decidim".
O namespace de código Ruby (Decidim::Surveys, Decidim::Forms, etc.) não muda — apenas o nome da gem no RubyGems ganha o prefixo bp-. A estrutura física de diretórios no monorepo também permanece a mesma, para manter compatibilidade de rebase com o upstream.
03User Stories
04Implementation Decisions
Escopo de renomeação
20 gems renomeadas e publicadas sob o namespace bp-*, distribuídas entre o monorepo do Decidim fork, módulos vendored e o repositório lappis-unb/decidimbr/components-brasil-participativo.
Padrão A — prefixo bp-decidim-
Gems do monorepo, vendored e componentes GovBR — recebem o prefixo bp-decidim- como sinal de distribuição.
| Gem atual | Novo nome | Origem | Versão |
|---|---|---|---|
decidim-forms | bp-decidim-forms | Monorepo (modificada) | 0.33.0.bp1 |
decidim-surveys | bp-decidim-surveys | Monorepo (modificada) | 0.33.0.bp1 |
decidim-govbr | bp-decidim-govbr | Monorepo (gem nova) | 0.33.0.bp1 |
decidim | bp-decidim | Meta-gem | 0.33.0.bp1 |
decidim-analytics | bp-decidim-analytics | Vendor | 0.30.0.bp1 |
decidim-decidim_awesome | bp-decidim-decidim_awesome | Vendor | 0.11.2.bp1 |
decidim-ephemeral_participation | bp-decidim-ephemeral_participation | Vendor | 0.0.9.bp1 |
decidim-extra_blocks | bp-decidim-extra_blocks | Vendor | 0.1.0.bp1 |
decidim-homepage_interactive_map | bp-decidim-homepage_interactive_map | Vendor | 2.0.0.bp1 |
decidim-toggle | bp-decidim-toggle | Vendor | 0.1.3.bp1 |
caracal | bp-caracal | Vendor | 1.7.2.bp1 |
decidim-government_spaces | bp-decidim-government_spaces | lappis-unb/components-br | 0.33.0.bp1 |
decidim-participatory_texts | bp-decidim-participatory_texts | lappis-unb/components-br | 0.33.0.bp1 |
decidim-questionnaires | bp-decidim-questionnaires | lappis-unb/components-br | 0.33.0.bp1 |
decidim-categories | bp-decidim-categories | lappis-unb/components-br | 0.33.0.bp1 |
decidim-extra_home_blocks | bp-decidim-extra_home_blocks | lappis-unb/components-br | 0.33.0.bp1 |
decidim-apartment | bp-decidim-apartment | lappis-unb/infra | 0.1.0.bp1 |
Padrão B — decidim-bp-X
Gems que já tinham bp_ no nome (do repo lappis-unb/decidimbr/components-brasil-participativo). Apenas trocamos underscore por hífen — o bp já identifica como nossa.
| Gem atual | Novo nome | Origem | Versão |
|---|---|---|---|
decidim-bp_proposals | decidim-bp-proposals | lappis-unb/components-br | 0.33.0.bp1 |
decidim-bp_meetings | decidim-bp-meetings | lappis-unb/components-br | 0.33.0.bp1 |
decidim-bp_comments | decidim-bp-comments | lappis-unb/components-br | 0.33.0.bp1 |
Gems NÃO renomeadas
Todas as demais do monorepo (decidim-core, decidim-admin, decidim-proposals, decidim-meetings, decidim-budgets, decidim-assemblies, …) continuam com nome upstream. Gems de terceiros que não são do escopo Brasil Participativo: decidim-community_templates (decidim-ice), deface (fork pontual), omniauth-govbr (mantida por pessoa física).
Versões suportadas
Apenas Decidim 0.33 (atual). Aplicações downstream em 0.30 continuam usando path: / git: como hoje. Não haverá branches de release paralelas para versões antigas do upstream.
Estratégia de versionamento
- Sincronizar
major.minorcom o upstream Decidim. - Usar sufixo pré-release
bp1,bp2, … para customizações locais (ex:0.33.0.bp1). - Quando o upstream lançar
0.33.0final, o Bundler oferecerá upgrade automaticamente (porque0.33.0.bp1 < 0.33.0na ordenação doGem::Version). - Após rebasar nossas mudanças no upstream final, publicar como
0.33.1.bp1ou0.34.0.bp1conforme a natureza. - Versões base das gems vendored preservam sua origem.
O que muda no empacotamento (e o que NÃO muda)
Muda: s.name no gemspec, s.authors (adicionar contribuidores locais), s.homepage e s.metadata (apontar para o repositório fork), add_dependency que referenciam gems do escopo.
NÃO muda:
- Diretórios físicos no monorepo (
decidim-surveys/continua com esse nome). - Paths de require (
require "decidim/surveys"continua igual). - Namespaces Ruby (
Decidim::Surveys,Decidim::Forms, etc.). - Tasks de engine, rotas, migrations, células, controllers.
Essa decisão é crítica para manter compatibilidade com o ecossistema Decidim e evitar refatoração massiva em aplicações downstream.
Meta-gem bp-decidim
Renomeada de decidim para bp-decidim. Lista como dependências:
- Gems do monorepo renomeadas (
bp-decidim-forms,bp-decidim-surveys,bp-decidim-govbr). - Gems do monorepo não-renomeadas (mantidas com nome upstream:
decidim-core,decidim-admin,decidim-proposals, …). - Gems vendored renomeadas (
bp-decidim-analytics,bp-decidim-decidim_awesome,bp-decidim-ephemeral_participation,bp-decidim-extra_blocks,bp-decidim-homepage_interactive_map,bp-decidim-toggle,bp-caracal). - Gems do repo
lappis-unb/decidimbrrenomeadas (bp-decidim-government_spaces,bp-decidim-participatory_texts,bp-decidim-questionnaires,bp-decidim-categories,bp-decidim-extra_home_blocks,bp-decidim-apartment). - Gems com padrão
decidim-bp-X(decidim-bp-proposals,decidim-bp-meetings,decidim-bp-comments).
Downstream instala só bp-decidim e recebe o conjunto completo.
Ordem de publicação (DAG de dependências)
Publicar na ordem topológica, das folhas para a raiz:
- Folhas (sem dependências internas bp-*):
bp-decidim-analytics,bp-decidim-toggle,bp-decidim-extra_blocks,bp-decidim-ephemeral_participation,bp-decidim-homepage_interactive_map,bp-decidim-decidim_awesome,bp-caracal. - Meio (dependem apenas de upstream não-renomeado):
bp-decidim-forms(depende dedecidim-coreupstream),bp-decidim-govbr(depende dedecidim-coreupstream). - Dependência interna:
bp-decidim-surveys(depende debp-decidim-formsjá publicada +decidim-coreupstream). - Raiz (meta-gem):
bp-decidim.
Processo de release
rake releasedo bundler/gem_tasks continua sendo o mecanismo base (build → tag → push → gem push).lib/decidim/release_manager.rbpassa a aceitar o remote do fork brasileiro (não mais exigedecidim/decidim).Rakefilecomrelease_allajustado para iterar sobre o conjuntobp-*na ordem da DAG.- Credenciais:
~/.gem/credentialscom:rubygems_api_key, obtido viagem signin. - MFA: obrigatório no RubyGems desde 2022; o
gem pushpede OTP interativamente. - Adicionar
s.metadata["allowed_push_host"] = "https://rubygems.org"em cada gemspec como segurança contra push acidental para registry privado.
Pré-requisitos da publicação
- Conta RubyGems com MFA ativo (decidir entre conta pessoal ou ownership group).
- Todos os 11 nomes
bp-*disponíveis no RubyGems (verificar via API; ter alternativos prontos:br-decidim-*,govbr-decidim-*). - Simplificações do
decidim-surveysaplicadas (fases 1–3 deplano-simplificacao-surveys.md) — pré-requisito hard, não publicar antes. - Specs de
decidim-formsedecidim-surveyspassando em baseline.
05Organização de Repositórios
Com 20 gems distribuídas hoje em 3 repositórios diferentes (monorepo Decidim fork, lappis-unb/decidimbr/components-brasil-participativo, lappis-unb/decidimbr/infra/participa-gem), vale avaliar se faz sentido consolidar ou manter a estrutura atual antes de investir no processo de publicação.
Monorepo (todas as gems bp-* em um único repositório)
- Commits atômicos cross-gem — uma mudança que afeta 3 gems é um único commit, uma revisão, um merge. Não existe o problema de sincronizar PRs em múltiplos repos.
- Refatoração global é barata — renomear uma interface compartilhada entre 5 gems gera um PR, não cinco.
- CI compartilhado — uma única pipeline, um único conjunto de configs (Rubocop, RSpec helpers, linters).
- Dependências internas via
path:durante desenvolvimento — sem publicar versão intermediária para testar integração. - Versionamento sincronizado — todas as gems podem ser versionadas juntas (o
BP_VERSIONúnico já sugere esse modelo). - Descoberta facilitada — qualquer pessoa encontra qualquer gem em um só lugar. Onboarding mais rápido.
- Issues e PRs unificados — um só backlog para discutir prioridades entre gems.
- Repo grande — clone e checkout mais lentos; histórico do git cresce mais rápido.
- Permissões grossas — quem tem write no repo tem write em todas as gems. Sem isolamento por time.
- CI lento sem filtros — uma mudança em qualquer gem pode disparar testes de todas (precisa filtros inteligentes por path).
- Acoplamento implícito — fácil criar dependências circulares entre gems porque "estão todas ali".
- Release acoplada — quando uma gem muda, as outras podem ser lançadas junto mesmo sem mudanças.
- Ponto único de falha — problema no repo trava todas as gems.
- Code ownership difusa — difícil ter "dono" específico de uma gem quando tudo está misturado.
Polyrepo (uma gem por repositório)
- Repos pequenos e rápidos — clone, CI, histórico todos enxutos.
- Permissões granulares — time A é dono da gem A, time B é dono da gem B.
- CI isolado — só roda testes da gem que mudou.
- Independência de release — cada gem lança no seu ritmo; gem estável não é arrastada por mudança em outra.
- Autonomia tecnológica — cada gem pode adotar versões diferentes de Ruby, ferramentas, padrões.
- Blame contido — um bug em uma gem não contamina outras.
- Documentação focada — cada repo tem README, issues e wiki dedicados ao seu escopo.
- Cross-repo changes são caras — uma mudança que afeta 3 gems exige 3 PRs, 3 revisões, 3 merges, 3 releases coordenadas.
- Dependências internas via
git:/ versão publicada — loop de feedback mais lento durante desenvolvimento. - Duplicação de boilerplate — cada repo precisa seu próprio CI, Rubocop, RSpec setup, templates de PR/issue.
- Versionamento desencontrado — saber quais combinações de 20 gems são compatíveis entre si vira um problema.
- Descoberta fragmentada — "onde está a gem X?" precisa de um índice ou catálogo.
- Refatoração global é cara — mudar uma interface compartilhada = N PRs, N revisões, N merges.
- Dependency hell distribuído — atualizar uma dependência compartilhada = rodar CI em 20 repos.
- Context switching — manter 20 abas de repo, 20 backlogs de issue, 20 pipelines de CI.
Recomendação para o contexto Brasil Participativo
Considerando time enxuto de governo, 20 gems com alta interdependência e versionamento sincronizado ao upstream Decidim, a avaliação pende para monorepo.
Por quê: (1) cross-repo changes são frequentes no nosso caso — modificações em decidim-forms frequentemente exigem ajustes em decidim-surveys, decidim-questionnaires e outras; (2) quando o upstream lança 0.33.0, queremos rebasar todas as nossas 20 gems de uma vez; (3) não temos pessoas suficientes para justificar o overhead de manter 20 repos separados; (4) ferramentas já existem — Bundler com path:, CI com filtros por path (os ci_*.yml do próprio Decidim upstream já fazem isso), tooling de release (o Rakefile atual já itera sobre múltiplas gems).
Custos a mitigar no monorepo:
- CI inteligente: filtrar jobs por paths modificados (já é feito no upstream).
- Code ownership: usar
CODEOWNERSno GitHub para atribuir gems específicas a pessoas específicas. - Release seletiva: só publicar gems que mudaram desde a última release (detectar via
git diff). - Readmes por diretório: cada subdiretório mantém seu próprio README específico, mesmo estando no mesmo repo.
Ações concretas sugeridas:
- Consolidar as gems do repo
lappis-unb/decidimbr/components-brasil-participativono monorepo principal (mover os 8 módulos para subdiretórios). - Consolidar
decidim-apartmentdo repoinfra/participa-gemtambém (ou mantê-lo separado se for considerado "infra" e não "componente"). - Manter os subdiretórios atuais (
decidim-forms/,decidim-surveys/, etc.) como unidades de release independentes, cada uma com seu gemspec, README, specs. - Criar um
CODEOWNERSque atribua cada subdiretório a responsáveis específicos. - Adicionar no monorepo um índice (
GEMS.mdou similar) listando todas as gems, suas responsabilidades e status.
Quando polyrepo faria sentido: se no futuro as gems amadurecerem e passarem a ter times mantenedores distintos (ex: um time cuidando só de analytics, outro só de formulários), ou se alguma gem precisar ser aberta para contribuição externa sem expor o monorepo inteiro, vale reavaliar a migração de gems específicas para repos próprios. Mas isso é uma decisão futura.
06Testing Decisions
O que torna um teste bom aqui
- Testar comportamento externo: "a gem publicada instala e resolve corretamente" — não detalhes de como o gemspec foi editado.
- Preferir testes de integração em aplicação downstream dummy em vez de testes unitários em cada gemspec.
- Reaproveitar a
spec/decidim_dummy_appjá existente no monorepo como harness de validação.
O que será testado
- Resolve do Bundler: criar Gemfile de teste com
gem "bp-decidim", "~> 0.33.0.bp1"e validar quebundle installresolve sem conflito. - Smoke test da aplicação:
bundle exec rails serverem dummy app combp-decidimcarregado deve subir sem erro. - Metadata das gems:
gem specification bp-decidim-surveys -rdeve retornar autores, homepage e dependências corretas. - Namespace preservado:
require "decidim/surveys"deve continuar funcionando;Decidim::Surveysdeve continuar sendo o módulo esperado. - decidim-govbr carrega Web Components: renderizar uma view com
<br-header>e validar que o componente é registrado. - Simplificações aplicadas: validar que
decidim-surveysnão tem mais as features removidas (publicação de respostas por pergunta, múltiplos surveys por componente,allow_unregistered). - Ordenação de versão:
Gem::Version.new("0.33.0.bp1") < Gem::Version.new("0.33.0")retornatrue.
Prior art
spec/i18n_spec.rb(já usado pelocheck_locale_completenessno Rakefile).spec/decidim_dummy_app(app de teste já gerada porrake test_app).- Workflows
ci_*.ymlem.github/workflows/servem de modelo para como as gems são testadas individualmente hoje.
07Out of Scope
Decidim::Surveys, etc.) — intencionalmente preservados para compatibilidade.release-bp-gems.yml em tag v*) entra em trabalho futuro, após validar o processo manual.scripts/publish_bp.rb).decidim-enhanced_textwork — incompatível com 0.33, será tratado em iniciativa separada.release-please ou similar).packages/. Este PRD cobre só gems Ruby; publicação npm fica como trabalho futuro se necessário.08Riscos e Mitigações
| Risco | Mitigação |
|---|---|
Nome bp-* já ocupado no RubyGems | Verificar via API antes. Alternativos: br-decidim-*, govbr-decidim-*. |
Dependência circular entre bp-* | DAG mapeada; publicar em ordem topológica. |
decidim-core upstream incompatível com nossas modificações | Travar versão (~> 0.33.0) no gemspec até validarmos. |
Upstream lançar 0.33.0 final enquanto estamos em 0.33.0.bp1 | Comportamento desejado: Bundler oferece upgrade. Rebasar e lançar 0.33.1.bp1. |
| MFA do RubyGems expirar no meio do publish | gem signin antes de começar; chave de recovery em local seguro. |
| Vendor modules perderem sincronia com upstream próprio | Documentar no README de cada vendored o commit/branch base e o que foi patcheado. |
09Sequência de Execução Recomendada
bp-*.plano-simplificacao-surveys.md.rspec em decidim-forms e decidim-surveys (baseline).BP_VERSION e ajustar gemspecs.lib/decidim/release_manager.rb e Rakefile.Gemfile / Gemfile.lock do monorepo.release/bp-gems.10Decisão-chave de Design
A decisão mais importante deste PRD é não renomear o namespace Ruby nem os diretórios. Isso permite que aplicações downstream não precisem reescrever require ou referências a módulos, que o monorepo continue podendo ser rebasado contra o upstream Decidim sem conflitos massivos, e que o prefixo bp- cumpra seu papel (identificação no registry) sem causar fricção no código.
O custo dessa decisão é uma assimetria entre o nome da gem (bp-decidim-surveys) e o módulo (Decidim::Surveys), mas esse custo é bem conhecido no ecossistema Ruby (ex: gem rinku exporta Rinku, gem redcarpet exporta Redcarpet — nomes não precisam bater).