@backstage/plugin-scaffolder 1.28.0-next.3 → 1.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # @backstage/plugin-scaffolder
2
2
 
3
+ ## 1.28.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 17088d2: Updating the `TaskLogStream` to take up all space in a running task, and also show the last line of the log by default
8
+ - 5d469c9: Added support for autocompletion of GitHub branches in scaffolder
9
+ - 8e67e4a: Added support for autocompletion to GithubRepoPicker component
10
+
11
+ ### Patch Changes
12
+
13
+ - 82300ea: Added support for experimental form decorators when dry-running templates in the template editor.
14
+ - eb3d91a: Use the custom error page if provided for displaying errors instead of the default error page
15
+ - 3107f1f: Fixed a bug in the BitbucketRepoBranchPicker component that crashed the scaffolder
16
+ - fe44946: Fixed bug of passing wrong value to `onChange` handler when using `GitLab` autocomplete
17
+ - 3edf7e7: Add schema output return type to the `makeFieldSchema` function return
18
+ - 1e935f0: Added conditional rendering of `oneOf` output schemas on the Installed Actions page for scaffolder actions
19
+ - 58ec9e7: Removed older versions of React packages as a preparatory step for upgrading to React 19. This commit does not introduce any functional changes, but removes dependencies on previous React versions, allowing for a cleaner upgrade path in subsequent commits.
20
+ - 65d7020: Use template title for ongoing task page header
21
+ - Updated dependencies
22
+ - @backstage/plugin-scaffolder-react@1.14.5
23
+ - @backstage/core-components@0.16.4
24
+ - @backstage/plugin-catalog-react@1.15.2
25
+ - @backstage/frontend-plugin-api@0.9.5
26
+ - @backstage/integration-react@1.2.4
27
+ - @backstage/core-compat-api@0.3.6
28
+ - @backstage/core-plugin-api@1.10.4
29
+ - @backstage/plugin-permission-react@0.4.31
30
+ - @backstage/catalog-client@1.9.1
31
+ - @backstage/catalog-model@1.7.3
32
+ - @backstage/errors@1.2.7
33
+ - @backstage/integration@1.16.1
34
+ - @backstage/types@1.2.1
35
+ - @backstage/plugin-catalog-common@1.1.3
36
+ - @backstage/plugin-scaffolder-common@1.5.9
37
+
3
38
  ## 1.28.0-next.3
4
39
 
5
40
  ### Minor Changes
@@ -2,6 +2,7 @@ import yaml from 'yaml';
2
2
  import { useApi } from '@backstage/core-plugin-api';
3
3
  import React, { createContext, useState, useRef, useCallback, useMemo, useContext } from 'react';
4
4
  import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react';
5
+ import { useFormDecorators } from '../../hooks/useFormDecorators.esm.js';
5
6
 
6
7
  const MAX_CONTENT_SIZE = 64 * 1024;
7
8
  const CHUNK_SIZE = 32 * 1024;
@@ -25,6 +26,7 @@ function base64EncodeContent(content) {
25
26
  }
26
27
  }
27
28
  function DryRunProvider(props) {
29
+ const decorators = useFormDecorators();
28
30
  const scaffolderApi = useApi(scaffolderApiRef);
29
31
  const [state, setState] = useState({
30
32
  results: [],
@@ -63,10 +65,15 @@ function DryRunProvider(props) {
63
65
  throw new Error("Scaffolder API does not support dry-run");
64
66
  }
65
67
  const parsed = yaml.parse(options.templateContent);
68
+ const { formState: values, secrets } = await decorators.run({
69
+ formState: options.values,
70
+ secrets: {},
71
+ manifest: parsed?.spec
72
+ });
66
73
  const response = await scaffolderApi.dryRun({
67
74
  template: parsed,
68
- values: options.values,
69
- secrets: {},
75
+ values,
76
+ secrets,
70
77
  directoryContents: options.files.map((file) => ({
71
78
  path: file.path,
72
79
  base64Content: base64EncodeContent(file.content)
@@ -81,7 +88,7 @@ function DryRunProvider(props) {
81
88
  selectedResult: prevState.selectedResult ?? result
82
89
  }));
83
90
  },
84
- [scaffolderApi]
91
+ [scaffolderApi, decorators]
85
92
  );
86
93
  const dryRun = useMemo(
87
94
  () => ({
@@ -1 +1 @@
1
- {"version":3,"file":"DryRunContext.esm.js","sources":["../../../../src/alpha/components/TemplateEditorPage/DryRunContext.tsx"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport yaml from 'yaml';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { JsonObject } from '@backstage/types';\nimport React, {\n createContext,\n ReactNode,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport {\n scaffolderApiRef,\n ScaffolderDryRunResponse,\n} from '@backstage/plugin-scaffolder-react';\n\nconst MAX_CONTENT_SIZE = 64 * 1024;\nconst CHUNK_SIZE = 32 * 1024;\n\ninterface DryRunOptions {\n templateContent: string;\n values: JsonObject;\n files: Array<{ path: string; content: string }>;\n}\n\nexport interface DryRunResult extends ScaffolderDryRunResponse {\n id: number;\n}\n\ninterface DryRun {\n results: DryRunResult[];\n selectedResult: DryRunResult | undefined;\n\n selectResult(id: number): void;\n deleteResult(id: number): void;\n execute(options: DryRunOptions): Promise<void>;\n}\n\nconst DryRunContext = createContext<DryRun | undefined>(undefined);\n\ninterface DryRunProviderProps {\n children: ReactNode;\n}\n\nexport function base64EncodeContent(content: string): string {\n if (content.length > MAX_CONTENT_SIZE) {\n return window.btoa('<file too large>');\n }\n\n try {\n return window.btoa(content);\n } catch {\n const decoder = new TextEncoder();\n const buffer = decoder.encode(content);\n\n const chunks = new Array<string>();\n for (let offset = 0; offset < buffer.length; offset += CHUNK_SIZE) {\n chunks.push(\n String.fromCharCode(...buffer.slice(offset, offset + CHUNK_SIZE)),\n );\n }\n return window.btoa(chunks.join(''));\n }\n}\n\nexport function DryRunProvider(props: DryRunProviderProps) {\n const scaffolderApi = useApi(scaffolderApiRef);\n\n const [state, setState] = useState<\n Pick<DryRun, 'results' | 'selectedResult'>\n >({\n results: [],\n selectedResult: undefined,\n });\n const idRef = useRef(1);\n\n const selectResult = useCallback((id: number) => {\n setState(prevState => {\n const result = prevState.results.find(r => r.id === id);\n if (result === prevState.selectedResult) {\n return prevState;\n }\n return {\n results: prevState.results,\n selectedResult: result,\n };\n });\n }, []);\n\n const deleteResult = useCallback((id: number) => {\n setState(prevState => {\n const index = prevState.results.findIndex(r => r.id === id);\n if (index === -1) {\n return prevState;\n }\n const newResults = prevState.results.slice();\n const [deleted] = newResults.splice(index, 1);\n return {\n results: newResults,\n selectedResult:\n prevState.selectedResult?.id === deleted.id\n ? newResults[0]\n : prevState.selectedResult,\n };\n });\n }, []);\n\n const execute = useCallback(\n async (options: DryRunOptions) => {\n if (!scaffolderApi.dryRun) {\n throw new Error('Scaffolder API does not support dry-run');\n }\n\n const parsed = yaml.parse(options.templateContent);\n\n const response = await scaffolderApi.dryRun({\n template: parsed,\n values: options.values,\n secrets: {},\n directoryContents: options.files.map(file => ({\n path: file.path,\n base64Content: base64EncodeContent(file.content),\n })),\n });\n\n const result = {\n ...response,\n id: idRef.current++,\n };\n\n setState(prevState => ({\n results: [...prevState.results, result],\n selectedResult: prevState.selectedResult ?? result,\n }));\n },\n [scaffolderApi],\n );\n\n const dryRun = useMemo(\n () => ({\n ...state,\n selectResult,\n deleteResult,\n execute,\n }),\n [state, selectResult, deleteResult, execute],\n );\n\n return (\n <DryRunContext.Provider value={dryRun}>\n {props.children}\n </DryRunContext.Provider>\n );\n}\n\nexport function useDryRun(): DryRun {\n const value = useContext(DryRunContext);\n if (!value) {\n throw new Error('must be used within a DryRunProvider');\n }\n return value;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DryRunContext.esm.js","sources":["../../../../src/alpha/components/TemplateEditorPage/DryRunContext.tsx"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport yaml from 'yaml';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { JsonObject, JsonValue } from '@backstage/types';\nimport React, {\n createContext,\n ReactNode,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport {\n scaffolderApiRef,\n ScaffolderDryRunResponse,\n} from '@backstage/plugin-scaffolder-react';\nimport { useFormDecorators } from '../../hooks/useFormDecorators';\n\nconst MAX_CONTENT_SIZE = 64 * 1024;\nconst CHUNK_SIZE = 32 * 1024;\n\ninterface DryRunOptions {\n templateContent: string;\n values: JsonObject;\n files: Array<{ path: string; content: string }>;\n}\n\nexport interface DryRunResult extends ScaffolderDryRunResponse {\n id: number;\n}\n\ninterface DryRun {\n results: DryRunResult[];\n selectedResult: DryRunResult | undefined;\n\n selectResult(id: number): void;\n deleteResult(id: number): void;\n execute(options: DryRunOptions): Promise<void>;\n}\n\nconst DryRunContext = createContext<DryRun | undefined>(undefined);\n\ninterface DryRunProviderProps {\n children: ReactNode;\n}\n\nexport function base64EncodeContent(content: string): string {\n if (content.length > MAX_CONTENT_SIZE) {\n return window.btoa('<file too large>');\n }\n\n try {\n return window.btoa(content);\n } catch {\n const decoder = new TextEncoder();\n const buffer = decoder.encode(content);\n\n const chunks = new Array<string>();\n for (let offset = 0; offset < buffer.length; offset += CHUNK_SIZE) {\n chunks.push(\n String.fromCharCode(...buffer.slice(offset, offset + CHUNK_SIZE)),\n );\n }\n return window.btoa(chunks.join(''));\n }\n}\n\nexport function DryRunProvider(props: DryRunProviderProps) {\n const decorators = useFormDecorators();\n const scaffolderApi = useApi(scaffolderApiRef);\n\n const [state, setState] = useState<\n Pick<DryRun, 'results' | 'selectedResult'>\n >({\n results: [],\n selectedResult: undefined,\n });\n const idRef = useRef(1);\n\n const selectResult = useCallback((id: number) => {\n setState(prevState => {\n const result = prevState.results.find(r => r.id === id);\n if (result === prevState.selectedResult) {\n return prevState;\n }\n return {\n results: prevState.results,\n selectedResult: result,\n };\n });\n }, []);\n\n const deleteResult = useCallback((id: number) => {\n setState(prevState => {\n const index = prevState.results.findIndex(r => r.id === id);\n if (index === -1) {\n return prevState;\n }\n const newResults = prevState.results.slice();\n const [deleted] = newResults.splice(index, 1);\n return {\n results: newResults,\n selectedResult:\n prevState.selectedResult?.id === deleted.id\n ? newResults[0]\n : prevState.selectedResult,\n };\n });\n }, []);\n\n const execute = useCallback(\n async (options: DryRunOptions) => {\n if (!scaffolderApi.dryRun) {\n throw new Error('Scaffolder API does not support dry-run');\n }\n\n const parsed = yaml.parse(options.templateContent);\n\n const { formState: values, secrets } = await decorators.run({\n formState: options.values as Record<string, JsonValue>,\n secrets: {},\n manifest: parsed?.spec,\n });\n\n const response = await scaffolderApi.dryRun({\n template: parsed,\n values,\n secrets,\n directoryContents: options.files.map(file => ({\n path: file.path,\n base64Content: base64EncodeContent(file.content),\n })),\n });\n\n const result = {\n ...response,\n id: idRef.current++,\n };\n\n setState(prevState => ({\n results: [...prevState.results, result],\n selectedResult: prevState.selectedResult ?? result,\n }));\n },\n [scaffolderApi, decorators],\n );\n\n const dryRun = useMemo(\n () => ({\n ...state,\n selectResult,\n deleteResult,\n execute,\n }),\n [state, selectResult, deleteResult, execute],\n );\n\n return (\n <DryRunContext.Provider value={dryRun}>\n {props.children}\n </DryRunContext.Provider>\n );\n}\n\nexport function useDryRun(): DryRun {\n const value = useContext(DryRunContext);\n if (!value) {\n throw new Error('must be used within a DryRunProvider');\n }\n return value;\n}\n"],"names":[],"mappings":"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"}
@@ -31,7 +31,7 @@ const TemplateWizardPage = (props) => {
31
31
  name: templateName
32
32
  });
33
33
  const { manifest } = useTemplateParameterSchema(templateRef);
34
- const decorators = useFormDecorators({ manifest });
34
+ const decorators = useFormDecorators();
35
35
  const { value: editUrl } = useAsync(async () => {
36
36
  const data = await catalogApi.getEntityByRef(templateRef);
37
37
  return data?.metadata.annotations?.[ANNOTATION_EDIT_URL];
@@ -43,7 +43,8 @@ const TemplateWizardPage = (props) => {
43
43
  setIsCreating(true);
44
44
  const { formState: values, secrets } = await decorators.run({
45
45
  formState: initialValues,
46
- secrets: contextSecrets
46
+ secrets: contextSecrets,
47
+ manifest
47
48
  });
48
49
  const { taskId } = await scaffolderApi.scaffold({
49
50
  templateRef,
@@ -1 +1 @@
1
- {"version":3,"file":"TemplateWizardPage.esm.js","sources":["../../../../src/alpha/components/TemplateWizardPage/TemplateWizardPage.tsx"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React, { useState } from 'react';\nimport { Navigate, useNavigate } from 'react-router-dom';\nimport useAsync from 'react-use/esm/useAsync';\nimport {\n stringifyEntityRef,\n ANNOTATION_EDIT_URL,\n} from '@backstage/catalog-model';\nimport {\n AnalyticsContext,\n useApi,\n useRouteRef,\n useRouteRefParams,\n} from '@backstage/core-plugin-api';\nimport {\n scaffolderApiRef,\n useTemplateSecrets,\n type LayoutOptions,\n FormProps,\n FieldExtensionOptions,\n ReviewStepProps,\n} from '@backstage/plugin-scaffolder-react';\nimport { catalogApiRef } from '@backstage/plugin-catalog-react';\n\nimport {\n Workflow,\n useTemplateParameterSchema,\n} from '@backstage/plugin-scaffolder-react/alpha';\nimport { JsonValue } from '@backstage/types';\nimport { Header, Page, Progress } from '@backstage/core-components';\n\nimport {\n rootRouteRef,\n scaffolderTaskRouteRef,\n selectedTemplateRouteRef,\n} from '../../../routes';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { scaffolderTranslationRef } from '../../../translation';\n\nimport { TemplateWizardPageContextMenu } from './TemplateWizardPageContextMenu';\nimport { useFormDecorators } from '../../hooks';\n\n/**\n * @alpha\n */\nexport type TemplateWizardPageProps = {\n customFieldExtensions: FieldExtensionOptions<any, any>[];\n components?: {\n ReviewStepComponent?: React.ComponentType<ReviewStepProps>;\n };\n layouts?: LayoutOptions[];\n formProps?: FormProps;\n headerOptions?: {\n pageTitleOverride?: string;\n title?: string;\n subtitle?: string;\n };\n};\n\nexport const TemplateWizardPage = (props: TemplateWizardPageProps) => {\n const rootRef = useRouteRef(rootRouteRef);\n const taskRoute = useRouteRef(scaffolderTaskRouteRef);\n const { secrets: contextSecrets } = useTemplateSecrets();\n const scaffolderApi = useApi(scaffolderApiRef);\n const catalogApi = useApi(catalogApiRef);\n const [isCreating, setIsCreating] = useState(false);\n const navigate = useNavigate();\n const { templateName, namespace } = useRouteRefParams(\n selectedTemplateRouteRef,\n );\n const { t } = useTranslationRef(scaffolderTranslationRef);\n\n const templateRef = stringifyEntityRef({\n kind: 'Template',\n namespace,\n name: templateName,\n });\n\n const { manifest } = useTemplateParameterSchema(templateRef);\n const decorators = useFormDecorators({ manifest });\n\n const { value: editUrl } = useAsync(async () => {\n const data = await catalogApi.getEntityByRef(templateRef);\n return data?.metadata.annotations?.[ANNOTATION_EDIT_URL];\n }, [templateRef, catalogApi]);\n\n const onCreate = async (initialValues: Record<string, JsonValue>) => {\n if (isCreating) {\n return;\n }\n\n setIsCreating(true);\n\n const { formState: values, secrets } = await decorators.run({\n formState: initialValues,\n secrets: contextSecrets,\n });\n\n const { taskId } = await scaffolderApi.scaffold({\n templateRef,\n values,\n secrets,\n });\n\n navigate(taskRoute({ taskId }));\n };\n\n const onError = () => <Navigate to={rootRef()} />;\n\n return (\n <AnalyticsContext attributes={{ entityRef: templateRef }}>\n <Page themeId=\"website\">\n <Header\n pageTitleOverride={t('templateWizardPage.pageTitle')}\n title={t('templateWizardPage.title')}\n subtitle={t('templateWizardPage.subtitle')}\n {...props.headerOptions}\n >\n <TemplateWizardPageContextMenu editUrl={editUrl} />\n </Header>\n {isCreating && <Progress />}\n <Workflow\n namespace={namespace}\n templateName={templateName}\n onCreate={onCreate}\n components={props.components}\n onError={onError}\n extensions={props.customFieldExtensions}\n formProps={props.formProps}\n layouts={props.layouts}\n />\n </Page>\n </AnalyticsContext>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TemplateWizardPage.esm.js","sources":["../../../../src/alpha/components/TemplateWizardPage/TemplateWizardPage.tsx"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React, { useState } from 'react';\nimport { Navigate, useNavigate } from 'react-router-dom';\nimport useAsync from 'react-use/esm/useAsync';\nimport {\n stringifyEntityRef,\n ANNOTATION_EDIT_URL,\n} from '@backstage/catalog-model';\nimport {\n AnalyticsContext,\n useApi,\n useRouteRef,\n useRouteRefParams,\n} from '@backstage/core-plugin-api';\nimport {\n scaffolderApiRef,\n useTemplateSecrets,\n type LayoutOptions,\n FormProps,\n FieldExtensionOptions,\n ReviewStepProps,\n} from '@backstage/plugin-scaffolder-react';\nimport { catalogApiRef } from '@backstage/plugin-catalog-react';\n\nimport {\n Workflow,\n useTemplateParameterSchema,\n} from '@backstage/plugin-scaffolder-react/alpha';\nimport { JsonValue } from '@backstage/types';\nimport { Header, Page, Progress } from '@backstage/core-components';\n\nimport {\n rootRouteRef,\n scaffolderTaskRouteRef,\n selectedTemplateRouteRef,\n} from '../../../routes';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { scaffolderTranslationRef } from '../../../translation';\n\nimport { TemplateWizardPageContextMenu } from './TemplateWizardPageContextMenu';\nimport { useFormDecorators } from '../../hooks';\n\n/**\n * @alpha\n */\nexport type TemplateWizardPageProps = {\n customFieldExtensions: FieldExtensionOptions<any, any>[];\n components?: {\n ReviewStepComponent?: React.ComponentType<ReviewStepProps>;\n };\n layouts?: LayoutOptions[];\n formProps?: FormProps;\n headerOptions?: {\n pageTitleOverride?: string;\n title?: string;\n subtitle?: string;\n };\n};\n\nexport const TemplateWizardPage = (props: TemplateWizardPageProps) => {\n const rootRef = useRouteRef(rootRouteRef);\n const taskRoute = useRouteRef(scaffolderTaskRouteRef);\n const { secrets: contextSecrets } = useTemplateSecrets();\n const scaffolderApi = useApi(scaffolderApiRef);\n const catalogApi = useApi(catalogApiRef);\n const [isCreating, setIsCreating] = useState(false);\n const navigate = useNavigate();\n const { templateName, namespace } = useRouteRefParams(\n selectedTemplateRouteRef,\n );\n const { t } = useTranslationRef(scaffolderTranslationRef);\n\n const templateRef = stringifyEntityRef({\n kind: 'Template',\n namespace,\n name: templateName,\n });\n\n const { manifest } = useTemplateParameterSchema(templateRef);\n const decorators = useFormDecorators();\n\n const { value: editUrl } = useAsync(async () => {\n const data = await catalogApi.getEntityByRef(templateRef);\n return data?.metadata.annotations?.[ANNOTATION_EDIT_URL];\n }, [templateRef, catalogApi]);\n\n const onCreate = async (initialValues: Record<string, JsonValue>) => {\n if (isCreating) {\n return;\n }\n\n setIsCreating(true);\n\n const { formState: values, secrets } = await decorators.run({\n formState: initialValues,\n secrets: contextSecrets,\n manifest,\n });\n\n const { taskId } = await scaffolderApi.scaffold({\n templateRef,\n values,\n secrets,\n });\n\n navigate(taskRoute({ taskId }));\n };\n\n const onError = () => <Navigate to={rootRef()} />;\n\n return (\n <AnalyticsContext attributes={{ entityRef: templateRef }}>\n <Page themeId=\"website\">\n <Header\n pageTitleOverride={t('templateWizardPage.pageTitle')}\n title={t('templateWizardPage.title')}\n subtitle={t('templateWizardPage.subtitle')}\n {...props.headerOptions}\n >\n <TemplateWizardPageContextMenu editUrl={editUrl} />\n </Header>\n {isCreating && <Progress />}\n <Workflow\n namespace={namespace}\n templateName={templateName}\n onCreate={onCreate}\n components={props.components}\n onError={onError}\n extensions={props.customFieldExtensions}\n formProps={props.formProps}\n layouts={props.layouts}\n />\n </Page>\n </AnalyticsContext>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -1,13 +1,11 @@
1
1
  import { useApi, errorApiRef, useApiHolder } from '@backstage/core-plugin-api';
2
2
  import { formDecoratorsApiRef } from '../api/ref.esm.js';
3
3
  import useAsync from 'react-use/esm/useAsync';
4
- import { useMemo } from 'react';
4
+ import { useMemo, useCallback } from 'react';
5
5
  import { OpaqueFormDecorator } from '../../packages/scaffolder-internal/src/wiring/InternalFormDecorator.esm.js';
6
6
  import '../../packages/scaffolder-internal/src/wiring/InternalFormField.esm.js';
7
7
 
8
- const useFormDecorators = ({
9
- manifest
10
- }) => {
8
+ const useFormDecorators = () => {
11
9
  const formDecoratorsApi = useApi(formDecoratorsApiRef);
12
10
  const errorApi = useApi(errorApiRef);
13
11
  const { value: decorators } = useAsync(
@@ -39,13 +37,14 @@ const useFormDecorators = ({
39
37
  }
40
38
  return decoratorsMap;
41
39
  }, [apiHolder, decorators, errorApi]);
42
- return {
43
- run: async (opts) => {
40
+ const run = useCallback(
41
+ async (opts) => {
44
42
  let formState = { ...opts.formState };
45
43
  let secrets = { ...opts.secrets };
46
- if (manifest?.EXPERIMENTAL_formDecorators) {
44
+ const formDecorators = opts.manifest?.EXPERIMENTAL_formDecorators;
45
+ if (formDecorators) {
47
46
  await Promise.all(
48
- manifest.EXPERIMENTAL_formDecorators.map(async (decorator) => {
47
+ formDecorators.map(async (decorator) => {
49
48
  const formDecorator = boundDecorators?.get(decorator.id);
50
49
  if (!formDecorator) {
51
50
  errorApi.post(
@@ -67,8 +66,15 @@ const useFormDecorators = ({
67
66
  );
68
67
  }
69
68
  return { formState, secrets };
70
- }
71
- };
69
+ },
70
+ [boundDecorators, errorApi]
71
+ );
72
+ return useMemo(
73
+ () => ({
74
+ run
75
+ }),
76
+ [run]
77
+ );
72
78
  };
73
79
 
74
80
  export { useFormDecorators };
@@ -1 +1 @@
1
- {"version":3,"file":"useFormDecorators.esm.js","sources":["../../../src/alpha/hooks/useFormDecorators.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { errorApiRef, useApi, useApiHolder } from '@backstage/core-plugin-api';\nimport { formDecoratorsApiRef } from '../api/ref';\nimport useAsync from 'react-use/esm/useAsync';\nimport { useMemo } from 'react';\nimport { ScaffolderFormDecoratorContext } from '@backstage/plugin-scaffolder-react/alpha';\nimport { OpaqueFormDecorator } from '@internal/scaffolder';\nimport { TemplateParameterSchema } from '@backstage/plugin-scaffolder-react';\nimport { JsonValue } from '@backstage/types';\n\n/** @internal */\ntype BoundFieldDecorator = {\n decorator: (ctx: ScaffolderFormDecoratorContext) => Promise<void>;\n};\n\nexport const useFormDecorators = ({\n manifest,\n}: {\n manifest?: TemplateParameterSchema;\n}) => {\n const formDecoratorsApi = useApi(formDecoratorsApiRef);\n const errorApi = useApi(errorApiRef);\n const { value: decorators } = useAsync(\n () => formDecoratorsApi.getFormDecorators(),\n [],\n );\n const apiHolder = useApiHolder();\n\n const boundDecorators = useMemo(() => {\n const decoratorsMap = new Map<string, BoundFieldDecorator>();\n\n for (const decorator of decorators ?? []) {\n try {\n const { decorator: decoratorFn, deps } =\n OpaqueFormDecorator.toInternal(decorator);\n\n const resolvedDeps = Object.entries(deps ?? {}).map(([key, value]) => {\n const api = apiHolder.get(value);\n if (!api) {\n throw new Error(\n `Failed to resolve apiRef ${value.id} for form decorator ${decorator.id} it will be disabled`,\n );\n }\n return [key, api];\n });\n\n decoratorsMap.set(decorator.id, {\n decorator: ctx => decoratorFn(ctx, Object.fromEntries(resolvedDeps)),\n });\n } catch (ex) {\n errorApi.post(ex);\n return undefined;\n }\n }\n return decoratorsMap;\n }, [apiHolder, decorators, errorApi]);\n\n return {\n run: async (opts: {\n formState: Record<string, JsonValue>;\n secrets: Record<string, string>;\n }) => {\n let formState: Record<string, JsonValue> = { ...opts.formState };\n let secrets: Record<string, string> = { ...opts.secrets };\n\n if (manifest?.EXPERIMENTAL_formDecorators) {\n // for each of the form decorators, go and call the decorator with the context\n await Promise.all(\n manifest.EXPERIMENTAL_formDecorators.map(async decorator => {\n const formDecorator = boundDecorators?.get(decorator.id);\n if (!formDecorator) {\n errorApi.post(\n new Error(`Failed to find form decorator ${decorator.id}`),\n );\n return;\n }\n\n await formDecorator.decorator({\n setSecrets: (\n handler: (\n oldState: Record<string, string>,\n ) => Record<string, string>,\n ) => {\n secrets = { ...handler(secrets) };\n },\n setFormState: (\n handler: (\n oldState: Record<string, JsonValue>,\n ) => Record<string, JsonValue>,\n ) => {\n formState = { ...handler(formState) };\n },\n formState,\n input: decorator.input ?? {},\n });\n }),\n );\n }\n\n return { formState, secrets };\n },\n };\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useFormDecorators.esm.js","sources":["../../../src/alpha/hooks/useFormDecorators.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { errorApiRef, useApi, useApiHolder } from '@backstage/core-plugin-api';\nimport { formDecoratorsApiRef } from '../api/ref';\nimport useAsync from 'react-use/esm/useAsync';\nimport { useCallback, useMemo } from 'react';\nimport { ScaffolderFormDecoratorContext } from '@backstage/plugin-scaffolder-react/alpha';\nimport { OpaqueFormDecorator } from '@internal/scaffolder';\nimport { TemplateParameterSchema } from '@backstage/plugin-scaffolder-react';\nimport { JsonValue } from '@backstage/types';\n\n/** @internal */\ntype BoundFieldDecorator = {\n decorator: (ctx: ScaffolderFormDecoratorContext) => Promise<void>;\n};\n\nexport const useFormDecorators = () => {\n const formDecoratorsApi = useApi(formDecoratorsApiRef);\n const errorApi = useApi(errorApiRef);\n const { value: decorators } = useAsync(\n () => formDecoratorsApi.getFormDecorators(),\n [],\n );\n const apiHolder = useApiHolder();\n\n const boundDecorators = useMemo(() => {\n const decoratorsMap = new Map<string, BoundFieldDecorator>();\n\n for (const decorator of decorators ?? []) {\n try {\n const { decorator: decoratorFn, deps } =\n OpaqueFormDecorator.toInternal(decorator);\n\n const resolvedDeps = Object.entries(deps ?? {}).map(([key, value]) => {\n const api = apiHolder.get(value);\n if (!api) {\n throw new Error(\n `Failed to resolve apiRef ${value.id} for form decorator ${decorator.id} it will be disabled`,\n );\n }\n return [key, api];\n });\n\n decoratorsMap.set(decorator.id, {\n decorator: ctx => decoratorFn(ctx, Object.fromEntries(resolvedDeps)),\n });\n } catch (ex) {\n errorApi.post(ex);\n return undefined;\n }\n }\n return decoratorsMap;\n }, [apiHolder, decorators, errorApi]);\n\n const run = useCallback(\n async (opts: {\n formState: Record<string, JsonValue>;\n secrets: Record<string, string>;\n manifest?: TemplateParameterSchema;\n }) => {\n let formState: Record<string, JsonValue> = { ...opts.formState };\n let secrets: Record<string, string> = { ...opts.secrets };\n\n const formDecorators = opts.manifest?.EXPERIMENTAL_formDecorators;\n if (formDecorators) {\n // for each of the form decorators, go and call the decorator with the context\n await Promise.all(\n formDecorators.map(async decorator => {\n const formDecorator = boundDecorators?.get(decorator.id);\n if (!formDecorator) {\n errorApi.post(\n new Error(`Failed to find form decorator ${decorator.id}`),\n );\n return;\n }\n\n await formDecorator.decorator({\n setSecrets: (\n handler: (\n oldState: Record<string, string>,\n ) => Record<string, string>,\n ) => {\n secrets = { ...handler(secrets) };\n },\n setFormState: (\n handler: (\n oldState: Record<string, JsonValue>,\n ) => Record<string, JsonValue>,\n ) => {\n formState = { ...handler(formState) };\n },\n formState,\n input: decorator.input ?? {},\n });\n }),\n );\n }\n\n return { formState, secrets };\n },\n [boundDecorators, errorApi],\n );\n\n return useMemo(\n () => ({\n run,\n }),\n [run],\n );\n};\n"],"names":[],"mappings":";;;;;;;AA6BO,MAAM,oBAAoB,MAAM;AACrC,EAAM,MAAA,iBAAA,GAAoB,OAAO,oBAAoB,CAAA;AACrD,EAAM,MAAA,QAAA,GAAW,OAAO,WAAW,CAAA;AACnC,EAAM,MAAA,EAAE,KAAO,EAAA,UAAA,EAAe,GAAA,QAAA;AAAA,IAC5B,MAAM,kBAAkB,iBAAkB,EAAA;AAAA,IAC1C;AAAC,GACH;AACA,EAAA,MAAM,YAAY,YAAa,EAAA;AAE/B,EAAM,MAAA,eAAA,GAAkB,QAAQ,MAAM;AACpC,IAAM,MAAA,aAAA,uBAAoB,GAAiC,EAAA;AAE3D,IAAW,KAAA,MAAA,SAAA,IAAa,UAAc,IAAA,EAAI,EAAA;AACxC,MAAI,IAAA;AACF,QAAA,MAAM,EAAE,SAAW,EAAA,WAAA,EAAa,MAC9B,GAAA,mBAAA,CAAoB,WAAW,SAAS,CAAA;AAE1C,QAAA,MAAM,YAAe,GAAA,MAAA,CAAO,OAAQ,CAAA,IAAA,IAAQ,EAAE,CAAE,CAAA,GAAA,CAAI,CAAC,CAAC,GAAK,EAAA,KAAK,CAAM,KAAA;AACpE,UAAM,MAAA,GAAA,GAAM,SAAU,CAAA,GAAA,CAAI,KAAK,CAAA;AAC/B,UAAA,IAAI,CAAC,GAAK,EAAA;AACR,YAAA,MAAM,IAAI,KAAA;AAAA,cACR,CAA4B,yBAAA,EAAA,KAAA,CAAM,EAAE,CAAA,oBAAA,EAAuB,UAAU,EAAE,CAAA,oBAAA;AAAA,aACzE;AAAA;AAEF,UAAO,OAAA,CAAC,KAAK,GAAG,CAAA;AAAA,SACjB,CAAA;AAED,QAAc,aAAA,CAAA,GAAA,CAAI,UAAU,EAAI,EAAA;AAAA,UAC9B,WAAW,CAAO,GAAA,KAAA,WAAA,CAAY,KAAK,MAAO,CAAA,WAAA,CAAY,YAAY,CAAC;AAAA,SACpE,CAAA;AAAA,eACM,EAAI,EAAA;AACX,QAAA,QAAA,CAAS,KAAK,EAAE,CAAA;AAChB,QAAO,OAAA,KAAA,CAAA;AAAA;AACT;AAEF,IAAO,OAAA,aAAA;AAAA,GACN,EAAA,CAAC,SAAW,EAAA,UAAA,EAAY,QAAQ,CAAC,CAAA;AAEpC,EAAA,MAAM,GAAM,GAAA,WAAA;AAAA,IACV,OAAO,IAID,KAAA;AACJ,MAAA,IAAI,SAAuC,GAAA,EAAE,GAAG,IAAA,CAAK,SAAU,EAAA;AAC/D,MAAA,IAAI,OAAkC,GAAA,EAAE,GAAG,IAAA,CAAK,OAAQ,EAAA;AAExD,MAAM,MAAA,cAAA,GAAiB,KAAK,QAAU,EAAA,2BAAA;AACtC,MAAA,IAAI,cAAgB,EAAA;AAElB,QAAA,MAAM,OAAQ,CAAA,GAAA;AAAA,UACZ,cAAA,CAAe,GAAI,CAAA,OAAM,SAAa,KAAA;AACpC,YAAA,MAAM,aAAgB,GAAA,eAAA,EAAiB,GAAI,CAAA,SAAA,CAAU,EAAE,CAAA;AACvD,YAAA,IAAI,CAAC,aAAe,EAAA;AAClB,cAAS,QAAA,CAAA,IAAA;AAAA,gBACP,IAAI,KAAA,CAAM,CAAiC,8BAAA,EAAA,SAAA,CAAU,EAAE,CAAE,CAAA;AAAA,eAC3D;AACA,cAAA;AAAA;AAGF,YAAA,MAAM,cAAc,SAAU,CAAA;AAAA,cAC5B,UAAA,EAAY,CACV,OAGG,KAAA;AACH,gBAAA,OAAA,GAAU,EAAE,GAAG,OAAQ,CAAA,OAAO,CAAE,EAAA;AAAA,eAClC;AAAA,cACA,YAAA,EAAc,CACZ,OAGG,KAAA;AACH,gBAAA,SAAA,GAAY,EAAE,GAAG,OAAQ,CAAA,SAAS,CAAE,EAAA;AAAA,eACtC;AAAA,cACA,SAAA;AAAA,cACA,KAAA,EAAO,SAAU,CAAA,KAAA,IAAS;AAAC,aAC5B,CAAA;AAAA,WACF;AAAA,SACH;AAAA;AAGF,MAAO,OAAA,EAAE,WAAW,OAAQ,EAAA;AAAA,KAC9B;AAAA,IACA,CAAC,iBAAiB,QAAQ;AAAA,GAC5B;AAEA,EAAO,OAAA,OAAA;AAAA,IACL,OAAO;AAAA,MACL;AAAA,KACF,CAAA;AAAA,IACA,CAAC,GAAG;AAAA,GACN;AACF;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-scaffolder",
3
- "version": "1.28.0-next.3",
3
+ "version": "1.28.0",
4
4
  "description": "The Backstage plugin that helps you create new things",
5
5
  "backstage": {
6
6
  "role": "frontend-plugin",
@@ -70,21 +70,21 @@
70
70
  "test": "backstage-cli package test"
71
71
  },
72
72
  "dependencies": {
73
- "@backstage/catalog-client": "1.9.1",
74
- "@backstage/catalog-model": "1.7.3",
75
- "@backstage/core-compat-api": "0.3.6-next.3",
76
- "@backstage/core-components": "0.16.4-next.1",
77
- "@backstage/core-plugin-api": "1.10.4-next.0",
78
- "@backstage/errors": "1.2.7",
79
- "@backstage/frontend-plugin-api": "0.9.5-next.3",
80
- "@backstage/integration": "1.16.1",
81
- "@backstage/integration-react": "1.2.4-next.0",
82
- "@backstage/plugin-catalog-common": "1.1.3",
83
- "@backstage/plugin-catalog-react": "1.15.2-next.3",
84
- "@backstage/plugin-permission-react": "0.4.31-next.0",
85
- "@backstage/plugin-scaffolder-common": "1.5.9",
86
- "@backstage/plugin-scaffolder-react": "1.14.5-next.3",
87
- "@backstage/types": "1.2.1",
73
+ "@backstage/catalog-client": "^1.9.1",
74
+ "@backstage/catalog-model": "^1.7.3",
75
+ "@backstage/core-compat-api": "^0.3.6",
76
+ "@backstage/core-components": "^0.16.4",
77
+ "@backstage/core-plugin-api": "^1.10.4",
78
+ "@backstage/errors": "^1.2.7",
79
+ "@backstage/frontend-plugin-api": "^0.9.5",
80
+ "@backstage/integration": "^1.16.1",
81
+ "@backstage/integration-react": "^1.2.4",
82
+ "@backstage/plugin-catalog-common": "^1.1.3",
83
+ "@backstage/plugin-catalog-react": "^1.15.2",
84
+ "@backstage/plugin-permission-react": "^0.4.31",
85
+ "@backstage/plugin-scaffolder-common": "^1.5.9",
86
+ "@backstage/plugin-scaffolder-react": "^1.14.5",
87
+ "@backstage/types": "^1.2.1",
88
88
  "@codemirror/language": "^6.0.0",
89
89
  "@codemirror/legacy-modes": "^6.1.0",
90
90
  "@codemirror/view": "^6.0.0",
@@ -117,12 +117,12 @@
117
117
  "zod-to-json-schema": "^3.20.4"
118
118
  },
119
119
  "devDependencies": {
120
- "@backstage/cli": "0.30.0-next.3",
121
- "@backstage/core-app-api": "1.15.5-next.0",
122
- "@backstage/dev-utils": "1.1.7-next.3",
123
- "@backstage/plugin-catalog": "1.27.0-next.3",
124
- "@backstage/plugin-permission-common": "0.8.4",
125
- "@backstage/test-utils": "1.7.5-next.0",
120
+ "@backstage/cli": "^0.30.0",
121
+ "@backstage/core-app-api": "^1.15.5",
122
+ "@backstage/dev-utils": "^1.1.7",
123
+ "@backstage/plugin-catalog": "^1.27.0",
124
+ "@backstage/plugin-permission-common": "^0.8.4",
125
+ "@backstage/test-utils": "^1.7.5",
126
126
  "@testing-library/dom": "^10.0.0",
127
127
  "@testing-library/jest-dom": "^6.0.0",
128
128
  "@testing-library/react": "^16.0.0",