Browse Source

SONAR-14079 Add Azure Pipelines tutorial for Azure DevOps Server

tags/8.6.0.39681
Philippe Perrin 3 years ago
parent
commit
9dea56b689
30 changed files with 1953 additions and 163 deletions
  1. 2
    6
      server/sonar-web/src/main/js/components/tutorials/TutorialSelection.tsx
  2. 25
    0
      server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx
  3. 1
    8
      server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelection-test.tsx
  4. 18
    0
      server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelectionRenderer-test.tsx
  5. 48
    0
      server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelectionRenderer-test.tsx.snap
  6. 130
    0
      server/sonar-web/src/main/js/components/tutorials/azure-pipelines/AzurePipelinesTutorial.tsx
  7. 208
    0
      server/sonar-web/src/main/js/components/tutorials/azure-pipelines/BranchAnalysisStepContent.tsx
  8. 55
    0
      server/sonar-web/src/main/js/components/tutorials/azure-pipelines/ExtensionInstallationStepContent.tsx
  9. 66
    0
      server/sonar-web/src/main/js/components/tutorials/azure-pipelines/SaveAndRunStepContent.tsx
  10. 92
    0
      server/sonar-web/src/main/js/components/tutorials/azure-pipelines/ServiceEndpointStepContent.tsx
  11. 111
    0
      server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-test.tsx
  12. 58
    0
      server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/BranchAnalysisStepContent-test.tsx
  13. 32
    0
      server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/ExtensionInstallationStepContent-test.tsx
  14. 32
    0
      server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/SaveAndRunStepContent-test.tsx
  15. 52
    0
      server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/ServiceEndpointStepContent-test.tsx
  16. 128
    0
      server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/AzurePipelinesTutorial-test.tsx.snap
  17. 613
    0
      server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/BranchAnalysisStepContent-test.tsx.snap
  18. 24
    0
      server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/ExtensionInstallationStepContent-test.tsx.snap
  19. 54
    0
      server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/SaveAndRunStepContent-test.tsx.snap
  20. 66
    0
      server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/ServiceEndpointStepContent-test.tsx.snap
  21. 4
    2
      server/sonar-web/src/main/js/components/tutorials/gitlabci/GitLabCITutorial.tsx
  22. 6
    5
      server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx
  23. 2
    4
      server/sonar-web/src/main/js/components/tutorials/jenkins/MultiBranchPipelineStep.tsx
  24. 2
    4
      server/sonar-web/src/main/js/components/tutorials/jenkins/WebhookStep.tsx
  25. 2
    1
      server/sonar-web/src/main/js/components/tutorials/types.ts
  26. 0
    67
      server/sonar-web/src/main/js/helpers/__tests__/alm-settings-test.ts
  27. 0
    66
      server/sonar-web/src/main/js/helpers/alm-settings.ts
  28. 14
    0
      server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts
  29. 50
    0
      server/sonar-web/src/main/js/types/alm-settings.ts
  30. 58
    0
      sonar-core/src/main/resources/org/sonar/l10n/core.properties

+ 2
- 6
server/sonar-web/src/main/js/components/tutorials/TutorialSelection.tsx View File

@@ -20,7 +20,7 @@
import * as React from 'react';
import { WithRouterProps } from 'react-router';
import { getAlmDefinitionsNoCatch, getProjectAlmBinding } from '../../api/alm-settings';
import { AlmBindingDefinition, AlmKeys, ProjectAlmBindingResponse } from '../../types/alm-settings';
import { AlmBindingDefinition, ProjectAlmBindingResponse } from '../../types/alm-settings';
import { withRouter } from '../hoc/withRouter';
import TutorialSelectionRenderer from './TutorialSelectionRenderer';
import { TutorialModes } from './types';
@@ -58,11 +58,7 @@ export class TutorialSelection extends React.PureComponent<Props, State> {
]);

if (this.mounted) {
// We only support Bitbucket, GitHub & Gitlab for now.
if (
projectBinding === undefined ||
![AlmKeys.Bitbucket, AlmKeys.GitHub, AlmKeys.GitLab].includes(projectBinding.alm)
) {
if (projectBinding === undefined) {
this.setState({ loading: false, forceManual: true });
} else {
let almBinding;

+ 25
- 0
server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx View File

@@ -22,6 +22,7 @@ import * as React from 'react';
import { translate } from 'sonar-ui-common/helpers/l10n';
import { getBaseUrl } from 'sonar-ui-common/helpers/urls';
import { AlmBindingDefinition, AlmKeys, ProjectAlmBindingResponse } from '../../types/alm-settings';
import AzurePipelinesTutorial from './azure-pipelines/AzurePipelinesTutorial';
import GitLabCITutorial from './gitlabci/GitLabCITutorial';
import JenkinsTutorial from './jenkins/JenkinsTutorial';
import ManualTutorial from './manual/ManualTutorial';
@@ -74,6 +75,22 @@ export default function TutorialSelectionRenderer(props: TutorialSelectionRender
</button>
)}

{projectBinding?.alm === AlmKeys.Azure && (
<button
className="button button-huge display-flex-column spacer-left spacer-right azure-pipelines"
onClick={() => props.onSelectTutorial(TutorialModes.AzurePipelines)}
type="button">
<img
alt="" // Should be ignored by screen readers
height={80}
src={`${getBaseUrl()}/images/alm/azure.svg`}
/>
<div className="medium big-spacer-top">
{translate('onboarding.tutorial.choose_method.azure_pipelines')}
</div>
</button>
)}

{jenkinsAvailable && (
<button
className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-jenkins"
@@ -126,6 +143,14 @@ export default function TutorialSelectionRenderer(props: TutorialSelectionRender
projectBinding={projectBinding}
/>
)}

{selectedTutorial === TutorialModes.AzurePipelines && projectBinding !== undefined && (
<AzurePipelinesTutorial
component={component}
currentUser={currentUser}
projectBinding={projectBinding}
/>
)}
</>
);
}

+ 1
- 8
server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelection-test.tsx View File

@@ -50,20 +50,13 @@ it('should select manual if project is not bound', async () => {
expect(wrapper.state().forceManual).toBe(true);
});

it('should not select anything if project is bound to Bitbucket', async () => {
it('should not select anything if project is bound', async () => {
(getProjectAlmBinding as jest.Mock).mockResolvedValueOnce({ alm: AlmKeys.Bitbucket });
const wrapper = shallowRender();
await waitAndUpdate(wrapper);
expect(wrapper.state().forceManual).toBe(false);
});

it('should select manual if project is bound to unsupported ALM', async () => {
(getProjectAlmBinding as jest.Mock).mockResolvedValueOnce({ alm: AlmKeys.Azure });
const wrapper = shallowRender();
await waitAndUpdate(wrapper);
expect(wrapper.state().forceManual).toBe(true);
});

it('should correctly find the global ALM binding definition', async () => {
const key = 'foo';
const almBinding = mockBitbucketBindingDefinition({ key });

+ 18
- 0
server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelectionRenderer-test.tsx View File

@@ -23,6 +23,7 @@ import * as React from 'react';
import { click } from 'sonar-ui-common/helpers/testUtils';
import {
mockBitbucketBindingDefinition,
mockProjectAzureBindingResponse,
mockProjectBitbucketBindingResponse,
mockProjectGitLabBindingResponse
} from '../../../helpers/mocks/alm-settings';
@@ -53,6 +54,12 @@ it('should render correctly', () => {
projectBinding: mockProjectGitLabBindingResponse()
})
).toMatchSnapshot('gitlab tutorial');
expect(
shallowRender({
selectedTutorial: TutorialModes.AzurePipelines,
projectBinding: mockProjectAzureBindingResponse()
})
).toMatchSnapshot('azure pipelines tutorial');
});

it('should allow mode selection', () => {
@@ -80,6 +87,17 @@ it('should allow gitlab selection', () => {
expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.GitLabCI);
});

it('should allow azure pipelines selection', () => {
const onSelectTutorial = jest.fn();
const wrapper = shallowRender({
onSelectTutorial,
projectBinding: mockProjectAzureBindingResponse()
});

click(wrapper.find('button.azure-pipelines'));
expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.AzurePipelines);
});

function shallowRender(props: Partial<TutorialSelectionRendererProps> = {}) {
return shallow<TutorialSelectionRendererProps>(
<TutorialSelectionRenderer

+ 48
- 0
server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelectionRenderer-test.tsx.snap View File

@@ -1,5 +1,53 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly: azure pipelines tutorial 1`] = `
<Fragment>
<AzurePipelinesTutorial
component={
Object {
"breadcrumbs": Array [],
"key": "my-project",
"name": "MyProject",
"organization": "foo",
"qualifier": "TRK",
"qualityGate": Object {
"isDefault": true,
"key": "30",
"name": "Sonar way",
},
"qualityProfiles": Array [
Object {
"deleted": false,
"key": "my-qp",
"language": "ts",
"name": "Sonar way",
},
],
"tags": Array [],
}
}
currentUser={
Object {
"groups": Array [],
"isLoggedIn": true,
"login": "luke",
"name": "Skywalker",
"scmAccounts": Array [],
}
}
projectBinding={
Object {
"alm": "azure",
"key": "foo",
"repository": "REPOSITORY_NAME",
"slug": "PROJECT_NAME",
"url": "https://ado.my_company.com/mycollection",
}
}
/>
</Fragment>
`;

exports[`should render correctly: gitlab tutorial 1`] = `
<Fragment>
<GitLabCITutorial

+ 130
- 0
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/AzurePipelinesTutorial.tsx View File

@@ -0,0 +1,130 @@
/*
* 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 { Button } from 'sonar-ui-common/components/controls/buttons';
import { Alert } from 'sonar-ui-common/components/ui/Alert';
import { translate } from 'sonar-ui-common/helpers/l10n';
import {
isProjectAzureBindingResponse,
ProjectAlmBindingResponse
} from '../../../types/alm-settings';
import Step from '../components/Step';
import BranchAnalysisStepContent from './BranchAnalysisStepContent';
import ExtensionInstallationStepContent from './ExtensionInstallationStepContent';
import SaveAndRunStepContent from './SaveAndRunStepContent';
import ServiceEndpointStepContent from './ServiceEndpointStepContent';

export interface AzurePipelinesTutorialProps {
component: T.Component;
currentUser: T.LoggedInUser;
projectBinding: ProjectAlmBindingResponse;
}

export enum Steps {
ExtensionInstallation,
ServiceEndpoint,
BranchAnalysis,
SaveAndRun
}

interface Step {
step: Steps;
content: JSX.Element;
checkValidity?: boolean;
}

export default function AzurePipelinesTutorial(props: AzurePipelinesTutorialProps) {
const { component, currentUser, projectBinding } = props;

const [currentStep, setCurrentStep] = React.useState(Steps.ExtensionInstallation);
const [isCurrentStepValid, setIsCurrentStepValid] = React.useState(false);

// Failsafe; should never happen.
if (!isProjectAzureBindingResponse(projectBinding)) {
return (
<Alert variant="error">
{translate('onboarding.tutorial.with.azure_pipelines.unsupported')}
</Alert>
);
}

const steps: Array<Step> = [
{ step: Steps.ExtensionInstallation, content: <ExtensionInstallationStepContent /> },
{
step: Steps.ServiceEndpoint,
content: <ServiceEndpointStepContent component={component} currentUser={currentUser} />
},
{
step: Steps.BranchAnalysis,
content: (
<BranchAnalysisStepContent
component={component}
onStepValidationChange={isValid => setIsCurrentStepValid(isValid)}
/>
),
checkValidity: true
},
{ step: Steps.SaveAndRun, content: <SaveAndRunStepContent /> }
];

const switchCurrentStep = (step: Steps) => {
setCurrentStep(step);
setIsCurrentStepValid(false);
};

const canContinue = (step: Step, i: number) =>
i < steps.length - 1 && (!step.checkValidity || isCurrentStepValid);

return (
<>
<div className="page-header big-spacer-bottom">
<h1 className="page-title">
{translate('onboarding.tutorial.with.azure_pipelines.title')}
</h1>
</div>

{steps.map((step, i) => (
<Step
key={step.step}
stepNumber={i + 1}
stepTitle={translate(
`onboarding.tutorial.with.azure_pipelines.${Steps[step.step]}.title`
)}
open={step.step === currentStep}
finished={step.step < currentStep}
onOpen={() => switchCurrentStep(step.step)}
renderForm={() => (
<div className="boxed-group-inner">
<div>{step.content}</div>
{canContinue(step, i) && (
<Button
className="big-spacer-top spacer-bottom"
onClick={() => switchCurrentStep(step.step + 1)}>
{translate('continue')}
</Button>
)}
</div>
)}
/>
))}
</>
);
}

+ 208
- 0
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/BranchAnalysisStepContent.tsx View File

@@ -0,0 +1,208 @@
/*
* 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 { ClipboardIconButton } from 'sonar-ui-common/components/controls/clipboard';
import { Alert } from 'sonar-ui-common/components/ui/Alert';
import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
import CodeSnippet from '../../common/CodeSnippet';
import RenderOptions from '../components/RenderOptions';
import SentenceWithHighlights from '../components/SentenceWithHighlights';

export interface BranchesAnalysisStepProps {
component: T.Component;
onStepValidationChange: (isValid: boolean) => void;
}

export enum BuildTechnology {
DotNet = 'dotnet',
Maven = 'maven',
Gradle = 'gradle',
Other = 'other'
}

export default function BranchAnalysisStepContent(props: BranchesAnalysisStepProps) {
const { component, onStepValidationChange } = props;

const [buildTechnology, setBuildTechnology] = React.useState<BuildTechnology | undefined>();

React.useEffect(() => {
if (buildTechnology) {
onStepValidationChange(true);
}
}, [buildTechnology, onStepValidationChange]);

const MAVEN_GRADLE_PROPS_SNIPPET = `# Additional properties that will be passed to the scanner,
# Put one key=value per line, example:
# sonar.exclusions=**/*.bin
sonar.projectKey=${component.key}`;

return (
<>
<span>{translate('onboarding.build')}</span>
<RenderOptions
checked={buildTechnology}
name="buildTechnology"
onCheck={value => setBuildTechnology(value as BuildTechnology)}
optionLabelKey="onboarding.build"
options={Object.values(BuildTechnology)}
/>
<ol className="list-styled big-spacer-top">
{buildTechnology && (
<>
<li>
<SentenceWithHighlights
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
highlightKeys={['pipeline', 'task', 'before']}
/>
</li>
<ul className="list-styled">
<li>
<SentenceWithHighlights
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint"
highlightKeys={['endpoint']}
/>
</li>
<li>
<FormattedMessage
id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
defaultMessage={translate(
'onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis'
)}
values={{
section: (
<strong>
{translate(
'onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section'
)}
</strong>
),
run_analysis_value: (
<strong>
{translate(
'onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values',
buildTechnology
)}
</strong>
)
}}
/>
</li>
{buildTechnology === BuildTechnology.Other && (
<li>
<SentenceWithHighlights
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.manual"
highlightKeys={['mode']}
/>
</li>
)}
{[BuildTechnology.DotNet, BuildTechnology.Other].includes(buildTechnology) && (
<li>
<FormattedMessage
id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence"
defaultMessage={translate(
'onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence'
)}
values={{
key: <code className="rule">{component.key}</code>,
button: <ClipboardIconButton copyValue={component.key} />
}}
/>
</li>
)}
{[BuildTechnology.Maven, BuildTechnology.Gradle].includes(buildTechnology) && (
<li>
<SentenceWithHighlights
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties"
highlightKeys={['section', 'properties']}
/>
:
<CodeSnippet snippet={MAVEN_GRADLE_PROPS_SNIPPET} />
</li>
)}
</ul>
{[BuildTechnology.DotNet, BuildTechnology.Other].includes(buildTechnology) && (
<li>
<SentenceWithHighlights
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run"
highlightKeys={['task', 'after']}
/>
</li>
)}
{[BuildTechnology.Maven, BuildTechnology.Gradle].includes(buildTechnology) && (
<>
<li>
{translateWithParameters(
'onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java',
translate('onboarding.build', buildTechnology)
)}
</li>
<ul className="list-styled big-spacer-bottom">
<li>
<SentenceWithHighlights
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings"
highlightKeys={['section', 'option']}
/>
</li>
</ul>
</>
)}

<li>
<SentenceWithHighlights
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg"
highlightKeys={['task']}
/>
<Alert variant="info" className="spacer-top">
{translate(
'onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1'
)}
</Alert>
</li>
<li>
<SentenceWithHighlights
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration"
highlightKeys={['tab', 'continuous_integration']}
/>
</li>
<hr />
<FormattedMessage
id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection"
defaultMessage={translate(
'onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection'
)}
values={{
link: (
<Link to="/documentation/analysis/azuredevops-integration/" target="_blank">
{translate(
'onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection.link'
)}
</Link>
)
}}
/>
</>
)}
</ol>
</>
);
}

+ 55
- 0
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/ExtensionInstallationStepContent.tsx View File

@@ -0,0 +1,55 @@
/*
* 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 { translate } from 'sonar-ui-common/helpers/l10n';

export default function ExtensionInstallationStepContent() {
return (
<span>
<FormattedMessage
defaultMessage={translate(
'onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence'
)}
id="onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence"
values={{
link: (
<a
href="https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarqube"
rel="noopener noreferrer"
target="_blank">
{translate(
'onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence.link'
)}
</a>
),
button: (
<strong>
{translate(
'onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence.button'
)}
</strong>
)
}}
/>
</span>
);
}

+ 66
- 0
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/SaveAndRunStepContent.tsx View File

@@ -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 { translate } from 'sonar-ui-common/helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';

export default function SaveAndRunStepContent() {
return (
<>
<div className="display-flex-row big-spacer-bottom">
<div>
<img
alt="" // Should be ignored by screen readers
className="big-spacer-right"
width={30}
src={`${getBaseUrl()}/images/tutorials/commit.svg`}
/>
</div>
<div>
<p className="little-spacer-bottom">
<strong>
{translate('onboarding.tutorial.with.azure_pipelines.SaveAndRun.commit')}
</strong>
</p>
<p>{translate('onboarding.tutorial.with.azure_pipelines.SaveAndRun.commit.why')}</p>
</div>
</div>
<div className="display-flex-row">
<div>
<img
alt="" // Should be ignored by screen readers
className="big-spacer-right"
width={30}
src={`${getBaseUrl()}/images/tutorials/refresh.svg`}
/>
</div>
<div>
<p className="little-spacer-bottom">
<strong>
{translate('onboarding.tutorial.with.azure_pipelines.SaveAndRun.refresh')}
</strong>
</p>
<p>{translate('onboarding.tutorial.with.azure_pipelines.SaveAndRun.refresh.why')}</p>
</div>
</div>
</>
);
}

+ 92
- 0
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/ServiceEndpointStepContent.tsx View File

@@ -0,0 +1,92 @@
/*
* 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 { Button } from 'sonar-ui-common/components/controls/buttons';
import { ClipboardIconButton } from 'sonar-ui-common/components/controls/clipboard';
import { translate } from 'sonar-ui-common/helpers/l10n';
import { getHostUrl } from 'sonar-ui-common/helpers/urls';
import EditTokenModal from '../components/EditTokenModal';
import SentenceWithHighlights from '../components/SentenceWithHighlights';

export interface ServiceEndpointStepProps {
component: T.Component;
currentUser: T.LoggedInUser;
}

export default function ServiceEndpointStepContent(props: ServiceEndpointStepProps) {
const { component, currentUser } = props;

const [isModalVisible, toggleModal] = React.useState(false);

return (
<>
<ol className="list-styled">
<li>
<SentenceWithHighlights
translationKey="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step1"
highlightKeys={['menu']}
/>
</li>
<li>
<SentenceWithHighlights
translationKey="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step2"
highlightKeys={['type']}
/>
</li>
<li>
{translate('onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step3.sentence')}
</li>
<li>
<FormattedMessage
defaultMessage={translate(
'onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step4.sentence'
)}
id="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step4.sentence"
values={{
url: <code className="rule">{getHostUrl()}</code>,
button: <ClipboardIconButton copyValue={getHostUrl()} />
}}
/>
</li>
<li>
<span>
{translate('onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step5.sentence')}:
</span>
<Button className="spacer-left" onClick={() => toggleModal(true)}>
{translate('onboarding.token.generate_token')}
</Button>
</li>
<li>
{translate('onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step6.sentence')}
</li>
</ol>

{isModalVisible && (
<EditTokenModal
component={component}
currentUser={currentUser}
onClose={() => toggleModal(false)}
/>
)}
</>
);
}

+ 111
- 0
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-test.tsx View File

@@ -0,0 +1,111 @@
/*
* 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 { Button } from 'sonar-ui-common/components/controls/buttons';
import { click } from 'sonar-ui-common/helpers/testUtils';
import {
mockProjectAzureBindingResponse,
mockProjectGithubBindingResponse
} from '../../../../helpers/mocks/alm-settings';
import { mockComponent, mockLoggedInUser } from '../../../../helpers/testMocks';
import Step from '../../components/Step';
import AzurePipelinesTutorial, { AzurePipelinesTutorialProps } from '../AzurePipelinesTutorial';

it('should render correctly', () => {
const wrapper = shallowRender();
expect(wrapper).toMatchSnapshot();
expect(
wrapper
.find(Step)
.first()
.dive()
).toMatchSnapshot('first-step-wrapper');
expect(
wrapper
.find(Step)
.last()
.dive()
).toMatchSnapshot('last-step-wrapper');
expect(shallowRender({ projectBinding: mockProjectGithubBindingResponse() })).toMatchSnapshot(
'wrong alm'
);
});

it('should display the next step when one is finished', () => {
const wrapper = shallowRender();
expect(
wrapper
.find(Step)
.filterWhere(elt => elt.props().open === true)
.props().stepNumber
).toBe(1);

click(
wrapper
.find(Step)
.first()
.dive()
.find(Button)
);

expect(
wrapper
.find(Step)
.filterWhere(elt => elt.props().open === true)
.props().stepNumber
).toBe(2);
});

it('should open a step when user click on it', () => {
const wrapper = shallowRender();
expect(
wrapper
.find(Step)
.filterWhere(elt => elt.props().open === true)
.props().stepNumber
).toBe(1);

(
wrapper
.find(Step)
.filterWhere(elt => elt.props().stepNumber === 3)
.props().onOpen ?? (() => {})
)();

expect(
wrapper
.find(Step)
.filterWhere(elt => elt.props().open === true)
.props().stepNumber
).toBe(3);
});

function shallowRender(props: Partial<AzurePipelinesTutorialProps> = {}) {
return shallow<AzurePipelinesTutorialProps>(
<AzurePipelinesTutorial
component={mockComponent()}
currentUser={mockLoggedInUser()}
projectBinding={mockProjectAzureBindingResponse()}
{...props}
/>
);
}

+ 58
- 0
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/BranchAnalysisStepContent-test.tsx View File

@@ -0,0 +1,58 @@
/*
* 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 { mockComponent } from '../../../../helpers/testMocks';
import RenderOptions from '../../components/RenderOptions';
import BranchAnalysisStepContent, {
BranchesAnalysisStepProps,
BuildTechnology
} from '../BranchAnalysisStepContent';

it('should render correctly', () => {
const wrapper = shallowRender();
expect(wrapper).toMatchSnapshot();
});

it.each([
BuildTechnology.DotNet,
BuildTechnology.Gradle,
BuildTechnology.Maven,
BuildTechnology.Other
])('should render correctly', (buildTech: BuildTechnology) => {
const wrapper = shallowRender();
wrapper
.find(RenderOptions)
.props()
.onCheck(buildTech);

expect(wrapper).toMatchSnapshot(buildTech);
});

function shallowRender(props: Partial<BranchesAnalysisStepProps> = {}) {
return shallow(
<BranchAnalysisStepContent
component={mockComponent()}
onStepValidationChange={jest.fn()}
{...props}
/>
);
}

+ 32
- 0
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/ExtensionInstallationStepContent-test.tsx View File

@@ -0,0 +1,32 @@
/*
* 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 ExtensionInstallationStepContent from '../ExtensionInstallationStepContent';

it('should render correctly', () => {
const wrapper = shallowRender();
expect(wrapper).toMatchSnapshot();
});

function shallowRender() {
return shallow(<ExtensionInstallationStepContent />);
}

+ 32
- 0
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/SaveAndRunStepContent-test.tsx View File

@@ -0,0 +1,32 @@
/*
* 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 SaveAndRunStepContent from '../SaveAndRunStepContent';

it('should render correctly', () => {
const wrapper = shallowRender();
expect(wrapper).toMatchSnapshot();
});

function shallowRender() {
return shallow(<SaveAndRunStepContent />);
}

+ 52
- 0
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/ServiceEndpointStepContent-test.tsx View File

@@ -0,0 +1,52 @@
/*
* 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 { Button } from 'sonar-ui-common/components/controls/buttons';
import { click } from 'sonar-ui-common/helpers/testUtils';
import { mockComponent, mockLoggedInUser } from '../../../../helpers/testMocks';
import EditTokenModal from '../../components/EditTokenModal';
import ServiceEndpointStepContent from '../ServiceEndpointStepContent';

it('should render correctly', () => {
const wrapper = shallowRender();
expect(wrapper).toMatchSnapshot();
});

it('should render open/close the token modal when asked to', () => {
const wrapper = shallowRender();
expect(wrapper.exists(EditTokenModal)).toBe(false);

click(wrapper.find(Button));
expect(wrapper.exists(EditTokenModal)).toBe(true);

wrapper
.find(EditTokenModal)
.props()
.onClose();
expect(wrapper.exists(EditTokenModal)).toBe(false);
});

function shallowRender() {
return shallow(
<ServiceEndpointStepContent component={mockComponent()} currentUser={mockLoggedInUser()} />
);
}

+ 128
- 0
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/AzurePipelinesTutorial-test.tsx.snap View File

@@ -0,0 +1,128 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<Fragment>
<div
className="page-header big-spacer-bottom"
>
<h1
className="page-title"
>
onboarding.tutorial.with.azure_pipelines.title
</h1>
</div>
<Step
finished={false}
key="0"
onOpen={[Function]}
open={true}
renderForm={[Function]}
stepNumber={1}
stepTitle="onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title"
/>
<Step
finished={false}
key="1"
onOpen={[Function]}
open={false}
renderForm={[Function]}
stepNumber={2}
stepTitle="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.title"
/>
<Step
finished={false}
key="2"
onOpen={[Function]}
open={false}
renderForm={[Function]}
stepNumber={3}
stepTitle="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.title"
/>
<Step
finished={false}
key="3"
onOpen={[Function]}
open={false}
renderForm={[Function]}
stepNumber={4}
stepTitle="onboarding.tutorial.with.azure_pipelines.SaveAndRun.title"
/>
</Fragment>
`;

exports[`should render correctly: first-step-wrapper 1`] = `
<div
className="boxed-group onboarding-step is-open"
>
<div
className="onboarding-step-number"
>
1
</div>
<div
className="boxed-group-header"
>
<h2>
onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title
</h2>
</div>
<div
className=""
>
<div
className="boxed-group-inner"
>
<div>
<ExtensionInstallationStepContent />
</div>
<Button
className="big-spacer-top spacer-bottom"
onClick={[Function]}
>
continue
</Button>
</div>
</div>
</div>
`;

exports[`should render correctly: last-step-wrapper 1`] = `
<div
className="boxed-group onboarding-step"
>
<div
className="onboarding-step-number"
>
4
</div>
<div
className="boxed-group-header"
>
<h2>
onboarding.tutorial.with.azure_pipelines.SaveAndRun.title
</h2>
</div>
<div
className="boxed-group-inner"
/>
<div
className="hidden"
>
<div
className="boxed-group-inner"
>
<div>
<SaveAndRunStepContent />
</div>
</div>
</div>
</div>
`;

exports[`should render correctly: wrong alm 1`] = `
<Alert
variant="error"
>
onboarding.tutorial.with.azure_pipelines.unsupported
</Alert>
`;

+ 613
- 0
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/BranchAnalysisStepContent-test.tsx.snap View File

@@ -0,0 +1,613 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<Fragment>
<span>
onboarding.build
</span>
<RenderOptions
name="buildTechnology"
onCheck={[Function]}
optionLabelKey="onboarding.build"
options={
Array [
"dotnet",
"maven",
"gradle",
"other",
]
}
/>
<ol
className="list-styled big-spacer-top"
/>
</Fragment>
`;

exports[`should render correctly: dotnet 1`] = `
<Fragment>
<span>
onboarding.build
</span>
<RenderOptions
checked="dotnet"
name="buildTechnology"
onCheck={[Function]}
optionLabelKey="onboarding.build"
options={
Array [
"dotnet",
"maven",
"gradle",
"other",
]
}
/>
<ol
className="list-styled big-spacer-top"
>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"pipeline",
"task",
"before",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
/>
</li>
<ul
className="list-styled"
>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"endpoint",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint"
/>
</li>
<li>
<FormattedMessage
defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
values={
Object {
"run_analysis_value": <strong>
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.dotnet
</strong>,
"section": <strong>
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section
</strong>,
}
}
/>
</li>
<li>
<FormattedMessage
defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence"
id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence"
values={
Object {
"button": <ClipboardIconButton
copyValue="my-project"
/>,
"key": <code
className="rule"
>
my-project
</code>,
}
}
/>
</li>
</ul>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"task",
"after",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run"
/>
</li>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"task",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg"
/>
<Alert
className="spacer-top"
variant="info"
>
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1
</Alert>
</li>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"tab",
"continuous_integration",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration"
/>
</li>
<hr />
<FormattedMessage
defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection"
id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection"
values={
Object {
"link": <Link
onlyActiveOnIndex={false}
style={Object {}}
target="_blank"
to="/documentation/analysis/azuredevops-integration/"
>
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection.link
</Link>,
}
}
/>
</ol>
</Fragment>
`;

exports[`should render correctly: gradle 1`] = `
<Fragment>
<span>
onboarding.build
</span>
<RenderOptions
checked="gradle"
name="buildTechnology"
onCheck={[Function]}
optionLabelKey="onboarding.build"
options={
Array [
"dotnet",
"maven",
"gradle",
"other",
]
}
/>
<ol
className="list-styled big-spacer-top"
>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"pipeline",
"task",
"before",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
/>
</li>
<ul
className="list-styled"
>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"endpoint",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint"
/>
</li>
<li>
<FormattedMessage
defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
values={
Object {
"run_analysis_value": <strong>
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.gradle
</strong>,
"section": <strong>
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section
</strong>,
}
}
/>
</li>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"section",
"properties",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties"
/>
:
<CodeSnippet
snippet="# Additional properties that will be passed to the scanner,
# Put one key=value per line, example:
# sonar.exclusions=**/*.bin
sonar.projectKey=my-project"
/>
</li>
</ul>
<li>
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.onboarding.build.gradle
</li>
<ul
className="list-styled big-spacer-bottom"
>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"section",
"option",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings"
/>
</li>
</ul>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"task",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg"
/>
<Alert
className="spacer-top"
variant="info"
>
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1
</Alert>
</li>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"tab",
"continuous_integration",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration"
/>
</li>
<hr />
<FormattedMessage
defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection"
id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection"
values={
Object {
"link": <Link
onlyActiveOnIndex={false}
style={Object {}}
target="_blank"
to="/documentation/analysis/azuredevops-integration/"
>
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection.link
</Link>,
}
}
/>
</ol>
</Fragment>
`;

exports[`should render correctly: maven 1`] = `
<Fragment>
<span>
onboarding.build
</span>
<RenderOptions
checked="maven"
name="buildTechnology"
onCheck={[Function]}
optionLabelKey="onboarding.build"
options={
Array [
"dotnet",
"maven",
"gradle",
"other",
]
}
/>
<ol
className="list-styled big-spacer-top"
>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"pipeline",
"task",
"before",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
/>
</li>
<ul
className="list-styled"
>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"endpoint",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint"
/>
</li>
<li>
<FormattedMessage
defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
values={
Object {
"run_analysis_value": <strong>
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.maven
</strong>,
"section": <strong>
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section
</strong>,
}
}
/>
</li>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"section",
"properties",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties"
/>
:
<CodeSnippet
snippet="# Additional properties that will be passed to the scanner,
# Put one key=value per line, example:
# sonar.exclusions=**/*.bin
sonar.projectKey=my-project"
/>
</li>
</ul>
<li>
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.onboarding.build.maven
</li>
<ul
className="list-styled big-spacer-bottom"
>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"section",
"option",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings"
/>
</li>
</ul>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"task",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg"
/>
<Alert
className="spacer-top"
variant="info"
>
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1
</Alert>
</li>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"tab",
"continuous_integration",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration"
/>
</li>
<hr />
<FormattedMessage
defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection"
id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection"
values={
Object {
"link": <Link
onlyActiveOnIndex={false}
style={Object {}}
target="_blank"
to="/documentation/analysis/azuredevops-integration/"
>
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection.link
</Link>,
}
}
/>
</ol>
</Fragment>
`;

exports[`should render correctly: other 1`] = `
<Fragment>
<span>
onboarding.build
</span>
<RenderOptions
checked="other"
name="buildTechnology"
onCheck={[Function]}
optionLabelKey="onboarding.build"
options={
Array [
"dotnet",
"maven",
"gradle",
"other",
]
}
/>
<ol
className="list-styled big-spacer-top"
>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"pipeline",
"task",
"before",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
/>
</li>
<ul
className="list-styled"
>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"endpoint",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint"
/>
</li>
<li>
<FormattedMessage
defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
values={
Object {
"run_analysis_value": <strong>
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.other
</strong>,
"section": <strong>
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section
</strong>,
}
}
/>
</li>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"mode",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.manual"
/>
</li>
<li>
<FormattedMessage
defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence"
id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence"
values={
Object {
"button": <ClipboardIconButton
copyValue="my-project"
/>,
"key": <code
className="rule"
>
my-project
</code>,
}
}
/>
</li>
</ul>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"task",
"after",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run"
/>
</li>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"task",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg"
/>
<Alert
className="spacer-top"
variant="info"
>
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1
</Alert>
</li>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"tab",
"continuous_integration",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration"
/>
</li>
<hr />
<FormattedMessage
defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection"
id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection"
values={
Object {
"link": <Link
onlyActiveOnIndex={false}
style={Object {}}
target="_blank"
to="/documentation/analysis/azuredevops-integration/"
>
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection.link
</Link>,
}
}
/>
</ol>
</Fragment>
`;

+ 24
- 0
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/ExtensionInstallationStepContent-test.tsx.snap View File

@@ -0,0 +1,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<span>
<FormattedMessage
defaultMessage="onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence"
id="onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence"
values={
Object {
"button": <strong>
onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence.button
</strong>,
"link": <a
href="https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarqube"
rel="noopener noreferrer"
target="_blank"
>
onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence.link
</a>,
}
}
/>
</span>
`;

+ 54
- 0
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/SaveAndRunStepContent-test.tsx.snap View File

@@ -0,0 +1,54 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<Fragment>
<div
className="display-flex-row big-spacer-bottom"
>
<div>
<img
alt=""
className="big-spacer-right"
src="/images/tutorials/commit.svg"
width={30}
/>
</div>
<div>
<p
className="little-spacer-bottom"
>
<strong>
onboarding.tutorial.with.azure_pipelines.SaveAndRun.commit
</strong>
</p>
<p>
onboarding.tutorial.with.azure_pipelines.SaveAndRun.commit.why
</p>
</div>
</div>
<div
className="display-flex-row"
>
<div>
<img
alt=""
className="big-spacer-right"
src="/images/tutorials/refresh.svg"
width={30}
/>
</div>
<div>
<p
className="little-spacer-bottom"
>
<strong>
onboarding.tutorial.with.azure_pipelines.SaveAndRun.refresh
</strong>
</p>
<p>
onboarding.tutorial.with.azure_pipelines.SaveAndRun.refresh.why
</p>
</div>
</div>
</Fragment>
`;

+ 66
- 0
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/ServiceEndpointStepContent-test.tsx.snap View File

@@ -0,0 +1,66 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<Fragment>
<ol
className="list-styled"
>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"menu",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step1"
/>
</li>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"type",
]
}
translationKey="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step2"
/>
</li>
<li>
onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step3.sentence
</li>
<li>
<FormattedMessage
defaultMessage="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step4.sentence"
id="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step4.sentence"
values={
Object {
"button": <ClipboardIconButton
copyValue="http://localhost"
/>,
"url": <code
className="rule"
>
http://localhost
</code>,
}
}
/>
</li>
<li>
<span>
onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step5.sentence
:
</span>
<Button
className="spacer-left"
onClick={[Function]}
>
onboarding.token.generate_token
</Button>
</li>
<li>
onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step6.sentence
</li>
</ol>
</Fragment>
`;

+ 4
- 2
server/sonar-web/src/main/js/components/tutorials/gitlabci/GitLabCITutorial.tsx View File

@@ -20,8 +20,10 @@
import * as React from 'react';
import { Alert } from 'sonar-ui-common/components/ui/Alert';
import { translate } from 'sonar-ui-common/helpers/l10n';
import { isProjectGitLabBindingResponse } from '../../../helpers/alm-settings';
import { ProjectAlmBindingResponse } from '../../../types/alm-settings';
import {
isProjectGitLabBindingResponse,
ProjectAlmBindingResponse
} from '../../../types/alm-settings';
import EnvironmentVariablesStep from './EnvironmentVariablesStep';
import ProjectKeyStep from './ProjectKeyStep';
import { BuildTools } from './types';

+ 6
- 5
server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx View File

@@ -21,13 +21,14 @@ import * as React from 'react';
import { connect } from 'react-redux';
import { Alert } from 'sonar-ui-common/components/ui/Alert';
import { translate } from 'sonar-ui-common/helpers/l10n';
import {
isProjectBitbucketBindingResponse,
isProjectGitHubBindingResponse
} from '../../../helpers/alm-settings';
import { getCurrentUserSetting, Store } from '../../../store/rootReducer';
import { setCurrentUserSetting } from '../../../store/users';
import { AlmBindingDefinition, ProjectAlmBindingResponse } from '../../../types/alm-settings';
import {
AlmBindingDefinition,
isProjectBitbucketBindingResponse,
isProjectGitHubBindingResponse,
ProjectAlmBindingResponse
} from '../../../types/alm-settings';
import JenkinsfileStep from './JenkinsfileStep';
import MultiBranchPipelineStep from './MultiBranchPipelineStep';
import PreRequisitesStep from './PreRequisitesStep';

+ 2
- 4
server/sonar-web/src/main/js/components/tutorials/jenkins/MultiBranchPipelineStep.tsx View File

@@ -21,12 +21,10 @@ import * as React from 'react';
import { Button } from 'sonar-ui-common/components/controls/buttons';
import { translate } from 'sonar-ui-common/helpers/l10n';
import {
AlmBindingDefinition,
isGithubBindingDefinition,
isProjectBitbucketBindingResponse,
isProjectGitHubBindingResponse
} from '../../../helpers/alm-settings';
import {
AlmBindingDefinition,
isProjectGitHubBindingResponse,
ProjectBitbucketBindingResponse,
ProjectGitHubBindingResponse
} from '../../../types/alm-settings';

+ 2
- 4
server/sonar-web/src/main/js/components/tutorials/jenkins/WebhookStep.tsx View File

@@ -21,13 +21,11 @@ import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { Button, ButtonLink } from 'sonar-ui-common/components/controls/buttons';
import { translate } from 'sonar-ui-common/helpers/l10n';
import {
isBitbucketBindingDefinition,
isGithubBindingDefinition
} from '../../../helpers/alm-settings';
import {
AlmBindingDefinition,
AlmKeys,
isBitbucketBindingDefinition,
isGithubBindingDefinition,
ProjectAlmBindingResponse
} from '../../../types/alm-settings';
import Step from '../components/Step';

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

@@ -20,7 +20,8 @@
export enum TutorialModes {
Manual = 'manual',
Jenkins = 'jenkins',
GitLabCI = 'gitlab-ci'
GitLabCI = 'gitlab-ci',
AzurePipelines = 'azure-pipelines'
}

export enum BuildTools {

+ 0
- 67
server/sonar-web/src/main/js/helpers/__tests__/alm-settings-test.ts View File

@@ -1,67 +0,0 @@
/*
* 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 { AlmKeys } from '../../types/alm-settings';
import {
isBitbucketBindingDefinition,
isGithubBindingDefinition,
isProjectBitbucketBindingResponse,
isProjectGitHubBindingResponse
} from '../alm-settings';
import {
mockBitbucketBindingDefinition,
mockGithubBindingDefinition,
mockProjectAlmBindingResponse,
mockProjectBitbucketBindingResponse,
mockProjectGithubBindingResponse
} from '../mocks/alm-settings';

/* eslint-disable sonarjs/no-duplicate-string */

describe('isProjectBitbucketBindingResponse', () => {
it('works as expected', () => {
expect(isProjectBitbucketBindingResponse(mockProjectAlmBindingResponse())).toBe(false);
expect(isProjectBitbucketBindingResponse(mockProjectBitbucketBindingResponse())).toBe(true);
});
});

describe('isBitbucketBindingDefinition', () => {
it('works as expected', () => {
expect(isBitbucketBindingDefinition(undefined)).toBe(false);
expect(isBitbucketBindingDefinition(mockGithubBindingDefinition())).toBe(false);
expect(isBitbucketBindingDefinition(mockBitbucketBindingDefinition())).toBe(true);
});
});

describe('isProjectGithubBindingResponse', () => {
it('works as expected', () => {
expect(
isProjectGitHubBindingResponse(mockProjectAlmBindingResponse({ alm: AlmKeys.Azure }))
).toBe(false);
expect(isProjectGitHubBindingResponse(mockProjectGithubBindingResponse())).toBe(true);
});
});

describe('isGithubBindingDefinition', () => {
it('works as expected', () => {
expect(isGithubBindingDefinition(undefined)).toBe(false);
expect(isGithubBindingDefinition(mockBitbucketBindingDefinition())).toBe(false);
expect(isGithubBindingDefinition(mockGithubBindingDefinition())).toBe(true);
});
});

+ 0
- 66
server/sonar-web/src/main/js/helpers/alm-settings.ts View File

@@ -1,66 +0,0 @@
/*
* 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 {
AlmBindingDefinition,
AlmKeys,
BitbucketBindingDefinition,
GithubBindingDefinition,
ProjectAlmBindingResponse,
ProjectBitbucketBindingResponse,
ProjectGitHubBindingResponse,
ProjectGitLabBindingResponse
} from '../types/alm-settings';

export function isProjectBitbucketBindingResponse(
binding: ProjectAlmBindingResponse
): binding is ProjectBitbucketBindingResponse {
return binding.alm === AlmKeys.Bitbucket;
}

export function isProjectGitHubBindingResponse(
binding: ProjectAlmBindingResponse
): binding is ProjectGitHubBindingResponse {
return binding.alm === AlmKeys.GitHub;
}

export function isProjectGitLabBindingResponse(
binding: ProjectAlmBindingResponse
): binding is ProjectGitLabBindingResponse {
return binding.alm === AlmKeys.GitLab;
}

export function isBitbucketBindingDefinition(
binding?: AlmBindingDefinition & { url?: string; personalAccessToken?: string }
): binding is BitbucketBindingDefinition {
return (
binding !== undefined && binding.url !== undefined && binding.personalAccessToken !== undefined
);
}

export function isGithubBindingDefinition(
binding?: AlmBindingDefinition & { appId?: string; privateKey?: string; url?: string }
): binding is GithubBindingDefinition {
return (
binding !== undefined &&
binding.appId !== undefined &&
binding.privateKey !== undefined &&
binding.url !== undefined
);
}

+ 14
- 0
server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts View File

@@ -27,6 +27,7 @@ import {
GithubBindingDefinition,
GitlabBindingDefinition,
ProjectAlmBindingResponse,
ProjectAzureBindingResponse,
ProjectBitbucketBindingResponse,
ProjectGitHubBindingResponse,
ProjectGitLabBindingResponse
@@ -132,6 +133,19 @@ export function mockProjectGitLabBindingResponse(
};
}

export function mockProjectAzureBindingResponse(
overrides: Partial<ProjectAzureBindingResponse> = {}
): ProjectAzureBindingResponse {
return {
alm: AlmKeys.Azure,
key: 'foo',
slug: 'PROJECT_NAME',
repository: 'REPOSITORY_NAME',
url: 'https://ado.my_company.com/mycollection',
...overrides
};
}

export function mockAlmSettingsBindingStatus(
overrides: Partial<AlmSettingsBindingStatus>
): AlmSettingsBindingStatus {

+ 50
- 0
server/sonar-web/src/main/js/types/alm-settings.ts View File

@@ -60,6 +60,13 @@ export interface ProjectAlmBindingResponse {
summaryCommentEnabled?: boolean;
}

export interface ProjectAzureBindingResponse extends ProjectAlmBindingResponse {
alm: AlmKeys.Azure;
repository: string;
slug: string;
url: string;
}

export interface ProjectBitbucketBindingResponse extends ProjectAlmBindingResponse {
alm: AlmKeys.Bitbucket;
repository: string;
@@ -126,3 +133,46 @@ export enum AlmSettingsBindingStatusType {
Failure,
Warning
}

export function isProjectBitbucketBindingResponse(
binding: ProjectAlmBindingResponse
): binding is ProjectBitbucketBindingResponse {
return binding.alm === AlmKeys.Bitbucket;
}

export function isProjectGitHubBindingResponse(
binding: ProjectAlmBindingResponse
): binding is ProjectGitHubBindingResponse {
return binding.alm === AlmKeys.GitHub;
}

export function isProjectGitLabBindingResponse(
binding: ProjectAlmBindingResponse
): binding is ProjectGitLabBindingResponse {
return binding.alm === AlmKeys.GitLab;
}

export function isProjectAzureBindingResponse(
binding: ProjectAlmBindingResponse
): binding is ProjectAzureBindingResponse {
return binding.alm === AlmKeys.Azure;
}

export function isBitbucketBindingDefinition(
binding?: AlmBindingDefinition & { url?: string; personalAccessToken?: string }
): binding is BitbucketBindingDefinition {
return (
binding !== undefined && binding.url !== undefined && binding.personalAccessToken !== undefined
);
}

export function isGithubBindingDefinition(
binding?: AlmBindingDefinition & { appId?: string; privateKey?: string; url?: string }
): binding is GithubBindingDefinition {
return (
binding !== undefined &&
binding.appId !== undefined &&
binding.privateKey !== undefined &&
binding.url !== undefined
);
}

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

@@ -3447,6 +3447,7 @@ onboarding.tutorial.choose_method=How do you want to analyze your repository?
onboarding.tutorial.choose_method.manual=Manually
onboarding.tutorial.choose_method.jenkins=With Jenkins
onboarding.tutorial.choose_method.gitlab_ci=With GitLab CI
onboarding.tutorial.choose_method.azure_pipelines=With Azure Pipelines

onboarding.tutorial.with.gitlab_ci.title=Analyze your project with GitLab CI
onboarding.tutorial.with.gitlab_ci.unsupported=This tutorial is only available for projects bound to GitLab.
@@ -3598,6 +3599,63 @@ onboarding.tutorial.with.jenkins.commit.why=Each new push you make on your branc
onboarding.tutorial.with.jenkins.refresh=This page will then refresh with your analysis results.
onboarding.tutorial.with.jenkins.refresh.why=If the page doesn't refresh after a while, please double-check the analysis configuration.

onboarding.tutorial.with.azure_pipelines.title=Analyze your project with Azure DevOps Pipelines
onboarding.tutorial.with.azure_pipelines.unsupported=This tutorial is only available for projects bound to Azure DevOps Server.
onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title=Install SonarQube extension for Azure DevOps Server
onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence=From your Azure DevOps Server instance, navigate to the Visual Studio Marketplace and install the {link} by clicking the {button} button.
onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence.link=SonarQube extension
onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence.button=Get it free
onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.title=Add a new SonarQube Service Endpoint
onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step1.sentence=In Azure DevOps Server, go to {menu}
onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step1.sentence.menu=Project settings > Service connections
onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step2.sentence=Add a new service connection of type {type}
onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step2.sentence.type=SonarQube
onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step3.sentence=Enter a memorable connection name
onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step4.sentence=Enter your SonarQube server url: {url} {button}
onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step5.sentence=Enter an existing token, or a newly generated one
onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step6.sentence=Create the service connection
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.title=Configure Branch analysis
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence=In Azure DevOps Server, create or edit a {pipeline} and add a new {task} task {before} your build task
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.pipeline=Build Pipeline
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.task=Prepare Analysis Configuration
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.before=before
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence=Select the {endpoint} you created in Step 2
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence.endpoint=SonarQube server endpoint
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis=Under {section}, select {run_analysis_value}
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section=Choose the way to run the analysis
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.dotnet=Integrate with MSBuild
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.maven=Integrate with Maven or Gradle
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.gradle=Integrate with Maven or Gradle
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.other=Use standalone scanner
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.manual.sentence=Select the {mode} mode
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.manual.sentence.mode=Manually provide configuration
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.sentence=Add a new {task} task {after} your build task
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.sentence.task=Run Code Analysis
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.sentence.after=after
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence=In the project key field, enter {key} {button}
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties.sentence=Expand the {section} and replace the {properties} with the following snippet
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties.sentence.section=Advanced section
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties.sentence.properties=Additional Properties
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java=Edit or add a new {0} task
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings.sentence=Under {section}, check {option}
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings.sentence.section=Code Analysis
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings.sentence.option=Run SonarQube or SonarCloud Analysis
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence=Add a new {task} task to publish SonarQube's Quality Gate results on your build pipeline summary
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1=This task may increase your build time as your pipeline will have to wait for SonarQube to process the analysis report. It is highly recommended but optional.
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence.task=Publish Quality Gate Result
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence=Under the {tab} tab of your pipeline, check {continuous_integration} and select all the branches for which you want the SonarQube analysis to run automatically
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence.tab=Triggers
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence.continuous_integration=Enable continuous integration
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection=To make sure your Pull Requests are analyzed automatically and aren't merged when they're failing their quality gate, check out the {link}.
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection.link=documentation
onboarding.tutorial.with.azure_pipelines.SaveAndRun.title=Save and run your pipeline
onboarding.tutorial.with.azure_pipelines.SaveAndRun.commit=Save your pipeline and push your code to start the analysis
onboarding.tutorial.with.azure_pipelines.SaveAndRun.commit.why=Each new push you make on your branches will trigger a new analysis in SonarQube
onboarding.tutorial.with.azure_pipelines.SaveAndRun.refresh=This page will then refresh with your analysis results
onboarding.tutorial.with.azure_pipelines.SaveAndRun.refresh.why=If this page doesn't refresh after a while, please double-check your pipeline configuration
onboarding.tutorial.with.azure_pipelines.SaveAndRun.then={what} Checkout our {link} to make sure SonarQube analyze and decorate all of your Pull Requests
onboarding.tutorial.with.azure_pipelines.SaveAndRun.then.what=Then what?
onboarding.tutorial.with.azure_pipelines.SaveAndRun.then.link=Azure integration documentation
#------------------------------------------------------------------------------
#
# BRANCHES

Loading…
Cancel
Save