Skip to content

Commit

Permalink
Update stack resolver to lts-22.20 (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
vladimirlogachev authored May 23, 2024
1 parent 31a731a commit 09187b5
Show file tree
Hide file tree
Showing 12 changed files with 113 additions and 123 deletions.
26 changes: 11 additions & 15 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ on:
# "synchronize" means new commits pushed to the HEAD of the pull request branch
- synchronize

permissions:
packages: write

env:
REGISTRY: ghcr.io
# What tags does:
Expand Down Expand Up @@ -44,20 +47,13 @@ jobs:
cache-dependency-path: frontend/package-lock.json

- name: Backend Build and test
uses: freckle/stack-action@v4
uses: freckle/stack-action@v5
id: stack
with:
working-directory: backend
fast: false
# Temporarily disabled
pedantic: false
test: true
cache-prefix: lts-20.18
stack-arguments: --copy-bins

- uses: freckle/weeder-action@v2
with:
ghc-version: ${{ steps.stack.outputs.compiler-version }}
cache-prefix: lts-22.20
stack-build-arguments: --pedantic --copy-bins --local-bin-path target

- uses: haskell/actions/hlint-setup@v2

Expand All @@ -67,7 +63,8 @@ jobs:
run: npm install -g elm-format

- name: Codegen
run: ${{ steps.stack.outputs.local-bin-path }}/codegen
working-directory: backend
run: ./target/codegen

- name: Setup Elm
uses: jorelali/setup-elm@v5
Expand Down Expand Up @@ -108,15 +105,14 @@ jobs:
id: meta-backend
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/VladimirLogachev/servant-to-elm-example-backend
images: ${{ env.REGISTRY }}/haskell-to-elm/servant-to-elm-example-backend
tags: ${{ env.TAGS }}

- name: Build and push Docker image (backend)
uses: docker/build-push-action@v4
with:
context: backend
context: backend/target
file: backend/Dockerfile
build-contexts: bin_path_context=${{ steps.stack.outputs.local-bin-path }}
push: true
tags: ${{ steps.meta-backend.outputs.tags}}
labels: ${{ steps.meta-backend.outputs.labels}}
Expand All @@ -127,7 +123,7 @@ jobs:
id: meta-frontend
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/VladimirLogachev/servant-to-elm-example-frontend
images: ${{ env.REGISTRY }}/haskell-to-elm/servant-to-elm-example-frontend
tags: ${{ env.TAGS }}

- name: Build and push Docker image (frontend)
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# servant-to-elm-example

![example workflow](https://github.com/vladimirlogachev/servant-to-elm-example/actions/workflows/build.yml/badge.svg)
![build status](https://github.com/haskell-to-elm/servant-to-elm-example/actions/workflows/build.yml/badge.svg)

<img src="screenshot.png" alt="servant-to-elm-example" width="500"/>

Expand Down Expand Up @@ -82,4 +82,4 @@ What's cool here is that [servant-to-elm] does the job of generating types and d
[stack]: https://docs.haskellstack.org/en/stable/README/#how-to-install
[elm]: https://guide.elm-lang.org/install/elm.html
[servant]: https://www.servant.dev/
[servant-to-elm]: https://github.com/folq/servant-to-elm
[servant-to-elm]: https://github.com/haskell-to-elm/servant-to-elm
4 changes: 2 additions & 2 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ ENV LANG=C.UTF-8
# libtinfo5 makes the binary ever run
RUN apt-get update && apt-get install -y ca-certificates libtinfo5 && update-ca-certificates

COPY --from=bin_path_context server /app/server
COPY server /app/server

WORKDIR /app

EXPOSE 8000

CMD ["/app/server"]
CMD ["/app/server"]
55 changes: 11 additions & 44 deletions backend/backend.cabal
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
cabal-version: 1.12

-- This file has been generated from package.yaml by hpack version 0.35.1.
-- This file has been generated from package.yaml by hpack version 0.36.0.
--
-- see: https://github.com/sol/hpack

name: backend
version: 0.1.0.0
description: Fullstack web app, an example of servant-to-elm
homepage: https://github.com/VladimirLogachev/servant-to-elm-example#readme
bug-reports: https://github.com/VladimirLogachev/servant-to-elm-example/issues
homepage: https://github.com/haskell-to-elm/servant-to-elm-example#readme
bug-reports: https://github.com/haskell-to-elm/servant-to-elm-example/issues
author: Vladimir Logachev
maintainer: logachev.dev@ya.ru
maintainer: vladimir@logachev.dev
copyright: 2022 Vladimir Logachev
license: BSD3
license-file: LICENSE
build-type: Simple

source-repository head
type: git
location: https://github.com/VladimirLogachev/servant-to-elm-example
location: https://github.com/haskell-to-elm/servant-to-elm-example

library
exposed-modules:
Expand All @@ -36,24 +36,13 @@ library
NoPolyKinds
DataKinds
DeriveAnyClass
DeriveGeneric
DerivingStrategies
DerivingVia
DuplicateRecordFields
FlexibleContexts
FlexibleInstances
ImportQualifiedPost
LambdaCase
MultiParamTypeClasses
NamedFieldPuns
RankNTypes
NoMonomorphismRestriction
OverloadedRecordDot
OverloadedStrings
RankNTypes
TupleSections
TypeApplications
TypeOperators
ghc-options: -j8 +RTS -A32m -RTS -O2 -haddock -Weverything -Wno-all-missed-specialisations -Wno-missed-specialisations -Wno-missing-local-signatures -Wno-implicit-prelude -Wno-missing-export-lists -Wno-unsafe -Wno-safe -Wno-unused-packages -Wno-missing-kind-signatures -Wno-ambiguous-fields -Wno-missing-safe-haskell-mode -Wno-missing-import-lists -Wno-prepositive-qualified-module
ghc-options: -j8 +RTS -A32m -RTS -O2 -haddock -Weverything -Wno-implicit-prelude -Wno-all-missed-specialisations -Wno-missed-specialisations -Wno-missing-local-signatures -Wno-missing-export-lists -Wno-unsafe -Wno-unused-packages -Wno-missing-kind-signatures -Wno-missing-safe-haskell-mode -Wno-missing-import-lists
build-depends:
aeson
, base >=4.7 && <5
Expand Down Expand Up @@ -85,24 +74,13 @@ executable codegen
NoPolyKinds
DataKinds
DeriveAnyClass
DeriveGeneric
DerivingStrategies
DerivingVia
DuplicateRecordFields
FlexibleContexts
FlexibleInstances
ImportQualifiedPost
LambdaCase
MultiParamTypeClasses
NamedFieldPuns
RankNTypes
NoMonomorphismRestriction
OverloadedRecordDot
OverloadedStrings
RankNTypes
TupleSections
TypeApplications
TypeOperators
ghc-options: -j8 +RTS -A32m -RTS -O2 -haddock -Weverything -Wno-all-missed-specialisations -Wno-missed-specialisations -Wno-missing-local-signatures -Wno-implicit-prelude -Wno-missing-export-lists -Wno-unsafe -Wno-safe -Wno-unused-packages -Wno-missing-kind-signatures -Wno-ambiguous-fields -Wno-missing-safe-haskell-mode -Wno-missing-import-lists -Wno-prepositive-qualified-module -threaded -rtsopts -with-rtsopts=-N
ghc-options: -j8 +RTS -A32m -RTS -O2 -haddock -Weverything -Wno-implicit-prelude -Wno-all-missed-specialisations -Wno-missed-specialisations -Wno-missing-local-signatures -Wno-missing-export-lists -Wno-unsafe -Wno-unused-packages -Wno-missing-kind-signatures -Wno-missing-safe-haskell-mode -Wno-missing-import-lists -threaded -rtsopts -with-rtsopts=-N
build-depends:
aeson
, backend
Expand Down Expand Up @@ -135,24 +113,13 @@ executable server
NoPolyKinds
DataKinds
DeriveAnyClass
DeriveGeneric
DerivingStrategies
DerivingVia
DuplicateRecordFields
FlexibleContexts
FlexibleInstances
ImportQualifiedPost
LambdaCase
MultiParamTypeClasses
NamedFieldPuns
RankNTypes
NoMonomorphismRestriction
OverloadedRecordDot
OverloadedStrings
RankNTypes
TupleSections
TypeApplications
TypeOperators
ghc-options: -j8 +RTS -A32m -RTS -O2 -haddock -Weverything -Wno-all-missed-specialisations -Wno-missed-specialisations -Wno-missing-local-signatures -Wno-implicit-prelude -Wno-missing-export-lists -Wno-unsafe -Wno-safe -Wno-unused-packages -Wno-missing-kind-signatures -Wno-ambiguous-fields -Wno-missing-safe-haskell-mode -Wno-missing-import-lists -Wno-prepositive-qualified-module -threaded -rtsopts -with-rtsopts=-N
ghc-options: -j8 +RTS -A32m -RTS -O2 -haddock -Weverything -Wno-implicit-prelude -Wno-all-missed-specialisations -Wno-missed-specialisations -Wno-missing-local-signatures -Wno-missing-export-lists -Wno-unsafe -Wno-unused-packages -Wno-missing-kind-signatures -Wno-missing-safe-haskell-mode -Wno-missing-import-lists -threaded -rtsopts -with-rtsopts=-N
build-depends:
aeson
, backend
Expand Down
70 changes: 38 additions & 32 deletions backend/package.yaml
Original file line number Diff line number Diff line change
@@ -1,72 +1,78 @@
name: backend
version: 0.1.0.0
github: "VladimirLogachev/servant-to-elm-example"
github: "haskell-to-elm/servant-to-elm-example"
license: BSD3
author: "Vladimir Logachev"
maintainer: "logachev.dev@ya.ru"
maintainer: "vladimir@logachev.dev"
copyright: "2022 Vladimir Logachev"

description: Fullstack web app, an example of servant-to-elm

# Complete list: https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/control.html
language: GHC2021

default-extensions:
- NoPolyKinds # Note: Breaks Elm instances for parametrized types
#
# Note on Monomorphism Restriction:
# - Essence: restricts polymorphism for ALL definitions,
# for which no type signatures are specified and which do not "look like functions" (have no binding to the left of = )
# - Inconvenience: Leads to non-intuitive type inference results - less polymorphic than expected
# - Convenience: increases performance and reduces memory usage
# More details: https://www.youtube.com/watch?v=zpBua3Mql18
# - Present in the Haskell specification, not just GHC
# - Disabled by default in GHC 9.4 (via NoMonomorphismRestriction)
# - But enabled by default in the GHC2021 extension set (via MonomorphismRestriction)
# - Instead of disabling monomorphism restriction with `NoMonomorphismRestriction`,
# we use the enabled `Wmonomorphism-restriction` warning (enabled in `Weverything`),
# and warning forces us to place type signatures where monomorphism restriction applies.
# And in the process of specifying types, we decide whether to leave polymorphism or a specific type there,
# and the behavior will always be as expected.
#
- NoPolyKinds # Note: Breaks Elm instances for parameterized types.
- DataKinds
- DeriveAnyClass
- DeriveGeneric
- DerivingStrategies
- DerivingVia
- DuplicateRecordFields
- FlexibleContexts
- FlexibleInstances
- ImportQualifiedPost
- LambdaCase
- MultiParamTypeClasses
- NamedFieldPuns
- RankNTypes
- NoMonomorphismRestriction
- OverloadedRecordDot
- OverloadedStrings
- RankNTypes
- TupleSections
- TypeApplications
- TypeOperators

ghc-options:
# Use up to 8 cores for compilation https://ghc.gitlab.haskell.org/ghc/doc/users_guide/using.html?highlight=j#ghc-flag--j[%E2%9F%A8n%E2%9F%A9]
- -j8
# Set up rts options for GHC itself https://ghc.gitlab.haskell.org/ghc/doc/users_guide/runtime_control.html#setting-rts-options
# Set up rts options for GHC itself https://ghc.gitlab.haskell.org/ghc/doc/users_guide/runtime_control.html#setting-rts-options
- +RTS -A32m -RTS
# Optimization level:
# -O0 produces a non-optimized binary (significantly reduces Seq performance), but builds fast.
# -O2 produces a heavily optimized binary, but builds ~ 2 times slower.
# Note: Stack reuses existing binaries instead of rebuilding if the source code hasn't changed.
# We build optimized binaries by default and turn optimizations off by `--fast` flag.
# Optimization level:
# -O0 produces a non-optimized binary (significantly reduces Seq performance), but builds fast.
# -O2 produces a heavily optimized binary, but builds ~ 2 times slower.
# Note: Stack reuses existing binaries instead of rebuilding if the source code hasn't changed.
# We build optimized binaries by default and turn optimizations off by `--fast` flag.
- -O2
# Enables local documentation
- -haddock
# Enable all possible warnings as errors (Werror is provided manually in dockerfile for production builds and CI)
- -Weverything
# Intentionally ignore some warnings
# Complains about Prelude
- -Wno-implicit-prelude
# Complains about imported functions
- -Wno-all-missed-specialisations
# Complains about imported functions
- -Wno-missed-specialisations
# Requires signatures for all local definitions
- -Wno-missing-local-signatures
- -Wno-implicit-prelude
# Requires explicitly list exports for all modules
- -Wno-missing-export-lists
# Requires to mark unsafe modules as unsafe
- -Wno-unsafe
- -Wno-safe
# Requires separate lists of dependencies per executable. But can really catch some unused packages.
- -Wno-unused-packages
# Requires kind signature for every newtype.
- -Wno-missing-kind-signatures
# Temporary disabled
- -Wno-ambiguous-fields
# Not sure if it's really useful for us, because some modules are still not "safe haskell".
# A lot of modules modules are still not "safe haskell".
- -Wno-missing-safe-haskell-mode
# Temporarily disabled warnings which need to be fixed eventually
# 1. It is inconvenient to specify imports for all record fields and all value constructors
# We need to find a better way.
# Requires import lists for all record fields and all value constructors
- -Wno-missing-import-lists
# 2. Because of hpack implementation details
- -Wno-prepositive-qualified-module

dependencies:
- base >= 4.7 && < 5
Expand Down
20 changes: 15 additions & 5 deletions backend/src/Database.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module Database where

import Control.Exception (throw, try)
import Control.Monad.Except
import Control.Monad.IO.Class (liftIO)
import Data.Bifunctor (first)
import Data.Foldable (traverse_)
import Data.Int (Int64)
Expand Down Expand Up @@ -40,8 +41,11 @@ initDb conn = do
insertAuthorAndBooks :: NewAuthor -> [NewBook] -> ExceptT ApplicationError IO ()
insertAuthorAndBooks newAuthor books = do
authorId <- insertAuthor conn newAuthor
traverse_ (insertBook conn) $
(\b -> b {author = ExistingAuthorId $ fromIntegral authorId} :: NewBook) <$> books
traverse_
( insertBook conn
. (\b -> NewBook {title = b.title, author = ExistingAuthorId $ fromIntegral authorId, imageUrl = b.imageUrl})
)
books

{- Functions for querying the database.
The reason for separating them from the Server is that they are used
Expand Down Expand Up @@ -87,9 +91,15 @@ insertBook conn NewBook {title, author, imageUrl} =
`catchError` (\e -> rollback >> throwError e)
where
-- "Exclusive" transaction provides the highest isolation level in SQLite.
begin = lift $ execute_ conn "BEGIN EXCLUSIVE TRANSACTION"
commit = lift $ execute_ conn "COMMIT TRANSACTION"
rollback = lift $ execute_ conn "ROLLBACK TRANSACTION"
begin :: ExceptT ApplicationError IO ()
begin = liftIO $ execute_ conn "BEGIN EXCLUSIVE TRANSACTION"

commit :: ExceptT ApplicationError IO ()
commit = liftIO $ execute_ conn "COMMIT TRANSACTION"

rollback :: ExceptT ApplicationError IO ()
rollback = liftIO $ execute_ conn "ROLLBACK TRANSACTION"

transformError (SQLError ErrorConstraint "UNIQUE constraint failed: books.title, books.author_id" _) =
UserReadableError "This author already has a book with such a title. Book title must be unique per author."
transformError (SQLError ErrorConstraint "FOREIGN KEY constraint failed" _) =
Expand Down
4 changes: 3 additions & 1 deletion backend/src/Server.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import Servant
runServer :: IO ()
runServer = do
let port = (8000 :: Int)
let dbfile = ":memory:"
let dbfile = ":memory:" :: String

withConnection dbfile $ \conn -> do
initDb conn
Expand Down Expand Up @@ -57,8 +57,10 @@ server conn =
:<|> search
where
{- Handlers are named similar to functions, generated by servant-to-elm -}
getBooks :: Maybe Text -> Handler [Book]
getBooks = liftIO . queryBooks conn

getAuthors :: Maybe Text -> Handler [Author]
getAuthors = liftIO . queryAuthors conn

postBook :: NewBook -> Handler NoContent
Expand Down
Loading

0 comments on commit 09187b5

Please sign in to comment.