diff --git a/.changeset/light-sheep-trade.md b/.changeset/light-sheep-trade.md new file mode 100644 index 0000000000..480ffc973e --- /dev/null +++ b/.changeset/light-sheep-trade.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Updated the ESLint plugin configuration that is enabled through `yarn start --check` to only pick up valid source files. diff --git a/.changeset/long-wolves-drive.md b/.changeset/long-wolves-drive.md new file mode 100644 index 0000000000..1c3ffadb5a --- /dev/null +++ b/.changeset/long-wolves-drive.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-octopus-deploy': patch +--- + +Added Octopus Deploy logo diff --git a/.changeset/rich-clocks-approve.md b/.changeset/rich-clocks-approve.md new file mode 100644 index 0000000000..319a8a7c9c --- /dev/null +++ b/.changeset/rich-clocks-approve.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-lighthouse-backend': patch +--- + +Update README.md; typo with plugin name & removed unneeded "import { Router } from 'express';" diff --git a/.changeset/silver-lies-rest.md b/.changeset/silver-lies-rest.md new file mode 100644 index 0000000000..34b77f5375 --- /dev/null +++ b/.changeset/silver-lies-rest.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-catalog-backend-module-gitlab': patch +--- + +The configuration key `branch` of the `GitlabDiscoveryEntityProvider` has been deprecated in favor of the configuration key `fallbackBranch`. +It will be reused in future release to enforce a concrete branch to be used in catalog file discovery. +To migrate to the new configuration value, rename `branch` to `fallbackBranch`. diff --git a/.changeset/unlucky-sloths-explain.md b/.changeset/unlucky-sloths-explain.md new file mode 100644 index 0000000000..3d5f963a73 --- /dev/null +++ b/.changeset/unlucky-sloths-explain.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs-module-addons-contrib': patch +--- + +Fixed bug in IssueLink component where the URL was not generated properly when the repository was located inside a Gitlab subgroup diff --git a/.changeset/witty-geckos-design.md b/.changeset/witty-geckos-design.md new file mode 100644 index 0000000000..d482332c23 --- /dev/null +++ b/.changeset/witty-geckos-design.md @@ -0,0 +1,9 @@ +--- +'@backstage/plugin-linguist-backend': patch +--- + +Allow kind to be configurable + +```ts +return createRouter({ schedule: schedule, kind: ['Component'] }, { ...env }); +``` diff --git a/.github/workflows/deploy_docker-image.yml b/.github/workflows/deploy_docker-image.yml index d45cc557d5..60affcb328 100644 --- a/.github/workflows/deploy_docker-image.yml +++ b/.github/workflows/deploy_docker-image.yml @@ -50,7 +50,7 @@ jobs: uses: docker/setup-buildx-action@v2 - name: Build and push - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: context: './example-app' file: ./example-app/packages/backend/Dockerfile diff --git a/.github/workflows/uffizzi-build.yml b/.github/workflows/uffizzi-build.yml index 30a3489264..d8776d74c7 100644 --- a/.github/workflows/uffizzi-build.yml +++ b/.github/workflows/uffizzi-build.yml @@ -58,7 +58,7 @@ jobs: tags: type=raw,value=60d - name: Build Image - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: context: . file: packages/backend/Dockerfile diff --git a/ADOPTERS.md b/ADOPTERS.md index f518a00338..068928260d 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -9,7 +9,6 @@ _You can do this by using the [Adopter form](https://info.backstage.spotify.com/ | [bol.com](https://www.bol.com) | [@acierto](https://github.com/acierto), [@clanghout](https://github.com/clanghout) | Initial work being done to unify platform tooling. | | [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. | | [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up | -| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. | | [SDA SE](https://sda.se) | [@dschwank](https://github.com/dschwank), [@iammnils](https://github.com/iammnils) | Central place for developing and sharing services in our insurance ecosystem. | | [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. | | [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications | diff --git a/docs/integrations/gitlab/discovery.md b/docs/integrations/gitlab/discovery.md index 9a065e776a..4c448d3892 100644 --- a/docs/integrations/gitlab/discovery.md +++ b/docs/integrations/gitlab/discovery.md @@ -21,7 +21,7 @@ catalog: gitlab: yourProviderId: host: gitlab-host # Identifies one of the hosts set up in the integrations - branch: main # Optional. Uses `master` as default + fallbackBranch: main # Optional. Fallback to be used if there is no default branch configured at the Gitlab repository. It is only used, if `branch` is undefined. Uses `master` as default group: example-group # Optional. Group and subgroup (if needed) to look for repositories. If not present the whole instance will be scanned entityFilename: catalog-info.yaml # Optional. Defaults to `catalog-info.yaml` projectPattern: '[\s\S]*' # Optional. Filters found projects based on provided patter. Defaults to `[\s\S]*`, which means to not filter anything diff --git a/microsite/data/plugins/octopus-deploy.yaml b/microsite/data/plugins/octopus-deploy.yaml new file mode 100644 index 0000000000..a6997e597d --- /dev/null +++ b/microsite/data/plugins/octopus-deploy.yaml @@ -0,0 +1,10 @@ +--- +title: 'Octopus Deploy' +author: jmezach +authorUrl: https://jmezach.github.io/ +category: CI/CD +description: Easily view your Octopus Deploy releases within the Software Catalog +documentation: https://github.com/backstage/backstage/blob/master/plugins/octopus-deploy/README.md +iconUrl: img/octopus-deploy.svg +npmPackageName: '@backstage/plugin-octopus-deploy' +addedDate: '2023-02-24' diff --git a/microsite/static/img/octopus-deploy.svg b/microsite/static/img/octopus-deploy.svg new file mode 100644 index 0000000000..a3f7927617 --- /dev/null +++ b/microsite/static/img/octopus-deploy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index a6d757028f..01f323a082 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -102,7 +102,7 @@ export async function createConfig( }), new ESLintPlugin({ context: paths.targetPath, - files: ['**', '!**/__tests__/**', '!**/?(*.)(spec|test).*'], + files: ['**/*.(ts|tsx|mts|cts|js|jsx|mjs|cjs)'], }), ); } @@ -330,7 +330,7 @@ export async function createBackendConfig( typescript: { configFile: paths.targetTsConfig }, }), new ESLintPlugin({ - files: ['**', '!**/__tests__/**', '!**/?(*.)(spec|test).*'], + files: ['**/*.(ts|tsx|mts|cts|js|jsx|mjs|cjs)'], }), ] : []), diff --git a/plugins/.changeset/dull-adults-drum.md b/plugins/.changeset/dull-adults-drum.md new file mode 100644 index 0000000000..088eb1539e --- /dev/null +++ b/plugins/.changeset/dull-adults-drum.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-adr': patch +--- + +Updated readme instructions diff --git a/plugins/adr/README.md b/plugins/adr/README.md index a669762dad..7feb9b03b0 100644 --- a/plugins/adr/README.md +++ b/plugins/adr/README.md @@ -28,7 +28,7 @@ yarn --cwd packages/app add @backstage/plugin-adr import { EntityAdrContent, isAdrAvailable } from '@backstage/plugin-adr'; ... - +// Note: Add to any other Pages as well (e.g. defaultEntityPage and websiteEntityPage) const serviceEntityPage = ( {/* other tabs... */} @@ -69,16 +69,46 @@ Afterwards, add the following code snippet to use `AdrSearchResultListItem` when ```tsx // In packages/app/src/components/search/SearchPage.tsx import { AdrSearchResultListItem } from '@backstage/plugin-adr'; +import { AdrDocument } from '@backstage/plugin-adr-common'; +... +// Optional - Add type to side pane +, + }, + ]} +/> ... -case 'adr': - return ( - - ); +// In results + + {({ results }) => ( + + {results.map(({ type, document, highlight, rank }) => { + switch (type) { + ... + case 'adr': + return ( + + ); + ... + } + })} + + )} + ``` ## Custom ADR formats diff --git a/plugins/catalog-backend-module-gitlab/src/lib/types.ts b/plugins/catalog-backend-module-gitlab/src/lib/types.ts index 2b2b888851..c3fc8a4ede 100644 --- a/plugins/catalog-backend-module-gitlab/src/lib/types.ts +++ b/plugins/catalog-backend-module-gitlab/src/lib/types.ts @@ -61,7 +61,15 @@ export type GitlabProviderConfig = { host: string; group: string; id: string; - branch: string; + /** + * @deprecated use `fallbackBranch` instead + */ + branch?: string; + /** + * If there is no default branch defined at the project, this fallback is used to discover catalog files. + * Defaults to: `master` + */ + fallbackBranch: string; catalogFile: string; projectPattern: RegExp; userPattern: RegExp; diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts index 1cc818c811..f0d88c34f5 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts @@ -61,7 +61,10 @@ export class GitlabDiscoveryEntityProvider implements EntityProvider { throw new Error('Either schedule or scheduler must be provided.'); } - const providerConfigs = readGitlabConfigs(config); + const providerConfigs = readGitlabConfigs( + config, + options.logger.child({ target: 'GitlabDiscoveryEntityProvider' }), + ); const integrations = ScmIntegrations.fromConfig(config).gitlab; const providers: GitlabDiscoveryEntityProvider[] = []; @@ -177,11 +180,15 @@ export class GitlabDiscoveryEntityProvider implements EntityProvider { continue; } - if (this.config.branch === '*' && project.default_branch === undefined) { + if ( + this.config.fallbackBranch === '*' && + project.default_branch === undefined + ) { continue; } - const project_branch = project.default_branch ?? this.config.branch; + const project_branch = + project.default_branch ?? this.config.fallbackBranch; const projectHasFile: boolean = await client.hasFile( project.path_with_namespace ?? '', @@ -204,7 +211,7 @@ export class GitlabDiscoveryEntityProvider implements EntityProvider { } private createLocationSpec(project: GitLabProject): LocationSpec { - const project_branch = project.default_branch ?? this.config.branch; + const project_branch = project.default_branch ?? this.config.fallbackBranch; return { type: 'url', target: `${project.web_url}/-/blob/${project_branch}/${this.config.catalogFile}`, diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts index f90a61f9e9..ad9d9dae79 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts @@ -72,7 +72,10 @@ export class GitlabOrgDiscoveryEntityProvider implements EntityProvider { throw new Error('Either schedule or scheduler must be provided.'); } - const providerConfigs = readGitlabConfigs(config); + const providerConfigs = readGitlabConfigs( + config, + options.logger.child({ target: 'GitlabOrgDiscoveryEntityProvider' }), + ); const integrations = ScmIntegrations.fromConfig(config).gitlab; const providers: GitlabOrgDiscoveryEntityProvider[] = []; diff --git a/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts b/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts index e72c38e9d1..c9b35e78c2 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts @@ -17,6 +17,9 @@ import { ConfigReader } from '@backstage/config'; import { Duration } from 'luxon'; import { readGitlabConfigs } from './config'; +import { getVoidLogger } from '@backstage/backend-common'; + +const logger = getVoidLogger(); describe('config', () => { it('empty gitlab config', () => { @@ -26,7 +29,7 @@ describe('config', () => { }, }); - const result = readGitlabConfigs(config); + const result = readGitlabConfigs(config, logger); expect(result).toHaveLength(0); }); @@ -44,13 +47,13 @@ describe('config', () => { }, }); - const result = readGitlabConfigs(config); + const result = readGitlabConfigs(config, logger); expect(result).toHaveLength(1); result.forEach(r => expect(r).toStrictEqual({ id: 'test', group: 'group', - branch: 'master', + fallbackBranch: 'master', host: 'host', catalogFile: 'catalog-info.yaml', projectPattern: /[\s\S]*/, @@ -78,13 +81,13 @@ describe('config', () => { }, }); - const result = readGitlabConfigs(config); + const result = readGitlabConfigs(config, logger); expect(result).toHaveLength(1); result.forEach(r => expect(r).toStrictEqual({ id: 'test', group: 'group', - branch: 'not-master', + fallbackBranch: 'not-master', host: 'host', catalogFile: 'custom-file.yaml', projectPattern: /[\s\S]*/, @@ -116,13 +119,13 @@ describe('config', () => { }, }); - const result = readGitlabConfigs(config); + const result = readGitlabConfigs(config, logger); expect(result).toHaveLength(1); result.forEach(r => expect(r).toStrictEqual({ id: 'test', group: 'group', - branch: 'master', + fallbackBranch: 'master', host: 'host', catalogFile: 'catalog-info.yaml', projectPattern: /[\s\S]*/, @@ -155,7 +158,7 @@ describe('config', () => { }, }); - expect(() => readGitlabConfigs(config)).toThrow( + expect(() => readGitlabConfigs(config, logger)).toThrow( "Missing required config value at 'catalog.providers.gitlab.test.host'", ); }); @@ -174,7 +177,7 @@ describe('config', () => { }, }); - const result = readGitlabConfigs(config); + const result = readGitlabConfigs(config, logger); expect(result).toHaveLength(1); expect(result[0].group).toEqual(''); }); diff --git a/plugins/catalog-backend-module-gitlab/src/providers/config.ts b/plugins/catalog-backend-module-gitlab/src/providers/config.ts index cd856f97da..e5cf5a6298 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/config.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/config.ts @@ -17,6 +17,7 @@ import { readTaskScheduleDefinitionFromConfig } from '@backstage/backend-tasks'; import { Config } from '@backstage/config'; import { GitlabProviderConfig } from '../lib'; +import { Logger } from 'winston'; /** * Extracts the gitlab config from a config object @@ -25,11 +26,25 @@ import { GitlabProviderConfig } from '../lib'; * * @param id - The provider key * @param config - The config object to extract from + * @param logger - The logger */ -function readGitlabConfig(id: string, config: Config): GitlabProviderConfig { +function readGitlabConfig( + id: string, + config: Config, + logger: Logger, +): GitlabProviderConfig { const group = config.getOptionalString('group') ?? ''; const host = config.getString('host'); - const branch = config.getOptionalString('branch') ?? 'master'; + const branch = config.getOptionalString('branch'); + + if (branch) { + logger.warn( + 'The configuration key `branch` has been deprecated in favor of the configuration key `fallbackBranch`.', + ); + } + + const fallbackBranch = + config.getOptionalString('fallbackBranch') ?? branch ?? 'master'; const catalogFile = config.getOptionalString('entityFilename') ?? 'catalog-info.yaml'; const projectPattern = new RegExp( @@ -50,7 +65,7 @@ function readGitlabConfig(id: string, config: Config): GitlabProviderConfig { return { id, group, - branch, + fallbackBranch, host, catalogFile, projectPattern, @@ -67,8 +82,12 @@ function readGitlabConfig(id: string, config: Config): GitlabProviderConfig { * @public * * @param config - The config object to extract from + * @param logger - The logger */ -export function readGitlabConfigs(config: Config): GitlabProviderConfig[] { +export function readGitlabConfigs( + config: Config, + logger: Logger, +): GitlabProviderConfig[] { const configs: GitlabProviderConfig[] = []; const providerConfigs = config.getOptionalConfig('catalog.providers.gitlab'); @@ -78,7 +97,13 @@ export function readGitlabConfigs(config: Config): GitlabProviderConfig[] { } for (const id of providerConfigs.keys()) { - configs.push(readGitlabConfig(id, providerConfigs.getConfig(id))); + configs.push( + readGitlabConfig( + id, + providerConfigs.getConfig(id), + logger.child({ target: id }), + ), + ); } return configs; diff --git a/plugins/lighthouse-backend/README.md b/plugins/lighthouse-backend/README.md index 8ae737725c..2c648754b3 100644 --- a/plugins/lighthouse-backend/README.md +++ b/plugins/lighthouse-backend/README.md @@ -8,13 +8,12 @@ Lighthouse Backend allows you to run scheduled lighthouse Tests for each Website ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/lighthouse-backend +yarn add --cwd packages/backend @backstage/plugin-lighthouse-backend ``` 2. Create a `lighthouse.ts` file inside `packages/backend/src/plugins/`: ```typescript -import { Router } from 'express'; import { createScheduler } from '@backstage/plugin-lighthouse-backend'; import { PluginEnvironment } from '../types'; import { CatalogClient } from '@backstage/catalog-client'; diff --git a/plugins/linguist-backend/README.md b/plugins/linguist-backend/README.md index cd63f5db03..a7c89d23b3 100644 --- a/plugins/linguist-backend/README.md +++ b/plugins/linguist-backend/README.md @@ -67,6 +67,14 @@ return createRouter({ schedule: schedule, batchSize: 40 }, { ...env }); **Note:** The default batch size is 20 +## Kind + +The default setup only processes entities of kind `['API', 'Component', 'Template']`. To control the `kind` that are processed provide that to the `createRouter` function in your `packages/backend/src/plugins/linguist.ts` like this: + +```ts +return createRouter({ schedule: schedule, kind: ['Component'] }, { ...env }); +``` + ## Refresh The default setup will only generate the language breakdown for entities with the linguist annotation that have not been generated yet. If you want this process to also refresh the data you can do so by adding the `age` (as a `HumanDuration`) in your `packages/backend/src/plugins/linguist.ts` when you call `createRouter`: diff --git a/plugins/linguist-backend/api-report.md b/plugins/linguist-backend/api-report.md index 18fcec978e..d3a77bc2d7 100644 --- a/plugins/linguist-backend/api-report.md +++ b/plugins/linguist-backend/api-report.md @@ -34,6 +34,7 @@ export class LinguistBackendApi { age?: HumanDuration, batchSize?: number, useSourceLocation?: boolean, + kind?: string[], ); // (undocumented) getEntityLanguages(entityRef: string): Promise; @@ -79,6 +80,8 @@ export interface PluginOptions { // (undocumented) batchSize?: number; // (undocumented) + kind?: string[]; + // (undocumented) schedule?: TaskScheduleDefinition; // (undocumented) useSourceLocation?: boolean; diff --git a/plugins/linguist-backend/src/api/LinguistBackendApi.test.ts b/plugins/linguist-backend/src/api/LinguistBackendApi.test.ts new file mode 100644 index 0000000000..741d52e734 --- /dev/null +++ b/plugins/linguist-backend/src/api/LinguistBackendApi.test.ts @@ -0,0 +1,28 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { kindOrDefault } from './LinguistBackendApi'; + +describe('kindOrDefault', () => { + it('should return default kind when undefined', () => { + expect(kindOrDefault()).toEqual(['API', 'Component', 'Template']); + }); + it('should return the default kind when empty', () => { + expect(kindOrDefault([])).toEqual(['API', 'Component', 'Template']); + }); + it('should return provided kind when not empty', () => { + expect(kindOrDefault(['API'])).toEqual(['API']); + }); +}); diff --git a/plugins/linguist-backend/src/api/LinguistBackendApi.ts b/plugins/linguist-backend/src/api/LinguistBackendApi.ts index 105335b0ed..6a4cc0ea56 100644 --- a/plugins/linguist-backend/src/api/LinguistBackendApi.ts +++ b/plugins/linguist-backend/src/api/LinguistBackendApi.ts @@ -56,6 +56,7 @@ export class LinguistBackendApi { private readonly age?: HumanDuration; private readonly batchSize?: number; private readonly useSourceLocation?: boolean; + private readonly kind: string[]; public constructor( logger: Logger, store: LinguistBackendStore, @@ -65,6 +66,7 @@ export class LinguistBackendApi { age?: HumanDuration, batchSize?: number, useSourceLocation?: boolean, + kind?: string[], ) { this.logger = logger; this.store = store; @@ -75,6 +77,7 @@ export class LinguistBackendApi { this.batchSize = batchSize; this.age = age; this.useSourceLocation = useSourceLocation; + this.kind = kindOrDefault(kind); } public async getEntityLanguages(entityRef: string): Promise { @@ -99,7 +102,7 @@ export class LinguistBackendApi { : LINGUIST_ANNOTATION; const request: GetEntitiesRequest = { filter: { - kind: ['API', 'Component', 'Template'], + kind: this.kind, [`metadata.annotations.${annotationKey}`]: CATALOG_FILTER_EXISTS, }, fields: ['kind', 'metadata'], @@ -228,3 +231,10 @@ export class LinguistBackendApi { } } } + +export function kindOrDefault(kind?: string[]) { + if (!kind || kind.length === 0) { + return ['API', 'Component', 'Template']; + } + return kind; +} diff --git a/plugins/linguist-backend/src/service/router.ts b/plugins/linguist-backend/src/service/router.ts index 18c43d996a..66138f5ed2 100644 --- a/plugins/linguist-backend/src/service/router.ts +++ b/plugins/linguist-backend/src/service/router.ts @@ -38,6 +38,7 @@ export interface PluginOptions { age?: HumanDuration; batchSize?: number; useSourceLocation?: boolean; + kind?: string[]; } /** @public */ @@ -56,7 +57,7 @@ export async function createRouter( pluginOptions: PluginOptions, routerOptions: RouterOptions, ): Promise { - const { schedule, age, batchSize, useSourceLocation } = pluginOptions; + const { schedule, age, batchSize, useSourceLocation, kind } = pluginOptions; const { logger, reader, database, discovery, scheduler, tokenManager } = routerOptions; @@ -76,6 +77,7 @@ export async function createRouter( age, batchSize, useSourceLocation, + kind, ); if (scheduler && schedule) { diff --git a/plugins/octopus-deploy/src/components/OctopusDeployIcon/OctopusDeployIcon.tsx b/plugins/octopus-deploy/src/components/OctopusDeployIcon/OctopusDeployIcon.tsx new file mode 100644 index 0000000000..626e61ccd8 --- /dev/null +++ b/plugins/octopus-deploy/src/components/OctopusDeployIcon/OctopusDeployIcon.tsx @@ -0,0 +1,31 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { SvgIcon, SvgIconProps } from '@material-ui/core'; + +import React from 'react'; + +export const OctopusDeployIcon = (props: SvgIconProps) => ( + + + + + + +); diff --git a/plugins/octopus-deploy/src/components/OctopusDeployIcon/index.ts b/plugins/octopus-deploy/src/components/OctopusDeployIcon/index.ts new file mode 100644 index 0000000000..d78e1bfc7f --- /dev/null +++ b/plugins/octopus-deploy/src/components/OctopusDeployIcon/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { OctopusDeployIcon } from './OctopusDeployIcon'; diff --git a/plugins/octopus-deploy/src/components/ReleaseTable/ReleaseTable.tsx b/plugins/octopus-deploy/src/components/ReleaseTable/ReleaseTable.tsx index dfda291e1a..bc185546b1 100644 --- a/plugins/octopus-deploy/src/components/ReleaseTable/ReleaseTable.tsx +++ b/plugins/octopus-deploy/src/components/ReleaseTable/ReleaseTable.tsx @@ -28,6 +28,7 @@ import { Table, TableColumn, } from '@backstage/core-components'; +import { OctopusDeployIcon } from '../OctopusDeployIcon'; type ReleaseTableProps = { environments?: OctopusEnvironment[]; @@ -141,6 +142,8 @@ export const ReleaseTable = ({ }} title={ + + Octopus Deploy - Releases ({releases ? releases.length : 0}) } diff --git a/plugins/techdocs-module-addons-contrib/src/ReportIssue/IssueLink.test.tsx b/plugins/techdocs-module-addons-contrib/src/ReportIssue/IssueLink.test.tsx index 5fff56ecb7..9a34fcf011 100644 --- a/plugins/techdocs-module-addons-contrib/src/ReportIssue/IssueLink.test.tsx +++ b/plugins/techdocs-module-addons-contrib/src/ReportIssue/IssueLink.test.tsx @@ -26,7 +26,7 @@ import { import { IssueLink } from './IssueLink'; -const defaultProps = { +const defaultGithubProps = { repository: { type: 'github', name: 'backstage', @@ -40,14 +40,28 @@ const defaultProps = { }, }; +const defaultGitlabProps = { + repository: { + type: 'gitlab', + name: 'backstageSubgroup/backstage', + owner: 'backstage', + protocol: 'https', + resource: 'gitlab.com', + }, + template: { + title: 'Documentation feedback', + body: '## Documentation Feedback 📝', + }, +}; + describe('FeedbackLink', () => { const apiSpy = new MockAnalyticsApi(); - it('Should open new issue tab', () => { + it('Should open new Github issue tab', () => { render( wrapInTestApp( - + , ), ); @@ -55,19 +69,39 @@ describe('FeedbackLink', () => { const link = screen.getByText(/Open new Github issue/); expect(link).toBeInTheDocument(); expect(link).toHaveAttribute('target', '_blank'); - const encodedTitle = encodeURIComponent(defaultProps.template.title); - const encodedBody = encodeURIComponent(defaultProps.template.body); + const encodedTitle = encodeURIComponent(defaultGithubProps.template.title); + const encodedBody = encodeURIComponent(defaultGithubProps.template.body); expect(link).toHaveAttribute( 'href', `https://github.com/backstage/backstage/issues/new?title=${encodedTitle}&body=${encodedBody}`, ); }); + it('Should open new Gitlab issue tab', () => { + render( + wrapInTestApp( + + + , + ), + ); + + const link = screen.getByText(/Open new Gitlab issue/); + expect(link).toBeInTheDocument(); + expect(link).toHaveAttribute('target', '_blank'); + const encodedTitle = encodeURIComponent(defaultGithubProps.template.title); + const encodedBody = encodeURIComponent(defaultGithubProps.template.body); + expect(link).toHaveAttribute( + 'href', + `https://gitlab.com/backstage/backstageSubgroup/backstage/issues/new?issue[title]=${encodedTitle}&issue[description]=${encodedBody}`, + ); + }); + it('Should track click events', async () => { render( wrapInTestApp( - + , ), ); diff --git a/plugins/techdocs-module-addons-contrib/src/ReportIssue/IssueLink.tsx b/plugins/techdocs-module-addons-contrib/src/ReportIssue/IssueLink.tsx index bb2de22350..eb16476fa6 100644 --- a/plugins/techdocs-module-addons-contrib/src/ReportIssue/IssueLink.tsx +++ b/plugins/techdocs-module-addons-contrib/src/ReportIssue/IssueLink.tsx @@ -59,14 +59,13 @@ const getUrl = (repository: Repository, template: ReportIssueTemplate) => { const encodedTitle = encodeURIComponent(title); const encodedBody = encodeURIComponent(body); const { protocol, resource, owner, name, type } = repository; - const encodedOwner = encodeURIComponent(owner); - const encodedName = encodeURIComponent(name); - const url = `${protocol}://${resource}/${encodedOwner}/${encodedName}`; + const url = `${protocol}://${resource}/${owner}/${name}`; + const encodedUrl = encodeURI(url); if (type === 'github') { - return `${url}/issues/new?title=${encodedTitle}&body=${encodedBody}`; + return `${encodedUrl}/issues/new?title=${encodedTitle}&body=${encodedBody}`; } - return `${url}/issues/new?issue[title]=${encodedTitle}&issue[description]=${encodedBody}`; + return `${encodedUrl}/issues/new?issue[title]=${encodedTitle}&issue[description]=${encodedBody}`; }; export const IssueLink = ({ template, repository }: IssueLinkProps) => { diff --git a/yarn.lock b/yarn.lock index 54b5e304d0..a30a280e01 100644 --- a/yarn.lock +++ b/yarn.lock @@ -27430,15 +27430,15 @@ __metadata: linkType: hard "json-schema-library@npm:^7.3.9": - version: 7.4.6 - resolution: "json-schema-library@npm:7.4.6" + version: 7.4.7 + resolution: "json-schema-library@npm:7.4.7" dependencies: "@sagold/json-pointer": ^5.0.0 "@sagold/json-query": ^6.0.0 deepmerge: ^4.2.2 fast-deep-equal: ^3.1.3 valid-url: ^1.0.9 - checksum: 4639464c3c25cc6ca04b1aea081c97426598f2477bdd20e4df57e39408bcf2de7a5f54f3e108c497b3e63f41904a251fbb5444451cf7a60fd83020941c55cdda + checksum: e738f0b149d52949d854a4de718e79576fabbae206bffdb1cf895ed0b87bbf255f80f162f1924fb89311f6ac1a8e3ed0a5940b709bc3a091ad217f64de70cf60 languageName: node linkType: hard @@ -31928,9 +31928,9 @@ __metadata: linkType: hard "photoswipe@npm:^5.3.5": - version: 5.3.5 - resolution: "photoswipe@npm:5.3.5" - checksum: bd3eafebffc114210b8e5403746b48a9bfe436cca05a65f8d5efd6eacea785a51f0f124d7326c8f651d754d76124c9768a920383843c4a619da582a86e535b27 + version: 5.3.6 + resolution: "photoswipe@npm:5.3.6" + checksum: 192004e33dbba7567744c96835b43f17a118653a24e099f9e31c2ddea9d7214ac7c28ff8994d4f7c20720c7f7fd00b62ad8ade5e3d59994f00a27390419ba11c languageName: node linkType: hard