packages feed

make-package (empty) → 1.0.0

raw patch · 16 files changed

+665/−0 lines, 16 filesdep +asyncdep +basedep +bytestringsetup-changed

Dependencies added: async, base, bytestring, configurator, containers, directory, filepath, github, haskeline, mtl, process, text, time, time-lens

Files

+ LICENSE view
@@ -0,0 +1,31 @@+Copyright (c) 2013, Chris Done+Copyright (c) 2013, Philipp Balzarek++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * 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.++    * Neither the name of Chris Done nor the names of other+      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+OWNER 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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ files/Main.hs view
@@ -0,0 +1,9 @@+-- | Main entry point to $name.+--+-- $desc++module Main where++-- | Main entry point.+main :: IO ()+main = return ()
+ files/Package.hs view
@@ -0,0 +1,3 @@+-- | $desc++module $exposed where
+ files/README.md view
@@ -0,0 +1,4 @@+$name+=====++$desc
+ files/Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ files/dot-gitignore view
@@ -0,0 +1,9 @@+*.o+*.hi+*~+dist/+cabal-dev/+.hsenv+TAGS+tags+*.tag
+ files/licenses/BSD3.license view
@@ -0,0 +1,24 @@+Copyright (c) $year, $name+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:+    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.+    * 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.+    * Neither the name of $name 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 <COPYRIGHT HOLDER> 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.
+ files/licenses/MIT.license view
@@ -0,0 +1,19 @@+Copyright (c) $year $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.
+ files/package.cabal view
@@ -0,0 +1,24 @@+name:                $name+version:             0.0.0+synopsis:            $desc+description:         $desc+license:             BSD3+license-file:        LICENSE+author:              $author+maintainer:          $email+copyright:           $year $author+category:            $category+build-type:          Simple+cabal-version:       >=1.8++library+  hs-source-dirs:    src/+  ghc-options:       -Wall -O2+  exposed-modules:   $exposed+  build-depends:     base >= 4 && <5++executable $name+  hs-source-dirs:    src/+  ghc-options:       -Wall -O2+  main-is:           Main.hs+  build-depends:     base >= 4 && < 5
+ make-package.cabal view
@@ -0,0 +1,44 @@+name:                make-package+version:             1.0.0+synopsis:            Make a cabalized package+description:         Create a cabalized haskell project+license:             BSD3+license-file:        LICENSE+copyright:           Chris Done, Philipp Balzarek+author:              Philipp Balzarek+maintainer:          p.balzarek@googlemail.com+category:            Development, Utils+homepage:            https://github.com/Philonous/make-package+Bug-Reports:         https://github.com/Philonous/make-package/issues/+build-type:          Simple+cabal-version:       >=1.8+data-files:          files/package.cabal+                     files/licenses/*.license+                     files/Setup.hs+                     files/dot-gitignore+                     files/Main.hs+                     files/README.md+                     files/Package.hs+extra-source-files:  make-package.conf++executable make-package+  hs-source-dirs:    src+  main-is:           Main.hs+  other-modules:     Github+                   , IO+                   , Options+  build-depends:     base >= 4 && < 5,+                     async,+                     bytestring,+                     configurator,+                     containers,+                     directory,+                     filepath,+                     github,+                     haskeline,+                     mtl,+                     process,+                     text,+                     time,+                     time-lens+  ghc-options:        -Wall
+ make-package.conf view
@@ -0,0 +1,49 @@+# Hashes turn the rest of the line into a comment+## lines after double hashes describe the following field++defaults{+## Default license+#  license = "MIT"++## Name of the author. Will be taken from git or prompted for if unset+#  author = "Igor Schmaltzhering"++## Email of the author. Will be taken from git or prompted for if unset+#  email = "no@spam.please"+}++git{+  enable = true++## Commit after copying templates+  do-commit = true++## Message to use for the initial commit (default is "initial commit")+#  initial-commit-message = "initial commit"+}++github{+## Enable Github interaction (Default = true)+#  enable = true++## Github username+#  username = "ISchmaltz"++## Time to wait before aborting github interactions (default is 5 seconds)+#  timeout = 5 # seconds++## Never try to clone an existing repository (default is false)+#  never-clone = false++## Never try to create github repository (default is false)+#  never-create = false++  auth {+## Github oauth string. Will be used when both the oauth string and the password+## are set+#    oauth = "<secret>"++# Github password+#    password = "<secret>"+  }+}
+ src/Github.hs view
@@ -0,0 +1,82 @@+{-# LANGUAGE DoAndIfThenElse #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE LambdaCase #-}++module Github where++import           Control.Applicative ((<$>))+import           Control.Monad+import           Control.Monad.Trans+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import           Github.Repos+import           System.Exit (exitSuccess)+import           System.Timeout++import           IO++-- | Check whether a repository with the given name exists on the users github+-- accoutn and suggest cloning it+maybeClone :: T.Text -> T.Text -> MakePackage ()+maybeClone repo dir = unlessConf "github.enable" (== False) $+                      unlessConf "github.never-clone" (== True) $ do+    uname <- confOrPrompt "github.username" "Github Username" ""+    liftIO $ putStrLn "Checking github for preexisting repos"+    timeoutDelay <- toSeconds <$> confLookup "github.timeout"+    mbRepoUrl <- liftIO . timeout timeoutDelay $ getRepo uname+    case mbRepoUrl of+        Nothing -> return ()+        Just (Left _) -> return ()+        Just (Right rUrl) ->+            do cloneP <- promptYesNo "clone"+                                     "A repository with this name already exists\+                                     \in your github account. Clone it instead?"+               when cloneP $ do run "git" ["clone", rUrl, T.unpack dir]+                                liftIO exitSuccess+  where+    toSeconds Nothing = 5 * 10^(6::Int)+    toSeconds (Just t) = t * 10^(6::Int)+    getRepo uname = fmap repoSshUrl <$> userRepo (T.unpack uname) (T.unpack repo)++-- | Create a github repository in the users account and set it as origin if+-- permitted by configuration+handleGithub :: T.Text -> T.Text -> MakePackage ()+handleGithub repo description = unlessConf "github.enable" (== False) $+                                unlessConf "github.never-create" (== True) $+                                unlessEmptyPrompt "repository" $ \rName ->  do+    auth <- confLookup "github.auth.oauth" >>= \case+        Just oauth -> return $ GithubOAuth oauth+        Nothing -> do uname <- confOrPrompt "github.username" "Github Username" ""+                      pwd <- confOrPrompt "github.auth.password"+                                          "Github Password"+                                          ""+                      return $ GithubBasicAuth (T.encodeUtf8 uname)+                                               (T.encodeUtf8 pwd)+    let nRepo = NewRepo { newRepoName         = T.unpack rName+                        , newRepoDescription  = Just (T.unpack description)+                        , newRepoHomepage     = Nothing+                        , newRepoPrivate      = Nothing+                        , newRepoHasIssues    = Nothing+                        , newRepoHasWiki      = Nothing+                        , newRepoAutoInit     = Nothing }+    liftIO $ putStrLn "Creating github repository"+    mbRepo <- liftIO $ createRepo auth nRepo+    case mbRepo of+        Left e -> do liftIO $ putStrLn $ "An Error occured while trying to create the repository:" ++ show e+        Right r -> do liftIO $ putStrLn "Done."+                      liftIO $ putStrLn "Configuring git to track the new repository"+                      run "git" ["remote", "add", "origin", repoSshUrl r]+                      run "git" ["config", "branch.master.remote", "origin"]+                      run "git" [ "config"+                                , "branch.master.merge"+                                , "refs/heads/master"]+                      liftIO $ putStrLn "Done."+                      whenConf "git.do-commit" (== True) $ run "git" [ "push"+                                                                     , "origin"+                                                                     , "master"]+  where+    unlessEmptyPrompt c f =+        do line <- prompt c "Repo Name (blank to skip)> " repo+           if T.null line+           then return ()+           else f line
+ src/IO.hs view
@@ -0,0 +1,203 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE PatternGuards #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE LambdaCase #-}++module IO where++import           Control.Applicative ((<$>))+import           Control.Monad.Reader+import           Control.Monad.State+import           Data.Char (isSpace)+import qualified Data.Configurator as Conf+import qualified Data.Configurator.Types as Conf+import           Data.List (isPrefixOf, isSuffixOf)+import           Data.Map (Map)+import qualified Data.Map as Map+import           Data.Maybe (fromMaybe)+import           Data.Monoid+import           Data.Text (Text)+import qualified Data.Text as T+import qualified Data.Text.IO as T+import           System.Console.Haskeline as HL+import           System.Console.Haskeline.MonadException()+import           System.Directory+import           System.Exit+import           System.FilePath+import           System.Process++import           Paths_make_package++newtype MakePackage a = MP {unConf :: StateT (Map Text Text)+                                      (ReaderT Conf.Config IO)+                                      a}+                        deriving (Monad, MonadIO, Functor)++withConfig :: MakePackage a -> IO a+withConfig (MP f) = do conf <- loadConfig+                       fst <$> (runReaderT (runStateT f (Map.empty)) conf)+++stored :: Conf.Configured a =>+          Text+       -> MakePackage (Maybe a)+stored c =+  do cache <- MP get+     return $ Conf.convert . Conf.String =<< Map.lookup c cache++optionDefault :: Conf.Configured b => Text -> b -> MakePackage b+optionDefault o d = fromMaybe d `fmap` stored o++confLookup :: Conf.Configured a => Conf.Name -> MakePackage (Maybe a)+confLookup c = stored c >>= \case+    Just x -> return $ Just x+    Nothing -> liftIO . (`Conf.lookup` c) =<< MP ask++confLookupDefault :: Conf.Configured a => Conf.Name -> a -> MakePackage a+confLookupDefault c d = maybe d id <$> confLookup c++-- | Prompt user+prompt :: T.Text -> T.Text -> Text -> MakePackage T.Text+prompt c p il = stored c >>= \case+    Just x -> return x+    Nothing ->+      do ln <- liftIO $ runInputT defaultSettings $+                   HL.getInputLineWithInitial (T.unpack $ p <> "> ")+                                              (T.unpack il, "")+         case ln of+             Nothing -> liftIO exitFailure+             Just line ->+                 do setOption c (T.pack line)+                    return (T.pack line)++promptYesNo :: T.Text -> T.Text -> MakePackage Bool+promptYesNo c p = stored c >>= \case+    Just x -> return x+    Nothing -> do liftIO $ T.putStrLn p+                  res <- liftIO $ runInputT defaultSettings go+                  setOption c (if res then "true" else "false")+                  return res+  where+    go = do char <- getInputChar ("[y]yes or [n]o> ")+            case char of+                Nothing -> liftIO exitFailure+                Just 'y' -> return True+                Just 'Y' -> return True+                Just 'n' -> return False+                Just 'N' -> return False+                _   -> go+++setOption :: Text -> Text -> MakePackage ()+setOption c x = MP $ modify (Map.insert c x)++-- | Get option from configuration file or prompt user+confOrPrompt :: T.Text -> T.Text -> T.Text -> MakePackage T.Text+confOrPrompt c p i = confLookup c >>= \case+    Nothing -> prompt c p i+    Just x  -> return x++-- | Get option from configuration file, git configuration or promp user+confOrGitOrPrompt :: T.Text -> T.Text -> T.Text -> T.Text -> MakePackage T.Text+confOrGitOrPrompt c gitQ p i = confLookup c >>= \case+    Just x -> return x+    Nothing -> confLookup "git.enable" >>= \case+               Just True -> queryGit (T.unpack gitQ) >>= \case+                   Just x -> do setOption c x+                                return x+                   Nothing -> do x <- prompt c p i+                                 setOption c x+                                 return x+               _ -> do x <- prompt c p i+                       setOption c x+                       return x++-- | Get license from configuration or prompt+getLicense :: MakePackage T.Text+getLicense = do+    licensesDir <- liftIO $ dataFile "licenses"+    licenses <- map takeBaseName . filter (".license" `isSuffixOf` ) <$>+                    liftIO (getDirectoryContents licensesDir)+    defaultLicense <- confLookup "defaults.license" >>= \case+        Nothing -> return Nothing+        Just l -> if (l `elem` licenses)+                  then return . Just $ T.pack l+                  else do liftIO . putStrLn $+                              "Warning, configured default license \""+                              ++ l ++ "\" is unknown"+                          return Nothing+    case defaultLicense of+        Just l -> return l+        Nothing -> T.pack <$> selectFrom "license" licenses++-- | Query git configuration+queryGit :: String -> MakePackage (Maybe T.Text)+queryGit q = liftIO (readProcessWithExitCode "git" ["config","--get", q] "")+             >>= \case+    (ExitSuccess, res, _stderr) -> return . Just $ oneLine res+    _ -> return Nothing+  where+    oneLine = T.concat . take 1 . T.lines . T.pack++-- | Run a programm, exit with error when program returns failure code+run :: String -> [String] -> MakePackage ()+run pg args = liftIO $ rawSystem pg args >>= \case+    ExitSuccess -> return ()+    ExitFailure n -> do+                    putStrLn $ "Program " ++ show pg+                        ++ " exited with error code " ++ show n+                    exitFailure++-- | Get the file path of packaged file+dataFile :: String -> IO String+dataFile fp = getDataFileName ("files/" <> fp)++-- | Load the configuration files+loadConfig :: IO Conf.Config+loadConfig = do+    appData <- getAppUserDataDirectory "make-package"+    home <- getHomeDirectory+    Conf.load [ Conf.Optional $ appData </> "make-package.conf"+              , Conf.Optional $ home </> ".make-package.conf"+              ]++-- | Run action unless configuration option is set and predicate is true+unlessConf :: Conf.Configured a => Conf.Name+                                -> (a -> Bool)+                                -> MakePackage ()+                                -> MakePackage ()+unlessConf option p f = confLookup option >>= \case+    Just x | p x -> return ()+    _ -> f++-- | Run action when configuration option is set and predicate is true+whenConf :: Conf.Configured a => Conf.Name+                              -> (a -> Bool)+                              -> MakePackage ()+                              -> MakePackage ()+whenConf option p f = confLookup option >>= \case+    Just x | p x -> f+    _ -> return ()++-- | Let the user select a string from a list+selectFrom :: String -> [String] -> MakePackage String+selectFrom p xs = liftIO $ do+    runInputT (setComplete cf defaultSettings) (HL.outputStrLn options >> go)+  where+    pairs = zip (map show [1:: Int ..]) xs+    options = unlines $ map (\(l,r) -> l <> ") " <> r ) pairs+    cf = completeWord Nothing " "+           (\wd -> return $ simpleCompletion <$> filter (wd `isPrefixOf`) xs)+    go = do+        mbln <- HL.getInputLine (p <> "> ")+        ln <- case mbln of+            Nothing -> liftIO exitFailure+            Just ln -> return . strip $ ln+        if | ln `elem` xs -> return ln+           | Just line <- ln `lookup` pairs -> return line+           | "?" <- ln -> HL.outputStrLn options >> go+           | otherwise -> do+               HL.outputStrLn $ ln ++ " is not valid, type ? to show options"+               go+    strip = reverse . dropWhile isSpace . reverse . dropWhile isSpace
+ src/Main.hs view
@@ -0,0 +1,77 @@+{-# LANGUAGE OverloadedStrings #-}++-- | Main entry point.++module Main where++import           IO+import           Github+import           Options++import           Control.Monad (when)+import           Control.Monad.Trans (liftIO)+import           Data.Monoid+import qualified Data.Text as T+import qualified Data.Text.IO as T+import           Data.Time+import           Data.Time.Lens+import           System.Directory+import           System.Exit (exitFailure)+import           System.FilePath++-- import System.Process++-- | Main entry point.+main :: IO ()+main = withConfig $ do+  handleArgs+  packageName <- prompt "package" "Package Name" ""+  packageDir <- optionDefault "directory" packageName+  checkExists packageDir+  maybeClone packageName packageDir+  author <- confOrGitOrPrompt "defaults.author" "user.name"  "Author" ""+  email <- confOrGitOrPrompt "defaults.email" "user.email" "Author Email" ""+  desc <- prompt "description" "Description" ""+  licenseType <- getLicense+  exposed <- prompt "modules" "Exposed Module(s)" "Main"+  category <- prompt "categories" "Category(s)" ""+  now <- liftIO getCurrentTime+  liftIO $ do createDirectoryIfMissing False (T.unpack packageName)+              setCurrentDirectory (T.unpack packageName)+              createDirectoryIfMissing False ("src/")+  let substitutions = [("name"     ,packageName)+                      ,("desc"     ,desc)+                      ,("email"    ,email)+                      ,("author"   ,author)+                      ,("year"     ,T.pack (show (getL year now)))+                      ,("category" ,category)+                      ,("exposed"  ,exposed)+                      ,("license"  ,licenseType)]+      copyTemplate = doCopyTemplate substitutions+  copyTemplate "package.cabal" (T.unpack (packageName <> ".cabal"))+  copyTemplate "dot-gitignore" ".gitignore"+  copyTemplate "Setup.hs" "Setup.hs"+  copyTemplate ("licenses"</> T.unpack licenseType <.> "license") "LICENSE"+  copyTemplate "README.md" "README.md"+  copyTemplate "Main.hs" "src/Main.hs"+  copyTemplate "Package.hs" (T.unpack ("src/" <> exposed <> ".hs"))+  run "cabal" ["configure"]+  unlessConf "git.enable" (== False) $ do+    run "git" ["init"]+    run "git" ["add", "."]+    whenConf "git.do-commit" (== True) $ do+      msg <- confLookupDefault "git.initial-commit-message" "initial commit"+      run "git" ["commit", "-m", msg]+  handleGithub packageName desc+  where+    checkExists pname = liftIO $+        do fileExists <- doesFileExist (T.unpack pname)+           dirExists <- doesDirectoryExist (T.unpack pname)+           when (fileExists || dirExists) $+               do putStrLn "A file or directory with this name already exists. Bailing out"+                  exitFailure++    doCopyTemplate substitutions infile outfile = liftIO $+        do template <- dataFile infile >>= T.readFile+           T.writeFile outfile (substitute template substitutions)+    substitute = foldl (\str (this,that) -> T.replace ("$" <> this) that str)
+ src/Options.hs view
@@ -0,0 +1,83 @@+{-# LANGUAGE DoAndIfThenElse #-}+{-# LANGUAGE OverloadedStrings #-}++module Options where++import           Control.Monad+import           Control.Monad.Trans+import qualified Data.Text as T+import           System.Console.GetOpt+import           System.Environment+import           System.Exit (exitSuccess, exitFailure)++import           IO++setOpt :: T.Text -> String -> ArgDescr (MakePackage ())+setOpt o = ReqArg (setOption o . T.pack)++options :: [OptDescr (MakePackage ())]+options = [ Option "h" ["help"] (NoArg $ help [])+              "display this help text"+          , Option "P" ["package"] (setOpt "package" "string")+              "package name"+          , Option "d" ["dir"] (setOpt "directory" "string")+            "directory to initialize in (default = package name)"+          , Option "A" ["author"] (setOpt "defaults.author" "string")+            "package author"+          , Option "E" ["email"] (setOpt "defaults.email" "string")+            "package author email address"+          , Option "D" ["description"] (setOpt "description" "string")+            "package description"+          , Option "L" ["license"] (setOpt "defaults.license" "string")+            "license name"+          , Option "M" ["modules"] (setOpt "modules" "string")+            "exposed modules"+          , Option "H" ["categories"] (setOpt "categories" "string")+            "hackage categories"+          , Option "G" ["git"] (NoArg $ setOption "git.enable" "true")+            "enable git"+          , Option "g" ["no-git"] (NoArg $ setOption "git.enable" "false")+            "disable git"+          , Option "C" ["commit"] (NoArg $ setOption "git.do-commit" "true")+            "run git commit"+          , Option "c" ["no-commit"] (NoArg $ setOption "git.do-commit" "false")+            "don't run git commit"+          , Option "M" ["commit-message"] (setOpt "git.initial-commit-message"+                                                  "string")+            "commit message to use"+          , Option "G" ["github"] (NoArg $ setOption "github.enable" "true")+            "enable github integration"+          , Option "g" ["no-github"] (NoArg $ setOption "github.enable" "false")+            "disable github integration"+          , Option "U" ["username"] (setOpt "github.username" "string")+            "github username"+          , Option "R" ["repository"] (setOpt "github.repository" "string")+            "github repository to create"+          , Option "T" ["timeout"] (setOpt "github.timeout" "int")+            "timeout for github interactions in seconds"+          , Option "K" ["clone"] (NoArg $ setOption "clone" "true")+            "clone existing repository "+          , Option "k" ["no-clone"] (NoArg $ setOption "clone" "false")+            "don't clone existing repository "+          , Option "O" ["oauth"] (setOpt "github.auth.oauth" "string")+            "github oauth token"+          , Option "P" ["password"] (setOpt "github.auth.password" "string")+            "github password"+          ]++help :: MonadIO m => [String] -> m a+help errors = liftIO $+  do forM_  errors putStrLn+     putStrLn $ usageInfo "make-package [package-name] [options]" options+     if null errors+     then exitSuccess+     else exitFailure++handleArgs :: MakePackage ()+handleArgs =+  do args <- liftIO $ getArgs+     let (opts, _, errs) = getOpt (ReturnInOrder (setOption "package" . T.pack))+                                  options+                                  args+     unless (null errs) $ help errs+     sequence_ opts