]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-14590 Adding GitHub Action tutorial
authorMathieu Suen <mathieu.suen@sonarsource.com>
Fri, 19 Mar 2021 14:56:31 +0000 (15:56 +0100)
committersonartech <sonartech@sonarsource.com>
Tue, 23 Mar 2021 20:08:24 +0000 (20:08 +0000)
54 files changed:
server/sonar-web/public/images/tutorials/github-actions.svg [new file with mode: 0644]
server/sonar-web/src/main/js/app/styles/init/misc.css
server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx
server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelectionRenderer-test.tsx
server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelectionRenderer-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/components/AllSet.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/components/DefaultProjectKey.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/components/TokenStepGenerator.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/components/__tests__/AllSet-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/components/__tests__/DefaultProjectKey-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/components/__tests__/TokenStepGenerator-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/AllSet-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/DefaultProjectKey-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/TokenStepGenerator-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/GitHubActionTutorial.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/SecretStep.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/YamlFileStep.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/AnalysisCommand-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/GitHubActionTutorial-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/SecretStep-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/YamlFileStep-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/AnalysisCommand-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/GitHubActionTutorial-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/SecretStep-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/YamlFileStep-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/commands/CreateYmlFile.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/commands/DotNet.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/commands/Gradle.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/commands/JavaMaven.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/commands/Others.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/CreateYmlFile-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/DotNet-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/Gradle-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/JavaMaven-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/Others-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/CreateYmlFile-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/DotNet-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/Gradle-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/Others-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/gitlabci/EnvironmentVariablesStep.tsx
server/sonar-web/src/main/js/components/tutorials/gitlabci/__tests__/__snapshots__/EnvironmentVariablesStep-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsfileStep.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsfileStep-test.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsTutorial-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsfileStep-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Gradle.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Maven.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Other.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/Other-test.tsx.snap
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

diff --git a/server/sonar-web/public/images/tutorials/github-actions.svg b/server/sonar-web/public/images/tutorials/github-actions.svg
new file mode 100644 (file)
index 0000000..6f0c864
--- /dev/null
@@ -0,0 +1,16 @@
+<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M10 1C14.9707 1 19 5.02932 19 10C19 14.9707 14.9707 19 10 19C5.02932 19 1 14.9707 1 10C1 5.02932 5.02932 1 10 1Z" stroke="#236a97" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M23 19C26.3137 19 29 21.6863 29 25C29 28.3137 26.3137 31 23 31C19.6863 31 17 28.3137 17 25C17 21.6863 19.6863 19 23 19Z" stroke="#236a97" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M23 35C26.3137 35 29 37.6863 29 41C29 44.3137 26.3137 47 23 47C19.6863 47 17 44.3137 17 41C17 37.6863 19.6863 35 23 35Z" stroke="#236a97" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M41 35C44.3137 35 47 37.6863 47 41C47 44.3137 44.3137 47 41 47C37.6863 47 35 44.3137 35 41C35 37.6863 37.6863 35 41 35Z" stroke="#cae3f2" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M25.0365 23.6069L21.9673 26.672L20.4766 25.1866" stroke="#236a97" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M41 19C44.3137 19 47 21.6863 47 25C47 28.3137 44.3137 31 41 31C37.6863 31 35 28.3137 35 25C35 21.6863 37.6863 19 41 19Z" stroke="#236a97" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M43.0365 23.6069L39.9673 26.672L38.4766 25.1866" stroke="#236a97" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M7 6.81201C7 6.02498 7.86709 5.54641 8.533 5.9659L13.6462 9.18692C14.2715 9.58082 14.2682 10.4935 13.6402 10.8829L8.52697 14.0534C7.8608 14.4664 7 13.9873 7 13.2035V6.81201Z" stroke="#236a97" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M9 19V34C9 37.866 12.1772 41 16 41H17" stroke="#236a97" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M16.949 26C17.5013 26 17.949 25.5523 17.949 25C17.949 24.4477 17.5013 24 16.949 24V26ZM8 19.0352C8 22.8817 11.1183 26 14.9648 26V24C12.2228 24 10 21.7772 10 19.0352H8ZM14.9648 26H16.949V24H14.9648V26Z" fill="#236a97"/>
+<path d="M29.0547 25H34.9993" stroke="#236a97" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M21 40C21.5518 40 22 40.4482 22 41C22 41.5526 21.5518 42 21 42C20.4482 42 20 41.5526 20 41C20 40.4482 20.4482 40 21 40Z" fill="#cae3f2"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M25 40C25.5518 40 26 40.4482 26 41C26 41.5526 25.5518 42 25 42C24.4482 42 24 41.5526 24 41C24 40.4482 24.4482 40 25 40Z" fill="#cae3f2"/>
+<path d="M34.0048 41.0069L32.9922 41.0399" stroke="#cae3f2" stroke-width="2" stroke-linecap="round"/>
+</svg>
index 6e6ba4d39c83bc4607638f500dff4fe390922ef8..ae61dfde85c7b30dee5381639ba2e2a9ea56ca10 100644 (file)
@@ -361,6 +361,10 @@ th.huge-spacer-right {
   width: 600px !important;
 }
 
+.abs-width-800 {
+  width: 800px !important;
+}
+
 .abs-height-50 {
   height: 50px !important;
 }
index 323c6a682cca9c0bf72b0a4ede3b1ff4fe380ed5..bd1f9862b8aad398f4d55691898860fd1bc41a1f 100644 (file)
@@ -22,6 +22,7 @@ import { translate } from 'sonar-ui-common/helpers/l10n';
 import { getBaseUrl } from 'sonar-ui-common/helpers/urls';
 import { AlmBindingDefinition, AlmKeys, ProjectAlmBindingResponse } from '../../types/alm-settings';
 import AzurePipelinesTutorial from './azure-pipelines/AzurePipelinesTutorial';
+import GitHubActionTutorial from './github-action/GitHubActionTutorial';
 import GitLabCITutorial from './gitlabci/GitLabCITutorial';
 import JenkinsTutorial from './jenkins/JenkinsTutorial';
 import ManualTutorial from './manual/ManualTutorial';
@@ -68,6 +69,23 @@ export default function TutorialSelectionRenderer(props: TutorialSelectionRender
           </header>
 
           <div className="display-flex-justify-center">
+            {projectBinding?.alm === AlmKeys.GitHub && (
+              <button
+                className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-github"
+                onClick={() => props.onSelectTutorial(TutorialModes.GitHubActions)}
+                type="button">
+                <img
+                  alt="" // Should be ignored by screen readers
+                  height={64}
+                  className="spacer-bottom spacer-top"
+                  src={`${getBaseUrl()}/images/tutorials/github-actions.svg`}
+                />
+                <div className="medium big-spacer-top">
+                  {translate('onboarding.tutorial.choose_method.github_action')}
+                </div>
+              </button>
+            )}
+
             {projectBinding?.alm === AlmKeys.GitLab && (
               <button
                 className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-gitlab"
@@ -137,6 +155,15 @@ export default function TutorialSelectionRenderer(props: TutorialSelectionRender
         <ManualTutorial component={component} currentUser={currentUser} />
       )}
 
+      {selectedTutorial === TutorialModes.GitHubActions && projectBinding !== undefined && (
+        <GitHubActionTutorial
+          baseUrl={baseUrl}
+          component={component}
+          currentUser={currentUser}
+          projectBinding={projectBinding}
+        />
+      )}
+
       {selectedTutorial === TutorialModes.Jenkins && projectBinding !== undefined && (
         <JenkinsTutorial
           almBinding={almBinding}
index 5ff0893230d6d6eba46099df64572e261bea60a6..4709f308631a6e075a64980fa8d422795301e989 100644 (file)
@@ -33,11 +33,17 @@ import TutorialSelectionRenderer, {
 } from '../TutorialSelectionRenderer';
 import { TutorialModes } from '../types';
 
+it.each([
+  ['bitbucket server', mockProjectBitbucketBindingResponse()],
+  ['github', mockProjectGithubBindingResponse()],
+  ['gitlab', mockProjectGitLabBindingResponse()],
+  ['azure', mockProjectAzureBindingResponse()]
+])('should render correctly for %s', (_, projectBinding) => {
+  expect(shallowRender({ projectBinding })).toMatchSnapshot();
+});
+
 it('should render correctly', () => {
   expect(shallowRender()).toMatchSnapshot('selection');
-  expect(shallowRender({ projectBinding: mockProjectBitbucketBindingResponse() })).toMatchSnapshot(
-    'selection with jenkins available'
-  );
   expect(shallowRender({ loading: true })).toMatchSnapshot('loading');
   expect(shallowRender({ selectedTutorial: TutorialModes.Manual })).toMatchSnapshot(
     'manual tutorial'
@@ -48,6 +54,12 @@ it('should render correctly', () => {
       projectBinding: mockProjectBitbucketBindingResponse()
     })
   ).toMatchSnapshot('jenkins tutorial');
+  expect(
+    shallowRender({
+      selectedTutorial: TutorialModes.GitHubActions,
+      projectBinding: mockProjectGitLabBindingResponse()
+    })
+  ).toMatchSnapshot('github actions tutorial');
   expect(
     shallowRender({
       selectedTutorial: TutorialModes.GitLabCI,
@@ -88,6 +100,9 @@ it('should allow mode selection for Github', () => {
 
   click(wrapper.find('button.tutorial-mode-manual'));
   expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.Manual);
+
+  click(wrapper.find('button.tutorial-mode-github'));
+  expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.GitHubActions);
 });
 
 it('should allow mode selection for GitLab', () => {
index bcdfec2e0169a372eaf5b2c00b787ff671677da6..69f3cb08ae5729645bed142b8db6e62853f433e3 100644 (file)
@@ -1,5 +1,254 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`should render correctly for azure 1`] = `
+<Fragment>
+  <div
+    className="tutorial-selection"
+  >
+    <header
+      className="spacer-top spacer-bottom padded"
+    >
+      <h1
+        className="text-center big-spacer-bottom"
+      >
+        onboarding.tutorial.choose_method
+      </h1>
+    </header>
+    <div
+      className="display-flex-justify-center"
+    >
+      <button
+        className="button button-huge display-flex-column spacer-left spacer-right azure-pipelines"
+        onClick={[Function]}
+        type="button"
+      >
+        <img
+          alt=""
+          height={80}
+          src="/images/alm/azure.svg"
+        />
+        <div
+          className="medium big-spacer-top"
+        >
+          onboarding.tutorial.choose_method.azure_pipelines
+        </div>
+      </button>
+      <button
+        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-manual"
+        onClick={[Function]}
+        type="button"
+      >
+        <img
+          alt=""
+          height={80}
+          src="/images/sonarcloud/analysis/manual.svg"
+        />
+        <div
+          className="medium big-spacer-top"
+        >
+          onboarding.tutorial.choose_method.manual
+        </div>
+      </button>
+    </div>
+  </div>
+</Fragment>
+`;
+
+exports[`should render correctly for bitbucket server 1`] = `
+<Fragment>
+  <div
+    className="tutorial-selection"
+  >
+    <header
+      className="spacer-top spacer-bottom padded"
+    >
+      <h1
+        className="text-center big-spacer-bottom"
+      >
+        onboarding.tutorial.choose_method
+      </h1>
+    </header>
+    <div
+      className="display-flex-justify-center"
+    >
+      <button
+        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-jenkins"
+        onClick={[Function]}
+        type="button"
+      >
+        <img
+          alt=""
+          height={80}
+          src="/images/tutorials/jenkins.svg"
+        />
+        <div
+          className="medium big-spacer-top"
+        >
+          onboarding.tutorial.choose_method.jenkins
+        </div>
+      </button>
+      <button
+        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-manual"
+        onClick={[Function]}
+        type="button"
+      >
+        <img
+          alt=""
+          height={80}
+          src="/images/sonarcloud/analysis/manual.svg"
+        />
+        <div
+          className="medium big-spacer-top"
+        >
+          onboarding.tutorial.choose_method.manual
+        </div>
+      </button>
+    </div>
+  </div>
+</Fragment>
+`;
+
+exports[`should render correctly for github 1`] = `
+<Fragment>
+  <div
+    className="tutorial-selection"
+  >
+    <header
+      className="spacer-top spacer-bottom padded"
+    >
+      <h1
+        className="text-center big-spacer-bottom"
+      >
+        onboarding.tutorial.choose_method
+      </h1>
+    </header>
+    <div
+      className="display-flex-justify-center"
+    >
+      <button
+        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-github"
+        onClick={[Function]}
+        type="button"
+      >
+        <img
+          alt=""
+          className="spacer-bottom spacer-top"
+          height={64}
+          src="/images/tutorials/github-actions.svg"
+        />
+        <div
+          className="medium big-spacer-top"
+        >
+          onboarding.tutorial.choose_method.github_action
+        </div>
+      </button>
+      <button
+        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-jenkins"
+        onClick={[Function]}
+        type="button"
+      >
+        <img
+          alt=""
+          height={80}
+          src="/images/tutorials/jenkins.svg"
+        />
+        <div
+          className="medium big-spacer-top"
+        >
+          onboarding.tutorial.choose_method.jenkins
+        </div>
+      </button>
+      <button
+        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-manual"
+        onClick={[Function]}
+        type="button"
+      >
+        <img
+          alt=""
+          height={80}
+          src="/images/sonarcloud/analysis/manual.svg"
+        />
+        <div
+          className="medium big-spacer-top"
+        >
+          onboarding.tutorial.choose_method.manual
+        </div>
+      </button>
+    </div>
+  </div>
+</Fragment>
+`;
+
+exports[`should render correctly for gitlab 1`] = `
+<Fragment>
+  <div
+    className="tutorial-selection"
+  >
+    <header
+      className="spacer-top spacer-bottom padded"
+    >
+      <h1
+        className="text-center big-spacer-bottom"
+      >
+        onboarding.tutorial.choose_method
+      </h1>
+    </header>
+    <div
+      className="display-flex-justify-center"
+    >
+      <button
+        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-gitlab"
+        onClick={[Function]}
+        type="button"
+      >
+        <img
+          alt=""
+          height={80}
+          src="/images/alm/gitlab.svg"
+        />
+        <div
+          className="medium big-spacer-top"
+        >
+          onboarding.tutorial.choose_method.gitlab_ci
+        </div>
+      </button>
+      <button
+        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-jenkins"
+        onClick={[Function]}
+        type="button"
+      >
+        <img
+          alt=""
+          height={80}
+          src="/images/tutorials/jenkins.svg"
+        />
+        <div
+          className="medium big-spacer-top"
+        >
+          onboarding.tutorial.choose_method.jenkins
+        </div>
+      </button>
+      <button
+        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-manual"
+        onClick={[Function]}
+        type="button"
+      >
+        <img
+          alt=""
+          height={80}
+          src="/images/sonarcloud/analysis/manual.svg"
+        />
+        <div
+          className="medium big-spacer-top"
+        >
+          onboarding.tutorial.choose_method.manual
+        </div>
+      </button>
+    </div>
+  </div>
+</Fragment>
+`;
+
 exports[`should render correctly: azure pipelines tutorial 1`] = `
 <Fragment>
   <AzurePipelinesTutorial
@@ -49,6 +298,54 @@ exports[`should render correctly: azure pipelines tutorial 1`] = `
 </Fragment>
 `;
 
+exports[`should render correctly: github actions tutorial 1`] = `
+<Fragment>
+  <GitHubActionTutorial
+    baseUrl="http://localhost:9000"
+    component={
+      Object {
+        "breadcrumbs": Array [],
+        "key": "my-project",
+        "name": "MyProject",
+        "qualifier": "TRK",
+        "qualityGate": Object {
+          "isDefault": true,
+          "key": "30",
+          "name": "Sonar way",
+        },
+        "qualityProfiles": Array [
+          Object {
+            "deleted": false,
+            "key": "my-qp",
+            "language": "ts",
+            "name": "Sonar way",
+          },
+        ],
+        "tags": Array [],
+      }
+    }
+    currentUser={
+      Object {
+        "groups": Array [],
+        "isLoggedIn": true,
+        "login": "luke",
+        "name": "Skywalker",
+        "scmAccounts": Array [],
+      }
+    }
+    projectBinding={
+      Object {
+        "alm": "gitlab",
+        "key": "foo",
+        "monorepo": true,
+        "repository": "PROJECT_KEY",
+        "url": "https://gitlab.com/api/v4",
+      }
+    }
+  />
+</Fragment>
+`;
+
 exports[`should render correctly: gitlab tutorial 1`] = `
 <Fragment>
   <GitLabCITutorial
@@ -223,57 +520,3 @@ exports[`should render correctly: selection 1`] = `
   </div>
 </Fragment>
 `;
-
-exports[`should render correctly: selection with jenkins available 1`] = `
-<Fragment>
-  <div
-    className="tutorial-selection"
-  >
-    <header
-      className="spacer-top spacer-bottom padded"
-    >
-      <h1
-        className="text-center big-spacer-bottom"
-      >
-        onboarding.tutorial.choose_method
-      </h1>
-    </header>
-    <div
-      className="display-flex-justify-center"
-    >
-      <button
-        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-jenkins"
-        onClick={[Function]}
-        type="button"
-      >
-        <img
-          alt=""
-          height={80}
-          src="/images/tutorials/jenkins.svg"
-        />
-        <div
-          className="medium big-spacer-top"
-        >
-          onboarding.tutorial.choose_method.jenkins
-        </div>
-      </button>
-      <button
-        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-manual"
-        onClick={[Function]}
-        type="button"
-      >
-        <img
-          alt=""
-          height={80}
-          src="/images/sonarcloud/analysis/manual.svg"
-        />
-        <div
-          className="medium big-spacer-top"
-        >
-          onboarding.tutorial.choose_method.manual
-        </div>
-      </button>
-    </div>
-  </div>
-</Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/AllSet.tsx b/server/sonar-web/src/main/js/components/tutorials/components/AllSet.tsx
new file mode 100644 (file)
index 0000000..74dd138
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import { getBaseUrl } from 'sonar-ui-common/helpers/urls';
+import { withAppState } from '../../hoc/withAppState';
+import SentenceWithHighlights from './SentenceWithHighlights';
+
+export interface AllSetProps {
+  appState: T.AppState;
+}
+
+export function AllSet(props: AllSetProps) {
+  const {
+    appState: { branchesEnabled }
+  } = props;
+
+  return (
+    <div className="abs-width-600">
+      <p className="big-spacer-bottom">
+        <SentenceWithHighlights
+          highlightKeys={['all_set']}
+          translationKey="onboarding.tutorial.ci_outro.all_set"
+        />
+      </p>
+      <div className="display-flex-row big-spacer-bottom">
+        <div>
+          <img
+            alt="" // Should be ignored by screen readers
+            className="big-spacer-right"
+            width={30}
+            src={`${getBaseUrl()}/images/tutorials/commit.svg`}
+          />
+        </div>
+        <div>
+          <p className="little-spacer-bottom">
+            <strong>{translate('onboarding.tutorial.ci_outro.commit')}</strong>
+          </p>
+          <p>
+            {branchesEnabled
+              ? translate('onboarding.tutorial.ci_outro.commit.why')
+              : translate('onboarding.tutorial.ci_outro.commit.why.no_branches')}
+          </p>
+        </div>
+      </div>
+      <div className="display-flex-row huge-spacer-bottom">
+        <div>
+          <img
+            alt="" // Should be ignored by screen readers
+            className="big-spacer-right"
+            width={30}
+            src={`${getBaseUrl()}/images/tutorials/refresh.svg`}
+          />
+        </div>
+        <div>
+          <p className="little-spacer-bottom">
+            <strong>{translate('onboarding.tutorial.ci_outro.refresh')}</strong>
+          </p>
+          <p>{translate('onboarding.tutorial.ci_outro.refresh.why')}</p>
+        </div>
+      </div>
+    </div>
+  );
+}
+
+export default withAppState(AllSet);
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/DefaultProjectKey.tsx b/server/sonar-web/src/main/js/components/tutorials/components/DefaultProjectKey.tsx
new file mode 100644 (file)
index 0000000..70f457f
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import CodeSnippet from '../../common/CodeSnippet';
+import SentenceWithFilename from './SentenceWithFilename';
+
+export interface DefaultProjectKeyProps {
+  component: T.Component;
+}
+
+const sonarProjectSnippet = (key: string) => `sonar.projectKey=${key}`;
+
+export default function DefaultProjectKey(props: DefaultProjectKeyProps) {
+  const { component } = props;
+  return (
+    <li className="abs-width-600">
+      <SentenceWithFilename
+        filename="sonar-project.properties"
+        translationKey="onboarding.tutorial.other.project_key"
+      />
+      <CodeSnippet snippet={sonarProjectSnippet(component.key)} />
+    </li>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/TokenStepGenerator.tsx b/server/sonar-web/src/main/js/components/tutorials/components/TokenStepGenerator.tsx
new file mode 100644 (file)
index 0000000..dea0fa4
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
+import { Button } from 'sonar-ui-common/components/controls/buttons';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import EditTokenModal from './EditTokenModal';
+
+export interface TokenStepGeneratorProps {
+  component: T.Component;
+  currentUser: T.LoggedInUser;
+}
+
+export default function TokenStepGenerator(props: TokenStepGeneratorProps) {
+  const { component, currentUser } = props;
+  const [isModalVisible, toggleModal] = React.useState(false);
+
+  const toggleTokenModal = () => toggleModal(visible => !visible);
+  const closeTokenModal = () => toggleModal(false);
+
+  return (
+    <>
+      <li className="big-spacer-bottom">
+        <FormattedMessage
+          defaultMessage={translate('onboarding.tutorial.env_variables')}
+          id="onboarding.tutorial.env_variables"
+          values={{
+            extra: (
+              <Button className="spacer-left" onClick={toggleTokenModal}>
+                {translate('onboarding.token.generate_token')}
+              </Button>
+            ),
+            field: <strong>{translate('onboarding.tutorial.env_variables.field')}</strong>,
+            value: translate('onboarding.tutorial.env_variables.token_generator.value')
+          }}
+        />
+      </li>
+      {isModalVisible && (
+        <EditTokenModal component={component} currentUser={currentUser} onClose={closeTokenModal} />
+      )}
+    </>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/AllSet-test.tsx b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/AllSet-test.tsx
new file mode 100644 (file)
index 0000000..1616135
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { mockAppState } from '../../../../helpers/testMocks';
+import { AllSet, AllSetProps } from '../AllSet';
+
+it('should render correctly', () => {
+  expect(shallowRender()).toMatchSnapshot();
+  expect(shallowRender({ appState: mockAppState({ branchesEnabled: false }) })).toMatchSnapshot(
+    'without branch'
+  );
+});
+
+function shallowRender(props: Partial<AllSetProps> = {}) {
+  return shallow<AllSetProps>(<AllSet appState={mockAppState()} {...props} />);
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/DefaultProjectKey-test.tsx b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/DefaultProjectKey-test.tsx
new file mode 100644 (file)
index 0000000..e4e023f
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { mockComponent } from '../../../../helpers/testMocks';
+import DefaultProjectKey, { DefaultProjectKeyProps } from '../DefaultProjectKey';
+
+it('should render correctly', () => {
+  expect(shallowRender()).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<DefaultProjectKeyProps> = {}) {
+  return shallow<DefaultProjectKeyProps>(
+    <DefaultProjectKey component={mockComponent()} {...props} />
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/TokenStepGenerator-test.tsx b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/TokenStepGenerator-test.tsx
new file mode 100644 (file)
index 0000000..b93f00b
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { mockComponent, mockLoggedInUser } from '../../../../helpers/testMocks';
+import TokenStepGenerator, { TokenStepGeneratorProps } from '../TokenStepGenerator';
+
+it('should render correctly', () => {
+  expect(shallowRender()).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<TokenStepGeneratorProps> = {}) {
+  return shallow<TokenStepGeneratorProps>(
+    <TokenStepGenerator component={mockComponent()} currentUser={mockLoggedInUser()} {...props} />
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/AllSet-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/AllSet-test.tsx.snap
new file mode 100644 (file)
index 0000000..6afe9cd
--- /dev/null
@@ -0,0 +1,135 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<div
+  className="abs-width-600"
+>
+  <p
+    className="big-spacer-bottom"
+  >
+    <SentenceWithHighlights
+      highlightKeys={
+        Array [
+          "all_set",
+        ]
+      }
+      translationKey="onboarding.tutorial.ci_outro.all_set"
+    />
+  </p>
+  <div
+    className="display-flex-row big-spacer-bottom"
+  >
+    <div>
+      <img
+        alt=""
+        className="big-spacer-right"
+        src="/images/tutorials/commit.svg"
+        width={30}
+      />
+    </div>
+    <div>
+      <p
+        className="little-spacer-bottom"
+      >
+        <strong>
+          onboarding.tutorial.ci_outro.commit
+        </strong>
+      </p>
+      <p>
+        onboarding.tutorial.ci_outro.commit.why.no_branches
+      </p>
+    </div>
+  </div>
+  <div
+    className="display-flex-row huge-spacer-bottom"
+  >
+    <div>
+      <img
+        alt=""
+        className="big-spacer-right"
+        src="/images/tutorials/refresh.svg"
+        width={30}
+      />
+    </div>
+    <div>
+      <p
+        className="little-spacer-bottom"
+      >
+        <strong>
+          onboarding.tutorial.ci_outro.refresh
+        </strong>
+      </p>
+      <p>
+        onboarding.tutorial.ci_outro.refresh.why
+      </p>
+    </div>
+  </div>
+</div>
+`;
+
+exports[`should render correctly: without branch 1`] = `
+<div
+  className="abs-width-600"
+>
+  <p
+    className="big-spacer-bottom"
+  >
+    <SentenceWithHighlights
+      highlightKeys={
+        Array [
+          "all_set",
+        ]
+      }
+      translationKey="onboarding.tutorial.ci_outro.all_set"
+    />
+  </p>
+  <div
+    className="display-flex-row big-spacer-bottom"
+  >
+    <div>
+      <img
+        alt=""
+        className="big-spacer-right"
+        src="/images/tutorials/commit.svg"
+        width={30}
+      />
+    </div>
+    <div>
+      <p
+        className="little-spacer-bottom"
+      >
+        <strong>
+          onboarding.tutorial.ci_outro.commit
+        </strong>
+      </p>
+      <p>
+        onboarding.tutorial.ci_outro.commit.why.no_branches
+      </p>
+    </div>
+  </div>
+  <div
+    className="display-flex-row huge-spacer-bottom"
+  >
+    <div>
+      <img
+        alt=""
+        className="big-spacer-right"
+        src="/images/tutorials/refresh.svg"
+        width={30}
+      />
+    </div>
+    <div>
+      <p
+        className="little-spacer-bottom"
+      >
+        <strong>
+          onboarding.tutorial.ci_outro.refresh
+        </strong>
+      </p>
+      <p>
+        onboarding.tutorial.ci_outro.refresh.why
+      </p>
+    </div>
+  </div>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/DefaultProjectKey-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/DefaultProjectKey-test.tsx.snap
new file mode 100644 (file)
index 0000000..cb21627
--- /dev/null
@@ -0,0 +1,15 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<li
+  className="abs-width-600"
+>
+  <SentenceWithFilename
+    filename="sonar-project.properties"
+    translationKey="onboarding.tutorial.other.project_key"
+  />
+  <CodeSnippet
+    snippet="sonar.projectKey=my-project"
+  />
+</li>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/TokenStepGenerator-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/TokenStepGenerator-test.tsx.snap
new file mode 100644 (file)
index 0000000..f7a1768
--- /dev/null
@@ -0,0 +1,28 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<Fragment>
+  <li
+    className="big-spacer-bottom"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.tutorial.env_variables"
+      id="onboarding.tutorial.env_variables"
+      values={
+        Object {
+          "extra": <Button
+            className="spacer-left"
+            onClick={[Function]}
+          >
+            onboarding.token.generate_token
+          </Button>,
+          "field": <strong>
+            onboarding.tutorial.env_variables.field
+          </strong>,
+          "value": "onboarding.tutorial.env_variables.token_generator.value",
+        }
+      }
+    />
+  </li>
+</Fragment>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx
new file mode 100644 (file)
index 0000000..c0147f4
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { withAppState } from '../../hoc/withAppState';
+import { BuildTools } from '../types';
+import DotNet from './commands/DotNet';
+import Gradle from './commands/Gradle';
+import JavaMaven from './commands/JavaMaven';
+import Others from './commands/Others';
+
+export interface AnalysisCommandProps {
+  appState: T.AppState;
+  buildTool?: BuildTools;
+  component: T.Component;
+}
+
+export function AnalysisCommand(props: AnalysisCommandProps) {
+  const {
+    buildTool,
+    component,
+    appState: { branchesEnabled }
+  } = props;
+
+  if (!buildTool) {
+    return null;
+  }
+
+  switch (buildTool) {
+    case BuildTools.Maven:
+      return <JavaMaven branchesEnabled={branchesEnabled} component={component} />;
+    case BuildTools.Gradle:
+      return <Gradle branchesEnabled={branchesEnabled} component={component} />;
+    case BuildTools.DotNet:
+      return <DotNet branchesEnabled={branchesEnabled} component={component} />;
+    case BuildTools.Other:
+      return <Others branchesEnabled={branchesEnabled} component={component} />;
+  }
+  return null;
+}
+
+export default withAppState(AnalysisCommand);
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/GitHubActionTutorial.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/GitHubActionTutorial.tsx
new file mode 100644 (file)
index 0000000..dc176fd
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import { ProjectAlmBindingResponse } from '../../../types/alm-settings';
+import Step from '../components/Step';
+import SecretStep from './SecretStep';
+import YamlFileStep from './YamlFileStep';
+
+export enum Steps {
+  CREATE_SECRET = 1,
+  YAML = 2
+}
+
+export interface GitHubActionTutorialProps {
+  baseUrl: string;
+  component: T.Component;
+  currentUser: T.LoggedInUser;
+  projectBinding: ProjectAlmBindingResponse;
+}
+
+export default function GitHubActionTutorial(props: GitHubActionTutorialProps) {
+  const { baseUrl, currentUser, component, projectBinding } = props;
+
+  const [step, setStep] = React.useState<Steps>(Steps.CREATE_SECRET);
+  return (
+    <>
+      <Step
+        finished={step > Steps.CREATE_SECRET}
+        onOpen={() => setStep(Steps.CREATE_SECRET)}
+        open={step === Steps.CREATE_SECRET}
+        renderForm={() => (
+          <SecretStep
+            baseUrl={baseUrl}
+            component={component}
+            currentUser={currentUser}
+            projectBinding={projectBinding}
+            onDone={() => setStep(Steps.YAML)}
+          />
+        )}
+        stepNumber={Steps.CREATE_SECRET}
+        stepTitle={translate('onboarding.tutorial.with.github_action.create_secret.title')}
+      />
+      <Step
+        onOpen={() => setStep(Steps.YAML)}
+        open={step === Steps.YAML}
+        renderForm={() => <YamlFileStep component={component} />}
+        stepNumber={Steps.YAML}
+        stepTitle={translate('onboarding.tutorial.with.github_action.yaml.title')}
+      />
+    </>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/SecretStep.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/SecretStep.tsx
new file mode 100644 (file)
index 0000000..67796e9
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
+import { Button } from 'sonar-ui-common/components/controls/buttons';
+import { ClipboardIconButton } from 'sonar-ui-common/components/controls/clipboard';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import { ProjectAlmBindingResponse } from '../../../types/alm-settings';
+import SentenceWithHighlights from '../components/SentenceWithHighlights';
+import TokenStepGenerator from '../components/TokenStepGenerator';
+
+export interface SecretStepProps {
+  baseUrl: string;
+  component: T.Component;
+  currentUser: T.LoggedInUser;
+  projectBinding: ProjectAlmBindingResponse;
+  onDone: () => void;
+}
+
+export default function SecretStep(props: SecretStepProps) {
+  const {
+    baseUrl,
+    component,
+    currentUser,
+    projectBinding: { repository }
+  } = props;
+
+  return (
+    <div className="boxed-group-inner">
+      <p className="big-spacer-bottom">
+        <FormattedMessage
+          defaultMessage={translate('onboarding.tutorial.with.github_action.secret.intro')}
+          id="onboarding.tutorial.with.github_action.secret.intro"
+          values={{
+            settings_secret: repository ? (
+              <a
+                href={`https://github.com/${repository}/settings/secrets`}
+                target="_blank"
+                rel="noopener noreferrer">
+                {translate('onboarding.tutorial.with.github_action.secret.intro.link')}
+              </a>
+            ) : (
+              translate('onboarding.tutorial.with.github_action.secret.intro.link')
+            )
+          }}
+        />
+      </p>
+      <ol className="list-styled">
+        <li>
+          <SentenceWithHighlights
+            translationKey="onboarding.tutorial.with.github_action.secret.new"
+            highlightKeys={['new_secret']}
+          />
+        </li>
+        <li>
+          <SentenceWithHighlights
+            translationKey="onboarding.tutorial.with.github_action.secret.name"
+            highlightKeys={['name']}
+          />
+          <code className="rule little-spacer-left">SONAR_TOKEN</code>
+          <ClipboardIconButton copyValue="SONAR_TOKEN" />
+        </li>
+        <TokenStepGenerator component={component} currentUser={currentUser} />
+        <li>
+          <SentenceWithHighlights
+            translationKey="onboarding.tutorial.with.github_action.secret.add"
+            highlightKeys={['add_secret']}
+          />
+        </li>
+      </ol>
+
+      <hr className="no-horizontal-margins" />
+
+      <ol className="list-styled big-spacer-top big-spacer-bottom">
+        <li>
+          <SentenceWithHighlights
+            translationKey="onboarding.tutorial.with.github_action.secret.new"
+            highlightKeys={['new_secret']}
+          />
+        </li>
+        <li>
+          <SentenceWithHighlights
+            translationKey="onboarding.tutorial.with.github_action.secret.name"
+            highlightKeys={['name']}
+          />
+
+          <code className="rule little-spacer-left">SONAR_HOST_URL</code>
+          <ClipboardIconButton copyValue="SONAR_HOST_URL" />
+        </li>
+        <li className="big-spacer-bottom">
+          <FormattedMessage
+            defaultMessage={translate('onboarding.tutorial.env_variables')}
+            id="onboarding.tutorial.env_variables"
+            values={{
+              extra: <ClipboardIconButton copyValue={baseUrl} />,
+              field: <strong>{translate('onboarding.tutorial.env_variables.field')}</strong>,
+              value: <code className="rule">{baseUrl}</code>
+            }}
+          />
+        </li>
+        <li>
+          <SentenceWithHighlights
+            translationKey="onboarding.tutorial.with.github_action.secret.add"
+            highlightKeys={['add_secret']}
+          />
+        </li>
+      </ol>
+      <Button onClick={props.onDone}>{translate('continue')}</Button>
+    </div>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/YamlFileStep.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/YamlFileStep.tsx
new file mode 100644 (file)
index 0000000..e76055a
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import AllSet from '../components/AllSet';
+import RenderOptions from '../components/RenderOptions';
+import { BuildTools } from '../types';
+import AnalysisCommand from './AnalysisCommand';
+
+export interface YamlFileStepProps {
+  component: T.Component;
+}
+
+export default function YamlFileStep(props: YamlFileStepProps) {
+  const { component } = props;
+  const buildTools = [BuildTools.Maven, BuildTools.Gradle, BuildTools.DotNet, BuildTools.Other];
+  const [buildToolSelected, setBuildToolSelected] = React.useState<BuildTools>();
+
+  return (
+    <>
+      <ol className="list-styled big-spacer-top big-spacer-bottom">
+        <li>
+          {translate('onboarding.build')}
+
+          <RenderOptions
+            checked={buildToolSelected}
+            name="language"
+            onCheck={value => setBuildToolSelected(value as BuildTools)}
+            options={buildTools}
+            optionLabelKey="onboarding.build"
+          />
+        </li>
+        <AnalysisCommand buildTool={buildToolSelected} component={component} />
+      </ol>
+      {buildToolSelected !== undefined && (
+        <>
+          <hr className="huge-spacer-top huge-spacer-bottom" />
+          <AllSet />
+        </>
+      )}
+    </>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/AnalysisCommand-test.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/AnalysisCommand-test.tsx
new file mode 100644 (file)
index 0000000..461b5ac
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { mockAppState, mockComponent } from '../../../../helpers/testMocks';
+import { BuildTools } from '../../types';
+import { AnalysisCommand, AnalysisCommandProps } from '../AnalysisCommand';
+
+it.each([
+  undefined,
+  BuildTools.CFamily,
+  BuildTools.DotNet,
+  BuildTools.Gradle,
+  BuildTools.Maven,
+  BuildTools.Other
+])('should render correctly for %p', buildTool => {
+  expect(shallowRender({ buildTool })).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<AnalysisCommandProps> = {}) {
+  return shallow<AnalysisCommandProps>(
+    <AnalysisCommand appState={mockAppState()} component={mockComponent()} {...props} />
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/GitHubActionTutorial-test.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/GitHubActionTutorial-test.tsx
new file mode 100644 (file)
index 0000000..cc7f4a3
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { mockProjectGithubBindingResponse } from '../../../../helpers/mocks/alm-settings';
+import { mockComponent, mockLoggedInUser } from '../../../../helpers/testMocks';
+import Step from '../../components/Step';
+import GitHubActionTutorial, { GitHubActionTutorialProps } from '../GitHubActionTutorial';
+
+it('should render correctly', () => {
+  const wrapper = shallowRender();
+  expect(wrapper).toMatchSnapshot('For secret steps');
+  const stepYaml = wrapper.find(Step).at(1);
+  stepYaml.simulate('open');
+  expect(wrapper).toMatchSnapshot('For yaml steps');
+});
+
+function shallowRender(props: Partial<GitHubActionTutorialProps> = {}) {
+  return shallow<GitHubActionTutorialProps>(
+    <GitHubActionTutorial
+      baseUrl="test"
+      currentUser={mockLoggedInUser()}
+      component={mockComponent()}
+      projectBinding={mockProjectGithubBindingResponse()}
+      {...props}
+    />
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/SecretStep-test.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/SecretStep-test.tsx
new file mode 100644 (file)
index 0000000..57b80ab
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { mockProjectGithubBindingResponse } from '../../../../helpers/mocks/alm-settings';
+import { mockComponent, mockLoggedInUser } from '../../../../helpers/testMocks';
+import SecretStep, { SecretStepProps } from '../SecretStep';
+
+it('should render correctly', () => {
+  expect(shallowRender()).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<SecretStepProps> = {}) {
+  return shallow<SecretStepProps>(
+    <SecretStep
+      baseUrl="test"
+      component={mockComponent()}
+      currentUser={mockLoggedInUser()}
+      projectBinding={mockProjectGithubBindingResponse()}
+      onDone={jest.fn()}
+      {...props}
+    />
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/YamlFileStep-test.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/YamlFileStep-test.tsx
new file mode 100644 (file)
index 0000000..1d1f3c0
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { mockComponent } from '../../../../helpers/testMocks';
+import YamlFileStep, { YamlFileStepProps } from '../YamlFileStep';
+
+it('should render correctly', () => {
+  expect(shallowRender()).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<YamlFileStepProps> = {}) {
+  return shallow<YamlFileStepProps>(<YamlFileStep component={mockComponent()} {...props} />);
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/AnalysisCommand-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/AnalysisCommand-test.tsx.snap
new file mode 100644 (file)
index 0000000..4f86247
--- /dev/null
@@ -0,0 +1,113 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly for "cfamily" 1`] = `""`;
+
+exports[`should render correctly for "dotnet" 1`] = `
+<DotNet
+  component={
+    Object {
+      "breadcrumbs": Array [],
+      "key": "my-project",
+      "name": "MyProject",
+      "qualifier": "TRK",
+      "qualityGate": Object {
+        "isDefault": true,
+        "key": "30",
+        "name": "Sonar way",
+      },
+      "qualityProfiles": Array [
+        Object {
+          "deleted": false,
+          "key": "my-qp",
+          "language": "ts",
+          "name": "Sonar way",
+        },
+      ],
+      "tags": Array [],
+    }
+  }
+/>
+`;
+
+exports[`should render correctly for "gradle" 1`] = `
+<Gradle
+  component={
+    Object {
+      "breadcrumbs": Array [],
+      "key": "my-project",
+      "name": "MyProject",
+      "qualifier": "TRK",
+      "qualityGate": Object {
+        "isDefault": true,
+        "key": "30",
+        "name": "Sonar way",
+      },
+      "qualityProfiles": Array [
+        Object {
+          "deleted": false,
+          "key": "my-qp",
+          "language": "ts",
+          "name": "Sonar way",
+        },
+      ],
+      "tags": Array [],
+    }
+  }
+/>
+`;
+
+exports[`should render correctly for "maven" 1`] = `
+<JavaMaven
+  component={
+    Object {
+      "breadcrumbs": Array [],
+      "key": "my-project",
+      "name": "MyProject",
+      "qualifier": "TRK",
+      "qualityGate": Object {
+        "isDefault": true,
+        "key": "30",
+        "name": "Sonar way",
+      },
+      "qualityProfiles": Array [
+        Object {
+          "deleted": false,
+          "key": "my-qp",
+          "language": "ts",
+          "name": "Sonar way",
+        },
+      ],
+      "tags": Array [],
+    }
+  }
+/>
+`;
+
+exports[`should render correctly for "other" 1`] = `
+<Others
+  component={
+    Object {
+      "breadcrumbs": Array [],
+      "key": "my-project",
+      "name": "MyProject",
+      "qualifier": "TRK",
+      "qualityGate": Object {
+        "isDefault": true,
+        "key": "30",
+        "name": "Sonar way",
+      },
+      "qualityProfiles": Array [
+        Object {
+          "deleted": false,
+          "key": "my-qp",
+          "language": "ts",
+          "name": "Sonar way",
+        },
+      ],
+      "tags": Array [],
+    }
+  }
+/>
+`;
+
+exports[`should render correctly for undefined 1`] = `""`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/GitHubActionTutorial-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/GitHubActionTutorial-test.tsx.snap
new file mode 100644 (file)
index 0000000..caec4a2
--- /dev/null
@@ -0,0 +1,41 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly: For secret steps 1`] = `
+<Fragment>
+  <Step
+    finished={false}
+    onOpen={[Function]}
+    open={true}
+    renderForm={[Function]}
+    stepNumber={1}
+    stepTitle="onboarding.tutorial.with.github_action.create_secret.title"
+  />
+  <Step
+    onOpen={[Function]}
+    open={false}
+    renderForm={[Function]}
+    stepNumber={2}
+    stepTitle="onboarding.tutorial.with.github_action.yaml.title"
+  />
+</Fragment>
+`;
+
+exports[`should render correctly: For yaml steps 1`] = `
+<Fragment>
+  <Step
+    finished={true}
+    onOpen={[Function]}
+    open={false}
+    renderForm={[Function]}
+    stepNumber={1}
+    stepTitle="onboarding.tutorial.with.github_action.create_secret.title"
+  />
+  <Step
+    onOpen={[Function]}
+    open={true}
+    renderForm={[Function]}
+    stepNumber={2}
+    stepTitle="onboarding.tutorial.with.github_action.yaml.title"
+  />
+</Fragment>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/SecretStep-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/SecretStep-test.tsx.snap
new file mode 100644 (file)
index 0000000..357fd88
--- /dev/null
@@ -0,0 +1,175 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<div
+  className="boxed-group-inner"
+>
+  <p
+    className="big-spacer-bottom"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.tutorial.with.github_action.secret.intro"
+      id="onboarding.tutorial.with.github_action.secret.intro"
+      values={
+        Object {
+          "settings_secret": <a
+            href="https://github.com/PROJECT_KEY/settings/secrets"
+            rel="noopener noreferrer"
+            target="_blank"
+          >
+            onboarding.tutorial.with.github_action.secret.intro.link
+          </a>,
+        }
+      }
+    />
+  </p>
+  <ol
+    className="list-styled"
+  >
+    <li>
+      <SentenceWithHighlights
+        highlightKeys={
+          Array [
+            "new_secret",
+          ]
+        }
+        translationKey="onboarding.tutorial.with.github_action.secret.new"
+      />
+    </li>
+    <li>
+      <SentenceWithHighlights
+        highlightKeys={
+          Array [
+            "name",
+          ]
+        }
+        translationKey="onboarding.tutorial.with.github_action.secret.name"
+      />
+      <code
+        className="rule little-spacer-left"
+      >
+        SONAR_TOKEN
+      </code>
+      <ClipboardIconButton
+        copyValue="SONAR_TOKEN"
+      />
+    </li>
+    <TokenStepGenerator
+      component={
+        Object {
+          "breadcrumbs": Array [],
+          "key": "my-project",
+          "name": "MyProject",
+          "qualifier": "TRK",
+          "qualityGate": Object {
+            "isDefault": true,
+            "key": "30",
+            "name": "Sonar way",
+          },
+          "qualityProfiles": Array [
+            Object {
+              "deleted": false,
+              "key": "my-qp",
+              "language": "ts",
+              "name": "Sonar way",
+            },
+          ],
+          "tags": Array [],
+        }
+      }
+      currentUser={
+        Object {
+          "groups": Array [],
+          "isLoggedIn": true,
+          "login": "luke",
+          "name": "Skywalker",
+          "scmAccounts": Array [],
+        }
+      }
+    />
+    <li>
+      <SentenceWithHighlights
+        highlightKeys={
+          Array [
+            "add_secret",
+          ]
+        }
+        translationKey="onboarding.tutorial.with.github_action.secret.add"
+      />
+    </li>
+  </ol>
+  <hr
+    className="no-horizontal-margins"
+  />
+  <ol
+    className="list-styled big-spacer-top big-spacer-bottom"
+  >
+    <li>
+      <SentenceWithHighlights
+        highlightKeys={
+          Array [
+            "new_secret",
+          ]
+        }
+        translationKey="onboarding.tutorial.with.github_action.secret.new"
+      />
+    </li>
+    <li>
+      <SentenceWithHighlights
+        highlightKeys={
+          Array [
+            "name",
+          ]
+        }
+        translationKey="onboarding.tutorial.with.github_action.secret.name"
+      />
+      <code
+        className="rule little-spacer-left"
+      >
+        SONAR_HOST_URL
+      </code>
+      <ClipboardIconButton
+        copyValue="SONAR_HOST_URL"
+      />
+    </li>
+    <li
+      className="big-spacer-bottom"
+    >
+      <FormattedMessage
+        defaultMessage="onboarding.tutorial.env_variables"
+        id="onboarding.tutorial.env_variables"
+        values={
+          Object {
+            "extra": <ClipboardIconButton
+              copyValue="test"
+            />,
+            "field": <strong>
+              onboarding.tutorial.env_variables.field
+            </strong>,
+            "value": <code
+              className="rule"
+            >
+              test
+            </code>,
+          }
+        }
+      />
+    </li>
+    <li>
+      <SentenceWithHighlights
+        highlightKeys={
+          Array [
+            "add_secret",
+          ]
+        }
+        translationKey="onboarding.tutorial.with.github_action.secret.add"
+      />
+    </li>
+  </ol>
+  <Button
+    onClick={[MockFunction]}
+  >
+    continue
+  </Button>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/YamlFileStep-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/YamlFileStep-test.tsx.snap
new file mode 100644 (file)
index 0000000..8f3bfa8
--- /dev/null
@@ -0,0 +1,50 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<Fragment>
+  <ol
+    className="list-styled big-spacer-top big-spacer-bottom"
+  >
+    <li>
+      onboarding.build
+      <RenderOptions
+        name="language"
+        onCheck={[Function]}
+        optionLabelKey="onboarding.build"
+        options={
+          Array [
+            "maven",
+            "gradle",
+            "dotnet",
+            "other",
+          ]
+        }
+      />
+    </li>
+    <Connect(withAppState(AnalysisCommand))
+      component={
+        Object {
+          "breadcrumbs": Array [],
+          "key": "my-project",
+          "name": "MyProject",
+          "qualifier": "TRK",
+          "qualityGate": Object {
+            "isDefault": true,
+            "key": "30",
+            "name": "Sonar way",
+          },
+          "qualityProfiles": Array [
+            Object {
+              "deleted": false,
+              "key": "my-qp",
+              "language": "ts",
+              "name": "Sonar way",
+            },
+          ],
+          "tags": Array [],
+        }
+      }
+    />
+  </ol>
+</Fragment>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/CreateYmlFile.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/CreateYmlFile.tsx
new file mode 100644 (file)
index 0000000..e7c47ed
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
+import { ClipboardIconButton } from 'sonar-ui-common/components/controls/clipboard';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import CodeSnippet from '../../../common/CodeSnippet';
+
+export interface CreateYmlFileProps {
+  yamlTemplate: string;
+}
+
+export default function CreateYmlFile(props: CreateYmlFileProps) {
+  const { yamlTemplate } = props;
+  return (
+    <li className="abs-width-800">
+      <FormattedMessage
+        defaultMessage={translate('onboarding.tutorial.with.github_action.yaml.create_yml')}
+        id="onboarding.tutorial.with.github_action.yaml.create_yml"
+        values={{
+          file: (
+            <>
+              <code className="rule">.github/workflows/build.yml</code>
+              <ClipboardIconButton copyValue=".github/workflows/build.yml" />
+            </>
+          )
+        }}
+      />
+      <CodeSnippet snippet={yamlTemplate} />
+    </li>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/DotNet.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/DotNet.tsx
new file mode 100644 (file)
index 0000000..f8a5d5e
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import CreateYmlFile from './CreateYmlFile';
+
+export interface DotNetProps {
+  branchesEnabled?: boolean;
+  component: T.Component;
+}
+
+const dotnetYamlTemplate = (projectKey: string, branchesEnabled: boolean) => `name: Build
+on:
+  push:
+    branches:
+      - master # or the name of your main branch
+${branchesEnabled ? '  pull_request:\n    types: [opened, synchronize, reopened]' : ''}
+jobs:
+  build:
+    name: Build
+    runs-on: windows-latest
+    steps:
+      - name: Set up JDK 11
+        uses: actions/setup-java@v1
+        with:
+          java-version: 1.11
+      - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0  # Shallow clones should be disabled for a better relevancy of analysis
+      - name: Cache SonarQube packages
+        uses: actions/cache@v1
+        with:
+          path: ~\\sonar\\cache
+          key: \${{ runner.os }}-sonar
+          restore-keys: \${{ runner.os }}-sonar
+      - name: Cache SonarQube scanner
+        id: cache-sonar-scanner
+        uses: actions/cache@v1
+        with:
+          path: .\\.sonar\\scanner
+          key: \${{ runner.os }}-sonar-scanner
+          restore-keys: \${{ runner.os }}-sonar-scanner
+      - name: Install SonarQube scanner
+        if: steps.cache-sonar-scanner.outputs.cache-hit != 'true'
+        shell: powershell
+        run: |
+          New-Item -Path .\\.sonar\\scanner -ItemType Directory
+          dotnet tool update dotnet-sonarscanner --tool-path .\\.sonar\\scanner
+      - name: Build and analyze
+        env:
+          GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}  # Needed to get PR information, if any
+        shell: powershell
+        run: |
+          .\\.sonar\\scanner\\dotnet-sonarscanner begin /k:"${projectKey}" /d:sonar.login="\${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="\${{ secrets.SONAR_HOST_URL }}"
+          dotnet build
+          .\\.sonar\\scanner\\dotnet-sonarscanner end /d:sonar.login="\${{ secrets.SONAR_TOKEN }}"`;
+
+export default function DotNet(props: DotNetProps) {
+  const { component, branchesEnabled } = props;
+  return <CreateYmlFile yamlTemplate={dotnetYamlTemplate(component.key, !!branchesEnabled)} />;
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/Gradle.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/Gradle.tsx
new file mode 100644 (file)
index 0000000..6f3d323
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
+import { ClipboardIconButton } from 'sonar-ui-common/components/controls/clipboard';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import CodeSnippet from '../../../common/CodeSnippet';
+import { buildGradleSnippet } from '../../utils';
+import CreateYmlFile from './CreateYmlFile';
+
+export interface GradleProps {
+  branchesEnabled?: boolean;
+  component: T.Component;
+}
+const gradleYamlTemplate = (branchesEnabled: boolean) => `name: Build
+on:
+  push:
+    branches:
+      - master # or the name of your main branch
+${branchesEnabled ? '  pull_request:\n    types: [opened, synchronize, reopened]' : ''}
+jobs:
+  build:
+    name: Build
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0  # Shallow clones should be disabled for a better relevancy of analysis
+      - name: Set up JDK 11
+        uses: actions/setup-java@v1
+        with:
+          java-version: 11
+      - name: Cache SonarQube packages
+        uses: actions/cache@v1
+        with:
+          path: ~/.sonar/cache
+          key: \${{ runner.os }}-sonar
+          restore-keys: \${{ runner.os }}-sonar
+      - name: Cache Gradle packages
+        uses: actions/cache@v1
+        with:
+          path: ~/.gradle/caches
+          key: \${{ runner.os }}-gradle-\${{ hashFiles('**/*.gradle') }}
+          restore-keys: \${{ runner.os }}-gradle
+      - name: Build and analyze
+        env:
+          GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}  # Needed to get PR information, if any
+          SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }}
+          SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }}
+        run: ./gradlew build sonarqube --info`;
+
+export default function Gradle(props: GradleProps) {
+  const { component, branchesEnabled } = props;
+
+  return (
+    <>
+      <li className="abs-width-600">
+        <FormattedMessage
+          defaultMessage={translate('onboarding.tutorial.with.github_action.yaml.gradle')}
+          id="onboarding.tutorial.with.github_action.yaml.gradle"
+          values={{
+            gradle: (
+              <>
+                <code className="rule">build.gradle</code>
+                <ClipboardIconButton copyValue="build.gradle" />
+              </>
+            ),
+            sq: <code className="rule">org.sonarqube</code>
+          }}
+        />
+        <CodeSnippet snippet={buildGradleSnippet(component.key)} />
+      </li>
+      <CreateYmlFile yamlTemplate={gradleYamlTemplate(!!branchesEnabled)} />
+    </>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/JavaMaven.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/JavaMaven.tsx
new file mode 100644 (file)
index 0000000..cbafc00
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
+import { ClipboardIconButton } from 'sonar-ui-common/components/controls/clipboard';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import CodeSnippet from '../../../common/CodeSnippet';
+import { mavenPomSnippet } from '../../utils';
+import CreateYmlFile from './CreateYmlFile';
+
+export interface JavaMavenProps {
+  branchesEnabled?: boolean;
+  component: T.Component;
+}
+
+const mavenYamlTemplte = (branchesEnabled: boolean) => `name: Build
+on:
+  push:
+    branches:
+      - master # or the name of your main branch
+${branchesEnabled ? '  pull_request:\n    types: [opened, synchronize, reopened]' : ''}
+jobs:
+  build:
+    name: Build
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0  # Shallow clones should be disabled for a better relevancy of analysis
+      - name: Set up JDK 11
+        uses: actions/setup-java@v1
+        with:
+          java-version: 11
+      - name: Cache SonarQube packages
+        uses: actions/cache@v1
+        with:
+          path: ~/.sonar/cache
+          key: \${{ runner.os }}-sonar
+          restore-keys: \${{ runner.os }}-sonar
+      - name: Cache Maven packages
+        uses: actions/cache@v1
+        with:
+          path: ~/.m2
+          key: \${{ runner.os }}-m2-\${{ hashFiles('**/pom.xml') }}
+          restore-keys: \${{ runner.os }}-m2
+      - name: Build and analyze
+        env:
+          GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}  # Needed to get PR information, if any
+          SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }}
+          SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }}
+        run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar`;
+
+export default function JavaMaven(props: JavaMavenProps) {
+  const { component, branchesEnabled } = props;
+  return (
+    <>
+      <li className="abs-width-600">
+        <FormattedMessage
+          defaultMessage={translate('onboarding.tutorial.with.github_action.yaml.maven.pom')}
+          id="onboarding.tutorial.with.github_action.yaml.maven.pom"
+          values={{
+            pom: (
+              <>
+                <code className="rule">pom.xml</code>
+                <ClipboardIconButton copyValue="pom.xml" />
+              </>
+            )
+          }}
+        />
+        <CodeSnippet snippet={mavenPomSnippet(component.key)} />
+      </li>
+      <CreateYmlFile yamlTemplate={mavenYamlTemplte(!!branchesEnabled)} />
+    </>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/Others.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/Others.tsx
new file mode 100644 (file)
index 0000000..b2de445
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import DefaultProjectKey from '../../components/DefaultProjectKey';
+import CreateYmlFile from './CreateYmlFile';
+
+export interface OthersProps {
+  branchesEnabled?: boolean;
+  component: T.Component;
+}
+
+const dotnetYamlTemplate = (branchesEnabled: boolean) => `name: Build
+on:
+  push:
+    branches:
+      - master # or the name of your main branch
+${branchesEnabled ? '  pull_request:\n    types: [opened, synchronize, reopened]' : ''}
+jobs:
+  build:
+    name: Build
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0
+      - uses: docker://sonarsource/sonar-scanner-cli:latest
+        env:
+          GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
+          SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }}
+          SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }}`;
+
+export default function Others(props: OthersProps) {
+  const { component, branchesEnabled } = props;
+  return (
+    <>
+      <DefaultProjectKey component={component} />
+      <CreateYmlFile yamlTemplate={dotnetYamlTemplate(!!branchesEnabled)} />
+    </>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/CreateYmlFile-test.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/CreateYmlFile-test.tsx
new file mode 100644 (file)
index 0000000..9dfe3cb
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import CreateYmlFile, { CreateYmlFileProps } from '../CreateYmlFile';
+import { GradleProps } from '../Gradle';
+
+it('should render correctly', () => {
+  expect(shallowRender()).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<CreateYmlFileProps> = {}) {
+  return shallow<GradleProps>(<CreateYmlFile yamlTemplate="temaplate" {...props} />);
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/DotNet-test.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/DotNet-test.tsx
new file mode 100644 (file)
index 0000000..64f0b1e
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { mockComponent } from '../../../../../helpers/testMocks';
+import DotNet, { DotNetProps } from '../DotNet';
+
+it('should render correctly', () => {
+  expect(shallowRender()).toMatchSnapshot();
+  expect(shallowRender({ branchesEnabled: false })).toMatchSnapshot('without branch enabled');
+});
+
+function shallowRender(props: Partial<DotNetProps> = {}) {
+  return shallow<DotNetProps>(
+    <DotNet branchesEnabled={true} component={mockComponent()} {...props} />
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/Gradle-test.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/Gradle-test.tsx
new file mode 100644 (file)
index 0000000..cc1adcf
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { mockComponent } from '../../../../../helpers/testMocks';
+import Gradle, { GradleProps } from '../Gradle';
+
+it('should render correctly', () => {
+  expect(shallowRender()).toMatchSnapshot();
+  expect(shallowRender({ branchesEnabled: false })).toMatchSnapshot('without branch enabled');
+});
+
+function shallowRender(props: Partial<GradleProps> = {}) {
+  return shallow<GradleProps>(
+    <Gradle branchesEnabled={true} component={mockComponent()} {...props} />
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/JavaMaven-test.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/JavaMaven-test.tsx
new file mode 100644 (file)
index 0000000..8099964
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { mockComponent } from '../../../../../helpers/testMocks';
+import JavaMaven, { JavaMavenProps } from '../../commands/JavaMaven';
+
+it('should render correctly', () => {
+  expect(shallowRender()).toMatchSnapshot();
+  expect(shallowRender({ branchesEnabled: false })).toMatchSnapshot('without branch enabled');
+});
+
+function shallowRender(props: Partial<JavaMavenProps> = {}) {
+  return shallow<JavaMavenProps>(
+    <JavaMaven branchesEnabled={true} component={mockComponent()} {...props} />
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/Others-test.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/Others-test.tsx
new file mode 100644 (file)
index 0000000..8130c93
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { mockComponent } from '../../../../../helpers/testMocks';
+import Others, { OthersProps } from '../../commands/Others';
+
+it('should render correctly', () => {
+  expect(shallowRender()).toMatchSnapshot();
+  expect(shallowRender({ branchesEnabled: false })).toMatchSnapshot('without branch enabled');
+});
+
+function shallowRender(props: Partial<OthersProps> = {}) {
+  return shallow<OthersProps>(
+    <Others branchesEnabled={true} component={mockComponent()} {...props} />
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/CreateYmlFile-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/CreateYmlFile-test.tsx.snap
new file mode 100644 (file)
index 0000000..61e32f7
--- /dev/null
@@ -0,0 +1,29 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<li
+  className="abs-width-800"
+>
+  <FormattedMessage
+    defaultMessage="onboarding.tutorial.with.github_action.yaml.create_yml"
+    id="onboarding.tutorial.with.github_action.yaml.create_yml"
+    values={
+      Object {
+        "file": <React.Fragment>
+          <code
+            className="rule"
+          >
+            .github/workflows/build.yml
+          </code>
+          <ClipboardIconButton
+            copyValue=".github/workflows/build.yml"
+          />
+        </React.Fragment>,
+      }
+    }
+  />
+  <CodeSnippet
+    snippet="temaplate"
+  />
+</li>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/DotNet-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/DotNet-test.tsx.snap
new file mode 100644 (file)
index 0000000..ae1bd7a
--- /dev/null
@@ -0,0 +1,102 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<CreateYmlFile
+  yamlTemplate="name: Build
+on:
+  push:
+    branches:
+      - master # or the name of your main branch
+  pull_request:
+    types: [opened, synchronize, reopened]
+jobs:
+  build:
+    name: Build
+    runs-on: windows-latest
+    steps:
+      - name: Set up JDK 11
+        uses: actions/setup-java@v1
+        with:
+          java-version: 1.11
+      - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0  # Shallow clones should be disabled for a better relevancy of analysis
+      - name: Cache SonarQube packages
+        uses: actions/cache@v1
+        with:
+          path: ~\\\\sonar\\\\cache
+          key: \${{ runner.os }}-sonar
+          restore-keys: \${{ runner.os }}-sonar
+      - name: Cache SonarQube scanner
+        id: cache-sonar-scanner
+        uses: actions/cache@v1
+        with:
+          path: .\\\\.sonar\\\\scanner
+          key: \${{ runner.os }}-sonar-scanner
+          restore-keys: \${{ runner.os }}-sonar-scanner
+      - name: Install SonarQube scanner
+        if: steps.cache-sonar-scanner.outputs.cache-hit != 'true'
+        shell: powershell
+        run: |
+          New-Item -Path .\\\\.sonar\\\\scanner -ItemType Directory
+          dotnet tool update dotnet-sonarscanner --tool-path .\\\\.sonar\\\\scanner
+      - name: Build and analyze
+        env:
+          GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}  # Needed to get PR information, if any
+        shell: powershell
+        run: |
+          .\\\\.sonar\\\\scanner\\\\dotnet-sonarscanner begin /k:\\"my-project\\" /d:sonar.login=\\"\${{ secrets.SONAR_TOKEN }}\\" /d:sonar.host.url=\\"\${{ secrets.SONAR_HOST_URL }}\\"
+          dotnet build
+          .\\\\.sonar\\\\scanner\\\\dotnet-sonarscanner end /d:sonar.login=\\"\${{ secrets.SONAR_TOKEN }}\\""
+/>
+`;
+
+exports[`should render correctly: without branch enabled 1`] = `
+<CreateYmlFile
+  yamlTemplate="name: Build
+on:
+  push:
+    branches:
+      - master # or the name of your main branch
+
+jobs:
+  build:
+    name: Build
+    runs-on: windows-latest
+    steps:
+      - name: Set up JDK 11
+        uses: actions/setup-java@v1
+        with:
+          java-version: 1.11
+      - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0  # Shallow clones should be disabled for a better relevancy of analysis
+      - name: Cache SonarQube packages
+        uses: actions/cache@v1
+        with:
+          path: ~\\\\sonar\\\\cache
+          key: \${{ runner.os }}-sonar
+          restore-keys: \${{ runner.os }}-sonar
+      - name: Cache SonarQube scanner
+        id: cache-sonar-scanner
+        uses: actions/cache@v1
+        with:
+          path: .\\\\.sonar\\\\scanner
+          key: \${{ runner.os }}-sonar-scanner
+          restore-keys: \${{ runner.os }}-sonar-scanner
+      - name: Install SonarQube scanner
+        if: steps.cache-sonar-scanner.outputs.cache-hit != 'true'
+        shell: powershell
+        run: |
+          New-Item -Path .\\\\.sonar\\\\scanner -ItemType Directory
+          dotnet tool update dotnet-sonarscanner --tool-path .\\\\.sonar\\\\scanner
+      - name: Build and analyze
+        env:
+          GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}  # Needed to get PR information, if any
+        shell: powershell
+        run: |
+          .\\\\.sonar\\\\scanner\\\\dotnet-sonarscanner begin /k:\\"my-project\\" /d:sonar.login=\\"\${{ secrets.SONAR_TOKEN }}\\" /d:sonar.host.url=\\"\${{ secrets.SONAR_HOST_URL }}\\"
+          dotnet build
+          .\\\\.sonar\\\\scanner\\\\dotnet-sonarscanner end /d:sonar.login=\\"\${{ secrets.SONAR_TOKEN }}\\""
+/>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/Gradle-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/Gradle-test.tsx.snap
new file mode 100644 (file)
index 0000000..1c61bf0
--- /dev/null
@@ -0,0 +1,164 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<Fragment>
+  <li
+    className="abs-width-600"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.tutorial.with.github_action.yaml.gradle"
+      id="onboarding.tutorial.with.github_action.yaml.gradle"
+      values={
+        Object {
+          "gradle": <React.Fragment>
+            <code
+              className="rule"
+            >
+              build.gradle
+            </code>
+            <ClipboardIconButton
+              copyValue="build.gradle"
+            />
+          </React.Fragment>,
+          "sq": <code
+            className="rule"
+          >
+            org.sonarqube
+          </code>,
+        }
+      }
+    />
+    <CodeSnippet
+      snippet="plugins {
+  id \\"org.sonarqube\\" version \\"3.1.1\\"
+}
+
+sonarqube {
+  properties {
+    property \\"sonar.projectKey\\", \\"my-project\\"
+  }
+}"
+    />
+  </li>
+  <CreateYmlFile
+    yamlTemplate="name: Build
+on:
+  push:
+    branches:
+      - master # or the name of your main branch
+  pull_request:
+    types: [opened, synchronize, reopened]
+jobs:
+  build:
+    name: Build
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0  # Shallow clones should be disabled for a better relevancy of analysis
+      - name: Set up JDK 11
+        uses: actions/setup-java@v1
+        with:
+          java-version: 11
+      - name: Cache SonarQube packages
+        uses: actions/cache@v1
+        with:
+          path: ~/.sonar/cache
+          key: \${{ runner.os }}-sonar
+          restore-keys: \${{ runner.os }}-sonar
+      - name: Cache Gradle packages
+        uses: actions/cache@v1
+        with:
+          path: ~/.gradle/caches
+          key: \${{ runner.os }}-gradle-\${{ hashFiles('**/*.gradle') }}
+          restore-keys: \${{ runner.os }}-gradle
+      - name: Build and analyze
+        env:
+          GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}  # Needed to get PR information, if any
+          SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }}
+          SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }}
+        run: ./gradlew build sonarqube --info"
+  />
+</Fragment>
+`;
+
+exports[`should render correctly: without branch enabled 1`] = `
+<Fragment>
+  <li
+    className="abs-width-600"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.tutorial.with.github_action.yaml.gradle"
+      id="onboarding.tutorial.with.github_action.yaml.gradle"
+      values={
+        Object {
+          "gradle": <React.Fragment>
+            <code
+              className="rule"
+            >
+              build.gradle
+            </code>
+            <ClipboardIconButton
+              copyValue="build.gradle"
+            />
+          </React.Fragment>,
+          "sq": <code
+            className="rule"
+          >
+            org.sonarqube
+          </code>,
+        }
+      }
+    />
+    <CodeSnippet
+      snippet="plugins {
+  id \\"org.sonarqube\\" version \\"3.1.1\\"
+}
+
+sonarqube {
+  properties {
+    property \\"sonar.projectKey\\", \\"my-project\\"
+  }
+}"
+    />
+  </li>
+  <CreateYmlFile
+    yamlTemplate="name: Build
+on:
+  push:
+    branches:
+      - master # or the name of your main branch
+
+jobs:
+  build:
+    name: Build
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0  # Shallow clones should be disabled for a better relevancy of analysis
+      - name: Set up JDK 11
+        uses: actions/setup-java@v1
+        with:
+          java-version: 11
+      - name: Cache SonarQube packages
+        uses: actions/cache@v1
+        with:
+          path: ~/.sonar/cache
+          key: \${{ runner.os }}-sonar
+          restore-keys: \${{ runner.os }}-sonar
+      - name: Cache Gradle packages
+        uses: actions/cache@v1
+        with:
+          path: ~/.gradle/caches
+          key: \${{ runner.os }}-gradle-\${{ hashFiles('**/*.gradle') }}
+          restore-keys: \${{ runner.os }}-gradle
+      - name: Build and analyze
+        env:
+          GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}  # Needed to get PR information, if any
+          SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }}
+          SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }}
+        run: ./gradlew build sonarqube --info"
+  />
+</Fragment>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap
new file mode 100644 (file)
index 0000000..6661734
--- /dev/null
@@ -0,0 +1,142 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<Fragment>
+  <li
+    className="abs-width-600"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.tutorial.with.github_action.yaml.maven.pom"
+      id="onboarding.tutorial.with.github_action.yaml.maven.pom"
+      values={
+        Object {
+          "pom": <React.Fragment>
+            <code
+              className="rule"
+            >
+              pom.xml
+            </code>
+            <ClipboardIconButton
+              copyValue="pom.xml"
+            />
+          </React.Fragment>,
+        }
+      }
+    />
+    <CodeSnippet
+      snippet="<properties>
+  <sonar.projectKey>my-project</sonar.projectKey>
+</properties>"
+    />
+  </li>
+  <CreateYmlFile
+    yamlTemplate="name: Build
+on:
+  push:
+    branches:
+      - master # or the name of your main branch
+  pull_request:
+    types: [opened, synchronize, reopened]
+jobs:
+  build:
+    name: Build
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0  # Shallow clones should be disabled for a better relevancy of analysis
+      - name: Set up JDK 11
+        uses: actions/setup-java@v1
+        with:
+          java-version: 11
+      - name: Cache SonarQube packages
+        uses: actions/cache@v1
+        with:
+          path: ~/.sonar/cache
+          key: \${{ runner.os }}-sonar
+          restore-keys: \${{ runner.os }}-sonar
+      - name: Cache Maven packages
+        uses: actions/cache@v1
+        with:
+          path: ~/.m2
+          key: \${{ runner.os }}-m2-\${{ hashFiles('**/pom.xml') }}
+          restore-keys: \${{ runner.os }}-m2
+      - name: Build and analyze
+        env:
+          GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}  # Needed to get PR information, if any
+          SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }}
+          SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }}
+        run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar"
+  />
+</Fragment>
+`;
+
+exports[`should render correctly: without branch enabled 1`] = `
+<Fragment>
+  <li
+    className="abs-width-600"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.tutorial.with.github_action.yaml.maven.pom"
+      id="onboarding.tutorial.with.github_action.yaml.maven.pom"
+      values={
+        Object {
+          "pom": <React.Fragment>
+            <code
+              className="rule"
+            >
+              pom.xml
+            </code>
+            <ClipboardIconButton
+              copyValue="pom.xml"
+            />
+          </React.Fragment>,
+        }
+      }
+    />
+    <CodeSnippet
+      snippet="<properties>
+  <sonar.projectKey>my-project</sonar.projectKey>
+</properties>"
+    />
+  </li>
+  <CreateYmlFile
+    yamlTemplate="name: Build
+on:
+  push:
+    branches:
+      - master # or the name of your main branch
+
+jobs:
+  build:
+    name: Build
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0  # Shallow clones should be disabled for a better relevancy of analysis
+      - name: Set up JDK 11
+        uses: actions/setup-java@v1
+        with:
+          java-version: 11
+      - name: Cache SonarQube packages
+        uses: actions/cache@v1
+        with:
+          path: ~/.sonar/cache
+          key: \${{ runner.os }}-sonar
+          restore-keys: \${{ runner.os }}-sonar
+      - name: Cache Maven packages
+        uses: actions/cache@v1
+        with:
+          path: ~/.m2
+          key: \${{ runner.os }}-m2-\${{ hashFiles('**/pom.xml') }}
+          restore-keys: \${{ runner.os }}-m2
+      - name: Build and analyze
+        env:
+          GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}  # Needed to get PR information, if any
+          SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }}
+          SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }}
+        run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar"
+  />
+</Fragment>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/Others-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/Others-test.tsx.snap
new file mode 100644 (file)
index 0000000..27415ba
--- /dev/null
@@ -0,0 +1,102 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<Fragment>
+  <DefaultProjectKey
+    component={
+      Object {
+        "breadcrumbs": Array [],
+        "key": "my-project",
+        "name": "MyProject",
+        "qualifier": "TRK",
+        "qualityGate": Object {
+          "isDefault": true,
+          "key": "30",
+          "name": "Sonar way",
+        },
+        "qualityProfiles": Array [
+          Object {
+            "deleted": false,
+            "key": "my-qp",
+            "language": "ts",
+            "name": "Sonar way",
+          },
+        ],
+        "tags": Array [],
+      }
+    }
+  />
+  <CreateYmlFile
+    yamlTemplate="name: Build
+on:
+  push:
+    branches:
+      - master # or the name of your main branch
+  pull_request:
+    types: [opened, synchronize, reopened]
+jobs:
+  build:
+    name: Build
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0
+      - uses: docker://sonarsource/sonar-scanner-cli:latest
+        env:
+          GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
+          SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }}
+          SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }}"
+  />
+</Fragment>
+`;
+
+exports[`should render correctly: without branch enabled 1`] = `
+<Fragment>
+  <DefaultProjectKey
+    component={
+      Object {
+        "breadcrumbs": Array [],
+        "key": "my-project",
+        "name": "MyProject",
+        "qualifier": "TRK",
+        "qualityGate": Object {
+          "isDefault": true,
+          "key": "30",
+          "name": "Sonar way",
+        },
+        "qualityProfiles": Array [
+          Object {
+            "deleted": false,
+            "key": "my-qp",
+            "language": "ts",
+            "name": "Sonar way",
+          },
+        ],
+        "tags": Array [],
+      }
+    }
+  />
+  <CreateYmlFile
+    yamlTemplate="name: Build
+on:
+  push:
+    branches:
+      - master # or the name of your main branch
+
+jobs:
+  build:
+    name: Build
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0
+      - uses: docker://sonarsource/sonar-scanner-cli:latest
+        env:
+          GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
+          SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }}
+          SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }}"
+  />
+</Fragment>
+`;
index 47e02fc1dc13cb6286437560086a27a5ae6a3dad..beb4f2f9ae62931a48e10a5e8cf20767a0cf117d 100644 (file)
@@ -22,8 +22,8 @@ import { FormattedMessage } from 'react-intl';
 import { Button } from 'sonar-ui-common/components/controls/buttons';
 import { ClipboardIconButton } from 'sonar-ui-common/components/controls/clipboard';
 import { translate } from 'sonar-ui-common/helpers/l10n';
-import EditTokenModal from '../components/EditTokenModal';
 import Step from '../components/Step';
+import TokenStepGenerator from '../components/TokenStepGenerator';
 
 export interface EnvironmentVariablesStepProps {
   baseUrl: string;
@@ -42,14 +42,7 @@ const pipelineDescriptionLinkLabel = translate(
 export default function EnvironmentVariablesStep(props: EnvironmentVariablesStepProps) {
   const { baseUrl, component, currentUser, finished, open } = props;
 
-  const [isModalVisible, toggleModal] = React.useState(false);
-
-  const toggleTokenModal = () => toggleModal(!isModalVisible);
-  const closeTokenModal = () => toggleModal(false);
-
-  const fieldValueTranslation = translate(
-    'onboarding.tutorial.with.gitlab_ci.env_variables.enter_field_value'
-  );
+  const fieldValueTranslation = translate('onboarding.tutorial.env_variables');
 
   const renderForm = () => (
     <div className="boxed-group-inner">
@@ -75,28 +68,16 @@ export default function EnvironmentVariablesStep(props: EnvironmentVariablesStep
             id="onboarding.tutorial.with.gitlab_ci.env_variables.step1"
             values={{
               extra: <ClipboardIconButton copyValue="SONAR_TOKEN" />,
-              field: translate('onboarding.tutorial.with.gitlab_ci.env_variables.step1'),
-              value: <code className="rule">SONAR_TOKEN</code>
-            }}
-          />
-        </li>
-        <li className="big-spacer-bottom">
-          <FormattedMessage
-            defaultMessage={fieldValueTranslation}
-            id="onboarding.tutorial.with.gitlab_ci.env_variables.step2"
-            values={{
-              extra: (
-                <Button className="spacer-left" onClick={toggleTokenModal}>
-                  {translate('onboarding.token.generate_token')}
-                </Button>
+              field: (
+                <strong>
+                  {translate('onboarding.tutorial.with.gitlab_ci.env_variables.step1')}
+                </strong>
               ),
-              field: translate('onboarding.tutorial.with.gitlab_ci.env_variables.step2'),
-              value: translate(
-                'onboarding.tutorial.with.gitlab_ci.env_variables.section.step2.value'
-              )
+              value: <code className="rule">SONAR_TOKEN</code>
             }}
           />
         </li>
+        <TokenStepGenerator component={component} currentUser={currentUser} />
         <li className="big-spacer-bottom">
           {translate('onboarding.tutorial.with.gitlab_ci.env_variables.step3')}
         </li>
@@ -129,7 +110,11 @@ export default function EnvironmentVariablesStep(props: EnvironmentVariablesStep
             id="onboarding.tutorial.with.gitlab_ci.env_variables.step1"
             values={{
               extra: <ClipboardIconButton copyValue="SONAR_HOST_URL" />,
-              field: translate('onboarding.tutorial.with.gitlab_ci.env_variables.step1'),
+              field: (
+                <strong>
+                  {translate('onboarding.tutorial.with.gitlab_ci.env_variables.step1')}
+                </strong>
+              ),
               value: <code className="rule">SONAR_HOST_URL</code>
             }}
           />
@@ -140,7 +125,7 @@ export default function EnvironmentVariablesStep(props: EnvironmentVariablesStep
             id="onboarding.tutorial.with.gitlab_ci.env_variables.step2"
             values={{
               extra: <ClipboardIconButton copyValue={baseUrl} />,
-              field: translate('onboarding.tutorial.with.gitlab_ci.env_variables.step2'),
+              field: <strong>{translate('onboarding.tutorial.env_variables.field')}</strong>,
               value: <code className="rule">{baseUrl}</code>
             }}
           />
@@ -160,19 +145,13 @@ export default function EnvironmentVariablesStep(props: EnvironmentVariablesStep
   );
 
   return (
-    <>
-      {isModalVisible && (
-        <EditTokenModal component={component} currentUser={currentUser} onClose={closeTokenModal} />
-      )}
-
-      <Step
-        finished={finished}
-        onOpen={props.onOpen}
-        open={open}
-        renderForm={renderForm}
-        stepNumber={2}
-        stepTitle={translate('onboarding.tutorial.with.gitlab_ci.env_variables.title')}
-      />
-    </>
+    <Step
+      finished={finished}
+      onOpen={props.onOpen}
+      open={open}
+      renderForm={renderForm}
+      stepNumber={2}
+      stepTitle={translate('onboarding.tutorial.with.gitlab_ci.env_variables.title')}
+    />
   );
 }
index c1e8ff710f1a85ff8394e1150d660e2353c10e57..3def93d2518667e155cf316ced975967bd34dfe2 100644 (file)
@@ -1,16 +1,14 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`should render correctly: Step wrapper 1`] = `
-<Fragment>
-  <Step
-    finished={false}
-    onOpen={[MockFunction]}
-    open={true}
-    renderForm={[Function]}
-    stepNumber={2}
-    stepTitle="onboarding.tutorial.with.gitlab_ci.env_variables.title"
-  />
-</Fragment>
+<Step
+  finished={false}
+  onOpen={[MockFunction]}
+  open={true}
+  renderForm={[Function]}
+  stepNumber={2}
+  stepTitle="onboarding.tutorial.with.gitlab_ci.env_variables.title"
+/>
 `;
 
 exports[`should render correctly: initial content 1`] = `
@@ -40,14 +38,16 @@ exports[`should render correctly: initial content 1`] = `
       className="big-spacer-bottom"
     >
       <FormattedMessage
-        defaultMessage="onboarding.tutorial.with.gitlab_ci.env_variables.enter_field_value"
+        defaultMessage="onboarding.tutorial.env_variables"
         id="onboarding.tutorial.with.gitlab_ci.env_variables.step1"
         values={
           Object {
             "extra": <ClipboardIconButton
               copyValue="SONAR_TOKEN"
             />,
-            "field": "onboarding.tutorial.with.gitlab_ci.env_variables.step1",
+            "field": <strong>
+              onboarding.tutorial.with.gitlab_ci.env_variables.step1
+            </strong>,
             "value": <code
               className="rule"
             >
@@ -57,26 +57,39 @@ exports[`should render correctly: initial content 1`] = `
         }
       />
     </li>
-    <li
-      className="big-spacer-bottom"
-    >
-      <FormattedMessage
-        defaultMessage="onboarding.tutorial.with.gitlab_ci.env_variables.enter_field_value"
-        id="onboarding.tutorial.with.gitlab_ci.env_variables.step2"
-        values={
-          Object {
-            "extra": <Button
-              className="spacer-left"
-              onClick={[Function]}
-            >
-              onboarding.token.generate_token
-            </Button>,
-            "field": "onboarding.tutorial.with.gitlab_ci.env_variables.step2",
-            "value": "onboarding.tutorial.with.gitlab_ci.env_variables.section.step2.value",
-          }
+    <TokenStepGenerator
+      component={
+        Object {
+          "breadcrumbs": Array [],
+          "key": "my-project",
+          "name": "MyProject",
+          "qualifier": "TRK",
+          "qualityGate": Object {
+            "isDefault": true,
+            "key": "30",
+            "name": "Sonar way",
+          },
+          "qualityProfiles": Array [
+            Object {
+              "deleted": false,
+              "key": "my-qp",
+              "language": "ts",
+              "name": "Sonar way",
+            },
+          ],
+          "tags": Array [],
         }
-      />
-    </li>
+      }
+      currentUser={
+        Object {
+          "groups": Array [],
+          "isLoggedIn": true,
+          "login": "luke",
+          "name": "Skywalker",
+          "scmAccounts": Array [],
+        }
+      }
+    />
     <li
       className="big-spacer-bottom"
     >
@@ -114,14 +127,16 @@ exports[`should render correctly: initial content 1`] = `
       className="big-spacer-bottom"
     >
       <FormattedMessage
-        defaultMessage="onboarding.tutorial.with.gitlab_ci.env_variables.enter_field_value"
+        defaultMessage="onboarding.tutorial.env_variables"
         id="onboarding.tutorial.with.gitlab_ci.env_variables.step1"
         values={
           Object {
             "extra": <ClipboardIconButton
               copyValue="SONAR_HOST_URL"
             />,
-            "field": "onboarding.tutorial.with.gitlab_ci.env_variables.step1",
+            "field": <strong>
+              onboarding.tutorial.with.gitlab_ci.env_variables.step1
+            </strong>,
             "value": <code
               className="rule"
             >
@@ -135,14 +150,16 @@ exports[`should render correctly: initial content 1`] = `
       className="big-spacer-bottom"
     >
       <FormattedMessage
-        defaultMessage="onboarding.tutorial.with.gitlab_ci.env_variables.enter_field_value"
+        defaultMessage="onboarding.tutorial.env_variables"
         id="onboarding.tutorial.with.gitlab_ci.env_variables.step2"
         values={
           Object {
             "extra": <ClipboardIconButton
               copyValue="http://localhost:9000"
             />,
-            "field": "onboarding.tutorial.with.gitlab_ci.env_variables.step2",
+            "field": <strong>
+              onboarding.tutorial.env_variables.field
+            </strong>,
             "value": <code
               className="rule"
             >
index 576463cf71648a442ee04be2ed02bc2567f2f3ef..4a2c4e2cf95423a52e1dec1870e49baf676025d3 100644 (file)
  */
 import * as React from 'react';
 import { translate } from 'sonar-ui-common/helpers/l10n';
-import { getBaseUrl } from 'sonar-ui-common/helpers/urls';
-import { withAppState } from '../../hoc/withAppState';
+import AllSet from '../components/AllSet';
 import RenderOptions from '../components/RenderOptions';
-import SentenceWithHighlights from '../components/SentenceWithHighlights';
 import Step from '../components/Step';
 import { BuildTools } from '../types';
 import DotNet from './buildtool-steps/DotNet';
@@ -31,7 +29,6 @@ import Maven from './buildtool-steps/Maven';
 import Other from './buildtool-steps/Other';
 
 export interface JenkinsfileStepProps {
-  appState: T.AppState;
   component: T.Component;
   open: boolean;
 }
@@ -48,12 +45,8 @@ const BUILDTOOL_COMPONENT_MAP: {
   [BuildTools.Other]: Other
 };
 
-export function JenkinsfileStep(props: JenkinsfileStepProps) {
-  const {
-    appState: { branchesEnabled },
-    component,
-    open
-  } = props;
+export default function JenkinsfileStep(props: JenkinsfileStepProps) {
+  const { component, open } = props;
   const [buildTool, setBuildTool] = React.useState<BuildToolsWithoutCFamily | undefined>(undefined);
   return (
     <Step
@@ -78,50 +71,7 @@ export function JenkinsfileStep(props: JenkinsfileStepProps) {
           {buildTool !== undefined && (
             <>
               <hr className="huge-spacer-top huge-spacer-bottom" />
-              <div className="abs-width-600">
-                <p className="big-spacer-bottom">
-                  <SentenceWithHighlights
-                    highlightKeys={['all_set']}
-                    translationKey="onboarding.tutorial.with.jenkins.all_set"
-                  />
-                </p>
-                <div className="display-flex-row big-spacer-bottom">
-                  <div>
-                    <img
-                      alt="" // Should be ignored by screen readers
-                      className="big-spacer-right"
-                      width={30}
-                      src={`${getBaseUrl()}/images/tutorials/commit.svg`}
-                    />
-                  </div>
-                  <div>
-                    <p className="little-spacer-bottom">
-                      <strong>{translate('onboarding.tutorial.with.jenkins.commit')}</strong>
-                    </p>
-                    <p>
-                      {branchesEnabled
-                        ? translate('onboarding.tutorial.with.jenkins.commit.why')
-                        : translate('onboarding.tutorial.with.jenkins.commit.why.no_branches')}
-                    </p>
-                  </div>
-                </div>
-                <div className="display-flex-row huge-spacer-bottom">
-                  <div>
-                    <img
-                      alt="" // Should be ignored by screen readers
-                      className="big-spacer-right"
-                      width={30}
-                      src={`${getBaseUrl()}/images/tutorials/refresh.svg`}
-                    />
-                  </div>
-                  <div>
-                    <p className="little-spacer-bottom">
-                      <strong>{translate('onboarding.tutorial.with.jenkins.refresh')}</strong>
-                    </p>
-                    <p>{translate('onboarding.tutorial.with.jenkins.refresh.why')}</p>
-                  </div>
-                </div>
-              </div>
+              <AllSet />
             </>
           )}
         </div>
@@ -131,5 +81,3 @@ export function JenkinsfileStep(props: JenkinsfileStepProps) {
     />
   );
 }
-
-export default withAppState(JenkinsfileStep);
index bdab1582f6054d95f7da31cdf1def6bea6e26348..d738038d7c54e0017a7c25a734257963f72d0904 100644 (file)
  */
 import { shallow, ShallowWrapper } from 'enzyme';
 import * as React from 'react';
-import { mockAppState, mockComponent } from '../../../../helpers/testMocks';
+import { mockComponent } from '../../../../helpers/testMocks';
 import RenderOptions from '../../components/RenderOptions';
 import Step from '../../components/Step';
 import { BuildTools } from '../../types';
-import { JenkinsfileStep, JenkinsfileStepProps } from '../JenkinsfileStep';
+import JenkinsfileStep, { JenkinsfileStepProps } from '../JenkinsfileStep';
 import { renderStepContent } from '../test-utils';
 
 it('should render correctly', () => {
@@ -32,12 +32,6 @@ it('should render correctly', () => {
   expect(renderStepContent(wrapper)).toMatchSnapshot('initial content');
 });
 
-it('should render correctly with no branches', () => {
-  const wrapper = shallowRender({ appState: mockAppState({ branchesEnabled: false }) });
-  selectBuildTool(wrapper, BuildTools.Gradle);
-  expect(renderStepContent(wrapper)).toMatchSnapshot();
-});
-
 it('should render correctly for Maven', () => {
   const wrapper = shallowRender();
   selectBuildTool(wrapper, BuildTools.Maven);
@@ -74,11 +68,6 @@ function selectBuildTool(wrapper: ShallowWrapper<JenkinsfileStepProps>, tool: Bu
 
 function shallowRender(props: Partial<JenkinsfileStepProps> = {}) {
   return shallow<JenkinsfileStepProps>(
-    <JenkinsfileStep
-      appState={mockAppState({ branchesEnabled: true })}
-      component={mockComponent()}
-      open={true}
-      {...props}
-    />
+    <JenkinsfileStep component={mockComponent()} open={true} {...props} />
   );
 }
index b6759ddfa3880680a1c53e2c3aa737f64075e350..a7ecadf597a8d36a95aecd293512dfb3a5d26e9a 100644 (file)
@@ -43,7 +43,7 @@ exports[`should render correctly: branches not enabled 1`] = `
       }
     }
   />
-  <Connect(withAppState(JenkinsfileStep))
+  <JenkinsfileStep
     component={
       Object {
         "breadcrumbs": Array [],
@@ -122,7 +122,7 @@ exports[`should render correctly: default 1`] = `
       }
     }
   />
-  <Connect(withAppState(JenkinsfileStep))
+  <JenkinsfileStep
     component={
       Object {
         "breadcrumbs": Array [],
index a4610804993e771ab532ea16ab98d72b52f31f9f..909464f7296cfaf6ff1b8ca2c0dde43f2101df7e 100644 (file)
@@ -53,70 +53,7 @@ exports[`should render correctly for .NET 1`] = `
     <hr
       className="huge-spacer-top huge-spacer-bottom"
     />
-    <div
-      className="abs-width-600"
-    >
-      <p
-        className="big-spacer-bottom"
-      >
-        <SentenceWithHighlights
-          highlightKeys={
-            Array [
-              "all_set",
-            ]
-          }
-          translationKey="onboarding.tutorial.with.jenkins.all_set"
-        />
-      </p>
-      <div
-        className="display-flex-row big-spacer-bottom"
-      >
-        <div>
-          <img
-            alt=""
-            className="big-spacer-right"
-            src="/images/tutorials/commit.svg"
-            width={30}
-          />
-        </div>
-        <div>
-          <p
-            className="little-spacer-bottom"
-          >
-            <strong>
-              onboarding.tutorial.with.jenkins.commit
-            </strong>
-          </p>
-          <p>
-            onboarding.tutorial.with.jenkins.commit.why
-          </p>
-        </div>
-      </div>
-      <div
-        className="display-flex-row huge-spacer-bottom"
-      >
-        <div>
-          <img
-            alt=""
-            className="big-spacer-right"
-            src="/images/tutorials/refresh.svg"
-            width={30}
-          />
-        </div>
-        <div>
-          <p
-            className="little-spacer-bottom"
-          >
-            <strong>
-              onboarding.tutorial.with.jenkins.refresh
-            </strong>
-          </p>
-          <p>
-            onboarding.tutorial.with.jenkins.refresh.why
-          </p>
-        </div>
-      </div>
-    </div>
+    <Connect(withAppState(AllSet)) />
   </React.Fragment>
 </div>
 `;
@@ -174,70 +111,7 @@ exports[`should render correctly for Gradle 1`] = `
     <hr
       className="huge-spacer-top huge-spacer-bottom"
     />
-    <div
-      className="abs-width-600"
-    >
-      <p
-        className="big-spacer-bottom"
-      >
-        <SentenceWithHighlights
-          highlightKeys={
-            Array [
-              "all_set",
-            ]
-          }
-          translationKey="onboarding.tutorial.with.jenkins.all_set"
-        />
-      </p>
-      <div
-        className="display-flex-row big-spacer-bottom"
-      >
-        <div>
-          <img
-            alt=""
-            className="big-spacer-right"
-            src="/images/tutorials/commit.svg"
-            width={30}
-          />
-        </div>
-        <div>
-          <p
-            className="little-spacer-bottom"
-          >
-            <strong>
-              onboarding.tutorial.with.jenkins.commit
-            </strong>
-          </p>
-          <p>
-            onboarding.tutorial.with.jenkins.commit.why
-          </p>
-        </div>
-      </div>
-      <div
-        className="display-flex-row huge-spacer-bottom"
-      >
-        <div>
-          <img
-            alt=""
-            className="big-spacer-right"
-            src="/images/tutorials/refresh.svg"
-            width={30}
-          />
-        </div>
-        <div>
-          <p
-            className="little-spacer-bottom"
-          >
-            <strong>
-              onboarding.tutorial.with.jenkins.refresh
-            </strong>
-          </p>
-          <p>
-            onboarding.tutorial.with.jenkins.refresh.why
-          </p>
-        </div>
-      </div>
-    </div>
+    <Connect(withAppState(AllSet)) />
   </React.Fragment>
 </div>
 `;
@@ -295,70 +169,7 @@ exports[`should render correctly for Maven 1`] = `
     <hr
       className="huge-spacer-top huge-spacer-bottom"
     />
-    <div
-      className="abs-width-600"
-    >
-      <p
-        className="big-spacer-bottom"
-      >
-        <SentenceWithHighlights
-          highlightKeys={
-            Array [
-              "all_set",
-            ]
-          }
-          translationKey="onboarding.tutorial.with.jenkins.all_set"
-        />
-      </p>
-      <div
-        className="display-flex-row big-spacer-bottom"
-      >
-        <div>
-          <img
-            alt=""
-            className="big-spacer-right"
-            src="/images/tutorials/commit.svg"
-            width={30}
-          />
-        </div>
-        <div>
-          <p
-            className="little-spacer-bottom"
-          >
-            <strong>
-              onboarding.tutorial.with.jenkins.commit
-            </strong>
-          </p>
-          <p>
-            onboarding.tutorial.with.jenkins.commit.why
-          </p>
-        </div>
-      </div>
-      <div
-        className="display-flex-row huge-spacer-bottom"
-      >
-        <div>
-          <img
-            alt=""
-            className="big-spacer-right"
-            src="/images/tutorials/refresh.svg"
-            width={30}
-          />
-        </div>
-        <div>
-          <p
-            className="little-spacer-bottom"
-          >
-            <strong>
-              onboarding.tutorial.with.jenkins.refresh
-            </strong>
-          </p>
-          <p>
-            onboarding.tutorial.with.jenkins.refresh.why
-          </p>
-        </div>
-      </div>
-    </div>
+    <Connect(withAppState(AllSet)) />
   </React.Fragment>
 </div>
 `;
@@ -416,191 +227,7 @@ exports[`should render correctly for Other 1`] = `
     <hr
       className="huge-spacer-top huge-spacer-bottom"
     />
-    <div
-      className="abs-width-600"
-    >
-      <p
-        className="big-spacer-bottom"
-      >
-        <SentenceWithHighlights
-          highlightKeys={
-            Array [
-              "all_set",
-            ]
-          }
-          translationKey="onboarding.tutorial.with.jenkins.all_set"
-        />
-      </p>
-      <div
-        className="display-flex-row big-spacer-bottom"
-      >
-        <div>
-          <img
-            alt=""
-            className="big-spacer-right"
-            src="/images/tutorials/commit.svg"
-            width={30}
-          />
-        </div>
-        <div>
-          <p
-            className="little-spacer-bottom"
-          >
-            <strong>
-              onboarding.tutorial.with.jenkins.commit
-            </strong>
-          </p>
-          <p>
-            onboarding.tutorial.with.jenkins.commit.why
-          </p>
-        </div>
-      </div>
-      <div
-        className="display-flex-row huge-spacer-bottom"
-      >
-        <div>
-          <img
-            alt=""
-            className="big-spacer-right"
-            src="/images/tutorials/refresh.svg"
-            width={30}
-          />
-        </div>
-        <div>
-          <p
-            className="little-spacer-bottom"
-          >
-            <strong>
-              onboarding.tutorial.with.jenkins.refresh
-            </strong>
-          </p>
-          <p>
-            onboarding.tutorial.with.jenkins.refresh.why
-          </p>
-        </div>
-      </div>
-    </div>
-  </React.Fragment>
-</div>
-`;
-
-exports[`should render correctly with no branches 1`] = `
-<div
-  className="boxed-group-inner"
->
-  <ol
-    className="list-styled"
-  >
-    <li>
-      onboarding.build
-      <RenderOptions
-        checked="gradle"
-        name="buildtool"
-        onCheck={[Function]}
-        optionLabelKey="onboarding.build"
-        options={
-          Array [
-            "maven",
-            "gradle",
-            "dotnet",
-            "other",
-          ]
-        }
-      />
-    </li>
-    <Gradle
-      component={
-        Object {
-          "breadcrumbs": Array [],
-          "key": "my-project",
-          "name": "MyProject",
-          "qualifier": "TRK",
-          "qualityGate": Object {
-            "isDefault": true,
-            "key": "30",
-            "name": "Sonar way",
-          },
-          "qualityProfiles": Array [
-            Object {
-              "deleted": false,
-              "key": "my-qp",
-              "language": "ts",
-              "name": "Sonar way",
-            },
-          ],
-          "tags": Array [],
-        }
-      }
-    />
-  </ol>
-  <React.Fragment>
-    <hr
-      className="huge-spacer-top huge-spacer-bottom"
-    />
-    <div
-      className="abs-width-600"
-    >
-      <p
-        className="big-spacer-bottom"
-      >
-        <SentenceWithHighlights
-          highlightKeys={
-            Array [
-              "all_set",
-            ]
-          }
-          translationKey="onboarding.tutorial.with.jenkins.all_set"
-        />
-      </p>
-      <div
-        className="display-flex-row big-spacer-bottom"
-      >
-        <div>
-          <img
-            alt=""
-            className="big-spacer-right"
-            src="/images/tutorials/commit.svg"
-            width={30}
-          />
-        </div>
-        <div>
-          <p
-            className="little-spacer-bottom"
-          >
-            <strong>
-              onboarding.tutorial.with.jenkins.commit
-            </strong>
-          </p>
-          <p>
-            onboarding.tutorial.with.jenkins.commit.why.no_branches
-          </p>
-        </div>
-      </div>
-      <div
-        className="display-flex-row huge-spacer-bottom"
-      >
-        <div>
-          <img
-            alt=""
-            className="big-spacer-right"
-            src="/images/tutorials/refresh.svg"
-            width={30}
-          />
-        </div>
-        <div>
-          <p
-            className="little-spacer-bottom"
-          >
-            <strong>
-              onboarding.tutorial.with.jenkins.refresh
-            </strong>
-          </p>
-          <p>
-            onboarding.tutorial.with.jenkins.refresh.why
-          </p>
-        </div>
-      </div>
-    </div>
+    <Connect(withAppState(AllSet)) />
   </React.Fragment>
 </div>
 `;
index 9ecebd02c11beb3af3d4dcb9ac2882c1548ceec2..9436298c2f842bb32d44b9492aadfd90baf89b7d 100644 (file)
 import * as React from 'react';
 import CodeSnippet from '../../../common/CodeSnippet';
 import SentenceWithFilename from '../../components/SentenceWithFilename';
+import { buildGradleSnippet } from '../../utils';
 import CreateJenkinsfileBulletPoint from './CreateJenkinsfileBulletPoint';
 
 export interface GradleProps {
   component: T.Component;
 }
 
-const buildGradleSnippet = (key: string) => `plugins {
-  id "org.sonarqube" version "3.1.1"
-}
-
-sonarqube {
-  properties {
-    property "sonar.projectKey", "${key}"
-  }
-}`;
-
 const JENKINSFILE_SNIPPET = `node {
   stage('SCM') {
     checkout scm
index 1f074b12121116e61517965961ad8a4183258b85..669ed20f4c9398a31af42d0f38a026da8b15fd04 100644 (file)
 import * as React from 'react';
 import CodeSnippet from '../../../common/CodeSnippet';
 import SentenceWithFilename from '../../components/SentenceWithFilename';
+import { mavenPomSnippet } from '../../utils';
 import CreateJenkinsfileBulletPoint from './CreateJenkinsfileBulletPoint';
 
 export interface MavenProps {
   component: T.Component;
 }
 
-const pomSnippet = (key: string) => `<properties>
-  <sonar.projectKey>${key}</sonar.projectKey>
-</properties>`;
-
 const JENKINSFILE_SNIPPET = `node {
   stage('SCM') {
     checkout scm
@@ -50,7 +47,7 @@ export default function Maven({ component }: MavenProps) {
           filename="pom.xml"
           translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.maven.step2"
         />
-        <CodeSnippet snippet={pomSnippet(component.key)} />
+        <CodeSnippet snippet={mavenPomSnippet(component.key)} />
       </li>
       <CreateJenkinsfileBulletPoint
         alertTranslationKeyPart="onboarding.tutorial.with.jenkins.jenkinsfile.maven.step3"
index 9fa8d96c280be58ae48ec0cb7fb2ac49b0e0e288..c66cd92cca6f179be1dc000548918dafdf657e0a 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import CodeSnippet from '../../../common/CodeSnippet';
-import SentenceWithFilename from '../../components/SentenceWithFilename';
+import DefaultProjectKey from '../../components/DefaultProjectKey';
 import CreateJenkinsfileBulletPoint from './CreateJenkinsfileBulletPoint';
 
 export interface OtherProps {
   component: T.Component;
 }
 
-const sonarProjectSnippet = (key: string) => `sonar.projectKey=${key}`;
-
 const JENKINSFILE_SNIPPET = `node {
   stage('SCM') {
     checkout scm
@@ -43,13 +40,7 @@ const JENKINSFILE_SNIPPET = `node {
 export default function Other({ component }: OtherProps) {
   return (
     <>
-      <li className="abs-width-600">
-        <SentenceWithFilename
-          filename="sonar-project.properties"
-          translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.other.step2"
-        />
-        <CodeSnippet snippet={sonarProjectSnippet(component.key)} />
-      </li>
+      <DefaultProjectKey component={component} />
       <CreateJenkinsfileBulletPoint
         alertTranslationKeyPart="onboarding.tutorial.with.jenkins.jenkinsfile.other.step3"
         snippet={JENKINSFILE_SNIPPET}
index c5a549388ddbc6690ec25e507027351e87128be1..d77f4bc3a5864305d4340505ac5c62b24179cbfa 100644 (file)
@@ -2,17 +2,30 @@
 
 exports[`should render correctly 1`] = `
 <Fragment>
-  <li
-    className="abs-width-600"
-  >
-    <SentenceWithFilename
-      filename="sonar-project.properties"
-      translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.other.step2"
-    />
-    <CodeSnippet
-      snippet="sonar.projectKey=my-project"
-    />
-  </li>
+  <DefaultProjectKey
+    component={
+      Object {
+        "breadcrumbs": Array [],
+        "key": "my-project",
+        "name": "MyProject",
+        "qualifier": "TRK",
+        "qualityGate": Object {
+          "isDefault": true,
+          "key": "30",
+          "name": "Sonar way",
+        },
+        "qualityProfiles": Array [
+          Object {
+            "deleted": false,
+            "key": "my-qp",
+            "language": "ts",
+            "name": "Sonar way",
+          },
+        ],
+        "tags": Array [],
+      }
+    }
+  />
   <CreateJenkinsfileBulletPoint
     alertTranslationKeyPart="onboarding.tutorial.with.jenkins.jenkinsfile.other.step3"
     snippet="node {
index 5f9ba566b6191b1fe83873723a9b68210e7d6465..ddfe8881ade76b60d75b8933c6bcbd25071e0c7a 100644 (file)
@@ -21,6 +21,7 @@ export enum TutorialModes {
   Manual = 'manual',
   Jenkins = 'jenkins',
   GitLabCI = 'gitlab-ci',
+  GitHubActions = 'github-actions',
   AzurePipelines = 'azure-pipelines'
 }
 
index 825a08e8d2a742b6eac0719e64d5fb7991c60431..463538e36a7a108bd0bb156595ac3fbb95e2607b 100644 (file)
@@ -23,6 +23,24 @@ export function quote(os: string): (s: string) => string {
   return os === 'win' ? (s: string) => `"${s}"` : (s: string) => s;
 }
 
+export function mavenPomSnippet(key: string) {
+  return `<properties>
+  <sonar.projectKey>${key}</sonar.projectKey>
+</properties>`;
+}
+
+export function buildGradleSnippet(key: string) {
+  return `plugins {
+  id "org.sonarqube" version "3.1.1"
+}
+
+sonarqube {
+  properties {
+    property "sonar.projectKey", "${key}"
+  }
+}`;
+}
+
 export function getUniqueTokenName(tokens: T.UserToken[], initialTokenName = '') {
   const hasToken = (name: string) => tokens.find(token => token.name === name) !== undefined;
 
index 35e787a8e898409b3772da5134aded1b28f68f40..8fc86bed269158a6e9b8c582f85d5a6aa1920701 100644 (file)
@@ -3296,6 +3296,10 @@ onboarding.token.use_existing_token.placeholder=Enter your existing token
 onboarding.token.use_existing_token.label=Existing token value
 onboarding.token.invalid_format=The token you have entered has invalid format.
 
+onboarding.tutorial.env_variables=In the {field} field, enter {value} {extra}
+onboarding.tutorial.env_variables.field=Value
+onboarding.tutorial.env_variables.token_generator.value=an existing token, or a newly generated one:
+
 onboarding.analysis.header=Run analysis on your project
 onboarding.analysis.auto_refresh_after_analysis=Once the analysis is completed, this page will automatically refresh and you will be able to browse the analysis results.
 
@@ -3383,13 +3387,37 @@ onboarding.analysis.dotnetcore.global.text=As a prerequisite you need to have th
 onboarding.analysis.dotnetcore.global.text.path=Make sure dotnet tools folder is in your path. See dotnet global tools documentation for more information.
 
 onboarding.tutorial.return_to_list=Choose another option
+onboarding.tutorial.ci_outro.all_set.sentence={all_set} and ready to improve the quality and security of your code!
+onboarding.tutorial.ci_outro.all_set.sentence.all_set=You're all set
+onboarding.tutorial.ci_outro.commit=Commit and push your code to start the analysis.
+onboarding.tutorial.ci_outro.commit.why=Each new push you make on your branches or pull requests will trigger a new analysis in SonarQube.
+onboarding.tutorial.ci_outro.commit.why.no_branches=Each new push you make on your main branch will trigger a new analysis in SonarQube.
+onboarding.tutorial.ci_outro.refresh=This page will then refresh with your analysis results.
+onboarding.tutorial.ci_outro.refresh.why=If the page doesn't refresh after a while, please double-check the analysis configuration.
+onboarding.tutorial.other.project_key.sentence=Create a {file} file in your repository and paste the following code:
 
 onboarding.tutorial.choose_method=How do you want to analyze your repository?
 onboarding.tutorial.choose_method.manual=Manually
 onboarding.tutorial.choose_method.jenkins=With Jenkins
+onboarding.tutorial.choose_method.github_action=With GitHub Actions
 onboarding.tutorial.choose_method.gitlab_ci=With GitLab CI
 onboarding.tutorial.choose_method.azure_pipelines=With Azure Pipelines
 
+onboarding.tutorial.with.github_action.create_secret.title=Create GitHub Secrets
+onboarding.tutorial.with.github_action.secret.intro=In your GitHub repository, go to {settings_secret} and create two new secrets:
+onboarding.tutorial.with.github_action.secret.intro.link=Settings > Secrets
+onboarding.tutorial.with.github_action.secret.name.sentence=In the {name} field, enter
+onboarding.tutorial.with.github_action.secret.name.sentence.name=Name
+onboarding.tutorial.with.github_action.secret.new.sentence=Click on {new_secret}
+onboarding.tutorial.with.github_action.secret.new.sentence.new_secret=New repository secret
+onboarding.tutorial.with.github_action.secret.add.sentence=Click on {add_secret}
+onboarding.tutorial.with.github_action.secret.add.sentence.add_secret=Add secret
+onboarding.tutorial.with.github_action.yaml.title=Create Workflow YAML File
+onboarding.tutorial.with.github_action.yaml.create_yml=Create or update your {file} YAML file with the following content:
+onboarding.tutorial.with.github_action.yaml.maven.pom=Update your {pom} file with the following properties:
+onboarding.tutorial.with.github_action.yaml.gradle=Update your {gradle} file with the {sq} plugin and it's configuration:
+
+
 onboarding.tutorial.with.gitlab_ci.title=Analyze your project with GitLab CI
 onboarding.tutorial.with.gitlab_ci.unsupported=This tutorial is only available for projects bound to GitLab.
 onboarding.tutorial.with.gitlab_ci.project_key.title=Set your project key
@@ -3400,14 +3428,12 @@ onboarding.tutorial.with.gitlab_ci.project_key.dotnet.step2=Create a {file} file
 
 
 onboarding.tutorial.with.gitlab_ci.env_variables.title=Add environment variables
-onboarding.tutorial.with.gitlab_ci.env_variables.enter_field_value=In the {field} field, enter {value} {extra}
 onboarding.tutorial.with.gitlab_ci.env_variables.description.link=Settings > CI/CD > Variables
 onboarding.tutorial.with.gitlab_ci.env_variables.section.title=a. Define the SonarQube Token environment variable
 onboarding.tutorial.with.gitlab_ci.env_variables.section.description=In GitLab, go to {link} to add the following variable and make sure it is available for your project:
 onboarding.tutorial.with.gitlab_ci.env_variables.edit.token.tooltip=Use an existing token or generate a new one.
 onboarding.tutorial.with.gitlab_ci.env_variables.step1=Key
-onboarding.tutorial.with.gitlab_ci.env_variables.step2=Value
-onboarding.tutorial.with.gitlab_ci.env_variables.section.step2.value=an existing token, or a newly generated one:
+
 onboarding.tutorial.with.gitlab_ci.env_variables.step3=Uncheck the "Protect Variable" checkbox
 onboarding.tutorial.with.gitlab_ci.env_variables.section.step4=Check the "Mask Variable" checkbox
 onboarding.tutorial.with.gitlab_ci.env_variables.section2.title=b. Define the SonarQube URL environment variable
@@ -3620,7 +3646,6 @@ onboarding.tutorial.with.jenkins.dotnet.scanner.prereqs.step5.sentence=Under {in
 onboarding.tutorial.with.jenkins.dotnet.scanner.prereqs.step5.sentence.install_from=Install from GitHub
 onboarding.tutorial.with.jenkins.dotnet.scanner.prereqs.step5.sentence.install_auto=Install automatically
 
-onboarding.tutorial.with.jenkins.jenkinsfile.other.step2.sentence=Create a {file} file in your repository and paste the following code:
 onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.replace.sentence=Make sure to replace {default} with the name you gave to your SonarQube Scanner tool {in_jenkins}.
 onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.replace.sentence.default=SonarScanner
 onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.replace.sentence.in_jenkins=in Jenkins
@@ -3629,13 +3654,7 @@ onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.help1.sentence.path=Man
 onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.help2.sentence=The name is located under the {path} section, in the {name} field.
 onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.help2.sentence.path=SonarQube Scanner > SonarQube Scanner installations
 onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.help2.sentence.name=Name
-onboarding.tutorial.with.jenkins.all_set.sentence={all_set} and ready to improve the quality and security of your code!
-onboarding.tutorial.with.jenkins.all_set.sentence.all_set=You're all set
-onboarding.tutorial.with.jenkins.commit=Commit and push your code to start the analysis.
-onboarding.tutorial.with.jenkins.commit.why=Each new push you make on your branches or pull requests will trigger a new analysis in SonarQube.
-onboarding.tutorial.with.jenkins.commit.why.no_branches=Each new push you make on your main branch will trigger a new analysis in SonarQube.
-onboarding.tutorial.with.jenkins.refresh=This page will then refresh with your analysis results.
-onboarding.tutorial.with.jenkins.refresh.why=If the page doesn't refresh after a while, please double-check the analysis configuration.
+
 
 onboarding.tutorial.with.azure_pipelines.os=What is your agent host?
 onboarding.tutorial.with.azure_pipelines.title=Analyze your project with Azure DevOps Pipelines
@@ -3654,7 +3673,7 @@ onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step4.sentence=Enter yo
 onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step5.sentence=Enter an existing token, or a newly generated one
 onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step6.sentence=Create the service connection
 onboarding.tutorial.with.azure_pipelines.BranchAnalysis.title=Configure analysis
-onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info=The following steps assume you are using the Azure Pipelines classic editor. Check out our {doc_link} for the yaml counterpart.
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info=The following steps assume you are using the Azure Pipelines classic editor. Check out our {doc_link} for the YAML counterpart.
 onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info.doc_link=Azure DevOps integration page
 
 onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp.sentence=In Azure DevOps, create or edit a build {pipeline} to make Build Wrapper available on the build agent.