chore: CI/CD (#98)

* Add coderabbit config

* Add basic CICD

* Add cabal update step

* Try using devenv script

* Typo

* Update

* Didnt save this one...

* Cache?

* Fix test

* Update

* Remove tests

* Update

* Attempt to fix install

* Trigger CI

* Update

* Retrigger

* Update

* Retrigger

* Always run cabal update

---------

Co-authored-by: NickSeagull <git@nickseagull.dev>
This commit is contained in:
Nick Seagull 2024-07-30 12:12:25 +01:00 committed by GitHub
parent b410aa7802
commit 9dae30ae70
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 249 additions and 160 deletions

95
.coderabbit.yaml Normal file
View File

@ -0,0 +1,95 @@
language: en-US
tone_instructions: >-
You must use a respectful, but commanding tone, as if you were impersonating an AI deity.
early_access: true
enable_free_tier: true
reviews:
profile: assertive
request_changes_workflow: true
high_level_summary: true
high_level_summary_placeholder: '@coderabbitai summary'
auto_title_placeholder: '@coderabbitai'
review_status: true
poem: true
collapse_walkthrough: false
sequence_diagrams: true
path_filters: []
path_instructions:
- path: ".hlint.yaml"
instructions: |
Ignore this file
- path: "*.hs"
instructions: |
Remember that this is a NeoHaskell file. NeoHaskell is a
Haskell dialect that is inspired by Elm, therefore the
Elm style and conventions should be followed. Also,
Elm core libs are available, and the Haskell Prelude is
ignored, as the NoImplicitPrelude extension is enabled.
abort_on_close: true
auto_review:
enabled: true
auto_incremental_review: true
ignore_title_keywords: []
labels: []
drafts: false
base_branches: []
tools:
shellcheck:
enabled: true
ruff:
enabled: false
markdownlint:
enabled: true
github-checks:
enabled: true
timeout_ms: 90000
languagetool:
enabled: true
enabled_only: false
level: default
enabled_rules: []
disabled_rules:
- EN_UNPAIRED_BRACKETS
enabled_categories: []
disabled_categories:
- TYPOS
- TYPOGRAPHY
- CASING
biome:
enabled: true
hadolint:
enabled: true
swiftlint:
enabled: true
phpstan:
enabled: true
level: default
golangci-lint:
enabled: true
yamllint:
enabled: true
gitleaks:
enabled: true
checkov:
enabled: true
detekt:
enabled: true
eslint:
enabled: true
ast-grep:
packages: []
rule_dirs: []
util_dirs: []
essential_rules: true
chat:
auto_reply: true
knowledge_base:
opt_out: false
learnings:
scope: global
issues:
scope: global
jira:
project_keys: []
linear:
team_keys: []

57
.github/workflows/test.yml vendored Normal file
View File

@ -0,0 +1,57 @@
name: "Test"
on:
pull_request:
branches:
- main
push:
branches:
- main
defaults:
run:
shell: devenv shell bash -- -e {0}
jobs:
tests:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v26
- uses: cachix/cachix-action@v14
with:
name: devenv
- name: Install devenv.sh
shell: sh
run: nix profile install nixpkgs#devenv
- name: Cache Cabal
id: cache
uses: actions/cache@v4
with:
path: |
~/.cabal
dist-newstyle
.devenv/profile
key: ${{ runner.os }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/*.lock') }}-1 # modify the key if the cache is not working as expected
- name: Update Cabal Hackage list
# if: steps.cache.outputs.cache-hit != 'true'
run: run-update
# - name: Build the devenv shell and run any pre-commit hooks
# run: devenv test
- name: Build the project
run: run-build
- name: Run core tests
run: run-core-tests
- name: Run CLI tests
run: run-cli-tests

View File

@ -69,9 +69,9 @@ update message model =
ProjectFileParsed projectDefinition ->
(model {project = Just projectDefinition}, Command.none)
BuildStarted ->
(model {status = "Build Started"}, Command.none)
(model {status = "Build Started!"}, Command.none)
BuildFailed _ ->
(model {status = "Build Failed"}, Command.none)
(model {status = "Build Failed!"}, Command.none)
view :: Model -> Text
view m =

View File

@ -1,4 +1,6 @@
module Main (main) where
import Core
main :: IO ()
main = putStrLn "Test suite not yet implemented."
main = print "Test suite not yet implemented."

View File

@ -1,38 +1,39 @@
-- | Fast immutable arrays. The elements in an array must have the same type.
module Array (
-- * Arrays
Array,
module Array
( -- * Arrays
Array,
-- * Creation
empty,
initialize,
repeat,
fromLinkedList,
-- * Creation
empty,
initialize,
repeat,
fromLinkedList,
-- * Query
isEmpty,
length,
get,
-- * Query
isEmpty,
length,
get,
-- * Manipulate
set,
push,
append,
slice,
-- * Manipulate
set,
push,
append,
slice,
-- * LinkedLists
toLinkedList,
toIndexedLinkedList,
-- * LinkedLists
toLinkedList,
toIndexedLinkedList,
-- * Transform
map,
indexedMap,
foldr,
foldl,
takeIf,
flatMap,
forEach,
) where
-- * Transform
map,
indexedMap,
foldr,
foldl,
takeIf,
flatMap,
forEach,
)
where
import Basics
import Data.Foldable qualified
@ -42,28 +43,30 @@ import GHC.IsList qualified as GHC
import LinkedList (LinkedList)
import LinkedList qualified
import Maybe (Maybe (..))
import Test.QuickCheck qualified as QuickCheck
import Tuple qualified
import Prelude qualified
-- | Representation of fast immutable arrays. You can create arrays of integers
-- (@Array Int@) or strings (@Array String@) or any other type of value you can
-- dream up.
newtype Array a = Array (Data.Vector.Vector a)
deriving (Prelude.Eq, Prelude.Show)
instance (QuickCheck.Arbitrary a) => QuickCheck.Arbitrary (Array a) where
arbitrary = do
list <- QuickCheck.arbitrary
pure (fromLinkedList list)
instance GHC.IsList (Array a) where
type Item (Array a) = a
fromList = Basics.fromList
toList = toLinkedList
-- | Helper function to unwrap an array
unwrap :: Array a -> Data.Vector.Vector a
unwrap (Array v) = v
-- | Return an empty array.
--
-- > length empty == 0
@ -71,14 +74,12 @@ empty :: Array a
empty =
Array Data.Vector.empty
-- | Determine if an array is empty.
--
-- > isEmpty empty == True
isEmpty :: Array a -> Bool
isEmpty = unwrap .> Data.Vector.null
-- | Return the length of an array.
--
-- > length (fromLinkedList [1,2,3]) == 3
@ -88,7 +89,6 @@ length =
.> Data.Vector.length
.> Prelude.fromIntegral
-- | Initialize an array. @initialize n f@ creates an array of length @n@ with
-- the element at index @i@ initialized to the result of @(f i)@.
--
@ -102,7 +102,6 @@ initialize n f =
(Prelude.fromIntegral n)
(Prelude.fromIntegral .> f)
-- | Creates an array with a given length, filled with a default element.
--
-- > repeat 5 0 == fromLinkedList [0,0,0,0,0]
@ -114,13 +113,11 @@ repeat n element =
Array
<| Data.Vector.replicate (Prelude.fromIntegral n) element
-- | Create an array from a 'LinkedList'.
fromLinkedList :: LinkedList a -> Array a
fromLinkedList =
Data.Vector.fromList .> Array
-- | Return @Just@ the element at the index or @Nothing@ if the index is out of range.
--
-- > get 0 (fromLinkedList [0,1,2]) == Just 0
@ -131,7 +128,6 @@ get :: Int -> Array a -> Maybe a
get i array =
unwrap array !? Prelude.fromIntegral i
-- | Set the element at a particular index. Returns an updated array.
--
-- If the index is out of range, the array is unaltered.
@ -139,13 +135,12 @@ get i array =
-- > set 1 7 (fromLinkedList [1,2,3]) == fromLinkedList [1,7,3]
set :: Int -> a -> Array a -> Array a
set i value array = Array result
where
len = length array
vector = unwrap array
result
| 0 <= i && i < len = vector Data.Vector.// [(Prelude.fromIntegral i, value)]
| otherwise = vector
where
len = length array
vector = unwrap array
result
| 0 <= i && i < len = vector Data.Vector.// [(Prelude.fromIntegral i, value)]
| otherwise = vector
-- | Push an element onto the end of an array.
--
@ -154,14 +149,12 @@ push :: a -> Array a -> Array a
push a (Array vector) =
Array (Data.Vector.snoc vector a)
-- | Create a list of elements from an array.
--
-- > toLinkedList (fromLinkedList [3,5,8]) == [3,5,8]
toLinkedList :: Array a -> LinkedList a
toLinkedList = unwrap .> Data.Vector.toList
-- | Create an indexed list from an array. Each element of the array will be
-- paired with its index.
--
@ -173,14 +166,12 @@ toIndexedLinkedList =
.> Data.Vector.toList
.> LinkedList.map (Tuple.mapFirst Prelude.fromIntegral)
-- | Reduce an array from the right. Read @foldr@ as fold from the right.
--
-- > foldr (+) 0 (repeat 3 5) == 15
foldr :: (a -> b -> b) -> b -> Array a -> b
foldr f value array = Prelude.foldr f value (unwrap array)
-- | Reduce an array from the left. Read @foldl@ as fold from the left.
--
-- > foldl (:) [] (fromLinkedList [1,2,3]) == [3,2,1]
@ -188,7 +179,6 @@ foldl :: (a -> b -> b) -> b -> Array a -> b
foldl f value array =
Data.Foldable.foldl' (\a b -> f b a) value (unwrap array)
-- | Keep elements that pass the test.
--
-- > takeIf isEven (fromLinkedList [1,2,3,4,5,6]) == (fromLinkedList [2,4,6])
@ -196,7 +186,6 @@ takeIf :: (a -> Bool) -> Array a -> Array a
takeIf f (Array vector) =
Array (Data.Vector.filter f vector)
-- | Apply a function on every element in an array.
--
-- > map sqrt (fromLinkedList [1,4,9]) == fromLinkedList [1,2,3]
@ -204,7 +193,6 @@ map :: (a -> b) -> Array a -> Array b
map f (Array vector) =
Array (Data.Vector.map f vector)
-- | Apply a function on every element with its index as first argument.
--
-- > indexedMap (*) (fromLinkedList [5,5,5]) == fromLinkedList [0,5,10]
@ -212,7 +200,6 @@ indexedMap :: (Int -> a -> b) -> Array a -> Array b
indexedMap f (Array vector) =
Array (Data.Vector.imap (Prelude.fromIntegral .> f) vector)
-- | Append two arrays to a new one.
--
-- > append (repeat 2 42) (repeat 3 81) == fromLinkedList [42,42,81,81,81]
@ -220,7 +207,6 @@ append :: Array a -> Array a -> Array a
append (Array first) (Array second) =
Array (first ++ second)
-- | Get a sub-section of an array: @(slice start end array)@. The @start@ is a
-- zero-based index where we will start our slice. The @end@ is a zero-based index
-- that indicates the end of the slice. The slice extracts up to but not including
@ -241,19 +227,18 @@ slice :: Int -> Int -> Array a -> Array a
slice from to (Array vector)
| sliceLen <= 0 = empty
| otherwise = Array <| Data.Vector.slice from' sliceLen vector
where
len = Data.Vector.length vector
handleNegative value
| value < 0 = len + value
| otherwise = value
normalize =
Prelude.fromIntegral
.> handleNegative
.> clamp 0 len
from' = normalize from
to' = normalize to
sliceLen = to' - from'
where
len = Data.Vector.length vector
handleNegative value
| value < 0 = len + value
| otherwise = value
normalize =
Prelude.fromIntegral
.> handleNegative
.> clamp 0 len
from' = normalize from
to' = normalize to
sliceLen = to' - from'
-- | Applies a function to each element of an array and flattens the resulting arrays into a single array.
--
@ -266,33 +251,7 @@ slice from to (Array vector)
-- element of `array` using the `map` function. Then, it flattens the resulting arrays into a single array
-- using the `foldr` function with the `append` function as the folding operation and `empty` as the initial
-- value. The resulting array is then returned.
--
-- The `flatMap` function has the following properties:
--
-- prop> flatMap f empty == empty
-- prop> flatMap f (singleton x) == f x
-- prop> flatMap f (fromLinkedList xs) == concatMap f xs
--
-- where `empty` is the empty array, `singleton` creates an array with a single element, and `fromLinkedList` creates
-- an array from a list of elements.
--
-- Examples:
--
-- >>> let array = fromLinkedList [1, 2, 3]
-- >>> let f x = fromLinkedList [x, x + 1]
-- >>> flatMap f array
-- [1, 2, 2, 3, 3, 4]
--
-- >>> let array = fromLinkedList ["Hello", "World"]
-- >>> let f x = fromLinkedList (words x)
-- >>> flatMap f array
-- ["Hello", "World"]
--
-- >>> let array = fromLinkedList [1, 2, 3]
-- >>> let f x = fromLinkedList [x * 2]
-- >>> flatMap f array
-- [2, 4, 6]
--
-- This function is commonly used in functional programming to apply a function to each element of a nested
-- data structure and flatten the resulting structure into a single level.
flatMap ::
@ -307,15 +266,7 @@ flatMap f array =
|> map f
|> foldr append empty
-- | Emulates a foreach-loop like in other languages.
--
-- Example:
--
-- >>> forEach [1, 2, 3] (\x -> putStrLn (show x))
-- 1
-- 2
-- 3
forEach :: forall (element :: Type). (element -> IO ()) -> Array element -> IO ()
forEach callback self =
Data.Foldable.traverse_ callback (unwrap self)

View File

@ -1,34 +1,27 @@
-- | This library fills a bunch of important niches in Elm. A Maybe can help you with optional arguments, error handling, and records with optional fields.
module Maybe (
-- * Definition
Maybe (..),
module Maybe
( -- * Definition
Maybe (..),
-- * Common Helpers
withDefault,
map,
-- * Common Helpers
withDefault,
map,
-- * Chaining Maybes
andThen,
getOrDie,
) where
-- * Chaining Maybes
andThen,
getOrDie,
)
where
import Basics
import Data.Maybe (Maybe (..), fromMaybe)
import Mappable qualified
import Thenable qualified
-- | Provide a default value, turning an optional value into a normal
-- value. This comes in handy when paired with functions like
-- 'Dict.get' which gives back a @Maybe@.
--
-- > >>> withDefault 100 (Just 42)
-- > 42
-- > >>> withDefault 100 Nothing
-- > 100
-- > >>> withDefault "unknown" (Dict.get "Tom" Dict.empty)
-- > "unknown"
--
-- __Note:__ This can be overused! Many cases are better handled by a @case@
-- expression. And if you end up using @withDefault@ a lot, it can be a good sign
-- that a [custom type](https://guide.elm-lang.org/types/custom_types.html)
@ -37,22 +30,11 @@ withDefault :: a -> Maybe a -> a
withDefault =
Data.Maybe.fromMaybe
-- | Transform a @Maybe@ value with a given function:
--
-- > >>> map sqrt (Just 9)
-- > Just 3
-- > >>> map sqrt Nothing
-- > Nothing
-- > >>> map sqrt (String.toFloat "9")
-- > Just 3
-- > >>> map sqrt (String.toFloat "x")
-- > Nothing
-- | Transform a @Maybe@ value with a function:
map :: (a -> b) -> Maybe a -> Maybe b
map =
Mappable.map
-- | Chain together many computations that may fail. It is helpful to see an
-- equivalent definition:
--
@ -89,7 +71,6 @@ andThen :: (a -> Maybe b) -> Maybe a -> Maybe b
andThen =
Thenable.andThen
-- | Attempts to retrieve the value from a @Maybe@. If the @Maybe@ is @Nothing@,
-- the application will crash abruptly.
getOrDie :: Maybe a -> a
@ -99,4 +80,4 @@ getOrDie maybe =
value
Nothing ->
dieWith "Maybe.getOrDie: Got Nothing"
{-# INLINE getOrDie #-}
{-# INLINE getOrDie #-}

View File

@ -27,25 +27,11 @@ newtype Unknown = Unknown Data.Dynamic.Dynamic
type Convertible value = Typeable value
-- | Convert a value of any type to 'Unknown'.
--
-- >>> fromValue 42
-- Unknown (Data.Dynamic.toDyn 42)
--
-- >>> fromValue "hello"
-- Unknown (Data.Dynamic.toDyn "hello")
fromValue :: (Typeable value) => value -> Unknown
fromValue value =
Unknown (Data.Dynamic.toDyn value)
-- | Convert an 'Unknown' value back to its original type, if possible.
--
-- >>> let unknown = fromValue 42
-- >>> toValue unknown :: Maybe Int
-- Just 42
--
-- >>> let unknown = fromValue "hello"
-- >>> toValue unknown :: Maybe Int
-- Nothing
toValue :: (Typeable value) => Unknown -> Maybe value
toValue (Unknown dynamic) =
Data.Dynamic.fromDynamic dynamic

View File

@ -37,6 +37,8 @@ common common_cfg
large-anon,
pretty-simple,
unagi-chan,
QuickCheck,
quickcheck-instances,
template-haskell
default-extensions:
ApplicativeDo

View File

@ -1,4 +1,6 @@
module Main (main) where
import Core
main :: IO ()
main = putStrLn "Test suite not yet implemented."
main = print "Test suite not yet implemented."

View File

@ -3,11 +3,11 @@
"devenv": {
"locked": {
"dir": "src/modules",
"lastModified": 1720853497,
"lastModified": 1722019186,
"owner": "cachix",
"repo": "devenv",
"rev": "7f569a0f2473b9f6000fd9e4c32511fd1b0d37c1",
"treeHash": "4d452ecc8223834e39d507f9ea92308f007ee05d",
"rev": "075c114280751f956335333179304d14ae01aedc",
"treeHash": "0e550d934a0d3f6248accff4c019c013e8e237e6",
"type": "github"
},
"original": {
@ -88,11 +88,11 @@
},
"nixpkgs-stable_2": {
"locked": {
"lastModified": 1720954236,
"lastModified": 1722087241,
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "53e81e790209e41f0c1efa9ff26ff2fd7ab35e27",
"treeHash": "ca1f1273cf201da604f7c704535d4b7fac62cdb2",
"rev": "8c50662509100d53229d4be607f1a3a31157fa12",
"treeHash": "cbc560aaf05dfc49bde55e55f603d7245515b13a",
"type": "github"
},
"original": {

View File

@ -9,10 +9,23 @@
git
ghcid
haskellPackages.implicit-hie
haskellPackages.doctest
];
# https://devenv.sh/scripts/
scripts.watch.exec = "ghcid --command=cabal repl $1";
scripts = {
run-watch.exec = "ghcid --command=cabal repl $1";
run-build.exec = "cabal build all";
run-update.exec = "cabal update";
run-cli.exec = "cabal run nhcli -- $@";
run-core-tests.exec = ''
cabal repl nhcore --with-compiler=doctest && \
cabal test nhcore
'';
run-cli-tests.exec = ''
cabal test nhcli
'';
};
enterShell = ''
gen-hie > hie.yaml