This is the code that runs elm-lang.de. The site elm-lang.de is a community hub for Elm developers in Germany and German speaking countries. It publishes news, events and articles related to the programming language Elm. Also, there is a developer directory where developers can create a profile for themselves and check who else works with Elm in their area. You can use the directory to connect with the community.
Contributions to elm-lang.de are most welcome. There's still a lot of work to do and features to build. So if you want to get your hands dirty, this section is for you.
Contributions can come in many forms. You can create issues if you notice any bugs or problems, that already helps a lot. If you feel like digging in to the code and change things, create a pull request for that. However, for bigger things it is probably a good idea to have a bit of discussion first. GitHub issues are one good way for that. Sending an email is fine, too.
The remainder of this section explains how to work with the code base.
The following prerequisites need to be installed:
- PostgreSQL (Database)
- Stack (Haskell build tool)
- Yarn (front end package manager).
- Docker (not required for development, but to build new versions for production)
- Docker Compose (not required for development, but to build new versions for production)
- entr (a file watcher, this tool is optional but recommended for development)
$ # Download the proper GHC for the project and install it into an isolated location
$ stack setup
$ # Install the dbmigrations executable for PostgreSQL (to manage the db schema)
$ stack install dbmigrations-postgresql
$ # Create the database schema
$ bin/db-recreate.sh
$ # Build the Haskell back end
$ stack build
$ # Download front end dependencies and build the Elm sources
$ yarn install
If you want to sign in via GitHub in you local development environment you also need to follow the instructions given in .github-secret.template.
If you would like to have some test data you can run
psql -d elmlangde < backend/sql/test_data_profiles.sql
so the list of developer profiles is not empty.
During development you will probably want to have bin/watch-all.sh
running all the time. It builds the back end and the front end and watches all relevant files for changes. If a back end source file changes, the back end is rebuild and restarted automatically. If a front end source file changes, the front end is rebuild and a browser reload is triggered.
The webpack dev server runs on localhost:7000
. Back end requests are proxied from localhost:7000/api to the back end by the webpack dev server. If for some reason you want to access the back end separately without relying on the webpack dev server proxy you can do so at localhost:8000
.
bin/backend-build-run.sh
: Builds and starts the back end without watching for file changes.bin/backend-run-web-prod.sh
: Start the backend in a configuration where it will serve the front end assets fromdist
(that is, the production webpack build) instead of serving fromfrontend
. This makes it easier to test the webpack production build locally.bin/watch-backend.sh
: Build and run the back end, watch for back end source file changes and rebuild and restart the back end when a file changes. Does not build or start the front end.bin/watch-backend.sh
: Build and run the back end, watch for back end source file changes and rebuild and restart the back end when a file changes. Does not build or start the front end.npm run build
: Production build for the front end sources.npm start
: Start the webpack-dev server, that is, build the front end and watch for file changes. Does not build or start the back end.bin/db-recreate.sh
: Recreate the db schema. This deletes all data. This can also be used to create the initial db schema before starting the back end the first time on a new host.bin/db-new-migration.sh
: Create a new dbmigrations file in backend/migrations.bin/db-upgrade.sh
: Executes all outstanding database migrations. You usually do not need this script, since the back end does the same on startup.
The webpack dev server runs on localhost:7000
Execute npm run webpack-production
once and start bin/backend-run-web-prod.sh
, then go to http://localhost:8000 to check how the front end assets work when having been built in webpack production mode. Or, run bin/backend-run-web-prod.sh
and npm run watch-webpack-production
in parallel - then the production webpack build will run on every front end asset file change (no automatical browser refresh, though, you'll have to refresh manually).
Some operational aspects can be configured. All configuration is done solely through environment variables.
Variable | Required | Default Value | Comment |
---|---|---|---|
PGHOST |
no | localhost |
The host name or IP of the PostgreSQL server |
PGDB |
no | elmlangde |
The name database of the PostgreSQL database |
PGUSER |
no | elmlangde |
The PostgreSQL user |
PGPASS |
no | elmlangde |
The PostgreSQL password |
The provided default values are okay for development on your local machine. They must not be used in production (see below).
Variable | Required | Default Value | Comment |
---|---|---|---|
HOST | no | HostIPv4 | The default value is https://hackage.haskell.org/package/warp-1.3.7/docs/Network-Wai-Handler-Warp.html#v:HostIPv4, which means "any IPv4 host". Override it with 127.0.0.1 to only accept connections from localhost (might be suitable for development). The default value is okay for production. |
PORT | no | 8000 |
The port on which the app listens to incoming connections. The default value is okay for production, assuming there is an Nginx for TLS termination in front of the app. |
GITHUB_CLIENT_ID | no | - | The client ID required for GitHub OAuth. Without a client ID, signing up and signing in via GitHub will not be available. |
GITHUB_CLIENT_SECRET | no | - | The client secret required for GitHub OAuth. Without a client secret, signing up and signing in via GitHub will not be available. |
GITHUB_REDIRECT_URL | no | https://elm-lang.de/oauth/github | The redirect URL required for GitHub OAuth. It must match the URL the browser uses to connect to the app. The default value is only correct for production. |
SECURE_COOKIES_DISABLED | no | False | If the cookies set by the app have the secure flag set. The setting must only be used in development, so cookies work without HTTPS. |
DEVELOPMENT_MODE | no | False | If this flag is set, static front end assets (like HTML, JS, CSS and images) will be served from /frontend instead of /dist . |
elm-lang.de's production setup uses three Docker containers, elmlangde-postgres (the database), elmlangde-nginx (Nginx, for SSL-Termination) and last but not least elmlangde-app, the Haskell/Servant app which also includes the static front end assets including the compiled Elm code.
The containers have Dockerfiles in their respective directory under docker/
. There is also a docker-compose.yml that describes the connections between the containers.
- Run
bin/container-rebuild-all.sh
to create (or recreate) all three containers. - If you only want to recreate the app container and leave the Nginx and the PostgreSQL containers untouched, use
bin/container-rebuild-app.sh
. - If you only need to recreate the nginx container, use
bin/container-rebuild-nginx.sh
.
This will create the Docker containers locally on your system. When the containers have been build (and tested) and you want to deploy them, they need to be pushed to the registry. Once a new version of a container is in the registry, it can be pulled on the target system and started there, see below.
For the nginx container there is one caveat: The initial deploy requires a rather ugly workaround on the very first deployment to production, to get the letsencrypt certs workflow right. It is documented in docker/nginx/Dockerfile
in the comment beginning with "Install letsencrypt cert".
When one or more of the containers have been created (or recreated) its time to push them to the Docker registry. Right now, they live in Bastian's account at:
- https://cloud.docker.com/app/basti1302/repository/docker/basti1302/elmlangde-postgres
- https://cloud.docker.com/app/basti1302/repository/docker/basti1302/elmlangde-nginx
- https://cloud.docker.com/app/basti1302/repository/docker/basti1302/elmlangde-app
To be able to push the containers to the registry for the first time, a repository has to be created using the Dockerhub web UI. To actually push the containers, use the script bin/container-push-all.sh
or these commands:
docker push basti1302/elmlangde-postgres
docker push basti1302/elmlangde-nginx
docker push basti1302/elmlangde-app
To prepare a target system for the first deployment, you need to:
- Create a user
elmlangde
and grant it sudo privileges (see https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-16-04). git clone
this repository there (prefered location:/opt/elm-lang-de
).- You might want to
chown -R
the repo to userelmlangde
- Run the script
/opt/elm-lang-de/docker/prepare-target-system.sh
on the target machine. This will install Docker and Docker Compose. - Add the following line to
/etc/rc.local
:/opt/elm-lang-de/bin/remote/start.sh
- Copy the file
docker/postgres/postgres-secrets.env
from your local machine (whereelmlangde-postgres
has been build) to/opt/elm-lang-de/docker/postgres/
on the target machine. - Create a file
docker/app/app-secrets.env
in/opt/elm-lang-de/docker/app/
on the target machine. You can use /docker/app/app-secrets.env.template as a template. Enter the client ID and client secret for GitHub OAuth there. See GitHub's authentication guide and GitHub's OAuth apps settings page for more information. Never add the client secret to version control or make it public by other means! - Start the Docker containers via
/opt/elm-lang-de/bin/remote/start.sh
. This will pull the Docker containers and start them. If you get an error message that you could not connect to the Docker daemon- Make sure Docker is running via
systemctl status docker
, - Make sure you have added your username to the docker group by executing
groups
, the output should listdocker
among other groups. If not, add your username now withusermod -aG docker $(whoami)
orusermod -aG docker elmlangde
- Log out and log in again (this might be necessary for the group change to take effect).
- Make sure Docker is running via
The script bin/remote/deploy.sh
is a convenience script that is intended to be executed on production, it will pull the latest app container and deploy it. It will not touch the postgres or nginx containers. bin/deploy-app-prod.sh
is a convenience script intended to run locally that rebuilds the app container, pushes it to the registry and immediately deploys it.
Note: /opt/elm-lang-de/bin/remote/start.sh
just starts the containers with the docker-compose
executable. It will pull the containers that do not exist on the system but it will not update them if they are there. For updates, you'll need to pull the latest container versions explicitly. You can pull all Docker containers with /opt/elm-lang-de/bin/container-pull-all.sh
or pull individual containers with one of the following commands:
docker pull basti1302/elmlangde-postgres
docker pull basti1302/elmlangde-nginx
docker pull basti1302/elmlangde-app