001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.maven.scm.provider.git.jgit.command.tag; 020 021import java.io.IOException; 022import java.util.ArrayList; 023import java.util.EnumSet; 024import java.util.List; 025import java.util.Optional; 026import java.util.function.BiFunction; 027 028import org.apache.maven.scm.ScmException; 029import org.apache.maven.scm.ScmFile; 030import org.apache.maven.scm.ScmFileSet; 031import org.apache.maven.scm.ScmFileStatus; 032import org.apache.maven.scm.ScmResult; 033import org.apache.maven.scm.ScmTagParameters; 034import org.apache.maven.scm.command.tag.AbstractTagCommand; 035import org.apache.maven.scm.command.tag.TagScmResult; 036import org.apache.maven.scm.provider.ScmProviderRepository; 037import org.apache.maven.scm.provider.git.command.GitCommand; 038import org.apache.maven.scm.provider.git.jgit.command.CustomizableSshSessionFactoryCommand; 039import org.apache.maven.scm.provider.git.jgit.command.JGitTransportConfigCallback; 040import org.apache.maven.scm.provider.git.jgit.command.JGitUtils; 041import org.apache.maven.scm.provider.git.jgit.command.PushException; 042import org.apache.maven.scm.provider.git.jgit.command.ScmProviderAwareSshdSessionFactory; 043import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository; 044import org.eclipse.jgit.api.Git; 045import org.eclipse.jgit.api.TransportConfigCallback; 046import org.eclipse.jgit.api.errors.GitAPIException; 047import org.eclipse.jgit.lib.Constants; 048import org.eclipse.jgit.lib.Ref; 049import org.eclipse.jgit.revwalk.RevCommit; 050import org.eclipse.jgit.revwalk.RevWalk; 051import org.eclipse.jgit.transport.RefSpec; 052import org.eclipse.jgit.transport.RemoteRefUpdate; 053import org.eclipse.jgit.treewalk.TreeWalk; 054import org.slf4j.Logger; 055 056/** 057 * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a> 058 * @author Dominik Bartholdi (imod) 059 * @since 1.9 060 */ 061public class JGitTagCommand extends AbstractTagCommand implements GitCommand, CustomizableSshSessionFactoryCommand { 062 063 private BiFunction<GitScmProviderRepository, Logger, ScmProviderAwareSshdSessionFactory> sshSessionFactorySupplier; 064 065 public JGitTagCommand() { 066 sshSessionFactorySupplier = ScmProviderAwareSshdSessionFactory::new; 067 } 068 069 @Override 070 public void setSshSessionFactorySupplier( 071 BiFunction<GitScmProviderRepository, Logger, ScmProviderAwareSshdSessionFactory> 072 sshSessionFactorySupplier) { 073 this.sshSessionFactorySupplier = sshSessionFactorySupplier; 074 } 075 076 public ScmResult executeTagCommand(ScmProviderRepository repo, ScmFileSet fileSet, String tag, String message) 077 throws ScmException { 078 return executeTagCommand(repo, fileSet, tag, new ScmTagParameters(message)); 079 } 080 081 /** 082 * {@inheritDoc} 083 */ 084 public ScmResult executeTagCommand( 085 ScmProviderRepository repo, ScmFileSet fileSet, String tag, ScmTagParameters scmTagParameters) 086 throws ScmException { 087 if (tag == null || tag.trim().isEmpty()) { 088 throw new ScmException("tag name must be specified"); 089 } 090 091 if (!fileSet.getFileList().isEmpty()) { 092 throw new ScmException("This provider doesn't support tagging subsets of a directory"); 093 } 094 095 String escapedTagName = tag.trim().replace(' ', '_'); 096 097 Git git = null; 098 try { 099 git = JGitUtils.openRepo(fileSet.getBasedir()); 100 101 // tag the revision 102 String tagMessage = scmTagParameters.getMessage(); 103 Ref tagRef = git.tag() 104 .setName(escapedTagName) 105 .setMessage(tagMessage) 106 .setForceUpdate(false) 107 .call(); 108 109 if (repo.isPushChanges()) { 110 TransportConfigCallback transportConfigCallback = new JGitTransportConfigCallback( 111 sshSessionFactorySupplier.apply((GitScmProviderRepository) repo, logger)); 112 113 logger.info("push tag [" + escapedTagName + "] to remote..."); 114 JGitUtils.push( 115 git, 116 (GitScmProviderRepository) repo, 117 new RefSpec(Constants.R_TAGS + escapedTagName), 118 EnumSet.of(RemoteRefUpdate.Status.OK, RemoteRefUpdate.Status.UP_TO_DATE), 119 Optional.of(transportConfigCallback)); 120 } 121 122 // search for the tagged files 123 RevWalk revWalk = new RevWalk(git.getRepository()); 124 RevCommit commit = revWalk.parseCommit(tagRef.getObjectId()); 125 revWalk.close(); 126 127 final TreeWalk walk = new TreeWalk(git.getRepository()); 128 walk.reset(); // drop the first empty tree, which we do not need here 129 walk.setRecursive(true); 130 walk.addTree(commit.getTree()); 131 132 List<ScmFile> taggedFiles = new ArrayList<>(); 133 while (walk.next()) { 134 taggedFiles.add(new ScmFile(walk.getPathString(), ScmFileStatus.CHECKED_OUT)); 135 } 136 walk.close(); 137 138 return new TagScmResult("JGit tag", taggedFiles); 139 } catch (PushException e) { 140 logger.debug("Failed to push tag", e); 141 return new TagScmResult("JGit tag", "Failed to push tag: " + e.getMessage(), "", false); 142 } catch (IOException | GitAPIException e) { 143 throw new ScmException("JGit tag failure!", e); 144 } finally { 145 JGitUtils.closeRepo(git); 146 } 147 } 148}