mirror of
https://github.com/anoma/juvix.git
synced 2024-12-17 11:51:42 +03:00
46ab163ca7
Stack LTS 21.6 uses GHC 9.4.5, binaries for HLS are available via ghcup. Changes required: 1. Fix warnings about type level `:` and `[]` used without backticks. 2. Fix warnings about deprecation of builtin `~` - replaced with `import Data.Type.Equality ( type (~) )` in the Prelude 3. SemVer is no longer a monoid 4. `path-io` now contains the `AnyPath` instances we were defining (thanks to Jan) so they can be removed. 5. Added `aeson-better-errors-0.9.1.1` as an extra-dep. The reason it is not part of the resolver is only because it has a strict bound on base which is not compatible with ghc 9.4.5. To work around this I've set: ``` allow-newer: true allow-newer-deps: - aeson-better-errors ``` which relaxed the upper constraint bounds for `aeson-better-errors` only. When the base constraints have been updated we can remove this workaround. 6. Use stack2cabal to generate the cabal.project file and to freeze dependency versions. https://www.stackage.org/lts-21.6/cabal.config now contains the constraint `haskeline installed`, which means that the version of haskeline that is globally installed with GHC 9.4.5 will be used, see: * https://github.com/commercialhaskell/stackage/issues/7002 GHC 9.4.5 comes with haskeline 0.8.2 preinstalled but our configuration contains the source-repository-package for haskeline 0.8.2.1 (required because we're using a fork) so if you try to run` cabal build` you get a conflict. Constraints from cabal imports cannot yet be overridden so it's not possible to get rid of this conflict using the import method. So we need to use stack2cabal with an explicit freeze file instead. 7. Remove `runTempFilePure` as this is unused and depends on `Polysemy.Fresh` in `polysemy-zoo` which is not available in the resolver. It turns out that it's not possible to use the `Fresh` effect in a pure context anyway, so it was not possible to use `runTempFilePure` for its original purpose. 8. We now use https://github.com/benz0li/ghc-musl as the base container for static linux builds, this means we don't need to maintain our own Docker container for this purpose. 9. The PR for the nightly builds is ready https://github.com/anoma/juvix-nightly-builds/pull/2, it should be merged as soon as this PR is merged. Thanks to @benz0li for maintaining https://github.com/benz0li/ghc-musl and (along with @TravisCardwell) for help with building the static binary. * Closes https://github.com/anoma/juvix/issues/2166
138 lines
4.0 KiB
Haskell
138 lines
4.0 KiB
Haskell
module Commands.Init where
|
|
|
|
import Data.Text qualified as Text
|
|
import Data.Versions
|
|
import Data.Yaml (encodeFile)
|
|
import Juvix.Compiler.Pipeline.Package
|
|
import Juvix.Data.Effect.Fail.Extra qualified as Fail
|
|
import Juvix.Extra.Paths
|
|
import Juvix.Prelude
|
|
import Juvix.Prelude.Pretty
|
|
import Text.Megaparsec (Parsec)
|
|
import Text.Megaparsec qualified as P
|
|
import Text.Megaparsec.Char qualified as P
|
|
|
|
type Err = Text
|
|
|
|
parse :: Parsec Void Text a -> Text -> Either Err a
|
|
parse p t = mapLeft ppErr (P.runParser p "<stdin>" t)
|
|
|
|
ppErr :: P.ParseErrorBundle Text Void -> Text
|
|
ppErr = pack . errorBundlePretty
|
|
|
|
init :: forall r. (Members '[Embed IO] r) => Sem r ()
|
|
init = do
|
|
checkNotInProject
|
|
say "✨ Your next Juvix adventure is about to begin! ✨"
|
|
say "I will help you set it up"
|
|
pkg <- getPackage
|
|
say ("creating " <> pack (toFilePath juvixYamlFile))
|
|
embed (encodeFile (toFilePath juvixYamlFile) (rawPackage pkg))
|
|
say "you are all set"
|
|
|
|
checkNotInProject :: forall r. (Members '[Embed IO] r) => Sem r ()
|
|
checkNotInProject =
|
|
whenM (doesFileExist juvixYamlFile) err
|
|
where
|
|
err :: Sem r ()
|
|
err = do
|
|
say "You are already in a Juvix project"
|
|
embed exitFailure
|
|
|
|
getPackage :: forall r. Members '[Embed IO] r => Sem r Package
|
|
getPackage = do
|
|
tproj <- getProjName
|
|
say "Write the version of your project [leave empty for 0.0.0]"
|
|
tversion :: SemVer <- getVersion
|
|
return
|
|
Package
|
|
{ _packageName = tproj,
|
|
_packageVersion = tversion,
|
|
_packageBuildDir = Nothing,
|
|
_packageMain = Nothing,
|
|
_packageDependencies = [defaultStdlibDep DefaultBuildDir]
|
|
}
|
|
|
|
getProjName :: forall r. (Members '[Embed IO] r) => Sem r Text
|
|
getProjName = do
|
|
d <- getDefault
|
|
let defMsg :: Text
|
|
defMsg = case d of
|
|
Nothing -> mempty
|
|
Just d' -> " [leave empty for '" <> d' <> "']"
|
|
say
|
|
( "Write the name of your project"
|
|
<> defMsg
|
|
<> " (lower case letters, numbers and dashes are allowed): "
|
|
)
|
|
readName d
|
|
where
|
|
getDefault :: Sem r (Maybe Text)
|
|
getDefault = runFail $ do
|
|
dir <- map toLower . dropTrailingPathSeparator . toFilePath . dirname <$> getCurrentDir
|
|
Fail.fromRight (parse projectNameParser (pack dir))
|
|
readName :: Maybe Text -> Sem r Text
|
|
readName def = go
|
|
where
|
|
go :: Sem r Text
|
|
go = do
|
|
txt <- embed getLine
|
|
if
|
|
| Text.null txt, Just def' <- def -> return def'
|
|
| otherwise ->
|
|
case parse projectNameParser txt of
|
|
Right p
|
|
| Text.length p <= projextNameMaxLength -> return p
|
|
| otherwise -> do
|
|
say ("The project name cannot exceed " <> prettyText projextNameMaxLength <> " characters")
|
|
retry
|
|
Left err -> do
|
|
say err
|
|
retry
|
|
where
|
|
retry :: Sem r Text
|
|
retry = do
|
|
tryAgain
|
|
go
|
|
|
|
say :: (Members '[Embed IO] r) => Text -> Sem r ()
|
|
say = embed . putStrLn
|
|
|
|
tryAgain :: (Members '[Embed IO] r) => Sem r ()
|
|
tryAgain = say "Please, try again:"
|
|
|
|
getVersion :: forall r. (Members '[Embed IO] r) => Sem r SemVer
|
|
getVersion = do
|
|
txt <- embed getLine
|
|
if
|
|
| Text.null txt -> return defaultVersion
|
|
| otherwise -> case parse semver' txt of
|
|
Right r -> return r
|
|
Left err -> do
|
|
say err
|
|
say "The version must follow the 'Semantic Versioning 2.0.0' specification"
|
|
retry
|
|
where
|
|
retry :: Sem r SemVer
|
|
retry = do
|
|
tryAgain
|
|
getVersion
|
|
|
|
projextNameMaxLength :: Int
|
|
projextNameMaxLength = 100
|
|
|
|
projectNameParser :: Parsec Void Text Text
|
|
projectNameParser = do
|
|
h <- P.satisfy validFirstChar
|
|
t <- P.takeWhileP (Just "project name character") validChar
|
|
P.hspace
|
|
P.eof
|
|
return (Text.cons h t)
|
|
where
|
|
validFirstChar :: Char -> Bool
|
|
validFirstChar c =
|
|
isAscii c
|
|
&& (isLower c || isNumber c)
|
|
validChar :: Char -> Bool
|
|
validChar c = c == '-' || validFirstChar c
|