Merge remote-tracking branch 'upstream/master' into airbrake-no-projectid-no-error

# Conflicts:
#	ADOPTERS.md
This commit is contained in:
Karan Shah
2022-03-04 14:12:06 +00:00
90 changed files with 383 additions and 611 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog': minor
---
**BREAKING**: Removed the `AboutCard` component which has been replaced by `EntityAboutCard`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-react': minor
---
**BREAKING**: Removed `reduceCatalogFilters` and `reduceEntityFilters` due to low external utility value.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
chore(deps): bump `minimatch` from 5.0.0 to 5.0.1
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/core-components': patch
'@backstage/plugin-gcp-projects': patch
---
chore(deps): bump `@react-hookz/web` from 12.3.0 to 13.0.0
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog': minor
---
**BREAKING**: Removed `CatalogResultListItemProps` and `CatalogResultListItem`, replaced by `CatalogSearchResultListItemProps` and `CatalogSearchResultListItem`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder': minor
---
**BREAKING**: Removed `ScaffolderTaskOutput.entityRef` and `ScaffolderTaskOutput.remoteUrl`, which both have been deprecated for over a year. Please use the `links` output instead.
+10
View File
@@ -0,0 +1,10 @@
---
'@backstage/plugin-catalog-backend': patch
---
**BREAKING**:
- Removed the previously deprecated `runPeriodically` export. Please use the `@backstage/backend-tasks` package instead, or copy [the actual implementation](https://github.com/backstage/backstage/blob/02875d4d56708c60f86f6b0a5b3da82e24988354/plugins/catalog-backend/src/util/runPeriodically.ts#L29) into your own code if you explicitly do not want coordination of task runs across your worker nodes.
- Removed the previously deprecated `CatalogProcessorLocationResult.optional` field. Please set the corresponding `LocationSpec.presence` field to `'optional'` instead.
- Related to the previous point, the `processingResult.location` function no longer has a second boolean `optional` argument. Please set the corresponding `LocationSpec.presence` field to `'optional'` instead.
- Removed the previously deprecated `StaticLocationProcessor`. It has not been in use for some time; its functionality is covered by `ConfigLocationEntityProvider` instead.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-catalog': patch
'@backstage/plugin-rollbar': patch
---
Removed usage of removed hook.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-react': minor
---
**BREAKING**: Removed `useEntityCompoundName`, use `useRouteRefParams(entityRouteRef)` instead.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/create-app': patch
---
Updated the template to write the Backstage release version to `backstage.json`, rather than the version of `@backstage/create-app`. This change is applied automatically when running `backstage-cli versions:bump` in the latest version of the Backstage CLI.
+8 -1
View File
@@ -1,3 +1,10 @@
{
"currentReleaseVersion": {}
"currentReleaseVersion": {
"@backstage/backend-common": "0.12.1",
"@backstage/catalog-model": "0.12.1",
"@backstage/cli": "0.15.1",
"@backstage/plugin-catalog-backend": "0.23.1",
"@backstage/plugin-catalog-common": "0.2.1",
"@backstage/plugin-catalog-react": "0.8.1"
}
}
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Applied the fix from version `0.15.1` of this package, which was part of the `v0.70.1` release of Backstage.
+9
View File
@@ -0,0 +1,9 @@
---
'@backstage/backend-common': patch
'@backstage/catalog-model': patch
'@backstage/plugin-catalog-backend': patch
'@backstage/plugin-catalog-common': patch
'@backstage/plugin-catalog-react': patch
---
Applied the fix for the `/alpha` entry point resolution that was part of the `v0.70.1` release of Backstage.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-react': minor
---
**BREAKING**: Removed `useEntityListProvider` use `useEntityList` instead.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/catalog-model': minor
---
**BREAKING**: The default validator for `metadata.tags` now permits the colon (`:`) character as well.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
The backend development setup now ignores the `"browser"` and `"module"` entry points in `package.json`, and instead always uses `"main"`.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-catalog-react': patch
'@backstage/plugin-org': patch
---
Fixed EntityOwnerPicker and OwnershipCard url filter issue with more than 21 owners
+8
View File
@@ -0,0 +1,8 @@
---
'@backstage/catalog-model': patch
---
**DEPRECATION**:
- Deprecated `CommonValidatorFunctions.isValidString`, please use `isNonEmptyString` instead which is equivalent but better named.
- Deprecated `CommonValidatorFunctions.isValidTag`, with no replacement. Its purpose was too specific and not reusable, so it will be removed.
+8
View File
@@ -0,0 +1,8 @@
---
'@backstage/backend-common': minor
---
**BREAKING**:
- Removed the (since way back) deprecated `createDatabase` export, please use `createDatabaseClient` instead.
- Removed the (since way back) deprecated `SingleConnectionDatabaseManager` export, please use `DatabaseManager` instead.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend': patch
---
Allow passing more repo configuration for `publish:github` action
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/catalog-client': minor
---
**BREAKING**: Removed `CatalogClient.getLocationByEntity` and `CatalogClient.getOriginLocationByEntity` which had previously been deprecated. Please use `CatalogApi.getLocationByRef` instead. Note that this only affects you if you were using `CatalogClient` (the class) directly, rather than `CatalogApi` (the interface), since it has been removed from the interface in an earlier release.
+1
View File
@@ -102,3 +102,4 @@ _If you're using Backstage in your organization, please try to add your company
| [Grupo Boticário](https://www.grupoboticario.com.br/) | [@chicoribas](https://github.com/chicoribas), [@fndiaz](https://github.com/fndiaz), [@digogid](https://github.com/digogid), [@manumbs](https://github.com/manumbs), [@haooliveira84](https://github.com/haooliveira84), [@Rhullyam](https://github.com/Rhullyam) | Centralized developer portal with catalog, documentation, and software templates to build a ready to go application, including pipelines (CI/CD) and cloud services provisioning |
| [Snyk](https://snyk.io/) | [@robcresswell](https://github.com/robcresswell) | Developer portal for software discovery, API documentation and templating |
| [Stilingue](https://www.stilingue.com.br/) | [@stilingue-inteligencia-artificial](https://github.com/Stilingue-IA), [@bbviana](https://github.com/bbviana) | Developer portal, services catalog and centralization of metrics from Grafana, Sentry and GCP. Furthermore, centralization of documentation and infra details like DNS, Network services, SSL and so on. |
| [TUI Group](https://www.tuigroup.com/) | [Simon Stamm](https://github.com/simonstamm), [Christian Rudolph](https://github.com/ChrisRu82) | Developer portal for all engineer to provide discoverability to all internal components, APIs, documentation and to scaffold templates with integrations to our internal tools extended by plugins from our community. |
@@ -49,29 +49,6 @@ yarn add sqlite3
From an operational perspective, you only need to install drivers for clients
that are actively used.
### Database Manager
Existing Backstage instances should be updated to use `DatabaseManager` from
`@backstage/backend-common` in your `packages/backend/src/index.ts` file, the
`SingleConnectionDatabaseManager` has been deprecated. Import the manager and
update the references as shown below if this is not the case:
```diff
import {
- SingleConnectionDatabaseManager,
+ DatabaseManager,
} from '@backstage/backend-common';
// ...
function makeCreateEnv(config: Config) {
// ...
- const databaseManager = SingleConnectionDatabaseManager.fromConfig(config);
+ const databaseManager = DatabaseManager.fromConfig(config);
// ...
}
```
## Configuration
You should set the base database client and connection information in your
+6
View File
@@ -1,5 +1,11 @@
# @backstage/backend-common
## 0.12.1
### Patch Changes
- Fixed runtime resolution of the `/alpha` entry point.
## 0.12.0
### Minor Changes
-6
View File
@@ -170,9 +170,6 @@ export class Contexts {
): Context;
}
// @public @deprecated
export const createDatabase: typeof createDatabaseClient;
// @public
export function createDatabaseClient(
dbConfig: Config,
@@ -574,9 +571,6 @@ export type ServiceBuilder = {
// @public
export function setRootLogger(newLogger: winston.Logger): void;
// @public @deprecated
export const SingleConnectionDatabaseManager: typeof DatabaseManager;
// @public
export class SingleHostDiscovery implements PluginEndpointDiscovery {
static fromConfig(
@@ -1,99 +0,0 @@
/*
* Copyright 2020 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 { ConfigReader } from '@backstage/config';
import { createDatabaseClient, ensureDatabaseExists } from './connection';
import { SingleConnectionDatabaseManager } from './SingleConnection';
jest.mock('./connection', () => ({
...jest.requireActual('./connection'),
createDatabaseClient: jest.fn(),
ensureDatabaseExists: jest.fn(),
}));
describe('SingleConnectionDatabaseManager', () => {
const defaultConfigOptions = {
backend: {
database: {
client: 'pg',
connection: {
host: 'localhost',
user: 'foo',
password: 'bar',
database: 'foodb',
},
},
},
};
const defaultConfig = () => new ConfigReader(defaultConfigOptions);
// This is similar to the ts-jest `mocked` helper.
const mocked = (f: Function) => f as jest.Mock;
afterEach(() => jest.resetAllMocks());
describe('SingleConnectionDatabaseManager.fromConfig', () => {
it('accesses the backend.database key', () => {
const config = defaultConfig();
const getConfig = jest.spyOn(config, 'getConfig');
SingleConnectionDatabaseManager.fromConfig(config);
expect(getConfig.mock.calls[0][0]).toEqual('backend.database');
});
});
describe('SingleConnectionDatabaseManager.forPlugin', () => {
const manager = SingleConnectionDatabaseManager.fromConfig(defaultConfig());
it('connects to a database scoped to the plugin', async () => {
const pluginId = 'test1';
await manager.forPlugin(pluginId).getClient();
expect(mocked(createDatabaseClient)).toHaveBeenCalledTimes(1);
const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1);
const callArgs = mockCalls[0];
expect(callArgs[1].connection.database).toEqual(
`backstage_plugin_${pluginId}`,
);
});
it('provides different plugins different databases', async () => {
const plugin1Id = 'test1';
const plugin2Id = 'test2';
await manager.forPlugin(plugin1Id).getClient();
await manager.forPlugin(plugin2Id).getClient();
expect(mocked(createDatabaseClient)).toHaveBeenCalledTimes(2);
const mockCalls = mocked(createDatabaseClient).mock.calls;
const plugin1CallArgs = mockCalls[0];
const plugin2CallArgs = mockCalls[1];
expect(plugin1CallArgs[1].connection.database).not.toEqual(
plugin2CallArgs[1].connection.database,
);
});
it('ensure plugin database is created', async () => {
await manager.forPlugin('test').getClient();
const mockCalls = mocked(ensureDatabaseExists).mock.calls.splice(-1);
const [_, database] = mockCalls[0];
expect(database).toEqual('backstage_plugin_test');
});
});
});
@@ -1,28 +0,0 @@
/*
* Copyright 2020 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 { DatabaseManager } from './DatabaseManager';
/**
* Implements a Database Manager which will automatically create new databases
* for plugins when requested. All requested databases are created with the
* credentials provided; if the database already exists no attempt to create
* the database will be made.
*
* @public
* @deprecated Use `DatabaseManager` from `@backend-common` instead.
*/
export const SingleConnectionDatabaseManager = DatabaseManager;
@@ -57,14 +57,6 @@ export function createDatabaseClient(
);
}
/**
* Alias for {@link createDatabaseClient}
*
* @public
* @deprecated Use createDatabaseClient instead
*/
export const createDatabase = createDatabaseClient;
/**
* Ensures that the given databases all exist, creating them if they do not.
*
@@ -14,18 +14,13 @@
* limitations under the License.
*/
export * from './SingleConnection';
export * from './DatabaseManager';
/*
* Undocumented API surface from connection is being reduced for future deprecation.
* Avoid exporting additional symbols.
*/
export {
createDatabaseClient,
createDatabase,
ensureDatabaseExists,
} from './connection';
export { createDatabaseClient, ensureDatabaseExists } from './connection';
export type { PluginDatabaseManager } from './types';
export { isDatabaseConflictError } from './util';
-10
View File
@@ -107,11 +107,6 @@ export class CatalogClient implements CatalogApi {
request: GetEntityFacetsRequest,
options?: CatalogRequestOptions,
): Promise<GetEntityFacetsResponse>;
// @deprecated (undocumented)
getLocationByEntity(
entity: Entity,
options?: CatalogRequestOptions,
): Promise<Location_2 | undefined>;
getLocationById(
id: string,
options?: CatalogRequestOptions,
@@ -120,11 +115,6 @@ export class CatalogClient implements CatalogApi {
locationRef: string,
options?: CatalogRequestOptions,
): Promise<Location_2 | undefined>;
// @deprecated (undocumented)
getOriginLocationByEntity(
entity: Entity,
options?: CatalogRequestOptions,
): Promise<Location_2 | undefined>;
refreshEntity(
entityRef: string,
options?: CatalogRequestOptions,
@@ -15,8 +15,6 @@
*/
import {
ANNOTATION_LOCATION,
ANNOTATION_ORIGIN_LOCATION,
Entity,
CompoundEntityRef,
parseEntityRef,
@@ -308,49 +306,6 @@ export class CatalogClient implements CatalogApi {
};
}
/**
* @deprecated please use getLocationByRef instead
*/
async getOriginLocationByEntity(
entity: Entity,
options?: CatalogRequestOptions,
): Promise<Location | undefined> {
const locationCompound =
entity.metadata.annotations?.[ANNOTATION_ORIGIN_LOCATION];
if (!locationCompound) {
return undefined;
}
const all: { data: Location }[] = await this.requestRequired(
'GET',
'/locations',
options,
);
return all
.map(r => r.data)
.find(l => locationCompound === stringifyLocationRef(l));
}
/**
* @deprecated please use getLocationByRef instead
*/
async getLocationByEntity(
entity: Entity,
options?: CatalogRequestOptions,
): Promise<Location | undefined> {
const locationCompound = entity.metadata.annotations?.[ANNOTATION_LOCATION];
if (!locationCompound) {
return undefined;
}
const all: { data: Location }[] = await this.requestRequired(
'GET',
'/locations',
options,
);
return all
.map(r => r.data)
.find(l => locationCompound === stringifyLocationRef(l));
}
/**
* {@inheritdoc CatalogApi.getLocationByRef}
*/
+6
View File
@@ -1,5 +1,11 @@
# @backstage/catalog-model
## 0.12.1
### Patch Changes
- Fixed runtime resolution of the `/alpha` entry point.
## 0.12.0
### Minor Changes
+3
View File
@@ -51,6 +51,7 @@ export const apiEntityV1alpha1Validator: KindValidator;
// @public
export class CommonValidatorFunctions {
static isJsonSafe(value: unknown): boolean;
static isNonEmptyString(value: unknown): value is string;
static isValidDnsLabel(value: unknown): boolean;
static isValidDnsSubdomain(value: unknown): boolean;
static isValidPrefixAndOrSuffix(
@@ -59,7 +60,9 @@ export class CommonValidatorFunctions {
isValidPrefix: (value: string) => boolean,
isValidSuffix: (value: string) => boolean,
): boolean;
// @deprecated
static isValidString(value: unknown): boolean;
// @deprecated
static isValidTag(value: unknown): boolean;
static isValidUrl(value: unknown): boolean;
}
@@ -95,6 +95,7 @@ export class FieldFormatEntityPolicy implements EntityPolicy {
expectation = 'a string that is a valid url';
break;
case 'isValidString':
case 'isNonEmptyString':
expectation = 'a non empty string';
break;
default:
@@ -156,7 +157,7 @@ export class FieldFormatEntityPolicy implements EntityPolicy {
optional(
`links.${i}.title`,
links[i]?.title,
CommonValidatorFunctions.isValidString,
CommonValidatorFunctions.isNonEmptyString,
);
optional(
`links.${i}.icon`,
@@ -226,4 +226,20 @@ describe('CommonValidatorFunctions', () => {
])(`isValidString %p ? %p`, (value, result) => {
expect(CommonValidatorFunctions.isValidString(value)).toBe(result);
});
it.each([
[null, false],
[true, false],
[7, false],
[{}, false],
['', false],
[' ', false],
[' ', false],
['abc', true],
[' abc ', true],
['abc xyz', true],
['abc xyz abc.', true],
])(`isNonEmptyString %p ? %p`, (value, result) => {
expect(CommonValidatorFunctions.isNonEmptyString(value)).toBe(result);
});
});
@@ -98,6 +98,7 @@ export class CommonValidatorFunctions {
/**
* Checks that the value is a valid tag.
*
* @deprecated This will be removed in a future release
* @param value - The value to check
*/
static isValidTag(value: unknown): boolean {
@@ -110,7 +111,7 @@ export class CommonValidatorFunctions {
}
/**
* Checks that the value is a valid URL.
* Checks that the value is a valid string URL.
*
* @param value - The value to check
*/
@@ -131,9 +132,19 @@ export class CommonValidatorFunctions {
/**
* Checks that the value is a non empty string value.
*
* @deprecated use isNonEmptyString instead
* @param value - The value to check
*/
static isValidString(value: unknown): boolean {
return typeof value === 'string' && value?.trim()?.length >= 1;
}
/**
* Checks that the value is a string value that's not empty.
*
* @param value - The value to check
*/
static isNonEmptyString(value: unknown): value is string {
return typeof value === 'string' && value?.trim()?.length >= 1;
}
}
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import { CommonValidatorFunctions } from './CommonValidatorFunctions';
import { KubernetesValidatorFunctions } from './KubernetesValidatorFunctions';
import { Validators } from './types';
@@ -27,7 +26,15 @@ const defaultValidators: Validators = {
isValidLabelValue: KubernetesValidatorFunctions.isValidLabelValue,
isValidAnnotationKey: KubernetesValidatorFunctions.isValidAnnotationKey,
isValidAnnotationValue: KubernetesValidatorFunctions.isValidAnnotationValue,
isValidTag: CommonValidatorFunctions.isValidTag,
isValidTag: (value: unknown): boolean => {
// NOTE(freben): This one is a bit of an oddball and doesn't fit well anywhere to delegate to, so it's just inlined for now.
return (
typeof value === 'string' &&
value.length >= 1 &&
value.length <= 63 &&
/^[a-z0-9:+#]+(\-[a-z0-9:+#]+)*$/.test(value)
);
},
};
/**
+6
View File
@@ -1,5 +1,11 @@
# @backstage/cli
## 0.15.1
### Patch Changes
- Fixed an issue where the release stage entry point of packages were not resolved correctly.
## 0.15.0
### Minor Changes
+1 -1
View File
@@ -89,7 +89,7 @@
"json-schema": "^0.4.0",
"jest-transform-yaml": "^1.0.0",
"lodash": "^4.17.21",
"minimatch": "5.0.0",
"minimatch": "5.0.1",
"mini-css-extract-plugin": "^2.4.2",
"npm-packlist": "^3.0.0",
"node-libs-browser": "^2.2.1",
+8 -3
View File
@@ -23,6 +23,11 @@ const SKIPPED_KEYS = ['access', 'registry', 'tag', 'alphaTypes', 'betaTypes'];
const PKG_PATH = 'package.json';
const PKG_BACKUP_PATH = 'package.json-prepack';
function resolveEntrypoint(pkg: any, name: string) {
const targetEntry = pkg.publishConfig[name] || pkg[name];
return targetEntry && joinPath('..', targetEntry);
}
// Writes e.g. alpha/package.json
async function writeReleaseStageEntrypoint(pkg: any, stage: 'alpha' | 'beta') {
await fs.ensureDir(paths.resolveTarget(stage));
@@ -31,9 +36,9 @@ async function writeReleaseStageEntrypoint(pkg: any, stage: 'alpha' | 'beta') {
{
name: pkg.name,
version: pkg.version,
main: (pkg.publishConfig.main || pkg.main) && '..',
module: (pkg.publishConfig.module || pkg.module) && '..',
browser: (pkg.publishConfig.browser || pkg.browser) && '..',
main: resolveEntrypoint(pkg, 'main'),
module: resolveEntrypoint(pkg, 'module'),
browser: resolveEntrypoint(pkg, 'browser'),
types: joinPath('..', pkg.publishConfig[`${stage}Types`]),
},
{ encoding: 'utf8', spaces: 2 },
+1 -1
View File
@@ -272,7 +272,7 @@ export async function createBackendConfig(
],
resolve: {
extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx'],
mainFields: ['browser', 'module', 'main'],
mainFields: ['main'],
modules: [paths.rootNodeModules, ...moduleDirs],
plugins: [
new LinkedPackageResolvePlugin(paths.rootNodeModules, externalPkgs),
@@ -20,6 +20,11 @@ import { join as joinPath, resolve as resolvePath } from 'path';
const SKIPPED_KEYS = ['access', 'registry', 'tag', 'alphaTypes', 'betaTypes'];
function resolveEntrypoint(pkg: any, name: string) {
const targetEntry = pkg.publishConfig[name] || pkg[name];
return targetEntry && joinPath('..', targetEntry);
}
// Writes e.g. alpha/package.json
async function writeReleaseStageEntrypoint(
pkg: any,
@@ -32,9 +37,9 @@ async function writeReleaseStageEntrypoint(
{
name: pkg.name,
version: pkg.version,
main: (pkg.publishConfig.main || pkg.main) && '..',
module: (pkg.publishConfig.module || pkg.module) && '..',
browser: (pkg.publishConfig.browser || pkg.browser) && '..',
main: resolveEntrypoint(pkg, 'main'),
module: resolveEntrypoint(pkg, 'module'),
browser: resolveEntrypoint(pkg, 'browser'),
types: joinPath('..', pkg.publishConfig[`${stage}Types`]),
},
{ encoding: 'utf8', spaces: 2 },
+1 -1
View File
@@ -65,7 +65,7 @@
"react-syntax-highlighter": "^15.4.5",
"react-text-truncate": "^0.18.0",
"react-use": "^17.3.2",
"@react-hookz/web": "^12.3.0",
"@react-hookz/web": "^13.0.0",
"react-virtualized-auto-sizer": "^1.0.6",
"react-window": "^1.8.6",
"remark-gfm": "^3.0.1",
+3 -3
View File
@@ -59,7 +59,7 @@ describe('command entrypoint', () => {
test('should call expected tasks with no path option', async () => {
const cmd = {} as unknown as Command;
await createApp(cmd, '1.0.0');
await createApp(cmd);
expect(checkAppExistsMock).toHaveBeenCalled();
expect(createTemporaryAppFolderMock).toHaveBeenCalled();
expect(templatingMock).toHaveBeenCalled();
@@ -69,7 +69,7 @@ describe('command entrypoint', () => {
it('should call expected tasks with path option', async () => {
const cmd = { path: 'myDirectory' } as unknown as Command;
await createApp(cmd, '1.0.0');
await createApp(cmd);
expect(checkPathExistsMock).toHaveBeenCalled();
expect(templatingMock).toHaveBeenCalled();
expect(buildAppMock).toHaveBeenCalled();
@@ -77,7 +77,7 @@ describe('command entrypoint', () => {
it('should not call `buildAppTask` when `skipInstall` is supplied', async () => {
const cmd = { skipInstall: true } as unknown as Command;
await createApp(cmd, '1.0.0');
await createApp(cmd);
expect(buildAppMock).not.toHaveBeenCalled();
});
});
+3 -3
View File
@@ -30,7 +30,7 @@ import {
templatingTask,
} from './lib/tasks';
export default async (cmd: Command, version: string): Promise<void> => {
export default async (cmd: Command): Promise<void> => {
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
@@ -80,7 +80,7 @@ export default async (cmd: Command, version: string): Promise<void> => {
await checkPathExistsTask(appDir);
Task.section('Preparing files');
await templatingTask(templateDir, cmd.path, answers, version);
await templatingTask(templateDir, cmd.path, answers);
} else {
// Template to temporary location, and then move files
@@ -91,7 +91,7 @@ export default async (cmd: Command, version: string): Promise<void> => {
await createTemporaryAppFolderTask(tempDir);
Task.section('Preparing files');
await templatingTask(templateDir, tempDir, answers, version);
await templatingTask(templateDir, tempDir, answers);
Task.section('Moving to final location');
await moveAppTask(tempDir, appDir, answers.name);
+1 -1
View File
@@ -38,7 +38,7 @@ const main = (argv: string[]) => {
'--skip-install',
'Skip the install and builds steps after creating the app',
)
.action(cmd => createApp(cmd, version));
.action(cmd => createApp(cmd));
program.parse(argv);
};
+3 -2
View File
@@ -18,6 +18,7 @@ import fs from 'fs-extra';
import mockFs from 'mock-fs';
import child_process from 'child_process';
import path from 'path';
import { version as releaseVersion } from '../../../../package.json';
import {
buildAppTask,
checkAppExistsTask,
@@ -195,11 +196,11 @@ describe('templatingTask', () => {
name: 'SuperCoolBackstageInstance',
dbTypeSqlite: true,
};
await templatingTask(templateDir, destinationDir, context, '1.0.0');
await templatingTask(templateDir, destinationDir, context);
expect(fs.existsSync('templatedApp/package.json')).toBe(true);
expect(fs.existsSync('templatedApp/.dockerignore')).toBe(true);
await expect(fs.readJson('templatedApp/backstage.json')).resolves.toEqual({
version: '1.0.0',
version: releaseVersion,
});
// catalog was populated with `context.name`
expect(
-9
View File
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import { BACKSTAGE_JSON } from '@backstage/cli-common';
import chalk from 'chalk';
import fs from 'fs-extra';
import handlebars from 'handlebars';
@@ -23,7 +22,6 @@ import recursive from 'recursive-readdir';
import {
basename,
dirname,
join,
resolve as resolvePath,
relative as relativePath,
} from 'path';
@@ -86,7 +84,6 @@ export async function templatingTask(
templateDir: string,
destinationDir: string,
context: any,
version: string,
) {
const files = await recursive(templateDir).catch(error => {
throw new Error(`Failed to read template directory: ${error.message}`);
@@ -136,12 +133,6 @@ export async function templatingTask(
});
}
}
await Task.forItem('creating', BACKSTAGE_JSON, () =>
fs.writeFile(
join(destinationDir, BACKSTAGE_JSON),
`{\n "version": ${JSON.stringify(version)}\n}\n`,
),
);
}
/**
+3
View File
@@ -30,6 +30,8 @@ Rollup will extract the value of the version field in each package at build time
leaving any imports in place.
*/
import { version as root } from '../../../../package.json';
import { version as appDefaults } from '../../../app-defaults/package.json';
import { version as backendCommon } from '../../../backend-common/package.json';
import { version as backendTasks } from '../../../backend-tasks/package.json';
@@ -75,6 +77,7 @@ import { version as pluginTechdocsBackend } from '../../../../plugins/techdocs-b
import { version as pluginUserSettings } from '../../../../plugins/user-settings/package.json';
export const packageVersions = {
root,
'@backstage/app-defaults': appDefaults,
'@backstage/backend-common': backendCommon,
'@backstage/backend-tasks': backendTasks,
@@ -0,0 +1,3 @@
{
"version": "{{version 'root'}}"
}
+11
View File
@@ -1,5 +1,16 @@
# @backstage/plugin-catalog-backend
## 0.23.1
### Patch Changes
- Marked `GithubMultiOrgReaderProcessor` as stable, as it was moved to `/alpha` by mistake.
- Fixed runtime resolution of the `/alpha` entry point.
- Updated dependencies
- @backstage/backend-common@0.12.1
- @backstage/catalog-model@0.12.1
- @backstage/plugin-catalog-common@0.2.1
## 0.23.0
### Minor Changes
+3 -23
View File
@@ -333,7 +333,6 @@ export type CatalogProcessorErrorResult = {
export type CatalogProcessorLocationResult = {
type: 'location';
location: LocationSpec;
optional?: boolean;
};
// @public
@@ -706,7 +705,7 @@ export type GithubMultiOrgConfig = Array<{
userNamespace: string | undefined;
}>;
// @alpha
// @public
export class GithubMultiOrgReaderProcessor implements CatalogProcessor {
constructor(options: {
integrations: ScmIntegrationRegistry;
@@ -812,7 +811,7 @@ function inputError(
// @public @deprecated (undocumented)
function location_2(
newLocation: LocationSpec,
optional?: boolean,
_optional?: boolean,
): CatalogProcessorResult;
// @public (undocumented)
@@ -1007,10 +1006,7 @@ export const processingResult: Readonly<{
atLocation: LocationSpec,
message: string,
) => CatalogProcessorResult;
readonly location: (
newLocation: LocationSpec,
optional?: boolean | undefined,
) => CatalogProcessorResult;
readonly location: (newLocation: LocationSpec) => CatalogProcessorResult;
readonly entity: (
atLocation: LocationSpec,
newEntity: Entity,
@@ -1074,22 +1070,6 @@ export interface RouterOptions {
refreshService?: RefreshService;
}
// @public @deprecated
export function runPeriodically(fn: () => any, delayMs: number): () => void;
// @public @deprecated (undocumented)
export class StaticLocationProcessor implements StaticLocationProcessor {
constructor(staticLocations: LocationSpec[]);
// (undocumented)
static fromConfig(config: Config): StaticLocationProcessor;
// (undocumented)
readLocation(
location: LocationSpec,
_optional: boolean,
emit: CatalogProcessorEmit,
): Promise<boolean>;
}
// @public (undocumented)
export class UrlReaderProcessor implements CatalogProcessor {
constructor(options: { reader: UrlReader; logger: Logger_2 });
@@ -68,9 +68,9 @@ export function generalError(
*/
export function location(
newLocation: LocationSpec,
optional?: boolean,
_optional?: boolean,
): CatalogProcessorResult {
return { type: 'location', location: newLocation, optional };
return { type: 'location', location: newLocation };
}
/**
@@ -54,11 +54,8 @@ export const processingResult = Object.freeze({
return { type: 'error', location: atLocation, error: new Error(message) };
},
location(
newLocation: LocationSpec,
optional?: boolean,
): CatalogProcessorResult {
return { type: 'location', location: newLocation, optional };
location(newLocation: LocationSpec): CatalogProcessorResult {
return { type: 'location', location: newLocation };
},
entity(atLocation: LocationSpec, newEntity: Entity): CatalogProcessorResult {
@@ -147,8 +147,6 @@ export type CatalogProcessorEmit = (generated: CatalogProcessorResult) => void;
export type CatalogProcessorLocationResult = {
type: 'location';
location: LocationSpec;
/** @deprecated Set `location.presence = 'optional'` instead */
optional?: boolean;
};
/** @public */
@@ -1,59 +0,0 @@
/*
* Copyright 2020 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 { Config } from '@backstage/config';
import {
processingResult,
CatalogProcessorEmit,
LocationSpec,
} from '../../api';
/**
* @deprecated no longer in use, replaced by the ConfigLocationEntityProvider.
* @public
*/
export class StaticLocationProcessor implements StaticLocationProcessor {
static fromConfig(config: Config): StaticLocationProcessor {
const locations: LocationSpec[] = [];
const lConfigs = config.getOptionalConfigArray('catalog.locations') ?? [];
for (const lConfig of lConfigs) {
const type = lConfig.getString('type');
const target = lConfig.getString('target');
locations.push({ type, target });
}
return new StaticLocationProcessor(locations);
}
constructor(private readonly staticLocations: LocationSpec[]) {}
async readLocation(
location: LocationSpec,
_optional: boolean,
emit: CatalogProcessorEmit,
): Promise<boolean> {
if (location.type !== 'bootstrap') {
return false;
}
for (const staticLocation of this.staticLocations) {
emit(processingResult.location(staticLocation));
}
return true;
}
}
@@ -28,6 +28,5 @@ export type {
PlaceholderResolverRead,
PlaceholderResolverResolveUrl,
} from './PlaceholderProcessor';
export { StaticLocationProcessor } from './StaticLocationProcessor';
export { UrlReaderProcessor } from './UrlReaderProcessor';
export { parseEntityYaml } from '../util/parse';
@@ -40,10 +40,11 @@ import {
import { buildOrgHierarchy } from '../util/org';
/**
* @alpha
* Extracts teams and users out of a multiple GitHub orgs namespaced per org.
*
* Be aware that this processor may not be compatible with future org structures in the catalog.
*
* @public
*/
export class GithubMultiOrgReaderProcessor implements CatalogProcessor {
private readonly integrations: ScmIntegrationRegistry;
@@ -106,9 +106,8 @@ export class ProcessorOutputCollector {
this.deferredEntities.push({ entity, locationKey: location });
} else if (i.type === 'location') {
const presence = i.optional ? 'optional' : 'required';
const entity = locationSpecToLocationEntity(
{ presence, ...i.location },
i.location,
this.parentEntity,
);
const locationKey = getEntityLocationRef(entity);
@@ -14,5 +14,4 @@
* limitations under the License.
*/
export * from './runPeriodically';
export * from './RecursivePartial';
@@ -1,56 +0,0 @@
/*
* Copyright 2020 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.
*/
/**
* Runs a function repeatedly, with a fixed wait between invocations.
*
* Supports async functions, and silently ignores exceptions and rejections.
*
* @param fn - The function to run. May return a Promise.
* @param delayMs - The delay between a completed function invocation and the
* next.
* @returns A function that, when called, stops the invocation loop.
* @deprecated use \@backstage/backend-tasks package instead.
* @public
*/
export function runPeriodically(fn: () => any, delayMs: number): () => void {
let cancel: () => void;
let cancelled = false;
const cancellationPromise = new Promise<void>(resolve => {
cancel = () => {
resolve();
cancelled = true;
};
});
const startRefresh = async () => {
while (!cancelled) {
try {
await fn();
} catch {
// ignore intentionally
}
await Promise.race([
new Promise(resolve => setTimeout(resolve, delayMs)),
cancellationPromise,
]);
}
};
startRefresh();
return cancel!;
}
+6
View File
@@ -1,5 +1,11 @@
# @backstage/plugin-catalog-common
## 0.2.1
### Patch Changes
- Fixed runtime resolution of the `/alpha` entry point.
## 0.2.0
### Minor Changes
+8
View File
@@ -1,5 +1,13 @@
# @backstage/plugin-catalog-react
## 0.8.1
### Patch Changes
- Fixed runtime resolution of the `/alpha` entry point.
- Updated dependencies
- @backstage/catalog-model@0.12.1
## 0.8.0
### Minor Changes
-22
View File
@@ -477,16 +477,6 @@ export class MockStarredEntitiesApi implements StarredEntitiesApi {
toggleStarred(entityRef: string): Promise<void>;
}
// @public @deprecated (undocumented)
export function reduceCatalogFilters(
filters: EntityFilter[],
): Record<string, string | symbol | (string | symbol)[]>;
// @public @deprecated (undocumented)
export function reduceEntityFilters(
filters: EntityFilter[],
): (entity: Entity) => boolean;
// @public
export interface StarredEntitiesApi {
starredEntitie$(): Observable<Set<string>>;
@@ -522,13 +512,6 @@ export function useEntity<TEntity extends Entity = Entity>(): {
refresh?: VoidFunction;
};
// @public @deprecated
export const useEntityCompoundName: () => {
kind: string;
namespace: string;
name: string;
};
// @public @deprecated (undocumented)
export const useEntityFromUrl: () => EntityLoadingStatus;
@@ -544,11 +527,6 @@ export function useEntityList<
EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,
>(): EntityListContextProps<EntityFilters>;
// @public @deprecated
export function useEntityListProvider<
EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,
>(): EntityListContextProps<EntityFilters>;
// @public
export function useEntityOwnership(): {
loading: boolean;
-2
View File
@@ -25,11 +25,9 @@ export type {
EntityProviderProps,
AsyncEntityProviderProps,
} from './useEntity';
export { useEntityCompoundName } from './useEntityCompoundName';
export {
EntityListContext,
EntityListProvider,
useEntityListProvider,
useEntityList,
} from './useEntityListProvider';
export type {
@@ -14,7 +14,11 @@
* limitations under the License.
*/
import { Entity } from '@backstage/catalog-model';
import { errorApiRef, useApi } from '@backstage/core-plugin-api';
import {
errorApiRef,
useApi,
useRouteRefParams,
} from '@backstage/core-plugin-api';
import {
createVersionedContext,
createVersionedValueMap,
@@ -24,7 +28,7 @@ import React, { ReactNode, useEffect } from 'react';
import { useNavigate } from 'react-router';
import useAsyncRetry from 'react-use/lib/useAsyncRetry';
import { catalogApiRef } from '../api';
import { useEntityCompoundName } from './useEntityCompoundName';
import { entityRouteRef } from '../routes';
/** @public */
export type EntityLoadingStatus<TEntity extends Entity = Entity> = {
@@ -104,7 +108,7 @@ export const EntityProvider = ({ entity, children }: EntityProviderProps) => (
* @deprecated will be deleted shortly due to low external usage, re-implement if needed.
*/
export const useEntityFromUrl = (): EntityLoadingStatus => {
const { kind, namespace, name } = useEntityCompoundName();
const { kind, namespace, name } = useRouteRefParams(entityRouteRef);
const navigate = useNavigate();
const errorApi = useApi(errorApiRef);
const catalogApi = useApi(catalogApiRef);
@@ -1,27 +0,0 @@
/*
* Copyright 2020 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 { entityRouteRef } from '../routes';
import { useRouteRefParams } from '@backstage/core-plugin-api';
/**
* Grabs entity kind, namespace, and name from the location
* @public
* @deprecated use {@link @backstage/core-plugin-api#useRouteRefParams} instead
*/
export const useEntityCompoundName = () => {
const { kind, namespace, name } = useRouteRefParams(entityRouteRef);
return { kind, namespace, name };
};
@@ -195,7 +195,7 @@ export const EntityListProvider = <EntityFilters extends DefaultEntityFilters>({
});
const newParams = qs.stringify(
{ ...oldParams, filters: queryParams },
{ addQueryPrefix: true },
{ addQueryPrefix: true, arrayFormat: 'repeat' },
);
const newUrl = `${window.location.pathname}${newParams}`;
// We use direct history manipulation since useSearchParams and
@@ -249,20 +249,6 @@ export const EntityListProvider = <EntityFilters extends DefaultEntityFilters>({
);
};
/**
* Hook for interacting with the entity list context provided by the {@link EntityListProvider}.
* @public
* @deprecated use {@link useEntityList} instead.
*/
export function useEntityListProvider<
EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,
>(): EntityListContextProps<EntityFilters> {
const context = useContext(EntityListContext);
if (!context)
throw new Error('useEntityList must be used within EntityListProvider');
return context;
}
/**
* Hook for interacting with the entity list context provided by the {@link EntityListProvider}.
* @public
+8 -1
View File
@@ -30,5 +30,12 @@ export * from './filters';
export { entityRouteParams, entityRouteRef } from './routes';
export * from './testUtils';
export * from './types';
export * from './utils';
export * from './overridableComponents';
export {
getEntityMetadataEditUrl,
getEntityMetadataViewUrl,
getEntityRelations,
getEntitySourceLocation,
isOwnerOf,
} from './utils';
export type { EntitySourceLocation } from './utils';
@@ -17,10 +17,6 @@
import { Entity } from '@backstage/catalog-model';
import { EntityFilter } from '../types';
/**
* @public
* @deprecated will be made private.
*/
export function reduceCatalogFilters(
filters: EntityFilter[],
): Record<string, string | symbol | (string | symbol)[]> {
@@ -32,10 +28,6 @@ export function reduceCatalogFilters(
}, {} as Record<string, string | symbol | (string | symbol)[]>);
}
/**
* @public
* @deprecated will be made private.
*/
export function reduceEntityFilters(
filters: EntityFilter[],
): (entity: Entity) => boolean {
-9
View File
@@ -26,9 +26,6 @@ import { TableProps } from '@backstage/core-components';
import { TabProps } from '@material-ui/core';
import { UserListFilterKind } from '@backstage/plugin-catalog-react';
// @public @deprecated (undocumented)
export function AboutCard(props: AboutCardProps): JSX.Element;
// @public
export interface AboutCardProps {
// (undocumented)
@@ -104,12 +101,6 @@ export const catalogPlugin: BackstagePlugin<
}
>;
// @public @deprecated (undocumented)
export const CatalogResultListItem: typeof CatalogSearchResultListItem;
// @public @deprecated (undocumented)
export type CatalogResultListItemProps = CatalogSearchResultListItemProps;
// @public (undocumented)
export function CatalogSearchResultListItem(
props: CatalogSearchResultListItemProps,
@@ -72,7 +72,7 @@ const useStyles = makeStyles({
});
/**
* Props for {@link AboutCard}.
* Props for {@link EntityAboutCard}.
*
* @public
*/
@@ -81,8 +81,7 @@ export interface AboutCardProps {
}
/**
* @public
* @deprecated Please use EntityAboutCard instead
* Exported publicly via the EntityAboutCard
*/
export function AboutCard(props: AboutCardProps) {
const { variant } = props;
@@ -73,15 +73,3 @@ export function CatalogSearchResultListItem(
</Link>
);
}
/**
* @public
* @deprecated use {@link CatalogSearchResultListItemProps} instead
*/
export type CatalogResultListItemProps = CatalogSearchResultListItemProps;
/**
* @public
* @deprecated use {@link CatalogSearchResultListItem} instead
*/
export const CatalogResultListItem = CatalogSearchResultListItem;
@@ -14,11 +14,5 @@
* limitations under the License.
*/
export {
CatalogSearchResultListItem,
CatalogResultListItem,
} from './CatalogSearchResultListItem';
export type {
CatalogSearchResultListItemProps,
CatalogResultListItemProps,
} from './CatalogSearchResultListItem';
export { CatalogSearchResultListItem } from './CatalogSearchResultListItem';
export type { CatalogSearchResultListItemProps } from './CatalogSearchResultListItem';
@@ -33,15 +33,16 @@ import {
attachComponentData,
IconComponent,
useElementFilter,
useRouteRefParams,
} from '@backstage/core-plugin-api';
import {
EntityRefLinks,
entityRouteRef,
FavoriteEntity,
getEntityRelations,
InspectEntityDialog,
UnregisterEntityDialog,
useAsyncEntity,
useEntityCompoundName,
} from '@backstage/plugin-catalog-react';
import { Box, TabProps } from '@material-ui/core';
import { Alert } from '@material-ui/lab';
@@ -176,7 +177,7 @@ export const EntityLayout = (props: EntityLayoutProps) => {
UNSTABLE_contextMenuOptions,
children,
} = props;
const { kind, namespace, name } = useEntityCompoundName();
const { kind, namespace, name } = useRouteRefParams(entityRouteRef);
const { entity, loading, error } = useAsyncEntity();
const location = useLocation();
const routes = useElementFilter(
+6 -1
View File
@@ -22,7 +22,12 @@
export * from './apis';
export * from './components/AboutCard';
export type {
AboutCardProps,
AboutContentProps,
AboutFieldProps,
} from './components/AboutCard';
export { AboutContent, AboutField } from './components/AboutCard';
export * from './components/CatalogKindHeader';
export * from './components/CatalogSearchResultListItem';
export * from './components/CatalogTable';
+1 -1
View File
@@ -41,7 +41,7 @@
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.57",
"react-router-dom": "6.0.0-beta.0",
"@react-hookz/web": "^12.3.0"
"@react-hookz/web": "^13.0.0"
},
"peerDependencies": {
"react": "^16.13.1 || ^17.0.0"
@@ -248,7 +248,7 @@ describe('OwnershipCard', () => {
expect(getByText('OPENAPI').closest('a')).toHaveAttribute(
'href',
'/create/?filters%5Bkind%5D=API&filters%5Btype%5D=openapi&filters%5Bowners%5D%5B0%5D=my-team&filters%5Buser%5D=all',
'/create/?filters%5Bkind%5D=API&filters%5Btype%5D=openapi&filters%5Bowners%5D=my-team&filters%5Buser%5D=all',
);
});
@@ -295,7 +295,7 @@ describe('OwnershipCard', () => {
expect(getByText('OPENAPI').closest('a')).toHaveAttribute(
'href',
'/create/?filters%5Bkind%5D=API&filters%5Btype%5D=openapi&filters%5Bowners%5D%5B0%5D=user%3Athe-user&filters%5Bowners%5D%5B1%5D=my-team&filters%5Buser%5D=all',
'/create/?filters%5Bkind%5D=API&filters%5Btype%5D=openapi&filters%5Bowners%5D=user%3Athe-user&filters%5Bowners%5D=my-team&filters%5Buser%5D=all',
);
});
});
@@ -120,9 +120,14 @@ const getQueryParams = (
const user = owner as UserEntity;
filters.owners = [...filters.owners, ...user.spec.memberOf];
}
const queryParams = qs.stringify({
filters,
});
const queryParams = qs.stringify(
{
filters,
},
{
arrayFormat: 'repeat',
},
);
return queryParams;
};
@@ -14,16 +14,13 @@
* limitations under the License.
*/
import {
catalogApiRef,
useEntityCompoundName,
} from '@backstage/plugin-catalog-react';
import { catalogApiRef, entityRouteRef } from '@backstage/plugin-catalog-react';
import useAsync from 'react-use/lib/useAsync';
import { useApi } from '@backstage/core-plugin-api';
import { useApi, useRouteRefParams } from '@backstage/core-plugin-api';
export function useCatalogEntity() {
const catalogApi = useApi(catalogApiRef);
const { namespace, name } = useEntityCompoundName();
const { namespace, name } = useRouteRefParams(entityRouteRef);
const {
value: entity,
+4
View File
@@ -233,6 +233,10 @@ export function createPublishGithubAction(options: {
description?: string | undefined;
access?: string | undefined;
defaultBranch?: string | undefined;
deleteBranchOnMerge?: boolean | undefined;
allowRebaseMerge?: boolean | undefined;
allowSquashMerge?: boolean | undefined;
allowMergeCommit?: boolean | undefined;
sourcePath?: string | undefined;
requireCodeOwnerReviews?: boolean | undefined;
repoVisibility?: 'internal' | 'private' | 'public' | undefined;
@@ -88,6 +88,10 @@ describe('publish:github', () => {
name: 'repo',
org: 'owner',
private: true,
delete_branch_on_merge: false,
allow_squash_merge: true,
allow_merge_commit: true,
allow_rebase_merge: true,
visibility: 'private',
});
@@ -103,6 +107,10 @@ describe('publish:github', () => {
name: 'repo',
org: 'owner',
private: false,
delete_branch_on_merge: false,
allow_squash_merge: true,
allow_merge_commit: true,
allow_rebase_merge: true,
visibility: 'public',
});
});
@@ -123,6 +131,10 @@ describe('publish:github', () => {
description: 'description',
name: 'repo',
private: true,
delete_branch_on_merge: false,
allow_squash_merge: true,
allow_merge_commit: true,
allow_rebase_merge: true,
});
await action.handler({
@@ -138,6 +150,10 @@ describe('publish:github', () => {
description: 'description',
name: 'repo',
private: false,
delete_branch_on_merge: false,
allow_squash_merge: true,
allow_merge_commit: true,
allow_rebase_merge: true,
});
});
@@ -46,6 +46,10 @@ export function createPublishGithubAction(options: {
description?: string;
access?: string;
defaultBranch?: string;
deleteBranchOnMerge?: boolean;
allowRebaseMerge?: boolean;
allowSquashMerge?: boolean;
allowMergeCommit?: boolean;
sourcePath?: string;
requireCodeOwnerReviews?: boolean;
repoVisibility?: 'private' | 'internal' | 'public';
@@ -94,6 +98,26 @@ export function createPublishGithubAction(options: {
type: 'string',
description: `Sets the default branch on the repository. The default value is 'master'`,
},
deleteBranchOnMerge: {
title: 'Delete Branch On Merge',
type: 'boolean',
description: `Delete the branch after merging the PR. The default value is 'false'`,
},
allowMergeCommit: {
title: 'Allow Merge Commits',
type: 'boolean',
description: `Allow merge commits. The default value is 'true'`,
},
allowSquashMerge: {
title: 'Allow Squash Merges',
type: 'boolean',
description: `Allow squash merges. The default value is 'true'`,
},
allowRebaseMerge: {
title: 'Allow Rebase Merges',
type: 'boolean',
description: `Allow rebase merges. The default value is 'true'`,
},
sourcePath: {
title: 'Source Path',
description:
@@ -156,6 +180,10 @@ export function createPublishGithubAction(options: {
requireCodeOwnerReviews = false,
repoVisibility = 'private',
defaultBranch = 'master',
deleteBranchOnMerge = false,
allowMergeCommit = true,
allowSquashMerge = true,
allowRebaseMerge = true,
collaborators,
topics,
token: providedToken,
@@ -188,11 +216,19 @@ export function createPublishGithubAction(options: {
private: repoVisibility === 'private',
visibility: repoVisibility,
description: description,
delete_branch_on_merge: deleteBranchOnMerge,
allow_merge_commit: allowMergeCommit,
allow_squash_merge: allowSquashMerge,
allow_rebase_merge: allowRebaseMerge,
})
: client.rest.repos.createForAuthenticatedUser({
name: repo,
private: repoVisibility === 'private',
description: description,
delete_branch_on_merge: deleteBranchOnMerge,
allow_merge_commit: allowMergeCommit,
allow_squash_merge: allowSquashMerge,
allow_rebase_merge: allowRebaseMerge,
});
const { data: newRepo } = await repoCreationPromise;
+8 -8
View File
@@ -314,6 +314,14 @@ export interface ScaffolderGetIntegrationsListResponse {
}[];
}
// @public (undocumented)
export type ScaffolderOutputLink = {
title?: string;
icon?: string;
url?: string;
entityRef?: string;
};
// Warning: (ae-missing-release-tag) "ScaffolderPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -372,12 +380,8 @@ export type ScaffolderTask = {
createdAt: string;
};
// Warning: (ae-missing-release-tag) "ScaffolderTaskOutput" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type ScaffolderTaskOutput = {
entityRef?: string;
remoteUrl?: string;
links?: ScaffolderOutputLink[];
} & {
[key: string]: unknown;
@@ -451,8 +455,4 @@ export const TemplateTypePicker: () => JSX.Element | null;
// @public
export const useTemplateSecrets: () => ScaffolderUseTemplateSecrets;
// Warnings were encountered during analysis:
//
// src/types.d.ts:32:5 - (ae-forgotten-export) The symbol "ScaffolderOutputLink" needs to be exported by the entry point index.d.ts
```
@@ -217,12 +217,8 @@ export const TaskStatusStepper = memo(
},
);
const hasLinks = ({
entityRef,
remoteUrl,
links = [],
}: ScaffolderTaskOutput): boolean =>
!!(entityRef || remoteUrl || links.length > 0);
const hasLinks = ({ links = [] }: ScaffolderTaskOutput): boolean =>
links.length > 0;
/**
* TaskPageProps for constructing a TaskPage
@@ -26,43 +26,6 @@ describe('TaskPageLinks', () => {
jest.resetAllMocks();
});
it('renders the entityRef link', async () => {
const output = { entityRef: 'Component:default/my-app' };
const { findByText } = await renderInTestApp(
<TaskPageLinks output={output} />,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name/*': entityRouteRef,
},
},
);
const element = await findByText('Open in catalog');
expect(element).toBeInTheDocument();
expect(element).toHaveAttribute(
'href',
'/catalog/default/Component/my-app',
);
});
it('renders the remoteUrl link', async () => {
const output = { remoteUrl: 'https://remote.url' };
const { findByText } = await renderInTestApp(
<TaskPageLinks output={output} />,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name/*': entityRouteRef,
},
},
);
const element = await findByText('Repo');
expect(element).toBeInTheDocument();
expect(element).toHaveAttribute('href', 'https://remote.url');
});
it('renders further links', async () => {
const output = {
links: [
@@ -28,29 +28,13 @@ type TaskPageLinksProps = {
};
export const TaskPageLinks = ({ output }: TaskPageLinksProps) => {
const { entityRef: entityRefOutput, remoteUrl } = output;
let { links = [] } = output;
const { links = [] } = output;
const app = useApp();
const entityRoute = useRouteRef(entityRouteRef);
const iconResolver = (key?: string): IconComponent =>
key ? app.getSystemIcon(key) ?? LanguageIcon : LanguageIcon;
if (remoteUrl) {
links = [{ url: remoteUrl, title: 'Repo' }, ...links];
}
if (entityRefOutput) {
links = [
{
entityRef: entityRefOutput,
title: 'Open in catalog',
icon: 'catalog',
},
...links,
];
}
return (
<Box px={3} pb={3}>
{links
+1
View File
@@ -28,6 +28,7 @@ export type {
ScaffolderApi,
ScaffolderGetIntegrationsListOptions,
ScaffolderGetIntegrationsListResponse,
ScaffolderOutputLink,
ScaffolderScaffoldOptions,
ScaffolderScaffoldResponse,
ScaffolderStreamLogsOptions,
+3 -5
View File
@@ -43,18 +43,16 @@ export type ListActionsResponse = Array<{
};
}>;
type ScaffolderOutputLink = {
/** @public */
export type ScaffolderOutputLink = {
title?: string;
icon?: string;
url?: string;
entityRef?: string;
};
/** @public */
export type ScaffolderTaskOutput = {
/** @deprecated use the `links` property to link out to relevant resources */
entityRef?: string;
/** @deprecated use the `links` property to link out to relevant resources */
remoteUrl?: string;
links?: ScaffolderOutputLink[];
} & {
[key: string]: unknown;
+11 -4
View File
@@ -4851,6 +4851,13 @@
dependencies:
"@react-hookz/deep-equal" "^1.0.1"
"@react-hookz/web@^13.0.0":
version "13.0.0"
resolved "https://registry.npmjs.org/@react-hookz/web/-/web-13.0.0.tgz#7c4d54fb4c1edf885879914d719e35086964e46e"
integrity sha512-sAMTOiOnAvxpJtHea7Vk+2+KJdTC/o77wPII+glnH+astNUSSiduptLaMPeoIXXwqjfJT+IJ9JzWyZPrLpdyFw==
dependencies:
"@react-hookz/deep-equal" "^1.0.1"
"@rjsf/core@^3.2.1":
version "3.2.1"
resolved "https://registry.npmjs.org/@rjsf/core/-/core-3.2.1.tgz#8a7b24c9a6f01f0ecb093fdfc777172c12b1b009"
@@ -17906,10 +17913,10 @@ minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4:
dependencies:
brace-expansion "^1.1.7"
minimatch@5.0.0, minimatch@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.0.0.tgz#281d8402aaaeed18a9e8406ad99c46a19206c6ef"
integrity sha512-EU+GCVjXD00yOUf1TwAHVP7v3fBD3A8RkkPYsWWKGWesxM/572sL53wJQnHxquHlRhYUV36wHkqrN8cdikKc2g==
minimatch@5.0.1, minimatch@^5.0.0:
version "5.0.1"
resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b"
integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==
dependencies:
brace-expansion "^2.0.1"