forked from commercialhaskell/stack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathUpgrade.hs
More file actions
253 lines (237 loc) · 10.2 KB
/
Upgrade.hs
File metadata and controls
253 lines (237 loc) · 10.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TemplateHaskell #-}
module Stack.Upgrade
( upgrade
, UpgradeOpts
, upgradeOpts
) where
import Stack.Prelude hiding (force)
import qualified Data.HashMap.Strict as HashMap
import qualified Data.List
import qualified Data.Map as Map
import qualified Data.Text as T
import Distribution.Version (mkVersion')
import Lens.Micro (set)
import Options.Applicative
import Path
import qualified Paths_stack as Paths
import Stack.Build
import Stack.Config
import Stack.Fetch
import Stack.PackageIndex
import Stack.PrettyPrint
import Stack.Setup
import Stack.Types.PackageIdentifier
import Stack.Types.PackageIndex
import Stack.Types.PackageName
import Stack.Types.Version
import Stack.Types.Config
import Stack.Types.Resolver
import System.Exit (ExitCode (ExitSuccess))
import System.Process (rawSystem, readProcess)
import System.Process.Run
upgradeOpts :: Parser UpgradeOpts
upgradeOpts = UpgradeOpts
<$> (sourceOnly <|> optional binaryOpts)
<*> (binaryOnly <|> optional sourceOpts)
where
binaryOnly = flag' Nothing (long "binary-only" <> help "Do not use a source upgrade path")
sourceOnly = flag' Nothing (long "source-only" <> help "Do not use a binary upgrade path")
binaryOpts = BinaryOpts
<$> optional (strOption
( long "binary-platform"
<> help "Platform type for archive to download"
<> showDefault))
<*> switch
(long "force-download" <>
help "Download the latest available stack executable")
<*> optional (strOption
(long "binary-version" <>
help "Download a specific stack version"))
<*> optional (strOption
(long "github-org" <>
help "Github organization name"))
<*> optional (strOption
(long "github-repo" <>
help "Github repository name"))
sourceOpts = SourceOpts
<$> ((\fromGit repo branch -> if fromGit then Just (repo, branch) else Nothing)
<$> switch
( long "git"
<> help "Clone from Git instead of downloading from Hackage (more dangerous)" )
<*> strOption
( long "git-repo"
<> help "Clone from specified git repository"
<> value "https://github.com/commercialhaskell/stack"
<> showDefault )
<*> strOption
( long "git-branch"
<> help "Clone from this git branch"
<> value "master"
<> showDefault ))
data BinaryOpts = BinaryOpts
{ _boPlatform :: !(Maybe String)
, _boForce :: !Bool
-- ^ force a download, even if the downloaded version is older
-- than what we are
, _boVersion :: !(Maybe String)
-- ^ specific version to download
, _boGithubOrg :: !(Maybe String)
, _boGithubRepo :: !(Maybe String)
}
deriving Show
newtype SourceOpts = SourceOpts (Maybe (String, String)) -- repo and branch
deriving Show
data UpgradeOpts = UpgradeOpts
{ _uoBinary :: !(Maybe BinaryOpts)
, _uoSource :: !(Maybe SourceOpts)
}
deriving Show
upgrade :: HasConfig env
=> ConfigMonoid
-> Maybe AbstractResolver
-> Maybe String -- ^ git hash at time of building, if known
-> UpgradeOpts
-> RIO env ()
upgrade gConfigMonoid mresolver builtHash (UpgradeOpts mbo mso) =
case (mbo, mso) of
-- FIXME It would be far nicer to capture this case in the
-- options parser itself so we get better error messages, but
-- I can't think of a way to make it happen.
(Nothing, Nothing) -> throwString "You must allow either binary or source upgrade paths"
(Just bo, Nothing) -> binary bo
(Nothing, Just so) -> source so
-- See #2977 - if --git or --git-repo is specified, do source upgrade.
(_, Just so@(SourceOpts (Just _))) -> source so
(Just bo, Just so) -> binary bo `catchAny` \e -> do
prettyWarnL
[ flow "Exception occured when trying to perform binary upgrade:"
, fromString . show $ e
, line <> flow "Falling back to source upgrade"
]
source so
where
binary bo = binaryUpgrade bo
source so = sourceUpgrade gConfigMonoid mresolver builtHash so
binaryUpgrade :: HasConfig env => BinaryOpts -> RIO env ()
binaryUpgrade (BinaryOpts mplatform force' mver morg mrepo) = do
platforms0 <-
case mplatform of
Nothing -> preferredPlatforms
Just p -> return [("windows" `T.isInfixOf` T.pack p, p)]
archiveInfo <- downloadStackReleaseInfo morg mrepo mver
let mdownloadVersion = getDownloadVersion archiveInfo
force =
case mver of
Nothing -> force'
Just _ -> True -- specifying a version implies we're forcing things
isNewer <-
case mdownloadVersion of
Nothing -> do
prettyErrorL $
flow "Unable to determine upstream version from Github metadata"
:
[ line <> flow "Rerun with --force-download to force an upgrade"
| not force]
return False
Just downloadVersion -> do
prettyInfoL
[ flow "Current Stack version:"
, display stackVersion <> ","
, flow "available download version:"
, display downloadVersion
]
return $ downloadVersion > stackVersion
toUpgrade <- case (force, isNewer) of
(False, False) -> do
prettyInfoS "Skipping binary upgrade, you are already running the most recent version"
return False
(True, False) -> do
prettyInfoS "Forcing binary upgrade"
return True
(_, True) -> do
prettyInfoS "Newer version detected, downloading"
return True
when toUpgrade $ do
config <- view configL
downloadStackExe platforms0 archiveInfo (configLocalBin config) True $ \tmpFile -> do
-- Sanity check!
ec <- rawSystem (toFilePath tmpFile) ["--version"]
unless (ec == ExitSuccess)
$ throwString "Non-success exit code from running newly downloaded executable"
sourceUpgrade
:: HasConfig env
=> ConfigMonoid
-> Maybe AbstractResolver
-> Maybe String
-> SourceOpts
-> RIO env ()
sourceUpgrade gConfigMonoid mresolver builtHash (SourceOpts gitRepo) =
withSystemTempDir "stack-upgrade" $ \tmp -> do
menv <- getMinimalEnvOverride
mdir <- case gitRepo of
Just (repo, branch) -> do
remote <- liftIO $ readProcess "git" ["ls-remote", repo, branch] []
latestCommit <-
case words remote of
[] -> throwString $ "No commits found for branch " ++ branch ++ " on repo " ++ repo
x:_ -> return x
when (isNothing builtHash) $
prettyWarnS $
"Information about the commit this version of stack was "
<> "built from is not available due to how it was built. "
<> "Will continue by assuming an upgrade is needed "
<> "because we have no information to the contrary."
if builtHash == Just latestCommit
then do
prettyInfoS "Already up-to-date, no upgrade required"
return Nothing
else do
prettyInfoS "Cloning stack"
-- NOTE: "--recursive" was added after v1.0.0 (and before the
-- next release). This means that we can't use submodules in
-- the stack repo until we're comfortable with "stack upgrade
-- --git" not working for earlier versions.
let args = [ "clone", repo , "stack", "--depth", "1", "--recursive", "--branch", branch]
runCmd (Cmd (Just tmp) "git" menv args) Nothing
return $ Just $ tmp </> $(mkRelDir "stack")
Nothing -> do
updateAllIndices
PackageCache caches <- getPackageCaches
let versions
= filter (/= $(mkVersion "9.9.9")) -- Mistaken upload to Hackage, just ignore it
$ maybe [] HashMap.keys
$ HashMap.lookup $(mkPackageName "stack") caches
when (null versions) (throwString "No stack found in package indices")
let version = Data.List.maximum versions
if version <= fromCabalVersion (mkVersion' Paths.version)
then do
prettyInfoS "Already at latest version, no upgrade required"
return Nothing
else do
let ident = PackageIdentifier $(mkPackageName "stack") version
paths <- unpackPackageIdents tmp Nothing
-- accept latest cabal revision
[PackageIdentifierRevision ident CFILatest]
case Map.lookup ident paths of
Nothing -> error "Stack.Upgrade.upgrade: invariant violated, unpacked directory not found"
Just path -> return $ Just path
forM_ mdir $ \dir -> do
lc <- loadConfig
gConfigMonoid
mresolver
(SYLOverride $ dir </> $(mkRelFile "stack.yaml"))
bconfig <- liftIO $ lcLoadBuildConfig lc Nothing
envConfig1 <- runRIO bconfig $ setupEnv $ Just $
"Try rerunning with --install-ghc to install the correct GHC into " <>
T.pack (toFilePath (configLocalPrograms (view configL bconfig)))
runRIO (set (buildOptsL.buildOptsInstallExesL) True envConfig1) $
build (const $ return ()) Nothing defaultBuildOptsCLI
{ boptsCLITargets = ["stack"]
}