Line data Source code
1 : // Copyright (C) 2022 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.server.query.change; 16 : 17 : import static com.google.gerrit.server.query.change.SubmitRequirementChangeQueryBuilder.SUBMODULE_UPDATE_HAS_ARG; 18 : 19 : import com.google.gerrit.entities.Patch.FileMode; 20 : import com.google.gerrit.exceptions.StorageException; 21 : import com.google.gerrit.server.git.GitRepositoryManager; 22 : import com.google.gerrit.server.patch.DiffNotAvailableException; 23 : import com.google.gerrit.server.patch.DiffOperations; 24 : import com.google.gerrit.server.patch.DiffOptions; 25 : import com.google.gerrit.server.patch.filediff.FileDiffOutput; 26 : import com.google.inject.Inject; 27 : import com.google.inject.assistedinject.Assisted; 28 : import java.io.IOException; 29 : import java.util.Map; 30 : import java.util.Optional; 31 : import org.eclipse.jgit.lib.Repository; 32 : import org.eclipse.jgit.revwalk.RevCommit; 33 : import org.eclipse.jgit.revwalk.RevWalk; 34 : 35 : /** 36 : * Submit requirement predicate that returns true if the diff of the latest patchset against the 37 : * parent number identified by {@link #base} has a submodule modified file, that is, a .gitmodules 38 : * or a git link file. 39 : */ 40 : public class HasSubmoduleUpdatePredicate extends SubmitRequirementPredicate { 41 : private static final String GIT_MODULES_FILE = ".gitmodules"; 42 : 43 : private final DiffOperations diffOperations; 44 : private final GitRepositoryManager repoManager; 45 : private final int base; 46 : 47 : public interface Factory { 48 : HasSubmoduleUpdatePredicate create(int base); 49 : } 50 : 51 : @Inject 52 : HasSubmoduleUpdatePredicate( 53 : DiffOperations diffOperations, GitRepositoryManager repoManager, @Assisted int base) { 54 1 : super("has", SUBMODULE_UPDATE_HAS_ARG); 55 1 : this.diffOperations = diffOperations; 56 1 : this.repoManager = repoManager; 57 1 : this.base = base; 58 1 : } 59 : 60 : @Override 61 : public boolean match(ChangeData cd) { 62 : try { 63 1 : try (Repository repo = repoManager.openRepository(cd.project()); 64 1 : RevWalk rw = new RevWalk(repo)) { 65 1 : RevCommit revCommit = rw.parseCommit(cd.currentPatchSet().commitId()); 66 1 : if (base > revCommit.getParentCount()) { 67 1 : return false; 68 : } 69 1 : } 70 1 : Map<String, FileDiffOutput> diffList = 71 1 : diffOperations.listModifiedFilesAgainstParent( 72 1 : cd.project(), cd.currentPatchSet().commitId(), base, DiffOptions.DEFAULTS); 73 1 : return diffList.values().stream().anyMatch(HasSubmoduleUpdatePredicate::isGitLink); 74 0 : } catch (DiffNotAvailableException e) { 75 0 : throw new StorageException( 76 0 : String.format( 77 : "Failed to evaluate the diff for commit %s against parent number %d", 78 0 : cd.currentPatchSet().commitId(), base), 79 : e); 80 0 : } catch (IOException e) { 81 0 : throw new StorageException( 82 0 : String.format("Failed to open repo for project %s", cd.project()), e); 83 : } 84 : } 85 : 86 : /** 87 : * Return true if the modified file is a {@link #GIT_MODULES_FILE} or a git link regardless of if 88 : * the modification type is add, remove or modify. 89 : */ 90 : private static boolean isGitLink(FileDiffOutput fileDiffOutput) { 91 1 : Optional<String> oldPath = fileDiffOutput.oldPath(); 92 1 : Optional<String> newPath = fileDiffOutput.newPath(); 93 1 : Optional<FileMode> oldMode = fileDiffOutput.oldMode(); 94 1 : Optional<FileMode> newMode = fileDiffOutput.newMode(); 95 : 96 1 : return (oldPath.isPresent() && oldPath.get().equals(GIT_MODULES_FILE)) 97 1 : || (newPath.isPresent() && newPath.get().equals(GIT_MODULES_FILE)) 98 1 : || (oldMode.isPresent() && oldMode.get().equals(FileMode.GITLINK)) 99 1 : || (newMode.isPresent() && newMode.get().equals(FileMode.GITLINK)); 100 : } 101 : 102 : @Override 103 : public int getCost() { 104 0 : return 1; 105 : } 106 : }