484a74f073
Try to enable special remotes configured with autoenable=yes when git-annex auto-initialization happens in a new clone of an existing repo. Previously, git-annex init had to be explicitly run to enable them. That was a bit of a wart of a special case for users to need to keep in mind. Special remotes cannot display anything when autoenabled this way, to avoid interfering with the output of git-annex query commands. Any error messages will be hidden, and if it fails, nothing is displayed. The user will realize the remote isn't enable when they try to use it, and can run git-annex init manually then to try the autoenable again and see what failed. That seems like a reasonable approach, and it's less complicated than communicating something across a pipe in order to display it as a side message. Other reason not to do that is that, if the first command the user runs is one like git-annex find that has machine readable output, any message about autoenable failing would need to not be displayed anyway. So better to not display a failure message ever, for consistency. (Had to split out Remote.List.Util to avoid an import cycle.)
184 lines
6.7 KiB
Haskell
184 lines
6.7 KiB
Haskell
{- git-annex assistant remote creation utilities
|
|
-
|
|
- Copyright 2012, 2013 Joey Hess <id@joeyh.name>
|
|
-
|
|
- Licensed under the GNU AGPL version 3 or higher.
|
|
-}
|
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
module Assistant.MakeRemote where
|
|
|
|
import Assistant.Common
|
|
import Assistant.Ssh
|
|
import qualified Types.Remote as R
|
|
import qualified Remote
|
|
import Remote.List.Util
|
|
import qualified Remote.Rsync as Rsync
|
|
import qualified Remote.GCrypt as GCrypt
|
|
import qualified Git
|
|
import qualified Git.Command
|
|
import qualified Annex
|
|
import qualified Annex.SpecialRemote
|
|
import Annex.SpecialRemote.Config
|
|
import Logs.UUID
|
|
import Logs.Remote
|
|
import Git.Remote
|
|
import Git.Types (RemoteName)
|
|
import Creds
|
|
import Assistant.Gpg
|
|
import Utility.Gpg (KeyId)
|
|
import Types.GitConfig
|
|
import Config
|
|
import Types.ProposedAccepted
|
|
|
|
import qualified Data.Map as M
|
|
|
|
{- Sets up a new git or rsync remote, accessed over ssh. -}
|
|
makeSshRemote :: SshData -> Annex RemoteName
|
|
makeSshRemote sshdata = maker (sshRepoName sshdata) (genSshUrl sshdata)
|
|
where
|
|
maker
|
|
| onlyCapability sshdata RsyncCapable = makeRsyncRemote
|
|
| otherwise = makeGitRemote
|
|
|
|
{- Runs an action that returns a name of the remote, and finishes adding it. -}
|
|
addRemote :: Annex RemoteName -> Annex Remote
|
|
addRemote a = do
|
|
name <- a
|
|
remotesChanged
|
|
maybe (error "failed to add remote") return
|
|
=<< Remote.byName (Just name)
|
|
|
|
{- Inits a rsync special remote, and returns its name. -}
|
|
makeRsyncRemote :: RemoteName -> String -> Annex String
|
|
makeRsyncRemote name location = makeRemote name location $ const $ void $
|
|
go =<< Annex.SpecialRemote.findExisting name
|
|
where
|
|
go Nothing = setupSpecialRemote name Rsync.remote config Nothing
|
|
(Nothing, R.Init, Annex.SpecialRemote.newConfig name Nothing mempty mempty) Nothing
|
|
go (Just (u, c, mcu)) = setupSpecialRemote name Rsync.remote config Nothing
|
|
(Just u, R.Enable c, c) mcu
|
|
config = M.fromList
|
|
[ (encryptionField, Proposed "shared")
|
|
, (Proposed "rsyncurl", Proposed location)
|
|
, (typeField, Proposed "rsync")
|
|
]
|
|
|
|
{- Inits a gcrypt special remote, and returns its name. -}
|
|
makeGCryptRemote :: RemoteName -> String -> KeyId -> Annex RemoteName
|
|
makeGCryptRemote remotename location keyid =
|
|
initSpecialRemote remotename GCrypt.remote Nothing $ M.fromList
|
|
[ (typeField, Proposed "gcrypt")
|
|
, (Proposed "gitrepo", Proposed location)
|
|
, configureEncryption HybridEncryption
|
|
, (Proposed "keyid", Proposed keyid)
|
|
]
|
|
|
|
type SpecialRemoteMaker = RemoteName -> RemoteType -> Maybe CredPair -> R.RemoteConfig -> Annex RemoteName
|
|
|
|
{- Inits a new special remote. The name is used as a suggestion, but
|
|
- will be changed if there is already a special remote with that name. -}
|
|
initSpecialRemote :: SpecialRemoteMaker
|
|
initSpecialRemote name remotetype mcreds config = go 0
|
|
where
|
|
go :: Int -> Annex RemoteName
|
|
go n = do
|
|
let fullname = if n == 0 then name else name ++ show n
|
|
Annex.SpecialRemote.findExisting fullname >>= \case
|
|
Nothing -> setupSpecialRemote fullname remotetype config mcreds
|
|
(Nothing, R.Init, Annex.SpecialRemote.newConfig fullname Nothing mempty mempty) Nothing
|
|
Just _ -> go (n + 1)
|
|
|
|
{- Enables an existing special remote. -}
|
|
enableSpecialRemote :: SpecialRemoteMaker
|
|
enableSpecialRemote name remotetype mcreds config =
|
|
Annex.SpecialRemote.findExisting name >>= \case
|
|
Nothing -> error $ "Cannot find a special remote named " ++ name
|
|
Just (u, c, mcu) -> setupSpecialRemote' False name remotetype config mcreds (Just u, R.Enable c, c) mcu
|
|
|
|
setupSpecialRemote :: RemoteName -> RemoteType -> R.RemoteConfig -> Maybe CredPair -> (Maybe UUID, R.SetupStage, R.RemoteConfig) -> Maybe (Annex.SpecialRemote.ConfigFrom UUID) -> Annex RemoteName
|
|
setupSpecialRemote = setupSpecialRemote' True
|
|
|
|
setupSpecialRemote' :: Bool -> RemoteName -> RemoteType -> R.RemoteConfig -> Maybe CredPair -> (Maybe UUID, R.SetupStage, R.RemoteConfig) -> Maybe (Annex.SpecialRemote.ConfigFrom UUID) -> Annex RemoteName
|
|
setupSpecialRemote' setdesc name remotetype config mcreds (mu, ss, c) mcu = do
|
|
{- Currently, only 'weak' ciphers can be generated from the
|
|
- assistant, because otherwise GnuPG may block once the entropy
|
|
- pool is drained, and as of now there's no way to tell the user
|
|
- to perform IO actions to refill the pool. -}
|
|
let weakc = M.insert (Proposed "highRandomQuality") (Proposed "false") (M.union config c)
|
|
dummycfg <- liftIO dummyRemoteGitConfig
|
|
(c', u) <- R.setup remotetype ss mu mcreds weakc dummycfg
|
|
case mcu of
|
|
Nothing ->
|
|
configSet u c'
|
|
Just (Annex.SpecialRemote.ConfigFrom cu) -> do
|
|
setConfig (remoteAnnexConfig c' "config-uuid") (fromUUID cu)
|
|
configSet cu c'
|
|
when setdesc $
|
|
whenM (isNothing . M.lookup u <$> uuidDescMap) $
|
|
describeUUID u (toUUIDDesc name)
|
|
return name
|
|
|
|
{- Returns the name of the git remote it created. If there's already a
|
|
- remote at the location, returns its name. -}
|
|
makeGitRemote :: String -> String -> Annex RemoteName
|
|
makeGitRemote basename location = makeRemote basename location $ \name ->
|
|
void $ inRepo $ Git.Command.runBool
|
|
[Param "remote", Param "add", Param name, Param location]
|
|
|
|
{- If there's not already a remote at the location, adds it using the
|
|
- action, which is passed the name of the remote to make.
|
|
-
|
|
- Returns the name of the remote. -}
|
|
makeRemote :: String -> String -> (RemoteName -> Annex ()) -> Annex RemoteName
|
|
makeRemote basename location a = do
|
|
rs <- Annex.getGitRemotes
|
|
if not (any samelocation rs)
|
|
then do
|
|
let name = uniqueRemoteName basename 0 rs
|
|
a name
|
|
return name
|
|
else return basename
|
|
where
|
|
samelocation x = Git.repoLocation x == location
|
|
|
|
{- Given a list of all remotes, generate an unused name for a new
|
|
- remote, adding a number if necessary.
|
|
-
|
|
- Ensures that the returned name is a legal git remote name. -}
|
|
uniqueRemoteName :: String -> Int -> [Git.Repo] -> RemoteName
|
|
uniqueRemoteName basename n rs
|
|
| null namecollision = name
|
|
| otherwise = uniqueRemoteName legalbasename (succ n) rs
|
|
where
|
|
namecollision = filter samename rs
|
|
samename x = Git.remoteName x == Just name
|
|
name
|
|
| n == 0 = legalbasename
|
|
| otherwise = legalbasename ++ show n
|
|
legalbasename = makeLegalName basename
|
|
|
|
{- Finds a CredPair belonging to any Remote that is of a given type
|
|
- and matches some other criteria.
|
|
-
|
|
- This can be used as a default when another repository is being set up
|
|
- using the same service.
|
|
-
|
|
- A function must be provided that returns the CredPairStorage
|
|
- to use for a particular Remote's uuid.
|
|
-}
|
|
previouslyUsedCredPair
|
|
:: (UUID -> CredPairStorage)
|
|
-> RemoteType
|
|
-> (Remote -> Bool)
|
|
-> Annex (Maybe CredPair)
|
|
previouslyUsedCredPair getstorage remotetype criteria =
|
|
getM fromstorage
|
|
=<< filter criteria . filter sametype
|
|
<$> Remote.remoteList
|
|
where
|
|
sametype r = R.typename (R.remotetype r) == R.typename remotetype
|
|
fromstorage r = do
|
|
let storage = getstorage (R.uuid r)
|
|
getRemoteCredPair (R.config r) (R.gitconfig r) storage
|