Installazione di Discourse per lo sviluppo su macOS usando asdf e docker-compose

Nota che questa è una guida per l’installazione in un ambiente di sviluppo non supportato. Le guide ufficialmente supportate per macOS sono qui (nativa) e qui (Docker). Procedi a tuo rischio.

L’obiettivo di questa guida è containerizzare PostgreSQL e Redis, ma mantenere Ruby al di fuori dei container.

Ho provato l’approccio Discourse per lo sviluppo usando Docker, ma si è rivelato troppo lento sulla mia macchina.

Successivamente ho esaminato la guida per lo sviluppo di Discourse su macOS. Ma la prima cosa che lo script faceva era installare brew. brew potrebbe benissimo essere ottimo, ma uso MacPorts da molto tempo e vorrei continuare a resistere con successo all’installazione di brew. Inoltre, quello script eseguiva anche installazioni globali di cose come PostgreSQL e Redis, che preferirei poter gestire su base per progetto.

Quindi ecco cosa ha funzionato per me usando una combinazione di asdf e docker-compose. Il risultato è un compromesso tra i due approcci descritti sopra. PostgreSQL e Redis vengono eseguiti in un container tramite docker-compose in modo da poterli fissare alle versioni ufficiali e alle installazioni utilizzate da Discourse in produzione. Rails gira direttamente sul sistema host. Questa combinazione è notevolmente più reattiva per me. I risultati possono variare.

Se vuoi seguire questa guida, dovrai avere installati sulla tua macchina sia asdf che Docker. (OMG asdf è davvero fantastico… dovresti assolutamente procurartelo se hai interesse a gestire facilmente molti diversi ambienti di sviluppo. Sostituisce renv, nvm… sembra quasi tutto tranne jenv.)

Se osservi cosa stava facendo lo script di installazione per macOS, puoi suddividere ciò che viene installato in tre categorie:

  • ambienti e strumenti da riga di comando come ruby e yarn. Li installeremo e fisseremo le loro versioni nella directory del nostro progetto utilizzando asdf.
  • servizi, in particolare PostgreSQL e Redis. Li installeremo utilizzando Docker Compose, ancora una volta per poter fissare le loro versioni a quelle necessarie per questo progetto e avere anche un ambiente di sviluppo che possiamo facilmente avviare e fermare.
  • altro, principalmente librerie per la manipolazione delle immagini come ImageMagick e ottimizzazione. Queste possono essere installate utilizzando brew, port o direttamente dal codice sorgente.

Dovremo anche riconfigurare leggermente il nostro ambiente di sviluppo per connetterci al server PostgreSQL eseguito da docker-compose.

Codice sorgente di Discourse

Tutti i passaggi seguenti dovrebbero essere eseguiti all’interno della tua directory del codice sorgente di Discourse:

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

Questo è importante poiché è qui che asdf salverà il suo file di configurazione .tool-versions e dove creeremo il nostro file docker-compose.yml per Docker.

asdf

Ci sono tre cose che dobbiamo installare utilizzando asdf: ruby, yarn e postgres. Fortunatamente, asdf rende facile installarle tutte insieme e fissare le versioni nella directory del nostro progetto. Innanzitutto, crea .tool-versions con questi contenuti:

yarn 1.22.2
ruby 2.6.5
postgres 10.12

Quindi esegui semplicemente asdf install.

Ora dovresti essere in grado di eseguire i passaggi di installazione delle librerie Ruby inclusi nello script e successivamente nelle istruzioni:

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

Potrebbe essere necessario regolare il percorso di pg_config a seconda di dove hai installato asdf.

docker-compose.yml

Successivamente dobbiamo creare il nostro file docker-compose.yml configurato per avviare Redis e PostgreSQL. Il mio appare così:

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
```\nGrazie a @pfaffman per il suggerimento di utilizzare un container standard dei dati di Discourse. `geoffreychallen/discourse_data:latest` è costruito partendo da [Discourse Docker](https://github.com/discourse/discourse_docker). Ho utilizzato il file di esempio `data.yml` con due modifiche. Innanzitutto ho impostato la password dell'utente discourse su 'discourse'. In secondo luogo ho reso quell'utente un superutente in modo che possa creare database di test. Ecco la parte `hooks` del mio file `data.yml`:

```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

Ancora una volta, questo è solo nel caso in cui tu voglia costruire il tuo container dei dati di Discourse e non usare il mio. Non utilizzare questo container in produzione: è completamente insicuro!

In questa configurazione esponiamo le porte standard di PostgreSQL e Redis ed eseguiamo il comando boot necessario al container per avviarsi.

Una volta che il tuo docker-compose.yml è in posizione, provalo:

docker-compose up

Assumendo che tutto sia configurato correttamente, dovresti vedere l’avvio di Redis e PostgreSQL. Usa Control-C per annullare o docker-compose down se per qualche motivo qualcosa non si spegne correttamente.

Librerie varie

La maggior parte delle librerie di ottimizzazione delle immagini può essere installata utilizzando port o brew. Ecco come fare con port:

sudo port install imagemagick pngquant optipng jhead jpegoptim gifsicle

svgo può essere installato una volta che hai npm. Non tratterò questo aspetto, poiché è piuttosto semplice.

A titolo informativo, per quanto ne so (FWIW AFAICT), nessuno di questi strumenti è obbligatorio. Vedo avvisi durante vari passaggi successivi riguardo alla loro assenza, ma nulla sembra esplodere.

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

Infine dobbiamo riconfigurare leggermente il nostro ambiente di sviluppo per connetterci correttamente a PostgreSQL. Di default tenta di utilizzare un socket Unix, che non viene esposto dal nostro container.

Per risolvere questo problema devi modificare config/database.yml. Fondamentalmente, ovunque vedi:

adapter: postgresql

Sostituiscilo con:

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

L’aggiunta di host fa sì che Discourse non utilizzi un socket, mentre username e password fanno sì che Discourse si connetta utilizzando l’utente predefinito del database di Discourse e la password che abbiamo impostato sopra.

Ho dovuto apportare questa modifica tre volte in config/database.yml: una volta sotto development, poi sotto test e infine sotto profile. Per far funzionare la suite di test ho dovuto anche apportare una modifica simile in spec/fixtures/multisite/two_dbs.yml.

Eccoci qui…

Bene, mettiamoci al lavoro! In una finestra avvia il tuo ambiente di sviluppo utilizzando docker-compose:

docker-compose up

In una seconda finestra eseguiamo i passaggi di configurazione del database:

bundle exec rake db:create

Assumendo che abbia funzionato, ora puoi riprendere dal punto appropriato nella guida per macOS basata su brew.

Quando hai finito di lavorare, ferma docker-compose e puoi mettere via il tuo ambiente di sviluppo fino alla prossima volta.

Se vuoi eliminare permanentemente il database e i contenuti di Redis, esegui semplicemente docker-compose down -v per cancellare i volumi persistenti insieme ai container stessi. Ma senza il flag -v, docker-compose down manterrà il tuo database tra le sessioni di sviluppo.

I test passano?

Il mio setup ha fallito due casi di test:

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

Per me il primo sembra indicare che pngquant funziona un po’ meglio del previsto. Non sono sicuro del perché ciò rappresenti un fallimento. Nemmeno il secondo lo capisco. Ma questo mi sembra sensato.

Buon hacking!

5 Mi Piace