Instalando o Discourse para desenvolvimento no macOS usando asdf e docker-compose

Nota: Este é um guia de instalação para ambiente de desenvolvimento não suportado. Os guias oficialmente suportados para macOS estão aqui (nativo) e aqui (Docker). Proceda por sua conta e risco.

O objetivo deste guia é containerizar o PostgreSQL e o Redis, mas manter o Ruby fora dos containers.

Eu tentei a abordagem Discourse para Desenvolvimento usando Docker, mas foi simplesmente muito lento na minha máquina.

Em seguida, analisei o guia Discourse para Desenvolvimento no macOS. Mas a primeira coisa que o script faz é instalar o brew. O brew pode ser ótimo, mas uso o MacPorts há muito tempo e gostaria de continuar resistindo com sucesso à instalação do brew. Além disso, esse script também realizava instalações globais de coisas como PostgreSQL e Redis, que eu prefiro poder manter em uma base por projeto.

Então, aqui está o que funcionou para mim usando uma combinação de asdf e docker-compose. O resultado é um meio-termo entre as duas abordagens descritas acima. O PostgreSQL e o Redis são executados em um container usando docker-compose, para que possam ser fixados nas versões oficiais e nas instalações que o Discourse usa em produção. O Rails roda diretamente no sistema. Essa combinação é consideravelmente mais ágil para mim. Seu resultado pode variar (YMMV).

Se você quiser acompanhar, precisará ter o asdf e o Docker instalados na sua máquina. (Nossa, o asdf é realmente fantástico… você definitivamente deveria baixá-lo se tiver qualquer interesse em manter facilmente muitos ambientes de desenvolvimento diferentes. Ele substitui o renv, o nvm… aparentemente quase tudo, exceto o jenv.)

Se você olhar o que o script de instalação para macOS estava fazendo, pode separar o que está sendo instalado em três categorias:

  • ambientes e ferramentas de linha de comando como ruby e yarn. Vamos instalá-los e fixar suas versões no diretório do nosso projeto usando asdf.
  • serviços—especificamente PostgreSQL e Redis. Vamos instalá-los usando Docker Compose, novamente para que possamos fixar suas versões no que precisamos para este projeto e também ter um ambiente de desenvolvimento que possamos iniciar e parar facilmente.
  • outros—principalmente bibliotecas para manipulação de imagem como ImageMagick e otimização. Estas podem ser instaladas usando brew, port ou diretamente do código-fonte.

Também precisaremos reconfigurar levemente nosso ambiente de desenvolvimento para conectar ao servidor PostgreSQL executado pelo docker-compose.

Código Fonte do Discourse

Todas as etapas abaixo devem ser feitas dentro do seu diretório de origem do Discourse:

git clone https://github.com/discourse/discourse.git && cd discourse

Isso é importante, pois é aqui que o asdf salvará seu arquivo de configuração .tool-versions e onde criaremos nosso arquivo docker-compose.yml para o Docker.

asdf

Há três coisas que precisamos instalar usando asdf: ruby, yarn e postgres. Felizmente, o asdf facilita tanto a instalação de todos de uma vez quanto a fixação das versões no diretório do nosso projeto. Primeiro, crie o arquivo .tool-versions com este conteúdo:

yarn 1.22.2
ruby 2.6.5
postgres 10.12

Em seguida, basta executar asdf install.

Agora você deve ser capaz de realizar as etapas de instalação da biblioteca Ruby incluídas no script e mais tarde nas instruções:

gem update --system
gem install bundler
gem install rails
gem install mailcatcher
gem install pg -- --with-pg-config=$HOME/.asdf/installs/postgres/10.12/bin/pg_config
bundle install

Você pode precisar ajustar o caminho para pg_config dependendo de onde instalou o asdf.

docker-compose.yml

Em seguida, precisamos criar nosso arquivo docker-compose.yml configurado para iniciar o Redis e o PostgreSQL. O meu se parece com isto:

version: "3"
networks:
  discourse:
    driver: bridge
services:
  data:
    image: "geoffreychallen/discourse_data:latest"
    command: /sbin/boot
    ports:
      - "5432:5432"
      - "6379:6379"
    volumes:
      - "data_shared:/shared/"
      - "data_logs:/var/log/"
    networks:
      - discourse
volumes:
  data_shared:
    driver: local
  data_logs:
    driver: local

Graças a @pfaffman pela sugestão de usar um container de dados padrão do Discourse. geoffreychallen/discourse_data:latest é construído a partir do Discourse Docker. Usei o arquivo de exemplo data.yml com duas alterações. Primeiro, defini a senha do usuário discourse como “discourse”. Segundo, tornei esse usuário um superusuário para que ele possa criar bancos de dados de teste. Aqui está a parte hooks do meu arquivo data.yml:

hooks:
  after_postgres:
    - exec:
        stdin: |
          alter user discourse with password 'discourse';
        cmd: sudo -u postgres psql discourse
        raise_on_fail: false
    - exec:
        stdin: |
          alter user "discourse" with superuser;
        cmd: sudo -u postgres psql discourse
        raise_on_fail: false

Novamente, isso é apenas no caso de você querer construir seu próprio container de dados do Discourse e não usar o meu. Por favor, não use este container em produção—ele é completamente inseguro!

Nesta configuração, expomos as portas padrão do PostgreSQL e do Redis e executamos o comando boot que o container precisa para iniciar.

Depois que seu docker-compose.yml estiver no lugar, teste-o:

docker-compose up

Supondo que tudo esteja configurado corretamente, você deverá ver o Redis e o PostgreSQL iniciando. Pressione Control-C para cancelar ou execute docker-compose down se, por algum motivo, algo não for desligado corretamente.

Bibliotecas Diversas

A maioria das bibliotecas de otimização de imagem pode ser instalada usando port ou brew. Veja como fazer isso com port:

sudo port install imagemagick pngquant optipng jhead jpegoptim gifsicle

O svgo pode ser instalado assim que você tiver o npm. Não vou cobrir isso, pois é bastante direto.

A propósito, pelo que posso ver (FWIW AFAICT), nenhuma dessas ferramentas é obrigatória. Vejo avisos durante várias etapas posteriores sobre elas estarem faltando, mas nada parece explodir.

config/database.yml e spec/fixtures/multisite/two_dbs.yml

Finalmente, precisamos reconfigurar levemente nosso ambiente de desenvolvimento para conectar corretamente ao PostgreSQL. Por padrão, ele tenta usar um socket Unix, que não é exportado pelo nosso container.

Para corrigir isso, você precisa modificar o config/database.yml. Essencialmente, em todos os lugares onde você vê:

adapter: postgresql

Substitua por:

adapter: postgresql
host: localhost
username: discourse
password: discourse

A adição da host faz com que o Discourse não use um socket, e o username e password fazem com que o Discourse se conecte usando o usuário padrão do banco de dados do Discourse e a senha que definimos acima.

Eu tive que fazer essa alteração três vezes no config/database.yml: uma vez em development, depois em test e finalmente em profile. Para fazer a suíte de testes funcionar, também precisei fazer uma alteração semelhante em spec/fixtures/multisite/two_dbs.yml.

Vamos Lá…

Tudo bem, vamos lá! Em uma janela, inicie seu ambiente de desenvolvimento usando docker-compose:

docker-compose up

Em uma segunda janela, vamos executar as etapas de configuração do banco de dados:

bundle exec rake db:create

Supondo que funcionou, agora você pode continuar no ponto apropriado no guia para macOS baseado em brew.

Quando terminar de trabalhar, pare o docker-compose e você pode guardar seu ambiente de desenvolvimento até a próxima vez.

Se você quiser excluir permanentemente o banco de dados e o conteúdo do Redis, basta executar um docker-compose down -v para apagar os volumes persistentes junto com os próprios containers. Mas sem a flag -v, o docker-compose down manterá seu banco de dados persistente entre sessões de desenvolvimento.

Os Testes Passam?

Minha configuração falhou em dois casos de teste:

Failures:

  1) UploadCreator#create_for pngquant should apply pngquant to optimized images
     Failure/Error: expect(upload.filesize).to eq(9558)

       expected: 9558
            got: 9550

       (compared using ==)
     # ./spec/lib/upload_creator_spec.rb:115:in `block (4 levels) in <main>'

  2) tasks/uploads uploads:secure_upload_analyse_and_update when store is external when secure media is enabled rebakes the posts attached
     Failure/Error: expect(post1.reload.baked_at).not_to eq(post1_baked)

       expected: value != 2020-03-08 03:20:01.777117000 +0000
            got: 2020-03-08 03:20:01.777117000 +0000

       (compared using ==)

       Diff:
         <The diff is empty, are your objects producing identical `#inspect` output?>
     # ./spec/tasks/uploads_spec.rb:90:in `block (5 levels) in <main>'

Finished in 19 minutes 21 seconds (files took 13.67 seconds to load)
4297 examples, 2 failures, 11 pending

Para mim, o primeiro parece que o pngquant está funcionando um pouco melhor do que o esperado. Não sei por que isso representa uma falha. O segundo eu também não entendo. Mas isso parece sensato para mim.

Bom hacking!

5 curtidas