mirror of
https://github.com/ansible/awx.git
synced 2026-03-03 09:48:51 -03:30
Encapsulate each scm type subform in its own component
This commit is contained in:
@@ -91,7 +91,7 @@ class ProjectListItem extends React.Component {
|
|||||||
<Link
|
<Link
|
||||||
id={labelId}
|
id={labelId}
|
||||||
to={`${detailUrl}`}
|
to={`${detailUrl}`}
|
||||||
style={{ marginLeft: '10px' }}
|
css={{ marginLeft: '10px' }}
|
||||||
>
|
>
|
||||||
<b>{project.name}</b>
|
<b>{project.name}</b>
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -1,30 +1,27 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { withRouter, Link } from 'react-router-dom';
|
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { withFormik, Field } from 'formik';
|
import { Formik, Field } from 'formik';
|
||||||
import { Config } from '@contexts/Config';
|
import { Config } from '@contexts/Config';
|
||||||
import {
|
import { Form, FormGroup } from '@patternfly/react-core';
|
||||||
Form as _Form,
|
|
||||||
FormGroup,
|
|
||||||
Title as _Title,
|
|
||||||
} from '@patternfly/react-core';
|
|
||||||
import AnsibleSelect from '@components/AnsibleSelect';
|
import AnsibleSelect from '@components/AnsibleSelect';
|
||||||
import ContentError from '@components/ContentError';
|
import ContentError from '@components/ContentError';
|
||||||
import ContentLoading from '@components/ContentLoading';
|
import ContentLoading from '@components/ContentLoading';
|
||||||
import FormActionGroup from '@components/FormActionGroup/FormActionGroup';
|
import FormActionGroup from '@components/FormActionGroup/FormActionGroup';
|
||||||
import FormField, { CheckboxField, FieldTooltip } from '@components/FormField';
|
import FormField, { FieldTooltip } from '@components/FormField';
|
||||||
import FormRow from '@components/FormRow';
|
import FormRow from '@components/FormRow';
|
||||||
import OrganizationLookup from '@components/Lookup/OrganizationLookup';
|
import OrganizationLookup from '@components/Lookup/OrganizationLookup';
|
||||||
import CredentialLookup from '@components/Lookup/CredentialLookup';
|
|
||||||
import { CredentialTypesAPI, ProjectsAPI } from '@api';
|
import { CredentialTypesAPI, ProjectsAPI } from '@api';
|
||||||
import { required } from '@util/validators';
|
import { required } from '@util/validators';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import {
|
||||||
const Form = styled(_Form)`
|
GitSubForm,
|
||||||
padding: 0 24px;
|
HgSubForm,
|
||||||
`;
|
SvnSubForm,
|
||||||
|
InsightsSubForm,
|
||||||
|
SubFormTitle,
|
||||||
|
} from './ProjectSubForms';
|
||||||
|
|
||||||
const ScmTypeFormRow = styled(FormRow)`
|
const ScmTypeFormRow = styled(FormRow)`
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
@@ -33,19 +30,10 @@ const ScmTypeFormRow = styled(FormRow)`
|
|||||||
padding: 24px;
|
padding: 24px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const OptionsFormGroup = styled.div`
|
|
||||||
grid-column: 1/-1;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Title = styled(_Title)`
|
|
||||||
--pf-c-title--m-md--FontWeight: 700;
|
|
||||||
grid-column: 1 / -1;
|
|
||||||
`;
|
|
||||||
|
|
||||||
function ProjectForm(props) {
|
function ProjectForm(props) {
|
||||||
const { values, handleCancel, handleSubmit, i18n } = props;
|
const { project, handleCancel, handleSubmit, i18n } = props;
|
||||||
const [contentError, setContentError] = useState(null);
|
const [contentError, setContentError] = useState(null);
|
||||||
const [hasContentLoading, setHasContentLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [organization, setOrganization] = useState(null);
|
const [organization, setOrganization] = useState(null);
|
||||||
const [scmTypeOptions, setScmTypeOptions] = useState(null);
|
const [scmTypeOptions, setScmTypeOptions] = useState(null);
|
||||||
const [scmCredential, setScmCredential] = useState({
|
const [scmCredential, setScmCredential] = useState({
|
||||||
@@ -58,45 +46,48 @@ function ProjectForm(props) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function fetchCredTypeId(params) {
|
|
||||||
try {
|
|
||||||
const {
|
|
||||||
data: {
|
|
||||||
results: [credential],
|
|
||||||
},
|
|
||||||
} = await CredentialTypesAPI.read(params);
|
|
||||||
return credential.id;
|
|
||||||
} catch (error) {
|
|
||||||
setContentError(error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
const insightsTypeId = await fetchCredTypeId({ name: 'Insights' });
|
try {
|
||||||
const scmTypeId = await fetchCredTypeId({ kind: 'scm' });
|
const [
|
||||||
const {
|
{
|
||||||
data: {
|
data: {
|
||||||
actions: {
|
results: [scmCredentialType],
|
||||||
GET: {
|
|
||||||
scm_type: { choices },
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
} = await ProjectsAPI.readOptions();
|
data: {
|
||||||
setInsightsCredential({ typeId: insightsTypeId });
|
results: [insightsCredentialType],
|
||||||
setScmCredential({ typeId: scmTypeId });
|
},
|
||||||
setScmTypeOptions(choices);
|
},
|
||||||
setHasContentLoading(false);
|
{
|
||||||
|
data: {
|
||||||
|
actions: {
|
||||||
|
GET: {
|
||||||
|
scm_type: { choices },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] = await Promise.all([
|
||||||
|
CredentialTypesAPI.read({ kind: 'scm' }),
|
||||||
|
CredentialTypesAPI.read({ name: 'Insights' }),
|
||||||
|
ProjectsAPI.readOptions(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
setScmCredential({ typeId: scmCredentialType.id });
|
||||||
|
setInsightsCredential({ typeId: insightsCredentialType.id });
|
||||||
|
setScmTypeOptions(choices);
|
||||||
|
} catch (error) {
|
||||||
|
setContentError(error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchData();
|
fetchData();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const resetScmTypeFields = (value, form) => {
|
const resetScmTypeFields = form => {
|
||||||
if (form.initialValues.scm_type === value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const scmFormFields = [
|
const scmFormFields = [
|
||||||
'scm_url',
|
'scm_url',
|
||||||
'scm_branch',
|
'scm_branch',
|
||||||
@@ -115,63 +106,7 @@ function ProjectForm(props) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const gitScmTooltip = (
|
if (isLoading) {
|
||||||
<span>
|
|
||||||
{i18n._(t`Example URLs for GIT SCM include:`)}
|
|
||||||
<ul css="margin: 10px 0 10px 20px">
|
|
||||||
<li>https://github.com/ansible/ansible.git</li>
|
|
||||||
<li>git@github.com:ansible/ansible.git</li>
|
|
||||||
<li>git://servername.example.com/ansible.git</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
{i18n._(t`Note: When using SSH protocol for GitHub or
|
|
||||||
Bitbucket, enter an SSH key only, do not enter a username
|
|
||||||
(other than git). Additionally, GitHub and Bitbucket do
|
|
||||||
not support password authentication when using SSH. GIT
|
|
||||||
read only protocol (git://) does not use username or
|
|
||||||
password information.`)}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
|
|
||||||
const hgScmTooltip = (
|
|
||||||
<span>
|
|
||||||
{i18n._(t`Example URLs for Mercurial SCM include:`)}
|
|
||||||
<ul style={{ margin: '10px 0 10px 20px' }}>
|
|
||||||
<li>https://bitbucket.org/username/project</li>
|
|
||||||
<li>ssh://hg@bitbucket.org/username/project</li>
|
|
||||||
<li>ssh://server.example.com/path</li>
|
|
||||||
</ul>
|
|
||||||
{i18n._(t`Note: Mercurial does not support password authentication
|
|
||||||
for SSH. Do not put the username and key in the URL. If using
|
|
||||||
Bitbucket and SSH, do not supply your Bitbucket username.
|
|
||||||
`)}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
|
|
||||||
const svnScmTooltip = (
|
|
||||||
<span>
|
|
||||||
{i18n._(t`Example URLs for Subversion SCM include:`)}
|
|
||||||
<ul style={{ margin: '10px 0 10px 20px' }}>
|
|
||||||
<li>https://github.com/ansible/ansible</li>
|
|
||||||
<li>svn://servername.example.com/path</li>
|
|
||||||
<li>svn+ssh://servername.example.com/path</li>
|
|
||||||
</ul>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
|
|
||||||
const scmUrlTooltips = {
|
|
||||||
git: gitScmTooltip,
|
|
||||||
hg: hgScmTooltip,
|
|
||||||
svn: svnScmTooltip,
|
|
||||||
};
|
|
||||||
|
|
||||||
const scmBranchLabels = {
|
|
||||||
git: i18n._(t`SCM Branch/Tag/Commit`),
|
|
||||||
hg: i18n._(t`SCM Branch/Tag/Revision`),
|
|
||||||
svn: i18n._(t`Revision #`),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (hasContentLoading) {
|
|
||||||
return <ContentLoading />;
|
return <ContentLoading />;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,343 +115,197 @@ function ProjectForm(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form autoComplete="off" onSubmit={handleSubmit}>
|
<Formik
|
||||||
<FormRow>
|
initialValues={{
|
||||||
<FormField
|
allow_override: project.allow_override || false,
|
||||||
id="project-name"
|
credential: project.credential || '',
|
||||||
label={i18n._(t`Name`)}
|
custom_virtualenv: project.custom_virtualenv || '',
|
||||||
name="name"
|
description: project.description || '',
|
||||||
type="text"
|
name: project.name || '',
|
||||||
validate={required(null, i18n)}
|
organization: project.organization || '',
|
||||||
isRequired
|
scm_branch: project.scm_branch || '',
|
||||||
/>
|
scm_clean: project.scm_clean || false,
|
||||||
<FormField
|
scm_delete_on_update: project.scm_delete_on_update || false,
|
||||||
id="project-description"
|
scm_refspec: project.scm_refspec || '',
|
||||||
label={i18n._(t`Description`)}
|
scm_type: project.scm_type || '',
|
||||||
name="description"
|
scm_update_cache_timeout: project.scm_update_cache_timeout || 0,
|
||||||
type="text"
|
scm_update_on_launch: project.scm_update_on_launch || false,
|
||||||
/>
|
scm_url: project.scm_url || '',
|
||||||
<Field
|
}}
|
||||||
name="organization"
|
onSubmit={handleSubmit}
|
||||||
validate={required(i18n._(t`Select a value for this field`), i18n)}
|
render={formik => (
|
||||||
render={({ form }) => (
|
<Form
|
||||||
<OrganizationLookup
|
autoComplete="off"
|
||||||
helperTextInvalid={form.errors.organization}
|
onSubmit={formik.handleSubmit}
|
||||||
isValid={!form.touched.organization || !form.errors.organization}
|
css="padding: 0 24px"
|
||||||
onBlur={() => form.setFieldTouched('organization')}
|
>
|
||||||
onChange={value => {
|
<FormRow>
|
||||||
form.setFieldValue('organization', value.id);
|
<FormField
|
||||||
setOrganization(value);
|
id="project-name"
|
||||||
}}
|
label={i18n._(t`Name`)}
|
||||||
value={organization}
|
name="name"
|
||||||
required
|
type="text"
|
||||||
/>
|
validate={required(null, i18n)}
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Field
|
|
||||||
name="scm_type"
|
|
||||||
validate={required(i18n._(t`Select a value for this field`), i18n)}
|
|
||||||
render={({ field, form }) => (
|
|
||||||
<FormGroup
|
|
||||||
fieldId="project-scm-type"
|
|
||||||
helperTextInvalid={form.errors.scm_type}
|
|
||||||
isRequired
|
isRequired
|
||||||
isValid={!form.touched.scm_type || !form.errors.scm_type}
|
/>
|
||||||
label={i18n._(t`SCM Type`)}
|
<FormField
|
||||||
>
|
id="project-description"
|
||||||
<AnsibleSelect
|
label={i18n._(t`Description`)}
|
||||||
{...field}
|
name="description"
|
||||||
id="scm_type"
|
type="text"
|
||||||
data={[
|
/>
|
||||||
{
|
<Field
|
||||||
value: '',
|
name="organization"
|
||||||
key: '',
|
validate={required(
|
||||||
label: i18n._(t`Choose an SCM Type`),
|
i18n._(t`Select a value for this field`),
|
||||||
isDisabled: true,
|
i18n
|
||||||
},
|
)}
|
||||||
...scmTypeOptions.map(option => {
|
render={({ form }) => (
|
||||||
if (option[1] === 'Manual') {
|
<OrganizationLookup
|
||||||
option[0] = 'manual';
|
helperTextInvalid={form.errors.organization}
|
||||||
}
|
isValid={
|
||||||
return {
|
!form.touched.organization || !form.errors.organization
|
||||||
label: option[1],
|
}
|
||||||
value: option[0],
|
onBlur={() => form.setFieldTouched('organization')}
|
||||||
key: option[0],
|
onChange={value => {
|
||||||
};
|
form.setFieldValue('organization', value.id);
|
||||||
}),
|
setOrganization(value);
|
||||||
]}
|
}}
|
||||||
onChange={(event, value) => {
|
value={organization}
|
||||||
form.setFieldValue('scm_type', value);
|
required
|
||||||
resetScmTypeFields(value, form);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{values.scm_type !== '' && (
|
|
||||||
<ScmTypeFormRow>
|
|
||||||
<Title size="md">{i18n._(t`Type Details`)}</Title>
|
|
||||||
{(values.scm_type === 'git' ||
|
|
||||||
values.scm_type === 'hg' ||
|
|
||||||
values.scm_type === 'svn') && (
|
|
||||||
<FormField
|
|
||||||
id="project-scm-url"
|
|
||||||
isRequired
|
|
||||||
label={i18n._(t`SCM URL`)}
|
|
||||||
name="scm_url"
|
|
||||||
type="text"
|
|
||||||
validate={required(null, i18n)}
|
|
||||||
tooltipMaxWidth="350px"
|
|
||||||
tooltip={scmUrlTooltips[values.scm_type]}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{(values.scm_type === 'git' ||
|
|
||||||
values.scm_type === 'hg' ||
|
|
||||||
values.scm_type === 'svn') && (
|
|
||||||
<FormField
|
|
||||||
id="project-scm-branch"
|
|
||||||
name="scm_branch"
|
|
||||||
type="text"
|
|
||||||
label={scmBranchLabels[values.scm_type]}
|
|
||||||
tooltip={i18n._(t`Branch to checkout. In addition to branches,
|
|
||||||
you can input tags, commit hashes, and arbitrary refs. Some
|
|
||||||
commit hashes and refs may not be availble unless you also
|
|
||||||
provide a custom refspec.`)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{values.scm_type === 'git' && (
|
|
||||||
<FormField
|
|
||||||
id="project-scm-refspec"
|
|
||||||
label={i18n._(t`SCM Refspec`)}
|
|
||||||
name="scm_refspec"
|
|
||||||
type="text"
|
|
||||||
tooltipMaxWidth="400px"
|
|
||||||
tooltip={
|
|
||||||
<span>
|
|
||||||
{i18n._(t`A refspec to fetch (passed to the Ansible git
|
|
||||||
module). This parameter allows access to references via
|
|
||||||
the branch field not otherwise available.`)}
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
{i18n._(
|
|
||||||
t`Note: This field assumes the remote name is "origin".`
|
|
||||||
)}
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
{i18n._(t`Examples include:`)}
|
|
||||||
<ul style={{ margin: '10px 0 10px 20px' }}>
|
|
||||||
<li>refs/*:refs/remotes/origin/*</li>
|
|
||||||
<li>
|
|
||||||
refs/pull/62/head:refs/remotes/origin/pull/62/head
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
{i18n._(t`The first fetches all references. The second
|
|
||||||
fetches the Github pull request number 62, in this example
|
|
||||||
the branch needs to be "pull/62/head".`)}
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
{i18n._(t`For more information, refer to the`)}{' '}
|
|
||||||
<Link to="https://docs.ansible.com/ansible-tower/latest/html/userguide/projects.html#manage-playbooks-using-source-control">
|
|
||||||
{i18n._(t`Ansible Tower Documentation.`)}
|
|
||||||
</Link>
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{(values.scm_type === 'git' ||
|
|
||||||
values.scm_type === 'hg' ||
|
|
||||||
values.scm_type === 'svn') && (
|
|
||||||
<Field
|
|
||||||
name="credential"
|
|
||||||
render={({ form }) => (
|
|
||||||
<CredentialLookup
|
|
||||||
credentialTypeId={scmCredential.typeId}
|
|
||||||
label={i18n._(t`SCM Credential`)}
|
|
||||||
value={scmCredential.value}
|
|
||||||
onChange={credential => {
|
|
||||||
form.setFieldValue('credential', credential.id);
|
|
||||||
setScmCredential({
|
|
||||||
...scmCredential,
|
|
||||||
value: credential,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{values.scm_type === 'insights' && (
|
|
||||||
<Field
|
|
||||||
name="credential"
|
|
||||||
validate={required(
|
|
||||||
i18n._(t`Select a value for this field`),
|
|
||||||
i18n
|
|
||||||
)}
|
|
||||||
render={({ form }) => (
|
|
||||||
<CredentialLookup
|
|
||||||
credentialTypeId={insightsCredential.typeId}
|
|
||||||
label={i18n._(t`Insights Credential`)}
|
|
||||||
helperTextInvalid={form.errors.credential}
|
|
||||||
isValid={
|
|
||||||
!form.touched.credential || !form.errors.credential
|
|
||||||
}
|
|
||||||
onBlur={() => form.setFieldTouched('credential')}
|
|
||||||
onChange={credential => {
|
|
||||||
form.setFieldValue('credential', credential.id);
|
|
||||||
setInsightsCredential({
|
|
||||||
...insightsCredential,
|
|
||||||
value: credential,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
value={insightsCredential.value}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{/*
|
|
||||||
PF Bug: FormGroup doesn't pass down className
|
|
||||||
Workaround is to wrap FormGroup with an extra div
|
|
||||||
Cleanup when upgraded to @patternfly/react-core@3.103.4
|
|
||||||
*/}
|
|
||||||
{values.scm_type !== 'manual' && (
|
|
||||||
<OptionsFormGroup>
|
|
||||||
<FormGroup
|
|
||||||
fieldId="project-option-checkboxes"
|
|
||||||
label={i18n._(t`Options`)}
|
|
||||||
>
|
|
||||||
<FormRow>
|
|
||||||
<CheckboxField
|
|
||||||
id="option-scm-clean"
|
|
||||||
name="scm_clean"
|
|
||||||
label={i18n._(t`Clean`)}
|
|
||||||
tooltip={i18n._(
|
|
||||||
t`Remove any local modifications prior to performing an update.`
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<CheckboxField
|
|
||||||
id="option-scm-delete-on-update"
|
|
||||||
name="scm_delete_on_update"
|
|
||||||
label={i18n._(t`Delete`)}
|
|
||||||
tooltip={i18n._(
|
|
||||||
t`Delete the local repository in its entirety prior to
|
|
||||||
performing an update. Depending on the size of the
|
|
||||||
repository this may significantly increase the amount
|
|
||||||
of time required to complete an update.`
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<CheckboxField
|
|
||||||
id="option-scm-update-on-launch"
|
|
||||||
name="scm_update_on_launch"
|
|
||||||
label={i18n._(t`Update Revision on Launch`)}
|
|
||||||
tooltip={i18n._(
|
|
||||||
t`Each time a job runs using this project, update the
|
|
||||||
revision of the project prior to starting the job.`
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{values.scm_type !== 'insights' && (
|
|
||||||
<CheckboxField
|
|
||||||
id="option-allow-override"
|
|
||||||
name="allow_override"
|
|
||||||
label={i18n._(t`Allow Branch Override`)}
|
|
||||||
tooltip={i18n._(
|
|
||||||
t`Allow changing the SCM branch or revision in a job
|
|
||||||
template that uses this project.`
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</FormRow>
|
|
||||||
</FormGroup>
|
|
||||||
</OptionsFormGroup>
|
|
||||||
)}
|
|
||||||
{values.scm_type !== 'manual' && values.scm_update_on_launch && (
|
|
||||||
<>
|
|
||||||
<Title size="md">{i18n._(t`Option Details`)}</Title>
|
|
||||||
<FormField
|
|
||||||
id="project-cache-timeout"
|
|
||||||
name="scm_update_cache_timeout"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
label={i18n._(t`Cache Timeout`)}
|
|
||||||
tooltip={i18n._(t`Time in seconds to consider a project
|
|
||||||
to be current. During job runs and callbacks the task
|
|
||||||
system will evaluate the timestamp of the latest project
|
|
||||||
update. If it is older than Cache Timeout, it is not
|
|
||||||
considered current, and a new project update will be
|
|
||||||
performed.`)}
|
|
||||||
/>
|
/>
|
||||||
</>
|
)}
|
||||||
|
/>
|
||||||
|
<Field
|
||||||
|
name="scm_type"
|
||||||
|
validate={required(
|
||||||
|
i18n._(t`Select a value for this field`),
|
||||||
|
i18n
|
||||||
|
)}
|
||||||
|
render={({ field, form }) => (
|
||||||
|
<FormGroup
|
||||||
|
fieldId="project-scm-type"
|
||||||
|
helperTextInvalid={form.errors.scm_type}
|
||||||
|
isRequired
|
||||||
|
isValid={!form.touched.scm_type || !form.errors.scm_type}
|
||||||
|
label={i18n._(t`SCM Type`)}
|
||||||
|
>
|
||||||
|
<AnsibleSelect
|
||||||
|
{...field}
|
||||||
|
id="scm_type"
|
||||||
|
data={[
|
||||||
|
{
|
||||||
|
value: '',
|
||||||
|
key: '',
|
||||||
|
label: i18n._(t`Choose an SCM Type`),
|
||||||
|
isDisabled: true,
|
||||||
|
},
|
||||||
|
...scmTypeOptions.map(([value, label]) => {
|
||||||
|
if (label === 'Manual') {
|
||||||
|
value = 'manual';
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
key: value,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
onChange={(event, value) => {
|
||||||
|
form.setFieldValue('scm_type', value);
|
||||||
|
resetScmTypeFields(form);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{formik.values.scm_type !== '' && (
|
||||||
|
<ScmTypeFormRow>
|
||||||
|
<SubFormTitle size="md">{i18n._(t`Type Details`)}</SubFormTitle>
|
||||||
|
{
|
||||||
|
{
|
||||||
|
git: (
|
||||||
|
<GitSubForm
|
||||||
|
setScmCredential={setScmCredential}
|
||||||
|
scmCredential={scmCredential}
|
||||||
|
scmUpdateOnLaunch={formik.values.scm_update_on_launch}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
hg: (
|
||||||
|
<HgSubForm
|
||||||
|
setScmCredential={setScmCredential}
|
||||||
|
scmCredential={scmCredential}
|
||||||
|
scmUpdateOnLaunch={formik.values.scm_update_on_launch}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
svn: (
|
||||||
|
<SvnSubForm
|
||||||
|
setScmCredential={setScmCredential}
|
||||||
|
scmCredential={scmCredential}
|
||||||
|
scmUpdateOnLaunch={formik.values.scm_update_on_launch}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
insights: (
|
||||||
|
<InsightsSubForm
|
||||||
|
setInsightsCredential={setInsightsCredential}
|
||||||
|
insightsCredential={insightsCredential}
|
||||||
|
scmUpdateOnLaunch={formik.values.scm_update_on_launch}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
}[formik.values.scm_type]
|
||||||
|
}
|
||||||
|
</ScmTypeFormRow>
|
||||||
)}
|
)}
|
||||||
</ScmTypeFormRow>
|
<Config>
|
||||||
)}
|
{({ custom_virtualenvs }) =>
|
||||||
<Config>
|
custom_virtualenvs &&
|
||||||
{({ custom_virtualenvs }) =>
|
custom_virtualenvs.length > 1 && (
|
||||||
custom_virtualenvs &&
|
<Field
|
||||||
custom_virtualenvs.length > 1 && (
|
name="custom_virtualenv"
|
||||||
<Field
|
render={({ field }) => (
|
||||||
name="custom_virtualenv"
|
<FormGroup
|
||||||
render={({ field }) => (
|
fieldId="project-custom-virtualenv"
|
||||||
<FormGroup
|
label={i18n._(t`Ansible Environment`)}
|
||||||
fieldId="project-custom-virtualenv"
|
>
|
||||||
label={i18n._(t`Ansible Environment`)}
|
<FieldTooltip
|
||||||
>
|
content={i18n._(t`Select the playbook to be executed by
|
||||||
<FieldTooltip
|
this job.`)}
|
||||||
content={i18n._(t`Select the playbook to be executed by
|
/>
|
||||||
this job.`)}
|
<AnsibleSelect
|
||||||
/>
|
id="project-custom-virtualenv"
|
||||||
<AnsibleSelect
|
data={[
|
||||||
id="project-custom-virtualenv"
|
{
|
||||||
data={[
|
label: i18n._(t`Use Default Ansible Environment`),
|
||||||
{
|
value: '/venv/ansible/',
|
||||||
label: i18n._(t`Use Default Ansible Environment`),
|
key: 'default',
|
||||||
value: '/venv/ansible/',
|
},
|
||||||
key: 'default',
|
...custom_virtualenvs
|
||||||
},
|
.filter(datum => datum !== '/venv/ansible/')
|
||||||
...custom_virtualenvs
|
.map(datum => ({
|
||||||
.filter(datum => datum !== '/venv/ansible/')
|
label: datum,
|
||||||
.map(datum => ({
|
value: datum,
|
||||||
label: datum,
|
key: datum,
|
||||||
value: datum,
|
})),
|
||||||
key: datum,
|
]}
|
||||||
})),
|
{...field}
|
||||||
]}
|
/>
|
||||||
{...field}
|
</FormGroup>
|
||||||
/>
|
)}
|
||||||
</FormGroup>
|
/>
|
||||||
)}
|
)
|
||||||
/>
|
}
|
||||||
)
|
</Config>
|
||||||
}
|
</FormRow>
|
||||||
</Config>
|
<FormActionGroup
|
||||||
</FormRow>
|
onCancel={handleCancel}
|
||||||
<FormActionGroup onCancel={handleCancel} onSubmit={handleSubmit} />
|
onSubmit={formik.handleSubmit}
|
||||||
</Form>
|
/>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const FormikApp = withFormik({
|
|
||||||
mapPropsToValues(props) {
|
|
||||||
const { project = {} } = props;
|
|
||||||
|
|
||||||
return {
|
|
||||||
credential: project.credential || '',
|
|
||||||
custom_virtualenv: project.custom_virtualenv || '',
|
|
||||||
description: project.description || '',
|
|
||||||
name: project.name || '',
|
|
||||||
organization: project.organization || '',
|
|
||||||
scm_branch: project.scm_branch || '',
|
|
||||||
scm_clean: project.scm_clean || false,
|
|
||||||
scm_delete_on_update: project.scm_delete_on_update || false,
|
|
||||||
scm_refspec: project.scm_refspec || '',
|
|
||||||
scm_type: project.scm_type || '',
|
|
||||||
scm_update_on_launch: project.scm_update_on_launch || false,
|
|
||||||
scm_url: project.scm_url || '',
|
|
||||||
scm_update_cache_timeout: project.scm_update_cache_timeout || 0,
|
|
||||||
allow_override: project.allow_override || false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
handleSubmit: (values, { props }) => props.handleSubmit(values),
|
|
||||||
})(ProjectForm);
|
|
||||||
|
|
||||||
ProjectForm.propTypes = {
|
ProjectForm.propTypes = {
|
||||||
handleCancel: PropTypes.func.isRequired,
|
handleCancel: PropTypes.func.isRequired,
|
||||||
handleSubmit: PropTypes.func.isRequired,
|
handleSubmit: PropTypes.func.isRequired,
|
||||||
@@ -527,4 +316,4 @@ ProjectForm.defaultProps = {
|
|||||||
project: {},
|
project: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withI18n()(withRouter(FormikApp));
|
export default withI18n()(ProjectForm);
|
||||||
|
|||||||
@@ -243,7 +243,7 @@ describe('<ProjectAdd />', () => {
|
|||||||
});
|
});
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
scmTypeSelect.props().onChange('git', { target: { name: 'insights' } });
|
scmTypeSelect.props().onChange('svn', { target: { name: 'Subversion' } });
|
||||||
});
|
});
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
expect(formik.state.values.scm_url).toEqual('');
|
expect(formik.state.values.scm_url).toEqual('');
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { withI18n } from '@lingui/react';
|
||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import FormField from '@components/FormField';
|
||||||
|
import {
|
||||||
|
UrlFormField,
|
||||||
|
BranchFormField,
|
||||||
|
ScmCredentialFormField,
|
||||||
|
ScmTypeOptions,
|
||||||
|
} from './SharedFields';
|
||||||
|
|
||||||
|
const GitSubForm = ({
|
||||||
|
i18n,
|
||||||
|
scmCredential,
|
||||||
|
setScmCredential,
|
||||||
|
scmUpdateOnLaunch,
|
||||||
|
}) => (
|
||||||
|
<>
|
||||||
|
<UrlFormField
|
||||||
|
i18n={i18n}
|
||||||
|
tooltip={
|
||||||
|
<span>
|
||||||
|
{i18n._(t`Example URLs for GIT SCM include:`)}
|
||||||
|
<ul css="margin: 10px 0 10px 20px">
|
||||||
|
<li>https://github.com/ansible/ansible.git</li>
|
||||||
|
<li>git@github.com:ansible/ansible.git</li>
|
||||||
|
<li>git://servername.example.com/ansible.git</li>
|
||||||
|
</ul>
|
||||||
|
{i18n._(t`Note: When using SSH protocol for GitHub or
|
||||||
|
Bitbucket, enter an SSH key only, do not enter a username
|
||||||
|
(other than git). Additionally, GitHub and Bitbucket do
|
||||||
|
not support password authentication when using SSH. GIT
|
||||||
|
read only protocol (git://) does not use username or
|
||||||
|
password information.`)}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<BranchFormField i18n={i18n} label={i18n._(t`SCM Branch/Tag/Commit`)} />
|
||||||
|
<FormField
|
||||||
|
id="project-scm-refspec"
|
||||||
|
label={i18n._(t`SCM Refspec`)}
|
||||||
|
name="scm_refspec"
|
||||||
|
type="text"
|
||||||
|
tooltipMaxWidth="400px"
|
||||||
|
tooltip={
|
||||||
|
<span>
|
||||||
|
{i18n._(t`A refspec to fetch (passed to the Ansible git
|
||||||
|
module). This parameter allows access to references via
|
||||||
|
the branch field not otherwise available.`)}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{i18n._(t`Note: This field assumes the remote name is "origin".`)}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{i18n._(t`Examples include:`)}
|
||||||
|
<ul css={{ margin: '10px 0 10px 20px' }}>
|
||||||
|
<li>refs/*:refs/remotes/origin/*</li>
|
||||||
|
<li>refs/pull/62/head:refs/remotes/origin/pull/62/head</li>
|
||||||
|
</ul>
|
||||||
|
{i18n._(t`The first fetches all references. The second
|
||||||
|
fetches the Github pull request number 62, in this example
|
||||||
|
the branch needs to be "pull/62/head".`)}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{i18n._(t`For more information, refer to the`)}{' '}
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href="https://docs.ansible.com/ansible-tower/latest/html/userguide/projects.html#manage-playbooks-using-source-control"
|
||||||
|
>
|
||||||
|
{i18n._(t`Ansible Tower Documentation.`)}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<ScmCredentialFormField
|
||||||
|
setScmCredential={setScmCredential}
|
||||||
|
scmCredential={scmCredential}
|
||||||
|
/>
|
||||||
|
<ScmTypeOptions scmUpdateOnLaunch={scmUpdateOnLaunch} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default withI18n()(GitSubForm);
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { withI18n } from '@lingui/react';
|
||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import {
|
||||||
|
UrlFormField,
|
||||||
|
BranchFormField,
|
||||||
|
ScmCredentialFormField,
|
||||||
|
ScmTypeOptions,
|
||||||
|
} from './SharedFields';
|
||||||
|
|
||||||
|
const HgSubForm = ({
|
||||||
|
i18n,
|
||||||
|
scmCredential,
|
||||||
|
setScmCredential,
|
||||||
|
scmUpdateOnLaunch,
|
||||||
|
}) => (
|
||||||
|
<>
|
||||||
|
<UrlFormField
|
||||||
|
i18n={i18n}
|
||||||
|
tooltip={
|
||||||
|
<span>
|
||||||
|
{i18n._(t`Example URLs for Mercurial SCM include:`)}
|
||||||
|
<ul css={{ margin: '10px 0 10px 20px' }}>
|
||||||
|
<li>https://bitbucket.org/username/project</li>
|
||||||
|
<li>ssh://hg@bitbucket.org/username/project</li>
|
||||||
|
<li>ssh://server.example.com/path</li>
|
||||||
|
</ul>
|
||||||
|
{i18n._(t`Note: Mercurial does not support password authentication
|
||||||
|
for SSH. Do not put the username and key in the URL. If using
|
||||||
|
Bitbucket and SSH, do not supply your Bitbucket username.
|
||||||
|
`)}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<BranchFormField i18n={i18n} label={i18n._(t`SCM Branch/Tag/Revision`)} />
|
||||||
|
<ScmCredentialFormField
|
||||||
|
setScmCredential={setScmCredential}
|
||||||
|
scmCredential={scmCredential}
|
||||||
|
/>
|
||||||
|
<ScmTypeOptions scmUpdateOnLaunch={scmUpdateOnLaunch} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default withI18n()(HgSubForm);
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { withI18n } from '@lingui/react';
|
||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { Field } from 'formik';
|
||||||
|
import CredentialLookup from '@components/Lookup/CredentialLookup';
|
||||||
|
import { required } from '@util/validators';
|
||||||
|
import { ScmTypeOptions } from './SharedFields';
|
||||||
|
|
||||||
|
const InsightsSubForm = ({
|
||||||
|
i18n,
|
||||||
|
setInsightsCredential,
|
||||||
|
insightsCredential,
|
||||||
|
scmUpdateOnLaunch,
|
||||||
|
}) => (
|
||||||
|
<>
|
||||||
|
<Field
|
||||||
|
name="credential"
|
||||||
|
validate={required(i18n._(t`Select a value for this field`), i18n)}
|
||||||
|
render={({ form }) => (
|
||||||
|
<CredentialLookup
|
||||||
|
credentialTypeId={insightsCredential.typeId}
|
||||||
|
label={i18n._(t`Insights Credential`)}
|
||||||
|
helperTextInvalid={form.errors.credential}
|
||||||
|
isValid={!form.touched.credential || !form.errors.credential}
|
||||||
|
onBlur={() => form.setFieldTouched('credential')}
|
||||||
|
onChange={credential => {
|
||||||
|
form.setFieldValue('credential', credential.id);
|
||||||
|
setInsightsCredential({
|
||||||
|
...insightsCredential,
|
||||||
|
value: credential,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
value={insightsCredential.value}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<ScmTypeOptions hideAllowOverride scmUpdateOnLaunch={scmUpdateOnLaunch} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default withI18n()(InsightsSubForm);
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { withI18n } from '@lingui/react';
|
||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { Field } from 'formik';
|
||||||
|
import CredentialLookup from '@components/Lookup/CredentialLookup';
|
||||||
|
import FormField, { CheckboxField } from '@components/FormField';
|
||||||
|
import { required } from '@util/validators';
|
||||||
|
import FormRow from '@components/FormRow';
|
||||||
|
import { FormGroup, Title } from '@patternfly/react-core';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const SubFormTitle = styled(Title)`
|
||||||
|
--pf-c-title--m-md--FontWeight: 700;
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const UrlFormField = withI18n()(({ i18n, tooltip }) => (
|
||||||
|
<FormField
|
||||||
|
id="project-scm-url"
|
||||||
|
isRequired
|
||||||
|
label={i18n._(t`SCM URL`)}
|
||||||
|
name="scm_url"
|
||||||
|
tooltip={tooltip}
|
||||||
|
tooltipMaxWidth="350px"
|
||||||
|
type="text"
|
||||||
|
validate={required(null, i18n)}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
|
||||||
|
export const BranchFormField = withI18n()(({ i18n, label }) => (
|
||||||
|
<FormField
|
||||||
|
id="project-scm-branch"
|
||||||
|
name="scm_branch"
|
||||||
|
type="text"
|
||||||
|
label={label}
|
||||||
|
tooltip={i18n._(t`Branch to checkout. In addition to branches,
|
||||||
|
you can input tags, commit hashes, and arbitrary refs. Some
|
||||||
|
commit hashes and refs may not be availble unless you also
|
||||||
|
provide a custom refspec.`)}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
|
||||||
|
export const ScmCredentialFormField = withI18n()(
|
||||||
|
({ i18n, setScmCredential, scmCredential }) => (
|
||||||
|
<Field
|
||||||
|
name="credential"
|
||||||
|
render={({ form }) => (
|
||||||
|
<CredentialLookup
|
||||||
|
credentialTypeId={scmCredential.typeId}
|
||||||
|
label={i18n._(t`SCM Credential`)}
|
||||||
|
value={scmCredential.value}
|
||||||
|
onChange={credential => {
|
||||||
|
form.setFieldValue('credential', credential.id);
|
||||||
|
setScmCredential({
|
||||||
|
...scmCredential,
|
||||||
|
value: credential,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
export const ScmTypeOptions = withI18n()(
|
||||||
|
({ i18n, scmUpdateOnLaunch, hideAllowOverride }) => (
|
||||||
|
<>
|
||||||
|
<FormGroup
|
||||||
|
css="grid-column: 1/-1"
|
||||||
|
fieldId="project-option-checkboxes"
|
||||||
|
label={i18n._(t`Options`)}
|
||||||
|
>
|
||||||
|
<FormRow>
|
||||||
|
<CheckboxField
|
||||||
|
id="option-scm-clean"
|
||||||
|
name="scm_clean"
|
||||||
|
label={i18n._(t`Clean`)}
|
||||||
|
tooltip={i18n._(
|
||||||
|
t`Remove any local modifications prior to performing an update.`
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<CheckboxField
|
||||||
|
id="option-scm-delete-on-update"
|
||||||
|
name="scm_delete_on_update"
|
||||||
|
label={i18n._(t`Delete`)}
|
||||||
|
tooltip={i18n._(
|
||||||
|
t`Delete the local repository in its entirety prior to
|
||||||
|
performing an update. Depending on the size of the
|
||||||
|
repository this may significantly increase the amount
|
||||||
|
of time required to complete an update.`
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<CheckboxField
|
||||||
|
id="option-scm-update-on-launch"
|
||||||
|
name="scm_update_on_launch"
|
||||||
|
label={i18n._(t`Update Revision on Launch`)}
|
||||||
|
tooltip={i18n._(
|
||||||
|
t`Each time a job runs using this project, update the
|
||||||
|
revision of the project prior to starting the job.`
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{!hideAllowOverride && (
|
||||||
|
<CheckboxField
|
||||||
|
id="option-allow-override"
|
||||||
|
name="allow_override"
|
||||||
|
label={i18n._(t`Allow Branch Override`)}
|
||||||
|
tooltip={i18n._(
|
||||||
|
t`Allow changing the SCM branch or revision in a job
|
||||||
|
template that uses this project.`
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</FormRow>
|
||||||
|
</FormGroup>
|
||||||
|
{scmUpdateOnLaunch && (
|
||||||
|
<>
|
||||||
|
<SubFormTitle size="md">{i18n._(t`Option Details`)}</SubFormTitle>
|
||||||
|
<FormField
|
||||||
|
id="project-cache-timeout"
|
||||||
|
name="scm_update_cache_timeout"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
label={i18n._(t`Cache Timeout`)}
|
||||||
|
tooltip={i18n._(t`Time in seconds to consider a project
|
||||||
|
to be current. During job runs and callbacks the task
|
||||||
|
system will evaluate the timestamp of the latest project
|
||||||
|
update. If it is older than Cache Timeout, it is not
|
||||||
|
considered current, and a new project update will be
|
||||||
|
performed.`)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
);
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { withI18n } from '@lingui/react';
|
||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import {
|
||||||
|
UrlFormField,
|
||||||
|
BranchFormField,
|
||||||
|
ScmCredentialFormField,
|
||||||
|
ScmTypeOptions,
|
||||||
|
} from './SharedFields';
|
||||||
|
|
||||||
|
const SvnSubForm = ({
|
||||||
|
i18n,
|
||||||
|
scmCredential,
|
||||||
|
setScmCredential,
|
||||||
|
scmUpdateOnLaunch,
|
||||||
|
}) => (
|
||||||
|
<>
|
||||||
|
<UrlFormField
|
||||||
|
i18n={i18n}
|
||||||
|
tooltip={
|
||||||
|
<span>
|
||||||
|
{i18n._(t`Example URLs for Subversion SCM include:`)}
|
||||||
|
<ul css={{ margin: '10px 0 10px 20px' }}>
|
||||||
|
<li>https://github.com/ansible/ansible</li>
|
||||||
|
<li>svn://servername.example.com/path</li>
|
||||||
|
<li>svn+ssh://servername.example.com/path</li>
|
||||||
|
</ul>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<BranchFormField i18n={i18n} label={i18n._(t`Revision #`)} />
|
||||||
|
<ScmCredentialFormField
|
||||||
|
setScmCredential={setScmCredential}
|
||||||
|
scmCredential={scmCredential}
|
||||||
|
/>
|
||||||
|
<ScmTypeOptions scmUpdateOnLaunch={scmUpdateOnLaunch} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default withI18n()(SvnSubForm);
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export { default as GitSubForm } from './GitSubForm';
|
||||||
|
export { default as HgSubForm } from './HgSubForm';
|
||||||
|
export { default as SvnSubForm } from './SvnSubForm';
|
||||||
|
export { default as InsightsSubForm } from './InsightsSubForm';
|
||||||
|
export { SubFormTitle } from './SharedFields';
|
||||||
Reference in New Issue
Block a user