Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#48] Add configurations, read from the file #59

Merged
merged 5 commits into from
May 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
=====

* __Important:__ Rename `hs-init` to `summoner`. Transform the script into the package.
* [#54](https://github.com/vrom911/hs-init/issues/54):
* [#54](https://github.com/kowainik/summoner/issues/54):
Rename `on` and `off` commands to `with` and `without`.
* [#48](https://github.com/kowainik/summoner/issues/48):
- Add ability to write configurations file. Remove `Targets` data type.
Use `Config` instead for default, file and CLI configurations.


# hs-init

Expand Down
62 changes: 60 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,72 @@ as well as a repository under your github account (if requested).

### Usage

See the basic usage syntax below:
There are several options how to set particular configurations:

1. Default configuration file (`~/summoner.toml`).
2. Explicitly specified configuration file by `--file FILENAME` option (used instead of default one if specified).
3. Options that are stated by CLI arguments.
4. Interactively inputed answers during work of the `summon` command
(for the options that were not specified on previous steps).

So the configuration uses [`Partial Options Monoid Pattern`](https://medium.com/@jonathangfischoff/the-partial-options-monoid-pattern-31914a71fc67).

If none of the mentioned above cases used then the configuration will be built interactively.

#### Configurations

##### `.toml` files:

Here is the list of the options that could be configured for your needs:

* `owner` – `GitHub` login.
* `fullName` – full name.
* `email` – e-mail address.
* `license` – license (possible options: `MIT`, `BSD2`, `BSD3`, `GPL-2`, `GPL-3`,
`LGPL-2.1`, `LGPL-3`, `AGPL-3`, `Apache-2.0`, `MPL-2.0`).
* `ghcVersions` – `summoner` uses default `GHC-8.2.2`. But additionally you can specify other versions.
For each version `x.y.z` the `stack-x.y.z.yaml` will be created.
* `github` – `true` if you want to turn on `GitHub` integration by default,
`false` if you don't. If not specified it would be asked during each run of the `summoner`.
* `travis` – `true` if you want to turn on `Travis` integration by default,
`false` if you don't. Ignored if `github = false`.
If not specified it would be asked during each run of the `summoner`.
* `appveyor` – `true` if you want to turn on `AppVeyor` integration by default,
`false` if you don't. Ignored if `github = false`.
If not specified it would be asked during each run of the `summoner`.
* `private` – `true` if you want to create private repositories by default,
`false` if you don't. Ignored if `github = false`.
If not specified it would be asked during each run of the `summoner`.
* `bscript` – `true` if you want to include [build script](#build-script) by default,
`false` if you don't. If not specified it would be asked during each run of the `summoner`.
* `lib` – `true` if you want to create `src` folder with dummy `Lib.hs` file and library target by default,
`false` if you don't. If not specified it would be asked during each run of the `summoner`.
* `exe` – `true` if you want to create `app` folder with dummy `Main.hs` file and executable target by default,
`false` if you don't. If not specified it would be asked during each run of the `summoner`.
* `test` – `true` if you want to create `test` folder with dummy `Spec.hs` file and test target by default,
`false` if you don't. If not specified it would be asked during each run of the `summoner`.
* `bench` – `true` if you want to create `benchmark` folder with `Main.hs` file with dummy `gauge` library usage example by default,
`false` if you don't. If not specified it would be asked during each run of the `summoner`.


See example of [configuration for projects of `Kowainik` organization](https://github.com/kowainik/org/blob/master/summoner.toml).


By default the `summoner` will look for the configuration file (`summoner.toml`) in home directory.

The other way to specify some particular `.toml` file is `summon PROJECTNAME --file FILEPATH` command.

##### CLI

See the basic usage syntax below (you can check it out with `summon --help` command):

```
summon PROJECT_NAME [with [OPTIONS]] [without [OPTIONS]]

Available global 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 if present
Available commands:
with Specify options to enable
without Specify options to disable
Expand Down
4 changes: 4 additions & 0 deletions src/Summoner.hs
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
module Summoner
( module Summoner.Ansi
, module Summoner.CLI
, module Summoner.Config
, module Summoner.Default
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I right that Default module will be removed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it should be

, module Summoner.License
, module Summoner.Process
, module Summoner.Project
, module Summoner.ProjectData
, module Summoner.Question
, module Summoner.Template
, module Summoner.Validation
) where

import Summoner.Ansi
import Summoner.CLI
import Summoner.Config
import Summoner.Default
import Summoner.License
import Summoner.Process
import Summoner.Project
import Summoner.ProjectData
import Summoner.Question
import Summoner.Template
import Summoner.Validation
8 changes: 6 additions & 2 deletions src/Summoner/Ansi.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ module Summoner.Ansi
, successMessage
, warningMessage
, errorMessage
, infoMessage
, skipMessage
) where

import Data.Semigroup (Semigroup (..))
import Data.Text (Text)
import System.Console.ANSI (Color (Blue, Green, Red, Yellow), ColorIntensity (Vivid),
import System.Console.ANSI (Color (Blue, Cyan, Green, Red, Yellow), ColorIntensity (Vivid),
ConsoleIntensity (BoldIntensity), ConsoleLayer (Foreground),
SGR (Reset, SetColor, SetConsoleIntensity), setSGR)
import System.IO (hFlush, stdout)
Expand Down Expand Up @@ -63,7 +65,9 @@ colorMessage color message = do
T.putStrLn $ " " <> message
reset

errorMessage, warningMessage, successMessage :: Text -> IO ()
errorMessage, warningMessage, successMessage, infoMessage, skipMessage :: Text -> IO ()
errorMessage = colorMessage Red
warningMessage = colorMessage Yellow
successMessage = colorMessage Green
infoMessage = colorMessage Blue
skipMessage = colorMessage Cyan
96 changes: 73 additions & 23 deletions src/Summoner/CLI.hs
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TupleSections #-}

-- | This module contains functions and data types to parse CLI inputs.

module Summoner.CLI
( summon
) where

import Data.Foldable (fold)
import Data.Foldable (fold, for_)
import Data.Semigroup (Semigroup (..))
import Data.Text (Text)
import NeatInterpolation (text)
import Options.Applicative (Parser, ParserInfo, command, execParser, flag, fullDesc, help, helper,
info, infoFooter, infoHeader, long, metavar, optional, progDesc, short,
strArgument, subparser)
strArgument, strOption, subparser)
import Options.Applicative.Help.Chunk (stringChunk)
import System.Directory (doesFileExist)
import System.Exit (exitFailure)

import Summoner.Ansi (boldText)
import Summoner.Default (endLine)
import Summoner.Project (Decision (..), Targets (..), generateProject)
import Summoner.Ansi (boldText, errorMessage, infoMessage, warningMessage)
import Summoner.Config (ConfigP (..), PartialConfig, defaultConfig, finalise, loadFileConfig)
import Summoner.Default (defaultConfigFile, endLine)
import Summoner.Project (generateProject)
import Summoner.ProjectData (Decision (..))
import Summoner.Validation (Validation (..))

import qualified Data.Text as T

Expand All @@ -31,27 +37,63 @@ summon = execParser prsr >>= runWithOptions

-- | Run 'hs-init' with cli options
runWithOptions :: InitOpts -> IO ()
runWithOptions (InitOpts projectName targets) = do
-- Generate the project.
generateProject projectName targets
runWithOptions (InitOpts projectName cliConfig maybeFile) = do
(isDefault, file) <- case maybeFile of
Nothing -> (True,) <$> defaultConfigFile
Just x -> pure (False, x)
isFile <- doesFileExist file
fileConfig <-
if isFile
then do
infoMessage $ "Configurations from " <> T.pack file <> " will be used."
loadFileConfig file
else if isDefault
then do
fp <- T.pack <$> defaultConfigFile
warningMessage $ "Default config " <> fp <> " file is missing."
pure mempty
else do
errorMessage $ "Specified configuration file " <> T.pack file <> " is not found."
exitFailure
-- union all possible configs
let unionConfig = defaultConfig <> fileConfig <> cliConfig
-- get the final config
finalConfig <- case finalise unionConfig of
Failure msgs -> do
for_ msgs errorMessage
exitFailure
Success c -> pure c
-- Generate the project.
generateProject projectName finalConfig

boldText "\nJob's done\n"

-- | Initial parsed options from cli
data InitOpts = InitOpts Text Targets -- ^ Includes the project name and target options.
data InitOpts = InitOpts Text PartialConfig (Maybe FilePath)
-- ^ Includes the project name, config from the CLI and possible file where custom congifs are.

targetsP :: Decision -> Parser Targets
targetsP :: Decision -> Parser PartialConfig
targetsP d = do
githubFlag <- githubP d
travisFlag <- travisP d
appVeyorFlag <- appVeyorP d
privateFlag <- privateP d
scriptFlag <- scriptP d
isLibrary <- libraryP d
isExecutable <- execP d
isTest <- testP d
isBenchmark <- benchmarkP d
pure Targets{..}
cGitHub <- githubP d
cTravis <- travisP d
cAppVey <- appVeyorP d
cPrivate <- privateP d
cScript <- scriptP d
cLib <- libraryP d
cExe <- execP d
cTest <- testP d
cBench <- benchmarkP d
pure mempty
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's too pity we can't write something like this pure mempty{..}... I with RecordWildCards update syntax exist..

{ cGitHub = cGitHub
, cTravis = cTravis
, cAppVey = cAppVey
, cPrivate= cPrivate
, cScript = cScript
, cLib = cLib
, cExe = cExe
, cTest = cTest
, cBench = cBench
}

githubP :: Decision -> Parser Decision
githubP d = flag Idk d
Expand Down Expand Up @@ -107,25 +149,33 @@ benchmarkP d = flag Idk d
<> short 'b'
<> help "Benchmarks"

withP :: Parser Targets
withP :: Parser PartialConfig
withP = subparser $ mconcat
[ metavar "with [OPTIONS]"
, command "with" $ info (helper <*> targetsP Yes) (progDesc "Specify options to enable")
]

withoutP :: Parser Targets
withoutP :: Parser PartialConfig
withoutP = subparser $ mconcat
[ metavar "without [OPTIONS]"
, command "without" $ info (helper <*> targetsP Nop) (progDesc "Specify options to disable")
]

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"

optsP :: Parser InitOpts
optsP = do
projectName <- strArgument (metavar "PROJECT_NAME")
with <- optional withP
without <- optional withoutP
file <- optional fileP

pure $ InitOpts projectName (fold $ with <> without)
pure $ InitOpts projectName (fold $ with <> without) file

prsr :: ParserInfo InitOpts
prsr = modifyHeader
Expand Down
Loading