blob: b2d940a19d206a53cff0409f8591abc5a25afbb8 [file] [log] [blame]
/*
* Copyright (c) 2017 Cisco and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.fd.maintainer.plugin.service;
import static java.lang.String.format;
import static java.util.Objects.nonNull;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import io.fd.maintainer.plugin.parser.ComponentInfo;
import io.fd.maintainer.plugin.parser.MaintainerMismatchException;
import io.fd.maintainer.plugin.parser.MaintainersParser;
import io.fd.maintainer.plugin.service.dto.PluginBranchSpecificSettings;
import io.fd.maintainer.plugin.util.ClosestMatch;
import io.fd.maintainer.plugin.util.PatchListProcessing;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nonnull;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class MaintainersProvider implements ClosestMatch, PatchListProcessing {
private static final Logger LOG = LoggerFactory.getLogger(MaintainersProvider.class);
final MaintainersParser maintainersParser;
@Inject
private GitRepositoryManager manager;
@Inject
private SettingsProvider settingsProvider;
@Inject
private SchemaFactory<ReviewDb> schemaFactory;
public MaintainersProvider() {
maintainersParser = new MaintainersParser();
}
@Nonnull
public List<ComponentInfo> getMaintainersInfo(@Nonnull final String branchName, final int changeNumber) {
// get configuration for branch of change
final PluginBranchSpecificSettings settings = settingsProvider.getBranchSpecificSettings(branchName);
try (final ReviewDb reviewDb = schemaFactory.open()) {
final Change change = reviewDb.changes().get(new Change.Id(changeNumber));
final String fullFileRef = settings.fullFileRef();
try (final Repository repository = manager.openRepository(change.getProject())) {
final Ref ref = Optional.ofNullable(repository.findRef(fullFileRef))
.orElseThrow(() -> new IllegalStateException(
format("Unable to get ref %s", fullFileRef)));
final RevCommit revCommit = new RevWalk(repository).parseCommit(ref.getObjectId());
final String maintainersFileContent =
findMostRecentMaintainersChangeContent(settings.getLocalFilePath(), repository,
new RevWalk(repository), revCommit);
if (nonNull(maintainersFileContent)) {
return maintainersParser.parseMaintainers(maintainersFileContent);
} else {
throw new IllegalStateException(
format("Unable to find file %s in branch %s", settings.getLocalFilePath(),
fullFileRef));
}
} catch (IOException | MaintainerMismatchException e) {
throw new IllegalStateException(e);
}
} catch (OrmException e) {
throw new IllegalStateException(e);
}
}
// skips head commit
private String findMostRecentMaintainersChangeContent(
final String maintainersFileName,
final Repository repository,
final RevWalk revWalk,
final RevCommit headCommit) {
LOG.info("Starting search at {}", headCommit);
final RevCommit parent = getRevCommit(revWalk, headCommit.getParent(0).getId());
LOG.info("Finding most recent maintainers file in {}", parent);
final String parentIdName = parent.getId().getName();
LOG.info("Parent id name {}", parentIdName);
try (TreeWalk treeWalk = new TreeWalk(repository)) {
treeWalk.addTree(parent.getTree());
treeWalk.setRecursive(true);
treeWalk.setFilter(PathFilter.create(maintainersFileName));
LOG.info("Attempting to find {}", maintainersFileName);
if (treeWalk.next()) {
LOG.info("Maintainers file found in commit {}", parent.getId());
ObjectId objectId = treeWalk.getObjectId(0);
ObjectLoader loader = repository.open(objectId);
// and then one can the loader to read the file
final ByteArrayOutputStream out = new ByteArrayOutputStream();
loader.copyTo(out);
revWalk.dispose();
return new String(out.toByteArray());
}
LOG.info("Maintainers file not found in commit {}, going deep", parent.getId());
if (parent.getParents() == null) {
throw new IllegalStateException(format("Root of branch reached with commit %s", parent));
}
return findMostRecentMaintainersChangeContent(maintainersFileName, repository, revWalk, parent);
} catch (IOException e) {
throw new IllegalStateException(format("Unable to detect maintainers file in %s", parent.getId()));
}
}
private RevCommit getRevCommit(final RevWalk revWalk, final ObjectId id) {
try {
return revWalk.parseCommit(id);
} catch (IOException e) {
throw new IllegalStateException(format("Unable to parse commit %s", id));
}
}
}