/** * Copyright 2005-2015 Red Hat, Inc. * <p> * Red Hat licenses this file to you 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 * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * 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.fabric8.project.support; import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; import com.jcraft.jsch.UserInfo; import io.fabric8.utils.Files; import io.fabric8.utils.IOHelpers; import io.fabric8.utils.Strings; import io.fabric8.utils.ssl.TrustEverythingSSLTrustManager; import org.eclipse.jgit.api.CommitCommand; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.GitCommand; import org.eclipse.jgit.api.PushCommand; import org.eclipse.jgit.api.TransportCommand; import org.eclipse.jgit.api.TransportConfigCallback; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.storage.file.FileRepositoryBuilder; import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.transport.CredentialsProviderUserInfo; import org.eclipse.jgit.transport.JschConfigSessionFactory; import org.eclipse.jgit.transport.OpenSshConfig; import org.eclipse.jgit.transport.PushResult; import org.eclipse.jgit.transport.RemoteRefUpdate; import org.eclipse.jgit.transport.SshSessionFactory; import org.eclipse.jgit.transport.SshTransport; import org.eclipse.jgit.transport.Transport; import org.eclipse.jgit.util.FS; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Properties; /** */ public class GitUtils { private static final transient Logger LOG = LoggerFactory.getLogger(GitUtils.class); public static File getRootGitDirectory(Git git) { return git.getRepository().getDirectory().getParentFile(); } public static String toString(Collection<RemoteRefUpdate> updates) { StringBuilder builder = new StringBuilder(); for (RemoteRefUpdate update : updates) { if (builder.length() > 0) { builder.append(" "); } builder.append(update.getMessage() + " " + update.getRemoteName() + " " + update.getNewObjectId()); } return builder.toString(); } public static void disableSslCertificateChecks() { LOG.info("Trusting all SSL certificates"); try { SSLContext context = SSLContext.getInstance("TLS"); context.init(null, new TrustManager[]{new TrustEverythingSSLTrustManager()}, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory()); // bypass host name check, too. HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { public boolean verify(String s, SSLSession sslSession) { return true; } }); } catch (NoSuchAlgorithmException e) { LOG.warn("Failed to bypass certificate check", e); } catch (KeyManagementException e) { LOG.warn("Failed to bypass certificate check", e); } } /** * Returns the remote git URL for the given branch */ public static String getRemoteURL(Git git, String branch) { Repository repository = git.getRepository(); return getRemoteURL(repository, branch); } /** * Returns the remote repository for the current branch in the given repository */ public static String getRemoteURL(Repository repository) throws IOException { if (repository != null) { return getRemoteURL(repository, "origin"); } return null; } public static String getRemoteURL(Repository repository, String remoteName) { if (repository != null) { StoredConfig config = repository.getConfig(); if (config != null) { return config.getString("remote", remoteName, "url"); } } return null; } public static void configureBranch(Git git, String branch, String origin, String remoteRepository) { // lets update the merge config if (!Strings.isNullOrBlank(branch)) { StoredConfig config = git.getRepository().getConfig(); config.setString("branch", branch, "remote", origin); config.setString("branch", branch, "merge", "refs/heads/" + branch); config.setString("remote", origin, "url", remoteRepository); config.setString("remote", origin, "fetch", "+refs/heads/*:refs/remotes/" + origin + "/*"); try { config.save(); } catch (IOException e) { LOG.error("Failed to save the git configuration to " + git.getRepository().getDirectory() + " with branch " + branch + " on " + origin + " remote repo: " + remoteRepository + " due: " + e.getMessage() + ". This exception is ignored.", e); } } } public static void addFiles(Git git, File... files) throws GitAPIException, IOException { File rootDir = getRootGitDirectory(git); for (File file : files) { String relativePath = getFilePattern(rootDir, file); git.add().addFilepattern(relativePath).call(); } } public static String getFilePattern(File rootDir, File file) throws IOException { String relativePath = Files.getRelativePath(rootDir, file); if (relativePath.startsWith(File.separator)) { relativePath = relativePath.substring(1); } return relativePath.replace(File.separatorChar, '/'); } public static RevCommit doCommitAndPush(Git git, String message, UserDetails userDetails, PersonIdent author, String branch, String origin, boolean pushOnCommit) throws GitAPIException { CommitCommand commit = git.commit().setAll(true).setMessage(message); if (author != null) { commit = commit.setAuthor(author); } RevCommit answer = commit.call(); if (LOG.isDebugEnabled()) { LOG.debug("Committed " + answer.getId() + " " + answer.getFullMessage()); } if (pushOnCommit) { PushCommand push = git.push(); configureCommand(push, userDetails); Iterable<PushResult> results = push.setRemote(origin).call(); for (PushResult result : results) { if (LOG.isDebugEnabled()) { LOG.debug("Pushed " + result.getMessages() + " " + result.getURI() + " branch: " + branch + " updates: " + toString(result.getRemoteUpdates())); } } } return answer; } public static void doAddCommitAndPushFiles(Git git, UserDetails userDetails, PersonIdent personIdent, String branch, String origin, String message, boolean pushOnCommit) throws GitAPIException { git.add().addFilepattern(".").call(); doCommitAndPush(git, message, userDetails, personIdent, branch, origin, pushOnCommit); } /** * Retrieves a Java Date from a Git commit. * * @param commit the commit * @return date of the commit or Date(0) if the commit is null */ public static Date getCommitDate(RevCommit commit) { if (commit == null) { return new Date(0); } return new Date(commit.getCommitTime() * 1000L); } /** * Configures the transport of the command to deal with things like SSH */ public static <C extends GitCommand> void configureCommand(TransportCommand<C, ?> command, UserDetails userDetails) { configureCommand(command, userDetails.createCredentialsProvider(), userDetails.getSshPrivateKey(), userDetails.getSshPublicKey()); command.setCredentialsProvider(userDetails.createCredentialsProvider()); } /** * Configures the transport of the command to deal with things like SSH */ public static <C extends GitCommand> void configureCommand(TransportCommand<C, ?> command, CredentialsProvider credentialsProvider, final File sshPrivateKey, final File sshPublicKey) { LOG.info("Using " + credentialsProvider); if (sshPrivateKey != null) { final CredentialsProvider provider = credentialsProvider; command.setTransportConfigCallback(new TransportConfigCallback() { @Override public void configure(Transport transport) { if (transport instanceof SshTransport) { SshTransport sshTransport = (SshTransport) transport; SshSessionFactory sshSessionFactory = new JschConfigSessionFactory() { @Override protected void configure(OpenSshConfig.Host host, Session session) { session.setConfig("StrictHostKeyChecking", "no"); UserInfo userInfo = new CredentialsProviderUserInfo(session, provider); session.setUserInfo(userInfo); } @Override protected JSch createDefaultJSch(FS fs) throws JSchException { JSch jsch = super.createDefaultJSch(fs); jsch.removeAllIdentity(); String absolutePath = sshPrivateKey.getAbsolutePath(); if (LOG.isDebugEnabled()) { LOG.debug("Adding identity privateKey: " + sshPrivateKey + " publicKey: " + sshPublicKey); } if (sshPublicKey != null) { jsch.addIdentity(absolutePath, sshPublicKey.getAbsolutePath(), null); } else { jsch.addIdentity(absolutePath); } return jsch; } }; sshTransport.setSshSessionFactory(sshSessionFactory); } } }); } } /** * Git tends to ignore empty directories so lets add a dummy file to empty folders to keep them in git */ public static void addDummyFileToEmptyFolders(File dir) { if (dir != null && dir.isDirectory()) { File[] children = dir.listFiles(); if (children == null || children.length == 0) { File dummyFile = new File(dir, ".gitkeep"); try { IOHelpers.writeFully(dummyFile, "This file is only here to avoid git removing empty folders\nOnce there are files in this folder feel free to delete this file!"); } catch (IOException e) { LOG.warn("Failed to write file " + dummyFile + ". " + e, e); } } else { for (File child : children) { if (child.isDirectory()) { addDummyFileToEmptyFolders(child); } } } } } /** * Returns the git repository for the current folder or null if none can be found */ public static Repository findRepository(File baseDir) throws IOException { File gitFolder = io.fabric8.utils.GitHelpers.findGitFolder(baseDir); if (gitFolder == null) { // No git repository found return null; } FileRepositoryBuilder builder = new FileRepositoryBuilder(); Repository repository = builder .readEnvironment() .setGitDir(gitFolder) .build(); return repository; } /** * Returns the ~/.gitconfig file parsed */ public static Map<String, Properties> parseGitConfig() throws IOException { String homeDir = System.getProperty("user.home", "."); File file = new File(homeDir, ".gitconfig"); if (file.exists() && file.isFile()) { return IniFileUtils.parseIniFile(file); } else { return new HashMap<>(); } } public static String getGitHostName(String gitUrl) { try { URI uri = new URI(gitUrl); return uri.getHost(); } catch (URISyntaxException e) { // ignore } String[] split = gitUrl.split(":"); if (split.length > 1) { String prefix = split[0]; int idx = prefix.indexOf('@'); if (idx >= 0) { return prefix.substring(idx + 1); } else { return prefix; } } return null; } public static String getGitProtocol(String gitUrl) { try { URI uri = new URI(gitUrl); return uri.getScheme(); } catch (URISyntaxException e) { // ignore } String[] split = gitUrl.split(":"); if (split.length > 1) { String prefix = split[0]; int idx = prefix.indexOf('@'); if (idx >= 0) { return "ssh"; } } return null; } }