-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Jordan Mackie
committed
Nov 29, 2020
1 parent
86faddc
commit 4f2213a
Showing
10 changed files
with
319 additions
and
169 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
--command="cabal new-repl test" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
name: CI | ||
on: | ||
pull_request: | ||
push: | ||
branches: | ||
- master | ||
|
||
jobs: | ||
build-test-check: | ||
matrix: | ||
os: | ||
- ubuntu-latest | ||
# TODO: check more OS? | ||
compiler: | ||
- "ghc802" | ||
- "ghc822" | ||
- "ghc844" | ||
- "ghc865" | ||
- "ghc883" | ||
- "ghc8101" | ||
|
||
runs-on: "${{ matrix.os }}" | ||
steps: | ||
- name: Checkout repository | ||
uses: actions/[email protected] | ||
|
||
- name: Install Nix | ||
uses: cachix/install-nix-action@v12 | ||
|
||
- name: Setup cabal cache | ||
uses: actions/cache@v1 | ||
with: | ||
path: "~/.cabal" | ||
key: ${{ runner.os }}-${{ matrix.ghc }}-${{ hashFiles('kesha.cabal') }} | ||
|
||
- name: Run ci.sh | ||
run: "nix-shell --arg dev false --run ./ci.sh" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,4 @@ dist-newstyle/ | |
dist/ | ||
.stack-work/ | ||
result | ||
cabal.project.freeze |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,4 @@ | ||
#!/usr/bin/env nix-shell | ||
#! nix-shell -i bash | ||
#!/usr/bin/env bash | ||
|
||
set -euo pipefail | ||
shopt -s inherit_errexit | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,28 @@ | ||
cabal-version: >=1.10 | ||
build-type: Simple | ||
cabal-version: >= 1.10 | ||
|
||
name: kesha | ||
version: 0.2.0 | ||
synopsis: Compute the cryptographic hash of any path | ||
description: | ||
A Haskell library and executable for computing the cryptographic hash of any path. | ||
|
||
version: 0.1.0 | ||
synopsis: Haskell implementation of nix-hash | ||
description: Compute the cryptographic hash of a path, à la <https://nixos.org/ Nix>. | ||
homepage: https://github.com/jmackie/kesha | ||
bug-reports: https://github.com/jmackie/kesha/issues | ||
license: MIT | ||
license-file: LICENSE | ||
author: Jordan Mackie | ||
maintainer: [email protected] | ||
maintainer: [email protected] | ||
copyright: (c) 2020 Jordan Mackie | ||
category: System | ||
build-type: Simple | ||
extra-source-files: | ||
README.md | ||
|
||
tested-with: | ||
GHC ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.3 | ||
GHC == 8.0.2, | ||
GHC == 8.2.2, | ||
GHC == 8.4.4, | ||
GHC == 8.6.5, | ||
GHC == 8.8.3, | ||
GHC == 8.10.1 | ||
|
||
source-repository head | ||
type: git | ||
|
@@ -28,7 +31,11 @@ source-repository head | |
library | ||
default-language: Haskell2010 | ||
hs-source-dirs: src | ||
ghc-options: -Wall | ||
ghc-options: | ||
-Weverything | ||
-fno-warn-missing-import-lists | ||
-fno-warn-safe | ||
-fno-warn-unsafe | ||
exposed-modules: | ||
Kesha | ||
Kesha.NAR | ||
|
@@ -37,34 +44,43 @@ library | |
-- >= 8.2.2 && < 8.9 | ||
base >= 4.10.1 && < 4.14, | ||
|
||
-- boot libraries | ||
binary, | ||
bytestring, | ||
containers, | ||
-- core libraries | ||
binary >= 0.8.6 && < 0.9, | ||
bytestring >= 0.10.8 && < 0.11, | ||
containers >= 0.6.0 && < 0.7, | ||
filepath >= 1.4.2 && < 1.5, | ||
text >= 1.2.3 && < 1.3, | ||
-- 1.3.1.0 introduced `getSymbolicLinkTarget` | ||
directory >= 1.3.1, | ||
filepath, | ||
text, | ||
directory >= 1.3.1 && < 1.4, | ||
|
||
cryptohash-sha256 | ||
cryptohash-md5 >= 0.11.100 && < 0.12, | ||
cryptohash-sha1 >= 0.11.100 && < 0.12, | ||
cryptohash-sha256 >= 0.11.101 && < 0.12 | ||
|
||
test-suite test | ||
default-language: Haskell2010 | ||
type: exitcode-stdio-1.0 | ||
main-is: Main.hs | ||
hs-source-dirs: test | ||
ghc-options: -Wall -threaded -rtsopts | ||
ghc-options: | ||
-Weverything | ||
-fno-warn-missing-import-lists | ||
-fno-warn-safe | ||
-fno-warn-unsafe | ||
|
||
-threaded | ||
-rtsopts | ||
build-depends: | ||
kesha, | ||
|
||
base >= 4.10.1 && < 4.14, | ||
|
||
base, | ||
bytestring, | ||
containers, | ||
directory >= 1.3.1, | ||
directory, | ||
filepath, | ||
process, | ||
|
||
-- Test dependencies | ||
process, | ||
hspec, | ||
QuickCheck, | ||
temporary |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,22 @@ | ||
{ compiler ? "ghc865", dev ? true # Include tools useful for development? | ||
}: | ||
let | ||
pinnedPkgs = | ||
# 2020-05-17T11:55:13+02:00 | ||
let rev = "85d6f3bcd9cbcc52c4a307d2ef5116dab4b41641"; | ||
in import (builtins.fetchTarball { | ||
name = "nixpkgs-${rev}"; | ||
url = "https://github.com/nixos/nixpkgs/archive/${rev}.tar.gz"; | ||
sha256 = "10jxpwq47clbj047jh5zn20hqmwpc821ac8zljgpayk0sk5p0mwv"; | ||
}) { }; | ||
in { pkgs ? pinnedPkgs, compiler ? "ghc865" }: | ||
pkgs.mkShell { | ||
buildInputs = [ | ||
pkgs.haskell.packages."${compiler}".ghc | ||
pkgs.cabal-install | ||
pkgs.ormolu | ||
pkgs.hlint | ||
]; | ||
} | ||
pkgs = let rev = "7d75a77954aaa61fa0b2931b354b61cc0aa4a60a"; | ||
in import (builtins.fetchTarball { | ||
name = "nixpkgs-${rev}"; | ||
url = "https://github.com/NixOS/nixpkgs/archive/${rev}.tar.gz"; | ||
sha256 = "0qf3rp0jqgipjzz51ws8nwxiaiglz9jf7hc6d7x75ddbp0wlfjmg"; | ||
}) { overlays = [ (self: super: { llvm_39 = super.llvm_5; }) ]; }; | ||
|
||
old-ghc-nix = let rev = "674d459c7376af6641c94e91cd7d36214661b481"; | ||
in import (builtins.fetchTarball { | ||
name = "old-ghc-nix-${rev}"; | ||
url = "https://github.com/mpickering/old-ghc-nix/archive/${rev}.tar.gz"; | ||
sha256 = "1w3d5dm9v7ms1s7xp39wncgwvh3kclp2bkdqa2kxn3x5dyayfm61"; | ||
}) { inherit pkgs; }; | ||
|
||
in pkgs.mkShell { | ||
buildInputs = | ||
[ old-ghc-nix."${compiler}" pkgs.cabal-install pkgs.ormolu pkgs.hlint ] | ||
++ (if dev then [ pkgs.ghcid ] else [ ]); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,28 @@ | ||
{-# LANGUAGE OverloadedStrings #-} | ||
|
||
-- | | ||
-- Module: Kesha | ||
-- Copyright: (c) 2020 Jordan Mackie | ||
-- License: MIT | ||
-- Maintainer: Jordan Mackie <[email protected]> | ||
-- Stability: experimental | ||
-- Portability: portable | ||
-- | ||
-- An implementation of @<https://nixos.org/guides/nix-pills/nix-store-paths.html#idm140737319621872 nix-hash>@. | ||
module Kesha | ||
( hash, | ||
hashWith, | ||
Opts (..), | ||
defaultOpts, | ||
HashOptions (..), | ||
defaultHashOptions, | ||
HashAlgo (..), | ||
HashRepr (..), | ||
) | ||
where | ||
|
||
import qualified Crypto.Hash.MD5 as MD5 | ||
import qualified Crypto.Hash.SHA1 as SHA1 | ||
import qualified Crypto.Hash.SHA256 as SHA256 | ||
import Data.Bits ((.&.), (.|.), shiftL, shiftR) | ||
import Data.Bits (shiftL, shiftR, (.&.), (.|.)) | ||
import qualified Data.ByteString as BS | ||
import qualified Data.ByteString.Char8 as ASCII | ||
import qualified Data.Char as Char | ||
|
@@ -22,87 +33,121 @@ import qualified Kesha.NAR as NAR | |
import Prelude hiding ((!!)) | ||
|
||
-- | | ||
-- Compute the cryptographic hash of a path using the 'defaultOpts'. | ||
-- Compute the cryptographic hash of a path using the 'defaultHashOptions'. | ||
-- | ||
-- The output of @'hash' path@ should be consistent with that of | ||
-- @nix-hash --type sha256 --base32 path@ (invoked at the command line). | ||
hash :: FilePath -> IO (Either String BS.ByteString) | ||
hash = hashWith defaultOpts | ||
-- @nix-hash --type sha256 --base32 path@. | ||
hash :: FilePath -> IO (Either NAR.PackError BS.ByteString) | ||
hash = hashWith defaultHashOptions | ||
|
||
-- | | ||
-- Compute the cryptographic hash of a path using the given 'Opts'. | ||
hashWith :: Opts -> FilePath -> IO (Either String BS.ByteString) | ||
-- Compute the cryptographic hash of a path using the given 'HashOptions'. | ||
hashWith :: HashOptions -> FilePath -> IO (Either NAR.PackError BS.ByteString) | ||
hashWith opts path = | ||
fmap (printNar (hashAlgo opts) (hashRepr opts)) <$> NAR.localPack path | ||
|
||
-- | | ||
-- Hashing options. | ||
data Opts | ||
= Opts | ||
{ -- | cryptographic hash algorithm to use | ||
hashAlgo :: HashAlgo, | ||
-- | how to print the hash | ||
hashRepr :: HashRepr | ||
} | ||
data HashOptions = HashOptions | ||
{ -- | cryptographic hash algorithm to use | ||
hashAlgo :: HashAlgo, | ||
-- | how to print the hash | ||
hashRepr :: HashRepr | ||
} | ||
|
||
-- | | ||
-- Default hashing options. | ||
-- | ||
-- These are the default options used by most of the Nix tooling (e.g. | ||
-- @nix-prefetch-git@). | ||
defaultOpts :: Opts | ||
defaultOpts = Opts SHA256 Base32 | ||
defaultHashOptions :: HashOptions | ||
defaultHashOptions = HashOptions SHA256 Base32 | ||
|
||
-- | | ||
-- Available hash algorithms. | ||
data HashAlgo | ||
= SHA256 | ||
= MD5 | ||
| SHA1 | ||
| SHA256 | ||
|
||
-- | | ||
-- Printable hash representations. | ||
data HashRepr | ||
= Base32 | ||
= Base16 | ||
| Base32 | ||
|
||
printNar :: HashAlgo -> HashRepr -> NAR.NAR -> BS.ByteString | ||
printNar SHA256 Base32 = | ||
printNar algo repr = | ||
ASCII.map Char.toLower | ||
. printHash32 SHA256 | ||
. SHA256.hash | ||
. ( case repr of | ||
Base16 -> printHash16 algo | ||
Base32 -> printHash32 algo | ||
) | ||
. ( case algo of | ||
MD5 -> MD5.hash | ||
SHA1 -> SHA1.hash | ||
SHA256 -> SHA256.hash | ||
) | ||
. NAR.dump | ||
|
||
-- https://github.com/NixOS/nix/blob/master/src/libutil/hash.cc | ||
printHash16 :: HashAlgo -> BS.ByteString -> BS.ByteString | ||
printHash16 algo rawHash = | ||
ASCII.pack $ | ||
foldMap | ||
( \i -> | ||
[ base16Chars !! fromIntegral (BS.index rawHash i `shiftR` 4), | ||
base16Chars !! fromIntegral (BS.index rawHash i .&. 15) | ||
] | ||
) | ||
[0 .. hashSize - 1] | ||
where | ||
hashSize :: Int | ||
hashSize = hashSizeForAlgo algo | ||
|
||
base16Chars :: Seq.Seq Char | ||
base16Chars = "0123456789abcdef" | ||
|
||
-- https://github.com/NixOS/nix/blob/master/src/libutil/hash.cc | ||
printHash32 :: HashAlgo -> BS.ByteString -> BS.ByteString | ||
printHash32 algo rawHash = go (len - 1) "" | ||
where | ||
hashSize :: Int | ||
hashSize = hashSizeForAlgo algo | ||
|
||
-- omitted: E O U T | ||
base32Chars :: Seq.Seq Char | ||
base32Chars = Seq.fromList "0123456789abcdfghijklmnpqrsvwxyz" | ||
|
||
len :: Int | ||
len = (hashSize * 8 - 1) `div` 5 + 1 | ||
|
||
go :: Int -> BS.ByteString -> BS.ByteString | ||
go n accum | ||
| n < 0 = accum | ||
| otherwise = | ||
go (pred n) $ | ||
ASCII.snoc accum (base32Chars !! (fromIntegral c .&. 0x1f)) | ||
where | ||
b , i, j :: Int | ||
b, i, j :: Int | ||
b = n * 5 | ||
i = b `div` 8 | ||
j = b `mod` 8 | ||
c :: Word8 | ||
c = | ||
((bytes !! i) `shiftR` j) | ||
.|. (if i >= (hashSize - 1) then 0 else (bytes !! (i + 1)) `shiftL` (8 - j)) | ||
|
||
bytes :: Seq.Seq Word8 | ||
bytes = Seq.fromList (BS.unpack rawHash) | ||
(!!) :: Seq.Seq a -> Int -> a | ||
(!!) xs i = fromJust (Seq.lookup i xs) | ||
infixl 9 !! | ||
|
||
-- https://github.com/NixOS/nix/blob/master/src/libutil/hash.hh | ||
hashSizeForAlgo :: HashAlgo -> Int | ||
hashSizeForAlgo MD5 = 16 | ||
hashSizeForAlgo SHA1 = 20 | ||
hashSizeForAlgo SHA256 = 32 | ||
|
||
-- omitted: E O U T | ||
base32Chars :: Seq.Seq Char | ||
base32Chars = Seq.fromList "0123456789abcdfghijklmnpqrsvwxyz" | ||
(!!) :: Seq.Seq a -> Int -> a | ||
(!!) xs i = fromJust (Seq.lookup i xs) | ||
|
||
infixl 9 !! |
Oops, something went wrong.