Setup CI
All checks were successful
Haskell CI / build (pull_request) Successful in 3m13s
Haskell CI / test (pull_request) Successful in 2m13s
Haskell CI / fourmolu (pull_request) Successful in 6s
Haskell CI / hlint (pull_request) Successful in 5s

This commit is contained in:
Igor Ranieri 2025-09-27 07:51:09 +00:00
parent f1cb583d0f
commit 7d561cf329
9 changed files with 508 additions and 325 deletions

View file

@ -1,2 +0,0 @@
runs-on: self-hosted

View file

@ -0,0 +1,99 @@
name: Haskell CI
on:
pull_request:
branches:
- dev
- main
push:
branches:
- main
jobs:
build:
runs-on: docker
container:
image: elland/haddock2:latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Check versions
run: |
ghc --version
cabal --version
node --version
- name: Cache Cabal packages
uses: actions/cache@v4
with:
path: |
~/.cabal/packages
~/.cabal/store
dist-newstyle
key: ${{ runner.os }}-haskell-9.10-cabal-${{ hashFiles('**/*.cabal', '**/cabal.project') }}
restore-keys: |
${{ runner.os }}-haskell-9.10-cabal-
- name: Update Cabal package index
run: cabal update
- name: Configure project
run: cabal configure --enable-tests --enable-benchmarks
- name: Build dependencies
run: cabal build --only-dependencies --enable-tests --enable-benchmarks
- name: Build project
run: cabal build --enable-tests --enable-benchmarks
- name: Run documentation build
run: cabal haddock
test:
runs-on: docker
container:
image: elland/haddock2:latest
needs: build
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Cache Cabal packages
uses: actions/cache@v4
with:
path: |
~/.cabal/packages
~/.cabal/store
dist-newstyle
key: ${{ runner.os }}-haskell-9.10-cabal-${{ hashFiles('**/*.cabal', '**/cabal.project') }}
restore-keys: |
${{ runner.os }}-haskell-9.10-cabal-
- name: Update Cabal package index
run: cabal update
- name: Configure project
run: cabal configure --enable-tests --enable-benchmarks
- name: Build dependencies
run: cabal build --only-dependencies --enable-tests --enable-benchmarks
- name: Build project
run: cabal build --enable-tests --enable-benchmarks
- name: Run tests
run: cabal test --test-show-details=direct
fourmolu:
runs-on: docker
container:
image: elland/haddock2:latest
needs: build
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run fourmolu
run: |
find src test app -name "*.hs" -exec fourmolu --check-idempotence {} \; 2>/dev/null || true
find src test app -name "*.hs" -exec fourmolu --mode check {} \;
hlint:
runs-on: docker
container:
image: elland/haddock2:latest
needs: build
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run hlint
run: |
if [ -d src ]; then hlint src/; fi
if [ -d test ]; then hlint test/; fi
if [ -d app ]; then hlint app/; fi

36
Dockerfile Normal file
View file

@ -0,0 +1,36 @@
FROM haskell:9.10.2-bullseye AS builder
RUN apt-get update && apt-get install -y curl git && rm -rf /var/lib/apt/lists/*
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \
apt-get install -y nodejs && \
rm -rf /var/lib/apt/lists/*
RUN cabal update && \
cabal install --install-method=copy --installdir=/usr/local/bin \
fourmolu hlint cabal-gild
WORKDIR /workspace
FROM haskell:9.10.2-bullseye
RUN apt-get update && apt-get install -y \
libgmp10 \
curl \
&& rm -rf /var/lib/apt/lists/*
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \
apt-get install -y nodejs && \
rm -rf /var/lib/apt/lists/*
RUN cabal update
COPY --from=builder /usr/local/bin/cabal /usr/local/bin/
COPY --from=builder /usr/local/bin/fourmolu /usr/local/bin/
COPY --from=builder /usr/local/bin/hlint /usr/local/bin/
COPY --from=builder /usr/local/bin/cabal-gild /usr/local/bin/
WORKDIR /workspace
CMD [ "bash" ]

44
Makefile Normal file
View file

@ -0,0 +1,44 @@
.PHONY: help
help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
.PHONY: build
build: ## Build the project
cabal build
.PHONY: test
test: ## Run tests
cabal test --test-show-details=direct
.PHONY: clean
clean: ## Clean build artifacts
cabal clean
.PHONY: fourmolu
fourmolu: ## Format Haskell code
find . -type f -name "*.hs" ! -path "./dist-newstyle/*" -exec fourmolu -i {} +
.PHONY: fourmolu-check
fourmolu-check: ## Check if code is formatted
find . -type f -name "*.hs" ! -path "./dist-newstyle/*" -exec fourmolu --mode check {} \;
.PHONY: lint
lint: ## Run hlint
hlint src test app
.PHONY: cabal-gild
cabal-gild: ## Format cabal file
cabal-gild --io=haddock2.cabal
.PHONY: format
format: fourmolu cabal-gild ## Run all formatters
.PHONY: check
check: fourmolu-check lint ## Run all checks (CI-style)
.PHONY: ci
ci: build test check ## Run full CI pipeline locally
.PHONY: docs
docs: ## Generate documentation
cabal haddock --haddock-hyperlink-source

View file

@ -46,10 +46,10 @@ test-suite haddock2-test
type: exitcode-stdio-1.0
main-is: Spec.hs
build-depends:
parsec ^>=3.1.18.0,
base >=4.20.1.0,
haddock2:{haddock2-lib},
hspec ^>=2.11.0,
parsec ^>=3.1.18.0,
text ^>=2.1.2,
hs-source-dirs: test

View file

@ -4,7 +4,8 @@ module Lexer (
Token (..),
lexer,
emphasis,
) where
)
where
import Control.Monad (mfilter, void)
import Data.Functor (($>))
@ -17,6 +18,7 @@ import Text.Parsec qualified as Parsec
import Text.Parsec.Pos (updatePosChar)
type Located a = (SourcePos, a)
type LocatedToken = (SourcePos, Token)
type Lexer = Parser [LocatedToken]
@ -67,7 +69,7 @@ located :: Parser a -> Parser (SourcePos, a)
located p = (,) <$> getPosition <*> p
tokenise :: [Parser a] -> Parser [(SourcePos, a)]
tokenise = sequence . map located
tokenise = mapM located
lexer :: String -> Either ParseError [LocatedToken]
lexer = Parsec.runParser lexText initialParserState "input" . Text.pack
@ -144,7 +146,7 @@ delimitedNoTrailing openP closeP openTok = asList <$> delimitedAsTuple (openTok
asList (a, tok, _) = [a, tok]
delimitedSymmetric :: Parser a -> Token -> Token -> Parser [LocatedToken]
delimitedSymmetric s t1 t2 = delimited s s t1 t2
delimitedSymmetric s = delimited s s
eol :: Parser ()
eol = void "\n" <|> void "\r\n" <|> Parsec.eof
@ -173,7 +175,6 @@ anchor = do
x <- located $ between "#" "#" (Anchor <$> anyUntil "#")
pure [x]
moduleNames :: Parser Text
moduleNames = intercalate "." . fmap Text.pack <$> upperId `sepBy1` char '.'
@ -302,6 +303,7 @@ scan ::
Parser Text
scan f initState = do
parserState@State{stateInput = input, statePos = pos} <- Parsec.getParserState
(remaining, finalPos, ct) <- go input initState pos 0
let newState = parserState{stateInput = remaining, statePos = finalPos}
Parsec.setParserState newState $> Text.take ct input

View file

@ -13,7 +13,9 @@ import Text.Parsec.Pos (updatePosChar)
Return everything consumed except for the end pattern itself.
-}
takeUntil :: Text -> Parser Text
takeUntil end_ = Text.dropEnd (Text.length end_) <$> requireEnd (scan p (False, end)) >>= gotSome
takeUntil end_ =
requireEnd (scan p (False, end))
>>= gotSome . Text.dropEnd (Text.length end_)
where
end = Text.unpack end_

View file

@ -9,6 +9,8 @@ module Types (
)
where
import Data.Foldable (fold)
newtype Document = Document
{ meta :: Meta
}
@ -28,6 +30,7 @@ data Since = Since
-- Could have a better type?
type Version = [Int]
type Package = String
data DocMarkup mod id
@ -136,7 +139,7 @@ instance Semigroup (DocMarkup mod id) where
instance Monoid (DocMarkup mod id) where
mempty = DocEmpty
mconcat = foldr (<>) mempty
mconcat = fold
data ModuleLink id = ModuleLink
{ name :: String

View file

@ -1,17 +1,15 @@
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -Wno-orphans #-}
import Test.Hspec
import Data.String (IsString (..))
import Data.Text (Text)
import GHC.Stack
import Identifier (Identifier)
import Lexer
import Parser
import Types
import Data.String (IsString (..))
import Data.Text (Text)
import Test.Hspec
import Text.Parsec.Pos
import GHC.Stack
import Types
main :: IO ()
main = hspec $ do
@ -34,9 +32,9 @@ main = hspec $ do
describe "Parser" do
it "Bold" do
"__bold__" `shouldParseTo` (DocBold (DocString "bold"))
"__bold__" `shouldParseTo` DocBold (DocString "bold")
it "Emphasis" do
"/emphasis/" `shouldParseTo` (DocEmphasis (DocString "emphasis"))
"/emphasis/" `shouldParseTo` DocEmphasis (DocString "emphasis")
------------
-- Tests
@ -57,6 +55,7 @@ modules = do
`shouldLexTo` [ (1, 2, Module "OtherModule.Name")
, (1, 18, Anchor "myAnchor")
]
link :: Expectation
link =
"[link to](http://some.website)"