]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-19850 Apply validation fixes
authorlukasz-jarocki-sonarsource <lukasz.jarocki@sonarsource.com>
Fri, 14 Jul 2023 10:39:17 +0000 (12:39 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 19 Jul 2023 20:03:06 +0000 (20:03 +0000)
SONAR-19850 Fix an issue where main branch was treated as orphan branch
SONAR-19850 Fix an issue where emails with reports were not sent to any receipients
SONAR-19850 Fix bug with reference branch as main branch
SONAR-19850 Add warning on unanalyzed branches
SONAR-19850 Fix portfolio refresh when change of main branch of project

12 files changed:
server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/taskprocessor/IgnoreOrphanBranchStepIT.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/taskprocessor/IgnoreOrphanBranchStep.java
server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx
server/sonar-web/src/main/js/apps/projectBranches/components/SetAsMainBranchModal.tsx
server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListener.java
server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListeners.java
server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListenersImpl.java
server/sonar-webserver-api/src/test/java/org/sonar/server/project/ProjectLifeCycleListenersImplTest.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/branch/ws/DeleteActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/branch/ws/SetMainBranchActionIT.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/branch/ws/DeleteAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/branch/ws/SetMainBranchAction.java

index 495476db7ff4fab24987e072cc23e21666f14f3c..7e8b3f56927f75080a1795329d94788e2e63691f 100644 (file)
@@ -27,6 +27,10 @@ import org.sonar.ce.task.CeTask;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbTester;
 import org.sonar.db.component.BranchDto;
+import org.sonar.db.component.ComponentDbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.project.ProjectDto;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -104,6 +108,51 @@ public class IgnoreOrphanBranchStepIT {
       .hasMessage("entity not found in task");
   }
 
+  @Test
+  public void execute_givenMainBranchWithDifferentUuidFromProject_shouldNotIncludeItInPurge() {
+    BranchDto branchDto = new BranchDto()
+      .setBranchType(BRANCH)
+      .setKey("main")
+      .setUuid(BRANCH_UUID)
+      .setProjectUuid(ENTITY_UUID)
+      .setIsMain(true)
+      .setNeedIssueSync(true)
+      .setExcludeFromPurge(true);
+    dbClient.branchDao().insert(dbTester.getSession(), branchDto);
+
+    ProjectDto projectDto = ComponentTesting.newProjectDto().setUuid(ENTITY_UUID).setKey("component key");
+    ComponentDto componentDto = ComponentTesting.newBranchComponent(projectDto, branchDto);
+    dbClient.componentDao().insert(dbTester.getSession(), componentDto, false);
+    dbClient.projectDao().insert(dbTester.getSession(), projectDto, false);
+    dbTester.commit();
+
+    underTest.execute(() -> null);
+
+    Optional<BranchDto> branch = dbClient.branchDao().selectByUuid(dbTester.getSession(), BRANCH_UUID);
+    assertThat(branch.get().isNeedIssueSync()).isTrue();
+    assertThat(branch.get().isExcludeFromPurge()).isTrue();
+  }
+
+  @Test
+  public void execute_givenNotExistingEntityUuid_shouldIncludeItInPurge() {
+    BranchDto branchDto = new BranchDto()
+      .setBranchType(BRANCH)
+      .setKey("main")
+      .setUuid(BRANCH_UUID)
+      .setProjectUuid(ENTITY_UUID)
+      .setIsMain(false)
+      .setNeedIssueSync(true)
+      .setExcludeFromPurge(true);
+    dbClient.branchDao().insert(dbTester.getSession(), branchDto);
+    dbTester.commit();
+
+    underTest.execute(() -> null);
+
+    Optional<BranchDto> branch = dbClient.branchDao().selectByUuid(dbTester.getSession(), BRANCH_UUID);
+    assertThat(branch.get().isNeedIssueSync()).isFalse();
+    assertThat(branch.get().isExcludeFromPurge()).isFalse();
+  }
+
   @Test
   public void verify_step_description() {
     assertThat(underTest.getDescription()).isEqualTo("Ignore orphan component");
index c40fee64e5e4f50ec2a0f10fc6da0b4fdb24efea..eb672bdfb6728adbfbd15b1ba82b96fd9613ce82 100644 (file)
@@ -27,6 +27,7 @@ import org.sonar.ce.task.step.ComputationStep;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.ComponentDto;
+import org.sonar.db.entity.EntityDto;
 
 public final class IgnoreOrphanBranchStep implements ComputationStep {
   private static final Logger LOG = LoggerFactory.getLogger(IgnoreOrphanBranchStep.class);
@@ -44,8 +45,9 @@ public final class IgnoreOrphanBranchStep implements ComputationStep {
     String componentUuid = ceTask.getComponent().orElseThrow(() -> new UnsupportedOperationException("component not found in task")).getUuid();
 
     try (DbSession dbSession = dbClient.openSession(false)) {
-      Optional<ComponentDto> componentDto = dbClient.componentDao().selectByUuid(dbSession, entityUuid);
-      if(!componentDto.isPresent()){
+      Optional<ComponentDto> componentDto = dbClient.componentDao().selectByUuid(dbSession, componentUuid);
+      Optional<EntityDto> entityDto = dbClient.entityDao().selectByUuid(dbSession, entityUuid);
+      if (componentDto.isEmpty() || entityDto.isEmpty()) {
         LOG.info("reindexation task has been trigger on an orphan branch. removing any exclude_from_purge flag, and skip the indexation");
         dbClient.branchDao().updateExcludeFromPurge(dbSession, componentUuid, false);
         dbClient.branchDao().updateNeedIssueSync(dbSession, componentUuid, false);
index 367048e6b7fc84e7695fa9b62150474346511c61..6da4abfc6ae2dc696a6d9063cb49cb2f176de90b 100644 (file)
@@ -58,11 +58,10 @@ export function EmptyOverview(props: EmptyOverviewProps) {
     branchLikes.length > 2 ||
     (branchLikes.length === 2 && branchLikes.some((branch) => isBranch(branch)));
 
-  const showWarning = isMainBranch(branchLike) && hasBranches;
   const showTutorial = isMainBranch(branchLike) && !hasBranches && !hasAnalyses;
 
   let warning;
-  if (isLoggedIn(currentUser) && showWarning && hasBadBranchConfig) {
+  if (isLoggedIn(currentUser) && isMainBranch(branchLike) && hasBranches && hasBadBranchConfig) {
     warning = translateWithParameters(
       'provisioning.no_analysis_on_main_branch.bad_configuration',
       getBranchLikeDisplayName(branchLike),
@@ -79,7 +78,7 @@ export function EmptyOverview(props: EmptyOverviewProps) {
     <div className="page page-limited">
       {isLoggedIn(currentUser) ? (
         <>
-          {showWarning && (
+          {hasBranches && (
             <Alert variant="warning" aria-label={warning}>
               {warning}
             </Alert>
index de7c44a323f64bf8b2a9a2e6db9d2fd864a60624..1f5445bc23e3fac264b8334d5bd4b845310d5d7a 100644 (file)
@@ -43,10 +43,11 @@ export default function SetAsMainBranchModal(props: SetAsMainBranchModalProps) {
 
   return (
     <Modal
-      headerTitle={translateWithParameters(
-        'project_branch_pull_request.branch.set_x_as_main',
-        branch.name
-      )}
+      headerTitle={
+        <span className="sw-break-all">
+          {translateWithParameters('project_branch_pull_request.branch.set_x_as_main', branch.name)}
+        </span>
+      }
       loading={isLoading}
       onClose={onClose}
       body={
index 4f89a015ee77d535863861cb9718547fce97df9e..683706c93122c3ac7ee04216d2223f4ac8f373f2 100644 (file)
@@ -32,7 +32,7 @@ public interface ProjectLifeCycleListener {
   /**
    * This method is called after the specified projects have branches deleted or main branch changed.
    */
-  void onProjectBranchesChanged(Set<Project> projects);
+  void onProjectBranchesChanged(Set<Project> projects, Set<String> impactedBranches);
 
   /**
    * This method is called after the specified projects' keys have been modified.
index 981237903e39d674b220fa933230f1ce6bebfb21..a14e3eab4c2b4e7faa1e983b3275c988baf2cae4 100644 (file)
@@ -34,13 +34,13 @@ public interface ProjectLifeCycleListeners {
 
   /**
    * This method is called after the specified project have any king of change (branch deleted, change of main branch, ...)
-   *  This method will call method {@link ProjectLifeCycleListener#onProjectBranchesChanged(Set)} of all known
+   *  This method will call method {@link ProjectLifeCycleListener#onProjectBranchesChanged(Set,Set)} of all known
    * {@link ProjectLifeCycleListener} implementations.
    * <p>
    * This method ensures all {@link ProjectLifeCycleListener} implementations are called, even if one or more of
    * them fail with an exception.
    */
-  void onProjectBranchesChanged(Set<Project> projects);
+  void onProjectBranchesChanged(Set<Project> projects, Set<String> impactedBranches);
 
   /**
    * This method is called after the specified project's key has been changed and will call method
index 9d98d0f80afb50964ed7283f3daa3d160f68aa7c..5f3ce502be309fedcaa4722144eb674ddc5cb316 100644 (file)
@@ -61,14 +61,14 @@ public class ProjectLifeCycleListenersImpl implements ProjectLifeCycleListeners
   }
 
   @Override
-  public void onProjectBranchesChanged(Set<Project> projects) {
+  public void onProjectBranchesChanged(Set<Project> projects, Set<String> impactedBranches) {
     checkNotNull(projects, "projects can't be null");
     if (projects.isEmpty()) {
       return;
     }
 
     Arrays.stream(listeners)
-      .forEach(safelyCallListener(listener -> listener.onProjectBranchesChanged(projects)));
+      .forEach(safelyCallListener(listener -> listener.onProjectBranchesChanged(projects, impactedBranches)));
   }
 
   @Override
index 600a387656463e0a91df0f15dac86101d0675b80..a12bce9b353a08d5cbdd33243ee34aa2e759f9f6 100644 (file)
@@ -32,12 +32,15 @@ import org.junit.runner.RunWith;
 import org.mockito.InOrder;
 import org.mockito.Mockito;
 
+import static java.util.Collections.emptySet;
 import static java.util.Collections.singleton;
 import static org.assertj.core.api.Assertions.assertThatCode;
 import static org.assertj.core.api.Assertions.assertThatNoException;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anySet;
 import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verifyNoInteractions;
@@ -128,30 +131,30 @@ public class ProjectLifeCycleListenersImplTest {
 
   @Test
   public void onProjectBranchesChanged_throws_NPE_if_set_is_null() {
-    assertThatThrownBy(() -> underTestWithListeners.onProjectBranchesChanged(null))
+    assertThatThrownBy(() -> underTestWithListeners.onProjectBranchesChanged(null, null))
       .isInstanceOf(NullPointerException.class)
       .hasMessage("projects can't be null");
   }
 
   @Test
   public void onProjectBranchesChanged_throws_NPE_if_set_is_null_even_if_no_listeners() {
-    assertThatThrownBy(() -> underTestNoListeners.onProjectBranchesChanged(null))
+    assertThatThrownBy(() -> underTestNoListeners.onProjectBranchesChanged(null, null))
       .isInstanceOf(NullPointerException.class)
       .hasMessage("projects can't be null");
   }
 
   @Test
   public void onProjectBranchesChanged_has_no_effect_if_set_is_empty() {
-    underTestNoListeners.onProjectBranchesChanged(Collections.emptySet());
+    underTestNoListeners.onProjectBranchesChanged(Collections.emptySet(), emptySet());
 
-    underTestWithListeners.onProjectBranchesChanged(Collections.emptySet());
+    underTestWithListeners.onProjectBranchesChanged(Collections.emptySet(), emptySet());
     verifyNoInteractions(listener1, listener2, listener3);
   }
 
   @Test
   @UseDataProvider("oneOrManyProjects")
   public void onProjectBranchesChanged_does_not_fail_if_there_is_no_listener(Set<Project> projects) {
-     assertThatNoException().isThrownBy(()-> underTestNoListeners.onProjectBranchesChanged(projects));
+     assertThatNoException().isThrownBy(()-> underTestNoListeners.onProjectBranchesChanged(projects, emptySet()));
   }
 
   @Test
@@ -159,11 +162,11 @@ public class ProjectLifeCycleListenersImplTest {
   public void onProjectBranchesChanged_calls_all_listeners_in_order_of_addition_to_constructor(Set<Project> projects) {
     InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
 
-    underTestWithListeners.onProjectBranchesChanged(projects);
+    underTestWithListeners.onProjectBranchesChanged(projects, emptySet());
 
-    inOrder.verify(listener1).onProjectBranchesChanged(same(projects));
-    inOrder.verify(listener2).onProjectBranchesChanged(same(projects));
-    inOrder.verify(listener3).onProjectBranchesChanged(same(projects));
+    inOrder.verify(listener1).onProjectBranchesChanged(same(projects), eq(emptySet()));
+    inOrder.verify(listener2).onProjectBranchesChanged(same(projects), eq(emptySet()));
+    inOrder.verify(listener3).onProjectBranchesChanged(same(projects), eq(emptySet()));
     inOrder.verifyNoMoreInteractions();
   }
 
@@ -173,13 +176,13 @@ public class ProjectLifeCycleListenersImplTest {
     InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
     doThrow(new RuntimeException("Faking listener2 throwing an exception"))
       .when(listener2)
-      .onProjectBranchesChanged(any());
+      .onProjectBranchesChanged(any(), anySet());
 
-    underTestWithListeners.onProjectBranchesChanged(projects);
+    underTestWithListeners.onProjectBranchesChanged(projects, emptySet());
 
-    inOrder.verify(listener1).onProjectBranchesChanged(same(projects));
-    inOrder.verify(listener2).onProjectBranchesChanged(same(projects));
-    inOrder.verify(listener3).onProjectBranchesChanged(same(projects));
+    inOrder.verify(listener1).onProjectBranchesChanged(same(projects), eq(emptySet()));
+    inOrder.verify(listener2).onProjectBranchesChanged(same(projects), eq(emptySet()));
+    inOrder.verify(listener3).onProjectBranchesChanged(same(projects), eq(emptySet()));
     inOrder.verifyNoMoreInteractions();
   }
 
@@ -189,13 +192,13 @@ public class ProjectLifeCycleListenersImplTest {
     InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
     doThrow(new Error("Faking listener2 throwing an Error"))
       .when(listener2)
-      .onProjectBranchesChanged(any());
+      .onProjectBranchesChanged(any(), anySet());
 
-    underTestWithListeners.onProjectBranchesChanged(projects);
+    underTestWithListeners.onProjectBranchesChanged(projects, emptySet());
 
-    inOrder.verify(listener1).onProjectBranchesChanged(same(projects));
-    inOrder.verify(listener2).onProjectBranchesChanged(same(projects));
-    inOrder.verify(listener3).onProjectBranchesChanged(same(projects));
+    inOrder.verify(listener1).onProjectBranchesChanged(same(projects), eq(emptySet()));
+    inOrder.verify(listener2).onProjectBranchesChanged(same(projects), eq(emptySet()));
+    inOrder.verify(listener3).onProjectBranchesChanged(same(projects), eq(emptySet()));
     inOrder.verifyNoMoreInteractions();
   }
 
index 45b7a464bf846997513c6c9cbde25736c7978283..6f3b39b5e52ce03d8250ceabd59a8aca47ab07b4 100644 (file)
@@ -39,6 +39,7 @@ import org.sonar.server.project.ProjectLifeCycleListeners;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.WsActionTester;
 
+import static java.util.Collections.emptySet;
 import static java.util.Collections.singleton;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -72,7 +73,7 @@ public class DeleteActionIT {
       .execute();
 
     verifyDeletedKey("branch1");
-    verify(projectLifeCycleListeners).onProjectBranchesChanged(singleton(Project.fromProjectDtoWithTags(project)));
+    verify(projectLifeCycleListeners).onProjectBranchesChanged(singleton(Project.fromProjectDtoWithTags(project)), emptySet());
   }
 
   @Test
index a6ad1c0de566ccdc3ac7ec5223762aba575303eb..7acaf2f7fd3f6b257270ab45f900891a23e579cb 100644 (file)
@@ -219,7 +219,7 @@ public class SetMainBranchActionIT {
       .setParam(PARAM_PROJECT, projectData.projectKey())
       .setParam(PARAM_BRANCH, newMainBranch.getKey()).execute();
 
-    checkCallToProjectLifeCycleListenersOnProjectBranchesChanges(projectData.getProjectDto());
+    checkCallToProjectLifeCycleListenersOnProjectBranchesChanges(projectData.getProjectDto(), projectData.getMainBranchDto().getUuid());
     verify(indexers).commitAndIndexBranches(any(), eq(List.of(projectData.getMainBranchDto(), newMainBranch)), eq(Indexers.BranchEvent.SWITCH_OF_MAIN_BRANCH));
     checkNewMainBranch(projectData.projectUuid(), newMainBranch.getUuid());
     checkPreviousMainBranch(projectData);
@@ -228,9 +228,9 @@ public class SetMainBranchActionIT {
         .formatted(projectData.projectKey(), newMainBranch.getKey(), projectData.getMainBranchDto().getKey()));
   }
 
-  private void checkCallToProjectLifeCycleListenersOnProjectBranchesChanges(ProjectDto projectDto) {
+  private void checkCallToProjectLifeCycleListenersOnProjectBranchesChanges(ProjectDto projectDto, String oldMainBranchUuid) {
     Project project = Project.from(projectDto);
-    verify(projectLifeCycleListeners).onProjectBranchesChanged(Set.of(project));
+    verify(projectLifeCycleListeners).onProjectBranchesChanged(Set.of(project), Set.of(oldMainBranchUuid) );
   }
 
   private void checkNewMainBranch(String projectUuid, String newBranchUuid) {
index 8c1d14e85f9719a600478144279fddf0f80acba6..efacedf5f1f5b7d45eab75689a72363d47e58cb7 100644 (file)
@@ -34,6 +34,7 @@ import org.sonar.server.project.Project;
 import org.sonar.server.project.ProjectLifeCycleListeners;
 import org.sonar.server.user.UserSession;
 
+import static java.util.Collections.emptySet;
 import static java.util.Collections.singleton;
 import static org.sonar.server.branch.ws.BranchesWs.addBranchParam;
 import static org.sonar.server.branch.ws.BranchesWs.addProjectParam;
@@ -86,7 +87,7 @@ public class DeleteAction implements BranchWsAction {
         "Branch '%s' not found for project '%s'", branchKey, projectKey);
 
       componentCleanerService.deleteBranch(dbSession, branch);
-      projectLifeCycleListeners.onProjectBranchesChanged(singleton(Project.fromProjectDtoWithTags(project)));
+      projectLifeCycleListeners.onProjectBranchesChanged(singleton(Project.fromProjectDtoWithTags(project)), emptySet());
       response.noContent();
     }
   }
index 8f62233136c18d60c28153523c7b81d1f5592123..05f4a2f457528836fe8ed466ac934aa73b5e04db 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.server.branch.ws;
 
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.server.ws.Request;
@@ -97,7 +98,7 @@ public class SetMainBranchAction implements BranchWsAction {
         return;
       }
       configureProjectWithNewMainBranch(dbSession, projectDto.getKey(), oldMainBranch, newMainBranch);
-      refreshApplicationsAndPortfoliosComputedByProject(projectDto);
+      refreshApplicationsAndPortfoliosComputedByProject(projectDto, Set.of(oldMainBranch.getUuid()));
       indexers.commitAndIndexBranches(dbSession, List.of(oldMainBranch, newMainBranch), Indexers.BranchEvent.SWITCH_OF_MAIN_BRANCH);
 
       dbSession.commit();
@@ -121,8 +122,8 @@ public class SetMainBranchAction implements BranchWsAction {
     return false;
   }
 
-  private void refreshApplicationsAndPortfoliosComputedByProject(ProjectDto projectDto) {
-    projectLifeCycleListeners.onProjectBranchesChanged(singleton(Project.from(projectDto)));
+  private void refreshApplicationsAndPortfoliosComputedByProject(ProjectDto projectDto, Set<String> impactedBranchesUuids) {
+    projectLifeCycleListeners.onProjectBranchesChanged(singleton(Project.from(projectDto)), impactedBranchesUuids);
   }
 
   private void updateNewMainBranch(DbSession dbSession, BranchDto newMainBranch) {