diff --git a/.changeset/little-carpets-itch.md b/.changeset/little-carpets-itch.md new file mode 100644 index 0000000000..6a4d95c140 --- /dev/null +++ b/.changeset/little-carpets-itch.md @@ -0,0 +1,14 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Added some deprecations as follows: + +- **DEPRECATED**: `TemplateCardComponent` and `TaskPageComponent` props have been deprecated, and moved to a `components` prop instead. You can pass them in through there instead. +- **DEPRECATED**: `TemplateList` and `TemplateListProps` has been deprecated. Please use the `TemplateCard` to create your own list component instead. +- **DEPRECATED**: `setSecret` has been deprecated in favour of `setSecrets` when calling `useTemplateSecrets` + +Other notable changes: + +- `scaffolderApi.scaffold()` `values` type has been narrowed from `Record` to `Record` instead. +- Moved all navigation internally over to using `routeRefs` and `subRouteRefs` diff --git a/plugins/scaffolder/api-report.md b/plugins/scaffolder/api-report.md index df8f1ca9e2..82dfecfaa5 100644 --- a/plugins/scaffolder/api-report.md +++ b/plugins/scaffolder/api-report.md @@ -20,9 +20,9 @@ import { FieldValidation } from '@rjsf/core'; import { IconButton } from '@material-ui/core'; import { JsonObject } from '@backstage/types'; import { JSONSchema7 } from 'json-schema'; +import { JsonValue } from '@backstage/types'; import { Observable } from '@backstage/types'; import { default as React_2 } from 'react'; -import { ReactNode } from 'react'; import { RouteRef } from '@backstage/core-plugin-api'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { TaskSpec } from '@backstage/plugin-scaffolder-common'; @@ -238,6 +238,31 @@ export interface RepoUrlPickerUiOptions { }; } +// Warning: (ae-missing-release-tag) "RouterProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type RouterProps = { + TemplateCardComponent?: + | ComponentType<{ + template: TemplateEntityV1beta2; + }> + | undefined; + TaskPageComponent?: ComponentType<{}>; + components?: { + TemplateCardComponent?: + | ComponentType<{ + template: TemplateEntityV1beta2; + }> + | undefined; + TaskPageComponent?: ComponentType<{}>; + }; + groups?: Array<{ + title?: string; + titleComponent?: React_2.ReactNode; + filter: (entity: Entity) => boolean; + }>; +}; + // @public export interface ScaffolderApi { // (undocumented) @@ -316,25 +341,7 @@ export interface ScaffolderGetIntegrationsListResponse { // 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) -export const ScaffolderPage: ({ - TemplateCardComponent, - TaskPageComponent, - groups, -}: { - TemplateCardComponent?: - | ComponentType<{ - template: TemplateEntityV1beta2; - }> - | undefined; - TaskPageComponent?: ComponentType<{}> | undefined; - groups?: - | { - title?: string | undefined; - titleComponent?: ReactNode; - filter: (entity: Entity) => boolean; - }[] - | undefined; -}) => JSX.Element; +export const ScaffolderPage: (props: RouterProps) => JSX.Element; // Warning: (ae-missing-release-tag) "scaffolderPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -357,7 +364,7 @@ export interface ScaffolderScaffoldOptions { // (undocumented) templateRef: string; // (undocumented) - values: Record; + values: Record; } // Warning: (ae-missing-release-tag) "ScaffolderScaffoldResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -410,6 +417,14 @@ export type ScaffolderTaskStatus = | 'completed' | 'skipped'; +// @public +export interface ScaffolderUseTemplateSecrets { + // @deprecated (undocumented) + setSecret: (input: Record) => void; + // (undocumented) + setSecrets: (input: Record) => void; +} + // @public export const TaskPage: ({ loadingText }: TaskPageProps) => JSX.Element; @@ -420,7 +435,7 @@ export type TaskPageProps = { // Warning: (ae-missing-release-tag) "TemplateList" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public @deprecated (undocumented) export const TemplateList: ({ TemplateCardComponent, group, @@ -428,7 +443,7 @@ export const TemplateList: ({ // Warning: (ae-missing-release-tag) "TemplateListProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public @deprecated (undocumented) export type TemplateListProps = { TemplateCardComponent?: | ComponentType<{ @@ -436,7 +451,7 @@ export type TemplateListProps = { }> | undefined; group?: { - title?: string; + title?: React_2.ReactNode; titleComponent?: React_2.ReactNode; filter: (entity: Entity) => boolean; }; @@ -459,9 +474,7 @@ export type TemplateParameterSchema = { export const TemplateTypePicker: () => JSX.Element | null; // @public -export const useTemplateSecrets: () => { - setSecret: (input: Record) => void; -}; +export const useTemplateSecrets: () => ScaffolderUseTemplateSecrets; // Warnings were encountered during analysis: // diff --git a/plugins/scaffolder/src/components/Router.tsx b/plugins/scaffolder/src/components/Router.tsx index 6b0de28ea9..a456f2bdd3 100644 --- a/plugins/scaffolder/src/components/Router.tsx +++ b/plugins/scaffolder/src/components/Router.tsx @@ -32,11 +32,20 @@ import { } from '../extensions'; import { useElementFilter } from '@backstage/core-plugin-api'; -type RouterProps = { +export type RouterProps = { + /** @deprecated use components.TemplateCardComponent instead */ TemplateCardComponent?: | ComponentType<{ template: TemplateEntityV1beta2 }> | undefined; + /** @deprecated use component.TaskPageComponent instead */ TaskPageComponent?: ComponentType<{}>; + + components?: { + TemplateCardComponent?: + | ComponentType<{ template: TemplateEntityV1beta2 }> + | undefined; + TaskPageComponent?: ComponentType<{}>; + }; groups?: Array<{ title?: string; titleComponent?: React.ReactNode; @@ -44,13 +53,26 @@ type RouterProps = { }>; }; -export const Router = ({ - TemplateCardComponent, - TaskPageComponent, - groups, -}: RouterProps) => { +export const Router = (props: RouterProps) => { + const { + TemplateCardComponent: legacyTemplateCardComponent, + TaskPageComponent: legacyTaskPageComponent, + groups, + components = {}, + } = props; + + if (legacyTemplateCardComponent || legacyTaskPageComponent) { + // eslint-disable-next-line no-console + console.warn( + "DEPRECATION: 'TemplateCardComponent' and 'TaskPageComponent' are deprecated when calling the 'ScaffolderPage'. Use 'components' prop to pass these component overrides instead.", + ); + } + + const { TemplateCardComponent, TaskPageComponent } = components; + const outlet = useOutlet(); - const TaskPageElement = TaskPageComponent || TaskPage; + const TaskPageElement = + TaskPageComponent ?? legacyTaskPageComponent ?? TaskPage; const customFieldExtensions = useElementFilter(outlet, elements => elements @@ -78,8 +100,10 @@ export const Router = ({ path="/" element={ } /> diff --git a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx index ec5d1272ab..da9f1f74b8 100644 --- a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx +++ b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx @@ -55,7 +55,8 @@ export type ScaffolderPageProps = { | ComponentType<{ template: TemplateEntityV1beta2 }> | undefined; groups?: Array<{ - title?: string; + title?: React.ReactNode; + /** @deprcated use title instead as it accepts a string and react component */ titleComponent?: React.ReactNode; filter: (entity: Entity) => boolean; }>; diff --git a/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx b/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx index 0882d49ff4..e0445ed641 100644 --- a/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx +++ b/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx @@ -46,9 +46,9 @@ import classNames from 'classnames'; import { DateTime, Interval } from 'luxon'; import qs from 'qs'; import React, { memo, useEffect, useMemo, useState } from 'react'; -import { generatePath, useNavigate, useParams } from 'react-router'; +import { useNavigate, useParams } from 'react-router'; import useInterval from 'react-use/lib/useInterval'; -import { rootRouteRef } from '../../routes'; +import { rootRouteRef, selectedTemplateRouteRef } from '../../routes'; import { ScaffolderTaskStatus, ScaffolderTaskOutput } from '../../types'; import { useTaskEventStream } from '../hooks/useEventStream'; import { TaskPageLinks } from './TaskPageLinks'; @@ -243,7 +243,8 @@ export type TaskPageProps = { export const TaskPage = ({ loadingText }: TaskPageProps) => { const classes = useStyles(); const navigate = useNavigate(); - const rootLink = useRouteRef(rootRouteRef); + const rootPath = useRouteRef(rootRouteRef); + const templateRoute = useRouteRef(selectedTemplateRouteRef); const [userSelectedStepId, setUserSelectedStepId] = useState< string | undefined >(undefined); @@ -297,7 +298,7 @@ export const TaskPage = ({ loadingText }: TaskPageProps) => { const handleStartOver = () => { if (!taskStream.task || !taskStream.task?.spec.templateInfo?.entityRef) { - navigate(generatePath(rootLink())); + navigate(rootPath()); return; } @@ -311,14 +312,9 @@ export const TaskPage = ({ loadingText }: TaskPageProps) => { ); navigate( - generatePath( - `${rootLink()}/templates/:templateName?${qs.stringify({ - formData: JSON.stringify(formData), - })}`, - { - templateName: name, - }, - ), + `${templateRoute({ templateName: name })}?${qs.stringify({ + formData: JSON.stringify(formData), + })}`, ); }; diff --git a/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx b/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx index 3495409dd6..f4710427c2 100644 --- a/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx +++ b/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx @@ -41,8 +41,7 @@ import { } from '@material-ui/core'; import WarningIcon from '@material-ui/icons/Warning'; import React from 'react'; -import { generatePath } from 'react-router'; -import { rootRouteRef } from '../../routes'; +import { selectedTemplateRouteRef } from '../../routes'; import { FavouriteTemplate } from '../FavouriteTemplate/FavouriteTemplate'; import { Button, ItemCardHeader } from '@backstage/core-components'; @@ -141,7 +140,7 @@ const DeprecationWarning = () => { export const TemplateCard = ({ template, deprecated }: TemplateCardProps) => { const backstageTheme = useTheme(); - const rootLink = useRouteRef(rootRouteRef); + const templateRoute = useRouteRef(selectedTemplateRouteRef); const templateProps = getTemplateCardProps(template); const ownedByRelations = getEntityRelations( template as Entity, @@ -152,9 +151,7 @@ export const TemplateCard = ({ template, deprecated }: TemplateCardProps) => { : 'other'; const theme = backstageTheme.getPageTheme({ themeId }); const classes = useStyles({ backgroundImage: theme.backgroundImage }); - const href = generatePath(`${rootLink()}/templates/:templateName`, { - templateName: templateProps.name, - }); + const href = templateRoute({ templateName: templateProps.name }); const scmIntegrationsApi = useApi(scmIntegrationsApiRef); const sourceLocation = getEntitySourceLocation(template, scmIntegrationsApi); diff --git a/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx b/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx index c95a8237eb..3d9da542de 100644 --- a/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx +++ b/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx @@ -29,17 +29,24 @@ import { useEntityList } from '@backstage/plugin-catalog-react'; import { Typography } from '@material-ui/core'; import { TemplateCard } from '../TemplateCard'; +/** + * @deprecated this type is deprecated and will be removed in a future releases, please use the TemplateCard to render your own list. + */ export type TemplateListProps = { TemplateCardComponent?: | ComponentType<{ template: TemplateEntityV1beta2 }> | undefined; group?: { - title?: string; + title?: React.ReactNode; + /** @deprecated use title instead, can be a string or a react component */ titleComponent?: React.ReactNode; filter: (entity: Entity) => boolean; }; }; +/** + * @deprecated this component is deprecated and will be removed in a future releases, please use the TemplateCard to render your own list. + */ export const TemplateList = ({ TemplateCardComponent, group, @@ -49,11 +56,24 @@ export const TemplateList = ({ const maybeFilteredEntities = group ? entities.filter(e => group.filter(e)) : entities; - const title = group ? ( - group.titleComponent || - ) : ( - - ); + + const titleComponent: React.ReactNode = (() => { + if (group?.titleComponent) { + // eslint-disable-next-line no-console + console.warn( + 'DEPRECATED: group.titleComponent is now deprecated. Use group.title instead, it can be a string or a react component', + ); + return group?.titleComponent; + } + if (group && group.title) { + if (typeof group.title === 'string') { + return ; + } + return group.title; + } + + return ; + })(); if (group && maybeFilteredEntities.length === 0) { return null; @@ -79,7 +99,7 @@ export const TemplateList = ({ )} - {title} + {titleComponent} {maybeFilteredEntities && maybeFilteredEntities?.length > 0 && diff --git a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx index 798ce31539..e69c4bab5c 100644 --- a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx +++ b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx @@ -18,13 +18,13 @@ import { LinearProgress } from '@material-ui/core'; import { FormValidation, IChangeEvent } from '@rjsf/core'; import qs from 'qs'; import React, { useCallback, useContext, useState } from 'react'; -import { generatePath, Navigate, useNavigate } from 'react-router'; +import { Navigate, useNavigate } from 'react-router'; import { useParams } from 'react-router-dom'; import useAsync from 'react-use/lib/useAsync'; import { scaffolderApiRef } from '../../api'; import { CustomFieldValidator, FieldExtensionOptions } from '../../extensions'; import { SecretsContext } from '../secrets/SecretsContext'; -import { rootRouteRef } from '../../routes'; +import { rootRouteRef, scaffolderTaskRouteRef } from '../../routes'; import { MultistepJsonForm } from '../MultistepJsonForm'; import { @@ -117,7 +117,8 @@ export const TemplatePage = ({ const scaffolderApi = useApi(scaffolderApiRef); const { templateName } = useParams(); const navigate = useNavigate(); - const rootLink = useRouteRef(rootRouteRef); + const scaffolderTaskRoute = useRouteRef(scaffolderTaskRouteRef); + const rootRoute = useRouteRef(rootRouteRef); const { schema, loading, error } = useTemplateParameterSchema(templateName); const [formState, setFormState] = useState>(() => { const query = qs.parse(window.location.search, { @@ -158,16 +159,16 @@ export const TemplatePage = ({ // extra back/forward slots. window.history?.replaceState(null, document.title, newUrl); - navigate(generatePath(`${rootLink()}/tasks/:taskId`, { taskId })); + navigate(scaffolderTaskRoute({ taskId })); }; if (error) { errorApi.post(new Error(`Failed to load template, ${error}`)); - return ; + return ; } if (!loading && !schema) { errorApi.post(new Error('Template was not found.')); - return ; + return ; } const customFieldComponents = Object.fromEntries( diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx index fe7c4b18a4..17fd5ef665 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx @@ -56,7 +56,7 @@ export const RepoUrlPicker = ( ); const integrationApi = useApi(scmIntegrationsApiRef); const scmAuthApi = useApi(scmAuthApiRef); - const { setSecret } = useTemplateSecrets(); + const { setSecrets } = useTemplateSecrets(); const allowedHosts = useMemo( () => uiSchema?.['ui:options']?.allowedHosts ?? [], [uiSchema], @@ -114,7 +114,7 @@ export const RepoUrlPicker = ( // set the secret using the key provided in the the ui:options for use // in the templating the manifest with ${{ secrets[secretsKey] }} - setSecret({ [requestUserCredentials.secretsKey]: token }); + setSecrets({ [requestUserCredentials.secretsKey]: token }); }, 500, [state, uiSchema], diff --git a/plugins/scaffolder/src/components/index.ts b/plugins/scaffolder/src/components/index.ts new file mode 100644 index 0000000000..030c485fd7 --- /dev/null +++ b/plugins/scaffolder/src/components/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2022 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 * from './fields'; +export type { RepoUrlPickerUiOptions } from './fields'; +export { FavouriteTemplate } from './FavouriteTemplate'; +export { TemplateList } from './TemplateList'; +export type { TemplateListProps } from './TemplateList'; +export { TemplateTypePicker } from './TemplateTypePicker'; +export * from './secrets'; +export { TaskPage } from './TaskPage'; +export type { RouterProps } from './Router'; diff --git a/plugins/scaffolder/src/components/secrets/SecretsContext.test.tsx b/plugins/scaffolder/src/components/secrets/SecretsContext.test.tsx index 37d35c9015..3b84c074d7 100644 --- a/plugins/scaffolder/src/components/secrets/SecretsContext.test.tsx +++ b/plugins/scaffolder/src/components/secrets/SecretsContext.test.tsx @@ -36,7 +36,7 @@ describe('SecretsContext', () => { ); expect(result.current.context?.secrets.foo).toEqual(undefined); - act(() => result.current.hook.setSecret({ foo: 'bar' })); + act(() => result.current.hook.setSecrets({ foo: 'bar' })); expect(result.current.context?.secrets.foo).toEqual('bar'); }); diff --git a/plugins/scaffolder/src/components/secrets/SecretsContext.tsx b/plugins/scaffolder/src/components/secrets/SecretsContext.tsx index 6570f9d901..f0b62f20a1 100644 --- a/plugins/scaffolder/src/components/secrets/SecretsContext.tsx +++ b/plugins/scaffolder/src/components/secrets/SecretsContext.tsx @@ -48,11 +48,21 @@ export const SecretsContextProvider = ({ children }: PropsWithChildren<{}>) => { ); }; +/** + * The return type from the useTemplateSecrets hook. + * @public + */ +export interface ScaffolderUseTemplateSecrets { + /** @deprecated use setSecrets instead */ + setSecret: (input: Record) => void; + setSecrets: (input: Record) => void; +} + /** * Hook to access the secrets context. * @public */ -export const useTemplateSecrets = () => { +export const useTemplateSecrets = (): ScaffolderUseTemplateSecrets => { const value = useContext(SecretsContext); if (!value) { throw new Error( @@ -60,14 +70,14 @@ export const useTemplateSecrets = () => { ); } - const { setSecrets } = value; + const { setSecrets: updateSecrets } = value; - const setSecret = useCallback( + const setSecrets = useCallback( (input: Record) => { - setSecrets(currentSecrets => ({ ...currentSecrets, ...input })); + updateSecrets(currentSecrets => ({ ...currentSecrets, ...input })); }, - [setSecrets], + [updateSecrets], ); - return { setSecret }; + return { setSecret: setSecrets, setSecrets }; }; diff --git a/plugins/scaffolder/src/components/secrets/index.ts b/plugins/scaffolder/src/components/secrets/index.ts index 44adb2a00a..c608834803 100644 --- a/plugins/scaffolder/src/components/secrets/index.ts +++ b/plugins/scaffolder/src/components/secrets/index.ts @@ -14,3 +14,4 @@ * limitations under the License. */ export { useTemplateSecrets } from './SecretsContext'; +export type { ScaffolderUseTemplateSecrets } from './SecretsContext'; diff --git a/plugins/scaffolder/src/extensions/index.tsx b/plugins/scaffolder/src/extensions/index.tsx index fd816681ea..53db4cd927 100644 --- a/plugins/scaffolder/src/extensions/index.tsx +++ b/plugins/scaffolder/src/extensions/index.tsx @@ -48,7 +48,9 @@ export function createScaffolderFieldExtension< }; } -export const ScaffolderFieldExtensions: React.ComponentType = () => null; +export const ScaffolderFieldExtensions: React.ComponentType = + (): JSX.Element | null => null; + attachComponentData( ScaffolderFieldExtensions, FIELD_EXTENSION_WRAPPER_KEY, diff --git a/plugins/scaffolder/src/index.ts b/plugins/scaffolder/src/index.ts index 90d5445c8c..71cd1d8f84 100644 --- a/plugins/scaffolder/src/index.ts +++ b/plugins/scaffolder/src/index.ts @@ -55,12 +55,5 @@ export { ScaffolderPage, scaffolderPlugin, } from './plugin'; -export * from './components/fields'; -export type { RepoUrlPickerUiOptions } from './components/fields'; -export { FavouriteTemplate } from './components/FavouriteTemplate'; -export { TemplateList } from './components/TemplateList'; -export type { TemplateListProps } from './components/TemplateList'; -export { TemplateTypePicker } from './components/TemplateTypePicker'; -export * from './components/secrets'; -export { TaskPage } from './components/TaskPage'; +export * from './components'; export type { TaskPageProps } from './components/TaskPage'; diff --git a/plugins/scaffolder/src/routes.ts b/plugins/scaffolder/src/routes.ts index df5868c9ea..97f942d8b9 100644 --- a/plugins/scaffolder/src/routes.ts +++ b/plugins/scaffolder/src/routes.ts @@ -16,6 +16,7 @@ import { createExternalRouteRef, createRouteRef, + createSubRouteRef, } from '@backstage/core-plugin-api'; export const registerComponentRouteRef = createExternalRouteRef({ @@ -26,3 +27,21 @@ export const registerComponentRouteRef = createExternalRouteRef({ export const rootRouteRef = createRouteRef({ id: 'scaffolder', }); + +export const selectedTemplateRouteRef = createSubRouteRef({ + id: 'scaffolder/selected-template', + parent: rootRouteRef, + path: '/templates/:templateName', +}); + +export const scaffolderTaskRouteRef = createSubRouteRef({ + id: 'scaffolder/task', + parent: rootRouteRef, + path: '/tasks/:taskId', +}); + +export const actionsRouteRef = createSubRouteRef({ + id: 'scaffolder/actions', + parent: rootRouteRef, + path: '/actions', +}); diff --git a/plugins/scaffolder/src/types.ts b/plugins/scaffolder/src/types.ts index 5d89f10291..0ffbd601a8 100644 --- a/plugins/scaffolder/src/types.ts +++ b/plugins/scaffolder/src/types.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import { TaskSpec } from '@backstage/plugin-scaffolder-common'; -import { JsonObject, Observable } from '@backstage/types'; +import { JsonObject, JsonValue, Observable } from '@backstage/types'; import { JSONSchema7 } from 'json-schema'; export type ScaffolderTaskStatus = @@ -82,7 +82,7 @@ export type LogEvent = { export interface ScaffolderScaffoldOptions { templateRef: string; - values: Record; + values: Record; secrets?: Record; }