init-haskell-project/holy-haskell.sh

354 lines
9.8 KiB
Bash
Raw Permalink Normal View History

2013-11-14 23:25:55 +00:00
#!/usr/bin/env zsh
autoload colors
colors
for COLOR in RED GREEN YELLOW BLUE MAGENTA CYAN BLACK WHITE; do
eval $COLOR='$fg_no_bold[${(L)COLOR}]'
eval BOLD_$COLOR='$fg_bold[${(L)COLOR}]'
done
eval RESET='$reset_color'
# --- Function declaration and global variables
2013-11-14 23:25:55 +00:00
answer=""
# Capitalize a string
capitalize(){
local str="$(print -- "$*" | sed 's/-/ /g')"
print -- ${(C)str} | sed 's/ //g'
}
bk(){print -- "${GREEN}Bridgekeeper: $*${RESET}"}
bkn(){print -n -- "${GREEN}Bridgekeeper: $*${RESET}"}
you(){print -- "${YELLOW}You: $*${RESET}"}
log(){print -- $*}
err(){
{
case $(( $RANDOM % 1 )); in
0) bk "What... is your favourite colour?"
you "Blue. No, yel..."
log "[You are thrown over the edge into the volcano]"
you "You: Auuuuuuuuuuuugh"
bk " Hee hee heh."
;;
1) bk "What is the capital of Assyria?"
you "I don't know that!"
log "[You are thrown over the edge into the volcano]"
you "Auuuuuuuuuuuugh"
;;
esac
print -- "\n$*"
}>&2
exit 1
}
# Ask the user for some information
# Search in .gitconfig some hints to provide default value
2013-11-14 23:25:55 +00:00
ask(){
local elem=$1
local gitconfigelem
bkn "What is your $elem?"
2013-11-14 23:25:55 +00:00
[[ -e ~/.gitconfig ]] && {
gitconfigelem="$(< ~/.gitconfig| awk '$1 == "'$elem'" {$1="";$2="";gsub("^ *","");print}')"
[[ $gitconfigelem = "" ]] || {
print -n -- " ($gitconfigelem)"
}
}
print -n "\n> ";read answer
2013-11-14 23:25:55 +00:00
[[ $answer = "" ]] && answer=$gitconfigelem
}
# Delete the project directory and show an error message
reinit(){
cd ..
[[ -e $project ]] && \rm -rf $project
err "Something went wrong, I removed the project directory"
}
2013-11-14 23:25:55 +00:00
# --- Start asking questions
bk "Stop!"
bk "Who would cross the Bridge of Death"
bk "must answer me these questions three,"
bk "ere the other side he see."
you "Ask me the questions, bridgekeeper, I am not afraid.\n"
# project name
bk "What is the name of your project?"
print -n "> ";read project
project=${${project:gs/ /-/}:l} # use lowercase and replace spaces by dashes
# Verify project has the right format
if perl -e 'exit("'$project'" =~ /^[a-z][a-z0-9-]*$/)'; then
err "The project must start with a letter and contains only letter, number, spaces or dashes"
fi
2013-11-14 23:25:55 +00:00
[[ $project = "" ]] && err "Can't use empty project name"
[[ -e $project ]] && err "$project directory already exists"
2013-11-14 23:25:55 +00:00
# Find the main module name from the project name
module=$(capitalize $project)
# author name
2013-11-14 23:25:55 +00:00
ask name
name="$answer"
# email
2013-11-14 23:25:55 +00:00
ask email
email="$answer"
# github
bkn "What is your github user name?"
2013-11-15 18:57:11 +00:00
githubname=( $( curl -sH 'Accept: application/vnd.github.v3.text-match+json' 'https://api.github.com/search/users?q='$email|grep '"login":'|perl -pe 's/.*"([^"]*)",/$1/' ) )
(( ${#githubname} == 1 )) && print -- " ($githubname)"
print -n "> ";read github
[[ $github = "" ]] && github=$githubname
# synopsis
bk "What is your project in less than ten words?"
print -n "> ";read description
# --- We start the creation of the project files here
2013-11-14 23:25:55 +00:00
mkdir $project
cd $project
print -- "Initialize git"
2013-11-14 23:25:55 +00:00
git init .
2013-11-15 21:14:22 +00:00
print -- "Create files"
print -- "\t.gitignore"
2013-11-14 23:25:55 +00:00
>.gitignore cat <<END
.cabal-sandbox
cabal.sandbox.config
dist
*.swp
*~
.ghci
END
print -- "\t$project.cabal"
2013-11-14 23:25:55 +00:00
> $project.cabal cat <<END
-- Initial $project.cabal generated by cabal init. For further documentation,
-- see http://haskell.org/cabal/users-guide/
name: $project
version: 0.1.0.0
synopsis: $description
-- description:
homepage: http://github.com/$github/$project
license: MIT
license-file: LICENSE
author: $name
maintainer: $email
-- copyright:
category: Unknown
build-type: Simple
-- extra-source-files:
cabal-version: >=1.10
executable $project
2013-11-14 23:25:55 +00:00
main-is: Main.hs
-- other-modules:
-- other-extensions:
build-depends: base >=4.6 && <4.7
hs-source-dirs: src
ghc-options: -Wall
default-language: Haskell2010
library
exposed-modules: $module
, $module.Swallow
, $module.Coconut
2013-11-14 23:25:55 +00:00
-- other-modules:
-- other-extensions:
build-depends: base >=4.6 && <4.7
ghc-options: -Wall
hs-source-dirs: src
default-language: Haskell2010
executable test-$project
2013-11-14 23:25:55 +00:00
hs-source-dirs: test
ghc-options: -Wall
main-is: Test.hs
default-language: Haskell2010
build-depends: base ==4.6.*, Cabal >= 1.16.0
, $project
, HUnit
, QuickCheck
, smallcheck
, tasty
, tasty-hunit
, tasty-quickcheck
, tasty-smallcheck
test-suite Tests
hs-source-dirs: test
ghc-options: -Wall
main-is: Test.hs
Type: exitcode-stdio-1.0
default-language: Haskell2010
build-depends: base ==4.6.*, Cabal >= 1.16.0
, $project
, HUnit
, QuickCheck
, smallcheck
, tasty
, tasty-hunit
, tasty-quickcheck
, tasty-smallcheck
END
print -- "\tSetup.hs"
2013-11-14 23:25:55 +00:00
> Setup.hs cat <<END
import Distribution.Simple
main = defaultMain
END
print -- "\tLICENSE (MIT)"
2013-11-14 23:25:55 +00:00
> LICENSE cat<<END
The MIT License (MIT)
Copyright (c) $(date +%Y) $name
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.
END
mkdir src test
print -- "\ttest/Test.hs"
2013-11-14 23:25:55 +00:00
> test/Test.hs cat <<END
module Main where
import Test.Tasty (defaultMain,testGroup,TestTree)
import $module.Swallow.Test
import $module.Coconut.Test
2013-11-14 23:25:55 +00:00
main :: IO ()
main = defaultMain tests
tests :: TestTree
tests = testGroup "All Tests"
[ swallowSuite
, coconutSuite
2013-11-14 23:25:55 +00:00
]
END
mkdir -p src/$module
mkdir -p test/$module/{Swallow,Coconut}
2013-11-14 23:25:55 +00:00
print -- "\ttest/$module/Swallow/Test.hs"
> test/$module/Swallow/Test.hs cat <<END
module $module.Swallow.Test
(swallowSuite)
2013-11-14 23:25:55 +00:00
where
import Test.Tasty (testGroup, TestTree)
import Test.Tasty.HUnit
import $module.Swallow
2013-11-14 23:25:55 +00:00
swallowSuite :: TestTree
swallowSuite = testGroup "Swallow"
[testCase "swallow test" testSwallow]
2013-11-14 23:25:55 +00:00
testSwallow :: Assertion
testSwallow = "something" @=? swallow "some" "thing"
2013-11-14 23:25:55 +00:00
END
print -- "\tsrc/$module/Swallow.hs"
> src/$module/Swallow.hs cat <<END
module $module.Swallow (swallow) where
2013-11-14 23:25:55 +00:00
swallow :: String -> String -> String
swallow prefix suffix = prefix ++ suffix
2013-11-14 23:25:55 +00:00
END
print -- "\ttest/$module/Coconut/Test.hs"
> test/$module/Coconut/Test.hs cat <<END
2013-11-14 23:25:55 +00:00
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module $module.Coconut.Test
(coconutSuite)
2013-11-14 23:25:55 +00:00
where
import Test.Tasty (testGroup, TestTree)
import Test.Tasty.HUnit
import Test.Tasty.SmallCheck as SC
import $module.Coconut
2013-11-14 23:25:55 +00:00
-- Make instance of CoconutDataStruct
2013-11-14 23:25:55 +00:00
-- we simply use consN Constr where N is the arity of Constr (SmallCheck)
-- we also needed the
-- {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
import Test.SmallCheck.Series
instance Monad m => Serial m CoconutDataStruct where series = cons1 CoconutConstr
-- Now we could test properties with smallcheck on CoconutDataStruct type.
2013-11-14 23:25:55 +00:00
coconutSuite :: TestTree
coconutSuite = testGroup "coconut"
[ testCase "coconut" testCoconut
, SC.testProperty "coconut property" prop_coconut
2013-11-14 23:25:55 +00:00
]
testCoconut :: Assertion
testCoconut = coconut @=? 10
2013-11-14 23:25:55 +00:00
prop_coconut :: Property IO
prop_coconut = forAll $ \coconutStruct -> coconutfunc coconutStruct >= 0
2013-11-14 23:25:55 +00:00
END
print -- "\tsrc/$module/Coconut.hs"
> src/$module/Coconut.hs cat <<END
module $module.Coconut (coconut,coconutfunc,CoconutDataStruct(..)) where
data CoconutDataStruct = CoconutConstr [Integer] deriving (Show)
coconut :: Integer
coconut = 10
2013-11-14 23:25:55 +00:00
coconutfunc :: CoconutDataStruct -> Int
coconutfunc (CoconutConstr l) = length l
END
2013-11-14 23:25:55 +00:00
print -- "\tsrc/$module.hs"
> "src/$module.hs" cat <<END
module $module where
import $module.Swallow ()
import $module.Coconut ()
2013-11-14 23:25:55 +00:00
END
print -- "\tsrc/Main.hs"
> "src/Main.hs" cat <<END
module Main where
main :: IO ()
main = do
putStrLn "You fight with the strength of many men sir Knight..."
putStrLn "You have proved yourself worthy; will you join me?"
putStrLn "You make me sad. So be it. Come, Patsy."
END
print -- "Cabal sandboxing, install and test"
cabal sandbox init || reinit
2013-11-14 23:25:55 +00:00
cabal install
cabal test
testfile="./.cabal-sandbox/bin/test-$project"
[[ -x $testfile ]] && $testfile
# -- Final touch
print "\n\n"
bk "What... is the air-speed velocity of an unladen swallow?"
you "What do you mean? An African or European swallow?"
bk "Huh? I... I don't know that."
log "[the bridgekeeper is thrown over]"
bk "Auuuuuuuuuuuugh"
log "Sir Bedevere: How do you know so much about swallows?"
you "Well, you have to know these things when you're a king, you know."