]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-22341 Add AutoConfig option to SQ onboarding tutorials for C/C++
author7PH <benjamin.raymond@sonarsource.com>
Tue, 4 Jun 2024 12:45:24 +0000 (14:45 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 6 Jun 2024 20:02:43 +0000 (20:02 +0000)
44 files changed:
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/AzurePipelinesTutorial.tsx
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/BranchAnalysisStepContent.tsx
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-it.tsx
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/AzurePipelinesTutorial-it.tsx.snap
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/AnalysisCommand.tsx
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/ClangGCC.tsx
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/PrepareAnalysisCommand.tsx
server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/AnalysisCommand.tsx
server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/BitbucketPipelinesTutorial.tsx
server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/PreambuleYaml.tsx
server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/__tests__/BitbucketPipelinesTutorial-it.tsx
server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/__tests__/__snapshots__/BitbucketPipelinesTutorial-it.tsx.snap
server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/CFamily.ts
server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/DotNet.ts
server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/Gradle.ts
server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/Maven.ts
server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/commands/Others.ts
server/sonar-web/src/main/js/components/tutorials/components/BuildConfigSelection.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/components/RenderOptions.tsx
server/sonar-web/src/main/js/components/tutorials/components/YamlFileStep.tsx
server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx
server/sonar-web/src/main/js/components/tutorials/github-action/GitHubActionTutorial.tsx
server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/GithubActionTutorial-it.tsx
server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/GithubActionTutorial-it.tsx.snap
server/sonar-web/src/main/js/components/tutorials/github-action/commands/CFamily.tsx
server/sonar-web/src/main/js/components/tutorials/gitlabci/GitLabCITutorial.tsx
server/sonar-web/src/main/js/components/tutorials/gitlabci/YmlFileStep.tsx
server/sonar-web/src/main/js/components/tutorials/gitlabci/commands/PipeCommand.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsStep.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsTutorial-it.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsTutorial-it.tsx.snap
server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/CFamilly.tsx [deleted file]
server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/CFamily.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/DotNet.tsx
server/sonar-web/src/main/js/components/tutorials/other/BuildToolForm.tsx
server/sonar-web/src/main/js/components/tutorials/other/ProjectAnalysisStep.tsx
server/sonar-web/src/main/js/components/tutorials/other/__tests__/OtherTutorial-it.tsx
server/sonar-web/src/main/js/components/tutorials/other/__tests__/__snapshots__/OtherTutorial-it.tsx.snap
server/sonar-web/src/main/js/components/tutorials/other/commands/AnalysisCommand.tsx
server/sonar-web/src/main/js/components/tutorials/test-utils.ts
server/sonar-web/src/main/js/components/tutorials/types.ts
server/sonar-web/src/main/js/components/tutorials/utils.ts
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 560eb393f6a2af1dfdb3ab14316610906ae7aad7..8f024dd20d14eeafd386349e126552bfeed5df91 100644 (file)
@@ -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<TutorialConfig>({});
   const [done, setDone] = React.useState<boolean>(false);
 
+  React.useEffect(() => {
+    setDone(Boolean(config.buildTool));
+  }, [config.buildTool]);
+
   return (
     <>
       <Title>{translate('onboarding.tutorial.with.azure_pipelines.title')}</Title>
@@ -76,7 +83,7 @@ export default function AzurePipelinesTutorial(props: AzurePipelinesTutorialProp
             `onboarding.tutorial.with.azure_pipelines.${Steps.BranchAnalysis}.title`,
           )}
         >
-          <BranchAnalysisStepContent component={component} onDone={setDone} />
+          <BranchAnalysisStepContent config={config} setConfig={setConfig} component={component} />
         </TutorialStep>
 
         {done && (
index e4f852ffa286434e091d5c11347cf12e8b926c8c..811e2ce5829ec0163239051f7f52f66f9110c7a6 100644 (file)
  */
 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> = [
-  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<BuildTools | undefined>();
-  const buildToolsList = languages['c']
-    ? BUILD_TOOLS_ORDERED
-    : BUILD_TOOLS_ORDERED.filter((t) => t !== BuildTools.CFamily);
   return (
     <>
-      <span>{translate('onboarding.build')}</span>
-      <RenderOptions
-        label={translate('onboarding.build')}
-        checked={buildTechnology}
-        onCheck={(value) => setBuildTechnology(value as BuildTools)}
-        optionLabelKey="onboarding.build"
-        options={buildToolsList}
-      />
-      <AnalysisCommand
-        onStepValidationChange={props.onDone}
-        buildTool={buildTechnology}
-        projectKey={component.key}
-        projectName={component.name}
+      <BuildConfigSelection
+        ci={TutorialModes.AzurePipelines}
+        config={config}
+        supportCFamily={Boolean(languages['c'])}
+        onSetConfig={setConfig}
       />
+
+      <AnalysisCommand config={config} projectKey={component.key} projectName={component.name} />
     </>
   );
 }
index 73dde6800bb6667844495b3655c37acd9c0975b6..3f58c95ae0ca279701e94843c36bbd46be24f690 100644 (file)
@@ -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`,
   );
 }
 
index bb4732751290c56f4adf9ac9f10d6b81a0357e74..3e7f2240b797216c91b58249d7c5b9534c7747d7 100644 (file)
@@ -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 <your build command here>"`;
+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 <your build command here>"`;
 
-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 <your build command here>"`;
+
+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 <your build command here>"`;
+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 <your build command here>"`;
 
-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 <your build command here>"`;
+
+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 <your build command here>"`;
 
-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 <your build command here>"`;
+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 <your build command here>"`;
 
-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 '.'"
 `;
index d87b5abfcbeb4988871e3ee6687f45bc37b2a71e..32d77e59a0359e58e362a0e5d3cfb55de6494216 100644 (file)
@@ -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 <JavaMaven projectKey={projectKey} projectName={projectName} />;
@@ -54,10 +49,9 @@ export default function AnalysisCommand(props: AnalysisCommandProps) {
     case BuildTools.DotNet:
       return <DotNet projectKey={projectKey} />;
 
-    case BuildTools.CFamily:
-      return (
-        <ClangGCC onStepValidationChange={props.onStepValidationChange} projectKey={projectKey} />
-      );
+    case BuildTools.Cpp:
+    case BuildTools.ObjectiveC:
+      return <ClangGCC config={config} projectKey={projectKey} />;
 
     case BuildTools.Other:
       return <Other projectKey={projectKey} />;
index 01f1ed48956783b78c32c817fa84fe43d4b69ef5..eaab6c0315187c12df9b0dec88ee79f7ef18bbb0 100644 (file)
@@ -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 | undefined>(OSs.Linux);
+  const { config, projectKey } = props;
+  const [os, setOs] = React.useState<OSs>(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 <Other projectKey={projectKey} />;
+  }
 
   return (
     <>
@@ -99,84 +92,80 @@ unzip build-wrapper.zip`,
       <RenderOptions
         label={translate('onboarding.tutorial.with.azure_pipelines.os')}
         checked={os}
-        onCheck={handlOsChange}
+        onCheck={(value: OSs) => setOs(value)}
         optionLabelKey="onboarding.build.other.os"
         options={Object.values(OSs)}
       />
 
-      {os && (
-        <>
-          <GithubCFamilyExampleRepositories
-            className="sw-mt-4 sw-w-abs-600"
-            os={os}
-            ci={TutorialModes.AzurePipelines}
+      <GithubCFamilyExampleRepositories
+        className="sw-mt-4 sw-w-abs-600"
+        os={os}
+        ci={TutorialModes.AzurePipelines}
+      />
+      <AlertClassicEditor />
+      <NumberedList className="sw-mt-4">
+        <NumberedListItem>
+          <SentenceWithHighlights
+            translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp"
+            highlightPrefixKeys="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
+            highlightKeys={['pipeline']}
           />
-          <AlertClassicEditor />
-          <NumberedList className="sw-mt-4">
-            <NumberedListItem>
+          <UnorderedList ticks className="sw-ml-12 sw-mt-2">
+            <ListItem>
               <SentenceWithHighlights
-                translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp"
-                highlightPrefixKeys="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
-                highlightKeys={['pipeline']}
+                translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp.script"
+                highlightPrefixKeys={codeSnippetDownload[os].highlightScriptKey}
+                highlightKeys={['task', 'inline']}
               />
-              <UnorderedList ticks className="sw-ml-12 sw-mt-2">
-                <ListItem>
-                  <SentenceWithHighlights
-                    translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp.script"
-                    highlightPrefixKeys={codeSnippetDownload[os].highlightScriptKey}
-                    highlightKeys={['task', 'inline']}
-                  />
-                  <CodeSnippet className="sw-p-6" snippet={codeSnippetDownload[os].script} />
-                </ListItem>
-              </UnorderedList>
-            </NumberedListItem>
+              <CodeSnippet className="sw-p-6" snippet={codeSnippetDownload[os].script} />
+            </ListItem>
+          </UnorderedList>
+        </NumberedListItem>
 
-            <NumberedListItem>
-              <SentenceWithHighlights
-                translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.ccpp"
-                highlightPrefixKeys="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
-                highlightKeys={['task', 'before']}
-              />
-              <PrepareAnalysisCommand
-                buildTool={BuildTools.CFamily}
-                kind={PrepareType.StandAlone}
-                projectKey={projectKey}
-              />
-            </NumberedListItem>
+        <NumberedListItem>
+          <SentenceWithHighlights
+            translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.ccpp"
+            highlightPrefixKeys="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
+            highlightKeys={['task', 'before']}
+          />
+          <PrepareAnalysisCommand
+            buildTool={BuildTools.Cpp}
+            kind={PrepareType.StandAlone}
+            projectKey={projectKey}
+          />
+        </NumberedListItem>
 
-            <NumberedListItem>
+        <NumberedListItem>
+          <SentenceWithHighlights
+            translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build.ccpp"
+            highlightKeys={['task']}
+          />
+          <UnorderedList className="sw-mt-2">
+            <ListItem>
               <SentenceWithHighlights
-                translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build.ccpp"
-                highlightKeys={['task']}
+                translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_script.ccpp"
+                highlightKeys={['build_wrapper']}
               />
-              <UnorderedList className="sw-mt-2">
-                <ListItem>
-                  <SentenceWithHighlights
-                    translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_script.ccpp"
-                    highlightKeys={['build_wrapper']}
-                  />
-                  <CodeSnippet
-                    className="sw-p-6"
-                    isOneLine
-                    snippet={codeSnippetDownload[os].scriptBuild}
-                  />
-                  <CompilationInfo />
-                </ListItem>
-              </UnorderedList>
-            </NumberedListItem>
-
-            <NumberedListItem>
-              <SentenceWithHighlights
-                translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.ccpp"
-                highlightPrefixKeys="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run"
-                highlightKeys={['task', 'after']}
+              <CodeSnippet
+                className="sw-p-6"
+                isOneLine
+                snippet={codeSnippetDownload[os].scriptBuild}
               />
-            </NumberedListItem>
+              <CompilationInfo />
+            </ListItem>
+          </UnorderedList>
+        </NumberedListItem>
+
+        <NumberedListItem>
+          <SentenceWithHighlights
+            translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.ccpp"
+            highlightPrefixKeys="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run"
+            highlightKeys={['task', 'after']}
+          />
+        </NumberedListItem>
 
-            <PublishSteps />
-          </NumberedList>
-        </>
-      )}
+        <PublishSteps />
+      </NumberedList>
     </>
   );
 }
index f9eb7b633dd1926cf81b2107030e46d2595a6bc5..2f484e85a68909f38cf348725b9ab7fd25f583f3 100644 (file)
@@ -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}
               />
             </span>
           </ListItem>
-          {buildTool === BuildTools.CFamily && (
+          {isCFamily(buildTool) && (
             <ListItem>
               <span className="sw-flex sw-items-center sw-flex-wrap">
                 <FormattedMessage
index fbcc35f2633c0f4d2adbbb4808ff265b61311ab8..66bf5fa08fdc6a629f2e78330143767d551803c0 100644 (file)
@@ -26,7 +26,8 @@ import { Feature } from '../../../types/features';
 import { Component } from '../../../types/types';
 import { CompilationInfo } from '../components/CompilationInfo';
 import CreateYmlFile from '../components/CreateYmlFile';
-import { BuildTools } from '../types';
+import { BuildTools, TutorialConfig } from '../types';
+import { isCFamily } from '../utils';
 import { PreambuleYaml } from './PreambuleYaml';
 import cFamilyExample from './commands/CFamily';
 import dotNetExample from './commands/DotNet';
@@ -35,42 +36,49 @@ import mavenExample from './commands/Maven';
 import othersExample from './commands/Others';
 
 export interface AnalysisCommandProps extends WithAvailableFeaturesProps {
-  buildTool: BuildTools;
   component: Component;
+  config: TutorialConfig;
   mainBranchName: string;
 }
 
-const YamlTemplate: Dictionary<
-  (
-    branchesEnabled?: boolean,
-    mainBranchName?: string,
-    projectKey?: string,
-    projectName?: string,
-  ) => string
-> = {
+export type BuildToolExampleBuilder = (data: {
+  branchesEnabled?: boolean;
+  config: TutorialConfig;
+  mainBranchName?: string;
+  projectKey?: string;
+  projectName?: string;
+}) => string;
+
+const YamlTemplate: Dictionary<BuildToolExampleBuilder> = {
   [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 (
     <>
-      <PreambuleYaml buildTool={buildTool} component={component} />
+      <PreambuleYaml buildTool={config.buildTool} component={component} />
       <CreateYmlFile yamlFileName="bitbucket-pipelines.yml" yamlTemplate={yamlTemplate} />
-      {buildTool === BuildTools.CFamily && <CompilationInfo />}
+      {isCFamily(config.buildTool) && <CompilationInfo />}
     </>
   );
 }
index 95dbda5bdfe197db174c4a6b8b86ed14126f7a16..21e6173ac78865e0c4c770a49e07c4c0859b46eb 100644 (file)
@@ -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<boolean>(false);
+  const [config, setConfig] = React.useState<TutorialConfig>({});
+  const [done, setDone] = React.useState(false);
+
+  React.useEffect(() => {
+    setDone(Boolean(config.buildTool));
+  }, [config.buildTool]);
+
   return (
     <>
       <Title>{translate('onboarding.tutorial.with.bitbucket_ci.title')}</Title>
@@ -66,17 +73,17 @@ export default function BitbucketPipelinesTutorial(props: BitbucketPipelinesTuto
           />
         </TutorialStep>
         <TutorialStep title={translate('onboarding.tutorial.with.bitbucket_pipelines.yaml.title')}>
-          <YamlFileStep setDone={setDone}>
-            {(buildTool) => (
+          <YamlFileStep config={config} setConfig={setConfig} ci={TutorialModes.BitbucketPipelines}>
+            {(config) => (
               <>
-                {buildTool === BuildTools.CFamily && (
+                {shouldShowGithubCFamilyExampleRepositories(config) && (
                   <GithubCFamilyExampleRepositories
                     className="sw-my-4 sw-w-abs-600"
                     ci={TutorialModes.BitbucketPipelines}
                   />
                 )}
                 <AnalysisCommand
-                  buildTool={buildTool}
+                  config={config}
                   component={component}
                   mainBranchName={mainBranchName}
                 />
index 1df171e33a0dba668f28c6f7fa9a54afa54d492f..83222ccbe612fdb27f6bcff4dba09b548da9c644 100644 (file)
@@ -33,7 +33,8 @@ export function PreambuleYaml(props: PreambuleYamlProps) {
   switch (buildTool) {
     case BuildTools.Gradle:
       return <GradleBuild component={component} />;
-    case BuildTools.CFamily:
+    case BuildTools.Cpp:
+    case BuildTools.ObjectiveC:
     case BuildTools.Other:
       return <DefaultProjectKey component={component} />;
     default:
index 13b3640f87b15d7214cc0a9c4541617e59073889..76f46695e447c080835eff6947b7da739c049b58 100644 (file)
@@ -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();
 });
index 827109d5f21a4e810e5509c690202ecadc224f2c..39b68ed6fddb8659c4fec450f9c4fb8df8408384 100644 (file)
@@ -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: <image ready for your build toolchain>
 
 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: <image ready for your build toolchain>
+
+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"
+          - <any step required before running your build, like ./configure>
+          - $HOME/.sonar/build-wrapper-linux-x86/build-wrapper-linux-x86-64 --out-dir bw-output <your clean build command>
+          - 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"`;
index ab6adff8e9ebdf7af36b697ab04fd4d3f34f30ea..2f6942746491fa41c869de703bcd3fa56a7e57e1 100644 (file)
  * 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: <image ready for your build toolchain>
 
 definitions:
@@ -53,4 +60,6 @@ ${
       - step: *build-step`
     : ''
 }`;
-}
+};
+
+export default cFamilyExample;
index 621cb328358fe29d348c310e7d0bd0efae5020c7..df12f554254a1092f557eb1ae0090cda6eb17a56 100644 (file)
  * 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;
index 4d96925a69ac530794a7855cd418595f44b2bd9d..45cb5b262e862a37c2970e8962762852d0556f37 100644 (file)
@@ -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;
index cc32ef6249b5d8845043d2cd11b1dd3ace7e98a2..6a53708be3db0c333bc56810d2e3e6856f9a87fb 100644 (file)
  * 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;
index 7ae93f051909653569f61bbdda2ba9329b27bdb3..94c145180f76efef2d0adda8d1fa9df40cccabc7 100644 (file)
@@ -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 (file)
index 0000000..9cfff92
--- /dev/null
@@ -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<BuildConfigSelectionProps>) {
+  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')}
+      <RenderOptions
+        label={translate('onboarding.build')}
+        checked={config.buildTool}
+        onCheck={onSelectBuildTool}
+        optionLabelKey="onboarding.build"
+        options={getBuildToolOptions(supportCFamily)}
+      />
+
+      {ci === TutorialModes.Jenkins && isCFamily(config.buildTool) && (
+        <FlagMessage variant="info" className="sw-mt-2 sw-w-abs-600">
+          {translate('onboarding.tutorial.with.jenkins.jenkinsfile.cfamilly.agent_setup')}
+        </FlagMessage>
+      )}
+
+      {!hideAutoConfig &&
+        config.buildTool &&
+        supportsAutoConfig(config.buildTool) &&
+        onSelectAutoConfig && (
+          <>
+            <div className="sw-mt-4">{translate('onboarding.build.cpp.autoconfig')}</div>
+            <RenderOptions
+              label="onboarding.build.cpp.autoconfig"
+              checked={config.autoConfig}
+              onCheck={onSelectAutoConfig}
+              optionLabelKey="onboarding.build.cpp.autoconfig"
+              options={[AutoConfig.Automatic, AutoConfig.Manual]}
+            />
+            <FlagMessage className="sw-mt-2 sw-w-abs-600" variant="info">
+              {translate(`onboarding.build.cpp.autoconfig.${config.autoConfig}.description`)}
+            </FlagMessage>
+          </>
+        )}
+    </>
+  );
+}
index b972264dfc2de584998532f9e4d37536a566b2ce..cb379b20db4b1156e8b2b9168e9516d6753ed0ba 100644 (file)
@@ -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);
   };
 
index fe16f855f8c8c8d65f3de12b761651eda80f3249..d1b01e6835a155dbcfce8c02390a418ce9ae62d2 100644 (file)
  */
 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<BuildTools>();
+  const { ci, config, setConfig, children, hasCLanguageFeature } = props;
 
   return (
     <NumberedList>
       <NumberedListItem>
-        {translate('onboarding.build')}
-        <RenderOptions
-          label={translate('onboarding.build')}
-          checked={buildToolSelected}
-          onCheck={(value) => setBuildToolSelected(value as BuildTools)}
-          options={buildTools}
-          optionLabelKey="onboarding.build"
-          setDone={props.setDone}
+        <BuildConfigSelection
+          ci={ci}
+          config={config}
+          onSetConfig={setConfig}
+          supportCFamily={hasCLanguageFeature}
         />
       </NumberedListItem>
-      {children && buildToolSelected && children(buildToolSelected)}
+
+      {children && config && children(config)}
     </NumberedList>
   );
 }
index 81cc14e14af52e0bf7197647b88d7c12dcb0fc3e..f58f9d0bae9dc57767af500011b3bf8c2ea5ad84 100644 (file)
@@ -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<AnalysisCommandProps>) {
-  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 (
         <JavaMaven
@@ -69,9 +69,11 @@ export function AnalysisCommand(props: Readonly<AnalysisCommandProps>) {
           component={component}
         />
       );
-    case BuildTools.CFamily:
+    case BuildTools.Cpp:
+    case BuildTools.ObjectiveC:
       return (
         <CFamily
+          config={config}
           branchesEnabled={branchSupportEnabled}
           mainBranchName={mainBranchName}
           monorepo={monorepo}
index bbb62cc3dfa7df8c3c46a135303ae7d11a440731..6bad8ad02d4892b47800587e1cc8f6f7aeea98b3 100644 (file)
@@ -25,6 +25,7 @@ import { Component } from '../../../types/types';
 import { LoggedInUser } from '../../../types/users';
 import AllSet from '../components/AllSet';
 import YamlFileStep from '../components/YamlFileStep';
+import { TutorialConfig, TutorialModes } from '../types';
 import AnalysisCommand from './AnalysisCommand';
 import SecretStep from './SecretStep';
 
@@ -39,7 +40,6 @@ export interface GitHubActionTutorialProps {
 }
 
 export default function GitHubActionTutorial(props: GitHubActionTutorialProps) {
-  const [done, setDone] = React.useState<boolean>(false);
   const {
     almBinding,
     baseUrl,
@@ -50,6 +50,13 @@ export default function GitHubActionTutorial(props: GitHubActionTutorialProps) {
     willRefreshAutomatically,
   } = props;
 
+  const [config, setConfig] = React.useState<TutorialConfig>({});
+  const [done, setDone] = React.useState<boolean>(false);
+
+  React.useEffect(() => {
+    setDone(Boolean(config.buildTool));
+  }, [config.buildTool]);
+
   return (
     <>
       <Title>{translate('onboarding.tutorial.with.github_ci.title')}</Title>
@@ -66,10 +73,10 @@ export default function GitHubActionTutorial(props: GitHubActionTutorialProps) {
           />
         </TutorialStep>
         <TutorialStep title={translate('onboarding.tutorial.with.github_action.yaml.title')}>
-          <YamlFileStep setDone={setDone}>
-            {(buildTool) => (
+          <YamlFileStep config={config} setConfig={setConfig} ci={TutorialModes.GitHubActions}>
+            {(config) => (
               <AnalysisCommand
-                buildTool={buildTool}
+                config={config}
                 mainBranchName={mainBranchName}
                 component={component}
                 monorepo={monorepo}
index 2ab91d22c1f415e1bcd348ff9983be6dfab5d82f..3f946d6a27b7dccf23f284044afa138324af532c 100644 (file)
@@ -84,26 +84,57 @@ it('should follow and complete all steps', async () => {
   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();
 });
index eb5e451a53d460dcd42d9a849564c7c5751aa360..3cc30b5bdf0f63b3b92d0bdb620ec61ffa81c042 100644 (file)
@@ -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 }} <insert_your_clean_build_command>
+      - 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 }} <insert_your_clean_build_command>
+      - 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 }} <insert_your_clean_build_command>
+      - 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"`;
 
index 7c8195a939e3b0b339599b7b2c8f2ea13b411c73..7c0513374fc656378b14605b30d1bd47cad9ce4b 100644 (file)
@@ -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<undefined | OSs>(OSs.Linux);
+  const { config, component, branchesEnabled, mainBranchName, monorepo } = props;
+  const [os, setOs] = React.useState<OSs>(OSs.Linux);
+
+  if (config.buildTool === BuildTools.Cpp && config.autoConfig === AutoConfig.Automatic) {
+    return <Others {...props} />;
+  }
 
   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 && (
-          <GithubCFamilyExampleRepositories
-            className="sw-mt-4 sw-w-abs-600"
-            os={os}
-            ci={TutorialModes.GitHubActions}
-          />
-        )}
+        <GithubCFamilyExampleRepositories
+          className="sw-mt-4 sw-w-abs-600"
+          os={os}
+          ci={TutorialModes.GitHubActions}
+        />
       </NumberedListItem>
-      {os &&
-        (monorepo ? (
-          <MonorepoDocLinkFallback />
-        ) : (
-          <>
-            <CreateYmlFile
-              yamlFileName=".github/workflows/build.yml"
-              yamlTemplate={generateGitHubActionsYaml(
-                mainBranchName,
-                !!branchesEnabled,
-                runsOn[os],
-                STEPS[os],
-                `env:
+      {monorepo ? (
+        <MonorepoDocLinkFallback />
+      ) : (
+        <>
+          <CreateYmlFile
+            yamlFileName=".github/workflows/build.yml"
+            yamlTemplate={generateGitHubActionsYaml(
+              mainBranchName,
+              !!branchesEnabled,
+              runsOn[os],
+              STEPS[os],
+              `env:
       BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed`,
-              )}
-            />
-            <CompilationInfo />
-          </>
-        ))}
+            )}
+          />
+          <CompilationInfo />
+        </>
+      )}
     </>
   );
 }
index bebd34e2a4a8af1f459e3cdbe2d9109660563224..0b778d63c6e1f13851231957cf9b25300383c819 100644 (file)
@@ -44,6 +44,7 @@ export default function GitLabCITutorial(props: GitLabCITutorialProps) {
   const { baseUrl, component, currentUser, willRefreshAutomatically } = props;
 
   const [done, setDone] = React.useState<boolean>(false);
+
   return (
     <>
       <Title>{translate('onboarding.tutorial.with.gitlab_ci.title')}</Title>
index c9382b021253f5587d3468733d4ef0f681bfc9b7..fa9f0642e5988ed82fc09f8126be1584eb311eff 100644 (file)
@@ -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) => `<properties>
@@ -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<BuildTools>();
+  const [config, setConfig] = React.useState<TutorialConfig>({});
+  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 = () => (
     <NumberedList>
       <NumberedListItem>
-        {translate('onboarding.build')}
-
-        <RenderOptions
-          checked={buildTool}
-          label={translate('onboarding.build')}
-          onCheck={setBuildTool as (key: string) => void}
-          optionLabelKey="onboarding.build"
-          options={buildTools}
-          setDone={props.setDone}
+        <BuildConfigSelection
+          ci={TutorialModes.GitLabCI}
+          config={config}
+          supportCFamily={hasCLanguageFeature}
+          hideAutoConfig
+          onSetConfig={onSetConfig}
         />
 
-        {buildTool === BuildTools.CFamily && (
+        {isCFamily(config.buildTool) && (
           <GithubCFamilyExampleRepositories
             ci={TutorialModes.GitLabCI}
             className="sw-my-4 sw-w-abs-600"
@@ -142,7 +144,8 @@ export function YmlFileStep(props: YmlFileStepProps) {
       </NumberedListItem>
 
       {buildTool !== undefined &&
-        buildTool !== BuildTools.CFamily &&
+        buildTool !== BuildTools.Cpp &&
+        buildTool !== BuildTools.ObjectiveC &&
         buildTool !== BuildTools.DotNet && (
           <NumberedListItem>
             <FormattedMessage
@@ -201,7 +204,7 @@ export function YmlFileStep(props: YmlFileStepProps) {
 
       {buildTool && (
         <>
-          {buildTool !== BuildTools.CFamily && (
+          {buildTool !== BuildTools.Cpp && buildTool !== BuildTools.ObjectiveC && (
             <NumberedListItem>
               <FormattedMessage
                 defaultMessage={translate('onboarding.tutorial.with.gitlab_ci.yaml.description')}
index a0c857a20e062da937397ee61cd8a7f9b07c864f..5e092e4b2cee7c8c4102dcd1195ce6c4d0d6a039 100644 (file)
@@ -22,7 +22,7 @@ import * as React from 'react';
 import { BuildTools } from '../../types';
 
 export interface PipeCommandProps {
-  buildTool: Exclude<BuildTools, BuildTools.CFamily>;
+  buildTool: Exclude<BuildTools, BuildTools.Cpp | BuildTools.ObjectiveC>;
   projectKey: string;
 }
 
index 4090603fe7ce374087521108516bdbbacece91c4..87ca0994688ad1cf5c3bb06c2f59ad7c81add4c3 100644 (file)
  * 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<React.PropsWithChildren<LanguageProps>>;
 } = {
   [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<BuildTools>();
+export function JenkinsStep(props: Readonly<JenkinsfileStepProps>) {
+  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<TutorialConfig>({});
 
   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 (
     <TutorialStep title={translate('onboarding.tutorial.with.jenkins.jenkinsfile.title')}>
       <NumberedList>
         <NumberedListItem>
-          {translate('onboarding.build')}
-          <RenderOptions
-            label={translate('onboarding.build')}
-            checked={buildTool}
-            onCheck={(value) => setBuildTool(value as BuildTools)}
-            optionLabelKey="onboarding.build"
-            options={buildToolOrder}
+          <BuildConfigSelection
+            ci={TutorialModes.Jenkins}
+            config={config}
+            supportCFamily={hasCLanguageFeature}
+            onSetConfig={setConfig}
           />
-          {buildTool === BuildTools.CFamily && (
-            <FlagMessage variant="info" className="sw-mt-2 sw-w-abs-600">
-              {translate('onboarding.tutorial.with.jenkins.jenkinsfile.cfamilly.agent_setup')}
-            </FlagMessage>
-          )}
         </NumberedListItem>
-        {BuildToolComponent !== undefined && (
-          <BuildToolComponent component={component} baseUrl={baseUrl} onDone={props.onDone} />
+        {BuildToolComponent && (
+          <BuildToolComponent config={config} component={component} baseUrl={baseUrl} />
         )}
       </NumberedList>
     </TutorialStep>
   );
 }
 
-export default withCLanguageFeature(JenkinsfileStep);
+export default withCLanguageFeature(JenkinsStep);
index a57b718d52a064f8c6cdd4a23942e4fc48bb8095..40368aa7222727c22f222c7d98f6c50ef057ff0a 100644 (file)
@@ -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}
             />
 
-            <JenkinsfileStep component={component} baseUrl={baseUrl} onDone={setDone} />
+            <JenkinsStep component={component} baseUrl={baseUrl} setDone={setDone} />
           </TutorialStepList>
           {done && (
             <>
index 87574ba0ea4861141292008930346dfd443589f5..9380803e1aeb11e9614122a3170ab75166905993 100644 (file)
@@ -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();
   },
index 70cf5185828c09ef87114cd44f355c9f5d62d4dd..5dd2df6b18d8507b82c25a98748b1ef9ce190813 100644 (file)
@@ -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 <your clean build command>"
+  }
+  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 <your clean build command>
+    '''
+  }
+  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 <your clean build command>
+    '''
+  }
+  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 <your clean build command>"
+  }
+  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 <your clean build command>
+    '''
+  }
+  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 <your clean build command>
+    '''
+  }
+  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 <your clean build command>"
+  }
+  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 <your clean build command>
+    '''
+  }
+  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 <your clean build command>
+    '''
+  }
+  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 <your clean build command>"
+  }
+  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 <your clean build command>
+    '''
+  }
+  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 <your clean build command>
+    '''
+  }
+  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 (file)
index 0366945..0000000
+++ /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<OSs, (baseUrl: string) => 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 <your clean build command>"
-  }
-  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 <your clean build command>
-    '''
-  }
-  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 <your clean build command>
-    '''
-  }
-  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>(OSs.Linux);
-
-  React.useEffect(() => {
-    onDone(os !== undefined);
-  }, [os, onDone]);
-
-  return (
-    <>
-      <DefaultProjectKey component={component} />
-      <NumberedListItem>
-        {translate('onboarding.build.other.os')}
-        <RenderOptions
-          label={translate('onboarding.build.other.os')}
-          checked={os}
-          optionLabelKey="onboarding.build.other.os"
-          onCheck={(value) => setOs(value as OSs)}
-          options={Object.values(OSs)}
-        />
-        {os && (
-          <GithubCFamilyExampleRepositories
-            className="sw-my-4 sw-w-abs-600"
-            os={os}
-            ci={TutorialModes.Jenkins}
-          />
-        )}
-      </NumberedListItem>
-      {os && (
-        <CreateJenkinsfileBulletPoint
-          alertTranslationKeyPart="onboarding.tutorial.with.jenkins.jenkinsfile.other.step3"
-          snippet={YAML_MAP[os](baseUrl)}
-        >
-          <CompilationInfo />
-        </CreateJenkinsfileBulletPoint>
-      )}
-    </>
-  );
-}
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 (file)
index 0000000..cef2de8
--- /dev/null
@@ -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<OSs, (baseUrl: string) => 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 <your clean build command>"
+  }
+  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 <your clean build command>
+    '''
+  }
+  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 <your clean build command>
+    '''
+  }
+  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<LanguageProps>) {
+  const { baseUrl, config, component } = props;
+  const [os, setOs] = React.useState<OSs>(OSs.Linux);
+
+  if (config.buildTool === BuildTools.Cpp && config.autoConfig === AutoConfig.Automatic) {
+    return <Other {...props} />;
+  }
+
+  return (
+    <>
+      <DefaultProjectKey component={component} />
+      <NumberedListItem>
+        {translate('onboarding.build.other.os')}
+        <RenderOptions
+          label={translate('onboarding.build.other.os')}
+          checked={os}
+          optionLabelKey="onboarding.build.other.os"
+          onCheck={(value: OSs) => setOs(value)}
+          options={Object.values(OSs)}
+        />
+        {
+          <GithubCFamilyExampleRepositories
+            ci={TutorialModes.Jenkins}
+            os={os}
+            className="sw-my-4 sw-w-abs-600"
+          />
+        }
+      </NumberedListItem>
+      {
+        <CreateJenkinsfileBulletPoint
+          alertTranslationKeyPart="onboarding.tutorial.with.jenkins.jenkinsfile.other.step3"
+          snippet={YAML_MAP[os](baseUrl)}
+        >
+          <CompilationInfo />
+        </CreateJenkinsfileBulletPoint>
+      }
+    </>
+  );
+}
index 3e34cb6a2e894060dc133517a03b9daa21ced4ae..bf5e5a08d76e3c30ca500a60e012c0279b45fabc 100644 (file)
@@ -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<keyof typeof DotNetFlavor>('win_core');
   const DotNetTutorial = flavorComponent && DotNetFlavor[flavorComponent];
 
-  React.useEffect(() => {
-    onDone(flavorComponent !== undefined);
-  }, [flavorComponent, onDone]);
-
   return (
     <>
       <NumberedListItem>
index f790489a03c1463ef62984621f6693a3e20944e5..f4bc5dab064c6cb003381bb13fb5fd36590e60cd 100644 (file)
  * 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;
+  configTutorialConfig;
   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<Props>) {
+  const { config, setConfig, os, setOs, hasCLanguageFeature } = props;
 
-export class BuildToolForm extends React.PureComponent<Props, State> {
-  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 (
-      <>
-        <div>
-          <label className="sw-block sw-mb-1">{translate('onboarding.build')}</label>
-          <ToggleButton
-            label={translate('onboarding.build')}
-            onChange={this.handleBuildToolChange}
-            options={buildTools.map((tool) => ({
-              label: translate('onboarding.build', tool),
-              value: tool,
-            }))}
-            value={config.buildTool}
-          />
-        </div>
-
-        {(config.buildTool === BuildTools.Other || config.buildTool === BuildTools.CFamily) && (
-          <RenderOptions
-            label={translate('onboarding.build.other.os')}
-            checked={config.os}
-            onCheck={this.handleOSChange}
-            optionLabelKey="onboarding.build.other.os"
-            options={[OSs.Linux, OSs.Windows, OSs.MacOS]}
-            titleLabelKey="onboarding.build.other.os"
-          />
-        )}
-
-        {config.buildTool === BuildTools.CFamily && config.os && (
-          <GithubCFamilyExampleRepositories
-            className="sw-mt-4 sw-w-abs-600"
-            os={config.os}
-            ci={TutorialModes.Local}
-          />
-        )}
-      </>
-    );
   }
+
+  return (
+    <>
+      {config && (
+        <BuildConfigSelection
+          ci={TutorialModes.OtherCI}
+          config={config}
+          supportCFamily={hasCLanguageFeature}
+          onSetConfig={handleConfigChange}
+        />
+      )}
+      {(config.buildTool === BuildTools.Other ||
+        config.buildTool === BuildTools.Cpp ||
+        config.buildTool === BuildTools.ObjectiveC) && (
+        <RenderOptions
+          label={translate('onboarding.build.other.os')}
+          checked={os}
+          onCheck={(value: OSs) => setOs(value)}
+          optionLabelKey="onboarding.build.other.os"
+          options={[OSs.Linux, OSs.Windows, OSs.MacOS]}
+          titleLabelKey="onboarding.build.other.os"
+        />
+      )}
+      {shouldShowGithubCFamilyExampleRepositories(config) && (
+        <GithubCFamilyExampleRepositories
+          ci={TutorialModes.OtherCI}
+          className="sw-my-4 sw-w-abs-600"
+        />
+      )}
+    </>
+  );
 }
 
 export default withCLanguageFeature(BuildToolForm);
index 799135f27b59de5c40f5933bc8af879d975b57ad..9d5e24d711a8cb01a0e4f0e2fc331695e6b6891e 100644 (file)
@@ -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<Props, State> {
-  state: State = {};
+export default function ProjectAnalysisStep(props: Readonly<Props>) {
+  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<TutorialConfig>({});
+  const [os, setOs] = React.useState<OSs>(OSs.Linux);
 
-  renderForm = () => {
-    const { component, baseUrl, isLocal, token } = this.props;
+  function renderForm() {
     return (
       <div className="sw-pb-4">
-        <BuildToolForm onDone={this.handleBuildToolSelect} />
+        <BuildToolForm config={config} setConfig={setConfig} os={os} setOs={setOs} />
 
-        {this.state.config && (
+        {config && (
           <div className="sw-mt-4">
             <AnalysisCommand
+              config={config}
+              os={os}
               component={component}
               baseUrl={baseUrl}
               isLocal={isLocal}
-              languageConfig={this.state.config}
               token={token}
             />
           </div>
         )}
       </div>
     );
-  };
-
-  render() {
-    return (
-      <Step
-        finished={false}
-        onOpen={noop}
-        open={this.props.open}
-        renderForm={this.renderForm}
-        stepNumber={this.props.stepNumber}
-        stepTitle={translate('onboarding.analysis.header')}
-      />
-    );
   }
+
+  return (
+    <Step
+      finished={false}
+      onOpen={noop}
+      open={open}
+      renderForm={renderForm}
+      stepNumber={stepNumber}
+      stepTitle={translate('onboarding.analysis.header')}
+    />
+  );
 }
index 53679e3194d47c2453f58cad18a6ea4ba815ecf8..f366797deac3d18cfd0e7daffffe384e5a0328d1 100644 (file)
@@ -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({
index 8371fca630e9537bddac829a7467ebff1b6585e5..a5e422839554a69c17e0c60efb3f19fd3058299e 100644 (file)
@@ -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"
-"
-`;
index bae1666034f18f2c0cad62951a07763ba0b3d1e6..f8fb5b2f954bd7931d1d95f994d40689fc0a3fea 100644 (file)
@@ -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 <JavaMaven baseUrl={baseUrl} component={component} token={token} />;
 
@@ -51,27 +52,27 @@ export default function AnalysisCommand(props: AnalysisCommandProps) {
     case BuildTools.DotNet:
       return <DotNet baseUrl={baseUrl} component={component} token={token} />;
 
-    case BuildTools.CFamily:
-      return languageConfig.os !== undefined ? (
-        <ClangGCCCustom
-          os={languageConfig.os}
-          baseUrl={baseUrl}
-          component={component}
-          isLocal={isLocal}
-          token={token}
-        />
-      ) : null;
-
     case BuildTools.Other:
-      return languageConfig.os !== undefined ? (
-        <Other
+      return (
+        <Other baseUrl={baseUrl} os={os} component={component} isLocal={isLocal} token={token} />
+      );
+
+    case BuildTools.Cpp:
+    case BuildTools.ObjectiveC:
+      if (config.buildTool === BuildTools.Cpp && config.autoConfig === AutoConfig.Automatic) {
+        return (
+          <Other os={os} baseUrl={baseUrl} component={component} isLocal={isLocal} token={token} />
+        );
+      }
+      return (
+        <ClangGCCCustom
+          os={os}
           baseUrl={baseUrl}
-          os={languageConfig.os}
           component={component}
           isLocal={isLocal}
           token={token}
         />
-      ) : null;
+      );
 
     default:
       return null;
index 591b1e8e12fca9ccad8de1c87400652b3e3b7f79..44f9de38066b32c7a56a348f4dd3735fcaa2d1a6 100644 (file)
@@ -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' }),
   };
 }
index b9d8a30906633cd0a72fc8b572901acf53da57ff..afb938c12b904089e74e7ed53b42847390874acb 100644 (file)
@@ -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;
+};
index 409236f6548c89df6d4baf5152e60ab54140398e..8231484984e012928b26cb8756b33a872602e717 100644 (file)
@@ -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;
+}
index f29248383e739f5076cbc602d268b31091401271..078b6e470f41ec0ed7d97a7764245917f279038a 100644 (file)
@@ -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