All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.metaeffekt.artifact.analysis.utils.GitAccess Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2021-2024 the original author or authors.
 *
 * 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 com.metaeffekt.artifact.analysis.utils;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

public class GitAccess {

    private static final Logger LOG = LoggerFactory.getLogger(GitAccess.class);

    private String proxyScheme;
    private String proxyHost;
    private String proxyUsername;
    private String proxyPassword;
    private Integer proxyPort;

    private GIT_COMMAND_PROXY_SET_TYPE gitCommandProxySetType = GIT_COMMAND_PROXY_SET_TYPE.ENVIRONMENT;

    public enum GIT_COMMAND_PROXY_SET_TYPE {
        ENVIRONMENT,
        PARAMETERS
    }

    public GitAccess(String proxyScheme, String proxyHost, Integer proxyPort, String proxyUsername, String proxyPassword) {
        this.setProxyConfig(proxyScheme, proxyHost, proxyPort, proxyUsername, proxyPassword);
    }

    public GitAccess(String proxyScheme, String proxyHost, Integer proxyPort) {
        this.setProxyConfig(proxyScheme, proxyHost, proxyPort, null, null);
    }

    public GitAccess() {
        this.setProxyConfig(null, null, null, null, null);
    }

    public void setProxyConfig(String proxyScheme, String proxyHost, Integer proxyPort, String proxyUsername, String proxyPassword) {
        this.proxyScheme = proxyScheme;
        this.proxyHost = proxyHost;
        this.proxyPort = proxyPort;
        this.proxyUsername = proxyUsername;
        this.proxyPassword = proxyPassword;
    }

    public void setGitCommandProxySetType(GIT_COMMAND_PROXY_SET_TYPE gitCommandProxySetType) {
        this.gitCommandProxySetType = gitCommandProxySetType;
    }

    public void cloneRemote(File targetDir, String remote, String branch) throws IOException {
        // only shallow clone
        final String[] gitClone = new String[]{"git", "clone", "--depth", "1", remote, targetDir.getAbsolutePath()};
        executeCommand(gitClone, null);

        if (branch != null) {
            checkout(targetDir, branch);
        }
    }

    public void pull(File targetDir) throws IOException {
        final String[] gitPull = new String[]{"git", "pull"};
        executeCommand(gitPull, targetDir);
    }

    public void fetch(File targetDir) throws IOException {
        final String[] gitFetch = new String[]{"git", "fetch"};
        executeCommand(gitFetch, targetDir);
    }

    public void checkout(File targetDir, String branchOrTag) throws IOException {
        final String[] gitCheckout = new String[]{"git", "checkout", branchOrTag};
        executeCommand(gitCheckout, targetDir);
    }

    public void push(File targetDir) throws IOException {
        final String[] gitPush = new String[]{"git", "push"};
        executeCommand(gitPush, targetDir);
    }

    public List listBranches(File targetDir) throws IOException {
        final String[] gitBranch = new String[]{"git", "branch", "-r"};
        return executeCommandAndCollectOutput(gitBranch, targetDir).stream()
                .filter(b -> !b.contains(" -> "))
                .map(String::trim)
                .filter(StringUtils::hasText)
                .distinct()
                .collect(Collectors.toList());
    }

    public List listTags(File targetDir) throws IOException {
        final String[] gitTag = new String[]{"git", "tag"};
        return executeCommandAndCollectOutput(gitTag, targetDir).stream()
                .map(String::trim)
                .filter(StringUtils::hasText)
                .distinct()
                .collect(Collectors.toList());
    }

    private ProcessBuilder buildProcess(final String[] command, final File targetDir) {
        final List modifiedCommand = new ArrayList<>(Arrays.asList(command));
        final boolean hasProxy = proxyScheme != null && proxyHost != null && proxyPort != null;
        final boolean hasAnyProxy = hasProxy || (proxyUsername != null && proxyPassword != null);

        if (hasAnyProxy != hasProxy) {
            LOG.warn("Incomplete proxy configuration in git command, will still try to execute command");
        }

        final ProcessBuilder processBuilder = new ProcessBuilder(modifiedCommand);
        if (targetDir != null) {
            if (targetDir.exists()) {
                processBuilder.directory(targetDir.getAbsoluteFile());
            } else {
                throw new IllegalStateException("Target directory does not exist: " + targetDir.getAbsolutePath());
            }
        }

        if (hasProxy) {
            final String proxyCredentials = proxyUsername != null && proxyPassword != null
                    ? proxyUsername + ":" + proxyPassword + "@"
                    : "";
            final String proxySetting = proxyScheme + "://" + proxyCredentials + proxyHost + ":" + proxyPort;

            LOG.info("Using [GIT_COMMAND_PROXY_SET_TYPE.{}] to configure git proxy", gitCommandProxySetType);
            if (gitCommandProxySetType == GIT_COMMAND_PROXY_SET_TYPE.ENVIRONMENT) {
                processBuilder.environment().put("HTTP_PROXY", proxySetting);
                processBuilder.environment().put("http_proxy", proxySetting);
                processBuilder.environment().put("HTTPS_PROXY", proxySetting);
                processBuilder.environment().put("https_proxy", proxySetting);
            } else if (gitCommandProxySetType == GIT_COMMAND_PROXY_SET_TYPE.PARAMETERS) {
                int gitIndex = modifiedCommand.indexOf("git");
                if (gitIndex != -1) {
                    modifiedCommand.add(gitIndex + 1, "-c");
                    modifiedCommand.add(gitIndex + 2, "http.proxy=" + proxySetting);
                }
            } else {
                throw new IllegalStateException("Unsupported proxy set type: " + gitCommandProxySetType);
            }
        }

        if (targetDir != null) {
            LOG.info("git command [{}]: {}", targetDir.getName(), String.join(" ", modifiedCommand));
        } else {
            LOG.info("git command: {}", String.join(" ", modifiedCommand));
        }

        return processBuilder;
    }

    private void executeCommand(final String[] command, final File targetDir) throws IOException {
        final ProcessBuilder processBuilder = buildProcess(command, targetDir);
        final Process p = processBuilder.start();

        final StringBuilder outputBuilder = new StringBuilder();
        final StringBuilder errorBuilder = new StringBuilder();

        // capture output stream
        final BufferedReader outputReader = new BufferedReader(new InputStreamReader(p.getInputStream()));
        String line;
        while ((line = outputReader.readLine()) != null) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("[stdout] {}", line);
            } else {
                outputBuilder.append(line).append("\n");
            }
        }

        // capture error stream
        final BufferedReader errorReader = new BufferedReader(new InputStreamReader(p.getErrorStream()));
        while ((line = errorReader.readLine()) != null) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("[stderr] {}", line);
            } else {
                errorBuilder.append(line).append("\n");
            }
        }

        waitForProcess(p);

        if (p.exitValue() != 0) {
            if (LOG.isDebugEnabled()) {
                throw new IllegalStateException("Command failed. Exit code: " + p.exitValue());
            } else {
                throw new IllegalStateException("Command failed. Exit code: " + p.exitValue() + "\nOutput:\n" + outputBuilder + "\n\nError:\n" + errorBuilder);
            }
        }
    }

    private List executeCommandAndCollectOutput(String[] command, File targetDir) throws IOException {
        final ProcessBuilder processBuilder = buildProcess(command, targetDir);
        final Process p = processBuilder.start();

        final StringBuilder outputBuilder = new StringBuilder();
        final StringBuilder errorBuilder = new StringBuilder();

        // capture output stream
        try (BufferedReader outputReader = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
            String line;
            while ((line = outputReader.readLine()) != null) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("[stdout] {}", line);
                }
                outputBuilder.append(line).append("\n");
            }
        }

        // capture error stream
        try (BufferedReader errorReader = new BufferedReader(new InputStreamReader(p.getErrorStream()))) {
            String line;
            while ((line = errorReader.readLine()) != null) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("[stderr] {}", line);
                } else {
                    errorBuilder.append(line).append("\n");
                }
            }
        }

        waitForProcess(p);

        if (p.exitValue() != 0) {
            if (LOG.isDebugEnabled()) {
                throw new IllegalStateException("Command failed. Exit code: " + p.exitValue());
            } else {
                throw new IllegalStateException("Command failed. Exit code: " + p.exitValue() + "\nOutput:\n" + outputBuilder + "\n\nError:\n" + errorBuilder);
            }
        }

        final List output = new ArrayList<>();
        for (String s : outputBuilder.toString().split("\n")) {
            output.add(s.trim());
        }

        return output;
    }

    private void waitForProcess(Process p) {
        try {
            while (p.isAlive()) {
                p.waitFor(1000, TimeUnit.MILLISECONDS);
                final ByteArrayOutputStream baos = new ByteArrayOutputStream();
                try {
                    IOUtils.copy(p.getInputStream(), baos);
                } catch (IOException e) {
                    LOG.error("Unable to copy input stream into output stream.", e);
                }
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy