diff --git a/cmd/depviz/main.go b/cmd/depviz/main.go index 6f66223c4..3fcf67314 100644 --- a/cmd/depviz/main.go +++ b/cmd/depviz/main.go @@ -74,6 +74,11 @@ var ( fetchFlags = flag.NewFlagSet("fetch", flag.ExitOnError) fetchGitHubToken = fetchFlags.String("github-token", "", "GitHub token") fetchResync = fetchFlags.Bool("resync", false, "resync already synced content") + + graphvizFlags = flag.NewFlagSet("graphviz", flag.ExitOnError) + graphvizLabel = graphvizFlags.String("label", "", "label to use for the graph") + graphvizType = graphvizFlags.String("type", "svg", "output type (svg, png, dot)") + graphvizFile = graphvizFlags.String("file", "", "output file (default: stdout)") ) func main() { @@ -130,7 +135,7 @@ func Main(args []string) error { Name: "gen", ShortHelp: "use the db to generate outputs, without requiring any fetch", Subcommands: []*ffcli.Command{ - {Name: "graphviz", Exec: execGenGraphviz, ShortHelp: "generate graphviz output"}, + {Name: "graphviz", Exec: execGenGraphviz, ShortHelp: "generate graphviz output", FlagSet: graphvizFlags}, {Name: "json", Exec: execGenJSON, ShortHelp: "generate JSON output"}, {Name: "csv", Exec: execGenCSV, ShortHelp: "generate CSV output"}, }, @@ -347,7 +352,35 @@ func storeFromArgs() (*cayley.Handle, error) { } func execGenGraphviz(ctx context.Context, args []string) error { - return fmt.Errorf("not implemented yet") + if err := globalPreRun(); err != nil { + return err + } + + store, err := storeFromArgs() + if err != nil { + return fmt.Errorf("init store: %w", err) + } + + genOpts := &dvcore.GenOpts{ + Logger: logger, + Schema: schemaConfig, + Vertical: *genVertical, + NoPert: *genNoPert, + NoGraph: *genNoGraph, + ShowClosed: *genShowClosed, + HideIsolated: *genHideIsolated, + HidePRs: *genHidePRs, + HideExternalDeps: *genHideExternalDeps, + } + + opts := dvcore.GraphvizOpts{ + GenOpts: genOpts, + Label: *graphvizLabel, + Type: *graphvizType, + File: *graphvizFile, + } + + return dvcore.GenGraphviz(store, args, opts) } func execGenJSON(ctx context.Context, args []string) error { diff --git a/go.mod b/go.mod index aedc0246b..af2255a43 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/go-chi/chi v1.5.4 github.com/go-chi/render v1.0.2 github.com/gobuffalo/packr/v2 v2.8.3 + github.com/goccy/go-graphviz v0.1.1 github.com/gogo/gateway v1.1.0 github.com/gogo/protobuf v1.3.2 github.com/golang/protobuf v1.5.2 @@ -31,7 +32,6 @@ require ( moul.io/banner v1.0.1 moul.io/godev v1.7.0 moul.io/graphman v1.6.0 - moul.io/graphman/viz v0.0.0-20210616043755-eef9a7ac05c7 moul.io/multipmuri v1.14.0 moul.io/srand v1.6.1 moul.io/u v1.27.0 @@ -40,13 +40,14 @@ require ( require ( github.com/ajg/form v1.5.1 // indirect - github.com/awalterschulze/gographviz v0.0.0-20190522210029-fa59802746ab // indirect github.com/beorn7/perks v1.0.0 // indirect github.com/boltdb/bolt v1.3.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dennwc/base v1.0.0 // indirect + github.com/fogleman/gg v1.3.0 // indirect github.com/gobuffalo/logger v1.0.6 // indirect github.com/gobuffalo/packd v1.0.1 // indirect + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/google/go-querystring v1.0.0 // indirect github.com/hidal-go/hidalgo v0.0.0-20190814174001-42e03f3b5eaa // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect @@ -57,6 +58,7 @@ require ( github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/piprate/json-gold v0.3.0 // indirect github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect github.com/prometheus/client_golang v0.9.3 // indirect @@ -70,7 +72,8 @@ require ( github.com/tylertreat/BoomFilters v0.0.0-20181028192813-611b3dbe80e8 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.7.0 // indirect - golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect + golang.org/x/crypto v0.7.0 // indirect + golang.org/x/image v0.6.0 // indirect golang.org/x/mod v0.8.0 // indirect golang.org/x/net v0.8.0 // indirect golang.org/x/sync v0.1.0 // indirect diff --git a/go.sum b/go.sum index 8c8e9fdff..c5a83ec23 100644 --- a/go.sum +++ b/go.sum @@ -57,8 +57,6 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/awalterschulze/gographviz v0.0.0-20190522210029-fa59802746ab h1:+cdNqtOJWjvepyhxy23G7z7vmpYCoC65AP0nqi1f53s= -github.com/awalterschulze/gographviz v0.0.0-20190522210029-fa59802746ab/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs= github.com/badgerodon/peg v0.0.0-20130729175151-9e5f7f4d07ca/go.mod h1:TWe0N2hv5qvpLHT+K16gYcGBllld4h65dQ/5CNuirmk= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -95,6 +93,7 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/corona10/goimagehash v1.0.2 h1:pUfB0LnsJASMPGEZLj7tGY251vF+qLGqOgEP4rUs6kA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -138,6 +137,8 @@ github.com/flimzy/diff v0.1.5/go.mod h1:lFJtC7SPsK0EroDmGTSrdtWKAxOk3rO+q+e04LL0 github.com/flimzy/diff v0.1.6/go.mod h1:lFJtC7SPsK0EroDmGTSrdtWKAxOk3rO+q+e04LL05Hs= github.com/flimzy/kivik v1.8.1/go.mod h1:S2aPycbG0eDFll4wgXt9uacSNkXISPufutnc9sv+mdA= github.com/flimzy/testy v0.1.16/go.mod h1:3szguN8NXqgq9bt9Gu8TQVj698PJWmyx/VY1frwwKrM= +github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -174,6 +175,8 @@ github.com/gobuffalo/packd v1.0.1/go.mod h1:PP2POP3p3RXGz7Jh6eYEf93S7vA2za6xM7QT github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc= github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XEWlY= github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc= +github.com/goccy/go-graphviz v0.1.1 h1:MGrsnzBxTyt7KG8FhHsFPDTGvF7UaQMmSa6A610DqPg= +github.com/goccy/go-graphviz v0.1.1/go.mod h1:lpnwvVDjskayq84ZxG8tGCPeZX/WxP88W+OJajh+gFk= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/gateway v1.1.0 h1:u0SuhL9+Il+UbjM9VIE3ntfRujKbvVpFvNB4HbjeVQ0= github.com/gogo/gateway v1.1.0/go.mod h1:S7rR8FRQyG3QFESeSv4l2WnsyzlCLG0CzBbUUo/mbic= @@ -184,6 +187,8 @@ github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -360,6 +365,7 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 h1:BvoENQQU+fZ9uukda/RzCAL/191HHwJA5b13R6diVlY= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -392,8 +398,9 @@ github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnI github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -503,6 +510,7 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= @@ -544,8 +552,10 @@ golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -558,6 +568,8 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.6.0 h1:bR8b5okrPI3g/gyZakLZHeWxAR8Dn5CyxXv1hLH5g/4= +golang.org/x/image v0.6.0/go.mod h1:MXLdDR43H7cDJq5GEGXEVeeNhPgi+YYEQ2pC1byI1x0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -581,6 +593,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -626,6 +639,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -654,6 +669,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -712,10 +728,14 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -726,6 +746,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -791,6 +813,7 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -953,8 +976,6 @@ moul.io/godev v1.7.0 h1:PgnL7BsCQPPjKwu9V0oxIVm2MyZAHAN2sl0S3+E37U0= moul.io/godev v1.7.0/go.mod h1:5lgSpI1oH7xWpLl2Ew/Nsgk8DiNM6FzN9WV9+lgW8RQ= moul.io/graphman v1.6.0 h1:p0pOkQDul7fv5R3pdQQZsYXaUIvLPfGMonznNa6cCG0= moul.io/graphman v1.6.0/go.mod h1:WfW75G37UTvAu09o4xdmcflGDn+VbZiwStaKmJKxkE0= -moul.io/graphman/viz v0.0.0-20210616043755-eef9a7ac05c7 h1:owwBeKfSYsjjl3EWIjs6ss+AUYy0/wedo17S3Ki//ek= -moul.io/graphman/viz v0.0.0-20210616043755-eef9a7ac05c7/go.mod h1:LrMvUXoSlBip3ZXSG7mFEVrDUjEKFVtLlLbInfEoJJs= moul.io/multipmuri v1.14.0 h1:zBVH1mbsYp4RoV1ubAT9zQK1LxhLIcDkat6VAyX6UrM= moul.io/multipmuri v1.14.0/go.mod h1:NinVrznZaHGUDU+4zfcL7L9WzMsHtb/kjLr4RaCbM+c= moul.io/srand v1.6.1 h1:SJ335F+54ivLdlH7wH52Rtyv0Ffos6DpsF5wu3ZVMXU= diff --git a/pkg/dvcore/gen.go b/pkg/dvcore/gen.go index b35a3b21d..3232b6934 100644 --- a/pkg/dvcore/gen.go +++ b/pkg/dvcore/gen.go @@ -18,19 +18,16 @@ import ( "moul.io/depviz/v3/pkg/githubprovider" "moul.io/godev" "moul.io/graphman" - "moul.io/graphman/viz" "moul.io/multipmuri" ) type GenOpts struct { // global - NoGraph bool Logger *zap.Logger Schema *schema.Config // graph - Format string Vertical bool NoPert bool @@ -82,49 +79,50 @@ func Gen(h *cayley.Handle, args []string, opts GenOpts) error { } fmt.Println(string(out)) return nil - case "dot": - // graph from PERT config - graph := graphman.FromPertConfig(*pertConfig) - - // initialize graph from config - if !opts.NoPert { - result := graphman.ComputePert(graph) - shortestPath, distance := graph.FindShortestPath("Start", "Finish") - opts.Logger.Debug("pert result", zap.Any("result", result), zap.Int64("distance", distance)) - - for _, edge := range shortestPath { - edge.Dst().SetColor("red") - edge.SetColor("red") - } - } - - // graph fine tuning - graph.GetVertex("Start").SetColor("blue") - graph.GetVertex("Finish").SetColor("blue") - if opts.Vertical { - graph.Attrs["rankdir"] = "TB" - } - graph.Attrs["overlap"] = "false" - graph.Attrs["pack"] = "true" - graph.Attrs["splines"] = "true" - graph.Attrs["sep"] = "0.1" - // graph.Attrs["layout"] = "neato" - // graph.Attrs["size"] = "\"11,11\"" - // graph.Attrs["start"] = "random" - // FIXME: hightlight critical paths - // FIXME: highlight other infos - // FIXME: highlight target - - // graphviz - s, err := viz.ToGraphviz(graph, &viz.Opts{ - CommentsInLabel: true, - }) - if err != nil { - return fmt.Errorf("graphviz: %w", err) - } - - fmt.Println(s) - return nil + // TODO: fix many issues with generated dependencies + //case "dot": + // // graph from PERT config + // graph := graphman.FromPertConfig(*pertConfig) + // + // // initialize graph from config + // if !opts.NoPert { + // result := graphman.ComputePert(graph) + // shortestPath, distance := graph.FindShortestPath("Start", "Finish") + // opts.Logger.Debug("pert result", zap.Any("result", result), zap.Int64("distance", distance)) + // + // for _, edge := range shortestPath { + // edge.Dst().SetColor("red") + // edge.SetColor("red") + // } + // } + // + // // graph fine tuning + // graph.GetVertex("Start").SetColor("blue") + // graph.GetVertex("Finish").SetColor("blue") + // if opts.Vertical { + // graph.Attrs["rankdir"] = "TB" + // } + // graph.Attrs["overlap"] = "false" + // graph.Attrs["pack"] = "true" + // graph.Attrs["splines"] = "true" + // graph.Attrs["sep"] = "0.1" + // // graph.Attrs["layout"] = "neato" + // // graph.Attrs["size"] = "\"11,11\"" + // // graph.Attrs["start"] = "random" + // // FIXME: hightlight critical paths + // // FIXME: highlight other infos + // // FIXME: highlight target + // + // // graphviz + // s, err := viz.ToGraphviz(graph, &viz.Opts{ + // CommentsInLabel: true, + // }) + // if err != nil { + // return fmt.Errorf("graphviz: %w", err) + // } + // + // fmt.Println(s) + // return nil case "quads": return fmt.Errorf("not implemented") default: diff --git a/pkg/dvcore/graphviz.go b/pkg/dvcore/graphviz.go new file mode 100644 index 000000000..7e5ce7ff5 --- /dev/null +++ b/pkg/dvcore/graphviz.go @@ -0,0 +1,119 @@ +package dvcore + +import ( + "fmt" + "os" + "strings" + + "github.com/cayleygraph/cayley" + "github.com/cayleygraph/quad" + "github.com/goccy/go-graphviz" + "github.com/goccy/go-graphviz/cgraph" + "go.uber.org/zap" + "moul.io/depviz/v3/pkg/dvmodel" + "moul.io/depviz/v3/pkg/dvparser" + "moul.io/depviz/v3/pkg/dvstore" +) + +type GraphvizOpts struct { + *GenOpts + + // graphviz + Label string + Type string + File string +} + +func GenGraphviz(h *cayley.Handle, args []string, opts GraphvizOpts) error { + if opts.Logger == nil { + opts.Logger = zap.NewNop() + } + opts.Logger.Debug("Gen called", zap.Strings("args", args), zap.Any("opts", opts)) + + targets, err := dvparser.ParseTargets(args) + if err != nil { + return fmt.Errorf("parse targets: %w", err) + } + + filters := dvstore.LoadTasksFilters{ + Targets: targets, + WithClosed: opts.ShowClosed, + WithoutIsolated: opts.HideIsolated, + WithoutPRs: opts.HidePRs, + WithoutExternalDeps: opts.HideExternalDeps, + } + tasks, err := dvstore.LoadTasks(h, opts.Schema, filters, opts.Logger) + if err != nil { + return fmt.Errorf("load tasks: %w", err) + } + + roadmap := make(map[string]dvmodel.Task) + for _, t := range tasks { + if t.Kind != 1 { + continue + } + roadmap[fmtIRI(t.ID)] = t + } + + g := graphviz.New() + graph, err := g.Graph() + if err != nil { + return fmt.Errorf("create graph: %w", err) + } + + graph.SetRankDir("LR") + graph.SetLabel(opts.Label) + defer func() { + if graph.Close() != nil { + panic("graph.Close() failed") + } + g.Close() + }() + + nodes := make(map[string]*cgraph.Node) + + for _, task := range roadmap { + node, err := graph.CreateNode(fmtIRI(task.ID)) + if err != nil { + return fmt.Errorf("create node: %w", err) + } + + node.SetLabel(task.Title) + node.SetHref(fmtIRI(task.ID)) + node.SetShape("box") + node.SetStyle("rounded") + + task.ApplyLabel(node) + + nodes[fmtIRI(task.ID)] = node + } + + for _, task := range roadmap { + for _, dependentID := range task.IsBlocking { + dependent := roadmap[fmtIRI(dependentID)] + name := task.ID + dependent.ID + edge, err := graph.CreateEdge(fmtIRI(name), nodes[fmtIRI(task.ID)], nodes[fmtIRI(dependent.ID)]) + if err != nil { + return fmt.Errorf("create dependent edge: %w", err) + } + _ = edge + } + for _, dependingID := range task.IsDependingOn { + depending := roadmap[fmtIRI(dependingID)] + name := depending.ID + task.ID + edge, err := graph.CreateEdge(fmtIRI(name), nodes[fmtIRI(depending.ID)], nodes[fmtIRI(task.ID)]) + if err != nil { + return fmt.Errorf("create depending edge: %w", err) + } + _ = edge + } + } + if opts.File == "" { + return g.Render(graph, graphviz.Format(opts.Type), os.Stdout) + } + return g.RenderFilename(graph, graphviz.Format(opts.Type), opts.File) +} + +func fmtIRI(s quad.IRI) string { + return strings.Replace(strings.Replace(s.String(), "<", "", -1), ">", "", -1) +} diff --git a/pkg/dvmodel/dvmodel.pb.go b/pkg/dvmodel/dvmodel.pb.go index 829c4ee9f..ec671cade 100644 --- a/pkg/dvmodel/dvmodel.pb.go +++ b/pkg/dvmodel/dvmodel.pb.go @@ -217,7 +217,8 @@ func (Topic_Kind) EnumDescriptor() ([]byte, []int) { // Owner is like a container of tasks or other containers. // It's something that is rarely deleted and cannot really "closed" or "due". // It's the entity used for Organizations, Teams, Groups, Users -// and for Projects, Workspaces, Repos, or a Provider. +// +// and for Projects, Workspaces, Repos, or a Provider. type Owner struct { ID github_com_cayleygraph_quad.IRI `protobuf:"bytes,1,opt,name=id,proto3,casttype=github.com/cayleygraph/quad.IRI" json:"id,omitempty" quad:"@id"` CreatedAt *time.Time `protobuf:"bytes,3,opt,name=created_at,json=createdAt,proto3,stdtime" json:"created_at,omitempty" quad:"schema:createdAt,optional"` diff --git a/pkg/dvmodel/task.go b/pkg/dvmodel/task.go index fcc90541f..ed628a3d9 100644 --- a/pkg/dvmodel/task.go +++ b/pkg/dvmodel/task.go @@ -1,7 +1,10 @@ package dvmodel import ( + "strings" + "github.com/cayleygraph/quad" + "github.com/goccy/go-graphviz/cgraph" "go.uber.org/zap" ) @@ -81,3 +84,32 @@ func (t *Task) MarshalCSV() []string { // t.IsBlocking, } } + +type fmtLabel struct { + label string + style string + color string +} + +// special depviz labels, used to colorize nodes in the graphviz generation +// TODO: determine a way to create 'themes' with custom config with the following format +var depvizLabels = [...]fmtLabel{ + {"focus", "filled,bold,rounded", "#ffeeee"}, + {"vision", "filled,rounded", "#eeeeee"}, +} + +// ApplyLabel apply modifications to the Node based on the label of the task +func (t Task) ApplyLabel(node *cgraph.Node) { + if t.Driver == Driver_GitHub { + for _, label := range t.HasLabel { + s, _ := strings.CutPrefix(label.String(), t.HasOwner.String()+"/labels/") + for _, dl := range depvizLabels { + if s == dl.label { + node.SetStyle(cgraph.NodeStyle(dl.style)) + node.SetColor(dl.color) + return + } + } + } + } +} diff --git a/pkg/dvstore/testing.go b/pkg/dvstore/testing.go index 2ce2e2836..ea427549c 100644 --- a/pkg/dvstore/testing.go +++ b/pkg/dvstore/testing.go @@ -8,11 +8,9 @@ import ( "github.com/cayleygraph/cayley" "github.com/cayleygraph/cayley/graph" - // required by cayley _ "github.com/cayleygraph/cayley/graph/kv/bolt" "github.com/cayleygraph/quad" - // required by cayley _ "github.com/cayleygraph/quad/gml" // required by cayley