@@ -20,6 +20,7 @@ import (
20
20
type MermaidWriter struct {
21
21
MinEdgeName string
22
22
SpecifiedPackageName string
23
+ UseV0Semantics bool
23
24
}
24
25
25
26
type MermaidOption func (* MermaidWriter )
@@ -52,6 +53,12 @@ func WithSpecifiedPackageName(specifiedPackageName string) MermaidOption {
52
53
}
53
54
}
54
55
56
+ func WithV0Semantics () MermaidOption {
57
+ return func (o * MermaidWriter ) {
58
+ o .UseV0Semantics = true
59
+ }
60
+ }
61
+
55
62
// writes out the channel edges of the declarative config graph in a mermaid format capable of being pasted into
56
63
// mermaid renderers like github, mermaid.live, etc.
57
64
// output is sorted lexicographically by package name, and then by channel name
@@ -125,6 +132,8 @@ func (writer *MermaidWriter) WriteChannels(cfg DeclarativeConfig, out io.Writer)
125
132
126
133
var deprecatedPackage string
127
134
deprecatedChannels := []string {}
135
+ linkID := 0
136
+ skippedLinkIDs := []string {}
128
137
129
138
for _ , c := range cfg .Channels {
130
139
filteredChannel := writer .filterChannel (& c , versionMap , minVersion , minEdgePackage )
@@ -137,8 +146,8 @@ func (writer *MermaidWriter) WriteChannels(cfg DeclarativeConfig, out io.Writer)
137
146
}
138
147
139
148
channelID := fmt .Sprintf ("%s-%s" , filteredChannel .Package , filteredChannel .Name )
140
- pkgBuilder . WriteString ( fmt .Sprintf ( " %%%% channel %q\n " , filteredChannel .Name ) )
141
- pkgBuilder . WriteString ( fmt .Sprintf ( " subgraph %s[%q]\n " , channelID , filteredChannel .Name ) )
149
+ fmt .Fprintf ( pkgBuilder , " %%%% channel %q\n " , filteredChannel .Name )
150
+ fmt .Fprintf ( pkgBuilder , " subgraph %s[%q]\n " , channelID , filteredChannel .Name )
142
151
143
152
if depByPackage .Has (filteredChannel .Package ) {
144
153
deprecatedPackage = filteredChannel .Package
@@ -148,47 +157,79 @@ func (writer *MermaidWriter) WriteChannels(cfg DeclarativeConfig, out io.Writer)
148
157
deprecatedChannels = append (deprecatedChannels , channelID )
149
158
}
150
159
151
- for _ , ce := range filteredChannel .Entries {
152
- if versionMap [ce .Name ].GE (minVersion ) {
153
- bundleDeprecation := ""
154
- if depByBundle .Has (ce .Name ) {
155
- bundleDeprecation = ":::deprecated"
156
- }
160
+ // sort edges by decreasing version
161
+ sortedEntries := make ([]* ChannelEntry , 0 , len (filteredChannel .Entries ))
162
+ for i := range filteredChannel .Entries {
163
+ sortedEntries = append (sortedEntries , & filteredChannel .Entries [i ])
164
+ }
165
+ sort .Slice (sortedEntries , func (i , j int ) bool {
166
+ // Sort by decreasing version: greater version comes first
167
+ return versionMap [sortedEntries [i ].Name ].GT (versionMap [sortedEntries [j ].Name ])
168
+ })
169
+
170
+ skippedEntities := sets.Set [string ]{}
171
+
172
+ for _ , ce := range sortedEntries {
173
+ bundleDecoration := ""
174
+ switch {
175
+ case depByBundle .Has (ce .Name ) && skippedEntities .Has (ce .Name ):
176
+ bundleDecoration = ":::depandskip"
177
+ case depByBundle .Has (ce .Name ):
178
+ bundleDecoration = ":::deprecated"
179
+ case skippedEntities .Has (ce .Name ):
180
+ bundleDecoration = ":::skipped"
181
+ }
157
182
158
- entryID := fmt .Sprintf ("%s-%s" , channelID , ce .Name )
159
- pkgBuilder . WriteString ( fmt .Sprintf ( " %s[%q]%s\n " , entryID , ce .Name , bundleDeprecation ) )
183
+ entryID := fmt .Sprintf ("%s-%s" , channelID , ce .Name )
184
+ fmt .Fprintf ( pkgBuilder , " %s[%q]%s\n " , entryID , ce .Name , bundleDecoration )
160
185
161
- if len (ce .Replaces ) > 0 {
162
- replacesID := fmt . Sprintf ( "%s-%s" , channelID , ce .Replaces )
163
- pkgBuilder . WriteString ( fmt .Sprintf (" %s[%q]-- %s --> %s[%q] \n " , replacesID , ce . Replaces , "replace" , entryID , ce . Name ) )
164
- }
165
- if len ( ce . Skips ) > 0 {
166
- for _ , s := range ce . Skips {
167
- skipsID := fmt . Sprintf ( "%s-%s" , channelID , s )
168
- pkgBuilder . WriteString ( fmt . Sprintf ( " %s[%q]-- %s --> %s[%q] \n " , skipsID , s , "skip" , entryID , ce . Name ) )
186
+ if len (ce .Skips ) > 0 {
187
+ for _ , s := range ce .Skips {
188
+ skipsID := fmt .Sprintf ("%s-%s " , channelID , s )
189
+ fmt . Fprintf ( pkgBuilder , " %s[%q]-- %s --> %s[%q] \n " , skipsID , s , "skip" , entryID , ce . Name )
190
+ if skippedEntities . Has ( s ) {
191
+ skippedLinkIDs = append ( skippedLinkIDs , fmt . Sprintf ( "%d" , linkID ))
192
+ } else {
193
+ skippedEntities . Insert ( s )
169
194
}
195
+ linkID ++
170
196
}
171
- if len (ce .SkipRange ) > 0 {
172
- skipRange , err := semver .ParseRange (ce .SkipRange )
173
- if err == nil {
174
- for _ , edgeName := range filteredChannel .Entries {
175
- if skipRange (versionMap [edgeName .Name ]) {
176
- skipRangeID := fmt .Sprintf ("%s-%s" , channelID , edgeName .Name )
177
- pkgBuilder .WriteString (fmt .Sprintf (" %s[%q]-- \" %s(%s)\" --> %s[%q]\n " , skipRangeID , edgeName .Name , "skipRange" , ce .SkipRange , entryID , ce .Name ))
197
+ }
198
+ if len (ce .SkipRange ) > 0 {
199
+ skipRange , err := semver .ParseRange (ce .SkipRange )
200
+ if err == nil {
201
+ for _ , edgeName := range filteredChannel .Entries {
202
+ if skipRange (versionMap [edgeName .Name ]) {
203
+ skipRangeID := fmt .Sprintf ("%s-%s" , channelID , edgeName .Name )
204
+ fmt .Fprintf (pkgBuilder , " %s[%q]-- \" %s(%s)\" --> %s[%q]\n " , skipRangeID , edgeName .Name , "skipRange" , ce .SkipRange , entryID , ce .Name )
205
+ if skippedEntities .Has (ce .Name ) {
206
+ skippedLinkIDs = append (skippedLinkIDs , fmt .Sprintf ("%d" , linkID ))
178
207
}
208
+ linkID ++
179
209
}
180
- } else {
181
- fmt .Fprintf (os .Stderr , "warning: ignoring invalid SkipRange for package/edge %q/%q: %v\n " , c .Package , ce .Name , err )
182
210
}
211
+ } else {
212
+ fmt .Fprintf (os .Stderr , "warning: ignoring invalid SkipRange for package/edge %q/%q: %v\n " , c .Package , ce .Name , err )
183
213
}
184
214
}
215
+ // have to process replaces last, because applicablity can be impacted by skips
216
+ if len (ce .Replaces ) > 0 {
217
+ replacesID := fmt .Sprintf ("%s-%s" , channelID , ce .Replaces )
218
+ fmt .Fprintf (pkgBuilder , " %s[%q]-- %s --> %s[%q]\n " , replacesID , ce .Replaces , "replace" , entryID , ce .Name )
219
+ if skippedEntities .Has (ce .Name ) {
220
+ skippedLinkIDs = append (skippedLinkIDs , fmt .Sprintf ("%d" , linkID ))
221
+ }
222
+ linkID ++
223
+ }
185
224
}
186
- pkgBuilder . WriteString ( " end\n " )
225
+ fmt . Fprintf ( pkgBuilder , " end\n " )
187
226
}
188
227
}
189
228
190
229
_ , _ = out .Write ([]byte ("graph LR\n " ))
191
230
_ , _ = out .Write ([]byte (" classDef deprecated fill:#E8960F\n " ))
231
+ _ , _ = out .Write ([]byte (" classDef skipped stroke:#FF0000,stroke-width:4px\n " ))
232
+ _ , _ = out .Write ([]byte (" classDef depandskip fill:#E8960,stroke:#FF0000,stroke-width:4px\n " ))
192
233
pkgNames := []string {}
193
234
for pname := range pkgs {
194
235
pkgNames = append (pkgNames , pname )
@@ -213,6 +254,10 @@ func (writer *MermaidWriter) WriteChannels(cfg DeclarativeConfig, out io.Writer)
213
254
}
214
255
}
215
256
257
+ if len (skippedLinkIDs ) > 0 {
258
+ _ , _ = out .Write ([]byte ("linkStyle " + strings .Join (skippedLinkIDs , "," ) + " stroke:#FF0000,stroke-width:3px,stroke-dasharray:5;" ))
259
+ }
260
+
216
261
return nil
217
262
}
218
263
0 commit comments