| /* |
| * 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.client.Project; |
| 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, |
| @Nonnull final Project.NameKey projectKey) { |
| |
| // get configuration for branch of change |
| final PluginBranchSpecificSettings settings = |
| settingsProvider.getBranchSpecificSettings(branchName, projectKey); |
| |
| 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)); |
| } |
| } |
| } |