mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-12-24 01:09:48 +01:00
Merge branch 'develop' into feature/import_git_from_url
This commit is contained in:
@@ -55,6 +55,8 @@ import org.eclipse.jgit.util.LfsFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.ContextEntry;
|
||||
import sonia.scm.security.GPG;
|
||||
import sonia.scm.security.PublicKey;
|
||||
import sonia.scm.util.HttpUtil;
|
||||
import sonia.scm.util.Util;
|
||||
import sonia.scm.web.GitUserAgentProvider;
|
||||
@@ -63,6 +65,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -73,73 +76,37 @@ import static java.util.Optional.of;
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public final class GitUtil
|
||||
{
|
||||
public final class GitUtil {
|
||||
|
||||
private static final GitUserAgentProvider GIT_USER_AGENT_PROVIDER = new GitUserAgentProvider();
|
||||
|
||||
/** Field description */
|
||||
public static final String REF_HEAD = "HEAD";
|
||||
|
||||
/** Field description */
|
||||
public static final String REF_HEAD_PREFIX = "refs/heads/";
|
||||
|
||||
/** Field description */
|
||||
public static final String REF_MASTER = "master";
|
||||
|
||||
/** Field description */
|
||||
private static final String DIRECTORY_DOTGIT = ".git";
|
||||
|
||||
/** Field description */
|
||||
private static final String DIRECTORY_OBJETCS = "objects";
|
||||
|
||||
/** Field description */
|
||||
private static final String DIRECTORY_REFS = "refs";
|
||||
|
||||
/** Field description */
|
||||
private static final String PREFIX_HEADS = "refs/heads/";
|
||||
|
||||
/** Field description */
|
||||
private static final String PREFIX_TAG = "refs/tags/";
|
||||
|
||||
/** Field description */
|
||||
private static final String REFSPEC = "+refs/heads/*:refs/remote/scm/%s/*";
|
||||
|
||||
/** Field description */
|
||||
private static final String REMOTE_REF = "refs/remote/scm/%s/%s";
|
||||
|
||||
/** Field description */
|
||||
private static final int TIMEOUT = 5;
|
||||
|
||||
/** Field description */
|
||||
private static final String USERAGENT_GIT = "git/";
|
||||
|
||||
/** the logger for GitUtil */
|
||||
/**
|
||||
* the logger for GitUtil
|
||||
*/
|
||||
private static final Logger logger = LoggerFactory.getLogger(GitUtil.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*/
|
||||
private GitUtil() {}
|
||||
private GitUtil() {
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param repo
|
||||
*/
|
||||
public static void close(org.eclipse.jgit.lib.Repository repo)
|
||||
{
|
||||
if (repo != null)
|
||||
{
|
||||
public static void close(org.eclipse.jgit.lib.Repository repo) {
|
||||
if (repo != null) {
|
||||
repo.close();
|
||||
}
|
||||
}
|
||||
@@ -147,42 +114,30 @@ public final class GitUtil
|
||||
/**
|
||||
* TODO cache
|
||||
*
|
||||
*
|
||||
* @param repository
|
||||
* @param revWalk
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Multimap<ObjectId,
|
||||
String> createTagMap(org.eclipse.jgit.lib.Repository repository,
|
||||
RevWalk revWalk)
|
||||
{
|
||||
RevWalk revWalk) {
|
||||
Multimap<ObjectId, String> tags = ArrayListMultimap.create();
|
||||
|
||||
Map<String, Ref> tagMap = repository.getTags();
|
||||
|
||||
if (tagMap != null)
|
||||
{
|
||||
for (Map.Entry<String, Ref> e : tagMap.entrySet())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (tagMap != null) {
|
||||
for (Map.Entry<String, Ref> e : tagMap.entrySet()) {
|
||||
try {
|
||||
|
||||
RevCommit c = getCommit(repository, revWalk, e.getValue());
|
||||
|
||||
if (c != null)
|
||||
{
|
||||
if (c != null) {
|
||||
tags.put(c.getId(), e.getKey());
|
||||
}
|
||||
else if (logger.isWarnEnabled())
|
||||
{
|
||||
} else {
|
||||
logger.warn("could not find commit for tag {}", e.getKey());
|
||||
}
|
||||
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
} catch (IOException ex) {
|
||||
logger.error("could not read commit for ref", ex);
|
||||
}
|
||||
|
||||
@@ -193,8 +148,7 @@ public final class GitUtil
|
||||
}
|
||||
|
||||
public static FetchResult fetch(Git git, File directory, Repository remoteRepository) {
|
||||
try
|
||||
{
|
||||
try {
|
||||
FetchCommand fetch = git.fetch();
|
||||
|
||||
fetch.setRemote(directory.getAbsolutePath());
|
||||
@@ -202,123 +156,63 @@ public final class GitUtil
|
||||
fetch.setTimeout((int) TimeUnit.MINUTES.toSeconds(TIMEOUT));
|
||||
|
||||
return fetch.call();
|
||||
}
|
||||
catch (GitAPIException ex)
|
||||
{
|
||||
} catch (GitAPIException ex) {
|
||||
throw new InternalRepositoryException(ContextEntry.ContextBuilder.entity("Remote", directory.toString()).in(remoteRepository), "could not fetch", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param directory
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public static org.eclipse.jgit.lib.Repository open(File directory)
|
||||
throws IOException
|
||||
{
|
||||
throws IOException {
|
||||
FS fs = FS.DETECTED;
|
||||
FileRepositoryBuilder builder = new FileRepositoryBuilder();
|
||||
|
||||
builder.setFS(fs);
|
||||
|
||||
if (isGitDirectory(fs, directory))
|
||||
{
|
||||
if (isGitDirectory(fs, directory)) {
|
||||
|
||||
// bare repository
|
||||
builder.setGitDir(directory).setBare();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
builder.setWorkTree(directory);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param formatter
|
||||
*/
|
||||
public static void release(DiffFormatter formatter)
|
||||
{
|
||||
if (formatter != null)
|
||||
{
|
||||
public static void release(DiffFormatter formatter) {
|
||||
if (formatter != null) {
|
||||
formatter.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param walk
|
||||
*/
|
||||
public static void release(TreeWalk walk)
|
||||
{
|
||||
if (walk != null)
|
||||
{
|
||||
public static void release(TreeWalk walk) {
|
||||
if (walk != null) {
|
||||
walk.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param walk
|
||||
*/
|
||||
public static void release(RevWalk walk)
|
||||
{
|
||||
if (walk != null)
|
||||
{
|
||||
public static void release(RevWalk walk) {
|
||||
if (walk != null) {
|
||||
walk.close();
|
||||
}
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param ref
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getBranch(Ref ref)
|
||||
{
|
||||
public static String getBranch(Ref ref) {
|
||||
String branch = null;
|
||||
|
||||
if (ref != null)
|
||||
{
|
||||
if (ref != null) {
|
||||
branch = getBranch(ref.getName());
|
||||
}
|
||||
|
||||
return branch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param name
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getBranch(String name)
|
||||
{
|
||||
public static String getBranch(String name) {
|
||||
String branch = null;
|
||||
|
||||
if (Util.isNotEmpty(name) && name.startsWith(PREFIX_HEADS))
|
||||
{
|
||||
if (Util.isNotEmpty(name) && name.startsWith(PREFIX_HEADS)) {
|
||||
branch = name.substring(PREFIX_HEADS.length());
|
||||
}
|
||||
|
||||
@@ -329,18 +223,15 @@ public final class GitUtil
|
||||
* Returns {@code true} if the provided reference name is a branch name.
|
||||
*
|
||||
* @param refName reference name
|
||||
*
|
||||
* @return {@code true} if the name is a branch name
|
||||
*
|
||||
* @since 1.50
|
||||
*/
|
||||
public static boolean isBranch(String refName)
|
||||
{
|
||||
public static boolean isBranch(String refName) {
|
||||
return Strings.nullToEmpty(refName).startsWith(PREFIX_HEADS);
|
||||
}
|
||||
|
||||
public static Ref getBranchIdOrCurrentHead(org.eclipse.jgit.lib.Repository gitRepository, String requestedBranch) throws IOException {
|
||||
if ( Strings.isNullOrEmpty(requestedBranch) ) {
|
||||
if (Strings.isNullOrEmpty(requestedBranch)) {
|
||||
logger.trace("no default branch configured, use repository head as default");
|
||||
Optional<Ref> repositoryHeadRef = GitUtil.getRepositoryHeadRef(gitRepository);
|
||||
return repositoryHeadRef.orElse(null);
|
||||
@@ -352,37 +243,28 @@ public final class GitUtil
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param repo
|
||||
* @param branchName
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public static Ref getBranchId(org.eclipse.jgit.lib.Repository repo,
|
||||
String branchName)
|
||||
throws IOException
|
||||
{
|
||||
String branchName)
|
||||
throws IOException {
|
||||
Ref ref = null;
|
||||
if (!branchName.startsWith(REF_HEAD))
|
||||
{
|
||||
if (!branchName.startsWith(REF_HEAD)) {
|
||||
branchName = PREFIX_HEADS.concat(branchName);
|
||||
}
|
||||
|
||||
checkBranchName(repo, branchName);
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
ref = repo.findRef(branchName);
|
||||
|
||||
if (ref == null)
|
||||
{
|
||||
if (ref == null) {
|
||||
logger.warn("could not find branch for {}", branchName);
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
} catch (IOException ex) {
|
||||
logger.warn("error occured during resolve of branch id", ex);
|
||||
}
|
||||
|
||||
@@ -415,33 +297,21 @@ public final class GitUtil
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param repository
|
||||
* @param revWalk
|
||||
* @param ref
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
* Returns the commit for the given ref.
|
||||
* If the given ref is for a tag, the commit that this tag belongs to is returned instead.
|
||||
*/
|
||||
public static RevCommit getCommit(org.eclipse.jgit.lib.Repository repository,
|
||||
RevWalk revWalk, Ref ref)
|
||||
throws IOException
|
||||
{
|
||||
RevWalk revWalk, Ref ref)
|
||||
throws IOException {
|
||||
RevCommit commit = null;
|
||||
ObjectId id = ref.getPeeledObjectId();
|
||||
|
||||
if (id == null)
|
||||
{
|
||||
if (id == null) {
|
||||
id = ref.getObjectId();
|
||||
}
|
||||
|
||||
if (id != null)
|
||||
{
|
||||
if (revWalk == null)
|
||||
{
|
||||
if (id != null) {
|
||||
if (revWalk == null) {
|
||||
revWalk = new RevWalk(repository);
|
||||
}
|
||||
|
||||
@@ -451,16 +321,30 @@ public final class GitUtil
|
||||
return commit;
|
||||
}
|
||||
|
||||
public static RevTag getTag(org.eclipse.jgit.lib.Repository repository,
|
||||
RevWalk revWalk, Ref ref)
|
||||
throws IOException {
|
||||
RevTag tag = null;
|
||||
ObjectId id = ref.getObjectId();
|
||||
|
||||
if (id != null) {
|
||||
if (revWalk == null) {
|
||||
revWalk = new RevWalk(repository);
|
||||
}
|
||||
|
||||
tag = revWalk.parseTag(id);
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param commit
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static long getCommitTime(RevCommit commit)
|
||||
{
|
||||
public static long getCommitTime(RevCommit commit) {
|
||||
long date = commit.getCommitTime();
|
||||
|
||||
date = date * 1000;
|
||||
@@ -471,17 +355,13 @@ public final class GitUtil
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param objectId
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getId(AnyObjectId objectId)
|
||||
{
|
||||
public static String getId(AnyObjectId objectId) {
|
||||
String id = Util.EMPTY_STRING;
|
||||
|
||||
if (objectId != null)
|
||||
{
|
||||
if (objectId != null) {
|
||||
id = objectId.name();
|
||||
}
|
||||
|
||||
@@ -491,44 +371,27 @@ public final class GitUtil
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param repository
|
||||
* @param id
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public static Ref getRefForCommit(org.eclipse.jgit.lib.Repository repository,
|
||||
ObjectId id)
|
||||
throws IOException
|
||||
{
|
||||
ObjectId id)
|
||||
throws IOException {
|
||||
Ref ref = null;
|
||||
RevWalk walk = null;
|
||||
|
||||
try
|
||||
{
|
||||
walk = new RevWalk(repository);
|
||||
|
||||
try (RevWalk walk = new RevWalk(repository)) {
|
||||
RevCommit commit = walk.parseCommit(id);
|
||||
|
||||
for (Map.Entry<String, Ref> e : repository.getAllRefs().entrySet())
|
||||
{
|
||||
if (e.getKey().startsWith(Constants.R_HEADS))
|
||||
{
|
||||
if (walk.isMergedInto(commit,
|
||||
walk.parseCommit(e.getValue().getObjectId())))
|
||||
{
|
||||
ref = e.getValue();
|
||||
}
|
||||
for (Map.Entry<String, Ref> e : repository.getAllRefs().entrySet()) {
|
||||
if (e.getKey().startsWith(Constants.R_HEADS) && walk.isMergedInto(commit,
|
||||
walk.parseCommit(e.getValue().getObjectId()))) {
|
||||
ref = e.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
release(walk);
|
||||
}
|
||||
|
||||
return ref;
|
||||
}
|
||||
@@ -580,26 +443,19 @@ public final class GitUtil
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param repo
|
||||
* @param revision
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public static ObjectId getRevisionId(org.eclipse.jgit.lib.Repository repo,
|
||||
String revision)
|
||||
throws IOException
|
||||
{
|
||||
String revision)
|
||||
throws IOException {
|
||||
ObjectId revId;
|
||||
|
||||
if (Util.isNotEmpty(revision))
|
||||
{
|
||||
if (Util.isNotEmpty(revision)) {
|
||||
revId = repo.resolve(revision);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
revId = getRepositoryHead(repo);
|
||||
}
|
||||
|
||||
@@ -609,34 +465,27 @@ public final class GitUtil
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param repository
|
||||
* @param localBranch
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getScmRemoteRefName(Repository repository,
|
||||
Ref localBranch)
|
||||
{
|
||||
Ref localBranch) {
|
||||
return getScmRemoteRefName(repository, localBranch.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param repository
|
||||
* @param localBranch
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getScmRemoteRefName(Repository repository,
|
||||
String localBranch)
|
||||
{
|
||||
String localBranch) {
|
||||
String branch = localBranch;
|
||||
|
||||
if (localBranch.startsWith(REF_HEAD_PREFIX))
|
||||
{
|
||||
if (localBranch.startsWith(REF_HEAD_PREFIX)) {
|
||||
branch = localBranch.substring(REF_HEAD_PREFIX.length());
|
||||
}
|
||||
|
||||
@@ -647,16 +496,12 @@ public final class GitUtil
|
||||
* Returns the name of the tag or {@code null} if the the ref is not a tag.
|
||||
*
|
||||
* @param refName ref name
|
||||
*
|
||||
* @return name of tag or {@link null}
|
||||
*
|
||||
* @since 1.50
|
||||
*/
|
||||
public static String getTagName(String refName)
|
||||
{
|
||||
public static String getTagName(String refName) {
|
||||
String tagName = null;
|
||||
if (refName.startsWith(PREFIX_TAG))
|
||||
{
|
||||
if (refName.startsWith(PREFIX_TAG)) {
|
||||
tagName = refName.substring(PREFIX_TAG.length());
|
||||
}
|
||||
|
||||
@@ -666,91 +511,112 @@ public final class GitUtil
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param ref
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getTagName(Ref ref)
|
||||
{
|
||||
public static String getTagName(Ref ref) {
|
||||
String name = ref.getName();
|
||||
|
||||
if (name.startsWith(PREFIX_TAG))
|
||||
{
|
||||
if (name.startsWith(PREFIX_TAG)) {
|
||||
name = name.substring(PREFIX_TAG.length());
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
private static final String GPG_HEADER = "-----BEGIN PGP SIGNATURE-----";
|
||||
|
||||
public static Optional<Signature> getTagSignature(RevObject revObject, GPG gpg, RevWalk revWalk) throws IOException {
|
||||
if (revObject instanceof RevTag) {
|
||||
final byte[] messageBytes = revWalk.getObjectReader().open(revObject.getId()).getBytes();
|
||||
final String message = new String(messageBytes);
|
||||
final int signatureStartIndex = message.indexOf(GPG_HEADER);
|
||||
if (signatureStartIndex < 0) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
final String signature = message.substring(signatureStartIndex);
|
||||
|
||||
String publicKeyId = gpg.findPublicKeyId(signature.getBytes());
|
||||
if (Strings.isNullOrEmpty(publicKeyId)) {
|
||||
// key not found
|
||||
return Optional.of(new Signature(publicKeyId, "gpg", SignatureStatus.NOT_FOUND, null, Collections.emptySet()));
|
||||
}
|
||||
|
||||
Optional<PublicKey> publicKeyById = gpg.findPublicKey(publicKeyId);
|
||||
if (!publicKeyById.isPresent()) {
|
||||
// key not found
|
||||
return Optional.of(new Signature(publicKeyId, "gpg", SignatureStatus.NOT_FOUND, null, Collections.emptySet()));
|
||||
}
|
||||
|
||||
PublicKey publicKey = publicKeyById.get();
|
||||
|
||||
String rawMessage = message.substring(0, signatureStartIndex);
|
||||
boolean verified = publicKey.verify(rawMessage.getBytes(), signature.getBytes());
|
||||
return Optional.of(new Signature(
|
||||
publicKeyId,
|
||||
"gpg",
|
||||
verified ? SignatureStatus.VERIFIED : SignatureStatus.INVALID,
|
||||
publicKey.getOwner().orElse(null),
|
||||
publicKey.getContacts()
|
||||
));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the request comes from a git client.
|
||||
*
|
||||
*
|
||||
* @param request servlet request
|
||||
*
|
||||
* @return true if the client is git
|
||||
*/
|
||||
public static boolean isGitClient(HttpServletRequest request)
|
||||
{
|
||||
public static boolean isGitClient(HttpServletRequest request) {
|
||||
return GIT_USER_AGENT_PROVIDER.parseUserAgent(request.getHeader(HttpUtil.HEADER_USERAGENT)) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param dir
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static boolean isGitDirectory(File dir)
|
||||
{
|
||||
public static boolean isGitDirectory(File dir) {
|
||||
return isGitDirectory(FS.DETECTED, dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param fs
|
||||
* @param dir
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static boolean isGitDirectory(FS fs, File dir)
|
||||
{
|
||||
public static boolean isGitDirectory(FS fs, File dir) {
|
||||
//J-
|
||||
return fs.resolve(dir, DIRECTORY_OBJETCS).exists()
|
||||
&& fs.resolve(dir, DIRECTORY_REFS).exists()
|
||||
&&!fs.resolve(dir, DIRECTORY_DOTGIT).exists();
|
||||
&& !fs.resolve(dir, DIRECTORY_DOTGIT).exists();
|
||||
//J+
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param ref
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static boolean isHead(String ref)
|
||||
{
|
||||
public static boolean isHead(String ref) {
|
||||
return ref.startsWith(REF_HEAD_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param id
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static boolean isValidObjectId(ObjectId id)
|
||||
{
|
||||
return (id != null) &&!id.equals(ObjectId.zeroId());
|
||||
public static boolean isValidObjectId(ObjectId id) {
|
||||
return (id != null) && !id.equals(ObjectId.zeroId());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -792,25 +658,20 @@ public final class GitUtil
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param repo
|
||||
* @param branchName
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static void checkBranchName(org.eclipse.jgit.lib.Repository repo,
|
||||
String branchName)
|
||||
throws IOException
|
||||
{
|
||||
if (branchName.contains(".."))
|
||||
{
|
||||
String branchName)
|
||||
throws IOException {
|
||||
if (branchName.contains("..")) {
|
||||
File repoDirectory = repo.getDirectory();
|
||||
File branchFile = new File(repoDirectory, branchName);
|
||||
|
||||
if (!branchFile.getCanonicalPath().startsWith(
|
||||
repoDirectory.getCanonicalPath()))
|
||||
{
|
||||
repoDirectory.getCanonicalPath())) {
|
||||
logger.error(
|
||||
"branch \"{}\" is outside of the repository. It looks like path traversal attack",
|
||||
branchName);
|
||||
@@ -824,13 +685,10 @@ public final class GitUtil
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param repository
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private static RefSpec createRefSpec(Repository repository)
|
||||
{
|
||||
private static RefSpec createRefSpec(Repository repository) {
|
||||
return new RefSpec(String.format(REFSPEC, repository.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
|
||||
Command.DIFF,
|
||||
Command.DIFF_RESULT,
|
||||
Command.LOG,
|
||||
Command.TAG,
|
||||
Command.TAGS,
|
||||
Command.BRANCH,
|
||||
Command.BRANCHES,
|
||||
@@ -142,7 +143,12 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
|
||||
|
||||
@Override
|
||||
public TagsCommand getTagsCommand() {
|
||||
return new GitTagsCommand(context);
|
||||
return commandInjector.getInstance(GitTagsCommand.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagCommand getTagCommand() {
|
||||
return commandInjector.getInstance(GitTagCommand.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.PersonIdent;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevObject;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import sonia.scm.event.ScmEventBus;
|
||||
import sonia.scm.repository.GitUtil;
|
||||
import sonia.scm.repository.InternalRepositoryException;
|
||||
import sonia.scm.repository.PostReceiveRepositoryHookEvent;
|
||||
import sonia.scm.repository.PreReceiveRepositoryHookEvent;
|
||||
import sonia.scm.repository.RepositoryHookEvent;
|
||||
import sonia.scm.repository.RepositoryHookType;
|
||||
import sonia.scm.repository.Tag;
|
||||
import sonia.scm.repository.api.HookContext;
|
||||
import sonia.scm.repository.api.HookContextFactory;
|
||||
import sonia.scm.repository.api.HookFeature;
|
||||
import sonia.scm.repository.api.HookTagProvider;
|
||||
import sonia.scm.repository.api.TagCreateRequest;
|
||||
import sonia.scm.repository.api.TagDeleteRequest;
|
||||
import sonia.scm.user.User;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singleton;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static sonia.scm.ContextEntry.ContextBuilder.entity;
|
||||
import static sonia.scm.NotFoundException.notFound;
|
||||
|
||||
public class GitTagCommand extends AbstractGitCommand implements TagCommand {
|
||||
private final HookContextFactory hookContextFactory;
|
||||
private final ScmEventBus eventBus;
|
||||
|
||||
@Inject
|
||||
GitTagCommand(GitContext context, HookContextFactory hookContextFactory, ScmEventBus eventBus) {
|
||||
super(context);
|
||||
this.hookContextFactory = hookContextFactory;
|
||||
this.eventBus = eventBus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag create(TagCreateRequest request) {
|
||||
final String name = request.getName();
|
||||
final String revision = request.getRevision();
|
||||
|
||||
if (Strings.isNullOrEmpty(revision)) {
|
||||
throw new IllegalArgumentException("Revision is required");
|
||||
}
|
||||
|
||||
if (Strings.isNullOrEmpty(name)) {
|
||||
throw new IllegalArgumentException("Name is required");
|
||||
}
|
||||
|
||||
try (Git git = new Git(context.open())) {
|
||||
|
||||
RevObject revObject;
|
||||
Long tagTime;
|
||||
|
||||
ObjectId taggedCommitObjectId = git.getRepository().resolve(revision);
|
||||
|
||||
if (taggedCommitObjectId == null) {
|
||||
throw notFound(entity("revision", revision).in(repository));
|
||||
}
|
||||
|
||||
try (RevWalk walk = new RevWalk(git.getRepository())) {
|
||||
revObject = walk.parseAny(taggedCommitObjectId);
|
||||
tagTime = GitUtil.getTagTime(walk, taggedCommitObjectId);
|
||||
}
|
||||
|
||||
Tag tag = new Tag(name, revision, tagTime);
|
||||
|
||||
RepositoryHookEvent hookEvent = createTagHookEvent(TagHookContextProvider.createHookEvent(tag));
|
||||
eventBus.post(new PreReceiveRepositoryHookEvent(hookEvent));
|
||||
|
||||
User user = SecurityUtils.getSubject().getPrincipals().oneByType(User.class);
|
||||
PersonIdent taggerIdent = new PersonIdent(user.getDisplayName(), user.getMail());
|
||||
|
||||
git.tag()
|
||||
.setObjectId(revObject)
|
||||
.setTagger(taggerIdent)
|
||||
.setName(name)
|
||||
.call();
|
||||
|
||||
eventBus.post(new PostReceiveRepositoryHookEvent(hookEvent));
|
||||
|
||||
return tag;
|
||||
} catch (IOException | GitAPIException ex) {
|
||||
throw new InternalRepositoryException(repository, "could not create tag " + name + " for revision " + revision, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(TagDeleteRequest request) {
|
||||
String name = request.getName();
|
||||
|
||||
if (Strings.isNullOrEmpty(name)) {
|
||||
throw new IllegalArgumentException("Name is required");
|
||||
}
|
||||
|
||||
try (Git git = new Git(context.open())) {
|
||||
final Repository repository = git.getRepository();
|
||||
Optional<Ref> tagRef = findTagRef(git, name);
|
||||
Tag tag;
|
||||
|
||||
// Deleting a non-existent tag is a valid action and simply succeeds without
|
||||
// anything happening.
|
||||
if (!tagRef.isPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (RevWalk walk = new RevWalk(repository)) {
|
||||
final RevCommit commit = GitUtil.getCommit(repository, walk, tagRef.get());
|
||||
Long tagTime = GitUtil.getTagTime(walk, tagRef.get().getObjectId());
|
||||
tag = new Tag(name, commit.name(), tagTime);
|
||||
}
|
||||
|
||||
RepositoryHookEvent hookEvent = createTagHookEvent(TagHookContextProvider.deleteHookEvent(tag));
|
||||
eventBus.post(new PreReceiveRepositoryHookEvent(hookEvent));
|
||||
git.tagDelete().setTags(name).call();
|
||||
eventBus.post(new PostReceiveRepositoryHookEvent(hookEvent));
|
||||
} catch (GitAPIException | IOException e) {
|
||||
throw new InternalRepositoryException(repository, "could not delete tag " + name, e);
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<Ref> findTagRef(Git git, String name) throws GitAPIException {
|
||||
final String tagRef = "refs/tags/" + name;
|
||||
return git.tagList().call().stream().filter(it -> it.getName().equals(tagRef)).findAny();
|
||||
}
|
||||
|
||||
private RepositoryHookEvent createTagHookEvent(TagHookContextProvider hookEvent) {
|
||||
HookContext context = hookContextFactory.createContext(hookEvent, this.context.getRepository());
|
||||
return new RepositoryHookEvent(context, this.context.getRepository(), RepositoryHookType.PRE_RECEIVE);
|
||||
}
|
||||
|
||||
private static class TagHookContextProvider extends HookContextProvider {
|
||||
private final List<Tag> newTags;
|
||||
private final List<Tag> deletedTags;
|
||||
|
||||
private TagHookContextProvider(List<Tag> newTags, List<Tag> deletedTags) {
|
||||
this.newTags = newTags;
|
||||
this.deletedTags = deletedTags;
|
||||
}
|
||||
|
||||
static TagHookContextProvider createHookEvent(Tag newTag) {
|
||||
return new TagHookContextProvider(singletonList(newTag), emptyList());
|
||||
}
|
||||
|
||||
static TagHookContextProvider deleteHookEvent(Tag deletedTag) {
|
||||
return new TagHookContextProvider(emptyList(), singletonList(deletedTag));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<HookFeature> getSupportedFeatures() {
|
||||
return singleton(HookFeature.TAG_PROVIDER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HookTagProvider getTagProvider() {
|
||||
return new HookTagProvider() {
|
||||
@Override
|
||||
public List<Tag> getCreatedTags() {
|
||||
return newTags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Tag> getDeletedTags() {
|
||||
return deletedTags;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public HookChangesetProvider getChangesetProvider() {
|
||||
return r -> new HookChangesetResponse(emptyList());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,15 +30,21 @@ import com.google.common.base.Function;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevObject;
|
||||
import org.eclipse.jgit.revwalk.RevTag;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.repository.GitUtil;
|
||||
import sonia.scm.repository.InternalRepositoryException;
|
||||
import sonia.scm.repository.Tag;
|
||||
import sonia.scm.security.GPG;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
@@ -49,32 +55,34 @@ import java.util.List;
|
||||
*/
|
||||
public class GitTagsCommand extends AbstractGitCommand implements TagsCommand {
|
||||
|
||||
private final GPG gpg;
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
public GitTagsCommand(GitContext context) {
|
||||
@Inject
|
||||
public GitTagsCommand(GitContext context, GPG gpp) {
|
||||
super(context);
|
||||
this.gpg = gpp;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public List<Tag> getTags() throws IOException {
|
||||
List<Tag> tags = null;
|
||||
List<Tag> tags;
|
||||
|
||||
RevWalk revWalk = null;
|
||||
|
||||
try {
|
||||
final Git git = new Git(open());
|
||||
|
||||
try (Git git = new Git(open())) {
|
||||
revWalk = new RevWalk(git.getRepository());
|
||||
|
||||
List<Ref> tagList = git.tagList().call();
|
||||
|
||||
tags = Lists.transform(tagList,
|
||||
new TransformFuntion(git.getRepository(), revWalk));
|
||||
new TransformFunction(git.getRepository(), revWalk, gpg));
|
||||
} catch (GitAPIException ex) {
|
||||
throw new InternalRepositoryException(repository, "could not read tags from repository", ex);
|
||||
} finally {
|
||||
@@ -92,26 +100,27 @@ public class GitTagsCommand extends AbstractGitCommand implements TagsCommand {
|
||||
* @author Enter your name here...
|
||||
* @version Enter version here..., 12/07/06
|
||||
*/
|
||||
private static class TransformFuntion implements Function<Ref, Tag> {
|
||||
private static class TransformFunction implements Function<Ref, Tag> {
|
||||
|
||||
/**
|
||||
* the logger for TransformFuntion
|
||||
*/
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(TransformFuntion.class);
|
||||
LoggerFactory.getLogger(TransformFunction.class);
|
||||
|
||||
//~--- constructors -------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
* @param repository
|
||||
* @param repository
|
||||
* @param revWalk
|
||||
*/
|
||||
public TransformFuntion(org.eclipse.jgit.lib.Repository repository,
|
||||
RevWalk revWalk) {
|
||||
public TransformFunction(Repository repository,
|
||||
RevWalk revWalk,
|
||||
GPG gpg) {
|
||||
this.repository = repository;
|
||||
this.revWalk = revWalk;
|
||||
this.gpg = gpg;
|
||||
}
|
||||
|
||||
//~--- methods ------------------------------------------------------------
|
||||
@@ -127,14 +136,17 @@ public class GitTagsCommand extends AbstractGitCommand implements TagsCommand {
|
||||
Tag tag = null;
|
||||
|
||||
try {
|
||||
RevObject revObject = GitUtil.getCommit(repository, revWalk, ref);
|
||||
|
||||
if (revObject != null) {
|
||||
RevCommit revCommit = GitUtil.getCommit(repository, revWalk, ref);
|
||||
if (revCommit != null) {
|
||||
String name = GitUtil.getTagName(ref);
|
||||
|
||||
tag = new Tag(name, revObject.getId().name(), GitUtil.getTagTime(revWalk, ref.getObjectId()));
|
||||
tag = new Tag(name, revCommit.getId().name(), GitUtil.getTagTime(revWalk, ref.getObjectId()));
|
||||
RevObject revObject = revWalk.parseAny(ref.getObjectId());
|
||||
if (revObject.getType() == Constants.OBJ_TAG) {
|
||||
RevTag revTag = (RevTag) revObject;
|
||||
GitUtil.getTagSignature(revTag, gpg, revWalk)
|
||||
.ifPresent(tag::addSignature);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException ex) {
|
||||
logger.error("could not get commit for tag", ex);
|
||||
}
|
||||
@@ -147,11 +159,12 @@ public class GitTagsCommand extends AbstractGitCommand implements TagsCommand {
|
||||
/**
|
||||
* Field description
|
||||
*/
|
||||
private org.eclipse.jgit.lib.Repository repository;
|
||||
private final org.eclipse.jgit.lib.Repository repository;
|
||||
|
||||
/**
|
||||
* Field description
|
||||
*/
|
||||
private RevWalk revWalk;
|
||||
private final RevWalk revWalk;
|
||||
private final GPG gpg;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import sonia.scm.event.ScmEventBus;
|
||||
import sonia.scm.repository.Branch;
|
||||
import sonia.scm.repository.BranchCreatedEvent;
|
||||
import sonia.scm.repository.PostReceiveRepositoryHookEvent;
|
||||
import sonia.scm.repository.PreReceiveRepositoryHookEvent;
|
||||
import sonia.scm.repository.api.BranchRequest;
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.mgt.DefaultSecurityManager;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.apache.shiro.util.ThreadContext;
|
||||
import org.eclipse.jgit.lib.GpgSigner;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import sonia.scm.event.ScmEventBus;
|
||||
import sonia.scm.repository.GitTestHelper;
|
||||
import sonia.scm.repository.PostReceiveRepositoryHookEvent;
|
||||
import sonia.scm.repository.PreReceiveRepositoryHookEvent;
|
||||
import sonia.scm.repository.Tag;
|
||||
import sonia.scm.repository.api.HookContext;
|
||||
import sonia.scm.repository.api.HookContextFactory;
|
||||
import sonia.scm.repository.api.TagDeleteRequest;
|
||||
import sonia.scm.repository.api.TagCreateRequest;
|
||||
import sonia.scm.security.GPG;
|
||||
import sonia.scm.util.MockUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class GitTagCommandTest extends AbstractGitCommandTestBase {
|
||||
|
||||
@Mock
|
||||
private GPG gpg;
|
||||
|
||||
@Mock
|
||||
private HookContextFactory hookContextFactory;
|
||||
|
||||
@Mock
|
||||
private ScmEventBus eventBus;
|
||||
|
||||
private Subject subject;
|
||||
|
||||
@Before
|
||||
public void setSigner() {
|
||||
GpgSigner.setDefault(new GitTestHelper.SimpleGpgSigner());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void bindThreadContext() {
|
||||
SecurityUtils.setSecurityManager(new DefaultSecurityManager());
|
||||
subject = MockUtil.createUserSubject(SecurityUtils.getSecurityManager());
|
||||
ThreadContext.bind(subject);
|
||||
}
|
||||
|
||||
@After
|
||||
public void unbindThreadContext() {
|
||||
ThreadContext.unbindSubject();
|
||||
ThreadContext.unbindSecurityManager();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateATag() throws IOException {
|
||||
createCommand().create(new TagCreateRequest("592d797cd36432e591416e8b2b98154f4f163411", "newtag"));
|
||||
Optional<Tag> optionalTag = findTag(createContext(), "newtag");
|
||||
assertThat(optionalTag).isNotEmpty();
|
||||
final Tag tag = optionalTag.get();
|
||||
assertThat(tag.getName()).isEqualTo("newtag");
|
||||
assertThat(tag.getRevision()).isEqualTo("592d797cd36432e591416e8b2b98154f4f163411");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPostCreateEvent() {
|
||||
ArgumentCaptor<Object> captor = ArgumentCaptor.forClass(Object.class);
|
||||
doNothing().when(eventBus).post(captor.capture());
|
||||
when(hookContextFactory.createContext(any(), any())).thenAnswer(this::createMockedContext);
|
||||
|
||||
createCommand().create(new TagCreateRequest("592d797cd36432e591416e8b2b98154f4f163411", "newtag"));
|
||||
|
||||
List<Object> events = captor.getAllValues();
|
||||
assertThat(events.get(0)).isInstanceOf(PreReceiveRepositoryHookEvent.class);
|
||||
assertThat(events.get(1)).isInstanceOf(PostReceiveRepositoryHookEvent.class);
|
||||
|
||||
PreReceiveRepositoryHookEvent event = (PreReceiveRepositoryHookEvent) events.get(0);
|
||||
assertThat(event.getContext().getTagProvider().getCreatedTags().get(0).getName()).isEqualTo("newtag");
|
||||
assertThat(event.getContext().getTagProvider().getDeletedTags()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDeleteATag() throws IOException {
|
||||
final GitContext context = createContext();
|
||||
Optional<Tag> tag = findTag(context, "test-tag");
|
||||
assertThat(tag).isNotEmpty();
|
||||
|
||||
createCommand().delete(new TagDeleteRequest("test-tag"));
|
||||
|
||||
tag = findTag(context, "test-tag");
|
||||
assertThat(tag).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPostDeleteEvent() {
|
||||
ArgumentCaptor<Object> captor = ArgumentCaptor.forClass(Object.class);
|
||||
doNothing().when(eventBus).post(captor.capture());
|
||||
when(hookContextFactory.createContext(any(), any())).thenAnswer(this::createMockedContext);
|
||||
|
||||
createCommand().delete(new TagDeleteRequest("test-tag"));
|
||||
|
||||
List<Object> events = captor.getAllValues();
|
||||
assertThat(events.get(0)).isInstanceOf(PreReceiveRepositoryHookEvent.class);
|
||||
assertThat(events.get(1)).isInstanceOf(PostReceiveRepositoryHookEvent.class);
|
||||
|
||||
PreReceiveRepositoryHookEvent event = (PreReceiveRepositoryHookEvent) events.get(0);
|
||||
assertThat(event.getContext().getTagProvider().getCreatedTags()).isEmpty();
|
||||
final Tag deletedTag = event.getContext().getTagProvider().getDeletedTags().get(0);
|
||||
assertThat(deletedTag.getName()).isEqualTo("test-tag");
|
||||
assertThat(deletedTag.getRevision()).isEqualTo("86a6645eceefe8b9a247db5eb16e3d89a7e6e6d1");
|
||||
}
|
||||
|
||||
private GitTagCommand createCommand() {
|
||||
return new GitTagCommand(createContext(), hookContextFactory, eventBus);
|
||||
}
|
||||
|
||||
private List<Tag> readTags(GitContext context) throws IOException {
|
||||
return new GitTagsCommand(context, gpg).getTags();
|
||||
}
|
||||
|
||||
private Optional<Tag> findTag(GitContext context, String name) throws IOException {
|
||||
List<Tag> tags = readTags(context);
|
||||
return tags.stream().filter(t -> name.equals(t.getName())).findFirst();
|
||||
}
|
||||
|
||||
private HookContext createMockedContext(InvocationOnMock invocation) {
|
||||
HookContext mock = mock(HookContext.class);
|
||||
when(mock.getTagProvider()).thenReturn(((HookContextProvider) invocation.getArgument(0)).getTagProvider());
|
||||
return mock;
|
||||
}
|
||||
}
|
||||
@@ -24,48 +24,86 @@
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import com.github.sdorra.shiro.ShiroRule;
|
||||
import com.github.sdorra.shiro.SubjectAware;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import sonia.scm.repository.SignatureStatus;
|
||||
import sonia.scm.repository.Tag;
|
||||
import sonia.scm.security.GPG;
|
||||
import sonia.scm.security.PublicKey;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@SubjectAware(configuration = "classpath:sonia/scm/configuration/shiro.ini", username = "admin", password = "secret")
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class GitTagsCommandTest extends AbstractGitCommandTestBase {
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
@Mock
|
||||
GPG gpg;
|
||||
|
||||
@Rule
|
||||
public BindTransportProtocolRule transportProtocolRule = new BindTransportProtocolRule();
|
||||
|
||||
@Rule
|
||||
public ShiroRule shiro = new ShiroRule();
|
||||
@Mock
|
||||
PublicKey publicKey;
|
||||
|
||||
@Test
|
||||
public void shouldGetDatesCorrectly() throws IOException {
|
||||
final GitContext gitContext = createContext();
|
||||
final GitTagsCommand tagsCommand = new GitTagsCommand(gitContext);
|
||||
final GitTagsCommand tagsCommand = new GitTagsCommand(gitContext, gpg);
|
||||
final List<Tag> tags = tagsCommand.getTags();
|
||||
assertThat(tags).hasSize(2);
|
||||
assertThat(tags).hasSize(3);
|
||||
|
||||
Tag annotatedTag = tags.get(0);
|
||||
assertThat(annotatedTag.getName()).isEqualTo("1.0.0");
|
||||
assertThat(annotatedTag.getDate()).contains(1598348105000L); // Annotated - Take tag date
|
||||
assertThat(annotatedTag.getRevision()).isEqualTo("fcd0ef1831e4002ac43ea539f4094334c79ea9ec");
|
||||
|
||||
Tag lightweightTag = tags.get(1);
|
||||
Tag lightweightTag = tags.get(2);
|
||||
assertThat(lightweightTag.getName()).isEqualTo("test-tag");
|
||||
assertThat(lightweightTag.getDate()).contains(1339416344000L); // Lightweight - Take commit date
|
||||
assertThat(lightweightTag.getRevision()).isEqualTo("86a6645eceefe8b9a247db5eb16e3d89a7e6e6d1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetSignatures() throws IOException {
|
||||
when(gpg.findPublicKeyId(ArgumentMatchers.any())).thenReturn("2BA27721F113C005CC16F06BAE63EFBC49F140CF");
|
||||
when(gpg.findPublicKey("2BA27721F113C005CC16F06BAE63EFBC49F140CF")).thenReturn(Optional.of(publicKey));
|
||||
String signature = "-----BEGIN PGP SIGNATURE-----\n" +
|
||||
"\n" +
|
||||
"iQEzBAABCgAdFiEEK6J3IfETwAXMFvBrrmPvvEnxQM8FAl+9acoACgkQrmPvvEnx\n" +
|
||||
"QM9abwgAnGP+Y/Ijli+PAsimfOmZQWYepjptoOv9m7i3bnHv8V+Qg6cm51I3E0YV\n" +
|
||||
"R2QaxxzW9PgS4hcES+L1qs8Lwo18RurF469eZEmNb8DcUFJ3sEWeHlIl5wZNNo/v\n" +
|
||||
"jJm0d9LNcSmtAIiQ8eDMoGdFXJzHewGickLOSsQGmfZgZus4Qlsh7r3BZTI1Zwd/\n" +
|
||||
"6jaBFctX13FuepCTxq2SjEfRaQHIYkyFQq2o6mjL5S2qfYJ/S//gcCCzxllQrisF\n" +
|
||||
"5fRW3LzLI4eXFH0vua7+UzNS2Rwpifg2OENJA/Kn+3R36LWEGxFK9pNqjVPRAcQj\n" +
|
||||
"1vSkcjK26RqhAqCjNLSagM8ATZrh+g==\n" +
|
||||
"=kUKm\n" +
|
||||
"-----END PGP SIGNATURE-----\n";
|
||||
String signedContent = "object 592d797cd36432e591416e8b2b98154f4f163411\n" +
|
||||
"type commit\n" +
|
||||
"tag signedtag\n" +
|
||||
"tagger Arthur Dent <arthur.dent@hitchhiker.com> 1606248906 +0100\n" +
|
||||
"\n" +
|
||||
"this tag is signed\n";
|
||||
when(publicKey.verify(signedContent.getBytes(), signature.getBytes())).thenReturn(true);
|
||||
|
||||
final GitContext gitContext = createContext();
|
||||
final GitTagsCommand tagsCommand = new GitTagsCommand(gitContext, gpg);
|
||||
final List<Tag> tags = tagsCommand.getTags();
|
||||
|
||||
assertThat(tags).hasSize(3);
|
||||
|
||||
Tag signedTag = tags.get(1);
|
||||
assertThat(signedTag.getSignatures()).isNotEmpty();
|
||||
assertThat(signedTag.getSignatures().get(0).getStatus()).isEqualTo(SignatureStatus.VERIFIED);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getZippedRepositoryResource() {
|
||||
return "sonia/scm/repository/spi/scm-git-spi-test-tags.zip";
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user