CircleCI2.0でHaskellのテストを実行する

浅井 勇樹
12

この記事は RECRUIT MARKETING PARTNERS Advent Calendar 2017 の投稿記事です。

はじめに

こんにちは。英語学習GでiOSエンジニアをしている浅井です。

趣味レベルですがHaskellを愛好しています。家庭内SlackのBotや、ちょっとしたツール類はHaskellで書くことが多いです。その中で得た知見をブログにしていきたいと思います。

本記事では、新規プロジェクト作成からCircleCIへの投入までを解説します。また、本記事で作成したプロジェクトをGitHubで公開していますので、参考にしていただければと思います。

開発環境について

下記のリンクを参考に開発環境をインストールしましょう。インストール済みであればスキップしてください。

プロジェクト作成&テスト実行

まずは stack new でプロジェクトを作成していきます。プロジェクト名を haskell-circleci-example とします。今回はテストフレームワークとして Hspec を使いたいので、 hspec というテンプレートを指定します。

$ stack new haskell-circleci-example hspec

作成されたプロジェクトには、あらかじめサンプルの実装とテストが含まれています。

実装

ある文字列の先頭及び末尾の余白文字列を取り除きます。

module Data.String.Strip (strip) where

import Data.Char

strip :: String -> String
strip = dropWhile isSpace . reverse . dropWhile isSpace . reverse

テスト

module Data.String.StripSpec (main, spec) where

import Test.Hspec
import Test.QuickCheck

import Data.String.Strip

-- `main` is here so that this module can be run from GHCi on its own. It is
-- not needed for automatic spec discovery.
main :: IO ()
main = hspec spec

spec :: Spec
spec = do
  describe "strip" $ do
    it "removes leading and trailing whitespace" $ do
      strip "\t foo bar\n" `shouldBe` "foo bar"
    it "is idempotent" $ property $
      strip str === strip (strip str)

上記の実装とテストに対して stack test が通ることを確認しておきましょう。

$ stack test

haskell-circleci-example-0.1.0.0: test (suite: haskell-circleci-example-test)

Data.String.Strip
  strip
    removes leading and trailing whitespace
    is idempotent

Finished in 0.0081 seconds
  2 examples, 0 failures

テストが通りましたね👍

CircleCIの設定ファイルを記述

CircleCI 2.0では、ビルドに利用するDockerイメージを好きに選択できます。幾つかの言語はCircleCIによって公式にサポートされていますが、Haskellはサポートされていないようです。以下のDockerイメージを利用することで対処します。

続いて、CircleCIの設定ファイルを記述します。

version: 2
jobs:
  build:
    docker:
      - image: fpco/stack-build:lts-9.14
    working_directory: /home/stackage
    steps:
      - checkout
      - restore_cache:
          key: cache
      - run: stack build --test --only-dependencies --no-terminal --system-ghc
      - save_cache:
          key: cache
          paths:
            - ~/.stack
      - run: stack install --test --no-terminal --system-ghc

CircleCI 2.0では、CLIを用いてローカルでビルドのチェックを行うことも可能です。本番のCircleCIに投入する前に試行錯誤できるので大変ありがたいです。以下のコマンドでビルドを実行します。

$ circleci build

初回はDockerイメージのダウンロードやパッケージの依存関係を解決するために数分かかることがあります。気長に待ちましょう。また、ローカルでのビルドではキャッシュ機構がサポートされていないため restore_cachesave_cache のステップでエラーが出ます。気にせず先に進みましょう。

====>> Spin up Environment
...
====>> Checkout code
...
====>> Restoring Cache
Error: Skipping cache - error checking storage: not supported

Step failed
====>> stack build --test --only-dependencies --no-terminal --system-ghc
...
====>> Saving Cache
Error: Skipping cache - error checking storage: not supported

Step failed
====>> stack install --test --no-terminal --system-ghc
...

Success!

CircleCIへ投入

ここまでくれば、あとはCircleCIに投入するだけです。今回はGitHubにリポジトリを作ったので、CircleCIからそのリポジトリを選択します。

Add Projects CircleCI.png

Langugageの選択で、 Otherを選ぶと Request a language という項目が表示されます。せっかくなので Haskell をリクエストしておきましょう。

ビルドを開始します。

Add Projects CircleCI 2.png

無事に成功しました🚀🚀🚀

最後に

特にハマることなく最後まですすめることができました。CIとしての旨味を活かすために、カバレッジの測定やLintなどやりたいことはまだまだたくさんあります。CircleCI 2.0で新たに追加されたWorkflowを用いて、近々チャレンジしてみようと思います💪