Skip to content

Commit

Permalink
Implement an upgrade command #237
Browse files Browse the repository at this point in the history
Also implemented #378 along the way
  • Loading branch information
snoyberg committed Jun 26, 2015
1 parent 711f2e9 commit 4fb03ff
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 12 deletions.
25 changes: 17 additions & 8 deletions src/Stack/Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ configFromConfigMonoid
-> Maybe Project
-> ConfigMonoid
-> m Config
configFromConfigMonoid configStackRoot mproject ConfigMonoid{..} = do
configFromConfigMonoid configStackRoot mproject configMonoid@ConfigMonoid{..} = do
let configDocker = Docker.dockerOptsFromMonoid mproject configStackRoot configMonoidDockerOpts
configConnectionCount = fromMaybe 8 configMonoidConnectionCount
configHideTHLoading = fromMaybe True configMonoidHideTHLoading
Expand Down Expand Up @@ -130,6 +130,8 @@ configFromConfigMonoid configStackRoot mproject ConfigMonoid{..} = do

configRequireStackVersion = simplifyVersionRange configMonoidRequireStackVersion

configConfigMonoid = configMonoid

origEnv <- getEnvOverride configPlatform
let configEnvOverride _ = return origEnv

Expand Down Expand Up @@ -229,11 +231,13 @@ instance HasPlatform MiniConfig
loadConfig :: (MonadLogger m,MonadIO m,MonadCatch m,MonadThrow m,MonadBaseControl IO m,MonadReader env m,HasHttpManager env,HasTerminal env)
=> ConfigMonoid
-- ^ Config monoid from parsed command-line arguments
-> Maybe (Path Abs File)
-- ^ Override stack.yaml
-> m (LoadConfig m)
loadConfig configArgs = do
loadConfig configArgs mstackYaml = do
stackRoot <- determineStackRoot
extraConfigs <- getExtraConfigs stackRoot >>= mapM loadYaml
mproject <- loadProjectConfig
mproject <- loadProjectConfig mstackYaml
config <- configFromConfigMonoid stackRoot (fmap (\(proj, _, _) -> proj) mproject) $ mconcat $
case mproject of
Nothing -> configArgs : extraConfigs
Expand Down Expand Up @@ -455,8 +459,11 @@ loadYaml path =

-- | Get the location of the project config file, if it exists.
getProjectConfig :: (MonadIO m, MonadThrow m, MonadLogger m)
=> m (Maybe (Path Abs File))
getProjectConfig = do
=> Maybe (Path Abs File)
-- ^ Override stack.yaml
-> m (Maybe (Path Abs File))
getProjectConfig (Just stackYaml) = return $ Just stackYaml
getProjectConfig Nothing = do
env <- liftIO getEnvironment
case lookup "STACK_YAML" env of
Just fp -> do
Expand Down Expand Up @@ -488,9 +495,11 @@ getProjectConfig = do
-- and otherwise traversing parents. If no config is found, we supply a default
-- based on current directory.
loadProjectConfig :: (MonadIO m, MonadThrow m, MonadLogger m)
=> m (Maybe (Project, Path Abs File, ConfigMonoid))
loadProjectConfig = do
mfp <- getProjectConfig
=> Maybe (Path Abs File)
-- ^ Override stack.yaml
-> m (Maybe (Project, Path Abs File, ConfigMonoid))
loadProjectConfig mstackYaml = do
mfp <- getProjectConfig mstackYaml
case mfp of
Just fp -> do
currDir <- getWorkingDir
Expand Down
2 changes: 2 additions & 0 deletions src/Stack/Types/Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ data Config =
-- ^ --extra-include-dirs arguments
,configExtraLibDirs :: !(Set Text)
-- ^ --extra-lib-dirs arguments
,configConfigMonoid :: !ConfigMonoid
-- ^ @ConfigMonoid@ used to generate this
}

-- | Information on a single package index
Expand Down
92 changes: 92 additions & 0 deletions src/Stack/Upgrade.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
module Stack.Upgrade (upgrade) where

import Control.Monad.Catch
import Control.Monad.IO.Class
import Control.Monad.Logger
import Control.Monad.Reader
import Control.Monad.Trans.Control
import qualified Data.Map as Map
import qualified Data.Set as Set
import Network.HTTP.Client.Conduit (HasHttpManager, getHttpManager)
import Path
import Stack.Build
import Stack.Build.Types
import Stack.Config
import Stack.Fetch
import Stack.PackageIndex
import Stack.Setup
import Stack.Types
import Stack.Types.Internal
import Stack.Types.StackT
import System.IO.Temp (withSystemTempDirectory)
import System.Process.Run

upgrade :: (MonadIO m, MonadMask m, MonadReader env m, HasConfig env, HasHttpManager env, MonadLogger m, HasTerminal env, HasLogLevel env, MonadBaseControl IO m)
=> Bool -- ^ use Git?
-> Maybe Resolver
-> m ()
upgrade fromGit mresolver = withSystemTempDirectory "stack-upgrade" $ \tmp' -> do
menv <- getMinimalEnvOverride
tmp <- parseAbsDir tmp'
dir <-
if fromGit
then do
$logInfo "Cloning stack"
runIn tmp "git" menv
[ "clone"
, "git@github.com:commercialhaskell/stack" -- TODO allow to be configured
, "stack"
, "--depth"
, "1"
]
Nothing
return $ tmp </> $(mkRelDir "stack")
-- Stack.PackageIndex.updateAllIndices menv
else do
-- updateAllIndices menv
caches <- getPackageCaches menv
let latest = Map.fromListWith max
$ map toTuple
$ Map.keys caches
case Map.lookup $(mkPackageName "stack") latest of
Nothing -> error "No stack found in package indices"
Just version -> do
let ident = PackageIdentifier $(mkPackageName "stack") version
paths <- unpackPackageIdents menv tmp Nothing $ Set.singleton ident
case Map.lookup ident paths of
Nothing -> error "Stack.Upgrade.upgrade: invariant violated, unpacked directory not found"
Just path -> return path

manager <- asks getHttpManager
logLevel <- asks getLogLevel
terminal <- asks getTerminal
configMonoid <- asks $ configConfigMonoid . getConfig

liftIO $ do
bconfig <- runStackLoggingT manager logLevel terminal $ do
lc <- loadConfig
configMonoid
(Just $ dir </> $(mkRelFile "stack.yaml"))
lcLoadBuildConfig lc mresolver ThrowException
envConfig1 <- runStackT manager logLevel bconfig terminal setupEnv
runStackT manager logLevel envConfig1 terminal $ build BuildOpts
{ boptsTargets = ["stack"]
, boptsLibProfile = False
, boptsExeProfile = False
, boptsEnableOptimizations = Nothing
, boptsHaddock = False
, boptsHaddockDeps = Nothing
, boptsFinalAction = DoNothing
, boptsDryrun = False
, boptsGhcOptions = []
, boptsFlags = Map.empty
, boptsInstallExes = True
, boptsPreFetch = False
, boptsTestArgs = []
, boptsOnlySnapshot = False
, boptsCoverage = False
}
32 changes: 30 additions & 2 deletions src/main/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ import Stack.Solver (solveExtraDeps)
import Stack.Types
import Stack.Types.Internal
import Stack.Types.StackT
import Stack.Upgrade
import qualified Stack.Upload as Upload
import System.Directory (canonicalizePath)
import System.Environment (getArgs, getProgName)
import System.Exit
import System.FilePath (searchPathSeparator)
Expand Down Expand Up @@ -137,6 +139,13 @@ main =
"Update the package index"
updateCmd
(pure ())
addCommand "upgrade"
"Upgrade to the latest stack (experimental)"
upgradeCmd
(switch
( long "git"
<> help "Clone from Git instead of downloading from Hackage (more dangerous)"
))
addCommand "upload"
"Upload a package to Hackage"
uploadCmd
Expand Down Expand Up @@ -499,6 +508,14 @@ updateCmd () go@GlobalOpts{..} = do
runStackT manager globalLogLevel (lcConfig lc) globalTerminal $
getMinimalEnvOverride >>= Stack.PackageIndex.updateAllIndices

upgradeCmd :: Bool -> GlobalOpts -> IO ()
upgradeCmd fromGit go@GlobalOpts{..} = do
(manager,lc) <- loadConfigWithOpts go
runStackT manager globalLogLevel (lcConfig lc) globalTerminal $
Docker.rerunWithOptionalContainer (lcProjectRoot lc) $
runStackT manager globalLogLevel (lcConfig lc) globalTerminal $
upgrade fromGit globalResolver

-- | Upload to Hackage
uploadCmd :: [String] -> GlobalOpts -> IO ()
uploadCmd args0 go = do
Expand Down Expand Up @@ -702,7 +719,11 @@ globalOpts defaultTerminal =
False
(long "no-terminal" <>
help
"Override terminal detection in the case of running in a false terminal")
"Override terminal detection in the case of running in a false terminal") <*>
(optional (strOption
(long "stack-yaml" <>
metavar "STACK-YAML" <>
help "Override project stack.yaml file (overrides any STACK_YAML environment variable)")))

-- | Parse for a logging level.
logLevelOpt :: Parser LogLevel
Expand Down Expand Up @@ -748,18 +769,25 @@ data GlobalOpts = GlobalOpts
, globalConfigMonoid :: ConfigMonoid -- ^ Config monoid, for passing into 'loadConfig'
, globalResolver :: Maybe Resolver -- ^ Resolver override
, globalTerminal :: Bool -- ^ We're in a terminal?
, globalStackYaml :: Maybe FilePath -- ^ Override project stack.yaml
} deriving (Show)

-- | Load the configuration with a manager. Convenience function used
-- throughout this module.
loadConfigWithOpts :: GlobalOpts -> IO (Manager,LoadConfig (StackLoggingT IO))
loadConfigWithOpts GlobalOpts{..} = do
manager <- newTLSManager
mstackYaml <-
case globalStackYaml of
Nothing -> return Nothing
Just fp -> do
path <- canonicalizePath fp >>= parseAbsFile
return $ Just path
lc <- runStackLoggingT
manager
globalLogLevel
globalTerminal
(loadConfig globalConfigMonoid)
(loadConfig globalConfigMonoid mstackYaml)
return (manager,lc)

-- | Project initialization
Expand Down
2 changes: 1 addition & 1 deletion src/test/Stack/BuildPlanSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ main = hspec spec
spec :: Spec
spec = beforeAll setup $ afterAll teardown $ do
let logLevel = LevelDebug
let loadConfig' m = runStackLoggingT m logLevel False (loadConfig mempty)
let loadConfig' m = runStackLoggingT m logLevel False (loadConfig mempty Nothing)
let loadBuildConfigRest m = runStackLoggingT m logLevel False
let inTempDir action = do
currentDirectory <- getCurrentDirectory
Expand Down
2 changes: 1 addition & 1 deletion src/test/Stack/ConfigSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ spec = beforeAll setup $ afterAll teardown $ do


describe "loadConfig" $ do
let loadConfig' m = runStackLoggingT m logLevel False (loadConfig mempty)
let loadConfig' m = runStackLoggingT m logLevel False (loadConfig mempty Nothing)
let loadBuildConfigRest m = runStackLoggingT m logLevel False
-- TODO(danburton): make sure parent dirs also don't have config file
it "works even if no config file exists" $ \T{..} -> example $ do
Expand Down
1 change: 1 addition & 0 deletions stack.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ library
Stack.Build.Installed
Stack.Build.Source
Stack.Build.Types
Stack.Upgrade
Stack.Upload
System.Process.Read
System.Process.Log
Expand Down

0 comments on commit 4fb03ff

Please sign in to comment.