@@ -8,25 +8,30 @@
8{-# LANGUAGE ScopedTypeVariables #-}
8{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE TypeApplications #-}
9{-# LANGUAGE TypeApplications #-}
10module Obelisk.Command.Thunk
10module Obelisk.Command.Thunk
13+ , ReadThunkError (..)
16+ , ThunkPackConfig (..)
11− ( ThunkPtr (..)
17+ , ThunkPtr (..)
12 , ThunkRev (..)
18 , ThunkRev (..)
13 , ThunkSource (..)
19 , ThunkSource (..)
14− , ThunkData (..)
20+ , ThunkUpdateConfig (..)
15− , ReadThunkError (..)
19− , updateThunkToLatest
20 , createThunk
21 , createThunk
21 , createThunkWithLatest
22 , createThunkWithLatest
22 , nixBuildAttrWithCache
27 , nixBuildAttrWithCache
23 , nixBuildThunkAttrWithCache
28 , nixBuildThunkAttrWithCache
25 , packThunk
29 , packThunk
26 , readThunk
31 , readThunk
27 , updateThunk
33 , updateThunk
28− , getThunkPtr
34+ , updateThunkToLatest
30 , uriThunkPtr
35 , uriThunkPtr
@@ -68,8 +73,7 @@ import System.FilePath
68import System.IO.Error
73import System.IO.Error
69import System.IO.Temp
74import System.IO.Temp
70import System.Posix (getSymbolicLinkStatus, modificationTime)
75import System.Posix (getSymbolicLinkStatus, modificationTime)
71−import System.Process (proc)
76+import qualified Text.URI as URI
74import Obelisk.App (MonadObelisk)
78import Obelisk.App (MonadObelisk)
75import Obelisk.CliApp
79import Obelisk.CliApp
@@ -111,37 +115,63 @@ data GitHubSource = GitHubSource
111 { _gitHubSource_owner :: Name Owner
115 { _gitHubSource_owner :: Name Owner
112 , _gitHubSource_repo :: Name Repo
116 , _gitHubSource_repo :: Name Repo
113 , _gitHubSource_branch :: Maybe (Name Branch)
117 , _gitHubSource_branch :: Maybe (Name Branch)
118+ , _gitHubSource_private :: Bool
115 deriving (Show, Eq, Ord)
120 deriving (Show, Eq, Ord)
122+newtype GitUri = GitUri URI.URI deriving (Eq, Ord, Show)
124+gitUriToText :: GitUri -> Text
125+gitUriToText (GitUri uri)
126+ | (T.toLower . URI.unRText <$> URI.uriScheme uri) == Just "file"
127+ , Just (_, path) <- URI.uriPath uri
128+ = "/" <> T.intercalate "/" (map URI.unRText $ NonEmpty.toList path)
129+ | otherwise = URI.render uri
117data GitSource = GitSource
131data GitSource = GitSource
118− { _gitSource_url :: URI
132+ { _gitSource_url :: GitUri
119 , _gitSource_branch :: Maybe (Name Branch)
133 , _gitSource_branch :: Maybe (Name Branch)
120 , _gitSource_fetchSubmodules :: Bool
134 , _gitSource_fetchSubmodules :: Bool
135+ , _gitSource_private :: Bool
122 deriving (Show, Eq, Ord)
137 deriving (Show, Eq, Ord)
139+newtype ThunkConfig = ThunkConfig
140+ { _thunkConfig_private :: Maybe Bool
143+data ThunkUpdateConfig = ThunkUpdateConfig
144+ { _thunkUpdateConfig_branch :: Maybe String
145+ , _thunkUpdateConfig_config :: ThunkConfig
148+data ThunkPackConfig = ThunkPackConfig
149+ { _thunkPackConfig_force :: Bool
150+ , _thunkPackConfig_config :: ThunkConfig
124-- | Convert a GitHub source to a regular Git source. Assumes no submodules.
153-- | Convert a GitHub source to a regular Git source. Assumes no submodules.
125forgetGithub :: Bool -> GitHubSource -> GitSource
154forgetGithub :: Bool -> GitHubSource -> GitSource
126forgetGithub useSsh s = GitSource
155forgetGithub useSsh s = GitSource
127− { _gitSource_url = URI
156+ { _gitSource_url = GitUri $ URI.URI
128− { uriScheme = Just $ fromRight' $ mkScheme $ if useSsh then "ssh" else "https"
157+ { URI.uriScheme = Just $ fromRight' $ URI.mkScheme $ if useSsh then "ssh" else "https"
129− , uriAuthority = Right $ Authority
158+ , URI.uriAuthority = Right $ URI.Authority
130− { authUserInfo = UserInfo (fromRight' $ mkUsername "git") Nothing
159+ { URI.authUserInfo = URI.UserInfo (fromRight' $ URI.mkUsername "git") Nothing
131 <$ guard useSsh
160 <$ guard useSsh
132− , authHost = fromRight' $ mkHost "github.com"
161+ , URI.authHost = fromRight' $ URI.mkHost "github.com"
133− , authPort = Nothing
162+ , URI.authPort = Nothing
135− , uriPath = Just ( False
164+ , URI.uriPath = Just ( False
136− , fromRight' . mkPathPiece <$>
165+ , fromRight' . URI.mkPathPiece <$>
137 untagName (_gitHubSource_owner s)
166 untagName (_gitHubSource_owner s)
138 :| [ untagName (_gitHubSource_repo s) <> ".git" ]
167 :| [ untagName (_gitHubSource_repo s) <> ".git" ]
140− , uriQuery = []
169+ , URI.uriQuery = []
141− , uriFragment = Nothing
170+ , URI.uriFragment = Nothing
143 , _gitSource_branch = _gitHubSource_branch s
172 , _gitSource_branch = _gitHubSource_branch s
144 , _gitSource_fetchSubmodules = False
173 , _gitSource_fetchSubmodules = False
174+ , _gitSource_private = _gitHubSource_private s
147getThunkGitBranch :: ThunkPtr -> Maybe Text
177getThunkGitBranch :: ThunkPtr -> Maybe Text
@@ -155,20 +185,20 @@ commitNameToRef (N c) = Ref.fromHex $ encodeUtf8 c
155-- TODO: Use spinner here.
185-- TODO: Use spinner here.
156getNixSha256ForUriUnpacked
186getNixSha256ForUriUnpacked
157 :: MonadObelisk m
187 :: MonadObelisk m
158− => URI
188+ => GitUri
159 -> m NixSha256
189 -> m NixSha256
160getNixSha256ForUriUnpacked uri =
190getNixSha256ForUriUnpacked uri =
161− withExitFailMessage ("nix-prefetch-url: Failed to determine sha256 hash of URL " <> render uri) $ do
191+ withExitFailMessage ("nix-prefetch-url: Failed to determine sha256 hash of URL " <> gitUriToText uri) $ do
162 [hash] <- fmap T.lines $ readProcessAndLogOutput (Debug, Debug) $
192 [hash] <- fmap T.lines $ readProcessAndLogOutput (Debug, Debug) $
163− proc "nix-prefetch-url" ["--unpack" , "--type" , "sha256" , T.unpack $ render uri]
193+ proc "nix-prefetch-url" ["--unpack", "--type", "sha256", T.unpack $ gitUriToText uri]
164 pure hash
194 pure hash
166−nixPrefetchGit :: MonadObelisk m => URI -> Text -> Bool -> m NixSha256
196+nixPrefetchGit :: MonadObelisk m => GitUri -> Text -> Bool -> m NixSha256
167nixPrefetchGit uri rev fetchSubmodules =
197nixPrefetchGit uri rev fetchSubmodules =
168− withExitFailMessage ("nix-prefetch-git: Failed to determine sha256 hash of Git repo " <> render uri <> " at " <> rev) $ do
198+ withExitFailMessage ("nix-prefetch-git: Failed to determine sha256 hash of Git repo " <> gitUriToText uri <> " at " <> rev) $ do
169 out <- readProcessAndLogStderr Debug $
199 out <- readProcessAndLogStderr Debug $
170 proc "nix-prefetch-git" $ filter (/="")
200 proc "nix-prefetch-git" $ filter (/="")
171− [ "--url", T.unpack $ render uri
201+ [ "--url", T.unpack $ gitUriToText uri
172 , "--rev", T.unpack rev
202 , "--rev", T.unpack rev
173 , if fetchSubmodules then "--fetch-submodules" else ""
203 , if fetchSubmodules then "--fetch-submodules" else ""
174 , "--quiet"
204 , "--quiet"
@@ -286,10 +316,12 @@ parseGitHubSource v = do
286 owner <- v Aeson..: "owner"
316 owner <- v Aeson..: "owner"
287 repo <- v Aeson..: "repo"
317 repo <- v Aeson..: "repo"
288 branch <- v Aeson..:! "branch"
318 branch <- v Aeson..:! "branch"
319+ private <- v Aeson..:? "private"
289 pure $ GitHubSource
320 pure $ GitHubSource
290 { _gitHubSource_owner = owner
321 { _gitHubSource_owner = owner
291 , _gitHubSource_repo = repo
322 , _gitHubSource_repo = repo
292 , _gitHubSource_branch = branch
323 , _gitHubSource_branch = branch
324+ , _gitHubSource_private = fromMaybe False private
295parseGitSource :: Aeson.Object -> Aeson.Parser GitSource
327parseGitSource :: Aeson.Object -> Aeson.Parser GitSource
@@ -297,10 +329,12 @@ parseGitSource v = do
297 Just url <- parseGitUri <$> v Aeson..: "url"
329 Just url <- parseGitUri <$> v Aeson..: "url"
298 branch <- v Aeson..:! "branch"
330 branch <- v Aeson..:! "branch"
299 fetchSubmodules <- v Aeson..:! "fetchSubmodules"
331 fetchSubmodules <- v Aeson..:! "fetchSubmodules"
332+ private <- v Aeson..:? "private"
300 pure $ GitSource
333 pure $ GitSource
301 { _gitSource_url = url
334 { _gitSource_url = url
302 , _gitSource_branch = branch
335 , _gitSource_branch = branch
303 , _gitSource_fetchSubmodules = fromMaybe False fetchSubmodules
336 , _gitSource_fetchSubmodules = fromMaybe False fetchSubmodules
337+ , _gitSource_private = fromMaybe False private
306overwriteThunk :: MonadObelisk m => FilePath -> ThunkPtr -> m ()
340overwriteThunk :: MonadObelisk m => FilePath -> ThunkPtr -> m ()
@@ -327,13 +361,15 @@ encodeThunkPtrData (ThunkPtr rev src) = case src of
327 , ("branch" .=) <$> _gitHubSource_branch s
361 , ("branch" .=) <$> _gitHubSource_branch s
328 , Just $ "rev" .= Ref.toHexString (_thunkRev_commit rev)
362 , Just $ "rev" .= Ref.toHexString (_thunkRev_commit rev)
329 , Just $ "sha256" .= _thunkRev_nixSha256 rev
363 , Just $ "sha256" .= _thunkRev_nixSha256 rev
364+ , Just $ "private" .= _gitHubSource_private s
331 ThunkSource_Git s -> encodePretty' plainGitCfg $ Aeson.object $ catMaybes
366 ThunkSource_Git s -> encodePretty' plainGitCfg $ Aeson.object $ catMaybes
332− [ Just $ "url" .= render (_gitSource_url s)
367+ [ Just $ "url" .= gitUriToText (_gitSource_url s)
333 , Just $ "rev" .= Ref.toHexString (_thunkRev_commit rev)
368 , Just $ "rev" .= Ref.toHexString (_thunkRev_commit rev)
334 , ("branch" .=) <$> _gitSource_branch s
369 , ("branch" .=) <$> _gitSource_branch s
335 , Just $ "sha256" .= _thunkRev_nixSha256 rev
370 , Just $ "sha256" .= _thunkRev_nixSha256 rev
336 , Just $ "fetchSubmodules" .= _gitSource_fetchSubmodules s
371 , Just $ "fetchSubmodules" .= _gitSource_fetchSubmodules s
372+ , Just $ "private" .= _gitSource_private s
339 githubCfg = defConfig
375 githubCfg = defConfig
@@ -354,6 +390,7 @@ encodeThunkPtrData (ThunkPtr rev src) = case src of
356 , "sha256"
392 , "sha256"
357 , "fetchSubmodules"
394 , "fetchSubmodules"
358 ] <> compare
395 ] <> compare
359 , confTrailingNewline = True
396 , confTrailingNewline = True
@@ -377,8 +414,8 @@ createThunkWithLatest target s = do
377 , _thunkPtr_rev = rev
414 , _thunkPtr_rev = rev
380−updateThunkToLatest :: MonadObelisk m => FilePath -> Maybe String -> m ()
417+updateThunkToLatest :: MonadObelisk m => ThunkUpdateConfig -> FilePath -> m ()
381−updateThunkToLatest target mBranch = withSpinner' ("Updating thunk " <> T.pack target <> " to latest") (pure $ const $ "Thunk " <> T.pack target <> " updated to latest") $ do
418+updateThunkToLatest (ThunkUpdateConfig mBranch thunkConfig) target = withSpinner' ("Updating thunk " <> T.pack target <> " to latest") (pure $ const $ "Thunk " <> T.pack target <> " updated to latest") $ do
382 checkThunkDirectory "ob thunk update directory cannot be '.'" target
419 checkThunkDirectory "ob thunk update directory cannot be '.'" target
383 -- check to see if thunk should be updated to a specific branch or just update it's current branch
420 -- check to see if thunk should be updated to a specific branch or just update it's current branch
384 case mBranch of
421 case mBranch of
@@ -390,7 +427,7 @@ updateThunkToLatest target mBranch = withSpinner' ("Updating thunk " <> T.pack t
390 ThunkData_Checkout _ -> failWith "cannot update an unpacked thunk"
427 ThunkData_Checkout _ -> failWith "cannot update an unpacked thunk"
391 let src = _thunkPtr_source ptr
428 let src = _thunkPtr_source ptr
392 rev <- getLatestRev src
429 rev <- getLatestRev src
393− overwriteThunk overwrite $ ThunkPtr
430+ overwriteThunk overwrite $ modifyThunkPtrByConfig thunkConfig $ ThunkPtr
394 { _thunkPtr_source = src
431 { _thunkPtr_source = src
395 , _thunkPtr_rev = rev
432 , _thunkPtr_rev = rev
@@ -398,24 +435,25 @@ updateThunkToLatest target mBranch = withSpinner' ("Updating thunk " <> T.pack t
398 Left err -> failWith $ T.pack $ "thunk update: " <> show err
435 Left err -> failWith $ T.pack $ "thunk update: " <> show err
399 Right c -> case c of
436 Right c -> case c of
400 ThunkData_Packed t -> case _thunkPtr_source t of
437 ThunkData_Packed t -> case _thunkPtr_source t of
401− ThunkSource_Git tsg -> setThunk target tsg branch
438+ ThunkSource_Git tsg -> setThunk thunkConfig target tsg branch
402 ThunkSource_GitHub tsgh -> do
439 ThunkSource_GitHub tsgh -> do
403 let tsg = forgetGithub False tsgh
440 let tsg = forgetGithub False tsgh
404− setThunk target tsg branch
441+ setThunk thunkConfig target tsg branch
405− ThunkData_Checkout _ -> failWith $ T.pack $ "thunk located at " <> (show target) <> " is unpacked. Use ob thunk pack on the desired directory and then try ob thunk update again."
442+ ThunkData_Checkout _ -> failWith $ T.pack $ "thunk located at " <> show target <> " is unpacked. Use ob thunk pack on the desired directory and then try ob thunk update again."
407−setThunk :: MonadObelisk m => FilePath -> GitSource -> String -> m ()
444+setThunk :: MonadObelisk m => ThunkConfig -> FilePath -> GitSource -> String -> m ()
408−setThunk target gs branch = do
445+setThunk thunkConfig target gs branch = do
409 newThunkPtr <- uriThunkPtr (_gitSource_url gs) (Just $ T.pack branch) Nothing
446 newThunkPtr <- uriThunkPtr (_gitSource_url gs) (Just $ T.pack branch) Nothing
410 overwriteThunk target newThunkPtr
447 overwriteThunk target newThunkPtr
411− updateThunkToLatest target Nothing
448+ updateThunkToLatest (ThunkUpdateConfig Nothing thunkConfig) target
413-- | All recognized github standalone loaders, ordered from newest to oldest.
450-- | All recognized github standalone loaders, ordered from newest to oldest.
414-- This tool will only ever produce the newest one when it writes a thunk.
451-- This tool will only ever produce the newest one when it writes a thunk.
415gitHubStandaloneLoaders :: NonEmpty Text
452gitHubStandaloneLoaders :: NonEmpty Text
416gitHubStandaloneLoaders =
453gitHubStandaloneLoaders =
417− gitHubStandaloneLoaderV2 :|
454+ gitHubStandaloneLoaderV4 :|
418 [ gitHubStandaloneLoaderV3
455 [ gitHubStandaloneLoaderV3
456+ , gitHubStandaloneLoaderV2
419 , gitHubStandaloneLoaderV1
457 , gitHubStandaloneLoaderV1
@@ -455,10 +493,23 @@ gitHubStandaloneLoaderV3 = T.unlines
455 , "in import (fetch (builtins.fromJSON (builtins.readFile ./github.json)))"
493 , "in import (fetch (builtins.fromJSON (builtins.readFile ./github.json)))"
496+gitHubStandaloneLoaderV4 :: Text
497+gitHubStandaloneLoaderV4 = T.unlines
498+ [ "# DO NOT HAND-EDIT THIS FILE"
499+ , "let fetch = { private ? false, fetchSubmodules ? false, owner, repo, rev, sha256, ... }:"
500+ , " if !fetchSubmodules && !private then builtins.fetchTarball {"
501+ , " url = \"https://github.com/${owner}/${repo}/archive/${rev}.tar.gz\"; inherit sha256;"
502+ , " } else (import <nixpkgs> {}).fetchFromGitHub {"
503+ , " inherit owner repo rev sha256 fetchSubmodules private;"
505+ , "in import (fetch (builtins.fromJSON (builtins.readFile ./github.json)))"
458plainGitStandaloneLoaders :: NonEmpty Text
508plainGitStandaloneLoaders :: NonEmpty Text
459plainGitStandaloneLoaders =
509plainGitStandaloneLoaders =
460− plainGitStandaloneLoaderV1 :|
510+ plainGitStandaloneLoaderV3 :|
511+ [ plainGitStandaloneLoaderV1
461− [ plainGitStandaloneLoaderV2
512+ , plainGitStandaloneLoaderV2
464plainGitStandaloneLoaderV1 :: Text
515plainGitStandaloneLoaderV1 :: Text
@@ -479,6 +530,22 @@ plainGitStandaloneLoaderV2 = T.unlines
479 , "in import (fetchGit (builtins.fromJSON (builtins.readFile ./git.json)))"
530 , "in import (fetchGit (builtins.fromJSON (builtins.readFile ./git.json)))"
533+plainGitStandaloneLoaderV3 :: Text
534+plainGitStandaloneLoaderV3 = T.unlines
535+ [ "# DO NOT HAND-EDIT THIS FILE"
536+ , "let fetch = {url, rev, ref ? null, sha256 ? null, fetchSubmodules ? false, private ? false, ...}:"
537+ , " let realUrl = let firstChar = builtins.substring 0 1 url; in"
538+ , " if firstChar == \"/\" then /. + url"
539+ , " else if firstChar == \".\" then ./. + url"
541+ , " in if !fetchSubmodules && private then builtins.fetchGit {"
542+ , " url = realUrl; inherit rev;"
543+ , " } else (import <nixpkgs> {}).fetchgit {"
544+ , " url = realUrl; inherit rev sha256;"
546+ , "in import (fetch (builtins.fromJSON (builtins.readFile ./git.json)))"
482thunkFileNames :: [FilePath]
549thunkFileNames :: [FilePath]
483thunkFileNames = ["default.nix", "github.json", "git.json"]
550thunkFileNames = ["default.nix", "github.json", "git.json"]
@@ -520,7 +587,7 @@ nixBuildThunkAttrWithCache thunkDir attr = do
520 Just c -> return c
587 Just c -> return c
521 Nothing -> do
588 Nothing -> do
522 putLog Warning $ T.pack $ mconcat [thunkDir, ": ", attr, " not cached, building ..."]
589 putLog Warning $ T.pack $ mconcat [thunkDir, ": ", attr, " not cached, building ..."]
523− _ <- nixCmd $ NixCmd_Build$ def
590+ _ <- nixCmd $ NixCmd_Build $ def
524 Lens.& nixBuildConfig_outLink Lens..~ OutLink_IndirectRoot cachePath
591 Lens.& nixBuildConfig_outLink Lens..~ OutLink_IndirectRoot cachePath
525 Lens.& nixCmdConfig_target Lens..~ Target
592 Lens.& nixCmdConfig_target Lens..~ Target
526 { _target_path = Just thunkDir
593 { _target_path = Just thunkDir
@@ -568,13 +635,13 @@ updateThunk p f = withSystemTempDirectory "obelisk-thunkptr-" $ \tmpDir -> do
568 Right (ThunkData_Packed _) -> do
635 Right (ThunkData_Packed _) -> do
569 let tmpThunk = tmpDir </> "thunk"
636 let tmpThunk = tmpDir </> "thunk"
570 callProcessAndLogOutput (Notice, Error) $
637 callProcessAndLogOutput (Notice, Error) $
571− proc "cp" ["-r", "-T", thunkDir, tmpThunk]
638+ proc cp ["-r", "-T", thunkDir, tmpThunk]
572 return tmpThunk
639 return tmpThunk
573− Right _ -> failWith $ "Thunk is not packed"
640+ Right _ -> failWith "Thunk is not packed"
574 updateThunkFromTmp p' = do
641 updateThunkFromTmp p' = do
575− _ <- packThunk' True False p'
642+ _ <- packThunk' True (ThunkPackConfig False (ThunkConfig Nothing)) p'
576 callProcessAndLogOutput (Notice, Error) $
643 callProcessAndLogOutput (Notice, Error) $
577− proc "cp" ["-r", "-T", p', p]
644+ proc cp ["-r", "-T", p', p]
579finalMsg :: Bool -> (a -> Text) -> Maybe (a -> Text)
646finalMsg :: Bool -> (a -> Text) -> Maybe (a -> Text)
580finalMsg noTrail s = if noTrail then Nothing else Just s
647finalMsg noTrail s = if noTrail then Nothing else Just s
@@ -606,7 +673,7 @@ unpackThunk' noTrail thunkDir = checkThunkDirectory "Can't pack/unpack from with
606 let git = callProcessAndLogOutput (Notice, Notice) . gitProc tmpRepo
673 let git = callProcessAndLogOutput (Notice, Notice) . gitProc tmpRepo
607 git $ [ "clone" ]
674 git $ [ "clone" ]
608 ++ ("--recursive" <$ guard (_gitSource_fetchSubmodules s))
675 ++ ("--recursive" <$ guard (_gitSource_fetchSubmodules s))
609− ++ [ T.unpack $ render $ _gitSource_url s ]
676+ ++ [ T.unpack $ gitUriToText $ _gitSource_url s ]
610 ++ do branch <- maybeToList $ _gitSource_branch s
677 ++ do branch <- maybeToList $ _gitSource_branch s
611 [ "--branch", T.unpack $ untagName branch ]
678 [ "--branch", T.unpack $ untagName branch ]
612 git ["reset", "--hard", Ref.toHexString $ _thunkRev_commit $ _thunkPtr_rev tptr]
679 git ["reset", "--hard", Ref.toHexString $ _thunkRev_commit $ _thunkPtr_rev tptr]
@@ -615,31 +682,39 @@ unpackThunk' noTrail thunkDir = checkThunkDirectory "Can't pack/unpack from with
616 liftIO $ createDirectory obGitDir
683 liftIO $ createDirectory obGitDir
617 callProcessAndLogOutput (Notice, Error) $
684 callProcessAndLogOutput (Notice, Error) $
618− proc "cp" ["-r", "-T", thunkDir </> ".", obGitDir </> "orig-thunk"]
685+ proc cp ["-r", "-T", thunkDir </> ".", obGitDir </> "orig-thunk"]
619 callProcessAndLogOutput (Notice, Error) $
686 callProcessAndLogOutput (Notice, Error) $
620 proc "rm" ["-r", thunkDir]
687 proc "rm" ["-r", thunkDir]
621 callProcessAndLogOutput (Notice, Error) $
688 callProcessAndLogOutput (Notice, Error) $
622 proc "mv" ["-T", tmpRepo, thunkDir]
689 proc "mv" ["-T", tmpRepo, thunkDir]
624--TODO: add a rollback mode to pack to the original thunk
691--TODO: add a rollback mode to pack to the original thunk
625−packThunk :: MonadObelisk m => Bool -> FilePath -> m ThunkPtr
692+packThunk :: MonadObelisk m => ThunkPackConfig -> FilePath -> m ThunkPtr
626packThunk = packThunk' False
693packThunk = packThunk' False
628−packThunk' :: MonadObelisk m => Bool -> Bool -> FilePath -> m ThunkPtr
695+packThunk' :: MonadObelisk m => Bool -> ThunkPackConfig -> FilePath -> m ThunkPtr
629−packThunk' noTrail force thunkDir = checkThunkDirectory "Can't pack/unpack from within the thunk directory" thunkDir >> readThunk thunkDir >>= \case
696+packThunk' noTrail (ThunkPackConfig force thunkConfig) thunkDir = checkThunkDirectory "Can't pack/unpack from within the thunk directory" thunkDir >> readThunk thunkDir >>= \case
630 Left err -> failWith $ T.pack $ "thunk pack: " <> show err
697 Left err -> failWith $ T.pack $ "thunk pack: " <> show err
631 Right (ThunkData_Packed _) -> failWith "pack: thunk is already packed"
698 Right (ThunkData_Packed _) -> failWith "pack: thunk is already packed"
632 Right (ThunkData_Checkout _) -> do
699 Right (ThunkData_Checkout _) -> do
633 withSpinner' ("Packing thunk " <> T.pack thunkDir)
700 withSpinner' ("Packing thunk " <> T.pack thunkDir)
634 (finalMsg noTrail $ const $ "Packed thunk " <> T.pack thunkDir) $ do
701 (finalMsg noTrail $ const $ "Packed thunk " <> T.pack thunkDir) $ do
635− thunkPtr <- getThunkPtr (not force) thunkDir
702+ thunkPtr <- modifyThunkPtrByConfig thunkConfig <$> getThunkPtr (not force) thunkDir
636 callProcessAndLogOutput (Debug, Error) $ proc "rm" ["-rf", thunkDir]
703 callProcessAndLogOutput (Debug, Error) $ proc "rm" ["-rf", thunkDir]
637 liftIO $ createThunk thunkDir thunkPtr
704 liftIO $ createThunk thunkDir thunkPtr
638 pure thunkPtr
705 pure thunkPtr
707+modifyThunkPtrByConfig :: ThunkConfig -> ThunkPtr -> ThunkPtr
708+modifyThunkPtrByConfig (ThunkConfig markPrivate') ptr = case markPrivate' of
710+ Just markPrivate -> ptr { _thunkPtr_source = case _thunkPtr_source ptr of
711+ ThunkSource_Git s -> ThunkSource_Git $ s { _gitSource_private = markPrivate }
712+ ThunkSource_GitHub s -> ThunkSource_GitHub $ s { _gitHubSource_private = markPrivate }
640getThunkPtr :: forall m. MonadObelisk m => Bool -> FilePath -> m ThunkPtr
715getThunkPtr :: forall m. MonadObelisk m => Bool -> FilePath -> m ThunkPtr
641getThunkPtr checkClean thunkDir = do
716getThunkPtr checkClean thunkDir = do
642− when checkClean $ ensureCleanGitRepo thunkDir True $
717+ when checkClean $ ensureCleanGitRepo thunkDir True
643 "thunk pack: thunk checkout contains unsaved modifications"
718 "thunk pack: thunk checkout contains unsaved modifications"
645 -- Check whether there are any stashes
720 -- Check whether there are any stashes
@@ -661,7 +736,7 @@ getThunkPtr checkClean thunkDir = do
661 <$> T.lines
736 <$> T.lines
662 <$> readGitProcess thunkDir ["rev-parse", "HEAD"]
737 <$> readGitProcess thunkDir ["rev-parse", "HEAD"]
663 case b of
738 case b of
664− (Just "HEAD") -> failWith $ T.unlines $
739+ (Just "HEAD") -> failWith $ T.unlines
665 [ "thunk pack: You are in 'detached HEAD' state."
740 [ "thunk pack: You are in 'detached HEAD' state."
666 , "If you want to pack at the current ref \
741 , "If you want to pack at the current ref \
667 \then please create a new branch with 'git checkout -b <new-branch-name>' and push this upstream."
742 \then please create a new branch with 'git checkout -b <new-branch-name>' and push this upstream."
@@ -733,7 +808,7 @@ getThunkPtr checkClean thunkDir = do
733 [ "thunk pack: Certain branches in the thunk have commits not yet pushed upstream:"
808 [ "thunk pack: Certain branches in the thunk have commits not yet pushed upstream:"
736− (flip map (Map.toList nonGood) $ \(branch, (upstream, (ahead, behind))) -> mconcat
811+ flip map (Map.toList nonGood) (\(branch, (upstream, (ahead, behind))) -> mconcat
737 [" ", branch, " ahead: ", T.pack (show ahead), " behind: ", T.pack (show behind), " remote branch ", upstream]) ++
812 [" ", branch, " ahead: ", T.pack (show ahead), " behind: ", T.pack (show behind), " remote branch ", upstream]) ++
739 , "Please push these upstream and try again. (Or just fetch, if they are somehow \
814 , "Please push these upstream and try again. (Or just fetch, if they are somehow \
@@ -741,7 +816,7 @@ getThunkPtr checkClean thunkDir = do
743 -- We assume it's safe to pack the thunk at this point
818 -- We assume it's safe to pack the thunk at this point
744− putLog Informational $ "All changes safe in git remotes. OK to pack thunk."
819+ putLog Informational "All changes safe in git remotes. OK to pack thunk."
746 let remote = maybe "origin" snd $ flip Map.lookup headUpstream =<< mCurrentBranch
821 let remote = maybe "origin" snd $ flip Map.lookup headUpstream =<< mCurrentBranch
@@ -773,7 +848,7 @@ getLatestRev os = do
773-- performance. If that doesn't work (e.g. authentication issue), we fall back
848-- performance. If that doesn't work (e.g. authentication issue), we fall back
774-- on just doing things the normal way for git repos in general, and save it as
849-- on just doing things the normal way for git repos in general, and save it as
775-- a regular git thunk.
850-- a regular git thunk.
776−uriThunkPtr :: MonadObelisk m => URI -> Maybe Text -> Maybe Text -> m ThunkPtr
851+uriThunkPtr :: MonadObelisk m => GitUri -> Maybe Text -> Maybe Text -> m ThunkPtr
777uriThunkPtr uri mbranch mcommit = do
852uriThunkPtr uri mbranch mcommit = do
778 commit <- case mcommit of
853 commit <- case mcommit of
779 Nothing -> gitGetCommitBranch uri mbranch >>= return . snd
854 Nothing -> gitGetCommitBranch uri mbranch >>= return . snd
@@ -784,7 +859,7 @@ uriThunkPtr uri mbranch mcommit = do
784 case rev of
859 case rev of
785 Right r -> pure (ThunkSource_GitHub s, r)
860 Right r -> pure (ThunkSource_GitHub s, r)
786 Left e -> do
861 Left e -> do
787− putLog Warning $ "\
862+ putLog Warning "\
788\Failed to fetch archive from GitHub. This is probably a private repo. \
863\Failed to fetch archive from GitHub. This is probably a private repo. \
789\Falling back on normal fetchgit. Original failure:"
864\Falling back on normal fetchgit. Original failure:"
790 errorToWarning e
865 errorToWarning e
@@ -801,33 +876,33 @@ uriThunkPtr uri mbranch mcommit = do
801-- If the thunk is a GitHub thunk and fails, we do *not* fall back like with
876-- If the thunk is a GitHub thunk and fails, we do *not* fall back like with
802-- `uriThunkPtr`. Unlike a plain URL, a thunk src explicitly states which method
877-- `uriThunkPtr`. Unlike a plain URL, a thunk src explicitly states which method
803-- should be employed, and so we respect that.
878-- should be employed, and so we respect that.
804−uriToThunkSource :: URI -> Maybe Text -> ThunkSource
879+uriToThunkSource :: GitUri -> Maybe Text -> ThunkSource
805−uriToThunkSource u
880+uriToThunkSource (GitUri u)
806− | Right uriAuth <- uriAuthority u
881+ | Right uriAuth <- URI.uriAuthority u
807− , Just scheme <- unRText <$> uriScheme u
882+ , Just scheme <- URI.unRText <$> URI.uriScheme u
808 , case scheme of
883 , case scheme of
809− "ssh" -> uriAuth == Authority
884+ "ssh" -> uriAuth == URI.Authority
810− { authUserInfo = Just $ UserInfo (fromRight' $ mkUsername "git") Nothing
885+ { URI.authUserInfo = Just $ URI.UserInfo (fromRight' $ URI.mkUsername "git") Nothing
811− , authHost = fromRight' $ mkHost "github.com"
886+ , URI.authHost = fromRight' $ URI.mkHost "github.com"
812− , authPort = Nothing
887+ , URI.authPort = Nothing
814 s -> s `L.elem` [ "git", "https", "http" ] -- "http:" just redirects to "https:"
889 s -> s `L.elem` [ "git", "https", "http" ] -- "http:" just redirects to "https:"
815− && unRText (authHost uriAuth) == "github.com"
890+ && URI.unRText (URI.authHost uriAuth) == "github.com"
816− , Just (_, owner :| [repoish]) <- uriPath u
891+ , Just (_, owner :| [repoish]) <- URI.uriPath u
817 = \mbranch -> ThunkSource_GitHub $ GitHubSource
892 = \mbranch -> ThunkSource_GitHub $ GitHubSource
818− { _gitHubSource_owner = N $ unRText owner
893+ { _gitHubSource_owner = N $ URI.unRText owner
819 , _gitHubSource_repo = N $ let
894 , _gitHubSource_repo = N $ let
820− repoish' = unRText repoish
895+ repoish' = URI.unRText repoish
821− in case T.stripSuffix ".git" repoish' of
896+ in fromMaybe repoish' $ T.stripSuffix ".git" repoish'
824 , _gitHubSource_branch = N <$> mbranch
897 , _gitHubSource_branch = N <$> mbranch
898+ , _gitHubSource_private = False -- TODO: Can we try something to infer this?
827 | otherwise = \mbranch -> ThunkSource_Git $ GitSource
901 | otherwise = \mbranch -> ThunkSource_Git $ GitSource
828− { _gitSource_url = u
902+ { _gitSource_url = GitUri u
829 , _gitSource_branch = N <$> mbranch
903 , _gitSource_branch = N <$> mbranch
830 , _gitSource_fetchSubmodules = False -- TODO: How do we determine if this should be true?
904 , _gitSource_fetchSubmodules = False -- TODO: How do we determine if this should be true?
905+ , _gitSource_private = False -- TODO: Can we try something to infer this?
833-- Funny signature indicates no effects depend on the optional branch name.
908-- Funny signature indicates no effects depend on the optional branch name.
@@ -840,19 +915,19 @@ githubThunkRev
840githubThunkRev s commit = do
915githubThunkRev s commit = do
841 owner <- forcePP $ _gitHubSource_owner s
916 owner <- forcePP $ _gitHubSource_owner s
842 repo <- forcePP $ _gitHubSource_repo s
917 repo <- forcePP $ _gitHubSource_repo s
843− revTarball <- mkPathPiece $ commit <> ".tar.gz"
918+ revTarball <- URI.mkPathPiece $ commit <> ".tar.gz"
844− let archiveUri = URI
919+ let archiveUri = GitUri $ URI.URI
845− { uriScheme = Just $ fromRight' $ mkScheme "https"
920+ { URI.uriScheme = Just $ fromRight' $ URI.mkScheme "https"
846− , uriAuthority = Right $ Authority
921+ , URI.uriAuthority = Right $ URI.Authority
847− { authUserInfo = Nothing
922+ { URI.authUserInfo = Nothing
848− , authHost = fromRight' $ mkHost "github.com"
923+ , URI.authHost = fromRight' $ URI.mkHost "github.com"
849− , authPort = Nothing
924+ , URI.authPort = Nothing
851− , uriPath = Just ( False
926+ , URI.uriPath = Just ( False
852− , owner :| [ repo, fromRight' $ mkPathPiece "archive", revTarball ]
927+ , owner :| [ repo, fromRight' $ URI.mkPathPiece "archive", revTarball ]
854− , uriQuery = []
929+ , URI.uriQuery = []
855− , uriFragment = Nothing
930+ , URI.uriFragment = Nothing
857 hash <- getNixSha256ForUriUnpacked archiveUri
932 hash <- getNixSha256ForUriUnpacked archiveUri
858 putLog Debug $ "Nix sha256 is " <> hash
933 putLog Debug $ "Nix sha256 is " <> hash
@@ -861,8 +936,8 @@ githubThunkRev s commit = do
861 , _thunkRev_nixSha256 = hash
936 , _thunkRev_nixSha256 = hash
864− forcePP :: Name entity -> m (RText 'PathPiece)
939+ forcePP :: Name entity -> m (URI.RText 'URI.PathPiece)
865− forcePP = mkPathPiece . untagName
940+ forcePP = URI.mkPathPiece . untagName
867gitThunkRev
942gitThunkRev
868 :: MonadObelisk m
943 :: MonadObelisk m
@@ -871,11 +946,11 @@ gitThunkRev
871 -> m ThunkRev
946 -> m ThunkRev
872gitThunkRev s commit = do
947gitThunkRev s commit = do
873 let u = _gitSource_url s
948 let u = _gitSource_url s
874− protocols = ["https", "ssh", "git"]
949+ protocols = ["file", "https", "ssh", "git"]
875− Just scheme <- pure $ unRText <$> uriScheme u
950+ scheme = maybe "file" URI.unRText $ URI.uriScheme $ (\(GitUri x) -> x) u
876 unless (T.toLower scheme `elem` protocols) $
951 unless (T.toLower scheme `elem` protocols) $
877 failWith $ "obelisk currently only supports "
952 failWith $ "obelisk currently only supports "
878− <> T.intercalate ", " protocols <> " protocols for non-GitHub remotes"
953+ <> T.intercalate ", " protocols <> " protocols for plain Git remotes"
879 hash <- nixPrefetchGit u commit $ _gitSource_fetchSubmodules s
954 hash <- nixPrefetchGit u commit $ _gitSource_fetchSubmodules s
880 putLog Informational $ "Nix sha256 is " <> hash
955 putLog Informational $ "Nix sha256 is " <> hash
881 pure $ ThunkRev
956 pure $ ThunkRev
@@ -889,10 +964,10 @@ gitThunkRev s commit = do
889-- If the branch name is passed in, it is returned exactly as-is. If it is not
964-- If the branch name is passed in, it is returned exactly as-is. If it is not
890-- passed it, the default branch of the repo is used instead.
965-- passed it, the default branch of the repo is used instead.
891gitGetCommitBranch
966gitGetCommitBranch
892− :: MonadObelisk m => URI -> Maybe Text -> m (Text, CommitId)
967+ :: MonadObelisk m => GitUri -> Maybe Text -> m (Text, CommitId)
893gitGetCommitBranch uri mbranch = withExitFailMessage ("Failure for git remote " <> uriMsg) $ do
968gitGetCommitBranch uri mbranch = withExitFailMessage ("Failure for git remote " <> uriMsg) $ do
894 (_, bothMaps) <- gitLsRemote
969 (_, bothMaps) <- gitLsRemote
895− (T.unpack $ render uri)
970+ (T.unpack $ gitUriToText uri)
896 (GitRef_Branch <$> mbranch)
971 (GitRef_Branch <$> mbranch)
898 branch <- case mbranch of
973 branch <- case mbranch of
@@ -908,18 +983,21 @@ gitGetCommitBranch uri mbranch = withExitFailMessage ("Failure for git remote "
908 pure (branch, commit)
983 pure (branch, commit)
910 rethrowE = either failWith pure
985 rethrowE = either failWith pure
911− uriMsg = render uri
986+ uriMsg = gitUriToText uri
988+parseGitUri :: Text -> Maybe GitUri
989+parseGitUri x = GitUri <$> (parseFileURI x <|> parseAbsoluteURI x <|> parseSshShorthand x)
913−parseGitUri :: Text -> Maybe URI
991+parseFileURI :: Text -> Maybe URI.URI
914−parseGitUri x = parseAbsoluteURI x <|> parseSshShorthand x
992+parseFileURI uri = if "/" `T.isPrefixOf` uri then parseAbsoluteURI ("file://" <> uri) else Nothing
916−parseAbsoluteURI :: Text -> Maybe URI
994+parseAbsoluteURI :: Text -> Maybe URI.URI
917parseAbsoluteURI uri = do
995parseAbsoluteURI uri = do
918− parsedUri <- mkURI uri
996+ parsedUri <- URI.mkURI uri
919− guard $ isPathAbsolute parsedUri
997+ guard $ URI.isPathAbsolute parsedUri
920− pure $ parsedUri
998+ pure parsedUri
922−parseSshShorthand :: Text -> Maybe URI
1000+parseSshShorthand :: Text -> Maybe URI.URI
923parseSshShorthand uri = do
1001parseSshShorthand uri = do
924 -- This is what git does to check that the remote
1002 -- This is what git does to check that the remote
925 -- is not a local file path when parsing shorthand.
1003 -- is not a local file path when parsing shorthand.
@@ -932,4 +1010,4 @@ parseSshShorthand uri = do
932 -- This check is used to disambiguate a filepath containing a colon from shorthand
1010 -- This check is used to disambiguate a filepath containing a colon from shorthand
933 guard $ isNothing (T.findIndex (=='/') authAndHostname)
1011 guard $ isNothing (T.findIndex (=='/') authAndHostname)
934 && not (T.null colonAndPath)
1012 && not (T.null colonAndPath)
935− mkURI properUri
1013+ URI.mkURI properUri