dhall-nixpkgs (empty) → 1.0.0
raw patch · 4 files changed
+1014/−0 lines, 4 filesdep +aesondep +basedep +data-fixsetup-changed
Dependencies added: aeson, base, data-fix, dhall, foldl, hnix, lens-family-core, megaparsec, mmorph, neat-interpolation, network-uri, optparse-applicative, prettyprinter, text, transformers, turtle
Files
- LICENSE +27/−0
- Main.hs +949/−0
- Setup.hs +2/−0
- dhall-nixpkgs.cabal +36/−0
+ LICENSE view
@@ -0,0 +1,27 @@+Copyright (c) 2020 Gabriel Gonzalez+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++1. Redistributions of source code must retain the above copyright notice, this+list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright notice,+this list of conditions and the following disclaimer in the documentation+and/or other materials provided with the distribution.++3. Neither the name of the author nor the names of its contributors may be+used to endorse or promote products derived from this software without+specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Main.hs view
@@ -0,0 +1,949 @@+{-# LANGUAGE ApplicativeDo #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE RecordWildCards #-}++{-| @dhall-to-nixpkgs@ is essentially the Dhall analog of @cabal2nix@.++ The following Nix code is required reading for understanding how+ @dhall-to-nixpkgs@ is supposed to work:++ <https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/interpreters/dhall/build-dhall-package.nix>++ The Nixpkgs support for Dhall builds packages without using Dhall's support+ for HTTP(S) imports. Instead, all remote packages are fetched by Nix+ (e.g. using @pkgs.fetchFromGitHub@) instead of being fetched by Dhall. In+ fact, the Nix builder enforces that Dhall builds are offline by compiling+ the @dhall@ executable with the @-f-with-http@ configure flag to disable+ support for remote imports and then using that executable to build Dhall+ packages.++ Nix is able to build Dhall packages with remote imports by requiring that+ remote imports are protected with semantic integrity checks and then+ replacing the remote imports with cache hits. So long as the correct+ dependency is added to `buildDhallPackage`'s `dependencies` argument then+ the builder will ensure that the dependency is present in the cache so that+ the remote import is elided.++ However, using `buildDhallPackage` to author a Dhall package is tedious,+ due to manual steps such as:++ * Discovering all of a Dhall package's remote dependencies and translating+ them to Nix code++ * For GitHub packages, computing the correct revision and hash++ @dhall-to-nixpkgs@ automates the generation of suitable Nix code, taking+ care of details such as these and more. Note that @dhall-to-nixpkgs@+ technically uses the higher-level @buildDhall{GitHub,Directory}Package@+ utilities instead of the lower-level @buildDhallPackage@ in order to+ simplify the generated code, but the general idea is still the same.++ The Nixpkgs support for Dhall makes a simplifying assumption of a+ "consistent package set", meaning that even though there may be multiple+ buildable versions for a Dhall package there will always be a preferred+ version for every package (analogous to how a Stackage resolver selects+ default package versions for Haskell). This means that the dependency list+ generated by @dhall-to-nixpkgs@ will not refer to specific versions or+ revisions of dependencies and will instead refer to the default preferred+ version.++ For example, if a package were to depend on both+ @https://prelude.dhall-lang.org/v10.0.0/package.dhall@ and+ @https://prelude.dhall-lang.org/v11.0.0/package.dhall@, @dhall-to-nixpkgs@+ would convert both of them to the same version-less @Prelude@ dependency+ (which would select the default Prelude version). That in turn would+ inevitably fail to satisfy one or the other dependency.++ This implies that @dhall-to-nixpkgs@ does not support packages that depend+ on multiple versions of the same dependency. We could technically support+ this, but for now the tool will discourage that. Users that really want to+ depend on a specific version of a dependency can still manually edit+ the code generated by @dhall-to-nixpkgs@ if they insist.+-}++module Main where++import Control.Applicative (empty, optional, (<|>))+import Control.Monad.IO.Class (MonadIO (..))+import Control.Monad.Morph (hoist)+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.State.Strict (StateT)+import Data.Aeson (FromJSON)+import Data.Fix (Fix)+import Data.List.NonEmpty (NonEmpty (..))+import Data.Text (Text)+import Data.Void (Void)+import Dhall.Import (Status (..), stack)+import Dhall.Parser (Src)+import GHC.Generics (Generic)+import Lens.Family.State.Strict (zoom)+import Network.URI (URI (..), URIAuth (..))+import Nix.Expr.Shorthands ((@.), (@@))+import Nix.Expr.Types (NExpr)+import Options.Applicative (Parser, ParserInfo)+import Prelude hiding (FilePath)+import System.Exit (ExitCode (..))+import Text.Megaparsec (Parsec)+import Turtle (FilePath, Shell, fp, (</>))++import Dhall.Core+ ( Expr (..)+ , File (..)+ , Import (..)+ , ImportHashed (..)+ , ImportMode (..)+ , ImportType (..)+ , URL (..)+ )++import qualified Control.Foldl as Foldl+import qualified Control.Monad.Trans.State.Strict as State+import qualified Data.Aeson as Aeson+import qualified Data.Foldable as Foldable+import qualified Data.List.NonEmpty as NonEmpty+import qualified Data.Text as Text+import qualified Data.Text.Encoding as Text.Encoding+import qualified Data.Text.IO as Text.IO+import qualified Data.Text.Prettyprint.Doc.Render.Text as Prettyprint.Text+import qualified Dhall.Core+import qualified Dhall.Import+import qualified Dhall.Optics+import qualified Dhall.Parser+import qualified GHC.IO.Encoding+import qualified NeatInterpolation+import qualified Network.URI as URI+import qualified Nix.Expr.Shorthands as Nix+import qualified Nix.Pretty+import qualified Options.Applicative as Options+import qualified System.Exit+import qualified System.IO+import qualified Text.Megaparsec as Megaparsec+import qualified Text.Megaparsec.Char as Megaparsec.Char+import qualified Text.Megaparsec.Char.Lexer as Megaparsec.Char.Lexer+import qualified Turtle++data Options+ = OptionsGitHub GitHub+ | OptionsDirectory Directory++data GitHub = GitHub+ { name :: Maybe Text+ , uri :: Text+ , rev :: Maybe Text+ , hash :: Maybe Text+ , fetchSubmodules :: Bool+ , directory :: FilePath+ , file :: FilePath+ , source :: Bool+ }++data Directory = Directory+ { name :: Maybe Text+ , directory :: FilePath+ , file :: FilePath+ , source :: Bool+ }++data NixPrefetchGit = NixPrefetchGit+ { url :: Text+ , rev :: Text+ , path :: Text+ , sha256 :: Text+ }+ deriving stock (Generic)+ deriving anyclass (FromJSON)++parseOptions :: Parser Options+parseOptions =+ subcommand+ "github"+ "Create a Nix package from a GitHub repository"+ (fmap OptionsGitHub parseGitHub)+ <|> subcommand+ "directory"+ "Create a Nix package from a local directory"+ (fmap OptionsDirectory parseDirectory)+ where+ subcommand name description parser =+ Options.hsubparser+ ( Options.command name parserInfo+ <> Options.metavar name+ )+ where+ parserInfo =+ Options.info parser+ ( Options.fullDesc+ <> Options.progDesc description+ )++parseFile :: Parser FilePath+parseFile =+ Options.strOption+ ( Options.long "file"+ <> Options.help "File to import, relative to the top-level directory"+ <> Options.value "package.dhall"+ )++parseSource :: Parser Bool+parseSource =+ Options.switch+ ( Options.long "source"+ <> Options.help "Configure the Nix package to include source code"+ )++parseName :: Parser (Maybe Text)+parseName =+ optional+ (Options.strOption+ ( Options.long "name"+ <> Options.help "Name for the Nix derivation"+ )+ )++parseGitHub :: Parser GitHub+parseGitHub = do+ name <- parseName++ uri <- Options.strArgument (Options.metavar "URL")++ rev <- do+ optional+ (Options.strOption+ ( Options.long "rev"+ <> Options.help "Git revision to use"+ )+ )++ hash <- do+ optional+ (Options.strOption+ ( Options.long "hash"+ <> Options.help "Expected SHA256 hash"+ )+ )++ fetchSubmodules <-+ Options.switch+ ( Options.long "fetch-submodules"+ <> Options.help "Fetch git submodules"+ )++ directory <-+ Options.strOption+ ( Options.long "directory"+ <> Options.help "Subdirectory containing the Dhall package"+ <> Options.value ""+ )++ file <- parseFile++ source <- parseSource++ return GitHub{..}++parseDirectory :: Parser Directory+parseDirectory = do+ name <- parseName++ directory <- Options.strArgument (Options.metavar "DIRECTORY")++ file <- parseFile++ source <- parseSource++ return Directory{..}++parserInfoOptions :: ParserInfo Options+parserInfoOptions =+ Options.info+ (Options.helper <*> parseOptions)+ ( Options.progDesc "Convert a Dhall project to a Nix package"+ <> Options.fullDesc+ )++main :: IO ()+main = do+ GHC.IO.Encoding.setLocaleEncoding System.IO.utf8++ options <- Options.execParser parserInfoOptions++ case options of+ OptionsGitHub github -> githubToNixpkgs github+ OptionsDirectory directory -> directoryToNixpkgs directory++-- | Convenient utility for generating command-line options+toListWith :: (a -> [ Text ]) -> Maybe a -> [ Text ]+toListWith f (Just x ) = f x+toListWith _ Nothing = [ ]++nub :: Ord a => [a] -> [a]+nub = Foldl.fold Foldl.nub++{-| This specialization of `nub` is necessary to work around a type-checking+ loop with GHC 8.4+-}+nub'+ :: Ord (f (Fix f)) => [ (Text, Maybe (Fix f)) ] -> [ (Text, Maybe (Fix f)) ]+nub' = nub++{-| The Nixpkgs support for Dhall essentially replaces all remote imports with+ cache hits, but doing so implies that all remote imports must be protected+ by an integrity check.++ This function finds all remote imports that are transitive dependencies of+ the given expression, failing if any of them are missing integrity checks.+-}+findExternalDependencies :: Expr Src Import -> StateT Status Shell URL+findExternalDependencies expression = do+ -- This is a best-effort attempt to pick an import alternative if there is+ -- more than one+ let pickAlt (ImportAlt e0 e1)+ -- If only the latter import has an integrity check, then select+ -- that+ | Embed Import{ importHashed = ImportHashed{ hash = Nothing } } <- Dhall.Core.shallowDenote e0+ , Embed Import{ importHashed = ImportHashed{ hash = Just _ } } <- Dhall.Core.shallowDenote e1 =+ Just e1+ -- Otherwise prefer the first import+ | otherwise =+ Just e0+ pickAlt _ =+ Nothing++ let rewrittenExpression =+ Dhall.Optics.rewriteOf Dhall.Core.subExpressions pickAlt expression++ import_ <- lift (Turtle.select (Foldable.toList rewrittenExpression))++ parent :| _ <- zoom stack State.get++ child <- hoist liftIO (Dhall.Import.chainImport parent import_)++ let Import{ importHashed, importMode } = Dhall.Import.chainedImport child++ let ImportHashed{ hash, importType } = importHashed++ case importMode of+ Code -> return ()+ RawText -> return ()+ Location -> empty -- "as Location" imports aren't real dependencies++ case importType of+ Missing ->+ empty++ Env {} ->+ empty++ Remote url ->+ case hash of+ Just _ ->+ return url+ Nothing ->+ die (MissingSemanticIntegrityCheck url)++ Local filePrefix file -> do+ filepath <- liftIO (Dhall.Import.localToPath filePrefix file)++ expressionText <- liftIO (Text.IO.readFile filepath)++ parsedExpression <- Dhall.Core.throws (Dhall.Parser.exprFromText filepath expressionText)++ zoom stack (State.modify (NonEmpty.cons child))++ findExternalDependencies parsedExpression++data Dependency = Dependency+ { functionParameter :: (Text, Maybe NExpr)+ -- ^ Function parameter used to bring the dependency into scope for the+ -- Nix package. The @`Maybe` `NExpr`@ is always `Nothing`, but we+ -- include it here for convenience+ , dependencyExpression :: NExpr+ -- ^ The dependency expression to include in the dependency list. This+ -- will be an expression of the form:+ --+ -- > someDependency.override { file = "./someFile.dhall" }+ }++{-| The Nixpkgs support for Dhall implements two conventions that+ @dhall-to-nixpkgs@ depends on:++ * Packages are named after their repository name+ * You can import a specific file using `packageName.override { file = …; }`++ This function is responsible for converting Dhall imports to package+ names and files that follow this convention. For example, given a Dhall+ import like:++ https://raw.githubusercontent.com/EarnestResearch/dhall-packages/master/kubernetes/k8s/1.14.dhall++ ... this will create the corresponding Nix dependency of the form:++ dhall-packages.override { file = "kubernetes/k8s/1.14.dhall"; }+-}+dependencyToNix :: URL -> IO Dependency+dependencyToNix url@URL{ authority, path } = do+ let prelude = "Prelude"++ case authority of+ "raw.githubusercontent.com" -> do+ let File{ directory, file } = path++ let Dhall.Core.Directory{ components } = directory++ case reverse (file : components) of+ -- Special case to recognize a Prelude import and treat it as if+ -- it were an import of prelude.dhall-lang.org+ "dhall-lang" : "dhall-lang" : _rev : "Prelude" : rest -> do+ let fileArgument = Text.intercalate "/" rest++ let functionParameter = (prelude, Nothing)++ let dependencyExpression =+ (Nix.mkSym prelude @. "override")+ @@ Nix.attrsE+ [ ("file", Nix.mkStr fileArgument ) ]++ return Dependency{..}++ _owner : repo : _rev : rest -> do+ let fileArgument = Text.intercalate "/" rest++ let functionParameter = (repo, Nothing)++ let dependencyExpression =+ (Nix.mkSym repo @. "override")+ @@ Nix.attrsE+ [ ("file", Nix.mkStr fileArgument ) ]++ return Dependency{..}++ _ -> do+ die (NotAValidGitHubRepositoryURL url)++ "prelude.dhall-lang.org" -> do+ let File{ directory, file } = path++ let Dhall.Core.Directory{ components } = directory++ let component :: Parsec Void Text Integer+ component = Megaparsec.Char.Lexer.decimal++ let version :: Parsec Void Text ()+ version = do+ _ <- Megaparsec.Char.char 'v'+ _ <- component+ _ <- Megaparsec.Char.char '.'+ _ <- component+ _ <- Megaparsec.Char.char '.'+ _ <- component+ return ()++ let pathComponents =+ case reverse (file : components) of+ first : rest+ -- Ignore the version. The Nixpkgs support assumes+ -- a curated set of package versions.+ | Just _ <- Megaparsec.parseMaybe version first ->+ rest+ rest ->+ rest+ + let fileArgument = Text.intercalate "/" pathComponents++ let functionParameter = (prelude, Nothing)++ let dependencyExpression =+ (Nix.mkSym prelude @. "override")+ @@ Nix.attrsE++ [ ("file", Nix.mkStr fileArgument) ]++ return Dependency{..}+ _ -> do+ die (UnsupportedDomainDependency url authority)++githubToNixpkgs :: GitHub -> IO ()+githubToNixpkgs GitHub{ name, uri, rev = maybeRev, hash, fetchSubmodules, directory, file, source } = do+ URI{ uriScheme, uriAuthority = Just URIAuth{ uriUserInfo, uriRegName, uriPort }, uriPath, uriQuery, uriFragment } <- do+ case URI.parseAbsoluteURI (Text.unpack uri) of+ Nothing -> die (RepositoryIsNotAValidURI uri)+ Just u -> return u++ case uriScheme of+ "https:" -> return ()+ _ -> die (UnsupportedURIScheme uri uriScheme)++ case uriRegName of+ "github.com" -> return ()+ _ -> die (UnsupportedDomain uri uriRegName)++ case uriPort of+ "" -> return ()+ _ -> die (UnsupportedPort uri uriPort)++ case uriQuery of+ "" -> return ()+ _ -> die (UnsupportedQuery uri uriQuery)++ case uriFragment of+ "" -> return ()+ _ -> die (UnsupportedFragment uri uriFragment)++ let githubBase = Text.pack (uriUserInfo <> uriRegName <> uriPort)++ let parsePath :: Parsec Void String (Text, Text)+ parsePath = do+ _ <- Megaparsec.Char.char '/'++ owner <- Megaparsec.takeWhile1P Nothing (/= '/')++ _ <- Megaparsec.Char.char '/'++ repo <- Megaparsec.takeWhile1P Nothing (/= '.')++ optional (Megaparsec.Char.string ".git")++ return (Text.pack owner, Text.pack repo)++ (owner, repo) <- case Megaparsec.parseMaybe parsePath uriPath of+ Nothing -> die (NotAValidGitHubRepository uri)++ Just (owner, repo) -> do+ return (owner, repo)++ let baseUrl =+ Text.pack uriScheme <> "//" <> githubBase <> "/" <> owner <> "/" <> repo++ (rev, sha256, repository) <- case maybeRev of+ Just r | not fetchSubmodules -> do+ let archiveURL = baseUrl <> "/archive/" <> r <> ".tar.gz"++ let args = [ "--unpack"+ , "--type", "sha256"+ , "--print-path"+ , "--name", repo+ , archiveURL+ ]+ <> toListWith (\t -> [ t ]) hash++ (exitCode, text) <- Turtle.procStrict "nix-prefetch-url" args empty++ case exitCode of+ ExitSuccess -> return ()+ ExitFailure _ -> die (FailedToFetchGitHubArchive args)++ case Text.lines text of+ [ sha256, path ] -> return (r, sha256, Turtle.fromText path)+ _ -> die (FailedToParseNixPrefetchURL args)++ _ -> do+ let args = [ "--url", baseUrl <> ".git"+ , "--quiet"+ ]+ <> toListWith (\t -> [ "--rev", t ]) maybeRev+ <> toListWith (\t -> [ "--hash", t ]) hash+ <> (if fetchSubmodules then [ "--fetch-submodules" ] else [])++ (exitCode, text) <- Turtle.procStrict "nix-prefetch-git" args empty++ case exitCode of+ ExitSuccess -> return ()+ ExitFailure _ -> die (FailedToCloneRepository args)++ let bytes = Text.Encoding.encodeUtf8 text++ NixPrefetchGit{ rev, sha256, path } <- case Aeson.eitherDecodeStrict' bytes of+ Left message -> do+ die (FailedToParseNixPrefetchGit args message)+ Right n -> do+ return n++ return (rev, sha256, Turtle.fromText path)++ let finalName =+ case name of+ Nothing -> repo+ Just n -> n++ let expressionFile = repository </> directory </> file++ let baseDirectory = Turtle.directory expressionFile++ let baseDirectoryString = Turtle.encodeString baseDirectory++ exists <- Turtle.testfile expressionFile++ if exists+ then return ()+ else die (MissingFile expressionFile)++ expressionText <- Turtle.readTextFile expressionFile++ expression <- Dhall.Core.throws (Dhall.Parser.exprFromText baseDirectoryString expressionText)++ let status = Dhall.Import.emptyStatus baseDirectoryString++ dependencies <- Turtle.reduce Foldl.nub (State.evalStateT (findExternalDependencies expression) status)++ nixDependencies <- traverse dependencyToNix dependencies++ let buildDhallGitHubPackage = "buildDhallGitHubPackage"++ let nixExpression =+ Nix.mkFunction+ (Nix.mkParamset+ ( [ (buildDhallGitHubPackage, Nothing) ]+ <> nub' (fmap functionParameter nixDependencies)+ )+ False+ )+ ( Nix.mkSym buildDhallGitHubPackage+ @@ Nix.attrsE+ [ ("name", Nix.mkStr finalName)+ , ("githubBase", Nix.mkStr githubBase)+ , ("owner", Nix.mkStr owner)+ , ("repo", Nix.mkStr repo)+ , ("rev", Nix.mkStr rev)+ , ("fetchSubmodules", Nix.mkBool fetchSubmodules)+ -- TODO: Support `private` / `varBase` options+ , ("sha256", Nix.mkStr sha256)+ , ("directory", Nix.mkStr (Turtle.format fp directory))+ , ("file", Nix.mkStr (Turtle.format fp file))+ , ("source", Nix.mkBool source)+ , ("dependencies", Nix.mkList (nub (fmap dependencyExpression nixDependencies)))+ ]+ )++ Prettyprint.Text.putDoc ((Nix.Pretty.prettyNix nixExpression) <> "\n")++directoryToNixpkgs :: Directory -> IO ()+directoryToNixpkgs Directory{ name, directory, file, source } = do+ let finalName =+ case name of+ Nothing -> Turtle.format fp (Turtle.dirname directory)+ Just n -> n++ let expressionFile = directory </> file++ exists <- Turtle.testfile expressionFile++ if exists+ then return ()+ else do+ die (MissingFile expressionFile)++ expressionText <- Turtle.readTextFile expressionFile++ let directoryString = Turtle.encodeString directory++ expression <- Dhall.Core.throws (Dhall.Parser.exprFromText directoryString expressionText)++ let status = Dhall.Import.emptyStatus directoryString++ dependencies <- Turtle.reduce Foldl.nub (State.evalStateT (findExternalDependencies expression) status)++ nixDependencies <- traverse dependencyToNix dependencies++ let buildDhallDirectoryPackage = "buildDhallDirectoryPackage"++ let src | null directoryString = directoryString+ | last directoryString == '/' = init directoryString+ | otherwise = directoryString++ let nixExpression =+ Nix.mkFunction+ (Nix.mkParamset+ ( [ (buildDhallDirectoryPackage, Nothing) ]+ <> nub' (fmap functionParameter nixDependencies)+ )+ False+ )+ ( Nix.mkSym buildDhallDirectoryPackage+ @@ Nix.attrsE+ [ ("name", Nix.mkStr finalName)+ , ("src", Nix.mkPath False src)+ , ("file", Nix.mkStr (Turtle.format fp file))+ , ("source", Nix.mkBool source)+ , ("dependencies", Nix.mkList (nub (fmap dependencyExpression nixDependencies)))+ ]+ )++ Prettyprint.Text.putDoc ((Nix.Pretty.prettyNix nixExpression) <> "\n")++die :: MonadIO io => Error -> io a+die e = liftIO $ do+ Text.IO.hPutStrLn System.IO.stderr (renderError e)++ System.Exit.exitFailure++data Error+ = MissingSemanticIntegrityCheck URL+ | NotAValidGitHubRepositoryURL URL+ | UnsupportedDomainDependency URL Text+ | RepositoryIsNotAValidURI Text+ | UnsupportedURIScheme Text String+ | UnsupportedDomain Text String+ | UnsupportedPort Text String+ | UnsupportedQuery Text String+ | UnsupportedFragment Text String+ | NotAValidGitHubRepository Text+ | FailedToFetchGitHubArchive [Text]+ | FailedToParseNixPrefetchURL [Text]+ | FailedToCloneRepository [Text]+ | FailedToParseNixPrefetchGit [Text] String+ | MissingFile FilePath+ +renderError :: Error -> Text+renderError e = case e of+ MissingSemanticIntegrityCheck url ->+ let dependency = Dhall.Core.pretty url++ in [NeatInterpolation.text|+Error: Dependency missing a semantic integrity check++Dhall's Nixpkgs requires that all of your remote dependencies are protected by+by semantic integrity checks. This ensures that Nix can replace the remote+imports with cached imports built by Nix instead of the imports being fetched+via HTTP requests using Dhall.++The following dependency is missing a semantic integrity check:++↳ $dependency+|]++ NotAValidGitHubRepositoryURL url ->+ let dependency = Dhall.Core.pretty url++ in [NeatInterpolation.text|+Error: Not a valid GitHub repository URL++Your Dhall package appears to depend on the following import:++↳ $dependency++... which is missing one or more path components that a raw GitHub import would+normally have. The URL should minimally have the following path components:++↳ https://raw.githubusercontent.com/$${owner}/$${repository}/$${revision}/…+|]++ UnsupportedDomainDependency url authority ->+ let dependency = Dhall.Core.pretty url++ in [NeatInterpolation.text|+Error: Unsupported domain++This tool currently only translates the following domains into Nix dependencies:++* raw.githubusercontent.com+* prelude.dhall-lang.org++One of the Dhall project's dependencies:++↳ $dependency++... used the following unexpected domain:++↳ $authority++If you would like to support a new domain for Dhall dependencies, please open an+issue here:++↳ https://github.com/dhall-lang/dhall-haskell/issues+|]++ RepositoryIsNotAValidURI uri ->+ [NeatInterpolation.text|+Error: The specified repository is not a valid URI++You provided the following argument:++↳ $uri++... which is not a valid URI+|]+ UnsupportedURIScheme uri uriScheme ->+ let uriSchemeText = Text.pack uriScheme++ in [NeatInterpolation.text|+Error: URI schemes other than https are not supported++You specified the following URI:++↳ $uri++... which has the following scheme:++↳ $uriSchemeText++... which is not https+|]++ UnsupportedDomain uri uriRegName ->+ let uriRegNameText = Text.pack uriRegName++ in [NeatInterpolation.text|+Error: Domains other than github.com are not supported++You specified the following URI:++↳ $uri++... which has the following domain:++↳ $uriRegNameText++... which is not github.com+|]++ UnsupportedPort uri uriPort ->+ let uriPortText = Text.pack uriPort++ in [NeatInterpolation.text|+Error: Non-default ports are not supported++You specified the following URI:++↳ $uri++... which has the following explicit port specification:++↳ $uriPortText++... which is not permitted by this tool+|]++ UnsupportedQuery uri uriQuery ->+ let uriQueryText = Text.pack uriQuery++ in [NeatInterpolation.text|+Error: Non-empty query strings are not supported++You specified the following URI:++↳ $uri++... which has the following query string:++↳ $uriQueryText++... which is not permitted by this tool+|]++ UnsupportedFragment uri uriFragment ->+ let uriFragmentText = Text.pack uriFragment++ in [NeatInterpolation.text|+Error: Non-empty query fragments are not supported++You specified the following URI:++↳ $uri++... which has the following query fragment:++↳ $uriFragmentText++... which is not permitted by this tool+|]++ NotAValidGitHubRepository uri ->+ [NeatInterpolation.text|+Error: Not a valid GitHub repository++You specified the following URI:++↳ $uri++... which is not a valid GitHub repository. A valid repository must match the+following format:++↳ https://github.com/$${owner}/$${repository}[.git]+|]++ FailedToFetchGitHubArchive args ->+ let argsText = Text.intercalate " " args++ in [NeatInterpolation.text|+Error: Failed to fetch the GitHub's repository archive++The following command failed to fetch the following archive for the repository:++ nix-prefetch-url $argsText+|]++ FailedToParseNixPrefetchURL args ->+ let argsText = Text.intercalate " " args++ in [NeatInterpolation.text|+Error: Failed to parse the nix-prefetch-url output++The following command:++ nix-prefetch-url $argsText++... should have produced two lines of output:++* First the SHA256 hash of the GitHub project+* Then the /nix/store/… path of the downloaded project++However, the output did not match, possibly indicating an internal error, either+with this tool or with nix-prefetch-url+|]++ FailedToCloneRepository args ->+ let argsText = Text.intercalate " " args++ in [NeatInterpolation.text|+Error: Failed to clone the GitHub repository++The following command failed to clone the repository:++ nix-prefetch-git $argsText+|]++ FailedToParseNixPrefetchGit args message ->+ let argsText = Text.intercalate " " args++ messageText = Text.pack message++ in [NeatInterpolation.text|+Error: Failed to parse the output of nix-prefetch-git++The following command:++ nix-prefetch-git $argsText++... should have produced a JSON output matching the following shape:++↳ { url : Text+ , rev : Text+ , path : Text+ , sha256 : Text+ , fetchSubmodules : Bool + }++... but JSON decoding failed with the following error:++↳ $messageText+|]++ MissingFile expressionFile ->+ let expressionFileText = Turtle.format fp expressionFile++ in [NeatInterpolation.text|+Error: Missing file++The following file does not exist:++↳ $expressionFileText++Perhaps you meant to specify a different file within the project using the+--file option?+|]
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ dhall-nixpkgs.cabal view
@@ -0,0 +1,36 @@+Cabal-Version: >=1.10+Name: dhall-nixpkgs+Version: 1.0.0+Synopsis: Convert Dhall projects to Nix packages+Description: This package provides a @dhall-to-nixpkgs@ executable that+ converts a Dhall project to a Nix expression that can be+ supplied to @pkgs.dhallPackages.callPackage@+Bug-Reports: https://github.com/dhall-lang/dhall-haskell/issues+License: BSD3+license-file: LICENSE+Author: Gabriel Gonzalez+Maintainer: Gabriel439@gmail.com+Copyright: 2020 Gabriel Gonzalez+Category: Nix+Build-Type: Simple++Executable dhall-to-nixpkgs+ Main-Is: Main.hs+ Build-Depends: base >= 4.11 && < 5+ , aeson >= 1.0.0.0 && < 1.5+ , data-fix+ , dhall >= 1.32.0 && < 1.35+ , foldl < 1.5+ , hnix >= 0.7 && < 0.10+ , lens-family-core >= 1.0.0 && < 2.2+ , megaparsec >= 7.0.0 && < 8.1+ , mmorph < 1.2+ , neat-interpolation < 0.6+ , optparse-applicative >= 0.14.0.0 && < 0.16+ , prettyprinter >= 1.5.1 && < 1.8+ , text >= 0.11.1.0 && < 1.3+ , transformers >= 0.2.0.0 && < 0.6+ , turtle < 1.6+ , network-uri < 2.8+ Default-Language: Haskell2010+ GHC-Options: -Wall -threaded