public void TheIsTokenAuthenticationMethod(string username, string password, string token, bool expectedValue)
        {
            var authenticationInfo = new AuthenticationInfo
            {
                Username = username,
                Password = password,
                Token = token
            };

            Assert.AreEqual(expectedValue, authenticationInfo.IsTokenAuthentication());
        }
Example #2
0
 public GitPreparer(string targetUrl, string dynamicRepositoryLocation, Authentication authentication, bool noFetch, string targetPath)
 {
     this.targetUrl = targetUrl;
     this.dynamicRepositoryLocation = dynamicRepositoryLocation;
     this.authentication = authentication == null ?
         null :
         new AuthenticationInfo
         {
             Username = authentication.Username,
             Password = authentication.Password
         };
     this.noFetch = noFetch;
     this.targetPath = targetPath.TrimEnd('/', '\\');
 }
Example #3
0
        static string CreateDynamicRepository(string targetPath, AuthenticationInfo authentication, string repositoryUrl, string targetBranch, bool noFetch)
        {
            if (string.IsNullOrWhiteSpace(targetBranch))
            {
                throw new Exception("Dynamic Git repositories must have a target branch (/b)");
            }
            Logger.WriteInfo(string.Format("Creating dynamic repository at '{0}'", targetPath));

            var gitDirectory = Path.Combine(targetPath, ".git");
            if (Directory.Exists(targetPath))
            {
                Logger.WriteInfo("Git repository already exists");
                GitRepositoryHelper.NormalizeGitDirectory(gitDirectory, authentication, noFetch, targetBranch);

                return gitDirectory;
            }

            CloneRepository(repositoryUrl, gitDirectory, authentication);

            // Normalize (download branches) before using the branch
            GitRepositoryHelper.NormalizeGitDirectory(gitDirectory, authentication, noFetch, targetBranch);

            return gitDirectory;
        }
Example #4
0
        static void CloneRepository(string repositoryUrl, string gitDirectory, AuthenticationInfo authentication)
        {
            Credentials credentials = null;
            if (!string.IsNullOrWhiteSpace(authentication.Username) && !string.IsNullOrWhiteSpace(authentication.Password))
            {
                Logger.WriteInfo(string.Format("Setting up credentials using name '{0}'", authentication.Username));

                credentials = new UsernamePasswordCredentials
                {
                    Username = authentication.Username,
                    Password = authentication.Password
                };
            }

            Logger.WriteInfo(string.Format("Retrieving git info from url '{0}'", repositoryUrl));

            try
            {
                var cloneOptions = new CloneOptions
                {
                    Checkout = false,
                    CredentialsProvider = (url, usernameFromUrl, types) => credentials
                };
                var returnedPath = Repository.Clone(repositoryUrl, gitDirectory, cloneOptions);
                Logger.WriteInfo(string.Format("Returned path after repository clone: {0}", returnedPath));
            }
            catch (LibGit2SharpException ex)
            {
                var message = ex.Message;
                if (message.Contains("401"))
                {
                    throw new Exception("Unauthorised: Incorrect username/password");
                }
                if (message.Contains("403"))
                {
                    throw new Exception("Forbidden: Possbily Incorrect username/password");
                }
                if (message.Contains("404"))
                {
                    throw new Exception("Not found: The repository was not found");
                }

                throw new Exception("There was an unknown problem with the Git repository you provided", ex);
            }
        }
        /// <summary>
        /// Normalisation of a git directory turns all remote branches into local branches, turns pull request refs into a real branch and a few other things. This is designed to be run *only on the build server* which checks out repositories in different ways.
        /// It is not recommended to run normalisation against a local repository
        /// </summary>
        public static void NormalizeGitDirectory(string gitDirectory, AuthenticationInfo authentication, bool noFetch, string currentBranch)
        {
            using (var repo = new Repository(gitDirectory))
            {
                // Need to unsure the HEAD does not move, this is essentially a BugCheck
                var expectedSha = repo.Head.Tip.Sha;
                try
                {
                    var remote = EnsureOnlyOneRemoteIsDefined(repo);

                    AddMissingRefSpecs(repo, remote);

                    //If noFetch is enabled, then GitVersion will assume that the git repository is normalized before execution, so that fetching from remotes is not required.
                    if (noFetch)
                    {
                        Log.Info("Skipping fetching, if GitVersion does not calculate your version as expected you might need to allow fetching or use dynamic repositories");
                    }
                    else
                    {
                        Fetch(authentication, remote, repo);
                    }

                    EnsureLocalBranchExistsForCurrentBranch(repo, currentBranch);
                    CreateOrUpdateLocalBranchesFromRemoteTrackingOnes(repo, remote.Name);

                    var headSha = repo.Refs.Head.TargetIdentifier;

                    if (!repo.Info.IsHeadDetached)
                    {
                        Log.Info(string.Format("HEAD points at branch '{0}'.", headSha));
                        return;
                    }

                    Log.Info(string.Format("HEAD is detached and points at commit '{0}'.", headSha));
                    Log.Info(string.Format("Local Refs:\r\n" + string.Join(Environment.NewLine, repo.Refs.FromGlob("*").Select(r => string.Format("{0} ({1})", r.CanonicalName, r.TargetIdentifier)))));

                    // In order to decide whether a fake branch is required or not, first check to see if any local branches have the same commit SHA of the head SHA.
                    // If they do, go ahead and checkout that branch
                    // If no, go ahead and check out a new branch, using the known commit SHA as the pointer
                    var localBranchesWhereCommitShaIsHead = repo.Branches.Where(b => !b.IsRemote && b.Tip.Sha == headSha).ToList();

                    var matchingCurrentBranch = !string.IsNullOrEmpty(currentBranch)
                        ? localBranchesWhereCommitShaIsHead.SingleOrDefault(b => b.CanonicalName.Replace("/heads/", "/") == currentBranch.Replace("/heads/", "/"))
                        : null;
                    if (matchingCurrentBranch != null)
                    {
                        Log.Info(string.Format("Checking out local branch '{0}'.", currentBranch));
                        repo.Checkout(matchingCurrentBranch);
                    }
                    else if (localBranchesWhereCommitShaIsHead.Count > 1)
                    {
                        var branchNames = localBranchesWhereCommitShaIsHead.Select(r => r.CanonicalName);
                        var csvNames = string.Join(", ", branchNames);
                        const string moveBranchMsg = "Move one of the branches along a commit to remove warning";

                        Log.Warn(string.Format("Found more than one local branch pointing at the commit '{0}' ({1}).", headSha, csvNames));
                        var master = localBranchesWhereCommitShaIsHead.SingleOrDefault(n => n.FriendlyName == "master");
                        if (master != null)
                        {
                            Log.Warn("Because one of the branches is 'master', will build master." + moveBranchMsg);
                            repo.Checkout(master);
                        }
                        else
                        {
                            var branchesWithoutSeparators = localBranchesWhereCommitShaIsHead.Where(b => !b.FriendlyName.Contains('/') && !b.FriendlyName.Contains('-')).ToList();
                            if (branchesWithoutSeparators.Count == 1)
                            {
                                var branchWithoutSeparator = branchesWithoutSeparators[0];
                                Log.Warn(string.Format("Choosing {0} as it is the only branch without / or - in it. " + moveBranchMsg, branchWithoutSeparator.CanonicalName));
                                repo.Checkout(branchWithoutSeparator);
                            }
                            else
                            {
                                throw new WarningException("Failed to try and guess branch to use. " + moveBranchMsg);
                            }
                        }
                    }
                    else if (localBranchesWhereCommitShaIsHead.Count == 0)
                    {
                        Log.Info(string.Format("No local branch pointing at the commit '{0}'. Fake branch needs to be created.", headSha));
                        CreateFakeBranchPointingAtThePullRequestTip(repo, authentication);
                    }
                    else
                    {
                        Log.Info(string.Format("Checking out local branch 'refs/heads/{0}'.", localBranchesWhereCommitShaIsHead[0].FriendlyName));
                        repo.Checkout(repo.Branches[localBranchesWhereCommitShaIsHead[0].FriendlyName]);
                    }
                }
                finally
                {
                    if (repo.Head.Tip.Sha != expectedSha)
                    {
                        if (Environment.GetEnvironmentVariable("IGNORE_NORMALISATION_GIT_HEAD_MOVE") != "1")
                        {
                            // Whoa, HEAD has moved, it shouldn't have. We need to blow up because there is a bug in normalisation
                            throw new BugException(string.Format(@"GitTools.Core has a bug, your HEAD has moved after repo normalisation.

To disable this error set an environmental variable called IGNORE_NORMALISATION_GIT_HEAD_MOVE to 1

Please run `git {0}` and submit it along with your build log (with personal info removed) in a new issue at https://github.com/GitTools/GitTools.Core",
                                LibGitExtensions.CreateGitLogArgs(100)));
                        }
                    }
                }
            }
        }
        static void CreateFakeBranchPointingAtThePullRequestTip(Repository repo, AuthenticationInfo authentication)
        {
            var remote = repo.Network.Remotes.Single();

            Log.Info("Fetching remote refs to see if there is a pull request ref");
            var remoteTips = (string.IsNullOrEmpty(authentication.Username) ?
                GetRemoteTipsForAnonymousUser(repo, remote) :
                GetRemoteTipsUsingUsernamePasswordCredentials(repo, remote, authentication.Username, authentication.Password))
                .ToList();

            Log.Info("Remote Refs:\r\n" + string.Join(Environment.NewLine, remoteTips.Select(r => r.CanonicalName)));

            var headTipSha = repo.Head.Tip.Sha;

            var refs = remoteTips.Where(r => r.TargetIdentifier == headTipSha).ToList();

            if (refs.Count == 0)
            {
                var message = string.Format("Couldn't find any remote tips from remote '{0}' pointing at the commit '{1}'.", remote.Url, headTipSha);
                throw new WarningException(message);
            }

            if (refs.Count > 1)
            {
                var names = string.Join(", ", refs.Select(r => r.CanonicalName));
                var message = string.Format("Found more than one remote tip from remote '{0}' pointing at the commit '{1}'. Unable to determine which one to use ({2}).", remote.Url, headTipSha, names);
                throw new WarningException(message);
            }

            var reference = refs[0];
            var canonicalName = reference.CanonicalName;
            Log.Info(string.Format("Found remote tip '{0}' pointing at the commit '{1}'.", canonicalName, headTipSha));

            if (canonicalName.StartsWith("refs/tags"))
            {
                Log.Info(string.Format("Checking out tag '{0}'", canonicalName));
                repo.Checkout(reference.Target.Sha);
                return;
            }

            if (!canonicalName.StartsWith("refs/pull/") && !canonicalName.StartsWith("refs/pull-requests/"))
            {
                var message = string.Format("Remote tip '{0}' from remote '{1}' doesn't look like a valid pull request.", canonicalName, remote.Url);
                throw new WarningException(message);
            }

            var fakeBranchName = canonicalName.Replace("refs/pull/", "refs/heads/pull/").Replace("refs/pull-requests/", "refs/heads/pull-requests/");

            Log.Info(string.Format("Creating fake local branch '{0}'.", fakeBranchName));
            repo.Refs.Add(fakeBranchName, new ObjectId(headTipSha));

            Log.Info(string.Format("Checking local branch '{0}' out.", fakeBranchName));
            repo.Checkout(fakeBranchName);
        }
 public static void Fetch(AuthenticationInfo authentication, Remote remote, Repository repo)
 {
     Log.Info(string.Format("Fetching from remote '{0}' using the following refspecs: {1}.",
         remote.Name, string.Join(", ", remote.FetchRefSpecs.Select(r => r.Specification))));
     Commands.Fetch(repo, remote.Name, new string[0], authentication.ToFetchOptions(), null);
 }
        /// <summary>
        /// Normalisation of a git directory turns all remote branches into local branches, turns pull request refs into a real branch and a few other things. This is designed to be run *only on the build server* which checks out repositories in different ways.
        /// It is not recommended to run normalisation against a local repository
        /// </summary>
        public static void NormalizeGitDirectory(string gitDirectory, AuthenticationInfo authentication, bool noFetch, string currentBranch)
        {
            using (var repo = new Repository(gitDirectory))
            {
                // Need to unsure the HEAD does not move, this is essentially a BugCheck
                var expectedSha = repo.Head.Tip.Sha;
                try
                {
                    var remote = EnsureOnlyOneRemoteIsDefined(repo);

                    AddMissingRefSpecs(repo, remote);

                    //If noFetch is enabled, then GitVersion will assume that the git repository is normalized before execution, so that fetching from remotes is not required.
                    if (noFetch)
                    {
                        Log.Info("Skipping fetching, if GitVersion does not calculate your version as expected you might need to allow fetching or use dynamic repositories");
                    }
                    else
                    {
                        Fetch(authentication, remote, repo);
                    }

                    EnsureLocalBranchExistsForCurrentBranch(repo, remote, currentBranch);
                    CreateOrUpdateLocalBranchesFromRemoteTrackingOnes(repo, remote.Name);

                    var headSha = repo.Refs.Head.TargetIdentifier;

                    if (!repo.Info.IsHeadDetached)
                    {
                        Log.Info(string.Format("HEAD points at branch '{0}'.", headSha));
                        return;
                    }

                    Log.Info(string.Format("HEAD is detached and points at commit '{0}'.", headSha));
                    Log.Info(string.Format("Local Refs:\r\n" + string.Join(Environment.NewLine, repo.Refs.FromGlob("*").Select(r => string.Format("{0} ({1})", r.CanonicalName, r.TargetIdentifier)))));

                    // In order to decide whether a fake branch is required or not, first check to see if any local branches have the same commit SHA of the head SHA.
                    // If they do, go ahead and checkout that branch
                    // If no, go ahead and check out a new branch, using the known commit SHA as the pointer
                    var localBranchesWhereCommitShaIsHead = repo.Branches.Where(b => !b.IsRemote && b.Tip.Sha == headSha).ToList();

                    var matchingCurrentBranch = !string.IsNullOrEmpty(currentBranch)
                        ? localBranchesWhereCommitShaIsHead.SingleOrDefault(b => b.CanonicalName.Replace("/heads/", "/") == currentBranch.Replace("/heads/", "/"))
                        : null;
                    if (matchingCurrentBranch != null)
                    {
                        Log.Info(string.Format("Checking out local branch '{0}'.", currentBranch));
                        Commands.Checkout(repo, matchingCurrentBranch);
                    }
                    else if (localBranchesWhereCommitShaIsHead.Count > 1)
                    {
                        var          branchNames   = localBranchesWhereCommitShaIsHead.Select(r => r.CanonicalName);
                        var          csvNames      = string.Join(", ", branchNames);
                        const string moveBranchMsg = "Move one of the branches along a commit to remove warning";

                        Log.Warn(string.Format("Found more than one local branch pointing at the commit '{0}' ({1}).", headSha, csvNames));
                        var master = localBranchesWhereCommitShaIsHead.SingleOrDefault(n => n.FriendlyName == "master");
                        if (master != null)
                        {
                            Log.Warn("Because one of the branches is 'master', will build master." + moveBranchMsg);
                            Commands.Checkout(repo, master);
                        }
                        else
                        {
                            var branchesWithoutSeparators = localBranchesWhereCommitShaIsHead.Where(b => !b.FriendlyName.Contains('/') && !b.FriendlyName.Contains('-')).ToList();
                            if (branchesWithoutSeparators.Count == 1)
                            {
                                var branchWithoutSeparator = branchesWithoutSeparators[0];
                                Log.Warn(string.Format("Choosing {0} as it is the only branch without / or - in it. " + moveBranchMsg, branchWithoutSeparator.CanonicalName));
                                Commands.Checkout(repo, branchWithoutSeparator);
                            }
                            else
                            {
                                throw new WarningException("Failed to try and guess branch to use. " + moveBranchMsg);
                            }
                        }
                    }
                    else if (localBranchesWhereCommitShaIsHead.Count == 0)
                    {
                        Log.Info(string.Format("No local branch pointing at the commit '{0}'. Fake branch needs to be created.", headSha));
                        CreateFakeBranchPointingAtThePullRequestTip(repo, authentication);
                    }
                    else
                    {
                        Log.Info(string.Format("Checking out local branch 'refs/heads/{0}'.", localBranchesWhereCommitShaIsHead[0].FriendlyName));
                        Commands.Checkout(repo, repo.Branches[localBranchesWhereCommitShaIsHead[0].FriendlyName]);
                    }
                }
                finally
                {
                    if (repo.Head.Tip.Sha != expectedSha)
                    {
                        if (Environment.GetEnvironmentVariable("IGNORE_NORMALISATION_GIT_HEAD_MOVE") != "1")
                        {
                            // Whoa, HEAD has moved, it shouldn't have. We need to blow up because there is a bug in normalisation
                            throw new BugException(string.Format(@"GitTools.Core has a bug, your HEAD has moved after repo normalisation.

To disable this error set an environmental variable called IGNORE_NORMALISATION_GIT_HEAD_MOVE to 1

Please run `git {0}` and submit it along with your build log (with personal info removed) in a new issue at https://github.com/GitTools/GitTools.Core",
                                                                 LibGitExtensions.CreateGitLogArgs(100)));
                        }
                    }
                }
            }
        }
 public static void Fetch(AuthenticationInfo authentication, Remote remote, Repository repo)
 {
     Log.Info(string.Format("Fetching from remote '{0}' using the following refspecs: {1}.",
                            remote.Name, string.Join(", ", remote.FetchRefSpecs.Select(r => r.Specification))));
     Commands.Fetch(repo, remote.Name, new string[0], authentication.ToFetchOptions(), null);
 }
        private static void CloneRepository(string repositoryUrl, string gitDirectory, AuthenticationInfo authentication)
        {
            Credentials credentials = null;
            if (!string.IsNullOrWhiteSpace(authentication.Username) && !string.IsNullOrWhiteSpace(authentication.Password))
            {
                Log.Info(string.Format("Setting up credentials using name '{0}'", authentication.Username));

                credentials = new UsernamePasswordCredentials
                {
                    Username = authentication.Username,
                    Password = authentication.Password
                };
            }

            Log.Info(string.Format("Retrieving git info from url '{0}'", repositoryUrl));

            try
            {
                Repository.Clone(repositoryUrl, gitDirectory,
                    new CloneOptions
                    {
                        Checkout = false,
                        CredentialsProvider = (url, usernameFromUrl, types) => credentials
                    });
            }
            catch (LibGit2SharpException ex)
            {
                var message = ex.Message;
                if (message.Contains("401"))
                {
                    throw new GitToolsException("Unauthorised: Incorrect username/password");
                }
                if (message.Contains("403"))
                {
                    throw new GitToolsException("Forbidden: Possbily Incorrect username/password");
                }
                if (message.Contains("404"))
                {
                    throw new GitToolsException("Not found: The repository was not found");
                }

                throw new GitToolsException("There was an unknown problem with the Git repository you provided");
            }
        }