Compare commits

..

8 Commits

12 changed files with 19149 additions and 232 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -116,7 +116,20 @@ else
if [ "${OS}" = "LINUX" ] ; then
eghcup --downloader=wget prefetch ghc 8.10.3
eghcup --offline install ghc 8.10.3
else # test wget a bit
if [ "${ARCH}" = "64" ] ; then
expected=$(cat "$( cd "$(dirname "$0")" ; pwd -P )/../ghc-8.10.3-linux.files" | sort)
actual=$(cd "${GHCUP_DIR}/ghc/8.10.3/" && find | sort)
[ "${actual}" = "${expected}" ]
unset actual expected
fi
elif [ "${OS}" = "WINDOWS" ] ; then
eghcup prefetch ghc 8.10.3
eghcup --offline install ghc 8.10.3
expected=$(cat "$( cd "$(dirname "$0")" ; pwd -P )/../ghc-8.10.3-windows.files" | sort)
actual=$(cd "${GHCUP_DIR}/ghc/8.10.3/" && find | sort)
[ "${actual}" = "${expected}" ]
unset actual expected
else
eghcup prefetch ghc 8.10.3
eghcup --offline install ghc 8.10.3
fi

View File

@@ -19,8 +19,6 @@ Similar in scope to [rustup](https://github.com/rust-lang-nursery/rustup.rs), [p
* [Env variables](#env-variables)
* [Installing custom bindists](#installing-custom-bindists)
* [Tips and tricks](#tips-and-tricks)
* [Stack hooks](#stack-hooks)
* [Sharing MSys2 between stack and ghcup](#sharing-msys2-between-stack-and-ghcup)
* [Design goals](#design-goals)
* [How](#how)
* [Known users](#known-users)
@@ -141,10 +139,6 @@ This is the complete list of env variables that change GHCup behavior:
* `GHCUP_SKIP_UPDATE_CHECK`: Skip the (possibly annoying) update check when you run a command
* `CC`/`LD` etc.: full environment is passed to the build system when compiling GHC via GHCup
On windows, there are additional variables:
* `GHCUP_MSYS2`: where to find msys2, so we can invoke shells and other cool stuff
### Installing custom bindists
There are a couple of good use cases to install custom bindists:
@@ -208,36 +202,6 @@ with_ghc 8.10.5 code path/to/haskell/source
Cabal and HLS will now see `8.10.5` as the primary GHC, without the need to
run `ghcup set` all the time when switching between projects.
### Stack hooks
GHCup distributes a patched Stack, which has support for custom installation hooks, see:
* https://github.com/commercialhaskell/stack/pull/5585
Usually, the bootstrap script will already install a hook for you. If not,
download it [here](https://gitlab.haskell.org/haskell/ghcup-hs/-/tree/master/hooks/stack/ghc-install.sh),
place it in `~/.stack/hooks/ghc-install.sh` and make sure it's executable.
Hooks aren't run when `system-ghc: true` is set in `stack.yaml`. If you want stack
to never fall back to its own installation logic if ghcup fails, run the following command:
```sh
stack config set install-ghc false --global
```
### Sharing MSys2 between stack and ghcup
You can tell stack to use GHCup's MSys2 installation. Add the following lines to `~/.stack/config.yaml`:
```yml
skip-msys: true
extra-path:
- "C:\\ghcup\\msys64\\usr\\bin"
- "C:\\ghcup\\msys64\\mingw64\\bin"
extra-include-dirs: "C:\\ghcup\\msys64\\mingw64\\include"
extra-lib-dirs: "C:\\ghcup\\msys64\\mingw64\\lib"
```
## Design goals
1. simplicity
@@ -314,6 +278,18 @@ to figure out whether you have the correct toolchain and
the correct dependencies. Refer to [the official docs](https://ghc.haskell.org/trac/ghc/wiki/Building/Preparation/Linux)
on how to prepare your environment for building GHC.
### Stack support
There may be a number of bugs when trying to make ghcup installed GHC versions work with stack,
such as:
- https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/188
Further, stack's upgrade procedure may break/confuse ghcup. There are a number of integration
issues discussed here:
- https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/153
### Windows support
Windows support is in early stages. Since windows doesn't support symbolic links properly,

View File

@@ -237,7 +237,7 @@ validateTarballs (TarballFilter etool versionRegex) dls gt = do
($(logError) $ T.pack $ prettyShow e)
liftIO $ exitWith (ExitFailure 2)
let appstate = AppState (Settings True False Never Curl False GHCupURL False) dirs defaultKeyBindings (GHCupInfo mempty mempty mempty) pfreq
let appstate = AppState (Settings True False Never Curl True GHCupURL False) dirs defaultKeyBindings (GHCupInfo mempty mempty mempty) pfreq
r <-
runLogger

View File

@@ -8,7 +8,6 @@
# * BOOTSTRAP_HASKELL_GHC_VERSION - the ghc version to install
# * BOOTSTRAP_HASKELL_CABAL_VERSION - the cabal version to install
# * BOOTSTRAP_HASKELL_INSTALL_STACK - whether to install latest stack
# * BOOTSTRAP_HASKELL_INSTALL_STACK_HOOK - whether to install stack hook as well
# * BOOTSTRAP_HASKELL_INSTALL_HLS - whether to install latest hls
# * BOOTSTRAP_HASKELL_ADJUST_BASHRC - whether to adjust PATH in bashrc (prepend)
# * BOOTSTRAP_HASKELL_ADJUST_CABAL_CONFIG - whether to adjust mingw paths in cabal.config on windows
@@ -509,25 +508,17 @@ ask_hls() {
ask_stack() {
if [ -n "${BOOTSTRAP_HASKELL_INSTALL_STACK}" ] ; then
if [ -n "${BOOTSTRAP_HASKELL_INSTALL_STACK_HOOK}" ] ; then
return 2
else
return 1
fi
return 1
fi
if [ -z "${BOOTSTRAP_HASKELL_NONINTERACTIVE}" ] ; then
echo "-------------------------------------------------------------------------------"
warn "Do you want to install stack and stack hooks?"
warn ""
warn "Do you want to install stack?"
warn "Stack is a haskell build tool similar to cabal that is used by some projects."
warn "Also see https://docs.haskellstack.org/"
warn ""
warn "Stack hooks allow stack to use ghcup for GHC installation (usually stack handles"
warn "installation itself, leading to possibly duplicated GHC versions)."
warn ""
warn "[Y] Yes, with hooks [L] Yes, no hooks [N] No [?] Help (default is \"N\")."
warn "[Y] Yes [N] No [?] Help (default is \"N\")."
warn ""
while true; do
@@ -535,16 +526,13 @@ ask_stack() {
case $stack_answer in
[Yy]*)
return 2 ;;
[Ll]*)
return 1 ;;
[Nn]* | "")
return 0 ;;
*)
echo "Possible choices are:"
echo
echo "Y - Yes, install stack with hooks"
echo "L - Yes, install stack without hooks"
echo "Y - Yes, install stack"
echo "N - No, don't install anything more (default)"
echo
echo "Please make your choice and press ENTER."
@@ -648,27 +636,7 @@ esac
case $ask_stack_answer in
1)
{
_eghcup --cache install stack
edo stack update
} || warn "Stack installation failed, continuing anyway"
;;
2)
{
_eghcup --cache install stack
edo stack update
if [ -n "${STACK_ROOT}" ] ; then
stack_root=$STACK_ROOT
else
stack_root=${HOME}/.stack
fi
edo mkdir -p "${stack_root}/hooks/"
edo curl -Lf "https://www.haskell.org/ghcup/sh/hooks/stack/ghc-install.sh" > "${stack_root}/hooks/ghc-install.sh"
edo chmod +x "${stack_root}/hooks/ghc-install.sh"
edo stack config set system-ghc false --global
edo stack config set install-ghc false --global
} || warn "Stack installation failed, continuing anyway"
_eghcup --cache install stack || warn "Stack installation failed, continuing anyway"
;;
*) ;;
esac

View File

@@ -29,8 +29,6 @@ param (
[switch]$InBash,
# Whether to install stack as well
[switch]$InstallStack,
# Whether to install stack hooks as well
[switch]$InstallStackHook,
# Whether to install hls as well
[switch]$InstallHLS,
# Skip adjusting cabal.config with mingw paths
@@ -225,7 +223,6 @@ $null = [Environment]::SetEnvironmentVariable("GHCUP_INSTALL_BASE_PREFIX", $Ghcu
$GhcupDir = ('{0}\ghcup' -f $GhcupBasePrefix)
$StackDir = ('{0}\sr' -f $GhcupBasePrefix)
$MsysDir = ('{0}\msys64' -f $GhcupDir)
$Bash = ('{0}\usr\bin\bash' -f $MsysDir)
if (!($BootstrapUrl)) {
@@ -313,28 +310,15 @@ if (!($InstallHLS)) {
}
# ask whether to install stack
$InstallStackDoc = @'
Do you want to install stack and stack hooks?
Stack is a haskell build tool similar to cabal that is used by some projects.
Also see https://docs.haskellstack.org/
Stack hooks allow stack to use ghcup for GHC installation (usually stack handles
installation itself, leading to possibly duplicated GHC versions).
'@
if (!($InstallStack)) {
if (!($Silent)) {
$StackDecision = $Host.UI.PromptForChoice('Install stack'
, $InstallStackDoc
, [System.Management.Automation.Host.ChoiceDescription[]] @('&Yes, with hooks'
'&Light install, without hooks'
, 'Do you want to install stack as well?'
, [System.Management.Automation.Host.ChoiceDescription[]] @('&Yes'
'&No'
'&Abort'), 2)
'&Abort'), 1)
if ($StackDecision -eq 0) {
$InstallStack = $true
$InstallStackHook = $true
} elseif ($StackDecision -eq 1) {
$InstallStack = $true
} elseif ($StackDecision -eq 2) {
Exit 0
@@ -515,11 +499,6 @@ $SilentExport = 'export BOOTSTRAP_HASKELL_NONINTERACTIVE=1 ;'
if ($InstallStack) {
$StackInstallExport = 'export BOOTSTRAP_HASKELL_INSTALL_STACK=1 ;'
$null = [Environment]::SetEnvironmentVariable("STACK_ROOT", $StackDir, [System.EnvironmentVariableTarget]::User)
}
if ($InstallStackHook) {
$StackInstallHookExport = 'export BOOTSTRAP_HASKELL_INSTALL_STACK_HOOK=1 ;'
}
if ($InstallHLS) {
@@ -531,9 +510,9 @@ if (!($NoAdjustCabalConfig)) {
}
if ((Get-Process -ID $PID).ProcessName.StartsWith("bootstrap-haskell") -Or $InBash) {
Exec "$Bash" '-lc' ('{4} {6} {7} {8} {9} [ -n ''{1}'' ] && export GHCUP_MSYS2=$(cygpath -m ''{1}'') ; [ -n ''{2}'' ] && export GHCUP_INSTALL_BASE_PREFIX=$(cygpath -m ''{2}/'') ; export PATH=$(cygpath -u ''{3}/bin''):$PATH ; export CABAL_DIR=''{5}'' ; export STACK_ROOT=$(cygpath -m ''{10}/'') ; [[ ''{0}'' = https* ]] && curl --proto ''=https'' --tlsv1.2 -sSf {0} | bash || cat $(cygpath -m ''{0}'') | bash' -f $BootstrapUrl, $MsysDir, $GhcupBasePrefix, $GhcupDir, $SilentExport, $CabalDirFull, $StackInstallExport, $HLSInstallExport, $AdjustCabalConfigExport, $StackInstallHookExport, $StackDir)
Exec "$Bash" '-lc' ('{4} {6} {7} {8} [ -n ''{1}'' ] && export GHCUP_MSYS2=$(cygpath -m ''{1}'') ; [ -n ''{2}'' ] && export GHCUP_INSTALL_BASE_PREFIX=$(cygpath -m ''{2}/'') ; export PATH=$(cygpath -u ''{3}/bin''):$PATH ; export CABAL_DIR=''{5}'' ; [[ ''{0}'' = https* ]] && curl --proto ''=https'' --tlsv1.2 -sSf {0} | bash || cat $(cygpath -m ''{0}'') | bash' -f $BootstrapUrl, $MsysDir, $GhcupBasePrefix, $GhcupDir, $SilentExport, $CabalDirFull, $StackInstallExport, $HLSInstallExport, $AdjustCabalConfigExport)
} else {
Exec "$Msys2Shell" '-mingw64' '-mintty' '-c' ('{4} {6} {7} {8} {9} [ -n ''{1}'' ] && export GHCUP_MSYS2=$(cygpath -m ''{1}'') ; [ -n ''{2}'' ] && export GHCUP_INSTALL_BASE_PREFIX=$(cygpath -m ''{2}/'') ; export PATH=$(cygpath -u ''{3}/bin''):$PATH ; export CABAL_DIR=''{5}'' ; export STACK_ROOT=$(cygpath -m ''{10}/'') ; trap ''echo Press any key to exit && read -n 1 && exit'' 2 ; [[ ''{0}'' = https* ]] && curl --proto ''=https'' --tlsv1.2 -sSf {0} | bash || cat $(cygpath -m ''{0}'') | bash ; echo ''Press any key to exit'' && read -n 1' -f $BootstrapUrl, $MsysDir, $GhcupBasePrefix, $GhcupDir, $SilentExport, $CabalDirFull, $StackInstallExport, $HLSInstallExport, $AdjustCabalConfigExport, $StackInstallHookExport, $StackDir)
Exec "$Msys2Shell" '-mingw64' '-mintty' '-c' ('{4} {6} {7} {8} [ -n ''{1}'' ] && export GHCUP_MSYS2=$(cygpath -m ''{1}'') ; [ -n ''{2}'' ] && export GHCUP_INSTALL_BASE_PREFIX=$(cygpath -m ''{2}/'') ; export PATH=$(cygpath -u ''{3}/bin''):$PATH ; export CABAL_DIR=''{5}'' ; trap ''echo Press any key to exit && read -n 1 && exit'' 2 ; [[ ''{0}'' = https* ]] && curl --proto ''=https'' --tlsv1.2 -sSf {0} | bash || cat $(cygpath -m ''{0}'') | bash ; echo ''Press any key to exit'' && read -n 1' -f $BootstrapUrl, $MsysDir, $GhcupBasePrefix, $GhcupDir, $SilentExport, $CabalDirFull, $StackInstallExport, $HLSInstallExport, $AdjustCabalConfigExport)
}

View File

@@ -2206,7 +2206,8 @@ ghcupDownloads:
unknown_versioning: *stack-271-64
2.7.3:
viTags:
- old
- Latest
- Recommended
viChangeLog: https://github.com/commercialhaskell/stack/blob/master/ChangeLog.md#v273
viPostInstall: *stack-post
viArch:
@@ -2231,65 +2232,4 @@ ghcupDownloads:
RegexDir: "stack-.*"
Linux_Alpine:
unknown_versioning: *stack-273-64
2.7.3.1:
viTags:
- Latest
- Recommended
viChangeLog: https://github.com/commercialhaskell/stack/blob/master/ChangeLog.md#v273
viPostInstall: &stack-post-new |
Stack manages GHC versions internally by default. It can also use hooks to automatically
install and locate GHC versions utilizing GHCup:
https://gitlab.haskell.org/haskell/ghcup-hs#stack-hooks
Alternatively, you can also tell stack to use the system GHC version (whatever is in PATH):
stack config set system-ghc true --global
If you want stack to use GHCup's provided MSys2, follow the instructions here (not recommended):
https://gitlab.haskell.org/haskell/ghcup-hs/-/tree/stack-fork#sharing-msys2-between-stack-and-ghcup
Also check out: https://docs.haskellstack.org/en/stable/yaml_configuration
!!! GHCup ships a patched version of stack. Before reporting bugs to stack developers !!!
!!! make sure it is reproducible with the original stack version: !!!
!!! https://docs.haskellstack.org/en/stable/install_and_upgrade/ !!!
viArch:
A_64:
Linux_UnknownLinux:
unknown_versioning: &stack-2731-64
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/stack/2.7.3.1/stack-2.7.3.1-linux-x86_64.tar.gz
dlSubdir:
RegexDir: "stack-.*"
dlHash: 7c10090d568651208b6d6c003c8d78ef79099f4b472aea5ca800f3e4a7a1c6ce
Darwin:
unknown_versioning:
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/stack/2.7.3.1/stack-2.7.3.1-osx-x86_64.tar.gz
dlSubdir:
RegexDir: "stack-.*"
dlHash: 23030868be377d62ed324332d239ddd915b12f7a64c887f297e1b60a3f65894f
Windows:
unknown_versioning:
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/stack/2.7.3.1/stack-2.7.3.1-windows-x86_64.tar.bz2
dlHash: 983ac24219370cdf220f47f9edb17d0aeb831a1253fd777d322fd1bff358c511
Linux_Alpine:
unknown_versioning: *stack-2731-64
FreeBSD:
unknown_versioning:
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/stack/2.7.3.1/stack-2.7.3.1-freebsd-x86_64.tar.gz
dlHash: b6b1eb59981b8c1d3a04b061f03296b1540ecbadbc34cbd4fe712a575a155ee9
A_32:
Linux_UnknownLinux:
unknown_versioning: &stack-2731-32
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/stack/2.7.3.1/stack-2.7.3.1-linux-i386.tar.gz
dlHash: da2081d84517d16a8216e929582dd39caa19643c819b77c6f68f138bd177a758
Linux_Alpine:
unknown_versioning: *stack-2731-32
A_ARM64:
Linux_UnknownLinux:
unknown_versioning:
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/stack/2.7.3.1/stack-2.7.3.1-linux-aarch64.tar.gz
dlHash: 0e1789852a476dd87f495fa0b481309398b2bafc00cf7c8d980c91977b39d9a6
A_ARM:
Linux_UnknownLinux:
unknown_versioning:
dlUri: https://downloads.haskell.org/ghcup/unofficial-bindists/stack/2.7.3.1/stack-2.7.3.1-linux-armv7.tar.gz
dlHash: 13d61616c169615d80381ea0c84e13540410b736a553565cd48d10b4d2bd7f23

View File

@@ -1,23 +0,0 @@
#!/bin/sh
set -eu
case $HOOK_GHC_TYPE in
bindist)
ghcup --offline whereis ghc $HOOK_GHC_VERSION || {
ghcup --cache install ghc $HOOK_GHC_VERSION && ghcup whereis ghc $HOOK_GHC_VERSION
}
;;
git)
>&2 echo "Hook doesn't support installing from source."
>&2 echo "Consider enabling stack GHC installs for this project, via:"
>&2 echo " stack config set install-ghc true"
exit 1
;;
*)
>&2 echo "Unsupported GHC installation type: ${HOOK_GHC_TYPE}."
>&2 echo "Consider enabling stack GHC installs for this project, via:"
>&2 echo " stack config set install-ghc true"
exit 2
;;
esac

View File

@@ -300,10 +300,6 @@ installUnpackedGHC path inst ver = do
setModificationTime dest mtime
#else
PlatformRequest {..} <- lift getPlatformReq
liftIO $ copyDirectoryRecursive path inst $ \source dest -> do
mtime <- getModificationTime source
copyFile source dest
setModificationTime dest mtime
let alpineArgs
| ver >= [vver|8.2.2|], Linux Alpine <- _rPlatform

View File

@@ -172,21 +172,31 @@ getBase :: ( MonadReader env m
-> Excepts '[JSONError] m GHCupInfo
getBase uri = do
Settings { noNetwork } <- lift getSettings
yaml <- lift $ yamlFromCache uri
unless noNetwork $
handleIO (\e -> warnCache (displayException e))
. catchE @_ @_ @'[] (\e@(DownloadFailed _) -> warnCache (prettyShow e))
. reThrowAll @_ @_ @'[DownloadFailed] DownloadFailed
. smartDl
$ uri
-- try to download yaml... usually this writes it into cache dir,
-- but in some cases not (e.g. when using file://), so we honour
-- the return filepath, if any
mYaml <- if noNetwork && view (uriSchemeL' % schemeBSL') uri /= "file" -- for file://, let it fall through
then pure Nothing
else handleIO (\e -> warnCache (displayException e) >> pure Nothing)
. catchE @_ @_ @'[] (\e@(DownloadFailed _) -> warnCache (prettyShow e) >> pure Nothing)
. reThrowAll @_ @_ @'[DownloadFailed] DownloadFailed
. fmap Just
. smartDl
$ uri
-- if we didn't get a filepath from the download, use the cached yaml
actualYaml <- maybe (lift $ yamlFromCache uri) pure mYaml
lift $ $(logDebug) [i|Decoding yaml at: #{actualYaml}|]
liftE
. onE_ (onError yaml)
. onE_ (onError actualYaml)
. lEM' @_ @_ @'[JSONError] JSONDecodeError
. fmap (first (\e -> [i|#{displayException e}
Consider removing "#{yaml}" manually.|]))
Consider removing "#{actualYaml}" manually.|]))
. liftIO
. Y.decodeFileEither
$ yaml
$ actualYaml
where
-- On error, remove the etags file and set access time to 0. This should ensure the next invocation
-- may re-download and succeed.
@@ -221,28 +231,32 @@ Consider removing "#{yaml}" manually.|]))
, DigestError
]
m1
()
FilePath
smartDl uri' = do
json_file <- lift $ yamlFromCache uri'
let scheme = view (uriSchemeL' % schemeBSL') uri'
e <- liftIO $ doesFileExist json_file
currentTime <- liftIO getCurrentTime
if e
then do
accessTime <- liftIO $ getAccessTime json_file
Dirs { cacheDir } <- lift getDirs
-- access time won't work on most linuxes, but we can try regardless
when ((utcTimeToPOSIXSeconds currentTime - utcTimeToPOSIXSeconds accessTime) > 300) $
-- no access in last 5 minutes, re-check upstream mod time
dlWithMod currentTime json_file
else
dlWithMod currentTime json_file
-- for local files, let's short-circuit and ignore access time
if | scheme == "file" -> liftE $ download uri' Nothing cacheDir Nothing True
| e -> do
accessTime <- liftIO $ getAccessTime json_file
-- access time won't work on most linuxes, but we can try regardless
if | ((utcTimeToPOSIXSeconds currentTime - utcTimeToPOSIXSeconds accessTime) > 300) ->
-- no access in last 5 minutes, re-check upstream mod time
dlWithMod currentTime json_file
| otherwise -> pure json_file
| otherwise -> dlWithMod currentTime json_file
where
dlWithMod modTime json_file = do
let (dir, fn) = splitFileName json_file
f <- liftE $ download uri' Nothing dir (Just fn) True
liftIO $ setModificationTime f modTime
liftIO $ setAccessTime f modTime
pure f
getDownloadInfo :: ( MonadReader env m
@@ -304,27 +318,25 @@ download :: ( MonadReader env m
)
=> URI
-> Maybe T.Text -- ^ expected hash
-> FilePath -- ^ destination dir
-> FilePath -- ^ destination dir (ignored for file:// scheme)
-> Maybe FilePath -- ^ optional filename
-> Bool -- ^ whether to read an write etags
-> Excepts '[DigestError , DownloadFailed] m FilePath
download uri eDigest dest mfn etags
| scheme == "https" = dl
| scheme == "http" = dl
| scheme == "file" = cp
| scheme == "file" = do
let destFile' = T.unpack . decUTF8Safe $ path
lift $ $(logDebug) [i|using local file: #{destFile'}|]
forM_ eDigest (liftE . flip checkDigest destFile')
pure destFile'
| otherwise = throwE $ DownloadFailed (variantFromValue UnsupportedScheme)
where
scheme = view (uriSchemeL' % schemeBSL') uri
cp = do
-- destination dir must exist
liftIO $ createDirRecursive' dest
let fromFile = T.unpack . decUTF8Safe $ path
liftIO $ copyFile fromFile destFile
pure destFile
dl = do
let uri' = decUTF8Safe (serializeURIRef' uri)
lift $ $(logInfo) [i|downloading: #{uri'}|]
destFile <- liftE . reThrowAll @_ @_ @'[DownloadFailed] DownloadFailed $ getDestFile
lift $ $(logInfo) [i|downloading: #{uri'} as file #{destFile}|]
-- destination dir must exist
liftIO $ createDirRecursive' dest
@@ -366,7 +378,7 @@ download uri eDigest dest mfn etags
_ -> liftE $ throwE @_ @'[DownloadFailed] (DownloadFailed (toVariantAt @0 (MalformedHeaders headers)
:: V '[MalformedHeaders]))
writeEtags (parseEtags headers)
writeEtags destFile (parseEtags headers)
else
liftE $ lEM @_ @'[ProcessError] $ exec "curl"
(o' ++ ["-fL", "-o", destFile, T.unpack uri']) Nothing Nothing
@@ -383,13 +395,13 @@ download uri eDigest dest mfn etags
case _exitCode of
ExitSuccess -> do
liftIO $ copyFile destFileTemp destFile
writeEtags (parseEtags (decUTF8Safe' _stdErr))
writeEtags destFile (parseEtags (decUTF8Safe' _stdErr))
ExitFailure i'
| i' == 8
, Just _ <- find (T.pack "304 Not Modified" `T.isInfixOf`) . T.lines . decUTF8Safe' $ _stdErr
-> do
$logDebug "Not modified, skipping download"
writeEtags (parseEtags (decUTF8Safe' _stdErr))
writeEtags destFile (parseEtags (decUTF8Safe' _stdErr))
| otherwise -> throwE (NonZeroExit i' "wget" opts)
else do
let opts = o' ++ ["-O", destFileTemp , T.unpack uri']
@@ -404,10 +416,10 @@ download uri eDigest dest mfn etags
let addHeaders = maybe mempty (\etag -> M.fromList [ (mk . E.encodeUtf8 . T.pack $ "If-None-Match"
, E.encodeUtf8 etag)]) metag
liftE
$ catchE @HTTPNotModified @'[DownloadFailed] @'[] (\(HTTPNotModified etag) -> lift $ writeEtags (pure $ Just etag))
$ catchE @HTTPNotModified @'[DownloadFailed] @'[] (\(HTTPNotModified etag) -> lift $ writeEtags destFile (pure $ Just etag))
$ do
r <- downloadToFile https host fullPath port destFile addHeaders
writeEtags (pure $ decUTF8Safe <$> getHeader r "etag")
writeEtags destFile (pure $ decUTF8Safe <$> getHeader r "etag")
else void $ liftE $ catchE @HTTPNotModified
@'[DownloadFailed]
(\e@(HTTPNotModified _) ->
@@ -420,12 +432,18 @@ download uri eDigest dest mfn etags
-- Manage to find a file we can write the body into.
destFile :: FilePath
destFile = maybe (dest </> T.unpack (decUTF8Safe (urlBaseName path)))
(dest </>)
mfn
getDestFile :: Monad m => Excepts '[NoUrlBase] m FilePath
getDestFile =
case mfn of
Just fn -> pure (dest </> fn)
Nothing
| let urlBase = T.unpack (decUTF8Safe (urlBaseName path))
, not (null urlBase) -> pure (dest </> urlBase)
-- TODO: remove this once we use hpath again
| otherwise -> throwE $ NoUrlBase uri'
path = view pathL' uri
uri' = decUTF8Safe (serializeURIRef' uri)
parseEtags :: (MonadLogger m, MonadIO m, MonadThrow m) => T.Text -> m (Maybe T.Text)
parseEtags stderr = do
@@ -444,8 +462,8 @@ download uri eDigest dest mfn etags
$logDebug "No etags header found"
pure Nothing
writeEtags :: (MonadLogger m, MonadIO m, MonadThrow m) => m (Maybe T.Text) -> m ()
writeEtags getTags = do
writeEtags :: (MonadLogger m, MonadIO m, MonadThrow m) => FilePath -> m (Maybe T.Text) -> m ()
writeEtags destFile getTags = do
getTags >>= \case
Just t -> do
$logDebug [i|Writing etagsFile #{(etagsFile destFile)}|]

View File

@@ -327,6 +327,15 @@ instance Pretty UnexpectedListLength where
instance Exception UnexpectedListLength
data NoUrlBase = NoUrlBase Text
deriving Show
instance Pretty NoUrlBase where
pPrint (NoUrlBase url) =
text [i|Couldn't get a base filename from url #{url}|]
instance Exception NoUrlBase
------------------------