Add support for Backstage plugins (#2739)

Closes #1509

Signed-off-by: Sergio Castaño Arteaga <tegioz@icloud.com>
Signed-off-by: Cintia Sanchez Garcia <cynthiasg@icloud.com>
Co-authored-by: Sergio Castaño Arteaga <tegioz@icloud.com>
Co-authored-by: Cintia Sanchez Garcia <cynthiasg@icloud.com>
This commit is contained in:
Sergio Castaño Arteaga 2023-02-06 13:15:43 +01:00 committed by GitHub
parent cc75c86028
commit f7fff2402b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
68 changed files with 332 additions and 60 deletions

View File

@ -14,6 +14,7 @@ Discovering artifacts to use with CNCF projects can be difficult. If every CNCF
At the moment, the following artifacts kinds are supported *(with plans to support more projects to follow)*: At the moment, the following artifacts kinds are supported *(with plans to support more projects to follow)*:
- [Backstage plugins](https://backstage.io)
- [Containers images](https://opencontainers.org) - [Containers images](https://opencontainers.org)
- [CoreDNS plugins](https://coredns.io/) - [CoreDNS plugins](https://coredns.io/)
- [Falco configurations](https://falco.org/) - [Falco configurations](https://falco.org/)

View File

@ -2,7 +2,7 @@ apiVersion: v2
name: artifact-hub name: artifact-hub
description: Artifact Hub is a web-based application that enables finding, installing, and publishing Kubernetes packages. description: Artifact Hub is a web-based application that enables finding, installing, and publishing Kubernetes packages.
type: application type: application
version: 1.12.1-2 version: 1.12.1-3
appVersion: 1.12.0 appVersion: 1.12.0
kubeVersion: ">= 1.19.0-0" kubeVersion: ">= 1.19.0-0"
home: https://artifacthub.io home: https://artifacthub.io
@ -24,6 +24,7 @@ keywords:
- gatekeeper - gatekeeper
- kyverno - kyverno
- knative - knative
- backstage
maintainers: maintainers:
- name: Sergio - name: Sergio
email: tegioz@icloud.com email: tegioz@icloud.com

View File

@ -93,7 +93,10 @@
"title": "Lifetime after finished execution", "title": "Lifetime after finished execution",
"description": "Limits the lifetime of the job after it has finished execution", "description": "Limits the lifetime of the job after it has finished execution",
"default": null, "default": null,
"type": ["null", "integer"] "type": [
"null",
"integer"
]
} }
}, },
"required": [ "required": [
@ -1066,7 +1069,7 @@
}, },
"repositoriesKinds": { "repositoriesKinds": {
"title": "Repositories kinds to process ([] = all)", "title": "Repositories kinds to process ([] = all)",
"description": "The following kinds are supported at the moment: falco, helm, olm, opa, tbaction, krew, helm-plugin, tekton-task, keda-scaler, coredns, keptn, tekton-pipeline, container, kubewarden, gatekeeper, kyverno, knative-client-plugin", "description": "The following kinds are supported at the moment: falco, helm, olm, opa, tbaction, krew, helm-plugin, tekton-task, keda-scaler, coredns, keptn, tekton-pipeline, container, kubewarden, gatekeeper, kyverno, knative-client-plugin, backstage",
"type": "array", "type": "array",
"items": { "items": {
"type": "string" "type": "string"

View File

@ -90,7 +90,7 @@ func newLintCmd() *cobra.Command {
return lint(opts, &output{cmd.OutOrStdout()}) return lint(opts, &output{cmd.OutOrStdout()})
}, },
} }
lintCmd.Flags().StringVarP(&opts.kind, "kind", "k", "helm", "repository kind: coredns, falco, helm, helm-plugin, keda-scaler, keptn, krew, kubewarden, olm, opa, tbaction, tekton-task, tekton-pipeline") lintCmd.Flags().StringVarP(&opts.kind, "kind", "k", "helm", "repository kind: backstage, coredns, falco, gatekeeper, helm, helm-plugin, keda-scaler, keptn, knative-client-plugin, krew, kubewarden, kyverno, olm, opa, tbaction, tekton-task, tekton-pipeline")
lintCmd.Flags().StringVarP(&opts.path, "path", "p", ".", "repository's packages path") lintCmd.Flags().StringVarP(&opts.path, "path", "p", ".", "repository's packages path")
return lintCmd return lintCmd
} }
@ -109,11 +109,15 @@ func lint(opts *lintOptions, out *output) error {
var report *lintReport var report *lintReport
switch kind { switch kind {
case case
hub.Backstage,
hub.CoreDNS, hub.CoreDNS,
hub.Falco, hub.Falco,
hub.Gatekeeper,
hub.KedaScaler, hub.KedaScaler,
hub.Keptn, hub.Keptn,
hub.KnativeClientPlugin,
hub.Kubewarden, hub.Kubewarden,
hub.Kyverno,
hub.OPA, hub.OPA,
hub.TBAction: hub.TBAction:
report = lintGeneric(opts.path, kind) report = lintGeneric(opts.path, kind)
@ -605,11 +609,15 @@ func (out *output) printPkgDetails(pkg *hub.Package) {
// Values specific to a repository kind // Values specific to a repository kind
switch pkg.Repository.Kind { switch pkg.Repository.Kind {
case case
hub.Backstage,
hub.CoreDNS, hub.CoreDNS,
hub.Falco, hub.Falco,
hub.Gatekeeper,
hub.KedaScaler, hub.KedaScaler,
hub.Keptn, hub.Keptn,
hub.KnativeClientPlugin,
hub.Kubewarden, hub.Kubewarden,
hub.Kyverno,
hub.OPA, hub.OPA,
hub.TBAction: hub.TBAction:

View File

@ -0,0 +1,5 @@
insert into repository_kind values (17, 'Backstage plugins');
---- create above / drop below ----
delete from repository_kind where repository_kind_id = 17;

View File

@ -524,7 +524,8 @@ select results_eq(
(13, 'Kubewarden policies'), (13, 'Kubewarden policies'),
(14, 'Gatekeeper policies'), (14, 'Gatekeeper policies'),
(15, 'Kyverno policies'), (15, 'Kyverno policies'),
(16, 'Knative client plugins') (16, 'Knative client plugins'),
(17, 'Backstage plugins')
$$, $$,
'Repository kinds should exist' 'Repository kinds should exist'
); );

View File

@ -1112,6 +1112,29 @@ paths:
$ref: "#/components/responses/TooManyRequests" $ref: "#/components/responses/TooManyRequests"
"500": "500":
$ref: "#/components/responses/InternalServerError" $ref: "#/components/responses/InternalServerError"
"/packages/backstage/{repoName}/{packageName}":
get:
tags:
- Packages
summary: Get package details
description: Get package details
operationId: getBackstagePluginsDetails
parameters:
- $ref: "#/components/parameters/RepoNameParam"
- $ref: "#/components/parameters/PackageNameParam"
responses:
"200":
description: ""
content:
application/json:
schema:
$ref: "#/components/schemas/BackstagePlugin"
"404":
$ref: "#/components/responses/NotFoundResponse"
"429":
$ref: "#/components/responses/TooManyRequests"
"500":
$ref: "#/components/responses/InternalServerError"
"/packages/container/{repoName}/{packageName}": "/packages/container/{repoName}/{packageName}":
get: get:
tags: tags:
@ -1503,6 +1526,30 @@ paths:
$ref: "#/components/responses/TooManyRequests" $ref: "#/components/responses/TooManyRequests"
"500": "500":
$ref: "#/components/responses/InternalServerError" $ref: "#/components/responses/InternalServerError"
"/packages/backstage/{repoName}/{packageName}/{version}":
get:
tags:
- Packages
summary: Get package version details
description: Get package version details
operationId: getBackstagePluginsVersionDetails
parameters:
- $ref: "#/components/parameters/RepoNameParam"
- $ref: "#/components/parameters/PackageNameParam"
- $ref: "#/components/parameters/VersionParam"
responses:
"200":
description: ""
content:
application/json:
schema:
$ref: "#/components/schemas/BackstagePlugin"
"404":
$ref: "#/components/responses/NotFoundResponse"
"429":
$ref: "#/components/responses/TooManyRequests"
"500":
$ref: "#/components/responses/InternalServerError"
"/packages/container/{repoName}/{packageName}/{version}": "/packages/container/{repoName}/{packageName}/{version}":
get: get:
tags: tags:
@ -3429,6 +3476,8 @@ components:
name: user1 name: user1
total: 3 total: 3
filter_key: user filter_key: user
BackstagePlugin:
$ref: "#/components/schemas/Package"
ContainerImage: ContainerImage:
allOf: allOf:
- $ref: "#/components/schemas/Package" - $ref: "#/components/schemas/Package"
@ -4311,6 +4360,7 @@ components:
* `14` - Gatekeeper policies * `14` - Gatekeeper policies
* `15` - Kyverno policies * `15` - Kyverno policies
* `16` - Knative client plugins * `16` - Knative client plugins
* `17` - Backstage plugins
RepositoryKindParam: RepositoryKindParam:
type: string type: string
enum: enum:
@ -4331,6 +4381,7 @@ components:
- gatekeeper - gatekeeper
- kyverno - kyverno
- knative-client-plugin - knative-client-plugin
- backstage
description: | description: |
Repository kind name: Repository kind name:
* `helm` - Helm charts * `helm` - Helm charts
@ -4350,6 +4401,7 @@ components:
* `gatekeeper` - Gatekeeper policies * `gatekeeper` - Gatekeeper policies
* `kyverno` - Kyverno policies * `kyverno` - Kyverno policies
* `knative-client-plugin` - Knative client plugins * `knative-client-plugin` - Knative client plugins
* `backstage` - Backstage plugins
RepositorySummary: RepositorySummary:
type: object type: object
required: required:
@ -4848,7 +4900,8 @@ components:
* `13` - Kubewarden policies * `13` - Kubewarden policies
* `14` - Gatekeeper policies * `14` - Gatekeeper policies
* `15` - Kyverno policies * `15` - Kyverno policies
* `15` - Knative client plugins * `16` - Knative client plugins
* `17` - Backstage plugins
PackageNameParam: PackageNameParam:
in: path in: path
name: packageName name: packageName

View File

@ -0,0 +1,49 @@
## Backstage plugins repositories
Backstage plugins repositories are expected to be hosted in GitHub, GitLab or Bitbucket repos. When adding your repository to Artifact Hub, the url used **must** follow the following format:
- `https://github.com/user/repo[/path/to/packages]`
- `https://gitlab.com/user/repo[/path/to/packages]`
- `https://bitbucket.org/user/repo[/path/to/packages]`
By default the `master` branch is used, but it's possible to specify a different one from the UI.
*Please NOTE that the repository URL used when adding the repository to Artifact Hub **must NOT** contain the git hosting platform specific parts, like **tree/branch**, just the path to your packages like it would show in the filesystem.*
The *path/to/packages* provided can contain metadata for one or more packages. Each package version **must** be on a separate folder, and it's up to you to decide if you want to publish one or multiple versions of your package.
The structure of a repository with multiple plugins packages and versions could look something like this:
```sh
$ tree path/to/packages
path/to/packages
├── artifacthub-repo.yml
├── package1
│   ├── 1.0.0
│   │   ├── README.md
│   │   └── artifacthub-pkg.yml
│   └── 2.0.0
│      ├── README.md
│   └── artifacthub-pkg.yml
└── package2
└── 1.0.0
      ├── README.md
└── artifacthub-pkg.yml
```
This structure is flexible, and in some cases it can be greatly simplified. In the case of a single package with a single version available at a time (the publisher doesn't want to make previous ones available, for example), the structure could look like this:
```sh
$ tree path/to/packages
path/to/packages
├── artifacthub-repo.yml
└── package1
   ├── README.md
└── artifacthub-pkg.yml
```
In the previous case, even the `package1` directory could be omitted. The reason is that both packages names and versions are read from the `artifacthub-pkg.yml` metadata file, so directories names are not used at all.
Each package version **needs** an `artifacthub-pkg.yml` metadata file. Please see the file [spec](https://github.com/artifacthub/hub/blob/master/docs/metadata/artifacthub-pkg.yml) for more details. The [artifacthub-repo.yml](https://github.com/artifacthub/hub/blob/master/docs/metadata/artifacthub-repo.yml) repository metadata file shown above can be used to setup features like [Verified publisher](https://github.com/artifacthub/hub/blob/master/docs/repositories.md#verified-publisher) or [Ownership claim](https://github.com/artifacthub/hub/blob/master/docs/repositories.md#ownership-claim). This file must be located at `/path/to/packages`.
Once you have added your repository, you are all set up. As you add new versions of your plugins packages or even new packages to your git repository, they'll be automatically indexed and listed in Artifact Hub.

View File

@ -4,6 +4,7 @@ Artifact Hub allows publishers to list their content in an automated way. Publis
The following repositories kinds are supported at the moment: The following repositories kinds are supported at the moment:
- [Backstage plugins repositories](https://github.com/artifacthub/hub/blob/master/docs/backstage_plugins_repositories.md)
- [Containers images repositories](https://github.com/artifacthub/hub/blob/master/docs/container_images_repositories.md) - [Containers images repositories](https://github.com/artifacthub/hub/blob/master/docs/container_images_repositories.md)
- [CoreDNS plugins repositories](https://github.com/artifacthub/hub/blob/master/docs/coredns_plugins_repositories.md) - [CoreDNS plugins repositories](https://github.com/artifacthub/hub/blob/master/docs/coredns_plugins_repositories.md)
- [Falco rules repositories](https://github.com/artifacthub/hub/blob/master/docs/falco_rules_repositories.md) - [Falco rules repositories](https://github.com/artifacthub/hub/blob/master/docs/falco_rules_repositories.md)

View File

@ -0,0 +1,6 @@
---
title: "Backstage plugins"
aliases: [
"/backstage_plugins_repositories",
]
---

View File

@ -1,6 +1,5 @@
--- ---
title: "Containers images" title: "Containers images"
weight: 1
aliases: [ aliases: [
"/container_images_repositories" "/container_images_repositories"
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "CoreDNS plugins" title: "CoreDNS plugins"
weight: 2
aliases: [ aliases: [
"/coredns_plugins_repositories", "/coredns_plugins_repositories",
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "Falco rules" title: "Falco rules"
weight: 3
aliases: [ aliases: [
"/falco_rules_repositories", "/falco_rules_repositories",
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "Gatekeeper policies" title: "Gatekeeper policies"
weight: 4
aliases: [ aliases: [
"/gatekeeper_policies_repositories", "/gatekeeper_policies_repositories",
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "Helm" title: "Helm"
weight: 1
aliases: [ aliases: [
"/helm_annotations" "/helm_annotations"
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "Helm charts" title: "Helm charts"
weight: 5
aliases: [ aliases: [
"/helm_charts_repositories", "/helm_charts_repositories",
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "Helm plugins" title: "Helm plugins"
weight: 6
aliases: [ aliases: [
"/helm_plugins_repositories", "/helm_plugins_repositories",
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "KEDA scalers" title: "KEDA scalers"
weight: 7
aliases: [ aliases: [
"/keda_scalers_repositories", "/keda_scalers_repositories",
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "Keptn" title: "Keptn"
weight: 2
aliases: [ aliases: [
"/keptn_annotations", "/keptn_annotations",
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "Keptn integrations" title: "Keptn integrations"
weight: 8
aliases: [ aliases: [
"/keptn_integrations_repositories", "/keptn_integrations_repositories",
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "Knative client plugins" title: "Knative client plugins"
weight: 9
aliases: [ aliases: [
"/knative_client_plugins_repositories", "/knative_client_plugins_repositories",
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "Krew" title: "Krew"
weight: 3
aliases: [ aliases: [
"/krew_annotations", "/krew_annotations",
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "Krew kubectl plugins" title: "Krew kubectl plugins"
weight: 10
aliases: [ aliases: [
"/krew_kubectl_plugins_repositories", "/krew_kubectl_plugins_repositories",
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "Kubewarden" title: "Kubewarden"
weight: 4
aliases: [ aliases: [
"/kubewarden_annotations", "/kubewarden_annotations",
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "Kubewarden policies" title: "Kubewarden policies"
weight: 11
aliases: [ aliases: [
"/kubewarden_policies_repositories", "/kubewarden_policies_repositories",
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "Kyverno" title: "Kyverno"
weight: 5
aliases: [ aliases: [
"/kyverno_annotations" "/kyverno_annotations"
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "Kyverno policies" title: "Kyverno policies"
weight: 12
aliases: [ aliases: [
"/kyverno_policies_repositories", "/kyverno_policies_repositories",
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "OLM" title: "OLM"
weight: 6
aliases: [ aliases: [
"/olm_annotations", "/olm_annotations",
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "OLM operators" title: "OLM operators"
weight: 13
aliases: [ aliases: [
"/olm_operators_repositories", "/olm_operators_repositories",
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "OPA policies" title: "OPA policies"
weight: 14
aliases: [ aliases: [
"/opa_policies_repositories", "/opa_policies_repositories",
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "Tekton" title: "Tekton"
weight: 7
aliases: [ aliases: [
"/tekton_annotations", "/tekton_annotations",
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "Tekton pipelines" title: "Tekton pipelines"
weight: 15
aliases: [ aliases: [
"/tekton_pipelines_repositories", "/tekton_pipelines_repositories",
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "Tekton tasks" title: "Tekton tasks"
weight: 16
aliases: [ aliases: [
"/tekton_tasks_repositories" "/tekton_tasks_repositories"
] ]

View File

@ -1,6 +1,5 @@
--- ---
title: "Tinkerbell actions" title: "Tinkerbell actions"
weight: 17
aliases: [ aliases: [
"/tinkerbell_actions_repositories", "/tinkerbell_actions_repositories",
] ]

View File

@ -264,7 +264,7 @@ func (h *Handlers) setupRouter() {
r.Get("/stats", h.Packages.GetStats) r.Get("/stats", h.Packages.GetStats)
r.With(corsMW).Get("/search", h.Packages.Search) r.With(corsMW).Get("/search", h.Packages.Search)
r.With(h.Users.RequireLogin).Get("/starred", h.Packages.GetStarredByUser) r.With(h.Users.RequireLogin).Get("/starred", h.Packages.GetStarredByUser)
r.Route("/{^helm$|^falco$|^opa$|^olm|^tbaction|^krew|^helm-plugin|^tekton-task|^keda-scaler|^coredns|^keptn|^tekton-pipeline|^container|^kubewarden|^gatekeeper|^kyverno|^knative-client-plugin$}/{repoName}/{packageName}", func(r chi.Router) { r.Route("/{^helm$|^falco$|^opa$|^olm|^tbaction|^krew|^helm-plugin|^tekton-task|^keda-scaler|^coredns|^keptn|^tekton-pipeline|^container|^kubewarden|^gatekeeper|^kyverno|^knative-client-plugin|^backstage$}/{repoName}/{packageName}", func(r chi.Router) {
r.Get("/feed/rss", h.Packages.RssFeed) r.Get("/feed/rss", h.Packages.RssFeed)
r.With(corsMW).Get("/summary", h.Packages.GetSummary) r.With(corsMW).Get("/summary", h.Packages.GetSummary)
r.Get("/{version}", h.Packages.Get) r.Get("/{version}", h.Packages.Get)
@ -421,7 +421,7 @@ func (h *Handlers) setupRouter() {
// Index special entry points // Index special entry points
r.Route("/packages", func(r chi.Router) { r.Route("/packages", func(r chi.Router) {
r.Route("/{^helm$|^falco$|^opa$|^olm|^tbaction|^krew|^helm-plugin|^tekton-task|^keda-scaler|^coredns|^keptn|^tekton-pipeline|^container|^kubewarden|^gatekeeper|^kyverno|^knative-client-plugin$}/{repoName}/{packageName}", func(r chi.Router) { r.Route("/{^helm$|^falco$|^opa$|^olm|^tbaction|^krew|^helm-plugin|^tekton-task|^keda-scaler|^coredns|^keptn|^tekton-pipeline|^container|^kubewarden|^gatekeeper|^kyverno|^knative-client-plugin|^backstage$}/{repoName}/{packageName}", func(r chi.Router) {
r.With(h.Packages.InjectIndexMeta).Get("/{version}", h.Static.Index) r.With(h.Packages.InjectIndexMeta).Get("/{version}", h.Static.Index)
r.With(h.Packages.InjectIndexMeta).Get("/", h.Static.Index) r.With(h.Packages.InjectIndexMeta).Get("/", h.Static.Index)
}) })

View File

@ -2069,6 +2069,17 @@ func TestBuildURL(t *testing.T) {
"2.0.0", "2.0.0",
baseURL + "/packages/knative-client-plugin/repo1/pkg1/2.0.0", baseURL + "/packages/knative-client-plugin/repo1/pkg1/2.0.0",
}, },
{
&hub.Package{
NormalizedName: "pkg1",
Repository: &hub.Repository{
Kind: hub.Backstage,
Name: "repo1",
},
},
"2.0.0",
baseURL + "/packages/backstage/repo1/pkg1/2.0.0",
},
} }
for _, tc := range testCases { for _, tc := range testCases {
tc := tc tc := tc

View File

@ -97,11 +97,16 @@ const (
// KnativeClientPlugin represents a repository with Knative client plugins. // KnativeClientPlugin represents a repository with Knative client plugins.
KnativeClientPlugin RepositoryKind = 16 KnativeClientPlugin RepositoryKind = 16
// Backstage represents a repository with Backstage plugins.
Backstage RepositoryKind = 17
) )
// GetKindName returns the name of the provided repository kind. // GetKindName returns the name of the provided repository kind.
func GetKindName(kind RepositoryKind) string { func GetKindName(kind RepositoryKind) string {
switch kind { switch kind {
case Backstage:
return "backstage"
case Container: case Container:
return "container" return "container"
case CoreDNS: case CoreDNS:
@ -145,6 +150,8 @@ func GetKindName(kind RepositoryKind) string {
// provided. // provided.
func GetKindFromName(kind string) (RepositoryKind, error) { func GetKindFromName(kind string) (RepositoryKind, error) {
switch kind { switch kind {
case "backstage":
return Backstage, nil
case "container": case "container":
return Container, nil return Container, nil
case "coredns": case "coredns":

View File

@ -78,6 +78,7 @@ var (
// validRepositoryKinds contains the repository kinds supported. // validRepositoryKinds contains the repository kinds supported.
validRepositoryKinds = []hub.RepositoryKind{ validRepositoryKinds = []hub.RepositoryKind{
hub.Backstage,
hub.Container, hub.Container,
hub.CoreDNS, hub.CoreDNS,
hub.Falco, hub.Falco,
@ -274,6 +275,7 @@ func (m *Manager) ClaimOwnership(ctx context.Context, repoName, orgName string)
var basePath string var basePath string
switch r.Kind { switch r.Kind {
case case
hub.Backstage,
hub.CoreDNS, hub.CoreDNS,
hub.Falco, hub.Falco,
hub.Gatekeeper, hub.Gatekeeper,
@ -448,6 +450,7 @@ func (m *Manager) locateMetadataFile(r *hub.Repository, basePath string) string
mdFile = r.URL mdFile = r.URL
} }
case case
hub.Backstage,
hub.CoreDNS, hub.CoreDNS,
hub.Falco, hub.Falco,
hub.Gatekeeper, hub.Gatekeeper,
@ -806,6 +809,7 @@ func (m *Manager) validateURL(r *hub.Repository) error {
} }
} }
case case
hub.Backstage,
hub.CoreDNS, hub.CoreDNS,
hub.Falco, hub.Falco,
hub.Gatekeeper, hub.Gatekeeper,

View File

@ -113,6 +113,7 @@ func SetupSource(i *hub.TrackerSourceInput) hub.TrackerSource {
case hub.OLM: case hub.OLM:
source = olm.NewTrackerSource(i) source = olm.NewTrackerSource(i)
case case
hub.Backstage,
hub.CoreDNS, hub.CoreDNS,
hub.Gatekeeper, hub.Gatekeeper,
hub.KedaScaler, hub.KedaScaler,

View File

@ -176,6 +176,7 @@ func (t *Tracker) cloneRepository() (string, string, error) {
tmpDir, packagesPath, err = t.svc.Rc.CloneRepository(t.svc.Ctx, t.r) tmpDir, packagesPath, err = t.svc.Rc.CloneRepository(t.svc.Ctx, t.r)
} }
case case
hub.Backstage,
hub.CoreDNS, hub.CoreDNS,
hub.Falco, hub.Falco,
hub.Gatekeeper, hub.Gatekeeper,

View File

@ -6,6 +6,7 @@ cat docs/www/headers/dev docs/dev.md > docs/www/content/topics/dev.md
cat docs/www/headers/infrastructure docs/infrastructure.md > docs/www/content/topics/infrastructure.md cat docs/www/headers/infrastructure docs/infrastructure.md > docs/www/content/topics/infrastructure.md
mkdir -p docs/www/content/topics/repositories mkdir -p docs/www/content/topics/repositories
cat docs/www/headers/repositories docs/repositories.md > docs/www/content/topics/repositories/_index.md cat docs/www/headers/repositories docs/repositories.md > docs/www/content/topics/repositories/_index.md
cat docs/www/headers/backstage_plugins_repositories docs/backstage_plugins_repositories.md > docs/www/content/topics/repositories/backstage-plugins.md
cat docs/www/headers/container_images_repositories docs/container_images_repositories.md > docs/www/content/topics/repositories/container-images.md cat docs/www/headers/container_images_repositories docs/container_images_repositories.md > docs/www/content/topics/repositories/container-images.md
cat docs/www/headers/coredns_plugins_repositories docs/coredns_plugins_repositories.md > docs/www/content/topics/repositories/coredns-plugins.md cat docs/www/headers/coredns_plugins_repositories docs/coredns_plugins_repositories.md > docs/www/content/topics/repositories/coredns-plugins.md
cat docs/www/headers/falco_rules_repositories docs/falco_rules_repositories.md > docs/www/content/topics/repositories/falco-rules.md cat docs/www/headers/falco_rules_repositories docs/falco_rules_repositories.md > docs/www/content/topics/repositories/falco-rules.md

View File

@ -0,0 +1 @@
<svg id="Assets" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 337.46475 428.49896"><defs><style>.cls-1{fill:#fff;}</style></defs><path class="cls-1" d="M303.02856,166.04816a80.384,80.384,0,0,0,13.44825-10.37128c.79419-.76227,1.55041-1.53052,2.30078-2.30078a82.04885,82.04885,0,0,0,7.9292-9.37811,63.42474,63.42474,0,0,0,6.26928-10.76539,48.6159,48.6159,0,0,0,4.35877-16.39782c1.48279-19.39118-9.96729-38.67493-35.62195-54.22687L198.55737,0l-120.26,115.22662L0,190.2478l108.60107,65.90778a111.60534,111.60534,0,0,0,57.76172,16.41577c24.92212,0,48.80127-8.803,66.41919-25.68646,19.15833-18.36218,25.51929-42.128,13.697-61.87146a49.0086,49.0086,0,0,0-6.7987-8.86865,89.32561,89.32561,0,0,0,19.28576,2.14355c.05164,0,.10144.004.15113.004a85.01244,85.01244,0,0,0,30.9707-5.7937A80.536,80.536,0,0,0,303.02856,166.04816ZM202.44543,225.85583c-19.3197,18.51-50.39855,21.23664-75.69982,5.89936L51.61328,186.14752l67.44336-64.64239,76.41687,46.38617C223.01074,184.58405,221.49072,207.60742,202.44543,225.85583Zm8.93348-82.21734-70.64722-42.8888,64.40723-61.76837L274.5083,81.09558c25.94006,15.72455,29.31006,37.04126,10.55042,55.017A60.70506,60.70506,0,0,1,211.37891,143.63849Zm29.86572,190.0401c-19.5763,18.75629-46.17029,29.08777-74.88184,29.08777A123.81616,123.81616,0,0,1,102.2561,344.5733L0,282.51672V307.194l108.60107,65.90778a111.60536,111.60536,0,0,0,57.76172,16.41571c24.92212,0,48.80127-8.80292,66.41919-25.6864,12.87708-12.34162,19.99024-27.12934,19.67981-41.49311l-.00207-1.79126A87.09493,87.09493,0,0,1,241.24463,333.67859Zm0-38.98346c-19.5763,18.75635-46.17029,29.08783-74.88184,29.08783A123.81616,123.81616,0,0,1,102.2561,305.5899L0,243.53333v24.67932l108.60107,65.90777a111.60583,111.60583,0,0,0,57.76172,16.41571c24.92212,0,48.80127-8.803,66.41919-25.6864,12.87708-12.34363,19.99024-27.12933,19.67981-41.49512l-.00207-1.78924A86.98006,86.98006,0,0,1,241.24463,294.69513Zm0-38.98138c-19.5763,18.75628-46.17029,29.08776-74.88184,29.08776a123.81568,123.81568,0,0,1-64.10669-18.19305L0,204.55188v24.67737L108.60107,295.137a111.60583,111.60583,0,0,0,57.76172,16.41571c24.92212,0,48.80127-8.803,66.41919-25.6864,12.87708-12.34167,19.99024-27.12939,19.67981-41.49316l-.00207-1.82306A86.47145,86.47145,0,0,1,241.24463,255.71375Zm83.69079,25.73815a94.13732,94.13732,0,0,1-60.19177,25.8556v-.002h-.01184l.01184,26.66766a81.65277,81.65277,0,0,0,51.73316-22.36865c13.96777-13.38458,21.14453-28.11059,20.98535-42.64154v-2.19726A95.16106,95.16106,0,0,1,324.93542,281.4519ZM241.24463,372.66c-19.5763,18.75629-46.17029,29.08777-74.88184,29.08777a123.81625,123.81625,0,0,1-64.10669-18.19305L0,321.49811v24.67737l108.60107,65.90771A111.60534,111.60534,0,0,0,166.36279,428.499c24.92212,0,48.80127-8.803,66.41919-25.6864,12.87708-12.34167,19.99024-27.12939,19.67981-41.49316l-.00207-1.78925A86.98289,86.98289,0,0,1,241.24463,372.66Zm85.74475-210.20623c-.67859.68664-1.34534,1.37329-2.054,2.05194A99.29568,99.29568,0,0,1,302.7041,180.193a94.67991,94.67991,0,0,1-26.2456,8.71143,98.08664,98.08664,0,0,1-14.16089,1.56634c.50561,1.61213.90368,3.25007,1.25,4.902a53.15974,53.15974,0,0,1,1.13843,12.00128l.0039,9.65284h.05371A84.26983,84.26983,0,0,0,290.08789,211.48a80.95382,80.95382,0,0,0,26.38892-16.82178c.802-.7702,1.50464-1.55835,2.26294-2.33654a82.04872,82.04872,0,0,0,7.92724-9.38019,63.32792,63.32792,0,0,0,6.28321-10.77527,48.44218,48.44218,0,0,0,4.319-16.44165c.09155-1.236.205-2.47388.193-3.70789v-2.048q-1.08069,1.55245-2.257,3.08893A97.66566,97.66566,0,0,1,326.98938,162.45374Zm-.03186,77.921c-.68652.69458-1.30566,1.4071-2.0221,2.09375a94.13054,94.13054,0,0,1-60.19177,25.8576v-.002h-.02978l.012,26.6676h.01392l.0039.002v-.002a81.65282,81.65282,0,0,0,51.73316-22.36865,73.5466,73.5466,0,0,0,16.47339-22.492,48.45386,48.45386,0,0,0,4.319-16.44159c.09155-1.238.205-2.47393.193-3.7099v-2.19323c-.74439,1.0747-1.46485,2.15344-2.27283,3.21624A97.104,97.104,0,0,1,326.95752,240.37476Zm0-38.98145c-.68652.69464-1.30359,1.40717-2.0221,2.09381a97.185,97.185,0,0,1-22.25122,15.67536,95.66871,95.66871,0,0,1-37.94055,10.18023H264.696l.01185,26.6676h.0299l.00586.002v-.002a81.65068,81.65068,0,0,0,51.73316-22.37066c.802-.7702,1.50464-1.55835,2.26294-2.33655A82.05256,82.05256,0,0,0,326.667,221.923a63.33031,63.33031,0,0,0,6.28321-10.77533,48.4418,48.4418,0,0,0,4.319-16.44159c.09155-1.236.205-2.47393.193-3.70788v-2.1933c-.74439,1.07477-1.46485,2.1535-2.27283,3.21631A97.33693,97.33693,0,0,1,326.95752,201.39331Z"/></svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -0,0 +1 @@
<svg id="Assets" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 337.46475 428.49896"><defs><style>.cls-1{fill:#7df3e1;}</style></defs><path class="cls-1" d="M303.02856,166.04816a80.384,80.384,0,0,0,13.44825-10.37128c.79419-.76227,1.55041-1.53052,2.30078-2.30078a82.04885,82.04885,0,0,0,7.9292-9.37811,63.42407,63.42407,0,0,0,6.26928-10.76539,48.6159,48.6159,0,0,0,4.35877-16.39782c1.48279-19.39118-9.96729-38.67493-35.62195-54.22687L198.55737,0l-120.26,115.22662L0,190.2478l108.60107,65.90778a111.60534,111.60534,0,0,0,57.76172,16.41577c24.92212,0,48.80127-8.803,66.41919-25.68646,19.15833-18.36218,25.51929-42.128,13.697-61.87146a49.0086,49.0086,0,0,0-6.7987-8.86865,89.32561,89.32561,0,0,0,19.28576,2.14355c.05164,0,.10144.004.15113.004a85.01244,85.01244,0,0,0,30.9707-5.7937A80.536,80.536,0,0,0,303.02856,166.04816ZM202.44543,225.85583c-19.3197,18.51-50.39855,21.23664-75.69982,5.89936L51.61328,186.14752l67.44336-64.64239,76.41687,46.38617C223.01074,184.58405,221.49072,207.60742,202.44543,225.85583Zm8.93348-82.21734-70.64722-42.8888,64.40723-61.76837L274.5083,81.09558c25.94006,15.72455,29.31006,37.04126,10.55042,55.017A60.70506,60.70506,0,0,1,211.37891,143.63849Zm29.86572,190.0401c-19.5763,18.75629-46.17029,29.08777-74.88184,29.08777A123.81616,123.81616,0,0,1,102.2561,344.5733L0,282.51672V307.194l108.60107,65.90778a111.60536,111.60536,0,0,0,57.76172,16.41571c24.92212,0,48.80127-8.80292,66.41919-25.6864,12.87708-12.34162,19.99024-27.12934,19.67981-41.49311l-.00207-1.79126A87.09493,87.09493,0,0,1,241.24463,333.67859Zm0-38.98346c-19.5763,18.75635-46.17029,29.08783-74.88184,29.08783A123.81616,123.81616,0,0,1,102.2561,305.5899L0,243.53333v24.67932l108.60107,65.90777a111.60583,111.60583,0,0,0,57.76172,16.41571c24.92212,0,48.80127-8.803,66.41919-25.6864,12.87708-12.34363,19.99024-27.12933,19.67981-41.49512l-.00207-1.78924A86.98006,86.98006,0,0,1,241.24463,294.69513Zm0-38.98138c-19.5763,18.75628-46.17029,29.08776-74.88184,29.08776a123.81568,123.81568,0,0,1-64.10669-18.19305L0,204.55188v24.67737L108.60107,295.137a111.60583,111.60583,0,0,0,57.76172,16.41571c24.92212,0,48.80127-8.803,66.41919-25.6864,12.87708-12.34167,19.99024-27.12939,19.67981-41.49316l-.00207-1.82306A86.47145,86.47145,0,0,1,241.24463,255.71375Zm83.69079,25.73815a94.13732,94.13732,0,0,1-60.19177,25.8556v-.002h-.01184l.01184,26.66766a81.65277,81.65277,0,0,0,51.73316-22.36865c13.96777-13.38458,21.14453-28.11059,20.98535-42.64154v-2.19726A95.16106,95.16106,0,0,1,324.93542,281.4519ZM241.24463,372.66c-19.5763,18.75629-46.17029,29.08777-74.88184,29.08777a123.81625,123.81625,0,0,1-64.10669-18.19305L0,321.49811v24.67737l108.60107,65.90771A111.60534,111.60534,0,0,0,166.36279,428.499c24.92212,0,48.80127-8.803,66.41919-25.6864,12.87708-12.34167,19.99024-27.12939,19.67981-41.49316l-.00207-1.78925A86.98289,86.98289,0,0,1,241.24463,372.66Zm85.74475-210.20623c-.67859.68664-1.34534,1.37329-2.054,2.05194A99.29568,99.29568,0,0,1,302.7041,180.193a94.67991,94.67991,0,0,1-26.2456,8.71143,98.08664,98.08664,0,0,1-14.16089,1.56634c.50561,1.61213.90368,3.25007,1.25,4.902a53.15974,53.15974,0,0,1,1.13843,12.00128l.0039,9.65284h.05371A84.26983,84.26983,0,0,0,290.08789,211.48a80.95382,80.95382,0,0,0,26.38892-16.82178c.802-.7702,1.50464-1.55835,2.26294-2.33654a82.04872,82.04872,0,0,0,7.92724-9.38019,63.32792,63.32792,0,0,0,6.28321-10.77527,48.44218,48.44218,0,0,0,4.319-16.44165c.09155-1.236.205-2.47388.193-3.70789v-2.048q-1.08069,1.55245-2.257,3.08893A97.66566,97.66566,0,0,1,326.98938,162.45374Zm-.03186,77.921c-.68652.69458-1.30566,1.4071-2.0221,2.09375a94.13054,94.13054,0,0,1-60.19177,25.8576v-.002h-.02978l.012,26.6676h.01392l.0039.002v-.002a81.65282,81.65282,0,0,0,51.73316-22.36865,73.5466,73.5466,0,0,0,16.47339-22.492,48.45386,48.45386,0,0,0,4.319-16.44159c.09155-1.238.205-2.47393.193-3.7099v-2.19323c-.74439,1.0747-1.46485,2.15344-2.27283,3.21624A97.104,97.104,0,0,1,326.95752,240.37476Zm0-38.98145c-.68652.69464-1.30359,1.40717-2.0221,2.09381a97.185,97.185,0,0,1-22.25122,15.67536,95.66871,95.66871,0,0,1-37.94055,10.18023H264.696l.01185,26.6676h.0299l.00586.002v-.002a81.65068,81.65068,0,0,0,51.73316-22.37066c.802-.7702,1.50464-1.55835,2.26294-2.33655A82.05256,82.05256,0,0,0,326.667,221.923a63.33031,63.33031,0,0,0,6.28321-10.77533,48.4418,48.4418,0,0,0,4.319-16.44159c.09155-1.236.205-2.47393.193-3.70788v-2.1933c-.74439,1.07477-1.46485,2.1535-2.27283,3.21631A97.33693,97.33693,0,0,1,326.95752,201.39331Z"/></svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

View File

@ -62,6 +62,8 @@ const Image = (props: Props) => {
return '/static/media/placeholder_pkg_kyverno.png'; return '/static/media/placeholder_pkg_kyverno.png';
case RepositoryKind.KnativeClientPlugin: case RepositoryKind.KnativeClientPlugin:
return '/static/media/placeholder_pkg_knative.png'; return '/static/media/placeholder_pkg_knative.png';
case RepositoryKind.Backstage:
return '/static/media/placeholder_pkg_backstage.png';
default: default:
return PLACEHOLDER_SRC; return PLACEHOLDER_SRC;
} }

View File

@ -128,6 +128,13 @@ describe('RepositoryIcon', () => {
expect(icon).toHaveProperty('src', 'http://localhost/static/media/knative-light.svg'); expect(icon).toHaveProperty('src', 'http://localhost/static/media/knative-light.svg');
}); });
it('renders Backstage plugin icon', () => {
render(<RepositoryIcon kind={RepositoryKind.Backstage} type="white" />);
const icon = screen.getByAltText('Icon');
expect(icon).toBeInTheDocument();
expect(icon).toHaveProperty('src', 'http://localhost/static/media/backstage-light.svg');
});
it('renders Chart icon - default type', () => { it('renders Chart icon - default type', () => {
render(<RepositoryIcon kind={RepositoryKind.Helm} />); render(<RepositoryIcon kind={RepositoryKind.Helm} />);
const icons = screen.getAllByAltText('Icon'); const icons = screen.getAllByAltText('Icon');

View File

@ -77,6 +77,10 @@ const ICONS = {
default: '/static/media/knative.svg', default: '/static/media/knative.svg',
white: '/static/media/knative-light.svg', white: '/static/media/knative-light.svg',
}, },
[RepositoryKind.Backstage]: {
default: '/static/media/backstage.svg',
white: '/static/media/backstage-light.svg',
},
}; };
const RepositoryIcon = (props: Props) => { const RepositoryIcon = (props: Props) => {

View File

@ -82,6 +82,10 @@ jest.mock('../../utils/getSampleQueries', () => () => {
name: 'Knative client plugings', name: 'Knative client plugings',
querystring: 'kind=16', querystring: 'kind=16',
}, },
{
name: 'Backstage plugings',
querystring: 'kind=17',
},
]; ];
}); });

View File

@ -431,6 +431,17 @@ const RepositoryModal = (props: Props) => {
</ExternalLink> </ExternalLink>
); );
break; break;
case RepositoryKind.Backstage:
link = (
<ExternalLink
href="/docs/topics/repositories/backstage-plugins"
className="text-primary fw-bold"
label="Open documentation"
>
Backstage plugins
</ExternalLink>
);
break;
} }
if (isUndefined(link)) return; if (isUndefined(link)) return;
@ -455,6 +466,7 @@ const RepositoryModal = (props: Props) => {
case RepositoryKind.Gatekeeper: case RepositoryKind.Gatekeeper:
case RepositoryKind.Kyverno: case RepositoryKind.Kyverno:
case RepositoryKind.KnativeClientPlugin: case RepositoryKind.KnativeClientPlugin:
case RepositoryKind.Backstage:
return ( return (
<> <>
<p <p
@ -843,6 +855,7 @@ const RepositoryModal = (props: Props) => {
RepositoryKind.Gatekeeper, RepositoryKind.Gatekeeper,
RepositoryKind.Kyverno, RepositoryKind.Kyverno,
RepositoryKind.KnativeClientPlugin, RepositoryKind.KnativeClientPlugin,
RepositoryKind.Backstage,
].includes(selectedKind) && ( ].includes(selectedKind) && (
<div> <div>
<InputField <InputField
@ -984,6 +997,7 @@ const RepositoryModal = (props: Props) => {
RepositoryKind.Gatekeeper, RepositoryKind.Gatekeeper,
RepositoryKind.Kyverno, RepositoryKind.Kyverno,
RepositoryKind.KnativeClientPlugin, RepositoryKind.KnativeClientPlugin,
RepositoryKind.Backstage,
].includes(selectedKind) && ( ].includes(selectedKind) && (
<div className="mt-4 mb-3"> <div className="mt-4 mb-3">
<div className="form-check form-switch ps-0"> <div className="form-check form-switch ps-0">

View File

@ -59,6 +59,11 @@ exports[`Repository Modal - repositories section creates snapshot 1`] = `
name="repoKind" name="repoKind"
required="" required=""
> >
<option
value="17"
>
Backstage plugins
</option>
<option <option
value="12" value="12"
> >

View File

@ -34,6 +34,7 @@
.aboutIcon { .aboutIcon {
width: 75px; width: 75px;
max-height: 75px;
} }
.legend { .legend {
@ -75,6 +76,7 @@
.aboutIcon { .aboutIcon {
width: 55px; width: 55px;
max-height: 55px;
} }
.extraInfo, .extraInfo,
@ -99,6 +101,7 @@
.aboutIcon { .aboutIcon {
width: 35px; width: 35px;
max-height: 35px;
} }
.logo { .logo {

View File

@ -332,7 +332,7 @@ exports[`Home index creates snapshot 1`] = `
<div <div
class="text-center px-3 px-xs-0" class="text-center px-3 px-xs-0"
> >
Artifact Hub is a web-based application that enables finding, installing, and publishing packages and configurations for CNCF projects. For example, this could include Helm charts and plugins, Falco configurations, Open Policy Agent (OPA) and Gatekeeper policies, OLM operators, Tinkerbell actions, kubectl plugins, Tekton tasks and pipelines, KEDA scalers, CoreDNS plugins, Keptn integrations, container images, Kubewarden policies, Kyverno policies and Knative client plugins. Artifact Hub is a web-based application that enables finding, installing, and publishing packages and configurations for CNCF projects. For example, this could include Helm charts and plugins, Falco configurations, Open Policy Agent (OPA) and Gatekeeper policies, OLM operators, Tinkerbell actions, kubectl plugins, Tekton tasks and pipelines, KEDA scalers, CoreDNS plugins, Keptn integrations, container images, Kubewarden policies, Kyverno policies, Knative client and Backstage plugins.
<div <div
class="mx-0 mx-md-3 mx-lg-5 my-4 my-sm-5 d-flex flex-row align-items-stretch justify-content-around" class="mx-0 mx-md-3 mx-lg-5 my-4 my-sm-5 d-flex flex-row align-items-stretch justify-content-around"
> >
@ -606,9 +606,6 @@ exports[`Home index creates snapshot 1`] = `
<div <div
class="mx-0 mx-md-3 mx-lg-5 my-4 my-sm-5 d-flex flex-row align-items-stretch justify-content-around" class="mx-0 mx-md-3 mx-lg-5 my-4 my-sm-5 d-flex flex-row align-items-stretch justify-content-around"
> >
<div
class="col"
/>
<a <a
aria-label="Open Container Initiative site" aria-label="Open Container Initiative site"
class="link col iconLink" class="link col iconLink"
@ -713,9 +710,32 @@ exports[`Home index creates snapshot 1`] = `
</div> </div>
</div> </div>
</a> </a>
<div <a
class="col" aria-label="Open Backstage web"
/> class="link col iconLink"
href="https://backstage.io/plugins"
rel="noopener noreferrer"
role="button"
tabindex="-1"
target="_blank"
>
<div
class="d-flex flex-column justify-content-between align-items-center h-100"
>
<img
alt="Icon"
class="aboutIcon"
src="/static/media/backstage-light.svg"
/>
<div
class="d-none d-sm-block text-light mt-2 legendIcon"
>
<small>
Backstage plugins
</small>
</div>
</div>
</a>
</div> </div>
Discovering artifacts to use with CNCF projects can be difficult. If every CNCF project that needs to share artifacts creates its own Hub this creates a fair amount of repeat work for each project and a fractured experience for those trying to find the artifacts to consume. The Artifact Hub attempts to solve that by providing a single experience for consumers that any CNCF project can leverage. Discovering artifacts to use with CNCF projects can be difficult. If every CNCF project that needs to share artifacts creates its own Hub this creates a fair amount of repeat work for each project and a fractured experience for those trying to find the artifacts to consume. The Artifact Hub attempts to solve that by providing a single experience for consumers that any CNCF project can leverage.
</div> </div>

View File

@ -131,7 +131,7 @@ describe('Home index', () => {
await waitFor(() => expect(API.getStats).toHaveBeenCalledTimes(1)); await waitFor(() => expect(API.getStats).toHaveBeenCalledTimes(1));
const links = await screen.findAllByRole('button'); const links = await screen.findAllByRole('button');
expect(links).toHaveLength(21); expect(links).toHaveLength(22);
expect(links[2]).toHaveProperty('href', 'https://github.com/artifacthub/hub'); expect(links[2]).toHaveProperty('href', 'https://github.com/artifacthub/hub');
expect(links[3]).toHaveProperty('href', 'https://cloud-native.slack.com/channels/artifact-hub'); expect(links[3]).toHaveProperty('href', 'https://cloud-native.slack.com/channels/artifact-hub');
@ -155,8 +155,9 @@ describe('Home index', () => {
expect(links[17]).toHaveProperty('href', 'https://www.kubewarden.io/'); expect(links[17]).toHaveProperty('href', 'https://www.kubewarden.io/');
expect(links[18]).toHaveProperty('href', 'https://www.kyverno.io/'); expect(links[18]).toHaveProperty('href', 'https://www.kyverno.io/');
expect(links[19]).toHaveProperty('href', 'https://github.com/knative/client'); expect(links[19]).toHaveProperty('href', 'https://github.com/knative/client');
expect(links[20]).toHaveProperty('href', 'https://backstage.io/plugins');
expect(links[20]).toHaveProperty('href', 'https://www.cncf.io/sandbox-projects/'); expect(links[21]).toHaveProperty('href', 'https://www.cncf.io/sandbox-projects/');
}); });
}); });
}); });

View File

@ -235,7 +235,7 @@ const HomeView = (props: Props) => {
configurations for CNCF projects. For example, this could include Helm charts and plugins, Falco configurations for CNCF projects. For example, this could include Helm charts and plugins, Falco
configurations, Open Policy Agent (OPA) and Gatekeeper policies, OLM operators, Tinkerbell actions, configurations, Open Policy Agent (OPA) and Gatekeeper policies, OLM operators, Tinkerbell actions,
kubectl plugins, Tekton tasks and pipelines, KEDA scalers, CoreDNS plugins, Keptn integrations, kubectl plugins, Tekton tasks and pipelines, KEDA scalers, CoreDNS plugins, Keptn integrations,
container images, Kubewarden policies, Kyverno policies and Knative client plugins. container images, Kubewarden policies, Kyverno policies, Knative client and Backstage plugins.
<div className="mx-0 mx-md-3 mx-lg-5 my-4 my-sm-5 d-flex flex-row align-items-stretch justify-content-around"> <div className="mx-0 mx-md-3 mx-lg-5 my-4 my-sm-5 d-flex flex-row align-items-stretch justify-content-around">
<ExternalLink href="https://helm.sh" className={`col ${styles.iconLink}`} label="Open Helm site"> <ExternalLink href="https://helm.sh" className={`col ${styles.iconLink}`} label="Open Helm site">
<div className="d-flex flex-column justify-content-between align-items-center h-100"> <div className="d-flex flex-column justify-content-between align-items-center h-100">
@ -341,7 +341,6 @@ const HomeView = (props: Props) => {
</ExternalLink> </ExternalLink>
</div> </div>
<div className="mx-0 mx-md-3 mx-lg-5 my-4 my-sm-5 d-flex flex-row align-items-stretch justify-content-around"> <div className="mx-0 mx-md-3 mx-lg-5 my-4 my-sm-5 d-flex flex-row align-items-stretch justify-content-around">
<div className="col" />
<ExternalLink <ExternalLink
href="https://opencontainers.org" href="https://opencontainers.org"
className={`col ${styles.iconLink}`} className={`col ${styles.iconLink}`}
@ -394,8 +393,18 @@ const HomeView = (props: Props) => {
</div> </div>
</div> </div>
</ExternalLink> </ExternalLink>
<ExternalLink
<div className="col" /> href="https://backstage.io/plugins"
className={`col ${styles.iconLink}`}
label="Open Backstage web"
>
<div className="d-flex flex-column justify-content-between align-items-center h-100">
<RepositoryIcon kind={RepositoryKind.Backstage} type="white" className={styles.aboutIcon} />
<div className={`d-none d-sm-block text-light mt-2 ${styles.legendIcon}`}>
<small>Backstage plugins</small>
</div>
</div>
</ExternalLink>
</div> </div>
Discovering artifacts to use with CNCF projects can be difficult. If every CNCF project that needs to Discovering artifacts to use with CNCF projects can be difficult. If every CNCF project that needs to
share artifacts creates its own Hub this creates a fair amount of repeat work for each project and a share artifacts creates its own Hub this creates a fair amount of repeat work for each project and a

View File

@ -18,6 +18,7 @@ export enum RepositoryKind {
Gatekeeper, Gatekeeper,
Kyverno, Kyverno,
KnativeClientPlugin, KnativeClientPlugin,
Backstage,
} }
export enum KeptnData { export enum KeptnData {

View File

@ -88,6 +88,15 @@ export interface RepoKindDef {
// Sorted alphabetically // Sorted alphabetically
export const REPOSITORY_KINDS: RepoKindDef[] = [ export const REPOSITORY_KINDS: RepoKindDef[] = [
{
kind: RepositoryKind.Backstage,
label: 'backstage',
name: 'Backstage plugins',
singular: 'Backstage plugin',
plural: 'Backstage plugins',
icon: <RepositoryIcon kind={RepositoryKind.Backstage} className="mw-100 mh-100" />,
active: true,
},
{ {
kind: RepositoryKind.Container, kind: RepositoryKind.Container,
label: 'container', label: 'container',
@ -795,4 +804,4 @@ export const CVSS_V3_VECTORS: { [key: string]: CVSSVectorMetric[] } = {
export const OCI_PREFIX = 'oci://'; export const OCI_PREFIX = 'oci://';
export const PKG_DETAIL_PATH = export const PKG_DETAIL_PATH =
/^\/packages\/(helm|falco|opa|olm|tbaction|krew|helm-plugin|tekton-task|keda-scaler|coredns|keptn|tekton-pipeline|kubewarden|gatekeeper|kyverno|knative-client-plugin|container)\//; /^\/packages\/(helm|falco|opa|olm|tbaction|krew|helm-plugin|tekton-task|keda-scaler|coredns|keptn|tekton-pipeline|kubewarden|gatekeeper|kyverno|knative-client-plugin|backstage|container)\//;

View File

@ -71,6 +71,10 @@ describe('repoKind', () => {
expect(methods.getRepoKind('knative-client-plugin')).toBe(RepositoryKind.KnativeClientPlugin); expect(methods.getRepoKind('knative-client-plugin')).toBe(RepositoryKind.KnativeClientPlugin);
}); });
it('backstage', () => {
expect(methods.getRepoKind('backstage')).toBe(RepositoryKind.Backstage);
});
it('unknown', () => { it('unknown', () => {
expect(methods.getRepoKind('unknown')).toBeNull(); expect(methods.getRepoKind('unknown')).toBeNull();
}); });
@ -145,6 +149,10 @@ describe('repoKind', () => {
expect(methods.getRepoKindName(RepositoryKind.KnativeClientPlugin)).toBe('knative-client-plugin'); expect(methods.getRepoKindName(RepositoryKind.KnativeClientPlugin)).toBe('knative-client-plugin');
}); });
it('backstage kind', () => {
expect(methods.getRepoKindName(RepositoryKind.Backstage)).toBe('backstage');
});
it('unknown kind', () => { it('unknown kind', () => {
expect(methods.getRepoKindName(20)).toBeNull(); expect(methods.getRepoKindName(20)).toBeNull();
}); });

View File

@ -36,6 +36,8 @@ const getRepoKind = (repoName: string): RepositoryKind | null => {
return RepositoryKind.Kyverno; return RepositoryKind.Kyverno;
case 'knative-client-plugin': case 'knative-client-plugin':
return RepositoryKind.KnativeClientPlugin; return RepositoryKind.KnativeClientPlugin;
case 'backstage':
return RepositoryKind.Backstage;
default: default:
return null; return null;
} }
@ -77,6 +79,8 @@ const getRepoKindName = (repoKind: RepositoryKind): string | null => {
return 'kyverno'; return 'kyverno';
case RepositoryKind.KnativeClientPlugin: case RepositoryKind.KnativeClientPlugin:
return 'knative-client-plugin'; return 'knative-client-plugin';
case RepositoryKind.Backstage:
return 'backstage';
default: default:
return null; return null;
} }

View File

@ -1,18 +1,33 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<title>Artifact Hub widget</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div class="artifacthub-widget-group" data-url="http://localhost:8000/packages/search?kind=16&sort=relevance&page=1" data-theme="light" data-header="false" data-stars="true" data-color="#417598" data-responsive="true" data-loading="true"></div> <head>
<div class="artifacthub-widget-group" data-url="http://localhost:8000/packages/search" data-theme="light" data-header="false" data-stars="true" data-color="#417598" data-responsive="true" data-loading="true"></div> <meta charset="utf-8" />
<div class="artifacthub-widget" data-url="https://artifacthub.io/packages/helm/artifact-hub/artifact-hub" data-theme="light" data-header="true" data-stars="false" data-responsive="false"><blockquote><p lang="en" dir="ltr"><b>artifact-hub</b>: Artifact Hub is a web-based application that enables finding, installing, and publishing Kubernetes packages.</p>&mdash; Open in <a href="https://artifacthub.io/packages/helm/artifact-hub/artifact-hub">Artifact Hub</a></blockquote></div> <meta name="viewport" content="width=device-width, initial-scale=1" />
<div class="artifacthub-widget-group" data-url="https://artifacthub.io/api/v1/packages/search" data-theme="dark" data-header="false" data-stars="false" data-color="#417598" data-responsive="true" data-loading="true"></div> <meta name="theme-color" content="#000000" />
<div class="artifacthub-widget-group" data-url="https://artifacthub.io/packages/search?page=1&ts_query_web=hub" data-theme="light" data-header="false" data-color="#417598" data-responsive="true" data-loading="true"></div> <title>Artifact Hub widget</title>
</body> </head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div class="artifacthub-widget-group" data-url="http://localhost:8000/packages/search?kind=17&sort=relevance&page=1"
data-theme="light" data-header="false" data-stars="true" data-color="#417598" data-responsive="true"
data-loading="true"></div>
<div class="artifacthub-widget-group" data-url="http://localhost:8000/packages/search" data-theme="light"
data-header="false" data-stars="true" data-color="#417598" data-responsive="true" data-loading="true"></div>
<div class="artifacthub-widget" data-url="https://artifacthub.io/packages/helm/artifact-hub/artifact-hub"
data-theme="light" data-header="true" data-stars="false" data-responsive="false">
<blockquote>
<p lang="en" dir="ltr"><b>artifact-hub</b>: Artifact Hub is a web-based application that enables finding,
installing, and publishing Kubernetes packages.</p>&mdash; Open in <a
href="https://artifacthub.io/packages/helm/artifact-hub/artifact-hub">Artifact Hub</a>
</blockquote>
</div>
<div class="artifacthub-widget-group" data-url="https://artifacthub.io/api/v1/packages/search" data-theme="dark"
data-header="false" data-stars="false" data-color="#417598" data-responsive="true" data-loading="true"></div>
<div class="artifacthub-widget-group" data-url="https://artifacthub.io/packages/search?page=1&ts_query_web=hub"
data-theme="light" data-header="false" data-color="#417598" data-responsive="true" data-loading="true"></div>
</body>
</html> </html>

View File

@ -83,6 +83,8 @@ const getRepoKindName = (repoKind: RepositoryKind): string | null => {
return 'kyverno'; return 'kyverno';
case RepositoryKind.KnativeClientPlugin: case RepositoryKind.KnativeClientPlugin:
return 'knative-client-plugin'; return 'knative-client-plugin';
case RepositoryKind.Backstage:
return 'backstage';
default: default:
return null; return null;
} }

View File

@ -144,6 +144,13 @@ describe('Image', () => {
expect(image).toHaveProperty('src', 'https://localhost:8000/static/media/placeholder_pkg_knative.png'); expect(image).toHaveProperty('src', 'https://localhost:8000/static/media/placeholder_pkg_knative.png');
}); });
it('renders Backstage plugin icon', () => {
render(<Image {...defaultProps} kind={RepositoryKind.Backstage} />);
const image = screen.getByAltText('alt image');
expect(image).toBeInTheDocument();
expect(image).toHaveProperty('src', 'https://localhost:8000/static/media/placeholder_pkg_backstage.png');
});
it('renders placeholder icon', () => { it('renders placeholder icon', () => {
render(<Image {...defaultProps} placeholderIcon={<>icon</>} />); render(<Image {...defaultProps} placeholderIcon={<>icon</>} />);
expect(screen.getByText('icon')).toBeInTheDocument(); expect(screen.getByText('icon')).toBeInTheDocument();

View File

@ -63,6 +63,8 @@ const Image = (props: Props) => {
return '/static/media/placeholder_pkg_kyverno.png'; return '/static/media/placeholder_pkg_kyverno.png';
case RepositoryKind.KnativeClientPlugin: case RepositoryKind.KnativeClientPlugin:
return '/static/media/placeholder_pkg_knative.png'; return '/static/media/placeholder_pkg_knative.png';
case RepositoryKind.Backstage:
return '/static/media/placeholder_pkg_backstage.png';
default: default:
return PLACEHOLDER_SRC; return PLACEHOLDER_SRC;
} }

View File

@ -29,6 +29,7 @@ const ICONS: IconsList = {
[RepositoryKind.Gatekeeper]: <SVGIcons name="gatekeeper" />, [RepositoryKind.Gatekeeper]: <SVGIcons name="gatekeeper" />,
[RepositoryKind.Kyverno]: <SVGIcons name="kyverno" />, [RepositoryKind.Kyverno]: <SVGIcons name="kyverno" />,
[RepositoryKind.KnativeClientPlugin]: <SVGIcons name="knative" />, [RepositoryKind.KnativeClientPlugin]: <SVGIcons name="knative" />,
[RepositoryKind.Backstage]: <SVGIcons name="backstage" />,
}; };
const RepositoryIcon = (props: Props) => ( const RepositoryIcon = (props: Props) => (

View File

@ -84,6 +84,10 @@ const REPOSITORY_KINDS: RepoKindDef[] = [
kind: RepositoryKind.KnativeClientPlugin, kind: RepositoryKind.KnativeClientPlugin,
name: 'Knative client plugin', name: 'Knative client plugin',
}, },
{
kind: RepositoryKind.Backstage,
name: 'Backstage plugin',
},
]; ];
const Wrapper = styled('span')` const Wrapper = styled('span')`

View File

@ -108,6 +108,11 @@ describe('SVGIcons', () => {
expect(screen.getByTitle('knative')); expect(screen.getByTitle('knative'));
}); });
it('renders backstage icon', () => {
render(<SVGIcons name="backstage" />);
expect(screen.getByTitle('backstage'));
});
it('does not render when name is not in the list', () => { it('does not render when name is not in the list', () => {
render(<SVGIcons name="not-listed" />); render(<SVGIcons name="not-listed" />);
expect(screen.getByTestId('iconWrapper')).toBeEmptyDOMElement(); expect(screen.getByTestId('iconWrapper')).toBeEmptyDOMElement();

View File

@ -904,6 +904,19 @@ const SVGIcons = (props: Props) => (
</svg> </svg>
); );
case 'backstage':
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 337.465 428.499">
<title>{props.name}</title>
<path
d="M303.029 166.048a80.384 80.384 0 0 0 13.448-10.371c.794-.762 1.55-1.53 2.3-2.3a82.049 82.049 0 0 0 7.93-9.379 63.424 63.424 0 0 0 6.27-10.765 48.616 48.616 0 0 0 4.358-16.398c1.483-19.391-9.967-38.675-35.622-54.227L198.557 0 78.297 115.227 0 190.247l108.601 65.909a111.605 111.605 0 0 0 57.762 16.415c24.922 0 48.801-8.803 66.419-25.686 19.158-18.362 25.52-42.128 13.697-61.872a49.009 49.009 0 0 0-6.799-8.868 89.326 89.326 0 0 0 19.286 2.143c.052 0 .101.004.151.004a85.012 85.012 0 0 0 30.97-5.793 80.536 80.536 0 0 0 12.942-6.45Zm-100.584 59.808c-19.32 18.51-50.398 21.236-75.7 5.9l-75.132-45.608 67.444-64.643 76.417 46.386c27.537 16.693 26.017 39.716 6.971 57.965Zm8.934-82.218-70.647-42.888 64.407-61.769 69.37 42.115c25.94 15.724 29.31 37.04 10.55 55.017a60.705 60.705 0 0 1-73.68 7.525Zm29.866 190.04c-19.577 18.757-46.17 29.088-74.882 29.088a123.816 123.816 0 0 1-64.107-18.193L0 282.517v24.677l108.601 65.908a111.605 111.605 0 0 0 57.762 16.415c24.922 0 48.801-8.802 66.419-25.686 12.877-12.342 19.99-27.13 19.68-41.493l-.002-1.791a87.095 87.095 0 0 1-11.215 13.132Zm0-38.983c-19.577 18.756-46.17 29.088-74.882 29.088a123.816 123.816 0 0 1-64.107-18.193L0 243.533v24.68l108.601 65.907a111.606 111.606 0 0 0 57.762 16.416c24.922 0 48.801-8.803 66.419-25.686 12.877-12.344 19.99-27.13 19.68-41.495l-.002-1.79a86.98 86.98 0 0 1-11.215 13.13Zm0-38.981c-19.577 18.756-46.17 29.088-74.882 29.088a123.816 123.816 0 0 1-64.107-18.194L0 204.552v24.677l108.601 65.908a111.606 111.606 0 0 0 57.762 16.416c24.922 0 48.801-8.803 66.419-25.687 12.877-12.341 19.99-27.13 19.68-41.493l-.002-1.823a86.471 86.471 0 0 1-11.215 13.164Zm83.69 25.738a94.137 94.137 0 0 1-60.191 25.856v-.002h-.012l.012 26.667a81.653 81.653 0 0 0 51.733-22.368c13.968-13.385 21.144-28.111 20.985-42.642v-2.197a95.161 95.161 0 0 1-12.527 14.686Zm-83.69 91.208c-19.577 18.756-46.17 29.088-74.882 29.088a123.816 123.816 0 0 1-64.107-18.193L0 321.498v24.677l108.601 65.908a111.605 111.605 0 0 0 57.762 16.416c24.922 0 48.801-8.803 66.419-25.686 12.877-12.342 19.99-27.13 19.68-41.494l-.002-1.789a86.983 86.983 0 0 1-11.215 13.13Zm85.744-210.206c-.678.686-1.345 1.373-2.054 2.052a99.296 99.296 0 0 1-22.23 15.687 94.68 94.68 0 0 1-26.247 8.711 98.087 98.087 0 0 1-14.16 1.567c.505 1.612.903 3.25 1.25 4.902a53.16 53.16 0 0 1 1.138 12.001l.004 9.653h.054a84.27 84.27 0 0 0 25.344-5.547 80.954 80.954 0 0 0 26.389-16.822c.802-.77 1.504-1.558 2.263-2.336a82.049 82.049 0 0 0 7.927-9.38 63.328 63.328 0 0 0 6.283-10.776 48.442 48.442 0 0 0 4.32-16.441c.09-1.236.204-2.474.192-3.708v-2.048q-1.08 1.552-2.257 3.089a97.666 97.666 0 0 1-8.216 9.396Zm-.031 77.92c-.687.695-1.306 1.408-2.023 2.095a94.13 94.13 0 0 1-60.191 25.857v-.002h-.03l.012 26.668h.014l.004.002v-.002a81.653 81.653 0 0 0 51.733-22.369 73.547 73.547 0 0 0 16.473-22.492 48.454 48.454 0 0 0 4.32-16.442c.09-1.238.204-2.473.192-3.71v-2.193c-.744 1.075-1.465 2.154-2.273 3.217a97.104 97.104 0 0 1-8.231 9.372Zm0-38.98c-.687.694-1.304 1.406-2.023 2.093a97.185 97.185 0 0 1-22.25 15.675 95.669 95.669 0 0 1-37.941 10.18h-.048l.012 26.668h.03l.006.002v-.002a81.65 81.65 0 0 0 51.733-22.37c.802-.77 1.504-1.559 2.263-2.337a82.053 82.053 0 0 0 7.927-9.38 63.33 63.33 0 0 0 6.283-10.775 48.442 48.442 0 0 0 4.32-16.442c.09-1.236.204-2.474.192-3.708v-2.193c-.744 1.075-1.465 2.153-2.273 3.216a97.337 97.337 0 0 1-8.231 9.372Z"
style={{
fill: '#7df3e1',
}}
/>
</svg>
);
default: default:
return null; return null;
} }

View File

@ -40,6 +40,7 @@ export enum RepositoryKind {
Gatekeeper, Gatekeeper,
Kyverno, Kyverno,
KnativeClientPlugin, KnativeClientPlugin,
Backstage,
} }
export interface SearchResults { export interface SearchResults {