getSDistTarball
  :: (StackM env m, HasEnvConfig env)
  => Maybe PvpBounds            -- ^ Override Config value
  -> Path Abs Dir               -- ^ Path to local package
  -> m (FilePath, L.ByteString, Maybe (PackageIdentifier, L.ByteString))
  -- ^ Filename, tarball contents, and option cabal file revision to upload
getSDistTarball mpvpBounds pkgDir = do
    config <- view configL
    let PvpBounds pvpBounds asRevision = fromMaybe (configPvpBounds config) mpvpBounds
        tweakCabal = pvpBounds /= PvpBoundsNone
        pkgFp = toFilePath pkgDir
    lp <- readLocalPackage pkgDir
    $logInfo $ "Getting file list for " <> T.pack pkgFp
    (fileList, cabalfp) <-  getSDistFileList lp
    $logInfo $ "Building sdist tarball for " <> T.pack pkgFp
    files <- normalizeTarballPaths (lines fileList)

    -- We're going to loop below and eventually find the cabal
    -- file. When we do, we'll upload this reference, if the
    -- mpvpBounds value indicates that we should be uploading a cabal
    -- file revision.
    cabalFileRevisionRef <- liftIO (newIORef Nothing)

    -- NOTE: Could make this use lazy I/O to only read files as needed
    -- for upload (both GZip.compress and Tar.write are lazy).
    -- However, it seems less error prone and more predictable to read
    -- everything in at once, so that's what we're doing for now:
    let tarPath isDir fp = either throwString return
            (Tar.toTarPath isDir (forceUtf8Enc (pkgId FP.</> fp)))
        -- convert a String of proper characters to a String of bytes
        -- in UTF8 encoding masquerading as characters. This is
        -- necessary for tricking the tar package into proper
        -- character encoding.
        forceUtf8Enc = S8.unpack . T.encodeUtf8 . T.pack
        packWith f isDir fp = liftIO $ f (pkgFp FP.</> fp) =<< tarPath isDir fp
        packDir = packWith Tar.packDirectoryEntry True
        packFile fp
            -- This is a cabal file, we're going to tweak it, but only
            -- tweak it as a revision.
            | tweakCabal && isCabalFp fp && asRevision = do
                lbsIdent <- getCabalLbs pvpBounds (Just 1) $ toFilePath cabalfp
                liftIO (writeIORef cabalFileRevisionRef (Just lbsIdent))
                packWith packFileEntry False fp
            -- Same, except we'll include the cabal file in the
            -- original tarball upload.
            | tweakCabal && isCabalFp fp = do
                (_ident, lbs) <- getCabalLbs pvpBounds Nothing $ toFilePath cabalfp
                currTime <- liftIO getPOSIXTime -- Seconds from UNIX epoch
                tp <- liftIO $ tarPath False fp
                return $ (Tar.fileEntry tp lbs) { Tar.entryTime = floor currTime }
            | otherwise = packWith packFileEntry False fp
        isCabalFp fp = toFilePath pkgDir FP.</> fp == toFilePath cabalfp
        tarName = pkgId FP.<.> "tar.gz"
        pkgId = packageIdentifierString (packageIdentifier (lpPackage lp))
    dirEntries <- mapM packDir (dirsFromFiles files)
    fileEntries <- mapM packFile files
    mcabalFileRevision <- liftIO (readIORef cabalFileRevisionRef)
    return (tarName, GZip.compress (Tar.write (dirEntries ++ fileEntries)), mcabalFileRevision)

-- | Get the PVP bounds-enabled version of the given cabal file
getSDistTarball
  :: (StackM env m, HasEnvConfig env)
  => Maybe PvpBounds            -- ^ Override Config value
  -> Path Abs Dir               -- ^ Path to local package
  -> m (FilePath, L.ByteString, Maybe (PackageIdentifier, L.ByteString))
getSDistTarball mpvpBounds pkgDir = do
    config <- view configL
    let PvpBounds pvpBounds asRevision = fromMaybe (configPvpBounds config) mpvpBounds
        tweakCabal = pvpBounds /= PvpBoundsNone
        pkgFp = toFilePath pkgDir
    lp <- readLocalPackage pkgDir
    $logInfo $ "Getting file list for " <> T.pack pkgFp
    (fileList, cabalfp) <-  getSDistFileList lp
    $logInfo $ "Building sdist tarball for " <> T.pack pkgFp
    files <- normalizeTarballPaths (lines fileList)

    -- We're going to loop below and eventually find the cabal
    -- file. When we do, we'll upload this reference, if the
    -- mpvpBounds value indicates that we should be uploading a cabal
    -- file revision.
    cabalFileRevisionRef <- liftIO (newIORef Nothing)

    -- NOTE: Could make this use lazy I/O to only read files as needed
    -- for upload (both GZip.compress and Tar.write are lazy).
    -- However, it seems less error prone and more predictable to read
    -- everything in at once, so that's what we're doing for now:
    let tarPath isDir fp = either throwString return
            (Tar.toTarPath isDir (forceUtf8Enc (pkgId FP.</> fp)))
        -- convert a String of proper characters to a String of bytes
        -- in UTF8 encoding masquerading as characters. This is
        -- necessary for tricking the tar package into proper
        -- character encoding.
        forceUtf8Enc = S8.unpack . T.encodeUtf8 . T.pack
        packWith f isDir fp = liftIO $ f (pkgFp FP.</> fp) =<< tarPath isDir fp
        packDir = packWith Tar.packDirectoryEntry True
        packFile fp
            -- This is a cabal file, we're going to tweak it, but only
            -- tweak it as a revision.
            | tweakCabal && isCabalFp fp && asRevision = do
                lbsIdent <- getCabalLbs pvpBounds (Just 1) $ toFilePath cabalfp
                liftIO (writeIORef cabalFileRevisionRef (Just lbsIdent))
                packWith packFileEntry False fp
            -- Same, except we'll include the cabal file in the
            -- original tarball upload.
            | tweakCabal && isCabalFp fp = do
                (_ident, lbs) <- getCabalLbs pvpBounds Nothing $ toFilePath cabalfp
                currTime <- liftIO getPOSIXTime -- Seconds from UNIX epoch
                tp <- liftIO $ tarPath False fp
                return $ (Tar.fileEntry tp lbs) { Tar.entryTime = floor currTime }
            | otherwise = packWith packFileEntry False fp
        isCabalFp fp = toFilePath pkgDir FP.</> fp == toFilePath cabalfp
        tarName = pkgId FP.<.> "tar.gz"
        pkgId = packageIdentifierString (packageIdentifier (lpPackage lp))
    dirEntries <- mapM packDir (dirsFromFiles files)
    fileEntries <- mapM packFile files
    mcabalFileRevision <- liftIO (readIORef cabalFileRevisionRef)
    return (tarName, GZip.compress (Tar.write (dirEntries ++ fileEntries)), mcabalFileRevision)

-- | Get the PVP bounds-enabled version of the given cabal file
getSDistTarball mpvpBounds pkgDir = do
    config <- view configL
    let PvpBounds pvpBounds asRevision = fromMaybe (configPvpBounds config) mpvpBounds
        tweakCabal = pvpBounds /= PvpBoundsNone
        pkgFp = toFilePath pkgDir
    lp <- readLocalPackage pkgDir
    $logInfo $ "Getting file list for " <> T.pack pkgFp
    (fileList, cabalfp) <-  getSDistFileList lp
    $logInfo $ "Building sdist tarball for " <> T.pack pkgFp
    files <- normalizeTarballPaths (lines fileList)

    -- We're going to loop below and eventually find the cabal
    -- file. When we do, we'll upload this reference, if the
    -- mpvpBounds value indicates that we should be uploading a cabal
    -- file revision.
    cabalFileRevisionRef <- liftIO (newIORef Nothing)

    -- NOTE: Could make this use lazy I/O to only read files as needed
    -- for upload (both GZip.compress and Tar.write are lazy).
    -- However, it seems less error prone and more predictable to read
    -- everything in at once, so that's what we're doing for now:
    let tarPath isDir fp = either throwString return
            (Tar.toTarPath isDir (forceUtf8Enc (pkgId FP.</> fp)))
        -- convert a String of proper characters to a String of bytes
        -- in UTF8 encoding masquerading as characters. This is
        -- necessary for tricking the tar package into proper
        -- character encoding.
        forceUtf8Enc = S8.unpack . T.encodeUtf8 . T.pack
        packWith f isDir fp = liftIO $ f (pkgFp FP.</> fp) =<< tarPath isDir fp
        packDir = packWith Tar.packDirectoryEntry True
        packFile fp
            -- This is a cabal file, we're going to tweak it, but only
            -- tweak it as a revision.
            | tweakCabal && isCabalFp fp && asRevision = do
                lbsIdent <- getCabalLbs pvpBounds (Just 1) $ toFilePath cabalfp
                liftIO (writeIORef cabalFileRevisionRef (Just lbsIdent))
                packWith packFileEntry False fp
            -- Same, except we'll include the cabal file in the
            -- original tarball upload.
            | tweakCabal && isCabalFp fp = do
                (_ident, lbs) <- getCabalLbs pvpBounds Nothing $ toFilePath cabalfp
                currTime <- liftIO getPOSIXTime -- Seconds from UNIX epoch
                tp <- liftIO $ tarPath False fp
                return $ (Tar.fileEntry tp lbs) { Tar.entryTime = floor currTime }
            | otherwise = packWith packFileEntry False fp
        isCabalFp fp = toFilePath pkgDir FP.</> fp == toFilePath cabalfp
        tarName = pkgId FP.<.> "tar.gz"
        pkgId = packageIdentifierString (packageIdentifier (lpPackage lp))
    dirEntries <- mapM packDir (dirsFromFiles files)
    fileEntries <- mapM packFile files
    mcabalFileRevision <- liftIO (readIORef cabalFileRevisionRef)
    return (tarName, GZip.compress (Tar.write (dirEntries ++ fileEntries)), mcabalFileRevision)

-- | Get the PVP bounds-enabled version of the given cabal file