hackage-repo-tool 0.1.1.1 → 0.1.1.2
raw patch · 4 files changed
+137/−16 lines, 4 filesdep +microlensdep ~Cabaldep ~basedep ~bytestring
Dependencies added: microlens
Dependency ranges changed: Cabal, base, bytestring, filepath, hackage-security, network, optparse-applicative, tar, time, zlib
Files
- ChangeLog.md +4/−0
- README.md +98/−0
- hackage-repo-tool.cabal +25/−13
- src/Main.hs +10/−3
ChangeLog.md view
@@ -1,3 +1,7 @@+0.1.1.2+-------+* Compat release for `hackage-security-0.6`+ 0.1.1.1 ------- * Make `hackage-repo-tool` buildable on Windows (#175)
+ README.md view
@@ -0,0 +1,98 @@+# `hackage-repo-tool`: Manage secure file-based package repositories++**Please refer to the [package description](https://hackage.haskell.org/package/hackage-repo-tool#description) for an overview of `hackage-repo-tool`, TUF and `hackage-security`.**++## Setting up a secure file-based repo++A file-based repository (as opposed to one running the actual [Hackage software](https://hackage.haskell.org/package/hackage-server)) is much easier to set up and will suffice for many purposes. Note that such a local file-based package repository can be turned into a remotely accessible secure package repository by any HTTP server supporting static file serving such as Nginx or Apache httpd.++1. Create a directory `~/my-secure-repo` containing a single+ subdirectory `~/my-secure-repo/package`. Put whatever packages you+ want to make available from your repo in this subdirectory. At this+ point your repository might look like++ ~/my-secure-repo/package/basic-sop-0.1.0.5.tar.gz+ ~/my-secure-repo/package/generics-sop-0.1.1.1.tar.gz+ ~/my-secure-repo/package/generics-sop-0.1.1.2.tar.gz+ ~/my-secure-repo/package/json-sop-0.1.0.4.tar.gz+ ~/my-secure-repo/package/lens-sop-0.1.0.2.tar.gz+ ~/my-secure-repo/package/pretty-sop-0.1.0.1.tar.gz+ ~/my-secure-repo/package/HsYAML-0.2.1.0.tar.gz++ *(Due to [#174](https://github.com/haskell/hackage-security/issues/174) this folder must contain at least one package tarball or `hackage-repo-tool` will fail in non-obvious ways)*++ Note the flat directory structure: different packages and+ different versions of those packages all live in the one+ directory.++2. Create public and private keys:++ # hackage-repo-tool create-keys \+ --keys ~/my-private-keys++ This will create a directory structure such as++ ~/my-private-keys/mirrors/id01.private+ ~/my-private-keys/mirrors/..+ ~/my-private-keys/root/id04.private+ ~/my-private-keys/root/..+ ~/my-private-keys/snapshot/id07.private+ ~/my-private-keys/target/id08.private+ ~/my-private-keys/target/..+ ~/my-private-keys/timestamp/id11.private++ containing keys for all the various TUF roles.++ Note that these keys are stored outside of the repository proper.++3. Create the initial TUF metadata and construct an index using++ # hackage-repo-tool bootstrap \+ --repo ~/my-secure-repo \+ --keys ~/my-private-keys++ This will create a directory `~/my-secure-repo/index` containing the+ `.cabal` files (extracted from the package tarballs) and TUF+ metadata for all packages++ ~/my-secure-repo/index/basic-sop/0.1.0.5/basic-sop.cabal+ ~/my-secure-repo/index/basic-sop/0.1.0.5/package.json+ ~/my-secure-repo/index/generics-sop/0.1.1.1/generics-sop.cabal+ ~/my-secure-repo/index/generics-sop/0.1.1.1/package.json+ ...++ and package the contents of that directory up as the index tarball+ `~/my-secure-repo/00-index.tar.gz`; it will also create the+ top-level metadata files++ ~/my-secure-repo/mirrors.json+ ~/my-secure-repo/root.json+ ~/my-secure-repo/snapshot.json+ ~/my-secure-repo/timestamp.json++4. The timestamp and snapshot are valid for three days, so you will+ need to resign these files regularly using++ # hackage-repo-tool update \+ --repo ~/my-secure-repo \+ --keys ~/my-private-keys++ You can use the same command whenever you add any additional+ packages to your repository.++5. If you now make this directory available (for instance, by pointing+ Apache httpd at it) you'll be able to use `cabal` to access it remotely by+ defining an appropriate [`repository` stanza](https://www.haskell.org/cabal/users-guide/installing-packages.html#using-secure-repositories):++ repository my-secure-repo+ url: http://packages.example.org/+ secure: True+ root-keys: 2ae741f4c4a5f70ed6e6c48762e0d7a493d8dd265e9cbc6c4037dfc7ceaec70e+ 32d3db5b4403935c0baf52a2bcb05031784a971ee2d43587288776f2e90609db+ eed36d2bb15f94628221cde558e99c4e1ad36fd243fe3748e1ee7ad00eb9d628+ key-threshold: 2++ Note that the keys in example above must be replaced: You need to+ copy the root key IDs from your generated `root.json` file (or you+ can set `key-threshold` to 0 if you're aware of the security+ implications)
hackage-repo-tool.cabal view
@@ -1,8 +1,9 @@ cabal-version: 1.12 name: hackage-repo-tool-version: 0.1.1.1+version: 0.1.1.2 -synopsis: Utility to manage secure file-based package repositories+build-type: Simple+synopsis: Manage secure file-based package repositories description: This utility can be used to manage secure file-based package repositories (creating [TUF](https://theupdateframework.github.io/) metadata as well as a Hackage index tarball) which can be used by@@ -20,10 +21,11 @@ category: Distribution homepage: https://github.com/haskell/hackage-security bug-reports: https://github.com/haskell/hackage-security/issues-build-type: Simple+tested-with: GHC==8.8.1, GHC==8.6.5, GHC==8.4.4, GHC==8.2.2, GHC==8.0.2,+ GHC==7.10.3, GHC==7.8.4, GHC==7.6.3, GHC==7.4.2 extra-source-files:- ChangeLog.md+ ChangeLog.md README.md source-repository head type: git@@ -47,16 +49,16 @@ Hackage.Security.RepoTool.Util.IO Prelude - build-depends: base >= 4.5 && < 5,- Cabal >= 1.14 && < 2.4,+ -- For boot libraries we try to accomodate the versions bundled with+ -- the respective GHC release+ build-depends: base >= 4.5 && < 4.14, bytestring >= 0.9 && < 0.11,+ Cabal >= 1.14 && < 1.26+ || >= 2.0 && < 2.6+ || >= 3.0 && < 3.2, directory >= 1.1 && < 1.4,- filepath >= 1.2 && < 1.5,- optparse-applicative >= 0.11 && < 0.15,- tar >= 0.4 && < 0.6,- time >= 1.2 && < 1.9,- zlib >= 0.5 && < 0.7,- hackage-security >= 0.5 && < 0.6+ filepath >= 1.3 && < 1.5,+ time >= 1.4 && < 1.10 if !os(windows) build-depends: unix >= 2.5 && < 2.8 @@ -66,12 +68,22 @@ else build-depends: directory >= 1.2 + -- For non-boot libraries we try to support single major versions+ -- to reduce the risk of semantic differences+ build-depends: microlens >= 0.4.11.2 && < 0.5,+ optparse-applicative >= 0.15.1 && < 0.16,+ tar >= 0.5 && < 0.6,+ zlib >= 0.6 && < 0.7,+ hackage-security >= 0.6 && < 0.7+ -- see comments in hackage-security.cabal if flag(use-network-uri) build-depends: network-uri >= 2.6 && < 2.7,- network >= 2.6 && < 2.7+ network >= 2.6 && < 2.9+ || >= 3.0 && < 3.2 else build-depends: network >= 2.5 && < 2.6+ default-language: Haskell2010 default-extensions: DeriveDataTypeable
src/Main.hs view
@@ -10,11 +10,13 @@ import Network.URI (URI) import System.Exit import qualified Data.ByteString.Lazy as BS.L+import qualified Lens.Micro as Lens import qualified System.FilePath as FilePath #ifndef mingw32_HOST_OS import System.IO.Error (isAlreadyExistsError) #endif+import System.IO.Error (isDoesNotExistError) -- Cabal import Distribution.Package@@ -23,12 +25,10 @@ -- hackage-security import Hackage.Security.Server import Hackage.Security.Util.Some-import Hackage.Security.Util.IO import Hackage.Security.Util.Path import Hackage.Security.Util.Pretty import qualified Hackage.Security.Key.Env as KeyEnv import qualified Hackage.Security.TUF.FileMap as FileMap-import qualified Hackage.Security.Util.Lens as Lens import Text.JSON.Canonical (JSValue) -- hackage-repo-tool@@ -494,7 +494,7 @@ -- version number) we don't overwrite it and return Nothing; otherwise we -- increment the version number, write the file, and (if it's in the index) -- copy it to the unpacked index directory.-updateFile :: forall a. (ToJSON WriteJSON (Signed a), HasHeader a)+updateFile :: forall a. (ToJSON WriteJSON a, HasHeader a) => GlobalOpts -> RepoLoc -> WhenWrite@@ -678,3 +678,10 @@ throwErrors :: Exception e => Either e a -> IO a throwErrors (Left err) = throwIO err throwErrors (Right a) = return a+++handleDoesNotExist :: IO a -> IO (Maybe a)+handleDoesNotExist act = handle aux (Just <$> act)+ where+ aux e | isDoesNotExistError e = return Nothing+ | otherwise = throwIO e