From 52a8b5b11773f58149ee96b29f7ce37f661cdde0 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Wed, 28 May 2025 14:17:28 -0400 Subject: [PATCH] map: Support --json option Sponsored-by: Dartmouth College's OpenNeuro project --- CHANGELOG | 1 + Command/Map.hs | 72 ++++++++++++++----- doc/git-annex-map.mdwn | 4 ++ doc/todo/map__58___add_--json.mdwn | 2 + ..._e9d4957cf5a3d5c015108cb1805df7da._comment | 51 +++++++++++++ 5 files changed, 113 insertions(+), 17 deletions(-) create mode 100644 doc/todo/map__58___add_--json/comment_2_e9d4957cf5a3d5c015108cb1805df7da._comment diff --git a/CHANGELOG b/CHANGELOG index 789872e8c0..8fb85dfb1b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,7 @@ git-annex (10.20250521) UNRELEASED; urgency=medium configured but fails, prevent initialization. This allows the user to fix their configuration and avoid crippled filesystem detection entering an adjusted branch. + * map: Support --json option. -- Joey Hess Thu, 22 May 2025 12:43:38 -0400 diff --git a/Command/Map.hs b/Command/Map.hs index ce28ca32c3..0deb5a6029 100644 --- a/Command/Map.hs +++ b/Command/Map.hs @@ -9,8 +9,6 @@ module Command.Map where -import qualified Data.Map as M - import Command import qualified Git import qualified Git.Url @@ -25,12 +23,17 @@ import Logs.Trust import Types.TrustLevel import qualified Remote.Helper.Ssh as Ssh import qualified Utility.Dot as Dot +import qualified Messages.JSON as JSON +import Messages.JSON ((.=)) +import Utility.Aeson (packString) + +import qualified Data.Map as M -- a repo and its remotes type RepoRemotes = (Git.Repo, [Git.Repo]) cmd :: Command -cmd = dontCheck repoExists $ +cmd = dontCheck repoExists $ withAnnexOptions [jsonOptions] $ command "map" SectionQuery "generate map of repositories" paramNothing (withParams seek) @@ -45,19 +48,23 @@ start = startingNoMessage (ActionItemOther Nothing) $ do umap <- uuidDescMap trustmap <- trustMapLoad - file <- () - <$> fromRepo gitAnnexDir - <*> pure (literalOsPath "map.dot") + ifM (outputJSONMap rs trustmap umap) + ( next $ return True + , do + file <- () + <$> fromRepo gitAnnexDir + <*> pure (literalOsPath "map.dot") - liftIO $ writeFile (fromOsPath file) (drawMap rs trustmap umap) - next $ - ifM (Annex.getRead Annex.fast) - ( runViewer file [] - , runViewer file - [ ("xdot", [File (fromOsPath file)]) - , ("dot", [Param "-Tx11", File (fromOsPath file)]) - ] - ) + liftIO $ writeFile (fromOsPath file) (drawMap rs trustmap umap) + next $ + ifM (Annex.getRead Annex.fast) + ( runViewer file [] + , runViewer file + [ ("xdot", [File (fromOsPath file)]) + , ("dot", [Param "-Tx11", File (fromOsPath file)]) + ] + ) + ) runViewer :: OsPath -> [(String, [CommandParam])] -> Annex Bool runViewer file [] = do @@ -198,7 +205,8 @@ same a b {- reads the config of a remote, with progress display -} scan :: Git.Repo -> Annex Git.Repo scan r = do - showStartMessage (StartMessage "map" (ActionItemOther (Just $ UnquotedString $ Git.repoDescribe r)) (SeekInput [])) + unlessM jsonOutputEnabled $ + showStartMessage (StartMessage "map" (ActionItemOther (Just $ UnquotedString $ Git.repoDescribe r)) (SeekInput [])) v <- tryScan r case v of Just r' -> do @@ -269,7 +277,7 @@ tryScan r configlist ok -> return ok - sshnote = do + sshnote = unlessM jsonOutputEnabled $ do showAction "sshing" showOutput @@ -287,3 +295,33 @@ safely a = do case result of Left _ -> return Nothing Right r' -> return $ Just r' + +outputJSONMap :: [RepoRemotes] -> TrustMap -> UUIDDescMap -> Annex Bool +outputJSONMap rs trustmap umap = + showFullJSON $ JSON.AesonObject $ case mapo of + JSON.Object obj -> obj + _ -> error "internal" + where + mapo = JSON.object + [ "nodes" .= map mknode (filterdead fst rs) + ] + + mknode (r, remotes) = JSON.object + [ "name" .= packString (repoName umap r) + , "uuid" .= mkuuid (getUncachedUUID r) + , "url" .= packString (Git.repoLocation r) + , "remotes" .= map mkremote (filterdead id remotes) + ] + + mkremote r = JSON.object + [ "name" .= packString (repoName umap r) + , "uuid" .= mkuuid (getUncachedUUID r) + , "url" .= packString (Git.repoLocation r) + ] + + mkuuid NoUUID = Nothing + mkuuid u = Just $ packString $ fromUUID u + + filterdead f = filter + (\i -> M.lookup (getUncachedUUID (f i)) trustmap /= Just DeadTrusted) + diff --git a/doc/git-annex-map.mdwn b/doc/git-annex-map.mdwn index debfa1c31a..23585fdae2 100644 --- a/doc/git-annex-map.mdwn +++ b/doc/git-annex-map.mdwn @@ -39,6 +39,10 @@ on that host. Don't display the generated Graphviz file, but save it for later use. +* `--json` + + Output the map as a JSON object. + * Also the [[git-annex-common-options]](1) can be used. # SEE ALSO diff --git a/doc/todo/map__58___add_--json.mdwn b/doc/todo/map__58___add_--json.mdwn index 58b21a51f2..5b431ecc3e 100644 --- a/doc/todo/map__58___add_--json.mdwn +++ b/doc/todo/map__58___add_--json.mdwn @@ -6,3 +6,5 @@ Please let me know on how feasible that would be, and any other thoughts you hav [[!meta author=yoh]] [[!tag projects/openneuro]] + +> [[done]] --[[Joey]] diff --git a/doc/todo/map__58___add_--json/comment_2_e9d4957cf5a3d5c015108cb1805df7da._comment b/doc/todo/map__58___add_--json/comment_2_e9d4957cf5a3d5c015108cb1805df7da._comment new file mode 100644 index 0000000000..15691af277 --- /dev/null +++ b/doc/todo/map__58___add_--json/comment_2_e9d4957cf5a3d5c015108cb1805df7da._comment @@ -0,0 +1,51 @@ +[[!comment format=mdwn + username="joey" + subject="""comment 2""" + date="2025-05-28T18:11:34Z" + content=""" +I went ahead and implemented `git-annx map --json`. + +Example output, after being passed through `jq` to pretty-print it: + + { + "nodes": [ + { + "name": "joey@darkstar:~/tmp/mapbench/a", + "remotes": [ + { + "name": "joey@darkstar:~/tmp/mapbench/b", + "url": "/home/joey/tmp/mapbench/b", + "uuid": "645d92d8-6461-43c1-b23c-6dd04dc3a015" + } + ], + "url": "/home/joey/tmp/mapbench/a", + "uuid": "3f34e4c2-dd19-433a-ab04-9fd4be959325" + }, + { + "name": "joey@darkstar:~/tmp/mapbench/b", + "remotes": [ + { + "name": "joey@darkstar:~/tmp/mapbench/a", + "url": "/home/joey/tmp/mapbench/a", + "uuid": "3f34e4c2-dd19-433a-ab04-9fd4be959325" + } + ], + "url": "/home/joey/tmp/mapbench/b", + "uuid": "645d92d8-6461-43c1-b23c-6dd04dc3a015" + }, + { + "name": "unknown", + "remotes": [ + { + "name": "joey@darkstar:~/tmp/mapbench/b", + "url": "/home/joey/tmp/mapbench/b", + "uuid": "645d92d8-6461-43c1-b23c-6dd04dc3a015" + } + ], + "url": ".", + "uuid": null + } + ], + "error-messages": [] + } +"""]]