mirror of
https://github.com/portainer/portainer.git
synced 2025-07-19 13:29:41 +02:00
fix(custom-templates): add stack validation, remove custom template validation [EE-7102] (#11938)
Some checks failed
ci / build_images (map[arch:amd64 platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:amd64 platform:windows version:1809]) (push) Has been cancelled
ci / build_images (map[arch:amd64 platform:windows version:ltsc2022]) (push) Has been cancelled
ci / build_images (map[arch:arm platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:arm64 platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:ppc64le platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:s390x platform:linux version:]) (push) Has been cancelled
/ triage (push) Has been cancelled
Test / test-server (map[arch:amd64 platform:windows version:1809]) (push) Has been cancelled
Test / test-server (map[arch:amd64 platform:windows version:ltsc2022]) (push) Has been cancelled
Test / test-server (map[arch:arm64 platform:linux]) (push) Has been cancelled
Lint / Run linters (push) Has been cancelled
Test / test-client (push) Has been cancelled
Test / test-server (map[arch:amd64 platform:linux]) (push) Has been cancelled
ci / build_manifests (push) Has been cancelled
Some checks failed
ci / build_images (map[arch:amd64 platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:amd64 platform:windows version:1809]) (push) Has been cancelled
ci / build_images (map[arch:amd64 platform:windows version:ltsc2022]) (push) Has been cancelled
ci / build_images (map[arch:arm platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:arm64 platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:ppc64le platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:s390x platform:linux version:]) (push) Has been cancelled
/ triage (push) Has been cancelled
Test / test-server (map[arch:amd64 platform:windows version:1809]) (push) Has been cancelled
Test / test-server (map[arch:amd64 platform:windows version:ltsc2022]) (push) Has been cancelled
Test / test-server (map[arch:arm64 platform:linux]) (push) Has been cancelled
Lint / Run linters (push) Has been cancelled
Test / test-client (push) Has been cancelled
Test / test-server (map[arch:amd64 platform:linux]) (push) Has been cancelled
ci / build_manifests (push) Has been cancelled
Co-authored-by: testa113 <testa113>
This commit is contained in:
parent
0f5988af49
commit
be9d3285e1
15 changed files with 68 additions and 44 deletions
|
@ -151,7 +151,14 @@ export const ngModule = angular
|
||||||
),
|
),
|
||||||
{ stackName: 'setStackName' }
|
{ stackName: 'setStackName' }
|
||||||
),
|
),
|
||||||
['setStackName', 'stackName', 'stacks', 'inputClassName', 'textTip']
|
[
|
||||||
|
'setStackName',
|
||||||
|
'stackName',
|
||||||
|
'stacks',
|
||||||
|
'inputClassName',
|
||||||
|
'textTip',
|
||||||
|
'error',
|
||||||
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.component(
|
.component(
|
||||||
|
|
|
@ -172,6 +172,7 @@
|
||||||
text-tip="'Enter or select a \'stack\' name to group multiple deployments together, or else leave empty to ignore.'"
|
text-tip="'Enter or select a \'stack\' name to group multiple deployments together, or else leave empty to ignore.'"
|
||||||
stacks="ctrl.stacks"
|
stacks="ctrl.stacks"
|
||||||
input-class-name="'col-lg-10 col-sm-9'"
|
input-class-name="'col-lg-10 col-sm-9'"
|
||||||
|
error="ctrl.state.stackNameError"
|
||||||
></kube-stack-name>
|
></kube-stack-name>
|
||||||
<!-- #endregion -->
|
<!-- #endregion -->
|
||||||
|
|
||||||
|
@ -234,6 +235,7 @@
|
||||||
text-tip="'Enter or select a \'stack\' name to group multiple deployments together, or else leave empty to ignore.'"
|
text-tip="'Enter or select a \'stack\' name to group multiple deployments together, or else leave empty to ignore.'"
|
||||||
stacks="ctrl.stacks"
|
stacks="ctrl.stacks"
|
||||||
input-class-name="'col-lg-10 col-sm-9'"
|
input-class-name="'col-lg-10 col-sm-9'"
|
||||||
|
error="ctrl.state.stackNameError"
|
||||||
></kube-stack-name>
|
></kube-stack-name>
|
||||||
<!-- #endregion -->
|
<!-- #endregion -->
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ import { confirmUpdateAppIngress } from '@/react/kubernetes/applications/CreateV
|
||||||
import { confirm, confirmUpdate, confirmWebEditorDiscard } from '@@/modals/confirm';
|
import { confirm, confirmUpdate, confirmWebEditorDiscard } from '@@/modals/confirm';
|
||||||
import { buildConfirmButton } from '@@/modals/utils';
|
import { buildConfirmButton } from '@@/modals/utils';
|
||||||
import { ModalType } from '@@/modals';
|
import { ModalType } from '@@/modals';
|
||||||
|
import { KUBE_STACK_NAME_VALIDATION_REGEX } from '@/react/kubernetes/DeployView/StackName/constants';
|
||||||
|
|
||||||
class KubernetesCreateApplicationController {
|
class KubernetesCreateApplicationController {
|
||||||
/* #region CONSTRUCTOR */
|
/* #region CONSTRUCTOR */
|
||||||
|
@ -127,6 +128,7 @@ class KubernetesCreateApplicationController {
|
||||||
// a validation message will be shown. isExistingCPUReservationUnchanged and isExistingMemoryReservationUnchanged (with available resources being exceeded) is used to decide whether to show the message or not.
|
// a validation message will be shown. isExistingCPUReservationUnchanged and isExistingMemoryReservationUnchanged (with available resources being exceeded) is used to decide whether to show the message or not.
|
||||||
isExistingCPUReservationUnchanged: false,
|
isExistingCPUReservationUnchanged: false,
|
||||||
isExistingMemoryReservationUnchanged: false,
|
isExistingMemoryReservationUnchanged: false,
|
||||||
|
stackNameError: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
this.isAdmin = this.Authentication.isAdmin();
|
this.isAdmin = this.Authentication.isAdmin();
|
||||||
|
@ -186,9 +188,16 @@ class KubernetesCreateApplicationController {
|
||||||
}
|
}
|
||||||
/* #endregion */
|
/* #endregion */
|
||||||
|
|
||||||
onChangeStackName(stackName) {
|
onChangeStackName(name) {
|
||||||
return this.$async(async () => {
|
return this.$async(async () => {
|
||||||
this.formValues.StackName = stackName;
|
if (KUBE_STACK_NAME_VALIDATION_REGEX.test(name) || name === '') {
|
||||||
|
this.state.stackNameError = '';
|
||||||
|
} else {
|
||||||
|
this.state.stackNameError =
|
||||||
|
"Stack must consist of alphanumeric characters, '-', '_' or '.', must start and end with an alphanumeric character and must be 63 characters or less (e.g. 'my-name', or 'abc-123').";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.formValues.StackName = name;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -649,7 +658,8 @@ class KubernetesCreateApplicationController {
|
||||||
const invalid = !this.isValid();
|
const invalid = !this.isValid();
|
||||||
const hasNoChanges = this.isEditAndNoChangesMade();
|
const hasNoChanges = this.isEditAndNoChangesMade();
|
||||||
const nonScalable = this.isNonScalable();
|
const nonScalable = this.isNonScalable();
|
||||||
return overflow || autoScalerOverflow || inProgress || invalid || hasNoChanges || nonScalable;
|
const stackNameInvalid = this.state.stackNameError !== '';
|
||||||
|
return overflow || autoScalerOverflow || inProgress || invalid || hasNoChanges || nonScalable || stackNameInvalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
isUpdateApplicationViaWebEditorButtonDisabled() {
|
isUpdateApplicationViaWebEditorButtonDisabled() {
|
||||||
|
|
|
@ -92,7 +92,12 @@
|
||||||
<div class="mb-4 w-fit">
|
<div class="mb-4 w-fit">
|
||||||
<stack-name-label-insight></stack-name-label-insight>
|
<stack-name-label-insight></stack-name-label-insight>
|
||||||
</div>
|
</div>
|
||||||
<kube-stack-name stack-name="ctrl.formValues.StackName" set-stack-name="(ctrl.setStackName)" stacks="ctrl.stacks"></kube-stack-name>
|
<kube-stack-name
|
||||||
|
stack-name="ctrl.formValues.StackName"
|
||||||
|
set-stack-name="(ctrl.setStackName)"
|
||||||
|
stacks="ctrl.stacks"
|
||||||
|
error="ctrl.state.stackNameError"
|
||||||
|
></kube-stack-name>
|
||||||
</div>
|
</div>
|
||||||
<!-- !namespace -->
|
<!-- !namespace -->
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { parseAutoUpdateResponse, transformAutoUpdateViewModel } from '@/react/p
|
||||||
import { baseStackWebhookUrl, createWebhookId } from '@/portainer/helpers/webhookHelper';
|
import { baseStackWebhookUrl, createWebhookId } from '@/portainer/helpers/webhookHelper';
|
||||||
import { confirmWebEditorDiscard } from '@@/modals/confirm';
|
import { confirmWebEditorDiscard } from '@@/modals/confirm';
|
||||||
import { getVariablesFieldDefaultValues } from '@/react/portainer/custom-templates/components/CustomTemplatesVariablesField';
|
import { getVariablesFieldDefaultValues } from '@/react/portainer/custom-templates/components/CustomTemplatesVariablesField';
|
||||||
|
import { KUBE_STACK_NAME_VALIDATION_REGEX } from '@/react/kubernetes/DeployView/StackName/constants';
|
||||||
|
|
||||||
class KubernetesDeployController {
|
class KubernetesDeployController {
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
|
@ -57,6 +58,7 @@ class KubernetesDeployController {
|
||||||
templateLoadFailed: false,
|
templateLoadFailed: false,
|
||||||
isEditorReadOnly: false,
|
isEditorReadOnly: false,
|
||||||
selectedHelmChart: '',
|
selectedHelmChart: '',
|
||||||
|
stackNameError: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
this.currentUser = {
|
this.currentUser = {
|
||||||
|
@ -117,7 +119,16 @@ class KubernetesDeployController {
|
||||||
}
|
}
|
||||||
|
|
||||||
setStackName(name) {
|
setStackName(name) {
|
||||||
|
return this.$async(async () => {
|
||||||
|
if (KUBE_STACK_NAME_VALIDATION_REGEX.test(name) || name === '') {
|
||||||
|
this.state.stackNameError = '';
|
||||||
|
} else {
|
||||||
|
this.state.stackNameError =
|
||||||
|
"Stack must consist of alphanumeric characters, '-', '_' or '.', must start and end with an alphanumeric character and must be 63 characters or less (e.g. 'my-name', or 'abc-123').";
|
||||||
|
}
|
||||||
|
|
||||||
this.formValues.StackName = name;
|
this.formValues.StackName = name;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderTemplate() {
|
renderTemplate() {
|
||||||
|
@ -197,9 +208,9 @@ class KubernetesDeployController {
|
||||||
const isWebEditorInvalid = this.state.BuildMethod === KubernetesDeployBuildMethods.WEB_EDITOR && _.isEmpty(this.formValues.EditorContent);
|
const isWebEditorInvalid = this.state.BuildMethod === KubernetesDeployBuildMethods.WEB_EDITOR && _.isEmpty(this.formValues.EditorContent);
|
||||||
const isURLFormInvalid = this.state.BuildMethod === KubernetesDeployBuildMethods.URL && _.isEmpty(this.formValues.ManifestURL);
|
const isURLFormInvalid = this.state.BuildMethod === KubernetesDeployBuildMethods.URL && _.isEmpty(this.formValues.ManifestURL);
|
||||||
const isCustomTemplateInvalid = this.state.BuildMethod === KubernetesDeployBuildMethods.CUSTOM_TEMPLATE && _.isEmpty(this.formValues.EditorContent);
|
const isCustomTemplateInvalid = this.state.BuildMethod === KubernetesDeployBuildMethods.CUSTOM_TEMPLATE && _.isEmpty(this.formValues.EditorContent);
|
||||||
|
|
||||||
const isNamespaceInvalid = _.isEmpty(this.formValues.Namespace);
|
const isNamespaceInvalid = _.isEmpty(this.formValues.Namespace);
|
||||||
return isWebEditorInvalid || isURLFormInvalid || isCustomTemplateInvalid || this.state.actionInProgress || isNamespaceInvalid;
|
const isStackNameInvalid = this.state.stackNameError !== '';
|
||||||
|
return isWebEditorInvalid || isURLFormInvalid || isCustomTemplateInvalid || this.state.actionInProgress || isNamespaceInvalid || isStackNameInvalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
onChangeFormValues(newValues) {
|
onChangeFormValues(newValues) {
|
||||||
|
|
|
@ -14,10 +14,12 @@ export function NameField({
|
||||||
onChange,
|
onChange,
|
||||||
value,
|
value,
|
||||||
errors,
|
errors,
|
||||||
|
placeholder,
|
||||||
}: {
|
}: {
|
||||||
onChange(value: string): void;
|
onChange(value: string): void;
|
||||||
value: string;
|
value: string;
|
||||||
errors?: FormikErrors<string>;
|
errors?: FormikErrors<string>;
|
||||||
|
placeholder?: string;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<FormControl inputId="name-input" label="Name" errors={errors} required>
|
<FormControl inputId="name-input" label="Name" errors={errors} required>
|
||||||
|
@ -25,6 +27,7 @@ export function NameField({
|
||||||
id="name-input"
|
id="name-input"
|
||||||
onChange={(e) => onChange(e.target.value)}
|
onChange={(e) => onChange(e.target.value)}
|
||||||
value={value}
|
value={value}
|
||||||
|
placeholder={placeholder}
|
||||||
required
|
required
|
||||||
data-cy="stack-name-input"
|
data-cy="stack-name-input"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -33,7 +33,7 @@ export const textByType = {
|
||||||
(Deployment, Secret, ConfigMap...)
|
(Deployment, Secret, ConfigMap...)
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
You can get more information about Kubernetes file format in the
|
You can get more information about Kubernetes file format in the{' '}
|
||||||
<a
|
<a
|
||||||
href="https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/"
|
href="https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|
|
@ -22,7 +22,7 @@ export function CollapseExpandButton({
|
||||||
aria-label={isExpanded ? 'Collapse' : 'Expand'}
|
aria-label={isExpanded ? 'Collapse' : 'Expand'}
|
||||||
aria-expanded={isExpanded}
|
aria-expanded={isExpanded}
|
||||||
type="button"
|
type="button"
|
||||||
className="flex-none border-none bg-transparent flex items-center p-0 px-3 group"
|
className="flex-none border-none bg-transparent flex items-center p-0 !ml-0 group"
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { Link } from '@@/Link';
|
||||||
import { TextTip } from '@@/Tip/TextTip';
|
import { TextTip } from '@@/Tip/TextTip';
|
||||||
import { Tooltip } from '@@/Tip/Tooltip';
|
import { Tooltip } from '@@/Tip/Tooltip';
|
||||||
import { AutocompleteSelect } from '@@/form-components/AutocompleteSelect';
|
import { AutocompleteSelect } from '@@/form-components/AutocompleteSelect';
|
||||||
|
import { FormError } from '@@/form-components/FormError';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
stackName: string;
|
stackName: string;
|
||||||
|
@ -13,6 +14,7 @@ type Props = {
|
||||||
stacks?: string[];
|
stacks?: string[];
|
||||||
inputClassName?: string;
|
inputClassName?: string;
|
||||||
textTip?: string;
|
textTip?: string;
|
||||||
|
error?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function StackName({
|
export function StackName({
|
||||||
|
@ -21,6 +23,7 @@ export function StackName({
|
||||||
stacks = [],
|
stacks = [],
|
||||||
inputClassName,
|
inputClassName,
|
||||||
textTip = "Enter or select a 'stack' name to group multiple deployments together, or else leave empty to ignore.",
|
textTip = "Enter or select a 'stack' name to group multiple deployments together, or else leave empty to ignore.",
|
||||||
|
error = '',
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const isAdminQuery = useIsEdgeAdmin();
|
const isAdminQuery = useIsEdgeAdmin();
|
||||||
const stackResults = useMemo(
|
const stackResults = useMemo(
|
||||||
|
@ -54,9 +57,11 @@ export function StackName({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{textTip ? (
|
||||||
<TextTip className="mb-4" color="blue">
|
<TextTip className="mb-4" color="blue">
|
||||||
{textTip}
|
{textTip}
|
||||||
</TextTip>
|
</TextTip>
|
||||||
|
) : null}
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label
|
<label
|
||||||
htmlFor="stack_name"
|
htmlFor="stack_name"
|
||||||
|
@ -77,6 +82,7 @@ export function StackName({
|
||||||
inputId="stack_name"
|
inputId="stack_name"
|
||||||
data-cy="k8s-deploy-stack-input"
|
data-cy="k8s-deploy-stack-input"
|
||||||
/>
|
/>
|
||||||
|
{error ? <FormError>{error}</FormError> : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
4
app/react/kubernetes/DeployView/StackName/constants.ts
Normal file
4
app/react/kubernetes/DeployView/StackName/constants.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
// this regex is to satisfy k8s label validation rules
|
||||||
|
// alphanumeric, lowercase, uppercase, can contain dashes, dots and underscores, max 63 characters
|
||||||
|
export const KUBE_STACK_NAME_VALIDATION_REGEX =
|
||||||
|
/^(([a-zA-Z0-9](?:(?:[-a-zA-Z0-9_.]){0,61}[a-zA-Z0-9])?))$/;
|
|
@ -95,14 +95,10 @@ export function CommonFields({
|
||||||
export function validation({
|
export function validation({
|
||||||
currentTemplateId,
|
currentTemplateId,
|
||||||
templates = [],
|
templates = [],
|
||||||
viewType = 'docker',
|
|
||||||
}: {
|
}: {
|
||||||
currentTemplateId?: CustomTemplate['Id'];
|
currentTemplateId?: CustomTemplate['Id'];
|
||||||
templates?: Array<CustomTemplate>;
|
templates?: Array<CustomTemplate>;
|
||||||
viewType?: 'kube' | 'docker' | 'edge';
|
|
||||||
} = {}): SchemaOf<Values> {
|
} = {}): SchemaOf<Values> {
|
||||||
const titlePattern = titlePatternValidation(viewType);
|
|
||||||
|
|
||||||
return object({
|
return object({
|
||||||
Title: string()
|
Title: string()
|
||||||
.required('Title is required.')
|
.required('Title is required.')
|
||||||
|
@ -116,31 +112,12 @@ export function validation({
|
||||||
template.Title === value && template.Id !== currentTemplateId
|
template.Title === value && template.Id !== currentTemplateId
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.matches(titlePattern.pattern, titlePattern.error),
|
.max(
|
||||||
|
200,
|
||||||
|
'Custom template title must be less than or equal to 200 characters'
|
||||||
|
),
|
||||||
Description: string().required('Description is required.'),
|
Description: string().required('Description is required.'),
|
||||||
Note: string().default(''),
|
Note: string().default(''),
|
||||||
Logo: string().default(''),
|
Logo: string().default(''),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TEMPLATE_NAME_VALIDATION_REGEX = '^[-_a-z0-9]+$';
|
|
||||||
|
|
||||||
const KUBE_TEMPLATE_NAME_VALIDATION_REGEX =
|
|
||||||
'^(([a-z0-9](?:(?:[-a-z0-9_.]){0,61}[a-z0-9])?))$'; // alphanumeric, lowercase, can contain dashes, dots and underscores, max 63 characters
|
|
||||||
|
|
||||||
function titlePatternValidation(type: 'kube' | 'docker' | 'edge') {
|
|
||||||
switch (type) {
|
|
||||||
case 'kube':
|
|
||||||
return {
|
|
||||||
pattern: new RegExp(KUBE_TEMPLATE_NAME_VALIDATION_REGEX),
|
|
||||||
error:
|
|
||||||
"This field must consist of lower-case alphanumeric characters, '.', '_' or '-', must start and end with an alphanumeric character and must be 63 characters or less (e.g. 'my-name', or 'abc-123').",
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
return {
|
|
||||||
pattern: new RegExp(TEMPLATE_NAME_VALIDATION_REGEX),
|
|
||||||
error:
|
|
||||||
"This field must consist of lower-case alphanumeric characters, '_' or '-' (e.g. 'my-name', or 'abc-123').",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { MetadataFieldset } from './MetadataFieldset';
|
||||||
|
|
||||||
export function MoreSettingsSection({ children }: PropsWithChildren<unknown>) {
|
export function MoreSettingsSection({ children }: PropsWithChildren<unknown>) {
|
||||||
return (
|
return (
|
||||||
<FormSection title="More settings" isFoldable>
|
<FormSection title="More settings" className="ml-0" isFoldable>
|
||||||
<div className="ml-8">
|
<div className="ml-8">
|
||||||
{children}
|
{children}
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,6 @@ export function useValidation({
|
||||||
}).concat(
|
}).concat(
|
||||||
commonFieldsValidation({
|
commonFieldsValidation({
|
||||||
templates: customTemplatesQuery.data,
|
templates: customTemplatesQuery.data,
|
||||||
viewType,
|
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
|
|
|
@ -62,7 +62,6 @@ export function useValidation({
|
||||||
commonFieldsValidation({
|
commonFieldsValidation({
|
||||||
templates: customTemplatesQuery.data,
|
templates: customTemplatesQuery.data,
|
||||||
currentTemplateId: templateId,
|
currentTemplateId: templateId,
|
||||||
viewType,
|
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
|
|
|
@ -63,7 +63,7 @@ export function DeployForm({
|
||||||
const isGit = !!template.GitConfig;
|
const isGit = !!template.GitConfig;
|
||||||
|
|
||||||
const initialValues: FormValues = {
|
const initialValues: FormValues = {
|
||||||
name: template.Title || '',
|
name: '',
|
||||||
variables: getVariablesFieldDefaultValues(template.Variables),
|
variables: getVariablesFieldDefaultValues(template.Variables),
|
||||||
accessControl: parseAccessControlFormData(
|
accessControl: parseAccessControlFormData(
|
||||||
isEdgeAdminQuery.isAdmin,
|
isEdgeAdminQuery.isAdmin,
|
||||||
|
@ -86,6 +86,7 @@ export function DeployForm({
|
||||||
value={values.name}
|
value={values.name}
|
||||||
onChange={(v) => setFieldValue('name', v)}
|
onChange={(v) => setFieldValue('name', v)}
|
||||||
errors={errors.name}
|
errors={errors.name}
|
||||||
|
placeholder="e.g. mystack"
|
||||||
/>
|
/>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue