refactored and generalized pre-command sanity checking

This commit is contained in:
Joey Hess 2011-10-27 16:31:35 -04:00
parent 66194684ac
commit 5b74b130a3
38 changed files with 73 additions and 67 deletions

View file

@ -21,7 +21,6 @@ import qualified Git
import Annex.Content import Annex.Content
import Command import Command
import Options import Options
import Init
{- Runs the passed command line. -} {- Runs the passed command line. -}
dispatch :: [String] -> [Command] -> [Option] -> String -> Git.Repo -> IO () dispatch :: [String] -> [Command] -> [Option] -> String -> Git.Repo -> IO ()
@ -41,7 +40,7 @@ parseCmd argv header cmds options = do
[] -> error $ "unknown command" ++ usagemsg [] -> error $ "unknown command" ++ usagemsg
[command] -> do [command] -> do
_ <- sequence flags _ <- sequence flags
checkCmdEnviron command checkCommand command
prepCommand command (drop 1 params) prepCommand command (drop 1 params)
_ -> error "internal error: multiple matching commands" _ -> error "internal error: multiple matching commands"
where where
@ -53,10 +52,6 @@ parseCmd argv header cmds options = do
lookupCmd cmd = filter (\c -> cmd == cmdname c) cmds lookupCmd cmd = filter (\c -> cmd == cmdname c) cmds
usagemsg = "\n\n" ++ usage header cmds options usagemsg = "\n\n" ++ usage header cmds options
{- Checks that the command can be run in the current environment. -}
checkCmdEnviron :: Command -> Annex ()
checkCmdEnviron command = when (cmdusesrepo command) ensureInitialized
{- Usage message with lists of commands and options. -} {- Usage message with lists of commands and options. -}
usage :: String -> [Command] -> [Option] -> String usage :: String -> [Command] -> [Option] -> String
usage header cmds options = usage header cmds options =

View file

@ -18,42 +18,38 @@ import Logs.Location
import Config import Config
import Backend import Backend
import Limit import Limit
import Init
{- A command runs in four stages. {- A command runs in these stages.
- -
- 0. The seek stage takes the parameters passed to the command, - a. The check stage is run once and should error out if anything
- prevents the command from running. -}
type CommandCheck = Annex ()
{- b. The seek stage takes the parameters passed to the command,
- looks through the repo to find the ones that are relevant - looks through the repo to find the ones that are relevant
- to that command (ie, new files to add), and generates - to that command (ie, new files to add), and generates
- a list of start stage actions. -} - a list of start stage actions. -}
type CommandSeek = [String] -> Annex [CommandStart] type CommandSeek = [String] -> Annex [CommandStart]
{- 1. The start stage is run before anything is printed about the {- c. The start stage is run before anything is printed about the
- command, is passed some input, and can early abort it - command, is passed some input, and can early abort it
- if the input does not make sense. It should run quickly and - if the input does not make sense. It should run quickly and
- should not modify Annex state. -} - should not modify Annex state. -}
type CommandStart = Annex (Maybe CommandPerform) type CommandStart = Annex (Maybe CommandPerform)
{- 2. The perform stage is run after a message is printed about the command {- d. The perform stage is run after a message is printed about the command
- being run, and it should be where the bulk of the work happens. -} - being run, and it should be where the bulk of the work happens. -}
type CommandPerform = Annex (Maybe CommandCleanup) type CommandPerform = Annex (Maybe CommandCleanup)
{- 3. The cleanup stage is run only if the perform stage succeeds, and it {- e. The cleanup stage is run only if the perform stage succeeds, and it
- returns the overall success/fail of the command. -} - returns the overall success/fail of the command. -}
type CommandCleanup = Annex Bool type CommandCleanup = Annex Bool
data Command = Command { data Command = Command {
cmdusesrepo :: Bool,
cmdname :: String, cmdname :: String,
cmdparams :: String, cmdparams :: String,
cmdcheck :: CommandCheck,
cmdseek :: [CommandSeek], cmdseek :: [CommandSeek],
cmddesc :: String cmddesc :: String
} }
{- Most commands operate on files in a git repo. -}
repoCommand :: String -> String -> [CommandSeek] -> String -> Command
repoCommand = Command True
{- Others can run anywhere. -}
standaloneCommand :: String -> String -> [CommandSeek] -> String -> Command
standaloneCommand = Command False
{- For start and perform stages to indicate what step to run next. -} {- For start and perform stages to indicate what step to run next. -}
next :: a -> Annex (Maybe a) next :: a -> Annex (Maybe a)
next a = return $ Just a next a = return $ Just a
@ -62,6 +58,18 @@ next a = return $ Just a
stop :: Annex (Maybe a) stop :: Annex (Maybe a)
stop = return Nothing stop = return Nothing
needsNothing :: CommandCheck
needsNothing = return ()
{- Most commands will check this, as they need to be run in an initialized
- repo. -}
needsRepo :: CommandCheck
needsRepo = ensureInitialized
{- Checks that the command can be run in the current environment. -}
checkCommand :: Command -> Annex ()
checkCommand Command { cmdcheck = check } = check
{- Prepares a list of actions to run to perform a command, based on {- Prepares a list of actions to run to perform a command, based on
- the parameters passed to it. -} - the parameters passed to it. -}
prepCommand :: Command -> [String] -> Annex [Annex Bool] prepCommand :: Command -> [String] -> Annex [Annex Bool]

View file

@ -19,7 +19,7 @@ import Utility.Touch
import Backend import Backend
command :: [Command] command :: [Command]
command = [repoCommand "add" paramPaths seek "add files to annex"] command = [Command "add" paramPaths needsRepo seek "add files to annex"]
{- Add acts on both files not checked into git yet, and unlocked files. -} {- Add acts on both files not checked into git yet, and unlocked files. -}
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -20,7 +20,7 @@ import Annex.Content
import Logs.Web import Logs.Web
command :: [Command] command :: [Command]
command = [repoCommand "addurl" (paramRepeating paramUrl) seek command = [Command "addurl" (paramRepeating paramUrl) needsRepo seek
"add urls to annex"] "add urls to annex"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -12,7 +12,7 @@ import Command
import Annex.UUID import Annex.UUID
command :: [Command] command :: [Command]
command = [repoCommand "configlist" paramNothing seek command = [Command "configlist" paramNothing needsRepo seek
"outputs relevant git configuration"] "outputs relevant git configuration"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -11,7 +11,7 @@ import Command
import qualified Command.Move import qualified Command.Move
command :: [Command] command :: [Command]
command = [repoCommand "copy" paramPaths seek command = [Command "copy" paramPaths needsRepo seek
"copy content of files to/from another repository"] "copy content of files to/from another repository"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -13,7 +13,7 @@ import qualified Remote
import Logs.UUID import Logs.UUID
command :: [Command] command :: [Command]
command = [repoCommand "describe" (paramPair paramRemote paramDesc) seek command = [Command "describe" (paramPair paramRemote paramDesc) needsRepo seek
"change description of a repository"] "change description of a repository"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -17,7 +17,7 @@ import Annex.Content
import Config import Config
command :: [Command] command :: [Command]
command = [repoCommand "drop" paramPaths seek command = [Command "drop" paramPaths needsRepo seek
"indicate content of files not currently wanted"] "indicate content of files not currently wanted"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -14,7 +14,7 @@ import Logs.Location
import Annex.Content import Annex.Content
command :: [Command] command :: [Command]
command = [repoCommand "dropkey" (paramRepeating paramKey) seek command = [Command "dropkey" (paramRepeating paramKey) needsRepo seek
"drops annexed content for specified keys"] "drops annexed content for specified keys"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -21,7 +21,7 @@ import Types.Key
type UnusedMap = M.Map String Key type UnusedMap = M.Map String Key
command :: [Command] command :: [Command]
command = [repoCommand "dropunused" (paramRepeating paramNumber) seek command = [Command "dropunused" (paramRepeating paramNumber) needsRepo seek
"drop unused file content"] "drop unused file content"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -13,7 +13,7 @@ import Annex.Content
import Limit import Limit
command :: [Command] command :: [Command]
command = [repoCommand "find" paramPaths seek "lists available files"] command = [Command "find" paramPaths needsRepo seek "lists available files"]
seek :: [CommandSeek] seek :: [CommandSeek]
seek = [withFilesInGit start] seek = [withFilesInGit start]

View file

@ -13,7 +13,7 @@ import qualified Annex.Queue
import Annex.Content import Annex.Content
command :: [Command] command :: [Command]
command = [repoCommand "fix" paramPaths seek command = [Command "fix" paramPaths needsRepo seek
"fix up symlinks to point to annexed content"] "fix up symlinks to point to annexed content"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -14,7 +14,7 @@ import Annex.Content
import Types.Key import Types.Key
command :: [Command] command :: [Command]
command = [repoCommand "fromkey" paramPath seek command = [Command "fromkey" paramPath needsRepo seek
"adds a file using a specific key"] "adds a file using a specific key"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -21,7 +21,7 @@ import Utility.FileMode
import Config import Config
command :: [Command] command :: [Command]
command = [repoCommand "fsck" paramPaths seek "check for problems"] command = [Command "fsck" paramPaths needsRepo seek "check for problems"]
seek :: [CommandSeek] seek :: [CommandSeek]
seek = [withNumCopies start] seek = [withNumCopies start]

View file

@ -15,7 +15,7 @@ import Annex.Content
import qualified Command.Move import qualified Command.Move
command :: [Command] command :: [Command]
command = [repoCommand "get" paramPaths seek command = [Command "get" paramPaths needsRepo seek
"make content of annexed files available"] "make content of annexed files available"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -12,7 +12,7 @@ import Command
import Annex.Content import Annex.Content
command :: [Command] command :: [Command]
command = [repoCommand "inannex" (paramRepeating paramKey) seek command = [Command "inannex" (paramRepeating paramKey) needsRepo seek
"checks if keys are present in the annex"] "checks if keys are present in the annex"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -14,8 +14,7 @@ import Logs.UUID
import Init import Init
command :: [Command] command :: [Command]
command = [standaloneCommand "init" paramDesc seek command = [Command "init" paramDesc needsNothing seek "initialize git-annex"]
"initialize git-annex"]
seek :: [CommandSeek] seek :: [CommandSeek]
seek = [withWords start] seek = [withWords start]

View file

@ -17,10 +17,9 @@ import qualified Types.Remote as R
import Annex.UUID import Annex.UUID
command :: [Command] command :: [Command]
command = [repoCommand "initremote" command = [Command "initremote"
(paramPair paramName $ (paramPair paramName $ paramOptional $ paramRepeating paramKeyValue)
paramOptional $ paramRepeating paramKeyValue) seek needsRepo seek "sets up a special (non-git) remote"]
"sets up a special (non-git) remote"]
seek :: [CommandSeek] seek :: [CommandSeek]
seek = [withWords start] seek = [withWords start]

View file

@ -13,7 +13,7 @@ import qualified Annex.Queue
import Backend import Backend
command :: [Command] command :: [Command]
command = [repoCommand "lock" paramPaths seek "undo unlock command"] command = [Command "lock" paramPaths needsRepo seek "undo unlock command"]
seek :: [CommandSeek] seek :: [CommandSeek]
seek = [withFilesUnlocked start, withFilesUnlockedToBeCommitted start] seek = [withFilesUnlocked start, withFilesUnlockedToBeCommitted start]

View file

@ -23,7 +23,8 @@ import qualified Utility.Dot as Dot
data Link = Link Git.Repo Git.Repo data Link = Link Git.Repo Git.Repo
command :: [Command] command :: [Command]
command = [repoCommand "map" paramNothing seek "generate map of repositories"] command = [Command "map" paramNothing needsNothing seek
"generate map of repositories"]
seek :: [CommandSeek] seek :: [CommandSeek]
seek = [withNothing start] seek = [withNothing start]

View file

@ -12,7 +12,7 @@ import Command
import qualified Annex.Branch import qualified Annex.Branch
command :: [Command] command :: [Command]
command = [repoCommand "merge" paramNothing seek command = [Command "merge" paramNothing needsRepo seek
"auto-merge remote changes into git-annex branch"] "auto-merge remote changes into git-annex branch"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -17,7 +17,7 @@ import Backend
import Logs.Web import Logs.Web
command :: [Command] command :: [Command]
command = [repoCommand "migrate" paramPaths seek command = [Command "migrate" paramPaths needsRepo seek
"switch data to different backend"] "switch data to different backend"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -17,7 +17,7 @@ import qualified Remote
import Annex.UUID import Annex.UUID
command :: [Command] command :: [Command]
command = [repoCommand "move" paramPaths seek command = [Command "move" paramPaths needsRepo seek
"move content of files to/from another repository"] "move content of files to/from another repository"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -13,7 +13,8 @@ import qualified Command.Fix
import Backend import Backend
command :: [Command] command :: [Command]
command = [repoCommand "pre-commit" paramPaths seek "run by git pre-commit hook"] command = [Command "pre-commit" paramPaths needsRepo seek
"run by git pre-commit hook"]
{- The pre-commit hook needs to fix symlinks to all files being committed. {- The pre-commit hook needs to fix symlinks to all files being committed.
- And, it needs to inject unlocked files into the annex. -} - And, it needs to inject unlocked files into the annex. -}

View file

@ -14,7 +14,7 @@ import Annex.Content
import Utility.RsyncFile import Utility.RsyncFile
command :: [Command] command :: [Command]
command = [repoCommand "recvkey" paramKey seek command = [Command "recvkey" paramKey needsRepo seek
"runs rsync in server mode to receive content"] "runs rsync in server mode to receive content"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -13,7 +13,7 @@ import qualified Remote
import Logs.Trust import Logs.Trust
command :: [Command] command :: [Command]
command = [repoCommand "semitrust" (paramRepeating paramRemote) seek command = [Command "semitrust" (paramRepeating paramRemote) needsRepo seek
"return repository to default trust level"] "return repository to default trust level"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -13,7 +13,7 @@ import Annex.Content
import Utility.RsyncFile import Utility.RsyncFile
command :: [Command] command :: [Command]
command = [repoCommand "sendkey" paramKey seek command = [Command "sendkey" paramKey needsRepo seek
"runs rsync in server mode to send content"] "runs rsync in server mode to send content"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -13,7 +13,7 @@ import Logs.Location
import Annex.Content import Annex.Content
command :: [Command] command :: [Command]
command = [repoCommand "setkey" paramPath seek command = [Command "setkey" paramPath needsRepo seek
"sets annexed content for a key using a temp file"] "sets annexed content for a key using a temp file"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -39,7 +39,7 @@ data StatInfo = StatInfo
type StatState = StateT StatInfo Annex type StatState = StateT StatInfo Annex
command :: [Command] command :: [Command]
command = [repoCommand "status" paramNothing seek command = [Command "status" paramNothing needsRepo seek
"shows status information about the annex"] "shows status information about the annex"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -13,7 +13,7 @@ import qualified Remote
import Logs.Trust import Logs.Trust
command :: [Command] command :: [Command]
command = [repoCommand "trust" (paramRepeating paramRemote) seek command = [Command "trust" (paramRepeating paramRemote) needsRepo seek
"trust a repository"] "trust a repository"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -19,7 +19,8 @@ import qualified Git
import qualified Git.LsFiles as LsFiles import qualified Git.LsFiles as LsFiles
command :: [Command] command :: [Command]
command = [repoCommand "unannex" paramPaths seek "undo accidential add command"] command = [Command "unannex" paramPaths needsRepo seek
"undo accidential add command"]
seek :: [CommandSeek] seek :: [CommandSeek]
seek = [withFilesInGit start] seek = [withFilesInGit start]

View file

@ -19,18 +19,15 @@ import qualified Annex.Branch
import Annex.Content import Annex.Content
command :: [Command] command :: [Command]
command = [repoCommand "uninit" paramPaths seek command = [Command "uninit" paramPaths check seek
"de-initialize git-annex and clean out repository"] "de-initialize git-annex and clean out repository"]
seek :: [CommandSeek] check :: Annex ()
seek = [withNothing startCheck, withFilesInGit startUnannex, withNothing start] check = do
needsRepo
startCheck :: CommandStart
startCheck = do
b <- current_branch b <- current_branch
when (b == Annex.Branch.name) $ error $ when (b == Annex.Branch.name) $ error $
"cannot uninit when the " ++ b ++ " branch is checked out" "cannot uninit when the " ++ b ++ " branch is checked out"
stop
where where
current_branch = do current_branch = do
g <- gitRepo g <- gitRepo
@ -38,6 +35,9 @@ startCheck = do
Git.pipeRead g [Params "rev-parse --abbrev-ref HEAD"] Git.pipeRead g [Params "rev-parse --abbrev-ref HEAD"]
return $ head $ lines $ B.unpack b return $ head $ lines $ B.unpack b
seek :: [CommandSeek]
seek = [withFilesInGit startUnannex, withNothing start]
startUnannex :: FilePath -> CommandStart startUnannex :: FilePath -> CommandStart
startUnannex file = do startUnannex file = do
-- Force fast mode before running unannex. This way, if multiple -- Force fast mode before running unannex. This way, if multiple

View file

@ -15,9 +15,11 @@ import Utility.FileMode
command :: [Command] command :: [Command]
command = command =
[ repoCommand "unlock" paramPaths seek "unlock files for modification" [ c "unlock" "unlock files for modification"
, repoCommand "edit" paramPaths seek "same as unlock" , c "edit" "same as unlock"
] ]
where
c n = Command n paramPaths needsRepo seek
seek :: [CommandSeek] seek :: [CommandSeek]
seek = [withFilesInGit start] seek = [withFilesInGit start]

View file

@ -13,7 +13,7 @@ import qualified Remote
import Logs.Trust import Logs.Trust
command :: [Command] command :: [Command]
command = [repoCommand "untrust" (paramRepeating paramRemote) seek command = [Command "untrust" (paramRepeating paramRemote) needsRepo seek
"do not trust a repository"] "do not trust a repository"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -28,7 +28,7 @@ import qualified Annex.Branch
import Annex.CatFile import Annex.CatFile
command :: [Command] command :: [Command]
command = [repoCommand "unused" paramNothing seek command = [Command "unused" paramNothing needsRepo seek
"look for unused file content"] "look for unused file content"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -13,7 +13,7 @@ import Upgrade
import Annex.Version import Annex.Version
command :: [Command] command :: [Command]
command = [standaloneCommand "upgrade" paramNothing seek command = [Command "upgrade" paramNothing needsNothing seek
"upgrade repository layout"] "upgrade repository layout"]
seek :: [CommandSeek] seek :: [CommandSeek]

View file

@ -13,7 +13,7 @@ import qualified Build.SysConfig as SysConfig
import Annex.Version import Annex.Version
command :: [Command] command :: [Command]
command = [standaloneCommand "version" paramNothing seek "show version info"] command = [Command "version" paramNothing needsNothing seek "show version info"]
seek :: [CommandSeek] seek :: [CommandSeek]
seek = [withNothing start] seek = [withNothing start]

View file

@ -14,7 +14,7 @@ import Remote
import Logs.Trust import Logs.Trust
command :: [Command] command :: [Command]
command = [repoCommand "whereis" paramPaths seek command = [Command "whereis" paramPaths needsRepo seek
"lists repositories that have file content"] "lists repositories that have file content"]
seek :: [CommandSeek] seek :: [CommandSeek]