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()); }
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('/', '\\'); }
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; }
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))); } } } } }
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"); } }