@@ -54,10 +54,13 @@ import qualified Distribution.System as Cabal
54
54
import Distribution.Text (simpleParse )
55
55
import Distribution.Types.PackageName (mkPackageName )
56
56
import Distribution.Version (mkVersion )
57
+ import Network.HTTP.Client (redirectCount )
57
58
import Network.HTTP.StackClient (CheckHexDigest (.. ), HashCheck (.. ),
58
59
getResponseBody , getResponseStatusCode , httpLbs , httpJSON ,
59
60
mkDownloadRequest , parseRequest , parseUrlThrow , setGithubHeaders ,
60
- setHashChecks , setLengthCheck , verifiedDownloadWithProgress , withResponse )
61
+ setHashChecks , setLengthCheck , verifiedDownloadWithProgress , withResponse ,
62
+ setRequestMethod )
63
+ import Network.HTTP.Simple (getResponseHeader )
61
64
import Path hiding (fileExtension )
62
65
import Path.CheckInstall (warnInstallSearchPathIssues )
63
66
import Path.Extended (fileExtension )
@@ -1780,14 +1783,92 @@ getUtf8EnvVars compilerVer =
1780
1783
1781
1784
-- Binary Stack upgrades
1782
1785
1783
- newtype StackReleaseInfo = StackReleaseInfo Value
1784
-
1785
- downloadStackReleaseInfo :: (MonadIO m , MonadThrow m )
1786
- => Maybe String -- Github org
1787
- -> Maybe String -- Github repo
1788
- -> Maybe String -- ^ optional version
1789
- -> m StackReleaseInfo
1790
- downloadStackReleaseInfo morg mrepo mver = liftIO $ do
1786
+ -- | Information on a binary release of Stack
1787
+ data StackReleaseInfo
1788
+ = SRIGithub ! Value
1789
+ -- ^ Metadata downloaded from GitHub releases about available binaries.
1790
+ | SRIHaskellStackOrg ! HaskellStackOrg
1791
+ -- ^ Information on the latest available binary for the current platforms.
1792
+
1793
+ data HaskellStackOrg = HaskellStackOrg
1794
+ { hsoUrl :: ! Text
1795
+ , hsoVersion :: ! Version
1796
+ }
1797
+ deriving Show
1798
+
1799
+ downloadStackReleaseInfo
1800
+ :: (HasPlatform env , HasLogFunc env )
1801
+ => Maybe String -- Github org
1802
+ -> Maybe String -- Github repo
1803
+ -> Maybe String -- ^ optional version
1804
+ -> RIO env StackReleaseInfo
1805
+ downloadStackReleaseInfo Nothing Nothing Nothing = do
1806
+ platform <- view platformL
1807
+ -- Fallback list of URLs to try for upgrading.
1808
+ let urls0 =
1809
+ case platform of
1810
+ Platform X86_64 Cabal. Linux ->
1811
+ [ " https://get.haskellstack.org/upgrade/linux-x86_64-static.tar.gz"
1812
+ , " https://get.haskellstack.org/upgrade/linux-x86_64.tar.gz"
1813
+ ]
1814
+ Platform X86_64 Cabal. OSX ->
1815
+ [ " https://get.haskellstack.org/upgrade/osx-x86_64.tar.gz"
1816
+ ]
1817
+ Platform X86_64 Cabal. Windows ->
1818
+ [ " https://get.haskellstack.org/upgrade/windows-x86_64.tar.gz"
1819
+ ]
1820
+ _ -> []
1821
+ -- Helper function: extract the version from a GitHub releases URL.
1822
+ let extractVersion loc = do
1823
+ version0 <-
1824
+ case reverse $ splitOn " /" $ T. unpack loc of
1825
+ _final: version0: _ -> Right version0
1826
+ _ -> Left $ " Insufficient pieces in location: " ++ show loc
1827
+ version1 <- maybe (Left " no leading v on version" ) Right $ stripPrefix " v" version0
1828
+ maybe (Left $ " Invalid version: " ++ show version1) Right $ parseVersion version1
1829
+
1830
+ -- Try out different URLs. If we've exhausted all of them, fall back to GitHub.
1831
+ loop [] = do
1832
+ logDebug " Could not get binary from haskellstack.org, trying GitHub"
1833
+ downloadStackReleaseInfoGithub Nothing Nothing Nothing
1834
+
1835
+ -- Try the next URL
1836
+ loop (url: urls) = do
1837
+ -- Make a HEAD request without any redirects
1838
+ req <- setRequestMethod " HEAD" <$> parseRequest (T. unpack url)
1839
+ res <- httpLbs req { redirectCount = 0 }
1840
+
1841
+ -- Look for a redirect. We're looking for a standard GitHub releases
1842
+ -- URL where we can extract version information from.
1843
+ case getResponseHeader " location" res of
1844
+ [] -> logDebug " No location header found, continuing" *> loop urls
1845
+ -- Exactly one location header.
1846
+ [locBS] ->
1847
+ case decodeUtf8' locBS of
1848
+ Left e -> logDebug (" Invalid UTF8: " <> displayShow (locBS, e)) *> loop urls
1849
+ Right loc ->
1850
+ case extractVersion loc of
1851
+ Left s -> logDebug (" No version found: " <> displayShow (url, loc, s)) *> loop (loc: urls)
1852
+ -- We found a valid URL, let's use it!
1853
+ Right version -> do
1854
+ let hso = HaskellStackOrg
1855
+ { hsoUrl = loc
1856
+ , hsoVersion = version
1857
+ }
1858
+ logDebug $ " Downloading from haskellstack.org: " <> displayShow hso
1859
+ pure $ SRIHaskellStackOrg hso
1860
+ locs -> logDebug (" Multiple location headers found: " <> displayShow locs) *> loop urls
1861
+ loop urls0
1862
+ downloadStackReleaseInfo morg mrepo mver = downloadStackReleaseInfoGithub morg mrepo mver
1863
+
1864
+ -- | Same as above, but always uses Github
1865
+ downloadStackReleaseInfoGithub
1866
+ :: (MonadIO m , MonadThrow m )
1867
+ => Maybe String -- Github org
1868
+ -> Maybe String -- Github repo
1869
+ -> Maybe String -- ^ optional version
1870
+ -> m StackReleaseInfo
1871
+ downloadStackReleaseInfoGithub morg mrepo mver = liftIO $ do
1791
1872
let org = fromMaybe " commercialhaskell" morg
1792
1873
repo = fromMaybe " stack" mrepo
1793
1874
let url = concat
@@ -1804,7 +1885,7 @@ downloadStackReleaseInfo morg mrepo mver = liftIO $ do
1804
1885
res <- httpJSON $ setGithubHeaders req
1805
1886
let code = getResponseStatusCode res
1806
1887
if code >= 200 && code < 300
1807
- then return $ StackReleaseInfo $ getResponseBody res
1888
+ then return $ SRIGithub $ getResponseBody res
1808
1889
else throwString $ " Could not get release information for Stack from: " ++ url
1809
1890
1810
1891
preferredPlatforms :: (MonadReader env m , HasPlatform env , MonadThrow m )
@@ -1899,7 +1980,7 @@ downloadStackExe platforms0 archiveInfo destDir checkPath testExe = do
1899
1980
`catchAny` (logError . displayShow)
1900
1981
where
1901
1982
1902
- findArchive (StackReleaseInfo val) pattern = do
1983
+ findArchive (SRIGithub val) pattern = do
1903
1984
Object top <- return val
1904
1985
Array assets <- HashMap. lookup " assets" top
1905
1986
getFirst $ fold $ fmap (First . findMatch pattern') assets
@@ -1913,6 +1994,7 @@ downloadStackExe platforms0 archiveInfo destDir checkPath testExe = do
1913
1994
String url <- HashMap. lookup " browser_download_url" o
1914
1995
Just url
1915
1996
findMatch _ _ = Nothing
1997
+ findArchive (SRIHaskellStackOrg hso) _ = pure $ hsoUrl hso
1916
1998
1917
1999
handleTarball :: Path Abs File -> Bool -> T. Text -> IO ()
1918
2000
handleTarball tmpFile isWindows url = do
@@ -1928,25 +2010,26 @@ downloadStackExe platforms0 archiveInfo destDir checkPath testExe = do
1928
2010
, T. unpack url
1929
2011
]
1930
2012
loop (Tar. Fail e) = throwM e
1931
- loop (Tar. Next e es)
1932
- | Tar. entryPath e == exeName =
1933
- case Tar. entryContent e of
1934
- Tar. NormalFile lbs _ -> do
1935
- ensureDir destDir
1936
- LBS. writeFile (toFilePath tmpFile) lbs
1937
- _ -> error $ concat
1938
- [ " Invalid file type for tar entry named "
1939
- , exeName
1940
- , " downloaded from "
1941
- , T. unpack url
1942
- ]
1943
- | otherwise = loop es
2013
+ loop (Tar. Next e es) =
2014
+ case FP. splitPath (Tar. entryPath e) of
2015
+ -- Ignore the first component, see: https://github.com/commercialhaskell/stack/issues/5288
2016
+ [_ignored, name] | name == exeName -> do
2017
+ case Tar. entryContent e of
2018
+ Tar. NormalFile lbs _ -> do
2019
+ ensureDir destDir
2020
+ LBS. writeFile (toFilePath tmpFile) lbs
2021
+ _ -> error $ concat
2022
+ [ " Invalid file type for tar entry named "
2023
+ , Tar. entryPath e
2024
+ , " downloaded from "
2025
+ , T. unpack url
2026
+ ]
2027
+ _ -> loop es
1944
2028
loop entries
1945
2029
where
1946
- -- The takeBaseName drops the .gz, dropExtension drops the .tar
1947
- exeName =
1948
- let base = FP. dropExtension (FP. takeBaseName (T. unpack url)) FP. </> " stack"
1949
- in if isWindows then base FP. <.> " exe" else base
2030
+ exeName
2031
+ | isWindows = " stack.exe"
2032
+ | otherwise = " stack"
1950
2033
1951
2034
-- | Ensure that the Stack executable download is in the same location
1952
2035
-- as the currently running executable. See:
@@ -2004,8 +2087,9 @@ performPathChecking newFile executablePath = do
2004
2087
| otherwise -> throwM e
2005
2088
2006
2089
getDownloadVersion :: StackReleaseInfo -> Maybe Version
2007
- getDownloadVersion (StackReleaseInfo val) = do
2090
+ getDownloadVersion (SRIGithub val) = do
2008
2091
Object o <- Just val
2009
2092
String rawName <- HashMap. lookup " name" o
2010
2093
-- drop the "v" at the beginning of the name
2011
2094
parseVersion $ T. unpack (T. drop 1 rawName)
2095
+ getDownloadVersion (SRIHaskellStackOrg hso) = Just $ hsoVersion hso
0 commit comments