import org.sonar.server.es.SearchIdResult;
import org.sonar.server.es.SearchOptions;
import org.sonar.server.es.metadata.MetadataIndex;
+import org.sonar.server.plugins.DetectPluginChange;
import org.sonar.server.plugins.ServerPluginRepository;
import org.sonar.server.qualityprofile.ActiveRuleChange;
import org.sonar.server.qualityprofile.QProfileRules;
import static java.util.Collections.singletonList;
import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.tuple;
import static org.mockito.ArgumentMatchers.any;
private final StartupRuleUpdater startupRuleUpdater = new StartupRuleUpdater(dbClient, system, uuidFactory, resolver);
private final NewRuleCreator newRuleCreator = new NewRuleCreator(resolver, uuidFactory, system);
private final QualityProfileChangesUpdater qualityProfileChangesUpdater = mock();
+ private final DetectPluginChange detectPluginChange = mock();
@Before
public void before() {
@Test
public void insert_new_rules() {
- execute(new FakeRepositoryV1());
+ executeWithPluginRules(new FakeRepositoryV1());
// verify db
assertThat(dbClient.ruleDao().selectAll(db.getSession())).hasSize(3);
@Test
public void insert_new_external_rule() {
- execute(new ExternalRuleRepository());
+ executeWithPluginRules(new ExternalRuleRepository());
// verify db
assertThat(dbClient.ruleDao().selectAll(db.getSession())).hasSize(2);
String ruleKey = randomAlphanumeric(5);
// register one rule
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
repo.createRule(ruleKey)
.setName(randomAlphanumeric(5))
verifyIndicesMarkedAsInitialized();
// register no rule
- execute(context -> context.createRepository("fake", "java").done());
+ executeWithPluginRules(context -> context.createRepository("fake", "java").done());
// verify db
assertThat(dbClient.ruleDao().selectAll(db.getSession()))
int numberOfRules = 5000;
// register many rules
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
IntStream.range(0, numberOfRules)
.mapToObj(i -> "rule-" + i)
.isNotEmpty();
// register no rule
- execute(context -> context.createRepository("fake", "java").done());
+ executeWithPluginRules(context -> context.createRepository("fake", "java").done());
// verify db
assertThat(dbClient.ruleDao().selectAll(db.getSession()))
db.getDbClient().ruleRepositoryDao().insert(dbSession, singletonList(repository));
dbSession.commit();
- execute(new FakeRepositoryV1());
+ executeWithPluginRules(new FakeRepositoryV1());
assertThat(db.getDbClient().ruleRepositoryDao().selectAll(dbSession)).extracting(RuleRepositoryDto::getKey).containsOnly("fake");
}
@Test
public void update_and_remove_rules_on_changes() {
- execute(new FakeRepositoryV1());
+ executeWithPluginRules(new FakeRepositoryV1());
assertThat(dbClient.ruleDao().selectAll(db.getSession())).hasSize(3);
RuleDto rule1 = dbClient.ruleDao().selectOrFailByKey(db.getSession(), RULE_KEY1);
RuleDto rule2 = dbClient.ruleDao().selectOrFailByKey(db.getSession(), RULE_KEY2);
db.getSession().commit();
system.setNow(DATE2.getTime());
- execute(new FakeRepositoryV2());
+ executeWithPluginRules(new FakeRepositoryV2());
verifyIndicesNotMarkedAsInitialized();
// rule1 has been updated
assertThat(dbClient.ruleRepositoryDao().selectAll(db.getSession())).extracting(RuleRepositoryDto::getKey).containsOnly("fake");
system.setNow(DATE3.getTime());
- execute(new FakeRepositoryV3());
+ executeWithPluginRules(new FakeRepositoryV3());
rule3 = dbClient.ruleDao().selectOrFailByKey(db.getSession(), RULE_KEY3);
assertThat(rule3.getDefaultRuleDescriptionSection().getContent()).isEqualTo("Rule Three V2");
assertThat(rule3.getDescriptionFormat()).isEqualTo(RuleDto.Format.MARKDOWN);
@Test
public void add_new_tag() {
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
repo.createRule("rule1")
.setName("Rule One")
RuleDto rule = dbClient.ruleDao().selectOrFailByKey(db.getSession(), RULE_KEY1);
assertThat(rule.getSystemTags()).containsOnly("tag1");
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
repo.createRule("rule1")
.setName("Rule One")
@Test
public void add_new_security_standards() {
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
repo.createRule("rule1")
.setName("Rule One")
RuleDto rule = dbClient.ruleDao().selectOrFailByKey(db.getSession(), RULE_KEY1);
assertThat(rule.getSecurityStandards()).containsOnly("cwe:123", "owaspTop10-2021:a1");
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
repo.createRule("rule1")
.setName("Rule One")
@Test
public void update_only_rule_name() {
system.setNow(DATE1.getTime());
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
repo.createRule("rule")
.setName("Name1")
});
system.setNow(DATE2.getTime());
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
repo.createRule("rule")
.setName("Name2")
@Test
public void update_template_rule_key_should_also_update_custom_rules() {
system.setNow(DATE1.getTime());
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("squid", "java");
repo.createRule("rule")
.setName("Name1")
db.commit();
// re-key rule
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("java", "java");
repo.createRule("rule")
.setName("Name1")
String repository = "fake";
system.setNow(DATE1.getTime());
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository(repository, "java");
repo.createRule(ruleKey1)
.setName("Name1")
assertThat(searchRule1.getTotal()).isOne();
system.setNow(DATE2.getTime());
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository(repository, "java");
repo.createRule(ruleKey2)
.setName("Name2")
String repository2 = "fake2";
system.setNow(DATE1.getTime());
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository(repository1, "java");
repo.createRule(ruleKey)
.setName("Name1")
assertThat(searchRule1.getTotal()).isOne();
system.setNow(DATE2.getTime());
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository(repository2, "java");
repo.createRule(ruleKey)
.setName("Name2")
String name = "Name1";
String description = "Description";
system.setNow(DATE1.getTime());
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository(repo1, "java");
repo.createRule(ruleKey1)
.setName(name)
.containsOnly(rule1.getUuid());
system.setNow(DATE2.getTime());
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository(repo2, "java");
repo.createRule(ruleKey2)
.setName(name)
String repository2 = "fake2";
system.setNow(DATE1.getTime());
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository(repository1, "java");
repo.createRule(ruleKey1)
.setName("Name1")
.containsOnly(rule1.getUuid());
system.setNow(DATE2.getTime());
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository(repository2, "java");
repo.createRule(ruleKey2)
.setName("Name2")
@Test
public void update_only_rule_description() {
system.setNow(DATE1.getTime());
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
repo.createRule("rule")
.setName("Name")
});
system.setNow(DATE2.getTime());
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
repo.createRule("rule")
.setName("Name")
RuleDescriptionSection section2context2 = createRuleDescriptionSection(RESOURCES_SECTION_KEY,"section2 ctx2 content", "ctx_2");
RuleDescriptionSection section3noContext = createRuleDescriptionSection(ASSESS_THE_PROBLEM_SECTION_KEY, "section3 content", null);
RuleDescriptionSection section4noContext = createRuleDescriptionSection(ROOT_CAUSE_SECTION_KEY, "section4 content", null);
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
repo.createRule("rule")
.setName("Name")
RuleDescriptionSection section4updatedWithContext1 = createRuleDescriptionSection(ROOT_CAUSE_SECTION_KEY, section4noContext.getHtmlContent(), "ctx_1");
RuleDescriptionSection section4updatedWithContext2 = createRuleDescriptionSection(ROOT_CAUSE_SECTION_KEY, section4noContext.getHtmlContent(), "ctx_2");
system.setNow(DATE2.getTime());
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
repo.createRule("rule")
.setName("Name")
public void rule_previously_created_as_adhoc_becomes_none_adhoc() {
RuleDto rule = db.rules().insert(r -> r.setRepositoryKey("external_fake").setIsExternal(true).setIsAdHoc(true));
system.setNow(DATE2.getTime());
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createExternalRepository("fake", rule.getLanguage());
repo.createRule(rule.getRuleKey())
.setName(rule.getName())
.setIsExternal(true)
.setIsAdHoc(false));
- execute();
+ executeWithPluginRules();
RuleDto reloaded = dbClient.ruleDao().selectByKey(db.getSession(), rule.getKey()).get();
assertThat(reloaded.getStatus()).isEqualTo(REMOVED);
.setIsExternal(true)
.setIsAdHoc(true));
- execute();
+ executeWithPluginRules();
RuleDto reloaded = dbClient.ruleDao().selectByKey(db.getSession(), rule.getKey()).get();
assertThat(reloaded.getStatus()).isEqualTo(READY);
public void disable_then_enable_rule() {
// Install rule
system.setNow(DATE1.getTime());
- execute(new FakeRepositoryV1());
+ executeWithPluginRules(new FakeRepositoryV1());
// Uninstall rule
system.setNow(DATE2.getTime());
- execute();
+ executeWithPluginRules();
RuleDto rule = dbClient.ruleDao().selectOrFailByKey(db.getSession(), RULE_KEY1);
assertThat(rule.getStatus()).isEqualTo(REMOVED);
// Re-install rule
system.setNow(DATE3.getTime());
- execute(new FakeRepositoryV1());
+ executeWithPluginRules(new FakeRepositoryV1());
rule = dbClient.ruleDao().selectOrFailByKey(db.getSession(), RULE_KEY1);
assertThat(rule.getStatus()).isEqualTo(RuleStatus.BETA);
@Test
public void do_not_update_rules_when_no_changes() {
- execute(new FakeRepositoryV1());
+ executeWithPluginRules(new FakeRepositoryV1());
assertThat(dbClient.ruleDao().selectAll(db.getSession())).hasSize(3);
system.setNow(DATE2.getTime());
- execute(new FakeRepositoryV1());
+ executeWithPluginRules(new FakeRepositoryV1());
RuleDto rule1 = dbClient.ruleDao().selectOrFailByKey(db.getSession(), RULE_KEY1);
assertThat(rule1.getCreatedAt()).isEqualTo(DATE1.getTime());
@Test
public void do_not_update_already_removed_rules() {
- execute(new FakeRepositoryV1());
+ executeWithPluginRules(new FakeRepositoryV1());
assertThat(dbClient.ruleDao().selectAll(db.getSession())).hasSize(3);
RuleDto rule1 = dbClient.ruleDao().selectOrFailByKey(db.getSession(), RULE_KEY1);
assertThat(rule2.getStatus()).isEqualTo(READY);
system.setNow(DATE2.getTime());
- execute(new FakeRepositoryV2());
+ executeWithPluginRules(new FakeRepositoryV2());
// On MySQL, need to update a rule otherwise rule2 will be seen as READY, but why ???
dbClient.ruleDao().update(db.getSession(), rule1);
assertThat(ruleIndex.search(new RuleQuery(), new SearchOptions()).getUuids()).containsOnly(rule1.getUuid(), rule3.getUuid());
system.setNow(DATE3.getTime());
- execute(new FakeRepositoryV2());
+ executeWithPluginRules(new FakeRepositoryV2());
db.getSession().commit();
// -> rule2 is still removed, but not update at DATE3
@Test
public void mass_insert() {
- execute(new BigRepository());
+ executeWithPluginRules(new BigRepository());
assertThat(db.countRowsOfTable("rules")).isEqualTo(BigRepository.SIZE);
assertThat(db.countRowsOfTable("rules_parameters")).isEqualTo(BigRepository.SIZE * 20);
assertThat(es.getIds(RuleIndexDefinition.TYPE_RULE)).hasSize(BigRepository.SIZE);
@Test
public void manage_repository_extensions() {
- execute(new FindbugsRepository(), new FbContribRepository());
+ executeWithPluginRules(new FindbugsRepository(), new FbContribRepository());
List<RuleDto> rules = dbClient.ruleDao().selectAll(db.getSession());
assertThat(rules).hasSize(2);
for (RuleDto rule : rules) {
db.getSession().commit();
// Synchronize rule without tag
- execute(new FindbugsRepository());
+ executeWithPluginRules(new FindbugsRepository());
List<RuleDto> rules = dbClient.ruleDao().selectAll(db.getSession());
assertThat(rules).hasSize(1).extracting(RuleDto::getKey, RuleDto::getSystemTags)
@Test
public void rules_that_deprecate_previous_rule_must_be_recorded() {
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
createRule(repo, "rule1");
repo.done();
});
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
createRule(repo, "newKey")
.addDeprecatedRuleKey("fake", "rule1")
@Test
public void rules_that_remove_deprecated_key_must_remove_records() {
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
createRule(repo, "rule1");
repo.done();
});
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
createRule(repo, "newKey")
.addDeprecatedRuleKey("fake", "rule1")
Set<DeprecatedRuleKeyDto> deprecatedRuleKeys = dbClient.ruleDao().selectAllDeprecatedRuleKeys(db.getSession());
assertThat(deprecatedRuleKeys).hasSize(2);
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
createRule(repo, "newKey");
repo.done();
@Test
public void declaring_two_rules_with_same_deprecated_RuleKey_should_throw_ISE() {
assertThatThrownBy(() -> {
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
createRule(repo, "newKey1")
.addDeprecatedRuleKey("fake", "old");
@Test
public void declaring_a_rule_with_a_deprecated_RuleKey_still_used_should_throw_ISE() {
assertThatThrownBy(() -> {
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
createRule(repo, "newKey1");
createRule(repo, "newKey2")
@Test
public void updating_the_deprecated_to_a_new_ruleKey_should_throw_an_ISE() {
// On this new rule add a deprecated key
- execute(context -> createRule(context, "javascript", "javascript", "s103",
+ executeWithPluginRules(context -> createRule(context, "javascript", "javascript", "s103",
r -> r.addDeprecatedRuleKey("javascript", "linelength")));
assertThatThrownBy(() -> {
// This rule should have been moved to another repository
- execute(context -> createRule(context, "javascript", "sonarjs", "s103",
+ executeWithPluginRules(context -> createRule(context, "javascript", "sonarjs", "s103",
r -> r.addDeprecatedRuleKey("javascript", "linelength")));
})
.isInstanceOf(IllegalStateException.class)
@Test
public void deprecate_rule_that_deprecated_another_rule() {
- execute(context -> createRule(context, "javascript", "javascript", "s103"));
- execute(context -> createRule(context, "javascript", "javascript", "s104",
+ executeWithPluginRules(context -> createRule(context, "javascript", "javascript", "s103"));
+ executeWithPluginRules(context -> createRule(context, "javascript", "javascript", "s104",
r -> r.addDeprecatedRuleKey("javascript", "s103")));
// This rule should have been moved to another repository
- execute(context -> createRule(context, "javascript", "sonarjs", "s105",
+ executeWithPluginRules(context -> createRule(context, "javascript", "sonarjs", "s105",
r -> r.addDeprecatedRuleKey("javascript", "s103")
.addDeprecatedRuleKey("javascript", "s104")));
}
@Test
public void declaring_a_rule_with_an_existing_RuleKey_still_used_should_throw_IAE() {
assertThatThrownBy(() -> {
- execute(context -> {
+ executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
createRule(repo, "newKey1");
createRule(repo, "newKey1");
ActiveRuleChange arChange = new ActiveRuleChange(DEACTIVATED, ActiveRuleDto.createFor(qProfileDto, ruleDto), ruleDto);
when(qProfileRules.deleteRule(any(DbSession.class), eq(ruleDto))).thenReturn(List.of(arChange));
//WHEN
- execute(context -> context.createRepository("fake", "java").done());
+ executeWithPluginRules(context -> context.createRepository("fake", "java").done());
//THEN
List<QProfileChangeDto> qProfileChangeDtos = dbClient.qProfileChangeDao().selectByQuery(db.getSession(), new QProfileChangeQuery(qProfileDto.getKee()));
assertThat(qProfileChangeDtos).extracting(QProfileChangeDto::getRulesProfileUuid, QProfileChangeDto::getChangeType, QProfileChangeDto::getSqVersion)
RuleDto removedRuleDto = db.rules().insert(RuleKey.of("old_repo", "removed_rule"));
RuleDto renamedRuleDto = db.rules().insert(RuleKey.of("old_repo", "renamed_rule"));
//WHEN
- execute(context -> createRule(context, "java", "new_repo", renamedRuleDto.getRuleKey(),
+ executeWithPluginRules(context -> createRule(context, "java", "new_repo", renamedRuleDto.getRuleKey(),
rule -> rule.addDeprecatedRuleKey(renamedRuleDto.getRepositoryKey(), renamedRuleDto.getRuleKey())));
//THEN
verify(qProfileRules).deleteRule(any(DbSession.class), eq(removedRuleDto));
}
- private void execute(RulesDefinition... defs) {
+ @Test
+ public void builtin_rules_should_be_updated_even_if_no_plugin_updates() {
+ RulesDefinition builtInRepoV1 = context -> createRule(context, "builtin", "sca", "rule1", rule -> rule.setName("Name before"));
+ RulesDefinition pluginRepo = context -> createRule(context, "java", "java", "rule2");
+
+ ServerPluginRepository pluginRepository = mock(ServerPluginRepository.class);
+ when(pluginRepository.getPluginKey(builtInRepoV1)).thenReturn(null);
+ when(pluginRepository.getPluginKey(pluginRepo)).thenReturn(FAKE_PLUGIN_KEY);
+ RuleDefinitionsLoader loader1 = new RuleDefinitionsLoader(pluginRepository, new RulesDefinition[] {builtInRepoV1, pluginRepo});
+ when(detectPluginChange.anyPluginChanged()).thenReturn(true);
+ execute(loader1);
+
+ RuleDto builtInRulev1 = dbClient.ruleDao().selectOrFailByKey(db.getSession(), RuleKey.of("sca", "rule1"));
+ assertThat(builtInRulev1.getName()).isEqualTo("Name before");
+
+ RulesDefinition builtInRepoV2 = context -> createRule(context, "builtin", "sca", "rule1", rule -> rule.setName("Name after"));
+ when(pluginRepository.getPluginKey(builtInRepoV2)).thenReturn(null);
+ RuleDefinitionsLoader loader2 = new RuleDefinitionsLoader(pluginRepository, new RulesDefinition[] {builtInRepoV2, pluginRepo});
+ when(detectPluginChange.anyPluginChanged()).thenReturn(false);
+ execute(loader2);
+
+ RuleDto builtInRulev2 = dbClient.ruleDao().selectOrFailByKey(db.getSession(), RuleKey.of("sca", "rule1"));
+ assertThat(builtInRulev2.getName()).isEqualTo("Name after");
+
+ assertThatCode(() -> dbClient.ruleDao().selectOrFailByKey(db.getSession(), RuleKey.of("java", "rule2"))).doesNotThrowAnyException();
+ }
+
+
+ private void executeWithPluginRules(RulesDefinition... defs) {
ServerPluginRepository pluginRepository = mock(ServerPluginRepository.class);
when(pluginRepository.getPluginKey(any(RulesDefinition.class))).thenReturn(FAKE_PLUGIN_KEY);
RuleDefinitionsLoader loader = new RuleDefinitionsLoader(pluginRepository, defs);
- Languages languages = mock(Languages.class);
- when(languages.get(any())).thenReturn(mock(Language.class));
+ when(detectPluginChange.anyPluginChanged()).thenReturn(true);
+ execute(loader);
+ }
+
+ private void execute(RuleDefinitionsLoader loader) {
reset(webServerRuleFinder);
- RulesRegistrant task = new RulesRegistrant(loader, qProfileRules, dbClient, ruleIndexer, activeRuleIndexer, languages, system, webServerRuleFinder, metadataIndex,
- rulesKeyVerifier, startupRuleUpdater, newRuleCreator, qualityProfileChangesUpdater, sonarQubeVersion);
+ RulesRegistrant task = new RulesRegistrant(loader, qProfileRules, dbClient, ruleIndexer, activeRuleIndexer, system, webServerRuleFinder, metadataIndex,
+ rulesKeyVerifier, startupRuleUpdater, newRuleCreator, qualityProfileChangesUpdater, sonarQubeVersion, detectPluginChange);
task.start();
// Execute a commit to refresh session state as the task is using its own session
db.getSession().commit();
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.sonar.api.Startable;
-import org.sonar.api.resources.Languages;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.server.rule.RulesDefinition;
import org.sonar.db.rule.RuleDto;
import org.sonar.db.rule.RuleRepositoryDto;
import org.sonar.server.es.metadata.MetadataIndex;
+import org.sonar.server.plugins.DetectPluginChange;
import org.sonar.server.qualityprofile.ActiveRuleChange;
import org.sonar.server.qualityprofile.QProfileRules;
import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
private final DbClient dbClient;
private final RuleIndexer ruleIndexer;
private final ActiveRuleIndexer activeRuleIndexer;
- private final Languages languages;
private final System2 system2;
private final WebServerRuleFinder webServerRuleFinder;
private final MetadataIndex metadataIndex;
private final NewRuleCreator newRuleCreator;
private final QualityProfileChangesUpdater qualityProfileChangesUpdater;
private final SonarQubeVersion sonarQubeVersion;
+ private final DetectPluginChange detectPluginChange;
public RulesRegistrant(RuleDefinitionsLoader defLoader, QProfileRules qProfileRules, DbClient dbClient, RuleIndexer ruleIndexer,
- ActiveRuleIndexer activeRuleIndexer, Languages languages, System2 system2, WebServerRuleFinder webServerRuleFinder,
+ ActiveRuleIndexer activeRuleIndexer, System2 system2, WebServerRuleFinder webServerRuleFinder,
MetadataIndex metadataIndex, RulesKeyVerifier rulesKeyVerifier, StartupRuleUpdater startupRuleUpdater,
- NewRuleCreator newRuleCreator, QualityProfileChangesUpdater qualityProfileChangesUpdater, SonarQubeVersion sonarQubeVersion) {
+ NewRuleCreator newRuleCreator, QualityProfileChangesUpdater qualityProfileChangesUpdater, SonarQubeVersion sonarQubeVersion, DetectPluginChange detectPluginChange) {
this.defLoader = defLoader;
this.qProfileRules = qProfileRules;
this.dbClient = dbClient;
this.ruleIndexer = ruleIndexer;
this.activeRuleIndexer = activeRuleIndexer;
- this.languages = languages;
this.system2 = system2;
this.webServerRuleFinder = webServerRuleFinder;
this.metadataIndex = metadataIndex;
this.newRuleCreator = newRuleCreator;
this.qualityProfileChangesUpdater = qualityProfileChangesUpdater;
this.sonarQubeVersion = sonarQubeVersion;
+ this.detectPluginChange = detectPluginChange;
}
@Override
public void start() {
Profiler profiler = Profiler.create(LOG).startInfo("Register rules");
try (DbSession dbSession = dbClient.openSession(true)) {
- List<RulesDefinition.Repository> repositories = defLoader.load().repositories();
- RulesRegistrationContext rulesRegistrationContext = RulesRegistrationContext.create(dbClient, dbSession);
- rulesKeyVerifier.verifyRuleKeyConsistency(repositories, rulesRegistrationContext);
+ var anyPluginChanged = detectPluginChange.anyPluginChanged();
+ RulesRegistrationContext rulesRegistrationContext = RulesRegistrationContext.create(dbClient, dbSession, !anyPluginChanged);
- for (RulesDefinition.ExtendedRepository repoDef : repositories) {
- if (languages.get(repoDef.language()) != null) {
- Set<PluginRuleUpdate> pluginRuleUpdates = registerRules(rulesRegistrationContext, repoDef.rules(), dbSession);
+ List<RulesDefinition.Repository> rulesRepositories = new ArrayList<>(defLoader.loadBuiltIn().repositories());
+ if (anyPluginChanged) {
+ LOG.info("Some plugins have changed, triggering loading of rules from plugins");
+ rulesRepositories.addAll(defLoader.loadFromPlugins().repositories());
+ }
+ rulesKeyVerifier.verifyRuleKeyConsistency(rulesRepositories, rulesRegistrationContext);
+
+ persistRepositories(dbSession, rulesRepositories, anyPluginChanged);
+
+ for (RulesDefinition.Repository repoDef : rulesRepositories) {
+ if (repoDef.language() == null) {
+ throw new IllegalStateException("Language is mandatory for repository " + repoDef.key());
+ }
+ Set<PluginRuleUpdate> pluginRuleUpdates = registerRules(rulesRegistrationContext, repoDef.rules(), dbSession);
+ if (!repoDef.isExternal()) {
+ // External rules are not part of quality profiles
qualityProfileChangesUpdater.createQprofileChangesForRuleUpdates(dbSession, pluginRuleUpdates);
- dbSession.commit();
}
+ dbSession.commit();
}
processRemainingDbRules(rulesRegistrationContext, dbSession);
- List<ActiveRuleChange> changes = removeActiveRulesOnStillExistingRepositories(dbSession, rulesRegistrationContext, repositories);
+ List<ActiveRuleChange> changes = anyPluginChanged ? removeActiveRulesOnStillExistingRepositories(dbSession, rulesRegistrationContext, rulesRepositories) : List.of();
dbSession.commit();
- persistRepositories(dbSession, repositories);
// FIXME lack of resiliency, active rules index is corrupted if rule index fails
// to be updated. Only a single DB commit should be executed.
ruleIndexer.commitAndIndex(dbSession, rulesRegistrationContext.getAllModified().map(RuleDto::getUuid).collect(Collectors.toSet()));
}
}
- private void persistRepositories(DbSession dbSession, List<RulesDefinition.Repository> repositories) {
+ private void persistRepositories(DbSession dbSession, List<RulesDefinition.Repository> repositories, boolean deleteMissing) {
List<String> keys = repositories.stream().map(RulesDefinition.Repository::key).toList();
Set<String> existingKeys = dbClient.ruleRepositoryDao().selectAllKeys(dbSession);
dbClient.ruleRepositoryDao().update(dbSession, dtos.getOrDefault(true, emptyList()));
dbClient.ruleRepositoryDao().insert(dbSession, dtos.getOrDefault(false, emptyList()));
- dbClient.ruleRepositoryDao().deleteIfKeyNotIn(dbSession, keys);
+ if (deleteMissing) {
+ dbClient.ruleRepositoryDao().deleteIfKeyNotIn(dbSession, keys);
+ }
dbSession.commit();
}
* Remove active rules on repositories that still exists.
* <p/>
* For instance, if the javascript repository do not provide anymore some rules, active rules related to this rules will be removed.
- * But if the javascript repository do not exists anymore, then related active rules will not be removed.
+ * But if the javascript repository does not exist anymore, then related active rules will not be removed.
* <p/>
* The side effect of this approach is that extended repositories will not be managed the same way.
- * If an extended repository do not exists anymore, then related active rules will be removed.
+ * If an extended repository does not exist anymore, then related active rules will be removed.
*/
private List<ActiveRuleChange> removeActiveRulesOnStillExistingRepositories(DbSession dbSession, RulesRegistrationContext recorder, List<RulesDefinition.Repository> context) {
Set<String> existingAndRenamedRepositories = getExistingAndRenamedRepositories(recorder, context);