Rewriting to Haskell–Project Setup

riccardoodone

Riccardo Odone

Posted on March 3, 2020

Rewriting to Haskell–Project Setup

You can keep reading here or jump to my blog to get the full experience, including the wonderful pink, blue and white palette.


This is part of a series:


We decided to go with Servant to rewrite the current Rails API for Stream. We don't really have a strong reason for that. We just like it and we believe it will allow us to do some cool stuff down the line!

Also, we picked Stack because it's what we are used to and seems to be less cryptic than Cabal.

We created the project with the following command:

stack new haskell servant
#     ^ Create a new project..
#         ^ ..in a new haskell/ folder..
#                 ^ ..using the Servant template.
Enter fullscreen mode Exit fullscreen mode

The Servant template creates a dummy application with an hardcoded endpoint and some tests that allow to start playing with code right away.

CI

Stream is already using CircleCI, so we opened the Haskell languge guide and started copy / pasting like there was no tomorrow.

We ended up with the following config.yml file that supports Rails, Elm and Haskell. See inlined comments for more info.

---
version: 2.1

commands:
  install_dependencies:
# ^ Define a reusable command (see invokations below) to install the dependencies needed for Rails and Elm.
    steps:
      - restore_cache:
          name: Restore bundle cache
          key: stream-{{ checksum "Gemfile.lock" }}

      - restore_cache:
          name: Restore yarn cache
          key: stream-yarn-{{ checksum "yarn.lock" }}

      - run: bundle install --path vendor/bundle

      - run: yarn install

      - save_cache:
          name: Store bundle cache
          key: stream-{{ checksum "Gemfile.lock" }}
          paths:
            - vendor/bundle

      - save_cache:
          name: Store yarn cache
          key: stream-yarn-{{ checksum "yarn.lock" }}
          paths:
            - ~/.cache/yarn/v1
            - node_modules

jobs:
  build:
# ^ Test Rails and Elm code.

    working_directory: ~/stream
    docker:
      - image: circleci/ruby:2.4.1-node-browsers
        environment:
          PGHOST: 127.0.0.1
          PGUSER: stream
          RAILS_ENV: test
      - image: circleci/postgres:9.5-alpine
        environment:
          POSTGRES_USER: stream
          POSTGRES_DB: stream_test
          POSTGRES_PASSWORD: ""

    steps:
      - checkout

      - install_dependencies

      # sysconfcpus is a hack for elm-make
      # https://github.com/elm-lang/elm-compiler/issues/1473
      - run: |
          git clone https://github.com/obmarg/libsysconfcpus.git ~/libsysconfcpus
          cd ~/libsysconfcpus
          ./configure
          make
          sudo make install
          cd -

      - run: yarn test

      - run: yarn run elm-format --validate app/javascript/

      - run: sysconfcpus --num 2 yarn run elm-test app/javascript/tests

      - run: dockerize -wait tcp://localhost:5432 -timeout 1m

      - run: cp config/database.yml.example config/database.yml

      - run: bin/rails db:setup

      - run: sysconfcpus --num 2 bin/rspec spec

      - run: bundle exec bundle audit check --update

      - run: bundle exec brakeman --ensure-latest -A -5

      - run: bundle exec bundle outdated || true

  build_haskell:
# ^ Test Haskell code.

    working_directory: ~/stream/haskell

    docker:
      - image: fpco/stack-build:lts

    steps:
      - checkout:
          path: ~/stream

      - restore_cache:
          name: Restore Cached Dependencies
          keys:
            - stream-haskell-{{ checksum "stack.yaml" }}-{{ checksum "package.yaml" }}
            - stream-haskell-{{ checksum "stack.yaml" }}

      - run:
          name: Resolve/Update Dependencies
          command: stack --no-terminal setup

      - run:
          name: Run tests
          command: stack --no-terminal test

      - run:
          name: Install executable
          command: stack --no-terminal install

      - save_cache:
          name: Cache Dependencies
          key: stream-haskell-{{ checksum "stack.yaml" }}-{{ checksum "package.yaml" }}
          paths:
            - "/root/.stack"
            - ".stack-work"

      - store_artifacts:
          path: ~/.local/bin/haskell-exe
          destination: haskell-exe

  deploy:
# ^ Deploy the Rails and Elm application. We will see in a later post how to deploy the Servant application.

    working_directory: ~/stream

    docker:
      - image: circleci/ruby:2.4.1-node-browsers

    steps:
      - checkout

      - install_dependencies

      - run : |
            if [ "${CIRCLE_BRANCH}" == "master" ]; then
              bundle exec cap staging deploy
            elif [ "${CIRCLE_BRANCH}" == "production" ]; then
              bundle exec cap production deploy
            else
              echo "${CIRCLE_BRANCH} is a feature branch so no deploy"
            fi

workflows:
  version: 2
  build_and_deploy:
    jobs:
      - build
      - build_haskell
      - deploy:
          requires:
            - build
            - build_haskell
Enter fullscreen mode Exit fullscreen mode

Get the latest content via email from me personally. Reply with your thoughts. Let's learn from each other. Subscribe to my PinkLetter!

💖 💪 🙅 🚩
riccardoodone
Riccardo Odone

Posted on March 3, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Rewriting to Haskell–Errors
functional Rewriting to Haskell–Errors

May 11, 2020

Rewriting to Haskell–Linting
functional Rewriting to Haskell–Linting

April 20, 2020

Rewriting to Haskell–Testing
functional Rewriting to Haskell–Testing

April 13, 2020