Browse Source

SONAR-17527 Add main branch name during manual project creation

tags/9.8.0.63668
Guillaume Peoc'h 1 year ago
parent
commit
ef563f2a2e

+ 1
- 0
server/sonar-web/src/main/js/api/components.ts View File

export function createProject(data: { export function createProject(data: {
name: string; name: string;
project: string; project: string;
mainBranch: string;
visibility?: Visibility; visibility?: Visibility;
}): Promise<{ project: ProjectBase }> { }): Promise<{ project: ProjectBase }> {
return postJSON('/api/projects/create', data).catch(throwGlobalError); return postJSON('/api/projects/create', data).catch(throwGlobalError);

+ 86
- 10
server/sonar-web/src/main/js/apps/create/project/ManualProjectCreate.tsx View File

* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import classNames from 'classnames'; import classNames from 'classnames';
import { debounce } from 'lodash';
import { debounce, isEmpty } from 'lodash';
import * as React from 'react'; import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { createProject, doesComponentExists } from '../../../api/components'; import { createProject, doesComponentExists } from '../../../api/components';
import { getValue } from '../../../api/settings';
import DocLink from '../../../components/common/DocLink';
import ProjectKeyInput from '../../../components/common/ProjectKeyInput'; import ProjectKeyInput from '../../../components/common/ProjectKeyInput';
import { SubmitButton } from '../../../components/controls/buttons'; import { SubmitButton } from '../../../components/controls/buttons';
import ValidationInput from '../../../components/controls/ValidationInput'; import ValidationInput from '../../../components/controls/ValidationInput';
import { translate } from '../../../helpers/l10n'; import { translate } from '../../../helpers/l10n';
import { PROJECT_KEY_INVALID_CHARACTERS, validateProjectKey } from '../../../helpers/projects'; import { PROJECT_KEY_INVALID_CHARACTERS, validateProjectKey } from '../../../helpers/projects';
import { ProjectKeyValidationResult } from '../../../types/component'; import { ProjectKeyValidationResult } from '../../../types/component';
import { GlobalSettingKeys } from '../../../types/settings';
import { PROJECT_NAME_MAX_LEN } from './constants'; import { PROJECT_NAME_MAX_LEN } from './constants';
import CreateProjectPageHeader from './CreateProjectPageHeader'; import CreateProjectPageHeader from './CreateProjectPageHeader';
import './ManualProjectCreate.css'; import './ManualProjectCreate.css';
projectKeyError?: string; projectKeyError?: string;
projectKeyTouched: boolean; projectKeyTouched: boolean;
validatingProjectKey: boolean; validatingProjectKey: boolean;
mainBranchName: string;
mainBranchNameError?: string;
mainBranchNameTouched: boolean;
submitting: boolean; submitting: boolean;
} }


submitting: false, submitting: false,
projectKeyTouched: false, projectKeyTouched: false,
projectNameTouched: false, projectNameTouched: false,
mainBranchName: 'main',
mainBranchNameTouched: false,
validatingProjectKey: false validatingProjectKey: false
}; };
this.checkFreeKey = debounce(this.checkFreeKey, 250); this.checkFreeKey = debounce(this.checkFreeKey, 250);


componentDidMount() { componentDidMount() {
this.mounted = true; this.mounted = true;
this.fetchMainBranchName();
} }


componentWillUnmount() { componentWillUnmount() {
this.mounted = false; this.mounted = false;
} }


fetchMainBranchName = async () => {
const mainBranchName = await getValue({ key: GlobalSettingKeys.MainBranchName });

if (this.mounted && mainBranchName.value !== undefined) {
this.setState({ mainBranchName: mainBranchName.value });
}
};

checkFreeKey = (key: string) => { checkFreeKey = (key: string) => {
this.setState({ validatingProjectKey: true }); this.setState({ validatingProjectKey: true });


}; };


canSubmit(state: State): state is ValidState { canSubmit(state: State): state is ValidState {
const { projectKey, projectKeyError, projectName, projectNameError } = state;
const { projectKey, projectKeyError, projectName, projectNameError, mainBranchName } = state;
return Boolean( return Boolean(
projectKeyError === undefined && projectKeyError === undefined &&
projectNameError === undefined && projectNameError === undefined &&
projectKey.length > 0 &&
projectName.length > 0
!isEmpty(projectKey) &&
!isEmpty(projectName) &&
!isEmpty(mainBranchName)
); );
} }


handleFormSubmit = (event: React.FormEvent<HTMLFormElement>) => { handleFormSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault(); event.preventDefault();
const { state } = this;
if (this.canSubmit(state)) {
const { projectKey, projectName, mainBranchName } = this.state;
if (this.canSubmit(this.state)) {
this.setState({ submitting: true }); this.setState({ submitting: true });
createProject({ createProject({
project: state.projectKey,
name: (state.projectName || state.projectKey).trim()
project: projectKey,
name: (projectName || projectKey).trim(),
mainBranch: mainBranchName
}).then( }).then(
({ project }) => this.props.onProjectCreate(project.key), ({ project }) => this.props.onProjectCreate(project.key),
() => { () => {
); );
}; };


handleBranchNameChange = (mainBranchName: string, fromUI = false) => {
this.setState({
mainBranchName,
mainBranchNameError: this.validateMainBranchName(mainBranchName),
mainBranchNameTouched: fromUI
});
};

validateKey = (projectKey: string) => { validateKey = (projectKey: string) => {
const result = validateProjectKey(projectKey); const result = validateProjectKey(projectKey);
return result === ProjectKeyValidationResult.Valid return result === ProjectKeyValidationResult.Valid
}; };


validateName = (projectName: string) => { validateName = (projectName: string) => {
if (projectName.length === 0) {
if (isEmpty(projectName)) {
return translate('onboarding.create_project.display_name.error.empty'); return translate('onboarding.create_project.display_name.error.empty');
} }
return undefined; return undefined;
}; };


validateMainBranchName = (mainBranchName: string) => {
if (isEmpty(mainBranchName)) {
return translate('onboarding.create_project.main_branch_name.error.empty');
}
return undefined;
};

render() { render() {
const { const {
projectKey, projectKey,
projectNameError, projectNameError,
projectNameTouched, projectNameTouched,
validatingProjectKey, validatingProjectKey,
mainBranchName,
mainBranchNameError,
mainBranchNameTouched,
submitting submitting
} = this.state; } = this.state;
const { branchesEnabled } = this.props; const { branchesEnabled } = this.props;


const touched = !!(projectKeyTouched || projectNameTouched);
const touched = Boolean(projectKeyTouched || projectNameTouched);
const projectNameIsInvalid = projectNameTouched && projectNameError !== undefined; const projectNameIsInvalid = projectNameTouched && projectNameError !== undefined;
const projectNameIsValid = projectNameTouched && projectNameError === undefined; const projectNameIsValid = projectNameTouched && projectNameError === undefined;
const mainBranchNameIsValid = mainBranchNameTouched && mainBranchNameError === undefined;
const mainBranchNameIsInvalid = mainBranchNameTouched && mainBranchNameError !== undefined;


return ( return (
<> <>
validating={validatingProjectKey} validating={validatingProjectKey}
/> />


<ValidationInput
className="form-field"
description={
<FormattedMessage
id="onboarding.create_project.main_branch_name.description"
defaultMessage={translate(
'onboarding.create_project.main_branch_name.description'
)}
values={{
learn_more: (
<DocLink to="/project-administration/project-existence">
{translate('learn_more')}
</DocLink>
)
}}
/>
}
error={mainBranchNameError}
id="main-branch-name"
isInvalid={mainBranchNameIsInvalid}
isValid={mainBranchNameIsValid}
label={translate('onboarding.create_project.main_branch_name')}
required={true}>
<input
id="main-branch-name"
className={classNames('input-super-large', {
'is-invalid': mainBranchNameIsInvalid,
'is-valid': mainBranchNameIsValid
})}
minLength={1}
onChange={e => this.handleBranchNameChange(e.currentTarget.value, true)}
type="text"
value={mainBranchName}
/>
</ValidationInput>

<SubmitButton disabled={!this.canSubmit(this.state) || submitting}> <SubmitButton disabled={!this.canSubmit(this.state) || submitting}>
{translate('set_up')} {translate('set_up')}
</SubmitButton> </SubmitButton>

+ 21
- 4
server/sonar-web/src/main/js/apps/create/project/__tests__/ManualProjectCreate-test.tsx View File

.mockImplementation(({ component }) => Promise.resolve(component === 'exists')) .mockImplementation(({ component }) => Promise.resolve(component === 'exists'))
})); }));


jest.mock('../../../../api/settings', () => ({
getValue: jest.fn().mockResolvedValue({ value: 'main' })
}));

beforeEach(() => { beforeEach(() => {
jest.clearAllMocks(); jest.clearAllMocks();
}); });
).toHaveValue('This-is-not-a-key-'); ).toHaveValue('This-is-not-a-key-');


// Clear name // Clear name
await user.click(
await screen.findByRole('textbox', {
await user.clear(
screen.getByRole('textbox', {
name: 'onboarding.create_project.display_name field_required' name: 'onboarding.create_project.display_name field_required'
}) })
); );
await user.keyboard('{Control>}a{/Control}{Backspace}');
expect( expect(
screen.getByRole('textbox', { name: 'onboarding.create_project.project_key field_required' }) screen.getByRole('textbox', { name: 'onboarding.create_project.project_key field_required' })
).toHaveValue(''); ).toHaveValue('');
expect( expect(
await screen.findByText('onboarding.create_project.project_key.taken') await screen.findByText('onboarding.create_project.project_key.taken')
).toBeInTheDocument(); ).toBeInTheDocument();

// Invalid main branch name
await user.clear(
screen.getByRole('textbox', {
name: 'onboarding.create_project.main_branch_name field_required'
})
);
expect(
await screen.findByText('onboarding.create_project.main_branch_name.error.empty')
).toBeInTheDocument();
}); });


it('should submit form input', async () => { it('should submit form input', async () => {
); );
await user.keyboard('test'); await user.keyboard('test');
await user.click(screen.getByRole('button', { name: 'set_up' })); await user.click(screen.getByRole('button', { name: 'set_up' }));
expect(createProject).toHaveBeenCalledWith({ name: 'test', project: 'test' });
expect(createProject).toHaveBeenCalledWith({
name: 'test',
project: 'test',
mainBranch: 'main'
});
expect(onProjectCreate).toHaveBeenCalled(); expect(onProjectCreate).toHaveBeenCalled();
}); });



+ 36
- 6
server/sonar-web/src/main/js/apps/projectsManagement/CreateProjectForm.tsx View File

import * as React from 'react'; import * as React from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { createProject } from '../../api/components'; import { createProject } from '../../api/components';
import { getValue } from '../../api/settings';
import Link from '../../components/common/Link'; import Link from '../../components/common/Link';
import VisibilitySelector from '../../components/common/VisibilitySelector'; import VisibilitySelector from '../../components/common/VisibilitySelector';
import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons'; import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons';
import MandatoryFieldsExplanation from '../../components/ui/MandatoryFieldsExplanation'; import MandatoryFieldsExplanation from '../../components/ui/MandatoryFieldsExplanation';
import { translate } from '../../helpers/l10n'; import { translate } from '../../helpers/l10n';
import { getProjectUrl } from '../../helpers/urls'; import { getProjectUrl } from '../../helpers/urls';
import { GlobalSettingKeys } from '../../types/settings';
import { Visibility } from '../../types/types'; import { Visibility } from '../../types/types';


interface Props { interface Props {
visibility?: Visibility; visibility?: Visibility;
// add index declaration to be able to do `this.setState({ [name]: value });` // add index declaration to be able to do `this.setState({ [name]: value });`
[x: string]: any; [x: string]: any;
mainBranchName: string;
} }


export default class CreateProjectForm extends React.PureComponent<Props, State> { export default class CreateProjectForm extends React.PureComponent<Props, State> {
key: '', key: '',
loading: false, loading: false,
name: '', name: '',
visibility: props.defaultProjectVisibility
visibility: props.defaultProjectVisibility,
mainBranchName: 'main'
}; };
} }


componentDidMount() { componentDidMount() {
this.mounted = true; this.mounted = true;
this.fetchMainBranchName();
} }


componentDidUpdate() { componentDidUpdate() {
this.mounted = false; this.mounted = false;
} }


fetchMainBranchName = async () => {
const mainBranchName = await getValue({ key: GlobalSettingKeys.MainBranchName });

if (this.mounted && mainBranchName.value !== undefined) {
this.setState({ mainBranchName: mainBranchName.value });
}
};

handleInputChange = (event: React.SyntheticEvent<HTMLInputElement>) => { handleInputChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
const { name, value } = event.currentTarget; const { name, value } = event.currentTarget;
this.setState({ [name]: value }); this.setState({ [name]: value });


handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => { handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
event.preventDefault(); event.preventDefault();
const { name, key, mainBranchName, visibility } = this.state;


const data = { const data = {
name: this.state.name,
project: this.state.key,
visibility: this.state.visibility
name,
project: key,
mainBranch: mainBranchName,
visibility
}; };


this.setState({ loading: true }); this.setState({ loading: true });
<MandatoryFieldsExplanation className="modal-field" /> <MandatoryFieldsExplanation className="modal-field" />
<div className="modal-field"> <div className="modal-field">
<label htmlFor="create-project-name"> <label htmlFor="create-project-name">
{translate('name')}
{translate('onboarding.create_project.display_name')}
<MandatoryFieldMarker /> <MandatoryFieldMarker />
</label> </label>
<input <input
</div> </div>
<div className="modal-field"> <div className="modal-field">
<label htmlFor="create-project-key"> <label htmlFor="create-project-key">
{translate('key')}
{translate('onboarding.create_project.project_key')}
<MandatoryFieldMarker /> <MandatoryFieldMarker />
</label> </label>
<input <input
value={this.state.key} value={this.state.key}
/> />
</div> </div>
<div className="modal-field">
<label htmlFor="create-project-main-branch-name">
{translate('onboarding.create_project.main_branch_name')}
<MandatoryFieldMarker />
</label>
<input
id="create-project-main-branch-name"
maxLength={400}
name="mainBranchName"
onChange={this.handleInputChange}
required={true}
type="text"
value={this.state.mainBranchName}
/>
</div>
<div className="modal-field"> <div className="modal-field">
<label>{translate('visibility')}</label> <label>{translate('visibility')}</label>
<VisibilitySelector <VisibilitySelector

+ 53
- 35
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/CreateProjectForm-test.tsx View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
/* eslint-disable import/first */
jest.mock('../../../api/components', () => ({
createProject: jest.fn(({ name }: { name: string }) =>
Promise.resolve({ project: { key: name, name } })
)
}));


import { shallow } from 'enzyme';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import * as React from 'react'; import * as React from 'react';
import { change, submit, waitAndUpdate } from '../../../helpers/testUtils';
import { createProject } from '../../../api/components';
import CreateProjectForm from '../CreateProjectForm'; import CreateProjectForm from '../CreateProjectForm';


const createProject = require('../../../api/components').createProject as jest.Mock<any>;
jest.mock('../../../api/components', () => ({
createProject: jest.fn().mockResolvedValue({}),
doesComponentExists: jest
.fn()
.mockImplementation(({ component }) => Promise.resolve(component === 'exists'))
}));

jest.mock('../../../api/settings', () => ({
getValue: jest.fn().mockResolvedValue({ value: 'main' })
}));

beforeEach(() => {
jest.clearAllMocks();
});

it('should render all inputs and create a project', async () => {
const user = userEvent.setup();
renderCreateProjectForm();


it('creates project', async () => {
const wrapper = shallow(
<CreateProjectForm
defaultProjectVisibility="public"
onClose={jest.fn()}
onProjectCreated={jest.fn()}
/>
await user.type(
screen.getByRole('textbox', {
name: 'onboarding.create_project.display_name field_required'
}),
'ProjectName'
); );
(wrapper.instance() as CreateProjectForm).mounted = true;
expect(wrapper).toMatchSnapshot();


change(wrapper.find('input[name="name"]'), 'name', {
currentTarget: { name: 'name', value: 'name' }
});
change(wrapper.find('input[name="key"]'), 'key', {
currentTarget: { name: 'key', value: 'key' }
});
wrapper.find('VisibilitySelector').prop<Function>('onChange')('private');
wrapper.update();
expect(wrapper).toMatchSnapshot();
await user.type(
screen.getByRole('textbox', {
name: 'onboarding.create_project.project_key field_required'
}),
'ProjectKey'
);

expect(
screen.getByRole('textbox', {
name: 'onboarding.create_project.main_branch_name field_required'
})
).toHaveValue('main');


submit(wrapper.find('form'));
await user.type(
screen.getByRole('textbox', {
name: 'onboarding.create_project.main_branch_name field_required'
}),
'{Control>}a{/Control}{Backspace}ProjectMainBranch'
);

await user.click(screen.getByRole('button', { name: 'create' }));
expect(createProject).toHaveBeenCalledWith({ expect(createProject).toHaveBeenCalledWith({
name: 'name',
project: 'key',
visibility: 'private'
name: 'ProjectName',
project: 'ProjectKey',
mainBranch: 'ProjectMainBranch'
}); });
expect(wrapper).toMatchSnapshot();

await waitAndUpdate(wrapper);
expect(wrapper).toMatchSnapshot();
}); });

function renderCreateProjectForm(props: Partial<CreateProjectForm['props']> = {}) {
render(<CreateProjectForm onClose={jest.fn()} onProjectCreated={jest.fn()} {...props} />);
}

+ 0
- 343
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap View File

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`creates project 1`] = `
<Modal
contentLabel="modal form"
onRequestClose={[MockFunction]}
>
<form
id="create-project-form"
onSubmit={[Function]}
>
<header
className="modal-head"
>
<h2>
qualifiers.create.TRK
</h2>
</header>
<div
className="modal-body"
>
<MandatoryFieldsExplanation
className="modal-field"
/>
<div
className="modal-field"
>
<label
htmlFor="create-project-name"
>
name
<MandatoryFieldMarker />
</label>
<input
autoFocus={true}
id="create-project-name"
maxLength={2000}
name="name"
onChange={[Function]}
required={true}
type="text"
value=""
/>
</div>
<div
className="modal-field"
>
<label
htmlFor="create-project-key"
>
key
<MandatoryFieldMarker />
</label>
<input
id="create-project-key"
maxLength={400}
name="key"
onChange={[Function]}
required={true}
type="text"
value=""
/>
</div>
<div
className="modal-field"
>
<label>
visibility
</label>
<VisibilitySelector
canTurnToPrivate={true}
className="little-spacer-top"
onChange={[Function]}
visibility="public"
/>
</div>
</div>
<footer
className="modal-foot"
>
<SubmitButton
disabled={false}
id="create-project-submit"
>
create
</SubmitButton>
<ResetButtonLink
id="create-project-cancel"
onClick={[MockFunction]}
>
cancel
</ResetButtonLink>
</footer>
</form>
</Modal>
`;

exports[`creates project 2`] = `
<Modal
contentLabel="modal form"
onRequestClose={[MockFunction]}
>
<form
id="create-project-form"
onSubmit={[Function]}
>
<header
className="modal-head"
>
<h2>
qualifiers.create.TRK
</h2>
</header>
<div
className="modal-body"
>
<MandatoryFieldsExplanation
className="modal-field"
/>
<div
className="modal-field"
>
<label
htmlFor="create-project-name"
>
name
<MandatoryFieldMarker />
</label>
<input
autoFocus={true}
id="create-project-name"
maxLength={2000}
name="name"
onChange={[Function]}
required={true}
type="text"
value="name"
/>
</div>
<div
className="modal-field"
>
<label
htmlFor="create-project-key"
>
key
<MandatoryFieldMarker />
</label>
<input
id="create-project-key"
maxLength={400}
name="key"
onChange={[Function]}
required={true}
type="text"
value="key"
/>
</div>
<div
className="modal-field"
>
<label>
visibility
</label>
<VisibilitySelector
canTurnToPrivate={true}
className="little-spacer-top"
onChange={[Function]}
visibility="private"
/>
</div>
</div>
<footer
className="modal-foot"
>
<SubmitButton
disabled={false}
id="create-project-submit"
>
create
</SubmitButton>
<ResetButtonLink
id="create-project-cancel"
onClick={[MockFunction]}
>
cancel
</ResetButtonLink>
</footer>
</form>
</Modal>
`;

exports[`creates project 3`] = `
<Modal
contentLabel="modal form"
onRequestClose={[MockFunction]}
>
<form
id="create-project-form"
onSubmit={[Function]}
>
<header
className="modal-head"
>
<h2>
qualifiers.create.TRK
</h2>
</header>
<div
className="modal-body"
>
<MandatoryFieldsExplanation
className="modal-field"
/>
<div
className="modal-field"
>
<label
htmlFor="create-project-name"
>
name
<MandatoryFieldMarker />
</label>
<input
autoFocus={true}
id="create-project-name"
maxLength={2000}
name="name"
onChange={[Function]}
required={true}
type="text"
value="name"
/>
</div>
<div
className="modal-field"
>
<label
htmlFor="create-project-key"
>
key
<MandatoryFieldMarker />
</label>
<input
id="create-project-key"
maxLength={400}
name="key"
onChange={[Function]}
required={true}
type="text"
value="key"
/>
</div>
<div
className="modal-field"
>
<label>
visibility
</label>
<VisibilitySelector
canTurnToPrivate={true}
className="little-spacer-top"
onChange={[Function]}
visibility="private"
/>
</div>
</div>
<footer
className="modal-foot"
>
<i
className="spinner spacer-right"
/>
<SubmitButton
disabled={true}
id="create-project-submit"
>
create
</SubmitButton>
<ResetButtonLink
id="create-project-cancel"
onClick={[MockFunction]}
>
cancel
</ResetButtonLink>
</footer>
</form>
</Modal>
`;

exports[`creates project 4`] = `
<Modal
contentLabel="modal form"
onRequestClose={[MockFunction]}
>
<div>
<header
className="modal-head"
>
<h2>
qualifiers.create.TRK
</h2>
</header>
<div
className="modal-body"
>
<Alert
variant="success"
>
<FormattedMessage
defaultMessage="projects_management.project_has_been_successfully_created"
id="projects_management.project_has_been_successfully_created"
values={
Object {
"project": <ForwardRef(Link)
to={
Object {
"pathname": "/dashboard",
"search": "?id=name",
}
}
>
name
</ForwardRef(Link)>,
}
}
/>
</Alert>
</div>
<footer
className="modal-foot"
>
<ResetButtonLink
id="create-project-close"
innerRef={[Function]}
onClick={[MockFunction]}
>
close
</ResetButtonLink>
</footer>
</div>
</Modal>
`;

+ 2
- 1
server/sonar-web/src/main/js/types/settings.ts View File

DeveloperAggregatedInfoDisabled = 'sonar.developerAggregatedInfo.disabled', DeveloperAggregatedInfoDisabled = 'sonar.developerAggregatedInfo.disabled',
UpdatecenterActivated = 'sonar.updatecenter.activate', UpdatecenterActivated = 'sonar.updatecenter.activate',
DisplayAnnouncementMessage = 'sonar.announcement.displayMessage', DisplayAnnouncementMessage = 'sonar.announcement.displayMessage',
AnnouncementMessage = 'sonar.announcement.message'
AnnouncementMessage = 'sonar.announcement.message',
MainBranchName = 'sonar.projectCreation.mainBranchName'
} }


export type SettingDefinitionAndValue = { export type SettingDefinitionAndValue = {

+ 5
- 0
sonar-core/src/main/resources/org/sonar/l10n/core.properties View File

onboarding.create_project.display_name=Project display name onboarding.create_project.display_name=Project display name
onboarding.create_project.display_name.error.empty=The display name is required. onboarding.create_project.display_name.error.empty=The display name is required.
onboarding.create_project.display_name.description=Up to 255 characters. Some scanners might override the value you provide. onboarding.create_project.display_name.description=Up to 255 characters. Some scanners might override the value you provide.

onboarding.create_project.main_branch_name=Main branch name
onboarding.create_project.main_branch_name.error.empty=The main branch name is required.
onboarding.create_project.main_branch_name.description=The name of your project’s default branch { learn_more }

onboarding.create_project.pr_decoration.information=Manually created projects won’t benefit from the features associated with DevOps Platforms integration unless you configure them in the project settings. onboarding.create_project.pr_decoration.information=Manually created projects won’t benefit from the features associated with DevOps Platforms integration unless you configure them in the project settings.
onboarding.create_project.repository_imported=Already set up onboarding.create_project.repository_imported=Already set up
onboarding.create_project.see_project=See the project onboarding.create_project.see_project=See the project

Loading…
Cancel
Save