]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-11525 analysis warning on module-lvl props
authorMichal Duda <michal.duda@sonarsource.com>
Thu, 29 Nov 2018 14:37:02 +0000 (15:37 +0100)
committersonartech <sonartech@sonarsource.com>
Wed, 16 Jan 2019 08:43:03 +0000 (09:43 +0100)
Analysis now produces a warning when the property that aggregates all previous module-level properties is not cleared.

12 files changed:
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v75/DbVersion75.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v75/MigrateModuleProperties.java [deleted file]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v76/DbVersion76.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v76/MigrateModuleProperties.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v75/DbVersion75Test.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v75/MigrateModulePropertiesTest.java [deleted file]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v76/DbVersion76Test.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v76/MigrateModulePropertiesTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v75/MigrateModulePropertiesTest/schema.sql [deleted file]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v76/MigrateModulePropertiesTest/schema.sql [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectServerSettingsProvider.java

index fd5f67164709b79ce75de484b0f2156f6ddf57b5..05176b69a7b59aad2a2042de4b1460975a1aa24d 100644 (file)
@@ -31,7 +31,6 @@ public class DbVersion75 implements DbVersion {
       .add(2401, "Create ORGANIZATION_ALM_BINDINGS table", CreateOrganizationsAlmBindingsTable.class)
       .add(2402, "Add column USER_EXTERNAL_ID in ALM_APP_INSTALLS", AddUserExternalIdColumnInAlmAppInstall.class)
       .add(2403, "Set IS_OWNER_USER not nullable in ALM_APP_INSTALLS", SetIsOwnerUserNotNullableInAlmAppInstalls.class)
-      .add(2404, "Add table EVENT_COMPONENT_CHANGES", AddEventComponentChanges.class)
-      .add(2405, "Move module and directory properties to a project level property", MigrateModuleProperties.class);
+      .add(2404, "Add table EVENT_COMPONENT_CHANGES", AddEventComponentChanges.class);
   }
 }
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v75/MigrateModuleProperties.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v75/MigrateModuleProperties.java
deleted file mode 100644 (file)
index 5a7d66c..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.
- */
-package org.sonar.server.platform.db.migration.version.v75;
-
-import java.sql.SQLException;
-import java.util.concurrent.atomic.AtomicReference;
-import org.sonar.api.utils.System2;
-import org.sonar.db.Database;
-import org.sonar.server.platform.db.migration.SupportsBlueGreen;
-import org.sonar.server.platform.db.migration.step.DataChange;
-import org.sonar.server.platform.db.migration.step.MassUpdate;
-import org.sonar.server.platform.db.migration.step.Upsert;
-
-@SupportsBlueGreen
-public class MigrateModuleProperties extends DataChange {
-
-  private static final String NEW_PROPERTY_NAME = "sonar.subprojects.settings.removed";
-
-  private final System2 system2;
-
-  public MigrateModuleProperties(Database db, System2 system2) {
-    super(db);
-    this.system2 = system2;
-  }
-
-  @Override
-  protected void execute(Context context) throws SQLException {
-    long now = system2.now();
-    moveModulePropertiesToProjectLevel(context, now);
-    removeModuleProperties(context);
-  }
-
-  private static void moveModulePropertiesToProjectLevel(Context context, long time) throws SQLException {
-    StringBuilder builder = new StringBuilder();
-    AtomicReference<Integer> currentProjectId = new AtomicReference<>();
-    AtomicReference<String> currentModuleUuid = new AtomicReference<>();
-
-    context.prepareSelect("select prop.prop_key, prop.text_value, prop.clob_value, mod.name, mod.uuid, root.id as project_id, root.name as project_name " +
-      "from properties prop " +
-      "left join projects mod on mod.id = prop.resource_id " +
-      "left join projects root on root.uuid = mod.project_uuid " +
-      "where mod.qualifier = 'BRC'" +
-      "order by root.uuid, mod.uuid, prop.prop_key")
-      .scroll(row -> {
-        String propertyKey = row.getString(1);
-        String propertyTextValue = row.getString(2);
-        String propertyClobValue = row.getString(3);
-        String moduleName = row.getString(4);
-        String moduleUuid = row.getString(5);
-        Integer projectId = row.getInt(6);
-        String projectName = row.getString(7);
-
-        if (!projectId.equals(currentProjectId.get())) {
-          if (currentProjectId.get() != null) {
-            insertProjectProperties(context, currentProjectId.get(), builder.toString(), time);
-          }
-
-          builder.setLength(0);
-          currentProjectId.set(projectId);
-        }
-
-        if (!moduleUuid.equals(currentModuleUuid.get())) {
-          if (currentModuleUuid.get() != null && builder.length() != 0) {
-            builder.append("\n");
-          }
-          builder.append("# previous settings for sub-project ").append(projectName).append("::").append(moduleName).append("\n");
-          currentModuleUuid.set(moduleUuid);
-        }
-
-        String propertyValue = propertyTextValue == null ? propertyClobValue : propertyTextValue;
-        builder.append(propertyKey).append("=").append(propertyValue).append("\n");
-      });
-
-    if (builder.length() > 0) {
-      insertProjectProperties(context, currentProjectId.get(), builder.toString(), time);
-    }
-  }
-
-  private static void insertProjectProperties(Context context, int projectId, String content, long time) throws SQLException {
-    Upsert upsert = context.prepareUpsert("insert into properties (prop_key, resource_id, is_empty, clob_value, created_at) values (?, ?, ?, ?, ?)");
-    upsert.setString(1, NEW_PROPERTY_NAME)
-      .setInt(2, projectId)
-      .setBoolean(3, false)
-      .setString(4, content)
-      .setLong(5, time)
-      .execute()
-      .commit();
-  }
-
-  private static void removeModuleProperties(Context context) throws SQLException {
-    MassUpdate massUpdate = context.prepareMassUpdate().rowPluralName("remove module properties");
-    massUpdate.select("select prop.id as property_id " +
-      "from properties prop " +
-      "left join projects mod on mod.id = prop.resource_id " +
-      "where mod.qualifier = 'BRC'");
-    massUpdate.update("delete from properties where id=?");
-    massUpdate.execute((row, update) -> {
-      update.setInt(1, row.getInt(1));
-      return true;
-    });
-  }
-}
index 235b8af28003fb338c549a65469c5ff70da177cd..0cdfeaf5f99fbc058441f115c35739c4f675dff7 100644 (file)
@@ -29,8 +29,8 @@ public class DbVersion76 implements DbVersion {
     registry
       .add(2500, "Create table USER_PROPERTIES", CreateUserPropertiesTable.class)
       .add(2501, "Add index in table USER_PROPERTIES", AddUniqueIndexInUserPropertiesTable.class)
+      .add(2502, "Move module and directory properties to a project level property", MigrateModuleProperties.class)
       .add(2505, "Fix the direction values of certain metrics (prepare for migration of conditions)", FixDirectionOfMetrics.class)
-      .add(2506, "Migrate quality gate conditions using warning, period and no more supported operations", MigrateNoMoreUsedQualityGateConditions.class)
-    ;
+      .add(2506, "Migrate quality gate conditions using warning, period and no more supported operations", MigrateNoMoreUsedQualityGateConditions.class);
   }
 }
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v76/MigrateModuleProperties.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v76/MigrateModuleProperties.java
new file mode 100644 (file)
index 0000000..fbdf1e5
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+package org.sonar.server.platform.db.migration.version.v76;
+
+import java.sql.SQLException;
+import java.util.concurrent.atomic.AtomicReference;
+import org.sonar.api.utils.System2;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.SupportsBlueGreen;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.MassUpdate;
+import org.sonar.server.platform.db.migration.step.Upsert;
+
+@SupportsBlueGreen
+public class MigrateModuleProperties extends DataChange {
+
+  protected static final String NEW_PROPERTY_NAME = "sonar.modules.archivedSettings";
+
+  private final System2 system2;
+
+  public MigrateModuleProperties(Database db, System2 system2) {
+    super(db);
+    this.system2 = system2;
+  }
+
+  @Override
+  protected void execute(Context context) throws SQLException {
+    long now = system2.now();
+    moveModulePropertiesToProjectLevel(context, now);
+    removeModuleProperties(context);
+  }
+
+  private static void moveModulePropertiesToProjectLevel(Context context, long time) throws SQLException {
+    StringBuilder builder = new StringBuilder();
+    AtomicReference<Integer> currentProjectId = new AtomicReference<>();
+    AtomicReference<String> currentModuleUuid = new AtomicReference<>();
+
+    context.prepareSelect("select prop.prop_key, prop.text_value, prop.clob_value, mod.name, mod.uuid, root.id as project_id, root.name as project_name " +
+      "from properties prop " +
+      "left join projects mod on mod.id = prop.resource_id " +
+      "left join projects root on root.uuid = mod.project_uuid " +
+      "where mod.qualifier = 'BRC'" +
+      "order by root.uuid, mod.uuid, prop.prop_key")
+      .scroll(row -> {
+        String propertyKey = row.getString(1);
+        String propertyTextValue = row.getString(2);
+        String propertyClobValue = row.getString(3);
+        String moduleName = row.getString(4);
+        String moduleUuid = row.getString(5);
+        Integer projectId = row.getInt(6);
+        String projectName = row.getString(7);
+
+        if (!projectId.equals(currentProjectId.get())) {
+          if (currentProjectId.get() != null) {
+            insertProjectProperties(context, currentProjectId.get(), builder.toString(), time);
+          }
+
+          builder.setLength(0);
+          currentProjectId.set(projectId);
+        }
+
+        if (!moduleUuid.equals(currentModuleUuid.get())) {
+          if (currentModuleUuid.get() != null && builder.length() != 0) {
+            builder.append("\n");
+          }
+          builder.append("# previous settings for sub-project ").append(projectName).append("::").append(moduleName).append("\n");
+          currentModuleUuid.set(moduleUuid);
+        }
+
+        String propertyValue = propertyTextValue == null ? propertyClobValue : propertyTextValue;
+        builder.append(propertyKey).append("=").append(propertyValue).append("\n");
+      });
+
+    if (builder.length() > 0) {
+      insertProjectProperties(context, currentProjectId.get(), builder.toString(), time);
+    }
+  }
+
+  private static void insertProjectProperties(Context context, int projectId, String content, long time) throws SQLException {
+    Upsert upsert = context.prepareUpsert("insert into properties (prop_key, resource_id, is_empty, clob_value, created_at) values (?, ?, ?, ?, ?)");
+    upsert.setString(1, NEW_PROPERTY_NAME)
+      .setInt(2, projectId)
+      .setBoolean(3, false)
+      .setString(4, content)
+      .setLong(5, time)
+      .execute()
+      .commit();
+  }
+
+  private static void removeModuleProperties(Context context) throws SQLException {
+    MassUpdate massUpdate = context.prepareMassUpdate().rowPluralName("remove module properties");
+    massUpdate.select("select prop.id as property_id " +
+      "from properties prop " +
+      "left join projects mod on mod.id = prop.resource_id " +
+      "where mod.qualifier = 'BRC'");
+    massUpdate.update("delete from properties where id=?");
+    massUpdate.execute((row, update) -> {
+      update.setInt(1, row.getInt(1));
+      return true;
+    });
+  }
+}
index 1d8a5afbba60c90526133c41b67a506231dc4cfe..6a0eee289e263a2fe767e6f66467363f8a284cf7 100644 (file)
@@ -35,6 +35,6 @@ public class DbVersion75Test {
 
   @Test
   public void verify_migration_count() {
-    verifyMigrationCount(underTest, 6);
+    verifyMigrationCount(underTest, 5);
   }
 }
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v75/MigrateModulePropertiesTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v75/MigrateModulePropertiesTest.java
deleted file mode 100644 (file)
index 52330c7..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.
- */
-package org.sonar.server.platform.db.migration.version.v75;
-
-import java.sql.SQLException;
-import java.util.List;
-import java.util.Map;
-import javax.annotation.Nullable;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.resources.Qualifiers;
-import org.sonar.api.resources.Scopes;
-import org.sonar.api.utils.System2;
-import org.sonar.api.utils.internal.TestSystem2;
-import org.sonar.core.util.SequenceUuidFactory;
-import org.sonar.core.util.UuidFactory;
-import org.sonar.db.CoreDbTester;
-
-import static java.lang.String.valueOf;
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class MigrateModulePropertiesTest {
-  private final static long NOW = 50_000_000_000L;
-
-  @Rule
-  public CoreDbTester db = CoreDbTester.createForSchema(MigrateModulePropertiesTest.class, "schema.sql");
-  private System2 system2 = new TestSystem2().setNow(NOW);
-  private UuidFactory uuidFactory = new SequenceUuidFactory();
-  private MigrateModuleProperties underTest = new MigrateModuleProperties(db.database(), system2);
-
-  @Test
-  public void multi_module_project_migration() throws SQLException {
-    setupMultiModuleProject();
-
-    underTest.execute();
-
-    verifyMultiModuleProjectMigration();
-  }
-
-  @Test
-  public void migration_is_reentrant() throws SQLException {
-    setupMultiModuleProject();
-
-    underTest.execute();
-    underTest.execute();
-
-    verifyMultiModuleProjectMigration();
-    List<Map<String, Object>> remainingProperties = db.select("select ID from properties");
-    assertThat(remainingProperties).hasSize(3);
-  }
-
-  @Test
-  public void single_module_project_migration() throws SQLException {
-    String project2Uuid = uuidFactory.create();
-    insertComponent(5, project2Uuid, null, project2Uuid, Qualifiers.PROJECT, "Single module project");
-    insertProperty(9, 5, "sonar.coverage.exclusions", "SingleModuleA.java");
-    insertProperty(10, 5, "sonar.cp.exclusions", "SingleModuleB.java");
-
-    underTest.execute();
-
-    List<Map<String, Object>> properties = db.select("select ID, TEXT_VALUE, CLOB_VALUE " +
-      "from properties " +
-      "where PROP_KEY='sonar.subprojects.settings.removed' and RESOURCE_ID = 5");
-    assertThat(properties).hasSize(0);
-    List<Map<String, Object>> remainingProperties = db.select("select ID from properties");
-    assertThat(remainingProperties).hasSize(2);
-  }
-
-  @Test
-  public void properties_do_not_leak_between_projects() throws SQLException {
-    setupMultiModuleProject();
-    setupSecondMultiModuleProject();
-
-    underTest.execute();
-
-    verifyMultiModuleProjectMigration();
-    verifySecondMultiModuleProjectMigration();
-  }
-
-  private void insertComponent(long id, String uuid, @Nullable String rootUuid, String projectUuid, String qualifier, String name) {
-    db.executeInsert(
-      "projects",
-      "ID", valueOf(id),
-      "UUID", uuid,
-      "ROOT_UUID", rootUuid,
-      "PROJECT_UUID", projectUuid,
-      "SCOPE", Scopes.PROJECT,
-      "QUALIFIER", qualifier,
-      "NAME", name);
-  }
-
-  private void insertProperty(long id, long componentId, String key, String value) {
-    db.executeInsert(
-      "properties",
-      "ID", valueOf(id),
-      "RESOURCE_ID", componentId,
-      "PROP_KEY", key,
-      "TEXT_VALUE", value,
-      "IS_EMPTY", false);
-  }
-
-  private void setupMultiModuleProject() {
-    String projectUuid = uuidFactory.create();
-    String moduleUuid = uuidFactory.create();
-    String subModule1Uuid = uuidFactory.create();
-    String subModule2Uuid = uuidFactory.create();
-    insertComponent(1, projectUuid, null, projectUuid, Qualifiers.PROJECT, "Multi-module project");
-    insertComponent(2, moduleUuid, projectUuid, projectUuid, Qualifiers.MODULE, "Module");
-    insertComponent(3, subModule1Uuid, moduleUuid, projectUuid, Qualifiers.MODULE, "Submodule 1");
-    insertComponent(4, subModule2Uuid, moduleUuid, projectUuid, Qualifiers.MODULE, "Submodule 2");
-    insertProperty(1, 1, "sonar.coverage.exclusions", "Proj1.java");
-    insertProperty(2, 1, "sonar.cpd.exclusions", "Proj2.java");
-    insertProperty(3, 2, "sonar.coverage.exclusions", "ModuleA.java");
-    insertProperty(4, 2, "sonar.cpd.exclusions", "ModuleB.java");
-    insertProperty(5, 3, "sonar.coverage.exclusions", "Module1A.java");
-    insertProperty(6, 3, "sonar.cpd.exclusions", "Moddule1B.java");
-    insertProperty(7, 4, "sonar.coverage.exclusions", "Module2A.java");
-    insertProperty(8, 4, "sonar.cpd.exclusions", "Module2B.java");
-  }
-
-  private void verifyMultiModuleProjectMigration() {
-    final String expectedResult = "# previous settings for sub-project Multi-module project::Module\n" +
-      "sonar.coverage.exclusions=ModuleA.java\n" +
-      "sonar.cpd.exclusions=ModuleB.java\n" +
-      "\n" +
-      "# previous settings for sub-project Multi-module project::Submodule 1\n" +
-      "sonar.coverage.exclusions=Module1A.java\n" +
-      "sonar.cpd.exclusions=Moddule1B.java\n" +
-      "\n" +
-      "# previous settings for sub-project Multi-module project::Submodule 2\n" +
-      "sonar.coverage.exclusions=Module2A.java\n" +
-      "sonar.cpd.exclusions=Module2B.java\n";
-
-    List<Map<String, Object>> properties = db.select("select ID, TEXT_VALUE, CLOB_VALUE " +
-      "from properties " +
-      "where PROP_KEY='sonar.subprojects.settings.removed' and RESOURCE_ID = 1");
-
-    assertThat(properties).hasSize(1);
-    Map<String, Object> project1Property = properties.get(0);
-    assertThat(project1Property.get("TEXT_VALUE")).isNull();
-    assertThat(project1Property.get("CLOB_VALUE")).isEqualTo(expectedResult);
-  }
-
-  private void setupSecondMultiModuleProject() {
-    String project3Uuid = uuidFactory.create();
-    String singleModuleUuid = uuidFactory.create();
-    insertComponent(6, project3Uuid, null, project3Uuid, Qualifiers.PROJECT, "Another multi-module project");
-    insertComponent(7, singleModuleUuid, project3Uuid, project3Uuid, Qualifiers.MODULE, "Module X");
-    insertProperty(11, 6, "sonar.coverage.exclusions", "InRoot.java");
-    insertProperty(12, 7, "sonar.coverage.exclusions", "InModule.java");
-  }
-
-  private void verifySecondMultiModuleProjectMigration() {
-    final String expectedResult = "# previous settings for sub-project Another multi-module project::Module X\n" +
-      "sonar.coverage.exclusions=InModule.java\n";
-
-    List<Map<String, Object>> properties = db.select("select ID, TEXT_VALUE, CLOB_VALUE " +
-      "from properties " +
-      "where PROP_KEY='sonar.subprojects.settings.removed' and RESOURCE_ID = 6");
-
-    assertThat(properties).hasSize(1);
-    Map<String, Object> project2Property = properties.get(0);
-    assertThat(project2Property.get("TEXT_VALUE")).isNull();
-    assertThat(project2Property.get("CLOB_VALUE")).isEqualTo(expectedResult);
-  }
-}
index bec86f8565a20bf00c53169e0ca722168f59d13d..595ce34382ed8aebe3be5b99004ed86e63ac5055 100644 (file)
@@ -35,7 +35,7 @@ public class DbVersion76Test {
 
   @Test
   public void verify_migration_count() {
-    verifyMigrationCount(underTest, 4);
+    verifyMigrationCount(underTest, 5);
   }
 
 }
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v76/MigrateModulePropertiesTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v76/MigrateModulePropertiesTest.java
new file mode 100644 (file)
index 0000000..4692e49
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+package org.sonar.server.platform.db.migration.version.v76;
+
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.resources.Scopes;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.internal.TestSystem2;
+import org.sonar.core.util.SequenceUuidFactory;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.db.CoreDbTester;
+
+import static java.lang.String.valueOf;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.platform.db.migration.version.v76.MigrateModuleProperties.NEW_PROPERTY_NAME;
+
+public class MigrateModulePropertiesTest {
+  private static final long NOW = 50_000_000_000L;
+
+  @Rule
+  public CoreDbTester db = CoreDbTester.createForSchema(MigrateModulePropertiesTest.class, "schema.sql");
+  private System2 system2 = new TestSystem2().setNow(NOW);
+  private UuidFactory uuidFactory = new SequenceUuidFactory();
+  private MigrateModuleProperties underTest = new MigrateModuleProperties(db.database(), system2);
+
+  @Test
+  public void multi_module_project_migration() throws SQLException {
+    setupMultiModuleProject();
+
+    underTest.execute();
+
+    verifyMultiModuleProjectMigration();
+  }
+
+  @Test
+  public void migration_is_reentrant() throws SQLException {
+    setupMultiModuleProject();
+
+    underTest.execute();
+    underTest.execute();
+
+    verifyMultiModuleProjectMigration();
+    List<Map<String, Object>> remainingProperties = db.select("select ID from properties");
+    assertThat(remainingProperties).hasSize(3);
+  }
+
+  @Test
+  public void single_module_project_migration() throws SQLException {
+    String project2Uuid = uuidFactory.create();
+    insertComponent(5, project2Uuid, null, project2Uuid, Qualifiers.PROJECT, "Single module project");
+    insertProperty(9, 5, "sonar.coverage.exclusions", "SingleModuleA.java");
+    insertProperty(10, 5, "sonar.cp.exclusions", "SingleModuleB.java");
+
+    underTest.execute();
+
+    List<Map<String, Object>> properties = db.select(String.format("select ID, TEXT_VALUE, CLOB_VALUE " +
+      "from properties " +
+      "where PROP_KEY='%s' and RESOURCE_ID = 5", NEW_PROPERTY_NAME));
+    assertThat(properties).hasSize(0);
+    List<Map<String, Object>> remainingProperties = db.select("select ID from properties");
+    assertThat(remainingProperties).hasSize(2);
+  }
+
+  @Test
+  public void properties_do_not_leak_between_projects() throws SQLException {
+    setupMultiModuleProject();
+    setupSecondMultiModuleProject();
+
+    underTest.execute();
+
+    verifyMultiModuleProjectMigration();
+    verifySecondMultiModuleProjectMigration();
+  }
+
+  private void insertComponent(long id, String uuid, @Nullable String rootUuid, String projectUuid, String qualifier, String name) {
+    db.executeInsert(
+      "projects",
+      "ID", valueOf(id),
+      "UUID", uuid,
+      "ROOT_UUID", rootUuid,
+      "PROJECT_UUID", projectUuid,
+      "SCOPE", Scopes.PROJECT,
+      "QUALIFIER", qualifier,
+      "NAME", name);
+  }
+
+  private void insertProperty(long id, long componentId, String key, String value) {
+    db.executeInsert(
+      "properties",
+      "ID", valueOf(id),
+      "RESOURCE_ID", componentId,
+      "PROP_KEY", key,
+      "TEXT_VALUE", value,
+      "IS_EMPTY", false);
+  }
+
+  private void setupMultiModuleProject() {
+    String projectUuid = uuidFactory.create();
+    String moduleUuid = uuidFactory.create();
+    String subModule1Uuid = uuidFactory.create();
+    String subModule2Uuid = uuidFactory.create();
+    insertComponent(1, projectUuid, null, projectUuid, Qualifiers.PROJECT, "Multi-module project");
+    insertComponent(2, moduleUuid, projectUuid, projectUuid, Qualifiers.MODULE, "Module");
+    insertComponent(3, subModule1Uuid, moduleUuid, projectUuid, Qualifiers.MODULE, "Submodule 1");
+    insertComponent(4, subModule2Uuid, moduleUuid, projectUuid, Qualifiers.MODULE, "Submodule 2");
+    insertProperty(1, 1, "sonar.coverage.exclusions", "Proj1.java");
+    insertProperty(2, 1, "sonar.cpd.exclusions", "Proj2.java");
+    insertProperty(3, 2, "sonar.coverage.exclusions", "ModuleA.java");
+    insertProperty(4, 2, "sonar.cpd.exclusions", "ModuleB.java");
+    insertProperty(5, 3, "sonar.coverage.exclusions", "Module1A.java");
+    insertProperty(6, 3, "sonar.cpd.exclusions", "Moddule1B.java");
+    insertProperty(7, 4, "sonar.coverage.exclusions", "Module2A.java");
+    insertProperty(8, 4, "sonar.cpd.exclusions", "Module2B.java");
+  }
+
+  private void verifyMultiModuleProjectMigration() {
+    final String expectedResult = "# previous settings for sub-project Multi-module project::Module\n" +
+      "sonar.coverage.exclusions=ModuleA.java\n" +
+      "sonar.cpd.exclusions=ModuleB.java\n" +
+      "\n" +
+      "# previous settings for sub-project Multi-module project::Submodule 1\n" +
+      "sonar.coverage.exclusions=Module1A.java\n" +
+      "sonar.cpd.exclusions=Moddule1B.java\n" +
+      "\n" +
+      "# previous settings for sub-project Multi-module project::Submodule 2\n" +
+      "sonar.coverage.exclusions=Module2A.java\n" +
+      "sonar.cpd.exclusions=Module2B.java\n";
+
+    List<Map<String, Object>> properties = db
+      .select(String.format("select ID, TEXT_VALUE, CLOB_VALUE from properties where PROP_KEY='%s' and RESOURCE_ID = 1",
+        NEW_PROPERTY_NAME));
+
+    assertThat(properties).hasSize(1);
+    Map<String, Object> project1Property = properties.get(0);
+    assertThat(project1Property.get("TEXT_VALUE")).isNull();
+    assertThat(project1Property.get("CLOB_VALUE")).isEqualTo(expectedResult);
+  }
+
+  private void setupSecondMultiModuleProject() {
+    String project3Uuid = uuidFactory.create();
+    String singleModuleUuid = uuidFactory.create();
+    insertComponent(6, project3Uuid, null, project3Uuid, Qualifiers.PROJECT, "Another multi-module project");
+    insertComponent(7, singleModuleUuid, project3Uuid, project3Uuid, Qualifiers.MODULE, "Module X");
+    insertProperty(11, 6, "sonar.coverage.exclusions", "InRoot.java");
+    insertProperty(12, 7, "sonar.coverage.exclusions", "InModule.java");
+  }
+
+  private void verifySecondMultiModuleProjectMigration() {
+    final String expectedResult = "# previous settings for sub-project Another multi-module project::Module X\n" +
+      "sonar.coverage.exclusions=InModule.java\n";
+
+    List<Map<String, Object>> properties = db.select(String.format("select ID, TEXT_VALUE, CLOB_VALUE " +
+      "from properties " +
+      "where PROP_KEY='%s' and RESOURCE_ID = 6", NEW_PROPERTY_NAME));
+
+    assertThat(properties).hasSize(1);
+    Map<String, Object> project2Property = properties.get(0);
+    assertThat(project2Property.get("TEXT_VALUE")).isNull();
+    assertThat(project2Property.get("CLOB_VALUE")).isEqualTo(expectedResult);
+  }
+}
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v75/MigrateModulePropertiesTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v75/MigrateModulePropertiesTest/schema.sql
deleted file mode 100644 (file)
index 1cbb2b7..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-CREATE TABLE "PROJECTS" (
-  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
-  "KEE" VARCHAR(400),
-  "UUID" VARCHAR(50) NOT NULL,
-  "ROOT_UUID" VARCHAR(50),
-  "PROJECT_UUID" VARCHAR(50) NOT NULL,
-  "MODULE_UUID" VARCHAR(50),
-  "MODULE_UUID_PATH" VARCHAR(1500),
-  "MAIN_BRANCH_PROJECT_UUID" VARCHAR(50),
-  "NAME" VARCHAR(2000),
-  "TAGS" VARCHAR(500),
-  "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
-  "SCOPE" VARCHAR(3),
-  "QUALIFIER" VARCHAR(10)
-);
-CREATE UNIQUE INDEX "PROJECTS_KEE" ON "PROJECTS" ("KEE");
-CREATE INDEX "PROJECTS_ROOT_UUID" ON "PROJECTS" ("ROOT_UUID");
-CREATE UNIQUE INDEX "PROJECTS_UUID" ON "PROJECTS" ("UUID");
-CREATE INDEX "PROJECTS_PROJECT_UUID" ON "PROJECTS" ("PROJECT_UUID");
-CREATE INDEX "PROJECTS_MODULE_UUID" ON "PROJECTS" ("MODULE_UUID");
-CREATE INDEX "PROJECTS_QUALIFIER" ON "PROJECTS" ("QUALIFIER");
-
-CREATE TABLE "PROPERTIES" (
-  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
-  "PROP_KEY" VARCHAR(512) NOT NULL,
-  "RESOURCE_ID" INTEGER,
-  "USER_ID" INTEGER,
-  "TEXT_VALUE" VARCHAR(4000),
-  "CLOB_VALUE" CLOB(2147483647),
-  "CREATED_AT" BIGINT,
-  "IS_EMPTY" BOOLEAN NOT NULL,
-);
-CREATE INDEX "PROPERTIES_KEY" ON "PROPERTIES" ("PROP_KEY");
-
-
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v76/MigrateModulePropertiesTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v76/MigrateModulePropertiesTest/schema.sql
new file mode 100644 (file)
index 0000000..1cbb2b7
--- /dev/null
@@ -0,0 +1,35 @@
+CREATE TABLE "PROJECTS" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "KEE" VARCHAR(400),
+  "UUID" VARCHAR(50) NOT NULL,
+  "ROOT_UUID" VARCHAR(50),
+  "PROJECT_UUID" VARCHAR(50) NOT NULL,
+  "MODULE_UUID" VARCHAR(50),
+  "MODULE_UUID_PATH" VARCHAR(1500),
+  "MAIN_BRANCH_PROJECT_UUID" VARCHAR(50),
+  "NAME" VARCHAR(2000),
+  "TAGS" VARCHAR(500),
+  "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+  "SCOPE" VARCHAR(3),
+  "QUALIFIER" VARCHAR(10)
+);
+CREATE UNIQUE INDEX "PROJECTS_KEE" ON "PROJECTS" ("KEE");
+CREATE INDEX "PROJECTS_ROOT_UUID" ON "PROJECTS" ("ROOT_UUID");
+CREATE UNIQUE INDEX "PROJECTS_UUID" ON "PROJECTS" ("UUID");
+CREATE INDEX "PROJECTS_PROJECT_UUID" ON "PROJECTS" ("PROJECT_UUID");
+CREATE INDEX "PROJECTS_MODULE_UUID" ON "PROJECTS" ("MODULE_UUID");
+CREATE INDEX "PROJECTS_QUALIFIER" ON "PROJECTS" ("QUALIFIER");
+
+CREATE TABLE "PROPERTIES" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "PROP_KEY" VARCHAR(512) NOT NULL,
+  "RESOURCE_ID" INTEGER,
+  "USER_ID" INTEGER,
+  "TEXT_VALUE" VARCHAR(4000),
+  "CLOB_VALUE" CLOB(2147483647),
+  "CREATED_AT" BIGINT,
+  "IS_EMPTY" BOOLEAN NOT NULL,
+);
+CREATE INDEX "PROPERTIES_KEY" ON "PROPERTIES" ("PROP_KEY");
+
+
index 5bbb4974373a6a4e27313778e61e2fdedb6677f0..e8066776c78e60a13247d05129459fa371d8dd6e 100644 (file)
@@ -79,7 +79,6 @@ public interface CoreProperties {
    */
   String CATEGORY_EXTERNAL_ISSUES = "externalIssues";
 
-  
   /**
    * @since 2.11
    */
@@ -366,7 +365,7 @@ public interface CoreProperties {
   /**
    * @since 3.6
    */
-  //TODO remove?
+  // TODO remove?
   String PROFILING_LOG_PROPERTY = "sonar.showProfiling";
 
   /**
@@ -527,4 +526,9 @@ public interface CoreProperties {
    * @since 5.1
    */
   String DEFAULT_ISSUE_ASSIGNEE = "sonar.issues.defaultAssigneeLogin";
+
+  /**
+   * @since 7.6
+   */
+  String MODULE_LEVEL_ARCHIVED_SETTINGS = "sonar.modules.archivedSettings";
 }
index 2a85f416445c079d98761ca57168acc3d910500e..88f20ae9772f0172418737d6a505ba4fd11a95ab 100644 (file)
 package org.sonar.scanner.scan;
 
 import java.util.Map;
+import org.apache.commons.lang.StringUtils;
 import org.picocontainer.injectors.ProviderAdapter;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.notifications.AnalysisWarnings;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
 import org.sonar.scanner.bootstrap.ScannerProperties;
 import org.sonar.scanner.repository.settings.SettingsLoader;
 
 public class ProjectServerSettingsProvider extends ProviderAdapter {
 
-  private ProjectServerSettings singleton;
+  private static final Logger LOG = Loggers.get(ProjectConfigurationProvider.class);
 
-  public ProjectServerSettings provide(SettingsLoader loader, ScannerProperties scannerProperties) {
+  private static final String MODULE_LEVEL_ARCHIVED_SETTINGS_WARNING = "Please migrate all the properties listed in " +
+    "`sonar.module.removedProperties` setting to appriopriate project level setting.";
+
+  private ProjectServerSettings singleton = null;
+
+  public ProjectServerSettings provide(SettingsLoader loader, ScannerProperties scannerProperties, AnalysisWarnings analysisWarnings) {
     if (singleton == null) {
       Map<String, String> serverSideSettings = loader.load(scannerProperties.getKeyWithBranch());
+      if (StringUtils.isNotBlank(serverSideSettings.get(CoreProperties.MODULE_LEVEL_ARCHIVED_SETTINGS))) {
+        LOG.warn(MODULE_LEVEL_ARCHIVED_SETTINGS_WARNING);
+        analysisWarnings.addUnique(MODULE_LEVEL_ARCHIVED_SETTINGS_WARNING);
+      }
       singleton = new ProjectServerSettings(serverSideSettings);
     }
     return singleton;