Merge changes I46de6f2a,I47e93d69
* changes:
Document that changes can become stale in the index on config changes
Handle invalid values for boolean config parameters gracefully
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/config/CodeOwnersPluginConfiguration.java b/java/com/google/gerrit/plugins/codeowners/backend/config/CodeOwnersPluginConfiguration.java
index 8b9407f..a9b5ea6 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/config/CodeOwnersPluginConfiguration.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/config/CodeOwnersPluginConfiguration.java
@@ -114,7 +114,7 @@
*/
public boolean areCodeOwnerConfigsReadOnly(Project.NameKey project) {
requireNonNull(project, "project");
- return generalConfig.getReadOnly(getPluginConfig(project));
+ return generalConfig.getReadOnly(project, getPluginConfig(project));
}
/**
@@ -126,7 +126,7 @@
*/
public boolean arePureRevertsExempted(Project.NameKey project) {
requireNonNull(project, "project");
- return generalConfig.getExemptPureReverts(getPluginConfig(project));
+ return generalConfig.getExemptPureReverts(project, getPluginConfig(project));
}
/**
@@ -140,7 +140,7 @@
*/
public boolean rejectNonResolvableCodeOwners(Project.NameKey project) {
requireNonNull(project, "project");
- return generalConfig.getRejectNonResolvableCodeOwners(getPluginConfig(project));
+ return generalConfig.getRejectNonResolvableCodeOwners(project, getPluginConfig(project));
}
/**
@@ -154,7 +154,7 @@
*/
public boolean rejectNonResolvableImports(Project.NameKey project) {
requireNonNull(project, "project");
- return generalConfig.getRejectNonResolvableImports(getPluginConfig(project));
+ return generalConfig.getRejectNonResolvableImports(project, getPluginConfig(project));
}
/**
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/config/GeneralConfig.java b/java/com/google/gerrit/plugins/codeowners/backend/config/GeneralConfig.java
index 75b3637..28c8456 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/config/GeneralConfig.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/config/GeneralConfig.java
@@ -202,11 +202,12 @@
* <p>The read-only configuration controls whether code owner config files are read-only and all
* modifications of code owner config files should be rejected.
*
+ * @param project the project for which the read-only configuration should be read
* @param pluginConfig the plugin config from which the read-only configuration should be read.
* @return whether code owner config files are read-only
*/
- boolean getReadOnly(Config pluginConfig) {
- return getBooleanConfig(pluginConfig, KEY_READ_ONLY, /* defaultValue= */ false);
+ boolean getReadOnly(Project.NameKey project, Config pluginConfig) {
+ return getBooleanConfig(project, pluginConfig, KEY_READ_ONLY, /* defaultValue= */ false);
}
/**
@@ -216,11 +217,13 @@
* <p>The exempt-pure-reverts configuration controls whether pure revert changes are exempted from
* needing code owner approvals for submit.
*
+ * @param project the project for which the exempt-pure-revert configuration should be read
* @param pluginConfig the plugin config from which the read-only configuration should be read.
* @return whether pure reverts are exempted from needing code owner approvals for submit
*/
- boolean getExemptPureReverts(Config pluginConfig) {
- return getBooleanConfig(pluginConfig, KEY_EXEMPT_PURE_REVERTS, /* defaultValue= */ false);
+ boolean getExemptPureReverts(Project.NameKey project, Config pluginConfig) {
+ return getBooleanConfig(
+ project, pluginConfig, KEY_EXEMPT_PURE_REVERTS, /* defaultValue= */ false);
}
/**
@@ -231,14 +234,16 @@
* with newly added non-resolvable code owners should be rejected on commit received and on
* submit.
*
+ * @param project the project for which the freject-non-resolvable-code-owners configuration
+ * should be read
* @param pluginConfig the plugin config from which the reject-non-resolvable-code-owners
* configuration should be read.
* @return whether code owner config files with newly added non-resolvable code owners should be
* rejected on commit received and on submit
*/
- boolean getRejectNonResolvableCodeOwners(Config pluginConfig) {
+ boolean getRejectNonResolvableCodeOwners(Project.NameKey project, Config pluginConfig) {
return getBooleanConfig(
- pluginConfig, KEY_REJECT_NON_RESOLVABLE_CODE_OWNERS, /* defaultValue= */ true);
+ project, pluginConfig, KEY_REJECT_NON_RESOLVABLE_CODE_OWNERS, /* defaultValue= */ true);
}
/**
@@ -248,26 +253,46 @@
* <p>The reject-non-resolvable-imports configuration controls whether code owner config files
* with newly added non-resolvable imports should be rejected on commit received and on submit.
*
+ * @param project the project for which the freject-non-resolvable-imports configuration should be
+ * read
* @param pluginConfig the plugin config from which the reject-non-resolvable-imports
* configuration should be read.
* @return whether code owner config files with newly added non-resolvable imports should be
* rejected on commit received and on submit
*/
- boolean getRejectNonResolvableImports(Config pluginConfig) {
+ boolean getRejectNonResolvableImports(Project.NameKey project, Config pluginConfig) {
return getBooleanConfig(
- pluginConfig, KEY_REJECT_NON_RESOLVABLE_IMPORTS, /* defaultValue= */ true);
+ project, pluginConfig, KEY_REJECT_NON_RESOLVABLE_IMPORTS, /* defaultValue= */ true);
}
- private boolean getBooleanConfig(Config pluginConfig, String key, boolean defaultValue) {
+ private boolean getBooleanConfig(
+ Project.NameKey project, Config pluginConfig, String key, boolean defaultValue) {
+ requireNonNull(project, "project");
requireNonNull(pluginConfig, "pluginConfig");
requireNonNull(key, "key");
- if (pluginConfig.getString(SECTION_CODE_OWNERS, /* subsection= */ null, key) != null) {
- return pluginConfig.getBoolean(
- SECTION_CODE_OWNERS, /* subsection= */ null, key, defaultValue);
+ String value = pluginConfig.getString(SECTION_CODE_OWNERS, /* subsection= */ null, key);
+ if (value != null) {
+ try {
+ return pluginConfig.getBoolean(
+ SECTION_CODE_OWNERS, /* subsection= */ null, key, defaultValue);
+ } catch (IllegalArgumentException e) {
+ logger.atWarning().withCause(e).log(
+ "Ignoring invalid value %s for %s in '%s.config' of project %s."
+ + " Falling back to global config.",
+ value, key, pluginName, project.get());
+ }
}
- return pluginConfigFromGerritConfig.getBoolean(key, defaultValue);
+ try {
+ return pluginConfigFromGerritConfig.getBoolean(key, defaultValue);
+ } catch (IllegalArgumentException e) {
+ logger.atWarning().withCause(e).log(
+ "Ignoring invalid value %s for %s in gerrit.config (parameter"
+ + " plugin.%s.%s). Falling back to default value %s.",
+ pluginConfigFromGerritConfig.getString(key), key, pluginName, key, defaultValue);
+ return defaultValue;
+ }
}
/**
diff --git a/javatests/com/google/gerrit/plugins/codeowners/backend/config/GeneralConfigTest.java b/javatests/com/google/gerrit/plugins/codeowners/backend/config/GeneralConfigTest.java
index 4e43414..b5716ee 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/backend/config/GeneralConfigTest.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/backend/config/GeneralConfigTest.java
@@ -107,23 +107,33 @@
}
@Test
+ public void cannotGetReadOnlyForNullProject() throws Exception {
+ NullPointerException npe =
+ assertThrows(
+ NullPointerException.class,
+ () -> generalConfig.getReadOnly(/* project= */ null, new Config()));
+ assertThat(npe).hasMessageThat().isEqualTo("project");
+ }
+
+ @Test
public void cannotGetReadOnlyForNullPluginConfig() throws Exception {
NullPointerException npe =
assertThrows(
- NullPointerException.class, () -> generalConfig.getReadOnly(/* pluginConfig= */ null));
+ NullPointerException.class,
+ () -> generalConfig.getReadOnly(project, /* pluginConfig= */ null));
assertThat(npe).hasMessageThat().isEqualTo("pluginConfig");
}
@Test
public void noReadOnlyConfiguration() throws Exception {
- assertThat(generalConfig.getReadOnly(new Config())).isFalse();
+ assertThat(generalConfig.getReadOnly(project, new Config())).isFalse();
}
@Test
@GerritConfig(name = "plugin.code-owners.readOnly", value = "true")
public void readOnlyConfigurationIsRetrievedFromGerritConfigIfNotSpecifiedOnProjectLevel()
throws Exception {
- assertThat(generalConfig.getReadOnly(new Config())).isTrue();
+ assertThat(generalConfig.getReadOnly(project, new Config())).isTrue();
}
@Test
@@ -132,7 +142,30 @@
throws Exception {
Config cfg = new Config();
cfg.setString(SECTION_CODE_OWNERS, /* subsection= */ null, KEY_READ_ONLY, "false");
- assertThat(generalConfig.getReadOnly(cfg)).isFalse();
+ assertThat(generalConfig.getReadOnly(project, cfg)).isFalse();
+ }
+
+ @Test
+ @GerritConfig(name = "plugin.code-owners.readOnly", value = "true")
+ public void invalidReadOnlyConfigurationInPluginConfigIsIgnored() throws Exception {
+ Config cfg = new Config();
+ cfg.setString(SECTION_CODE_OWNERS, /* subsection= */ null, KEY_READ_ONLY, "INVALID");
+ assertThat(generalConfig.getReadOnly(project, cfg)).isTrue();
+ }
+
+ @Test
+ @GerritConfig(name = "plugin.code-owners.readOnly", value = "INVALID")
+ public void invalidReadOnlyConfigurationInGerritConfigIsIgnored() throws Exception {
+ assertThat(generalConfig.getReadOnly(project, new Config())).isFalse();
+ }
+
+ @Test
+ public void cannotGetExemptPureRevertsForNullProject() throws Exception {
+ NullPointerException npe =
+ assertThrows(
+ NullPointerException.class,
+ () -> generalConfig.getExemptPureReverts(/* project= */ null, new Config()));
+ assertThat(npe).hasMessageThat().isEqualTo("project");
}
@Test
@@ -140,13 +173,13 @@
NullPointerException npe =
assertThrows(
NullPointerException.class,
- () -> generalConfig.getExemptPureReverts(/* pluginConfig= */ null));
+ () -> generalConfig.getExemptPureReverts(project, /* pluginConfig= */ null));
assertThat(npe).hasMessageThat().isEqualTo("pluginConfig");
}
@Test
public void noExemptPureRevertsConfiguration() throws Exception {
- assertThat(generalConfig.getExemptPureReverts(new Config())).isFalse();
+ assertThat(generalConfig.getExemptPureReverts(project, new Config())).isFalse();
}
@Test
@@ -154,7 +187,7 @@
public void
exemptPureRevertsConfigurationIsRetrievedFromGerritConfigIfNotSpecifiedOnProjectLevel()
throws Exception {
- assertThat(generalConfig.getExemptPureReverts(new Config())).isTrue();
+ assertThat(generalConfig.getExemptPureReverts(project, new Config())).isTrue();
}
@Test
@@ -164,7 +197,31 @@
throws Exception {
Config cfg = new Config();
cfg.setString(SECTION_CODE_OWNERS, null, KEY_EXEMPT_PURE_REVERTS, "false");
- assertThat(generalConfig.getExemptPureReverts(cfg)).isFalse();
+ assertThat(generalConfig.getExemptPureReverts(project, cfg)).isFalse();
+ }
+
+ @Test
+ @GerritConfig(name = "plugin.code-owners.exemptPureReverts", value = "true")
+ public void invalidExemptPureRevertsInPluginConfigIsIgnored() throws Exception {
+ Config cfg = new Config();
+ cfg.setString(SECTION_CODE_OWNERS, /* subsection= */ null, KEY_EXEMPT_PURE_REVERTS, "INVALID");
+ assertThat(generalConfig.getExemptPureReverts(project, cfg)).isTrue();
+ }
+
+ @Test
+ @GerritConfig(name = "plugin.code-owners.exemptPureReverts", value = "INVALID")
+ public void invalidExemptPureRevertsConfigurationInGerritConfigIsIgnored() throws Exception {
+ assertThat(generalConfig.getExemptPureReverts(project, new Config())).isFalse();
+ }
+
+ @Test
+ public void cannotGetRejectNonResolvableCodeOwnersForNullProject() throws Exception {
+ NullPointerException npe =
+ assertThrows(
+ NullPointerException.class,
+ () ->
+ generalConfig.getRejectNonResolvableCodeOwners(/* project= */ null, new Config()));
+ assertThat(npe).hasMessageThat().isEqualTo("project");
}
@Test
@@ -172,13 +229,14 @@
NullPointerException npe =
assertThrows(
NullPointerException.class,
- () -> generalConfig.getRejectNonResolvableCodeOwners(/* pluginConfig= */ null));
+ () ->
+ generalConfig.getRejectNonResolvableCodeOwners(project, /* pluginConfig= */ null));
assertThat(npe).hasMessageThat().isEqualTo("pluginConfig");
}
@Test
public void noRejectNonResolvableCodeOwnersConfiguration() throws Exception {
- assertThat(generalConfig.getRejectNonResolvableCodeOwners(new Config())).isTrue();
+ assertThat(generalConfig.getRejectNonResolvableCodeOwners(project, new Config())).isTrue();
}
@Test
@@ -186,7 +244,7 @@
public void
rejectNonResolvableCodeOwnersConfigurationIsRetrievedFromGerritConfigIfNotSpecifiedOnProjectLevel()
throws Exception {
- assertThat(generalConfig.getRejectNonResolvableCodeOwners(new Config())).isFalse();
+ assertThat(generalConfig.getRejectNonResolvableCodeOwners(project, new Config())).isFalse();
}
@Test
@@ -197,7 +255,35 @@
Config cfg = new Config();
cfg.setString(
SECTION_CODE_OWNERS, /* subsection= */ null, KEY_REJECT_NON_RESOLVABLE_CODE_OWNERS, "true");
- assertThat(generalConfig.getRejectNonResolvableCodeOwners(cfg)).isTrue();
+ assertThat(generalConfig.getRejectNonResolvableCodeOwners(project, cfg)).isTrue();
+ }
+
+ @Test
+ @GerritConfig(name = "plugin.code-owners.rejectNonResolvableCodeOwners", value = "false")
+ public void invalidRejectNonResolvableCodeOwnersInPluginConfigIsIgnored() throws Exception {
+ Config cfg = new Config();
+ cfg.setString(
+ SECTION_CODE_OWNERS,
+ /* subsection= */ null,
+ KEY_REJECT_NON_RESOLVABLE_CODE_OWNERS,
+ "INVALID");
+ assertThat(generalConfig.getRejectNonResolvableCodeOwners(project, cfg)).isFalse();
+ }
+
+ @Test
+ @GerritConfig(name = "plugin.code-owners.rejectNonResolvableCodeOwners", value = "INVALID")
+ public void invalidRejectNonResolvableCodeOwnersConfigurationInGerritConfigIsIgnored()
+ throws Exception {
+ assertThat(generalConfig.getRejectNonResolvableCodeOwners(project, new Config())).isTrue();
+ }
+
+ @Test
+ public void cannotGetRejectNonResolvableImportsForNullProject() throws Exception {
+ NullPointerException npe =
+ assertThrows(
+ NullPointerException.class,
+ () -> generalConfig.getRejectNonResolvableImports(/* project= */ null, new Config()));
+ assertThat(npe).hasMessageThat().isEqualTo("project");
}
@Test
@@ -205,13 +291,13 @@
NullPointerException npe =
assertThrows(
NullPointerException.class,
- () -> generalConfig.getRejectNonResolvableImports(/* pluginConfig= */ null));
+ () -> generalConfig.getRejectNonResolvableImports(project, /* pluginConfig= */ null));
assertThat(npe).hasMessageThat().isEqualTo("pluginConfig");
}
@Test
public void noRejectNonResolvableImportsConfiguration() throws Exception {
- assertThat(generalConfig.getRejectNonResolvableImports(new Config())).isTrue();
+ assertThat(generalConfig.getRejectNonResolvableImports(project, new Config())).isTrue();
}
@Test
@@ -219,7 +305,7 @@
public void
rejectNonResolvableImportsConfigurationIsRetrievedFromGerritConfigIfNotSpecifiedOnProjectLevel()
throws Exception {
- assertThat(generalConfig.getRejectNonResolvableImports(new Config())).isFalse();
+ assertThat(generalConfig.getRejectNonResolvableImports(project, new Config())).isFalse();
}
@Test
@@ -230,7 +316,23 @@
Config cfg = new Config();
cfg.setString(
SECTION_CODE_OWNERS, /* scubsection= */ null, KEY_REJECT_NON_RESOLVABLE_IMPORTS, "true");
- assertThat(generalConfig.getRejectNonResolvableImports(cfg)).isTrue();
+ assertThat(generalConfig.getRejectNonResolvableImports(project, cfg)).isTrue();
+ }
+
+ @Test
+ @GerritConfig(name = "plugin.code-owners.rejectNonResolvableImports", value = "false")
+ public void invalidRejectNonResolvableImportsInPluginConfigIsIgnored() throws Exception {
+ Config cfg = new Config();
+ cfg.setString(
+ SECTION_CODE_OWNERS, /* subsection= */ null, KEY_REJECT_NON_RESOLVABLE_IMPORTS, "INVALID");
+ assertThat(generalConfig.getRejectNonResolvableImports(project, cfg)).isFalse();
+ }
+
+ @Test
+ @GerritConfig(name = "plugin.code-owners.rejectNonResolvableImports", value = "INVALID")
+ public void invalidRejectNonResolvableImportsConfigurationInGerritConfigIsIgnored()
+ throws Exception {
+ assertThat(generalConfig.getRejectNonResolvableImports(project, new Config())).isTrue();
}
@Test
diff --git a/resources/Documentation/config.md b/resources/Documentation/config.md
index 4aa0a26..a94cf63 100644
--- a/resources/Documentation/config.md
+++ b/resources/Documentation/config.md
@@ -23,6 +23,23 @@
*not* possible to just add a value to the inherited list, but if this is wanted
the complete list with the additional value has to be set on project level.
+## <a id="staleIndexOnConfigChanges">
+**NOTE:** Some configuration changes can lead to changes becoming stale in the
+change index. E.g. if an additional branch is newly exempted in `gerrit.config`
+or in the `code-owners.config` of a parent project, the submittability of
+changes for that branch in child projects may change (since they no longer
+require code owner approvals), but it's not feasible to reindex all affected
+changes when this config change is done (as config changes can potentially
+affect all open changes on the host and reindexing that many changes would be
+too expensive). In this case the affected changes will be become stale in the
+change index (e.g. the change index contains outdated submit records) and as a
+result of this you may not observe the effects of the config change on all
+changes immediately, but only when they have been reindexed (which happens on
+any modification of the changes). If needed, you may force the reindexing of a
+change by calling the [Index
+Changes](../../../Documentation/rest-api-changes.html#index-change) REST
+endpoint or by touching the change (e.g. by adding a comment).
+
# <a id="globalConfiguration">Global configuration in gerrit.config</a>
<a id="pluginCodeOwnersDisabled">plugin.@PLUGIN@.disabled</a>