Skip to content

Commit f64331d

Browse files
committed
Use shorter hash for script-builds directories
Using a Base64 hash and truncating it to 26 characters, saves 38 chars, which helps avoid long paths issues on Windows, while still providing 130 bits of hash in order to avoid collisions. Bug haskell#8841
1 parent d53b4d4 commit f64331d

File tree

5 files changed

+18
-6
lines changed

5 files changed

+18
-6
lines changed

cabal-install/cabal-install.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ library
205205
async >= 2.0 && < 2.3,
206206
array >= 0.4 && < 0.6,
207207
base16-bytestring >= 0.1.1 && < 1.1.0.0,
208+
base64-bytestring >= 1.0 && < 1.3,
208209
binary >= 0.7.3 && < 0.9,
209210
bytestring >= 0.10.6.0 && < 0.12,
210211
containers >= 0.5.6.2 && < 0.7,

cabal-install/src/Distribution/Client/HashValue.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module Distribution.Client.HashValue (
66
hashValue,
77
truncateHash,
88
showHashValue,
9+
showHashValueBase64,
910
readFileHashValue,
1011
hashFromTUF,
1112
) where
@@ -17,6 +18,7 @@ import qualified Hackage.Security.Client as Sec
1718

1819
import qualified Crypto.Hash.SHA256 as SHA256
1920
import qualified Data.ByteString.Base16 as Base16
21+
import qualified Data.ByteString.Base64 as Base64
2022
import qualified Data.ByteString.Char8 as BS
2123
import qualified Data.ByteString.Lazy.Char8 as LBS
2224

@@ -55,6 +57,9 @@ hashValue = HashValue . SHA256.hashlazy
5557
showHashValue :: HashValue -> String
5658
showHashValue (HashValue digest) = BS.unpack (Base16.encode digest)
5759

60+
showHashValueBase64 :: HashValue -> String
61+
showHashValueBase64 (HashValue digest) = BS.unpack (Base64.encode digest)
62+
5863
-- | Hash the content of a file. Uses SHA256.
5964
--
6065
readFileHashValue :: FilePath -> IO HashValue

cabal-install/src/Distribution/Client/ScriptUtils.hs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import Distribution.Client.Config
2626
import Distribution.Client.DistDirLayout
2727
( DistDirLayout(..) )
2828
import Distribution.Client.HashValue
29-
( hashValue, showHashValue )
29+
( hashValue, showHashValueBase64 )
3030
import Distribution.Client.HttpUtils
3131
( HttpTransport, configureTransport )
3232
import Distribution.Client.NixStyleOptions
@@ -125,7 +125,12 @@ import qualified Text.Parsec as P
125125
-- Two hashes will be the same as long as the absolute paths
126126
-- are the same.
127127
getScriptHash :: FilePath -> IO String
128-
getScriptHash script = showHashValue . hashValue . fromString <$> canonicalizePath script
128+
getScriptHash script
129+
-- Base64 is shorter than Base16, which helps avoid long path issues on windows
130+
-- but it can contain /'s which aren't valid in file paths so replace them with
131+
-- %'s. 26 chars / 130 bits is enough to practically avoid collisions.
132+
= map (\c -> if c == '/' then '%' else c) . take 26
133+
. showHashValueBase64 . hashValue . fromString <$> canonicalizePath script
129134

130135
-- | Get the directory for caching a script build.
131136
--

cabal-testsuite/cabal-testsuite.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ library
6060
, aeson ^>= 1.4.2.0 || ^>=1.5.0.0 || ^>= 2.0.0.0 || ^>= 2.1.0.0
6161
, async ^>= 2.2.1
6262
, attoparsec ^>= 0.13.2.2 || ^>=0.14.1
63-
, base16-bytestring ^>= 0.1.1.6 || ^>= 1.0.0.0
63+
, base64-bytestring ^>= 1.0.0.0 || ^>= 1.1.0.0 || ^>= 1.2.0.0
6464
, bytestring ^>= 0.10.0.2 || ^>= 0.11.0.0
6565
, containers ^>= 0.5.0.0 || ^>= 0.6.0.1
6666
, cryptohash-sha256 ^>= 0.11.101.0

cabal-testsuite/src/Test/Cabal/Prelude.hs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ import Control.Monad (unless, when, void, forM_, liftM2, liftM4)
5252
import Control.Monad.Trans.Reader (withReaderT, runReaderT)
5353
import Control.Monad.IO.Class (MonadIO (..))
5454
import qualified Crypto.Hash.SHA256 as SHA256
55-
import qualified Data.ByteString.Base16 as Base16
55+
import qualified Data.ByteString.Base64 as Base64
5656
import qualified Data.ByteString.Char8 as C
57-
import Data.List (isInfixOf, stripPrefix, isPrefixOf, intercalate)
57+
import Data.List (dropWhileEnd, isInfixOf, stripPrefix, isPrefixOf, intercalate)
5858
import Data.List.NonEmpty (NonEmpty (..))
5959
import qualified Data.List.NonEmpty as NE
6060
import Data.Maybe (mapMaybe, fromMaybe)
@@ -837,7 +837,8 @@ getScriptCacheDirectory :: FilePath -> TestM FilePath
837837
getScriptCacheDirectory script = do
838838
cabalDir <- testCabalDir `fmap` getTestEnv
839839
hashinput <- liftIO $ canonicalizePath script
840-
let hash = C.unpack . Base16.encode . SHA256.hash . C.pack $ hashinput
840+
let hash = map (\c -> if c == '/' then '%' else c) . take 26
841+
. C.unpack . Base64.encode . SHA256.hash . C.pack $ hashinput
841842
return $ cabalDir </> "script-builds" </> hash
842843

843844
------------------------------------------------------------------------

0 commit comments

Comments
 (0)