map: Support --json option
authorJoey Hess <joeyh@joeyh.name>
Wed, 28 May 2025 18:17:28 +0000 (14:17 -0400)
committerJoey Hess <joeyh@joeyh.name>
Wed, 28 May 2025 18:17:28 +0000 (14:17 -0400)
Sponsored-by: Dartmouth College's OpenNeuro project
CHANGELOG
Command/Map.hs
doc/git-annex-map.mdwn
doc/todo/map__58___add_--json.mdwn
doc/todo/map__58___add_--json/comment_2_e9d4957cf5a3d5c015108cb1805df7da._comment [new file with mode: 0644]

index 789872e8c018f747d3e7904c6ae04e30010ba4dc..8fb85dfb1b3ee9476ef1e5092507283868a7ac49 100644 (file)
--- 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 <id@joeyh.name>  Thu, 22 May 2025 12:43:38 -0400
 
index ce28ca32c3ebb5070ec9966e23e0029b9bc2a63f..0deb5a6029f83b18424db9c4e03aa65d73c4947b 100644 (file)
@@ -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")
-
-       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)])
-                               ]       
-                       )
+       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)])
+                                               ]       
+                                       )
+               )
 
 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)
+
index debfa1c31a3dc20ff7296d4b0bbf98cbab95cf79..23585fdae26be4667550c930bfff7eafdf19ee06 100644 (file)
@@ -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
index 58b21a51f2850e8da752782e6ad28bf5fa6fc9cf..5b431ecc3eecd5cf0cbdd0280c800b396c06abe1 100644 (file)
@@ -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 (file)
index 0000000..15691af
--- /dev/null
@@ -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": []
+       }
+"""]]