From: 7PH Date: Tue, 4 Jun 2024 12:45:24 +0000 (+0200) Subject: SONAR-22341 Add AutoConfig option to SQ onboarding tutorials for C/C++ X-Git-Tag: 10.6.0.92116~49 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=32a9a1730baad588acb37495a99d6384b973618c;p=sonarqube.git SONAR-22341 Add AutoConfig option to SQ onboarding tutorials for C/C++ --- diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/AzurePipelinesTutorial.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/AzurePipelinesTutorial.tsx index 560eb393f6a..8f024dd20d1 100644 --- a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/AzurePipelinesTutorial.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/AzurePipelinesTutorial.tsx @@ -24,6 +24,7 @@ import { AlmKeys } from '../../../types/alm-settings'; import { Component } from '../../../types/types'; import { LoggedInUser } from '../../../types/users'; import AllSet from '../components/AllSet'; +import { TutorialConfig } from '../types'; import BranchAnalysisStepContent from './BranchAnalysisStepContent'; import ExtensionInstallationStepContent from './ExtensionInstallationStepContent'; import ServiceEndpointStepContent from './ServiceEndpointStepContent'; @@ -44,8 +45,14 @@ export enum Steps { export default function AzurePipelinesTutorial(props: AzurePipelinesTutorialProps) { const { alm, baseUrl, component, currentUser, willRefreshAutomatically } = props; + + const [config, setConfig] = React.useState({}); const [done, setDone] = React.useState(false); + React.useEffect(() => { + setDone(Boolean(config.buildTool)); + }, [config.buildTool]); + return ( <> {translate('onboarding.tutorial.with.azure_pipelines.title')} @@ -76,7 +83,7 @@ export default function AzurePipelinesTutorial(props: AzurePipelinesTutorialProp `onboarding.tutorial.with.azure_pipelines.${Steps.BranchAnalysis}.title`, )} > - + {done && ( diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/BranchAnalysisStepContent.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/BranchAnalysisStepContent.tsx index e4f852ffa28..811e2ce5829 100644 --- a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/BranchAnalysisStepContent.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/BranchAnalysisStepContent.tsx @@ -19,50 +19,32 @@ */ import * as React from 'react'; import withLanguagesContext from '../../../app/components/languages/withLanguagesContext'; -import { translate } from '../../../helpers/l10n'; import { Languages } from '../../../types/languages'; import { Component } from '../../../types/types'; -import RenderOptions from '../components/RenderOptions'; -import { BuildTools } from '../types'; +import BuildConfigSelection from '../components/BuildConfigSelection'; +import { TutorialConfig, TutorialModes } from '../types'; import AnalysisCommand from './commands/AnalysisCommand'; export interface BranchesAnalysisStepProps { component: Component; + config: TutorialConfig; languages: Languages; - onDone: (done: boolean) => void; + setConfig: (config: TutorialConfig) => void; } -const BUILD_TOOLS_ORDERED: Array = [ - BuildTools.DotNet, - BuildTools.Maven, - BuildTools.Gradle, - BuildTools.CFamily, - BuildTools.Other, -]; - export function BranchAnalysisStepContent(props: BranchesAnalysisStepProps) { - const { component, languages } = props; + const { config, setConfig, component, languages } = props; - const [buildTechnology, setBuildTechnology] = React.useState(); - const buildToolsList = languages['c'] - ? BUILD_TOOLS_ORDERED - : BUILD_TOOLS_ORDERED.filter((t) => t !== BuildTools.CFamily); return ( <> - {translate('onboarding.build')} - setBuildTechnology(value as BuildTools)} - optionLabelKey="onboarding.build" - options={buildToolsList} - /> - + + ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-it.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-it.tsx index 73dde6800bb..3f58c95ae0c 100644 --- a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-it.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-it.tsx @@ -53,10 +53,10 @@ it('should render correctly and allow token generation', async () => { screen.getByRole('heading', { name: 'onboarding.tutorial.with.azure_pipelines.title' }), ).toBeInTheDocument(); - //// Default step. + // Default step. assertDefaultStepIsCorrectlyRendered(); - //// Token step. + // Token step. assertServiceEndpointStepIsCorrectlyRendered(); // Generate a token. @@ -73,36 +73,50 @@ it('should render correctly and allow token generation', async () => { ).toBeInTheDocument(); await clickButton(user, 'continue', modal); - //// Analysis step: .NET + // Analysis step: .NET await user.click(getTutorialBuildButtons().dotnetBuildButton.get()); assertDotNetStepIsCorrectlyRendered(); - //// Analysis step: Maven + // Analysis step: Maven await user.click(getTutorialBuildButtons().mavenBuildButton.get()); assertMavenStepIsCorrectlyRendered(); - //// Analysis step: Gradle + // Analysis step: Gradle await user.click(getTutorialBuildButtons().gradleBuildButton.get()); assertGradleStepIsCorrectlyRendered(); - //// Analysis step: C Family - await user.click(getTutorialBuildButtons().cFamilyBuildButton.get()); - - // OS's + // Analysis step: C Family + await user.click(getTutorialBuildButtons().cppBuildButton.get()); + // Default: Automatic configuration + // expect linux/win/macos buttons not to be present + expect(getTutorialBuildButtons().linuxButton.query()).not.toBeInTheDocument(); + expect(getTutorialBuildButtons().windowsButton.query()).not.toBeInTheDocument(); + expect(getTutorialBuildButtons().macosButton.query()).not.toBeInTheDocument(); + assertAutomaticCppStepIsCorrectlyRendered(); + + // Switch to manual configuration + await user.click(getTutorialBuildButtons().autoConfigManual.get()); await user.click(getTutorialBuildButtons().linuxButton.get()); - assertCFamilyStepIsCorrectlyRendered(OSs.Linux); - + assertManualCppStepIsCorrectlyRendered(OSs.Linux); await user.click(getTutorialBuildButtons().windowsButton.get()); - assertCFamilyStepIsCorrectlyRendered(OSs.Windows); + assertObjCStepIsCorrectlyRendered(OSs.Windows); + await user.click(getTutorialBuildButtons().macosButton.get()); + assertObjCStepIsCorrectlyRendered(OSs.MacOS); + // Analysis step: C Family + await user.click(getTutorialBuildButtons().objCBuildButton.get()); + await user.click(getTutorialBuildButtons().linuxButton.get()); + assertObjCStepIsCorrectlyRendered(OSs.Linux); + await user.click(getTutorialBuildButtons().windowsButton.get()); + assertObjCStepIsCorrectlyRendered(OSs.Windows); await user.click(getTutorialBuildButtons().macosButton.get()); - assertCFamilyStepIsCorrectlyRendered(OSs.MacOS); + assertObjCStepIsCorrectlyRendered(OSs.MacOS); - //// Analysis step: Other + // Analysis step: Other await user.click(getTutorialBuildButtons().otherBuildButton.get()); assertOtherStepIsCorrectlyRendered(); - //// Finish tutorial + // Finish tutorial assertFinishStepIsCorrectlyRendered(); }); @@ -110,7 +124,7 @@ it('should not offer CFamily analysis if the language is not available', () => { renderAzurePipelinesTutorial(undefined, { languages: {} }); expect(getTutorialBuildButtons().dotnetBuildButton.get()).toBeInTheDocument(); - expect(getTutorialBuildButtons().cFamilyBuildButton.query()).not.toBeInTheDocument(); + expect(getTutorialBuildButtons().cppBuildButton.query()).not.toBeInTheDocument(); }); function assertDefaultStepIsCorrectlyRendered() { @@ -151,14 +165,29 @@ function assertGradleStepIsCorrectlyRendered() { expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('gradle, copy additional properties'); } -function assertCFamilyStepIsCorrectlyRendered(os: string) { - expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(`cfamily ${os}, copy shell script`); +function assertObjCStepIsCorrectlyRendered(os: string) { + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(`objectivec ${os}, copy shell script`); + expect(getCopyToClipboardValue(1, 'Copy to clipboard')).toBe('foo'); + expect(getCopyToClipboardValue(2, 'Copy to clipboard')).toMatchSnapshot( + `objectivec ${os}, copy additional properties`, + ); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( + `objectivec ${os}, copy build-wrapper command`, + ); +} + +function assertAutomaticCppStepIsCorrectlyRendered() { + assertOtherStepIsCorrectlyRendered(); +} + +function assertManualCppStepIsCorrectlyRendered(os: string) { + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(`manual-cpp ${os}, copy shell script`); expect(getCopyToClipboardValue(1, 'Copy to clipboard')).toBe('foo'); expect(getCopyToClipboardValue(2, 'Copy to clipboard')).toMatchSnapshot( - `cfamily ${os}, copy additional properties`, + `manual-cpp ${os}, copy additional properties`, ); expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( - `cfamily ${os}, copy build-wrapper command`, + `manual-cpp ${os}, copy build-wrapper command`, ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/AzurePipelinesTutorial-it.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/AzurePipelinesTutorial-it.tsx.snap index bb473275129..3e7f2240b79 100644 --- a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/AzurePipelinesTutorial-it.tsx.snap +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/AzurePipelinesTutorial-it.tsx.snap @@ -1,46 +1,73 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should render correctly and allow token generation: cfamily linux, copy additional properties 1`] = `"sonar.cfamily.compile-commands=bw-output/compile_commands.json"`; +exports[`should render correctly and allow token generation: gradle, copy additional properties 1`] = ` +"# Additional properties that will be passed to the scanner, +# Put one key=value per line, example: +# sonar.exclusions=**/*.bin +sonar.projectKey=foo +sonar.projectName=MyProject +" +`; + +exports[`should render correctly and allow token generation: manual-cpp linux, copy additional properties 1`] = `"sonar.cfamily.compile-commands=bw-output/compile_commands.json"`; -exports[`should render correctly and allow token generation: cfamily linux, copy build-wrapper command 1`] = `"./build-wrapper-linux-x86/build-wrapper-linux-x86-64 --out-dir bw-output "`; +exports[`should render correctly and allow token generation: manual-cpp linux, copy build-wrapper command 1`] = `"./build-wrapper-linux-x86/build-wrapper-linux-x86-64 --out-dir bw-output "`; -exports[`should render correctly and allow token generation: cfamily linux, copy shell script 1`] = ` +exports[`should render correctly and allow token generation: manual-cpp linux, copy shell script 1`] = ` "curl 'http://localhost/static/cpp/build-wrapper-linux-x86.zip' --output build-wrapper.zip unzip build-wrapper.zip" `; -exports[`should render correctly and allow token generation: cfamily mac, copy additional properties 1`] = `"sonar.cfamily.compile-commands=bw-output/compile_commands.json"`; +exports[`should render correctly and allow token generation: maven, copy additional properties 1`] = ` +"# Additional properties that will be passed to the scanner, +# Put one key=value per line, example: +# sonar.exclusions=**/*.bin +sonar.projectKey=foo +sonar.projectName=MyProject +" +`; + +exports[`should render correctly and allow token generation: objectivec linux, copy additional properties 1`] = `"sonar.cfamily.compile-commands=bw-output/compile_commands.json"`; + +exports[`should render correctly and allow token generation: objectivec linux, copy build-wrapper command 1`] = `"./build-wrapper-linux-x86/build-wrapper-linux-x86-64 --out-dir bw-output "`; + +exports[`should render correctly and allow token generation: objectivec linux, copy shell script 1`] = ` +"curl 'http://localhost/static/cpp/build-wrapper-linux-x86.zip' --output build-wrapper.zip +unzip build-wrapper.zip" +`; + +exports[`should render correctly and allow token generation: objectivec mac, copy additional properties 1`] = `"sonar.cfamily.compile-commands=bw-output/compile_commands.json"`; + +exports[`should render correctly and allow token generation: objectivec mac, copy additional properties 2`] = `"sonar.cfamily.compile-commands=bw-output/compile_commands.json"`; -exports[`should render correctly and allow token generation: cfamily mac, copy build-wrapper command 1`] = `"./build-wrapper-macosx-x86/build-wrapper-macosx-x86 --out-dir bw-output "`; +exports[`should render correctly and allow token generation: objectivec mac, copy build-wrapper command 1`] = `"./build-wrapper-macosx-x86/build-wrapper-macosx-x86 --out-dir bw-output "`; -exports[`should render correctly and allow token generation: cfamily mac, copy shell script 1`] = ` +exports[`should render correctly and allow token generation: objectivec mac, copy build-wrapper command 2`] = `"./build-wrapper-macosx-x86/build-wrapper-macosx-x86 --out-dir bw-output "`; + +exports[`should render correctly and allow token generation: objectivec mac, copy shell script 1`] = ` "curl 'http://localhost/static/cpp/build-wrapper-macosx-x86.zip' --output build-wrapper.zip unzip build-wrapper.zip" `; -exports[`should render correctly and allow token generation: cfamily win, copy additional properties 1`] = `"sonar.cfamily.compile-commands=bw-output/compile_commands.json"`; +exports[`should render correctly and allow token generation: objectivec mac, copy shell script 2`] = ` +"curl 'http://localhost/static/cpp/build-wrapper-macosx-x86.zip' --output build-wrapper.zip +unzip build-wrapper.zip" +`; + +exports[`should render correctly and allow token generation: objectivec win, copy additional properties 1`] = `"sonar.cfamily.compile-commands=bw-output/compile_commands.json"`; + +exports[`should render correctly and allow token generation: objectivec win, copy additional properties 2`] = `"sonar.cfamily.compile-commands=bw-output/compile_commands.json"`; + +exports[`should render correctly and allow token generation: objectivec win, copy build-wrapper command 1`] = `"build-wrapper-win-x86/build-wrapper-win-x86-64.exe --out-dir bw-output "`; -exports[`should render correctly and allow token generation: cfamily win, copy build-wrapper command 1`] = `"build-wrapper-win-x86/build-wrapper-win-x86-64.exe --out-dir bw-output "`; +exports[`should render correctly and allow token generation: objectivec win, copy build-wrapper command 2`] = `"build-wrapper-win-x86/build-wrapper-win-x86-64.exe --out-dir bw-output "`; -exports[`should render correctly and allow token generation: cfamily win, copy shell script 1`] = ` +exports[`should render correctly and allow token generation: objectivec win, copy shell script 1`] = ` "Invoke-WebRequest -Uri 'http://localhost/static/cpp/build-wrapper-win-x86.zip' -OutFile 'build-wrapper.zip' Expand-Archive -Path 'build-wrapper.zip' -DestinationPath '.'" `; -exports[`should render correctly and allow token generation: gradle, copy additional properties 1`] = ` -"# Additional properties that will be passed to the scanner, -# Put one key=value per line, example: -# sonar.exclusions=**/*.bin -sonar.projectKey=foo -sonar.projectName=MyProject -" -`; - -exports[`should render correctly and allow token generation: maven, copy additional properties 1`] = ` -"# Additional properties that will be passed to the scanner, -# Put one key=value per line, example: -# sonar.exclusions=**/*.bin -sonar.projectKey=foo -sonar.projectName=MyProject -" +exports[`should render correctly and allow token generation: objectivec win, copy shell script 2`] = ` +"Invoke-WebRequest -Uri 'http://localhost/static/cpp/build-wrapper-win-x86.zip' -OutFile 'build-wrapper.zip' +Expand-Archive -Path 'build-wrapper.zip' -DestinationPath '.'" `; diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/AnalysisCommand.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/AnalysisCommand.tsx index d87b5abfcbe..32d77e59a03 100644 --- a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/AnalysisCommand.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/AnalysisCommand.tsx @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { BuildTools } from '../../types'; +import { BuildTools, TutorialConfig } from '../../types'; import ClangGCC from './ClangGCC'; import DotNet from './DotNet'; import JavaGradle from './JavaGradle'; @@ -26,24 +26,19 @@ import JavaMaven from './JavaMaven'; import Other from './Other'; export interface AnalysisCommandProps { - buildTool?: BuildTools; - onStepValidationChange: (isValid: boolean) => void; + config: TutorialConfig; projectKey: string; projectName: string; } export default function AnalysisCommand(props: AnalysisCommandProps) { - const { buildTool, onStepValidationChange, projectKey, projectName } = props; - - React.useEffect(() => { - if (buildTool && buildTool !== BuildTools.CFamily) { - onStepValidationChange(true); - } - }, [buildTool, onStepValidationChange]); + const { config, projectKey, projectName } = props; + const { buildTool } = config; if (!buildTool) { return null; } + switch (buildTool) { case BuildTools.Maven: return ; @@ -54,10 +49,9 @@ export default function AnalysisCommand(props: AnalysisCommandProps) { case BuildTools.DotNet: return ; - case BuildTools.CFamily: - return ( - - ); + case BuildTools.Cpp: + case BuildTools.ObjectiveC: + return ; case BuildTools.Other: return ; diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/ClangGCC.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/ClangGCC.tsx index 01f1ed48956..eaab6c03151 100644 --- a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/ClangGCC.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/ClangGCC.tsx @@ -31,13 +31,14 @@ import { CompilationInfo } from '../../components/CompilationInfo'; import GithubCFamilyExampleRepositories from '../../components/GithubCFamilyExampleRepositories'; import RenderOptions from '../../components/RenderOptions'; import SentenceWithHighlights from '../../components/SentenceWithHighlights'; -import { BuildTools, OSs, TutorialModes } from '../../types'; +import { AutoConfig, BuildTools, OSs, TutorialConfig, TutorialModes } from '../../types'; import AlertClassicEditor from './AlertClassicEditor'; +import Other from './Other'; import PrepareAnalysisCommand, { PrepareType } from './PrepareAnalysisCommand'; import PublishSteps from './PublishSteps'; export interface ClangGCCProps { - onStepValidationChange: (isValid: boolean) => void; + config: TutorialConfig; projectKey: string; } @@ -50,8 +51,8 @@ type OsConstant = { }; export default function ClangGCC(props: ClangGCCProps) { - const { projectKey, onStepValidationChange } = props; - const [os, setOs] = React.useState(OSs.Linux); + const { config, projectKey } = props; + const [os, setOs] = React.useState(OSs.Linux); const host = getHostUrl(); const codeSnippetDownload: OsConstant = { @@ -81,17 +82,9 @@ unzip build-wrapper.zip`, }, }; - React.useEffect(() => { - if (os) { - onStepValidationChange(true); - } else { - onStepValidationChange(false); - } - }, [os, onStepValidationChange]); - - const handlOsChange = (value: OSs) => { - setOs(value); - }; + if (config.buildTool === BuildTools.Cpp && config.autoConfig === AutoConfig.Automatic) { + return ; + } return ( <> @@ -99,84 +92,80 @@ unzip build-wrapper.zip`, setOs(value)} optionLabelKey="onboarding.build.other.os" options={Object.values(OSs)} /> - {os && ( - <> - + + + + - - - + + - - - - - - - + + + + - - - - + + + + - + + + + - - - - - - - - - - - - + + + + + + + + - - - - )} + + ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/PrepareAnalysisCommand.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/PrepareAnalysisCommand.tsx index f9eb7b633dd..2f484e85a68 100644 --- a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/PrepareAnalysisCommand.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/PrepareAnalysisCommand.tsx @@ -24,6 +24,7 @@ import { translate } from '../../../../helpers/l10n'; import { InlineSnippet } from '../../components/InlineSnippet'; import SentenceWithHighlights from '../../components/SentenceWithHighlights'; import { BuildTools } from '../../types'; +import { isCFamily } from '../../utils'; export enum PrepareType { JavaMavenGradle, @@ -118,7 +119,7 @@ sonar.projectName=${projectName} /> - {buildTool === BuildTools.CFamily && ( + {isCFamily(buildTool) && ( string -> = { +export type BuildToolExampleBuilder = (data: { + branchesEnabled?: boolean; + config: TutorialConfig; + mainBranchName?: string; + projectKey?: string; + projectName?: string; +}) => string; + +const YamlTemplate: Dictionary = { [BuildTools.Gradle]: gradleExample, [BuildTools.Maven]: mavenExample, [BuildTools.DotNet]: dotNetExample, - [BuildTools.CFamily]: cFamilyExample, + [BuildTools.Cpp]: cFamilyExample, + [BuildTools.ObjectiveC]: cFamilyExample, [BuildTools.Other]: othersExample, }; export function AnalysisCommand(props: AnalysisCommandProps) { - const { buildTool, mainBranchName, component } = props; - const branchSupportEnabled = props.hasFeature(Feature.BranchSupport); + const { config, mainBranchName, component } = props; + const branchesEnabled = props.hasFeature(Feature.BranchSupport); + + if (!config.buildTool) { + return null; + } - const yamlTemplate = YamlTemplate[buildTool]( - branchSupportEnabled, + const yamlTemplate = YamlTemplate[config.buildTool]({ + config, + branchesEnabled, mainBranchName, - component.key, - component.name, - ); + projectKey: component.key, + projectName: component.name, + }); return ( <> - + - {buildTool === BuildTools.CFamily && } + {isCFamily(config.buildTool) && } ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/BitbucketPipelinesTutorial.tsx b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/BitbucketPipelinesTutorial.tsx index 95dbda5bdfe..21e6173ac78 100644 --- a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/BitbucketPipelinesTutorial.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/BitbucketPipelinesTutorial.tsx @@ -26,7 +26,8 @@ import { LoggedInUser } from '../../../types/users'; import AllSet from '../components/AllSet'; import GithubCFamilyExampleRepositories from '../components/GithubCFamilyExampleRepositories'; import YamlFileStep from '../components/YamlFileStep'; -import { BuildTools, TutorialModes } from '../types'; +import { TutorialConfig, TutorialModes } from '../types'; +import { shouldShowGithubCFamilyExampleRepositories } from '../utils'; import AnalysisCommand from './AnalysisCommand'; import RepositoryVariables from './RepositoryVariables'; @@ -49,7 +50,13 @@ export default function BitbucketPipelinesTutorial(props: BitbucketPipelinesTuto const { almBinding, baseUrl, currentUser, component, willRefreshAutomatically, mainBranchName } = props; - const [done, setDone] = React.useState(false); + const [config, setConfig] = React.useState({}); + const [done, setDone] = React.useState(false); + + React.useEffect(() => { + setDone(Boolean(config.buildTool)); + }, [config.buildTool]); + return ( <> {translate('onboarding.tutorial.with.bitbucket_ci.title')} @@ -66,17 +73,17 @@ export default function BitbucketPipelinesTutorial(props: BitbucketPipelinesTuto /> - - {(buildTool) => ( + + {(config) => ( <> - {buildTool === BuildTools.CFamily && ( + {shouldShowGithubCFamilyExampleRepositories(config) && ( )} diff --git a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/PreambuleYaml.tsx b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/PreambuleYaml.tsx index 1df171e33a0..83222ccbe61 100644 --- a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/PreambuleYaml.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/PreambuleYaml.tsx @@ -33,7 +33,8 @@ export function PreambuleYaml(props: PreambuleYamlProps) { switch (buildTool) { case BuildTools.Gradle: return ; - case BuildTools.CFamily: + case BuildTools.Cpp: + case BuildTools.ObjectiveC: case BuildTools.Other: return ; default: diff --git a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/__tests__/BitbucketPipelinesTutorial-it.tsx b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/__tests__/BitbucketPipelinesTutorial-it.tsx index 13b3640f87b..76f46695e44 100644 --- a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/__tests__/BitbucketPipelinesTutorial-it.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/__tests__/BitbucketPipelinesTutorial-it.tsx @@ -86,15 +86,41 @@ it('should follow and complete all steps', async () => { await user.click(ui.dotnetBuildButton.get()); expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('.NET: bitbucket-pipelines.yml'); - // CFamily - await user.click(ui.cFamilyBuildButton.get()); - expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('CFamily: sonar-project.properties'); - expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('CFamily: bitbucket-pipelines.yml'); + // Cpp + await user.click(ui.cppBuildButton.get()); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot( + 'C++ (automatic) and other: sonar-project.properties', + ); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( + 'C++ (automatic) and other: bitbucket-pipelines.yml', + ); + + // Cpp (manual) + await user.click(ui.autoConfigManual.get()); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot( + 'C++ (manual) and Objective-C: sonar-project.properties', + ); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( + 'C++ (manual) and Objective-C: bitbucket-pipelines.yml', + ); + + // Objective-C + await user.click(ui.objCBuildButton.get()); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot( + 'C++ (manual) and Objective-C: bitbucket-pipelines.yml', + ); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( + 'C++ (manual) and Objective-C: sonar-project.properties', + ); // Other await user.click(ui.otherBuildButton.get()); - expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Other: sonar-project.properties'); - expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Other: .github/workflows/build.yml'); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot( + 'C++ (automatic) and other: sonar-project.properties', + ); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( + 'C++ (automatic) and other: .github/workflows/build.yml', + ); expect(ui.allSetSentence.get()).toBeInTheDocument(); }); diff --git a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/__tests__/__snapshots__/BitbucketPipelinesTutorial-it.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/__tests__/__snapshots__/BitbucketPipelinesTutorial-it.tsx.snap index 827109d5f21..39b68ed6fdd 100644 --- a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/__tests__/__snapshots__/BitbucketPipelinesTutorial-it.tsx.snap +++ b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/__tests__/__snapshots__/BitbucketPipelinesTutorial-it.tsx.snap @@ -31,7 +31,69 @@ pipelines: - step: *build-step" `; -exports[`should follow and complete all steps: CFamily: bitbucket-pipelines.yml 1`] = ` +exports[`should follow and complete all steps: C++ (automatic) and other: .github/workflows/build.yml 1`] = ` +"image: maven:3.3.9 + +definitions: + steps: + - step: &build-step + name: SonarQube analysis + script: + - pipe: sonarsource/sonarqube-scan:2.0.1 + variables: + SONAR_HOST_URL: \${SONAR_HOST_URL} # Get the value from the repository/workspace variable. + SONAR_TOKEN: \${SONAR_TOKEN} # Get the value from the repository/workspace variable. You shouldn't set secret in clear text here. + caches: + sonar: ~/.sonar + +clone: + depth: full + +pipelines: + branches: + '{main}': + - step: *build-step + + pull-requests: + '**': + - step: *build-step +" +`; + +exports[`should follow and complete all steps: C++ (automatic) and other: bitbucket-pipelines.yml 1`] = ` +"image: maven:3.3.9 + +definitions: + steps: + - step: &build-step + name: SonarQube analysis + script: + - pipe: sonarsource/sonarqube-scan:2.0.1 + variables: + SONAR_HOST_URL: \${SONAR_HOST_URL} # Get the value from the repository/workspace variable. + SONAR_TOKEN: \${SONAR_TOKEN} # Get the value from the repository/workspace variable. You shouldn't set secret in clear text here. + caches: + sonar: ~/.sonar + +clone: + depth: full + +pipelines: + branches: + '{main}': + - step: *build-step + + pull-requests: + '**': + - step: *build-step +" +`; + +exports[`should follow and complete all steps: C++ (automatic) and other: sonar-project.properties 1`] = `"sonar.projectKey=my-project"`; + +exports[`should follow and complete all steps: C++ (automatic) and other: sonar-project.properties 2`] = `"sonar.projectKey=my-project"`; + +exports[`should follow and complete all steps: C++ (manual) and Objective-C: bitbucket-pipelines.yml 1`] = ` "image: definitions: @@ -65,7 +127,43 @@ pipelines: - step: *build-step" `; -exports[`should follow and complete all steps: CFamily: sonar-project.properties 1`] = `"sonar.projectKey=my-project"`; +exports[`should follow and complete all steps: C++ (manual) and Objective-C: bitbucket-pipelines.yml 2`] = `"sonar.projectKey=my-project"`; + +exports[`should follow and complete all steps: C++ (manual) and Objective-C: sonar-project.properties 1`] = `"sonar.projectKey=my-project"`; + +exports[`should follow and complete all steps: C++ (manual) and Objective-C: sonar-project.properties 2`] = ` +"image: + +definitions: + steps: + - step: &build-step + name: Build the project, and run the SonarQube analysis + script: + - export SONAR_SCANNER_VERSION=5.0.1.3006 + - mkdir $HOME/.sonar + - curl -sSLo $HOME/.sonar/build-wrapper-linux-x86.zip \${SONAR_HOST_URL}/static/cpp/build-wrapper-linux-x86.zip + - unzip -o $HOME/.sonar/build-wrapper-linux-x86.zip -d $HOME/.sonar/ + - curl -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-\${SONAR_SCANNER_VERSION}-linux.zip + - unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/ + - export PATH="$PATH:$HOME/.sonar/sonar-scanner-\${SONAR_SCANNER_VERSION}-linux/bin" + - + - $HOME/.sonar/build-wrapper-linux-x86/build-wrapper-linux-x86-64 --out-dir bw-output + - sonar-scanner -Dsonar.cfamily.compile-commands=bw-output/compile_commands.json + caches: + sonar: ~/.sonar + +clone: + depth: full + +pipelines: + branches: + '{main}': + - step: *build-step + + pull-requests: + '**': + - step: *build-step" +`; exports[`should follow and complete all steps: Gradle: bitbucket-pipelines.yml 1`] = ` "image: eclipse-temurin:17 @@ -149,37 +247,6 @@ pipelines: - step: *build-step" `; -exports[`should follow and complete all steps: Other: .github/workflows/build.yml 1`] = ` -"image: maven:3.3.9 - -definitions: - steps: - - step: &build-step - name: SonarQube analysis - script: - - pipe: sonarsource/sonarqube-scan:2.0.1 - variables: - SONAR_HOST_URL: \${SONAR_HOST_URL} # Get the value from the repository/workspace variable. - SONAR_TOKEN: \${SONAR_TOKEN} # Get the value from the repository/workspace variable. You shouldn't set secret in clear text here. - caches: - sonar: ~/.sonar - -clone: - depth: full - -pipelines: - branches: - '{main}': - - step: *build-step - - pull-requests: - '**': - - step: *build-step -" -`; - -exports[`should follow and complete all steps: Other: sonar-project.properties 1`] = `"sonar.projectKey=my-project"`; - exports[`should follow and complete all steps: sonar token key 1`] = `"SONAR_TOKEN"`; exports[`should follow and complete all steps: sonarqube host url key 1`] = `"SONAR_HOST_URL"`; diff --git a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/CFamily.ts b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/CFamily.ts index ab6adff8e9e..2f694274649 100644 --- a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/CFamily.ts +++ b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/CFamily.ts @@ -17,7 +17,14 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -export default function cFamilyExample(branchesEnabled: boolean, mainBranchName: string) { +import { AutoConfig, BuildTools } from '../../types'; +import { BuildToolExampleBuilder } from '../AnalysisCommand'; +import othersExample from './Others'; + +const cFamilyExample: BuildToolExampleBuilder = ({ config, branchesEnabled, mainBranchName }) => { + if (config.buildTool === BuildTools.Cpp && config.autoConfig === AutoConfig.Automatic) { + return othersExample({ config, branchesEnabled, mainBranchName }); + } return `image: definitions: @@ -53,4 +60,6 @@ ${ - step: *build-step` : '' }`; -} +}; + +export default cFamilyExample; diff --git a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/DotNet.ts b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/DotNet.ts index 621cb328358..df12f554254 100644 --- a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/DotNet.ts +++ b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/DotNet.ts @@ -17,11 +17,13 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -export default function dotNetExample( - branchesEnabled: boolean, - mainBranchName: string, - projectKey: string, -) { +import { BuildToolExampleBuilder } from '../AnalysisCommand'; + +const dotNetExample: BuildToolExampleBuilder = ({ + branchesEnabled, + mainBranchName, + projectKey, +}) => { return `image: mcr.microsoft.com/dotnet/sdk:7.0 definitions: @@ -54,4 +56,6 @@ ${ - step: *build-step` : '' }`; -} +}; + +export default dotNetExample; diff --git a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/Gradle.ts b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/Gradle.ts index 4d96925a69a..45cb5b262e8 100644 --- a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/Gradle.ts +++ b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/Gradle.ts @@ -17,7 +17,9 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -export default function gradleExample(branchesEnabled: boolean, mainBranchName: string) { +import { BuildToolExampleBuilder } from '../AnalysisCommand'; + +const gradleExample: BuildToolExampleBuilder = ({ branchesEnabled, mainBranchName }) => { return `image: eclipse-temurin:17 definitions: @@ -47,4 +49,6 @@ ${ - step: *build-step` : '' }`; -} +}; + +export default gradleExample; diff --git a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/Maven.ts b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/Maven.ts index cc32ef6249b..6a53708be3d 100644 --- a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/Maven.ts +++ b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/Maven.ts @@ -17,12 +17,14 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -export default function mavenExample( - branchesEnabled: boolean, - mainBranchName: string, - projectKey: string, - projectName: string, -) { +import { BuildToolExampleBuilder } from '../AnalysisCommand'; + +const mavenExample: BuildToolExampleBuilder = ({ + branchesEnabled, + mainBranchName, + projectKey, + projectName, +}) => { return `image: maven:3-eclipse-temurin-17 definitions: @@ -52,4 +54,6 @@ ${ - step: *build-step` : '' }`; -} +}; + +export default mavenExample; diff --git a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/Others.ts b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/Others.ts index 7ae93f05190..94c145180f7 100644 --- a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/Others.ts +++ b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/Others.ts @@ -17,7 +17,9 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -export default function othersExample(branchesEnabled: boolean, mainBranchName: string) { +import { BuildToolExampleBuilder } from '../AnalysisCommand'; + +const othersExample: BuildToolExampleBuilder = ({ branchesEnabled, mainBranchName }) => { return `image: maven:3.3.9 definitions: @@ -48,4 +50,6 @@ ${ ` : '' }`; -} +}; + +export default othersExample; diff --git a/server/sonar-web/src/main/js/components/tutorials/components/BuildConfigSelection.tsx b/server/sonar-web/src/main/js/components/tutorials/components/BuildConfigSelection.tsx new file mode 100644 index 00000000000..9cfff92517a --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/components/BuildConfigSelection.tsx @@ -0,0 +1,84 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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 { FlagMessage } from 'design-system'; +import * as React from 'react'; +import { translate } from '../../../helpers/l10n'; +import { AutoConfig, BuildTools, TutorialConfig, TutorialModes } from '../types'; +import { getBuildToolOptions, isCFamily, supportsAutoConfig } from '../utils'; +import RenderOptions from './RenderOptions'; + +export interface BuildConfigSelectionProps { + ci: TutorialModes; + config: TutorialConfig; + hideAutoConfig?: boolean; + onSetConfig(config: TutorialConfig): void; + supportCFamily: boolean; +} + +export default function BuildConfigSelection(props: Readonly) { + const { ci, config, hideAutoConfig, supportCFamily, onSetConfig } = props; + + function onSelectBuildTool(buildTool: BuildTools) { + const autoConfig = buildTool === BuildTools.Cpp ? AutoConfig.Automatic : undefined; + onSetConfig({ ...config, buildTool, autoConfig }); + } + + function onSelectAutoConfig(autoConfig: AutoConfig) { + onSetConfig({ ...config, autoConfig }); + } + + return ( + <> + {translate('onboarding.build')} + + + {ci === TutorialModes.Jenkins && isCFamily(config.buildTool) && ( + + {translate('onboarding.tutorial.with.jenkins.jenkinsfile.cfamilly.agent_setup')} + + )} + + {!hideAutoConfig && + config.buildTool && + supportsAutoConfig(config.buildTool) && + onSelectAutoConfig && ( + <> +
{translate('onboarding.build.cpp.autoconfig')}
+ + + {translate(`onboarding.build.cpp.autoconfig.${config.autoConfig}.description`)} + + + )} + + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/components/RenderOptions.tsx b/server/sonar-web/src/main/js/components/tutorials/components/RenderOptions.tsx index b972264dfc2..cb379b20db4 100644 --- a/server/sonar-web/src/main/js/components/tutorials/components/RenderOptions.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/components/RenderOptions.tsx @@ -27,7 +27,6 @@ export interface RenderOptionsProps { onCheck: (checked: string) => void; optionLabelKey: string; options: string[]; - setDone?: (doneStatus: boolean) => void; titleLabelKey?: string; } @@ -35,15 +34,11 @@ export default function RenderOptions({ checked, label, onCheck, - setDone, optionLabelKey, options, titleLabelKey, }: RenderOptionsProps) { const onChange = (checked: string) => { - if (setDone) { - setDone(true); - } onCheck(checked); }; diff --git a/server/sonar-web/src/main/js/components/tutorials/components/YamlFileStep.tsx b/server/sonar-web/src/main/js/components/tutorials/components/YamlFileStep.tsx index fe16f855f8c..d1b01e6835a 100644 --- a/server/sonar-web/src/main/js/components/tutorials/components/YamlFileStep.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/components/YamlFileStep.tsx @@ -19,42 +19,33 @@ */ import { NumberedList, NumberedListItem } from 'design-system'; import * as React from 'react'; -import { translate } from '../../../helpers/l10n'; import { withCLanguageFeature } from '../../hoc/withCLanguageFeature'; -import RenderOptions from '../components/RenderOptions'; -import { BuildTools } from '../types'; +import { TutorialConfig, TutorialModes } from '../types'; +import BuildConfigSelection from './BuildConfigSelection'; export interface YamlFileStepProps { - children?: (buildTool: BuildTools) => React.ReactElement<{}>; + children?: (config: TutorialConfig) => React.ReactElement<{}>; + ci: TutorialModes; + config: TutorialConfig; hasCLanguageFeature: boolean; - setDone?: (doneStatus: boolean) => void; + setConfig: (config: TutorialConfig) => void; } export function YamlFileStep(props: YamlFileStepProps) { - const { children, hasCLanguageFeature } = props; - - const buildTools = [BuildTools.Maven, BuildTools.Gradle, BuildTools.DotNet]; - if (hasCLanguageFeature) { - buildTools.push(BuildTools.CFamily); - } - buildTools.push(BuildTools.Other); - - const [buildToolSelected, setBuildToolSelected] = React.useState(); + const { ci, config, setConfig, children, hasCLanguageFeature } = props; return ( - {translate('onboarding.build')} - setBuildToolSelected(value as BuildTools)} - options={buildTools} - optionLabelKey="onboarding.build" - setDone={props.setDone} + - {children && buildToolSelected && children(buildToolSelected)} + + {children && config && children(config)} ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx index 81cc14e14af..f58f9d0bae9 100644 --- a/server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx @@ -23,7 +23,7 @@ import withAvailableFeatures, { } from '../../../app/components/available-features/withAvailableFeatures'; import { Feature } from '../../../types/features'; import { Component } from '../../../types/types'; -import { BuildTools } from '../types'; +import { BuildTools, TutorialConfig } from '../types'; import CFamily from './commands/CFamily'; import DotNet from './commands/DotNet'; import Gradle from './commands/Gradle'; @@ -31,17 +31,17 @@ import JavaMaven from './commands/JavaMaven'; import Others from './commands/Others'; export interface AnalysisCommandProps extends WithAvailableFeaturesProps { - buildTool: BuildTools; component: Component; + config: TutorialConfig; mainBranchName: string; monorepo?: boolean; } export function AnalysisCommand(props: Readonly) { - const { buildTool, component, mainBranchName, monorepo } = props; + const { config, component, mainBranchName, monorepo } = props; const branchSupportEnabled = props.hasFeature(Feature.BranchSupport); - switch (buildTool) { + switch (config.buildTool) { case BuildTools.Maven: return ( ) { component={component} /> ); - case BuildTools.CFamily: + case BuildTools.Cpp: + case BuildTools.ObjectiveC: return ( (false); const { almBinding, baseUrl, @@ -50,6 +50,13 @@ export default function GitHubActionTutorial(props: GitHubActionTutorialProps) { willRefreshAutomatically, } = props; + const [config, setConfig] = React.useState({}); + const [done, setDone] = React.useState(false); + + React.useEffect(() => { + setDone(Boolean(config.buildTool)); + }, [config.buildTool]); + return ( <> {translate('onboarding.tutorial.with.github_ci.title')} @@ -66,10 +73,10 @@ export default function GitHubActionTutorial(props: GitHubActionTutorialProps) { />
- - {(buildTool) => ( + + {(config) => ( { await user.click(ui.dotnetBuildButton.get()); expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('.NET: .github/workflows/build.yml'); - // CFamily - await user.click(ui.cFamilyBuildButton.get()); - expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('CFamily: sonar-project.properties'); + // Cpp + await user.click(ui.cppBuildButton.get()); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot( + 'C++ (automatic) and other: sonar-project.properties', + ); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( + 'C++ (automatic) and other: .github/workflows/build.yml', + ); + + await user.click(ui.autoConfigManual.get()); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('C++: sonar-project.properties'); await user.click(ui.linuxButton.get()); expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( - 'CFamily Linux: .github/workflows/build.yml', + 'C++ Linux: .github/workflows/build.yml', ); await user.click(ui.windowsButton.get()); expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( - 'CFamily Windows: .github/workflows/build.yml', + 'C++ Windows: .github/workflows/build.yml', ); await user.click(ui.macosButton.get()); expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( - 'CFamily MacOS: .github/workflows/build.yml', + 'C++ MacOS: .github/workflows/build.yml', + ); + + // Objective-C + await user.click(ui.objCBuildButton.get()); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot( + 'Objective-C: sonar-project.properties', + ); + + await user.click(ui.linuxButton.get()); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( + 'Objective-C Linux: .github/workflows/build.yml', + ); + await user.click(ui.windowsButton.get()); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( + 'Objective-C Windows: .github/workflows/build.yml', + ); + await user.click(ui.macosButton.get()); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( + 'Objective-C MacOS: .github/workflows/build.yml', ); // Other await user.click(ui.otherBuildButton.get()); - expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Other: sonar-project.properties'); - expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Other: .github/workflows/build.yml'); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot( + 'C++ (automatic) and other: sonar-project.properties', + ); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( + 'C++ (automatic) and other: .github/workflows/build.yml', + ); expect(ui.allSetSentence.get()).toBeInTheDocument(); }); diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/GithubActionTutorial-it.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/GithubActionTutorial-it.tsx.snap index eb5e451a53d..3cc30b5bdf0 100644 --- a/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/GithubActionTutorial-it.tsx.snap +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/GithubActionTutorial-it.tsx.snap @@ -50,7 +50,77 @@ jobs: .\\.sonar\\scanner\\dotnet-sonarscanner end /d:sonar.token="\${{ secrets.SONAR_TOKEN }}"" `; -exports[`should follow and complete all steps: CFamily Linux: .github/workflows/build.yml 1`] = ` +exports[`should follow and complete all steps: C++ (automatic) and other: .github/workflows/build.yml 1`] = ` +"name: Build + +on: + push: + branches: + - main + pull_request: + types: [opened, synchronize, reopened] + +jobs: + build: + name: Build and analyze + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - uses: sonarsource/sonarqube-scan-action@master + env: + SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }} + # If you wish to fail your job when the Quality Gate is red, uncomment the + # following lines. This would typically be used to fail a deployment. + # We do not recommend to use this in a pull request. Prefer using pull request + # decoration instead. + # - uses: sonarsource/sonarqube-quality-gate-action@master + # timeout-minutes: 5 + # env: + # SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }}" +`; + +exports[`should follow and complete all steps: C++ (automatic) and other: .github/workflows/build.yml 2`] = ` +"name: Build + +on: + push: + branches: + - main + pull_request: + types: [opened, synchronize, reopened] + +jobs: + build: + name: Build and analyze + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - uses: sonarsource/sonarqube-scan-action@master + env: + SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }} + # If you wish to fail your job when the Quality Gate is red, uncomment the + # following lines. This would typically be used to fail a deployment. + # We do not recommend to use this in a pull request. Prefer using pull request + # decoration instead. + # - uses: sonarsource/sonarqube-quality-gate-action@master + # timeout-minutes: 5 + # env: + # SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }}" +`; + +exports[`should follow and complete all steps: C++ (automatic) and other: sonar-project.properties 1`] = `"sonar.projectKey=my-project"`; + +exports[`should follow and complete all steps: C++ (automatic) and other: sonar-project.properties 2`] = `"sonar.projectKey=my-project"`; + +exports[`should follow and complete all steps: C++ Linux: .github/workflows/build.yml 1`] = ` "name: Build on: @@ -86,7 +156,7 @@ jobs: sonar-scanner --define sonar.cfamily.compile-commands="\${{ env.BUILD_WRAPPER_OUT_DIR }}/compile_commands.json"" `; -exports[`should follow and complete all steps: CFamily MacOS: .github/workflows/build.yml 1`] = ` +exports[`should follow and complete all steps: C++ MacOS: .github/workflows/build.yml 1`] = ` "name: Build on: @@ -122,7 +192,7 @@ jobs: sonar-scanner --define sonar.cfamily.compile-commands="\${{ env.BUILD_WRAPPER_OUT_DIR }}/compile_commands.json"" `; -exports[`should follow and complete all steps: CFamily Windows: .github/workflows/build.yml 1`] = ` +exports[`should follow and complete all steps: C++ Windows: .github/workflows/build.yml 1`] = ` "name: Build on: @@ -158,7 +228,7 @@ jobs: sonar-scanner --define sonar.cfamily.compile-commands="\${{ env.BUILD_WRAPPER_OUT_DIR }}/compile_commands.json"" `; -exports[`should follow and complete all steps: CFamily: sonar-project.properties 1`] = `"sonar.projectKey=my-project"`; +exports[`should follow and complete all steps: C++: sonar-project.properties 1`] = `"sonar.projectKey=my-project"`; exports[`should follow and complete all steps: Gradle: .github/workflows/build.yml 1`] = ` "name: Build @@ -270,7 +340,7 @@ jobs: run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=my-project -Dsonar.projectName='MyProject'" `; -exports[`should follow and complete all steps: Other: .github/workflows/build.yml 1`] = ` +exports[`should follow and complete all steps: Objective-C Linux: .github/workflows/build.yml 1`] = ` "name: Build on: @@ -284,26 +354,101 @@ jobs: build: name: Build and analyze runs-on: ubuntu-latest - + env: + BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - uses: sonarsource/sonarqube-scan-action@master + - name: Install sonar-scanner and build-wrapper env: + SONAR_HOST_URL: \${{secrets.SONAR_HOST_URL}} + uses: SonarSource/sonarqube-github-c-cpp@v1 + - name: Run build-wrapper + run: | + build-wrapper-linux-x86-64 --out-dir \${{ env.BUILD_WRAPPER_OUT_DIR }} + - name: Run sonar-scanner + env: + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }} - SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }} - # If you wish to fail your job when the Quality Gate is red, uncomment the - # following lines. This would typically be used to fail a deployment. - # We do not recommend to use this in a pull request. Prefer using pull request - # decoration instead. - # - uses: sonarsource/sonarqube-quality-gate-action@master - # timeout-minutes: 5 - # env: - # SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }}" + SONAR_HOST_URL: \${{secrets.SONAR_HOST_URL}} + run: | + sonar-scanner --define sonar.cfamily.compile-commands="\${{ env.BUILD_WRAPPER_OUT_DIR }}/compile_commands.json"" +`; + +exports[`should follow and complete all steps: Objective-C MacOS: .github/workflows/build.yml 1`] = ` +"name: Build + +on: + push: + branches: + - main + pull_request: + types: [opened, synchronize, reopened] + +jobs: + build: + name: Build and analyze + runs-on: macos-latest + env: + BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Install sonar-scanner and build-wrapper + env: + SONAR_HOST_URL: \${{secrets.SONAR_HOST_URL}} + uses: SonarSource/sonarqube-github-c-cpp@v1 + - name: Run build-wrapper + run: | + build-wrapper-macosx-x86 --out-dir \${{ env.BUILD_WRAPPER_OUT_DIR }} + - name: Run sonar-scanner + env: + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: \${{secrets.SONAR_HOST_URL}} + run: | + sonar-scanner --define sonar.cfamily.compile-commands="\${{ env.BUILD_WRAPPER_OUT_DIR }}/compile_commands.json"" +`; + +exports[`should follow and complete all steps: Objective-C Windows: .github/workflows/build.yml 1`] = ` +"name: Build + +on: + push: + branches: + - main + pull_request: + types: [opened, synchronize, reopened] + +jobs: + build: + name: Build and analyze + runs-on: windows-latest + env: + BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Install sonar-scanner and build-wrapper + env: + SONAR_HOST_URL: \${{secrets.SONAR_HOST_URL}} + uses: SonarSource/sonarqube-github-c-cpp@v1 + - name: Run build-wrapper + run: | + build-wrapper-win-x86-64 --out-dir \${{ env.BUILD_WRAPPER_OUT_DIR }} + - name: Run sonar-scanner + env: + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: \${{secrets.SONAR_HOST_URL}} + run: | + sonar-scanner --define sonar.cfamily.compile-commands="\${{ env.BUILD_WRAPPER_OUT_DIR }}/compile_commands.json"" `; -exports[`should follow and complete all steps: Other: sonar-project.properties 1`] = `"sonar.projectKey=my-project"`; +exports[`should follow and complete all steps: Objective-C: sonar-project.properties 1`] = `"sonar.projectKey=my-project"`; exports[`should follow and complete all steps: sonar token key 1`] = `"SONAR_TOKEN"`; diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/CFamily.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/CFamily.tsx index 7c8195a939e..7c0513374fc 100644 --- a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/CFamily.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/CFamily.tsx @@ -26,13 +26,15 @@ import CreateYmlFile from '../../components/CreateYmlFile'; import DefaultProjectKey from '../../components/DefaultProjectKey'; import GithubCFamilyExampleRepositories from '../../components/GithubCFamilyExampleRepositories'; import RenderOptions from '../../components/RenderOptions'; -import { OSs, TutorialModes } from '../../types'; +import { AutoConfig, BuildTools, OSs, TutorialConfig, TutorialModes } from '../../types'; import { generateGitHubActionsYaml } from '../utils'; import MonorepoDocLinkFallback from './MonorepoDocLinkFallback'; +import Others from './Others'; export interface CFamilyProps { branchesEnabled?: boolean; component: Component; + config: TutorialConfig; mainBranchName: string; monorepo?: boolean; } @@ -86,8 +88,12 @@ const STEPS = { }; export default function CFamily(props: CFamilyProps) { - const { component, branchesEnabled, mainBranchName, monorepo } = props; - const [os, setOs] = React.useState(OSs.Linux); + const { config, component, branchesEnabled, mainBranchName, monorepo } = props; + const [os, setOs] = React.useState(OSs.Linux); + + if (config.buildTool === BuildTools.Cpp && config.autoConfig === AutoConfig.Automatic) { + return ; + } const runsOn = { [OSs.Linux]: 'ubuntu-latest', @@ -106,33 +112,30 @@ export default function CFamily(props: CFamilyProps) { optionLabelKey="onboarding.build.other.os" options={Object.values(OSs)} /> - {os && ( - - )} + - {os && - (monorepo ? ( - - ) : ( - <> - + ) : ( + <> + - - - ))} + )} + /> + + + )} ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/gitlabci/GitLabCITutorial.tsx b/server/sonar-web/src/main/js/components/tutorials/gitlabci/GitLabCITutorial.tsx index bebd34e2a4a..0b778d63c6e 100644 --- a/server/sonar-web/src/main/js/components/tutorials/gitlabci/GitLabCITutorial.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/gitlabci/GitLabCITutorial.tsx @@ -44,6 +44,7 @@ export default function GitLabCITutorial(props: GitLabCITutorialProps) { const { baseUrl, component, currentUser, willRefreshAutomatically } = props; const [done, setDone] = React.useState(false); + return ( <> {translate('onboarding.tutorial.with.gitlab_ci.title')} diff --git a/server/sonar-web/src/main/js/components/tutorials/gitlabci/YmlFileStep.tsx b/server/sonar-web/src/main/js/components/tutorials/gitlabci/YmlFileStep.tsx index c9382b02125..fa9f0642e59 100644 --- a/server/sonar-web/src/main/js/components/tutorials/gitlabci/YmlFileStep.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/gitlabci/YmlFileStep.tsx @@ -34,17 +34,18 @@ import { GRADLE_SCANNER_VERSION } from '../../../helpers/constants'; import { translate } from '../../../helpers/l10n'; import { Component } from '../../../types/types'; import { withCLanguageFeature } from '../../hoc/withCLanguageFeature'; +import BuildConfigSelection from '../components/BuildConfigSelection'; import GithubCFamilyExampleRepositories from '../components/GithubCFamilyExampleRepositories'; import GradleBuildSelection from '../components/GradleBuildSelection'; import { InlineSnippet } from '../components/InlineSnippet'; -import RenderOptions from '../components/RenderOptions'; -import { BuildTools, GradleBuildDSL, TutorialModes } from '../types'; +import { BuildTools, GradleBuildDSL, TutorialConfig, TutorialModes } from '../types'; +import { isCFamily } from '../utils'; import PipeCommand from './commands/PipeCommand'; export interface YmlFileStepProps extends WithAvailableFeaturesProps { component: Component; hasCLanguageFeature: boolean; - setDone: (doneStatus: boolean) => void; + setDone: (done: boolean) => void; } const mavenSnippet = (key: string, name: string) => ` @@ -86,54 +87,55 @@ sonar.qualitygate.wait=true `; const snippetForBuildTool = { - [BuildTools.CFamily]: otherSnippet, + [BuildTools.Cpp]: otherSnippet, + [BuildTools.ObjectiveC]: otherSnippet, [BuildTools.Gradle]: gradleSnippet, [BuildTools.Maven]: mavenSnippet, [BuildTools.Other]: otherSnippet, }; const filenameForBuildTool = { - [BuildTools.CFamily]: 'sonar-project.properties', + [BuildTools.Cpp]: 'sonar-project.properties', + [BuildTools.ObjectiveC]: 'sonar-project.properties', [BuildTools.Gradle]: GradleBuildDSL.Groovy, [BuildTools.Maven]: 'pom.xml', [BuildTools.Other]: 'sonar-project.properties', }; const snippetLanguageForBuildTool = { - [BuildTools.CFamily]: undefined, + [BuildTools.Cpp]: undefined, + [BuildTools.ObjectiveC]: undefined, [BuildTools.Gradle]: undefined, [BuildTools.Maven]: 'xml', [BuildTools.Other]: undefined, }; export function YmlFileStep(props: YmlFileStepProps) { - const { component, hasCLanguageFeature } = props; + const { component, hasCLanguageFeature, setDone } = props; - const [buildTool, setBuildTool] = React.useState(); + const [config, setConfig] = React.useState({}); + const { buildTool } = config; - const buildTools = [BuildTools.Maven, BuildTools.Gradle, BuildTools.DotNet]; - - if (hasCLanguageFeature) { - buildTools.push(BuildTools.CFamily); + function onSetConfig(config: TutorialConfig) { + setConfig(config); } - buildTools.push(BuildTools.Other); + React.useEffect(() => { + setDone(Boolean(config.buildTool)); + }, [config.buildTool, setDone]); const renderForm = () => ( - {translate('onboarding.build')} - - void} - optionLabelKey="onboarding.build" - options={buildTools} - setDone={props.setDone} + - {buildTool === BuildTools.CFamily && ( + {isCFamily(config.buildTool) && ( {buildTool !== undefined && - buildTool !== BuildTools.CFamily && + buildTool !== BuildTools.Cpp && + buildTool !== BuildTools.ObjectiveC && buildTool !== BuildTools.DotNet && ( - {buildTool !== BuildTools.CFamily && ( + {buildTool !== BuildTools.Cpp && buildTool !== BuildTools.ObjectiveC && ( ; + buildTool: Exclude; projectKey: string; } diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsStep.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsStep.tsx index 4090603fe7c..87ca0994688 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsStep.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsStep.tsx @@ -17,90 +17,71 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { FlagMessage, NumberedList, NumberedListItem, TutorialStep } from 'design-system'; +import { NumberedList, NumberedListItem, TutorialStep } from 'design-system'; import * as React from 'react'; import { translate } from '../../../helpers/l10n'; import { Component } from '../../../types/types'; import { withCLanguageFeature } from '../../hoc/withCLanguageFeature'; -import RenderOptions from '../components/RenderOptions'; -import { BuildTools } from '../types'; -import CFamilly from './buildtool-steps/CFamilly'; +import BuildConfigSelection from '../components/BuildConfigSelection'; +import { BuildTools, TutorialConfig, TutorialModes } from '../types'; +import CFamily from './buildtool-steps/CFamily'; import DotNet from './buildtool-steps/DotNet'; import Gradle from './buildtool-steps/Gradle'; import Maven from './buildtool-steps/Maven'; import Other from './buildtool-steps/Other'; -const BUILD_TOOLS_WITH_NO_ADDITIONAL_OPTIONS = [ - BuildTools.Maven, - BuildTools.Gradle, - BuildTools.Other, -]; - const BUILDTOOL_COMPONENT_MAP: { [x in BuildTools]: React.ComponentType>; } = { [BuildTools.Maven]: Maven, [BuildTools.Gradle]: Gradle, [BuildTools.DotNet]: DotNet, - [BuildTools.CFamily]: CFamilly, + [BuildTools.Cpp]: CFamily, + [BuildTools.ObjectiveC]: CFamily, [BuildTools.Other]: Other, }; -export interface JenkinsfileStepProps { +export interface LanguageProps { baseUrl: string; component: Component; - hasCLanguageFeature: boolean; - onDone: (done: boolean) => void; + config: TutorialConfig; } -export interface LanguageProps { +export interface JenkinsfileStepProps { baseUrl: string; component: Component; - onDone: (done: boolean) => void; + hasCLanguageFeature: boolean; + setDone: (done: boolean) => void; } -export function JenkinsfileStep(props: JenkinsfileStepProps) { - const { component, hasCLanguageFeature, baseUrl, onDone } = props; - - const [buildTool, setBuildTool] = React.useState(); +export function JenkinsStep(props: Readonly) { + const { component, hasCLanguageFeature, baseUrl, setDone } = props; - const buildToolOrder = Object.keys(BUILDTOOL_COMPONENT_MAP); - if (!hasCLanguageFeature) { - buildToolOrder.splice(buildToolOrder.indexOf(BuildTools.CFamily), 1); - } - - const BuildToolComponent = buildTool ? BUILDTOOL_COMPONENT_MAP[buildTool] : undefined; + const [config, setConfig] = React.useState({}); React.useEffect(() => { - if (buildTool && BUILD_TOOLS_WITH_NO_ADDITIONAL_OPTIONS.includes(buildTool)) { - onDone(true); - } - }, [buildTool, onDone]); + setDone(Boolean(config.buildTool)); + }, [config.buildTool, setDone]); + + const BuildToolComponent = config.buildTool && BUILDTOOL_COMPONENT_MAP[config.buildTool]; return ( - {translate('onboarding.build')} - setBuildTool(value as BuildTools)} - optionLabelKey="onboarding.build" - options={buildToolOrder} + - {buildTool === BuildTools.CFamily && ( - - {translate('onboarding.tutorial.with.jenkins.jenkinsfile.cfamilly.agent_setup')} - - )} - {BuildToolComponent !== undefined && ( - + {BuildToolComponent && ( + )} ); } -export default withCLanguageFeature(JenkinsfileStep); +export default withCLanguageFeature(JenkinsStep); diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx index a57b718d52a..40368aa7222 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx @@ -28,7 +28,7 @@ import { AlmKeys, AlmSettingsInstance } from '../../../types/alm-settings'; import { Feature } from '../../../types/features'; import { Component } from '../../../types/types'; import AllSet from '../components/AllSet'; -import JenkinsfileStep from './JenkinsStep'; +import JenkinsStep from './JenkinsStep'; import MultiBranchPipelineStep from './MultiBranchPipelineStep'; import PipelineStep from './PipelineStep'; import PreRequisitesStep from './PreRequisitesStep'; @@ -81,7 +81,7 @@ export function JenkinsTutorial(props: JenkinsTutorialProps) { projectBinding={projectBinding} /> - + {done && ( <> diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsTutorial-it.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsTutorial-it.tsx index 87574ba0ea4..9380803e1ae 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsTutorial-it.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsTutorial-it.tsx @@ -131,25 +131,61 @@ it.each([AlmKeys.BitbucketCloud, AlmKeys.BitbucketServer, AlmKeys.GitHub, AlmKey await user.click(ui.linuxDotnetCoreButton.get()); expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot(`linux dotnet core jenkinsfile`); - // CFamilly - await user.click(ui.cFamilyBuildButton.get()); + // C++ (automatic) + await user.click(ui.cppBuildButton.get()); + expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot( + `c++ (automatic and other): build tools sonar-project.properties code`, + ); + expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot( + `c++ (automatic and other): build tools jenkinsfile`, + ); + + // C++ (manual) + await user.click(ui.autoConfigManual.get()); expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot(`sonar-project.properties code`); await user.click(ui.linuxButton.get()); - expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot(`cfamily linux jenkinsfile`); + expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot( + `c++ (manual) and objectivec: linux jenkinsfile`, + ); await user.click(ui.windowsButton.get()); - expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot(`cfamily windows jenkinsfile`); + expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot( + `c++ (manual) and objectivec: windows jenkinsfile`, + ); await user.click(ui.macosButton.get()); - expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot(`cfamily macos jenkinsfile`); + expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot( + `c++ (manual) and objectivec: macos jenkinsfile`, + ); + + // Objective-C + await user.click(ui.objCBuildButton.get()); + expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot(`sonar-project.properties code`); + + await user.click(ui.linuxButton.get()); + expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot( + `c++ (manual) and objectivec: linux jenkinsfile`, + ); + + await user.click(ui.windowsButton.get()); + expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot( + `c++ (manual) and objectivec: windows jenkinsfile`, + ); + + await user.click(ui.macosButton.get()); + expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot( + `c++ (manual) and objectivec: macos jenkinsfile`, + ); // Other await user.click(ui.otherBuildButton.get()); expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot( - `other build tools sonar-project.properties code`, + `c++ (automatic and other): build tools sonar-project.properties code`, + ); + expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot( + `c++ (automatic and other): build tools jenkinsfile`, ); - expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot(`other build tools jenkinsfile`); expect(ui.allSetSentence.get()).toBeInTheDocument(); }, diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsTutorial-it.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsTutorial-it.tsx.snap index 70cf5185828..5dd2df6b18d 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsTutorial-it.tsx.snap +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsTutorial-it.tsx.snap @@ -26,7 +26,61 @@ sonar { }" `; -exports[`bitbucket: can select devops platform and complete all the steps with copying code snippets: cfamily linux jenkinsfile 1`] = ` +exports[`bitbucket: can select devops platform and complete all the steps with copying code snippets: c++ (automatic and other): build tools jenkinsfile 1`] = ` +"node { + stage('SCM') { + checkout scm + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + sh "\${scannerHome}/bin/sonar-scanner" + } + } +}" +`; + +exports[`bitbucket: can select devops platform and complete all the steps with copying code snippets: c++ (automatic and other): build tools jenkinsfile 2`] = ` +"node { + stage('SCM') { + checkout scm + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + sh "\${scannerHome}/bin/sonar-scanner" + } + } +}" +`; + +exports[`bitbucket: can select devops platform and complete all the steps with copying code snippets: c++ (automatic and other): build tools sonar-project.properties code 1`] = `"sonar.projectKey=my-project"`; + +exports[`bitbucket: can select devops platform and complete all the steps with copying code snippets: c++ (automatic and other): build tools sonar-project.properties code 2`] = `"sonar.projectKey=my-project"`; + +exports[`bitbucket: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: linux jenkinsfile 1`] = ` +"node { + stage('SCM') { + checkout scm + } + stage('Download Build Wrapper') { + sh "mkdir -p .sonar" + sh "curl -sSLo build-wrapper-linux-x86.zip http://localhost:9000/static/cpp/build-wrapper-linux-x86.zip" + sh "unzip -o build-wrapper-linux-x86.zip -d .sonar" + } + stage('Build') { + sh ".sonar/build-wrapper-linux-x86/build-wrapper-linux-x86-64 --out-dir bw-output " + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + sh "\${scannerHome}/bin/sonar-scanner -Dsonar.cfamily.compile-commands=bw-output/compile_commands.json" + } + } +}" +`; + +exports[`bitbucket: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: linux jenkinsfile 2`] = ` "node { stage('SCM') { checkout scm @@ -48,7 +102,33 @@ exports[`bitbucket: can select devops platform and complete all the steps with c }" `; -exports[`bitbucket: can select devops platform and complete all the steps with copying code snippets: cfamily macos jenkinsfile 1`] = ` +exports[`bitbucket: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: macos jenkinsfile 1`] = ` +"node { + stage('SCM') { + checkout scm + } + stage('Download Build Wrapper') { + sh ''' + mkdir -p .sonar + curl -sSLo build-wrapper-macosx-x86.zip http://localhost:9000/static/cpp/build-wrapper-macosx-x86.zip + unzip -o build-wrapper-macosx-x86.zip -d .sonar + ''' + } + stage('Build') { + sh ''' + .sonar/build-wrapper-macosx-x86/build-wrapper-macosx-x86 --out-dir bw-output + ''' + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + sh "\${scannerHome}/bin/sonar-scanner -Dsonar.cfamily.compile-commands=bw-output/compile_commands.json" + } + } +}" +`; + +exports[`bitbucket: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: macos jenkinsfile 2`] = ` "node { stage('SCM') { checkout scm @@ -74,7 +154,39 @@ exports[`bitbucket: can select devops platform and complete all the steps with c }" `; -exports[`bitbucket: can select devops platform and complete all the steps with copying code snippets: cfamily windows jenkinsfile 1`] = ` +exports[`bitbucket: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: windows jenkinsfile 1`] = ` +"node { + stage('SCM') { + checkout scm + } + stage('Download Build Wrapper') { + powershell ''' + $path = "$HOME/.sonar/build-wrapper-win-x86.zip" + rm build-wrapper-win-x86 -Recurse -Force -ErrorAction SilentlyContinue + rm $path -Force -ErrorAction SilentlyContinue + mkdir $HOME/.sonar + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + (New-Object System.Net.WebClient).DownloadFile(http://localhost:9000/static/cpp/build-wrapper-win-x86.zip", $path) + Add-Type -AssemblyName System.IO.Compression.FileSystem + [System.IO.Compression.ZipFile]::ExtractToDirectory($path, "$HOME/.sonar") + ''' + } + stage('Build') { + powershell ''' + $env:Path += ";$HOME/.sonar/build-wrapper-win-x86" + build-wrapper-win-x86-64 --out-dir bw-output + ''' + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + powershell "\${scannerHome}/bin/sonar-scanner -Dsonar.cfamily.compile-commands=bw-output/compile_commands.json" + } + } +}" +`; + +exports[`bitbucket: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: windows jenkinsfile 2`] = ` "node { stage('SCM') { checkout scm @@ -152,26 +264,12 @@ exports[`bitbucket: can select devops platform and complete all the steps with c }" `; -exports[`bitbucket: can select devops platform and complete all the steps with copying code snippets: other build tools jenkinsfile 1`] = ` -"node { - stage('SCM') { - checkout scm - } - stage('SonarQube Analysis') { - def scannerHome = tool 'SonarScanner'; - withSonarQubeEnv() { - sh "\${scannerHome}/bin/sonar-scanner" - } - } -}" -`; - -exports[`bitbucket: can select devops platform and complete all the steps with copying code snippets: other build tools sonar-project.properties code 1`] = `"sonar.projectKey=my-project"`; - exports[`bitbucket: can select devops platform and complete all the steps with copying code snippets: ref spec 1`] = `"+refs/heads/*:refs/remotes/@{remote}/*"`; exports[`bitbucket: can select devops platform and complete all the steps with copying code snippets: sonar-project.properties code 1`] = `"sonar.projectKey=my-project"`; +exports[`bitbucket: can select devops platform and complete all the steps with copying code snippets: sonar-project.properties code 2`] = `"sonar.projectKey=my-project"`; + exports[`bitbucket: can select devops platform and complete all the steps with copying code snippets: windows dotnet core jenkinsfile 1`] = ` "node { stage('SCM') { @@ -233,7 +331,61 @@ sonar { }" `; -exports[`bitbucketcloud: can select devops platform and complete all the steps with copying code snippets: cfamily linux jenkinsfile 1`] = ` +exports[`bitbucketcloud: can select devops platform and complete all the steps with copying code snippets: c++ (automatic and other): build tools jenkinsfile 1`] = ` +"node { + stage('SCM') { + checkout scm + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + sh "\${scannerHome}/bin/sonar-scanner" + } + } +}" +`; + +exports[`bitbucketcloud: can select devops platform and complete all the steps with copying code snippets: c++ (automatic and other): build tools jenkinsfile 2`] = ` +"node { + stage('SCM') { + checkout scm + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + sh "\${scannerHome}/bin/sonar-scanner" + } + } +}" +`; + +exports[`bitbucketcloud: can select devops platform and complete all the steps with copying code snippets: c++ (automatic and other): build tools sonar-project.properties code 1`] = `"sonar.projectKey=my-project"`; + +exports[`bitbucketcloud: can select devops platform and complete all the steps with copying code snippets: c++ (automatic and other): build tools sonar-project.properties code 2`] = `"sonar.projectKey=my-project"`; + +exports[`bitbucketcloud: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: linux jenkinsfile 1`] = ` +"node { + stage('SCM') { + checkout scm + } + stage('Download Build Wrapper') { + sh "mkdir -p .sonar" + sh "curl -sSLo build-wrapper-linux-x86.zip http://localhost:9000/static/cpp/build-wrapper-linux-x86.zip" + sh "unzip -o build-wrapper-linux-x86.zip -d .sonar" + } + stage('Build') { + sh ".sonar/build-wrapper-linux-x86/build-wrapper-linux-x86-64 --out-dir bw-output " + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + sh "\${scannerHome}/bin/sonar-scanner -Dsonar.cfamily.compile-commands=bw-output/compile_commands.json" + } + } +}" +`; + +exports[`bitbucketcloud: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: linux jenkinsfile 2`] = ` "node { stage('SCM') { checkout scm @@ -255,7 +407,33 @@ exports[`bitbucketcloud: can select devops platform and complete all the steps w }" `; -exports[`bitbucketcloud: can select devops platform and complete all the steps with copying code snippets: cfamily macos jenkinsfile 1`] = ` +exports[`bitbucketcloud: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: macos jenkinsfile 1`] = ` +"node { + stage('SCM') { + checkout scm + } + stage('Download Build Wrapper') { + sh ''' + mkdir -p .sonar + curl -sSLo build-wrapper-macosx-x86.zip http://localhost:9000/static/cpp/build-wrapper-macosx-x86.zip + unzip -o build-wrapper-macosx-x86.zip -d .sonar + ''' + } + stage('Build') { + sh ''' + .sonar/build-wrapper-macosx-x86/build-wrapper-macosx-x86 --out-dir bw-output + ''' + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + sh "\${scannerHome}/bin/sonar-scanner -Dsonar.cfamily.compile-commands=bw-output/compile_commands.json" + } + } +}" +`; + +exports[`bitbucketcloud: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: macos jenkinsfile 2`] = ` "node { stage('SCM') { checkout scm @@ -281,7 +459,39 @@ exports[`bitbucketcloud: can select devops platform and complete all the steps w }" `; -exports[`bitbucketcloud: can select devops platform and complete all the steps with copying code snippets: cfamily windows jenkinsfile 1`] = ` +exports[`bitbucketcloud: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: windows jenkinsfile 1`] = ` +"node { + stage('SCM') { + checkout scm + } + stage('Download Build Wrapper') { + powershell ''' + $path = "$HOME/.sonar/build-wrapper-win-x86.zip" + rm build-wrapper-win-x86 -Recurse -Force -ErrorAction SilentlyContinue + rm $path -Force -ErrorAction SilentlyContinue + mkdir $HOME/.sonar + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + (New-Object System.Net.WebClient).DownloadFile(http://localhost:9000/static/cpp/build-wrapper-win-x86.zip", $path) + Add-Type -AssemblyName System.IO.Compression.FileSystem + [System.IO.Compression.ZipFile]::ExtractToDirectory($path, "$HOME/.sonar") + ''' + } + stage('Build') { + powershell ''' + $env:Path += ";$HOME/.sonar/build-wrapper-win-x86" + build-wrapper-win-x86-64 --out-dir bw-output + ''' + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + powershell "\${scannerHome}/bin/sonar-scanner -Dsonar.cfamily.compile-commands=bw-output/compile_commands.json" + } + } +}" +`; + +exports[`bitbucketcloud: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: windows jenkinsfile 2`] = ` "node { stage('SCM') { checkout scm @@ -359,26 +569,12 @@ exports[`bitbucketcloud: can select devops platform and complete all the steps w }" `; -exports[`bitbucketcloud: can select devops platform and complete all the steps with copying code snippets: other build tools jenkinsfile 1`] = ` -"node { - stage('SCM') { - checkout scm - } - stage('SonarQube Analysis') { - def scannerHome = tool 'SonarScanner'; - withSonarQubeEnv() { - sh "\${scannerHome}/bin/sonar-scanner" - } - } -}" -`; - -exports[`bitbucketcloud: can select devops platform and complete all the steps with copying code snippets: other build tools sonar-project.properties code 1`] = `"sonar.projectKey=my-project"`; - exports[`bitbucketcloud: can select devops platform and complete all the steps with copying code snippets: ref spec 1`] = `"+refs/heads/*:refs/remotes/@{remote}/*"`; exports[`bitbucketcloud: can select devops platform and complete all the steps with copying code snippets: sonar-project.properties code 1`] = `"sonar.projectKey=my-project"`; +exports[`bitbucketcloud: can select devops platform and complete all the steps with copying code snippets: sonar-project.properties code 2`] = `"sonar.projectKey=my-project"`; + exports[`bitbucketcloud: can select devops platform and complete all the steps with copying code snippets: windows dotnet core jenkinsfile 1`] = ` "node { stage('SCM') { @@ -440,7 +636,61 @@ sonar { }" `; -exports[`github: can select devops platform and complete all the steps with copying code snippets: cfamily linux jenkinsfile 1`] = ` +exports[`github: can select devops platform and complete all the steps with copying code snippets: c++ (automatic and other): build tools jenkinsfile 1`] = ` +"node { + stage('SCM') { + checkout scm + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + sh "\${scannerHome}/bin/sonar-scanner" + } + } +}" +`; + +exports[`github: can select devops platform and complete all the steps with copying code snippets: c++ (automatic and other): build tools jenkinsfile 2`] = ` +"node { + stage('SCM') { + checkout scm + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + sh "\${scannerHome}/bin/sonar-scanner" + } + } +}" +`; + +exports[`github: can select devops platform and complete all the steps with copying code snippets: c++ (automatic and other): build tools sonar-project.properties code 1`] = `"sonar.projectKey=my-project"`; + +exports[`github: can select devops platform and complete all the steps with copying code snippets: c++ (automatic and other): build tools sonar-project.properties code 2`] = `"sonar.projectKey=my-project"`; + +exports[`github: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: linux jenkinsfile 1`] = ` +"node { + stage('SCM') { + checkout scm + } + stage('Download Build Wrapper') { + sh "mkdir -p .sonar" + sh "curl -sSLo build-wrapper-linux-x86.zip http://localhost:9000/static/cpp/build-wrapper-linux-x86.zip" + sh "unzip -o build-wrapper-linux-x86.zip -d .sonar" + } + stage('Build') { + sh ".sonar/build-wrapper-linux-x86/build-wrapper-linux-x86-64 --out-dir bw-output " + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + sh "\${scannerHome}/bin/sonar-scanner -Dsonar.cfamily.compile-commands=bw-output/compile_commands.json" + } + } +}" +`; + +exports[`github: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: linux jenkinsfile 2`] = ` "node { stage('SCM') { checkout scm @@ -462,7 +712,33 @@ exports[`github: can select devops platform and complete all the steps with copy }" `; -exports[`github: can select devops platform and complete all the steps with copying code snippets: cfamily macos jenkinsfile 1`] = ` +exports[`github: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: macos jenkinsfile 1`] = ` +"node { + stage('SCM') { + checkout scm + } + stage('Download Build Wrapper') { + sh ''' + mkdir -p .sonar + curl -sSLo build-wrapper-macosx-x86.zip http://localhost:9000/static/cpp/build-wrapper-macosx-x86.zip + unzip -o build-wrapper-macosx-x86.zip -d .sonar + ''' + } + stage('Build') { + sh ''' + .sonar/build-wrapper-macosx-x86/build-wrapper-macosx-x86 --out-dir bw-output + ''' + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + sh "\${scannerHome}/bin/sonar-scanner -Dsonar.cfamily.compile-commands=bw-output/compile_commands.json" + } + } +}" +`; + +exports[`github: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: macos jenkinsfile 2`] = ` "node { stage('SCM') { checkout scm @@ -488,7 +764,39 @@ exports[`github: can select devops platform and complete all the steps with copy }" `; -exports[`github: can select devops platform and complete all the steps with copying code snippets: cfamily windows jenkinsfile 1`] = ` +exports[`github: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: windows jenkinsfile 1`] = ` +"node { + stage('SCM') { + checkout scm + } + stage('Download Build Wrapper') { + powershell ''' + $path = "$HOME/.sonar/build-wrapper-win-x86.zip" + rm build-wrapper-win-x86 -Recurse -Force -ErrorAction SilentlyContinue + rm $path -Force -ErrorAction SilentlyContinue + mkdir $HOME/.sonar + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + (New-Object System.Net.WebClient).DownloadFile(http://localhost:9000/static/cpp/build-wrapper-win-x86.zip", $path) + Add-Type -AssemblyName System.IO.Compression.FileSystem + [System.IO.Compression.ZipFile]::ExtractToDirectory($path, "$HOME/.sonar") + ''' + } + stage('Build') { + powershell ''' + $env:Path += ";$HOME/.sonar/build-wrapper-win-x86" + build-wrapper-win-x86-64 --out-dir bw-output + ''' + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + powershell "\${scannerHome}/bin/sonar-scanner -Dsonar.cfamily.compile-commands=bw-output/compile_commands.json" + } + } +}" +`; + +exports[`github: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: windows jenkinsfile 2`] = ` "node { stage('SCM') { checkout scm @@ -566,26 +874,12 @@ exports[`github: can select devops platform and complete all the steps with copy }" `; -exports[`github: can select devops platform and complete all the steps with copying code snippets: other build tools jenkinsfile 1`] = ` -"node { - stage('SCM') { - checkout scm - } - stage('SonarQube Analysis') { - def scannerHome = tool 'SonarScanner'; - withSonarQubeEnv() { - sh "\${scannerHome}/bin/sonar-scanner" - } - } -}" -`; - -exports[`github: can select devops platform and complete all the steps with copying code snippets: other build tools sonar-project.properties code 1`] = `"sonar.projectKey=my-project"`; - exports[`github: can select devops platform and complete all the steps with copying code snippets: ref spec 1`] = `"+refs/heads/*:refs/remotes/@{remote}/*"`; exports[`github: can select devops platform and complete all the steps with copying code snippets: sonar-project.properties code 1`] = `"sonar.projectKey=my-project"`; +exports[`github: can select devops platform and complete all the steps with copying code snippets: sonar-project.properties code 2`] = `"sonar.projectKey=my-project"`; + exports[`github: can select devops platform and complete all the steps with copying code snippets: windows dotnet core jenkinsfile 1`] = ` "node { stage('SCM') { @@ -647,7 +941,61 @@ sonar { }" `; -exports[`gitlab: can select devops platform and complete all the steps with copying code snippets: cfamily linux jenkinsfile 1`] = ` +exports[`gitlab: can select devops platform and complete all the steps with copying code snippets: c++ (automatic and other): build tools jenkinsfile 1`] = ` +"node { + stage('SCM') { + checkout scm + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + sh "\${scannerHome}/bin/sonar-scanner" + } + } +}" +`; + +exports[`gitlab: can select devops platform and complete all the steps with copying code snippets: c++ (automatic and other): build tools jenkinsfile 2`] = ` +"node { + stage('SCM') { + checkout scm + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + sh "\${scannerHome}/bin/sonar-scanner" + } + } +}" +`; + +exports[`gitlab: can select devops platform and complete all the steps with copying code snippets: c++ (automatic and other): build tools sonar-project.properties code 1`] = `"sonar.projectKey=my-project"`; + +exports[`gitlab: can select devops platform and complete all the steps with copying code snippets: c++ (automatic and other): build tools sonar-project.properties code 2`] = `"sonar.projectKey=my-project"`; + +exports[`gitlab: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: linux jenkinsfile 1`] = ` +"node { + stage('SCM') { + checkout scm + } + stage('Download Build Wrapper') { + sh "mkdir -p .sonar" + sh "curl -sSLo build-wrapper-linux-x86.zip http://localhost:9000/static/cpp/build-wrapper-linux-x86.zip" + sh "unzip -o build-wrapper-linux-x86.zip -d .sonar" + } + stage('Build') { + sh ".sonar/build-wrapper-linux-x86/build-wrapper-linux-x86-64 --out-dir bw-output " + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + sh "\${scannerHome}/bin/sonar-scanner -Dsonar.cfamily.compile-commands=bw-output/compile_commands.json" + } + } +}" +`; + +exports[`gitlab: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: linux jenkinsfile 2`] = ` "node { stage('SCM') { checkout scm @@ -669,7 +1017,33 @@ exports[`gitlab: can select devops platform and complete all the steps with copy }" `; -exports[`gitlab: can select devops platform and complete all the steps with copying code snippets: cfamily macos jenkinsfile 1`] = ` +exports[`gitlab: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: macos jenkinsfile 1`] = ` +"node { + stage('SCM') { + checkout scm + } + stage('Download Build Wrapper') { + sh ''' + mkdir -p .sonar + curl -sSLo build-wrapper-macosx-x86.zip http://localhost:9000/static/cpp/build-wrapper-macosx-x86.zip + unzip -o build-wrapper-macosx-x86.zip -d .sonar + ''' + } + stage('Build') { + sh ''' + .sonar/build-wrapper-macosx-x86/build-wrapper-macosx-x86 --out-dir bw-output + ''' + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + sh "\${scannerHome}/bin/sonar-scanner -Dsonar.cfamily.compile-commands=bw-output/compile_commands.json" + } + } +}" +`; + +exports[`gitlab: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: macos jenkinsfile 2`] = ` "node { stage('SCM') { checkout scm @@ -695,7 +1069,39 @@ exports[`gitlab: can select devops platform and complete all the steps with copy }" `; -exports[`gitlab: can select devops platform and complete all the steps with copying code snippets: cfamily windows jenkinsfile 1`] = ` +exports[`gitlab: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: windows jenkinsfile 1`] = ` +"node { + stage('SCM') { + checkout scm + } + stage('Download Build Wrapper') { + powershell ''' + $path = "$HOME/.sonar/build-wrapper-win-x86.zip" + rm build-wrapper-win-x86 -Recurse -Force -ErrorAction SilentlyContinue + rm $path -Force -ErrorAction SilentlyContinue + mkdir $HOME/.sonar + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + (New-Object System.Net.WebClient).DownloadFile(http://localhost:9000/static/cpp/build-wrapper-win-x86.zip", $path) + Add-Type -AssemblyName System.IO.Compression.FileSystem + [System.IO.Compression.ZipFile]::ExtractToDirectory($path, "$HOME/.sonar") + ''' + } + stage('Build') { + powershell ''' + $env:Path += ";$HOME/.sonar/build-wrapper-win-x86" + build-wrapper-win-x86-64 --out-dir bw-output + ''' + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + powershell "\${scannerHome}/bin/sonar-scanner -Dsonar.cfamily.compile-commands=bw-output/compile_commands.json" + } + } +}" +`; + +exports[`gitlab: can select devops platform and complete all the steps with copying code snippets: c++ (manual) and objectivec: windows jenkinsfile 2`] = ` "node { stage('SCM') { checkout scm @@ -773,26 +1179,12 @@ exports[`gitlab: can select devops platform and complete all the steps with copy }" `; -exports[`gitlab: can select devops platform and complete all the steps with copying code snippets: other build tools jenkinsfile 1`] = ` -"node { - stage('SCM') { - checkout scm - } - stage('SonarQube Analysis') { - def scannerHome = tool 'SonarScanner'; - withSonarQubeEnv() { - sh "\${scannerHome}/bin/sonar-scanner" - } - } -}" -`; - -exports[`gitlab: can select devops platform and complete all the steps with copying code snippets: other build tools sonar-project.properties code 1`] = `"sonar.projectKey=my-project"`; - exports[`gitlab: can select devops platform and complete all the steps with copying code snippets: ref spec 1`] = `"+refs/heads/*:refs/remotes/@{remote}/*"`; exports[`gitlab: can select devops platform and complete all the steps with copying code snippets: sonar-project.properties code 1`] = `"sonar.projectKey=my-project"`; +exports[`gitlab: can select devops platform and complete all the steps with copying code snippets: sonar-project.properties code 2`] = `"sonar.projectKey=my-project"`; + exports[`gitlab: can select devops platform and complete all the steps with copying code snippets: windows dotnet core jenkinsfile 1`] = ` "node { stage('SCM') { diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/CFamilly.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/CFamilly.tsx deleted file mode 100644 index 03669459838..00000000000 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/CFamilly.tsx +++ /dev/null @@ -1,144 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 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 { NumberedListItem } from 'design-system'; -import * as React from 'react'; -import { translate } from '../../../../helpers/l10n'; -import { CompilationInfo } from '../../components/CompilationInfo'; -import DefaultProjectKey from '../../components/DefaultProjectKey'; -import GithubCFamilyExampleRepositories from '../../components/GithubCFamilyExampleRepositories'; -import RenderOptions from '../../components/RenderOptions'; -import { OSs, TutorialModes } from '../../types'; -import { LanguageProps } from '../JenkinsStep'; -import CreateJenkinsfileBulletPoint from './CreateJenkinsfileBulletPoint'; - -const YAML_MAP: Record string> = { - [OSs.Linux]: (baseUrl) => `node { - stage('SCM') { - checkout scm - } - stage('Download Build Wrapper') { - sh "mkdir -p .sonar" - sh "curl -sSLo build-wrapper-linux-x86.zip ${baseUrl}/static/cpp/build-wrapper-linux-x86.zip" - sh "unzip -o build-wrapper-linux-x86.zip -d .sonar" - } - stage('Build') { - sh ".sonar/build-wrapper-linux-x86/build-wrapper-linux-x86-64 --out-dir bw-output " - } - stage('SonarQube Analysis') { - def scannerHome = tool 'SonarScanner'; - withSonarQubeEnv() { - sh "\${scannerHome}/bin/sonar-scanner -Dsonar.cfamily.compile-commands=bw-output/compile_commands.json" - } - } -}`, - [OSs.MacOS]: (baseUrl) => `node { - stage('SCM') { - checkout scm - } - stage('Download Build Wrapper') { - sh ''' - mkdir -p .sonar - curl -sSLo build-wrapper-macosx-x86.zip ${baseUrl}/static/cpp/build-wrapper-macosx-x86.zip - unzip -o build-wrapper-macosx-x86.zip -d .sonar - ''' - } - stage('Build') { - sh ''' - .sonar/build-wrapper-macosx-x86/build-wrapper-macosx-x86 --out-dir bw-output - ''' - } - stage('SonarQube Analysis') { - def scannerHome = tool 'SonarScanner'; - withSonarQubeEnv() { - sh "\${scannerHome}/bin/sonar-scanner -Dsonar.cfamily.compile-commands=bw-output/compile_commands.json" - } - } -}`, - [OSs.Windows]: (baseUrl) => `node { - stage('SCM') { - checkout scm - } - stage('Download Build Wrapper') { - powershell ''' - $path = "$HOME/.sonar/build-wrapper-win-x86.zip" - rm build-wrapper-win-x86 -Recurse -Force -ErrorAction SilentlyContinue - rm $path -Force -ErrorAction SilentlyContinue - mkdir $HOME/.sonar - [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - (New-Object System.Net.WebClient).DownloadFile(${baseUrl}/static/cpp/build-wrapper-win-x86.zip", $path) - Add-Type -AssemblyName System.IO.Compression.FileSystem - [System.IO.Compression.ZipFile]::ExtractToDirectory($path, "$HOME/.sonar") - ''' - } - stage('Build') { - powershell ''' - $env:Path += ";$HOME/.sonar/build-wrapper-win-x86" - build-wrapper-win-x86-64 --out-dir bw-output - ''' - } - stage('SonarQube Analysis') { - def scannerHome = tool 'SonarScanner'; - withSonarQubeEnv() { - powershell "\${scannerHome}/bin/sonar-scanner -Dsonar.cfamily.compile-commands=bw-output/compile_commands.json" - } - } -}`, -}; - -export default function CFamilly(props: LanguageProps) { - const { baseUrl, component, onDone } = props; - const [os, setOs] = React.useState(OSs.Linux); - - React.useEffect(() => { - onDone(os !== undefined); - }, [os, onDone]); - - return ( - <> - - - {translate('onboarding.build.other.os')} - setOs(value as OSs)} - options={Object.values(OSs)} - /> - {os && ( - - )} - - {os && ( - - - - )} - - ); -} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/CFamily.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/CFamily.tsx new file mode 100644 index 00000000000..cef2de8da04 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/CFamily.tsx @@ -0,0 +1,145 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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 { NumberedListItem } from 'design-system'; +import * as React from 'react'; +import { translate } from '../../../../helpers/l10n'; +import { CompilationInfo } from '../../components/CompilationInfo'; +import DefaultProjectKey from '../../components/DefaultProjectKey'; +import GithubCFamilyExampleRepositories from '../../components/GithubCFamilyExampleRepositories'; +import RenderOptions from '../../components/RenderOptions'; +import { AutoConfig, BuildTools, OSs, TutorialModes } from '../../types'; +import { LanguageProps } from '../JenkinsStep'; +import CreateJenkinsfileBulletPoint from './CreateJenkinsfileBulletPoint'; +import Other from './Other'; + +const YAML_MAP: Record string> = { + [OSs.Linux]: (baseUrl) => `node { + stage('SCM') { + checkout scm + } + stage('Download Build Wrapper') { + sh "mkdir -p .sonar" + sh "curl -sSLo build-wrapper-linux-x86.zip ${baseUrl}/static/cpp/build-wrapper-linux-x86.zip" + sh "unzip -o build-wrapper-linux-x86.zip -d .sonar" + } + stage('Build') { + sh ".sonar/build-wrapper-linux-x86/build-wrapper-linux-x86-64 --out-dir bw-output " + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + sh "\${scannerHome}/bin/sonar-scanner -Dsonar.cfamily.compile-commands=bw-output/compile_commands.json" + } + } +}`, + [OSs.MacOS]: (baseUrl) => `node { + stage('SCM') { + checkout scm + } + stage('Download Build Wrapper') { + sh ''' + mkdir -p .sonar + curl -sSLo build-wrapper-macosx-x86.zip ${baseUrl}/static/cpp/build-wrapper-macosx-x86.zip + unzip -o build-wrapper-macosx-x86.zip -d .sonar + ''' + } + stage('Build') { + sh ''' + .sonar/build-wrapper-macosx-x86/build-wrapper-macosx-x86 --out-dir bw-output + ''' + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + sh "\${scannerHome}/bin/sonar-scanner -Dsonar.cfamily.compile-commands=bw-output/compile_commands.json" + } + } +}`, + [OSs.Windows]: (baseUrl) => `node { + stage('SCM') { + checkout scm + } + stage('Download Build Wrapper') { + powershell ''' + $path = "$HOME/.sonar/build-wrapper-win-x86.zip" + rm build-wrapper-win-x86 -Recurse -Force -ErrorAction SilentlyContinue + rm $path -Force -ErrorAction SilentlyContinue + mkdir $HOME/.sonar + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + (New-Object System.Net.WebClient).DownloadFile(${baseUrl}/static/cpp/build-wrapper-win-x86.zip", $path) + Add-Type -AssemblyName System.IO.Compression.FileSystem + [System.IO.Compression.ZipFile]::ExtractToDirectory($path, "$HOME/.sonar") + ''' + } + stage('Build') { + powershell ''' + $env:Path += ";$HOME/.sonar/build-wrapper-win-x86" + build-wrapper-win-x86-64 --out-dir bw-output + ''' + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + powershell "\${scannerHome}/bin/sonar-scanner -Dsonar.cfamily.compile-commands=bw-output/compile_commands.json" + } + } +}`, +}; + +export default function CFamily(props: Readonly) { + const { baseUrl, config, component } = props; + const [os, setOs] = React.useState(OSs.Linux); + + if (config.buildTool === BuildTools.Cpp && config.autoConfig === AutoConfig.Automatic) { + return ; + } + + return ( + <> + + + {translate('onboarding.build.other.os')} + setOs(value)} + options={Object.values(OSs)} + /> + { + + } + + { + + + + } + + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/DotNet.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/DotNet.tsx index 3e34cb6a2e8..bf5e5a08d76 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/DotNet.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/DotNet.tsx @@ -43,15 +43,11 @@ const DotOS: { [key in keyof typeof DotNetFlavor]: OSDotNet } = { }; export default function DotNet(props: LanguageProps) { - const { component, onDone } = props; + const { component } = props; const [flavorComponent, setFlavorComponent] = React.useState('win_core'); const DotNetTutorial = flavorComponent && DotNetFlavor[flavorComponent]; - React.useEffect(() => { - onDone(flavorComponent !== undefined); - }, [flavorComponent, onDone]); - return ( <> diff --git a/server/sonar-web/src/main/js/components/tutorials/other/BuildToolForm.tsx b/server/sonar-web/src/main/js/components/tutorials/other/BuildToolForm.tsx index f790489a03c..f4bc5dab064 100644 --- a/server/sonar-web/src/main/js/components/tutorials/other/BuildToolForm.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/other/BuildToolForm.tsx @@ -17,97 +17,70 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { ToggleButton } from 'design-system'; import * as React from 'react'; import { translate } from '../../../helpers/l10n'; import { withCLanguageFeature } from '../../hoc/withCLanguageFeature'; +import BuildConfigSelection from '../components/BuildConfigSelection'; import GithubCFamilyExampleRepositories from '../components/GithubCFamilyExampleRepositories'; import RenderOptions from '../components/RenderOptions'; -import { BuildTools, ManualTutorialConfig, OSs, TutorialModes } from '../types'; +import { BuildTools, OSs, TutorialConfig, TutorialModes } from '../types'; +import { shouldShowGithubCFamilyExampleRepositories } from '../utils'; interface Props { - config?: ManualTutorialConfig; + config: TutorialConfig; hasCLanguageFeature: boolean; - onDone: (config: ManualTutorialConfig) => void; + os?: OSs; + setConfig: (config: TutorialConfig) => void; + setOs: (os: OSs) => void; } -interface State { - config: ManualTutorialConfig; -} +export function BuildToolForm(props: Readonly) { + const { config, setConfig, os, setOs, hasCLanguageFeature } = props; -export class BuildToolForm extends React.PureComponent { - constructor(props: Props) { - super(props); - this.state = { - config: this.props.config || {}, - }; - } - - handleBuildToolChange = (buildTool: BuildTools) => { - const selectOsByDefault = (buildTool === BuildTools.CFamily || - buildTool === BuildTools.Other) && { + function handleConfigChange(newConfig: TutorialConfig) { + const selectOsByDefault = (newConfig.buildTool === BuildTools.Cpp || + newConfig.buildTool === BuildTools.ObjectiveC || + newConfig.buildTool === BuildTools.Other) && { os: OSs.Linux, }; - this.setState({ config: { buildTool, ...selectOsByDefault } }, () => { - this.props.onDone(this.state.config); + setConfig({ + ...config, + ...newConfig, + ...selectOsByDefault, }); - }; - - handleOSChange = (os: OSs) => { - this.setState( - ({ config }) => ({ config: { buildTool: config.buildTool, os } }), - () => { - this.props.onDone(this.state.config); - }, - ); - }; - - render() { - const { config } = this.state; - const { hasCLanguageFeature } = this.props; - const buildTools = [BuildTools.Maven, BuildTools.Gradle, BuildTools.DotNet]; - if (hasCLanguageFeature) { - buildTools.push(BuildTools.CFamily); - } - buildTools.push(BuildTools.Other); - - return ( - <> -
- - ({ - label: translate('onboarding.build', tool), - value: tool, - }))} - value={config.buildTool} - /> -
- - {(config.buildTool === BuildTools.Other || config.buildTool === BuildTools.CFamily) && ( - - )} - - {config.buildTool === BuildTools.CFamily && config.os && ( - - )} - - ); } + + return ( + <> + {config && ( + + )} + {(config.buildTool === BuildTools.Other || + config.buildTool === BuildTools.Cpp || + config.buildTool === BuildTools.ObjectiveC) && ( + setOs(value)} + optionLabelKey="onboarding.build.other.os" + options={[OSs.Linux, OSs.Windows, OSs.MacOS]} + titleLabelKey="onboarding.build.other.os" + /> + )} + {shouldShowGithubCFamilyExampleRepositories(config) && ( + + )} + + ); } export default withCLanguageFeature(BuildToolForm); diff --git a/server/sonar-web/src/main/js/components/tutorials/other/ProjectAnalysisStep.tsx b/server/sonar-web/src/main/js/components/tutorials/other/ProjectAnalysisStep.tsx index 799135f27b5..9d5e24d711a 100644 --- a/server/sonar-web/src/main/js/components/tutorials/other/ProjectAnalysisStep.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/other/ProjectAnalysisStep.tsx @@ -22,7 +22,7 @@ import * as React from 'react'; import { translate } from '../../../helpers/l10n'; import { Component } from '../../../types/types'; import Step from '../components/Step'; -import { ManualTutorialConfig } from '../types'; +import { OSs, TutorialConfig } from '../types'; import BuildToolForm from './BuildToolForm'; import AnalysisCommand from './commands/AnalysisCommand'; @@ -30,58 +30,46 @@ interface Props { baseUrl: string; component: Component; isLocal: boolean; - onFinish?: (projectKey?: string) => void; open: boolean; stepNumber: number; token?: string; } -interface State { - config?: ManualTutorialConfig; -} - -export default class ProjectAnalysisStep extends React.PureComponent { - state: State = {}; +export default function ProjectAnalysisStep(props: Readonly) { + const { component, open, stepNumber, baseUrl, isLocal, token } = props; - handleBuildToolSelect = (config: ManualTutorialConfig) => { - const { component } = this.props; - this.setState({ config }); - if (this.props.onFinish) { - this.props.onFinish(component.key); - } - }; + const [config, setConfig] = React.useState({}); + const [os, setOs] = React.useState(OSs.Linux); - renderForm = () => { - const { component, baseUrl, isLocal, token } = this.props; + function renderForm() { return (
- + - {this.state.config && ( + {config && (
)}
); - }; - - render() { - return ( - - ); } + + return ( + + ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/other/__tests__/OtherTutorial-it.tsx b/server/sonar-web/src/main/js/components/tutorials/other/__tests__/OtherTutorial-it.tsx index 53679e3194d..f366797deac 100644 --- a/server/sonar-web/src/main/js/components/tutorials/other/__tests__/OtherTutorial-it.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/other/__tests__/OtherTutorial-it.tsx @@ -121,40 +121,63 @@ it('can choose build tools and copy provided settings', async () => { expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('dotnet framework: execute command 2'); expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot('dotnet framework: execute command 3'); - // C Family - Linux - await user.click(ui.cFamilyBuildButton.get()); + // C++ - Automatic + await user.click(ui.cppBuildButton.get()); + await user.click(ui.linuxButton.get()); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( + 'c++ (automatic) and other linux: execute scanner', + ); + await user.click(ui.windowsButton.get()); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( + 'c++ (automatic) and other windows: execute scanner', + ); + await user.click(ui.macosButton.get()); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( + 'c++ (automatic) and other macos: execute scanner', + ); + + // C++ - Linux + await user.click(ui.autoConfigManual.get()); await user.click(ui.linuxButton.get()); expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot( - 'cfamily linux: execute build wrapper', + 'c++ (manual) linux: execute build wrapper', ); - expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('cfamily linux: execute scanner'); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('c++ (manual) linux: execute scanner'); - // C Family - Windows + // C++ - Windows await user.click(ui.windowsButton.get()); expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot( - 'cfamily windows: execute build wrapper', + 'c++ (manual) windows: execute build wrapper', + ); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( + 'c++ (manual) windows: execute scanner', ); - expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('cfamily windows: execute scanner'); - // C Family - MacOS + // C++ - MacOS await user.click(ui.macosButton.get()); expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot( - 'cfamily macos: execute build wrapper', + 'c++ (manual) macos: execute build wrapper', ); - expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('cfamily macos: execute scanner'); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('c++ (manual) macos: execute scanner'); // Other - Linux await user.click(ui.otherBuildButton.get()); await user.click(ui.linuxButton.get()); - expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('other linux: execute scanner'); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot( + 'c++ (automatic) and other linux: execute scanner', + ); // Other - Windows await user.click(ui.windowsButton.get()); - expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('other windows: execute scanner'); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot( + 'c++ (automatic) and other windows: execute scanner', + ); // Other - MacOS await user.click(ui.macosButton.get()); - expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('other macos: execute scanner'); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot( + 'c++ (automatic) and other macos: execute scanner', + ); }); function renderOtherTutorial({ diff --git a/server/sonar-web/src/main/js/components/tutorials/other/__tests__/__snapshots__/OtherTutorial-it.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/other/__tests__/__snapshots__/OtherTutorial-it.tsx.snap index 8371fca630e..a5e42283955 100644 --- a/server/sonar-web/src/main/js/components/tutorials/other/__tests__/__snapshots__/OtherTutorial-it.tsx.snap +++ b/server/sonar-web/src/main/js/components/tutorials/other/__tests__/__snapshots__/OtherTutorial-it.tsx.snap @@ -1,13 +1,64 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`can choose build tools and copy provided settings: cfamily linux: execute build wrapper 1`] = ` +exports[`can choose build tools and copy provided settings: c++ (automatic) and other linux: execute scanner 1`] = ` +"sonar-scanner \\ + -Dsonar.projectKey=my-project \\ + -Dsonar.sources=. \\ + -Dsonar.host.url=http://localhost:9000" +`; + +exports[`can choose build tools and copy provided settings: c++ (automatic) and other linux: execute scanner 2`] = ` +"export SONAR_SCANNER_VERSION=5.0.1.3006 +export SONAR_SCANNER_HOME=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux +curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux.zip +unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/ +export PATH=$SONAR_SCANNER_HOME/bin:$PATH +export SONAR_SCANNER_OPTS="-server" +" +`; + +exports[`can choose build tools and copy provided settings: c++ (automatic) and other macos: execute scanner 1`] = ` +"sonar-scanner \\ + -Dsonar.projectKey=my-project \\ + -Dsonar.sources=. \\ + -Dsonar.host.url=http://localhost:9000" +`; + +exports[`can choose build tools and copy provided settings: c++ (automatic) and other macos: execute scanner 2`] = ` +"export SONAR_SCANNER_VERSION=5.0.1.3006 +export SONAR_SCANNER_HOME=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-macosx +curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-macosx.zip +unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/ +export PATH=$SONAR_SCANNER_HOME/bin:$PATH +export SONAR_SCANNER_OPTS="-server" +" +`; + +exports[`can choose build tools and copy provided settings: c++ (automatic) and other windows: execute scanner 1`] = `"sonar-scanner.bat -D"sonar.projectKey=my-project" -D"sonar.sources=." -D"sonar.host.url=http://localhost:9000""`; + +exports[`can choose build tools and copy provided settings: c++ (automatic) and other windows: execute scanner 2`] = ` +"$env:SONAR_SCANNER_VERSION = "5.0.1.3006" +$env:SONAR_DIRECTORY = [System.IO.Path]::Combine($(get-location).Path,".sonar") +$env:SONAR_SCANNER_HOME = "$env:SONAR_DIRECTORY/sonar-scanner-$env:SONAR_SCANNER_VERSION-windows" +rm $env:SONAR_SCANNER_HOME -Force -Recurse -ErrorAction SilentlyContinue +New-Item -path $env:SONAR_SCANNER_HOME -type directory +(New-Object System.Net.WebClient).DownloadFile("https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$env:SONAR_SCANNER_VERSION-windows.zip", "$env:SONAR_DIRECTORY/sonar-scanner.zip") +Add-Type -AssemblyName System.IO.Compression.FileSystem +[System.IO.Compression.ZipFile]::ExtractToDirectory("$env:SONAR_DIRECTORY/sonar-scanner.zip", "$env:SONAR_DIRECTORY") +rm ./.sonar/sonar-scanner.zip -Force -ErrorAction SilentlyContinue +$env:Path += ";$env:SONAR_SCANNER_HOME/bin" +$env:SONAR_SCANNER_OPTS="-server" +" +`; + +exports[`can choose build tools and copy provided settings: c++ (manual) linux: execute build wrapper 1`] = ` "curl --create-dirs -sSLo $HOME/.sonar/build-wrapper-linux-x86.zip http://localhost:9000/static/cpp/build-wrapper-linux-x86.zip unzip -o $HOME/.sonar/build-wrapper-linux-x86.zip -d $HOME/.sonar/ export PATH=$HOME/.sonar/build-wrapper-linux-x86:$PATH " `; -exports[`can choose build tools and copy provided settings: cfamily linux: execute scanner 1`] = ` +exports[`can choose build tools and copy provided settings: c++ (manual) linux: execute scanner 1`] = ` "export SONAR_SCANNER_VERSION=5.0.1.3006 export SONAR_SCANNER_HOME=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux.zip @@ -17,14 +68,14 @@ export SONAR_SCANNER_OPTS="-server" " `; -exports[`can choose build tools and copy provided settings: cfamily macos: execute build wrapper 1`] = ` +exports[`can choose build tools and copy provided settings: c++ (manual) macos: execute build wrapper 1`] = ` "curl --create-dirs -sSLo $HOME/.sonar/build-wrapper-macosx-x86.zip http://localhost:9000/static/cpp/build-wrapper-macosx-x86.zip unzip -o $HOME/.sonar/build-wrapper-macosx-x86.zip -d $HOME/.sonar/ export PATH=$HOME/.sonar/build-wrapper-macosx-x86:$PATH " `; -exports[`can choose build tools and copy provided settings: cfamily macos: execute scanner 1`] = ` +exports[`can choose build tools and copy provided settings: c++ (manual) macos: execute scanner 1`] = ` "export SONAR_SCANNER_VERSION=5.0.1.3006 export SONAR_SCANNER_HOME=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-macosx curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-macosx.zip @@ -34,7 +85,7 @@ export SONAR_SCANNER_OPTS="-server" " `; -exports[`can choose build tools and copy provided settings: cfamily windows: execute build wrapper 1`] = ` +exports[`can choose build tools and copy provided settings: c++ (manual) windows: execute build wrapper 1`] = ` "$env:SONAR_DIRECTORY = [System.IO.Path]::Combine($(get-location).Path,".sonar") rm "$env:SONAR_DIRECTORY/build-wrapper-win-x86" -Force -Recurse -ErrorAction SilentlyContinue New-Item -path $env:SONAR_DIRECTORY/build-wrapper-win-x86 -type directory @@ -45,7 +96,7 @@ $env:Path += ";$env:SONAR_DIRECTORY/build-wrapper-win-x86" " `; -exports[`can choose build tools and copy provided settings: cfamily windows: execute scanner 1`] = ` +exports[`can choose build tools and copy provided settings: c++ (manual) windows: execute scanner 1`] = ` "$env:SONAR_SCANNER_VERSION = "5.0.1.3006" $env:SONAR_DIRECTORY = [System.IO.Path]::Combine($(get-location).Path,".sonar") $env:SONAR_SCANNER_HOME = "$env:SONAR_DIRECTORY/sonar-scanner-$env:SONAR_SCANNER_VERSION-windows" @@ -95,38 +146,3 @@ exports[`can choose build tools and copy provided settings: maven: execute scann -Dsonar.host.url=http://localhost:9000 \\ -Dsonar.token=generatedtoken2" `; - -exports[`can choose build tools and copy provided settings: other linux: execute scanner 1`] = ` -"export SONAR_SCANNER_VERSION=5.0.1.3006 -export SONAR_SCANNER_HOME=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux -curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux.zip -unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/ -export PATH=$SONAR_SCANNER_HOME/bin:$PATH -export SONAR_SCANNER_OPTS="-server" -" -`; - -exports[`can choose build tools and copy provided settings: other macos: execute scanner 1`] = ` -"export SONAR_SCANNER_VERSION=5.0.1.3006 -export SONAR_SCANNER_HOME=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-macosx -curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-macosx.zip -unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/ -export PATH=$SONAR_SCANNER_HOME/bin:$PATH -export SONAR_SCANNER_OPTS="-server" -" -`; - -exports[`can choose build tools and copy provided settings: other windows: execute scanner 1`] = ` -"$env:SONAR_SCANNER_VERSION = "5.0.1.3006" -$env:SONAR_DIRECTORY = [System.IO.Path]::Combine($(get-location).Path,".sonar") -$env:SONAR_SCANNER_HOME = "$env:SONAR_DIRECTORY/sonar-scanner-$env:SONAR_SCANNER_VERSION-windows" -rm $env:SONAR_SCANNER_HOME -Force -Recurse -ErrorAction SilentlyContinue -New-Item -path $env:SONAR_SCANNER_HOME -type directory -(New-Object System.Net.WebClient).DownloadFile("https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$env:SONAR_SCANNER_VERSION-windows.zip", "$env:SONAR_DIRECTORY/sonar-scanner.zip") -Add-Type -AssemblyName System.IO.Compression.FileSystem -[System.IO.Compression.ZipFile]::ExtractToDirectory("$env:SONAR_DIRECTORY/sonar-scanner.zip", "$env:SONAR_DIRECTORY") -rm ./.sonar/sonar-scanner.zip -Force -ErrorAction SilentlyContinue -$env:Path += ";$env:SONAR_SCANNER_HOME/bin" -$env:SONAR_SCANNER_OPTS="-server" -" -`; diff --git a/server/sonar-web/src/main/js/components/tutorials/other/commands/AnalysisCommand.tsx b/server/sonar-web/src/main/js/components/tutorials/other/commands/AnalysisCommand.tsx index bae1666034f..f8fb5b2f954 100644 --- a/server/sonar-web/src/main/js/components/tutorials/other/commands/AnalysisCommand.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/other/commands/AnalysisCommand.tsx @@ -19,7 +19,7 @@ */ import * as React from 'react'; import { Component } from '../../../../types/types'; -import { BuildTools, ManualTutorialConfig } from '../../types'; +import { AutoConfig, BuildTools, OSs, TutorialConfig } from '../../types'; import ClangGCCCustom from './ClangGCCCommand'; import DotNet from './DotNet'; import JavaGradle from './JavaGradle'; @@ -29,19 +29,20 @@ import Other from './Other'; export interface AnalysisCommandProps { baseUrl: string; component: Component; + config: TutorialConfig; isLocal: boolean; - languageConfig: ManualTutorialConfig; + os: OSs; token?: string; } export default function AnalysisCommand(props: AnalysisCommandProps) { - const { component, baseUrl, isLocal, languageConfig, token } = props; + const { config, os, component, baseUrl, isLocal, token } = props; - if (!token) { + if (typeof token === 'undefined') { return null; } - switch (languageConfig.buildTool) { + switch (config.buildTool) { case BuildTools.Maven: return ; @@ -51,27 +52,27 @@ export default function AnalysisCommand(props: AnalysisCommandProps) { case BuildTools.DotNet: return ; - case BuildTools.CFamily: - return languageConfig.os !== undefined ? ( - - ) : null; - case BuildTools.Other: - return languageConfig.os !== undefined ? ( - + ); + + case BuildTools.Cpp: + case BuildTools.ObjectiveC: + if (config.buildTool === BuildTools.Cpp && config.autoConfig === AutoConfig.Automatic) { + return ( + + ); + } + return ( + - ) : null; + ); default: return null; diff --git a/server/sonar-web/src/main/js/components/tutorials/test-utils.ts b/server/sonar-web/src/main/js/components/tutorials/test-utils.ts index 591b1e8e12f..44f9de38066 100644 --- a/server/sonar-web/src/main/js/components/tutorials/test-utils.ts +++ b/server/sonar-web/src/main/js/components/tutorials/test-utils.ts @@ -72,7 +72,8 @@ export function getTutorialBuildButtons() { gradleBuildButton: byRole('radio', { name: `onboarding.build.${BuildTools.Gradle}` }), gradleDSLButton: (name: GradleBuildDSL) => byRole('radio', { name }), dotnetBuildButton: byRole('radio', { name: `onboarding.build.${BuildTools.DotNet}` }), - cFamilyBuildButton: byRole('radio', { name: `onboarding.build.${BuildTools.CFamily}` }), + cppBuildButton: byRole('radio', { name: `onboarding.build.${BuildTools.Cpp}` }), + objCBuildButton: byRole('radio', { name: `onboarding.build.${BuildTools.ObjectiveC}` }), otherBuildButton: byRole('radio', { name: `onboarding.build.${BuildTools.Other}` }), windowsDotnetCoreButton: byRole('radio', { name: `onboarding.build.${BuildTools.DotNet}.win_core`, @@ -92,5 +93,7 @@ export function getTutorialBuildButtons() { linuxButton: byRole('radio', { name: `onboarding.build.other.os.${OSs.Linux}` }), windowsButton: byRole('radio', { name: `onboarding.build.other.os.${OSs.Windows}` }), macosButton: byRole('radio', { name: `onboarding.build.other.os.${OSs.MacOS}` }), + autoConfigAutomatic: byRole('radio', { name: 'onboarding.build.cpp.autoconfig.automatic' }), + autoConfigManual: byRole('radio', { name: 'onboarding.build.cpp.autoconfig.manual' }), }; } diff --git a/server/sonar-web/src/main/js/components/tutorials/types.ts b/server/sonar-web/src/main/js/components/tutorials/types.ts index b9d8a309066..afb938c12b9 100644 --- a/server/sonar-web/src/main/js/components/tutorials/types.ts +++ b/server/sonar-web/src/main/js/components/tutorials/types.ts @@ -30,7 +30,8 @@ export enum TutorialModes { export enum BuildTools { Maven = 'maven', Gradle = 'gradle', - CFamily = 'cfamily', + Cpp = 'cpp', + ObjectiveC = 'objectivec', DotNet = 'dotnet', Other = 'other', } @@ -46,6 +47,12 @@ export enum OSs { MacOS = 'mac', } -export type ManualTutorialConfig = - | { buildTool?: BuildTools.Maven | BuildTools.Gradle | BuildTools.DotNet } - | { buildTool: BuildTools.Other | BuildTools.CFamily; os?: OSs }; +export enum AutoConfig { + Automatic = 'automatic', + Manual = 'manual', +} + +export type TutorialConfig = { + autoConfig?: AutoConfig; + buildTool?: BuildTools; +}; diff --git a/server/sonar-web/src/main/js/components/tutorials/utils.ts b/server/sonar-web/src/main/js/components/tutorials/utils.ts index 409236f6548..8231484984e 100644 --- a/server/sonar-web/src/main/js/components/tutorials/utils.ts +++ b/server/sonar-web/src/main/js/components/tutorials/utils.ts @@ -21,7 +21,7 @@ import { GRADLE_SCANNER_VERSION } from '../../helpers/constants'; import { convertGithubApiUrlToLink, stripTrailingSlash } from '../../helpers/urls'; import { AlmSettingsInstance, ProjectAlmBindingResponse } from '../../types/alm-settings'; import { UserToken } from '../../types/token'; -import { GradleBuildDSL } from './types'; +import { AutoConfig, BuildTools, GradleBuildDSL, TutorialConfig } from './types'; export function quote(os: string): (s: string) => string { return os === 'win' ? (s: string) => `"${s}"` : (s: string) => s; @@ -91,3 +91,31 @@ export function buildBitbucketCloudLink( return `${stripTrailingSlash(almBinding.url)}/${projectBinding.repository}`; } + +export function supportsAutoConfig(buildTool: BuildTools) { + return buildTool === BuildTools.Cpp; +} + +export function getBuildToolOptions(supportCFamily: boolean) { + const list = [BuildTools.Maven, BuildTools.Gradle, BuildTools.DotNet]; + if (supportCFamily) { + list.push(BuildTools.Cpp); + list.push(BuildTools.ObjectiveC); + } + list.push(BuildTools.Other); + return list; +} + +export function isCFamily(buildTool?: BuildTools) { + return buildTool === BuildTools.Cpp || buildTool === BuildTools.ObjectiveC; +} + +export function shouldShowGithubCFamilyExampleRepositories(config: TutorialConfig) { + if (config.buildTool === BuildTools.Cpp && config.autoConfig === AutoConfig.Manual) { + return true; + } + if (config.buildTool === BuildTools.ObjectiveC) { + return true; + } + return false; +} 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 f29248383e7..078b6e470f4 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -4540,7 +4540,7 @@ onboarding.analysis.auto_refresh_after_analysis.check_these_links=Check these us onboarding.analysis.auto_refresh_after_analysis.check_these_links.pr_analysis=Pull Request Analysis onboarding.analysis.auto_refresh_after_analysis.check_these_links.branches=Branch Analysis -onboarding.build=What option best describes your build? +onboarding.build=What option best describes your project? onboarding.build.maven=Maven onboarding.build.gradle=Gradle onboarding.build.make=Make @@ -4548,7 +4548,8 @@ onboarding.build.dotnet=.NET onboarding.build.dotnet.win_core=Windows + .NET Core onboarding.build.dotnet.win_msbuild=Windows + .NET Framework onboarding.build.dotnet.linux_core=Linux + .NET Core -onboarding.build.cfamily=C,C++ or ObjC +onboarding.build.cpp=C or C++ +onboarding.build.objectivec=Objective-C onboarding.build.other=Other (for JS, TS, Go, Python, PHP, ...) onboarding.build.dotnet.variant=Choose your build tool @@ -4560,6 +4561,12 @@ onboarding.build.other.os.linux=Linux onboarding.build.other.os.win=Windows onboarding.build.other.os.mac=macOS +onboarding.build.cpp.autoconfig=Do you want to use automatic configuration or manual configuration? +onboarding.build.cpp.autoconfig.automatic=Automatic +onboarding.build.cpp.autoconfig.automatic.description=Automatic Configuration allows the onboarding of most projects. Getting started does not require any specific knowledge about the project or the language. +onboarding.build.cpp.autoconfig.manual=Manual +onboarding.build.cpp.autoconfig.manual.description=Manual Configuration provides enhanced control over the setup, making it more suitable for certain specific use-cases. However, it may not support all scenarios and requires a certain level of understanding about the project's build process. + onboarding.analysis.docs=Please visit the {link} for more details. onboarding.analysis.build_wrapper.header.linux=Download and unzip the Build Wrapper for Linux onboarding.analysis.build_wrapper.header.win=Download and unzip the Build Wrapper for Windows @@ -4692,7 +4699,8 @@ onboarding.tutorial.with.gitlab_ci.project_key.maven.step2=Add the following to onboarding.tutorial.with.gitlab_ci.project_key.gradle.step2=Add the following to your {file} or {file2} file: onboarding.tutorial.with.gitlab_ci.project_key.other.step2=Create a {file} file in your repository and paste the following code: onboarding.tutorial.with.gitlab_ci.project_key.dotnet.step2=Create a {file} file in your repository and paste the following code: -onboarding.tutorial.with.gitlab_ci.project_key.cfamily.step2=Create a {file} file in your repository and paste the following code: +onboarding.tutorial.with.gitlab_ci.project_key.cpp.step2=Create a {file} file in your repository and paste the following code: +onboarding.tutorial.with.gitlab_ci.project_key.objectivec.step2=Create a {file} file in your repository and paste the following code: onboarding.tutorial.with.gitlab_ci.variables.title=Add environment variables onboarding.tutorial.with.gitlab_ci.variables.description.link=Settings > CI/CD > Variables @@ -5000,7 +5008,8 @@ onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.sec 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.cfamily=Use standalone scanner +onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.cpp=Use standalone scanner +onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.objectivec=Use standalone scanner 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