packages feed

hackmanager (empty) → 0.1.0.0

raw patch · 15 files changed

+760/−0 lines, 15 filesdep +Cabaldep +Globdep +basesetup-changed

Dependencies added: Cabal, Glob, base, bytestring, directory, file-embed, hackmanager, hastache, http-client, http-client-tls, http-types, mtl, optparse-applicative, process, text

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2015 Alexander Thiemann++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 Alexander Thiemann 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.
+ README.md view
@@ -0,0 +1,90 @@+hackmanager+=====++[![Build Status](https://travis-ci.org/agrafix/hackmanager.svg)](https://travis-ci.org/agrafix/hackmanager)+++## Intro+++Generate useful files for Haskell projects++## Cli Usage: hackmanager++```sh+$ hackmanager --help+hackmanager - Generate useful files for Haskell projects++Usage: hackmanager COMMAND+  Simplify managing Haskell projects by generating files like README.md,+  .travis.yml, etc.++Available options:+  -h,--help                Show this help text++Available commands:+  readme                   +  travis                   +  gitignore                ++(c) 2015 Alexander Thiemann - BSD3 License++```++## Library Usage Example++```haskell+module Main where++import Hack.Manager.Collector+import Hack.Manager.Readme++import qualified Data.Text as T++main :: IO ()+main =+    do pi <- getProjectInfo+       case pi of+         Left err -> putStrLn err+         Right info ->+             do rm <- renderReadme info+                putStrLn (T.unpack rm)++```++## Install++* From Source (cabal): `git clone https://github.com/agrafix/hackmanager.git && cd hackmanager && cabal install`+* From Source (stack): `git clone https://github.com/agrafix/hackmanager.git && cd hackmanager && stack build`++## Features++* Automagically collect package information such as+	* package name+	* GHC compatibility+	* stack Project+	* Hackage / Stackage status+	* License+	* Examples+	* Cli Usage+* Typecheck examples+* Generate informative README.md (Can be extended using a MORE.md)+* Generate .travis.yml (cabal or stack based)+* Generate .gitignore++The generated `.travis.yml` and `.gitignore` are intended as starting templates, while the generated `README.md` should not be modified by hand. Rerun `hackmanager readme` before every commit (commit hook?) to keep it up to date. If you would like to add custom sections, create a `MORE.md`.++## Roadmap++There's no real roadmap - I will add features as needed. I am open to any contributions!++## Misc++### Supported GHC Versions++* 7.10.2++### License++Released under the BSD3 license.+(c) 2015 Alexander Thiemann
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ app/Main.hs view
@@ -0,0 +1,64 @@+{-# LANGUAGE OverloadedStrings #-}+module Main where++import Hack.Manager.Collector+import Hack.Manager.Gitignore+import Hack.Manager.Readme+import Hack.Manager.Travis+import Hack.Manager.Types++import Control.Monad+import Options.Applicative+import qualified Data.Text as T+import qualified Data.Text.IO as T++main :: IO ()+main = join $ execParser optParser++withProjectInfo :: (ProjectInfo -> IO ()) -> IO ()+withProjectInfo cont =+    do raw <- getProjectInfo+       case raw of+         Left err ->+             putStrLn err+         Right pinfo ->+             cont pinfo++makeReadme :: IO ()+makeReadme =+    withProjectInfo $ \pinfo ->+    do rm <- renderReadme pinfo+       T.writeFile "README.md" rm++makeTravis :: TravisOpts -> IO ()+makeTravis to =+    withProjectInfo $ \pinfo ->+    do travis <- renderTravis to pinfo+       T.writeFile ".travis.yml" travis++travisOptsParser :: Parser TravisOpts+travisOptsParser =+    TravisOpts+    <$> (T.pack <$> argument str (metavar "GHC-RELEASE_VERSION"))+    <*> switch (long "use-stack" <> help "Use stack to run travis build")++makeGitignore :: IO ()+makeGitignore =+    withProjectInfo $ \pinfo ->+    do gitignore <- renderGitignore pinfo+       T.writeFile ".gitignore" gitignore++commands :: Mod CommandFields (IO ())+commands =+    command "readme" (info (pure makeReadme) idm)+    <> command "travis" (info (makeTravis <$> travisOptsParser) idm)+    <> command "gitignore" (info (pure makeGitignore) idm)++optParser :: ParserInfo (IO ())+optParser =+    info (helper <*> subparser commands)+         ( fullDesc+         <> progDesc "Simplify managing Haskell projects by generating files like README.md, .travis.yml, etc."+         <> header "hackmanager - Generate useful files for Haskell projects"+         <> footer "(c) 2015 Alexander Thiemann - BSD3 License"+         )
+ examples/ReadmePrinter.hs view
@@ -0,0 +1,15 @@+module Main where++import Hack.Manager.Collector+import Hack.Manager.Readme++import qualified Data.Text as T++main :: IO ()+main =+    do pi <- getProjectInfo+       case pi of+         Left err -> putStrLn err+         Right info ->+             do rm <- renderReadme info+                putStrLn (T.unpack rm)
+ hackmanager.cabal view
@@ -0,0 +1,60 @@+name:                hackmanager+version:             0.1.0.0+synopsis:            Generate useful files for Haskell projects+description:         Simplify managing Haskell projects by generating files like README.md, .travis.yml, etc.+homepage:            http://github.com/agrafix/hackmanager+license:             BSD3+license-file:        LICENSE+author:              Alexander Thiemann <mail@athiemann.net>+maintainer:          Alexander Thiemann <mail@athiemann.net>+copyright:           (c) 2015 Alexander Thiemann+category:            Development+build-type:          Simple+cabal-version:       >=1.10+tested-with:         GHC==7.10.*+extra-source-files:+    README.md+    templates/README.mustache+    templates/travis-stack.mustache+    templates/travis-cabal.mustache+    templates/gitignore.mustache+    examples/ReadmePrinter.hs++library+  hs-source-dirs:      src+  exposed-modules:+                       Hack.Manager.Readme+                       Hack.Manager.Types+                       Hack.Manager.Collector+                       Hack.Manager.Travis+                       Hack.Manager.Gitignore+  build-depends:+                       base >= 4.7 && < 5,+                       hastache >=0.6,+                       file-embed >=0.0.8,+                       bytestring >=0.10,+                       Glob >=0.7,+                       Cabal >=1.20,+                       text >=1.2,+                       mtl >=2.2,+                       process >=1.2,+                       http-client >=0.4,+                       http-client-tls >=0.2,+                       http-types >=0.8.6,+                       directory >=1.2+  default-language:    Haskell2010++executable hackmanager+  hs-source-dirs:      app+  main-is:             Main.hs+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N+  build-depends:+                       base,+                       hackmanager,+                       text >=1.2,+                       optparse-applicative >=0.11+  default-language:    Haskell2010++source-repository head+  type:     git+  location: https://github.com/agrafix/hackmanager
+ src/Hack/Manager/Collector.hs view
@@ -0,0 +1,167 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE FlexibleContexts #-}+module Hack.Manager.Collector where++import Hack.Manager.Types++import Control.Exception+import Control.Monad.Except+import System.Directory+import System.Exit+import System.Process+import qualified Data.List as L+import qualified Data.Text as T+import qualified Distribution.Compiler as Comp+import qualified Distribution.Package as Pkg+import qualified Distribution.PackageDescription as PD+import qualified Distribution.PackageDescription.Parse as PD+import qualified Distribution.Text as DT+import qualified Distribution.Version as Vers+import qualified Network.HTTP.Client as C+import qualified Network.HTTP.Client.TLS as C+import qualified Network.HTTP.Types.Status as Http+import qualified System.FilePath.Glob as G++getProjectInfo :: IO (Either String ProjectInfo)+getProjectInfo =+    runExceptT $+    do cabalFiles <- liftIO $ G.glob "*.cabal"+       case cabalFiles of+         [] -> throwError "No cabal file in working directory!"+         (f1:_) ->+             do cabalData <- liftIO $ readFile f1+                case PD.parsePackageDescription cabalData of+                  PD.ParseFailed err -> throwError (show err)+                  PD.ParseOk _ val -> compileProjectInfo val++onStackageCheck :: T.Text -> IO Bool+onStackageCheck projectName =+    do mgr <- C.newManager C.tlsManagerSettings+       initReq <- C.parseUrl ("https://www.stackage.org/package/" ++ T.unpack projectName)+       let tryGet =+               do resp <- C.httpNoBody initReq mgr+                  return $ C.responseStatus resp == Http.ok200+       tryGet `catch` \(_ :: SomeException) -> return False++onHackageCheck :: T.Text -> IO Bool+onHackageCheck projectName =+    do mgr <- C.newManager C.tlsManagerSettings+       initReq <- C.parseUrl ("https://hackage.haskell.org/package/" ++ T.unpack projectName)+       let tryGet =+               do resp <- C.httpNoBody initReq mgr+                  return $ C.responseStatus resp == Http.ok200+       tryGet `catch` \(_ :: SomeException) -> return False++findGhcVersions :: [(Comp.CompilerFlavor, Vers.VersionRange)] -> ExceptT String IO [T.Text]+findGhcVersions origVersions =+    forM versions $ \vers ->+        let loop [] = throwError ("Unknown ghc version: " ++ show vers)+            loop (x:xs) =+                if Vers.withinRange x vers then return x else loop xs+        in liftM (T.pack . DT.display) $ loop ghcLatest+    where+      versions = map snd $ filter (\(flavor, _) -> flavor == Comp.GHC) origVersions+      ghcLatest =+          [ Vers.Version [7, 4, 2] []+          , Vers.Version [7, 6, 3] []+          , Vers.Version [7, 8, 4] []+          , Vers.Version [7, 10, 2] []+          ]++getCliUsage :: Bool -> [String] -> ExceptT String IO [CliExecutable]+getCliUsage hasStack exec =+    forM exec $ \program ->+    do (ec, stdOut, stdErr) <-+           liftIO $+           if hasStack+           then readProcessWithExitCode "/bin/bash" ["-c", "stack exec -- " ++ program ++ " --help"] ""+           else readProcessWithExitCode "/bin/bash" ["-c", "cabal run -- " ++ program ++ " --help"] ""+       when (ec /= ExitSuccess) $+            throwError $ "Failed to run " ++ program ++ " --help to retrieve cli usage. StdOut was: " ++ stdOut ++ " \n StdErr was: " ++ stdErr+       return+          CliExecutable+          { ce_name = T.pack program+          , ce_help = T.pack stdOut+          }++moreFile :: ExceptT String IO (Maybe T.Text)+moreFile =+    liftIO $+    do more <- doesFileExist "MORE.md"+       if more+       then do ct <- readFile "MORE.md"+               return (Just $ T.pack ct)+       else return Nothing++compileProjectInfo :: PD.GenericPackageDescription -> ExceptT String IO ProjectInfo+compileProjectInfo gpd =+    do let pkgName = T.pack $ Pkg.unPackageName $ Pkg.pkgName $ PD.package pd+       ghInfo <-+           case filter repoFilter $ PD.sourceRepos pd of+             [] -> throwError "No head github source-repository given in cabal file!"+             (repo:_) ->+                 case PD.repoLocation repo of+                   Nothing ->+                       throwError "Missing source-repository location"+                   Just loc -> extractGithub loc+       hasStack <- liftM (not . L.null) $ liftIO $ G.glob "stack*.yaml"+       (example, hasMoreEx) <-+           do files <- liftIO $ G.glob "examples/*.hs"+              forM_ files $ \file ->+                  do res <-+                         liftIO $+                         if hasStack+                         then system $ "stack exec -- ghc -fno-code " ++ file+                         else system $ "cabal exec -- ghc -fno-code " ++ file+                     when (res /= ExitSuccess) $ throwError $ "Failed to compile " ++ file+              case files of+                [] -> return (Nothing, False)+                (file:xs) ->+                    do ex <- liftIO $ readFile file+                       return (Just $ T.pack ex, not $ L.null xs)+       onStackage <- liftIO $ onStackageCheck pkgName+       onHackage <- liftIO $ onHackageCheck pkgName+       ghcVers <- findGhcVersions (PD.testedWith pd)+       cliUsage <- getCliUsage hasStack (map fst $ PD.condExecutables gpd)+       moreFile <- moreFile+       return+           ProjectInfo+           { pi_name = pkgName+           , pi_pkgName = pkgName+           , pi_pkgDesc = T.pack $ PD.synopsis pd+           , pi_stackFile = hasStack+           , pi_onStackage = onStackage+           , pi_onHackage = onHackage+           , pi_example = example+           , pi_moreExamples = hasMoreEx+           , pi_github = ghInfo+           , pi_license =+               LicenseInfo+               { li_copyright = T.pack $ PD.copyright pd+               , li_type = T.pack $ DT.display $ PD.license pd+               }+           , pi_ghcVersions = ghcVers+           , pi_cliUsage = cliUsage+           , pi_moreInfo = moreFile+           }+    where+      pd = PD.packageDescription gpd+      repoFilter rep =+          PD.repoKind rep == PD.RepoHead+          && PD.repoType rep == Just PD.Git+          && checkGithub (PD.repoLocation rep)+      checkGithub r =+          case r of+            Nothing -> False+            Just str -> "github.com" `L.isInfixOf` str+      extractGithub loc =+          case L.stripPrefix "https://github.com/" loc of+            Nothing ->+                throwError "source-repository location must start with https://github.com/"+            Just rest ->+                let (usr, slashedRepo) = L.break (=='/') rest+                in return+                   GithubInfo+                   { gi_user = T.pack usr+                   , gi_project = T.pack $ L.drop 1 slashedRepo+                   }
+ src/Hack/Manager/Gitignore.hs view
@@ -0,0 +1,30 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE OverloadedStrings #-}+module Hack.Manager.Gitignore+    ( renderGitignore )+where++import Hack.Manager.Types++import Control.Monad+import Data.FileEmbed+import qualified Data.ByteString as BS+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import qualified Data.Text.Lazy as TL+import qualified Text.Hastache as H+import qualified Text.Hastache.Context as H++giTemplate :: BS.ByteString+giTemplate = $(embedFile "templates/gitignore.mustache")++renderGitignore :: ProjectInfo -> IO T.Text+renderGitignore pinfo =+   liftM TL.toStrict $ H.hastacheStr cfg (T.decodeUtf8 giTemplate) ctx+    where+      ctx = H.mkGenericContext pinfo+      cfg =+          H.defaultConfig+          { H.muTemplateFileDir = Nothing+          , H.muEscapeFunc = H.emptyEscape+          }
+ src/Hack/Manager/Readme.hs view
@@ -0,0 +1,30 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE OverloadedStrings #-}+module Hack.Manager.Readme+    ( renderReadme )+where++import Hack.Manager.Types++import Control.Monad+import Data.FileEmbed+import qualified Data.ByteString as BS+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import qualified Data.Text.Lazy as TL+import qualified Text.Hastache as H+import qualified Text.Hastache.Context as H++readmeTemplate :: BS.ByteString+readmeTemplate = $(embedFile "templates/README.mustache")++renderReadme :: ProjectInfo -> IO T.Text+renderReadme pinfo =+   liftM TL.toStrict $ H.hastacheStr cfg (T.decodeUtf8 readmeTemplate) ctx+    where+      ctx = H.mkGenericContext pinfo+      cfg =+          H.defaultConfig+          { H.muTemplateFileDir = Nothing+          , H.muEscapeFunc = H.emptyEscape+          }
+ src/Hack/Manager/Travis.hs view
@@ -0,0 +1,56 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DeriveDataTypeable #-}+module Hack.Manager.Travis+    ( renderTravis+    , TravisOpts (..)+    )+where++import Hack.Manager.Types++import Control.Monad+import Data.Data+import Data.FileEmbed+import qualified Data.ByteString as BS+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import qualified Data.Text.Lazy as TL+import qualified Text.Hastache as H+import qualified Text.Hastache.Context as H++data TravisOpts+   = TravisOpts+   { to_ghcRelease :: T.Text+   , to_useStack :: Bool+   } deriving (Show, Eq, Data, Typeable)++data TravisCtx+   = TravisCtx+   { tc_project :: ProjectInfo+   , tc_opts :: TravisOpts+   } deriving (Show, Eq, Data, Typeable)++stackTemplate :: BS.ByteString+stackTemplate = $(embedFile "templates/travis-stack.mustache")++cabalTemplate :: BS.ByteString+cabalTemplate = $(embedFile "templates/travis-cabal.mustache")++renderTravis :: TravisOpts -> ProjectInfo -> IO T.Text+renderTravis to pinfo =+   liftM TL.toStrict $ H.hastacheStr cfg (T.decodeUtf8 tpl) ctx+    where+      tpl =+          if to_useStack to then stackTemplate else cabalTemplate+      ctx =+          H.mkGenericContext $+          TravisCtx+          { tc_project = pinfo+          , tc_opts = to+          }+      cfg =+          H.defaultConfig+          { H.muTemplateFileDir = Nothing+          , H.muEscapeFunc = H.emptyEscape+          }
+ src/Hack/Manager/Types.hs view
@@ -0,0 +1,41 @@+{-# LANGUAGE DeriveDataTypeable #-}+module Hack.Manager.Types where++import Data.Data+import Data.Typeable+import qualified Data.Text as T++data GithubInfo+   = GithubInfo+   { gi_user :: T.Text+   , gi_project :: T.Text+   } deriving (Show, Eq, Data, Typeable)++data LicenseInfo+   = LicenseInfo+   { li_type :: T.Text+   , li_copyright :: T.Text+   } deriving (Show, Eq, Data, Typeable)++data CliExecutable+   = CliExecutable+   { ce_name :: T.Text+   , ce_help :: T.Text+   } deriving (Show, Eq, Data, Typeable)++data ProjectInfo+   = ProjectInfo+   { pi_name :: T.Text+   , pi_pkgName :: T.Text+   , pi_pkgDesc :: T.Text+   , pi_stackFile :: Bool+   , pi_onStackage :: Bool+   , pi_onHackage :: Bool+   , pi_example :: Maybe T.Text+   , pi_moreExamples :: Bool+   , pi_cliUsage :: [CliExecutable]+   , pi_github :: GithubInfo+   , pi_license :: LicenseInfo+   , pi_ghcVersions :: [T.Text]+   , pi_moreInfo :: Maybe T.Text+   } deriving (Show, Eq, Data, Typeable)
+ templates/README.mustache view
@@ -0,0 +1,67 @@+{{pi_name}}+=====++[![Build Status](https://travis-ci.org/{{pi_github.gi_user}}/{{pi_github.gi_project}}.svg)](https://travis-ci.org/{{pi_github.gi_user}}/{{pi_github.gi_project}})+{{#pi_onHackage}}[![Hackage](https://img.shields.io/hackage/v/{{pi_pkgName}}.svg)](http://hackage.haskell.org/package/{{pi_pkgName}}){{/pi_onHackage}}++## Intro++{{#pi_onHackage}}+Hackage: [{{pi_pkgName}}](http://hackage.haskell.org/package/{{pi_pkgName}})+{{/pi_onHackage}}+{{#pi_onStackage}}+Stackage: [{{pi_pkgName}}](https://www.stackage.org/package/{{pi_pkgName}})+{{/pi_onStackage}}++{{pi_pkgDesc}}++{{#pi_cliUsage}}+## Cli Usage: {{ce_name}}++```sh+$ {{ce_name}} --help+{{ce_help}}+```+{{/pi_cliUsage}}++{{#pi_example}}+## Library Usage Example++```haskell+{{.}}+```+{{#pi_moreExamples}}++For more examples check the examples/ directory.+{{/pi_moreExamples}}++{{/pi_example}}+## Install++{{#pi_onHackage}}+* Using cabal: `cabal install {{pi_pkgName}}`+{{/pi_onHackage}}+{{#pi_onStackage}}+* Using Stack: `stack install {{pi_pkgName}}`+{{/pi_onStackage}}+* From Source (cabal): `git clone https://github.com/{{pi_github.gi_user}}/{{pi_github.gi_project}}.git && cd {{pi_github.gi_project}} && cabal install`+{{#pi_stackFile}}+* From Source (stack): `git clone https://github.com/{{pi_github.gi_user}}/{{pi_github.gi_project}}.git && cd {{pi_github.gi_project}} && stack build`+{{/pi_stackFile}}++{{#pi_moreInfo}}+{{.}}+{{/pi_moreInfo}}++## Misc++### Supported GHC Versions++{{#pi_ghcVersions}}+* {{.}}+{{/pi_ghcVersions}}++### License++Released under the {{pi_license.li_type}} license.+{{pi_license.li_copyright}}
+ templates/gitignore.mustache view
@@ -0,0 +1,22 @@+{{^pi_stackFile}}+dist+cabal-dev+{{/pi_stackFile}}+*.o+*.hi+*.chi+*.chs.h+*.dyn_o+*.dyn_hi+.hpc+.hsenv+{{^pi_stackFile}}+.cabal-sandbox/+cabal.sandbox.config+{{/pi_stackFile}}+*.prof+*.aux+*.hp+{{#pi_stackFile}}+.stack-work/+{{/pi_stackFile}}
+ templates/travis-cabal.mustache view
@@ -0,0 +1,40 @@+language: haskell+env:+{{#tc_project.pi_ghcVersions}}+- GHCVER={{.}}+{{/tc_project.pi_ghcVersions}}+- GHCVER=head+matrix:+  allow_failures:+  - env: GHCVER=head+before_install:+- |+  if [ $GHCVER = `ghc --numeric-version` ]; then+    travis/cabal-apt-install --enable-tests $MODE+    export CABAL=cabal+  else+    travis_retry sudo add-apt-repository -y ppa:hvr/ghc+    travis_retry sudo apt-get update+    travis_retry sudo apt-get install cabal-install-1.22 ghc-$GHCVER happy+    export CABAL=cabal-1.22+    export PATH=/opt/ghc/$GHCVER/bin:$PATH+  fi+- $CABAL update+- |+  $CABAL install happy alex+  export PATH=$HOME/.cabal/bin:$PATH+install:+- $CABAL install --dependencies-only --enable-tests+- $CABAL configure -flib-Werror --enable-tests $MODE+script:+- ghc --numeric-version+- $CABAL check+- $CABAL build+- $CABAL test --show-details=always+deploy:+  provider: hackage+  username: AlexanderThiemann+  skip_cleanup: true+  on:+    condition: "$GHCVER = {{tc_opts.to_ghcRelease}}"+    tags: true
+ templates/travis-stack.mustache view
@@ -0,0 +1,46 @@+# NB: don't set `language: haskell` here++sudo: false++matrix:+  include:+  {{#tc_project.pi_ghcVersions}}+  - env: GHCVER={{.}} STACK_YAML=stack.yaml+    addons:+      apt:+        sources:+        - hvr-ghc+        packages:+        - ghc-{{.}}+  {{/tc_project.pi_ghcVersions}}++before_install:+  # ghc+  - export PATH=/opt/ghc/$GHCVER/bin:$PATH+  # stack+  - mkdir -p ~/.local/bin+  - export PATH=~/.local/bin:$PATH+  - travis_retry curl -L https://github.com/commercialhaskell/stack/releases/download/v0.1.2.0/stack-0.1.2.0-x86_64-linux.gz | gunzip > ~/.local/bin/stack+  - chmod a+x ~/.local/bin/stack+  # versions+  - stack +RTS -N1 -RTS --version+  - echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]"++install:+  - ./travis_long stack +RTS -N1 -RTS --no-terminal --skip-ghc-check setup+  - ./travis_long stack +RTS -N1 -RTS --no-terminal --skip-ghc-check test --only-snapshot++script:+  - stack +RTS -N2 -RTS --no-terminal --skip-ghc-check test++cache:+  directories:+  - $HOME/.stack++deploy:+  provider: hackage+  username: AlexanderThiemann+  skip_cleanup: true+  on:+    condition: "$GHCVER = {{tc_opts.to_ghcRelease}}"+    tags: true