diff --git a/ChangeLog.md b/ChangeLog.md index 9027ef109f..9b20ed3a30 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -101,6 +101,12 @@ Behavior changes: addition, the `packagename> ` prefix is no longer included in interelaved mode when only building a single target. +* Stack will check occassionally if there is a new version available and prompt + the user to upgrade. This will not incur any additional network traffic, as + it will piggy-back on the existing Hackage index updates. You can set + `recommend-stack-upgrade: false` to bypass this. See + [#1681](https://github.com/commercialhaskell/stack/issues/1681). + Other enhancements: * Defer loading up of files for local packages. This allows us to get diff --git a/doc/yaml_configuration.md b/doc/yaml_configuration.md index cabb5826fb..cbb4219d47 100644 --- a/doc/yaml_configuration.md +++ b/doc/yaml_configuration.md @@ -1045,3 +1045,13 @@ a yaml configuration file. (The British English spelling (colour) is also accepted. In yaml configuration files, the American spelling is the alternative that has priority.) + +### recommend-stack-upgrade + +When Stack notices that a new version of Stack is available, should it notify the user? + +```yaml +recommend-stack-upgrade: true +``` + +Since 2.0 diff --git a/src/Stack/Config.hs b/src/Stack/Config.hs index b80d2a720c..740213b322 100644 --- a/src/Stack/Config.hs +++ b/src/Stack/Config.hs @@ -309,6 +309,7 @@ configFromConfigMonoid configDumpLogs = fromFirst DumpWarningLogs configMonoidDumpLogs configSaveHackageCreds = fromFirst True configMonoidSaveHackageCreds configHackageBaseUrl = fromFirst "https://hackage.haskell.org/" configMonoidHackageBaseUrl + configRecommendUpgrade = fromFirstTrue configMonoidRecommendUpgrade configAllowDifferentUser <- case getFirst configMonoidAllowDifferentUser of diff --git a/src/Stack/Runners.hs b/src/Stack/Runners.hs index bb16383948..d0f08c50cd 100644 --- a/src/Stack/Runners.hs +++ b/src/Stack/Runners.hs @@ -20,7 +20,10 @@ module Stack.Runners ) where import Stack.Prelude +import Distribution.Version (mkVersion') +import qualified Paths_stack import RIO.Process (mkDefaultProcessContext) +import RIO.Time (addUTCTime, getCurrentTime) import Stack.Build.Target(NeedTargets(..)) import Stack.Config import Stack.Constants @@ -28,6 +31,7 @@ import Stack.DefaultColorWhen (defaultColorWhen) import qualified Stack.Docker as Docker import qualified Stack.Nix as Nix import Stack.Setup +import Stack.Storage (upgradeChecksSince, logUpgradeCheck) import Stack.Types.Config import Stack.Types.Docker (dockerEnable) import Stack.Types.Nix (nixEnable) @@ -94,7 +98,11 @@ withConfig shouldReexec inner = -- happen ASAP but needs a configuration. view (globalOptsL.to globalDockerEntrypoint) >>= traverse_ (Docker.entrypoint config) - runRIO config $ + runRIO config $ do + -- Catching all exceptions here, since we don't want this + -- check to ever cause Stack to stop working + shouldUpgradeCheck `catchAny` \e -> + logError ("Error when running shouldUpgradeCheck: " <> displayShow e) case shouldReexec of YesReexec -> reexec inner NoReexec -> inner @@ -169,3 +177,32 @@ withRunnerGlobal go inner = do | w < minTerminalWidth = minTerminalWidth | w > maxTerminalWidth = maxTerminalWidth | otherwise = w + +-- | Check if we should recommend upgrading Stack and, if so, recommend it. +shouldUpgradeCheck :: RIO Config () +shouldUpgradeCheck = do + config <- ask + when (configRecommendUpgrade config) $ do + now <- getCurrentTime + let yesterday = addUTCTime (-24 * 60 * 60) now + checks <- upgradeChecksSince yesterday + when (checks == 0) $ do + mversion <- getLatestHackageVersion "stack" UsePreferredVersions -- FIXME ensure it doesn't force an update ever + case mversion of + Just (PackageIdentifierRevision _ version _) | version > mkVersion' Paths_stack.version -> do + logWarn "<<<<<<<<<<<<<<<<<<" + logWarn $ + "You are currently using Stack version " <> + fromString (versionString (mkVersion' Paths_stack.version)) <> + ", but version " <> + fromString (versionString version) <> + " is available" + logWarn "You can try to upgrade by running 'stack upgrade'" + logWarn $ + "Tired of seeing this? Add 'recommend-stack-upgrade: false' to " <> + fromString (toFilePath (configUserConfigPath config)) + logWarn ">>>>>>>>>>>>>>>>>>" + logWarn "" + logWarn "" + _ -> pure () + logUpgradeCheck now diff --git a/src/Stack/Storage.hs b/src/Stack/Storage.hs index 04f9ce6985..89eda48839 100644 --- a/src/Stack/Storage.hs +++ b/src/Stack/Storage.hs @@ -26,6 +26,8 @@ module Stack.Storage , savePrecompiledCache , loadDockerImageExeCache , saveDockerImageExeCache + , upgradeChecksSince + , logUpgradeCheck ) where import qualified Data.ByteString as S @@ -114,6 +116,10 @@ DockerImageExeCache compatible Bool DockerImageExeCacheUnique imageHash exePath exeTimestamp deriving Show + +-- History of checks for whether we should upgrade Stack +UpgradeCheck + timestamp UTCTime |] -- | Initialize the database. @@ -412,3 +418,11 @@ updateList recordCons parentFieldCons parentId indexFieldCons old new = insertMany_ $ map (uncurry $ recordCons parentId) $ Set.toList (Set.difference newSet oldSet) + +-- | How many upgrade checks have occurred since the given timestamp? +upgradeChecksSince :: HasConfig env => UTCTime -> RIO env Int +upgradeChecksSince since = withStorage $ count [UpgradeCheckTimestamp >=. since] + +-- | Log in the database that an upgrade check occurred at the given time. +logUpgradeCheck :: HasConfig env => UTCTime -> RIO env () +logUpgradeCheck = withStorage . insert_ . UpgradeCheck diff --git a/src/Stack/Types/Config.hs b/src/Stack/Types/Config.hs index 2397cbd241..685898e7db 100644 --- a/src/Stack/Types/Config.hs +++ b/src/Stack/Types/Config.hs @@ -360,6 +360,8 @@ data Config = -- ^ Any resolver override from the command line ,configStorage :: !Storage -- ^ Database connection pool for Stack database + ,configRecommendUpgrade :: !Bool + -- ^ Recommend a Stack upgrade? } -- | The project root directory, if in a project. @@ -764,6 +766,8 @@ data ConfigMonoid = , configMonoidColorWhen :: !(First ColorWhen) -- ^ When to use 'ANSI' colors , configMonoidStyles :: !StylesUpdate + , configMonoidRecommendUpgrade :: !FirstTrue + -- ^ See 'configRecommendUpgrade' } deriving (Show, Generic) @@ -879,6 +883,8 @@ parseConfigMonoidObject rootDir obj = do let configMonoidStyles = fromMaybe mempty $ configMonoidStylesUS <|> configMonoidStylesGB + configMonoidRecommendUpgrade <- FirstTrue <$> obj ..:? configMonoidRecommendUpgradeName + return ConfigMonoid {..} where handleExplicitSetupDep :: Monad m => (Text, Bool) -> m (Maybe PackageName, Bool) @@ -1029,6 +1035,9 @@ configMonoidStylesUSName = "stack-colors" configMonoidStylesGBName :: Text configMonoidStylesGBName = "stack-colours" +configMonoidRecommendUpgradeName :: Text +configMonoidRecommendUpgradeName = "recommend-stack-upgrade" + data ConfigException = ParseConfigFileException (Path Abs File) ParseException | ParseCustomSnapshotException Text ParseException