summoner 1.4.0.0 → 2.0.0.0
raw patch · 105 files changed
+2723/−1258 lines, 105 filesdep +colouristadep +generic-datadep +hspec-hedgehogdep −ansi-terminaldep −generic-derivingdep ~basedep ~containersPVP ok
version bump matches the API change (PVP)
Dependencies added: colourista, generic-data, hspec-hedgehog, validation-selective
Dependencies removed: ansi-terminal, generic-deriving
Dependency ranges changed: base, containers
API changes (from Hackage documentation)
- Summoner.Ansi: Black :: Color
- Summoner.Ansi: Blue :: Color
- Summoner.Ansi: Cyan :: Color
- Summoner.Ansi: Green :: Color
- Summoner.Ansi: Magenta :: Color
- Summoner.Ansi: Red :: Color
- Summoner.Ansi: White :: Color
- Summoner.Ansi: Yellow :: Color
- Summoner.Ansi: beautyPrint :: [IO ()] -> Text -> IO ()
- Summoner.Ansi: blueCode :: String
- Summoner.Ansi: bold :: IO ()
- Summoner.Ansi: boldCode :: String
- Summoner.Ansi: boldText :: Text -> IO ()
- Summoner.Ansi: data Color
- Summoner.Ansi: italic :: IO ()
- Summoner.Ansi: redCode :: String
- Summoner.Ansi: reset :: IO ()
- Summoner.Ansi: resetCode :: String
- Summoner.Ansi: setColor :: Color -> IO ()
- Summoner.CLI: [newOptsOffline] :: NewOpts -> Bool
- Summoner.Config: Config :: !p :- Text -> !p :- Text -> !p :- Text -> !p :- LicenseName -> !p :- [GhcVer] -> !Decision -> !Decision -> !Decision -> !Decision -> !Decision -> !Decision -> !Decision -> !Decision -> !Decision -> !Decision -> !Last CustomPrelude -> ![Text] -> ![Text] -> ![Text] -> !Last Source -> !Last Source -> !Any -> !Map FilePath Source -> ConfigP
- Summoner.Config: [cContributing] :: ConfigP -> !Last Source
- Summoner.Config: [cStylish] :: ConfigP -> !Last Source
- Summoner.Config: instance (Generics.Deriving.Monoid.Internal.GMonoid (p Summoner.Config.:- Data.Text.Internal.Text), Generics.Deriving.Monoid.Internal.GMonoid (p Summoner.Config.:- Summoner.License.LicenseName), Generics.Deriving.Monoid.Internal.GMonoid (p Summoner.Config.:- [Summoner.GhcVer.GhcVer])) => Generics.Deriving.Monoid.Internal.GMonoid (Summoner.Config.ConfigP p)
- Summoner.Config: instance (Generics.Deriving.Semigroup.Internal.GSemigroup (p Summoner.Config.:- Data.Text.Internal.Text), Generics.Deriving.Semigroup.Internal.GSemigroup (p Summoner.Config.:- Summoner.License.LicenseName), Generics.Deriving.Semigroup.Internal.GSemigroup (p Summoner.Config.:- [Summoner.GhcVer.GhcVer])) => Generics.Deriving.Semigroup.Internal.GSemigroup (Summoner.Config.ConfigP p)
- Summoner.Config: instance GHC.Classes.Ord k => Generics.Deriving.Monoid.Internal.GMonoid (Data.Map.Internal.Map k v)
- Summoner.Config: instance GHC.Classes.Ord k => Generics.Deriving.Semigroup.Internal.GSemigroup (Data.Map.Internal.Map k v)
- Summoner.Decision: instance Generics.Deriving.Monoid.Internal.GMonoid Summoner.Decision.Decision
- Summoner.Decision: instance Generics.Deriving.Semigroup.Internal.GSemigroup Summoner.Decision.Decision
- Summoner.GhcVer: Ghc881 :: GhcVer
- Summoner.License: None :: LicenseName
- Summoner.License: cabalLicense :: LicenseName -> Text
- Summoner.Project: fetchSources :: Bool -> Map FilePath Source -> IO [TreeFs]
- Summoner.Settings: [settingsBaseType] :: Settings -> !Text
- Summoner.Settings: [settingsContributing] :: Settings -> !Maybe Text
- Summoner.Settings: [settingsStylish] :: Settings -> !Maybe Text
- Summoner.Source: sourceT :: Key -> TomlCodec Source
+ Summoner.CLI: Config :: ConfigOpts -> Command
+ Summoner.CLI: [newOptsConnectMode] :: NewOpts -> !ConnectMode
+ Summoner.CLI: [newOptsInteractivity] :: NewOpts -> !Interactivity
+ Summoner.CLI: getCustomLicenseText :: LicenseName -> IO License
+ Summoner.CLI: runConfig :: ConfigOpts -> IO ()
+ Summoner.Config: ConfigP :: !p :- Text -> !p :- Text -> !p :- Text -> !p :- LicenseName -> !p :- [GhcVer] -> !Decision -> !Decision -> !Decision -> !Decision -> !Decision -> !Decision -> !Decision -> !Decision -> !Decision -> !Decision -> !Decision -> !Last CustomPrelude -> ![Text] -> ![Text] -> ![Text] -> !Any -> !Map FilePath Source -> ConfigP (p :: Phase)
+ Summoner.Config: [cGhActions] :: ConfigP (p :: Phase) -> !Decision
+ Summoner.Config: guessConfigFromGit :: IO PartialConfig
+ Summoner.Decision: decisionsToBools :: (Decision, Decision) -> (Bool, Bool)
+ Summoner.Decision: promptDecisionToBool :: Decision -> YesNoPrompt -> IO Bool
+ Summoner.Default: defaultConfigFileContent :: Text
+ Summoner.Default: defaultEmail :: Text
+ Summoner.Default: defaultFullName :: Text
+ Summoner.Default: defaultLicenseName :: LicenseName
+ Summoner.Default: defaultOwner :: Text
+ Summoner.GhcVer: Ghc883 :: GhcVer
+ Summoner.License: ISC :: LicenseName
+ Summoner.License: NONE :: LicenseName
+ Summoner.License: fetchLicenseCustom :: LicenseName -> Text -> Text -> IO License
+ Summoner.Mode: Interactive :: Interactivity
+ Summoner.Mode: NonInteractive :: Interactivity
+ Summoner.Mode: Offline :: ConnectMode
+ Summoner.Mode: Online :: ConnectMode
+ Summoner.Mode: data ConnectMode
+ Summoner.Mode: data Interactivity
+ Summoner.Mode: instance GHC.Classes.Eq Summoner.Mode.ConnectMode
+ Summoner.Mode: instance GHC.Classes.Eq Summoner.Mode.Interactivity
+ Summoner.Mode: instance GHC.Show.Show Summoner.Mode.ConnectMode
+ Summoner.Mode: instance GHC.Show.Show Summoner.Mode.Interactivity
+ Summoner.Mode: isNonInteractive :: Interactivity -> Bool
+ Summoner.Mode: isOffline :: ConnectMode -> Bool
+ Summoner.Project: generateProjectInteractive :: ConnectMode -> Text -> Config -> IO ()
+ Summoner.Project: generateProjectNonInteractive :: ConnectMode -> Text -> Config -> IO ()
+ Summoner.Question: doesExistProjectName :: Text -> IO Bool
+ Summoner.Settings: [settingsGhActions] :: Settings -> !Bool
+ Summoner.Source: fetchSources :: ConnectMode -> Map FilePath Source -> IO [TreeFs]
- Summoner.Ansi: boldDefault :: Text -> IO ()
+ Summoner.Ansi: boldDefault :: Text -> Text
- Summoner.CLI: NewOpts :: Text -> Bool -> Bool -> Maybe FilePath -> PartialConfig -> NewOpts
+ Summoner.CLI: NewOpts :: !Text -> !Bool -> !ConnectMode -> !Interactivity -> !Maybe FilePath -> !PartialConfig -> NewOpts
- Summoner.CLI: [newOptsCliConfig] :: NewOpts -> PartialConfig
+ Summoner.CLI: [newOptsCliConfig] :: NewOpts -> !PartialConfig
- Summoner.CLI: [newOptsConfigFile] :: NewOpts -> Maybe FilePath
+ Summoner.CLI: [newOptsConfigFile] :: NewOpts -> !Maybe FilePath
- Summoner.CLI: [newOptsIgnoreFile] :: NewOpts -> Bool
+ Summoner.CLI: [newOptsIgnoreFile] :: NewOpts -> !Bool
- Summoner.CLI: [newOptsProjectName] :: NewOpts -> Text
+ Summoner.CLI: [newOptsProjectName] :: NewOpts -> !Text
- Summoner.Config: [cAppVey] :: ConfigP -> !Decision
+ Summoner.Config: [cAppVey] :: ConfigP (p :: Phase) -> !Decision
- Summoner.Config: [cBench] :: ConfigP -> !Decision
+ Summoner.Config: [cBench] :: ConfigP (p :: Phase) -> !Decision
- Summoner.Config: [cCabal] :: ConfigP -> !Decision
+ Summoner.Config: [cCabal] :: ConfigP (p :: Phase) -> !Decision
- Summoner.Config: [cEmail] :: ConfigP -> !p :- Text
+ Summoner.Config: [cEmail] :: ConfigP (p :: Phase) -> !p :- Text
- Summoner.Config: [cExe] :: ConfigP -> !Decision
+ Summoner.Config: [cExe] :: ConfigP (p :: Phase) -> !Decision
- Summoner.Config: [cExtensions] :: ConfigP -> ![Text]
+ Summoner.Config: [cExtensions] :: ConfigP (p :: Phase) -> ![Text]
- Summoner.Config: [cFiles] :: ConfigP -> !Map FilePath Source
+ Summoner.Config: [cFiles] :: ConfigP (p :: Phase) -> !Map FilePath Source
- Summoner.Config: [cFullName] :: ConfigP -> !p :- Text
+ Summoner.Config: [cFullName] :: ConfigP (p :: Phase) -> !p :- Text
- Summoner.Config: [cGhcOptions] :: ConfigP -> ![Text]
+ Summoner.Config: [cGhcOptions] :: ConfigP (p :: Phase) -> ![Text]
- Summoner.Config: [cGhcVer] :: ConfigP -> !p :- [GhcVer]
+ Summoner.Config: [cGhcVer] :: ConfigP (p :: Phase) -> !p :- [GhcVer]
- Summoner.Config: [cGitHub] :: ConfigP -> !Decision
+ Summoner.Config: [cGitHub] :: ConfigP (p :: Phase) -> !Decision
- Summoner.Config: [cGitignore] :: ConfigP -> ![Text]
+ Summoner.Config: [cGitignore] :: ConfigP (p :: Phase) -> ![Text]
- Summoner.Config: [cLib] :: ConfigP -> !Decision
+ Summoner.Config: [cLib] :: ConfigP (p :: Phase) -> !Decision
- Summoner.Config: [cLicense] :: ConfigP -> !p :- LicenseName
+ Summoner.Config: [cLicense] :: ConfigP (p :: Phase) -> !p :- LicenseName
- Summoner.Config: [cNoUpload] :: ConfigP -> !Any
+ Summoner.Config: [cNoUpload] :: ConfigP (p :: Phase) -> !Any
- Summoner.Config: [cOwner] :: ConfigP -> !p :- Text
+ Summoner.Config: [cOwner] :: ConfigP (p :: Phase) -> !p :- Text
- Summoner.Config: [cPrelude] :: ConfigP -> !Last CustomPrelude
+ Summoner.Config: [cPrelude] :: ConfigP (p :: Phase) -> !Last CustomPrelude
- Summoner.Config: [cPrivate] :: ConfigP -> !Decision
+ Summoner.Config: [cPrivate] :: ConfigP (p :: Phase) -> !Decision
- Summoner.Config: [cStack] :: ConfigP -> !Decision
+ Summoner.Config: [cStack] :: ConfigP (p :: Phase) -> !Decision
- Summoner.Config: [cTest] :: ConfigP -> !Decision
+ Summoner.Config: [cTest] :: ConfigP (p :: Phase) -> !Decision
- Summoner.Config: [cTravis] :: ConfigP -> !Decision
+ Summoner.Config: [cTravis] :: ConfigP (p :: Phase) -> !Decision
- Summoner.Config: type Config = ConfigP 'Final
+ Summoner.Config: type Config = ConfigP 'Final
- Summoner.Config: type PartialConfig = ConfigP 'Partial
+ Summoner.Config: type PartialConfig = ConfigP 'Partial
- Summoner.Decision: decisionToBool :: Decision -> YesNoPrompt -> IO Bool
+ Summoner.Decision: decisionToBool :: Decision -> Bool
- Summoner.Project: generateProject :: Bool -> Text -> Config -> IO ()
+ Summoner.Project: generateProject :: Interactivity -> ConnectMode -> Text -> Config -> IO ()
- Summoner.Settings: Settings :: !Text -> !Text -> !Text -> !Text -> !Text -> !Text -> !Text -> !LicenseName -> !License -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> ![GhcVer] -> !Text -> !Maybe CustomPrelude -> ![Text] -> ![Text] -> ![Text] -> !Bool -> !Bool -> !Maybe Text -> !Maybe Text -> !Bool -> ![TreeFs] -> Settings
+ Summoner.Settings: Settings :: !Text -> !Text -> !Text -> !Text -> !Text -> !Text -> !Text -> !LicenseName -> !License -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> ![GhcVer] -> !Maybe CustomPrelude -> ![Text] -> ![Text] -> ![Text] -> !Bool -> !Bool -> !Bool -> ![TreeFs] -> Settings
- Summoner.Source: fetchSource :: Bool -> Source -> IO (Maybe Text)
+ Summoner.Source: fetchSource :: ConnectMode -> Source -> IO (Maybe Text)
Files
- CHANGELOG.md +119/−0
- README.md +155/−63
- app/Cli.hs +1/−1
- examples/cabal-full/.github/workflows/ci.yml +44/−0
- examples/cabal-full/.gitignore +54/−0
- examples/cabal-full/.travis.yml +33/−0
- examples/cabal-full/CHANGELOG.md +11/−0
- examples/cabal-full/LICENSE +29/−0
- examples/cabal-full/README.md +9/−0
- examples/cabal-full/app/Main.hs +7/−0
- examples/cabal-full/appveyor.yml +34/−0
- examples/cabal-full/benchmark/Main.hs +7/−0
- examples/cabal-full/cabal-full.cabal +84/−0
- examples/cabal-full/src/CabalFull.hs +15/−0
- examples/cabal-full/test/Spec.hs +5/−0
- examples/cabal-minimal/CHANGELOG.md +9/−0
- examples/cabal-minimal/README.md +5/−0
- examples/cabal-minimal/app/Main.hs +5/−0
- examples/cabal-minimal/cabal-minimal.cabal +42/−0
- examples/full-batteries/.github/CODEOWNERS +1/−0
- examples/full-batteries/.github/workflows/ci.yml +46/−0
- examples/full-batteries/.gitignore +57/−0
- examples/full-batteries/.stylish-haskell.yaml +1/−0
- examples/full-batteries/.travis.yml +72/−0
- examples/full-batteries/CHANGELOG.md +11/−0
- examples/full-batteries/CONTRIBUTING.md +1/−0
- examples/full-batteries/LICENSE +21/−0
- examples/full-batteries/README.md +11/−0
- examples/full-batteries/app/Main.hs +7/−0
- examples/full-batteries/appveyor.yml +34/−0
- examples/full-batteries/benchmark/Main.hs +7/−0
- examples/full-batteries/extra.txt +1/−0
- examples/full-batteries/full-batteries.cabal +75/−0
- examples/full-batteries/src/FullBatteries.hs +15/−0
- examples/full-batteries/stack-8.0.2.yaml +1/−0
- examples/full-batteries/stack-8.2.2.yaml +1/−0
- examples/full-batteries/stack-8.4.4.yaml +1/−0
- examples/full-batteries/stack-8.6.5.yaml +1/−0
- examples/full-batteries/stack.yaml +1/−0
- examples/full-batteries/test/Spec.hs +5/−0
- examples/stack-full/.gitignore +54/−0
- examples/stack-full/.travis.yml +45/−0
- examples/stack-full/CHANGELOG.md +11/−0
- examples/stack-full/LICENSE +29/−0
- examples/stack-full/README.md +10/−0
- examples/stack-full/app/Main.hs +7/−0
- examples/stack-full/appveyor.yml +30/−0
- examples/stack-full/benchmark/Main.hs +7/−0
- examples/stack-full/src/StackFull.hs +15/−0
- examples/stack-full/stack-8.4.4.yaml +1/−0
- examples/stack-full/stack-8.6.5.yaml +1/−0
- examples/stack-full/stack-full.cabal +84/−0
- examples/stack-full/stack.yaml +1/−0
- examples/stack-full/test/Spec.hs +5/−0
- examples/summoner-default.toml +87/−0
- src/Summoner.hs +1/−0
- src/Summoner/Ansi.hs +16/−61
- src/Summoner/CLI.hs +205/−121
- src/Summoner/Config.hs +106/−112
- src/Summoner/Decision.hs +21/−11
- src/Summoner/Default.hs +123/−2
- src/Summoner/GhcVer.hs +11/−9
- src/Summoner/License.hs +32/−23
- src/Summoner/Mode.hs +40/−0
- src/Summoner/Project.hs +142/−79
- src/Summoner/Question.hs +21/−16
- src/Summoner/Settings.hs +1/−3
- src/Summoner/Source.hs +32/−26
- src/Summoner/Template/Cabal.hs +94/−99
- src/Summoner/Template/Doc.hs +46/−44
- src/Summoner/Template/GitHub.hs +111/−28
- src/Summoner/Template/Haskell.hs +19/−19
- src/Summoner/Template/Stack.hs +2/−7
- src/Summoner/Text.hs +3/−2
- src/Summoner/Tree.hs +4/−5
- summoner.cabal +67/−34
- test/Spec.hs +11/−22
- test/Test/DecisionSpec.hs +15/−14
- test/Test/Golden.hs +136/−39
- test/Test/Script.hs +1/−1
- test/Test/Show.hs +6/−6
- test/Test/TomlSpec.hs +32/−10
- test/golden/fullProject/.gitignore +0/−57
- test/golden/fullProject/.stylish-haskell.yaml +0/−1
- test/golden/fullProject/.travis.yml +0/−60
- test/golden/fullProject/CHANGELOG.md +0/−11
- test/golden/fullProject/CONTRIBUTING.md +0/−1
- test/golden/fullProject/LICENSE +0/−21
- test/golden/fullProject/README.md +0/−10
- test/golden/fullProject/app/Main.hs +0/−7
- test/golden/fullProject/appveyor.yml +0/−34
- test/golden/fullProject/benchmark/Main.hs +0/−7
- test/golden/fullProject/fullProject.cabal +0/−105
- test/golden/fullProject/src/FullProject.hs +0/−6
- test/golden/fullProject/src/Prelude.hs +0/−7
- test/golden/fullProject/stack-8.0.2.yaml +0/−3
- test/golden/fullProject/stack-8.2.2.yaml +0/−3
- test/golden/fullProject/stack-8.4.4.yaml +0/−3
- test/golden/fullProject/stack-8.6.5.yaml +0/−3
- test/golden/fullProject/stack.yaml +0/−3
- test/golden/fullProject/test/Spec.hs +0/−4
- test/golden/smallProject/CHANGELOG.md +0/−9
- test/golden/smallProject/README.md +0/−5
- test/golden/smallProject/app/Main.hs +0/−4
- test/golden/smallProject/smallProject.cabal +0/−37
CHANGELOG.md view
@@ -3,6 +3,125 @@ `summoner` uses [PVP Versioning][1]. The changelog is available [on GitHub][2]. +## 2.0.0.0 – Mar 28, 2019++* [#246](https://github.com/kowainik/summoner/issues/246):+ Put common fields into the `common-options` [common stanza](https://vrom911.github.io/blog/common-stanzas).+ (by [@vrom911](https://github.com/vrom911))+* [#351](https://github.com/kowainik/summoner/issues/351),+ [#395](https://github.com/kowainik/summoner/issues/395),+ [#398](https://github.com/kowainik/summoner/issues/398):+ __#CLI__ __#TUI__+ Add GitHub Actions CI check as an option for the generated+ project.++ * Add `-a` or `--actions` CLI option for GitHub actions.+ * Add `GitHub Actions` entry into interactive CLI mode.+ * Add `GitHub Actions` button into TUI.+ * Add `githubActions` to the TOML configs.++ (by [@patrickt](https://github.com/patrickt), [@vrom911](https://github.com/vrom911), [@chshersh](https://github.com/chshersh))+* [#401](https://github.com/kowainik/summoner/issues/401),+ [#429](https://github.com/kowainik/summoner/issues/429):+ Support GHC-8.8.3 in the project.+ Make GHC-8.8.3 default for the generated projects.+ Upgrade Stack LTS to `15.5`.+ (by [@vrom911](https://github.com/vrom911))+* [#418](https://github.com/kowainik/summoner/issues/418):+ Bump default `cabal` version to `2.4` in the generated project.+ (by [@vrom911](https://github.com/vrom911))+* [#114](https://github.com/kowainik/summoner/issues/114):+ __#CLI__ __#TUI__+ Implement non-interactive mode with `-n | --non-interacive` command-line option.+ (by [@vrom911](https://github.com/vrom911))+* [#70](https://github.com/kowainik/summoner/issues/70):+ __#CLI__ __#TUI__+ Implement `summon config` command. This command will generate the+ default TOML configuration file with helpful comments.+ (by [@chshersh](https://github.com/chshersh))+* [#361](https://github.com/kowainik/summoner/issues/361):+ Always put all default warnings in `ghc-options` inside common+ stanza under cabal conditionals on the `GHC` version. Now they look+ like this:++ ```haskell+ if impl(ghc >= 8.4)+ ghc-options: -Wmissing-export-lists+ -Wpartial-fields+ if impl(ghc >= 8.8)+ ghc-options: -Wmissing-deriving-strategies+ ```++ (by [@chshersh](https://github.com/chshersh))+* [#345](https://github.com/kowainik/summoner/issues/345):+ Apply `mixins` approach for using alternative preludes instead of+ the `base-noprelude` trick. Now it looks like this in the `.cabal`+ file:++ ```haskell+ mixins: base hiding (Prelude)+ , relude (Relude as Prelude)+ ```+ (by [@chshersh](https://github.com/chshersh))+* [#251](https://github.com/kowainik/summoner/issues/251):+ Update LICENSE names for `.cabal` files in generated projects.+ Add `ISC` to the list of the accepted licenses.+ (by [@vrom911](https://github.com/vrom911))+* __#CLI__ __#TUI__+ Customise outputted LICENSE text for `summon show license LICENSE_NAME` command+ (by [@vrom911](https://github.com/vrom911))+* [#399](https://github.com/kowainik/summoner/issues/399):+ Add Haddock header to library module.++ ```haskell+ {- |+ Copyright: (c) 2020 FULL_NAME+ SPDX-License-Identifier: LICENSE_NAME+ Maintainer: NAME <email@email.com>++ Project description+ -}+ ```+ (by [@vrom911](https://github.com/vrom911))+* Improve `maintainer` field in the generated `.cabal` file.+ (by [@vrom911](https://github.com/vrom911))+* [#430](https://github.com/kowainik/summoner/issues/430):+ Instead of not including old GHCs for Stack check at Travis,+ those versions are added to the `allow_failures` section of matrix.+ (by [@vrom911](https://github.com/vrom911))++ _Note:_ For the Stack failure reasons see [this issue](https://github.com/commercialhaskell/stack/issues/4488).+* [#394](https://github.com/kowainik/summoner/issues/394):+ Do not crush when `hub` is not installed. Instead, print descriptive warning+ message.+ (by [@vrom911](https://github.com/vrom911))+* [#34](https://github.com/kowainik/summoner/issues/34):+ Print helpful error message when `hub` fails (for example, if the+ password is incorrect).+ (by [@chshersh](https://github.com/chshersh))+* [#281](https://github.com/kowainik/summoner/issues/281):+ Add more example projects and golden tests for various scaffolding+ configurations.+ (by [@chshersh](https://github.com/chshersh))+* [#375](https://github.com/kowainik/summoner/issues/375):+ Remove deprecated `stylish` and `contributing` fields in the configurations.+ Use `files` instead.+ (by [@vrom911](https://github.com/vrom911))+* Use `--silent` and `--fail` options with `curl` commands.+ (by [@chshersh](https://github.com/chshersh))+* Upgrade GHC-8.6.5 LTS to `14.27`.+ (by [@vrom911](https://github.com/vrom911))+* __#TUI__ Allow `brick-0.52`.+ (by [@vrom911](https://github.com/vrom911))+* [#363](https://github.com/kowainik/summoner/issues/363):+ Move from `generic-deriving` to `generic-data`.+ (by [@chshersh](https://github.com/chshersh))+* [#434](https://github.com/kowainik/summoner/issues/434):+ Move to `validation-selective`.+ (by [@vrom911](https://github.com/vrom911))+* Use `colourista` for pretty terminal formatting.+ (by [@chshersh](https://github.com/chshersh))+ ## 1.4.0.0 – Dec 25, 2019 🎅 * [#339](https://github.com/kowainik/summoner/issues/339):
README.md view
@@ -1,6 +1,7 @@ # 🔮 Summoner +[](https://github.com/kowainik/summoner/actions) [](http://travis-ci.org/kowainik/summoner) [](https://ci.appveyor.com/project/kowainik/summoner) [](https://hackage.haskell.org/package/summoner)@@ -14,11 +15,11 @@ Summoner is a tool for scaffolding fully configured batteries-included production-level Haskell projects. Do you want to create a library that is to be uploaded to Hackage/Stackage, that builds with both Cabal and Stack and supports the latest three major GHC versions?-Or are you building a production application which uses a custom prelude and has CI with Travis Linux and AppVeyors Windows checks?+Or are you building a production application which uses a custom prelude and has CI with GitHub Actions or Travis Linux and AppVeyors Windows checks? Maybe do you want to play with your idea in a single module without introducing the whole complexity of the Haskell projects? Summoner can help you do all that with minimal effort from you - it can even upload the project to GitHub if you wish! -By the way, Summoner operates as either CLI or TUI application, so you can choose what you're more comfortable with and install only the required one.+By the way, Summoner operates as either CLI or TUI application, so you can choose what you are more comfortable with and install only the required one. ## Structure @@ -32,10 +33,14 @@ + [Installation](#installation-) + [Summon-TUI](#summon-tui-) + [TUI: download binary](#tui-download-binary-)+ + [TUI: Homebrew](#tui-homebrew-)+ + [TUI: Ubuntu](#tui-ubuntu-) + [TUI: from Hackage/Stackage](#tui-from-hackagestackage-) + [TUI: from source](#tui-from-source-) + [Summon-CLI](#summon-cli-) + [CLI: download binary](#cli-download-binary-)+ + [CLI: Homebrew](#cli-homebrew-)+ + [CLI: Ubuntu](#cli-ubuntu-) + [CLI: from Hackage/Stackage](#cli-from-hackagestackage-) + [CLI: from source](#cli-from-source-) + [Setup](#setup-)@@ -55,21 +60,31 @@ ### TUI demo [↑](#structure) -[](https://asciinema.org/a/214424)+[](https://asciinema.org/a/314375) ### CLI demo [↑](#structure) -[](https://asciinema.org/a/214433)+[](https://asciinema.org/a/314374) ### Examples You can also see complete examples in the following folder: -* [`summon-cli/tests/golden`: Examples of scaffolded projects](https://github.com/kowainik/summoner/tree/master/summoner-cli/test/golden)+* [`summon-cli/examples/`: Examples of scaffolded projects](https://github.com/kowainik/summoner/tree/master/summoner-cli/examples/) -Directory `smallProject` contains an example of the straightforward package,-and `fullProject` shows a generated project with more advanced structure and more features.+The directory contains the following examples: +* [`cabal-minimal`](https://github.com/kowainik/summoner/tree/master/summoner-cli/examples/cabal-minimal):+ Minimal Haskell project with the Cabal-only support, default+ settings and all of the integrations disabled.+* [`cabal-full`](https://github.com/kowainik/summoner/tree/master/summoner-cli/examples/cabal-full):+ Cabal-only project with all integrations enabled.+* [`stack-full`](https://github.com/kowainik/summoner/tree/master/summoner-cli/examples/stack-full):+ Stack-only project with all integrations enabled.+* [`full-batteries`](https://github.com/kowainik/summoner/tree/master/summoner-cli/examples/full-batteries):+ All batteries-included project which supports both build tools and+ shows every Summoner feature.+ ## Features [↑](#structure) Summoner is a tool that combines predefined configurations, command-line arguments and a chosen interface.@@ -84,30 +99,30 @@ + Support for Cabal and Stack build tools. + Ability to pick stanzas (library, executable, test-suite, benchmark).++ Usage of [common stanza](https://vrom911.github.io/blog/common-stanzas) to reduce `.cabal` file's boilerplate. + Option to include an alternative prelude, if desired. The project would then- use [`base-noprelude` technique](http://hackage.haskell.org/package/Prelude),- and the `Prelude` module would be added to the library target.+ use the [`mixins` technique](https://www.reddit.com/r/haskelltil/comments/9qa366/easy_way_to_replace_default_prelude_with_the/). + Whole Hackage-upload checklist support (exhaustive `.cabal` file, PVP versioning, GHC options, conventional meta files).-+ Support for multiple GHC versions, with thoughtful reflection on project meta, base versions (e.g. `base >= 4.9 && < 4.12`), etc.++ Support for multiple GHC versions, with thoughtful reflection on project meta, base versions (e.g. `base >= 4.9 && < 4.13`), etc. + Ability to create runnable Haskell scripts.-+ Different license support: MIT, BSD2, BSD3, GPL-2, GPL-3, LGPL-2.1, LGPL-3, AGPL-3, Apache-2.0, MPL-2.0, None (All Rights Reserved license without file).++ Different license support: MIT, BSD-2-Clause, BSD-3-Clause, GPL-2.0, GPL-3.0, LGPL-2.1, LGPL-3.0, AGPL-3.0, Apache-2.0, MPL-2.0, ISC, None (without file). + Creation of the `CHANGELOG.md` file with [PVP versioning policy](https://pvp.haskell.org).-+ Ability to include any custom files (including `.stylish-haskell.yaml`, `CONTRIBUTING.md`, `CODEOWNERS`, etc.).++ Ability to include any custom files (including `.stylish-haskell.yaml`, `CONTRIBUTING.md`, `CODEOWNERS`, `FUNDING.yml` etc.). + Usage of the `ghc-options` field with sensible defaults. If `ghc-options` are not explicitly stated in the configuration file, then the following list of GHC flags is added to all stanzas: ``` -Wall- -Wincomplete-uni-patterns- -Wincomplete-record-updates -Wcompat -Widentities- -Wredundant-constraints (GHC >= 8.0)- -fhide-source-paths (GHC >= 8.2.2)- -Wmissing-export-lists (GHC >= 8.4.1)- -Wpartial-fields (GHC >= 8.4.1)- -Wmissing-deriving-strategies (GHC >= 8.8.1)+ -Wincomplete-uni-patterns+ -Wincomplete-record-updates+ -Wredundant-constraints (GHC ⩾ 8.0)+ -fhide-source-paths (GHC ⩾ 8.2)+ -Wmissing-export-lists (GHC ⩾ 8.4)+ -Wpartial-fields (GHC ⩾ 8.4)+ -Wmissing-deriving-strategies (GHC ⩾ 8.8) ``` Besides, the following GHC options are added to the executable, tests and benchmark stanzas:@@ -134,16 +149,19 @@ + Generation of the `.travis.yml` file that runs build and tests on CI under Linux using [Dead simple Haskell Travis Settings for Cabal and Stack](https://chshersh.github.io/posts/2019-02-25-haskell-travis). + Generation of the `appveyor.yaml` file which runs build and tests on CI under Windows.++ Generation of the `.github/workflows/ci.yml` file that runs build and tests on+ GitHub Actions CI under Linux using Cabal. + Configuration matrix on CI to build with multiple GHC versions and various build tools. + `-Werror` is enabled on CI not to miss any warnings. + Run HLint checks on CI. ### Others [↑](#structure) ++ Carefully collected Haskell project best practices gathered in your projects' scaffold.++ Generate beginner-friendly default configuration using the `summon config` command. + Ability to create a project in the offline mode. + Ability to check GHC-specific versions of the corresponding `base` library and Stackage snapshot resolver via `summon show ghc` command.-+ Carefully collected Haskell project best practices gathered in your projects' scaffold. ### Project structure example [↑](#structure) @@ -187,8 +205,8 @@ We also have minimal version requirements for build tools: -* [Cabal >= 2.0](https://www.haskell.org/cabal/)-* [Stack >= 2.1](http://haskellstack.org)+* [Cabal ⩾ 2.4](https://www.haskell.org/cabal/)+* [Stack ⩾ 2.1.3](http://haskellstack.org) However, it is always recommended to use the newest versions of build tools. @@ -216,13 +234,36 @@ mv summon-cli-linux ~/.local/bin/summon ``` +##### TUI: Homebrew [↑](#structure)++If you are on MacOS, you can get Summoner using Homebrew Kowainik's Tap.++You need to run the following commands for that:++```shell+$ brew tap kowainik/tap+$ brew install summoner-tui+```++##### TUI: Ubuntu [↑](#structure)++If you are on Ubuntu, you can get Summoner from Kowainik's PPA.++You need to run the following commands for that:++```shell+$ sudo add-apt-repository ppa:kowainik/summoner-tui+$ sudo apt update+$ sudo apt install summoner-tui+```+ ##### TUI: from Hackage/Stackage [↑](#structure) Using `cabal`: ```shell-cabal new-update-cabal new-install summoner-tui+cabal v2-update+cabal v2-install summoner-tui ``` Using `stack`:@@ -254,7 +295,7 @@ Build and install using `cabal`: ```shell-cabal new-install summoner-tui:exe:summon-tui+cabal v2-install summoner-tui:exe:summon-tui ``` Build and install using `stack`:@@ -276,13 +317,36 @@ mv summon-cli-linux ~/.local/bin/summon ``` +##### CLI: Homebrew [↑](#structure)++If you are on MacOS, you can get Summoner using Homebrew Kowainik's Tap.++You need to run the following commands for that:++```shell+$ brew tap kowainik/tap+$ brew install summoner-cli+```++##### CLI: Ubuntu [↑](#structure)++If you are on Ubuntu, you can get Summoner from Kowainik's PPA.++You need to run the following commands for that:++```shell+$ sudo add-apt-repository ppa:kowainik/summoner-cli+$ sudo apt update+$ sudo apt install summoner-cli+```+ ##### CLI: from Hackage/Stackage [↑](#structure) Using `cabal`: ```shell-cabal new-update-cabal new-install summoner+cabal v2-update+cabal v2-install summoner ``` Using `stack`:@@ -314,7 +378,7 @@ Build and install using `cabal`: ```shell-cabal new-install summoner-cli:exe:summon+cabal v2-install summoner-cli:exe:summon ``` Build and install using `stack`:@@ -358,11 +422,12 @@ | `fullName` | Text | Full name. | | `email` | Text | E-mail address. | | `license` | License | One of: `MIT`, `BSD2`, `BSD3`, `GPL-2`, `GPL-3`, `LGPL-2.1`, `LGPL-3`, `AGPL-3`, `Apache-2.0`, `MPL-2.0`, `None`. |-| `ghcVersions` | [GHC] | `summoner` uses default `GHC-8.8.1`. However, additionally you can specify other versions. For each version `x.y.z` the `stack-x.y.z.yaml` will be created. |+| `ghcVersions` | [GHC] | `summoner` uses default `GHC-8.8.3`. However, additionally you can specify other versions. For each version `x.y.z` the `stack-x.y.z.yaml` will be created. | | `github` | Bool | Turn on `GitHub` integration by default? | | `gitignore` | [Text] | List of files you want added to the default `.gitignore`. (Ignored if `github = false`) | | `noUpload` | Bool | Do not upload to GitHub, but create all GitHub related files if specified (Ignored if `github = false`) | | `private` | Bool | Create private repository by default? (Ignored if `github = false`) |+| `githubActions` | Bool | Turn on `GitHub Actions` integration by default? (Currently working with `Cabal` only. Ignored if `github = false`) | | `travis` | Bool | Turn on `Travis` integration by default? (Ignored if `github = false`) | | `appveyor` | Bool | Turn on `AppVeyor` integration by default? (Ignored if `github = false`) | | `lib` | Bool | Create `src` folder with simple `ProjectName.hs` file and library target? |@@ -371,15 +436,16 @@ | `bench` | Bool | Create `benchmark` folder with `Main.hs` file with [`gauge`](https://hackage.haskell.org/package/gauge) library usage example? | | `extensions` | [Text] | List of the default extensions to add into `default-extensions` section in the `.cabal`. | | `ghc-options` | [Text] | List of the default GHC options to add into `ghc-options` section in the `.cabal`. |-| `stylish.*` | Text | **DEPRECATED** `stylish.file` to provide the absolute file path OR `stylish.url` to download the `.stylish-haskell.yaml` file to use in the project. |-| `contributing.*` | Text | **DEPRECATED** `contributing.file` to provide the absolute file path OR `contributing.url` download OR `contribuint.link` to link the `CONTRIBUTING.md` file to use in the project. | | `files` | Map FilePath Source | Custom mapping of files to their sources. Represented as a list of inline tables in TOML in a format like `files = [ { path = "foo", url = "https://..." }, ... ]`. Supported file types: `url`, `local`, `raw`. | |`[prelude]` | | | | `package` | Text | The package name of the custom prelude you'd like to use in the project (doesn't work without `module` field). | | `module` | Text | The module name of the custom prelude you'd like to use in the project (doesn't work without `package` field). | -See the example of [the configuration for projects of the `Kowainik` organization](https://github.com/kowainik/org/blob/master/.summoner.toml).+You can create default configuration using the `summon config`+command. See [the default content here](summoner-cli/test/golden/summoner-default.toml). +For a real-life example of the configuration, see [the configuration for projects of the `Kowainik` organization](https://github.com/kowainik/org/blob/master/.summoner.toml).+ ### Command line arguments [↑](#structure) Available commands:@@ -393,6 +459,7 @@ new Create a new Haskell project script Create a new Haskell script show Show available licenses or ghc versions+ config Create default TOML configuration for summoner Available global options: -h, --help Show this help text@@ -421,7 +488,7 @@ --cabal Cabal support for the project --stack Stack support for the project -f, --file FILENAME Path to the toml file with configurations. If not- specified '~/.summoner.toml' will be used if present+ specified '~/.summoner.toml' will be used by default --prelude-package PACKAGE_NAME Name for the package of the custom prelude to use in the project@@ -437,6 +504,7 @@ -h, --help Show this help text -g, --github Github integration -p, --private Create private GitHub repository+ -a, --actions GitHub Actions CI integration -c, --travis Travis CI integration -w, --app-veyor AppVeyor CI integration -l, --library Library target@@ -474,12 +542,41 @@ -h, --help Show this help text ``` -For example, the following command preconfigures the project settings with the custom prelude `relude`, included library, executable, test stanzas (but without benchmarks), creates a private repository on GitHub with the Travis CI and AppVeyor CI integrated.+For example, the following command preconfigures the project settings with the+custom prelude `relude`, included library, executable, test stanzas (but without+benchmarks), creates a private repository on GitHub with the GitHub Actions CI,+Travis CI and AppVeyor CI integrated. ```shell-summon new my-project with -letgcpw without -b --prelude-package relude --prelude-module Relude+summon new my-project with -letgcpwa without -b --prelude-package relude --prelude-module Relude ``` +#### **summon config** command: [↑](#structure)++```+Usage: summon config [-f|--file=FILENAME]+ Create a default TOML configuration file for summoner++Available options:+ -h,--help Show this help text+ -f,--file=FILENAME Path to the toml file with configurations. If not+ specified '~/.summoner.toml' will be used by default+```++This command will generate a TOML configuration file with the default settings+that can be used to scaffold future Haskell packages. It contains all+options supported by Summoner with comments and examples. Though, all+options would be turned off by default and to use them one will need+to uncomment the correspoding lines.+See [the default content here](summoner-cli/test/golden/summoner-default.toml).++Possible command usages:++```shell+summon config+summon config --file ~/.summoner-demo.toml+```+ ### TUI [↑](#structure) #### TUI new command [↑](#structure)@@ -553,7 +650,7 @@ ```haskell #!/usr/bin/env stack {- stack- --resolver lts-14.18+ --resolver lts-15.5 script --package base -}@@ -591,6 +688,21 @@ export HUB_PROTOCOL="https" ``` +<hr>++> Why some of the Stack checks on my TravisCI matrix are marked as "This job is+> allowed to fail"?++Due to the Stack problem of working with older versions of Cabal, the build can+fail with Stack for some older GHCs. We are adding these checks into+`allow_failures` section, as this is a known issue, and there is no need for the+whole build to fail because of that.++For more information about the issue and some workarounds from Stack developers,+see the following ticket:++ * [commersialhaskell/stack issue](https://github.com/commercialhaskell/stack/issues/4488)+ ## For Developers [↑](#structure) If you'd like to take part in the development processes, here are a few things to keep in mind:@@ -610,7 +722,7 @@ To build the project you can use the following commands: ```shell=-cabal new-build all+cabal v2-build all ``` for Cabal and@@ -619,14 +731,7 @@ stack build ``` -for Stack and--```shell=-nix-build -A summoner # To build the CLI only-nix-build -A summoner-tui # To build the TUI-```--for Nix.+for Stack. ### Test [↑](#structure) @@ -635,42 +740,29 @@ To actually run tests you need to run: ```shell-cabal new-test all+cabal v2-test all ``` -or, if using Stack,+or, if using Stack ```shell stack test ``` -or, if using Nix,--```shell-nix-shell --run 'cabal new-test all'-```- ### Run [↑](#structure) Building Summoner with the recommended method creates two executables: `summon` and `summon-tui`. To run the compiled executable you can use the following commands: ```shell-cabal new-exec summon -- SOME_COMMAND-cabal new-exec summon-tui -- SOME_COMMAND+cabal v2-exec summon -- SOME_COMMAND+cabal v2-exec summon-tui -- SOME_COMMAND ``` -or, if using Stack,+or, if using Stack ```shell stack exec summon -- SOME_COMMAND stack exec summon-tui -- SOME_COMMAND-```--or, if using Nix,--```shell-nix-shell --run 'cabal new-run summon -- SOME_COMMAND'-nix-shell --run 'cabal new-run summon-tui -- SOME_COMMAND' ``` ## Acknowledgments [↑](#structure)
app/Cli.hs view
@@ -1,4 +1,4 @@-module Main where+module Main (main) where import System.IO (hSetEncoding, utf8)
+ examples/cabal-full/.github/workflows/ci.yml view
@@ -0,0 +1,44 @@+name: CI++# Trigger the workflow on push or pull request, but only for the master branch+on:+ pull_request:+ push:+ branches: [master]++jobs:+ build:+ name: ghc ${{ matrix.ghc }}+ runs-on: ubuntu-16.04+ strategy:+ matrix:+ cabal: ["2.4"]+ ghc:+ - "8.4.4"+ - "8.6.5"+ - "8.8.3"++ steps:+ - uses: actions/checkout@v2+ if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/master'++ - uses: actions/setup-haskell@v1+ name: Setup Haskell+ with:+ ghc-version: ${{ matrix.ghc }}+ cabal-version: ${{ matrix.cabal }}++ - uses: actions/cache@v1+ name: Cache ~/.cabal/store+ with:+ path: ~/.cabal/store+ key: ${{ runner.os }}-${{ matrix.ghc }}-cabal++ - name: Build+ run: |+ cabal v2-update+ cabal v2-build --enable-tests --enable-benchmarks++ - name: Test+ run: |+ cabal v2-test --enable-tests
+ examples/cabal-full/.gitignore view
@@ -0,0 +1,54 @@+### Haskell+dist+dist-*+cabal-dev+*.o+*.hi+*.chi+*.chs.h+*.dyn_o+*.dyn_hi+*.prof+*.aux+*.hp+*.eventlog+.virtualenv+.hsenv+.hpc+.cabal-sandbox/+cabal.sandbox.config+cabal.config+cabal.project.local+.ghc.environment.*+.HTF/+# Stack+.stack-work/+stack.yaml.lock++### IDE/support+# Vim+[._]*.s[a-v][a-z]+[._]*.sw[a-p]+[._]s[a-v][a-z]+[._]sw[a-p]+*~+tags++# IntellijIDEA+.idea/+.ideaHaskellLib/+*.iml++# Atom+.haskell-ghc-mod.json++# VS+.vscode/++# Emacs+*#+.dir-locals.el+TAGS++# other+.DS_Store
+ examples/cabal-full/.travis.yml view
@@ -0,0 +1,33 @@+sudo: true+language: haskell++git:+ depth: 5++cabal: "2.4"++cache:+ directories:+ - "$HOME/.cabal/store"+ ++matrix:+ include:+ - ghc: 8.4.4+ - ghc: 8.6.5+ - ghc: 8.8.3+ + ++install:+ # HLint check+ - curl -sSL https://raw.github.com/ndmitchell/neil/master/misc/travis.sh | sh -s -- hlint .++ - cabal v2-update+ - cabal v2-build --enable-tests --enable-benchmarks++script:+ - cabal v2-test --enable-tests++notifications:+ email: false
+ examples/cabal-full/CHANGELOG.md view
@@ -0,0 +1,11 @@+# Changelog++`cabal-full` uses [PVP Versioning][1].+The changelog is available [on GitHub][2].++## 0.0.0.0++* Initially created.++[1]: https://pvp.haskell.org+[2]: https://github.com/kowainik/cabal-full/releases
+ examples/cabal-full/LICENSE view
@@ -0,0 +1,29 @@+BSD 3-Clause License++Copyright (c) 2020, Kowainik+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++1. Redistributions of source code must retain the above copyright notice, this+ list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright notice,+ this list of conditions and the following disclaimer in the documentation+ and/or other materials provided with the distribution.++3. Neither the name of the copyright holder nor the names of its+ contributors may be used to endorse or promote products derived from+ this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ examples/cabal-full/README.md view
@@ -0,0 +1,9 @@+# cabal-full++[](https://github.com/kowainik/cabal-full/actions)+[](https://travis-ci.org/kowainik/cabal-full)+[](https://ci.appveyor.com/project/kowainik/cabal-full)+[](https://hackage.haskell.org/package/cabal-full)+[](LICENSE)++Cabal-only example with all integrations
+ examples/cabal-full/app/Main.hs view
@@ -0,0 +1,7 @@+module Main (main) where++import CabalFull (someFunc)+++main :: IO ()+main = someFunc
+ examples/cabal-full/appveyor.yml view
@@ -0,0 +1,34 @@+clone_folder: "c:\\WORK"+clone_depth: 5++# Do not build feature branch with open Pull Requests+skip_branch_with_pr: true++platform:+ - x86_64++cache:+ - "C:\\SR"+ - dist-newstyle++environment:+ global:+ CABOPTS: --store-dir=C:\\SR++ matrix:+ - GHCVER: 8.8.3++install:+ - choco source add -n mistuke -s https://www.myget.org/F/mistuke/api/v2+ - choco install -y cabal --version 2.4.1.0+ - choco install -y ghc --version 8.8.3+ - refreshenv++before_build:+ - cabal --version+ - ghc --version+ - cabal %CABOPTS% v2-update++build_script:+ - cabal %CABOPTS% v2-build --enable-tests+ - cabal %CABOPTS% v2-test --enable-tests
+ examples/cabal-full/benchmark/Main.hs view
@@ -0,0 +1,7 @@+module Main (main) where++import Gauge.Main+++main :: IO ()+main = defaultMain [bench "const" (whnf const ())]
+ examples/cabal-full/cabal-full.cabal view
@@ -0,0 +1,84 @@+cabal-version: 2.4+name: cabal-full+version: 0.0.0.0+synopsis: Cabal-only example with all integrations+description: Cabal-only example with all integrations+homepage: https://github.com/kowainik/cabal-full+bug-reports: https://github.com/kowainik/cabal-full/issues+license: BSD-3-Clause+license-file: LICENSE+author: Kowainik+maintainer: Kowainik <xrom.xkov@gmail.com>+copyright: 2020 Kowainik+category: Testing+build-type: Simple+extra-doc-files: README.md+ CHANGELOG.md+tested-with: GHC == 8.4.4+ GHC == 8.6.5+ GHC == 8.8.3++source-repository head+ type: git+ location: https://github.com/kowainik/cabal-full.git++common common-options+ build-depends: base >= 4.11.1.0 && < 4.14+ , relude+ + mixins: base hiding (Prelude)+ , relude (Relude as Prelude)+ + ghc-options: -Wall+ -Wcompat+ -Widentities+ -Wincomplete-uni-patterns+ -Wincomplete-record-updates+ if impl(ghc >= 8.0)+ ghc-options: -Wredundant-constraints+ if impl(ghc >= 8.2)+ ghc-options: -fhide-source-paths+ if impl(ghc >= 8.4)+ ghc-options: -Wmissing-export-lists+ -Wpartial-fields+ if impl(ghc >= 8.8)+ ghc-options: -Wmissing-deriving-strategies++ default-language: Haskell2010+ default-extensions: DeriveGeneric+ TypeApplications++library+ import: common-options+ hs-source-dirs: src+ exposed-modules: CabalFull++executable cabal-full+ import: common-options+ hs-source-dirs: app+ main-is: Main.hs+ build-depends: cabal-full+ ghc-options: -threaded+ -rtsopts+ -with-rtsopts=-N++test-suite cabal-full-test+ import: common-options+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Spec.hs+ build-depends: cabal-full+ ghc-options: -threaded+ -rtsopts+ -with-rtsopts=-N++benchmark cabal-full-benchmark+ import: common-options+ type: exitcode-stdio-1.0+ hs-source-dirs: benchmark+ main-is: Main.hs+ build-depends: gauge+ , cabal-full+ ghc-options: -threaded+ -rtsopts+ -with-rtsopts=-N
+ examples/cabal-full/src/CabalFull.hs view
@@ -0,0 +1,15 @@+{- |+Copyright: (c) 2020 Kowainik+SPDX-License-Identifier: BSD-3-Clause+Maintainer: Kowainik <xrom.xkov@gmail.com>++Cabal-only example with all integrations+-}++module CabalFull+ ( someFunc+ ) where+++someFunc :: IO ()+someFunc = putStrLn ("someFunc" :: String)
+ examples/cabal-full/test/Spec.hs view
@@ -0,0 +1,5 @@+module Main (main) where+++main :: IO ()+main = putStrLn ("Test suite is not implemented" :: String)
+ examples/cabal-minimal/CHANGELOG.md view
@@ -0,0 +1,9 @@+# Changelog++`cabal-minimal` uses [PVP Versioning][1].++## 0.0.0.0++* Initially created.++[1]: https://pvp.haskell.org
+ examples/cabal-minimal/README.md view
@@ -0,0 +1,5 @@+# cabal-minimal++[](https://hackage.haskell.org/package/cabal-minimal)++Minimal cabal-only test project
+ examples/cabal-minimal/app/Main.hs view
@@ -0,0 +1,5 @@+module Main (main) where+++main :: IO ()+main = putStrLn ("Hello, world!" :: String)
+ examples/cabal-minimal/cabal-minimal.cabal view
@@ -0,0 +1,42 @@+cabal-version: 2.4+name: cabal-minimal+version: 0.0.0.0+synopsis: Minimal cabal-only test project+description: Minimal cabal-only test project+license: NONE+author: Kowainik+maintainer: Kowainik <xrom.xkov@gmail.com>+copyright: 2020 Kowainik+build-type: Simple+extra-doc-files: README.md+ CHANGELOG.md+tested-with: GHC == 8.8.3++common common-options+ build-depends: base ^>= 4.13.0.0+ + ghc-options: -Wall+ -Wcompat+ -Widentities+ -Wincomplete-uni-patterns+ -Wincomplete-record-updates+ if impl(ghc >= 8.0)+ ghc-options: -Wredundant-constraints+ if impl(ghc >= 8.2)+ ghc-options: -fhide-source-paths+ if impl(ghc >= 8.4)+ ghc-options: -Wmissing-export-lists+ -Wpartial-fields+ if impl(ghc >= 8.8)+ ghc-options: -Wmissing-deriving-strategies++ default-language: Haskell2010++executable cabal-minimal+ import: common-options+ hs-source-dirs: app+ main-is: Main.hs+ + ghc-options: -threaded+ -rtsopts+ -with-rtsopts=-N
+ examples/full-batteries/.github/CODEOWNERS view
@@ -0,0 +1,1 @@+* @chshersh @vrom911
+ examples/full-batteries/.github/workflows/ci.yml view
@@ -0,0 +1,46 @@+name: CI++# Trigger the workflow on push or pull request, but only for the master branch+on:+ pull_request:+ push:+ branches: [master]++jobs:+ build:+ name: ghc ${{ matrix.ghc }}+ runs-on: ubuntu-16.04+ strategy:+ matrix:+ cabal: ["2.4"]+ ghc:+ - "8.0.2"+ - "8.2.2"+ - "8.4.4"+ - "8.6.5"+ - "8.8.3"++ steps:+ - uses: actions/checkout@v2+ if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/master'++ - uses: actions/setup-haskell@v1+ name: Setup Haskell+ with:+ ghc-version: ${{ matrix.ghc }}+ cabal-version: ${{ matrix.cabal }}++ - uses: actions/cache@v1+ name: Cache ~/.cabal/store+ with:+ path: ~/.cabal/store+ key: ${{ runner.os }}-${{ matrix.ghc }}-cabal++ - name: Build+ run: |+ cabal v2-update+ cabal v2-build --enable-tests --enable-benchmarks++ - name: Test+ run: |+ cabal v2-test --enable-tests
+ examples/full-batteries/.gitignore view
@@ -0,0 +1,57 @@+### Haskell+dist+dist-*+cabal-dev+*.o+*.hi+*.chi+*.chs.h+*.dyn_o+*.dyn_hi+*.prof+*.aux+*.hp+*.eventlog+.virtualenv+.hsenv+.hpc+.cabal-sandbox/+cabal.sandbox.config+cabal.config+cabal.project.local+.ghc.environment.*+.HTF/+# Stack+.stack-work/+stack.yaml.lock++### IDE/support+# Vim+[._]*.s[a-v][a-z]+[._]*.sw[a-p]+[._]s[a-v][a-z]+[._]sw[a-p]+*~+tags++# IntellijIDEA+.idea/+.ideaHaskellLib/+*.iml++# Atom+.haskell-ghc-mod.json++# VS+.vscode/++# Emacs+*#+.dir-locals.el+TAGS++# other+.DS_Store++# User specific+.secret
+ examples/full-batteries/.stylish-haskell.yaml view
@@ -0,0 +1,1 @@+This is stylish-haskell.yaml
+ examples/full-batteries/.travis.yml view
@@ -0,0 +1,72 @@+sudo: true+language: haskell++git:+ depth: 5++cabal: "2.4"++cache:+ directories:+ - "$HOME/.cabal/store"+ - "$HOME/.stack"+ - "$TRAVIS_BUILD_DIR/.stack-work"++matrix:+ include:+ - ghc: 8.0.2+ - ghc: 8.2.2+ - ghc: 8.4.4+ - ghc: 8.6.5+ - ghc: 8.8.3+ + - ghc: 8.0.2+ env: STACK_YAML="$TRAVIS_BUILD_DIR/stack-8.0.2.yaml"+ + - ghc: 8.2.2+ env: STACK_YAML="$TRAVIS_BUILD_DIR/stack-8.2.2.yaml"+ + - ghc: 8.4.4+ env: STACK_YAML="$TRAVIS_BUILD_DIR/stack-8.4.4.yaml"+ + - ghc: 8.6.5+ env: STACK_YAML="$TRAVIS_BUILD_DIR/stack-8.6.5.yaml"+ + - ghc: 8.8.3+ env: STACK_YAML="$TRAVIS_BUILD_DIR/stack.yaml"+ + allow_failures:+ + - ghc: 8.0.2+ env: STACK_YAML="$TRAVIS_BUILD_DIR/stack-8.0.2.yaml"+ + - ghc: 8.2.2+ env: STACK_YAML="$TRAVIS_BUILD_DIR/stack-8.2.2.yaml"+ + - ghc: 8.4.4+ env: STACK_YAML="$TRAVIS_BUILD_DIR/stack-8.4.4.yaml"++install:+ # HLint check+ - curl -sSL https://raw.github.com/ndmitchell/neil/master/misc/travis.sh | sh -s -- hlint .++ - |+ if [ -z "$STACK_YAML" ]; then+ cabal v2-update+ cabal v2-build --enable-tests --enable-benchmarks+ else+ curl -sSL https://get.haskellstack.org/ | sh+ stack --version+ stack build --system-ghc --test --bench --no-run-tests --no-run-benchmarks --ghc-options=-Werror+ fi++script:+ - |+ if [ -z "$STACK_YAML" ]; then+ cabal v2-test --enable-tests+ else+ stack test --system-ghc+ fi++notifications:+ email: false
+ examples/full-batteries/CHANGELOG.md view
@@ -0,0 +1,11 @@+# Changelog++`full-batteries` uses [PVP Versioning][1].+The changelog is available [on GitHub][2].++## 0.0.0.0++* Initially created.++[1]: https://pvp.haskell.org+[2]: https://github.com/kowainik/full-batteries/releases
+ examples/full-batteries/CONTRIBUTING.md view
@@ -0,0 +1,1 @@+This is contributing guide
+ examples/full-batteries/LICENSE view
@@ -0,0 +1,21 @@+MIT License++Copyright (c) 2018 Kowainik++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ examples/full-batteries/README.md view
@@ -0,0 +1,11 @@+# full-batteries++[](https://github.com/kowainik/full-batteries/actions)+[](https://travis-ci.org/kowainik/full-batteries)+[](https://ci.appveyor.com/project/kowainik/full-batteries)+[](https://hackage.haskell.org/package/full-batteries)+[](http://stackage.org/lts/package/full-batteries)+[](http://stackage.org/nightly/package/full-batteries)+[](LICENSE)++Full-featured test project
+ examples/full-batteries/app/Main.hs view
@@ -0,0 +1,7 @@+module Main (main) where++import FullBatteries (someFunc)+++main :: IO ()+main = someFunc
+ examples/full-batteries/appveyor.yml view
@@ -0,0 +1,34 @@+clone_folder: "c:\\WORK"+clone_depth: 5++# Do not build feature branch with open Pull Requests+skip_branch_with_pr: true++platform:+ - x86_64++cache:+ - "C:\\SR"+ - dist-newstyle++environment:+ global:+ CABOPTS: --store-dir=C:\\SR++ matrix:+ - GHCVER: 8.8.3++install:+ - choco source add -n mistuke -s https://www.myget.org/F/mistuke/api/v2+ - choco install -y cabal --version 2.4.1.0+ - choco install -y ghc --version 8.8.3+ - refreshenv++before_build:+ - cabal --version+ - ghc --version+ - cabal %CABOPTS% v2-update++build_script:+ - cabal %CABOPTS% v2-build --enable-tests+ - cabal %CABOPTS% v2-test --enable-tests
+ examples/full-batteries/benchmark/Main.hs view
@@ -0,0 +1,7 @@+module Main (main) where++import Gauge.Main+++main :: IO ()+main = defaultMain [bench "const" (whnf const ())]
+ examples/full-batteries/extra.txt view
@@ -0,0 +1,1 @@+See full content of the file [here](@github)
+ examples/full-batteries/full-batteries.cabal view
@@ -0,0 +1,75 @@+cabal-version: 2.4+name: full-batteries+version: 0.0.0.0+synopsis: Full-featured test project+description: Full-featured test project+homepage: https://github.com/kowainik/full-batteries+bug-reports: https://github.com/kowainik/full-batteries/issues+license: MIT+license-file: LICENSE+author: Kowainik+maintainer: Kowainik <xrom.xkov@gmail.com>+copyright: 2020 Kowainik+category: Testing+build-type: Simple+extra-doc-files: README.md+ CHANGELOG.md+tested-with: GHC == 8.0.2+ GHC == 8.2.2+ GHC == 8.4.4+ GHC == 8.6.5+ GHC == 8.8.3++source-repository head+ type: git+ location: https://github.com/kowainik/full-batteries.git++common common-options+ build-depends: base >= 4.9.1.0 && < 4.14+ , relude+ + mixins: base hiding (Prelude)+ , relude (Relude as Prelude)+ + ghc-options: -Wcompat+ -Widentities++ default-language: Haskell2010+ default-extensions: ConstraintKinds+ LambdaCase+ OverloadedStrings++library+ import: common-options+ hs-source-dirs: src+ exposed-modules: FullBatteries++executable full-batteries+ import: common-options+ hs-source-dirs: app+ main-is: Main.hs+ build-depends: full-batteries+ ghc-options: -threaded+ -rtsopts+ -with-rtsopts=-N++test-suite full-batteries-test+ import: common-options+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Spec.hs+ build-depends: full-batteries+ ghc-options: -threaded+ -rtsopts+ -with-rtsopts=-N++benchmark full-batteries-benchmark+ import: common-options+ type: exitcode-stdio-1.0+ hs-source-dirs: benchmark+ main-is: Main.hs+ build-depends: gauge+ , full-batteries+ ghc-options: -threaded+ -rtsopts+ -with-rtsopts=-N
+ examples/full-batteries/src/FullBatteries.hs view
@@ -0,0 +1,15 @@+{- |+Copyright: (c) 2020 Kowainik+SPDX-License-Identifier: MIT+Maintainer: Kowainik <xrom.xkov@gmail.com>++Full-featured test project+-}++module FullBatteries+ ( someFunc+ ) where+++someFunc :: IO ()+someFunc = putStrLn ("someFunc" :: String)
+ examples/full-batteries/stack-8.0.2.yaml view
@@ -0,0 +1,1 @@+resolver: lts-9.21
+ examples/full-batteries/stack-8.2.2.yaml view
@@ -0,0 +1,1 @@+resolver: lts-11.22
+ examples/full-batteries/stack-8.4.4.yaml view
@@ -0,0 +1,1 @@+resolver: lts-12.26
+ examples/full-batteries/stack-8.6.5.yaml view
@@ -0,0 +1,1 @@+resolver: lts-14.27
+ examples/full-batteries/stack.yaml view
@@ -0,0 +1,1 @@+resolver: lts-15.5
+ examples/full-batteries/test/Spec.hs view
@@ -0,0 +1,5 @@+module Main (main) where+++main :: IO ()+main = putStrLn ("Test suite is not implemented" :: String)
+ examples/stack-full/.gitignore view
@@ -0,0 +1,54 @@+### Haskell+dist+dist-*+cabal-dev+*.o+*.hi+*.chi+*.chs.h+*.dyn_o+*.dyn_hi+*.prof+*.aux+*.hp+*.eventlog+.virtualenv+.hsenv+.hpc+.cabal-sandbox/+cabal.sandbox.config+cabal.config+cabal.project.local+.ghc.environment.*+.HTF/+# Stack+.stack-work/+stack.yaml.lock++### IDE/support+# Vim+[._]*.s[a-v][a-z]+[._]*.sw[a-p]+[._]s[a-v][a-z]+[._]sw[a-p]+*~+tags++# IntellijIDEA+.idea/+.ideaHaskellLib/+*.iml++# Atom+.haskell-ghc-mod.json++# VS+.vscode/++# Emacs+*#+.dir-locals.el+TAGS++# other+.DS_Store
+ examples/stack-full/.travis.yml view
@@ -0,0 +1,45 @@+sudo: true+language: haskell++git:+ depth: 5++cabal: "2.4"++cache:+ directories:+ + - "$HOME/.stack"+ - "$TRAVIS_BUILD_DIR/.stack-work"++matrix:+ include:+ + + - ghc: 8.4.4+ env: STACK_YAML="$TRAVIS_BUILD_DIR/stack-8.4.4.yaml"+ + - ghc: 8.6.5+ env: STACK_YAML="$TRAVIS_BUILD_DIR/stack-8.6.5.yaml"+ + - ghc: 8.8.3+ env: STACK_YAML="$TRAVIS_BUILD_DIR/stack.yaml"+ + allow_failures:+ + - ghc: 8.4.4+ env: STACK_YAML="$TRAVIS_BUILD_DIR/stack-8.4.4.yaml"++install:+ # HLint check+ - curl -sSL https://raw.github.com/ndmitchell/neil/master/misc/travis.sh | sh -s -- hlint .++ - curl -sSL https://get.haskellstack.org/ | sh+ - stack --version+ - stack build --system-ghc --test --bench --no-run-tests --no-run-benchmarks --ghc-options=-Werror++script:+ - stack test --system-ghc++notifications:+ email: false
+ examples/stack-full/CHANGELOG.md view
@@ -0,0 +1,11 @@+# Changelog++`stack-full` uses [PVP Versioning][1].+The changelog is available [on GitHub][2].++## 0.0.0.0++* Initially created.++[1]: https://pvp.haskell.org+[2]: https://github.com/kowainik/stack-full/releases
+ examples/stack-full/LICENSE view
@@ -0,0 +1,29 @@+BSD 3-Clause License++Copyright (c) 2020, Kowainik+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++1. Redistributions of source code must retain the above copyright notice, this+ list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright notice,+ this list of conditions and the following disclaimer in the documentation+ and/or other materials provided with the distribution.++3. Neither the name of the copyright holder nor the names of its+ contributors may be used to endorse or promote products derived from+ this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ examples/stack-full/README.md view
@@ -0,0 +1,10 @@+# stack-full++[](https://travis-ci.org/kowainik/stack-full)+[](https://ci.appveyor.com/project/kowainik/stack-full)+[](https://hackage.haskell.org/package/stack-full)+[](http://stackage.org/lts/package/stack-full)+[](http://stackage.org/nightly/package/stack-full)+[](LICENSE)++Stack-only example with all integrations
+ examples/stack-full/app/Main.hs view
@@ -0,0 +1,7 @@+module Main (main) where++import StackFull (someFunc)+++main :: IO ()+main = someFunc
+ examples/stack-full/appveyor.yml view
@@ -0,0 +1,30 @@+clone_depth: 5++# Do not build feature branch with open Pull Requests+skip_branch_with_pr: true++environment:+ STACK_ROOT: C:\sr+ STACK_VERSION: 2.1.1++ # Workaround a gnarly bug https://github.com/haskell/cabal/issues/5386+ # See: https://www.fpcomplete.com/blog/2018/06/sed-a-debugging-story+ # TODO: check if it's fixed once we switch to lst-13 and GHC 8.6+ TMP: "c:\\tmp"++ matrix:+ - STACK_YAML: stack.yaml++cache:+ - "%STACK_ROOT% -> %STACK_YAML%, appveyor.yml"+ - ".stack-work -> %STACK_YAML%, appveyor.yml"++install:+ - choco install -y haskell-stack --version %STACK_VERSION%+ - stack setup > nul++build_script:+ - stack build --test --bench --no-run-tests --no-run-benchmarks --ghc-options=-Werror++test_script:+ - stack test
+ examples/stack-full/benchmark/Main.hs view
@@ -0,0 +1,7 @@+module Main (main) where++import Gauge.Main+++main :: IO ()+main = defaultMain [bench "const" (whnf const ())]
+ examples/stack-full/src/StackFull.hs view
@@ -0,0 +1,15 @@+{- |+Copyright: (c) 2020 Kowainik+SPDX-License-Identifier: BSD-3-Clause+Maintainer: Kowainik <xrom.xkov@gmail.com>++Stack-only example with all integrations+-}++module StackFull+ ( someFunc+ ) where+++someFunc :: IO ()+someFunc = putStrLn ("someFunc" :: String)
+ examples/stack-full/stack-8.4.4.yaml view
@@ -0,0 +1,1 @@+resolver: lts-12.26
+ examples/stack-full/stack-8.6.5.yaml view
@@ -0,0 +1,1 @@+resolver: lts-14.27
+ examples/stack-full/stack-full.cabal view
@@ -0,0 +1,84 @@+cabal-version: 2.4+name: stack-full+version: 0.0.0.0+synopsis: Stack-only example with all integrations+description: Stack-only example with all integrations+homepage: https://github.com/kowainik/stack-full+bug-reports: https://github.com/kowainik/stack-full/issues+license: BSD-3-Clause+license-file: LICENSE+author: Kowainik+maintainer: Kowainik <xrom.xkov@gmail.com>+copyright: 2020 Kowainik+category: Testing+build-type: Simple+extra-doc-files: README.md+ CHANGELOG.md+tested-with: GHC == 8.4.4+ GHC == 8.6.5+ GHC == 8.8.3++source-repository head+ type: git+ location: https://github.com/kowainik/stack-full.git++common common-options+ build-depends: base >= 4.11.1.0 && < 4.14+ , relude+ + mixins: base hiding (Prelude)+ , relude (Relude as Prelude)+ + ghc-options: -Wall+ -Wcompat+ -Widentities+ -Wincomplete-uni-patterns+ -Wincomplete-record-updates+ if impl(ghc >= 8.0)+ ghc-options: -Wredundant-constraints+ if impl(ghc >= 8.2)+ ghc-options: -fhide-source-paths+ if impl(ghc >= 8.4)+ ghc-options: -Wmissing-export-lists+ -Wpartial-fields+ if impl(ghc >= 8.8)+ ghc-options: -Wmissing-deriving-strategies++ default-language: Haskell2010+ default-extensions: DeriveGeneric+ TypeApplications++library+ import: common-options+ hs-source-dirs: src+ exposed-modules: StackFull++executable stack-full+ import: common-options+ hs-source-dirs: app+ main-is: Main.hs+ build-depends: stack-full+ ghc-options: -threaded+ -rtsopts+ -with-rtsopts=-N++test-suite stack-full-test+ import: common-options+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Spec.hs+ build-depends: stack-full+ ghc-options: -threaded+ -rtsopts+ -with-rtsopts=-N++benchmark stack-full-benchmark+ import: common-options+ type: exitcode-stdio-1.0+ hs-source-dirs: benchmark+ main-is: Main.hs+ build-depends: gauge+ , stack-full+ ghc-options: -threaded+ -rtsopts+ -with-rtsopts=-N
+ examples/stack-full/stack.yaml view
@@ -0,0 +1,1 @@+resolver: lts-15.5
+ examples/stack-full/test/Spec.hs view
@@ -0,0 +1,5 @@+module Main (main) where+++main :: IO ()+main = putStrLn ("Test suite is not implemented" :: String)
+ examples/summoner-default.toml view
@@ -0,0 +1,87 @@+# This file is automatically generated by Summoner.+# Edit required fields, uncomment additional settings or delete irrelevant+# lines for the personalized configuration.++# GitHub user name+owner = "kowainik"++# First and last name+fullName = "Kowainik"++# Email used at GitHub+email = "xrom.xkov@gmail.com"++# Default OSS license for your projects.+# Run the 'summon show license' command to see the list of supported licenses.+license = "MIT"++# Create projects with the Cabal support+# cabal = true++# Create projects with the Stack support+# stack = true++# Should your projects have the library stanza by default?+# lib = true++# Should your projects have an executable stanza by default?+# exe = true++# Should your projects have the test-suite stanza by default?+# test = true++# Should your projects have the benchmark stanza by default?+# bench = true++# Summoner suports 'git' version control system and GitHub as a platform for+# hosting 'git' repos. The following set of flags controls integration with GitHub.+# github = true # enabled GitHub integration+# noUpload = true # Init git repo, but don't create it on GitHub+# private = true # create private repos by default+# gitignore = # list of additional entries to be added in .gitignore+# [ "build"+# , "result"+# ]++# Summoner supports various CI services. Uncomment those you want to use by default.+# githubActions = true # GitHub Actions CI+# travis = true # Travis CI+# appveyor = true # AppVeyor CI++# List of additional GHC versions to support besides 8.8.3.+# Run the 'summon show ghc' command to see the list of all supported GHC versions.+# ghcVersions = ["8.4.4", "8.6.5"]++# List of default-extensions in the .cabal file+# extensions = [ "ConstraintKinds"+# , "DeriveGeneric"+# , "GeneralizedNewtypeDeriving"+# , "InstanceSigs"+# , "KindSignatures"+# , "LambdaCase"+# , "OverloadedStrings"+# , "RecordWildCards"+# , "ScopedTypeVariables"+# , "StandaloneDeriving"+# , "TupleSections"+# , "TypeApplications"+# , "ViewPatterns"+# ]++# List of additional files to add after creating the project+# files =+# [ { path = ".stylish-haskell.yaml"+# , url = "https://raw.githubusercontent.com/kowainik/org/master/.stylish-haskell.yaml"+# }+# , { path = ".github/CODEOWNERS"+# , raw = "* @kowainik"+# }+# , { path = "src/Foo.hs"+# , local = "/home/user/.default/Foo.hs"+# }+# ]++# Alternative Prelude to be used instead of the default one if you prefer+# [prelude]+# package = "relude"+# module = "Relude"
src/Summoner.hs view
@@ -18,6 +18,7 @@ import Summoner.Default as Summoner import Summoner.GhcVer as Summoner import Summoner.License as Summoner+import Summoner.Mode as Summoner import Summoner.Project as Summoner import Summoner.Question as Summoner import Summoner.Settings as Summoner
src/Summoner/Ansi.hs view
@@ -7,20 +7,9 @@ -} module Summoner.Ansi- ( Color (..)+ ( boldDefault , putStrFlush- , beautyPrint- , boldCode- , blueCode- , bold- , boldText- , boldDefault- , italic- , redCode- , reset- , resetCode , prompt- , setColor , successMessage , warningMessage , errorMessage@@ -28,10 +17,10 @@ , skipMessage ) where -import System.Console.ANSI (Color (..), ColorIntensity (Vivid), ConsoleIntensity (BoldIntensity),- ConsoleLayer (Foreground), SGR (..), setSGR, setSGRCode)+import Colourista (blue, bold, formatWith) import System.IO (hFlush) +import qualified Colourista import qualified Data.Text as T @@ -41,57 +30,23 @@ putText msg hFlush stdout -setColor :: Color -> IO ()-setColor color = setSGR [SetColor Foreground Vivid color]---- | Starts bold printing.-bold :: IO ()-bold = setSGR [SetConsoleIntensity BoldIntensity]--italic :: IO ()-italic = setSGR [SetItalicized True]---- | Resets all previous settings.-reset :: IO ()-reset = do- setSGR [Reset]- hFlush stdout---- | Takes list of formatting options, prints text using this format options.-beautyPrint :: [IO ()] -> Text -> IO ()-beautyPrint formats msg = do- sequence_ formats- putText msg- reset-+{- | Read 'Text' from standard input after arrow prompt.+-} prompt :: IO Text prompt = do- setColor Blue- putStrFlush " -> "- reset+ putStrFlush $ formatWith [blue] " -> " T.strip <$> getLine -boldText :: Text -> IO ()-boldText message = bold >> putStrFlush message >> reset--boldDefault :: Text -> IO ()-boldDefault message = boldText (" [" <> message <> "]")--colorMessage :: Color -> Text -> IO ()-colorMessage color message = do- setColor color- putTextLn $ " " <> message- reset+boldDefault :: Text -> Text+boldDefault message = formatWith [bold] $ " [" <> message <> "]" errorMessage, warningMessage, successMessage, infoMessage, skipMessage :: Text -> IO ()-errorMessage = colorMessage Red-warningMessage = colorMessage Yellow-successMessage = colorMessage Green-infoMessage = colorMessage Blue-skipMessage = colorMessage Cyan+errorMessage = Colourista.errorMessage . indent+warningMessage = Colourista.warningMessage . indent+successMessage = Colourista.successMessage . indent+infoMessage = Colourista.infoMessage . indent+skipMessage = Colourista.skipMessage . indent -blueCode, boldCode, redCode, resetCode :: String-redCode = setSGRCode [SetColor Foreground Vivid Red]-blueCode = setSGRCode [SetColor Foreground Vivid Blue]-boldCode = setSGRCode [SetConsoleIntensity BoldIntensity]-resetCode = setSGRCode [Reset]+-- | Add 2 spaces in front.+indent :: Text -> Text+indent = (" " <>)
src/Summoner/CLI.hs view
@@ -22,37 +22,39 @@ , summonCli -- * Runners+ , runConfig , runScript -- * Common helper functions , getFinalConfig+ , getCustomLicenseText ) where +import Colourista (blue, bold, formatWith) import Data.Version (Version, showVersion)-import Development.GitRev (gitCommitDate, gitDirty, gitHash)+import Development.GitRev (gitCommitDate, gitHash) import NeatInterpolation (text) import Options.Applicative (Parser, ParserInfo, ParserPrefs, argument, command, customExecParser, flag, fullDesc, help, helpLongEquals, helper, info, infoFooter,- infoHeader, infoOption, long, maybeReader, metavar, option, optional,- prefs, progDesc, short, showHelpOnEmpty, strArgument, strOption,+ infoHeader, infoOption, long, maybeReader, metavar, option, prefs,+ progDesc, short, showDefault, showHelpOnEmpty, strArgument, strOption, subparser, subparserInline, switch, value) import Options.Applicative.Help.Chunk (stringChunk) import Relude.Extra.Enum (universe)-import Relude.Extra.Validation (Validation (..))-import Shellmet (($?), ($|)) import System.Directory (doesFileExist) import System.Info (os)+import Validation (Validation (..)) -import Summoner.Ansi (blueCode, boldCode, errorMessage, infoMessage, redCode, resetCode,- successMessage, warningMessage)+import Summoner.Ansi (errorMessage, infoMessage, successMessage, warningMessage) import Summoner.Config (Config, ConfigP (..), PartialConfig, defaultConfig, finalise,- loadFileConfig)+ guessConfigFromGit, loadFileConfig) import Summoner.CustomPrelude (CustomPrelude (..)) import Summoner.Decision (Decision (..))-import Summoner.Default (defaultConfigFile, defaultGHC)+import Summoner.Default (currentYear, defaultConfigFile, defaultConfigFileContent, defaultGHC) import Summoner.GhcVer (GhcVer, ghcTable, parseGhcVer)-import Summoner.License (License (..), LicenseName (..), fetchLicense, parseLicenseName,+import Summoner.License (License (..), LicenseName (..), fetchLicenseCustom, parseLicenseName, showLicenseWithDesc)+import Summoner.Mode (ConnectMode (..), Interactivity (..), isOffline) import Summoner.Project (generateProject) import Summoner.Settings (Tool, parseTool) import Summoner.Template.Script (scriptFile)@@ -85,7 +87,36 @@ New opts -> runNew opts Script opts -> runScript opts ShowInfo opts -> runShow opts+ Config opts -> runConfig opts ++{- | Runs @config@ command++@+Usage: summon config [-f|--file=FILENAME]+ Create a default TOML configuration file for summoner++Available options:+ -h,--help Show this help text+ -f,--file=FILENAME Path to the toml file with configurations. If not+ specified '~/.summoner.toml' will be used by default+@+-}+runConfig :: ConfigOpts -> IO ()+runConfig ConfigOpts{..} = do+ configFile <- whenNothing configOptsName defaultConfigFile+ let configFileTxt = toText configFile+ alreadyExist <- doesFileExist configFile+ if alreadyExist+ then do+ warningMessage $ "File '" <> configFileTxt <> "' already exits."+ infoMessage "Use 'summon config --file <path>' to specify another path."+ exitFailure+ else do+ writeFileText configFile defaultConfigFileContent+ infoMessage $ "Created default configuration file: " <> configFileTxt+ infoMessage "Open the file using the editor of your choice."+ {- | Runs @show@ command. @@@ -115,12 +146,22 @@ showBulletList @LicenseName show universe -- get and show a license`s text Just licenseName -> do- fetchedLicense <- fetchLicense licenseName- putTextLn $ unLicense fetchedLicense+ licenseCustomText <- getCustomLicenseText licenseName+ putTextLn $ unLicense licenseCustomText where showBulletList :: (a -> Text) -> [a] -> IO () showBulletList showT = mapM_ (infoMessage . T.append "➤ " . showT) +-- | Get the customized License text for @summon show license NAME@ command.+getCustomLicenseText :: LicenseName -> IO License+getCustomLicenseText licenseName = do+ year <- currentYear+ guessConfig <- guessConfigFromGit+ fetchLicenseCustom+ licenseName+ (fromMaybe "YOUR NAME" $ getLast $ cFullName guessConfig)+ year+ {- | Runs @script@ command. @@@ -166,7 +207,7 @@ -- get the final config finalConfig <- getFinalConfig newOpts -- Generate the project.- generateProject newOptsOffline newOptsProjectName finalConfig+ generateProject newOptsInteractivity newOptsConnectMode newOptsProjectName finalConfig -- | By the given 'NewOpts' return the final configurations. getFinalConfig :: NewOpts -> IO Config@@ -175,7 +216,7 @@ fileConfig <- readFileConfig newOptsIgnoreFile newOptsConfigFile -- guess config from the global ~/.gitconfig file- gitConfig <- guessFromGit+ gitConfig <- guessConfigFromGit -- union all possible configs let unionConfig = defaultConfig <> gitConfig <> fileConfig <> newOptsCliConfig@@ -184,19 +225,7 @@ case finalise unionConfig of Success c -> pure c Failure msgs -> for_ msgs errorMessage >> exitFailure- where- guessFromGit :: IO PartialConfig- guessFromGit = do- gitOwner <- (Just <$> "git" $| ["config", "user.login"]) $? pure Nothing- gitName <- (Just <$> "git" $| ["config", "user.name"]) $? pure Nothing- gitEmail <- (Just <$> "git" $| ["config", "user.email"]) $? pure Nothing- pure $ defaultConfig- { cOwner = Last gitOwner- , cFullName = Last gitName- , cEmail = Last gitEmail- } - -- | Reads and parses the given config file. If no file is provided the default -- configuration returned. readFileConfig :: Bool -> Maybe FilePath -> IO PartialConfig@@ -230,17 +259,20 @@ | Script ScriptOpts -- | @show@ command shows supported licenses or GHC versions | ShowInfo ShowOpts+ -- | @config@ command creates the TOML configuration file+ | Config ConfigOpts --- | Options parsed with @new@ command+-- | Options parsed with the @new@ command data NewOpts = NewOpts- { newOptsProjectName :: Text -- ^ project name- , newOptsIgnoreFile :: Bool -- ^ ignore all config files if 'True'- , newOptsOffline :: Bool -- ^ Offline mode- , newOptsConfigFile :: Maybe FilePath -- ^ file with custom configuration- , newOptsCliConfig :: PartialConfig -- ^ config gathered during CLI+ { newOptsProjectName :: !Text -- ^ Project name+ , newOptsIgnoreFile :: !Bool -- ^ Ignore all config files if 'True'+ , newOptsConnectMode :: !ConnectMode -- ^ 'Online'/'Offline' mode+ , newOptsInteractivity :: !Interactivity -- ^ Interactive or non-interactive mode is on?+ , newOptsConfigFile :: !(Maybe FilePath) -- ^ File with custom configuration+ , newOptsCliConfig :: !PartialConfig -- ^ Config gathered via command-line } --- | Options parsed with @script@ command+-- | Options parsed with the @script@ command data ScriptOpts = ScriptOpts { scriptOptsTool :: !Tool -- ^ Build tool: `cabal` or `stack` , scriptOptsName :: !FilePath -- ^ File path to the script@@ -252,6 +284,11 @@ = GhcList | LicenseList (Maybe String) +-- | Options parsed with the @config@ command+newtype ConfigOpts = ConfigOpts+ { configOptsName :: Maybe FilePath+ }+ ---------------------------------------------------------------------------- -- Parsers ----------------------------------------------------------------------------@@ -271,23 +308,32 @@ <> help "Show summoner's version" summonerVersion :: Version -> String-summonerVersion version = toString- $ intercalate "\n"- $ [sVersion, sHash, sDate] ++ [sDirty | $(gitDirty)]+summonerVersion version = intercalate "\n" [sVersion, sHash, sDate] where- sVersion = blueCode <> boldCode <> "Summoner " <> "v" <> showVersion version <> resetCode- sHash = " ➤ " <> blueCode <> boldCode <> "Git revision: " <> resetCode <> $(gitHash)- sDate = " ➤ " <> blueCode <> boldCode <> "Commit date: " <> resetCode <> $(gitCommitDate)- sDirty = redCode <> "There are non-committed files." <> resetCode+ sVersion, sHash, sDate :: String+ sVersion = formatWith [blue, bold] $ "Summoner " <> "v" <> showVersion version+ sHash = " ➤ " <> formatWith [blue, bold] "Git revision: " <> $(gitHash)+ sDate = " ➤ " <> formatWith [blue, bold] "Commit date: " <> $(gitCommitDate) --- All possible commands.+-- | All possible commands. summonerP :: Parser Command summonerP = subparser $ command "new" (info (helper <*> newP) $ progDesc "Create a new Haskell project") <> command "script" (info (helper <*> scriptP) $ progDesc "Create a new Haskell script") <> command "show" (info (helper <*> showP) $ progDesc "Show supported licenses or ghc versions")+ <> command "config" (info (helper <*> configP) $ progDesc "Create a default TOML configuration file for summoner") ----------------------------------------------------------------------------+-- @config@ command parsers+----------------------------------------------------------------------------++-- | Parses options of the @config@ command.+configP :: Parser Command+configP = do+ configOptsName <- optional configFileP+ pure $ Config ConfigOpts{..}++---------------------------------------------------------------------------- -- @show@ command parsers ---------------------------------------------------------------------------- @@ -332,6 +378,7 @@ ( long "ghc" <> short 'g' <> value defaultGHC+ <> showDefault <> metavar "GHC_VERSION" <> help "Version of the compiler to be used for script" )@@ -343,11 +390,12 @@ -- | Parses options of the @new@ command. newP :: Parser Command newP = do- newOptsProjectName <- strArgument (metavar "PROJECT_NAME")- newOptsIgnoreFile <- ignoreFileP- noUpload <- noUploadP- newOptsOffline <- offlineP- newOptsConfigFile <- optional fileP+ newOptsProjectName <- strArgument (metavar "PROJECT_NAME")+ newOptsIgnoreFile <- ignoreFileP+ noUpload <- noUploadP+ newOptsConnectMode <- connectModeP+ newOptsInteractivity <- interactivityP+ newOptsConfigFile <- optional configFileP cabal <- cabalP stack <- stackP preludePack <- optional preludePackP@@ -360,79 +408,96 @@ { cPrelude = Last $ CustomPrelude <$> preludePack <*> preludeMod , cCabal = cabal , cStack = stack- , cNoUpload = Any $ noUpload || newOptsOffline+ , cNoUpload = Any $ noUpload || isOffline newOptsConnectMode } , .. } targetsP :: Decision -> Parser PartialConfig targetsP d = do- cGitHub <- githubP d- cTravis <- travisP d- cAppVey <- appVeyorP d- cPrivate <- privateP d- cLib <- libraryP d- cExe <- execP d- cTest <- testP d- cBench <- benchmarkP d+ cGitHub <- githubP d+ cGhActions <- ghActionsP d+ cTravis <- travisP d+ cAppVey <- appVeyorP d+ cPrivate <- privateP d+ cLib <- libraryP d+ cExe <- execP d+ cTest <- testP d+ cBench <- benchmarkP d pure mempty- { cGitHub = cGitHub- , cTravis = cTravis- , cAppVey = cAppVey- , cPrivate= cPrivate- , cLib = cLib- , cExe = cExe- , cTest = cTest- , cBench = cBench+ { cGitHub = cGitHub+ , cGhActions = cGhActions+ , cTravis = cTravis+ , cAppVey = cAppVey+ , cPrivate = cPrivate+ , cLib = cLib+ , cExe = cExe+ , cTest = cTest+ , cBench = cBench } githubP :: Decision -> Parser Decision-githubP d = flag Idk d- $ long "github"- <> short 'g'- <> help "GitHub integration"+githubP d = flag Idk d $ mconcat+ [ long "github"+ , short 'g'+ , help "GitHub integration"+ ] +ghActionsP :: Decision -> Parser Decision+ghActionsP d = flag Idk d $ mconcat+ [ long "actions"+ , short 'a'+ , help "GitHub Actions CI"+ ]+ travisP :: Decision -> Parser Decision-travisP d = flag Idk d- $ long "travis"- <> short 'c'- <> help "Travis CI integration"+travisP d = flag Idk d $ mconcat+ [ long "travis"+ , short 'c'+ , help "Travis CI integration"+ ] appVeyorP :: Decision -> Parser Decision-appVeyorP d = flag Idk d- $ long "app-veyor"- <> short 'w'- <> help "AppVeyor CI integration"+appVeyorP d = flag Idk d $ mconcat+ [ long "app-veyor"+ , short 'w'+ , help "AppVeyor CI integration"+ ] privateP :: Decision -> Parser Decision-privateP d = flag Idk d- $ long "private"- <> short 'p'- <> help "Private repository"+privateP d = flag Idk d $ mconcat+ [ long "private"+ , short 'p'+ , help "Private repository"+ ] libraryP :: Decision -> Parser Decision-libraryP d = flag Idk d- $ long "library"- <> short 'l'- <> help "Library folder"+libraryP d = flag Idk d $ mconcat+ [ long "library"+ , short 'l'+ , help "Library folder"+ ] execP :: Decision -> Parser Decision-execP d = flag Idk d- $ long "exec"- <> short 'e'- <> help "Executable target"+execP d = flag Idk d $ mconcat+ [ long "exec"+ , short 'e'+ , help "Executable target"+ ] testP :: Decision -> Parser Decision-testP d = flag Idk d- $ long "test"- <> short 't'- <> help "Test target"+testP d = flag Idk d $ mconcat+ [ long "test"+ , short 't'+ , help "Test target"+ ] benchmarkP :: Decision -> Parser Decision-benchmarkP d = flag Idk d- $ long "benchmark"- <> short 'b'- <> help "Benchmarks"+benchmarkP d = flag Idk d $ mconcat+ [ long "benchmark"+ , short 'b'+ , help "Benchmarks"+ ] withP :: Parser PartialConfig withP = subparser $ mconcat@@ -447,44 +512,63 @@ ] ignoreFileP :: Parser Bool-ignoreFileP = switch $ long "ignore-config" <> help "Ignore configuration file"+ignoreFileP = switch $ mconcat+ [ long "ignore-config"+ , help "Ignore configuration file"+ ] noUploadP :: Parser Bool-noUploadP = switch $ long "no-upload" <> help "Do not upload to GitHub. Special case of the '--offline' flag."+noUploadP = switch $ mconcat+ [ long "no-upload"+ , help "Do not upload to GitHub. Special case of the '--offline' flag."+ ] -offlineP :: Parser Bool-offlineP = switch- $ long "offline"- <> help "Offline mode: create project with 'All Rights Reserved' license and without uploading to GitHub."+connectModeP :: Parser ConnectMode+connectModeP = flag Online Offline $ mconcat+ [ long "offline"+ , help "Offline mode: create a project with 'All Rights Reserved' license and without uploading to GitHub."+ ] -fileP :: Parser FilePath-fileP = strOption- $ long "file"- <> short 'f'- <> metavar "FILENAME"- <> help "Path to the toml file with configurations. If not specified '~/.summoner.toml' will be used if present"+interactivityP :: Parser Interactivity+interactivityP = flag Interactive NonInteractive $ mconcat+ [ long "non-interactive"+ , short 'n'+ , help "Non-interactive mode: create a project without interactive questions."+ ] +configFileP :: Parser FilePath+configFileP = strOption $ mconcat+ [ long "file"+ , short 'f'+ , metavar "FILENAME"+ , help "Path to the toml file with configurations. If not specified '~/.summoner.toml' will be used by default"+ ]+ preludePackP :: Parser Text-preludePackP = strOption- $ long "prelude-package"- <> metavar "PACKAGE_NAME"- <> help "Name for the package of the custom prelude to use in the project"+preludePackP = strOption $ mconcat+ [ long "prelude-package"+ , metavar "PACKAGE_NAME"+ , help "Name for the package of the custom prelude to use in the project"+ ] preludeModP :: Parser Text-preludeModP = strOption- $ long "prelude-module"- <> metavar "MODULE_NAME"- <> help "Name for the module of the custom prelude to use in the project"+preludeModP = strOption $ mconcat+ [ long "prelude-module"+ , metavar "MODULE_NAME"+ , help "Name for the module of the custom prelude to use in the project"+ ] cabalP :: Parser Decision-cabalP = flag Idk Yes- $ long "cabal"- <> help "Cabal support for the project"+cabalP = flag Idk Yes $ mconcat+ [ long "cabal"+ , help "Cabal support for the project"+ ] stackP :: Parser Decision-stackP = flag Idk Yes- $ long "stack"- <> help "Stack support for the project"+stackP = flag Idk Yes $ mconcat+ [ long "stack"+ , help "Stack support for the project"+ ] ---------------------------------------------------------------------------- -- Beauty util
src/Summoner/Config.hs view
@@ -1,7 +1,4 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}- {-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE StandaloneDeriving #-}@@ -24,22 +21,24 @@ , Config , configCodec , defaultConfig+ , guessConfigFromGit , finalise , loadFileConfig ) where import Data.List (lookup)-import Generics.Deriving.Monoid (GMonoid (..), gmemptydefault)-import Generics.Deriving.Semigroup (GSemigroup (..), gsappenddefault)+import Generic.Data (gmappend, gmempty)+import Shellmet (($?), ($|)) import Toml (Key, TomlBiMap, TomlCodec, (.=))-import Relude.Extra.Validation (Validation (..))+import Validation (Validation (..)) import Summoner.CustomPrelude (CustomPrelude (..), customPreludeT) import Summoner.Decision (Decision (..))+import Summoner.Default (defaultEmail, defaultFullName, defaultLicenseName, defaultOwner) import Summoner.GhcVer (GhcVer (..), parseGhcVer, showGhcVer) import Summoner.License (LicenseName (..), parseLicenseName)-import Summoner.Source (Source, sourceT, sourceCodec)+import Summoner.Source (Source, sourceCodec) import qualified Toml @@ -49,45 +48,40 @@ = Partial | Final +{- | Type family to map 'Phase' to the corresponding field. This is a+Higher-Kinded Data approach specialised to custom enumeration.+-}+infixl 3 :-+type family phase :- field where+ 'Partial :- field = Last field+ 'Final :- field = field+ -- | Potentially incomplete configuration.-data ConfigP (p :: Phase) = Config- { cOwner :: !(p :- Text)- , cFullName :: !(p :- Text)- , cEmail :: !(p :- Text)- , cLicense :: !(p :- LicenseName)- , cGhcVer :: !(p :- [GhcVer])- , cCabal :: !Decision- , cStack :: !Decision- , cGitHub :: !Decision- , cTravis :: !Decision- , cAppVey :: !Decision- , cPrivate :: !Decision- , cLib :: !Decision- , cExe :: !Decision- , cTest :: !Decision- , cBench :: !Decision- , cPrelude :: !(Last CustomPrelude)- , cExtensions :: ![Text]- , cGhcOptions :: ![Text] -- ^ GHC options to add to each stanza- , cGitignore :: ![Text]- , cStylish :: !(Last Source) -- ^ DEPRECATED: source to .stylish-haskell.yaml- , cContributing :: !(Last Source) -- ^ DEPRECATED: source to CONTRIBUTING.md- , cNoUpload :: !Any -- ^ Do not upload to the GitHub (even if enabled)- , cFiles :: !(Map FilePath Source) -- ^ Custom files+data ConfigP (p :: Phase) = ConfigP+ { cOwner :: !(p :- Text)+ , cFullName :: !(p :- Text)+ , cEmail :: !(p :- Text)+ , cLicense :: !(p :- LicenseName)+ , cGhcVer :: !(p :- [GhcVer])+ , cCabal :: !Decision+ , cStack :: !Decision+ , cGitHub :: !Decision+ , cGhActions :: !Decision+ , cTravis :: !Decision+ , cAppVey :: !Decision+ , cPrivate :: !Decision+ , cLib :: !Decision+ , cExe :: !Decision+ , cTest :: !Decision+ , cBench :: !Decision+ , cPrelude :: !(Last CustomPrelude)+ , cExtensions :: ![Text]+ , cGhcOptions :: ![Text] -- ^ GHC options to add to each stanza+ , cGitignore :: ![Text]+ , cNoUpload :: !Any -- ^ Do not upload to the GitHub (even if enabled)+ , cFiles :: !(Map FilePath Source) -- ^ Custom files } deriving stock (Generic) -deriving anyclass instance- ( GSemigroup (p :- Text)- , GSemigroup (p :- LicenseName)- , GSemigroup (p :- [GhcVer])- ) => GSemigroup (ConfigP p)--deriving anyclass instance- ( GMonoid (p :- Text)- , GMonoid (p :- LicenseName)- , GMonoid (p :- [GhcVer])- ) => GMonoid (ConfigP p)- deriving stock instance ( Eq (p :- Text) , Eq (p :- LicenseName)@@ -100,85 +94,71 @@ , Show (p :- [GhcVer]) ) => Show (ConfigP p) -infixl 3 :--type family phase :- field where- 'Partial :- field = Last field- 'Final :- field = field---- | Incomplete configurations.-type PartialConfig = ConfigP 'Partial---- | Complete configurations.-type Config = ConfigP 'Final- instance Semigroup PartialConfig where- (<>) = gsappenddefault+ (<>) = gmappend instance Monoid PartialConfig where- mempty = gmemptydefault+ mempty = gmempty mappend = (<>) -instance Ord k => GSemigroup (Map k v) where- gsappend = (<>)+-- | Incomplete configurations.+type PartialConfig = ConfigP 'Partial -instance Ord k => GMonoid (Map k v) where- gmempty = mempty- gmappend = (<>)+-- | Complete configurations.+type Config = ConfigP 'Final -- | Default 'Config' configurations. defaultConfig :: PartialConfig-defaultConfig = Config- { cOwner = Last (Just "kowainik")- , cFullName = Last (Just "Kowainik")- , cEmail = Last (Just "xrom.xkov@gmail.com")- , cLicense = Last (Just MIT)- , cGhcVer = Last (Just [])- , cCabal = Idk- , cStack = Idk- , cGitHub = Idk- , cTravis = Idk- , cAppVey = Idk- , cPrivate = Idk- , cLib = Idk- , cExe = Idk- , cTest = Idk- , cBench = Idk- , cPrelude = Last Nothing- , cExtensions = []- , cGhcOptions = []- , cGitignore = []- , cStylish = Last Nothing- , cContributing = Last Nothing- , cNoUpload = Any False- , cFiles = mempty+defaultConfig = ConfigP+ { cOwner = Last (Just defaultOwner)+ , cFullName = Last (Just defaultFullName)+ , cEmail = Last (Just defaultEmail)+ , cLicense = Last (Just defaultLicenseName)+ , cGhcVer = Last (Just [])+ , cCabal = Idk+ , cStack = Idk+ , cGitHub = Idk+ , cGhActions = Idk+ , cTravis = Idk+ , cAppVey = Idk+ , cPrivate = Idk+ , cLib = Idk+ , cExe = Idk+ , cTest = Idk+ , cBench = Idk+ , cPrelude = Last Nothing+ , cExtensions = []+ , cGhcOptions = []+ , cGitignore = []+ , cNoUpload = Any False+ , cFiles = mempty } -- | Identifies how to read 'Config' data from the @.toml@ file. configCodec :: TomlCodec PartialConfig-configCodec = Config- <$> Toml.last Toml.text "owner" .= cOwner- <*> Toml.last Toml.text "fullName" .= cFullName- <*> Toml.last Toml.text "email" .= cEmail- <*> Toml.last license "license" .= cLicense- <*> Toml.last ghcVerArr "ghcVersions" .= cGhcVer- <*> decision "cabal" .= cCabal- <*> decision "stack" .= cStack- <*> decision "github" .= cGitHub- <*> decision "travis" .= cTravis- <*> decision "appveyor" .= cAppVey- <*> decision "private" .= cPrivate- <*> decision "lib" .= cLib- <*> decision "exe" .= cExe- <*> decision "test" .= cTest- <*> decision "bench" .= cBench- <*> Toml.last preludeT "prelude" .= cPrelude- <*> textArr "extensions" .= cExtensions- <*> textArr "ghc-options" .= cGhcOptions- <*> textArr "gitignore" .= cGitignore- <*> Toml.last sourceT "stylish" .= cStylish- <*> Toml.last sourceT "contributing" .= cContributing- <*> Toml.any "noUpload" .= cNoUpload- <*> filesCodec "files" .= cFiles+configCodec = ConfigP+ <$> Toml.last Toml.text "owner" .= cOwner+ <*> Toml.last Toml.text "fullName" .= cFullName+ <*> Toml.last Toml.text "email" .= cEmail+ <*> Toml.last license "license" .= cLicense+ <*> Toml.last ghcVerArr "ghcVersions" .= cGhcVer+ <*> decision "cabal" .= cCabal+ <*> decision "stack" .= cStack+ <*> decision "github" .= cGitHub+ <*> decision "githubActions" .= cGhActions+ <*> decision "travis" .= cTravis+ <*> decision "appveyor" .= cAppVey+ <*> decision "private" .= cPrivate+ <*> decision "lib" .= cLib+ <*> decision "exe" .= cExe+ <*> decision "test" .= cTest+ <*> decision "bench" .= cBench+ <*> Toml.last preludeT "prelude" .= cPrelude+ <*> textArr "extensions" .= cExtensions+ <*> textArr "ghc-options" .= cGhcOptions+ <*> textArr "gitignore" .= cGitignore+ <*> Toml.any "noUpload" .= cNoUpload+ <*> filesCodec "files" .= cFiles where _GhcVer :: TomlBiMap GhcVer Toml.AnyValue _GhcVer = Toml._TextBy showGhcVer (maybeToRight "Wrong GHC version" . parseGhcVer)@@ -213,9 +193,24 @@ filesCodec :: Key -> TomlCodec (Map FilePath Source) filesCodec = Toml.map (Toml.string "path") sourceCodec +{- | Try to retrieve user information from Git config.+Return the 'PartialConfig' with the corresponding filled fields if the+information is applicable.+-}+guessConfigFromGit :: IO PartialConfig+guessConfigFromGit = do+ gitOwner <- (Just <$> "git" $| ["config", "user.login"]) $? pure Nothing+ gitName <- (Just <$> "git" $| ["config", "user.name"]) $? pure Nothing+ gitEmail <- (Just <$> "git" $| ["config", "user.email"]) $? pure Nothing+ pure $ defaultConfig+ { cOwner = Last gitOwner+ , cFullName = Last gitName+ , cEmail = Last gitEmail+ }+ -- | Make sure that all the required configurations options were specified. finalise :: PartialConfig -> Validation [Text] Config-finalise Config{..} = Config+finalise ConfigP{..} = ConfigP <$> fin "owner" cOwner <*> fin "fullName" cFullName <*> fin "email" cEmail@@ -224,6 +219,7 @@ <*> pure cCabal <*> pure cStack <*> pure cGitHub+ <*> pure cGhActions <*> pure cTravis <*> pure cAppVey <*> pure cPrivate@@ -235,8 +231,6 @@ <*> pure cExtensions <*> pure cGhcOptions <*> pure cGitignore- <*> pure cStylish- <*> pure cContributing <*> pure cNoUpload <*> pure cFiles where
src/Summoner/Decision.hs view
@@ -9,11 +9,10 @@ module Summoner.Decision ( Decision (..) , decisionToBool+ , decisionsToBools+ , promptDecisionToBool ) where -import Generics.Deriving.Monoid (GMonoid (..))-import Generics.Deriving.Semigroup (GSemigroup (..))- import Summoner.Question (YesNoPrompt (..), chooseYesNoBool, falseMessage, trueMessage) @@ -34,15 +33,26 @@ mempty = Idk mappend = (<>) -instance GSemigroup Decision where- gsappend = (<>)--instance GMonoid Decision where- gmempty = mempty- gmappend = (<>)+-- | Translate 'Decision' to 'Bool'.+decisionToBool :: Decision -> Bool+decisionToBool = \case+ Yes -> True+ Nop -> False+ Idk -> False -decisionToBool :: Decision -> YesNoPrompt -> IO Bool-decisionToBool decision target@YesNoPrompt {..} = case decision of+-- | Prompt the 'Decision' to terminal output depending on the value.+promptDecisionToBool :: Decision -> YesNoPrompt -> IO Bool+promptDecisionToBool decision target@YesNoPrompt {..} = case decision of Yes -> trueMessage yesNoTarget Nop -> falseMessage yesNoTarget Idk -> chooseYesNoBool target++-- | Translate a pair of dependant 'Decision's to the pair of 'Bool's.+decisionsToBools :: (Decision, Decision) -> (Bool, Bool)+decisionsToBools = \case+ (Idk, Idk) -> (True, True)+ (Yes, Idk) -> (True, False)+ (Idk, Yes) -> (False, True)+ (Nop, Idk) -> (False, True)+ (Idk, Nop) -> (True, False)+ (x, y) -> (decisionToBool x, decisionToBool y)
src/Summoner/Default.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE QuasiQuotes #-}+ {- | Copyright: (c) 2017-2019 Kowainik SPDX-License-Identifier: MPL-2.0@@ -9,17 +11,24 @@ module Summoner.Default ( defaultGHC , defaultCabal+ , defaultLicenseName+ , defaultOwner+ , defaultFullName+ , defaultEmail , defaultTomlFile , defaultConfigFile+ , defaultConfigFileContent , defaultDescription , currentYear ) where import Data.Time (getCurrentTime, toGregorian, utctDay)+import NeatInterpolation (text) import System.Directory (getHomeDirectory) import System.FilePath ((</>)) -import Summoner.GhcVer (GhcVer)+import Summoner.GhcVer (GhcVer, showGhcVer)+import Summoner.License (LicenseName (MIT)) -- | Default GHC version is the latest available.@@ -28,8 +37,20 @@ -- | Default version of the Cabal. defaultCabal :: Text-defaultCabal = "2.0"+defaultCabal = "2.4" +defaultLicenseName :: LicenseName+defaultLicenseName = MIT++defaultOwner :: Text+defaultOwner = "kowainik"++defaultFullName :: Text+defaultFullName = "Kowainik"++defaultEmail :: Text+defaultEmail = "xrom.xkov@gmail.com"+ defaultTomlFile :: FilePath defaultTomlFile = ".summoner.toml" @@ -44,3 +65,103 @@ now <- getCurrentTime let (year, _, _) = toGregorian $ utctDay now pure $ show year++{- | Default content of the @~/summoner.toml@ file to be generated by+the @summon config@ command.+-}+defaultConfigFileContent :: Text+defaultConfigFileContent = [text|+ # This file is automatically generated by Summoner.+ # Edit required fields, uncomment additional settings or delete irrelevant+ # lines for the personalized configuration.++ # GitHub user name+ owner = "$defaultOwner"++ # First and last name+ fullName = "$defaultFullName"++ # Email used at GitHub+ email = "$defaultEmail"++ # Default OSS license for your projects.+ # Run the 'summon show license' command to see the list of supported licenses.+ license = "$licenseName"++ # Create projects with the Cabal support+ # cabal = true++ # Create projects with the Stack support+ # stack = true++ # Should your projects have the library stanza by default?+ # lib = true++ # Should your projects have an executable stanza by default?+ # exe = true++ # Should your projects have the test-suite stanza by default?+ # test = true++ # Should your projects have the benchmark stanza by default?+ # bench = true++ # Summoner suports 'git' version control system and GitHub as a platform for+ # hosting 'git' repos. The following set of flags controls integration with GitHub.+ # github = true # enabled GitHub integration+ # noUpload = true # Init git repo, but don't create it on GitHub+ # private = true # create private repos by default+ # gitignore = # list of additional entries to be added in .gitignore+ # [ "build"+ # , "result"+ # ]++ # Summoner supports various CI services. Uncomment those you want to use by default.+ # githubActions = true # GitHub Actions CI+ # travis = true # Travis CI+ # appveyor = true # AppVeyor CI++ # List of additional GHC versions to support besides $defaultGhcVer.+ # Run the 'summon show ghc' command to see the list of all supported GHC versions.+ # ghcVersions = ["8.4.4", "8.6.5"]++ # List of default-extensions in the .cabal file+ # extensions = [ "ConstraintKinds"+ # , "DeriveGeneric"+ # , "GeneralizedNewtypeDeriving"+ # , "InstanceSigs"+ # , "KindSignatures"+ # , "LambdaCase"+ # , "OverloadedStrings"+ # , "RecordWildCards"+ # , "ScopedTypeVariables"+ # , "StandaloneDeriving"+ # , "TupleSections"+ # , "TypeApplications"+ # , "ViewPatterns"+ # ]++ # List of additional files to add after creating the project+ # files =+ # [ { path = ".stylish-haskell.yaml"+ # , url = "https://raw.githubusercontent.com/kowainik/org/master/.stylish-haskell.yaml"+ # }+ # , { path = ".github/CODEOWNERS"+ # , raw = "* @$defaultOwner"+ # }+ # , { path = "src/Foo.hs"+ # , local = "/home/user/.default/Foo.hs"+ # }+ # ]++ # Alternative Prelude to be used instead of the default one if you prefer+ # [prelude]+ # package = "relude"+ # module = "Relude"+|]+ where+ licenseName :: Text+ licenseName = show defaultLicenseName++ defaultGhcVer :: Text+ defaultGhcVer = showGhcVer defaultGHC
src/Summoner/GhcVer.hs view
@@ -24,8 +24,8 @@ import Data.List (maximum, minimum) import Relude.Extra.Enum (inverseMap, universe) -import qualified Text.Show as Show import qualified Data.Text as T+import qualified Text.Show as Show -- | Represents some selected set of GHC versions.@@ -35,7 +35,7 @@ | Ghc822 | Ghc844 | Ghc865- | Ghc881+ | Ghc883 deriving stock (Eq, Ord, Show, Enum, Bounded) -- | Converts 'GhcVer' into dot-separated string.@@ -46,11 +46,13 @@ Ghc822 -> "8.2.2" Ghc844 -> "8.4.4" Ghc865 -> "8.6.5"- Ghc881 -> "8.8.1"+ Ghc883 -> "8.8.3" --- | These are old GHC versions that are not working with default GHC versions when using Stack.+{- | These are old GHC versions that are not working with default GHC versions+when using Stack.+-} oldGhcs :: [GhcVer]-oldGhcs = [minBound .. Ghc802]+oldGhcs = [minBound .. Ghc844] parseGhcVer :: Text -> Maybe GhcVer parseGhcVer = inverseMap showGhcVer@@ -62,8 +64,8 @@ Ghc802 -> "lts-9.21" Ghc822 -> "lts-11.22" Ghc844 -> "lts-12.26"- Ghc865 -> "lts-14.18"- Ghc881 -> "nightly-2019-12-25"+ Ghc865 -> "lts-14.27"+ Ghc883 -> "lts-15.5" -- | Represents PVP versioning (4 numbers). data Pvp = Pvp@@ -85,7 +87,7 @@ Ghc822 -> Pvp 4 10 1 0 Ghc844 -> Pvp 4 11 1 0 Ghc865 -> Pvp 4 12 0 0- Ghc881 -> Pvp 4 13 0 0+ Ghc883 -> Pvp 4 13 0 0 -- | Returns corresponding @base@ version of the given GHC version. baseVer :: GhcVer -> Text@@ -144,4 +146,4 @@ <> " " <> T.justifyLeft 14 ' ' gmBase <> " "- <> T.justifyLeft 18 ' ' gmResolver+ <> gmResolver
src/Summoner/License.hs view
@@ -1,5 +1,5 @@ {- |-Copyright: (c) 2017-2019 Kowainik+Copyright: (c) 2017-2020 Kowainik SPDX-License-Identifier: MPL-2.0 Maintainer: Kowainik <xrom.xkov@gmail.com> @@ -10,11 +10,11 @@ module Summoner.License ( LicenseName(..) , License(..)- , cabalLicense , customizeLicense , githubLicenseQueryNames , parseLicenseName , fetchLicense+ , fetchLicenseCustom , licenseShortDesc , showLicenseWithDesc ) where@@ -41,21 +41,23 @@ | AGPL3 | Apache20 | MPL20- | None+ | ISC+ | NONE deriving stock (Eq, Ord, Enum, Bounded, Generic) instance Show LicenseName where show MIT = "MIT"- show BSD2 = "BSD2"- show BSD3 = "BSD3"- show GPL2 = "GPL-2"- show GPL3 = "GPL-3"- show LGPL21 = "LGPL-2.1"- show LGPL3 = "LGPL-3"- show AGPL3 = "AGPL-3"+ show BSD2 = "BSD-2-Clause"+ show BSD3 = "BSD-3-Clause"+ show GPL2 = "GPL-2.0-only"+ show GPL3 = "GPL-3.0-only"+ show LGPL21 = "LGPL-2.1-only"+ show LGPL3 = "LGPL-3.0-only"+ show AGPL3 = "AGPL-3.0-only" show Apache20 = "Apache-2.0" show MPL20 = "MPL-2.0"- show None = "None"+ show ISC = "ISC"+ show NONE = "NONE" newtype License = License { unLicense :: Text@@ -65,11 +67,6 @@ instance FromJSON License where parseJSON = withObject "License" $ \o -> License <$> o .: "body" --- | As it will be shown in the @cabal@ file.-cabalLicense :: LicenseName -> Text-cabalLicense None = "AllRightsReserved"-cabalLicense l = show l- -- | Used for downloading the license text form @Github@. githubLicenseQueryNames :: LicenseName -> Text githubLicenseQueryNames = \case@@ -83,7 +80,8 @@ AGPL3 -> "agpl-3.0" Apache20 -> "apache-2.0" MPL20 -> "mpl-2.0"- None -> "none"+ ISC -> "isc"+ NONE -> "none" parseLicenseName :: Text -> Maybe LicenseName parseLicenseName = inverseMap show@@ -91,7 +89,7 @@ -- | Replaces name/year placeholders with the actual data. customizeLicense :: LicenseName -> License -> Text -> Text -> License customizeLicense l license@(License licenseText) nm year- | l `elem` [MIT, BSD2, BSD3] = License updatedLicenseText+ | l `elem` [MIT, BSD2, BSD3, ISC] = License updatedLicenseText | otherwise = license where updatedLicenseText :: Text@@ -102,14 +100,17 @@ afterN = T.tail $ T.dropWhile (/= ']') withN in beforeY <> year <> beforeN <> nm <> afterN +-- | Download the given LICENSE text as it is from GitHub API. fetchLicense :: LicenseName -> IO License-fetchLicense None = pure $ License $ licenseShortDesc None+fetchLicense NONE = pure $ License $ licenseShortDesc NONE fetchLicense name = do let licenseLink = "https://api.github.com/licenses/" <> githubLicenseQueryNames name licenseJson <- "curl" $| [ licenseLink , "-H" , "Accept: application/vnd.github.drax-preview+json"+ , "--silent"+ , "--fail" ] whenNothing (decodeStrict @License $ encodeUtf8 licenseJson) $ do@@ -117,6 +118,14 @@ putTextLn $ "Fetched content:\n" <> licenseJson exitFailure +{- | Fetches the license by given name and customises user information where+applicable.+-}+fetchLicenseCustom :: LicenseName -> Text -> Text -> IO License+fetchLicenseCustom license fullName year = do+ licenseText <- fetchLicense license+ pure $ customizeLicense license licenseText fullName year+ -- | Show short information for the 'LicenseName'. licenseShortDesc :: LicenseName -> Text licenseShortDesc = \case@@ -129,10 +138,10 @@ LGPL3 -> "GNU Lesser General Public License, version 3" AGPL3 -> "GNU Affero General Public License, version 3" Apache20 -> "Apache License, version 2.0"- MPL20 -> "Mozilla Public License, version 2.0."- None -> "License file won't be added. Explicitly 'All Rights Reserved', eg \- \for proprietary software. The package may not be legally modified or \- \redistributed by anyone but the rightsholder"+ MPL20 -> "Mozilla Public License, version 2.0"+ ISC -> "Internet Systems Consortium"+ NONE -> "License file won't be added. The package may not be legally \+ \modified or redistributed by anyone but the rightsholder" -- | Show license name along with its short description. showLicenseWithDesc :: LicenseName -> Text
+ src/Summoner/Mode.hs view
@@ -0,0 +1,40 @@+{- |+Copyright: (c) 2020 Kowainik+SPDX-License-Identifier: MPL-2.0+Maintainer: Kowainik <xrom.xkov@gmail.com>++This module introduces different modes in which the command-line interface+could be.+-}++module Summoner.Mode+ ( Interactivity (..)+ , isNonInteractive+ , ConnectMode (..)+ , isOffline+ ) where+++-- | Switcher for non-interactive mode.+data Interactivity+ = Interactive+ | NonInteractive+ deriving stock (Show, Eq)++-- | Is interactivity mode 'NonInteractive'?+isNonInteractive :: Interactivity -> Bool+isNonInteractive = \case+ NonInteractive -> True+ Interactive -> False++-- | Switcher for offline mode.+data ConnectMode+ = Online+ | Offline+ deriving stock (Show, Eq)++-- | Is connection mode 'Offline'?+isOffline :: ConnectMode -> Bool+isOffline = \case+ Offline -> True+ Online -> False
src/Summoner/Project.hs view
@@ -1,5 +1,7 @@ {-# LANGUAGE QuasiQuotes #-} +{- HLINT ignore "Reduce duplication" -}+ {- | Copyright: (c) 2017-2019 Kowainik SPDX-License-Identifier: MPL-2.0@@ -10,44 +12,55 @@ module Summoner.Project ( generateProject+ , generateProjectInteractive+ , generateProjectNonInteractive , initializeProject- , fetchSources ) where -import Data.List (intersect)+import Colourista (bold, formattedMessage, green) import NeatInterpolation (text) import Relude.Extra.Enum (universe)-import Shellmet ()-import System.Directory (setCurrentDirectory)+import Shellmet (($?))+import System.Directory (findExecutable, setCurrentDirectory) -import Summoner.Ansi (Color (Green), beautyPrint, bold, errorMessage, infoMessage, setColor,- skipMessage, successMessage, warningMessage)+import Summoner.Ansi (errorMessage, infoMessage, skipMessage, successMessage, warningMessage) import Summoner.Config (Config, ConfigP (..)) import Summoner.CustomPrelude (CustomPrelude (..))-import Summoner.Decision (Decision (..), decisionToBool)+import Summoner.Decision (Decision (..), decisionToBool, decisionsToBools, promptDecisionToBool) import Summoner.Default (currentYear, defaultDescription, defaultGHC)-import Summoner.GhcVer (oldGhcs, parseGhcVer, showGhcVer)-import Summoner.License (LicenseName (..), customizeLicense, fetchLicense, licenseShortDesc,- parseLicenseName)-import Summoner.Question (YesNoPrompt (..), checkUniqueName, choose, falseMessage,- mkDefaultYesNoPrompt, query, queryDef, queryManyRepeatOnFail,- queryWithPredicate, targetMessageWithText, trueMessage)+import Summoner.GhcVer (parseGhcVer, showGhcVer)+import Summoner.License (LicenseName (..), fetchLicenseCustom, licenseShortDesc, parseLicenseName)+import Summoner.Mode (ConnectMode (..), Interactivity (..), isOffline)+import Summoner.Question (YesNoPrompt (..), checkUniqueName, choose, doesExistProjectName,+ falseMessage, mkDefaultYesNoPrompt, query, queryDef,+ queryManyRepeatOnFail, queryWithPredicate, targetMessageWithText,+ trueMessage) import Summoner.Settings (Settings (..))-import Summoner.Source (Source, fetchSource)+import Summoner.Source (fetchSources) import Summoner.Template (createProjectTemplate)+import Summoner.Template.Mempty (memptyIfFalse) import Summoner.Text (intercalateMap, moduleNameValid, packageNameValid, packageToModule)-import Summoner.Tree (TreeFs, pathToTree, showBoldTree, traverseTree)--import qualified Data.Map.Strict as Map+import Summoner.Tree (showBoldTree, traverseTree) -- | Generate the project. generateProject- :: Bool -- ^ @offline@ mode option- -> Text -- ^ Given project name.- -> Config -- ^ Given configurations.+ :: Interactivity -- ^ Is it interactive or non-interactive mode?+ -> ConnectMode -- ^ @offline@ mode option.+ -> Text -- ^ Given project name.+ -> Config -- ^ Given configurations. -> IO ()-generateProject isOffline projectName Config{..} = do+generateProject Interactive = generateProjectInteractive+generateProject NonInteractive = generateProjectNonInteractive+++-- | Generate the project.+generateProjectInteractive+ :: ConnectMode -- ^ @offline@ mode option.+ -> Text -- ^ Given project name.+ -> Config -- ^ Given configurations.+ -> IO ()+generateProjectInteractive connectMode projectName ConfigP{..} = do settingsRepo <- checkUniqueName projectName -- decide cabal stack or both (settingsCabal, settingsStack) <- getCabalStack (cCabal, cStack)@@ -61,20 +74,18 @@ settingsCategories <- query "Category: " putText licenseText- settingsLicenseName <- if isOffline- then None <$ infoMessage "'AllRightsReserved' license is used in offline mode"+ settingsLicenseName <- if isOffline connectMode+ then NONE <$ infoMessage "'NONE' license is used in offline mode" else choose parseLicenseName "License: " $ ordNub (cLicense : universe) -- License creation- fetchedLicense <- fetchLicense settingsLicenseName settingsYear <- currentYear- let settingsLicenseText = customizeLicense- settingsLicenseName- fetchedLicense- settingsFullName- settingsYear+ settingsLicenseText <- fetchLicenseCustom+ settingsLicenseName+ settingsFullName+ settingsYear - settingsGitHub <- decisionToBool cGitHub+ settingsGitHub <- promptDecisionToBool cGitHub (YesNoPrompt "GitHub integration" "Do you want to create a GitHub repository?") let settingsNoUpload = getAny cNoUpload@@ -85,19 +96,17 @@ (settingsGitHub && not settingsNoUpload) (YesNoPrompt "private repository" "Create as a private repository (Requires a GitHub private repo plan)?") cPrivate- settingsTravis <- decisionIf settingsGitHub (mkDefaultYesNoPrompt "Travis CI integration") cTravis- settingsAppVeyor <- decisionIf settingsGitHub (mkDefaultYesNoPrompt "AppVeyor CI integration") cAppVey- settingsIsLib <- decisionToBool cLib (mkDefaultYesNoPrompt "library target")- settingsIsExe <- let target = "executable target" in+ settingsGhActions <- decisionIf (settingsCabal && settingsGitHub) (mkDefaultYesNoPrompt "GitHub Actions CI integration") cGhActions+ settingsTravis <- decisionIf settingsGitHub (mkDefaultYesNoPrompt "Travis CI integration") cTravis+ settingsAppVeyor <- decisionIf settingsGitHub (mkDefaultYesNoPrompt "AppVeyor CI integration") cAppVey+ settingsIsLib <- promptDecisionToBool cLib (mkDefaultYesNoPrompt "library target")+ settingsIsExe <- let target = "executable target" in if settingsIsLib- then decisionToBool cExe (mkDefaultYesNoPrompt target)+ then promptDecisionToBool cExe (mkDefaultYesNoPrompt target) else trueMessage target- settingsTest <- decisionToBool cTest (mkDefaultYesNoPrompt "tests")- settingsBench <- decisionToBool cBench (mkDefaultYesNoPrompt "benchmarks")- settingsPrelude <- if settingsIsLib then getPrelude else pure Nothing- let settingsBaseType = case settingsPrelude of- Nothing -> "base"- Just _ -> "base-noprelude"+ settingsTest <- promptDecisionToBool cTest (mkDefaultYesNoPrompt "tests")+ settingsBench <- promptDecisionToBool cBench (mkDefaultYesNoPrompt "benchmarks")+ settingsPrelude <- getPrelude let settingsExtensions = cExtensions let settingsGhcOptions = cGhcOptions@@ -113,22 +122,8 @@ vers -> do putTextLn $ "Also these GHC versions will be added: " <> intercalateMap " " showGhcVer vers pure vers- -- Inform if there are old GHCs that won't be included to Travis Stack matrix- let oldGhcIncluded = not $ null $ settingsTestedVersions `intersect` oldGhcs- when (oldGhcIncluded && settingsStack && settingsTravis) $- warningMessage "Old GHC versions won't be included into Stack matrix at Travis CI because of the Stack issue with newer Cabal versions." - let fetchLast :: Text -> Last Source -> IO (Maybe Text)- fetchLast option (Last mSource) = case mSource of- Nothing -> pure Nothing- Just source -> do- let msg = [text|The option '${option}' is deprecated. Use 'files' instead.|]- warningMessage msg- fetchSource isOffline source-- settingsStylish <- fetchLast "stylish.{url,file,link}" cStylish- settingsContributing <- fetchLast "contributing.{url,file,link}" cContributing- settingsFiles <- fetchSources isOffline cFiles+ settingsFiles <- fetchSources connectMode cFiles -- Create project data from all variables in scope -- and make a project from it.@@ -136,10 +131,9 @@ where decisionIf :: Bool -> YesNoPrompt -> Decision -> IO Bool decisionIf p ynPrompt decision = if p- then decisionToBool decision ynPrompt+ then promptDecisionToBool decision ynPrompt else falseMessage (yesNoTarget ynPrompt) - categoryText :: Text categoryText = [text|@@ -194,8 +188,8 @@ -- If user chose only one during CLI, we assume to use only that one. getCabalStack :: (Decision, Decision) -> IO (Bool, Bool) getCabalStack = \case- (Idk, Idk) -> decisionToBool cCabal (mkDefaultYesNoPrompt "cabal") >>= \c ->- if c then decisionToBool cStack (mkDefaultYesNoPrompt "stack") >>= \s -> pure (c, s)+ (Idk, Idk) -> promptDecisionToBool cCabal (mkDefaultYesNoPrompt "cabal") >>= \c ->+ if c then promptDecisionToBool cStack (mkDefaultYesNoPrompt "stack") >>= \s -> pure (c, s) else stackMsg True >> pure (False, True) (Nop, Nop) -> errorMessage "Neither cabal nor stack was chosen" >> exitFailure (Yes, Yes) -> output (True, True)@@ -210,26 +204,73 @@ cabalMsg c = targetMessageWithText c "Cabal" "used in this project" stackMsg c = targetMessageWithText c "Stack" "used in this project" +----------------------------------------------------------------------------+-- Non-interactive+----------------------------------------------------------------------------++generateProjectNonInteractive+ :: ConnectMode -- ^ @offline@ mode option.+ -> Text -- ^ Given project name.+ -> Config -- ^ Given configurations.+ -> IO ()+generateProjectNonInteractive connectMode projectName ConfigP{..} = do+ isNonUnique <- doesExistProjectName projectName+ when isNonUnique $ do+ errorMessage "Project with this name is already exist. Please choose another one."+ exitFailure+ let settingsRepo = projectName+ -- decide cabal stack or both+ let (settingsCabal, settingsStack) = decisionsToBools (cCabal, cStack)++ let settingsOwner = cOwner+ let settingsDescription = defaultDescription+ let settingsFullName = cFullName+ let settingsEmail = cEmail+ let settingsCategories = ""+ let settingsLicenseName = if isOffline connectMode then NONE else cLicense++ -- License creation+ settingsYear <- currentYear+ settingsLicenseText <- fetchLicenseCustom+ settingsLicenseName+ settingsFullName+ settingsYear++ let settingsGitHub = decisionToBool cGitHub+ let settingsNoUpload = getAny cNoUpload || isOffline connectMode++ let settingsPrivate = decisionIf (settingsGitHub && not settingsNoUpload) cPrivate+ let settingsGhActions = decisionIf (settingsCabal && settingsGitHub) cGhActions+ let settingsTravis = decisionIf settingsGitHub cTravis+ let settingsAppVeyor = decisionIf settingsGitHub cAppVey+ let (settingsIsLib, settingsIsExe) = decisionsToBools (cLib, cExe)+ let settingsTest = decisionToBool cTest+ let settingsBench = decisionToBool cBench+ let settingsPrelude = getLast cPrelude++ let settingsExtensions = cExtensions+ let settingsGhcOptions = cGhcOptions+ let settingsGitignore = cGitignore+ let settingsTestedVersions = sortNub $ defaultGHC : cGhcVer+ settingsFiles <- fetchSources connectMode cFiles++ -- Create project data from all variables in scope+ -- and make a project from it.+ initializeProject Settings{..}+ where+ decisionIf :: Bool -> Decision -> Bool+ decisionIf p d = p && decisionToBool d++----------------------------------------------------------------------------+-- Initialize+----------------------------------------------------------------------------+ -- | Creates the directory and run GitHub commands. initializeProject :: Settings -> IO () initializeProject settings@Settings{..} = do createProjectDirectory settings when settingsGitHub $ doGithubCommands settings- beautyPrint [bold, setColor Green] "\nJob's done\n"--{- | This function fetches contents of extra file sources.--}-fetchSources :: Bool -> Map FilePath Source -> IO [TreeFs]-fetchSources isOffline = mapMaybeM sourceToTree . Map.toList- where- sourceToTree :: (FilePath, Source) -> IO (Maybe TreeFs)- sourceToTree (path, source) = do- infoMessage $ "Fetching content of the extra file: " <> toText path- fetchSource isOffline source >>= \case- Nothing -> do- errorMessage $ "Error fetching: " <> toText path- pure Nothing- Just content -> pure $ Just $ pathToTree path content+ formattedMessage [bold, green] "\nJob's done" -- | From the given 'Settings' creates the project. createProjectDirectory :: Settings -> IO ()@@ -248,7 +289,29 @@ "git" ["add", "."] "git" ["commit", "-m", "Create the project"] unless settingsNoUpload $ do- "hub" $ ["create", "-d", settingsDescription, settingsOwner <> "/" <> settingsRepo]- ++ ["-p" | settingsPrivate] -- Create private repository if asked so- -- Upload repository to GitHub.- "git" ["push", "-u", "origin", "master"]+ let repo = settingsOwner <> "/" <> settingsRepo+ hubInstalled <- findExecutable "hub"+ case hubInstalled of+ Just _ -> do+ isHubSuccess <- runHub repo+ if isHubSuccess+ then "git" ["push", "-u", "origin", "master"]+ else do+ warningMessage "Error running 'hub'. Possible reason: incorrect password."+ hubHelp repo+ Nothing -> do+ warningMessage "'hub' is not found at this machine. Cannot create the GitHub repository."+ warningMessage "Please install 'hub' for the proper work of Summoner."+ hubHelp repo+ where+ -- Create repo on GitHub and return 'True' in case of sucsess+ runHub :: Text -> IO Bool+ runHub repo =+ True <$ "hub" (["create", "-d", settingsDescription, repo]+ ++ ["-p" | settingsPrivate]) -- Create private repository if asked so+ $? pure False++ hubHelp :: Text -> IO ()+ hubHelp repo = do+ infoMessage "To finish the process manually you can run the following command:"+ putTextLn $ " $ hub create -d '" <> settingsDescription <> "' " <> repo <> memptyIfFalse settingsPrivate " -p"
src/Summoner/Question.hs view
@@ -27,6 +27,7 @@ , queryDef , queryManyRepeatOnFail , checkUniqueName+ , doesExistProjectName -- * Customize target message , targetMessageWithText@@ -35,17 +36,19 @@ , falseMessage ) where +import Colourista (bold, cyan, formatWith, formattedMessage, green, italic) import System.Directory (doesPathExist, getCurrentDirectory) import System.FilePath ((</>)) -import Summoner.Ansi (Color (..), beautyPrint, bold, boldDefault, errorMessage, italic, prompt,- putStrFlush, setColor, warningMessage)+import Summoner.Ansi (boldDefault, errorMessage, prompt, putStrFlush, warningMessage) import Summoner.Text (headToUpper, intercalateMap) import qualified Data.Text as T import qualified Relude.Unsafe as Unsafe +{- HLINT ignore "Redundant multi-way if" -}+ {- | Build a prompt For example,@@ -115,8 +118,7 @@ -> IO () printQuestion question [] = putTextLn question printQuestion question (def:rest) = do- putStrFlush question- boldDefault def+ putStrFlush $ question <> boldDefault def let restSlash = T.intercalate "/" rest if restSlash == "" then putTextLn ""@@ -166,13 +168,13 @@ targetMessageWithText :: Bool -> Text -> Text -> IO Bool targetMessageWithText result target text = do let (color, actionResult) = if result- then (Green, " will be " <> text)- else (Cyan, " won't be " <> text)-- beautyPrint [italic, bold, setColor color] $ " " <> headToUpper target- beautyPrint [setColor color] actionResult- putTextLn ""+ then (green, " will be " <> text)+ else (cyan, " won't be " <> text) + putTextLn $ mconcat+ [ formatWith [italic, bold, color] $ " " <> headToUpper target+ , formatWith [color] actionResult+ ] pure result -- | Like 'targetMessageWithText' but the text is "added to the project"@@ -202,7 +204,7 @@ loop :: IO Text loop = do printQuestion question options- beautyPrint [italic] $ instruction <> "\n"+ formattedMessage [italic] instruction input <- prompt if input /= "" && not (p input) then errorMessage ("'" <> input <> "' doesn't satisfy the requirements.") >> loop@@ -222,9 +224,7 @@ -- | Like 'query' but has the default answer if no answer is specified. queryDef :: Text -> Text -> IO Text queryDef question defAnswer = do- putStrFlush question- boldDefault defAnswer- putTextLn ""+ putTextLn $ question <> boldDefault defAnswer answer <- prompt if | T.null answer -> pure defAnswer | otherwise -> pure answer@@ -261,11 +261,16 @@ checkUniqueName :: Text -> IO Text checkUniqueName nm = do- curPath <- getCurrentDirectory- exist <- doesPathExist $ curPath </> toString nm+ exist <- doesExistProjectName nm if exist then do warningMessage "Project with this name is already exist. Please choose another one" newNm <- queryNotNull "Project name: " checkUniqueName newNm else pure nm++-- | Check if the folder with the suggested project name is already exist.+doesExistProjectName :: Text -> IO Bool+doesExistProjectName projectName = do+ curPath <- getCurrentDirectory+ doesPathExist $ curPath </> toString projectName
src/Summoner/Settings.hs view
@@ -35,6 +35,7 @@ , settingsLicenseText :: !License -- ^ license text , settingsGitHub :: !Bool -- ^ GitHub repository , settingsPrivate :: !Bool -- ^ private repository+ , settingsGhActions :: !Bool -- ^ GitHub Actions CI integration , settingsTravis :: !Bool -- ^ Travis CI integration , settingsAppVeyor :: !Bool -- ^ AppVeyor CI integration , settingsIsLib :: !Bool -- ^ is library@@ -42,15 +43,12 @@ , settingsTest :: !Bool -- ^ add tests , settingsBench :: !Bool -- ^ add benchmarks , settingsTestedVersions :: ![GhcVer] -- ^ GHC versions- , settingsBaseType :: !Text -- ^ Base library to use , settingsPrelude :: !(Maybe CustomPrelude) -- ^ custom prelude to be used , settingsExtensions :: ![Text] -- ^ default extensions , settingsGhcOptions :: ![Text] -- ^ default GHC options , settingsGitignore :: ![Text] -- ^ .gitignore file , settingsCabal :: !Bool , settingsStack :: !Bool- , settingsStylish :: !(Maybe Text) -- ^ @.stylish-haskell.yaml@ file- , settingsContributing :: !(Maybe Text) -- ^ @CONTRIBUTING.md@ file , settingsNoUpload :: !Bool -- ^ do not upload to GitHub , settingsFiles :: ![TreeFs] -- ^ Tree nodes of extra files } deriving stock (Show)
src/Summoner/Source.hs view
@@ -8,18 +8,20 @@ module Summoner.Source ( Source (..)- , sourceT , sourceCodec+ , fetchSources , fetchSource ) where -import Control.Arrow ((>>>)) import Control.Exception (catch) import System.Process (readProcess)-import Toml (Key, TomlBiMap, TomlBiMapError (..), TomlCodec)+import Toml (TomlBiMapError (..), TomlCodec) import Summoner.Ansi (errorMessage, infoMessage)+import Summoner.Mode (ConnectMode (..), isOffline)+import Summoner.Tree (TreeFs, pathToTree) +import qualified Data.Map.Strict as Map import qualified Toml @@ -60,21 +62,6 @@ matchRaw (Raw raw) = Right raw matchRaw e = Left $ WrongConstructor "Raw" $ showSource e --- DEPRECATED: To be removed in 2.0-sourceT :: Key -> TomlCodec Source-sourceT nm = Toml.match (_Url >>> Toml._Text) (nm <> "url")- <|> Toml.match (_Local >>> Toml._String) (nm <> "Local")- <|> Toml.match (_Raw >>> Toml._Text) (nm <> "raw")- where- _Url :: TomlBiMap Source Text- _Url = Toml.prism Url matchUrl-- _Local :: TomlBiMap Source FilePath- _Local = Toml.prism Local matchLocal-- _Raw :: TomlBiMap Source Text- _Raw = Toml.prism Raw matchRaw- {- | This 'TomlCodec' is used in the @files@ field of config. It decodes corresponding constructor from the top-level key. -}@@ -85,21 +72,40 @@ , Toml.dimatch (rightToMaybe . matchRaw) Raw (Toml.text "raw") ] -fetchSource :: Bool -> Source -> IO (Maybe Text)-fetchSource isOffline = \case+{- | This function fetches contents of extra file sources.+-}+fetchSources :: ConnectMode -> Map FilePath Source -> IO [TreeFs]+fetchSources connectMode = mapMaybeM sourceToTree . Map.toList+ where+ sourceToTree :: (FilePath, Source) -> IO (Maybe TreeFs)+ sourceToTree (path, source) = do+ infoMessage $ "Fetching content of the extra file: " <> toText path+ fetchSource connectMode source >>= \case+ Nothing -> Nothing <$ errorMessage ("Error fetching: " <> toText path)+ Just content -> pure $ Just $ pathToTree path content++{- | Fetches content of the given extra file source.+Doesn't fetch 'Url' if the 'ConnectMode' is 'Offline'.+-}+fetchSource :: ConnectMode -> Source -> IO (Maybe Text)+fetchSource connectMode = \case Local path -> catch (Just <$> readFileText path) (localError path)- Url url -> if isOffline+ Url url -> if isOffline connectMode then Nothing <$ infoMessage ("Ignoring fetching from URL in offline mode from source: " <> url) else fetchUrl url `catch` urlError url Raw raw -> pure $ Just raw where localError :: FilePath -> SomeException -> IO (Maybe Text)- localError path _ = errorMessage ("Couldn't read file: " <> toText path)- >> pure Nothing+ localError path err = do+ errorMessage ("Couldn't read file: " <> toText path)+ errorMessage $ toText $ displayException err+ pure Nothing urlError :: Text -> SomeException -> IO (Maybe Text)- urlError url _ = errorMessage ("Couldn't get to link: " <> url)- >> pure Nothing+ urlError url err = do+ errorMessage ("Couldn't get to link: " <> url)+ errorMessage $ toText $ displayException err+ pure Nothing fetchUrl :: Text -> IO (Maybe Text)- fetchUrl url = Just . toText <$> readProcess "curl" [toString url] ""+ fetchUrl url = Just . toText <$> readProcess "curl" [toString url, "--silent", "--fail"] ""
src/Summoner/Template/Cabal.hs view
@@ -16,8 +16,8 @@ import Summoner.CustomPrelude (CustomPrelude (..)) import Summoner.Default (defaultCabal)-import Summoner.GhcVer (GhcVer (..), cabalBaseVersions, showGhcVer)-import Summoner.License (LicenseName (..), cabalLicense)+import Summoner.GhcVer (cabalBaseVersions, showGhcVer)+import Summoner.License (LicenseName (..)) import Summoner.Settings (Settings (..)) import Summoner.Template.Mempty (memptyIfFalse) import Summoner.Text (endLine, intercalateMap, packageToModule)@@ -34,10 +34,11 @@ cabalFileContent = T.concat [ cabalHeader , memptyIfFalse settingsGitHub sourceRepository- , memptyIfFalse settingsIsLib libraryStanza- , memptyIfFalse settingsIsExe $ executableStanza $ memptyIfFalse settingsIsLib $ ", " <> settingsRepo- , memptyIfFalse settingsTest $ testSuiteStanza $ memptyIfFalse settingsIsLib $ ", " <> settingsRepo- , memptyIfFalse settingsBench $ benchmarkStanza $ memptyIfFalse settingsIsLib $ ", " <> settingsRepo+ , commonStanza+ , memptyIfFalse settingsIsLib libraryStanza+ , memptyIfFalse settingsIsExe executableStanza+ , memptyIfFalse settingsTest testSuiteStanza+ , memptyIfFalse settingsBench $ benchmarkStanza $ memptyIfFalse settingsIsLib $ ", " <> settingsRepo ] -- TODO: do something to not have empty lines@@ -51,14 +52,14 @@ [ "homepage: " <> githubUrl | settingsGitHub ] ++ [ "bug-reports: " <> githubBugReports | settingsGitHub ] ++ ( "license: " <> licenseName) :- [ "license-file: LICENSE" | settingsLicenseName /= None] +++ [ "license-file: LICENSE" | settingsLicenseName /= NONE] ++ [ "author: " <> settingsFullName- , "maintainer: " <> settingsEmail+ , "maintainer: " <> settingsFullName <> " <" <> settingsEmail <> ">" , "copyright: " <> settingsYear <> " " <> settingsFullName ] ++ [ "category: " <> settingsCategories | "" /= settingsCategories ] ++ [ "build-type: Simple" , "extra-doc-files: README.md"- , " , CHANGELOG.md"+ , " CHANGELOG.md" , "tested-with: " <> testedGhcs ] @@ -67,12 +68,12 @@ githubBugReports = githubUrl <> "/issues" licenseName, libModuleName :: Text- licenseName = cabalLicense settingsLicenseName+ licenseName = show settingsLicenseName libModuleName = packageToModule settingsRepo testedGhcs :: Text testedGhcs = intercalateMap- ("\n" <> T.replicate 19 " " <> ", ")+ ("\n" <> T.replicate 21 " ") (mappend "GHC == " . showGhcVer) settingsTestedVersions @@ -85,98 +86,124 @@ location: ${githubUrl}.git |] + commonStanza :: Text+ commonStanza =+ [text|+ $endLine+ common common-options+ build-depends: base $baseBounds+ $customPrelude+ $ghcOptions++ default-language: Haskell2010+ |] <> defaultExtensions+ baseBounds :: Text baseBounds = cabalBaseVersions settingsTestedVersions + ghcOptions :: Text+ ghcOptions = case settingsGhcOptions of+ [] -> defaultGhcOptions+ x:xs ->+ let customGhcOptions = T.intercalate "\n" $ x : map (T.replicate 21 " " <>) xs in+ [text|+ ghc-options: $customGhcOptions+ |]++ defaultGhcOptions :: Text+ defaultGhcOptions =+ [text|+ ghc-options: -Wall+ -Wcompat+ -Widentities+ -Wincomplete-uni-patterns+ -Wincomplete-record-updates+ if impl(ghc >= 8.0)+ ghc-options: -Wredundant-constraints+ if impl(ghc >= 8.2)+ ghc-options: -fhide-source-paths+ if impl(ghc >= 8.4)+ ghc-options: -Wmissing-export-lists+ -Wpartial-fields+ if impl(ghc >= 8.8)+ ghc-options: -Wmissing-deriving-strategies+ |]+ libraryStanza :: Text libraryStanza = [text| $endLine library+ import: common-options hs-source-dirs: src exposed-modules: $libModuleName- $preludeMod-- build-depends: $settingsBaseType $baseBounds- $commaPreludeLibrary-- ghc-options: -Wall- $ghcOptions-- default-language: Haskell2010- |] <> defaultExtensions+ |] - executableStanza :: Text -> Text- executableStanza commaRepo =+ executableStanza :: Text+ executableStanza = [text| $endLine executable $settingsRepo+ import: common-options hs-source-dirs: app main-is: Main.hs-- build-depends: $settingsBaseType $baseBounds- $commaRepo- $commaPreludeLibrary-- ghc-options: -Wall- -threaded- -rtsopts- -with-rtsopts=-N- $ghcOptions-- default-language: Haskell2010- |] <> defaultExtensions+ $buildDepends+ $rtsOptions+ |] - testSuiteStanza :: Text -> Text- testSuiteStanza commaRepo =+ testSuiteStanza :: Text+ testSuiteStanza = [text| $endLine test-suite ${settingsRepo}-test+ import: common-options type: exitcode-stdio-1.0 hs-source-dirs: test main-is: Spec.hs-- build-depends: $settingsBaseType $baseBounds- $commaRepo- $commaPreludeLibrary-- ghc-options: -Wall- -threaded- -rtsopts- -with-rtsopts=-N- $ghcOptions-- default-language: Haskell2010- |] <> defaultExtensions+ $buildDepends+ $rtsOptions+ |] benchmarkStanza :: Text -> Text benchmarkStanza commaRepo = [text| $endLine benchmark ${settingsRepo}-benchmark+ import: common-options type: exitcode-stdio-1.0 hs-source-dirs: benchmark main-is: Main.hs-- build-depends: $settingsBaseType $baseBounds- , gauge+ build-depends: gauge $commaRepo- $commaPreludeLibrary-- ghc-options: -Wall- -threaded- -rtsopts- -with-rtsopts=-N- $ghcOptions+ $rtsOptions+ |] - default-language: Haskell2010- |] <> defaultExtensions+ -- | @build-depends@ for the repo, only if the library is on.+ buildDepends :: Text+ buildDepends =+ if settingsIsLib+ then "build-depends: " <> settingsRepo+ else "" + rtsOptions :: Text+ rtsOptions =+ [text|+ ghc-options: -threaded+ -rtsopts+ -with-rtsopts=-N+ |] - preludeMod, commaPreludeLibrary :: Text- (preludeMod, commaPreludeLibrary) = case settingsPrelude of- Nothing -> ("", "")- Just CustomPrelude{..} -> ("Prelude", ", " <> cpPackage)+ customPrelude :: Text+ customPrelude = case settingsPrelude of+ Nothing -> ""+ Just CustomPrelude{..} ->+ " , " <> cpPackage <> "\n" <>+ [text|+ $endLine+ mixins: base hiding (Prelude)+ , $cpPackage ($cpModule as Prelude)+ $endLine+ |] defaultExtensions :: Text defaultExtensions = case settingsExtensions of@@ -184,35 +211,3 @@ xs -> " default-extensions: " <> T.intercalate "\n " xs <> "\n"-- ghcOptions :: Text- ghcOptions = case settingsGhcOptions of- [] -> defaultGhcOptions- xs -> T.intercalate "\n" xs-- defaultGhcOptions :: Text- defaultGhcOptions =- [text|- -Wincomplete-uni-patterns- -Wincomplete-record-updates- -Wcompat- -Widentities- $versionGhcOptions- |]-- versionGhcOptions :: Text- versionGhcOptions- = memptyIfFalse (settingsTestedVersions `hasLeast` Ghc802)- "-Wredundant-constraints\n"- <> memptyIfFalse (settingsTestedVersions `hasLeast` Ghc822)- "-fhide-source-paths\n"- <> memptyIfFalse (settingsTestedVersions `hasLeast` Ghc844)- [text|- -Wmissing-export-lists- -Wpartial-fields- |]- <> memptyIfFalse (settingsTestedVersions `hasLeast` Ghc881)- "-Wmissing-deriving-strategies\n"- where- hasLeast :: [GhcVer] -> GhcVer -> Bool- hasLeast list el = all (>= el) list
src/Summoner/Template/Doc.hs view
@@ -6,7 +6,6 @@ Templates for various documentation files: * CHANGELOG.md-* CONTRIBUTING.md * LICENSE * README.md -}@@ -15,7 +14,7 @@ ( docFiles ) where -import Summoner.License (License (..), LicenseName (None))+import Summoner.License (License (..), LicenseName (NONE)) import Summoner.Settings (Settings (..)) import Summoner.Tree (TreeFs (..)) @@ -26,11 +25,10 @@ docFiles Settings{..} = [ File "README.md" readme , File "CHANGELOG.md" changelog ] ++- [ File "LICENSE" (unLicense settingsLicenseText) | hasLicense ] ++- maybeToList (File "CONTRIBUTING.md" <$> settingsContributing)+ [ File "LICENSE" (unLicense settingsLicenseText) | hasLicense ] where hasLicense :: Bool- hasLicense = settingsLicenseName /= None+ hasLicense = settingsLicenseName /= NONE licenseName :: Text licenseName = show settingsLicenseName@@ -40,57 +38,61 @@ [ "# " <> settingsRepo , "" ]- ++ [travisBadge | settingsTravis]- ++ [appVeyorBadge | settingsAppVeyor]- ++ [hackage]- ++ [stackLtsBadge | settingsStack]- ++ [stackNightlyBadge | settingsStack]- ++ [licenseBadge | hasLicense]+ ++ [githubActionsBadge | settingsGhActions]+ ++ [travisBadge | settingsTravis]+ ++ [appVeyorBadge | settingsAppVeyor]+ ++ [hackageBadge]+ ++ [stackLtsBadge | settingsStack]+ ++ [stackNightlyBadge | settingsStack]+ ++ [licenseBadge | hasLicense] ++ ["" , settingsDescription ] where- hackageShield :: Text =- "https://img.shields.io/hackage/v/" <> settingsRepo <> ".svg?logo=haskell"- hackageLink :: Text =- "https://hackage.haskell.org/package/" <> settingsRepo- hackage :: Text = makeBadge "Hackage" hackageShield hackageLink+ shieldsIo :: Text+ shieldsIo = "https://img.shields.io/" - licenseShield :: Text =- "https://img.shields.io/badge/license-" <> T.replace "-" "--" licenseName <> "-blue.svg"- licenseBadge :: Text =- makeBadge (licenseName <> " license") licenseShield "LICENSE"+ hackageShield, hackageLink, hackageBadge :: Text+ hackageShield = shieldsIo <> "hackage/v/" <> settingsRepo <> ".svg?logo=haskell"+ hackageLink = "https://hackage.haskell.org/package/" <> settingsRepo+ hackageBadge = makeBadge "Hackage" hackageShield hackageLink - stackShieldLts :: Text =- "http://stackage.org/package/" <> settingsRepo <> "/badge/lts"- stackLinkLts :: Text =- "http://stackage.org/lts/package/" <> settingsRepo+ licenseShield, licenseBadge :: Text+ licenseShield = shieldsIo <> "badge/license-" <> T.replace "-" "--" licenseName <> "-blue.svg"+ licenseBadge = makeBadge (licenseName <> " license") licenseShield "LICENSE" - stackShieldNightly :: Text =- "http://stackage.org/package/" <> settingsRepo <> "/badge/nightly"- stackLinkNightly :: Text =- "http://stackage.org/nightly/package/" <> settingsRepo- stackLtsBadge :: Text =- makeBadge "Stackage Lts" stackShieldLts stackLinkLts- stackNightlyBadge :: Text =- makeBadge "Stackage Nightly" stackShieldNightly stackLinkNightly+ stackOrg, stackLtsShield, stackLtsLink, stackLtsBadge :: Text+ stackOrg = "http://stackage.org/"+ stackLtsShield = stackOrg <> "package/" <> settingsRepo <> "/badge/lts"+ stackLtsLink = stackOrg <> "lts/package/" <> settingsRepo+ stackLtsBadge = makeBadge "Stackage Lts" stackLtsShield stackLtsLink - travisShield :: Text =- "https://img.shields.io/travis/" <> settingsOwner <> "/" <> settingsRepo <> ".svg?logo=travis"- travisLink :: Text =- "https://travis-ci.org/" <> settingsOwner <> "/" <> settingsRepo- travisBadge :: Text =- makeBadge "Build status" travisShield travisLink+ stackNightlyShield, stackNightlyLink, stackNightlyBadge :: Text+ stackNightlyShield = stackOrg <> "package/" <> settingsRepo <> "/badge/nightly"+ stackNightlyLink = stackOrg <> "nightly/package/" <> settingsRepo+ stackNightlyBadge = makeBadge "Stackage Nightly" stackNightlyShield stackNightlyLink - appVeyorShield :: Text =- "https://ci.appveyor.com/api/projects/status/github/" <> settingsOwner <> "/" <> settingsRepo <> "?branch=master&svg=true"- appVeyorLink :: Text =- "https://ci.appveyor.com/project/" <> settingsOwner <> "/" <> settingsRepo- appVeyorBadge :: Text =- makeBadge "Windows build status" appVeyorShield appVeyorLink+ githubActionsShield, githubActionsLink, githubActionsBadge :: Text+ githubActionsShield = "https://github.com/" <> ownerRepo <> "/workflows/CI/badge.svg"+ githubActionsLink = "https://github.com/" <> ownerRepo <> "/actions"+ githubActionsBadge = makeBadge "GitHub CI" githubActionsShield githubActionsLink + travisShield, travisLink, travisBadge :: Text+ travisShield = shieldsIo <> "travis/" <> ownerRepo <> ".svg?logo=travis"+ travisLink = "https://travis-ci.org/" <> ownerRepo+ travisBadge = makeBadge "Build status" travisShield travisLink++ appVeyorCom, appVeyorShield, appVeyorLink, appVeyorBadge :: Text+ appVeyorCom = "https://ci.appveyor.com/"+ appVeyorShield = appVeyorCom <> "api/projects/status/github/" <> ownerRepo <> "?branch=master&svg=true"+ appVeyorLink = appVeyorCom <> "project/" <> ownerRepo+ appVeyorBadge = makeBadge "Windows build status" appVeyorShield appVeyorLink+ makeBadge :: Text -> Text -> Text -> Text makeBadge title shield link = "[](" <> link <> ")"++ ownerRepo :: Text+ ownerRepo = settingsOwner <> "/" <> settingsRepo changelog :: Text changelog = unlines $
src/Summoner/Template/GitHub.hs view
@@ -9,12 +9,13 @@ This module contains template for GitHub related docs: * @.gitignore@ — static file with all Haskell related ignored files.- * @appveyor.yml@ — Appveyor CI for Stack project only.+ * @.github/workflows/ci.yml@ — GitHub acttions for Cabal projects.+ * @appveyor.yml@ — Appveyor CI for Cabal or Stack projects. * @.travis.yml@ — depending on the build tool and supported GHC versions builds the Travis matrix with all necessary checks, including HLint check.- __NOTE:__ Old GHC versions is not included into Travis matrix for Stack due to- Stack limitations with the Cabal version usage on each GHC. See this issue to- track the problem:+ __NOTE:__ Old GHC versions are included into @allow_failure@ Travis matrix+ section for Stack due to Stack limitations with the Cabal version usage on+ each GHC. See this issue to track the problem: + https://github.com/commercialhaskell/stack/issues/4488 -}@@ -23,20 +24,21 @@ ( gitHubFiles ) where -import Data.List ((\\))+import Data.List (delete, intersect) import NeatInterpolation (text) -import Summoner.Default (defaultGHC)+import Summoner.Default (defaultCabal, defaultGHC) import Summoner.GhcVer (GhcVer (..), oldGhcs, showGhcVer) import Summoner.Settings (Settings (..)) import Summoner.Template.Mempty (memptyIfFalse)-import Summoner.Text (endLine, tconcatMap)+import Summoner.Text (endLine, intercalateMap, tconcatMap) import Summoner.Tree (TreeFs (..)) gitHubFiles :: Settings -> [TreeFs] gitHubFiles Settings{..} = concat [ [File ".gitignore" (gitignoreDefault <> gitignoreCustom) | settingsGitHub]+ , [Dir ".github" [ Dir "workflows" [ File "ci.yml" ghActionsYml ]] | settingsGhActions ] , [File ".travis.yml" travisYml | settingsTravis] , [File "appveyor.yml" appVeyorYml | settingsAppVeyor] ]@@ -107,6 +109,62 @@ then "" else unlines ("\n# User specific" : settingsGitignore) + ghActionsYml :: Text+ ghActionsYml = [text|+ name: CI++ # Trigger the workflow on push or pull request, but only for the master branch+ on:+ pull_request:+ push:+ branches: [master]++ jobs:+ build:+ name: ghc ${{ matrix.ghc }}+ runs-on: ubuntu-16.04+ strategy:+ matrix:+ cabal: ["${defaultCabal}"]+ ghc:+ ${ghActionsVersions}++ steps:+ - uses: actions/checkout@v2+ if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/master'++ - uses: actions/setup-haskell@v1+ name: Setup Haskell+ with:+ ghc-version: ${{ matrix.ghc }}+ cabal-version: ${{ matrix.cabal }}++ - uses: actions/cache@v1+ name: Cache ~/.cabal/store+ with:+ path: ~/.cabal/store+ key: ${{ runner.os }}-${{ matrix.ghc }}-cabal++ - name: Build+ run: |+ $cabalUpdate+ $cabalBuild++ - name: Test+ run: |+ ${cabalTest}+ |]+++ ghActionsVersions :: Text+ ghActionsVersions = intercalateMap+ "\n"+ (\ghc -> "- " <> ghActionsMatrixItem ghc)+ settingsTestedVersions++ ghActionsMatrixItem :: GhcVer -> Text+ ghActionsMatrixItem v = "\"" <> showGhcVer v <> "\""+ -- create travis.yml template travisYml :: Text travisYml =@@ -117,7 +175,7 @@ git: depth: 5 - cabal: "2.4"+ cabal: "${defaultCabal}" cache: directories:@@ -128,6 +186,7 @@ include: $travisCabalMtr $travisStackMtr+ $travisStackAllowFailuresMtr $installAndScript @@ -143,12 +202,6 @@ - "$$TRAVIS_BUILD_DIR/.stack-work" |] -- cabalTest :: Text- cabalTest = if settingsTest- then "cabal new-test --enable-tests"- else "echo 'No tests'"- travisCabalMtr :: Text travisCabalMtr = memptyIfFalse settingsCabal $ tconcatMap travisCabalMatrixItem settingsTestedVersions@@ -156,12 +209,27 @@ travisCabalMatrixItem :: GhcVer -> Text travisCabalMatrixItem (showGhcVer -> ghcV) = [text|- ghc: $ghcV|] - -- Due to Stach issues with newer Cabal versions we are not supporting Travis CI for GHC <= 8.0.2 for stack+ -- Due to the Stack issues with newer Cabal versions TravisCI for 'oldGhcs'+ -- can fail. Possible failure jobs are added to the @allow-failures@ section. travisStackMtr :: Text travisStackMtr = memptyIfFalse settingsStack $ tconcatMap- travisStackMatrixItem (settingsTestedVersions \\ (defaultGHC:oldGhcs))+ travisStackMatrixItem (delete defaultGHC settingsTestedVersions) <> travisStackMatrixDefaultItem + travisStackAllowFailuresMtr :: Text+ travisStackAllowFailuresMtr = memptyIfFalse (settingsStack && not (null old))+ [text|+ $endLine+ allow_failures:+ $matrix+ |]+ where+ old :: [GhcVer]+ old = settingsTestedVersions `intersect` oldGhcs++ matrix :: Text+ matrix = tconcatMap travisStackMatrixItem old+ travisStackMatrixItem :: GhcVer -> Text travisStackMatrixItem (showGhcVer -> ghcV) = [text|@@ -194,22 +262,20 @@ - | if [ -z "$$STACK_YAML" ]; then- ghc --version- cabal --version- cabal new-update- cabal new-build --enable-tests --enable-benchmarks+ $cabalUpdate+ $cabalBuild else curl -sSL https://get.haskellstack.org/ | sh stack --version- stack build --system-ghc --test --bench --no-run-tests --no-run-benchmarks --ghc-options=-Werror+ $stackBuild fi script: - | if [ -z "$$STACK_YAML" ]; then- ${cabalTest}+ ${cabalTest} else- stack test --system-ghc+ $stackTest fi |] @@ -219,8 +285,8 @@ install: $hlintCheck - - cabal new-update- - cabal new-build --enable-tests --enable-benchmarks+ - $cabalUpdate+ - $cabalBuild script: - ${cabalTest}@@ -234,12 +300,29 @@ - curl -sSL https://get.haskellstack.org/ | sh - stack --version- - stack build --system-ghc --test --bench --no-run-tests --no-run-benchmarks --ghc-options=-Werror+ - $stackBuild script:- - stack test --system-ghc+ - $stackTest |] + cabalUpdate :: Text+ cabalUpdate = "cabal v2-update"++ cabalBuild :: Text+ cabalBuild = "cabal v2-build --enable-tests --enable-benchmarks"++ cabalTest :: Text+ cabalTest = if settingsTest+ then "cabal v2-test --enable-tests"+ else "echo 'No tests'"++ stackBuild :: Text+ stackBuild = "stack build --system-ghc --test --bench --no-run-tests --no-run-benchmarks --ghc-options=-Werror"++ stackTest :: Text+ stackTest = "stack test --system-ghc"+ hlintCheck :: Text hlintCheck = [text|@@ -322,7 +405,7 @@ - stack setup > nul build_script:- - stack build --test --bench --no-run-tests --no-run-benchmarks+ - stack build --test --bench --no-run-tests --no-run-benchmarks --ghc-options=-Werror test_script: - stack test
src/Summoner/Template/Haskell.hs view
@@ -14,7 +14,6 @@ import NeatInterpolation (text) -import Summoner.CustomPrelude (CustomPrelude (..)) import Summoner.Settings (Settings (..)) import Summoner.Text (packageToModule) import Summoner.Tree (TreeFs (..))@@ -22,19 +21,28 @@ haskellFiles :: Settings -> [TreeFs] haskellFiles Settings{..} = concat- [ [ Dir "src" $ libFile : preludeFile | settingsIsLib ]- , [ Dir "app" [exeFile] | settingsIsExe ]- , [ Dir "test" [testFile] | settingsTest ]- , [ Dir "benchmark" [benchmarkFile] | settingsBench ]- ] ++ maybeToList (File ".stylish-haskell.yaml" <$> settingsStylish)+ [ [ Dir "src" [libFile] | settingsIsLib ]+ , [ Dir "app" [exeFile] | settingsIsExe ]+ , [ Dir "test" [testFile] | settingsTest ]+ , [ Dir "benchmark" [benchmarkFile] | settingsBench ]+ ] where libFile :: TreeFs libFile = File (toString libModuleName <> ".hs") [text|+ {- |+ Copyright: (c) $settingsYear $settingsFullName+ SPDX-License-Identifier: $licenseName+ Maintainer: $settingsFullName <$settingsEmail>++ $settingsDescription+ -}+ module $libModuleName ( someFunc ) where + someFunc :: IO () someFunc = putStrLn ("someFunc" :: String) |]@@ -42,18 +50,8 @@ libModuleName :: Text libModuleName = packageToModule settingsRepo - preludeFile :: [TreeFs]- preludeFile = maybeToList $- settingsPrelude <&> \CustomPrelude{..} -> File "Prelude.hs"- [text|- -- | Uses [$cpPackage](https://hackage.haskell.org/package/${cpPackage}) as default Prelude.-- module Prelude- ( module $cpModule- ) where-- import $cpModule- |]+ licenseName :: Text+ licenseName = show settingsLicenseName exeFile :: TreeFs exeFile = File "Main.hs" $ if settingsIsLib then createExe else createOnlyExe@@ -63,6 +61,7 @@ [text| module Main (main) where + main :: IO () main = putStrLn ("Hello, world!" :: String) |]@@ -84,8 +83,9 @@ [text| module Main (main) where + main :: IO ()- main = putStrLn ("Test suite not yet implemented" :: String)+ main = putStrLn ("Test suite is not implemented" :: String) |] benchmarkFile :: TreeFs
src/Summoner/Template/Stack.hs view
@@ -11,7 +11,7 @@ ) where import Summoner.Default (defaultGHC)-import Summoner.GhcVer (GhcVer (..), baseVer, latestLts, showGhcVer)+import Summoner.GhcVer (GhcVer (..), latestLts, showGhcVer) import Summoner.Settings (Settings (..)) import Summoner.Tree (TreeFs (..)) @@ -22,14 +22,9 @@ -- create @stack.yaml@ file with LTS corresponding to specified ghc version createStackYaml :: GhcVer -> TreeFs createStackYaml ghcV = File (toString $ "stack" <> ver <> ".yaml")- $ "resolver: " <> latestLts ghcV <> extraDeps+ $ "resolver: " <> latestLts ghcV <> "\n" where ver :: Text ver = if ghcV == defaultGHC then "" else "-" <> showGhcVer ghcV-- extraDeps :: Text- extraDeps = case settingsPrelude of- Nothing -> ""- Just _ -> "\n\nextra-deps: [base-noprelude-" <> baseVer ghcV <> "]"
src/Summoner/Text.hs view
@@ -49,8 +49,9 @@ && T.all C.isAlphaNum s && C.isUpper (T.head s) --- | Converts every element of list into 'Text' and then joins every element--- into single 'Text' like 'T.intercalate'.+{- | Converts every element of list into 'Text' and then joins every element+into single 'Text' like 'T.intercalate'.+-} intercalateMap :: Text -> (a -> Text) -> [a] -> Text intercalateMap between showT = T.intercalate between . map showT
src/Summoner/Tree.hs view
@@ -17,12 +17,11 @@ , showTree ) where +import Colourista (bold, reset) import System.Directory (createDirectoryIfMissing, withCurrentDirectory) import System.FilePath (splitDirectories) -import Summoner.Ansi (boldCode, resetCode) - -- | Describes simple structure of filesystem tree. data TreeFs -- | Name of directory (relative) and its containing entries@@ -55,11 +54,11 @@ pathToTree path content = let pathParts = splitDirectories path in case pathParts of- [] -> Dir path [] -- shouldn't happen+ [] -> Dir path [] -- shouldn't happen x:xs -> go x xs where go :: FilePath -> [FilePath] -> TreeFs- go p [] = File p content+ go p [] = File p content go p (x:xs) = Dir p [go x xs] {- | This functions inserts given 'TreeFs' node into the list of existing@@ -103,7 +102,7 @@ boldDir :: FilePath -> Text boldDir str = toText $ if isBold- then boldCode <> str <> resetCode+ then bold <> str <> reset else str extension :: Text
summoner.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: summoner-version: 1.4.0.0+version: 2.0.0.0 synopsis: Tool for scaffolding fully configured batteries-included production-level Haskell projects. description: Tool for scaffolding fully configured batteries-included production-level Haskell projects. See [README.md](https://github.com/kowainik/summoner#-summoner) for details.@@ -15,55 +15,84 @@ build-type: Simple stability: stable extra-doc-files: README.md- , CHANGELOG.md-tested-with: GHC == 8.2.2- , GHC == 8.4.4- , GHC == 8.6.5- , GHC == 8.8.1+ CHANGELOG.md+tested-with: GHC == 8.4.4+ GHC == 8.6.5+ GHC == 8.8.3 extra-source-files:- test/golden/fullProject/*.yml- test/golden/fullProject/*.yaml- test/golden/fullProject/*.md- test/golden/fullProject/*.cabal- test/golden/fullProject/src/*.hs- test/golden/fullProject/app/*.hs- test/golden/fullProject/test/*.hs- test/golden/fullProject/benchmark/*.hs- test/golden/fullProject/.gitignore- test/golden/fullProject/.travis.yml- test/golden/fullProject/.stylish-haskell.yaml- test/golden/fullProject/stack-8.4.4.yaml- test/golden/fullProject/stack-8.2.2.yaml- test/golden/fullProject/stack-8.0.2.yaml- test/golden/fullProject/LICENSE+ examples/summoner-default.toml - test/golden/smallProject/*.md- test/golden/smallProject/*.cabal- test/golden/smallProject/app/*.hs+ examples/cabal-minimal/*.md+ examples/cabal-minimal/*.cabal+ examples/cabal-minimal/app/*.hs + examples/cabal-full/*.yml+ examples/cabal-full/*.md+ examples/cabal-full/*.cabal+ examples/cabal-full/src/*.hs+ examples/cabal-full/app/*.hs+ examples/cabal-full/test/*.hs+ examples/cabal-full/benchmark/*.hs+ examples/cabal-full/.gitignore+ examples/cabal-full/.github/workflows/ci.yml+ examples/cabal-full/.travis.yml+ examples/cabal-full/LICENSE++ examples/stack-full/*.yml+ examples/stack-full/*.yaml+ examples/stack-full/*.md+ examples/stack-full/*.cabal+ examples/stack-full/src/*.hs+ examples/stack-full/app/*.hs+ examples/stack-full/test/*.hs+ examples/stack-full/benchmark/*.hs+ examples/stack-full/.gitignore+ examples/stack-full/.travis.yml+ examples/stack-full/LICENSE++ examples/full-batteries/*.yml+ examples/full-batteries/*.yaml+ examples/full-batteries/*.md+ examples/full-batteries/*.cabal+ examples/full-batteries/*.txt+ examples/full-batteries/src/*.hs+ examples/full-batteries/app/*.hs+ examples/full-batteries/test/*.hs+ examples/full-batteries/benchmark/*.hs+ examples/full-batteries/.gitignore+ examples/full-batteries/.github/CODEOWNERS+ examples/full-batteries/.github/workflows/ci.yml+ examples/full-batteries/.stylish-haskell.yaml+ examples/full-batteries/.travis.yml+ examples/full-batteries/LICENSE+ source-repository head type: git location: git@github.com:kowainik/summoner.git common common-options- build-depends: base >= 4.10 && < 4.14+ build-depends: base >= 4.11 && < 4.14 , relude ^>= 0.6.0.0 mixins: base hiding (Prelude) , relude (Relude as Prelude , Relude.Extra.Enum- , Relude.Extra.Validation , Relude.Unsafe ) ghc-options: -Wall- -Wincomplete-uni-patterns- -Wincomplete-record-updates -Wcompat -Widentities- -Wredundant-constraints- -fhide-source-paths- if impl(ghc >= 8.8.1)+ -Wincomplete-uni-patterns+ -Wincomplete-record-updates+ if impl(ghc >= 8.0)+ ghc-options: -Wredundant-constraints+ if impl(ghc >= 8.2)+ ghc-options: -fhide-source-paths+ if impl(ghc >= 8.4)+ ghc-options: -Wmissing-export-lists+ -Wpartial-fields+ if impl(ghc >= 8.8) ghc-options: -Wmissing-deriving-strategies -Werror=missing-deriving-strategies @@ -91,6 +120,7 @@ Summoner.Default Summoner.GhcVer Summoner.License+ Summoner.Mode Summoner.Project Summoner.Question Summoner.Settings@@ -110,12 +140,12 @@ other-modules: Paths_summoner build-depends: aeson >= 1.2.4.0 && < 1.5- , ansi-terminal >= 0.8 && < 0.11 , bytestring ^>= 0.10.8.2- , containers+ , colourista ^>= 0.0+ , containers ^>= 0.6 , directory ^>= 1.3.0.2 , filepath ^>= 1.4.1.2- , generic-deriving >= 1.12.2 && < 1.14+ , generic-data ^>= 0.8.0.0 , gitrev ^>= 1.3.1 , neat-interpolation ^>= 0.3.2.2 , optparse-applicative ^>= 0.15@@ -124,6 +154,7 @@ , text ^>= 1.2.3.0 , time >= 1.8 && < 1.10 , tomland ^>= 1.2.1.0+ , validation-selective ^>= 0.0.0.0 executable summon import: common-options@@ -153,11 +184,13 @@ , filepath , hedgehog >= 0.5.3 && < 1.1 , hspec >= 2.4.8+ , hspec-hedgehog ^>= 0.0.1.1 , neat-interpolation , text , tomland , tree-diff >= 0.0.2 && < 0.2 , summoner+ , validation-selective ghc-options: -threaded -rtsopts
test/Spec.hs view
@@ -1,33 +1,22 @@-module Main where+module Main (main) where -import Hedgehog (Group (..), checkParallel) import Test.Hspec (hspec) import Test.CustomPrelude (customPreludeSpec)-import Test.DecisionSpec (decisionMonoidMempty, decisionSemigroupAssoc)+import Test.DecisionSpec (decisionPropertySpec) import Test.Golden (goldenSpec) import Test.QuestionSpec (yesNoPromptSpec) import Test.Script (scriptSpec) import Test.Show (showCommandSpec)-import Test.TomlSpec (tomlProp)+import Test.TomlSpec (tomlSpec) main :: IO ()-main = do- hspec $ do- yesNoPromptSpec- scriptSpec- showCommandSpec- goldenSpec- customPreludeSpec- ifM (checkParallel hedgehogTests) exitSuccess exitFailure--hedgehogTests :: Group-hedgehogTests = Group "Roundtrip properties"- [ decisionSemigroupAssoc `named` "Decision Semigroup:Assoc"- , decisionMonoidMempty `named` "Decision Monoid:Mempty"- , tomlProp `named` "decode . encode == id"- ]- where- named :: a -> b -> (b, a)- named = flip (,)+main = hspec $ do+ yesNoPromptSpec+ scriptSpec+ showCommandSpec+ goldenSpec+ customPreludeSpec+ tomlSpec+ decisionPropertySpec
test/Test/DecisionSpec.hs view
@@ -2,32 +2,33 @@ {- HLINT ignore "Monoid law, left identity" -} module Test.DecisionSpec- ( decisionSemigroupAssoc- , decisionMonoidMempty+ ( decisionPropertySpec -- * Gen , genDecision ) where -import Hedgehog (MonadGen, Property, forAll, property, (===))+import Hedgehog (MonadGen, forAll, (===))+import Test.Hspec (Spec, describe, it)+import Test.Hspec.Hedgehog (hedgehog) import Summoner.Decision (Decision) import qualified Hedgehog.Gen as Gen -decisionSemigroupAssoc :: Property-decisionSemigroupAssoc = property $ do- x <- forAll genDecision- y <- forAll genDecision- z <- forAll genDecision- (x <> y) <> z === x <> (y <> z)+decisionPropertySpec :: Spec+decisionPropertySpec = describe "Decision property tests" $ do+ it "Decision Semigroup:Assoc" $ hedgehog $ do+ x <- forAll genDecision+ y <- forAll genDecision+ z <- forAll genDecision+ (x <> y) <> z === x <> (y <> z) -decisionMonoidMempty :: Property-decisionMonoidMempty = property $ do- x <- forAll genDecision- x <> mempty === x- mempty <> x === x+ it "Decision Monoid:Mempty" $ hedgehog $ do+ x <- forAll genDecision+ x <> mempty === x+ mempty <> x === x genDecision :: MonadGen m => m Decision genDecision = Gen.enumBounded
test/Test/Golden.hs view
@@ -33,10 +33,14 @@ goldenSpec :: Spec goldenSpec = describe "golden tests" $ do- it "correctly creates full project" $- checkProject "test/golden/fullProject" fullProject- it "correctly creates small project" $- checkProject "test/golden/smallProject" smallProject+ it "correctly scaffolds the 'cabal-minimal' project" $+ checkProject "examples/cabal-minimal" cabalMinimal+ it "correctly scaffolds the 'cabal-full' project" $+ checkProject "examples/cabal-full" cabalFull+ it "correctly scaffolds the 'stack-full' project" $+ checkProject "examples/stack-full" stackFull+ it "correctly scaffolds the 'full-batteries' project" $+ checkProject "examples/full-batteries" fullBatteries where checkProject :: FilePath -> Settings -> IO () checkProject path settings = do@@ -61,18 +65,19 @@ Dir path fs -> Dir path $ sort $ map sortTree fs -fullProject :: Settings-fullProject = Settings- { settingsRepo = "fullProject"+fullBatteries :: Settings+fullBatteries = Settings+ { settingsRepo = "full-batteries" , settingsOwner = "kowainik"- , settingsDescription = "Full test project"+ , settingsDescription = "Full-featured test project" , settingsFullName = "Kowainik" , settingsEmail = "xrom.xkov@gmail.com"- , settingsYear = "2018"+ , settingsYear = "2020" , settingsCategories = "Testing" , settingsLicenseName = MIT , settingsLicenseText = mitLicense , settingsGitHub = True+ , settingsGhActions = True , settingsPrivate = True , settingsTravis = True , settingsAppVeyor = True@@ -81,59 +86,121 @@ , settingsTest = True , settingsBench = True , settingsTestedVersions = [Ghc802 .. defaultGHC]- , settingsBaseType = "base-noprelude" , settingsPrelude = Just $ CustomPrelude "relude" "Relude" , settingsExtensions = ["ConstraintKinds", "LambdaCase", "OverloadedStrings"] , settingsGitignore = [".secret"] , settingsGhcOptions = ["-Wcompat", "-Widentities"] , settingsCabal = True , settingsStack = True- , settingsStylish = Just "This is stylish-haskell.yaml\n"- , settingsContributing = Just "This is contributing guide\n" , settingsNoUpload = True , settingsFiles = [ File "extra.txt" "See full content of the file [here](@github)\n" , Dir ".github" [File "CODEOWNERS" "* @chshersh @vrom911\n"]+ , File ".stylish-haskell.yaml" "This is stylish-haskell.yaml\n"+ , File "CONTRIBUTING.md" "This is contributing guide\n" ] } where mitLicense :: License mitLicense = License [text|- MIT License+MIT License - Copyright (c) 2018 Kowainik+Copyright (c) 2018 Kowainik - Permission is hereby granted, free of charge, to any person obtaining a copy- of this software and associated documentation files (the "Software"), to deal- in the Software without restriction, including without limitation the rights- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell- copies of the Software, and to permit persons to whom the Software is- furnished to do so, subject to the following conditions:+Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all- copies or substantial portions of the Software.+The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE- SOFTWARE.- |]+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.+|] -smallProject :: Settings-smallProject = Settings- { settingsRepo = "smallProject"+stackFull :: Settings+stackFull = Settings+ { settingsRepo = "stack-full" , settingsOwner = "kowainik"- , settingsDescription = "Small test project"+ , settingsDescription = "Stack-only example with all integrations" , settingsFullName = "Kowainik" , settingsEmail = "xrom.xkov@gmail.com"- , settingsYear = "2018"+ , settingsYear = "2020"+ , settingsCategories = "Testing"+ , settingsLicenseName = BSD3+ , settingsLicenseText = bsd3License+ , settingsGitHub = True+ , settingsGhActions = False+ , settingsPrivate = False+ , settingsTravis = True+ , settingsAppVeyor = True+ , settingsIsLib = True+ , settingsIsExe = True+ , settingsTest = True+ , settingsBench = True+ , settingsTestedVersions = [Ghc844 .. defaultGHC]+ , settingsPrelude = Just $ CustomPrelude "relude" "Relude"+ , settingsExtensions = ["DeriveGeneric", "TypeApplications"]+ , settingsGitignore = []+ , settingsGhcOptions = []+ , settingsCabal = False+ , settingsStack = True+ , settingsNoUpload = False+ , settingsFiles = []+ }++cabalFull :: Settings+cabalFull = Settings+ { settingsRepo = "cabal-full"+ , settingsOwner = "kowainik"+ , settingsDescription = "Cabal-only example with all integrations"+ , settingsFullName = "Kowainik"+ , settingsEmail = "xrom.xkov@gmail.com"+ , settingsYear = "2020"+ , settingsCategories = "Testing"+ , settingsLicenseName = BSD3+ , settingsLicenseText = bsd3License+ , settingsGitHub = True+ , settingsGhActions = True+ , settingsPrivate = False+ , settingsTravis = True+ , settingsAppVeyor = True+ , settingsIsLib = True+ , settingsIsExe = True+ , settingsTest = True+ , settingsBench = True+ , settingsTestedVersions = [Ghc844 .. defaultGHC]+ , settingsPrelude = Just $ CustomPrelude "relude" "Relude"+ , settingsExtensions = ["DeriveGeneric", "TypeApplications"]+ , settingsGitignore = []+ , settingsGhcOptions = []+ , settingsCabal = True+ , settingsStack = False+ , settingsNoUpload = False+ , settingsFiles = []+ }++cabalMinimal :: Settings+cabalMinimal = Settings+ { settingsRepo = "cabal-minimal"+ , settingsOwner = "kowainik"+ , settingsDescription = "Minimal cabal-only test project"+ , settingsFullName = "Kowainik"+ , settingsEmail = "xrom.xkov@gmail.com"+ , settingsYear = "2020" , settingsCategories = ""- , settingsLicenseName = None+ , settingsLicenseName = NONE , settingsLicenseText = "" , settingsGitHub = False+ , settingsGhActions = False , settingsPrivate = False , settingsTravis = False , settingsAppVeyor = False@@ -142,18 +209,48 @@ , settingsTest = False , settingsBench = False , settingsTestedVersions = [defaultGHC]- , settingsBaseType = "base" , settingsPrelude = Nothing , settingsExtensions = [] , settingsGhcOptions = [] , settingsGitignore = [] , settingsCabal = True , settingsStack = False- , settingsStylish = Nothing- , settingsContributing = Nothing , settingsNoUpload = True , settingsFiles = mempty }++bsd3License :: License+bsd3License = License [text|+BSD 3-Clause License++Copyright (c) 2020, Kowainik+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++1. Redistributions of source code must retain the above copyright notice, this+ list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright notice,+ this list of conditions and the following disclaimer in the documentation+ and/or other materials provided with the distribution.++3. Neither the name of the copyright holder nor the names of its+ contributors may be used to endorse or promote products derived from+ this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+|] -- Orphan instances
test/Test/Script.hs view
@@ -35,7 +35,7 @@ stackScript = [text| #!/usr/bin/env stack {- stack- --resolver lts-14.18+ --resolver lts-14.27 script --package base -}
test/Test/Show.hs view
@@ -14,10 +14,10 @@ goldenGhcTable :: [Text] goldenGhcTable =- [ "GHC-7.10.3 base-4.8.0.2 lts-6.35 "- , "GHC-8.0.2 base-4.9.1.0 lts-9.21 "- , "GHC-8.2.2 base-4.10.1.0 lts-11.22 "- , "GHC-8.4.4 base-4.11.1.0 lts-12.26 "- , "GHC-8.6.5 base-4.12.0.0 lts-14.18 "- , "GHC-8.8.1 base-4.13.0.0 nightly-2019-12-25"+ [ "GHC-7.10.3 base-4.8.0.2 lts-6.35"+ , "GHC-8.0.2 base-4.9.1.0 lts-9.21"+ , "GHC-8.2.2 base-4.10.1.0 lts-11.22"+ , "GHC-8.4.4 base-4.11.1.0 lts-12.26"+ , "GHC-8.6.5 base-4.12.0.0 lts-14.27"+ , "GHC-8.8.3 base-4.13.0.0 lts-15.5" ]
test/Test/TomlSpec.hs view
@@ -1,13 +1,17 @@ module Test.TomlSpec- ( tomlProp+ ( tomlSpec ) where -import Hedgehog (MonadGen, Property, forAll, property, tripping)+import Hedgehog (MonadGen, forAll, tripping) import Relude.Extra.Enum (universe)+import Test.Hspec (Spec, describe, it, shouldReturn, shouldSatisfy)+import Test.Hspec.Hedgehog (hedgehog) import Toml.Bi.Code (decode, encode)+import Validation (Validation (..)) -import Summoner.Config (ConfigP (..), PartialConfig, configCodec)+import Summoner.Config (ConfigP (..), PartialConfig, configCodec, defaultConfig, finalise) import Summoner.CustomPrelude (CustomPrelude (..))+import Summoner.Default (defaultConfigFileContent) import Summoner.GhcVer (GhcVer) import Summoner.License (LicenseName) import Summoner.Source (Source (..))@@ -18,11 +22,30 @@ import qualified Hedgehog.Range as Range -tomlProp :: Property-tomlProp = property $ do- configToml <- forAll genPartialConfig- tripping configToml (encode configCodec) (decode configCodec)+tomlSpec :: Spec+tomlSpec = describe "TOML Testing" $ do+ tomlConfigSpec+ tomlProp +tomlConfigSpec :: Spec+tomlConfigSpec = describe "TOML configuration spec" $ do+ it "finalises default configuration" $+ finalise defaultConfig `shouldSatisfy` isSuccess+ it "parses default configuration" $+ decode configCodec defaultConfigFileContent `shouldSatisfy` isRight+ it "default configuration is up-to-date" $+ readFileText "examples/summoner-default.toml"+ `shouldReturn` defaultConfigFileContent+ where+ isSuccess :: Validation e a -> Bool+ isSuccess = \case { Success _ -> True; _ -> False }++tomlProp :: Spec+tomlProp = describe "TOML property tests" $+ it "decode . encode == id" $ hedgehog $ do+ configToml <- forAll genPartialConfig+ tripping configToml (encode configCodec) (decode configCodec)+ ---------------------------------------------------------------------------- -- Generators ----------------------------------------------------------------------------@@ -72,6 +95,7 @@ cCabal <- genDecision cStack <- genDecision cGitHub <- genDecision+ cGhActions <- genDecision cTravis <- genDecision cAppVey <- genDecision cPrivate <- genDecision@@ -83,8 +107,6 @@ cExtensions <- genTextArr cGhcOptions <- genTextArr cGitignore <- genTextArr- cStylish <- Last <$> Gen.maybe genSource- cContributing <- Last <$> Gen.maybe genSource cNoUpload <- Any <$> Gen.bool cFiles <- Gen.map (Range.constant 0 10) (liftA2 (,) genString genSource)- pure Config{..}+ pure ConfigP{..}
− test/golden/fullProject/.gitignore
@@ -1,57 +0,0 @@-### Haskell-dist-dist-*-cabal-dev-*.o-*.hi-*.chi-*.chs.h-*.dyn_o-*.dyn_hi-*.prof-*.aux-*.hp-*.eventlog-.virtualenv-.hsenv-.hpc-.cabal-sandbox/-cabal.sandbox.config-cabal.config-cabal.project.local-.ghc.environment.*-.HTF/-# Stack-.stack-work/-stack.yaml.lock--### IDE/support-# Vim-[._]*.s[a-v][a-z]-[._]*.sw[a-p]-[._]s[a-v][a-z]-[._]sw[a-p]-*~-tags--# IntellijIDEA-.idea/-.ideaHaskellLib/-*.iml--# Atom-.haskell-ghc-mod.json--# VS-.vscode/--# Emacs-*#-.dir-locals.el-TAGS--# other-.DS_Store--# User specific-.secret
− test/golden/fullProject/.stylish-haskell.yaml
@@ -1,1 +0,0 @@-This is stylish-haskell.yaml
− test/golden/fullProject/.travis.yml
@@ -1,60 +0,0 @@-sudo: true-language: haskell--git:- depth: 5--cabal: "2.4"--cache:- directories:- - "$HOME/.cabal/store"- - "$HOME/.stack"- - "$TRAVIS_BUILD_DIR/.stack-work"--matrix:- include:- - ghc: 8.0.2- - ghc: 8.2.2- - ghc: 8.4.4- - ghc: 8.6.5- - ghc: 8.8.1- - - ghc: 8.2.2- env: STACK_YAML="$TRAVIS_BUILD_DIR/stack-8.2.2.yaml"- - - ghc: 8.4.4- env: STACK_YAML="$TRAVIS_BUILD_DIR/stack-8.4.4.yaml"- - - ghc: 8.6.5- env: STACK_YAML="$TRAVIS_BUILD_DIR/stack-8.6.5.yaml"- - - ghc: 8.8.1- env: STACK_YAML="$TRAVIS_BUILD_DIR/stack.yaml"--install:- # HLint check- - curl -sSL https://raw.github.com/ndmitchell/neil/master/misc/travis.sh | sh -s -- hlint .-- - |- if [ -z "$STACK_YAML" ]; then- ghc --version- cabal --version- cabal new-update- cabal new-build --enable-tests --enable-benchmarks- else- curl -sSL https://get.haskellstack.org/ | sh- stack --version- stack build --system-ghc --test --bench --no-run-tests --no-run-benchmarks --ghc-options=-Werror- fi--script:- - |- if [ -z "$STACK_YAML" ]; then- cabal new-test --enable-tests- else- stack test --system-ghc- fi--notifications:- email: false
− test/golden/fullProject/CHANGELOG.md
@@ -1,11 +0,0 @@-# Changelog--`fullProject` uses [PVP Versioning][1].-The changelog is available [on GitHub][2].--## 0.0.0.0--* Initially created.--[1]: https://pvp.haskell.org-[2]: https://github.com/kowainik/fullProject/releases
− test/golden/fullProject/CONTRIBUTING.md
@@ -1,1 +0,0 @@-This is contributing guide
− test/golden/fullProject/LICENSE
@@ -1,21 +0,0 @@-MIT License--Copyright (c) 2018 Kowainik--Permission is hereby granted, free of charge, to any person obtaining a copy-of this software and associated documentation files (the "Software"), to deal-in the Software without restriction, including without limitation the rights-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell-copies of the Software, and to permit persons to whom the Software is-furnished to do so, subject to the following conditions:--The above copyright notice and this permission notice shall be included in all-copies or substantial portions of the Software.--THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE-SOFTWARE.
− test/golden/fullProject/README.md
@@ -1,10 +0,0 @@-# fullProject--[](https://travis-ci.org/kowainik/fullProject)-[](https://ci.appveyor.com/project/kowainik/fullProject)-[](https://hackage.haskell.org/package/fullProject)-[](http://stackage.org/lts/package/fullProject)-[](http://stackage.org/nightly/package/fullProject)-[](LICENSE)--Full test project
− test/golden/fullProject/app/Main.hs
@@ -1,7 +0,0 @@-module Main (main) where--import FullProject (someFunc)---main :: IO ()-main = someFunc
− test/golden/fullProject/appveyor.yml
@@ -1,34 +0,0 @@-clone_folder: "c:\\WORK"-clone_depth: 5--# Do not build feature branch with open Pull Requests-skip_branch_with_pr: true--platform:- - x86_64--cache:- - "C:\\SR"- - dist-newstyle--environment:- global:- CABOPTS: --store-dir=C:\\SR-- matrix:- - GHCVER: 8.8.1--install:- - choco source add -n mistuke -s https://www.myget.org/F/mistuke/api/v2- - choco install -y cabal --version 2.4.1.0- - choco install -y ghc --version 8.8.1- - refreshenv--before_build:- - cabal --version- - ghc --version- - cabal %CABOPTS% v2-update--build_script:- - cabal %CABOPTS% v2-build --enable-tests- - cabal %CABOPTS% v2-test --enable-tests
− test/golden/fullProject/benchmark/Main.hs
@@ -1,7 +0,0 @@-module Main (main) where--import Gauge.Main---main :: IO ()-main = defaultMain [bench "const" (whnf const ())]
− test/golden/fullProject/fullProject.cabal
@@ -1,105 +0,0 @@-cabal-version: 2.0-name: fullProject-version: 0.0.0.0-synopsis: Full test project-description: Full test project-homepage: https://github.com/kowainik/fullProject-bug-reports: https://github.com/kowainik/fullProject/issues-license: MIT-license-file: LICENSE-author: Kowainik-maintainer: xrom.xkov@gmail.com-copyright: 2018 Kowainik-category: Testing-build-type: Simple-extra-doc-files: README.md- , CHANGELOG.md-tested-with: GHC == 8.0.2- , GHC == 8.2.2- , GHC == 8.4.4- , GHC == 8.6.5- , GHC == 8.8.1--source-repository head- type: git- location: https://github.com/kowainik/fullProject.git--library- hs-source-dirs: src- exposed-modules: FullProject- Prelude-- build-depends: base-noprelude >= 4.9.1.0 && < 4.14- , relude-- ghc-options: -Wall- -Wcompat- -Widentities-- default-language: Haskell2010- default-extensions: ConstraintKinds- LambdaCase- OverloadedStrings--executable fullProject- hs-source-dirs: app- main-is: Main.hs-- build-depends: base-noprelude >= 4.9.1.0 && < 4.14- , fullProject- , relude-- ghc-options: -Wall- -threaded- -rtsopts- -with-rtsopts=-N- -Wcompat- -Widentities-- default-language: Haskell2010- default-extensions: ConstraintKinds- LambdaCase- OverloadedStrings--test-suite fullProject-test- type: exitcode-stdio-1.0- hs-source-dirs: test- main-is: Spec.hs-- build-depends: base-noprelude >= 4.9.1.0 && < 4.14- , fullProject- , relude-- ghc-options: -Wall- -threaded- -rtsopts- -with-rtsopts=-N- -Wcompat- -Widentities-- default-language: Haskell2010- default-extensions: ConstraintKinds- LambdaCase- OverloadedStrings--benchmark fullProject-benchmark- type: exitcode-stdio-1.0- hs-source-dirs: benchmark- main-is: Main.hs-- build-depends: base-noprelude >= 4.9.1.0 && < 4.14- , gauge- , fullProject- , relude-- ghc-options: -Wall- -threaded- -rtsopts- -with-rtsopts=-N- -Wcompat- -Widentities-- default-language: Haskell2010- default-extensions: ConstraintKinds- LambdaCase- OverloadedStrings
− test/golden/fullProject/src/FullProject.hs
@@ -1,6 +0,0 @@-module FullProject- ( someFunc- ) where--someFunc :: IO ()-someFunc = putStrLn ("someFunc" :: String)
− test/golden/fullProject/src/Prelude.hs
@@ -1,7 +0,0 @@--- | Uses [relude](https://hackage.haskell.org/package/relude) as default Prelude.--module Prelude- ( module Relude- ) where--import Relude
− test/golden/fullProject/stack-8.0.2.yaml
@@ -1,3 +0,0 @@-resolver: lts-9.21--extra-deps: [base-noprelude-4.9.1.0]
− test/golden/fullProject/stack-8.2.2.yaml
@@ -1,3 +0,0 @@-resolver: lts-11.22--extra-deps: [base-noprelude-4.10.1.0]
− test/golden/fullProject/stack-8.4.4.yaml
@@ -1,3 +0,0 @@-resolver: lts-12.26--extra-deps: [base-noprelude-4.11.1.0]
− test/golden/fullProject/stack-8.6.5.yaml
@@ -1,3 +0,0 @@-resolver: lts-14.18--extra-deps: [base-noprelude-4.12.0.0]
− test/golden/fullProject/stack.yaml
@@ -1,3 +0,0 @@-resolver: nightly-2019-12-25--extra-deps: [base-noprelude-4.13.0.0]
− test/golden/fullProject/test/Spec.hs
@@ -1,4 +0,0 @@-module Main (main) where--main :: IO ()-main = putStrLn ("Test suite not yet implemented" :: String)
− test/golden/smallProject/CHANGELOG.md
@@ -1,9 +0,0 @@-# Changelog--`smallProject` uses [PVP Versioning][1].--## 0.0.0.0--* Initially created.--[1]: https://pvp.haskell.org
− test/golden/smallProject/README.md
@@ -1,5 +0,0 @@-# smallProject--[](https://hackage.haskell.org/package/smallProject)--Small test project
− test/golden/smallProject/app/Main.hs
@@ -1,4 +0,0 @@-module Main (main) where--main :: IO ()-main = putStrLn ("Hello, world!" :: String)
− test/golden/smallProject/smallProject.cabal
@@ -1,37 +0,0 @@-cabal-version: 2.0-name: smallProject-version: 0.0.0.0-synopsis: Small test project-description: Small test project-license: AllRightsReserved-author: Kowainik-maintainer: xrom.xkov@gmail.com-copyright: 2018 Kowainik-build-type: Simple-extra-doc-files: README.md- , CHANGELOG.md-tested-with: GHC == 8.8.1--executable smallProject- hs-source-dirs: app- main-is: Main.hs-- build-depends: base ^>= 4.13.0.0- - -- ghc-options: -Wall- -threaded- -rtsopts- -with-rtsopts=-N- -Wincomplete-uni-patterns- -Wincomplete-record-updates- -Wcompat- -Widentities- -Wredundant-constraints- -fhide-source-paths- -Wmissing-export-lists- -Wpartial-fields- -Wmissing-deriving-strategies-- default-language: Haskell2010