Skip to content

Commit b99d113

Browse files
committed
feat(catalog): add WellKnownAnnotations
Parses well-known annotations from Backstage and GitHub.
1 parent 595a450 commit b99d113

File tree

2 files changed

+187
-0
lines changed

2 files changed

+187
-0
lines changed
+149
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package catalog
2+
3+
import (
4+
"reflect"
5+
"strings"
6+
)
7+
8+
// WellKnownAnnotations contains a number of well-known annotations with defined semantics.
9+
// They can be attached to catalog entities and consumed by plugins as needed.
10+
//
11+
// See: https://backstage.io/docs/features/software-catalog/well-known-annotations
12+
type WellKnownAnnotations struct {
13+
// BackstageManagedByLocation is a so-called location reference string.
14+
//
15+
// It points to the source from which the entity was originally fetched.
16+
//
17+
// This annotation is added automatically by the catalog as it fetches the
18+
// data from a registered location, and is not meant to normally be written
19+
// by humans.
20+
//
21+
// The annotation may point to any type of generic location that the catalog
22+
// supports, so it cannot be relied on to always be specifically of type url,
23+
// nor that it even represents a single file. Note also that a single
24+
// location can be the source of many entities, so it represents a
25+
// many-to-one relationship.
26+
//
27+
//
28+
// The format of the value is <type>:<target>.
29+
//
30+
// Note that the target may also contain colons, so it is not advisable to
31+
// naively split the value on : and expecting a two-item array out of it. The
32+
// format of the target part is type-dependent and could conceivably even be
33+
// an empty string, but the separator colon is always present.
34+
BackstageManagedByLocation string `json:"backstage.io/managed-by-location,omitempty"`
35+
36+
// BackstageManagedByOriginLocation is a location reference string (see above).
37+
//
38+
// It points to the location, whose registration lead to the creation of the
39+
// entity.
40+
//
41+
// In most cases, the backstage.io/managed-by-location and
42+
// backstage.io/managed-by-origin-location will be equal. They will be
43+
// different if the original location delegates to another location.
44+
//
45+
// A common case is, that a location is registered as bootstrap:bootstrap
46+
// which means that it is part of the app-config.yaml of a Backstage
47+
// installation.
48+
BackstageManagedByOriginLocation string `json:"backstage.io/managed-by-origin-location,omitempty"`
49+
50+
// BackstageOrphan is either absent, or present with the exact string value "true".
51+
//
52+
// It should never be added manually. Instead, the catalog itself injects the
53+
// annotation as part of its processing loops, on entities that are found to
54+
// have no registered locations or config locations that keep them "active" /
55+
// "alive".
56+
//
57+
// For example, suppose that the user first registers a location URL pointing
58+
// to a Location kind entity, which in turn refers to two Component kind
59+
// entities in two other files nearby. The end result is that the catalog
60+
// contains those three entities. Now suppose that the user edits the original
61+
// Location entity to only refer to the first of the Component kind entities.
62+
// This will intentionally not lead to the other Component entity to be removed
63+
// from the catalog (for safety reasons). Instead, it gains this orphan marker
64+
// annotation, to make it clear that user action is required to completely
65+
// remove it, if desired.
66+
BackstageOrphan string `json:"backstage.io/orphan,omitempty"`
67+
68+
// BackstageTechDocsRef informs where TechDocs source content is stored so
69+
// that it can be read and docs can be generated from it.
70+
//
71+
// Most commonly, it's written as a path, relative to the location of the
72+
// catalog-info.yaml itself, where the associated mkdocs.yml file can be
73+
// found.
74+
//
75+
// In unusual situations where the documentation for a catalog entity does
76+
// not live alongside the entity's source code, the value of this annotation
77+
// can point to an absolute URL, matching the location reference string
78+
// format outlined above, for example:
79+
// url:https://github.com/backstage/backstage/tree/master
80+
BackstageTechDocsRef string `json:"backstage.io/techdocs-ref,omitempty"`
81+
82+
// BackstageViewURL allows customizing links from the catalog pages.
83+
//
84+
// The view URL should point to the canonical metadata YAML that governs this entity.
85+
BackstageViewURL string `json:"backstage.io/view-url,omitempty"`
86+
87+
// BackstageEditURL allows customizing links from the catalog pages.
88+
//
89+
// The edit URL should point to the source file for the metadata.
90+
BackstageEditURL string `json:"backstage.io/edit-url,omitempty"`
91+
92+
// BackstageSourceLocation is a Location reference that points to the source
93+
// code of the entity (typically a Component).
94+
//
95+
// Useful when catalog files do not get ingested from the source code
96+
// repository itself.
97+
//
98+
// If the URL points to a folder, it is important that it is suffixed with a
99+
// '/' in order for relative path resolution to work consistently.
100+
BackstageSourceLocation string `json:"backstage.io/source-location,omitempty"`
101+
102+
// GitHubProjectSlug is the so-called slug that identifies a repository on
103+
// GitHub that is related to this entity.
104+
//
105+
// It is on the format <organization or owner>/<repository>, and is the same
106+
// as can be seen in the URL location bar of the browser when viewing that
107+
// repository.
108+
//
109+
// Specifying this annotation will enable GitHub related features in
110+
// Backstage for that entity.
111+
GitHubProjectSlug string `json:"github.com/project-slug,omitempty"`
112+
113+
// GitHubTeamSlug is the so-called slug that identifies a team on GitHub that is
114+
// related to this entity.
115+
//
116+
// It is on the format <organization>/<team>, and is the same as can be seen
117+
// in the URL location bar of the browser when viewing that team.
118+
//
119+
// This annotation can be used on a Group entity to note that it originated
120+
// from that team on GitHub.
121+
GitHubTeamSlug string `json:"github.com/team-slug,omitempty"`
122+
123+
// GitHubUserLogin is the so-called login that identifies a user on GitHub
124+
// that is related to this entity.
125+
//
126+
// It is on the format <username>, and is the same as can be seen in the URL
127+
// location bar of the browser when viewing that user.
128+
//
129+
// This annotation can be used on a User entity to note that it originated
130+
// from that user on GitHub.
131+
GitHubUserLogin string `json:"github.com/user-login,omitempty"`
132+
}
133+
134+
// UnmarshalAnnotations sets the wll-known annotations from a map of annotations.
135+
func (w *WellKnownAnnotations) UnmarshalAnnotations(annotations map[string]string) {
136+
wt := reflect.TypeOf(w).Elem()
137+
wv := reflect.ValueOf(w).Elem()
138+
for i := 0; i < wt.NumField(); i++ {
139+
field := wt.Field(i)
140+
if field.Type.Kind() != reflect.String {
141+
continue
142+
}
143+
if fieldName, _, _ := strings.Cut(field.Tag.Get("json"), ","); fieldName != "" {
144+
if annotation, ok := annotations[fieldName]; ok {
145+
wv.Field(i).SetString(annotation)
146+
}
147+
}
148+
}
149+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package catalog
2+
3+
import (
4+
"testing"
5+
6+
"gotest.tools/v3/assert"
7+
)
8+
9+
func TestWellKnownAnnotations_UnmarshalAnnotations(t *testing.T) {
10+
annotations := map[string]string{
11+
"backstage.io/managed-by-location": "url:http://github.com/backstage/backstage/blob/master/catalog-info.yaml",
12+
"backstage.io/managed-by-origin-location": "url:http://github.com/backstage/backstage/blob/master/catalog-info.yaml",
13+
"backstage.io/orphan": "true",
14+
"backstage.io/techdocs-ref": "dir:.",
15+
"backstage.io/view-url": "https://some.website/catalog-info.yaml",
16+
"backstage.io/edit-url": "https://github.com/my-org/catalog/edit/master/my-service.jsonnet",
17+
"backstage.io/source-location": "url:https://github.com/my-org/my-service/",
18+
"github.com/project-slug": "backstage/backstage",
19+
"github.com/team-slug": "backstage/maintainers",
20+
"github.com/user-login": "freben",
21+
"example.com/unknown-annotation": "foo",
22+
}
23+
expected := WellKnownAnnotations{
24+
BackstageManagedByLocation: "url:http://github.com/backstage/backstage/blob/master/catalog-info.yaml",
25+
BackstageManagedByOriginLocation: "url:http://github.com/backstage/backstage/blob/master/catalog-info.yaml",
26+
BackstageOrphan: "true",
27+
BackstageTechDocsRef: "dir:.",
28+
BackstageViewURL: "https://some.website/catalog-info.yaml",
29+
BackstageEditURL: "https://github.com/my-org/catalog/edit/master/my-service.jsonnet",
30+
BackstageSourceLocation: "url:https://github.com/my-org/my-service/",
31+
GitHubProjectSlug: "backstage/backstage",
32+
GitHubTeamSlug: "backstage/maintainers",
33+
GitHubUserLogin: "freben",
34+
}
35+
var actual WellKnownAnnotations
36+
actual.UnmarshalAnnotations(annotations)
37+
assert.DeepEqual(t, expected, actual)
38+
}

0 commit comments

Comments
 (0)