aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Davis <jeremy.davis@sonarsource.com>2020-07-22 10:57:38 +0200
committersonartech <sonartech@sonarsource.com>2020-08-17 20:06:22 +0000
commitc9460063e843eef0c94a442c3326c4d24d1a921b (patch)
treef7bdbc7734f6f085a9af3c8069d49363220268db
parent30d1c1a1e306b6e3ee0fe8d59df6f4f8d5640172 (diff)
downloadsonarqube-c9460063e843eef0c94a442c3326c4d24d1a921b.tar.gz
sonarqube-c9460063e843eef0c94a442c3326c4d24d1a921b.zip
SONAR-13628 Add PAT form for GitLab onboarding
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/GlobalNavPlus.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/BitbucketProjectCreateRenderer.tsx35
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/CreateProjectModeSelection.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx20
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/GitlabProjectCreate.tsx144
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/GitlabProjectCreateRenderer.tsx84
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/PersonalAccessTokenForm.tsx (renamed from server/sonar-web/src/main/js/apps/create/project/BitbucketPersonalAccessTokenForm.tsx)107
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/WrongBindingCountAlert.tsx66
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectModeSelection-test.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectPage-test.tsx7
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/GitlabProjectCreate-test.tsx116
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/GitlabProjectCreateRenderer-test.tsx51
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/PersonalAccessTokenForm-test.tsx (renamed from server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketPersonalAccessTokenForm-test.tsx)27
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/WrongBindingCountAlert-test.tsx34
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/BitbucketProjectCreateRenderer-test.tsx.snap43
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectModeSelection-test.tsx.snap97
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectPage-test.tsx.snap38
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitlabProjectCreate-test.tsx.snap18
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitlabProjectCreateRenderer-test.tsx.snap103
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/PersonalAccessTokenForm-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/BitbucketPersonalAccessTokenForm-test.tsx.snap)252
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/WrongBindingCountAlert-test.tsx.snap63
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/types.ts3
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties28
23 files changed, 1201 insertions, 144 deletions
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavPlus.tsx b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavPlus.tsx
index 0b58cdc9491..65eb8c75bfe 100644
--- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavPlus.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavPlus.tsx
@@ -47,7 +47,7 @@ interface State {
/*
* ALMs for which the import feature has been implemented
*/
-const IMPORT_COMPATIBLE_ALMS = [AlmKeys.Bitbucket, AlmKeys.GitHub];
+const IMPORT_COMPATIBLE_ALMS = [AlmKeys.Bitbucket, AlmKeys.GitHub, AlmKeys.GitLab];
export class GlobalNavPlus extends React.PureComponent<Props, State> {
mounted = false;
diff --git a/server/sonar-web/src/main/js/apps/create/project/BitbucketProjectCreateRenderer.tsx b/server/sonar-web/src/main/js/apps/create/project/BitbucketProjectCreateRenderer.tsx
index 878a11b609c..88eaf370202 100644
--- a/server/sonar-web/src/main/js/apps/create/project/BitbucketProjectCreateRenderer.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/BitbucketProjectCreateRenderer.tsx
@@ -18,10 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { FormattedMessage } from 'react-intl';
-import { Link } from 'react-router';
import { Button } from 'sonar-ui-common/components/controls/buttons';
-import { Alert } from 'sonar-ui-common/components/ui/Alert';
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
import { translate } from 'sonar-ui-common/helpers/l10n';
import { getBaseUrl } from 'sonar-ui-common/helpers/urls';
@@ -30,11 +27,11 @@ import {
BitbucketProjectRepositories,
BitbucketRepository
} from '../../../types/alm-integration';
-import { AlmSettingsInstance } from '../../../types/alm-settings';
-import { ALM_INTEGRATION } from '../../settings/components/AdditionalCategoryKeys';
+import { AlmKeys, AlmSettingsInstance } from '../../../types/alm-settings';
import BitbucketImportRepositoryForm from './BitbucketImportRepositoryForm';
-import BitbucketPersonalAccessTokenForm from './BitbucketPersonalAccessTokenForm';
import CreateProjectPageHeader from './CreateProjectPageHeader';
+import PersonalAccessTokenForm from './PersonalAccessTokenForm';
+import WrongBindingCountAlert from './WrongBindingCountAlert';
export interface BitbucketProjectCreateRendererProps {
bitbucketSetting?: AlmSettingsInstance;
@@ -104,34 +101,14 @@ export default function BitbucketProjectCreateRenderer(props: BitbucketProjectCr
{loading && <i className="spinner" />}
{!loading && !bitbucketSetting && (
- <Alert variant="error">
- {canAdmin ? (
- <FormattedMessage
- defaultMessage={translate('onboarding.create_project.no_bbs_binding.admin')}
- id="onboarding.create_project.no_bbs_binding.admin"
- values={{
- url: (
- <Link
- to={{
- pathname: '/admin/settings',
- query: { category: ALM_INTEGRATION }
- }}>
- {translate('settings.page')}
- </Link>
- )
- }}
- />
- ) : (
- translate('onboarding.create_project.no_bbs_binding')
- )}
- </Alert>
+ <WrongBindingCountAlert alm={AlmKeys.Bitbucket} canAdmin={!!canAdmin} />
)}
{!loading &&
bitbucketSetting &&
(showPersonalAccessTokenForm ? (
- <BitbucketPersonalAccessTokenForm
- bitbucketSetting={bitbucketSetting}
+ <PersonalAccessTokenForm
+ almSetting={bitbucketSetting}
onPersonalAccessTokenCreate={props.onPersonalAccessTokenCreate}
submitting={submittingToken}
validationFailed={tokenValidationFailed}
diff --git a/server/sonar-web/src/main/js/apps/create/project/CreateProjectModeSelection.tsx b/server/sonar-web/src/main/js/apps/create/project/CreateProjectModeSelection.tsx
index 43229c38eef..4508e2762c2 100644
--- a/server/sonar-web/src/main/js/apps/create/project/CreateProjectModeSelection.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/CreateProjectModeSelection.tsx
@@ -44,7 +44,7 @@ function renderAlmOption(
return (
<button
className={classNames(
- 'button button-huge big-spacer-left display-flex-column create-project-mode-type-bbs',
+ 'button button-huge big-spacer-left display-flex-column create-project-mode-type-alm',
{ disabled }
)}
disabled={disabled}
@@ -114,6 +114,7 @@ export default function CreateProjectModeSelection(props: CreateProjectModeSelec
{renderAlmOption(props, AlmKeys.Bitbucket, CreateProjectModes.BitbucketServer)}
{renderAlmOption(props, AlmKeys.GitHub, CreateProjectModes.GitHub)}
+ {renderAlmOption(props, AlmKeys.GitLab, CreateProjectModes.GitLab)}
</div>
</>
);
diff --git a/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx b/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx
index 3b4183beae4..5b79dd82e6b 100644
--- a/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx
@@ -30,6 +30,7 @@ import { AlmKeys, AlmSettingsInstance } from '../../../types/alm-settings';
import BitbucketProjectCreate from './BitbucketProjectCreate';
import CreateProjectModeSelection from './CreateProjectModeSelection';
import GitHubProjectCreate from './GitHubProjectCreate';
+import GitlabProjectCreate from './GitlabProjectCreate';
import ManualProjectCreate from './ManualProjectCreate';
import './style.css';
import { CreateProjectModes } from './types';
@@ -42,12 +43,13 @@ interface Props extends Pick<WithRouterProps, 'router' | 'location'> {
interface State {
bitbucketSettings: AlmSettingsInstance[];
githubSettings: AlmSettingsInstance[];
+ gitlabSettings: AlmSettingsInstance[];
loading: boolean;
}
export class CreateProjectPage extends React.PureComponent<Props, State> {
mounted = false;
- state: State = { bitbucketSettings: [], githubSettings: [], loading: true };
+ state: State = { bitbucketSettings: [], githubSettings: [], gitlabSettings: [], loading: true };
componentDidMount() {
const {
@@ -71,6 +73,7 @@ export class CreateProjectPage extends React.PureComponent<Props, State> {
this.setState({
bitbucketSettings: almSettings.filter(s => s.alm === AlmKeys.Bitbucket),
githubSettings: almSettings.filter(s => s.alm === AlmKeys.GitHub),
+ gitlabSettings: almSettings.filter(s => s.alm === AlmKeys.GitLab),
loading: false
});
}
@@ -102,7 +105,7 @@ export class CreateProjectPage extends React.PureComponent<Props, State> {
location,
router
} = this.props;
- const { bitbucketSettings, githubSettings, loading } = this.state;
+ const { bitbucketSettings, githubSettings, gitlabSettings, loading } = this.state;
switch (mode) {
case CreateProjectModes.BitbucketServer: {
@@ -128,6 +131,17 @@ export class CreateProjectPage extends React.PureComponent<Props, State> {
/>
);
}
+ case CreateProjectModes.GitLab: {
+ return (
+ <GitlabProjectCreate
+ canAdmin={!!canAdmin}
+ loadingBindings={loading}
+ location={location}
+ onProjectCreate={this.handleProjectCreate}
+ settings={gitlabSettings}
+ />
+ );
+ }
case CreateProjectModes.Manual: {
return <ManualProjectCreate onProjectCreate={this.handleProjectCreate} />;
}
@@ -136,7 +150,7 @@ export class CreateProjectPage extends React.PureComponent<Props, State> {
[AlmKeys.Azure]: 0,
[AlmKeys.Bitbucket]: bitbucketSettings.length,
[AlmKeys.GitHub]: githubSettings.length,
- [AlmKeys.GitLab]: 0
+ [AlmKeys.GitLab]: gitlabSettings.length
};
return (
<CreateProjectModeSelection
diff --git a/server/sonar-web/src/main/js/apps/create/project/GitlabProjectCreate.tsx b/server/sonar-web/src/main/js/apps/create/project/GitlabProjectCreate.tsx
new file mode 100644
index 00000000000..33d05358658
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/project/GitlabProjectCreate.tsx
@@ -0,0 +1,144 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { WithRouterProps } from 'react-router';
+import {
+ checkPersonalAccessTokenIsValid,
+ setAlmPersonalAccessToken
+} from '../../../api/alm-integrations';
+import { AlmSettingsInstance } from '../../../types/alm-settings';
+import GitlabProjectCreateRenderer from './GitlabProjectCreateRenderer';
+
+interface Props extends Pick<WithRouterProps, 'location'> {
+ canAdmin: boolean;
+ loadingBindings: boolean;
+ onProjectCreate: (projectKeys: string[]) => void;
+ settings: AlmSettingsInstance[];
+}
+
+interface State {
+ loading: boolean;
+ submittingToken: boolean;
+ tokenIsValid: boolean;
+ tokenValidationFailed: boolean;
+ settings?: AlmSettingsInstance;
+}
+
+export default class GitlabProjectCreate extends React.PureComponent<Props, State> {
+ mounted = false;
+
+ constructor(props: Props) {
+ super(props);
+ this.state = {
+ loading: false,
+ tokenIsValid: false,
+ settings: props.settings.length === 1 ? props.settings[0] : undefined,
+ submittingToken: false,
+ tokenValidationFailed: false
+ };
+ }
+
+ componentDidMount() {
+ this.mounted = true;
+ this.fetchInitialData();
+ }
+
+ componentDidUpdate(prevProps: Props) {
+ if (prevProps.settings.length === 0 && this.props.settings.length > 0) {
+ this.setState(
+ { settings: this.props.settings.length === 1 ? this.props.settings[0] : undefined },
+ () => this.fetchInitialData()
+ );
+ }
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ fetchInitialData = async () => {
+ this.setState({ loading: true });
+
+ const tokenIsValid = await this.checkPersonalAccessToken();
+
+ if (this.mounted) {
+ this.setState({
+ tokenIsValid,
+ loading: false
+ });
+ }
+ };
+
+ checkPersonalAccessToken = () => {
+ const { settings } = this.state;
+
+ if (!settings) {
+ return Promise.resolve(false);
+ }
+
+ return checkPersonalAccessTokenIsValid(settings.key).catch(() => false);
+ };
+
+ handlePersonalAccessTokenCreate = (token: string) => {
+ const { settings } = this.state;
+
+ if (!settings || token.length < 1) {
+ return;
+ }
+
+ this.setState({ submittingToken: true, tokenValidationFailed: false });
+ setAlmPersonalAccessToken(settings.key, token)
+ .then(this.checkPersonalAccessToken)
+ .then(patIsValid => {
+ if (this.mounted) {
+ this.setState({
+ submittingToken: false,
+ tokenIsValid: patIsValid,
+ tokenValidationFailed: !patIsValid
+ });
+ if (patIsValid) {
+ this.fetchInitialData();
+ }
+ }
+ })
+ .catch(() => {
+ if (this.mounted) {
+ this.setState({ submittingToken: false });
+ }
+ });
+ };
+
+ render() {
+ const { canAdmin, loadingBindings, location } = this.props;
+ const { loading, tokenIsValid, settings, submittingToken, tokenValidationFailed } = this.state;
+
+ return (
+ <GitlabProjectCreateRenderer
+ settings={settings}
+ canAdmin={canAdmin}
+ loading={loading || loadingBindings}
+ onPersonalAccessTokenCreate={this.handlePersonalAccessTokenCreate}
+ showPersonalAccessTokenForm={!tokenIsValid || Boolean(location.query.resetPat)}
+ submittingToken={submittingToken}
+ tokenValidationFailed={tokenValidationFailed}
+ />
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/create/project/GitlabProjectCreateRenderer.tsx b/server/sonar-web/src/main/js/apps/create/project/GitlabProjectCreateRenderer.tsx
new file mode 100644
index 00000000000..90f47b85d01
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/project/GitlabProjectCreateRenderer.tsx
@@ -0,0 +1,84 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import { getBaseUrl } from 'sonar-ui-common/helpers/urls';
+import { AlmKeys, AlmSettingsInstance } from '../../../types/alm-settings';
+import CreateProjectPageHeader from './CreateProjectPageHeader';
+import PersonalAccessTokenForm from './PersonalAccessTokenForm';
+import WrongBindingCountAlert from './WrongBindingCountAlert';
+
+export interface GitlabProjectCreateRendererProps {
+ canAdmin?: boolean;
+ loading: boolean;
+ onPersonalAccessTokenCreate: (pat: string) => void;
+ settings?: AlmSettingsInstance;
+ showPersonalAccessTokenForm?: boolean;
+ submittingToken?: boolean;
+ tokenValidationFailed: boolean;
+}
+
+export default function GitlabProjectCreateRenderer(props: GitlabProjectCreateRendererProps) {
+ const {
+ canAdmin,
+ loading,
+ settings,
+ showPersonalAccessTokenForm,
+ submittingToken,
+ tokenValidationFailed
+ } = props;
+
+ return (
+ <>
+ <CreateProjectPageHeader
+ title={
+ <span className="text-middle">
+ <img
+ alt="" // Should be ignored by screen readers
+ className="spacer-right"
+ height="24"
+ src={`${getBaseUrl()}/images/alm/gitlab.svg`}
+ />
+ {translate('onboarding.create_project.gitlab.title')}
+ </span>
+ }
+ />
+
+ {loading && <i className="spinner" />}
+
+ {!loading && !settings && (
+ <WrongBindingCountAlert alm={AlmKeys.GitLab} canAdmin={!!canAdmin} />
+ )}
+
+ {!loading &&
+ settings &&
+ (showPersonalAccessTokenForm ? (
+ <PersonalAccessTokenForm
+ almSetting={settings}
+ onPersonalAccessTokenCreate={props.onPersonalAccessTokenCreate}
+ submitting={submittingToken}
+ validationFailed={tokenValidationFailed}
+ />
+ ) : (
+ <div>Token is valid!</div>
+ ))}
+ </>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/create/project/BitbucketPersonalAccessTokenForm.tsx b/server/sonar-web/src/main/js/apps/create/project/PersonalAccessTokenForm.tsx
index 4f6826be68a..2a0342a5d33 100644
--- a/server/sonar-web/src/main/js/apps/create/project/BitbucketPersonalAccessTokenForm.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/PersonalAccessTokenForm.tsx
@@ -26,20 +26,29 @@ import { Alert } from 'sonar-ui-common/components/ui/Alert';
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
import { translate } from 'sonar-ui-common/helpers/l10n';
import { getBaseUrl } from 'sonar-ui-common/helpers/urls';
-import { AlmSettingsInstance } from '../../../types/alm-settings';
+import { AlmKeys, AlmSettingsInstance } from '../../../types/alm-settings';
-export interface BitbucketPersonalAccessTokenFormProps {
- bitbucketSetting: AlmSettingsInstance;
+export interface PersonalAccessTokenFormProps {
+ almSetting: AlmSettingsInstance;
onPersonalAccessTokenCreate: (token: string) => void;
submitting?: boolean;
validationFailed: boolean;
}
-export default function BitbucketPersonalAccessTokenForm(
- props: BitbucketPersonalAccessTokenFormProps
-) {
+function getPatUrl(alm: AlmKeys, url: string) {
+ if (alm === AlmKeys.Bitbucket) {
+ return `${url.replace(/\/$/, '')}/plugins/servlet/access-tokens/add`;
+ } else {
+ // GitLab
+ return url.endsWith('/api/v4')
+ ? `${url.replace('/api/v4', '').replace(/\/$/, '')}/profile/personal_access_tokens`
+ : 'https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#creating-a-personal-access-token';
+ }
+}
+
+export default function PersonalAccessTokenForm(props: PersonalAccessTokenFormProps) {
const {
- bitbucketSetting: { url },
+ almSetting: { alm, url },
submitting = false,
validationFailed
} = props;
@@ -59,9 +68,9 @@ export default function BitbucketPersonalAccessTokenForm(
const value = new FormData(e.currentTarget).get('personal_access_token') as string;
props.onPersonalAccessTokenCreate(value);
}}>
- <h2 className="big">{translate('onboarding.create_project.grant_access_to_bbs.title')}</h2>
+ <h2 className="big">{translate('onboarding.create_project.pat_form.title', alm)}</h2>
<p className="big-spacer-top big-spacer-bottom">
- {translate('onboarding.create_project.grant_access_to_bbs.help')}
+ {translate('onboarding.create_project.pat_form.help', alm)}
</p>
<ValidationInput
@@ -96,7 +105,11 @@ export default function BitbucketPersonalAccessTokenForm(
<h3>{translate('onboarding.create_project.pat_help.title')}</h3>
<p className="big-spacer-top big-spacer-bottom">
- {translate('onboarding.create_project.pat_help.bbs_help_1')}
+ <FormattedMessage
+ id="onboarding.create_project.pat_help.instructions"
+ defaultMessage={translate('onboarding.create_project.pat_help.instructions')}
+ values={{ alm: translate('onboarding.alm', alm) }}
+ />
</p>
{url && (
@@ -105,46 +118,60 @@ export default function BitbucketPersonalAccessTokenForm(
alt="" // Should be ignored by screen readers
className="spacer-right"
height="16"
- src={`${getBaseUrl()}/images/alm/bitbucket.svg`}
+ src={`${getBaseUrl()}/images/alm/${alm}.svg`}
/>
- <a
- href={`${url.replace(/\/$/, '')}/plugins/servlet/access-tokens/add`}
- rel="noopener noreferrer"
- target="_blank">
+ <a href={getPatUrl(alm, url)} rel="noopener noreferrer" target="_blank">
{translate('onboarding.create_project.pat_help.link')}
</a>
</div>
)}
<p className="big-spacer-top big-spacer-bottom">
- {translate('onboarding.create_project.pat_help.bbs_help_2')}
+ {translate('onboarding.create_project.pat_help.instructions2', alm)}
</p>
<ul>
- <li>
- <FormattedMessage
- defaultMessage={translate(
- 'onboarding.create_project.pat_help.bbs_permission_projects'
- )}
- id="onboarding.create_project.pat_help.bbs_permission_projects"
- values={{
- perm: (
- <strong>{translate('onboarding.create_project.pat_help.read_permission')}</strong>
- )
- }}
- />
- </li>
- <li>
- <FormattedMessage
- defaultMessage={translate('onboarding.create_project.pat_help.bbs_permission_repos')}
- id="onboarding.create_project.pat_help.bbs_permission_repos"
- values={{
- perm: (
- <strong>{translate('onboarding.create_project.pat_help.read_permission')}</strong>
- )
- }}
- />
- </li>
+ {alm === AlmKeys.Bitbucket && (
+ <>
+ <li>
+ <FormattedMessage
+ defaultMessage={translate(
+ 'onboarding.create_project.pat_help.bbs_permission_projects'
+ )}
+ id="onboarding.create_project.pat_help.bbs_permission_projects"
+ values={{
+ perm: (
+ <strong>
+ {translate('onboarding.create_project.pat_help.read_permission')}
+ </strong>
+ )
+ }}
+ />
+ </li>
+ <li>
+ <FormattedMessage
+ defaultMessage={translate(
+ 'onboarding.create_project.pat_help.bbs_permission_repos'
+ )}
+ id="onboarding.create_project.pat_help.bbs_permission_repos"
+ values={{
+ perm: (
+ <strong>
+ {translate('onboarding.create_project.pat_help.read_permission')}
+ </strong>
+ )
+ }}
+ />
+ </li>
+ </>
+ )}
+ {alm === AlmKeys.GitLab && (
+ <li className="spacer-bottom">
+ <strong>
+ {translate('onboarding.create_project.pat_help.gitlab.read_api_permission')}
+ </strong>
+ </li>
+ )}
</ul>
</Alert>
</div>
diff --git a/server/sonar-web/src/main/js/apps/create/project/WrongBindingCountAlert.tsx b/server/sonar-web/src/main/js/apps/create/project/WrongBindingCountAlert.tsx
new file mode 100644
index 00000000000..afab4980052
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/project/WrongBindingCountAlert.tsx
@@ -0,0 +1,66 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
+import { Link } from 'react-router';
+import { Alert } from 'sonar-ui-common/components/ui/Alert';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import { AlmKeys } from '../../../types/alm-settings';
+import { ALM_INTEGRATION } from '../../settings/components/AdditionalCategoryKeys';
+
+export interface WrongBindingCountAlertProps {
+ alm: AlmKeys;
+ canAdmin: boolean;
+}
+
+export default function WrongBindingCountAlert(props: WrongBindingCountAlertProps) {
+ const { alm, canAdmin } = props;
+
+ return (
+ <Alert variant="error">
+ {canAdmin ? (
+ <FormattedMessage
+ defaultMessage={translate('onboarding.create_project.wrong_binding_count.admin')}
+ id="onboarding.create_project.wrong_binding_count.admin"
+ values={{
+ alm: translate('onboarding.alm', alm),
+ url: (
+ <Link
+ to={{
+ pathname: '/admin/settings',
+ query: { category: ALM_INTEGRATION }
+ }}>
+ {translate('settings.page')}
+ </Link>
+ )
+ }}
+ />
+ ) : (
+ <FormattedMessage
+ defaultMessage={translate('onboarding.create_project.wrong_binding_count')}
+ id="onboarding.create_project.wrong_binding_count"
+ values={{
+ alm: translate('onboarding.alm', alm)
+ }}
+ />
+ )}
+ </Alert>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectModeSelection-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectModeSelection-test.tsx
index 2e11297a12d..6d0717eecaf 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectModeSelection-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectModeSelection-test.tsx
@@ -42,10 +42,10 @@ it('should correctly pass the selected mode up', () => {
click(wrapper.find('button.create-project-mode-type-manual'));
expect(onSelectMode).toBeCalledWith(CreateProjectModes.Manual);
- click(wrapper.find('button.create-project-mode-type-bbs').at(0));
+ click(wrapper.find('button.create-project-mode-type-alm').at(0));
expect(onSelectMode).toBeCalledWith(CreateProjectModes.BitbucketServer);
- click(wrapper.find('button.create-project-mode-type-bbs').at(1));
+ click(wrapper.find('button.create-project-mode-type-alm').at(1));
expect(onSelectMode).toBeCalledWith(CreateProjectModes.GitHub);
});
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectPage-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectPage-test.tsx
index 8368b8eb386..e2d16dd4ed7 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectPage-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectPage-test.tsx
@@ -65,6 +65,13 @@ it('should render correctly if the GitHub method is selected', () => {
expect(wrapper).toMatchSnapshot();
});
+it('should render correctly if the GitLab method is selected', () => {
+ const wrapper = shallowRender({
+ location: mockLocation({ query: { mode: CreateProjectModes.GitLab } })
+ });
+ expect(wrapper).toMatchSnapshot();
+});
+
function shallowRender(props: Partial<CreateProjectPage['props']> = {}) {
return shallow<CreateProjectPage>(
<CreateProjectPage
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/GitlabProjectCreate-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/GitlabProjectCreate-test.tsx
new file mode 100644
index 00000000000..da88c061d27
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/GitlabProjectCreate-test.tsx
@@ -0,0 +1,116 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
+import {
+ checkPersonalAccessTokenIsValid,
+ setAlmPersonalAccessToken
+} from '../../../../api/alm-integrations';
+import { mockAlmSettingsInstance } from '../../../../helpers/mocks/alm-settings';
+import { mockLocation } from '../../../../helpers/testMocks';
+import { AlmKeys } from '../../../../types/alm-settings';
+import GitlabProjectCreate from '../GitlabProjectCreate';
+
+jest.mock('../../../../api/alm-integrations', () => ({
+ checkPersonalAccessTokenIsValid: jest.fn().mockResolvedValue(true),
+ setAlmPersonalAccessToken: jest.fn().mockResolvedValue(null)
+}));
+
+beforeEach(jest.clearAllMocks);
+
+const almSettingKey = 'gitlab-setting';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot();
+});
+
+it('should correctly check PAT on mount', async () => {
+ const wrapper = shallowRender();
+ await waitAndUpdate(wrapper);
+ expect(checkPersonalAccessTokenIsValid).toBeCalledWith(almSettingKey);
+});
+
+it('should correctly check PAT when settings are added after mount', async () => {
+ const wrapper = shallowRender({ settings: [] });
+ await waitAndUpdate(wrapper);
+
+ wrapper.setProps({
+ settings: [mockAlmSettingsInstance({ alm: AlmKeys.GitLab, key: 'otherKey' })]
+ });
+
+ expect(checkPersonalAccessTokenIsValid).toBeCalledWith('otherKey');
+});
+
+it('should correctly handle a valid PAT', async () => {
+ (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce(true);
+ const wrapper = shallowRender();
+ await waitAndUpdate(wrapper);
+ expect(wrapper.state().tokenIsValid).toBe(true);
+});
+
+it('should correctly handle an invalid PAT', async () => {
+ (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce(false);
+ const wrapper = shallowRender();
+ await waitAndUpdate(wrapper);
+ expect(wrapper.state().tokenIsValid).toBe(false);
+});
+
+describe('setting a new PAT', () => {
+ const wrapper = shallowRender();
+
+ it('should correctly handle it if invalid', async () => {
+ (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce(false);
+
+ wrapper.instance().handlePersonalAccessTokenCreate('invalidtoken');
+ expect(setAlmPersonalAccessToken).toBeCalledWith(almSettingKey, 'invalidtoken');
+ expect(wrapper.state().submittingToken).toBe(true);
+ await waitAndUpdate(wrapper);
+ expect(checkPersonalAccessTokenIsValid).toBeCalled();
+ expect(wrapper.state().submittingToken).toBe(false);
+ expect(wrapper.state().tokenValidationFailed).toBe(true);
+ });
+
+ it('should correctly handle it if valid', async () => {
+ (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce(true);
+
+ wrapper.instance().handlePersonalAccessTokenCreate('validtoken');
+ expect(setAlmPersonalAccessToken).toBeCalledWith(almSettingKey, 'validtoken');
+ expect(wrapper.state().submittingToken).toBe(true);
+ await waitAndUpdate(wrapper);
+ expect(checkPersonalAccessTokenIsValid).toBeCalled();
+ expect(wrapper.state().submittingToken).toBe(false);
+ expect(wrapper.state().tokenValidationFailed).toBe(false);
+ });
+});
+
+function shallowRender(props: Partial<GitlabProjectCreate['props']> = {}) {
+ return shallow<GitlabProjectCreate>(
+ <GitlabProjectCreate
+ canAdmin={false}
+ loadingBindings={false}
+ location={mockLocation()}
+ onProjectCreate={jest.fn()}
+ settings={[mockAlmSettingsInstance({ alm: AlmKeys.GitLab, key: almSettingKey })]}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/GitlabProjectCreateRenderer-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/GitlabProjectCreateRenderer-test.tsx
new file mode 100644
index 00000000000..c824f5ea041
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/GitlabProjectCreateRenderer-test.tsx
@@ -0,0 +1,51 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { mockAlmSettingsInstance } from '../../../../helpers/mocks/alm-settings';
+import { AlmKeys } from '../../../../types/alm-settings';
+import GitlabProjectCreateRenderer, {
+ GitlabProjectCreateRendererProps
+} from '../GitlabProjectCreateRenderer';
+
+it('should render correctly', () => {
+ expect(shallowRender({ loading: true })).toMatchSnapshot('loading');
+ expect(shallowRender({ settings: undefined })).toMatchSnapshot('invalid settings');
+ expect(shallowRender({ canAdmin: true, settings: undefined })).toMatchSnapshot(
+ 'invalid settings, admin user'
+ );
+ expect(shallowRender()).toMatchSnapshot('pat form');
+});
+
+function shallowRender(props: Partial<GitlabProjectCreateRendererProps> = {}) {
+ return shallow<GitlabProjectCreateRendererProps>(
+ <GitlabProjectCreateRenderer
+ canAdmin={false}
+ loading={false}
+ onPersonalAccessTokenCreate={jest.fn()}
+ showPersonalAccessTokenForm={true}
+ submittingToken={false}
+ tokenValidationFailed={false}
+ settings={mockAlmSettingsInstance({ alm: AlmKeys.GitLab })}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketPersonalAccessTokenForm-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/PersonalAccessTokenForm-test.tsx
index 570833989cd..e096aed245d 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketPersonalAccessTokenForm-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/PersonalAccessTokenForm-test.tsx
@@ -24,14 +24,25 @@ import { SubmitButton } from 'sonar-ui-common/components/controls/buttons';
import { change, submit } from 'sonar-ui-common/helpers/testUtils';
import { mockAlmSettingsInstance } from '../../../../helpers/mocks/alm-settings';
import { AlmKeys } from '../../../../types/alm-settings';
-import BitbucketPersonalAccessTokenForm, {
- BitbucketPersonalAccessTokenFormProps
-} from '../BitbucketPersonalAccessTokenForm';
+import PersonalAccessTokenForm, { PersonalAccessTokenFormProps } from '../PersonalAccessTokenForm';
it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot('default');
+ expect(shallowRender()).toMatchSnapshot('bitbucket');
expect(shallowRender({ submitting: true })).toMatchSnapshot('submitting');
expect(shallowRender({ validationFailed: true })).toMatchSnapshot('validation failed');
+ expect(
+ shallowRender({
+ almSetting: mockAlmSettingsInstance({ alm: AlmKeys.GitLab, url: 'https://gitlab.com/api/v4' })
+ })
+ ).toMatchSnapshot('gitlab');
+ expect(
+ shallowRender({
+ almSetting: mockAlmSettingsInstance({
+ alm: AlmKeys.GitLab,
+ url: 'https://gitlabapi.unexpectedurl.org'
+ })
+ })
+ ).toMatchSnapshot('gitlab with non-standard api path');
});
it('should correctly handle form interactions', () => {
@@ -57,10 +68,10 @@ it('should correctly handle form interactions', () => {
expect(wrapper.find(SubmitButton).prop('disabled')).toBe(true);
});
-function shallowRender(props: Partial<BitbucketPersonalAccessTokenFormProps> = {}) {
- return shallow<BitbucketPersonalAccessTokenFormProps>(
- <BitbucketPersonalAccessTokenForm
- bitbucketSetting={mockAlmSettingsInstance({
+function shallowRender(props: Partial<PersonalAccessTokenFormProps> = {}) {
+ return shallow<PersonalAccessTokenFormProps>(
+ <PersonalAccessTokenForm
+ almSetting={mockAlmSettingsInstance({
alm: AlmKeys.Bitbucket,
url: 'http://www.example.com'
})}
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/WrongBindingCountAlert-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/WrongBindingCountAlert-test.tsx
new file mode 100644
index 00000000000..9f67f5f3fee
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/WrongBindingCountAlert-test.tsx
@@ -0,0 +1,34 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { AlmKeys } from '../../../../types/alm-settings';
+import WrongBindingCountAlert, { WrongBindingCountAlertProps } from '../WrongBindingCountAlert';
+
+it('should render correctly', () => {
+ expect(shallowRender({ canAdmin: true })).toMatchSnapshot('for admin');
+ expect(shallowRender({ alm: AlmKeys.Bitbucket })).toMatchSnapshot('bitbucket');
+ expect(shallowRender({ alm: AlmKeys.GitLab })).toMatchSnapshot('gitlab');
+});
+
+function shallowRender(props: Partial<WrongBindingCountAlertProps> = {}) {
+ return shallow(<WrongBindingCountAlert alm={AlmKeys.Bitbucket} canAdmin={false} {...props} />);
+}
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/BitbucketProjectCreateRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/BitbucketProjectCreateRenderer-test.tsx.snap
index 5b0b50bc580..8d767ba357d 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/BitbucketProjectCreateRenderer-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/BitbucketProjectCreateRenderer-test.tsx.snap
@@ -171,32 +171,10 @@ exports[`should render correctly: invalid config, admin user 1`] = `
</span>
}
/>
- <Alert
- variant="error"
- >
- <FormattedMessage
- defaultMessage="onboarding.create_project.no_bbs_binding.admin"
- id="onboarding.create_project.no_bbs_binding.admin"
- values={
- Object {
- "url": <Link
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/admin/settings",
- "query": Object {
- "category": "almintegration",
- },
- }
- }
- >
- settings.page
- </Link>,
- }
- }
- />
- </Alert>
+ <WrongBindingCountAlert
+ alm="bitbucket"
+ canAdmin={true}
+ />
</Fragment>
`;
@@ -235,11 +213,10 @@ exports[`should render correctly: invalid config, regular user 1`] = `
</span>
}
/>
- <Alert
- variant="error"
- >
- onboarding.create_project.no_bbs_binding
- </Alert>
+ <WrongBindingCountAlert
+ alm="bitbucket"
+ canAdmin={false}
+ />
</Fragment>
`;
@@ -302,8 +279,8 @@ exports[`should render correctly: pat form 1`] = `
</span>
}
/>
- <BitbucketPersonalAccessTokenForm
- bitbucketSetting={
+ <PersonalAccessTokenForm
+ almSetting={
Object {
"alm": "bitbucket",
"key": "key",
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectModeSelection-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectModeSelection-test.tsx.snap
index b0e0b0f9f3d..cd06179f636 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectModeSelection-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectModeSelection-test.tsx.snap
@@ -36,7 +36,7 @@ exports[`should render correctly: default 1`] = `
</div>
</button>
<button
- className="button button-huge big-spacer-left display-flex-column create-project-mode-type-bbs"
+ className="button button-huge big-spacer-left display-flex-column create-project-mode-type-alm"
disabled={false}
onClick={[Function]}
type="button"
@@ -53,7 +53,7 @@ exports[`should render correctly: default 1`] = `
</div>
</button>
<button
- className="button button-huge big-spacer-left display-flex-column create-project-mode-type-bbs disabled"
+ className="button button-huge big-spacer-left display-flex-column create-project-mode-type-alm disabled"
disabled={true}
onClick={[Function]}
type="button"
@@ -83,6 +83,37 @@ exports[`should render correctly: default 1`] = `
/>
</div>
</button>
+ <button
+ className="button button-huge big-spacer-left display-flex-column create-project-mode-type-alm disabled"
+ disabled={true}
+ onClick={[Function]}
+ type="button"
+ >
+ <img
+ alt=""
+ height={80}
+ src="/images/alm/gitlab.svg"
+ />
+ <div
+ className="medium big-spacer-top"
+ >
+ onboarding.create_project.select_method.gitlab
+ </div>
+ <div
+ className="text-muted small spacer-top"
+ style={
+ Object {
+ "lineHeight": 1.5,
+ }
+ }
+ >
+ onboarding.create_project.alm_not_configured
+ <HelpTooltip
+ className="little-spacer-left"
+ overlay="onboarding.create_project.zero_alm_instances.gitlab"
+ />
+ </div>
+ </button>
</div>
</Fragment>
`;
@@ -123,7 +154,7 @@ exports[`should render correctly: invalid configs 1`] = `
</div>
</button>
<button
- className="button button-huge big-spacer-left display-flex-column create-project-mode-type-bbs disabled"
+ className="button button-huge big-spacer-left display-flex-column create-project-mode-type-alm disabled"
disabled={true}
onClick={[Function]}
type="button"
@@ -154,7 +185,7 @@ exports[`should render correctly: invalid configs 1`] = `
</div>
</button>
<button
- className="button button-huge big-spacer-left display-flex-column create-project-mode-type-bbs disabled"
+ className="button button-huge big-spacer-left display-flex-column create-project-mode-type-alm disabled"
disabled={true}
onClick={[Function]}
type="button"
@@ -185,6 +216,37 @@ exports[`should render correctly: invalid configs 1`] = `
/>
</div>
</button>
+ <button
+ className="button button-huge big-spacer-left display-flex-column create-project-mode-type-alm disabled"
+ disabled={true}
+ onClick={[Function]}
+ type="button"
+ >
+ <img
+ alt=""
+ height={80}
+ src="/images/alm/gitlab.svg"
+ />
+ <div
+ className="medium big-spacer-top"
+ >
+ onboarding.create_project.select_method.gitlab
+ </div>
+ <div
+ className="text-muted small spacer-top"
+ style={
+ Object {
+ "lineHeight": 1.5,
+ }
+ }
+ >
+ onboarding.create_project.alm_not_configured
+ <HelpTooltip
+ className="little-spacer-left"
+ overlay="onboarding.create_project.zero_alm_instances.gitlab"
+ />
+ </div>
+ </button>
</div>
</Fragment>
`;
@@ -225,7 +287,7 @@ exports[`should render correctly: loading instances 1`] = `
</div>
</button>
<button
- className="button button-huge big-spacer-left display-flex-column create-project-mode-type-bbs disabled"
+ className="button button-huge big-spacer-left display-flex-column create-project-mode-type-alm disabled"
disabled={true}
onClick={[Function]}
type="button"
@@ -248,7 +310,7 @@ exports[`should render correctly: loading instances 1`] = `
</span>
</button>
<button
- className="button button-huge big-spacer-left display-flex-column create-project-mode-type-bbs disabled"
+ className="button button-huge big-spacer-left display-flex-column create-project-mode-type-alm disabled"
disabled={true}
onClick={[Function]}
type="button"
@@ -270,6 +332,29 @@ exports[`should render correctly: loading instances 1`] = `
/>
</span>
</button>
+ <button
+ className="button button-huge big-spacer-left display-flex-column create-project-mode-type-alm disabled"
+ disabled={true}
+ onClick={[Function]}
+ type="button"
+ >
+ <img
+ alt=""
+ height={80}
+ src="/images/alm/gitlab.svg"
+ />
+ <div
+ className="medium big-spacer-top"
+ >
+ onboarding.create_project.select_method.gitlab
+ </div>
+ <span>
+ onboarding.create_project.check_alm_supported
+ <i
+ className="little-spacer-left spinner"
+ />
+ </span>
+ </button>
</div>
</Fragment>
`;
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectPage-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectPage-test.tsx.snap
index 3a1c1edc18b..3c54a4b2d4d 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectPage-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectPage-test.tsx.snap
@@ -142,6 +142,44 @@ exports[`should render correctly if the GitHub method is selected 1`] = `
</Fragment>
`;
+exports[`should render correctly if the GitLab method is selected 1`] = `
+<Fragment>
+ <Helmet
+ defer={true}
+ encodeSpecialCharacters={true}
+ title="my_account.create_new.TRK"
+ titleTemplate="%s"
+ />
+ <A11ySkipTarget
+ anchor="create_project_main"
+ />
+ <div
+ className="page page-limited huge-spacer-bottom position-relative"
+ id="create-project"
+ >
+ <GitlabProjectCreate
+ canAdmin={false}
+ loadingBindings={true}
+ location={
+ Object {
+ "action": "PUSH",
+ "hash": "",
+ "key": "key",
+ "pathname": "/path",
+ "query": Object {
+ "mode": "gitlab",
+ },
+ "search": "",
+ "state": Object {},
+ }
+ }
+ onProjectCreate={[Function]}
+ settings={Array []}
+ />
+ </div>
+</Fragment>
+`;
+
exports[`should render correctly if the manual method is selected 1`] = `
<Fragment>
<Helmet
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitlabProjectCreate-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitlabProjectCreate-test.tsx.snap
new file mode 100644
index 00000000000..8bf4d3701b3
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitlabProjectCreate-test.tsx.snap
@@ -0,0 +1,18 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<GitlabProjectCreateRenderer
+ canAdmin={false}
+ loading={true}
+ onPersonalAccessTokenCreate={[Function]}
+ settings={
+ Object {
+ "alm": "gitlab",
+ "key": "gitlab-setting",
+ }
+ }
+ showPersonalAccessTokenForm={true}
+ submittingToken={false}
+ tokenValidationFailed={false}
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitlabProjectCreateRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitlabProjectCreateRenderer-test.tsx.snap
new file mode 100644
index 00000000000..d0d2a723737
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitlabProjectCreateRenderer-test.tsx.snap
@@ -0,0 +1,103 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly: invalid settings 1`] = `
+<Fragment>
+ <CreateProjectPageHeader
+ title={
+ <span
+ className="text-middle"
+ >
+ <img
+ alt=""
+ className="spacer-right"
+ height="24"
+ src="/images/alm/gitlab.svg"
+ />
+ onboarding.create_project.gitlab.title
+ </span>
+ }
+ />
+ <WrongBindingCountAlert
+ alm="gitlab"
+ canAdmin={false}
+ />
+</Fragment>
+`;
+
+exports[`should render correctly: invalid settings, admin user 1`] = `
+<Fragment>
+ <CreateProjectPageHeader
+ title={
+ <span
+ className="text-middle"
+ >
+ <img
+ alt=""
+ className="spacer-right"
+ height="24"
+ src="/images/alm/gitlab.svg"
+ />
+ onboarding.create_project.gitlab.title
+ </span>
+ }
+ />
+ <WrongBindingCountAlert
+ alm="gitlab"
+ canAdmin={true}
+ />
+</Fragment>
+`;
+
+exports[`should render correctly: loading 1`] = `
+<Fragment>
+ <CreateProjectPageHeader
+ title={
+ <span
+ className="text-middle"
+ >
+ <img
+ alt=""
+ className="spacer-right"
+ height="24"
+ src="/images/alm/gitlab.svg"
+ />
+ onboarding.create_project.gitlab.title
+ </span>
+ }
+ />
+ <i
+ className="spinner"
+ />
+</Fragment>
+`;
+
+exports[`should render correctly: pat form 1`] = `
+<Fragment>
+ <CreateProjectPageHeader
+ title={
+ <span
+ className="text-middle"
+ >
+ <img
+ alt=""
+ className="spacer-right"
+ height="24"
+ src="/images/alm/gitlab.svg"
+ />
+ onboarding.create_project.gitlab.title
+ </span>
+ }
+ />
+ <PersonalAccessTokenForm
+ almSetting={
+ Object {
+ "alm": "gitlab",
+ "key": "key",
+ }
+ }
+ onPersonalAccessTokenCreate={[MockFunction]}
+ submitting={false}
+ validationFailed={false}
+ />
+</Fragment>
+`;
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/BitbucketPersonalAccessTokenForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/PersonalAccessTokenForm-test.tsx.snap
index 18a3bf5e37b..c735dca4150 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/BitbucketPersonalAccessTokenForm-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/PersonalAccessTokenForm-test.tsx.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`should render correctly: default 1`] = `
+exports[`should render correctly: bitbucket 1`] = `
<div
className="display-flex-start"
>
@@ -10,12 +10,12 @@ exports[`should render correctly: default 1`] = `
<h2
className="big"
>
- onboarding.create_project.grant_access_to_bbs.title
+ onboarding.create_project.pat_form.title.bitbucket
</h2>
<p
className="big-spacer-top big-spacer-bottom"
>
- onboarding.create_project.grant_access_to_bbs.help
+ onboarding.create_project.pat_form.help.bitbucket
</p>
<ValidationInput
id="personal_access_token"
@@ -56,7 +56,15 @@ exports[`should render correctly: default 1`] = `
<p
className="big-spacer-top big-spacer-bottom"
>
- onboarding.create_project.pat_help.bbs_help_1
+ <FormattedMessage
+ defaultMessage="onboarding.create_project.pat_help.instructions"
+ id="onboarding.create_project.pat_help.instructions"
+ values={
+ Object {
+ "alm": "onboarding.alm.bitbucket",
+ }
+ }
+ />
</p>
<div
className="text-middle"
@@ -78,7 +86,7 @@ exports[`should render correctly: default 1`] = `
<p
className="big-spacer-top big-spacer-bottom"
>
- onboarding.create_project.pat_help.bbs_help_2
+ onboarding.create_project.pat_help.instructions2.bitbucket
</p>
<ul>
<li>
@@ -112,6 +120,208 @@ exports[`should render correctly: default 1`] = `
</div>
`;
+exports[`should render correctly: gitlab 1`] = `
+<div
+ className="display-flex-start"
+>
+ <form
+ onSubmit={[Function]}
+ >
+ <h2
+ className="big"
+ >
+ onboarding.create_project.pat_form.title.gitlab
+ </h2>
+ <p
+ className="big-spacer-top big-spacer-bottom"
+ >
+ onboarding.create_project.pat_form.help.gitlab
+ </p>
+ <ValidationInput
+ id="personal_access_token"
+ isInvalid={false}
+ isValid={false}
+ label="onboarding.create_project.enter_pat"
+ required={true}
+ >
+ <input
+ autoFocus={true}
+ className="input-super-large"
+ id="personal_access_token"
+ minLength={1}
+ name="personal_access_token"
+ onChange={[Function]}
+ type="text"
+ />
+ </ValidationInput>
+ <SubmitButton
+ disabled={true}
+ >
+ save
+ </SubmitButton>
+ <DeferredSpinner
+ className="spacer-left"
+ loading={false}
+ timeout={100}
+ />
+ </form>
+ <Alert
+ className="big-spacer-left big-spacer-top"
+ display="block"
+ variant="info"
+ >
+ <h3>
+ onboarding.create_project.pat_help.title
+ </h3>
+ <p
+ className="big-spacer-top big-spacer-bottom"
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.create_project.pat_help.instructions"
+ id="onboarding.create_project.pat_help.instructions"
+ values={
+ Object {
+ "alm": "onboarding.alm.gitlab",
+ }
+ }
+ />
+ </p>
+ <div
+ className="text-middle"
+ >
+ <img
+ alt=""
+ className="spacer-right"
+ height="16"
+ src="/images/alm/gitlab.svg"
+ />
+ <a
+ href="https://gitlab.com/profile/personal_access_tokens"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ onboarding.create_project.pat_help.link
+ </a>
+ </div>
+ <p
+ className="big-spacer-top big-spacer-bottom"
+ >
+ onboarding.create_project.pat_help.instructions2.gitlab
+ </p>
+ <ul>
+ <li
+ className="spacer-bottom"
+ >
+ <strong>
+ onboarding.create_project.pat_help.gitlab.read_api_permission
+ </strong>
+ </li>
+ </ul>
+ </Alert>
+</div>
+`;
+
+exports[`should render correctly: gitlab with non-standard api path 1`] = `
+<div
+ className="display-flex-start"
+>
+ <form
+ onSubmit={[Function]}
+ >
+ <h2
+ className="big"
+ >
+ onboarding.create_project.pat_form.title.gitlab
+ </h2>
+ <p
+ className="big-spacer-top big-spacer-bottom"
+ >
+ onboarding.create_project.pat_form.help.gitlab
+ </p>
+ <ValidationInput
+ id="personal_access_token"
+ isInvalid={false}
+ isValid={false}
+ label="onboarding.create_project.enter_pat"
+ required={true}
+ >
+ <input
+ autoFocus={true}
+ className="input-super-large"
+ id="personal_access_token"
+ minLength={1}
+ name="personal_access_token"
+ onChange={[Function]}
+ type="text"
+ />
+ </ValidationInput>
+ <SubmitButton
+ disabled={true}
+ >
+ save
+ </SubmitButton>
+ <DeferredSpinner
+ className="spacer-left"
+ loading={false}
+ timeout={100}
+ />
+ </form>
+ <Alert
+ className="big-spacer-left big-spacer-top"
+ display="block"
+ variant="info"
+ >
+ <h3>
+ onboarding.create_project.pat_help.title
+ </h3>
+ <p
+ className="big-spacer-top big-spacer-bottom"
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.create_project.pat_help.instructions"
+ id="onboarding.create_project.pat_help.instructions"
+ values={
+ Object {
+ "alm": "onboarding.alm.gitlab",
+ }
+ }
+ />
+ </p>
+ <div
+ className="text-middle"
+ >
+ <img
+ alt=""
+ className="spacer-right"
+ height="16"
+ src="/images/alm/gitlab.svg"
+ />
+ <a
+ href="https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#creating-a-personal-access-token"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ onboarding.create_project.pat_help.link
+ </a>
+ </div>
+ <p
+ className="big-spacer-top big-spacer-bottom"
+ >
+ onboarding.create_project.pat_help.instructions2.gitlab
+ </p>
+ <ul>
+ <li
+ className="spacer-bottom"
+ >
+ <strong>
+ onboarding.create_project.pat_help.gitlab.read_api_permission
+ </strong>
+ </li>
+ </ul>
+ </Alert>
+</div>
+`;
+
exports[`should render correctly: submitting 1`] = `
<div
className="display-flex-start"
@@ -122,12 +332,12 @@ exports[`should render correctly: submitting 1`] = `
<h2
className="big"
>
- onboarding.create_project.grant_access_to_bbs.title
+ onboarding.create_project.pat_form.title.bitbucket
</h2>
<p
className="big-spacer-top big-spacer-bottom"
>
- onboarding.create_project.grant_access_to_bbs.help
+ onboarding.create_project.pat_form.help.bitbucket
</p>
<ValidationInput
id="personal_access_token"
@@ -168,7 +378,15 @@ exports[`should render correctly: submitting 1`] = `
<p
className="big-spacer-top big-spacer-bottom"
>
- onboarding.create_project.pat_help.bbs_help_1
+ <FormattedMessage
+ defaultMessage="onboarding.create_project.pat_help.instructions"
+ id="onboarding.create_project.pat_help.instructions"
+ values={
+ Object {
+ "alm": "onboarding.alm.bitbucket",
+ }
+ }
+ />
</p>
<div
className="text-middle"
@@ -190,7 +408,7 @@ exports[`should render correctly: submitting 1`] = `
<p
className="big-spacer-top big-spacer-bottom"
>
- onboarding.create_project.pat_help.bbs_help_2
+ onboarding.create_project.pat_help.instructions2.bitbucket
</p>
<ul>
<li>
@@ -234,12 +452,12 @@ exports[`should render correctly: validation failed 1`] = `
<h2
className="big"
>
- onboarding.create_project.grant_access_to_bbs.title
+ onboarding.create_project.pat_form.title.bitbucket
</h2>
<p
className="big-spacer-top big-spacer-bottom"
>
- onboarding.create_project.grant_access_to_bbs.help
+ onboarding.create_project.pat_form.help.bitbucket
</p>
<ValidationInput
error="onboarding.create_project.pat_incorrect"
@@ -281,7 +499,15 @@ exports[`should render correctly: validation failed 1`] = `
<p
className="big-spacer-top big-spacer-bottom"
>
- onboarding.create_project.pat_help.bbs_help_1
+ <FormattedMessage
+ defaultMessage="onboarding.create_project.pat_help.instructions"
+ id="onboarding.create_project.pat_help.instructions"
+ values={
+ Object {
+ "alm": "onboarding.alm.bitbucket",
+ }
+ }
+ />
</p>
<div
className="text-middle"
@@ -303,7 +529,7 @@ exports[`should render correctly: validation failed 1`] = `
<p
className="big-spacer-top big-spacer-bottom"
>
- onboarding.create_project.pat_help.bbs_help_2
+ onboarding.create_project.pat_help.instructions2.bitbucket
</p>
<ul>
<li>
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/WrongBindingCountAlert-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/WrongBindingCountAlert-test.tsx.snap
new file mode 100644
index 00000000000..b30756a2619
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/WrongBindingCountAlert-test.tsx.snap
@@ -0,0 +1,63 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly: bitbucket 1`] = `
+<Alert
+ variant="error"
+>
+ <FormattedMessage
+ defaultMessage="onboarding.create_project.wrong_binding_count"
+ id="onboarding.create_project.wrong_binding_count"
+ values={
+ Object {
+ "alm": "onboarding.alm.bitbucket",
+ }
+ }
+ />
+</Alert>
+`;
+
+exports[`should render correctly: for admin 1`] = `
+<Alert
+ variant="error"
+>
+ <FormattedMessage
+ defaultMessage="onboarding.create_project.wrong_binding_count.admin"
+ id="onboarding.create_project.wrong_binding_count.admin"
+ values={
+ Object {
+ "alm": "onboarding.alm.bitbucket",
+ "url": <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/admin/settings",
+ "query": Object {
+ "category": "almintegration",
+ },
+ }
+ }
+ >
+ settings.page
+ </Link>,
+ }
+ }
+ />
+</Alert>
+`;
+
+exports[`should render correctly: gitlab 1`] = `
+<Alert
+ variant="error"
+>
+ <FormattedMessage
+ defaultMessage="onboarding.create_project.wrong_binding_count"
+ id="onboarding.create_project.wrong_binding_count"
+ values={
+ Object {
+ "alm": "onboarding.alm.gitlab",
+ }
+ }
+ />
+</Alert>
+`;
diff --git a/server/sonar-web/src/main/js/apps/create/project/types.ts b/server/sonar-web/src/main/js/apps/create/project/types.ts
index 0fe8944a405..c84dd2c1d4c 100644
--- a/server/sonar-web/src/main/js/apps/create/project/types.ts
+++ b/server/sonar-web/src/main/js/apps/create/project/types.ts
@@ -20,5 +20,6 @@
export enum CreateProjectModes {
Manual = 'manual',
BitbucketServer = 'bitbucket',
- GitHub = 'github'
+ GitHub = 'github',
+ GitLab = 'gitlab'
}
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index e45d4af8035..e051a4109ca 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -1800,8 +1800,9 @@ my_account.create_new.VW=Create Portfolio
my_account.create_new.APP=Create Application
my_account.add_project=Add Project
my_account.add_project.manual=Manually
-my_account.add_project.github=GitHub
my_account.add_project.bitbucket=Bitbucket
+my_account.add_project.github=GitHub
+my_account.add_project.gitlab=GitLab
my_account.create_new_project_portfolio_or_application=Analyze new project / Create new portfolio or application
@@ -3095,6 +3096,9 @@ footer.web_api=Web API
# ONBOARDING
#
#------------------------------------------------------------------------------
+onboarding.alm.bitbucket=Bitbucket Server
+onboarding.alm.gitlab=GitLab
+
onboarding.project_analysis.header=Analyze your project
onboarding.project_analysis.description=We initialized your project on {instance}, now it's up to you to launch analyses!
onboarding.project_analysis.guide_to_integrate_pipelines=follow the guide to integrating with Pipelines
@@ -3103,6 +3107,7 @@ onboarding.create_project.setup_manually=Create a project
onboarding.create_project.select_method.manual=Manually
onboarding.create_project.select_method.bitbucket=From Bitbucket Server
onboarding.create_project.select_method.github=From GitHub
+onboarding.create_project.select_method.gitlab=From GitLab
onboarding.create_project.alm_not_configured=Currently not active
onboarding.create_project.check_alm_supported=Checking if available
onboarding.create_project.project_key=Project key
@@ -3125,25 +3130,33 @@ onboarding.create_project.search_repositories=Search for a repository
onboarding.create_project.select_repositories=Select repositories
onboarding.create_project.select_all_repositories=Select all available repositories
onboarding.create_project.from_bbs=Create a project from Bitbucket Server
-onboarding.create_project.grant_access_to_bbs.title=Grant access to your repositories
-onboarding.create_project.grant_access_to_bbs.help=SonarQube needs a personal access token to access and list your repositories from Bitbucket Server.
+
+onboarding.create_project.pat_form.title.bitbucket=Grant access to your repositories
+onboarding.create_project.pat_form.title.gitlab=Grant access to your projects
+onboarding.create_project.pat_form.help.bitbucket=SonarQube needs a personal access token to access and list your repositories from Bitbucket Server.
+onboarding.create_project.pat_form.help.gitlab=SonarQube needs a personal access token to access and list your projects from GitLab.
onboarding.create_project.select_method=How do you want to create your project?
onboarding.create_project.too_many_alm_instances.bitbucket=You must have exactly 1 Bitbucket Server instance configured in order to use this method.
onboarding.create_project.too_many_alm_instances.github=You must have exactly 1 Bitbucket Server instance configured in order to use this method.
onboarding.create_project.alm_instances_count_X=You currently have {0}.
onboarding.create_project.zero_alm_instances.bitbucket=You must first configure a Bitbucket Server instance.
onboarding.create_project.zero_alm_instances.github=You must first configure a GitHub instance.
-onboarding.create_project.no_bbs_binding=You must have exactly at least 1 Bitbucket Server instance configured in order to use this method, but none were found. Either create the project manually, or contact your system administrator.
-onboarding.create_project.no_bbs_binding.admin=You must have exactly at least 1 Bitbucket Server instance configured in order to use this method. You can configure instances under {url}.
+onboarding.create_project.wrong_binding_count=You must have exactly 1 {alm} instance configured in order to use this method, but none were found. Either create the project manually, or contact your system administrator.
+onboarding.create_project.wrong_binding_count.admin=You must have exactly 1 {alm} instance configured in order to use this method. You can configure instances under {url}.
onboarding.create_project.enter_pat=Enter personal access token
onboarding.create_project.pat_incorrect=Your personal access token failed to validate.
onboarding.create_project.pat_help.title=How to create a personal access token?
-onboarding.create_project.pat_help.bbs_help_1=Click the following link to generate a token in Bitbucket Server, and copy-paste it into the personal access token field.
-onboarding.create_project.pat_help.bbs_help_2=Set a name, for example "SonarQube", and select the following permissions:
+
+onboarding.create_project.pat_help.instructions=Click the following link to generate a token in {alm}, and copy-paste it into the personal access token field.
+onboarding.create_project.pat_help.instructions2.bitbucket=Set a name, for example "SonarQube", and select the following permissions:
onboarding.create_project.pat_help.link=Create personal access token
onboarding.create_project.pat_help.bbs_permission_projects=Projects: {perm}
onboarding.create_project.pat_help.bbs_permission_repos=Repositories: {perm}
onboarding.create_project.pat_help.read_permission=Read
+
+onboarding.create_project.pat_help.instructions2.gitlab=Set a name, for example "SonarQube", and select the following scope:
+onboarding.create_project.pat_help.gitlab.read_api_permission=read_api
+
onboarding.create_project.no_bbs_projects=No projects could be fetched from Bitbucket Server. Contact your system administrator, or {link}.
onboarding.create_project.no_bbs_repos=No repositories were found for this project. Contact your system administrator, or {link}.
onboarding.create_project.update_your_token=update your personal access token
@@ -3159,6 +3172,7 @@ onboarding.create_project.github.warning.message_admin=Please make sure the GitH
onboarding.create_project.github.warning.message_admin.link=ALM integration settings
onboarding.create_project.github.no_orgs=We couldn't load any organizations with your key. Contact an administrator.
onboarding.create_project.github.no_orgs_admin=We couldn't load any organizations. Make sure the GitHub App is installed in at least one organization and check the GitHub instance configuration in the {link}.
+onboarding.create_project.gitlab.title=Which GitLab project do you want to setup?
onboarding.create_organization.page.header=Create Organization
onboarding.create_organization.page.description=An organization is a space where a team or a whole company can collaborate accross many projects.