private Optional validateCodeOwnerConfig()

in java/com/google/gerrit/plugins/codeowners/validation/CodeOwnerConfigValidator.java [344:467]


  private Optional<ValidationResult> validateCodeOwnerConfig(
      BranchNameKey branchNameKey,
      RevCommit revCommit,
      IdentifiedUser user,
      boolean force,
      ImmutableListMultimap<String, String> pushOptions) {
    CodeOwnersPluginProjectConfigSnapshot codeOwnersConfig =
        codeOwnersPluginConfiguration.getProjectConfig(branchNameKey.project());
    logger.atFine().log("force = %s", force);
    if (!force && codeOwnersConfig.isDisabled(branchNameKey.branch())) {
      return Optional.of(
          ValidationResult.create(
              pluginName,
              "skipping validation of code owner config files",
              new CommitValidationMessage(
                  "code-owners functionality is disabled", ValidationMessage.Type.HINT)));
    }

    try {
      if (skipCodeOwnerConfigValidationPushOption.skipValidation(pushOptions)) {
        logger.atFine().log("skip validation requested");
        return Optional.of(
            ValidationResult.create(
                pluginName,
                "skipping validation of code owner config files",
                new CommitValidationMessage(
                    String.format(
                        "the validation is skipped due to the --%s~%s push option",
                        pluginName, SkipCodeOwnerConfigValidationPushOption.NAME),
                    ValidationMessage.Type.HINT)));
      }
    } catch (AuthException e) {
      logger.atFine().withCause(e).log("Not allowed to skip code owner config validation");
      return Optional.of(
          ValidationResult.create(
              pluginName,
              "skipping code owner config validation not allowed",
              new CommitValidationMessage(e.getMessage(), ValidationMessage.Type.ERROR)));
    } catch (SkipCodeOwnerConfigValidationPushOption.InvalidValueException e) {
      logger.atFine().log("%s", e.getMessage());
      return Optional.of(
          ValidationResult.create(
              pluginName,
              "invalid push option",
              new CommitValidationMessage(e.getMessage(), ValidationMessage.Type.ERROR)));
    }

    if (codeOwnersConfig.areCodeOwnerConfigsReadOnly()) {
      return Optional.of(
          ValidationResult.create(
              pluginName,
              "modifying code owner config files not allowed",
              new CommitValidationMessage(
                  "code owner config files are configured to be read-only",
                  ValidationMessage.Type.ERROR)));
    }

    try {
      CodeOwnerBackend codeOwnerBackend = codeOwnersConfig.getBackend(branchNameKey.branch());

      // For merge commits, always do the comparison against the destination branch
      // (MergeCommitStrategy.ALL_CHANGED_FILES). Doing the comparison against the auto-merge
      // (MergeCommitStrategy.FILES_WITH_CONFLICT_RESOLUTION) is not possible because loading the
      // auto-merge cannot reuse the rev walk that can see newly created merge commits and hence
      // trying to get the auto merge would fail with a missing object exception. This is why we
      // use MergeCommitStrategy.ALL_CHANGED_FILES here even if
      // MergeCommitStrategy.FILES_WITH_CONFLICT_RESOLUTION is configured.
      ImmutableList<ChangedFile> modifiedCodeOwnerConfigFiles =
          changedFiles
              .getFromDiffCache(
                  branchNameKey.project(), revCommit, MergeCommitStrategy.ALL_CHANGED_FILES)
              .stream()
              // filter out deletions (files without new path)
              .filter(changedFile -> changedFile.newPath().isPresent())
              // filter out non code owner config files
              .filter(
                  changedFile ->
                      codeOwnerBackend.isCodeOwnerConfigFile(
                          branchNameKey.project(),
                          Paths.get(changedFile.newPath().get().toString())
                              .getFileName()
                              .toString()))
              .collect(toImmutableList());

      if (modifiedCodeOwnerConfigFiles.isEmpty()) {
        return Optional.empty();
      }

      // validate the code owner config files
      return Optional.of(
          ValidationResult.create(
              pluginName,
              modifiedCodeOwnerConfigFiles.stream()
                  .flatMap(
                      changedFile ->
                          validateCodeOwnerConfig(
                              user, codeOwnerBackend, branchNameKey, changedFile, revCommit))));
    } catch (InvalidPluginConfigurationException e) {
      // If the code-owners plugin configuration is invalid we cannot get the code owners backend
      // and hence we are not able to detect and validate code owner config files. Instead of
      // failing in this case (which would block all change uploads) we only log a warning and
      // accept that it's possible to add invalid code owner configs while the plugin configuration
      // is invalid.
      logger.atWarning().log(
          "cannot validate code owner config files due to invalid code-owners plugin"
              + " configuration: %s",
          e.getMessage());
      return Optional.of(
          ValidationResult.create(
              pluginName,
              "skipping validation of code owner config files",
              new CommitValidationMessage(
                  "code-owners plugin configuration is invalid,"
                      + " cannot validate code owner config files",
                  ValidationMessage.Type.WARNING)));
    } catch (IOException | DiffNotAvailableException e) {
      String errorMessage =
          String.format(
              "failed to validate code owner config files in revision %s"
                  + " (project = %s, branch = %s)",
              revCommit.getName(), branchNameKey.project(), branchNameKey.branch());
      throw new CodeOwnersInternalServerErrorException(errorMessage, e);
    }
  }