jetbrains.buildServer.buildTriggers.vcs.git.submodules.SubmoduleResolverImpl.java Source code

Java tutorial

Introduction

Here is the source code for jetbrains.buildServer.buildTriggers.vcs.git.submodules.SubmoduleResolverImpl.java

Source

/*
 * Copyright 2000-2014 JetBrains s.r.o.
 *
 * 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 jetbrains.buildServer.buildTriggers.vcs.git.submodules;

import com.intellij.openapi.diagnostic.Logger;
import jetbrains.buildServer.buildTriggers.vcs.git.CommitLoader;
import jetbrains.buildServer.buildTriggers.vcs.git.GitUtils;
import jetbrains.buildServer.buildTriggers.vcs.git.OperationContext;
import jetbrains.buildServer.buildTriggers.vcs.git.VcsAuthenticationException;
import jetbrains.buildServer.vcs.VcsException;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.lib.BlobBasedConfig;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.URIish;
import org.jetbrains.annotations.NotNull;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Arrays;

/**
 * The resolver for submodules
 */
public class SubmoduleResolverImpl implements SubmoduleResolver {

    private static Logger LOG = Logger.getInstance(SubmoduleResolverImpl.class.getName());
    /**
     * Path from the root of the first repository.
     * For root repository = "".
     * For submodule repository = path of submodule.
     * For sub-submodules = path of submodule/path of sub-submodule in submodule repository.
     */
    protected final String myPathFromRoot;
    protected final OperationContext myContext;

    private final RevCommit myCommit;
    private final Repository myDb;
    protected final CommitLoader myCommitLoader;
    private SubmodulesConfig myConfig;

    public SubmoduleResolverImpl(@NotNull OperationContext context, @NotNull CommitLoader commitLoader,
            @NotNull Repository db, @NotNull RevCommit commit, @NotNull String pathFromRoot) {
        myCommitLoader = commitLoader;
        myDb = db;
        myCommit = commit;
        myContext = context;
        myPathFromRoot = pathFromRoot;
    }

    /**
     * Resolve the commit for submodule
     *
     * @param parentRepositoryUrl url of the parent repository
     * @param path   the within repository path
     * @param commit the commit identifier
     * @return the the resoled commit in other repository
     * @throws VcsAuthenticationException if there are authentication problems
     * @throws URISyntaxException if there are errors in submodule repository URI
     */
    @NotNull
    public RevCommit getSubmoduleCommit(@NotNull String parentRepositoryUrl, @NotNull String path,
            @NotNull ObjectId commit) throws CorruptObjectException, VcsException, URISyntaxException {
        ensureConfigLoaded();
        if (myConfig == null)
            throw new MissingSubmoduleConfigException(parentRepositoryUrl, myCommit.name(), path);

        final Submodule submodule = myConfig.findSubmodule(path);
        if (submodule == null)
            throw new MissingSubmoduleEntryException(parentRepositoryUrl, myCommit.name(), path);

        Repository r = resolveRepository(submodule.getUrl());
        String submoduleUrl = myContext.getConfig(r).getString("teamcity", null, "remote");

        if (!isCommitExist(r, commit)) {
            try {
                fetch(r, path, submodule.getUrl());
            } catch (Exception e) {
                throw new SubmoduleFetchException(parentRepositoryUrl, path, submoduleUrl, myCommit, e);
            }
        }
        try {
            return myCommitLoader.getCommit(r, commit);
        } catch (Exception e) {
            throw new MissingSubmoduleCommitException(parentRepositoryUrl, myCommit.name(), path,
                    submodule.getUrl(), commit.name());
        }
    }

    private boolean isCommitExist(final Repository r, final ObjectId commit) {
        RevWalk walk = new RevWalk(r);
        try {
            walk.parseCommit(commit);
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    public URIish resolveSubmoduleUrl(@NotNull String url) throws URISyntaxException {
        return new URIish(resolveSubmoduleUrl(myContext.getConfig(getRepository()), url));
    }

    private static boolean isRelative(@NotNull String url) {
        return url.startsWith(".");
    }

    @NotNull
    public static String resolveSubmoduleUrl(@NotNull final Repository repository,
            @NotNull final String relativeUrl) throws URISyntaxException {
        return resolveSubmoduleUrl(repository.getConfig(), relativeUrl);
    }

    @NotNull
    private static String resolveSubmoduleUrl(@NotNull StoredConfig mainRepoConfig, @NotNull String submoduleUrl)
            throws URISyntaxException {
        if (!isRelative(submoduleUrl))
            return submoduleUrl;

        String baseUrl = mainRepoConfig.getString("teamcity", null, "remote");
        URIish u = new URIish(baseUrl);
        String newPath = u.getPath();
        if (newPath.length() == 0) {
            newPath = submoduleUrl;
        } else {
            newPath = GitUtils.normalizePath(newPath + '/' + submoduleUrl);
        }
        return u.setPath(newPath).toPrivateString();
    }

    /**
     * Check if the specified directory is a submodule prefix
     *
     * @param path the path to check
     * @return true if the path contains submodules
     */
    public boolean containsSubmodule(String path) {
        ensureConfigLoaded();
        return myConfig != null && myConfig.isSubmodulePrefix(path);
    }

    /**
     * @return the current repository
     */
    public Repository getRepository() {
        return myDb;
    }

    /**
     * Get submodule url by it's path in current repository
     *
     * @param submodulePath path of submodule in current repository
     * @return submodule repository url or null if no submodules is registered for specified path
     */
    public String getSubmoduleUrl(String submodulePath) {
        ensureConfigLoaded();
        if (myConfig != null) {
            Submodule submodule = myConfig.findSubmodule(submodulePath);
            return submodule != null ? submodule.getUrl() : null;
        } else {
            return null;
        }
    }

    /**
     * Ensure that submodule configuration has been loaded.
     */
    private void ensureConfigLoaded() {
        if (myConfig == null) {
            try {
                myConfig = new SubmodulesConfig(myContext.getConfig(myDb),
                        new BlobBasedConfig(null, myDb, myCommit, ".gitmodules"));
            } catch (FileNotFoundException e) {
                // do nothing
            } catch (Exception e) {
                LOG.error("Unable to load or parse submodule configuration at: " + myCommit.getId().name(), e);
            }
        }
    }

    public Repository resolveRepository(@NotNull String submoduleUrl) throws VcsException, URISyntaxException {
        LOG.debug("Resolve repository for URL: " + submoduleUrl);
        final URIish uri = resolveSubmoduleUrl(submoduleUrl);
        Repository r = myContext.getRepositoryFor(uri);
        LOG.debug("Repository dir for submodule " + submoduleUrl + " is " + r.getDirectory().getAbsolutePath());
        return r;
    }

    public void fetch(Repository r, String submodulePath, String submoduleUrl)
            throws VcsException, URISyntaxException, IOException {
        if (LOG.isDebugEnabled())
            LOG.debug("Fetching submodule " + submoduleUrl + " used at " + submodulePath + " for "
                    + myContext.getGitRoot().debugInfo());
        URIish uri = resolveSubmoduleUrl(submoduleUrl);
        myContext.fetchSubmodule(r, uri, Arrays.asList(new RefSpec("+refs/*:refs/*")),
                myContext.getGitRoot().getAuthSettings());
    }

    public SubmoduleResolverImpl getSubResolver(RevCommit commit, String path) {
        Repository db = null;
        try {
            db = resolveRepository(getSubmoduleUrl(path));
        } catch (Exception e) {
            //exception means path does not contain submodule, use current repository
            db = getRepository();
        }
        return new SubmoduleResolverImpl(myContext, myCommitLoader, db, commit, fullPath(path));
    }

    /**
     * Get full path using from local path
     *
     * @param path the path to examine
     * @return the full including the base path
     */
    private String fullPath(String path) {
        return myPathFromRoot.length() == 0 ? path : myPathFromRoot + "/" + path;
    }
}