on master help sign in
selftest: Add test for legacy git thunks
commitbef18e4023ec1796cf617ba21c044bda68939132 authorElliot Cameron <eacameron@gmail.com> dateFri 27 Mar 2020, 16:14 (7 years ago) parentba1ddd1
3 files changed+143−10
default.nix+1 lib/selftest/obelisk-selftest.cabal+2 lib/selftest/src/Obelisk/SelfTest.hs+140−10
default.nix+1
@@ -82,6 +82,7 @@ let
8282 pkgs.coreutils
8383 pkgs.git
8484 pkgs.nix
85+ pkgs.nix-prefetch-git
8586 pkgs.rsync
8687 ];
8788 };
lib/selftest/obelisk-selftest.cabal+2
@@ -9,6 +9,8 @@ library
99 hs-source-dirs: src
1010 build-depends:
1111 base
12+ , bytestring
13+ , aeson
1214 , containers
1315 , directory
1416 , filepath
lib/selftest/src/Obelisk/SelfTest.hs+140−10
@@ -13,13 +13,19 @@ import Control.Concurrent (threadDelay)
1313import Control.Exception (bracket, throw, try)
1414import Control.Monad
1515import Control.Monad.IO.Class
16+import qualified Data.Aeson as Aeson
17+import qualified Data.Aeson.Types as Aeson
1618import Data.Bool (bool)
19+import qualified Data.ByteString.Lazy as LBS
20+import Data.Foldable (for_)
1721import Data.Function (fix)
22+import qualified Data.Map as Map
1823import Data.Semigroup ((<>))
1924import qualified Data.Set as Set
2025import Data.String
2126import Data.Text (Text)
2227import qualified Data.Text as T
28+import qualified Data.Text.Encoding as T
2329import qualified Data.Text.IO as T
2430import qualified Network.HTTP.Client as HTTP
2531import qualified Network.HTTP.Types as HTTP
@@ -67,6 +73,9 @@ whoamiPath = $(staticWhich "whoami")
6773nixBuildPath :: FilePath
6874nixBuildPath = $(staticWhich "nix-build")
6975
76+nixPrefetchGitPath :: FilePath
77+nixPrefetchGitPath = $(staticWhich "nix-prefetch-git")
78+
7079lnPath :: FilePath
7180lnPath = $(staticWhich "ln")
7281
@@ -103,6 +112,17 @@ ob = "ob"
103112augmentWithVerbosity :: (String -> [Text] -> a) -> String -> Bool -> [Text] -> a
104113augmentWithVerbosity runner executable isVerbose args = runner executable $ (if isVerbose then ("-v" :) else id) args
105114
115+isolatedGitShell :: Bool -> FilePath -> (([Text] -> Sh Text) -> Sh a) -> IO a
116+isolatedGitShell isVerbose dir f = shelly $ bool silently verbosely isVerbose $ do
117+ setenv "HOME" "/dev/null"
118+ setenv "GIT_CONFIG_NOSYSTEM" "1"
119+ _ <- git ["init"]
120+ _ <- git ["config", "user.name", "SelfTest"]
121+ _ <- git ["config", "user.email", "self@test"]
122+ f git
123+ where
124+ git args = run gitPath $ ["-C", toTextIgnore dir] <> args
125+
106126-- | Copies a git repo to a new location and "resets" the git history to include
107127-- exactly one commit with all files added. It then restricts writing and reading
108128-- for group and user to make the repo ideal for being a valid git remote for thunks.
@@ -110,21 +130,14 @@ augmentWithVerbosity runner executable isVerbose args = runner executable $ (if
110130-- Using this allows dirty repos to be used as git remotes during the test since 'git clone'ing
111131-- a dirty repo will not include the uncommitted changes.
112132copyForGitRemote :: Bool -> FilePath -> FilePath -> IO ()
113−copyForGitRemote isVerbose origDir copyDir = shelly $ bool silently verbosely isVerbose $ do
114− setenv "HOME" "/dev/null"
115− setenv "GIT_CONFIG_NOSYSTEM" "1"
133+copyForGitRemote isVerbose origDir copyDir = isolatedGitShell isVerbose copyDir $ \git -> do
116134 run_ rsyncPath
117135 [ "-r", "--no-perms", "--no-owner", "--no-group", "--exclude", ".git"
118136 , toTextIgnore (addTrailingPathSeparator origDir), toTextIgnore copyDir
119137 ]
120− git ["init"]
121− git ["config", "user.name", "SelfTest"]
122− git ["config", "user.email", "self@test"]
123− git ["add", "--all"]
124− git ["commit", "-m", "Copy repo"]
138+ _ <- git ["add", "--all"]
139+ _ <- git ["commit", "-m", "Copy repo"]
125140 run_ chmodPath ["-R", "u-w,g-rw,o-rw", toTextIgnore copyDir] -- Freeze this state
126− where
127− git args = run_ gitPath $ ["-C", toTextIgnore copyDir] <> args
128141
129142main :: IO ()
130143main = do
@@ -265,6 +278,8 @@ main' isVerbose httpManager obeliskRepoReadOnly = withInitCache $ \initCache ->
265278 it "aborts thunk pack when there are uncommitted files" $ inTmpObInitWithImplCopy $ \dir -> do
266279 testThunkPack' (dir </> thunk)
267280
281+ it "works on legacy git thunks" $ testLegacyGitThunks isVerbose
282+
268283 describe "ob thunk update --branch" $ parallel $ do
269284 it "can change a thunk to the latest version of a desired branch" $ withTmp $ \dir -> do
270285 let branch1 = "master"
@@ -461,5 +476,120 @@ getFreePorts n = Socket.withSocketsDo $ do
461476 Socket.bind sock (Socket.addrAddress addr)
462477 pure sock
463478
479+testLegacyGitThunks :: Bool -> IO ()
480+testLegacyGitThunks isVerbose = withSystemTempDirectory "test-git-repo" $ \gitDir -> do
481+ isolatedGitShell isVerbose gitDir $ \git -> do
482+ writefile (gitDir </> ("default.nix" :: FilePath)) "{}: (import <nixpkgs> {}).hello"
483+ _ <- git ["add", "--all"]
484+ _ <- git ["commit", "-m", "Initial commit"]
485+ rev <- T.strip <$> git ["rev-parse", "HEAD"]
486+ sha256 :: Text
487+ <- either error pure
488+ =<< (Aeson.parseEither (Aeson..: "sha256") <=< Aeson.eitherDecodeStrict . T.encodeUtf8)
489+ <$> run nixPrefetchGitPath [toTextIgnore gitDir]
490+
491+ for_ (legacyGitThunks (GitThunkParams gitDir rev sha256)) $ \mkFiles ->
492+ withSystemTempDirectory "test-thunks" $ \thunkDir -> do
493+ liftIO $ mkFiles thunkDir
494+ run_ "ob" ["thunk", "unpack", toTextIgnore thunkDir]
495+ run_ "ob" ["thunk", "pack", toTextIgnore thunkDir]
496+
497+data GitThunkParams = GitThunkParams
498+ { _gitThunkParams_repo :: !FilePath
499+ , _gitThunkParams_rev :: !Text
500+ , _gitThunkParams_sha256 :: !Text
501+ } deriving Show
502+
503+legacyGitThunks :: GitThunkParams -> [FilePath -> IO ()]
504+legacyGitThunks (GitThunkParams repo' rev sha256) =
505+ [ mkLegacyIO
506+ (T.unlines
507+ [ "# DO NOT HAND-EDIT THIS FILE"
508+ , "let fetchGit = {url, rev, ref ? null, branch ? null, sha256 ? null, fetchSubmodules ? null}:"
509+ , " assert !fetchSubmodules; (import <nixpkgs> {}).fetchgit { inherit url rev sha256; };"
510+ , "in import (fetchGit (builtins.fromJSON (builtins.readFile ./git.json)))"
511+ ]
512+ )
513+ (Map.fromList
514+ [ ("url", repo)
515+ , ("rev", rev)
516+ , ("branch", "master")
517+ , ("sha256", sha256)
518+ ]
519+ )
520+ , mkLegacyIO
521+ (T.unlines
522+ [ "# DO NOT HAND-EDIT THIS FILE"
523+ , "let fetchGit = {url, rev, ref ? null, branch ? null, sha256 ? null, fetchSubmodules ? null}:"
524+ , " if builtins.hasAttr \"fetchGit\" builtins"
525+ , " then builtins.fetchGit ({ inherit url rev; } // (if branch == null then {} else { ref = branch; }))"
526+ , " else abort \"Plain Git repositories are only supported on nix 2.0 or higher.\";"
527+ , "in import (fetchGit (builtins.fromJSON (builtins.readFile ./git.json)))"
528+ ]
529+ )
530+ (Map.fromList
531+ [ ("url", repo)
532+ , ("rev", rev)
533+ , ("branch", "master")
534+ , ("sha256", sha256)
535+ ]
536+ )
537+ , mkLegacyIO
538+ (T.unlines
539+ [ "# DO NOT HAND-EDIT THIS FILE"
540+ , "let fetch = {url, rev, ref ? null, sha256 ? null, fetchSubmodules ? false, private ? false, ...}:"
541+ , " let realUrl = let firstChar = builtins.substring 0 1 url; in"
542+ , " if firstChar == \"/\" then /. + url"
543+ , " else if firstChar == \".\" then ./. + url"
544+ , " else url;"
545+ , " in if !fetchSubmodules && private then builtins.fetchGit {"
546+ , " url = realUrl; inherit rev;"
547+ , " } else (import <nixpkgs> {}).fetchgit {"
548+ , " url = realUrl; inherit rev sha256;"
549+ , " };"
550+ , "in import (fetch (builtins.fromJSON (builtins.readFile ./git.json)))"
551+ ]
552+ )
553+ (Map.fromList
554+ [ ("url", Aeson.String repo)
555+ , ("rev", Aeson.String rev)
556+ , ("branch", Aeson.String "master")
557+ , ("sha256", Aeson.String sha256)
558+ , ("private", Aeson.Bool False)
559+ ]
560+ )
561+ , mkLegacyIO
562+ (T.unlines
563+ [ "# DO NOT HAND-EDIT THIS FILE"
564+ , "let fetch = {url, rev, branch ? null, sha256 ? null, fetchSubmodules ? false, private ? false, ...}:"
565+ , " let realUrl = let firstChar = builtins.substring 0 1 url; in"
566+ , " if firstChar == \"/\" then /. + url"
567+ , " else if firstChar == \".\" then ./. + url"
568+ , " else url;"
569+ , " in if !fetchSubmodules && private then builtins.fetchGit {"
570+ , " url = realUrl; inherit rev;"
571+ , " ${if branch == null then null else \"ref\"} = branch;"
572+ , " } else (import <nixpkgs> {}).fetchgit {"
573+ , " url = realUrl; inherit rev sha256;"
574+ , " };"
575+ , "in import (fetch (builtins.fromJSON (builtins.readFile ./git.json)))"
576+ ]
577+ )
578+ (Map.fromList
579+ [ ("url", Aeson.String repo)
580+ , ("rev", Aeson.String rev)
581+ , ("branch", Aeson.String "master")
582+ , ("sha256", Aeson.String sha256)
583+ , ("private", Aeson.Bool False)
584+ ]
585+ )
586+ ]
587+ where
588+ repo = T.pack repo'
589+ mkLegacyIO :: Aeson.ToJSON v => Text -> Map.Map Text v -> FilePath -> IO ()
590+ mkLegacyIO defaultNix gitJson dir = do
591+ T.writeFile (dir </> ("default.nix" :: FilePath)) defaultNix
592+ LBS.writeFile (dir </> ("git.json" :: FilePath)) (Aeson.encode gitJson)
593+
464594unpackedDirName :: FilePath
465595unpackedDirName = "unpacked"